162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Moxa C101 synchronous serial card driver for Linux 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2000-2003 Krzysztof Halasa <khc@pm.waw.pl> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * For information see <https://www.kernel.org/pub/linux/utils/net/hdlc/> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Sources of information: 1062306a36Sopenharmony_ci * Hitachi HD64570 SCA User's Manual 1162306a36Sopenharmony_ci * Moxa C101 User's Manual 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/kernel.h> 1862306a36Sopenharmony_ci#include <linux/capability.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/types.h> 2162306a36Sopenharmony_ci#include <linux/string.h> 2262306a36Sopenharmony_ci#include <linux/errno.h> 2362306a36Sopenharmony_ci#include <linux/init.h> 2462306a36Sopenharmony_ci#include <linux/netdevice.h> 2562306a36Sopenharmony_ci#include <linux/hdlc.h> 2662306a36Sopenharmony_ci#include <linux/delay.h> 2762306a36Sopenharmony_ci#include <asm/io.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include "hd64570.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic const char *version = "Moxa C101 driver version: 1.15"; 3262306a36Sopenharmony_cistatic const char *devname = "C101"; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#undef DEBUG_PKT 3562306a36Sopenharmony_ci#define DEBUG_RINGS 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define C101_PAGE 0x1D00 3862306a36Sopenharmony_ci#define C101_DTR 0x1E00 3962306a36Sopenharmony_ci#define C101_SCA 0x1F00 4062306a36Sopenharmony_ci#define C101_WINDOW_SIZE 0x2000 4162306a36Sopenharmony_ci#define C101_MAPPED_RAM_SIZE 0x4000 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define RAM_SIZE (256 * 1024) 4462306a36Sopenharmony_ci#define TX_RING_BUFFERS 10 4562306a36Sopenharmony_ci#define RX_RING_BUFFERS ((RAM_SIZE - C101_WINDOW_SIZE) / \ 4662306a36Sopenharmony_ci (sizeof(pkt_desc) + HDLC_MAX_MRU) - TX_RING_BUFFERS) 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#define CLOCK_BASE 9830400 /* 9.8304 MHz */ 4962306a36Sopenharmony_ci#define PAGE0_ALWAYS_MAPPED 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic char *hw; /* pointer to hw=xxx command line string */ 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_citypedef struct card_s { 5462306a36Sopenharmony_ci struct net_device *dev; 5562306a36Sopenharmony_ci spinlock_t lock; /* TX lock */ 5662306a36Sopenharmony_ci u8 __iomem *win0base; /* ISA window base address */ 5762306a36Sopenharmony_ci u32 phy_winbase; /* ISA physical base address */ 5862306a36Sopenharmony_ci sync_serial_settings settings; 5962306a36Sopenharmony_ci int rxpart; /* partial frame received, next frame invalid*/ 6062306a36Sopenharmony_ci unsigned short encoding; 6162306a36Sopenharmony_ci unsigned short parity; 6262306a36Sopenharmony_ci u16 rx_ring_buffers; /* number of buffers in a ring */ 6362306a36Sopenharmony_ci u16 tx_ring_buffers; 6462306a36Sopenharmony_ci u16 buff_offset; /* offset of first buffer of first channel */ 6562306a36Sopenharmony_ci u16 rxin; /* rx ring buffer 'in' pointer */ 6662306a36Sopenharmony_ci u16 txin; /* tx ring buffer 'in' and 'last' pointers */ 6762306a36Sopenharmony_ci u16 txlast; 6862306a36Sopenharmony_ci u8 rxs, txs, tmc; /* SCA registers */ 6962306a36Sopenharmony_ci u8 irq; /* IRQ (3-15) */ 7062306a36Sopenharmony_ci u8 page; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci struct card_s *next_card; 7362306a36Sopenharmony_ci} card_t; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_citypedef card_t port_t; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic card_t *first_card; 7862306a36Sopenharmony_cistatic card_t **new_card = &first_card; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci#define sca_in(reg, card) readb((card)->win0base + C101_SCA + (reg)) 8162306a36Sopenharmony_ci#define sca_out(value, reg, card) writeb(value, (card)->win0base + C101_SCA + (reg)) 8262306a36Sopenharmony_ci#define sca_inw(reg, card) readw((card)->win0base + C101_SCA + (reg)) 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/* EDA address register must be set in EDAL, EDAH order - 8 bit ISA bus */ 8562306a36Sopenharmony_ci#define sca_outw(value, reg, card) do { \ 8662306a36Sopenharmony_ci writeb(value & 0xFF, (card)->win0base + C101_SCA + (reg)); \ 8762306a36Sopenharmony_ci writeb((value >> 8) & 0xFF, (card)->win0base + C101_SCA + (reg + 1));\ 8862306a36Sopenharmony_ci} while (0) 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci#define port_to_card(port) (port) 9162306a36Sopenharmony_ci#define log_node(port) (0) 9262306a36Sopenharmony_ci#define phy_node(port) (0) 9362306a36Sopenharmony_ci#define winsize(card) (C101_WINDOW_SIZE) 9462306a36Sopenharmony_ci#define win0base(card) ((card)->win0base) 9562306a36Sopenharmony_ci#define winbase(card) ((card)->win0base + 0x2000) 9662306a36Sopenharmony_ci#define get_port(card, port) (card) 9762306a36Sopenharmony_cistatic void sca_msci_intr(port_t *port); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic inline u8 sca_get_page(card_t *card) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci return card->page; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic inline void openwin(card_t *card, u8 page) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci card->page = page; 10762306a36Sopenharmony_ci writeb(page, card->win0base + C101_PAGE); 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci#include "hd64570.c" 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic inline void set_carrier(port_t *port) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci if (!(sca_in(MSCI1_OFFSET + ST3, port) & ST3_DCD)) 11562306a36Sopenharmony_ci netif_carrier_on(port_to_dev(port)); 11662306a36Sopenharmony_ci else 11762306a36Sopenharmony_ci netif_carrier_off(port_to_dev(port)); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic void sca_msci_intr(port_t *port) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci u8 stat = sca_in(MSCI0_OFFSET + ST1, port); /* read MSCI ST1 status */ 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* Reset MSCI TX underrun and CDCD (ignored) status bit */ 12562306a36Sopenharmony_ci sca_out(stat & (ST1_UDRN | ST1_CDCD), MSCI0_OFFSET + ST1, port); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (stat & ST1_UDRN) { 12862306a36Sopenharmony_ci /* TX Underrun error detected */ 12962306a36Sopenharmony_ci port_to_dev(port)->stats.tx_errors++; 13062306a36Sopenharmony_ci port_to_dev(port)->stats.tx_fifo_errors++; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci stat = sca_in(MSCI1_OFFSET + ST1, port); /* read MSCI1 ST1 status */ 13462306a36Sopenharmony_ci /* Reset MSCI CDCD status bit - uses ch#2 DCD input */ 13562306a36Sopenharmony_ci sca_out(stat & ST1_CDCD, MSCI1_OFFSET + ST1, port); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (stat & ST1_CDCD) 13862306a36Sopenharmony_ci set_carrier(port); 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic void c101_set_iface(port_t *port) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci u8 rxs = port->rxs & CLK_BRG_MASK; 14462306a36Sopenharmony_ci u8 txs = port->txs & CLK_BRG_MASK; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci switch (port->settings.clock_type) { 14762306a36Sopenharmony_ci case CLOCK_INT: 14862306a36Sopenharmony_ci rxs |= CLK_BRG_RX; /* TX clock */ 14962306a36Sopenharmony_ci txs |= CLK_RXCLK_TX; /* BRG output */ 15062306a36Sopenharmony_ci break; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci case CLOCK_TXINT: 15362306a36Sopenharmony_ci rxs |= CLK_LINE_RX; /* RXC input */ 15462306a36Sopenharmony_ci txs |= CLK_BRG_TX; /* BRG output */ 15562306a36Sopenharmony_ci break; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci case CLOCK_TXFROMRX: 15862306a36Sopenharmony_ci rxs |= CLK_LINE_RX; /* RXC input */ 15962306a36Sopenharmony_ci txs |= CLK_RXCLK_TX; /* RX clock */ 16062306a36Sopenharmony_ci break; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci default: /* EXTernal clock */ 16362306a36Sopenharmony_ci rxs |= CLK_LINE_RX; /* RXC input */ 16462306a36Sopenharmony_ci txs |= CLK_LINE_TX; /* TXC input */ 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci port->rxs = rxs; 16862306a36Sopenharmony_ci port->txs = txs; 16962306a36Sopenharmony_ci sca_out(rxs, MSCI1_OFFSET + RXS, port); 17062306a36Sopenharmony_ci sca_out(txs, MSCI1_OFFSET + TXS, port); 17162306a36Sopenharmony_ci sca_set_port(port); 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic int c101_open(struct net_device *dev) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci port_t *port = dev_to_port(dev); 17762306a36Sopenharmony_ci int result; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci result = hdlc_open(dev); 18062306a36Sopenharmony_ci if (result) 18162306a36Sopenharmony_ci return result; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci writeb(1, port->win0base + C101_DTR); 18462306a36Sopenharmony_ci sca_out(0, MSCI1_OFFSET + CTL, port); /* RTS uses ch#2 output */ 18562306a36Sopenharmony_ci sca_open(dev); 18662306a36Sopenharmony_ci /* DCD is connected to port 2 !@#$%^& - disable MSCI0 CDCD interrupt */ 18762306a36Sopenharmony_ci sca_out(IE1_UDRN, MSCI0_OFFSET + IE1, port); 18862306a36Sopenharmony_ci sca_out(IE0_TXINT, MSCI0_OFFSET + IE0, port); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci set_carrier(port); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci /* enable MSCI1 CDCD interrupt */ 19362306a36Sopenharmony_ci sca_out(IE1_CDCD, MSCI1_OFFSET + IE1, port); 19462306a36Sopenharmony_ci sca_out(IE0_RXINTA, MSCI1_OFFSET + IE0, port); 19562306a36Sopenharmony_ci sca_out(0x48, IER0, port); /* TXINT #0 and RXINT #1 */ 19662306a36Sopenharmony_ci c101_set_iface(port); 19762306a36Sopenharmony_ci return 0; 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic int c101_close(struct net_device *dev) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci port_t *port = dev_to_port(dev); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci sca_close(dev); 20562306a36Sopenharmony_ci writeb(0, port->win0base + C101_DTR); 20662306a36Sopenharmony_ci sca_out(CTL_NORTS, MSCI1_OFFSET + CTL, port); 20762306a36Sopenharmony_ci hdlc_close(dev); 20862306a36Sopenharmony_ci return 0; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic int c101_siocdevprivate(struct net_device *dev, struct ifreq *ifr, 21262306a36Sopenharmony_ci void __user *data, int cmd) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci#ifdef DEBUG_RINGS 21562306a36Sopenharmony_ci port_t *port = dev_to_port(dev); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (cmd == SIOCDEVPRIVATE) { 21862306a36Sopenharmony_ci sca_dump_rings(dev); 21962306a36Sopenharmony_ci printk(KERN_DEBUG "MSCI1: ST: %02x %02x %02x %02x\n", 22062306a36Sopenharmony_ci sca_in(MSCI1_OFFSET + ST0, port), 22162306a36Sopenharmony_ci sca_in(MSCI1_OFFSET + ST1, port), 22262306a36Sopenharmony_ci sca_in(MSCI1_OFFSET + ST2, port), 22362306a36Sopenharmony_ci sca_in(MSCI1_OFFSET + ST3, port)); 22462306a36Sopenharmony_ci return 0; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci#endif 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci return -EOPNOTSUPP; 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic int c101_ioctl(struct net_device *dev, struct if_settings *ifs) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci const size_t size = sizeof(sync_serial_settings); 23462306a36Sopenharmony_ci sync_serial_settings new_line; 23562306a36Sopenharmony_ci sync_serial_settings __user *line = ifs->ifs_ifsu.sync; 23662306a36Sopenharmony_ci port_t *port = dev_to_port(dev); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci switch (ifs->type) { 23962306a36Sopenharmony_ci case IF_GET_IFACE: 24062306a36Sopenharmony_ci ifs->type = IF_IFACE_SYNC_SERIAL; 24162306a36Sopenharmony_ci if (ifs->size < size) { 24262306a36Sopenharmony_ci ifs->size = size; /* data size wanted */ 24362306a36Sopenharmony_ci return -ENOBUFS; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci if (copy_to_user(line, &port->settings, size)) 24662306a36Sopenharmony_ci return -EFAULT; 24762306a36Sopenharmony_ci return 0; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci case IF_IFACE_SYNC_SERIAL: 25062306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 25162306a36Sopenharmony_ci return -EPERM; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (copy_from_user(&new_line, line, size)) 25462306a36Sopenharmony_ci return -EFAULT; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (new_line.clock_type != CLOCK_EXT && 25762306a36Sopenharmony_ci new_line.clock_type != CLOCK_TXFROMRX && 25862306a36Sopenharmony_ci new_line.clock_type != CLOCK_INT && 25962306a36Sopenharmony_ci new_line.clock_type != CLOCK_TXINT) 26062306a36Sopenharmony_ci return -EINVAL; /* No such clock setting */ 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (new_line.loopback != 0 && new_line.loopback != 1) 26362306a36Sopenharmony_ci return -EINVAL; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci memcpy(&port->settings, &new_line, size); /* Update settings */ 26662306a36Sopenharmony_ci c101_set_iface(port); 26762306a36Sopenharmony_ci return 0; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci default: 27062306a36Sopenharmony_ci return hdlc_ioctl(dev, ifs); 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic void c101_destroy_card(card_t *card) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci readb(card->win0base + C101_PAGE); /* Resets SCA? */ 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (card->irq) 27962306a36Sopenharmony_ci free_irq(card->irq, card); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (card->win0base) { 28262306a36Sopenharmony_ci iounmap(card->win0base); 28362306a36Sopenharmony_ci release_mem_region(card->phy_winbase, C101_MAPPED_RAM_SIZE); 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci free_netdev(card->dev); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci kfree(card); 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic const struct net_device_ops c101_ops = { 29262306a36Sopenharmony_ci .ndo_open = c101_open, 29362306a36Sopenharmony_ci .ndo_stop = c101_close, 29462306a36Sopenharmony_ci .ndo_start_xmit = hdlc_start_xmit, 29562306a36Sopenharmony_ci .ndo_siocwandev = c101_ioctl, 29662306a36Sopenharmony_ci .ndo_siocdevprivate = c101_siocdevprivate, 29762306a36Sopenharmony_ci}; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic int __init c101_run(unsigned long irq, unsigned long winbase) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci struct net_device *dev; 30262306a36Sopenharmony_ci hdlc_device *hdlc; 30362306a36Sopenharmony_ci card_t *card; 30462306a36Sopenharmony_ci int result; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (irq < 3 || irq > 15 || irq == 6) /* FIXME */ { 30762306a36Sopenharmony_ci pr_err("invalid IRQ value\n"); 30862306a36Sopenharmony_ci return -ENODEV; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (winbase < 0xC0000 || winbase > 0xDFFFF || (winbase & 0x3FFF) != 0) { 31262306a36Sopenharmony_ci pr_err("invalid RAM value\n"); 31362306a36Sopenharmony_ci return -ENODEV; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci card = kzalloc(sizeof(card_t), GFP_KERNEL); 31762306a36Sopenharmony_ci if (!card) 31862306a36Sopenharmony_ci return -ENOBUFS; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci card->dev = alloc_hdlcdev(card); 32162306a36Sopenharmony_ci if (!card->dev) { 32262306a36Sopenharmony_ci pr_err("unable to allocate memory\n"); 32362306a36Sopenharmony_ci kfree(card); 32462306a36Sopenharmony_ci return -ENOBUFS; 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (request_irq(irq, sca_intr, 0, devname, card)) { 32862306a36Sopenharmony_ci pr_err("could not allocate IRQ\n"); 32962306a36Sopenharmony_ci c101_destroy_card(card); 33062306a36Sopenharmony_ci return -EBUSY; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci card->irq = irq; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (!request_mem_region(winbase, C101_MAPPED_RAM_SIZE, devname)) { 33562306a36Sopenharmony_ci pr_err("could not request RAM window\n"); 33662306a36Sopenharmony_ci c101_destroy_card(card); 33762306a36Sopenharmony_ci return -EBUSY; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci card->phy_winbase = winbase; 34062306a36Sopenharmony_ci card->win0base = ioremap(winbase, C101_MAPPED_RAM_SIZE); 34162306a36Sopenharmony_ci if (!card->win0base) { 34262306a36Sopenharmony_ci pr_err("could not map I/O address\n"); 34362306a36Sopenharmony_ci c101_destroy_card(card); 34462306a36Sopenharmony_ci return -EFAULT; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci card->tx_ring_buffers = TX_RING_BUFFERS; 34862306a36Sopenharmony_ci card->rx_ring_buffers = RX_RING_BUFFERS; 34962306a36Sopenharmony_ci card->buff_offset = C101_WINDOW_SIZE; /* Bytes 1D00-1FFF reserved */ 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci readb(card->win0base + C101_PAGE); /* Resets SCA? */ 35262306a36Sopenharmony_ci udelay(100); 35362306a36Sopenharmony_ci writeb(0, card->win0base + C101_PAGE); 35462306a36Sopenharmony_ci writeb(0, card->win0base + C101_DTR); /* Power-up for RAM? */ 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci sca_init(card, 0); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci dev = port_to_dev(card); 35962306a36Sopenharmony_ci hdlc = dev_to_hdlc(dev); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci spin_lock_init(&card->lock); 36262306a36Sopenharmony_ci dev->irq = irq; 36362306a36Sopenharmony_ci dev->mem_start = winbase; 36462306a36Sopenharmony_ci dev->mem_end = winbase + C101_MAPPED_RAM_SIZE - 1; 36562306a36Sopenharmony_ci dev->tx_queue_len = 50; 36662306a36Sopenharmony_ci dev->netdev_ops = &c101_ops; 36762306a36Sopenharmony_ci hdlc->attach = sca_attach; 36862306a36Sopenharmony_ci hdlc->xmit = sca_xmit; 36962306a36Sopenharmony_ci card->settings.clock_type = CLOCK_EXT; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci result = register_hdlc_device(dev); 37262306a36Sopenharmony_ci if (result) { 37362306a36Sopenharmony_ci pr_warn("unable to register hdlc device\n"); 37462306a36Sopenharmony_ci c101_destroy_card(card); 37562306a36Sopenharmony_ci return result; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci sca_init_port(card); /* Set up C101 memory */ 37962306a36Sopenharmony_ci set_carrier(card); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci netdev_info(dev, "Moxa C101 on IRQ%u, using %u TX + %u RX packets rings\n", 38262306a36Sopenharmony_ci card->irq, card->tx_ring_buffers, card->rx_ring_buffers); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci *new_card = card; 38562306a36Sopenharmony_ci new_card = &card->next_card; 38662306a36Sopenharmony_ci return 0; 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cistatic int __init c101_init(void) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci if (!hw) { 39262306a36Sopenharmony_ci#ifdef MODULE 39362306a36Sopenharmony_ci pr_info("no card initialized\n"); 39462306a36Sopenharmony_ci#endif 39562306a36Sopenharmony_ci return -EINVAL; /* no parameters specified, abort */ 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci pr_info("%s\n", version); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci do { 40162306a36Sopenharmony_ci unsigned long irq, ram; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci irq = simple_strtoul(hw, &hw, 0); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (*hw++ != ',') 40662306a36Sopenharmony_ci break; 40762306a36Sopenharmony_ci ram = simple_strtoul(hw, &hw, 0); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (*hw == ':' || *hw == '\x0') 41062306a36Sopenharmony_ci c101_run(irq, ram); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (*hw == '\x0') 41362306a36Sopenharmony_ci return first_card ? 0 : -EINVAL; 41462306a36Sopenharmony_ci } while (*hw++ == ':'); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci pr_err("invalid hardware parameters\n"); 41762306a36Sopenharmony_ci return first_card ? 0 : -EINVAL; 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_cistatic void __exit c101_cleanup(void) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci card_t *card = first_card; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci while (card) { 42562306a36Sopenharmony_ci card_t *ptr = card; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci card = card->next_card; 42862306a36Sopenharmony_ci unregister_hdlc_device(port_to_dev(ptr)); 42962306a36Sopenharmony_ci c101_destroy_card(ptr); 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cimodule_init(c101_init); 43462306a36Sopenharmony_cimodule_exit(c101_cleanup); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ciMODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>"); 43762306a36Sopenharmony_ciMODULE_DESCRIPTION("Moxa C101 serial port driver"); 43862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 43962306a36Sopenharmony_cimodule_param(hw, charp, 0444); 44062306a36Sopenharmony_ciMODULE_PARM_DESC(hw, "irq,ram:irq,..."); 441