[PATCH] GX bus support

Benjamin Herrenschmidt benh at kernel.crashing.org
Mon Nov 14 19:16:40 EST 2005


From: Heiko J Schick <schickhj at de.ibm.com>

This patch adds the necessary core bus support used by device drivers
that sit on the IBM GX bus on modern pSeries machines like the Galaxy
infiniband for example. It provide transparent DMA ops (the low level
driver works with virtual addresses directly) along with a simple bus
layer using the Open Firmware matching routines.

Signed-off-by: Heiko J Schick <schickhj at de.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh at kernel.crashing.org>
---

You'll probably have to hack the path to arch/ppc64/kernel/dma.c in the
patch before applying if you already moved that to arch/powerpc. It was
not yet moved in my tree so I didn't "fix" the patch. Please send along
with your next batch to Linus, it's been ready in time but some mailer
breakage conspiracy prevented me from "polishing" it in time for -rc1.
 
Index: linux-work/arch/ppc64/kernel/dma.c
===================================================================
--- linux-work.orig/arch/ppc64/kernel/dma.c	2005-11-14 19:08:12.000000000 +1100
+++ linux-work/arch/ppc64/kernel/dma.c	2005-11-14 19:08:14.000000000 +1100
@@ -10,6 +10,7 @@
 /* Include the busses we support */
 #include <linux/pci.h>
 #include <asm/vio.h>
+#include <asm/ebus.h>
 #include <asm/scatterlist.h>
 #include <asm/bug.h>
 
@@ -23,6 +24,10 @@
 	if (dev->bus == &vio_bus_type)
 		return &vio_dma_ops;
 #endif
+#ifdef CONFIG_IBMEBUS
+	if (dev->bus == &ebus_bus_type)
+		return &ebus_dma_ops;
+#endif
 	return NULL;
 }
 
@@ -46,7 +51,11 @@
 #ifdef CONFIG_IBMVIO
 	if (dev->bus == &vio_bus_type)
 		return -EIO;
-#endif /* CONFIG_IBMVIO */
+#endif
+#ifdef CONFIG_IBMEBUS
+	if (dev->bus == &ebus_bus_type)
+		return -EIO;
+#endif
 	BUG();
 	return 0;
 }
Index: linux-work/arch/powerpc/platforms/pseries/ebus.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-work/arch/powerpc/platforms/pseries/ebus.c	2005-11-14 19:11:08.000000000 +1100
@@ -0,0 +1,372 @@
+/*
+ * IBM PowerPC eBus Infrastructure Support.
+ *
+ * Copyright (c) 2005 IBM Corporation
+ *  Heiko J Schick <schickhj at de.ibm.com>
+ *
+ * All rights reserved.
+ *
+ * This source code is distributed under a dual license of GPL v2.0 and OpenIB
+ * BSD.
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/kobject.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <asm/ebus.h>
+#include <asm/abs_addr.h>
+
+static struct ebus_dev ebus_bus_device = { /* fake "parent" device */
+	.name = ebus_bus_device.ofdev.dev.bus_id,
+	.ofdev.dev.bus_id = "ebus",
+	.ofdev.dev.bus    = &ebus_bus_type,
+};
+
+static void *ebus_alloc_coherent(struct device *dev,
+				 size_t size,
+				 dma_addr_t *dma_handle,
+				 unsigned int __nocast flag)
+{
+	return NULL;
+}
+
+static void ebus_free_coherent(struct device *dev,
+			       size_t size, void *vaddr, dma_addr_t dma_handle)
+{
+	return;
+}
+
+static dma_addr_t ebus_map_single(struct device *dev,
+				  void *ptr,
+				  size_t size,
+				  enum dma_data_direction direction)
+{
+	return (dma_addr_t)(ptr);
+}
+
+static void ebus_unmap_single(struct device *dev,
+			      dma_addr_t dma_addr,
+			      size_t size, enum dma_data_direction direction)
+{
+	return;
+}
+
+static int ebus_map_sg(struct device *dev,
+		       struct scatterlist *sg,
+		       int nents, enum dma_data_direction direction)
+{
+	int i;
+
+	for (i = 0; i < nents; i++) {
+		sg[i].dma_address = (dma_addr_t)page_address(sg[i].page)
+			+ sg[i].offset;
+	}
+
+	return nents;
+}
+
+static void ebus_unmap_sg(struct device *dev,
+			  struct scatterlist *sg,
+			  int nents, enum dma_data_direction direction)
+{
+	return;
+}
+
+static int ebus_dma_supported(struct device *dev, u64 mask)
+{
+	return 1;
+}
+
+struct dma_mapping_ops ebus_dma_ops = {
+	.alloc_coherent = ebus_alloc_coherent,
+	.free_coherent  = ebus_free_coherent,
+	.map_single     = ebus_map_single,
+	.unmap_single   = ebus_unmap_single,
+	.map_sg         = ebus_map_sg,
+	.unmap_sg       = ebus_unmap_sg,
+	.dma_supported  = ebus_dma_supported,
+};
+
+static int ebus_bus_probe(struct device *dev)
+{
+	struct ebus_dev *ebusdev    = to_ebus_dev(dev);
+	struct ebus_driver *ebusdrv = to_ebus_driver(dev->driver);
+	const struct of_device_id *id;
+	int error = -ENODEV;
+
+	if (!ebusdrv->probe)
+		return error;
+
+	id = of_match_device(ebusdrv->id_table, &ebusdev->ofdev);
+	if (id) {
+		error = ebusdrv->probe(ebusdev, id);
+	}
+
+	return error;
+}
+
+static int ebus_bus_remove(struct device *dev)
+{
+	struct ebus_dev *ebusdev    = to_ebus_dev(dev);
+	struct ebus_driver *ebusdrv = to_ebus_driver(dev->driver);
+
+	if (ebusdrv->remove) {
+		return ebusdrv->remove(ebusdev);
+	}
+
+	return 1;
+}
+
+static void __devinit ebus_dev_release(struct device *dev)
+{
+	of_node_put(dev->platform_data);
+	kfree(to_ebus_dev(dev));
+}
+
+static ssize_t ebusdev_show_name(struct device *dev,
+ 				 struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%s\n", to_ebus_dev(dev)->name);
+}
+static DEVICE_ATTR(name, S_IRUSR | S_IRGRP | S_IROTH, ebusdev_show_name, NULL);
+
+static struct ebus_dev* __devinit ebus_register_device_common(
+	struct ebus_dev *ebusdev, char *name)
+{
+	ebusdev->name = name;
+	ebusdev->ofdev.dev.parent  = &ebus_bus_device.ofdev.dev;
+	ebusdev->ofdev.dev.bus     = &ebus_bus_type;
+	ebusdev->ofdev.dev.release = ebus_dev_release;
+
+	if (of_device_register(&ebusdev->ofdev) != 0) {
+		printk(KERN_ERR "%s: failed to register device %s\n",
+		       __FUNCTION__, ebusdev->ofdev.dev.bus_id);
+		return NULL;
+	}
+
+	device_create_file(&ebusdev->ofdev.dev, &dev_attr_name);
+
+	return ebusdev;
+}
+
+struct ebus_dev* __devinit ebus_register_device_node(struct device_node *dn)
+{
+	struct ebus_dev *ebusdev;
+	char *loc_code;
+	int length;
+
+	loc_code = (char *)get_property(dn, "ibm,loc-code", NULL);
+	if (!loc_code) {
+                printk(KERN_WARNING "%s: node %s missing 'ibm,loc-code'\n",
+		       __FUNCTION__, dn->name ? dn->name : "<unknown>");
+		return NULL;
+        }
+
+	if (strlen(loc_code) == 0) {
+	        printk(KERN_WARNING "%s: 'ibm,loc-code' is invalid\n",
+		       __FUNCTION__);
+		return NULL;
+	}
+
+	ebusdev = kmalloc(sizeof(struct ebus_dev), GFP_KERNEL);
+	if (!ebusdev) {
+		return NULL;
+	}
+	memset(ebusdev, 0, sizeof(struct ebus_dev));
+
+	ebusdev->ofdev.node = of_node_get(dn);
+
+	length = strlen(loc_code);
+	strncpy(ebusdev->ofdev.dev.bus_id, loc_code
+		+ (strlen(loc_code) - min(length, BUS_ID_SIZE)), BUS_ID_SIZE);
+
+	/* register with generic device framework */
+	if (ebus_register_device_common(ebusdev, dn->name) == NULL) {
+		kfree(ebusdev);
+		return NULL;
+	}
+
+	return ebusdev;
+}
+
+static void probe_bus(char* name)
+{
+	struct device_node *dn = NULL;
+
+	while ((dn = of_find_node_by_name(dn, name))) {
+		ebus_register_device_node(dn);
+	}
+
+	of_node_put(dn);
+}
+
+static int ebus_unregister_device(struct device *dev)
+{
+	device_remove_file(dev, &dev_attr_name);
+	of_device_unregister(to_of_device(dev));
+
+	return 0;
+}
+
+static int ebus_match_helper(struct device *dev, void *data)
+{
+	if (strcmp((char*)data, to_ebus_dev(dev)->name) == 0)
+		return 1;
+
+	return 0;
+}
+
+int ebus_register_driver(struct ebus_driver *ebusdrv)
+{
+	struct of_device_id *idt;
+	struct device *dev;
+
+	ebusdrv->driver.name   = ebusdrv->name;
+	ebusdrv->driver.bus    = &ebus_bus_type;
+	ebusdrv->driver.probe  = ebus_bus_probe;
+	ebusdrv->driver.remove = ebus_bus_remove;
+
+	/* check if a driver for that device name is already loaded */
+	idt = ebusdrv->id_table;
+	while (strlen(idt->name) > 0) {
+		dev = bus_find_device(&ebus_bus_type, NULL, (void*)idt->name,
+				      ebus_match_helper);
+		if (dev) {
+			printk(KERN_ERR
+			       "%s: driver for device name %s already loaded\n",
+			       __FUNCTION__, idt->name);
+			return -EPERM;
+		}
+		idt++;
+	}
+
+	idt = ebusdrv->id_table;
+	while (strlen(idt->name) > 0) {
+		probe_bus(idt->name);
+		idt++;
+	}
+
+	return driver_register(&ebusdrv->driver);
+}
+EXPORT_SYMBOL(ebus_register_driver);
+
+void ebus_unregister_driver(struct ebus_driver *ebusdrv)
+{
+	struct of_device_id *idt;
+	struct device *dev;
+
+	driver_unregister(&ebusdrv->driver);
+
+	idt = ebusdrv->id_table;
+	while (strlen(idt->name) > 0) {
+		while ((dev = bus_find_device(&ebus_bus_type, NULL,
+					     (void*)idt->name,
+					     ebus_match_helper))) {
+			ebus_unregister_device(dev);
+		}
+		idt++;
+
+	}
+}
+EXPORT_SYMBOL(ebus_unregister_driver);
+
+int ebus_request_irq(u32 ist,
+		     irqreturn_t (*handler)(int, void*, struct pt_regs *),
+		     unsigned long irq_flags, const char * devname,
+		     void *dev_id)
+{
+	unsigned int irq = virt_irq_create_mapping(ist);
+
+	if (irq == NO_IRQ)
+		return -EINVAL;
+
+	irq = irq_offset_up(irq);
+
+	return request_irq(irq, handler,
+			   irq_flags, devname, dev_id);
+}
+EXPORT_SYMBOL(ebus_request_irq);
+
+void ebus_free_irq(u32 ist, void *dev_id)
+{
+	unsigned int irq = virt_irq_create_mapping(ist);
+
+	irq = irq_offset_up(irq);
+	free_irq(irq, dev_id);
+
+	return;
+}
+EXPORT_SYMBOL(ebus_free_irq);
+
+static int ebus_bus_match(struct device *dev, struct device_driver *drv)
+{
+	const struct ebus_dev *ebus_dev = to_ebus_dev(dev);
+	struct ebus_driver *ebus_drv    = to_ebus_driver(drv);
+	const struct of_device_id *ids  = ebus_drv->id_table;
+	const struct of_device_id *found_id;
+
+	if (!ids)
+		return 0;
+
+	found_id = of_match_device(ids, &ebus_dev->ofdev);
+	if (found_id)
+		return 1;
+
+	return 0;
+}
+
+struct bus_type ebus_bus_type = {
+	.name = "ebus",
+	.match = ebus_bus_match,
+};
+EXPORT_SYMBOL(ebus_bus_type);
+
+static int __init ebus_bus_init(void)
+{
+	int err;
+
+	printk(KERN_INFO "eBus Device Driver\n");
+
+	err = bus_register(&ebus_bus_type);
+	if (err) {
+		printk(KERN_ERR "failed to register eBus\n");
+		return err;
+	}
+
+	err = device_register(&ebus_bus_device.ofdev.dev);
+	if (err) {
+		printk(KERN_WARNING "%s: device_register returned %i\n",
+		       __FUNCTION__, err);
+		return err;
+	}
+
+	return 0;
+}
+__initcall(ebus_bus_init);
Index: linux-work/include/asm-powerpc/ebus.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-work/include/asm-powerpc/ebus.h	2005-11-14 19:08:14.000000000 +1100
@@ -0,0 +1,87 @@
+/*
+ * IBM PowerPC eBus Infrastructure Support.
+ *
+ * Copyright (c) 2005 IBM Corporation
+ *  Heiko J Schick <schickhj at de.ibm.com>
+ *
+ * All rights reserved.
+ *
+ * This source code is distributed under a dual license of GPL v2.0 and OpenIB
+ * BSD.
+ *
+ * OpenIB BSD License
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _ASM_EBUS_H
+#define _ASM_EBUS_H
+
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/mod_devicetable.h>
+#include <asm/of_device.h>
+
+extern struct dma_mapping_ops ebus_dma_ops;
+extern struct bus_type ebus_bus_type;
+
+struct ebus_dev {
+	char *name;
+	u64 unit_address;
+	struct of_device ofdev;
+};
+
+struct ebus_driver {
+	struct list_head node;
+	char *name;
+	struct of_device_id *id_table;
+	int (*probe) (struct ebus_dev *dev, const struct of_device_id *id);
+	int (*remove) (struct ebus_dev *dev);
+	unsigned long driver_data;
+
+	struct device_driver driver;
+};
+
+int ebus_register_driver(struct ebus_driver *ebusdrv);
+void ebus_unregister_driver(struct ebus_driver *ebusdrv);
+
+int ebus_request_irq(u32 ist,
+		     irqreturn_t (*handler)(int, void*, struct pt_regs *),
+		     unsigned long irq_flags, const char * devname,
+		     void *dev_id);
+
+void ebus_free_irq(u32 ist, void *dev_id);
+
+static inline struct ebus_driver *to_ebus_driver(struct device_driver *drv)
+{
+	return container_of(drv, struct ebus_driver, driver);
+}
+
+static inline struct ebus_dev *to_ebus_dev(struct device *dev)
+{
+	return container_of(dev, struct ebus_dev, ofdev.dev);
+}
+
+
+#endif /* _ASM_EBUS_H */
Index: linux-work/arch/powerpc/Kconfig
===================================================================
--- linux-work.orig/arch/powerpc/Kconfig	2005-11-14 19:08:12.000000000 +1100
+++ linux-work/arch/powerpc/Kconfig	2005-11-14 19:08:14.000000000 +1100
@@ -384,6 +384,13 @@
 	bool
 	default y
 
+config IBMEBUS
+	depends on PPC_PSERIES
+	bool "Support for GX bus based adapters"
+	default y
+	help
+	  Bus device driver for GX bus based adapters.
+
 config PPC_MPC106
 	bool
 	default n
Index: linux-work/arch/powerpc/platforms/pseries/Makefile
===================================================================
--- linux-work.orig/arch/powerpc/platforms/pseries/Makefile	2005-11-14 19:08:12.000000000 +1100
+++ linux-work/arch/powerpc/platforms/pseries/Makefile	2005-11-14 19:08:14.000000000 +1100
@@ -4,4 +4,5 @@
 obj-$(CONFIG_IBMVIO)	+= vio.o
 obj-$(CONFIG_XICS)	+= xics.o
 obj-$(CONFIG_SCANLOG)	+= scanlog.o
-obj-$(CONFIG_EEH)    += eeh.o eeh_event.o
+obj-$(CONFIG_EEH)	+= eeh.o eeh_event.o
+obj-$(CONFIG_IBMEBUS)	+= ebus.o





More information about the Linuxppc64-dev mailing list