4b68965b8bb2ede71b420f57cea6ca89e279bad4
[linux-gpio-pinout] / gpio.pl
1 #!/usr/bin/perl
2 use warnings;
3 use strict;
4 use autodie;
5 use Data::Dump qw(dump);
6 use Getopt::Long;
7
8 my $opt_svg = 0;
9 my $opt_alt = 0;
10 my $opt_invert = 0;
11 my $opt_vertical = 0;
12 my $opt_horizontal = 0;
13 my $opt_edge = 0;
14 my $opt_middle = 0;
15 my $opt_zebra = 0;
16 my $opt_lines = 0;
17 my $opt_read = '';
18 my $opt_pins = '';
19 my $opt_color = 0;
20 GetOptions(
21         'svg!' => \$opt_svg,
22         'alt!' => \$opt_alt,
23         'invert!' => \$opt_invert,
24         'vertical-flip!' => \$opt_vertical,
25         'horizontal-flip!' => \$opt_horizontal,
26         'edge-pins!' => \$opt_edge,
27         'middle-pins!' => \$opt_middle,
28         'zebra!' => \$opt_zebra,
29         'lines!' => \$opt_lines,
30         'read=s' => \$opt_read,
31         'pins=s' => \$opt_pins,
32         'color' => \$opt_color,
33 );
34
35 # svg font hints
36 my $font_w = 1.67; # < 2.54, font is not perfect square
37 my $font_b = 2.10; # font baseline position
38
39 $opt_read .= '/' unless $opt_read =~ m/\/$/;
40
41 sub slurp {
42         open(my $fh, '<', $opt_read . shift);
43         local $/ = undef;
44         <$fh>;
45 }
46
47 my $pins;
48
49 my $model = slurp('/proc/device-tree/model');
50 $model =~ s/\x00$//; # strip kernel NULL
51 warn "# model [$model]";
52
53 open(DATA, '<', $opt_pins) if $opt_pins;
54
55 my @lines;
56 my $line_i = 0;
57
58 my $include = 0;
59 while(<DATA>) {
60         chomp;
61         if ( m/^#\s(.+)/ ) {
62                 warn "MODEL [$1] == [$model] ?\n";
63                 if ( $model =~ m/$1/ ) {
64                         $include = 1;
65                 } else {
66                         $include = 0;
67                 }
68         } elsif ( $include || $opt_pins ) {
69                 push @{ $pins->{$1} }, $line_i while ( m/\t(\w+\d+)/g );
70
71                 push @lines, $_;
72
73                 $line_i++;
74         } else {
75                 warn "IGNORE: [$_]\n";
76         }
77 }
78
79 die "add pin definition for # $model" unless $pins;
80
81 shift(@lines) while ( ! $lines[0] );    # remove empty at beginning
82 pop(@lines) while ( ! $lines[-1] );     # remove empty at end
83
84 warn "# pins ",dump($pins);
85
86 my $serial_tty;
87 foreach ( glob $opt_read . '/sys//devices/platform/soc*/*.serial/tty/tty*' ) {
88         my @v = split(/\//, $_);
89         $serial_tty->{ $v[-3] } = $v[-1];
90 }
91 warn "# serial_tty = ",dump($serial_tty);
92
93
94 my $pin_function;
95 my $device;
96
97 open(my $fh, '<', $opt_read . '/sys/kernel/debug/pinctrl/pinctrl-handles');
98 while(<$fh>) {
99         chomp;
100         if ( m/device: (\S+)/ ) {
101                 $device = $1;
102                 if ( my $replace = $serial_tty->{$device} ) {
103                         $device = $replace; # replace serial hex with kernel name
104                 } else {
105                         $device =~ s/^[0-9a-f]*\.//; # remove hex address
106                 }
107         } elsif ( m/group: (\w+\d+)\s.+function: (\S+)/ ) {
108                 my ($pin, $function) = ($1,$2);
109                 $pin_function->{$pin} = "$device $function";
110
111                 if ( $pins->{$pin} ) {
112                         foreach my $line ( @{$pins->{$pin}} ) {
113                                 my $t = $lines[$line];
114                                 if ( $opt_svg ) {
115                                         $t =~ s/$pin/[$device $function]/;
116                                 } else {
117                                         $t =~ s/$pin/$pin [$device $function]/ || warn "can't find $pin in [$t]";
118                                 }
119                                 $lines[$line] = $t;
120                                 warn "# $line: $lines[$line]\n";
121                         }
122                 } else {
123                         warn "IGNORED: pin $pin function $function\n";
124                 }
125         }
126 }
127
128 warn "# pin_function = ",dump($pin_function);
129
130 my @max_len = ( 0,0,0,0 );
131 my @line_parts;
132 foreach my $line (@lines) {
133         if ( $line =~ m/^#/ ) {
134                 push @line_parts, [ $line ] unless $opt_svg && $line =~ m/^###+/; # SVG doesn't display 3rd level comments
135                 next;
136         }
137         $line =~ s/(\[(?:uart|serial|tty\w+))([^\t]*\]\s[^\t]*(rx|tx)d?)/$1 $3$2/gi;
138         $line =~ s/(\[i2c)([^\t]*\]\s[^\t]*(scl?k?|sda))/$1 $3$2/gi;
139         $line =~ s/(\[spi)([^\t]*\]\s[^\t]*(miso|mosi|s?clk|c[se]\d*))/$1 $3$2/gi;
140         $line =~ s/\s*\([^\)]+\)//g if ! $opt_alt;
141
142         # shorten duplicate kernel device/function
143         $line =~ s/\[serial (\w+) (uart\d+)\]/[$2 $1]/g;
144         $line =~ s/\[(\w+) (\w+) \1(\d+)\]/[$1$3 $2]/g;
145
146         my @v = split(/\s*\t+\s*/,$line,4);
147         @v = ( $v[2], $v[3], $v[0], $v[1] ) if $opt_horizontal && $v[2];
148
149         push @line_parts, [ @v ];
150         foreach my $i ( 0 .. 3 ) {
151                 next unless exists $v[$i];
152                 next if $v[$i] =~ m/^#/; # don't calculate comments into max length
153                 my $l = length($v[$i]);
154                 $max_len[$i] = $l if $l > $max_len[$i];
155         }
156 }
157
158 warn "# max_len = ",dump( \@max_len );
159 warn "# line_parts = ",dump( \@line_parts );
160
161 #print "$_\n" foreach @lines;
162
163 my $x = 20.00; # mm
164 my $y = 20.00; # mm
165
166 if ( $opt_svg ) {
167         print qq{<?xml version="1.0" encoding="UTF-8" standalone="no"?>
168 <svg
169    xmlns:dc="http://purl.org/dc/elements/1.1/"
170    xmlns:cc="http://creativecommons.org/ns#"
171    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
172    xmlns:svg="http://www.w3.org/2000/svg"
173    xmlns="http://www.w3.org/2000/svg"
174    xmlns:xlink="http://www.w3.org/1999/xlink"
175    id="svg8"
176    version="1.1"
177    viewBox="0 0 210 297"
178    height="297mm"
179    width="210mm">
180
181
182 <g id="layer1">
183
184         }; # svg, insert rest of rect
185
186         print qq{<rect x="0" y="0" width="210" height="297" style="fill:#000000" id="high-contrast"/>} if $opt_invert;
187 }
188
189 my @later;
190
191 my $cols = {    # foreground background
192         txt  => [ '#000000', '#ffffff' ],
193         pins => [ '#ffffff', '#ff00ff' ],
194         vcc  => [ '#ff0000', '#ffff00' ],
195         gnd  => [ '#000000', '#00ffff' ],
196         i2c  => [ '#008800', '#ffcccc' ],
197         serial=>[ '#000088', '#ccffcc' ],
198         spi  => [ '#880000', '#ccccff' ],
199 };
200
201 sub swap_cols {
202         my $swap = shift;
203         die "$swap not found in ",dump($cols) unless $cols->{$swap};
204         my ( $c1, $c2 ) = @{ $cols->{$swap} };
205         $cols->{$swap} = [ $c2, $c1 ];
206 }
207
208 swap_cols 'txt' if $opt_invert;
209         
210
211 sub svg_style {
212         my ($name,$x,$y,$col) = @_;
213
214         return '' unless $opt_color;
215
216         $y -= $font_b; # shift box overlay to right vertical position based on font baseline
217
218         sub rect {
219                 my ($x,$y,$col,$fill) = @_;
220                 print qq{<rect x="$x" y="$y" height="2.54" width="}, $max_len[$col] * $font_w, qq{" style="fill:$fill;stroke:#ffffff;stroke-width:0.10" />\n};
221
222         }
223
224         if ( $name =~ m/^(\d+)$/ ) { # pins
225                 my $pin = $1;
226                 my ( $fg, $bg ) = @{ $cols->{pins} };
227                 if ( $pin == 1 ) {
228                         my $w  = $max_len[$col]*$font_w - 0.1;
229                         my $cx = $x + $w;
230                         my $cy = $y + 2.54;
231                         #print qq{<polygon points="$x,$y $cx,$y $x,$cy $x,$y" stroke="$fg" stroke-width="0.25" fill="$bg" />};
232                         #print qq{<polygon points="$x,$cy $cx,$cy $cx,$y $x,$cy" stroke="$bg" stroke-width="0.25" fill="$fg" />};
233                         print qq{<rect x="$x" y="$y" width="$w" height="2.54" stroke="$fg" stroke-width="0.3" fill="$bg" />};
234                         my ( $fg, $bg ) = @{ $cols->{txt} };
235                         print qq{<rect x="$x" y="$y" width="$w" height="2.54" rx="1" ry="1" stroke="$fg" stroke-width="0.3" fill="$bg" />};
236                 } else {
237                         rect $x,$y,$col,$fg;
238                 }
239                 return qq{ style="fill:$bg"};
240         }
241
242         if ( $name =~ m/(VCC|3V3|3.3V|5v)/i ) {
243                 my ($fg,$bg) = @{ $cols->{vcc} };
244                 rect $x,$y,$col,$bg;
245                 return qq{ style="fill:$fg"};
246         } elsif ( $name =~ m/(G(ND|Round)|VSS|0v)/i ) {
247                 my ($fg,$bg) = @{ $cols->{gnd} };
248                 rect $x,$y,$col,$bg;
249                 return qq{ style="fill:$fg"};
250         } elsif ( $name =~ m/\[(\w+)/ ) { # kernel
251                 my $dev = $1;
252                 my ($fg,$bg) = @{ $cols->{txt} };
253                 $dev = 'serial' if $dev =~ m/^tty/;
254                 ($fg,$bg) = @{ $cols->{$dev} } if exists $cols->{$dev};
255                 rect $x,$y,$col,$bg;
256                 return qq{ style="fill:$fg"};
257         } else {
258                 my ( $fg, $bg ) = @{ $cols->{txt} };
259                 rect $x,$y,$col,$bg;
260                 #return qq{ style="fill:$fg"};
261                 return '';
262         }
263 }
264
265 my $alt_col = 0;
266
267 my @cols_order = ( 0,1,2,3 );
268 my @cols_align = ( '','-','','-' ); # sprintf prefix
269
270 my @cols_shuffle = @cols_order;
271
272 if ( $opt_edge ) {
273         # pins outside on the right
274         @cols_shuffle = ( 0,1,3,2 ) if $opt_edge;
275         @cols_align = ( '-','-','','' );
276 } elsif ( $opt_middle ) {
277         # pins in middle
278         @cols_shuffle = ( 1,0,2,3 );
279         @cols_align = ( '','','-','-' );
280 }
281
282 sub cols_shuffle {
283         my ( $what, $order ) = @_;
284         my $new = [];
285         foreach my $i ( 0 .. $#$what ) {
286                 $new->[$i] = $what->[ $order->[$i] ];
287         }
288         warn "# cols_shuffle what=",dump($what)," order=",dump($order)," new=",dump($new);
289         return @$new;
290 }
291
292 @cols_order = cols_shuffle( \@cols_order, \@cols_shuffle );
293 @max_len    = cols_shuffle( \@max_len,    \@cols_shuffle );
294
295 warn "# cols_order = ",dump( \@cols_order );
296 warn "# cols_align = ",dump( \@cols_align );
297
298 my $fmt = "%$cols_align[0]$max_len[0]s %$cols_align[1]$max_len[1]s %$cols_align[2]$max_len[2]s %$cols_align[3]$max_len[3]s\n";
299
300
301 # cut marks
302 my ($fg,$bg) = @{ $cols->{txt} };
303 my $line_fmt = qq{<line x1="%s" y1="%s" x2="%s" y2="%s" style="stroke:$fg;stroke-width:0.10;fill:$bg" />\n};
304
305 my @cut_marks;
306 sub cut_mark {
307         my ($x,$y) = @_;
308         return unless $opt_svg;
309         push @cut_marks, sprintf($line_fmt, $x-5, $y-$font_b,   $x+5, $y-$font_b);
310         push @cut_marks, sprintf($line_fmt, $x,   $y-$font_b-5, $x,   $y-$font_b+5);
311 }
312 #cut_mark $x, $y;
313 my $max_x = $x;
314 $max_x += $max_len[$_] * $font_w foreach ( 0 .. 3 );
315 #cut_mark $max_x, $y;
316
317 sub line {
318         my ($x,$y,$max_x) = @_;
319         push @cut_marks, sprintf($line_fmt, $x, $y-$font_b, $max_x, $y-$font_b);
320 }
321
322
323 my $last_cut_mark = 0;
324
325 foreach my $i ( 0 .. $#line_parts ) {
326         $i = $#line_parts - $i if $opt_vertical;
327         my $line = $line_parts[$i];
328
329         if ( $opt_svg ) {
330
331                 # not a minimal two column pin description
332                 if ( ! exists $line->[1] ) {
333                         $last_cut_mark = 1 if $line->[0] =~ m/^##/; # skip comments
334
335                         # before first empty line
336                         if ( $last_cut_mark == 0 ) {
337                                 cut_mark $x, $y;
338                                 cut_mark $max_x, $y;
339                                 $last_cut_mark = 1;
340                                 line $x, $y, $max_x if $opt_lines;
341                                 $y += 15; # make spacing between pinouts
342                         }
343                 } elsif ( $last_cut_mark ) {
344                         # first full line
345                         cut_mark $x, $y;
346                         cut_mark $max_x, $y;
347                         $last_cut_mark = 0;
348                 } else {
349                         #warn "CUTMARK no magic";
350                 }
351
352                 line $x, $y, $max_x if $opt_lines && exists $line->[1];
353
354                 my ($fg,$bg) = @{ $cols->{txt} };
355                 my $tspan = qq{<tspan x="$x" y="$y" style="line-height:2.54;fill:$fg;stroke:none;">\n};
356
357                 my $x_pos = $x;
358                 foreach my $i ( 0 .. $#cols_order ) {
359                         my $order = $cols_order[$i];
360                         next unless $line->[$order];
361
362                         my $text_anchor = 'middle';
363                         my $len = $max_len[$i];
364                         my $x2 = $x_pos + ( $len * $font_w ) / 2;
365                         # is this comment?
366                         if ( $#$line == 0 && $line->[$order] =~ s/^#+s*// ) {
367                                 # comment, center over whole width
368                                 $len = length($line->[$order]);
369                                 $x2 = $x + (($max_x-$x)/2); # middle
370                                 $tspan .= qq{\t<tspan x="$x2" text-anchor="$text_anchor"}.sprintf( '>%' . $cols_align[$i] . $len . 's</tspan>', $line->[0]);
371                         } else {
372                                 $tspan .= qq{\t<tspan x="$x2" text-anchor="$text_anchor"}.svg_style($line->[$order],$x_pos,$y,$i).sprintf( '>%' . $cols_align[$i] . $len . 's</tspan>', $line->[$order]);
373                         }
374                         $x_pos += $len * $font_w;
375                 }
376
377                 $tspan .= qq{\n</tspan>\n};
378                 push @later,sprintf $tspan, @$line;
379                 $y += 2.54;
380
381                 # swap pin colors for line stripe
382                 if ( $opt_zebra ) {
383                         swap_cols $_ foreach qw( pins txt );
384                 } else {
385                         swap_cols 'pins';
386                 }
387
388         } else {
389
390                 if ( $#$line == 0 ) {
391                         print $line->[0], "\n";
392                 } else {
393                         push @$line, '' while ($#$line < 3); # fill-in single row header
394                         printf $fmt, map { $line->[$_] } @cols_order;
395                 }
396
397         }
398 }
399
400 if ( $opt_svg ) {
401         cut_mark $x,$y;
402         cut_mark $max_x,$y;
403         line $x, $y, $max_x if $opt_lines;
404
405         print qq{
406     <text
407        id="text4506"
408        y="$x"
409        x="$y"
410        style="font-size:2.34px;line-height:2.54px;font-family:'Andale Mono';stroke:none"
411        xml:space="preserve">
412
413         }; #svg
414
415         print @later, qq{</text>\n}, @cut_marks, qq{</g>\n</svg>};
416
417 }
418
419 __DATA__
420 # Cubietech Cubieboard
421 ## U14 (Next to SATA connector)
422 ###     SPI0
423 48      PI13 (SPI0-MISO/UART6-RX/EINT25)        47      PI11 (SPI0-CLK/UART5-RX/EINT23)
424 46      PI12 (SPI0-MOSI/UART6-TX/EINT24)        45      PI10 (SPI0-CS/UART5-TX/EINT22)
425 ###     LCD
426 44      3.3V (nc in 2012-08-08)                 43      VCC-5V
427 42      Ground                                  41      SPDIF
428 40      PB10 (LCD0-SCK/LCD-PIO1)                39      PB11 (LCD0-SDA/LCD-PIO2)
429 38      Ground                                  37      PH7 (LCD0-BL-EN/LCD-PIO0/UART5-RX/EINT7)
430 36      XN_TP (TP-X2)                           35      YN_TP (TP-Y2)
431 34      XP_TP (TP-X1)                           33      YP_TP (TP-Y1)
432 32      PD25 (LCDDE)                            31      PB2 (PWM0)
433 30      PD26 (LCDHSYNC/VGA-HSYNC)               29      PD24 (LCDCLK)
434 28      PD23 (LCDD23)                           27      PD27 (LCDVSYNC/VGA-VSYNC)
435 26      PD21 (LCDD21)                           25      PD22 (LCDD22)
436 24      PD19 (LCDD19/LVDS1N3)                   23      PD20 (LCDD20)
437 22      PD17 (LCDD17/LVDS1NC)                   21      PD18 (LCDD18/LVDS1P3)
438 20      Ground                                  19      PD16 (LCDD16/LVDS1PC)
439 18      PD14 (LCDD14/LVDS1P2)                   17      PD15 (LCDD15/LVDS1N2)
440 16      PD12 (LCDD12/LVDS1P1)                   15      PD13 (LCDD13/LVDS1N1)
441 14      PD10 (LCDD10/LVDS1P0)                   13      PD11 (LCDD11/LVDS1N0)
442 12      PD8 (LCDD8/LVDS0P3)                     11      PD9 (LCDD9/LVDS0N3)
443 10      PD7 (LCDD7/LVDS0NC)                     9       Ground
444 8       PD5 (LCDD5/LVDS0N2)                     7       PD6 (LCDD6/LVDS0PC)
445 6       PD3 (LCDD3/LVDS0N1)                     5       PD4 (LCDD4/LNVS0P2)
446 4       PD1 (LCDD1/LVDS0N0)                     3       PD2 (LCDD2/LVDS0P1)
447 2       Ground                                  1       PD0 (LCDD0/LVDSP0)
448
449 ## U15 (Between Ethernet port and USB ports)
450 ### CSI1/TS
451 1       VCC-5V                                  2       PH15 (CSI1-PWR/EINT15)
452 3       CSI1-IO-2V8                             4       PH14 (CSI1-RST#/EINT14)
453 5       PG0 (CSI1-PCLK/SDC1-CMD)                6       PB18 (TWI1-SCK)
454 7       PB19 (TWI1-SDA)                         8       PG3 (CSI1-VSYNC/SDC1-D1)
455 9       PG2 (CSI1-HSYNC/SDC1-D0)                10      PG1 (CSI1-MCLK/SDC1-CLK)
456 11      PG4 (CSI1-D0/SDC1-D2)                   12      PG5 (CSI1-D1/SDC1-D3)
457 13      PG6 (CSI1-D2/UART3-TX)                  14      PG7 (CSI1-D3/UART3-RX)
458 15      PG8 (CSI1-D4/UART3-RTS)                 16      PG9 (CSI1-D5/UART3-CTS)
459 17      PG10 (CSI1-D6/UART4-TX)                 18      PG11 (CSI1-D7/UART4-RX)
460 19      Ground                                  20      Ground
461 ###     Analog SDIO3
462 21      FMINL                                   22      PI4 (SDC3-CMD)
463 23      FMINR                                   24      PI5 (SDC3-CLK)
464 25      Ground                                  26      PI6 (SDC3-D0)
465 27      VGA-R                                   28      PI7 (SDC3-D1)
466 29      VGA-G                                   30      PI8 (SDC3-D2)
467 31      VGA-B                                   32      PI9 (SDC3-D3)
468 ###     CSI0/TS
469 33      LCD1-VSYNC                              34      PE4 (CSI0-D0)
470 35      LCD1-HSYNC                              36      PE5 (CSI0-D1)
471 37      Ground                                  38      PE6 (CSI0-D2)
472 39      AVCC                                    40      PE7 (CSI0-D3)
473 41      LRADC0                                  42      PE8 (CSI0-D4)
474 43      CVBS                                    44      PE9 (CSI0-D5)
475 45      HPL                                     46      PE10 (CSI0-D6)
476 47      HPR                                     48      PE11 (CSI0-D7)
477
478 ## DEBUG serial (middle of board)
479 4       PB22 (UART0-TX)
480 3       PB23 (UART0-RX)
481 2       VCC-3V3
482 1       GND
483
484
485 # Lamobo R1
486 ## CON3 rpi DIP26-254
487 1       3.3v                    2       5v     
488 3       PB20 SDA.1              4       5V     
489 5       PB21 SCL.1              6       0v     
490 7       PI3 PWM1                8       PH0 UART3_TX
491 9       0v                      10      PH1 UART3_RX
492 11      PI19 UART2_RX           12      PH2
493 13      PI18 UART2_TX           14      0v     
494 15      PI17 UART2_CTS          16      PH21 CAN_RX 
495 17      3.3v                    18      PH20 CAN_TX 
496 19      PI12 SPI0_MOSI          20      0v     
497 21      PI13 SPI0_MISO          22      PI16 UART2_RTS   
498 23      PI11 SPI0_SCLK          24      PI10 SPI0_CS0    
499 25      0v                      26      PI14 SPI0_CS1
500
501 ## J13 DIP2-254
502 2       PB22 UART0_TX
503 1       PB23 UART0_RX
504
505 ## J12 DIP8-254
506 8       GND                     7       GND
507 6       PI20 UART7_TX           5       PH3
508 4       PI21 UART7_RX           3       PH5
509 2       3V3                     1       SATA-5V
510
511 # Raspberry Pi
512 1       3.3v                    2       5v
513 3       gpio2 (SDA.1)           4       5v
514 5       gpio3 (SCL.1)           6       0v
515 7       gpio4 (WPi 7)           8       gpio14  (TxD)
516 9       0v                      10      gpio15  (RxD)
517 11      gpio17 (WPi 0)          12      gpio18  (WPi 1)
518 13      gpio27 (WPi 2)          14      0v
519 15      gpio22 (WPi 3)          16      gpio23  (WPi 4)
520 17      3.3v                    18      gpio24  (WPi 5)
521 19      gpio10 (MOSI)           20      0v
522 21      gpio9 (MISO)            22      gpio25  (WPi 6) 
523 23      gpio11 (SCLK)           24      gpio8   (CE0)
524 25      0v                      26      gpio7   (CE1)
525 # Raspberry Pi 3 Model B Rev 1.2
526 27      gpio0 (SDA.0)           28      gpio1   (SCL.0)
527 29      gpio5 (WPi 21)          30      0v
528 31      gpio6 (WPi 22)          32      gpio12  (WPi 26)
529 33      gpio13 (WPi 23)         34      0v
530 35      gpio19 (WPi 24)         36      gpio16  (WPi 27)
531 37      gpio26 (WPi 25)         38      gpio20  (WPi 28)
532 39      0v                      40      gpio21  (WPi 29)
533