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