420 lines
13 KiB
Plaintext
420 lines
13 KiB
Plaintext
|
#! /usr/bin/perl -w
|
||
|
use strict;
|
||
|
|
||
|
# Make warnings fatal
|
||
|
local $SIG{__WARN__} = sub { die @_ };
|
||
|
|
||
|
#
|
||
|
# $Id$
|
||
|
#
|
||
|
|
||
|
#
|
||
|
# Written by Oron Peled <oron@actcom.co.il>
|
||
|
# 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 = <F>;
|
||
|
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;
|