[Cbe-oss-dev] Axon End Point (axonep.ko) driver for review: patch file 1 of 1

H Brett Bolen brettb at trilug.org
Wed Apr 2 01:30:49 EST 2008


From: Brett Bolen

This is a new patch that creates an axonep.ko ( Axon Companion
Chipset 'End-Point') driver.  This driver will be used to
access and export axon resources to child drivers.  This driver
will communicate with a 'host' axonpci.ko driver which sees the
axon as a pci device.  Axonep.ko and Axonpci.ko will allow
child drivers to support shared memory, dma ( via dmax), and
notification services.

This driver will be followed by a series of child drivers
to export functionality to userspace, and a network driver.

Signed-off-by: Brett Bolen <brettb at linux.vnet.ibm.com>

Index: linux-2.6/arch/powerpc/Kconfig
===================================================================
--- linux-2.6.orig/arch/powerpc/Kconfig
+++ linux-2.6/arch/powerpc/Kconfig
@@ -702,6 +702,8 @@ source "fs/Kconfig"
 
 source "arch/powerpc/sysdev/qe_lib/Kconfig"
 
+source "arch/powerpc/sysdev/axonep/Kconfig"
+
 source "lib/Kconfig"
 
 source "arch/powerpc/Kconfig.debug"
Index: linux-2.6/arch/powerpc/sysdev/Makefile
===================================================================
--- linux-2.6.orig/arch/powerpc/sysdev/Makefile
+++ linux-2.6/arch/powerpc/sysdev/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_FSL_PCI)		+= fsl_pci.o
 obj-$(CONFIG_RAPIDIO)		+= fsl_rio.o
 obj-$(CONFIG_TSI108_BRIDGE)	+= tsi108_pci.o tsi108_dev.o
 obj-$(CONFIG_QUICC_ENGINE)	+= qe_lib/
+obj-$(CONFIG_AXONEP)		+= axonep/
 obj-$(CONFIG_PPC_BESTCOMM)	+= bestcomm/
 mv64x60-$(CONFIG_PCI)		+= mv64x60_pci.o
 obj-$(CONFIG_MV64X60)		+= $(mv64x60-y) mv64x60_pic.o mv64x60_dev.o \
Index: linux-2.6/arch/powerpc/sysdev/axonep/Kconfig
===================================================================
--- /dev/null
+++ linux-2.6/arch/powerpc/sysdev/axonep/Kconfig
@@ -0,0 +1,18 @@
+#
+# AXON base and child drivers configuration
+#
+
+menu "Axon Bus driver for the IBM Axon Companion Chipset PCI Endpoint"
+
+config AXONEP
+	tristate "Axon bus endpoint driver"
+#	depends on PPC_IBM_CELL_BLADE
+	---help---
+	  Provides bus driver for axon chipset.
+
+	  To compile this driver as a module, choose M here; the
+	  module will be called axonep. If unsure, say N.
+
+
+endmenu
+
Index: linux-2.6/arch/powerpc/sysdev/axonep/Makefile
===================================================================
--- /dev/null
+++ linux-2.6/arch/powerpc/sysdev/axonep/Makefile
@@ -0,0 +1,10 @@
+
+obj-$(CONFIG_AXONEP) += axonep.o
+
+axonep-objs := axonep_main.o
+
+
+
+
+
+
Index: linux-2.6/arch/powerpc/sysdev/axonep/axon_hw.h
===================================================================
--- /dev/null
+++ linux-2.6/arch/powerpc/sysdev/axonep/axon_hw.h
@@ -0,0 +1,89 @@
+/**
+ *  axon_hw - axon chip defines.
+ *
+ *  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, 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
+ *
+ *  Copyright (C) IBM Corporation, 2008
+ *
+ *  Authors: H Brett Bolen <hbbolen at us.ibm.com>,
+ *	     Tim Schimke   <tschimke at us.ibm.com>,
+ *	     Jesse Arroyo  <arroyoj at us.ibm.com>,
+ *	     Murali N Iyer <mniyer at us.ibm.com>
+ *
+ */
+
+#ifndef __LINUX_AXON_HW_H__
+#define __LINUX_AXON_HW_H__
+
+/* Device base on Axon PLB5 */
+#define PLB5_DEV_BASE_HI		0x40000044u
+#define PLB5_DEV_BASE_LO		0x00000000u
+
+#define BE_DEV_BASE_OFFSET		0x0000004400000000ul
+
+/* PCIe0 UTL+CFG */
+#define PCIE_CFG_OFFSET			0x0000000000005000ul
+#define PCIE_CFG_SIZE			0x0000000000001000ul
+
+#define PCIE_CFG_POM0L			0x0380u
+#define PCIE_CFG_POM0H			0x0384u
+#define PCIE_CFG_POM1L			0x0388u
+#define PCIE_CFG_POM1H			0x038Cu
+#define PCIE_CFG_POM2L			0x0390u
+#define PCIE_CFG_POM2H			0x0394u
+
+#define PCIE_CFG_POM0L_MASK		0xf8000000u
+
+/* PCIe1 UTL+CFG */
+#define PCIE_UTL_OFFSET			0x0000000000004000ul
+#define PCIE_UTL_SIZE			0x0000000000001000ul
+
+#define DEV_C3PO_OFFSET			0x0000000000000000ul
+#define DEV_C3PO_SIZE			0x0000000000001000ul
+
+/* scratchpad 32 4-byte words */
+#define C3PO_SP_OFFSET			0xC00u
+#define C3PO_SP_SIZE			0x80u
+
+#define DEV_MBX_MSG0_OFFSET		0x0000000000002000ul
+#define DEV_MBX_MSG0_SIZE		0x0000000000000010ul
+
+#define DCR_MEM0_BASE_UPPER		0x06
+#define DCR_MEM0_BASE_LOWER		0x07
+#define DCR_MEM0_MASK_UPPER		0x08
+#define DCR_MEM0_MASK_LOWER		0x09
+#define DCR_MEM1_BASE_UPPER		0x0A
+#define DCR_MEM1_BASE_LOWER		0x0B
+#define DCR_MEM1_MASK_UPPER		0x0C
+#define DCR_MEM1_MASK_LOWER		0x0D
+#define DCR_MEM2_BASE_UPPER		0x0E
+#define DCR_MEM2_BASE_LOWER		0x0F
+#define DCR_MEM2_MASK_UPPER		0x10
+#define DCR_MEM2_MASK_LOWER		0x11
+
+#define DCR_MEM0_MASK_RSTFLGS		0xF8000000u
+#define DCR_MEM0_MASK_1CHNL		0x0002
+#define DCR_MEM0_MASK_2CHNL		0x0001
+#define DCR_MEM0_MASK_VALID		0x0400
+
+#define DCR_MEM2_MASK_RSTFLGS		0xFFFFFF80u
+#define DCR_MEM2_MASK_1CHNL		0x0008
+#define DCR_MEM2_MASK_2CHNL		0x0004
+#define DCR_MEM2_MASK_VALID		0x0001
+
+#define BE_AXON_BASE_PADDR_MASK 0x0000030000000000ul
+
+#endif /* __LINUX_AXON_HW_H__ */
+
Index: linux-2.6/arch/powerpc/sysdev/axonep/axon_scratchpad.h
===================================================================
--- /dev/null
+++ linux-2.6/arch/powerpc/sysdev/axonep/axon_scratchpad.h
@@ -0,0 +1,88 @@
+/**
+ * axonb_scratchpad - provides access to a small memory space on the
axon
+ *                    chip that is used for communication between the
+ *                    host and the cell systems.
+ *
+ *  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, 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
+ *
+ *  Copyright (C) IBM Corporation, 2008
+ *
+ *  Authors: H Brett Bolen <hbbolen at us.ibm.com>,
+ *	     Tim Schimke   <tschimke at us.ibm.com>,
+ *	     Jesse Arroyo  <arroyoj at us.ibm.com>,
+ *	     Murali N Iyer <mniyer at us.ibm.com>
+ *
+ */
+
+#ifndef __SCRATCHPAD_H__
+#define __SCRATCHPAD_H__
+
+/**
+ * scratchpad_comm
+ *   general purpose memory area accessable from both the host
+ *   and the cell processors.
+ *   32 word split between two sides
+ *
+ *   Each side writes to local_sp registers
+ *   and reads from remote_sp registers
+ *
+ *   todo: lowercase members ( these were defines ).
+ *
+ */
+
+/**
+ * each side ( host, and cell sb/companion_chip) has their a special
+ * area that is used to communicate with the other side.
+ *
+ * NOTE:
+ *    This area is going to be reorganized so that only the last
+ *    8 words ( of the 32) are going to be used. We will be able
+ *    to use the of_dev device treee and the pci cfg space for
+ *    some of this information.
+ */
+union scratchpad_comm {
+	struct {
+		u32   cmd_seqno;
+		u32   cmd_ack;
+		u32   cmd;               /* not used */
+		u32   subcmd;            /* not used */
+		u32   params[4];         /* not used */
+		u32   rfu0;              /* not used */
+		u32   int_disable;
+		u32   d2d_size;
+		u32   d2d_phys_hi;
+		u32   d2d_phys_lo;
+		u32   sma_size;
+		u32   sma_phys_hi;
+		u32   sma_phys_lo;
+	};
+	u32	sp_array[16];
+};
+
+/**
+ * This area is a small memory area on the axon chip that is
+ * accessable to both sides: host and sb/companion chip;
+ */
+/* todo - switch host side to big endian */
+struct scratchpad_area {
+	union scratchpad_comm  cellcc;
+	union scratchpad_comm  host;
+};
+
+#define SP_INIT_VAL	0xA55AA55A
+#define SP_SMA_READY	0x41584F4E	/* AXON */
+
+
+#endif /* __SCRATCHPAD_H__ */
Index: linux-2.6/arch/powerpc/sysdev/axonep/axonbus.h
===================================================================
--- /dev/null
+++ linux-2.6/arch/powerpc/sysdev/axonep/axonbus.h
@@ -0,0 +1,83 @@
+/**
+ *  Axon bus driver.
+ *
+ *  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, 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
+ *
+ *  Copyright (C) IBM Corporation, 2008
+ *
+ *  Authors: H Brett Bolen <hbbolen at us.ibm.com>,
+ *	     Tim Schimke   <tschimke at us.ibm.com>,
+ *	     Jesse Arroyo  <arroyoj at us.ibm.com>,
+ *	     Murali N Iyer <mniyer at us.ibm.com>
+ *
+ */
+
+#ifndef __AXONBUS_H__
+#define __AXONBUS_H__
+
+struct axonep_instance;
+struct axonep_driver;
+
+extern struct bus_type axonep_bus_type;
+
+/**
+ * axonep_driver - struct used by child driver to access axon
+ * resources.
+ */
+struct axonep_driver {
+	char *version;
+	char *name;
+	char *owner;
+	struct module *module;
+	struct device_driver driver;
+	int (*probe)(struct axonep_driver *, struct axonep_instance*);
+	int (*remove)(struct axonep_driver *, struct axonep_instance*);
+	int (*device_irq)(struct axonep_driver *, int type, int flags);
+};
+
+/* access to memory regions */
+enum axonep_local_memory_region {
+	MEM0_REGION = 0,
+	/* MEM1_REGION not used - axon regs */
+	MEM2_REGION = 2,
+};
+
+enum axonep_remote_memory_region {
+	RMEM0_REGION = 0,
+	/* RMEM1_REGION not used */
+	RMEM2_REGION = 2,
+};
+
+/* registration support */
+extern int  axonep_register_driver(struct axonep_driver *);
+extern void axonep_unregister_driver(struct axonep_driver *);
+
+/* axon services */
+extern void __iomem *axonep_get_regs(struct axonep_instance *handle,
int *len);
+extern u64  axonep_get_rmem2_paddr(struct axonep_instance *handle,
+				   int *len);
+
+extern void *axonep_get_mem(struct axonep_instance *handle,
+			    int *len,
+			    enum axonep_local_memory_region lmem);
+extern void __iomem *axonep_get_rmem(struct axonep_instance *handle,
+				     int *len,
+				     enum axonep_remote_memory_region rmem);
+
+/* registration of of devices */
+extern struct axonep_instance *axonep_get_handle(struct of_device
*ofdev);
+
+
+#endif /* __AXONBUS_H__ */
Index: linux-2.6/arch/powerpc/sysdev/axonep/axonep_main.c
===================================================================
--- /dev/null
+++ linux-2.6/arch/powerpc/sysdev/axonep/axonep_main.c
@@ -0,0 +1,996 @@
+/**
+ *  Axon End Point bus driver for Axon Companion Chipset.
+ *
+ *  This driver also provides a bus driver for services related to the
+ *  axon companion chip.  The axon services are implemented by child
+ *  drivers.  The axon child driver support support a userspace
+ *  and kernel interface to shared memory using axon memory mapping
+ *  and axon mailbox notification.
+ *
+ *  This driver implements the axon bus, allows for driver
+ *  registration, attaches to each axon found, provides resources
+ *  to child drivers.  The interface provided by this driver
+ *  will also be provided by a host pci driver in order for
+ *  similar child drivers to be supported via the axon pcie
+ *  host interace.
+ *
+ *  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, 51 Franklin Street, Fifth Floor, Boston,
+ *  MA	02110-1301, USA.
+ *
+ *  Copyright (C) IBM Corporation, 2008
+ *
+ *  Authors: H Brett Bolen <brettb at linux.vnet.ibm.com>,
+ *	     Tim Schimke   <tschimke at us.ibm.com>,
+ *	     Jesse Arroyo  <arroyoj at us.ibm.com>,
+ *	     Murali N Iyer <mniyer at linux.vnet.ibm.com>,
+ */
+
+#define DEBUG
+#define USE_DCR		    /* remove with firmware fix */
+#define SMA_ENABLED	    /* move to axonsma driver */
+#undef	OF_COMPATIBLE	    /* compatible flag in of_dev tree */
+
+#include <asm/machdep.h>
+#include <asm/dcr.h>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/platform_device.h>
+#include <linux/of_platform.h>
+
+#include "axon_hw.h"
+#include "axon_scratchpad.h"
+#include "axonbus.h"
+#include "axonep.h"
+
+#define MODULE_NAME    "axonep"
+
+static struct list_head	 axonep_list;
+
+static int mem0_size = (4*1024*1024);  /* 4 MB - driver - d2d */
+static int mem2_size = (1*1024*1024);  /* 1 MB - app -	  sma */
+
+module_param(mem0_size, int, S_IRUGO);
+module_param(mem2_size, int, S_IRUGO);
+
+/* handle dcr interface chage */
+#if 1
+#define DCR_ADDR(base,offset)	(base+offset)
+#else
+#define DCR_ADDR(base,offset)	(offset)
+#endif
+
+/**
+ * dump_image - hexdump data
+ * @ptr: pointer to data
+ * @offfset: start offset
+ * @len: length to dump
+ * @banner: banner to print
+ *
+ * output form "addr: hex[0..7] | ascii[0..7]\n"
+ */
+static void dump_image(void *buf, int offset, int len, char *banner)
+{
+	int    i;
+	int    start;
+	char  *ptr = buf;
+
+	/* strip the newline and print banner */
+	char	 *nl = strchr(banner, '\n');
+	if (nl)	 *nl = '\0';
+	pr_info("image @%p[s=0x%x, l=0x%x]  ---- '%s' \n",
+		buf, offset, len, banner);
+
+	if (!ptr) {
+		pr_info("null pointer for '%s'\n", banner);
+		return;
+	}
+
+	for (start = offset;  start < offset + len; start += 8) {
+		char out[180] = {0};
+		int  out_len  = 0;
+		out_len += sprintf(&out[out_len], " 0x%04x: ", start);
+		for (i = 0; i < 8; i++) {
+			char ch = (char) *( ptr + start + i);
+			out_len += sprintf(&out[out_len], " %02x",ch);
+		}
+		out_len += sprintf(&out[out_len], " | ");
+		for (i = 0; i < 8; i++) {
+			char ch = (char) *( ptr + start + i);
+			if ((ch >= ' ') && (ch <= 'z')) {
+				out_len += sprintf(&out[out_len], "%c", ch);
+			} else {
+				out_len += sprintf(&out[out_len], ".");
+			}
+		}
+		pr_info("%s\n", out);
+	}
+	pr_info(".\n");
+}
+
+/**
+ * dump_scratchpad - print scratchpad to syslog
+ * @priv: instance data
+ * @banner: banner to print
+ */
+static void axonep_dump_scratchpad(struct axonep_instance *priv, char
*banner)
+{
+	void *buf = (void *) priv;
+	char *nl = strchr(banner, '\n');
+	if (nl)	 *nl = '\0';
+	pr_info("priv @ %p ---- '%s' \n", priv, banner);
+	pr_info("  priv->pcie_reg   @ %p\n", priv->pcie_reg_base_addr);
+	pr_info("  priv->c3po	      @ %p\n", priv->c3po_base_addr);
+	pr_info("  priv->scratchpad @ %p \n", priv->scratchpad);
+	dump_image( buf, 0, 0x80, banner);
+}
+
+/**
+ * axonep_register_driver - Registers a new Axon child device driver
+ * @adrv: pointer to the driver definition structure
+ *
+ * This function is called by child drivers to register
+ * itself on the axon bus.  Note: axon device support
+ * will be needed at a later date.
+ */
+int axonep_register_driver(struct axonep_driver *adrv)
+{
+	int rc;
+	struct axonep_instance *ipriv;
+	struct list_head  *pos, *next;
+
+	/* initialize common driver fields */
+	adrv->driver.name = adrv->name;
+	adrv->driver.bus = &axonep_bus_type;
+
+	/* register with core */
+	rc = driver_register(&adrv->driver);
+	pr_debug("%s: registering '%s' @ %p returns %d\n",
+		 __FUNCTION__, adrv->name, adrv, rc);
+
+	/* notify child driver of each axon */
+	list_for_each_safe(pos, next, &axonep_list) {
+		if (adrv->probe) {
+			ipriv = list_entry(pos, struct axonep_instance, list);
+			if (!ipriv)
+				pr_info("%s: bad ipriv\n", __FUNCTION__);
+			else
+				adrv->probe( adrv, ipriv);
+		}
+	}
+	return rc;
+}
+EXPORT_SYMBOL_GPL(axonep_register_driver);
+
+/**
+ * axonep_unregister_driver - Unregisters a new Axon device driver
+ * @adrv: pointer to the driver definition structure
+ */
+void axonep_unregister_driver(struct axonep_driver *adrv)
+{
+	struct list_head *pos, *next;
+	pr_debug("%s: unregistering '%s' \n",
+		 __FUNCTION__, adrv->name);
+
+	/* notify child driver of each axon */
+	list_for_each_safe(pos, next, &axonep_list) {
+		if (adrv->remove) {
+			struct axonep_instance *ipriv;
+			ipriv = list_entry(pos, struct axonep_instance, list);
+			adrv->remove(adrv, ipriv);
+		}
+	}
+	driver_unregister(&adrv->driver);
+}
+EXPORT_SYMBOL_GPL(axonep_unregister_driver);
+
+/* axonep_get_handle - given of_device, return axon handle
+ * @ofdev - of_device to match
+ *
+ * if the of_device and the axon instance's of_device share
+ * a common grandparent of_device then the corresponding
+ * axon priv instance is returned to be used as a 'handle'
+ * to get axon base resources.
+ */
+struct axonep_instance *axonep_get_handle(struct of_device *ofdev)
+{
+	struct list_head *pos, *next;
+	struct device_node *parent, *grandparent;
+
+	parent = of_get_parent(ofdev->node);
+	if (!parent) {
+		pr_info("%s: no parent\n", __FUNCTION__);
+		return NULL;
+	}
+	grandparent = of_get_parent(parent);
+	if (!grandparent) {
+		pr_info("%s: no grandparent\n", __FUNCTION__);
+		return NULL;
+	}
+
+	/* search for axon of_dev */
+	list_for_each_safe(pos, next, &axonep_list) {
+		struct device_node    *axon_parent;
+		struct device_node    *axon_grandparent;
+		struct axonep_instance *ipriv;
+		ipriv = list_entry(pos, struct axonep_instance, list);
+		if (ipriv) {
+			axon_parent = of_get_parent(ipriv->ofdev->node);
+			if (!axon_parent) {
+				pr_info("%s: no axon parent\n", __FUNCTION__);
+				return NULL;
+			}
+			axon_grandparent = of_get_parent(axon_parent);
+			if (!axon_grandparent) {
+				pr_info("%s: no axon grandparent\n",
+					__FUNCTION__);
+				return NULL;
+			}
+			if (axon_grandparent == grandparent) {
+				/* success */
+				return ipriv;
+			}
+		}
+	}
+
+	pr_debug("%s: no match\n", __FUNCTION__);
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(axonep_get_handle);
+
+
+
+/**
+ * bus type
+ */
+struct bus_type axonep_bus_type = {
+	.name	= "axonep",
+};
+EXPORT_SYMBOL_GPL(axonep_bus_type);
+
+/**
+ * allocate_exported_mem
+ * @priv:  instance struct for device
+ *
+ *  The axon scratchpad is used for communication between
+ *  the ep and pci side of the axon drivers.  This interface
+ *  is currently defined in axon_scratchpad.h. It will,
+ *  however, be modified so that only the last 8 u32 will
+ *  be used.
+ */
+static int allocate_exported_mem(struct axonep_instance *priv)
+{
+	int order;
+	order = get_order(mem0_size);
+	priv->mem0.size = (1<<order)*PAGE_SIZE;
+	priv->mem0.ptr= dma_alloc_coherent(&priv->ofdev->dev,
+					   priv->mem0.size,
+					   &priv->mem0.handle,
+					   GFP_KERNEL);
+
+	if (priv->mem0.ptr) {
+
+		/* notify remote via scratchpad -- mem0 window */
+		iowrite32be(priv->mem0.size,
+			    &(priv->scratchpad->cellcc.d2d_size));
+		iowrite32be(priv->mem0.handle & 0xffffffff,
+			    &(priv->scratchpad->cellcc.d2d_phys_lo));
+		iowrite32be(priv->mem0.handle >> 32,
+			    &(priv->scratchpad->cellcc.d2d_phys_hi));
+
+	} else {
+		return -ENOMEM;
+	}
+
+#ifdef SMA_ENABLED
+	order = get_order(mem2_size);
+	priv->mem2.size = (1<<order)*PAGE_SIZE;
+	priv->mem2.ptr = dma_alloc_coherent(&priv->ofdev->dev,
+					    priv->mem2.size,
+					    &priv->mem2.handle,
+					    GFP_KERNEL);
+	if (priv->mem2.ptr) {
+		/* set mem2 window (app sma ) */
+		iowrite32be(priv->mem2.size,
+			    &(priv->scratchpad->cellcc.sma_size));
+		iowrite32be(priv->mem2.handle & 0xffffffff,
+			    &(priv->scratchpad->cellcc.sma_phys_lo));
+		iowrite32be(priv->mem2.handle >> 32,
+			    &(priv->scratchpad->cellcc.sma_phys_hi));
+	} else {
+		return -ENOMEM;
+	}
+#endif
+
+	/* local scratchpad is ready */
+	iowrite32be(SP_SMA_READY,
+		    &(priv->scratchpad->cellcc.cmd_seqno));
+
+	return 0;
+}
+
+/**
+ * free_exported_memory
+ */
+static void free_exported_memory(struct axonep_instance *priv)
+{
+	if (priv->mem0.ptr)
+		dma_free_coherent(&priv->ofdev->dev,
+				  priv->mem0.size,
+				  priv->mem0.ptr,
+				  priv->mem0.handle);
+#ifdef SMA_ENABLED
+	if (priv->mem2.ptr)
+		dma_free_coherent(&priv->ofdev->dev,
+				  priv->mem2.size,
+				  priv->mem2.ptr,
+				  priv->mem2.handle);
+#endif
+}
+
+/**
+ * unmap_rmem0 - map remote driver shared memory.
+ * @priv:  instance struct for device
+ */
+static void unmap_rmem0(struct axonep_instance *priv)
+{
+	pr_info("%s: release rmem0 0x%x bytes at 0x%p\n",
+		__FUNCTION__,
+		priv->rmem0.size, (void*) priv->rmem0.paddr);
+	/* axon pcie port 0 memory region 2 (remote system memory) */
+	iounmap(priv->rmem0.ptr);
+	release_mem_region(priv->rmem0.paddr,  priv->rmem0.size);
+}
+
+/**
+ * map_rmem0 - map remote driver shared memory.
+ * @priv:  instance struct for device
+ */
+static int map_rmem0(struct axonep_instance *priv)
+{
+	int rc;
+	u32 paddr_hi, paddr_lo, paddr_sz;
+	u64 mask;
+
+	/* get remote's physical address from scratpad */
+	paddr_sz = ioread32(&priv->scratchpad->host.d2d_size);
+	paddr_hi = ioread32(&priv->scratchpad->host.d2d_phys_hi);
+	paddr_lo = ioread32(&priv->scratchpad->host.d2d_phys_lo);
+
+#ifdef USE_DCR		   /* firmware fix needed */
+	dcr_write(priv->dcrreg_host,
+		  DCR_ADDR(priv->dcrreg_base, DCR_MEM0_BASE_UPPER),
+		  (priv->pcie_mem0_base >> 32) & 0xFFFFFFFF);
+
+	dcr_write(priv->dcrreg_host,
+		  DCR_ADDR(priv->dcrreg_base, DCR_MEM0_BASE_LOWER),
+		  (priv->pcie_mem0_base	& 0xFFFFFFFF));
+#endif
+
+	iowrite32(paddr_hi,
+		  priv->pcie_cnfg_base_addr + PCIE_CFG_POM0H);
+	iowrite32((paddr_lo & PCIE_CFG_POM0L_MASK),
+		  priv->pcie_cnfg_base_addr + PCIE_CFG_POM0L);
+
+	/* local's physical address */
+	priv->rmem0.paddr = priv->c3poregs_paddr +
+		(priv->pcie_mem0_base & 0x00FFFFFFFFFFFFFFul) +
+		(paddr_lo & ~PCIE_CFG_POM0L_MASK);
+	priv->rmem0.size = paddr_sz;
+
+	/* This generates a mask of leading ones with following
+	 * zeros.  The zeros are used to determine the size
+	 * of the region. */
+	mask = ~((u64) (paddr_sz -1));
+
+#ifdef USE_DCR		   /* firmware fix needed */
+	dcr_write(priv->dcrreg_host,
+		  DCR_ADDR(priv->dcrreg_base, DCR_MEM0_MASK_UPPER),
+		  (u32) (mask>>32));
+	dcr_write(priv->dcrreg_host,
+		  DCR_ADDR(priv->dcrreg_base, DCR_MEM0_MASK_LOWER),
+		  (u32) ((mask & DCR_MEM0_MASK_RSTFLGS) |
+			 DCR_MEM0_MASK_1CHNL |
+			 DCR_MEM0_MASK_VALID));
+
+#endif
+
+	/* axon pcie port 0 memory region 2 (remote system memory) */
+	if (!request_mem_region(priv->rmem0.paddr,
+				priv->rmem0.size,
+				"axon_rd2d"))	{
+		rc = -ENOMEM;
+		dev_info(&priv->ofdev->dev,
+			 "%s:request_mem_region failed for rd2d\n",
+			 __FUNCTION__);
+	} else {
+		rc = 0;
+		priv->rmem0.ptr = (void __iomem *)
+			ioremap(priv->rmem0.paddr,
+				priv->rmem0.size);
+	}
+	return rc;
+}
+
+/**
+ * unmap_rmem2 - map remote application shared memory.
+ * @priv:  instance struct for device
+ */
+static void unmap_rmem2(struct axonep_instance *priv)
+{
+	pr_info("%s: release rmem2 0x%x bytes at %lx\n",
+		__FUNCTION__,
+		priv->rmem2.size, priv->rmem2.paddr);
+
+	/* unmap and give memory back */
+	iounmap(priv->rmem2.ptr);
+	release_mem_region(priv->rmem2.paddr,  priv->rmem2.size);
+}
+
+/**
+ * map_rmem2  - map remote application shared memory.
+ * @priv:  instance struct for device
+ */
+static int map_rmem2(struct axonep_instance *priv)
+{
+	int rc;
+	u32 paddr_hi, paddr_lo, paddr_sz;
+	u64 mask;
+
+	/* remote's physical address */
+	paddr_sz = ioread32(&priv->scratchpad->host.sma_size);
+	paddr_hi = ioread32(&priv->scratchpad->host.sma_phys_hi);
+	paddr_lo = ioread32(&priv->scratchpad->host.sma_phys_lo);
+
+#ifdef USE_DCR		   /* firmware fix needed */
+	dcr_write(priv->dcrreg_host,
+		  DCR_ADDR(priv->dcrreg_base, DCR_MEM2_BASE_UPPER),
+		  (priv->pcie_mem2_base >> 32) & 0xFFFFFFFF);
+	dcr_write(priv->dcrreg_host,
+		  DCR_ADDR(priv->dcrreg_base, DCR_MEM2_BASE_LOWER),
+		  (priv->pcie_mem2_base	& 0xFFFFFFFF));
+
+#endif
+
+	iowrite32(paddr_hi, priv->pcie_cnfg_base_addr + PCIE_CFG_POM2H);
+	iowrite32(paddr_lo, priv->pcie_cnfg_base_addr + PCIE_CFG_POM2L);
+
+	/* local's physical address */
+	priv->rmem2.paddr = priv->c3poregs_paddr +
+		(priv->pcie_mem2_base & 0x00FFFFFFFFFFFFFFFFul);
+
+	priv->rmem2.size = paddr_sz;
+	mask = ~((u64) (paddr_sz -1));
+
+#ifdef USE_DCR		   /* firmware fix needed */
+	dcr_write(priv->dcrreg_host,
+		  DCR_ADDR(priv->dcrreg_base, DCR_MEM2_MASK_UPPER),
+		  (u32) (mask>>32));
+	dcr_write(priv->dcrreg_host,
+		  DCR_ADDR(priv->dcrreg_base, DCR_MEM2_MASK_LOWER),
+		  (u32) ((mask & DCR_MEM2_MASK_RSTFLGS) |
+			 DCR_MEM2_MASK_1CHNL |
+			 DCR_MEM2_MASK_VALID));
+#endif
+
+	/* grab memory */
+	if (!request_mem_region(priv->rmem2.paddr,
+				priv->rmem2.size,
+				"axon_rsma"))	{
+		rc = -ENOMEM;
+		dev_info(&priv->ofdev->dev,
+			 "request_mem_region failed for rsma\n");
+	} else {
+		rc = 0;
+		priv->rmem2.ptr = (void __iomem *)
+			ioremap(priv->rmem2.paddr,
+				priv->rmem2.size);
+	}
+	return rc;
+}
+
+/**
+ * map_remote_memory - map remote memory
+ * @priv:  instance struct for device
+ */
+static int map_remote_memory(struct axonep_instance *priv)
+{
+	int rc, rc2;
+	u32 u1;
+
+	rc = rc2 = 0;
+
+	u1 = ioread32(&priv->scratchpad->host.cmd_seqno);
+	if  (u1 == SP_SMA_READY) {
+		rc = map_rmem0(priv);
+		if (rc)
+			dev_info(&priv->ofdev->dev,
+				 "%s: map_rmem0 failure %d\n",
+				 __FUNCTION__, rc);
+#ifdef SMA_ENABLED
+		rc2 = map_rmem2(priv);
+		if (rc2)
+			dev_info(&priv->ofdev->dev,
+				 "%s: map_rmem2 failure %d\n",
+				 __FUNCTION__, rc2);
+#endif
+	}
+	return rc;
+}
+
+/**
+ * unmap_remote_memory
+ * @priv:  instance struct for device
+ */
+static void unmap_remote_memory(struct axonep_instance *priv)
+{
+	int r1;
+
+	r1 = ioread32(&priv->scratchpad->cellcc.cmd_seqno);
+	if  (r1 == SP_SMA_READY) {
+		unmap_rmem0(priv);
+#ifdef SMA_ENABLED
+		unmap_rmem2(priv);
+#endif
+	}
+
+	/* local scratchpad is NOT ready */
+	iowrite32be(SP_INIT_VAL,
+		    &(priv->scratchpad->cellcc.cmd_seqno));
+}
+
+/**
+ * clear scratchpad
+ * @priv:  instance struct for device
+ */
+static void clear_scratchpad(struct axonep_instance *priv)
+{
+	int  i;
+	/* clear it out	 */
+	for (i = 0;  i < 16; i++)
+		iowrite32be(0, &(priv->scratchpad->cellcc.sp_array[i]));
+}
+
+/**
+ * initialize scratchpad
+ * @priv:  instance struct for device
+ */
+static void initialize_scratchpad(struct axonep_instance *priv)
+{
+	clear_scratchpad(priv);
+
+	/* attach signature */
+	iowrite32be(SP_INIT_VAL, &(priv->scratchpad->cellcc.cmd_ack));
+	iowrite32be(SP_INIT_VAL, &(priv->scratchpad->cellcc.cmd_seqno));
+
+	/* turn off mbx interrupt */
+	iowrite32be(0xffff, &(priv->scratchpad->cellcc.int_disable));
+}
+
+/**
+ * map registers and request memory regions
+ * @priv:  instance struct for device
+ * @ofdev: open firmware device
+ */
+static int  request_resources(struct axonep_instance *priv,
+			      struct of_device *ofdev)
+{
+	int	     rc;
+	const u64   *regions;
+
+	dev_dbg( &(priv->ofdev->dev),
+		 "%s: requesting pcieregs mem region\n",
+		 __FUNCTION__);
+
+	priv->pcieregs_paddr = priv->res.start;
+	priv->pcieregs_plen  = priv->res.end - priv->res.start + 1;
+
+	rc = 0;
+	if (!request_mem_region(priv->pcieregs_paddr,
+				priv->pcieregs_plen,
+				"axon_pciregs")) {
+		dev_info(&(priv->ofdev->dev),
+			 "%s: request_mem_region failed for axonpciregs\n",
+			 __FUNCTION__);
+		rc = -ENODEV;
+		goto fail1;
+	} else {
+		priv->pcie_reg_base_addr =
+			ioremap(priv->pcieregs_paddr + PCIE_UTL_OFFSET,
+				PCIE_UTL_SIZE + PCIE_CFG_SIZE);
+		priv->pcie_cnfg_base_addr =
+			priv->pcie_reg_base_addr + PCIE_UTL_SIZE;
+	}
+
+	regions = of_get_property(ofdev->node, "outbound-regions", NULL);
+	if (regions) {
+		dev_dbg(&priv->ofdev->dev,
+			"Outbound-regions found in device tree\n");
+		priv->pcie_mem0_base = regions[0];
+		priv->pcie_mem0_len  = regions[1];
+		priv->pcie_mem1_base = regions[4];
+		priv->pcie_mem1_len  = regions[5];
+		priv->pcie_mem2_base = regions[8];
+		priv->pcie_mem2_len  = regions[9];
+		pr_debug(".Mem0base: %p\n", (void *)priv->pcie_mem0_base);
+		pr_debug(".Mem1base: %p\n", (void *)priv->pcie_mem1_base);
+		pr_debug(".Mem2base: %p\n", (void *)priv->pcie_mem2_base);
+	} else {
+		rc = -ENODEV;
+		dev_info(&priv->ofdev->dev, "%s: No of_dev tree entries\n",
+			 __FUNCTION__);
+		goto fail1;
+	}
+
+	/* map axon c3po registers (including scratchpad) */
+	priv->c3poregs_paddr = priv->res.start & BE_AXON_BASE_PADDR_MASK;
+
+	if (!request_mem_region((priv->c3poregs_paddr + BE_DEV_BASE_OFFSET +
+				 DEV_C3PO_OFFSET),
+				DEV_C3PO_SIZE,
+				"axon_regs"))	{
+		dev_info(&priv->ofdev->dev,
+			 "%s: request_mem_region failed for axon regs\n",
+			 __FUNCTION__);
+		rc = -ENODEV;
+		goto fail1;
+	} else {
+		priv->c3po_base_addr =
+			ioremap((priv->c3poregs_paddr +
+				 BE_DEV_BASE_OFFSET + DEV_C3PO_OFFSET),
+				DEV_C3PO_SIZE);
+		priv->scratchpad = (struct scratchpad_area __iomem *)
+			(priv->c3po_base_addr + C3PO_SP_OFFSET);
+		initialize_scratchpad(priv);
+	}
+
+/* map pcie dcr register area */
+	priv->dcrreg_base = dcr_resource_start(ofdev->node, 0);
+	priv->dcrreg_size = dcr_resource_len(ofdev->node, 0);
+	priv->dcrreg_host = dcr_map(ofdev->node,
+				    priv->dcrreg_base,
+				    priv->dcrreg_size);
+
+	if (!DCR_MAP_OK(priv->dcrreg_host)) {
+		dev_info(&priv->ofdev->dev,
+			 "%s: failed to map pcie DCRs !\n", __FUNCTION__);
+		priv->dcrreg_base = 0;
+		rc = -ENODEV;
+		goto fail1;
+	}
+
+	return rc;
+
+fail1:
+	pr_info("%s: release pcieregs 0x%x bytes at %p\n",
+		__FUNCTION__,
+		(unsigned int) priv->pcieregs_plen,
+		(void *) priv->pcieregs_paddr);
+
+	if (priv->pcie_reg_base_addr)
+		iounmap(priv->pcie_reg_base_addr);
+	release_mem_region(priv->pcieregs_paddr,
+			   priv->pcieregs_plen);
+
+	pr_info("%s: release c3po 0x%lx bytes at %p\n",
+		__FUNCTION__,
+		(long unsigned int) DEV_C3PO_SIZE,
+		(void *) priv->c3poregs_paddr);
+
+	if (priv->c3po_base_addr)
+		iounmap(priv->c3po_base_addr);
+	release_mem_region(priv->c3poregs_paddr +
+			   (BE_DEV_BASE_OFFSET + DEV_C3PO_OFFSET),
+			   DEV_C3PO_SIZE);
+	return rc;
+
+}
+
+/**
+ * reslease_resources - unmap and release memory regions
+ * @priv:  instance struct for device
+ */
+static void release_resources(struct axonep_instance *priv)
+{
+	pr_info("%s: pcieregs: 0x%x bytes @ %p\n",
+		__FUNCTION__,
+		(unsigned int) priv->pcieregs_plen,
+		(void*) priv->pcieregs_paddr);
+	if (priv->pcie_reg_base_addr)
+		iounmap(priv->pcie_reg_base_addr);
+	release_mem_region(priv->pcieregs_paddr,
+			   priv->pcieregs_plen);
+
+	pr_info("%s: c3po: 0x%x bytes @ %p\n",
+		__FUNCTION__,
+		(unsigned int) DEV_C3PO_SIZE,
+		(void *) (priv->c3poregs_paddr +
+			  BE_DEV_BASE_OFFSET + DEV_C3PO_OFFSET));
+	if (priv->c3po_base_addr)
+		iounmap(priv->c3po_base_addr);
+	release_mem_region(priv->c3poregs_paddr +
+			   (BE_DEV_BASE_OFFSET + DEV_C3PO_OFFSET),
+			   DEV_C3PO_SIZE);
+
+	if (priv->dcrreg_base) {
+		dcr_unmap(priv->dcrreg_host,
+			  priv->dcrreg_base);
+		priv->dcrreg_base = 0;
+	}
+}
+
+/**
+ * axonep_get_regs
+ * @handle: axonep_instance struct
+ * @length: size of mem area
+ */
+void __iomem *axonep_get_regs(struct axonep_instance *handle, int
*length)
+{
+	if (handle) {
+		if (0) axonep_dump_scratchpad(handle, "axonep_get_regs");
+		if (!handle->rmem2.ptr)
+			map_remote_memory(handle);
+		*length = PCIE_UTL_SIZE + PCIE_CFG_SIZE;
+		return handle->pcie_reg_base_addr;
+	}
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(axonep_get_regs);
+
+/**
+ * axonep_get_rmem2_base
+ * @handle: axonep_instance struct
+ * @length: size of mem area
+ */
+u64 axonep_get_rmem2_paddr(struct axonep_instance *handle,
+			   int *length)
+{
+	if (handle) {
+		if (!handle->rmem2.ptr)
+			map_remote_memory(handle);
+		*length = handle->rmem2.size;
+		return handle->rmem2.paddr;
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(axonep_get_rmem2_paddr);
+
+/**
+ * axonep_get_mem - get local axon shared memory region
+ * @handle: handle to axon instance
+ * @length: length of memory region
+ * @lmem: local memory region to retrieve
+ */
+void  *axonep_get_mem(struct axonep_instance *handle,
+		      int *length,
+		      enum axonep_local_memory_region lmem)
+{
+	*length = 0;
+	if (!handle)
+		return NULL;
+	if (!handle->rmem2.ptr)
+		map_remote_memory(handle);
+	switch(lmem) {
+	case MEM0_REGION:
+		*length = handle->mem0.size;
+		return handle->mem0.ptr;
+	case MEM2_REGION:
+		*length = handle->mem2.size;
+		return handle->mem2.ptr;
+	}
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(axonep_get_mem);
+
+
+/**
+ * axonep_get_rmem - get remote axon shared memory region
+ * @handle: handle to axon instance
+ * @length: length of memory region
+ * @rmem: remote memory region to retrieve
+ */
+void __iomem *axonep_get_rmem(struct axonep_instance *handle,
+			      int *length,
+			      enum axonep_remote_memory_region rmem)
+{
+	*length = 0;
+	if (!handle)
+		return NULL;
+	if (!handle->rmem2.ptr)
+		map_remote_memory(handle);
+	switch(rmem) {
+	case RMEM0_REGION:
+		*length = handle->rmem0.size;
+		return handle->rmem0.ptr;
+	case  RMEM2_REGION:
+		*length = handle->rmem2.size;
+		return handle->rmem2.ptr;
+	}
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(axonep_get_rmem);
+
+/**
+ * axonep_probe - called during of registration
+ * @ofdev: of device tree entry
+ * @match: matching of device id;
+ */
+static int __devinit axonep_of_probe(struct of_device *ofdev,
+				     const struct of_device_id *match)
+{
+	int		 rc = 0, rc2 = 0;
+	int		 current_axon;
+	struct axonep_instance *priv;
+	struct resource	       res;
+	static int	       num_found = 0;
+
+	dev_info(&ofdev->dev, "%s:: Found %s\n", __FUNCTION__, MODULE_NAME);
+
+	rc = of_address_to_resource(ofdev->node, 0, &res);
+	if (rc) {
+		dev_info(&ofdev->dev, "%s: of_address_to_resource() failed "
+			 "errno = %d\n",
+			 __FUNCTION__, rc);
+		return -ENODEV;
+	}
+
+	/*  allocate a private struct for each axon */
+	current_axon = num_found++;
+	priv = (struct axonep_instance *)
+		kzalloc(sizeof(struct axonep_instance), GFP_KERNEL);
+	if (!priv) {
+		dev_info(&ofdev->dev, "%s: no memory\n", __FUNCTION__);
+		rc = -ENOMEM;
+		goto error;
+	}
+	priv->instance = current_axon;
+	priv->ofdev = ofdev;
+	priv->res   = res;
+	list_add_tail(&(priv->list), &axonep_list);
+
+	dev_set_drvdata(&ofdev->dev, (void *) priv);
+
+	/* chip resources */
+	rc = request_resources(priv, ofdev);
+	if (rc) {
+		dev_info(&ofdev->dev, "%s: unable to get resources\n",
+			 __FUNCTION__);
+		rc = -ENODEV;
+		goto error;
+	}
+
+	/* memory to export */
+	rc = allocate_exported_mem(priv);
+	if (rc) {
+		dev_info(&ofdev->dev, "%s: unable to get exported memory\n",
+			 __FUNCTION__);
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	/* remote resources - this will fail if the other side is
+	 * not loaded  */
+	rc2 = map_remote_memory(priv);
+	if (rc2)
+		dev_info(&ofdev->dev, "axonep:  unable to get remote memory, "
+			 "try later\n");
+
+	dev_info(&ofdev->dev, "Probe of %s.%d on %s succeeded (%p)"
+		 "with rc: %d \n",
+		 MODULE_NAME,	current_axon,
+		 ofdev->node->full_name, priv, rc);
+error:
+	return rc;
+}
+
+/**
+ * axonep_of_remove - remove axonep instance
+ */
+static int __devexit axonep_of_remove(struct of_device *ofdev)
+{
+	struct axonep_instance *priv;
+	priv = dev_get_drvdata(&ofdev->dev);
+
+	/* clean up */
+	clear_scratchpad(priv);
+	unmap_remote_memory(priv);
+	free_exported_memory(priv);
+	release_resources(priv);
+
+	pr_debug("%s: remove axonep.#%d\n", __FUNCTION__, priv->instance);
+	return 0;
+}
+
+/**
+ * open firmware device id, and driver struct
+ */
+static struct of_device_id pcie_device_id[] = {
+	{
+		.type = "pcie-endpoint",
+#ifdef	 OF_COMPATIBLE
+		.compatible = "ibm,axon-pciep"	  /* qs22 only */
+#endif
+	},
+	{}
+};
+
+static struct of_platform_driver axonep_of_driver = {
+	.owner		= THIS_MODULE,
+	.name		= MODULE_NAME,
+	.match_table	= pcie_device_id,
+	.probe		= axonep_of_probe,
+	.remove		= axonep_of_remove
+};
+
+/**
+ * axonep_module_init - initialize the axon base module
+ *
+ */
+static int __init axonep_module_init(void)
+{
+	int rc1,rc2;
+	pr_info("%s: init mem0_size 0x%x, mem2_size 0x%x\n",
+		__FUNCTION__, mem0_size, mem2_size);
+
+	INIT_LIST_HEAD(&axonep_list);
+	rc1 = bus_register(&axonep_bus_type);
+	if ( rc1 < 0 ) {
+		pr_info("%s: could not register axon bus %d\n",
+			__FUNCTION__, rc1);
+		return -ENODEV;
+	}
+	rc2 = of_register_platform_driver(&axonep_of_driver);
+	if (rc2 < 0) {
+		pr_info("%s: could not register axon  (%d)\n",
+			__FUNCTION__, rc2);
+		bus_unregister( &axonep_bus_type);
+		return -ENODEV;
+	}
+	return 0;
+}
+
+module_init(axonep_module_init);
+
+/**
+ * axonep_module_exit
+ */
+static void __exit axonep_module_exit(void)
+{
+	/* free device instances */
+	struct list_head *pos, *next;
+	list_for_each_safe(pos, next, &axonep_list) {
+		struct axonep_instance *ipriv;
+		ipriv = list_entry(pos, struct axonep_instance, list);
+		list_del(&ipriv->list);
+		kfree(ipriv);
+	}
+
+	/* unregister my bus and myself form the of bus */
+	bus_unregister(&axonep_bus_type);
+	of_unregister_platform_driver(&axonep_of_driver);
+
+	pr_debug("%s: module removed\n", __FUNCTION__);
+
+	return;
+}
+module_exit(axonep_module_exit)
+
+MODULE_AUTHOR("<hbbolen at us.ibm.com>");
+MODULE_VERSION("0.0.2b");
+MODULE_DESCRIPTION("Provides Axon Bus support");
+MODULE_LICENSE("GPL v2");
+





More information about the cbe-oss-dev mailing list