This patch adds device tree support to the pnx-i2c driver by using platform
resources for memory region and irq and removing dependency on mach includes.

The following platforms are affected:

* PNX
* LPC31xx (WIP)
* LPC32xx

The patch is based on a patch by Jon Smirl, working on lpc31xx integration

Signed-off-by: Roland Stigge <[email protected]>

---

 Applies to v3.4-rc1

 Updates:
 * Introducing slave-addr for I2C controller (this convention already exists in
   the kernel)
 * Bugfix: of_i2c_register_devices() was missing

 Documentation/devicetree/bindings/i2c/pnx.txt |   36 +++++
 drivers/i2c/busses/i2c-pnx.c                  |  160 +++++++++++++++++++-------
 include/linux/i2c-pnx.h                       |   10 -
 3 files changed, 158 insertions(+), 48 deletions(-)

--- /dev/null
+++ linux-2.6/Documentation/devicetree/bindings/i2c/pnx.txt
@@ -0,0 +1,36 @@
+* NXP PNX I2C Controller
+
+Required properties:
+
+ - reg: Offset and length of the register set for the device
+ - compatible: should be "nxp,pnx-i2c"
+ - interrupts: <a b> where a is the interrupt number and b is a
+   field that represents an encoding of the sense and level
+   information for the interrupt
+ - interrupt-parent: the phandle for the interrupt controller that
+   services interrupts for this device.
+
+Optional properties:
+
+ - clock-frequency: desired I2C bus clock frequency in Hz, Default: 100000 Hz
+ - pnx,timeout: I2C bus timeout in milliseconds, Default: 10 ms
+ - slave-addr: Address used by the controller, Hardware default: 110
+
+Examples:
+
+       i2c1: i2c@400A0000 {
+               compatible = "nxp,pnx-i2c";
+               reg = <0x400A0000 0x100>;
+               interrupt-parent = <&mic>;
+               interrupts = <51 0>;
+       };
+
+       i2c2: i2c@400A8000 {
+               compatible = "nxp,pnx-i2c";
+               reg = <0x400A8000 0x100>;
+               interrupt-parent = <&mic>;
+               interrupts = <50 0>;
+               clock-frequency = <0x186a0>;
+               pnx,timeout = <0x64>;
+               slave-addr = <0x11>;
+       };
--- linux-2.6.orig/drivers/i2c/busses/i2c-pnx.c
+++ linux-2.6/drivers/i2c/busses/i2c-pnx.c
@@ -23,16 +23,61 @@
 #include <linux/err.h>
 #include <linux/clk.h>
 #include <linux/slab.h>
+#include <linux/of_i2c.h>
 
-#include <mach/hardware.h>
-#include <mach/i2c.h>
+#define I2C_PNX_TIMEOUT_DEFAULT                10 /* msec */
+#define I2C_PNX_SPEED_KHZ_DEFAULT      100
+#define I2C_PNX_REGION_SIZE            0x100
+
+enum {
+       mstatus_tdi = 0x00000001,
+       mstatus_afi = 0x00000002,
+       mstatus_nai = 0x00000004,
+       mstatus_drmi = 0x00000008,
+       mstatus_active = 0x00000020,
+       mstatus_scl = 0x00000040,
+       mstatus_sda = 0x00000080,
+       mstatus_rff = 0x00000100,
+       mstatus_rfe = 0x00000200,
+       mstatus_tff = 0x00000400,
+       mstatus_tfe = 0x00000800,
+};
+
+enum {
+       mcntrl_tdie = 0x00000001,
+       mcntrl_afie = 0x00000002,
+       mcntrl_naie = 0x00000004,
+       mcntrl_drmie = 0x00000008,
+       mcntrl_daie = 0x00000020,
+       mcntrl_rffie = 0x00000040,
+       mcntrl_tffie = 0x00000080,
+       mcntrl_reset = 0x00000100,
+       mcntrl_cdbmode = 0x00000400,
+};
 
-#define I2C_PNX_TIMEOUT                10 /* msec */
-#define I2C_PNX_SPEED_KHZ      100
-#define I2C_PNX_REGION_SIZE    0x100
+enum {
+       rw_bit = 1 << 0,
+       start_bit = 1 << 8,
+       stop_bit = 1 << 9,
+};
+
+#define I2C_REG_RX(a)  ((a)->ioaddr)           /* Rx FIFO reg (RO) */
+#define I2C_REG_TX(a)  ((a)->ioaddr)           /* Tx FIFO reg (WO) */
+#define I2C_REG_STS(a) ((a)->ioaddr + 0x04)    /* Status reg (RO) */
+#define I2C_REG_CTL(a) ((a)->ioaddr + 0x08)    /* Ctl reg */
+#define I2C_REG_CKL(a) ((a)->ioaddr + 0x0c)    /* Clock divider low */
+#define I2C_REG_CKH(a) ((a)->ioaddr + 0x10)    /* Clock divider high */
+#define I2C_REG_ADR(a) ((a)->ioaddr + 0x14)    /* I2C address */
+#define I2C_REG_RFL(a) ((a)->ioaddr + 0x18)    /* Rx FIFO level (RO) */
+#define I2C_REG_TFL(a) ((a)->ioaddr + 0x1c)    /* Tx FIFO level (RO) */
+#define I2C_REG_RXB(a) ((a)->ioaddr + 0x20)    /* Num of bytes Rx-ed (RO) */
+#define I2C_REG_TXB(a) ((a)->ioaddr + 0x24)    /* Num of bytes Tx-ed (RO) */
+#define I2C_REG_TXS(a) ((a)->ioaddr + 0x28)    /* Tx slave FIFO (RO) */
+#define I2C_REG_STFL(a)        ((a)->ioaddr + 0x2c)    /* Tx slave FIFO level 
(RO) */
 
-static inline int wait_timeout(long timeout, struct i2c_pnx_algo_data *data)
+static inline int wait_timeout(struct i2c_pnx_algo_data *data)
 {
+       long timeout = data->timeout;
        while (timeout > 0 &&
                        (ioread32(I2C_REG_STS(data)) & mstatus_active)) {
                mdelay(1);
@@ -41,8 +86,9 @@ static inline int wait_timeout(long time
        return (timeout <= 0);
 }
 
-static inline int wait_reset(long timeout, struct i2c_pnx_algo_data *data)
+static inline int wait_reset(struct i2c_pnx_algo_data *data)
 {
+       long timeout = data->timeout;
        while (timeout > 0 &&
                        (ioread32(I2C_REG_CTL(data)) & mcntrl_reset)) {
                mdelay(1);
@@ -54,7 +100,7 @@ static inline int wait_reset(long timeou
 static inline void i2c_pnx_arm_timer(struct i2c_pnx_algo_data *alg_data)
 {
        struct timer_list *timer = &alg_data->mif.timer;
-       unsigned long expires = msecs_to_jiffies(I2C_PNX_TIMEOUT);
+       unsigned long expires = msecs_to_jiffies(alg_data->timeout);
 
        if (expires <= 1)
                expires = 2;
@@ -92,7 +138,7 @@ static int i2c_pnx_start(unsigned char s
        }
 
        /* First, make sure bus is idle */
-       if (wait_timeout(I2C_PNX_TIMEOUT, alg_data)) {
+       if (wait_timeout(alg_data)) {
                /* Somebody else is monopolizing the bus */
                dev_err(&alg_data->adapter.dev,
                        "%s: Bus busy. Slave addr = %02x, cntrl = %x, stat = 
%x\n",
@@ -185,7 +231,7 @@ static int i2c_pnx_master_xmit(struct i2
                if (alg_data->mif.len == 0) {
                        if (alg_data->last) {
                                /* Wait until the STOP is seen. */
-                               if (wait_timeout(I2C_PNX_TIMEOUT, alg_data))
+                               if (wait_timeout(alg_data))
                                        dev_err(&alg_data->adapter.dev,
                                                "The bus is still active after 
timeout\n");
                        }
@@ -283,7 +329,7 @@ static int i2c_pnx_master_rcv(struct i2c
                if (alg_data->mif.len == 0) {
                        if (alg_data->last)
                                /* Wait until the STOP is seen. */
-                               if (wait_timeout(I2C_PNX_TIMEOUT, alg_data))
+                               if (wait_timeout(alg_data))
                                        dev_err(&alg_data->adapter.dev,
                                                "The bus is still active after 
timeout\n");
 
@@ -399,7 +445,7 @@ static void i2c_pnx_timeout(unsigned lon
 
        ctl |= mcntrl_reset;
        iowrite32(ctl, I2C_REG_CTL(alg_data));
-       wait_reset(I2C_PNX_TIMEOUT, alg_data);
+       wait_reset(alg_data);
        alg_data->mif.ret = -EIO;
        complete(&alg_data->mif.complete);
 }
@@ -414,18 +460,18 @@ static inline void bus_reset_if_active(s
                        alg_data->adapter.name);
                iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset,
                          I2C_REG_CTL(alg_data));
-               wait_reset(I2C_PNX_TIMEOUT, alg_data);
+               wait_reset(alg_data);
        } else if (!(stat & mstatus_rfe) || !(stat & mstatus_tfe)) {
                /* If there is data in the fifo's after transfer,
                 * flush fifo's by reset.
                 */
                iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset,
                          I2C_REG_CTL(alg_data));
-               wait_reset(I2C_PNX_TIMEOUT, alg_data);
+               wait_reset(alg_data);
        } else if (stat & mstatus_nai) {
                iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset,
                          I2C_REG_CTL(alg_data));
-               wait_reset(I2C_PNX_TIMEOUT, alg_data);
+               wait_reset(alg_data);
        }
 }
 
@@ -569,14 +615,9 @@ static int __devinit i2c_pnx_probe(struc
        int ret = 0;
        struct i2c_pnx_algo_data *alg_data;
        unsigned long freq;
-       struct i2c_pnx_data *i2c_pnx = pdev->dev.platform_data;
-
-       if (!i2c_pnx || !i2c_pnx->name) {
-               dev_err(&pdev->dev, "%s: no platform data supplied\n",
-                      __func__);
-               ret = -EINVAL;
-               goto out;
-       }
+       struct resource *res;
+       u32 speed = I2C_PNX_SPEED_KHZ_DEFAULT * 1000;
+       u32 slave_addr = ~0;
 
        alg_data = kzalloc(sizeof(*alg_data), GFP_KERNEL);
        if (!alg_data) {
@@ -586,14 +627,22 @@ static int __devinit i2c_pnx_probe(struc
 
        platform_set_drvdata(pdev, alg_data);
 
-       strlcpy(alg_data->adapter.name, i2c_pnx->name,
-               sizeof(alg_data->adapter.name));
        alg_data->adapter.dev.parent = &pdev->dev;
        alg_data->adapter.algo = &pnx_algorithm;
        alg_data->adapter.algo_data = alg_data;
        alg_data->adapter.nr = pdev->id;
-       alg_data->i2c_pnx = i2c_pnx;
-
+       alg_data->timeout = I2C_PNX_TIMEOUT_DEFAULT;
+#ifdef CONFIG_OF
+       alg_data->adapter.dev.of_node = of_node_get(pdev->dev.of_node);
+       if (pdev->dev.of_node) {
+               of_property_read_u32(pdev->dev.of_node, "pnx,timeout",
+                                    &alg_data->timeout);
+               of_property_read_u32(pdev->dev.of_node, "clock-frequency",
+                                    &speed);
+               of_property_read_u32(pdev->dev.of_node, "slave-addr",
+                                    &slave_addr);
+       }
+#endif
        alg_data->clk = clk_get(&pdev->dev, NULL);
        if (IS_ERR(alg_data->clk)) {
                ret = PTR_ERR(alg_data->clk);
@@ -604,17 +653,27 @@ static int __devinit i2c_pnx_probe(struc
        alg_data->mif.timer.function = i2c_pnx_timeout;
        alg_data->mif.timer.data = (unsigned long)alg_data;
 
+       snprintf(alg_data->adapter.name, sizeof(alg_data->adapter.name),
+                "%s", pdev->name);
+
        /* Register I/O resource */
-       if (!request_mem_region(i2c_pnx->base, I2C_PNX_REGION_SIZE,
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(&pdev->dev, "Unable to get mem resource.\n");
+               ret = -EBUSY;
+               goto out_clkget;
+       }
+       if (!request_mem_region(res->start, I2C_PNX_REGION_SIZE,
                                pdev->name)) {
                dev_err(&pdev->dev,
                       "I/O region 0x%08x for I2C already in use.\n",
-                      i2c_pnx->base);
-               ret = -ENODEV;
+                      res->start);
+               ret = -ENOMEM;
                goto out_clkget;
        }
 
-       alg_data->ioaddr = ioremap(i2c_pnx->base, I2C_PNX_REGION_SIZE);
+       alg_data->base = res->start;
+       alg_data->ioaddr = ioremap(res->start, I2C_PNX_REGION_SIZE);
        if (!alg_data->ioaddr) {
                dev_err(&pdev->dev, "Couldn't ioremap I2C I/O region\n");
                ret = -ENOMEM;
@@ -625,6 +684,9 @@ static int __devinit i2c_pnx_probe(struc
        if (ret)
                goto out_unmap;
 
+       if (slave_addr != ~0)
+               iowrite32(slave_addr, I2C_REG_ADR(alg_data));
+
        freq = clk_get_rate(alg_data->clk);
 
        /*
@@ -638,20 +700,25 @@ static int __devinit i2c_pnx_probe(struc
         * the deglitching filter length.
         */
 
-       tmp = ((freq / 1000) / I2C_PNX_SPEED_KHZ) / 2 - 2;
+       tmp = (freq / speed) / 2 - 2;
        if (tmp > 0x3FF)
                tmp = 0x3FF;
        iowrite32(tmp, I2C_REG_CKH(alg_data));
        iowrite32(tmp, I2C_REG_CKL(alg_data));
 
        iowrite32(mcntrl_reset, I2C_REG_CTL(alg_data));
-       if (wait_reset(I2C_PNX_TIMEOUT, alg_data)) {
+       if (wait_reset(alg_data)) {
                ret = -ENODEV;
                goto out_clock;
        }
        init_completion(&alg_data->mif.complete);
 
-       ret = request_irq(i2c_pnx->irq, i2c_pnx_interrupt,
+       alg_data->irq = platform_get_irq(pdev, 0);
+       if (alg_data->irq < 0) {
+               dev_err(&pdev->dev, "Failed to get IRQ from platform 
resource\n");
+               goto out_irq;
+       }
+       ret = request_irq(alg_data->irq, i2c_pnx_interrupt,
                        0, pdev->name, alg_data);
        if (ret)
                goto out_clock;
@@ -663,39 +730,39 @@ static int __devinit i2c_pnx_probe(struc
                goto out_irq;
        }
 
+       of_i2c_register_devices(&alg_data->adapter);
+
        dev_dbg(&pdev->dev, "%s: Master at %#8x, irq %d.\n",
-              alg_data->adapter.name, i2c_pnx->base, i2c_pnx->irq);
+              alg_data->adapter.name, res->start, alg_data->irq);
 
        return 0;
 
 out_irq:
-       free_irq(i2c_pnx->irq, alg_data);
+       free_irq(alg_data->irq, alg_data);
 out_clock:
        clk_disable(alg_data->clk);
 out_unmap:
        iounmap(alg_data->ioaddr);
 out_release:
-       release_mem_region(i2c_pnx->base, I2C_PNX_REGION_SIZE);
+       release_mem_region(res->start, I2C_PNX_REGION_SIZE);
 out_clkget:
        clk_put(alg_data->clk);
 out_drvdata:
        kfree(alg_data);
 err_kzalloc:
        platform_set_drvdata(pdev, NULL);
-out:
        return ret;
 }
 
 static int __devexit i2c_pnx_remove(struct platform_device *pdev)
 {
        struct i2c_pnx_algo_data *alg_data = platform_get_drvdata(pdev);
-       struct i2c_pnx_data *i2c_pnx = alg_data->i2c_pnx;
 
-       free_irq(i2c_pnx->irq, alg_data);
+       free_irq(alg_data->irq, alg_data);
        i2c_del_adapter(&alg_data->adapter);
        clk_disable(alg_data->clk);
        iounmap(alg_data->ioaddr);
-       release_mem_region(i2c_pnx->base, I2C_PNX_REGION_SIZE);
+       release_mem_region(alg_data->base, I2C_PNX_REGION_SIZE);
        clk_put(alg_data->clk);
        kfree(alg_data);
        platform_set_drvdata(pdev, NULL);
@@ -703,10 +770,21 @@ static int __devexit i2c_pnx_remove(stru
        return 0;
 }
 
+#ifdef CONFIG_OF
+static const struct of_device_id i2c_pnx_of_match[] = {
+       { .compatible = "nxp,pnx-i2c" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, i2c_pnx_of_match);
+#endif
+
 static struct platform_driver i2c_pnx_driver = {
        .driver = {
                .name = "pnx-i2c",
                .owner = THIS_MODULE,
+#ifdef CONFIG_OF
+               .of_match_table = i2c_pnx_of_match,
+#endif
        },
        .probe = i2c_pnx_probe,
        .remove = __devexit_p(i2c_pnx_remove),
--- linux-2.6.orig/include/linux/i2c-pnx.h
+++ linux-2.6/include/linux/i2c-pnx.h
@@ -29,14 +29,10 @@ struct i2c_pnx_algo_data {
        struct i2c_pnx_mif      mif;
        int                     last;
        struct clk              *clk;
-       struct i2c_pnx_data     *i2c_pnx;
        struct i2c_adapter      adapter;
-};
-
-struct i2c_pnx_data {
-       const char *name;
-       u32 base;
-       int irq;
+       phys_addr_t             base;
+       int                     irq;
+       u32                     timeout;
 };
 
 #endif /* __I2C_PNX_H__ */
--
To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to