* [PATCH] add I2C adapter driver for the netX family of processors
@ 2012-04-27 11:40 Juergen Beisert
0 siblings, 0 replies; 2+ messages in thread
From: Juergen Beisert @ 2012-04-27 11:40 UTC (permalink / raw)
To: linux-i2c-u79uwXL29TY76Z2rM5mHXA
Signed-off-by: Juergen Beisert <jbe-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index d2c5095..6c01f17 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -425,6 +425,16 @@ config I2C_IMX
This driver can also be built as a module. If so, the module
will be called i2c-imx.
+config I2C_NETX
+ tristate "NETX I2C interface"
+ depends on ARCH_NETX
+ help
+ Say Y here if you want to use the IIC bus controller on
+ the Hilscher netX processors.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-netx.
+
config I2C_INTEL_MID
tristate "Intel Moorestown/Medfield Platform I2C controller"
depends on PCI
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 569567b..acbe444 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -65,6 +65,7 @@ obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o
obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o
obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o
obj-$(CONFIG_I2C_SIRF) += i2c-sirf.o
+obj-$(CONFIG_I2C_NETX) += i2c-netx.o
obj-$(CONFIG_I2C_STU300) += i2c-stu300.o
obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o
obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
diff --git a/drivers/i2c/busses/i2c-netx.c b/drivers/i2c/busses/i2c-netx.c
new file mode 100644
index 0000000..4a18996
--- /dev/null
+++ b/drivers/i2c/busses/i2c-netx.c
@@ -0,0 +1,422 @@
+/*
+ * Copyright (c) 2012 Juergen Beisert <kernel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>, Pengutronix
+ * On behalf of Hilscher GmbH, http://www.hilscher.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * Note: the I2C master hardware on these chips is broken. The silicon cannot
+ * detect any ACK signals.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/i2c.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/netx_iic.h>
+
+#define NETX_I2C_CTRL 0x00
+# define NETX_I2C_CTRL_ENABLE (1 << 0)
+# define NETX_I2C_CTRL_SPEED(x) ((x) << 1)
+# define NETX_I2C_CTRL_SLAVEID(x) (((x) & 0x7f) << 4)
+# define NETX_I2C_CTRL_SLAVEMASK (0x7f << 4)
+
+#define NETX_I2C_DATA 0x04
+# define NETX_I2C_DATA_SEND_STOP (1 << 8)
+# define NETX_I2C_DATA_READ (1 << 9)
+# define NETX_I2C_DATA_WRITE (0 << 9)
+# define NETX_I2C_DATA_SEND_START (1 << 10)
+# define NETX_I2C_DATA_BUSY (1 << 11)
+# define NETX_I2C_DATA_EXECUTE (1 << 11)
+# define NETX_I2C_DATA_RDF (1 << 12)
+
+struct netx_i2c {
+ struct i2c_adapter adapter;
+ unsigned time_idx; /* index into the bt table */
+ void __iomem *regs;
+};
+
+/* time per byte in [µs] at different clock speeds */
+static const unsigned bt[8] = {400, 200, 100, 50, 25, 16, 12, 10};
+
+static int netx_iic_is_busy(void __iomem *regs)
+{
+ u32 val = readl(regs + NETX_I2C_DATA);
+ if (val & NETX_I2C_DATA_BUSY)
+ return 1; /* still busy */
+ return 0;
+}
+
+static void netx_iic_set_slaveid(void __iomem *regs, unsigned sid)
+{
+ u32 reg = readl(regs + NETX_I2C_CTRL) & ~NETX_I2C_CTRL_SLAVEMASK;
+
+ reg |= NETX_I2C_CTRL_SLAVEID(sid);
+ writel(reg, regs + NETX_I2C_CTRL);
+}
+
+static void netx_iic_read_cycle(void __iomem *regs, unsigned iic_flags)
+{
+ u32 val = iic_flags | NETX_I2C_DATA_READ | NETX_I2C_DATA_EXECUTE;
+ writel(val, regs + NETX_I2C_DATA);
+}
+
+static u8 netx_iic_read_byte(void __iomem *regs)
+{
+ return readl(regs + NETX_I2C_DATA) & 0xff;
+}
+
+static void netx_iic_write_byte(void __iomem *regs, unsigned byte,
+ unsigned iic_flags)
+{
+ u32 val = (byte & 0xff) | iic_flags | NETX_I2C_DATA_WRITE |
+ NETX_I2C_DATA_EXECUTE;
+ writel(val, regs + NETX_I2C_DATA);
+}
+
+static void netx_iic_reset_controller(struct netx_i2c *this)
+{
+ writel(0, this->regs + NETX_I2C_CTRL);
+ udelay(5);
+ /* re-enable the controller and clear the bus */
+ writel(NETX_I2C_CTRL_SPEED(this->time_idx) | NETX_I2C_CTRL_ENABLE,
+ this->regs + NETX_I2C_CTRL);
+ /* send some empty clocks to terminate all external statemachines */
+ netx_iic_write_byte(this->regs, 0xff, 0);
+ udelay(bt[this->time_idx]); /* wait, until this byte was sent */
+}
+
+static int netx_iic_wait(struct netx_i2c *this, unsigned cnt)
+{
+ /* we have no interrupt :( */
+ udelay(bt[this->time_idx] * cnt); /* wait the time for 'n' bytes */
+ if (netx_iic_is_busy(this->regs)) { /* still busy? */
+ /* wait a little bit more... */
+ udelay((bt[this->time_idx] * cnt) >> 1);
+ if (netx_iic_is_busy(this->regs)) { /* still busy? */
+ /* no luck */
+ netx_iic_reset_controller(this);
+ return -ETIMEDOUT;
+ }
+ }
+ return 0;
+}
+
+/*
+ * This I2C master controller cannot handle sending the slave address without
+ * a payload byte. Thus, we we need a special handling here to get the address
+ * out.
+ */
+static int netx_iic_start_transfer(struct netx_i2c *this, struct i2c_msg *msg,
+ unsigned iic_flags)
+{
+ int rc;
+
+ iic_flags |= NETX_I2C_DATA_SEND_START;
+
+ netx_iic_set_slaveid(this->regs, msg->addr);
+
+ if (msg->flags & I2C_M_RD) {
+ dev_dbg(&this->adapter.dev,
+ "Send address 0x%02X for reading\n", msg->addr);
+ netx_iic_read_cycle(this->regs, iic_flags);
+ } else {
+ dev_dbg(&this->adapter.dev,
+ "Send address 0x%02X and byte 0x%02X for writing\n",
+ msg->addr, msg->buf[0]);
+ netx_iic_write_byte(this->regs, msg->buf[0], iic_flags);
+ }
+
+ rc = netx_iic_wait(this, 2); /* addr + data */
+ if (rc < 0) {
+ dev_err(&this->adapter.dev, "Device addressing timeout\n");
+ return rc;
+ }
+
+ if (msg->flags & I2C_M_RD) {
+ msg->buf[0] = netx_iic_read_byte(this->regs);
+ dev_dbg(&this->adapter.dev,
+ "Receiving byte 0x%02X\n", msg->buf[0]);
+ }
+
+ return 0;
+}
+
+/* set 'iic_flags' to 'NETX_I2C_DATA_SEND_STOP' if this is the last message */
+static int netx_iic_read_data_stream(struct netx_i2c *this,
+ struct i2c_msg *msg, int rec, unsigned iic_flags)
+{
+ unsigned u;
+ int rc;
+
+ for (u = rec; u < msg->len; u++) {
+ if (u == (msg->len - 1)) { /* last byte? */
+ netx_iic_read_cycle(this->regs, iic_flags);
+ dev_dbg(&this->adapter.dev, "Last Byte read\n");
+ } else {
+ netx_iic_read_cycle(this->regs, 0);
+ }
+
+ rc = netx_iic_wait(this, 1);
+ if (rc < 0) {
+ dev_dbg(&this->adapter.dev, "Read timeout\n");
+ return rc;
+ }
+
+ msg->buf[u] = netx_iic_read_byte(this->regs);
+ dev_dbg(&this->adapter.dev, "Read Byte %02X\n", msg->buf[u]);
+ }
+
+ return 0;
+}
+
+/* set 'iic_flags' to 'NETX_I2C_DATA_SEND_STOP' if this is the last message */
+static int netx_iic_write_data_stream(struct netx_i2c *this,
+ struct i2c_msg *msg, int sent, unsigned iic_flags)
+{
+ unsigned u;
+ int rc;
+
+ for (u = sent; u < msg->len; u++) {
+ if (u == (msg->len - 1)) { /* last byte? */
+ netx_iic_write_byte(this->regs, msg->buf[u], iic_flags);
+ dev_dbg(&this->adapter.dev,
+ "Last byte '%02X' written\n", msg->buf[u]);
+ } else {
+ netx_iic_write_byte(this->regs, msg->buf[u], 0);
+ dev_dbg(&this->adapter.dev,
+ "byte '%02X' written\n", msg->buf[u]);
+ rc = netx_iic_wait(this, 1);
+ if (rc != 0) {
+ dev_dbg(&this->adapter.dev, "Write timeout\n");
+ return rc;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int netx_iic_data_stream(struct netx_i2c *this, struct i2c_msg *msg,
+ int sent, int last_msg)
+{
+ unsigned iic_flags = last_msg ? NETX_I2C_DATA_SEND_STOP : 0;
+
+ if (msg->flags & I2C_M_RD)
+ return netx_iic_read_data_stream(this, msg, sent, iic_flags);
+
+ return netx_iic_write_data_stream(this, msg, sent, iic_flags);
+}
+
+static int netx_iic_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
+ int msg_cnt)
+{
+ unsigned flags = 0;
+ int i, rc, sent;
+ struct netx_i2c *this = i2c_get_adapdata(adapter);
+
+ dev_dbg(&this->adapter.dev, "Called to transfer %d messages\n",
+ msg_cnt);
+
+ /* walk through the messages list */
+ for (i = 0; i < msg_cnt; msgs++, i++) {
+ /* read/write data in one message */
+ sent = 0;
+ if (!(msgs->flags & I2C_M_NOSTART)) {
+ if (msgs->len < 1) {
+ dev_err(&this->adapter.dev,
+ "Cannot handle messages without data\n");
+ return -EINVAL;
+ }
+
+ if ((msgs->len == 1) && ((i + 1) == msg_cnt))
+ flags |= NETX_I2C_DATA_SEND_STOP;
+
+ rc = netx_iic_start_transfer(this, msgs, flags);
+ if (rc != 0)
+ return rc;
+ sent = 1;
+ /*
+ * the regular job here would be to wait for the ACK of
+ * the addressed device. But the ACK detection is broken
+ * in this I2C master hardware and cannot be used. So,
+ * we must continue blindly.
+ */
+ } else
+ dev_dbg(&this->adapter.dev, "Message without START\n");
+
+ if (msgs->len > sent) { /* still remaining data? */
+ rc = netx_iic_data_stream(this, msgs, sent,
+ (i + 1) == msg_cnt);
+ if (rc != 0)
+ return rc;
+ }
+ }
+
+ return i;
+}
+
+static u32 netx_iic_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static struct i2c_algorithm netx_iic_algo = {
+ .master_xfer = netx_iic_xfer,
+ .functionality = netx_iic_func,
+};
+
+static void __init netx_iic_set_speed(struct netx_i2c *this,
+ struct netx_i2c_pdata *pdata)
+{
+ unsigned speed = ARRAY_SIZE(bt) - 1; /* start with 1 MHz (maximum) */
+
+ if (pdata->speed < 1000)
+ speed--; /* 800 kHz */
+ if (pdata->speed < 800)
+ speed--; /* 600 kHz */
+ if (pdata->speed < 600)
+ speed--; /* 400 kHz */
+ if (pdata->speed < 400)
+ speed--; /* 200 kHz */
+ if (pdata->speed < 200)
+ speed--; /* 100 kHz */
+ if (pdata->speed < 100)
+ speed--; /* 50 kHz */
+ if (pdata->speed < 50)
+ speed--; /* 25 kHz (minimum) */
+
+ dev_dbg(&this->adapter.dev, "Setting up speed to index %u\n", speed);
+
+ writel(NETX_I2C_CTRL_SPEED(speed), this->regs + NETX_I2C_CTRL);
+ this->time_idx = speed;
+ netx_iic_reset_controller(this);
+}
+
+static int __init netx_iic_probe(struct platform_device *pdev)
+{
+ struct netx_i2c *this;
+ struct netx_i2c_pdata *pdata = pdev->dev.platform_data;
+ struct resource *res;
+ void __iomem *base;
+ int ret;
+
+ dev_dbg(&pdev->dev, "Probing IIC master device\n");
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "can't get device resources\n");
+ return -ENOENT;
+ }
+
+ if (!request_mem_region(res->start, resource_size(res),
+ NETX_IIC_DRIVER_NAME)) {
+ dev_err(&pdev->dev, "request_mem_region failed\n");
+ return -EBUSY;
+ }
+
+ base = ioremap(res->start, resource_size(res));
+ if (!base) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ ret = -EIO;
+ goto fail1;
+ }
+
+ this = kzalloc(sizeof(struct netx_i2c), GFP_KERNEL);
+ if (!this) {
+ dev_err(&pdev->dev, "can't allocate interface\n");
+ ret = -ENOMEM;
+ goto fail2;
+ }
+
+ /* accumulate all available info */
+ this->regs = base;
+ strcpy(this->adapter.name, pdev->name);
+ this->adapter.owner = THIS_MODULE;
+ this->adapter.algo = &netx_iic_algo;
+ this->adapter.dev.parent = &pdev->dev;
+ this->adapter.nr = pdev->id;
+ this->adapter.dev.of_node = pdev->dev.of_node; /* TODO */
+
+ i2c_set_adapdata(&this->adapter, this);
+
+ ret = i2c_add_numbered_adapter(&this->adapter);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "registration failed\n");
+ goto fail3;
+ }
+
+ netx_iic_set_speed(this, pdata);
+
+ platform_set_drvdata(pdev, this);
+
+ return 0;
+
+fail3:
+ kfree(this);
+fail2:
+ iounmap(base);
+fail1:
+ release_mem_region(res->start, resource_size(res));
+ return ret;
+}
+
+static int __devexit netx_iic_remove(struct platform_device *pdev)
+{
+ struct netx_i2c *this = platform_get_drvdata(pdev);
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ /* remove adapter */
+ i2c_del_adapter(&this->adapter);
+ platform_set_drvdata(pdev, NULL);
+
+ /* disable the I2C unit */
+ writel(0, this->regs + NETX_I2C_CTRL);
+
+ iounmap(this->regs);
+ release_mem_region(res->start, resource_size(res));
+ kfree(this);
+
+ return 0;
+}
+
+static const struct of_device_id netx_iic_dt_ids[] = {
+ { .compatible = "hilscher,netx-i2c", },
+ { /* sentinel */ }
+};
+
+static struct platform_driver netx_iic_driver = {
+ .remove = __exit_p(netx_iic_remove),
+ .driver = {
+ .name = NETX_IIC_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = netx_iic_dt_ids,
+ }
+};
+
+static int __init netx_i2c_adap_init(void)
+{
+ return platform_driver_probe(&netx_iic_driver, netx_iic_probe);
+}
+subsys_initcall(netx_i2c_adap_init);
+
+static void __exit netx_i2c_adap_exit(void)
+{
+ platform_driver_unregister(&netx_iic_driver);
+}
+module_exit(netx_i2c_adap_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Juergen Beisert");
+MODULE_DESCRIPTION("I2C adapter driver for netX I2C bus");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/include/linux/platform_data/netx_iic.h b/include/linux/platform_data/netx_iic.h
new file mode 100644
index 0000000..a3e9536
--- /dev/null
+++ b/include/linux/platform_data/netx_iic.h
@@ -0,0 +1,10 @@
+#ifndef PLATFORM_DATA_NETX_IIC_H
+# define PLATFORM_DATA_NETX_IIC_H
+
+struct netx_i2c_pdata {
+ unsigned speed; /* in [kHz] */
+};
+
+#define NETX_IIC_DRIVER_NAME "netx-iic"
+
+#endif
--
Pengutronix e.K. | Juergen Beisert |
Linux Solutions for Science and Industry | http://www.pengutronix.de/ |
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH] add I2C adapter driver for the netX family of processors
[not found] ` <201205261304.38501.jbe-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
@ 2012-05-26 12:35 ` Wolfram Sang
0 siblings, 0 replies; 2+ messages in thread
From: Wolfram Sang @ 2012-05-26 12:35 UTC (permalink / raw)
To: Juergen Beisert; +Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA
[-- Attachment #1: Type: text/plain, Size: 15991 bytes --]
On Sat, May 26, 2012 at 01:04:38PM +0200, Juergen Beisert wrote:
> Signed-off-by: Juergen Beisert <jbe-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
>
> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> index d2c5095..6c01f17 100644
> --- a/drivers/i2c/busses/Kconfig
> +++ b/drivers/i2c/busses/Kconfig
> @@ -425,6 +425,16 @@ config I2C_IMX
> This driver can also be built as a module. If so, the module
> will be called i2c-imx.
>
> +config I2C_NETX
> + tristate "NETX I2C interface"
> + depends on ARCH_NETX
> + help
> + Say Y here if you want to use the IIC bus controller on
> + the Hilscher netX processors.
> +
> + This driver can also be built as a module. If so, the module
> + will be called i2c-netx.
> +
> config I2C_INTEL_MID
> tristate "Intel Moorestown/Medfield Platform I2C controller"
> depends on PCI
> diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
> index 569567b..acbe444 100644
> --- a/drivers/i2c/busses/Makefile
> +++ b/drivers/i2c/busses/Makefile
> @@ -65,6 +65,7 @@ obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o
> obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o
> obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o
> obj-$(CONFIG_I2C_SIRF) += i2c-sirf.o
> +obj-$(CONFIG_I2C_NETX) += i2c-netx.o
> obj-$(CONFIG_I2C_STU300) += i2c-stu300.o
> obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o
> obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
Kconfig and Makefile are sorted, please stick to that.
> diff --git a/drivers/i2c/busses/i2c-netx.c b/drivers/i2c/busses/i2c-netx.c
> new file mode 100644
> index 0000000..4a18996
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-netx.c
> @@ -0,0 +1,422 @@
> +/*
> + * Copyright (c) 2012 Juergen Beisert <kernel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>, Pengutronix
> + * On behalf of Hilscher GmbH, http://www.hilscher.com/
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation.
> + *
> + * 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.
> + *
> + * Note: the I2C master hardware on these chips is broken. The silicon cannot
> + * detect any ACK signals.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/i2c.h>
> +#include <linux/device.h>
> +#include <linux/platform_device.h>
> +#include <linux/platform_data/netx_iic.h>
I think I'd prefer "linux/i2c/..."
> +
> +#define NETX_I2C_CTRL 0x00
> +# define NETX_I2C_CTRL_ENABLE (1 << 0)
> +# define NETX_I2C_CTRL_SPEED(x) ((x) << 1)
> +# define NETX_I2C_CTRL_SLAVEID(x) (((x) & 0x7f) << 4)
> +# define NETX_I2C_CTRL_SLAVEMASK (0x7f << 4)
> +
> +#define NETX_I2C_DATA 0x04
> +# define NETX_I2C_DATA_SEND_STOP (1 << 8)
> +# define NETX_I2C_DATA_READ (1 << 9)
> +# define NETX_I2C_DATA_WRITE (0 << 9)
> +# define NETX_I2C_DATA_SEND_START (1 << 10)
> +# define NETX_I2C_DATA_BUSY (1 << 11)
> +# define NETX_I2C_DATA_EXECUTE (1 << 11)
> +# define NETX_I2C_DATA_RDF (1 << 12)
> +
> +struct netx_i2c {
> + struct i2c_adapter adapter;
> + unsigned time_idx; /* index into the bt table */
> + void __iomem *regs;
> +};
> +
> +/* time per byte in [µs] at different clock speeds */
Please '[us]' to avoid unnecessary encoding issues.
> +static const unsigned bt[8] = {400, 200, 100, 50, 25, 16, 12, 10};
> +
> +static int netx_iic_is_busy(void __iomem *regs)
> +{
> + u32 val = readl(regs + NETX_I2C_DATA);
Newline please.
> + if (val & NETX_I2C_DATA_BUSY)
> + return 1; /* still busy */
> + return 0;
return !!(val & NETX_I2C_DATA_BUSY)?
> +}
> +
> +static void netx_iic_set_slaveid(void __iomem *regs, unsigned sid)
> +{
> + u32 reg = readl(regs + NETX_I2C_CTRL) & ~NETX_I2C_CTRL_SLAVEMASK;
> +
> + reg |= NETX_I2C_CTRL_SLAVEID(sid);
> + writel(reg, regs + NETX_I2C_CTRL);
> +}
> +
> +static void netx_iic_read_cycle(void __iomem *regs, unsigned iic_flags)
> +{
> + u32 val = iic_flags | NETX_I2C_DATA_READ | NETX_I2C_DATA_EXECUTE;
newline
> + writel(val, regs + NETX_I2C_DATA);
> +}
> +
> +static u8 netx_iic_read_byte(void __iomem *regs)
> +{
> + return readl(regs + NETX_I2C_DATA) & 0xff;
> +}
> +
> +static void netx_iic_write_byte(void __iomem *regs, unsigned byte,
> + unsigned iic_flags)
> +{
Couldn't 'byte' be u8 instead of unsigned?
> + u32 val = (byte & 0xff) | iic_flags | NETX_I2C_DATA_WRITE |
> + NETX_I2C_DATA_EXECUTE;
> + writel(val, regs + NETX_I2C_DATA);
> +}
> +
> +static void netx_iic_reset_controller(struct netx_i2c *this)
> +{
> + writel(0, this->regs + NETX_I2C_CTRL);
> + udelay(5);
> + /* re-enable the controller and clear the bus */
> + writel(NETX_I2C_CTRL_SPEED(this->time_idx) | NETX_I2C_CTRL_ENABLE,
> + this->regs + NETX_I2C_CTRL);
> + /* send some empty clocks to terminate all external statemachines */
> + netx_iic_write_byte(this->regs, 0xff, 0);
> + udelay(bt[this->time_idx]); /* wait, until this byte was sent */
> +}
> +
> +static int netx_iic_wait(struct netx_i2c *this, unsigned cnt)
> +{
> + /* we have no interrupt :( */
> + udelay(bt[this->time_idx] * cnt); /* wait the time for 'n' bytes */
> + if (netx_iic_is_busy(this->regs)) { /* still busy? */
> + /* wait a little bit more... */
> + udelay((bt[this->time_idx] * cnt) >> 1);
> + if (netx_iic_is_busy(this->regs)) { /* still busy? */
> + /* no luck */
> + netx_iic_reset_controller(this);
> + return -ETIMEDOUT;
> + }
> + }
> + return 0;
> +}
> +
> +/*
> + * This I2C master controller cannot handle sending the slave address without
> + * a payload byte. Thus, we we need a special handling here to get the address
> + * out.
> + */
> +static int netx_iic_start_transfer(struct netx_i2c *this, struct i2c_msg *msg,
> + unsigned iic_flags)
> +{
> + int rc;
> +
> + iic_flags |= NETX_I2C_DATA_SEND_START;
> +
> + netx_iic_set_slaveid(this->regs, msg->addr);
> +
> + if (msg->flags & I2C_M_RD) {
> + dev_dbg(&this->adapter.dev,
> + "Send address 0x%02X for reading\n", msg->addr);
> + netx_iic_read_cycle(this->regs, iic_flags);
> + } else {
> + dev_dbg(&this->adapter.dev,
> + "Send address 0x%02X and byte 0x%02X for writing\n",
> + msg->addr, msg->buf[0]);
> + netx_iic_write_byte(this->regs, msg->buf[0], iic_flags);
> + }
> +
> + rc = netx_iic_wait(this, 2); /* addr + data */
> + if (rc < 0) {
> + dev_err(&this->adapter.dev, "Device addressing timeout\n");
> + return rc;
> + }
> +
> + if (msg->flags & I2C_M_RD) {
> + msg->buf[0] = netx_iic_read_byte(this->regs);
> + dev_dbg(&this->adapter.dev,
> + "Receiving byte 0x%02X\n", msg->buf[0]);
> + }
> +
> + return 0;
> +}
> +
> +/* set 'iic_flags' to 'NETX_I2C_DATA_SEND_STOP' if this is the last message */
> +static int netx_iic_read_data_stream(struct netx_i2c *this,
> + struct i2c_msg *msg, int rec, unsigned iic_flags)
> +{
> + unsigned u;
> + int rc;
> +
> + for (u = rec; u < msg->len; u++) {
> + if (u == (msg->len - 1)) { /* last byte? */
> + netx_iic_read_cycle(this->regs, iic_flags);
> + dev_dbg(&this->adapter.dev, "Last Byte read\n");
> + } else {
> + netx_iic_read_cycle(this->regs, 0);
> + }
> +
> + rc = netx_iic_wait(this, 1);
> + if (rc < 0) {
> + dev_dbg(&this->adapter.dev, "Read timeout\n");
> + return rc;
> + }
> +
> + msg->buf[u] = netx_iic_read_byte(this->regs);
> + dev_dbg(&this->adapter.dev, "Read Byte %02X\n", msg->buf[u]);
> + }
> +
> + return 0;
> +}
> +
> +/* set 'iic_flags' to 'NETX_I2C_DATA_SEND_STOP' if this is the last message */
> +static int netx_iic_write_data_stream(struct netx_i2c *this,
> + struct i2c_msg *msg, int sent, unsigned iic_flags)
> +{
> + unsigned u;
> + int rc;
> +
> + for (u = sent; u < msg->len; u++) {
> + if (u == (msg->len - 1)) { /* last byte? */
> + netx_iic_write_byte(this->regs, msg->buf[u], iic_flags);
> + dev_dbg(&this->adapter.dev,
> + "Last byte '%02X' written\n", msg->buf[u]);
> + } else {
> + netx_iic_write_byte(this->regs, msg->buf[u], 0);
> + dev_dbg(&this->adapter.dev,
> + "byte '%02X' written\n", msg->buf[u]);
> + rc = netx_iic_wait(this, 1);
> + if (rc != 0) {
if (netx_iic_wait(this, 1)) ?
Would save the rc-variable and still be readable.
> + dev_dbg(&this->adapter.dev, "Write timeout\n");
> + return rc;
> + }
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int netx_iic_data_stream(struct netx_i2c *this, struct i2c_msg *msg,
> + int sent, int last_msg)
> +{
> + unsigned iic_flags = last_msg ? NETX_I2C_DATA_SEND_STOP : 0;
> +
> + if (msg->flags & I2C_M_RD)
> + return netx_iic_read_data_stream(this, msg, sent, iic_flags);
> +
> + return netx_iic_write_data_stream(this, msg, sent, iic_flags);
> +}
> +
> +static int netx_iic_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
> + int msg_cnt)
> +{
> + unsigned flags = 0;
> + int i, rc, sent;
> + struct netx_i2c *this = i2c_get_adapdata(adapter);
> +
> + dev_dbg(&this->adapter.dev, "Called to transfer %d messages\n",
> + msg_cnt);
Drop this. There is a similar debug message in the core which can be
activated via Kconfig.
> +
> + /* walk through the messages list */
> + for (i = 0; i < msg_cnt; msgs++, i++) {
> + /* read/write data in one message */
> + sent = 0;
> + if (!(msgs->flags & I2C_M_NOSTART)) {
> + if (msgs->len < 1) {
> + dev_err(&this->adapter.dev,
> + "Cannot handle messages without data\n");
> + return -EINVAL;
> + }
> +
> + if ((msgs->len == 1) && ((i + 1) == msg_cnt))
> + flags |= NETX_I2C_DATA_SEND_STOP;
> +
> + rc = netx_iic_start_transfer(this, msgs, flags);
> + if (rc != 0)
> + return rc;
> + sent = 1;
> + /*
> + * the regular job here would be to wait for the ACK of
> + * the addressed device. But the ACK detection is broken
> + * in this I2C master hardware and cannot be used. So,
> + * we must continue blindly.
> + */
> + } else
> + dev_dbg(&this->adapter.dev, "Message without START\n");
> +
> + if (msgs->len > sent) { /* still remaining data? */
> + rc = netx_iic_data_stream(this, msgs, sent,
> + (i + 1) == msg_cnt);
> + if (rc != 0)
> + return rc;
> + }
> + }
> +
> + return i;
> +}
> +
> +static u32 netx_iic_func(struct i2c_adapter *adapter)
> +{
> + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
> +}
> +
> +static struct i2c_algorithm netx_iic_algo = {
> + .master_xfer = netx_iic_xfer,
> + .functionality = netx_iic_func,
> +};
> +
> +static void __init netx_iic_set_speed(struct netx_i2c *this,
> + struct netx_i2c_pdata *pdata)
> +{
> + unsigned speed = ARRAY_SIZE(bt) - 1; /* start with 1 MHz (maximum) */
> +
> + if (pdata->speed < 1000)
> + speed--; /* 800 kHz */
> + if (pdata->speed < 800)
> + speed--; /* 600 kHz */
> + if (pdata->speed < 600)
> + speed--; /* 400 kHz */
> + if (pdata->speed < 400)
> + speed--; /* 200 kHz */
> + if (pdata->speed < 200)
> + speed--; /* 100 kHz */
> + if (pdata->speed < 100)
> + speed--; /* 50 kHz */
> + if (pdata->speed < 50)
> + speed--; /* 25 kHz (minimum) */
> +
> + dev_dbg(&this->adapter.dev, "Setting up speed to index %u\n", speed);
> +
> + writel(NETX_I2C_CTRL_SPEED(speed), this->regs + NETX_I2C_CTRL);
> + this->time_idx = speed;
> + netx_iic_reset_controller(this);
> +}
> +
> +static int __init netx_iic_probe(struct platform_device *pdev)
> +{
> + struct netx_i2c *this;
> + struct netx_i2c_pdata *pdata = pdev->dev.platform_data;
> + struct resource *res;
> + void __iomem *base;
> + int ret;
> +
> + dev_dbg(&pdev->dev, "Probing IIC master device\n");
There is similar debug output in the driver core.
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res) {
> + dev_err(&pdev->dev, "can't get device resources\n");
> + return -ENOENT;
> + }
> +
> + if (!request_mem_region(res->start, resource_size(res),
> + NETX_IIC_DRIVER_NAME)) {
> + dev_err(&pdev->dev, "request_mem_region failed\n");
> + return -EBUSY;
> + }
> +
> + base = ioremap(res->start, resource_size(res));
> + if (!base) {
> + dev_err(&pdev->dev, "ioremap failed\n");
> + ret = -EIO;
> + goto fail1;
> + }
If you use devm_request_and_ioremap() here...
> +
> + this = kzalloc(sizeof(struct netx_i2c), GFP_KERNEL);
> + if (!this) {
> + dev_err(&pdev->dev, "can't allocate interface\n");
> + ret = -ENOMEM;
> + goto fail2;
> + }
... and devm_kzalloc here, you can remove all error paths from this
function and heavily simplify remove().
> +
> + /* accumulate all available info */
> + this->regs = base;
> + strcpy(this->adapter.name, pdev->name);
> + this->adapter.owner = THIS_MODULE;
> + this->adapter.algo = &netx_iic_algo;
> + this->adapter.dev.parent = &pdev->dev;
> + this->adapter.nr = pdev->id;
> + this->adapter.dev.of_node = pdev->dev.of_node; /* TODO */
> +
> + i2c_set_adapdata(&this->adapter, this);
> +
> + ret = i2c_add_numbered_adapter(&this->adapter);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "registration failed\n");
> + goto fail3;
> + }
> +
> + netx_iic_set_speed(this, pdata);
> +
> + platform_set_drvdata(pdev, this);
> +
> + return 0;
> +
> +fail3:
> + kfree(this);
> +fail2:
> + iounmap(base);
> +fail1:
> + release_mem_region(res->start, resource_size(res));
> + return ret;
> +}
> +
> +static int __devexit netx_iic_remove(struct platform_device *pdev)
> +{
> + struct netx_i2c *this = platform_get_drvdata(pdev);
> + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +
> + /* remove adapter */
> + i2c_del_adapter(&this->adapter);
> + platform_set_drvdata(pdev, NULL);
> +
> + /* disable the I2C unit */
> + writel(0, this->regs + NETX_I2C_CTRL);
> +
> + iounmap(this->regs);
> + release_mem_region(res->start, resource_size(res));
> + kfree(this);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id netx_iic_dt_ids[] = {
> + { .compatible = "hilscher,netx-i2c", },
> + { /* sentinel */ }
> +};
Please skip this. This platform has no DT support yet, and I prefer to
add it later when it gets added, so it has been tested then.
> +
> +static struct platform_driver netx_iic_driver = {
> + .remove = __exit_p(netx_iic_remove),
__devexit_p!
> + .driver = {
> + .name = NETX_IIC_DRIVER_NAME,
> + .owner = THIS_MODULE,
> + .of_match_table = netx_iic_dt_ids,
> + }
> +};
> +
> +static int __init netx_i2c_adap_init(void)
> +{
> + return platform_driver_probe(&netx_iic_driver, netx_iic_probe);
> +}
> +subsys_initcall(netx_i2c_adap_init);
> +
> +static void __exit netx_i2c_adap_exit(void)
> +{
> + platform_driver_unregister(&netx_iic_driver);
> +}
> +module_exit(netx_i2c_adap_exit);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Juergen Beisert");
> +MODULE_DESCRIPTION("I2C adapter driver for netX I2C bus");
> +MODULE_ALIAS("platform:" DRIVER_NAME);
> diff --git a/include/linux/platform_data/netx_iic.h b/include/linux/platform_data/netx_iic.h
> new file mode 100644
> index 0000000..a3e9536
> --- /dev/null
> +++ b/include/linux/platform_data/netx_iic.h
> @@ -0,0 +1,10 @@
> +#ifndef PLATFORM_DATA_NETX_IIC_H
> +# define PLATFORM_DATA_NETX_IIC_H
> +
> +struct netx_i2c_pdata {
> + unsigned speed; /* in [kHz] */
> +};
> +
> +#define NETX_IIC_DRIVER_NAME "netx-iic"
> +
> +#endif
--
Pengutronix e.K. | Wolfram Sang |
Industrial Linux Solutions | http://www.pengutronix.de/ |
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 198 bytes --]
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2012-05-26 12:35 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-04-27 11:40 [PATCH] add I2C adapter driver for the netX family of processors Juergen Beisert
[not found] <201205261304.38501.jbe@pengutronix.de>
[not found] ` <201205261304.38501.jbe-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
2012-05-26 12:35 ` Wolfram Sang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).