Skip to contentSkip to conversation

CPM2 i2c driver

PATCH In review Esben Haabendal 2 participants 2 messages Jun 20, 2008

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 CPM2 i2c driver

Hi

Here is an i2c driver for Freescale processors with CPM2. It is based on previous work by Angelos Manousaridis which again were based on a driver from Heiko Schocher.

I am using it on an in-house MPC8270 target, and don't have access to any other boards with CPM2. So if anybody is able to test this driver with a supported board, I would by happy to help out. It would be nice to have it tested and integrated with one or more reference boards, and have the needed dts changed included also.

Best regards,
Esben Haabendal

-------- Original Message --------
Subject: i2c-cpm2.patch
Date: Fri, 20 Jun 2008 11:02:3ga3 +0200
From: Esben Haabendal <[email protected]>
To: [email protected]

arch/powerpc/sysdev/fsl_soc.c +39 -0
index ca54563..662c076 100644
@@ -537,6 +537,45 @@ static int __init fsl_i2c_of_init(void)
 		i++;
 	}
 
+	for_each_compatible_node(np, NULL, "fsl-cpm2-i2c") {
+		struct resource r[2];
+		struct fsl_i2c_platform_data i2c_data;
+		const unsigned char *flags = NULL;
+
+		printk(KERN_INFO "%s: fsl-cpm2-i2c node found\n", __FUNCTION__);
+
+		memset(&r, 0, sizeof(r));
+		memset(&i2c_data, 0, sizeof(i2c_data));
+
+		ret = of_address_to_resource(np, 0, &r[0]);
+		if (ret)
+			goto err;
+		printk(KERN_INFO "%s: r[0] = %x %x\n",
+		       __FUNCTION__, r[0].start, r[0].end);
+
+		of_irq_to_resource(np, 0, &r[1]);
+		printk(KERN_INFO "%s: r[1] = %x %x\n",
+		       __FUNCTION__, r[1].start, r[2].end);
+
+		i2c_dev = platform_device_register_simple(
+			"fsl-cpm2-i2c", i, r, 2);
+		if (IS_ERR(i2c_dev)) {
+			ret = PTR_ERR(i2c_dev);
+			goto err;
+		}
+
+		i2c_data.device_flags = 0;
+
+		ret =
+		    platform_device_add_data(i2c_dev, &i2c_data,
+					     sizeof(struct
+						    fsl_i2c_platform_data));
+		if (ret)
+			goto unreg;
+
+		of_register_i2c_devices(np, i++);
+	}
+
 	return 0;
 
 unreg:
drivers/i2c/busses/Kconfig +10 -0
index 48438cc..3715d12 100644
@@ -305,6 +305,16 @@ config I2C_MPC
 	  This driver can also be built as a module.  If so, the module
 	  will be called i2c-mpc.
 
+config I2C_CPM2
+	tristate "MPC CPM2"
+	depends on I2C && PPC32 && CPM2
+	help
+	  If you say yes to this option, support will be included for the
+	  I2C interface on PPC with CPM2
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called i2c-cpm2.
+
 config I2C_NFORCE2
 	tristate "Nvidia nForce2, nForce3 and nForce4"
 	depends on PCI
drivers/i2c/busses/Makefile +1 -0
index e8c882a..544decb 100644
@@ -55,6 +55,7 @@ obj-$(CONFIG_I2C_VIAPRO)	+= i2c-viapro.o
 obj-$(CONFIG_I2C_VOODOO3)	+= i2c-voodoo3.o
 obj-$(CONFIG_SCx200_ACB)	+= scx200_acb.o
 obj-$(CONFIG_SCx200_I2C)	+= scx200_i2c.o
+obj-$(CONFIG_I2C_CPM2)		+= i2c-cpm2.o
 
 ifeq ($(CONFIG_I2C_DEBUG_BUS),y)
 EXTRA_CFLAGS += -DDEBUG
drivers/i2c/busses/i2c-cpm2.c new file +377 -0

377 lines not on this page · raw message

int num)
+{
+ struct i2c_msg *pmsg;
+ struct cpm2_i2c *cpm2_i2c = i2c_get_adapdata(adap);
+ int i;
+ int ret = 0;
+ u_char addr;
+
+ dev_dbg(&adap->dev, "%s: %p %d\n", __FUNCTION__, msgs, num);
+
+ for (i = 0; i < num; i++) {
+ pmsg = &msgs[i];
+ addr = pmsg->addr << 1;
+ if (pmsg->flags & I2C_M_RD)
+ addr |= 1;
+ if (pmsg->flags & I2C_M_REV_DIR_ADDR)
+ addr ^= 1;
+ dev_dbg(&adap->dev, "%s: %hx %hx %hu %p\n",
+ __FUNCTION__,
+ pmsg->addr, pmsg->flags, pmsg->len, pmsg->buf);
+ if (pmsg->flags & I2C_M_RD) {
+ /* read bytes into buffer*/
+ ret = cpm2_i2c_read(cpm2_i2c, addr,
+ pmsg->buf, pmsg->len);
+ dev_dbg(&cpm2_i2c->adap.dev, "read %d bytes\n", ret);
+ if (ret < pmsg->len) {
+ return (ret<0) ? ret : -EREMOTEIO;
+ }
+ } else {
+ /* write bytes from buffer */
+ ret = cpm2_i2c_write(cpm2_i2c, addr,
+ pmsg->buf, pmsg->len);
+ dev_dbg(&cpm2_i2c->adap.dev, "wrote %d\n", ret);
+ if (ret < pmsg->len) {
+ return (ret<0) ? ret : -EREMOTEIO;
+ }
+ }
+ }
+ return (ret < 0) ? ret : num;
+}
+
+static u32 cpm2_functionality(struct i2c_adapter *adap) +{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm cpm2_algo = {
+ .master_xfer = cpm2_xfer,
+ .functionality = cpm2_functionality,
+};
+
+static struct i2c_adapter cpm2_ops = {
+ .owner = THIS_MODULE,
+ .name = "MPC CPM2 adapter",
+ .id = I2C_HW_CPM2,
+ .algo = &cpm2_algo,
+ .class = I2C_CLASS_HWMON,
+ .timeout = 1,
+ .retries = 1
+};
+
+static int cpm2_i2c_probe(struct platform_device *pdev) +{
+ struct cpm2_i2c *i2c;
+ struct resource *r;
+ void *start;
+ int result = 0;
+
+ dev_info(&pdev->dev, "probed (id=%d)\n", pdev->id);
+
+ i2c = kmalloc(sizeof(*i2c), GFP_KERNEL);
+ if (i2c == NULL)
+ return -ENOMEM;
+ memset(i2c, 0, sizeof(*i2c));
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (r == NULL) {
+ dev_err(&pdev->dev, "register resource not found\n");
+ result = -EINVAL;
+ goto fail_map1;
+ }
+
+ start = ioremap((phys_addr_t)r->start, sizeof(i2c_cpm2_t));
+ if (start==NULL) {
+ dev_err(&pdev->dev, "ioremap for registers failed\n");
+ result = -ENOMEM;
+ goto fail_map1;
+ }
+ i2c->regs = start;
+
+ i2c->irq = platform_get_irq(pdev, 0);
+ if (!i2c->irq) {
+ dev_err(&pdev->dev, "i2c irq resource not found\n");
+ result = -EINVAL;
+ goto fail_irq;
+ }
+
+ if ((result = request_irq(i2c->irq, cpm2_i2c_isr,
+ IRQF_SHARED, "i2c-cpm2", i2c)) < 0 ) {
+ dev_err(&pdev->dev, "failed to attach interrupt\n");
+ goto fail_irq;
+ }
+
+ /* Allocate I2C parameter table and set base pointer */
+ i2c->base = (u16 *)cpm2_map_size(im_dprambase[PROFF_I2C_BASE], 2);
+ i2c->dp_offset = cpm_dpalloc(sizeof(iic_t), 64);
+ i2c->dp_addr = (iic_t *)cpm_dpram_addr(i2c->dp_offset);
+ *(i2c->base) = i2c->dp_offset;
+
+ i2c->rbdf_offset = cpm_dpalloc(sizeof(cbd_t) * 2, 8);
+ i2c->tbdf_offset = cpm_dpalloc(sizeof(cbd_t) * 2, 8);
+ i2c->rbdf_addr = (cbd_t *)cpm_dpram_addr(i2c->rbdf_offset);
+ i2c->tbdf_addr = (cbd_t *)cpm_dpram_addr(i2c->tbdf_offset);
+
+ dev_dbg(&pdev->dev, "%s: dp_offset=%x rbdf_offset=%x tbdf_offset=%x\n",
+ __FUNCTION__,
+ i2c->dp_offset, i2c->rbdf_offset, i2c->tbdf_offset);
+
+ cpm2_pram_init(i2c);
+ cpm2_pin_init(i2c);
+
+ /* Initialize Tx/Rx parameters. */
+ {
+ volatile cpm_cpm2_t *cp = cpmp;
+ cp->cp_cpcr =
+ mk_cr_cmd(CPM_CR_I2C_PAGE,
+ CPM_CR_I2C_SBLOCK,
+ 0x00, CPM_CR_INIT_TRX) | CPM_CR_FLG;
+ while (cp->cp_cpcr & CPM_CR_FLG);
+ }
+
+ /* Select an arbitrary address. Just make sure it is unique. */
+ out_8(&i2c->regs->i2c_i2add, 0x34);
+ /* Pre-divider is BRGCLK/4 and enable clock filter */
+ out_8(&i2c->regs->i2c_i2mod, 0x0e);
+ /* Divider is 2 * ( 34 + 3 + 2 ) */
+ out_8(&i2c->regs->i2c_i2brg, 34);
+ /* Disable interrupts */
+ out_8(&i2c->regs->i2c_i2cmr, 0x12);
+ /* Acknowledge old interrupts */
+ out_8(&i2c->regs->i2c_i2cer, 0xff);
+
+ init_waitqueue_head(&i2c->queue);
+
+ i2c->state = I2C_IDLE;
+
+ platform_set_drvdata(pdev, i2c);
+ i2c->adap = cpm2_ops;
+ i2c_set_adapdata(&i2c->adap, i2c);
+ i2c->adap.dev.parent = &pdev->dev;
+ i2c->adap.nr = pdev->id;
+ spin_lock_init(&i2c->lock);
+
+ result = i2c_add_numbered_adapter(&i2c->adap);
+ if (result < 0) {
+ dev_err(&pdev->dev, "failed to add adapter\n");
+ goto fail_add;
+ }
+
+ return result;
+
+fail_add:
+ free_irq(i2c->irq, NULL);
+ cpm_dpfree(i2c->rbdf_offset);
+ cpm_dpfree(i2c->tbdf_offset);
+ cpm_dpfree(i2c->dp_offset);
+ cpm2_unmap(i2c->base);
+fail_irq:
+ iounmap(i2c->regs);
+fail_map1:
+ kfree(i2c);
+ return result;
+};
+
+
+static void
+cpm2_i2c_shutdown(struct cpm2_i2c *i2c)
+{
+ /* Shut down I2C controller */
+ out_8(&i2c->regs->i2c_i2mod, 0);
+ out_8(&i2c->regs->i2c_i2cmr, 0);
+ out_8(&i2c->regs->i2c_i2cer, 0xff);
+}
+
+
+static int cpm2_i2c_remove(struct platform_device *pdev) +{
+ struct cpm2_i2c *i2c = platform_get_drvdata(pdev);
+
+ cpm2_i2c_shutdown(i2c);
+
+ i2c_del_adapter(&i2c->adap);
+ platform_set_drvdata(pdev, NULL);
+
+ free_irq(i2c->irq, i2c);
+
+ cpm_dpfree(i2c->rbdf_offset);
+ cpm_dpfree(i2c->tbdf_offset);
+ cpm_dpfree(i2c->dp_offset);
+ cpm2_unmap(i2c->base);
+
+ iounmap(i2c->regs);
+ kfree(i2c);
+
+ return 0;
+};
+
+/* Structure for a device driver */
+static struct platform_driver cpm2_i2c_driver = {
+ .probe = cpm2_i2c_probe,
+ .remove = cpm2_i2c_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "fsl-cpm2-i2c",
+ },
+};
+
+static int __init cpm2_i2c_init(void)
+{
+ return platform_driver_register(&cpm2_i2c_driver);
+}
+
+static void __exit cpm2_i2c_exit(void)
+{
+ platform_driver_unregister(&cpm2_i2c_driver);
+}
+
+module_init(cpm2_i2c_init);
+module_exit(cpm2_i2c_exit);
+
+MODULE_AUTHOR("Esben Haabendal <[email protected]>"); +MODULE_DESCRIPTION("I2C-Bus adapter for MPC processors with CPM2"); +MODULE_LICENSE("GPL");

include/asm-powerpc/cpm.h +12 -1
index ede38ff..1f92c36 100644
@@ -89,7 +89,18 @@ typedef struct cpm_buf_desc {
 
 /* Buffer descriptor control/status used by I2C.
  */
-#define BD_I2C_START		(0x0400)
+#define BD_I2C_EMPTY	(0x8000)	/* Receive buffer is empty */
+#define BD_I2C_READY	(0x8000)	/* Transmit buffer is ready */
+#define BD_I2C_WRAP	(0x2000)	/* Last buffer descriptor */
+#define BD_I2C_INTRPT	(0x1000)	/* Interrupt on event */
+#define BD_I2C_LAST	(0x0800)	/* Last buffer in frame */
+#define BD_I2C_START	(0x0400)	/* Generate start condition */
+#define BD_I2C_NAK	(0x0004)	/* No acknowledge */
+#define BD_I2C_OV	(0x0002)	/* Overrun */
+#define BD_I2C_UN	(0x0002)	/* Underrun */
+#define BD_I2C_CL	(0x0001)	/* Collision */
+
+
 
 int cpm_muram_init(void);
 unsigned long cpm_muram_alloc(unsigned long size, unsigned long align);
include/linux/i2c-id.h +3 -0
index 580acc9..9844953 100644
@@ -133,6 +133,9 @@
 /* --- PowerPC on-chip adapters						*/
 #define I2C_HW_OCP		0x120000 /* IBM on-chip I2C adapter */
 
+/* --- MPC82xx PowerPC adapters						*/
+#define I2C_HW_CPM2		0x100002 /* MPC CPM2 I2C adapter */
+
 /* --- Broadcom SiByte adapters						*/
 #define I2C_HW_SIBYTE		0x150000
 

.

Signature · 4 lines
Esben Haabendal, Senior Software Consultant
DoréDevelopment ApS, Ved Stranden 1, 9560 Hadsund, DK-Denmark
Tlf: +45 51 92 53 93, mail: [email protected]
WWW: http://www.doredevelopment.dk
#1 lore raw

Hello Esben,

Quoted 3 lines · Esben Haabendal

Here is an i2c driver for Freescale processors with CPM2. It is based on previous work by Angelos Manousaridis which again were based on a driver from Heiko Schocher.

Just recently a similar driver was added to the i2c-tree:

http://article.gmane.org/gmane.linux.kernel/684037

Maybe you can bring in your special knowledge and check if it is missing features from your implementation?

All the best,

Wolfram

Signature · 2 lines
  Dipl.-Ing. Wolfram Sang | http://www.pengutronix.de
 Pengutronix - Linux Solutions for Science and Industry