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