summaryrefslogtreecommitdiff
path: root/drivers/i2c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-05-22 20:30:52 +0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-05-22 20:30:52 +0400
commitb324c67d4800e59171f48d9ddab6cbfb59110482 (patch)
treea16509a710e76fa24c01514b60aed06b7db13716 /drivers/i2c
parent8dca6010d44cc722a94dc6da96560f9083dac782 (diff)
parent396d81cd0fe12ce5d1f6d159f093f9315d2837bc (diff)
downloadlinux-b324c67d4800e59171f48d9ddab6cbfb59110482.tar.xz
Merge tag 'dt' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull device tree conversions for arm-soc, part 1, from Olof Johansson: "The spear3xx, lpc32xx, shmobile and mmp platforms are joining the game of booting using device trees, which is a great step forward for them. at91 and spear have pretty much completed this process with a huge amount of work being put into at91. The other platforms are continuing the process. We finally start to see the payback on this investment, as new machines are getting supported purely by adding a .dts source file that can be completely independent of the kernel source." Fix up trivial conflict in arch/arm/Kconfig * tag 'dt' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (83 commits) ARM: at91: Add ADC driver to at91sam9260/at91sam9g20 dtsi files arm/dts: omap4-panda: Add LEDs support arm/dts: omap4-sdp: Add LEDs support arm/dts: twl4030: Add twl4030-gpio node OMAP4: devices: Do not create mcpdm device if the dtb has been provided OMAP4: devices: Do not create dmic device if the dtb has been provided Documentation: update docs for mmp dt ARM: dts: refresh dts file for arch mmp ARM: mmp: support pxa910 with device tree ARM: mmp: support mmp2 with device tree gpio: pxa: parse gpio from DTS file ARM: mmp: support DT in timer ARM: mmp: support DT in irq ARM: mmp: append CONFIG_MACH_MMP2_DT ARM: mmp: fix build issue on mmp with device tree ARM: ux500: Enable PRCMU Timer 4 (clocksource) for Device Tree ARM: ux500: Disable SMSC911x platform code registration when DT is enabled ARM: ux500: Fork cpu-db8500 platform_devs for sequential DT enablement ARM: ux500: Do not attempt to register non-existent i2c devices on Snowball ARM: SPEAr3xx: Correct keyboard data passed from DT ...
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/busses/i2c-pnx.c157
1 files changed, 117 insertions, 40 deletions
diff --git a/drivers/i2c/busses/i2c-pnx.c b/drivers/i2c/busses/i2c-pnx.c
index eb8ad538c79f..99389d2eae51 100644
--- a/drivers/i2c/busses/i2c-pnx.c
+++ b/drivers/i2c/busses/i2c-pnx.c
@@ -23,16 +23,61 @@
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/slab.h>
+#include <linux/of_i2c.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_daie = 0x00000020,
+ mcntrl_rffie = 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 +86,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 +100,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;
@@ -92,7 +138,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 +231,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");
}
@@ -283,7 +329,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 +445,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 +460,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);
}
}
@@ -568,14 +614,8 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev)
int ret = 0;
struct i2c_pnx_algo_data *alg_data;
unsigned long freq;
- struct i2c_pnx_data *i2c_pnx = pdev->dev.platform_data;
-
- if (!i2c_pnx || !i2c_pnx->name) {
- dev_err(&pdev->dev, "%s: no platform data supplied\n",
- __func__);
- ret = -EINVAL;
- goto out;
- }
+ struct resource *res;
+ u32 speed = I2C_PNX_SPEED_KHZ_DEFAULT * 1000;
alg_data = kzalloc(sizeof(*alg_data), GFP_KERNEL);
if (!alg_data) {
@@ -585,14 +625,27 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev)
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->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 = clk_get(&pdev->dev, NULL);
if (IS_ERR(alg_data->clk)) {
ret = PTR_ERR(alg_data->clk);
@@ -603,17 +656,27 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev)
alg_data->mif.timer.function = i2c_pnx_timeout;
alg_data->mif.timer.data = (unsigned long)alg_data;
+ snprintf(alg_data->adapter.name, sizeof(alg_data->adapter.name),
+ "%s", pdev->name);
+
/* Register I/O resource */
- if (!request_mem_region(i2c_pnx->base, I2C_PNX_REGION_SIZE,
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "Unable to get mem resource.\n");
+ ret = -EBUSY;
+ goto out_clkget;
+ }
+ if (!request_mem_region(res->start, 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;
+ res->start);
+ ret = -ENOMEM;
goto out_clkget;
}
- alg_data->ioaddr = ioremap(i2c_pnx->base, I2C_PNX_REGION_SIZE);
+ alg_data->base = res->start;
+ alg_data->ioaddr = ioremap(res->start, I2C_PNX_REGION_SIZE);
if (!alg_data->ioaddr) {
dev_err(&pdev->dev, "Couldn't ioremap I2C I/O region\n");
ret = -ENOMEM;
@@ -637,20 +700,25 @@ 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,
+ 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");
+ goto out_irq;
+ }
+ ret = request_irq(alg_data->irq, i2c_pnx_interrupt,
0, pdev->name, alg_data);
if (ret)
goto out_clock;
@@ -662,39 +730,39 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev)
goto out_irq;
}
+ of_i2c_register_devices(&alg_data->adapter);
+
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);
+ free_irq(alg_data->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);
+ release_mem_region(res->start, 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)
{
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);
+ free_irq(alg_data->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);
+ release_mem_region(alg_data->base, I2C_PNX_REGION_SIZE);
clk_put(alg_data->clk);
kfree(alg_data);
platform_set_drvdata(pdev, NULL);
@@ -702,10 +770,19 @@ static int __devexit i2c_pnx_remove(struct platform_device *pdev)
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),
},
.probe = i2c_pnx_probe,
.remove = __devexit_p(i2c_pnx_remove),