All of lore.kernel.org
 help / color / mirror / Atom feed
* Merging Skylark's IOC3 patch
@ 2006-02-19 21:15 ` Martin Michlmayr
  0 siblings, 0 replies; 13+ messages in thread
From: Martin Michlmayr @ 2006-02-19 21:15 UTC (permalink / raw)
  To: Ralf Baechle; +Cc: linux-mips, Stanislaw Skowronek

Ralf,

Can you please review and/or merge Skylark's IOC3 patch from
ftp://ftp.linux-mips.org/pub/linux/mips/people/skylark/linux-mips-2.6.14-ioc3-r26.patch.bz2

From my basic understanding I believe that this patch needs to be split up
and submitted to different sub-system maintainers.  However, it would be
nice if the MIPS part could get in first because the other parts depend on
this.  I'll start splitting stuff out in the meantime.

 arch/mips/Kconfig                        |    8
 arch/mips/pci/Makefile                   |    2
 arch/mips/pci/ioc3.c                     |  801 +++++++++++++++++++++++++++++++
 arch/mips/sgi-ip27/ip27-console.c        |    2
 drivers/input/serio/Kconfig              |    7
 drivers/input/serio/Makefile             |    1
 drivers/input/serio/ioc3kbd.c            |  172 ++++++
 drivers/net/Kconfig                      |    2
 drivers/net/ioc3-eth.c                   |  458 ++---------------
 drivers/serial/8250.c                    |   17
 drivers/serial/Kconfig                   |    7
 drivers/serial/Makefile                  |    1
 drivers/serial/ioc3uart.c                |  135 +++++
 drivers/serial/serial_core.c             |   12
 include/asm-mips/mach-ip27/mangle-port.h |    2
 include/linux/ioc3.h                     |   87 +++
 include/linux/serial.h                   |    1
 include/linux/serial_core.h              |    1
 18 files changed, 1304 insertions(+), 412 deletions(-)

-- 
Martin Michlmayr
http://www.cyrius.com/

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Merging Skylark's IOC3 patch
@ 2006-02-19 21:15 ` Martin Michlmayr
  0 siblings, 0 replies; 13+ messages in thread
From: Martin Michlmayr @ 2006-02-19 21:15 UTC (permalink / raw)
  To: Ralf Baechle; +Cc: linux-mips, Stanislaw Skowronek

Ralf,

Can you please review and/or merge Skylark's IOC3 patch from
ftp://ftp.linux-mips.org/pub/linux/mips/people/skylark/linux-mips-2.6.14-ioc3-r26.patch.bz2

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Merging Skylark's IOC3 patch
  2006-02-19 21:15 ` Martin Michlmayr
  (?)
@ 2006-02-19 21:54 ` Martin Michlmayr
  2006-02-19 22:04   ` Martin Michlmayr
  -1 siblings, 1 reply; 13+ messages in thread
From: Martin Michlmayr @ 2006-02-19 21:54 UTC (permalink / raw)
  To: Ralf Baechle; +Cc: linux-mips, Stanislaw Skowronek

* Martin Michlmayr <tbm@cyrius.com> [2006-02-19 21:15]:
> Can you please review and/or merge Skylark's IOC3 patch from
> ftp://ftp.linux-mips.org/pub/linux/mips/people/skylark/linux-mips-2.6.14-ioc3-r26.patch.bz2
> 
> From my basic understanding I believe that this patch needs to be split up
> and submitted to different sub-system maintainers.


From: Stanislaw Skowronek <skylark@linux-mips.org>

[PATCH 1/6] [MIPS] Improved IOC3 driver

An an improved driver for the IOC3 bridge, used in SGI Origin and Octane.

Signed-off-by: Stanislaw Skowronek <skylark@linux-mips.org>
Signed-off-by: Martin Michlmayr <tbm@cyrius.com>

---

 Kconfig      |    8
 pci/Makefile |    2
 pci/ioc3.c   |  801 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 811 insertions(+)

diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 4ca93ff..3c5cc08 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -1663,6 +1663,14 @@ config PCI_DOMAINS
 
 source "drivers/pci/Kconfig"
 
+config SGI_IOC3
+	bool "SGI IOC3 Master Driver"
+	depends on PCI
+	help
+	  If you have a Silicon Graphics Origin or Octane, say Y.
+	  This driver provides base for IOC3 feature drivers, such as
+	  Ethernet, keyboard, mouse, serial ports, LEDs and RTC.
+
 #
 # ISA support is now enabled via select.  Too many systems still have the one
 # or other ISA chip on the board that users don't know about so don't expect
diff --git a/arch/mips/pci/Makefile b/arch/mips/pci/Makefile
index 16205b5..8e0456b 100644
--- a/arch/mips/pci/Makefile
+++ b/arch/mips/pci/Makefile
@@ -57,3 +57,5 @@ obj-$(CONFIG_TOSHIBA_RBTX4927)	+= fixup-
 obj-$(CONFIG_TOSHIBA_RBTX4938)	+= fixup-tx4938.o ops-tx4938.o
 obj-$(CONFIG_VICTOR_MPC30X)	+= fixup-mpc30x.o
 obj-$(CONFIG_ZAO_CAPCELLA)	+= fixup-capcella.o
+
+obj-$(CONFIG_SGI_IOC3) += ioc3.o
--- /dev/null	2006-02-13 15:11:07.474148640 +0000
+++ b/arch/mips/pci/ioc3.c	2006-02-19 21:19:36.000000000 +0000
@@ -0,0 +1,801 @@
+/*
+ * SGI IOC3 master driver and IRQ demuxer
+ *
+ * Copyright (c) 2005 Stanislaw Skowronek <skylark@linux-mips.org>
+ * Heavily based on similar work by:
+ *   Brent Casavant <bcasavan@sgi.com> - IOC4 master driver
+ *   Pat Gefre <pfg@sgi.com> - IOC3 serial port IRQ demuxer
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+
+#include <linux/ioc3.h>
+
+#define IOC3_PCI_SIZE 0x100000
+
+static LIST_HEAD(ioc3_devices);
+static int ioc3_counter;
+static DECLARE_RWSEM(ioc3_devices_rwsem);
+
+static struct ioc3_submodule *ioc3_submodules[IOC3_MAX_SUBMODULES];
+static struct ioc3_submodule *ioc3_ethernet;
+static DEFINE_RWLOCK(ioc3_submodules_lock);
+
+/* NIC probing code */
+
+static inline unsigned mcr_pack(unsigned pulse, unsigned sample)
+{
+	return (pulse << 10) | (sample << 2);
+}
+
+static int nic_wait(struct ioc3_driver_data *idd)
+{
+	unsigned mcr;
+
+        do {
+                mcr = idd->vma->mcr;
+        } while (!(mcr & 2));
+
+        return mcr & 1;
+}
+
+static int nic_reset(struct ioc3_driver_data *idd)
+{
+        int presence;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	idd->vma->mcr = mcr_pack(500, 65);
+	presence = nic_wait(idd);
+	local_irq_restore(flags);
+
+	udelay(500);
+
+        return presence;
+}
+
+static inline int nic_read_bit(struct ioc3_driver_data *idd)
+{
+	int result;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	idd->vma->mcr = mcr_pack(6, 13);
+	result = nic_wait(idd);
+	local_irq_restore(flags);
+
+	udelay(500);
+
+	return result;
+}
+
+static inline void nic_write_bit(struct ioc3_driver_data *idd, int bit)
+{
+	if (bit)
+		idd->vma->mcr = mcr_pack(6, 110);
+	else
+		idd->vma->mcr = mcr_pack(80, 30);
+
+	nic_wait(idd);
+}
+
+static unsigned nic_read_byte(struct ioc3_driver_data *idd)
+{
+	unsigned result = 0;
+	int i;
+
+	for (i = 0; i < 8; i++)
+		result = (result >> 1) | (nic_read_bit(idd) << 7);
+
+	return result;
+}
+
+static void nic_write_byte(struct ioc3_driver_data *idd, int byte)
+{
+	int i, bit;
+
+	for (i = 8; i; i--) {
+		bit = byte & 1;
+		byte >>= 1;
+
+		nic_write_bit(idd, bit);
+	}
+}
+
+static unsigned long nic_find(struct ioc3_driver_data *idd, int *last, unsigned long addr)
+{
+	int a, b, index, disc;
+
+	nic_reset(idd);
+
+	/* Search ROM.  */
+	nic_write_byte(idd, 0xF0);
+
+	/* Algorithm from ``Book of iButton Standards''.  */
+	for (index = 0, disc = 0; index < 64; index++) {
+		a = nic_read_bit(idd);
+		b = nic_read_bit(idd);
+
+		if (a && b) {
+			printk(KERN_WARNING "IOC3 NIC search failed.\n");
+			*last = 0;
+			return 0;
+		}
+
+		if (!a && !b) {
+			if (index == *last) {
+				addr |= 1UL << index;
+			} else if (index > *last) {
+				addr &= ~(1UL << index);
+				disc = index;
+			} else if ((addr & (1UL << index)) == 0)
+				disc = index;
+			nic_write_bit(idd, (addr>>index)&1);
+			continue;
+		} else {
+			if (a)
+				addr |= 1UL << index;
+			else
+				addr &= ~(1UL << index);
+			nic_write_bit(idd, a);
+			continue;
+		}
+	}
+
+	*last = disc;
+
+	return addr;
+}
+
+static void nic_addr(struct ioc3_driver_data *idd, unsigned long addr)
+{
+	int index;
+
+	nic_reset(idd);
+	nic_write_byte(idd, 0xF0);
+	for (index = 0; index < 64; index++) {
+		nic_read_bit(idd);
+		nic_read_bit(idd);
+		nic_write_bit(idd, (addr>>index)&1);
+	}
+}
+
+static void crc16_byte(unsigned int *crc, unsigned char db)
+{
+	int i;
+	for(i=0;i<8;i++) {
+		*crc <<= 1;
+		if((db^(*crc>>16)) & 1)
+			*crc ^= 0x8005;
+		db >>= 1;
+	}
+	*crc &= 0xFFFF;
+}
+
+static unsigned int crc16_area(unsigned char *dbs, int size, unsigned int crc)
+{
+	while(size--)
+		crc16_byte(&crc, *(dbs++));
+	return crc;
+}
+
+static void crc8_byte(unsigned int *crc, unsigned char db)
+{
+	int i,f;
+	for(i=0;i<8;i++) {
+		f = (*crc ^ db) & 1;
+		*crc >>= 1;
+		db >>= 1;
+		if(f)
+			*crc ^= 0x8c;
+	}
+	*crc &= 0xff;
+}
+
+static unsigned int crc8_addr(unsigned long addr)
+{
+	int i;
+	unsigned int crc = 0x00;
+	for(i=0;i<8;i++)
+		crc8_byte(&crc, addr>>(i<<3));
+	return crc;
+}
+
+static void read_redir_page(struct ioc3_driver_data *idd, unsigned long addr, int page, unsigned char *redir, unsigned char *data)
+{
+	int loops = 16, i;
+	while(redir[page] != 0xFF) {
+		page = redir[page]^0xFF;
+		loops--;
+		if(loops<0) {
+			printk(KERN_ERR "IOC3: NIC circular redirection\n");
+			return;
+		}
+	}
+	loops = 3;
+	while(loops>0) {
+		nic_addr(idd, addr);
+		nic_write_byte(idd, 0xF0);
+		nic_write_byte(idd, (page << 5) & 0xE0);
+		nic_write_byte(idd, (page >> 3) & 0x1F);
+		for(i=0;i<0x20;i++)
+			data[i] = nic_read_byte(idd);
+		if(crc16_area(data, 0x20, 0x0000) == 0x800d)
+			return;
+		loops--;
+	}
+	printk(KERN_ERR "IOC3: CRC error in data page\n");
+	for(i=0;i<0x20;i++)
+		data[i] = 0x00;
+}
+
+static void read_redir_map(struct ioc3_driver_data *idd, unsigned long addr, unsigned char *redir)
+{
+	int i,j,loops = 3,crc_ok;
+	unsigned int crc;
+	while(loops>0) {
+		crc_ok = 1;
+		nic_addr(idd, addr);
+		nic_write_byte(idd, 0xAA);
+		nic_write_byte(idd, 0x00);
+		nic_write_byte(idd, 0x01);
+		for(i=0;i<64;i+=8) {
+			for(j=0;j<8;j++)
+				redir[i+j] = nic_read_byte(idd);
+			crc = crc16_area(redir+i, 8, (i==0)?0x8707:0x0000);
+			crc16_byte(&crc, nic_read_byte(idd));
+			crc16_byte(&crc, nic_read_byte(idd));
+			if(crc != 0x800d)
+				crc_ok = 0;
+		}
+		if(crc_ok)
+			return;
+		loops--;
+	}
+	printk(KERN_ERR "IOC3: CRC error in redirection page\n");
+	for(i=0;i<64;i++)
+		redir[i] = 0xFF;
+}
+
+static void read_nic(struct ioc3_driver_data *idd, unsigned long addr)
+{
+	unsigned char redir[64];
+	unsigned char data[64],part[32];
+	int i,j;
+	/* read redirections */
+	read_redir_map(idd, addr, redir);
+	/* read data pages */
+	read_redir_page(idd, addr, 0, redir, data);
+	read_redir_page(idd, addr, 1, redir, data+32);
+	/* assemble the part # */
+	j=0;
+	for(i=0;i<19;i++)
+		if(data[i+11] != ' ')
+			part[j++] = data[i+11];
+	for(i=0;i<6;i++)
+		if(data[i+32] != ' ')
+			part[j++] = data[i+32];
+	part[j] = 0;
+	/* skip Octane power supplies */
+	if(!strncmp(part, "060-0035-", 9))
+		return;
+	if(!strncmp(part, "060-0038-", 9))
+		return;
+	strcpy(idd->nic_part, part);
+	/* assemble the serial # */
+	j=0;
+	for(i=0;i<10;i++)
+		if(data[i+1] != ' ')
+			idd->nic_serial[j++] = data[i+1];
+	idd->nic_serial[j] = 0;
+}
+
+static void read_mac(struct ioc3_driver_data *idd, unsigned long addr)
+{
+	int i, loops = 3;
+	unsigned char data[13];
+	while(loops>0) {
+		nic_addr(idd, addr);
+		nic_write_byte(idd, 0xF0);
+		nic_write_byte(idd, 0x00);
+		nic_write_byte(idd, 0x00);
+		nic_read_byte(idd);
+		for(i=0;i<13;i++)
+			data[i] = nic_read_byte(idd);
+		if(crc16_area(data, 13, 0x0000) == 0x800d) {
+			for(i=10;i>4;i--)
+				idd->nic_mac[10-i] = data[i];
+			return;
+		}
+		loops--;
+	}
+	printk(KERN_ERR "IOC3: CRC error in MAC address\n");
+	for(i=0;i<6;i++)
+		idd->nic_mac[i] = 0x00;
+}
+
+static void probe_nic(struct ioc3_driver_data *idd)
+{
+	int save = 0, loops = 3;
+	unsigned long first, addr;
+	idd->vma->gpcr_s = GPCR_MLAN_EN;
+	while(loops>0) {
+		idd->nic_part[0] = 0;
+		idd->nic_serial[0] = 0;
+		addr = first = nic_find(idd, &save, 0);
+		if(!first)
+			return;
+		while(1) {
+			if(crc8_addr(addr))
+				break;
+			else {
+				switch(addr & 0xFF) {
+				case 0x0B:
+					read_nic(idd, addr);
+					break;
+				case 0x09:
+				case 0x89:
+				case 0x91:
+					read_mac(idd, addr);
+					break;
+				}
+			}
+			addr = nic_find(idd, &save, addr);
+			if(addr == first)
+				return;
+		}
+		loops--;
+	}
+	printk(KERN_ERR "IOC3: CRC error in NIC address\n");
+}
+
+/* Interrupts */
+
+#define IOC3_W_IES		0
+#define IOC3_W_IEC		1
+static inline void write_ireg(struct ioc3_driver_data *idd, uint32_t val, int which)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&idd->ir_lock, flags);
+	switch (which) {
+	case IOC3_W_IES:
+		writel(val, &idd->vma->sio_ies);
+		break;
+	case IOC3_W_IEC:
+		writel(val, &idd->vma->sio_iec);
+		break;
+	}
+	spin_unlock_irqrestore(&idd->ir_lock, flags);
+}
+static inline uint32_t get_pending_intrs(struct ioc3_driver_data *idd)
+{
+	unsigned long flag;
+	uint32_t intrs = 0;
+
+	spin_lock_irqsave(&idd->ir_lock, flag);
+	intrs = readl(&idd->vma->sio_ir);
+	intrs &= readl(&idd->vma->sio_ies);
+	spin_unlock_irqrestore(&idd->ir_lock, flag);
+	return intrs;
+}
+
+static irqreturn_t ioc3_intr_io(int irq, void *arg, struct pt_regs *regs)
+{
+	unsigned long flags;
+	struct ioc3_driver_data *idd = (struct ioc3_driver_data *)arg;
+	int handled = 1, id;
+	unsigned int pending;
+
+	read_lock_irqsave(&ioc3_submodules_lock, flags);
+	if(!idd->dual_irq && idd->vma->eisr)	/* send Ethernet IRQ to the driver */
+		if(ioc3_ethernet && idd->active[ioc3_ethernet->id] && ioc3_ethernet->intr)
+			handled = handled && !ioc3_ethernet->intr(ioc3_ethernet, idd, 0, regs);
+	pending = get_pending_intrs(idd);	/* look at the IO IRQs */
+	for(id=0;id<IOC3_MAX_SUBMODULES;id++)
+		if(idd->active[id] && ioc3_submodules[id] && (pending & ioc3_submodules[id]->irq_mask) && ioc3_submodules[id]->intr) {
+			write_ireg(idd, ioc3_submodules[id]->irq_mask, IOC3_W_IEC);
+			if(!ioc3_submodules[id]->intr(ioc3_submodules[id], idd, pending & ioc3_submodules[id]->irq_mask, regs))
+				pending &=~ ioc3_submodules[id]->irq_mask;
+			write_ireg(idd, ioc3_submodules[id]->irq_mask, IOC3_W_IES);
+		}
+	read_unlock_irqrestore(&ioc3_submodules_lock, flags);
+	if(pending) {
+		printk(KERN_WARNING "IOC3: Pending IRQs 0x%08x discarded and disabled\n",pending);
+		write_ireg(idd, pending, IOC3_W_IEC);
+		handled = 1;
+	}
+	return handled?IRQ_HANDLED:IRQ_NONE;
+}
+
+static irqreturn_t ioc3_intr_eth(int irq, void *arg, struct pt_regs *regs)
+{
+	unsigned long flags;
+	struct ioc3_driver_data *idd = (struct ioc3_driver_data *)arg;
+	int handled = 1;
+
+	if(!idd->dual_irq)
+		return IRQ_NONE;
+	read_lock_irqsave(&ioc3_submodules_lock, flags);
+	if(ioc3_ethernet && idd->active[ioc3_ethernet->id] && ioc3_ethernet->intr)
+		handled = handled && !ioc3_ethernet->intr(ioc3_ethernet, idd, 0, regs);
+	read_unlock_irqrestore(&ioc3_submodules_lock, flags);
+	return handled?IRQ_HANDLED:IRQ_NONE;
+}
+
+void ioc3_enable(struct ioc3_submodule *is, struct ioc3_driver_data *idd)
+{
+	write_ireg(idd, is->irq_mask, IOC3_W_IES);
+}
+
+void ioc3_ack(struct ioc3_submodule *is, struct ioc3_driver_data *idd, unsigned int irqs)
+{
+	writel(irqs & is->irq_mask, &idd->vma->sio_ir);
+}
+
+void ioc3_disable(struct ioc3_submodule *is, struct ioc3_driver_data *idd)
+{
+	write_ireg(idd, is->irq_mask, IOC3_W_IEC);
+}
+
+void ioc3_gpio(struct ioc3_driver_data *idd, unsigned int mask, unsigned int val)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&idd->gpio_lock, flags);
+	idd->gpdr_shadow &= ~mask;
+	idd->gpdr_shadow |= val&mask;
+	idd->vma->gpdr = idd->gpdr_shadow;
+	spin_unlock_irqrestore(&idd->gpio_lock, flags);
+}
+
+/* Keep it simple, stupid! */
+static int find_slot(void **tab, int max)
+{
+	int i;
+	for(i=0;i<max;i++)
+		if(!(tab[i]))
+			return i;
+	return -1;
+}
+
+/* Register an IOC3 submodule */
+int ioc3_register_submodule(struct ioc3_submodule *is)
+{
+	struct ioc3_driver_data *idd;
+	int alloc_id;
+	unsigned long flags;
+
+	write_lock_irqsave(&ioc3_submodules_lock, flags);
+	alloc_id = find_slot((void **)ioc3_submodules, IOC3_MAX_SUBMODULES);
+	if(alloc_id != -1) {
+		ioc3_submodules[alloc_id] = is;
+		if(is->ethernet) {
+			if(ioc3_ethernet==NULL)
+				ioc3_ethernet=is;
+			else
+				printk(KERN_WARNING "IOC3 Ethernet module already registered!\n");
+		}
+	}
+	write_unlock_irqrestore(&ioc3_submodules_lock, flags);
+
+	if(alloc_id == -1) {
+		printk(KERN_WARNING "Increase IOC3_MAX_SUBMODULES!\n");
+		return -ENOMEM;
+	}
+
+	is->id=alloc_id;
+
+	/* Initialize submodule for each IOC3 */
+	if (!is->probe)
+		return 0;
+
+	down_read(&ioc3_devices_rwsem);
+	list_for_each_entry(idd, &ioc3_devices, list) {
+		/* set to 1 for IRQs in probe */
+		idd->active[alloc_id] = 1;
+		idd->active[alloc_id] = !is->probe(is, idd);
+	}
+	up_read(&ioc3_devices_rwsem);
+
+	return 0;
+}
+
+/* Unregister an IOC3 submodule */
+void ioc3_unregister_submodule(struct ioc3_submodule *is)
+{
+	struct ioc3_driver_data *idd;
+	unsigned long flags;
+
+	write_lock_irqsave(&ioc3_submodules_lock, flags);
+	if(ioc3_submodules[is->id]==is)
+		ioc3_submodules[is->id]=NULL;
+	else
+		printk(KERN_WARNING "IOC3 submodule %s has wrong ID.\n",is->name);
+	if(ioc3_ethernet==is)
+		ioc3_ethernet = NULL;
+	write_unlock_irqrestore(&ioc3_submodules_lock, flags);
+
+	/* Remove submodule for each IOC3 */
+	down_read(&ioc3_devices_rwsem);
+	list_for_each_entry(idd, &ioc3_devices, list)
+		if(idd->active[is->id]) {
+			if(is->remove)
+				if(is->remove(is, idd))
+					printk(KERN_WARNING
+					       "%s: IOC3 submodule %s remove failed "
+					       "for pci_dev %s.\n",
+					       __FUNCTION__, module_name(is->owner),
+					       pci_name(idd->pdev));
+			idd->active[is->id] = 0;
+			if(is->irq_mask)
+				write_ireg(idd, is->irq_mask, IOC3_W_IEC);
+		}
+	up_read(&ioc3_devices_rwsem);
+}
+
+/*********************
+ * Device management *
+ *********************/
+
+static char *ioc3_class_names[]={"unknown", "IP27 BaseIO", "IP30 system", "MENET 1/2/3", "MENET 4", "CADduo", "Altix Serial"};
+
+static int ioc3_class(struct ioc3_driver_data *idd)
+{
+	int res = IOC3_CLASS_NONE;
+	/* NIC-based logic */
+	if(!strncmp(idd->nic_part, "030-0891-", 9))
+		res = IOC3_CLASS_BASE_IP30;
+	if(!strncmp(idd->nic_part, "030-1155-", 9))
+		res = IOC3_CLASS_CADDUO;
+	if(!strncmp(idd->nic_part, "030-1657-", 9))
+		res = IOC3_CLASS_SERIAL;
+	if(!strncmp(idd->nic_part, "030-1664-", 9))
+		res = IOC3_CLASS_SERIAL;
+	/* total random heuristics */
+#ifdef CONFIG_SGI_IP27
+	if(!idd->nic_part[0])
+		res = IOC3_CLASS_BASE_IP27;
+#endif
+	/* print educational message */
+	printk(KERN_INFO "IOC3 part: [%s], serial: [%s] => class %s\n", idd->nic_part, idd->nic_serial, ioc3_class_names[res]);
+	return res;
+}
+
+/* Adds a new instance of an IOC3 card */
+static int ioc3_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
+{
+	struct ioc3_driver_data *idd;
+	uint32_t pcmd;
+	int ret, id;
+
+	/* Enable IOC3 and take ownership of it */
+	if ((ret = pci_enable_device(pdev))) {
+		printk(KERN_WARNING
+		       "%s: Failed to enable IOC3 device for pci_dev %s.\n",
+		       __FUNCTION__, pci_name(pdev));
+		goto out;
+	}
+	pci_set_master(pdev);
+
+        ret = pci_set_dma_mask(pdev, 0xffffffffffffffffULL);
+        if (!ret) {
+                ret = pci_set_consistent_dma_mask(pdev, 0xffffffffffffffffULL);
+                if (ret < 0) {
+                        printk(KERN_WARNING "%s: Unable to obtain 64 bit DMA "
+                               "for consistent allocations\n",
+				__FUNCTION__);
+                }
+	}
+
+	/* Set up per-IOC3 data */
+	idd = kmalloc(sizeof(struct ioc3_driver_data), GFP_KERNEL);
+	if (!idd) {
+		printk(KERN_WARNING
+		       "%s: Failed to allocate IOC3 data for pci_dev %s.\n",
+		       __FUNCTION__, pci_name(pdev));
+		ret = -ENODEV;
+		goto out_idd;
+	}
+	memset(idd, 0, sizeof(struct ioc3_driver_data));
+	spin_lock_init(&idd->ir_lock);
+	spin_lock_init(&idd->gpio_lock);
+	idd->pdev = pdev;
+
+	/* Map all IOC3 registers.  These are shared between subdevices
+	 * so the main IOC3 module manages them.
+	 */
+	idd->pma = pci_resource_start(pdev, 0);
+	if (!idd->pma) {
+		printk(KERN_WARNING
+		       "%s: Unable to find IOC3 resource "
+		       "for pci_dev %s.\n",
+		       __FUNCTION__, pci_name(pdev));
+		ret = -ENODEV;
+		goto out_pci;
+	}
+	if (!request_region(idd->pma, IOC3_PCI_SIZE, "ioc3")) {
+		printk(KERN_WARNING
+		       "%s: Unable to request IOC3 region "
+		       "for pci_dev %s.\n",
+		       __FUNCTION__, pci_name(pdev));
+		ret = -ENODEV;
+		goto out_pci;
+	}
+	idd->vma = ioremap(idd->pma, IOC3_PCI_SIZE);
+	if (!idd->vma) {
+		printk(KERN_WARNING
+		       "%s: Unable to remap IOC3 region "
+		       "for pci_dev %s.\n",
+		       __FUNCTION__, pci_name(pdev));
+		ret = -ENODEV;
+		goto out_misc_region;
+	}
+
+	/* Track PCI-device specific data */
+	pci_set_drvdata(pdev, idd);
+	down_write(&ioc3_devices_rwsem);
+	list_add(&idd->list, &ioc3_devices);
+	idd->id = ioc3_counter++;
+	up_write(&ioc3_devices_rwsem);
+
+	idd->gpdr_shadow = idd->vma->gpdr;
+
+	/* Read IOC3 NIC contents */
+	probe_nic(idd);
+
+	/* Detect IOC3 class */
+	idd->class = ioc3_class(idd);
+
+	/* Initialize IOC3 */
+	pci_read_config_dword(pdev, PCI_COMMAND, &pcmd);
+	pci_write_config_dword(pdev, PCI_COMMAND,
+			       pcmd | PCI_COMMAND_PARITY | PCI_COMMAND_SERR);
+
+	write_ireg(idd, ~0, IOC3_W_IEC);
+	writel(~0, &idd->vma->sio_ir);
+
+	writel(0, &idd->vma->eier);
+	writel(~0, &idd->vma->eisr);
+
+	/* Set up IRQs */
+	if(idd->class == IOC3_CLASS_BASE_IP30 || idd->class == IOC3_CLASS_BASE_IP27) {
+		idd->dual_irq = 1;
+		if (!request_irq(pdev->irq, ioc3_intr_eth, SA_SHIRQ,
+				 "ioc3-eth", (void *)idd)) {
+			idd->irq_eth = pdev->irq;
+		} else {
+			printk(KERN_WARNING
+			       "%s : request_irq fails for IRQ 0x%x\n ",
+			       __FUNCTION__, pdev->irq);
+		}
+		if (!request_irq(pdev->irq+2, ioc3_intr_io, SA_SHIRQ,
+				 "ioc3-io", (void *)idd)) {
+			idd->irq_io = pdev->irq+2;
+		} else {
+			printk(KERN_WARNING
+			       "%s : request_irq fails for IRQ 0x%x\n ",
+			       __FUNCTION__, pdev->irq+2);
+		}
+	} else {
+		if (!request_irq(pdev->irq, ioc3_intr_io, SA_SHIRQ,
+				 "ioc3", (void *)idd)) {
+			idd->irq_io = pdev->irq;
+		} else {
+			printk(KERN_WARNING
+			       "%s : request_irq fails for IRQ 0x%x\n ",
+			       __FUNCTION__, pdev->irq);
+		}
+	}
+
+	/* Add this IOC3 to all submodules */
+	read_lock(&ioc3_submodules_lock);
+	for(id=0;id<IOC3_MAX_SUBMODULES;id++)
+		if(ioc3_submodules[id] && ioc3_submodules[id]->probe) {
+			idd->active[id] = 1;
+			idd->active[id] = !ioc3_submodules[id]->probe(ioc3_submodules[id], idd);
+		}
+	read_unlock(&ioc3_submodules_lock);
+
+	printk(KERN_INFO "IOC3 Master Driver loaded for %s\n", pci_name(pdev));
+
+	return 0;
+
+out_misc_region:
+	release_region(idd->pma, IOC3_PCI_SIZE);
+out_pci:
+	kfree(idd);
+out_idd:
+	pci_disable_device(pdev);
+out:
+	return ret;
+}
+
+/* Removes a particular instance of an IOC3 card. */
+static void ioc3_remove(struct pci_dev *pdev)
+{
+	int id;
+	struct ioc3_driver_data *idd;
+
+	idd = pci_get_drvdata(pdev);
+
+	/* Remove this IOC3 from all submodules */
+	read_lock(&ioc3_submodules_lock);
+	for(id=0;id<IOC3_MAX_SUBMODULES;id++)
+		if(idd->active[id]) {
+			if(ioc3_submodules[id] && ioc3_submodules[id]->remove)
+				if(ioc3_submodules[id]->remove(ioc3_submodules[id], idd))
+					printk(KERN_WARNING
+					       "%s: IOC3 submodule 0x%s remove failed "
+					       "for pci_dev %s.\n",
+					        __FUNCTION__, module_name(ioc3_submodules[id]->owner),
+					        pci_name(pdev));
+			idd->active[id] = 0;
+		}
+	read_unlock(&ioc3_submodules_lock);
+
+	/* Clear and disable all IRQs */
+	write_ireg(idd, ~0, IOC3_W_IEC);
+	writel(~0, &idd->vma->sio_ir);
+
+	/* Release resources */
+	free_irq(idd->irq_io, (void *)idd);
+	if(idd->dual_irq)
+		free_irq(idd->irq_eth, (void *)idd);
+	iounmap(idd->vma);
+	release_region(idd->pma, IOC3_PCI_SIZE);
+
+	/* Disable IOC3 and relinquish */
+	pci_disable_device(pdev);
+
+	/* Remove and free driver data */
+	down_write(&ioc3_devices_rwsem);
+	list_del(&idd->list);
+	up_write(&ioc3_devices_rwsem);
+	kfree(idd);
+}
+
+static struct pci_device_id ioc3_id_table[] = {
+	{PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3, PCI_ANY_ID, PCI_ANY_ID},
+	{0}
+};
+
+static struct pci_driver ioc3_driver = {
+	.name = "SGI IOC3",
+	.id_table = ioc3_id_table,
+	.probe = ioc3_probe,
+	.remove = ioc3_remove,
+};
+
+MODULE_DEVICE_TABLE(pci, ioc3_id_table);
+
+/*********************
+ * Module management *
+ *********************/
+
+/* Module load */
+static int __devinit ioc3_init(void)
+{
+	return pci_register_driver(&ioc3_driver);
+}
+
+/* Module unload */
+static void __devexit ioc3_exit(void)
+{
+	pci_unregister_driver(&ioc3_driver);
+}
+
+module_init(ioc3_init);
+module_exit(ioc3_exit);
+
+MODULE_AUTHOR("Stanislaw Skowronek <skylark@linux-mips.org>");
+MODULE_DESCRIPTION("PCI driver for SGI IOC3");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(ioc3_register_submodule);
+EXPORT_SYMBOL(ioc3_unregister_submodule);


-- 
Martin Michlmayr
http://www.cyrius.com/

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: Merging Skylark's IOC3 patch
  2006-02-19 21:15 ` Martin Michlmayr
  (?)
  (?)
@ 2006-02-19 21:55 ` Martin Michlmayr
  2006-02-19 22:31   ` Dmitry Torokhov
  -1 siblings, 1 reply; 13+ messages in thread
From: Martin Michlmayr @ 2006-02-19 21:55 UTC (permalink / raw)
  To: Ralf Baechle, Dmitry Torokhov; +Cc: linux-mips, Stanislaw Skowronek

* Martin Michlmayr <tbm@cyrius.com> [2006-02-19 21:15]:
> Can you please review and/or merge Skylark's IOC3 patch from
> ftp://ftp.linux-mips.org/pub/linux/mips/people/skylark/linux-mips-2.6.14-ioc3-r26.patch.bz2
> 
> From my basic understanding I believe that this patch needs to be split up
> and submitted to different sub-system maintainers.

(Dmitry, this is only a RFC for now since the main support patch
for IOC3 has not been merged yet.)


From: Stanislaw Skowronek <skylark@linux-mips.org>

[PATCH 2/5] serio: SGI IOC3 PS/2 controller driver

Add a PS/2 driver based on the SGI IOC3.  This is useful for SGI Octane
machines (IP27).

Signed-off-by: Stanislaw Skowronek <skylark@linux-mips.org>
Signed-off-by: Martin Michlmayr <tbm@cyrius.com>

---

 Kconfig   |    7 ++
 Makefile  |    1 
 ioc3kbd.c |  172 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 180 insertions(+)

diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
index 98acf17..7dedec0 100644
--- a/drivers/input/serio/Kconfig
+++ b/drivers/input/serio/Kconfig
@@ -18,6 +18,13 @@ config SERIO
 
 if SERIO
 
+config SERIO_SGI_IOC3
+	tristate "SGI IOC3 keyboard controller"
+	default y
+	depends on SGI_IOC3
+	---help---
+	  If you have an Octane and you want to use its keyboard, select this.
+
 config SERIO_I8042
 	tristate "i8042 PC Keyboard controller" if EMBEDDED || !X86
 	default y
diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile
index 4155197..14cb41b 100644
--- a/drivers/input/serio/Makefile
+++ b/drivers/input/serio/Makefile
@@ -18,5 +18,6 @@ obj-$(CONFIG_HP_SDC)		+= hp_sdc.o
 obj-$(CONFIG_HIL_MLC)		+= hp_sdc_mlc.o hil_mlc.o
 obj-$(CONFIG_SERIO_PCIPS2)	+= pcips2.o
 obj-$(CONFIG_SERIO_MACEPS2)	+= maceps2.o
+obj-$(CONFIG_SERIO_SGI_IOC3)	+= ioc3kbd.o
 obj-$(CONFIG_SERIO_LIBPS2)	+= libps2.o
 obj-$(CONFIG_SERIO_RAW)		+= serio_raw.o
--- /dev/null	2006-02-13 15:11:07.474148640 +0000
+++ b/drivers/input/serio/ioc3kbd.c	2006-02-19 21:26:55.000000000 +0000
@@ -0,0 +1,172 @@
+/*
+ * SGI IOC3 PS/2 controller driver for Linux
+ *
+ * Copyright (C) 2005 Stanislaw Skowronek <skylark@linux-mips.org>
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/err.h>
+#include <linux/pci.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <linux/ioc3.h>
+
+MODULE_AUTHOR("Stanislaw Skowronek <skylark@linux-mips.org>");
+MODULE_DESCRIPTION("SGI IOC3 serio driver");
+MODULE_LICENSE("GPL");
+
+struct ioc3kbd_data {
+	struct ioc3_driver_data *idd;
+	struct serio *kbd,*aux;
+};
+
+static int ioc3kbd_write(struct serio *dev, unsigned char val)
+{
+	struct ioc3kbd_data *d = (struct ioc3kbd_data *)(dev->port_data);
+	unsigned mask;
+	unsigned long timeout=0;
+
+	mask = (dev==d->aux) ? KM_CSR_M_WRT_PEND : KM_CSR_K_WRT_PEND;
+	while((d->idd->vma->km_csr & mask) && (timeout<1000)) {
+		udelay(100);
+		timeout++;
+	}
+
+	if(dev==d->aux)
+		d->idd->vma->m_wd=((unsigned)val)&0x000000ff;
+	else
+		d->idd->vma->k_wd=((unsigned)val)&0x000000ff;
+
+	if(timeout>=1000)
+		return -1;
+	return 0;
+}
+
+static int ioc3kbd_intr(struct ioc3_submodule *is, struct ioc3_driver_data *idd, unsigned int irq, struct pt_regs *regs)
+{
+	struct ioc3kbd_data *d = (struct ioc3kbd_data *)(idd->data[is->id]);
+	unsigned int data_k, data_m;
+
+	ioc3_ack(is,idd,irq);
+	data_k=d->idd->vma->k_rd;
+	data_m=d->idd->vma->m_rd;
+
+	if(data_k & KM_RD_VALID_0)
+		serio_interrupt(d->kbd, (data_k >> KM_RD_DATA_0_SHIFT) & 0xFF, 0, regs);
+	if(data_k & KM_RD_VALID_1)
+		serio_interrupt(d->kbd, (data_k >> KM_RD_DATA_1_SHIFT) & 0xFF, 0, regs);
+	if(data_k & KM_RD_VALID_2)
+		serio_interrupt(d->kbd, (data_k >> KM_RD_DATA_2_SHIFT) & 0xFF, 0, regs);
+	if(data_m & KM_RD_VALID_0)
+		serio_interrupt(d->aux, (data_m >> KM_RD_DATA_0_SHIFT) & 0xFF, 0, regs);
+	if(data_m & KM_RD_VALID_1)
+		serio_interrupt(d->aux, (data_m >> KM_RD_DATA_1_SHIFT) & 0xFF, 0, regs);
+	if(data_m & KM_RD_VALID_2)
+		serio_interrupt(d->aux, (data_m >> KM_RD_DATA_2_SHIFT) & 0xFF, 0, regs);
+
+	return 0;
+}
+
+static int ioc3kbd_open(struct serio *dev)
+{
+	return 0;
+}
+
+static void ioc3kbd_close(struct serio *dev)
+{
+}
+
+static struct ioc3kbd_data * __init ioc3kbd_allocate_port(int idx, struct ioc3_driver_data *idd)
+{
+	struct serio *sk, *sa;
+	struct ioc3kbd_data *d;
+
+	sk = kmalloc(sizeof(struct serio), GFP_KERNEL);
+	sa = kmalloc(sizeof(struct serio), GFP_KERNEL);
+	d = kmalloc(sizeof(struct ioc3kbd_data), GFP_KERNEL);
+	if (sk && sa && d) {
+		memset(sk, 0, sizeof(struct serio));
+		sk->id.type = SERIO_8042;
+		sk->write = ioc3kbd_write;
+		sk->open = ioc3kbd_open;
+		sk->close = ioc3kbd_close;
+		snprintf(sk->name, sizeof(sk->name), "IOC3 keyboard %d", idx);
+		snprintf(sk->phys, sizeof(sk->phys), "ioc3/serio%dkbd", idx);
+		sk->port_data = d;
+		sk->dev.parent = &(idd->pdev->dev);
+		memset(sa, 0, sizeof(struct serio));
+		sa->id.type = SERIO_8042;
+		sa->write = ioc3kbd_write;
+		sa->open = ioc3kbd_open;
+		sa->close = ioc3kbd_close;
+		snprintf(sa->name, sizeof(sa->name), "IOC3 auxiliary %d", idx);
+		snprintf(sa->phys, sizeof(sa->phys), "ioc3/serio%daux", idx);
+		sa->port_data = d;
+		sa->dev.parent = &(idd->pdev->dev);
+		d->idd = idd;
+		d->kbd = sk;
+		d->aux = sa;
+		return d;
+	}
+	return NULL;
+}
+
+static int ioc3kbd_probe(struct ioc3_submodule *is, struct ioc3_driver_data *idd)
+{
+	struct ioc3kbd_data *d;
+	if(idd->class != IOC3_CLASS_BASE_IP30 && idd->class != IOC3_CLASS_CADDUO)
+		return 1;
+	d = ioc3kbd_allocate_port(idd->id, idd);
+	idd->data[is->id] = d;
+	if(!d)
+		return 1;
+	ioc3_enable(is, idd);
+	serio_register_port(d->kbd);
+	serio_register_port(d->aux);
+	return 0;
+}
+
+static int ioc3kbd_remove(struct ioc3_submodule *is, struct ioc3_driver_data *idd)
+{
+	struct ioc3kbd_data *d = (struct ioc3kbd_data *)(idd->data[is->id]);
+	serio_unregister_port(d->kbd);
+	serio_unregister_port(d->aux);
+	kfree(d->kbd);
+	kfree(d->aux);
+	kfree(d);
+	idd->data[is->id] = NULL;
+	return 0;
+}
+
+static struct ioc3_submodule ioc3kbd_submodule = {
+	.name = "serio",
+	.probe = ioc3kbd_probe,
+	.remove = ioc3kbd_remove,
+	.irq_mask = SIO_IR_KBD_INT,
+	.intr = ioc3kbd_intr,
+	.owner = THIS_MODULE,
+};
+
+static int __init ioc3kbd_init(void)
+{
+	ioc3_register_submodule(&ioc3kbd_submodule);
+	return 0;
+}
+
+static void __exit ioc3kbd_exit(void)
+{
+	ioc3_unregister_submodule(&ioc3kbd_submodule);
+}
+
+module_init(ioc3kbd_init);
+module_exit(ioc3kbd_exit);


-- 
Martin Michlmayr
http://www.cyrius.com/

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: Merging Skylark's IOC3 patch
  2006-02-19 21:15 ` Martin Michlmayr
                   ` (2 preceding siblings ...)
  (?)
@ 2006-02-19 21:56 ` Martin Michlmayr
  -1 siblings, 0 replies; 13+ messages in thread
From: Martin Michlmayr @ 2006-02-19 21:56 UTC (permalink / raw)
  To: Ralf Baechle, netdev; +Cc: linux-mips, Stanislaw Skowronek

* Martin Michlmayr <tbm@cyrius.com> [2006-02-19 21:15]:
> Can you please review and/or merge Skylark's IOC3 patch from
> ftp://ftp.linux-mips.org/pub/linux/mips/people/skylark/linux-mips-2.6.14-ioc3-r26.patch.bz2
> 
> From my basic understanding I believe that this patch needs to be split up
> and submitted to different sub-system maintainers.

(netdev@vger.kernel.org, this is only a RFC for now since the main
support patch for IOC3 has not been merged yet.)


From: Stanislaw Skowronek <skylark@linux-mips.org>

[PATCH 3/5] net: Convert the SGI IOC3 Ethernet driver to use IOC3 meta driver

Convert the SGI IOC3 Ethernet driver to use the IOC3 meta driver.

Signed-off-by: Stanislaw Skowronek <skylark@linux-mips.org>
Signed-off-by: Martin Michlmayr <tbm@cyrius.com>

---

 Kconfig    |    2 
 ioc3-eth.c |  458 +++++++------------------------------------------------------
 2 files changed, 55 insertions(+), 405 deletions(-)

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 94ad74c..66c340d 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -462,7 +462,7 @@ config MIPS_AU1X00_ENET
 
 config SGI_IOC3_ETH
 	bool "SGI IOC3 Ethernet"
-	depends on NET_ETHERNET && PCI && SGI_IP27
+	depends on NET_ETHERNET && SGI_IOC3
 	select CRC32
 	select MII
 	help
diff --git a/drivers/net/ioc3-eth.c b/drivers/net/ioc3-eth.c
index 9b8295e..8fadae6 100644
--- a/drivers/net/ioc3-eth.c
+++ b/drivers/net/ioc3-eth.c
@@ -5,6 +5,7 @@
  *
  * Driver for SGI's IOC3 based Ethernet cards as found in the PCI card.
  *
+ * Copyright (C) 2005 Stanislaw Skowronek (port to meta-driver)
  * Copyright (C) 1999, 2000, 2001, 2003 Ralf Baechle
  * Copyright (C) 1995, 1999, 2000, 2001 by Silicon Graphics, Inc.
  *
@@ -20,15 +21,13 @@
  *  o Use prefetching for large packets.  What is a good lower limit for
  *    prefetching?
  *  o We're probably allocating a bit too much memory.
- *  o Use hardware checksums.
- *  o Convert to using a IOC3 meta driver.
  *  o Which PHYs might possibly be attached to the IOC3 in real live,
  *    which workarounds are required for them?  Do we ever have Lucent's?
  *  o For the 2.5 branch kill the mii-tool ioctls.
  */
 
 #define IOC3_NAME	"ioc3-eth"
-#define IOC3_VERSION	"2.6.3-3"
+#define IOC3_VERSION	"2.6.4-s2"
 
 #include <linux/config.h>
 #include <linux/init.h>
@@ -45,11 +44,6 @@
 #include <linux/tcp.h>
 #include <linux/udp.h>
 
-#ifdef CONFIG_SERIAL_8250
-#include <linux/serial_core.h>
-#include <linux/serial_8250.h>
-#endif
-
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/ethtool.h>
@@ -61,14 +55,19 @@
 #include <asm/io.h>
 #include <asm/pgtable.h>
 #include <asm/uaccess.h>
+
+#ifdef CONFIG_SGI_IP30
+#include <asm/mach-ip30/addrs.h>
+#else
 #include <asm/sn/types.h>
 #include <asm/sn/sn0/addrs.h>
 #include <asm/sn/sn0/hubni.h>
 #include <asm/sn/sn0/hubio.h>
 #include <asm/sn/klconfig.h>
-#include <asm/sn/ioc3.h>
 #include <asm/sn/sn0/ip27.h>
+#endif
 #include <asm/pci/bridge.h>
+#include <linux/ioc3.h>
 
 /*
  * 64 RX buffers.  This is tunable in the range of 16 <= x < 512.  The
@@ -81,6 +80,7 @@
 
 /* Private per NIC data of the driver.  */
 struct ioc3_private {
+	struct ioc3_driver_data *idd;
 	struct ioc3 *regs;
 	unsigned long *rxr;		/* pointer to receiver ring */
 	struct ioc3_etxd *txr;
@@ -149,8 +149,15 @@ static inline unsigned long ioc3_map(voi
 	return vdev | (0xaUL << PCI64_ATTR_TARG_SHFT) | PCI64_ATTR_PREF |
 	       ((unsigned long)ptr & TO_PHYS_MASK);
 #else
+#ifdef CONFIG_SGI_IP30
+	vdev <<= 58;   /* Shift to PCI64_ATTR_VIRTUAL */
+
+	return vdev | (0x8UL << PCI64_ATTR_TARG_SHFT) | PCI64_ATTR_PREF |
+	       ((unsigned long)ptr & TO_PHYS_MASK);
+#else
 	return virt_to_bus(ptr);
 #endif
+#endif
 }
 
 /* BEWARE: The IOC3 documentation documents the size of rx buffers as
@@ -226,219 +233,6 @@ static inline unsigned long ioc3_map(voi
 #define ioc3_r_midr_w()		be32_to_cpu(ioc3->midr_w)
 #define ioc3_w_midr_w(v)	do { ioc3->midr_w = cpu_to_be32(v); } while (0)
 
-static inline u32 mcr_pack(u32 pulse, u32 sample)
-{
-	return (pulse << 10) | (sample << 2);
-}
-
-static int nic_wait(struct ioc3 *ioc3)
-{
-	u32 mcr;
-
-        do {
-                mcr = ioc3_r_mcr();
-        } while (!(mcr & 2));
-
-        return mcr & 1;
-}
-
-static int nic_reset(struct ioc3 *ioc3)
-{
-        int presence;
-
-	ioc3_w_mcr(mcr_pack(500, 65));
-	presence = nic_wait(ioc3);
-
-	ioc3_w_mcr(mcr_pack(0, 500));
-	nic_wait(ioc3);
-
-        return presence;
-}
-
-static inline int nic_read_bit(struct ioc3 *ioc3)
-{
-	int result;
-
-	ioc3_w_mcr(mcr_pack(6, 13));
-	result = nic_wait(ioc3);
-	ioc3_w_mcr(mcr_pack(0, 100));
-	nic_wait(ioc3);
-
-	return result;
-}
-
-static inline void nic_write_bit(struct ioc3 *ioc3, int bit)
-{
-	if (bit)
-		ioc3_w_mcr(mcr_pack(6, 110));
-	else
-		ioc3_w_mcr(mcr_pack(80, 30));
-
-	nic_wait(ioc3);
-}
-
-/*
- * Read a byte from an iButton device
- */
-static u32 nic_read_byte(struct ioc3 *ioc3)
-{
-	u32 result = 0;
-	int i;
-
-	for (i = 0; i < 8; i++)
-		result = (result >> 1) | (nic_read_bit(ioc3) << 7);
-
-	return result;
-}
-
-/*
- * Write a byte to an iButton device
- */
-static void nic_write_byte(struct ioc3 *ioc3, int byte)
-{
-	int i, bit;
-
-	for (i = 8; i; i--) {
-		bit = byte & 1;
-		byte >>= 1;
-
-		nic_write_bit(ioc3, bit);
-	}
-}
-
-static u64 nic_find(struct ioc3 *ioc3, int *last)
-{
-	int a, b, index, disc;
-	u64 address = 0;
-
-	nic_reset(ioc3);
-	/* Search ROM.  */
-	nic_write_byte(ioc3, 0xf0);
-
-	/* Algorithm from ``Book of iButton Standards''.  */
-	for (index = 0, disc = 0; index < 64; index++) {
-		a = nic_read_bit(ioc3);
-		b = nic_read_bit(ioc3);
-
-		if (a && b) {
-			printk("NIC search failed (not fatal).\n");
-			*last = 0;
-			return 0;
-		}
-
-		if (!a && !b) {
-			if (index == *last) {
-				address |= 1UL << index;
-			} else if (index > *last) {
-				address &= ~(1UL << index);
-				disc = index;
-			} else if ((address & (1UL << index)) == 0)
-				disc = index;
-			nic_write_bit(ioc3, address & (1UL << index));
-			continue;
-		} else {
-			if (a)
-				address |= 1UL << index;
-			else
-				address &= ~(1UL << index);
-			nic_write_bit(ioc3, a);
-			continue;
-		}
-	}
-
-	*last = disc;
-
-	return address;
-}
-
-static int nic_init(struct ioc3 *ioc3)
-{
-	const char *type;
-	u8 crc;
-	u8 serial[6];
-	int save = 0, i;
-
-	type = "unknown";
-
-	while (1) {
-		u64 reg;
-		reg = nic_find(ioc3, &save);
-
-		switch (reg & 0xff) {
-		case 0x91:
-			type = "DS1981U";
-			break;
-		default:
-			if (save == 0) {
-				/* Let the caller try again.  */
-				return -1;
-			}
-			continue;
-		}
-
-		nic_reset(ioc3);
-
-		/* Match ROM.  */
-		nic_write_byte(ioc3, 0x55);
-		for (i = 0; i < 8; i++)
-			nic_write_byte(ioc3, (reg >> (i << 3)) & 0xff);
-
-		reg >>= 8; /* Shift out type.  */
-		for (i = 0; i < 6; i++) {
-			serial[i] = reg & 0xff;
-			reg >>= 8;
-		}
-		crc = reg & 0xff;
-		break;
-	}
-
-	printk("Found %s NIC", type);
-	if (type != "unknown") {
-		printk (" registration number %02x:%02x:%02x:%02x:%02x:%02x,"
-			" CRC %02x", serial[0], serial[1], serial[2],
-			serial[3], serial[4], serial[5], crc);
-	}
-	printk(".\n");
-
-	return 0;
-}
-
-/*
- * Read the NIC (Number-In-a-Can) device used to store the MAC address on
- * SN0 / SN00 nodeboards and PCI cards.
- */
-static void ioc3_get_eaddr_nic(struct ioc3_private *ip)
-{
-	struct ioc3 *ioc3 = ip->regs;
-	u8 nic[14];
-	int tries = 2; /* There may be some problem with the battery?  */
-	int i;
-
-	ioc3_w_gpcr_s(1 << 21);
-
-	while (tries--) {
-		if (!nic_init(ioc3))
-			break;
-		udelay(500);
-	}
-
-	if (tries < 0) {
-		printk("Failed to read MAC address\n");
-		return;
-	}
-
-	/* Read Memory.  */
-	nic_write_byte(ioc3, 0xf0);
-	nic_write_byte(ioc3, 0x00);
-	nic_write_byte(ioc3, 0x00);
-
-	for (i = 13; i >= 0; i--)
-		nic[i] = nic_read_byte(ioc3);
-
-	for (i = 2; i < 8; i++)
-		priv_netdev(ip)->dev_addr[i - 2] = nic[i];
-}
-
 /*
  * Ok, this is hosed by design.  It's necessary to know what machine the
  * NIC is in in order to know how to read the NIC address.  We also have
@@ -446,12 +240,16 @@ static void ioc3_get_eaddr_nic(struct io
  */
 static void ioc3_get_eaddr(struct ioc3_private *ip)
 {
-	int i;
+	int i,nz=0;
 
-
-	ioc3_get_eaddr_nic(ip);
+	for(i=0;i<6;i++)
+		nz |= (priv_netdev(ip)->dev_addr[i] = ip->idd->nic_mac[i]);
 
 	printk("Ethernet address is ");
+	if(!nz) {
+		printk("unreadable.\n");
+		return;
+	}
 	for (i = 0; i < 6; i++) {
 		printk("%02x", priv_netdev(ip)->dev_addr[i]);
 		if (i < 5)
@@ -750,9 +548,9 @@ static void ioc3_error(struct ioc3_priva
 
 /* The interrupt handler does all of the Rx thread work and cleans up
    after the Tx thread.  */
-static irqreturn_t ioc3_interrupt(int irq, void *_dev, struct pt_regs *regs)
+static int ioc3eth_intr(struct ioc3_submodule *is, struct ioc3_driver_data *idd, unsigned int irq, struct pt_regs *regs)
 {
-	struct net_device *dev = (struct net_device *)_dev;
+	struct net_device *dev = (struct net_device *)(idd->data[is->id]);
 	struct ioc3_private *ip = netdev_priv(dev);
 	struct ioc3 *ioc3 = ip->regs;
 	const u32 enabled = EISR_RXTIMERINT | EISR_RXOFLO | EISR_RXBUFOFLO |
@@ -773,7 +571,7 @@ static irqreturn_t ioc3_interrupt(int ir
 	if (eisr & EISR_TXEXPLICIT)
 		ioc3_tx(ip);
 
-	return IRQ_HANDLED;
+	return 0;
 }
 
 static inline void ioc3_setup_duplex(struct ioc3_private *ip)
@@ -840,6 +638,7 @@ static int ioc3_mii_init(struct ioc3_pri
 	ip->ioc3_timer.expires = jiffies + (12 * HZ)/10;  /* 1.2 sec. */
 	ip->ioc3_timer.data = (unsigned long) ip;
 	ip->ioc3_timer.function = &ioc3_timer;
+
 	add_timer(&ip->ioc3_timer);
 
 out:
@@ -1026,7 +825,7 @@ static void ioc3_init(struct net_device 
 	(void) ioc3_r_emcr();
 
 	/* Misc registers  */
-#ifdef CONFIG_SGI_IP27
+#if (defined CONFIG_SGI_IP27) || (defined CONFIG_SGI_IP30)
 	ioc3_w_erbar(PCI64_ATTR_BAR >> 32);	/* Barrier on last store */
 #else
 	ioc3_w_erbar(0);			/* Let PCI API get it right */
@@ -1063,12 +862,6 @@ static int ioc3_open(struct net_device *
 {
 	struct ioc3_private *ip = netdev_priv(dev);
 
-	if (request_irq(dev->irq, ioc3_interrupt, SA_SHIRQ, ioc3_str, dev)) {
-		printk(KERN_ERR "%s: Can't get irq %d\n", dev->name, dev->irq);
-
-		return -EAGAIN;
-	}
-
 	ip->ehar_h = 0;
 	ip->ehar_l = 0;
 	ioc3_init(dev);
@@ -1086,105 +879,12 @@ static int ioc3_close(struct net_device 
 	netif_stop_queue(dev);
 
 	ioc3_stop(ip);
-	free_irq(dev->irq, dev);
 
 	ioc3_free_rings(ip);
 	return 0;
 }
 
-/*
- * MENET cards have four IOC3 chips, which are attached to two sets of
- * PCI slot resources each: the primary connections are on slots
- * 0..3 and the secondaries are on 4..7
- *
- * All four ethernets are brought out to connectors; six serial ports
- * (a pair from each of the first three IOC3s) are brought out to
- * MiniDINs; all other subdevices are left swinging in the wind, leave
- * them disabled.
- */
-static inline int ioc3_is_menet(struct pci_dev *pdev)
-{
-	struct pci_dev *dev;
-
-	return pdev->bus->parent == NULL
-	       && (dev = pci_find_slot(pdev->bus->number, PCI_DEVFN(0, 0)))
-	       && dev->vendor == PCI_VENDOR_ID_SGI
-	       && dev->device == PCI_DEVICE_ID_SGI_IOC3
-	       && (dev = pci_find_slot(pdev->bus->number, PCI_DEVFN(1, 0)))
-	       && dev->vendor == PCI_VENDOR_ID_SGI
-	       && dev->device == PCI_DEVICE_ID_SGI_IOC3
-	       && (dev = pci_find_slot(pdev->bus->number, PCI_DEVFN(2, 0)))
-	       && dev->vendor == PCI_VENDOR_ID_SGI
-	       && dev->device == PCI_DEVICE_ID_SGI_IOC3;
-}
-
-#ifdef CONFIG_SERIAL_8250
-/*
- * Note about serial ports and consoles:
- * For console output, everyone uses the IOC3 UARTA (offset 0x178)
- * connected to the master node (look in ip27_setup_console() and
- * ip27prom_console_write()).
- *
- * For serial (/dev/ttyS0 etc), we can not have hardcoded serial port
- * addresses on a partitioned machine. Since we currently use the ioc3
- * serial ports, we use dynamic serial port discovery that the serial.c
- * driver uses for pci/pnp ports (there is an entry for the SGI ioc3
- * boards in pci_boards[]). Unfortunately, UARTA's pio address is greater
- * than UARTB's, although UARTA on o200s has traditionally been known as
- * port 0. So, we just use one serial port from each ioc3 (since the
- * serial driver adds addresses to get to higher ports).
- *
- * The first one to do a register_console becomes the preferred console
- * (if there is no kernel command line console= directive). /dev/console
- * (ie 5, 1) is then "aliased" into the device number returned by the
- * "device" routine referred to in this console structure
- * (ip27prom_console_dev).
- *
- * Also look in ip27-pci.c:pci_fixup_ioc3() for some comments on working
- * around ioc3 oddities in this respect.
- *
- * The IOC3 serials use a 22MHz clock rate with an additional divider by 3.
- */
-
-static void __devinit ioc3_serial_probe(struct pci_dev *pdev, struct ioc3 *ioc3)
-{
-	struct uart_port port;
-
-	/*
-	 * We need to recognice and treat the fourth MENET serial as it
-	 * does not have an SuperIO chip attached to it, therefore attempting
-	 * to access it will result in bus errors.  We call something an
-	 * MENET if PCI slot 0, 1, 2 and 3 of a master PCI bus all have an IOC3
-	 * in it.  This is paranoid but we want to avoid blowing up on a
-	 * showhorn PCI box that happens to have 4 IOC3 cards in it so it's
-	 * not paranoid enough ...
-	 */
-	if (ioc3_is_menet(pdev) && PCI_SLOT(pdev->devfn) == 3)
-		return;
-
-	/*
-	 * Register to interrupt zero because we share the interrupt with
-	 * the serial driver which we don't properly support yet.
-	 *
-	 * Can't use UPF_IOREMAP as the whole of IOC3 resources have already
-	 * been registered.
-	 */
-	memset(&port, 0, sizeof(port));
-	port.irq      = 0;
-	port.flags    = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF;
-	port.iotype   = UPIO_MEM;
-	port.regshift = 0;
-	port.uartclk  = 22000000 / 3;
-
-	port.membase  = (unsigned char *) &ioc3->sregs.uarta;
-	serial8250_register_port(&port);
-
-	port.membase  = (unsigned char *) &ioc3->sregs.uartb;
-	serial8250_register_port(&port);
-}
-#endif
-
-static int ioc3_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+static int ioc3eth_probe(struct ioc3_submodule *is, struct ioc3_driver_data *idd)
 {
 	unsigned int sw_physid1, sw_physid2;
 	struct net_device *dev = NULL;
@@ -1194,63 +894,29 @@ static int ioc3_probe(struct pci_dev *pd
 	u32 vendor, model, rev;
 	int err, pci_using_dac;
 
-	/* Configure DMA attributes. */
-	err = pci_set_dma_mask(pdev, 0xffffffffffffffffULL);
-	if (!err) {
-		pci_using_dac = 1;
-		err = pci_set_consistent_dma_mask(pdev, 0xffffffffffffffffULL);
-		if (err < 0) {
-			printk(KERN_ERR "%s: Unable to obtain 64 bit DMA "
-			       "for consistent allocations\n", pci_name(pdev));
-			goto out;
-		}
-	} else {
-		err = pci_set_dma_mask(pdev, 0xffffffffULL);
-		if (err) {
-			printk(KERN_ERR "%s: No usable DMA configuration, "
-			       "aborting.\n", pci_name(pdev));
-			goto out;
-		}
-		pci_using_dac = 0;
-	}
-
-	if (pci_enable_device(pdev))
-		return -ENODEV;
+	/* check for board type */
+	if(idd->class == IOC3_CLASS_SERIAL)
+		return 1;
 
 	dev = alloc_etherdev(sizeof(struct ioc3_private));
 	if (!dev) {
 		err = -ENOMEM;
 		goto out_disable;
 	}
+	idd->data[is->id] = dev;
 
-	if (pci_using_dac)
-		dev->features |= NETIF_F_HIGHDMA;
-
-	err = pci_request_regions(pdev, "ioc3");
-	if (err)
-		goto out_free;
+	/* assume we always have DAC */
+	dev->features |= NETIF_F_HIGHDMA;
 
 	SET_MODULE_OWNER(dev);
-	SET_NETDEV_DEV(dev, &pdev->dev);
+	SET_NETDEV_DEV(dev, &(idd->pdev->dev));
 
 	ip = netdev_priv(dev);
 
-	dev->irq = pdev->irq;
+	dev->irq = idd->pdev->irq;
 
-	ioc3_base = pci_resource_start(pdev, 0);
-	ioc3_size = pci_resource_len(pdev, 0);
-	ioc3 = (struct ioc3 *) ioremap(ioc3_base, ioc3_size);
-	if (!ioc3) {
-		printk(KERN_CRIT "ioc3eth(%s): ioremap failed, goodbye.\n",
-		       pci_name(pdev));
-		err = -ENOMEM;
-		goto out_res;
-	}
-	ip->regs = ioc3;
-
-#ifdef CONFIG_SERIAL_8250
-	ioc3_serial_probe(pdev, ioc3);
-#endif
+	ip->idd = idd;
+	ip->regs = ioc3 = idd->vma;
 
 	spin_lock_init(&ip->ioc3_lock);
 	init_timer(&ip->ioc3_timer);
@@ -1258,7 +924,7 @@ static int ioc3_probe(struct pci_dev *pd
 	ioc3_stop(ip);
 	ioc3_init(dev);
 
-	ip->pdev = pdev;
+	ip->pdev = idd->pdev;
 
 	ip->mii.phy_id_mask = 0x1f;
 	ip->mii.reg_num_mask = 0x1f;
@@ -1270,7 +936,7 @@ static int ioc3_probe(struct pci_dev *pd
 
 	if (ip->mii.phy_id == -1) {
 		printk(KERN_CRIT "ioc3-eth(%s): Didn't find a PHY, goodbye.\n",
-		       pci_name(pdev));
+		       pci_name(idd->pdev));
 		err = -ENODEV;
 		goto out_stop;
 	}
@@ -1316,56 +982,40 @@ static int ioc3_probe(struct pci_dev *pd
 out_stop:
 	ioc3_stop(ip);
 	ioc3_free_rings(ip);
-out_res:
-	pci_release_regions(pdev);
-out_free:
 	free_netdev(dev);
 out_disable:
-	/*
-	 * We should call pci_disable_device(pdev); here if the IOC3 wasn't
-	 * such a weird device ...
-	 */
 out:
 	return err;
 }
 
-static void __devexit ioc3_remove_one (struct pci_dev *pdev)
+static int ioc3eth_remove(struct ioc3_submodule *is, struct ioc3_driver_data *idd)
 {
-	struct net_device *dev = pci_get_drvdata(pdev);
+	struct net_device *dev = idd->data[is->id];
 	struct ioc3_private *ip = netdev_priv(dev);
-	struct ioc3 *ioc3 = ip->regs;
 
 	unregister_netdev(dev);
-	iounmap(ioc3);
-	pci_release_regions(pdev);
 	free_netdev(dev);
-	/*
-	 * We should call pci_disable_device(pdev); here if the IOC3 wasn't
-	 * such a weird device ...
-	 */
+	return 0;
 }
 
-static struct pci_device_id ioc3_pci_tbl[] = {
-	{ PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3, PCI_ANY_ID, PCI_ANY_ID },
-	{ 0 }
-};
-MODULE_DEVICE_TABLE(pci, ioc3_pci_tbl);
-
-static struct pci_driver ioc3_driver = {
-	.name		= "ioc3-eth",
-	.id_table	= ioc3_pci_tbl,
-	.probe		= ioc3_probe,
-	.remove		= __devexit_p(ioc3_remove_one),
+static struct ioc3_submodule ioc3eth_submodule = {
+	.name = "ethernet",
+	.probe = ioc3eth_probe,
+	.remove = ioc3eth_remove,
+	.ethernet = 1,
+	.intr = ioc3eth_intr,
+	.owner = THIS_MODULE,
 };
 
 static int __init ioc3_init_module(void)
 {
-	return pci_register_driver(&ioc3_driver);
+	ioc3_register_submodule(&ioc3eth_submodule);
+	return 0;
 }
 
 static void __exit ioc3_cleanup_module(void)
 {
-	pci_unregister_driver(&ioc3_driver);
+	ioc3_unregister_submodule(&ioc3eth_submodule);
 }
 
 static int ioc3_start_xmit(struct sk_buff *skb, struct net_device *dev)

-- 
Martin Michlmayr
http://www.cyrius.com/

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: Merging Skylark's IOC3 patch
  2006-02-19 21:15 ` Martin Michlmayr
                   ` (3 preceding siblings ...)
  (?)
@ 2006-02-19 21:57 ` Martin Michlmayr
  2006-02-19 22:07   ` Russell King
  -1 siblings, 1 reply; 13+ messages in thread
From: Martin Michlmayr @ 2006-02-19 21:57 UTC (permalink / raw)
  To: Ralf Baechle, rmk+serial; +Cc: linux-mips, Stanislaw Skowronek

* Martin Michlmayr <tbm@cyrius.com> [2006-02-19 21:15]:
> Can you please review and/or merge Skylark's IOC3 patch from
> ftp://ftp.linux-mips.org/pub/linux/mips/people/skylark/linux-mips-2.6.14-ioc3-r26.patch.bz2
> 
> From my basic understanding I believe that this patch needs to be split up
> and submitted to different sub-system maintainers.

(Russell, this is only a RFC for now since the main support patch
for IOC3 has not been merged yet.  But please do comment)


From: Stanislaw Skowronek <skylark@linux-mips.org>

[PATCH 4/6] serial: UART support for SGI IOC3 bridge

Add UART support for the SGI IOC3 bridge, used in Origin and Octane
machines.

Signed-off-by: Stanislaw Skowronek <skylark@linux-mips.org>
Signed-off-by: Martin Michlmayr <tbm@cyrius.com>

---

 drivers/serial/8250.c        |   17 ++++-
 drivers/serial/Kconfig       |    7 ++
 drivers/serial/Makefile      |    1 
 drivers/serial/ioc3uart.c    |  135 +++++++++++++++++++++++++++++++++++++++++++
 drivers/serial/serial_core.c |   12 ++-
 include/linux/serial.h       |    1 
 include/linux/serial_core.h  |    1 
 7 files changed, 169 insertions(+), 5 deletions(-)

--- a/drivers/serial/8250.c
+++ b/drivers/serial/8250.c
@@ -310,6 +310,9 @@ static unsigned int serial_in(struct uar
 	case UPIO_MEM:
 		return readb(up->port.membase + offset);
 
+	case UPIO_IOC3:
+		return readb(up->port.membase + (offset^3));
+
 	case UPIO_MEM32:
 		return readl(up->port.membase + offset);
 
@@ -338,6 +341,10 @@ serial_out(struct uart_8250_port *up, in
 		writeb(value, up->port.membase + offset);
 		break;
 
+	case UPIO_IOC3:
+		writeb(value, up->port.membase + (offset^3));
+		break;
+
 	case UPIO_MEM32:
 		writel(value, up->port.membase + offset);
 		break;
@@ -1906,6 +1913,8 @@ static int serial8250_request_std_resour
 	int ret = 0;
 
 	switch (up->port.iotype) {
+	case UPIO_IOC3:
+		break;
 	case UPIO_MEM:
 		if (!up->port.mapbase)
 			break;
@@ -1938,6 +1947,8 @@ static void serial8250_release_std_resou
 	unsigned int size = 8 << up->port.regshift;
 
 	switch (up->port.iotype) {
+	case UPIO_IOC3:
+		break;
 	case UPIO_MEM:
 		if (!up->port.mapbase)
 			break;
@@ -2300,8 +2311,10 @@ int __init serial8250_start_console(stru
 
 	add_preferred_console("ttyS", line, options);
 	printk("Adding console on ttyS%d at %s 0x%lx (options '%s')\n",
-		line, port->iotype == UPIO_MEM ? "MMIO" : "I/O port",
-		port->iotype == UPIO_MEM ? (unsigned long) port->mapbase :
+		line, port->iotype == UPIO_MEM ? "MMIO" : 
+		port->iotype == UPIO_IOC3 ? "IOC3" : "I/O port",
+		(port->iotype == UPIO_MEM || port->iotype == UPIO_IOC3) ?
+		    (unsigned long) port->mapbase :
 		    (unsigned long) port->iobase, options);
 	if (!(serial8250_console.flags & CON_ENABLED)) {
 		serial8250_console.flags &= ~CON_PRINTBUFFER;
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index baa8417..0f1102f 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -225,6 +225,13 @@ config SERIAL_8250_AU1X00
 	  to this option.  The driver can handle 1 or 2 serial ports.
 	  If unsure, say N.
 
+config SGI_IOC3_UART
+	bool "SGI IOC3 UART support"
+	depends on SGI_IOC3 && SERIAL_8250
+	help
+	  Enable this if you have a SGI Origin or Octane machine. This module
+	  provides serial port support for IOC3 chips on those systems.
+
 comment "Non-8250 serial port support"
 
 config SERIAL_AMBA_PL010
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index 738f0a0..5ea2e3e 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o
 obj-$(CONFIG_SERIAL_JSM) += jsm/
 obj-$(CONFIG_SERIAL_TXX9) += serial_txx9.o
 obj-$(CONFIG_SERIAL_VR41XX) += vr41xx_siu.o
+obj-$(CONFIG_SGI_IOC3_UART) += ioc3uart.o
 obj-$(CONFIG_SERIAL_SGI_IOC4) += ioc4_serial.o
 obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o
 obj-$(CONFIG_SERIAL_AT91) += at91_serial.o
diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c
index 95fb493..40db05c 100644
--- a/drivers/serial/serial_core.c
+++ b/drivers/serial/serial_core.c
@@ -1636,9 +1636,10 @@ static int uart_line_info(char *buf, str
 
 	ret = sprintf(buf, "%d: uart:%s %s%08lX irq:%d",
 			port->line, uart_type(port),
-			port->iotype == UPIO_MEM ? "mmio:0x" : "port:",
-			port->iotype == UPIO_MEM ? port->mapbase :
-						(unsigned long) port->iobase,
+			port->iotype == UPIO_MEM ? "mmio:0x" :
+			  port->iotype == UPIO_IOC3 ? "ioc3:0x" : "port:",
+			(port->iotype == UPIO_MEM || port->iotype == UPIO_IOC3) ?
+			  port->mapbase : (unsigned long) port->iobase,
 			port->irq);
 
 	if (port->type == PORT_UNKNOWN) {
@@ -1981,6 +1982,10 @@ uart_report_port(struct uart_driver *drv
 		snprintf(address, sizeof(address),
 			 "MMIO 0x%lx", port->mapbase);
 		break;
+	case UPIO_IOC3:
+		snprintf(address, sizeof(address), "IOC3 0x%lx",
+			 port->mapbase);
+		break;
 	default:
 		strlcpy(address, "*unknown*", sizeof(address));
 		break;
@@ -2311,6 +2316,7 @@ int uart_match_port(struct uart_port *po
 		return (port1->iobase == port2->iobase) &&
 		       (port1->hub6   == port2->hub6);
 	case UPIO_MEM:
+	case UPIO_IOC3:
 		return (port1->mapbase == port2->mapbase);
 	}
 	return 0;
diff --git a/include/linux/serial.h b/include/linux/serial.h
index c69c6b9..a3ee515 100644
--- a/include/linux/serial.h
+++ b/include/linux/serial.h
@@ -82,6 +82,7 @@ struct serial_struct {
 #define SERIAL_IO_PORT	0
 #define SERIAL_IO_HUB6	1
 #define SERIAL_IO_MEM	2
+#define SERIAL_IO_IOC3	3
 
 struct serial_uart_config {
 	char	*name;
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 4041122..9441c90 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -221,6 +221,7 @@ struct uart_port {
 #define UPIO_MEM		(2)
 #define UPIO_MEM32		(3)
 #define UPIO_AU			(4)			/* Au1x00 type IO */
+#define UPIO_IOC3		(5)
 
 	unsigned int		read_status_mask;	/* driver specific */
 	unsigned int		ignore_status_mask;	/* driver specific */
--- /dev/null	2006-02-13 15:11:07.474148640 +0000
+++ b/drivers/serial/ioc3uart.c	2006-02-19 21:26:13.000000000 +0000
@@ -0,0 +1,135 @@
+/*
+ * SGI IOC3 bridge for UARTs
+ *
+ * Copyright (C) 2005 Stanislaw Skowronek <skylark@linux-mips.org>
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/err.h>
+#include <linux/pci.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <linux/ioc3.h>
+
+#include <linux/serial.h>
+#include <asm/serial.h>
+
+#include "8250.h"
+
+#define IOC3_UARTCLK (22000000 / 3)
+
+MODULE_AUTHOR("Stanislaw Skowronek <skylark@linux-mips.org>");
+MODULE_DESCRIPTION("SGI IOC3 UART driver");
+MODULE_LICENSE("GPL");
+
+/* !!! write dynirq support for IP27 !!! */
+#ifdef CONFIG_SGI_IP30
+int new_dynamic_irq(void);
+void call_dynamic_irq(int irq, struct pt_regs *regs);
+void delete_dynamic_irq(int irq);
+#else
+int new_dynamic_irq(void) { return 0; }
+void call_dynamic_irq(int irq, struct pt_regs *regs) { }
+void delete_dynamic_irq(int irq) { }
+#endif
+
+struct ioc3uart_data {
+	int line_a, line_b;
+	int irq;
+};
+
+static int ioc3uart_intr(struct ioc3_submodule *is, struct ioc3_driver_data *idd, unsigned int irq, struct pt_regs *regs)
+{
+	struct ioc3uart_data *d = (struct ioc3uart_data *)(idd->data[is->id]);
+
+	ioc3_ack(is, idd, irq);
+	call_dynamic_irq(d->irq, regs);
+
+	return 0;
+}
+
+static int ioc3uart_probe(struct ioc3_submodule *is, struct ioc3_driver_data *idd)
+{
+	struct uart_port port;
+	struct ioc3uart_data *d;
+
+	/* check for UART-less add-on boards */
+	if(idd->class == IOC3_CLASS_MENET_4 || idd->class == IOC3_CLASS_CADDUO)
+		return 1;
+
+	/* set PIO mode for SuperIO UARTs */
+	idd->vma->sscr_a = 0;
+	idd->vma->sscr_b = 0;
+	udelay(1000);
+	idd->vma->sregs.uarta.iu_fcr = 0;
+	idd->vma->sregs.uartb.iu_fcr = 0;
+	udelay(1000);
+
+	d = kmalloc(sizeof(struct ioc3uart_data), GFP_KERNEL);
+	idd->data[is->id] = d;
+	d->irq = new_dynamic_irq();
+
+	/* register serial ports with 8250.c */
+	memset(&port, 0, sizeof(struct uart_port));
+	port.irq = d->irq;
+	port.flags = UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ;
+	port.uartclk = IOC3_UARTCLK;
+	port.iotype = UPIO_IOC3;
+	port.regshift = 0;
+	port.dev = &(idd->pdev->dev);
+
+	port.membase = (unsigned char *) &idd->vma->sregs.uarta;
+	port.mapbase = ((unsigned long) port.membase) & 0xFFFFFFFFFF;
+	d->line_a = serial8250_register_port(&port);
+
+	port.membase = (unsigned char *) &idd->vma->sregs.uartb;
+	port.mapbase = ((unsigned long) port.membase) & 0xFFFFFFFFFF;
+	d->line_b = serial8250_register_port(&port);
+
+	ioc3_enable(is, idd);
+	return 0;
+}
+
+static int ioc3uart_remove(struct ioc3_submodule *is, struct ioc3_driver_data *idd)
+{
+	struct ioc3uart_data *d = (struct ioc3uart_data *)(idd->data[is->id]);
+	serial8250_unregister_port(d->line_a);
+	serial8250_unregister_port(d->line_b);
+	delete_dynamic_irq(d->irq);
+	kfree(d);
+	idd->data[is->id] = NULL;
+	return 0;
+}
+
+static struct ioc3_submodule ioc3uart_submodule = {
+	.name = "uart",
+	.probe = ioc3uart_probe,
+	.remove = ioc3uart_remove,
+	.irq_mask = SIO_IR_SA_INT | SIO_IR_SB_INT,
+	.intr = ioc3uart_intr,
+	.owner = THIS_MODULE,
+};
+
+static int __init ioc3uart_init(void)
+{
+	ioc3_register_submodule(&ioc3uart_submodule);
+	return 0;
+}
+
+static void __exit ioc3uart_exit(void)
+{
+	ioc3_unregister_submodule(&ioc3uart_submodule);
+}
+
+module_init(ioc3uart_init);
+module_exit(ioc3uart_exit);

-- 
Martin Michlmayr
http://www.cyrius.com/

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: Merging Skylark's IOC3 patch
  2006-02-19 21:15 ` Martin Michlmayr
                   ` (4 preceding siblings ...)
  (?)
@ 2006-02-19 21:58 ` Martin Michlmayr
  2006-02-19 22:01   ` Martin Michlmayr
  -1 siblings, 1 reply; 13+ messages in thread
From: Martin Michlmayr @ 2006-02-19 21:58 UTC (permalink / raw)
  To: Ralf Baechle; +Cc: linux-mips, Stanislaw Skowronek

Ralf, please apply.


From: Stanislaw Skowronek <skylark@linux-mips.org>

[PATCH 5/6] [MIPS] Make 8-bit devices on PCI work on IP27

Fix the definition of __swizzle_addr_b and make 8-bit PCI devices work
on IP27.

Signed-off-by: Stanislaw Skowronek <skylark@linux-mips.org>
Signed-off-by: Martin Michlmayr <tbm@cyrius.com>

---

 mangle-port.h |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/asm-mips/mach-ip27/mangle-port.h b/include/asm-mips/mach-ip27/mangle-port.h
index f76c448..83e12e3 100644
--- a/include/asm-mips/mach-ip27/mangle-port.h
+++ b/include/asm-mips/mach-ip27/mangle-port.h
@@ -8,7 +8,7 @@
 #ifndef __ASM_MACH_IP27_MANGLE_PORT_H
 #define __ASM_MACH_IP27_MANGLE_PORT_H
 
-#define __swizzle_addr_b(port)	(port)
+#define __swizzle_addr_b(port)	((port) ^ 3)
 #define __swizzle_addr_w(port)	((port) ^ 2)
 #define __swizzle_addr_l(port)	(port)
 #define __swizzle_addr_q(port)	(port)

-- 
Martin Michlmayr
http://www.cyrius.com/

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: Merging Skylark's IOC3 patch
  2006-02-19 21:15 ` Martin Michlmayr
                   ` (5 preceding siblings ...)
  (?)
@ 2006-02-19 21:58 ` Martin Michlmayr
  -1 siblings, 0 replies; 13+ messages in thread
From: Martin Michlmayr @ 2006-02-19 21:58 UTC (permalink / raw)
  To: linux-mips, Stanislaw Skowronek

To be applied after the IOC3 UART driver has been accepted.


From: Stanislaw Skowronek <skylark@linux-mips.org>

[PATCH 6/6] [MIPS] Switch IP27 to the new IOC3 UART driver

Signed-off-by: Stanislaw Skowronek <skylark@linux-mips.org>
Signed-off-by: Martin Michlmayr <tbm@cyrius.com>

---

 ip27-console.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/mips/sgi-ip27/ip27-console.c b/arch/mips/sgi-ip27/ip27-console.c
index 3e1ac29..a973610 100644
--- a/arch/mips/sgi-ip27/ip27-console.c
+++ b/arch/mips/sgi-ip27/ip27-console.c
@@ -64,7 +64,7 @@ static void inline ioc3_console_probe(vo
 	up.irq		= 0;
 	up.uartclk	= IOC3_CLK;
 	up.regshift	= 0;
-	up.iotype	= UPIO_MEM;
+	up.iotype	= UPIO_IOC3;
 	up.flags	= IOC3_FLAGS;
 	up.line		= 0;
 
-- 
Martin Michlmayr
http://www.cyrius.com/

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: Merging Skylark's IOC3 patch
  2006-02-19 21:58 ` Martin Michlmayr
@ 2006-02-19 22:01   ` Martin Michlmayr
  2006-02-19 22:10     ` Martin Michlmayr
  0 siblings, 1 reply; 13+ messages in thread
From: Martin Michlmayr @ 2006-02-19 22:01 UTC (permalink / raw)
  To: Ralf Baechle; +Cc: linux-mips, Stanislaw Skowronek

* Martin Michlmayr <tbm@cyrius.com> [2006-02-19 21:58]:
> Ralf, please apply.

or maybe not.

21:59 < Skylark> The 6/6 isn't exactly right
22:00 < Skylark> It should be a part of the 5/6, really (making it 5/5)
22:00 < Skylark> And dependent on 4/6

-- 
Martin Michlmayr
http://www.cyrius.com/

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Merging Skylark's IOC3 patch
  2006-02-19 21:54 ` Martin Michlmayr
@ 2006-02-19 22:04   ` Martin Michlmayr
  0 siblings, 0 replies; 13+ messages in thread
From: Martin Michlmayr @ 2006-02-19 22:04 UTC (permalink / raw)
  To: Ralf Baechle; +Cc: linux-mips, Stanislaw Skowronek

* Martin Michlmayr <tbm@cyrius.com> [2006-02-19 21:54]:
> From: Stanislaw Skowronek <skylark@linux-mips.org>
> 
> [PATCH 1/6] [MIPS] Improved IOC3 driver
> 
> An an improved driver for the IOC3 bridge, used in SGI Origin and Octane.

"Add an"...

-- 
Martin Michlmayr
http://www.cyrius.com/

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Merging Skylark's IOC3 patch
  2006-02-19 21:57 ` Martin Michlmayr
@ 2006-02-19 22:07   ` Russell King
  0 siblings, 0 replies; 13+ messages in thread
From: Russell King @ 2006-02-19 22:07 UTC (permalink / raw)
  To: Martin Michlmayr; +Cc: Ralf Baechle, linux-mips, Stanislaw Skowronek

On Sun, Feb 19, 2006 at 09:57:40PM +0000, Martin Michlmayr wrote:
> --- a/drivers/serial/8250.c
> +++ b/drivers/serial/8250.c
> @@ -310,6 +310,9 @@ static unsigned int serial_in(struct uar
>  	case UPIO_MEM:
>  		return readb(up->port.membase + offset);
>  
> +	case UPIO_IOC3:
> +		return readb(up->port.membase + (offset^3));
> +
>  	case UPIO_MEM32:
>  		return readl(up->port.membase + offset);
>  
> @@ -338,6 +341,10 @@ serial_out(struct uart_8250_port *up, in
>  		writeb(value, up->port.membase + offset);
>  		break;
>  
> +	case UPIO_IOC3:
> +		writeb(value, up->port.membase + (offset^3));
> +		break;
> +
>  	case UPIO_MEM32:
>  		writel(value, up->port.membase + offset);
>  		break;

Hmm, this starts to open up the question of whether having an array
of register pointers becomes cheaper.

> @@ -2300,8 +2311,10 @@ int __init serial8250_start_console(stru
>  
>  	add_preferred_console("ttyS", line, options);
>  	printk("Adding console on ttyS%d at %s 0x%lx (options '%s')\n",
> -		line, port->iotype == UPIO_MEM ? "MMIO" : "I/O port",
> -		port->iotype == UPIO_MEM ? (unsigned long) port->mapbase :
> +		line, port->iotype == UPIO_MEM ? "MMIO" : 
> +		port->iotype == UPIO_IOC3 ? "IOC3" : "I/O port",
> +		(port->iotype == UPIO_MEM || port->iotype == UPIO_IOC3) ?
> +		    (unsigned long) port->mapbase :
>  		    (unsigned long) port->iobase, options);

Maybe the "iotype" should index an array?

> diff --git a/include/linux/serial.h b/include/linux/serial.h
> index c69c6b9..a3ee515 100644
> --- a/include/linux/serial.h
> +++ b/include/linux/serial.h
> @@ -82,6 +82,7 @@ struct serial_struct {
>  #define SERIAL_IO_PORT	0
>  #define SERIAL_IO_HUB6	1
>  #define SERIAL_IO_MEM	2
> +#define SERIAL_IO_IOC3	3
>  
>  struct serial_uart_config {
>  	char	*name;
> diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
> index 4041122..9441c90 100644
> --- a/include/linux/serial_core.h
> +++ b/include/linux/serial_core.h
> @@ -221,6 +221,7 @@ struct uart_port {
>  #define UPIO_MEM		(2)
>  #define UPIO_MEM32		(3)
>  #define UPIO_AU			(4)			/* Au1x00 type IO */
> +#define UPIO_IOC3		(5)

Comparing SERIAL_IO_* and UPIO_*, I have to say no here.  Always ensure
that they end up with the same number.  SERIAL_IO_* is the current
userland version of UPIO_*.  At some point in the future (once the
pipedream of having _all_ serial drivers updated becomes reality) this
can be sorted out sanely.

> +	port.membase = (unsigned char *) &idd->vma->sregs.uarta;
> +	port.mapbase = ((unsigned long) port.membase) & 0xFFFFFFFFFF;
> +	d->line_a = serial8250_register_port(&port);

mapbase is supposed to be the physical address, whereas membase is what is
used to access the register, and should be something compatible with MMIO
accessors.  Is the cast really required here?

Are you tagging MMIO stuff with __iomem ?

Apart from that, I don't have any further comments at present.

-- 
Russell King
 Linux kernel    2.6 ARM Linux   - http://www.arm.linux.org.uk/
 maintainer of:  2.6 Serial core

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Merging Skylark's IOC3 patch
  2006-02-19 22:01   ` Martin Michlmayr
@ 2006-02-19 22:10     ` Martin Michlmayr
  0 siblings, 0 replies; 13+ messages in thread
From: Martin Michlmayr @ 2006-02-19 22:10 UTC (permalink / raw)
  To: Ralf Baechle; +Cc: linux-mips, Stanislaw Skowronek

* Martin Michlmayr <tbm@cyrius.com> [2006-02-19 22:01]:
> 21:59 < Skylark> The 6/6 isn't exactly right
> 22:00 < Skylark> It should be a part of the 5/6, really (making it 5/5)
> 22:00 < Skylark> And dependent on 4/6

Updated patch, merged 5/6 and 6/6 into 5/5.


From: Stanislaw Skowronek <skylark@linux-mips.org>

[PATCH 5/5] [MIPS] Switch IP27 to the new IOC3 UART driver

Switch IP27 to the new IOC3 UART driver.  Also now that IOC3 swapping
has been moved to where it belongs, namely into the serial layer, make
IP27 swizzle the 8-bit accesses in the correct way.

Signed-off-by: Stanislaw Skowronek <skylark@linux-mips.org>
Signed-off-by: Martin Michlmayr <tbm@cyrius.com>

---

 arch/mips/sgi-ip27/ip27-console.c        |    2 +-
 include/asm-mips/mach-ip27/mangle-port.h |    2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/include/asm-mips/mach-ip27/mangle-port.h b/include/asm-mips/mach-ip27/mangle-port.h
index f76c448..83e12e3 100644
--- a/include/asm-mips/mach-ip27/mangle-port.h
+++ b/include/asm-mips/mach-ip27/mangle-port.h
@@ -8,7 +8,7 @@
 #ifndef __ASM_MACH_IP27_MANGLE_PORT_H
 #define __ASM_MACH_IP27_MANGLE_PORT_H
 
-#define __swizzle_addr_b(port)	(port)
+#define __swizzle_addr_b(port)	((port) ^ 3)
 #define __swizzle_addr_w(port)	((port) ^ 2)
 #define __swizzle_addr_l(port)	(port)
 #define __swizzle_addr_q(port)	(port)
diff --git a/arch/mips/sgi-ip27/ip27-console.c b/arch/mips/sgi-ip27/ip27-console.c
index 3e1ac29..a973610 100644
--- a/arch/mips/sgi-ip27/ip27-console.c
+++ b/arch/mips/sgi-ip27/ip27-console.c
@@ -64,7 +64,7 @@ static void inline ioc3_console_probe(vo
 	up.irq		= 0;
 	up.uartclk	= IOC3_CLK;
 	up.regshift	= 0;
-	up.iotype	= UPIO_MEM;
+	up.iotype	= UPIO_IOC3;
 	up.flags	= IOC3_FLAGS;
 	up.line		= 0;
 


-- 
Martin Michlmayr
http://www.cyrius.com/

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: Merging Skylark's IOC3 patch
  2006-02-19 21:55 ` Martin Michlmayr
@ 2006-02-19 22:31   ` Dmitry Torokhov
  0 siblings, 0 replies; 13+ messages in thread
From: Dmitry Torokhov @ 2006-02-19 22:31 UTC (permalink / raw)
  To: Martin Michlmayr; +Cc: Ralf Baechle, linux-mips, Stanislaw Skowronek

On Sunday 19 February 2006 16:55, Martin Michlmayr wrote:
> * Martin Michlmayr <tbm@cyrius.com> [2006-02-19 21:15]:
> > Can you please review and/or merge Skylark's IOC3 patch from
> > ftp://ftp.linux-mips.org/pub/linux/mips/people/skylark/linux-mips-2.6.14-ioc3-r26.patch.bz2
> > 
> > From my basic understanding I believe that this patch needs to be split up
> > and submitted to different sub-system maintainers.
> 
> (Dmitry, this is only a RFC for now since the main support patch
> for IOC3 has not been merged yet.)
> 

OK... I have some comments, mostly cosmetic.

> +
> +struct ioc3kbd_data {
> +	struct ioc3_driver_data *idd;
> +	struct serio *kbd,*aux;
> +};

Space after comma please.

> +
> +static int ioc3kbd_write(struct serio *dev, unsigned char val)
> +{
> +	struct ioc3kbd_data *d = (struct ioc3kbd_data *)(dev->port_data);

Unneeded parens...

> +	unsigned mask;
> +	unsigned long timeout=0;

Missing spaces around operators...

> +
> +	mask = (dev==d->aux) ? KM_CSR_M_WRT_PEND : KM_CSR_K_WRT_PEND;

Missing spaces around operators...

> +	while((d->idd->vma->km_csr & mask) && (timeout<1000)) {

Missing a space after operator and unneeded parens again.
 
> +	if(dev==d->aux)
> +		d->idd->vma->m_wd=((unsigned)val)&0x000000ff;
> +	else
> +		d->idd->vma->k_wd=((unsigned)val)&0x000000ff;
> +
> +	if(timeout>=1000)

Missing spaces around operators...

> +
> +static int ioc3kbd_open(struct serio *dev)
> +{
> +	return 0;
> +}
> +
> +static void ioc3kbd_close(struct serio *dev)
> +{
> +}

You don't need to implement dummy functions, serio core can live without
these.

> +
> +static struct ioc3kbd_data * __init ioc3kbd_allocate_port(int idx, struct ioc3_driver_data *idd)
> +{
> +	struct serio *sk, *sa;
> +	struct ioc3kbd_data *d;
> +
> +	sk = kmalloc(sizeof(struct serio), GFP_KERNEL);
> +	sa = kmalloc(sizeof(struct serio), GFP_KERNEL);

kzalloc()? You fill them with 0 later anyway. 

> +	d = kmalloc(sizeof(struct ioc3kbd_data), GFP_KERNEL);
> +	if (sk && sa && d) {
> +		memset(sk, 0, sizeof(struct serio));
> +		sk->id.type = SERIO_8042;
> +		sk->write = ioc3kbd_write;
> +		sk->open = ioc3kbd_open;
> +		sk->close = ioc3kbd_close;
> +		snprintf(sk->name, sizeof(sk->name), "IOC3 keyboard %d", idx);
> +		snprintf(sk->phys, sizeof(sk->phys), "ioc3/serio%dkbd", idx);
> +		sk->port_data = d;
> +		sk->dev.parent = &(idd->pdev->dev);
> +		memset(sa, 0, sizeof(struct serio));
> +		sa->id.type = SERIO_8042;
> +		sa->write = ioc3kbd_write;
> +		sa->open = ioc3kbd_open;
> +		sa->close = ioc3kbd_close;
> +		snprintf(sa->name, sizeof(sa->name), "IOC3 auxiliary %d", idx);
> +		snprintf(sa->phys, sizeof(sa->phys), "ioc3/serio%daux", idx);
> +		sa->port_data = d;
> +		sa->dev.parent = &(idd->pdev->dev);
> +		d->idd = idd;
> +		d->kbd = sk;
> +		d->aux = sa;
> +		return d;
> +	}

Need to free() allocated stuff here in case it was not the first allocation
that failed.

> +	return NULL;
> +}
> +
> +static int ioc3kbd_probe(struct ioc3_submodule *is, struct ioc3_driver_data *idd)

__devinit perhaps?

> +{
> +	struct ioc3kbd_data *d;
> +	if(idd->class != IOC3_CLASS_BASE_IP30 && idd->class != IOC3_CLASS_CADDUO)
> +		return 1;

Usually failure indicator is negative. Serio core does not really care
but people expect to  see either -ENODEV or at least -1.

> +	d = ioc3kbd_allocate_port(idd->id, idd);
> +	idd->data[is->id] = d;
> +	if(!d)
> +		return 1;
> +	ioc3_enable(is, idd);
> +	serio_register_port(d->kbd);
> +	serio_register_port(d->aux);
> +	return 0;
> +}
> +
> +static int ioc3kbd_remove(struct ioc3_submodule *is, struct ioc3_driver_data *idd)
> +{
> +	struct ioc3kbd_data *d = (struct ioc3kbd_data *)(idd->data[is->id]);
> +	serio_unregister_port(d->kbd);
> +	serio_unregister_port(d->aux);
> +	kfree(d->kbd);
> +	kfree(d->aux);

Double free - serio core frees serio structure once the last reference
is dropped. You are freeing already freed memory here.

> +	kfree(d);
> +	idd->data[is->id] = NULL;
> +	return 0;
> +}
> +
> +static struct ioc3_submodule ioc3kbd_submodule = {
> +	.name = "serio",
> +	.probe = ioc3kbd_probe,
> +	.remove = ioc3kbd_remove,
> +	.irq_mask = SIO_IR_KBD_INT,
> +	.intr = ioc3kbd_intr,
> +	.owner = THIS_MODULE,
> +};
> +
> +static int __init ioc3kbd_init(void)
> +{
> +	ioc3_register_submodule(&ioc3kbd_submodule);

Can this ioc3_... function fail?

-- 
Dmitry

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2006-02-19 22:24 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-02-19 21:15 Merging Skylark's IOC3 patch Martin Michlmayr
2006-02-19 21:15 ` Martin Michlmayr
2006-02-19 21:54 ` Martin Michlmayr
2006-02-19 22:04   ` Martin Michlmayr
2006-02-19 21:55 ` Martin Michlmayr
2006-02-19 22:31   ` Dmitry Torokhov
2006-02-19 21:56 ` Martin Michlmayr
2006-02-19 21:57 ` Martin Michlmayr
2006-02-19 22:07   ` Russell King
2006-02-19 21:58 ` Martin Michlmayr
2006-02-19 22:01   ` Martin Michlmayr
2006-02-19 22:10     ` Martin Michlmayr
2006-02-19 21:58 ` Martin Michlmayr

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.