162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * wanXL serial card driver for Linux 462306a36Sopenharmony_ci * host part 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2003 Krzysztof Halasa <khc@pm.waw.pl> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Status: 962306a36Sopenharmony_ci * - Only DTE (external clock) support with NRZ and NRZI encodings 1062306a36Sopenharmony_ci * - wanXL100 will require minor driver modifications, no access to hw 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/kernel.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/sched.h> 1962306a36Sopenharmony_ci#include <linux/types.h> 2062306a36Sopenharmony_ci#include <linux/fcntl.h> 2162306a36Sopenharmony_ci#include <linux/string.h> 2262306a36Sopenharmony_ci#include <linux/errno.h> 2362306a36Sopenharmony_ci#include <linux/init.h> 2462306a36Sopenharmony_ci#include <linux/interrupt.h> 2562306a36Sopenharmony_ci#include <linux/ioport.h> 2662306a36Sopenharmony_ci#include <linux/netdevice.h> 2762306a36Sopenharmony_ci#include <linux/hdlc.h> 2862306a36Sopenharmony_ci#include <linux/pci.h> 2962306a36Sopenharmony_ci#include <linux/dma-mapping.h> 3062306a36Sopenharmony_ci#include <linux/delay.h> 3162306a36Sopenharmony_ci#include <asm/io.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include "wanxl.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic const char *version = "wanXL serial card driver version: 0.48"; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define PLX_CTL_RESET 0x40000000 /* adapter reset */ 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#undef DEBUG_PKT 4062306a36Sopenharmony_ci#undef DEBUG_PCI 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* MAILBOX #1 - PUTS COMMANDS */ 4362306a36Sopenharmony_ci#define MBX1_CMD_ABORTJ 0x85000000 /* Abort and Jump */ 4462306a36Sopenharmony_ci#ifdef __LITTLE_ENDIAN 4562306a36Sopenharmony_ci#define MBX1_CMD_BSWAP 0x8C000001 /* little-endian Byte Swap Mode */ 4662306a36Sopenharmony_ci#else 4762306a36Sopenharmony_ci#define MBX1_CMD_BSWAP 0x8C000000 /* big-endian Byte Swap Mode */ 4862306a36Sopenharmony_ci#endif 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* MAILBOX #2 - DRAM SIZE */ 5162306a36Sopenharmony_ci#define MBX2_MEMSZ_MASK 0xFFFF0000 /* PUTS Memory Size Register mask */ 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistruct port { 5462306a36Sopenharmony_ci struct net_device *dev; 5562306a36Sopenharmony_ci struct card *card; 5662306a36Sopenharmony_ci spinlock_t lock; /* for wanxl_xmit */ 5762306a36Sopenharmony_ci int node; /* physical port #0 - 3 */ 5862306a36Sopenharmony_ci unsigned int clock_type; 5962306a36Sopenharmony_ci int tx_in, tx_out; 6062306a36Sopenharmony_ci struct sk_buff *tx_skbs[TX_BUFFERS]; 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistruct card_status { 6462306a36Sopenharmony_ci desc_t rx_descs[RX_QUEUE_LENGTH]; 6562306a36Sopenharmony_ci port_status_t port_status[4]; 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistruct card { 6962306a36Sopenharmony_ci int n_ports; /* 1, 2 or 4 ports */ 7062306a36Sopenharmony_ci u8 irq; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci u8 __iomem *plx; /* PLX PCI9060 virtual base address */ 7362306a36Sopenharmony_ci struct pci_dev *pdev; /* for pci_name(pdev) */ 7462306a36Sopenharmony_ci int rx_in; 7562306a36Sopenharmony_ci struct sk_buff *rx_skbs[RX_QUEUE_LENGTH]; 7662306a36Sopenharmony_ci struct card_status *status; /* shared between host and card */ 7762306a36Sopenharmony_ci dma_addr_t status_address; 7862306a36Sopenharmony_ci struct port ports[]; /* 1 - 4 port structures follow */ 7962306a36Sopenharmony_ci}; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic inline struct port *dev_to_port(struct net_device *dev) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci return (struct port *)dev_to_hdlc(dev)->priv; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic inline port_status_t *get_status(struct port *port) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci return &port->card->status->port_status[port->node]; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci#ifdef DEBUG_PCI 9262306a36Sopenharmony_cistatic inline dma_addr_t pci_map_single_debug(struct pci_dev *pdev, void *ptr, 9362306a36Sopenharmony_ci size_t size, int direction) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci dma_addr_t addr = dma_map_single(&pdev->dev, ptr, size, direction); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (addr + size > 0x100000000LL) 9862306a36Sopenharmony_ci pr_crit("%s: pci_map_single() returned memory at 0x%llx!\n", 9962306a36Sopenharmony_ci pci_name(pdev), (unsigned long long)addr); 10062306a36Sopenharmony_ci return addr; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#undef pci_map_single 10462306a36Sopenharmony_ci#define pci_map_single pci_map_single_debug 10562306a36Sopenharmony_ci#endif 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/* Cable and/or personality module change interrupt service */ 10862306a36Sopenharmony_cistatic inline void wanxl_cable_intr(struct port *port) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci u32 value = get_status(port)->cable; 11162306a36Sopenharmony_ci int valid = 1; 11262306a36Sopenharmony_ci const char *cable, *pm, *dte = "", *dsr = "", *dcd = ""; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci switch (value & 0x7) { 11562306a36Sopenharmony_ci case STATUS_CABLE_V35: 11662306a36Sopenharmony_ci cable = "V.35"; 11762306a36Sopenharmony_ci break; 11862306a36Sopenharmony_ci case STATUS_CABLE_X21: 11962306a36Sopenharmony_ci cable = "X.21"; 12062306a36Sopenharmony_ci break; 12162306a36Sopenharmony_ci case STATUS_CABLE_V24: 12262306a36Sopenharmony_ci cable = "V.24"; 12362306a36Sopenharmony_ci break; 12462306a36Sopenharmony_ci case STATUS_CABLE_EIA530: 12562306a36Sopenharmony_ci cable = "EIA530"; 12662306a36Sopenharmony_ci break; 12762306a36Sopenharmony_ci case STATUS_CABLE_NONE: 12862306a36Sopenharmony_ci cable = "no"; 12962306a36Sopenharmony_ci break; 13062306a36Sopenharmony_ci default: 13162306a36Sopenharmony_ci cable = "invalid"; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci switch ((value >> STATUS_CABLE_PM_SHIFT) & 0x7) { 13562306a36Sopenharmony_ci case STATUS_CABLE_V35: 13662306a36Sopenharmony_ci pm = "V.35"; 13762306a36Sopenharmony_ci break; 13862306a36Sopenharmony_ci case STATUS_CABLE_X21: 13962306a36Sopenharmony_ci pm = "X.21"; 14062306a36Sopenharmony_ci break; 14162306a36Sopenharmony_ci case STATUS_CABLE_V24: 14262306a36Sopenharmony_ci pm = "V.24"; 14362306a36Sopenharmony_ci break; 14462306a36Sopenharmony_ci case STATUS_CABLE_EIA530: 14562306a36Sopenharmony_ci pm = "EIA530"; 14662306a36Sopenharmony_ci break; 14762306a36Sopenharmony_ci case STATUS_CABLE_NONE: 14862306a36Sopenharmony_ci pm = "no personality"; 14962306a36Sopenharmony_ci valid = 0; 15062306a36Sopenharmony_ci break; 15162306a36Sopenharmony_ci default: 15262306a36Sopenharmony_ci pm = "invalid personality"; 15362306a36Sopenharmony_ci valid = 0; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (valid) { 15762306a36Sopenharmony_ci if ((value & 7) == ((value >> STATUS_CABLE_PM_SHIFT) & 7)) { 15862306a36Sopenharmony_ci dsr = (value & STATUS_CABLE_DSR) ? ", DSR ON" : 15962306a36Sopenharmony_ci ", DSR off"; 16062306a36Sopenharmony_ci dcd = (value & STATUS_CABLE_DCD) ? ", carrier ON" : 16162306a36Sopenharmony_ci ", carrier off"; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci dte = (value & STATUS_CABLE_DCE) ? " DCE" : " DTE"; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci netdev_info(port->dev, "%s%s module, %s cable%s%s\n", 16662306a36Sopenharmony_ci pm, dte, cable, dsr, dcd); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (value & STATUS_CABLE_DCD) 16962306a36Sopenharmony_ci netif_carrier_on(port->dev); 17062306a36Sopenharmony_ci else 17162306a36Sopenharmony_ci netif_carrier_off(port->dev); 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci/* Transmit complete interrupt service */ 17562306a36Sopenharmony_cistatic inline void wanxl_tx_intr(struct port *port) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct net_device *dev = port->dev; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci while (1) { 18062306a36Sopenharmony_ci desc_t *desc = &get_status(port)->tx_descs[port->tx_in]; 18162306a36Sopenharmony_ci struct sk_buff *skb = port->tx_skbs[port->tx_in]; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci switch (desc->stat) { 18462306a36Sopenharmony_ci case PACKET_FULL: 18562306a36Sopenharmony_ci case PACKET_EMPTY: 18662306a36Sopenharmony_ci netif_wake_queue(dev); 18762306a36Sopenharmony_ci return; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci case PACKET_UNDERRUN: 19062306a36Sopenharmony_ci dev->stats.tx_errors++; 19162306a36Sopenharmony_ci dev->stats.tx_fifo_errors++; 19262306a36Sopenharmony_ci break; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci default: 19562306a36Sopenharmony_ci dev->stats.tx_packets++; 19662306a36Sopenharmony_ci dev->stats.tx_bytes += skb->len; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci desc->stat = PACKET_EMPTY; /* Free descriptor */ 19962306a36Sopenharmony_ci dma_unmap_single(&port->card->pdev->dev, desc->address, 20062306a36Sopenharmony_ci skb->len, DMA_TO_DEVICE); 20162306a36Sopenharmony_ci dev_consume_skb_irq(skb); 20262306a36Sopenharmony_ci port->tx_in = (port->tx_in + 1) % TX_BUFFERS; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci/* Receive complete interrupt service */ 20762306a36Sopenharmony_cistatic inline void wanxl_rx_intr(struct card *card) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci desc_t *desc; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci while (desc = &card->status->rx_descs[card->rx_in], 21262306a36Sopenharmony_ci desc->stat != PACKET_EMPTY) { 21362306a36Sopenharmony_ci if ((desc->stat & PACKET_PORT_MASK) > card->n_ports) { 21462306a36Sopenharmony_ci pr_crit("%s: received packet for nonexistent port\n", 21562306a36Sopenharmony_ci pci_name(card->pdev)); 21662306a36Sopenharmony_ci } else { 21762306a36Sopenharmony_ci struct sk_buff *skb = card->rx_skbs[card->rx_in]; 21862306a36Sopenharmony_ci struct port *port = &card->ports[desc->stat & 21962306a36Sopenharmony_ci PACKET_PORT_MASK]; 22062306a36Sopenharmony_ci struct net_device *dev = port->dev; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (!skb) { 22362306a36Sopenharmony_ci dev->stats.rx_dropped++; 22462306a36Sopenharmony_ci } else { 22562306a36Sopenharmony_ci dma_unmap_single(&card->pdev->dev, 22662306a36Sopenharmony_ci desc->address, BUFFER_LENGTH, 22762306a36Sopenharmony_ci DMA_FROM_DEVICE); 22862306a36Sopenharmony_ci skb_put(skb, desc->length); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci#ifdef DEBUG_PKT 23162306a36Sopenharmony_ci printk(KERN_DEBUG "%s RX(%i):", dev->name, 23262306a36Sopenharmony_ci skb->len); 23362306a36Sopenharmony_ci debug_frame(skb); 23462306a36Sopenharmony_ci#endif 23562306a36Sopenharmony_ci dev->stats.rx_packets++; 23662306a36Sopenharmony_ci dev->stats.rx_bytes += skb->len; 23762306a36Sopenharmony_ci skb->protocol = hdlc_type_trans(skb, dev); 23862306a36Sopenharmony_ci netif_rx(skb); 23962306a36Sopenharmony_ci skb = NULL; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (!skb) { 24362306a36Sopenharmony_ci skb = dev_alloc_skb(BUFFER_LENGTH); 24462306a36Sopenharmony_ci desc->address = skb ? 24562306a36Sopenharmony_ci dma_map_single(&card->pdev->dev, 24662306a36Sopenharmony_ci skb->data, 24762306a36Sopenharmony_ci BUFFER_LENGTH, 24862306a36Sopenharmony_ci DMA_FROM_DEVICE) : 0; 24962306a36Sopenharmony_ci card->rx_skbs[card->rx_in] = skb; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci desc->stat = PACKET_EMPTY; /* Free descriptor */ 25362306a36Sopenharmony_ci card->rx_in = (card->rx_in + 1) % RX_QUEUE_LENGTH; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic irqreturn_t wanxl_intr(int irq, void *dev_id) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci struct card *card = dev_id; 26062306a36Sopenharmony_ci int i; 26162306a36Sopenharmony_ci u32 stat; 26262306a36Sopenharmony_ci int handled = 0; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci while ((stat = readl(card->plx + PLX_DOORBELL_FROM_CARD)) != 0) { 26562306a36Sopenharmony_ci handled = 1; 26662306a36Sopenharmony_ci writel(stat, card->plx + PLX_DOORBELL_FROM_CARD); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci for (i = 0; i < card->n_ports; i++) { 26962306a36Sopenharmony_ci if (stat & (1 << (DOORBELL_FROM_CARD_TX_0 + i))) 27062306a36Sopenharmony_ci wanxl_tx_intr(&card->ports[i]); 27162306a36Sopenharmony_ci if (stat & (1 << (DOORBELL_FROM_CARD_CABLE_0 + i))) 27262306a36Sopenharmony_ci wanxl_cable_intr(&card->ports[i]); 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci if (stat & (1 << DOORBELL_FROM_CARD_RX)) 27562306a36Sopenharmony_ci wanxl_rx_intr(card); 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci return IRQ_RETVAL(handled); 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic netdev_tx_t wanxl_xmit(struct sk_buff *skb, struct net_device *dev) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci struct port *port = dev_to_port(dev); 28462306a36Sopenharmony_ci desc_t *desc; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci spin_lock(&port->lock); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci desc = &get_status(port)->tx_descs[port->tx_out]; 28962306a36Sopenharmony_ci if (desc->stat != PACKET_EMPTY) { 29062306a36Sopenharmony_ci /* should never happen - previous xmit should stop queue */ 29162306a36Sopenharmony_ci#ifdef DEBUG_PKT 29262306a36Sopenharmony_ci printk(KERN_DEBUG "%s: transmitter buffer full\n", dev->name); 29362306a36Sopenharmony_ci#endif 29462306a36Sopenharmony_ci netif_stop_queue(dev); 29562306a36Sopenharmony_ci spin_unlock(&port->lock); 29662306a36Sopenharmony_ci return NETDEV_TX_BUSY; /* request packet to be queued */ 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci#ifdef DEBUG_PKT 30062306a36Sopenharmony_ci printk(KERN_DEBUG "%s TX(%i):", dev->name, skb->len); 30162306a36Sopenharmony_ci debug_frame(skb); 30262306a36Sopenharmony_ci#endif 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci port->tx_skbs[port->tx_out] = skb; 30562306a36Sopenharmony_ci desc->address = dma_map_single(&port->card->pdev->dev, skb->data, 30662306a36Sopenharmony_ci skb->len, DMA_TO_DEVICE); 30762306a36Sopenharmony_ci desc->length = skb->len; 30862306a36Sopenharmony_ci desc->stat = PACKET_FULL; 30962306a36Sopenharmony_ci writel(1 << (DOORBELL_TO_CARD_TX_0 + port->node), 31062306a36Sopenharmony_ci port->card->plx + PLX_DOORBELL_TO_CARD); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci port->tx_out = (port->tx_out + 1) % TX_BUFFERS; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (get_status(port)->tx_descs[port->tx_out].stat != PACKET_EMPTY) { 31562306a36Sopenharmony_ci netif_stop_queue(dev); 31662306a36Sopenharmony_ci#ifdef DEBUG_PKT 31762306a36Sopenharmony_ci printk(KERN_DEBUG "%s: transmitter buffer full\n", dev->name); 31862306a36Sopenharmony_ci#endif 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci spin_unlock(&port->lock); 32262306a36Sopenharmony_ci return NETDEV_TX_OK; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic int wanxl_attach(struct net_device *dev, unsigned short encoding, 32662306a36Sopenharmony_ci unsigned short parity) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci struct port *port = dev_to_port(dev); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (encoding != ENCODING_NRZ && 33162306a36Sopenharmony_ci encoding != ENCODING_NRZI) 33262306a36Sopenharmony_ci return -EINVAL; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (parity != PARITY_NONE && 33562306a36Sopenharmony_ci parity != PARITY_CRC32_PR1_CCITT && 33662306a36Sopenharmony_ci parity != PARITY_CRC16_PR1_CCITT && 33762306a36Sopenharmony_ci parity != PARITY_CRC32_PR0_CCITT && 33862306a36Sopenharmony_ci parity != PARITY_CRC16_PR0_CCITT) 33962306a36Sopenharmony_ci return -EINVAL; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci get_status(port)->encoding = encoding; 34262306a36Sopenharmony_ci get_status(port)->parity = parity; 34362306a36Sopenharmony_ci return 0; 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic int wanxl_ioctl(struct net_device *dev, struct if_settings *ifs) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci const size_t size = sizeof(sync_serial_settings); 34962306a36Sopenharmony_ci sync_serial_settings line; 35062306a36Sopenharmony_ci struct port *port = dev_to_port(dev); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci switch (ifs->type) { 35362306a36Sopenharmony_ci case IF_GET_IFACE: 35462306a36Sopenharmony_ci ifs->type = IF_IFACE_SYNC_SERIAL; 35562306a36Sopenharmony_ci if (ifs->size < size) { 35662306a36Sopenharmony_ci ifs->size = size; /* data size wanted */ 35762306a36Sopenharmony_ci return -ENOBUFS; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci memset(&line, 0, sizeof(line)); 36062306a36Sopenharmony_ci line.clock_type = get_status(port)->clocking; 36162306a36Sopenharmony_ci line.clock_rate = 0; 36262306a36Sopenharmony_ci line.loopback = 0; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (copy_to_user(ifs->ifs_ifsu.sync, &line, size)) 36562306a36Sopenharmony_ci return -EFAULT; 36662306a36Sopenharmony_ci return 0; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci case IF_IFACE_SYNC_SERIAL: 36962306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 37062306a36Sopenharmony_ci return -EPERM; 37162306a36Sopenharmony_ci if (dev->flags & IFF_UP) 37262306a36Sopenharmony_ci return -EBUSY; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (copy_from_user(&line, ifs->ifs_ifsu.sync, 37562306a36Sopenharmony_ci size)) 37662306a36Sopenharmony_ci return -EFAULT; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci if (line.clock_type != CLOCK_EXT && 37962306a36Sopenharmony_ci line.clock_type != CLOCK_TXFROMRX) 38062306a36Sopenharmony_ci return -EINVAL; /* No such clock setting */ 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci if (line.loopback != 0) 38362306a36Sopenharmony_ci return -EINVAL; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci get_status(port)->clocking = line.clock_type; 38662306a36Sopenharmony_ci return 0; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci default: 38962306a36Sopenharmony_ci return hdlc_ioctl(dev, ifs); 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic int wanxl_open(struct net_device *dev) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci struct port *port = dev_to_port(dev); 39662306a36Sopenharmony_ci u8 __iomem *dbr = port->card->plx + PLX_DOORBELL_TO_CARD; 39762306a36Sopenharmony_ci unsigned long timeout; 39862306a36Sopenharmony_ci int i; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci if (get_status(port)->open) { 40162306a36Sopenharmony_ci netdev_err(dev, "port already open\n"); 40262306a36Sopenharmony_ci return -EIO; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci i = hdlc_open(dev); 40662306a36Sopenharmony_ci if (i) 40762306a36Sopenharmony_ci return i; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci port->tx_in = port->tx_out = 0; 41062306a36Sopenharmony_ci for (i = 0; i < TX_BUFFERS; i++) 41162306a36Sopenharmony_ci get_status(port)->tx_descs[i].stat = PACKET_EMPTY; 41262306a36Sopenharmony_ci /* signal the card */ 41362306a36Sopenharmony_ci writel(1 << (DOORBELL_TO_CARD_OPEN_0 + port->node), dbr); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci timeout = jiffies + HZ; 41662306a36Sopenharmony_ci do { 41762306a36Sopenharmony_ci if (get_status(port)->open) { 41862306a36Sopenharmony_ci netif_start_queue(dev); 41962306a36Sopenharmony_ci return 0; 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci } while (time_after(timeout, jiffies)); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci netdev_err(dev, "unable to open port\n"); 42462306a36Sopenharmony_ci /* ask the card to close the port, should it be still alive */ 42562306a36Sopenharmony_ci writel(1 << (DOORBELL_TO_CARD_CLOSE_0 + port->node), dbr); 42662306a36Sopenharmony_ci return -EFAULT; 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic int wanxl_close(struct net_device *dev) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci struct port *port = dev_to_port(dev); 43262306a36Sopenharmony_ci unsigned long timeout; 43362306a36Sopenharmony_ci int i; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci hdlc_close(dev); 43662306a36Sopenharmony_ci /* signal the card */ 43762306a36Sopenharmony_ci writel(1 << (DOORBELL_TO_CARD_CLOSE_0 + port->node), 43862306a36Sopenharmony_ci port->card->plx + PLX_DOORBELL_TO_CARD); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci timeout = jiffies + HZ; 44162306a36Sopenharmony_ci do { 44262306a36Sopenharmony_ci if (!get_status(port)->open) 44362306a36Sopenharmony_ci break; 44462306a36Sopenharmony_ci } while (time_after(timeout, jiffies)); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if (get_status(port)->open) 44762306a36Sopenharmony_ci netdev_err(dev, "unable to close port\n"); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci netif_stop_queue(dev); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci for (i = 0; i < TX_BUFFERS; i++) { 45262306a36Sopenharmony_ci desc_t *desc = &get_status(port)->tx_descs[i]; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci if (desc->stat != PACKET_EMPTY) { 45562306a36Sopenharmony_ci desc->stat = PACKET_EMPTY; 45662306a36Sopenharmony_ci dma_unmap_single(&port->card->pdev->dev, 45762306a36Sopenharmony_ci desc->address, port->tx_skbs[i]->len, 45862306a36Sopenharmony_ci DMA_TO_DEVICE); 45962306a36Sopenharmony_ci dev_kfree_skb(port->tx_skbs[i]); 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci return 0; 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic struct net_device_stats *wanxl_get_stats(struct net_device *dev) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci struct port *port = dev_to_port(dev); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci dev->stats.rx_over_errors = get_status(port)->rx_overruns; 47062306a36Sopenharmony_ci dev->stats.rx_frame_errors = get_status(port)->rx_frame_errors; 47162306a36Sopenharmony_ci dev->stats.rx_errors = dev->stats.rx_over_errors + 47262306a36Sopenharmony_ci dev->stats.rx_frame_errors; 47362306a36Sopenharmony_ci return &dev->stats; 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic int wanxl_puts_command(struct card *card, u32 cmd) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci unsigned long timeout = jiffies + 5 * HZ; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci writel(cmd, card->plx + PLX_MAILBOX_1); 48162306a36Sopenharmony_ci do { 48262306a36Sopenharmony_ci if (readl(card->plx + PLX_MAILBOX_1) == 0) 48362306a36Sopenharmony_ci return 0; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci schedule(); 48662306a36Sopenharmony_ci } while (time_after(timeout, jiffies)); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci return -1; 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cistatic void wanxl_reset(struct card *card) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci u32 old_value = readl(card->plx + PLX_CONTROL) & ~PLX_CTL_RESET; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci writel(0x80, card->plx + PLX_MAILBOX_0); 49662306a36Sopenharmony_ci writel(old_value | PLX_CTL_RESET, card->plx + PLX_CONTROL); 49762306a36Sopenharmony_ci readl(card->plx + PLX_CONTROL); /* wait for posted write */ 49862306a36Sopenharmony_ci udelay(1); 49962306a36Sopenharmony_ci writel(old_value, card->plx + PLX_CONTROL); 50062306a36Sopenharmony_ci readl(card->plx + PLX_CONTROL); /* wait for posted write */ 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_cistatic void wanxl_pci_remove_one(struct pci_dev *pdev) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci struct card *card = pci_get_drvdata(pdev); 50662306a36Sopenharmony_ci int i; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci for (i = 0; i < card->n_ports; i++) { 50962306a36Sopenharmony_ci unregister_hdlc_device(card->ports[i].dev); 51062306a36Sopenharmony_ci free_netdev(card->ports[i].dev); 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci /* unregister and free all host resources */ 51462306a36Sopenharmony_ci if (card->irq) 51562306a36Sopenharmony_ci free_irq(card->irq, card); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci wanxl_reset(card); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci for (i = 0; i < RX_QUEUE_LENGTH; i++) 52062306a36Sopenharmony_ci if (card->rx_skbs[i]) { 52162306a36Sopenharmony_ci dma_unmap_single(&card->pdev->dev, 52262306a36Sopenharmony_ci card->status->rx_descs[i].address, 52362306a36Sopenharmony_ci BUFFER_LENGTH, DMA_FROM_DEVICE); 52462306a36Sopenharmony_ci dev_kfree_skb(card->rx_skbs[i]); 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci if (card->plx) 52862306a36Sopenharmony_ci iounmap(card->plx); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if (card->status) 53162306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, sizeof(struct card_status), 53262306a36Sopenharmony_ci card->status, card->status_address); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci pci_release_regions(pdev); 53562306a36Sopenharmony_ci pci_disable_device(pdev); 53662306a36Sopenharmony_ci kfree(card); 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci#include "wanxlfw.inc" 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cistatic const struct net_device_ops wanxl_ops = { 54262306a36Sopenharmony_ci .ndo_open = wanxl_open, 54362306a36Sopenharmony_ci .ndo_stop = wanxl_close, 54462306a36Sopenharmony_ci .ndo_start_xmit = hdlc_start_xmit, 54562306a36Sopenharmony_ci .ndo_siocwandev = wanxl_ioctl, 54662306a36Sopenharmony_ci .ndo_get_stats = wanxl_get_stats, 54762306a36Sopenharmony_ci}; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_cistatic int wanxl_pci_init_one(struct pci_dev *pdev, 55062306a36Sopenharmony_ci const struct pci_device_id *ent) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci struct card *card; 55362306a36Sopenharmony_ci u32 ramsize, stat; 55462306a36Sopenharmony_ci unsigned long timeout; 55562306a36Sopenharmony_ci u32 plx_phy; /* PLX PCI base address */ 55662306a36Sopenharmony_ci u32 mem_phy; /* memory PCI base addr */ 55762306a36Sopenharmony_ci u8 __iomem *mem; /* memory virtual base addr */ 55862306a36Sopenharmony_ci int i, ports; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci#ifndef MODULE 56162306a36Sopenharmony_ci pr_info_once("%s\n", version); 56262306a36Sopenharmony_ci#endif 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci i = pci_enable_device(pdev); 56562306a36Sopenharmony_ci if (i) 56662306a36Sopenharmony_ci return i; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci /* QUICC can only access first 256 MB of host RAM directly, 56962306a36Sopenharmony_ci * but PLX9060 DMA does 32-bits for actual packet data transfers 57062306a36Sopenharmony_ci */ 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci /* FIXME when PCI/DMA subsystems are fixed. 57362306a36Sopenharmony_ci * We set both dma_mask and consistent_dma_mask to 28 bits 57462306a36Sopenharmony_ci * and pray pci_alloc_consistent() will use this info. It should 57562306a36Sopenharmony_ci * work on most platforms 57662306a36Sopenharmony_ci */ 57762306a36Sopenharmony_ci if (dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(28)) || 57862306a36Sopenharmony_ci dma_set_mask(&pdev->dev, DMA_BIT_MASK(28))) { 57962306a36Sopenharmony_ci pr_err("No usable DMA configuration\n"); 58062306a36Sopenharmony_ci pci_disable_device(pdev); 58162306a36Sopenharmony_ci return -EIO; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci i = pci_request_regions(pdev, "wanXL"); 58562306a36Sopenharmony_ci if (i) { 58662306a36Sopenharmony_ci pci_disable_device(pdev); 58762306a36Sopenharmony_ci return i; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci switch (pdev->device) { 59162306a36Sopenharmony_ci case PCI_DEVICE_ID_SBE_WANXL100: 59262306a36Sopenharmony_ci ports = 1; 59362306a36Sopenharmony_ci break; 59462306a36Sopenharmony_ci case PCI_DEVICE_ID_SBE_WANXL200: 59562306a36Sopenharmony_ci ports = 2; 59662306a36Sopenharmony_ci break; 59762306a36Sopenharmony_ci default: 59862306a36Sopenharmony_ci ports = 4; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci card = kzalloc(struct_size(card, ports, ports), GFP_KERNEL); 60262306a36Sopenharmony_ci if (!card) { 60362306a36Sopenharmony_ci pci_release_regions(pdev); 60462306a36Sopenharmony_ci pci_disable_device(pdev); 60562306a36Sopenharmony_ci return -ENOBUFS; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci pci_set_drvdata(pdev, card); 60962306a36Sopenharmony_ci card->pdev = pdev; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci card->status = dma_alloc_coherent(&pdev->dev, 61262306a36Sopenharmony_ci sizeof(struct card_status), 61362306a36Sopenharmony_ci &card->status_address, GFP_KERNEL); 61462306a36Sopenharmony_ci if (!card->status) { 61562306a36Sopenharmony_ci wanxl_pci_remove_one(pdev); 61662306a36Sopenharmony_ci return -ENOBUFS; 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci#ifdef DEBUG_PCI 62062306a36Sopenharmony_ci printk(KERN_DEBUG "wanXL %s: pci_alloc_consistent() returned memory" 62162306a36Sopenharmony_ci " at 0x%LX\n", pci_name(pdev), 62262306a36Sopenharmony_ci (unsigned long long)card->status_address); 62362306a36Sopenharmony_ci#endif 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci /* FIXME when PCI/DMA subsystems are fixed. 62662306a36Sopenharmony_ci * We set both dma_mask and consistent_dma_mask back to 32 bits 62762306a36Sopenharmony_ci * to indicate the card can do 32-bit DMA addressing 62862306a36Sopenharmony_ci */ 62962306a36Sopenharmony_ci if (dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)) || 63062306a36Sopenharmony_ci dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) { 63162306a36Sopenharmony_ci pr_err("No usable DMA configuration\n"); 63262306a36Sopenharmony_ci wanxl_pci_remove_one(pdev); 63362306a36Sopenharmony_ci return -EIO; 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci /* set up PLX mapping */ 63762306a36Sopenharmony_ci plx_phy = pci_resource_start(pdev, 0); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci card->plx = ioremap(plx_phy, 0x70); 64062306a36Sopenharmony_ci if (!card->plx) { 64162306a36Sopenharmony_ci pr_err("ioremap() failed\n"); 64262306a36Sopenharmony_ci wanxl_pci_remove_one(pdev); 64362306a36Sopenharmony_ci return -EFAULT; 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci#if RESET_WHILE_LOADING 64762306a36Sopenharmony_ci wanxl_reset(card); 64862306a36Sopenharmony_ci#endif 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci timeout = jiffies + 20 * HZ; 65162306a36Sopenharmony_ci while ((stat = readl(card->plx + PLX_MAILBOX_0)) != 0) { 65262306a36Sopenharmony_ci if (time_before(timeout, jiffies)) { 65362306a36Sopenharmony_ci pr_warn("%s: timeout waiting for PUTS to complete\n", 65462306a36Sopenharmony_ci pci_name(pdev)); 65562306a36Sopenharmony_ci wanxl_pci_remove_one(pdev); 65662306a36Sopenharmony_ci return -ENODEV; 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci switch (stat & 0xC0) { 66062306a36Sopenharmony_ci case 0x00: /* hmm - PUTS completed with non-zero code? */ 66162306a36Sopenharmony_ci case 0x80: /* PUTS still testing the hardware */ 66262306a36Sopenharmony_ci break; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci default: 66562306a36Sopenharmony_ci pr_warn("%s: PUTS test 0x%X failed\n", 66662306a36Sopenharmony_ci pci_name(pdev), stat & 0x30); 66762306a36Sopenharmony_ci wanxl_pci_remove_one(pdev); 66862306a36Sopenharmony_ci return -ENODEV; 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci schedule(); 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci /* get on-board memory size (PUTS detects no more than 4 MB) */ 67562306a36Sopenharmony_ci ramsize = readl(card->plx + PLX_MAILBOX_2) & MBX2_MEMSZ_MASK; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci /* set up on-board RAM mapping */ 67862306a36Sopenharmony_ci mem_phy = pci_resource_start(pdev, 2); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci /* sanity check the board's reported memory size */ 68162306a36Sopenharmony_ci if (ramsize < BUFFERS_ADDR + 68262306a36Sopenharmony_ci (TX_BUFFERS + RX_BUFFERS) * BUFFER_LENGTH * ports) { 68362306a36Sopenharmony_ci pr_warn("%s: no enough on-board RAM (%u bytes detected, %u bytes required)\n", 68462306a36Sopenharmony_ci pci_name(pdev), ramsize, 68562306a36Sopenharmony_ci BUFFERS_ADDR + 68662306a36Sopenharmony_ci (TX_BUFFERS + RX_BUFFERS) * BUFFER_LENGTH * ports); 68762306a36Sopenharmony_ci wanxl_pci_remove_one(pdev); 68862306a36Sopenharmony_ci return -ENODEV; 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci if (wanxl_puts_command(card, MBX1_CMD_BSWAP)) { 69262306a36Sopenharmony_ci pr_warn("%s: unable to Set Byte Swap Mode\n", pci_name(pdev)); 69362306a36Sopenharmony_ci wanxl_pci_remove_one(pdev); 69462306a36Sopenharmony_ci return -ENODEV; 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci for (i = 0; i < RX_QUEUE_LENGTH; i++) { 69862306a36Sopenharmony_ci struct sk_buff *skb = dev_alloc_skb(BUFFER_LENGTH); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci card->rx_skbs[i] = skb; 70162306a36Sopenharmony_ci if (skb) 70262306a36Sopenharmony_ci card->status->rx_descs[i].address = 70362306a36Sopenharmony_ci dma_map_single(&card->pdev->dev, skb->data, 70462306a36Sopenharmony_ci BUFFER_LENGTH, DMA_FROM_DEVICE); 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci mem = ioremap(mem_phy, PDM_OFFSET + sizeof(firmware)); 70862306a36Sopenharmony_ci if (!mem) { 70962306a36Sopenharmony_ci pr_err("ioremap() failed\n"); 71062306a36Sopenharmony_ci wanxl_pci_remove_one(pdev); 71162306a36Sopenharmony_ci return -EFAULT; 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci for (i = 0; i < sizeof(firmware); i += 4) 71562306a36Sopenharmony_ci writel(ntohl(*(__be32 *)(firmware + i)), mem + PDM_OFFSET + i); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci for (i = 0; i < ports; i++) 71862306a36Sopenharmony_ci writel(card->status_address + 71962306a36Sopenharmony_ci (void *)&card->status->port_status[i] - 72062306a36Sopenharmony_ci (void *)card->status, mem + PDM_OFFSET + 4 + i * 4); 72162306a36Sopenharmony_ci writel(card->status_address, mem + PDM_OFFSET + 20); 72262306a36Sopenharmony_ci writel(PDM_OFFSET, mem); 72362306a36Sopenharmony_ci iounmap(mem); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci writel(0, card->plx + PLX_MAILBOX_5); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci if (wanxl_puts_command(card, MBX1_CMD_ABORTJ)) { 72862306a36Sopenharmony_ci pr_warn("%s: unable to Abort and Jump\n", pci_name(pdev)); 72962306a36Sopenharmony_ci wanxl_pci_remove_one(pdev); 73062306a36Sopenharmony_ci return -ENODEV; 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci timeout = jiffies + 5 * HZ; 73462306a36Sopenharmony_ci do { 73562306a36Sopenharmony_ci stat = readl(card->plx + PLX_MAILBOX_5); 73662306a36Sopenharmony_ci if (stat) 73762306a36Sopenharmony_ci break; 73862306a36Sopenharmony_ci schedule(); 73962306a36Sopenharmony_ci } while (time_after(timeout, jiffies)); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci if (!stat) { 74262306a36Sopenharmony_ci pr_warn("%s: timeout while initializing card firmware\n", 74362306a36Sopenharmony_ci pci_name(pdev)); 74462306a36Sopenharmony_ci wanxl_pci_remove_one(pdev); 74562306a36Sopenharmony_ci return -ENODEV; 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci#if DETECT_RAM 74962306a36Sopenharmony_ci ramsize = stat; 75062306a36Sopenharmony_ci#endif 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci pr_info("%s: at 0x%X, %u KB of RAM at 0x%X, irq %u\n", 75362306a36Sopenharmony_ci pci_name(pdev), plx_phy, ramsize / 1024, mem_phy, pdev->irq); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci /* Allocate IRQ */ 75662306a36Sopenharmony_ci if (request_irq(pdev->irq, wanxl_intr, IRQF_SHARED, "wanXL", card)) { 75762306a36Sopenharmony_ci pr_warn("%s: could not allocate IRQ%i\n", 75862306a36Sopenharmony_ci pci_name(pdev), pdev->irq); 75962306a36Sopenharmony_ci wanxl_pci_remove_one(pdev); 76062306a36Sopenharmony_ci return -EBUSY; 76162306a36Sopenharmony_ci } 76262306a36Sopenharmony_ci card->irq = pdev->irq; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci for (i = 0; i < ports; i++) { 76562306a36Sopenharmony_ci hdlc_device *hdlc; 76662306a36Sopenharmony_ci struct port *port = &card->ports[i]; 76762306a36Sopenharmony_ci struct net_device *dev = alloc_hdlcdev(port); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci if (!dev) { 77062306a36Sopenharmony_ci pr_err("%s: unable to allocate memory\n", 77162306a36Sopenharmony_ci pci_name(pdev)); 77262306a36Sopenharmony_ci wanxl_pci_remove_one(pdev); 77362306a36Sopenharmony_ci return -ENOMEM; 77462306a36Sopenharmony_ci } 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci port->dev = dev; 77762306a36Sopenharmony_ci hdlc = dev_to_hdlc(dev); 77862306a36Sopenharmony_ci spin_lock_init(&port->lock); 77962306a36Sopenharmony_ci dev->tx_queue_len = 50; 78062306a36Sopenharmony_ci dev->netdev_ops = &wanxl_ops; 78162306a36Sopenharmony_ci hdlc->attach = wanxl_attach; 78262306a36Sopenharmony_ci hdlc->xmit = wanxl_xmit; 78362306a36Sopenharmony_ci port->card = card; 78462306a36Sopenharmony_ci port->node = i; 78562306a36Sopenharmony_ci get_status(port)->clocking = CLOCK_EXT; 78662306a36Sopenharmony_ci if (register_hdlc_device(dev)) { 78762306a36Sopenharmony_ci pr_err("%s: unable to register hdlc device\n", 78862306a36Sopenharmony_ci pci_name(pdev)); 78962306a36Sopenharmony_ci free_netdev(dev); 79062306a36Sopenharmony_ci wanxl_pci_remove_one(pdev); 79162306a36Sopenharmony_ci return -ENOBUFS; 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci card->n_ports++; 79462306a36Sopenharmony_ci } 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci pr_info("%s: port", pci_name(pdev)); 79762306a36Sopenharmony_ci for (i = 0; i < ports; i++) 79862306a36Sopenharmony_ci pr_cont("%s #%i: %s", 79962306a36Sopenharmony_ci i ? "," : "", i, card->ports[i].dev->name); 80062306a36Sopenharmony_ci pr_cont("\n"); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci for (i = 0; i < ports; i++) 80362306a36Sopenharmony_ci wanxl_cable_intr(&card->ports[i]); /* get carrier status etc.*/ 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci return 0; 80662306a36Sopenharmony_ci} 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_cistatic const struct pci_device_id wanxl_pci_tbl[] = { 80962306a36Sopenharmony_ci { PCI_VENDOR_ID_SBE, PCI_DEVICE_ID_SBE_WANXL100, PCI_ANY_ID, 81062306a36Sopenharmony_ci PCI_ANY_ID, 0, 0, 0 }, 81162306a36Sopenharmony_ci { PCI_VENDOR_ID_SBE, PCI_DEVICE_ID_SBE_WANXL200, PCI_ANY_ID, 81262306a36Sopenharmony_ci PCI_ANY_ID, 0, 0, 0 }, 81362306a36Sopenharmony_ci { PCI_VENDOR_ID_SBE, PCI_DEVICE_ID_SBE_WANXL400, PCI_ANY_ID, 81462306a36Sopenharmony_ci PCI_ANY_ID, 0, 0, 0 }, 81562306a36Sopenharmony_ci { 0, } 81662306a36Sopenharmony_ci}; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_cistatic struct pci_driver wanxl_pci_driver = { 81962306a36Sopenharmony_ci .name = "wanXL", 82062306a36Sopenharmony_ci .id_table = wanxl_pci_tbl, 82162306a36Sopenharmony_ci .probe = wanxl_pci_init_one, 82262306a36Sopenharmony_ci .remove = wanxl_pci_remove_one, 82362306a36Sopenharmony_ci}; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_cistatic int __init wanxl_init_module(void) 82662306a36Sopenharmony_ci{ 82762306a36Sopenharmony_ci#ifdef MODULE 82862306a36Sopenharmony_ci pr_info("%s\n", version); 82962306a36Sopenharmony_ci#endif 83062306a36Sopenharmony_ci return pci_register_driver(&wanxl_pci_driver); 83162306a36Sopenharmony_ci} 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_cistatic void __exit wanxl_cleanup_module(void) 83462306a36Sopenharmony_ci{ 83562306a36Sopenharmony_ci pci_unregister_driver(&wanxl_pci_driver); 83662306a36Sopenharmony_ci} 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ciMODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>"); 83962306a36Sopenharmony_ciMODULE_DESCRIPTION("SBE Inc. wanXL serial port driver"); 84062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 84162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, wanxl_pci_tbl); 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_cimodule_init(wanxl_init_module); 84462306a36Sopenharmony_cimodule_exit(wanxl_cleanup_module); 845