diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-pnx.c')
| -rw-r--r-- | drivers/i2c/busses/i2c-pnx.c | 271 |
1 files changed, 158 insertions, 113 deletions
diff --git a/drivers/i2c/busses/i2c-pnx.c b/drivers/i2c/busses/i2c-pnx.c index a97e3fec814..dc7ff829ad7 100644 --- a/drivers/i2c/busses/i2c-pnx.c +++ b/drivers/i2c/busses/i2c-pnx.c @@ -23,16 +23,62 @@ #include <linux/err.h> #include <linux/clk.h> #include <linux/slab.h> +#include <linux/of.h> + +#define I2C_PNX_TIMEOUT_DEFAULT 10 /* msec */ +#define I2C_PNX_SPEED_KHZ_DEFAULT 100 +#define I2C_PNX_REGION_SIZE 0x100 + +enum { + mstatus_tdi = 0x00000001, + mstatus_afi = 0x00000002, + mstatus_nai = 0x00000004, + mstatus_drmi = 0x00000008, + mstatus_active = 0x00000020, + mstatus_scl = 0x00000040, + mstatus_sda = 0x00000080, + mstatus_rff = 0x00000100, + mstatus_rfe = 0x00000200, + mstatus_tff = 0x00000400, + mstatus_tfe = 0x00000800, +}; -#include <mach/hardware.h> -#include <mach/i2c.h> +enum { + mcntrl_tdie = 0x00000001, + mcntrl_afie = 0x00000002, + mcntrl_naie = 0x00000004, + mcntrl_drmie = 0x00000008, + mcntrl_drsie = 0x00000010, + mcntrl_rffie = 0x00000020, + mcntrl_daie = 0x00000040, + mcntrl_tffie = 0x00000080, + mcntrl_reset = 0x00000100, + mcntrl_cdbmode = 0x00000400, +}; -#define I2C_PNX_TIMEOUT 10 /* msec */ -#define I2C_PNX_SPEED_KHZ 100 -#define I2C_PNX_REGION_SIZE 0x100 +enum { + rw_bit = 1 << 0, + start_bit = 1 << 8, + stop_bit = 1 << 9, +}; -static inline int wait_timeout(long timeout, struct i2c_pnx_algo_data *data) +#define I2C_REG_RX(a) ((a)->ioaddr) /* Rx FIFO reg (RO) */ +#define I2C_REG_TX(a) ((a)->ioaddr) /* Tx FIFO reg (WO) */ +#define I2C_REG_STS(a) ((a)->ioaddr + 0x04) /* Status reg (RO) */ +#define I2C_REG_CTL(a) ((a)->ioaddr + 0x08) /* Ctl reg */ +#define I2C_REG_CKL(a) ((a)->ioaddr + 0x0c) /* Clock divider low */ +#define I2C_REG_CKH(a) ((a)->ioaddr + 0x10) /* Clock divider high */ +#define I2C_REG_ADR(a) ((a)->ioaddr + 0x14) /* I2C address */ +#define I2C_REG_RFL(a) ((a)->ioaddr + 0x18) /* Rx FIFO level (RO) */ +#define I2C_REG_TFL(a) ((a)->ioaddr + 0x1c) /* Tx FIFO level (RO) */ +#define I2C_REG_RXB(a) ((a)->ioaddr + 0x20) /* Num of bytes Rx-ed (RO) */ +#define I2C_REG_TXB(a) ((a)->ioaddr + 0x24) /* Num of bytes Tx-ed (RO) */ +#define I2C_REG_TXS(a) ((a)->ioaddr + 0x28) /* Tx slave FIFO (RO) */ +#define I2C_REG_STFL(a) ((a)->ioaddr + 0x2c) /* Tx slave FIFO level (RO) */ + +static inline int wait_timeout(struct i2c_pnx_algo_data *data) { + long timeout = data->timeout; while (timeout > 0 && (ioread32(I2C_REG_STS(data)) & mstatus_active)) { mdelay(1); @@ -41,8 +87,9 @@ static inline int wait_timeout(long timeout, struct i2c_pnx_algo_data *data) return (timeout <= 0); } -static inline int wait_reset(long timeout, struct i2c_pnx_algo_data *data) +static inline int wait_reset(struct i2c_pnx_algo_data *data) { + long timeout = data->timeout; while (timeout > 0 && (ioread32(I2C_REG_CTL(data)) & mcntrl_reset)) { mdelay(1); @@ -54,7 +101,7 @@ static inline int wait_reset(long timeout, struct i2c_pnx_algo_data *data) static inline void i2c_pnx_arm_timer(struct i2c_pnx_algo_data *alg_data) { struct timer_list *timer = &alg_data->mif.timer; - unsigned long expires = msecs_to_jiffies(I2C_PNX_TIMEOUT); + unsigned long expires = msecs_to_jiffies(alg_data->timeout); if (expires <= 1) expires = 2; @@ -65,7 +112,7 @@ static inline void i2c_pnx_arm_timer(struct i2c_pnx_algo_data *alg_data) jiffies, expires); timer->expires = jiffies + expires; - timer->data = (unsigned long)&alg_data; + timer->data = (unsigned long)alg_data; add_timer(timer); } @@ -92,7 +139,7 @@ static int i2c_pnx_start(unsigned char slave_addr, } /* First, make sure bus is idle */ - if (wait_timeout(I2C_PNX_TIMEOUT, alg_data)) { + if (wait_timeout(alg_data)) { /* Somebody else is monopolizing the bus */ dev_err(&alg_data->adapter.dev, "%s: Bus busy. Slave addr = %02x, cntrl = %x, stat = %x\n", @@ -185,7 +232,7 @@ static int i2c_pnx_master_xmit(struct i2c_pnx_algo_data *alg_data) if (alg_data->mif.len == 0) { if (alg_data->last) { /* Wait until the STOP is seen. */ - if (wait_timeout(I2C_PNX_TIMEOUT, alg_data)) + if (wait_timeout(alg_data)) dev_err(&alg_data->adapter.dev, "The bus is still active after timeout\n"); } @@ -244,31 +291,37 @@ static int i2c_pnx_master_rcv(struct i2c_pnx_algo_data *alg_data) * or we didn't 'ask' for it yet. */ if (ioread32(I2C_REG_STS(alg_data)) & mstatus_rfe) { - dev_dbg(&alg_data->adapter.dev, - "%s(): Write dummy data to fill Rx-fifo...\n", - __func__); + /* 'Asking' is done asynchronously, e.g. dummy TX of several + * bytes is done before the first actual RX arrives in FIFO. + * Therefore, ordered bytes (via TX) are counted separately. + */ + if (alg_data->mif.order) { + dev_dbg(&alg_data->adapter.dev, + "%s(): Write dummy data to fill Rx-fifo...\n", + __func__); - if (alg_data->mif.len == 1) { - /* Last byte, do not acknowledge next rcv. */ - val |= stop_bit; + if (alg_data->mif.order == 1) { + /* Last byte, do not acknowledge next rcv. */ + val |= stop_bit; + + /* + * Enable interrupt RFDAIE (data in Rx fifo), + * and disable DRMIE (need data for Tx) + */ + ctl = ioread32(I2C_REG_CTL(alg_data)); + ctl |= mcntrl_rffie | mcntrl_daie; + ctl &= ~mcntrl_drmie; + iowrite32(ctl, I2C_REG_CTL(alg_data)); + } /* - * Enable interrupt RFDAIE (data in Rx fifo), - * and disable DRMIE (need data for Tx) + * Now we'll 'ask' for data: + * For each byte we want to receive, we must + * write a (dummy) byte to the Tx-FIFO. */ - ctl = ioread32(I2C_REG_CTL(alg_data)); - ctl |= mcntrl_rffie | mcntrl_daie; - ctl &= ~mcntrl_drmie; - iowrite32(ctl, I2C_REG_CTL(alg_data)); + iowrite32(val, I2C_REG_TX(alg_data)); + alg_data->mif.order--; } - - /* - * Now we'll 'ask' for data: - * For each byte we want to receive, we must - * write a (dummy) byte to the Tx-FIFO. - */ - iowrite32(val, I2C_REG_TX(alg_data)); - return 0; } @@ -283,7 +336,7 @@ static int i2c_pnx_master_rcv(struct i2c_pnx_algo_data *alg_data) if (alg_data->mif.len == 0) { if (alg_data->last) /* Wait until the STOP is seen. */ - if (wait_timeout(I2C_PNX_TIMEOUT, alg_data)) + if (wait_timeout(alg_data)) dev_err(&alg_data->adapter.dev, "The bus is still active after timeout\n"); @@ -399,7 +452,7 @@ static void i2c_pnx_timeout(unsigned long data) ctl |= mcntrl_reset; iowrite32(ctl, I2C_REG_CTL(alg_data)); - wait_reset(I2C_PNX_TIMEOUT, alg_data); + wait_reset(alg_data); alg_data->mif.ret = -EIO; complete(&alg_data->mif.complete); } @@ -414,18 +467,18 @@ static inline void bus_reset_if_active(struct i2c_pnx_algo_data *alg_data) alg_data->adapter.name); iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset, I2C_REG_CTL(alg_data)); - wait_reset(I2C_PNX_TIMEOUT, alg_data); + wait_reset(alg_data); } else if (!(stat & mstatus_rfe) || !(stat & mstatus_tfe)) { /* If there is data in the fifo's after transfer, * flush fifo's by reset. */ iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset, I2C_REG_CTL(alg_data)); - wait_reset(I2C_PNX_TIMEOUT, alg_data); + wait_reset(alg_data); } else if (stat & mstatus_nai) { iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset, I2C_REG_CTL(alg_data)); - wait_reset(I2C_PNX_TIMEOUT, alg_data); + wait_reset(alg_data); } } @@ -468,6 +521,7 @@ i2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) alg_data->mif.buf = pmsg->buf; alg_data->mif.len = pmsg->len; + alg_data->mif.order = pmsg->len; alg_data->mif.mode = (pmsg->flags & I2C_M_RD) ? I2C_SMBUS_READ : I2C_SMBUS_WRITE; alg_data->mif.ret = 0; @@ -520,6 +574,7 @@ i2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) /* Cleanup to be sure... */ alg_data->mif.buf = NULL; alg_data->mif.len = 0; + alg_data->mif.order = 0; dev_dbg(&alg_data->adapter.dev, "%s(): exiting, stat = %x\n", __func__, ioread32(I2C_REG_STS(alg_data))); @@ -540,90 +595,86 @@ static struct i2c_algorithm pnx_algorithm = { .functionality = i2c_pnx_func, }; -#ifdef CONFIG_PM -static int i2c_pnx_controller_suspend(struct platform_device *pdev, - pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int i2c_pnx_controller_suspend(struct device *dev) { - struct i2c_pnx_algo_data *alg_data = platform_get_drvdata(pdev); + struct i2c_pnx_algo_data *alg_data = dev_get_drvdata(dev); - /* FIXME: shouldn't this be clk_disable? */ - clk_enable(alg_data->clk); + clk_disable(alg_data->clk); return 0; } -static int i2c_pnx_controller_resume(struct platform_device *pdev) +static int i2c_pnx_controller_resume(struct device *dev) { - struct i2c_pnx_algo_data *alg_data = platform_get_drvdata(pdev); + struct i2c_pnx_algo_data *alg_data = dev_get_drvdata(dev); return clk_enable(alg_data->clk); } + +static SIMPLE_DEV_PM_OPS(i2c_pnx_pm, + i2c_pnx_controller_suspend, i2c_pnx_controller_resume); +#define PNX_I2C_PM (&i2c_pnx_pm) #else -#define i2c_pnx_controller_suspend NULL -#define i2c_pnx_controller_resume NULL +#define PNX_I2C_PM NULL #endif -static int __devinit i2c_pnx_probe(struct platform_device *pdev) +static int i2c_pnx_probe(struct platform_device *pdev) { unsigned long tmp; int ret = 0; struct i2c_pnx_algo_data *alg_data; unsigned long freq; - struct i2c_pnx_data *i2c_pnx = pdev->dev.platform_data; + struct resource *res; + u32 speed = I2C_PNX_SPEED_KHZ_DEFAULT * 1000; - if (!i2c_pnx || !i2c_pnx->name) { - dev_err(&pdev->dev, "%s: no platform data supplied\n", - __func__); - ret = -EINVAL; - goto out; - } - - alg_data = kzalloc(sizeof(*alg_data), GFP_KERNEL); - if (!alg_data) { - ret = -ENOMEM; - goto err_kzalloc; - } + alg_data = devm_kzalloc(&pdev->dev, sizeof(*alg_data), GFP_KERNEL); + if (!alg_data) + return -ENOMEM; platform_set_drvdata(pdev, alg_data); - strlcpy(alg_data->adapter.name, i2c_pnx->name, - sizeof(alg_data->adapter.name)); alg_data->adapter.dev.parent = &pdev->dev; alg_data->adapter.algo = &pnx_algorithm; alg_data->adapter.algo_data = alg_data; alg_data->adapter.nr = pdev->id; - alg_data->i2c_pnx = i2c_pnx; - alg_data->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(alg_data->clk)) { - ret = PTR_ERR(alg_data->clk); - goto out_drvdata; + alg_data->timeout = I2C_PNX_TIMEOUT_DEFAULT; +#ifdef CONFIG_OF + alg_data->adapter.dev.of_node = of_node_get(pdev->dev.of_node); + if (pdev->dev.of_node) { + of_property_read_u32(pdev->dev.of_node, "clock-frequency", + &speed); + /* + * At this point, it is planned to add an OF timeout property. + * As soon as there is a consensus about how to call and handle + * this, sth. like the following can be put here: + * + * of_property_read_u32(pdev->dev.of_node, "timeout", + * &alg_data->timeout); + */ } +#endif + alg_data->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(alg_data->clk)) + return PTR_ERR(alg_data->clk); init_timer(&alg_data->mif.timer); alg_data->mif.timer.function = i2c_pnx_timeout; alg_data->mif.timer.data = (unsigned long)alg_data; - /* Register I/O resource */ - if (!request_mem_region(i2c_pnx->base, I2C_PNX_REGION_SIZE, - pdev->name)) { - dev_err(&pdev->dev, - "I/O region 0x%08x for I2C already in use.\n", - i2c_pnx->base); - ret = -ENODEV; - goto out_clkget; - } + snprintf(alg_data->adapter.name, sizeof(alg_data->adapter.name), + "%s", pdev->name); - alg_data->ioaddr = ioremap(i2c_pnx->base, I2C_PNX_REGION_SIZE); - if (!alg_data->ioaddr) { - dev_err(&pdev->dev, "Couldn't ioremap I2C I/O region\n"); - ret = -ENOMEM; - goto out_release; - } + /* Register I/O resource */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + alg_data->ioaddr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(alg_data->ioaddr)) + return PTR_ERR(alg_data->ioaddr); ret = clk_enable(alg_data->clk); if (ret) - goto out_unmap; + return ret; freq = clk_get_rate(alg_data->clk); @@ -638,21 +689,27 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev) * the deglitching filter length. */ - tmp = ((freq / 1000) / I2C_PNX_SPEED_KHZ) / 2 - 2; + tmp = (freq / speed) / 2 - 2; if (tmp > 0x3FF) tmp = 0x3FF; iowrite32(tmp, I2C_REG_CKH(alg_data)); iowrite32(tmp, I2C_REG_CKL(alg_data)); iowrite32(mcntrl_reset, I2C_REG_CTL(alg_data)); - if (wait_reset(I2C_PNX_TIMEOUT, alg_data)) { + if (wait_reset(alg_data)) { ret = -ENODEV; goto out_clock; } init_completion(&alg_data->mif.complete); - ret = request_irq(i2c_pnx->irq, i2c_pnx_interrupt, - 0, pdev->name, alg_data); + alg_data->irq = platform_get_irq(pdev, 0); + if (alg_data->irq < 0) { + dev_err(&pdev->dev, "Failed to get IRQ from platform resource\n"); + ret = alg_data->irq; + goto out_clock; + } + ret = devm_request_irq(&pdev->dev, alg_data->irq, i2c_pnx_interrupt, + 0, pdev->name, alg_data); if (ret) goto out_clock; @@ -660,58 +717,46 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev) ret = i2c_add_numbered_adapter(&alg_data->adapter); if (ret < 0) { dev_err(&pdev->dev, "I2C: Failed to add bus\n"); - goto out_irq; + goto out_clock; } dev_dbg(&pdev->dev, "%s: Master at %#8x, irq %d.\n", - alg_data->adapter.name, i2c_pnx->base, i2c_pnx->irq); + alg_data->adapter.name, res->start, alg_data->irq); return 0; -out_irq: - free_irq(i2c_pnx->irq, alg_data); out_clock: clk_disable(alg_data->clk); -out_unmap: - iounmap(alg_data->ioaddr); -out_release: - release_mem_region(i2c_pnx->base, I2C_PNX_REGION_SIZE); -out_clkget: - clk_put(alg_data->clk); -out_drvdata: - kfree(alg_data); -err_kzalloc: - platform_set_drvdata(pdev, NULL); -out: return ret; } -static int __devexit i2c_pnx_remove(struct platform_device *pdev) +static int i2c_pnx_remove(struct platform_device *pdev) { struct i2c_pnx_algo_data *alg_data = platform_get_drvdata(pdev); - struct i2c_pnx_data *i2c_pnx = alg_data->i2c_pnx; - free_irq(i2c_pnx->irq, alg_data); i2c_del_adapter(&alg_data->adapter); clk_disable(alg_data->clk); - iounmap(alg_data->ioaddr); - release_mem_region(i2c_pnx->base, I2C_PNX_REGION_SIZE); - clk_put(alg_data->clk); - kfree(alg_data); - platform_set_drvdata(pdev, NULL); return 0; } +#ifdef CONFIG_OF +static const struct of_device_id i2c_pnx_of_match[] = { + { .compatible = "nxp,pnx-i2c" }, + { }, +}; +MODULE_DEVICE_TABLE(of, i2c_pnx_of_match); +#endif + static struct platform_driver i2c_pnx_driver = { .driver = { .name = "pnx-i2c", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(i2c_pnx_of_match), + .pm = PNX_I2C_PM, }, .probe = i2c_pnx_probe, - .remove = __devexit_p(i2c_pnx_remove), - .suspend = i2c_pnx_controller_suspend, - .resume = i2c_pnx_controller_resume, + .remove = i2c_pnx_remove, }; static int __init i2c_adap_pnx_init(void) |
