#! /usr/bin/perl -w use strict; # Make warnings fatal local $SIG{__WARN__} = sub { die @_ }; # # $Id$ # # # Written by Oron Peled # Copyright (C) 2007, 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 # XBUS_LABEL - xbus label 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. # - WD Write Direct register. # 3. Register number in hexadecimal. # 5. Data byte in hexadecimal. (for WD 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; 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 PRI; sub gen { my $fmt = shift; $| = 1; printf "$fmt\n", @_; } sub init_quad() { main::select_subunit(0); PRI::gen "0 WD D6 20"; # GPC6.COMP_DIS=1 # (Compatibility Mode Disable) # Tuning of clocking unit to the 16.384 MHz reference frequence # by setting Global Clock Mode registers (GCM[1:8]), same for E1 and T1/J1 PRI::gen "0 WD 92 00"; # GCM1 PRI::gen "0 WD 93 18"; # GCM2 PRI::gen "0 WD 94 FB"; # GCM3 PRI::gen "0 WD 95 0B"; # GCM4 PRI::gen "0 WD 96 01"; # GCM5 PRI::gen "0 WD 97 0B"; # GCM6 PRI::gen "0 WD 98 DB"; # GCM7 PRI::gen "0 WD 99 DF"; # GCM8 } sub finish_quad() { PRI::gen "0 WD BB 2C"; # REGFP PRI::gen "0 WD BC FF"; # REGFD PRI::gen "0 WD BB AC"; # REGFP PRI::gen "0 WD BB 2B"; # REGFP PRI::gen "0 WD BC 00"; # REGFD PRI::gen "0 WD BB AB"; # REGFP PRI::gen "0 WD BB 2A"; # REGFP PRI::gen "0 WD BC FF"; # REGFD PRI::gen "0 WD BB AA"; # REGFP PRI::gen "0 WD BB 29"; # REGFP PRI::gen "0 WD BC FF"; # REGFD PRI::gen "0 WD BB A9"; # REGFP PRI::gen "0 WD BB 28"; # REGFP PRI::gen "0 WD BC 00"; # REGFD PRI::gen "0 WD BB A8"; # REGFP PRI::gen "0 WD BB 27"; # REGFP PRI::gen "0 WD BC FF"; # REGFD PRI::gen "0 WD BB A7"; # REGFP PRI::gen "0 WD BB 00"; # REGFP # PRI::gen "0 WD 80 00"; # PC1 (Port configuration 1): RPB_1.SYPR , XPB_1.SYPX } 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 PRI::Port; sub new { my $pack = shift; my $port = { @_ }; bless $port, $pack; return $port; } sub get_pri_protocol { my $port = shift; my $subunit = $port->{PORT_NUM}; my $xpd_name = "XPD-$ENV{UNIT_NUMBER}$subunit"; my $pri_protocol; my @keys = ( "pri_protocol/connector:$ENV{XBUS_CONNECTOR}/$xpd_name", "pri_protocol/label:$ENV{XBUS_LABEL}/$xpd_name", "pri_protocol/$ENV{XBUS_NAME}/$xpd_name", "pri_protocol" ); foreach my $k (@keys) { $k = lc($k); # Lowercase $pri_protocol = $settings{$k}; if(defined $pri_protocol) { $port->{pri_protocol} = $pri_protocol; return $pri_protocol; } } return undef; } sub write_pri_info { my $port = shift; my $subunit = $port->{PORT_NUM}; my $pri_protocol = $port->get_pri_protocol; my $xpd_name = sprintf("XPD-%1d%1d", $ENV{UNIT_NUMBER}, $subunit); if(defined $pri_protocol) { main::logit "$xpd_name: pri_protocol $pri_protocol"; my $file = sprintf "/sys/bus/xpds/devices/%02d:%1d:%1d/pri_protocol", $ENV{XBUS_NUMBER}, $ENV{UNIT_NUMBER}, $subunit; if(! -f $file) { $file = "/proc/xpp/$ENV{XBUS_NAME}/$xpd_name/pri_info"; main::logit "OLD DRIVER: does not use /sys chipregs. Falling back to /proc" if -f $file; } open(INFO, ">$file") || die "Failed to open '$file': $!\n"; print INFO "$pri_protocol\n" || die "Failed writing '$pri_protocol' to '$file': $!\n"; close INFO || die "Failed during close of '$file': $!\n"; } else { main::logit "$xpd_name: pri_protocol not given. Driver will use defaults."; } } sub port_setup($) { my $port = shift; my $portno = $port->{PORT_NUM}; my $pri_protocol = $port->get_pri_protocol; PRI::gen "$portno WD 28 40"; # XPM2.XLT Tristate my $cmr5 = sprintf("%x", ($portno << 5)); PRI::gen "$portno WD 42 $cmr5"; # CMR5.DRSS=portno PRI::gen "$portno WD 26 F6"; # XPM0: Pulse Shape Programming for R1=18Ohms PRI::gen "$portno WD 27 02"; # XPM1: ...3V Pulse Level at the line (Vp-p=6v) # if (unchannelized) #PRI::gen "$portno WD 1F 22"; # LOOP (Channel Looback): # ECLB (Enable Channel Loop-Back) # CLA (Channel Address) PRI::gen "$portno WD 2B EF"; # IDL (Idle): # If channel loopback is enabled than transmit this code on the outgoing PRI::gen "$portno WD 1F 00"; # LOOP (Channel Looback): #if($portno eq 0){ # PRI::gen "0 WD 1F 00"; # LOOP (Channel Looback): # # channels (XL1/XL2) #}else { # PRI::gen "0 WD 1F 20"; # LOOP (Channel Looback): #} # only one of the following loopbacks can be activated in the same time my $LIM1_RL = 0 << 1; # RL (Remote Loopback) my $lim1 = 0xB0 | $LIM1_RL; PRI::gen "$portno WD 37 %02X", $lim1; # LIM1: ~RL (Remote Loop bit 0x02), # ~DRS (Dual Rail Select, latch receive data while trasmit), # RIL1, RIL0 (Receive Input Treshold 0.62 V), # CLOS (Clear data in case of LOS) PRI::gen "$portno WD 3A 20"; # LIM2: SLT1, SLT0 = 01 # (Receiver Slicer Threshold, the receive slicer # generates a mark (digital one) if the voltage at # RL1/2 exceeds 50% of the peak amplitude, # default, recommended in E1 mode). PRI::gen "$portno WD 38 0A"; # PCD: (Pulse Count Detection, LOS Detection after 176 consecutive 0s) PRI::gen "$portno WD 39 15"; # PCR: (Pulse Count Recovery, LOS Recovery after 22 ones in PCD interval) # Configure system interface PRI::gen "$portno WD 3E C2"; # SIC1: SSC1 (System clock ) is 8.192 Mhz, # SSD1 (System Data rate) is 8.192 Mbit/s, # ~BIM (Byte interleaved mode), # XBS (Transmit Buffer Size) is 2 frames PRI::gen "$portno WD 40 04"; # SIC3: Edges for capture, Synchronous Pulse Receive @Rising Edge PRI::gen "$portno WD 41 04"; # CMR4: RCLK is 8.192 MHz PRI::gen "$portno WD 43 04"; # CMR5: TCLK is 8.192 MHz PRI::gen "$portno WD 44 34"; # CMR6: Receive reference clock generated by channel 1, # RCLK is at 8.192 Mhz dejittered, Clock recovered from the line # TCLK is at 8.192 MHz is de-jittered by DCO-R to drive a6.176 MHz # clock on RCLK.*/ PRI::gen "$portno WD 22 00"; # XC0: (Transmit Counter Offset = 497/T=2) PRI::gen "$portno WD 23 04"; # XC1: X=4 => T=4-X=0 offset PRI::gen "$portno WD 24 00"; # RC0: (Receive Counter Offset = 497/T=2) PRI::gen "$portno WD 25 05"; # RC1: Remaining part of RC0 my $sic2 = sprintf("%x", 0x00 | ($portno << 1)); PRI::gen "$portno WD 3F $sic2"; # SIC2: No FFS, no center receive elastic buffer, data active at phase ($sic >> 1) # enable the following interrupt sources PRI::gen "$portno WD 14 F7"; # IMR0 (Interrupt Mask Register2): Enable CASC_E1/RSC_T1 PRI::gen "$portno WD 16 00"; # IMR2 (Interrupt Mask Register2): Enable ALL PRI::gen "$portno WD 17 3F"; # IMR3 ~ES, ~SEC (Enable ES and SEC interrupts) PRI::gen "$portno WD 18 00"; # IMR4: Enable ALL PRI::gen "$portno WD 46 80"; # GCR: (Global Configuration Register) # VIS (Masked Interrupts Visible) PRI::gen "$portno WD 08 04"; # IPC: SYNC is 8 Khz PRI::gen "$portno WD 02 51"; # CMDR (Command Register): RRES, XRES, SRES (Receiver/Transmitter reset) PRI::gen "$portno WD 02 00"; # CMDR PRI::gen "$portno WD 45 00"; # CMR2: External sources for SYPR, SCLKR, SYPX, SCLKX for TX and RX. # Configure ports PRI::gen "$portno WD 85 80"; # GPC1 (Global Port Configuration 1): #PRI::gen "$portno WD 85 00"; # GPC1 (Global Port Configuration 1): # SMM (System Interface Multiplex Mode) PRI::gen "$portno WD 80 00"; # PC1: SYPR/SYPX provided to RPA/XPA inputs PRI::gen "$portno WD 84 31"; # PC5: XMFS active low, SCLKR is input, RCLK is output (unused) PRI::gen "$portno WD 3B 00"; # Clear LCR1 - Loop Code Register 1 # printk("TE110P: Successfully initialized serial bus for card\n"); # Initialize PCM and SIG regs PRI::gen "$portno WD A0 00"; # TSEO (Time Slot Even/Odd Select) PRI::gen "$portno WD A1 FF"; # TSBS (Time Slot Bit Select)- only selected bits are used for HDLC channel 1 # in selected time slots PRI::gen "$portno WD 03 89"; # Mode Register: # MDS (Mode Select) = 100 (No address comparison) # HRAC (Receiver Active - HDLC channel 1) # RFT2 (HDLC Receive FIFO is 64 byte deep) my $ccr1 = 0x18; # CCR1 (Common Configuration Register1) # EITS (Enable Internal Time Slot 0 to 31 Signalling) # ITF (Interframe Time Fill) my $sysfs_pri_protocol; if (defined $pri_protocol) { $sysfs_pri_protocol = $pri_protocol; } else { my $file = sprintf "/sys/bus/xpds/devices/%02d:%1d:%1d/pri_protocol", $ENV{XBUS_NUMBER}, $ENV{UNIT_NUMBER}, $portno; # The 'open' will fail if the port does not exist. # (or rather: the XPD for it does not exist). While # we only read this file to get the default E1/T1 value, # if it does not exist, it also implies the commands sent would # get nowhere. So we might as well quit now. open(F, $file) || return; $sysfs_pri_protocol = ; close F; chomp $sysfs_pri_protocol; } if($sysfs_pri_protocol eq 'T1') { $ccr1 |= 0x80; # RSCC (Serial CAS Format Selection) } PRI::gen "$portno WD 09 %02X", $ccr1; PRI::gen "$portno WD 0A 04"; # CCR2 (Common Configuration Register2) # RCRC (enable CRC - HDLC channel 1enable CRC - HDLC channel 1) PRI::gen "$portno WD 0C 00"; # RTR1 (Receive Time Slot register 1) PRI::gen "$portno WD 0D 00"; # RTR2 (Receive Time Slot register 2) PRI::gen "$portno WD 0E 00"; # RTR3 (Receive Time Slot register 3), TS16 (Enable time slot 16) PRI::gen "$portno WD 0F 00"; # RTR4 (Receive Time Slot register 4) PRI::gen "$portno WD 10 00"; # TTR1 (Transmit Time Slot register 1) PRI::gen "$portno WD 11 00"; # TTR2 (Transmit Time Slot register 2) PRI::gen "$portno WD 12 00"; # TTR3 (Transmit Time Slot register 3), TS16 (Enable time slot 16) PRI::gen "$portno WD 13 00"; # TTR4 (Transmit Time Slot register 4) # configure the best performance of the Bipolar Violation detection for all four channels PRI::gen "$portno WD BD 00"; # BFR (Bugfix Register): ~BVP (Bipolar Violations), # use Improved Bipolar Violation Detection instead } package main; main::debug "Starting '$0'"; PRI::read_defaults; sub main() { my @ports; my $subunit; main::debug "main(): Initializing chip ($ENV{UNIT_SUBUNITS} ports)"; PRI::init_quad; # Must initialize all 4 ports, regardless how much there are for($subunit = 0; $subunit < 4; $subunit++) { #main::debug "main(): Initializing subunit $subunit"; my $p = PRI::Port->new( 'PORT_NUM' => $subunit, 'EXIST' => ($subunit < $ENV{UNIT_SUBUNITS}) ); $p->port_setup; push(@ports, $p); } PRI::finish_quad; foreach my $p (@ports) { if($p->{EXIST}) { $p->write_pri_info; } } } main; main::debug "Ending '$0'"; close REG; close STDERR; exit 0;