Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 08d09997 authored by Linus Walleij's avatar Linus Walleij Committed by David S. Miller
Browse files

[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: default avatarSamuel Ortiz <samuel.ortiz@nokia.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 269690ac
Loading
Loading
Loading
Loading
+264 −52
Original line number Diff line number Diff line
@@ -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" */
		.sir_io = 0x03f8,
		.fir_io = 0x0130,
		.fir_irq = 0x03,
		.fir_dma = 0x03,
		.cfg_base = 0x002e,
		.preconfigure = preconfigure_through_82801,
		.name = "Toshiba laptop with Intel 82801CAM ISA bridge",
	},
	{
		/* 82801DBM (ICH4-M) LPC Interface Bridge */
		.vendor = PCIID_VENDOR_INTEL,
		.device = 0x24cc,
		.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 8281DBM LPC bridge",
	},
	{
		.vendor = PCIID_VENDOR_ALI, /* ALi M1533/M1535 PCI to ISA Bridge [Aladdin IV/V/V+] */
		/* 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)