aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/i2c/busses/i2c-s3c2410.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c/busses/i2c-s3c2410.c')
-rw-r--r--drivers/i2c/busses/i2c-s3c2410.c244
1 files changed, 133 insertions, 111 deletions
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c
index 6344f91..7d6d2b7 100644
--- a/drivers/i2c/busses/i2c-s3c2410.c
+++ b/drivers/i2c/busses/i2c-s3c2410.c
@@ -35,6 +35,8 @@
#include <linux/cpufreq.h>
#include <linux/slab.h>
#include <linux/io.h>
+#include <linux/of_i2c.h>
+#include <linux/of_gpio.h>
#include <asm/irq.h>
@@ -54,7 +56,6 @@ enum s3c24xx_i2c_state {
enum s3c24xx_i2c_type {
TYPE_S3C2410,
TYPE_S3C2440,
- TYPE_S3C2440_HDMIPHY,
};
struct s3c24xx_i2c {
@@ -79,6 +80,8 @@ struct s3c24xx_i2c {
struct resource *ioarea;
struct i2c_adapter adap;
+ struct s3c2410_platform_i2c *pdata;
+ int gpios[2];
#ifdef CONFIG_CPU_FREQ
struct notifier_block freq_transition;
#endif
@@ -86,17 +89,6 @@ struct s3c24xx_i2c {
/* default platform data removed, dev should always carry data. */
-static inline void dump_i2c_register(struct s3c24xx_i2c *i2c)
-{
- dev_dbg(i2c->dev, "Register dump(%d) : %x %x %x %x %x\n"
- , i2c->suspended
- , readl(i2c->regs + S3C2410_IICCON)
- , readl(i2c->regs + S3C2410_IICSTAT)
- , readl(i2c->regs + S3C2410_IICADD)
- , readl(i2c->regs + S3C2410_IICDS)
- , readl(i2c->regs + S3C2440_IICLC) );
-}
-
/* s3c24xx_i2c_is2440()
*
* return true is this is an s3c2440
@@ -107,21 +99,14 @@ static inline int s3c24xx_i2c_is2440(struct s3c24xx_i2c *i2c)
struct platform_device *pdev = to_platform_device(i2c->dev);
enum s3c24xx_i2c_type type;
- type = platform_get_device_id(pdev)->driver_data;
- return type == TYPE_S3C2440 || type == TYPE_S3C2440_HDMIPHY;
-}
-
-/* s3c24xx_i2c_is2440_hdmiphy()
- *
- * return true is this is an s3c2440 dedicated for HDMIPHY interface
-*/
-static inline int s3c24xx_i2c_is2440_hdmiphy(struct s3c24xx_i2c *i2c)
-{
- struct platform_device *pdev = to_platform_device(i2c->dev);
- enum s3c24xx_i2c_type type;
+#ifdef CONFIG_OF
+ if (i2c->dev->of_node)
+ return of_device_is_compatible(i2c->dev->of_node,
+ "samsung,s3c2440-i2c");
+#endif
type = platform_get_device_id(pdev)->driver_data;
- return type == TYPE_S3C2440_HDMIPHY;
+ return type == TYPE_S3C2440;
}
/* s3c24xx_i2c_master_complete
@@ -226,29 +211,18 @@ static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,
static inline void s3c24xx_i2c_stop(struct s3c24xx_i2c *i2c, int ret)
{
- unsigned long iicstat;
- unsigned long iiccon;
+ unsigned long iicstat = readl(i2c->regs + S3C2410_IICSTAT);
dev_dbg(i2c->dev, "STOP\n");
/* stop the transfer */
-
- /* Disable irq */
- s3c24xx_i2c_disable_irq(i2c);
-
- /* STOP signal generation : MTx(0xD0) */
- iicstat = readl(i2c->regs + S3C2410_IICSTAT);
iicstat &= ~S3C2410_IICSTAT_START;
writel(iicstat, i2c->regs + S3C2410_IICSTAT);
- /* Clear pending bit */
- iiccon = readl(i2c->regs + S3C2410_IICCON);
- iiccon &= ~S3C2410_IICCON_IRQPEND;
- writel(iiccon, i2c->regs + S3C2410_IICCON);
+ i2c->state = STATE_STOP;
s3c24xx_i2c_master_complete(i2c, ret);
-
- i2c->state = STATE_STOP;
+ s3c24xx_i2c_disable_irq(i2c);
}
/* helper functions to determine the current state in the set of
@@ -384,21 +358,12 @@ static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
* forces us to send a new START
* when we change direction */
- dev_dbg(i2c->dev, "Cannot do this\n");
s3c24xx_i2c_stop(i2c, -EINVAL);
}
- /* For multiple messages,
- * ex)
- * Msg[0]: Slave Addr + Write(Addr)
- * Msg[1]: Write(Data) */
goto retry_write;
} else {
/* send the new start */
- /* For multiple messages,
- * ex)
- * Msg[0]: Slave Addr + Write(Addr)
- * Msg[1]: Slave Addr + Read/Write(Data) */
s3c24xx_i2c_message_start(i2c, i2c->msg);
i2c->state = STATE_START;
}
@@ -469,8 +434,6 @@ static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id)
unsigned long status;
unsigned long tmp;
- spin_lock(&i2c->lock);
-
status = readl(i2c->regs + S3C2410_IICSTAT);
if (status & S3C2410_IICSTAT_ARBITR) {
@@ -493,8 +456,6 @@ static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id)
i2c_s3c_irq_nextbyte(i2c, status);
out:
- spin_unlock(&i2c->lock);
-
return IRQ_HANDLED;
}
@@ -539,7 +500,6 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,
ret = s3c24xx_i2c_set_master(i2c);
if (ret != 0) {
dev_err(i2c->dev, "cannot get bus (error %d)\n", ret);
- dump_i2c_register(i2c);
ret = -EAGAIN;
goto out;
}
@@ -564,15 +524,9 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,
* noisy when doing an i2cdetect */
if (timeout == 0)
- {
dev_dbg(i2c->dev, "timeout\n");
- dump_i2c_register(i2c);
- }
else if (ret != num)
- {
dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret);
- dump_i2c_register(i2c);
- }
/* ensure the stop has been through the bus */
@@ -580,33 +534,18 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,
/* first, try busy waiting briefly */
do {
+ cpu_relax();
iicstat = readl(i2c->regs + S3C2410_IICSTAT);
} while ((iicstat & S3C2410_IICSTAT_START) && --spins);
/* if that timed out sleep */
if (!spins) {
- usleep_range(1000, 1000);
+ msleep(1);
iicstat = readl(i2c->regs + S3C2410_IICSTAT);
}
- /* if still not finished, clean it up */
- spin_lock_irq(&i2c->lock);
-
- if (iicstat & S3C2410_IICSTAT_BUSBUSY) {
- dev_dbg(i2c->dev, "timeout waiting for bus idle\n");
- dump_i2c_register(i2c);
-
- if (i2c->state != STATE_STOP) {
- dev_dbg(i2c->dev, "timeout : i2c interrupt hasn't occurred\n");
- s3c24xx_i2c_stop(i2c, 0);
- }
-
- /* Disable Serial Out : To forcely terminate the connection */
- iicstat = readl(i2c->regs + S3C2410_IICSTAT);
- iicstat &= ~S3C2410_IICSTAT_TXRXEN;
- writel(iicstat, i2c->regs + S3C2410_IICSTAT);
- }
- spin_unlock_irq(&i2c->lock);
+ if (iicstat & S3C2410_IICSTAT_START)
+ dev_warn(i2c->dev, "timeout waiting for bus idle\n");
out:
return ret;
@@ -625,13 +564,6 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
int retry;
int ret;
- if (i2c->suspended)
- {
- dev_err(i2c->dev, "I2C is not initialzed.\n");
- dump_i2c_register(i2c);
- return -EIO;
- }
-
clk_enable(i2c->clk);
for (retry = 0; retry < adap->retries; retry++) {
@@ -704,7 +636,7 @@ static int s3c24xx_i2c_calcdivisor(unsigned long clkin, unsigned int wanted,
static int s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got)
{
- struct s3c2410_platform_i2c *pdata = i2c->dev->platform_data;
+ struct s3c2410_platform_i2c *pdata = i2c->pdata;
unsigned long clkin = clk_get_rate(i2c->clk);
unsigned int divs, div1;
unsigned long target_frequency;
@@ -789,7 +721,7 @@ static int s3c24xx_i2c_cpufreq_transition(struct notifier_block *nb,
if (ret < 0)
dev_err(i2c->dev, "cannot find frequency\n");
else
- dev_dbg(i2c->dev, "setting freq %d\n", got);
+ dev_info(i2c->dev, "setting freq %d\n", got);
}
return 0;
@@ -820,6 +752,49 @@ static inline void s3c24xx_i2c_deregister_cpufreq(struct s3c24xx_i2c *i2c)
}
#endif
+#ifdef CONFIG_OF
+static int s3c24xx_i2c_parse_dt_gpio(struct s3c24xx_i2c *i2c)
+{
+ int idx, gpio, ret;
+
+ for (idx = 0; idx < 2; idx++) {
+ gpio = of_get_gpio(i2c->dev->of_node, idx);
+ if (!gpio_is_valid(gpio)) {
+ dev_err(i2c->dev, "invalid gpio[%d]: %d\n", idx, gpio);
+ goto free_gpio;
+ }
+
+ ret = gpio_request(gpio, "i2c-bus");
+ if (ret) {
+ dev_err(i2c->dev, "gpio [%d] request failed\n", gpio);
+ goto free_gpio;
+ }
+ }
+ return 0;
+
+free_gpio:
+ while (--idx >= 0)
+ gpio_free(i2c->gpios[idx]);
+ return -EINVAL;
+}
+
+static void s3c24xx_i2c_dt_gpio_free(struct s3c24xx_i2c *i2c)
+{
+ unsigned int idx;
+ for (idx = 0; idx < 2; idx++)
+ gpio_free(i2c->gpios[idx]);
+}
+#else
+static int s3c24xx_i2c_parse_dt_gpio(struct s3c24xx_i2c *i2c)
+{
+ return 0;
+}
+
+static void s3c24xx_i2c_dt_gpio_free(struct s3c24xx_i2c *i2c)
+{
+}
+#endif
+
/* s3c24xx_i2c_init
*
* initialise the controller, set the IO lines and frequency
@@ -833,18 +808,21 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
/* get the plafrom data */
- pdata = i2c->dev->platform_data;
+ pdata = i2c->pdata;
/* inititalise the gpio */
if (pdata->cfg_gpio)
pdata->cfg_gpio(to_platform_device(i2c->dev));
+ else
+ if (s3c24xx_i2c_parse_dt_gpio(i2c))
+ return -EINVAL;
/* write slave address */
writeb(pdata->slave_addr, i2c->regs + S3C2410_IICADD);
- dev_dbg(i2c->dev, "slave address 0x%02x\n", pdata->slave_addr);
+ dev_info(i2c->dev, "slave address 0x%02x\n", pdata->slave_addr);
writel(iicon, i2c->regs + S3C2410_IICCON);
@@ -858,12 +836,40 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
/* todo - check that the i2c lines aren't being dragged anywhere */
- dev_dbg(i2c->dev, "bus frequency set to %d KHz\n", freq);
+ dev_info(i2c->dev, "bus frequency set to %d KHz\n", freq);
dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lx\n", iicon);
return 0;
}
+#ifdef CONFIG_OF
+/* s3c24xx_i2c_parse_dt
+ *
+ * Parse the device tree node and retreive the platform data.
+*/
+
+static void
+s3c24xx_i2c_parse_dt(struct device_node *np, struct s3c24xx_i2c *i2c)
+{
+ struct s3c2410_platform_i2c *pdata = i2c->pdata;
+
+ if (!np)
+ return;
+
+ pdata->bus_num = -1; /* i2c bus number is dynamically assigned */
+ of_property_read_u32(np, "samsung,i2c-sda-delay", &pdata->sda_delay);
+ of_property_read_u32(np, "samsung,i2c-slave-addr", &pdata->slave_addr);
+ of_property_read_u32(np, "samsung,i2c-max-bus-freq",
+ (u32 *)&pdata->frequency);
+}
+#else
+static void
+s3c24xx_i2c_parse_dt(struct device_node *np, struct s3c24xx_i2c *i2c)
+{
+ return;
+}
+#endif
+
/* s3c24xx_i2c_probe
*
* called by the bus driver when a suitable device is found
@@ -872,14 +878,16 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
struct s3c24xx_i2c *i2c;
- struct s3c2410_platform_i2c *pdata;
+ struct s3c2410_platform_i2c *pdata = NULL;
struct resource *res;
int ret;
- pdata = pdev->dev.platform_data;
- if (!pdata) {
- dev_err(&pdev->dev, "no platform data\n");
- return -EINVAL;
+ if (!pdev->dev.of_node) {
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev, "no platform data\n");
+ return -EINVAL;
+ }
}
i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);
@@ -888,6 +896,17 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ i2c->pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!i2c->pdata) {
+ ret = -ENOMEM;
+ goto err_noclk;
+ }
+
+ if (pdata)
+ memcpy(i2c->pdata, pdata, sizeof(*pdata));
+ else
+ s3c24xx_i2c_parse_dt(pdev->dev.of_node, i2c);
+
strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
i2c->adap.owner = THIS_MODULE;
i2c->adap.algo = &s3c24xx_i2c_algorithm;
@@ -962,7 +981,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
goto err_iomap;
}
- ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,
+ ret = request_irq(i2c->irq, s3c24xx_i2c_irq, 0,
dev_name(&pdev->dev), i2c);
if (ret != 0) {
@@ -982,7 +1001,8 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
* being bus 0.
*/
- i2c->adap.nr = pdata->bus_num;
+ i2c->adap.nr = i2c->pdata->bus_num;
+ i2c->adap.dev.of_node = pdev->dev.of_node;
ret = i2c_add_numbered_adapter(&i2c->adap);
if (ret < 0) {
@@ -990,6 +1010,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
goto err_cpufreq;
}
+ of_i2c_register_devices(&i2c->adap);
platform_set_drvdata(pdev, i2c);
dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
@@ -1038,6 +1059,7 @@ static int s3c24xx_i2c_remove(struct platform_device *pdev)
iounmap(i2c->regs);
release_resource(i2c->ioarea);
+ s3c24xx_i2c_dt_gpio_free(i2c);
kfree(i2c->ioarea);
kfree(i2c);
@@ -1055,7 +1077,7 @@ static int s3c24xx_i2c_suspend_noirq(struct device *dev)
return 0;
}
-static int s3c24xx_i2c_resume_noirq(struct device *dev)
+static int s3c24xx_i2c_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev);
@@ -1070,12 +1092,7 @@ static int s3c24xx_i2c_resume_noirq(struct device *dev)
static const struct dev_pm_ops s3c24xx_i2c_dev_pm_ops = {
.suspend_noirq = s3c24xx_i2c_suspend_noirq,
- .resume_noirq = s3c24xx_i2c_resume_noirq,
-#ifdef CONFIG_HIBERNATION
- .freeze_noirq = s3c24xx_i2c_suspend_noirq,
- .thaw_noirq = s3c24xx_i2c_resume_noirq,
- .restore_noirq = s3c24xx_i2c_resume_noirq,
-#endif
+ .resume = s3c24xx_i2c_resume,
};
#define S3C24XX_DEV_PM_OPS (&s3c24xx_i2c_dev_pm_ops)
@@ -1092,13 +1109,21 @@ static struct platform_device_id s3c24xx_driver_ids[] = {
}, {
.name = "s3c2440-i2c",
.driver_data = TYPE_S3C2440,
- }, {
- .name = "s3c2440-hdmiphy-i2c",
- .driver_data = TYPE_S3C2440_HDMIPHY,
}, { },
};
MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);
+#ifdef CONFIG_OF
+static const struct of_device_id s3c24xx_i2c_match[] = {
+ { .compatible = "samsung,s3c2410-i2c" },
+ { .compatible = "samsung,s3c2440-i2c" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, s3c24xx_i2c_match);
+#else
+#define s3c24xx_i2c_match NULL
+#endif
+
static struct platform_driver s3c24xx_i2c_driver = {
.probe = s3c24xx_i2c_probe,
.remove = s3c24xx_i2c_remove,
@@ -1107,6 +1132,7 @@ static struct platform_driver s3c24xx_i2c_driver = {
.owner = THIS_MODULE,
.name = "s3c-i2c",
.pm = S3C24XX_DEV_PM_OPS,
+ .of_match_table = s3c24xx_i2c_match,
},
};
@@ -1114,11 +1140,7 @@ static int __init i2c_adap_s3c_init(void)
{
return platform_driver_register(&s3c24xx_i2c_driver);
}
-#ifdef CONFIG_FAST_RESUME
-beforeresume_initcall(i2c_adap_s3c_init);
-#else
subsys_initcall(i2c_adap_s3c_init);
-#endif
static void __exit i2c_adap_s3c_exit(void)
{