Revert "Remove support for xpp drivers."
[dahdi/linux.git] / drivers / dahdi / xpp / init_card_1_30
1 #! /usr/bin/perl -w
2 use strict;
3
4 # Make warnings fatal
5 local $SIG{__WARN__} = sub { die @_ };
6
7 #
8 # Written by Oron Peled <oron@actcom.co.il>
9 # Copyright (C) 2006, Xorcom
10 #
11 # All rights reserved.
12 #
13 # This program is free software; you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation; either version 2 of the License, or
16 # (at your option) any later version.
17 #
18 # See the file LICENSE in the top level of this tarball.
19 #
20
21 #
22 # $Id$
23 #
24 # Data format:
25 #       - A comment start with ';' or '#' until the end of line
26 #       - Blank lines are ignored
27 #       - Fields are whitespace separated (spaces or tabs)
28 #
29 # The fields are (in command line order):
30 #       1. SLIC select in decimal (range 0-7).
31 #          * is a special value which means ALL SLICS (only some registers
32 #          accept settings for ALL SLICS).
33 #       2. Command word:
34 #               - RD    Read Direct register.
35 #               - RS    Read Sub-register.
36 #               - WD    Write Direct register.
37 #               - WS    Write Sub-register.
38 #       3. Register number in hexadecimal.
39 #       4. Low data byte in hexadecimal. (for WD and WS commands).
40 #       5. High data byte in hexadecimal. (for WS command only).
41 #
42 #
43
44 package main;
45 use File::Basename;
46 use Getopt::Std;
47
48 my $program = basename("$0");
49 my $init_dir = dirname("$0");
50 BEGIN { $init_dir = dirname($0); unshift(@INC, "$init_dir"); }
51 use XppConfig $init_dir;
52 my $unit_id;
53 my %opts;
54 my $eeprom_release_201 = 0;
55
56 getopts('o:', \%opts);
57
58 my %settings;
59 $settings{debug} = 0;
60 $settings{fxs_skip_calib} = 0;
61 my $chipregs;
62 my $ring_registers;
63
64 sub logit {
65         print STDERR "$unit_id: @_\n";
66 }
67
68 sub debug {
69         logit @_ if $settings{debug};
70 }
71
72 # Arrange for error logging
73 if (-t STDERR) {
74         $unit_id = 'Interactive';
75         debug "Interactive startup";
76 } else {
77         $unit_id = "$ENV{XBUS_NAME}/UNIT-$ENV{UNIT_NUMBER}";
78         open (STDERR, "| logger -t $program -p kern.info") || die;
79         debug "Non Interactive startup";
80         foreach my $k (qw(
81                         XBUS_NAME
82                         XBUS_NUMBER
83                         XBUS_MODEL_STRING
84                         UNIT_NUMBER
85                         UNIT_TYPE
86                         UNIT_SUBUNITS
87                         UNIT_SUBUNITS_DIR
88                         XBUS_REVISION
89                         XBUS_CONNECTOR
90                         XBUS_LABEL)) {
91                 unless(defined $ENV{$k}) {
92                         logit "Missing ENV{$k}\n";
93                         die;
94                 }
95         }
96         logit "XBUS_MODEL_STRING='$ENV{XBUS_MODEL_STRING}'";
97         if ($ENV{XBUS_MODEL_STRING} =~ m{.*/.*/20.}) {
98                 $eeprom_release_201 = 1;
99         }
100         $chipregs = sprintf "/sys/bus/xpds/devices/%02d:%1d:0/chipregs",
101                 $ENV{XBUS_NUMBER}, $ENV{UNIT_NUMBER};
102         if(! -f $chipregs) {
103                 my $xpd_name = sprintf("XPD-%1d0", $ENV{UNIT_NUMBER});
104                 $chipregs = "/proc/xpp/$ENV{XBUS_NAME}/$xpd_name/chipregs";
105                 logit "OLD DRIVER: does not use /sys chipregs. Falling back to /proc"
106                         if -f $chipregs;
107         }
108         $ring_registers = sprintf "/sys/bus/xpds/devices/%02d:%1d:0/fxs_ring_registers",
109                 $ENV{XBUS_NUMBER}, $ENV{UNIT_NUMBER};
110         logit "OLD DRIVER: missing '$ring_registers' -- fallback to hard-coded defaults"
111                 unless -f $ring_registers;
112 }
113
114 sub set_output() {
115         my $output;
116
117         if($opts{o}) {
118                 $output = $opts{o};
119         } else {
120                 # No subunits in FXS (everything is subunit 0)
121                 $output = $chipregs;
122         }
123         open(REG, ">$output") || die "Failed to open '$output': $!\n";
124         my $oldfh = select REG;
125         main::logit "# Setting output" if $opts{o};
126         return $oldfh;
127 }
128
129 sub mysleep($) {
130         my $timeout = shift;
131         select(undef,undef,undef,$timeout);
132 }
133
134 package FXS;
135
136 sub gen {
137         my $fmt = shift;
138         $| = 1;
139         printf "$fmt\n", @_;
140 }
141
142 my @SlicNums = (0 .. 7);
143
144 sub write_to_slic_file($) {
145         my $write_str = shift;
146
147         open(SLICS,">$chipregs") or 
148                 die("Failed writing to chipregs file $chipregs");
149         print SLICS $write_str;
150         close(SLICS) or die "Failed writing '$write_str' to '$chipregs': $!";
151         main::mysleep(0.001);
152         
153 }
154
155 sub write_to_ring_register($) {
156         my $write_str = shift;
157
158         open(SLICS,">$ring_registers") or
159                 die("Failed writing to ring_registers file $ring_registers");
160         print SLICS $write_str;
161         close(SLICS) or die "Failed writing '$write_str' to '$ring_registers': $!";
162         main::mysleep(0.001);
163 }
164
165 sub read_reg($$$) {
166         my $read_slic = shift;
167         my $read_reg = shift;
168         my $direct = shift;
169         
170         write_to_slic_file(
171                 sprintf("%s R%s %02X", $read_slic, $direct, $read_reg));
172         my $retries = 10;
173         my @reply;
174         # If the command queue is long, we may need to wait...
175 WAIT_RESULTS:
176         {
177                 my @results;
178
179                 # The time to sleep is a tradeoff:
180                 #   - Too long is a waste of time.
181                 #   - Too short will cause many retries, wastes time.
182                 # So the current value (after trial and error) is...
183                 main::mysleep(0.013);
184                 open(SLICS,$chipregs) or 
185                         die("Failed reading from chipregs file $chipregs");
186                 while(<SLICS>){
187                         s/#.*//;
188                         next unless /\S/;
189                         @results = /^\s*(\d+)\s+[RW][DI]\s+([[:xdigit:]]+)\s+([[:xdigit:]]+)\s+([[:xdigit:]]*)/;
190                         if(@results != 4) {
191                                 main::logit "Failed reading from '$chipregs' ($read_slic,$read_reg,$direct)";
192                                 die;
193                         }
194                 }
195                 close(SLICS);
196                 my $reg = hex($results[1]);
197                 if($results[0] ne $read_slic || $reg ne $read_reg) {
198                         # We read obsolete values, need to wait some more
199                         if(--$retries) {
200                                 main::debug "$read_slic RD $read_reg -- retry ($results[0], $reg)";
201                                 redo WAIT_RESULTS;
202                         } else {
203                                 main::logit "Failed: $read_slic RD $read_reg returned $results[0], $reg";
204                                 die;
205                         }
206                 }
207                 # Good.
208                 @reply = (hex($results[2]), hex($results[3]));
209
210         }
211         if ($direct eq 'S') {
212                 return @reply;
213         } else {
214                 return $reply[0];
215         }
216 }
217
218 # TODO: rearange arguments
219 sub write_reg{#($$$$$) {
220         my $read_slic = shift;
221         my $read_reg = shift;
222         my $direct = shift;
223         my $reg_val_low = shift;
224         my $reg_val_hi = shift;
225         
226         my $str  = sprintf "%s W%s %02X %02X", 
227                 $read_slic, $direct, $read_reg, $reg_val_low;
228         if ($direct eq 'S') {
229                 $str .= sprintf " %02X", $reg_val_hi;
230         }
231         write_to_slic_file($str);
232 }
233
234 sub log_calib_params() {
235         for my $i (100 .. 107) {
236                 my $line="Calib Reg $i:  ";
237                 for my $slic (@SlicNums) {
238                         $line .= " ".read_reg($slic, $i, 'D');
239                 }
240                 main::debug($line);
241         }
242 }
243
244 sub init_indirect_registers() {
245         return write_to_slic_file("#
246 *       WS      1E      00      C2      55
247 *       WS      1E      01      E6      51
248 *       WS      1E      02      85      4B
249 *       WS      1E      03      37      49
250                           
251 *       WS      1E      04      33      33
252 *       WS      1E      05      02      02
253 *       WS      1E      06      02      02
254 *       WS      1E      07      98      01
255                           
256 *       WS      1E      08      98      01
257 *       WS      1E      09      11      06
258 *       WS      1E      0A      02      02
259 *       WS      1E      0B      E5      00
260                           
261 *       WS      1E      0C      1C      0A
262 *       WS      1E      0D      30      7B
263 *       WS      1E      0E      63      00
264 *       WS      1E      0F      00      00
265                           
266 *       WS      1E      10      70      78
267 *       WS      1E      11      7D      00
268 *       WS      1E      12      00      00
269 *       WS      1E      13      00      00
270                           
271 *       WS      1E      14      FD      7E
272 *       WS      1E      15      77      01
273 *       WS      1E      16      00      00
274 *       WS      1E      17      00      20
275                           
276 *       WS      1E      18      00      20
277 *       WS      1E      19      00      00
278 *       WS      1E      1A      00      20
279 *       WS      1E      1B      00      40
280                           
281 *       WS      1E      1C      00      10
282 *       WS      1E      1D      00      36
283 *       WS      1E      1E      00      10
284 *       WS      1E      1F      00      02
285                           
286 *       WS      1E      20      C0      07
287 *       WS      1E      21      6F      37
288 *       WS      1E      22      80      1B
289 *       WS      1E      23      00      80
290                           
291 *       WS      1E      24      00      08
292 *       WS      1E      25      00      08
293 *       WS      1E      26      00      08
294 *       WS      1E      27      00      08
295                           
296 *       WS      1E      28      00      00
297 *       WS      1E      2B      00      08 # LCRTL = 5.08 mA
298                           
299 *       WS      1E      63      DA      00
300 *       WS      1E      64      60      6B
301 *       WS      1E      65      74      00
302 *       WS      1E      66      C0      79
303                           
304 *       WS      1E      67      20      11
305 *       WS      1E      68      E0      3B      
306 #");
307 }
308
309 sub init_early_direct_regs() {
310         my $lbv = ($eeprom_release_201) ? "20" : "10";
311         my $vcm = ($eeprom_release_201) ? "02" : "03";
312
313         return write_to_slic_file("#
314 *       WD      08      00      # Audio Path Loopback Control
315 *       WD      6C      01
316 *       WD      4A      34      # High Battery Voltage
317 *       WD      4B      $lbv    # Low Battery Voltage
318 *       WD      49      $vcm    # Common Mode Voltage (VCM)
319 *       WD      40      00      # Line Feed Control
320 #")
321 }
322
323 my @FilterParams = ();
324
325 sub save_indirect_filter_params() {
326         for my $slic (@SlicNums) {
327                 for my $reg (35 .. 39) {
328                         $FilterParams[$slic][$reg] = 
329                                 [read_reg($slic, $reg, 'S')];
330                         write_reg($slic, $reg, 'S', 0, 0x80);
331                 }
332         }
333         
334 }
335
336 sub restore_indirect_filter_params() {
337         for my $slic (@SlicNums) {
338                 for my $reg (35 .. 39) {
339                         write_reg($slic, $reg, 'S', 
340                                 @{$FilterParams[$slic][$reg]});
341                 }
342         }
343 }
344
345 my $ManualCalibrationSleepTime = 0.04; # 40ms
346
347 sub manual_calibrate_loop($$) {
348         my $write_reg = shift;
349         my $read_reg = shift;
350         my @curr_slics = @SlicNums;
351
352         # initialize counters
353         my @slic_counters = map { 0x1F } @curr_slics;
354
355         # wait until all slics have finished calibration, or for timeout
356         while (@curr_slics) {
357                 my $debug_calib_str = "ManualCalib:: ";
358                 my @next_slics;
359
360                 for my $slic (@curr_slics) {
361                         write_reg($slic,$write_reg,'D',$slic_counters[$slic]);
362                 }
363                 main::mysleep $ManualCalibrationSleepTime;
364                 for my $slic (@curr_slics) {
365                         my $value = read_reg($slic, $read_reg, 'D');
366                         $debug_calib_str .= sprintf " [%d:%d:%X]",
367                                 $slic, $slic_counters[$slic], $value;
368                         next if $value == 0;    # This one is calibrated.
369                         if ($slic_counters[$slic] > 0) {
370                                 $slic_counters[$slic]--;
371                                 push(@next_slics, $slic);
372                         } else {
373                                 main::logit("ERROR: SLIC $slic reached 0 during manual calibration");
374                         }
375                 }
376                 @curr_slics = @next_slics;
377                 main::debug($debug_calib_str);
378         }
379         main::debug("No more slics to calibrate");
380 }
381
382 sub manual_calibrate() {
383         manual_calibrate_loop(98, 88);
384         manual_calibrate_loop(99, 89);
385 }
386
387 sub auto_calibrate($$) {
388         my $calib_96 = shift;
389         my $calib_97 = shift;
390
391         #log_calib_params();
392         # start calibration:
393         for my $slic(@SlicNums) {
394                 write_to_slic_file(
395                         sprintf
396                                 "$slic WD 61 %02X\n".
397                                 "$slic WD 60 %02X\n".
398                                 "", $calib_97, $calib_96
399                                 
400                 );
401         }
402
403         # wait until all slics have finished calibration, or for timeout
404         # time periods in seconds:
405         my $sleep_time = 0.001;
406         my $timeout_time = 0.600; # Maximum from the spec
407         my @curr_slics = @SlicNums;
408         my $sleep_cnt = 0;
409 CALIB_LOOP:
410         while(1) {
411                 main::mysleep($sleep_time);
412                 my @next_slics;
413                 for my $slic (@curr_slics) {
414                         main::debug("checking slic $slic");
415                         my $val = read_reg($slic, 96, 'D');
416                         push(@next_slics, $slic) if $val != 0;
417                 }
418                 @curr_slics = @next_slics;
419                 last unless @curr_slics;
420                 if ($sleep_cnt * $sleep_time > $timeout_time) {
421                         main::logit("Auto Calibration: Exiting on timeout: $timeout_time.");
422                         last CALIB_LOOP;
423                 }
424                 main::debug("auto_calibrate not done yet($sleep_cnt): @curr_slics");
425                 $sleep_cnt++;
426         }
427         #log_calib_params();
428 }
429
430 sub calibrate_slics() {
431         main::debug "Calibrating '$0'";
432         auto_calibrate(0x40, 0x1E);
433         main::debug "after auto_calibrate";
434         manual_calibrate();
435         main::debug "after manul_calibrate";
436         auto_calibrate(0x40, 0x01);
437         main::debug "after auto_calibrate 2";
438         main::debug "Continue '$0'";
439 }
440
441 sub read_defaults() {
442         if(XppConfig::read_config(\%settings)) {
443                 main::logit "Defaults from $settings{xppconf}";
444         } else {
445                 main::logit "No defaults file, use hard-coded defaults.";
446         }
447 }
448
449 # Try to identify which slics are valid
450 sub check_slics() {
451         my @slics;
452         foreach my $slic (0 .. 7) {
453                 my $value = read_reg($slic, 0, 'D');
454                 push(@slics, $slic) if $value != 0xFF;
455         }
456         main::logit "Found " . scalar(@slics) . " SLICS (@slics)";
457         return @slics;
458 }
459
460 sub overwrite_ring_registers() {
461         write_to_ring_register("NEON 0x33 0x12");
462 }
463
464 package main;
465
466 main::debug "Starting '$0'";
467
468 FXS::read_defaults;
469 @SlicNums = FXS::check_slics;
470 main::debug "before init_indirect_registers";
471 FXS::init_indirect_registers();
472 main::debug "after init_indirect_registers";
473 FXS::init_early_direct_regs();
474 main::debug "after init_early_direct_regs";
475 if($settings{fxs_skip_calib}) {
476         main::logit "==== WARNING: SKIPPED SLIC CALIBRATION =====";
477 } else {
478         FXS::calibrate_slics;
479 }
480 set_output;
481 while(<DATA>) {
482         chomp;
483         s/[#;].*$//;            # remove comments
484         s/^\s+//;               # trim whitespace
485         s/\s+$//;               # trim whitespace
486         s/\t+/ /g;              # replace tabs with spaces (for logs)
487         next unless /\S/;       # Skip empty lines
488         main::debug "writing: '$_'";
489         print "$_\n";
490 }
491 close REG;
492 FXS::overwrite_ring_registers();
493
494 main::debug "Ending '$0'";
495 close STDERR;
496 exit 0;
497
498 # ----------------------------------==== 8-channel FXS unit initialization ===-----------------------------------------
499
500 __DATA__
501
502 # Flush out energy accumulators
503 *       WS      1E      58      00 00
504 *       WS      1E      59      00 00
505 *       WS      1E      5A      00 00
506 *       WS      1E      5B      00 00
507 *       WS      1E      5C      00 00
508 *       WS      1E      5D      00 00
509 *       WS      1E      5E      00 00
510 *       WS      1E      5F      00 00
511
512 *       WS      1E      61      00 00
513
514 *       WS      1E      C1      00 00
515 *       WS      1E      C2      00 00
516 *       WS      1E      C3      00 00
517 *       WS      1E      C4      00 00
518 *       WS      1E      C5      00 00
519 *       WS      1E      C6      00 00
520 *       WS      1E      C7      00 00
521 *       WS      1E      C8      00 00
522 *       WS      1E      C9      00 00
523 *       WS      1E      CA      00 00
524 *       WS      1E      CB      00 00
525 *       WS      1E      CC      00 00
526 *       WS      1E      CD      00 00
527 *       WS      1E      CE      00 00
528 *       WS      1E      CF      00 00
529 *       WS      1E      D0      00 00
530 *       WS      1E      D1      00 00
531 *       WS      1E      D2      00 00
532 *       WS      1E      D3      00 00
533
534 # Clear and disable interrupts
535 *       WD      12      FF
536 *       WD      13      FF
537 *       WD      14      FF
538 *       WD      15      00
539 *       WD      16      00
540 *       WD      17      00
541                 
542 ## Mode(8-bit,u-Law,1 PCLK ) 
543 *       WD      01      08      # Disable PCM transfers
544
545 # Setting of SLICs offsets
546 # New card initialization
547
548 *       WD      03      00
549 *       WD      05      00
550
551 0       WD      02      00
552 0       WD      04      00
553 0       WD      01      28      # Enable PCM transfers
554 1       WD      02      08
555 1       WD      04      08
556 1       WD      01      28
557 2       WD      02      10
558 2       WD      04      10
559 2       WD      01      28
560 3       WD      02      18
561 3       WD      04      18
562 3       WD      01      28
563 4       WD      02      20
564 4       WD      04      20
565 4       WD      01      28
566 5       WD      02      28
567 5       WD      04      28
568 5       WD      01      28
569 6       WD      02      30
570 6       WD      04      30
571 6       WD      01      28
572 7       WD      02      38
573 7       WD      04      38
574 7       WD      01      28
575
576 # Audio path. (also initialize 0A and 0B here if necessary)
577 *       WD      08 00
578 *       WD      09 00
579 *       WD      0A 08
580 *       WD      0B 33
581
582 #------ Metering tone
583 *       WD      2C      00      # Timer dL
584 *       WD      2D      03      # Timer dH
585 *       WS      1E      17      61 15   # Amplitue Ramp-up
586 *       WS      1E      18      61 15   # Max Amplitude
587 *       WS      1E      19      FB 30   # Frequency
588
589 # Ring regs are set by driver
590
591 # Automatic/Manual Control: defaults but:
592 #       Cancel AOPN - Power Alarm
593 #       Cancel ABAT - Battery Feed Automatic Select
594 *       WD      43 16
595
596 # Loop Closure Debounce Interval
597 *       WD      45 0A
598
599 # Ring Detect Debounce Interval
600 *       WD      46 47
601
602 # Battery Feed Control: Battery low (DCSW low)
603 *       WD      42 00
604
605 # Loop Current Limit
606 *       WD      47 00
607
608 # On-Hook Line Voltage (VOC)
609 *       WD      48 20
610
611 *       WS      1E      23      00 80
612 *       WS      1E      24      20 03
613 *       WS      1E      25      8C 00
614 *       WS      1E      26      00 00
615 *       WS      1E      27      10 00
616
617 *       WD      0E 00