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