Commit 20c9d2c4ab8243a1c311248232954b2c1da3ba75

Authored by Kalle Jokiniemi
Committed by Ben Dooks
1 parent f38e66e007

i2c-omap: add mpu wake up latency constraint in i2c

While waiting for completion of the i2c transfer, the
MPU could hit OFF mode and cause several msecs of
delay that made i2c transfers fail more often. The
extra delays and subsequent re-trys cause i2c clocks
to be active more often. This has also an negative
effect on power consumption.

Created a mechanism for passing and using the
constraint setting function in driver code. The used
mpu wake up latency constraints are now set individually
per bus, and they are calculated based on clock rate
and fifo size.

Thanks to Jarkko Nikula, Moiz Sonasath, Paul Walmsley,
and Nishanth Menon for tuning out the details of
this patch.

Updates by Kevin as requested by Tony:

- Remove omap_set_i2c_constraint_func() in favor of conditionally
  adding the flag in omap_i2c_add_bus() in order to keep all the OMAP
  conditional checking in a single location.
- Update set_mpu_wkup_lat prototypes to match OMAP PM layer so
  OMAP PM function can be used directly in pdata.

Cc: Moiz Sonasath <m-sonasath@ti.com>
Cc: Jarkko Nikula <jhnikula@gmail.com>
Cc: Paul Walmsley <paul@pwsan.com>
Cc: Nishanth Menon <nm@ti.com>
Signed-off-by: Kalle Jokiniemi <kalle.jokiniemi@digia.com>
Signed-off-by: Kevin Hilman <khilman@deeprootsystems.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Ben Dooks <ben-linux@fluff.org>

Showing 3 changed files with 56 additions and 16 deletions Side-by-side Diff

arch/arm/plat-omap/i2c.c
... ... @@ -26,9 +26,12 @@
26 26 #include <linux/kernel.h>
27 27 #include <linux/platform_device.h>
28 28 #include <linux/i2c.h>
  29 +#include <linux/i2c-omap.h>
  30 +
29 31 #include <mach/irqs.h>
30 32 #include <plat/mux.h>
31 33 #include <plat/i2c.h>
  34 +#include <plat/omap-pm.h>
32 35  
33 36 #define OMAP_I2C_SIZE 0x3f
34 37 #define OMAP1_I2C_BASE 0xfffb3800
35 38  
36 39  
37 40  
... ... @@ -70,14 +73,14 @@
70 73 }, \
71 74 }
72 75  
73   -static u32 i2c_rate[ARRAY_SIZE(i2c_resources)];
  76 +static struct omap_i2c_bus_platform_data i2c_pdata[ARRAY_SIZE(i2c_resources)];
74 77 static struct platform_device omap_i2c_devices[] = {
75   - I2C_DEV_BUILDER(1, i2c_resources[0], &i2c_rate[0]),
  78 + I2C_DEV_BUILDER(1, i2c_resources[0], &i2c_pdata[0]),
76 79 #if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
77   - I2C_DEV_BUILDER(2, i2c_resources[1], &i2c_rate[1]),
  80 + I2C_DEV_BUILDER(2, i2c_resources[1], &i2c_pdata[1]),
78 81 #endif
79 82 #if defined(CONFIG_ARCH_OMAP3)
80   - I2C_DEV_BUILDER(3, i2c_resources[2], &i2c_rate[2]),
  83 + I2C_DEV_BUILDER(3, i2c_resources[2], &i2c_pdata[2]),
81 84 #endif
82 85 };
83 86  
84 87  
... ... @@ -100,10 +103,12 @@
100 103 static int __init omap_i2c_add_bus(int bus_id)
101 104 {
102 105 struct platform_device *pdev;
  106 + struct omap_i2c_bus_platform_data *pd;
103 107 struct resource *res;
104 108 resource_size_t base, irq;
105 109  
106 110 pdev = &omap_i2c_devices[bus_id - 1];
  111 + pd = pdev->dev.platform_data;
107 112 if (bus_id == 1) {
108 113 res = pdev->resource;
109 114 if (cpu_class_is_omap1()) {
... ... @@ -123,6 +128,15 @@
123 128 if (cpu_class_is_omap2())
124 129 omap2_i2c_mux_pins(bus_id);
125 130  
  131 + /*
  132 + * When waiting for completion of a i2c transfer, we need to
  133 + * set a wake up latency constraint for the MPU. This is to
  134 + * ensure quick enough wakeup from idle, when transfer
  135 + * completes.
  136 + */
  137 + if (cpu_is_omap34xx())
  138 + pd->set_mpu_wkup_lat = omap_pm_set_max_mpu_wakeup_lat;
  139 +
126 140 return platform_device_register(pdev);
127 141 }
128 142  
... ... @@ -146,8 +160,8 @@
146 160 get_options(str, 3, ints);
147 161 if (ints[0] < 2 || ints[1] < 1 || ints[1] > ports)
148 162 return 0;
149   - i2c_rate[ints[1] - 1] = ints[2];
150   - i2c_rate[ints[1] - 1] |= OMAP_I2C_CMDLINE_SETUP;
  163 + i2c_pdata[ints[1] - 1].clkrate = ints[2];
  164 + i2c_pdata[ints[1] - 1].clkrate |= OMAP_I2C_CMDLINE_SETUP;
151 165  
152 166 return 1;
153 167 }
... ... @@ -161,9 +175,9 @@
161 175 {
162 176 int i, err = 0;
163 177  
164   - for (i = 0; i < ARRAY_SIZE(i2c_rate); i++)
165   - if (i2c_rate[i] & OMAP_I2C_CMDLINE_SETUP) {
166   - i2c_rate[i] &= ~OMAP_I2C_CMDLINE_SETUP;
  178 + for (i = 0; i < ARRAY_SIZE(i2c_pdata); i++)
  179 + if (i2c_pdata[i].clkrate & OMAP_I2C_CMDLINE_SETUP) {
  180 + i2c_pdata[i].clkrate &= ~OMAP_I2C_CMDLINE_SETUP;
167 181 err = omap_i2c_add_bus(i + 1);
168 182 if (err)
169 183 goto out;
... ... @@ -197,9 +211,10 @@
197 211 return err;
198 212 }
199 213  
200   - if (!i2c_rate[bus_id - 1])
201   - i2c_rate[bus_id - 1] = clkrate;
202   - i2c_rate[bus_id - 1] &= ~OMAP_I2C_CMDLINE_SETUP;
  214 + if (!i2c_pdata[bus_id - 1].clkrate)
  215 + i2c_pdata[bus_id - 1].clkrate = clkrate;
  216 +
  217 + i2c_pdata[bus_id - 1].clkrate &= ~OMAP_I2C_CMDLINE_SETUP;
203 218  
204 219 return omap_i2c_add_bus(bus_id);
205 220 }
drivers/i2c/busses/i2c-omap.c
... ... @@ -38,6 +38,7 @@
38 38 #include <linux/clk.h>
39 39 #include <linux/io.h>
40 40 #include <linux/slab.h>
  41 +#include <linux/i2c-omap.h>
41 42  
42 43 /* I2C controller revisions */
43 44 #define OMAP_I2C_REV_2 0x20
... ... @@ -175,6 +176,9 @@
175 176 struct clk *fclk; /* Functional clock */
176 177 struct completion cmd_complete;
177 178 struct resource *ioarea;
  179 + u32 latency; /* maximum mpu wkup latency */
  180 + void (*set_mpu_wkup_lat)(struct device *dev,
  181 + long latency);
178 182 u32 speed; /* Speed of bus in Khz */
179 183 u16 cmd_err;
180 184 u8 *buf;
181 185  
... ... @@ -603,8 +607,12 @@
603 607 * REVISIT: We should abort the transfer on signals, but the bus goes
604 608 * into arbitration and we're currently unable to recover from it.
605 609 */
  610 + if (dev->set_mpu_wkup_lat != NULL)
  611 + dev->set_mpu_wkup_lat(dev->dev, dev->latency);
606 612 r = wait_for_completion_timeout(&dev->cmd_complete,
607 613 OMAP_I2C_TIMEOUT);
  614 + if (dev->set_mpu_wkup_lat != NULL)
  615 + dev->set_mpu_wkup_lat(dev->dev, -1);
608 616 dev->buf_len = 0;
609 617 if (r < 0)
610 618 return r;
... ... @@ -927,6 +935,7 @@
927 935 struct omap_i2c_dev *dev;
928 936 struct i2c_adapter *adap;
929 937 struct resource *mem, *irq, *ioarea;
  938 + struct omap_i2c_bus_platform_data *pdata = pdev->dev.platform_data;
930 939 irq_handler_t isr;
931 940 int r;
932 941 u32 speed = 0;
... ... @@ -956,10 +965,13 @@
956 965 goto err_release_region;
957 966 }
958 967  
959   - if (pdev->dev.platform_data != NULL)
960   - speed = *(u32 *)pdev->dev.platform_data;
961   - else
962   - speed = 100; /* Defualt speed */
  968 + if (pdata != NULL) {
  969 + speed = pdata->clkrate;
  970 + dev->set_mpu_wkup_lat = pdata->set_mpu_wkup_lat;
  971 + } else {
  972 + speed = 100; /* Default speed */
  973 + dev->set_mpu_wkup_lat = NULL;
  974 + }
963 975  
964 976 dev->speed = speed;
965 977 dev->idle = 1;
... ... @@ -1011,6 +1023,10 @@
1011 1023 dev->fifo_size = (dev->fifo_size / 2);
1012 1024 dev->b_hw = 1; /* Enable hardware fixes */
1013 1025 }
  1026 + /* calculate wakeup latency constraint for MPU */
  1027 + if (dev->set_mpu_wkup_lat != NULL)
  1028 + dev->latency = (1000000 * dev->fifo_size) /
  1029 + (1000 * speed / 8);
1014 1030 }
1015 1031  
1016 1032 /* reset ASAP, clearing any IRQs */
include/linux/i2c-omap.h
  1 +#ifndef __I2C_OMAP_H__
  2 +#define __I2C_OMAP_H__
  3 +
  4 +struct omap_i2c_bus_platform_data {
  5 + u32 clkrate;
  6 + void (*set_mpu_wkup_lat)(struct device *dev, long set);
  7 +};
  8 +
  9 +#endif