#! /usr/bin/perl -w use strict; # Make warnings fatal local $SIG{__WARN__} = sub { die @_ }; # # $Id$ # # # Written by Oron Peled # 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. # # This script is run from the xpp kernel module upon detection # of a new XPD. # # Expects the following environment variables to be set: # XBUS_NAME - bus name # UNIT_NUMBER - xpd unit number # UNIT_SUBUNITS - number of subunits in this xpd # UNIT_TYPE - xpd type number (from protocol reply): # 1 - FXS # 2 - FXO # 3 - BRI # 4 - PRI # XBUS_REVISION - xbus revision number # XBUS_CONNECTOR - xbus connector string # # Output data format: # - An optional comment start with ';' or '#' until the end of line # - Optional Blank lines are ignored # - Fields are whitespace separated (spaces or tabs) # # The fields are (in command line order): # 1. CHIP select in decimal (ignored, taken from 3 LSB's of subunit number) # 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. Subregister number in hexadecimal. (for RS and WS commands). # 5. Data byte in hexadecimal. (for WD and WS commands 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; getopts('o:', \%opts); my %settings; 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 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; } } } sub select_subunit($) { my $subunit = shift; die unless defined $subunit; my $output; if($opts{o}) { $output = $opts{o}; } else { $output = sprintf "/sys/bus/xpds/devices/%02d:%1d:%1d/chipregs", $ENV{XBUS_NUMBER}, $ENV{UNIT_NUMBER}, $subunit; if(! -f $output) { my $xpd_name = sprintf("XPD-%1d%1d", $ENV{UNIT_NUMBER}, $subunit); $output = "/proc/xpp/$ENV{XBUS_NAME}/$xpd_name/chipregs"; logit "OLD DRIVER: does not use /sys chipregs. Falling back to /proc" if -f $output; } } open(REG, ">$output") || die "Failed to open '$output': $!\n"; my $oldfh = select REG; print "# Selecting subunit $subunit\n" if $opts{o}; return $oldfh; } package BRI; sub gen { my $fmt = shift; $| = 1; printf "$fmt\n", @_; } # Turning on/off multi-byte packet reception. sub multibyte($) { my $active = (shift) ? 'M' : 'm'; for my $subunit (0 .. $ENV{UNIT_SUBUNITS} - 1) { #main::logit "multibyte(): $subunit -> $active"; main::select_subunit($subunit); BRI::gen "$subunit W$active"; } } sub read_defaults() { if(XppConfig::read_config(\%settings)) { main::logit "Defaults from $settings{xppconf}"; } else { main::logit "No defaults file, use hard-coded defaults."; } } package BRI::Port; sub new { my $pack = shift; my $port = { @_ }; bless $port, $pack; } # zap_xhfc_su.c:995 sub init_su { my $port = shift; my $portnum = $port->{PORT_NUM}; my $port_mode_up = $port->{PORT_MODE_UP}; my $port_mode_exch = $port->{PORT_MODE_EXCH}; my $bri_nt = $port->{BRI_NT}; #main::logit "init_su(portnum=$portnum, port_mode_up=$port_mode_up, bri_nt=$bri_nt)"; # Setting PLL # R_PLL_CTRL = 0 (V_PLL_M = 0, Reset PLL, Disable PLL_ # R_CLK_CFG = 05 (PLL clock as system clock, output it to CLK_OUT pin) # R_PLL_P = 1 # R_PLL_N = 6 # R_PLL_S = 1 # R_PLL_CTRL = 1 (V_PLL_M) BRI::gen "#--------------------------- init_su($portnum, $bri_nt, $port_mode_up, $port_mode_exch)"; BRI::gen "$portnum WD 02 04"; BRI::gen "$portnum WD 50 00"; # disable PLL BRI::gen "$portnum WD 51 02"; BRI::gen "$portnum WD 52 06"; BRI::gen "$portnum WD 53 04"; BRI::gen "$portnum WD 50 01"; # Enable PLL BRI::gen "$portnum WD 02 05"; # Enable PLL su_sel($portnum); # select port if ("$port_mode_up" == 1) { $port->{CTRL3} = 0x01; # A_ST_CTRL3: V_ST_SEL = 1 $port->{CTRL0} = 0x10; # A_SU_CTRL0: V_ST_SQ_EN = 1 BRI::gen "$portnum WD 34 0F"; # A_MS_TX: # (multiframe/superframe transmit register) } else { $port->{CTRL3} = 0x00; # A_ST_CTRL3: V_ST_SEL = 0 $port->{CTRL0} = 0x00; # A_SU_CTRL0: V_ST_SQ_EN = 0 } if ("$bri_nt" == 1) { $port->{CTRL0} |= 0x04; # V_SU_MD } # ((V_SU_EXCH)?0x80:00) (change polarity) if($port_mode_exch) { $port->{CTRL2} = 0x80; } else { $port->{CTRL2} = 0x00; } BRI::gen "$portnum WD 35 %02X", $port->{CTRL3}; # A_ST_CTRL3 BRI::gen "$portnum WD 31 %02X", $port->{CTRL0}; # A_SU_CTRL0 BRI::gen "$portnum WD 35 F8"; # A_ST_CTRL3 = set end of pulse control to 0xF8 BRI::gen "$portnum WD 32 09"; # A_SU_CTRL1 = Ignore E-channel data, Force automatic transition from G2 to G3 BRI::gen "$portnum WD 33 %02X", $port->{CTRL2}; # A_SU_CTRL2 # zap_xhfc_su.c:1030 in init_su() # A_SU_CLK_DLY my $clk_dly; if ("$bri_nt" == 1) { $clk_dly = 0x6C; } else { $clk_dly = 0x0E; } #main::logit "clk_dly=$clk_dly"; BRI::gen "$portnum WD 37 %02X", "$clk_dly"; } sub su_sel { if (@_ != 1 ) { main::logit "ERROR: su_sel() called with " . scalar(@_) . " parameters"; exit 1; } my $portnum = shift; BRI::gen "$portnum WD 16 %02X", $portnum; # R_SU_SEL } # zap_xhfc_su.c:281 sub xhfc_selfifo { my $port = shift; my $portnum = $port->{PORT_NUM}; if (@_ != 1 ) { main::logit "ERROR: xhfc_selfifo() called with " . scalar(@_) . " parameters"; exit 1; } my $fifonum = shift; #main::logit "xhfc_selfifo($fifonum)"; BRI::gen "$portnum WD 0F %02X", $fifonum; # --> WAIT UNTIL (R_STATUS & M_BUSY) == 0 } # zap_xhfc_su.c:295 sub xhfc_resetfifo() { my $port = shift; my $portnum = $port->{PORT_NUM}; #main::logit "xhfc_resetfifo()"; # A_INC_RES_FIFO = M_RES_FIFO | M_RES_FIFO_ERR BRI::gen "$portnum WD 0E 0A"; # --> WAIT UNTIL (R_STATUS & M_BUSY) == 0 } # zap_xhfc_su.c:1040 # Initialize fifo (called for each portnum, channel, direction) sub setup_fifo { my $port = shift; my $chan = shift; my $direction = shift; my $conhdlc = shift; my $subcfg = shift; my $fifoctrl = shift; my $portnum = $port->{PORT_NUM}; my $port_mode_up = $port->{PORT_MODE_UP}; my $port_mode_exch = $port->{PORT_MODE_EXCH}; my $bri_nt = $port->{BRI_NT}; BRI::gen "#--------------------------- setup_fifo($portnum, $chan, $direction)"; # my $fifonum = 0x80 | ($portnum << 3) | ($chan << 1) | ($direction); # # MSB first my $fifonum = ($portnum << 3) | ($chan << 1) | ($direction); # # MSB first my $r_slot = ($portnum << 3) | ($chan << 1) | ($direction); # channel order workaround, swap odd and even portnums in $r_slot for PCM (chan 0, 1) only if ("$chan" == 0 || "$chan" == 1) { $r_slot = $r_slot ^ 0x08; } my $short_portnum = $portnum & 0x03; my $a_sl_cfg = (0x80 | ($short_portnum << 3) | ($chan << 1) | ($direction)); # receive data from STIO2, transmit to STIO1 #main::logit "setup_fifo($fifonum)"; $port->xhfc_selfifo($fifonum); # A_CON_HDLC: transparent mode selection BRI::gen "$portnum WD FA %02X", $conhdlc; # A_SUBCH_CFG: subchnl params BRI::gen "$portnum WD FB %02X", $subcfg; # A_FIFO_CTRL: FIFO Control Register BRI::gen "$portnum WD FF %02X", $fifoctrl; $port->xhfc_resetfifo(); $port->xhfc_selfifo($fifonum); # wait for busy is builtin in this command BRI::gen "$portnum WD 10 %02X", $r_slot; # R_SLOT BRI::gen "$portnum WD D0 %02X", $a_sl_cfg; # A_SL_CFG #system("/bin/echo \"----=====TE\" \"short_portnum=\"$short_portnum \"portnum=\" $portnum \"chan=\" $chan\"======----\n\" >>/root/xortel/test_init"); } # zap_xhfc_su.c:1071 sub setup_su { my $port = shift; my $bchan = shift; my $portnum = $port->{PORT_NUM}; my $port_mode_exch = $port->{PORT_MODE_EXCH}; my $bri_nt = $port->{BRI_NT}; BRI::gen "#--------------------------- setup_su($portnum, $bchan)"; #main::logit "setup_su(portnum=$portnum, bchan=$bchan, port_mode_exch=$port_mode_exch, bri_nt=$bri_nt)"; $port->{CTRL0} |= (1 << $bchan) | $bri_nt; $port->{CTRL2} |= ($port_mode_exch << 7) | (1 << $bchan); su_sel($portnum); # Select port BRI::gen "$portnum WD 31 %02X", $port->{CTRL0}; # A_SU_CTRL0: V_B1_TX_EN | V_SU_MD | (NT/TE) BRI::gen "$portnum WD 33 %02X", $port->{CTRL2}; # A_SU_CTRL2: V_B1_RX_EN } sub xhfc_ph_command { my $port = shift; my $cmd = shift; my $portnum = $port->{PORT_NUM}; #main::logit "xhfc_ph_command(portnum=$portnum)"; if ("$cmd" eq "HFC_L1_ACTIVATE_TE") { su_sel($portnum); # Select port BRI::gen "$portnum WD 30 60"; # A_SU_WR_STA = (M_SU_ACT & 0x03) # (set activation) } elsif ("$cmd" eq "HFC_L1_FORCE_DEACTIVATE_TE") { su_sel($portnum); # Select port BRI::gen "$portnum WD 30 40"; # A_SU_WR_STA = (M_SU_ACT & 0x02) # (set deactivation) } elsif ("$cmd" eq "HFC_L1_ACTIVATE_NT") { su_sel($portnum); # Select port BRI::gen "$portnum WD 30 E0"; # A_SU_WR_STA = (M_SU_ACT & 0x03) | 0x80 # (set activation + NT) } elsif ("$cmd" eq "HFC_L1_DEACTIVATE_NT") { su_sel($portnum); # Select port BRI::gen "$portnum WD 30 40"; # A_SU_WR_STA = (M_SU_ACT & 0x02) # (set deactivation) } } sub zthfc_startup { my $port = shift; my $portnum = $port->{PORT_NUM}; my $port_mode_exch = $port->{PORT_MODE_EXCH}; my $bri_nt = $port->{BRI_NT}; #main::logit "zthfc_startup(portnum=$portnum, port_mode_exch=$port_mode_exch, bri_nt=$bri_nt)"; # PCM <-> ST/Up Configuration foreach my $chan ( 0, 1 ) { $port->setup_fifo($chan, 0, 0xFE, 0, 0);# Transparent mode, FIFO EN, ST->PCM $port->setup_fifo($chan, 1, 0xFE, 0, 0);# Transparent mode, FIFO EN, ST->PCM $port->setup_su($chan); # zap_xhfc_su.c:194 } # Dahdi chan 2 used as HDLC D-Channel $port->setup_fifo(2, 0, 0x05, 2, 0); # D-TX: zap_xhfc_su.c:205 $port->setup_fifo(2, 1, 0x05, 2, 0); # D-RX: zap_xhfc_su.c:206 # E-chan, Echo channel is ignored # enable this port's state machine su_sel($portnum); # Select port # A_SU_WR_STA: reset port state machine BRI::gen "$portnum WD 30 00"; if ("$bri_nt" == 0) { $port->xhfc_ph_command("HFC_L1_ACTIVATE_TE"); } else { $port->xhfc_ph_command("HFC_L1_ACTIVATE_NT"); } } package main; debug "Starting '$0'"; BRI::read_defaults; #------------------------------------------- Instance detection # zap_xhfc_su.c:895 sub init_xhfc($) { my $portnum = shift; main::debug "init_xhfc($portnum)"; BRI::gen "#--------------------------- init_xhfc"; BRI::gen "$portnum WD 0D 00"; # r_FIFO_MD: 16 fifos, # 64 bytes for TX and RX each (FIFO mode config) # software reset to enable R_FIFO_MD setting BRI::gen "$portnum WD 00 08"; # R_CIRM = M_SRES (soft reset) # --> WAIT 5u BRI::gen "$portnum WD 00 00"; # R_CIRM = 0 (zero it to deactivate reset) # amplitude BRI::gen "$portnum WD 46 80"; # R_PWM_MD: (PWM output mode register) # PWM push to zero only BRI::gen "$portnum WD 39 18"; # R_PWM1: (modulator register for PWM1) # set duty cycle BRI::gen "$portnum WD 0C 11"; # R_FIFO_THRES: (FIFO fill lvl control register) # RX/TX threshold = 16 bytes # set PCM bus mode to slave by default BRI::gen "$portnum WD 14 08"; # R_PCM_MD0 = PCM slave mode, F0IO duration is 2 HFC_PCLK's # (C4IO, F0IO are inputs) BRI::gen "$portnum WD 14 98"; # R_PCM_MD0: Index value to select # the register at address 15 BRI::gen "$portnum WD 15 20"; # R_PCM_MD1: V_PLL_ADJ (DPLL adjust speed), # in the last slot of PCM frame # V_PCM_DR, C4IO is 16.384MHz(128 time slots) BRI::gen "$portnum WD 4C 07"; # GPIOGPIO function (not PWM) on GPIO0, GPIO1 and GPIO2 pins BRI::gen "$portnum WD 4A 07"; # Output enable for GPIO0, GPIO1 and GPIO2 pins BRI::gen "$portnum WD 48 01"; # GPIO output data bits } my @port_type = ( { 'BRI_NT' => 1 }, { 'BRI_NT' => 0 } ); # zap_xhfc_su.c:175 sub main() { my $subunit; my $subunits_mask = pack("C", $ENV{UNIT_SUBUNITS_DIR}); my @direction = split(//, unpack("b*", $subunits_mask)); #logit "main(): UNIT_TYPE=$ENV{UNIT_TYPE} UNIT_SUBUNITS_DIR=[@direction]"; if(!$opts{o}) { foreach my $var (qw(XBUS_NAME UNIT_NUMBER UNIT_TYPE UNIT_SUBUNITS UNIT_SUBUNITS_DIR XBUS_REVISION XBUS_CONNECTOR)) { die "Missing mandatory '$var' environment variable" unless defined $var; } } # Turn off multi-byte packet reception before initialization started # Otherwise we mess with registers while the FPGA firmware tries to # send us packets. BRI::multibyte(0); # Port initialization for($subunit = 0; $subunit < $ENV{UNIT_SUBUNITS}; $subunit++) { my $is_nt = $direction[$subunit]; main::select_subunit($subunit); if(($subunit % 4) == 0) { # A new xhfc chip #logit "main(): Initializing chip"; init_xhfc($subunit); # zap_xhfc_su.c:1173 in setup_instance() } #logit "main(): Initializing subunit $subunit is_nt=$is_nt"; my $p = BRI::Port->new( 'PORT_NUM' => $subunit, 'BRI_NT' => $is_nt, 'PORT_MODE_UP' => 0, 'PORT_MODE_EXCH' => 0 ); # zap_XHfc_su.c:1186 in setup_instance() $p->init_su; $p->zthfc_startup; } # Turn on multi-byte packet reception when ports initialization finished BRI::multibyte(1); } main; debug "Ending '$0'"; close REG; close STDERR; exit 0;