[IRDA]: smsc-ircc2, smcinit support for ALi ISA bridges
authorLinus Walleij <triad@df.lth.se>
Fri, 14 Apr 2006 23:03:33 +0000 (16:03 -0700)
committerDavid S. Miller <davem@davemloft.net>
Fri, 14 Apr 2006 23:03:33 +0000 (16:03 -0700)
From: Linus Walleij <triad@df.lth.se>

This patch enables support for ALi ISA bridges when we run the smcinit
code.  It is needed to properly configure some Toshiba laptops.

Signed-off-by: Samuel Ortiz <samuel.ortiz@nokia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/irda/smsc-ircc2.c

index bbcfc8ec35a1556892adf1cbe87fc7f36dc23295..58f76cefbc8394c0a82fb33eaa8119a7c7480e00 100644 (file)
@@ -225,6 +225,8 @@ 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 void __init preconfigure_ali_port(struct pci_dev *dev,
+                                        unsigned short port);
 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,
@@ -2327,9 +2329,14 @@ static int __init smsc_superio_lpc(unsigned short cfg_base)
  * 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.
+ * for ISA bridges with an LPC (Low Pin Count) controller which
+ * handles the communication with the SMSC device. After the LPC
+ * controller is initialized through PCI, the SMSC device is initialized
+ * through a dedicated port in the ISA port-mapped I/O area, this latter
+ * area is used to configure the SMSC device with default
+ * SIR and FIR I/O ports, DMA and IRQ. Different vendors have
+ * used different sets of parameters and different control port
+ * addresses making a subsystem device table necessary.
  */
 #ifdef CONFIG_PCI
 #define PCIID_VENDOR_INTEL 0x8086
@@ -2340,9 +2347,10 @@ static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __dev
                .device = 0x24cc,
                .subvendor = 0x103c,
                .subdevice = 0x088c,
-               .sir_io = 0x02f8, /* Quite certain these are the same for nc8000 as for nc6000 */
+               /* Quite certain these are the same for nc8000 as for nc6000 */
+               .sir_io = 0x02f8,
                .fir_io = 0x0130,
-               .fir_irq = 0x09,
+               .fir_irq = 0x05,
                .fir_dma = 0x03,
                .cfg_base = 0x004e,
                .preconfigure = preconfigure_through_82801,
@@ -2355,60 +2363,79 @@ static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __dev
                .subdevice = 0x0890,
                .sir_io = 0x02f8,
                .fir_io = 0x0130,
-               .fir_irq = 0x09,
+               .fir_irq = 0x05,
                .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 */
+               /* Intel 82801DB/DBL (ICH4/ICH4-L) LPC Interface Bridge */
+               .vendor = PCIID_VENDOR_INTEL,
                .device = 0x24c0,
                .subvendor = 0x1179,
-               .subdevice = 0xffff, /* 0xffff is "any", Not sure, 0x0001 or 0x0002 */
+               .subdevice = 0xffff, /* 0xffff is "any" */
                .sir_io = 0x03f8,
                .fir_io = 0x0130,
                .fir_irq = 0x07,
                .fir_dma = 0x01,
                .cfg_base = 0x002e,
                .preconfigure = preconfigure_through_82801,
-               .name = "Toshiba Satellite 2450",
+               .name = "Toshiba laptop with Intel 82801DB/DBL LPC bridge",
        },
        {
                .vendor = PCIID_VENDOR_INTEL, /* Intel 82801CAM ISA bridge */
-               .device = 0x248c, /* Some use 24cc? */
+               .device = 0x248c,
                .subvendor = 0x1179,
-               .subdevice = 0xffff, /* 0xffff is "any", Not sure, 0x0001 or 0x0002 */
+               .subdevice = 0xffff, /* 0xffff is "any" */
                .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",
+               .name = "Toshiba laptop with Intel 82801CAM ISA bridge",
        },
        {
-               .vendor = PCIID_VENDOR_ALI, /* ALi M1533/M1535 PCI to ISA Bridge [Aladdin IV/V/V+] */
+               /* 82801DBM (ICH4-M) LPC Interface Bridge */
+               .vendor = PCIID_VENDOR_INTEL,
+               .device = 0x24cc,
+               .subvendor = 0x1179,
+               .subdevice = 0xffff, /* 0xffff is "any" */
+               .sir_io = 0x03f8,
+               .fir_io = 0x0130,
+               .fir_irq = 0x03,
+               .fir_dma = 0x03,
+               .cfg_base = 0x002e,
+               .preconfigure = preconfigure_through_82801,
+               .name = "Toshiba laptop with Intel 8281DBM LPC bridge",
+       },
+       {
+               /* ALi M1533/M1535 PCI to ISA Bridge [Aladdin IV/V/V+] */
+               .vendor = PCIID_VENDOR_ALI,
                .device = 0x1533,
                .subvendor = 0x1179,
-               .subdevice = 0xffff, /* 0xffff is "any", Not sure, 0x0001 or 0x0002 */
+               .subdevice = 0xffff, /* 0xffff is "any" */
                .sir_io = 0x02e8,
                .fir_io = 0x02f8,
                .fir_irq = 0x07,
                .fir_dma = 0x03,
                .cfg_base = 0x002e,
                .preconfigure = preconfigure_through_ali,
-               .name = "Toshiba Satellite 1800",
+               .name = "Toshiba laptop with ALi ISA bridge",
        },
        { } // Terminator
 };
 
 
 /*
- * This sets up the basic SMSC parameters (FIR port, SIR port, FIR DMA, FIR IRQ)
+ * 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)
+static int __init preconfigure_smsc_chip(struct
+                                        smsc_ircc_subsystem_configuration
+                                        *conf)
 {
        unsigned short iobase = conf->cfg_base;
        unsigned char tmpbyte;
@@ -2416,7 +2443,9 @@ static int __init preconfigure_smsc_chip(struct smsc_ircc_subsystem_configuratio
        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);
+       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
@@ -2426,6 +2455,7 @@ static int __init preconfigure_smsc_chip(struct smsc_ircc_subsystem_configuratio
        tmpbyte = inb(iobase + 1);
        if (tmpbyte != (conf->sir_io >> 2) ) {
                IRDA_WARNING("ERROR: could not configure SIR ioport.\n");
+               IRDA_WARNING("Try to supply ircc_cfg argument.\n");
                return -ENXIO;
        }
 
@@ -2461,7 +2491,8 @@ static int __init preconfigure_smsc_chip(struct smsc_ircc_subsystem_configuratio
 
        outb(SMSCSIOFLAT_UARTMODE0C_REG, iobase);  // CR0C - UART mode
        tmpbyte = inb(iobase + 1);
-       tmpbyte &= ~SMSCSIOFLAT_UART2MODE_MASK | SMSCSIOFLAT_UART2MODE_VAL_IRDA;
+       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
@@ -2486,53 +2517,226 @@ static int __init preconfigure_smsc_chip(struct smsc_ircc_subsystem_configuratio
        return 0;
 }
 
-/* 82801CAM registers */
+/* 82801CAM generic registers */
 #define VID 0x00
 #define DID 0x02
-#define PIRQA_ROUT 0x60
+#define PIRQ_A_D_ROUT 0x60
+#define SIRQ_CNTL 0x64
+#define PIRQ_E_H_ROUT 0x68
 #define PCI_DMA_C 0x90
+/* LPC-specific registers */
 #define COM_DEC 0xe0
+#define GEN1_DEC 0xe4
 #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!
+ * 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)
+                                            struct
+                                            smsc_ircc_subsystem_configuration
+                                            *conf)
 {
        unsigned short tmpword;
-       int ret;
+       unsigned char tmpbyte;
 
-       IRDA_MESSAGE("Setting up the SMSC device via the 82801 controller.\n");
-       pci_write_config_byte(dev, COM_DEC, 0x10);
+       IRDA_MESSAGE("Setting up Intel 82801 controller and SMSC device\n");
+       /*
+        * Select the range for the COMA COM port (SIR)
+        * Register COM_DEC:
+        * Bit 7: reserved
+        * Bit 6-4, COMB decode range
+        * Bit 3: reserved
+        * Bit 2-0, COMA decode range
+        *
+        * Decode ranges:
+        *   000 = 0x3f8-0x3ff (COM1)
+        *   001 = 0x2f8-0x2ff (COM2)
+        *   010 = 0x220-0x227
+        *   011 = 0x228-0x22f
+        *   100 = 0x238-0x23f
+        *   101 = 0x2e8-0x2ef (COM4)
+        *   110 = 0x338-0x33f
+        *   111 = 0x3e8-0x3ef (COM3)
+        */
+       pci_read_config_byte(dev, COM_DEC, &tmpbyte);
+       tmpbyte &= 0xf8; /* mask COMA bits */
+       switch(conf->sir_io) {
+       case 0x3f8:
+               tmpbyte |= 0x00;
+               break;
+       case 0x2f8:
+               tmpbyte |= 0x01;
+               break;
+       case 0x220:
+               tmpbyte |= 0x02;
+               break;
+       case 0x228:
+               tmpbyte |= 0x03;
+               break;
+       case 0x238:
+               tmpbyte |= 0x04;
+               break;
+       case 0x2e8:
+               tmpbyte |= 0x05;
+               break;
+       case 0x338:
+               tmpbyte |= 0x06;
+               break;
+       case 0x3e8:
+               tmpbyte |= 0x07;
+               break;
+       default:
+               tmpbyte |= 0x01; /* COM2 default */
+       }
+       IRDA_DEBUG(1, "COM_DEC (write): 0x%02x\n", tmpbyte);
+       pci_write_config_byte(dev, COM_DEC, tmpbyte);
 
-       /* 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 */
+       /* Enable Low Pin Count interface */
+       pci_read_config_word(dev, LPC_EN, &tmpword);
+       /* These seem to be set up at all times,
+        * just make sure it is properly set.
+        */
+       switch(conf->cfg_base) {
+       case 0x04e:
+               tmpword |= 0x2000;
+               break;
+       case 0x02e:
+               tmpword |= 0x1000;
+               break;
+       case 0x062:
+               tmpword |= 0x0800;
+               break;
+       case 0x060:
+               tmpword |= 0x0400;
+               break;
+       default:
+               IRDA_WARNING("Uncommon I/O base address: 0x%04x\n",
+                            conf->cfg_base);
+               break;
+       }
+       tmpword &= 0xfffd; /* disable LPC COMB */
+       tmpword |= 0x0001; /* set bit 0 : enable LPC COMA addr range (GEN2) */
+       IRDA_DEBUG(1, "LPC_EN (write): 0x%04x\n", tmpword);
        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 */
+       /*
+        * Configure LPC DMA channel
+        * PCI_DMA_C bits:
+        * Bit 15-14: DMA channel 7 select
+        * Bit 13-12: DMA channel 6 select
+        * Bit 11-10: DMA channel 5 select
+        * Bit 9-8:   Reserved
+        * Bit 7-6:   DMA channel 3 select
+        * Bit 5-4:   DMA channel 2 select
+        * Bit 3-2:   DMA channel 1 select
+        * Bit 1-0:   DMA channel 0 select
+        *  00 = Reserved value
+        *  01 = PC/PCI DMA
+        *  10 = Reserved value
+        *  11 = LPC I/F DMA
+        */
+       pci_read_config_word(dev, PCI_DMA_C, &tmpword);
+       switch(conf->fir_dma) {
+       case 0x07:
+               tmpword |= 0xc000;
+               break;
+       case 0x06:
+               tmpword |= 0x3000;
+               break;
+       case 0x05:
+               tmpword |= 0x0c00;
+               break;
+       case 0x03:
+               tmpword |= 0x00c0;
+               break;
+       case 0x02:
+               tmpword |= 0x0030;
+               break;
+       case 0x01:
+               tmpword |= 0x000c;
+               break;
+       case 0x00:
+               tmpword |= 0x0003;
+               break;
+       default:
+               break; /* do not change settings */
+       }
+       IRDA_DEBUG(1, "PCI_DMA_C (write): 0x%04x\n", tmpword);
+       pci_write_config_word(dev, PCI_DMA_C, tmpword);
+
+       /*
+        * GEN2_DEC bits:
+        * Bit 15-4: Generic I/O range
+        * Bit 3-1: reserved (read as 0)
+        * Bit 0: enable GEN2 range on LPC I/F
+        */
+       tmpword = conf->fir_io & 0xfff8;
+       tmpword |= 0x0001;
+       IRDA_DEBUG(1, "GEN2_DEC (write): 0x%04x\n", tmpword);
+       pci_write_config_word(dev, GEN2_DEC, tmpword);
 
        /* Pre-configure chip */
-       ret = preconfigure_smsc_chip(conf);
+       return 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;
+/*
+ * Pre-configure a certain port on the ALi 1533 bridge.
+ * This is based on reverse-engineering since ALi does not
+ * provide any data sheet for the 1533 chip.
+ */
+static void __init preconfigure_ali_port(struct pci_dev *dev,
+                                        unsigned short port)
+{
+       unsigned char reg;
+       /* These bits obviously control the different ports */
+       unsigned char mask;
+       unsigned char tmpbyte;
+
+       switch(port) {
+       case 0x0130:
+       case 0x0178:
+               reg = 0xb0;
+               mask = 0x80;
+               break;
+       case 0x03f8:
+               reg = 0xb4;
+               mask = 0x80;
+               break;
+       case 0x02f8:
+               reg = 0xb4;
+               mask = 0x30;
+               break;
+       case 0x02e8:
+               reg = 0xb4;
+               mask = 0x08;
+               break;
+       default:
+               IRDA_ERROR("Failed to configure unsupported port on ALi 1533 bridge: 0x%04x\n", port);
+               return;
+       }
+
+       pci_read_config_byte(dev, reg, &tmpbyte);
+       /* Turn on the right bits */
+       tmpbyte |= mask;
+       pci_write_config_byte(dev, reg, tmpbyte);
+       IRDA_MESSAGE("Activated ALi 1533 ISA bridge port 0x%04x.\n", port);
+       return;
 }
 
 static int __init preconfigure_through_ali(struct pci_dev *dev,
-                                 struct smsc_ircc_subsystem_configuration *conf)
+                                          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;
+       /* Configure the two ports on the ALi 1533 */
+       preconfigure_ali_port(dev, conf->sir_io);
+       preconfigure_ali_port(dev, conf->fir_io);
+
+       /* Pre-configure chip */
+       return preconfigure_smsc_chip(conf);
 }
 
 static int __init smsc_ircc_preconfigure_subsystems(unsigned short ircc_cfg,
@@ -2552,9 +2756,10 @@ static int __init smsc_ircc_preconfigure_subsystems(unsigned short ircc_cfg,
                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.
+                * 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;
@@ -2564,13 +2769,20 @@ static int __init smsc_ircc_preconfigure_subsystems(unsigned short ircc_cfg,
                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;
+                          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));
+                               memcpy(&tmpconf, conf,
+                                      sizeof(struct smsc_ircc_subsystem_configuration));
 
-                               /* Override the default values with anything passed in as parameter */
+                               /*
+                                * Override the default values with anything
+                                * passed in as parameter
+                                */
                                if (ircc_cfg != 0)
                                        tmpconf.cfg_base = ircc_cfg;
                                if (ircc_fir != 0)