Commit 921a86e0 authored by Krzysztof Halasa's avatar Krzysztof Halasa Committed by Greg Kroah-Hartman

Staging: Add SBE 2T3E3 WAN driver

This is a driver for SBE Inc.'s dual port T3/E3 WAN cards. Based on
their original GPLed driver.

The original driver tarball is now accessible at
http://userweb.kernel.org/~chris/SBE_2T3_Linux_2.0c.tgz

It needs at least a new generic HDLC setup code (not yet written) before
moving to drivers/net/wan.
Signed-off-by: default avatarKrzysztof Hałasa <khc@pm.waw.pl>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent b0b57633
......@@ -155,5 +155,7 @@ source "drivers/staging/quickstart/Kconfig"
source "drivers/staging/westbridge/Kconfig"
source "drivers/staging/sbe-2t3e3/Kconfig"
endif # !STAGING_EXCLUDE_BUILD
endif # STAGING
......@@ -58,3 +58,4 @@ obj-$(CONFIG_SOLO6X10) += solo6x10/
obj-$(CONFIG_TIDSPBRIDGE) += tidspbridge/
obj-$(CONFIG_ACPI_QUICKSTART) += quickstart/
obj-$(CONFIG_WESTBRIDGE_ASTORIA) += westbridge/astoria/
obj-$(CONFIG_SBE_2T3E3) += sbe-2t3e3/
This diff is collapsed.
config SBE_2T3E3
tristate "SBE wanPMC-2T3E3 support"
depends on HDLC && PCI
help
Driver for wanPMC-2T3E3 cards by SBE Inc.
If you have such a card, say Y here and see
<http://www.kernel.org/pub/linux/utils/net/hdlc/>.
To compile this as a module, choose M here: the
module will be called sbe-2t3e3.
If unsure, say N.
obj-$(CONFIG_SBE_2T3E3) += sbe-2t3e3.o
sbe-2t3e3-objs := module.o netdev.o maps.o \
main.o cpld.o intr.o ctrl.o io.o dc.o exar7250.o exar7300.o
TODO:
- additional cleaning and tests
- wait for the new configuration interface in generic HDLC layer and
when available, convert the driver to it
Please send patches to Krzysztof Halasa <khc@pm.waw.pl>.
\ No newline at end of file
/*
* SBE 2T3E3 synchronous serial card driver for Linux
*
* Copyright (C) 2009-2010 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation.
*
* This code is based on a driver written by SBE Inc.
*/
#include <linux/delay.h>
#include "2t3e3.h"
#include "ctrl.h"
#define bootrom_set_bit(sc, reg, bit) \
bootrom_write((sc), (reg), \
bootrom_read((sc), (reg)) | (bit))
#define bootrom_clear_bit(sc, reg, bit) \
bootrom_write((sc), (reg), \
bootrom_read((sc), (reg)) & ~(bit))
static inline void cpld_set_bit(struct channel *channel, unsigned reg, u32 bit)
{
unsigned long flags;
spin_lock_irqsave(&channel->card->bootrom_lock, flags);
bootrom_set_bit(channel, CPLD_MAP_REG(reg, channel), bit);
spin_unlock_irqrestore(&channel->card->bootrom_lock, flags);
}
static inline void cpld_clear_bit(struct channel *channel, unsigned reg, u32 bit)
{
unsigned long flags;
spin_lock_irqsave(&channel->card->bootrom_lock, flags);
bootrom_clear_bit(channel, CPLD_MAP_REG(reg, channel), bit);
spin_unlock_irqrestore(&channel->card->bootrom_lock, flags);
}
void cpld_init(struct channel *sc)
{
u32 val;
#if 0
/* reset LIU and Framer */
val = cpld_val_map[SBE_2T3E3_CPLD_VAL_LIU_FRAMER_RESET][sc->h.slot];
cpld_write(sc, SBE_2T3E3_CPLD_REG_STATIC_RESET, val);
udelay(10000); /* TODO - how long? */
val = 0;
cpld_write(sc, SBE_2T3E3_CPLD_REG_STATIC_RESET, val);
#endif
/* PCRA */
val = SBE_2T3E3_CPLD_VAL_CRC32 |
cpld_val_map[SBE_2T3E3_CPLD_VAL_LOOP_TIMING_SOURCE][sc->h.slot];
cpld_write(sc, SBE_2T3E3_CPLD_REG_PCRA, val);
/* PCRB */
val = 0;
cpld_write(sc, SBE_2T3E3_CPLD_REG_PCRB, val);
/* PCRC */
val = 0;
cpld_write(sc, SBE_2T3E3_CPLD_REG_PCRC, val);
/* PBWF */
val = 0;
cpld_write(sc, SBE_2T3E3_CPLD_REG_PBWF, val);
/* PBWL */
val = 0;
cpld_write(sc, SBE_2T3E3_CPLD_REG_PBWL, val);
/* PLTR */
val = SBE_2T3E3_CPLD_VAL_LCV_COUNTER;
cpld_write(sc, SBE_2T3E3_CPLD_REG_PLTR, val);
udelay(1000);
/* PLCR */
val = 0;
cpld_write(sc, SBE_2T3E3_CPLD_REG_PLCR, val);
udelay(1000);
/* PPFR */
val = 0x55;
cpld_write(sc, SBE_2T3E3_CPLD_REG_PPFR, val);
/* TODO: this doesn't work!!! */
/* SERIAL_CHIP_SELECT */
val = 0;
cpld_write(sc, SBE_2T3E3_CPLD_REG_SERIAL_CHIP_SELECT, val);
/* PICSR */
val = SBE_2T3E3_CPLD_VAL_DMO_SIGNAL_DETECTED |
SBE_2T3E3_CPLD_VAL_RECEIVE_LOSS_OF_LOCK_DETECTED |
SBE_2T3E3_CPLD_VAL_RECEIVE_LOSS_OF_SIGNAL_DETECTED;
cpld_write(sc, SBE_2T3E3_CPLD_REG_PICSR, val);
cpld_start_intr(sc);
udelay(1000);
}
void cpld_start_intr(struct channel *sc)
{
u32 val;
/* PIER */
val = SBE_2T3E3_CPLD_VAL_INTERRUPT_FROM_ETHERNET_ENABLE |
SBE_2T3E3_CPLD_VAL_INTERRUPT_FROM_FRAMER_ENABLE;
cpld_write(sc, SBE_2T3E3_CPLD_REG_PIER, val);
#if 0
/*
do you want to hang up your computer?
ENABLE REST OF INTERRUPTS !!!
you have been warned :).
*/
#endif
}
void cpld_stop_intr(struct channel *sc)
{
u32 val;
/* PIER */
val = 0;
cpld_write(sc, SBE_2T3E3_CPLD_REG_PIER, val);
}
void cpld_set_frame_mode(struct channel *sc, u32 mode)
{
if (sc->p.frame_mode == mode)
return;
switch (mode) {
case SBE_2T3E3_FRAME_MODE_HDLC:
cpld_clear_bit(sc, SBE_2T3E3_CPLD_REG_PCRA,
SBE_2T3E3_CPLD_VAL_TRANSPARENT_MODE |
SBE_2T3E3_CPLD_VAL_RAW_MODE);
exar7250_unipolar_onoff(sc, SBE_2T3E3_OFF);
exar7300_unipolar_onoff(sc, SBE_2T3E3_OFF);
break;
case SBE_2T3E3_FRAME_MODE_TRANSPARENT:
cpld_clear_bit(sc, SBE_2T3E3_CPLD_REG_PCRA,
SBE_2T3E3_CPLD_VAL_RAW_MODE);
cpld_set_bit(sc, SBE_2T3E3_CPLD_REG_PCRA,
SBE_2T3E3_CPLD_VAL_TRANSPARENT_MODE);
exar7250_unipolar_onoff(sc, SBE_2T3E3_OFF);
exar7300_unipolar_onoff(sc, SBE_2T3E3_OFF);
break;
case SBE_2T3E3_FRAME_MODE_RAW:
cpld_set_bit(sc, SBE_2T3E3_CPLD_REG_PCRA,
SBE_2T3E3_CPLD_VAL_RAW_MODE);
exar7250_unipolar_onoff(sc, SBE_2T3E3_ON);
exar7300_unipolar_onoff(sc, SBE_2T3E3_ON);
break;
default:
return;
}
sc->p.frame_mode = mode;
}
/* set rate of the local clock */
void cpld_set_frame_type(struct channel *sc, u32 type)
{
switch (type) {
case SBE_2T3E3_FRAME_TYPE_E3_G751:
case SBE_2T3E3_FRAME_TYPE_E3_G832:
cpld_set_bit(sc, SBE_2T3E3_CPLD_REG_PCRA,
SBE_2T3E3_CPLD_VAL_LOCAL_CLOCK_E3);
break;
case SBE_2T3E3_FRAME_TYPE_T3_CBIT:
case SBE_2T3E3_FRAME_TYPE_T3_M13:
cpld_clear_bit(sc, SBE_2T3E3_CPLD_REG_PCRA,
SBE_2T3E3_CPLD_VAL_LOCAL_CLOCK_E3);
break;
default:
return;
}
}
void cpld_set_scrambler(struct channel *sc, u32 mode)
{
if (sc->p.scrambler == mode)
return;
switch (mode) {
case SBE_2T3E3_SCRAMBLER_OFF:
cpld_clear_bit(sc, SBE_2T3E3_CPLD_REG_PCRB,
SBE_2T3E3_CPLD_VAL_SCRAMBLER_ENABLE);
break;
case SBE_2T3E3_SCRAMBLER_LARSCOM:
cpld_clear_bit(sc, SBE_2T3E3_CPLD_REG_PCRB,
SBE_2T3E3_CPLD_VAL_SCRAMBLER_TYPE);
cpld_set_bit(sc, SBE_2T3E3_CPLD_REG_PCRB,
SBE_2T3E3_CPLD_VAL_SCRAMBLER_ENABLE);
break;
case SBE_2T3E3_SCRAMBLER_ADC_KENTROX_DIGITAL:
cpld_set_bit(sc, SBE_2T3E3_CPLD_REG_PCRB,
SBE_2T3E3_CPLD_VAL_SCRAMBLER_TYPE);
cpld_set_bit(sc, SBE_2T3E3_CPLD_REG_PCRB,
SBE_2T3E3_CPLD_VAL_SCRAMBLER_ENABLE);
break;
default:
return;
}
sc->p.scrambler = mode;
}
void cpld_set_crc(struct channel *sc, u32 crc)
{
if (sc->p.crc == crc)
return;
switch (crc) {
case SBE_2T3E3_CRC_16:
cpld_clear_bit(sc, SBE_2T3E3_CPLD_REG_PCRA,
SBE_2T3E3_CPLD_VAL_CRC32);
break;
case SBE_2T3E3_CRC_32:
cpld_set_bit(sc, SBE_2T3E3_CPLD_REG_PCRA,
SBE_2T3E3_CPLD_VAL_CRC32);
break;
default:
return;
}
sc->p.crc = crc;
}
void cpld_select_panel(struct channel *sc, u32 panel)
{
if (sc->p.panel == panel)
return;
switch (panel) {
case SBE_2T3E3_PANEL_FRONT:
cpld_clear_bit(sc, SBE_2T3E3_CPLD_REG_PCRA,
SBE_2T3E3_CPLD_VAL_REAR_PANEL);
break;
case SBE_2T3E3_PANEL_REAR:
cpld_set_bit(sc, SBE_2T3E3_CPLD_REG_PCRA,
SBE_2T3E3_CPLD_VAL_REAR_PANEL);
break;
default:
return;
}
udelay(100);
sc->p.panel = panel;
}
extern void cpld_set_clock(struct channel *sc, u32 mode)
{
if (sc->p.clock_source == mode)
return;
switch (mode) {
case SBE_2T3E3_TIMING_LOCAL:
cpld_set_bit(sc, SBE_2T3E3_CPLD_REG_PCRA,
SBE_2T3E3_CPLD_VAL_ALT);
break;
case SBE_2T3E3_TIMING_LOOP:
cpld_clear_bit(sc, SBE_2T3E3_CPLD_REG_PCRA,
SBE_2T3E3_CPLD_VAL_ALT);
break;
default:
return;
}
sc->p.clock_source = mode;
}
void cpld_set_pad_count(struct channel *sc, u32 count)
{
u32 val;
if (sc->p.pad_count == count)
return;
switch (count) {
case SBE_2T3E3_PAD_COUNT_1:
val = SBE_2T3E3_CPLD_VAL_PAD_COUNT_1;
break;
case SBE_2T3E3_PAD_COUNT_2:
val = SBE_2T3E3_CPLD_VAL_PAD_COUNT_2;
break;
case SBE_2T3E3_PAD_COUNT_3:
val = SBE_2T3E3_CPLD_VAL_PAD_COUNT_3;
break;
case SBE_2T3E3_PAD_COUNT_4:
val = SBE_2T3E3_CPLD_VAL_PAD_COUNT_4;
break;
default:
return;
}
cpld_clear_bit(sc, SBE_2T3E3_CPLD_REG_PCRB,
SBE_2T3E3_CPLD_VAL_PAD_COUNT);
cpld_set_bit(sc, SBE_2T3E3_CPLD_REG_PCRB, val);
sc->p.pad_count = count;
}
void cpld_LOS_update(struct channel *sc)
{
u_int8_t los;
cpld_write(sc, SBE_2T3E3_CPLD_REG_PICSR,
SBE_2T3E3_CPLD_VAL_DMO_SIGNAL_DETECTED |
SBE_2T3E3_CPLD_VAL_RECEIVE_LOSS_OF_LOCK_DETECTED |
SBE_2T3E3_CPLD_VAL_RECEIVE_LOSS_OF_SIGNAL_DETECTED);
los = cpld_read(sc, SBE_2T3E3_CPLD_REG_PICSR) &
SBE_2T3E3_CPLD_VAL_RECEIVE_LOSS_OF_SIGNAL_DETECTED;
if (los != sc->s.LOS)
dev_info(&sc->pdev->dev, "SBE 2T3E3: LOS status: %s\n",
los ? "Loss of signal" : "Signal OK");
sc->s.LOS = los;
}
void cpld_set_fractional_mode(struct channel *sc, u32 mode,
u32 start, u32 stop)
{
if (mode == SBE_2T3E3_FRACTIONAL_MODE_NONE) {
start = 0;
stop = 0;
}
if (sc->p.fractional_mode == mode && sc->p.bandwidth_start == start &&
sc->p.bandwidth_stop == stop)
return;
switch (mode) {
case SBE_2T3E3_FRACTIONAL_MODE_NONE:
cpld_write(sc, SBE_2T3E3_CPLD_REG_PCRC,
SBE_2T3E3_CPLD_VAL_FRACTIONAL_MODE_NONE);
break;
case SBE_2T3E3_FRACTIONAL_MODE_0:
cpld_write(sc, SBE_2T3E3_CPLD_REG_PCRC,
SBE_2T3E3_CPLD_VAL_FRACTIONAL_MODE_0);
break;
case SBE_2T3E3_FRACTIONAL_MODE_1:
cpld_write(sc, SBE_2T3E3_CPLD_REG_PCRC,
SBE_2T3E3_CPLD_VAL_FRACTIONAL_MODE_1);
break;
case SBE_2T3E3_FRACTIONAL_MODE_2:
cpld_write(sc, SBE_2T3E3_CPLD_REG_PCRC,
SBE_2T3E3_CPLD_VAL_FRACTIONAL_MODE_2);
break;
default:
printk(KERN_ERR "wrong mode in set_fractional_mode\n");
return;
}
cpld_write(sc, SBE_2T3E3_CPLD_REG_PBWF, start);
cpld_write(sc, SBE_2T3E3_CPLD_REG_PBWL, stop);
sc->p.fractional_mode = mode;
sc->p.bandwidth_start = start;
sc->p.bandwidth_stop = stop;
}
/*
* SBE 2T3E3 synchronous serial card driver for Linux
*
* Copyright (C) 2009-2010 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation.
*
* This code is based on a driver written by SBE Inc.
*/
#include <linux/types.h>
#include "2t3e3.h"
#include "ctrl.h"
void t3e3_set_frame_type(struct channel *sc, u32 mode)
{
if (sc->p.frame_type == mode)
return;
if (sc->r.flags & SBE_2T3E3_FLAG_NETWORK_UP) {
dev_err(&sc->pdev->dev, "SBE 2T3E3: changing frame type during active connection\n");
return;
}
exar7300_set_frame_type(sc, mode);
exar7250_set_frame_type(sc, mode);
cpld_set_frame_type(sc, mode);
sc->p.frame_type = mode;
}
void t3e3_set_loopback(struct channel *sc, u32 mode)
{
u32 tx, rx;
if (sc->p.loopback == mode)
return;
tx = sc->p.transmitter_on;
rx = sc->p.receiver_on;
if (tx == SBE_2T3E3_ON)
dc_transmitter_onoff(sc, SBE_2T3E3_OFF);
if (rx == SBE_2T3E3_ON)
dc_receiver_onoff(sc, SBE_2T3E3_OFF);
/* stop current loopback if any exists */
switch (sc->p.loopback) {
case SBE_2T3E3_LOOPBACK_NONE:
break;
case SBE_2T3E3_LOOPBACK_ETHERNET:
dc_set_loopback(sc, SBE_2T3E3_21143_VAL_LOOPBACK_OFF);
break;
case SBE_2T3E3_LOOPBACK_FRAMER:
exar7250_set_loopback(sc, SBE_2T3E3_FRAMER_VAL_LOOPBACK_OFF);
break;
case SBE_2T3E3_LOOPBACK_LIU_DIGITAL:
case SBE_2T3E3_LOOPBACK_LIU_ANALOG:
case SBE_2T3E3_LOOPBACK_LIU_REMOTE:
exar7300_set_loopback(sc, SBE_2T3E3_LIU_VAL_LOOPBACK_OFF);
break;
default:
return;
}
switch (mode) {
case SBE_2T3E3_LOOPBACK_NONE:
break;
case SBE_2T3E3_LOOPBACK_ETHERNET:
dc_set_loopback(sc, SBE_2T3E3_21143_VAL_LOOPBACK_INTERNAL);
break;
case SBE_2T3E3_LOOPBACK_FRAMER:
exar7250_set_loopback(sc, SBE_2T3E3_FRAMER_VAL_LOOPBACK_ON);
break;
case SBE_2T3E3_LOOPBACK_LIU_DIGITAL:
exar7300_set_loopback(sc, SBE_2T3E3_LIU_VAL_LOOPBACK_DIGITAL);
break;
case SBE_2T3E3_LOOPBACK_LIU_ANALOG:
exar7300_set_loopback(sc, SBE_2T3E3_LIU_VAL_LOOPBACK_ANALOG);
break;
case SBE_2T3E3_LOOPBACK_LIU_REMOTE:
exar7300_set_loopback(sc, SBE_2T3E3_LIU_VAL_LOOPBACK_REMOTE);
break;
default:
return;
}
sc->p.loopback = mode;
if (tx == SBE_2T3E3_ON)
dc_transmitter_onoff(sc, SBE_2T3E3_ON);
if (rx == SBE_2T3E3_ON)
dc_receiver_onoff(sc, SBE_2T3E3_ON);
}
void t3e3_reg_read(struct channel *sc, u32 *reg, u32 *val)
{
u32 i;
*val = 0;
switch (reg[0]) {
case SBE_2T3E3_CHIP_21143:
if (!(reg[1] & 7))
*val = dc_read(sc->addr, reg[1] / 8);
break;
case SBE_2T3E3_CHIP_CPLD:
for (i = 0; i < SBE_2T3E3_CPLD_REG_MAX; i++)
if (cpld_reg_map[i][sc->h.slot] == reg[1]) {
*val = cpld_read(sc, i);
break;
}
break;
case SBE_2T3E3_CHIP_FRAMER:
for (i = 0; i < SBE_2T3E3_FRAMER_REG_MAX; i++)
if (t3e3_framer_reg_map[i] == reg[1]) {
*val = exar7250_read(sc, i);
break;
}
break;
case SBE_2T3E3_CHIP_LIU:
for (i = 0; i < SBE_2T3E3_LIU_REG_MAX; i++)
if (t3e3_liu_reg_map[i] == reg[1]) {
*val = exar7300_read(sc, i);
break;
}
break;
default:
break;
}
}
void t3e3_reg_write(struct channel *sc, u32 *reg)
{
u32 i;
switch (reg[0]) {
case SBE_2T3E3_CHIP_21143:
dc_write(sc->addr, reg[1], reg[2]);
break;
case SBE_2T3E3_CHIP_CPLD:
for (i = 0; i < SBE_2T3E3_CPLD_REG_MAX; i++)
if (cpld_reg_map[i][sc->h.slot] == reg[1]) {
cpld_write(sc, i, reg[2]);
break;
}
break;
case SBE_2T3E3_CHIP_FRAMER:
for (i = 0; i < SBE_2T3E3_FRAMER_REG_MAX; i++)
if (t3e3_framer_reg_map[i] == reg[1]) {
exar7250_write(sc, i, reg[2]);
break;
}
break;
case SBE_2T3E3_CHIP_LIU:
for (i = 0; i < SBE_2T3E3_LIU_REG_MAX; i++)
if (t3e3_liu_reg_map[i] == reg[1]) {
exar7300_write(sc, i, reg[2]);
break;
}
break;
}
}
void t3e3_port_get(struct channel *sc, t3e3_param_t *param)
{
memcpy(param, &(sc->p), sizeof(t3e3_param_t));
}
void t3e3_port_set(struct channel *sc, t3e3_param_t *param)
{
if (param->frame_mode != 0xff)
cpld_set_frame_mode(sc, param->frame_mode);
if (param->fractional_mode != 0xff)
cpld_set_fractional_mode(sc, param->fractional_mode,
param->bandwidth_start,
param->bandwidth_stop);
if (param->pad_count != 0xff)
cpld_set_pad_count(sc, param->pad_count);
if (param->crc != 0xff)
cpld_set_crc(sc, param->crc);
if (param->receiver_on != 0xff)
dc_receiver_onoff(sc, param->receiver_on);
if (param->transmitter_on != 0xff)
dc_transmitter_onoff(sc, param->transmitter_on);
if (param->frame_type != 0xff)
t3e3_set_frame_type(sc, param->frame_type);
if (param->panel != 0xff)
cpld_select_panel(sc, param->panel);
if (param->line_build_out != 0xff)
exar7300_line_build_out_onoff(sc, param->line_build_out);
if (param->receive_equalization != 0xff)
exar7300_receive_equalization_onoff(sc, param->receive_equalization);
if (param->transmit_all_ones != 0xff)
exar7300_transmit_all_ones_onoff(sc, param->transmit_all_ones);
if (param->loopback != 0xff)
t3e3_set_loopback(sc, param->loopback);
if (param->clock_source != 0xff)
cpld_set_clock(sc, param->clock_source);
if (param->scrambler != 0xff)
cpld_set_scrambler(sc, param->scrambler);
}
void t3e3_port_get_stats(struct channel *sc,
t3e3_stats_t *stats)
{
u32 result;
sc->s.LOC = exar7250_read(sc, SBE_2T3E3_FRAMER_REG_IO_CONTROL)
& SBE_2T3E3_FRAMER_VAL_LOSS_OF_CLOCK_STATUS ? 1 : 0;
switch (sc->p.frame_type) {
case SBE_2T3E3_FRAME_TYPE_E3_G751:
case SBE_2T3E3_FRAME_TYPE_E3_G832:
result = exar7250_read(sc, SBE_2T3E3_FRAMER_REG_E3_RX_CONFIGURATION_STATUS_2);
sc->s.LOF = result & SBE_2T3E3_FRAMER_VAL_E3_RX_LOF ? 1 : 0;
sc->s.OOF = result & SBE_2T3E3_FRAMER_VAL_E3_RX_OOF ? 1 : 0;
#if 0
sc->s.LOS = result & SBE_2T3E3_FRAMER_VAL_E3_RX_LOS ? 1 : 0;
#else
cpld_LOS_update(sc);
#endif
sc->s.AIS = result & SBE_2T3E3_FRAMER_VAL_E3_RX_AIS ? 1 : 0;
sc->s.FERF = result & SBE_2T3E3_FRAMER_VAL_E3_RX_FERF ? 1 : 0;
break;
case SBE_2T3E3_FRAME_TYPE_T3_CBIT:
case SBE_2T3E3_FRAME_TYPE_T3_M13:
result = exar7250_read(sc, SBE_2T3E3_FRAMER_REG_T3_RX_CONFIGURATION_STATUS);
sc->s.AIS = result & SBE_2T3E3_FRAMER_VAL_T3_RX_AIS ? 1 : 0;
#if 0
sc->s.LOS = result & SBE_2T3E3_FRAMER_VAL_T3_RX_LOS ? 1 : 0;
#else
cpld_LOS_update(sc);
#endif
sc->s.IDLE = result & SBE_2T3E3_FRAMER_VAL_T3_RX_IDLE ? 1 : 0;
sc->s.OOF = result & SBE_2T3E3_FRAMER_VAL_T3_RX_OOF ? 1 : 0;
result = exar7250_read(sc, SBE_2T3E3_FRAMER_REG_T3_RX_STATUS);
sc->s.FERF = result & SBE_2T3E3_FRAMER_VAL_T3_RX_FERF ? 1 : 0;
sc->s.AIC = result & SBE_2T3E3_FRAMER_VAL_T3_RX_AIC ? 1 : 0;
sc->s.FEBE_code = result & SBE_2T3E3_FRAMER_VAL_T3_RX_FEBE;
sc->s.FEAC = exar7250_read(sc, SBE_2T3E3_FRAMER_REG_T3_RX_FEAC);
break;
default:
break;
}
result = exar7250_read(sc, SBE_2T3E3_FRAMER_REG_PMON_LCV_EVENT_COUNT_MSB) << 8;
result += exar7250_read(sc, SBE_2T3E3_FRAMER_REG_PMON_HOLDING_REGISTER);
sc->s.LCV += result;
result = exar7250_read(sc, SBE_2T3E3_FRAMER_REG_PMON_FRAMING_BIT_ERROR_EVENT_COUNT_MSB) << 8;
result += exar7250_read(sc, SBE_2T3E3_FRAMER_REG_PMON_HOLDING_REGISTER);
sc->s.FRAMING_BIT += result;
result = exar7250_read(sc, SBE_2T3E3_FRAMER_REG_PMON_PARITY_ERROR_EVENT_COUNT_MSB) << 8;
result += exar7250_read(sc, SBE_2T3E3_FRAMER_REG_PMON_HOLDING_REGISTER);
sc->s.PARITY_ERROR += result;
result = exar7250_read(sc, SBE_2T3E3_FRAMER_REG_PMON_FEBE_EVENT_COUNT_MSB) << 8;
result += exar7250_read(sc, SBE_2T3E3_FRAMER_REG_PMON_HOLDING_REGISTER);
sc->s.FEBE_count += result;
result = exar7250_read(sc, SBE_2T3E3_FRAMER_REG_PMON_CP_BIT_ERROR_EVENT_COUNT_MSB) << 8;
result += exar7250_read(sc, SBE_2T3E3_FRAMER_REG_PMON_HOLDING_REGISTER);
sc->s.CP_BIT += result;
memcpy(stats, &(sc->s), sizeof(t3e3_stats_t));
}
void t3e3_port_del_stats(struct channel *sc)
{
memset(&(sc->s), 0, sizeof(t3e3_stats_t));
}
void t3e3_if_config(struct channel *sc, u32 cmd, char *set,
t3e3_resp_t *ret, int *rlen)
{
t3e3_param_t *param = (t3e3_param_t *)set;
u32 *data = (u32 *)set;
/* turn off all interrupt */
/* cpld_stop_intr(sc); */
switch (cmd) {
case SBE_2T3E3_PORT_GET:
t3e3_port_get(sc, &(ret->u.param));
*rlen = sizeof(ret->u.param);
break;
case SBE_2T3E3_PORT_SET:
t3e3_port_set(sc, param);
*rlen = 0;
break;
case SBE_2T3E3_PORT_GET_STATS:
t3e3_port_get_stats(sc, &(ret->u.stats));
*rlen = sizeof(ret->u.stats);
break;
case SBE_2T3E3_PORT_DEL_STATS:
t3e3_port_del_stats(sc);
*rlen = 0;
break;
case SBE_2T3E3_PORT_READ_REGS:
t3e3_reg_read(sc, data, &(ret->u.data));
*rlen = sizeof(ret->u.data);
break;
case SBE_2T3E3_PORT_WRITE_REGS:
#if 0
printk(KERN_DEBUG "SBE_2T3E3_PORT_WRITE_REGS, 0x%x, 0x%x, 0x%x\n",
((int*)data)[0], ((int*)data)[1], ((int*)data)[2]);
#endif
t3e3_reg_write(sc, data);
*rlen = 0;
break;
case SBE_2T3E3_LOG_LEVEL:
*rlen = 0;
break;
default:
*rlen = 0;
break;
}
/* turn on interrupt */
/* cpld_start_intr(sc); */
}
void t3e3_sc_init(struct channel *sc)
{
memset(sc, 0, sizeof(*sc));
sc->p.frame_mode = SBE_2T3E3_FRAME_MODE_HDLC;
sc->p.fractional_mode = SBE_2T3E3_FRACTIONAL_MODE_NONE;
sc->p.crc = SBE_2T3E3_CRC_32;
sc->p.receiver_on = SBE_2T3E3_OFF;
sc->p.transmitter_on = SBE_2T3E3_OFF;
sc->p.frame_type = SBE_2T3E3_FRAME_TYPE_T3_CBIT;
sc->p.panel = SBE_2T3E3_PANEL_FRONT;
sc->p.line_build_out = SBE_2T3E3_OFF;
sc->p.receive_equalization = SBE_2T3E3_OFF;
sc->p.transmit_all_ones = SBE_2T3E3_OFF;
sc->p.loopback = SBE_2T3E3_LOOPBACK_NONE;
sc->p.clock_source = SBE_2T3E3_TIMING_LOCAL;
sc->p.scrambler = SBE_2T3E3_SCRAMBLER_OFF;
sc->p.pad_count = SBE_2T3E3_PAD_COUNT_1;
}
/*
* SBE 2T3E3 synchronous serial card driver for Linux
*
* Copyright (C) 2009-2010 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation.
*
* This code is based on a driver written by SBE Inc.
*/
#ifndef CTRL_H
#define CTRL_H
#define SBE_2T3E3_OFF 0
#define SBE_2T3E3_ON 1
#define SBE_2T3E3_LED_NONE 0
#define SBE_2T3E3_LED_GREEN 1
#define SBE_2T3E3_LED_YELLOW 2
#define SBE_2T3E3_CABLE_LENGTH_LESS_THAN_255_FEET 0
#define SBE_2T3E3_CABLE_LENGTH_GREATER_THAN_255_FEET 1
#define SBE_2T3E3_CRC_16 0
#define SBE_2T3E3_CRC_32 1
#define SBE_2T3E3_PANEL_FRONT 0
#define SBE_2T3E3_PANEL_REAR 1
#define SBE_2T3E3_FRAME_MODE_HDLC 0
#define SBE_2T3E3_FRAME_MODE_TRANSPARENT 1
#define SBE_2T3E3_FRAME_MODE_RAW 2
#define SBE_2T3E3_FRAME_TYPE_E3_G751 0
#define SBE_2T3E3_FRAME_TYPE_E3_G832 1
#define SBE_2T3E3_FRAME_TYPE_T3_CBIT 2
#define SBE_2T3E3_FRAME_TYPE_T3_M13 3
#define SBE_2T3E3_FRACTIONAL_MODE_NONE 0
#define SBE_2T3E3_FRACTIONAL_MODE_0 1
#define SBE_2T3E3_FRACTIONAL_MODE_1 2
#define SBE_2T3E3_FRACTIONAL_MODE_2 3
#define SBE_2T3E3_SCRAMBLER_OFF 0
#define SBE_2T3E3_SCRAMBLER_LARSCOM 1
#define SBE_2T3E3_SCRAMBLER_ADC_KENTROX_DIGITAL 2
#define SBE_2T3E3_TIMING_LOCAL 0
#define SBE_2T3E3_TIMING_LOOP 1
#define SBE_2T3E3_LOOPBACK_NONE 0
#define SBE_2T3E3_LOOPBACK_ETHERNET 1
#define SBE_2T3E3_LOOPBACK_FRAMER 2
#define SBE_2T3E3_LOOPBACK_LIU_DIGITAL 3
#define SBE_2T3E3_LOOPBACK_LIU_ANALOG 4
#define SBE_2T3E3_LOOPBACK_LIU_REMOTE 5
#define SBE_2T3E3_PAD_COUNT_1 1
#define SBE_2T3E3_PAD_COUNT_2 2
#define SBE_2T3E3_PAD_COUNT_3 3
#define SBE_2T3E3_PAD_COUNT_4 4
#define SBE_2T3E3_CHIP_21143 0
#define SBE_2T3E3_CHIP_CPLD 1
#define SBE_2T3E3_CHIP_FRAMER 2
#define SBE_2T3E3_CHIP_LIU 3
#define SBE_2T3E3_LOG_LEVEL_NONE 0
#define SBE_2T3E3_LOG_LEVEL_ERROR 1
#define SBE_2T3E3_LOG_LEVEL_WARNING 2
#define SBE_2T3E3_LOG_LEVEL_INFO 3
/* commands */
#define SBE_2T3E3_PORT_GET 0
#define SBE_2T3E3_PORT_SET 1
#define SBE_2T3E3_PORT_GET_STATS 2
#define SBE_2T3E3_PORT_DEL_STATS 3
#define SBE_2T3E3_PORT_READ_REGS 4
#define SBE_2T3E3_LOG_LEVEL 5
#define SBE_2T3E3_PORT_WRITE_REGS 6
#define NG_SBE_2T3E3_NODE_TYPE "sbe2T3E3"
#define NG_SBE_2T3E3_COOKIE 0x03800891
typedef struct t3e3_param {
u_int8_t frame_mode; /* FRAME_MODE_* */
u_int8_t crc; /* CRC_* */
u_int8_t receiver_on; /* ON/OFF */
u_int8_t transmitter_on; /* ON/OFF */
u_int8_t frame_type; /* FRAME_TYPE_* */
u_int8_t panel; /* PANEL_* */
u_int8_t line_build_out; /* ON/OFF */
u_int8_t receive_equalization; /* ON/OFF */
u_int8_t transmit_all_ones; /* ON/OFF */
u_int8_t loopback; /* LOOPBACK_* */
u_int8_t clock_source; /* TIMING_* */
u_int8_t scrambler; /* SCRAMBLER_* */
u_int8_t pad_count; /* PAD_COUNT_* */
u_int8_t log_level; /* LOG_LEVEL_* - unused */
u_int8_t fractional_mode; /* FRACTIONAL_MODE_* */
u_int8_t bandwidth_start; /* 0-255 */
u_int8_t bandwidth_stop; /* 0-255 */
} t3e3_param_t;
typedef struct t3e3_stats {
u_int64_t in_bytes;
u32 in_packets, in_dropped;
u32 in_errors, in_error_desc, in_error_coll, in_error_drib,
in_error_crc, in_error_mii;
u_int64_t out_bytes;
u32 out_packets, out_dropped;
u32 out_errors, out_error_jab, out_error_lost_carr,
out_error_no_carr, out_error_link_fail, out_error_underflow,
out_error_dereferred;
u_int8_t LOC, LOF, OOF, LOS, AIS, FERF, IDLE, AIC, FEAC;
u_int16_t FEBE_code;
u32 LCV, FRAMING_BIT, PARITY_ERROR, FEBE_count, CP_BIT;
} t3e3_stats_t;
typedef struct t3e3_resp {
union {
t3e3_param_t param;
t3e3_stats_t stats;
u32 data;
} u;
} t3e3_resp_t;
#endif /* CTRL_H */
This diff is collapsed.
/*
* SBE 2T3E3 synchronous serial card driver for Linux
*
* Copyright (C) 2009-2010 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation.
*
* This code is based on a driver written by SBE Inc.
*/
#include "2t3e3.h"
#include "ctrl.h"
void exar7250_init(struct channel *sc)
{
exar7250_write(sc, SBE_2T3E3_FRAMER_REG_OPERATING_MODE,
SBE_2T3E3_FRAMER_VAL_T3_CBIT |
SBE_2T3E3_FRAMER_VAL_INTERRUPT_ENABLE_RESET |
SBE_2T3E3_FRAMER_VAL_TIMING_ASYNCH_TXINCLK);
exar7250_write(sc, SBE_2T3E3_FRAMER_REG_IO_CONTROL,
SBE_2T3E3_FRAMER_VAL_DISABLE_TX_LOSS_OF_CLOCK |
SBE_2T3E3_FRAMER_VAL_DISABLE_RX_LOSS_OF_CLOCK |
SBE_2T3E3_FRAMER_VAL_AMI_LINE_CODE |
SBE_2T3E3_FRAMER_VAL_RX_LINE_CLOCK_INVERT);
exar7250_set_frame_type(sc, SBE_2T3E3_FRAME_TYPE_T3_CBIT);
}
void exar7250_set_frame_type(struct channel *sc, u32 type)
{
u32 val;
switch (type) {
case SBE_2T3E3_FRAME_TYPE_E3_G751:
case SBE_2T3E3_FRAME_TYPE_E3_G832:
case SBE_2T3E3_FRAME_TYPE_T3_CBIT:
case SBE_2T3E3_FRAME_TYPE_T3_M13:
break;
default:
return;
}
exar7250_stop_intr(sc, type);
val = exar7250_read(sc, SBE_2T3E3_FRAMER_REG_OPERATING_MODE);
val &= ~(SBE_2T3E3_FRAMER_VAL_LOCAL_LOOPBACK_MODE |
SBE_2T3E3_FRAMER_VAL_T3_E3_SELECT |
SBE_2T3E3_FRAMER_VAL_FRAME_FORMAT_SELECT);
switch (type) {
case SBE_2T3E3_FRAME_TYPE_E3_G751:
val |= SBE_2T3E3_FRAMER_VAL_E3_G751;
break;
case SBE_2T3E3_FRAME_TYPE_E3_G832:
val |= SBE_2T3E3_FRAMER_VAL_E3_G832;
break;
case SBE_2T3E3_FRAME_TYPE_T3_CBIT:
val |= SBE_2T3E3_FRAMER_VAL_T3_CBIT;
break;
case SBE_2T3E3_FRAME_TYPE_T3_M13:
val |= SBE_2T3E3_FRAMER_VAL_T3_M13;
break;
default:
return;
}
exar7250_write(sc, SBE_2T3E3_FRAMER_REG_OPERATING_MODE, val);
exar7250_start_intr(sc, type);
}
void exar7250_start_intr(struct channel *sc, u32 type)
{
u32 val;
switch (type) {
case SBE_2T3E3_FRAME_TYPE_E3_G751:
case SBE_2T3E3_FRAME_TYPE_E3_G832:
val = exar7250_read(sc, SBE_2T3E3_FRAMER_REG_E3_RX_CONFIGURATION_STATUS_2);
#if 0
sc->s.LOS = val & SBE_2T3E3_FRAMER_VAL_E3_RX_LOS ? 1 : 0;
#else
cpld_LOS_update(sc);
#endif
sc->s.OOF = val & SBE_2T3E3_FRAMER_VAL_E3_RX_OOF ? 1 : 0;
exar7250_read(sc, SBE_2T3E3_FRAMER_REG_E3_RX_INTERRUPT_STATUS_1);
exar7250_write(sc, SBE_2T3E3_FRAMER_REG_E3_RX_INTERRUPT_ENABLE_1,
SBE_2T3E3_FRAMER_VAL_E3_RX_OOF_INTERRUPT_ENABLE |
SBE_2T3E3_FRAMER_VAL_E3_RX_LOS_INTERRUPT_ENABLE);
#if 0
/*SBE_2T3E3_FRAMER_VAL_E3_RX_COFA_INTERRUPT_ENABLE |
SBE_2T3E3_FRAMER_VAL_E3_RX_OOF_INTERRUPT_ENABLE |
SBE_2T3E3_FRAMER_VAL_E3_RX_LOF_INTERRUPT_ENABLE |
SBE_2T3E3_FRAMER_VAL_E3_RX_LOS_INTERRUPT_ENABLE |
SBE_2T3E3_FRAMER_VAL_E3_RX_AIS_INTERRUPT_ENABLE);*/
#endif
exar7250_read(sc, SBE_2T3E3_FRAMER_REG_E3_RX_INTERRUPT_STATUS_2);
#if 0
exar7250_write(sc, SBE_2T3E3_FRAMER_REG_E3_RX_INTERRUPT_ENABLE_2,
SBE_2T3E3_FRAMER_VAL_E3_RX_FEBE_INTERRUPT_ENABLE |
SBE_2T3E3_FRAMER_VAL_E3_RX_FERF_INTERRUPT_ENABLE |
SBE_2T3E3_FRAMER_VAL_E3_RX_FRAMING_BYTE_ERROR_INTERRUPT_ENABLE);
#endif
break;
case SBE_2T3E3_FRAME_TYPE_T3_CBIT:
case SBE_2T3E3_FRAME_TYPE_T3_M13:
val = exar7250_read(sc, SBE_2T3E3_FRAMER_REG_T3_RX_CONFIGURATION_STATUS);
#if 0
sc->s.LOS = val & SBE_2T3E3_FRAMER_VAL_T3_RX_LOS ? 1 : 0;
#else
cpld_LOS_update(sc);
#endif
sc->s.OOF = val & SBE_2T3E3_FRAMER_VAL_T3_RX_OOF ? 1 : 0;
exar7250_read(sc, SBE_2T3E3_FRAMER_REG_T3_RX_INTERRUPT_STATUS);
exar7250_write(sc, SBE_2T3E3_FRAMER_REG_T3_RX_INTERRUPT_ENABLE,
SBE_2T3E3_FRAMER_VAL_T3_RX_LOS_INTERRUPT_ENABLE |
SBE_2T3E3_FRAMER_VAL_T3_RX_OOF_INTERRUPT_ENABLE);
#if 0
/* SBE_2T3E3_FRAMER_VAL_T3_RX_CP_BIT_ERROR_INTERRUPT_ENABLE |
SBE_2T3E3_FRAMER_VAL_T3_RX_LOS_INTERRUPT_ENABLE |
SBE_2T3E3_FRAMER_VAL_T3_RX_AIS_INTERRUPT_ENABLE |
SBE_2T3E3_FRAMER_VAL_T3_RX_IDLE_INTERRUPT_ENABLE |
SBE_2T3E3_FRAMER_VAL_T3_RX_FERF_INTERRUPT_ENABLE |
SBE_2T3E3_FRAMER_VAL_T3_RX_AIC_INTERRUPT_ENABLE |
SBE_2T3E3_FRAMER_VAL_T3_RX_OOF_INTERRUPT_ENABLE |
SBE_2T3E3_FRAMER_VAL_T3_RX_P_BIT_INTERRUPT_ENABLE);*/
#endif
exar7250_read(sc, SBE_2T3E3_FRAMER_REG_T3_RX_FEAC_INTERRUPT_ENABLE_STATUS);
#if 0
exar7250_write(sc, SBE_2T3E3_FRAMER_REG_T3_RX_FEAC_INTERRUPT_ENABLE_STATUS,
SBE_2T3E3_FRAMER_VAL_T3_RX_FEAC_REMOVE_INTERRUPT_ENABLE |
SBE_2T3E3_FRAMER_VAL_T3_RX_FEAC_VALID_INTERRUPT_ENABLE);
#endif
exar7250_write(sc, SBE_2T3E3_FRAMER_REG_T3_RX_LAPD_CONTROL, 0);
break;
default:
return;
}
exar7250_read(sc, SBE_2T3E3_FRAMER_REG_BLOCK_INTERRUPT_STATUS);
exar7250_write(sc, SBE_2T3E3_FRAMER_REG_BLOCK_INTERRUPT_ENABLE,
SBE_2T3E3_FRAMER_VAL_RX_INTERRUPT_ENABLE |
SBE_2T3E3_FRAMER_VAL_TX_INTERRUPT_ENABLE);
}
void exar7250_stop_intr(struct channel *sc, u32 type)
{
exar7250_write(sc, SBE_2T3E3_FRAMER_REG_BLOCK_INTERRUPT_ENABLE, 0);
exar7250_read(sc, SBE_2T3E3_FRAMER_REG_BLOCK_INTERRUPT_STATUS);
switch (type) {
case SBE_2T3E3_FRAME_TYPE_E3_G751:
case SBE_2T3E3_FRAME_TYPE_E3_G832:
exar7250_write(sc, SBE_2T3E3_FRAMER_REG_E3_RX_INTERRUPT_ENABLE_1, 0);
exar7250_read(sc, SBE_2T3E3_FRAMER_REG_E3_RX_INTERRUPT_STATUS_1);
exar7250_write(sc, SBE_2T3E3_FRAMER_REG_E3_RX_INTERRUPT_ENABLE_2, 0);
exar7250_read(sc, SBE_2T3E3_FRAMER_REG_E3_RX_INTERRUPT_STATUS_2);
exar7250_write(sc, SBE_2T3E3_FRAMER_REG_E3_RX_LAPD_CONTROL, 0);
exar7250_read(sc, SBE_2T3E3_FRAMER_REG_E3_RX_LAPD_CONTROL);
exar7250_write(sc, SBE_2T3E3_FRAMER_REG_E3_TX_LAPD_STATUS, 0);
exar7250_read(sc, SBE_2T3E3_FRAMER_REG_E3_TX_LAPD_STATUS);
break;
case SBE_2T3E3_FRAME_TYPE_T3_CBIT:
case SBE_2T3E3_FRAME_TYPE_T3_M13:
exar7250_write(sc, SBE_2T3E3_FRAMER_REG_T3_RX_INTERRUPT_ENABLE, 0);
exar7250_read(sc, SBE_2T3E3_FRAMER_REG_T3_RX_INTERRUPT_STATUS);
exar7250_write(sc, SBE_2T3E3_FRAMER_REG_T3_RX_FEAC_INTERRUPT_ENABLE_STATUS, 0);
exar7250_read(sc, SBE_2T3E3_FRAMER_REG_T3_RX_FEAC_INTERRUPT_ENABLE_STATUS);
exar7250_write(sc, SBE_2T3E3_FRAMER_REG_T3_RX_LAPD_CONTROL, 0);
exar7250_read(sc, SBE_2T3E3_FRAMER_REG_T3_RX_LAPD_CONTROL);
exar7250_write(sc, SBE_2T3E3_FRAMER_REG_T3_TX_FEAC_CONFIGURATION_STATUS, 0);
exar7250_read(sc, SBE_2T3E3_FRAMER_REG_T3_TX_FEAC_CONFIGURATION_STATUS);
exar7250_write(sc, SBE_2T3E3_FRAMER_REG_T3_TX_LAPD_STATUS, 0);
exar7250_read(sc, SBE_2T3E3_FRAMER_REG_T3_TX_LAPD_STATUS);
break;
}
}
void exar7250_unipolar_onoff(struct channel *sc, u32 mode)
{
switch (mode) {
case SBE_2T3E3_OFF:
exar7300_clear_bit(sc, SBE_2T3E3_FRAMER_REG_IO_CONTROL,
SBE_2T3E3_FRAMER_VAL_UNIPOLAR);
break;
case SBE_2T3E3_ON:
exar7300_set_bit(sc, SBE_2T3E3_FRAMER_REG_IO_CONTROL,
SBE_2T3E3_FRAMER_VAL_UNIPOLAR);
break;
}
}
void exar7250_set_loopback(struct channel *sc, u32 mode)
{
switch (mode) {
case SBE_2T3E3_FRAMER_VAL_LOOPBACK_OFF:
exar7300_clear_bit(sc, SBE_2T3E3_FRAMER_REG_OPERATING_MODE,
SBE_2T3E3_FRAMER_VAL_LOCAL_LOOPBACK_MODE);
break;
case SBE_2T3E3_FRAMER_VAL_LOOPBACK_ON:
exar7300_set_bit(sc, SBE_2T3E3_FRAMER_REG_OPERATING_MODE,
SBE_2T3E3_FRAMER_VAL_LOCAL_LOOPBACK_MODE);
break;
}
}
/*
* SBE 2T3E3 synchronous serial card driver for Linux
*
* Copyright (C) 2009-2010 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation.
*
* This code is based on a driver written by SBE Inc.
*/
#include "2t3e3.h"
#include "ctrl.h"
void exar7300_init(struct channel *sc)
{
exar7300_write(sc, SBE_2T3E3_LIU_REG_REG1, 0);
/* enable line decodeer and encoder */
exar7300_write(sc, SBE_2T3E3_LIU_REG_REG2, 0);
exar7300_write(sc, SBE_2T3E3_LIU_REG_REG3, 0);
exar7300_write(sc, SBE_2T3E3_LIU_REG_REG4,
SBE_2T3E3_LIU_VAL_T3_MODE_SELECT |
SBE_2T3E3_LIU_VAL_LOOPBACK_OFF);
}
void exar7300_set_loopback(struct channel *sc, u32 mode)
{
u32 val;
switch (mode) {
case SBE_2T3E3_LIU_VAL_LOOPBACK_OFF:
case SBE_2T3E3_LIU_VAL_LOOPBACK_REMOTE:
case SBE_2T3E3_LIU_VAL_LOOPBACK_ANALOG:
case SBE_2T3E3_LIU_VAL_LOOPBACK_DIGITAL:
break;
default:
return;
}
val = exar7300_read(sc, SBE_2T3E3_LIU_REG_REG4);
val &= ~(SBE_2T3E3_LIU_VAL_LOCAL_LOOPBACK | SBE_2T3E3_LIU_VAL_REMOTE_LOOPBACK);
val |= mode;
exar7300_write(sc, SBE_2T3E3_LIU_REG_REG4, val);
#if 0
/* TODO - is it necessary? idea from 2T3E3_HW_Test_code */
switch (mode) {
case SBE_2T3E3_LIU_VAL_LOOPBACK_OFF:
break;
case SBE_2T3E3_LIU_VAL_LOOPBACK_REMOTE:
exar7300_receive_equalization_onoff(sc, SBE_2T3E3_ON);
break;
case SBE_2T3E3_LIU_VAL_LOOPBACK_ANALOG:
exar7300_receive_equalization_onoff(sc, SBE_2T3E3_OFF);
break;
case SBE_2T3E3_LIU_VAL_LOOPBACK_DIGITAL:
exar7300_receive_equalization_onoff(sc, SBE_2T3E3_ON);
break;
}
#endif
}
void exar7300_set_frame_type(struct channel *sc, u32 type)
{
u32 val;
switch (type) {
case SBE_2T3E3_FRAME_TYPE_T3_CBIT:
case SBE_2T3E3_FRAME_TYPE_T3_M13:
case SBE_2T3E3_FRAME_TYPE_E3_G751:
case SBE_2T3E3_FRAME_TYPE_E3_G832:
break;
default:
return;
}
val = exar7300_read(sc, SBE_2T3E3_LIU_REG_REG4);
val &= ~(SBE_2T3E3_LIU_VAL_T3_MODE_SELECT |
SBE_2T3E3_LIU_VAL_E3_MODE_SELECT);
switch (type) {
case SBE_2T3E3_FRAME_TYPE_T3_CBIT:
case SBE_2T3E3_FRAME_TYPE_T3_M13:
val |= SBE_2T3E3_LIU_VAL_T3_MODE_SELECT;
break;
case SBE_2T3E3_FRAME_TYPE_E3_G751:
case SBE_2T3E3_FRAME_TYPE_E3_G832:
val |= SBE_2T3E3_LIU_VAL_E3_MODE_SELECT;
break;
default:
return;
}
exar7300_write(sc, SBE_2T3E3_LIU_REG_REG4, val);
}
void exar7300_transmit_all_ones_onoff(struct channel *sc, u32 mode)
{
if (sc->p.transmit_all_ones == mode)
return;
switch (mode) {
case SBE_2T3E3_ON:
exar7300_set_bit(sc, SBE_2T3E3_LIU_REG_REG1,
SBE_2T3E3_LIU_VAL_TRANSMIT_ALL_ONES);
break;
case SBE_2T3E3_OFF:
exar7300_clear_bit(sc, SBE_2T3E3_LIU_REG_REG1,
SBE_2T3E3_LIU_VAL_TRANSMIT_ALL_ONES);
break;
default:
return;
}
sc->p.transmit_all_ones = mode;
}
void exar7300_receive_equalization_onoff(struct channel *sc, u32 mode)
{
if (sc->p.receive_equalization == mode)
return;
switch (mode) {
case SBE_2T3E3_OFF:
exar7300_set_bit(sc, SBE_2T3E3_LIU_REG_REG2,
SBE_2T3E3_LIU_VAL_RECEIVE_EQUALIZATION_DISABLE);
break;
case SBE_2T3E3_ON:
exar7300_clear_bit(sc, SBE_2T3E3_LIU_REG_REG2,
SBE_2T3E3_LIU_VAL_RECEIVE_EQUALIZATION_DISABLE);
break;
default:
return;
}
sc->p.receive_equalization = mode;
}
void exar7300_line_build_out_onoff(struct channel *sc, u32 mode)
{
if (sc->p.line_build_out == mode)
return;
switch (mode) {
case SBE_2T3E3_OFF:
exar7300_set_bit(sc, SBE_2T3E3_LIU_REG_REG1,
SBE_2T3E3_LIU_VAL_TRANSMIT_LEVEL_SELECT);
exar7300_receive_equalization_onoff(sc, SBE_2T3E3_OFF);
break;
case SBE_2T3E3_ON:
exar7300_clear_bit(sc, SBE_2T3E3_LIU_REG_REG1,
SBE_2T3E3_LIU_VAL_TRANSMIT_LEVEL_SELECT);
exar7300_receive_equalization_onoff(sc, SBE_2T3E3_ON);
break;
default:
return;
}
sc->p.line_build_out = mode;
}
/* TODO - what about encoder in raw mode??? disable it too? */
void exar7300_unipolar_onoff(struct channel *sc, u32 mode)
{
switch (mode) {
case SBE_2T3E3_OFF:
exar7300_clear_bit(sc, SBE_2T3E3_LIU_REG_REG3,
SBE_2T3E3_LIU_VAL_DECODER_DISABLE);
exar7300_clear_bit(sc, SBE_2T3E3_LIU_REG_REG1,
SBE_2T3E3_LIU_VAL_TRANSMIT_BINARY_DATA);
break;
case SBE_2T3E3_ON:
exar7300_set_bit(sc, SBE_2T3E3_LIU_REG_REG3,
SBE_2T3E3_LIU_VAL_DECODER_DISABLE);
exar7300_set_bit(sc, SBE_2T3E3_LIU_REG_REG1,
SBE_2T3E3_LIU_VAL_TRANSMIT_BINARY_DATA);
break;
}
}
This diff is collapsed.
This diff is collapsed.
/*
* SBE 2T3E3 synchronous serial card driver for Linux
*
* Copyright (C) 2009-2010 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation.
*
* This code is based on a driver written by SBE Inc.
*/
#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include "2t3e3.h"
void t3e3_init(struct channel *sc)
{
cpld_init(sc);
dc_reset(sc);
dc_init(sc);
exar7250_init(sc);
exar7300_init(sc);
}
int t3e3_if_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct channel *sc = dev_to_priv(dev);
u32 current_write, last_write;
unsigned long flags;
struct sk_buff *skb2;
if (skb == NULL) {
sc->s.out_errors++;
return 0;
}
if (sc->p.transmitter_on != SBE_2T3E3_ON) {
sc->s.out_errors++;
sc->s.out_dropped++;
dev_kfree_skb_any(skb);
return 0;
}
if (sc->s.OOF && sc->p.loopback == SBE_2T3E3_LOOPBACK_NONE) {
sc->s.out_dropped++;
dev_kfree_skb_any(skb);
return 0;
}
spin_lock_irqsave(&sc->ether.tx_lock, flags);
current_write = sc->ether.tx_ring_current_write;
for (skb2 = skb; skb2 != NULL; skb2 = NULL) {
if (skb2->len) {
if ((sc->ether.tx_ring[current_write].tdes1 &
SBE_2T3E3_TX_DESC_BUFFER_1_SIZE) > 0)
break;
current_write = (current_write + 1) % SBE_2T3E3_TX_DESC_RING_SIZE;
/*
* Leave at least 1 tx desc free so that dc_intr_tx() can
* identify empty list
*/
if (current_write == sc->ether.tx_ring_current_read)
break;
}
}
if (skb2 != NULL) {
netif_stop_queue(sc->dev);
sc->ether.tx_full = 1;
dev_dbg(&sc->pdev->dev, "SBE 2T3E3: out of descriptors\n");
spin_unlock_irqrestore(&sc->ether.tx_lock, flags);
return NETDEV_TX_BUSY;
}
current_write = last_write = sc->ether.tx_ring_current_write;
dev_dbg(&sc->pdev->dev, "sending mbuf (current_write = %d)\n",
current_write);
for (skb2 = skb; skb2 != NULL; skb2 = NULL) {
if (skb2->len) {
dev_dbg(&sc->pdev->dev,
"sending mbuf (len = %d, next = %p)\n",
skb2->len, NULL);
sc->ether.tx_free_cnt--;
sc->ether.tx_ring[current_write].tdes0 = 0;
sc->ether.tx_ring[current_write].tdes1 &=
SBE_2T3E3_TX_DESC_END_OF_RING |
SBE_2T3E3_TX_DESC_SECOND_ADDRESS_CHAINED;
/* DISABLE_PADDING sometimes gets lost somehow, hands off... */
sc->ether.tx_ring[current_write].tdes1 |=
SBE_2T3E3_TX_DESC_DISABLE_PADDING | skb2->len;
if (current_write == sc->ether.tx_ring_current_write) {
sc->ether.tx_ring[current_write].tdes1 |=
SBE_2T3E3_TX_DESC_FIRST_SEGMENT;
} else {
sc->ether.tx_ring[current_write].tdes0 =
SBE_2T3E3_TX_DESC_21143_OWN;
}
sc->ether.tx_ring[current_write].tdes2 = virt_to_phys(skb2->data);
sc->ether.tx_data[current_write] = NULL;
last_write = current_write;
current_write = (current_write + 1) % SBE_2T3E3_TX_DESC_RING_SIZE;
}
}
sc->ether.tx_data[last_write] = skb;
sc->ether.tx_ring[last_write].tdes1 |=
SBE_2T3E3_TX_DESC_LAST_SEGMENT |
SBE_2T3E3_TX_DESC_INTERRUPT_ON_COMPLETION;
sc->ether.tx_ring[sc->ether.tx_ring_current_write].tdes0 |=
SBE_2T3E3_TX_DESC_21143_OWN;
sc->ether.tx_ring_current_write = current_write;
dev_dbg(&sc->pdev->dev, "txput: tdes0 = %08X tdes1 = %08X\n",
sc->ether.tx_ring[last_write].tdes0,
sc->ether.tx_ring[last_write].tdes1);
dc_write(sc->addr, SBE_2T3E3_21143_REG_TRANSMIT_POLL_DEMAND,
0xffffffff);
spin_unlock_irqrestore(&sc->ether.tx_lock, flags);
return 0;
}
void t3e3_read_card_serial_number(struct channel *sc)
{
u32 i;
for (i = 0; i < 3; i++)
sc->ether.card_serial_number[i] = t3e3_eeprom_read_word(sc, 10 + i);
printk(KERN_INFO "SBE wanPMC-2T3E3 serial number: %04X%04X%04X\n",
sc->ether.card_serial_number[0], sc->ether.card_serial_number[1],
sc->ether.card_serial_number[2]);
}
/*
bit 0 led1 (green)
bit 1 led1 (yellow)
bit 2 led2 (green)
bit 3 led2 (yellow)
bit 4 led3 (green)
bit 5 led3 (yellow)
bit 6 led4 (green)
bit 7 led4 (yellow)
*/
void update_led(struct channel *sc, int blinker)
{
int leds;
if (sc->s.LOS)
leds = 0; /* led1 = off */
else if (sc->s.OOF)
leds = 2; /* led1 = yellow */
else if ((blinker & 1) && sc->rcv_count) {
leds = 0; /* led1 = off */
sc->rcv_count = 0;
} else
leds = 1; /* led1 = green */
cpld_write(sc, SBE_2T3E3_CPLD_REG_LEDR, leds);
sc->leds = leds;
}
/*
* SBE 2T3E3 synchronous serial card driver for Linux
*
* Copyright (C) 2009-2010 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation.
*
* This code is based on a driver written by SBE Inc.
*/
#include <linux/kernel.h>
#include "2t3e3.h"
const u32 cpld_reg_map[][2] =
{
{ 0x0000, 0x0080 }, /* 0 - Port Control Register A (PCRA) */
{ 0x0004, 0x0084 }, /* 1 - Port Control Register B (PCRB) */
{ 0x0008, 0x0088 }, /* 2 - LCV Count Register (PLCR) */
{ 0x000c, 0x008c }, /* 3 - LCV Threshold register (PLTR) */
{ 0x0010, 0x0090 }, /* 4 - Payload Fill Register (PPFR) */
{ 0x0200, 0x0200 }, /* 5 - Board ID / FPGA Programming Status Register */
{ 0x0204, 0x0204 }, /* 6 - FPGA Version Register */
{ 0x0800, 0x1000 }, /* 7 - Framer Registers Base Address */
{ 0x2000, 0x2000 }, /* 8 - Serial Chip Select Register */
{ 0x2004, 0x2004 }, /* 9 - Static Reset Register */
{ 0x2008, 0x2008 }, /* 10 - Pulse Reset Register */
{ 0x200c, 0x200c }, /* 11 - FPGA Reconfiguration Register */
{ 0x2010, 0x2014 }, /* 12 - LED Register (LEDR) */
{ 0x2018, 0x201c }, /* 13 - LIU Control and Status Register (PISCR) */
{ 0x2020, 0x2024 }, /* 14 - Interrupt Enable Register (PIER) */
{ 0x0068, 0x00e8 }, /* 15 - Port Control Register C (PCRC) */
{ 0x006c, 0x00ec }, /* 16 - Port Bandwidth Start (PBWF) */
{ 0x0070, 0x00f0 }, /* 17 - Port Bandwidth Stop (PBWL) */
};
const u32 cpld_val_map[][2] =
{
{ 0x01, 0x02 }, /* LIU1 / LIU2 select for Serial Chip Select */
{ 0x04, 0x08 }, /* DAC1 / DAC2 select for Serial Chip Select */
{ 0x00, 0x04 }, /* LOOP1 / LOOP2 - select of loop timing source */
{ 0x01, 0x02 } /* PORT1 / PORT2 - select LIU and Framer for reset */
};
const u32 t3e3_framer_reg_map[] = {
0x00, /* 0 - OPERATING_MODE */
0x01, /* 1 - IO_CONTROL */
0x04, /* 2 - BLOCK_INTERRUPT_ENABLE */
0x05, /* 3 - BLOCK_INTERRUPT_STATUS */
0x10, /* 4 - T3_RX_CONFIGURATION_STATUS, E3_RX_CONFIGURATION_STATUS_1 */
0x11, /* 5 - T3_RX_STATUS, E3_RX_CONFIGURATION_STATUS_2 */
0x12, /* 6 - T3_RX_INTERRUPT_ENABLE, E3_RX_INTERRUPT_ENABLE_1 */
0x13, /* 7 - T3_RX_INTERRUPT_STATUS, E3_RX_INTERRUPT_ENABLE_2 */
0x14, /* 8 - T3_RX_SYNC_DETECT_ENABLE, E3_RX_INTERRUPT_STATUS_1 */
0x15, /* 9 - E3_RX_INTERRUPT_STATUS_2 */
0x16, /* 10 - T3_RX_FEAC */
0x17, /* 11 - T3_RX_FEAC_INTERRUPT_ENABLE_STATUS */
0x18, /* 12 - T3_RX_LAPD_CONTROL, E3_RX_LAPD_CONTROL */
0x19, /* 13 - T3_RX_LAPD_STATUS, E3_RX_LAPD_STATUS */
0x1a, /* 14 - E3_RX_NR_BYTE, E3_RX_SERVICE_BITS */
0x1b, /* 15 - E3_RX_GC_BYTE */
0x30, /* 16 - T3_TX_CONFIGURATION, E3_TX_CONFIGURATION */
0x31, /* 17 - T3_TX_FEAC_CONFIGURATION_STATUS */
0x32, /* 18 - T3_TX_FEAC */
0x33, /* 19 - T3_TX_LAPD_CONFIGURATION, E3_TX_LAPD_CONFIGURATION */
0x34, /* 20 - T3_TX_LAPD_STATUS, E3_TX_LAPD_STATUS_INTERRUPT */
0x35, /* 21 - T3_TX_MBIT_MASK, E3_TX_GC_BYTE, E3_TX_SERVICE_BITS */
0x36, /* 22 - T3_TX_FBIT_MASK, E3_TX_MA_BYTE */
0x37, /* 23 - T3_TX_FBIT_MASK_2, E3_TX_NR_BYTE */
0x38, /* 24 - T3_TX_FBIT_MASK_3 */
0x48, /* 25 - E3_TX_FA1_ERROR_MASK, E3_TX_FAS_ERROR_MASK_UPPER */
0x49, /* 26 - E3_TX_FA2_ERROR_MASK, E3_TX_FAS_ERROR_MASK_LOWER */
0x4a, /* 27 - E3_TX_BIP8_MASK, E3_TX_BIP4_MASK */
0x50, /* 28 - PMON_LCV_EVENT_COUNT_MSB */
0x51, /* 29 - PMON_LCV_EVENT_COUNT_LSB */
0x52, /* 30 - PMON_FRAMING_BIT_ERROR_EVENT_COUNT_MSB */
0x53, /* 31 - PMON_FRAMING_BIT_ERROR_EVENT_COUNT_LSB */
0x54, /* 32 - PMON_PARITY_ERROR_EVENT_COUNT_MSB */
0x55, /* 33 - PMON_PARITY_ERROR_EVENT_COUNT_LSB */
0x56, /* 34 - PMON_FEBE_EVENT_COUNT_MSB */
0x57, /* 35 - PMON_FEBE_EVENT_COUNT_LSB */
0x58, /* 36 - PMON_CP_BIT_ERROR_EVENT_COUNT_MSB */
0x59, /* 37 - PMON_CP_BIT_ERROR_EVENT_COUNT_LSB */
0x6c, /* 38 - PMON_HOLDING_REGISTER */
0x6d, /* 39 - ONE_SECOND_ERROR_STATUS */
0x6e, /* 40 - LCV_ONE_SECOND_ACCUMULATOR_MSB */
0x6f, /* 41 - LCV_ONE_SECOND_ACCUMULATOR_LSB */
0x70, /* 42 - FRAME_PARITY_ERROR_ONE_SECOND_ACCUMULATOR_MSB */
0x71, /* 43 - FRAME_PARITY_ERROR_ONE_SECOND_ACCUMULATOR_LSB */
0x72, /* 44 - FRAME_CP_BIT_ERROR_ONE_SECOND_ACCUMULATOR_MSB */
0x73, /* 45 - FRAME_CP_BIT_ERROR_ONE_SECOND_ACCUMULATOR_LSB */
0x80, /* 46 - LINE_INTERFACE_DRIVE */
0x81 /* 47 - LINE_INTERFACE_SCAN */
};
const u32 t3e3_liu_reg_map[] =
{
0x00, /* REG0 */
0x01, /* REG1 */
0x02, /* REG2 */
0x03, /* REG3 */
0x04 /* REG4 */
};
/*
* SBE 2T3E3 synchronous serial card driver for Linux
*
* Copyright (C) 2009-2010 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation.
*
* This code is based on a driver written by SBE Inc.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/pci.h>
#include <linux/hdlc.h>
#include <linux/if_arp.h>
#include <linux/interrupt.h>
#include "2t3e3.h"
static void check_leds(unsigned long arg)
{
struct card *card = (struct card *)arg;
struct channel *channel0 = &card->channels[0];
static int blinker;
update_led(channel0, ++blinker);
if (has_two_ports(channel0->pdev))
update_led(&card->channels[1], blinker);
card->timer.expires = jiffies + HZ / 10;
add_timer(&card->timer);
}
static void t3e3_remove_channel(struct channel *channel)
{
struct pci_dev *pdev = channel->pdev;
struct net_device *dev = channel->dev;
/* system hangs if board asserts irq while module is unloaded */
cpld_stop_intr(channel);
free_irq(dev->irq, dev);
dc_drop_descriptor_list(channel);
unregister_hdlc_device(dev);
free_netdev(dev);
pci_release_regions(pdev);
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
}
static int __devinit t3e3_init_channel(struct channel *channel, struct pci_dev *pdev, struct card *card)
{
struct net_device *dev;
unsigned int val;
int err;
err = pci_enable_device(pdev);
if (err)
return err;
err = pci_request_regions(pdev, "SBE 2T3E3");
if (err)
goto disable;
dev = alloc_hdlcdev(channel);
if (!dev) {
printk(KERN_ERR "SBE 2T3E3" ": Out of memory\n");
goto free_regions;
}
t3e3_sc_init(channel);
dev_to_priv(dev) = channel;
channel->pdev = pdev;
channel->dev = dev;
channel->card = card;
channel->addr = pci_resource_start(pdev, 0);
if (pdev->subsystem_device == PCI_SUBDEVICE_ID_SBE_2T3E3_P1)
channel->h.slot = 1;
else
channel->h.slot = 0;
if (setup_device(dev, channel))
goto free_regions;
pci_read_config_dword(channel->pdev, 0x40, &val); /* mask sleep mode */
pci_write_config_dword(channel->pdev, 0x40, val & 0x3FFFFFFF);
pci_read_config_byte(channel->pdev, PCI_CACHE_LINE_SIZE, &channel->h.cache_size);
pci_read_config_dword(channel->pdev, PCI_COMMAND, &channel->h.command);
t3e3_init(channel);
if (request_irq(dev->irq, &t3e3_intr, IRQF_SHARED, dev->name, dev)) {
printk(KERN_WARNING "%s: could not get irq: %d\n", dev->name, dev->irq);
goto free_regions;
}
pci_set_drvdata(pdev, channel);
return 0;
free_regions:
pci_release_regions(pdev);
disable:
pci_disable_device(pdev);
return err;
}
static void __devexit t3e3_remove_card(struct pci_dev *pdev)
{
struct channel *channel0 = pci_get_drvdata(pdev);
struct card *card = channel0->card;
del_timer(&card->timer);
if (has_two_ports(channel0->pdev)) {
t3e3_remove_channel(&card->channels[1]);
pci_dev_put(card->channels[1].pdev);
}
t3e3_remove_channel(channel0);
kfree(card);
}
static int __devinit t3e3_init_card(struct pci_dev *pdev, const struct pci_device_id *ent)
{
/* pdev points to channel #0 */
struct pci_dev *pdev1 = NULL;
struct card *card;
int channels = 1, err;
if (has_two_ports(pdev)) {
while ((pdev1 = pci_get_subsys(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21142,
PCI_VENDOR_ID_SBE, PCI_SUBDEVICE_ID_SBE_2T3E3_P1,
pdev1)))
if (pdev1->bus == pdev->bus &&
pdev1->devfn == pdev->devfn + 8 /* next device on the same bus */)
break; /* found the second channel */
if (!pdev1) {
printk(KERN_ERR "SBE 2T3E3" ": Can't find the second channel\n");
return -EFAULT;
}
channels = 2;
/* holds the reference for pdev1 */
}
card = kzalloc(sizeof(struct card) + channels * sizeof(struct channel), GFP_KERNEL);
if (!card) {
printk(KERN_ERR "SBE 2T3E3" ": Out of memory\n");
return -ENOBUFS;
}
spin_lock_init(&card->bootrom_lock);
card->bootrom_addr = pci_resource_start(pdev, 0);
err = t3e3_init_channel(&card->channels[0], pdev, card);
if (err)
goto free_card;
if (channels == 2) {
err = t3e3_init_channel(&card->channels[1], pdev1, card);
if (err) {
t3e3_remove_channel(&card->channels[0]);
goto free_card;
}
}
/* start LED timer */
init_timer(&card->timer);
card->timer.function = check_leds;
card->timer.expires = jiffies + HZ / 10;
card->timer.data = (unsigned long)card;
add_timer(&card->timer);
return 0;
free_card:
kfree(card);
return err;
}
static struct pci_device_id t3e3_pci_tbl[] __devinitdata = {
{ PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21142,
PCI_VENDOR_ID_SBE, PCI_SUBDEVICE_ID_SBE_T3E3, 0, 0, 0 },
{ PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21142,
PCI_VENDOR_ID_SBE, PCI_SUBDEVICE_ID_SBE_2T3E3_P0, 0, 0, 0 },
/* channel 1 will be initialized after channel 0 */
{ 0, }
};
static struct pci_driver t3e3_pci_driver = {
.name = "SBE T3E3",
.id_table = t3e3_pci_tbl,
.probe = t3e3_init_card,
.remove = t3e3_remove_card,
};
static int __init t3e3_init_module(void)
{
return pci_register_driver(&t3e3_pci_driver);
}
static void __exit t3e3_cleanup_module(void)
{
pci_unregister_driver(&t3e3_pci_driver);
}
module_init(t3e3_init_module);
module_exit(t3e3_cleanup_module);
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(pci, t3e3_pci_tbl);
/*
* SBE 2T3E3 synchronous serial card driver for Linux
*
* Copyright (C) 2009-2010 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation.
*
* This code is based on a driver written by SBE Inc.
*/
#include <linux/capability.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/pci.h>
#include <linux/hdlc.h>
#include <linux/if_arp.h>
#include <linux/interrupt.h>
#include "2t3e3.h"
int t3e3_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct channel *sc = dev_to_priv(dev);
int cmd_2t3e3, len, rlen;
t3e3_param_t param;
t3e3_resp_t resp;
void *data = ifr->ifr_data + sizeof(cmd_2t3e3) + sizeof(len);
if (cmd == SIOCWANDEV)
return hdlc_ioctl(dev, ifr, cmd);
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
if (cmd != SIOCDEVPRIVATE + 15)
return -EINVAL;
if (copy_from_user(&cmd_2t3e3, ifr->ifr_data, sizeof(cmd_2t3e3)))
return -EFAULT;
if (copy_from_user(&len, ifr->ifr_data + sizeof(cmd_2t3e3), sizeof(len)))
return -EFAULT;
if (len > sizeof(param))
return -EFAULT;
if (len)
if (copy_from_user(&param, data, len))
return -EFAULT;
t3e3_if_config(sc, cmd_2t3e3, (char *)&param, &resp, &rlen);
if (rlen)
if (copy_to_user(data, &resp, rlen))
return -EFAULT;
return 0;
}
static struct net_device_stats* t3e3_get_stats(struct net_device *dev)
{
struct net_device_stats *nstats = &dev->stats;
struct channel *sc = dev_to_priv(dev);
t3e3_stats_t *stats = &sc->s;
memset(nstats, 0, sizeof(struct net_device_stats));
nstats->rx_packets = stats->in_packets;
nstats->tx_packets = stats->out_packets;
nstats->rx_bytes = stats->in_bytes;
nstats->tx_bytes = stats->out_bytes;
nstats->rx_errors = stats->in_errors;
nstats->tx_errors = stats->out_errors;
nstats->rx_crc_errors = stats->in_error_crc;
nstats->rx_dropped = stats->in_dropped;
nstats->tx_dropped = stats->out_dropped;
nstats->tx_carrier_errors = stats->out_error_lost_carr +
stats->out_error_no_carr;
return nstats;
}
int t3e3_open(struct net_device *dev)
{
struct channel *sc = dev_to_priv(dev);
int ret = hdlc_open(dev);
if (ret)
return ret;
sc->r.flags |= SBE_2T3E3_FLAG_NETWORK_UP;
dc_start(dev_to_priv(dev));
netif_start_queue(dev);
try_module_get(THIS_MODULE);
return 0;
}
int t3e3_close(struct net_device *dev)
{
struct channel *sc = dev_to_priv(dev);
hdlc_close(dev);
netif_stop_queue(dev);
dc_stop(sc);
sc->r.flags &= ~SBE_2T3E3_FLAG_NETWORK_UP;
module_put(THIS_MODULE);
return 0;
}
static int t3e3_attach(struct net_device *dev, unsigned short foo1,
unsigned short foo2)
{
return 0;
}
static const struct net_device_ops t3e3_ops = {
.ndo_open = t3e3_open,
.ndo_stop = t3e3_close,
.ndo_change_mtu = hdlc_change_mtu,
.ndo_start_xmit = hdlc_start_xmit,
.ndo_do_ioctl = t3e3_ioctl,
.ndo_get_stats = t3e3_get_stats,
};
int setup_device(struct net_device *dev, struct channel *sc)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
int retval;
dev->base_addr = pci_resource_start(sc->pdev, 0);
dev->irq = sc->pdev->irq;
dev->netdev_ops = &t3e3_ops;
dev->tx_queue_len = 100;
hdlc->xmit = t3e3_if_start_xmit;
hdlc->attach = t3e3_attach;
if ((retval = register_hdlc_device(dev))) {
dev_err(&sc->pdev->dev, "error registering HDLC device\n");
return retval;
}
return 0;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment