dahdi-linux/drivers/dahdi/xpp/init_card_1_30

598 lines
12 KiB
Plaintext
Raw Normal View History

#! /usr/bin/perl -w
use strict;
# Make warnings fatal
local $SIG{__WARN__} = sub { die @_ };
#
# Written by Oron Peled <oron@actcom.co.il>
# Copyright (C) 2006, 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;
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};
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;
}
}
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 = (0 .. 7);
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 read_reg($$$) {
my $read_slic = shift;
my $read_reg = shift;
my $direct = shift;
write_to_slic_file(
sprintf("%s R%s %02X", $read_slic, $direct, $read_reg));
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(<SLICS>){
s/#.*//;
next unless /\S/;
@results = /^\s*(\d+)\s+[RW][DI]\s+([[:xdigit:]]+)\s+([[:xdigit:]]+)\s+([[:xdigit:]]*)/;
if(@results != 4) {
main::logit "Failed reading from '$chipregs' ($read_slic,$read_reg,$direct)";
die;
}
}
close(SLICS);
my $reg = hex($results[1]);
if($results[0] ne $read_slic || $reg ne $read_reg) {
# We read obsolete values, need to wait some more
if(--$retries) {
main::debug "$read_slic RD $read_reg -- retry ($results[0], $reg)";
redo WAIT_RESULTS;
} else {
main::logit "Failed: $read_slic RD $read_reg returned $results[0], $reg";
die;
}
}
# Good.
@reply = (hex($results[2]), hex($results[3]));
}
if ($direct eq 'S') {
return @reply;
} else {
return $reply[0];
}
}
# TODO: rearange arguments
sub write_reg{#($$$$$) {
my $read_slic = shift;
my $read_reg = shift;
my $direct = shift;
my $reg_val_low = shift;
my $reg_val_hi = shift;
my $str = sprintf "%s W%s %02X %02X",
$read_slic, $direct, $read_reg, $reg_val_low;
if ($direct eq 'S') {
$str .= sprintf " %02X", $reg_val_hi;
}
write_to_slic_file($str);
}
sub log_calib_params() {
for my $i (100 .. 107) {
my $line="Calib Reg $i: ";
for my $slic (@SlicNums) {
$line .= " ".read_reg($slic, $i, 'D');
}
main::debug($line);
}
}
sub init_indirect_registers() {
return write_to_slic_file("#
* WS 1E 00 C2 55
* WS 1E 01 E6 51
* WS 1E 02 85 4B
* WS 1E 03 37 49
* WS 1E 04 33 33
* WS 1E 05 02 02
* WS 1E 06 02 02
* WS 1E 07 98 01
* WS 1E 08 98 01
* WS 1E 09 11 06
* WS 1E 0A 02 02
* WS 1E 0B E5 00
* WS 1E 0C 1C 0A
* WS 1E 0D 30 7B
* WS 1E 0E 63 00
* WS 1E 0F 00 00
* WS 1E 10 70 78
* WS 1E 11 7D 00
* WS 1E 12 00 00
* WS 1E 13 00 00
* WS 1E 14 FD 7E
* WS 1E 15 77 01
* WS 1E 16 00 00
* WS 1E 17 00 20
* WS 1E 18 00 20
* WS 1E 19 00 00
* WS 1E 1A 00 20
* WS 1E 1B 00 40
* WS 1E 1C 00 10
* WS 1E 1D 00 36
* WS 1E 1E 00 10
* WS 1E 1F 00 02
* WS 1E 20 C0 07
* WS 1E 21 6F 37
* WS 1E 22 80 1B
* WS 1E 23 00 80
* WS 1E 24 00 08
* WS 1E 25 00 08
* WS 1E 26 00 08
* WS 1E 27 00 08
* WS 1E 28 00 00
* WS 1E 2B 00 08 # LCRTL = 5.08 mA
* WS 1E 63 DA 00
* WS 1E 64 60 6B
* WS 1E 65 74 00
* WS 1E 66 C0 79
* WS 1E 67 20 11
* WS 1E 68 E0 3B
#");
}
sub init_early_direct_regs() {
my $lbv = ($eeprom_release_201) ? "20" : "10";
my $vcm = ($eeprom_release_201) ? "02" : "03";
return write_to_slic_file("#
* WD 08 00 # Audio Path Loopback Control
* WD 6C 01
* WD 4A 3F # High Battery Voltage
* WD 4B $lbv # Low Battery Voltage
* WD 49 $vcm # Common Mode Voltage (VCM)
* WD 40 00 # Line Feed Control
#")
}
my @FilterParams = ();
sub save_indirect_filter_params() {
for my $slic (@SlicNums) {
for my $reg (35 .. 39) {
$FilterParams[$slic][$reg] =
[read_reg($slic, $reg, 'S')];
write_reg($slic, $reg, 'S', 0, 0x80);
}
}
}
sub restore_indirect_filter_params() {
for my $slic (@SlicNums) {
for my $reg (35 .. 39) {
write_reg($slic, $reg, 'S',
@{$FilterParams[$slic][$reg]});
}
}
}
my $ManualCalibrationSleepTime = 0.04; # 40ms
sub manual_calibrate_loop($$) {
my $write_reg = shift;
my $read_reg = shift;
my @curr_slics = @SlicNums;
# initialize counters
my @slic_counters = map { 0x1F } @curr_slics;
# wait until all slics have finished calibration, or for timeout
while (@curr_slics) {
my $debug_calib_str = "ManualCalib:: ";
my @next_slics;
for my $slic (@curr_slics) {
write_reg($slic,$write_reg,'D',$slic_counters[$slic]);
}
main::mysleep $ManualCalibrationSleepTime;
for my $slic (@curr_slics) {
my $value = read_reg($slic, $read_reg, 'D');
$debug_calib_str .= sprintf " [%d:%d:%X]",
$slic, $slic_counters[$slic], $value;
next if $value == 0; # This one is calibrated.
if ($slic_counters[$slic] > 0) {
$slic_counters[$slic]--;
push(@next_slics, $slic);
} else {
main::logit("ERROR: SLIC $slic reached 0 during manual calibration");
}
}
@curr_slics = @next_slics;
main::debug($debug_calib_str);
}
main::debug("No more slics to calibrate");
}
sub manual_calibrate() {
manual_calibrate_loop(98, 88);
manual_calibrate_loop(99, 89);
}
sub auto_calibrate($$) {
my $calib_96 = shift;
my $calib_97 = shift;
#log_calib_params();
# start calibration:
for my $slic(@SlicNums) {
write_to_slic_file(
sprintf
"$slic WD 61 %02X\n".
"$slic WD 60 %02X\n".
"", $calib_97, $calib_96
);
}
# 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) {
main::debug("checking slic $slic");
my $val = read_reg($slic, 96, 'D');
push(@next_slics, $slic) if $val != 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 calibrate_slics() {
main::debug "Calibrating '$0'";
auto_calibrate(0x40, 0x1E);
main::debug "after auto_calibrate";
manual_calibrate();
main::debug "after manul_calibrate";
auto_calibrate(0x40, 0x01);
main::debug "after auto_calibrate 2";
main::debug "Continue '$0'";
}
sub read_defaults() {
if(XppConfig::read_config(\%settings)) {
main::logit "Defaults from $settings{xppconf}";
} else {
main::logit "No defaults file, use hard-coded defaults.";
}
}
# Try to identify which slics are valid
sub check_slics() {
my @slics;
foreach my $slic (0 .. 7) {
my $value = read_reg($slic, 0, 'D');
push(@slics, $slic) if $value != 0xFF;
}
main::logit "Found " . scalar(@slics) . " SLICS (@slics)";
return @slics;
}
package main;
main::debug "Starting '$0'";
FXS::read_defaults;
@SlicNums = FXS::check_slics;
main::debug "before init_indirect_registers";
FXS::init_indirect_registers();
main::debug "after init_indirect_registers";
FXS::init_early_direct_regs();
main::debug "after init_early_direct_regs";
if($settings{fxs_skip_calib}) {
main::logit "==== WARNING: SKIPPED SLIC CALIBRATION =====";
} else {
FXS::calibrate_slics;
}
set_output;
while(<DATA>) {
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;
main::debug "Ending '$0'";
close STDERR;
exit 0;
# ----------------------------------==== 8-channel FXS unit initialization ===-----------------------------------------
__DATA__
# Flush out energy accumulators
* WS 1E 58 00 00
* WS 1E 59 00 00
* WS 1E 5A 00 00
* WS 1E 5B 00 00
* WS 1E 5C 00 00
* WS 1E 5D 00 00
* WS 1E 5E 00 00
* WS 1E 5F 00 00
* WS 1E 61 00 00
* WS 1E C1 00 00
* WS 1E C2 00 00
* WS 1E C3 00 00
* WS 1E C4 00 00
* WS 1E C5 00 00
* WS 1E C6 00 00
* WS 1E C7 00 00
* WS 1E C8 00 00
* WS 1E C9 00 00
* WS 1E CA 00 00
* WS 1E CB 00 00
* WS 1E CC 00 00
* WS 1E CD 00 00
* WS 1E CE 00 00
* WS 1E CF 00 00
* WS 1E D0 00 00
* WS 1E D1 00 00
* WS 1E D2 00 00
* WS 1E D3 00 00
# Clear and disable interrupts
* WD 12 FF
* WD 13 FF
* WD 14 FF
* WD 15 00
* WD 16 00
* WD 17 00
## Mode(8-bit,u-Law,1 PCLK )
* WD 01 08 # Disable PCM transfers
# Setting of SLICs offsets
# New card initialization
* WD 03 00
* WD 05 00
0 WD 02 00
0 WD 04 00
0 WD 01 28 # Enable PCM transfers
1 WD 02 08
1 WD 04 08
1 WD 01 28
2 WD 02 10
2 WD 04 10
2 WD 01 28
3 WD 02 18
3 WD 04 18
3 WD 01 28
4 WD 02 20
4 WD 04 20
4 WD 01 28
5 WD 02 28
5 WD 04 28
5 WD 01 28
6 WD 02 30
6 WD 04 30
6 WD 01 28
7 WD 02 38
7 WD 04 38
7 WD 01 28
# Audio path. (also initialize 0A and 0B here if necessary)
* WD 08 00
* WD 09 00
* WD 0A 08
* WD 0B 33
#------ Metering tone
* WD 2C 00 # Timer dL
* WD 2D 03 # Timer dH
* WS 1E 17 61 15 # Amplitue Ramp-up
* WS 1E 18 61 15 # Max Amplitude
* WS 1E 19 FB 30 # Frequency
# Ring regs are set by driver
# Automatic/Manual Control: defaults but:
# Cancel AOPN - Power Alarm
# Cancel ABAT - Battery Feed Automatic Select
* WD 43 16
# Loop Closure Debounce Interval
* WD 45 0A
# Ring Detect Debounce Interval
* WD 46 47
# Battery Feed Control: Battery low (DCSW low)
* WD 42 00
# Loop Current Limit
* WD 47 00
# On-Hook Line Voltage (VOC)
* WD 48 20
* WS 1E 23 00 80
* WS 1E 24 20 03
* WS 1E 25 8C 00
* WS 1E 26 00 00
* WS 1E 27 10 00
* WD 0E 00