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(-) (limited to 'drivers/i2c/busses/i2c-nomadik.c') diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index e10e5cf3751..182761eace8 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(-) (limited to 'drivers/i2c/busses/i2c-nomadik.c') diff --git a/arch/arm/plat-nomadik/include/plat/i2c.h b/arch/arm/plat-nomadik/include/plat/i2c.h index 1621db67a53..4ed4e27d136 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 182761eace8..3bf95b90aba 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 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(-) (limited to 'drivers/i2c/busses/i2c-nomadik.c') diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index 3bf95b90aba..a02141a9971 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(-) (limited to 'drivers/i2c/busses/i2c-nomadik.c') diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index a02141a9971..dbd93b25d2c 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(-) (limited to 'drivers/i2c/busses/i2c-nomadik.c') diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index dbd93b25d2c..28389c29af7 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(-) (limited to 'drivers/i2c/busses/i2c-nomadik.c') diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index 28389c29af7..c8bf81abcd3 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(-) (limited to 'drivers/i2c/busses/i2c-nomadik.c') diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index c8bf81abcd3..234e4a9070b 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(-) (limited to 'drivers/i2c/busses/i2c-nomadik.c') diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index 234e4a9070b..b49ff25a268 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(-) (limited to 'drivers/i2c/busses/i2c-nomadik.c') diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index b49ff25a268..e3cd62e40df 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(-) (limited to 'drivers/i2c/busses/i2c-nomadik.c') diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index e3cd62e40df..b2de1a56dc8 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(-) (limited to 'drivers/i2c/busses/i2c-nomadik.c') diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index b2de1a56dc8..fa7b10639ce 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(-) (limited to 'drivers/i2c/busses/i2c-nomadik.c') diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index fa7b10639ce..0c731ca69f1 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