|
@@ -23,10 +23,11 @@
|
|
|
#include <linux/err.h>
|
|
|
#include <linux/clk.h>
|
|
|
#include <linux/slab.h>
|
|
|
+#include <linux/of_i2c.h>
|
|
|
|
|
|
-#define I2C_PNX_TIMEOUT 10 /* msec */
|
|
|
-#define I2C_PNX_SPEED_KHZ 100
|
|
|
-#define I2C_PNX_REGION_SIZE 0x100
|
|
|
+#define I2C_PNX_TIMEOUT_DEFAULT 10 /* msec */
|
|
|
+#define I2C_PNX_SPEED_KHZ_DEFAULT 100
|
|
|
+#define I2C_PNX_REGION_SIZE 0x100
|
|
|
|
|
|
enum {
|
|
|
mstatus_tdi = 0x00000001,
|
|
@@ -74,8 +75,9 @@ enum {
|
|
|
#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(long timeout, struct i2c_pnx_algo_data *data)
|
|
|
+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);
|
|
@@ -84,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);
|
|
@@ -97,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;
|
|
@@ -135,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",
|
|
@@ -228,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");
|
|
|
}
|
|
@@ -326,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");
|
|
|
|
|
@@ -442,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);
|
|
|
}
|
|
@@ -457,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);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -612,6 +615,7 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev)
|
|
|
struct i2c_pnx_algo_data *alg_data;
|
|
|
unsigned long freq;
|
|
|
struct resource *res;
|
|
|
+ u32 speed = I2C_PNX_SPEED_KHZ_DEFAULT * 1000;
|
|
|
|
|
|
alg_data = kzalloc(sizeof(*alg_data), GFP_KERNEL);
|
|
|
if (!alg_data) {
|
|
@@ -626,6 +630,22 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev)
|
|
|
alg_data->adapter.algo_data = alg_data;
|
|
|
alg_data->adapter.nr = pdev->id;
|
|
|
|
|
|
+ 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);
|
|
@@ -651,7 +671,7 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev)
|
|
|
dev_err(&pdev->dev,
|
|
|
"I/O region 0x%08x for I2C already in use.\n",
|
|
|
res->start);
|
|
|
- ret = -ENODEV;
|
|
|
+ ret = -ENOMEM;
|
|
|
goto out_clkget;
|
|
|
}
|
|
|
|
|
@@ -680,14 +700,14 @@ 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;
|
|
|
}
|
|
@@ -710,6 +730,8 @@ 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, res->start, alg_data->irq);
|
|
|
|
|
@@ -748,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),
|