All of lore.kernel.org
 help / color / mirror / Atom feed
From: eajames@linux.vnet.ibm.com
To: openbmc@lists.ozlabs.org
Cc: joel@jms.id.au, alistair@popple.id.au, benh@kernel.crashing.org,
	"Edward A. James" <eajames@us.ibm.com>
Subject: [PATCH linux v1 6/8] drivers: fsi: i2c: probe fsi device for i2c client
Date: Thu,  2 Feb 2017 17:25:59 -0600	[thread overview]
Message-ID: <1486077964-11346-4-git-send-email-eajames@linux.vnet.ibm.com> (raw)
In-Reply-To: <1486077964-11346-1-git-send-email-eajames@linux.vnet.ibm.com>

From: "Edward A. James" <eajames@us.ibm.com>

Signed-off-by: Edward A. James <eajames@us.ibm.com>
---
 drivers/fsi/i2c/Makefile   |   2 +-
 drivers/fsi/i2c/iic-fsi.c  | 464 ++++++++++++++++++++++++++++++++++++++++++++-
 drivers/fsi/i2c/iic-mstr.c | 429 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 893 insertions(+), 2 deletions(-)
 create mode 100644 drivers/fsi/i2c/iic-mstr.c

diff --git a/drivers/fsi/i2c/Makefile b/drivers/fsi/i2c/Makefile
index f9f9048..b1f28a1 100644
--- a/drivers/fsi/i2c/Makefile
+++ b/drivers/fsi/i2c/Makefile
@@ -1 +1 @@
-obj-$(CONFIG_FSI_I2C) += iic-fsi.o
+obj-$(CONFIG_FSI_I2C) += iic-fsi.o iic-mstr.o
diff --git a/drivers/fsi/i2c/iic-fsi.c b/drivers/fsi/i2c/iic-fsi.c
index 53be538..3a9b25d 100644
--- a/drivers/fsi/i2c/iic-fsi.c
+++ b/drivers/fsi/i2c/iic-fsi.c
@@ -40,6 +40,56 @@ static const char iic_fsi_version[] = "3.0";
 
 int iic_fsi_probe(struct device *dev);
 int iic_fsi_remove(struct device *dev);
+void iic_fsi_shutdown(struct device *dev);
+int iic_fsi_suspend(struct device *dev);
+int iic_fsi_resume(struct device *dev);
+static void iic_eng_release(struct kobject* kobj);
+
+/* callback function for when the reference count for an engine reaches 0 */
+static void iic_eng_release(struct kobject* kobj)
+{
+	iic_eng_t* eng = container_of(kobj, iic_eng_t, kobj);
+	struct device* dev = eng->dev;
+	unsigned long flags;
+
+	IENTER();
+
+	/*remove all busses associated with the engine */
+	spin_lock_irqsave(&eng->lock, flags);
+	while(eng->busses){
+		iic_bus_t* temp = eng->busses;
+		eng->busses = temp->next;
+		iic_delete_bus(iic_fsi_class, temp);
+	}
+
+	eng->enabled = 0x0ULL;
+	spin_unlock_irqrestore(&eng->lock, flags);
+
+
+	/* providing an arch specific cleanup routine is optional.
+	 * if not specified, use the default.
+	 */
+	if (iic_eng_ops_is_vaild(eng->ops)) {
+		if(eng->ops->cleanup_eng)
+		{
+			eng->ops->cleanup_eng(eng);
+		}
+		else
+		{
+			IDBGd(0, "free engine\n");
+			kfree(eng);
+		}
+	}
+
+	dev_set_drvdata(dev, 0);
+	kobject_put(&dev->kobj);
+
+	IEXIT(0);
+}
+
+static struct kobj_type iic_eng_ktype = {
+	.release = iic_eng_release,
+};
 
 int readb_wrap(iic_eng_t* eng, unsigned int addr, unsigned char *val, 
 	       iic_ffdc_t** ffdc)
@@ -157,6 +207,133 @@ static const struct fsi_driver i2c_drv = {
 	}
 };
 
+/* Adds ports to the eng->buses SLL.  Access to the SLL must be protected since
+ * iic_fsi_remove or iic_eng_release could be called asynchronously and they
+ * also traverse/modify the SLL.
+ */
+int iic_add_ports(iic_eng_t* eng, uint64_t ports)
+{
+	uint64_t ports_left;
+	int bus_num = 0;
+	unsigned long flags;
+	iic_bus_t* new_bus = 0;
+	int minor = 0;
+	char name[64];
+	int rc = 0;
+	struct fsi_device *fdev = to_fsi_dev(eng->dev);
+
+	IENTER();
+
+	IFLDi(3, "Adding ports[0x%08x%08x] to eng[0x%08x]",
+	      (uint32_t)(ports >> 32),
+	      (uint32_t)ports,
+	      eng->id);
+
+	/* walk the mask adding master ports as needed */
+	for(ports_left = ports; ports_left; ports_left = ports >> ++bus_num)
+	{
+		if(!(ports_left & 0x1))
+			continue;
+
+
+		if( minor < 0 ) {
+			IFLDe(1, "bb_get_minor %d", minor);
+			rc = minor;
+			goto exit;
+		}
+
+		sprintf(name, "i2cfsi%02d", bus_num);
+
+		/* results in hotplug event for each master bus */
+		new_bus = iic_create_bus(iic_fsi_class, eng,
+						MKDEV(MAJOR(iic_devnum_start), minor),
+						name, bus_num, minor);
+		if(!new_bus)
+		{
+			IFLDe(1, "iic_create_bus failed on eng %d", eng->id);
+			rc = -ENODEV;
+			goto exit;
+		}
+
+		/* atomically insert the new bus into the SLL unless
+		 * iic_fsi_remove has been started. 
+		 */
+		spin_lock_irqsave(&eng->lock, flags);
+		if(test_bit(IIC_ENG_REMOVED, &eng->flags))
+		{
+			/* if iic_fsi_remove has been started then
+			 * don't add this bus to the SLL and delete it.
+			 * Previously added buses will be removed by
+			 * iic_eng_release
+			 */
+			rc = -ENODEV;
+			iic_delete_bus(iic_fsi_class, new_bus);
+			spin_unlock_irqrestore(&eng->lock, flags);
+			goto exit;
+		}
+		else
+		{
+			new_bus->next = eng->busses;
+			eng->busses = new_bus;
+			eng->enabled |= 0x1ULL << bus_num;
+		}
+		spin_unlock_irqrestore(&eng->lock, flags);
+
+		minor++;
+	}
+
+exit:
+	IEXIT(rc);
+	return rc;
+}
+
+/* Removes ports from the eng->buses SLL.  Access to the SLL must be protected
+ * since iic_fsi_remove or iic_eng_release could be called at same time as this
+ * and they also traverse/modify the SLL.
+ */
+int iic_del_ports(iic_eng_t* eng, uint64_t ports)
+{
+	unsigned long flags;
+	iic_bus_t* abusp;
+	iic_bus_t** p_abusp;
+
+	IENTER();
+
+	IFLDi(3, "removing ports[0x%08x%08x] from eng[0x%08x]",
+	      (uint32_t)(ports >> 32),
+	      (uint32_t)ports,
+	      eng->id);
+
+	/* walk unordered SLL and delete bus if it is in the ports bit mask */
+	spin_lock_irqsave(&eng->lock, flags);
+	if(test_bit(IIC_ENG_REMOVED, &eng->flags))
+		goto exit;
+	p_abusp = &eng->busses;
+	abusp = *p_abusp;
+	while(abusp)
+	{
+		if(ports & (0x1ULL << abusp->port))
+		{
+			/* found a match, remove it */
+			*p_abusp = abusp->next;
+			eng->enabled &= ~(0x1ULL << abusp->port);
+			device_destroy(iic_fsi_class, abusp->devnum);
+			iic_delete_bus(iic_fsi_class, abusp);
+		}
+		else
+		{
+			/* not a match, skip to next one */
+			p_abusp = &abusp->next;
+		}
+		abusp = *p_abusp;
+	}
+
+exit:
+	spin_unlock_irqrestore(&eng->lock, flags);
+	IEXIT(0);
+	return 0;
+}
+
 /*
  * Called when an FSI IIC engine is plugged in.  
  * Causes creation of the /dev entry.
@@ -165,7 +342,86 @@ static const struct fsi_driver i2c_drv = {
  */
 int iic_fsi_probe(struct device *dev)
 {
-	return 0;
+	uint64_t new_ports = 0;
+	int rc = 0;
+	struct fsi_device *dp = to_fsi_dev(dev);
+	iic_eng_t* eng = 0;
+
+	IENTER();
+
+	eng = (iic_eng_t*)kmalloc(sizeof(iic_eng_t), GFP_KERNEL);
+
+	if(!eng)
+	{
+		IFLDe(0, "Couldn't malloc engine\n");
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	memset(eng, 0, sizeof(*eng));
+	iic_init_eng(eng);
+	set_bit(IIC_ENG_BLOCK, &eng->flags); //block until resumed
+	eng->id = 0x00F5112C;
+	IFLDi(1, "PROBE    eng[%08x]", eng->id);
+	eng->ra = &fsi_reg_access;
+	IFLDd(1, "vaddr=%#08lx\n", eng->base);
+	eng->dev = dev;
+	// The new kernel now requires 2 arguments
+	kobject_init(&eng->kobj, &iic_eng_ktype); //ref count = 1
+	eng->ops = iic_get_eng_ops(FSI_ENGID_I2C);
+	if(!eng->ops)
+	{
+		IFLDi(1, "support for engine type 0x%08x not loaded.\n",
+				FSI_ENGID_I2C);
+		rc = -ENODEV;
+		goto error;
+	}
+	
+	/* Register interrupt handler with the kernel */	
+	IDBGd(0, "requesting irq\n");
+	dp->irq_handler = eng->ops->int_handler;
+
+	IFLDd(1, "irq  = %d\n", eng->irq);
+
+	new_ports = 0xFFFULL;
+	set_bit(IIC_ENG_P8_Z8_CENTAUR, &eng->flags);
+
+
+	/* Add master and slave ports to the engine */
+	rc = iic_add_ports(eng, new_ports);
+	if(rc)
+		goto error;
+
+	dev_set_drvdata(dev, eng);
+	eng->private_data = 0; //unused
+
+
+	/* set the callback function for when the eng ref count reaches 0 */
+	kobject_get(&eng->dev->kobj);
+
+	iic_fsi_resume(dev);
+
+error:
+	if(rc)
+	{
+		IFLDi(1, "IIC: iic_fsi_probe failed: %d\n", rc);
+		while(eng && eng->busses){
+			iic_bus_t* temp = eng->busses;
+			eng->busses = temp->next;
+			iic_delete_bus(iic_fsi_class, temp);
+		}
+		if(eng)
+		{
+			kfree(eng);
+		}
+	}
+	else
+	{
+		IFLDd(1, "engine 0x%08X probed.\n", eng->id);
+	}
+
+	IEXIT(rc);
+	return rc;
 }
 
 /* This function is called when a link is removed or the driver is unloaded.
@@ -174,9 +430,215 @@ int iic_fsi_probe(struct device *dev)
  */
 int iic_fsi_remove(struct device* dev)
 {
+	int rc = 0;
+	iic_bus_t* bus;
+	iic_eng_t* eng = (iic_eng_t*)dev_get_drvdata(dev);
+	unsigned long flags;
+
+	IENTER();
+       
+	if(!eng)
+	{
+		IFLDe(1, "iic_fsi_remove called with bad dev: %p\n", dev);
+		rc = -1;
+		goto error;
+	}
+
+	/* set ENG_REMOVED flag so that aborted operations have status
+	 * set to ENOLINK (lost fsi link) instead of ENODEV (no lbus).
+	 */
+	set_bit(IIC_ENG_REMOVED, &eng->flags);
+
+	iic_fsi_suspend(dev); //ignore rc
+
+	IFLDi(1, "REMOVE   eng[%08x]\n", eng->id);
+
+	/* Clean up device files immediately, don't wait for ref count */
+	spin_lock_irqsave(&eng->lock, flags);
+	bus = eng->busses;
+	while(bus)
+	{
+		/* causes hot unplug event */
+		device_destroy(iic_fsi_class, bus->devnum);
+		bus = bus->next;
+	}
+	spin_unlock_irqrestore(&eng->lock, flags);
+
+	/* cleans up engine and bus structures if ref count is zero */
+	kobject_put(&eng->kobj);
+	
+error:
+	IEXIT(0);
 	return 0;
 }
 
+/* This function is called when a link is removed or the driver is unloaded.
+ * It's job is to quiesce and disable the hardware if possible and unregister 
+ * interrupts. It always precedes the remove function.
+ *
+ * The device may be in the resumed or suspended state when this function is
+ * called.
+ *
+ * This function is no longer called for mcp5
+ */
+void iic_fsi_shutdown(struct device *dev)
+{
+	int rc = 0;
+	iic_eng_t* eng = (iic_eng_t*)dev_get_drvdata(dev);
+	struct fsi_device* fsidev = to_fsi_dev(dev);
+
+	IENTER();
+	if(!eng || !eng->ops)
+	{
+		rc = -1;
+		goto error;
+	}
+	IFLDi(1, "SHUTDOWN eng[%08x]\n", eng->id);
+
+	/* set ENG_REMOVED flag so that aborted operations have status
+	 * set to ENOLINK (lost fsi link) instead of ENODEV (no lbus).
+	 */
+	set_bit(IIC_ENG_REMOVED, &eng->flags);
+
+	iic_fsi_suspend(dev);
+	
+error:
+	if(rc)
+	{
+		IFLDe(1, "iic_fsi_shutdown failed: %d\n", rc);
+	}
+	IEXIT(0);
+	return;
+}
+
+/* This function is called when we loose ownership or are preparing to give
+ * up ownership of the local bus.  If we still own lbus, then we try to
+ * gracefully halt any pending transfer.  No hot unplug events are caused by
+ * this function.
+ *
+ * This function also is called from iic_fsi_remove.
+ *
+ * Note: In the case where we lose local bus and then loose the link or
+ * get rmmod'd, this function could be called twice without a resume
+ * in the middle!
+ */
+int iic_fsi_suspend(struct device *dev)
+{
+	int rc = 0;
+	iic_eng_t* eng = (iic_eng_t*)dev_get_drvdata(dev);
+	unsigned long xfr_status;
+
+	IENTER();
+	if(!eng)
+	{
+		rc = -1;
+		goto error;
+	}
+
+	IFLDi(2, "SUSPEND  eng[%08x]\n", eng->id);
+
+	/* Prohibit new engine operations until resumed*/
+	set_bit(IIC_ENG_BLOCK, &eng->flags);
+
+
+	/* Terminate pending operations (set status to ENODEV)
+	 * If we have access to the engine, halt any transfers 
+	 * and Disable engine interrupts.
+	 */
+
+	/* lost lbus -> ENODEV, hot unplug -> ENOLINK */
+	xfr_status = test_bit(IIC_ENG_REMOVED, &eng->flags)? -ENOLINK: -ENODEV;
+	iic_abort_all(eng, 0, xfr_status);
+
+	/* disable slave transfers */
+	if (iic_eng_ops_is_vaild(eng->ops)) {
+		if(eng->ops->slv_off) {
+			eng->ops->slv_off(eng, 0);
+		}
+	}
+
+	/* prohibit hw access using this engine object */
+	set_bit(IIC_NO_ACCESS, &eng->flags);
+	
+	/* disable interrupt handler if not already done */
+	if(test_and_clear_bit(IIC_ENG_RESUMED, &eng->flags))
+	{
+		fsi_disable_irq(to_fsi_dev(dev)); 
+	}
+
+error:	
+	IEXIT(rc);
+	return rc;
+}
+
+/* This function is called when we are given (back) ownership of the local
+ * bus.
+ */
+int iic_fsi_resume(struct device *dev)
+{
+	iic_ffdc_t* ffdc = 0;
+	int rc = 0;
+	iic_eng_t* eng = 0;
+	struct fsi_device *dp = to_fsi_dev(dev);
+
+	IENTER();	
+	// The device structure has changed for the new kernel.
+	// The member driver_data has been deprecated.
+	eng = (iic_eng_t*)dev_get_drvdata(dev);
+
+	if(!eng || !iic_eng_ops_is_vaild(eng->ops))
+	{
+		rc = -EIO;
+		goto error;
+	}
+
+	IFLDi(1, "RESUME   eng[%08x]\n", eng->id);
+
+	eng->bus_speed = 20833333;
+	IFLDd(1, "eng->bus_speed=%ld\n", eng->bus_speed);
+
+	/* Reset the engine */
+	rc = eng->ops->reset_eng(eng, &ffdc);
+	if(rc)
+	{
+		goto error;
+	}
+
+	/* Give the engine time to determine the state of the bus before
+	 * allowing transfers to begin after resetting the engine.
+	 */
+	udelay(200);
+
+	/* Initialize the engine */
+	rc = eng->ops->init(eng, &ffdc);
+	if(rc)
+	{
+		goto error;
+	}
+
+	/* Enable interrupt handler in the kernel */	
+	IDBGd(0, "enabling irq\n");
+	rc = fsi_enable_irq(dp);
+	if(rc)
+	{
+		IFLDe(1, "fsi_enable_irq failed rc=%d\n", rc);
+		goto error;
+	}
+
+	set_bit(IIC_ENG_RESUMED, &eng->flags);
+
+	/* allow operations to be submitted */
+	clear_bit(IIC_ENG_BLOCK, &eng->flags);
+	goto exit;
+
+error:
+	IFLDe(1, "iic_fsi_resume failed, rc=%d\n", rc);
+exit:
+	IEXIT(rc);
+	return rc;
+}
+
+
 /*
  * Initialize this module.  Creates a class for fsi connected iic devices and
  * allocates device numbers for them.
diff --git a/drivers/fsi/i2c/iic-mstr.c b/drivers/fsi/i2c/iic-mstr.c
new file mode 100644
index 0000000..de05e5d
--- /dev/null
+++ b/drivers/fsi/i2c/iic-mstr.c
@@ -0,0 +1,429 @@
+/*
+ *   Copyright (c) International Business Machines Corp., 2006, 2009, 2010
+ *
+ *   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
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/sysfs.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <asm/uaccess.h>
+#include <linux/delay.h>
+#include <linux/param.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <asm/page.h>
+#include <linux/pagemap.h>
+#include <linux/aio.h>
+#include "iic-int.h"
+#include <linux/fsi.h>
+#include <linux/time.h>
+#include <asm/io.h>
+#include <linux/moduleparam.h>
+#include <linux/interrupt.h>
+
+typedef struct
+{
+	unsigned long type;
+	iic_eng_ops_t *ops;
+	struct list_head list;
+} iic_eng_type_t;
+
+typedef struct
+{
+	unsigned long type;
+	iic_bus_t * bus;
+	struct list_head list;
+} iic_bus_type_t;
+
+/* This is a DLL of engine ops for the engines that are supported */
+LIST_HEAD(iic_eng_type_list);
+
+/* DLL for bus ops for the busses that are supported */
+LIST_HEAD(iic_bus_type_list);
+
+iic_opts_t iic_dflt_opts =
+{
+	.xfr_opts =
+	{
+		.dev_addr = 0,		/* 8 bits with LSB ignored */
+		.dev_width = 0,		/* offset width in bytes */
+		.offset = 0,		/* offset in bytes */
+		.inc_addr = 0,		/* address increment mask */
+		.timeout = 5000,	/* transfer timeout in milliseconds */
+		.wdelay = 0,		/* write delay in milliseconds */
+		.rdelay = 0,		/* read delay in milliseconds */ 
+		.wsplit = 0,		/* write split chunk size (bytes) */
+		.rsplit = 0,		/* read split chunk size (bytes) */
+		.flags = 0,
+	},
+	.recovery =
+	{
+		.redo_pol = 0,
+		.redo_delay = 0,
+	}
+};
+
+static const char iic_mstr_version[] = "3.0";
+
+/* save off the default cdev type pointer so we can call the default cdev
+ * release function in our own bus release function
+ */
+static struct kobj_type* cdev_dflt_type = 0;
+struct kobj_type iic_bus_type;
+
+/* funtion called when cdev object (embedded in bus object) ref count
+ * reaches zero.  (prevents cdev memory from being freed to early)
+ */
+void iic_bus_release(struct kobject* kobj)
+{
+	struct cdev *p = container_of(kobj, struct cdev, kobj);
+	iic_bus_t* bus = container_of(p, iic_bus_t, cdev);
+
+	IFLDi(1, "deleting bus[%08lx]\n", bus->bus_id);
+	if(cdev_dflt_type && cdev_dflt_type->release)
+		cdev_dflt_type->release(kobj);
+	kfree(bus);
+}
+
+int iic_open(struct inode* inode, struct file* filp);
+int iic_release(struct inode* inode, struct file* filp);
+ssize_t iic_read(struct file *file, char __user *buf, size_t count,
+		 loff_t *offset);
+ssize_t iic_write(struct file *file, const char __user *buf, size_t count,
+	       	  loff_t *offset);
+ssize_t iic_aio_read(struct kiocb *iocb, const struct iovec *buf, unsigned long count, loff_t pos);
+ssize_t iic_aio_write(struct kiocb *iocb, const struct iovec *buf, unsigned long count, loff_t pos);
+long iic_ioctl(struct file *file, unsigned int cmd,
+              unsigned long arg);
+static int iic_mmap(struct file* file, struct vm_area_struct* vma);
+int iic_xfr(iic_client_t* client, char* buf, size_t count, loff_t* offset, 
+		char read_flag);
+loff_t iic_llseek(struct file *filp, loff_t off, int whence);
+
+struct file_operations iic_fops = {
+	.owner = THIS_MODULE,
+	.open = iic_open,
+	.release = iic_release,
+	.read = iic_read,
+	.write = iic_write,
+	.unlocked_ioctl = iic_ioctl,
+	.llseek = iic_llseek,
+	.mmap = iic_mmap,
+};
+
+static iic_bus_t * iic_get_bus(unsigned long port, unsigned long type)
+{
+	iic_bus_type_t* iterator;
+	int found = 0;
+
+	IENTER();
+
+	list_for_each_entry(iterator, &iic_bus_type_list, list)
+	{
+		if((iterator->type == type) && (iterator->bus->port == port))
+		{
+			found = 1;
+			break;
+		}
+	}
+
+	IEXIT(0);
+	return (found == 1)? iterator->bus: NULL;
+}
+
+/* Abort all pending xfrs for a client, or if client is 0, abort all
+ * pending xfrs for the engine.  
+ */
+int iic_abort_all(iic_eng_t* eng, iic_client_t* client, int status)
+{
+	return 0;
+}
+EXPORT_SYMBOL(iic_abort_all);
+
+int iic_register_eng_ops(iic_eng_ops_t* new_ops, unsigned long type)
+{
+	iic_eng_type_t* new_type = (iic_eng_type_t*)
+		kmalloc(sizeof(iic_eng_type_t), GFP_KERNEL);
+	IENTER();
+	if(!new_type)
+	{
+		return -ENOMEM;
+	}
+	
+	new_type->type = type;
+	new_type->ops = new_ops;
+
+	/* Add this eng type object to beginning of engine type list*/
+	list_add(&new_type->list, &iic_eng_type_list);
+	IDBGd(1, "eng type %08lx registered\n", type);
+	IEXIT(0);
+	return 0;
+}
+EXPORT_SYMBOL(iic_register_eng_ops);
+
+int iic_unregister_eng_ops(unsigned long type)
+{
+	iic_eng_type_t *iterator, *found;
+	IENTER();
+	found = 0;
+
+	list_for_each_entry(iterator, &iic_eng_type_list, list)
+	{
+		if(iterator->type == type)
+		{
+			found = iterator;
+			break;
+		}
+	}
+	if(found)
+	{
+		list_del(&found->list);
+		kfree(found);
+		IDBGd(1, "engine type %08lx unregistered\n", type);
+	}
+	IEXIT(0);
+	return 0;
+}
+EXPORT_SYMBOL(iic_unregister_eng_ops);
+
+void iic_register_bus(iic_bus_t * new_bus, unsigned long type)
+{
+        iic_bus_type_t* new_type = (iic_bus_type_t*)
+                kmalloc(sizeof(iic_bus_type_t), GFP_KERNEL);
+
+        IENTER();
+
+        new_type->type = type;
+        new_type->bus = new_bus;
+        list_add(&new_type->list, &iic_bus_type_list);
+
+        IEXIT(0);
+}
+EXPORT_SYMBOL(iic_register_bus);
+
+void iic_unregister_bus(iic_bus_t *bus, unsigned long type)
+{
+        iic_bus_type_t *iterator, *temp;
+
+        IENTER();
+
+        list_for_each_entry_safe(iterator, temp, &iic_bus_type_list, list)
+        {
+                if((iterator->type == type) && (iterator->bus == bus))
+                {
+                        list_del(&iterator->list);
+                        kfree(iterator);
+                }
+        }
+        IEXIT(0);
+}
+EXPORT_SYMBOL(iic_unregister_bus);
+
+void iic_init_eng(iic_eng_t* eng)
+{
+	IENTER();
+	spin_lock_init(&eng->lock);
+	sema_init(&eng->sem, 1);
+	INIT_LIST_HEAD(&eng->xfrq);
+	eng->cur_xfr = 0;
+	iic_lck_mgr_init(&eng->lck_mgr);
+	init_waitqueue_head(&eng->waitq);
+	INIT_WORK(&eng->work, iic_finish_abort);
+	atomic_set(&eng->xfr_num, 0);
+	IEXIT(0);
+}
+EXPORT_SYMBOL(iic_init_eng);
+
+int iic_eng_ops_is_vaild(struct iic_eng_ops *ops)
+{
+	int found = 0;
+	iic_eng_type_t *iterator;
+
+	list_for_each_entry(iterator, &iic_eng_type_list, list)
+	{
+		if(iterator->ops == ops)
+		{
+			found = 1;
+			break;
+		}
+	}
+
+	return found;
+}
+EXPORT_SYMBOL(iic_eng_ops_is_vaild);
+
+struct iic_eng_ops* iic_get_eng_ops(unsigned long type)
+{
+	iic_eng_type_t *iterator;
+	iic_eng_ops_t *found = 0;
+	IENTER();
+
+	/* return the eng ops for the given type of engine */
+	list_for_each_entry(iterator, &iic_eng_type_list, list)
+	{
+		if(iterator->type == type)
+		{
+			found = iterator->ops;
+			break;
+		}
+	}
+	IEXIT((int)found);
+	return found;
+}
+EXPORT_SYMBOL(iic_get_eng_ops);
+
+/* called when an ffdc q for a bus is unlocked */
+void iic_ffdc_q_unlocked(int scope, void* data)
+{
+	iic_eng_t* eng = (iic_eng_t*)data;
+	unsigned long flags;
+	if(eng)
+	{
+		spin_lock_irqsave(&eng->lock, flags);
+		iic_start_next_xfr(eng);
+		spin_unlock_irqrestore(&eng->lock, flags);
+	}
+}
+
+/* Register this bus's minor number with the kernel and add
+ * it to the iic class in sysfs so that a hotplug event is
+ * sent to udev.  The sysfs name needs to be unique because
+ * all entries are placed in the same directory.  udev
+ * will take care of creating the correct /dev name.
+ */
+#define IIC_BUS_MAX_FFDC 4
+iic_bus_t*  iic_create_bus(struct class* classp, iic_eng_t* eng,
+			   dev_t devnum, char* name, unsigned char port,
+			   unsigned long bus_id)
+{
+	int rc = 0;
+	iic_bus_t* bus = 0;
+
+	IENTER();
+
+	if(!eng)
+	{
+		goto exit;
+	}
+	bus = (iic_bus_t*)kmalloc(sizeof(iic_bus_t), GFP_KERNEL);
+	if(!bus)
+	{
+		goto exit;
+	}
+	memset(bus, 0, sizeof(iic_bus_t));
+	bus->port = port;
+	bus->bus_id = bus_id;
+	bus->eng = eng;
+	bus->devnum = devnum;
+	bus->i2c_hz = 400000;
+	cdev_init(&bus->cdev, &iic_fops); // ref count = 1
+	/* since cdev is embedded in our bus structure, override the cdev
+	 * cleanup function with our own so that the bus object doesn't get
+	 * freed until the cdev ref count goes to zero.
+	 */
+	if(!cdev_dflt_type)
+	{
+		cdev_dflt_type = bus->cdev.kobj.ktype;
+		memcpy(&iic_bus_type, cdev_dflt_type, sizeof(iic_bus_type));
+		iic_bus_type.release = iic_bus_release;
+	}
+	bus->cdev.kobj.ktype = &iic_bus_type;
+	kobject_set_name(&bus->cdev.kobj, name);
+	rc = cdev_add(&bus->cdev, devnum, 1);
+	if(rc)
+	{
+		IFLDe(1, "cdev_add failed for bus %08lx\n", bus->bus_id);
+		kobject_put(&bus->cdev.kobj);
+		goto exit_cdev_add;
+	}
+
+	bus->class_dev = device_create(classp, NULL,
+			devnum,
+			bus->eng->dev,
+			(const char *)"%s",
+			name);
+	if(!bus->class_dev)
+	{
+		IFLDe(1, "device create failed, %08lx\n", bus->bus_id);
+		goto exit_class_add;
+	}
+
+	IFLDi(1, "bus[%08lx] created\n", bus->bus_id);
+	goto exit;
+
+exit_q_create:
+	device_destroy(classp, bus->devnum);
+exit_class_add:
+	cdev_del(&bus->cdev);
+exit_cdev_add:
+	bus = 0;
+exit:
+	IEXIT((int)bus);
+	return bus;
+}
+EXPORT_SYMBOL(iic_create_bus);
+
+void iic_delete_bus(struct class* classp, iic_bus_t* bus)
+{
+	IENTER();
+
+	if(!bus)
+	{
+		goto exit;
+	}
+	IFLDi(1, "cleanup bus[%08lx]\n", bus->bus_id);
+	cdev_del(&bus->cdev);
+exit:
+	IEXIT(0);
+	return;
+}
+EXPORT_SYMBOL(iic_delete_bus);
+
+static int __init iic_init(void)
+{
+	int rc = 0;
+
+	IENTER();
+	printk("IIC: base support loaded  ver. %s\n", iic_mstr_version);
+	IEXIT(rc);
+	return rc;
+}
+
+static void __exit iic_exit(void)
+{
+	IENTER();
+	printk("IIC: base support unloaded.\n");
+	IEXIT(0);
+}
+
+static int iic_set_trc_sz(const char* val, struct kernel_param *kp)
+{
+	int rc = param_set_int(val, kp);
+	if(rc)
+		return rc;
+	return 0;
+}
+
+module_init(iic_init);
+module_exit(iic_exit);
+MODULE_AUTHOR("Eddie James <eajames@us.ibm.com>");
+MODULE_DESCRIPTION("Base IIC Driver");
+MODULE_LICENSE("GPL");
-- 
1.8.3.1

  parent reply	other threads:[~2017-02-02 23:26 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-02-02 23:25 [PATCH linux v1 0/8] drivers: fsi: interrupt polling, i2c client eajames
2017-02-02 23:25 ` [PATCH linux v1 4/8] drivers: fsi: Add i2c client driver eajames
2017-02-03  0:56   ` Alistair Popple
2017-02-03  0:58     ` Joel Stanley
2017-02-02 23:25 ` [PATCH linux v1 5/8] drivers: fsi: i2c: Add engine access wrappers eajames
2017-02-02 23:25 ` eajames [this message]
2017-02-02 23:26 ` [PATCH linux v1 7/8] drivers: fsi: i2c: add driver file operations and bus locking eajames
2017-02-02 23:26 ` [PATCH linux v1 8/8] drivers: fsi: i2c: boe engine eajames
2017-02-02 23:26 ` [PATCH v2 1/3] drivers/fsi: Add slave interrupt polling eajames
2017-02-03  1:11   ` Alistair Popple
2017-02-03 16:18     ` eajames
2017-02-03 20:33       ` Benjamin Herrenschmidt
2017-02-02 23:26 ` [PATCH v2 2/3] drivers/fsi: Add Client IRQ Enable / Disable eajames
2017-02-02 23:26 ` [PATCH v2 3/3] drivers/fsi: Add sysfs file to adjust i-poll period eajames
2017-02-03  0:55 ` [PATCH linux v1 0/8] drivers: fsi: interrupt polling, i2c client Joel Stanley
2017-02-03 16:36   ` eajames

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1486077964-11346-4-git-send-email-eajames@linux.vnet.ibm.com \
    --to=eajames@linux.vnet.ibm.com \
    --cc=alistair@popple.id.au \
    --cc=benh@kernel.crashing.org \
    --cc=eajames@us.ibm.com \
    --cc=joel@jms.id.au \
    --cc=openbmc@lists.ozlabs.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.