diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 22:20:36 (GMT) |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 22:20:36 (GMT) |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/net/irda/w83977af_ir.c | |
download | linux-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.xz |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/net/irda/w83977af_ir.c')
-rw-r--r-- | drivers/net/irda/w83977af_ir.c | 1379 |
1 files changed, 1379 insertions, 0 deletions
diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c new file mode 100644 index 0000000..0ea65c4 --- /dev/null +++ b/drivers/net/irda/w83977af_ir.c @@ -0,0 +1,1379 @@ +/********************************************************************* + * + * Filename: w83977af_ir.c + * Version: 1.0 + * Description: FIR driver for the Winbond W83977AF Super I/O chip + * Status: Experimental. + * Author: Paul VanderSpek + * Created at: Wed Nov 4 11:46:16 1998 + * Modified at: Fri Jan 28 12:10:59 2000 + * Modified by: Dag Brattli <dagb@cs.uit.no> + * + * Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no> + * Copyright (c) 1998-1999 Rebel.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Paul VanderSpek nor Rebel.com admit liability nor provide + * warranty for any of this software. This material is provided "AS-IS" + * and at no charge. + * + * If you find bugs in this file, its very likely that the same bug + * will also be in pc87108.c since the implementations are quite + * similar. + * + * Notice that all functions that needs to access the chip in _any_ + * way, must save BSR register on entry, and restore it on exit. + * It is _very_ important to follow this policy! + * + * __u8 bank; + * + * bank = inb( iobase+BSR); + * + * do_your_stuff_here(); + * + * outb( bank, iobase+BSR); + * + ********************************************************************/ + +#include <linux/module.h> +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/rtnetlink.h> +#include <linux/dma-mapping.h> + +#include <asm/io.h> +#include <asm/dma.h> +#include <asm/byteorder.h> + +#include <net/irda/irda.h> +#include <net/irda/wrapper.h> +#include <net/irda/irda_device.h> +#include "w83977af.h" +#include "w83977af_ir.h" + +#ifdef CONFIG_ARCH_NETWINDER /* Adjust to NetWinder differences */ +#undef CONFIG_NETWINDER_TX_DMA_PROBLEMS /* Not needed */ +#define CONFIG_NETWINDER_RX_DMA_PROBLEMS /* Must have this one! */ +#endif +#undef CONFIG_USE_INTERNAL_TIMER /* Just cannot make that timer work */ +#define CONFIG_USE_W977_PNP /* Currently needed */ +#define PIO_MAX_SPEED 115200 + +static char *driver_name = "w83977af_ir"; +static int qos_mtt_bits = 0x07; /* 1 ms or more */ + +#define CHIP_IO_EXTENT 8 + +static unsigned int io[] = { 0x180, ~0, ~0, ~0 }; +#ifdef CONFIG_ARCH_NETWINDER /* Adjust to NetWinder differences */ +static unsigned int irq[] = { 6, 0, 0, 0 }; +#else +static unsigned int irq[] = { 11, 0, 0, 0 }; +#endif +static unsigned int dma[] = { 1, 0, 0, 0 }; +static unsigned int efbase[] = { W977_EFIO_BASE, W977_EFIO2_BASE }; +static unsigned int efio = W977_EFIO_BASE; + +static struct w83977af_ir *dev_self[] = { NULL, NULL, NULL, NULL}; + +/* Some prototypes */ +static int w83977af_open(int i, unsigned int iobase, unsigned int irq, + unsigned int dma); +static int w83977af_close(struct w83977af_ir *self); +static int w83977af_probe(int iobase, int irq, int dma); +static int w83977af_dma_receive(struct w83977af_ir *self); +static int w83977af_dma_receive_complete(struct w83977af_ir *self); +static int w83977af_hard_xmit(struct sk_buff *skb, struct net_device *dev); +static int w83977af_pio_write(int iobase, __u8 *buf, int len, int fifo_size); +static void w83977af_dma_write(struct w83977af_ir *self, int iobase); +static void w83977af_change_speed(struct w83977af_ir *self, __u32 speed); +static int w83977af_is_receiving(struct w83977af_ir *self); + +static int w83977af_net_open(struct net_device *dev); +static int w83977af_net_close(struct net_device *dev); +static int w83977af_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static struct net_device_stats *w83977af_net_get_stats(struct net_device *dev); + +/* + * Function w83977af_init () + * + * Initialize chip. Just try to find out how many chips we are dealing with + * and where they are + */ +static int __init w83977af_init(void) +{ + int i; + + IRDA_DEBUG(0, "%s()\n", __FUNCTION__ ); + + for (i=0; (io[i] < 2000) && (i < 4); i++) { + if (w83977af_open(i, io[i], irq[i], dma[i]) == 0) + return 0; + } + return -ENODEV; +} + +/* + * Function w83977af_cleanup () + * + * Close all configured chips + * + */ +static void __exit w83977af_cleanup(void) +{ + int i; + + IRDA_DEBUG(4, "%s()\n", __FUNCTION__ ); + + for (i=0; i < 4; i++) { + if (dev_self[i]) + w83977af_close(dev_self[i]); + } +} + +/* + * Function w83977af_open (iobase, irq) + * + * Open driver instance + * + */ +int w83977af_open(int i, unsigned int iobase, unsigned int irq, + unsigned int dma) +{ + struct net_device *dev; + struct w83977af_ir *self; + int err; + + IRDA_DEBUG(0, "%s()\n", __FUNCTION__ ); + + /* Lock the port that we need */ + if (!request_region(iobase, CHIP_IO_EXTENT, driver_name)) { + IRDA_DEBUG(0, "%s(), can't get iobase of 0x%03x\n", + __FUNCTION__ , iobase); + return -ENODEV; + } + + if (w83977af_probe(iobase, irq, dma) == -1) { + err = -1; + goto err_out; + } + /* + * Allocate new instance of the driver + */ + dev = alloc_irdadev(sizeof(struct w83977af_ir)); + if (dev == NULL) { + printk( KERN_ERR "IrDA: Can't allocate memory for " + "IrDA control block!\n"); + err = -ENOMEM; + goto err_out; + } + + self = dev->priv; + spin_lock_init(&self->lock); + + + /* Initialize IO */ + self->io.fir_base = iobase; + self->io.irq = irq; + self->io.fir_ext = CHIP_IO_EXTENT; + self->io.dma = dma; + self->io.fifo_size = 32; + + /* Initialize QoS for this device */ + irda_init_max_qos_capabilies(&self->qos); + + /* The only value we must override it the baudrate */ + + /* FIXME: The HP HDLS-1100 does not support 1152000! */ + self->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600| + IR_115200|IR_576000|IR_1152000|(IR_4000000 << 8); + + /* The HP HDLS-1100 needs 1 ms according to the specs */ + self->qos.min_turn_time.bits = qos_mtt_bits; + irda_qos_bits_to_value(&self->qos); + + /* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */ + self->rx_buff.truesize = 14384; + self->tx_buff.truesize = 4000; + + /* Allocate memory if needed */ + self->rx_buff.head = + dma_alloc_coherent(NULL, self->rx_buff.truesize, + &self->rx_buff_dma, GFP_KERNEL); + if (self->rx_buff.head == NULL) { + err = -ENOMEM; + goto err_out1; + } + + memset(self->rx_buff.head, 0, self->rx_buff.truesize); + + self->tx_buff.head = + dma_alloc_coherent(NULL, self->tx_buff.truesize, + &self->tx_buff_dma, GFP_KERNEL); + if (self->tx_buff.head == NULL) { + err = -ENOMEM; + goto err_out2; + } + memset(self->tx_buff.head, 0, self->tx_buff.truesize); + + self->rx_buff.in_frame = FALSE; + self->rx_buff.state = OUTSIDE_FRAME; + self->tx_buff.data = self->tx_buff.head; + self->rx_buff.data = self->rx_buff.head; + self->netdev = dev; + + /* Keep track of module usage */ + SET_MODULE_OWNER(dev); + + /* Override the network functions we need to use */ + dev->hard_start_xmit = w83977af_hard_xmit; + dev->open = w83977af_net_open; + dev->stop = w83977af_net_close; + dev->do_ioctl = w83977af_net_ioctl; + dev->get_stats = w83977af_net_get_stats; + + err = register_netdev(dev); + if (err) { + IRDA_ERROR("%s(), register_netdevice() failed!\n", __FUNCTION__); + goto err_out3; + } + IRDA_MESSAGE("IrDA: Registered device %s\n", dev->name); + + /* Need to store self somewhere */ + dev_self[i] = self; + + return 0; +err_out3: + dma_free_coherent(NULL, self->tx_buff.truesize, + self->tx_buff.head, self->tx_buff_dma); +err_out2: + dma_free_coherent(NULL, self->rx_buff.truesize, + self->rx_buff.head, self->rx_buff_dma); +err_out1: + free_netdev(dev); +err_out: + release_region(iobase, CHIP_IO_EXTENT); + return err; +} + +/* + * Function w83977af_close (self) + * + * Close driver instance + * + */ +static int w83977af_close(struct w83977af_ir *self) +{ + int iobase; + + IRDA_DEBUG(0, "%s()\n", __FUNCTION__ ); + + iobase = self->io.fir_base; + +#ifdef CONFIG_USE_W977_PNP + /* enter PnP configuration mode */ + w977_efm_enter(efio); + + w977_select_device(W977_DEVICE_IR, efio); + + /* Deactivate device */ + w977_write_reg(0x30, 0x00, efio); + + w977_efm_exit(efio); +#endif /* CONFIG_USE_W977_PNP */ + + /* Remove netdevice */ + unregister_netdev(self->netdev); + + /* Release the PORT that this driver is using */ + IRDA_DEBUG(0 , "%s(), Releasing Region %03x\n", + __FUNCTION__ , self->io.fir_base); + release_region(self->io.fir_base, self->io.fir_ext); + + if (self->tx_buff.head) + dma_free_coherent(NULL, self->tx_buff.truesize, + self->tx_buff.head, self->tx_buff_dma); + + if (self->rx_buff.head) + dma_free_coherent(NULL, self->rx_buff.truesize, + self->rx_buff.head, self->rx_buff_dma); + + free_netdev(self->netdev); + + return 0; +} + +int w83977af_probe( int iobase, int irq, int dma) +{ + int version; + int i; + + for (i=0; i < 2; i++) { + IRDA_DEBUG( 0, "%s()\n", __FUNCTION__ ); +#ifdef CONFIG_USE_W977_PNP + /* Enter PnP configuration mode */ + w977_efm_enter(efbase[i]); + + w977_select_device(W977_DEVICE_IR, efbase[i]); + + /* Configure PnP port, IRQ, and DMA channel */ + w977_write_reg(0x60, (iobase >> 8) & 0xff, efbase[i]); + w977_write_reg(0x61, (iobase) & 0xff, efbase[i]); + + w977_write_reg(0x70, irq, efbase[i]); +#ifdef CONFIG_ARCH_NETWINDER + /* Netwinder uses 1 higher than Linux */ + w977_write_reg(0x74, dma+1, efbase[i]); +#else + w977_write_reg(0x74, dma, efbase[i]); +#endif /*CONFIG_ARCH_NETWINDER */ + w977_write_reg(0x75, 0x04, efbase[i]); /* Disable Tx DMA */ + + /* Set append hardware CRC, enable IR bank selection */ + w977_write_reg(0xf0, APEDCRC|ENBNKSEL, efbase[i]); + + /* Activate device */ + w977_write_reg(0x30, 0x01, efbase[i]); + + w977_efm_exit(efbase[i]); +#endif /* CONFIG_USE_W977_PNP */ + /* Disable Advanced mode */ + switch_bank(iobase, SET2); + outb(iobase+2, 0x00); + + /* Turn on UART (global) interrupts */ + switch_bank(iobase, SET0); + outb(HCR_EN_IRQ, iobase+HCR); + + /* Switch to advanced mode */ + switch_bank(iobase, SET2); + outb(inb(iobase+ADCR1) | ADCR1_ADV_SL, iobase+ADCR1); + + /* Set default IR-mode */ + switch_bank(iobase, SET0); + outb(HCR_SIR, iobase+HCR); + + /* Read the Advanced IR ID */ + switch_bank(iobase, SET3); + version = inb(iobase+AUID); + + /* Should be 0x1? */ + if (0x10 == (version & 0xf0)) { + efio = efbase[i]; + + /* Set FIFO size to 32 */ + switch_bank(iobase, SET2); + outb(ADCR2_RXFS32|ADCR2_TXFS32, iobase+ADCR2); + + /* Set FIFO threshold to TX17, RX16 */ + switch_bank(iobase, SET0); + outb(UFR_RXTL|UFR_TXTL|UFR_TXF_RST|UFR_RXF_RST| + UFR_EN_FIFO,iobase+UFR); + + /* Receiver frame length */ + switch_bank(iobase, SET4); + outb(2048 & 0xff, iobase+6); + outb((2048 >> 8) & 0x1f, iobase+7); + + /* + * Init HP HSDL-1100 transceiver. + * + * Set IRX_MSL since we have 2 * receive paths IRRX, + * and IRRXH. Clear IRSL0D since we want IRSL0 * to + * be a input pin used for IRRXH + * + * IRRX pin 37 connected to receiver + * IRTX pin 38 connected to transmitter + * FIRRX pin 39 connected to receiver (IRSL0) + * CIRRX pin 40 connected to pin 37 + */ + switch_bank(iobase, SET7); + outb(0x40, iobase+7); + + IRDA_MESSAGE("W83977AF (IR) driver loaded. " + "Version: 0x%02x\n", version); + + return 0; + } else { + /* Try next extented function register address */ + IRDA_DEBUG( 0, "%s(), Wrong chip version", __FUNCTION__ ); + } + } + return -1; +} + +void w83977af_change_speed(struct w83977af_ir *self, __u32 speed) +{ + int ir_mode = HCR_SIR; + int iobase; + __u8 set; + + iobase = self->io.fir_base; + + /* Update accounting for new speed */ + self->io.speed = speed; + + /* Save current bank */ + set = inb(iobase+SSR); + + /* Disable interrupts */ + switch_bank(iobase, SET0); + outb(0, iobase+ICR); + + /* Select Set 2 */ + switch_bank(iobase, SET2); + outb(0x00, iobase+ABHL); + + switch (speed) { + case 9600: outb(0x0c, iobase+ABLL); break; + case 19200: outb(0x06, iobase+ABLL); break; + case 38400: outb(0x03, iobase+ABLL); break; + case 57600: outb(0x02, iobase+ABLL); break; + case 115200: outb(0x01, iobase+ABLL); break; + case 576000: + ir_mode = HCR_MIR_576; + IRDA_DEBUG(0, "%s(), handling baud of 576000\n", __FUNCTION__ ); + break; + case 1152000: + ir_mode = HCR_MIR_1152; + IRDA_DEBUG(0, "%s(), handling baud of 1152000\n", __FUNCTION__ ); + break; + case 4000000: + ir_mode = HCR_FIR; + IRDA_DEBUG(0, "%s(), handling baud of 4000000\n", __FUNCTION__ ); + break; + default: + ir_mode = HCR_FIR; + IRDA_DEBUG(0, "%s(), unknown baud rate of %d\n", __FUNCTION__ , speed); + break; + } + + /* Set speed mode */ + switch_bank(iobase, SET0); + outb(ir_mode, iobase+HCR); + + /* set FIFO size to 32 */ + switch_bank(iobase, SET2); + outb(ADCR2_RXFS32|ADCR2_TXFS32, iobase+ADCR2); + + /* set FIFO threshold to TX17, RX16 */ + switch_bank(iobase, SET0); + outb(0x00, iobase+UFR); /* Reset */ + outb(UFR_EN_FIFO, iobase+UFR); /* First we must enable FIFO */ + outb(0xa7, iobase+UFR); + + netif_wake_queue(self->netdev); + + /* Enable some interrupts so we can receive frames */ + switch_bank(iobase, SET0); + if (speed > PIO_MAX_SPEED) { + outb(ICR_EFSFI, iobase+ICR); + w83977af_dma_receive(self); + } else + outb(ICR_ERBRI, iobase+ICR); + + /* Restore SSR */ + outb(set, iobase+SSR); +} + +/* + * Function w83977af_hard_xmit (skb, dev) + * + * Sets up a DMA transfer to send the current frame. + * + */ +int w83977af_hard_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct w83977af_ir *self; + __s32 speed; + int iobase; + __u8 set; + int mtt; + + self = (struct w83977af_ir *) dev->priv; + + iobase = self->io.fir_base; + + IRDA_DEBUG(4, "%s(%ld), skb->len=%d\n", __FUNCTION__ , jiffies, + (int) skb->len); + + /* Lock transmit buffer */ + netif_stop_queue(dev); + + /* Check if we need to change the speed */ + speed = irda_get_next_speed(skb); + if ((speed != self->io.speed) && (speed != -1)) { + /* Check for empty frame */ + if (!skb->len) { + w83977af_change_speed(self, speed); + dev->trans_start = jiffies; + dev_kfree_skb(skb); + return 0; + } else + self->new_speed = speed; + } + + /* Save current set */ + set = inb(iobase+SSR); + + /* Decide if we should use PIO or DMA transfer */ + if (self->io.speed > PIO_MAX_SPEED) { + self->tx_buff.data = self->tx_buff.head; + memcpy(self->tx_buff.data, skb->data, skb->len); + self->tx_buff.len = skb->len; + + mtt = irda_get_mtt(skb); +#ifdef CONFIG_USE_INTERNAL_TIMER + if (mtt > 50) { + /* Adjust for timer resolution */ + mtt /= 1000+1; + + /* Setup timer */ + switch_bank(iobase, SET4); + outb(mtt & 0xff, iobase+TMRL); + outb((mtt >> 8) & 0x0f, iobase+TMRH); + + /* Start timer */ + outb(IR_MSL_EN_TMR, iobase+IR_MSL); + self->io.direction = IO_XMIT; + + /* Enable timer interrupt */ + switch_bank(iobase, SET0); + outb(ICR_ETMRI, iobase+ICR); + } else { +#endif + IRDA_DEBUG(4, "%s(%ld), mtt=%d\n", __FUNCTION__ , jiffies, mtt); + if (mtt) + udelay(mtt); + + /* Enable DMA interrupt */ + switch_bank(iobase, SET0); + outb(ICR_EDMAI, iobase+ICR); + w83977af_dma_write(self, iobase); +#ifdef CONFIG_USE_INTERNAL_TIMER + } +#endif + } else { + self->tx_buff.data = self->tx_buff.head; + self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, + self->tx_buff.truesize); + + /* Add interrupt on tx low level (will fire immediately) */ + switch_bank(iobase, SET0); + outb(ICR_ETXTHI, iobase+ICR); + } + dev->trans_start = jiffies; + dev_kfree_skb(skb); + + /* Restore set register */ + outb(set, iobase+SSR); + + return 0; +} + +/* + * Function w83977af_dma_write (self, iobase) + * + * Send frame using DMA + * + */ +static void w83977af_dma_write(struct w83977af_ir *self, int iobase) +{ + __u8 set; +#ifdef CONFIG_NETWINDER_TX_DMA_PROBLEMS + unsigned long flags; + __u8 hcr; +#endif + IRDA_DEBUG(4, "%s(), len=%d\n", __FUNCTION__ , self->tx_buff.len); + + /* Save current set */ + set = inb(iobase+SSR); + + /* Disable DMA */ + switch_bank(iobase, SET0); + outb(inb(iobase+HCR) & ~HCR_EN_DMA, iobase+HCR); + + /* Choose transmit DMA channel */ + switch_bank(iobase, SET2); + outb(ADCR1_D_CHSW|/*ADCR1_DMA_F|*/ADCR1_ADV_SL, iobase+ADCR1); +#ifdef CONFIG_NETWINDER_TX_DMA_PROBLEMS + spin_lock_irqsave(&self->lock, flags); + + disable_dma(self->io.dma); + clear_dma_ff(self->io.dma); + set_dma_mode(self->io.dma, DMA_MODE_READ); + set_dma_addr(self->io.dma, self->tx_buff_dma); + set_dma_count(self->io.dma, self->tx_buff.len); +#else + irda_setup_dma(self->io.dma, self->tx_buff_dma, self->tx_buff.len, + DMA_MODE_WRITE); +#endif + self->io.direction = IO_XMIT; + + /* Enable DMA */ + switch_bank(iobase, SET0); +#ifdef CONFIG_NETWINDER_TX_DMA_PROBLEMS + hcr = inb(iobase+HCR); + outb(hcr | HCR_EN_DMA, iobase+HCR); + enable_dma(self->io.dma); + spin_unlock_irqrestore(&self->lock, flags); +#else + outb(inb(iobase+HCR) | HCR_EN_DMA | HCR_TX_WT, iobase+HCR); +#endif + + /* Restore set register */ + outb(set, iobase+SSR); +} + +/* + * Function w83977af_pio_write (iobase, buf, len, fifo_size) + * + * + * + */ +static int w83977af_pio_write(int iobase, __u8 *buf, int len, int fifo_size) +{ + int actual = 0; + __u8 set; + + IRDA_DEBUG(4, "%s()\n", __FUNCTION__ ); + + /* Save current bank */ + set = inb(iobase+SSR); + + switch_bank(iobase, SET0); + if (!(inb_p(iobase+USR) & USR_TSRE)) { + IRDA_DEBUG(4, + "%s(), warning, FIFO not empty yet!\n", __FUNCTION__ ); + + fifo_size -= 17; + IRDA_DEBUG(4, "%s(), %d bytes left in tx fifo\n", + __FUNCTION__ , fifo_size); + } + + /* Fill FIFO with current frame */ + while ((fifo_size-- > 0) && (actual < len)) { + /* Transmit next byte */ + outb(buf[actual++], iobase+TBR); + } + + IRDA_DEBUG(4, "%s(), fifo_size %d ; %d sent of %d\n", + __FUNCTION__ , fifo_size, actual, len); + + /* Restore bank */ + outb(set, iobase+SSR); + + return actual; +} + +/* + * Function w83977af_dma_xmit_complete (self) + * + * The transfer of a frame in finished. So do the necessary things + * + * + */ +static void w83977af_dma_xmit_complete(struct w83977af_ir *self) +{ + int iobase; + __u8 set; + + IRDA_DEBUG(4, "%s(%ld)\n", __FUNCTION__ , jiffies); + + IRDA_ASSERT(self != NULL, return;); + + iobase = self->io.fir_base; + + /* Save current set */ + set = inb(iobase+SSR); + + /* Disable DMA */ + switch_bank(iobase, SET0); + outb(inb(iobase+HCR) & ~HCR_EN_DMA, iobase+HCR); + + /* Check for underrrun! */ + if (inb(iobase+AUDR) & AUDR_UNDR) { + IRDA_DEBUG(0, "%s(), Transmit underrun!\n", __FUNCTION__ ); + + self->stats.tx_errors++; + self->stats.tx_fifo_errors++; + + /* Clear bit, by writing 1 to it */ + outb(AUDR_UNDR, iobase+AUDR); + } else + self->stats.tx_packets++; + + + if (self->new_speed) { + w83977af_change_speed(self, self->new_speed); + self->new_speed = 0; + } + + /* Unlock tx_buff and request another frame */ + /* Tell the network layer, that we want more frames */ + netif_wake_queue(self->netdev); + + /* Restore set */ + outb(set, iobase+SSR); +} + +/* + * Function w83977af_dma_receive (self) + * + * Get ready for receiving a frame. The device will initiate a DMA + * if it starts to receive a frame. + * + */ +int w83977af_dma_receive(struct w83977af_ir *self) +{ + int iobase; + __u8 set; +#ifdef CONFIG_NETWINDER_RX_DMA_PROBLEMS + unsigned long flags; + __u8 hcr; +#endif + IRDA_ASSERT(self != NULL, return -1;); + + IRDA_DEBUG(4, "%s\n", __FUNCTION__ ); + + iobase= self->io.fir_base; + + /* Save current set */ + set = inb(iobase+SSR); + + /* Disable DMA */ + switch_bank(iobase, SET0); + outb(inb(iobase+HCR) & ~HCR_EN_DMA, iobase+HCR); + + /* Choose DMA Rx, DMA Fairness, and Advanced mode */ + switch_bank(iobase, SET2); + outb((inb(iobase+ADCR1) & ~ADCR1_D_CHSW)/*|ADCR1_DMA_F*/|ADCR1_ADV_SL, + iobase+ADCR1); + + self->io.direction = IO_RECV; + self->rx_buff.data = self->rx_buff.head; + +#ifdef CONFIG_NETWINDER_RX_DMA_PROBLEMS + spin_lock_irqsave(&self->lock, flags); + + disable_dma(self->io.dma); + clear_dma_ff(self->io.dma); + set_dma_mode(self->io.dma, DMA_MODE_READ); + set_dma_addr(self->io.dma, self->rx_buff_dma); + set_dma_count(self->io.dma, self->rx_buff.truesize); +#else + irda_setup_dma(self->io.dma, self->rx_buff_dma, self->rx_buff.truesize, + DMA_MODE_READ); +#endif + /* + * Reset Rx FIFO. This will also flush the ST_FIFO, it's very + * important that we don't reset the Tx FIFO since it might not + * be finished transmitting yet + */ + switch_bank(iobase, SET0); + outb(UFR_RXTL|UFR_TXTL|UFR_RXF_RST|UFR_EN_FIFO, iobase+UFR); + self->st_fifo.len = self->st_fifo.tail = self->st_fifo.head = 0; + + /* Enable DMA */ + switch_bank(iobase, SET0); +#ifdef CONFIG_NETWINDER_RX_DMA_PROBLEMS + hcr = inb(iobase+HCR); + outb(hcr | HCR_EN_DMA, iobase+HCR); + enable_dma(self->io.dma); + spin_unlock_irqrestore(&self->lock, flags); +#else + outb(inb(iobase+HCR) | HCR_EN_DMA, iobase+HCR); +#endif + /* Restore set */ + outb(set, iobase+SSR); + + return 0; +} + +/* + * Function w83977af_receive_complete (self) + * + * Finished with receiving a frame + * + */ +int w83977af_dma_receive_complete(struct w83977af_ir *self) +{ + struct sk_buff *skb; + struct st_fifo *st_fifo; + int len; + int iobase; + __u8 set; + __u8 status; + + IRDA_DEBUG(4, "%s\n", __FUNCTION__ ); + + st_fifo = &self->st_fifo; + + iobase = self->io.fir_base; + + /* Save current set */ + set = inb(iobase+SSR); + + iobase = self->io.fir_base; + + /* Read status FIFO */ + switch_bank(iobase, SET5); + while ((status = inb(iobase+FS_FO)) & FS_FO_FSFDR) { + st_fifo->entries[st_fifo->tail].status = status; + + st_fifo->entries[st_fifo->tail].len = inb(iobase+RFLFL); + st_fifo->entries[st_fifo->tail].len |= inb(iobase+RFLFH) << 8; + + st_fifo->tail++; + st_fifo->len++; + } + + while (st_fifo->len) { + /* Get first entry */ + status = st_fifo->entries[st_fifo->head].status; + len = st_fifo->entries[st_fifo->head].len; + st_fifo->head++; + st_fifo->len--; + + /* Check for errors */ + if (status & FS_FO_ERR_MSK) { + if (status & FS_FO_LST_FR) { + /* Add number of lost frames to stats */ + self->stats.rx_errors += len; + } else { + /* Skip frame */ + self->stats.rx_errors++; + + self->rx_buff.data += len; + + if (status & FS_FO_MX_LEX) + self->stats.rx_length_errors++; + + if (status & FS_FO_PHY_ERR) + self->stats.rx_frame_errors++; + + if (status & FS_FO_CRC_ERR) + self->stats.rx_crc_errors++; + } + /* The errors below can be reported in both cases */ + if (status & FS_FO_RX_OV) + self->stats.rx_fifo_errors++; + + if (status & FS_FO_FSF_OV) + self->stats.rx_fifo_errors++; + + } else { + /* Check if we have transferred all data to memory */ + switch_bank(iobase, SET0); + if (inb(iobase+USR) & USR_RDR) { +#ifdef CONFIG_USE_INTERNAL_TIMER + /* Put this entry back in fifo */ + st_fifo->head--; + st_fifo->len++; + st_fifo->entries[st_fifo->head].status = status; + st_fifo->entries[st_fifo->head].len = len; + + /* Restore set register */ + outb(set, iobase+SSR); + + return FALSE; /* I'll be back! */ +#else + udelay(80); /* Should be enough!? */ +#endif + } + + skb = dev_alloc_skb(len+1); + if (skb == NULL) { + printk(KERN_INFO + "%s(), memory squeeze, dropping frame.\n", __FUNCTION__); + /* Restore set register */ + outb(set, iobase+SSR); + + return FALSE; + } + + /* Align to 20 bytes */ + skb_reserve(skb, 1); + + /* Copy frame without CRC */ + if (self->io.speed < 4000000) { + skb_put(skb, len-2); + memcpy(skb->data, self->rx_buff.data, len-2); + } else { + skb_put(skb, len-4); + memcpy(skb->data, self->rx_buff.data, len-4); + } + + /* Move to next frame */ + self->rx_buff.data += len; + self->stats.rx_packets++; + + skb->dev = self->netdev; + skb->mac.raw = skb->data; + skb->protocol = htons(ETH_P_IRDA); + netif_rx(skb); + self->netdev->last_rx = jiffies; + } + } + /* Restore set register */ + outb(set, iobase+SSR); + + return TRUE; +} + +/* + * Function pc87108_pio_receive (self) + * + * Receive all data in receiver FIFO + * + */ +static void w83977af_pio_receive(struct w83977af_ir *self) +{ + __u8 byte = 0x00; + int iobase; + + IRDA_DEBUG(4, "%s()\n", __FUNCTION__ ); + + IRDA_ASSERT(self != NULL, return;); + + iobase = self->io.fir_base; + + /* Receive all characters in Rx FIFO */ + do { + byte = inb(iobase+RBR); + async_unwrap_char(self->netdev, &self->stats, &self->rx_buff, + byte); + } while (inb(iobase+USR) & USR_RDR); /* Data available */ +} + +/* + * Function w83977af_sir_interrupt (self, eir) + * + * Handle SIR interrupt + * + */ +static __u8 w83977af_sir_interrupt(struct w83977af_ir *self, int isr) +{ + int actual; + __u8 new_icr = 0; + __u8 set; + int iobase; + + IRDA_DEBUG(4, "%s(), isr=%#x\n", __FUNCTION__ , isr); + + iobase = self->io.fir_base; + /* Transmit FIFO low on data */ + if (isr & ISR_TXTH_I) { + /* Write data left in transmit buffer */ + actual = w83977af_pio_write(self->io.fir_base, + self->tx_buff.data, + self->tx_buff.len, + self->io.fifo_size); + + self->tx_buff.data += actual; + self->tx_buff.len -= actual; + + self->io.direction = IO_XMIT; + + /* Check if finished */ + if (self->tx_buff.len > 0) { + new_icr |= ICR_ETXTHI; + } else { + set = inb(iobase+SSR); + switch_bank(iobase, SET0); + outb(AUDR_SFEND, iobase+AUDR); + outb(set, iobase+SSR); + + self->stats.tx_packets++; + + /* Feed me more packets */ + netif_wake_queue(self->netdev); + new_icr |= ICR_ETBREI; + } + } + /* Check if transmission has completed */ + if (isr & ISR_TXEMP_I) { + /* Check if we need to change the speed? */ + if (self->new_speed) { + IRDA_DEBUG(2, + "%s(), Changing speed!\n", __FUNCTION__ ); + w83977af_change_speed(self, self->new_speed); + self->new_speed = 0; + } + + /* Turn around and get ready to receive some data */ + self->io.direction = IO_RECV; + new_icr |= ICR_ERBRI; + } + + /* Rx FIFO threshold or timeout */ + if (isr & ISR_RXTH_I) { + w83977af_pio_receive(self); + + /* Keep receiving */ + new_icr |= ICR_ERBRI; + } + return new_icr; +} + +/* + * Function pc87108_fir_interrupt (self, eir) + * + * Handle MIR/FIR interrupt + * + */ +static __u8 w83977af_fir_interrupt(struct w83977af_ir *self, int isr) +{ + __u8 new_icr = 0; + __u8 set; + int iobase; + + iobase = self->io.fir_base; + set = inb(iobase+SSR); + + /* End of frame detected in FIFO */ + if (isr & (ISR_FEND_I|ISR_FSF_I)) { + if (w83977af_dma_receive_complete(self)) { + + /* Wait for next status FIFO interrupt */ + new_icr |= ICR_EFSFI; + } else { + /* DMA not finished yet */ + + /* Set timer value, resolution 1 ms */ + switch_bank(iobase, SET4); + outb(0x01, iobase+TMRL); /* 1 ms */ + outb(0x00, iobase+TMRH); + + /* Start timer */ + outb(IR_MSL_EN_TMR, iobase+IR_MSL); + + new_icr |= ICR_ETMRI; + } + } + /* Timer finished */ + if (isr & ISR_TMR_I) { + /* Disable timer */ + switch_bank(iobase, SET4); + outb(0, iobase+IR_MSL); + + /* Clear timer event */ + /* switch_bank(iobase, SET0); */ +/* outb(ASCR_CTE, iobase+ASCR); */ + + /* Check if this is a TX timer interrupt */ + if (self->io.direction == IO_XMIT) { + w83977af_dma_write(self, iobase); + + new_icr |= ICR_EDMAI; + } else { + /* Check if DMA has now finished */ + w83977af_dma_receive_complete(self); + + new_icr |= ICR_EFSFI; + } + } + /* Finished with DMA */ + if (isr & ISR_DMA_I) { + w83977af_dma_xmit_complete(self); + + /* Check if there are more frames to be transmitted */ + /* if (irda_device_txqueue_empty(self)) { */ + + /* Prepare for receive + * + * ** Netwinder Tx DMA likes that we do this anyway ** + */ + w83977af_dma_receive(self); + new_icr = ICR_EFSFI; + /* } */ + } + + /* Restore set */ + outb(set, iobase+SSR); + + return new_icr; +} + +/* + * Function w83977af_interrupt (irq, dev_id, regs) + * + * An interrupt from the chip has arrived. Time to do some work + * + */ +static irqreturn_t w83977af_interrupt(int irq, void *dev_id, + struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct w83977af_ir *self; + __u8 set, icr, isr; + int iobase; + + if (!dev) { + printk(KERN_WARNING "%s: irq %d for unknown device.\n", + driver_name, irq); + return IRQ_NONE; + } + self = (struct w83977af_ir *) dev->priv; + + iobase = self->io.fir_base; + + /* Save current bank */ + set = inb(iobase+SSR); + switch_bank(iobase, SET0); + + icr = inb(iobase+ICR); + isr = inb(iobase+ISR) & icr; /* Mask out the interesting ones */ + + outb(0, iobase+ICR); /* Disable interrupts */ + + if (isr) { + /* Dispatch interrupt handler for the current speed */ + if (self->io.speed > PIO_MAX_SPEED ) + icr = w83977af_fir_interrupt(self, isr); + else + icr = w83977af_sir_interrupt(self, isr); + } + + outb(icr, iobase+ICR); /* Restore (new) interrupts */ + outb(set, iobase+SSR); /* Restore bank register */ + return IRQ_RETVAL(isr); +} + +/* + * Function w83977af_is_receiving (self) + * + * Return TRUE is we are currently receiving a frame + * + */ +static int w83977af_is_receiving(struct w83977af_ir *self) +{ + int status = FALSE; + int iobase; + __u8 set; + + IRDA_ASSERT(self != NULL, return FALSE;); + + if (self->io.speed > 115200) { + iobase = self->io.fir_base; + + /* Check if rx FIFO is not empty */ + set = inb(iobase+SSR); + switch_bank(iobase, SET2); + if ((inb(iobase+RXFDTH) & 0x3f) != 0) { + /* We are receiving something */ + status = TRUE; + } + outb(set, iobase+SSR); + } else + status = (self->rx_buff.state != OUTSIDE_FRAME); + + return status; +} + +/* + * Function w83977af_net_open (dev) + * + * Start the device + * + */ +static int w83977af_net_open(struct net_device *dev) +{ + struct w83977af_ir *self; + int iobase; + char hwname[32]; + __u8 set; + + IRDA_DEBUG(0, "%s()\n", __FUNCTION__ ); + + IRDA_ASSERT(dev != NULL, return -1;); + self = (struct w83977af_ir *) dev->priv; + + IRDA_ASSERT(self != NULL, return 0;); + + iobase = self->io.fir_base; + + if (request_irq(self->io.irq, w83977af_interrupt, 0, dev->name, + (void *) dev)) { + return -EAGAIN; + } + /* + * Always allocate the DMA channel after the IRQ, + * and clean up on failure. + */ + if (request_dma(self->io.dma, dev->name)) { + free_irq(self->io.irq, self); + return -EAGAIN; + } + + /* Save current set */ + set = inb(iobase+SSR); + + /* Enable some interrupts so we can receive frames again */ + switch_bank(iobase, SET0); + if (self->io.speed > 115200) { + outb(ICR_EFSFI, iobase+ICR); + w83977af_dma_receive(self); + } else + outb(ICR_ERBRI, iobase+ICR); + + /* Restore bank register */ + outb(set, iobase+SSR); + + /* Ready to play! */ + netif_start_queue(dev); + + /* Give self a hardware name */ + sprintf(hwname, "w83977af @ 0x%03x", self->io.fir_base); + + /* + * Open new IrLAP layer instance, now that everything should be + * initialized properly + */ + self->irlap = irlap_open(dev, &self->qos, hwname); + + return 0; +} + +/* + * Function w83977af_net_close (dev) + * + * Stop the device + * + */ +static int w83977af_net_close(struct net_device *dev) +{ + struct w83977af_ir *self; + int iobase; + __u8 set; + + IRDA_DEBUG(0, "%s()\n", __FUNCTION__ ); + + IRDA_ASSERT(dev != NULL, return -1;); + + self = (struct w83977af_ir *) dev->priv; + + IRDA_ASSERT(self != NULL, return 0;); + + iobase = self->io.fir_base; + + /* Stop device */ + netif_stop_queue(dev); + + /* Stop and remove instance of IrLAP */ + if (self->irlap) + irlap_close(self->irlap); + self->irlap = NULL; + + disable_dma(self->io.dma); + + /* Save current set */ + set = inb(iobase+SSR); + + /* Disable interrupts */ + switch_bank(iobase, SET0); + outb(0, iobase+ICR); + + free_irq(self->io.irq, dev); + free_dma(self->io.dma); + + /* Restore bank register */ + outb(set, iobase+SSR); + + return 0; +} + +/* + * Function w83977af_net_ioctl (dev, rq, cmd) + * + * Process IOCTL commands for this device + * + */ +static int w83977af_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct if_irda_req *irq = (struct if_irda_req *) rq; + struct w83977af_ir *self; + unsigned long flags; + int ret = 0; + + IRDA_ASSERT(dev != NULL, return -1;); + + self = dev->priv; + + IRDA_ASSERT(self != NULL, return -1;); + + IRDA_DEBUG(2, "%s(), %s, (cmd=0x%X)\n", __FUNCTION__ , dev->name, cmd); + + spin_lock_irqsave(&self->lock, flags); + + switch (cmd) { + case SIOCSBANDWIDTH: /* Set bandwidth */ + if (!capable(CAP_NET_ADMIN)) { + ret = -EPERM; + goto out; + } + w83977af_change_speed(self, irq->ifr_baudrate); + break; + case SIOCSMEDIABUSY: /* Set media busy */ + if (!capable(CAP_NET_ADMIN)) { + ret = -EPERM; + goto out; + } + irda_device_set_media_busy(self->netdev, TRUE); + break; + case SIOCGRECEIVING: /* Check if we are receiving right now */ + irq->ifr_receiving = w83977af_is_receiving(self); + break; + default: + ret = -EOPNOTSUPP; + } +out: + spin_unlock_irqrestore(&self->lock, flags); + return ret; +} + +static struct net_device_stats *w83977af_net_get_stats(struct net_device *dev) +{ + struct w83977af_ir *self = (struct w83977af_ir *) dev->priv; + + return &self->stats; +} + +MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>"); +MODULE_DESCRIPTION("Winbond W83977AF IrDA Device Driver"); +MODULE_LICENSE("GPL"); + + +module_param(qos_mtt_bits, int, 0); +MODULE_PARM_DESC(qos_mtt_bits, "Mimimum Turn Time"); +module_param_array(io, int, NULL, 0); +MODULE_PARM_DESC(io, "Base I/O addresses"); +module_param_array(irq, int, NULL, 0); +MODULE_PARM_DESC(irq, "IRQ lines"); + +/* + * Function init_module (void) + * + * + * + */ +module_init(w83977af_init); + +/* + * Function cleanup_module (void) + * + * + * + */ +module_exit(w83977af_cleanup); |