diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/net/irda/smsc-ircc2.c |
Linux-2.6.12-rc2v2.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/smsc-ircc2.c')
-rw-r--r-- | drivers/net/irda/smsc-ircc2.c | 2396 |
1 files changed, 2396 insertions, 0 deletions
diff --git a/drivers/net/irda/smsc-ircc2.c b/drivers/net/irda/smsc-ircc2.c new file mode 100644 index 00000000000..10125a1dba2 --- /dev/null +++ b/drivers/net/irda/smsc-ircc2.c @@ -0,0 +1,2396 @@ +/********************************************************************* + * $Id: smsc-ircc2.c,v 1.19.2.5 2002/10/27 11:34:26 dip Exp $ + * + * Description: Driver for the SMC Infrared Communications Controller + * Status: Experimental. + * Author: Daniele Peri (peri@csai.unipa.it) + * Created at: + * Modified at: + * Modified by: + * + * Copyright (c) 2002 Daniele Peri + * All Rights Reserved. + * Copyright (c) 2002 Jean Tourrilhes + * + * + * Based on smc-ircc.c: + * + * Copyright (c) 2001 Stefani Seibold + * Copyright (c) 1999-2001 Dag Brattli + * Copyright (c) 1998-1999 Thomas Davis, + * + * and irport.c: + * + * Copyright (c) 1997, 1998, 1999-2000 Dag Brattli, All Rights Reserved. + * + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ********************************************************************/ + +#include <linux/module.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/serial_reg.h> +#include <linux/dma-mapping.h> + +#include <asm/io.h> +#include <asm/dma.h> +#include <asm/byteorder.h> + +#include <linux/spinlock.h> +#include <linux/pm.h> + +#include <net/irda/wrapper.h> +#include <net/irda/irda.h> +#include <net/irda/irda_device.h> + +#include "smsc-ircc2.h" +#include "smsc-sio.h" + +/* Types */ + +struct smsc_transceiver { + char *name; + void (*set_for_speed)(int fir_base, u32 speed); + int (*probe)(int fir_base); +}; +typedef struct smsc_transceiver smsc_transceiver_t; + +#if 0 +struct smc_chip { + char *name; + u16 flags; + u8 devid; + u8 rev; +}; +typedef struct smc_chip smc_chip_t; +#endif + +struct smsc_chip { + char *name; + #if 0 + u8 type; + #endif + u16 flags; + u8 devid; + u8 rev; +}; +typedef struct smsc_chip smsc_chip_t; + +struct smsc_chip_address { + unsigned int cfg_base; + unsigned int type; +}; +typedef struct smsc_chip_address smsc_chip_address_t; + +/* Private data for each instance */ +struct smsc_ircc_cb { + struct net_device *netdev; /* Yes! we are some kind of netdevice */ + struct net_device_stats stats; + struct irlap_cb *irlap; /* The link layer we are binded to */ + + chipio_t io; /* IrDA controller information */ + iobuff_t tx_buff; /* Transmit buffer */ + iobuff_t rx_buff; /* Receive buffer */ + dma_addr_t tx_buff_dma; + dma_addr_t rx_buff_dma; + + struct qos_info qos; /* QoS capabilities for this device */ + + spinlock_t lock; /* For serializing operations */ + + __u32 new_speed; + __u32 flags; /* Interface flags */ + + int tx_buff_offsets[10]; /* Offsets between frames in tx_buff */ + int tx_len; /* Number of frames in tx_buff */ + + int transceiver; + struct pm_dev *pmdev; +}; + +/* Constants */ + +static const char *driver_name = "smsc-ircc2"; +#define DIM(x) (sizeof(x)/(sizeof(*(x)))) +#define SMSC_IRCC2_C_IRDA_FALLBACK_SPEED 9600 +#define SMSC_IRCC2_C_DEFAULT_TRANSCEIVER 1 +#define SMSC_IRCC2_C_NET_TIMEOUT 0 +#define SMSC_IRCC2_C_SIR_STOP 0 + +/* Prototypes */ + +static int smsc_ircc_open(unsigned int firbase, unsigned int sirbase, u8 dma, u8 irq); +static int smsc_ircc_present(unsigned int fir_base, unsigned int sir_base); +static void smsc_ircc_setup_io(struct smsc_ircc_cb *self, unsigned int fir_base, unsigned int sir_base, u8 dma, u8 irq); +static void smsc_ircc_setup_qos(struct smsc_ircc_cb *self); +static void smsc_ircc_init_chip(struct smsc_ircc_cb *self); +static int __exit smsc_ircc_close(struct smsc_ircc_cb *self); +static int smsc_ircc_dma_receive(struct smsc_ircc_cb *self, int iobase); +static void smsc_ircc_dma_receive_complete(struct smsc_ircc_cb *self, int iobase); +static void smsc_ircc_sir_receive(struct smsc_ircc_cb *self); +static int smsc_ircc_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev); +static int smsc_ircc_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev); +static void smsc_ircc_dma_xmit(struct smsc_ircc_cb *self, int iobase, int bofs); +static void smsc_ircc_dma_xmit_complete(struct smsc_ircc_cb *self, int iobase); +static void smsc_ircc_change_speed(void *priv, u32 speed); +static void smsc_ircc_set_sir_speed(void *priv, u32 speed); +static irqreturn_t smsc_ircc_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static irqreturn_t smsc_ircc_interrupt_sir(struct net_device *dev); +static void smsc_ircc_sir_start(struct smsc_ircc_cb *self); +#if SMSC_IRCC2_C_SIR_STOP +static void smsc_ircc_sir_stop(struct smsc_ircc_cb *self); +#endif +static void smsc_ircc_sir_write_wakeup(struct smsc_ircc_cb *self); +static int smsc_ircc_sir_write(int iobase, int fifo_size, __u8 *buf, int len); +static int smsc_ircc_net_open(struct net_device *dev); +static int smsc_ircc_net_close(struct net_device *dev); +static int smsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +#if SMSC_IRCC2_C_NET_TIMEOUT +static void smsc_ircc_timeout(struct net_device *dev); +#endif +static struct net_device_stats *smsc_ircc_net_get_stats(struct net_device *dev); +static int smsc_ircc_pmproc(struct pm_dev *dev, pm_request_t rqst, void *data); +static int smsc_ircc_is_receiving(struct smsc_ircc_cb *self); +static void smsc_ircc_probe_transceiver(struct smsc_ircc_cb *self); +static void smsc_ircc_set_transceiver_for_speed(struct smsc_ircc_cb *self, u32 speed); +static void smsc_ircc_sir_wait_hw_transmitter_finish(struct smsc_ircc_cb *self); + +/* Probing */ +static int __init smsc_ircc_look_for_chips(void); +static const smsc_chip_t * __init smsc_ircc_probe(unsigned short cfg_base,u8 reg,const smsc_chip_t *chip,char *type); +static int __init smsc_superio_flat(const smsc_chip_t *chips, unsigned short cfg_base, char *type); +static int __init smsc_superio_paged(const smsc_chip_t *chips, unsigned short cfg_base, char *type); +static int __init smsc_superio_fdc(unsigned short cfg_base); +static int __init smsc_superio_lpc(unsigned short cfg_base); + +/* Transceivers specific functions */ + +static void smsc_ircc_set_transceiver_toshiba_sat1800(int fir_base, u32 speed); +static int smsc_ircc_probe_transceiver_toshiba_sat1800(int fir_base); +static void smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select(int fir_base, u32 speed); +static int smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select(int fir_base); +static void smsc_ircc_set_transceiver_smsc_ircc_atc(int fir_base, u32 speed); +static int smsc_ircc_probe_transceiver_smsc_ircc_atc(int fir_base); + +/* Power Management */ + +static void smsc_ircc_suspend(struct smsc_ircc_cb *self); +static void smsc_ircc_wakeup(struct smsc_ircc_cb *self); +static int smsc_ircc_pmproc(struct pm_dev *dev, pm_request_t rqst, void *data); + + +/* Transceivers for SMSC-ircc */ + +static smsc_transceiver_t smsc_transceivers[]= +{ + { "Toshiba Satellite 1800 (GP data pin select)", smsc_ircc_set_transceiver_toshiba_sat1800, smsc_ircc_probe_transceiver_toshiba_sat1800}, + { "Fast pin select", smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select, smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select}, + { "ATC IRMode", smsc_ircc_set_transceiver_smsc_ircc_atc, smsc_ircc_probe_transceiver_smsc_ircc_atc}, + { NULL, NULL} +}; +#define SMSC_IRCC2_C_NUMBER_OF_TRANSCEIVERS (DIM(smsc_transceivers)-1) + +/* SMC SuperIO chipsets definitions */ + +#define KEY55_1 0 /* SuperIO Configuration mode with Key <0x55> */ +#define KEY55_2 1 /* SuperIO Configuration mode with Key <0x55,0x55> */ +#define NoIRDA 2 /* SuperIO Chip has no IRDA Port */ +#define SIR 0 /* SuperIO Chip has only slow IRDA */ +#define FIR 4 /* SuperIO Chip has fast IRDA */ +#define SERx4 8 /* SuperIO Chip supports 115,2 KBaud * 4=460,8 KBaud */ + +static smsc_chip_t __initdata fdc_chips_flat[]= +{ + /* Base address 0x3f0 or 0x370 */ + { "37C44", KEY55_1|NoIRDA, 0x00, 0x00 }, /* This chip cannot be detected */ + { "37C665GT", KEY55_2|NoIRDA, 0x65, 0x01 }, + { "37C665GT", KEY55_2|NoIRDA, 0x66, 0x01 }, + { "37C669", KEY55_2|SIR|SERx4, 0x03, 0x02 }, + { "37C669", KEY55_2|SIR|SERx4, 0x04, 0x02 }, /* ID? */ + { "37C78", KEY55_2|NoIRDA, 0x78, 0x00 }, + { "37N769", KEY55_1|FIR|SERx4, 0x28, 0x00 }, + { "37N869", KEY55_1|FIR|SERx4, 0x29, 0x00 }, + { NULL } +}; + +static smsc_chip_t __initdata fdc_chips_paged[]= +{ + /* Base address 0x3f0 or 0x370 */ + { "37B72X", KEY55_1|SIR|SERx4, 0x4c, 0x00 }, + { "37B77X", KEY55_1|SIR|SERx4, 0x43, 0x00 }, + { "37B78X", KEY55_1|SIR|SERx4, 0x44, 0x00 }, + { "37B80X", KEY55_1|SIR|SERx4, 0x42, 0x00 }, + { "37C67X", KEY55_1|FIR|SERx4, 0x40, 0x00 }, + { "37C93X", KEY55_2|SIR|SERx4, 0x02, 0x01 }, + { "37C93XAPM", KEY55_1|SIR|SERx4, 0x30, 0x01 }, + { "37C93XFR", KEY55_2|FIR|SERx4, 0x03, 0x01 }, + { "37M707", KEY55_1|SIR|SERx4, 0x42, 0x00 }, + { "37M81X", KEY55_1|SIR|SERx4, 0x4d, 0x00 }, + { "37N958FR", KEY55_1|FIR|SERx4, 0x09, 0x04 }, + { "37N971", KEY55_1|FIR|SERx4, 0x0a, 0x00 }, + { "37N972", KEY55_1|FIR|SERx4, 0x0b, 0x00 }, + { NULL } +}; + +static smsc_chip_t __initdata lpc_chips_flat[]= +{ + /* Base address 0x2E or 0x4E */ + { "47N227", KEY55_1|FIR|SERx4, 0x5a, 0x00 }, + { "47N267", KEY55_1|FIR|SERx4, 0x5e, 0x00 }, + { NULL } +}; + +static smsc_chip_t __initdata lpc_chips_paged[]= +{ + /* Base address 0x2E or 0x4E */ + { "47B27X", KEY55_1|SIR|SERx4, 0x51, 0x00 }, + { "47B37X", KEY55_1|SIR|SERx4, 0x52, 0x00 }, + { "47M10X", KEY55_1|SIR|SERx4, 0x59, 0x00 }, + { "47M120", KEY55_1|NoIRDA|SERx4, 0x5c, 0x00 }, + { "47M13X", KEY55_1|SIR|SERx4, 0x59, 0x00 }, + { "47M14X", KEY55_1|SIR|SERx4, 0x5f, 0x00 }, + { "47N252", KEY55_1|FIR|SERx4, 0x0e, 0x00 }, + { "47S42X", KEY55_1|SIR|SERx4, 0x57, 0x00 }, + { NULL } +}; + +#define SMSCSIO_TYPE_FDC 1 +#define SMSCSIO_TYPE_LPC 2 +#define SMSCSIO_TYPE_FLAT 4 +#define SMSCSIO_TYPE_PAGED 8 + +static smsc_chip_address_t __initdata possible_addresses[]= +{ + {0x3f0, SMSCSIO_TYPE_FDC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED}, + {0x370, SMSCSIO_TYPE_FDC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED}, + {0xe0, SMSCSIO_TYPE_FDC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED}, + {0x2e, SMSCSIO_TYPE_LPC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED}, + {0x4e, SMSCSIO_TYPE_LPC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED}, + {0,0} +}; + +/* Globals */ + +static struct smsc_ircc_cb *dev_self[] = { NULL, NULL}; + +static int ircc_irq=255; +static int ircc_dma=255; +static int ircc_fir=0; +static int ircc_sir=0; +static int ircc_cfg=0; +static int ircc_transceiver=0; + +static unsigned short dev_count=0; + +static inline void register_bank(int iobase, int bank) +{ + outb(((inb(iobase+IRCC_MASTER) & 0xf0) | (bank & 0x07)), + iobase+IRCC_MASTER); +} + + +/******************************************************************************* + * + * + * SMSC-ircc stuff + * + * + *******************************************************************************/ + +/* + * Function smsc_ircc_init () + * + * Initialize chip. Just try to find out how many chips we are dealing with + * and where they are + */ +static int __init smsc_ircc_init(void) +{ + int ret=-ENODEV; + + IRDA_DEBUG(1, "%s\n", __FUNCTION__); + + dev_count=0; + + if ((ircc_fir>0)&&(ircc_sir>0)) { + IRDA_MESSAGE(" Overriding FIR address 0x%04x\n", ircc_fir); + IRDA_MESSAGE(" Overriding SIR address 0x%04x\n", ircc_sir); + + if (smsc_ircc_open(ircc_fir, ircc_sir, ircc_dma, ircc_irq) == 0) + return 0; + + return -ENODEV; + } + + /* try user provided configuration register base address */ + if (ircc_cfg>0) { + IRDA_MESSAGE(" Overriding configuration address 0x%04x\n", + ircc_cfg); + if (!smsc_superio_fdc(ircc_cfg)) + ret = 0; + if (!smsc_superio_lpc(ircc_cfg)) + ret = 0; + } + + if(smsc_ircc_look_for_chips()>0) ret = 0; + + return ret; +} + +/* + * Function smsc_ircc_open (firbase, sirbase, dma, irq) + * + * Try to open driver instance + * + */ +static int __init smsc_ircc_open(unsigned int fir_base, unsigned int sir_base, u8 dma, u8 irq) +{ + struct smsc_ircc_cb *self; + struct net_device *dev; + int err; + + IRDA_DEBUG(1, "%s\n", __FUNCTION__); + + err = smsc_ircc_present(fir_base, sir_base); + if(err) + goto err_out; + + err = -ENOMEM; + if (dev_count > DIM(dev_self)) { + IRDA_WARNING("%s(), too many devices!\n", __FUNCTION__); + goto err_out1; + } + + /* + * Allocate new instance of the driver + */ + dev = alloc_irdadev(sizeof(struct smsc_ircc_cb)); + if (!dev) { + IRDA_WARNING("%s() can't allocate net device\n", __FUNCTION__); + goto err_out1; + } + + SET_MODULE_OWNER(dev); + + dev->hard_start_xmit = smsc_ircc_hard_xmit_sir; +#if SMSC_IRCC2_C_NET_TIMEOUT + dev->tx_timeout = smsc_ircc_timeout; + dev->watchdog_timeo = HZ*2; /* Allow enough time for speed change */ +#endif + dev->open = smsc_ircc_net_open; + dev->stop = smsc_ircc_net_close; + dev->do_ioctl = smsc_ircc_net_ioctl; + dev->get_stats = smsc_ircc_net_get_stats; + + self = dev->priv; + self->netdev = dev; + + /* Make ifconfig display some details */ + dev->base_addr = self->io.fir_base = fir_base; + dev->irq = self->io.irq = irq; + + /* Need to store self somewhere */ + dev_self[dev_count++] = self; + spin_lock_init(&self->lock); + + self->rx_buff.truesize = SMSC_IRCC2_RX_BUFF_TRUESIZE; + self->tx_buff.truesize = SMSC_IRCC2_TX_BUFF_TRUESIZE; + + self->rx_buff.head = + dma_alloc_coherent(NULL, self->rx_buff.truesize, + &self->rx_buff_dma, GFP_KERNEL); + if (self->rx_buff.head == NULL) { + IRDA_ERROR("%s, Can't allocate memory for receive buffer!\n", + driver_name); + goto err_out2; + } + + self->tx_buff.head = + dma_alloc_coherent(NULL, self->tx_buff.truesize, + &self->tx_buff_dma, GFP_KERNEL); + if (self->tx_buff.head == NULL) { + IRDA_ERROR("%s, Can't allocate memory for transmit buffer!\n", + driver_name); + goto err_out3; + } + + memset(self->rx_buff.head, 0, self->rx_buff.truesize); + 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; + + smsc_ircc_setup_io(self, fir_base, sir_base, dma, irq); + + smsc_ircc_setup_qos(self); + + smsc_ircc_init_chip(self); + + if(ircc_transceiver > 0 && + ircc_transceiver < SMSC_IRCC2_C_NUMBER_OF_TRANSCEIVERS) + self->transceiver = ircc_transceiver; + else + smsc_ircc_probe_transceiver(self); + + err = register_netdev(self->netdev); + if(err) { + IRDA_ERROR("%s, Network device registration failed!\n", + driver_name); + goto err_out4; + } + + self->pmdev = pm_register(PM_SYS_DEV, PM_SYS_IRDA, smsc_ircc_pmproc); + if (self->pmdev) + self->pmdev->data = self; + + IRDA_MESSAGE("IrDA: Registered device %s\n", dev->name); + + return 0; + err_out4: + dma_free_coherent(NULL, self->tx_buff.truesize, + self->tx_buff.head, self->tx_buff_dma); + err_out3: + dma_free_coherent(NULL, self->rx_buff.truesize, + self->rx_buff.head, self->rx_buff_dma); + err_out2: + free_netdev(self->netdev); + dev_self[--dev_count] = NULL; + err_out1: + release_region(fir_base, SMSC_IRCC2_FIR_CHIP_IO_EXTENT); + release_region(sir_base, SMSC_IRCC2_SIR_CHIP_IO_EXTENT); + err_out: + return err; +} + +/* + * Function smsc_ircc_present(fir_base, sir_base) + * + * Check the smsc-ircc chip presence + * + */ +static int smsc_ircc_present(unsigned int fir_base, unsigned int sir_base) +{ + unsigned char low, high, chip, config, dma, irq, version; + + if (!request_region(fir_base, SMSC_IRCC2_FIR_CHIP_IO_EXTENT, + driver_name)) { + IRDA_WARNING("%s: can't get fir_base of 0x%03x\n", + __FUNCTION__, fir_base); + goto out1; + } + + if (!request_region(sir_base, SMSC_IRCC2_SIR_CHIP_IO_EXTENT, + driver_name)) { + IRDA_WARNING("%s: can't get sir_base of 0x%03x\n", + __FUNCTION__, sir_base); + goto out2; + } + + register_bank(fir_base, 3); + + high = inb(fir_base+IRCC_ID_HIGH); + low = inb(fir_base+IRCC_ID_LOW); + chip = inb(fir_base+IRCC_CHIP_ID); + version = inb(fir_base+IRCC_VERSION); + config = inb(fir_base+IRCC_INTERFACE); + dma = config & IRCC_INTERFACE_DMA_MASK; + irq = (config & IRCC_INTERFACE_IRQ_MASK) >> 4; + + if (high != 0x10 || low != 0xb8 || (chip != 0xf1 && chip != 0xf2)) { + IRDA_WARNING("%s(), addr 0x%04x - no device found!\n", + __FUNCTION__, fir_base); + goto out3; + } + IRDA_MESSAGE("SMsC IrDA Controller found\n IrCC version %d.%d, " + "firport 0x%03x, sirport 0x%03x dma=%d, irq=%d\n", + chip & 0x0f, version, fir_base, sir_base, dma, irq); + + return 0; + out3: + release_region(sir_base, SMSC_IRCC2_SIR_CHIP_IO_EXTENT); + out2: + release_region(fir_base, SMSC_IRCC2_FIR_CHIP_IO_EXTENT); + out1: + return -ENODEV; +} + +/* + * Function smsc_ircc_setup_io(self, fir_base, sir_base, dma, irq) + * + * Setup I/O + * + */ +static void smsc_ircc_setup_io(struct smsc_ircc_cb *self, + unsigned int fir_base, unsigned int sir_base, + u8 dma, u8 irq) +{ + unsigned char config, chip_dma, chip_irq; + + register_bank(fir_base, 3); + config = inb(fir_base+IRCC_INTERFACE); + chip_dma = config & IRCC_INTERFACE_DMA_MASK; + chip_irq = (config & IRCC_INTERFACE_IRQ_MASK) >> 4; + + self->io.fir_base = fir_base; + self->io.sir_base = sir_base; + self->io.fir_ext = SMSC_IRCC2_FIR_CHIP_IO_EXTENT; + self->io.sir_ext = SMSC_IRCC2_SIR_CHIP_IO_EXTENT; + self->io.fifo_size = SMSC_IRCC2_FIFO_SIZE; + self->io.speed = SMSC_IRCC2_C_IRDA_FALLBACK_SPEED; + + if (irq < 255) { + if (irq != chip_irq) + IRDA_MESSAGE("%s, Overriding IRQ - chip says %d, using %d\n", + driver_name, chip_irq, irq); + self->io.irq = irq; + } + else + self->io.irq = chip_irq; + + if (dma < 255) { + if (dma != chip_dma) + IRDA_MESSAGE("%s, Overriding DMA - chip says %d, using %d\n", + driver_name, chip_dma, dma); + self->io.dma = dma; + } + else + self->io.dma = chip_dma; + +} + +/* + * Function smsc_ircc_setup_qos(self) + * + * Setup qos + * + */ +static void smsc_ircc_setup_qos(struct smsc_ircc_cb *self) +{ + /* Initialize QoS for this device */ + irda_init_max_qos_capabilies(&self->qos); + + self->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600| + IR_115200|IR_576000|IR_1152000|(IR_4000000 << 8); + + self->qos.min_turn_time.bits = SMSC_IRCC2_MIN_TURN_TIME; + self->qos.window_size.bits = SMSC_IRCC2_WINDOW_SIZE; + irda_qos_bits_to_value(&self->qos); +} + +/* + * Function smsc_ircc_init_chip(self) + * + * Init chip + * + */ +static void smsc_ircc_init_chip(struct smsc_ircc_cb *self) +{ + int iobase, ir_mode, ctrl, fast; + + IRDA_ASSERT( self != NULL, return; ); + iobase = self->io.fir_base; + + ir_mode = IRCC_CFGA_IRDA_SIR_A; + ctrl = 0; + fast = 0; + + register_bank(iobase, 0); + outb(IRCC_MASTER_RESET, iobase+IRCC_MASTER); + outb(0x00, iobase+IRCC_MASTER); + + register_bank(iobase, 1); + outb(((inb(iobase+IRCC_SCE_CFGA) & 0x87) | ir_mode), + iobase+IRCC_SCE_CFGA); + +#ifdef smsc_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(SMSC_IRCC2_FIFO_THRESHOLD, iobase+IRCC_FIFO_THRESHOLD); + + register_bank(iobase, 4); + outb((inb(iobase+IRCC_CONTROL) & 0x30) | ctrl, iobase+IRCC_CONTROL); + + register_bank(iobase, 0); + outb(fast, iobase+IRCC_LCR_A); + + smsc_ircc_set_sir_speed(self, SMSC_IRCC2_C_IRDA_FALLBACK_SPEED); + + /* Power on device */ + outb(0x00, iobase+IRCC_MASTER); +} + +/* + * Function smsc_ircc_net_ioctl (dev, rq, cmd) + * + * Process IOCTL commands for this device + * + */ +static int smsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct if_irda_req *irq = (struct if_irda_req *) rq; + struct smsc_ircc_cb *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); + + switch (cmd) { + case SIOCSBANDWIDTH: /* Set bandwidth */ + if (!capable(CAP_NET_ADMIN)) + ret = -EPERM; + else { + /* Make sure we are the only one touching + * self->io.speed and the hardware - Jean II */ + spin_lock_irqsave(&self->lock, flags); + smsc_ircc_change_speed(self, irq->ifr_baudrate); + spin_unlock_irqrestore(&self->lock, flags); + } + break; + case SIOCSMEDIABUSY: /* Set media busy */ + if (!capable(CAP_NET_ADMIN)) { + ret = -EPERM; + break; + } + + irda_device_set_media_busy(self->netdev, TRUE); + break; + case SIOCGRECEIVING: /* Check if we are receiving right now */ + irq->ifr_receiving = smsc_ircc_is_receiving(self); + break; + #if 0 + case SIOCSDTRRTS: + if (!capable(CAP_NET_ADMIN)) { + ret = -EPERM; + break; + } + smsc_ircc_sir_set_dtr_rts(dev, irq->ifr_dtr, irq->ifr_rts); + break; + #endif + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + +static struct net_device_stats *smsc_ircc_net_get_stats(struct net_device *dev) +{ + struct smsc_ircc_cb *self = (struct smsc_ircc_cb *) dev->priv; + + return &self->stats; +} + +#if SMSC_IRCC2_C_NET_TIMEOUT +/* + * Function smsc_ircc_timeout (struct net_device *dev) + * + * The networking timeout management. + * + */ + +static void smsc_ircc_timeout(struct net_device *dev) +{ + struct smsc_ircc_cb *self; + unsigned long flags; + + self = (struct smsc_ircc_cb *) dev->priv; + + IRDA_WARNING("%s: transmit timed out, changing speed to: %d\n", + dev->name, self->io.speed); + spin_lock_irqsave(&self->lock, flags); + smsc_ircc_sir_start(self); + smsc_ircc_change_speed(self, self->io.speed); + dev->trans_start = jiffies; + netif_wake_queue(dev); + spin_unlock_irqrestore(&self->lock, flags); +} +#endif + +/* + * Function smsc_ircc_hard_xmit_sir (struct sk_buff *skb, struct net_device *dev) + * + * Transmits the current frame until FIFO is full, then + * waits until the next transmit interrupt, and continues until the + * frame is transmitted. + */ +int smsc_ircc_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev) +{ + struct smsc_ircc_cb *self; + unsigned long flags; + int iobase; + s32 speed; + + IRDA_DEBUG(1, "%s\n", __FUNCTION__); + + IRDA_ASSERT(dev != NULL, return 0;); + + self = (struct smsc_ircc_cb *) dev->priv; + IRDA_ASSERT(self != NULL, return 0;); + + iobase = self->io.sir_base; + + netif_stop_queue(dev); + + /* Make sure test of self->io.speed & speed change are atomic */ + spin_lock_irqsave(&self->lock, flags); + + /* 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) { + /* + * We send frames one by one in SIR mode (no + * pipelining), so at this point, if we were sending + * a previous frame, we just received the interrupt + * telling us it is finished (UART_IIR_THRI). + * Therefore, waiting for the transmitter to really + * finish draining the fifo won't take too long. + * And the interrupt handler is not expected to run. + * - Jean II */ + smsc_ircc_sir_wait_hw_transmitter_finish(self); + smsc_ircc_change_speed(self, speed); + spin_unlock_irqrestore(&self->lock, flags); + dev_kfree_skb(skb); + return 0; + } else { + self->new_speed = speed; + } + } + + /* Init tx buffer */ + self->tx_buff.data = self->tx_buff.head; + + /* Copy skb to tx_buff while wrapping, stuffing and making CRC */ + self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, + self->tx_buff.truesize); + + self->stats.tx_bytes += self->tx_buff.len; + + /* Turn on transmit finished interrupt. Will fire immediately! */ + outb(UART_IER_THRI, iobase+UART_IER); + + spin_unlock_irqrestore(&self->lock, flags); + + dev_kfree_skb(skb); + + return 0; +} + +/* + * Function smsc_ircc_set_fir_speed (self, baud) + * + * Change the speed of the device + * + */ +static void smsc_ircc_set_fir_speed(struct smsc_ircc_cb *self, u32 speed) +{ + int fir_base, ir_mode, ctrl, fast; + + IRDA_ASSERT(self != NULL, return;); + fir_base = self->io.fir_base; + + self->io.speed = speed; + + switch(speed) { + default: + case 576000: + ir_mode = IRCC_CFGA_IRDA_HDLC; + ctrl = IRCC_CRC; + fast = 0; + IRDA_DEBUG(0, "%s(), handling baud of 576000\n", __FUNCTION__); + break; + case 1152000: + ir_mode = IRCC_CFGA_IRDA_HDLC; + ctrl = IRCC_1152 | IRCC_CRC; + fast = IRCC_LCR_A_FAST | IRCC_LCR_A_GP_DATA; + IRDA_DEBUG(0, "%s(), handling baud of 1152000\n", + __FUNCTION__); + break; + case 4000000: + ir_mode = IRCC_CFGA_IRDA_4PPM; + ctrl = IRCC_CRC; + fast = IRCC_LCR_A_FAST; + IRDA_DEBUG(0, "%s(), handling baud of 4000000\n", + __FUNCTION__); + break; + } + #if 0 + Now in tranceiver! + /* This causes an interrupt */ + register_bank(fir_base, 0); + outb((inb(fir_base+IRCC_LCR_A) & 0xbf) | fast, fir_base+IRCC_LCR_A); + #endif + + register_bank(fir_base, 1); + outb(((inb(fir_base+IRCC_SCE_CFGA) & IRCC_SCE_CFGA_BLOCK_CTRL_BITS_MASK) | ir_mode), fir_base+IRCC_SCE_CFGA); + + register_bank(fir_base, 4); + outb((inb(fir_base+IRCC_CONTROL) & 0x30) | ctrl, fir_base+IRCC_CONTROL); +} + +/* + * Function smsc_ircc_fir_start(self) + * + * Change the speed of the device + * + */ +static void smsc_ircc_fir_start(struct smsc_ircc_cb *self) +{ + struct net_device *dev; + int fir_base; + + IRDA_DEBUG(1, "%s\n", __FUNCTION__); + + IRDA_ASSERT(self != NULL, return;); + dev = self->netdev; + IRDA_ASSERT(dev != NULL, return;); + + fir_base = self->io.fir_base; + + /* Reset everything */ + + /* Install FIR transmit handler */ + dev->hard_start_xmit = smsc_ircc_hard_xmit_fir; + + /* Clear FIFO */ + outb(inb(fir_base+IRCC_LCR_A)|IRCC_LCR_A_FIFO_RESET, fir_base+IRCC_LCR_A); + + /* Enable interrupt */ + /*outb(IRCC_IER_ACTIVE_FRAME|IRCC_IER_EOM, fir_base+IRCC_IER);*/ + + register_bank(fir_base, 1); + + /* Select the TX/RX interface */ +#ifdef SMSC_669 /* Uses pin 88/89 for Rx/Tx */ + outb(((inb(fir_base+IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_MUX_COM), + fir_base+IRCC_SCE_CFGB); +#else + outb(((inb(fir_base+IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_MUX_IR), + fir_base+IRCC_SCE_CFGB); +#endif + (void) inb(fir_base+IRCC_FIFO_THRESHOLD); + + /* Enable SCE interrupts */ + outb(0, fir_base+IRCC_MASTER); + register_bank(fir_base, 0); + outb(IRCC_IER_ACTIVE_FRAME|IRCC_IER_EOM, fir_base+IRCC_IER); + outb(IRCC_MASTER_INT_EN, fir_base+IRCC_MASTER); +} + +/* + * Function smsc_ircc_fir_stop(self, baud) + * + * Change the speed of the device + * + */ +static void smsc_ircc_fir_stop(struct smsc_ircc_cb *self) +{ + int fir_base; + + IRDA_DEBUG(1, "%s\n", __FUNCTION__); + + IRDA_ASSERT(self != NULL, return;); + + fir_base = self->io.fir_base; + register_bank(fir_base, 0); + /*outb(IRCC_MASTER_RESET, fir_base+IRCC_MASTER);*/ + outb(inb(fir_base+IRCC_LCR_B) & IRCC_LCR_B_SIP_ENABLE, fir_base+IRCC_LCR_B); +} + + +/* + * Function smsc_ircc_change_speed(self, baud) + * + * Change the speed of the device + * + * This function *must* be called with spinlock held, because it may + * be called from the irq handler. - Jean II + */ +static void smsc_ircc_change_speed(void *priv, u32 speed) +{ + struct smsc_ircc_cb *self = (struct smsc_ircc_cb *) priv; + struct net_device *dev; + int iobase; + int last_speed_was_sir; + + IRDA_DEBUG(0, "%s() changing speed to: %d\n", __FUNCTION__, speed); + + IRDA_ASSERT(self != NULL, return;); + dev = self->netdev; + iobase = self->io.fir_base; + + last_speed_was_sir = self->io.speed <= SMSC_IRCC2_MAX_SIR_SPEED; + + #if 0 + /* Temp Hack */ + speed= 1152000; + self->io.speed = speed; + last_speed_was_sir = 0; + smsc_ircc_fir_start(self); + #endif + + if(self->io.speed == 0) + smsc_ircc_sir_start(self); + + #if 0 + if(!last_speed_was_sir) speed = self->io.speed; + #endif + + if(self->io.speed != speed) smsc_ircc_set_transceiver_for_speed(self, speed); + + self->io.speed = speed; + + if(speed <= SMSC_IRCC2_MAX_SIR_SPEED) { + if(!last_speed_was_sir) { + smsc_ircc_fir_stop(self); + smsc_ircc_sir_start(self); + } + smsc_ircc_set_sir_speed(self, speed); + } + else { + if(last_speed_was_sir) { + #if SMSC_IRCC2_C_SIR_STOP + smsc_ircc_sir_stop(self); + #endif + smsc_ircc_fir_start(self); + } + smsc_ircc_set_fir_speed(self, speed); + + #if 0 + self->tx_buff.len = 10; + self->tx_buff.data = self->tx_buff.head; + + smsc_ircc_dma_xmit(self, iobase, 4000); + #endif + /* Be ready for incoming frames */ + smsc_ircc_dma_receive(self, iobase); + } + + netif_wake_queue(dev); +} + +/* + * Function smsc_ircc_set_sir_speed (self, speed) + * + * Set speed of IrDA port to specified baudrate + * + */ +void smsc_ircc_set_sir_speed(void *priv, __u32 speed) +{ + struct smsc_ircc_cb *self = (struct smsc_ircc_cb *) priv; + int iobase; + int fcr; /* FIFO control reg */ + int lcr; /* Line control reg */ + int divisor; + + IRDA_DEBUG(0, "%s(), Setting speed to: %d\n", __FUNCTION__, speed); + + IRDA_ASSERT(self != NULL, return;); + iobase = self->io.sir_base; + + /* Update accounting for new speed */ + self->io.speed = speed; + + /* Turn off interrupts */ + outb(0, iobase+UART_IER); + + divisor = SMSC_IRCC2_MAX_SIR_SPEED/speed; + + fcr = UART_FCR_ENABLE_FIFO; + + /* + * Use trigger level 1 to avoid 3 ms. timeout delay at 9600 bps, and + * almost 1,7 ms at 19200 bps. At speeds above that we can just forget + * about this timeout since it will always be fast enough. + */ + if (self->io.speed < 38400) + fcr |= UART_FCR_TRIGGER_1; + else + fcr |= UART_FCR_TRIGGER_14; + + /* IrDA ports use 8N1 */ + lcr = UART_LCR_WLEN8; + + outb(UART_LCR_DLAB | lcr, iobase+UART_LCR); /* Set DLAB */ + outb(divisor & 0xff, iobase+UART_DLL); /* Set speed */ + outb(divisor >> 8, iobase+UART_DLM); + outb(lcr, iobase+UART_LCR); /* Set 8N1 */ + outb(fcr, iobase+UART_FCR); /* Enable FIFO's */ + + /* Turn on interrups */ + outb(UART_IER_RLSI|UART_IER_RDI|UART_IER_THRI, iobase+UART_IER); + + IRDA_DEBUG(2, "%s() speed changed to: %d\n", __FUNCTION__, speed); +} + + +/* + * Function smsc_ircc_hard_xmit_fir (skb, dev) + * + * Transmit the frame! + * + */ +static int smsc_ircc_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev) +{ + struct smsc_ircc_cb *self; + unsigned long flags; + s32 speed; + int iobase; + int mtt; + + IRDA_ASSERT(dev != NULL, return 0;); + self = (struct smsc_ircc_cb *) dev->priv; + IRDA_ASSERT(self != NULL, return 0;); + + iobase = self->io.fir_base; + + netif_stop_queue(dev); + + /* Make sure test of self->io.speed & speed change are atomic */ + spin_lock_irqsave(&self->lock, flags); + + /* Check if we need to change the speed after this frame */ + speed = irda_get_next_speed(skb); + if ((speed != self->io.speed) && (speed != -1)) { + /* Check for empty frame */ + if (!skb->len) { + /* Note : you should make sure that speed changes + * are not going to corrupt any outgoing frame. + * Look at nsc-ircc for the gory details - Jean II */ + smsc_ircc_change_speed(self, speed); + spin_unlock_irqrestore(&self->lock, flags); + dev_kfree_skb(skb); + return 0; + } else + self->new_speed = speed; + } + + memcpy(self->tx_buff.head, skb->data, skb->len); + + self->tx_buff.len = skb->len; + self->tx_buff.data = self->tx_buff.head; + + mtt = irda_get_mtt(skb); + if (mtt) { + int bofs; + + /* + * Compute how 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; + + smsc_ircc_dma_xmit(self, iobase, bofs); + } else { + /* Transmit frame */ + smsc_ircc_dma_xmit(self, iobase, 0); + } + spin_unlock_irqrestore(&self->lock, flags); + dev_kfree_ |