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