diff -u -p --new-file linux/drivers/net/irda.d1/Kconfig linux/drivers/net/irda/Kconfig --- linux/drivers/net/irda.d1/Kconfig Mon Mar 24 14:01:14 2003 +++ linux/drivers/net/irda/Kconfig Tue May 13 13:18:34 2003 @@ -289,15 +289,28 @@ config TOSHIBA_FIR . The module will be called donauboe. -config SMC_IRCC_FIR - tristate "SMC IrCC (EXPERIMENTAL)" +config SMC_IRCC_OLD + tristate "SMC IrCC (old driver) (EXPERIMENTAL)" depends on EXPERIMENTAL && IRDA help Say Y here if you want to build support for the SMC Infrared Communications Controller. It is used in the Fujitsu Lifebook 635t - and Sony PCG-505TX. If you want to compile it as a module, say M - here and read . The module will be + and Sony PCG-505TX. This driver is obsolete, will no more be + maintained and will be removed in favor of the new driver. + If you want to compile it as a module, say M here and read + . The module will be called smc-ircc. + +config SMC_IRCC_FIR + tristate "SMSC IrCC (EXPERIMENTAL)" + depends on EXPERIMENTAL && IRDA + help + Say Y here if you want to build support for the SMC Infrared + Communications Controller. It is used in a wide variety of + laptops (Fujitsu, Sony, Compaq and some Toshiba). + If you want to compile it as a module, say M here and read + . The module will be + called smsc-ircc2.o. config ALI_FIR tristate "ALi M5123 FIR (EXPERIMENTAL)" diff -u -p --new-file linux/drivers/net/irda.d1/Makefile linux/drivers/net/irda/Makefile --- linux/drivers/net/irda.d1/Makefile Tue May 13 13:17:49 2003 +++ linux/drivers/net/irda/Makefile Tue May 13 13:18:34 2003 @@ -15,13 +15,14 @@ obj-$(CONFIG_WINBOND_FIR) += w83977af_ir obj-$(CONFIG_SA1100_FIR) += sa1100_ir.o obj-$(CONFIG_TOSHIBA_OLD) += toshoboe.o obj-$(CONFIG_TOSHIBA_FIR) += donauboe.o -obj-$(CONFIG_SMC_IRCC_FIR) += smc-ircc.o irport.o +obj-$(CONFIG_SMC_IRCC_OLD) += smc-ircc.o irport.o +obj-$(CONFIG_SMC_IRCC_FIR) += smsc-ircc2.o obj-$(CONFIG_ALI_FIR) += ali-ircc.o obj-$(CONFIG_VLSI_FIR) += vlsi_ir.o # Old dongle drivers for old SIR drivers -obj-$(CONFIG_ESI_OLD) += esi.o -obj-$(CONFIG_TEKRAM_OLD) += tekram.o -obj-$(CONFIG_ACTISYS_OLD) += actisys.o +obj-$(CONFIG_ESI_DONGLE_OLD) += esi.o +obj-$(CONFIG_TEKRAM_DONGLE_OLD) += tekram.o +obj-$(CONFIG_ACTISYS_DONGLE_OLD) += actisys.o obj-$(CONFIG_GIRBIL_DONGLE) += girbil.o obj-$(CONFIG_LITELINK_DONGLE) += litelink.o obj-$(CONFIG_OLD_BELKIN_DONGLE) += old_belkin.o diff -u -p --new-file linux/drivers/net/irda.d1/smsc-ircc2.c linux/drivers/net/irda/smsc-ircc2.c --- linux/drivers/net/irda.d1/smsc-ircc2.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/irda/smsc-ircc2.c Tue May 13 13:24:35 2003 @@ -0,0 +1,2441 @@ +/********************************************************************* + * $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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#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 */ + + 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 int smsc_ircc_setup_io(struct smsc_ircc_cb *self, unsigned int fir_base, unsigned int sir_base, u8 dma, u8 irq); +static int smsc_ircc_setup_buffers(struct smsc_ircc_cb *self); +static void smsc_ircc_setup_qos(struct smsc_ircc_cb *self); +static int smsc_ircc_setup_netdev(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 void smsc_ircc_interrupt_sir(int irq, void *dev_id, struct pt_regs *regs); +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_init(struct net_device *dev); +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 */ + +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 + */ +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)) { + MESSAGE(" Overriding FIR address 0x%04x\n", ircc_fir); + 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) { + 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; + int err; + + IRDA_DEBUG(1, "%s\n", __FUNCTION__); + + err= smsc_ircc_present(fir_base, sir_base); + if(err) return -ENODEV; + + if (dev_count>DIM(dev_self)) { + WARNING("%s(), too many devices!\n", __FUNCTION__); + return -ENOMEM; + } + + /* + * Allocate new instance of the driver + */ + self = kmalloc(sizeof(struct smsc_ircc_cb), GFP_KERNEL); + if (self == NULL) { + ERROR("%s, Can't allocate memory for control block!\n", + driver_name); + return -ENOMEM; + } + memset(self, 0, sizeof(struct smsc_ircc_cb)); + + /* Need to store self somewhere */ + dev_self[dev_count++] = self; + spin_lock_init(&self->lock); + + err = smsc_ircc_setup_buffers(self); + if(err) return err; + + err= smsc_ircc_setup_io(self, fir_base, sir_base, dma, irq); + if(err) return err; + + smsc_ircc_setup_qos(self); + + self->flags = IFF_FIR|IFF_MIR|IFF_SIR|IFF_DMA|IFF_PIO; + + 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 = smsc_ircc_setup_netdev(self); + if(err) return err; + + self->pmdev = pm_register(PM_SYS_DEV, PM_SYS_IRDA, smsc_ircc_pmproc); + if (self->pmdev) + self->pmdev->data = self; + + return 0; +} + +/* + * 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 (check_region(fir_base, SMSC_IRCC2_FIR_CHIP_IO_EXTENT) < 0) { + WARNING("%s: can't get fir_base of 0x%03x\n", + __FUNCTION__, fir_base); + return -ENODEV; + } +#if POSSIBLE_USED_BY_SERIAL_DRIVER + if (check_region(sir_base, SMSC_IRCC2_SIR_CHIP_IO_EXTENT) < 0) { + WARNING("%s: can't get sir_base of 0x%03x\n", + __FUNCTION__, sir_base); + return -ENODEV; + } +#endif + + 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)) { + WARNING("%s(), addr 0x%04x - no device found!\n", + __FUNCTION__, fir_base); + return -ENODEV; + } + 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; +} + +/* + * Function smsc_ircc_setup_buffers(self) + * + * Setup RX/TX buffers + * + */ +static int smsc_ircc_setup_buffers(struct smsc_ircc_cb *self) +{ + self->rx_buff.truesize = SMSC_IRCC2_RX_BUFF_TRUESIZE; + self->tx_buff.truesize = SMSC_IRCC2_TX_BUFF_TRUESIZE; + + self->rx_buff.head = (u8 *) kmalloc(self->rx_buff.truesize, + GFP_KERNEL|GFP_DMA); + if (self->rx_buff.head == NULL) { + ERROR("%s, Can't allocate memory for receive buffer!\n", + driver_name); + kfree(self); + return -ENOMEM; + } + + self->tx_buff.head = (u8 *) kmalloc(self->tx_buff.truesize, + GFP_KERNEL|GFP_DMA); + if (self->tx_buff.head == NULL) { + ERROR("%s, Can't allocate memory for transmit buffer!\n", + driver_name); + kfree(self->rx_buff.head); + kfree(self); + return -ENOMEM; + } + + 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; + + return 0; +} + +/* + * Function smsc_ircc_setup_io(self, fir_base, sir_base, dma, irq) + * + * Setup I/O + * + */ +static int 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; + void *ret; + + 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) + 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) + 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; + + ret = request_region(self->io.fir_base, self->io.fir_ext, driver_name); + if (!ret) { + WARNING("%s(), can't get iobase of 0x%03x\n", + __FUNCTION__, self->io.fir_base); + kfree(self->tx_buff.head); + kfree(self->rx_buff.head); + kfree(self); + return -ENODEV; + } + ret = request_region(self->io.sir_base, self->io.sir_ext, driver_name); + if (!ret) { + WARNING("%s(), can't get iobase of 0x%03x\n", + __FUNCTION__, self->io.sir_base); + release_region(self->io.fir_base, self->io.fir_ext); + kfree(self->tx_buff.head); + kfree(self->rx_buff.head); + kfree(self); + return -ENODEV; + } + + return 0; +} + +/* + * 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; + + 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_setup_netdev(self) + * + * Alloc and setup network device + * + */ +static int smsc_ircc_setup_netdev(struct smsc_ircc_cb *self) +{ + struct net_device *dev; + int err; + /* Alloc netdev */ + + if (!(dev = dev_alloc("irda%d", &err))) { + ERROR("%s(), dev_alloc() failed!\n", __FUNCTION__); + kfree(self->tx_buff.head); + kfree(self->rx_buff.head); + kfree(self); + return -ENOMEM; + } + + dev->priv = (void *) self; + self->netdev = dev; + + dev->init = smsc_ircc_net_init; + 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; + + /* Make ifconfig display some details */ + dev->base_addr = self->io.fir_base; + dev->irq = self->io.irq; + + rtnl_lock(); + err = register_netdevice(dev); + rtnl_unlock(); + if (err) { + ERROR("%s(), register_netdev() failed!\n", __FUNCTION__); + kfree(self->tx_buff.head); + kfree(self->rx_buff.head); + kfree(self); + return -ENODEV; + } + MESSAGE("IrDA: Registered device %s\n", dev->name); + + return 0; +} + +/* + * 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; + + ASSERT(dev != NULL, return -1;); + + self = dev->priv; + + 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; + + 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__); + + ASSERT(dev != NULL, return 0;); + + self = (struct smsc_ircc_cb *) dev->priv; + 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; + + 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__); + + ASSERT(self != NULL, return;); + dev = self->netdev; + 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__); + + 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); + + 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); + + 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; + + ASSERT(dev != NULL, return 0;); + self = (struct smsc_ircc_cb *) dev->priv; + 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_skb(skb); + + return 0; +} + +/* + * Function smsc_ircc_dma_xmit (self, iobase) + * + * Transmit data using DMA + * + */ +static void smsc_ircc_dma_xmit(struct smsc_ircc_cb *self, int iobase, int bofs) +{ + u8 ctrl; + + IRDA_DEBUG(3, "%s\n", __FUNCTION__); +#if 1 + /* 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); + + self->io.direction = IO_XMIT; + + /* Set BOF additional count for generating the min turn time */ + register_bank(iobase, 4); + 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); + + /*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 | + IRCC_CFGB_DMA_BURST, iobase+IRCC_SCE_CFGB); + + /* 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); + + /* Enable interrupt */ + + register_bank(iobase, 0); + outb(IRCC_IER_ACTIVE_FRAME | IRCC_IER_EOM, iobase+IRCC_IER); + 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); +} + +/* + * Function smsc_ircc_dma_xmit_complete (self) + * + * The transfer of a frame in finished. This function will only be called + * by the interrupt handler + * + */ +static void smsc_ircc_dma_xmit_complete(struct smsc_ircc_cb *self, int iobase) +{ + IRDA_DEBUG(3, "%s\n", __FUNCTION__); +#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 underrun! */ + register_bank(iobase, 0); + if (inb(iobase+IRCC_LSR) & IRCC_LSR_UNDERRUN) { + self->stats.tx_errors++; + self->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->stats.tx_packets++; + self->stats.tx_bytes += self->tx_buff.len; + } + + /* Check if it's time to change the speed */ + if (self->new_speed) { + smsc_ircc_change_speed(self, self->new_speed); + self->new_speed = 0; + } + + netif_wake_queue(self->netdev); +} + +/* + * Function smsc_ircc_dma_receive(self) + * + * Get ready for receiving a frame. The device will initiate a DMA + * if it starts to receive a frame. + * + */ +static int smsc_ircc_dma_receive(struct smsc_ircc_cb *self, int iobase) +{ +#if 0 + /* Turn off chip DMA */ + register_bank(iobase, 1); + outb(inb(iobase+IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE, + iobase+IRCC_SCE_CFGB); +#endif + + /* Disable Tx */ + register_bank(iobase, 0); + outb(0x00, iobase+IRCC_LCR_B); + + /* Turn off chip DMA */ + register_bank(iobase, 1); + outb(inb(iobase+IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE, + iobase+IRCC_SCE_CFGB); + + self->io.direction = IO_RECV; + self->rx_buff.data = self->rx_buff.head; + + /* 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); + + /* Setup DMA controller */ + setup_dma(self->io.dma, self->rx_buff.data, self->rx_buff.truesize, + DMA_RX_MODE); + + /* 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); + + /* Enable interrupt */ + register_bank(iobase, 0); + outb(IRCC_IER_ACTIVE_FRAME | IRCC_IER_EOM, iobase+IRCC_IER); + outb(IRCC_MASTER_INT_EN, iobase+IRCC_MASTER); + + + /* Enable receiver */ + register_bank(iobase, 0); + outb(IRCC_LCR_B_SCE_RECEIVE | IRCC_LCR_B_SIP_ENABLE, + iobase+IRCC_LCR_B); + + return 0; +} + +/* + * Function smsc_ircc_dma_receive_complete(self, iobase) + * + * Finished with receiving frames + * + */ +static void smsc_ircc_dma_receive_complete(struct smsc_ircc_cb *self, int iobase) +{ + struct sk_buff *skb; + int len, msgcnt, lsr; + + register_bank(iobase, 0); + + IRDA_DEBUG(3, "%s\n", __FUNCTION__); +#if 0 + /* Disable Rx */ + register_bank(iobase, 0); + outb(0x00, iobase+IRCC_LCR_B); +#endif + register_bank(iobase, 0); + outb(inb(iobase+IRCC_LSAR) & ~IRCC_LSAR_ADDRESS_MASK, iobase+IRCC_LSAR); + lsr= inb(iobase+IRCC_LSR); + msgcnt = inb(iobase+IRCC_LCR_B) & 0x08; + + IRDA_DEBUG(2, "%s: dma count = %d\n", __FUNCTION__, + get_dma_residue(self->io.dma)); + + len = self->rx_buff.truesize - get_dma_residue(self->io.dma); + + /* Look for errors + */ + + if(lsr & (IRCC_LSR_FRAME_ERROR | IRCC_LSR_CRC_ERROR | IRCC_LSR_SIZE_ERROR)) { + self->stats.rx_errors++; + if(lsr & IRCC_LSR_FRAME_ERROR) self->stats.rx_frame_errors++; + if(lsr & IRCC_LSR_CRC_ERROR) self->stats.rx_crc_errors++; + if(lsr & IRCC_LSR_SIZE_ERROR) self->stats.rx_length_errors++; + if(lsr & (IRCC_LSR_UNDERRUN | IRCC_LSR_OVERRUN)) self->stats.rx_length_errors++; + return; + } + /* Remove CRC */ + if (self->io.speed < 4000000) + len -= 2; + else + len -= 4; + + if ((len < 2) || (len > 2050)) { + WARNING("%s(), bogus len=%d\n", __FUNCTION__, len); + return; + } + IRDA_DEBUG(2, "%s: msgcnt = %d, len=%d\n", __FUNCTION__, msgcnt, len); + + skb = dev_alloc_skb(len+1); + if (!skb) { + WARNING("%s(), memory squeeze, dropping frame.\n", + __FUNCTION__); + return; + } + /* Make sure IP header gets aligned */ + skb_reserve(skb, 1); + + memcpy(skb_put(skb, len), self->rx_buff.data, len); + self->stats.rx_packets++; + self->stats.rx_bytes += len; + + skb->dev = self->netdev; + skb->mac.raw = skb->data; + skb->protocol = htons(ETH_P_IRDA); + netif_rx(skb); +} + +/* + * Function smsc_ircc_sir_receive (self) + * + * Receive one frame from the infrared port + * + */ +static void smsc_ircc_sir_receive(struct smsc_ircc_cb *self) +{ + int boguscount = 0; + int iobase; + + ASSERT(self != NULL, return;); + + iobase = self->io.sir_base; + + /* + * Receive all characters in Rx FIFO, unwrap and unstuff them. + * async_unwrap_char will deliver all found frames + */ + do { + async_unwrap_char(self->netdev, &self->stats, &self->rx_buff, + inb(iobase+UART_RX)); + + /* Make sure we don't stay here to long */ + if (boguscount++ > 32) { + IRDA_DEBUG(2, "%s(), breaking!\n", __FUNCTION__); + break; + } + } while (inb(iobase+UART_LSR) & UART_LSR_DR); +} + + +/* + * Function smsc_ircc_interrupt (irq, dev_id, regs) + * + * An interrupt from the chip has arrived. Time to do some work + * + */ +static irqreturn_t smsc_ircc_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct smsc_ircc_cb *self; + int iobase, iir, lcra, lsr; + + if (dev == NULL) { + printk(KERN_WARNING "%s: irq %d for unknown device.\n", + driver_name, irq); + return IRQ_NONE; + } + self = (struct smsc_ircc_cb *) dev->priv; + ASSERT(self != NULL, return IRQ_NONE;); + + /* Serialise the interrupt handler in various CPUs, stop Tx path */ + spin_lock(&self->lock); + + /* Check if we should use the SIR interrupt handler */ + if (self->io.speed <= SMSC_IRCC2_MAX_SIR_SPEED) { + smsc_ircc_interrupt_sir(irq, dev_id, regs); + spin_unlock(&self->lock); + return IRQ_HANDLED; + } + iobase = self->io.fir_base; + + register_bank(iobase, 0); + iir = inb(iobase+IRCC_IIR); + /* Disable interrupts */ + outb(0, iobase+IRCC_IER); + lcra = inb(iobase+IRCC_LCR_A); + lsr = inb(iobase+IRCC_LSR); + + IRDA_DEBUG(2, "%s(), iir = 0x%02x\n", __FUNCTION__, iir); + + if (iir & IRCC_IIR_EOM) { + if (self->io.direction == IO_RECV) + smsc_ircc_dma_receive_complete(self, iobase); + else + smsc_ircc_dma_xmit_complete(self, iobase); + + smsc_ircc_dma_receive(self, iobase); + } + + if (iir & IRCC_IIR_ACTIVE_FRAME) { + /*printk(KERN_WARNING __FUNCTION__ "(): Active Frame\n");*/ + } + + /* Enable interrupts again */ + + register_bank(iobase, 0); + outb(IRCC_IER_ACTIVE_FRAME|IRCC_IER_EOM, iobase+IRCC_IER); + + spin_unlock(&self->lock); + return IRQ_HANDLED; +} + +/* + * Function irport_interrupt_sir (irq, dev_id, regs) + * + * Interrupt handler for SIR modes + */ +void smsc_ircc_interrupt_sir(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct smsc_ircc_cb *self; + int boguscount = 0; + int iobase; + int iir, lsr; + + if (!dev) { + WARNING("%s() irq %d for unknown device.\n", + __FUNCTION__, irq); + return; + } + self = (struct smsc_ircc_cb *) dev->priv; + + /* Already locked comming here in smsc_ircc_interrupt() */ + /*spin_lock(&self->lock);*/ + + iobase = self->io.sir_base; + + iir = inb(iobase+UART_IIR) & UART_IIR_ID; + while (iir) { + /* Clear interrupt */ + lsr = inb(iobase+UART_LSR); + + IRDA_DEBUG(4, "%s(), iir=%02x, lsr=%02x, iobase=%#x\n", + __FUNCTION__, iir, lsr, iobase); + + switch (iir) { + case UART_IIR_RLSI: + IRDA_DEBUG(2, "%s(), RLSI\n", __FUNCTION__); + break; + case UART_IIR_RDI: + /* Receive interrupt */ + smsc_ircc_sir_receive(self); + break; + case UART_IIR_THRI: + if (lsr & UART_LSR_THRE) + /* Transmitter ready for data */ + smsc_ircc_sir_write_wakeup(self); + break; + default: + IRDA_DEBUG(0, "%s(), unhandled IIR=%#x\n", + __FUNCTION__, iir); + break; + } + + /* Make sure we don't stay here to long */ + if (boguscount++ > 100) + break; + + iir = inb(iobase + UART_IIR) & UART_IIR_ID; + } + /*spin_unlock(&self->lock);*/ +} + + +#if 0 /* unused */ +/* + * Function ircc_is_receiving (self) + * + * Return TRUE is we are currently receiving a frame + * + */ +static int ircc_is_receiving(struct smsc_ircc_cb *self) +{ + int status = FALSE; + /* int iobase; */ + + IRDA_DEBUG(1, "%s\n", __FUNCTION__); + + ASSERT(self != NULL, return FALSE;); + + IRDA_DEBUG(0, "%s: dma count = %d\n", __FUNCTION__, + get_dma_residue(self->io.dma)); + + status = (self->rx_buff.state != OUTSIDE_FRAME); + + return status; +} +#endif /* unused */ + +static int smsc_ircc_net_init(struct net_device *dev) +{ + /* Keep track of module usage */ + SET_MODULE_OWNER(dev); + + /* Set up to be a normal IrDA network device driver */ + irda_device_setup(dev); + + /* Insert overrides below this line! */ + + return 0; +} + +/* + * Function smsc_ircc_net_open (dev) + * + * Start the device + * + */ +static int smsc_ircc_net_open(struct net_device *dev) +{ + struct smsc_ircc_cb *self; + int iobase; + char hwname[16]; + unsigned long flags; + + IRDA_DEBUG(1, "%s\n", __FUNCTION__); + + ASSERT(dev != NULL, return -1;); + self = (struct smsc_ircc_cb *) dev->priv; + ASSERT(self != NULL, return 0;); + + iobase = self->io.fir_base; + + if (request_irq(self->io.irq, smsc_ircc_interrupt, 0, dev->name, + (void *) dev)) { + IRDA_DEBUG(0, "%s(), unable to allocate irq=%d\n", + __FUNCTION__, self->io.irq); + return -EAGAIN; + } + + spin_lock_irqsave(&self->lock, flags); + /*smsc_ircc_sir_start(self);*/ + self->io.speed = 0; + smsc_ircc_change_speed(self, SMSC_IRCC2_C_IRDA_FALLBACK_SPEED); + spin_unlock_irqrestore(&self->lock, flags); + + /* Give self a hardware name */ + /* It would be cool to offer the chip revision here - Jean II */ + sprintf(hwname, "SMSC @ 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); + + /* + * Always allocate the DMA channel after the IRQ, + * and clean up on failure. + */ + if (request_dma(self->io.dma, dev->name)) { + smsc_ircc_net_close(dev); + + WARNING("%s(), unable to allocate DMA=%d\n", + __FUNCTION__, self->io.dma); + return -EAGAIN; + } + + netif_start_queue(dev); + + return 0; +} + +/* + * Function smsc_ircc_net_close (dev) + * + * Stop the device + * + */ +static int smsc_ircc_net_close(struct net_device *dev) +{ + struct smsc_ircc_cb *self; + int iobase; + + IRDA_DEBUG(1, "%s\n", __FUNCTION__); + + ASSERT(dev != NULL, return -1;); + self = (struct smsc_ircc_cb *) dev->priv; + 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; + + free_irq(self->io.irq, dev); + + disable_dma(self->io.dma); + + free_dma(self->io.dma); + + return 0; +} + + +static void smsc_ircc_suspend(struct smsc_ircc_cb *self) +{ + MESSAGE("%s, Suspending\n", driver_name); + + if (self->io.suspended) + return; + + smsc_ircc_net_close(self->netdev); + + self->io.suspended = 1; +} + +static void smsc_ircc_wakeup(struct smsc_ircc_cb *self) +{ + if (!self->io.suspended) + return; + + /* The code was doing a "cli()" here, but this can't be right. + * If you need protection, do it in net_open with a spinlock + * or give a good reason. - Jean II */ + + smsc_ircc_net_open(self->netdev); + + MESSAGE("%s, Waking up\n", driver_name); +} + +static int smsc_ircc_pmproc(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + struct smsc_ircc_cb *self = (struct smsc_ircc_cb*) dev->data; + if (self) { + switch (rqst) { + case PM_SUSPEND: + smsc_ircc_suspend(self); + break; + case PM_RESUME: + smsc_ircc_wakeup(self); + break; + } + } + return 0; +} + +/* + * Function smsc_ircc_close (self) + * + * Close driver instance + * + */ +static int __exit smsc_ircc_close(struct smsc_ircc_cb *self) +{ + int iobase; + unsigned long flags; + + IRDA_DEBUG(1, "%s\n", __FUNCTION__); + + ASSERT(self != NULL, return -1;); + + iobase = self->io.fir_base; + + if (self->pmdev) + pm_unregister(self->pmdev); + + /* Remove netdevice */ + if (self->netdev) { + rtnl_lock(); + unregister_netdevice(self->netdev); + rtnl_unlock(); + } + + /* Make sure the irq handler is not exectuting */ + spin_lock_irqsave(&self->lock, flags); + + /* 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 + spin_unlock_irqrestore(&self->lock, flags); + + /* Release the PORTS that this driver is using */ + IRDA_DEBUG(0, "%s(), releasing 0x%03x\n", __FUNCTION__, + self->io.fir_base); + + release_region(self->io.fir_base, self->io.fir_ext); + + IRDA_DEBUG(0, "%s(), releasing 0x%03x\n", __FUNCTION__, + self->io.sir_base); + + release_region(self->io.sir_base, self->io.sir_ext); + + if (self->tx_buff.head) + kfree(self->tx_buff.head); + + if (self->rx_buff.head) + kfree(self->rx_buff.head); + + kfree(self); + + return 0; +} + +void __exit smsc_ircc_cleanup(void) +{ + int i; + + IRDA_DEBUG(1, "%s\n", __FUNCTION__); + + for (i=0; i < 2; i++) { + if (dev_self[i]) + smsc_ircc_close(dev_self[i]); + } +} + +/* + * Start SIR operations + * + * This function *must* be called with spinlock held, because it may + * be called from the irq handler (via smsc_ircc_change_speed()). - Jean II + */ +void smsc_ircc_sir_start(struct smsc_ircc_cb *self) +{ + struct net_device *dev; + int fir_base, sir_base; + + IRDA_DEBUG(3, "%s\n", __FUNCTION__); + + ASSERT(self != NULL, return;); + dev= self->netdev; + ASSERT(dev != NULL, return;); + dev->hard_start_xmit = &smsc_ircc_hard_xmit_sir; + + fir_base = self->io.fir_base; + sir_base = self->io.sir_base; + + /* Reset everything */ + outb(IRCC_MASTER_RESET, fir_base+IRCC_MASTER); + + #if SMSC_IRCC2_C_SIR_STOP + /*smsc_ircc_sir_stop(self);*/ + #endif + + register_bank(fir_base, 1); + outb(((inb(fir_base+IRCC_SCE_CFGA) & IRCC_SCE_CFGA_BLOCK_CTRL_BITS_MASK) | IRCC_CFGA_IRDA_SIR_A), fir_base+IRCC_SCE_CFGA); + + /* Initialize UART */ + outb(UART_LCR_WLEN8, sir_base+UART_LCR); /* Reset DLAB */ + outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), sir_base+UART_MCR); + + /* Turn on interrups */ + outb(UART_IER_RLSI | UART_IER_RDI |UART_IER_THRI, sir_base+UART_IER); + + IRDA_DEBUG(3, "%s() - exit\n", __FUNCTION__); + + outb(0x00, fir_base+IRCC_MASTER); +} + +#if SMSC_IRCC2_C_SIR_STOP +void smsc_ircc_sir_stop(struct smsc_ircc_cb *self) +{ + int iobase; + + IRDA_DEBUG(3, "%s\n", __FUNCTION__); + iobase = self->io.sir_base; + + /* Reset UART */ + outb(0, iobase+UART_MCR); + + /* Turn off interrupts */ + outb(0, iobase+UART_IER); +} +#endif + +/* + * Function smsc_sir_write_wakeup (self) + * + * Called by the SIR interrupt handler when there's room for more data. + * If we have more packets to send, we send them here. + * + */ +static void smsc_ircc_sir_write_wakeup(struct smsc_ircc_cb *self) +{ + int actual = 0; + int iobase; + int fcr; + + ASSERT(self != NULL, return;); + + IRDA_DEBUG(4, "%s\n", __FUNCTION__); + + iobase = self->io.sir_base; + + /* Finished with frame? */ + if (self->tx_buff.len > 0) { + /* Write data left in transmit buffer */ + actual = smsc_ircc_sir_write(iobase, self->io.fifo_size, + self->tx_buff.data, self->tx_buff.len); + self->tx_buff.data += actual; + self->tx_buff.len -= actual; + } else { + + /*if (self->tx_buff.len ==0) {*/ + + /* + * Now serial buffer is almost free & we can start + * transmission of another packet. But first we must check + * if we need to change the speed of the hardware + */ + if (self->new_speed) { + IRDA_DEBUG(5, "%s(), Changing speed to %d.\n", + __FUNCTION__, self->new_speed); + smsc_ircc_sir_wait_hw_transmitter_finish(self); + smsc_ircc_change_speed(self, self->new_speed); + self->new_speed = 0; + } else { + /* Tell network layer that we want more frames */ + netif_wake_queue(self->netdev); + } + self->stats.tx_packets++; + + if(self->io.speed <= 115200) { + /* + * Reset Rx FIFO to make sure that all reflected transmit data + * is discarded. This is needed for half duplex operation + */ + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR; + if (self->io.speed < 38400) + fcr |= UART_FCR_TRIGGER_1; + else + fcr |= UART_FCR_TRIGGER_14; + + outb(fcr, iobase+UART_FCR); + + /* Turn on receive interrupts */ + outb(UART_IER_RDI, iobase+UART_IER); + } + } +} + +/* + * Function smsc_ircc_sir_write (iobase, fifo_size, buf, len) + * + * Fill Tx FIFO with transmit data + * + */ +static int smsc_ircc_sir_write(int iobase, int fifo_size, __u8 *buf, int len) +{ + int actual = 0; + + /* Tx FIFO should be empty! */ + if (!(inb(iobase+UART_LSR) & UART_LSR_THRE)) { + WARNING("%s(), failed, fifo not empty!\n", __FUNCTION__); + return 0; + } + + /* Fill FIFO with current frame */ + while ((fifo_size-- > 0) && (actual < len)) { + /* Transmit next byte */ + outb(buf[actual], iobase+UART_TX); + actual++; + } + return actual; +} + +/* + * Function smsc_ircc_is_receiving (self) + * + * Returns true is we are currently receiving data + * + */ +static int smsc_ircc_is_receiving(struct smsc_ircc_cb *self) +{ + return (self->rx_buff.state != OUTSIDE_FRAME); +} + + +/* + * Function smsc_ircc_probe_transceiver(self) + * + * Tries to find the used Transceiver + * + */ +static void smsc_ircc_probe_transceiver(struct smsc_ircc_cb *self) +{ + unsigned int i; + + ASSERT(self != NULL, return;); + + for(i=0; smsc_transceivers[i].name!=NULL; i++) + if((*smsc_transceivers[i].probe)(self->io.fir_base)) { + MESSAGE(" %s transceiver found\n", smsc_transceivers[i].name); + self->transceiver= i+1; + return; + } + MESSAGE("No transceiver found. Defaulting to %s\n", smsc_transceivers[SMSC_IRCC2_C_DEFAULT_TRANSCEIVER].name); + + self->transceiver= SMSC_IRCC2_C_DEFAULT_TRANSCEIVER; +} + + +/* + * Function smsc_ircc_set_transceiver_for_speed(self, speed) + * + * Set the transceiver according to the speed + * + */ +static void smsc_ircc_set_transceiver_for_speed(struct smsc_ircc_cb *self, u32 speed) +{ + unsigned int trx; + + trx = self->transceiver; + if(trx>0) (*smsc_transceivers[trx-1].set_for_speed)(self->io.fir_base, speed); +} + +/* + * Function smsc_ircc_wait_hw_transmitter_finish () + * + * Wait for the real end of HW transmission + * + * The UART is a strict FIFO, and we get called only when we have finished + * pushing data to the FIFO, so the maximum amount of time we must wait + * is only for the FIFO to drain out. + * + * We use a simple calibrated loop. We may need to adjust the loop + * delay (udelay) to balance I/O traffic and latency. And we also need to + * adjust the maximum timeout. + * It would probably be better to wait for the proper interrupt, + * but it doesn't seem to be available. + * + * We can't use jiffies or kernel timers because : + * 1) We are called from the interrupt handler, which disable softirqs, + * so jiffies won't be increased + * 2) Jiffies granularity is usually very coarse (10ms), and we don't + * want to wait that long to detect stuck hardware. + * Jean II + */ + +static void smsc_ircc_sir_wait_hw_transmitter_finish(struct smsc_ircc_cb *self) +{ + int iobase; + int count = SMSC_IRCC2_HW_TRANSMITTER_TIMEOUT_US; + + iobase = self->io.sir_base; + + /* Calibrated busy loop */ + while((count-- > 0) && !(inb(iobase+UART_LSR) & UART_LSR_TEMT)) + udelay(1); + + if(count == 0) + IRDA_DEBUG(0, "%s(): stuck transmitter\n", __FUNCTION__); +} + + +/* PROBING + * + * + */ + +static int __init smsc_ircc_look_for_chips(void) +{ + smsc_chip_address_t *address; + char *type; + unsigned int cfg_base, found; + + found = 0; + address = possible_addresses; + + while(address->cfg_base){ + cfg_base = address->cfg_base; + + /*printk(KERN_WARNING __FUNCTION__ "(): probing: 0x%02x for: 0x%02x\n", cfg_base, address->type);*/ + + if( address->type & SMSCSIO_TYPE_FDC){ + type = "FDC"; + if((address->type) & SMSCSIO_TYPE_FLAT) { + if(!smsc_superio_flat(fdc_chips_flat,cfg_base, type)) found++; + } + if((address->type) & SMSCSIO_TYPE_PAGED) { + if(!smsc_superio_paged(fdc_chips_paged,cfg_base, type)) found++; + } + } + if( address->type & SMSCSIO_TYPE_LPC){ + type = "LPC"; + if((address->type) & SMSCSIO_TYPE_FLAT) { + if(!smsc_superio_flat(lpc_chips_flat,cfg_base,type)) found++; + } + if((address->type) & SMSCSIO_TYPE_PAGED) { + if(!smsc_superio_paged(lpc_chips_paged,cfg_base,"LPC")) found++; + } + } + address++; + } + return found; +} + +/* + * Function smsc_superio_flat (chip, base, type) + * + * Try to get configuration of a smc SuperIO chip with flat register model + * + */ +static int __init smsc_superio_flat(const smsc_chip_t *chips, unsigned short cfgbase, char *type) +{ + unsigned short firbase, sirbase; + u8 mode, dma, irq; + int ret = -ENODEV; + + IRDA_DEBUG(1, "%s\n", __FUNCTION__); + + if (smsc_ircc_probe(cfgbase, SMSCSIOFLAT_DEVICEID_REG, chips, type)==NULL) + return ret; + + outb(SMSCSIOFLAT_UARTMODE0C_REG, cfgbase); + mode = inb(cfgbase+1); + + /*printk(KERN_WARNING __FUNCTION__ "(): mode: 0x%02x\n", mode);*/ + + if(!(mode & SMSCSIOFLAT_UART2MODE_VAL_IRDA)) + WARNING("%s(): IrDA not enabled\n", __FUNCTION__); + + outb(SMSCSIOFLAT_UART2BASEADDR_REG, cfgbase); + sirbase = inb(cfgbase+1) << 2; + + /* FIR iobase */ + outb(SMSCSIOFLAT_FIRBASEADDR_REG, cfgbase); + firbase = inb(cfgbase+1) << 3; + + /* DMA */ + outb(SMSCSIOFLAT_FIRDMASELECT_REG, cfgbase); + dma = inb(cfgbase+1) & SMSCSIOFLAT_FIRDMASELECT_MASK; + + /* IRQ */ + outb(SMSCSIOFLAT_UARTIRQSELECT_REG, cfgbase); + irq = inb(cfgbase+1) & SMSCSIOFLAT_UART2IRQSELECT_MASK; + + MESSAGE("%s(): fir: 0x%02x, sir: 0x%02x, dma: %02d, irq: %d, mode: 0x%02x\n", __FUNCTION__, firbase, sirbase, dma, irq, mode); + + if (firbase) { + if (smsc_ircc_open(firbase, sirbase, dma, irq) == 0) + ret=0; + } + + /* Exit configuration */ + outb(SMSCSIO_CFGEXITKEY, cfgbase); + + return ret; +} + +/* + * Function smsc_superio_paged (chip, base, type) + * + * Try to get configuration of a smc SuperIO chip with paged register model + * + */ +static int __init smsc_superio_paged(const smsc_chip_t *chips, unsigned short cfg_base, char *type) +{ + unsigned short fir_io, sir_io; + int ret = -ENODEV; + + IRDA_DEBUG(1, "%s\n", __FUNCTION__); + + if (smsc_ircc_probe(cfg_base,0x20,chips,type)==NULL) + return ret; + + /* Select logical device (UART2) */ + outb(0x07, cfg_base); + outb(0x05, cfg_base + 1); + + /* SIR iobase */ + outb(0x60, cfg_base); + sir_io = inb(cfg_base + 1) << 8; + outb(0x61, cfg_base); + sir_io |= 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) { + if (smsc_ircc_open(fir_io, sir_io, ircc_dma, ircc_irq) == 0) + ret=0; + } + + /* Exit configuration */ + outb(SMSCSIO_CFGEXITKEY, cfg_base); + + return ret; +} + + +static int __init smsc_access(unsigned short cfg_base,unsigned char reg) +{ + IRDA_DEBUG(1, "%s\n", __FUNCTION__); + + outb(reg, cfg_base); + + if (inb(cfg_base)!=reg) + return -1; + + return 0; +} + +static const smsc_chip_t * __init smsc_ircc_probe(unsigned short cfg_base,u8 reg,const smsc_chip_t *chip,char *type) +{ + u8 devid,xdevid,rev; + + IRDA_DEBUG(1, "%s\n", __FUNCTION__); + + /* Leave configuration */ + + outb(SMSCSIO_CFGEXITKEY, cfg_base); + + if (inb(cfg_base) == SMSCSIO_CFGEXITKEY) /* not a smc superio chip */ + return NULL; + + outb(reg, cfg_base); + + xdevid=inb(cfg_base+1); + + /* Enter configuration */ + + outb(SMSCSIO_CFGACCESSKEY, cfg_base); + + #if 0 + if (smsc_access(cfg_base,0x55)) /* send second key and check */ + return NULL; + #endif + + /* probe device ID */ + + if (smsc_access(cfg_base,reg)) + return NULL; + + devid=inb(cfg_base+1); + + if (devid==0) /* typical value for unused port */ + return NULL; + + if (devid==0xff) /* typical value for unused port */ + return NULL; + + /* probe revision ID */ + + if (smsc_access(cfg_base,reg+1)) + return NULL; + + rev=inb(cfg_base+1); + + if (rev>=128) /* i think this will make no sense */ + return NULL; + + if (devid==xdevid) /* protection against false positives */ + return NULL; + + /* Check for expected device ID; are there others? */ + + while(chip->devid!=devid) { + + chip++; + + if (chip->name==NULL) + return NULL; + } + + MESSAGE("found SMC SuperIO Chip (devid=0x%02x rev=%02X base=0x%04x): %s%s\n",devid,rev,cfg_base,type,chip->name); + + if (chip->rev>rev){ + MESSAGE("Revision higher than expected\n"); + return NULL; + } + + if (chip->flags&NoIRDA) + MESSAGE("chipset does not support IRDA\n"); + + return chip; +} + +static int __init smsc_superio_fdc(unsigned short cfg_base) +{ + if (check_region(cfg_base, 2) < 0) { + WARNING("%s: can't get cfg_base of 0x%03x\n", + __FUNCTION__, cfg_base); + return -1; + } + + if (!smsc_superio_flat(fdc_chips_flat,cfg_base,"FDC")||!smsc_superio_paged(fdc_chips_paged,cfg_base,"FDC")) + return 0; + + return -1; +} + +static int __init smsc_superio_lpc(unsigned short cfg_base) +{ +#if 0 + if (check_region(cfg_base, 2) < 0) { + IRDA_DEBUG(0, __FUNCTION__ ": can't get cfg_base of 0x%03x\n", + cfg_base); + return -1; + } +#endif + + if (!smsc_superio_flat(lpc_chips_flat,cfg_base,"LPC")||!smsc_superio_paged(lpc_chips_paged,cfg_base,"LPC")) + return 0; + + return -1; +} + +/************************************************ + * + * Transceivers specific functions + * + ************************************************/ + + +/* + * Function smsc_ircc_set_transceiver_smsc_ircc_atc(fir_base, speed) + * + * Program transceiver through smsc-ircc ATC circuitry + * + */ + +static void smsc_ircc_set_transceiver_smsc_ircc_atc(int fir_base, u32 speed) +{ + unsigned long jiffies_now, jiffies_timeout; + u8 val; + + jiffies_now= jiffies; + jiffies_timeout= jiffies+SMSC_IRCC2_ATC_PROGRAMMING_TIMEOUT_JIFFIES; + + /* ATC */ + register_bank(fir_base, 4); + outb((inb(fir_base+IRCC_ATC) & IRCC_ATC_MASK) |IRCC_ATC_nPROGREADY|IRCC_ATC_ENABLE, fir_base+IRCC_ATC); + while((val=(inb(fir_base+IRCC_ATC) & IRCC_ATC_nPROGREADY)) && !time_after(jiffies, jiffies_timeout)); + if(val) + WARNING("%s(): ATC: 0x%02x\n", __FUNCTION__, + inb(fir_base+IRCC_ATC)); +} + +/* + * Function smsc_ircc_probe_transceiver_smsc_ircc_atc(fir_base) + * + * Probe transceiver smsc-ircc ATC circuitry + * + */ + +static int smsc_ircc_probe_transceiver_smsc_ircc_atc(int fir_base) +{ + return 0; +} + +/* + * Function smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select(self, speed) + * + * Set transceiver + * + */ + +static void smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select(int fir_base, u32 speed) +{ + u8 fast_mode; + + switch(speed) + { + default: + case 576000 : + fast_mode = 0; + break; + case 1152000 : + case 4000000 : + fast_mode = IRCC_LCR_A_FAST; + break; + + } + register_bank(fir_base, 0); + outb((inb(fir_base+IRCC_LCR_A) & 0xbf) | fast_mode, fir_base+IRCC_LCR_A); +} + +/* + * Function smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select(fir_base) + * + * Probe transceiver + * + */ + +static int smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select(int fir_base) +{ + return 0; +} + +/* + * Function smsc_ircc_set_transceiver_toshiba_sat1800(fir_base, speed) + * + * Set transceiver + * + */ + +static void smsc_ircc_set_transceiver_toshiba_sat1800(int fir_base, u32 speed) +{ + u8 fast_mode; + + switch(speed) + { + default: + case 576000 : + fast_mode = 0; + break; + case 1152000 : + case 4000000 : + fast_mode = /*IRCC_LCR_A_FAST |*/ IRCC_LCR_A_GP_DATA; + break; + + } + /* This causes an interrupt */ + register_bank(fir_base, 0); + outb((inb(fir_base+IRCC_LCR_A) & 0xbf) | fast_mode, fir_base+IRCC_LCR_A); +} + +/* + * Function smsc_ircc_probe_transceiver_toshiba_sat1800(fir_base) + * + * Probe transceiver + * + */ + +static int smsc_ircc_probe_transceiver_toshiba_sat1800(int fir_base) +{ + return 0; +} + + +module_init(smsc_ircc_init); +module_exit(smsc_ircc_cleanup); + +MODULE_AUTHOR("Daniele Peri "); +MODULE_DESCRIPTION("SMC IrCC SIR/FIR controller driver"); +MODULE_LICENSE("GPL"); + +MODULE_PARM(ircc_dma, "1i"); +MODULE_PARM_DESC(ircc_dma, "DMA channel"); +MODULE_PARM(ircc_irq, "1i"); +MODULE_PARM_DESC(ircc_irq, "IRQ line"); +MODULE_PARM(ircc_fir, "1-4i"); +MODULE_PARM_DESC(ircc_fir, "FIR Base Address"); +MODULE_PARM(ircc_sir, "1-4i"); +MODULE_PARM_DESC(ircc_sir, "SIR Base Address"); +MODULE_PARM(ircc_cfg, "1-4i"); +MODULE_PARM_DESC(ircc_cfg, "Configuration register base address"); +MODULE_PARM(ircc_transceiver, "1i"); +MODULE_PARM_DESC(ircc_transceiver, "Transceiver type"); diff -u -p --new-file linux/drivers/net/irda.d1/smsc-ircc2.h linux/drivers/net/irda/smsc-ircc2.h --- linux/drivers/net/irda.d1/smsc-ircc2.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/irda/smsc-ircc2.h Tue May 13 13:18:34 2003 @@ -0,0 +1,194 @@ +/********************************************************************* + * $Id: smsc-ircc2.h,v 1.12.2.1 2002/10/27 10:52:37 dip Exp $ + * + * Description: Definitions for the SMC IrCC chipset + * Status: Experimental. + * Author: Daniele Peri (peri@csai.unipa.it) + * + * Copyright (c) 2002 Daniele Peri + * All Rights Reserved. + * + * Based on smc-ircc.h: + * + * Copyright (c) 1999-2000, Dag Brattli + * Copyright (c) 1998-1999, Thomas Davis (tadavis@jps.net> + * 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 + * + ********************************************************************/ + +#ifndef SMSC_IRCC2_H +#define SMSC_IRCC2_H + +/* DMA modes needed */ +#define DMA_TX_MODE 0x08 /* Mem to I/O, ++, demand. */ +#define DMA_RX_MODE 0x04 /* I/O to mem, ++, demand. */ + +/* Master Control Register */ +#define IRCC_MASTER 0x07 +#define IRCC_MASTER_POWERDOWN 0x80 +#define IRCC_MASTER_RESET 0x40 +#define IRCC_MASTER_INT_EN 0x20 +#define IRCC_MASTER_ERROR_RESET 0x10 + +/* Register block 0 */ + +/* Interrupt Identification */ +#define IRCC_IIR 0x01 +#define IRCC_IIR_ACTIVE_FRAME 0x80 +#define IRCC_IIR_EOM 0x40 +#define IRCC_IIR_RAW_MODE 0x20 +#define IRCC_IIR_FIFO 0x10 + +/* Interrupt Enable */ +#define IRCC_IER 0x02 +#define IRCC_IER_ACTIVE_FRAME 0x80 +#define IRCC_IER_EOM 0x40 +#define IRCC_IER_RAW_MODE 0x20 +#define IRCC_IER_FIFO 0x10 + +/* Line Status Register */ +#define IRCC_LSR 0x03 +#define IRCC_LSR_UNDERRUN 0x80 +#define IRCC_LSR_OVERRUN 0x40 +#define IRCC_LSR_FRAME_ERROR 0x20 +#define IRCC_LSR_SIZE_ERROR 0x10 +#define IRCC_LSR_CRC_ERROR 0x80 +#define IRCC_LSR_FRAME_ABORT 0x40 + +/* Line Status Address Register */ +#define IRCC_LSAR 0x03 +#define IRCC_LSAR_ADDRESS_MASK 0x07 + +/* Line Control Register A */ +#define IRCC_LCR_A 0x04 +#define IRCC_LCR_A_FIFO_RESET 0x80 +#define IRCC_LCR_A_FAST 0x40 +#define IRCC_LCR_A_GP_DATA 0x20 +#define IRCC_LCR_A_RAW_TX 0x10 +#define IRCC_LCR_A_RAW_RX 0x08 +#define IRCC_LCR_A_ABORT 0x04 +#define IRCC_LCR_A_DATA_DONE 0x02 + +/* Line Control Register B */ +#define IRCC_LCR_B 0x05 +#define IRCC_LCR_B_SCE_DISABLED 0x00 +#define IRCC_LCR_B_SCE_TRANSMIT 0x40 +#define IRCC_LCR_B_SCE_RECEIVE 0x80 +#define IRCC_LCR_B_SCE_UNDEFINED 0xc0 +#define IRCC_LCR_B_SIP_ENABLE 0x20 +#define IRCC_LCR_B_BRICK_WALL 0x10 + +/* Bus Status Register */ +#define IRCC_BSR 0x06 +#define IRCC_BSR_NOT_EMPTY 0x80 +#define IRCC_BSR_FIFO_FULL 0x40 +#define IRCC_BSR_TIMEOUT 0x20 + +/* Register block 1 */ + +#define IRCC_FIFO_THRESHOLD 0x02 + +#define IRCC_SCE_CFGA 0x00 +#define IRCC_CFGA_AUX_IR 0x80 +#define IRCC_CFGA_HALF_DUPLEX 0x04 +#define IRCC_CFGA_TX_POLARITY 0x02 +#define IRCC_CFGA_RX_POLARITY 0x01 + +#define IRCC_CFGA_COM 0x00 +#define IRCC_SCE_CFGA_BLOCK_CTRL_BITS_MASK 0x87 +#define IRCC_CFGA_IRDA_SIR_A 0x08 +#define IRCC_CFGA_ASK_SIR 0x10 +#define IRCC_CFGA_IRDA_SIR_B 0x18 +#define IRCC_CFGA_IRDA_HDLC 0x20 +#define IRCC_CFGA_IRDA_4PPM 0x28 +#define IRCC_CFGA_CONSUMER 0x30 +#define IRCC_CFGA_RAW_IR 0x38 +#define IRCC_CFGA_OTHER 0x40 + +#define IRCC_IR_HDLC 0x04 +#define IRCC_IR_4PPM 0x01 +#define IRCC_IR_CONSUMER 0x02 + +#define IRCC_SCE_CFGB 0x01 +#define IRCC_CFGB_LOOPBACK 0x20 +#define IRCC_CFGB_LPBCK_TX_CRC 0x10 +#define IRCC_CFGB_NOWAIT 0x08 +#define IRCC_CFGB_STRING_MOVE 0x04 +#define IRCC_CFGB_DMA_BURST 0x02 +#define IRCC_CFGB_DMA_ENABLE 0x01 + +#define IRCC_CFGB_MUX_COM 0x00 +#define IRCC_CFGB_MUX_IR 0x40 +#define IRCC_CFGB_MUX_AUX 0x80 +#define IRCC_CFGB_MUX_INACTIVE 0xc0 + +/* Register block 3 - Identification Registers! */ +#define IRCC_ID_HIGH 0x00 /* 0x10 */ +#define IRCC_ID_LOW 0x01 /* 0xB8 */ +#define IRCC_CHIP_ID 0x02 /* 0xF1 */ +#define IRCC_VERSION 0x03 /* 0x01 */ +#define IRCC_INTERFACE 0x04 /* low 4 = DMA, high 4 = IRQ */ +#define IRCC_INTERFACE_DMA_MASK 0x0F /* low 4 = DMA, high 4 = IRQ */ +#define IRCC_INTERFACE_IRQ_MASK 0xF0 /* low 4 = DMA, high 4 = IRQ */ + +/* Register block 4 - IrDA */ +#define IRCC_CONTROL 0x00 +#define IRCC_BOF_COUNT_LO 0x01 /* Low byte */ +#define IRCC_BOF_COUNT_HI 0x00 /* High nibble (bit 0-3) */ +#define IRCC_BRICKWALL_CNT_LO 0x02 /* Low byte */ +#define IRCC_BRICKWALL_CNT_HI 0x03 /* High nibble (bit 4-7) */ +#define IRCC_TX_SIZE_LO 0x04 /* Low byte */ +#define IRCC_TX_SIZE_HI 0x03 /* High nibble (bit 0-3) */ +#define IRCC_RX_SIZE_HI 0x05 /* High nibble (bit 0-3) */ +#define IRCC_RX_SIZE_LO 0x06 /* Low byte */ + +#define IRCC_1152 0x80 +#define IRCC_CRC 0x40 + +/* Register block 5 - IrDA */ +#define IRCC_ATC 0x00 +#define IRCC_ATC_nPROGREADY 0x80 +#define IRCC_ATC_SPEED 0x40 +#define IRCC_ATC_ENABLE 0x20 +#define IRCC_ATC_MASK 0xE0 + + +#define IRCC_IRHALFDUPLEX_TIMEOUT 0x01 + +#define IRCC_SCE_TX_DELAY_TIMER 0x02 + +/* + * Other definitions + */ + +#define SMSC_IRCC2_MAX_SIR_SPEED 115200 +#define SMSC_IRCC2_FIR_CHIP_IO_EXTENT 8 +#define SMSC_IRCC2_SIR_CHIP_IO_EXTENT 8 +#define SMSC_IRCC2_FIFO_SIZE 16 +#define SMSC_IRCC2_FIFO_THRESHOLD 64 +/* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */ +#define SMSC_IRCC2_RX_BUFF_TRUESIZE 14384 +#define SMSC_IRCC2_TX_BUFF_TRUESIZE 14384 +#define SMSC_IRCC2_MIN_TURN_TIME 0x07 +#define SMSC_IRCC2_WINDOW_SIZE 0x07 +/* Maximum wait for hw transmitter to finish */ +#define SMSC_IRCC2_HW_TRANSMITTER_TIMEOUT_US 1000 /* 1 ms */ +/* Maximum wait for ATC transceiver programming to finish */ +#define SMSC_IRCC2_ATC_PROGRAMMING_TIMEOUT_JIFFIES 1 +#endif /* SMSC_IRCC2_H */ diff -u -p --new-file linux/drivers/net/irda.d1/smsc-sio.h linux/drivers/net/irda/smsc-sio.h --- linux/drivers/net/irda.d1/smsc-sio.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/irda/smsc-sio.h Tue May 13 13:18:34 2003 @@ -0,0 +1,100 @@ +#ifndef SMSC_SIO_H +#define SMSC_SIO_H + +/****************************************** + Keys. They should work with every SMsC SIO + ******************************************/ + +#define SMSCSIO_CFGACCESSKEY 0x55 +#define SMSCSIO_CFGEXITKEY 0xaa + +/***************************** + * Generic SIO Flat (!?) * + *****************************/ + +/* Register 0x0d */ +#define SMSCSIOFLAT_DEVICEID_REG 0x0d + +/* Register 0x0c */ +#define SMSCSIOFLAT_UARTMODE0C_REG 0x0c +#define SMSCSIOFLAT_UART2MODE_MASK 0x38 +#define SMSCSIOFLAT_UART2MODE_VAL_COM 0x00 +#define SMSCSIOFLAT_UART2MODE_VAL_IRDA 0x08 +#define SMSCSIOFLAT_UART2MODE_VAL_ASKIR 0x10 + +/* Register 0x25 */ +#define SMSCSIOFLAT_UART2BASEADDR_REG 0x25 + +/* Register 0x2b */ +#define SMSCSIOFLAT_FIRBASEADDR_REG 0x2b + +/* Register 0x2c */ +#define SMSCSIOFLAT_FIRDMASELECT_REG 0x2c +#define SMSCSIOFLAT_FIRDMASELECT_MASK 0x0f + +/* Register 0x28 */ +#define SMSCSIOFLAT_UARTIRQSELECT_REG 0x28 +#define SMSCSIOFLAT_UART2IRQSELECT_MASK 0x0f +#define SMSCSIOFLAT_UART1IRQSELECT_MASK 0xf0 +#define SMSCSIOFLAT_UARTIRQSELECT_VAL_NONE 0x00 + + +/********************* + * LPC47N227 * + *********************/ + +#define LPC47N227_CFGACCESSKEY 0x55 +#define LPC47N227_CFGEXITKEY 0xaa + +/* Register 0x00 */ +#define LPC47N227_FDCPOWERVALIDCONF_REG 0x00 +#define LPC47N227_FDCPOWER_MASK 0x08 +#define LPC47N227_VALID_MASK 0x80 + +/* Register 0x02 */ +#define LPC47N227_UART12POWER_REG 0x02 +#define LPC47N227_UART1POWERDOWN_MASK 0x08 +#define LPC47N227_UART2POWERDOWN_MASK 0x80 + +/* Register 0x07 */ +#define LPC47N227_APMBOOTDRIVE_REG 0x07 +#define LPC47N227_PARPORT2AUTOPWRDOWN_MASK 0x10 /* auto power down on if set */ +#define LPC47N227_UART2AUTOPWRDOWN_MASK 0x20 /* auto power down on if set */ +#define LPC47N227_UART1AUTOPWRDOWN_MASK 0x40 /* auto power down on if set */ + +/* Register 0x0c */ +#define LPC47N227_UARTMODE0C_REG 0x0c +#define LPC47N227_UART2MODE_MASK 0x38 +#define LPC47N227_UART2MODE_VAL_COM 0x00 +#define LPC47N227_UART2MODE_VAL_IRDA 0x08 +#define LPC47N227_UART2MODE_VAL_ASKIR 0x10 + +/* Register 0x0d */ +#define LPC47N227_DEVICEID_REG 0x0d +#define LPC47N227_DEVICEID_DEFVAL 0x5a + +/* Register 0x0e */ +#define LPC47N227_REVISIONID_REG 0x0e + +/* Register 0x25 */ +#define LPC47N227_UART2BASEADDR_REG 0x25 + +/* Register 0x28 */ +#define LPC47N227_UARTIRQSELECT_REG 0x28 +#define LPC47N227_UART2IRQSELECT_MASK 0x0f +#define LPC47N227_UART1IRQSELECT_MASK 0xf0 +#define LPC47N227_UARTIRQSELECT_VAL_NONE 0x00 + +/* Register 0x2b */ +#define LPC47N227_FIRBASEADDR_REG 0x2b + +/* Register 0x2c */ +#define LPC47N227_FIRDMASELECT_REG 0x2c +#define LPC47N227_FIRDMASELECT_MASK 0x0f +#define LPC47N227_FIRDMASELECT_VAL_DMA1 0x01 /* 47n227 has three dma channels */ +#define LPC47N227_FIRDMASELECT_VAL_DMA2 0x02 +#define LPC47N227_FIRDMASELECT_VAL_DMA3 0x03 +#define LPC47N227_FIRDMASELECT_VAL_NONE 0x0f + + +#endif