From 65a1a0ace554d61ea5a90377a54df1505275c1b1 Mon Sep 17 00:00:00 2001 From: Kenneth Waters Date: Mon, 25 Apr 2011 12:29:54 -0600 Subject: i2c: tegra: Enable new slave mode. For Tegra i2c controller to function properly new slave mode must be enabled. swarren notes: In particular, I found this was needed when working on enabling the Tegra audio driver on the Seaboard board. There are two different PCB layouts for this board; a "clamshell" version, which works just fine without this change, and the original non-clamshell version, which needs this change in order for I2C to operate correctly. Without it, I2C probing fails for some devices, e.g. with: wm8903 0-001a: Device with ID register 0 is not a WM8903 wm8903 0-001a: asoc: failed to probe CODEC wm8903.0-001a: -19 asoc: failed to instantiate card tegra-wm8903: -19 ALSA device list: No soundcards found. Signed-off-by: Rakesh Iyer Cc: stable Signed-off-by: Stephen Warren Signed-off-by: Ben Dooks --- drivers/i2c/busses/i2c-tegra.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index b4ab39b741eb..5f1b92c74bd9 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -330,6 +330,11 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) i2c_writel(i2c_dev, 0, I2C_INT_MASK); clk_set_rate(i2c_dev->clk, i2c_dev->bus_clk_rate * 8); + if (!i2c_dev->is_dvc) { + u32 sl_cfg = i2c_readl(i2c_dev, I2C_SL_CNFG); + i2c_writel(i2c_dev, sl_cfg | I2C_SL_CNFG_NEWSL, I2C_SL_CNFG); + } + val = 7 << I2C_FIFO_CONTROL_TX_TRIG_SHIFT | 0 << I2C_FIFO_CONTROL_RX_TRIG_SHIFT; i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL); -- cgit v1.2.3 From cb63c62d8ee56d169463d8125ec32e1aa0fe11de Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 25 Apr 2011 15:32:25 -0600 Subject: i2c: tegra: recover from spurious interrupt storm Re-init the I2C controller when an IRQ arrives with no I2C_INT_STATUS bits set to indicate why the interrupt was sent. Storms of such mystery interrupts are infrequently seen. Dump some more status when these interrupts arrive. Set an error for the current request and wake up the requester (rather than timing out the request or possibly silently ignoring the interrupts). If the I2C block is inside the DVC, also ACK the DVC I2C transfer done interrupt in the ISR error return path, as is done for the normal return path. Signed-off-by: Todd Poynor [swarren: Fix minor checkpatch whitespace issue, commit tag] Signed-off-by: Stephen Warren Signed-off-by: Ben Dooks --- drivers/i2c/busses/i2c-tegra.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 5f1b92c74bd9..c0b9aa7df0e2 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -37,6 +37,7 @@ #define I2C_CNFG 0x000 #define I2C_CNFG_PACKET_MODE_EN (1<<10) #define I2C_CNFG_NEW_MASTER_FSM (1<<11) +#define I2C_STATUS 0x01C #define I2C_SL_CNFG 0x020 #define I2C_SL_CNFG_NEWSL (1<<2) #define I2C_SL_ADDR1 0x02c @@ -77,6 +78,7 @@ #define I2C_ERR_NONE 0x00 #define I2C_ERR_NO_ACK 0x01 #define I2C_ERR_ARBITRATION_LOST 0x02 +#define I2C_ERR_UNKNOWN_INTERRUPT 0x04 #define PACKET_HEADER0_HEADER_SIZE_SHIFT 28 #define PACKET_HEADER0_PACKET_ID_SHIFT 16 @@ -121,6 +123,7 @@ struct tegra_i2c_dev { void __iomem *base; int cont_id; int irq; + bool irq_disabled; int is_dvc; struct completion msg_complete; int msg_err; @@ -343,6 +346,12 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) err = -ETIMEDOUT; clk_disable(i2c_dev->clk); + + if (i2c_dev->irq_disabled) { + i2c_dev->irq_disabled = 0; + enable_irq(i2c_dev->irq); + } + return err; } @@ -355,8 +364,19 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id) status = i2c_readl(i2c_dev, I2C_INT_STATUS); if (status == 0) { - dev_warn(i2c_dev->dev, "interrupt with no status\n"); - return IRQ_NONE; + dev_warn(i2c_dev->dev, "irq status 0 %08x %08x %08x\n", + i2c_readl(i2c_dev, I2C_PACKET_TRANSFER_STATUS), + i2c_readl(i2c_dev, I2C_STATUS), + i2c_readl(i2c_dev, I2C_CNFG)); + i2c_dev->msg_err |= I2C_ERR_UNKNOWN_INTERRUPT; + + if (!i2c_dev->irq_disabled) { + disable_irq_nosync(i2c_dev->irq); + i2c_dev->irq_disabled = 1; + } + + complete(&i2c_dev->msg_complete); + goto err; } if (unlikely(status & status_err)) { @@ -396,6 +416,8 @@ err: I2C_INT_PACKET_XFER_COMPLETE | I2C_INT_TX_FIFO_DATA_REQ | I2C_INT_RX_FIFO_DATA_REQ); i2c_writel(i2c_dev, status, I2C_INT_STATUS); + if (i2c_dev->is_dvc) + dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS); return IRQ_HANDLED; } -- cgit v1.2.3 From 2078cf3b2230cc2ee456e67d28dd9a869097e1ff Mon Sep 17 00:00:00 2001 From: Erik Gilling Date: Mon, 25 Apr 2011 15:32:26 -0600 Subject: i2c: tegra: fix repeated start handling A repeated start should be used for all but the last msg in an xfer. The NOSTART flag is for skipping the START frame (addr/rw) Signed-off-by: Erik Gilling Signed-off-by: Stephen Warren Signed-off-by: Ben Dooks --- drivers/i2c/busses/i2c-tegra.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index c0b9aa7df0e2..6b69b7007f00 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -451,12 +451,12 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, packet_header = msg->addr << I2C_HEADER_SLAVE_ADDR_SHIFT; packet_header |= I2C_HEADER_IE_ENABLE; + if (!stop) + packet_header |= I2C_HEADER_REPEAT_START; if (msg->flags & I2C_M_TEN) packet_header |= I2C_HEADER_10BIT_ADDR; if (msg->flags & I2C_M_IGNORE_NAK) packet_header |= I2C_HEADER_CONT_ON_NAK; - if (msg->flags & I2C_M_NOSTART) - packet_header |= I2C_HEADER_REPEAT_START; if (msg->flags & I2C_M_RD) packet_header |= I2C_HEADER_READ; i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); -- cgit v1.2.3 From 40abcf772357355a04f966416eebc943fd0281d4 Mon Sep 17 00:00:00 2001 From: Jay Cheng Date: Mon, 25 Apr 2011 15:32:27 -0600 Subject: i2c: tegra: Add de-bounce cycles. This enables debouncing of the I2C lines. The debounce period is 2 * the debounce register field value, in terms of the I2C block's main clock. The Tegra TRM indicates that a setting yielding >50nS is desirable. Hence, a setting of 2 => 4 clocks @ 72MHz => ~55nS. Signed-off-by: Ken Radtke [swarren: Added commit description body, Fixed 80-column limit, Reverted file permission change] Signed-off-by: Stephen Warren Signed-off-by: Ben Dooks --- drivers/i2c/busses/i2c-tegra.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 6b69b7007f00..4d9319665e32 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -35,6 +35,7 @@ #define BYTES_PER_FIFO_WORD 4 #define I2C_CNFG 0x000 +#define I2C_CNFG_DEBOUNCE_CNT_SHIFT 12 #define I2C_CNFG_PACKET_MODE_EN (1<<10) #define I2C_CNFG_NEW_MASTER_FSM (1<<11) #define I2C_STATUS 0x01C @@ -328,7 +329,8 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) if (i2c_dev->is_dvc) tegra_dvc_init(i2c_dev); - val = I2C_CNFG_NEW_MASTER_FSM | I2C_CNFG_PACKET_MODE_EN; + val = I2C_CNFG_NEW_MASTER_FSM | I2C_CNFG_PACKET_MODE_EN | + (0x2 << I2C_CNFG_DEBOUNCE_CNT_SHIFT); i2c_writel(i2c_dev, val, I2C_CNFG); i2c_writel(i2c_dev, 0, I2C_INT_MASK); clk_set_rate(i2c_dev->clk, i2c_dev->bus_clk_rate * 8); -- cgit v1.2.3 From efbe0f27ccfa7237c5524b10e8ccc91f97002f16 Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Mon, 9 May 2011 16:32:31 +0900 Subject: i2c-eg20t: Support new device ML7223 IOH Support new device OKI SEMICONDUCTOR ML7223 IOH(Input/Output Hub). The ML7223 IOH is for MP(Media Phone) use. The ML7223 is companion chip for Intel Atom E6xx series. The ML7223 is completely compatible for Intel EG20T PCH. Signed-off-by: Tomoya MORINAGA Signed-off-by: Ben Dooks --- drivers/i2c/busses/Kconfig | 10 +++++++--- drivers/i2c/busses/i2c-eg20t.c | 2 ++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 326652f673f7..697ffa86f2cc 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -671,15 +671,19 @@ config I2C_XILINX will be called xilinx_i2c. config I2C_EG20T - tristate "Intel EG20T PCH/OKI SEMICONDUCTOR ML7213 IOH" + tristate "Intel EG20T PCH / OKI SEMICONDUCTOR IOH(ML7213/ML7223)" depends on PCI help This driver is for PCH(Platform controller Hub) I2C of EG20T which is an IOH(Input/Output Hub) for x86 embedded processor. This driver can access PCH I2C bus device. - This driver also supports the ML7213, a companion chip for the - Atom E6xx series and compatible with the Intel EG20T PCH. + This driver also can be used for OKI SEMICONDUCTOR IOH(Input/ + Output Hub), ML7213 and ML7223. + ML7213 IOH is for IVI(In-Vehicle Infotainment) use and ML7223 IOH is + for MP(Media Phone) use. + ML7213/ML7223 is companion chip for Intel Atom E6xx series. + ML7213/ML7223 is completely compatible for Intel EG20T PCH. comment "External I2C/SMBus adapter drivers" diff --git a/drivers/i2c/busses/i2c-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c index 878a12026af2..8abfa4a03ce1 100644 --- a/drivers/i2c/busses/i2c-eg20t.c +++ b/drivers/i2c/busses/i2c-eg20t.c @@ -182,10 +182,12 @@ static DEFINE_MUTEX(pch_mutex); /* Definition for ML7213 by OKI SEMICONDUCTOR */ #define PCI_VENDOR_ID_ROHM 0x10DB #define PCI_DEVICE_ID_ML7213_I2C 0x802D +#define PCI_DEVICE_ID_ML7223_I2C 0x8010 static struct pci_device_id __devinitdata pch_pcidev_id[] = { { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_PCH_I2C), 1, }, { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_I2C), 2, }, + { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_I2C), 1, }, {0,} }; -- cgit v1.2.3 From 1082d5d29d9a71f43b969b89ce613374602a6946 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Thu, 21 Apr 2011 22:20:39 +0900 Subject: i2c: i2c-sh_mobile clock string removal Remove the clock string magic from the i2c-sh_mobile driver now when all platforms support clkdev properly. Signed-off-by: Magnus Damm Signed-off-by: Ben Dooks --- drivers/i2c/busses/i2c-sh_mobile.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 2707f5e17158..5a1d37d0b62f 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -577,7 +577,6 @@ static int sh_mobile_i2c_probe(struct platform_device *dev) struct sh_mobile_i2c_data *pd; struct i2c_adapter *adap; struct resource *res; - char clk_name[8]; int size; int ret; @@ -587,10 +586,9 @@ static int sh_mobile_i2c_probe(struct platform_device *dev) return -ENOMEM; } - snprintf(clk_name, sizeof(clk_name), "i2c%d", dev->id); - pd->clk = clk_get(&dev->dev, clk_name); + pd->clk = clk_get(&dev->dev, NULL); if (IS_ERR(pd->clk)) { - dev_err(&dev->dev, "cannot get clock \"%s\"\n", clk_name); + dev_err(&dev->dev, "cannot get clock\n"); ret = PTR_ERR(pd->clk); goto err; } -- cgit v1.2.3 From 81f8115305f821335cf9e16110bf0806f7b93283 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Thu, 28 Apr 2011 13:25:36 +0900 Subject: i2c: i2c-sh_mobile bus speed platform data V2 Add support to the i2c-sh_mobile driver for setting the I2C bus speed using platform data. Signed-off-by: Magnus Damm Signed-off-by: Ben Dooks --- drivers/i2c/busses/i2c-sh_mobile.c | 13 +++++++++++-- include/linux/i2c/i2c-sh_mobile.h | 10 ++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 include/linux/i2c/i2c-sh_mobile.h diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 5a1d37d0b62f..d917dd1cc091 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -32,6 +32,7 @@ #include #include #include +#include /* Transmit operation: */ /* */ @@ -117,7 +118,7 @@ struct sh_mobile_i2c_data { struct device *dev; void __iomem *reg; struct i2c_adapter adap; - + unsigned long bus_speed; struct clk *clk; u_int8_t icic; u_int8_t iccl; @@ -205,7 +206,7 @@ static void activate_ch(struct sh_mobile_i2c_data *pd) * We also round off the result. */ num = i2c_clk * 5; - denom = NORMAL_SPEED * 9; + denom = pd->bus_speed * 9; tmp = num * 10 / denom; if (tmp % 10 >= 5) pd->iccl = (u_int8_t)((num/denom) + 1); @@ -574,6 +575,7 @@ static int sh_mobile_i2c_hook_irqs(struct platform_device *dev, int hook) static int sh_mobile_i2c_probe(struct platform_device *dev) { + struct i2c_sh_mobile_platform_data *pdata = dev->dev.platform_data; struct sh_mobile_i2c_data *pd; struct i2c_adapter *adap; struct resource *res; @@ -618,6 +620,11 @@ static int sh_mobile_i2c_probe(struct platform_device *dev) goto err_irq; } + /* Use platformd data bus speed or NORMAL_SPEED */ + pd->bus_speed = NORMAL_SPEED; + if (pdata && pdata->bus_speed) + pd->bus_speed = pdata->bus_speed; + /* The IIC blocks on SH-Mobile ARM processors * come with two new bits in ICIC. */ @@ -658,6 +665,8 @@ static int sh_mobile_i2c_probe(struct platform_device *dev) goto err_all; } + dev_info(&dev->dev, "I2C adapter %d with bus speed %lu Hz\n", + adap->nr, pd->bus_speed); return 0; err_all: diff --git a/include/linux/i2c/i2c-sh_mobile.h b/include/linux/i2c/i2c-sh_mobile.h new file mode 100644 index 000000000000..beda7081aead --- /dev/null +++ b/include/linux/i2c/i2c-sh_mobile.h @@ -0,0 +1,10 @@ +#ifndef __I2C_SH_MOBILE_H__ +#define __I2C_SH_MOBILE_H__ + +#include + +struct i2c_sh_mobile_platform_data { + unsigned long bus_speed; +}; + +#endif /* __I2C_SH_MOBILE_H__ */ -- cgit v1.2.3 From a20d23945f30ec701a544fdd90d6537f4041af6f Mon Sep 17 00:00:00 2001 From: Jonas Aberg Date: Fri, 13 May 2011 12:29:02 +0200 Subject: i2c-nomadik: add regulator support This on-chip I2C controller needs to fetch the regulator representing its voltage domain so that it won't be switched off. Signed-off-by: Jonas Aberg Signed-off-by: Linus Walleij Signed-off-by: Ben Dooks --- drivers/i2c/busses/i2c-nomadik.c | 73 +++++++++++++++++++++++++++++++--------- 1 file changed, 58 insertions(+), 15 deletions(-) diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index e10e5cf3751a..182761eace80 100644 --- a/drivers/i2c/busses/i2c-nomadik.c +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -22,6 +22,7 @@ #include #include #include +#include #include @@ -151,6 +152,7 @@ struct i2c_nmk_client { * @stop: stop condition * @xfer_complete: acknowledge completion for a I2C message * @result: controller propogated result + * @busy: Busy doing transfer */ struct nmk_i2c_dev { struct platform_device *pdev; @@ -163,6 +165,8 @@ struct nmk_i2c_dev { int stop; struct completion xfer_complete; int result; + struct regulator *regulator; + bool busy; }; /* controller's abort causes */ @@ -257,7 +261,7 @@ static int init_hw(struct nmk_i2c_dev *dev) stat = flush_i2c_fifo(dev); if (stat) - return stat; + goto exit; /* disable the controller */ i2c_clr_bit(dev->virtbase + I2C_CR , I2C_CR_PE); @@ -268,10 +272,16 @@ static int init_hw(struct nmk_i2c_dev *dev) dev->cli.operation = I2C_NO_OPERATION; +exit: + /* TODO: Why disable clocks after init hw? */ clk_disable(dev->clk); - + /* + * TODO: What is this delay for? + * Must be pretty pointless since the hw block + * is frozen. Or? + */ udelay(I2C_DELAY); - return 0; + return stat; } /* enable peripheral, master mode operation */ @@ -562,9 +572,14 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap, u32 cause; struct nmk_i2c_dev *dev = i2c_get_adapdata(i2c_adap); + dev->busy = true; + + if (dev->regulator) + regulator_enable(dev->regulator); + status = init_hw(dev); if (status) - return status; + goto out2; clk_enable(dev->clk); @@ -575,7 +590,9 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap, if (unlikely(msgs[i].flags & I2C_M_TEN)) { dev_err(&dev->pdev->dev, "10 bit addressing" "not supported\n"); - return -EINVAL; + + status = -EINVAL; + goto out; } dev->cli.slave_adr = msgs[i].addr; dev->cli.buffer = msgs[i].buf; @@ -600,12 +617,19 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap, dev_err(&dev->pdev->dev, "%s\n", cause >= ARRAY_SIZE(abort_causes) ? "unknown reason" : abort_causes[cause]); - clk_disable(dev->clk); - return status; + + goto out; } udelay(I2C_DELAY); } + +out: clk_disable(dev->clk); +out2: + if (dev->regulator) + regulator_disable(dev->regulator); + + dev->busy = false; /* return the no. messages processed */ if (status) @@ -805,6 +829,21 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg) return IRQ_HANDLED; } + +#ifdef CONFIG_PM +static int nmk_i2c_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + struct nmk_i2c_dev *dev = platform_get_drvdata(pdev); + + if (dev->busy) + return -EBUSY; + else + return 0; +} +#else +#define nmk_i2c_suspend NULL +#endif + static unsigned int nmk_i2c_functionality(struct i2c_adapter *adap) { return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; @@ -830,7 +869,7 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev) ret = -ENOMEM; goto err_no_mem; } - + dev->busy = false; dev->pdev = pdev; platform_set_drvdata(pdev, dev); @@ -860,6 +899,12 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev) goto err_irq; } + dev->regulator = regulator_get(&pdev->dev, "v-i2c"); + if (IS_ERR(dev->regulator)) { + dev_warn(&pdev->dev, "could not get i2c regulator\n"); + dev->regulator = NULL; + } + dev->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(dev->clk)) { dev_err(&pdev->dev, "could not get i2c clock\n"); @@ -887,12 +932,6 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev) i2c_set_adapdata(adap, dev); - ret = init_hw(dev); - if (ret != 0) { - dev_err(&pdev->dev, "error in initializing i2c hardware\n"); - goto err_init_hw; - } - dev_info(&pdev->dev, "initialize %s on virtual " "base %p\n", adap->name, dev->virtbase); @@ -904,10 +943,11 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev) return 0; - err_init_hw: err_add_adap: clk_put(dev->clk); err_no_clk: + if (dev->regulator) + regulator_put(dev->regulator); free_irq(dev->irq, dev); err_irq: iounmap(dev->virtbase); @@ -938,6 +978,8 @@ static int __devexit nmk_i2c_remove(struct platform_device *pdev) if (res) release_mem_region(res->start, resource_size(res)); clk_put(dev->clk); + if (dev->regulator) + regulator_put(dev->regulator); platform_set_drvdata(pdev, NULL); kfree(dev); @@ -951,6 +993,7 @@ static struct platform_driver nmk_i2c_driver = { }, .probe = nmk_i2c_probe, .remove = __devexit_p(nmk_i2c_remove), + .suspend = nmk_i2c_suspend, }; static int __init nmk_i2c_init(void) -- cgit v1.2.3 From 9772760079253cd9a428434b43d415d18b152332 Mon Sep 17 00:00:00 2001 From: Virupax Sadashivpetimath Date: Fri, 13 May 2011 12:29:12 +0200 Subject: i2c-nomadik: make i2c timeout specific per i2c bus Add option to have different i2c timeout delay for different i2c buses specified in platform data. Default to the old value unless specified. Signed-off-by: Virupax Sadashivpetimath Reviewed-by: Srinidhi Kasagar Signed-off-by: Linus Walleij Signed-off-by: Ben Dooks --- arch/arm/plat-nomadik/include/plat/i2c.h | 6 ++++-- drivers/i2c/busses/i2c-nomadik.c | 10 ++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/arch/arm/plat-nomadik/include/plat/i2c.h b/arch/arm/plat-nomadik/include/plat/i2c.h index 1621db67a53d..4ed4e27d1368 100644 --- a/arch/arm/plat-nomadik/include/plat/i2c.h +++ b/arch/arm/plat-nomadik/include/plat/i2c.h @@ -24,13 +24,15 @@ enum i2c_freq_mode { * to the values of 14, 6, 2 for a 48 MHz i2c clk * @tft: Tx FIFO Threshold in bytes * @rft: Rx FIFO Threshold in bytes + * @timeout Slave response timeout(ms) * @sm: speed mode */ struct nmk_i2c_controller { unsigned long clk_freq; unsigned short slsu; - unsigned char tft; - unsigned char rft; + unsigned char tft; + unsigned char rft; + int timeout; enum i2c_freq_mode sm; }; diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index 182761eace80..3bf95b90abae 100644 --- a/drivers/i2c/busses/i2c-nomadik.c +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -121,9 +121,6 @@ enum i2c_operation { I2C_READ = 0x01 }; -/* controller response timeout in ms */ -#define I2C_TIMEOUT_MS 2000 - /** * struct i2c_nmk_client - client specific data * @slave_adr: 7-bit slave address @@ -213,7 +210,7 @@ static int flush_i2c_fifo(struct nmk_i2c_dev *dev) writel((I2C_CR_FTX | I2C_CR_FRX), dev->virtbase + I2C_CR); for (i = 0; i < LOOP_ATTEMPTS; i++) { - timeout = jiffies + msecs_to_jiffies(I2C_TIMEOUT_MS); + timeout = jiffies + msecs_to_jiffies(dev->adap.timeout); while (!time_after(jiffies, timeout)) { if ((readl(dev->virtbase + I2C_CR) & @@ -434,7 +431,7 @@ static int read_i2c(struct nmk_i2c_dev *dev) dev->virtbase + I2C_IMSCR); timeout = wait_for_completion_interruptible_timeout( - &dev->xfer_complete, msecs_to_jiffies(I2C_TIMEOUT_MS)); + &dev->xfer_complete, msecs_to_jiffies(dev->adap.timeout)); if (timeout < 0) { dev_err(&dev->pdev->dev, @@ -498,7 +495,7 @@ static int write_i2c(struct nmk_i2c_dev *dev) dev->virtbase + I2C_IMSCR); timeout = wait_for_completion_interruptible_timeout( - &dev->xfer_complete, msecs_to_jiffies(I2C_TIMEOUT_MS)); + &dev->xfer_complete, msecs_to_jiffies(dev->adap.timeout)); if (timeout < 0) { dev_err(&dev->pdev->dev, @@ -917,6 +914,7 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev) adap->owner = THIS_MODULE; adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; adap->algo = &nmk_i2c_algo; + adap->timeout = pdata->timeout ? pdata->timeout : 20000; snprintf(adap->name, sizeof(adap->name), "Nomadik I2C%d at %lx", pdev->id, (unsigned long)res->start); -- cgit v1.2.3 From 2320f504d3c87b814abf37f224ab68d52d2a512c Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 13 May 2011 12:29:20 +0200 Subject: i2c-nomadik: fix speed enumerator The I2C speed enumerators in the i2c-nomadik header file were in the wrong order. Signed-off-by: Linus Walleij Signed-off-by: Ben Dooks --- arch/arm/plat-nomadik/include/plat/i2c.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/plat-nomadik/include/plat/i2c.h b/arch/arm/plat-nomadik/include/plat/i2c.h index 4ed4e27d1368..8ba70ffc31ec 100644 --- a/arch/arm/plat-nomadik/include/plat/i2c.h +++ b/arch/arm/plat-nomadik/include/plat/i2c.h @@ -11,8 +11,8 @@ enum i2c_freq_mode { I2C_FREQ_MODE_STANDARD, /* up to 100 Kb/s */ I2C_FREQ_MODE_FAST, /* up to 400 Kb/s */ + I2C_FREQ_MODE_HIGH_SPEED, /* up to 3.4 Mb/s */ I2C_FREQ_MODE_FAST_PLUS, /* up to 1 Mb/s */ - I2C_FREQ_MODE_HIGH_SPEED /* up to 3.4 Mb/s */ }; /** -- cgit v1.2.3 From 99381bec0a569a69345148cb1ac74c2caa43a227 Mon Sep 17 00:00:00 2001 From: Virupax Sadashivpetimath Date: Fri, 13 May 2011 12:29:28 +0200 Subject: i2c-nomadik: corrrect returned error numbers The code was returning bad error numbers or just -1 in some cases. Signed-off-by: Virupax Sadashivpetimath Reviewed-by: Srinidhi Kasagar Signed-off-by: Linus Walleij Signed-off-by: Ben Dooks --- drivers/i2c/busses/i2c-nomadik.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index 3bf95b90abae..a02141a99713 100644 --- a/drivers/i2c/busses/i2c-nomadik.c +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -615,6 +615,8 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap, cause >= ARRAY_SIZE(abort_causes) ? "unknown reason" : abort_causes[cause]); + status = status ? status : dev->result; + goto out; } udelay(I2C_DELAY); @@ -759,7 +761,7 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg) | I2C_IT_RXFF | I2C_IT_RXFE)); if (dev->cli.count) { - dev->result = -1; + dev->result = -EIO; dev_err(&dev->pdev->dev, "%lu bytes still remain to be" "xfered\n", dev->cli.count); (void) init_hw(dev); @@ -770,7 +772,7 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg) /* Master Arbitration lost interrupt */ case I2C_IT_MAL: - dev->result = -1; + dev->result = -EIO; (void) init_hw(dev); i2c_set_bit(dev->virtbase + I2C_ICR, I2C_IT_MAL); @@ -784,7 +786,7 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg) * during the transaction. */ case I2C_IT_BERR: - dev->result = -1; + dev->result = -EIO; /* get the status */ if (((readl(dev->virtbase + I2C_SR) >> 2) & 0x3) == I2C_ABORT) (void) init_hw(dev); @@ -800,7 +802,7 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg) * the Tx FIFO is full. */ case I2C_IT_TXFOVR: - dev->result = -1; + dev->result = -EIO; (void) init_hw(dev); dev_err(&dev->pdev->dev, "Tx Fifo Over run\n"); -- cgit v1.2.3 From c436f92a4646e4635554bda39407dfb907352321 Mon Sep 17 00:00:00 2001 From: srinidhi kasagar Date: Fri, 13 May 2011 12:29:38 +0200 Subject: i2c-nomadik: remove the redundant error message The abort cause string itself is an error, so remove the redundant explicit error message. Signed-off-by: Srinidhi Kasagar Reviewed-by: Jonas Aberg Signed-off-by: Linus Walleij Signed-off-by: Ben Dooks --- drivers/i2c/busses/i2c-nomadik.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index a02141a99713..dbd93b25d2c5 100644 --- a/drivers/i2c/busses/i2c-nomadik.c +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -609,8 +609,6 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap, if (status || (dev->result)) { /* get the abort cause */ cause = (readl(dev->virtbase + I2C_SR) >> 4) & 0x7; - dev_err(&dev->pdev->dev, "error during I2C" - "message xfer: %d\n", cause); dev_err(&dev->pdev->dev, "%s\n", cause >= ARRAY_SIZE(abort_causes) ? "unknown reason" : abort_causes[cause]); -- cgit v1.2.3 From cd20e4fa910540c339b483d0b95ca237abf3354a Mon Sep 17 00:00:00 2001 From: Virupax Sadashivpetimath Date: Fri, 13 May 2011 12:29:46 +0200 Subject: i2c-nomadik: correct adapter timeout initialization Correct the incorrect initialization of adapter timeout not to be in milliseconds, as it needs to be done in jiffies. Signed-off-by: Virupax Sadashivpetimath Reviewed-by: Srinidhi Kasagar Signed-off-by: Linus Walleij Signed-off-by: Ben Dooks --- drivers/i2c/busses/i2c-nomadik.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index dbd93b25d2c5..28389c29af75 100644 --- a/drivers/i2c/busses/i2c-nomadik.c +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -210,7 +210,7 @@ static int flush_i2c_fifo(struct nmk_i2c_dev *dev) writel((I2C_CR_FTX | I2C_CR_FRX), dev->virtbase + I2C_CR); for (i = 0; i < LOOP_ATTEMPTS; i++) { - timeout = jiffies + msecs_to_jiffies(dev->adap.timeout); + timeout = jiffies + dev->adap.timeout; while (!time_after(jiffies, timeout)) { if ((readl(dev->virtbase + I2C_CR) & @@ -431,7 +431,7 @@ static int read_i2c(struct nmk_i2c_dev *dev) dev->virtbase + I2C_IMSCR); timeout = wait_for_completion_interruptible_timeout( - &dev->xfer_complete, msecs_to_jiffies(dev->adap.timeout)); + &dev->xfer_complete, dev->adap.timeout); if (timeout < 0) { dev_err(&dev->pdev->dev, @@ -495,7 +495,7 @@ static int write_i2c(struct nmk_i2c_dev *dev) dev->virtbase + I2C_IMSCR); timeout = wait_for_completion_interruptible_timeout( - &dev->xfer_complete, msecs_to_jiffies(dev->adap.timeout)); + &dev->xfer_complete, dev->adap.timeout); if (timeout < 0) { dev_err(&dev->pdev->dev, @@ -914,7 +914,8 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev) adap->owner = THIS_MODULE; adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; adap->algo = &nmk_i2c_algo; - adap->timeout = pdata->timeout ? pdata->timeout : 20000; + adap->timeout = pdata->timeout ? msecs_to_jiffies(pdata->timeout) : + msecs_to_jiffies(20000); snprintf(adap->name, sizeof(adap->name), "Nomadik I2C%d at %lx", pdev->id, (unsigned long)res->start); -- cgit v1.2.3 From 4cb3f538cd88fddaa659a924e7abdb685cd5e784 Mon Sep 17 00:00:00 2001 From: Virupax Sadashivpetimath Date: Fri, 13 May 2011 12:29:55 +0200 Subject: i2c-nomadik: print abort cause only on abort tag Modify the code to: 1)Print the cause of i2c failure only if the status is set to ABORT. 2)Print slave address on send/receive fail, will help in which slave failed. Signed-off-by: Virupax Sadashivpetimath Reviewed-by: Jonas Aberg Signed-off-by: Linus Walleij Signed-off-by: Ben Dooks --- drivers/i2c/busses/i2c-nomadik.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index 28389c29af75..c8bf81abcd3c 100644 --- a/drivers/i2c/busses/i2c-nomadik.c +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -442,7 +442,8 @@ static int read_i2c(struct nmk_i2c_dev *dev) if (timeout == 0) { /* controller has timedout, re-init the h/w */ - dev_err(&dev->pdev->dev, "controller timed out, re-init h/w\n"); + dev_err(&dev->pdev->dev, "read from slave 0x%x timed out\n", + dev->cli.slave_adr); (void) init_hw(dev); status = -ETIMEDOUT; } @@ -506,7 +507,8 @@ static int write_i2c(struct nmk_i2c_dev *dev) if (timeout == 0) { /* controller has timedout, re-init the h/w */ - dev_err(&dev->pdev->dev, "controller timed out, re-init h/w\n"); + dev_err(&dev->pdev->dev, "write to slave 0x%x timed out\n", + dev->cli.slave_adr); (void) init_hw(dev); status = -ETIMEDOUT; } @@ -568,6 +570,7 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap, int i; u32 cause; struct nmk_i2c_dev *dev = i2c_get_adapdata(i2c_adap); + u32 i2c_sr; dev->busy = true; @@ -607,14 +610,22 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap, status = write_i2c(dev); } if (status || (dev->result)) { - /* get the abort cause */ - cause = (readl(dev->virtbase + I2C_SR) >> 4) & 0x7; - dev_err(&dev->pdev->dev, "%s\n", - cause >= ARRAY_SIZE(abort_causes) - ? "unknown reason" : abort_causes[cause]); + i2c_sr = readl(dev->virtbase + I2C_SR); + /* + * Check if the controller I2C operation status is set + * to ABORT(11b). + */ + if (((i2c_sr >> 2) & 0x3) == 0x3) { + /* get the abort cause */ + cause = (i2c_sr >> 4) + & 0x7; + dev_err(&dev->pdev->dev, "%s\n", cause >= + ARRAY_SIZE(abort_causes) ? + "unknown reason" : + abort_causes[cause]); + } status = status ? status : dev->result; - goto out; } udelay(I2C_DELAY); -- cgit v1.2.3 From b0e751a925260e5998a76dad41d4565ef26870db Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Fri, 13 May 2011 12:30:07 +0200 Subject: i2c-nomadik: use pm_runtime API Use the pm_runtime API for pins control. Signed-off-by: Rabin Vincent Reviewed-by: Srinidhi Kasagar Reviewed-by: Jonas Aberg [deleted some surplus runtime PM code] Signed-off-by: Linus Walleij Signed-off-by: Ben Dooks --- drivers/i2c/busses/i2c-nomadik.c | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index c8bf81abcd3c..234e4a9070b8 100644 --- a/drivers/i2c/busses/i2c-nomadik.c +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -23,6 +23,7 @@ #include #include #include +#include #include @@ -576,6 +577,7 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap, if (dev->regulator) regulator_enable(dev->regulator); + pm_runtime_get_sync(&dev->pdev->dev); status = init_hw(dev); if (status) @@ -634,6 +636,7 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap, out: clk_disable(dev->clk); out2: + pm_runtime_put_sync(&dev->pdev->dev); if (dev->regulator) regulator_disable(dev->regulator); @@ -839,19 +842,36 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg) #ifdef CONFIG_PM -static int nmk_i2c_suspend(struct platform_device *pdev, pm_message_t mesg) +static int nmk_i2c_suspend(struct device *dev) { - struct nmk_i2c_dev *dev = platform_get_drvdata(pdev); + struct platform_device *pdev = to_platform_device(dev); + struct nmk_i2c_dev *nmk_i2c = platform_get_drvdata(pdev); - if (dev->busy) + if (nmk_i2c->busy) return -EBUSY; - else - return 0; + + return 0; +} + +static int nmk_i2c_resume(struct device *dev) +{ + return 0; } #else #define nmk_i2c_suspend NULL +#define nmk_i2c_resume NULL #endif +/* + * We use noirq so that we suspend late and resume before the wakeup interrupt + * to ensure that we do the !pm_runtime_suspended() check in resume before + * there has been a regular pm runtime resume (via pm_runtime_get_sync()). + */ +static const struct dev_pm_ops nmk_i2c_pm = { + .suspend_noirq = nmk_i2c_suspend, + .resume_noirq = nmk_i2c_resume, +}; + static unsigned int nmk_i2c_functionality(struct i2c_adapter *adap) { return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; @@ -913,6 +933,9 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev) dev->regulator = NULL; } + pm_suspend_ignore_children(&pdev->dev, true); + pm_runtime_enable(&pdev->dev); + dev->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(dev->clk)) { dev_err(&pdev->dev, "could not get i2c clock\n"); @@ -958,6 +981,7 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev) err_no_clk: if (dev->regulator) regulator_put(dev->regulator); + pm_runtime_disable(&pdev->dev); free_irq(dev->irq, dev); err_irq: iounmap(dev->virtbase); @@ -990,6 +1014,7 @@ static int __devexit nmk_i2c_remove(struct platform_device *pdev) clk_put(dev->clk); if (dev->regulator) regulator_put(dev->regulator); + pm_runtime_disable(&pdev->dev); platform_set_drvdata(pdev, NULL); kfree(dev); @@ -1000,10 +1025,10 @@ static struct platform_driver nmk_i2c_driver = { .driver = { .owner = THIS_MODULE, .name = DRIVER_NAME, + .pm = &nmk_i2c_pm, }, .probe = nmk_i2c_probe, .remove = __devexit_p(nmk_i2c_remove), - .suspend = nmk_i2c_suspend, }; static int __init nmk_i2c_init(void) -- cgit v1.2.3 From ebd10e0783d9fb92a147e60902e22c2d3f3ad69d Mon Sep 17 00:00:00 2001 From: Virupax Sadashivpetimath Date: Fri, 13 May 2011 12:30:23 +0200 Subject: i2c-nomadik: add code to retry on timeout failure It is seen that i2c-nomadik controller randomly stops generating the interrupts leading to a i2c timeout. As a workaround to this problem, add retries to the on going transfer on failure. Signed-off-by: Virupax Sadashivpetimath Reviewed-by: Jonas ABERG Signed-off-by: Linus Walleij Signed-off-by: Ben Dooks --- drivers/i2c/busses/i2c-nomadik.c | 97 ++++++++++++++++++++-------------------- 1 file changed, 49 insertions(+), 48 deletions(-) diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index 234e4a9070b8..b49ff25a2688 100644 --- a/drivers/i2c/busses/i2c-nomadik.c +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -255,8 +255,6 @@ static int init_hw(struct nmk_i2c_dev *dev) { int stat; - clk_enable(dev->clk); - stat = flush_i2c_fifo(dev); if (stat) goto exit; @@ -271,8 +269,6 @@ static int init_hw(struct nmk_i2c_dev *dev) dev->cli.operation = I2C_NO_OPERATION; exit: - /* TODO: Why disable clocks after init hw? */ - clk_disable(dev->clk); /* * TODO: What is this delay for? * Must be pretty pointless since the hw block @@ -572,6 +568,7 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap, u32 cause; struct nmk_i2c_dev *dev = i2c_get_adapdata(i2c_adap); u32 i2c_sr; + int j; dev->busy = true; @@ -579,63 +576,67 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap, regulator_enable(dev->regulator); pm_runtime_get_sync(&dev->pdev->dev); + clk_enable(dev->clk); + status = init_hw(dev); if (status) - goto out2; + goto out; - clk_enable(dev->clk); - - /* setup the i2c controller */ - setup_i2c_controller(dev); + for (j = 0; j < 3; j++) { + /* setup the i2c controller */ + setup_i2c_controller(dev); - for (i = 0; i < num_msgs; i++) { - if (unlikely(msgs[i].flags & I2C_M_TEN)) { - dev_err(&dev->pdev->dev, "10 bit addressing" - "not supported\n"); + for (i = 0; i < num_msgs; i++) { + if (unlikely(msgs[i].flags & I2C_M_TEN)) { + dev_err(&dev->pdev->dev, "10 bit addressing" + "not supported\n"); - status = -EINVAL; - goto out; - } - dev->cli.slave_adr = msgs[i].addr; - dev->cli.buffer = msgs[i].buf; - dev->cli.count = msgs[i].len; - dev->stop = (i < (num_msgs - 1)) ? 0 : 1; - dev->result = 0; - - if (msgs[i].flags & I2C_M_RD) { - /* it is a read operation */ - dev->cli.operation = I2C_READ; - status = read_i2c(dev); - } else { - /* write operation */ - dev->cli.operation = I2C_WRITE; - status = write_i2c(dev); - } - if (status || (dev->result)) { - i2c_sr = readl(dev->virtbase + I2C_SR); - /* - * Check if the controller I2C operation status is set - * to ABORT(11b). - */ - if (((i2c_sr >> 2) & 0x3) == 0x3) { - /* get the abort cause */ - cause = (i2c_sr >> 4) - & 0x7; - dev_err(&dev->pdev->dev, "%s\n", cause >= - ARRAY_SIZE(abort_causes) ? + status = -EINVAL; + goto out; + } + dev->cli.slave_adr = msgs[i].addr; + dev->cli.buffer = msgs[i].buf; + dev->cli.count = msgs[i].len; + dev->stop = (i < (num_msgs - 1)) ? 0 : 1; + dev->result = 0; + + if (msgs[i].flags & I2C_M_RD) { + /* it is a read operation */ + dev->cli.operation = I2C_READ; + status = read_i2c(dev); + } else { + /* write operation */ + dev->cli.operation = I2C_WRITE; + status = write_i2c(dev); + } + if (status || (dev->result)) { + i2c_sr = readl(dev->virtbase + I2C_SR); + /* + * Check if the controller I2C operation status + * is set to ABORT(11b). + */ + if (((i2c_sr >> 2) & 0x3) == 0x3) { + /* get the abort cause */ + cause = (i2c_sr >> 4) + & 0x7; + dev_err(&dev->pdev->dev, "%s\n", cause + >= ARRAY_SIZE(abort_causes) ? "unknown reason" : abort_causes[cause]); - } + } - status = status ? status : dev->result; - goto out; + status = status ? status : dev->result; + + break; + } + udelay(I2C_DELAY); } - udelay(I2C_DELAY); + if (status == 0) + break; } out: clk_disable(dev->clk); -out2: pm_runtime_put_sync(&dev->pdev->dev); if (dev->regulator) regulator_disable(dev->regulator); -- cgit v1.2.3 From 553553413a6abdda220a697ef439ba2f4b1d4a7c Mon Sep 17 00:00:00 2001 From: Virupax Sadashivpetimath Date: Fri, 13 May 2011 12:30:34 +0200 Subject: i2c-nomadik: change the TX and RX threshold 1) Increase RX FIFO threshold so that there is a reduction in the number of interrupts handled to complete a transaction. 2) Fill TX FIFO in the write function. Signed-off-by: Virupax Sadashivpetimath Reviewed-by: Jonas Aberg Signed-off-by: Linus Walleij Signed-off-by: Ben Dooks --- drivers/i2c/busses/i2c-nomadik.c | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index b49ff25a2688..e3cd62e40df5 100644 --- a/drivers/i2c/busses/i2c-nomadik.c +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -447,6 +447,24 @@ static int read_i2c(struct nmk_i2c_dev *dev) return status; } +static void fill_tx_fifo(struct nmk_i2c_dev *dev, int no_bytes) +{ + int count; + + for (count = (no_bytes - 2); + (count > 0) && + (dev->cli.count != 0); + count--) { + /* write to the Tx FIFO */ + writeb(*dev->cli.buffer, + dev->virtbase + I2C_TFR); + dev->cli.buffer++; + dev->cli.count--; + dev->cli.xfer_bytes++; + } + +} + /** * write_i2c() - Write data to I2C client. * @dev: private data of I2C Driver @@ -474,8 +492,13 @@ static int write_i2c(struct nmk_i2c_dev *dev) init_completion(&dev->xfer_complete); /* enable interrupts by settings the masks */ - irq_mask = (I2C_IT_TXFNE | I2C_IT_TXFOVR | - I2C_IT_MAL | I2C_IT_BERR); + irq_mask = (I2C_IT_TXFOVR | I2C_IT_MAL | I2C_IT_BERR); + + /* Fill the TX FIFO with transmit data */ + fill_tx_fifo(dev, MAX_I2C_FIFO_THRESHOLD); + + if (dev->cli.count != 0) + irq_mask |= I2C_IT_TXFNE; /* * check if we want to transfer a single or multiple bytes, if so @@ -702,17 +725,7 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg) */ disable_interrupts(dev, I2C_IT_TXFNE); } else { - for (count = (MAX_I2C_FIFO_THRESHOLD - tft - 2); - (count > 0) && - (dev->cli.count != 0); - count--) { - /* write to the Tx FIFO */ - writeb(*dev->cli.buffer, - dev->virtbase + I2C_TFR); - dev->cli.buffer++; - dev->cli.count--; - dev->cli.xfer_bytes++; - } + fill_tx_fifo(dev, (MAX_I2C_FIFO_THRESHOLD - tft)); /* * if done, close the transfer by disabling the * corresponding TXFNE interrupt -- cgit v1.2.3 From b5e890f7e70707d1e10e8d4844806d2223e8b36d Mon Sep 17 00:00:00 2001 From: Virupax Sadashivpetimath Date: Fri, 13 May 2011 12:30:42 +0200 Subject: i2c-nomadik: remove the unnecessary delay The delay in the driver seems to be not needed, so remove it. Signed-off-by: Virupax Sadashivpetimath Reviewed-by: Markus Grape Tested-by: Per Persson Tested-by: Chethan Krishna N Reviewed-by: Srinidhi Kasagar Signed-off-by: Linus Walleij Signed-off-by: Ben Dooks --- drivers/i2c/busses/i2c-nomadik.c | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index e3cd62e40df5..b2de1a56dc8d 100644 --- a/drivers/i2c/busses/i2c-nomadik.c +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -105,9 +104,6 @@ /* maximum threshold value */ #define MAX_I2C_FIFO_THRESHOLD 15 -/* per-transfer delay, required for the hardware to stabilize */ -#define I2C_DELAY 150 - enum i2c_status { I2C_NOP, I2C_ON_GOING, @@ -269,12 +265,6 @@ static int init_hw(struct nmk_i2c_dev *dev) dev->cli.operation = I2C_NO_OPERATION; exit: - /* - * TODO: What is this delay for? - * Must be pretty pointless since the hw block - * is frozen. Or? - */ - udelay(I2C_DELAY); return stat; } @@ -652,7 +642,6 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap, break; } - udelay(I2C_DELAY); } if (status == 0) break; @@ -778,13 +767,8 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg) } } - i2c_set_bit(dev->virtbase + I2C_ICR, I2C_IT_MTD); - i2c_set_bit(dev->virtbase + I2C_ICR, I2C_IT_MTDWS); - - disable_interrupts(dev, - (I2C_IT_TXFNE | I2C_IT_TXFE | I2C_IT_TXFF - | I2C_IT_TXFOVR | I2C_IT_RXFNF - | I2C_IT_RXFF | I2C_IT_RXFE)); + disable_all_interrupts(dev); + clear_all_interrupts(dev); if (dev->cli.count) { dev->result = -EIO; -- cgit v1.2.3 From 0511f643cbe6990daf4b53b1268b5c2ea28d1733 Mon Sep 17 00:00:00 2001 From: Virupax Sadashivpetimath Date: Fri, 13 May 2011 12:30:53 +0200 Subject: i2c-nomadik: reset the hw after status check In case of I2C timeout, reset the HW only after the HW status is read, otherwise the staus will be lost. Signed-off-by: Virupax Sadashivpetimath Reviewed-by: Jonas Aberg Reviewed-by: Srinidhi Kasagar Signed-off-by: Linus Walleij Signed-off-by: Ben Dooks --- drivers/i2c/busses/i2c-nomadik.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index b2de1a56dc8d..fa7b10639ce2 100644 --- a/drivers/i2c/busses/i2c-nomadik.c +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -428,10 +428,9 @@ static int read_i2c(struct nmk_i2c_dev *dev) } if (timeout == 0) { - /* controller has timedout, re-init the h/w */ + /* Controller timed out */ dev_err(&dev->pdev->dev, "read from slave 0x%x timed out\n", dev->cli.slave_adr); - (void) init_hw(dev); status = -ETIMEDOUT; } return status; @@ -516,10 +515,9 @@ static int write_i2c(struct nmk_i2c_dev *dev) } if (timeout == 0) { - /* controller has timedout, re-init the h/w */ + /* Controller timed out */ dev_err(&dev->pdev->dev, "write to slave 0x%x timed out\n", dev->cli.slave_adr); - (void) init_hw(dev); status = -ETIMEDOUT; } @@ -638,6 +636,8 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap, abort_causes[cause]); } + (void) init_hw(dev); + status = status ? status : dev->result; break; -- cgit v1.2.3 From 82a4413450376cbce0bb2b794fb880dbfda89299 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 13 May 2011 12:31:01 +0200 Subject: i2c-nomadik: break out single messsage transmission Reduce code size in the message transfer function by factoring out a single-message transfer function. Signed-off-by: Linus Walleij Signed-off-by: Ben Dooks --- drivers/i2c/busses/i2c-nomadik.c | 80 ++++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 32 deletions(-) diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index fa7b10639ce2..0c731ca69f15 100644 --- a/drivers/i2c/busses/i2c-nomadik.c +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -524,6 +524,51 @@ static int write_i2c(struct nmk_i2c_dev *dev) return status; } +/** + * nmk_i2c_xfer_one() - transmit a single I2C message + * @dev: device with a message encoded into it + * @flags: message flags + */ +static int nmk_i2c_xfer_one(struct nmk_i2c_dev *dev, u16 flags) +{ + int status; + + if (flags & I2C_M_RD) { + /* read operation */ + dev->cli.operation = I2C_READ; + status = read_i2c(dev); + } else { + /* write operation */ + dev->cli.operation = I2C_WRITE; + status = write_i2c(dev); + } + + if (status || (dev->result)) { + u32 i2c_sr; + u32 cause; + + i2c_sr = readl(dev->virtbase + I2C_SR); + /* + * Check if the controller I2C operation status + * is set to ABORT(11b). + */ + if (((i2c_sr >> 2) & 0x3) == 0x3) { + /* get the abort cause */ + cause = (i2c_sr >> 4) & 0x7; + dev_err(&dev->pdev->dev, "%s\n", cause + >= ARRAY_SIZE(abort_causes) ? + "unknown reason" : + abort_causes[cause]); + } + + (void) init_hw(dev); + + status = status ? status : dev->result; + } + + return status; +} + /** * nmk_i2c_xfer() - I2C transfer function used by kernel framework * @i2c_adap: Adapter pointer to the controller @@ -576,9 +621,7 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap, { int status; int i; - u32 cause; struct nmk_i2c_dev *dev = i2c_get_adapdata(i2c_adap); - u32 i2c_sr; int j; dev->busy = true; @@ -593,6 +636,7 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap, if (status) goto out; + /* Attempt three times to send the message queue */ for (j = 0; j < 3; j++) { /* setup the i2c controller */ setup_i2c_controller(dev); @@ -611,37 +655,9 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap, dev->stop = (i < (num_msgs - 1)) ? 0 : 1; dev->result = 0; - if (msgs[i].flags & I2C_M_RD) { - /* it is a read operation */ - dev->cli.operation = I2C_READ; - status = read_i2c(dev); - } else { - /* write operation */ - dev->cli.operation = I2C_WRITE; - status = write_i2c(dev); - } - if (status || (dev->result)) { - i2c_sr = readl(dev->virtbase + I2C_SR); - /* - * Check if the controller I2C operation status - * is set to ABORT(11b). - */ - if (((i2c_sr >> 2) & 0x3) == 0x3) { - /* get the abort cause */ - cause = (i2c_sr >> 4) - & 0x7; - dev_err(&dev->pdev->dev, "%s\n", cause - >= ARRAY_SIZE(abort_causes) ? - "unknown reason" : - abort_causes[cause]); - } - - (void) init_hw(dev); - - status = status ? status : dev->result; - + status = nmk_i2c_xfer_one(dev, msgs[i].flags); + if (status != 0) break; - } } if (status == 0) break; -- cgit v1.2.3 From 2b030bda66b0a59f8ebf0ce2117088256a5f9f97 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 13 May 2011 12:31:13 +0200 Subject: mach-ux500: set proper I2C platform data from MOP500s This specifies the new per-platform timeout per I2C bus and switches the I2C buses to fast mode, and increase the FIFO depth to 8 for reads and writes. Signed-off-by: Linus Walleij Signed-off-by: Ben Dooks --- arch/arm/mach-ux500/board-mop500.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c index 6e1907fa94f0..bb26f40493e6 100644 --- a/arch/arm/mach-ux500/board-mop500.c +++ b/arch/arm/mach-ux500/board-mop500.c @@ -204,7 +204,7 @@ static struct i2c_board_info __initdata mop500_i2c2_devices[] = { }, }; -#define U8500_I2C_CONTROLLER(id, _slsu, _tft, _rft, clk, _sm) \ +#define U8500_I2C_CONTROLLER(id, _slsu, _tft, _rft, clk, t_out, _sm) \ static struct nmk_i2c_controller u8500_i2c##id##_data = { \ /* \ * slave data setup time, which is \ @@ -219,19 +219,21 @@ static struct nmk_i2c_controller u8500_i2c##id##_data = { \ .rft = _rft, \ /* std. mode operation */ \ .clk_freq = clk, \ + /* Slave response timeout(ms) */\ + .timeout = t_out, \ .sm = _sm, \ } /* * The board uses 4 i2c controllers, initialize all of * them with slave data setup time of 250 ns, - * Tx & Rx FIFO threshold values as 1 and standard + * Tx & Rx FIFO threshold values as 8 and standard * mode of operation */ -U8500_I2C_CONTROLLER(0, 0xe, 1, 1, 100000, I2C_FREQ_MODE_STANDARD); -U8500_I2C_CONTROLLER(1, 0xe, 1, 1, 100000, I2C_FREQ_MODE_STANDARD); -U8500_I2C_CONTROLLER(2, 0xe, 1, 1, 100000, I2C_FREQ_MODE_STANDARD); -U8500_I2C_CONTROLLER(3, 0xe, 1, 1, 100000, I2C_FREQ_MODE_STANDARD); +U8500_I2C_CONTROLLER(0, 0xe, 1, 8, 100000, 200, I2C_FREQ_MODE_FAST); +U8500_I2C_CONTROLLER(1, 0xe, 1, 8, 100000, 200, I2C_FREQ_MODE_FAST); +U8500_I2C_CONTROLLER(2, 0xe, 1, 8, 100000, 200, I2C_FREQ_MODE_FAST); +U8500_I2C_CONTROLLER(3, 0xe, 1, 8, 100000, 200, I2C_FREQ_MODE_FAST); static void __init mop500_i2c_init(void) { -- cgit v1.2.3