Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753393AbYJFJTS (ORCPT ); Mon, 6 Oct 2008 05:19:18 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1752319AbYJFJS5 (ORCPT ); Mon, 6 Oct 2008 05:18:57 -0400 Received: from wf-out-1314.google.com ([209.85.200.171]:28675 "EHLO wf-out-1314.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752641AbYJFJSs (ORCPT ); Mon, 6 Oct 2008 05:18:48 -0400 DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=message-id:date:from:to:subject:cc:in-reply-to:mime-version :content-type:content-transfer-encoding:content-disposition :references; b=M/vR6mHLlciD8OedIHnyAibRRU+KXoN294/bZTOPpTO5HlfB3wObhdT7cH2U0B4ZA4 rHcIo+rr5Hw9q45VWM9UbKBDuwzQ74TbxnLJYUXdCFYNFWDo0Bqan/VtJZdFlV3CnDu5 oQAERKDj9g5/KSPi0yftnoG+IF6BOXiNhBH5w= Message-ID: Date: Mon, 6 Oct 2008 12:18:46 +0300 From: "Matti Linnanvuori" To: "Krzysztof Halasa" Subject: [patch v0.7.68] WAN: add drivers etp and etp_stream Cc: "Jeff Garzik" , netdev@vger.kernel.org, linux-kernel@vger.kernel.org In-Reply-To: MIME-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Content-Disposition: inline References: Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 154538 Lines: 4899 From: Matti Linnanvuori Adding ETP G.703 drivers etp and etp_stream. Signed-off-by: Matti Linnanvuori --- This patch migrates the etp driver from syncppp to Generic HDLC. http://groups.google.com/group/pcidriver/web/etp.patch --- linux-2.6.27-rc2/MAINTAINERS 2008-08-08 13:21:10.470637659 +0300 +++ linux/MAINTAINERS 2008-08-08 13:25:00.661113955 +0300 @@ -1678,6 +1678,13 @@ P: Mika Kuoppala M: miku@iki.fi S: Maintained +ETP WAN DRIVERS +P: Matti Linnanvuori +M: matti.linnanvuori@ascom.com +L: netdev@vger.kernel.org +L: linux-kernel@vger.kernel.org +S: Supported + EXT2 FILE SYSTEM L: linux-ext4@vger.kernel.org S: Maintained --- linux-2.6.27-rc2/drivers/net/wan/Kconfig 2008-08-08 13:21:20.448131033 +0300 +++ linux/drivers/net/wan/Kconfig 2008-08-08 12:59:30.828005756 +0300 @@ -492,4 +492,23 @@ config SBNI_MULTILINE If unsure, say N. +config ETP + tristate "ETP support" + depends on PCI && HDLC + help + Driver for ETP PCI and PCI104 cards, which + support G.703 with Cisco HDLC or Ethernet encapsulation. + + To compile this driver as a module, choose M here: the + module will be called etp. + +config ETP_STREAM + tristate "ETP raw bitstream and sensitivity support" + depends on ETP + help + Driver for ETP raw bitstream and sensitivity. + + To compile this driver as a module, choose M here: the + module will be called etp_stream. + endif # WAN --- linux-2.6.27-rc2/drivers/net/wan/Makefile 2008-08-08 13:21:20.452131629 +0300 +++ linux/drivers/net/wan/Makefile 2008-08-08 12:59:30.828005756 +0300 @@ -41,6 +41,10 @@ obj-$(CONFIG_C101) += c101.o obj-$(CONFIG_WANXL) += wanxl.o obj-$(CONFIG_PCI200SYN) += pci200syn.o obj-$(CONFIG_PC300TOO) += pc300too.o +etp-y := etp_main.o etp_idt.o etp_proc.o +obj-$(CONFIG_ETP) += etp.o +etp-objs := $(etp-y) +obj-$(CONFIG_ETP_STREAM) += etp_stream/ clean-files := wanxlfw.inc $(obj)/wanxl.o: $(obj)/wanxlfw.inc --- linux-2.6.27-rc2/drivers/net/wan/etp_ioctl.h 1970-01-01 02:00:00.000000000 +0200 +++ linux/drivers/net/wan/etp_ioctl.h 2008-08-08 13:07:06.928732087 +0300 @@ -0,0 +1,139 @@ +/* etp_ioctl.h */ + +/* + Copyright (C) 2005 Jouni Kujala, Flexibilis Oy. + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + All the drivers derived from or based on this code must retain + the copyright notice. +*/ + +#ifndef _ETP_IOCTL_H_ +#define _ETP_IOCTL_H_ + +#define INTERFACES_PER_DEVICE 8 +#define E1_TIMESLOTS_PER_INTERFACE 32 + +#define ETP_IOCTL_MAGIC 0xF2 + +#define CLOCK_SOURCE_NCO 0x0 +#define CLOCK_SOURCE_DALLAS 0x1 +#define CLOCK_SOURCE_RJ 0x2 +#define CLOCK_SOURCE_LVDS 0x3 +#define CLOCK_SOURCE_RX(x) (((x) | 0x8) & 0xF) +#define CLOCK_SOURCE_RX0 CLOCK_SOURCE_RX(0) +#define CLOCK_SOURCE_RX1 CLOCK_SOURCE_RX(1) +#define CLOCK_SOURCE_RX2 CLOCK_SOURCE_RX(2) +#define CLOCK_SOURCE_RX3 CLOCK_SOURCE_RX(3) +#define CLOCK_SOURCE_RX4 CLOCK_SOURCE_RX(4) +#define CLOCK_SOURCE_RX5 CLOCK_SOURCE_RX(5) +#define CLOCK_SOURCE_RX6 CLOCK_SOURCE_RX(6) +#define CLOCK_SOURCE_RX7 CLOCK_SOURCE_RX(7) + +#define LOCAL_CLK_kHz 32768 /* local crystal on the board */ +#define CLOCK_COUNTER_PERIOD 512 +#define COUNTER_TO_kHz(x) ((x) * (LOCAL_CLK_kHz / CLOCK_COUNTER_PERIOD)) +#define NCO_ADDEND_DEFAULT_VALUE 0x10000000 /* 2 Mbps */ +#define PCM_RATE_kHz 8 + +struct etp_device_status_struct { + /* Value sets the frequency of numerically controllable oscillator. */ + uint32_t nco_addend_value; + unsigned int external_input_clock_rj_status; /* 0 idle, 1 active */ + unsigned int external_input_clock_rj_speed; /* in kHz */ + unsigned int external_input_clock_lvds_status; /* 0 idle, 1 active */ + unsigned int external_input_clock_lvds_speed; /* in kHz */ + uint32_t ext_output_clock_source; /* CLOCK_SOURCE_XXXX */ +}; + +struct etp_interface_status_struct { + unsigned int interface; + /* settable ones: */ +#define IF_MODE_CLOSED 0 +#define IF_MODE_HDLC 1 +#define IF_MODE_TIMESLOT 2 +#define IF_MODE_STREAM 3 + unsigned int mode; /* IF_MODE_XXXX */ + uint32_t tx_on; /* 0 no, DMA_ENABLE_MASK yes */ + uint32_t rx_on; /* 0 no, DMA_ENABLE_MASK yes */ + uint32_t tx_clock_source; /* CLOCK_SOURCE_XXXX */ +#define HDLC_MODE_CISCO_OVER_G703 0 +#define HDLC_MODE_CISCO_OVER_G704 1 +#define HDLC_MODE_RETINA_OVER_G703 10 +#define HDLC_MODE_RETINA_OVER_G704 11 +#define HDLC_MODE_RETINA_OVER_G703_POINTOPOINT 12 +#define HDLC_MODE_RETINA_OVER_G704_POINTOPOINT 13 + unsigned int hdlc_mode; /* HDLC_MODE_XXXX */ + uint32_t hdlc_mode_g704_used_timeslots; /* timeslots for HDLC frame */ + unsigned int led; /* LED status */ + unsigned int loss_of_signal; /* 1 = loss of signal */ +}; + +/* ioctl call specific structures: */ +struct etp_ioctl_open { + unsigned int interface; /* 0 .. INTERFACES_PER_DEVICE-1 */ + unsigned int if_mode; /* IF_MODE_TIMESLOT or IF_MODE_STREAM */ + unsigned int rx_slots; + unsigned int tx_slots; +}; + +struct etp_ioctl_interface_settings { + unsigned int interface; /* 0 .. INTERFACES_PER_DEVICE-1 */ + uint32_t tx_clock_source; /* CLOCK_SOURCE_XXX */ + unsigned int hdlc_mode; /* HDLC_MODE_XXX */ + uint32_t hdlc_mode_g704_used_timeslots; /* timeslots for HDLC frame */ +}; + +struct etp_ioctl_ext_output_clock { + uint32_t clock_source; /* CLOCK_SOURCE_X */ +}; + +struct etp_ioctl_nco_adjust { + uint32_t nco_addend_value; +}; + +struct etp_ioctl_e1_access { + unsigned int write; /* 0 = read, 1 = write */ + unsigned int address; /* address on E1 chip */ + unsigned int data; /* data read or written */ +}; + +struct etp_ioctl_buffer_poll { + unsigned int interface; /* 0 .. INTERFACES_PER_DEVICE-1 */ + unsigned int rx_slot; /* latest rx slot received */ + unsigned int tx_slot; /* latest tx slot transmitted */ +}; + +/* ioctl calls: */ +#define ETP_IOCTL_INTERFACE_OPEN _IOW(ETP_IOCTL_MAGIC, 1, struct etp_ioctl_open) +#define ETP_IOCTL_INTERFACE_CLOSE _IO(ETP_IOCTL_MAGIC, 2) +#define ETP_IOCTL_TX_ON _IO(ETP_IOCTL_MAGIC, 10) +#define ETP_IOCTL_TX_OFF _IO(ETP_IOCTL_MAGIC, 11) +#define ETP_IOCTL_RX_ON _IO(ETP_IOCTL_MAGIC, 12) +#define ETP_IOCTL_RX_OFF _IO(ETP_IOCTL_MAGIC, 13) + +#define ETP_IOCTL_INTERFACE_SETTINGS \ + _IOW(ETP_IOCTL_MAGIC, 20, struct etp_ioctl_interface_settings) +#define ETP_IOCTL_EXT_OUTPUT_CLOCK \ + _IOW(ETP_IOCTL_MAGIC, 21, struct etp_ioctl_ext_output_clock) +#define ETP_IOCTL_NCO _IOW(ETP_IOCTL_MAGIC, 22, struct etp_ioctl_nco_adjust) + +#define ETP_IOCTL_DEVICE_STATUS_GET \ + _IOWR(ETP_IOCTL_MAGIC, 30, struct etp_device_status_struct) +#define ETP_IOCTL_INTERFACE_STATUS_GET \ + _IOWR(ETP_IOCTL_MAGIC, 31, struct etp_interface_status_struct) +#define ETP_IOCTL_E1_ACCESS \ + _IOWR(ETP_IOCTL_MAGIC, 32, struct etp_ioctl_e1_access) +#define ETP_IOCTL_RXTX_NOSLEEP_POLL \ + _IOWR(ETP_IOCTL_MAGIC, 35, struct etp_ioctl_buffer_poll) + +#endif --- linux-2.6.27-rc2/drivers/net/wan/etp_idt.h 1970-01-01 02:00:00.000000000 +0200 +++ linux/drivers/net/wan/etp_idt.h 2008-08-08 13:07:06.916730302 +0300 @@ -0,0 +1,115 @@ +/* etp_idt.h */ + +/* + Copyright (C) 2005 Jouni Kujala, Flexibilis Oy. + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + All the drivers derived from or based on this code must retain + the copyright notice. +*/ + +#ifndef _ETP_IDT_H_ +#define _ETP_IDT_H_ +#include "etp.h" +#include "idt82p2288.h" + +#define ALL_IDT_INTERFACES 0xFF +#define IDT_INTERFACES 8 + +static inline unsigned if_to_idt_if_etp(unsigned interface) +{ + static const unsigned char to_idt_if[] = { 6, 4, 0, 2, 7, 5, 1, 3 }; + return to_idt_if[interface]; +} + +static inline unsigned if_to_idt_if_etp104(unsigned interface) +{ + return 7 - interface; +} + +static inline unsigned idt_if_to_if_etp(unsigned interface) +{ + static const unsigned char to_if[] = { 2, 6, 3, 7, 1, 5, 0, 4 }; + return to_if[interface]; +} + +static inline unsigned idt_if_to_if_etp104(unsigned interface) +{ + return 7 - interface; +} + +extern unsigned int etp_if_to_idt_if(unsigned interface, + unsigned short pci_device_id); + +/** + * Returns the IDT register offset of a span whose number is given as the second + * argument. + **/ +static inline unsigned idt_offset_down(const struct etp_device_private *device, + unsigned span) +{ + return etp_if_to_idt_if(span, device->pci_dev->device) << 8; +} + +extern void idt_init_default(struct etp_device_private *dp); +extern int idt_open_if_hdlc_g703(struct etp_device_private *dp, + unsigned); +extern int idt_open_if_hdlc_g704(struct etp_device_private *dp, + unsigned); +extern int idt_open_if_timeslot(struct etp_device_private *dp, + unsigned); +extern int idt_open_if_stream(struct etp_device_private *dp, + unsigned); +extern int idt_close_if(struct etp_device_private *dp, unsigned); +extern int etp_idt_reset(unsigned device); + +extern int etp_read_idt_register_lock(unsigned device, unsigned reg); +extern int etp_read_idt_register_if_lock(unsigned device, unsigned reg, + unsigned interface); + +static inline unsigned read_idt_register(uint32_t __iomem *ioaddr, unsigned reg) +{ + unsigned value; + while (readl_relaxed(ioaddr) & E1_ACCESS_ON) + cpu_relax(); + writel(((reg << E1_REGISTER_SHIFT) & E1_REGISTER_MASK) + | E1_DIR_READ | E1_ACCESS_ON, + ioaddr); + while ((value = readl_relaxed(ioaddr)) & E1_ACCESS_ON) + cpu_relax(); + return value & E1_DATA_MASK; +} + +static inline void write_idt_register(uint32_t __iomem *ioaddr, unsigned value) +{ + while (readl_relaxed(ioaddr) & E1_ACCESS_ON) + cpu_relax(); + writel(value, ioaddr); +} + +static inline unsigned etp_value(unsigned reg, unsigned value) +{ + return ((reg << E1_REGISTER_SHIFT) & E1_REGISTER_MASK) | + E1_DIR_WRITE | E1_ACCESS_ON | value; +} + +extern int etp_write_idt_register_lock(unsigned device, unsigned reg, + unsigned value); +extern int etp_write_idt_register_if_lock(unsigned device, unsigned reg, + unsigned interface, + unsigned value); + +extern int idt_set_ref_clk(struct etp_device_private *dp, + unsigned interface); +extern int idt_get_ref_clk(struct etp_device_private *dp); + +#endif --- linux-2.6.27-rc2/drivers/net/wan/etp_idt.c 1970-01-01 02:00:00.000000000 +0200 +++ linux/drivers/net/wan/etp_idt.c 2008-08-08 13:07:06.900727921 +0300 @@ -0,0 +1,346 @@ +/* etp_idt.c */ + +/* + Copyright (C) 2006 Jouni Kujala, Flexibilis Oy. + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + All the drivers derived from or based on this code must retain + the copyright notice. +*/ + +#include "etp.h" +#include "etp_ioctl.h" +#include "etp_idt.h" + +int etp_read_idt_register_if_lock(unsigned device, unsigned reg, + unsigned interface) +{ + const int offset = etp_idt_offset(device, interface); + if (unlikely(offset < 0)) + return -ENXIO; + return etp_read_idt_register_lock(device, reg | offset); +} +EXPORT_SYMBOL(etp_read_idt_register_if_lock); + +static int +write_idt_register_if(unsigned device, unsigned reg, + unsigned interface, unsigned value); + +static inline unsigned int idt_if_to_if(unsigned interface, + unsigned short pci_device_id) +{ + switch (pci_device_id) { + case PCI_DEVICE_ID_ETP_ORIGINAL: + return idt_if_to_if_etp(interface); + default: + return idt_if_to_if_etp104(interface); + } +} + +unsigned int etp_if_to_idt_if(unsigned interface, unsigned short pci_device_id) +{ + switch (pci_device_id) { + case PCI_DEVICE_ID_ETP_ORIGINAL: + return if_to_idt_if_etp(interface); + default: + return if_to_idt_if_etp104(interface); + } +} +EXPORT_SYMBOL(etp_if_to_idt_if); + +int etp_idt_reset(unsigned device) +{ + struct etp_device_private *etp = get_dev_priv(device); + int error; + etp_down(etp); + if (likely(!atomic_read(&etp->reset))) { + mutex_lock(&etp->mutex); + /* Give SW Reset: */ + write_idt_register((uint32_t __iomem *) + (etp->ioaddr + REG_E1_CTRL), + etp_value(E1_TRNCVR_SW_RESET_REG, 0x0)); + /* Wait for PCI write to finish. */ + readl_relaxed(etp->ioaddr + E1_ACCESS_ON); + /* wait for E1 chip to be ready: */ + msleep(2); /* should be at least 2 ms */ + mutex_unlock(&etp->mutex); + error = 0; + } else { + error = -ENXIO; + } + etp_up(etp); + return error; +} +EXPORT_SYMBOL(etp_idt_reset); + +void idt_init_default(struct etp_device_private *dp) +{ + const unsigned device = device_number(dp); + /* Enable Tx Jitter Attenuation: */ + write_idt_register_if(device, + E1_TRNCVR_TX_JITTER_ATTEN_CONF_REG, + ALL_IDT_INTERFACES, 0x08); + /* Enable Rx Jitter Attenuation */ + write_idt_register_if(device, + E1_TRNCVR_RX_JITTER_ATTEN_CONF_REG, + ALL_IDT_INTERFACES, 0x8); + /* Select Auto report mode */ + write_idt_register_if(device, + E1_TRNCVR_MAINT_FUNC_CTRL2_REG, + ALL_IDT_INTERFACES, 0x2); + /* Set internal impedance */ + write_idt_register_if(device, + E1_TRNCVR_TX_RX_TERM_CONF_REG, + ALL_IDT_INTERFACES, 0x9); + /* Set the transmit Clock Slave mode */ + write_idt_register_if(device, + E1_TRNCVR_TBIF_OPERATING_MODE_REG, + ALL_IDT_INTERFACES, 0x1); + /* Set Backplane config: Each link uses its own timing: */ + write_idt_register_if(device, + E1_TRNCVR_BP_GLOBAL_CONF_REG, + ALL_IDT_INTERFACES, 0x14); + write_idt_register_if(device, + E1_TRNCVR_TBIF_OPTION_REG, + ALL_IDT_INTERFACES, 0x18); + /* Disable the RSD/RSIG tri-state buffer */ + write_idt_register_if(device, + E1_TRNCVR_RBIF_OPTION_REG, + ALL_IDT_INTERFACES, 0x0C); + /* Set the receive Clock Master mode */ + write_idt_register_if(device, + E1_TRNCVR_RBIF_MODE_REG, ALL_IDT_INTERFACES, 0x0); + /* Autoyellow on: */ + write_idt_register_if(device, + E1_TRNCVR_FGEN_MAINT0_REG, + ALL_IDT_INTERFACES, 0x2); + /* Clock select from the recovered clock in line side */ + write_idt_register_if(device, + E1_TRNCVR_TX_TIMING_OPTION_REG, + ALL_IDT_INTERFACES, 0x0); + /* G.775 Alarm detection criteria selected */ + write_idt_register_if(device, + E1_TRNCVR_ALARM_CRITERIA_CTRL_REG, + ALL_IDT_INTERFACES, 0x2); + /* Shall trigger an interrupt to notify about loss of signal. */ + write_idt_register_if(device, E1_TRNCVR_INT_ENA_CTRL0_REG, + ALL_IDT_INTERFACES, 1); +/* Shall trigger an interrupt to notify about changes in loss of signal. */ + write_idt_register_if(device, E1_TRNCVR_INT_TRIG_EDGE_SEL_REG, + ALL_IDT_INTERFACES, 1); +} + +int idt_open_if_hdlc_g703(struct etp_device_private *dp, + unsigned interface) +{ + const unsigned device = device_number(dp); + /* IDT receive in unframed mode: */ + int error = write_idt_register_if(device, + E1_TRNCVR_FRMR_MODE0_REG, + interface, + 0x0E); + if (error) + return error; + /* idt transmit unframed: (FDIS = 1) */ + error = write_idt_register_if(device, + E1_TRNCVR_E1_MODE_REG, interface, 0x01); + if (error) + return error; + /* Disable Tx High-Z, Set cable impedance: */ + return write_idt_register_if(device, + E1_TRNCVR_TX_CONF1_REG, interface, 0x01); +} + +int idt_open_if_hdlc_g704(struct etp_device_private *dp, + unsigned interface) +{ + const unsigned device = device_number(dp); + /* idt in receive framed mode: */ + int error = write_idt_register_if(device, + E1_TRNCVR_FRMR_MODE0_REG, + interface, + 0x06); + if (error) + return error; + /* IDT transmit framed: (FDIS = 0), no CAS, but CRC. Works with Cisco */ + error = write_idt_register_if(device, + E1_TRNCVR_E1_MODE_REG, interface, 0x02); + if (error) + return error; + /* Disable Tx High-Z, Set cable impedance: */ + return write_idt_register_if(device, + E1_TRNCVR_TX_CONF1_REG, interface, 0x01); +} + +int idt_open_if_timeslot(struct etp_device_private *dp, unsigned interface) +{ + const unsigned device = device_number(dp); + /* IDT in receive framed mode: */ + int error = write_idt_register_if(device, + E1_TRNCVR_FRMR_MODE0_REG, + interface, + 0x06); + if (error) + return error; + /* IDT transmit framed: (FDIS = 0) */ + error = write_idt_register_if(device, + E1_TRNCVR_E1_MODE_REG, interface, 0x06); + if (error) + return error; + /* Disable Tx High-Z, Set cable impedance: */ + return write_idt_register_if(device, + E1_TRNCVR_TX_CONF1_REG, interface, 0x01); +} + +int idt_open_if_stream(struct etp_device_private *dp, unsigned interface) +{ + const unsigned device = device_number(dp); + /* idt receive in unframed mode: */ + int error = write_idt_register_if(device, + E1_TRNCVR_FRMR_MODE0_REG, + interface, + 0x0E); + if (error) + return error; + /* idt transmit unframed: (FDIS = 1) */ + error = write_idt_register_if(device, + E1_TRNCVR_E1_MODE_REG, interface, 0x01); + if (error) + return error; + /* Disable Tx High-Z, Set cable impedance: */ + return write_idt_register_if(device, + E1_TRNCVR_TX_CONF1_REG, interface, 0x01); +} + +int idt_close_if(struct etp_device_private *dp, unsigned interface) +{ + /* Tx to High-Z: */ + return write_idt_register_if(device_number(dp), + E1_TRNCVR_TX_CONF1_REG, interface, 0x10); +} + +int etp_read_idt_register_lock(unsigned device, unsigned reg) +{ + unsigned value; + struct etp_device_private *etp = get_dev_priv(device); + uint32_t __iomem *ioaddr = (uint32_t __iomem *) + (etp->ioaddr + REG_E1_CTRL); + mutex_lock(&etp->mutex); + if (unlikely(atomic_read(&etp->reset))) { + mutex_unlock(&etp->mutex); + return -ENXIO; + } + value = read_idt_register(ioaddr, reg); + mutex_unlock(&etp->mutex); + return value; +} +EXPORT_SYMBOL(etp_read_idt_register_lock); + + +/** + * Returns the IDT register offset of a span whose numbers are given as the + * arguments or -ENXIO on no card present. + **/ +int etp_idt_offset(unsigned card_number, unsigned span) +{ + struct etp_device_private *device = get_dev_priv(card_number); + struct mutex *mutex = &device->mutex; + int offset; + mutex_lock(mutex); + if (unlikely(atomic_read(&device->reset))) + offset = -ENXIO; + else + offset = idt_offset_down(device, span); + mutex_unlock(mutex); + return offset; +} +EXPORT_SYMBOL(etp_idt_offset); + +static int +write_idt_register_if(unsigned device, unsigned reg, + unsigned interface, unsigned value) +{ + if (interface == ALL_IDT_INTERFACES) { + int error; + unsigned int i = IDT_INTERFACES - 1u; + do { + error = etp_write_idt_register_lock(device, + reg | (i << 8), + value); + if (unlikely(error)) + return error; + } while (i--); + return error; + } else { + unsigned offset = idt_offset_down(get_dev_priv(device), + interface); + return etp_write_idt_register_lock(device, reg | offset, value); + } +} + +int etp_write_idt_register_if_lock(unsigned device, unsigned reg, + unsigned interface, unsigned value) +{ + if (interface == ALL_IDT_INTERFACES) { + int error; + unsigned int i = IDT_INTERFACES - 1u; + do { + error = etp_write_idt_register_lock(device, + reg | (i << 8), + value); + if (unlikely(error)) + return error; + } while (i--); + return error; + } else { + int offset = etp_idt_offset(device, interface); + if (unlikely(offset == -ENXIO)) + return offset; + return etp_write_idt_register_lock(device, reg | offset, value); + } +} +EXPORT_SYMBOL(etp_write_idt_register_if_lock); + +int etp_write_idt_register_lock(unsigned device, unsigned reg, unsigned value) +{ + struct etp_device_private *etp = get_dev_priv(device); + mutex_lock(&etp->mutex); + if (unlikely(atomic_read(&etp->reset))) { + mutex_unlock(&etp->mutex); + return -ENXIO; + } + write_idt_register((uint32_t __iomem *)(etp->ioaddr + REG_E1_CTRL), + etp_value(reg, value)); + mutex_unlock(&etp->mutex); + return 0; +} +EXPORT_SYMBOL(etp_write_idt_register_lock); + +/* Set ref clk to be from certain interface */ +int idt_set_ref_clk(struct etp_device_private *dp, unsigned interface) +{ + unsigned short pci_device_id = dp->pci_dev->device; + return etp_write_idt_register_lock(device_number(dp), + E1_TRNCVR_REF_CLK_REG, + etp_if_to_idt_if(interface, + pci_device_id)); +} + +/* Get the interface where from the ref clock is. */ +int idt_get_ref_clk(struct etp_device_private *dp) +{ + unsigned short pci_device_id = dp->pci_dev->device; + return idt_if_to_if(0xf & + etp_read_idt_register_lock(device_number(dp), + E1_TRNCVR_REF_CLK_REG), + pci_device_id); +} --- linux-2.6.27-rc2/drivers/net/wan/etp_proc.c 1970-01-01 02:00:00.000000000 +0200 +++ linux/drivers/net/wan/etp_proc.c 2008-08-08 13:07:06.968738040 +0300 @@ -0,0 +1,99 @@ +/* etp_proc.c */ + +/* + Copyright (C) 2005 Jouni Kujala, Flexibilis Oy. + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + All the drivers derived from or based on this code must retain + the copyright notice. +*/ + +#include "etp.h" +#include "etp_ioctl.h" +#include "etp_idt.h" + +int etp_device_status_get(unsigned device, + struct etp_device_status_struct *device_status) +{ + struct etp_device_private *dp; + int error; + if (unlikely(device >= etp_number_devices())) + return -ENXIO; + dp = get_dev_priv(device); + down_read(&dp->interface_privates[0].semaphore); + if (likely(!atomic_read(&dp->reset))) { + uint8_t __iomem *ioaddr = dp->ioaddr; + device_status->nco_addend_value = + (readl_relaxed(ioaddr + REG_NCO_CTRL)); + device_status->external_input_clock_rj_status = + (readl_relaxed(ioaddr + REG_CLK_STAT) & + EXT_CLOCK_RJ_STATUS_MASK) ? 1 : 0; + device_status->external_input_clock_rj_speed = + COUNTER_TO_kHz((readl_relaxed(ioaddr + REG_CLK_STAT) & + EXT_CLOCK_RJ_STATUS_MASK) >> + EXT_CLOCK_RJ_STATUS_SHIFT); + device_status->external_input_clock_lvds_status = + (readl_relaxed(ioaddr + REG_CLK_STAT) & + EXT_CLOCK_LVDS_STATUS_MASK) ? 1 : 0; + device_status->external_input_clock_lvds_speed = + COUNTER_TO_kHz((readl_relaxed(ioaddr + REG_CLK_STAT) & + EXT_CLOCK_LVDS_STATUS_MASK) >> + EXT_CLOCK_LVDS_STATUS_SHIFT); + device_status->ext_output_clock_source = + (readl_relaxed(ioaddr + REG_GENERAL) & + OUTPUT_CLK_SELECT_MASK) >> OUTPUT_CLK_SELECT_SHIFT; + if (device_status->ext_output_clock_source == CLOCK_SELECT_E1_A) + device_status->ext_output_clock_source = + CLOCK_SELECT_RX(idt_get_ref_clk(dp)); + error = 0; + } else { + error = -ENXIO; + } + up_read(&dp->interface_privates[0].semaphore); + return error; +} + +int etp_interface_status_get(unsigned device, unsigned interface, + struct etp_interface_status_struct *status_struct) +{ + struct etp_device_private *dp; + int error; + struct etp_interface_private *ip; + if (unlikely(device >= etp_number_devices() + || interface > INTERFACES_PER_DEVICE - 1)) + return -ENXIO; + dp = get_dev_priv(device); + ip = &dp->interface_privates[interface]; + down_read(&ip->semaphore); + if (likely(!atomic_read(&dp->reset))) { + struct etp_channel_private *cp = + &dp->interface_privates[interface].ch_priv; + + status_struct->interface = interface; + status_struct->mode = ip->if_mode; + status_struct->tx_on = etp_tx_on_get(cp); + status_struct->rx_on = etp_rx_on_get(cp); + status_struct->tx_clock_source = ip->tx_clock_source; + status_struct->hdlc_mode = cp->hdlc_mode; + status_struct->hdlc_mode_g704_used_timeslots = + cp->hdlc_mode_g704_used_timeslots; + status_struct->led = get_led(ip); + status_struct->loss_of_signal = ip->los + || ip->if_mode == IF_MODE_CLOSED; + error = 0; + } else { + error = -ENXIO; + } + up_read(&ip->semaphore); + return error; +} +EXPORT_SYMBOL(etp_interface_status_get); --- linux-2.6.27-rc2/drivers/net/wan/idt82p2288.h 1970-01-01 02:00:00.000000000 +0200 +++ linux/drivers/net/wan/idt82p2288.h 2008-08-08 13:07:06.988741017 +0300 @@ -0,0 +1,270 @@ +/* Author: Flexibilis Oy / Petri Anttila */ + +/* + + ATMUX (Analog Telephone Multiplexer) + + Copyright (C) 2005 Petri Anttila, Flexibilis Oy. + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +*/ + +#ifndef _IDT82P2281_H_ +#define _IDT82P2281_H_ + + +/* Registers*/ +#define E1_TRNCVR_ID_REG 0x0 +#define E1_TRNCVR_SW_RESET_REG 0x04 +#define E1_TRNCVR_G772_MON_CTRL_REG 0x05 +#define E1_TRNCVR_GPIO_REG 0x06 +#define E1_TRNCVR_REF_CLK_REG 0x07 +#define E1_TRNCVR_INT_REQ_LINK_ID_REG 0x09 +#define E1_TRNCVR_TIMER_INT_CTRL_REG 0x0A +#define E1_TRNCVR_TIMER_INT_IND_REG 0x0B +#define E1_TRNCVR_PMON_ACCESS_PORT_REG 0x0E +#define E1_TRNCVR_PMON_ACCESS_DATA_REG 0x0F +#define E1_TRNCVR_BP_GLOBAL_CONF_REG 0x10 + +#define E1_TRNCVR_T1_E1_MODE_REG 0x20 +#define E1_TRNCVR_TX_JITTER_ATTEN_CONF_REG 0x21 +#define E1_TRNCVR_TX_CONF0_REG 0x22 +#define E1_TRNCVR_TX_CONF1_REG 0x23 +#define E1_TRNCVR_TX_CONF2_REG 0x24 +#define E1_TRNCVR_TX_CONF3_REG 0x25 +#define E1_TRNCVR_TX_CONF4_REG 0x26 +#define E1_TRNCVR_RX_JITTER_ATTEN_CONF_REG 0x27 +#define E1_TRNCVR_RX_CONF0_REG 0x28 +#define E1_TRNCVR_RX_CONF1_REG 0x29 +#define E1_TRNCVR_RX_CONF2_REG 0x2A +#define E1_TRNCVR_MAINT_FUNC_CTRL0_REG 0x2B +#define E1_TRNCVR_MAINT_FUNC_CTRL1_REG 0x2C +#define E1_TRNCVR_MAINT_FUNC_CTRL2_REG 0x31 +#define E1_TRNCVR_TX_RX_TERM_CONF_REG 0x32 +#define E1_TRNCVR_INT_ENA_CTRL0_REG 0x33 +#define E1_TRNCVR_INT_ENA_CTRL1_REG 0x34 +#define E1_TRNCVR_INT_TRIG_EDGE_SEL_REG 0x35 +#define E1_TRNCVR_LINE_STATUS0_REG 0x36 +#define E1_TRNCVR_LINE_STATUS1_REG 0x37 +#define E1_TRNCVR_TX_JITTER_MEAS_VAL_IND_REG 0x38 +#define E1_TRNCVR_RX_JITTER_MEAS_VAL_IND_REG 0x39 +#define E1_TRNCVR_INT_STATUS0_REG 0x3A +#define E1_TRNCVR_INT_STATUS1_REG 0x3B +#define E1_TRNCVR_EXZ_ERROR_CNT_H_BYTE_REG 0x3C +#define E1_TRNCVR_EXZ_ERROR_CNT_L_BYTE_REG 0x3D +#define E1_TRNCVR_REF_CLK_CTRL_REG 0x3E +#define E1_TRNCVR_INT_MOD_IND2_REG 0x3F +#define E1_TRNCVR_INT_MOD_IND0_REG 0x40 +#define E1_TRNCVR_INT_MOD_IND1_REG 0x41 +#define E1_TRNCVR_TBIF_OPTION_REG 0x42 +#define E1_TRNCVR_TBIF_OPERATING_MODE_REG 0x43 +#define E1_TRNCVR_TBIF_TS_OFFSET_REG 0x44 +#define E1_TRNCVR_TBIF_BIT_OFFSET_REG 0x45 +#define E1_TRNCVR_RBIF_OPTION_REG 0x46 +#define E1_TRNCVR_RBIF_MODE_REG 0x47 +#define E1_TRNCVR_RBIF_FRAME_PULSE_REG 0x48 +#define E1_TRNCVR_RBIF_TS_OFFSET_REG 0x49 +#define E1_TRNCVR_RBIF_BIT_OFFSET_REG 0x4A +#define E1_TRNCVR_RTSFS_CHANGE_IND_REG 0x4B +#define E1_TRNCVR_RTSFS_INT_CTRL_REG 0x4C +#define E1_TRNCVR_FRMR_MODE0_REG 0x4D +#define E1_TRNCVR_FRMR_MODE1_REG 0x4E +#define E1_TRNCVR_FRMR_STATUS_REG 0x4F +#define E1_TRNCVR_FRMR_INT_CTRL0_REG 0x50 +#define E1_TRNCVR_FRMR_INT_CTRL1_REG 0x51 +#define E1_TRNCVR_FRMR_INT_IND0_REG 0x52 +#define E1_TRNCVR_FRMR_INT_IND1_REG 0x53 +#define E1_TRNCVR_TS0_INTERNAT_NAT_REG 0x54 +#define E1_TRNCVR_TS16_SPARE_REG 0x55 +#define E1_TRNCVR_SA4_CODEWORD_REG 0x56 +#define E1_TRNCVR_SA5_CODEWORD_REG 0x57 +#define E1_TRNCVR_SA6_CODEWORD_REG 0x58 +#define E1_TRNCVR_SA7_CODEWORD_REG 0x59 +#define E1_TRNCVR_SA8_CODEWORD_REG 0x5A +#define E1_TRNCVR_SA6_CODEWORD_IND_REG 0x5B +#define E1_TRNCVR_SA_CODEWORD_INT_CTRL_REG 0x5C +#define E1_TRNCVR_SA_CODEWORD_INT_IND_REG 0x5D +#define E1_TRNCVR_OVERH_ERROR_STATUS_REG 0x5F +#define E1_TRNCVR_OVERH_INT_CTRL_REG 0x60 +#define E1_TRNCVR_OVERH_INT_IND_REG 0x61 +#define E1_TRNCVR_E1_MODE_REG 0x62 +#define E1_TRNCVR_FGEN_INTERN_BIT_REG 0x63 +#define E1_TRNCVR_FGEN_SA_CTRL_REG 0x64 +#define E1_TRNCVR_SA4_CODE_WORD_REG 0x65 +#define E1_TRNCVR_SA5_CODE_WORD_REG 0x66 +#define E1_TRNCVR_SA6_CODE_WORD_REG 0x67 +#define E1_TRNCVR_SA7_CODE_WORD_REG 0x68 +#define E1_TRNCVR_SA8_CODE_WORD_REG 0x69 +#define E1_TRNCVR_FGEN_EXTRA_REG 0x6A +#define E1_TRNCVR_FGEN_MAINT0_REG 0x6B +#define E1_TRNCVR_FGEN_MAINT1_REG 0x6C +#define E1_TRNCVR_FGEN_INT_CTRL_REG 0x6D +#define E1_TRNCVR_FGEN_INT_IND_REG 0x6E +#define E1_TRNCVR_ERROR_INSERTION_REG 0x6F +#define E1_TRNCVR_TX_TIMING_OPTION_REG 0x70 +#define E1_TRNCVR_PRGD_CTRL_REG 0x71 +#define E1_TRNCVR_PRGD_STATUS_CTRL_REG 0x72 +#define E1_TRNCVR_PRGD_INT_IND_REG 0x73 +#define E1_TRNCVR_ELST_CONF_REG 0x7C +#define E1_TRNCVR_ELST_INT_IND_REG 0x7D +#define E1_TRNCVR_ELST_TRUNK_CODE_REG 0x7E + +#define E1_TRNCVR_THDLC_ENA_CTRL_REG 0x84 +#define E1_TRNCVR_THDLC1_ASSIGNMENT_REG 0x85 +#define E1_TRNCVR_THDLC2_ASSIGNMENT_REG 0x86 +#define E1_TRNCVR_THDLC3_ASSIGNMENT_REG 0x87 +#define E1_TRNCVR_THDLC1_BIT_SEL_REG 0x88 +#define E1_TRNCVR_THDLC2_BIT_SEL_REG 0x89 +#define E1_TRNCVR_THDLC3_BIT_SEL_REG 0x8A +#define E1_TRNCVR_RHDLC_ENA_CTRL_REG 0x8B +#define E1_TRNCVR_RHDLC1_ASSIGNMENT_REG 0x8C +#define E1_TRNCVR_RHDLC2_ASSIGNMENT_REG 0x8D +#define E1_TRNCVR_RHDLC3_ASSIGNMENT_REG 0x8E +#define E1_TRNCVR_RHDLC1_BIT_SEL_REG 0x8F +#define E1_TRNCVR_RHDLC2_BIT_SEL_REG 0x90 +#define E1_TRNCVR_RHDLC3_BIT_SEL_REG 0x91 +#define E1_TRNCVR_RHDLC1_CTRL_REG 0x92 +#define E1_TRNCVR_RHDLC2_CTRL_REG 0x93 +#define E1_TRNCVR_RHDLC3_CTRL_REG 0x94 +#define E1_TRNCVR_RHDLC1_RFIFO_ACC_STAT_REG 0x95 +#define E1_TRNCVR_RHDLC2_RFIFO_ACC_STAT_REG 0x96 +#define E1_TRNCVR_RHDLC3_RFIFO_ACC_STAT_REG 0x97 +#define E1_TRNCVR_RHDLC1_DATA_REG 0x98 +#define E1_TRNCVR_RHDLC2_DATA_REG 0x99 +#define E1_TRNCVR_RHDLC3_DATA_REG 0x9A +#define E1_TRNCVR_RHDLC1_INT_CTRL_REG 0x9B +#define E1_TRNCVR_RHDLC2_INT_CTRL_REG 0x9C +#define E1_TRNCVR_RHDLC3_INT_CTRL_REG 0x9D +#define E1_TRNCVR_RHDLC1_INT_IND_REG 0x9E +#define E1_TRNCVR_RHDLC2_INT_IND_REG 0x9F +#define E1_TRNCVR_RHDLC3_INT_IND_REG 0xA0 +#define E1_TRNCVR_RHDLC1_HIGH_ADDR_REG 0xA1 +#define E1_TRNCVR_RHDLC2_HIGH_ADDR_REG 0xA2 +#define E1_TRNCVR_RHDLC3_HIGH_ADDR_REG 0xA3 +#define E1_TRNCVR_RHDLC1_LOW_ADDR_REG 0xA4 +#define E1_TRNCVR_RHDLC2_LOW_ADDR_REG 0xA5 +#define E1_TRNCVR_RHDLC3_LOW_ADDR_REG 0xA6 +#define E1_TRNCVR_THDLC1_CTRL_REG 0xA7 +#define E1_TRNCVR_THDLC2_CTRL_REG 0xA8 +#define E1_TRNCVR_THDLC3_CTRL_REG 0xA9 +#define E1_TRNCVR_TFIFO1_TRESHOLD_REG 0xAA +#define E1_TRNCVR_TFIFO2_TRESHOLD_REG 0xAB +#define E1_TRNCVR_TFIFO3_TRESHOLD_REG 0xAC +#define E1_TRNCVR_THDLC1_DATA_REG 0xAD +#define E1_TRNCVR_THDLC2_DATA_REG 0xAE +#define E1_TRNCVR_THDLC3_DATA_REG 0xAF +#define E1_TRNCVR_TFIFO1_STATUS_REG 0xB0 +#define E1_TRNCVR_TFIFO2_STATUS_REG 0xB1 +#define E1_TRNCVR_TFIFO3_STATUS_REG 0xB2 +#define E1_TRNCVR_THDLC1_INT_CTRL 0XB3 +#define E1_TRNCVR_THDLC2_INT_CTRL 0XB4 +#define E1_TRNCVR_THDLC3_INT_CTRL 0XB5 +#define E1_TRNCVR_THDLC1_INT_IND_REG 0XB6 +#define E1_TRNCVR_THDLC2_INT_IND_REG 0XB7 +#define E1_TRNCVR_THDLC3_INT_IND_REG 0XB8 +#define E1_TRNCVR_ALARM_STATUS_REG 0xB9 +#define E1_TRNCVR_ALARM_CTRL_REG 0xBA +#define E1_TRNCVR_ALARM_IND_REG 0xBB +#define E1_TRNCVR_ALARM_CRITERIA_CTRL_REG 0xBC +#define E1_TRNCVR_PMON_CTRL_REG 0xC2 +#define E1_TRNCVR_PMON_INT_CTRL0_REG 0xC3 +#define E1_TRNCVR_PMON_INT_CTRL1_REG 0xC4 +#define E1_TRNCVR_PMON_INT_IND0_REG 0xC5 +#define E1_TRNCVR_PMON_INT_IND1_REG 0xC6 +#define E1_TRNCVR_TPLC_RPLC_PRGD_TEST_CFG_REG 0xC7 +#define E1_TRNCVR_TPLC_ACCESS_STATUS_REG 0xC8 +#define E1_TRNCVR_TPLC_ACCESS_CTRL_REG 0xC9 +#define E1_TRNCVR_TPLC_ACCESS_DATA_REG 0xCA +#define E1_TRNCVR_TPLC_CONF_REG 0xCB +#define E1_TRNCVR_TPLC_CTRL_ENA_REG 0xCC +#define E1_TRNCVR_RPLC_ACCESS_STATUS_REG 0xCD +#define E1_TRNCVR_RPLC_ACCESS_CTRL_REG 0xCE +#define E1_TRNCVR_RPLC_ACCESS_DATA_REG 0xCF +#define E1_TRNCVR_RPLC_CONF_REG 0xD0 +#define E1_TRNCVR_RPLC_CTRL_ENA_REG 0xD1 +#define E1_TRNCVR_RCRB_CONF_REG 0xD2 +#define E1_TRNCVR_RCRB_ACCESS_STATUS_REG 0xD3 +#define E1_TRNCVR_RCRB_ACCESS_CTRL_REG 0xD4 +#define E1_TRNCVR_RCRB_ACCESS_DATA_REG 0xD5 +#define E1_TRNCVR_RCRB_STATE_CHANGE_IND0_REG 0xD6 +#define E1_TRNCVR_RCRB_STATE_CHANGE_IND1_REG 0xD7 +#define E1_TRNCVR_RCRB_STATE_CHANGE_IND2_REG 0xD8 +#define E1_TRNCVR_RCRB_STATE_CHANGE_IND3_REG 0xD9 + +/* RCRB INDIRECT REGISTERS*/ +#define E1_TRNCVR_RCRB_RX_SIGN_DATA_TS1 0x01 +#define E1_TRNCVR_RCRB_RX_SIGN_DATA_TS2 0x02 +#define E1_TRNCVR_RCRB_RX_SIGN_DATA_TS3 0x03 +#define E1_TRNCVR_RCRB_RX_SIGN_DATA_TS4 0x04 +#define E1_TRNCVR_RCRB_RX_SIGN_DATA_TS5 0x05 +#define E1_TRNCVR_RCRB_RX_SIGN_DATA_TS6 0x06 +#define E1_TRNCVR_RCRB_RX_SIGN_DATA_TS7 0x07 +#define E1_TRNCVR_RCRB_RX_SIGN_DATA_TS8 0x08 +#define E1_TRNCVR_RCRB_RX_SIGN_DATA_TS9 0x09 +#define E1_TRNCVR_RCRB_RX_SIGN_DATA_TS10 0x0a +#define E1_TRNCVR_RCRB_RX_SIGN_DATA_TS11 0x0b +#define E1_TRNCVR_RCRB_RX_SIGN_DATA_TS12 0x0c +#define E1_TRNCVR_RCRB_RX_SIGN_DATA_TS13 0x0d +#define E1_TRNCVR_RCRB_RX_SIGN_DATA_TS14 0x0e +#define E1_TRNCVR_RCRB_RX_SIGN_DATA_TS15 0x0f + +#define E1_TRNCVR_RCRB_RX_SIGN_DATA_TS17 0x11 +#define E1_TRNCVR_RCRB_RX_SIGN_DATA_TS18 0x12 +#define E1_TRNCVR_RCRB_RX_SIGN_DATA_TS19 0x13 +#define E1_TRNCVR_RCRB_RX_SIGN_DATA_TS20 0x14 +#define E1_TRNCVR_RCRB_RX_SIGN_DATA_TS21 0x15 +#define E1_TRNCVR_RCRB_RX_SIGN_DATA_TS22 0x16 +#define E1_TRNCVR_RCRB_RX_SIGN_DATA_TS23 0x17 +#define E1_TRNCVR_RCRB_RX_SIGN_DATA_TS24 0x18 +#define E1_TRNCVR_RCRB_RX_SIGN_DATA_TS25 0x19 +#define E1_TRNCVR_RCRB_RX_SIGN_DATA_TS26 0x1a +#define E1_TRNCVR_RCRB_RX_SIGN_DATA_TS27 0x1b +#define E1_TRNCVR_RCRB_RX_SIGN_DATA_TS28 0x1c +#define E1_TRNCVR_RCRB_RX_SIGN_DATA_TS29 0x1d +#define E1_TRNCVR_RCRB_RX_SIGN_DATA_TS30 0x1e +#define E1_TRNCVR_RCRB_RX_SIGN_DATA_TS31 0x1f + +/* RPLC INDIRECT REGISTERS*/ + +/* TPLC INDIRECT REGISTERS*/ +#define E1_TRNCVR_TPLC_TS1_SIGNALING_TRUNK_REG 0x41 +#define E1_TRNCVR_TPLC_TS2_SIGNALING_TRUNK_REG 0x42 +#define E1_TRNCVR_TPLC_TS3_SIGNALING_TRUNK_REG 0x43 +#define E1_TRNCVR_TPLC_TS4_SIGNALING_TRUNK_REG 0x44 +#define E1_TRNCVR_TPLC_TS5_SIGNALING_TRUNK_REG 0x45 +#define E1_TRNCVR_TPLC_TS6_SIGNALING_TRUNK_REG 0x46 +#define E1_TRNCVR_TPLC_TS7_SIGNALING_TRUNK_REG 0x47 +#define E1_TRNCVR_TPLC_TS8_SIGNALING_TRUNK_REG 0x48 +#define E1_TRNCVR_TPLC_TS9_SIGNALING_TRUNK_REG 0x49 +#define E1_TRNCVR_TPLC_TS10_SIGNALING_TRUNK_REG 0x4a +#define E1_TRNCVR_TPLC_TS11_SIGNALING_TRUNK_REG 0x4b +#define E1_TRNCVR_TPLC_TS12_SIGNALING_TRUNK_REG 0x4c +#define E1_TRNCVR_TPLC_TS13_SIGNALING_TRUNK_REG 0x4d +#define E1_TRNCVR_TPLC_TS14_SIGNALING_TRUNK_REG 0x4e +#define E1_TRNCVR_TPLC_TS15_SIGNALING_TRUNK_REG 0x4f + +#define E1_TRNCVR_TPLC_TS17_SIGNALING_TRUNK_REG 0x51 +#define E1_TRNCVR_TPLC_TS18_SIGNALING_TRUNK_REG 0x52 +#define E1_TRNCVR_TPLC_TS19_SIGNALING_TRUNK_REG 0x53 +#define E1_TRNCVR_TPLC_TS20_SIGNALING_TRUNK_REG 0x54 +#define E1_TRNCVR_TPLC_TS21_SIGNALING_TRUNK_REG 0x55 +#define E1_TRNCVR_TPLC_TS22_SIGNALING_TRUNK_REG 0x56 +#define E1_TRNCVR_TPLC_TS23_SIGNALING_TRUNK_REG 0x57 +#define E1_TRNCVR_TPLC_TS24_SIGNALING_TRUNK_REG 0x58 +#define E1_TRNCVR_TPLC_TS25_SIGNALING_TRUNK_REG 0x59 +#define E1_TRNCVR_TPLC_TS26_SIGNALING_TRUNK_REG 0x5a +#define E1_TRNCVR_TPLC_TS27_SIGNALING_TRUNK_REG 0x5b +#define E1_TRNCVR_TPLC_TS28_SIGNALING_TRUNK_REG 0x5c +#define E1_TRNCVR_TPLC_TS29_SIGNALING_TRUNK_REG 0x5d +#define E1_TRNCVR_TPLC_TS30_SIGNALING_TRUNK_REG 0x5e +#define E1_TRNCVR_TPLC_TS31_SIGNALING_TRUNK_REG 0x5f + +#endif --- linux-2.6.27-rc2/drivers/net/wan/etp_stream/Makefile 1970-01-01 02:00:00.000000000 +0200 +++ linux/drivers/net/wan/etp_stream/Makefile 2008-08-08 12:59:30.824005131 +0300 @@ -0,0 +1,7 @@ +# +# Makefile for the ETP stream device driver. +# +# 1 Jul 2008, Matti Linnanvuori +# + +obj-$(CONFIG_ETP_STREAM) += etp_stream.o --- linux-2.6.27-rc2/drivers/net/wan/etp_stream/etp_stream.h 1970-01-01 02:00:00.000000000 +0200 +++ linux/drivers/net/wan/etp_stream/etp_stream.h 2008-08-08 13:07:18.042386059 +0300 @@ -0,0 +1,15 @@ +/* etp_stream.h */ + +/* Matti Linnanvuori, Copyright (C) 2006 Ascom (Finland) Oy. */ + +#define ETP_STREAM_SLOT _IO(0xE1, 1) +#define ETP_STREAM_SENSITIVITY _IO(0xE1, 2) +#define ETP_STREAM_SENSITIVITY_GET _IO(0xE1, 3) +#define ETP_STREAM_GET_TX_BUFFER_FILL _IO(0xE1, 4) +#define ETP_STREAM_BUFFER_SIZE_GET _IO(0xE1, 5) +#define ETP_STREAM_CLEAR 0 +#define ETP_STREAM_OVERFLOW_BIT 0 +#define ETP_STREAM_UNDERFLOW_BIT 1 +#define ETP_STREAM_OVERFLOW (1 << ETP_STREAM_OVERFLOW_BIT) +#define ETP_STREAM_UNDERFLOW (1 << ETP_STREAM_UNDERFLOW_BIT) +#define ETP_STREAM_GET_CLEAR_EXCEPTIONS _IO(0xE1, 6) --- linux-2.6.27-rc2/drivers/net/wan/etp_stream/etp_stream.c 1970-01-01 02:00:00.000000000 +0200 +++ linux/drivers/net/wan/etp_stream/etp_stream.c 2008-08-08 13:07:18.034384868 +0300 @@ -0,0 +1,844 @@ +/** + * + * etp_stream.c Pseudowire and sensitivity for ETP Octal E1/T1 card + * + * + * Author Matti Linnanvuori (matti.linnanvuori@ascom.com) + * + * This file is (c) under GNU PUBLIC LICENSE + * + **/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../etp.h" +#include "../etp_ioctl.h" +#include "../etp_idt.h" +#include "../idt82p2288.h" +#include "etp_stream.h" + +MODULE_DESCRIPTION("ETP Octal E1/T1 card pseudowire and sensitivity module"); +MODULE_VERSION("1.0.33"); +MODULE_AUTHOR("Matti Linnanvuori"); +MODULE_LICENSE("GPL"); + +enum { RED = HZ / 10ul }; + +static dev_t from; /* The first in the range of numbers. */ +enum { DEVICES = 256u * INTERFACES_PER_DEVICE }; +static struct cdev cdev; + +struct etp_interface { + struct mutex mutex; /* Lock mutex before etp. */ + struct hlist_head file; /* struct etp_file objects opened. */ + wait_queue_head_t queue; /* Blocking poll system calls queue. */ + /* The next word is written by either one soft interrupt or one timer */ + unsigned short transmitting; /* The number of the slot transmitted */ + unsigned short receiving; /* The number of the slot received. */ + unsigned char g704; /* The number of open streaming G.704 files. */ + unsigned char timeslot0;/* The number of open streaming G.704 files + using timeslot 0. */ + bool out; /* 1 if out of basic frame synchronization, + else 0. */ + unsigned long red; /* jiffies when red alarm would be declared. */ +}; + +struct etp_card { + struct delayed_work work; + unsigned number; + struct etp_interface interface[INTERFACES_PER_DEVICE]; +}; + +static struct etp_card *cards; /* Pointer to array. */ +static unsigned number; /* The number of the cards handled by this module. */ + +struct etp_file { + struct hlist_node node; + loff_t *position; + unsigned char card; /* The number of the device. */ + unsigned char interface;/* The number of the interface. */ + /* Starting timeslot and timeslot range length and first past timeslot + range end. */ + unsigned char slot, length; + unsigned char beyond; + atomic_t exceptions; /* ETP_STREAM_OVERFLOW | ETP_STREAM_UNDERFLOW */ + unsigned long flow; /* ETP_STREAM_OVERFLOW | ETP_STREAM_UNDERFLOW */ +}; + +/* Cleans up resources when this kernel module is removed. */ +static void __exit etp_cleanup(void) +{ + unregister_chrdev_region(from, DEVICES); + cdev_del(&cdev); + { + unsigned card = number - 1; + do { + struct etp_card *my_card = cards + card; + cancel_delayed_work_sync(&my_card->work); + } while (card--); + } + kfree(cards); +} + +static inline unsigned read_slot(loff_t *ppos) +{ + return (*ppos >> 8) & 0xffff; +} + +/* Notifies about reception of data from ETP and checks overflow. */ +static void notify_reception(unsigned device, + unsigned interface, + unsigned can_be_read, + const struct slot_struct *slot) +{ + struct hlist_node *node; + struct etp_interface *my_interface = + &cards[device].interface[interface]; + wait_queue_head_t *queue; + struct etp_file *file; + + my_interface->receiving = can_be_read; + rcu_read_lock(); + hlist_for_each_entry_rcu(file, node, &my_interface->file, node) { + if (unlikely(read_slot(file->position) == can_be_read)) { + set_bit(ETP_STREAM_OVERFLOW_BIT, &file->flow); + if (!(atomic_read(&file->exceptions) & + ETP_STREAM_OVERFLOW)) { + atomic_inc(&file->exceptions); + smp_mb__after_atomic_inc(); + } + } + } + rcu_read_unlock(); + queue = &my_interface->queue; + if (waitqueue_active(queue)) + wake_up_interruptible(queue); +} + +static inline unsigned write_slot(loff_t *ppos) +{ + return *ppos >> 40; +} + +/* Notifies about transmission of data to ETP. */ +static void notify_transmission(unsigned device, + unsigned interface, + unsigned can_be_written, + struct slot_struct *slot) +{ + struct etp_interface *my_interface = + &cards[device].interface[interface]; + wait_queue_head_t *queue; + struct hlist_node *node; + struct etp_file *file; + + my_interface->transmitting = can_be_written; + rcu_read_lock(); + hlist_for_each_entry_rcu(file, node, &my_interface->file, node) { + if (unlikely(write_slot(file->position) == can_be_written)) { + set_bit(ETP_STREAM_UNDERFLOW_BIT, &file->flow); + if (!(atomic_read(&file->exceptions) & + ETP_STREAM_UNDERFLOW)) + atomic_add(ETP_STREAM_UNDERFLOW, + &file->exceptions); + } + } + rcu_read_unlock(); + queue = &my_interface->queue; + if (waitqueue_active(queue)) + wake_up_interruptible(queue); +} + +/* Frame alignment signal OK? */ +static inline bool frame(const struct slot_struct *slot) +{ + uint8_t last = slot->e1_frame[FRAMES_IN_SLOT - 1u].e1_timeslot[0]; + if (last & 0x40) + last = slot->e1_frame[FRAMES_IN_SLOT - 2u].e1_timeslot[0]; + return (last & 0x7f) == 0x1b; +} + +/* Clearing all alarm indications to stop redundant IDT interrupts. */ +static void clear_alarm_indications(unsigned device, unsigned interface) +{ + int error = etp_write_idt_register_if_lock(device, + E1_TRNCVR_ALARM_IND_REG, + interface, 0x3f); + if (unlikely(error)) + dev_warn(&get_dev_priv(device)->interface_privates[interface] + .ch_priv.this_netdev->dev, + "Failed to clear alarm indication: %d\n", error); +} + +/* Checks if frame alignment signal is OK on a streaming G.703 interface. */ +static inline void +check_frame(struct etp_card *card, struct etp_device_private *etp, + unsigned device, unsigned interface, + struct etp_interface *my_interface, const struct slot_struct *slot, + const struct etp_channel_private *cp) +{ + if (frame(slot)) { + my_interface->out = false; + } else if (my_interface->out) { + if (time_before_eq(my_interface->red, jiffies)) { + int error; + rtnl_lock(); + error = etp_frame(device, interface, 1); + rtnl_unlock(); + if (unlikely(error)) { + dev_warn(&cp->this_netdev->dev, + "Failed to set to timeslot mode: %d\n", + error); + } else { + error = etp_write_idt_register_if_lock(device, + E1_TRNCVR_E1_MODE_REG, + interface, 0u); + if (unlikely(error)) + dev_warn(&cp->this_netdev->dev, + "Failed to disable multi-frame" + ": %d\n", error); + else + queue_work(etp->queue, + &card->work.work); + } + } + } else { + my_interface->red = jiffies + RED; + my_interface->out = true; + } +} + +/* Checks the alarms and frame alignment on streaming interfaces of a card. */ +static void check_alarm(struct work_struct *work) +{ + struct delayed_work *delayed = container_of(work, struct delayed_work, + work); + struct etp_card *card = container_of(delayed, struct etp_card, work); + const unsigned device = card->number; + struct etp_device_private *etp = get_dev_priv(device); + struct etp_interface_private *interfaces = etp->interface_privates; + struct etp_interface *my_interfaces = card->interface; + unsigned interface = 0u; + do { + struct etp_interface *my_interface = my_interfaces + interface; + struct etp_interface_private *ip = interfaces + interface; + down_write(&ip->semaphore); + if (my_interface->g704) { + clear_alarm_indications(device, interface); + if (ip->if_mode == IF_MODE_TIMESLOT && + my_interface->timeslot0) { +/* Timeslot 0 used. */ unsigned alarm; + bool red; + if (unlikely(atomic_read(&etp->reset))) + break; + alarm = etp_read_idt_register_if_lock( + device, + E1_TRNCVR_ALARM_STATUS_REG, + interface); + red = alarm & 1u; + if (!red) { + int error; +/* No red alarm. */ if (!frame(ip->ch_priv.tx + + ip->ch_priv. + last_tx_slot_transmitted)) { + queue_delayed_work(etp->queue, + &card->work, + RED); + goto UNLOCK; + } + rtnl_lock(); + error = etp_frame(device, interface, 0); + rtnl_unlock(); + my_interface->out = 0; + if (unlikely(error)) + dev_warn(&ip->ch_priv. + this_netdev->dev, + "Failed to set to " + "stream mode: %d\n", + error); + else + clear_alarm_indications(device, + interface); + } + } + } +UNLOCK: up_write(&ip->semaphore); + } while (interface++ < INTERFACES_PER_DEVICE - 1); +} + +/* Queue streaming alarm and frame alignment checking work. */ +static void etp_idt_int_callback(unsigned device) +{ + struct etp_card *card = &cards[device]; + queue_work(get_dev_priv(device)->queue, &card->work.work); +} + +static inline void save_read(loff_t *ppos, unsigned slot, unsigned frame, + unsigned timeslot) +{ + *ppos = (*ppos & 0xffffffff00000000ull) | (slot << 8) | (frame << 5) | + timeslot; +} + +static inline void save_write(loff_t *ppos, loff_t slot, loff_t frame, + loff_t timeslot) +{ + *ppos = (*ppos & 0xffffffffull) | (slot << 40) | (frame << 37) + | (timeslot << 32); +} + +/* Handles the open system call. */ +static int etp_open(struct inode *inode, struct file *filp) +{ + unsigned minor = MINOR(inode->i_rdev); + unsigned card = minor >> 3; + struct etp_file *file; + if (unlikely(card >= number)) + return -ENXIO; + if (unlikely(!try_module_get(THIS_MODULE))) + return -EBUSY; + file = kmalloc(sizeof(struct etp_file), GFP_KERNEL); + if (likely(file)) { + unsigned interface_number = minor & (INTERFACES_PER_DEVICE - 1); + filp->private_data = file; + file->interface = interface_number; + file->card = card; + INIT_HLIST_NODE(&file->node); + file->slot = 0u; + save_write(&filp->f_pos, 0u, 0u, 0u); + save_read(&filp->f_pos, 0u, 0u, 0u); + file->beyond = E1_TIMESLOTS_PER_INTERFACE; + atomic_set(&file->exceptions, ETP_STREAM_CLEAR); + file->flow = ETP_STREAM_CLEAR; + file->length = E1_TIMESLOTS_PER_INTERFACE; + file->position = &filp->f_pos; + return 0; + } else { + return -ENOMEM; + } +} + +/* Handles the close system call. */ +static int etp_close(struct inode *inode, struct file *filp) +{ + struct etp_file *file = filp->private_data; + if (!hlist_unhashed(&file->node)) { + const unsigned card_number = file->card; + const unsigned interface_number = file->interface; + struct etp_card *card = &cards[card_number]; + struct etp_interface *interface = + &card->interface[interface_number]; + struct mutex *mutex = &interface->mutex; + mutex_lock(mutex); /* Protect list and memory integrity. */ + hlist_del_rcu(&file->node); + if (file->length < E1_TIMESLOTS_PER_INTERFACE) { + interface->g704--; + if (file->slot == 0) + interface->timeslot0--; + } + /* No more open files for interface? */ + if (hlist_empty(&interface->file)) { + const struct etp_callback_struct callback = { + NULL, NULL, NULL, + card_number, interface_number, 1 }; + etp_register_callbacks(&callback); + } + mutex_unlock(mutex); + synchronize_rcu(); + } + kfree(file); + module_put(THIS_MODULE); + return 0; +} + +static inline unsigned read_frame(loff_t *ppos) +{ + return (*ppos >> 5) & (FRAMES_IN_SLOT - 1); +} + +static inline unsigned read_timeslot(loff_t *ppos) +{ + return *ppos & (E1_TIMESLOTS_PER_INTERFACE - 1); +} + +/* Reads data from ETP DMA reception buffer to user space. */ +static ssize_t +etp_read(struct file *file_p, char __user *buf, size_t length, loff_t *ppos) +{ + struct etp_file *file = file_p->private_data; + const unsigned device = file->card; + const unsigned interface_number = file->interface; + const struct slot_struct *rx, *slot; + ssize_t read = 0; + unsigned reading, reading_frame, reading_slot, rx_slots, beyond; + unsigned starting; + struct etp_card *card = &cards[device]; + struct etp_interface *interface = &card->interface[interface_number]; + struct etp_device_private *dp = get_dev_priv(device); + struct etp_interface_private *ip = + &dp->interface_privates[interface_number]; + const struct etp_channel_private *channel = &ip->ch_priv; + struct rw_semaphore *semaphore = &ip->semaphore; + down_write(semaphore); + rx = channel->rx; + if (unlikely(rx == NULL)) { + up_write(semaphore); + return -ENXIO; + } + rx_slots = channel->rx_slots; + reading = read_slot(ppos); + reading *= reading < rx_slots; + slot = rx + reading; + if (ip->if_mode == IF_MODE_STREAM && interface->g704) + check_frame(card, dp, device, interface_number, interface, + slot, channel); + reading_frame = read_frame(ppos); + reading_slot = read_timeslot(ppos); + beyond = file->beyond; + starting = file->slot; + while (length) { + const void *source; + unsigned slots; + if (unlikely(reading == interface->receiving && + !reading_frame)) { + if (file->flow & ETP_STREAM_OVERFLOW) { + clear_bit(ETP_STREAM_OVERFLOW_BIT, &file->flow); + goto NEXT; + } + if (read == 0) + read = -EAGAIN; + goto SAVE; + } + source = slot->e1_frame[reading_frame].e1_timeslot + + reading_slot; + prefetch(source); + slots = beyond - reading_slot; + slots = min(length, slots); + if (unlikely(__copy_to_user(buf + read, source, slots))) { + read = -EFAULT; + goto SAVE; + } + read += slots; + length -= slots; + reading_slot += slots; + if (likely(reading_slot >= beyond)) { + reading_slot = starting; + reading_frame++; + if (reading_frame == FRAMES_IN_SLOT) { + reading_frame = 0; +NEXT: reading++; + reading *= reading < rx_slots; + slot = rx + reading; + } + } + } +SAVE: save_read(ppos, reading, reading_frame, reading_slot); + up_write(semaphore); + return read; +} + +static inline unsigned write_frame(loff_t *ppos) +{ + return (*ppos >> 37) & (FRAMES_IN_SLOT - 1); +} + +static inline unsigned write_timeslot(loff_t *ppos) +{ + return (*ppos >> 32) & (E1_TIMESLOTS_PER_INTERFACE - 1); +} + +/* Writes data to ETP DMA transmission buffer from user space. */ +static ssize_t +etp_write(struct file *file_p, const char __user *buf, size_t count, + loff_t *ppos) +{ + struct etp_file *file = file_p->private_data; + const unsigned device = file->card; + const unsigned interface_number = file->interface; + struct slot_struct *slot, *write; + ssize_t written = 0; + struct etp_interface *interface = &cards[device].interface + [interface_number]; + const struct etp_channel_private *channel = + &get_dev_priv(device)->interface_privates[interface_number]. + ch_priv; + unsigned writing; + unsigned writing_frame; + unsigned tx_slots; + unsigned writing_slot; + unsigned beyond; + unsigned starting; + struct rw_semaphore *semaphore = &this_if_priv(channel)->semaphore; + down_write(semaphore); + slot = channel->tx; + if (unlikely(slot == NULL)) { + up_write(semaphore); + return -ENXIO; + } + tx_slots = channel->tx_slots; + writing = write_slot(ppos); + writing *= writing < tx_slots; + write = slot + writing; + writing_frame = write_frame(ppos); + writing_slot = write_timeslot(ppos); + beyond = file->beyond; + starting = file->slot; + while (count) { + unsigned length; + if (unlikely(writing == interface->transmitting && + !writing_frame)) { + if (file->flow & ETP_STREAM_UNDERFLOW) { + clear_bit(ETP_STREAM_UNDERFLOW_BIT, + &file->flow); + goto NEXT; + } + if (!written) + written = -EAGAIN; + goto SAVE; + } + length = beyond - writing_slot; + length = min(count, length); + if (unlikely(__copy_from_user + (write->e1_frame[writing_frame].e1_timeslot + + writing_slot, buf + written, length))) { + written = -EFAULT; + goto SAVE; + } + written += length; + count -= length; + writing_slot += length; + if (likely(writing_slot >= beyond)) { + writing_slot = starting; + writing_frame++; + if (writing_frame == FRAMES_IN_SLOT) { + writing_frame = 0; +NEXT: writing++; + writing *= writing < tx_slots; + write = slot + writing; + } + } + } +SAVE: save_write(ppos, writing, writing_frame, writing_slot); + up_write(semaphore); + flush_write_buffers(); + return written; +} + +/* Handles select system call. */ +static unsigned int etp_poll(struct file *file, poll_table *wait) +{ + struct etp_file *etp = file->private_data; + struct etp_interface *interface = + &cards[etp->card].interface[etp->interface]; + loff_t *position = etp->position; + unsigned long flow; + poll_wait(file, &interface->queue, wait); + flow = etp->flow; + return + ((interface->receiving != read_slot(position) || + (flow & ETP_STREAM_OVERFLOW)) * (POLLIN | POLLRDNORM)) | + ((interface->transmitting != write_slot(position) || + (flow & ETP_STREAM_UNDERFLOW)) * (POLLOUT | POLLWRNORM)) + | ((atomic_read(&etp->exceptions) != ETP_STREAM_CLEAR) * POLLPRI); +} + +/* Sets the starting slot and slot range length of the opened file. */ +static inline int etp_slot(struct file *file_p, unsigned long arg) +{ + struct etp_file *file = file_p->private_data; + const unsigned char card_number = file->card; + struct etp_card *card = &cards[card_number]; + struct etp_device_private *device = get_dev_priv(card_number); + const unsigned char interface_number = file->interface; + struct etp_interface *my_interface = &card->interface[interface_number]; + const unsigned char slot = file->slot; + const unsigned char oldlength = file->length; + struct etp_interface_private *interface = + &device->interface_privates[interface_number]; + int error; + const struct etp_callback_struct callback = { + notify_reception, notify_transmission, + etp_idt_int_callback, card_number, interface_number, 1 }; + struct mutex *mutex = &my_interface->mutex; + struct rw_semaphore *semaphore = &interface->semaphore; + mutex_lock(mutex); + down_write(semaphore); + file->slot = arg & (E1_TIMESLOTS_PER_INTERFACE - 1); + file->length = arg >> 5; + if (unlikely(!file->length || + file->length > E1_TIMESLOTS_PER_INTERFACE)) + file->length = E1_TIMESLOTS_PER_INTERFACE; + file->beyond = file->slot + file->length; + if (unlikely(file->beyond > E1_TIMESLOTS_PER_INTERFACE)) { + file->beyond = E1_TIMESLOTS_PER_INTERFACE; + file->length = E1_TIMESLOTS_PER_INTERFACE - file->slot; + } + save_write(&file_p->f_pos, write_slot(&file_p->f_pos), + write_frame(&file_p->f_pos), file->slot); + save_read(&file_p->f_pos, read_slot(&file_p->f_pos), + read_frame(&file_p->f_pos), file->slot); + switch (interface->if_mode) { + case IF_MODE_STREAM: + if (likely(file->length < E1_TIMESLOTS_PER_INTERFACE)) { + my_interface->g704 += + oldlength == E1_TIMESLOTS_PER_INTERFACE; + my_interface->timeslot0 += + (file->slot == 0) - + (slot == 0 && + oldlength < E1_TIMESLOTS_PER_INTERFACE); + rtnl_lock(); + error = etp_frame(card_number, interface_number, 1); + rtnl_unlock(); + if (unlikely(error)) + dev_warn(&interface->ch_priv.this_netdev->dev, + "Failed to set to timeslot mode: %d\n", + error); + goto TIMESLOT; + } else if (unlikely(oldlength < E1_TIMESLOTS_PER_INTERFACE)) { + my_interface->g704--; + my_interface->timeslot0 -= slot == 0; + } + break; + case IF_MODE_TIMESLOT: + { + unsigned g704 = + file->length < E1_TIMESLOTS_PER_INTERFACE; + unsigned g704_old = oldlength < + E1_TIMESLOTS_PER_INTERFACE; + error = etp_write_idt_register_if_lock(card_number, + E1_TRNCVR_E1_MODE_REG, + interface_number, 0u); + if (unlikely(error)) + dev_warn(&interface->ch_priv.this_netdev->dev, + "Failed to disable multi-frame: %d\n", + error); + my_interface->g704 += g704 - g704_old; + if (likely(file->slot == 0u)) { + my_interface->timeslot0 += g704 && (!g704_old + || slot != + 0u); +TIMESLOT: queue_work(device->queue, &card->work.work); + } else { + my_interface->timeslot0 -= + g704_old && slot == 0u; + } + } + } + if (hlist_unhashed(&file->node)) + hlist_add_head_rcu(&file->node, &my_interface->file); + up_write(semaphore); + error = etp_register_callbacks(&callback); + mutex_unlock(mutex); + return error; +} + +static uint32_t etp_fill(const struct etp_file *file, + unsigned short tx_slots, unsigned char length, + unsigned char card, unsigned char interface_number) +{ + const struct etp_interface *interface = + &cards[card].interface[interface_number]; + uint32_t slots = (uint32_t)write_slot(file->position) - + (uint32_t)interface->transmitting; + if (slots >= MAX_SLOTS) /* uint32_t underflow */ + slots += tx_slots; + return slots * FRAMES_IN_SLOT * length; +} + +/* Handles ioctl system calls. */ +static int +etp_ioctl(struct inode *inode, + struct file *file_p, unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case ETP_STREAM_GET_TX_BUFFER_FILL: + { + const struct etp_file *file = file_p->private_data; + unsigned char card = file->card; + unsigned char interface = file->interface; + return etp_fill(file, get_dev_priv(card)-> + interface_privates[interface]. + ch_priv.tx_slots, file->length, + card, interface); + } + case ETP_STREAM_GET_CLEAR_EXCEPTIONS: + { + struct etp_file *file = file_p->private_data; + return atomic_xchg(&file->exceptions, ETP_STREAM_CLEAR); + } + case ETP_STREAM_SLOT: + return etp_slot(file_p, arg); + case ETP_STREAM_SENSITIVITY: +/* Sets the sensitivity -10 dB (short haul) or -44 dB (long haul) */ + { + unsigned data = arg == (unsigned long)-10 ? 0x15 : 0x54; + struct etp_file *file = file_p->private_data; + return etp_write_idt_register_if_lock( + file->card, + E1_TRNCVR_RX_CONF1_REG, + file->interface, + data); + } + case ETP_STREAM_SENSITIVITY_GET: +/* Returns the value of the IDT register Receive Configuration 1 */ + { + struct etp_file *file = file_p->private_data; + return etp_read_idt_register_if_lock(file->card, + E1_TRNCVR_RX_CONF1_REG, + file->interface); + } + case ETP_STREAM_BUFFER_SIZE_GET: + { + struct etp_file *interface = file_p->private_data; + return get_dev_priv(interface->card)-> + interface_privates[interface->interface]. + ch_priv.tx_slots; + } + default: + return -ENOTTY; + } +} + +static inline loff_t write_position(loff_t offset) +{ + return offset >> 32; +} + +static loff_t etp_seek(struct file *file_p, loff_t loffset, int whence) +{ + struct etp_file *file = file_p->private_data; + const unsigned char length = file->length; + int32_t offset = loffset; + const unsigned char card = file->card, interface = file->interface; + int32_t slot_offset, frame_offset, slot, frame, writing; + unsigned short slots; + struct etp_interface_private *ip = + &get_dev_priv(card)->interface_privates[interface]; + struct rw_semaphore *semaphore = &ip->semaphore; + down_write(semaphore); + slots = ip->ch_priv.tx_slots; + if (unlikely(!slots)) { + up_write(semaphore); + return -ESPIPE; + } + switch (whence) { + case SEEK_CUR: + { + int32_t fill = -etp_fill(file, slots, length, card, interface); + if (unlikely(offset < fill)) { + goto INVALID; + } else if (unlikely(offset == fill)) { + if (!write_frame(file->position)) + set_bit(ETP_STREAM_UNDERFLOW_BIT, &file->flow); + } else { + int32_t limit = (int32_t)slots * FRAMES_IN_SLOT * + (int32_t)length + fill; + if (unlikely(offset > limit)) { + if (file->flow & ETP_STREAM_UNDERFLOW) { + clear_bit(ETP_STREAM_UNDERFLOW_BIT, + &file->flow); + } else { +INVALID: up_write(semaphore); + return -EINVAL; + } + } + } + } +CUR: + slot_offset = offset % (int32_t)length; + frame_offset = offset / (int32_t)length; + slot = (int32_t)write_timeslot(&file_p->f_pos) + slot_offset; + frame = (int32_t)write_frame(&file_p->f_pos) + frame_offset; + if (slot < 0) { + slot += length; + frame--; + } else if (slot >= file->beyond) { + slot -= length; + frame++; + } + writing = (int32_t)write_slot(&file_p->f_pos) + frame / FRAMES_IN_SLOT; + frame %= FRAMES_IN_SLOT; + if (frame < 0) { + frame += FRAMES_IN_SLOT; + writing--; + } + writing %= slots; + if (writing < 0) + writing += slots; + save_write(&file_p->f_pos, writing, frame, slot); + loffset = write_position(file_p->f_pos); + break; + case SEEK_END: + writing = cards[card].interface[interface].transmitting; + frame = 0u; + slot = file->slot; + save_write(&file_p->f_pos, writing, frame, slot); + goto CUR; + default: + file_p->f_pos = (file_p->f_pos & 0xffffffffull) | (loffset << 32); + loffset = write_position(file_p->f_pos); + } + up_write(semaphore); + return loffset; +} + +static struct file_operations etp_char_fops = { + .read = etp_read, + .write = etp_write, + .open = etp_open, + .release = etp_close, + .ioctl = etp_ioctl, + .poll = etp_poll, + .llseek = etp_seek +}; + +/* Initializes this kernel module. */ +static int __init etp_init(void) +{ + unsigned index; + int error; + number = etp_number_devices(); + if (unlikely(number == 0u)) + return -ENXIO; + cards = kzalloc(sizeof(struct etp_card) * number, GFP_KERNEL); + if (unlikely(cards == NULL)) + return -ENOMEM; + index = number - 1u; + do { + struct etp_card *card = cards + index; + unsigned interface; + card->number = index; + interface = INTERFACES_PER_DEVICE - 1; + do { + struct etp_interface *my_interface = + card->interface + interface; + INIT_HLIST_HEAD(&my_interface->file); + init_waitqueue_head(&my_interface->queue); + mutex_init(&my_interface->mutex); + } while (interface--); + INIT_DELAYED_WORK(&card->work, check_alarm); + } while (index--); + + error = alloc_chrdev_region(&from, 0u, 256u * INTERFACES_PER_DEVICE, + THIS_MODULE->name); + if (unlikely(error)) { +FREE: kfree(cards); + return error; + } + cdev_init(&cdev, &etp_char_fops); + error = cdev_add(&cdev, from, DEVICES); + if (unlikely(error)) { + unregister_chrdev_region(from, DEVICES); + goto FREE; + } + return 0; +} + +module_init(etp_init); +module_exit(etp_cleanup); --- linux/drivers/net/wan/etp.h 1970-01-01 02:00:00.000000000 +0200 +++ linux-next/drivers/net/wan/etp.h 2008-10-06 10:55:40.654426735 +0300 @@ -0,0 +1,451 @@ +/* etp.h */ + +/* + Copyright (C) 2005 Jouni Kujala, Flexibilis Oy. + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + All the drivers derived from or based on this code must retain + the copyright notice. +*/ + +#ifndef _ETP_H_ +#define _ETP_H_ + +#include +#include +#include +#include +#include "etp_ioctl.h" + +#define PCI_DEVICE_ID_ETP_ORIGINAL 0x2 +#define PCI_DEVICE_ID_ETP_104 0xA +#define PCI_DEVICE_ID_DONTCARE 0x0 + +/* Offsets to the registers. */ +#define REG_GENERAL 0x0 +#define REG_LED_CTRL 0x4 +#define REG_RST_CTRL 0x10 +#define REG_NCO_CTRL 0x20 +#define REG_CLK_STAT 0x30 +#define REG_E1_CTRL 0x40 + +#define REG_INT_MASK0 0x80 +#define REG_INT_MASK1 0x84 +#define REG_INT_MASK2 0x88 +#define REG_INT_STAT0 0xc0 +#define REG_INT_STAT1 0xc4 +#define REG_INT_STAT2 0xc8 + +#define REG_RXCTRL_IF(x) (0x2000 + (x) * 0x80) +#define REG_RXCTRL1_IF(x) (0x2004 + (x) * 0x80) +#define REG_TXCTRL_IF(x) (0x3000 + (x) * 0x80) +#define REG_TXCTRL1_IF(x) (0x3004 + (x) * 0x80) +#define REG_RXCTRL_CH(x) (0x4000 + (x) * 0x80) +#define REG_TXCTRL_CH(x) (0x6000 + (x) * 0x80) + +#define REG_RXDESCxA_CHy(x, y) (0x10000 + (x) * 0x8 + (y) * 0x80) +#define REG_RXDESCxB_CHy(x, y) (0x10004 + (x) * 0x8 + (y) * 0x80) + +#define REG_TXDESCxA_CHy(x, y) (0x18000 + (x) * 0x8 + (y) * 0x80) +#define REG_TXDESCxB_CHy(x, y) (0x18004 + (x) * 0x8 + (y) * 0x80) + +struct rxdesc { + uint32_t desc_a; + uint32_t desc_b; +}; + +struct txdesc { + uint32_t desc_a; + uint32_t desc_b; +}; + +/* Bits in General register: */ + +#define LVDS_ENABLE_MASK (1 << 20) +#define LVDS_ENABLE (1 << 20) + +#define E1_RESET_MASK (1 << 21) +#define E1_RESET_ENABLE (1 << 21) + +#define E1_HIGH_Z_MASK (1 << 22) +#define E1_HIFH_Z_ENABLE (1 << 22) + +#define OUTPUT_CLK_SELECT_MASK ((1 << 27) | (1 << 28) | (1 << 29)) +#define OUTPUT_CLK_SELECT_SHIFT 27 +#define CLOCK_SELECT_LOCAL 0x0 +#define CLOCK_SELECT_DALLAS 0x1 +#define CLOCK_SELECT_RJ 0x2 +#define CLOCK_SELECT_LVDS 0x3 +#define CLOCK_SELECT_E1_GEN 0x5 +#define CLOCK_SELECT_E1_A 0x6 +#define CLOCK_SELECT_NO_CLOCK 0x7 + +/* Bits in Reset Control register. */ +#define RESET_CH(x) (1 << (x)) + +/* Bits in LED ctrl register: */ +#define ALL_LED_BITS (0x3) +#define LED_CTRL_OFF (0x0) +#define LED_CTRL_ON (0x1) +#define LED_CTRL_BLINK (0x2) +#define LED_CTRL_TRAFFIC (0x3) + +#define LEDx_SHIFT(x) ((x) * 2) + + +/* Bits in CLOCK STATUS register: */ +#define EXT_CLOCK_RJ_STATUS_MASK 0xFF +#define EXT_CLOCK_RJ_STATUS_SHIFT 0 +#define EXT_CLOCK_LVDS_STATUS_MASK 0xFF0000 +#define EXT_CLOCK_LVDS_STATUS_SHIFT 16 +#define EXT_CLOCK_NCO_STATUS_MASK 0xFF000000 +#define EXT_CLOCK_NCO_STATUS_SHIFT 24 + +/* Bits in E1 control register: */ +#define E1_DATA_MASK 0xFF +#define E1_REGISTER_MASK 0xFFF0000 +#define E1_REGISTER_MASK_NO_IF 0xFF0000 +#define E1_REGISTER_MASK_IF 0xF000000 +#define E1_REGISTER_SHIFT 16 +#define E1_REGISTER_SHIFT_IF 24 +#define E1_DIR_MASK (1 << 30) +#define E1_DIR_READ (1 << 30) +#define E1_DIR_WRITE 0x0 +#define E1_ACCESS_ON (1 << 31) + +/* Bits in interrupt mask0 and status0 register: */ +#define INT_0_RECEIVED_CH(x) (1 << (4 * (x))) +#define INT_0_TRANSMITTED_CH(x) (1 << (4 * (x) + 1)) +#define INT_0_RX_DROPPED_CH(x) (1 << (4 * (x) + 2)) +#define INT_0_TX_UNDERF_CH(x) (1 << (4 * (x) + 3)) + +/* Bits in interrupt mask2 and status2 register: */ +#define INT_2_E1_INT (1 << 0) +#define INT_2_E1_ACCESS_DONE (1 << 8) +#define INT_2_ALLINTS (INT_2_E1_INT | INT_2_E1_ACCESS_DONE) +#define INT_2_RX_RESYNC_CH(x) (1 << (16 + (x))) +#define INT_2_TX_RESYNC_CH(x) (1 << (24 + (x))) + +/* Interrupt bit generalization */ +#define INT_0_BIT_SHIFT_CH(x) ((x) * 4) +#define INT_2_BIT_SHIFT_CH(x) ((x) + 16) +#define CH_ALLINTS_MASK 0xF +#define INT_RECEIVED (1 << 0) +#define INT_TRANSMITTED (1 << 1) +#define INT_RX_DROPPED (1 << 2) +#define INT_TX_UNDERF (1 << 3) + +#define INT2_RX_RESYNC (1 << 0) +#define INT2_TX_RESYNC (1 << 8) +#define CH_ALLINTS2_MASK (INT2_RX_RESYNC | INT2_TX_RESYNC) + +/* Bits in interface RX control register. */ +#define E1_MODE_HDLC 1 +#define E1_MODE_TIMESLOT 0 +#define E1_MODE_MASK 1 +#define HDLC_CRC_16 (1 << 4) +#define HDLC_CRC_32 (0) +#define HDLC_CRC_DELAY (1 << 5) +#define HDLC_CRC_MASK ((1 << 4) | (1 << 5)) +#define HDLC_RETINA_FLAG (1 << 6) + +#define CLOCK_SELECT_RX_X 0x8 /* check if clock is rx clock */ +#define CLOCK_SELECT_RX(x) (((x) | 0x8) & 0xF) /* interface clock */ +#define CLOCK_SELECT_RX_TO_CH(x) ((x) & 0x7) /* clock select to interface */ +#define TX_CLOCK_SELECT_SHIFT 24 +#define TX_CLOCK_SELECT_MASK (0xF << TX_CLOCK_SELECT_SHIFT) + +/* Bits in channel RX control register */ +#define DMA_LENGTH_LIMIT_MASK (0xFFF) +#define FIFO_THRESHOLD_SHIFT 24 +#define FIFO_THRESHOLD_MASK (0x7 << FIFO_THRESHOLD_SHIFT) +#define RX_FIFO_THRESHOLD_DEFAULT (0x2 << FIFO_THRESHOLD_SHIFT) +#define DMA_ENABLE_MASK (1 << 31) +#define DMA_ENABLE (1 << 31) + +/* Bits in channel TX control register */ +#define TX_FIFO_THRESHOLD_DEFAULT (0x6 << FIFO_THRESHOLD_SHIFT) +#define TX_START_LEVEL_SHIFT 27 +#define TX_START_LEVEL_MASK (0x7 << TX_START_LEVEL_SHIFT) +#define TX_START_LEVEL_DEFAULT (0x4 << TX_START_LEVEL_SHIFT) + +/* Bits in descriptors */ +#define RX_DESCB_LENGT_MASK (0xFFF) +#define RX_DESCB_FIFO_ERR (1 << 16) +#define RX_DESCB_SIZE_ERR (1 << 17) +#define RX_DESCB_CRC_ERR (1 << 18) +#define RX_DESCB_OCTET_ERR (1 << 19) +#define RX_DESCB_TRANSFER (1 << 31) + +#define TX_DESCB_LENGT_MASK (0xFFF) +#define TX_DESCB_FIFO_ERR (1 << 16) +#define TX_DESCB_TRANSFER (1 << 31) + +/* interface to channel defines: */ +#define IF_TO_CH(x) (x) +#define CH_TO_IF(x) (x) + +#define DESCRIPTORS_PER_CHANNEL 8 +#define TX_TIMEOUT (1*HZ) /* 1 sec in jiffies */ + +#define MAX_SLOTS 65535 +#define MIN_SLOTS 0x8 +#define SLOT_SIZE 0x100 + +#define E1_TIMESLOTS_PER_CHANNEL 32 +struct e1_frame { + uint8_t e1_timeslot[E1_TIMESLOTS_PER_CHANNEL]; +}; + +#define FRAMES_IN_SLOT 8 +struct slot_struct { + struct e1_frame e1_frame[FRAMES_IN_SLOT]; +}; + +#define ETP_TIMER (HZ > 1000 / DESCRIPTORS_PER_CHANNEL) + +struct rx_descriptor { + struct rxdesc __iomem *descriptor; + struct sk_buff *skb; + DECLARE_PCI_UNMAP_ADDR(address) +}; + +struct tx_descriptor { + struct txdesc __iomem *descriptor; + struct sk_buff *skb; + DECLARE_PCI_UNMAP_ADDR(address) +}; + +struct etp_channel_private { + struct etp_device_private *this_dev_priv; + struct net_device *this_netdev; + struct napi_struct napi; + bool interrupt; /* A reception or transmission event to handle? */ + unsigned char channel_number; /* channel number inside a device */ + unsigned char device_number; + uint32_t __iomem *reg_ch_rxctrl; + struct rx_descriptor rx_descriptor[DESCRIPTORS_PER_CHANNEL]; + uint32_t __iomem *reg_ch_txctrl; + struct tx_descriptor tx_descriptor[DESCRIPTORS_PER_CHANNEL]; +/* ------------ hdlc mode specific: ------------- */ + uint32_t hdlc_mode_g704_used_timeslots; + unsigned char hdlc_mode; /* HDLC_MODE_XXXX */ + /* last or next sent descriptor written by etp_netdev_start_xmit */ + unsigned char last_tx_desc_transmitted; +/* ------------ timeslot mode specific: ------------- */ + unsigned short tx_slots; /* 8 - */ +#if ETP_TIMER + struct timer_list timer; +#endif + struct slot_struct *rx; + dma_addr_t rx_address; + void (*rx_callback) (unsigned device, + unsigned interface, + unsigned read, + const struct slot_struct *rx); + unsigned short rx_slots; /* 8 - */ + unsigned short last_rx_slot_received; + /* last or next received descriptor */ + unsigned char last_rx_desc_received; + unsigned char last_tx_desc_released; /* last tx descriptor released */ + unsigned short last_tx_slot_transmitted; + struct slot_struct *tx; + dma_addr_t tx_address; + void (*tx_callback) (unsigned device, + unsigned interface, + unsigned written, + struct slot_struct *tx); + atomic_t owner; /* Owner (0, 1 or unowned) of callbacks. */ +}; + +/** + * Locking order: 1 struct etp_device_private idt[0] + * 2 struct etp_device_private idt[1] + * 3 struct etp_interface_private semaphore e1_00 + * ... + * 66 struct etp_interface_private semaphore e1_63 + * 67 rtnl_lock(); + * 68 struct etp_device_private mutex + **/ + +struct etp_interface_private { + struct etp_channel_private ch_priv; + struct rw_semaphore semaphore; + uint32_t tx_clock_source; + /* The updates of the next word are synchronized with rtnl_lock(): */ + unsigned char if_mode; + bool los; /* Loss of signal? */ + + /* interface specific register locations: */ + uint32_t __iomem *reg_if_rxctrl; + uint32_t __iomem *reg_if_txctrl; + uint32_t __iomem *reg_if_rxctrl1; + uint32_t __iomem *reg_if_txctrl1; +}; + +enum { ETP_CALLBACKS = 2 }; +typedef void (*etp_idt_callback_t) (unsigned device); + +struct etp_device_private { + struct work_struct status_work; + struct mutex idt; /* The next word is written with mutex locked. */ + unsigned char number; /* The number of the card. */ + unsigned char run[ETP_CALLBACKS]; /* Run callback with index? Bitmap. */ + etp_idt_callback_t idt_int_callback[ETP_CALLBACKS]; + struct delayed_work led; + struct workqueue_struct *queue; + struct etp_interface_private interface_privates[INTERFACES_PER_DEVICE]; + + struct mutex mutex; /* IDT chip access mutex */ + atomic_t reset; /* 1: device unusable; 0: device usable. */ + atomic_t interrupt; /* 1: IDT interrupt; 0: no IDT interrupt. */ + uint32_t led_register_value; + spinlock_t lock2; + uint32_t reg_int_mask2; + + struct pci_dev *pci_dev; /* this PCI device */ + uint8_t __iomem *ioaddr; + spinlock_t lock0; + uint32_t reg_int_mask0; +}; + +extern unsigned get_led(const struct etp_interface_private *ip); + +extern struct etp_device_private **etp_devices; + +static inline struct etp_device_private *get_dev_priv(unsigned device) +{ + struct etp_device_private *card; + rcu_read_lock(); + card = rcu_dereference(etp_devices[device]); + rcu_read_unlock(); + return card; +} + +static inline +struct etp_interface_private *this_if_priv(const struct etp_channel_private *cp) +{ + return container_of(cp, struct etp_interface_private, ch_priv); +} + +static inline unsigned device_number(const struct etp_device_private *dp) +{ + return dp->number; +} + +/* kernel interface: struct to be used when registering callback functions: */ + +struct etp_callback_struct { + void (*rx_callback) (unsigned device, + unsigned interface, + unsigned read, + const struct slot_struct *buffer); + void (*tx_callback) (unsigned device, + unsigned interface, + unsigned written, + struct slot_struct *buffer); + void (*idt_int_callback) (unsigned device); + unsigned device, interface; + bool index; +}; + +/** + * Functions callable from inside kernel, i.e. kernel interface functions. + * Unless otherwise stated, the functions return 0 on success. + **/ + +static inline struct etp_device_private * +this_device_priv(const struct etp_channel_private *cp) +{ + return cp->this_dev_priv; +} + +static inline +struct etp_device_private *this_dev_priv(const struct etp_interface_private *ip) +{ + return this_device_priv(&ip->ch_priv); +} + +static inline unsigned interface_number(const struct etp_interface_private *ip) +{ + return CH_TO_IF(ip->ch_priv.channel_number); +} + +/* Registers callback fuctions. */ +extern int etp_register_callbacks(const struct etp_callback_struct *callback); + +/* Open interface (timeslot and stream mode only). */ +extern int etp_if_open(unsigned device, + unsigned interface, + unsigned if_mode, + unsigned rx_slots, + unsigned tx_slots); + +/** + * Set timeslot (true) or stream (false) mode for an interface that is in stream + * or timeslot mode. Caller must hold rtnl_lock(). + **/ +extern int etp_frame(unsigned device, unsigned interface, bool frame); + +/* Close interface (timeslot and stream mode only) */ +extern int etp_if_close(unsigned device, unsigned interface); + +/* Start transmitter (timeslotand stream mode only) */ +int etp_tx_on(unsigned device, unsigned interface); + +/* Stop transmitter (timeslot and stream mode only). */ +int etp_tx_off(unsigned device, unsigned interface); + +/* Start receiver (timeslot and stream mode only) */ +int etp_rx_on(unsigned device, unsigned interface); + +/* Stop receiver (timeslot and stream mode only) */ +int etp_rx_off(unsigned device, unsigned interface); + +/* Change settings of an interface. */ +int etp_if_settings(unsigned device, + unsigned interface, + uint32_t clock_source, + unsigned hdlc_mode, /* HDLC_MODE_XXX */ + uint32_t hdlc_mode_g704_used_timeslots); + +/* Det output clock source. */ +int etp_ext_output_clock(unsigned device, uint32_t clock_source); + +/* Fine tune local clock frequency */ +int etp_nco_adjust(unsigned device, uint32_t nco_addend_value); + +extern unsigned etp_number; +/* Ask the number of devices installed in the system. */ +static inline unsigned etp_number_devices(void) +{ + return etp_number; +} + +/* Gets the current settings and status of a device */ +int etp_device_status_get(unsigned device, + struct etp_device_status_struct *device_status); + +int etp_interface_status_get(unsigned device, unsigned interface, + struct etp_interface_status_struct *status); + +extern uint32_t etp_rx_on_get(const struct etp_channel_private *cp); + +extern uint32_t etp_tx_on_get(const struct etp_channel_private *cp); + +void etp_down(struct etp_device_private *device); + +void etp_up(struct etp_device_private *device); + +/* returns IDT register address offset of a card's span or -ENXIO on error. */ +int etp_idt_offset(unsigned card_number, unsigned span); +#endif --- linux/drivers/net/wan/etp_main.c 1970-01-01 02:00:00.000000000 +0200 +++ linux-next/drivers/net/wan/etp_main.c 2008-10-06 10:55:40.654426735 +0300 @@ -0,0 +1,2491 @@ +/* etp_main.c */ + +/* + Copyright (C) 2006 Jouni Kujala, Flexibilis Oy. + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + All the drivers derived from or based on this code must retain + the copyright notice. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* ip_fast_csum */ +#include +#include + +#include "etp.h" +#include "etp_idt.h" + +MODULE_VERSION("0.7.68"); + +/* PCI IO size */ +#define ETP_SIZE 0x20000 + +enum { ETP_MRU = 1800u, ETP_DMA = ETP_MRU + 2u }; + +enum { ETP_ON = 0, ETP_OFF = 1 }; + +enum { ETP_INTERRUPT_NONE = 0, ETP_INTERRUPT = 1 }; + +static struct pci_device_id etp_pci_tbl[] __devinitdata = { + {0x10EE, PCI_DEVICE_ID_ETP_ORIGINAL, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + /* etp-104 (1a2b:000a) */ + {0x1A2B, PCI_DEVICE_ID_ETP_104, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0,} +}; + +MODULE_DESCRIPTION("ETP"); +MODULE_AUTHOR("Jouni Kujala"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, etp_pci_tbl); + +/* Global variables (common to whole driver, all the devices) */ +static int major; /* Character device major number */ +struct etp_device_private **etp_devices; +EXPORT_SYMBOL(etp_devices); +unsigned int etp_number; /* The number of the devices found. */ +EXPORT_SYMBOL(etp_number); +static const char etp_netdev_name[] = "e1_xx"; + +/* Functions */ + +static int etp_char_open(struct inode *inode, struct file *filp); +static int etp_char_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + +static void etp_enable_interrupt(struct etp_device_private *dp); + +static struct file_operations etp_char_fops = { + .owner = THIS_MODULE, + .ioctl = etp_char_ioctl, + .open = etp_char_open +}; + +static inline void etp_unregister_char_device(void) +{ + unregister_chrdev(major, THIS_MODULE->name); +} + +static inline int etp_register_char_device(void) +{ + int error = register_chrdev(0u /* dynamic */, THIS_MODULE->name, + &etp_char_fops); + if (unlikely(error < 0)) { + printk(KERN_WARNING + "%s: unable to register char device\n", + THIS_MODULE->name); + } + return error; +} + +static irqreturn_t etp_interrupt(int irq, void *device); +static int etp_change_mtu(struct net_device *dev, int mtu); +static void etp_netdev_tx_timeout(struct net_device *dev); +static int etp_netdev_open(struct net_device *dev); +static int etp_netdev_start_xmit(struct sk_buff *skb, struct net_device *dev); +static int etp_netdev_close(struct net_device *dev); +static void etp_netdev_close_down(struct net_device *dev, + struct etp_channel_private *cp, + struct etp_interface_private *ip, + struct etp_device_private *dp); +static int etp_netdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); +static int etp_netdev_attach(struct net_device *dev, unsigned short encoding, + unsigned short parity); +static int etp_if_settings_down(struct etp_device_private *dp, + struct etp_interface_private *ip, + uint32_t clock_source, + unsigned hdlc_mode, + uint32_t hdlc_mode_g704_used_timeslots); + +static void status_work(struct work_struct *work); +static void led_work(struct work_struct *work); +static int etp_tx_on_down(struct etp_interface_private *ip); +static int etp_rx_on_down(struct etp_interface_private *ip); +static int etp_rx_off_down(struct etp_interface_private *ip); +static int etp_tx_off_down(struct etp_interface_private *ip); +static int etp_if_close_down(unsigned interface, + struct etp_device_private *dp, + struct etp_interface_private *ip); +static void rx_task_stream_timeslot(unsigned long channel); + +static unsigned if_to_led(unsigned interface) +{ + if (interface < 4u) + return interface << 1; + else + return ((interface - 4u) << 1) + 1u; +} + +static void set_led(uint32_t new_value, struct etp_interface_private *ip, + unsigned interface, struct etp_device_private *dp) +{ + uint8_t __iomem *ioaddr = dp->ioaddr; + /* The idea here is that we do not need to read the old value from + device because we know what we have written there. */ + uint32_t old = dp->led_register_value; + uint32_t temp = old; /* LED value temp */ + /* reset bits */ + temp &= ~(ALL_LED_BITS << LEDx_SHIFT(if_to_led(interface))); + /* write new value */ + temp |= new_value << LEDx_SHIFT(if_to_led(interface)); + /* write bits */ + if (old != temp) { + writel(temp, ioaddr + REG_LED_CTRL); + if (new_value) { + cancel_delayed_work(&dp->led); + queue_delayed_work(dp->queue, &dp->led, 5ul * HZ); + } + } + dp->led_register_value = temp; +} + +unsigned int get_led(const struct etp_interface_private *ip) +{ + struct etp_device_private *dp = this_dev_priv(ip); + unsigned int interface = interface_number(ip); + return (dp->led_register_value >> LEDx_SHIFT(if_to_led(interface))) & + ALL_LED_BITS; +} + +static int __devinit etp_init_device(struct pci_dev *pdev, + const struct pci_device_id *ent); + +#ifdef CONFIG_PM +static int etp_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct etp_device_private *dp = pci_get_drvdata(pdev); + unsigned channel; + struct mutex *device_mutex = &dp->mutex; + cancel_delayed_work(&dp->led); + etp_down(dp); + channel = 0u; + do { + struct etp_interface_private *ip = dp->interface_privates + + channel; + const unsigned mode = ip->if_mode; + struct etp_channel_private *cp = &ip->ch_priv; + if (mode >= IF_MODE_TIMESLOT) { + rtnl_lock(); + etp_if_close_down(channel, dp, ip); + rtnl_unlock(); + } else if (mode != IF_MODE_CLOSED) { + struct net_device *dev = cp->this_netdev; + if (dev) { + netif_device_detach(dev); + rtnl_lock(); + etp_netdev_close_down(dev, cp, ip, dp); + rtnl_unlock(); + } + } + rtnl_lock(); + ip->if_mode = mode; + rtnl_unlock(); + } while (channel++ < INTERFACES_PER_DEVICE - 1u); + mutex_lock(device_mutex); + atomic_set(&dp->reset, ETP_OFF); + mutex_unlock(device_mutex); + etp_up(dp); + flush_workqueue(dp->queue); + /* Set E1 and access done interrupts disabled. */ + writel(dp->reg_int_mask2 = 0u, dp->ioaddr + REG_INT_MASK2); + /* Disable IRQ. */ + free_irq(pdev->irq, dp); + pci_save_state(pdev); + /* Disable IO/bus master/IRQ router. */ + pci_disable_device(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + return 0; +} + +static int etp_resume(struct pci_dev *pdev) +{ + struct etp_device_private *dp = pci_get_drvdata(pdev); + unsigned channel; + int error; + unsigned irq; + struct etp_interface_private *interfaces; + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + /* device's irq possibly is changed, driver should take care */ + error = pci_enable_device(pdev); + if (unlikely(error)) + return error; + pci_set_master(pdev); + /* driver specific operations */ + msleep(2u); /* IDT chip reset timeout. */ + irq = pdev->irq; + error = request_irq(irq, &etp_interrupt, IRQF_SHARED, THIS_MODULE->name, + dp); + if (unlikely(error)) + return error; + atomic_set(&dp->reset, ETP_ON); + /* Set default settings to E1 chip (IDT). */ + idt_init_default(dp); + etp_enable_interrupt(dp); + channel = 0u; + interfaces = dp->interface_privates; + do { + struct etp_interface_private *ip = interfaces + channel; + struct etp_channel_private *cp = &ip->ch_priv; + struct net_device *dev = cp->this_netdev; + if (likely(dev)) { + dev->irq = irq; + if (netif_running(dev)) { + rtnl_lock(); + etp_netdev_open(dev); + rtnl_unlock(); + } + netif_device_attach(dev); + } + } while (channel++ < INTERFACES_PER_DEVICE - 1u); + return error; +} +#endif + +static void __devexit etp_remove_device(struct pci_dev *pdev); + +static struct pci_driver etp_driver = { + .name = THIS_MODULE->name, + .id_table = etp_pci_tbl, + .probe = etp_init_device, + .remove = etp_remove_device, +#ifdef CONFIG_PM + .suspend = etp_suspend, + .resume = etp_resume +#endif +}; + +static int __init etp_init(void) +{ + int ret = pci_register_driver(&etp_driver); + if (unlikely(ret)) + return ret; + major = etp_register_char_device(); + if (unlikely(major < 0)) { + pci_unregister_driver(&etp_driver); + return major; + } + return ret; +} + +static void __exit etp_cleanup(void) +{ + unsigned device; + pci_unregister_driver(&etp_driver); + etp_unregister_char_device(); + for (device = 0u; device < etp_number; device++) { + struct etp_device_private *card = etp_devices[device]; + struct workqueue_struct *queue = card->queue; + if (queue) + destroy_workqueue(queue); + kfree(card); + } + kfree(etp_devices); +} + +module_init(etp_init); +module_exit(etp_cleanup); + +static int etp_poll(struct napi_struct *napi, int weight); + +/* Callback functions that do nothing: */ +static void rx_null_callback(unsigned device, unsigned interface, + unsigned can_be, const struct slot_struct *rx) +{ +} + +static void tx_null_callback(unsigned device, unsigned interface, + unsigned can_be, struct slot_struct *tx) +{ +} + +static int etp_init_netdev(struct etp_channel_private *cp, int hdlc_mode) +{ + struct net_device *netdev; + unsigned int interface = CH_TO_IF(cp->channel_number); + unsigned int device = cp->device_number; + struct etp_device_private *dp = this_device_priv(cp); + + if (hdlc_mode <= HDLC_MODE_CISCO_OVER_G704) { + netdev = alloc_hdlcdev(cp); + if (unlikely(!netdev)) + goto NO_MEMORY; + /* name := xxx00..xxxnn */ + memcpy(netdev->name, etp_netdev_name, 6u); + } else { + netdev = alloc_etherdev(sizeof(struct hdlc_device)); + if (unlikely(!netdev)) { +NO_MEMORY: dev_err(&dp->pci_dev->dev, + "cannot allocate net device\n"); + return -ENOMEM; + } + dev_to_hdlc(netdev)->priv = cp; + + /* name := xxx00..xxxnn */ + memcpy(netdev->name, etp_netdev_name, 6u); + + ether_setup(netdev); + random_ether_addr(netdev->dev_addr); + } + netdev->name[4] = /* number -> ascii */ + ((device * INTERFACES_PER_DEVICE + interface) % 10u) + 0x30; + netdev->name[3] = /* number -> ascii */ + ((device * INTERFACES_PER_DEVICE + interface) / 10u) + 0x30; + netdev->base_addr = (unsigned long)dp->ioaddr; + netdev->irq = dp->pci_dev->irq; + + /* The FEPCI specific entries in the network device structure. */ + netdev->open = &etp_netdev_open; + netdev->hard_start_xmit = &etp_netdev_start_xmit; + netdev->stop = &etp_netdev_close; + netdev->change_mtu = &etp_change_mtu; + netdev->tx_timeout = etp_netdev_tx_timeout; + netdev->watchdog_timeo = TX_TIMEOUT; + netif_napi_add(netdev, &cp->napi, etp_poll, DESCRIPTORS_PER_CHANNEL); + netdev->do_ioctl = etp_netdev_ioctl; + cp->hdlc_mode = hdlc_mode; + + switch (hdlc_mode) { + case HDLC_MODE_CISCO_OVER_G703: + case HDLC_MODE_CISCO_OVER_G704: + { + struct hdlc_device *hdlc = dev_to_hdlc(netdev); + hdlc->attach = etp_netdev_attach; + hdlc->xmit = etp_netdev_start_xmit; + } + break; + case HDLC_MODE_RETINA_OVER_G703_POINTOPOINT: + case HDLC_MODE_RETINA_OVER_G704_POINTOPOINT: + netdev->flags |= (IFF_POINTOPOINT); /* Point-to-point link. */ + netdev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST); + } + SET_NETDEV_DEV(netdev, &dp->pci_dev->dev); + cp->this_netdev = netdev; + if (cp->hdlc_mode <= HDLC_MODE_CISCO_OVER_G704) + return register_hdlc_device(netdev); + else + return register_netdev(netdev); +} + +static int etp_netdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct etp_channel_private *cp = + ((struct hdlc_device *)(dev_to_hdlc(dev)))->priv; + struct etp_interface_private *ip = this_if_priv(cp); + te1_settings line; + const size_t size = sizeof(line); + + if (cmd != SIOCWANDEV && cp->hdlc_mode <= HDLC_MODE_CISCO_OVER_G704) + return hdlc_ioctl(dev, ifr, cmd); + + switch (ifr->ifr_settings.type) { + case IF_GET_IFACE: + { + ifr->ifr_settings.type = IF_IFACE_E1; + if (ifr->ifr_settings.size < size) { + ifr->ifr_settings.size = size; /* data size wanted */ + return -ENOBUFS; + } + switch (ip->tx_clock_source) { + case CLOCK_SOURCE_NCO: + line.clock_type = CLOCK_TXINT; + break; + default: + line.clock_type = CLOCK_TXFROMRX; + } + line.loopback = 0u; + line.slot_map = (cp->hdlc_mode & 1u) ? + cp->hdlc_mode_g704_used_timeslots : 0xffff; + + if (copy_to_user(ifr->ifr_settings.ifs_ifsu.te1, &line, size)) + return -EFAULT; + return 0; + } + case IF_IFACE_E1: + { + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (dev->flags & IFF_UP) + return -EBUSY; + + if (copy_from_user(&line, ifr->ifr_settings.ifs_ifsu.te1, + size)) + return -EFAULT; + + if (line.clock_type == CLOCK_INT) + return -EINVAL; + + if (line.loopback) + return -EINVAL; + + return etp_if_settings_down(cp->this_dev_priv, ip, + line.clock_type == CLOCK_TXINT || + line.clock_type == CLOCK_DEFAULT ? + CLOCK_SOURCE_NCO : + CLOCK_SOURCE_RX(cp->channel_number), + cp->hdlc_mode, line.slot_map); + } + default: + if (cp->hdlc_mode <= HDLC_MODE_CISCO_OVER_G704) + return hdlc_ioctl(dev, ifr, cmd); + else + return -EBUSY; + } +} + +static int etp_netdev_attach(struct net_device *dev, unsigned short encoding, + unsigned short parity) +{ + return parity != PARITY_DEFAULT && + parity != PARITY_CRC16_PR0_CCITT && + parity != PARITY_CRC16_PR1_CCITT ? -EINVAL : 0; +} + +static void etp_free_netdev(struct etp_channel_private *cp) +{ + struct net_device *device = cp->this_netdev; + if (unlikely(device == NULL)) + return; + cp->this_netdev = NULL; + if (device->reg_state == NETREG_REGISTERED) { + if (cp->hdlc_mode <= HDLC_MODE_CISCO_OVER_G704) + unregister_hdlc_device(device); + else + unregister_netdev(device); + } + synchronize_irq(device->irq); + free_netdev(device); +} + +static void etp_init_channel(struct etp_channel_private *cp, + struct etp_interface_private *ip, + uint8_t __iomem *ioaddr) +{ + unsigned int descriptor; + unsigned int interface = interface_number(ip); + + cp->reg_ch_rxctrl = (uint32_t __iomem *) + (ioaddr + REG_RXCTRL_CH(IF_TO_CH(interface))); + cp->reg_ch_txctrl = (uint32_t __iomem *) + (ioaddr + REG_TXCTRL_CH(IF_TO_CH(interface))); + for (descriptor = 0u; descriptor < DESCRIPTORS_PER_CHANNEL; + descriptor++) { + struct rx_descriptor *rx = &cp->rx_descriptor[descriptor]; + struct tx_descriptor *tx = &cp->tx_descriptor[descriptor]; + /* Initialize descriptor pointers. */ + rx->descriptor = (struct rxdesc __iomem *) + (ioaddr + REG_RXDESCxA_CHy(descriptor, + IF_TO_CH + (interface))); + tx->descriptor = (struct txdesc __iomem *) + (ioaddr + REG_TXDESCxA_CHy(descriptor, + IF_TO_CH + (interface))); + rx->skb = NULL; + tx->skb = NULL; + } + + if (unlikely(etp_init_netdev(cp, /* HDLC mode to default */ + HDLC_MODE_RETINA_OVER_G703_POINTOPOINT))) + etp_free_netdev(cp); +} + +/* Fine tune local clock frequency. */ +static void etp_nco_adjust_down(struct etp_device_private *dp, + uint32_t nco_addend_value) +{ + writel(nco_addend_value, dp->ioaddr + REG_NCO_CTRL); +} + +/* Set output clock source.*/ +static int etp_ext_output_clock_down(struct etp_device_private *dp, + uint32_t clock_source) +{ + switch (clock_source) { + case CLOCK_SELECT_E1_GEN: /* for testing only */ + case CLOCK_SOURCE_NCO: + case CLOCK_SOURCE_DALLAS: + case CLOCK_SOURCE_RJ: + case CLOCK_SOURCE_LVDS: + writel((clock_source << OUTPUT_CLK_SELECT_SHIFT) | + (~OUTPUT_CLK_SELECT_MASK & + readl_relaxed(dp->ioaddr + REG_GENERAL)), + dp->ioaddr + REG_GENERAL); + return 0; + case CLOCK_SOURCE_RX0: + case CLOCK_SOURCE_RX1: + case CLOCK_SOURCE_RX2: + case CLOCK_SOURCE_RX3: + case CLOCK_SOURCE_RX4: + case CLOCK_SOURCE_RX5: + case CLOCK_SOURCE_RX6: + case CLOCK_SOURCE_RX7: + { + int error = idt_set_ref_clk(dp, + CLOCK_SELECT_RX_TO_CH + (clock_source)); + if (unlikely(error)) + return error; + writel((CLOCK_SELECT_E1_A << OUTPUT_CLK_SELECT_SHIFT) | + (~OUTPUT_CLK_SELECT_MASK & + readl_relaxed(dp->ioaddr + REG_GENERAL)), + dp->ioaddr + REG_GENERAL); + return 0; + } + default: + dev_warn(&dp->pci_dev->dev, "Invalid clock source 0x%x\n", + clock_source); + return -EINVAL; + } +} + +/* Change settings of an interface. */ +static int etp_if_settings_down(struct etp_device_private *dp, + struct etp_interface_private *ip, + uint32_t clock_source, + unsigned hdlc_mode, + uint32_t hdlc_mode_g704_used_timeslots) +{ + struct etp_channel_private *cp = &ip->ch_priv; + struct net_device *netdev = cp->this_netdev; + if (cp->hdlc_mode != hdlc_mode) { + switch (ip->if_mode) { + case IF_MODE_CLOSED: { + int error; + etp_free_netdev(cp); + error = etp_init_netdev(cp, hdlc_mode); + if (unlikely(error)) { + etp_free_netdev(cp); + return error; + } + break; + } + default: + dev_warn(&netdev->dev, + "Interface open: cannot change HDLC mode\n"); + return -EBUSY; + } + } + switch (clock_source) { + case CLOCK_SOURCE_NCO: + case CLOCK_SOURCE_DALLAS: + case CLOCK_SOURCE_RJ: + case CLOCK_SOURCE_LVDS: + case CLOCK_SELECT_E1_GEN: /* for testing only */ + case CLOCK_SOURCE_RX0: + case CLOCK_SOURCE_RX1: + case CLOCK_SOURCE_RX2: + case CLOCK_SOURCE_RX3: + case CLOCK_SOURCE_RX4: + case CLOCK_SOURCE_RX5: + case CLOCK_SOURCE_RX6: + case CLOCK_SOURCE_RX7: + if (ip->tx_clock_source != clock_source) { + if (unlikely(ip->if_mode != IF_MODE_CLOSED)) { + dev_warn(&netdev->dev, "Interface open: " + "cannot change clocking\n"); + return -EBUSY; + } + ip->tx_clock_source = clock_source; + } + break; + default: + if (netdev) + dev_warn(&netdev->dev, + "Invalid clock source 0x%x\n", clock_source); + return -EINVAL; + } + if (unlikely(hdlc_mode_g704_used_timeslots & 0x1)) { /* sync channel */ + if (netdev) + dev_warn(&netdev->dev, + "Cannot use channel 0 for data in G.704\n"); + return -EINVAL; + } + cp->hdlc_mode_g704_used_timeslots = hdlc_mode_g704_used_timeslots; + if (ip->if_mode == IF_MODE_HDLC && (cp->hdlc_mode & 1u)) { /* G.704 */ + int error; + if (likely(!atomic_read(&dp->reset))) { + writel(~hdlc_mode_g704_used_timeslots, + ip->reg_if_rxctrl1); + writel(~hdlc_mode_g704_used_timeslots, + ip->reg_if_txctrl1); + error = 0; + } else { + error = -ENXIO; + } + return error; + } + return 0; +} + +void etp_down(struct etp_device_private *device) +{ + unsigned interface = 0u; + do { + down_write(&device->interface_privates[interface].semaphore); + } while (++interface <= INTERFACES_PER_DEVICE - 1); +} + +void etp_up(struct etp_device_private *device) +{ + unsigned interface = 0u; + do { + up_write(&device->interface_privates[interface].semaphore); + } while (++interface <= INTERFACES_PER_DEVICE - 1); +} + +static int __devinit etp_init_device(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int i; + uint8_t __iomem *ioaddr; + unsigned int device; + unsigned int interface; + struct etp_device_private *card, **cards, **old; + struct etp_interface_private *interfaces; + + for (device = 0u; device < etp_number; device++) { + card = etp_devices[device]; + if (card->pci_dev == NULL) + goto ENABLE; + } + if (unlikely(etp_number == 256u)) + return -ENOMEM; + card = kzalloc(sizeof(struct etp_device_private), GFP_KERNEL); + if (unlikely(card == NULL)) + return -ENOMEM; + cards = kmalloc((etp_number + 1u) * sizeof(struct etp_device_private *), + GFP_KERNEL); + if (unlikely(cards == NULL)) { + kfree(card); + return -ENOMEM; + } + for (i = 0u; i < device; i++) + cards[i] = etp_devices[i]; + cards[i] = card; + interfaces = card->interface_privates; + interface = 0u; + card->number = device; + do { + struct etp_interface_private *ip = interfaces + interface; + struct etp_channel_private *cp = &ip->ch_priv; + init_rwsem(&ip->semaphore); + cp->channel_number = IF_TO_CH(interface); + cp->device_number = device; + cp->this_dev_priv = card; + atomic_set(&cp->owner, ETP_CALLBACKS); + cp->rx_callback = rx_null_callback; + cp->tx_callback = tx_null_callback; +#if ETP_TIMER + init_timer(&cp->timer); + cp->timer.function = rx_task_stream_timeslot; + cp->timer.data = (unsigned long)cp; +#endif + } while (interface++ < INTERFACES_PER_DEVICE - 1u); + mutex_init(&card->mutex); + mutex_init(&card->idt); + spin_lock_init(&card->lock0); + spin_lock_init(&card->lock2); + INIT_WORK(&card->status_work, status_work); + INIT_DELAYED_WORK(&card->led, led_work); + atomic_set(&card->reset, ETP_OFF); + old = etp_devices; + rcu_assign_pointer(etp_devices, cards); + synchronize_rcu(); + kfree(old); + etp_number++; +ENABLE: i = pci_enable_device(pdev); + if (unlikely(i)) { + dev_warn(&pdev->dev, "enabling device failed\n"); + return i; + } + + pci_set_master(pdev); + + i = pci_request_regions(pdev, THIS_MODULE->name); + if (unlikely(i)) { + dev_warn(&pdev->dev, "requesting regions failed\n"); + pci_disable_device(pdev); + return i; + } + + if (unlikely(pci_set_dma_mask(pdev, DMA_32BIT_MASK))) { + dev_warn(&pdev->dev, "no suitable DMA available\n"); + i = -ENOMEM; + goto ERROR; + } + + if (unlikely(!(pci_resource_flags(pdev, 0u) & IORESOURCE_MEM))) { + i = -ENXIO; + goto ERROR; + } + if (unlikely(pci_resource_len(pdev, 0u) < ETP_SIZE)) { + dev_warn(&pdev->dev, "resource length less than required %u\n", + ETP_SIZE); + i = -ENXIO; + goto ERROR; + } + ioaddr = pci_iomap(pdev, 0u, ETP_SIZE); + if (unlikely(ioaddr == NULL)) { + dev_warn(&pdev->dev, "mapping failed\n"); + i = -ENOMEM; + goto ERROR; + } + card->pci_dev = pdev; + card->ioaddr = ioaddr; + + /* All LEDs on. */ + writel(0x5555, ioaddr + REG_LED_CTRL); + + /* E1 reset. */ + writel(E1_RESET_ENABLE | readl_relaxed(ioaddr + REG_GENERAL), + ioaddr + REG_GENERAL); + writel(~E1_RESET_ENABLE & readl_relaxed(ioaddr + REG_GENERAL), + ioaddr + REG_GENERAL); + readl_relaxed(ioaddr + REG_GENERAL); /* Wait for reset enable off. */ + /* Wait after hardware reset: should be at least 2 milliseconds. */ + msleep(2u); + + pci_set_drvdata(pdev, card); + + /* Enable LVDS. */ + writel(LVDS_ENABLE | readl_relaxed(ioaddr + REG_GENERAL), + ioaddr + REG_GENERAL); + + interfaces = card->interface_privates; + for (interface = 0u; interface < INTERFACES_PER_DEVICE; interface++) { + struct etp_interface_private *ip = interfaces + interface; + struct etp_channel_private *cp = &ip->ch_priv; + /* Initialize register pointers. */ + ip->reg_if_rxctrl = (uint32_t __iomem *) + (ioaddr + REG_RXCTRL_IF(interface)); + ip->reg_if_txctrl = (uint32_t __iomem *) + (ioaddr + REG_TXCTRL_IF(interface)); + ip->reg_if_rxctrl1 = (uint32_t __iomem *) + (ioaddr + REG_RXCTRL1_IF(interface)); + ip->reg_if_txctrl1 = (uint32_t __iomem *) + (ioaddr + REG_TXCTRL1_IF(interface)); + + etp_init_channel(cp, ip, ioaddr); + + /* Set interface clock setting to local (NCO) clock... */ + etp_if_settings_down(card, ip, CLOCK_SOURCE_NCO, cp->hdlc_mode, + 0u /* no timeslots used in G.704 */); + + /* ...but do not enable the clock output at the FPGA */ + writel((CLOCK_SELECT_NO_CLOCK << TX_CLOCK_SELECT_SHIFT) | + (~TX_CLOCK_SELECT_MASK & + readl_relaxed(ip->reg_if_txctrl)), ip->reg_if_txctrl); + } + + /* all LEDs off */ + writel(0x0, ioaddr + REG_LED_CTRL); + + /* set NCO value */ + etp_nco_adjust_down(card, NCO_ADDEND_DEFAULT_VALUE); + + /* Set output clock to local. */ + etp_ext_output_clock_down(card, CLOCK_SELECT_LOCAL); + + if (likely(card->queue == NULL)) { + struct workqueue_struct *queue = + create_singlethread_workqueue(THIS_MODULE->name); + if (unlikely(queue == NULL)) { + i = -ENOMEM; + goto CLEANUP; + } + card->queue = queue; + } + + etp_down(card); + atomic_set(&card->reset, ETP_ON); + /* Default settings to E1 chip (IDT). */ + idt_init_default(card); + + /* Set interface closed at IDT chip. */ + for (interface = 0u; interface < INTERFACES_PER_DEVICE; interface++) + idt_close_if(card, interface); + + /* Register interrupt handler. */ + i = request_irq(pdev->irq, &etp_interrupt, IRQF_SHARED, + THIS_MODULE->name, card); + if (unlikely(i)) { + atomic_set(&card->reset, ETP_OFF); + etp_up(card); +CLEANUP: + card->pci_dev = NULL; + iounmap(ioaddr); + goto ERROR; + } + + etp_enable_interrupt(card); + etp_up(card); + + return 0; + +ERROR: + pci_set_drvdata(pdev, NULL); + pci_disable_device(pdev); + pci_release_regions(pdev); + return i; +} + +static void __devexit etp_remove_device(struct pci_dev *pdev) +{ + unsigned int i; + struct etp_device_private *dp = pci_get_drvdata(pdev); + struct etp_interface_private *interfaces; + uint8_t __iomem *ioaddr = dp->ioaddr; + const unsigned device = device_number(dp); + + etp_down(dp); + mutex_lock(&dp->mutex); + atomic_set(&dp->reset, ETP_OFF); + mutex_unlock(&dp->mutex); + etp_up(dp); + + interfaces = dp->interface_privates; + for (i = 0u; i < INTERFACES_PER_DEVICE; i++) { + struct etp_interface_private *ip = &(interfaces[i]); + switch (ip->if_mode) { + case IF_MODE_HDLC: + unregister_netdev(ip->ch_priv.this_netdev); + break; + case IF_MODE_TIMESLOT: + case IF_MODE_STREAM: + etp_if_close(device, i); + } + } + + /* Switch E1 and access done interrupts off. */ + writel(dp->reg_int_mask2 = 0u, ioaddr + REG_INT_MASK2); + + free_irq(pdev->irq, dp); + for (i = 0u; i < INTERFACES_PER_DEVICE; i++) { + struct etp_interface_private *ip = &(interfaces[i]); + etp_free_netdev(&ip->ch_priv); + } + + cancel_delayed_work(&dp->led); + + /* Switch all LEDs off. */ + writel(0x0, ioaddr + REG_LED_CTRL); + + /* Leave E1 in reset, LVDS disable. */ + writel(E1_RESET_ENABLE, ioaddr + REG_GENERAL); + + iounmap(ioaddr); + dp->pci_dev = NULL; + pci_set_drvdata(pdev, NULL); + + pci_disable_device(pdev); + pci_release_regions(pdev); +} + +static int etp_char_open(struct inode *inode, struct file *filp) +{ + unsigned int minor = MINOR(inode->i_rdev); + + /* If trying to access a device that has not been probed. */ + if (unlikely(minor >= etp_number)) + return -ENXIO; + filp->private_data = get_dev_priv(minor); + return 0; +} + +static int etp_char_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct etp_device_private *dp = filp->private_data; + unsigned char device = dp->number; + unsigned int interface; + int error = 0; + + if (unlikely((_IOC_DIR(cmd) & _IOC_WRITE) && + !access_ok(VERIFY_READ, (void __user *)arg, + _IOC_SIZE(cmd)))) + return -EFAULT; + + switch (cmd) { + case ETP_IOCTL_INTERFACE_OPEN: + { + struct etp_ioctl_open open_struct; + if (unlikely(__copy_from_user(&open_struct, + (void __user *)arg, + _IOC_SIZE(cmd)))) + return -EFAULT; + error = etp_if_open(device, open_struct.interface, + open_struct.if_mode, open_struct.rx_slots, + open_struct.tx_slots); + } + break; + case ETP_IOCTL_INTERFACE_CLOSE: + interface = arg; /* here arg == interface_number */ + error = etp_if_close(device, interface); + break; + case ETP_IOCTL_TX_ON: + interface = arg; /* here arg == interface_number */ + error = etp_tx_on(device, interface); + break; + case ETP_IOCTL_TX_OFF: + interface = arg; /* here arg == interface_number */ + error = etp_tx_off(device, interface); + break; + case ETP_IOCTL_RX_ON: + interface = arg; /* here arg == interface_number */ + error = etp_rx_on(device, interface); + break; + case ETP_IOCTL_RX_OFF: + interface = arg; /* here arg == interface_number */ + error = etp_rx_off(device, interface); + break; + case ETP_IOCTL_INTERFACE_SETTINGS: + { + struct etp_ioctl_interface_settings settings_struct; + if (unlikely(__copy_from_user + (&settings_struct, (void __user *)arg, + _IOC_SIZE(cmd)))) + return -EFAULT; + error = etp_if_settings + (device, settings_struct.interface, + settings_struct.tx_clock_source, + settings_struct.hdlc_mode, + settings_struct.hdlc_mode_g704_used_timeslots); + } + break; + case ETP_IOCTL_EXT_OUTPUT_CLOCK: + { + struct etp_ioctl_ext_output_clock clock_struct; + if (unlikely(__copy_from_user(&clock_struct, + (void __user *)arg, + _IOC_SIZE(cmd)))) + return -EFAULT; + error = etp_ext_output_clock + (device, clock_struct.clock_source); + } + break; + case ETP_IOCTL_NCO: + { + struct etp_ioctl_nco_adjust nco_struct; + if (unlikely(__copy_from_user(&nco_struct, + (void __user *)arg, + _IOC_SIZE(cmd)))) + return -EFAULT; + error = etp_nco_adjust + (device, nco_struct.nco_addend_value); + } + break; + case ETP_IOCTL_DEVICE_STATUS_GET: + { + struct etp_device_status_struct status_struct; + error = etp_device_status_get(device, &status_struct); + if (unlikely(error)) + break; + if (unlikely(__copy_to_user((void __user *)arg, + &status_struct, + _IOC_SIZE(cmd)))) + return -EFAULT; + } + break; + case ETP_IOCTL_INTERFACE_STATUS_GET: + { + struct etp_interface_status_struct status_struct; + if (unlikely(__copy_from_user(&status_struct, + (void __user *)arg, + _IOC_SIZE(cmd)))) + return -EFAULT; + error = etp_interface_status_get + (device, status_struct.interface, &status_struct); + if (unlikely(error)) + break; + if (unlikely(__copy_to_user((void __user *)arg, + &status_struct, + _IOC_SIZE(cmd)))) + return -EFAULT; + } + break; + case ETP_IOCTL_E1_ACCESS: /* Read / write IDT chip. */ + if (unlikely(device >= etp_number)) { + return -ENXIO; + } else { + struct etp_ioctl_e1_access e1_struct; + if (unlikely(__copy_from_user(&e1_struct, + (void __user *)arg, + _IOC_SIZE(cmd)))) + return -EFAULT; + if (!e1_struct.write) { + e1_struct.data = etp_read_idt_register_lock( + device, + e1_struct. + address); + } else { /* write */ + error = etp_write_idt_register_lock(device, + e1_struct. + address, + e1_struct.data); + if (unlikely(error)) + break; + } + if (unlikely(__copy_to_user((void __user *)arg, + &e1_struct, + _IOC_SIZE(cmd)))) + return -EFAULT; + } + break; + case ETP_IOCTL_RXTX_NOSLEEP_POLL: + if (unlikely(device >= etp_number)) { + return -ENXIO; + } else { + struct etp_ioctl_buffer_poll poll_struct; + struct etp_interface_private *ip; + struct etp_channel_private *cp; + if (unlikely(__copy_from_user(&poll_struct, + (void __user *)arg, + _IOC_SIZE(cmd)))) + return -EFAULT; + ip = &dp->interface_privates[poll_struct.interface]; + cp = &ip->ch_priv; + poll_struct.rx_slot = cp->last_rx_slot_received; + poll_struct.tx_slot = cp->last_tx_slot_transmitted; + if (unlikely(__copy_to_user((void __user *)arg, + &poll_struct, + _IOC_SIZE(cmd)))) + return -EFAULT; + } + break; + default: + return -ENOTTY; + } + return error; +} + +static inline void etp_disable_interrupt0(struct etp_device_private *dp, + unsigned channel_number, + uint8_t __iomem *ioaddr) +{ + spinlock_t *lock = &dp->lock0; + spin_lock(lock); + writel(dp->reg_int_mask0 &= ~(CH_ALLINTS_MASK << + INT_0_BIT_SHIFT_CH(channel_number)), + ioaddr + REG_INT_MASK0); + mmiowb(); + spin_unlock(lock); +} + +static void etp_disable_interrupt0_irq(struct etp_device_private *dp, + unsigned channel_number, + uint8_t __iomem *ioaddr) +{ + local_irq_disable(); + etp_disable_interrupt0(dp, channel_number, ioaddr); + local_irq_enable(); +} + +static inline void etp_schedule(struct etp_channel_private *cp, + struct etp_device_private *dp, + unsigned interface, + uint8_t __iomem *ioaddr) +{ + struct napi_struct *napi = &cp->napi; + cp->interrupt = true; + if (napi_schedule_prep(napi)) { + etp_disable_interrupt0(dp, IF_TO_CH(interface), ioaddr); + __napi_schedule(napi); + } +} + +static inline bool etp_disable_interrupt2(struct etp_device_private *dp, + uint8_t __iomem *ioaddr) +{ + spinlock_t *lock = &dp->lock2; + bool disable; + spin_lock(lock); + if (dp->reg_int_mask2 & INT_2_E1_INT) { + writel(dp->reg_int_mask2 &= ~INT_2_E1_INT, + ioaddr + REG_INT_MASK2); + mmiowb(); + disable = true; + } else { + disable = false; + } + spin_unlock(lock); + return disable; +} + + +static inline void queue_status(struct etp_device_private *dp, + uint8_t __iomem *ioaddr) +{ + atomic_set(&dp->interrupt, ETP_INTERRUPT); + if (etp_disable_interrupt2(dp, ioaddr)) + queue_work(dp->queue, &dp->status_work); +} + +static +void queue_status_work(struct etp_device_private *dp, uint8_t __iomem *ioaddr) +{ + local_irq_disable(); + queue_status(dp, ioaddr); + local_irq_enable(); +} + +/* Interrupt handler. */ +static irqreturn_t etp_interrupt(int irq, void *device) +{ + struct etp_device_private *dp = (struct etp_device_private *)device; + unsigned int interface; + uint8_t __iomem *ioaddr = dp->ioaddr; + irqreturn_t irqreturn; + /* Get interrupt status */ + uint32_t intr_status_0 = readl(ioaddr + REG_INT_STAT0); + uint32_t intr_status_2 = readl_relaxed(ioaddr + REG_INT_STAT2); + struct etp_interface_private *interfaces; + /* Clear interrupts (only those visible in status, + not those that happened after reading status) */ + if (intr_status_0) { + writel(~intr_status_0, ioaddr + REG_INT_STAT0); + irqreturn = IRQ_HANDLED; + } else { + irqreturn = IRQ_NONE; + } + if (intr_status_2) { + writel(~intr_status_2, ioaddr + REG_INT_STAT2); + irqreturn = IRQ_HANDLED; + } + /* Check interrupts for each channel. */ + interfaces = dp->interface_privates; + interface = INTERFACES_PER_DEVICE - 1u; + do { + uint32_t ch_intr_status = + (intr_status_0 >> + INT_0_BIT_SHIFT_CH(IF_TO_CH(interface))) + & CH_ALLINTS_MASK; + if (ch_intr_status & + (INT_RECEIVED | INT_RX_DROPPED | INT_TRANSMITTED)) { + struct etp_channel_private *cp = + &interfaces[interface].ch_priv; + if (cp->this_netdev) + etp_schedule(cp, dp, interface, ioaddr); + } + } while (interface--); + if (intr_status_2 & INT_2_E1_INT) + queue_status(dp, ioaddr); + return irqreturn; +} + +/* Returns zero on success; non-zero on error. */ +static inline bool etp_update_rx_descriptor_statistics_netdev(struct + net_device_stats + *netdev_stats, + uint32_t desc_b, + uint32_t length) +{ + if (unlikely(length <= 2u || length > ETP_DMA)) { + netdev_stats->rx_length_errors++; + netdev_stats->rx_errors++; + return true; + } + if (unlikely(desc_b & (RX_DESCB_FIFO_ERR | RX_DESCB_SIZE_ERR | + RX_DESCB_CRC_ERR | RX_DESCB_OCTET_ERR))) { + if (desc_b & RX_DESCB_FIFO_ERR) + netdev_stats->rx_fifo_errors++; + else if (desc_b & RX_DESCB_SIZE_ERR) + netdev_stats->rx_over_errors++; + else if (desc_b & RX_DESCB_CRC_ERR) + netdev_stats->rx_crc_errors++; + else + netdev_stats->rx_frame_errors++; + netdev_stats->rx_errors++; + return true; + } else { /* OK, no error. */ + netdev_stats->rx_bytes += length; + netdev_stats->rx_packets++; + return false; + } +} + +static inline void etp_update_tx_descriptor_statistics_netdev(struct + net_device_stats + *netdev_stats, + uint32_t desc_b, + uint32_t length) +{ + if (unlikely(desc_b & TX_DESCB_FIFO_ERR)) { + netdev_stats->tx_fifo_errors++; + } else { + netdev_stats->tx_packets++; + netdev_stats->tx_bytes += length; + } +} + +static inline int rx_task_hdlc(struct etp_channel_private *cp, int weight, + struct net_device *netdev, int poll) +{ + unsigned d = cp->last_rx_desc_received; + struct sk_buff *skb; + for (;;) { + struct rx_descriptor *rx = rx = cp->rx_descriptor + d; + struct rxdesc __iomem *rxdesc = rx->descriptor; + uint32_t descb = readl(&rxdesc->desc_b); + if (descb & RX_DESCB_TRANSFER) + break; + /* Transfer done. */ + skb = rx->skb; + if (likely(skb)) { + uint32_t length = descb & RX_DESCB_LENGT_MASK; + bool error = etp_update_rx_descriptor_statistics_netdev( + &netdev->stats, + descb, length); + if (unlikely(error)) { + /* If error, reuse old skbuff. */ + writel(RX_DESCB_TRANSFER, &rxdesc->desc_b); + goto NEXT; + } else { /* If no error. */ + if (unlikely(poll == weight)) + break; + pci_unmap_single(this_device_priv(cp)->pci_dev, + pci_unmap_addr(rx, address), + ETP_DMA, + PCI_DMA_FROMDEVICE); + if (cp->hdlc_mode < + HDLC_MODE_RETINA_OVER_G703) { + /* -2 is the CRC. */ + __skb_put(skb, length - 2u); + /* Select correct protocol. */ + skb->protocol = + hdlc_type_trans(skb, netdev); + } else { /* Retina ethernet mode. */ + __skb_put(skb, length); + /* Remove CALP header. */ + __skb_pull(skb, 2u); + skb->protocol = + eth_type_trans(skb, netdev); + } + if (likely(netdev->flags & IFF_POINTOPOINT)) { + /* Received is for us. */ + if (unlikely(netdev->flags & + IFF_NOARP)) { + /* NOARP applied -> destination MAC addresses bogus */ + if (skb->pkt_type == + PACKET_OTHERHOST) + skb->pkt_type = + PACKET_HOST; + } else { + /* NOARP not applied -> destination MAC addresses are broadcast */ + if (skb->pkt_type == + PACKET_BROADCAST) + skb->pkt_type = + PACKET_HOST; + } /* IFF_NOARP */ + } /* IFF_POINTOPOINT */ + netdev->last_rx = jiffies; + netif_receive_skb(skb); + poll++; + } + } + skb = netdev_alloc_skb(netdev, ETP_DMA + NET_IP_ALIGN); + if (likely(skb)) { + dma_addr_t bus_address; + skb_reserve(skb, NET_IP_ALIGN); + bus_address = + pci_map_single(this_device_priv(cp)->pci_dev, + skb->data, + ETP_DMA, + PCI_DMA_FROMDEVICE); + if (likely(!pci_dma_mapping_error(this_device_priv(cp)-> + pci_dev, + bus_address))) { + pci_unmap_addr_set(rx, address, bus_address); + rx->skb = skb; + writel(bus_address, &rxdesc->desc_a); + writel(RX_DESCB_TRANSFER, &rxdesc->desc_b); + skb->ip_summed = CHECKSUM_UNNECESSARY; + } else { + dev_kfree_skb_any(skb); + rx->skb = NULL; + dev_warn(&netdev->dev, + "failed to map DMA buffer\n"); + goto CHECK; + } +NEXT: + d++; + d &= DESCRIPTORS_PER_CHANNEL - 1u; + } else { + rx->skb = NULL; + dev_warn(&netdev->dev, "failed to allocate buffer\n"); +CHECK: + d++; + d &= DESCRIPTORS_PER_CHANNEL - 1u; + if (unlikely(d == cp->last_rx_desc_received)) + break; + } + } + cp->last_rx_desc_received = d; + return poll; +} + +static inline void tx_task_stream_timeslot(struct etp_channel_private *cp) +{ + /* Descriptor: start from the next descriptor to the last used. */ + unsigned char d = (cp->last_tx_desc_transmitted + 1u) & + (DESCRIPTORS_PER_CHANNEL - 1u); + const unsigned short slots = cp->tx_slots; + /* Go through all the descriptors consumed by the hardware. */ + uint32_t desc_b; + struct txdesc __iomem *txdesc; + while (((desc_b = + readl_relaxed(&(txdesc = cp->tx_descriptor[d].descriptor)-> + desc_b)) & TX_DESCB_TRANSFER) == 0u) { + /* Has been sent. */ + unsigned short slot = cp->last_tx_slot_transmitted + 1u; + dma_addr_t address; + etp_update_tx_descriptor_statistics_netdev( + &cp->this_netdev->stats, + desc_b, SLOT_SIZE); + cp->last_tx_desc_transmitted = d; + slot *= slot < slots; + cp->last_tx_slot_transmitted = slot; + address = slot + DESCRIPTORS_PER_CHANNEL; + address -= (address >= slots) * slots; + writel(cp->tx_address + (address << 8), &txdesc->desc_a); + writel((SLOT_SIZE & TX_DESCB_LENGT_MASK) + | TX_DESCB_TRANSFER, &txdesc->desc_b); + { + unsigned written = slot + 1u; + written *= written < slots; + cp->tx_callback(cp->device_number, + CH_TO_IF(cp->channel_number), written, cp->tx); + } + flush_write_buffers(); + d = (d + 1u) & (DESCRIPTORS_PER_CHANNEL - 1u); + } +#if ETP_TIMER + if (likely(this_if_priv(cp)->if_mode >= IF_MODE_TIMESLOT)) + mod_timer(&cp->timer, jiffies + max(1ul, HZ / 1000ul)); +#endif +} + +static void rx_task_stream_timeslot(unsigned long channel) +{ + struct etp_channel_private *cp = (struct etp_channel_private *)channel; + /* Start from the next descriptor to the last used. */ + unsigned char d = (cp->last_rx_desc_received + 1u) & + (DESCRIPTORS_PER_CHANNEL - 1u); + const unsigned short slots = cp->rx_slots; + uint32_t desc_b; + struct rxdesc __iomem *rxdesc; + /* Go through all the descriptors consumed by the hardware. */ + while (((desc_b = readl(&(rxdesc = cp->rx_descriptor[d].descriptor) + ->desc_b)) & RX_DESCB_TRANSFER) == 0u) { + /* Transfer done. */ + unsigned short slot = cp->last_rx_slot_received + 1u; + dma_addr_t address; + /* Update statistics. */ + etp_update_rx_descriptor_statistics_netdev( + &cp->this_netdev->stats, + desc_b, SLOT_SIZE); + /* update counters pointing to last received descriptors & slots + and increase last received descriptor counter */ + cp->last_rx_desc_received = d; + slot *= slot < slots; + cp->last_rx_slot_received = slot; + /* Move to next slot: initialize next descriptor and slot: */ + address = slot + DESCRIPTORS_PER_CHANNEL; + address -= (address >= slots) * slots; + writel(cp->rx_address + (address << 8), &rxdesc->desc_a); + writel(RX_DESCB_TRANSFER, &rxdesc->desc_b); + { + unsigned read = slot + 1; + read *= read < slots; + cp->rx_callback(cp->device_number, + CH_TO_IF(cp->channel_number), read, cp->rx); + } + d = (d + 1u) & (DESCRIPTORS_PER_CHANNEL - 1u); + } + tx_task_stream_timeslot(cp); +} + +static inline void tx_task_hdlc(struct etp_channel_private *cp, + struct net_device *netdev) +{ + unsigned d; + uint32_t desc_b; + struct tx_descriptor *tx; + struct sk_buff *skb; + struct txdesc __iomem *txdesc; + + d = cp->last_tx_desc_released + 1u; + d &= (DESCRIPTORS_PER_CHANNEL - 1u); + while (((skb = (tx = cp->tx_descriptor + d)->skb) != NULL) && + (((desc_b = + readl_relaxed(&(txdesc = tx->descriptor)-> + desc_b)) & TX_DESCB_TRANSFER) == 0u)) { + /* Has been sent. */ + uint32_t length = desc_b & TX_DESCB_LENGT_MASK; + pci_unmap_single(this_device_priv(cp)->pci_dev, + pci_unmap_addr(tx, address), + length, PCI_DMA_TODEVICE); + etp_update_tx_descriptor_statistics_netdev(&netdev->stats, + desc_b, length); + dev_kfree_skb_any(skb); + tx->skb = NULL; + cp->last_tx_desc_released = d; + d++; + d &= (DESCRIPTORS_PER_CHANNEL - 1u); + } + + netif_tx_lock(netdev); + /* If the next tx descriptor is free, continue taking new ones. */ + if (netif_queue_stopped(netdev) && + cp->tx_descriptor[cp->last_tx_desc_transmitted].skb == NULL && + this_if_priv(cp)->if_mode == IF_MODE_HDLC) + netif_wake_queue(netdev); + netif_tx_unlock(netdev); +} + +static inline void etp_enable_interrupt0(struct etp_device_private *dp, + unsigned channel_number, + uint8_t __iomem *ioaddr) +{ + unsigned long flags; + spinlock_t *lock = &dp->lock0; + spin_lock_irqsave(lock, flags); + writel(dp->reg_int_mask0 |= + CH_ALLINTS_MASK << INT_0_BIT_SHIFT_CH(channel_number), + ioaddr + REG_INT_MASK0); + mmiowb(); + spin_unlock_irqrestore(lock, flags); +} + +static int etp_poll(struct napi_struct *napi, int weight) +{ + struct etp_channel_private *cp = + container_of(napi, struct etp_channel_private, napi); + struct etp_interface_private *ip = this_if_priv(cp); + + switch (ip->if_mode) { +#if !ETP_TIMER + case IF_MODE_TIMESLOT: + case IF_MODE_STREAM: + { + struct etp_device_private *dp; + do { + cp->interrupt = false; + rx_task_stream_timeslot((unsigned long)cp); + napi_complete(&cp->napi); + } while (cp->interrupt && napi_reschedule(&cp->napi)); + dp = this_device_priv(cp); + etp_enable_interrupt0(dp, cp->channel_number, dp->ioaddr); + return 0; + } +#endif + case IF_MODE_HDLC: + { + struct etp_device_private *dp; + int poll = 0; + do { + struct net_device *dev = cp->this_netdev; + cp->interrupt = false; + tx_task_hdlc(cp, dev); + poll = rx_task_hdlc(cp, weight, dev, poll); + if (poll == weight) + return poll; + napi_complete(&cp->napi); + } while (cp->interrupt && napi_reschedule(&cp->napi)); + dp = this_device_priv(cp); + etp_enable_interrupt0(dp, cp->channel_number, dp->ioaddr); + return poll; + } + default: + napi_complete(napi); + return 0; + } +} + +static int etp_change_mtu(struct net_device *dev, int mtu) +{ + if (unlikely(mtu > ETP_MRU)) + return -EINVAL; + dev->mtu = mtu; + return 0; +} + +static void etp_netdev_tx_timeout(struct net_device *dev) +{ + struct etp_channel_private *cp = + ((struct hdlc_device *)(dev_to_hdlc(dev)))->priv; + struct etp_device_private *dp = cp->this_dev_priv; + local_irq_disable(); + etp_schedule(cp, dp, CH_TO_IF(cp->channel_number), dp->ioaddr); + local_irq_enable(); +} + +/* Clear (initialize) descriptors. */ +static inline void clear_descriptors(struct etp_channel_private *cp) +{ + unsigned d; + for (d = 0u; d < DESCRIPTORS_PER_CHANNEL; d++) { + struct rxdesc __iomem *rxdesc = + cp->rx_descriptor[d].descriptor; + struct txdesc __iomem *txdesc; + writel(0u, &rxdesc->desc_b); + writel(0u, &rxdesc->desc_a); + txdesc = cp->tx_descriptor[d].descriptor; + writel(0u, &txdesc->desc_b); + writel(0u, &txdesc->desc_a); + } +} + +static inline void etp_free_rx(struct etp_channel_private *cp, + struct etp_device_private *dp) +{ + unsigned d; + for (d = 0u; d < DESCRIPTORS_PER_CHANNEL; d++) { + struct rx_descriptor *rx = cp->rx_descriptor + d; + struct sk_buff *skb = rx->skb; + if (skb != NULL) { + pci_unmap_single(dp->pci_dev, + pci_unmap_addr(rx, address), + ETP_DMA, PCI_DMA_FROMDEVICE); + dev_kfree_skb(skb); + rx->skb = NULL; + } + } +} + +static int etp_netdev_open(struct net_device *netdev) +{ + struct etp_channel_private *cp = + ((struct hdlc_device *)(dev_to_hdlc(netdev)))->priv; + unsigned channel_number = cp->channel_number; + struct etp_interface_private *ip = this_if_priv(cp); + struct etp_device_private *dp = this_dev_priv(ip); + unsigned d; + uint8_t __iomem *ioaddr; + int error; + + if (unlikely(ip->if_mode >= IF_MODE_TIMESLOT)) /* timeslot or stream */ + return -EBUSY; + + cp->last_rx_desc_received = 0u; + cp->last_tx_desc_transmitted = 0u; + cp->last_tx_desc_released = DESCRIPTORS_PER_CHANNEL - 1u; + + /* Clear CRC mode (and flag multiply) in TX and RX registers. */ + writel(~(HDLC_CRC_MASK | HDLC_RETINA_FLAG) + & readl_relaxed(ip->reg_if_rxctrl), ip->reg_if_rxctrl); + writel(~(HDLC_CRC_MASK | HDLC_RETINA_FLAG) + & readl_relaxed(ip->reg_if_txctrl), ip->reg_if_txctrl); + switch (cp->hdlc_mode) { + case HDLC_MODE_CISCO_OVER_G703: + { + /* Set E1 mode to HDLC, configure CRC mode. */ + writel(E1_MODE_HDLC | HDLC_CRC_16 | + readl_relaxed(ip->reg_if_rxctrl), + ip->reg_if_rxctrl); + writel(E1_MODE_HDLC | HDLC_CRC_16 | + readl_relaxed(ip->reg_if_txctrl), + ip->reg_if_txctrl); + error = idt_open_if_hdlc_g703(dp, + CH_TO_IF(channel_number)); + if (unlikely(error)) + return error; + /* Select all timeslots. */ + writel(0u, ip->reg_if_rxctrl1); + writel(0u, ip->reg_if_txctrl1); + break; + } + case HDLC_MODE_RETINA_OVER_G703: + case HDLC_MODE_RETINA_OVER_G703_POINTOPOINT: + { + /* Set E1 mode to HDLC, configure CRC mode. */ + writel(E1_MODE_HDLC | HDLC_CRC_32 | HDLC_CRC_DELAY | + HDLC_RETINA_FLAG | + readl_relaxed(ip->reg_if_rxctrl), + ip->reg_if_rxctrl); + writel(E1_MODE_HDLC | HDLC_CRC_32 | HDLC_CRC_DELAY | + HDLC_RETINA_FLAG | + readl_relaxed(ip->reg_if_txctrl), + ip->reg_if_txctrl); + error = idt_open_if_hdlc_g703(dp, + CH_TO_IF(channel_number)); + if (unlikely(error)) + return error; + /* Select all timeslots. */ + writel(0u, ip->reg_if_rxctrl1); + writel(0u, ip->reg_if_txctrl1); + break; + } + case HDLC_MODE_CISCO_OVER_G704: + { + /* Set E1 mode to HDLC and configure CRC mode. */ + writel(E1_MODE_HDLC | HDLC_CRC_16 | + readl_relaxed(ip->reg_if_rxctrl), + ip->reg_if_rxctrl); + writel(E1_MODE_HDLC | HDLC_CRC_16 | + readl_relaxed(ip->reg_if_txctrl), + ip->reg_if_txctrl); + error = idt_open_if_hdlc_g704(dp, + CH_TO_IF(channel_number)); + if (unlikely(error)) + return error; + /* Select wanted timeslots. */ + writel(~(cp->hdlc_mode_g704_used_timeslots), + ip->reg_if_rxctrl1); + writel(~(cp->hdlc_mode_g704_used_timeslots), + ip->reg_if_txctrl1); + break; + } + case HDLC_MODE_RETINA_OVER_G704: + case HDLC_MODE_RETINA_OVER_G704_POINTOPOINT: + { + /* Set E1 mode to HDLC and configure CRC mode. */ + writel(E1_MODE_HDLC | HDLC_CRC_32 | HDLC_CRC_DELAY | + HDLC_RETINA_FLAG | + readl_relaxed(ip->reg_if_rxctrl), + ip->reg_if_rxctrl); + writel(E1_MODE_HDLC | HDLC_CRC_32 | HDLC_CRC_DELAY | + HDLC_RETINA_FLAG | + readl_relaxed(ip->reg_if_txctrl), + ip->reg_if_txctrl); + error = idt_open_if_hdlc_g704(dp, + CH_TO_IF(channel_number)); + if (unlikely(error)) + return error; + /* Select wanted timeslots. */ + writel(~(cp->hdlc_mode_g704_used_timeslots), + ip->reg_if_rxctrl1); + writel(~(cp->hdlc_mode_g704_used_timeslots), + ip->reg_if_txctrl1); + break; + } + } + + if (cp->hdlc_mode <= HDLC_MODE_CISCO_OVER_G704) { /* Cisco HDLC. */ + error = hdlc_open(netdev); + if (unlikely(error)) + return error; + } + + clear_descriptors(cp); + /* Go through all the descriptors and reserve new struct sk_buffs. */ + for (d = 0u; d < DESCRIPTORS_PER_CHANNEL; d++) { + dma_addr_t address; + struct sk_buff *skb = __netdev_alloc_skb(netdev, + ETP_DMA + NET_IP_ALIGN, + GFP_KERNEL); + if (unlikely(skb == NULL)) + continue; + skb_reserve(skb, NET_IP_ALIGN); + address = pci_map_single(dp->pci_dev, skb->data, + ETP_DMA, PCI_DMA_FROMDEVICE); + if (likely(!pci_dma_mapping_error(dp->pci_dev, address))) { + struct rx_descriptor *rx = cp->rx_descriptor + d; + struct rxdesc __iomem *rxdesc; + pci_unmap_addr_set(rx, address, address); + rx->skb = skb; + rxdesc = rx->descriptor; + writel(address, &rxdesc->desc_a); + writel(RX_DESCB_TRANSFER, &rxdesc->desc_b); + skb->ip_summed = CHECKSUM_UNNECESSARY; + } else { + dev_kfree_skb(skb); + } + } + + /* Start the reception and transmission channels. */ + writel(DMA_ENABLE | RX_FIFO_THRESHOLD_DEFAULT | ETP_DMA, + cp->reg_ch_rxctrl); + writel(DMA_ENABLE | TX_FIFO_THRESHOLD_DEFAULT | TX_START_LEVEL_DEFAULT, + cp->reg_ch_txctrl); + /* Turn the transmit clock on. */ + writel((ip->tx_clock_source << TX_CLOCK_SELECT_SHIFT) | + (~TX_CLOCK_SELECT_MASK + & readl_relaxed(ip->reg_if_txctrl)), ip->reg_if_txctrl); + ip->if_mode = IF_MODE_HDLC; + ioaddr = dp->ioaddr; + queue_status_work(dp, ioaddr); + napi_enable(&cp->napi); + /* Enable interrupts by setting the interrupt mask. */ + etp_enable_interrupt0(dp, channel_number, ioaddr); + netif_start_queue(netdev); + return 0; +} + +static int etp_netdev_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct etp_channel_private *cp + = ((struct hdlc_device *)(dev_to_hdlc(dev)))->priv; + unsigned last_transmitted; + uint8_t *data; + struct tx_descriptor *tx; + unsigned tx_length = skb->len; +#ifdef ETP_TESTER + /* change IP addresses to be able to ping myself */ + { + struct iphdr *ip_header = + (struct iphdr *)((skb->data) + sizeof(struct ethhdr)); + uint32_t *s_addr = &ip_header->saddr; + uint32_t *d_addr = &ip_header->daddr; + if (skb->len < sizeof(struct ethhdr) + sizeof(struct iphdr)) + goto no_messing_with_ip; + ((u8 *) s_addr)[3] ^= 4; + ((u8 *) d_addr)[3] ^= 4; + /* calculate new checksum: */ + ip_header->check = 0; + ip_header->check = ip_fast_csum((unsigned char *) + ip_header, ip_header->ihl); + } +no_messing_with_ip: +#endif /* ETP_TESTER */ + if (unlikely(tx_length < ETH_ZLEN)) { + if (unlikely(skb_padto(skb, ETH_ZLEN))) { + dev->stats.tx_dropped++; + return NETDEV_TX_OK; + } + tx_length = ETH_ZLEN; + } + if (cp->hdlc_mode >= HDLC_MODE_RETINA_OVER_G703) { + /* make room for CALP header */ + if (unlikely(skb_cow_head(skb, 2u))) + return NETDEV_TX_BUSY; + data = __skb_push(skb, 2u); + *data = 0x0; /* the CALP header */ + data[1] = 0x40; /* the CALP header */ + /* add CALP header length (+2), minus CRC (-4) */ + tx_length += 2u; + } else { + data = skb->data; + } + { + dma_addr_t bus_address = + pci_map_single(this_device_priv(cp)->pci_dev, data, + tx_length, PCI_DMA_TODEVICE); + if (likely(!pci_dma_mapping_error(this_device_priv(cp)->pci_dev, + bus_address))) { + struct txdesc __iomem *txdesc; + last_transmitted = cp->last_tx_desc_transmitted; + tx = cp->tx_descriptor + last_transmitted; + pci_unmap_addr_set(tx, address, bus_address); + txdesc = tx->descriptor; + writel(bus_address, &txdesc->desc_a); + writel(tx_length | TX_DESCB_TRANSFER, + &txdesc->desc_b); + } else { + if (cp->hdlc_mode >= HDLC_MODE_RETINA_OVER_G703) + __skb_pull(skb, 2u); + return NETDEV_TX_BUSY; + } + } + tx->skb = skb; + { + /* Calculate the next transmission descriptor entry */ + unsigned next = (last_transmitted + 1u) & + (DESCRIPTORS_PER_CHANNEL - 1u); + cp->last_tx_desc_transmitted = next; + /* If next descriptor is busy, discontinue taking new ones. */ + if (cp->tx_descriptor[next].skb != NULL) + netif_stop_queue(dev); + } + dev->trans_start = jiffies; + return NETDEV_TX_OK; +} + +static void etp_netdev_close_down(struct net_device *dev, + struct etp_channel_private *cp, + struct etp_interface_private *ip, + struct etp_device_private *dp) +{ + uint8_t __iomem *ioaddr, *reg_rst_ctrl; + unsigned d = cp->channel_number; + uint32_t __iomem *reg_if_txctrl = ip->reg_if_txctrl; + + if (ip->if_mode == IF_MODE_CLOSED) + return; + ip->if_mode = IF_MODE_CLOSED; + netif_tx_disable(dev); + napi_disable(&cp->napi); + + if (cp->hdlc_mode <= HDLC_MODE_CISCO_OVER_G704) + hdlc_close(dev); + + idt_close_if(dp, CH_TO_IF(d)); + /* Transmit clock off. */ + writel((CLOCK_SELECT_NO_CLOCK << TX_CLOCK_SELECT_SHIFT) | + (~TX_CLOCK_SELECT_MASK & + readl_relaxed(reg_if_txctrl)), reg_if_txctrl); + ioaddr = dp->ioaddr; + /* Disable interrupts by clearing the interrupt mask. */ + etp_disable_interrupt0_irq(dp, d, ioaddr); + /* Stop DMA. */ + writel(~DMA_ENABLE & readl(cp->reg_ch_rxctrl), cp->reg_ch_rxctrl); + writel(~DMA_ENABLE & readl_relaxed(cp->reg_ch_txctrl), + cp->reg_ch_txctrl); + /* Reset the channel. */ + reg_rst_ctrl = ioaddr + REG_RST_CTRL; + writel(RESET_CH(d), reg_rst_ctrl); + readl(reg_rst_ctrl); /* Wait for DMA to end before free. */ + /* Free all the reception skbuffs */ + etp_free_rx(cp, dp); + /* and transmission. */ + for (d = 0u; d < DESCRIPTORS_PER_CHANNEL; d++) { + struct tx_descriptor *tx = cp->tx_descriptor + d; + struct sk_buff *skb = tx->skb; + if (skb != NULL) { + pci_unmap_single(dp->pci_dev, + pci_unmap_addr(tx, address), + skb->len, PCI_DMA_TODEVICE); + dev_kfree_skb(skb); + tx->skb = NULL; + } + } + queue_status_work(dp, ioaddr); +} + +static int etp_netdev_close(struct net_device *dev) +{ + struct etp_channel_private *cp + = ((struct hdlc_device *)(dev_to_hdlc(dev)))->priv; + struct etp_interface_private *ip = this_if_priv(cp); + struct etp_device_private *dp = this_dev_priv(ip); + if (unlikely(!netif_device_present(dev))) + return -ENXIO; + etp_netdev_close_down(dev, cp, ip, dp); + return 0; +} + +/* For getting LOS information. */ +static inline int idt_los(unsigned device, unsigned offset) +{ + return etp_read_idt_register_lock(device, + E1_TRNCVR_LINE_STATUS0_REG | offset); +} + +/* Set E1 interrupt enabled. */ +static +void etp_enable_interrupt(struct etp_device_private *dp) +{ + spinlock_t *lock = &dp->lock2; + spin_lock_irq(lock); + writel(dp->reg_int_mask2 |= INT_2_E1_INT, dp->ioaddr + REG_INT_MASK2); + mmiowb(); + spin_unlock_irq(lock); +} + +/* Work called to read IDT chip. */ +static void status_work(struct work_struct *work) +{ + struct etp_device_private *dp = + container_of(work, struct etp_device_private, status_work); + struct etp_interface_private *interfaces = dp->interface_privates; + unsigned interface; + const unsigned device = device_number(dp); + struct mutex *mutex = &dp->idt; + if (unlikely(atomic_read(&dp->reset))) + return; + mutex_lock(mutex); + atomic_set(&dp->interrupt, ETP_INTERRUPT_NONE); + if (dp->run[0]) + dp->idt_int_callback[0](device); + if (dp->run[1]) + dp->idt_int_callback[1](device); + mutex_unlock(mutex); + interface = 0u; + do { + struct etp_interface_private *ip; + unsigned mode; + int los; + int offset = etp_idt_offset(device, interface); + struct net_device *this_netdev; + if (unlikely(offset < 0)) + return; + /* Clear E1 Interrupt Status 0. */ + etp_write_idt_register_lock(device, E1_TRNCVR_INT_STATUS0_REG + | offset, 1u); + los = idt_los(device, offset); + if (unlikely(los < 0)) + return; + los &= 1; + ip = &interfaces[interface]; + rtnl_lock(); + mode = ip->if_mode; + ip->los = los; + this_netdev = ip->ch_priv.this_netdev; + if (likely(this_netdev)) { + if (los || mode == IF_MODE_CLOSED) { + set_led(LED_CTRL_OFF, ip, interface, dp); + netif_carrier_off(this_netdev); + } else { /* Link up and interface opened. */ + netif_carrier_on(this_netdev); + set_led(mode == IF_MODE_HDLC ? LED_CTRL_TRAFFIC + : LED_CTRL_ON, ip, interface, dp); + } + } + rtnl_unlock(); + } while (interface++ < INTERFACES_PER_DEVICE - 1u); + if (unlikely(atomic_read(&dp->interrupt))) { +QUEUE: queue_delayed_work(dp->queue, &dp->led, HZ * 4ul / 5ul); + } else { + etp_enable_interrupt(dp); + if (unlikely(atomic_read(&dp->interrupt) && + etp_disable_interrupt2(dp, dp->ioaddr))) + goto QUEUE; + } +} + +/* Work called to read IDT chip for setting LEDs right after 4 seconds delay. */ +static void led_work(struct work_struct *work) +{ + struct delayed_work *led = + container_of(work, struct delayed_work, work); + struct etp_device_private *dp = + container_of(led, struct etp_device_private, led); + status_work(&dp->status_work); +} + +/* ---------- Functions of etp kernel interface (defined in etp.h) ---------- */ + +/* Registers callback functions. */ +int etp_register_callbacks(const struct etp_callback_struct *callback_p) +{ + struct etp_device_private *dp = get_dev_priv(callback_p->device); + struct etp_interface_private *interfaces = dp->interface_privates; + unsigned interface = callback_p->interface; + struct etp_interface_private *ip = interfaces + interface; + struct etp_channel_private *cp = &ip->ch_priv; + unsigned index = callback_p->index; + struct mutex *mutex = &dp->idt; + etp_idt_callback_t callback = callback_p->idt_int_callback; + void (*rx_callback) (unsigned device, + unsigned interface, + unsigned read, + const struct slot_struct *) = + callback_p->rx_callback; + void (*rx_old) (unsigned device, + unsigned interface, + unsigned read, + const struct slot_struct *); + void (*tx_callback) (unsigned device, + unsigned interface, + unsigned written, + struct slot_struct *) = callback_p->tx_callback; + void (*tx_old) (unsigned device, + unsigned interface, + unsigned written, + struct slot_struct *); + int error = 0; + mutex_lock(mutex); + if (callback) { + dp->idt_int_callback[index] = callback; + dp->run[index] |= 1u << interface; + } else { + dp->run[index] &= ~(1u << interface); + } + rx_old = cp->rx_callback; + tx_old = cp->tx_callback; + if (likely(atomic_read(&cp->owner) != !index)) { + if (rx_callback) { + atomic_set(&cp->owner, index); + cp->rx_callback = rx_callback; + cp->tx_callback = tx_callback; + } else { + atomic_set(&cp->owner, ETP_CALLBACKS); + cp->tx_callback = tx_null_callback; + cp->rx_callback = rx_null_callback; + } + } else if (unlikely(rx_callback)) { + error = -EBUSY; + } + mutex_unlock(mutex); + return error; +} +EXPORT_SYMBOL(etp_register_callbacks); + +uint32_t etp_rx_on_get(const struct etp_channel_private *cp) +{ + return readl(cp->reg_ch_rxctrl) & DMA_ENABLE_MASK; +} + +uint32_t etp_tx_on_get(const struct etp_channel_private *cp) +{ + return readl_relaxed(cp->reg_ch_txctrl) & DMA_ENABLE_MASK; +} + +int etp_frame(unsigned device, unsigned interface, bool frame) +{ + if (unlikely(device >= etp_number)) { + return -ENXIO; + } else { + struct etp_device_private *dp = get_dev_priv(device); + struct etp_interface_private *ip = + &dp->interface_privates[interface]; + if (frame) { + /* Set channel E1 mode to TIMESLOT. */ + int error = idt_open_if_timeslot(dp, interface); + if (unlikely(error)) + return error; + ip->if_mode = IF_MODE_TIMESLOT; + } else { + /* Set channel E1 mode to STREAM. */ + int error = idt_open_if_stream(dp, interface); + if (unlikely(error)) + return error; + ip->if_mode = IF_MODE_STREAM; + } + } + + return 0; +} +EXPORT_SYMBOL(etp_frame); + +/* Open interface (in timeslot or stream mode). */ +int etp_if_open(unsigned device, /* The number of the device. */ + unsigned interface, /* The number of the interface. */ + unsigned if_mode, + unsigned rx_slots, /* The size of the rx buffer. */ + unsigned tx_slots) /* The size of the rx buffer. */ +{ + struct etp_device_private *dp; + struct etp_interface_private *ip; + struct etp_channel_private *cp; + int error; + unsigned d; + struct rw_semaphore *ip_semaphore; + struct net_device *net_device; + struct device *dev; + + if (unlikely(tx_slots < MIN_SLOTS + || tx_slots > MAX_SLOTS + || rx_slots < MIN_SLOTS + || rx_slots > MAX_SLOTS)) + return -EINVAL; + if (unlikely(interface >= INTERFACES_PER_DEVICE)) + return -ENXIO; + if (unlikely(device >= etp_number)) + return -ENXIO; + dp = get_dev_priv(device); + ip = &dp->interface_privates[interface]; + ip_semaphore = &ip->semaphore; + down_write(ip_semaphore); + if (unlikely(atomic_read(&dp->reset))) { + error = -ENXIO; + goto UP; + } + cp = &ip->ch_priv; + while ((net_device = cp->this_netdev) == NULL) { + error = etp_init_netdev(cp, + HDLC_MODE_RETINA_OVER_G703_POINTOPOINT); + if (unlikely(error)) + goto UP; + } + rtnl_lock(); + if (unlikely(ip->if_mode != IF_MODE_CLOSED)) { /* The current mode */ + dev_warn(&net_device->dev, + "Interface must be closed before it can be opened\n"); + error = -EBUSY; + goto UNLOCK; + } + if (unlikely(if_mode < IF_MODE_TIMESLOT)) { /* The wanted mode */ + dev_warn(&net_device->dev, + "Invalid mode %u for the interface\n", if_mode); + error = -EINVAL; + goto UNLOCK; + } + dev = &dp->pci_dev->dev; + /* Reserve the buffers. */ + cp->tx = dma_alloc_coherent(dev, tx_slots * SLOT_SIZE, &cp->tx_address, + GFP_KERNEL); + if (unlikely(cp->tx == NULL)) { + error = -ENOMEM; + goto UNLOCK; + } + cp->tx_slots = tx_slots; + cp->rx = dma_alloc_coherent(dev, rx_slots * SLOT_SIZE, &cp->rx_address, + GFP_KERNEL); + if (unlikely(cp->rx == NULL)) { + error = -ENOMEM; + goto CLOSE; + } + cp->rx_slots = rx_slots; + cp->last_rx_desc_received = DESCRIPTORS_PER_CHANNEL - 1u; + cp->last_rx_slot_received = rx_slots - 1u; + cp->last_tx_desc_transmitted = DESCRIPTORS_PER_CHANNEL - 1u; + cp->last_tx_slot_transmitted = tx_slots - 1u; + /* Initialize the descriptors. */ + for (d = 0u; d < DESCRIPTORS_PER_CHANNEL; d++) { + struct rxdesc __iomem *rxdesc = + cp->rx_descriptor[d].descriptor; + struct txdesc __iomem *txdesc = + cp->tx_descriptor[d].descriptor; + writel(cp->rx_address + d * SLOT_SIZE, &rxdesc->desc_a); + writel(RX_DESCB_TRANSFER, &rxdesc->desc_b); + writel(cp->tx_address + d * SLOT_SIZE, &txdesc->desc_a); + writel((SLOT_SIZE & TX_DESCB_LENGT_MASK) | TX_DESCB_TRANSFER, + &txdesc->desc_b); + } + + /* Enable the disabled timeslots. */ + writel(0u, ip->reg_if_rxctrl1); + writel(0u, ip->reg_if_txctrl1); + writel(~(E1_MODE_MASK | HDLC_CRC_MASK) & + readl_relaxed(ip->reg_if_rxctrl), + ip->reg_if_rxctrl); + writel(~(E1_MODE_MASK | HDLC_CRC_MASK) & + readl_relaxed(ip->reg_if_txctrl), + ip->reg_if_txctrl); + error = etp_frame(device, interface, if_mode == IF_MODE_TIMESLOT); + if (likely(!error)) { + uint8_t __iomem *ioaddr = dp->ioaddr; + queue_status_work(dp, ioaddr); +#if ETP_TIMER + { + struct timer_list *timer = &cp->timer; + timer->expires = jiffies + HZ / 1000ul; + add_timer(timer); + } + mmiowb(); +#else + napi_enable(&cp->napi); + /* Enable interrupts by setting the interrupt mask. */ + etp_enable_interrupt0(dp, IF_TO_CH(interface), ioaddr); +#endif + } else { + goto CLOSE; + } +UNLOCK: rtnl_unlock(); +UP: up_write(ip_semaphore); + return error; +CLOSE: + etp_if_close_down(interface, dp, ip); + goto UNLOCK; +} +EXPORT_SYMBOL(etp_if_open); + +/** + * Close an interface in timeslot or stream mode. + * The caller must be holding the interface semaphore and rtnl_lock(). + **/ +static int etp_if_close_down(unsigned interface, struct etp_device_private *dp, + struct etp_interface_private *ip) +{ + struct etp_channel_private *cp = &ip->ch_priv; + uint8_t __iomem *ioaddr; + struct net_device *net_device = cp->this_netdev; + struct device *device; + unsigned char mode = ip->if_mode; + if (unlikely(net_device == NULL)) + return 0; + if (unlikely(mode == IF_MODE_HDLC)) { + dev_warn(&net_device->dev, + "Trying to close interface that is in HDLC mode\n"); + return -EBUSY; + } + idt_close_if(dp, interface); + etp_tx_off_down(ip); + etp_rx_off_down(ip); + ioaddr = dp->ioaddr; + /* Prevent the running of new polls and timers. */ + ip->if_mode = IF_MODE_CLOSED; +#if ETP_TIMER + smp_wmb(); /* Prevent restarting the timer by setting mode closed. */ + /* Kill a possible running timer before freeing DMA buffers. */ + del_timer_sync(&cp->timer); +#else + etp_disable_interrupt0_irq(dp, IF_TO_CH(interface), ioaddr); + /* Kill a possible running poll before freeing DMA buffers. */ + if (mode != IF_MODE_CLOSED) + napi_disable(&cp->napi); +#endif + /* Reset the channel. */ + writel(RESET_CH(IF_TO_CH(interface)), ioaddr + REG_RST_CTRL); + readl(ioaddr + REG_RST_CTRL); /* Wait for the card to respond. */ + device = &dp->pci_dev->dev; + /* Free the buffers. */ + if (likely(cp->tx)) { + dma_free_coherent(device, (size_t)cp->tx_slots * SLOT_SIZE, + cp->tx, cp->tx_address); + cp->tx = NULL; + } + if (likely(cp->rx)) { + dma_free_coherent(device, (size_t)cp->rx_slots * SLOT_SIZE, + cp->rx, cp->rx_address); + cp->rx = NULL; + } + queue_status_work(dp, ioaddr); + return 0; +} + +/* Close an interface in timeslot or stream mode only. */ +int etp_if_close(unsigned device, /* The number of the device. */ + unsigned interface) /* The number of the interface. */ +{ + struct etp_device_private *dp = get_dev_priv(device); + struct etp_interface_private *ip = &dp->interface_privates[interface]; + struct rw_semaphore *ip_semaphore = &ip->semaphore; + int error; + down_write(ip_semaphore); + rtnl_lock(); + error = etp_if_close_down(interface, dp, ip); + mmiowb(); + rtnl_unlock(); + up_write(ip_semaphore); + return error; +} +EXPORT_SYMBOL(etp_if_close); + +static int etp_tx_on_down(struct etp_interface_private *ip) +{ + struct etp_channel_private *cp = &ip->ch_priv; + if (unlikely(ip->if_mode < IF_MODE_TIMESLOT)) { + struct net_device *device = cp->this_netdev; + if (device) + dev_warn(&device->dev, "Cannot set transmitter on " + "because not in timeslot or stream mode\n"); + return -EBUSY; + } + /* Set DMA on... */ + writel(DMA_ENABLE | TX_FIFO_THRESHOLD_DEFAULT | TX_START_LEVEL_DEFAULT, + cp->reg_ch_txctrl); + /* ...and then the transmit clock on. */ + writel((ip->tx_clock_source << TX_CLOCK_SELECT_SHIFT) | + (~TX_CLOCK_SELECT_MASK + & readl_relaxed(ip->reg_if_txctrl)), ip->reg_if_txctrl); + return 0; +} + +/* Start transmitter (timeslot or stream mode only). */ +int etp_tx_on(unsigned device, unsigned channel) +{ + struct etp_device_private *dp; + struct etp_interface_private *ip; + int error; + struct rw_semaphore *ip_semaphore; + if (unlikely(device >= etp_number || channel >= INTERFACES_PER_DEVICE)) + return -ENXIO; + dp = get_dev_priv(device); + ip = &dp->interface_privates[channel]; + ip_semaphore = &ip->semaphore; + down_write(ip_semaphore); + error = etp_tx_on_down(ip); + mmiowb(); + up_write(ip_semaphore); + return error; +} +EXPORT_SYMBOL(etp_tx_on); + +/* Stop transmitter (timeslot or stream mode). */ +static int etp_tx_off_down(struct etp_interface_private *ip) +{ + unsigned mode = ip->if_mode; + struct etp_channel_private *cp = &ip->ch_priv; + + if (unlikely(mode == IF_MODE_HDLC)) { + dev_warn(&cp->this_netdev->dev, "Cannot set transmitter off " + "because not in timeslot or stream mode\n"); + return -EBUSY; + } + if (mode != IF_MODE_CLOSED) { + /* Transmit clock off. */ + writel((CLOCK_SELECT_NO_CLOCK << TX_CLOCK_SELECT_SHIFT) | + (~TX_CLOCK_SELECT_MASK & + readl_relaxed(ip->reg_if_txctrl)), + ip->reg_if_txctrl); + /* DMA off. */ + writel(~DMA_ENABLE & readl_relaxed(cp->reg_ch_txctrl), + cp->reg_ch_txctrl); + } + return 0; +} + +/* Stop transmitter (timeslot or stream mode only). */ +int etp_tx_off(unsigned device, unsigned channel) +{ + struct etp_device_private *dp; + struct etp_interface_private *ip; + struct rw_semaphore *ip_semaphore; + int error; + if (unlikely(device >= etp_number || channel >= INTERFACES_PER_DEVICE)) + return -ENXIO; + dp = get_dev_priv(device); + ip = &dp->interface_privates[channel]; + ip_semaphore = &ip->semaphore; + down_write(ip_semaphore); + error = etp_tx_off_down(ip); + mmiowb(); + up_write(ip_semaphore); + return 0; +} + +static int etp_rx_on_down(struct etp_interface_private *ip) +{ + struct etp_channel_private *cp = &ip->ch_priv; + if (unlikely(ip->if_mode < IF_MODE_TIMESLOT)) { + struct net_device *device = cp->this_netdev; + if (device) + dev_warn(&device->dev, "Cannot set receiver on " + "because not in timeslot or stream mode\n"); + return -EBUSY; + } + + writel(DMA_ENABLE | RX_FIFO_THRESHOLD_DEFAULT | SLOT_SIZE, + cp->reg_ch_rxctrl); + return 0; +} + +/* Start receiver (timeslot or stream mode only). */ +int etp_rx_on(unsigned device, unsigned channel) +{ + struct etp_device_private *dp; + struct etp_interface_private *ip; + int error; + struct rw_semaphore *ip_semaphore; + if (unlikely(device >= etp_number || channel >= INTERFACES_PER_DEVICE)) + return -ENXIO; + dp = get_dev_priv(device); + ip = &dp->interface_privates[channel]; + ip_semaphore = &ip->semaphore; + down_write(ip_semaphore); + error = etp_rx_on_down(ip); + mmiowb(); + up_write(ip_semaphore); + return error; +} +EXPORT_SYMBOL(etp_rx_on); + +/* Stop receiver (timeslot or stream mode only). */ +static int etp_rx_off_down(struct etp_interface_private *ip) +{ + struct etp_channel_private *cp = &ip->ch_priv; + if (unlikely(ip->if_mode == IF_MODE_HDLC)) { + dev_warn(&cp->this_netdev->dev, "Cannot set receiver off " + "because not in timeslot or stream mode\n"); + return -EBUSY; + } + if (ip->if_mode != IF_MODE_CLOSED) { + writel(~DMA_ENABLE & readl(cp->reg_ch_rxctrl), + cp->reg_ch_rxctrl); + } + return 0; +} + +/* Stop receiver (timeslot or stream mode only). */ +int etp_rx_off(unsigned device, /* The number of the device. */ + unsigned channel) +{ + struct etp_device_private *dp; + struct etp_interface_private *ip; + struct rw_semaphore *ip_semaphore; + int error; + if (unlikely(device >= etp_number || channel >= INTERFACES_PER_DEVICE)) + return -ENXIO; + dp = get_dev_priv(device); + ip = &dp->interface_privates[channel]; + ip_semaphore = &ip->semaphore; + down_write(ip_semaphore); + error = etp_rx_off_down(ip); + mmiowb(); + up_write(ip_semaphore); + return 0; +} + +/* Change settings of an interface. */ +int etp_if_settings(unsigned device, /* The number of the device */ + unsigned channel, /* The number of interface */ + uint32_t clock_source, /* whence the transmit clock comes */ + unsigned hdlc_mode, + uint32_t hdlc_mode_g704_used_timeslots) +{ + struct etp_device_private *dp; + struct etp_interface_private *ip; + int error; + struct rw_semaphore *ip_semaphore; + if (unlikely(device >= etp_number || channel >= INTERFACES_PER_DEVICE)) + return -ENXIO; + dp = get_dev_priv(device); + ip = &dp->interface_privates[channel]; + ip_semaphore = &ip->semaphore; + down_write(ip_semaphore); + if (unlikely(atomic_read(&dp->reset))) { + error = -ENXIO; + } else { + error = etp_if_settings_down(dp, ip, clock_source, hdlc_mode, + hdlc_mode_g704_used_timeslots); + mmiowb(); + } + up_write(ip_semaphore); + return error; +} +EXPORT_SYMBOL(etp_if_settings); + +/* Set output clock source. */ +int etp_ext_output_clock(unsigned device, uint32_t clock_source) +{ + struct etp_device_private *dp; + int error; + struct rw_semaphore *ip_semaphore; + if (unlikely(device >= etp_number)) + return -ENXIO; + dp = get_dev_priv(device); + ip_semaphore = &dp->interface_privates[0].semaphore; + down_write(ip_semaphore); + if (likely(!atomic_read(&dp->reset))) { + error = etp_ext_output_clock_down(dp, clock_source); + mmiowb(); + } else { + error = -ENXIO; + } + up_write(ip_semaphore); + return error; +} + +/* Fine tune local clock frequency. */ +int etp_nco_adjust(unsigned device, uint32_t nco_addend_value) +{ + struct etp_device_private *dp; + struct mutex *mutex; + int error; + if (unlikely(device >= etp_number)) + return -ENXIO; + dp = get_dev_priv(device); + mutex = &dp->mutex; + mutex_lock(mutex); + if (unlikely(atomic_read(&dp->reset))) { + error = -ENXIO; + } else { + etp_nco_adjust_down(dp, nco_addend_value); + error = 0; + mmiowb(); + } + mutex_unlock(mutex); + return error; +} -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/