[IRDA]: smsc-ircc2, smcinit support for ALi ISA bridges

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>
This commit is contained in:
Linus Walleij 2006-04-14 16:03:33 -07:00 committed by David S. Miller
parent 269690ac16
commit 08d099974a

View 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)