Merge git://git.denx.de/u-boot-fsl-qoriq
[oweals/u-boot.git] / drivers / i2c / omap24xx_i2c.c
index 8dea5fa55661dca962f7e94e0da26c0073f9e816..5d33815146216910f66a6a4cf91cd5b2447a06dd 100644 (file)
@@ -39,6 +39,7 @@
  */
 
 #include <common.h>
+#include <dm.h>
 #include <i2c.h>
 
 #include <asm/arch/i2c.h>
@@ -53,38 +54,62 @@ DECLARE_GLOBAL_DATA_PTR;
 /* Absolutely safe for status update at 100 kHz I2C: */
 #define I2C_WAIT       200
 
+struct omap_i2c {
+       struct udevice *clk;
+       struct i2c *regs;
+       unsigned int speed;
+       int waitdelay;
+       int clk_id;
+};
+
 static int omap24_i2c_findpsc(u32 *pscl, u32 *psch, uint speed)
 {
-       unsigned int sampleclk, prescaler;
-       int fsscll, fssclh;
+       unsigned long internal_clk = 0, fclk;
+       unsigned int prescaler;
 
-       speed <<= 1;
-       prescaler = 0;
        /*
-        * some divisors may cause a precission loss, but shouldn't
-        * be a big thing, because i2c_clk is then allready very slow.
+        * This method is only called for Standard and Fast Mode speeds
+        *
+        * For some TI SoCs it is explicitly written in TRM (e,g, SPRUHZ6G,
+        * page 5685, Table 24-7)
+        * that the internal I2C clock (after prescaler) should be between
+        * 7-12 MHz (at least for Fast Mode (FS)).
+        *
+        * Such approach is used in v4.9 Linux kernel in:
+        * ./drivers/i2c/busses/i2c-omap.c (omap_i2c_init function).
         */
-       while (prescaler <= 0xFF) {
-               sampleclk = I2C_IP_CLK / (prescaler+1);
 
-               fsscll = sampleclk / speed;
-               fssclh = fsscll;
-               fsscll -= I2C_FASTSPEED_SCLL_TRIM;
-               fssclh -= I2C_FASTSPEED_SCLH_TRIM;
-
-               if (((fsscll > 0) && (fssclh > 0)) &&
-                   ((fsscll <= (255-I2C_FASTSPEED_SCLL_TRIM)) &&
-                   (fssclh <= (255-I2C_FASTSPEED_SCLH_TRIM)))) {
-                       if (pscl)
-                               *pscl = fsscll;
-                       if (psch)
-                               *psch = fssclh;
-
-                       return prescaler;
-               }
-               prescaler++;
+       speed /= 1000; /* convert speed to kHz */
+
+       if (speed > 100)
+               internal_clk = 9600;
+       else
+               internal_clk = 4000;
+
+       fclk = I2C_IP_CLK / 1000;
+       prescaler = fclk / internal_clk;
+       prescaler = prescaler - 1;
+
+       if (speed > 100) {
+               unsigned long scl;
+
+               /* Fast mode */
+               scl = internal_clk / speed;
+               *pscl = scl - (scl / 3) - I2C_FASTSPEED_SCLL_TRIM;
+               *psch = (scl / 3) - I2C_FASTSPEED_SCLH_TRIM;
+       } else {
+               /* Standard mode */
+               *pscl = internal_clk / (speed * 2) - I2C_FASTSPEED_SCLL_TRIM;
+               *psch = internal_clk / (speed * 2) - I2C_FASTSPEED_SCLH_TRIM;
        }
-       return -1;
+
+       debug("%s: speed [kHz]: %d psc: 0x%x sscl: 0x%x ssch: 0x%x\n",
+             __func__, speed, prescaler, *pscl, *psch);
+
+       if (*pscl <= 0 || *psch <= 0 || prescaler <= 0)
+               return -EINVAL;
+
+       return prescaler;
 }
 
 /*
@@ -97,7 +122,7 @@ static int wait_for_bb(struct i2c *i2c_base, int waitdelay)
        u16 stat;
 
        writew(0xFFFF, &i2c_base->stat);        /* clear current interrupts...*/
-#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX)
+#if defined(CONFIG_OMAP34XX)
        while ((stat = readw(&i2c_base->stat) & I2C_STAT_BB) && timeout--) {
 #else
        /* Read RAW status */
@@ -128,7 +153,7 @@ static u16 wait_for_event(struct i2c *i2c_base, int waitdelay)
 
        do {
                udelay(waitdelay);
-#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX)
+#if defined(CONFIG_OMAP34XX)
                status = readw(&i2c_base->stat);
 #else
                /* Read RAW status */
@@ -313,7 +338,7 @@ retry:
        /* own address */
        writew(slaveadd, &i2c_base->oa);
 
-#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX)
+#if defined(CONFIG_OMAP34XX)
        /*
         * Have to enable interrupts for OMAP2/3, these IPs don't have
         * an 'irqstatus_raw' register and we shall have to poll 'stat'
@@ -667,6 +692,7 @@ wr_exit:
        return i2c_error;
 }
 
+#ifndef CONFIG_DM_I2C
 /*
  * The legacy I2C functions. These need to get removed once
  * all users of this driver are converted to DM.
@@ -680,15 +706,15 @@ static struct i2c *omap24_get_base(struct i2c_adapter *adap)
        case 1:
                return (struct i2c *)I2C_BASE2;
                break;
-#if (I2C_BUS_MAX > 2)
+#if (CONFIG_SYS_I2C_BUS_MAX > 2)
        case 2:
                return (struct i2c *)I2C_BASE3;
                break;
-#if (I2C_BUS_MAX > 3)
+#if (CONFIG_SYS_I2C_BUS_MAX > 3)
        case 3:
                return (struct i2c *)I2C_BASE4;
                break;
-#if (I2C_BUS_MAX > 4)
+#if (CONFIG_SYS_I2C_BUS_MAX > 4)
        case 4:
                return (struct i2c *)I2C_BASE5;
                break;
@@ -729,7 +755,7 @@ static uint omap24_i2c_setspeed(struct i2c_adapter *adap, uint speed)
 
        ret = __omap24_i2c_setspeed(i2c_base, speed, &adap->waitdelay);
        if (ret) {
-               error("%s: set i2c speed failed\n", __func__);
+               pr_err("%s: set i2c speed failed\n", __func__);
                return ret;
        }
 
@@ -769,7 +795,7 @@ U_BOOT_I2C_ADAP_COMPLETE(omap24_1, omap24_i2c_init, omap24_i2c_probe,
                         CONFIG_SYS_OMAP24_I2C_SPEED1,
                         CONFIG_SYS_OMAP24_I2C_SLAVE1,
                         1)
-#if (I2C_BUS_MAX > 2)
+#if (CONFIG_SYS_I2C_BUS_MAX > 2)
 #if !defined(CONFIG_SYS_OMAP24_I2C_SPEED2)
 #define CONFIG_SYS_OMAP24_I2C_SPEED2 CONFIG_SYS_OMAP24_I2C_SPEED
 #endif
@@ -782,7 +808,7 @@ U_BOOT_I2C_ADAP_COMPLETE(omap24_2, omap24_i2c_init, omap24_i2c_probe,
                         CONFIG_SYS_OMAP24_I2C_SPEED2,
                         CONFIG_SYS_OMAP24_I2C_SLAVE2,
                         2)
-#if (I2C_BUS_MAX > 3)
+#if (CONFIG_SYS_I2C_BUS_MAX > 3)
 #if !defined(CONFIG_SYS_OMAP24_I2C_SPEED3)
 #define CONFIG_SYS_OMAP24_I2C_SPEED3 CONFIG_SYS_OMAP24_I2C_SPEED
 #endif
@@ -795,7 +821,7 @@ U_BOOT_I2C_ADAP_COMPLETE(omap24_3, omap24_i2c_init, omap24_i2c_probe,
                         CONFIG_SYS_OMAP24_I2C_SPEED3,
                         CONFIG_SYS_OMAP24_I2C_SLAVE3,
                         3)
-#if (I2C_BUS_MAX > 4)
+#if (CONFIG_SYS_I2C_BUS_MAX > 4)
 #if !defined(CONFIG_SYS_OMAP24_I2C_SPEED4)
 #define CONFIG_SYS_OMAP24_I2C_SPEED4 CONFIG_SYS_OMAP24_I2C_SPEED
 #endif
@@ -811,3 +837,93 @@ U_BOOT_I2C_ADAP_COMPLETE(omap24_4, omap24_i2c_init, omap24_i2c_probe,
 #endif
 #endif
 #endif
+
+#else /* CONFIG_DM_I2C */
+
+static int omap_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs)
+{
+       struct omap_i2c *priv = dev_get_priv(bus);
+       int ret;
+
+       debug("i2c_xfer: %d messages\n", nmsgs);
+       for (; nmsgs > 0; nmsgs--, msg++) {
+               debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len);
+               if (msg->flags & I2C_M_RD) {
+                       ret = __omap24_i2c_read(priv->regs, priv->waitdelay,
+                                               msg->addr, 0, 0, msg->buf,
+                                               msg->len);
+               } else {
+                       ret = __omap24_i2c_write(priv->regs, priv->waitdelay,
+                                                msg->addr, 0, 0, msg->buf,
+                                                msg->len);
+               }
+               if (ret) {
+                       debug("i2c_write: error sending\n");
+                       return -EREMOTEIO;
+               }
+       }
+
+       return 0;
+}
+
+static int omap_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
+{
+       struct omap_i2c *priv = dev_get_priv(bus);
+
+       priv->speed = speed;
+
+       return __omap24_i2c_setspeed(priv->regs, speed, &priv->waitdelay);
+}
+
+static int omap_i2c_probe_chip(struct udevice *bus, uint chip_addr,
+                                    uint chip_flags)
+{
+       struct omap_i2c *priv = dev_get_priv(bus);
+
+       return __omap24_i2c_probe(priv->regs, priv->waitdelay, chip_addr);
+}
+
+static int omap_i2c_probe(struct udevice *bus)
+{
+       struct omap_i2c *priv = dev_get_priv(bus);
+
+       __omap24_i2c_init(priv->regs, priv->speed, 0, &priv->waitdelay);
+
+       return 0;
+}
+
+static int omap_i2c_ofdata_to_platdata(struct udevice *bus)
+{
+       struct omap_i2c *priv = dev_get_priv(bus);
+
+       priv->regs = map_physmem(devfdt_get_addr(bus), sizeof(void *),
+                                MAP_NOCACHE);
+       priv->speed = CONFIG_SYS_OMAP24_I2C_SPEED;
+
+       return 0;
+}
+
+static const struct dm_i2c_ops omap_i2c_ops = {
+       .xfer           = omap_i2c_xfer,
+       .probe_chip     = omap_i2c_probe_chip,
+       .set_bus_speed  = omap_i2c_set_bus_speed,
+};
+
+static const struct udevice_id omap_i2c_ids[] = {
+       { .compatible = "ti,omap3-i2c" },
+       { .compatible = "ti,omap4-i2c" },
+       { }
+};
+
+U_BOOT_DRIVER(i2c_omap) = {
+       .name   = "i2c_omap",
+       .id     = UCLASS_I2C,
+       .of_match = omap_i2c_ids,
+       .ofdata_to_platdata = omap_i2c_ofdata_to_platdata,
+       .probe  = omap_i2c_probe,
+       .priv_auto_alloc_size = sizeof(struct omap_i2c),
+       .ops    = &omap_i2c_ops,
+       .flags  = DM_FLAG_PRE_RELOC,
+};
+
+#endif /* CONFIG_DM_I2C */