18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* $Id: cosa.c,v 1.31 2000/03/08 17:47:16 kas Exp $ */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci/* 58c2ecf20Sopenharmony_ci * Copyright (C) 1995-1997 Jan "Yenya" Kasprzak <kas@fi.muni.cz> 68c2ecf20Sopenharmony_ci * Generic HDLC port Copyright (C) 2008 Krzysztof Halasa <khc@pm.waw.pl> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci/* 108c2ecf20Sopenharmony_ci * The driver for the SRP and COSA synchronous serial cards. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * HARDWARE INFO 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Both cards are developed at the Institute of Computer Science, 158c2ecf20Sopenharmony_ci * Masaryk University (https://www.ics.muni.cz/). The hardware is 168c2ecf20Sopenharmony_ci * developed by Jiri Novotny <novotny@ics.muni.cz>. More information 178c2ecf20Sopenharmony_ci * and the photo of both cards is available at 188c2ecf20Sopenharmony_ci * http://www.pavoucek.cz/cosa.html. The card documentation, firmwares 198c2ecf20Sopenharmony_ci * and other goods can be downloaded from ftp://ftp.ics.muni.cz/pub/cosa/. 208c2ecf20Sopenharmony_ci * For Linux-specific utilities, see below in the "Software info" section. 218c2ecf20Sopenharmony_ci * If you want to order the card, contact Jiri Novotny. 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * The SRP (serial port?, the Czech word "srp" means "sickle") card 248c2ecf20Sopenharmony_ci * is a 2-port intelligent (with its own 8-bit CPU) synchronous serial card 258c2ecf20Sopenharmony_ci * with V.24 interfaces up to 80kb/s each. 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * The COSA (communication serial adapter?, the Czech word "kosa" means 288c2ecf20Sopenharmony_ci * "scythe") is a next-generation sync/async board with two interfaces 298c2ecf20Sopenharmony_ci * - currently any of V.24, X.21, V.35 and V.36 can be selected. 308c2ecf20Sopenharmony_ci * It has a 16-bit SAB80166 CPU and can do up to 10 Mb/s per channel. 318c2ecf20Sopenharmony_ci * The 8-channels version is in development. 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * Both types have downloadable firmware and communicate via ISA DMA. 348c2ecf20Sopenharmony_ci * COSA can be also a bus-mastering device. 358c2ecf20Sopenharmony_ci * 368c2ecf20Sopenharmony_ci * SOFTWARE INFO 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * The homepage of the Linux driver is at https://www.fi.muni.cz/~kas/cosa/. 398c2ecf20Sopenharmony_ci * The CVS tree of Linux driver can be viewed there, as well as the 408c2ecf20Sopenharmony_ci * firmware binaries and user-space utilities for downloading the firmware 418c2ecf20Sopenharmony_ci * into the card and setting up the card. 428c2ecf20Sopenharmony_ci * 438c2ecf20Sopenharmony_ci * The Linux driver (unlike the present *BSD drivers :-) can work even 448c2ecf20Sopenharmony_ci * for the COSA and SRP in one computer and allows each channel to work 458c2ecf20Sopenharmony_ci * in one of the two modes (character or network device). 468c2ecf20Sopenharmony_ci * 478c2ecf20Sopenharmony_ci * AUTHOR 488c2ecf20Sopenharmony_ci * 498c2ecf20Sopenharmony_ci * The Linux driver was written by Jan "Yenya" Kasprzak <kas@fi.muni.cz>. 508c2ecf20Sopenharmony_ci * 518c2ecf20Sopenharmony_ci * You can mail me bugfixes and even success reports. I am especially 528c2ecf20Sopenharmony_ci * interested in the SMP and/or muliti-channel success/failure reports 538c2ecf20Sopenharmony_ci * (I wonder if I did the locking properly :-). 548c2ecf20Sopenharmony_ci * 558c2ecf20Sopenharmony_ci * THE AUTHOR USED THE FOLLOWING SOURCES WHEN PROGRAMMING THE DRIVER 568c2ecf20Sopenharmony_ci * 578c2ecf20Sopenharmony_ci * The COSA/SRP NetBSD driver by Zdenek Salvet and Ivos Cernohlavek 588c2ecf20Sopenharmony_ci * The skeleton.c by Donald Becker 598c2ecf20Sopenharmony_ci * The SDL Riscom/N2 driver by Mike Natale 608c2ecf20Sopenharmony_ci * The Comtrol Hostess SV11 driver by Alan Cox 618c2ecf20Sopenharmony_ci * The Sync PPP/Cisco HDLC layer (syncppp.c) ported to Linux by Alan Cox 628c2ecf20Sopenharmony_ci */ 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci#include <linux/module.h> 678c2ecf20Sopenharmony_ci#include <linux/kernel.h> 688c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 698c2ecf20Sopenharmony_ci#include <linux/slab.h> 708c2ecf20Sopenharmony_ci#include <linux/poll.h> 718c2ecf20Sopenharmony_ci#include <linux/fs.h> 728c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 738c2ecf20Sopenharmony_ci#include <linux/delay.h> 748c2ecf20Sopenharmony_ci#include <linux/hdlc.h> 758c2ecf20Sopenharmony_ci#include <linux/errno.h> 768c2ecf20Sopenharmony_ci#include <linux/ioport.h> 778c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 788c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 798c2ecf20Sopenharmony_ci#include <linux/mutex.h> 808c2ecf20Sopenharmony_ci#include <linux/device.h> 818c2ecf20Sopenharmony_ci#include <asm/io.h> 828c2ecf20Sopenharmony_ci#include <asm/dma.h> 838c2ecf20Sopenharmony_ci#include <asm/byteorder.h> 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci#undef COSA_SLOW_IO /* for testing purposes only */ 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci#include "cosa.h" 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/* Maximum length of the identification string. */ 908c2ecf20Sopenharmony_ci#define COSA_MAX_ID_STRING 128 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* Maximum length of the channel name */ 938c2ecf20Sopenharmony_ci#define COSA_MAX_NAME (sizeof("cosaXXXcXXX")+1) 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/* Per-channel data structure */ 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistruct channel_data { 988c2ecf20Sopenharmony_ci int usage; /* Usage count; >0 for chrdev, -1 for netdev */ 998c2ecf20Sopenharmony_ci int num; /* Number of the channel */ 1008c2ecf20Sopenharmony_ci struct cosa_data *cosa; /* Pointer to the per-card structure */ 1018c2ecf20Sopenharmony_ci int txsize; /* Size of transmitted data */ 1028c2ecf20Sopenharmony_ci char *txbuf; /* Transmit buffer */ 1038c2ecf20Sopenharmony_ci char name[COSA_MAX_NAME]; /* channel name */ 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci /* The HW layer interface */ 1068c2ecf20Sopenharmony_ci /* routine called from the RX interrupt */ 1078c2ecf20Sopenharmony_ci char *(*setup_rx)(struct channel_data *channel, int size); 1088c2ecf20Sopenharmony_ci /* routine called when the RX is done (from the EOT interrupt) */ 1098c2ecf20Sopenharmony_ci int (*rx_done)(struct channel_data *channel); 1108c2ecf20Sopenharmony_ci /* routine called when the TX is done (from the EOT interrupt) */ 1118c2ecf20Sopenharmony_ci int (*tx_done)(struct channel_data *channel, int size); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* Character device parts */ 1148c2ecf20Sopenharmony_ci struct mutex rlock; 1158c2ecf20Sopenharmony_ci struct semaphore wsem; 1168c2ecf20Sopenharmony_ci char *rxdata; 1178c2ecf20Sopenharmony_ci int rxsize; 1188c2ecf20Sopenharmony_ci wait_queue_head_t txwaitq, rxwaitq; 1198c2ecf20Sopenharmony_ci int tx_status, rx_status; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci /* generic HDLC device parts */ 1228c2ecf20Sopenharmony_ci struct net_device *netdev; 1238c2ecf20Sopenharmony_ci struct sk_buff *rx_skb, *tx_skb; 1248c2ecf20Sopenharmony_ci}; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci/* cosa->firmware_status bits */ 1278c2ecf20Sopenharmony_ci#define COSA_FW_RESET (1<<0) /* Is the ROM monitor active? */ 1288c2ecf20Sopenharmony_ci#define COSA_FW_DOWNLOAD (1<<1) /* Is the microcode downloaded? */ 1298c2ecf20Sopenharmony_ci#define COSA_FW_START (1<<2) /* Is the microcode running? */ 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistruct cosa_data { 1328c2ecf20Sopenharmony_ci int num; /* Card number */ 1338c2ecf20Sopenharmony_ci char name[COSA_MAX_NAME]; /* Card name - e.g "cosa0" */ 1348c2ecf20Sopenharmony_ci unsigned int datareg, statusreg; /* I/O ports */ 1358c2ecf20Sopenharmony_ci unsigned short irq, dma; /* IRQ and DMA number */ 1368c2ecf20Sopenharmony_ci unsigned short startaddr; /* Firmware start address */ 1378c2ecf20Sopenharmony_ci unsigned short busmaster; /* Use busmastering? */ 1388c2ecf20Sopenharmony_ci int nchannels; /* # of channels on this card */ 1398c2ecf20Sopenharmony_ci int driver_status; /* For communicating with firmware */ 1408c2ecf20Sopenharmony_ci int firmware_status; /* Downloaded, reseted, etc. */ 1418c2ecf20Sopenharmony_ci unsigned long rxbitmap, txbitmap;/* Bitmap of channels who are willing to send/receive data */ 1428c2ecf20Sopenharmony_ci unsigned long rxtx; /* RX or TX in progress? */ 1438c2ecf20Sopenharmony_ci int enabled; 1448c2ecf20Sopenharmony_ci int usage; /* usage count */ 1458c2ecf20Sopenharmony_ci int txchan, txsize, rxsize; 1468c2ecf20Sopenharmony_ci struct channel_data *rxchan; 1478c2ecf20Sopenharmony_ci char *bouncebuf; 1488c2ecf20Sopenharmony_ci char *txbuf, *rxbuf; 1498c2ecf20Sopenharmony_ci struct channel_data *chan; 1508c2ecf20Sopenharmony_ci spinlock_t lock; /* For exclusive operations on this structure */ 1518c2ecf20Sopenharmony_ci char id_string[COSA_MAX_ID_STRING]; /* ROM monitor ID string */ 1528c2ecf20Sopenharmony_ci char *type; /* card type */ 1538c2ecf20Sopenharmony_ci}; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci/* 1568c2ecf20Sopenharmony_ci * Define this if you want all the possible ports to be autoprobed. 1578c2ecf20Sopenharmony_ci * It is here but it probably is not a good idea to use this. 1588c2ecf20Sopenharmony_ci */ 1598c2ecf20Sopenharmony_ci/* #define COSA_ISA_AUTOPROBE 1 */ 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci/* 1628c2ecf20Sopenharmony_ci * Character device major number. 117 was allocated for us. 1638c2ecf20Sopenharmony_ci * The value of 0 means to allocate a first free one. 1648c2ecf20Sopenharmony_ci */ 1658c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(cosa_chardev_mutex); 1668c2ecf20Sopenharmony_cistatic int cosa_major = 117; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci/* 1698c2ecf20Sopenharmony_ci * Encoding of the minor numbers: 1708c2ecf20Sopenharmony_ci * The lowest CARD_MINOR_BITS bits means the channel on the single card, 1718c2ecf20Sopenharmony_ci * the highest bits means the card number. 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_ci#define CARD_MINOR_BITS 4 /* How many bits in minor number are reserved 1748c2ecf20Sopenharmony_ci * for the single card */ 1758c2ecf20Sopenharmony_ci/* 1768c2ecf20Sopenharmony_ci * The following depends on CARD_MINOR_BITS. Unfortunately, the "MODULE_STRING" 1778c2ecf20Sopenharmony_ci * macro doesn't like anything other than the raw number as an argument :-( 1788c2ecf20Sopenharmony_ci */ 1798c2ecf20Sopenharmony_ci#define MAX_CARDS 16 1808c2ecf20Sopenharmony_ci/* #define MAX_CARDS (1 << (8-CARD_MINOR_BITS)) */ 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci#define DRIVER_RX_READY 0x0001 1838c2ecf20Sopenharmony_ci#define DRIVER_TX_READY 0x0002 1848c2ecf20Sopenharmony_ci#define DRIVER_TXMAP_SHIFT 2 1858c2ecf20Sopenharmony_ci#define DRIVER_TXMAP_MASK 0x0c /* FIXME: 0xfc for 8-channel version */ 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci/* 1888c2ecf20Sopenharmony_ci * for cosa->rxtx - indicates whether either transmit or receive is 1898c2ecf20Sopenharmony_ci * in progress. These values are mean number of the bit. 1908c2ecf20Sopenharmony_ci */ 1918c2ecf20Sopenharmony_ci#define TXBIT 0 1928c2ecf20Sopenharmony_ci#define RXBIT 1 1938c2ecf20Sopenharmony_ci#define IRQBIT 2 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci#define COSA_MTU 2000 /* FIXME: I don't know this exactly */ 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci#undef DEBUG_DATA //1 /* Dump the data read or written to the channel */ 1988c2ecf20Sopenharmony_ci#undef DEBUG_IRQS //1 /* Print the message when the IRQ is received */ 1998c2ecf20Sopenharmony_ci#undef DEBUG_IO //1 /* Dump the I/O traffic */ 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci#define TX_TIMEOUT (5*HZ) 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci/* Maybe the following should be allocated dynamically */ 2048c2ecf20Sopenharmony_cistatic struct cosa_data cosa_cards[MAX_CARDS]; 2058c2ecf20Sopenharmony_cistatic int nr_cards; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci#ifdef COSA_ISA_AUTOPROBE 2088c2ecf20Sopenharmony_cistatic int io[MAX_CARDS+1] = { 0x220, 0x228, 0x210, 0x218, 0, }; 2098c2ecf20Sopenharmony_ci/* NOTE: DMA is not autoprobed!!! */ 2108c2ecf20Sopenharmony_cistatic int dma[MAX_CARDS+1] = { 1, 7, 1, 7, 1, 7, 1, 7, 0, }; 2118c2ecf20Sopenharmony_ci#else 2128c2ecf20Sopenharmony_cistatic int io[MAX_CARDS+1]; 2138c2ecf20Sopenharmony_cistatic int dma[MAX_CARDS+1]; 2148c2ecf20Sopenharmony_ci#endif 2158c2ecf20Sopenharmony_ci/* IRQ can be safely autoprobed */ 2168c2ecf20Sopenharmony_cistatic int irq[MAX_CARDS+1] = { -1, -1, -1, -1, -1, -1, 0, }; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci/* for class stuff*/ 2198c2ecf20Sopenharmony_cistatic struct class *cosa_class; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci#ifdef MODULE 2228c2ecf20Sopenharmony_cimodule_param_hw_array(io, int, ioport, NULL, 0); 2238c2ecf20Sopenharmony_ciMODULE_PARM_DESC(io, "The I/O bases of the COSA or SRP cards"); 2248c2ecf20Sopenharmony_cimodule_param_hw_array(irq, int, irq, NULL, 0); 2258c2ecf20Sopenharmony_ciMODULE_PARM_DESC(irq, "The IRQ lines of the COSA or SRP cards"); 2268c2ecf20Sopenharmony_cimodule_param_hw_array(dma, int, dma, NULL, 0); 2278c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dma, "The DMA channels of the COSA or SRP cards"); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jan \"Yenya\" Kasprzak, <kas@fi.muni.cz>"); 2308c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Modular driver for the COSA or SRP synchronous card"); 2318c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2328c2ecf20Sopenharmony_ci#endif 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci/* I use this mainly for testing purposes */ 2358c2ecf20Sopenharmony_ci#ifdef COSA_SLOW_IO 2368c2ecf20Sopenharmony_ci#define cosa_outb outb_p 2378c2ecf20Sopenharmony_ci#define cosa_outw outw_p 2388c2ecf20Sopenharmony_ci#define cosa_inb inb_p 2398c2ecf20Sopenharmony_ci#define cosa_inw inw_p 2408c2ecf20Sopenharmony_ci#else 2418c2ecf20Sopenharmony_ci#define cosa_outb outb 2428c2ecf20Sopenharmony_ci#define cosa_outw outw 2438c2ecf20Sopenharmony_ci#define cosa_inb inb 2448c2ecf20Sopenharmony_ci#define cosa_inw inw 2458c2ecf20Sopenharmony_ci#endif 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci#define is_8bit(cosa) (!(cosa->datareg & 0x08)) 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci#define cosa_getstatus(cosa) (cosa_inb(cosa->statusreg)) 2508c2ecf20Sopenharmony_ci#define cosa_putstatus(cosa, stat) (cosa_outb(stat, cosa->statusreg)) 2518c2ecf20Sopenharmony_ci#define cosa_getdata16(cosa) (cosa_inw(cosa->datareg)) 2528c2ecf20Sopenharmony_ci#define cosa_getdata8(cosa) (cosa_inb(cosa->datareg)) 2538c2ecf20Sopenharmony_ci#define cosa_putdata16(cosa, dt) (cosa_outw(dt, cosa->datareg)) 2548c2ecf20Sopenharmony_ci#define cosa_putdata8(cosa, dt) (cosa_outb(dt, cosa->datareg)) 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci/* Initialization stuff */ 2578c2ecf20Sopenharmony_cistatic int cosa_probe(int ioaddr, int irq, int dma); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci/* HW interface */ 2608c2ecf20Sopenharmony_cistatic void cosa_enable_rx(struct channel_data *chan); 2618c2ecf20Sopenharmony_cistatic void cosa_disable_rx(struct channel_data *chan); 2628c2ecf20Sopenharmony_cistatic int cosa_start_tx(struct channel_data *channel, char *buf, int size); 2638c2ecf20Sopenharmony_cistatic void cosa_kick(struct cosa_data *cosa); 2648c2ecf20Sopenharmony_cistatic int cosa_dma_able(struct channel_data *chan, char *buf, int data); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci/* Network device stuff */ 2678c2ecf20Sopenharmony_cistatic int cosa_net_attach(struct net_device *dev, unsigned short encoding, 2688c2ecf20Sopenharmony_ci unsigned short parity); 2698c2ecf20Sopenharmony_cistatic int cosa_net_open(struct net_device *d); 2708c2ecf20Sopenharmony_cistatic int cosa_net_close(struct net_device *d); 2718c2ecf20Sopenharmony_cistatic void cosa_net_timeout(struct net_device *d, unsigned int txqueue); 2728c2ecf20Sopenharmony_cistatic netdev_tx_t cosa_net_tx(struct sk_buff *skb, struct net_device *d); 2738c2ecf20Sopenharmony_cistatic char *cosa_net_setup_rx(struct channel_data *channel, int size); 2748c2ecf20Sopenharmony_cistatic int cosa_net_rx_done(struct channel_data *channel); 2758c2ecf20Sopenharmony_cistatic int cosa_net_tx_done(struct channel_data *channel, int size); 2768c2ecf20Sopenharmony_cistatic int cosa_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci/* Character device */ 2798c2ecf20Sopenharmony_cistatic char *chrdev_setup_rx(struct channel_data *channel, int size); 2808c2ecf20Sopenharmony_cistatic int chrdev_rx_done(struct channel_data *channel); 2818c2ecf20Sopenharmony_cistatic int chrdev_tx_done(struct channel_data *channel, int size); 2828c2ecf20Sopenharmony_cistatic ssize_t cosa_read(struct file *file, 2838c2ecf20Sopenharmony_ci char __user *buf, size_t count, loff_t *ppos); 2848c2ecf20Sopenharmony_cistatic ssize_t cosa_write(struct file *file, 2858c2ecf20Sopenharmony_ci const char __user *buf, size_t count, loff_t *ppos); 2868c2ecf20Sopenharmony_cistatic unsigned int cosa_poll(struct file *file, poll_table *poll); 2878c2ecf20Sopenharmony_cistatic int cosa_open(struct inode *inode, struct file *file); 2888c2ecf20Sopenharmony_cistatic int cosa_release(struct inode *inode, struct file *file); 2898c2ecf20Sopenharmony_cistatic long cosa_chardev_ioctl(struct file *file, unsigned int cmd, 2908c2ecf20Sopenharmony_ci unsigned long arg); 2918c2ecf20Sopenharmony_ci#ifdef COSA_FASYNC_WORKING 2928c2ecf20Sopenharmony_cistatic int cosa_fasync(struct inode *inode, struct file *file, int on); 2938c2ecf20Sopenharmony_ci#endif 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic const struct file_operations cosa_fops = { 2968c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2978c2ecf20Sopenharmony_ci .llseek = no_llseek, 2988c2ecf20Sopenharmony_ci .read = cosa_read, 2998c2ecf20Sopenharmony_ci .write = cosa_write, 3008c2ecf20Sopenharmony_ci .poll = cosa_poll, 3018c2ecf20Sopenharmony_ci .unlocked_ioctl = cosa_chardev_ioctl, 3028c2ecf20Sopenharmony_ci .open = cosa_open, 3038c2ecf20Sopenharmony_ci .release = cosa_release, 3048c2ecf20Sopenharmony_ci#ifdef COSA_FASYNC_WORKING 3058c2ecf20Sopenharmony_ci .fasync = cosa_fasync, 3068c2ecf20Sopenharmony_ci#endif 3078c2ecf20Sopenharmony_ci}; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci/* Ioctls */ 3108c2ecf20Sopenharmony_cistatic int cosa_start(struct cosa_data *cosa, int address); 3118c2ecf20Sopenharmony_cistatic int cosa_reset(struct cosa_data *cosa); 3128c2ecf20Sopenharmony_cistatic int cosa_download(struct cosa_data *cosa, void __user *a); 3138c2ecf20Sopenharmony_cistatic int cosa_readmem(struct cosa_data *cosa, void __user *a); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci/* COSA/SRP ROM monitor */ 3168c2ecf20Sopenharmony_cistatic int download(struct cosa_data *cosa, const char __user *data, int addr, int len); 3178c2ecf20Sopenharmony_cistatic int startmicrocode(struct cosa_data *cosa, int address); 3188c2ecf20Sopenharmony_cistatic int readmem(struct cosa_data *cosa, char __user *data, int addr, int len); 3198c2ecf20Sopenharmony_cistatic int cosa_reset_and_read_id(struct cosa_data *cosa, char *id); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci/* Auxiliary functions */ 3228c2ecf20Sopenharmony_cistatic int get_wait_data(struct cosa_data *cosa); 3238c2ecf20Sopenharmony_cistatic int put_wait_data(struct cosa_data *cosa, int data); 3248c2ecf20Sopenharmony_cistatic int puthexnumber(struct cosa_data *cosa, int number); 3258c2ecf20Sopenharmony_cistatic void put_driver_status(struct cosa_data *cosa); 3268c2ecf20Sopenharmony_cistatic void put_driver_status_nolock(struct cosa_data *cosa); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci/* Interrupt handling */ 3298c2ecf20Sopenharmony_cistatic irqreturn_t cosa_interrupt(int irq, void *cosa); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci/* I/O ops debugging */ 3328c2ecf20Sopenharmony_ci#ifdef DEBUG_IO 3338c2ecf20Sopenharmony_cistatic void debug_data_in(struct cosa_data *cosa, int data); 3348c2ecf20Sopenharmony_cistatic void debug_data_out(struct cosa_data *cosa, int data); 3358c2ecf20Sopenharmony_cistatic void debug_data_cmd(struct cosa_data *cosa, int data); 3368c2ecf20Sopenharmony_cistatic void debug_status_in(struct cosa_data *cosa, int status); 3378c2ecf20Sopenharmony_cistatic void debug_status_out(struct cosa_data *cosa, int status); 3388c2ecf20Sopenharmony_ci#endif 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic inline struct channel_data* dev_to_chan(struct net_device *dev) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci return (struct channel_data *)dev_to_hdlc(dev)->priv; 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci/* ---------- Initialization stuff ---------- */ 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic int __init cosa_init(void) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci int i, err = 0; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci if (cosa_major > 0) { 3528c2ecf20Sopenharmony_ci if (register_chrdev(cosa_major, "cosa", &cosa_fops)) { 3538c2ecf20Sopenharmony_ci pr_warn("unable to get major %d\n", cosa_major); 3548c2ecf20Sopenharmony_ci err = -EIO; 3558c2ecf20Sopenharmony_ci goto out; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci } else { 3588c2ecf20Sopenharmony_ci if (!(cosa_major=register_chrdev(0, "cosa", &cosa_fops))) { 3598c2ecf20Sopenharmony_ci pr_warn("unable to register chardev\n"); 3608c2ecf20Sopenharmony_ci err = -EIO; 3618c2ecf20Sopenharmony_ci goto out; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci for (i=0; i<MAX_CARDS; i++) 3658c2ecf20Sopenharmony_ci cosa_cards[i].num = -1; 3668c2ecf20Sopenharmony_ci for (i=0; io[i] != 0 && i < MAX_CARDS; i++) 3678c2ecf20Sopenharmony_ci cosa_probe(io[i], irq[i], dma[i]); 3688c2ecf20Sopenharmony_ci if (!nr_cards) { 3698c2ecf20Sopenharmony_ci pr_warn("no devices found\n"); 3708c2ecf20Sopenharmony_ci unregister_chrdev(cosa_major, "cosa"); 3718c2ecf20Sopenharmony_ci err = -ENODEV; 3728c2ecf20Sopenharmony_ci goto out; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci cosa_class = class_create(THIS_MODULE, "cosa"); 3758c2ecf20Sopenharmony_ci if (IS_ERR(cosa_class)) { 3768c2ecf20Sopenharmony_ci err = PTR_ERR(cosa_class); 3778c2ecf20Sopenharmony_ci goto out_chrdev; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci for (i = 0; i < nr_cards; i++) 3808c2ecf20Sopenharmony_ci device_create(cosa_class, NULL, MKDEV(cosa_major, i), NULL, 3818c2ecf20Sopenharmony_ci "cosa%d", i); 3828c2ecf20Sopenharmony_ci err = 0; 3838c2ecf20Sopenharmony_ci goto out; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ciout_chrdev: 3868c2ecf20Sopenharmony_ci unregister_chrdev(cosa_major, "cosa"); 3878c2ecf20Sopenharmony_ciout: 3888c2ecf20Sopenharmony_ci return err; 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_cimodule_init(cosa_init); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cistatic void __exit cosa_exit(void) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci struct cosa_data *cosa; 3958c2ecf20Sopenharmony_ci int i; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci for (i = 0; i < nr_cards; i++) 3988c2ecf20Sopenharmony_ci device_destroy(cosa_class, MKDEV(cosa_major, i)); 3998c2ecf20Sopenharmony_ci class_destroy(cosa_class); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci for (cosa = cosa_cards; nr_cards--; cosa++) { 4028c2ecf20Sopenharmony_ci /* Clean up the per-channel data */ 4038c2ecf20Sopenharmony_ci for (i = 0; i < cosa->nchannels; i++) { 4048c2ecf20Sopenharmony_ci /* Chardev driver has no alloc'd per-channel data */ 4058c2ecf20Sopenharmony_ci unregister_hdlc_device(cosa->chan[i].netdev); 4068c2ecf20Sopenharmony_ci free_netdev(cosa->chan[i].netdev); 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci /* Clean up the per-card data */ 4098c2ecf20Sopenharmony_ci kfree(cosa->chan); 4108c2ecf20Sopenharmony_ci kfree(cosa->bouncebuf); 4118c2ecf20Sopenharmony_ci free_irq(cosa->irq, cosa); 4128c2ecf20Sopenharmony_ci free_dma(cosa->dma); 4138c2ecf20Sopenharmony_ci release_region(cosa->datareg, is_8bit(cosa) ? 2 : 4); 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci unregister_chrdev(cosa_major, "cosa"); 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_cimodule_exit(cosa_exit); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_cistatic const struct net_device_ops cosa_ops = { 4208c2ecf20Sopenharmony_ci .ndo_open = cosa_net_open, 4218c2ecf20Sopenharmony_ci .ndo_stop = cosa_net_close, 4228c2ecf20Sopenharmony_ci .ndo_start_xmit = hdlc_start_xmit, 4238c2ecf20Sopenharmony_ci .ndo_do_ioctl = cosa_net_ioctl, 4248c2ecf20Sopenharmony_ci .ndo_tx_timeout = cosa_net_timeout, 4258c2ecf20Sopenharmony_ci}; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_cistatic int cosa_probe(int base, int irq, int dma) 4288c2ecf20Sopenharmony_ci{ 4298c2ecf20Sopenharmony_ci struct cosa_data *cosa = cosa_cards+nr_cards; 4308c2ecf20Sopenharmony_ci int i, err = 0; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci memset(cosa, 0, sizeof(struct cosa_data)); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci /* Checking validity of parameters: */ 4358c2ecf20Sopenharmony_ci /* IRQ should be 2-7 or 10-15; negative IRQ means autoprobe */ 4368c2ecf20Sopenharmony_ci if ((irq >= 0 && irq < 2) || irq > 15 || (irq < 10 && irq > 7)) { 4378c2ecf20Sopenharmony_ci pr_info("invalid IRQ %d\n", irq); 4388c2ecf20Sopenharmony_ci return -1; 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci /* I/O address should be between 0x100 and 0x3ff and should be 4418c2ecf20Sopenharmony_ci * multiple of 8. */ 4428c2ecf20Sopenharmony_ci if (base < 0x100 || base > 0x3ff || base & 0x7) { 4438c2ecf20Sopenharmony_ci pr_info("invalid I/O address 0x%x\n", base); 4448c2ecf20Sopenharmony_ci return -1; 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci /* DMA should be 0,1 or 3-7 */ 4478c2ecf20Sopenharmony_ci if (dma < 0 || dma == 4 || dma > 7) { 4488c2ecf20Sopenharmony_ci pr_info("invalid DMA %d\n", dma); 4498c2ecf20Sopenharmony_ci return -1; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci /* and finally, on 16-bit COSA DMA should be 4-7 and 4528c2ecf20Sopenharmony_ci * I/O base should not be multiple of 0x10 */ 4538c2ecf20Sopenharmony_ci if (((base & 0x8) && dma < 4) || (!(base & 0x8) && dma > 3)) { 4548c2ecf20Sopenharmony_ci pr_info("8/16 bit base and DMA mismatch (base=0x%x, dma=%d)\n", 4558c2ecf20Sopenharmony_ci base, dma); 4568c2ecf20Sopenharmony_ci return -1; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci cosa->dma = dma; 4608c2ecf20Sopenharmony_ci cosa->datareg = base; 4618c2ecf20Sopenharmony_ci cosa->statusreg = is_8bit(cosa)?base+1:base+2; 4628c2ecf20Sopenharmony_ci spin_lock_init(&cosa->lock); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci if (!request_region(base, is_8bit(cosa)?2:4,"cosa")) 4658c2ecf20Sopenharmony_ci return -1; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci if (cosa_reset_and_read_id(cosa, cosa->id_string) < 0) { 4688c2ecf20Sopenharmony_ci printk(KERN_DEBUG "probe at 0x%x failed.\n", base); 4698c2ecf20Sopenharmony_ci err = -1; 4708c2ecf20Sopenharmony_ci goto err_out; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci /* Test the validity of identification string */ 4748c2ecf20Sopenharmony_ci if (!strncmp(cosa->id_string, "SRP", 3)) 4758c2ecf20Sopenharmony_ci cosa->type = "srp"; 4768c2ecf20Sopenharmony_ci else if (!strncmp(cosa->id_string, "COSA", 4)) 4778c2ecf20Sopenharmony_ci cosa->type = is_8bit(cosa)? "cosa8": "cosa16"; 4788c2ecf20Sopenharmony_ci else { 4798c2ecf20Sopenharmony_ci/* Print a warning only if we are not autoprobing */ 4808c2ecf20Sopenharmony_ci#ifndef COSA_ISA_AUTOPROBE 4818c2ecf20Sopenharmony_ci pr_info("valid signature not found at 0x%x\n", base); 4828c2ecf20Sopenharmony_ci#endif 4838c2ecf20Sopenharmony_ci err = -1; 4848c2ecf20Sopenharmony_ci goto err_out; 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci /* Update the name of the region now we know the type of card */ 4878c2ecf20Sopenharmony_ci release_region(base, is_8bit(cosa)?2:4); 4888c2ecf20Sopenharmony_ci if (!request_region(base, is_8bit(cosa)?2:4, cosa->type)) { 4898c2ecf20Sopenharmony_ci printk(KERN_DEBUG "changing name at 0x%x failed.\n", base); 4908c2ecf20Sopenharmony_ci return -1; 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci /* Now do IRQ autoprobe */ 4948c2ecf20Sopenharmony_ci if (irq < 0) { 4958c2ecf20Sopenharmony_ci unsigned long irqs; 4968c2ecf20Sopenharmony_ci/* pr_info("IRQ autoprobe\n"); */ 4978c2ecf20Sopenharmony_ci irqs = probe_irq_on(); 4988c2ecf20Sopenharmony_ci /* 4998c2ecf20Sopenharmony_ci * Enable interrupt on tx buffer empty (it sure is) 5008c2ecf20Sopenharmony_ci * really sure ? 5018c2ecf20Sopenharmony_ci * FIXME: When this code is not used as module, we should 5028c2ecf20Sopenharmony_ci * probably call udelay() instead of the interruptible sleep. 5038c2ecf20Sopenharmony_ci */ 5048c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 5058c2ecf20Sopenharmony_ci cosa_putstatus(cosa, SR_TX_INT_ENA); 5068c2ecf20Sopenharmony_ci schedule_timeout(msecs_to_jiffies(300)); 5078c2ecf20Sopenharmony_ci irq = probe_irq_off(irqs); 5088c2ecf20Sopenharmony_ci /* Disable all IRQs from the card */ 5098c2ecf20Sopenharmony_ci cosa_putstatus(cosa, 0); 5108c2ecf20Sopenharmony_ci /* Empty the received data register */ 5118c2ecf20Sopenharmony_ci cosa_getdata8(cosa); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci if (irq < 0) { 5148c2ecf20Sopenharmony_ci pr_info("multiple interrupts obtained (%d, board at 0x%x)\n", 5158c2ecf20Sopenharmony_ci irq, cosa->datareg); 5168c2ecf20Sopenharmony_ci err = -1; 5178c2ecf20Sopenharmony_ci goto err_out; 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci if (irq == 0) { 5208c2ecf20Sopenharmony_ci pr_info("no interrupt obtained (board at 0x%x)\n", 5218c2ecf20Sopenharmony_ci cosa->datareg); 5228c2ecf20Sopenharmony_ci /* return -1; */ 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci cosa->irq = irq; 5278c2ecf20Sopenharmony_ci cosa->num = nr_cards; 5288c2ecf20Sopenharmony_ci cosa->usage = 0; 5298c2ecf20Sopenharmony_ci cosa->nchannels = 2; /* FIXME: how to determine this? */ 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci if (request_irq(cosa->irq, cosa_interrupt, 0, cosa->type, cosa)) { 5328c2ecf20Sopenharmony_ci err = -1; 5338c2ecf20Sopenharmony_ci goto err_out; 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci if (request_dma(cosa->dma, cosa->type)) { 5368c2ecf20Sopenharmony_ci err = -1; 5378c2ecf20Sopenharmony_ci goto err_out1; 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci cosa->bouncebuf = kmalloc(COSA_MTU, GFP_KERNEL|GFP_DMA); 5418c2ecf20Sopenharmony_ci if (!cosa->bouncebuf) { 5428c2ecf20Sopenharmony_ci err = -ENOMEM; 5438c2ecf20Sopenharmony_ci goto err_out2; 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci sprintf(cosa->name, "cosa%d", cosa->num); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci /* Initialize the per-channel data */ 5488c2ecf20Sopenharmony_ci cosa->chan = kcalloc(cosa->nchannels, sizeof(struct channel_data), GFP_KERNEL); 5498c2ecf20Sopenharmony_ci if (!cosa->chan) { 5508c2ecf20Sopenharmony_ci err = -ENOMEM; 5518c2ecf20Sopenharmony_ci goto err_out3; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci for (i = 0; i < cosa->nchannels; i++) { 5558c2ecf20Sopenharmony_ci struct channel_data *chan = &cosa->chan[i]; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci chan->cosa = cosa; 5588c2ecf20Sopenharmony_ci chan->num = i; 5598c2ecf20Sopenharmony_ci sprintf(chan->name, "cosa%dc%d", chan->cosa->num, i); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci /* Initialize the chardev data structures */ 5628c2ecf20Sopenharmony_ci mutex_init(&chan->rlock); 5638c2ecf20Sopenharmony_ci sema_init(&chan->wsem, 1); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci /* Register the network interface */ 5668c2ecf20Sopenharmony_ci if (!(chan->netdev = alloc_hdlcdev(chan))) { 5678c2ecf20Sopenharmony_ci pr_warn("%s: alloc_hdlcdev failed\n", chan->name); 5688c2ecf20Sopenharmony_ci err = -ENOMEM; 5698c2ecf20Sopenharmony_ci goto err_hdlcdev; 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci dev_to_hdlc(chan->netdev)->attach = cosa_net_attach; 5728c2ecf20Sopenharmony_ci dev_to_hdlc(chan->netdev)->xmit = cosa_net_tx; 5738c2ecf20Sopenharmony_ci chan->netdev->netdev_ops = &cosa_ops; 5748c2ecf20Sopenharmony_ci chan->netdev->watchdog_timeo = TX_TIMEOUT; 5758c2ecf20Sopenharmony_ci chan->netdev->base_addr = chan->cosa->datareg; 5768c2ecf20Sopenharmony_ci chan->netdev->irq = chan->cosa->irq; 5778c2ecf20Sopenharmony_ci chan->netdev->dma = chan->cosa->dma; 5788c2ecf20Sopenharmony_ci err = register_hdlc_device(chan->netdev); 5798c2ecf20Sopenharmony_ci if (err) { 5808c2ecf20Sopenharmony_ci netdev_warn(chan->netdev, 5818c2ecf20Sopenharmony_ci "register_hdlc_device() failed\n"); 5828c2ecf20Sopenharmony_ci free_netdev(chan->netdev); 5838c2ecf20Sopenharmony_ci goto err_hdlcdev; 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci pr_info("cosa%d: %s (%s at 0x%x irq %d dma %d), %d channels\n", 5888c2ecf20Sopenharmony_ci cosa->num, cosa->id_string, cosa->type, 5898c2ecf20Sopenharmony_ci cosa->datareg, cosa->irq, cosa->dma, cosa->nchannels); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci return nr_cards++; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_cierr_hdlcdev: 5948c2ecf20Sopenharmony_ci while (i-- > 0) { 5958c2ecf20Sopenharmony_ci unregister_hdlc_device(cosa->chan[i].netdev); 5968c2ecf20Sopenharmony_ci free_netdev(cosa->chan[i].netdev); 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci kfree(cosa->chan); 5998c2ecf20Sopenharmony_cierr_out3: 6008c2ecf20Sopenharmony_ci kfree(cosa->bouncebuf); 6018c2ecf20Sopenharmony_cierr_out2: 6028c2ecf20Sopenharmony_ci free_dma(cosa->dma); 6038c2ecf20Sopenharmony_cierr_out1: 6048c2ecf20Sopenharmony_ci free_irq(cosa->irq, cosa); 6058c2ecf20Sopenharmony_cierr_out: 6068c2ecf20Sopenharmony_ci release_region(cosa->datareg,is_8bit(cosa)?2:4); 6078c2ecf20Sopenharmony_ci pr_notice("cosa%d: allocating resources failed\n", cosa->num); 6088c2ecf20Sopenharmony_ci return err; 6098c2ecf20Sopenharmony_ci} 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci/*---------- network device ---------- */ 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_cistatic int cosa_net_attach(struct net_device *dev, unsigned short encoding, 6158c2ecf20Sopenharmony_ci unsigned short parity) 6168c2ecf20Sopenharmony_ci{ 6178c2ecf20Sopenharmony_ci if (encoding == ENCODING_NRZ && parity == PARITY_CRC16_PR1_CCITT) 6188c2ecf20Sopenharmony_ci return 0; 6198c2ecf20Sopenharmony_ci return -EINVAL; 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_cistatic int cosa_net_open(struct net_device *dev) 6238c2ecf20Sopenharmony_ci{ 6248c2ecf20Sopenharmony_ci struct channel_data *chan = dev_to_chan(dev); 6258c2ecf20Sopenharmony_ci int err; 6268c2ecf20Sopenharmony_ci unsigned long flags; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci if (!(chan->cosa->firmware_status & COSA_FW_START)) { 6298c2ecf20Sopenharmony_ci pr_notice("%s: start the firmware first (status %d)\n", 6308c2ecf20Sopenharmony_ci chan->cosa->name, chan->cosa->firmware_status); 6318c2ecf20Sopenharmony_ci return -EPERM; 6328c2ecf20Sopenharmony_ci } 6338c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->cosa->lock, flags); 6348c2ecf20Sopenharmony_ci if (chan->usage != 0) { 6358c2ecf20Sopenharmony_ci pr_warn("%s: cosa_net_open called with usage count %d\n", 6368c2ecf20Sopenharmony_ci chan->name, chan->usage); 6378c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->cosa->lock, flags); 6388c2ecf20Sopenharmony_ci return -EBUSY; 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci chan->setup_rx = cosa_net_setup_rx; 6418c2ecf20Sopenharmony_ci chan->tx_done = cosa_net_tx_done; 6428c2ecf20Sopenharmony_ci chan->rx_done = cosa_net_rx_done; 6438c2ecf20Sopenharmony_ci chan->usage = -1; 6448c2ecf20Sopenharmony_ci chan->cosa->usage++; 6458c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->cosa->lock, flags); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci err = hdlc_open(dev); 6488c2ecf20Sopenharmony_ci if (err) { 6498c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->cosa->lock, flags); 6508c2ecf20Sopenharmony_ci chan->usage = 0; 6518c2ecf20Sopenharmony_ci chan->cosa->usage--; 6528c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->cosa->lock, flags); 6538c2ecf20Sopenharmony_ci return err; 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci netif_start_queue(dev); 6578c2ecf20Sopenharmony_ci cosa_enable_rx(chan); 6588c2ecf20Sopenharmony_ci return 0; 6598c2ecf20Sopenharmony_ci} 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_cistatic netdev_tx_t cosa_net_tx(struct sk_buff *skb, 6628c2ecf20Sopenharmony_ci struct net_device *dev) 6638c2ecf20Sopenharmony_ci{ 6648c2ecf20Sopenharmony_ci struct channel_data *chan = dev_to_chan(dev); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci netif_stop_queue(dev); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci chan->tx_skb = skb; 6698c2ecf20Sopenharmony_ci cosa_start_tx(chan, skb->data, skb->len); 6708c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 6718c2ecf20Sopenharmony_ci} 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_cistatic void cosa_net_timeout(struct net_device *dev, unsigned int txqueue) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci struct channel_data *chan = dev_to_chan(dev); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci if (test_bit(RXBIT, &chan->cosa->rxtx)) { 6788c2ecf20Sopenharmony_ci chan->netdev->stats.rx_errors++; 6798c2ecf20Sopenharmony_ci chan->netdev->stats.rx_missed_errors++; 6808c2ecf20Sopenharmony_ci } else { 6818c2ecf20Sopenharmony_ci chan->netdev->stats.tx_errors++; 6828c2ecf20Sopenharmony_ci chan->netdev->stats.tx_aborted_errors++; 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci cosa_kick(chan->cosa); 6858c2ecf20Sopenharmony_ci if (chan->tx_skb) { 6868c2ecf20Sopenharmony_ci dev_kfree_skb(chan->tx_skb); 6878c2ecf20Sopenharmony_ci chan->tx_skb = NULL; 6888c2ecf20Sopenharmony_ci } 6898c2ecf20Sopenharmony_ci netif_wake_queue(dev); 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_cistatic int cosa_net_close(struct net_device *dev) 6938c2ecf20Sopenharmony_ci{ 6948c2ecf20Sopenharmony_ci struct channel_data *chan = dev_to_chan(dev); 6958c2ecf20Sopenharmony_ci unsigned long flags; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci netif_stop_queue(dev); 6988c2ecf20Sopenharmony_ci hdlc_close(dev); 6998c2ecf20Sopenharmony_ci cosa_disable_rx(chan); 7008c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->cosa->lock, flags); 7018c2ecf20Sopenharmony_ci if (chan->rx_skb) { 7028c2ecf20Sopenharmony_ci kfree_skb(chan->rx_skb); 7038c2ecf20Sopenharmony_ci chan->rx_skb = NULL; 7048c2ecf20Sopenharmony_ci } 7058c2ecf20Sopenharmony_ci if (chan->tx_skb) { 7068c2ecf20Sopenharmony_ci kfree_skb(chan->tx_skb); 7078c2ecf20Sopenharmony_ci chan->tx_skb = NULL; 7088c2ecf20Sopenharmony_ci } 7098c2ecf20Sopenharmony_ci chan->usage = 0; 7108c2ecf20Sopenharmony_ci chan->cosa->usage--; 7118c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->cosa->lock, flags); 7128c2ecf20Sopenharmony_ci return 0; 7138c2ecf20Sopenharmony_ci} 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_cistatic char *cosa_net_setup_rx(struct channel_data *chan, int size) 7168c2ecf20Sopenharmony_ci{ 7178c2ecf20Sopenharmony_ci /* 7188c2ecf20Sopenharmony_ci * We can safely fall back to non-dma-able memory, because we have 7198c2ecf20Sopenharmony_ci * the cosa->bouncebuf pre-allocated. 7208c2ecf20Sopenharmony_ci */ 7218c2ecf20Sopenharmony_ci kfree_skb(chan->rx_skb); 7228c2ecf20Sopenharmony_ci chan->rx_skb = dev_alloc_skb(size); 7238c2ecf20Sopenharmony_ci if (chan->rx_skb == NULL) { 7248c2ecf20Sopenharmony_ci pr_notice("%s: Memory squeeze, dropping packet\n", chan->name); 7258c2ecf20Sopenharmony_ci chan->netdev->stats.rx_dropped++; 7268c2ecf20Sopenharmony_ci return NULL; 7278c2ecf20Sopenharmony_ci } 7288c2ecf20Sopenharmony_ci netif_trans_update(chan->netdev); 7298c2ecf20Sopenharmony_ci return skb_put(chan->rx_skb, size); 7308c2ecf20Sopenharmony_ci} 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_cistatic int cosa_net_rx_done(struct channel_data *chan) 7338c2ecf20Sopenharmony_ci{ 7348c2ecf20Sopenharmony_ci if (!chan->rx_skb) { 7358c2ecf20Sopenharmony_ci pr_warn("%s: rx_done with empty skb!\n", chan->name); 7368c2ecf20Sopenharmony_ci chan->netdev->stats.rx_errors++; 7378c2ecf20Sopenharmony_ci chan->netdev->stats.rx_frame_errors++; 7388c2ecf20Sopenharmony_ci return 0; 7398c2ecf20Sopenharmony_ci } 7408c2ecf20Sopenharmony_ci chan->rx_skb->protocol = hdlc_type_trans(chan->rx_skb, chan->netdev); 7418c2ecf20Sopenharmony_ci chan->rx_skb->dev = chan->netdev; 7428c2ecf20Sopenharmony_ci skb_reset_mac_header(chan->rx_skb); 7438c2ecf20Sopenharmony_ci chan->netdev->stats.rx_packets++; 7448c2ecf20Sopenharmony_ci chan->netdev->stats.rx_bytes += chan->cosa->rxsize; 7458c2ecf20Sopenharmony_ci netif_rx(chan->rx_skb); 7468c2ecf20Sopenharmony_ci chan->rx_skb = NULL; 7478c2ecf20Sopenharmony_ci return 0; 7488c2ecf20Sopenharmony_ci} 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci/* ARGSUSED */ 7518c2ecf20Sopenharmony_cistatic int cosa_net_tx_done(struct channel_data *chan, int size) 7528c2ecf20Sopenharmony_ci{ 7538c2ecf20Sopenharmony_ci if (!chan->tx_skb) { 7548c2ecf20Sopenharmony_ci pr_warn("%s: tx_done with empty skb!\n", chan->name); 7558c2ecf20Sopenharmony_ci chan->netdev->stats.tx_errors++; 7568c2ecf20Sopenharmony_ci chan->netdev->stats.tx_aborted_errors++; 7578c2ecf20Sopenharmony_ci return 1; 7588c2ecf20Sopenharmony_ci } 7598c2ecf20Sopenharmony_ci dev_consume_skb_irq(chan->tx_skb); 7608c2ecf20Sopenharmony_ci chan->tx_skb = NULL; 7618c2ecf20Sopenharmony_ci chan->netdev->stats.tx_packets++; 7628c2ecf20Sopenharmony_ci chan->netdev->stats.tx_bytes += size; 7638c2ecf20Sopenharmony_ci netif_wake_queue(chan->netdev); 7648c2ecf20Sopenharmony_ci return 1; 7658c2ecf20Sopenharmony_ci} 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci/*---------- Character device ---------- */ 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_cistatic ssize_t cosa_read(struct file *file, 7708c2ecf20Sopenharmony_ci char __user *buf, size_t count, loff_t *ppos) 7718c2ecf20Sopenharmony_ci{ 7728c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 7738c2ecf20Sopenharmony_ci unsigned long flags; 7748c2ecf20Sopenharmony_ci struct channel_data *chan = file->private_data; 7758c2ecf20Sopenharmony_ci struct cosa_data *cosa = chan->cosa; 7768c2ecf20Sopenharmony_ci char *kbuf; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci if (!(cosa->firmware_status & COSA_FW_START)) { 7798c2ecf20Sopenharmony_ci pr_notice("%s: start the firmware first (status %d)\n", 7808c2ecf20Sopenharmony_ci cosa->name, cosa->firmware_status); 7818c2ecf20Sopenharmony_ci return -EPERM; 7828c2ecf20Sopenharmony_ci } 7838c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&chan->rlock)) 7848c2ecf20Sopenharmony_ci return -ERESTARTSYS; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci chan->rxdata = kmalloc(COSA_MTU, GFP_DMA|GFP_KERNEL); 7878c2ecf20Sopenharmony_ci if (chan->rxdata == NULL) { 7888c2ecf20Sopenharmony_ci mutex_unlock(&chan->rlock); 7898c2ecf20Sopenharmony_ci return -ENOMEM; 7908c2ecf20Sopenharmony_ci } 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci chan->rx_status = 0; 7938c2ecf20Sopenharmony_ci cosa_enable_rx(chan); 7948c2ecf20Sopenharmony_ci spin_lock_irqsave(&cosa->lock, flags); 7958c2ecf20Sopenharmony_ci add_wait_queue(&chan->rxwaitq, &wait); 7968c2ecf20Sopenharmony_ci while (!chan->rx_status) { 7978c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 7988c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cosa->lock, flags); 7998c2ecf20Sopenharmony_ci schedule(); 8008c2ecf20Sopenharmony_ci spin_lock_irqsave(&cosa->lock, flags); 8018c2ecf20Sopenharmony_ci if (signal_pending(current) && chan->rx_status == 0) { 8028c2ecf20Sopenharmony_ci chan->rx_status = 1; 8038c2ecf20Sopenharmony_ci remove_wait_queue(&chan->rxwaitq, &wait); 8048c2ecf20Sopenharmony_ci __set_current_state(TASK_RUNNING); 8058c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cosa->lock, flags); 8068c2ecf20Sopenharmony_ci mutex_unlock(&chan->rlock); 8078c2ecf20Sopenharmony_ci return -ERESTARTSYS; 8088c2ecf20Sopenharmony_ci } 8098c2ecf20Sopenharmony_ci } 8108c2ecf20Sopenharmony_ci remove_wait_queue(&chan->rxwaitq, &wait); 8118c2ecf20Sopenharmony_ci __set_current_state(TASK_RUNNING); 8128c2ecf20Sopenharmony_ci kbuf = chan->rxdata; 8138c2ecf20Sopenharmony_ci count = chan->rxsize; 8148c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cosa->lock, flags); 8158c2ecf20Sopenharmony_ci mutex_unlock(&chan->rlock); 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci if (copy_to_user(buf, kbuf, count)) { 8188c2ecf20Sopenharmony_ci kfree(kbuf); 8198c2ecf20Sopenharmony_ci return -EFAULT; 8208c2ecf20Sopenharmony_ci } 8218c2ecf20Sopenharmony_ci kfree(kbuf); 8228c2ecf20Sopenharmony_ci return count; 8238c2ecf20Sopenharmony_ci} 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_cistatic char *chrdev_setup_rx(struct channel_data *chan, int size) 8268c2ecf20Sopenharmony_ci{ 8278c2ecf20Sopenharmony_ci /* Expect size <= COSA_MTU */ 8288c2ecf20Sopenharmony_ci chan->rxsize = size; 8298c2ecf20Sopenharmony_ci return chan->rxdata; 8308c2ecf20Sopenharmony_ci} 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_cistatic int chrdev_rx_done(struct channel_data *chan) 8338c2ecf20Sopenharmony_ci{ 8348c2ecf20Sopenharmony_ci if (chan->rx_status) { /* Reader has died */ 8358c2ecf20Sopenharmony_ci kfree(chan->rxdata); 8368c2ecf20Sopenharmony_ci up(&chan->wsem); 8378c2ecf20Sopenharmony_ci } 8388c2ecf20Sopenharmony_ci chan->rx_status = 1; 8398c2ecf20Sopenharmony_ci wake_up_interruptible(&chan->rxwaitq); 8408c2ecf20Sopenharmony_ci return 1; 8418c2ecf20Sopenharmony_ci} 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_cistatic ssize_t cosa_write(struct file *file, 8458c2ecf20Sopenharmony_ci const char __user *buf, size_t count, loff_t *ppos) 8468c2ecf20Sopenharmony_ci{ 8478c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 8488c2ecf20Sopenharmony_ci struct channel_data *chan = file->private_data; 8498c2ecf20Sopenharmony_ci struct cosa_data *cosa = chan->cosa; 8508c2ecf20Sopenharmony_ci unsigned long flags; 8518c2ecf20Sopenharmony_ci char *kbuf; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci if (!(cosa->firmware_status & COSA_FW_START)) { 8548c2ecf20Sopenharmony_ci pr_notice("%s: start the firmware first (status %d)\n", 8558c2ecf20Sopenharmony_ci cosa->name, cosa->firmware_status); 8568c2ecf20Sopenharmony_ci return -EPERM; 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci if (down_interruptible(&chan->wsem)) 8598c2ecf20Sopenharmony_ci return -ERESTARTSYS; 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci if (count > COSA_MTU) 8628c2ecf20Sopenharmony_ci count = COSA_MTU; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci /* Allocate the buffer */ 8658c2ecf20Sopenharmony_ci kbuf = kmalloc(count, GFP_KERNEL|GFP_DMA); 8668c2ecf20Sopenharmony_ci if (kbuf == NULL) { 8678c2ecf20Sopenharmony_ci up(&chan->wsem); 8688c2ecf20Sopenharmony_ci return -ENOMEM; 8698c2ecf20Sopenharmony_ci } 8708c2ecf20Sopenharmony_ci if (copy_from_user(kbuf, buf, count)) { 8718c2ecf20Sopenharmony_ci up(&chan->wsem); 8728c2ecf20Sopenharmony_ci kfree(kbuf); 8738c2ecf20Sopenharmony_ci return -EFAULT; 8748c2ecf20Sopenharmony_ci } 8758c2ecf20Sopenharmony_ci chan->tx_status=0; 8768c2ecf20Sopenharmony_ci cosa_start_tx(chan, kbuf, count); 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci spin_lock_irqsave(&cosa->lock, flags); 8798c2ecf20Sopenharmony_ci add_wait_queue(&chan->txwaitq, &wait); 8808c2ecf20Sopenharmony_ci while (!chan->tx_status) { 8818c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 8828c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cosa->lock, flags); 8838c2ecf20Sopenharmony_ci schedule(); 8848c2ecf20Sopenharmony_ci spin_lock_irqsave(&cosa->lock, flags); 8858c2ecf20Sopenharmony_ci if (signal_pending(current) && chan->tx_status == 0) { 8868c2ecf20Sopenharmony_ci chan->tx_status = 1; 8878c2ecf20Sopenharmony_ci remove_wait_queue(&chan->txwaitq, &wait); 8888c2ecf20Sopenharmony_ci __set_current_state(TASK_RUNNING); 8898c2ecf20Sopenharmony_ci chan->tx_status = 1; 8908c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cosa->lock, flags); 8918c2ecf20Sopenharmony_ci up(&chan->wsem); 8928c2ecf20Sopenharmony_ci kfree(kbuf); 8938c2ecf20Sopenharmony_ci return -ERESTARTSYS; 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci } 8968c2ecf20Sopenharmony_ci remove_wait_queue(&chan->txwaitq, &wait); 8978c2ecf20Sopenharmony_ci __set_current_state(TASK_RUNNING); 8988c2ecf20Sopenharmony_ci up(&chan->wsem); 8998c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cosa->lock, flags); 9008c2ecf20Sopenharmony_ci kfree(kbuf); 9018c2ecf20Sopenharmony_ci return count; 9028c2ecf20Sopenharmony_ci} 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_cistatic int chrdev_tx_done(struct channel_data *chan, int size) 9058c2ecf20Sopenharmony_ci{ 9068c2ecf20Sopenharmony_ci if (chan->tx_status) { /* Writer was interrupted */ 9078c2ecf20Sopenharmony_ci kfree(chan->txbuf); 9088c2ecf20Sopenharmony_ci up(&chan->wsem); 9098c2ecf20Sopenharmony_ci } 9108c2ecf20Sopenharmony_ci chan->tx_status = 1; 9118c2ecf20Sopenharmony_ci wake_up_interruptible(&chan->txwaitq); 9128c2ecf20Sopenharmony_ci return 1; 9138c2ecf20Sopenharmony_ci} 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_cistatic __poll_t cosa_poll(struct file *file, poll_table *poll) 9168c2ecf20Sopenharmony_ci{ 9178c2ecf20Sopenharmony_ci pr_info("cosa_poll is here\n"); 9188c2ecf20Sopenharmony_ci return 0; 9198c2ecf20Sopenharmony_ci} 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_cistatic int cosa_open(struct inode *inode, struct file *file) 9228c2ecf20Sopenharmony_ci{ 9238c2ecf20Sopenharmony_ci struct cosa_data *cosa; 9248c2ecf20Sopenharmony_ci struct channel_data *chan; 9258c2ecf20Sopenharmony_ci unsigned long flags; 9268c2ecf20Sopenharmony_ci int n; 9278c2ecf20Sopenharmony_ci int ret = 0; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci mutex_lock(&cosa_chardev_mutex); 9308c2ecf20Sopenharmony_ci if ((n=iminor(file_inode(file))>>CARD_MINOR_BITS) 9318c2ecf20Sopenharmony_ci >= nr_cards) { 9328c2ecf20Sopenharmony_ci ret = -ENODEV; 9338c2ecf20Sopenharmony_ci goto out; 9348c2ecf20Sopenharmony_ci } 9358c2ecf20Sopenharmony_ci cosa = cosa_cards+n; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci if ((n=iminor(file_inode(file)) 9388c2ecf20Sopenharmony_ci & ((1<<CARD_MINOR_BITS)-1)) >= cosa->nchannels) { 9398c2ecf20Sopenharmony_ci ret = -ENODEV; 9408c2ecf20Sopenharmony_ci goto out; 9418c2ecf20Sopenharmony_ci } 9428c2ecf20Sopenharmony_ci chan = cosa->chan + n; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci file->private_data = chan; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci spin_lock_irqsave(&cosa->lock, flags); 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci if (chan->usage < 0) { /* in netdev mode */ 9498c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cosa->lock, flags); 9508c2ecf20Sopenharmony_ci ret = -EBUSY; 9518c2ecf20Sopenharmony_ci goto out; 9528c2ecf20Sopenharmony_ci } 9538c2ecf20Sopenharmony_ci cosa->usage++; 9548c2ecf20Sopenharmony_ci chan->usage++; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci chan->tx_done = chrdev_tx_done; 9578c2ecf20Sopenharmony_ci chan->setup_rx = chrdev_setup_rx; 9588c2ecf20Sopenharmony_ci chan->rx_done = chrdev_rx_done; 9598c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cosa->lock, flags); 9608c2ecf20Sopenharmony_ciout: 9618c2ecf20Sopenharmony_ci mutex_unlock(&cosa_chardev_mutex); 9628c2ecf20Sopenharmony_ci return ret; 9638c2ecf20Sopenharmony_ci} 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_cistatic int cosa_release(struct inode *inode, struct file *file) 9668c2ecf20Sopenharmony_ci{ 9678c2ecf20Sopenharmony_ci struct channel_data *channel = file->private_data; 9688c2ecf20Sopenharmony_ci struct cosa_data *cosa; 9698c2ecf20Sopenharmony_ci unsigned long flags; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci cosa = channel->cosa; 9728c2ecf20Sopenharmony_ci spin_lock_irqsave(&cosa->lock, flags); 9738c2ecf20Sopenharmony_ci cosa->usage--; 9748c2ecf20Sopenharmony_ci channel->usage--; 9758c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cosa->lock, flags); 9768c2ecf20Sopenharmony_ci return 0; 9778c2ecf20Sopenharmony_ci} 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci#ifdef COSA_FASYNC_WORKING 9808c2ecf20Sopenharmony_cistatic struct fasync_struct *fasync[256] = { NULL, }; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci/* To be done ... */ 9838c2ecf20Sopenharmony_cistatic int cosa_fasync(struct inode *inode, struct file *file, int on) 9848c2ecf20Sopenharmony_ci{ 9858c2ecf20Sopenharmony_ci int port = iminor(inode); 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci return fasync_helper(inode, file, on, &fasync[port]); 9888c2ecf20Sopenharmony_ci} 9898c2ecf20Sopenharmony_ci#endif 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci/* ---------- Ioctls ---------- */ 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci/* 9958c2ecf20Sopenharmony_ci * Ioctl subroutines can safely be made inline, because they are called 9968c2ecf20Sopenharmony_ci * only from cosa_ioctl(). 9978c2ecf20Sopenharmony_ci */ 9988c2ecf20Sopenharmony_cistatic inline int cosa_reset(struct cosa_data *cosa) 9998c2ecf20Sopenharmony_ci{ 10008c2ecf20Sopenharmony_ci char idstring[COSA_MAX_ID_STRING]; 10018c2ecf20Sopenharmony_ci if (cosa->usage > 1) 10028c2ecf20Sopenharmony_ci pr_info("cosa%d: WARNING: reset requested with cosa->usage > 1 (%d). Odd things may happen.\n", 10038c2ecf20Sopenharmony_ci cosa->num, cosa->usage); 10048c2ecf20Sopenharmony_ci cosa->firmware_status &= ~(COSA_FW_RESET|COSA_FW_START); 10058c2ecf20Sopenharmony_ci if (cosa_reset_and_read_id(cosa, idstring) < 0) { 10068c2ecf20Sopenharmony_ci pr_notice("cosa%d: reset failed\n", cosa->num); 10078c2ecf20Sopenharmony_ci return -EIO; 10088c2ecf20Sopenharmony_ci } 10098c2ecf20Sopenharmony_ci pr_info("cosa%d: resetting device: %s\n", cosa->num, idstring); 10108c2ecf20Sopenharmony_ci cosa->firmware_status |= COSA_FW_RESET; 10118c2ecf20Sopenharmony_ci return 0; 10128c2ecf20Sopenharmony_ci} 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci/* High-level function to download data into COSA memory. Calls download() */ 10158c2ecf20Sopenharmony_cistatic inline int cosa_download(struct cosa_data *cosa, void __user *arg) 10168c2ecf20Sopenharmony_ci{ 10178c2ecf20Sopenharmony_ci struct cosa_download d; 10188c2ecf20Sopenharmony_ci int i; 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci if (cosa->usage > 1) 10218c2ecf20Sopenharmony_ci pr_info("%s: WARNING: download of microcode requested with cosa->usage > 1 (%d). Odd things may happen.\n", 10228c2ecf20Sopenharmony_ci cosa->name, cosa->usage); 10238c2ecf20Sopenharmony_ci if (!(cosa->firmware_status & COSA_FW_RESET)) { 10248c2ecf20Sopenharmony_ci pr_notice("%s: reset the card first (status %d)\n", 10258c2ecf20Sopenharmony_ci cosa->name, cosa->firmware_status); 10268c2ecf20Sopenharmony_ci return -EPERM; 10278c2ecf20Sopenharmony_ci } 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci if (copy_from_user(&d, arg, sizeof(d))) 10308c2ecf20Sopenharmony_ci return -EFAULT; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci if (d.addr < 0 || d.addr > COSA_MAX_FIRMWARE_SIZE) 10338c2ecf20Sopenharmony_ci return -EINVAL; 10348c2ecf20Sopenharmony_ci if (d.len < 0 || d.len > COSA_MAX_FIRMWARE_SIZE) 10358c2ecf20Sopenharmony_ci return -EINVAL; 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci /* If something fails, force the user to reset the card */ 10398c2ecf20Sopenharmony_ci cosa->firmware_status &= ~(COSA_FW_RESET|COSA_FW_DOWNLOAD); 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci i = download(cosa, d.code, d.len, d.addr); 10428c2ecf20Sopenharmony_ci if (i < 0) { 10438c2ecf20Sopenharmony_ci pr_notice("cosa%d: microcode download failed: %d\n", 10448c2ecf20Sopenharmony_ci cosa->num, i); 10458c2ecf20Sopenharmony_ci return -EIO; 10468c2ecf20Sopenharmony_ci } 10478c2ecf20Sopenharmony_ci pr_info("cosa%d: downloading microcode - 0x%04x bytes at 0x%04x\n", 10488c2ecf20Sopenharmony_ci cosa->num, d.len, d.addr); 10498c2ecf20Sopenharmony_ci cosa->firmware_status |= COSA_FW_RESET|COSA_FW_DOWNLOAD; 10508c2ecf20Sopenharmony_ci return 0; 10518c2ecf20Sopenharmony_ci} 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci/* High-level function to read COSA memory. Calls readmem() */ 10548c2ecf20Sopenharmony_cistatic inline int cosa_readmem(struct cosa_data *cosa, void __user *arg) 10558c2ecf20Sopenharmony_ci{ 10568c2ecf20Sopenharmony_ci struct cosa_download d; 10578c2ecf20Sopenharmony_ci int i; 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci if (cosa->usage > 1) 10608c2ecf20Sopenharmony_ci pr_info("cosa%d: WARNING: readmem requested with cosa->usage > 1 (%d). Odd things may happen.\n", 10618c2ecf20Sopenharmony_ci cosa->num, cosa->usage); 10628c2ecf20Sopenharmony_ci if (!(cosa->firmware_status & COSA_FW_RESET)) { 10638c2ecf20Sopenharmony_ci pr_notice("%s: reset the card first (status %d)\n", 10648c2ecf20Sopenharmony_ci cosa->name, cosa->firmware_status); 10658c2ecf20Sopenharmony_ci return -EPERM; 10668c2ecf20Sopenharmony_ci } 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci if (copy_from_user(&d, arg, sizeof(d))) 10698c2ecf20Sopenharmony_ci return -EFAULT; 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci /* If something fails, force the user to reset the card */ 10728c2ecf20Sopenharmony_ci cosa->firmware_status &= ~COSA_FW_RESET; 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci i = readmem(cosa, d.code, d.len, d.addr); 10758c2ecf20Sopenharmony_ci if (i < 0) { 10768c2ecf20Sopenharmony_ci pr_notice("cosa%d: reading memory failed: %d\n", cosa->num, i); 10778c2ecf20Sopenharmony_ci return -EIO; 10788c2ecf20Sopenharmony_ci } 10798c2ecf20Sopenharmony_ci pr_info("cosa%d: reading card memory - 0x%04x bytes at 0x%04x\n", 10808c2ecf20Sopenharmony_ci cosa->num, d.len, d.addr); 10818c2ecf20Sopenharmony_ci cosa->firmware_status |= COSA_FW_RESET; 10828c2ecf20Sopenharmony_ci return 0; 10838c2ecf20Sopenharmony_ci} 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci/* High-level function to start microcode. Calls startmicrocode(). */ 10868c2ecf20Sopenharmony_cistatic inline int cosa_start(struct cosa_data *cosa, int address) 10878c2ecf20Sopenharmony_ci{ 10888c2ecf20Sopenharmony_ci int i; 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci if (cosa->usage > 1) 10918c2ecf20Sopenharmony_ci pr_info("cosa%d: WARNING: start microcode requested with cosa->usage > 1 (%d). Odd things may happen.\n", 10928c2ecf20Sopenharmony_ci cosa->num, cosa->usage); 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci if ((cosa->firmware_status & (COSA_FW_RESET|COSA_FW_DOWNLOAD)) 10958c2ecf20Sopenharmony_ci != (COSA_FW_RESET|COSA_FW_DOWNLOAD)) { 10968c2ecf20Sopenharmony_ci pr_notice("%s: download the microcode and/or reset the card first (status %d)\n", 10978c2ecf20Sopenharmony_ci cosa->name, cosa->firmware_status); 10988c2ecf20Sopenharmony_ci return -EPERM; 10998c2ecf20Sopenharmony_ci } 11008c2ecf20Sopenharmony_ci cosa->firmware_status &= ~COSA_FW_RESET; 11018c2ecf20Sopenharmony_ci if ((i=startmicrocode(cosa, address)) < 0) { 11028c2ecf20Sopenharmony_ci pr_notice("cosa%d: start microcode at 0x%04x failed: %d\n", 11038c2ecf20Sopenharmony_ci cosa->num, address, i); 11048c2ecf20Sopenharmony_ci return -EIO; 11058c2ecf20Sopenharmony_ci } 11068c2ecf20Sopenharmony_ci pr_info("cosa%d: starting microcode at 0x%04x\n", cosa->num, address); 11078c2ecf20Sopenharmony_ci cosa->startaddr = address; 11088c2ecf20Sopenharmony_ci cosa->firmware_status |= COSA_FW_START; 11098c2ecf20Sopenharmony_ci return 0; 11108c2ecf20Sopenharmony_ci} 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci/* Buffer of size at least COSA_MAX_ID_STRING is expected */ 11138c2ecf20Sopenharmony_cistatic inline int cosa_getidstr(struct cosa_data *cosa, char __user *string) 11148c2ecf20Sopenharmony_ci{ 11158c2ecf20Sopenharmony_ci int l = strlen(cosa->id_string)+1; 11168c2ecf20Sopenharmony_ci if (copy_to_user(string, cosa->id_string, l)) 11178c2ecf20Sopenharmony_ci return -EFAULT; 11188c2ecf20Sopenharmony_ci return l; 11198c2ecf20Sopenharmony_ci} 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci/* Buffer of size at least COSA_MAX_ID_STRING is expected */ 11228c2ecf20Sopenharmony_cistatic inline int cosa_gettype(struct cosa_data *cosa, char __user *string) 11238c2ecf20Sopenharmony_ci{ 11248c2ecf20Sopenharmony_ci int l = strlen(cosa->type)+1; 11258c2ecf20Sopenharmony_ci if (copy_to_user(string, cosa->type, l)) 11268c2ecf20Sopenharmony_ci return -EFAULT; 11278c2ecf20Sopenharmony_ci return l; 11288c2ecf20Sopenharmony_ci} 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_cistatic int cosa_ioctl_common(struct cosa_data *cosa, 11318c2ecf20Sopenharmony_ci struct channel_data *channel, unsigned int cmd, unsigned long arg) 11328c2ecf20Sopenharmony_ci{ 11338c2ecf20Sopenharmony_ci void __user *argp = (void __user *)arg; 11348c2ecf20Sopenharmony_ci switch (cmd) { 11358c2ecf20Sopenharmony_ci case COSAIORSET: /* Reset the device */ 11368c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 11378c2ecf20Sopenharmony_ci return -EACCES; 11388c2ecf20Sopenharmony_ci return cosa_reset(cosa); 11398c2ecf20Sopenharmony_ci case COSAIOSTRT: /* Start the firmware */ 11408c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_RAWIO)) 11418c2ecf20Sopenharmony_ci return -EACCES; 11428c2ecf20Sopenharmony_ci return cosa_start(cosa, arg); 11438c2ecf20Sopenharmony_ci case COSAIODOWNLD: /* Download the firmware */ 11448c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_RAWIO)) 11458c2ecf20Sopenharmony_ci return -EACCES; 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci return cosa_download(cosa, argp); 11488c2ecf20Sopenharmony_ci case COSAIORMEM: 11498c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_RAWIO)) 11508c2ecf20Sopenharmony_ci return -EACCES; 11518c2ecf20Sopenharmony_ci return cosa_readmem(cosa, argp); 11528c2ecf20Sopenharmony_ci case COSAIORTYPE: 11538c2ecf20Sopenharmony_ci return cosa_gettype(cosa, argp); 11548c2ecf20Sopenharmony_ci case COSAIORIDSTR: 11558c2ecf20Sopenharmony_ci return cosa_getidstr(cosa, argp); 11568c2ecf20Sopenharmony_ci case COSAIONRCARDS: 11578c2ecf20Sopenharmony_ci return nr_cards; 11588c2ecf20Sopenharmony_ci case COSAIONRCHANS: 11598c2ecf20Sopenharmony_ci return cosa->nchannels; 11608c2ecf20Sopenharmony_ci case COSAIOBMSET: 11618c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_RAWIO)) 11628c2ecf20Sopenharmony_ci return -EACCES; 11638c2ecf20Sopenharmony_ci if (is_8bit(cosa)) 11648c2ecf20Sopenharmony_ci return -EINVAL; 11658c2ecf20Sopenharmony_ci if (arg != COSA_BM_OFF && arg != COSA_BM_ON) 11668c2ecf20Sopenharmony_ci return -EINVAL; 11678c2ecf20Sopenharmony_ci cosa->busmaster = arg; 11688c2ecf20Sopenharmony_ci return 0; 11698c2ecf20Sopenharmony_ci case COSAIOBMGET: 11708c2ecf20Sopenharmony_ci return cosa->busmaster; 11718c2ecf20Sopenharmony_ci } 11728c2ecf20Sopenharmony_ci return -ENOIOCTLCMD; 11738c2ecf20Sopenharmony_ci} 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_cistatic int cosa_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) 11768c2ecf20Sopenharmony_ci{ 11778c2ecf20Sopenharmony_ci int rv; 11788c2ecf20Sopenharmony_ci struct channel_data *chan = dev_to_chan(dev); 11798c2ecf20Sopenharmony_ci rv = cosa_ioctl_common(chan->cosa, chan, cmd, 11808c2ecf20Sopenharmony_ci (unsigned long)ifr->ifr_data); 11818c2ecf20Sopenharmony_ci if (rv != -ENOIOCTLCMD) 11828c2ecf20Sopenharmony_ci return rv; 11838c2ecf20Sopenharmony_ci return hdlc_ioctl(dev, ifr, cmd); 11848c2ecf20Sopenharmony_ci} 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_cistatic long cosa_chardev_ioctl(struct file *file, unsigned int cmd, 11878c2ecf20Sopenharmony_ci unsigned long arg) 11888c2ecf20Sopenharmony_ci{ 11898c2ecf20Sopenharmony_ci struct channel_data *channel = file->private_data; 11908c2ecf20Sopenharmony_ci struct cosa_data *cosa; 11918c2ecf20Sopenharmony_ci long ret; 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci mutex_lock(&cosa_chardev_mutex); 11948c2ecf20Sopenharmony_ci cosa = channel->cosa; 11958c2ecf20Sopenharmony_ci ret = cosa_ioctl_common(cosa, channel, cmd, arg); 11968c2ecf20Sopenharmony_ci mutex_unlock(&cosa_chardev_mutex); 11978c2ecf20Sopenharmony_ci return ret; 11988c2ecf20Sopenharmony_ci} 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci/*---------- HW layer interface ---------- */ 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci/* 12048c2ecf20Sopenharmony_ci * The higher layer can bind itself to the HW layer by setting the callbacks 12058c2ecf20Sopenharmony_ci * in the channel_data structure and by using these routines. 12068c2ecf20Sopenharmony_ci */ 12078c2ecf20Sopenharmony_cistatic void cosa_enable_rx(struct channel_data *chan) 12088c2ecf20Sopenharmony_ci{ 12098c2ecf20Sopenharmony_ci struct cosa_data *cosa = chan->cosa; 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci if (!test_and_set_bit(chan->num, &cosa->rxbitmap)) 12128c2ecf20Sopenharmony_ci put_driver_status(cosa); 12138c2ecf20Sopenharmony_ci} 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_cistatic void cosa_disable_rx(struct channel_data *chan) 12168c2ecf20Sopenharmony_ci{ 12178c2ecf20Sopenharmony_ci struct cosa_data *cosa = chan->cosa; 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci if (test_and_clear_bit(chan->num, &cosa->rxbitmap)) 12208c2ecf20Sopenharmony_ci put_driver_status(cosa); 12218c2ecf20Sopenharmony_ci} 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci/* 12248c2ecf20Sopenharmony_ci * FIXME: This routine probably should check for cosa_start_tx() called when 12258c2ecf20Sopenharmony_ci * the previous transmit is still unfinished. In this case the non-zero 12268c2ecf20Sopenharmony_ci * return value should indicate to the caller that the queuing(sp?) up 12278c2ecf20Sopenharmony_ci * the transmit has failed. 12288c2ecf20Sopenharmony_ci */ 12298c2ecf20Sopenharmony_cistatic int cosa_start_tx(struct channel_data *chan, char *buf, int len) 12308c2ecf20Sopenharmony_ci{ 12318c2ecf20Sopenharmony_ci struct cosa_data *cosa = chan->cosa; 12328c2ecf20Sopenharmony_ci unsigned long flags; 12338c2ecf20Sopenharmony_ci#ifdef DEBUG_DATA 12348c2ecf20Sopenharmony_ci int i; 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci pr_info("cosa%dc%d: starting tx(0x%x)", 12378c2ecf20Sopenharmony_ci chan->cosa->num, chan->num, len); 12388c2ecf20Sopenharmony_ci for (i=0; i<len; i++) 12398c2ecf20Sopenharmony_ci pr_cont(" %02x", buf[i]&0xff); 12408c2ecf20Sopenharmony_ci pr_cont("\n"); 12418c2ecf20Sopenharmony_ci#endif 12428c2ecf20Sopenharmony_ci spin_lock_irqsave(&cosa->lock, flags); 12438c2ecf20Sopenharmony_ci chan->txbuf = buf; 12448c2ecf20Sopenharmony_ci chan->txsize = len; 12458c2ecf20Sopenharmony_ci if (len > COSA_MTU) 12468c2ecf20Sopenharmony_ci chan->txsize = COSA_MTU; 12478c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cosa->lock, flags); 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci /* Tell the firmware we are ready */ 12508c2ecf20Sopenharmony_ci set_bit(chan->num, &cosa->txbitmap); 12518c2ecf20Sopenharmony_ci put_driver_status(cosa); 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci return 0; 12548c2ecf20Sopenharmony_ci} 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_cistatic void put_driver_status(struct cosa_data *cosa) 12578c2ecf20Sopenharmony_ci{ 12588c2ecf20Sopenharmony_ci unsigned long flags; 12598c2ecf20Sopenharmony_ci int status; 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci spin_lock_irqsave(&cosa->lock, flags); 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci status = (cosa->rxbitmap ? DRIVER_RX_READY : 0) 12648c2ecf20Sopenharmony_ci | (cosa->txbitmap ? DRIVER_TX_READY : 0) 12658c2ecf20Sopenharmony_ci | (cosa->txbitmap? ~(cosa->txbitmap<<DRIVER_TXMAP_SHIFT) 12668c2ecf20Sopenharmony_ci &DRIVER_TXMAP_MASK : 0); 12678c2ecf20Sopenharmony_ci if (!cosa->rxtx) { 12688c2ecf20Sopenharmony_ci if (cosa->rxbitmap|cosa->txbitmap) { 12698c2ecf20Sopenharmony_ci if (!cosa->enabled) { 12708c2ecf20Sopenharmony_ci cosa_putstatus(cosa, SR_RX_INT_ENA); 12718c2ecf20Sopenharmony_ci#ifdef DEBUG_IO 12728c2ecf20Sopenharmony_ci debug_status_out(cosa, SR_RX_INT_ENA); 12738c2ecf20Sopenharmony_ci#endif 12748c2ecf20Sopenharmony_ci cosa->enabled = 1; 12758c2ecf20Sopenharmony_ci } 12768c2ecf20Sopenharmony_ci } else if (cosa->enabled) { 12778c2ecf20Sopenharmony_ci cosa->enabled = 0; 12788c2ecf20Sopenharmony_ci cosa_putstatus(cosa, 0); 12798c2ecf20Sopenharmony_ci#ifdef DEBUG_IO 12808c2ecf20Sopenharmony_ci debug_status_out(cosa, 0); 12818c2ecf20Sopenharmony_ci#endif 12828c2ecf20Sopenharmony_ci } 12838c2ecf20Sopenharmony_ci cosa_putdata8(cosa, status); 12848c2ecf20Sopenharmony_ci#ifdef DEBUG_IO 12858c2ecf20Sopenharmony_ci debug_data_cmd(cosa, status); 12868c2ecf20Sopenharmony_ci#endif 12878c2ecf20Sopenharmony_ci } 12888c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cosa->lock, flags); 12898c2ecf20Sopenharmony_ci} 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_cistatic void put_driver_status_nolock(struct cosa_data *cosa) 12928c2ecf20Sopenharmony_ci{ 12938c2ecf20Sopenharmony_ci int status; 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci status = (cosa->rxbitmap ? DRIVER_RX_READY : 0) 12968c2ecf20Sopenharmony_ci | (cosa->txbitmap ? DRIVER_TX_READY : 0) 12978c2ecf20Sopenharmony_ci | (cosa->txbitmap? ~(cosa->txbitmap<<DRIVER_TXMAP_SHIFT) 12988c2ecf20Sopenharmony_ci &DRIVER_TXMAP_MASK : 0); 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci if (cosa->rxbitmap|cosa->txbitmap) { 13018c2ecf20Sopenharmony_ci cosa_putstatus(cosa, SR_RX_INT_ENA); 13028c2ecf20Sopenharmony_ci#ifdef DEBUG_IO 13038c2ecf20Sopenharmony_ci debug_status_out(cosa, SR_RX_INT_ENA); 13048c2ecf20Sopenharmony_ci#endif 13058c2ecf20Sopenharmony_ci cosa->enabled = 1; 13068c2ecf20Sopenharmony_ci } else { 13078c2ecf20Sopenharmony_ci cosa_putstatus(cosa, 0); 13088c2ecf20Sopenharmony_ci#ifdef DEBUG_IO 13098c2ecf20Sopenharmony_ci debug_status_out(cosa, 0); 13108c2ecf20Sopenharmony_ci#endif 13118c2ecf20Sopenharmony_ci cosa->enabled = 0; 13128c2ecf20Sopenharmony_ci } 13138c2ecf20Sopenharmony_ci cosa_putdata8(cosa, status); 13148c2ecf20Sopenharmony_ci#ifdef DEBUG_IO 13158c2ecf20Sopenharmony_ci debug_data_cmd(cosa, status); 13168c2ecf20Sopenharmony_ci#endif 13178c2ecf20Sopenharmony_ci} 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci/* 13208c2ecf20Sopenharmony_ci * The "kickme" function: When the DMA times out, this is called to 13218c2ecf20Sopenharmony_ci * clean up the driver status. 13228c2ecf20Sopenharmony_ci * FIXME: Preliminary support, the interface is probably wrong. 13238c2ecf20Sopenharmony_ci */ 13248c2ecf20Sopenharmony_cistatic void cosa_kick(struct cosa_data *cosa) 13258c2ecf20Sopenharmony_ci{ 13268c2ecf20Sopenharmony_ci unsigned long flags, flags1; 13278c2ecf20Sopenharmony_ci char *s = "(probably) IRQ"; 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci if (test_bit(RXBIT, &cosa->rxtx)) 13308c2ecf20Sopenharmony_ci s = "RX DMA"; 13318c2ecf20Sopenharmony_ci if (test_bit(TXBIT, &cosa->rxtx)) 13328c2ecf20Sopenharmony_ci s = "TX DMA"; 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci pr_info("%s: %s timeout - restarting\n", cosa->name, s); 13358c2ecf20Sopenharmony_ci spin_lock_irqsave(&cosa->lock, flags); 13368c2ecf20Sopenharmony_ci cosa->rxtx = 0; 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci flags1 = claim_dma_lock(); 13398c2ecf20Sopenharmony_ci disable_dma(cosa->dma); 13408c2ecf20Sopenharmony_ci clear_dma_ff(cosa->dma); 13418c2ecf20Sopenharmony_ci release_dma_lock(flags1); 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci /* FIXME: Anything else? */ 13448c2ecf20Sopenharmony_ci udelay(100); 13458c2ecf20Sopenharmony_ci cosa_putstatus(cosa, 0); 13468c2ecf20Sopenharmony_ci udelay(100); 13478c2ecf20Sopenharmony_ci (void) cosa_getdata8(cosa); 13488c2ecf20Sopenharmony_ci udelay(100); 13498c2ecf20Sopenharmony_ci cosa_putdata8(cosa, 0); 13508c2ecf20Sopenharmony_ci udelay(100); 13518c2ecf20Sopenharmony_ci put_driver_status_nolock(cosa); 13528c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cosa->lock, flags); 13538c2ecf20Sopenharmony_ci} 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci/* 13568c2ecf20Sopenharmony_ci * Check if the whole buffer is DMA-able. It means it is below the 16M of 13578c2ecf20Sopenharmony_ci * physical memory and doesn't span the 64k boundary. For now it seems 13588c2ecf20Sopenharmony_ci * SKB's never do this, but we'll check this anyway. 13598c2ecf20Sopenharmony_ci */ 13608c2ecf20Sopenharmony_cistatic int cosa_dma_able(struct channel_data *chan, char *buf, int len) 13618c2ecf20Sopenharmony_ci{ 13628c2ecf20Sopenharmony_ci static int count; 13638c2ecf20Sopenharmony_ci unsigned long b = (unsigned long)buf; 13648c2ecf20Sopenharmony_ci if (b+len >= MAX_DMA_ADDRESS) 13658c2ecf20Sopenharmony_ci return 0; 13668c2ecf20Sopenharmony_ci if ((b^ (b+len)) & 0x10000) { 13678c2ecf20Sopenharmony_ci if (count++ < 5) 13688c2ecf20Sopenharmony_ci pr_info("%s: packet spanning a 64k boundary\n", 13698c2ecf20Sopenharmony_ci chan->name); 13708c2ecf20Sopenharmony_ci return 0; 13718c2ecf20Sopenharmony_ci } 13728c2ecf20Sopenharmony_ci return 1; 13738c2ecf20Sopenharmony_ci} 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci/* ---------- The SRP/COSA ROM monitor functions ---------- */ 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci/* 13798c2ecf20Sopenharmony_ci * Downloading SRP microcode: say "w" to SRP monitor, it answers by "w=", 13808c2ecf20Sopenharmony_ci * drivers need to say 4-digit hex number meaning start address of the microcode 13818c2ecf20Sopenharmony_ci * separated by a single space. Monitor replies by saying " =". Now driver 13828c2ecf20Sopenharmony_ci * has to write 4-digit hex number meaning the last byte address ended 13838c2ecf20Sopenharmony_ci * by a single space. Monitor has to reply with a space. Now the download 13848c2ecf20Sopenharmony_ci * begins. After the download monitor replies with "\r\n." (CR LF dot). 13858c2ecf20Sopenharmony_ci */ 13868c2ecf20Sopenharmony_cistatic int download(struct cosa_data *cosa, const char __user *microcode, int length, int address) 13878c2ecf20Sopenharmony_ci{ 13888c2ecf20Sopenharmony_ci int i; 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci if (put_wait_data(cosa, 'w') == -1) return -1; 13918c2ecf20Sopenharmony_ci if ((i=get_wait_data(cosa)) != 'w') { printk("dnld: 0x%04x\n",i); return -2;} 13928c2ecf20Sopenharmony_ci if (get_wait_data(cosa) != '=') return -3; 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci if (puthexnumber(cosa, address) < 0) return -4; 13958c2ecf20Sopenharmony_ci if (put_wait_data(cosa, ' ') == -1) return -10; 13968c2ecf20Sopenharmony_ci if (get_wait_data(cosa) != ' ') return -11; 13978c2ecf20Sopenharmony_ci if (get_wait_data(cosa) != '=') return -12; 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci if (puthexnumber(cosa, address+length-1) < 0) return -13; 14008c2ecf20Sopenharmony_ci if (put_wait_data(cosa, ' ') == -1) return -18; 14018c2ecf20Sopenharmony_ci if (get_wait_data(cosa) != ' ') return -19; 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci while (length--) { 14048c2ecf20Sopenharmony_ci char c; 14058c2ecf20Sopenharmony_ci#ifndef SRP_DOWNLOAD_AT_BOOT 14068c2ecf20Sopenharmony_ci if (get_user(c, microcode)) 14078c2ecf20Sopenharmony_ci return -23; /* ??? */ 14088c2ecf20Sopenharmony_ci#else 14098c2ecf20Sopenharmony_ci c = *microcode; 14108c2ecf20Sopenharmony_ci#endif 14118c2ecf20Sopenharmony_ci if (put_wait_data(cosa, c) == -1) 14128c2ecf20Sopenharmony_ci return -20; 14138c2ecf20Sopenharmony_ci microcode++; 14148c2ecf20Sopenharmony_ci } 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci if (get_wait_data(cosa) != '\r') return -21; 14178c2ecf20Sopenharmony_ci if (get_wait_data(cosa) != '\n') return -22; 14188c2ecf20Sopenharmony_ci if (get_wait_data(cosa) != '.') return -23; 14198c2ecf20Sopenharmony_ci#if 0 14208c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cosa%d: download completed.\n", cosa->num); 14218c2ecf20Sopenharmony_ci#endif 14228c2ecf20Sopenharmony_ci return 0; 14238c2ecf20Sopenharmony_ci} 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci/* 14278c2ecf20Sopenharmony_ci * Starting microcode is done via the "g" command of the SRP monitor. 14288c2ecf20Sopenharmony_ci * The chat should be the following: "g" "g=" "<addr><CR>" 14298c2ecf20Sopenharmony_ci * "<CR><CR><LF><CR><LF>". 14308c2ecf20Sopenharmony_ci */ 14318c2ecf20Sopenharmony_cistatic int startmicrocode(struct cosa_data *cosa, int address) 14328c2ecf20Sopenharmony_ci{ 14338c2ecf20Sopenharmony_ci if (put_wait_data(cosa, 'g') == -1) return -1; 14348c2ecf20Sopenharmony_ci if (get_wait_data(cosa) != 'g') return -2; 14358c2ecf20Sopenharmony_ci if (get_wait_data(cosa) != '=') return -3; 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci if (puthexnumber(cosa, address) < 0) return -4; 14388c2ecf20Sopenharmony_ci if (put_wait_data(cosa, '\r') == -1) return -5; 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci if (get_wait_data(cosa) != '\r') return -6; 14418c2ecf20Sopenharmony_ci if (get_wait_data(cosa) != '\r') return -7; 14428c2ecf20Sopenharmony_ci if (get_wait_data(cosa) != '\n') return -8; 14438c2ecf20Sopenharmony_ci if (get_wait_data(cosa) != '\r') return -9; 14448c2ecf20Sopenharmony_ci if (get_wait_data(cosa) != '\n') return -10; 14458c2ecf20Sopenharmony_ci#if 0 14468c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cosa%d: microcode started\n", cosa->num); 14478c2ecf20Sopenharmony_ci#endif 14488c2ecf20Sopenharmony_ci return 0; 14498c2ecf20Sopenharmony_ci} 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ci/* 14528c2ecf20Sopenharmony_ci * Reading memory is done via the "r" command of the SRP monitor. 14538c2ecf20Sopenharmony_ci * The chat is the following "r" "r=" "<addr> " " =" "<last_byte> " " " 14548c2ecf20Sopenharmony_ci * Then driver can read the data and the conversation is finished 14558c2ecf20Sopenharmony_ci * by SRP monitor sending "<CR><LF>." (dot at the end). 14568c2ecf20Sopenharmony_ci * 14578c2ecf20Sopenharmony_ci * This routine is not needed during the normal operation and serves 14588c2ecf20Sopenharmony_ci * for debugging purposes only. 14598c2ecf20Sopenharmony_ci */ 14608c2ecf20Sopenharmony_cistatic int readmem(struct cosa_data *cosa, char __user *microcode, int length, int address) 14618c2ecf20Sopenharmony_ci{ 14628c2ecf20Sopenharmony_ci if (put_wait_data(cosa, 'r') == -1) return -1; 14638c2ecf20Sopenharmony_ci if ((get_wait_data(cosa)) != 'r') return -2; 14648c2ecf20Sopenharmony_ci if ((get_wait_data(cosa)) != '=') return -3; 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci if (puthexnumber(cosa, address) < 0) return -4; 14678c2ecf20Sopenharmony_ci if (put_wait_data(cosa, ' ') == -1) return -5; 14688c2ecf20Sopenharmony_ci if (get_wait_data(cosa) != ' ') return -6; 14698c2ecf20Sopenharmony_ci if (get_wait_data(cosa) != '=') return -7; 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci if (puthexnumber(cosa, address+length-1) < 0) return -8; 14728c2ecf20Sopenharmony_ci if (put_wait_data(cosa, ' ') == -1) return -9; 14738c2ecf20Sopenharmony_ci if (get_wait_data(cosa) != ' ') return -10; 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci while (length--) { 14768c2ecf20Sopenharmony_ci char c; 14778c2ecf20Sopenharmony_ci int i; 14788c2ecf20Sopenharmony_ci if ((i=get_wait_data(cosa)) == -1) { 14798c2ecf20Sopenharmony_ci pr_info("0x%04x bytes remaining\n", length); 14808c2ecf20Sopenharmony_ci return -11; 14818c2ecf20Sopenharmony_ci } 14828c2ecf20Sopenharmony_ci c=i; 14838c2ecf20Sopenharmony_ci#if 1 14848c2ecf20Sopenharmony_ci if (put_user(c, microcode)) 14858c2ecf20Sopenharmony_ci return -23; /* ??? */ 14868c2ecf20Sopenharmony_ci#else 14878c2ecf20Sopenharmony_ci *microcode = c; 14888c2ecf20Sopenharmony_ci#endif 14898c2ecf20Sopenharmony_ci microcode++; 14908c2ecf20Sopenharmony_ci } 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci if (get_wait_data(cosa) != '\r') return -21; 14938c2ecf20Sopenharmony_ci if (get_wait_data(cosa) != '\n') return -22; 14948c2ecf20Sopenharmony_ci if (get_wait_data(cosa) != '.') return -23; 14958c2ecf20Sopenharmony_ci#if 0 14968c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cosa%d: readmem completed.\n", cosa->num); 14978c2ecf20Sopenharmony_ci#endif 14988c2ecf20Sopenharmony_ci return 0; 14998c2ecf20Sopenharmony_ci} 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci/* 15028c2ecf20Sopenharmony_ci * This function resets the device and reads the initial prompt 15038c2ecf20Sopenharmony_ci * of the device's ROM monitor. 15048c2ecf20Sopenharmony_ci */ 15058c2ecf20Sopenharmony_cistatic int cosa_reset_and_read_id(struct cosa_data *cosa, char *idstring) 15068c2ecf20Sopenharmony_ci{ 15078c2ecf20Sopenharmony_ci int i=0, id=0, prev=0, curr=0; 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci /* Reset the card ... */ 15108c2ecf20Sopenharmony_ci cosa_putstatus(cosa, 0); 15118c2ecf20Sopenharmony_ci cosa_getdata8(cosa); 15128c2ecf20Sopenharmony_ci cosa_putstatus(cosa, SR_RST); 15138c2ecf20Sopenharmony_ci msleep(500); 15148c2ecf20Sopenharmony_ci /* Disable all IRQs from the card */ 15158c2ecf20Sopenharmony_ci cosa_putstatus(cosa, 0); 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_ci /* 15188c2ecf20Sopenharmony_ci * Try to read the ID string. The card then prints out the 15198c2ecf20Sopenharmony_ci * identification string ended by the "\n\x2e". 15208c2ecf20Sopenharmony_ci * 15218c2ecf20Sopenharmony_ci * The following loop is indexed through i (instead of id) 15228c2ecf20Sopenharmony_ci * to avoid looping forever when for any reason 15238c2ecf20Sopenharmony_ci * the port returns '\r', '\n' or '\x2e' permanently. 15248c2ecf20Sopenharmony_ci */ 15258c2ecf20Sopenharmony_ci for (i=0; i<COSA_MAX_ID_STRING-1; i++, prev=curr) { 15268c2ecf20Sopenharmony_ci if ((curr = get_wait_data(cosa)) == -1) { 15278c2ecf20Sopenharmony_ci return -1; 15288c2ecf20Sopenharmony_ci } 15298c2ecf20Sopenharmony_ci curr &= 0xff; 15308c2ecf20Sopenharmony_ci if (curr != '\r' && curr != '\n' && curr != 0x2e) 15318c2ecf20Sopenharmony_ci idstring[id++] = curr; 15328c2ecf20Sopenharmony_ci if (curr == 0x2e && prev == '\n') 15338c2ecf20Sopenharmony_ci break; 15348c2ecf20Sopenharmony_ci } 15358c2ecf20Sopenharmony_ci /* Perhaps we should fail when i==COSA_MAX_ID_STRING-1 ? */ 15368c2ecf20Sopenharmony_ci idstring[id] = '\0'; 15378c2ecf20Sopenharmony_ci return id; 15388c2ecf20Sopenharmony_ci} 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci/* ---------- Auxiliary routines for COSA/SRP monitor ---------- */ 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_ci/* 15448c2ecf20Sopenharmony_ci * This routine gets the data byte from the card waiting for the SR_RX_RDY 15458c2ecf20Sopenharmony_ci * bit to be set in a loop. It should be used in the exceptional cases 15468c2ecf20Sopenharmony_ci * only (for example when resetting the card or downloading the firmware. 15478c2ecf20Sopenharmony_ci */ 15488c2ecf20Sopenharmony_cistatic int get_wait_data(struct cosa_data *cosa) 15498c2ecf20Sopenharmony_ci{ 15508c2ecf20Sopenharmony_ci int retries = 1000; 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci while (--retries) { 15538c2ecf20Sopenharmony_ci /* read data and return them */ 15548c2ecf20Sopenharmony_ci if (cosa_getstatus(cosa) & SR_RX_RDY) { 15558c2ecf20Sopenharmony_ci short r; 15568c2ecf20Sopenharmony_ci r = cosa_getdata8(cosa); 15578c2ecf20Sopenharmony_ci#if 0 15588c2ecf20Sopenharmony_ci pr_info("get_wait_data returning after %d retries\n", 15598c2ecf20Sopenharmony_ci 999-retries); 15608c2ecf20Sopenharmony_ci#endif 15618c2ecf20Sopenharmony_ci return r; 15628c2ecf20Sopenharmony_ci } 15638c2ecf20Sopenharmony_ci /* sleep if not ready to read */ 15648c2ecf20Sopenharmony_ci schedule_timeout_interruptible(1); 15658c2ecf20Sopenharmony_ci } 15668c2ecf20Sopenharmony_ci pr_info("timeout in get_wait_data (status 0x%x)\n", 15678c2ecf20Sopenharmony_ci cosa_getstatus(cosa)); 15688c2ecf20Sopenharmony_ci return -1; 15698c2ecf20Sopenharmony_ci} 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci/* 15728c2ecf20Sopenharmony_ci * This routine puts the data byte to the card waiting for the SR_TX_RDY 15738c2ecf20Sopenharmony_ci * bit to be set in a loop. It should be used in the exceptional cases 15748c2ecf20Sopenharmony_ci * only (for example when resetting the card or downloading the firmware). 15758c2ecf20Sopenharmony_ci */ 15768c2ecf20Sopenharmony_cistatic int put_wait_data(struct cosa_data *cosa, int data) 15778c2ecf20Sopenharmony_ci{ 15788c2ecf20Sopenharmony_ci int retries = 1000; 15798c2ecf20Sopenharmony_ci while (--retries) { 15808c2ecf20Sopenharmony_ci /* read data and return them */ 15818c2ecf20Sopenharmony_ci if (cosa_getstatus(cosa) & SR_TX_RDY) { 15828c2ecf20Sopenharmony_ci cosa_putdata8(cosa, data); 15838c2ecf20Sopenharmony_ci#if 0 15848c2ecf20Sopenharmony_ci pr_info("Putdata: %d retries\n", 999-retries); 15858c2ecf20Sopenharmony_ci#endif 15868c2ecf20Sopenharmony_ci return 0; 15878c2ecf20Sopenharmony_ci } 15888c2ecf20Sopenharmony_ci#if 0 15898c2ecf20Sopenharmony_ci /* sleep if not ready to read */ 15908c2ecf20Sopenharmony_ci schedule_timeout_interruptible(1); 15918c2ecf20Sopenharmony_ci#endif 15928c2ecf20Sopenharmony_ci } 15938c2ecf20Sopenharmony_ci pr_info("cosa%d: timeout in put_wait_data (status 0x%x)\n", 15948c2ecf20Sopenharmony_ci cosa->num, cosa_getstatus(cosa)); 15958c2ecf20Sopenharmony_ci return -1; 15968c2ecf20Sopenharmony_ci} 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci/* 15998c2ecf20Sopenharmony_ci * The following routine puts the hexadecimal number into the SRP monitor 16008c2ecf20Sopenharmony_ci * and verifies the proper echo of the sent bytes. Returns 0 on success, 16018c2ecf20Sopenharmony_ci * negative number on failure (-1,-3,-5,-7) means that put_wait_data() failed, 16028c2ecf20Sopenharmony_ci * (-2,-4,-6,-8) means that reading echo failed. 16038c2ecf20Sopenharmony_ci */ 16048c2ecf20Sopenharmony_cistatic int puthexnumber(struct cosa_data *cosa, int number) 16058c2ecf20Sopenharmony_ci{ 16068c2ecf20Sopenharmony_ci char temp[5]; 16078c2ecf20Sopenharmony_ci int i; 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci /* Well, I should probably replace this by something faster. */ 16108c2ecf20Sopenharmony_ci sprintf(temp, "%04X", number); 16118c2ecf20Sopenharmony_ci for (i=0; i<4; i++) { 16128c2ecf20Sopenharmony_ci if (put_wait_data(cosa, temp[i]) == -1) { 16138c2ecf20Sopenharmony_ci pr_notice("cosa%d: puthexnumber failed to write byte %d\n", 16148c2ecf20Sopenharmony_ci cosa->num, i); 16158c2ecf20Sopenharmony_ci return -1-2*i; 16168c2ecf20Sopenharmony_ci } 16178c2ecf20Sopenharmony_ci if (get_wait_data(cosa) != temp[i]) { 16188c2ecf20Sopenharmony_ci pr_notice("cosa%d: puthexhumber failed to read echo of byte %d\n", 16198c2ecf20Sopenharmony_ci cosa->num, i); 16208c2ecf20Sopenharmony_ci return -2-2*i; 16218c2ecf20Sopenharmony_ci } 16228c2ecf20Sopenharmony_ci } 16238c2ecf20Sopenharmony_ci return 0; 16248c2ecf20Sopenharmony_ci} 16258c2ecf20Sopenharmony_ci 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ci/* ---------- Interrupt routines ---------- */ 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci/* 16308c2ecf20Sopenharmony_ci * There are three types of interrupt: 16318c2ecf20Sopenharmony_ci * At the beginning of transmit - this handled is in tx_interrupt(), 16328c2ecf20Sopenharmony_ci * at the beginning of receive - it is in rx_interrupt() and 16338c2ecf20Sopenharmony_ci * at the end of transmit/receive - it is the eot_interrupt() function. 16348c2ecf20Sopenharmony_ci * These functions are multiplexed by cosa_interrupt() according to the 16358c2ecf20Sopenharmony_ci * COSA status byte. I have moved the rx/tx/eot interrupt handling into 16368c2ecf20Sopenharmony_ci * separate functions to make it more readable. These functions are inline, 16378c2ecf20Sopenharmony_ci * so there should be no overhead of function call. 16388c2ecf20Sopenharmony_ci * 16398c2ecf20Sopenharmony_ci * In the COSA bus-master mode, we need to tell the card the address of a 16408c2ecf20Sopenharmony_ci * buffer. Unfortunately, COSA may be too slow for us, so we must busy-wait. 16418c2ecf20Sopenharmony_ci * It's time to use the bottom half :-( 16428c2ecf20Sopenharmony_ci */ 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci/* 16458c2ecf20Sopenharmony_ci * Transmit interrupt routine - called when COSA is willing to obtain 16468c2ecf20Sopenharmony_ci * data from the OS. The most tricky part of the routine is selection 16478c2ecf20Sopenharmony_ci * of channel we (OS) want to send packet for. For SRP we should probably 16488c2ecf20Sopenharmony_ci * use the round-robin approach. The newer COSA firmwares have a simple 16498c2ecf20Sopenharmony_ci * flow-control - in the status word has bits 2 and 3 set to 1 means that the 16508c2ecf20Sopenharmony_ci * channel 0 or 1 doesn't want to receive data. 16518c2ecf20Sopenharmony_ci * 16528c2ecf20Sopenharmony_ci * It seems there is a bug in COSA firmware (need to trace it further): 16538c2ecf20Sopenharmony_ci * When the driver status says that the kernel has no more data for transmit 16548c2ecf20Sopenharmony_ci * (e.g. at the end of TX DMA) and then the kernel changes its mind 16558c2ecf20Sopenharmony_ci * (e.g. new packet is queued to hard_start_xmit()), the card issues 16568c2ecf20Sopenharmony_ci * the TX interrupt but does not mark the channel as ready-to-transmit. 16578c2ecf20Sopenharmony_ci * The fix seems to be to push the packet to COSA despite its request. 16588c2ecf20Sopenharmony_ci * We first try to obey the card's opinion, and then fall back to forced TX. 16598c2ecf20Sopenharmony_ci */ 16608c2ecf20Sopenharmony_cistatic inline void tx_interrupt(struct cosa_data *cosa, int status) 16618c2ecf20Sopenharmony_ci{ 16628c2ecf20Sopenharmony_ci unsigned long flags, flags1; 16638c2ecf20Sopenharmony_ci#ifdef DEBUG_IRQS 16648c2ecf20Sopenharmony_ci pr_info("cosa%d: SR_DOWN_REQUEST status=0x%04x\n", cosa->num, status); 16658c2ecf20Sopenharmony_ci#endif 16668c2ecf20Sopenharmony_ci spin_lock_irqsave(&cosa->lock, flags); 16678c2ecf20Sopenharmony_ci set_bit(TXBIT, &cosa->rxtx); 16688c2ecf20Sopenharmony_ci if (!test_bit(IRQBIT, &cosa->rxtx)) { 16698c2ecf20Sopenharmony_ci /* flow control, see the comment above */ 16708c2ecf20Sopenharmony_ci int i=0; 16718c2ecf20Sopenharmony_ci if (!cosa->txbitmap) { 16728c2ecf20Sopenharmony_ci pr_warn("%s: No channel wants data in TX IRQ. Expect DMA timeout.\n", 16738c2ecf20Sopenharmony_ci cosa->name); 16748c2ecf20Sopenharmony_ci put_driver_status_nolock(cosa); 16758c2ecf20Sopenharmony_ci clear_bit(TXBIT, &cosa->rxtx); 16768c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cosa->lock, flags); 16778c2ecf20Sopenharmony_ci return; 16788c2ecf20Sopenharmony_ci } 16798c2ecf20Sopenharmony_ci while (1) { 16808c2ecf20Sopenharmony_ci cosa->txchan++; 16818c2ecf20Sopenharmony_ci i++; 16828c2ecf20Sopenharmony_ci if (cosa->txchan >= cosa->nchannels) 16838c2ecf20Sopenharmony_ci cosa->txchan = 0; 16848c2ecf20Sopenharmony_ci if (!(cosa->txbitmap & (1<<cosa->txchan))) 16858c2ecf20Sopenharmony_ci continue; 16868c2ecf20Sopenharmony_ci if (~status & (1 << (cosa->txchan+DRIVER_TXMAP_SHIFT))) 16878c2ecf20Sopenharmony_ci break; 16888c2ecf20Sopenharmony_ci /* in second pass, accept first ready-to-TX channel */ 16898c2ecf20Sopenharmony_ci if (i > cosa->nchannels) { 16908c2ecf20Sopenharmony_ci /* Can be safely ignored */ 16918c2ecf20Sopenharmony_ci#ifdef DEBUG_IRQS 16928c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: Forcing TX " 16938c2ecf20Sopenharmony_ci "to not-ready channel %d\n", 16948c2ecf20Sopenharmony_ci cosa->name, cosa->txchan); 16958c2ecf20Sopenharmony_ci#endif 16968c2ecf20Sopenharmony_ci break; 16978c2ecf20Sopenharmony_ci } 16988c2ecf20Sopenharmony_ci } 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_ci cosa->txsize = cosa->chan[cosa->txchan].txsize; 17018c2ecf20Sopenharmony_ci if (cosa_dma_able(cosa->chan+cosa->txchan, 17028c2ecf20Sopenharmony_ci cosa->chan[cosa->txchan].txbuf, cosa->txsize)) { 17038c2ecf20Sopenharmony_ci cosa->txbuf = cosa->chan[cosa->txchan].txbuf; 17048c2ecf20Sopenharmony_ci } else { 17058c2ecf20Sopenharmony_ci memcpy(cosa->bouncebuf, cosa->chan[cosa->txchan].txbuf, 17068c2ecf20Sopenharmony_ci cosa->txsize); 17078c2ecf20Sopenharmony_ci cosa->txbuf = cosa->bouncebuf; 17088c2ecf20Sopenharmony_ci } 17098c2ecf20Sopenharmony_ci } 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci if (is_8bit(cosa)) { 17128c2ecf20Sopenharmony_ci if (!test_bit(IRQBIT, &cosa->rxtx)) { 17138c2ecf20Sopenharmony_ci cosa_putstatus(cosa, SR_TX_INT_ENA); 17148c2ecf20Sopenharmony_ci cosa_putdata8(cosa, ((cosa->txchan << 5) & 0xe0)| 17158c2ecf20Sopenharmony_ci ((cosa->txsize >> 8) & 0x1f)); 17168c2ecf20Sopenharmony_ci#ifdef DEBUG_IO 17178c2ecf20Sopenharmony_ci debug_status_out(cosa, SR_TX_INT_ENA); 17188c2ecf20Sopenharmony_ci debug_data_out(cosa, ((cosa->txchan << 5) & 0xe0)| 17198c2ecf20Sopenharmony_ci ((cosa->txsize >> 8) & 0x1f)); 17208c2ecf20Sopenharmony_ci debug_data_in(cosa, cosa_getdata8(cosa)); 17218c2ecf20Sopenharmony_ci#else 17228c2ecf20Sopenharmony_ci cosa_getdata8(cosa); 17238c2ecf20Sopenharmony_ci#endif 17248c2ecf20Sopenharmony_ci set_bit(IRQBIT, &cosa->rxtx); 17258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cosa->lock, flags); 17268c2ecf20Sopenharmony_ci return; 17278c2ecf20Sopenharmony_ci } else { 17288c2ecf20Sopenharmony_ci clear_bit(IRQBIT, &cosa->rxtx); 17298c2ecf20Sopenharmony_ci cosa_putstatus(cosa, 0); 17308c2ecf20Sopenharmony_ci cosa_putdata8(cosa, cosa->txsize&0xff); 17318c2ecf20Sopenharmony_ci#ifdef DEBUG_IO 17328c2ecf20Sopenharmony_ci debug_status_out(cosa, 0); 17338c2ecf20Sopenharmony_ci debug_data_out(cosa, cosa->txsize&0xff); 17348c2ecf20Sopenharmony_ci#endif 17358c2ecf20Sopenharmony_ci } 17368c2ecf20Sopenharmony_ci } else { 17378c2ecf20Sopenharmony_ci cosa_putstatus(cosa, SR_TX_INT_ENA); 17388c2ecf20Sopenharmony_ci cosa_putdata16(cosa, ((cosa->txchan<<13) & 0xe000) 17398c2ecf20Sopenharmony_ci | (cosa->txsize & 0x1fff)); 17408c2ecf20Sopenharmony_ci#ifdef DEBUG_IO 17418c2ecf20Sopenharmony_ci debug_status_out(cosa, SR_TX_INT_ENA); 17428c2ecf20Sopenharmony_ci debug_data_out(cosa, ((cosa->txchan<<13) & 0xe000) 17438c2ecf20Sopenharmony_ci | (cosa->txsize & 0x1fff)); 17448c2ecf20Sopenharmony_ci debug_data_in(cosa, cosa_getdata8(cosa)); 17458c2ecf20Sopenharmony_ci debug_status_out(cosa, 0); 17468c2ecf20Sopenharmony_ci#else 17478c2ecf20Sopenharmony_ci cosa_getdata8(cosa); 17488c2ecf20Sopenharmony_ci#endif 17498c2ecf20Sopenharmony_ci cosa_putstatus(cosa, 0); 17508c2ecf20Sopenharmony_ci } 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_ci if (cosa->busmaster) { 17538c2ecf20Sopenharmony_ci unsigned long addr = virt_to_bus(cosa->txbuf); 17548c2ecf20Sopenharmony_ci int count=0; 17558c2ecf20Sopenharmony_ci pr_info("busmaster IRQ\n"); 17568c2ecf20Sopenharmony_ci while (!(cosa_getstatus(cosa)&SR_TX_RDY)) { 17578c2ecf20Sopenharmony_ci count++; 17588c2ecf20Sopenharmony_ci udelay(10); 17598c2ecf20Sopenharmony_ci if (count > 1000) break; 17608c2ecf20Sopenharmony_ci } 17618c2ecf20Sopenharmony_ci pr_info("status %x\n", cosa_getstatus(cosa)); 17628c2ecf20Sopenharmony_ci pr_info("ready after %d loops\n", count); 17638c2ecf20Sopenharmony_ci cosa_putdata16(cosa, (addr >> 16)&0xffff); 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_ci count = 0; 17668c2ecf20Sopenharmony_ci while (!(cosa_getstatus(cosa)&SR_TX_RDY)) { 17678c2ecf20Sopenharmony_ci count++; 17688c2ecf20Sopenharmony_ci if (count > 1000) break; 17698c2ecf20Sopenharmony_ci udelay(10); 17708c2ecf20Sopenharmony_ci } 17718c2ecf20Sopenharmony_ci pr_info("ready after %d loops\n", count); 17728c2ecf20Sopenharmony_ci cosa_putdata16(cosa, addr &0xffff); 17738c2ecf20Sopenharmony_ci flags1 = claim_dma_lock(); 17748c2ecf20Sopenharmony_ci set_dma_mode(cosa->dma, DMA_MODE_CASCADE); 17758c2ecf20Sopenharmony_ci enable_dma(cosa->dma); 17768c2ecf20Sopenharmony_ci release_dma_lock(flags1); 17778c2ecf20Sopenharmony_ci } else { 17788c2ecf20Sopenharmony_ci /* start the DMA */ 17798c2ecf20Sopenharmony_ci flags1 = claim_dma_lock(); 17808c2ecf20Sopenharmony_ci disable_dma(cosa->dma); 17818c2ecf20Sopenharmony_ci clear_dma_ff(cosa->dma); 17828c2ecf20Sopenharmony_ci set_dma_mode(cosa->dma, DMA_MODE_WRITE); 17838c2ecf20Sopenharmony_ci set_dma_addr(cosa->dma, virt_to_bus(cosa->txbuf)); 17848c2ecf20Sopenharmony_ci set_dma_count(cosa->dma, cosa->txsize); 17858c2ecf20Sopenharmony_ci enable_dma(cosa->dma); 17868c2ecf20Sopenharmony_ci release_dma_lock(flags1); 17878c2ecf20Sopenharmony_ci } 17888c2ecf20Sopenharmony_ci cosa_putstatus(cosa, SR_TX_DMA_ENA|SR_USR_INT_ENA); 17898c2ecf20Sopenharmony_ci#ifdef DEBUG_IO 17908c2ecf20Sopenharmony_ci debug_status_out(cosa, SR_TX_DMA_ENA|SR_USR_INT_ENA); 17918c2ecf20Sopenharmony_ci#endif 17928c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cosa->lock, flags); 17938c2ecf20Sopenharmony_ci} 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_cistatic inline void rx_interrupt(struct cosa_data *cosa, int status) 17968c2ecf20Sopenharmony_ci{ 17978c2ecf20Sopenharmony_ci unsigned long flags; 17988c2ecf20Sopenharmony_ci#ifdef DEBUG_IRQS 17998c2ecf20Sopenharmony_ci pr_info("cosa%d: SR_UP_REQUEST\n", cosa->num); 18008c2ecf20Sopenharmony_ci#endif 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_ci spin_lock_irqsave(&cosa->lock, flags); 18038c2ecf20Sopenharmony_ci set_bit(RXBIT, &cosa->rxtx); 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_ci if (is_8bit(cosa)) { 18068c2ecf20Sopenharmony_ci if (!test_bit(IRQBIT, &cosa->rxtx)) { 18078c2ecf20Sopenharmony_ci set_bit(IRQBIT, &cosa->rxtx); 18088c2ecf20Sopenharmony_ci put_driver_status_nolock(cosa); 18098c2ecf20Sopenharmony_ci cosa->rxsize = cosa_getdata8(cosa) <<8; 18108c2ecf20Sopenharmony_ci#ifdef DEBUG_IO 18118c2ecf20Sopenharmony_ci debug_data_in(cosa, cosa->rxsize >> 8); 18128c2ecf20Sopenharmony_ci#endif 18138c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cosa->lock, flags); 18148c2ecf20Sopenharmony_ci return; 18158c2ecf20Sopenharmony_ci } else { 18168c2ecf20Sopenharmony_ci clear_bit(IRQBIT, &cosa->rxtx); 18178c2ecf20Sopenharmony_ci cosa->rxsize |= cosa_getdata8(cosa) & 0xff; 18188c2ecf20Sopenharmony_ci#ifdef DEBUG_IO 18198c2ecf20Sopenharmony_ci debug_data_in(cosa, cosa->rxsize & 0xff); 18208c2ecf20Sopenharmony_ci#endif 18218c2ecf20Sopenharmony_ci#if 0 18228c2ecf20Sopenharmony_ci pr_info("cosa%d: receive rxsize = (0x%04x)\n", 18238c2ecf20Sopenharmony_ci cosa->num, cosa->rxsize); 18248c2ecf20Sopenharmony_ci#endif 18258c2ecf20Sopenharmony_ci } 18268c2ecf20Sopenharmony_ci } else { 18278c2ecf20Sopenharmony_ci cosa->rxsize = cosa_getdata16(cosa); 18288c2ecf20Sopenharmony_ci#ifdef DEBUG_IO 18298c2ecf20Sopenharmony_ci debug_data_in(cosa, cosa->rxsize); 18308c2ecf20Sopenharmony_ci#endif 18318c2ecf20Sopenharmony_ci#if 0 18328c2ecf20Sopenharmony_ci pr_info("cosa%d: receive rxsize = (0x%04x)\n", 18338c2ecf20Sopenharmony_ci cosa->num, cosa->rxsize); 18348c2ecf20Sopenharmony_ci#endif 18358c2ecf20Sopenharmony_ci } 18368c2ecf20Sopenharmony_ci if (((cosa->rxsize & 0xe000) >> 13) >= cosa->nchannels) { 18378c2ecf20Sopenharmony_ci pr_warn("%s: rx for unknown channel (0x%04x)\n", 18388c2ecf20Sopenharmony_ci cosa->name, cosa->rxsize); 18398c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cosa->lock, flags); 18408c2ecf20Sopenharmony_ci goto reject; 18418c2ecf20Sopenharmony_ci } 18428c2ecf20Sopenharmony_ci cosa->rxchan = cosa->chan + ((cosa->rxsize & 0xe000) >> 13); 18438c2ecf20Sopenharmony_ci cosa->rxsize &= 0x1fff; 18448c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cosa->lock, flags); 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_ci cosa->rxbuf = NULL; 18478c2ecf20Sopenharmony_ci if (cosa->rxchan->setup_rx) 18488c2ecf20Sopenharmony_ci cosa->rxbuf = cosa->rxchan->setup_rx(cosa->rxchan, cosa->rxsize); 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_ci if (!cosa->rxbuf) { 18518c2ecf20Sopenharmony_cireject: /* Reject the packet */ 18528c2ecf20Sopenharmony_ci pr_info("cosa%d: rejecting packet on channel %d\n", 18538c2ecf20Sopenharmony_ci cosa->num, cosa->rxchan->num); 18548c2ecf20Sopenharmony_ci cosa->rxbuf = cosa->bouncebuf; 18558c2ecf20Sopenharmony_ci } 18568c2ecf20Sopenharmony_ci 18578c2ecf20Sopenharmony_ci /* start the DMA */ 18588c2ecf20Sopenharmony_ci flags = claim_dma_lock(); 18598c2ecf20Sopenharmony_ci disable_dma(cosa->dma); 18608c2ecf20Sopenharmony_ci clear_dma_ff(cosa->dma); 18618c2ecf20Sopenharmony_ci set_dma_mode(cosa->dma, DMA_MODE_READ); 18628c2ecf20Sopenharmony_ci if (cosa_dma_able(cosa->rxchan, cosa->rxbuf, cosa->rxsize & 0x1fff)) { 18638c2ecf20Sopenharmony_ci set_dma_addr(cosa->dma, virt_to_bus(cosa->rxbuf)); 18648c2ecf20Sopenharmony_ci } else { 18658c2ecf20Sopenharmony_ci set_dma_addr(cosa->dma, virt_to_bus(cosa->bouncebuf)); 18668c2ecf20Sopenharmony_ci } 18678c2ecf20Sopenharmony_ci set_dma_count(cosa->dma, (cosa->rxsize&0x1fff)); 18688c2ecf20Sopenharmony_ci enable_dma(cosa->dma); 18698c2ecf20Sopenharmony_ci release_dma_lock(flags); 18708c2ecf20Sopenharmony_ci spin_lock_irqsave(&cosa->lock, flags); 18718c2ecf20Sopenharmony_ci cosa_putstatus(cosa, SR_RX_DMA_ENA|SR_USR_INT_ENA); 18728c2ecf20Sopenharmony_ci if (!is_8bit(cosa) && (status & SR_TX_RDY)) 18738c2ecf20Sopenharmony_ci cosa_putdata8(cosa, DRIVER_RX_READY); 18748c2ecf20Sopenharmony_ci#ifdef DEBUG_IO 18758c2ecf20Sopenharmony_ci debug_status_out(cosa, SR_RX_DMA_ENA|SR_USR_INT_ENA); 18768c2ecf20Sopenharmony_ci if (!is_8bit(cosa) && (status & SR_TX_RDY)) 18778c2ecf20Sopenharmony_ci debug_data_cmd(cosa, DRIVER_RX_READY); 18788c2ecf20Sopenharmony_ci#endif 18798c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cosa->lock, flags); 18808c2ecf20Sopenharmony_ci} 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_cistatic inline void eot_interrupt(struct cosa_data *cosa, int status) 18838c2ecf20Sopenharmony_ci{ 18848c2ecf20Sopenharmony_ci unsigned long flags, flags1; 18858c2ecf20Sopenharmony_ci spin_lock_irqsave(&cosa->lock, flags); 18868c2ecf20Sopenharmony_ci flags1 = claim_dma_lock(); 18878c2ecf20Sopenharmony_ci disable_dma(cosa->dma); 18888c2ecf20Sopenharmony_ci clear_dma_ff(cosa->dma); 18898c2ecf20Sopenharmony_ci release_dma_lock(flags1); 18908c2ecf20Sopenharmony_ci if (test_bit(TXBIT, &cosa->rxtx)) { 18918c2ecf20Sopenharmony_ci struct channel_data *chan = cosa->chan+cosa->txchan; 18928c2ecf20Sopenharmony_ci if (chan->tx_done) 18938c2ecf20Sopenharmony_ci if (chan->tx_done(chan, cosa->txsize)) 18948c2ecf20Sopenharmony_ci clear_bit(chan->num, &cosa->txbitmap); 18958c2ecf20Sopenharmony_ci } else if (test_bit(RXBIT, &cosa->rxtx)) { 18968c2ecf20Sopenharmony_ci#ifdef DEBUG_DATA 18978c2ecf20Sopenharmony_ci { 18988c2ecf20Sopenharmony_ci int i; 18998c2ecf20Sopenharmony_ci pr_info("cosa%dc%d: done rx(0x%x)", 19008c2ecf20Sopenharmony_ci cosa->num, cosa->rxchan->num, cosa->rxsize); 19018c2ecf20Sopenharmony_ci for (i=0; i<cosa->rxsize; i++) 19028c2ecf20Sopenharmony_ci pr_cont(" %02x", cosa->rxbuf[i]&0xff); 19038c2ecf20Sopenharmony_ci pr_cont("\n"); 19048c2ecf20Sopenharmony_ci } 19058c2ecf20Sopenharmony_ci#endif 19068c2ecf20Sopenharmony_ci /* Packet for unknown channel? */ 19078c2ecf20Sopenharmony_ci if (cosa->rxbuf == cosa->bouncebuf) 19088c2ecf20Sopenharmony_ci goto out; 19098c2ecf20Sopenharmony_ci if (!cosa_dma_able(cosa->rxchan, cosa->rxbuf, cosa->rxsize)) 19108c2ecf20Sopenharmony_ci memcpy(cosa->rxbuf, cosa->bouncebuf, cosa->rxsize); 19118c2ecf20Sopenharmony_ci if (cosa->rxchan->rx_done) 19128c2ecf20Sopenharmony_ci if (cosa->rxchan->rx_done(cosa->rxchan)) 19138c2ecf20Sopenharmony_ci clear_bit(cosa->rxchan->num, &cosa->rxbitmap); 19148c2ecf20Sopenharmony_ci } else { 19158c2ecf20Sopenharmony_ci pr_notice("cosa%d: unexpected EOT interrupt\n", cosa->num); 19168c2ecf20Sopenharmony_ci } 19178c2ecf20Sopenharmony_ci /* 19188c2ecf20Sopenharmony_ci * Clear the RXBIT, TXBIT and IRQBIT (the latest should be 19198c2ecf20Sopenharmony_ci * cleared anyway). We should do it as soon as possible 19208c2ecf20Sopenharmony_ci * so that we can tell the COSA we are done and to give it a time 19218c2ecf20Sopenharmony_ci * for recovery. 19228c2ecf20Sopenharmony_ci */ 19238c2ecf20Sopenharmony_ciout: 19248c2ecf20Sopenharmony_ci cosa->rxtx = 0; 19258c2ecf20Sopenharmony_ci put_driver_status_nolock(cosa); 19268c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cosa->lock, flags); 19278c2ecf20Sopenharmony_ci} 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_cistatic irqreturn_t cosa_interrupt(int irq, void *cosa_) 19308c2ecf20Sopenharmony_ci{ 19318c2ecf20Sopenharmony_ci unsigned status; 19328c2ecf20Sopenharmony_ci int count = 0; 19338c2ecf20Sopenharmony_ci struct cosa_data *cosa = cosa_; 19348c2ecf20Sopenharmony_ciagain: 19358c2ecf20Sopenharmony_ci status = cosa_getstatus(cosa); 19368c2ecf20Sopenharmony_ci#ifdef DEBUG_IRQS 19378c2ecf20Sopenharmony_ci pr_info("cosa%d: got IRQ, status 0x%02x\n", cosa->num, status & 0xff); 19388c2ecf20Sopenharmony_ci#endif 19398c2ecf20Sopenharmony_ci#ifdef DEBUG_IO 19408c2ecf20Sopenharmony_ci debug_status_in(cosa, status); 19418c2ecf20Sopenharmony_ci#endif 19428c2ecf20Sopenharmony_ci switch (status & SR_CMD_FROM_SRP_MASK) { 19438c2ecf20Sopenharmony_ci case SR_DOWN_REQUEST: 19448c2ecf20Sopenharmony_ci tx_interrupt(cosa, status); 19458c2ecf20Sopenharmony_ci break; 19468c2ecf20Sopenharmony_ci case SR_UP_REQUEST: 19478c2ecf20Sopenharmony_ci rx_interrupt(cosa, status); 19488c2ecf20Sopenharmony_ci break; 19498c2ecf20Sopenharmony_ci case SR_END_OF_TRANSFER: 19508c2ecf20Sopenharmony_ci eot_interrupt(cosa, status); 19518c2ecf20Sopenharmony_ci break; 19528c2ecf20Sopenharmony_ci default: 19538c2ecf20Sopenharmony_ci /* We may be too fast for SRP. Try to wait a bit more. */ 19548c2ecf20Sopenharmony_ci if (count++ < 100) { 19558c2ecf20Sopenharmony_ci udelay(100); 19568c2ecf20Sopenharmony_ci goto again; 19578c2ecf20Sopenharmony_ci } 19588c2ecf20Sopenharmony_ci pr_info("cosa%d: unknown status 0x%02x in IRQ after %d retries\n", 19598c2ecf20Sopenharmony_ci cosa->num, status & 0xff, count); 19608c2ecf20Sopenharmony_ci } 19618c2ecf20Sopenharmony_ci#ifdef DEBUG_IRQS 19628c2ecf20Sopenharmony_ci if (count) 19638c2ecf20Sopenharmony_ci pr_info("%s: %d-times got unknown status in IRQ\n", 19648c2ecf20Sopenharmony_ci cosa->name, count); 19658c2ecf20Sopenharmony_ci else 19668c2ecf20Sopenharmony_ci pr_info("%s: returning from IRQ\n", cosa->name); 19678c2ecf20Sopenharmony_ci#endif 19688c2ecf20Sopenharmony_ci return IRQ_HANDLED; 19698c2ecf20Sopenharmony_ci} 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci 19728c2ecf20Sopenharmony_ci/* ---------- I/O debugging routines ---------- */ 19738c2ecf20Sopenharmony_ci/* 19748c2ecf20Sopenharmony_ci * These routines can be used to monitor COSA/SRP I/O and to printk() 19758c2ecf20Sopenharmony_ci * the data being transferred on the data and status I/O port in a 19768c2ecf20Sopenharmony_ci * readable way. 19778c2ecf20Sopenharmony_ci */ 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci#ifdef DEBUG_IO 19808c2ecf20Sopenharmony_cistatic void debug_status_in(struct cosa_data *cosa, int status) 19818c2ecf20Sopenharmony_ci{ 19828c2ecf20Sopenharmony_ci char *s; 19838c2ecf20Sopenharmony_ci switch (status & SR_CMD_FROM_SRP_MASK) { 19848c2ecf20Sopenharmony_ci case SR_UP_REQUEST: 19858c2ecf20Sopenharmony_ci s = "RX_REQ"; 19868c2ecf20Sopenharmony_ci break; 19878c2ecf20Sopenharmony_ci case SR_DOWN_REQUEST: 19888c2ecf20Sopenharmony_ci s = "TX_REQ"; 19898c2ecf20Sopenharmony_ci break; 19908c2ecf20Sopenharmony_ci case SR_END_OF_TRANSFER: 19918c2ecf20Sopenharmony_ci s = "ET_REQ"; 19928c2ecf20Sopenharmony_ci break; 19938c2ecf20Sopenharmony_ci default: 19948c2ecf20Sopenharmony_ci s = "NO_REQ"; 19958c2ecf20Sopenharmony_ci break; 19968c2ecf20Sopenharmony_ci } 19978c2ecf20Sopenharmony_ci pr_info("%s: IO: status -> 0x%02x (%s%s%s%s)\n", 19988c2ecf20Sopenharmony_ci cosa->name, 19998c2ecf20Sopenharmony_ci status, 20008c2ecf20Sopenharmony_ci status & SR_USR_RQ ? "USR_RQ|" : "", 20018c2ecf20Sopenharmony_ci status & SR_TX_RDY ? "TX_RDY|" : "", 20028c2ecf20Sopenharmony_ci status & SR_RX_RDY ? "RX_RDY|" : "", 20038c2ecf20Sopenharmony_ci s); 20048c2ecf20Sopenharmony_ci} 20058c2ecf20Sopenharmony_ci 20068c2ecf20Sopenharmony_cistatic void debug_status_out(struct cosa_data *cosa, int status) 20078c2ecf20Sopenharmony_ci{ 20088c2ecf20Sopenharmony_ci pr_info("%s: IO: status <- 0x%02x (%s%s%s%s%s%s)\n", 20098c2ecf20Sopenharmony_ci cosa->name, 20108c2ecf20Sopenharmony_ci status, 20118c2ecf20Sopenharmony_ci status & SR_RX_DMA_ENA ? "RXDMA|" : "!rxdma|", 20128c2ecf20Sopenharmony_ci status & SR_TX_DMA_ENA ? "TXDMA|" : "!txdma|", 20138c2ecf20Sopenharmony_ci status & SR_RST ? "RESET|" : "", 20148c2ecf20Sopenharmony_ci status & SR_USR_INT_ENA ? "USRINT|" : "!usrint|", 20158c2ecf20Sopenharmony_ci status & SR_TX_INT_ENA ? "TXINT|" : "!txint|", 20168c2ecf20Sopenharmony_ci status & SR_RX_INT_ENA ? "RXINT" : "!rxint"); 20178c2ecf20Sopenharmony_ci} 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_cistatic void debug_data_in(struct cosa_data *cosa, int data) 20208c2ecf20Sopenharmony_ci{ 20218c2ecf20Sopenharmony_ci pr_info("%s: IO: data -> 0x%04x\n", cosa->name, data); 20228c2ecf20Sopenharmony_ci} 20238c2ecf20Sopenharmony_ci 20248c2ecf20Sopenharmony_cistatic void debug_data_out(struct cosa_data *cosa, int data) 20258c2ecf20Sopenharmony_ci{ 20268c2ecf20Sopenharmony_ci pr_info("%s: IO: data <- 0x%04x\n", cosa->name, data); 20278c2ecf20Sopenharmony_ci} 20288c2ecf20Sopenharmony_ci 20298c2ecf20Sopenharmony_cistatic void debug_data_cmd(struct cosa_data *cosa, int data) 20308c2ecf20Sopenharmony_ci{ 20318c2ecf20Sopenharmony_ci pr_info("%s: IO: data <- 0x%04x (%s|%s)\n", 20328c2ecf20Sopenharmony_ci cosa->name, data, 20338c2ecf20Sopenharmony_ci data & SR_RDY_RCV ? "RX_RDY" : "!rx_rdy", 20348c2ecf20Sopenharmony_ci data & SR_RDY_SND ? "TX_RDY" : "!tx_rdy"); 20358c2ecf20Sopenharmony_ci} 20368c2ecf20Sopenharmony_ci#endif 20378c2ecf20Sopenharmony_ci 20388c2ecf20Sopenharmony_ci/* EOF -- this file has not been truncated */ 2039