Merge branch 'master' of git://git.denx.de/u-boot-mmc
[platform/kernel/u-boot.git] / drivers / i2c / fsl_i2c.c
index b0e65fc..ff3dc25 100644 (file)
@@ -12,6 +12,8 @@
 #include <i2c.h>               /* Functional interface */
 #include <asm/io.h>
 #include <asm/fsl_i2c.h>       /* HW definitions */
+#include <dm.h>
+#include <mapmem.h>
 
 /* The maximum number of microseconds we will wait until another master has
  * released the bus.  If not defined in the board header file, then use a
@@ -34,6 +36,7 @@
 
 DECLARE_GLOBAL_DATA_PTR;
 
+#ifndef CONFIG_DM_I2C
 static const struct fsl_i2c_base *i2c_base[4] = {
        (struct fsl_i2c_base *)(CONFIG_SYS_IMMR + CONFIG_SYS_FSL_I2C_OFFSET),
 #ifdef CONFIG_SYS_FSL_I2C2_OFFSET
@@ -46,9 +49,11 @@ static const struct fsl_i2c_base *i2c_base[4] = {
        (struct fsl_i2c_base *)(CONFIG_SYS_IMMR + CONFIG_SYS_FSL_I2C4_OFFSET)
 #endif
 };
+#endif
 
 /* I2C speed map for a DFSR value of 1 */
 
+#ifdef __M68K__
 /*
  * Map I2C frequency dividers to FDR and DFSR values
  *
@@ -80,7 +85,6 @@ static const struct {
        unsigned short divider;
        u8 fdr;
 } fsl_i2c_speed_map[] = {
-#ifdef __M68K__
        {20, 32}, {22, 33}, {24, 34}, {26, 35},
        {28, 0}, {28, 36}, {30, 1}, {32, 37},
        {34, 2}, {36, 38}, {40, 3}, {40, 39},
@@ -98,8 +102,8 @@ static const struct {
        {1536, 61}, {1792, 62}, {1920, 27}, {2048, 63},
        {2304, 28}, {2560, 29}, {3072, 30}, {3840, 31},
        {-1, 31}
-#endif
 };
+#endif
 
 /**
  * Set the I2C bus speed for a given I2C device
@@ -192,6 +196,7 @@ static unsigned int set_i2c_bus_speed(const struct fsl_i2c_base *base,
        return speed;
 }
 
+#ifndef CONFIG_DM_I2C
 static unsigned int get_i2c_clock(int bus)
 {
        if (bus)
@@ -199,6 +204,7 @@ static unsigned int get_i2c_clock(int bus)
        else
                return gd->arch.i2c1_clk;       /* I2C1 clock */
 }
+#endif
 
 static int fsl_i2c_fixup(const struct fsl_i2c_base *base)
 {
@@ -247,9 +253,9 @@ err:
        return ret;
 }
 
-static void fsl_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd)
+static void __i2c_init(const struct fsl_i2c_base *base, int speed, int
+                      slaveadd, int i2c_clk, int busnum)
 {
-       const struct fsl_i2c_base *base;
        const unsigned long long timeout = usec2ticks(CONFIG_I2C_MBB_TIMEOUT);
        unsigned long long timeval;
 
@@ -260,11 +266,9 @@ static void fsl_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd)
        */
        i2c_init_board();
 #endif
-       base = (struct fsl_i2c_base *)i2c_base[adap->hwadapnr];
-
        writeb(0, &base->cr);           /* stop I2C controller */
        udelay(5);                      /* let it shutdown in peace */
-       set_i2c_bus_speed(base, get_i2c_clock(adap->hwadapnr), speed);
+       set_i2c_bus_speed(base, i2c_clk, speed);
        writeb(slaveadd << 1, &base->adr);/* write slave address */
        writeb(0x0, &base->sr);         /* clear status register */
        writeb(I2C_CR_MEN, &base->cr);  /* start I2C controller */
@@ -276,26 +280,15 @@ static void fsl_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd)
 
                if (fsl_i2c_fixup(base))
                        debug("i2c_init: BUS#%d failed to init\n",
-                             adap->hwadapnr);
+                             busnum);
 
                break;
        }
-
-#ifdef CONFIG_SYS_I2C_BOARD_LATE_INIT
-       /* Call board specific i2c bus reset routine AFTER the bus has been
-        * initialized. Use either this callpoint or i2c_init_board;
-        * which is called before i2c_init operations.
-        * For details about this problem see doc/I2C_Edge_Conditions.
-       */
-       i2c_board_late_init();
-#endif
 }
 
 static int
-i2c_wait4bus(struct i2c_adapter *adap)
+i2c_wait4bus(const struct fsl_i2c_base *base)
 {
-       struct fsl_i2c_base *base =
-               (struct fsl_i2c_base *)i2c_base[adap->hwadapnr];
        unsigned long long timeval = get_ticks();
        const unsigned long long timeout = usec2ticks(CONFIG_I2C_MBB_TIMEOUT);
 
@@ -307,14 +300,12 @@ i2c_wait4bus(struct i2c_adapter *adap)
        return 0;
 }
 
-static __inline__ int
-i2c_wait(struct i2c_adapter *adap, int write)
+static inline int
+i2c_wait(const struct fsl_i2c_base *base, int write)
 {
        u32 csr;
        unsigned long long timeval = get_ticks();
        const unsigned long long timeout = usec2ticks(CONFIG_I2C_TIMEOUT);
-       struct fsl_i2c_base *base =
-               (struct fsl_i2c_base *)i2c_base[adap->hwadapnr];
 
        do {
                csr = readb(&base->sr);
@@ -347,46 +338,39 @@ i2c_wait(struct i2c_adapter *adap, int write)
        return -1;
 }
 
-static __inline__ int
-i2c_write_addr(struct i2c_adapter *adap, u8 dev, u8 dir, int rsta)
+static inline int
+i2c_write_addr(const struct fsl_i2c_base *base, u8 dev, u8 dir, int rsta)
 {
-       struct fsl_i2c_base *base =
-               (struct fsl_i2c_base *)i2c_base[adap->hwadapnr];
-
        writeb(I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_MTX
               | (rsta ? I2C_CR_RSTA : 0),
               &base->cr);
 
        writeb((dev << 1) | dir, &base->dr);
 
-       if (i2c_wait(adap, I2C_WRITE_BIT) < 0)
+       if (i2c_wait(base, I2C_WRITE_BIT) < 0)
                return 0;
 
        return 1;
 }
 
-static __inline__ int
-__i2c_write(struct i2c_adapter *adap, u8 *data, int length)
+static inline int
+__i2c_write_data(const struct fsl_i2c_base *base, u8 *data, int length)
 {
-       struct fsl_i2c_base *base =
-               (struct fsl_i2c_base *)i2c_base[adap->hwadapnr];
        int i;
 
        for (i = 0; i < length; i++) {
                writeb(data[i], &base->dr);
 
-               if (i2c_wait(adap, I2C_WRITE_BIT) < 0)
+               if (i2c_wait(base, I2C_WRITE_BIT) < 0)
                        break;
        }
 
        return i;
 }
 
-static __inline__ int
-__i2c_read(struct i2c_adapter *adap, u8 *data, int length)
+static inline int
+__i2c_read_data(const struct fsl_i2c_base *base, u8 *data, int length)
 {
-       struct fsl_i2c_base *base =
-               (struct fsl_i2c_base *)i2c_base[adap->hwadapnr];
        int i;
 
        writeb(I2C_CR_MEN | I2C_CR_MSTA | ((length == 1) ? I2C_CR_TXAK : 0),
@@ -396,7 +380,7 @@ __i2c_read(struct i2c_adapter *adap, u8 *data, int length)
        readb(&base->dr);
 
        for (i = 0; i < length; i++) {
-               if (i2c_wait(adap, I2C_READ_BIT) < 0)
+               if (i2c_wait(base, I2C_READ_BIT) < 0)
                        break;
 
                /* Generate ack on last next to last byte */
@@ -416,57 +400,45 @@ __i2c_read(struct i2c_adapter *adap, u8 *data, int length)
 }
 
 static int
-fsl_i2c_read(struct i2c_adapter *adap, u8 chip_addr, uint offset, int olen,
-            u8 *data, int dlen)
+__i2c_read(const struct fsl_i2c_base *base, u8 chip_addr, u8 *offset, int olen,
+          u8 *data, int dlen)
 {
-       struct fsl_i2c_base *base =
-               (struct fsl_i2c_base *)i2c_base[adap->hwadapnr];
        int ret = -1; /* signal error */
-       u8 *o = (u8 *)&offset;
-       int len = olen * -1;
 
-       if (i2c_wait4bus(adap) < 0)
+       if (i2c_wait4bus(base) < 0)
                return -1;
 
-       /* To handle the need of I2C devices that require to write few bytes
-        * (more than 4 bytes of address as in the case of else part)
-        * of data before reading, Negative equivalent of dlen(bytes to write)
-        * is passed, but used the +ve part of len for writing data
+       /* Some drivers use offset lengths in excess of 4 bytes. These drivers
+        * adhere to the following convention:
+        * - the offset length is passed as negative (that is, the absolute
+        *   value of olen is the actual offset length)
+        * - the offset itself is passed in data, which is overwritten by the
+        *   subsequent read operation
         */
        if (olen < 0) {
-               /* Generate a START and send the Address and
-                * the Tx Bytes to the slave.
-                * "START: Address: Write bytes data[len]"
-                * IF part supports writing any number of bytes in contrast
-                * to the else part, which supports writing address offset
-                * of upto 4 bytes only.
-                * bytes that need to be written are passed in
-                * "data", which will eventually keep the data READ,
-                * after writing the len bytes out of it
-                */
-               if (i2c_write_addr(adap, chip_addr, I2C_WRITE_BIT, 0) != 0)
-                       ret = __i2c_write(adap, data, len);
-
-               if (ret != len)
+               if (i2c_write_addr(base, chip_addr, I2C_WRITE_BIT, 0) != 0)
+                       ret = __i2c_write_data(base, data, -olen);
+
+               if (ret != -olen)
                        return -1;
 
-               if (dlen && i2c_write_addr(adap, chip_addr,
+               if (dlen && i2c_write_addr(base, chip_addr,
                                           I2C_READ_BIT, 1) != 0)
-                       ret = __i2c_read(adap, data, dlen);
+                       ret = __i2c_read_data(base, data, dlen);
        } else {
                if ((!dlen || olen > 0) &&
-                   i2c_write_addr(adap, chip_addr, I2C_WRITE_BIT, 0) != 0  &&
-                   __i2c_write(adap, &o[4 - olen], olen) == olen)
+                   i2c_write_addr(base, chip_addr, I2C_WRITE_BIT, 0) != 0  &&
+                   __i2c_write_data(base, offset, olen) == olen)
                        ret = 0; /* No error so far */
 
-               if (dlen && i2c_write_addr(adap, chip_addr, I2C_READ_BIT,
+               if (dlen && i2c_write_addr(base, chip_addr, I2C_READ_BIT,
                                           olen ? 1 : 0) != 0)
-                       ret = __i2c_read(adap, data, dlen);
+                       ret = __i2c_read_data(base, data, dlen);
        }
 
        writeb(I2C_CR_MEN, &base->cr);
 
-       if (i2c_wait4bus(adap)) /* Wait until STOP */
+       if (i2c_wait4bus(base)) /* Wait until STOP */
                debug("i2c_read: wait4bus timed out\n");
 
        if (ret == dlen)
@@ -476,24 +448,21 @@ fsl_i2c_read(struct i2c_adapter *adap, u8 chip_addr, uint offset, int olen,
 }
 
 static int
-fsl_i2c_write(struct i2c_adapter *adap, u8 chip_addr, uint offset, int olen,
-             u8 *data, int dlen)
+__i2c_write(const struct fsl_i2c_base *base, u8 chip_addr, u8 *offset, int olen,
+           u8 *data, int dlen)
 {
-       struct fsl_i2c_base *base =
-               (struct fsl_i2c_base *)i2c_base[adap->hwadapnr];
        int ret = -1; /* signal error */
-       u8 *o = (u8 *)&offset;
 
-       if (i2c_wait4bus(adap) < 0)
+       if (i2c_wait4bus(base) < 0)
                return -1;
 
-       if (i2c_write_addr(adap, chip_addr, I2C_WRITE_BIT, 0) != 0 &&
-           __i2c_write(adap, &o[4 - olen], olen) == olen) {
-               ret = __i2c_write(adap, data, dlen);
+       if (i2c_write_addr(base, chip_addr, I2C_WRITE_BIT, 0) != 0 &&
+           __i2c_write_data(base, offset, olen) == olen) {
+               ret = __i2c_write_data(base, data, dlen);
        }
 
        writeb(I2C_CR_MEN, &base->cr);
-       if (i2c_wait4bus(adap)) /* Wait until STOP */
+       if (i2c_wait4bus(base)) /* Wait until STOP */
                debug("i2c_write: wait4bus timed out\n");
 
        if (ret == dlen)
@@ -503,10 +472,8 @@ fsl_i2c_write(struct i2c_adapter *adap, u8 chip_addr, uint offset, int olen,
 }
 
 static int
-fsl_i2c_probe(struct i2c_adapter *adap, uchar chip)
+__i2c_probe_chip(const struct fsl_i2c_base *base, uchar chip)
 {
-       struct fsl_i2c_base *base =
-               (struct fsl_i2c_base *)i2c_base[adap->hwadapnr];
        /* For unknow reason the controller will ACK when
         * probing for a slave with the same address, so skip
         * it.
@@ -514,44 +481,174 @@ fsl_i2c_probe(struct i2c_adapter *adap, uchar chip)
        if (chip == (readb(&base->adr) >> 1))
                return -1;
 
-       return fsl_i2c_read(adap, chip, 0, 0, NULL, 0);
+       return __i2c_read(base, chip, 0, 0, NULL, 0);
 }
 
-static unsigned int fsl_i2c_set_bus_speed(struct i2c_adapter *adap,
-                       unsigned int speed)
+static unsigned int __i2c_set_bus_speed(const struct fsl_i2c_base *base,
+                       unsigned int speed, int i2c_clk)
 {
-       struct fsl_i2c_base *base =
-               (struct fsl_i2c_base *)i2c_base[adap->hwadapnr];
-
        writeb(0, &base->cr);           /* stop controller */
-       set_i2c_bus_speed(base, get_i2c_clock(adap->hwadapnr), speed);
+       set_i2c_bus_speed(base, i2c_clk, speed);
        writeb(I2C_CR_MEN, &base->cr);  /* start controller */
 
        return 0;
 }
 
+#ifndef CONFIG_DM_I2C
+static void fsl_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd)
+{
+       __i2c_init(i2c_base[adap->hwadapnr], speed, slaveadd,
+                  get_i2c_clock(adap->hwadapnr), adap->hwadapnr);
+}
+
+static int
+fsl_i2c_probe_chip(struct i2c_adapter *adap, uchar chip)
+{
+       return __i2c_probe_chip(i2c_base[adap->hwadapnr], chip);
+}
+
+static int
+fsl_i2c_read(struct i2c_adapter *adap, u8 chip_addr, uint offset, int olen,
+            u8 *data, int dlen)
+{
+       u8 *o = (u8 *)&offset;
+       return __i2c_read(i2c_base[adap->hwadapnr], chip_addr, &o[4 - olen],
+                         olen, data, dlen);
+}
+
+static int
+fsl_i2c_write(struct i2c_adapter *adap, u8 chip_addr, uint offset, int olen,
+             u8 *data, int dlen)
+{
+       u8 *o = (u8 *)&offset;
+       return __i2c_write(i2c_base[adap->hwadapnr], chip_addr, &o[4 - olen],
+                          olen, data, dlen);
+}
+
+static unsigned int fsl_i2c_set_bus_speed(struct i2c_adapter *adap,
+                                         unsigned int speed)
+{
+       return __i2c_set_bus_speed(i2c_base[adap->hwadapnr], speed,
+                                  get_i2c_clock(adap->hwadapnr));
+}
+
 /*
  * Register fsl i2c adapters
  */
-U_BOOT_I2C_ADAP_COMPLETE(fsl_0, fsl_i2c_init, fsl_i2c_probe, fsl_i2c_read,
+U_BOOT_I2C_ADAP_COMPLETE(fsl_0, fsl_i2c_init, fsl_i2c_probe_chip, fsl_i2c_read,
                         fsl_i2c_write, fsl_i2c_set_bus_speed,
                         CONFIG_SYS_FSL_I2C_SPEED, CONFIG_SYS_FSL_I2C_SLAVE,
                         0)
 #ifdef CONFIG_SYS_FSL_I2C2_OFFSET
-U_BOOT_I2C_ADAP_COMPLETE(fsl_1, fsl_i2c_init, fsl_i2c_probe, fsl_i2c_read,
+U_BOOT_I2C_ADAP_COMPLETE(fsl_1, fsl_i2c_init, fsl_i2c_probe_chip, fsl_i2c_read,
                         fsl_i2c_write, fsl_i2c_set_bus_speed,
                         CONFIG_SYS_FSL_I2C2_SPEED, CONFIG_SYS_FSL_I2C2_SLAVE,
                         1)
 #endif
 #ifdef CONFIG_SYS_FSL_I2C3_OFFSET
-U_BOOT_I2C_ADAP_COMPLETE(fsl_2, fsl_i2c_init, fsl_i2c_probe, fsl_i2c_read,
+U_BOOT_I2C_ADAP_COMPLETE(fsl_2, fsl_i2c_init, fsl_i2c_probe_chip, fsl_i2c_read,
                         fsl_i2c_write, fsl_i2c_set_bus_speed,
                         CONFIG_SYS_FSL_I2C3_SPEED, CONFIG_SYS_FSL_I2C3_SLAVE,
                         2)
 #endif
 #ifdef CONFIG_SYS_FSL_I2C4_OFFSET
-U_BOOT_I2C_ADAP_COMPLETE(fsl_3, fsl_i2c_init, fsl_i2c_probe, fsl_i2c_read,
+U_BOOT_I2C_ADAP_COMPLETE(fsl_3, fsl_i2c_init, fsl_i2c_probe_chip, fsl_i2c_read,
                         fsl_i2c_write, fsl_i2c_set_bus_speed,
                         CONFIG_SYS_FSL_I2C4_SPEED, CONFIG_SYS_FSL_I2C4_SLAVE,
                         3)
 #endif
+#else /* CONFIG_DM_I2C */
+static int fsl_i2c_probe_chip(struct udevice *bus, u32 chip_addr,
+                             u32 chip_flags)
+{
+       struct fsl_i2c_dev *dev = dev_get_priv(bus);
+       return __i2c_probe_chip(dev->base, chip_addr);
+}
+
+static int fsl_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
+{
+       struct fsl_i2c_dev *dev = dev_get_priv(bus);
+       return __i2c_set_bus_speed(dev->base, speed, dev->i2c_clk);
+}
+
+static int fsl_i2c_ofdata_to_platdata(struct udevice *bus)
+{
+       struct fsl_i2c_dev *dev = dev_get_priv(bus);
+       fdt_addr_t addr;
+       fdt_size_t size;
+       int node = dev_of_offset(bus);
+
+       addr = fdtdec_get_addr_size_auto_noparent(gd->fdt_blob, node, "reg", 0,
+                                                 &size, false);
+
+       dev->base = map_sysmem(CONFIG_SYS_IMMR + addr, size);
+
+       if (!dev->base)
+               return -ENOMEM;
+
+       dev->index = fdtdec_get_int(gd->fdt_blob, node, "cell-index", -1);
+       dev->slaveadd = fdtdec_get_int(gd->fdt_blob, node,
+                                      "u-boot,i2c-slave-addr", 0x7f);
+       dev->speed = fdtdec_get_int(gd->fdt_blob, node, "clock-frequency",
+                                   400000);
+
+       dev->i2c_clk = dev->index ? gd->arch.i2c2_clk : gd->arch.i2c1_clk;
+
+       return 0;
+}
+
+static int fsl_i2c_probe(struct udevice *bus)
+{
+       struct fsl_i2c_dev *dev = dev_get_priv(bus);
+       __i2c_init(dev->base, dev->speed, dev->slaveadd, dev->i2c_clk,
+                  dev->index);
+       return 0;
+}
+
+static int fsl_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs)
+{
+       struct fsl_i2c_dev *dev = dev_get_priv(bus);
+       struct i2c_msg *dmsg, *omsg, dummy;
+
+       memset(&dummy, 0, sizeof(struct i2c_msg));
+
+       /* We expect either two messages (one with an offset and one with the
+        * actucal data) or one message (just data) */
+       if (nmsgs > 2 || nmsgs == 0) {
+               debug("%s: Only one or two messages are supported.", __func__);
+               return -1;
+       }
+
+       omsg = nmsgs == 1 ? &dummy : msg;
+       dmsg = nmsgs == 1 ? msg : msg + 1;
+
+       if (dmsg->flags & I2C_M_RD)
+               return __i2c_read(dev->base, dmsg->addr, omsg->buf, omsg->len,
+                                 dmsg->buf, dmsg->len);
+       else
+               return __i2c_write(dev->base, dmsg->addr, omsg->buf, omsg->len,
+                                  dmsg->buf, dmsg->len);
+}
+
+static const struct dm_i2c_ops fsl_i2c_ops = {
+       .xfer           = fsl_i2c_xfer,
+       .probe_chip     = fsl_i2c_probe_chip,
+       .set_bus_speed  = fsl_i2c_set_bus_speed,
+};
+
+static const struct udevice_id fsl_i2c_ids[] = {
+       { .compatible = "fsl-i2c", },
+       { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(i2c_fsl) = {
+       .name = "i2c_fsl",
+       .id = UCLASS_I2C,
+       .of_match = fsl_i2c_ids,
+       .probe = fsl_i2c_probe,
+       .ofdata_to_platdata = fsl_i2c_ofdata_to_platdata,
+       .priv_auto_alloc_size = sizeof(struct fsl_i2c_dev),
+       .ops = &fsl_i2c_ops,
+};
+
+#endif /* CONFIG_DM_I2C */