[Cbe-oss-dev] Axon End Point Driver: request for comment
brettb at wingedlizard.homeip.net
brettb at wingedlizard.homeip.net
Tue Apr 1 11:42:58 EST 2008
From: Brett Bolen
This is a patch for a new 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