init_card_1_30 12 KB

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