diff options
Diffstat (limited to 'drivers/net/irda/smc-ircc.c')
-rw-r--r-- | drivers/net/irda/smc-ircc.c | 699 |
1 files changed, 427 insertions, 272 deletions
diff --git a/drivers/net/irda/smc-ircc.c b/drivers/net/irda/smc-ircc.c index 1d857c2b0..48b38227e 100644 --- a/drivers/net/irda/smc-ircc.c +++ b/drivers/net/irda/smc-ircc.c @@ -1,12 +1,12 @@ /********************************************************************* * * Filename: smc-ircc.c - * Version: 0.3 + * Version: 0.4 * Description: Driver for the SMC Infrared Communications Controller * Status: Experimental. * Author: Thomas Davis (tadavis@jps.net) * Created at: - * Modified at: Wed Jan 5 12:38:06 2000 + * Modified at: Fri Jan 21 09:41:08 2000 * Modified by: Dag Brattli <dagb@cs.uit.no> * * Copyright (c) 1999-2000 Dag Brattli @@ -28,11 +28,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * - * SIO's: SMC FDC37N869, FDC37C669 + * SIO's: SMC FDC37N869, FDC37C669, FDC37N958 * Applicable Models : Fujitsu Lifebook 635t, Sony PCG-505TX * ********************************************************************/ +#include <linux/config.h> #include <linux/module.h> #include <linux/kernel.h> @@ -44,11 +45,16 @@ #include <linux/malloc.h> #include <linux/init.h> #include <linux/rtnetlink.h> +#include <linux/serial_reg.h> #include <asm/io.h> #include <asm/dma.h> #include <asm/byteorder.h> +#ifdef CONFIG_APM +#include <linux/apm_bios.h> +#endif + #include <net/irda/wrapper.h> #include <net/irda/irda.h> #include <net/irda/irmod.h> @@ -62,10 +68,10 @@ static char *driver_name = "smc-ircc"; #define CHIP_IO_EXTENT 8 -static unsigned int io[] = { 0x2e8, 0x140, 0x118, 0x240 }; -static unsigned int io2[] = { 0x2f8, 0x3e8, 0x2e8, 0x3e8 }; +static unsigned int io[] = { ~0, ~0 }; +static unsigned int io2[] = { 0, 0 }; -static struct ircc_cb *dev_self[] = { NULL, NULL, NULL, NULL}; +static struct ircc_cb *dev_self[] = { NULL, NULL}; /* Some prototypes */ static int ircc_open(int i, unsigned int iobase, unsigned int board_addr); @@ -73,17 +79,30 @@ static int ircc_open(int i, unsigned int iobase, unsigned int board_addr); static int ircc_close(struct ircc_cb *self); #endif /* MODULE */ static int ircc_probe(int iobase, int board_addr); -static int ircc_probe_smc(int *ioaddr, int *ioaddr2); -static int ircc_dma_receive(struct ircc_cb *self); -static int ircc_dma_receive_complete(struct ircc_cb *self, int iobase); +static int ircc_probe_58(smc_chip_t *chip, chipio_t *info); +static int ircc_probe_69(smc_chip_t *chip, chipio_t *info); +static int ircc_dma_receive(struct ircc_cb *self, int iobase); +static void ircc_dma_receive_complete(struct ircc_cb *self, int iobase); static int ircc_hard_xmit(struct sk_buff *skb, struct net_device *dev); -static void ircc_dma_xmit(struct ircc_cb *self, int iobase); +static void ircc_dma_xmit(struct ircc_cb *self, int iobase, int bofs); static void ircc_change_speed(void *priv, __u32 speed); static void ircc_interrupt(int irq, void *dev_id, struct pt_regs *regs); static int ircc_is_receiving(struct ircc_cb *self); static int ircc_net_open(struct net_device *dev); static int ircc_net_close(struct net_device *dev); +#ifdef CONFIG_APM +static int ircc_apmproc(apm_event_t event); +#endif /* CONFIG_APM */ + +/* These are the currently known SMC chipsets */ +static smc_chip_t chips[] = +{ + { "FDC37C669", 0x55, 0x55, 0x0d, 0x04, ircc_probe_69 }, + { "FDC37N869", 0x55, 0x00, 0x0d, 0x29, ircc_probe_69 }, + { "FDC37N958", 0x55, 0x55, 0x20, 0x09, ircc_probe_58 }, + { NULL } +}; static int ircc_irq=255; static int ircc_dma=255; @@ -102,26 +121,38 @@ static inline void register_bank(int iobase, int bank) */ int __init ircc_init(void) { - int ioaddr, ioaddr2; + static int smcreg[] = { 0x3f0, 0x370 }; + smc_chip_t *chip; + chipio_t info; + int ret = -ENODEV; int i; IRDA_DEBUG(0, __FUNCTION__ "\n"); - for (i=0; (io[i] < 2000) && (i < 4); i++) { - int ioaddr = io[i]; - if (check_region(ioaddr, CHIP_IO_EXTENT)) - continue; - if (ircc_open(i, io[i], io2[i]) == 0) - return 0; - } - /* last chance saloon, see what the controller says */ - if (ircc_probe_smc(&ioaddr, &ioaddr2) == 0) { - if (check_region(ioaddr, CHIP_IO_EXTENT) == 0) - if (ircc_open(0, ioaddr, ioaddr2) == 0) - return 0; + /* Probe for all the NSC chipsets we know about */ + for (chip=chips; chip->name ; chip++,i++) { + for (i=0; i<2; i++) { + info.cfg_base = smcreg[i]; + + /* + * First we check if the user has supplied any + * parameters which we should use instead of probed + * values + */ + if (io[i] < 2000) { + info.fir_base = io[i]; + info.sir_base = io2[i]; + } else if (chip->probe(chip, &info) < 0) + continue; + if (check_region(info.fir_base, CHIP_IO_EXTENT) < 0) + continue; + if (check_region(info.sir_base, CHIP_IO_EXTENT) < 0) + continue; + if (ircc_open(i, info.fir_base, info.sir_base) == 0) + ret = 0; + } } - - return -ENODEV; + return ret; } /* @@ -137,7 +168,7 @@ static void ircc_cleanup(void) IRDA_DEBUG(0, __FUNCTION__ "\n"); - for (i=0; i < 4; i++) { + for (i=0; i < 2; i++) { if (dev_self[i]) ircc_close(dev_self[i]); } @@ -150,7 +181,7 @@ static void ircc_cleanup(void) * Open driver instance * */ -static int ircc_open(int i, unsigned int iobase, unsigned int iobase2) +static int ircc_open(int i, unsigned int fir_base, unsigned int sir_base) { struct ircc_cb *self; struct irport_cb *irport; @@ -159,9 +190,9 @@ static int ircc_open(int i, unsigned int iobase, unsigned int iobase2) IRDA_DEBUG(0, __FUNCTION__ "\n"); - if ((config = ircc_probe(iobase, iobase2)) == -1) { + if ((config = ircc_probe(fir_base, sir_base)) == -1) { IRDA_DEBUG(0, __FUNCTION__ - "(), addr 0x%04x - no device found!\n", iobase); + "(), addr 0x%04x - no device found!\n", fir_base); return -1; } @@ -180,7 +211,7 @@ static int ircc_open(int i, unsigned int iobase, unsigned int iobase2) /* Need to store self somewhere */ dev_self[i] = self; - irport = irport_open(0, iobase2, config >> 4 & 0x0f); + irport = irport_open(i, sir_base, config >> 4 & 0x0f); if (!irport) return -ENODEV; @@ -190,34 +221,32 @@ static int ircc_open(int i, unsigned int iobase, unsigned int iobase2) irport->priv = self; /* Initialize IO */ - self->io.iobase = iobase; - self->io.iobase2 = iobase2; /* Used by irport */ + self->io.fir_base = fir_base; + self->io.sir_base = sir_base; /* Used by irport */ self->io.irq = config >> 4 & 0x0f; if (ircc_irq < 255) { MESSAGE("%s, Overriding IRQ - chip says %d, using %d\n", driver_name, self->io.irq, ircc_irq); self->io.irq = ircc_irq; } - self->io.io_ext = CHIP_IO_EXTENT; - self->io.io_ext2 = 8; /* Used by irport */ + self->io.fir_ext = CHIP_IO_EXTENT; + self->io.sir_ext = 8; /* Used by irport */ self->io.dma = config & 0x0f; if (ircc_dma < 255) { MESSAGE("%s, Overriding DMA - chip says %d, using %d\n", driver_name, self->io.dma, ircc_dma); self->io.dma = ircc_dma; } - self->io.fifo_size = 16; /* Lock the port that we need */ - ret = check_region(self->io.iobase, self->io.io_ext); + ret = check_region(self->io.fir_base, self->io.fir_ext); if (ret < 0) { - IRDA_DEBUG(0, __FUNCTION__ ": can't get iobase of 0x%03x\n", - self->io.iobase); - /* ircc_cleanup(self->self); */ + IRDA_DEBUG(0, __FUNCTION__ ": can't get fir_base of 0x%03x\n", + self->io.fir_base); + kfree(self); return -ENODEV; } - - request_region(self->io.iobase, self->io.io_ext, driver_name); + request_region(self->io.fir_base, self->io.fir_ext, driver_name); /* Initialize QoS for this device */ irda_init_max_qos_capabilies(&irport->qos); @@ -229,7 +258,7 @@ static int ircc_open(int i, unsigned int iobase, unsigned int iobase2) irport->qos.min_turn_time.bits = 0x07; irda_qos_bits_to_value(&irport->qos); - irport->flags = IFF_FIR|IFF_SIR|IFF_DMA|IFF_PIO; + irport->flags = IFF_FIR|IFF_MIR|IFF_SIR|IFF_DMA|IFF_PIO; /* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */ self->rx_buff.truesize = 4000; @@ -256,6 +285,7 @@ static int ircc_open(int i, unsigned int iobase, unsigned int iobase2) /* Override the speed change function, since we must control it now */ irport->change_speed = &ircc_change_speed; + irport->interrupt = &ircc_interrupt; self->netdev->open = &ircc_net_open; self->netdev->stop = &ircc_net_close; @@ -279,23 +309,26 @@ static int ircc_close(struct ircc_cb *self) ASSERT(self != NULL, return -1;); - iobase = self->io.iobase; + iobase = self->io.fir_base; irport_close(self->irport); + /* Stop interrupts */ register_bank(iobase, 0); outb(0, iobase+IRCC_IER); outb(IRCC_MASTER_RESET, iobase+IRCC_MASTER); - + outb(0x00, iobase+IRCC_MASTER); +#if 0 + /* Reset to SIR mode */ register_bank(iobase, 1); - outb(IRCC_CFGA_IRDA_SIR_A|IRCC_CFGA_TX_POLARITY, iobase+IRCC_SCE_CFGA); outb(IRCC_CFGB_IR, iobase+IRCC_SCE_CFGB); - +#endif /* Release the PORT that this driver is using */ - IRDA_DEBUG(0, __FUNCTION__ "(), releasing 0x%03x\n", self->io.iobase); + IRDA_DEBUG(0, __FUNCTION__ "(), releasing 0x%03x\n", + self->io.fir_base); - release_region(self->io.iobase, self->io.io_ext); + release_region(self->io.fir_base, self->io.fir_ext); if (self->tx_buff.head) kfree(self->tx_buff.head); @@ -310,58 +343,106 @@ static int ircc_close(struct ircc_cb *self) #endif /* MODULE */ /* - * Function ircc_probe_smc (ioaddr, ioaddr2) + * Function ircc_probe_69 (chip, info) * - * Probe the SMC Chip for an IrDA port + * Probes for the SMC FDC37C669 and FDC37N869 * */ -static int ircc_probe_smc(int *ioaddr, int *ioaddr2) +static int ircc_probe_69(smc_chip_t *chip, chipio_t *info) { - static int smcreg[] = { 0x3f0, 0x370 }; + int cfg_base = info->cfg_base; __u8 devid, mode; - __u8 conf_reg; - int ret = -1; + int ret = -ENODEV; int fir_io; - int i; IRDA_DEBUG(0, __FUNCTION__ "()\n"); - for (i = 0; i < 2 && ret == -1; i++) { - conf_reg = smcreg[i]; - - /* Enter configuration */ - outb(0x55, conf_reg); - outb(0x55, conf_reg); - - outb(0x0d, conf_reg); - devid = inb(conf_reg+1); - IRDA_DEBUG(0, __FUNCTION__ "(), devid=0x%02x\n",devid); + /* Enter configuration */ + outb(chip->entr1, cfg_base); + outb(chip->entr2, cfg_base); + + outb(chip->cid_index, cfg_base); + devid = inb(cfg_base+1); + IRDA_DEBUG(0, __FUNCTION__ "(), devid=0x%02x\n",devid); + + /* Check for expected device ID; are there others? */ + if (devid == chip->cid_value) { + outb(0x0c, cfg_base); + mode = inb(cfg_base+1); + mode = (mode & 0x38) >> 3; - /* Check for expected device ID; are there others? */ - if (devid == 0x29) { - outb(0x0c, conf_reg); - mode = inb(conf_reg+1); - mode = (mode & 0x38) >> 3; - - /* Value for IR port */ - if (mode && mode < 4) { - /* SIR iobase */ - outb(0x25, conf_reg); - *ioaddr2 = inb(conf_reg+1) << 2; - - /* FIR iobase */ - outb(0x2b, conf_reg); - fir_io = inb(conf_reg+1) << 3; - if (fir_io) { - ret = 0; - *ioaddr = fir_io; - } + /* Value for IR port */ + if (mode && mode < 4) { + /* SIR iobase */ + outb(0x25, cfg_base); + info->sir_base = inb(cfg_base+1) << 2; + + /* FIR iobase */ + outb(0x2b, cfg_base); + fir_io = inb(cfg_base+1) << 3; + if (fir_io) { + ret = 0; + info->fir_base = fir_io; } } + } + + /* Exit configuration */ + outb(0xaa, cfg_base); + + return ret; +} + +/* + * Function ircc_probe_58 (chip, info) + * + * Probes for the SMC FDC37N958 + * + */ +static int ircc_probe_58(smc_chip_t *chip, chipio_t *info) +{ + int cfg_base = info->cfg_base; + __u8 devid; + int ret = -ENODEV; + int fir_io; + + IRDA_DEBUG(0, __FUNCTION__ "()\n"); + + /* Enter configuration */ + outb(chip->entr1, cfg_base); + outb(chip->entr2, cfg_base); + + outb(chip->cid_index, cfg_base); + devid = inb(cfg_base+1); + IRDA_DEBUG(0, __FUNCTION__ "(), devid=0x%02x\n",devid); + + /* Check for expected device ID; are there others? */ + if (devid == chip->cid_value) { + /* Select logical device (UART2) */ + outb(0x07, cfg_base); + outb(0x05, cfg_base + 1); + + /* SIR iobase */ + outb(0x60, cfg_base); + info->sir_base = inb(cfg_base + 1) << 8; + outb(0x61, cfg_base); + info->sir_base |= inb(cfg_base + 1); + + /* Read FIR base */ + outb(0x62, cfg_base); + fir_io = inb(cfg_base + 1) << 8; + outb(0x63, cfg_base); + fir_io |= inb(cfg_base + 1); + outb(0x2b, cfg_base); + if (fir_io) { + ret = 0; + info->fir_base = fir_io; + } + } + + /* Exit configuration */ + outb(0xaa, cfg_base); - /* Exit configuration */ - outb(0xaa, conf_reg); - } return ret; } @@ -371,34 +452,32 @@ static int ircc_probe_smc(int *ioaddr, int *ioaddr2) * Returns non-negative on success. * */ -static int ircc_probe(int iobase, int iobase2) +static int ircc_probe(int fir_base, int sir_base) { - int version = 1; int low, high, chip, config, dma, irq; - - IRDA_DEBUG(0, __FUNCTION__ "\n"); + int iobase = fir_base; + int version = 1; - /* Power on device */ - outb(inb(iobase+IRCC_MASTER) & ~IRCC_MASTER_POWERDOWN, - iobase+IRCC_MASTER); + IRDA_DEBUG(0, __FUNCTION__ "\n"); register_bank(iobase, 3); - high = inb(iobase+IRCC_ID_HIGH); - low = inb(iobase+IRCC_ID_LOW); - chip = inb(iobase+IRCC_CHIP_ID); + high = inb(iobase+IRCC_ID_HIGH); + low = inb(iobase+IRCC_ID_LOW); + chip = inb(iobase+IRCC_CHIP_ID); version = inb(iobase+IRCC_VERSION); - config = inb(iobase+IRCC_INTERFACE); - irq = config >> 4 & 0x0f; - dma = config & 0x0f; + config = inb(iobase+IRCC_INTERFACE); + irq = config >> 4 & 0x0f; + dma = config & 0x0f; if (high == 0x10 && low == 0xb8 && (chip == 0xf1 || chip == 0xf2)) { - IRDA_DEBUG(0, "SMC IrDA Controller found; IrCC version %d.%d, " - "port 0x%04x, dma %d, interrupt %d\n", - chip & 0x0f, version, iobase, dma, irq); + MESSAGE("SMC IrDA Controller found; IrCC version %d.%d, " + "port 0x%03x, dma=%d, irq=%d\n", + chip & 0x0f, version, iobase, dma, irq); } else - return -1; + return -ENODEV; - outb(0, iobase+IRCC_MASTER); + /* Power on device */ + outb(0x00, iobase+IRCC_MASTER); return config; } @@ -411,7 +490,7 @@ static int ircc_probe(int iobase, int iobase2) */ static void ircc_change_speed(void *priv, __u32 speed) { - int iobase, ir_mode, select, fast; + int iobase, ir_mode, ctrl, fast; struct ircc_cb *self = (struct ircc_cb *) priv; struct net_device *dev; @@ -420,49 +499,39 @@ static void ircc_change_speed(void *priv, __u32 speed) ASSERT(self != NULL, return;); dev = self->netdev; - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Update accounting for new speed */ self->io.speed = speed; + outb(IRCC_MASTER_RESET, iobase+IRCC_MASTER); + outb(0x00, iobase+IRCC_MASTER); + switch (speed) { case 9600: case 19200: case 38400: case 57600: - case 115200: - IRDA_DEBUG(0, __FUNCTION__ - "(), using irport to change speed to %d\n", speed); - - register_bank(iobase, 0); - outb(0, iobase+IRCC_IER); - outb(IRCC_MASTER_RESET, iobase+IRCC_MASTER); - outb(IRCC_MASTER_INT_EN, iobase+IRCC_MASTER); - - dev->hard_start_xmit = &irport_hard_xmit; - - /* We must give the interrupt back to irport */ - self->irport->interrupt = irport_interrupt; - - irport_start(self->irport); - irport_change_speed(self->irport, speed); - return; + case 115200: + ir_mode = IRCC_CFGA_IRDA_SIR_A; + ctrl = 0; + fast = 0; break; case 576000: ir_mode = IRCC_CFGA_IRDA_HDLC; - select = 0; + ctrl = IRCC_CRC; fast = 0; IRDA_DEBUG(0, __FUNCTION__ "(), handling baud of 576000\n"); break; case 1152000: ir_mode = IRCC_CFGA_IRDA_HDLC; - select = IRCC_1152; + ctrl = IRCC_1152 | IRCC_CRC; fast = 0; IRDA_DEBUG(0, __FUNCTION__ "(), handling baud of 1152000\n"); break; case 4000000: ir_mode = IRCC_CFGA_IRDA_4PPM; - select = 0; + ctrl = IRCC_CRC; fast = IRCC_LCR_A_FAST; IRDA_DEBUG(0, __FUNCTION__ "(), handling baud of 4000000\n"); break; @@ -471,39 +540,55 @@ static void ircc_change_speed(void *priv, __u32 speed) speed); return; } - - outb(IRCC_MASTER_RESET, iobase+IRCC_MASTER); - + register_bank(iobase, 0); outb(0, iobase+IRCC_IER); - - irport_stop(self->irport); + outb(IRCC_MASTER_INT_EN, iobase+IRCC_MASTER); + + /* Make special FIR init if necessary */ + if (speed > 115200) { + irport_stop(self->irport); - /* Install FIR transmit handler */ - dev->hard_start_xmit = &ircc_hard_xmit; + /* Install FIR transmit handler */ + dev->hard_start_xmit = &ircc_hard_xmit; - /* Need to steal the interrupt as well */ - self->irport->interrupt = &ircc_interrupt; + /* + * Don't know why we have to do this, but FIR interrupts + * stops working if we remove it. + */ + /* outb(UART_MCR_OUT2, self->io.sir_base + UART_MCR); */ + /* Be ready for incomming frames */ + ircc_dma_receive(self, iobase); + } else { + /* Install SIR transmit handler */ + dev->hard_start_xmit = &irport_hard_xmit; + irport_start(self->irport); + + IRDA_DEBUG(0, __FUNCTION__ + "(), using irport to change speed to %d\n", speed); + irport_change_speed(self->irport, speed); + } dev->tbusy = 0; - + register_bank(iobase, 1); outb(((inb(iobase+IRCC_SCE_CFGA) & 0x87) | ir_mode), iobase+IRCC_SCE_CFGA); - - outb(((inb(iobase+IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_IR), - iobase+IRCC_SCE_CFGB); +#ifdef SMC_669 /* Uses pin 88/89 for Rx/Tx */ + outb(((inb(iobase+IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_MUX_COM), + iobase+IRCC_SCE_CFGB); +#else + outb(((inb(iobase+IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_MUX_IR), + iobase+IRCC_SCE_CFGB); +#endif (void) inb(iobase+IRCC_FIFO_THRESHOLD); outb(64, iobase+IRCC_FIFO_THRESHOLD); - + register_bank(iobase, 4); - - outb((inb(iobase+IRCC_CONTROL) & 0x30) | select | IRCC_CRC, - iobase+IRCC_CONTROL); - + outb((inb(iobase+IRCC_CONTROL) & 0x30) | ctrl, iobase+IRCC_CONTROL); + register_bank(iobase, 0); - outb(fast, iobase+IRCC_LCR_A); } @@ -517,21 +602,20 @@ static int ircc_hard_xmit(struct sk_buff *skb, struct net_device *dev) { struct irport_cb *irport; struct ircc_cb *self; + unsigned long flags; + __u32 speed; int iobase; int mtt; - __u32 speed; irport = (struct irport_cb *) dev->priv; self = (struct ircc_cb *) irport->priv; - ASSERT(self != NULL, return 0;); - iobase = self->io.iobase; + iobase = self->io.fir_base; - IRDA_DEBUG(2, __FUNCTION__ "(%ld), skb->len=%d\n", jiffies, - (int) skb->len); + spin_lock_irqsave(&self->lock, flags); - /* Check if we need to change the speed */ + /* Check if we need to change the speed after this frame */ if ((speed = irda_get_speed(skb)) != self->io.speed) self->new_speed = speed; @@ -541,19 +625,28 @@ static int ircc_hard_xmit(struct sk_buff *skb, struct net_device *dev) memcpy(self->tx_buff.head, skb->data, skb->len); - /* Make sure that the length is a multiple of 16 bits */ - if (skb->len & 0x01) - skb->len++; - self->tx_buff.len = skb->len; self->tx_buff.data = self->tx_buff.head; mtt = irda_get_mtt(skb); - if (mtt) - udelay(mtt); - - ircc_dma_xmit(self, iobase); + if (mtt) { + int bofs; + + /* + * Compute who many BOFS (STA or PA's) we need to waste the + * min turn time given the speed of the link. + */ + bofs = mtt * (self->io.speed / 1000) / 8000; + if (bofs > 4095) + bofs = 4095; + + ircc_dma_xmit(self, iobase, bofs); + } else { + /* Transmit frame */ + ircc_dma_xmit(self, iobase, 0); + } + spin_unlock_irqrestore(&self->lock, flags); dev_kfree_skb(skb); return 0; @@ -565,44 +658,49 @@ static int ircc_hard_xmit(struct sk_buff *skb, struct net_device *dev) * Transmit data using DMA * */ -static void ircc_dma_xmit(struct ircc_cb *self, int iobase) +static void ircc_dma_xmit(struct ircc_cb *self, int iobase, int bofs) { - IRDA_DEBUG(2, __FUNCTION__ "\n"); + __u8 ctrl; - ASSERT(self != NULL, return;); - - iobase = self->io.iobase; + IRDA_DEBUG(2, __FUNCTION__ "\n"); +#if 0 + /* Disable Rx */ + register_bank(iobase, 0); + outb(0x00, iobase+IRCC_LCR_B); +#endif + register_bank(iobase, 1); + outb(inb(iobase+IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE, + iobase+IRCC_SCE_CFGB); - setup_dma(self->io.dma, self->tx_buff.data, self->tx_buff.len, - DMA_TX_MODE); - self->io.direction = IO_XMIT; - outb(0x08, self->io.iobase2+4); - + /* Set BOF additional count for generating the min turn time */ register_bank(iobase, 4); - outb((inb(iobase+IRCC_CONTROL) & 0xf0), iobase+IRCC_CONTROL); - - outb(2, iobase+IRCC_BOF_COUNT_LO); - outb(0, iobase+IRCC_BRICKWALL_CNT_LO); -#if 1 - outb(self->tx_buff.len >> 8, iobase+IRCC_BRICKWALL_TX_CNT_HI); + outb(bofs & 0xff, iobase+IRCC_BOF_COUNT_LO); + ctrl = inb(iobase+IRCC_CONTROL) & 0xf0; + outb(ctrl | ((bofs >> 8) & 0x0f), iobase+IRCC_BOF_COUNT_HI); + + /* Set max Tx frame size */ + outb(self->tx_buff.len >> 8, iobase+IRCC_TX_SIZE_HI); outb(self->tx_buff.len & 0xff, iobase+IRCC_TX_SIZE_LO); -#else - outb(0, iobase+IRCC_BRICKWALL_TX_CNT_HI); - outb(0, iobase+IRCC_TX_SIZE_LO); -#endif + /* Setup DMA controller (must be done after enabling chip DMA) */ + setup_dma(self->io.dma, self->tx_buff.data, self->tx_buff.len, + DMA_TX_MODE); + + outb(UART_MCR_OUT2, self->io.sir_base + UART_MCR); + /* Enable burst mode chip Tx DMA */ register_bank(iobase, 1); - outb(inb(iobase+IRCC_SCE_CFGB) | IRCC_CFGB_DMA_ENABLE, - iobase+IRCC_SCE_CFGB); + outb(inb(iobase+IRCC_SCE_CFGB) | IRCC_CFGB_DMA_ENABLE | + IRCC_CFGB_DMA_BURST, iobase+IRCC_SCE_CFGB); + /* Enable interrupt */ + outb(IRCC_MASTER_INT_EN, iobase+IRCC_MASTER); register_bank(iobase, 0); - outb(IRCC_IER_ACTIVE_FRAME | IRCC_IER_EOM, iobase+IRCC_IER); - outb(IRCC_LCR_B_SCE_TRANSMIT|IRCC_LCR_B_SIP_ENABLE, iobase+IRCC_LCR_B); - outb(IRCC_MASTER_INT_EN, iobase+IRCC_MASTER); + /* Enable transmit */ + outb(IRCC_LCR_B_SCE_TRANSMIT|IRCC_LCR_B_SIP_ENABLE, iobase+IRCC_LCR_B); } /* @@ -612,39 +710,36 @@ static void ircc_dma_xmit(struct ircc_cb *self, int iobase) * by the interrupt handler * */ -static void ircc_dma_xmit_complete(struct ircc_cb *self, int underrun) +static void ircc_dma_xmit_complete(struct ircc_cb *self, int iobase) { - int iobase, d; - IRDA_DEBUG(2, __FUNCTION__ "\n"); - - ASSERT(self != NULL, return;); - - register_bank(self->io.iobase, 1); - - outb(inb(self->io.iobase+IRCC_SCE_CFGB) & IRCC_CFGB_DMA_ENABLE, - self->io.iobase+IRCC_SCE_CFGB); - - d = get_dma_residue(self->io.dma); - - IRDA_DEBUG(0, __FUNCTION__ - ": dma residue = %d, len=%d, sent=%d\n", - d, self->tx_buff.len, self->tx_buff.len - d); - - iobase = self->io.iobase; +#if 0 + /* Disable Tx */ + register_bank(iobase, 0); + outb(0x00, iobase+IRCC_LCR_B); +#endif + register_bank(self->io.fir_base, 1); + outb(inb(self->io.fir_base+IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE, + self->io.fir_base+IRCC_SCE_CFGB); /* Check for underrrun! */ - if (underrun) { + register_bank(iobase, 0); + if (inb(iobase+IRCC_LSR) & IRCC_LSR_UNDERRUN) { self->irport->stats.tx_errors++; - self->irport->stats.tx_fifo_errors++; + self->irport->stats.tx_fifo_errors++; + + /* Reset error condition */ + register_bank(iobase, 0); + outb(IRCC_MASTER_ERROR_RESET, iobase+IRCC_MASTER); + outb(0x00, iobase+IRCC_MASTER); } else { self->irport->stats.tx_packets++; self->irport->stats.tx_bytes += self->tx_buff.len; } + /* Check if it's time to change the speed */ if (self->new_speed) { - ircc_change_speed(self, self->new_speed); - + ircc_change_speed(self, self->new_speed); self->new_speed = 0; } @@ -662,39 +757,31 @@ static void ircc_dma_xmit_complete(struct ircc_cb *self, int underrun) * if it starts to receive a frame. * */ -static int ircc_dma_receive(struct ircc_cb *self) -{ - int iobase; - - IRDA_DEBUG(2, __FUNCTION__ "\n"); - - ASSERT(self != NULL, return -1;); - - iobase= self->io.iobase; +static int ircc_dma_receive(struct ircc_cb *self, int iobase) +{ + /* Turn off chip DMA */ + //register_bank(iobase, 1); + //outb(inb(iobase+IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE, + // iobase+IRCC_SCE_CFGB); setup_dma(self->io.dma, self->rx_buff.data, self->rx_buff.truesize, - DMA_RX_MODE); - - /* driver->media_busy = FALSE; */ + DMA_RX_MODE); + /* Set max Rx frame size */ + register_bank(iobase, 4); + outb((2050 >> 8) & 0x0f, iobase+IRCC_RX_SIZE_HI); + outb(2050 & 0xff, iobase+IRCC_RX_SIZE_LO); + self->io.direction = IO_RECV; self->rx_buff.data = self->rx_buff.head; -#if 0 - self->rx_buff.offset = 0; -#endif - - register_bank(iobase, 4); - outb(inb(iobase+IRCC_CONTROL) & 0xf0, iobase+IRCC_CONTROL); - outb(2, iobase+IRCC_BOF_COUNT_LO); - outb(0, iobase+IRCC_BRICKWALL_CNT_LO); - outb(0, iobase+IRCC_BRICKWALL_TX_CNT_HI); - outb(0, iobase+IRCC_TX_SIZE_LO); - outb(0, iobase+IRCC_RX_SIZE_HI); - outb(0, iobase+IRCC_RX_SIZE_LO); + /* Setup DMA controller */ + + /* Enable receiver */ register_bank(iobase, 0); outb(IRCC_LCR_B_SCE_RECEIVE | IRCC_LCR_B_SIP_ENABLE, iobase+IRCC_LCR_B); + /* Enable burst mode chip Rx DMA */ register_bank(iobase, 1); outb(inb(iobase+IRCC_SCE_CFGB) | IRCC_CFGB_DMA_ENABLE | IRCC_CFGB_DMA_BURST, iobase+IRCC_SCE_CFGB); @@ -709,45 +796,54 @@ static int ircc_dma_receive(struct ircc_cb *self) * * */ -static int ircc_dma_receive_complete(struct ircc_cb *self, int iobase) +static void ircc_dma_receive_complete(struct ircc_cb *self, int iobase) { + unsigned long flags; struct sk_buff *skb; int len, msgcnt; IRDA_DEBUG(2, __FUNCTION__ "\n"); +#if 0 + /* Disable Rx */ + register_bank(iobase, 0); + outb(0x00, iobase+IRCC_LCR_B); +#endif + register_bank(iobase, 0); + msgcnt = inb(iobase+IRCC_LCR_B) & 0x08; - msgcnt = inb(self->io.iobase+IRCC_LCR_B) & 0x08; - - IRDA_DEBUG(0, __FUNCTION__ ": dma count = %d\n", + IRDA_DEBUG(2, __FUNCTION__ ": dma count = %d\n", get_dma_residue(self->io.dma)); - len = self->rx_buff.truesize - get_dma_residue(self->io.dma) - 4; - - IRDA_DEBUG(0, __FUNCTION__ ": msgcnt = %d, len=%d\n", msgcnt, len); + len = self->rx_buff.truesize - get_dma_residue(self->io.dma); + + /* Remove CRC */ + if (self->io.speed < 4000000) + len -= 2; + else + len -= 4; + + if ((len < 2) && (len > 2050)) { + WARNING(__FUNCTION__ "(), bogus len=%d\n", len); + return; + } + IRDA_DEBUG(2, __FUNCTION__ ": msgcnt = %d, len=%d\n", msgcnt, len); skb = dev_alloc_skb(len+1); if (!skb) { WARNING(__FUNCTION__ "(), memory squeeze, dropping frame.\n"); - return FALSE; - } - + return; + } /* Make sure IP header gets aligned */ skb_reserve(skb, 1); - skb_put(skb, len); - memcpy(skb->data, self->rx_buff.data, len); + memcpy(skb_put(skb, len), self->rx_buff.data, len); self->irport->stats.rx_packets++; + self->irport->stats.rx_bytes += len; skb->dev = self->netdev; skb->mac.raw = skb->data; skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); - - register_bank(self->io.iobase, 1); - outb(inb(self->io.iobase+IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE, - self->io.iobase+IRCC_SCE_CFGB); - - return TRUE; } /* @@ -758,58 +854,54 @@ static int ircc_dma_receive_complete(struct ircc_cb *self, int iobase) */ static void ircc_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - int iobase, iir; struct net_device *dev = (struct net_device *) dev_id; + struct irport_cb *irport; struct ircc_cb *self; + int iobase, iir; if (dev == NULL) { printk(KERN_WARNING "%s: irq %d for unknown device.\n", driver_name, irq); return; } - - self = (struct ircc_cb *) dev->priv; + irport = (struct irport_cb *) dev->priv; + ASSERT(irport != NULL, return;); + self = (struct ircc_cb *) irport->priv; + ASSERT(self != NULL, return;); - iobase = self->io.iobase; + /* Check if we should use the SIR interrupt handler */ + if (self->io.speed < 576000) { + irport_interrupt(irq, dev_id, regs); + return; + } + iobase = self->io.fir_base; + spin_lock(&self->lock); dev->interrupt = 1; - outb(0, iobase+IRCC_MASTER); - register_bank(iobase, 0); iir = inb(iobase+IRCC_IIR); /* Disable interrupts */ outb(0, iobase+IRCC_IER); - IRDA_DEBUG(0, __FUNCTION__ "(), iir = 0x%02x\n", iir); + IRDA_DEBUG(2, __FUNCTION__ "(), iir = 0x%02x\n", iir); if (iir & IRCC_IIR_EOM) { - IRDA_DEBUG(0, __FUNCTION__ "(), IRCC_IIR_EOM\n"); - if (self->io.direction == IO_RECV) ircc_dma_receive_complete(self, iobase); else ircc_dma_xmit_complete(self, iobase); - ircc_dma_receive(self); - } - if (iir & IRCC_IIR_ACTIVE_FRAME) { - IRDA_DEBUG(0, __FUNCTION__ "(), IRCC_IIR_ACTIVE_FRAME\n"); - self->rx_buff.state = INSIDE_FRAME; -#if 0 - ircc_dma_receive(self); -#endif - } - if (iir & IRCC_IIR_RAW_MODE) { - IRDA_DEBUG(0, __FUNCTION__ "(), IIR RAW mode interrupt.\n"); + ircc_dma_receive(self, iobase); } + /* Enable interrupts again */ register_bank(iobase, 0); outb(IRCC_IER_ACTIVE_FRAME|IRCC_IER_EOM, iobase+IRCC_IER); - outb(IRCC_MASTER_INT_EN, iobase+IRCC_MASTER); dev->interrupt = 0; + spin_unlock(&self->lock); } /* @@ -855,7 +947,7 @@ static int ircc_net_open(struct net_device *dev) ASSERT(self != NULL, return 0;); - iobase = self->io.iobase; + iobase = self->io.fir_base; irport_net_open(dev); /* irport allocates the irq */ @@ -894,7 +986,7 @@ static int ircc_net_close(struct net_device *dev) ASSERT(self != NULL, return 0;); - iobase = self->io.iobase; + iobase = self->io.fir_base; irport_net_close(dev); @@ -907,6 +999,69 @@ static int ircc_net_close(struct net_device *dev) return 0; } +#ifdef CONFIG_APM +static void ircc_suspend(struct ircc_cb *self) +{ + int i = 10; + + MESSAGE("%s, Suspending\n", driver_name); + + if (self->io.suspended) + return; + + ircc_net_close(self->netdev); + + self->io.suspended = 1; +} + +static void ircc_wakeup(struct ircc_cb *self) +{ + struct net_device *dev = self->netdev; + unsigned long flags; + + if (!self->io.suspended) + return; + + save_flags(flags); + cli(); + + ircc_net_open(self->netdev); + + restore_flags(flags); + MESSAGE("%s, Waking up\n", driver_name); +} + +static int ircc_apmproc(apm_event_t event) +{ + static int down = 0; /* Filter out double events */ + int i; + + switch (event) { + case APM_SYS_SUSPEND: + case APM_USER_SUSPEND: + if (!down) { + for (i=0; i<4; i++) { + if (dev_self[i]) + ircc_suspend(dev_self[i]); + } + } + down = 1; + break; + case APM_NORMAL_RESUME: + case APM_CRITICAL_RESUME: + if (down) { + for (i=0; i<4; i++) { + if (dev_self[i]) + ircc_wakeup(dev_self[i]); + } + } + down = 0; + break; + } + return 0; +} +#endif /* CONFIG_APM */ + #ifdef MODULE MODULE_AUTHOR("Thomas Davis <tadavis@jps.net>"); MODULE_DESCRIPTION("SMC IrCC controller driver"); |