diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-s3c2410.c')
-rw-r--r-- | drivers/i2c/busses/i2c-s3c2410.c | 244 |
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) { |