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