]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/net/irda/smsc-ircc2.c
[IRDA]: smcinit merged into smsc-ircc driver
[mv-sheeva.git] / drivers / net / irda / smsc-ircc2.c
index dd89bda1f1315914349fe692029108b30dd6262a..bbcfc8ec35a1556892adf1cbe87fc7f36dc23295 100644 (file)
@@ -11,6 +11,7 @@
  *     Copyright (c) 2002      Daniele Peri
  *     All Rights Reserved.
  *     Copyright (c) 2002      Jean Tourrilhes
+ *     Copyright (c) 2006      Linus Walleij
  *
  *
  * Based on smc-ircc.c:
@@ -53,6 +54,7 @@
 #include <linux/rtnetlink.h>
 #include <linux/serial_reg.h>
 #include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
 
 #include <asm/io.h>
 #include <asm/dma.h>
@@ -60,6 +62,9 @@
 
 #include <linux/spinlock.h>
 #include <linux/pm.h>
+#ifdef CONFIG_PCI
+#include <linux/pci.h>
+#endif
 
 #include <net/irda/wrapper.h>
 #include <net/irda/irda.h>
@@ -99,6 +104,22 @@ MODULE_PARM_DESC(ircc_transceiver, "Transceiver type");
 
 /* Types */
 
+#ifdef CONFIG_PCI
+struct smsc_ircc_subsystem_configuration {
+       unsigned short vendor; /* PCI vendor ID */
+       unsigned short device; /* PCI vendor ID */
+       unsigned short subvendor; /* PCI subsystem vendor ID */
+       unsigned short subdevice; /* PCI sybsystem device ID */
+       unsigned short sir_io; /* I/O port for SIR */
+       unsigned short fir_io; /* I/O port for FIR */
+       unsigned char  fir_irq; /* FIR IRQ */
+       unsigned char  fir_dma; /* FIR DMA */
+       unsigned short cfg_base; /* I/O port for chip configuration */
+       int (*preconfigure)(struct pci_dev *dev, struct smsc_ircc_subsystem_configuration *conf); /* Preconfig function */
+       const char *name;       /* name shown as info */
+};
+#endif
+
 struct smsc_transceiver {
        char *name;
        void (*set_for_speed)(int fir_base, u32 speed);
@@ -201,6 +222,16 @@ static int __init smsc_superio_flat(const struct smsc_chip *chips, unsigned shor
 static int __init smsc_superio_paged(const struct smsc_chip *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);
+#ifdef CONFIG_PCI
+static int __init preconfigure_smsc_chip(struct smsc_ircc_subsystem_configuration *conf);
+static int __init preconfigure_through_82801(struct pci_dev *dev, struct smsc_ircc_subsystem_configuration *conf);
+static int __init preconfigure_through_ali(struct pci_dev *dev, struct smsc_ircc_subsystem_configuration *conf);
+static int __init smsc_ircc_preconfigure_subsystems(unsigned short ircc_cfg,
+                                                   unsigned short ircc_fir,
+                                                   unsigned short ircc_sir,
+                                                   unsigned char ircc_dma,
+                                                   unsigned char ircc_irq);
+#endif
 
 /* Transceivers specific functions */
 
@@ -213,14 +244,15 @@ static int  smsc_ircc_probe_transceiver_smsc_ircc_atc(int fir_base);
 
 /* Power Management */
 
-static int smsc_ircc_suspend(struct device *dev, pm_message_t state, u32 level);
-static int smsc_ircc_resume(struct device *dev, u32 level);
+static int smsc_ircc_suspend(struct platform_device *dev, pm_message_t state);
+static int smsc_ircc_resume(struct platform_device *dev);
 
-static struct device_driver smsc_ircc_driver = {
-       .name           = SMSC_IRCC2_DRIVER_NAME,
-       .bus            = &platform_bus_type,
+static struct platform_driver smsc_ircc_driver = {
        .suspend        = smsc_ircc_suspend,
        .resume         = smsc_ircc_resume,
+       .driver         = {
+               .name   = SMSC_IRCC2_DRIVER_NAME,
+       },
 };
 
 /* Transceivers for SMSC-ircc */
@@ -345,12 +377,19 @@ static int __init smsc_ircc_init(void)
 
        IRDA_DEBUG(1, "%s\n", __FUNCTION__);
 
-       ret = driver_register(&smsc_ircc_driver);
+       ret = platform_driver_register(&smsc_ircc_driver);
        if (ret) {
                IRDA_ERROR("%s, Can't register driver!\n", driver_name);
                return ret;
        }
 
+#ifdef CONFIG_PCI
+       if (smsc_ircc_preconfigure_subsystems(ircc_cfg, ircc_fir, ircc_sir, ircc_dma, ircc_irq) < 0) {
+               /* Ignore errors from preconfiguration */
+               IRDA_ERROR("%s, Preconfiguration failed !\n", driver_name);
+       }
+#endif
+
        dev_count = 0;
 
        if (ircc_fir > 0 && ircc_sir > 0) {
@@ -377,7 +416,7 @@ static int __init smsc_ircc_init(void)
        }
 
        if (ret)
-               driver_unregister(&smsc_ircc_driver);
+               platform_driver_unregister(&smsc_ircc_driver);
 
        return ret;
 }
@@ -490,7 +529,7 @@ static int __init smsc_ircc_open(unsigned int fir_base, unsigned int sir_base, u
                err = PTR_ERR(self->pldev);
                goto err_out5;
        }
-       dev_set_drvdata(&self->pldev->dev, self);
+       platform_set_drvdata(self->pldev, self);
 
        IRDA_MESSAGE("IrDA: Registered device %s\n", dev->name);
        dev_count++;
@@ -638,21 +677,14 @@ static void smsc_ircc_setup_qos(struct smsc_ircc_cb *self)
  */
 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;
+       int iobase = self->io.fir_base;
 
        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),
+       outb(((inb(iobase + IRCC_SCE_CFGA) & 0x87) | IRCC_CFGA_IRDA_SIR_A),
             iobase + IRCC_SCE_CFGA);
 
 #ifdef smsc_669 /* Uses pin 88/89 for Rx/Tx */
@@ -666,10 +698,10 @@ static void smsc_ircc_init_chip(struct smsc_ircc_cb *self)
        outb(SMSC_IRCC2_FIFO_THRESHOLD, iobase + IRCC_FIFO_THRESHOLD);
 
        register_bank(iobase, 4);
-       outb((inb(iobase + IRCC_CONTROL) & 0x30) | ctrl, iobase + IRCC_CONTROL);
+       outb((inb(iobase + IRCC_CONTROL) & 0x30), iobase + IRCC_CONTROL);
 
        register_bank(iobase, 0);
-       outb(fast, iobase + IRCC_LCR_A);
+       outb(0, iobase + IRCC_LCR_A);
 
        smsc_ircc_set_sir_speed(self, SMSC_IRCC2_C_IRDA_FALLBACK_SPEED);
 
@@ -1556,6 +1588,46 @@ static int ircc_is_receiving(struct smsc_ircc_cb *self)
 }
 #endif /* unused */
 
+static int smsc_ircc_request_irq(struct smsc_ircc_cb *self)
+{
+       int error;
+
+       error = request_irq(self->io.irq, smsc_ircc_interrupt, 0,
+                           self->netdev->name, self->netdev);
+       if (error)
+               IRDA_DEBUG(0, "%s(), unable to allocate irq=%d, err=%d\n",
+                          __FUNCTION__, self->io.irq, error);
+
+       return error;
+}
+
+static void smsc_ircc_start_interrupts(struct smsc_ircc_cb *self)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&self->lock, flags);
+
+       self->io.speed = 0;
+       smsc_ircc_change_speed(self, SMSC_IRCC2_C_IRDA_FALLBACK_SPEED);
+
+       spin_unlock_irqrestore(&self->lock, flags);
+}
+
+static void smsc_ircc_stop_interrupts(struct smsc_ircc_cb *self)
+{
+       int iobase = self->io.fir_base;
+       unsigned long flags;
+
+       spin_lock_irqsave(&self->lock, flags);
+
+       register_bank(iobase, 0);
+       outb(0, iobase + IRCC_IER);
+       outb(IRCC_MASTER_RESET, iobase + IRCC_MASTER);
+       outb(0x00, iobase + IRCC_MASTER);
+
+       spin_unlock_irqrestore(&self->lock, flags);
+}
+
 
 /*
  * Function smsc_ircc_net_open (dev)
@@ -1567,7 +1639,6 @@ static int smsc_ircc_net_open(struct net_device *dev)
 {
        struct smsc_ircc_cb *self;
        char hwname[16];
-       unsigned long flags;
 
        IRDA_DEBUG(1, "%s\n", __FUNCTION__);
 
@@ -1575,6 +1646,11 @@ static int smsc_ircc_net_open(struct net_device *dev)
        self = netdev_priv(dev);
        IRDA_ASSERT(self != NULL, return 0;);
 
+       if (self->io.suspended) {
+               IRDA_DEBUG(0, "%s(), device is suspended\n", __FUNCTION__);
+               return -EAGAIN;
+       }
+
        if (request_irq(self->io.irq, smsc_ircc_interrupt, 0, dev->name,
                        (void *) dev)) {
                IRDA_DEBUG(0, "%s(), unable to allocate irq=%d\n",
@@ -1582,11 +1658,7 @@ static int smsc_ircc_net_open(struct net_device *dev)
                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);
+       smsc_ircc_start_interrupts(self);
 
        /* Give self a hardware name */
        /* It would be cool to offer the chip revision here - Jean II */
@@ -1639,37 +1711,63 @@ static int smsc_ircc_net_close(struct net_device *dev)
                irlap_close(self->irlap);
        self->irlap = NULL;
 
-       free_irq(self->io.irq, dev);
+       smsc_ircc_stop_interrupts(self);
+
+       /* if we are called from smsc_ircc_resume we don't have IRQ reserved */
+       if (!self->io.suspended)
+               free_irq(self->io.irq, dev);
+
        disable_dma(self->io.dma);
        free_dma(self->io.dma);
 
        return 0;
 }
 
-static int smsc_ircc_suspend(struct device *dev, pm_message_t state, u32 level)
+static int smsc_ircc_suspend(struct platform_device *dev, pm_message_t state)
 {
-       struct smsc_ircc_cb *self = dev_get_drvdata(dev);
+       struct smsc_ircc_cb *self = platform_get_drvdata(dev);
 
-       IRDA_MESSAGE("%s, Suspending\n", driver_name);
+       if (!self->io.suspended) {
+               IRDA_DEBUG(1, "%s, Suspending\n", driver_name);
 
-       if (level == SUSPEND_DISABLE && !self->io.suspended) {
-               smsc_ircc_net_close(self->netdev);
+               rtnl_lock();
+               if (netif_running(self->netdev)) {
+                       netif_device_detach(self->netdev);
+                       smsc_ircc_stop_interrupts(self);
+                       free_irq(self->io.irq, self->netdev);
+                       disable_dma(self->io.dma);
+               }
                self->io.suspended = 1;
+               rtnl_unlock();
        }
 
        return 0;
 }
 
-static int smsc_ircc_resume(struct device *dev, u32 level)
+static int smsc_ircc_resume(struct platform_device *dev)
 {
-       struct smsc_ircc_cb *self = dev_get_drvdata(dev);
-
-       if (level == RESUME_ENABLE && self->io.suspended) {
-
-               smsc_ircc_net_open(self->netdev);
+       struct smsc_ircc_cb *self = platform_get_drvdata(dev);
+
+       if (self->io.suspended) {
+               IRDA_DEBUG(1, "%s, Waking up\n", driver_name);
+
+               rtnl_lock();
+               smsc_ircc_init_chip(self);
+               if (netif_running(self->netdev)) {
+                       if (smsc_ircc_request_irq(self)) {
+                               /*
+                                * Don't fail resume process, just kill this
+                                * network interface
+                                */
+                               unregister_netdevice(self->netdev);
+                       } else {
+                               enable_dma(self->io.dma);
+                               smsc_ircc_start_interrupts(self);
+                               netif_device_attach(self->netdev);
+                       }
+               }
                self->io.suspended = 0;
-
-               IRDA_MESSAGE("%s, Waking up\n", driver_name);
+               rtnl_unlock();
        }
        return 0;
 }
@@ -1682,9 +1780,6 @@ static int smsc_ircc_resume(struct device *dev, u32 level)
  */
 static int __exit smsc_ircc_close(struct smsc_ircc_cb *self)
 {
-       int iobase;
-       unsigned long flags;
-
        IRDA_DEBUG(1, "%s\n", __FUNCTION__);
 
        IRDA_ASSERT(self != NULL, return -1;);
@@ -1694,22 +1789,7 @@ static int __exit smsc_ircc_close(struct smsc_ircc_cb *self)
        /* Remove netdevice */
        unregister_netdev(self->netdev);
 
-       /* Make sure the irq handler is not exectuting */
-       spin_lock_irqsave(&self->lock, flags);
-
-       /* Stop interrupts */
-       iobase = self->io.fir_base;
-       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);
+       smsc_ircc_stop_interrupts(self);
 
        /* Release the PORTS that this driver is using */
        IRDA_DEBUG(0, "%s(), releasing 0x%03x\n",  __FUNCTION__,
@@ -1746,7 +1826,7 @@ static void __exit smsc_ircc_cleanup(void)
                        smsc_ircc_close(dev_self[i]);
        }
 
-       driver_unregister(&smsc_ircc_driver);
+       platform_driver_unregister(&smsc_ircc_driver);
 }
 
 /*
@@ -2242,6 +2322,280 @@ static int __init smsc_superio_lpc(unsigned short cfg_base)
        return ret;
 }
 
+/*
+ * Look for some specific subsystem setups that need
+ * pre-configuration not properly done by the BIOS (especially laptops)
+ * This code is based in part on smcinit.c, tosh1800-smcinit.c
+ * and tosh2450-smcinit.c. The table lists the device entries
+ * for ISA bridges with an LPC (Local Peripheral Configurator)
+ * that are in turn used to configure the SMSC device with default
+ * SIR and FIR I/O ports, DMA and IRQ.
+ */
+#ifdef CONFIG_PCI
+#define PCIID_VENDOR_INTEL 0x8086
+#define PCIID_VENDOR_ALI 0x10b9
+static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __devinitdata = {
+       {
+               .vendor = PCIID_VENDOR_INTEL, /* Intel 82801DBM LPC bridge */
+               .device = 0x24cc,
+               .subvendor = 0x103c,
+               .subdevice = 0x088c,
+               .sir_io = 0x02f8, /* Quite certain these are the same for nc8000 as for nc6000 */
+               .fir_io = 0x0130,
+               .fir_irq = 0x09,
+               .fir_dma = 0x03,
+               .cfg_base = 0x004e,
+               .preconfigure = preconfigure_through_82801,
+               .name = "HP nc8000",
+       },
+       {
+               .vendor = PCIID_VENDOR_INTEL, /* Intel 82801DBM LPC bridge */
+               .device = 0x24cc,
+               .subvendor = 0x103c,
+               .subdevice = 0x0890,
+               .sir_io = 0x02f8,
+               .fir_io = 0x0130,
+               .fir_irq = 0x09,
+               .fir_dma = 0x03,
+               .cfg_base = 0x004e,
+               .preconfigure = preconfigure_through_82801,
+               .name = "HP nc6000",
+       },
+       {
+               .vendor = PCIID_VENDOR_INTEL, /* Intel 82801DB/DBL (ICH4/ICH4-L) LPC Interface Bridge */
+               .device = 0x24c0,
+               .subvendor = 0x1179,
+               .subdevice = 0xffff, /* 0xffff is "any", Not sure, 0x0001 or 0x0002 */
+               .sir_io = 0x03f8,
+               .fir_io = 0x0130,
+               .fir_irq = 0x07,
+               .fir_dma = 0x01,
+               .cfg_base = 0x002e,
+               .preconfigure = preconfigure_through_82801,
+               .name = "Toshiba Satellite 2450",
+       },
+       {
+               .vendor = PCIID_VENDOR_INTEL, /* Intel 82801CAM ISA bridge */
+               .device = 0x248c, /* Some use 24cc? */
+               .subvendor = 0x1179,
+               .subdevice = 0xffff, /* 0xffff is "any", Not sure, 0x0001 or 0x0002 */
+               .sir_io = 0x03f8,
+               .fir_io = 0x0130,
+               .fir_irq = 0x03,
+               .fir_dma = 0x03,
+               .cfg_base = 0x002e,
+               .preconfigure = preconfigure_through_82801,
+               .name = "Toshiba Satellite 5100/5200, Tecra 9100",
+       },
+       {
+               .vendor = PCIID_VENDOR_ALI, /* ALi M1533/M1535 PCI to ISA Bridge [Aladdin IV/V/V+] */
+               .device = 0x1533,
+               .subvendor = 0x1179,
+               .subdevice = 0xffff, /* 0xffff is "any", Not sure, 0x0001 or 0x0002 */
+               .sir_io = 0x02e8,
+               .fir_io = 0x02f8,
+               .fir_irq = 0x07,
+               .fir_dma = 0x03,
+               .cfg_base = 0x002e,
+               .preconfigure = preconfigure_through_ali,
+               .name = "Toshiba Satellite 1800",
+       },
+       { } // Terminator
+};
+
+
+/*
+ * This sets up the basic SMSC parameters (FIR port, SIR port, FIR DMA, FIR IRQ)
+ * through the chip configuration port.
+ */
+static int __init preconfigure_smsc_chip(struct smsc_ircc_subsystem_configuration *conf)
+{
+       unsigned short iobase = conf->cfg_base;
+       unsigned char tmpbyte;
+
+       outb(LPC47N227_CFGACCESSKEY, iobase); // enter configuration state
+       outb(SMSCSIOFLAT_DEVICEID_REG, iobase); // set for device ID
+       tmpbyte = inb(iobase +1); // Read device ID
+       IRDA_DEBUG(0, "Detected Chip id: 0x%02x, setting up registers...\n",tmpbyte);
+
+       /* Disable UART1 and set up SIR I/O port */
+       outb(0x24, iobase);  // select CR24 - UART1 base addr
+       outb(0x00, iobase + 1); // disable UART1
+       outb(SMSCSIOFLAT_UART2BASEADDR_REG, iobase);  // select CR25 - UART2 base addr
+       outb( (conf->sir_io >> 2), iobase + 1); // bits 2-9 of 0x3f8
+       tmpbyte = inb(iobase + 1);
+       if (tmpbyte != (conf->sir_io >> 2) ) {
+               IRDA_WARNING("ERROR: could not configure SIR ioport.\n");
+               return -ENXIO;
+       }
+
+       /* Set up FIR IRQ channel for UART2 */
+       outb(SMSCSIOFLAT_UARTIRQSELECT_REG, iobase); // select CR28 - UART1,2 IRQ select
+       tmpbyte = inb(iobase + 1);
+       tmpbyte &= SMSCSIOFLAT_UART1IRQSELECT_MASK; // Do not touch the UART1 portion
+       tmpbyte |= (conf->fir_irq & SMSCSIOFLAT_UART2IRQSELECT_MASK);
+       outb(tmpbyte, iobase + 1);
+       tmpbyte = inb(iobase + 1) & SMSCSIOFLAT_UART2IRQSELECT_MASK;
+       if (tmpbyte != conf->fir_irq) {
+               IRDA_WARNING("ERROR: could not configure FIR IRQ channel.\n");
+               return -ENXIO;
+       }
+
+       /* Set up FIR I/O port */
+       outb(SMSCSIOFLAT_FIRBASEADDR_REG, iobase);  // CR2B - SCE (FIR) base addr
+       outb((conf->fir_io >> 3), iobase + 1);
+       tmpbyte = inb(iobase + 1);
+       if (tmpbyte != (conf->fir_io >> 3) ) {
+               IRDA_WARNING("ERROR: could not configure FIR I/O port.\n");
+               return -ENXIO;
+       }
+
+       /* Set up FIR DMA channel */
+       outb(SMSCSIOFLAT_FIRDMASELECT_REG, iobase);  // CR2C - SCE (FIR) DMA select
+       outb((conf->fir_dma & LPC47N227_FIRDMASELECT_MASK), iobase + 1); // DMA
+       tmpbyte = inb(iobase + 1) & LPC47N227_FIRDMASELECT_MASK;
+       if (tmpbyte != (conf->fir_dma & LPC47N227_FIRDMASELECT_MASK)) {
+               IRDA_WARNING("ERROR: could not configure FIR DMA channel.\n");
+               return -ENXIO;
+       }
+
+       outb(SMSCSIOFLAT_UARTMODE0C_REG, iobase);  // CR0C - UART mode
+       tmpbyte = inb(iobase + 1);
+       tmpbyte &= ~SMSCSIOFLAT_UART2MODE_MASK | SMSCSIOFLAT_UART2MODE_VAL_IRDA;
+       outb(tmpbyte, iobase + 1); // enable IrDA (HPSIR) mode, high speed
+
+       outb(LPC47N227_APMBOOTDRIVE_REG, iobase);  // CR07 - Auto Pwr Mgt/boot drive sel
+       tmpbyte = inb(iobase + 1);
+       outb(tmpbyte | LPC47N227_UART2AUTOPWRDOWN_MASK, iobase + 1); // enable UART2 autopower down
+
+       /* This one was not part of tosh1800 */
+       outb(0x0a, iobase);  // CR0a - ecp fifo / ir mux
+       tmpbyte = inb(iobase + 1);
+       outb(tmpbyte | 0x40, iobase + 1); // send active device to ir port
+
+       outb(LPC47N227_UART12POWER_REG, iobase);  // CR02 - UART 1,2 power
+       tmpbyte = inb(iobase + 1);
+       outb(tmpbyte | LPC47N227_UART2POWERDOWN_MASK, iobase + 1); // UART2 power up mode, UART1 power down
+
+       outb(LPC47N227_FDCPOWERVALIDCONF_REG, iobase);  // CR00 - FDC Power/valid config cycle
+       tmpbyte = inb(iobase + 1);
+       outb(tmpbyte | LPC47N227_VALID_MASK, iobase + 1); // valid config cycle done
+
+       outb(LPC47N227_CFGEXITKEY, iobase);  // Exit configuration
+
+       return 0;
+}
+
+/* 82801CAM registers */
+#define VID 0x00
+#define DID 0x02
+#define PIRQA_ROUT 0x60
+#define PCI_DMA_C 0x90
+#define COM_DEC 0xe0
+#define LPC_EN 0xe6
+#define GEN2_DEC 0xec
+/*
+ * Sets up the I/O range using the 82801CAM ISA bridge, 82801DBM LPC bridge or
+ * Intel 82801DB/DBL (ICH4/ICH4-L) LPC Interface Bridge. They all work the same way!
+ */
+static int __init preconfigure_through_82801(struct pci_dev *dev,
+                                 struct smsc_ircc_subsystem_configuration *conf)
+{
+       unsigned short tmpword;
+       int ret;
+
+       IRDA_MESSAGE("Setting up the SMSC device via the 82801 controller.\n");
+       pci_write_config_byte(dev, COM_DEC, 0x10);
+
+       /* Enable LPC */
+       pci_read_config_word(dev, LPC_EN, &tmpword); /* LPC_EN register */
+       tmpword &= 0xfffd; /* mask bit 1 */
+       tmpword |= 0x0001; /* set bit 0 : COMA addr range enable */
+       pci_write_config_word(dev, LPC_EN, tmpword);
+
+       /* Setup DMA */
+       pci_write_config_word(dev, PCI_DMA_C, 0xc0c0); /* LPC I/F DMA on, channel 3  -- rtm (?? PCI DMA ?) */
+       pci_write_config_word(dev, GEN2_DEC, 0x131); /* LPC I/F 2nd decode range */
+
+       /* Pre-configure chip */
+       ret = preconfigure_smsc_chip(conf);
+
+       /* Disable LPC */
+       pci_read_config_word(dev, LPC_EN, &tmpword); /* LPC_EN register */
+       tmpword &= 0xfffc; /* mask bit 1 and bit 0, COMA addr range disable */
+       pci_write_config_word(dev, LPC_EN, tmpword);
+       return ret;
+}
+
+static int __init preconfigure_through_ali(struct pci_dev *dev,
+                                 struct smsc_ircc_subsystem_configuration *conf)
+{
+       /* TODO: put in ALi 1533 configuration here. */
+       IRDA_MESSAGE("SORRY: %s has an unsupported bridge controller (ALi): not pre-configured.\n", conf->name);
+       return -ENODEV;
+}
+
+static int __init smsc_ircc_preconfigure_subsystems(unsigned short ircc_cfg,
+                                                   unsigned short ircc_fir,
+                                                   unsigned short ircc_sir,
+                                                   unsigned char ircc_dma,
+                                                   unsigned char ircc_irq)
+{
+       struct pci_dev *dev = NULL;
+       unsigned short ss_vendor = 0x0000;
+       unsigned short ss_device = 0x0000;
+       int ret = 0;
+
+       dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev);
+
+       while (dev != NULL) {
+               struct smsc_ircc_subsystem_configuration *conf;
+
+               /*
+                * Cache the subsystem vendor/device: some manufacturers fail to set
+                * this for all components, so we save it in case there is just
+                * 0x0000 0x0000 on the device we want to check.
+                */
+               if (dev->subsystem_vendor != 0x0000U) {
+                       ss_vendor = dev->subsystem_vendor;
+                       ss_device = dev->subsystem_device;
+               }
+               conf = subsystem_configurations;
+               for( ; conf->subvendor; conf++) {
+                       if(conf->vendor == dev->vendor &&
+                          conf->device == dev->device &&
+                          conf->subvendor == ss_vendor && /* Sometimes these are cached values */
+                          (conf->subdevice == ss_device || conf->subdevice == 0xffff)) {
+                               struct smsc_ircc_subsystem_configuration tmpconf;
+
+                               memcpy(&tmpconf, conf, sizeof(struct smsc_ircc_subsystem_configuration));
+
+                               /* Override the default values with anything passed in as parameter */
+                               if (ircc_cfg != 0)
+                                       tmpconf.cfg_base = ircc_cfg;
+                               if (ircc_fir != 0)
+                                       tmpconf.fir_io = ircc_fir;
+                               if (ircc_sir != 0)
+                                       tmpconf.sir_io = ircc_sir;
+                               if (ircc_dma != 0xff)
+                                       tmpconf.fir_dma = ircc_dma;
+                               if (ircc_irq != 0xff)
+                                       tmpconf.fir_irq = ircc_irq;
+
+                               IRDA_MESSAGE("Detected unconfigured %s SMSC IrDA chip, pre-configuring device.\n", conf->name);
+                               if (conf->preconfigure)
+                                       ret = conf->preconfigure(dev, &tmpconf);
+                               else
+                                       ret = -ENODEV;
+                       }
+               }
+               dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev);
+       }
+
+       return ret;
+}
+#endif // CONFIG_PCI
+
 /************************************************
  *
  * Transceivers specific functions