#! /usr/bin/perl -w use strict; # Make warnings fatal local $SIG{__WARN__} = sub { die @_ }; # # Written by Oron Peled # Copyright (C) 2017, Xorcom # # All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # See the file LICENSE in the top level of this tarball. # # # $Id$ # # Data format: # - A comment start with ';' or '#' until the end of line # - Blank lines are ignored # - Fields are whitespace separated (spaces or tabs) # # The fields are (in command line order): # 1. SLIC select in decimal (range 0-7). # * is a special value which means ALL SLICS (only some registers # accept settings for ALL SLICS). # 2. Command word: # - RD Read Direct register. # - RS Read Sub-register. # - WD Write Direct register. # - WS Write Sub-register. # 3. Register number in hexadecimal. # 4. Low data byte in hexadecimal. (for WD and WS commands). # 5. High data byte in hexadecimal. (for WS command only). # # package main; use File::Basename; use Getopt::Std; my $program = basename("$0"); my $init_dir = dirname("$0"); BEGIN { $init_dir = dirname($0); unshift(@INC, "$init_dir"); } use XppConfig $init_dir; my $unit_id; my %opts; my $eeprom_release_201 = 0; getopts('o:', \%opts); my %settings; $settings{debug} = 0; $settings{fxs_skip_calib} = 0; my $chipregs; my $command; my $expander_cmd; my $ring_registers; sub logit { print STDERR "$unit_id: @_\n"; } sub debug { logit @_ if $settings{debug}; } # Arrange for error logging if (-t STDERR) { $unit_id = 'Interactive'; debug "Interactive startup"; } else { $unit_id = "$ENV{XBUS_NAME}/UNIT-$ENV{UNIT_NUMBER}"; open (STDERR, "| logger -t $program -p kern.info") || die; debug "Non Interactive startup"; foreach my $k (qw( XBUS_NAME XBUS_NUMBER XBUS_MODEL_STRING UNIT_NUMBER UNIT_TYPE UNIT_SUBUNITS UNIT_SUBUNITS_DIR XBUS_REVISION XBUS_CONNECTOR XBUS_LABEL)) { unless(defined $ENV{$k}) { logit "Missing ENV{$k}\n"; die; } } logit "XBUS_MODEL_STRING='$ENV{XBUS_MODEL_STRING}'"; if ($ENV{XBUS_MODEL_STRING} =~ m{.*/.*/201}) { $eeprom_release_201 = 1; } $chipregs = sprintf "/sys/bus/xpds/devices/%02d:%1d:0/chipregs", $ENV{XBUS_NUMBER}, $ENV{UNIT_NUMBER}; $command = "/proc/xpp/$ENV{XBUS_NAME}/command"; if(! -f $chipregs) { my $xpd_name = sprintf("XPD-%1d0", $ENV{UNIT_NUMBER}); $chipregs = "/proc/xpp/$ENV{XBUS_NAME}/$xpd_name/chipregs"; logit "OLD DRIVER: does not use /sys chipregs. Falling back to /proc" if -f $chipregs; } $ring_registers = sprintf "/sys/bus/xpds/devices/%02d:%1d:0/fxs_ring_registers", $ENV{XBUS_NUMBER}, $ENV{UNIT_NUMBER}; logit "OLD DRIVER: missing '$ring_registers' -- fallback to hard-coded defaults" unless -f $ring_registers; } sub set_output() { my $output; if($opts{o}) { $output = $opts{o}; } else { # No subunits in FXS (everything is subunit 0) $output = $chipregs; } open(REG, ">$output") || die "Failed to open '$output': $!\n"; my $oldfh = select REG; main::logit "# Setting output" if $opts{o}; return $oldfh; } sub mysleep($) { my $timeout = shift; select(undef,undef,undef,$timeout); } package FXS; sub gen { my $fmt = shift; $| = 1; printf "$fmt\n", @_; } my @SlicNums; sub write_to_slic_file($) { my $write_str = shift; open(SLICS,">$chipregs") or die("Failed writing to chipregs file $chipregs"); print SLICS $write_str; close(SLICS) or die "Failed writing '$write_str' to '$chipregs': $!"; main::mysleep(0.001); } sub write_to_ring_register($) { my $write_str = shift; open(SLICS,">$ring_registers") or die("Failed writing to ring_registers file $ring_registers"); print SLICS $write_str; close(SLICS) or die "Failed writing '$write_str' to '$ring_registers': $!"; main::mysleep(0.001); } sub read_reg($$$;$) { my $slic = shift; my $addr = shift; my $direct = shift; my $bits_shift = shift; $bits_shift = 0 unless defined $bits_shift; my $addr_low; my $addr_high; main::debug("read_reg: $slic, $addr, $direct"); $addr_low = $addr & 0xFF; $addr_high = $addr >> 8; my $str; if ($direct eq 'D') { $str = sprintf "%s RD %02X", $slic, $addr_low; } else { $str = sprintf "%s RR %02X %02X", $slic, $addr_low, $addr_high; } write_to_slic_file($str); my $retries = 10; my $reply = ""; # If the command queue is long, we may need to wait... WAIT_RESULTS: { my @results; # The time to sleep is a tradeoff: # - Too long is a waste of time. # - Too short will cause many retries, wastes time. # So the current value (after trial and error) is... main::mysleep(0.013); open(SLICS,$chipregs) or die("Failed reading from chipregs file $chipregs"); while(){ s/#.*//; next unless /\S/; if ($direct eq 'D') { @results = /^\s*(\d+)\s+[RW][DR]\s+([[:xdigit:]]+)\s+([[:xdigit:]]+)\s+([[:xdigit:]]*)/; } else { @results = /^\s*(\d+)\s+[RW][DR]\s+([[:xdigit:]]+)\s+([[:xdigit:]]+)\s+([[:xdigit:]]+)\s+([[:xdigit:]]+)\s+([[:xdigit:]]+)\s+([[:xdigit:]]*)/; } if(($direct eq 'D' && @results != 4) || ($direct eq 'R' && @results != 7)) { main::logit "Failed reading from '$chipregs' ($direct, $slic,$addr)"; die; } } close(SLICS); my $read_addr; if ($direct eq 'D') { $read_addr = hex($results[1]); } else { $read_addr = hex(sprintf "0x$results[2]$results[1]"); } if($results[0] ne $slic || $read_addr ne $addr) { # We read obsolete values, need to wait some more if(--$retries) { main::debug "$slic R$direct $addr -- retry (@results, $read_addr)"; redo WAIT_RESULTS; } else { main::logit "Failed: $slic R$direct $addr returned @results, $addr"; die; } } # Good. $results[0] = ""; $results[1] = ""; if ($direct eq 'R') { $results[2] = ""; } foreach my $val (@results) { $reply = sprintf("%s%s", $val, $reply); } $reply = hex(sprintf("0x%s", $reply)); } return $reply >> $bits_shift; } # TODO: rearange arguments sub write_reg{#($$$) { my $slic = shift; my $reg = shift; my $val = shift; my $str = sprintf "%s WD %02X %02X", $slic, $reg, $val; main::debug("write_reg: $slic, $reg, $val"); write_to_slic_file($str); } # # BITS: # 3 - port num # 1 - read/write # 1 - Register / RAM # 11 - address # 29 - data sub write_ram($$$$) { my $slic = shift; my $addr = shift; my $value = shift; my $bits_shift = shift; my $addr_low; my $addr_high; my $value_0; my $value_1; my $value_2; my $value_3; my $log_output = sprintf("write_ram: %s, %4d, 0x%08X", $slic, $addr, $value); main::debug($log_output); $value = $value << $bits_shift; $addr_low = $addr & 0xFF; $addr_high = $addr >> 8; $value_0 = $value & 0xFF; $value >>= 8; $value_1 = $value & 0xFF; $value >>= 8; $value_2 = $value & 0xFF; $value >>= 8; $value_3 = $value & 0xFF; my $str = sprintf "%s WR %02X %02X %02X %02X %02X %02X", $slic, $addr_low, $addr_high, $value_0, $value_1, $value_2, $value_3; write_to_slic_file($str); } sub set_user_mode { my $slic = shift; my $on = shift; my $current = read_reg($slic, 0x7E, 'D'); $current &= 0x1; $on &= 0x1; main::debug("::set_user_mode($slic, $on): " . $current . " -> " . $on . "\n"); return 1 if $current == $on; write_reg($slic, 126, 0x02); write_reg($slic, 126, 0x08); write_reg($slic, 126, 0x0E); write_reg($slic, 126, 0x00); return 1; } sub init_early() { # I/O Expander initialization $expander_cmd = sprintf "echo 0A 00 0F %1d0 05 08 00 00 00 FF > %s", $ENV{UNIT_NUMBER}, $command; # enable outputs for Greed LEDs system("$expander_cmd"); $expander_cmd = sprintf "echo 0A 00 0F %1d0 05 08 01 00 AA 55 > %s", $ENV{UNIT_NUMBER}, $command; # enable outputs for Red LEDs and relays system("$expander_cmd"); $expander_cmd = sprintf "echo 0A 00 0F %1d0 05 08 0D 00 AA AA > %s", $ENV{UNIT_NUMBER}, $command; # enable pull-ups for inputs system("$expander_cmd"); } sub load_patch($) { my $slics_ref = shift; my @slics = @{ $slics_ref }; my $slic; main::debug "Loading patch based on si3226x_patch_C_TSS_ISO_2014JUN18.c"; foreach $slic (@slics) { FXS::set_user_mode($slic, 1); # Turn on user mode } write_reg('*', 81, 0x00); # JMPEN, disable the patch main::debug "=====Patch data======"; my @patch_data_array = ( 141541, 540867, 141541, 543427, 141541, 553155, 141541, 577731, 141541, 579779, 141541, 581315, 141541, 592579, 141541, 633027, 141541, 637635, 141541, 639683, 141541, 650947, 141541, 651459, 141541, 651971, 141541, 652483, 141541, 652995, 141541, 653507, 736, 491712, 452200, 141541, 491200, 5733, 524290, 142565, 550083, 3685, 519266, 5220, 144098, 550083, 3685, 524291, 141541, 551619, 5221, 3682, 524292, 5, 141541, 135362, 98021, 727745, 474213, 17637, 557251, 101093, 557251, 473701, 515653, 843365, 188002, 843355, 145125, 560835, 524290, 660069, 518053, 517224, 518244, 142565, 564419, 524288, 521733, 843365, 188002, 524315, 145125, 568003, 843365, 522850, 523387, 147685, 573123, 522363, 145125, 575171, 521826, 141541, 575683, 518757, 521826, 141541, 575683, 521824, 522245, 522338, 141541, 716481, 173669, 523845, 141541, 730304, 523877, 141541, 690368, 614117, 588995, 457221, 558181, 457122, 457333, 143077, 588995, 144608, 587971, 524292, 141541, 588483, 524304, 671746, 558181, 410018, 437365, 143586, 100034, 141541, 98498, 550117, 619715, 558181, 410018, 403061, 143077, 619715, 524290, 143589, 608963, 402533, 524290, 400901, 29189, 431717, 408133, 432741, 406085, 392805, 407621, 792165, 405573, 406629, 792133, 408677, 431680, 432645, 409189, 392785, 402949, 141541, 630979, 560741, 400482, 398852, 143077, 615107, 402533, 398946, 400901, 29186, 400389, 141541, 630979, 400997, 262242, 143077, 618691, 524291, 400901, 29189, 141541, 630979, 558181, 407458, 524309, 694789, 558085, 694789, 403045, 524290, 143077, 630979, 405605, 792133, 408165, 431685, 406117, 432709, 407653, 392768, 402949, 694789, 560645, 694789, 743525, 119426, 141541, 1003201, 560741, 524290, 143584, 636099, 141541, 191682, 694789, 141541, 859842, 171109, 170565, 141541, 963776, 524291, 144613, 641731, 199685, 667365, 644803, 431717, 197189, 136805, 198725, 170597, 262242, 524291, 144613, 647875, 170501, 667365, 886464, 136805, 180293, 141541, 886464, 524293, 524293, 524293, 524293, 524293, 524293, ); write_ram('*', 1358, 0x00000000, 3); # PRAM_ADDR, reset the patch RAM address foreach my $val (@patch_data_array) { write_ram('*', 1359, $val, 12); # PRAM_DATA, shl 12, addr auto inc } main::debug "=====Patch entries======"; # lowest 8 entries are registers my @patch_entries_array = ( 950, 4347, 3431, 1425, 1347, 4287, 4006, 4469, 1881, 1720, ); my $jmp_table_low = 82; my $jmp_table_high = 1597; foreach my $val (@patch_entries_array) { last if $val == 0; if ($jmp_table_low <= 97) { write_reg('*', $jmp_table_low, $val & 0xFF);# JMPnLO $jmp_table_low++; write_reg('*', $jmp_table_low, $val >> 8 );# JMPnHI $jmp_table_low++; } else { write_ram('*', $jmp_table_high, $val & 0x1FFF, 3); # shl 3 $jmp_table_high++; } } write_ram('*', 448, 0x06182014, 3); # PATCH_ID, shl 3, a unique code which is a hash of the patch. main::debug "=====Patch support======"; my @patch_support_addr_array = ( 800, 795, 799, 798, 794, 787, 786, 782, 892, 893, 925, 926, 1014, 1020, 1021, 1022, 333, 334, 352, ); my @patch_support_data_array = ( 0x200000, 0x7D80000, 0x69580EA, 0x82C58C, 0x1500000, 0x0, 0x320000, 0x0, 0x400000, 0x0, 0xA00000, 0x1F00000, 0x2D8000, 0x0, 0x2075F60, 0x220335B, 0x0, 0x0, 0x0, ); for (my $i = 0; $i < @patch_support_addr_array; $i++) { my $addr = $patch_support_addr_array[$i]; my $val = $patch_support_data_array[$i]; write_ram('*', $addr, $val, 3); } if ($settings{debug}) { foreach $slic (@slics) { main::debug "=====Verify patch======="; my $read_val; # PRAM_ADDR, reset the patch RAM address: write_ram($slic, 1358, 0x00000000, 3); foreach my $val (@patch_data_array) { # PRAM_DATA, shr 12, addr auto inc $read_val = read_reg($slic, 1359, 'R', 12); if ($val != $read_val) { printf "0x%X =? 0x%X\n", $val, $read_val; printf "patch verification failed\n"; exit 0; } } main::debug "Patch has been verified!"; main::debug "=====Verify patch entries======="; $jmp_table_low = 82; $jmp_table_high = 1597; foreach my $val (@patch_entries_array) { last if $val == 0; if ($jmp_table_low <= 97) { $read_val = read_reg($slic, $jmp_table_low, 'D');# JMPnLO $jmp_table_low++; $read_val |= read_reg($slic, $jmp_table_low, 'D') << 8;# JMPnHI $jmp_table_low++; } else { $read_val = read_reg($slic, $jmp_table_high, 'R', 3); # PRAM_DATA, shr 3 $read_val &= 0x1FFF; $jmp_table_high++; } if ($val != $read_val) { printf "0x%X =? 0x%X\n", $val, $read_val; printf "patch entries verification failed\n"; exit 0; } } main::debug "Patch entries has been verified!"; main::debug "=====Verify patch support======="; for (my $i = 0; $i < @patch_support_addr_array; $i++) { my $addr = $patch_support_addr_array[$i]; my $val = $patch_support_data_array[$i]; $read_val = read_reg($slic, $addr, 'R', 3); if ($val != $read_val) { printf "0x%X =? 0x%X\n", $val, $read_val; printf "Patch support verification failed\n"; exit 0; } } main::debug "patch support has been verified!"; } } write_reg('*', 81, 0x01); # JMPEN, enable the patch write_reg('*', 05, 0x00); # RAM_ADDR_HI, back to normal RAM access foreach $slic (@slics) { FXS::set_user_mode($slic, 0); # Turn off user mode } } sub load_general_params($) { my $slics_ref = shift; my @slics = @{ $slics_ref }; my $slic; main::debug "Loading patch based on si3226x_patch_C_TSS_ISO_2014JUN18.c"; foreach $slic (@slics) { FXS::set_user_mode($slic, 1); # Turn on user mode } write_reg('*', 47, 0x00); # ENHANCE write_reg('*', 80, 0x2F); # AUTO write_ram('*', 764, 0x0050C480, 3); #BAT_HYST # Ringing parameters 65Vrms 0Vdc 20Hz LPR # Loop = 500.0 ft @ 0.044 ohms/ft, REN = 5, Rcpe = 600 ohms # Rprot = 30 ohms, Type = LPR, Waveform = SINE write_ram('*', 637, 0x15E5200E, 3); #ringing impedance = 100 ohms write_ram('*', 766, 0xA3D705, 3); #VBATL_EXPECT 10 V write_ram('*', 767, 0xA3D705, 3); #VBATH_EXPECT 10 V write_ram('*', 768, 0x5ED285F, 3); #VBATR_EXPECT 92.6 V write_ram('*', 768, 0xFFF0000, 3); #PWRSAVE_TIMER write_ram('*', 916, 0x0F5C28F, 3); #OFFHOOK_THRESH write_ram('*', 919, 0x00F00000, 3); #VBAT_TRACK_MIN write_ram('*', 920, 0x02000000, 3); #VBAT_TRACK_MIN_RNG write_ram('*', 970, 0x00800000, 3); #THERM_DBI write_ram('*', 1004, 0x00F18900, 3); #DCDC_VERR write_ram('*', 1005, 0x0080C480, 3); #DCDC_VERR_HYST write_ram('*', 1006, 0x00800000, 3); #DCDC_OITHRESH_LO write_ram('*', 1007, 0x01F00000, 3); #DCDC_OITHRESH_HI write_ram('*', 1540, 0x00200000, 3); #PD_UVLO write_ram('*', 1541, 0x00300000, 3); #PD_OVLO write_ram('*', 1542, 0x00200000, 3); #PD_OCLO write_ram('*', 1545, 0x00C00000, 3); #DCDC_UVHYST, (94v - 90v -1) / 0.257v = 12 write_ram('*', 1546, 0x03D00000, 3); #DCDC_UVTHRESH, 94v / 1.543v = 61 write_ram('*', 1547, 0x1200000, 3); #DCDC_OVTHRESH write_ram('*', 1554, 0x07700000, 3); #DCDC_UVPOL write_ram('*', 1558, 0x00000000, 3); #DCDC_VREF_CTRL write_ram('*', 1560, 0x00200000, 3); #DCDC_RNGTYPE write_ram('*', 1585, 0x00300000, 3); #DCDC_ANA_GAIN write_ram('*', 1586, 0x00300000, 3); #DCDC_ANA_TOFF write_ram('*', 1587, 0x00100000, 3); #DCDC_ANA_TONMIN write_ram('*', 1588, 0x00FFC000, 3); #DCDC_ANA_TONMAX write_ram('*', 1589, 0x00F00000, 3); #DCDC_ANA_DSHIFT write_ram('*', 1590, 0x0FDA4000, 3); #DCDC_ANA_LPOLY write_ram('*', 759, 0x07FEB800, 3); # COEF_P_HVIC write_ram('*', 756, 0x0048D15B, 3); # P_TH_HVIC write_ram('*', 967, 0x03A2E8BA, 3); # SCALE_KAUDIO # /* GC RAM locations that moved from RevB to RevC */ write_ram('*', 1018, 0x03000000, 3); # LKG_OFHK_OFFSET write_ram('*', 1017, 0x05000000, 3); # LKG_LB_OFFSET write_ram('*', 1013, 0x02200000, 3); # VBATH_DELTA write_ram('*', 1012, 0x03700000, 3); # UVTHRESH_MAX write_ram('*', 1011, 0x04F80200, 3); # UVTHRESH_SCALE write_ram('*', 1010, 0x00A23000, 3); # UVTHRESH_BIAS # /* Hardcoded mods to default settings */ write_reg('*', 98, 0x80); #PDN write_ram('*', 626, 0x723F235, 3); # ROW0_C2 write_ram('*', 627, 0x57A9804, 3); # ROW1_C2 write_ram('*', 918, 0x36000, 3); # XTALK_TIMER write_ram('*', 1616, 0x1100000, 3); # DCDC_CPUMP_LP_MASK #/* Smart VOV Default Settings - set here in case no ring preset is loaded */ write_ram('*', 973, 0xFFFFFF, 3); # VOV_DCDC_SLOPE write_ram('*', 974, 0xA18937, 3); # VOV_DCDC_OS write_ram('*', 975, 0xE49BA5, 3); # VOV_RING_BAT_MAX write_ram('*', 516, 0x10038D, 3); # VDIFFLPF write_ram('*', 513, 0x4EDDB9, 3); # ILOOPLPF write_ram('*', 514, 0x806D6, 3); # ILONGLPF write_ram('*', 517, 0x10059F, 3); # VCMLPF write_ram('*', 708, 0xF0000, 3); # CM_SPEEDUP_TIMER write_ram('*', 709, 0x106240, 3); # VCM_TH # /* Prevent Ref Osc from powering down in PLL Freerun mode (pd_ref_osc) */ # write_ram('*', 709, 0x106240, 3); # PWRSAVE_CTRL_LO shall be arithmetic here # batType == VCM_HYST write_ram('*', 1565, 0xC00000, 3); # write_ram('*', 750, 0x0306280, 3); #VCM_HYST write_ram('*', 971, 0x2A00000, 3); #LPR_SCALE write_ram('*', 972, 0x061EB80, 3); #LPR_CM_OS write_ram('*', 1565, 0x0A00000, 3); #DCDC_OIMASK write_ram('*', 1643, 0x000000, 3); #DCDC_OV_DEBOUNCE write_ram('*', 1641, 0x0D00000, 3); #DCDC_UV_DEBOUNCE write_ram('*', 1512, 0x00200000, 3); # PD_OFFLD_DAC write_ram('*', 1513, 0x00200000, 3); # PD_OFFLD_GM write_ram('*', 1592, 0x0300000, 3); #DCDC_PD_ANA write_ram('*', 897, 0x0480CBF, 3); #P_TH_OFFLOAD write_reg('*', 35, 0x03); #OFFLOAD write_ram('*', 1553, 0x00000000, 3); #DCDC_SWDRV_POL write_ram('*', 860, 0x008B9ACA, 3); # IRING_LIM (90.000 mA) foreach $slic (@slics) { FXS::set_user_mode($slic, 0); # Turn on user mode } # Loading Coeff before cal to ensure accurate zsynth results in OHT # This set of coefficients are for the following input parameters: # Device = Si3217x, R1 = 600, R2 = 0, C = 0, R_surge = 20, R_fuse = 24, 10.txt # Generated on 9/30/2009 2:51:17 PM # TXACEQ write_ram('*', 540, 0x07F55480, 3); #TXACEQ_CO write_ram('*', 541, 0x000D6400, 3); #TXACEQ_C1 write_ram('*', 542, 0x00011A00, 3); #TXACEQ_C2 write_ram('*', 543, 0x1FFD7F00, 3); #TXACEQ_C3 # RXACEQ write_ram('*', 546, 0x07F04900, 3); #RXACEQ_CO write_ram('*', 547, 0x00126A00, 3); #RXACEQ_C1 write_ram('*', 548, 0x1FFE1D00, 3); #RXACEQ_C2 write_ram('*', 549, 0x1FFC9480, 3); #RXACEQ_C3 # ECFIR/ECIIR write_ram('*', 563, 0x0012A580, 3); #ECFIR_C2 write_ram('*', 564, 0x1FE59900, 3); #ECFIR_C3 write_ram('*', 565, 0x01BCB180, 3); #ECFIR_C4 write_ram('*', 566, 0x00790780, 3); #ECFIR_C5 write_ram('*', 567, 0x02113380, 3); #ECFIR_C6 write_ram('*', 568, 0x1F172380, 3); #ECFIR_C7 write_ram('*', 569, 0x00805080, 3); #ECFIR_C8 write_ram('*', 570, 0x1FD6E600, 3); #ECFIR_C9 write_ram('*', 571, 0x1FFDF800, 3); #ECIIR_B0 write_ram('*', 572, 0x00010980, 3); #ECIIR_B1 write_ram('*', 573, 0x0E46D280, 3); #ECIIR_A1 write_ram('*', 574, 0x19B4F900, 3); #ECIIR_A2 # ZSYNTH write_ram('*', 653, 0x007A8C00, 3); #ZSYNTH_B0 write_ram('*', 654, 0x1F0BB880, 3); #ZSYNTH_B1 write_ram('*', 655, 0x0079BD00, 3); #ZSYNTH_B2 write_ram('*', 656, 0x0FF66D00, 3); #ZSYNTH_A1 write_ram('*', 657, 0x18099080, 3); #ZSYNTH_A2 write_reg('*', 45, 0x59); #RA # TXGAIN/RXGAIN write_ram('*', 544, 0x08769A80, 3); #TXACGAIN write_ram('*', 545, 0x0141E080, 3); #RXACGAIN write_ram('*', 906, 0x0141E080, 3); #RXACGAIN_SAVE # RXACHPF write_ram('*', 658, 0x07AC0400, 3); #RXACHPF_B0_1 write_ram('*', 659, 0x1853FC80, 3); #RXACHPF_B1_1 write_ram('*', 660, 0x07580880, 3); #RXACHPF_A1_1 write_ram('*', 666, 0x8000000, 3); #RXACHPF_GAIN foreach $slic (@slics) { FXS::set_user_mode($slic, 1); # Turn on user mode } write_reg('*', 98, 0x80); # Power up MADC foreach $slic (@slics) { FXS::set_user_mode($slic, 0); # Turn off user mode } } sub calibrate_slics($$$) { my $cal0 = shift; my $cal1 = shift; my $cal2 = shift; main::debug "Calibrating channels @SlicNums"; write_reg('*', 26, $cal0); # CAL0 write_reg('*', 27, $cal1); # CAL1 write_reg('*', 28, $cal2); # CAL2 write_reg('*', 29, 0x80); # CAL3, CAL_EN # wait until all slics have finished calibration, or for timeout # time periods in seconds: my $sleep_time = 0.001; my $timeout_time = 0.600; # Maximum from the spec my @curr_slics = @SlicNums; my $sleep_cnt = 0; CALIB_LOOP: while(1) { main::mysleep($sleep_time); my @next_slics; for my $slic (@curr_slics) { my $val = read_reg($slic, 29, 'D'); main::debug("checking slic $slic: $val."); push(@next_slics, $slic) if ($val & 0x80) != 0; } @curr_slics = @next_slics; last unless @curr_slics; if ($sleep_cnt * $sleep_time > $timeout_time) { main::logit("Auto Calibration: Exiting on timeout: $timeout_time."); last CALIB_LOOP; } main::debug("auto_calibrate not done yet($sleep_cnt): @curr_slics"); $sleep_cnt++; } #log_calib_params(); } sub dc_powerup($) { my $slics_ref = shift; my @slics = @{ $slics_ref }; foreach my $slic (@slics) { FXS::set_user_mode($slic, 1); # Turn on user mode } write_ram('*', 1538, 0x700000, 3); # PD_DCDC, In case OV or UV previously occurred # write_ram('*', 1555, 0x100000, 3); # DCDC_CPUMP, Turn on charge pump main::mysleep(0.010); # write_ram('*', 1538, 0x00600000, 3); # start up converter main::mysleep(0.500); write_ram('*', 1555, 0x000000, 3); # DCDC_CPUMP, Turn off charge pump write_ram('*', 1542, 0x300000, 3); # PD_OCLO write_ram('*', 1551, 0x00000000, 3); # sClear DCDC status main::mysleep(0.030); write_ram('*', 1538, 0x00400000, 3); foreach my $slic (@slics) { FXS::set_user_mode($slic, 0); # Turn off user mode } } sub read_defaults() { if(XppConfig::read_config(\%settings)) { main::logit "Defaults from $settings{xppconf}"; } else { main::logit "No defaults file, use hard-coded defaults."; } } sub soft_reset() { write_reg('*', 1, 0x03); # RESET } # Try to identify which slics are valid sub check_slics() { my @slics; main::debug "check_slics()"; foreach my $slic (0 .. 7) { my $value = read_reg($slic, 0, 'D'); #main::logit sprintf "Slic($slic): RD 00 0x%X\n", $value; next if($value != 0xC3); write_reg($slic, 14, 0x40); $value = read_reg($slic, 14, 'D'); #main::logit sprintf "Slic($slic): RD 14 0x%X\n", $value; next if($value != 0x40); $value = read_reg($slic, 3, 'D') & 0x1F; #main::logit sprintf "Slic($slic): RD 03 0x%X\n", $value; push(@slics, $slic) if $value == 0x1F; } main::logit "Found " . scalar(@slics) . " SLICS (@slics)"; return @slics; } sub load_custom_preset($) { my $slics_ref = shift; my @slics = @{ $slics_ref }; my $slic; main::debug "Loading patch based on si3226x_patch_C_TSS_ISO_2014JUN18.c"; foreach $slic (@slics) { FXS::set_user_mode($slic, 1); # Turn on user mode } write_ram('*', 755, 0x00050000, 3); #RTPER write_ram('*', 844, 0x7EFE000, 3); #RINGFR = 20Hz write_ram('*', 845, 0x00208847, 3); #RINGAMP = 53 Vrms open circuit with 100ohm ring impedance write_ram('*', 846, 0x00000000, 3); #RINGPHAS write_ram('*', 843, 0x00000000, 3); #RINGOF write_ram('*', 637, 0x15E5200E, 3); #SLOPE_RING (100.000 ohms) write_ram('*', 848, 0x007B9068, 3); #RTACTH (68.236 mA) write_ram('*', 847, 0x0FFFFFFF, 3); #RTDCTH (450.000 mA) */ write_ram('*', 850, 0x00006000, 3); #RTACDB (75.000 ms) write_ram('*', 849, 0x00006000, 3); #RTDCDB (75.000 ms) write_ram('*', 753, 0x00C49BA0, 3); # VOV_RING_BAT (12.000 v) write_ram('*', 896, 0x00000000, 3); # VOV_RING_GND (0.000 v) write_ram('*', 975, 0xE49BA5, 3);#vov_ring_bat_max = ~14V write_reg('*', 39, 0x80); #RINGTALO write_reg('*', 40, 0x3E); #RINGTAHI write_reg('*', 41, 0x00); #RINGTILO write_reg('*', 42, 0x7D); #RINGTIHI write_reg('*', 38, 0x80); #RINGCON Both timers are disabled; enable LPR write_ram('*', 483, 0x28C000, 3);#delta_vcm: This means ring side needs 2v extra. write_ram('*', 973, 0xFFFFFF, 3);# slope_vov_dcdc write_ram('*', 974, 0xA18937, 3);# vov_dcdc_os = ~10v write_ram('*', 509, 0x00C49BA0, 3);# VOV_RING_BAT (12.000 v) write_ram('*', 975, 0xE49BA5, 3);#vov_ring_bat_max = ~14V write_ram('*', 971, 0x2A00000, 3);# scale for LPR amplitude, full scale 28-bit write_ram('*', 972, 0x61EB80, 3);# 6v CM offset write_ram('*', 970, 0x800000, 3);# # DC_FEED: write_ram('*', 634, 0x1C8A024C, 3); # SLOPE_VLIM write_ram('*', 635, 0x1E3081AA, 3); # SLOPE_RFEED write_ram('*', 636, 0x0040A0E0, 3); # SLOPE_ILIM write_ram('*', 638, 0x1B27130B, 3); # SLOPE_DELTA1 write_ram('*', 639, 0x1DD87A3E, 3); # SLOPE_DELTA2 # write_ram('*', 640, 0x043AA4A6, 3); # V_VLIM (36.000 v) write_ram('*', 640, 0x034A1036, 3); # V_VLIM (28.000 v) write_ram('*', 641, 0x03A446AC, 3); # V_RFEED (31.000 v) write_ram('*', 642, 0x034A0E48, 3); # V_ILIM (28.000 v) write_ram('*', 643, 0x01363429, 3); # CONST_RFEED (15.000 mA) write_ram('*', 644, 0x005D0FA6, 3); # CONST_ILIM = 20 mA write_ram('*', 645, 0x00222A30, 3); # I_VLIM = 0.000 mA write_ram('*', 853, 0x005B0AFB, 3); # LCRONHK (10.000 mA) write_ram('*', 852, 0x006D4060, 3); # LCROFFHK (12.000 mA) write_ram('*', 701, 0x00008000, 3); # LCRDBI (5.000 ms) write_ram('*', 858, 0x0048D595, 3); # LONGHITH (8.000 mA) write_ram('*', 859, 0x003FBAE2, 3); # LONGLOTH (7.000 mA) write_ram('*', 702, 0x00008000, 3); # LONGDBI (5.000 ms) write_ram('*', 854, 0x000F0000, 3); # LCRMASK (150.000 ms) write_ram('*', 855, 0x00080000, 3); # LCRMASK_POLREV (80.000 ms) write_ram('*', 856, 0x00140000, 3); # LCRMASK_STATE (200.000 ms) write_ram('*', 857, 0x00140000, 3); # LCRMASK_LINECAP (200.000 ms) write_ram('*', 748, 0x01BA5E35, 3); # VCM_OH (27.000 v) write_ram('*', 752, 0x0051EB85, 3); # VOV_BAT (5.000 v) write_ram('*', 751, 0x00415F45, 3); # VOV_GND (3.990 v) write_reg('*', 46, 0x04); # zcal_en write_reg('*', 23, 0x10); # DTMF_EN = 1 Enable Interupts in IRQ2 Register 19 write_reg('*', 24, 0x03); # P_HVIC_IE = 1, P_THERM_IE = 1 Enable Interupts in IRQ3 Register 20 write_reg('*', 66, 0x00); # USERSTAT write_reg('*', 71, 0x00); # DIAG1 write_ram('*', 799, 95 * 1074 * 1000, 3); #PRAM_VBATH_NEON = -95 V write_ram('*', 786, 70 * 65536, 3); #PRAM_LCRMASK_MWI = 70 mSec foreach $slic (@slics) { FXS::set_linefeed($slic, 1); } write_ram('*', 918, 0x36000, 3); foreach $slic (@slics) { FXS::set_user_mode($slic, 1); # Turn on user mode } write_ram('*', 1018, 0x03000000, 3); # LKG_OFHK_OFFSET write_ram('*', 1017, 0x05000000, 3); # LKG_LB_OFFSET foreach $slic (@slics) { if (($slic & 0x01) == 0) { FXS::set_linefeed($slic, 0); write_ram($slic, 1538, 0x00600000, 3); } else { write_ram($slic & 0xFE, 1538, 0x00400000, 3); } FXS::set_user_mode($slic, 0); # Turn off user mode } } sub configure_pcm($) { my $slic = shift; main::debug "Configure_pcm()"; my $pcm_offset = $slic * 8; write_reg($slic, 12, $pcm_offset); #PCMTXLO write_reg($slic, 13, 0x00); #PCMTXHI write_reg($slic, 14, $pcm_offset); #PCMTXLO write_reg($slic, 15, 0x00); #PCMTXHI write_reg($slic, 11, 0x11); #PCMMODE, u-Law # write_reg($slic, 43, 0x00); #LOOPBACK # write_reg($slic, 44, 0x00); #DIGCON } sub set_linefeed($$) { my $slic = shift; my $lfd = shift; write_reg($slic, 30, $lfd); #LINEFEED } sub overwrite_ring_registers() { } package main; main::logit "Starting '$0' (@SlicNums)\n"; FXS::read_defaults(); FXS::soft_reset(); @SlicNums = FXS::check_slics(); FXS::init_early(); FXS::load_patch(\@SlicNums); FXS::load_general_params(\@SlicNums); if($settings{fxs_skip_calib}) { main::logit "==== WARNING: SKIPPED SLIC CALIBRATION ====="; } else { FXS::calibrate_slics(0x00, 0x00, 0x01); main::mysleep(0.060); FXS::dc_powerup(\@SlicNums); FXS::calibrate_slics(0x00, 0xC0, 0x18); # remaining cals (except mads, lb) main::mysleep(0.700); foreach my $slic (@SlicNums) { FXS::set_linefeed($slic, 0); FXS::set_linefeed($slic, 1); } main::mysleep(1.000); } FXS::load_custom_preset(\@SlicNums); foreach my $slic (@SlicNums) { FXS::configure_pcm($slic); FXS::set_linefeed($slic, 1); } set_output; while() { chomp; s/[#;].*$//; # remove comments s/^\s+//; # trim whitespace s/\s+$//; # trim whitespace s/\t+/ /g; # replace tabs with spaces (for logs) next unless /\S/; # Skip empty lines main::debug "writing: '$_'"; print "$_\n"; } close REG; FXS::overwrite_ring_registers(); main::debug "Ending '$0'"; close STDERR; exit 0; # ----------------------------------==== 8-channel FXS unit initialization ===----------------------------------------- __DATA__