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 <jonas.aberg@stericsson.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
This commit is contained in:
Jonas Aberg 2011-05-13 12:29:02 +02:00 committed by Ben Dooks
parent d762f43831
commit a20d23945f

View File

@ -22,6 +22,7 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/regulator/consumer.h>
#include <plat/i2c.h> #include <plat/i2c.h>
@ -151,6 +152,7 @@ struct i2c_nmk_client {
* @stop: stop condition * @stop: stop condition
* @xfer_complete: acknowledge completion for a I2C message * @xfer_complete: acknowledge completion for a I2C message
* @result: controller propogated result * @result: controller propogated result
* @busy: Busy doing transfer
*/ */
struct nmk_i2c_dev { struct nmk_i2c_dev {
struct platform_device *pdev; struct platform_device *pdev;
@ -163,6 +165,8 @@ struct nmk_i2c_dev {
int stop; int stop;
struct completion xfer_complete; struct completion xfer_complete;
int result; int result;
struct regulator *regulator;
bool busy;
}; };
/* controller's abort causes */ /* controller's abort causes */
@ -257,7 +261,7 @@ static int init_hw(struct nmk_i2c_dev *dev)
stat = flush_i2c_fifo(dev); stat = flush_i2c_fifo(dev);
if (stat) if (stat)
return stat; goto exit;
/* disable the controller */ /* disable the controller */
i2c_clr_bit(dev->virtbase + I2C_CR , I2C_CR_PE); 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; dev->cli.operation = I2C_NO_OPERATION;
exit:
/* TODO: Why disable clocks after init hw? */
clk_disable(dev->clk); clk_disable(dev->clk);
/*
* TODO: What is this delay for?
* Must be pretty pointless since the hw block
* is frozen. Or?
*/
udelay(I2C_DELAY); udelay(I2C_DELAY);
return 0; return stat;
} }
/* enable peripheral, master mode operation */ /* enable peripheral, master mode operation */
@ -562,9 +572,14 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap,
u32 cause; u32 cause;
struct nmk_i2c_dev *dev = i2c_get_adapdata(i2c_adap); struct nmk_i2c_dev *dev = i2c_get_adapdata(i2c_adap);
dev->busy = true;
if (dev->regulator)
regulator_enable(dev->regulator);
status = init_hw(dev); status = init_hw(dev);
if (status) if (status)
return status; goto out2;
clk_enable(dev->clk); 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)) { if (unlikely(msgs[i].flags & I2C_M_TEN)) {
dev_err(&dev->pdev->dev, "10 bit addressing" dev_err(&dev->pdev->dev, "10 bit addressing"
"not supported\n"); "not supported\n");
return -EINVAL;
status = -EINVAL;
goto out;
} }
dev->cli.slave_adr = msgs[i].addr; dev->cli.slave_adr = msgs[i].addr;
dev->cli.buffer = msgs[i].buf; 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", dev_err(&dev->pdev->dev, "%s\n",
cause >= ARRAY_SIZE(abort_causes) cause >= ARRAY_SIZE(abort_causes)
? "unknown reason" : abort_causes[cause]); ? "unknown reason" : abort_causes[cause]);
clk_disable(dev->clk);
return status; goto out;
} }
udelay(I2C_DELAY); udelay(I2C_DELAY);
} }
out:
clk_disable(dev->clk); clk_disable(dev->clk);
out2:
if (dev->regulator)
regulator_disable(dev->regulator);
dev->busy = false;
/* return the no. messages processed */ /* return the no. messages processed */
if (status) if (status)
@ -805,6 +829,21 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
return IRQ_HANDLED; 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) static unsigned int nmk_i2c_functionality(struct i2c_adapter *adap)
{ {
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
@ -830,7 +869,7 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev)
ret = -ENOMEM; ret = -ENOMEM;
goto err_no_mem; goto err_no_mem;
} }
dev->busy = false;
dev->pdev = pdev; dev->pdev = pdev;
platform_set_drvdata(pdev, dev); platform_set_drvdata(pdev, dev);
@ -860,6 +899,12 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev)
goto err_irq; 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); dev->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(dev->clk)) { if (IS_ERR(dev->clk)) {
dev_err(&pdev->dev, "could not get i2c clock\n"); 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); 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 " dev_info(&pdev->dev, "initialize %s on virtual "
"base %p\n", adap->name, dev->virtbase); "base %p\n", adap->name, dev->virtbase);
@ -904,10 +943,11 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev)
return 0; return 0;
err_init_hw:
err_add_adap: err_add_adap:
clk_put(dev->clk); clk_put(dev->clk);
err_no_clk: err_no_clk:
if (dev->regulator)
regulator_put(dev->regulator);
free_irq(dev->irq, dev); free_irq(dev->irq, dev);
err_irq: err_irq:
iounmap(dev->virtbase); iounmap(dev->virtbase);
@ -938,6 +978,8 @@ static int __devexit nmk_i2c_remove(struct platform_device *pdev)
if (res) if (res)
release_mem_region(res->start, resource_size(res)); release_mem_region(res->start, resource_size(res));
clk_put(dev->clk); clk_put(dev->clk);
if (dev->regulator)
regulator_put(dev->regulator);
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
kfree(dev); kfree(dev);
@ -951,6 +993,7 @@ static struct platform_driver nmk_i2c_driver = {
}, },
.probe = nmk_i2c_probe, .probe = nmk_i2c_probe,
.remove = __devexit_p(nmk_i2c_remove), .remove = __devexit_p(nmk_i2c_remove),
.suspend = nmk_i2c_suspend,
}; };
static int __init nmk_i2c_init(void) static int __init nmk_i2c_init(void)