[PATCH] ITE887x SuperIO serial port support
Michael Smith
msmith at cbnco.com
Mon Feb 26 17:50:51 CST 2007
Hi all,
The current kernel has support for parallel ports on ITE887x SuperIO
devices. ITE887x devices with the same PCI ID can have various
combinations of parallel and serial ports. I've got an ITE8874, which has
2 serial and 0 parallel.
I'd like to revive Ted Wen's patch from 2003 to add support for ITE887x
serial SuperIO devices. It moves ITE887x support from
drivers/parpoprt/parport_pc.c to parport_serial.c and supports both
parallel and serial ports.
http://lkml.org/lkml/2003/6/10/374
I've cleaned it up a bit and I have something that works for me on
2.6.18.3. But, I would like some advice. In particular:
* I don't have any ITE887x devices with parallel ports. So I could be
breaking someone's parallel device and not know it.
* The patch contains an interrupt handler for ITE887x. No other device in
parport_serial.c or 8250_pci.c requires a custom interrupt handler, so
I'm a little perplexed as to why this device would.
* I'm using a very ugly way to pass information from parport_serial to
8250_pci. The code in parport_serial determines the number of serial and
parallel ports when called from parport_register(), and sticks the
number of serial ports into the static pci_parport_serial_boards[]
table. Yuck.
* There must be a better way to store the per-board stuff
(struct ite887x_private) rather than putting it in a static array.
The patch is below. I'd welcome any comments.
Thanks,
Mike
--- linux/drivers/parport/parport_pc.c.orig 2006-11-18 22:28:22.000000000 -0500
+++ linux/drivers/parport/parport_pc.c 2007-02-26 10:00:40.000000000 -0500
@@ -2396,112 +2396,6 @@ EXPORT_SYMBOL (parport_pc_unregister_por
#ifdef CONFIG_PCI
-/* ITE support maintained by Rich Liu <richliu at poorman.org> */
-static int __devinit sio_ite_8872_probe (struct pci_dev *pdev, int autoirq,
- int autodma,
- const struct parport_pc_via_data *via)
-{
- short inta_addr[6] = { 0x2A0, 0x2C0, 0x220, 0x240, 0x1E0 };
- struct resource *base_res;
- u32 ite8872set;
- u32 ite8872_lpt, ite8872_lpthi;
- u8 ite8872_irq, type;
- char *fake_name = "parport probe";
- int irq;
- int i;
-
- DPRINTK (KERN_DEBUG "sio_ite_8872_probe()\n");
-
- // make sure which one chip
- for(i = 0; i < 5; i++) {
- base_res = request_region(inta_addr[i], 0x8, fake_name);
- if (base_res) {
- int test;
- pci_write_config_dword (pdev, 0x60,
- 0xe7000000 | inta_addr[i]);
- pci_write_config_dword (pdev, 0x78,
- 0x00000000 | inta_addr[i]);
- test = inb (inta_addr[i]);
- if (test != 0xff) break;
- release_region(inta_addr[i], 0x8);
- }
- }
- if(i >= 5) {
- printk (KERN_INFO "parport_pc: cannot find ITE8872 INTA\n");
- return 0;
- }
-
- type = inb (inta_addr[i] + 0x18);
- type &= 0x0f;
-
- switch (type) {
- case 0x2:
- printk (KERN_INFO "parport_pc: ITE8871 found (1P)\n");
- ite8872set = 0x64200000;
- break;
- case 0xa:
- printk (KERN_INFO "parport_pc: ITE8875 found (1P)\n");
- ite8872set = 0x64200000;
- break;
- case 0xe:
- printk (KERN_INFO "parport_pc: ITE8872 found (2S1P)\n");
- ite8872set = 0x64e00000;
- break;
- case 0x6:
- printk (KERN_INFO "parport_pc: ITE8873 found (1S)\n");
- return 0;
- case 0x8:
- DPRINTK (KERN_DEBUG "parport_pc: ITE8874 found (2S)\n");
- return 0;
- default:
- printk (KERN_INFO "parport_pc: unknown ITE887x\n");
- printk (KERN_INFO "parport_pc: please mail 'lspci -nvv' "
- "output to Rich.Liu at ite.com.tw\n");
- return 0;
- }
-
- pci_read_config_byte (pdev, 0x3c, &ite8872_irq);
- pci_read_config_dword (pdev, 0x1c, &ite8872_lpt);
- ite8872_lpt &= 0x0000ff00;
- pci_read_config_dword (pdev, 0x20, &ite8872_lpthi);
- ite8872_lpthi &= 0x0000ff00;
- pci_write_config_dword (pdev, 0x6c, 0xe3000000 | ite8872_lpt);
- pci_write_config_dword (pdev, 0x70, 0xe3000000 | ite8872_lpthi);
- pci_write_config_dword (pdev, 0x80, (ite8872_lpthi<<16) | ite8872_lpt);
- // SET SPP&EPP , Parallel Port NO DMA , Enable All Function
- // SET Parallel IRQ
- pci_write_config_dword (pdev, 0x9c,
- ite8872set | (ite8872_irq * 0x11111));
-
- DPRINTK (KERN_DEBUG "ITE887x: The IRQ is %d.\n", ite8872_irq);
- DPRINTK (KERN_DEBUG "ITE887x: The PARALLEL I/O port is 0x%x.\n",
- ite8872_lpt);
- DPRINTK (KERN_DEBUG "ITE887x: The PARALLEL I/O porthi is 0x%x.\n",
- ite8872_lpthi);
-
- /* Let the user (or defaults) steer us away from interrupts */
- irq = ite8872_irq;
- if (autoirq != PARPORT_IRQ_AUTO)
- irq = PARPORT_IRQ_NONE;
-
- /*
- * Release the resource so that parport_pc_probe_port can get it.
- */
- release_resource(base_res);
- if (parport_pc_probe_port (ite8872_lpt, ite8872_lpthi,
- irq, PARPORT_DMA_NONE, NULL)) {
- printk (KERN_INFO
- "parport_pc: ITE 8872 parallel port: io=0x%X",
- ite8872_lpt);
- if (irq != PARPORT_IRQ_NONE)
- printk (", irq=%d", irq);
- printk ("\n");
- return 1;
- }
-
- return 0;
-}
-
/* VIA 8231 support by Pavel Fedin <sonic_amiga at rambler.ru>
based on VIA 686a support code by Jeff Garzik <jgarzik at pobox.com> */
static int __devinitdata parport_init_mode = 0;
@@ -2692,7 +2586,6 @@ static int __devinit sio_via_probe (stru
enum parport_pc_sio_types {
sio_via_686a = 0, /* Via VT82C686A motherboard Super I/O */
sio_via_8231, /* Via VT8231 south bridge integrated Super IO */
- sio_ite_8872,
last_sio
};
@@ -2704,7 +2597,6 @@ static struct parport_pc_superio {
} parport_pc_superio_info[] __devinitdata = {
{ sio_via_probe, &via_686a_data, },
{ sio_via_probe, &via_8231_data, },
- { sio_ite_8872_probe, NULL, },
};
enum parport_pc_pci_cards {
@@ -2837,8 +2729,6 @@ static const struct pci_device_id parpor
/* Super-IO onboard chips */
{ 0x1106, 0x0686, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sio_via_686a },
{ 0x1106, 0x8231, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sio_via_8231 },
- { PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8872,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, sio_ite_8872 },
/* PCI cards */
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1P_10x,
--- linux/drivers/parport/parport_serial.c.orig 2006-11-18 22:28:22.000000000 -0500
+++ linux/drivers/parport/parport_serial.c 2007-02-26 15:38:19.000000000 -0500
@@ -24,6 +24,7 @@
#include <linux/parport.h>
#include <linux/parport_pc.h>
#include <linux/8250_pci.h>
+#include <linux/interrupt.h>
enum parport_pc_pci_cards {
titan_110l = 0,
@@ -38,6 +39,7 @@
siig_2p1s_20x,
siig_1s1p_20x,
siig_2s1p_20x,
+ ite_887x,
};
/* each element directly indexed from enum list, above */
@@ -60,6 +62,9 @@
* is non-zero we couldn't use any of the ports. */
void (*postinit_hook) (struct pci_dev *pdev,
struct parport_pc_pci *card, int failed);
+
+ /* If set, this is called after deregistering ports. */
+ void (*exit) (struct pci_dev *dev);
};
static int __devinit netmos_parallel_init(struct pci_dev *dev, struct parport_pc_pci *card, int autoirq, int autodma)
@@ -73,6 +78,51 @@
return 0;
}
+struct ite887x_private {
+ struct pci_dev * dev;
+ u32 intc;
+ struct resource * inta;
+};
+static struct ite887x_private ite887x[8];
+static int ite_count = 0;
+
+static irqreturn_t ite887x_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+ u8 itmp;
+ int i;
+
+ for (i = 0; i < ite_count; ++i) {
+ if (dev_id == ite887x[i].dev) {
+ /* clean interrupt */
+ itmp = inb(ite887x[i].intc + 2);
+ if (itmp & 0xf) {
+ outb(itmp, ite887x[i].intc + 2);
+ }
+ break;
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+static int __devinit ite887x_preinit(struct pci_dev *dev,
+ struct parport_pc_pci *card,
+ int autoirq, int autodma);
+
+static void __devexit ite887x_exit(struct pci_dev *dev)
+{
+ int i;
+
+ free_irq(dev->irq, dev);
+
+ for (i = 0; i < ite_count; ++i) {
+ if (dev == ite887x[i].dev) {
+ release_resource(ite887x[i].inta);
+ ite887x[i].dev = NULL;
+ break;
+ }
+ }
+}
+
static struct parport_pc_pci cards[] __devinitdata = {
/* titan_110l */ { 1, { { 3, -1 }, } },
/* titan_210l */ { 1, { { 3, -1 }, } },
@@ -86,6 +136,8 @@
/* siig_2p1s_20x */ { 2, { { 1, 2 }, { 3, 4 }, } },
/* siig_1s1p_20x */ { 1, { { 1, 2 }, } },
/* siig_2s1p_20x */ { 1, { { 2, 3 }, } },
+ /* ite_887x */ { 0, { { 3, 4 }, }, ite887x_preinit,
+ NULL, __devexit_p(ite887x_exit) },
};
static struct pci_device_id parport_serial_pci_tbl[] = {
@@ -153,6 +205,8 @@
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x },
+ { PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8872,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ite_887x },
{ 0, } /* terminate list */
};
@@ -238,8 +292,157 @@
.base_baud = 921600,
.uart_offset = 8,
},
+ [ite_887x] = {
+ .flags = FL_BASE1 | FL_BASE_BARS,
+ .num_ports = 0, /* set in ite887x_preinit */
+ .base_baud = 115200,
+ .uart_offset = 8, /* ignored when FL_BASE_BARS? */
+ },
};
+
+static int __devinit ite887x_preinit(struct pci_dev *dev,
+ struct parport_pc_pci *card,
+ int autoirq, int autodma)
+{
+ static const short inta_addr[] = {
+ 0x2a0, 0x2c0, 0x220, 0x240, 0x1e0, 0x200, 0x280, 0
+ };
+ char * name = "ite887x";
+
+ int numserial = 0, numparallel = 0;
+ int j;
+ u32 prev60, prev78, setting, intc_addr;
+ struct resource * inta_resource = NULL;
+
+ pci_read_config_dword(dev, 0x60, &prev60);
+ pci_read_config_dword(dev, 0x78, &prev78);
+
+ for (j = 0; inta_addr[j]; ++j) {
+ inta_resource = request_region(inta_addr[j], 8, name);
+ if (inta_resource) {
+ pci_write_config_dword(dev, 0x60,
+ 0xe7000000 | inta_addr[j]);
+ pci_write_config_dword(dev, 0x78, inta_addr[j]);
+ if (inb(inta_addr[j]) != 0xff)
+ break;
+ release_resource(inta_resource);
+ }
+ }
+ if (!inta_addr[j]) {
+ printk(KERN_INFO "%s: can't find INTA\n", name);
+ goto errout;
+ }
+
+ switch (inb(inta_addr[j] + 0x18) & 0x0f) {
+ case 0x2:
+ name = "ite8871";
+ setting = 0x64200000;
+ numserial = 0;
+ numparallel = 1;
+ break;
+
+ case 0xa:
+ name = "ite8875";
+ setting = 0x64200000;
+ numserial = 0;
+ numparallel = 1;
+ break;
+
+ case 0xe:
+ name = "ite8872";
+ setting = 0x64e00000;
+ numserial = 2;
+ numparallel = 1;
+ break;
+
+ case 0x6:
+ name = "ite8873";
+ setting = 0x64800000;
+ numserial = 1;
+ numparallel = 0;
+ break;
+
+ case 0x8:
+ name = "ite8874";
+ setting = 0x64c00000;
+ numserial = 2;
+ numparallel = 0;
+ break;
+
+ default:
+ printk("%s: unknown device type\n", name);
+ goto errout;
+ }
+ printk(KERN_INFO "%s: %d serial, %d parallel\n",
+ name, numserial, numparallel);
+ rename_region(inta_resource, name);
+
+ card->numports = numparallel;
+
+ /*
+ * Store the serial port count so 8250_pci can find it.
+ * This means we rely on parport_register() being called before
+ * serial_register().
+ */
+ pci_parport_serial_boards[ite_887x].num_ports = numserial;
+
+ /* Find INTC. */
+ pci_read_config_dword(dev, 0x10, &intc_addr);
+ intc_addr &= 0x0000ff00;
+ pci_write_config_dword(dev, 0x60, 0xe5000000 | intc_addr);
+ pci_write_config_dword(dev, 0x78, intc_addr);
+
+ /* Set up serial ports. */
+ if (numserial > 0) {
+ u32 com1, com2 = 0;
+ pci_read_config_dword(dev, 0x14, &com1);
+ if (numserial > 1) {
+ pci_read_config_dword(dev, 0x18, &com2);
+ com2 &= 0x0000ff00;
+ pci_write_config_dword(dev, 0x68, 0xe3000000 | com2);
+ }
+ pci_write_config_dword(dev, 0x7c, (com2 << 16) | com1);
+ }
+
+ /* Set up parallel ports. */
+ if (numparallel > 0) {
+ u32 lpt, lpthi;
+ pci_read_config_dword(dev, 0x1c, &lpt);
+ pci_read_config_dword(dev, 0x20, &lpthi);
+ lpt &= 0x0000ff00;
+ lpthi &= 0x0000ff00;
+
+ pci_write_config_dword(dev, 0x6c, 0xe3000000 | lpt);
+ pci_write_config_dword(dev, 0x70, 0xe3000000 | lpthi);
+ pci_write_config_dword(dev, 0x80, (lpthi << 16) | lpt);
+ }
+
+ /* The irq and remove handlers use these to find their way around. */
+ ite887x[ite_count].dev = dev;
+ ite887x[ite_count].intc = intc_addr;
+ ite887x[ite_count].inta = inta_resource;
+
+ if (request_irq(dev->irq, ite887x_irq, IRQF_SHARED, name, dev) != 0) {
+ printk(KERN_INFO "%s: can't assign irq 0x%x\n", name, dev->irq);
+ goto errout;
+ }
+
+ /* 9C write enable UART, IRQ function */
+ pci_write_config_dword(dev, 0x9c, setting | (0x11111 * dev->irq));
+
+ ++ite_count;
+ return 0;
+
+errout:
+ if (inta_resource)
+ release_resource(inta_resource);
+ pci_write_config_dword(dev, 0x60, prev60);
+ pci_write_config_dword(dev, 0x78, prev78);
+ return -ENODEV;
+}
+
+
struct parport_serial_private {
struct serial_private *serial;
int num_par;
@@ -370,6 +573,9 @@
for (i = 0; i < priv->num_par; i++)
parport_pc_unregister_port (priv->port[i]);
+ if (priv->par.exit)
+ priv->par.exit(dev);
+
kfree (priv);
return;
}
More information about the Kernel-mentors
mailing list