LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH/2.6.17-rc6 1/4 v2] Powerpc: Add general support for mpc74 48hpc2 (Taiga) platform
From: Zang Roy-r61911 @ 2006-06-13  7:07 UTC (permalink / raw)
  To: Paul Mackerras, Benjamin Herrenschmidt
  Cc: linuxppc-dev list, Alexandre.Bounine, Yang Xin-Xin-r48390

Add support for Freescale mpc7448 (Taiga) board support

Signed-off-by: Roy Zang  <tie-fei.zang@freescale.com>

---

diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 6729c98..d8a9add 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -323,7 +323,7 @@ config PPC_ISERIES
 
 config EMBEDDED6xx
 	bool "Embedded 6xx/7xx/7xxx-based board"
-	depends on PPC32 && BROKEN
+	depends on PPC32 && (BROKEN||BROKEN_ON_SMP)
 
 config APUS
 	bool "Amiga-APUS"
@@ -408,7 +408,8 @@ config U3_DART
 	default n
 
 config MPIC
-	depends on PPC_PSERIES || PPC_PMAC || PPC_MAPLE || PPC_CHRP
+	depends on PPC_PSERIES || PPC_PMAC || PPC_MAPLE || PPC_CHRP \
+			       || MPC7448HPC2
 	bool
 	default y
 
@@ -798,7 +799,8 @@ config MCA
 	bool
 
 config PCI
-	bool "PCI support" if 40x || CPM2 || PPC_83xx || PPC_85xx || PPC_MPC52xx || (EMBEDDED && PPC_ISERIES)
+	bool "PCI support" if 40x || CPM2 || PPC_83xx || PPC_85xx || PPC_MPC52xx || (EMBEDDED && PPC_ISERIES) \
+				  || MPC7448HPC2
 	default y if !40x && !CPM2 && !8xx && !APUS && !PPC_83xx && !PPC_85xx
 	default PCI_PERMEDIA if !4xx && !CPM2 && !8xx && APUS
 	default PCI_QSPAN if !4xx && !CPM2 && 8xx
diff --git a/arch/powerpc/kernel/legacy_serial.c b/arch/powerpc/kernel/legacy_serial.c
index 6e67b5b..c0e19ef 100644
--- a/arch/powerpc/kernel/legacy_serial.c
+++ b/arch/powerpc/kernel/legacy_serial.c
@@ -302,6 +302,17 @@ void __init find_legacy_serial_ports(voi
 		of_node_put(isa);
 	}
 
+	/* First fill our array with tsi-bridge ports */
+	for (np = NULL; (np = of_find_compatible_node(np, "serial", "ns16550")) != NULL;) {
+		struct device_node *tsi = of_get_parent(np);
+		if (tsi && !strcmp(tsi->type, "tsi-bridge")) {
+			index = add_legacy_soc_port(np, np);
+			if (index >= 0 && np == stdout)
+				legacy_serial_console = index;
+		}
+		of_node_put(tsi);
+	}
+	
 #ifdef CONFIG_PCI
 	/* Next, try to locate PCI ports */
 	for (np = NULL; (np = of_find_all_nodes(np));) {
diff --git a/arch/powerpc/platforms/Makefile b/arch/powerpc/platforms/Makefile
index c4f6b0d..308fead 100644
--- a/arch/powerpc/platforms/Makefile
+++ b/arch/powerpc/platforms/Makefile
@@ -13,3 +13,4 @@ obj-$(CONFIG_PPC_PSERIES)	+= pseries/
 obj-$(CONFIG_PPC_ISERIES)	+= iseries/
 obj-$(CONFIG_PPC_MAPLE)		+= maple/
 obj-$(CONFIG_PPC_CELL)		+= cell/
+obj-$(CONFIG_EMBEDDED6xx)	+= embedded6xx/
diff --git a/arch/powerpc/platforms/embedded6xx/Kconfig b/arch/powerpc/platforms/embedded6xx/Kconfig
index 4fdbc9a..2b78196 100644
--- a/arch/powerpc/platforms/embedded6xx/Kconfig
+++ b/arch/powerpc/platforms/embedded6xx/Kconfig
@@ -74,6 +74,16 @@ config SANDPOINT
 	  Select SANDPOINT if configuring for a Motorola Sandpoint X3
 	  (any flavor).
 
+config MPC7448HPC2
+	bool "Freescale MPC7448HPC2(Taiga)"
+	select TSI108_BRIDGE
+	select DEFAULT_UIMAGE
+	select PPC_UDBG_16550
+	select MPIC
+	help
+	  Select MPC7448HPC2 if configuring for Freescale MPC7448HPC2 (Taiga)
+	  platform
+
 config RADSTONE_PPC7D
 	bool "Radstone Technology PPC7D board"
 	select PPC_I8259
@@ -221,6 +231,11 @@ config MV64X60
 	select PPC_INDIRECT_PCI
 	default y
 
+config TSI108_BRIDGE
+	bool
+	depends on MPC7448HPC2
+	default y
+	
 menu "Set bridge options"
 	depends on MV64X60
 
diff --git a/arch/powerpc/platforms/embedded6xx/Makefile b/arch/powerpc/platforms/embedded6xx/Makefile
new file mode 100644
index 0000000..fa499fe
--- /dev/null
+++ b/arch/powerpc/platforms/embedded6xx/Makefile
@@ -0,0 +1,4 @@
+#
+# Makefile for the 6xx/7xx/7xxxx linux kernel.
+#
+obj-$(CONFIG_MPC7448HPC2)	+= mpc7448_hpc2.o
diff --git a/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c b/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c
new file mode 100644
index 0000000..1273bd6
--- /dev/null
+++ b/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c
@@ -0,0 +1,335 @@
+/*
+ * mpc7448_hpc2.c
+ *
+ * Board setup routines for the Freescale Taiga platform
+ *
+ * Author: Jacob Pan
+ *	 jacob.pan@freescale.com
+ * Author: Xianghua Xiao
+ *       x.xiao@freescale.com
+ * Maintainer: Roy Zang <tie-fei.zang@freescale.com> 
+ * 	Add Flat Device Tree support fot mpc7448hpc2 board
+ *
+ * Copyright 2004-2006 Freescale Semiconductor, Inc.
+ * 
+ * This file is licensed under
+ * the terms of the GNU General Public License version 2.  This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#include <linux/config.h>
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/kdev_t.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/ide.h>
+#include <linux/seq_file.h>
+#include <linux/root_dev.h>
+#include <linux/serial.h>
+#include <linux/tty.h>
+#include <linux/serial_core.h>
+
+#include <asm/system.h>
+#include <asm/time.h>
+#include <asm/machdep.h>
+#include <asm/prom.h>
+#include <asm/udbg.h>
+#include <asm/tsi108.h>
+#include <asm/pci-bridge.h>
+#include <asm/reg.h>
+#include <mm/mmu_decl.h>
+#include "mpc7448_hpc2.h"
+#include <asm/tsi108_irq.h>
+#include <asm/mpic.h>
+
+#undef DEBUG
+#ifdef DEBUG
+#define DBG(fmt...) do { printk(fmt); } while(0)
+#else
+#define DBG(fmt...) do { } while(0)
+#endif
+
+#ifndef CONFIG_PCI
+isa_io_base = MPC7448_HPC2_ISA_IO_BASE;
+isa_mem_base = MPC7448_HPC2_ISA_MEM_BASE;
+pci_dram_offset = MPC7448_HPC2_PCI_MEM_OFFSET;
+#endif
+
+extern int tsi108_setup_pci(struct device_node *dev);
+extern void _nmask_and_or_msr(unsigned long nmask, unsigned long or_val);
+extern void tsi108_pci_int_init(void);
+extern int tsi108_irq_cascade(struct pt_regs *regs, void *unused);
+
+/*
+ * Define all of the IRQ senses and polarities.  Taken from the
+ * mpc7448hpc  manual.
+ * Note:  Likely, this table and the following function should be
+ *        obtained and derived from the OF Device Tree.
+ */
+
+static u_char mpc7448_hpc2_pic_initsenses[] __initdata = {
+	/* External on-board sources */
+	(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),	/* INT[0] XINT0 from FPGA */
+	(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),	/* INT[1] XINT1 from FPGA */
+	(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),	/* INT[2] PHY_INT from both GIGE */
+	(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),	/* INT[3] RESERVED */
+	/* Internal Tsi108/109 interrupt sources */
+	(IRQ_SENSE_EDGE  | IRQ_POLARITY_POSITIVE),	/* Reserved IRQ */
+	(IRQ_SENSE_EDGE  | IRQ_POLARITY_POSITIVE),	/* Reserved IRQ */
+	(IRQ_SENSE_EDGE  | IRQ_POLARITY_POSITIVE),	/* Reserved IRQ */
+	(IRQ_SENSE_EDGE  | IRQ_POLARITY_POSITIVE),	/* Reserved IRQ */
+	(IRQ_SENSE_EDGE  | IRQ_POLARITY_POSITIVE),	/* DMA0 */
+	(IRQ_SENSE_EDGE  | IRQ_POLARITY_POSITIVE),	/* DMA1 */
+	(IRQ_SENSE_EDGE  | IRQ_POLARITY_POSITIVE),	/* DMA2 */
+	(IRQ_SENSE_EDGE  | IRQ_POLARITY_POSITIVE),	/* DMA3 */
+	(IRQ_SENSE_EDGE  | IRQ_POLARITY_POSITIVE),	/* UART0 */
+	(IRQ_SENSE_EDGE  | IRQ_POLARITY_POSITIVE),	/* UART1 */
+	(IRQ_SENSE_EDGE  | IRQ_POLARITY_POSITIVE),	/* I2C */
+	(IRQ_SENSE_EDGE  | IRQ_POLARITY_POSITIVE),	/* GPIO */
+	(IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),	/* GIGE0 */
+	(IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),	/* GIGE1 */
+	(IRQ_SENSE_EDGE  | IRQ_POLARITY_POSITIVE),	/* Reserved IRQ */
+	(IRQ_SENSE_EDGE  | IRQ_POLARITY_POSITIVE),	/* HLP */
+	(IRQ_SENSE_EDGE  | IRQ_POLARITY_POSITIVE),	/* SDC */
+	(IRQ_SENSE_EDGE  | IRQ_POLARITY_POSITIVE),	/* Processor IF */
+	(IRQ_SENSE_EDGE  | IRQ_POLARITY_POSITIVE),	/* Reserved IRQ */
+	(IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),	/* PCI/X block */
+};
+
+int mpc7448_hpc2_exclude_device(u_char bus, u_char devfn)
+{
+	if (bus == 0 && PCI_SLOT(devfn) == 0)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	else
+		return PCIBIOS_SUCCESSFUL;
+}
+
+/*
+ * find pci slot by devfn in interrupt map of OF tree
+ */
+u8 find_slot_by_devfn(unsigned int *interrupt_map, unsigned int devfn)
+{
+	int i;
+	unsigned int tmp;
+	for (i = 0; i < 4; i++){
+		tmp = interrupt_map[i*4*7];
+		if ((tmp >> 11) == (devfn >> 3))
+			return i;
+	}
+	return i;
+}
+
+/*
+ * Scans the interrupt map for pci device
+ */
+void mpc7448_hpc2_fixup_irq(struct pci_dev *dev)
+{
+	struct pci_controller *hose;
+	struct device_node *node;
+	unsigned int *interrupt;
+	int busnr;
+	int len;
+	u8 slot;
+	u8 pin;
+	
+	/* Lookup the hose */
+	busnr = dev->bus->number;
+	hose = pci_bus_to_hose(busnr);
+	if (!hose)
+		printk(KERN_ERR "No pci hose found\n");
+	
+	/* Check it has an OF node associated */
+	node = (struct device_node *) hose->arch_data;
+	if (!node)
+		printk(KERN_ERR "No pci node found\n");
+	
+	interrupt = (unsigned int *) get_property(node, "interrupt-map", &len);
+	slot = find_slot_by_devfn(interrupt, dev->devfn);
+	pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
+	if (pin == 0 || pin > 4)
+		pin = 1;
+	pin--;
+	dev->irq  = interrupt[slot*4*7 + pin*7 + 5];
+	DBG("TSI_PCI: dev->irq = 0x%x\n", dev->irq);
+}
+/* temporary pci irq map fixup*/
+
+void __init mpc7448_hpc2_pcibios_fixup(void)
+{
+	struct pci_dev *dev = NULL;
+	for_each_pci_dev(dev) {
+		mpc7448_hpc2_fixup_irq(dev);
+		pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
+	}
+}
+
+static void __init mpc7448_hpc2_setup_arch(void)
+{
+	struct device_node *cpu;
+	struct device_node *np;
+	if (ppc_md.progress)
+		ppc_md.progress("mpc7448_hpc2_setup_arch():set_bridge", 0);
+
+	cpu = of_find_node_by_type(NULL, "cpu");
+	if (cpu != 0) {
+		unsigned int *fp;
+
+		fp = (int *)get_property(cpu, "clock-frequency", NULL);
+		if (fp != 0)
+			loops_per_jiffy = *fp / HZ;
+		else
+			loops_per_jiffy = 50000000 / HZ;
+		of_node_put(cpu);
+	}
+	tsi108_csr_vir_base = get_vir_csrbase();
+	
+#ifdef	CONFIG_ROOT_NFS
+	ROOT_DEV = Root_NFS;
+#else
+	ROOT_DEV = Root_HDA1;
+#endif
+
+#ifdef CONFIG_BLK_DEV_INITRD
+	ROOT_DEV = Root_RAM0;
+#endif
+
+	/* setup PCI host bridge */
+#ifdef CONFIG_PCI
+	for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;)
+		tsi108_setup_pci(np);
+
+	ppc_md.pci_exclude_device = mpc7448_hpc2_exclude_device;
+	if (ppc_md.progress)
+		ppc_md.progress("tsi108: resources set", 0x100);
+#endif
+
+	printk(KERN_INFO "MPC7448HPC2 (TAIGA) Platform\n");
+	printk(KERN_INFO
+	       "Jointly ported by Freescale and Tundra Semiconductor\n");
+	printk(KERN_INFO
+	       "Enabling L2 cache then enabling the HID0 prefetch engine.\n");
+}
+
+/*
+ * Interrupt setup and service.  Interrrupts on the mpc7448_hpc2 come
+ * from the four external INT pins, PCI interrupts are routed via 
+ * PCI interrupt control registers, it generates internal IRQ23
+ *
+ * Interrupt routing on the Taiga Board:
+ * TSI108:PB_INT[0] -> CPU0:INT#
+ * TSI108:PB_INT[1] -> CPU0:MCP#
+ * TSI108:PB_INT[2] -> N/C
+ * TSI108:PB_INT[3] -> N/C
+ */
+static void __init mpc7448_hpc2_init_IRQ(void)
+{
+	struct mpic *mpic;
+	phys_addr_t mpic_paddr = 0;
+	struct device_node *tsi_pic;
+
+	tsi_pic = of_find_node_by_type(NULL, "open-pic");
+	if (tsi_pic) {
+		unsigned int size;
+		void *prop = get_property(tsi_pic, "reg", &size);
+		mpic_paddr = of_translate_address(tsi_pic, prop);
+	}
+
+	if (mpic_paddr == 0) {
+		printk("%s: No tsi108 PIC found !\n", __FUNCTION__);
+		return;
+	}
+
+	DBG("%s: tsi108pic phys_addr = 0x%x\n", __FUNCTION__,
+	    (u32) mpic_paddr);
+
+	mpic = mpic_alloc(mpic_paddr,
+			MPIC_PRIMARY | MPIC_BIG_ENDIAN | MPIC_WANTS_RESET |
+			MPIC_SPV_EOI | MPIC_MOD_ID(MPIC_ID_TSI108),
+			0, /* num_sources used */
+			TSI108_IRQ_BASE,
+			0, /* num_sources used */
+			NR_IRQS - 4 /* XXXX */,
+			mpc7448_hpc2_pic_initsenses,
+			sizeof(mpc7448_hpc2_pic_initsenses), "Tsi108_PIC");
+
+	BUG_ON(mpic == NULL); /* XXXX */
+
+	mpic_init(mpic);
+	mpic_setup_cascade(IRQ_TSI108_PCI, tsi108_irq_cascade, mpic);
+	tsi108_pci_int_init();
+
+	/* Configure MPIC outputs to CPU0 */
+	tsi108_write_reg(TSI108_MPIC_OFFSET + 0x30c, 0);
+}
+
+void mpc7448_hpc2_show_cpuinfo(struct seq_file *m)
+{
+	seq_printf(m, "vendor\t\t: Freescale Semiconductor\n");
+	seq_printf(m, "machine\t\t: MPC7448hpc2\n");
+}
+
+void mpc7448_hpc2_restart(char *cmd)
+{
+	local_irq_disable();
+
+	/* Set exception prefix high - to the firmware */
+	_nmask_and_or_msr(0, MSR_IP);
+
+	for (;;) ;		/* Spin until reset happens */
+}
+
+void mpc7448_hpc2_power_off(void)
+{
+	local_irq_disable();
+	for (;;) ;		/* No way to shut power off with software */
+}
+
+void mpc7448_hpc2_halt(void)
+{
+	mpc7448_hpc2_power_off();
+}
+
+/*
+ * Called very early, device-tree isn't unflattened
+ */
+static int __init mpc7448_hpc2_probe(void)
+{
+	unsigned long root = of_get_flat_dt_root();
+
+	if (!of_flat_dt_is_compatible(root, "mpc74xx"))
+		return 0;
+	return 1;
+}
+
+static int mpc7448_machine_check_exception(struct pt_regs *regs)
+{
+	extern void tsi108_clear_pci_cfg_error(void);
+	const struct exception_table_entry *entry;
+
+	/* Are we prepared to handle this fault */
+	if ((entry = search_exception_tables(regs->nip)) != NULL) {
+		tsi108_clear_pci_cfg_error();
+		regs->msr |= MSR_RI;
+		regs->nip = entry->fixup;
+		return 1;
+	}
+	return 0;
+
+}
+define_machine(mpc7448_hpc2){
+	.name 			= "MPC7448 HPC2",
+	.probe 			= mpc7448_hpc2_probe,
+	.setup_arch 		= mpc7448_hpc2_setup_arch,
+	.init_IRQ 		= mpc7448_hpc2_init_IRQ,
+	.show_cpuinfo 		= mpc7448_hpc2_show_cpuinfo,
+	.get_irq 		= mpic_get_irq,
+	.pcibios_fixup 		= mpc7448_hpc2_pcibios_fixup,
+	.restart 		= mpc7448_hpc2_restart,
+	.calibrate_decr 	= generic_calibrate_decr,
+	.machine_check_exception= mpc7448_machine_check_exception,
+	.progress 		= udbg_progress,
+};
diff --git a/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.h b/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.h
new file mode 100644
index 0000000..8296990
--- /dev/null
+++ b/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.h
@@ -0,0 +1,26 @@
+/*
+ * mpc7448_hpc2.h
+ *
+ * Definitions for Freescale MPC7448_HPC2 platform
+ *
+ * Author: Jacob Pan
+ *         jacob.pan@freescale.com
+ * Maintainer: Roy Zang <roy.zang@freescale.com>
+ *
+ * 2006 (c) Freescale Semiconductor, Inc.  This file is licensed under
+ * the terms of the GNU General Public License version 2.  This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#ifndef __PPC_PLATFORMS_MPC7448_HPC2_H
+#define __PPC_PLATFORMS_MPC7448_HPC2_H
+
+#include <asm/ppcboot.h>
+
+/* Base Addresses for the PCI bus 
+ */
+#define MPC7448_HPC2_PCI_MEM_OFFSET	(0x00000000)
+#define MPC7448_HPC2_ISA_IO_BASE	(0x00000000)
+#define MPC7448_HPC2_ISA_MEM_BASE	(0x00000000)
+#endif				/* __PPC_PLATFORMS_MPC7448_HPC2_H */

^ permalink raw reply related

* [PATCH/2.6.17-rc6  0/4 v2]  powerpc: add mpc7448hpc2 (Taiga) boa rd support
From: Zang Roy-r61911 @ 2006-06-13  7:07 UTC (permalink / raw)
  To: Paul Mackerras, Benjamin Herrenschmidt
  Cc: linuxppc-dev list, Alexandre.Bounine, Yang Xin-Xin-r48390


 Hi, 
  	This series of patch adds mpc7448hpc2 (taiga) board 
 support in arch/powerpc.	
 	Tsi108 chip of Tundra Semiconductor is also supported.
	The code has been revised according to the feedback.

 Roy Zang

^ permalink raw reply

* [PATCH/2.6.17-rc6] net:  Add tsi108 Ethernet device driver suppo rt
From: Zang Roy-r61911 @ 2006-06-13  7:07 UTC (permalink / raw)
  To: jgarzik
  Cc: netdev, Yang Xin-Xin-r48390, linux-kernel, Alexandre.Bounine,
	linuxppc-dev list


 This patch adds a net device driver and configuration options for
 Tundra Semiconductor Tsi108 integrated dual port Gigabit 
 Ethernet controller
 
 Signed-off-by: Alexandre Bounine <alexandreb@tundra.com>
 Signed-off-by: Roy Zang	<tie-fei.zang@freescale.com>
 
 ---


diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index bdaaad8..8a4ef29 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2177,6 +2177,14 @@ config SPIDER_NET
 	  This driver supports the Gigabit Ethernet chips present on the
 	  Cell Processor-Based Blades from IBM.
 
+config TSI108_ETH
+	   tristate "Tundra TSI108 gigabit Ethernet support"
+	   depends on TSI108_BRIDGE
+	   help
+	     This driver supports Tundra TSI108 gigabit Ethernet ports.
+	     To compile this driver as a module, choose M here: the module
+	     will be called tsi108_eth.
+
 config GIANFAR
 	tristate "Gianfar Ethernet"
 	depends on 85xx || 83xx
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index b90468a..5f90b30 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -109,6 +109,7 @@ obj-$(CONFIG_B44) += b44.o
 obj-$(CONFIG_FORCEDETH) += forcedeth.o
 obj-$(CONFIG_NE_H8300) += ne-h8300.o 8390.o
 
+obj-$(CONFIG_TSI108_ETH) += tsi108_eth.o
 obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o
 
 obj-$(CONFIG_PPP) += ppp_generic.o slhc.o
diff --git a/drivers/net/tsi108_eth.c b/drivers/net/tsi108_eth.c
new file mode 100644
index 0000000..77d5a11
--- /dev/null
+++ b/drivers/net/tsi108_eth.c
@@ -0,0 +1,1755 @@
+/*******************************************************************************
+  
+  Copyright(c) 2005 Tundra Semiconductor Corporation.
+  
+  This program is free software; you can redistribute it and/or modify it 
+  under the terms of the GNU General Public License as published by the Free 
+  Software Foundation; either version 2 of the License, or (at your option) 
+  any later version.
+  
+  This program is distributed in the hope that it will be useful, but WITHOUT 
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+  more details.
+  
+  You should have received a copy of the GNU General Public License along with
+  this program; if not, write to the Free Software Foundation, Inc., 59 
+  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*******************************************************************************/
+
+/* This driver is based on the driver code originally developed	
+ * for the Intel IOC80314 (ForestLake) Gigabit Ethernet by
+ * scott.wood@timesys.com  * Copyright (C) 2003 TimeSys Corporation
+ *
+ * Currently changes from original version are:
+ * - portig to Tsi108-based platform and kernel 2.6 (kong.lai@tundra.com)
+ * - modifications to handle two ports independently and support for
+ *   additional PHY devices (alexandre.bounine@tundra.com)
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/crc32.h>
+#include <linux/mii.h>
+#include <linux/device.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/pci.h>
+#include <linux/rtnetlink.h>
+#include <linux/timer.h>
+#include <linux/platform_device.h>
+
+#include <asm/tsi108_irq.h>
+#include <asm/tsi108.h>
+#include "tsi108_eth.h"
+
+#undef DEBUG
+#ifdef DEBUG
+#define DBG(fmt...) do { printk(fmt); } while(0)
+#else
+#define DBG(fmt...) do { } while(0)
+#endif
+
+typedef struct net_device net_device;
+typedef struct sk_buff sk_buff;
+
+#define MII_READ_DELAY 10000	/* max link wait time in msec */
+
+#define TSI108_RXRING_LEN     256
+
+/* NOTE: The driver currently does not support receiving packets
+ * larger than the buffer size, so don't decrease this (unless you
+ * want to add such support).
+ */
+#define TSI108_RXBUF_SIZE     1536
+
+#define TSI108_TXRING_LEN     256
+
+#define TSI108_TX_INT_FREQ    64
+
+/* Check the phy status every half a second. */
+#define CHECK_PHY_INTERVAL (HZ/2)
+
+static int tsi108_init_one(struct platform_device *pdev);
+static int tsi108_ether_remove(struct platform_device *pdev);
+
+typedef struct {
+	volatile u32 regs;	/* Base of normal regs */
+	volatile u32 phyregs;	/* Base of register bank used for PHY access */
+	int phy;		/* Index of PHY for this interface */
+	int irq_num;
+
+	struct timer_list timer;/* Timer that triggers the check phy function */
+	int rxtail;		/* Next entry in rxring to read */
+	int rxhead;		/* Next entry in rxring to give a new buffer */
+	int rxfree;		/* Number of free, allocated RX buffers */
+
+	int rxpending;		/* Non-zero if there are still descriptors
+				 * to be processed from a previous descriptor
+				 * interrupt condition that has been cleared */
+
+	int txtail;		/* Next TX descriptor to check status on */
+	int txhead;		/* Next TX descriptor to use */
+
+	/* Number of free TX descriptors.  This could be calculated from
+	 * rxhead and rxtail if one descriptor were left unused to disambiguate
+	 * full and empty conditions, but it's simpler to just keep track
+	 * explicitly. */
+
+	int txfree;
+
+	int phy_ok;		/* The PHY is currently powered on. */
+
+	/* PHY status (duplex is 1 for half, 2 for full,
+	 * so that the default 0 indicates that neither has
+	 * yet been configured). */
+
+	int link_up;
+	int speed;
+	int duplex;
+
+	tx_desc *txring;
+	rx_desc *rxring;
+	sk_buff *txskbs[TSI108_TXRING_LEN];
+	sk_buff *rxskbs[TSI108_RXRING_LEN];
+
+	dma_addr_t txdma, rxdma;
+
+	/* txlock nests in misclock and phy_lock */
+
+	spinlock_t txlock, misclock;
+
+	/* stats is used to hold the upper bits of each hardware counter,
+	 * and tmpstats is used to hold the full values for returning
+	 * to the caller of get_stats().  They must be separate in case
+	 * an overflow interrupt occurs before the stats are consumed.
+	 */
+
+	struct net_device_stats stats;
+	struct net_device_stats tmpstats;
+
+	/* These stats are kept separate in hardware, thus require individual
+	 * fields for handling carry.  They are combined in get_stats.
+	 */
+
+	unsigned long rx_fcs;	/* Add to rx_frame_errors */
+	unsigned long rx_short_fcs;	/* Add to rx_frame_errors */
+	unsigned long rx_long_fcs;	/* Add to rx_frame_errors */
+	unsigned long rx_underruns;	/* Add to rx_length_errors */
+	unsigned long rx_overruns;	/* Add to rx_length_errors */
+
+	unsigned long tx_coll_abort;	/* Add to tx_aborted_errors/collisions */
+	unsigned long tx_pause_drop;	/* Add to tx_aborted_errors */
+
+	unsigned long mc_hash[16];
+} tsi108_prv_data;
+
+/* Structure for a device driver */
+
+static struct platform_driver tsi_eth_driver = {
+	.probe = tsi108_init_one,
+	.remove = tsi108_ether_remove,
+	.driver	= {
+		.name = "tsi-ethernet",
+	},
+};
+
+static void tsi108_timed_checker(unsigned long dev_ptr);
+
+static void dump_eth_one(net_device * dev)
+{
+	tsi108_prv_data *data = netdev_priv(dev);
+
+	printk("Dumping %s...\n", dev->name);
+	printk("intstat %x intmask %x phy_ok %d"
+	       " link %d speed %d duplex %d\n",
+	       TSI108_ETH_READ_REG(TSI108_EC_INTSTAT),
+	       TSI108_ETH_READ_REG(TSI108_EC_INTMASK), data->phy_ok,
+	       data->link_up, data->speed, data->duplex);
+
+	printk("TX: head %d, tail %d, free %d, stat %x, estat %x, err %x\n",
+	       data->txhead, data->txtail, data->txfree,
+	       TSI108_ETH_READ_REG(TSI108_EC_TXSTAT),
+	       TSI108_ETH_READ_REG(TSI108_EC_TXESTAT),
+	       TSI108_ETH_READ_REG(TSI108_EC_TXERR));
+
+	printk("RX: head %d, tail %d, free %d, stat %x,"
+	       " estat %x, err %x, pending %d\n\n",
+	       data->rxhead, data->rxtail, data->rxfree,
+	       TSI108_ETH_READ_REG(TSI108_EC_RXSTAT),
+	       TSI108_ETH_READ_REG(TSI108_EC_RXESTAT),
+	       TSI108_ETH_READ_REG(TSI108_EC_RXERR), data->rxpending);
+}
+
+/* Synchronization is needed between the thread and up/down events.
+ * Note that the PHY is accessed through the same registers for both
+ * interfaces, so this can't be made interface-specific.
+ */
+
+static spinlock_t phy_lock = SPIN_LOCK_UNLOCKED;
+
+static inline u16 tsi108_read_mii(tsi108_prv_data * data, int reg, int *status)
+{
+	int i;
+	u16 ret;
+
+	TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_ADDR,
+				(data->phy << TSI108_MAC_MII_ADDR_PHY) |
+				(reg << TSI108_MAC_MII_ADDR_REG));
+	mb();
+	TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_CMD, 0);
+	mb();
+	TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_CMD, TSI108_MAC_MII_CMD_READ);
+	mb();
+	for (i = 0; i < 100; i++) {
+		if (!(TSI108_ETH_READ_PHYREG(TSI108_MAC_MII_IND) &
+		      (TSI108_MAC_MII_IND_NOTVALID | TSI108_MAC_MII_IND_BUSY)))
+			break;
+		udelay(10);
+	}
+
+	if (i == 100) {
+		if (status)
+			*status = -EBUSY;
+
+		ret = 0xffff;
+	} else {
+		if (status)
+			*status = 0;
+
+		ret = TSI108_ETH_READ_PHYREG(TSI108_MAC_MII_DATAIN);
+	}
+
+	return ret;
+}
+
+static inline void tsi108_write_mii(tsi108_prv_data * data, int reg, u16 val)
+{
+	TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_ADDR,
+				(data->phy << TSI108_MAC_MII_ADDR_PHY) |
+				(reg << TSI108_MAC_MII_ADDR_REG));
+	mb();
+	TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_DATAOUT, val);
+	mb();
+	while (TSI108_ETH_READ_PHYREG(TSI108_MAC_MII_IND) &
+	       TSI108_MAC_MII_IND_BUSY) ;
+}
+
+static inline void tsi108_write_tbi(tsi108_prv_data * data, int reg, u16 val)
+{
+
+	TSI108_ETH_WRITE_REG(TSI108_MAC_MII_ADDR,
+			     (0x1e << TSI108_MAC_MII_ADDR_PHY)
+			     | (reg << TSI108_MAC_MII_ADDR_REG));
+	mb();
+
+	TSI108_ETH_WRITE_REG(TSI108_MAC_MII_DATAOUT, val);
+	mb();
+	while (TSI108_ETH_READ_REG(TSI108_MAC_MII_IND) &
+	       TSI108_MAC_MII_IND_BUSY) ;
+}
+
+static void tsi108_check_phy(net_device * dev)
+{
+	tsi108_prv_data *data = netdev_priv(dev);
+	u16 sumstat;
+	u32 mac_cfg2_reg, portctrl_reg;
+	u32 fdx_flag = 0, reg_update = 0;
+
+	/* Do a dummy read, as for some reason the first read
+	 * after a link becomes up returns link down, even if
+	 * it's been a while since the link came up.
+	 */
+
+	spin_lock(&phy_lock);
+
+	if (!data->phy_ok)
+		goto out;
+
+	tsi108_read_mii(data, PHY_STAT, NULL);
+
+	if (!(tsi108_read_mii(data, PHY_STAT, NULL) & PHY_STAT_LINKUP)) {
+		if (data->link_up == 1) {
+			netif_stop_queue(dev);
+			data->link_up = 0;
+			printk(KERN_NOTICE "%s : link is down\n", dev->name);
+			netif_carrier_off(dev);
+		}
+
+		goto out;
+	}
+
+	{
+		mac_cfg2_reg = TSI108_ETH_READ_REG(TSI108_MAC_CFG2);
+		portctrl_reg = TSI108_ETH_READ_REG(TSI108_EC_PORTCTRL);
+
+		sumstat = tsi108_read_mii(data, PHY_SUM_STAT, NULL);
+
+		switch (sumstat & PHY_SUM_STAT_SPEED_MASK) {
+		case PHY_SUM_STAT_1000T_FD:
+			fdx_flag++;
+		case PHY_SUM_STAT_1000T_HD:
+			if (data->speed != 1000) {
+				mac_cfg2_reg &= ~TSI108_MAC_CFG2_IFACE_MASK;
+				mac_cfg2_reg |= TSI108_MAC_CFG2_GIG;
+				portctrl_reg &= ~TSI108_EC_PORTCTRL_NOGIG;
+				data->speed = 1000;
+				reg_update++;
+			}
+			break;
+		case PHY_SUM_STAT_100TX_FD:
+			fdx_flag++;
+		case PHY_SUM_STAT_100TX_HD:
+			if (data->speed != 100) {
+				mac_cfg2_reg &= ~TSI108_MAC_CFG2_IFACE_MASK;
+				mac_cfg2_reg |= TSI108_MAC_CFG2_NOGIG;
+				portctrl_reg |= TSI108_EC_PORTCTRL_NOGIG;
+				data->speed = 100;
+				reg_update++;
+			}
+			break;
+
+		case PHY_SUM_STAT_10T_FD:
+			fdx_flag++;
+		case PHY_SUM_STAT_10T_HD:
+			if (data->speed != 10) {
+				mac_cfg2_reg &= ~TSI108_MAC_CFG2_IFACE_MASK;
+				mac_cfg2_reg |= TSI108_MAC_CFG2_NOGIG;
+				portctrl_reg |= TSI108_EC_PORTCTRL_NOGIG;
+				data->speed = 10;
+				reg_update++;
+			}
+			break;
+
+		default:
+			if (net_ratelimit())
+				printk(KERN_ERR "PHY reported invalid speed,"
+				       KERN_ERR " summary status %x\n",
+				       sumstat);
+			goto out;
+		}
+
+		if (fdx_flag || (sumstat & PHY_SUM_STAT_FULLDUPLEX)) {
+			if (data->duplex != 2) {
+				mac_cfg2_reg |= TSI108_MAC_CFG2_FULLDUPLEX;
+				portctrl_reg &= ~TSI108_EC_PORTCTRL_HALFDUPLEX;
+				data->duplex = 2;
+				reg_update++;
+			}
+		} else {
+			if (data->duplex != 1) {
+				mac_cfg2_reg &= ~TSI108_MAC_CFG2_FULLDUPLEX;
+				portctrl_reg |= TSI108_EC_PORTCTRL_HALFDUPLEX;
+				data->duplex = 1;
+				reg_update++;
+			}
+		}
+
+		if (reg_update) {
+			TSI108_ETH_WRITE_REG(TSI108_MAC_CFG2, mac_cfg2_reg);
+			mb();
+			TSI108_ETH_WRITE_REG(TSI108_EC_PORTCTRL, portctrl_reg);
+			mb();
+		}
+
+	}
+
+	if (data->link_up == 0) {
+		/* The manual says it can take 3-4 usecs for the speed change
+		 * to take effect.
+		 */
+		udelay(5);
+
+		spin_lock(&data->txlock);
+		if (netif_queue_stopped(dev)
+		    && is_valid_ether_addr(dev->dev_addr) && data->txfree)
+			netif_wake_queue(dev);
+
+		data->link_up = 1;
+		spin_unlock(&data->txlock);
+		printk("%s : link is up: %dMb %s-duplex\n",
+		       dev->name, data->speed,
+		       (data->duplex == 2) ? "full" : "half");
+		netif_carrier_on(dev);
+	}
+
+      out:
+	spin_unlock(&phy_lock);
+}
+
+static inline void
+tsi108_stat_carry_one(int carry, int carry_bit, int carry_shift,
+		      unsigned long *upper)
+{
+	if (carry & carry_bit)
+		*upper += carry_shift;
+}
+
+static void tsi108_stat_carry(net_device * dev)
+{
+	tsi108_prv_data *data = netdev_priv(dev);
+	u32 carry1, carry2;
+
+	spin_lock_irq(&data->misclock);
+
+	carry1 = TSI108_ETH_READ_REG(TSI108_STAT_CARRY1);
+	carry2 = TSI108_ETH_READ_REG(TSI108_STAT_CARRY2);
+
+	TSI108_ETH_WRITE_REG(TSI108_STAT_CARRY1, carry1);
+	TSI108_ETH_WRITE_REG(TSI108_STAT_CARRY2, carry2);
+
+	tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXBYTES,
+			      TSI108_STAT_RXBYTES_CARRY, &data->stats.rx_bytes);
+
+	tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXPKTS,
+			      TSI108_STAT_RXPKTS_CARRY,
+			      &data->stats.rx_packets);
+
+	tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXFCS,
+			      TSI108_STAT_RXFCS_CARRY, &data->rx_fcs);
+
+	tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXMCAST,
+			      TSI108_STAT_RXMCAST_CARRY,
+			      &data->stats.multicast);
+
+	tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXALIGN,
+			      TSI108_STAT_RXALIGN_CARRY,
+			      &data->stats.rx_frame_errors);
+
+	tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXLENGTH,
+			      TSI108_STAT_RXLENGTH_CARRY,
+			      &data->stats.rx_length_errors);
+
+	tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXRUNT,
+			      TSI108_STAT_RXRUNT_CARRY, &data->rx_underruns);
+
+	tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXJUMBO,
+			      TSI108_STAT_RXJUMBO_CARRY, &data->rx_overruns);
+
+	tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXFRAG,
+			      TSI108_STAT_RXFRAG_CARRY, &data->rx_short_fcs);
+
+	tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXJABBER,
+			      TSI108_STAT_RXJABBER_CARRY, &data->rx_long_fcs);
+
+	tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXDROP,
+			      TSI108_STAT_RXDROP_CARRY,
+			      &data->stats.rx_missed_errors);
+
+	tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXBYTES,
+			      TSI108_STAT_TXBYTES_CARRY, &data->stats.tx_bytes);
+
+	tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXPKTS,
+			      TSI108_STAT_TXPKTS_CARRY,
+			      &data->stats.tx_packets);
+
+	tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXEXDEF,
+			      TSI108_STAT_TXEXDEF_CARRY,
+			      &data->stats.tx_aborted_errors);
+
+	tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXEXCOL,
+			      TSI108_STAT_TXEXCOL_CARRY, &data->tx_coll_abort);
+
+	tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXTCOL,
+			      TSI108_STAT_TXTCOL_CARRY,
+			      &data->stats.collisions);
+
+	tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXPAUSE,
+			      TSI108_STAT_TXPAUSEDROP_CARRY,
+			      &data->tx_pause_drop);
+
+	spin_unlock_irq(&data->misclock);
+}
+
+/* Read a stat counter atomically with respect to carries.
+ * data->misclock must be held.
+ */
+static inline unsigned long
+tsi108_read_stat(tsi108_prv_data * data, int reg, int carry_bit,
+		 int carry_shift, unsigned long *upper)
+{
+	int carryreg;
+	unsigned long val;
+
+	if (reg < 0xb0)
+		carryreg = TSI108_STAT_CARRY1;
+	else
+		carryreg = TSI108_STAT_CARRY2;
+
+      again:
+	val = TSI108_ETH_READ_REG(reg) | *upper;
+
+	rmb();
+
+	/* Check to see if it overflowed, but the interrupt hasn't
+	 * been serviced yet.  If so, handle the carry here, and
+	 * try again.
+	 */
+
+	if (unlikely(TSI108_ETH_READ_REG(carryreg) & carry_bit)) {
+		*upper += carry_shift;
+		TSI108_ETH_WRITE_REG(carryreg, carry_bit);
+		mb();
+
+		goto again;
+	}
+
+	return val;
+}
+
+static struct net_device_stats *tsi108_get_stats(net_device * dev)
+{
+	unsigned long excol;
+
+	tsi108_prv_data *data = netdev_priv(dev);
+	spin_lock_irq(&data->misclock);
+
+	data->tmpstats.rx_packets =
+	    tsi108_read_stat(data, TSI108_STAT_RXPKTS,
+			     TSI108_STAT_CARRY1_RXPKTS,
+			     TSI108_STAT_RXPKTS_CARRY, &data->stats.rx_packets);
+
+	data->tmpstats.tx_packets =
+	    tsi108_read_stat(data, TSI108_STAT_TXPKTS,
+			     TSI108_STAT_CARRY2_TXPKTS,
+			     TSI108_STAT_TXPKTS_CARRY, &data->stats.tx_packets);
+
+	data->tmpstats.rx_bytes =
+	    tsi108_read_stat(data, TSI108_STAT_RXBYTES,
+			     TSI108_STAT_CARRY1_RXBYTES,
+			     TSI108_STAT_RXBYTES_CARRY, &data->stats.rx_bytes);
+
+	data->tmpstats.tx_bytes =
+	    tsi108_read_stat(data, TSI108_STAT_TXBYTES,
+			     TSI108_STAT_CARRY2_TXBYTES,
+			     TSI108_STAT_TXBYTES_CARRY, &data->stats.tx_bytes);
+
+	data->tmpstats.multicast =
+	    tsi108_read_stat(data, TSI108_STAT_RXMCAST,
+			     TSI108_STAT_CARRY1_RXMCAST,
+			     TSI108_STAT_RXMCAST_CARRY, &data->stats.multicast);
+
+	excol = tsi108_read_stat(data, TSI108_STAT_TXEXCOL,
+				 TSI108_STAT_CARRY2_TXEXCOL,
+				 TSI108_STAT_TXEXCOL_CARRY,
+				 &data->tx_coll_abort);
+
+	data->tmpstats.collisions =
+	    tsi108_read_stat(data, TSI108_STAT_TXTCOL,
+			     TSI108_STAT_CARRY2_TXTCOL,
+			     TSI108_STAT_TXTCOL_CARRY, &data->stats.collisions);
+
+	data->tmpstats.collisions += excol;
+
+	data->tmpstats.rx_length_errors =
+	    tsi108_read_stat(data, TSI108_STAT_RXLENGTH,
+			     TSI108_STAT_CARRY1_RXLENGTH,
+			     TSI108_STAT_RXLENGTH_CARRY,
+			     &data->stats.rx_length_errors);
+
+	data->tmpstats.rx_length_errors +=
+	    tsi108_read_stat(data, TSI108_STAT_RXRUNT,
+			     TSI108_STAT_CARRY1_RXRUNT,
+			     TSI108_STAT_RXRUNT_CARRY, &data->rx_underruns);
+
+	data->tmpstats.rx_length_errors +=
+	    tsi108_read_stat(data, TSI108_STAT_RXJUMBO,
+			     TSI108_STAT_CARRY1_RXJUMBO,
+			     TSI108_STAT_RXJUMBO_CARRY, &data->rx_overruns);
+
+	data->tmpstats.rx_frame_errors =
+	    tsi108_read_stat(data, TSI108_STAT_RXALIGN,
+			     TSI108_STAT_CARRY1_RXALIGN,
+			     TSI108_STAT_RXALIGN_CARRY,
+			     &data->stats.rx_frame_errors);
+
+	data->tmpstats.rx_frame_errors +=
+	    tsi108_read_stat(data, TSI108_STAT_RXFCS,
+			     TSI108_STAT_CARRY1_RXFCS, TSI108_STAT_RXFCS_CARRY,
+			     &data->rx_fcs);
+
+	data->tmpstats.rx_frame_errors +=
+	    tsi108_read_stat(data, TSI108_STAT_RXFRAG,
+			     TSI108_STAT_CARRY1_RXFRAG,
+			     TSI108_STAT_RXFRAG_CARRY, &data->rx_short_fcs);
+
+	data->tmpstats.rx_missed_errors =
+	    tsi108_read_stat(data, TSI108_STAT_RXDROP,
+			     TSI108_STAT_CARRY1_RXDROP,
+			     TSI108_STAT_RXDROP_CARRY,
+			     &data->stats.rx_missed_errors);
+
+	/* These three are maintained by software. */
+	data->tmpstats.rx_fifo_errors = data->stats.rx_fifo_errors;
+	data->tmpstats.rx_crc_errors = data->stats.rx_crc_errors;
+
+	data->tmpstats.tx_aborted_errors =
+	    tsi108_read_stat(data, TSI108_STAT_TXEXDEF,
+			     TSI108_STAT_CARRY2_TXEXDEF,
+			     TSI108_STAT_TXEXDEF_CARRY,
+			     &data->stats.tx_aborted_errors);
+
+	data->tmpstats.tx_aborted_errors +=
+	    tsi108_read_stat(data, TSI108_STAT_TXPAUSEDROP,
+			     TSI108_STAT_CARRY2_TXPAUSE,
+			     TSI108_STAT_TXPAUSEDROP_CARRY,
+			     &data->tx_pause_drop);
+
+	data->tmpstats.tx_aborted_errors += excol;
+
+	data->tmpstats.tx_errors = data->tmpstats.tx_aborted_errors;
+	data->tmpstats.rx_errors = data->tmpstats.rx_length_errors +
+	    data->tmpstats.rx_crc_errors +
+	    data->tmpstats.rx_frame_errors +
+	    data->tmpstats.rx_fifo_errors + data->tmpstats.rx_missed_errors;
+
+	spin_unlock_irq(&data->misclock);
+	return &data->tmpstats;
+}
+
+static void tsi108_restart_rx(tsi108_prv_data * data, net_device * dev)
+{
+	TSI108_ETH_WRITE_REG(TSI108_EC_RXQ_PTRHIGH,
+			     TSI108_EC_RXQ_PTRHIGH_VALID);
+
+	wmb();
+	TSI108_ETH_WRITE_REG(TSI108_EC_RXCTRL, TSI108_EC_RXCTRL_GO
+			     | TSI108_EC_RXCTRL_QUEUE0);
+}
+
+static void tsi108_restart_tx(tsi108_prv_data * data)
+{
+	TSI108_ETH_WRITE_REG(TSI108_EC_TXQ_PTRHIGH,
+			     TSI108_EC_TXQ_PTRHIGH_VALID);
+
+	wmb();
+	TSI108_ETH_WRITE_REG(TSI108_EC_TXCTRL, TSI108_EC_TXCTRL_IDLEINT |
+			     TSI108_EC_TXCTRL_GO | TSI108_EC_TXCTRL_QUEUE0);
+}
+
+/* txlock must be held by caller, with IRQs disabled, and
+ * with permission to re-enable them when the lock is dropped.
+ */
+static void tsi108_check_for_completed_tx(net_device * dev)
+{
+	tsi108_prv_data *data = netdev_priv(dev);
+	int tx;
+	struct sk_buff *skb;
+	int release = 0;
+
+	while (!data->txfree || data->txhead != data->txtail) {
+		tx = data->txtail;
+
+		if (data->txring[tx].misc & TSI108_TX_OWN)
+			break;
+
+		skb = data->txskbs[tx];
+
+		if (!(data->txring[tx].misc & TSI108_TX_OK))
+			printk("%s: bad tx packet, misc %x\n",
+			       dev->name, data->txring[tx].misc);
+
+		data->txtail = (data->txtail + 1) % TSI108_TXRING_LEN;
+		data->txfree++;
+
+		if (data->txring[tx].misc & TSI108_TX_EOF) {
+			dev_kfree_skb_any(skb);
+			release++;
+		}
+	}
+
+	if (release) {
+
+		if (netif_queue_stopped(dev)
+		    && is_valid_ether_addr(dev->dev_addr) && data->link_up)
+			netif_wake_queue(dev);
+	}
+}
+
+static int tsi108_send_packet(sk_buff * skb, net_device * dev)
+{
+	tsi108_prv_data *data = netdev_priv(dev);
+	int frags = skb_shinfo(skb)->nr_frags + 1;
+	int i;
+#ifdef FIXME_SG_CSUM_NOT_TESTED	/* FIXME: Not supported now. */
+	long csstart;
+	long csum;
+
+	csstart = skb->len - skb->data_len;
+	if (csstart > skb->len - skb->data_len)
+		BUG();
+	csum = 0;
+	if (csstart != skb->len)
+		csum = skb_checksum(skb, csstart, skb->len - csstart, 0);
+#endif
+
+	if (!data->phy_ok && net_ratelimit())
+		printk(KERN_ERR "%s: Transmit while PHY is down!\n", dev->name);
+
+	if (!data->link_up) {
+		printk(KERN_ERR "%s: Transmit while link is down!\n",
+		       dev->name);
+		netif_stop_queue(dev);
+		return 1;
+	}
+
+	if (data->txfree < MAX_SKB_FRAGS + 1) {
+		netif_stop_queue(dev);
+
+		if (net_ratelimit())
+			printk(KERN_ERR "%s: Transmit with full tx ring!\n",
+			       dev->name);
+		return 1;
+	}
+
+	if (data->txfree - frags < MAX_SKB_FRAGS + 1) {
+		netif_stop_queue(dev);
+	}
+
+	spin_lock_irq(&data->txlock);
+
+	for (i = 0; i < frags; i++) {
+		int misc = 0;
+		int tx = data->txhead;
+
+		/* This is done to mark every TSI108_TX_INT_FREQ tx buffers with
+		 * the interrupt bit.  TX descriptor-complete interrupts are
+		 * enabled when the queue fills up, and masked when there is
+		 * still free space.  This way, when saturating the outbound
+		 * link, the tx interrupts are kept to a reasonable level. 
+		 * When the queue is not full, reclamation of skbs still occurs
+		 * as new packets are transmitted, or on a queue-empty
+		 * interrupt.
+		 */
+
+		if ((tx % TSI108_TX_INT_FREQ == 0) &&
+		    ((TSI108_TXRING_LEN - data->txfree) >= TSI108_TX_INT_FREQ)
+		    )
+			misc = TSI108_TX_INT;
+
+		data->txskbs[tx] = skb;
+
+		if (i == 0) {
+			data->txring[tx].buf0 = virt_to_phys(skb->data);
+			data->txring[tx].len = skb->len - skb->data_len;
+			misc |= TSI108_TX_SOF;
+		} else {
+			skb_frag_t *frag = &skb_shinfo(skb)->frags[i - 1];
+
+			data->txring[tx].buf0 =
+			    page_to_phys(frag->page) + frag->page_offset;
+			data->txring[tx].len = frag->size;
+		}
+
+		if (i == frags - 1)
+			misc |= TSI108_TX_EOF;
+
+#ifdef TSI108_PRINT_TX_FRAME
+		{
+			int i;
+			printk("%s: Tx Frame contents (%d)\n", dev->name,
+			       skb->len);
+			for (i = 0; i < skb->len; i++)
+				printk(" %2.2x", skb->data[i]);
+			printk(".\n");
+		}
+#endif				/* TSI108_PRINT_TX_FRAME */
+
+		mb();
+		data->txring[tx].misc = misc | TSI108_TX_OWN;
+
+		data->txhead = (data->txhead + 1) % TSI108_TXRING_LEN;
+		data->txfree--;
+	}
+
+	tsi108_check_for_completed_tx(dev);
+
+	/* This must be done after the check for completed tx descriptors,
+	 * so that the tail pointer is correct.
+	 */
+
+	if (!(TSI108_ETH_READ_REG(TSI108_EC_TXSTAT) & TSI108_EC_TXSTAT_QUEUE0))
+		tsi108_restart_tx(data);
+
+	spin_unlock_irq(&data->txlock);
+	return 0;
+}
+
+static int tsi108_check_for_completed_rx(net_device * dev, int budget)
+{
+	tsi108_prv_data *data = netdev_priv(dev);
+	int done = 0;
+
+	while (data->rxfree && done != budget) {
+		int rx = data->rxtail;
+		struct sk_buff *skb;
+
+		if (data->rxring[rx].misc & TSI108_RX_OWN)
+			break;
+
+		skb = data->rxskbs[rx];
+		data->rxtail = (data->rxtail + 1) % TSI108_RXRING_LEN;
+		data->rxfree--;
+		done++;
+
+		if (data->rxring[rx].misc & TSI108_RX_BAD) {
+			spin_lock_irq(&data->misclock);
+
+			if (data->rxring[rx].misc & TSI108_RX_CRC)
+				data->stats.rx_crc_errors++;
+			if (data->rxring[rx].misc & TSI108_RX_OVER)
+				data->stats.rx_fifo_errors++;
+
+			spin_unlock_irq(&data->misclock);
+
+			dev_kfree_skb_any(skb);
+			continue;
+		}
+#ifdef TSI108_PRINT_RX_FRAME
+		{
+			int i;
+			printk("%s: Rx Frame contents (%d)\n",
+			       dev->name, data->rxring[rx].len);
+			for (i = 0; i < data->rxring[rx].len; i++)
+				printk(" %2.2x", skb->data[i]);
+			printk(".\n");
+		}
+#endif				/* TSI108_PRINT_RX_FRAME */
+
+		skb->dev = dev;
+		skb_put(skb, data->rxring[rx].len);
+		skb->protocol = eth_type_trans(skb, dev);
+		netif_receive_skb(skb);
+		dev->last_rx = jiffies;
+	}
+
+	return done;
+}
+
+static int tsi108_refill_rx(net_device * dev, int budget)
+{
+	tsi108_prv_data *data = netdev_priv(dev);
+	int done = 0;
+
+	while (data->rxfree != TSI108_RXRING_LEN && done != budget) {
+		int rx = data->rxhead;
+		sk_buff *skb;
+
+		data->rxskbs[rx] = skb = dev_alloc_skb(TSI108_RXBUF_SIZE + 2);
+		if (!skb)
+			break;
+
+		skb_reserve(skb, 2);	/* Align the data on a 4-byte boundary. */
+
+		data->rxring[rx].buf0 = virt_to_phys(skb->data);
+
+		/* Sometimes the hardware sets blen to zero after packet
+		 * reception, even though the manual says that it's only ever
+		 * modified by the driver.
+		 */
+
+		data->rxring[rx].blen = 1536;
+		mb();
+		data->rxring[rx].misc = TSI108_RX_OWN | TSI108_RX_INT;
+
+		data->rxhead = (data->rxhead + 1) % TSI108_RXRING_LEN;
+		data->rxfree++;
+		done++;
+	}
+
+	mb();
+
+	if (done != 0 && !(TSI108_ETH_READ_REG(TSI108_EC_RXSTAT) &
+			   TSI108_EC_RXSTAT_QUEUE0))
+		tsi108_restart_rx(data, dev);
+
+	return done;
+}
+
+static int tsi108_poll(net_device * dev, int *budget)
+{
+	tsi108_prv_data *data = netdev_priv(dev);
+	u32 estat = TSI108_ETH_READ_REG(TSI108_EC_RXESTAT);
+	u32 intstat = TSI108_ETH_READ_REG(TSI108_EC_INTSTAT);
+	int total_budget = min(*budget, dev->quota);
+	int num_received = 0, num_filled = 0, budget_used;
+
+	intstat &= TSI108_INT_RXQUEUE0 | TSI108_INT_RXTHRESH |
+	    TSI108_INT_RXOVERRUN | TSI108_INT_RXERROR | TSI108_INT_RXWAIT;
+
+	TSI108_ETH_WRITE_REG(TSI108_EC_RXESTAT, estat);
+	TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, intstat);
+
+	if (data->rxpending || (estat & TSI108_EC_RXESTAT_Q0_DESCINT))
+		num_received = tsi108_check_for_completed_rx(dev, total_budget);
+
+	/* This should normally fill no more slots than the number of
+	 * packets received in tsi108_check_for_completed_rx().  The exception
+	 * is when we previously ran out of memory for RX SKBs.  In that
+	 * case, it's helpful to obey the budget, not only so that the
+	 * CPU isn't hogged, but so that memory (which may still be low)
+	 * is not hogged by one device.
+	 *
+	 * A work unit is considered to be two SKBs to allow us to catch
+	 * up when the ring has shrunk due to out-of-memory but we're
+	 * still removing the full budget's worth of packets each time.
+	 */
+
+	if (data->rxfree < TSI108_RXRING_LEN)
+		num_filled = tsi108_refill_rx(dev, total_budget * 2);
+
+	if (intstat & TSI108_INT_RXERROR) {
+		u32 err = TSI108_ETH_READ_REG(TSI108_EC_RXERR);
+		TSI108_ETH_WRITE_REG(TSI108_EC_RXERR, err);
+
+		if (err) {
+			if (net_ratelimit())
+				printk(KERN_DEBUG "%s: RX error %x\n",
+				       dev->name, err);
+
+			if (!(TSI108_ETH_READ_REG(TSI108_EC_RXSTAT) &
+			      TSI108_EC_RXSTAT_QUEUE0))
+				tsi108_restart_rx(data, dev);
+		}
+	}
+
+	if (intstat & TSI108_INT_RXOVERRUN) {
+		spin_lock_irq(&data->misclock);
+		data->stats.rx_fifo_errors++;
+		spin_unlock_irq(&data->misclock);
+	}
+
+	budget_used = max(num_received, num_filled / 2);
+
+	*budget -= budget_used;
+	dev->quota -= budget_used;
+
+	if (budget_used != total_budget) {
+		data->rxpending = 0;
+		netif_rx_complete(dev);
+
+		TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK,
+				     TSI108_ETH_READ_REG(TSI108_EC_INTMASK)
+				     & ~(TSI108_INT_RXQUEUE0
+					 | TSI108_INT_RXTHRESH |
+					 TSI108_INT_RXOVERRUN |
+					 TSI108_INT_RXERROR |
+					 TSI108_INT_RXWAIT));
+
+		mb();
+
+		/* IRQs are level-triggered, so no need to re-check */
+		return 0;
+	} else {
+		data->rxpending = 1;
+	}
+
+	return 1;
+}
+
+static void tsi108_rx_int(net_device * dev)
+{
+	tsi108_prv_data *data = netdev_priv(dev);
+
+	/* A race could cause dev to already be scheduled, so it's not an
+	 * error if that happens (and interrupts shouldn't be re-masked,
+	 * because that can cause harmful races, if poll has already
+	 * unmasked them but not cleared LINK_STATE_SCHED).  
+	 *
+	 * This can happen if this code races with tsi108_poll(), which masks
+	 * the interrupts after tsi108_irq_one() read the mask, but before
+	 * netif_rx_schedule is called.  It could also happen due to calls
+	 * from tsi108_check_rxring().
+	 */
+
+	if (netif_rx_schedule_prep(dev)) {
+		/* Mask, rather than ack, the receive interrupts.  The ack
+		 * will happen in tsi108_poll().
+		 */
+
+		TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK,
+				     TSI108_ETH_READ_REG(TSI108_EC_INTMASK) |
+				     TSI108_INT_RXQUEUE0
+				     | TSI108_INT_RXTHRESH |
+				     TSI108_INT_RXOVERRUN | TSI108_INT_RXERROR |
+				     TSI108_INT_RXWAIT);
+		mb();
+		__netif_rx_schedule(dev);
+	} else {
+		if (!netif_running(dev)) {
+			/* This can happen if an interrupt occurs while the
+			 * interface is being brought down, as the START
+			 * bit is cleared before the stop function is called.
+			 *
+			 * In this case, the interrupts must be masked, or
+			 * they will continue indefinitely.
+			 *
+			 * There's a race here if the interface is brought down
+			 * and then up in rapid succession, as the device could
+			 * be made running after the above check and before
+			 * the masking below.  This will only happen if the IRQ
+			 * thread has a lower priority than the task brining
+			 * up the interface.  Fixing this race would likely
+			 * require changes in generic code.
+			 */
+
+			TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK,
+					     TSI108_ETH_READ_REG
+					     (TSI108_EC_INTMASK) |
+					     TSI108_INT_RXQUEUE0 |
+					     TSI108_INT_RXTHRESH |
+					     TSI108_INT_RXOVERRUN |
+					     TSI108_INT_RXERROR |
+					     TSI108_INT_RXWAIT);
+			mb();
+		}
+	}
+}
+
+/* If the RX ring has run out of memory, try periodically
+ * to allocate some more, as otherwise poll would never
+ * get called (apart from the initial end-of-queue condition).
+ *
+ * This is called once per second (by default) from the thread.
+ */
+
+static void tsi108_check_rxring(net_device * dev)
+{
+	tsi108_prv_data *data = netdev_priv(dev);
+
+	/* A poll is scheduled, as opposed to caling tsi108_refill_rx 
+	 * directly, so as to keep the receive path single-threaded
+	 * (and thus not needing a lock).
+	 */
+
+	if (netif_running(dev) && data->rxfree < TSI108_RXRING_LEN / 4)
+		tsi108_rx_int(dev);
+}
+
+static void tsi108_tx_int(net_device * dev)
+{
+	tsi108_prv_data *data = netdev_priv(dev);
+	u32 estat = TSI108_ETH_READ_REG(TSI108_EC_TXESTAT);
+
+	mb();
+	TSI108_ETH_WRITE_REG(TSI108_EC_TXESTAT, estat);
+	mb();
+	TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, TSI108_INT_TXQUEUE0 |
+			     TSI108_INT_TXIDLE | TSI108_INT_TXERROR);
+	mb();
+	if (estat & TSI108_EC_TXESTAT_Q0_ERR) {
+		u32 err = TSI108_ETH_READ_REG(TSI108_EC_TXERR);
+		TSI108_ETH_WRITE_REG(TSI108_EC_TXERR, err);
+
+		if (err && net_ratelimit())
+			printk(KERN_ERR "%s: TX error %x\n", dev->name, err);
+	}
+
+	if (estat & (TSI108_EC_TXESTAT_Q0_DESCINT | TSI108_EC_TXESTAT_Q0_EOQ)) {
+		spin_lock(&data->txlock);
+		tsi108_check_for_completed_tx(dev);
+		spin_unlock(&data->txlock);
+	}
+}
+
+static irqreturn_t tsi108_irq_one(net_device * dev)
+{
+	tsi108_prv_data *data = netdev_priv(dev);
+	u32 stat = TSI108_ETH_READ_REG(TSI108_EC_INTSTAT);
+
+	if (!(stat & TSI108_INT_ANY))
+		return IRQ_NONE;	/* Not our interrupt */
+
+	stat &= ~TSI108_ETH_READ_REG(TSI108_EC_INTMASK);
+
+	if (stat & (TSI108_INT_TXQUEUE0 | TSI108_INT_TXIDLE |
+		    TSI108_INT_TXERROR))
+		tsi108_tx_int(dev);
+	if (stat & (TSI108_INT_RXQUEUE0 | TSI108_INT_RXTHRESH |
+		    TSI108_INT_RXWAIT | TSI108_INT_RXOVERRUN |
+		    TSI108_INT_RXERROR))
+		tsi108_rx_int(dev);
+
+	if (stat & TSI108_INT_SFN) {
+		if (net_ratelimit())
+			printk(KERN_DEBUG "%s: SFN error\n", dev->name);
+		TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, TSI108_INT_SFN);
+	}
+
+	if (stat & TSI108_INT_STATCARRY) {
+		tsi108_stat_carry(dev);
+		TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, TSI108_INT_STATCARRY);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t tsi108_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	if ((IRQ_TSI108_GIGE0 != irq) && (IRQ_TSI108_GIGE1 != irq))
+		return IRQ_NONE;	/* Not our interrupt */
+
+	return tsi108_irq_one((struct net_device *)dev_id);
+}
+
+static void tsi108_stop_ethernet(net_device * dev)
+{
+	tsi108_prv_data *data = netdev_priv(dev);
+
+	/* Disable all TX and RX queues ... */
+	TSI108_ETH_WRITE_REG(TSI108_EC_TXCTRL, 0);
+	TSI108_ETH_WRITE_REG(TSI108_EC_RXCTRL, 0);
+
+	/* ...and wait for them to become idle */
+	mb();
+	while (TSI108_ETH_READ_REG(TSI108_EC_TXSTAT) &
+	       TSI108_EC_TXSTAT_ACTIVE) ;
+	while (TSI108_ETH_READ_REG(TSI108_EC_RXSTAT) &
+	       TSI108_EC_RXSTAT_ACTIVE) ;
+}
+
+static void tsi108_reset_ether(tsi108_prv_data * data)
+{
+	TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1, TSI108_MAC_CFG1_SOFTRST);
+	udelay(100);
+	TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1, 0);
+
+	TSI108_ETH_WRITE_REG(TSI108_EC_PORTCTRL, TSI108_EC_PORTCTRL_STATRST);
+	udelay(100);
+	TSI108_ETH_WRITE_REG(TSI108_EC_PORTCTRL,
+			     TSI108_ETH_READ_REG(TSI108_EC_PORTCTRL) &
+			     ~TSI108_EC_PORTCTRL_STATRST);
+
+	TSI108_ETH_WRITE_REG(TSI108_EC_TXCFG, TSI108_EC_TXCFG_RST);
+	udelay(100);
+	TSI108_ETH_WRITE_REG(TSI108_EC_TXCFG,
+			     TSI108_ETH_READ_REG(TSI108_EC_TXCFG) &
+			     ~TSI108_EC_TXCFG_RST);
+
+	TSI108_ETH_WRITE_REG(TSI108_EC_RXCFG, TSI108_EC_RXCFG_RST);
+	udelay(100);
+	TSI108_ETH_WRITE_REG(TSI108_EC_RXCFG,
+			     TSI108_ETH_READ_REG(TSI108_EC_RXCFG) &
+			     ~TSI108_EC_RXCFG_RST);
+
+	TSI108_ETH_WRITE_REG(TSI108_MAC_MII_MGMT_CFG,
+			     TSI108_ETH_READ_REG(TSI108_MAC_MII_MGMT_CFG) |
+			     TSI108_MAC_MII_MGMT_RST);
+	udelay(100);
+	TSI108_ETH_WRITE_REG(TSI108_MAC_MII_MGMT_CFG,
+			     TSI108_ETH_READ_REG(TSI108_MAC_MII_MGMT_CFG) &
+			     ~(TSI108_MAC_MII_MGMT_RST |
+			       TSI108_MAC_MII_MGMT_CLK));
+
+	TSI108_ETH_WRITE_REG(TSI108_MAC_MII_MGMT_CFG,
+			     TSI108_ETH_READ_REG(TSI108_MAC_MII_MGMT_CFG) |
+			     TSI108_MAC_MII_MGMT_CLK);
+}
+
+static int tsi108_get_mac(net_device * dev)
+{
+	tsi108_prv_data *data = netdev_priv(dev);
+
+	u32 word1 = TSI108_ETH_READ_REG(TSI108_MAC_ADDR1);
+	u32 word2 = TSI108_ETH_READ_REG(TSI108_MAC_ADDR2);
+
+	/* Note that the octets are reversed from what the manual says,
+	 * producing an even weirder ordering...
+	 */
+	if (word2 == 0 && word1 == 0) {
+		dev->dev_addr[0] = 0x00;
+		dev->dev_addr[1] = 0x06;
+		dev->dev_addr[2] = 0xd2;
+		dev->dev_addr[3] = 0x00;
+		dev->dev_addr[4] = 0x00;
+		if (0x8 == data->phy)
+			dev->dev_addr[5] = 0x01;
+		else
+			dev->dev_addr[5] = 0x02;
+
+		word2 = (dev->dev_addr[0] << 16) | (dev->dev_addr[1] << 24);
+
+		word1 = (dev->dev_addr[2] << 0) | (dev->dev_addr[3] << 8) |
+		    (dev->dev_addr[4] << 16) | (dev->dev_addr[5] << 24);
+
+		TSI108_ETH_WRITE_REG(TSI108_MAC_ADDR1, word1);
+		TSI108_ETH_WRITE_REG(TSI108_MAC_ADDR2, word2);
+	} else {
+		dev->dev_addr[0] = (word2 >> 16) & 0xff;
+		dev->dev_addr[1] = (word2 >> 24) & 0xff;
+		dev->dev_addr[2] = (word1 >> 0) & 0xff;
+		dev->dev_addr[3] = (word1 >> 8) & 0xff;
+		dev->dev_addr[4] = (word1 >> 16) & 0xff;
+		dev->dev_addr[5] = (word1 >> 24) & 0xff;
+	}
+
+	if (!is_valid_ether_addr(dev->dev_addr)) {
+		printk("KERN_ERR: word1: %08x, word2: %08x\n", word1, word2);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int tsi108_set_mac(net_device * dev, void *addr)
+{
+	tsi108_prv_data *data = netdev_priv(dev);
+	u32 word1, word2;
+	int i;
+
+	if (!is_valid_ether_addr(addr))
+		return -EINVAL;
+
+	for (i = 0; i < 6; i++)
+		/* +2 is for the offset of the HW addr type */
+		dev->dev_addr[i] = ((unsigned char *)addr)[i + 2];
+
+	word2 = (dev->dev_addr[0] << 16) | (dev->dev_addr[1] << 24);
+
+	word1 = (dev->dev_addr[2] << 0) | (dev->dev_addr[3] << 8) |
+	    (dev->dev_addr[4] << 16) | (dev->dev_addr[5] << 24);
+
+	spin_lock_irq(&data->misclock);
+	TSI108_ETH_WRITE_REG(TSI108_MAC_ADDR1, word1);
+	TSI108_ETH_WRITE_REG(TSI108_MAC_ADDR2, word2);
+	spin_lock(&data->txlock);
+
+	if (netif_queue_stopped(dev) && data->txfree && data->link_up)
+		netif_wake_queue(dev);
+
+	spin_unlock(&data->txlock);
+	spin_unlock_irq(&data->misclock);
+	return 0;
+}
+
+/* Protected by dev->xmit_lock. */
+static void tsi108_set_rx_mode(net_device * dev)
+{
+	tsi108_prv_data *data = netdev_priv(dev);
+	u32 rxcfg = TSI108_ETH_READ_REG(TSI108_EC_RXCFG);
+
+	if (dev->flags & IFF_PROMISC) {
+		rxcfg &= ~(TSI108_EC_RXCFG_UC_HASH | TSI108_EC_RXCFG_MC_HASH);
+		rxcfg |= TSI108_EC_RXCFG_UFE | TSI108_EC_RXCFG_MFE;
+		goto out;
+	}
+
+	rxcfg &= ~(TSI108_EC_RXCFG_UFE | TSI108_EC_RXCFG_MFE);
+
+	if (dev->mc_count) {
+		int i;
+		struct dev_mc_list *mc = dev->mc_list;
+		rxcfg |= TSI108_EC_RXCFG_MFE | TSI108_EC_RXCFG_MC_HASH;
+
+		memset(data->mc_hash, 0, sizeof(data->mc_hash));
+
+		while (mc) {
+			u32 hash, crc;
+
+			if (mc->dmi_addrlen == 6) {
+				crc = ether_crc(6, mc->dmi_addr);
+				hash = crc >> 23;
+
+				__set_bit(hash, &data->mc_hash[0]);
+			} else {
+				printk(KERN_ERR
+				       "%s: got multicast address of length %d "
+				       "instead of 6.\n", dev->name,
+				       mc->dmi_addrlen);
+			}
+
+			mc = mc->next;
+		}
+
+		TSI108_ETH_WRITE_REG(TSI108_EC_HASHADDR,
+				     TSI108_EC_HASHADDR_AUTOINC |
+				     TSI108_EC_HASHADDR_MCAST);
+
+		for (i = 0; i < 16; i++) {
+			/* The manual says that the hardware may drop
+			 * back-to-back writes to the data register.
+			 */
+			udelay(1);
+			mb();
+			TSI108_ETH_WRITE_REG(TSI108_EC_HASHDATA,
+					     data->mc_hash[i]);
+			mb();
+		}
+	}
+
+      out:
+	mb();
+	TSI108_ETH_WRITE_REG(TSI108_EC_RXCFG, rxcfg);
+}
+
+static void tsi108_init_phy(net_device * dev)
+{
+	tsi108_prv_data *data = netdev_priv(dev);
+	u32 i = 0;
+	u16 phyVal = 0;
+
+	spin_lock_irq(&phy_lock);
+
+	tsi108_write_mii(data, PHY_CTRL, PHY_CTRL_RESET);
+	mb();
+	while (tsi108_read_mii(data, PHY_CTRL, NULL) & PHY_CTRL_RESET) ;
+
+#if (TSI108_PHY_TYPE == PHY_BCM54XX)	/* Broadcom BCM54xx PHY */
+	tsi108_write_mii(data, 0x09, 0x0300);
+	tsi108_write_mii(data, 0x10, 0x1020);
+	tsi108_write_mii(data, 0x1c, 0x8c00);
+	mb();
+#endif
+
+	tsi108_write_mii(data,
+			 PHY_CTRL,
+			 PHY_CTRL_AUTONEG_EN | PHY_CTRL_AUTONEG_START);
+	mb();
+	while (tsi108_read_mii(data, PHY_CTRL, NULL) & PHY_CTRL_AUTONEG_START) ;
+
+	/* Set G/MII mode and receive clock select in TBI control #2.  The
+	 * second port won't work if this isn't done, even though we don't
+	 * use TBI mode.
+	 */
+
+	tsi108_write_tbi(data, 0x11, 0x30);
+
+	/* FIXME: It seems to take more than 2 back-to-back reads to the
+	 * PHY_STAT register before the link up status bit is set.
+	 */
+
+	data->link_up = 1;
+
+	while (!((phyVal = tsi108_read_mii(data, PHY_STAT, NULL)) &
+		 PHY_STAT_LINKUP)) {
+		if (i++ > (MII_READ_DELAY / 10)) {
+			data->link_up = 0;
+			break;
+		}
+		mdelay(10);
+	}
+
+	printk(KERN_DEBUG "PHY_STAT reg contains %08x\n", phyVal);
+	data->phy_ok = 1;
+	spin_unlock_irq(&phy_lock);
+}
+
+static void tsi108_kill_phy(struct net_device *dev)
+{
+	tsi108_prv_data *data = netdev_priv(dev);
+
+	spin_lock_irq(&phy_lock);
+	tsi108_write_mii(data, PHY_CTRL, PHY_CTRL_POWERDOWN);
+	data->phy_ok = 0;
+	spin_unlock_irq(&phy_lock);
+}
+
+static int tsi108_open(struct net_device *dev)
+{
+	int i;
+	tsi108_prv_data *data = netdev_priv(dev);
+	unsigned int rxring_size = TSI108_RXRING_LEN * sizeof(rx_desc);
+	unsigned int txring_size = TSI108_TXRING_LEN * sizeof(tx_desc);
+
+	printk(KERN_DEBUG "Inside tsi108_open()!\n");
+
+	i = request_irq(data->irq_num, tsi108_irq, 0, dev->name, dev);
+	if (i != 0) {
+		printk(KERN_ERR "tsi108_eth%d: Could not allocate IRQ%d.\n",
+		       data->irq_num % IRQ_TSI108_GIGE0, data->irq_num);
+		return i;
+	} else {
+		dev->irq = data->irq_num;
+		printk(KERN_NOTICE
+		       "tsi108_open : Port %d Assigned IRQ %d to %s\n",
+		       data->irq_num % IRQ_TSI108_GIGE0, dev->irq, dev->name);
+	}
+
+	data->rxring = pci_alloc_consistent(NULL, rxring_size, &data->rxdma);
+
+	if (!data->rxring) {
+		printk(KERN_DEBUG
+		       "TSI108_ETH: failed to allocate memory for rxring!\n");
+		return -ENOMEM;
+	} else {
+		memset(data->rxring, 0, rxring_size);
+	}
+
+	data->txring = pci_alloc_consistent(NULL, txring_size, &data->txdma);
+
+	if (!data->txring) {
+		printk(KERN_DEBUG
+		       "TSI108_ETH: failed to allocate memory for txring!\n");
+		pci_free_consistent(0, rxring_size, data->rxring, data->rxdma);
+		return -ENOMEM;
+	} else {
+		memset(data->txring, 0, txring_size);
+	}
+
+	for (i = 0; i < TSI108_RXRING_LEN; i++) {
+		data->rxring[i].next0 = data->rxdma + (i + 1) * sizeof(rx_desc);
+		data->rxring[i].blen = TSI108_RXBUF_SIZE;
+		data->rxring[i].vlan = 0;
+	}
+
+	data->rxring[TSI108_RXRING_LEN - 1].next0 = data->rxdma;
+
+	data->rxtail = 0;
+	data->rxhead = 0;
+
+	for (i = 0; i < TSI108_RXRING_LEN; i++) {
+		sk_buff *skb = dev_alloc_skb(TSI108_RXBUF_SIZE + NET_IP_ALIGN);
+
+		if (!skb) {
+			/* Bah.  No memory for now, but maybe we'll get
+			 * some more later.
+			 * For now, we'll live with the smaller ring.
+			 */
+			printk(KERN_WARNING
+			       "%s: Could only allocate %d receive skb(s).\n",
+			       dev->name, i);
+			data->rxhead = i;
+			break;
+		}
+
+		data->rxskbs[i] = skb;
+		/* Align the payload on a 4-byte boundary */
+		skb_reserve(skb, 2);
+		data->rxskbs[i] = skb;
+		data->rxring[i].buf0 = virt_to_phys(data->rxskbs[i]->data);
+		data->rxring[i].misc = TSI108_RX_OWN | TSI108_RX_INT;
+	}
+
+	data->rxfree = i;
+	TSI108_ETH_WRITE_REG(TSI108_EC_RXQ_PTRLOW, data->rxdma);
+
+	for (i = 0; i < TSI108_TXRING_LEN; i++) {
+		data->txring[i].next0 = data->txdma + (i + 1) * sizeof(tx_desc);
+		data->txring[i].misc = 0;
+	}
+
+	data->txring[TSI108_TXRING_LEN - 1].next0 = data->txdma;
+	data->txtail = 0;
+	data->txhead = 0;
+	data->txfree = TSI108_TXRING_LEN;
+	TSI108_ETH_WRITE_REG(TSI108_EC_TXQ_PTRLOW, data->txdma);
+	tsi108_init_phy(dev);
+
+	init_timer(&data->timer);
+	data->timer.expires = jiffies + 1;
+	data->timer.data = (unsigned long)dev;
+	data->timer.function = &tsi108_timed_checker;	/* timer handler */
+	add_timer(&data->timer);
+
+	tsi108_restart_rx(data, dev);
+
+	TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, ~0);
+	mb();
+
+	TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK,
+			     ~(TSI108_INT_TXQUEUE0 | TSI108_INT_RXERROR |
+			       TSI108_INT_RXTHRESH | TSI108_INT_RXQUEUE0 |
+			       TSI108_INT_RXOVERRUN | TSI108_INT_RXWAIT |
+			       TSI108_INT_SFN | TSI108_INT_STATCARRY));
+
+	TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1,
+			     TSI108_MAC_CFG1_RXEN | TSI108_MAC_CFG1_TXEN);
+	netif_start_queue(dev);
+	return 0;
+}
+
+static int tsi108_close(net_device * dev)
+{
+	tsi108_prv_data *data = netdev_priv(dev);
+
+	netif_stop_queue(dev);
+
+	del_timer_sync(&data->timer);
+
+	printk(KERN_DEBUG "Inside tsi108_ifdown!\n");
+
+	tsi108_stop_ethernet(dev);
+	tsi108_kill_phy(dev);
+	TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK, ~0);
+	TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1, 0);
+
+	/* Check for any pending TX packets, and drop them. */
+
+	while (!data->txfree || data->txhead != data->txtail) {
+		int tx = data->txtail;
+		struct sk_buff *skb;
+		skb = data->txskbs[tx];
+		data->txtail = (data->txtail + 1) % TSI108_TXRING_LEN;
+		data->txfree++;
+		dev_kfree_skb(skb);
+	}
+
+	synchronize_irq(data->irq_num);
+	free_irq(data->irq_num, dev);
+
+	/* Discard the RX ring. */
+
+	while (data->rxfree) {
+		int rx = data->rxtail;
+		struct sk_buff *skb;
+
+		skb = data->rxskbs[rx];
+		data->rxtail = (data->rxtail + 1) % TSI108_RXRING_LEN;
+		data->rxfree--;
+		dev_kfree_skb(skb);
+	}
+
+	pci_free_consistent(0,
+			    TSI108_RXRING_LEN * sizeof(rx_desc),
+			    data->rxring, data->rxdma);
+	pci_free_consistent(0,
+			    TSI108_TXRING_LEN * sizeof(tx_desc),
+			    data->txring, data->txdma);
+
+	return 0;
+}
+
+static void tsi108_init_mac(net_device * dev)
+{
+	tsi108_prv_data *data = netdev_priv(dev);
+
+	TSI108_ETH_WRITE_REG(TSI108_MAC_CFG2, TSI108_MAC_CFG2_DFLT_PREAMBLE |
+			     TSI108_MAC_CFG2_PADCRC);
+
+	TSI108_ETH_WRITE_REG(TSI108_EC_TXTHRESH,
+			     (192 << TSI108_EC_TXTHRESH_STARTFILL) |
+			     (192 << TSI108_EC_TXTHRESH_STOPFILL));
+
+	TSI108_ETH_WRITE_REG(TSI108_STAT_CARRYMASK1,
+			     ~(TSI108_STAT_CARRY1_RXBYTES |
+			       TSI108_STAT_CARRY1_RXPKTS |
+			       TSI108_STAT_CARRY1_RXFCS |
+			       TSI108_STAT_CARRY1_RXMCAST |
+			       TSI108_STAT_CARRY1_RXALIGN |
+			       TSI108_STAT_CARRY1_RXLENGTH |
+			       TSI108_STAT_CARRY1_RXRUNT |
+			       TSI108_STAT_CARRY1_RXJUMBO |
+			       TSI108_STAT_CARRY1_RXFRAG |
+			       TSI108_STAT_CARRY1_RXJABBER |
+			       TSI108_STAT_CARRY1_RXDROP));
+
+	TSI108_ETH_WRITE_REG(TSI108_STAT_CARRYMASK2,
+			     ~(TSI108_STAT_CARRY2_TXBYTES |
+			       TSI108_STAT_CARRY2_TXPKTS |
+			       TSI108_STAT_CARRY2_TXEXDEF |
+			       TSI108_STAT_CARRY2_TXEXCOL |
+			       TSI108_STAT_CARRY2_TXTCOL |
+			       TSI108_STAT_CARRY2_TXPAUSE));
+
+	TSI108_ETH_WRITE_REG(TSI108_EC_PORTCTRL, TSI108_EC_PORTCTRL_STATEN);
+	TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1, 0);
+
+	TSI108_ETH_WRITE_REG(TSI108_EC_RXCFG,
+			     TSI108_EC_RXCFG_SE | TSI108_EC_RXCFG_BFE);
+
+	TSI108_ETH_WRITE_REG(TSI108_EC_TXQ_CFG, TSI108_EC_TXQ_CFG_DESC_INT |
+			     TSI108_EC_TXQ_CFG_EOQ_OWN_INT |
+			     TSI108_EC_TXQ_CFG_WSWP | (TSI108_PBM_PORT <<
+						       TSI108_EC_TXQ_CFG_SFNPORT));
+
+	TSI108_ETH_WRITE_REG(TSI108_EC_RXQ_CFG, TSI108_EC_RXQ_CFG_DESC_INT |
+			     TSI108_EC_RXQ_CFG_EOQ_OWN_INT |
+			     TSI108_EC_RXQ_CFG_WSWP | (TSI108_PBM_PORT <<
+						       TSI108_EC_RXQ_CFG_SFNPORT));
+
+	TSI108_ETH_WRITE_REG(TSI108_EC_TXQ_BUFCFG,
+			     TSI108_EC_TXQ_BUFCFG_BURST256 |
+			     TSI108_EC_TXQ_BUFCFG_BSWP | (TSI108_PBM_PORT <<
+							  TSI108_EC_TXQ_BUFCFG_SFNPORT));
+
+	TSI108_ETH_WRITE_REG(TSI108_EC_RXQ_BUFCFG,
+			     TSI108_EC_RXQ_BUFCFG_BURST256 |
+			     TSI108_EC_RXQ_BUFCFG_BSWP | (TSI108_PBM_PORT <<
+							  TSI108_EC_RXQ_BUFCFG_SFNPORT));
+
+	TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK, ~0);
+}
+
+static int tsi108_ioctl(net_device * dev, struct ifreq *rq, int cmd)
+{
+	tsi108_prv_data *data = netdev_priv(dev);
+	struct mii_ioctl_data *mii_data =
+	    (struct mii_ioctl_data *)&rq->ifr_data;
+	int ret;
+
+	switch (cmd) {
+	case SIOCGMIIPHY:
+		mii_data->phy_id = data->phy;
+		ret = 0;
+		break;
+
+	case SIOCGMIIREG:
+		spin_lock_irq(&phy_lock);
+		mii_data->val_out =
+		    tsi108_read_mii(data, mii_data->reg_num, &ret);
+		spin_unlock_irq(&phy_lock);
+		break;
+
+	default:
+		ret = -EOPNOTSUPP;
+	}
+
+	return ret;
+}
+
+static int
+tsi108_init_one(struct platform_device *pdev)
+{
+	struct net_device *dev = NULL;
+	tsi108_prv_data *data = NULL;
+	hw_info *einfo;
+	int ret;
+	
+	einfo = ( hw_info *) pdev->dev.platform_data;
+	
+	if (NULL == einfo) {
+		printk(KERN_ERR "tsi-eth %d: Missing additional data!\n",
+		       pdev->id);
+		return -ENODEV;
+	}
+
+	/* Create an ethernet device instance */
+
+	dev = alloc_etherdev(sizeof(tsi108_prv_data));
+	if (!dev) {
+		printk("tsi108_eth: Could not allocate a device structure\n");
+		return -ENOMEM;
+	}
+
+	printk("tsi108_eth%d: probe...\n", pdev->id);
+	data = netdev_priv(dev);
+	memset(data, 0, sizeof(tsi108_prv_data));
+
+	DBG("tsi108_eth%d: regs:phyresgs:phy:irq_num=0x%x:0x%x:0x%x:0x%x\n",
+			pdev->id, einfo->regs, einfo->phyregs,
+			einfo->phy, einfo->irq_num);
+
+	data->regs = (u32)ioremap(einfo->regs, 0x400);	/*FIX ME */
+	data->phyregs = (u32)ioremap(einfo->phyregs, 0x400); 	/*FIX ME */
+	data->phy = einfo->phy;
+	data->irq_num = einfo->irq_num;
+	dev->open = tsi108_open;
+	dev->stop = tsi108_close;
+	dev->hard_start_xmit = tsi108_send_packet;
+	dev->set_mac_address = tsi108_set_mac;
+	dev->set_multicast_list = tsi108_set_rx_mode;
+	dev->get_stats = tsi108_get_stats;
+	dev->poll = tsi108_poll;
+	dev->do_ioctl = tsi108_ioctl;
+	dev->weight = 64;	/* 64 is more suitable for GigE interface - klai */
+
+	/* Apparently, the Linux networking code won't use scatter-gather
+	 * if the hardware doesn't do checksums.  However, it's faster
+	 * to checksum in place and use SG, as (among other reasons)
+	 * the cache won't be dirtied (which then has to be flushed
+	 * before DMA).  The checksumming is done by the driver (via
+	 * a new function skb_csum_dev() in net/core/skbuff.c).
+	 */
+
+#ifdef FIXME_SG_CSUM_NOT_TESTED	/* FIXME: Not supported now. */
+	dev->features = NETIF_F_HIGHDMA | NETIF_F_SG | NETIF_F_HW_CSUM;
+#else
+	dev->features = NETIF_F_HIGHDMA;
+#endif
+	SET_MODULE_OWNER(dev);
+
+	spin_lock_init(&data->txlock);
+	spin_lock_init(&data->misclock);
+
+	tsi108_reset_ether(data);
+	tsi108_kill_phy(dev);
+
+	if (tsi108_get_mac(dev) != 0)
+		printk(KERN_ERR "%s: Invalid MAC address.  Please correct.\n",
+		       dev->name);
+
+	tsi108_init_mac(dev);
+	ret = register_netdev(dev);
+	if (ret < 0) {
+		kfree(dev);
+		return ret;
+	}
+
+	printk(KERN_INFO "%s: Tsi108 Gigabit Ethernet, MAC: "
+	       "%02x:%02x:%02x:%02x:%02x:%02x\n", dev->name,
+	       dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
+	       dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
+#ifdef DEBUG
+	dump_eth_one(dev);
+#endif
+
+	return 0;
+}
+
+/* There's no way to either get interrupts from the PHY when
+ * something changes, or to have the Tsi108 automatically communicate
+ * with the PHY to reconfigure itself.
+ *
+ * Thus, we have to do it using a timer. 
+ */
+
+static void tsi108_timed_checker(unsigned long dev_ptr)
+{
+	struct net_device *dev = (struct net_device *)dev_ptr;
+	tsi108_prv_data *data = netdev_priv(dev);
+
+	tsi108_check_phy(dev);
+	tsi108_check_rxring(dev);
+	data->timer.expires = jiffies + CHECK_PHY_INTERVAL;
+	add_timer(&data->timer);
+}
+
+static int tsi108_ether_init(void)
+{
+	int ret;
+	ret = platform_driver_register (&tsi_eth_driver);
+	if (ret < 0){
+		printk("tsi108_ether_init: error initializing ethernet "
+		       "device\n");
+		return ret;
+	}
+	return 0;
+}
+
+static int tsi108_ether_remove(struct platform_device *pdev)
+{
+	struct net_device *dev = platform_get_drvdata(pdev);
+	tsi108_prv_data *priv = netdev_priv(dev);
+	
+	unregister_netdev(dev);
+	tsi108_stop_ethernet(dev);
+	platform_set_drvdata(pdev, NULL);
+	iounmap((void __iomem *)priv->regs);
+	iounmap((void __iomem *)priv->phyregs);
+	free_netdev(dev);
+	
+	return 0;
+}
+static void tsi108_ether_exit(void)
+{
+	platform_driver_unregister(&tsi_eth_driver);
+}
+
+module_init(tsi108_ether_init);
+module_exit(tsi108_ether_exit);
+
+MODULE_AUTHOR("Tundra Semiconductor Corporation");
+MODULE_DESCRIPTION("Tsi108 Gigabit Ethernet driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/tsi108_eth.h b/drivers/net/tsi108_eth.h
new file mode 100644
index 0000000..cb54f92
--- /dev/null
+++ b/drivers/net/tsi108_eth.h
@@ -0,0 +1,404 @@
+/*
+ * (C) Copyright 2005 Tundra Semiconductor Corp.
+ * Kong Lai, <kong.lai@tundra.com).
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+/*
+ * net/tsi108_eth.h - definitions for Tsi108 GIGE network controller.
+ */
+
+#ifndef __TSI108_ETH_H
+#define __TSI108_ETH_H
+
+#include <linux/config.h>
+#include <linux/types.h>
+
+#define TSI108_ETH_WRITE_REG(offset, val) \
+	out_be32((volatile u32 *)(data->regs + offset), val)
+
+#define TSI108_ETH_READ_REG(offset) \
+	in_be32((volatile u32 *)(data->regs + offset))
+
+#define TSI108_ETH_WRITE_PHYREG(offset, val) \
+	out_be32((volatile u32 *)(data->phyregs + offset), val)
+
+#define TSI108_ETH_READ_PHYREG(offset) \
+	in_be32((volatile u32 *)(data->phyregs + offset))
+
+/*
+ * PHY Configuration Options
+ *
+ * NOTE: Enable set of definitions corresponding to your board type
+ */
+#define PHY_MV88E	1	/* Marvel 88Exxxx PHY */
+#define PHY_BCM54XX	2	/* Broardcom BCM54xx PHY */
+#define TSI108_PHY_TYPE	PHY_MV88E
+
+/*
+ * TSI108 GIGE port registers
+ */
+
+#define TSI108_ETH_PORT_NUM		2
+#define TSI108_PBM_PORT			2
+#define TSI108_SDRAM_PORT		4
+
+#define TSI108_MAC_CFG1			(0x000)
+#define TSI108_MAC_CFG1_SOFTRST		(1 << 31)
+#define TSI108_MAC_CFG1_LOOPBACK	(1 << 8)
+#define TSI108_MAC_CFG1_RXEN		(1 << 2)
+#define TSI108_MAC_CFG1_TXEN		(1 << 0)
+
+#define TSI108_MAC_CFG2			(0x004)
+#define TSI108_MAC_CFG2_DFLT_PREAMBLE	(7 << 12)
+#define TSI108_MAC_CFG2_IFACE_MASK	(3 << 8)
+#define TSI108_MAC_CFG2_NOGIG		(1 << 8)
+#define TSI108_MAC_CFG2_GIG		(2 << 8)
+#define TSI108_MAC_CFG2_PADCRC		(1 << 2)
+#define TSI108_MAC_CFG2_FULLDUPLEX	(1 << 0)
+
+#define TSI108_MAC_MII_MGMT_CFG		(0x020)
+#define TSI108_MAC_MII_MGMT_CLK		(7 << 0)
+#define TSI108_MAC_MII_MGMT_RST		(1 << 31)
+
+#define TSI108_MAC_MII_CMD		(0x024)
+#define TSI108_MAC_MII_CMD_READ		(1 << 0)
+
+#define TSI108_MAC_MII_ADDR		(0x028)
+#define TSI108_MAC_MII_ADDR_REG		0
+#define TSI108_MAC_MII_ADDR_PHY		8
+
+#define TSI108_MAC_MII_DATAOUT		(0x02c)
+#define TSI108_MAC_MII_DATAIN		(0x030)
+
+#define TSI108_MAC_MII_IND		(0x034)
+#define TSI108_MAC_MII_IND_NOTVALID	(1 << 2)
+#define TSI108_MAC_MII_IND_SCANNING	(1 << 1)
+#define TSI108_MAC_MII_IND_BUSY		(1 << 0)
+
+#define TSI108_MAC_IFCTRL		(0x038)
+#define TSI108_MAC_IFCTRL_PHYMODE	(1 << 24)
+
+#define TSI108_MAC_ADDR1		(0x040)
+#define TSI108_MAC_ADDR2		(0x044)
+
+#define TSI108_STAT_RXBYTES		(0x06c)
+#define TSI108_STAT_RXBYTES_CARRY	(1 << 24)
+
+#define TSI108_STAT_RXPKTS		(0x070)
+#define TSI108_STAT_RXPKTS_CARRY	(1 << 18)
+
+#define TSI108_STAT_RXFCS		(0x074)
+#define TSI108_STAT_RXFCS_CARRY		(1 << 12)
+
+#define TSI108_STAT_RXMCAST		(0x078)
+#define TSI108_STAT_RXMCAST_CARRY	(1 << 18)
+
+#define TSI108_STAT_RXALIGN		(0x08c)
+#define TSI108_STAT_RXALIGN_CARRY	(1 << 12)
+
+#define TSI108_STAT_RXLENGTH		(0x090)
+#define TSI108_STAT_RXLENGTH_CARRY	(1 << 12)
+
+#define TSI108_STAT_RXRUNT		(0x09c)
+#define TSI108_STAT_RXRUNT_CARRY	(1 << 12)
+
+#define TSI108_STAT_RXJUMBO		(0x0a0)
+#define TSI108_STAT_RXJUMBO_CARRY	(1 << 12)
+
+#define TSI108_STAT_RXFRAG		(0x0a4)
+#define TSI108_STAT_RXFRAG_CARRY	(1 << 12)
+
+#define TSI108_STAT_RXJABBER		(0x0a8)
+#define TSI108_STAT_RXJABBER_CARRY	(1 << 12)
+
+#define TSI108_STAT_RXDROP		(0x0ac)
+#define TSI108_STAT_RXDROP_CARRY	(1 << 12)
+
+#define TSI108_STAT_TXBYTES		(0x0b0)
+#define TSI108_STAT_TXBYTES_CARRY	(1 << 24)
+
+#define TSI108_STAT_TXPKTS		(0x0b4)
+#define TSI108_STAT_TXPKTS_CARRY	(1 << 18)
+
+#define TSI108_STAT_TXEXDEF		(0x0c8)
+#define TSI108_STAT_TXEXDEF_CARRY	(1 << 12)
+
+#define TSI108_STAT_TXEXCOL		(0x0d8)
+#define TSI108_STAT_TXEXCOL_CARRY	(1 << 12)
+
+#define TSI108_STAT_TXTCOL		(0x0dc)
+#define TSI108_STAT_TXTCOL_CARRY	(1 << 13)
+
+#define TSI108_STAT_TXPAUSEDROP		(0x0e4)
+#define TSI108_STAT_TXPAUSEDROP_CARRY	(1 << 12)
+
+#define TSI108_STAT_CARRY1		(0x100)
+#define TSI108_STAT_CARRY1_RXBYTES	(1 << 16)
+#define TSI108_STAT_CARRY1_RXPKTS	(1 << 15)
+#define TSI108_STAT_CARRY1_RXFCS	(1 << 14)
+#define TSI108_STAT_CARRY1_RXMCAST	(1 << 13)
+#define TSI108_STAT_CARRY1_RXALIGN	(1 << 8)
+#define TSI108_STAT_CARRY1_RXLENGTH	(1 << 7)
+#define TSI108_STAT_CARRY1_RXRUNT	(1 << 4)
+#define TSI108_STAT_CARRY1_RXJUMBO	(1 << 3)
+#define TSI108_STAT_CARRY1_RXFRAG	(1 << 2)
+#define TSI108_STAT_CARRY1_RXJABBER	(1 << 1)
+#define TSI108_STAT_CARRY1_RXDROP	(1 << 0)
+
+#define TSI108_STAT_CARRY2		(0x104)
+#define TSI108_STAT_CARRY2_TXBYTES	(1 << 13)
+#define TSI108_STAT_CARRY2_TXPKTS	(1 << 12)
+#define TSI108_STAT_CARRY2_TXEXDEF	(1 << 7)
+#define TSI108_STAT_CARRY2_TXEXCOL	(1 << 3)
+#define TSI108_STAT_CARRY2_TXTCOL	(1 << 2)
+#define TSI108_STAT_CARRY2_TXPAUSE	(1 << 0)
+
+#define TSI108_STAT_CARRYMASK1		(0x108)
+#define TSI108_STAT_CARRYMASK2		(0x10c)
+
+#define TSI108_EC_PORTCTRL		(0x200)
+#define TSI108_EC_PORTCTRL_STATRST	(1 << 31)
+#define TSI108_EC_PORTCTRL_STATEN	(1 << 28)
+#define TSI108_EC_PORTCTRL_NOGIG	(1 << 18)
+#define TSI108_EC_PORTCTRL_HALFDUPLEX	(1 << 16)
+
+#define TSI108_EC_INTSTAT		(0x204)
+#define TSI108_EC_INTMASK		(0x208)
+
+#define TSI108_INT_ANY			(1 << 31)
+#define TSI108_INT_SFN			(1 << 30)
+#define TSI108_INT_RXIDLE		(1 << 29)
+#define TSI108_INT_RXABORT		(1 << 28)
+#define TSI108_INT_RXERROR		(1 << 27)
+#define TSI108_INT_RXOVERRUN		(1 << 26)
+#define TSI108_INT_RXTHRESH		(1 << 25)
+#define TSI108_INT_RXWAIT		(1 << 24)
+#define TSI108_INT_RXQUEUE0		(1 << 16)
+#define TSI108_INT_STATCARRY		(1 << 15)
+#define TSI108_INT_TXIDLE		(1 << 13)
+#define TSI108_INT_TXABORT		(1 << 12)
+#define TSI108_INT_TXERROR		(1 << 11)
+#define TSI108_INT_TXUNDERRUN		(1 << 10)
+#define TSI108_INT_TXTHRESH		(1 <<  9)
+#define TSI108_INT_TXWAIT		(1 <<  8)
+#define TSI108_INT_TXQUEUE0		(1 <<  0)
+
+#define TSI108_EC_TXCFG			(0x220)
+#define TSI108_EC_TXCFG_RST		(1 << 31)
+
+#define TSI108_EC_TXCTRL		(0x224)
+#define TSI108_EC_TXCTRL_IDLEINT	(1 << 31)
+#define TSI108_EC_TXCTRL_ABORT		(1 << 30)
+#define TSI108_EC_TXCTRL_GO		(1 << 15)
+#define TSI108_EC_TXCTRL_QUEUE0		(1 <<  0)
+
+#define TSI108_EC_TXSTAT		(0x228)
+#define TSI108_EC_TXSTAT_ACTIVE		(1 << 15)
+#define TSI108_EC_TXSTAT_QUEUE0		(1 << 0)
+
+#define TSI108_EC_TXESTAT		(0x22c)
+#define TSI108_EC_TXESTAT_Q0_ERR	(1 << 24)
+#define TSI108_EC_TXESTAT_Q0_DESCINT	(1 << 16)
+#define TSI108_EC_TXESTAT_Q0_EOF	(1 <<  8)
+#define TSI108_EC_TXESTAT_Q0_EOQ	(1 <<  0)
+
+#define TSI108_EC_TXERR			(0x278)
+
+#define TSI108_EC_TXQ_CFG		(0x280)
+#define TSI108_EC_TXQ_CFG_DESC_INT	(1 << 20)
+#define TSI108_EC_TXQ_CFG_EOQ_OWN_INT	(1 << 19)
+#define TSI108_EC_TXQ_CFG_WSWP		(1 << 11)
+#define TSI108_EC_TXQ_CFG_BSWP		(1 << 10)
+#define TSI108_EC_TXQ_CFG_SFNPORT	0
+
+#define TSI108_EC_TXQ_BUFCFG		(0x284)
+#define TSI108_EC_TXQ_BUFCFG_BURST8	(0 << 8)
+#define TSI108_EC_TXQ_BUFCFG_BURST32	(1 << 8)
+#define TSI108_EC_TXQ_BUFCFG_BURST128	(2 << 8)
+#define TSI108_EC_TXQ_BUFCFG_BURST256	(3 << 8)
+#define TSI108_EC_TXQ_BUFCFG_WSWP	(1 << 11)
+#define TSI108_EC_TXQ_BUFCFG_BSWP	(1 << 10)
+#define TSI108_EC_TXQ_BUFCFG_SFNPORT	0
+
+#define TSI108_EC_TXQ_PTRLOW		(0x288)
+
+#define TSI108_EC_TXQ_PTRHIGH		(0x28c)
+#define TSI108_EC_TXQ_PTRHIGH_VALID	(1 << 31)
+
+#define TSI108_EC_TXTHRESH		(0x230)
+#define TSI108_EC_TXTHRESH_STARTFILL	0
+#define TSI108_EC_TXTHRESH_STOPFILL	16
+
+#define TSI108_EC_RXCFG			(0x320)
+#define TSI108_EC_RXCFG_RST		(1 << 31)
+
+#define TSI108_EC_RXSTAT		(0x328)
+#define TSI108_EC_RXSTAT_ACTIVE		(1 << 15)
+#define TSI108_EC_RXSTAT_QUEUE0		(1 << 0)
+
+#define TSI108_EC_RXESTAT		(0x32c)
+#define TSI108_EC_RXESTAT_Q0_ERR	(1 << 24)
+#define TSI108_EC_RXESTAT_Q0_DESCINT	(1 << 16)
+#define TSI108_EC_RXESTAT_Q0_EOF	(1 <<  8)
+#define TSI108_EC_RXESTAT_Q0_EOQ	(1 <<  0)
+
+#define TSI108_EC_HASHADDR		(0x360)
+#define TSI108_EC_HASHADDR_AUTOINC	(1 << 31)
+#define TSI108_EC_HASHADDR_DO1STREAD	(1 << 30)
+#define TSI108_EC_HASHADDR_UNICAST	(0 <<  4)
+#define TSI108_EC_HASHADDR_MCAST	(1 <<  4)
+
+#define TSI108_EC_HASHDATA		(0x364)
+
+#define TSI108_EC_RXQ_PTRLOW		(0x388)
+
+#define TSI108_EC_RXQ_PTRHIGH		(0x38c)
+#define TSI108_EC_RXQ_PTRHIGH_VALID	(1 << 31)
+
+/* Station Enable -- accept packets destined for us */
+#define TSI108_EC_RXCFG_SE		(1 << 13)
+/* Unicast Frame Enable -- for packets not destined for us */
+#define TSI108_EC_RXCFG_UFE		(1 << 12)
+/* Multicast Frame Enable */
+#define TSI108_EC_RXCFG_MFE		(1 << 11)
+/* Broadcast Frame Enable */
+#define TSI108_EC_RXCFG_BFE		(1 << 10)
+#define TSI108_EC_RXCFG_UC_HASH		(1 <<  9)
+#define TSI108_EC_RXCFG_MC_HASH		(1 <<  8)
+
+#define TSI108_EC_RXQ_CFG		(0x380)
+#define TSI108_EC_RXQ_CFG_DESC_INT	(1 << 20)
+#define TSI108_EC_RXQ_CFG_EOQ_OWN_INT	(1 << 19)
+#define TSI108_EC_RXQ_CFG_WSWP		(1 << 11)
+#define TSI108_EC_RXQ_CFG_BSWP		(1 << 10)
+#define TSI108_EC_RXQ_CFG_SFNPORT	0
+
+#define TSI108_EC_RXQ_BUFCFG		(0x384)
+#define TSI108_EC_RXQ_BUFCFG_BURST8	(0 << 8)
+#define TSI108_EC_RXQ_BUFCFG_BURST32	(1 << 8)
+#define TSI108_EC_RXQ_BUFCFG_BURST128	(2 << 8)
+#define TSI108_EC_RXQ_BUFCFG_BURST256	(3 << 8)
+#define TSI108_EC_RXQ_BUFCFG_WSWP	(1 << 11)
+#define TSI108_EC_RXQ_BUFCFG_BSWP	(1 << 10)
+#define TSI108_EC_RXQ_BUFCFG_SFNPORT	0
+
+#define TSI108_EC_RXCTRL		(0x324)
+#define TSI108_EC_RXCTRL_ABORT		(1 << 30)
+#define TSI108_EC_RXCTRL_GO		(1 << 15)
+#define TSI108_EC_RXCTRL_QUEUE0		(1 << 0)
+
+#define TSI108_EC_RXERR			(0x378)
+
+#define PHY_CTRL			0
+#define PHY_CTRL_RESET			(1 << 15)
+#define PHY_CTRL_AUTONEG_EN		(1 << 12)
+#define PHY_CTRL_POWERDOWN		(1 << 11)
+#define PHY_CTRL_AUTONEG_START		(1 << 9)
+
+#define PHY_STAT			1
+#define PHY_STAT_LINKUP			(1 << 2)
+
+#if (TSI108_PHY_TYPE == PHY_MV88E)
+/* Marvel 88E1xxx-specific */
+#define PHY_SUM_STAT			0x11
+#define PHY_SUM_STAT_SPEED_MASK		(3 << 14)
+#define PHY_SUM_STAT_SPEED_10		(0 << 14)
+#define PHY_SUM_STAT_SPEED_100		(1 << 14)
+#define PHY_SUM_STAT_SPEED_1000		(2 << 14)
+#define PHY_SUM_STAT_FULLDUPLEX		(1 << 13)
+
+#define PHY_SUM_STAT_1000T_FD    (PHY_SUM_STAT_SPEED_1000 | PHY_SUM_STAT_FULLDUPLEX)
+#define PHY_SUM_STAT_1000T_HD    (PHY_SUM_STAT_SPEED_1000)
+#define PHY_SUM_STAT_100TX_FD    (PHY_SUM_STAT_SPEED_100 | PHY_SUM_STAT_FULLDUPLEX)
+#define PHY_SUM_STAT_100TX_HD    (PHY_SUM_STAT_SPEED_100)
+#define PHY_SUM_STAT_10T_FD	    (PHY_SUM_STAT_SPEED_10 | PHY_SUM_STAT_FULLDUPLEX)
+#define PHY_SUM_STAT_10T_HD	    (PHY_SUM_STAT_SPEED_10)
+#elif (TSI108_PHY_TYPE == PHY_BCM54XX)
+/*Broadcom BCM54xx */
+#define PHY_SUM_STAT			0x19
+#define PHY_SUM_STAT_SPEED_MASK		(7 << 8)	/* Auto Negotiation HCD */
+#define PHY_SUM_STAT_1000T_FD		(7 << 8)	/* 1000BASE-T Full-Duplex */
+#define PHY_SUM_STAT_1000T_HD		(6 << 8)	/* 1000BASE-T Half-Duplex */
+#define PHY_SUM_STAT_100TX_FD		(5 << 8)	/* 100BASE-TX Full-Duplex */
+#define PHY_SUM_STAT_100T4		(4 << 8)	/* 100BASE-T4 */
+#define PHY_SUM_STAT_100TX_HD		(3 << 8)	/* 100BASE-TX Half-Duplex */
+#define PHY_SUM_STAT_10T_FD		(2 << 8)	/* 10BASE-T Full-Duplex */
+#define PHY_SUM_STAT_10T_HD		(1 << 8)	/* 10BASE-T Half-Duplex */
+#define PHY_SUM_STAT_AN_FAIL		(0 << 8)	/* 1000BASE-T Half-Duplex */
+#else
+#error "PHY Device not specified"
+#endif				/* MV88_PHY */
+
+#define TSI108_TX_EOF	(1 << 0)	/* End of frame; last fragment of packet */
+#define TSI108_TX_SOF	(1 << 1)	/* Start of frame; first frag. of packet */
+#define TSI108_TX_VLAN	(1 << 2)	/* Per-frame VLAN: enables VLAN override */
+#define TSI108_TX_HUGE	(1 << 3)	/* Huge frame enable */
+#define TSI108_TX_PAD	(1 << 4)	/* Pad the packet if too short */
+#define TSI108_TX_CRC	(1 << 5)	/* Generate CRC for this packet */
+#define TSI108_TX_INT	(1 << 14)	/* Generate an IRQ after frag. processed */
+#define TSI108_TX_RETRY	16	/* 4 bit field indicating num. of retries */
+#define TSI108_TX_COL	(1 << 20)	/* Set if a collision occured */
+#define TSI108_TX_LCOL	(1 << 24)	/* Set if a late collision occured */
+#define TSI108_TX_UNDER	(1 << 25)	/* Set if a FIFO underrun occured */
+#define TSI108_TX_RLIM	(1 << 26)	/* Set if the retry limit was reached */
+#define TSI108_TX_OK	(1 << 30)	/* Set if the frame TX was successful */
+#define TSI108_TX_OWN	(1 << 31)	/* Set if the device owns the descriptor */
+
+/* Note: the descriptor layouts assume big-endian byte order. */
+typedef struct {
+	u32 buf0;
+	u32 buf1;		/* Base address of buffer */
+	u32 next0;		/* Address of next descriptor, if any */
+	u32 next1;
+	u16 vlan;		/* VLAN, if override enabled for this packet */
+	u16 len;		/* Length of buffer in bytes */
+	u32 misc;		/* See TSI108_TX_* above */
+	u32 reserved0;		/*reserved0 and reserved1 are added to make the desc */
+	u32 reserved1;		/* 32-byte aligned */
+} __attribute__ ((aligned(32))) tx_desc;
+
+#define TSI108_RX_EOF	(1 << 0)	/* End of frame; last fragment of packet */
+#define TSI108_RX_SOF	(1 << 1)	/* Start of frame; first frag. of packet */
+#define TSI108_RX_VLAN	(1 << 2)	/* Set on SOF if packet has a VLAN */
+#define TSI108_RX_FTYPE	(1 << 3)	/* Length/Type field is type, not length */
+#define TSI108_RX_RUNT	(1 << 4)/* Packet is less than minimum size */
+#define TSI108_RX_HASH	(1 << 7)/* Hash table match */
+#define TSI108_RX_BAD	(1 << 8)	/* Bad frame */
+#define TSI108_RX_OVER	(1 << 9)	/* FIFO overrun occured */
+#define TSI108_RX_TRUNC	(1 << 11)	/* Packet truncated due to excess length */
+#define TSI108_RX_CRC	(1 << 12)	/* Packet had a CRC error */
+#define TSI108_RX_INT	(1 << 13)	/* Generate an IRQ after frag. processed */
+#define TSI108_RX_OWN	(1 << 15)	/* Set if the device owns the descriptor */
+
+typedef struct {
+	u32 buf0;		/* Base address of buffer */
+	u32 buf1;		/* Base address of buffer */
+	u32 next0;		/* Address of next descriptor, if any */
+	u32 next1;		/* Address of next descriptor, if any */
+	u16 vlan;		/* VLAN of received packet, first frag only */
+	u16 len;		/* Length of received fragment in bytes */
+	u16 blen;		/* Length of buffer in bytes */
+	u16 misc;		/* See TSI108_RX_* above */
+	u32 reserved0;		/* reserved0 and reserved1 are added to make the desc */
+	u32 reserved1;		/* 32-byte aligned */
+} __attribute__ ((aligned(32))) rx_desc;
+
+#endif				/* __TSI108_ETH_H */

^ permalink raw reply related

* RE: [PATCH/2.6.17-rc4 8/10]  Add  tsi108 8250 serial support
From: Zang Roy-r61911 @ 2006-06-13  5:39 UTC (permalink / raw)
  To: Russell King
  Cc: Alexandre.Bounine, linux-kernel, linuxppc-dev list,
	Paul Mackerras, linux-serial, Yang Xin-Xin-r48390

> > 
> > On May 17, 2006, at 5:14 AM, Zang Roy-r61911 wrote:
> > 
> > > This patch contains changes to the serial device driver 
> specific for 
> > > integrated serial port in Tsi108 Host Bridge.
> 
> There's no explaination about why this is required.  What is 
> the problem?
> Which changes relate directly to this problem and which 
> changes are related to fixing some other issue not related to 
> the errata?
> 
> Plus, every patch line is prefixed by "> "... patch doesn't like that.

The reason is that the serial port on tsi108/9 is a bit difference with the 
standard 8250 serial port. the patch deal with the difference.
The prefixed ">" is caused by "Fw" the email. Sorry for that. The following 
is the original patch.

Signed-off-by: Alexandre Bounine <alexandreb@tundra.com>
Signed-off-by: Roy Zang	<tie-fei.zang@freescale.com>

 drivers/serial/8250.c |   17 +++++++++++++++++
 1 files changed, 17 insertions(+), 0 deletions(-)

6cb950357e9970afa671d59f172dbc4b03f11560
diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index bbf78aa..c12f516 100644
--- a/drivers/serial/8250.c
+++ b/drivers/serial/8250.c
@@ -723,7 +723,9 @@ static int broken_efr(struct uart_8250_p  static void autoconfig_16550a(struct uart_8250_port *up)  {
 	unsigned char status1, status2;
+#ifndef CONFIG_TSI108_BRIDGE
 	unsigned int iersave;
+#endif
 
 	up->port.type = PORT_16550A;
 	up->capabilities |= UART_CAP_FIFO;
@@ -833,6 +835,7 @@ static void autoconfig_16550a(struct uar
 	 * trying to write and read a 1 just to make sure it's not
 	 * already a 1 and maybe locked there before we even start start.
 	 */
+#ifndef CONFIG_TSI108_BRIDGE
 	iersave = serial_in(up, UART_IER);
 	serial_outp(up, UART_IER, iersave & ~UART_IER_UUE);
 	if (!(serial_in(up, UART_IER) & UART_IER_UUE)) { @@ -859,6 +862,7 @@ static void autoconfig_16550a(struct uar
 		DEBUG_AUTOCONF("Couldn't force IER_UUE to 0 ");
 	}
 	serial_outp(up, UART_IER, iersave);
+#endif
 }
 
 /*
@@ -1348,7 +1352,12 @@ static irqreturn_t serial8250_interrupt(
 
 		up = list_entry(l, struct uart_8250_port, list);
 
+#ifdef CONFIG_TSI108_BRIDGE /* for TSI108_REV_Z1 errata U2 */
+		/* read IIR as part of 32-bit word */
+		iir = (in_be32((u32 *)(up->port.membase + UART_RX)) >> 8) & 0xff; 
+#else
 		iir = serial_in(up, UART_IIR);
+#endif
 		if (!(iir & UART_IIR_NO_INT)) {
 			serial8250_handle_port(up, regs);
 
@@ -1529,7 +1538,9 @@ static int serial8250_startup(struct uar  {
 	struct uart_8250_port *up = (struct uart_8250_port *)port;
 	unsigned long flags;
+#ifndef CONFIG_TSI108_BRIDGE
 	unsigned char lsr, iir;
+#endif
 	int retval;
 
 	up->capabilities = uart_config[up->port.type].flags; @@ -1567,7 +1578,9 @@ #endif
 	 */
 	(void) serial_inp(up, UART_LSR);
 	(void) serial_inp(up, UART_RX);
+#ifndef CONFIG_TSI108_BRIDGE /* for TSI108_REV_Z1 errata U2 */
 	(void) serial_inp(up, UART_IIR);
+#endif
 	(void) serial_inp(up, UART_MSR);
 
 	/*
@@ -1634,6 +1647,7 @@ #endif
 
 	serial8250_set_mctrl(&up->port, up->port.mctrl);
 
+#ifndef CONFIG_TSI108_BRIDGE
 	/*
 	 * Do a quick test to see if we receive an
 	 * interrupt when we enable the TX irq.
@@ -1652,6 +1666,7 @@ #endif
 	} else {
 		up->bugs &= ~UART_BUG_TXEN;
 	}
+#endif
 
 	spin_unlock_irqrestore(&up->port.lock, flags);
 
@@ -1678,7 +1693,9 @@ #endif
 	 */
 	(void) serial_inp(up, UART_LSR);
 	(void) serial_inp(up, UART_RX);
+#ifndef CONFIG_TSI108_BRIDGE /* for TSI108_REV_Z1 errata U2 */
 	(void) serial_inp(up, UART_IIR);
+#endif
 	(void) serial_inp(up, UART_MSR);
 
 	return 0;
--
1.3.0

^ permalink raw reply

* mv64x60 DMA controller
From: Phil Nitschke @ 2006-06-13  5:03 UTC (permalink / raw)
  To: linuxppc-embedded

Hi guys,

I've written a DMA controller driver for the Marvell Discovery series
(particularly the MV64360, MV64460).  It is still a work-in-progress and
comes with many caveats, but I'm happy to share it should anyone be
interested.  Some of the caveats include:
  - does not support scatter-gather operations yet
  - quite limited testing has been performed to date

The 50 kb, 5000 line patch is usually applied to a 2.6.16 kernel which
has already been patched for use with Artesyn boards, particularly the
PmPPC7448, and affects the following files:

        linux-2.6.16/arch/ppc/Kconfig
        linux-2.6.16/arch/ppc/configs/pmppc744x_defconfig
        linux-2.6.16/arch/ppc/platforms/pmppc7447.c
        linux-2.6.16/arch/ppc/syslib/Makefile
        linux-2.6.16/arch/ppc/syslib/mv64x60.c
        linux-2.6.16/arch/ppc/syslib/mv64x6x_dma.c
        linux-2.6.16/include/asm-ppc/mv64x60.h
        linux-2.6.16/include/asm-ppc/mv64x60_defs.h
        linux-2.6.16/include/asm-ppc/mv64x6x_dma.h
        linux-2.6.16/include/linux/mv643xx.h
        
Please let me know if there is anyone would like me to post this patch,
and in what form I should do this.

-- 
Phil

^ permalink raw reply

* [PATCH] powerpc-genirq: port cell iic and spider-pic to genirq
From: Benjamin Herrenschmidt @ 2006-06-13  4:07 UTC (permalink / raw)
  To: linuxppc-dev list; +Cc: Ingo Molnar, Thomas Gleixner

This patch is a quick hack at porting the cell iic and spider-pic to
genirq. It applies on top of other cell patches not yet in mainline. It
needs more work though, for example, we need to handle proper retrigger
of interrupts that have been masked etc...

It also hacks together some cascade handling that need to be reworked,
mostly when my irq number allocator/remapper is ready (I currently
hijack spider irq 32 which doesn't exist to do the iic->spider cascade).
It needs some work to properly handle the RAS interrupts on cell as
well. One feature it adds that wasn't there is better handling of
stacked priorities (that should be done for xics as well btw). That is,
when an irq is completed (eoi), the processor priority is restored to
the previous priority that got interrupted rather than 0xff
systematically, thus avoiding the potential for infinite stacking of
interrupts.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---

Patch depends on Ingo/Thomas genirq patchset. It's not to be merged
right away, It's being posted here for comments as I may have further
rework.

Index: linux-work/arch/powerpc/platforms/cell/interrupt.c
===================================================================
--- linux-work.orig/arch/powerpc/platforms/cell/interrupt.c	2006-06-08 15:10:24.000000000 +1000
+++ linux-work/arch/powerpc/platforms/cell/interrupt.c	2006-06-09 18:08:56.000000000 +1000
@@ -57,64 +57,51 @@ struct iic_regs {
 struct iic {
 	struct iic_regs __iomem *regs;
 	u8 target_id;
+	u8 eoi_stack[16];
+	int eoi_ptr;
 };
 
 static DEFINE_PER_CPU(struct iic, iic);
 
-void iic_local_enable(void)
+static void iic_mask(unsigned int irq)
 {
-	struct iic *iic = &__get_cpu_var(iic);
-	u64 tmp;
-
-	/*
-	 * There seems to be a bug that is present in DD2.x CPUs
-	 * and still only partially fixed in DD3.1.
-	 * This bug causes a value written to the priority register
-	 * not to make it there, resulting in a system hang unless we
-	 * write it again.
-	 * Masking with 0xf0 is done because the Cell BE does not
-	 * implement the lower four bits of the interrupt priority,
-	 * they always read back as zeroes, although future CPUs
-	 * might implement different bits.
-	 */
-	do {
-		out_be64(&iic->regs->prio, 0xff);
-		tmp = in_be64(&iic->regs->prio);
-	} while ((tmp & 0xf0) != 0xf0);
-}
-
-void iic_local_disable(void)
-{
-	out_be64(&__get_cpu_var(iic).regs->prio, 0x0);
 }
 
-static unsigned int iic_startup(unsigned int irq)
+static void iic_unmask(unsigned int irq)
 {
-	return 0;
 }
 
-static void iic_enable(unsigned int irq)
+static void iic_eoi(unsigned int irq)
 {
-	iic_local_enable();
-}
-
-static void iic_disable(unsigned int irq)
-{
-}
-
-static void iic_end(unsigned int irq)
-{
-	iic_local_enable();
+	struct iic *iic = &__get_cpu_var(iic);
+	out_be64(&iic->regs->prio, iic->eoi_stack[--iic->eoi_ptr]);
+	BUG_ON(iic->eoi_ptr < 0);
 }
 
-static struct hw_interrupt_type iic_pic = {
+static struct irq_chip iic_chip = {
 	.typename = " CELL-IIC ",
-	.startup = iic_startup,
-	.enable = iic_enable,
-	.disable = iic_disable,
-	.end = iic_end,
+	.mask = iic_mask,
+	.unmask = iic_unmask,
+	.eoi = iic_eoi,
 };
 
+/* XXX All of this has to be reworked completely. We need to assign a real
+ * interrupt numbers to the external interrupts and remove all the hard coded
+ * interrupt maps (rely on the device-tree whenever possible).
+ *
+ * Basically, my scheme is to define the "pendings" bits to be the HW interrupt
+ * number (ignoring the data and flags here). That means we can sort-of split
+ * external sources based on priority, and we can use request_irq() on pretty
+ * much anything.
+ *
+ * For spider or axon, they have their own interrupt space. spider will just have
+ * local "hardward" interrupts 0...xx * node stride. The node stride is not
+ * necessary (separate interrupt chips will have separate HW number space), but
+ * will allow to be compatible with existing device-trees.
+ *
+ * All of thise little world will get a standard remapping scheme to map those HW
+ * numbers into the linux flat irq number space.
+ */
 static int iic_external_get_irq(struct iic_pending_bits pending)
 {
 	int irq;
@@ -138,9 +125,10 @@ static int iic_external_get_irq(struct i
 		 */
 		if (pending.class != 2)
 			break;
-		irq = IIC_EXT_OFFSET
-			+ spider_get_irq(node)
-			+ node * IIC_NODE_STRIDE;
+		/* TODO: We might want to silently ignore cascade interrupts
+		 * when no cascade handler exist yet
+		 */
+		irq = IIC_EXT_CASCADE + node * IIC_NODE_STRIDE;
 		break;
 	case 0x01 ... 0x04:
 	case 0x07 ... 0x0a:
@@ -172,6 +160,8 @@ int iic_get_irq(struct pt_regs *regs)
 	iic = &__get_cpu_var(iic);
 	*(unsigned long *) &pending = 
 		in_be64((unsigned long __iomem *) &iic->regs->pending_destr);
+	iic->eoi_stack[++iic->eoi_ptr] = pending.prio;
+	BUG_ON(iic->eoi_ptr > 15);
 
 	irq = -1;
 	if (pending.flags & IIC_VALID) {
@@ -192,7 +182,7 @@ int iic_get_irq(struct pt_regs *regs)
 
 /* hardcoded part to be compatible with older firmware */
 
-static int setup_iic_hardcoded(void)
+static int __init setup_iic_hardcoded(void)
 {
 	struct device_node *np;
 	int nodeid, cpu;
@@ -232,7 +222,7 @@ static int setup_iic_hardcoded(void)
 	return 0;
 }
 
-static int setup_iic(void)
+static int __init setup_iic(void)
 {
 	struct device_node *dn;
 	unsigned long *regs;
@@ -268,11 +258,13 @@ static int setup_iic(void)
  		iic = &per_cpu(iic, np[0]);
  		iic->regs = ioremap(regs[0], sizeof(struct iic_regs));
 		iic->target_id = ((np[0] & 2) << 3) + ((np[0] & 1) ? 0xf : 0xe);
+		iic->eoi_stack[0] = 0xff;
  		printk("IIC for CPU %d at %lx mapped to %p\n", np[0], regs[0], iic->regs);
 
  		iic = &per_cpu(iic, np[1]);
  		iic->regs = ioremap(regs[2], sizeof(struct iic_regs));
 		iic->target_id = ((np[1] & 2) << 3) + ((np[1] & 1) ? 0xf : 0xe);
+		iic->eoi_stack[0] = 0xff;
  		printk("IIC for CPU %d at %lx mapped to %p\n", np[1], regs[2], iic->regs);
 
 		found++;
@@ -324,10 +316,10 @@ static void iic_request_ipi(int ipi, con
 	int irq;
 
 	irq = iic_ipi_to_irq(ipi);
+
 	/* IPIs are marked SA_INTERRUPT as they must run with irqs
 	 * disabled */
-	get_irq_desc(irq)->chip = &iic_pic;
-	get_irq_desc(irq)->status |= IRQ_PER_CPU;
+	set_irq_chip_and_handler(irq, &iic_chip, handle_percpu_irq);
 	request_irq(irq, iic_ipi_action, SA_INTERRUPT, name, NULL);
 }
 
@@ -341,20 +333,26 @@ void iic_request_IPIs(void)
 }
 #endif /* CONFIG_SMP */
 
-static void iic_setup_spe_handlers(void)
+static void __init iic_setup_builtin_handlers(void)
 {
 	int be, isrc;
 
-	/* Assume two threads per BE are present */
+	/* XXX FIXME: Assume two threads per BE are present */
 	for (be=0; be < num_present_cpus() / 2; be++) {
+		int irq;
+
+		/* setup SPE chip and handlers */
 		for (isrc = 0; isrc < IIC_CLASS_STRIDE * 3; isrc++) {
-			int irq = IIC_NODE_STRIDE * be + IIC_SPE_OFFSET + isrc;
-			get_irq_desc(irq)->chip = &iic_pic;
+			irq = IIC_NODE_STRIDE * be + IIC_SPE_OFFSET + isrc;
+			set_irq_chip_and_handler(irq, &iic_chip, handle_fasteoi_irq);
 		}
+		/* setup cascade chip */
+		irq = IIC_EXT_CASCADE + be * IIC_NODE_STRIDE;
+		set_irq_chip_and_handler(irq, &iic_chip, handle_fasteoi_irq);
 	}
 }
 
-void iic_init_IRQ(void)
+void __init iic_init_IRQ(void)
 {
 	int cpu, irq_offset;
 	struct iic *iic;
@@ -368,5 +366,6 @@ void iic_init_IRQ(void)
 		if (iic->regs)
 			out_be64(&iic->regs->prio, 0xff);
 	}
-	iic_setup_spe_handlers();
+	iic_setup_builtin_handlers();
+
 }
Index: linux-work/arch/powerpc/Kconfig.debug
===================================================================
--- linux-work.orig/arch/powerpc/Kconfig.debug	2006-03-31 12:13:01.000000000 +1100
+++ linux-work/arch/powerpc/Kconfig.debug	2006-06-09 17:50:53.000000000 +1000
@@ -131,12 +131,30 @@ config PPC_EARLY_DEBUG_G5
 	help
 	  Select this to enable early debugging for Apple G5 machines.
 
-config PPC_EARLY_DEBUG_RTAS
+config PPC_EARLY_DEBUG_RTAS_PANEL
 	bool "RTAS Panel"
 	depends on PPC_RTAS
 	help
 	  Select this to enable early debugging via the RTAS panel.
 
+config PPC_EARLY_DEBUG_RTAS_CONSOLE
+	bool "RTAS Console"
+	depends on PPC_RTAS
+	help
+	  Select this to enable early debugging via the RTAS console.
+
+config PPC_EARLY_DEBUG_RTAS_PUTCHAR_TOKEN
+	hex "  RTAS token for put-term-char function" if PPC_EARLY_DEBUG_RTAS_CONSOLE
+	default "0x27"
+	help
+	  Enter the value for the put-term-char rtas token on your machine
+
+config PPC_EARLY_DEBUG_RTAS_GETCHAR_TOKEN
+	hex "  RTAS token for get-term-char function" if PPC_EARLY_DEBUG_RTAS_CONSOLE
+	default "0x28"
+	help
+	  Enter the value for the get-term-char rtas token on your machine
+
 config PPC_EARLY_DEBUG_MAPLE
 	bool "Maple real mode"
 	depends on PPC_MAPLE
Index: linux-work/arch/powerpc/kernel/rtas.c
===================================================================
--- linux-work.orig/arch/powerpc/kernel/rtas.c	2006-04-21 14:16:03.000000000 +1000
+++ linux-work/arch/powerpc/kernel/rtas.c	2006-06-09 18:00:28.000000000 +1000
@@ -106,11 +106,68 @@ static void call_rtas_display_status_del
 	}
 }
 
-void __init udbg_init_rtas(void)
+void __init udbg_init_rtas_panel(void)
 {
 	udbg_putc = call_rtas_display_status_delay;
 }
 
+#ifdef CONFIG_PPC_EARLY_DEBUG_RTAS_CONSOLE
+
+static unsigned int rtas_putchar_token;
+static unsigned int rtas_getchar_token;
+
+static void udbg_rtascon_putc(char c)
+{
+	int tries, result;
+
+	if (!rtas.base)
+		return;
+
+	/* Add CRs before LFs */
+	if (c == 10)
+		udbg_rtascon_putc(13);
+
+	/* if there is more than one character to be displayed, wait a bit */
+	for (tries = 0; tries < 16; tries++) {
+		result = rtas_call(rtas_putchar_token, 1, 1, NULL, c);
+		if (result == 0)
+			break;
+		udelay(1000);
+	}
+}
+
+static int udbg_rtascon_getc_poll(void)
+{
+	int c;
+
+	if (!rtas.base)
+		return -1;
+
+	if (rtas_call(rtas_getchar_token, 0, 2, &c))
+		return -1;
+	return c;
+}
+
+static int udbg_rtascon_getc(void)
+{
+	int c;
+	while ((c = udbg_rtascon_getc_poll()) == -1)
+		;
+	return c;
+}
+
+
+void __init udbg_init_rtas_console(unsigned int put_token,
+				   unsigned int get_token)
+{
+	rtas_putchar_token = put_token;
+	rtas_getchar_token = get_token;
+	udbg_putc = udbg_rtascon_putc;
+	udbg_getc = udbg_rtascon_getc;
+	udbg_getc_poll = udbg_rtascon_getc_poll;
+}
+#endif /* CONFIG_PPC_EARLY_DEBUG_RTAS_CONSOLE */
+
 void rtas_progress(char *s, unsigned short hex)
 {
 	struct device_node *root;
Index: linux-work/arch/powerpc/kernel/udbg.c
===================================================================
--- linux-work.orig/arch/powerpc/kernel/udbg.c	2006-06-06 13:38:03.000000000 +1000
+++ linux-work/arch/powerpc/kernel/udbg.c	2006-06-09 17:49:54.000000000 +1000
@@ -34,9 +34,13 @@ void __init udbg_early_init(void)
 #elif defined(CONFIG_PPC_EARLY_DEBUG_G5)
 	/* For use on Apple G5 machines */
 	udbg_init_pmac_realmode();
-#elif defined(CONFIG_PPC_EARLY_DEBUG_RTAS)
+#elif defined(CONFIG_PPC_EARLY_DEBUG_RTAS_PANEL)
 	/* RTAS panel debug */
-	udbg_init_rtas();
+	udbg_init_rtas_panel();
+#elif defined(CONFIG_PPC_EARLY_DEBUG_RTAS_CONSOLE)
+	/* RTAS console debug */
+	udbg_init_rtas_console(CONFIG_PPC_EARLY_DEBUG_RTAS_PUTCHAR_TOKEN,
+			       CONFIG_PPC_EARLY_DEBUG_RTAS_GETCHAR_TOKEN);
 #elif defined(CONFIG_PPC_EARLY_DEBUG_MAPLE)
 	/* Maple real mode debug */
 	udbg_init_maple_realmode();
Index: linux-work/arch/powerpc/platforms/cell/interrupt.h
===================================================================
--- linux-work.orig/arch/powerpc/platforms/cell/interrupt.h	2006-03-31 12:13:01.000000000 +1100
+++ linux-work/arch/powerpc/platforms/cell/interrupt.h	2006-06-09 15:51:29.000000000 +1000
@@ -38,6 +38,7 @@
 
 enum {
 	IIC_EXT_OFFSET   = 0x00, /* Start of south bridge IRQs */
+	IIC_EXT_CASCADE  = 0x20, /* There is no interrupt 32 on spider */
 	IIC_NUM_EXT      = 0x40, /* Number of south bridge IRQs */
 	IIC_SPE_OFFSET   = 0x40, /* Start of SPE interrupts */
 	IIC_CLASS_STRIDE = 0x10, /* SPE IRQs per class    */
@@ -51,13 +52,10 @@ extern int  iic_get_irq(struct pt_regs *
 extern void iic_cause_IPI(int cpu, int mesg);
 extern void iic_request_IPIs(void);
 extern void iic_setup_cpu(void);
-extern void iic_local_enable(void);
-extern void iic_local_disable(void);
 
 extern u8 iic_get_target_id(int cpu);
 
 extern void spider_init_IRQ(void);
-extern int spider_get_irq(int node);
 
 #endif
 #endif /* ASM_CELL_PIC_H */
Index: linux-work/arch/powerpc/platforms/cell/setup.c
===================================================================
--- linux-work.orig/arch/powerpc/platforms/cell/setup.c	2006-06-06 13:38:03.000000000 +1000
+++ linux-work/arch/powerpc/platforms/cell/setup.c	2006-06-09 18:07:15.000000000 +1000
@@ -77,11 +77,14 @@ static void cell_progress(char *s, unsig
 	printk("*** %04x : %s\n", hex, s ? s : "");
 }
 
-static void __init cell_setup_arch(void)
+static void __init cell_init_irq(void)
 {
-	ppc_md.init_IRQ       = iic_init_IRQ;
-	ppc_md.get_irq        = iic_get_irq;
+	iic_init_IRQ();
+	spider_init_IRQ();
+}
 
+static void __init cell_setup_arch(void)
+{
 #ifdef CONFIG_SMP
 	smp_init_cell();
 #endif
@@ -97,7 +100,6 @@ static void __init cell_setup_arch(void)
 	/* Find and initialize PCI host bridges */
 	init_pci_config_tokens();
 	find_and_init_phbs();
-	spider_init_IRQ();
 	cell_pervasive_init();
 #ifdef CONFIG_DUMMY_CONSOLE
 	conswitchp = &dummy_con;
@@ -158,6 +160,9 @@ define_machine(cell) {
 	.calibrate_decr		= generic_calibrate_decr,
 	.check_legacy_ioport	= cell_check_legacy_ioport,
 	.progress		= cell_progress,
+	.init_IRQ       	= cell_init_irq,
+	.get_irq        	= iic_get_irq,
+
 #ifdef CONFIG_KEXEC
 	.machine_kexec		= default_machine_kexec,
 	.machine_kexec_prepare	= default_machine_kexec_prepare,
Index: linux-work/arch/powerpc/platforms/cell/spider-pic.c
===================================================================
--- linux-work.orig/arch/powerpc/platforms/cell/spider-pic.c	2006-06-05 17:55:22.000000000 +1000
+++ linux-work/arch/powerpc/platforms/cell/spider-pic.c	2006-06-09 17:24:45.000000000 +1000
@@ -82,17 +82,20 @@ static void __iomem *spider_get_irq_conf
 	return pic + TIR_CFGA + 8 * spider_get_nr(irq);
 }
 
-static void spider_enable_irq(unsigned int irq)
+static void spider_unmask_irq(unsigned int irq)
 {
 	int nodeid = (irq / IIC_NODE_STRIDE) * 0x10;
 	void __iomem *cfg = spider_get_irq_config(irq);
 	irq = spider_get_nr(irq);
 
+	/* FIXME: Most of that is configuration and has nothing to do with enabling/disable,
+	 * besides, it's also partially bogus.
+	 */
 	out_be32(cfg, (in_be32(cfg) & ~0xf0)| 0x3107000eu | nodeid);
 	out_be32(cfg + 4, in_be32(cfg + 4) | 0x00020000u | irq);
 }
 
-static void spider_disable_irq(unsigned int irq)
+static void spider_mask_irq(unsigned int irq)
 {
 	void __iomem *cfg = spider_get_irq_config(irq);
 	irq = spider_get_nr(irq);
@@ -100,39 +103,21 @@ static void spider_disable_irq(unsigned 
 	out_be32(cfg, in_be32(cfg) & ~0x30000000u);
 }
 
-static unsigned int spider_startup_irq(unsigned int irq)
-{
-	spider_enable_irq(irq);
-	return 0;
-}
-
-static void spider_shutdown_irq(unsigned int irq)
-{
-	spider_disable_irq(irq);
-}
-
-static void spider_end_irq(unsigned int irq)
-{
-	spider_enable_irq(irq);
-}
-
 static void spider_ack_irq(unsigned int irq)
 {
-	spider_disable_irq(irq);
-	iic_local_enable();
+	/* Should reset edge detection logic but we don't configure any edge interrupt
+	 * at the moment.
+	 */
 }
 
-static struct hw_interrupt_type spider_pic = {
+static struct irq_chip spider_pic = {
 	.typename = " SPIDER   ",
-	.startup = spider_startup_irq,
-	.shutdown = spider_shutdown_irq,
-	.enable = spider_enable_irq,
-	.disable = spider_disable_irq,
+	.unmask = spider_unmask_irq,
+	.mask = spider_mask_irq,
 	.ack = spider_ack_irq,
-	.end = spider_end_irq,
 };
 
-int spider_get_irq(int node)
+static int spider_get_irq(int node)
 {
 	unsigned long cs;
 	void __iomem *regs = spider_pics[node];
@@ -145,95 +130,89 @@ int spider_get_irq(int node)
 		return cs;
 }
 
+static void spider_irq_cascade(unsigned int irq, struct irq_desc *desc,
+			       struct pt_regs *regs)
+{
+	int node = (int)(long)desc->handler_data;
+	int cascade_irq;
+
+	cascade_irq = spider_get_irq(node);
+	generic_handle_irq(cascade_irq, regs);
+	desc->chip->eoi(irq);
+}
+
 /* hardcoded part to be compatible with older firmware */
 
-void spider_init_IRQ_hardcoded(void)
+static void __init spider_init_one(int node, unsigned long addr)
 {
-	int node;
-	long spiderpic;
-	long pics[] = { 0x24000008000, 0x34000008000 };
-	int n;
-
-	pr_debug("%s(%d): Using hardcoded defaults\n", __FUNCTION__, __LINE__);
-
-	for (node = 0; node < num_present_cpus()/2; node++) {
-		spiderpic = pics[node];
-		printk(KERN_DEBUG "SPIDER addr: %lx\n", spiderpic);
-		spider_pics[node] = ioremap(spiderpic, 0x800);
-		for (n = 0; n < IIC_NUM_EXT; n++) {
-			int irq = n + IIC_EXT_OFFSET + node * IIC_NODE_STRIDE;
-			get_irq_desc(irq)->chip = &spider_pic;
-		}
-
- 		/* do not mask any interrupts because of level */
- 		out_be32(spider_pics[node] + TIR_MSK, 0x0);
-
- 		/* disable edge detection clear */
- 		/* out_be32(spider_pics[node] + TIR_EDC, 0x0); */
-
- 		/* enable interrupt packets to be output */
- 		out_be32(spider_pics[node] + TIR_PIEN,
-			in_be32(spider_pics[node] + TIR_PIEN) | 0x1);
-
- 		/* Enable the interrupt detection enable bit. Do this last! */
- 		out_be32(spider_pics[node] + TIR_DEN,
-			in_be32(spider_pics[node] + TIR_DEN) | 0x1);
+	int n, irq;
+
+	spider_pics[node] = ioremap(addr, 0x800);
+	if (spider_pics[node] == NULL)
+		panic("spider_pic: can't map registers !");
+
+	printk(KERN_INFO "spider_pic: mapped for node %d, addr: 0x%lx mapped to %p\n",
+	       node, addr, spider_pics[node]);
+
+	for (n = 0; n < IIC_NUM_EXT; n++) {
+		if (n == IIC_EXT_CASCADE)
+			continue;
+		irq = n + IIC_EXT_OFFSET + node * IIC_NODE_STRIDE;
+		set_irq_chip_and_handler(irq, &spider_pic, handle_level_irq);
+		get_irq_desc(irq)->status |= IRQ_LEVEL;
 	}
+
+	/* do not mask any interrupts because of level */
+	out_be32(spider_pics[node] + TIR_MSK, 0x0);
+
+	/* disable edge detection clear */
+	/* out_be32(spider_pics[node] + TIR_EDC, 0x0); */
+
+	/* enable interrupt packets to be output */
+	out_be32(spider_pics[node] + TIR_PIEN,
+		 in_be32(spider_pics[node] + TIR_PIEN) | 0x1);
+
+	/* Hook up cascade */
+	irq = IIC_EXT_CASCADE + node * IIC_NODE_STRIDE;
+	set_irq_data(irq, (void *)(long)node);
+	set_irq_chained_handler(irq, spider_irq_cascade);
+
+	/* Enable the interrupt detection enable bit. Do this last! */
+	out_be32(spider_pics[node] + TIR_DEN,
+		 in_be32(spider_pics[node] + TIR_DEN) | 0x1);
 }
 
-void spider_init_IRQ(void)
+void __init spider_init_IRQ(void)
 {
-	long spider_reg;
+	unsigned long *spider_reg;
 	struct device_node *dn;
 	char *compatible;
-	int n, node = 0;
+	int node = 0;
 
+	/* XXX node numbers are totally bogus. We _hope_ we get the device nodes in the right
+	 * order here but that's definitely not guaranteed, we need to get the node from the
+	 * device tree instead. There is currently no proper property for it (but our whole
+	 * device-tree is bogus anyway) so all we can do is pray or maybe test the address
+	 * and deduce the node-id
+	 */
 	for (dn = NULL; (dn = of_find_node_by_name(dn, "interrupt-controller"));) {
 		compatible = (char *)get_property(dn, "compatible", NULL);
 
 		if (!compatible)
 			continue;
 
-		if (strstr(compatible, "CBEA,platform-spider-pic"))
-			spider_reg = *(long *)get_property(dn,"reg", NULL);
-		else if (strstr(compatible, "sti,platform-spider-pic")) {
-			spider_init_IRQ_hardcoded();
-			return;
+ 		if (strstr(compatible, "CBEA,platform-spider-pic"))
+			spider_reg = (unsigned long *)get_property(dn, "reg", NULL);
+		else if (strstr(compatible, "sti,platform-spider-pic") && (node < 2)) {
+			static long hard_coded_pics[] = { 0x24000008000, 0x34000008000 };
+			spider_reg = &hard_coded_pics[node];
 		} else
 			continue;
 
-		if (!spider_reg)
-			printk("interrupt controller does not have reg property !\n");
-
-		n = prom_n_addr_cells(dn);
-
-		if ( n != 2)
-			printk("reg property with invalid number of elements \n");
-
-		spider_pics[node] = ioremap(spider_reg, 0x800);
-
-		printk("SPIDER addr: %lx with %i addr_cells mapped to %p\n",
-		       spider_reg, n, spider_pics[node]);
-
-		for (n = 0; n < IIC_NUM_EXT; n++) {
-			int irq = n + IIC_EXT_OFFSET + node * IIC_NODE_STRIDE;
-			get_irq_desc(irq)->chip = &spider_pic;
-		}
-
-		/* do not mask any interrupts because of level */
-		out_be32(spider_pics[node] + TIR_MSK, 0x0);
-
-		/* disable edge detection clear */
-		/* out_be32(spider_pics[node] + TIR_EDC, 0x0); */
-
-		/* enable interrupt packets to be output */
-		out_be32(spider_pics[node] + TIR_PIEN,
-			in_be32(spider_pics[node] + TIR_PIEN) | 0x1);
-
-		/* Enable the interrupt detection enable bit. Do this last! */
-		out_be32(spider_pics[node] + TIR_DEN,
-			in_be32(spider_pics[node] + TIR_DEN) | 0x1);
+		if (spider_reg == NULL)
+			printk(KERN_ERR "spider_pic: No address for node %d\n", node);
 
+		spider_init_one(node, *spider_reg);
 		node++;
 	}
 }
Index: linux-work/include/asm-powerpc/udbg.h
===================================================================
--- linux-work.orig/include/asm-powerpc/udbg.h	2006-01-14 14:43:33.000000000 +1100
+++ linux-work/include/asm-powerpc/udbg.h	2006-06-09 17:50:10.000000000 +1000
@@ -41,7 +41,9 @@ extern void __init udbg_init_debug_lpar(
 extern void __init udbg_init_pmac_realmode(void);
 extern void __init udbg_init_maple_realmode(void);
 extern void __init udbg_init_iseries(void);
-extern void __init udbg_init_rtas(void);
+extern void __init udbg_init_rtas_panel(void);
+extern void __init udbg_init_rtas_console(unsigned int put_token,
+					  unsigned int get_token);
 
 #endif /* __KERNEL__ */
 #endif /* _ASM_POWERPC_UDBG_H */

^ permalink raw reply

* [PATCH] powerpc-genirq: port iseries pseudo-PIC to genirq
From: Benjamin Herrenschmidt @ 2006-06-13  3:58 UTC (permalink / raw)
  To: linuxppc-dev list; +Cc: Ingo Molnar, Thomas Gleixner

This patch ports the iSeries pseudo-PIC to genirq
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---

Patch depends on Ingo/Thomas genirq patchset. It's not to be merged
right away, It's being posted here for comments as I may have further
rework.

Index: linux-work/arch/powerpc/platforms/iseries/irq.c
===================================================================
--- linux-work.orig/arch/powerpc/platforms/iseries/irq.c	2006-06-05 17:55:22.000000000 +1000
+++ linux-work/arch/powerpc/platforms/iseries/irq.c	2006-06-07 14:41:55.000000000 +1000
@@ -297,13 +297,13 @@ static void iseries_end_IRQ(unsigned int
 		(REAL_IRQ_TO_IDSEL(rirq) << 4) + REAL_IRQ_TO_FUNC(rirq));
 }
 
-static hw_irq_controller iSeries_IRQ_handler = {
-	.typename = "iSeries irq controller",
-	.startup = iseries_startup_IRQ,
-	.shutdown = iseries_shutdown_IRQ,
-	.enable = iseries_enable_IRQ,
-	.disable = iseries_disable_IRQ,
-	.end = iseries_end_IRQ
+static struct irq_chip iseries_pic = {
+	.typename	= "iSeries irq controller",
+	.startup	= iseries_startup_IRQ,
+	.shutdown	= iseries_shutdown_IRQ,
+	.unmask		= iseries_enable_IRQ,
+	.mask		= iseries_disable_IRQ,
+	.eoi		= iseries_end_IRQ
 };
 
 /*
@@ -322,8 +322,7 @@ int __init iSeries_allocate_IRQ(HvBusNum
 	realirq = (((((sub_bus << 8) + (bus - 1)) << 3) + (idsel - 1)) << 3)
 		+ function;
 	virtirq = virt_irq_create_mapping(realirq);
-
-	irq_desc[virtirq].chip = &iSeries_IRQ_handler;
+	set_irq_chip_and_handler(virtirq, &iseries_pic, handle_fasteoi_irq);
 	return virtirq;
 }
 

^ permalink raw reply

* [PATCH] powerpc-genirq: port legacy powermac PIC to genirq
From: Benjamin Herrenschmidt @ 2006-06-13  3:56 UTC (permalink / raw)
  To: linuxppc-dev list; +Cc: Ingo Molnar, Thomas Gleixner

This patch ports the legacy powermac PIC to genirq.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---

Patch depends on Ingo/Thomas genirq patchset. It's not to be merged
right away, It's being posted here for comments as I may have further
rework.

Index: linux-work/arch/powerpc/kernel/irq.c
===================================================================
--- linux-work.orig/arch/powerpc/kernel/irq.c	2006-06-06 13:38:03.000000000 +1000
+++ linux-work/arch/powerpc/kernel/irq.c	2006-06-07 11:27:07.000000000 +1000
@@ -62,28 +62,27 @@
 #endif
 
 int __irq_offset_value;
-#ifdef CONFIG_PPC32
-EXPORT_SYMBOL(__irq_offset_value);
-#endif
-
 static int ppc_spurious_interrupts;
 
 #ifdef CONFIG_PPC32
-#define NR_MASK_WORDS	((NR_IRQS + 31) / 32)
+EXPORT_SYMBOL(__irq_offset_value);
+atomic_t ppc_n_lost_interrupts;
 
+#ifndef CONFIG_PPC_MERGE
+#define NR_MASK_WORDS	((NR_IRQS + 31) / 32)
 unsigned long ppc_cached_irq_mask[NR_MASK_WORDS];
-atomic_t ppc_n_lost_interrupts;
+#endif
 
 #ifdef CONFIG_TAU_INT
 extern int tau_initialized;
 extern int tau_interrupts(int);
 #endif
+#endif /* CONFIG_PPC32 */
 
 #if defined(CONFIG_SMP) && !defined(CONFIG_PPC_MERGE)
 extern atomic_t ipi_recv;
 extern atomic_t ipi_sent;
 #endif
-#endif /* CONFIG_PPC32 */
 
 #ifdef CONFIG_PPC64
 EXPORT_SYMBOL(irq_desc);
Index: linux-work/arch/powerpc/platforms/powermac/pic.c
===================================================================
--- linux-work.orig/arch/powerpc/platforms/powermac/pic.c	2006-06-06 14:44:55.000000000 +1000
+++ linux-work/arch/powerpc/platforms/powermac/pic.c	2006-06-07 11:53:46.000000000 +1000
@@ -71,18 +71,19 @@ static struct interrupt_info gatwick_int
 
 #define NR_MASK_WORDS	((NR_IRQS + 31) / 32)
 static unsigned long ppc_lost_interrupts[NR_MASK_WORDS];
+static unsigned long ppc_cached_irq_mask[NR_MASK_WORDS];
+static int pmac_irq_cascade = -1;
 
-/*
- * Mark an irq as "lost".  This is only used on the pmac
- * since it can lose interrupts (see pmac_set_irq_mask).
- * -- Cort
- */
-void __set_lost(unsigned long irq_nr, int nokick)
+static void __pmac_retrigger(unsigned int irq_nr)
 {
-	if (!test_and_set_bit(irq_nr, ppc_lost_interrupts)) {
+	if (irq_nr >= max_real_irqs && pmac_irq_cascade > 0) {
+		__set_bit(irq_nr, ppc_lost_interrupts);
+		irq_nr = pmac_irq_cascade;
+		mb();
+	}
+	if (!__test_and_set_bit(irq_nr, ppc_lost_interrupts)) {
 		atomic_inc(&ppc_n_lost_interrupts);
-		if (!nokick)
-			set_dec(1);
+		set_dec(1);
 	}
 }
 
@@ -95,10 +96,10 @@ static void pmac_mask_and_ack_irq(unsign
         if ((unsigned)irq_nr >= max_irqs)
                 return;
 
-        clear_bit(irq_nr, ppc_cached_irq_mask);
-        if (test_and_clear_bit(irq_nr, ppc_lost_interrupts))
-                atomic_dec(&ppc_n_lost_interrupts);
 	spin_lock_irqsave(&pmac_pic_lock, flags);
+        __clear_bit(irq_nr, ppc_cached_irq_mask);
+        if (__test_and_clear_bit(irq_nr, ppc_lost_interrupts))
+                atomic_dec(&ppc_n_lost_interrupts);
         out_le32(&pmac_irq_hw[i]->enable, ppc_cached_irq_mask[i]);
         out_le32(&pmac_irq_hw[i]->ack, bit);
         do {
@@ -110,7 +111,7 @@ static void pmac_mask_and_ack_irq(unsign
 	spin_unlock_irqrestore(&pmac_pic_lock, flags);
 }
 
-static void pmac_set_irq_mask(unsigned int irq_nr, int nokicklost)
+static void pmac_ack_irq(unsigned int irq_nr)
 {
         unsigned long bit = 1UL << (irq_nr & 0x1f);
         int i = irq_nr >> 5;
@@ -119,7 +120,22 @@ static void pmac_set_irq_mask(unsigned i
         if ((unsigned)irq_nr >= max_irqs)
                 return;
 
-	spin_lock_irqsave(&pmac_pic_lock, flags);
+  	spin_lock_irqsave(&pmac_pic_lock, flags);
+	if (__test_and_clear_bit(irq_nr, ppc_lost_interrupts))
+                atomic_dec(&ppc_n_lost_interrupts);
+        out_le32(&pmac_irq_hw[i]->ack, bit);
+        (void)in_le32(&pmac_irq_hw[i]->ack);
+	spin_unlock_irqrestore(&pmac_pic_lock, flags);
+}
+
+static void __pmac_set_irq_mask(unsigned int irq_nr, int nokicklost)
+{
+        unsigned long bit = 1UL << (irq_nr & 0x1f);
+        int i = irq_nr >> 5;
+
+        if ((unsigned)irq_nr >= max_irqs)
+                return;
+
         /* enable unmasked interrupts */
         out_le32(&pmac_irq_hw[i]->enable, ppc_cached_irq_mask[i]);
 
@@ -136,8 +152,7 @@ static void pmac_set_irq_mask(unsigned i
          * the bit in the flag register or request another interrupt.
          */
         if (bit & ppc_cached_irq_mask[i] & in_le32(&pmac_irq_hw[i]->level))
-		__set_lost((ulong)irq_nr, nokicklost);
-	spin_unlock_irqrestore(&pmac_pic_lock, flags);
+		__pmac_retrigger(irq_nr);
 }
 
 /* When an irq gets requested for the first client, if it's an
@@ -145,62 +160,67 @@ static void pmac_set_irq_mask(unsigned i
  */
 static unsigned int pmac_startup_irq(unsigned int irq_nr)
 {
+	unsigned long flags;
         unsigned long bit = 1UL << (irq_nr & 0x1f);
         int i = irq_nr >> 5;
 
+  	spin_lock_irqsave(&pmac_pic_lock, flags);
 	if ((irq_desc[irq_nr].status & IRQ_LEVEL) == 0)
 		out_le32(&pmac_irq_hw[i]->ack, bit);
-        set_bit(irq_nr, ppc_cached_irq_mask);
-        pmac_set_irq_mask(irq_nr, 0);
+        __set_bit(irq_nr, ppc_cached_irq_mask);
+        __pmac_set_irq_mask(irq_nr, 0);
+  	spin_unlock_irqrestore(&pmac_pic_lock, flags);
 
 	return 0;
 }
 
 static void pmac_mask_irq(unsigned int irq_nr)
 {
-        clear_bit(irq_nr, ppc_cached_irq_mask);
-        pmac_set_irq_mask(irq_nr, 0);
-        mb();
+	unsigned long flags;
+
+  	spin_lock_irqsave(&pmac_pic_lock, flags);
+        __clear_bit(irq_nr, ppc_cached_irq_mask);
+        __pmac_set_irq_mask(irq_nr, 0);
+  	spin_unlock_irqrestore(&pmac_pic_lock, flags);
 }
 
 static void pmac_unmask_irq(unsigned int irq_nr)
 {
-        set_bit(irq_nr, ppc_cached_irq_mask);
-        pmac_set_irq_mask(irq_nr, 0);
+	unsigned long flags;
+
+	spin_lock_irqsave(&pmac_pic_lock, flags);
+	__set_bit(irq_nr, ppc_cached_irq_mask);
+        __pmac_set_irq_mask(irq_nr, 0);
+  	spin_unlock_irqrestore(&pmac_pic_lock, flags);
 }
 
-static void pmac_end_irq(unsigned int irq_nr)
+static int pmac_retrigger(unsigned int irq_nr)
 {
-	if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))
-	    && irq_desc[irq_nr].action) {
-        	set_bit(irq_nr, ppc_cached_irq_mask);
-	        pmac_set_irq_mask(irq_nr, 1);
-	}
-}
+	unsigned long flags;
 
+  	spin_lock_irqsave(&pmac_pic_lock, flags);
+	__pmac_retrigger(irq_nr);
+  	spin_unlock_irqrestore(&pmac_pic_lock, flags);
+	return 1;
+}
 
-struct hw_interrupt_type pmac_pic = {
+static struct irq_chip pmac_pic = {
 	.typename	= " PMAC-PIC ",
 	.startup	= pmac_startup_irq,
-	.enable		= pmac_unmask_irq,
-	.disable	= pmac_mask_irq,
-	.ack		= pmac_mask_and_ack_irq,
-	.end		= pmac_end_irq,
-};
-
-struct hw_interrupt_type gatwick_pic = {
-	.typename	= " GATWICK  ",
-	.startup	= pmac_startup_irq,
-	.enable		= pmac_unmask_irq,
-	.disable	= pmac_mask_irq,
-	.ack		= pmac_mask_and_ack_irq,
-	.end		= pmac_end_irq,
+	.mask		= pmac_mask_irq,
+	.ack		= pmac_ack_irq,
+	.mask_ack	= pmac_mask_and_ack_irq,
+	.unmask		= pmac_unmask_irq,
+	.retrigger	= pmac_retrigger,
 };
 
 static irqreturn_t gatwick_action(int cpl, void *dev_id, struct pt_regs *regs)
 {
+	unsigned long flags;
 	int irq, bits;
+	int rc = IRQ_NONE;
 
+  	spin_lock_irqsave(&pmac_pic_lock, flags);
 	for (irq = max_irqs; (irq -= 32) >= max_real_irqs; ) {
 		int i = irq >> 5;
 		bits = in_le32(&pmac_irq_hw[i]->event) | ppc_lost_interrupts[i];
@@ -210,17 +230,20 @@ static irqreturn_t gatwick_action(int cp
 		if (bits == 0)
 			continue;
 		irq += __ilog2(bits);
+		spin_unlock_irqrestore(&pmac_pic_lock, flags);
 		__do_IRQ(irq, regs);
-		return IRQ_HANDLED;
+		spin_lock_irqsave(&pmac_pic_lock, flags);
+		rc = IRQ_HANDLED;
 	}
-	printk("gatwick irq not from gatwick pic\n");
-	return IRQ_NONE;
+  	spin_unlock_irqrestore(&pmac_pic_lock, flags);
+	return rc;
 }
 
 static int pmac_get_irq(struct pt_regs *regs)
 {
 	int irq;
 	unsigned long bits = 0;
+	unsigned long flags;
 
 #ifdef CONFIG_SMP
 	void psurge_smp_message_recv(struct pt_regs *);
@@ -231,6 +254,7 @@ static int pmac_get_irq(struct pt_regs *
 		return -2;	/* ignore, already handled */
         }
 #endif /* CONFIG_SMP */
+  	spin_lock_irqsave(&pmac_pic_lock, flags);
 	for (irq = max_real_irqs; (irq -= 32) >= 0; ) {
 		int i = irq >> 5;
 		bits = in_le32(&pmac_irq_hw[i]->event) | ppc_lost_interrupts[i];
@@ -242,6 +266,7 @@ static int pmac_get_irq(struct pt_regs *
 		irq += __ilog2(bits);
 		break;
 	}
+  	spin_unlock_irqrestore(&pmac_pic_lock, flags);
 
 	return irq;
 }
@@ -390,7 +415,6 @@ static struct irqaction gatwick_cascade_
 static void __init pmac_pic_probe_oldstyle(void)
 {
         int i;
-	int irq_cascade = -1;
         struct device_node *master = NULL;
 	struct device_node *slave = NULL;
 	u8 __iomem *addr;
@@ -444,9 +468,16 @@ static void __init pmac_pic_probe_oldsty
 	}
 	BUG_ON(master == NULL);
 
-	/* Set the handler for the main PIC */
-	for ( i = 0; i < max_real_irqs ; i++ )
-		irq_desc[i].chip = &pmac_pic;
+	/* Mark level interrupts and set handlers */
+	for (i = 0; i < max_irqs; i++) {
+		int level = !!(level_mask[i >> 5] & (1UL << (i & 0x1f)));
+		if (level)
+			irq_desc[i].status |= IRQ_LEVEL;
+		else
+			irq_desc[i].status |= IRQ_DELAYED_DISABLE;
+		set_irq_chip_and_handler(i, &pmac_pic, level ?
+					 handle_level_irq : handle_edge_irq);
+	}
 
 	/* Get addresses of first controller if we have a node for it */
 	BUG_ON(of_address_to_resource(master, 0, &r));
@@ -473,29 +504,22 @@ static void __init pmac_pic_probe_oldsty
 			pmac_irq_hw[i++] =
 				(volatile struct pmac_irq_hw __iomem *)
 				(addr + 0x10);
-		irq_cascade = slave->intrs[0].line;
+		pmac_irq_cascade = slave->intrs[0].line;
 
 		printk(KERN_INFO "irq: Found slave Apple PIC %s for %d irqs"
 		       " cascade: %d\n", slave->full_name,
-		       max_irqs - max_real_irqs, irq_cascade);
+		       max_irqs - max_real_irqs, pmac_irq_cascade);
 	}
 	of_node_put(slave);
 
-	/* disable all interrupts in all controllers */
+	/* Disable all interrupts in all controllers */
 	for (i = 0; i * 32 < max_irqs; ++i)
 		out_le32(&pmac_irq_hw[i]->enable, 0);
 
-	/* mark level interrupts */
-	for (i = 0; i < max_irqs; i++)
-		if (level_mask[i >> 5] & (1UL << (i & 0x1f)))
-			irq_desc[i].status = IRQ_LEVEL;
-
-	/* Setup handlers for secondary controller and hook cascade irq*/
-	if (slave) {
-		for ( i = max_real_irqs ; i < max_irqs ; i++ )
-			irq_desc[i].chip = &gatwick_pic;
-		setup_irq(irq_cascade, &gatwick_cascade_action);
-	}
+	/* Hookup cascade irq */
+	if (slave)
+		setup_irq(pmac_irq_cascade, &gatwick_cascade_action);
+
 	printk(KERN_INFO "irq: System has %d possible interrupts\n", max_irqs);
 #ifdef CONFIG_XMON
 	setup_irq(20, &xmon_action);
Index: linux-work/include/asm-powerpc/irq.h
===================================================================
--- linux-work.orig/include/asm-powerpc/irq.h	2006-06-06 14:45:10.000000000 +1000
+++ linux-work/include/asm-powerpc/irq.h	2006-06-06 16:20:08.000000000 +1000
@@ -429,9 +429,12 @@ extern u64 ppc64_interrupt_controller;
 
 #endif
 
+#ifndef CONFIG_PPC_MERGE
 #define NR_MASK_WORDS	((NR_IRQS + 31) / 32)
 /* pedantic: these are long because they are used with set_bit --RR */
 extern unsigned long ppc_cached_irq_mask[NR_MASK_WORDS];
+#endif
+
 extern atomic_t ppc_n_lost_interrupts;
 
 #define virt_irq_create_mapping(x)	(x)

^ permalink raw reply

* [PATCH] powerpc-genirq: port 8259 driver
From: Benjamin Herrenschmidt @ 2006-06-13  3:54 UTC (permalink / raw)
  To: linuxppc-dev list; +Cc: Ingo Molnar, Thomas Gleixner

This patch ports the powerpc's i8259 driver to genirq.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---

Patch depends on Ingo/Thomas genirq patchset. It's not to be merged
right away, It's being posted here for comments as I may have further
rework.

Index: linux-work/arch/powerpc/sysdev/i8259.c
===================================================================
--- linux-work.orig/arch/powerpc/sysdev/i8259.c	2006-06-06 14:39:19.000000000 +1000
+++ linux-work/arch/powerpc/sysdev/i8259.c	2006-06-06 14:45:10.000000000 +1000
@@ -69,11 +69,6 @@ int i8259_irq(struct pt_regs *regs)
 	return irq + i8259_pic_irq_offset;
 }
 
-int i8259_irq_cascade(struct pt_regs *regs, void *unused)
-{
-	return i8259_irq(regs);
-}
-
 static void i8259_mask_and_ack_irq(unsigned int irq_nr)
 {
 	unsigned long flags;
@@ -129,19 +124,11 @@ static void i8259_unmask_irq(unsigned in
 	spin_unlock_irqrestore(&i8259_lock, flags);
 }
 
-static void i8259_end_irq(unsigned int irq)
-{
-	if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))
-	    && irq_desc[irq].action)
-		i8259_unmask_irq(irq);
-}
-
-struct hw_interrupt_type i8259_pic = {
-	.typename = " i8259    ",
-	.enable = i8259_unmask_irq,
-	.disable = i8259_mask_irq,
-	.ack = i8259_mask_and_ack_irq,
-	.end = i8259_end_irq,
+static struct irq_chip i8259_pic = {
+	.typename	= " i8259    ",
+	.mask		= i8259_mask_irq,
+	.unmask		= i8259_unmask_irq,
+	.mask_ack	= i8259_mask_and_ack_irq,
 };
 
 static struct resource pic1_iores = {
@@ -207,8 +194,11 @@ void __init i8259_init(unsigned long int
 
 	spin_unlock_irqrestore(&i8259_lock, flags);
 
-	for (i = 0; i < NUM_ISA_INTERRUPTS; ++i)
-		irq_desc[offset + i].chip = &i8259_pic;
+	for (i = 0; i < NUM_ISA_INTERRUPTS; ++i) {
+		set_irq_chip_and_handler(offset + i, &i8259_pic,
+					 handle_level_irq);
+		irq_desc[offset + i].status |= IRQ_LEVEL;
+	}
 
 	/* reserve our resources */
 	setup_irq(offset + 2, &i8259_irqaction);
Index: linux-work/include/asm-powerpc/i8259.h
===================================================================
--- linux-work.orig/include/asm-powerpc/i8259.h	2006-06-06 14:39:19.000000000 +1000
+++ linux-work/include/asm-powerpc/i8259.h	2006-06-06 14:45:10.000000000 +1000
@@ -4,11 +4,8 @@
 
 #include <linux/irq.h>
 
-extern struct hw_interrupt_type i8259_pic;
-
 extern void i8259_init(unsigned long intack_addr, int offset);
 extern int i8259_irq(struct pt_regs *regs);
-extern int i8259_irq_cascade(struct pt_regs *regs, void *unused);
 
 #endif /* __KERNEL__ */
 #endif /* _ASM_POWERPC_I8259_H */

^ permalink raw reply

* [PATCH] powerpc-genirq: port xics driver
From: Benjamin Herrenschmidt @ 2006-06-13  3:53 UTC (permalink / raw)
  To: linuxppc-dev list; +Cc: Ingo Molnar, Thomas Gleixner

This patch ports the xics driver to genirq and updates the pSeries
platform code accordingly. It also removes the local function pointers
structure and use separate callbacks at the toplevel instead when
necessary (to avoid too many function pointer indirections) or if's in
some cases.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---

Patch depends on Ingo/Thomas genirq patchset. It's not to be merged
right away, It's being posted here for comments as I may have further
rework.

Index: linux-work/arch/powerpc/platforms/pseries/setup.c
===================================================================
--- linux-work.orig/arch/powerpc/platforms/pseries/setup.c	2006-06-06 14:39:19.000000000 +1000
+++ linux-work/arch/powerpc/platforms/pseries/setup.c	2006-06-06 14:43:43.000000000 +1000
@@ -119,6 +119,21 @@ static void __init fwnmi_init(void)
 		fwnmi_active = 1;
 }
 
+void pSeries_8259_cascade(unsigned int irq, struct irq_desc *desc,
+			  struct pt_regs *regs)
+{
+	unsigned int max = 100;
+
+	while(max--) {
+		int cascade_irq = i8259_irq(regs);
+		if (max == 99)
+			desc->chip->eoi(irq);
+		if (cascade_irq < 0)
+			break;
+		generic_handle_irq(cascade_irq, regs);
+	};
+}
+
 static void __init pSeries_init_mpic(void)
 {
         unsigned int *addrp;
@@ -141,7 +156,7 @@ static void __init pSeries_init_mpic(voi
 	i8259_init(intack, 0);
 
 	/* Hook cascade to mpic */
-	mpic_setup_cascade(NUM_ISA_INTERRUPTS, i8259_irq_cascade, NULL);
+	set_irq_chained_handler(NUM_ISA_INTERRUPTS, pSeries_8259_cascade);
 }
 
 static void __init pSeries_setup_mpic(void)
@@ -202,10 +217,8 @@ static void __init pSeries_setup_arch(vo
 		/* Allocate the mpic now, so that find_and_init_phbs() can
 		 * fill the ISUs */
 		pSeries_setup_mpic();
-	} else {
+	} else
 		ppc_md.init_IRQ       = xics_init_IRQ;
-		ppc_md.get_irq        = xics_get_irq;
-	}
 
 #ifdef CONFIG_SMP
 	smp_init_pSeries();
@@ -292,10 +305,7 @@ static void pSeries_mach_cpu_die(void)
 {
 	local_irq_disable();
 	idle_task_exit();
-	/* Some hardware requires clearing the CPPR, while other hardware does not
-	 * it is safe either way
-	 */
-	pSeriesLP_cppr_info(0, 0);
+	xics_teardown_cpu(0);
 	rtas_stop_self();
 	/* Should never get here... */
 	BUG();
Index: linux-work/arch/powerpc/platforms/pseries/xics.c
===================================================================
--- linux-work.orig/arch/powerpc/platforms/pseries/xics.c	2006-06-06 14:39:19.000000000 +1000
+++ linux-work/arch/powerpc/platforms/pseries/xics.c	2006-06-06 14:39:48.000000000 +1000
@@ -32,23 +32,6 @@
 
 #include "xics.h"
 
-static unsigned int xics_startup(unsigned int irq);
-static void xics_enable_irq(unsigned int irq);
-static void xics_disable_irq(unsigned int irq);
-static void xics_mask_and_ack_irq(unsigned int irq);
-static void xics_end_irq(unsigned int irq);
-static void xics_set_affinity(unsigned int irq_nr, cpumask_t cpumask);
-
-static struct hw_interrupt_type xics_pic = {
-	.typename = " XICS     ",
-	.startup = xics_startup,
-	.enable = xics_enable_irq,
-	.disable = xics_disable_irq,
-	.ack = xics_mask_and_ack_irq,
-	.end = xics_end_irq,
-	.set_affinity = xics_set_affinity
-};
-
 /* This is used to map real irq numbers to virtual */
 static struct radix_tree_root irq_map = RADIX_TREE_INIT(GFP_ATOMIC);
 
@@ -99,47 +82,33 @@ static int ibm_set_xive;
 static int ibm_int_on;
 static int ibm_int_off;
 
-typedef struct {
-	int (*xirr_info_get)(int cpu);
-	void (*xirr_info_set)(int cpu, int val);
-	void (*cppr_info)(int cpu, u8 val);
-	void (*qirr_info)(int cpu, u8 val);
-} xics_ops;
 
+/* Direct HW low level accessors */
 
-/* SMP */
 
-static int pSeries_xirr_info_get(int n_cpu)
+static inline int direct_xirr_info_get(int n_cpu)
 {
 	return in_be32(&xics_per_cpu[n_cpu]->xirr.word);
 }
 
-static void pSeries_xirr_info_set(int n_cpu, int value)
+static inline void direct_xirr_info_set(int n_cpu, int value)
 {
 	out_be32(&xics_per_cpu[n_cpu]->xirr.word, value);
 }
 
-static void pSeries_cppr_info(int n_cpu, u8 value)
+static inline void direct_cppr_info(int n_cpu, u8 value)
 {
 	out_8(&xics_per_cpu[n_cpu]->xirr.bytes[0], value);
 }
 
-static void pSeries_qirr_info(int n_cpu, u8 value)
+static inline void direct_qirr_info(int n_cpu, u8 value)
 {
 	out_8(&xics_per_cpu[n_cpu]->qirr.bytes[0], value);
 }
 
-static xics_ops pSeries_ops = {
-	pSeries_xirr_info_get,
-	pSeries_xirr_info_set,
-	pSeries_cppr_info,
-	pSeries_qirr_info
-};
-
-static xics_ops *ops = &pSeries_ops;
 
+/* LPAR low level accessors */
 
-/* LPAR */
 
 static inline long plpar_eoi(unsigned long xirr)
 {
@@ -162,7 +131,7 @@ static inline long plpar_xirr(unsigned l
 	return plpar_hcall(H_XIRR, 0, 0, 0, 0, xirr_ret, &dummy, &dummy);
 }
 
-static int pSeriesLP_xirr_info_get(int n_cpu)
+static inline int lpar_xirr_info_get(int n_cpu)
 {
 	unsigned long lpar_rc;
 	unsigned long return_value;
@@ -173,7 +142,7 @@ static int pSeriesLP_xirr_info_get(int n
 	return (int)return_value;
 }
 
-static void pSeriesLP_xirr_info_set(int n_cpu, int value)
+static inline void lpar_xirr_info_set(int n_cpu, int value)
 {
 	unsigned long lpar_rc;
 	unsigned long val64 = value & 0xffffffff;
@@ -184,7 +153,7 @@ static void pSeriesLP_xirr_info_set(int 
 		      val64);
 }
 
-void pSeriesLP_cppr_info(int n_cpu, u8 value)
+static inline void lpar_cppr_info(int n_cpu, u8 value)
 {
 	unsigned long lpar_rc;
 
@@ -193,7 +162,7 @@ void pSeriesLP_cppr_info(int n_cpu, u8 v
 		panic("bad return code cppr - rc = %lx\n", lpar_rc);
 }
 
-static void pSeriesLP_qirr_info(int n_cpu , u8 value)
+static inline void lpar_qirr_info(int n_cpu , u8 value)
 {
 	unsigned long lpar_rc;
 
@@ -202,36 +171,9 @@ static void pSeriesLP_qirr_info(int n_cp
 		panic("bad return code qirr - rc = %lx\n", lpar_rc);
 }
 
-xics_ops pSeriesLP_ops = {
-	pSeriesLP_xirr_info_get,
-	pSeriesLP_xirr_info_set,
-	pSeriesLP_cppr_info,
-	pSeriesLP_qirr_info
-};
-
-static unsigned int xics_startup(unsigned int virq)
-{
-	unsigned int irq;
-
-	irq = irq_offset_down(virq);
-	if (radix_tree_insert(&irq_map, virt_irq_to_real(irq),
-			      &virt_irq_to_real_map[irq]) == -ENOMEM)
-		printk(KERN_CRIT "Out of memory creating real -> virtual"
-		       " IRQ mapping for irq %u (real 0x%x)\n",
-		       virq, virt_irq_to_real(irq));
-	xics_enable_irq(virq);
-	return 0;	/* return value is ignored */
-}
 
-static unsigned int real_irq_to_virt(unsigned int real_irq)
-{
-	unsigned int *ptr;
+/* High level handlers and init code */
 
-	ptr = radix_tree_lookup(&irq_map, real_irq);
-	if (ptr == NULL)
-		return NO_IRQ;
-	return ptr - virt_irq_to_real_map;
-}
 
 #ifdef CONFIG_SMP
 static int get_irq_server(unsigned int irq)
@@ -265,17 +207,20 @@ static int get_irq_server(unsigned int i
 }
 #endif
 
-static void xics_enable_irq(unsigned int virq)
+
+static void xics_unmask_irq(unsigned int virq)
 {
 	unsigned int irq;
 	int call_status;
 	unsigned int server;
 
 	irq = virt_irq_to_real(irq_offset_down(virq));
-	if (irq == XICS_IPI)
+	WARN_ON(irq == NO_IRQ);
+	if (irq == XICS_IPI || irq == NO_IRQ)
 		return;
 
 	server = get_irq_server(virq);
+
 	call_status = rtas_call(ibm_set_xive, 3, 1, NULL, irq, server,
 				DEFAULT_PRIORITY);
 	if (call_status != 0) {
@@ -294,7 +239,7 @@ static void xics_enable_irq(unsigned int
 	}
 }
 
-static void xics_disable_real_irq(unsigned int irq)
+static void xics_mask_real_irq(unsigned int irq)
 {
 	int call_status;
 	unsigned int server;
@@ -319,75 +264,104 @@ static void xics_disable_real_irq(unsign
 	}
 }
 
-static void xics_disable_irq(unsigned int virq)
+static void xics_mask_irq(unsigned int virq)
 {
 	unsigned int irq;
 
 	irq = virt_irq_to_real(irq_offset_down(virq));
-	xics_disable_real_irq(irq);
+	WARN_ON(irq == NO_IRQ);
+	if (irq != NO_IRQ)
+		xics_mask_real_irq(irq);
+}
+
+static void xics_set_irq_revmap(unsigned int virq)
+{
+	unsigned int irq;
+
+	irq = irq_offset_down(virq);
+	if (radix_tree_insert(&irq_map, virt_irq_to_real(irq),
+			      &virt_irq_to_real_map[irq]) == -ENOMEM)
+		printk(KERN_CRIT "Out of memory creating real -> virtual"
+		       " IRQ mapping for irq %u (real 0x%x)\n",
+		       virq, virt_irq_to_real(irq));
+}
+
+static unsigned int xics_startup(unsigned int virq)
+{
+	xics_set_irq_revmap(virq);
+	xics_unmask_irq(virq);
+	return 0;
+}
+
+static unsigned int real_irq_to_virt(unsigned int real_irq)
+{
+	unsigned int *ptr;
+
+	ptr = radix_tree_lookup(&irq_map, real_irq);
+	if (ptr == NULL)
+		return NO_IRQ;
+	return ptr - virt_irq_to_real_map;
 }
 
-static void xics_end_irq(unsigned int irq)
+static void xics_eoi_direct(unsigned int irq)
 {
 	int cpu = smp_processor_id();
 
 	iosync();
-	ops->xirr_info_set(cpu, ((0xff << 24) |
-				 (virt_irq_to_real(irq_offset_down(irq)))));
-
+	direct_xirr_info_set(cpu, ((0xff << 24) |
+				   (virt_irq_to_real(irq_offset_down(irq)))));
 }
 
-static void xics_mask_and_ack_irq(unsigned int irq)
+
+static void xics_eoi_lpar(unsigned int irq)
 {
 	int cpu = smp_processor_id();
 
-	if (irq < irq_offset_value()) {
-		i8259_pic.ack(irq);
-		iosync();
-		ops->xirr_info_set(cpu, ((0xff<<24) |
-					 xics_irq_8259_cascade_real));
-		iosync();
-	}
+	iosync();
+	lpar_xirr_info_set(cpu, ((0xff << 24) |
+				 (virt_irq_to_real(irq_offset_down(irq)))));
+
 }
 
-int xics_get_irq(struct pt_regs *regs)
+static inline int xics_remap_irq(int vec)
 {
-	unsigned int cpu = smp_processor_id();
-	unsigned int vec;
 	int irq;
 
-	vec = ops->xirr_info_get(cpu);
-	/*  (vec >> 24) == old priority */
 	vec &= 0x00ffffff;
 
-	/* for sanity, this had better be < NR_IRQS - 16 */
-	if (vec == xics_irq_8259_cascade_real) {
-		irq = i8259_irq(regs);
-		xics_end_irq(irq_offset_up(xics_irq_8259_cascade));
-	} else if (vec == XICS_IRQ_SPURIOUS) {
-		irq = -1;
-	} else {
-		irq = real_irq_to_virt(vec);
-		if (irq == NO_IRQ)
-			irq = real_irq_to_virt_slowpath(vec);
-		if (irq == NO_IRQ) {
-			printk(KERN_ERR "Interrupt %u (real) is invalid,"
-			       " disabling it.\n", vec);
-			xics_disable_real_irq(vec);
-		} else
-			irq = irq_offset_up(irq);
-	}
-	return irq;
+	if (vec == XICS_IRQ_SPURIOUS)
+		return NO_IRQ;
+
+	irq = real_irq_to_virt(vec);
+	if (irq == NO_IRQ)
+		irq = real_irq_to_virt_slowpath(vec);
+	if (likely(irq != NO_IRQ))
+		return irq_offset_up(irq);
+
+	printk(KERN_ERR "Interrupt %u (real) is invalid,"
+	       " disabling it.\n", vec);
+	xics_mask_real_irq(vec);
+	return NO_IRQ;
 }
 
-#ifdef CONFIG_SMP
+static int xics_get_irq_direct(struct pt_regs *regs)
+{
+	unsigned int cpu = smp_processor_id();
 
-static irqreturn_t xics_ipi_action(int irq, void *dev_id, struct pt_regs *regs)
+	return xics_remap_irq(direct_xirr_info_get(cpu));
+}
+
+static int xics_get_irq_lpar(struct pt_regs *regs)
 {
-	int cpu = smp_processor_id();
+	unsigned int cpu = smp_processor_id();
+
+	return xics_remap_irq(lpar_xirr_info_get(cpu));
+}
 
-	ops->qirr_info(cpu, 0xff);
+#ifdef CONFIG_SMP
 
+static irqreturn_t xics_ipi_dispatch(int cpu, struct pt_regs *regs)
+{
 	WARN_ON(cpu_is_offline(cpu));
 
 	while (xics_ipi_message[cpu].value) {
@@ -419,18 +393,108 @@ static irqreturn_t xics_ipi_action(int i
 	return IRQ_HANDLED;
 }
 
+static irqreturn_t xics_ipi_action_direct(int irq, void *dev_id, struct pt_regs *regs)
+{
+	int cpu = smp_processor_id();
+
+	direct_qirr_info(cpu, 0xff);
+
+	return xics_ipi_dispatch(cpu, regs);
+}
+
+static irqreturn_t xics_ipi_action_lpar(int irq, void *dev_id, struct pt_regs *regs)
+{
+	int cpu = smp_processor_id();
+
+	lpar_qirr_info(cpu, 0xff);
+
+	return xics_ipi_dispatch(cpu, regs);
+}
+
 void xics_cause_IPI(int cpu)
 {
-	ops->qirr_info(cpu, IPI_PRIORITY);
+	if (firmware_has_feature(FW_FEATURE_LPAR))
+		lpar_qirr_info(cpu, IPI_PRIORITY);
+	else
+		direct_qirr_info(cpu, IPI_PRIORITY);
 }
+
 #endif /* CONFIG_SMP */
 
+static void xics_set_cpu_priority(int cpu, unsigned char cppr)
+{
+	if (firmware_has_feature(FW_FEATURE_LPAR))
+		lpar_cppr_info(cpu, cppr);
+	else
+		direct_cppr_info(cpu, cppr);
+	iosync();
+}
+
+static void xics_set_affinity(unsigned int virq, cpumask_t cpumask)
+{
+	unsigned int irq;
+	int status;
+	int xics_status[2];
+	unsigned long newmask;
+	cpumask_t tmp = CPU_MASK_NONE;
+
+	irq = virt_irq_to_real(irq_offset_down(virq));
+	if (irq == XICS_IPI || irq == NO_IRQ)
+		return;
+
+	status = rtas_call(ibm_get_xive, 1, 3, xics_status, irq);
+
+	if (status) {
+		printk(KERN_ERR "xics_set_affinity: irq=%u ibm,get-xive "
+		       "returns %d\n", irq, status);
+		return;
+	}
+
+	/* For the moment only implement delivery to all cpus or one cpu */
+	if (cpus_equal(cpumask, CPU_MASK_ALL)) {
+		newmask = default_distrib_server;
+	} else {
+		cpus_and(tmp, cpu_online_map, cpumask);
+		if (cpus_empty(tmp))
+			return;
+		newmask = get_hard_smp_processor_id(first_cpu(tmp));
+	}
+
+	status = rtas_call(ibm_set_xive, 3, 1, NULL,
+				irq, newmask, xics_status[1]);
+
+	if (status) {
+		printk(KERN_ERR "xics_set_affinity: irq=%u ibm,set-xive "
+		       "returns %d\n", irq, status);
+		return;
+	}
+}
+
+static struct irq_chip xics_pic_direct = {
+	.typename = " XICS     ",
+	.startup = xics_startup,
+	.mask = xics_mask_irq,
+	.unmask = xics_unmask_irq,
+	.eoi = xics_eoi_direct,
+	.set_affinity = xics_set_affinity
+};
+
+
+static struct irq_chip xics_pic_lpar = {
+	.typename = " XICS     ",
+	.startup = xics_startup,
+	.mask = xics_mask_irq,
+	.unmask = xics_unmask_irq,
+	.eoi = xics_eoi_lpar,
+	.set_affinity = xics_set_affinity
+};
+
+
 void xics_setup_cpu(void)
 {
 	int cpu = smp_processor_id();
 
-	ops->cppr_info(cpu, 0xff);
-	iosync();
+	xics_set_cpu_priority(cpu, 0xff);
 
 	/*
 	 * Put the calling processor into the GIQ.  This is really only
@@ -454,6 +518,7 @@ void xics_init_IRQ(void)
 		unsigned long addr;
 		unsigned long size;
 	} intnodes[NR_CPUS];
+	struct irq_chip *chip;
 
 	ppc64_boot_msg(0x20, "XICS Init");
 
@@ -520,26 +585,10 @@ nextnode:
 	intr_base = intnodes[0].addr;
 	intr_size = intnodes[0].size;
 
-	np = of_find_node_by_type(NULL, "interrupt-controller");
-	if (!np) {
-		printk(KERN_WARNING "xics: no ISA interrupt controller\n");
-		xics_irq_8259_cascade_real = -1;
-		xics_irq_8259_cascade = -1;
+	if (firmware_has_feature(FW_FEATURE_LPAR)) {
+		ppc_md.get_irq = xics_get_irq_lpar;
+		chip = &xics_pic_lpar;
 	} else {
-		ireg = (uint *) get_property(np, "interrupts", NULL);
-		if (!ireg)
-			panic("xics_init_IRQ: can't find ISA interrupts property");
-
-		xics_irq_8259_cascade_real = *ireg;
-		xics_irq_8259_cascade
-			= virt_irq_create_mapping(xics_irq_8259_cascade_real);
-		i8259_init(0, 0);
-		of_node_put(np);
-	}
-
-	if (firmware_has_feature(FW_FEATURE_LPAR))
-		ops = &pSeriesLP_ops;
-	else {
 #ifdef CONFIG_SMP
 		for_each_possible_cpu(i) {
 			int hard_id;
@@ -555,32 +604,54 @@ nextnode:
 #else
 		xics_per_cpu[0] = ioremap(intr_base, intr_size);
 #endif /* CONFIG_SMP */
+		ppc_md.get_irq = xics_get_irq_direct;
+		chip = &xics_pic_direct;
+
 	}
 
-	for (i = irq_offset_value(); i < NR_IRQS; ++i)
-		get_irq_desc(i)->chip = &xics_pic;
+	for (i = irq_offset_value(); i < NR_IRQS; ++i) {
+		/* All IRQs on XICS are level for now. MSI code may want to modify
+		 * that for reporting purposes
+		 */
+		get_irq_desc(i)->status |= IRQ_LEVEL;
+		set_irq_chip_and_handler(i, chip, handle_fasteoi_irq);
+	}
 
 	xics_setup_cpu();
 
 	ppc64_boot_msg(0x21, "XICS Done");
 }
 
-/*
- * We cant do this in init_IRQ because we need the memory subsystem up for
- * request_irq()
- */
-static int __init xics_setup_i8259(void)
+static int xics_setup_8259_cascade(void)
 {
-	if (ppc64_interrupt_controller == IC_PPC_XIC &&
-	    xics_irq_8259_cascade != -1) {
-		if (request_irq(irq_offset_up(xics_irq_8259_cascade),
-				no_action, 0, "8259 cascade", NULL))
-			printk(KERN_ERR "xics_setup_i8259: couldn't get 8259 "
-					"cascade\n");
+	struct device_node *np;
+	uint *ireg;
+
+	np = of_find_node_by_type(NULL, "interrupt-controller");
+	if (np == NULL) {
+		printk(KERN_WARNING "xics: no ISA interrupt controller\n");
+		xics_irq_8259_cascade_real = -1;
+		xics_irq_8259_cascade = -1;
+		return 0;
 	}
+
+	ireg = (uint *) get_property(np, "interrupts", NULL);
+	if (!ireg)
+		panic("xics_init_IRQ: can't find ISA interrupts property");
+
+	xics_irq_8259_cascade_real = *ireg;
+	xics_irq_8259_cascade = irq_offset_up
+		(virt_irq_create_mapping(xics_irq_8259_cascade_real));
+	i8259_init(0, 0);
+	of_node_put(np);
+
+	xics_set_irq_revmap(xics_irq_8259_cascade);
+	set_irq_chained_handler(xics_irq_8259_cascade, pSeries_8259_cascade);
+
 	return 0;
 }
-arch_initcall(xics_setup_i8259);
+arch_initcall(xics_setup_8259_cascade);
+
 
 #ifdef CONFIG_SMP
 void xics_request_IPIs(void)
@@ -588,64 +659,28 @@ void xics_request_IPIs(void)
 	virt_irq_to_real_map[XICS_IPI] = XICS_IPI;
 
 	/* IPIs are marked SA_INTERRUPT as they must run with irqs disabled */
-	request_irq(irq_offset_up(XICS_IPI), xics_ipi_action, SA_INTERRUPT,
-		    "IPI", NULL);
-	get_irq_desc(irq_offset_up(XICS_IPI))->status |= IRQ_PER_CPU;
+	set_irq_handler(irq_offset_up(XICS_IPI), handle_percpu_irq);
+	if (firmware_has_feature(FW_FEATURE_LPAR))
+		request_irq(irq_offset_up(XICS_IPI), xics_ipi_action_lpar,
+			    SA_INTERRUPT, "IPI", NULL);
+	else
+		request_irq(irq_offset_up(XICS_IPI), xics_ipi_action_direct,
+			    SA_INTERRUPT, "IPI", NULL);
 }
 #endif
 
-static void xics_set_affinity(unsigned int virq, cpumask_t cpumask)
-{
-	unsigned int irq;
-	int status;
-	int xics_status[2];
-	unsigned long newmask;
-	cpumask_t tmp = CPU_MASK_NONE;
-
-	irq = virt_irq_to_real(irq_offset_down(virq));
-	if (irq == XICS_IPI || irq == NO_IRQ)
-		return;
-
-	status = rtas_call(ibm_get_xive, 1, 3, xics_status, irq);
-
-	if (status) {
-		printk(KERN_ERR "xics_set_affinity: irq=%u ibm,get-xive "
-		       "returns %d\n", irq, status);
-		return;
-	}
-
-	/* For the moment only implement delivery to all cpus or one cpu */
-	if (cpus_equal(cpumask, CPU_MASK_ALL)) {
-		newmask = default_distrib_server;
-	} else {
-		cpus_and(tmp, cpu_online_map, cpumask);
-		if (cpus_empty(tmp))
-			return;
-		newmask = get_hard_smp_processor_id(first_cpu(tmp));
-	}
-
-	status = rtas_call(ibm_set_xive, 3, 1, NULL,
-				irq, newmask, xics_status[1]);
-
-	if (status) {
-		printk(KERN_ERR "xics_set_affinity: irq=%u ibm,set-xive "
-		       "returns %d\n", irq, status);
-		return;
-	}
-}
-
 void xics_teardown_cpu(int secondary)
 {
 	int cpu = smp_processor_id();
 
-	ops->cppr_info(cpu, 0x00);
-	iosync();
+	xics_set_cpu_priority(cpu, 0);
 
 	/*
 	 * Some machines need to have at least one cpu in the GIQ,
 	 * so leave the master cpu in the group.
 	 */
 	if (secondary) {
+		struct irq_desc *desc = get_irq_desc(irq_offset_up(XICS_IPI));
 		/*
 		 * we need to EOI the IPI if we got here from kexec down IPI
 		 *
@@ -653,7 +688,8 @@ void xics_teardown_cpu(int secondary)
 		 * should we be flagging idle loop instead?
 		 * or creating some task to be scheduled?
 		 */
-		ops->xirr_info_set(cpu, XICS_IPI);
+		if (desc->chip && desc->chip->eoi)
+			desc->chip->eoi(XICS_IPI);
 		rtas_set_indicator(GLOBAL_INTERRUPT_QUEUE,
 			(1UL << interrupt_server_size) - 1 -
 			default_distrib_server, 0);
@@ -669,8 +705,7 @@ void xics_migrate_irqs_away(void)
 	unsigned int irq, virq, cpu = smp_processor_id();
 
 	/* Reject any interrupt that was queued to us... */
-	ops->cppr_info(cpu, 0);
-	iosync();
+	xics_set_cpu_priority(cpu, 0);
 
 	/* remove ourselves from the global interrupt queue */
 	status = rtas_set_indicator(GLOBAL_INTERRUPT_QUEUE,
@@ -678,11 +713,10 @@ void xics_migrate_irqs_away(void)
 	WARN_ON(status < 0);
 
 	/* Allow IPIs again... */
-	ops->cppr_info(cpu, DEFAULT_PRIORITY);
-	iosync();
+	xics_set_cpu_priority(cpu, DEFAULT_PRIORITY);
 
 	for_each_irq(virq) {
-		irq_desc_t *desc;
+		struct irq_desc *desc;
 		int xics_status[2];
 		unsigned long flags;
 
Index: linux-work/arch/powerpc/platforms/pseries/xics.h
===================================================================
--- linux-work.orig/arch/powerpc/platforms/pseries/xics.h	2006-06-06 14:39:19.000000000 +1000
+++ linux-work/arch/powerpc/platforms/pseries/xics.h	2006-06-06 14:39:48.000000000 +1000
@@ -14,13 +14,12 @@
 
 #include <linux/cache.h>
 
-void xics_init_IRQ(void);
-int xics_get_irq(struct pt_regs *);
-void xics_setup_cpu(void);
-void xics_teardown_cpu(int secondary);
-void xics_cause_IPI(int cpu);
-void xics_request_IPIs(void);
-void xics_migrate_irqs_away(void);
+extern void xics_init_IRQ(void);
+extern void xics_setup_cpu(void);
+extern void xics_teardown_cpu(int secondary);
+extern void xics_cause_IPI(int cpu);
+extern  void xics_request_IPIs(void);
+extern void xics_migrate_irqs_away(void);
 
 /* first argument is ignored for now*/
 void pSeriesLP_cppr_info(int n_cpu, u8 value);
@@ -31,4 +30,8 @@ struct xics_ipi_struct {
 
 extern struct xics_ipi_struct xics_ipi_message[NR_CPUS] __cacheline_aligned;
 
+struct irq_desc;
+extern void pSeries_8259_cascade(unsigned int irq, struct irq_desc *desc,
+				 struct pt_regs *regs);
+
 #endif /* _POWERPC_KERNEL_XICS_H */
Index: linux-work/include/asm-powerpc/irq.h
===================================================================
--- linux-work.orig/include/asm-powerpc/irq.h	2006-06-06 14:39:19.000000000 +1000
+++ linux-work/include/asm-powerpc/irq.h	2006-06-06 14:39:48.000000000 +1000
@@ -496,7 +496,6 @@ extern void irq_ctx_init(void);
 extern void call_do_softirq(struct thread_info *tp);
 extern int call_handle_irq(int irq, void *p1, void *p2,
 			   struct thread_info *tp, void *func);
-
 #else
 #define irq_ctx_init()
 

^ permalink raw reply

* [PATCH] powerpc-genirq: port mpic driver
From: Benjamin Herrenschmidt @ 2006-06-13  3:51 UTC (permalink / raw)
  To: linuxppc-dev list; +Cc: Ingo Molnar, Thomas Gleixner

This patch ports the MPIC driver to genirq. Special case cascade
handling is gone from the driver itself and now done by the platform
code using "standard" genirq mecanism. I also cleaned up a bit the ifdef
mess by having a separate irq_chip for HT interrupts.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---

Patch depends on Ingo/Thomas genirq patchset. It's not to be merged
right away, It's being posted here for comments as I may have further
rework.

Index: linux-work/arch/powerpc/sysdev/mpic.c
===================================================================
--- linux-work.orig/arch/powerpc/sysdev/mpic.c	2006-06-06 13:36:36.000000000 +1000
+++ linux-work/arch/powerpc/sysdev/mpic.c	2006-06-06 13:38:03.000000000 +1000
@@ -101,8 +101,8 @@ static inline u32 _mpic_cpu_read(struct 
 
 	if (mpic->flags & MPIC_PRIMARY)
 		cpu = hard_smp_processor_id();
-
-	return _mpic_read(mpic->flags & MPIC_BIG_ENDIAN, mpic->cpuregs[cpu], reg);
+	return _mpic_read(mpic->flags & MPIC_BIG_ENDIAN,
+			  mpic->cpuregs[cpu], reg);
 }
 
 static inline void _mpic_cpu_write(struct mpic *mpic, unsigned int reg, u32 value)
@@ -379,14 +379,14 @@ static inline u32 mpic_physmask(u32 cpum
 /* Get the mpic structure from the IPI number */
 static inline struct mpic * mpic_from_ipi(unsigned int ipi)
 {
-	return container_of(irq_desc[ipi].chip, struct mpic, hc_ipi);
+	return irq_desc[ipi].chip_data;
 }
 #endif
 
 /* Get the mpic structure from the irq number */
 static inline struct mpic * mpic_from_irq(unsigned int irq)
 {
-	return container_of(irq_desc[irq].chip, struct mpic, hc_irq);
+	return irq_desc[irq].chip_data;
 }
 
 /* Send an EOI */
@@ -411,7 +411,7 @@ static irqreturn_t mpic_ipi_action(int i
  */
 
 
-static void mpic_enable_irq(unsigned int irq)
+static void mpic_unmask_irq(unsigned int irq)
 {
 	unsigned int loops = 100000;
 	struct mpic *mpic = mpic_from_irq(irq);
@@ -430,35 +430,9 @@ static void mpic_enable_irq(unsigned int
 			break;
 		}
 	} while(mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) & MPIC_VECPRI_MASK);	
-
-#ifdef CONFIG_MPIC_BROKEN_U3
-	if (mpic->flags & MPIC_BROKEN_U3) {
-		unsigned int src = irq - mpic->irq_offset;
-		if (mpic_is_ht_interrupt(mpic, src) &&
-		    (irq_desc[irq].status & IRQ_LEVEL))
-			mpic_ht_end_irq(mpic, src);
-	}
-#endif /* CONFIG_MPIC_BROKEN_U3 */
-}
-
-static unsigned int mpic_startup_irq(unsigned int irq)
-{
-#ifdef CONFIG_MPIC_BROKEN_U3
-	struct mpic *mpic = mpic_from_irq(irq);
-	unsigned int src = irq - mpic->irq_offset;
-#endif /* CONFIG_MPIC_BROKEN_U3 */
-
-	mpic_enable_irq(irq);
-
-#ifdef CONFIG_MPIC_BROKEN_U3
-	if (mpic_is_ht_interrupt(mpic, src))
-		mpic_startup_ht_interrupt(mpic, src, irq_desc[irq].status);
-#endif /* CONFIG_MPIC_BROKEN_U3 */
-
-	return 0;
 }
 
-static void mpic_disable_irq(unsigned int irq)
+static void mpic_mask_irq(unsigned int irq)
 {
 	unsigned int loops = 100000;
 	struct mpic *mpic = mpic_from_irq(irq);
@@ -479,23 +453,58 @@ static void mpic_disable_irq(unsigned in
 	} while(!(mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) & MPIC_VECPRI_MASK));
 }
 
-static void mpic_shutdown_irq(unsigned int irq)
+static void mpic_end_irq(unsigned int irq)
 {
+	struct mpic *mpic = mpic_from_irq(irq);
+
+#ifdef DEBUG_IRQ
+	DBG("%s: end_irq: %d\n", mpic->name, irq);
+#endif
+	/* We always EOI on end_irq() even for edge interrupts since that
+	 * should only lower the priority, the MPIC should have properly
+	 * latched another edge interrupt coming in anyway
+	 */
+
+	mpic_eoi(mpic);
+}
+
 #ifdef CONFIG_MPIC_BROKEN_U3
+
+static void mpic_unmask_ht_irq(unsigned int irq)
+{
 	struct mpic *mpic = mpic_from_irq(irq);
 	unsigned int src = irq - mpic->irq_offset;
 
-	if (mpic_is_ht_interrupt(mpic, src))
-		mpic_shutdown_ht_interrupt(mpic, src, irq_desc[irq].status);
+	mpic_unmask_irq(irq);
 
-#endif /* CONFIG_MPIC_BROKEN_U3 */
+	if (irq_desc[irq].status & IRQ_LEVEL)
+		mpic_ht_end_irq(mpic, src);
+}
 
-	mpic_disable_irq(irq);
+static unsigned int mpic_startup_ht_irq(unsigned int irq)
+{
+	struct mpic *mpic = mpic_from_irq(irq);
+	unsigned int src = irq - mpic->irq_offset;
+
+	mpic_unmask_irq(irq);
+	mpic_startup_ht_interrupt(mpic, src, irq_desc[irq].status);
+
+	return 0;
 }
 
-static void mpic_end_irq(unsigned int irq)
+static void mpic_shutdown_ht_irq(unsigned int irq)
 {
 	struct mpic *mpic = mpic_from_irq(irq);
+	unsigned int src = irq - mpic->irq_offset;
+
+	mpic_shutdown_ht_interrupt(mpic, src, irq_desc[irq].status);
+	mpic_mask_irq(irq);
+}
+
+static void mpic_end_ht_irq(unsigned int irq)
+{
+	struct mpic *mpic = mpic_from_irq(irq);
+	unsigned int src = irq - mpic->irq_offset;
 
 #ifdef DEBUG_IRQ
 	DBG("%s: end_irq: %d\n", mpic->name, irq);
@@ -505,21 +514,16 @@ static void mpic_end_irq(unsigned int ir
 	 * latched another edge interrupt coming in anyway
 	 */
 
-#ifdef CONFIG_MPIC_BROKEN_U3
-	if (mpic->flags & MPIC_BROKEN_U3) {
-		unsigned int src = irq - mpic->irq_offset;
-		if (mpic_is_ht_interrupt(mpic, src) &&
-		    (irq_desc[irq].status & IRQ_LEVEL))
-			mpic_ht_end_irq(mpic, src);
-	}
-#endif /* CONFIG_MPIC_BROKEN_U3 */
-
+	if (irq_desc[irq].status & IRQ_LEVEL)
+		mpic_ht_end_irq(mpic, src);
 	mpic_eoi(mpic);
 }
 
+#endif /* CONFIG_MPIC_BROKEN_U3 */
+
 #ifdef CONFIG_SMP
 
-static void mpic_enable_ipi(unsigned int irq)
+static void mpic_unmask_ipi(unsigned int irq)
 {
 	struct mpic *mpic = mpic_from_ipi(irq);
 	unsigned int src = irq - mpic->ipi_offset;
@@ -528,7 +532,7 @@ static void mpic_enable_ipi(unsigned int
 	mpic_ipi_write(src, mpic_ipi_read(src) & ~MPIC_VECPRI_MASK);
 }
 
-static void mpic_disable_ipi(unsigned int irq)
+static void mpic_mask_ipi(unsigned int irq)
 {
 	/* NEVER disable an IPI... that's just plain wrong! */
 }
@@ -561,6 +565,28 @@ static void mpic_set_affinity(unsigned i
 		       mpic_physmask(cpus_addr(tmp)[0]));	
 }
 
+static struct irq_chip mpic_irq_chip = {
+	.mask	= mpic_mask_irq,
+	.unmask	= mpic_unmask_irq,
+	.eoi	= mpic_end_irq,
+};
+
+static struct irq_chip mpic_ipi_chip = {
+	.mask	= mpic_mask_ipi,
+	.unmask	= mpic_unmask_ipi,
+	.eoi	= mpic_end_ipi,
+};
+
+#ifdef CONFIG_MPIC_BROKEN_U3
+static struct irq_chip mpic_irq_ht_chip = {
+	.startup	= mpic_startup_ht_irq,
+	.shutdown	= mpic_shutdown_ht_irq,
+	.mask		= mpic_mask_irq,
+	.unmask		= mpic_unmask_ht_irq,
+	.eoi		= mpic_end_ht_irq,
+};
+#endif /* CONFIG_MPIC_BROKEN_U3 */
+
 
 /*
  * Exported functions
@@ -590,19 +616,19 @@ struct mpic * __init mpic_alloc(unsigned
 	memset(mpic, 0, sizeof(struct mpic));
 	mpic->name = name;
 
+	mpic->hc_irq = mpic_irq_chip;
 	mpic->hc_irq.typename = name;
-	mpic->hc_irq.startup = mpic_startup_irq;
-	mpic->hc_irq.shutdown = mpic_shutdown_irq;
-	mpic->hc_irq.enable = mpic_enable_irq;
-	mpic->hc_irq.disable = mpic_disable_irq;
-	mpic->hc_irq.end = mpic_end_irq;
 	if (flags & MPIC_PRIMARY)
 		mpic->hc_irq.set_affinity = mpic_set_affinity;
+#ifdef CONFIG_MPIC_BROKEN_U3
+	mpic->hc_ht_irq = mpic_irq_ht_chip;
+	mpic->hc_ht_irq.typename = name;
+	if (flags & MPIC_PRIMARY)
+		mpic->hc_ht_irq.set_affinity = mpic_set_affinity;
+#endif /* CONFIG_MPIC_BROKEN_U3 */
 #ifdef CONFIG_SMP
 	mpic->hc_ipi.typename = name;
-	mpic->hc_ipi.enable = mpic_enable_ipi;
-	mpic->hc_ipi.disable = mpic_disable_ipi;
-	mpic->hc_ipi.end = mpic_end_ipi;
+	mpic->hc_ipi = mpic_ipi_chip;
 #endif /* CONFIG_SMP */
 
 	mpic->flags = flags;
@@ -698,28 +724,6 @@ void __init mpic_assign_isu(struct mpic 
 		mpic->num_sources = isu_first + mpic->isu_size;
 }
 
-void __init mpic_setup_cascade(unsigned int irq, mpic_cascade_t handler,
-			       void *data)
-{
-	struct mpic *mpic = mpic_find(irq, NULL);
-	unsigned long flags;
-
-	/* Synchronization here is a bit dodgy, so don't try to replace cascade
-	 * interrupts on the fly too often ... but normally it's set up at boot.
-	 */
-	spin_lock_irqsave(&mpic_lock, flags);
-	if (mpic->cascade)	       
-		mpic_disable_irq(mpic->cascade_vec + mpic->irq_offset);
-	mpic->cascade = NULL;
-	wmb();
-	mpic->cascade_vec = irq - mpic->irq_offset;
-	mpic->cascade_data = data;
-	wmb();
-	mpic->cascade = handler;
-	mpic_enable_irq(irq);
-	spin_unlock_irqrestore(&mpic_lock, flags);
-}
-
 void __init mpic_init(struct mpic *mpic)
 {
 	int i;
@@ -751,8 +755,10 @@ void __init mpic_init(struct mpic *mpic)
 #ifdef CONFIG_SMP
 		if (!(mpic->flags & MPIC_PRIMARY))
 			continue;
-		irq_desc[mpic->ipi_offset+i].status |= IRQ_PER_CPU;
-		irq_desc[mpic->ipi_offset+i].chip = &mpic->hc_ipi;
+		set_irq_chip_data(mpic->ipi_offset+i, mpic);
+		set_irq_chip_and_handler(mpic->ipi_offset+i,
+					 &mpic->hc_ipi,
+					 handle_percpu_irq);
 #endif /* CONFIG_SMP */
 	}
 
@@ -764,7 +770,7 @@ void __init mpic_init(struct mpic *mpic)
 	/* Do the HT PIC fixups on U3 broken mpic */
 	DBG("MPIC flags: %x\n", mpic->flags);
 	if ((mpic->flags & MPIC_BROKEN_U3) && (mpic->flags & MPIC_PRIMARY))
-		mpic_scan_ht_pics(mpic);
+ 		mpic_scan_ht_pics(mpic);
 #endif /* CONFIG_MPIC_BROKEN_U3 */
 
 	for (i = 0; i < mpic->num_sources; i++) {
@@ -812,8 +818,17 @@ void __init mpic_init(struct mpic *mpic)
 
 		/* init linux descriptors */
 		if (i < mpic->irq_count) {
-			irq_desc[mpic->irq_offset+i].status = level ? IRQ_LEVEL : 0;
-			irq_desc[mpic->irq_offset+i].chip = &mpic->hc_irq;
+			struct irq_chip *chip = &mpic->hc_irq;
+
+			irq_desc[mpic->irq_offset+i].status |=
+				level ? IRQ_LEVEL : 0;
+#ifdef CONFIG_MPIC_BROKEN_U3
+			if (mpic_is_ht_interrupt(mpic, i))
+				chip = &mpic->hc_ht_irq;
+#endif /* CONFIG_MPIC_BROKEN_U3 */
+			set_irq_chip_data(mpic->irq_offset+i, mpic);
+			set_irq_chip_and_handler(mpic->irq_offset+i, chip,
+						 handle_fasteoi_irq);
 		}
 	}
 	
@@ -967,14 +982,6 @@ int mpic_get_one_irq(struct mpic *mpic, 
 #ifdef DEBUG_LOW
 	DBG("%s: get_one_irq(): %d\n", mpic->name, irq);
 #endif
-	if (mpic->cascade && irq == mpic->cascade_vec) {
-#ifdef DEBUG_LOW
-		DBG("%s: cascading ...\n", mpic->name);
-#endif
-		irq = mpic->cascade(regs, mpic->cascade_data);
-		mpic_eoi(mpic);
-		return irq;
-	}
 	if (unlikely(irq == MPIC_VEC_SPURRIOUS))
 		return -1;
 	if (irq < MPIC_VEC_IPI_0) {
Index: linux-work/include/asm-powerpc/mpic.h
===================================================================
--- linux-work.orig/include/asm-powerpc/mpic.h	2006-06-06 13:36:36.000000000 +1000
+++ linux-work/include/asm-powerpc/mpic.h	2006-06-06 13:38:03.000000000 +1000
@@ -110,9 +110,6 @@
 #define MPIC_VEC_TIMER_1	248
 #define MPIC_VEC_TIMER_0	247
 
-/* Type definition of the cascade handler */
-typedef int (*mpic_cascade_t)(struct pt_regs *regs, void *data);
-
 #ifdef CONFIG_MPIC_BROKEN_U3
 /* Fixup table entry */
 struct mpic_irq_fixup
@@ -129,9 +126,12 @@ struct mpic_irq_fixup
 struct mpic
 {
 	/* The "linux" controller struct */
-	hw_irq_controller	hc_irq;
+	struct irq_chip		hc_irq;
+#ifdef CONFIG_MPIC_BROKEN_U3
+	struct irq_chip		hc_ht_irq;
+#endif
 #ifdef CONFIG_SMP
-	hw_irq_controller	hc_ipi;
+	struct irq_chip		hc_ipi;
 #endif
 	const char		*name;
 	/* Flags */
@@ -149,10 +149,6 @@ struct mpic
 	unsigned int		num_sources;
 	/* Number of CPUs */
 	unsigned int		num_cpus;
-	/* cascade handler */
-	mpic_cascade_t		cascade;
-	void			*cascade_data;
-	unsigned int		cascade_vec;
 	/* senses array */
 	unsigned char		*senses;
 	unsigned int		senses_count;
@@ -233,17 +229,6 @@ extern void mpic_assign_isu(struct mpic 
  */
 extern void mpic_init(struct mpic *mpic);
 
-/* Setup a cascade. Currently, only one cascade is supported this
- * way, though you can always do a normal request_irq() and add
- * other cascades this way. You should call this _after_ having
- * added all the ISUs
- *
- * @irq_no:	"linux" irq number of the cascade (that is offset'ed vector)
- * @handler:	cascade handler function
- */
-extern void mpic_setup_cascade(unsigned int irq_no, mpic_cascade_t hanlder,
-			       void *data);
-
 /*
  * All of the following functions must only be used after the
  * ISUs have been assigned and the controller fully initialized
Index: linux-work/arch/powerpc/platforms/powermac/pic.c
===================================================================
--- linux-work.orig/arch/powerpc/platforms/powermac/pic.c	2006-06-06 13:36:36.000000000 +1000
+++ linux-work/arch/powerpc/platforms/powermac/pic.c	2006-06-06 14:44:55.000000000 +1000
@@ -503,9 +503,20 @@ static void __init pmac_pic_probe_oldsty
 }
 #endif /* CONFIG_PPC32 */
 
-static int pmac_u3_cascade(struct pt_regs *regs, void *data)
+static void pmac_u3_cascade(unsigned int irq, struct irq_desc *desc,
+			    struct pt_regs *regs)
 {
-	return mpic_get_one_irq((struct mpic *)data, regs);
+	struct mpic *mpic = desc->handler_data;
+	unsigned int max = 100;
+
+	while(max--) {
+		int cascade_irq = mpic_get_one_irq(mpic, regs);
+		if (max == 99)
+			desc->chip->eoi(irq);
+		if (irq < 0)
+			break;
+		generic_handle_irq(cascade_irq, regs);
+	};
 }
 
 static void __init pmac_pic_setup_mpic_nmi(struct mpic *mpic)
@@ -613,7 +624,8 @@ static int __init pmac_pic_probe_mpic(vo
 		of_node_put(slave);
 		return 0;
 	}
-	mpic_setup_cascade(slave->intrs[0].line, pmac_u3_cascade, mpic2);
+	set_irq_data(slave->intrs[0].line, mpic2);
+	set_irq_chained_handler(slave->intrs[0].line, pmac_u3_cascade);
 
 	of_node_put(slave);
 	return 0;
Index: linux-work/arch/powerpc/platforms/chrp/setup.c
===================================================================
--- linux-work.orig/arch/powerpc/platforms/chrp/setup.c	2006-06-06 13:36:36.000000000 +1000
+++ linux-work/arch/powerpc/platforms/chrp/setup.c	2006-06-06 13:38:03.000000000 +1000
@@ -316,6 +316,21 @@ chrp_event_scan(unsigned long unused)
 		  jiffies + event_scan_interval);
 }
 
+void chrp_8259_cascade(unsigned int irq, struct irq_desc *desc,
+			struct pt_regs *regs)
+{
+	unsigned int max = 100;
+
+	while(max--) {
+		int irq = i8259_irq(regs);
+		if (max == 99)
+			desc->chip->eoi(irq);
+		if (irq < 0)
+			break;
+		generic_handle_irq(irq, regs);
+	};
+}
+
 /*
  * Finds the open-pic node and sets up the mpic driver.
  */
@@ -403,7 +418,7 @@ static void __init chrp_find_openpic(voi
 	}
 
 	mpic_init(chrp_mpic);
-	mpic_setup_cascade(NUM_ISA_INTERRUPTS, i8259_irq_cascade, NULL);
+	set_irq_chained_handler(NUM_ISA_INTERRUPTS, chrp_8259_cascade);
 }
 
 #if defined(CONFIG_VT) && defined(CONFIG_INPUT_ADBHID) && defined(XMON)

^ permalink raw reply

* [PATCH] powerpc-genirq: Use generic_handle_irq()
From: Benjamin Herrenschmidt @ 2006-06-13  3:47 UTC (permalink / raw)
  To: linuxppc-dev list; +Cc: Ingo Molnar, Thomas Gleixner

This patch updates the ppc and powerpc architectures to use the new
generic_handle_irq() so that interrupt controllers can be ported to the
new genirq layer.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---

Patch depends on Ingo/Thomas genirq patchset.

Index: linux-work/arch/powerpc/kernel/irq.c
===================================================================
--- linux-work.orig/arch/powerpc/kernel/irq.c	2006-06-05 15:29:37.000000000 +1000
+++ linux-work/arch/powerpc/kernel/irq.c	2006-06-05 15:50:53.000000000 +1000
@@ -219,15 +219,19 @@ void do_IRQ(struct pt_regs *regs)
 		curtp = current_thread_info();
 		irqtp = hardirq_ctx[smp_processor_id()];
 		if (curtp != irqtp) {
+			struct irq_desc *desc = irq_desc + irq;
+			void *handler = desc->handle_irq;
+			if (handler == NULL)
+				handler = &__do_IRQ;
 			irqtp->task = curtp->task;
 			irqtp->flags = 0;
-			call___do_IRQ(irq, regs, irqtp);
+			call_handle_irq(irq, desc, regs, irqtp, handler);
 			irqtp->task = NULL;
 			if (irqtp->flags)
 				set_bits(irqtp->flags, &curtp->flags);
 		} else
 #endif
-			__do_IRQ(irq, regs);
+			generic_handle_irq(irq, regs);
 	} else if (irq != -2)
 		/* That's not SMP safe ... but who cares ? */
 		ppc_spurious_interrupts++;
Index: linux-work/arch/powerpc/kernel/misc_64.S
===================================================================
--- linux-work.orig/arch/powerpc/kernel/misc_64.S	2006-06-05 15:29:37.000000000 +1000
+++ linux-work/arch/powerpc/kernel/misc_64.S	2006-06-05 15:38:01.000000000 +1000
@@ -89,12 +89,14 @@ _GLOBAL(call_do_softirq)
 	mtlr	r0
 	blr
 
-_GLOBAL(call___do_IRQ)
+_GLOBAL(call_handle_irq)
+	ld	r8,0(r7)
 	mflr	r0
 	std	r0,16(r1)
-	stdu	r1,THREAD_SIZE-112(r5)
-	mr	r1,r5
-	bl	.__do_IRQ
+	mtctr	r8
+	stdu	r1,THREAD_SIZE-112(r6)
+	mr	r1,r6
+	bctrl
 	ld	r1,0(r1)
 	ld	r0,16(r1)
 	mtlr	r0
Index: linux-work/include/asm-powerpc/irq.h
===================================================================
--- linux-work.orig/include/asm-powerpc/irq.h	2006-06-05 15:29:37.000000000 +1000
+++ linux-work/include/asm-powerpc/irq.h	2006-06-05 15:50:53.000000000 +1000
@@ -494,8 +494,8 @@ extern struct thread_info *softirq_ctx[N
 
 extern void irq_ctx_init(void);
 extern void call_do_softirq(struct thread_info *tp);
-extern int call___do_IRQ(int irq, struct pt_regs *regs,
-		struct thread_info *tp);
+extern int call_handle_irq(int irq, void *p1, void *p2,
+			   struct thread_info *tp, void *func);
 
 #else
 #define irq_ctx_init()

^ permalink raw reply

* [PATCH] Fix builtin command line interaction with firmware
From: Amos Waterland @ 2006-06-13  3:45 UTC (permalink / raw)
  To: linuxppc-dev

It seems that prom_init's early_cmdline_parse is broken on at least
Apple 970 xserves and IBM JS20 blades with SLOF.  The firmware of these
machines returns -1 and 1 respectively when getprop is called for the
bootargs property of /chosen, causing Linux to ignore its builtin
command line in favor of a null string.  This patch makes Linux use its
builtin command line if getprop returns an error or a null string.

Signed-off-by: Amos Waterland <apw@us.ibm.com>

---

 arch/powerpc/kernel/prom_init.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

0f0caf0342f39b4437dcb3e9f2f1d62bfb4e3e7d
diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c
index f70bd09..2e63a90 100644
--- a/arch/powerpc/kernel/prom_init.c
+++ b/arch/powerpc/kernel/prom_init.c
@@ -574,7 +574,7 @@ static void __init early_cmdline_parse(v
 	if ((long)_prom->chosen > 0)
 		l = prom_getprop(_prom->chosen, "bootargs", p, COMMAND_LINE_SIZE-1);
 #ifdef CONFIG_CMDLINE
-	if (l == 0) /* dbl check */
+	if (l <= 0 || p[0] == '\0') /* dbl check */
 		strlcpy(RELOC(prom_cmd_line),
 			RELOC(CONFIG_CMDLINE), sizeof(prom_cmd_line));
 #endif /* CONFIG_CMDLINE */
-- 
1.0.4

^ permalink raw reply related

* [PATCH] Use 64k pages without needing cache-inhibited large pages
From: Paul Mackerras @ 2006-06-13  3:39 UTC (permalink / raw)
  To: linuxppc-dev

Some POWER5+ machines can do 64k hardware pages for normal memory but
not for cache-inhibited pages.  This patch lets us use 64k hardware
pages for most user processes on such machines (assuming the kernel
has been configured with CONFIG_PPC_64K_PAGES=y).  User processes
start out using 64k pages and get switched to 4k pages if they use any
non-cacheable mappings.

With this, we use 64k pages for the vmalloc region and 4k pages for
the imalloc region.  If anything creates a non-cacheable mapping in
the vmalloc region, the vmalloc region will get switched to 4k pages.
I don't know of any driver other than the DRM that would do this,
though, and these machines don't have AGP.

When a region gets switched from 64k pages to 4k pages, we do not have
to clear out all the 64k HPTEs from the hash table immediately.  We
use the _PAGE_COMBO bit in the Linux PTE to indicate whether the page
was hashed in as a 64k page or a set of 4k pages.  If hash_page is
trying to insert a 4k page for a Linux PTE and it sees that it has
already been inserted as a 64k page, it first invalidates the 64k HPTE
before inserting the 4k HPTE.  The hash invalidation routines also use
the _PAGE_COMBO bit, to determine whether to look for a 64k HPTE or a
set of 4k HPTEs to remove.  With those two changes, we can tolerate a
mix of 4k and 64k HPTEs in the hash table, and they will all get
removed when the address space is torn down.

Signed-off-by: Paul Mackerras <paulus@samba.org>
---
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
index aa0486d..ff29405 100644
--- a/arch/powerpc/kernel/asm-offsets.c
+++ b/arch/powerpc/kernel/asm-offsets.c
@@ -122,6 +122,8 @@ #ifdef CONFIG_PPC64
 	DEFINE(PACASLBCACHE, offsetof(struct paca_struct, slb_cache));
 	DEFINE(PACASLBCACHEPTR, offsetof(struct paca_struct, slb_cache_ptr));
 	DEFINE(PACACONTEXTID, offsetof(struct paca_struct, context.id));
+	DEFINE(PACACONTEXTSLLP, offsetof(struct paca_struct, context.sllp));
+	DEFINE(PACAVMALLOCSLLP, offsetof(struct paca_struct, vmalloc_sllp));
 #ifdef CONFIG_HUGETLB_PAGE
 	DEFINE(PACALOWHTLBAREAS, offsetof(struct paca_struct, context.low_htlb_areas));
 	DEFINE(PACAHIGHHTLBAREAS, offsetof(struct paca_struct, context.high_htlb_areas));
diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c
index 969f4ab..d77d24a 100644
--- a/arch/powerpc/kernel/prom.c
+++ b/arch/powerpc/kernel/prom.c
@@ -948,7 +948,10 @@ static struct ibm_pa_feature {
 	{CPU_FTR_CTRL, 0,		0, 3, 0},
 	{CPU_FTR_NOEXECUTE, 0,		0, 6, 0},
 	{CPU_FTR_NODSISRALIGN, 0,	1, 1, 1},
+#if 0
+	/* put this back once we know how to test if firmware does 64k IO */
 	{CPU_FTR_CI_LARGE_PAGE, 0,	1, 2, 0},
+#endif
 };
 
 static void __init check_cpu_pa_features(unsigned long node)
diff --git a/arch/powerpc/mm/hash_low_64.S b/arch/powerpc/mm/hash_low_64.S
index 106fba3..52e9142 100644
--- a/arch/powerpc/mm/hash_low_64.S
+++ b/arch/powerpc/mm/hash_low_64.S
@@ -369,6 +369,7 @@ _GLOBAL(__hash_page_4K)
 	rlwinm	r30,r4,32-9+7,31-7,31-7	/* _PAGE_RW -> _PAGE_DIRTY */
 	or	r30,r30,r31
 	ori	r30,r30,_PAGE_BUSY | _PAGE_ACCESSED | _PAGE_HASHPTE
+	oris	r30,r30,_PAGE_COMBO@h
 	/* Write the linux PTE atomically (setting busy) */
 	stdcx.	r30,0,r6
 	bne-	1b
@@ -428,6 +429,14 @@ END_FTR_SECTION(CPU_FTR_NOEXECUTE|CPU_FT
 	andi.	r0,r31,_PAGE_HASHPTE
 	li	r26,0			/* Default hidx */
 	beq	htab_insert_pte
+
+	/*
+	 * Check if the pte was already inserted into the hash table
+	 * as a 64k HW page, and invalidate the 64k HPTE if so.
+	 */
+	andis.	r0,r31,_PAGE_COMBO@h
+	beq	htab_inval_old_hpte
+
 	ld	r6,STK_PARM(r6)(r1)
 	ori	r26,r6,0x8000		/* Load the hidx mask */
 	ld	r26,0(r26)
@@ -498,6 +507,19 @@ _GLOBAL(htab_call_hpte_remove)
 	/* Try all again */
 	b	htab_insert_pte
 
+	/*
+	 * Call out to C code to invalidate an 64k HW HPTE that is
+	 * useless now that the segment has been switched to 4k pages.
+	 */
+htab_inval_old_hpte:
+	mr	r3,r29			/* virtual addr */
+	mr	r4,r31			/* PTE.pte */
+	li	r5,0			/* PTE.hidx */
+	li	r6,MMU_PAGE_64K		/* psize */
+	ld	r7,STK_PARM(r8)(r1)	/* local */
+	bl	.flush_hash_page
+	b	htab_insert_pte
+	
 htab_bail_ok:
 	li	r3,0
 	b	htab_bail
@@ -638,6 +660,12 @@ _GLOBAL(__hash_page_64K)
 	 * is changing this PTE anyway and might hash it.
 	 */
 	bne-	ht64_bail_ok
+BEGIN_FTR_SECTION
+	/* Check if PTE has the cache-inhibit bit set */
+	andi.	r0,r31,_PAGE_NO_CACHE
+	/* If so, bail out and refault as a 4k page */
+	bne-	ht64_bail_ok
+END_FTR_SECTION_IFCLR(CPU_FTR_CI_LARGE_PAGE)
 	/* Prepare new PTE value (turn access RW into DIRTY, then
 	 * add BUSY,HASHPTE and ACCESSED)
 	 */
diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c
index b43ed92..d03fd2b 100644
--- a/arch/powerpc/mm/hash_utils_64.c
+++ b/arch/powerpc/mm/hash_utils_64.c
@@ -92,10 +92,15 @@ unsigned long htab_size_bytes;
 unsigned long htab_hash_mask;
 int mmu_linear_psize = MMU_PAGE_4K;
 int mmu_virtual_psize = MMU_PAGE_4K;
+int mmu_vmalloc_psize = MMU_PAGE_4K;
+int mmu_io_psize = MMU_PAGE_4K;
 #ifdef CONFIG_HUGETLB_PAGE
 int mmu_huge_psize = MMU_PAGE_16M;
 unsigned int HPAGE_SHIFT;
 #endif
+#ifdef CONFIG_PPC_64K_PAGES
+int mmu_ci_restrictions;
+#endif
 
 /* There are definitions of page sizes arrays to be used when none
  * is provided by the firmware.
@@ -308,20 +313,31 @@ static void __init htab_init_page_sizes(
 	else if (mmu_psize_defs[MMU_PAGE_1M].shift)
 		mmu_linear_psize = MMU_PAGE_1M;
 
+#ifdef CONFIG_PPC_64K_PAGES
 	/*
 	 * Pick a size for the ordinary pages. Default is 4K, we support
-	 * 64K if cache inhibited large pages are supported by the
-	 * processor
+	 * 64K for user mappings and vmalloc if supported by the processor.
+	 * We only use 64k for ioremap if the processor
+	 * (and firmware) support cache-inhibited large pages.
+	 * If not, we use 4k and set mmu_ci_restrictions so that
+	 * hash_page knows to switch processes that use cache-inhibited
+	 * mappings to 4k pages.
 	 */
-#ifdef CONFIG_PPC_64K_PAGES
-	if (mmu_psize_defs[MMU_PAGE_64K].shift &&
-	    cpu_has_feature(CPU_FTR_CI_LARGE_PAGE))
+	if (mmu_psize_defs[MMU_PAGE_64K].shift) {
 		mmu_virtual_psize = MMU_PAGE_64K;
+		mmu_vmalloc_psize = MMU_PAGE_64K;
+		if (cpu_has_feature(CPU_FTR_CI_LARGE_PAGE))
+			mmu_io_psize = MMU_PAGE_64K;
+		else
+			mmu_ci_restrictions = 1;
+	}
 #endif
 
-	printk(KERN_DEBUG "Page orders: linear mapping = %d, others = %d\n",
+	printk(KERN_DEBUG "Page orders: linear mapping = %d, "
+	       "virtual = %d, io = %d\n",
 	       mmu_psize_defs[mmu_linear_psize].shift,
-	       mmu_psize_defs[mmu_virtual_psize].shift);
+	       mmu_psize_defs[mmu_virtual_psize].shift,
+	       mmu_psize_defs[mmu_io_psize].shift);
 
 #ifdef CONFIG_HUGETLB_PAGE
 	/* Init large page size. Currently, we pick 16M or 1M depending
@@ -556,6 +572,7 @@ int hash_page(unsigned long ea, unsigned
 	pte_t *ptep;
 	cpumask_t tmp;
 	int rc, user_region = 0, local = 0;
+	int psize;
 
 	DBG_LOW("hash_page(ea=%016lx, access=%lx, trap=%lx\n",
 		ea, access, trap);
@@ -575,10 +592,15 @@ int hash_page(unsigned long ea, unsigned
 			return 1;
 		}
 		vsid = get_vsid(mm->context.id, ea);
+		psize = mm->context.user_psize;
 		break;
 	case VMALLOC_REGION_ID:
 		mm = &init_mm;
 		vsid = get_kernel_vsid(ea);
+		if (ea < VMALLOC_END)
+			psize = mmu_vmalloc_psize;
+		else
+			psize = mmu_io_psize;
 		break;
 	default:
 		/* Not a valid range
@@ -629,7 +651,40 @@ #endif
 #ifndef CONFIG_PPC_64K_PAGES
 	rc = __hash_page_4K(ea, access, vsid, ptep, trap, local);
 #else
-	if (mmu_virtual_psize == MMU_PAGE_64K)
+	if (mmu_ci_restrictions) {
+		/* If this PTE is non-cacheable, switch to 4k */
+		if (psize == MMU_PAGE_64K &&
+		    (pte_val(*ptep) & _PAGE_NO_CACHE)) {
+			if (user_region) {
+				psize = MMU_PAGE_4K;
+				mm->context.user_psize = MMU_PAGE_4K;
+				mm->context.sllp = SLB_VSID_USER |
+					mmu_psize_defs[MMU_PAGE_4K].sllp;
+			} else if (ea < VMALLOC_END) {
+				/*
+				 * some driver did a non-cacheable mapping
+				 * in vmalloc space, so switch vmalloc
+				 * to 4k pages
+				 */
+				printk(KERN_ALERT "Reducing vmalloc segment "
+				       "to 4kB pages because of "
+				       "non-cacheable mapping\n");
+				psize = mmu_vmalloc_psize = MMU_PAGE_4K;
+			}
+		}
+		if (user_region) {
+			if (psize != get_paca()->context.user_psize) {
+				get_paca()->context = mm->context;
+				slb_flush_and_rebolt();
+			}
+		} else if (get_paca()->vmalloc_sllp !=
+			   mmu_psize_defs[mmu_vmalloc_psize].sllp) {
+			get_paca()->vmalloc_sllp =
+				mmu_psize_defs[mmu_vmalloc_psize].sllp;
+			slb_flush_and_rebolt();
+		}
+	}
+	if (psize == MMU_PAGE_64K)
 		rc = __hash_page_64K(ea, access, vsid, ptep, trap, local);
 	else
 		rc = __hash_page_4K(ea, access, vsid, ptep, trap, local);
@@ -681,7 +736,18 @@ void hash_preload(struct mm_struct *mm, 
 #ifndef CONFIG_PPC_64K_PAGES
 	__hash_page_4K(ea, access, vsid, ptep, trap, local);
 #else
-	if (mmu_virtual_psize == MMU_PAGE_64K)
+	if (mmu_ci_restrictions) {
+		/* If this PTE is non-cacheable, switch to 4k */
+		if (mm->context.user_psize == MMU_PAGE_64K &&
+		    (pte_val(*ptep) & _PAGE_NO_CACHE)) {
+			mm->context.user_psize = MMU_PAGE_4K;
+			mm->context.sllp = SLB_VSID_USER |
+				mmu_psize_defs[MMU_PAGE_4K].sllp;
+			get_paca()->context = mm->context;
+			slb_flush_and_rebolt();
+		}
+	}
+	if (mm->context.user_psize == MMU_PAGE_64K)
 		__hash_page_64K(ea, access, vsid, ptep, trap, local);
 	else
 		__hash_page_4K(ea, access, vsid, ptep, trap, local);
diff --git a/arch/powerpc/mm/mmu_context_64.c b/arch/powerpc/mm/mmu_context_64.c
index 714a84d..65d18dc 100644
--- a/arch/powerpc/mm/mmu_context_64.c
+++ b/arch/powerpc/mm/mmu_context_64.c
@@ -49,6 +49,9 @@ again:
 	}
 
 	mm->context.id = index;
+	mm->context.user_psize = mmu_virtual_psize;
+	mm->context.sllp = SLB_VSID_USER |
+		mmu_psize_defs[mmu_virtual_psize].sllp;
 
 	return 0;
 }
diff --git a/arch/powerpc/mm/slb.c b/arch/powerpc/mm/slb.c
index 2cc6173..6a8bf6c 100644
--- a/arch/powerpc/mm/slb.c
+++ b/arch/powerpc/mm/slb.c
@@ -60,19 +60,19 @@ static inline void create_slbe(unsigned 
 		     : "memory" );
 }
 
-static void slb_flush_and_rebolt(void)
+void slb_flush_and_rebolt(void)
 {
 	/* If you change this make sure you change SLB_NUM_BOLTED
 	 * appropriately too. */
-	unsigned long linear_llp, virtual_llp, lflags, vflags;
+	unsigned long linear_llp, vmalloc_llp, lflags, vflags;
 	unsigned long ksp_esid_data;
 
 	WARN_ON(!irqs_disabled());
 
 	linear_llp = mmu_psize_defs[mmu_linear_psize].sllp;
-	virtual_llp = mmu_psize_defs[mmu_virtual_psize].sllp;
+	vmalloc_llp = mmu_psize_defs[mmu_vmalloc_psize].sllp;
 	lflags = SLB_VSID_KERNEL | linear_llp;
-	vflags = SLB_VSID_KERNEL | virtual_llp;
+	vflags = SLB_VSID_KERNEL | vmalloc_llp;
 
 	ksp_esid_data = mk_esid_data(get_paca()->kstack, 2);
 	if ((ksp_esid_data & ESID_MASK) == PAGE_OFFSET)
@@ -164,11 +164,10 @@ static inline void patch_slb_encoding(un
 
 void slb_initialize(void)
 {
-	unsigned long linear_llp, virtual_llp;
+	unsigned long linear_llp, vmalloc_llp, io_llp;
 	static int slb_encoding_inited;
 	extern unsigned int *slb_miss_kernel_load_linear;
-	extern unsigned int *slb_miss_kernel_load_virtual;
-	extern unsigned int *slb_miss_user_load_normal;
+	extern unsigned int *slb_miss_kernel_load_io;
 #ifdef CONFIG_HUGETLB_PAGE
 	extern unsigned int *slb_miss_user_load_huge;
 	unsigned long huge_llp;
@@ -178,18 +177,19 @@ #endif
 
 	/* Prepare our SLB miss handler based on our page size */
 	linear_llp = mmu_psize_defs[mmu_linear_psize].sllp;
-	virtual_llp = mmu_psize_defs[mmu_virtual_psize].sllp;
+	io_llp = mmu_psize_defs[mmu_io_psize].sllp;
+	vmalloc_llp = mmu_psize_defs[mmu_vmalloc_psize].sllp;
+	get_paca()->vmalloc_sllp = SLB_VSID_KERNEL | vmalloc_llp;
+
 	if (!slb_encoding_inited) {
 		slb_encoding_inited = 1;
 		patch_slb_encoding(slb_miss_kernel_load_linear,
 				   SLB_VSID_KERNEL | linear_llp);
-		patch_slb_encoding(slb_miss_kernel_load_virtual,
-				   SLB_VSID_KERNEL | virtual_llp);
-		patch_slb_encoding(slb_miss_user_load_normal,
-				   SLB_VSID_USER | virtual_llp);
+		patch_slb_encoding(slb_miss_kernel_load_io,
+				   SLB_VSID_KERNEL | io_llp);
 
 		DBG("SLB: linear  LLP = %04x\n", linear_llp);
-		DBG("SLB: virtual LLP = %04x\n", virtual_llp);
+		DBG("SLB: io      LLP = %04x\n", io_llp);
 #ifdef CONFIG_HUGETLB_PAGE
 		patch_slb_encoding(slb_miss_user_load_huge,
 				   SLB_VSID_USER | huge_llp);
@@ -204,7 +204,7 @@ #ifndef CONFIG_PPC_ISERIES
 	unsigned long lflags, vflags;
 
 	lflags = SLB_VSID_KERNEL | linear_llp;
-	vflags = SLB_VSID_KERNEL | virtual_llp;
+	vflags = SLB_VSID_KERNEL | vmalloc_llp;
 
 	/* Invalidate the entire SLB (even slot 0) & all the ERATS */
 	asm volatile("isync":::"memory");
@@ -212,7 +212,6 @@ #ifndef CONFIG_PPC_ISERIES
 	asm volatile("isync; slbia; isync":::"memory");
 	create_slbe(PAGE_OFFSET, lflags, 0);
 
-	/* VMALLOC space has 4K pages always for now */
 	create_slbe(VMALLOC_START, vflags, 1);
 
 	/* We don't bolt the stack for the time being - we're in boot,
diff --git a/arch/powerpc/mm/slb_low.S b/arch/powerpc/mm/slb_low.S
index abfaabf..8548dcf 100644
--- a/arch/powerpc/mm/slb_low.S
+++ b/arch/powerpc/mm/slb_low.S
@@ -59,10 +59,19 @@ _GLOBAL(slb_miss_kernel_load_linear)
 	li	r11,0
 	b	slb_finish_load
 
-1:	/* vmalloc/ioremap mapping encoding bits, the "li" instruction below
+1:	/* vmalloc/ioremap mapping encoding bits, the "li" instructions below
 	 * will be patched by the kernel at boot
 	 */
-_GLOBAL(slb_miss_kernel_load_virtual)
+BEGIN_FTR_SECTION
+	/* check whether this is in vmalloc or ioremap space */
+	clrldi	r11,r10,48
+	cmpldi	r11,(VMALLOC_SIZE >> 28) - 1
+	bgt	5f
+	lhz	r11,PACAVMALLOCSLLP(r13)
+	b	slb_finish_load
+5:
+END_FTR_SECTION_IFCLR(CPU_FTR_CI_LARGE_PAGE)
+_GLOBAL(slb_miss_kernel_load_io)
 	li	r11,0
 	b	slb_finish_load
 
@@ -96,9 +105,7 @@ _GLOBAL(slb_miss_user_load_huge)
 1:
 #endif /* CONFIG_HUGETLB_PAGE */
 
-_GLOBAL(slb_miss_user_load_normal)
-	li	r11,0
-
+	lhz	r11,PACACONTEXTSLLP(r13)
 2:
 	ld	r9,PACACONTEXTID(r13)
 	rldimi	r10,r9,USER_ESID_BITS,0
diff --git a/arch/powerpc/mm/stab.c b/arch/powerpc/mm/stab.c
diff --git a/arch/powerpc/mm/tlb_64.c b/arch/powerpc/mm/tlb_64.c
index f734b11..4bcfd45 100644
--- a/arch/powerpc/mm/tlb_64.c
+++ b/arch/powerpc/mm/tlb_64.c
@@ -131,7 +131,7 @@ void hpte_update(struct mm_struct *mm, u
 {
 	struct ppc64_tlb_batch *batch = &__get_cpu_var(ppc64_tlb_batch);
 	unsigned long vsid;
-	unsigned int psize = mmu_virtual_psize;
+	unsigned int psize;
 	int i;
 
 	i = batch->index;
@@ -148,7 +148,8 @@ #ifdef CONFIG_HUGETLB_PAGE
 #else
 		BUG();
 #endif
-	}
+	} else
+		psize = (pte & _PAGE_COMBO)? MMU_PAGE_4K: MMU_PAGE_64K;
 
 	/*
 	 * This can happen when we are in the middle of a TLB batch and
diff --git a/include/asm-powerpc/mmu.h b/include/asm-powerpc/mmu.h
index 8853974..3a5ebe2 100644
--- a/include/asm-powerpc/mmu.h
+++ b/include/asm-powerpc/mmu.h
@@ -165,6 +165,16 @@ #ifndef __ASSEMBLY__
 extern struct mmu_psize_def mmu_psize_defs[MMU_PAGE_COUNT];
 extern int mmu_linear_psize;
 extern int mmu_virtual_psize;
+extern int mmu_vmalloc_psize;
+extern int mmu_io_psize;
+
+/*
+ * If the processor supports 64k normal pages but not 64k cache
+ * inhibited pages, we have to be prepared to switch processes
+ * to use 4k pages when they create cache-inhibited mappings.
+ * If this is the case, mmu_ci_restrictions will be set to 1.
+ */
+extern int mmu_ci_restrictions;
 
 #ifdef CONFIG_HUGETLB_PAGE
 /*
@@ -256,6 +266,7 @@ extern long iSeries_hpte_insert(unsigned
 
 extern void stabs_alloc(void);
 extern void slb_initialize(void);
+extern void slb_flush_and_rebolt(void);
 extern void stab_initialize(unsigned long stab);
 
 #endif /* __ASSEMBLY__ */
@@ -359,6 +370,8 @@ typedef unsigned long mm_context_id_t;
 
 typedef struct {
 	mm_context_id_t id;
+	u16 user_psize;			/* page size index */
+	u16 sllp;			/* SLB entry page size encoding */
 #ifdef CONFIG_HUGETLB_PAGE
 	u16 low_htlb_areas, high_htlb_areas;
 #endif
diff --git a/include/asm-powerpc/mmu_context.h b/include/asm-powerpc/mmu_context.h
diff --git a/include/asm-powerpc/paca.h b/include/asm-powerpc/paca.h
index c17fd54..1740635 100644
--- a/include/asm-powerpc/paca.h
+++ b/include/asm-powerpc/paca.h
@@ -81,6 +81,7 @@ #endif /* CONFIG_PPC_ISERIES */
  				 * on the linear mapping */
 
 	mm_context_t context;
+	u16 vmalloc_sllp;
 	u16 slb_cache[SLB_CACHE_ENTRIES];
 	u16 slb_cache_ptr;
 
diff --git a/include/asm-powerpc/pgtable.h b/include/asm-powerpc/pgtable.h
index e9f1f46..260a0fa 100644
--- a/include/asm-powerpc/pgtable.h
+++ b/include/asm-powerpc/pgtable.h
@@ -47,8 +47,8 @@ #endif
 /*
  * Define the address range of the vmalloc VM area.
  */
-#define VMALLOC_START (0xD000000000000000ul)
-#define VMALLOC_SIZE  (0x80000000000UL)
+#define VMALLOC_START ASM_CONST(0xD000000000000000)
+#define VMALLOC_SIZE  ASM_CONST(0x80000000000)
 #define VMALLOC_END   (VMALLOC_START + VMALLOC_SIZE)
 
 /*
@@ -413,12 +413,6 @@ static inline void set_pte_at(struct mm_
 		flush_tlb_pending();
 	}
 	pte = __pte(pte_val(pte) & ~_PAGE_HPTEFLAGS);
-
-#ifdef CONFIG_PPC_64K_PAGES
-	if (mmu_virtual_psize != MMU_PAGE_64K)
-		pte = __pte(pte_val(pte) | _PAGE_COMBO);
-#endif /* CONFIG_PPC_64K_PAGES */
-
 	*ptep = pte;
 }
 

^ permalink raw reply related

* RE: [PATCH/2.6.17-rc4 4/10]Powerpc:  Add tsi108 pic support
From: Benjamin Herrenschmidt @ 2006-06-13  2:09 UTC (permalink / raw)
  To: Zang Roy-r61911
  Cc: Alexandre Bounine, Yang Xin-Xin-r48390, Paul Mackerras,
	linuxppc-dev list
In-Reply-To: <9FCDBA58F226D911B202000BDBAD467306907E1B@zch01exm40.ap.freescale.net>

On Fri, 2006-06-09 at 17:25 +0800, Zang Roy-r61911 wrote:

> Any comment?

Not right now. We'll have to rework this a bit on top of my current
changes to the powerpc interrupt stuff. I'll post patches soon.

Ben.

^ permalink raw reply

* [PATCH] powerpc: check firmware state before suspending
From: Dave C Boutcher @ 2006-06-13  0:49 UTC (permalink / raw)
  To: linuxppc64-dev


Currently the kernel blindly halts all the processors and calls the
ibm,suspend-me rtas call.  If the firmware is not in the correct
state, we then re-start all the processors and return.  It is much
smarter to first check the firmware state, and only if it is waiting,
call the ibm,suspend-me call.

---

 arch/powerpc/kernel/rtas.c   |   22 ++++++++++++++++++++++
 include/asm-powerpc/hvcall.h |   10 ++++++++++
 include/asm-powerpc/rtas.h   |    1 +
 3 files changed, 33 insertions(+), 0 deletions(-)

579349d70b1395df6a139a0d1bc66b55ebc7a050
diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c
index 13496f3..fd15e3e 100644
--- a/arch/powerpc/kernel/rtas.c
+++ b/arch/powerpc/kernel/rtas.c
@@ -593,9 +593,31 @@ out:
 static int rtas_ibm_suspend_me(struct rtas_args *args)
 {
 	int i;
+	long state;
+	long rc;
+	unsigned long dummy;
 
 	struct rtas_suspend_me_data data;
 
+	/* Make sure the state is valid */
+	rc = plpar_hcall(H_VASI_STATE,
+			 ((u64)args->args[0] << 32) | args->args[1],
+			 0, 0, 0,
+			 &state, &dummy, &dummy);
+
+	if (rc) {
+		printk(KERN_ERR "rtas_ibm_suspend_me: vasi_state returned %ld\n",rc);
+		return rc;
+	} else if (state == H_VASI_ENABLED) {
+		args->args[args->nargs] = RTAS_NOT_SUSPENDABLE;
+		return 0;
+	} else if (state != H_VASI_SUSPENDING) {
+		printk(KERN_ERR "rtas_ibm_suspend_me: vasi_state returned state %ld\n",
+		       state);
+		args->args[args->nargs] = -1;
+		return 0;
+	}
+
 	data.waiting = 1;
 	data.args = args;
 
diff --git a/include/asm-powerpc/hvcall.h b/include/asm-powerpc/hvcall.h
index 6cc7e1f..0d3c4e8 100644
--- a/include/asm-powerpc/hvcall.h
+++ b/include/asm-powerpc/hvcall.h
@@ -102,6 +102,15 @@
 #define H_PP1			(1UL<<(63-62))
 #define H_PP2			(1UL<<(63-63))
 
+/* VASI States */
+#define H_VASI_INVALID          0
+#define H_VASI_ENABLED          1
+#define H_VASI_ABORTED          2
+#define H_VASI_SUSPENDING       3
+#define H_VASI_SUSPENDED        4
+#define H_VASI_RESUMED          5
+#define H_VASI_COMPLETED        6
+
 /* DABRX flags */
 #define H_DABRX_HYPERVISOR	(1UL<<(63-61))
 #define H_DABRX_KERNEL		(1UL<<(63-62))
@@ -190,6 +199,7 @@
 #define H_QUERY_INT_STATE       0x1E4
 #define H_POLL_PENDING		0x1D8
 #define H_JOIN			0x298
+#define H_VASI_STATE            0x2A4
 #define H_ENABLE_CRQ		0x2B0
 
 #ifndef __ASSEMBLY__
diff --git a/include/asm-powerpc/rtas.h b/include/asm-powerpc/rtas.h
index a3b4e55..02e213e 100644
--- a/include/asm-powerpc/rtas.h
+++ b/include/asm-powerpc/rtas.h
@@ -24,6 +24,7 @@
 #define RTAS_RMOBUF_MAX (64 * 1024)
 
 /* RTAS return status codes */
+#define RTAS_NOT_SUSPENDABLE	-9004
 #define RTAS_BUSY		-2    /* RTAS Busy */
 #define RTAS_EXTENDED_DELAY_MIN	9900
 #define RTAS_EXTENDED_DELAY_MAX	9905
-- 
1.2.4.gc98264

^ permalink raw reply related

* Re: How to determine IRQ w/ PPC045 (Virtex II Pro)?
From: Grant Likely @ 2006-06-12 20:03 UTC (permalink / raw)
  To: Chris Dumoulin; +Cc: linuxppc-embedded
In-Reply-To: <448DC718.1010700@ics-ltd.com>

On 6/12/06, Chris Dumoulin <cdumoulin@ics-ltd.com> wrote:
> When writing device drivers for a 2.6 linux kernel running on a PPC405,
> in a Virtex II Pro, how do I determine which IRQ to use in my call to
> request_irq? I imagine that the IRQ will be directly dependent on how my
> device's interrupt is connected to the interrupt controller I'm using,
> but I don't know how to figure out the IRQ number. Any help would be
> appreciated.

IRQ attachments are provided as #defines in the xparameters.h file
generated by Xilinx EDK.

g.

-- 
Grant Likely, B.Sc. P.Eng.
Secret Lab Technologies Ltd.
grant.likely@secretlab.ca
(403) 399-0195

^ permalink raw reply

* How to determine IRQ w/ PPC045 (Virtex II Pro)?
From: Chris Dumoulin @ 2006-06-12 19:57 UTC (permalink / raw)
  To: linuxppc-embedded

When writing device drivers for a 2.6 linux kernel running on a PPC405, 
in a Virtex II Pro, how do I determine which IRQ to use in my call to 
request_irq? I imagine that the IRQ will be directly dependent on how my 
device's interrupt is connected to the interrupt controller I'm using, 
but I don't know how to figure out the IRQ number. Any help would be 
appreciated.

Regards,
Chris Dumoulin
-- 
*--Christopher Dumoulin--*
Software Team Leader

<http://ics-ltd.com/>
<http://ics-ltd.com/>

Interactive Circuits and Systems Ltd.
5430 Canotek Road
Ottawa, ON
K1J 9G2
(613)749-9241
1-800-267-9794 (USA only)

------------------------------------------------------------------------
This e-mail is private and confidential and is for the addressee only. 
If misdirected, please notify us by telephone and confirm that it has 
been deleted from your system and any hard copies destroyed. You are 
strictly prohibited from using, printing, distributing or disseminating 
it or any information contained in it save to the intended recipient.

^ permalink raw reply

* Re: Linux 2.4 Kernel on Xilinx Virtex4 FX100's PPC
From: Stephen Telfer @ 2006-06-12 16:57 UTC (permalink / raw)
  To: linuxppc-embedded
In-Reply-To: <447F2C0E.1000306@xilinx.com>

I have been working on an FX60 part with PVR 0x20011430.  It has memory 
attached to the PLB.  I have noticed some possible corruptions during 
linux kernel boot that appear to follow a pattern.  If a variable is 
stored immediately before a function call is made or returns, the 
callee/caller when reading the value gets stale data.  Print the value 
of the variable in-between and coherency returns.

Does that sound like a known problem?  I went through the errata on 
solution record 20658 but nothing looked like a good fit.  So I'm 
assuming at the moment it's some stupid bug of my own...

Any suggestions gratefully appreciated.

Regards,
Stig Telfer


On 1 Jun 2006, at 19:03, Peter Ryser wrote:

> It's a little bit more complicated than that but your statement is
> basically correct.
>
> - Peter
>
>
> Grant Likely wrote:
>
>> On 6/1/06, Peter Ryser <peter.ryser@xilinx.com> wrote:
>>
>>> There are some silicon issues on the PPC405 in V4 with PVR 0x20011430
>>> which are documented in Xilinx solution record 20658. All these 
>>> issues
>>> are fixed in silicon where the PPC405 has a PVR of 0x20011470.
>>>
>>> Said that it's not true that the caches cannot be used in silicon 
>>> with
>>> PVR 0x20011430. The problem is a corner case which does not show in
>>> typical designs.
>>
>>
>> If I understand correctly, the cache issue only shows up with RAM
>> attached to the OPB (instead of PLB).  Is that correct?
>>
>> Cheers,
>> g.
>>
>>
>
>
>
> _______________________________________________
> Linuxppc-embedded mailing list
> Linuxppc-embedded@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-embedded

^ permalink raw reply

* Re: [PATCH] reorg RTAS delay code
From: John Rose @ 2006-06-12 16:18 UTC (permalink / raw)
  To: Anton Blanchard; +Cc: Paul Mackerras, Nathan Lynch, External List
In-Reply-To: <20060610020805.GG23891@krispykreme>


This patch attempts to handle RTAS "busy" return codes in a more simple
and consistent manner.  Typical callers of RTAS shouldn't have to
manage wait times and delay calls.

This patch also changes the kernel to use msleep() rather than udelay()
when a runtime delay is necessary.  This will avoid CPU soft lockups
for extended delay conditions.

Signed-off-by: John Rose <johnrose@austin.ibm.com>

---

Resend - Addressed module build breaks.  Thanks Anton.

Thanks-
John

---

 2_6_linus-johnrose/arch/powerpc/kernel/rtas-rtc.c           |   30 ++--
 2_6_linus-johnrose/arch/powerpc/kernel/rtas.c               |   86 +++++-------
 2_6_linus-johnrose/arch/powerpc/kernel/rtas_flash.c         |   25 ---
 2_6_linus-johnrose/arch/powerpc/platforms/pseries/scanlog.c |    6 
 2_6_linus-johnrose/include/asm-powerpc/rtas.h               |    8 -
 5 files changed, 61 insertions(+), 94 deletions(-)

diff -puN arch/powerpc/kernel/rtas-rtc.c~rtas_delay_reorg arch/powerpc/kernel/rtas-rtc.c
--- 2_6_linus/arch/powerpc/kernel/rtas-rtc.c~rtas_delay_reorg	2006-06-12 11:21:29.000000000 -0500
+++ 2_6_linus-johnrose/arch/powerpc/kernel/rtas-rtc.c	2006-06-12 11:21:29.000000000 -0500
@@ -14,19 +14,20 @@
 unsigned long __init rtas_get_boot_time(void)
 {
 	int ret[8];
-	int error, wait_time;
+	int error;
+	unsigned int wait_time;
 	u64 max_wait_tb;
 
 	max_wait_tb = get_tb() + tb_ticks_per_usec * 1000 * MAX_RTC_WAIT;
 	do {
 		error = rtas_call(rtas_token("get-time-of-day"), 0, 8, ret);
-		if (error == RTAS_CLOCK_BUSY || rtas_is_extended_busy(error)) {
-			wait_time = rtas_extended_busy_delay_time(error);
+
+		wait_time = rtas_busy_delay_time(error);
+		if (wait_time) {
 			/* This is boot time so we spin. */
 			udelay(wait_time*1000);
-			error = RTAS_CLOCK_BUSY;
 		}
-	} while (error == RTAS_CLOCK_BUSY && (get_tb() < max_wait_tb));
+	} while (wait_time && (get_tb() < max_wait_tb));
 
 	if (error != 0 && printk_ratelimit()) {
 		printk(KERN_WARNING "error: reading the clock failed (%d)\n",
@@ -44,24 +45,25 @@ unsigned long __init rtas_get_boot_time(
 void rtas_get_rtc_time(struct rtc_time *rtc_tm)
 {
         int ret[8];
-	int error, wait_time;
+	int error;
+	unsigned int wait_time;
 	u64 max_wait_tb;
 
 	max_wait_tb = get_tb() + tb_ticks_per_usec * 1000 * MAX_RTC_WAIT;
 	do {
 		error = rtas_call(rtas_token("get-time-of-day"), 0, 8, ret);
-		if (error == RTAS_CLOCK_BUSY || rtas_is_extended_busy(error)) {
+
+		wait_time = rtas_busy_delay_time(error);
+		if (wait_time) {
 			if (in_interrupt() && printk_ratelimit()) {
 				memset(rtc_tm, 0, sizeof(struct rtc_time));
 				printk(KERN_WARNING "error: reading clock"
 				       " would delay interrupt\n");
 				return;	/* delay not allowed */
 			}
-			wait_time = rtas_extended_busy_delay_time(error);
 			msleep(wait_time);
-			error = RTAS_CLOCK_BUSY;
 		}
-	} while (error == RTAS_CLOCK_BUSY && (get_tb() < max_wait_tb));
+	} while (wait_time && (get_tb() < max_wait_tb));
 
         if (error != 0 && printk_ratelimit()) {
                 printk(KERN_WARNING "error: reading the clock failed (%d)\n",
@@ -88,14 +90,14 @@ int rtas_set_rtc_time(struct rtc_time *t
 				  tm->tm_year + 1900, tm->tm_mon + 1,
 				  tm->tm_mday, tm->tm_hour, tm->tm_min,
 				  tm->tm_sec, 0);
-		if (error == RTAS_CLOCK_BUSY || rtas_is_extended_busy(error)) {
+
+		wait_time = rtas_busy_delay_time(error);
+		if (wait_time) {
 			if (in_interrupt())
 				return 1;	/* probably decrementer */
-			wait_time = rtas_extended_busy_delay_time(error);
 			msleep(wait_time);
-			error = RTAS_CLOCK_BUSY;
 		}
-	} while (error == RTAS_CLOCK_BUSY && (get_tb() < max_wait_tb));
+	} while (wait_time && (get_tb() < max_wait_tb));
 
         if (error != 0 && printk_ratelimit())
                 printk(KERN_WARNING "error: setting the clock failed (%d)\n",
diff -puN arch/powerpc/kernel/rtas.c~rtas_delay_reorg arch/powerpc/kernel/rtas.c
--- 2_6_linus/arch/powerpc/kernel/rtas.c~rtas_delay_reorg	2006-06-12 11:21:29.000000000 -0500
+++ 2_6_linus-johnrose/arch/powerpc/kernel/rtas.c	2006-06-12 11:21:29.000000000 -0500
@@ -370,24 +370,36 @@ int rtas_call(int token, int nargs, int 
 	return ret;
 }
 
-/* Given an RTAS status code of 990n compute the hinted delay of 10^n
- * (last digit) milliseconds.  For now we bound at n=5 (100 sec).
+/* For RTAS_BUSY (-2), delay for 1 millisecond.  For an extended busy status
+ * code of 990n, perform the hinted delay of 10^n (last digit) milliseconds.
  */
-unsigned int rtas_extended_busy_delay_time(int status)
+unsigned int rtas_busy_delay_time(int status)
 {
-	int order = status - 9900;
-	unsigned long ms;
+	int order;
+	unsigned int ms = 0;
 
-	if (order < 0)
-		order = 0;	/* RTC depends on this for -2 clock busy */
-	else if (order > 5)
-		order = 5;	/* bound */
+	if (status == RTAS_BUSY) {
+		ms = 1;
+	} else if (status >= 9900 && status <= 9905) {
+		order = status - 9900;
+		for (ms = 1; order > 0; order--)
+			ms *= 10;
+	}
+
+	return ms;
+}
+
+/* For an RTAS busy status code, perform the hinted delay. */
+unsigned int rtas_busy_delay(int status)
+{
+	unsigned int ms;
 
-	/* Use microseconds for reasonable accuracy */
-	for (ms = 1; order > 0; order--)
-		ms *= 10;
+	might_sleep();
+	ms = rtas_busy_delay_time(status);
+	if (ms)
+		msleep(ms);
 
-	return ms; 
+	return ms;
 }
 
 int rtas_error_rc(int rtas_rc)
@@ -438,22 +450,14 @@ int rtas_get_power_level(int powerdomain
 int rtas_set_power_level(int powerdomain, int level, int *setlevel)
 {
 	int token = rtas_token("set-power-level");
-	unsigned int wait_time;
 	int rc;
 
 	if (token == RTAS_UNKNOWN_SERVICE)
 		return -ENOENT;
 
-	while (1) {
+	do {
 		rc = rtas_call(token, 2, 2, setlevel, powerdomain, level);
-		if (rc == RTAS_BUSY)
-			udelay(1);
-		else if (rtas_is_extended_busy(rc)) {
-			wait_time = rtas_extended_busy_delay_time(rc);
-			udelay(wait_time * 1000);
-		} else
-			break;
-	}
+	} while (rtas_busy_delay(rc));
 
 	if (rc < 0)
 		return rtas_error_rc(rc);
@@ -463,22 +467,14 @@ int rtas_set_power_level(int powerdomain
 int rtas_get_sensor(int sensor, int index, int *state)
 {
 	int token = rtas_token("get-sensor-state");
-	unsigned int wait_time;
 	int rc;
 
 	if (token == RTAS_UNKNOWN_SERVICE)
 		return -ENOENT;
 
-	while (1) {
+	do {
 		rc = rtas_call(token, 2, 2, state, sensor, index);
-		if (rc == RTAS_BUSY)
-			udelay(1);
-		else if (rtas_is_extended_busy(rc)) {
-			wait_time = rtas_extended_busy_delay_time(rc);
-			udelay(wait_time * 1000);
-		} else
-			break;
-	}
+	} while (rtas_busy_delay(rc));
 
 	if (rc < 0)
 		return rtas_error_rc(rc);
@@ -488,23 +484,14 @@ int rtas_get_sensor(int sensor, int inde
 int rtas_set_indicator(int indicator, int index, int new_value)
 {
 	int token = rtas_token("set-indicator");
-	unsigned int wait_time;
 	int rc;
 
 	if (token == RTAS_UNKNOWN_SERVICE)
 		return -ENOENT;
 
-	while (1) {
+	do {
 		rc = rtas_call(token, 3, 1, NULL, indicator, index, new_value);
-		if (rc == RTAS_BUSY)
-			udelay(1);
-		else if (rtas_is_extended_busy(rc)) {
-			wait_time = rtas_extended_busy_delay_time(rc);
-			udelay(wait_time * 1000);
-		}
-		else
-			break;
-	}
+	} while (rtas_busy_delay(rc));
 
 	if (rc < 0)
 		return rtas_error_rc(rc);
@@ -555,13 +542,11 @@ void rtas_os_term(char *str)
 	do {
 		status = rtas_call(rtas_token("ibm,os-term"), 1, 1, NULL,
 				   __pa(rtas_os_term_buf));
+	} while (rtas_busy_delay(status));
 
-		if (status == RTAS_BUSY)
-			udelay(1);
-		else if (status != 0)
-			printk(KERN_EMERG "ibm,os-term call failed %d\n",
+	if (status != 0)
+		printk(KERN_EMERG "ibm,os-term call failed %d\n",
 			       status);
-	} while (status == RTAS_BUSY);
 }
 
 static int ibm_suspend_me_token = RTAS_UNKNOWN_SERVICE;
@@ -789,7 +774,8 @@ EXPORT_SYMBOL(rtas_token);
 EXPORT_SYMBOL(rtas_call);
 EXPORT_SYMBOL(rtas_data_buf);
 EXPORT_SYMBOL(rtas_data_buf_lock);
-EXPORT_SYMBOL(rtas_extended_busy_delay_time);
+EXPORT_SYMBOL(rtas_busy_delay);
+EXPORT_SYMBOL(rtas_busy_delay_time);
 EXPORT_SYMBOL(rtas_get_sensor);
 EXPORT_SYMBOL(rtas_get_power_level);
 EXPORT_SYMBOL(rtas_set_power_level);
diff -puN arch/powerpc/kernel/rtas_flash.c~rtas_delay_reorg arch/powerpc/kernel/rtas_flash.c
--- 2_6_linus/arch/powerpc/kernel/rtas_flash.c~rtas_delay_reorg	2006-06-12 11:21:29.000000000 -0500
+++ 2_6_linus-johnrose/arch/powerpc/kernel/rtas_flash.c	2006-06-12 11:21:29.000000000 -0500
@@ -365,20 +365,12 @@ static int rtas_excl_release(struct inod
 
 static void manage_flash(struct rtas_manage_flash_t *args_buf)
 {
-	unsigned int wait_time;
 	s32 rc;
 
-	while (1) {
+	do {
 		rc = rtas_call(rtas_token("ibm,manage-flash-image"), 1, 
 			       1, NULL, args_buf->op);
-		if (rc == RTAS_RC_BUSY)
-			udelay(1);
-		else if (rtas_is_extended_busy(rc)) {
-			wait_time = rtas_extended_busy_delay_time(rc);
-			udelay(wait_time * 1000);
-		} else
-			break;
-	}
+	} while (rtas_busy_delay(rc));
 
 	args_buf->status = rc;
 }
@@ -451,27 +443,18 @@ static ssize_t manage_flash_write(struct
 static void validate_flash(struct rtas_validate_flash_t *args_buf)
 {
 	int token = rtas_token("ibm,validate-flash-image");
-	unsigned int wait_time;
 	int update_results;
 	s32 rc;	
 
 	rc = 0;
-	while(1) {
+	do {
 		spin_lock(&rtas_data_buf_lock);
 		memcpy(rtas_data_buf, args_buf->buf, VALIDATE_BUF_SIZE);
 		rc = rtas_call(token, 2, 2, &update_results, 
 			       (u32) __pa(rtas_data_buf), args_buf->buf_size);
 		memcpy(args_buf->buf, rtas_data_buf, VALIDATE_BUF_SIZE);
 		spin_unlock(&rtas_data_buf_lock);
-			
-		if (rc == RTAS_RC_BUSY)
-			udelay(1);
-		else if (rtas_is_extended_busy(rc)) {
-			wait_time = rtas_extended_busy_delay_time(rc);
-			udelay(wait_time * 1000);
-		} else
-			break;
-	}
+	} while (rtas_busy_delay(rc));
 
 	args_buf->status = rc;
 	args_buf->update_results = update_results;
diff -puN include/asm-powerpc/rtas.h~rtas_delay_reorg include/asm-powerpc/rtas.h
--- 2_6_linus/include/asm-powerpc/rtas.h~rtas_delay_reorg	2006-06-12 11:21:29.000000000 -0500
+++ 2_6_linus-johnrose/include/asm-powerpc/rtas.h	2006-06-12 11:21:29.000000000 -0500
@@ -177,12 +177,8 @@ extern unsigned long rtas_get_boot_time(
 extern void rtas_get_rtc_time(struct rtc_time *rtc_time);
 extern int rtas_set_rtc_time(struct rtc_time *rtc_time);
 
-/* Given an RTAS status code of 9900..9905 compute the hinted delay */
-unsigned int rtas_extended_busy_delay_time(int status);
-static inline int rtas_is_extended_busy(int status)
-{
-	return status >= 9900 && status <= 9909;
-}
+extern unsigned int rtas_busy_delay_time(int status);
+extern unsigned int rtas_busy_delay(int status);
 
 extern void pSeries_log_error(char *buf, unsigned int err_type, int fatal);
 
diff -puN arch/powerpc/platforms/pseries/scanlog.c~rtas_delay_reorg arch/powerpc/platforms/pseries/scanlog.c
--- 2_6_linus/arch/powerpc/platforms/pseries/scanlog.c~rtas_delay_reorg	2006-06-12 11:21:29.000000000 -0500
+++ 2_6_linus-johnrose/arch/powerpc/platforms/pseries/scanlog.c	2006-06-12 11:21:29.000000000 -0500
@@ -107,9 +107,9 @@ static ssize_t scanlog_read(struct file 
 			/* Break to sleep default time */
 			break;
 		    default:
-			if (status > 9900 && status <= 9905) {
-				wait_time = rtas_extended_busy_delay_time(status);
-			} else {
+			/* Assume extended busy */
+			wait_time = rtas_busy_delay_time(status);
+			if (!wait_time) {
 				printk(KERN_ERR "scanlog: unknown error from rtas: %d\n", status);
 				return -EIO;
 			}

_

^ permalink raw reply

* Re: [PATCH] yaboot: enable boot from iscsi target via ethernet devices on js20.
From: Paul Nasrat @ 2006-06-12 14:46 UTC (permalink / raw)
  To: Doug Maxey; +Cc: yaboot-devel, Linux PowerPC List
In-Reply-To: <200606012056.k51KuUOP008931@falcon10.austin.ibm.com>

On Thu, 2006-06-01 at 15:56 -0500, Doug Maxey wrote:
> On Tue, 16 May 2006 15:40:30 CDT, Doug Maxey wrote:

> >>Is the , always guaranteed to be there - eg if I have boot
> >>eth1:iscsi,ISCSIARGS  won't this check fail.
> >
> >Yes, with the above command line this would fail. 
> >
> >My point of reference are the bindings that we cannot yet talk about 
> >here, yet.  The device args would always be followed by a comma.  I suppose 
> >that we could just reference the string "iscsi", but then some wag 
> >would want to create some other property that included "iscsi" as a 
> >substring.  Maybe append a comma?
> 
> Any preferences on this?  

Both are kind of messy

> I have another, more radical solution.  
> 
> Adding a parser that understands the full device path and that can 
> return the elements neatly packaged.  Film at 11. 

This sounds to be the better way to do things - and is probably more
generally useful for other node types. Do you have any ideas on this?

Paul

^ permalink raw reply

* Re: Re: RFC: dma_mmap_coherent() for powerpc/ppc architecture and ALSA?
From: Gerhard Pircher @ 2006-06-12 14:42 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: linuxppc-dev, rlrevell, alsa-devel, linux-kernel
In-Reply-To: <s5hbqsyd4uz.wl%tiwai@suse.de>


> -------- Original-Nachricht --------
> Datum: Mon, 12 Jun 2006 12:51:16 +0200
> Von: Takashi Iwai <tiwai@suse.de>
> An: Gerhard Pircher <gerhard_pircher@gmx.net>
> Betreff: Re: RFC: dma_mmap_coherent() for powerpc/ppc architecture and
> ALSA?
> 
> > Well, implementing the dma_mmap_coherent() function isn't the
> > problem, because it is already implemented for the ARM
> > architecture.
> 
> Actually, I wrote dma_coherent_mmap patch long time ago but it has
> been left forgotten.  The patch attached below seems applicable to
> 2.6.17 tree, but I'm not sure whether it still works properly.
> It's untested on most of architectures.

Thanks, that helps me a lot!

> > But as far as I understand this would require a rewrite of all the
> > ALSA drivers (or at least a rewrite of the ALSA's DMA helper
> > functions).
> 
> Yes.  The change of ALSA side has been also on my tree.  But it was
> still pending since I'm not satisfied with the design yet.
> If you're interested in it, let me know.  I'll post the patch.

Yes, please! Then I can test, if the dma_mmap_coherent() patch works on
my non cache coherent powerpc machine. Do you think the DMA Layer/ALSA patches will go upstream in one of the next ALSA/Linux kernel versions?

Thanks!

Gerhard

-- 


Der GMX SmartSurfer hilft bis zu 70% Ihrer Onlinekosten zu sparen!
Ideal für Modem und ISDN: http://www.gmx.net/de/go/smartsurfer

^ permalink raw reply

* Re: Jumbo ethernet frames on MVME6100
From: Brent Cook @ 2006-06-12 13:30 UTC (permalink / raw)
  To: linuxppc-embedded; +Cc: Martin, Tim
In-Reply-To: <821B2170E9E7F04FA38DF7EC21DE487105550413@VCAEXCH01.hq.corp.viasat.com>

On Friday 09 June 2006 19:57, Martin, Tim wrote:
> I'm using Linux 2.6.6 with the Motorola Computer Group patch for the
> MVME6100 available at
>
> https://mcg.motorola.com/cfm/templates/swDetails.cfm?PageID=704&Software
> ID=6&ProductID=202
>
> and compiling with GCC from ELDK 3.1.1, although using the userspace
> module utils (e.g. insmod) from ELDK 4.0.

> The patch includes support for the Marvel gigabit ethernet part on the
> board (MV64360?) which works for non-jumbo ethernet frames, but doesn't
> receive (or even generate errors) if I send jumbo-ethernet frames.  I
> haven't debugged this issue very long, but a cursory look through the
> driver hints that jumbo frame support should be on (the port config
> serial register PCSR has what I imagine to be okay bits).
>
> I'm ifconfiging the 2nd ethernet device with the command
> ifconfig eth1 mtu 9000 192.168.1.1
>
> I'm wondering if anyone else using an MVME6100 has gotten jumbo frame
> support to work?

I can say that the MV64x60 ethernet driver that got rolled into the mainline 
kernel around the 2.6.11/12 timeframe does support jumbo frames for send and 
receive. If that patch is anything like some patches I've seen for similar 
products (based on old Marvel/Galileo reference code), I'd say your best-off 
cherry-picking the HW-specific bits from the patch and trying to either 
forward-port/add from scratch the board support to something >= 2.6.12 and 
getting the improved MV64x60 support as a bonus, or backporting at least the 
newer MV ethernet driver. Not only is the newer one _much_ simpler and easier 
to read (like 3 or 4x shorter), but it would appear to work better as well.

> Also, at what point did the Linux 2.6 kernel embedded PPC support start
> working with GCC 4.x (e.g. GCC in ELDK 4.0).  I'm getting compiler
> errors when I use GCC 4.x with this version of the kernel.

No problems here with 2.6.11 through 17-rc6 on gcc 4.

 - Brent

^ permalink raw reply

* Re: CPM_UART should allocate DPRAM for SMCx parameter RAM on MPC82xx
From: Laurent Pinchart @ 2006-06-12 11:43 UTC (permalink / raw)
  To: Vitaly Bordug; +Cc: linuxppc-embedded
In-Reply-To: <20060610030950.1a690abb@localhost.localdomain>

[-- Attachment #1: Type: text/plain, Size: 482 bytes --]

Hi Vitaly,

> If and when you'll have some code that address your problem, submit it
> to me cc linuxppc-embedded, and I'll push it upstream.

here's a simple patch (against 2.6.17-rc6) that fixes the issue by modifying 
the pram addresses for SMC resources. I'm not completely satisfied, as the 
SMCx_BASE registers configuration is board-specific (must be handled in 
init_ioports as you mentionned in your previous e-mail), which will lead to 
duplicated code.

Laurent Pinchart

[-- Attachment #2: pq2_devices.diff --]
[-- Type: text/x-diff, Size: 1047 bytes --]

diff --git a/arch/ppc/syslib/pq2_devices.c b/arch/ppc/syslib/pq2_devices.c
index fefbc21..0a7082e 100644
--- a/arch/ppc/syslib/pq2_devices.c
+++ b/arch/ppc/syslib/pq2_devices.c
@@ -289,15 +289,15 @@ struct platform_device ppc_sys_platform_
 		.num_resources	 = 3,
 		.resource = (struct resource[]) {
 			{
-				.name	= "smc_mem",
+				.name	= "regs",
 				.start	= 0x11A80,
 				.end	= 0x11A8F,
 				.flags	= IORESOURCE_MEM,
 			},
 			{
-				.name	= "smc_pram",
-				.start	= 0x87fc,
-				.end	= 0x87fd,
+				.name	= "pram",
+				.start	= 0x0000,
+				.end	= 0x003f,
 				.flags	= IORESOURCE_MEM,
 			},
 			{
@@ -313,15 +313,15 @@ struct platform_device ppc_sys_platform_
 		.num_resources	 = 3,
 		.resource = (struct resource[]) {
 			{
-				.name	= "smc_mem",
+				.name	= "regs",
 				.start	= 0x11A90,
 				.end	= 0x11A9F,
 				.flags	= IORESOURCE_MEM,
 			},
 			{
-				.name	= "smc_pram",
-				.start	= 0x88fc,
-				.end	= 0x88fd,
+				.name	= "pram",
+				.start	= 0x0040,
+				.end	= 0x007f,
 				.flags	= IORESOURCE_MEM,
 			},
 			{

^ permalink raw reply related

* Re: RFC: dma_mmap_coherent() for powerpc/ppc architecture and ALSA?
From: Takashi Iwai @ 2006-06-12 10:51 UTC (permalink / raw)
  To: Gerhard Pircher; +Cc: Lee Revell, alsa-devel, linux-kernel, linuxppc-dev
In-Reply-To: <20060610082223.321730@gmx.net>

At Sat, 10 Jun 2006 10:22:23 +0200,
Gerhard Pircher wrote:
> 
> > -------- Original-Nachricht --------
> > Datum: Fri, 09 Jun 2006 20:46:32 -0400
> > Von: Lee Revell <rlrevell@joe-job.com>
> > An: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> > Betreff: Re: RFC: dma_mmap_coherent() for powerpc/ppc architecture and 
> > ALSA?
> > 
> > On Sat, 2006-06-10 at 10:34 +1000, Benjamin Herrenschmidt wrote:
> > > > This leads me to the question, if there are any plans to include the 
> > > > dma_mmap_coherent() function (for powerpc/ppc and/or any other
> > > > platform) in one of the next kernel versions and if an adapation of
> > > > the ALSA drivers is planned. Or is there a simple way (hack) to fix
> > > > this problem?
> > > 
> > > You are welcome to do a patch implementing this :)
> > 
> > Please cc: alsa-devel when you do so.
> 
> :)
> 
> Well, implementing the dma_mmap_coherent() function isn't the
> problem, because it is already implemented for the ARM
> architecture.

Actually, I wrote dma_coherent_mmap patch long time ago but it has
been left forgotten.  The patch attached below seems applicable to
2.6.17 tree, but I'm not sure whether it still works properly.
It's untested on most of architectures.


> But as far as I understand this would require a
> rewrite of all the ALSA drivers (or at least a rewrite of the ALSA's
> DMA helper functions). 

Yes.  The change of ALSA side has been also on my tree.  But it was
still pending since I'm not satisfied with the design yet.
If you're interested in it, let me know.  I'll post the patch.


Takashi


diff --git a/arch/frv/mb93090-mb00/pci-dma-nommu.c b/arch/frv/mb93090-mb00/pci-dma-nommu.c
index 4985466..17a3064 100644
--- a/arch/frv/mb93090-mb00/pci-dma-nommu.c
+++ b/arch/frv/mb93090-mb00/pci-dma-nommu.c
@@ -106,6 +106,14 @@ void dma_free_coherent(struct device *hw
 
 EXPORT_SYMBOL(dma_free_coherent);
 
+int dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
+		      void *cpu_addr, dma_addr_t handle, size_t size)
+{
+	return -ENXIO;
+}
+
+EXPORT_SYMBOL(dma_mmap_coherent);
+
 /*
  * Map a single buffer of the indicated size for DMA in streaming mode.
  * The 32-bit bus address to use is returned.
diff --git a/arch/frv/mb93090-mb00/pci-dma.c b/arch/frv/mb93090-mb00/pci-dma.c
index 671ce1e..d4a8326 100644
--- a/arch/frv/mb93090-mb00/pci-dma.c
+++ b/arch/frv/mb93090-mb00/pci-dma.c
@@ -37,6 +37,21 @@ void dma_free_coherent(struct device *hw
 
 EXPORT_SYMBOL(dma_free_coherent);
 
+int dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
+		      void *cpu_addr, dma_addr_t handle, size_t size)
+{
+	unsigned long pfn;
+
+	/* bus is equivalent with phys */
+	pfn = ((unsigned long)handle) >> PAGE_SHIFT;
+	return remap_pfn_range(vma, vma->vm_start,
+			       pfn + vma->vm_pgoff,
+			       vma->vm_end - vma->vm_start,
+			       vma->vm_page_prot);
+}
+
+EXPORT_SYMBOL(dma_mmap_coherent);
+
 /*
  * Map a single buffer of the indicated size for DMA in streaming mode.
  * The 32-bit bus address to use is returned.
diff --git a/arch/mips/mm/dma-coherent.c b/arch/mips/mm/dma-coherent.c
index f6b3c72..2b591e4 100644
--- a/arch/mips/mm/dma-coherent.c
+++ b/arch/mips/mm/dma-coherent.c
@@ -59,6 +59,24 @@ void dma_free_coherent(struct device *de
 
 EXPORT_SYMBOL(dma_free_coherent);
 
+int dma_mmap_noncoherent(struct device *dev, struct vm_area_struct *vma,
+			 void *cpu_addr, dma_addr_t handle, size_t size)
+{
+	unsigned long pfn = page_to_pfn(virt_to_page(cpu_addr));
+	return remap_pfn_range(vma, vma->vm_start,
+			       pfn + vma->vm_pgoff,
+			       vma->vm_end - vma->vm_start,
+			       vma->vm_page_prot);
+}
+
+EXPORT_SYMBOL(dma_mmap_noncoherent);
+
+int dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
+		      void *cpu_addr, dma_addr_t handle, size_t size)
+	__attribute__((alias("dma_mmap_noncoherent")));
+
+EXPORT_SYMBOL(dma_mmap_coherent);
+
 dma_addr_t dma_map_single(struct device *dev, void *ptr, size_t size,
 	enum dma_data_direction direction)
 {
diff --git a/arch/mips/mm/dma-ip27.c b/arch/mips/mm/dma-ip27.c
index 8da19fd..db453eb 100644
--- a/arch/mips/mm/dma-ip27.c
+++ b/arch/mips/mm/dma-ip27.c
@@ -64,6 +64,27 @@ void dma_free_coherent(struct device *de
 
 EXPORT_SYMBOL(dma_free_coherent);
 
+int dma_mmap_noncoherent(struct device *dev, struct vm_area_struct *vma,
+			 void *cpu_addr, dma_addr_t handle, size_t size)
+{
+	unsigned long pfn = page_to_pfn(virt_to_page(cpu_addr));
+	return remap_pfn_range(vma, vma->vm_start,
+			       pfn + vma->vm_pgoff,
+			       vma->vm_end - vma->vm_start,
+			       vma->vm_page_prot);
+}
+
+EXPORT_SYMBOL(dma_mmap_noncoherent);
+
+int dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
+		      void *cpu_addr, dma_addr_t handle, size_t size)
+{
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+	return dma_mmap_noncoherent(dev, vma, cpu_addr, handle, size);
+}
+
+EXPORT_SYMBOL(dma_mmap_coherent);
+
 dma_addr_t dma_map_single(struct device *dev, void *ptr, size_t size,
 	enum dma_data_direction direction)
 {
diff --git a/arch/mips/mm/dma-ip32.c b/arch/mips/mm/dma-ip32.c
index ec54ed0..7cf348e 100644
--- a/arch/mips/mm/dma-ip32.c
+++ b/arch/mips/mm/dma-ip32.c
@@ -95,6 +95,29 @@ void dma_free_coherent(struct device *de
 
 EXPORT_SYMBOL(dma_free_coherent);
 
+int dma_mmap_noncoherent(struct device *dev, struct vm_area_struct *vma,
+			 void *cpu_addr, dma_addr_t handle, size_t size)
+{
+	unsigned long pfn = page_to_pfn(virt_to_page(cpu_addr));
+	return remap_pfn_range(vma, vma->vm_start,
+			       pfn + vma->vm_pgoff,
+			       vma->vm_end - vma->vm_start,
+			       vma->vm_page_prot);
+}
+
+EXPORT_SYMBOL(dma_mmap_noncoherent);
+
+int dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
+		      void *cpu_addr, dma_addr_t handle, size_t size)
+{
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+	return dma_mmap_noncoherent(dev, vma,
+				    (void *)CAC_ADDR((unsigned long)cpu_addr),
+				    handle, size);
+}
+
+EXPORT_SYMBOL(dma_mmap_coherent);
+
 static inline void __dma_sync(unsigned long addr, size_t size,
 	enum dma_data_direction direction)
 {
diff --git a/arch/mips/mm/dma-noncoherent.c b/arch/mips/mm/dma-noncoherent.c
index cd4ea84..64ac8fd 100644
--- a/arch/mips/mm/dma-noncoherent.c
+++ b/arch/mips/mm/dma-noncoherent.c
@@ -79,6 +79,29 @@ void dma_free_coherent(struct device *de
 
 EXPORT_SYMBOL(dma_free_coherent);
 
+int dma_mmap_noncoherent(struct device *dev, struct vm_area_struct *vma,
+			 void *cpu_addr, dma_addr_t handle, size_t size)
+{
+	unsigned long pfn = page_to_pfn(virt_to_page(cpu_addr));
+	return remap_pfn_range(vma, vma->vm_start,
+			       pfn + vma->vm_pgoff,
+			       vma->vm_end - vma->vm_start,
+			       vma->vm_page_prot);
+}
+
+EXPORT_SYMBOL(dma_mmap_noncoherent);
+
+int dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
+		      void *cpu_addr, dma_addr_t handle, size_t size)
+{
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+	return dma_mmap_noncoherent(dev, vma,
+				    (void *)CAC_ADDR((unsigned long)cpu_addr),
+				    handle, size);
+}
+
+EXPORT_SYMBOL(dma_mmap_coherent);
+
 static inline void __dma_sync(unsigned long addr, size_t size,
 	enum dma_data_direction direction)
 {
diff --git a/arch/parisc/kernel/pci-dma.c b/arch/parisc/kernel/pci-dma.c
index a6caf10..76481b3 100644
--- a/arch/parisc/kernel/pci-dma.c
+++ b/arch/parisc/kernel/pci-dma.c
@@ -396,6 +396,23 @@ static void pa11_dma_free_consistent (st
 	free_pages((unsigned long)__va(dma_handle), order);
 }
 
+static int pa11_mmap_noncoherent(struct device *dev, struct vm_area_struct *vma,
+				 void *cpu_addr, dma_addr_t handle, size_t size)
+{
+	unsigned long pfn = (unsigned long)handle >> PAGE_SHIFT;
+	return remap_pfn_range(vma, vma->vm_start,
+			       pfn + vma->vm_pgoff,
+			       vma->vm_end - vma->vm_start,
+			       vma->vm_page_prot);
+}
+
+static int pa11_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
+			      void *cpu_addr, dma_addr_t handle, size_t size)
+{
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+	return pa11_mmap_noncoherent(dev, vma, cpu_addr, handle, size);
+}
+
 static dma_addr_t pa11_dma_map_single(struct device *dev, void *addr, size_t size, enum dma_data_direction direction)
 {
 	if (direction == DMA_NONE) {
@@ -509,6 +526,8 @@ struct hppa_dma_ops pcxl_dma_ops = {
 	.dma_sync_single_for_device = pa11_dma_sync_single_for_device,
 	.dma_sync_sg_for_cpu = pa11_dma_sync_sg_for_cpu,
 	.dma_sync_sg_for_device = pa11_dma_sync_sg_for_device,
+	.mmap_coherent =	pa11_mmap_coherent,
+	.mmap_noncoherent =	pa11_mmap_noncoherent,
 };
 
 static void *fail_alloc_consistent(struct device *dev, size_t size,
@@ -537,6 +556,12 @@ static void pa11_dma_free_noncoherent(st
 	return;
 }
 
+static int fail_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
+			      void *cpu_addr, dma_addr_t handle, size_t size)
+{
+	return -ENXIO;
+}
+
 struct hppa_dma_ops pcx_dma_ops = {
 	.dma_supported =	pa11_dma_supported,
 	.alloc_consistent =	fail_alloc_consistent,
@@ -550,6 +575,8 @@ struct hppa_dma_ops pcx_dma_ops = {
 	.dma_sync_single_for_device =	pa11_dma_sync_single_for_device,
 	.dma_sync_sg_for_cpu =		pa11_dma_sync_sg_for_cpu,
 	.dma_sync_sg_for_device =	pa11_dma_sync_sg_for_device,
+	.mmap_coherent =	fail_mmap_coherent,
+	.mmap_noncoherent =	fail_mmap_coherent,
 };
 
 
diff --git a/arch/sh/mm/consistent.c b/arch/sh/mm/consistent.c
index ee73e30..097a0bf 100644
--- a/arch/sh/mm/consistent.c
+++ b/arch/sh/mm/consistent.c
@@ -59,6 +59,15 @@ void consistent_free(void *vaddr, size_t
 	}
 }
 
+int consistent_mmap(struct vm_area_struct *vma, void *vaddr, size_t size)
+{
+	vaddr = (void *)P1SEGADDR((unsigned long)vaddr);
+	return remap_pfn_range(vma, vma->vm_start,
+			       page_to_pfn(virt_to_page(vaddr)) + vma->vm_pgoff,
+			       vma->vm_end - vma->vm_start,
+			       vma->vm_page_prot);
+}
+
 void consistent_sync(void *vaddr, size_t size, int direction)
 {
 	void * p1addr = (void*) P1SEGADDR((unsigned long)vaddr);
@@ -80,5 +89,6 @@ void consistent_sync(void *vaddr, size_t
 
 EXPORT_SYMBOL(consistent_alloc);
 EXPORT_SYMBOL(consistent_free);
+EXPORT_SYMBOL(consistent_mmap);
 EXPORT_SYMBOL(consistent_sync);
 
diff --git a/arch/sparc/kernel/ioport.c b/arch/sparc/kernel/ioport.c
index f9ff297..46e1fc3 100644
--- a/arch/sparc/kernel/ioport.c
+++ b/arch/sparc/kernel/ioport.c
@@ -508,6 +508,22 @@ void pci_free_consistent(struct pci_dev 
 	free_pages(pgp, get_order(n));
 }
 
+/* Mmap a consistent area allocated via pci_alloc_consistent */
+int pci_mmap_consistent(struct pci_dev *pdev, struct vm_area_struct *vma,
+			void *cpu_addr, dma_addr_t handle, size_t size)
+{
+	unsigned long pfn;
+
+	/* calculate pfn from bus address */
+	pfn = pfn_base + (((unsigned long)handle - phys_base) >> PAGE_SHIFT);
+
+	return remap_pfn_range(vma, vma->vm_start,
+			       pfn + vma->vm_pgoff,
+			       vma->vm_end - vma->vm_start,
+			       vma->vm_page_prot);
+}
+
+
 /* Map a single buffer of the indicated size for DMA in streaming mode.
  * The 32-bit bus address to use is returned.
  *
diff --git a/arch/sparc/kernel/sparc_ksyms.c b/arch/sparc/kernel/sparc_ksyms.c
index 4b376fa..a48766a 100644
--- a/arch/sparc/kernel/sparc_ksyms.c
+++ b/arch/sparc/kernel/sparc_ksyms.c
@@ -198,6 +198,7 @@ EXPORT_SYMBOL(insl);
 EXPORT_SYMBOL(outsl);
 EXPORT_SYMBOL(pci_alloc_consistent);
 EXPORT_SYMBOL(pci_free_consistent);
+EXPORT_SYMBOL(pci_mmap_consistent);
 EXPORT_SYMBOL(pci_map_single);
 EXPORT_SYMBOL(pci_unmap_single);
 EXPORT_SYMBOL(pci_dma_sync_single_for_cpu);
diff --git a/arch/sparc64/kernel/pci_iommu.c b/arch/sparc64/kernel/pci_iommu.c
index 82e5455..50e4691 100644
--- a/arch/sparc64/kernel/pci_iommu.c
+++ b/arch/sparc64/kernel/pci_iommu.c
@@ -290,6 +290,18 @@ static void pci_4u_free_consistent(struc
 		free_pages((unsigned long)cpu, order);
 }
 
+/* Mmap a consistent area allocated via pci_alloc_consistent */
+int pci_mmap_consistent(struct pci_dev *pdev, struct vm_area_struct *vma,
+			void *cpu_addr, dma_addr_t handle, size_t size)
+{
+	unsigned long pfn = page_to_pfn(virt_to_page(cpu_addr));
+
+	return remap_pfn_range(vma, vma->vm_start,
+			       pfn + vma->vm_pgoff,
+			       vma->vm_end - vma->vm_start,
+			       vma->vm_page_prot);
+}
+
 /* Map a single buffer at PTR of SZ bytes for PCI DMA
  * in streaming mode.
  */
diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c
index 62d8a99..609abca 100644
--- a/arch/sparc64/kernel/sparc64_ksyms.c
+++ b/arch/sparc64/kernel/sparc64_ksyms.c
@@ -223,6 +223,7 @@ EXPORT_SYMBOL(isa_chain);
 EXPORT_SYMBOL(pci_memspace_mask);
 EXPORT_SYMBOL(pci_alloc_consistent);
 EXPORT_SYMBOL(pci_free_consistent);
+EXPORT_SYMBOL(pci_mmap_consistent);
 EXPORT_SYMBOL(pci_map_single);
 EXPORT_SYMBOL(pci_unmap_single);
 EXPORT_SYMBOL(pci_map_sg);
diff --git a/arch/x86_64/kernel/pci-dma.c b/arch/x86_64/kernel/pci-dma.c
index a9275c9..93f9120 100644
--- a/arch/x86_64/kernel/pci-dma.c
+++ b/arch/x86_64/kernel/pci-dma.c
@@ -166,6 +166,22 @@ void dma_free_coherent(struct device *de
 }
 EXPORT_SYMBOL(dma_free_coherent);
 
+int dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
+		      void *cpu_addr, dma_addr_t handle, size_t size)
+{
+	unsigned long pfn;
+
+	if (dma_ops->mmap_coherent)
+		return dma_ops->mmap_coherent(dev, vma, cpu_addr, handle, size);
+
+	pfn = page_to_pfn(virt_to_page(cpu_addr));
+	return remap_pfn_range(vma, vma->vm_start,
+			       pfn + vma->vm_pgoff,
+			       vma->vm_end - vma->vm_start,
+			       vma->vm_page_prot);
+}
+EXPORT_SYMBOL(dma_mmap_coherent);
+
 int dma_supported(struct device *dev, u64 mask)
 {
 	if (dma_ops->dma_supported)
diff --git a/include/asm-alpha/dma-mapping.h b/include/asm-alpha/dma-mapping.h
index 62d0d66..a2dc0a5 100644
--- a/include/asm-alpha/dma-mapping.h
+++ b/include/asm-alpha/dma-mapping.h
@@ -50,8 +50,20 @@ #define dma_mapping_error(addr)  (0)
 
 #endif	/* !CONFIG_PCI */
 
+static inline int
+dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
+		  void *cpu_addr, dma_addr_t handle, size_t size)
+{
+	unsigned long pfn = page_to_pfn(virt_to_page(cpu_addr));
+	return remap_pfn_range(vma, vma->vm_start,
+			       pfn + vma->vm_pgoff,
+			       vma->vm_end - vma->vm_start,
+			       vma->vm_page_prot);
+}
+
 #define dma_alloc_noncoherent(d, s, h, f)	dma_alloc_coherent(d, s, h, f)
 #define dma_free_noncoherent(d, s, v, h)	dma_free_coherent(d, s, v, h)
+#define dma_mmap_noncoherent(d, v, a, h, s)	dma_mmap_coherent(d, v, a, h, s)
 #define dma_is_consistent(dev)			(1)
 
 int dma_set_mask(struct device *dev, u64 mask);
diff --git a/include/asm-cris/dma-mapping.h b/include/asm-cris/dma-mapping.h
index cbf1a98..d2a629e 100644
--- a/include/asm-cris/dma-mapping.h
+++ b/include/asm-cris/dma-mapping.h
@@ -12,6 +12,7 @@ #include <asm/scatterlist.h>
 
 #define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f)
 #define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h)
+#define dma_mmap_noncoherent(d, v, a, h, s) dma_mmap_coherent(d, v, a, h, s)
 
 #ifdef CONFIG_PCI
 void *dma_alloc_coherent(struct device *dev, size_t size,
@@ -19,6 +20,18 @@ void *dma_alloc_coherent(struct device *
 
 void dma_free_coherent(struct device *dev, size_t size,
 			 void *vaddr, dma_addr_t dma_handle);
+
+static inline int
+dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
+		  void *cpu_addr, dma_addr_t handle, size_t size)
+{
+	unsigned long pfn = page_to_pfn(virt_to_page(cpu_addr));
+	return remap_pfn_range(vma, vma->vm_start,
+			       pfn + vma->vm_pgoff,
+			       vma->vm_end - vma->vm_start,
+			       vma->vm_page_prot);
+}
+
 #else
 static inline void *
 dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle,
@@ -34,6 +47,15 @@ dma_free_coherent(struct device *dev, si
 {
         BUG();
 }
+
+static inline int
+dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
+		  void *cpu_addr, dma_addr_t handle, size_t size)
+{
+	BUG();
+	return -ENXIO;
+}
+
 #endif
 static inline dma_addr_t
 dma_map_single(struct device *dev, void *ptr, size_t size,
diff --git a/include/asm-frv/dma-mapping.h b/include/asm-frv/dma-mapping.h
index e9fc1d4..7cd67fb 100644
--- a/include/asm-frv/dma-mapping.h
+++ b/include/asm-frv/dma-mapping.h
@@ -9,12 +9,16 @@ #include <asm/io.h>
 
 #define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f)
 #define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h)
+#define dma_mmap_noncoherent(d, v, a, h, s) dma_mmap_coherent(d, v, a, h, s)
 
 extern unsigned long __nongprelbss dma_coherent_mem_start;
 extern unsigned long __nongprelbss dma_coherent_mem_end;
 
 void *dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t gfp);
 void dma_free_coherent(struct device *dev, size_t size, void *vaddr, dma_addr_t dma_handle);
+int dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
+		      void *cpu_addr, dma_addr_t handle, size_t size);
+
 
 /*
  * These macros should be used after a pci_map_sg call has been done
diff --git a/include/asm-generic/dma-mapping-broken.h b/include/asm-generic/dma-mapping-broken.h
index a7f1a55..fcf656e 100644
--- a/include/asm-generic/dma-mapping-broken.h
+++ b/include/asm-generic/dma-mapping-broken.h
@@ -19,4 +19,12 @@ dma_free_coherent(struct device *dev, si
 	BUG();
 }
 
+static inline int
+dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
+		  void *cpu_addr, dma_addr_t handle, size_t size)
+{
+	BUG();
+	return -ENXIO;
+}
+
 #endif /* _ASM_GENERIC_DMA_MAPPING_H */
diff --git a/include/asm-generic/dma-mapping.h b/include/asm-generic/dma-mapping.h
index 1b35620..95d31d0 100644
--- a/include/asm-generic/dma-mapping.h
+++ b/include/asm-generic/dma-mapping.h
@@ -37,9 +37,13 @@ static inline void *
 dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle,
 		   gfp_t flag)
 {
+#ifdef CONFIG_MMU
 	BUG_ON(dev->bus != &pci_bus_type);
 
 	return pci_alloc_consistent(to_pci_dev(dev), size, dma_handle);
+#else
+	return -ENXIO;
+#endif
 }
 
 static inline void
@@ -51,6 +55,15 @@ dma_free_coherent(struct device *dev, si
 	pci_free_consistent(to_pci_dev(dev), size, cpu_addr, dma_handle);
 }
 
+static inline int
+dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
+		  void *cpu_addr, dma_addr_t handle, size_t size)
+{
+	BUG_ON(dev->bus != &pci_bus_type);
+
+	return pci_mmap_consistent(to_pci_dev(dev), vma, cpu_addr, handle, size);
+}
+
 static inline dma_addr_t
 dma_map_single(struct device *dev, void *cpu_addr, size_t size,
 	       enum dma_data_direction direction)
@@ -181,6 +194,14 @@ dma_free_coherent(struct device *dev, si
 	BUG();
 }
 
+static inline int
+dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
+		  void *cpu_addr, dma_addr_t handle, size_t size)
+{
+	BUG();
+	return -ENXIO;
+}
+
 static inline dma_addr_t
 dma_map_single(struct device *dev, void *cpu_addr, size_t size,
 	       enum dma_data_direction direction)
@@ -267,6 +288,7 @@ #endif
 
 #define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f)
 #define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h)
+#define dma_mmap_noncoherent(d, v, a, h, s) dma_mmap_coherent(d, v, a, h, s)
 #define dma_is_consistent(d)	(1)
 
 static inline int
diff --git a/include/asm-i386/dma-mapping.h b/include/asm-i386/dma-mapping.h
index 9cf20ca..144b5c7 100644
--- a/include/asm-i386/dma-mapping.h
+++ b/include/asm-i386/dma-mapping.h
@@ -10,6 +10,7 @@ #include <asm/bug.h>
 
 #define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f)
 #define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h)
+#define dma_mmap_noncoherent(d, v, a, h, s) dma_mmap_coherent(d, v, a, h, s)
 
 void *dma_alloc_coherent(struct device *dev, size_t size,
 			   dma_addr_t *dma_handle, gfp_t flag);
@@ -17,6 +18,17 @@ void *dma_alloc_coherent(struct device *
 void dma_free_coherent(struct device *dev, size_t size,
 			 void *vaddr, dma_addr_t dma_handle);
 
+static inline int
+dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
+		  void *cpu_addr, dma_addr_t handle, size_t size)
+{
+	unsigned long pfn = page_to_pfn(virt_to_page(cpu_addr));
+	return remap_pfn_range(vma, vma->vm_start,
+			       pfn + vma->vm_pgoff,
+			       vma->vm_end - vma->vm_start,
+			       vma->vm_page_prot);
+}
+
 static inline dma_addr_t
 dma_map_single(struct device *dev, void *ptr, size_t size,
 	       enum dma_data_direction direction)
diff --git a/include/asm-ia64/dma-mapping.h b/include/asm-ia64/dma-mapping.h
index df67d40..d441031 100644
--- a/include/asm-ia64/dma-mapping.h
+++ b/include/asm-ia64/dma-mapping.h
@@ -12,6 +12,20 @@ #define dma_alloc_coherent	platform_dma_
 #define dma_alloc_noncoherent	platform_dma_alloc_coherent	/* coherent mem. is cheap */
 #define dma_free_coherent	platform_dma_free_coherent
 #define dma_free_noncoherent	platform_dma_free_coherent
+
+static inline int
+dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
+		  void *cpu_addr, dma_addr_t handle, size_t size)
+{
+	unsigned long pfn = page_to_pfn(virt_to_page(cpu_addr));
+	return remap_pfn_range(vma, vma->vm_start,
+			       pfn + vma->vm_pgoff,
+			       vma->vm_end - vma->vm_start,
+			       vma->vm_page_prot);
+}
+
+#define dma_mmap_noncoherent	dma_mmap_coherent
+
 #define dma_map_single		platform_dma_map_single
 #define dma_map_sg		platform_dma_map_sg
 #define dma_unmap_single	platform_dma_unmap_single
diff --git a/include/asm-m32r/dma-mapping.h b/include/asm-m32r/dma-mapping.h
index a7fa030..d00e400 100644
--- a/include/asm-m32r/dma-mapping.h
+++ b/include/asm-m32r/dma-mapping.h
@@ -1,23 +1 @@
-#ifndef _ASM_M32R_DMA_MAPPING_H
-#define _ASM_M32R_DMA_MAPPING_H
-
-/*
- * NOTE: Do not include <asm-generic/dma-mapping.h>
- * Because it requires PCI stuffs, but current M32R don't provide these.
- */
-
-static inline void *
-dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle,
-		   gfp_t flag)
-{
-	return (void *)NULL;
-}
-
-static inline void
-dma_free_coherent(struct device *dev, size_t size, void *cpu_addr,
-		    dma_addr_t dma_handle)
-{
-	return;
-}
-
-#endif /* _ASM_M32R_DMA_MAPPING_H */
+#include <asm-generic/dma-mapping-broken.h>
diff --git a/include/asm-mips/dma-mapping.h b/include/asm-mips/dma-mapping.h
index 4328863..391abbd 100644
--- a/include/asm-mips/dma-mapping.h
+++ b/include/asm-mips/dma-mapping.h
@@ -16,6 +16,12 @@ void *dma_alloc_coherent(struct device *
 void dma_free_coherent(struct device *dev, size_t size,
 			 void *vaddr, dma_addr_t dma_handle);
 
+int dma_mmap_noncoherent(struct device *dev, struct vm_area_struct *vma,
+			 void *cpu_addr, dma_addr_t handle, size_t size);
+
+int dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
+		      void *cpu_addr, dma_addr_t handle, size_t size);
+
 extern dma_addr_t dma_map_single(struct device *dev, void *ptr, size_t size,
 	enum dma_data_direction direction);
 extern void dma_unmap_single(struct device *dev, dma_addr_t dma_addr,
diff --git a/include/asm-parisc/dma-mapping.h b/include/asm-parisc/dma-mapping.h
index 74d4ac6..c5cb337 100644
--- a/include/asm-parisc/dma-mapping.h
+++ b/include/asm-parisc/dma-mapping.h
@@ -20,6 +20,10 @@ struct hppa_dma_ops {
 	void (*dma_sync_single_for_device)(struct device *dev, dma_addr_t iova, unsigned long offset, size_t size, enum dma_data_direction direction);
 	void (*dma_sync_sg_for_cpu)(struct device *dev, struct scatterlist *sg, int nelems, enum dma_data_direction direction);
 	void (*dma_sync_sg_for_device)(struct device *dev, struct scatterlist *sg, int nelems, enum dma_data_direction direction);
+	int (*mmap_coherent)(struct device *dev, struct vm_area_struct *vma,
+			     void *cpu_addr, dma_addr_t handle, size_t size);
+	int (*mmap_noncoherent)(struct device *dev, struct vm_area_struct *vma,
+				void *cpu_addr, dma_addr_t handle, size_t size);
 };
 
 /*
@@ -75,6 +79,20 @@ dma_free_noncoherent(struct device *dev,
 	hppa_dma_ops->free_consistent(dev, size, vaddr, dma_handle);
 }
 
+static inline int
+dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
+		  void *cpu_addr, dma_addr_t handle, size_t size)
+{
+	hppa_dma_ops->mmap_coherent(dev, vma, cpu_addr, handle, size);
+}
+
+static inline int
+dma_mmap_noncoherent(struct device *dev, struct vm_area_struct *vma,
+		     void *cpu_addr, dma_addr_t handle, size_t size)
+{
+	hppa_dma_ops->mmap_noncoherent(dev, vma, cpu_addr, handle, size);
+}
+
 static inline dma_addr_t
 dma_map_single(struct device *dev, void *ptr, size_t size,
 	       enum dma_data_direction direction)
diff --git a/include/asm-powerpc/dma-mapping.h b/include/asm-powerpc/dma-mapping.h
index 2ac63f5..7e74385 100644
--- a/include/asm-powerpc/dma-mapping.h
+++ b/include/asm-powerpc/dma-mapping.h
@@ -167,6 +167,17 @@ #define dma_unmap_sg(dev, sg, nents, dir
 
 #endif /* CONFIG_PPC64 */
 
+static inline int
+dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
+		  void *cpu_addr, dma_addr_t handle, size_t size)
+{
+	unsigned long pfn = page_to_pfn(virt_to_page(cpu_addr));
+	return remap_pfn_range(vma, vma->vm_start,
+			       pfn + vma->vm_pgoff,
+			       vma->vm_end - vma->vm_start,
+			       vma->vm_page_prot);
+}
+
 static inline void dma_sync_single_for_cpu(struct device *dev,
 		dma_addr_t dma_handle, size_t size,
 		enum dma_data_direction direction)
@@ -218,6 +229,7 @@ #endif
 
 #define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f)
 #define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h)
+#define dma_mmap_noncoherent(d, v, a, h, s) dma_mmap_coherent(d, v, a, h, s)
 #ifdef CONFIG_NOT_COHERENT_CACHE
 #define dma_is_consistent(d)	(0)
 #else
diff --git a/include/asm-sh/dma-mapping.h b/include/asm-sh/dma-mapping.h
index 48f1f42..7c84cbf 100644
--- a/include/asm-sh/dma-mapping.h
+++ b/include/asm-sh/dma-mapping.h
@@ -12,6 +12,7 @@ extern struct bus_type pci_bus_type;
 /* arch/sh/mm/consistent.c */
 extern void *consistent_alloc(gfp_t gfp, size_t size, dma_addr_t *handle);
 extern void consistent_free(void *vaddr, size_t size);
+extern int consistent_mmap(struct vm_area_struct *vma, void *vaddr, size_t size);
 extern void consistent_sync(void *vaddr, size_t size, int direction);
 
 #define dma_supported(dev, mask)	(1)
@@ -54,6 +55,16 @@ static inline void dma_free_coherent(str
 	consistent_free(vaddr, size);
 }
 
+static inline int
+dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
+		  void *cpu_addr, dma_addr_t handle, size_t size)
+{
+	if (sh_mv.mv_consistent_alloc)
+		return -ENXIO;
+	else
+		return consistent_mmap(vma, cpu_addr, size);
+}
+
 static inline void dma_cache_sync(void *vaddr, size_t size,
 				  enum dma_data_direction dir)
 {
diff --git a/include/asm-sh64/dma-mapping.h b/include/asm-sh64/dma-mapping.h
index cc9a2e8..66cceaf 100644
--- a/include/asm-sh64/dma-mapping.h
+++ b/include/asm-sh64/dma-mapping.h
@@ -11,6 +11,8 @@ extern void *consistent_alloc(struct pci
 				    dma_addr_t *dma_handle);
 extern void consistent_free(struct pci_dev *hwdev, size_t size,
 				  void *vaddr, dma_addr_t dma_handle);
+extern int consistent_mmap(struct vm_area_struct *vma, void *vaddr,
+			   dma_addr_t handle, size_t size);
 
 #define dma_supported(dev, mask)	(1)
 
@@ -36,6 +38,21 @@ static inline void dma_free_coherent(str
 	consistent_free(NULL, size, vaddr, dma_handle);
 }
 
+static inline int
+dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
+		  void *cpu_addr, dma_addr_t handle, size_t size)
+{
+	unsigned long pfn;
+
+	/* dma_handle is equivalent with phys addr */
+	pfn = (unsigned long)handle >> PAGE_SHIFT;
+	return remap_pfn_range(vma, vma->vm_start,
+			       pfn + vma->vm_pgoff,
+			       vma->vm_end - vma->vm_start,
+			       vma->vm_page_prot);
+
+}
+
 static inline void dma_cache_sync(void *vaddr, size_t size,
 				  enum dma_data_direction dir)
 {
diff --git a/include/asm-sparc/dma-mapping.h b/include/asm-sparc/dma-mapping.h
index d7c3b0f..22380c0 100644
--- a/include/asm-sparc/dma-mapping.h
+++ b/include/asm-sparc/dma-mapping.h
@@ -6,20 +6,7 @@ #include <linux/config.h>
 #ifdef CONFIG_PCI
 #include <asm-generic/dma-mapping.h>
 #else
-
-static inline void *dma_alloc_coherent(struct device *dev, size_t size,
-			 dma_addr_t *dma_handle, gfp_t flag)
-{
-	BUG();
-	return NULL;
-}
-
-static inline void dma_free_coherent(struct device *dev, size_t size,
-		       void *vaddr, dma_addr_t dma_handle)
-{
-	BUG();
-}
-
+#include <asm-generic/dma-mapping-broken.h>
 #endif /* PCI */
 
 #endif /* _ASM_SPARC_DMA_MAPPING_H */
diff --git a/include/asm-sparc64/dma-mapping.h b/include/asm-sparc64/dma-mapping.h
index a8d39f2..8ea0082 100644
--- a/include/asm-sparc64/dma-mapping.h
+++ b/include/asm-sparc64/dma-mapping.h
@@ -145,22 +145,7 @@ dma_mapping_error(dma_addr_t dma_addr)
 }
 
 #else
-
-struct device;
-
-static inline void *dma_alloc_coherent(struct device *dev, size_t size,
-			 dma_addr_t *dma_handle, gfp_t flag)
-{
-	BUG();
-	return NULL;
-}
-
-static inline void dma_free_coherent(struct device *dev, size_t size,
-		       void *vaddr, dma_addr_t dma_handle)
-{
-	BUG();
-}
-
+#include <asm-generic/dma-mapping-broken.h>
 #endif /* PCI */
 
 #endif /* _ASM_SPARC64_DMA_MAPPING_H */
diff --git a/include/asm-x86_64/dma-mapping.h b/include/asm-x86_64/dma-mapping.h
index 49a81a6..c3a9b25 100644
--- a/include/asm-x86_64/dma-mapping.h
+++ b/include/asm-x86_64/dma-mapping.h
@@ -49,6 +49,10 @@ struct dma_mapping_ops {
 				struct scatterlist *sg, int nents,
 				int direction);
 	int             (*dma_supported)(struct device *hwdev, u64 mask);
+	int		(*mmap_coherent)(struct device *hwdev,
+					 struct vm_area_struct *vma,
+					 void *cpu_addr, dma_addr_t handle,
+					 size_t size);
 	int		is_phys;
 };
 
@@ -69,6 +73,9 @@ extern void *dma_alloc_coherent(struct d
 extern void dma_free_coherent(struct device *dev, size_t size, void *vaddr,
 			      dma_addr_t dma_handle);
 
+extern int dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
+			     void *cpu_addr, dma_addr_t handle, size_t size);
+
 static inline dma_addr_t
 dma_map_single(struct device *hwdev, void *ptr, size_t size,
 	       int direction)
diff --git a/include/asm-xtensa/dma-mapping.h b/include/asm-xtensa/dma-mapping.h
index c425f10..e0b1ca9 100644
--- a/include/asm-xtensa/dma-mapping.h
+++ b/include/asm-xtensa/dma-mapping.h
@@ -33,6 +33,17 @@ void *dma_alloc_coherent(struct device *
 void dma_free_coherent(struct device *dev, size_t size,
 			 void *vaddr, dma_addr_t dma_handle);
 
+static inline int
+dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
+		  void *cpu_addr, dma_addr_t handle, size_t size)
+{
+	struct page *page = virt_to_page(bus_to_virt(handle));
+	return remap_pfn_range(vma, vma->vm_start,
+			       page_to_pfn(page) + vma->vm_pgoff,
+			       vma->vm_end - vma->vm_start,
+			       vma->vm_page_prot);
+}
+
 static inline dma_addr_t
 dma_map_single(struct device *dev, void *ptr, size_t size,
 	       enum dma_data_direction direction)

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox