diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-s3c2410.c')
| -rw-r--r-- | drivers/i2c/busses/i2c-s3c2410.c | 240 |
1 files changed, 166 insertions, 74 deletions
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index a290d089cea..e828a1dba0e 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -36,19 +36,57 @@ #include <linux/cpufreq.h> #include <linux/slab.h> #include <linux/io.h> -#include <linux/of_i2c.h> +#include <linux/of.h> #include <linux/of_gpio.h> #include <linux/pinctrl/consumer.h> #include <asm/irq.h> -#include <plat/regs-iic.h> #include <linux/platform_data/i2c-s3c2410.h> +/* see s3c2410x user guide, v1.1, section 9 (p447) for more info */ + +#define S3C2410_IICCON 0x00 +#define S3C2410_IICSTAT 0x04 +#define S3C2410_IICADD 0x08 +#define S3C2410_IICDS 0x0C +#define S3C2440_IICLC 0x10 + +#define S3C2410_IICCON_ACKEN (1 << 7) +#define S3C2410_IICCON_TXDIV_16 (0 << 6) +#define S3C2410_IICCON_TXDIV_512 (1 << 6) +#define S3C2410_IICCON_IRQEN (1 << 5) +#define S3C2410_IICCON_IRQPEND (1 << 4) +#define S3C2410_IICCON_SCALE(x) ((x) & 0xf) +#define S3C2410_IICCON_SCALEMASK (0xf) + +#define S3C2410_IICSTAT_MASTER_RX (2 << 6) +#define S3C2410_IICSTAT_MASTER_TX (3 << 6) +#define S3C2410_IICSTAT_SLAVE_RX (0 << 6) +#define S3C2410_IICSTAT_SLAVE_TX (1 << 6) +#define S3C2410_IICSTAT_MODEMASK (3 << 6) + +#define S3C2410_IICSTAT_START (1 << 5) +#define S3C2410_IICSTAT_BUSBUSY (1 << 5) +#define S3C2410_IICSTAT_TXRXEN (1 << 4) +#define S3C2410_IICSTAT_ARBITR (1 << 3) +#define S3C2410_IICSTAT_ASSLAVE (1 << 2) +#define S3C2410_IICSTAT_ADDR0 (1 << 1) +#define S3C2410_IICSTAT_LASTBIT (1 << 0) + +#define S3C2410_IICLC_SDA_DELAY0 (0 << 0) +#define S3C2410_IICLC_SDA_DELAY5 (1 << 0) +#define S3C2410_IICLC_SDA_DELAY10 (2 << 0) +#define S3C2410_IICLC_SDA_DELAY15 (3 << 0) +#define S3C2410_IICLC_SDA_DELAY_MASK (3 << 0) + +#define S3C2410_IICLC_FILTER_ON (1 << 2) + /* Treat S3C2410 as baseline hardware, anything else is supported via quirks */ #define QUIRK_S3C2440 (1 << 0) #define QUIRK_HDMIPHY (1 << 1) #define QUIRK_NO_GPIO (1 << 2) +#define QUIRK_POLL (1 << 3) /* Max time to wait for bus to become idle after a xfer (in us) */ #define S3C2410_IDLE_TIMEOUT 5000 @@ -64,7 +102,7 @@ enum s3c24xx_i2c_state { struct s3c24xx_i2c { wait_queue_head_t wait; - unsigned int quirks; + kernel_ulong_t quirks; unsigned int suspended:1; struct i2c_msg *msg; @@ -86,7 +124,7 @@ struct s3c24xx_i2c { struct s3c2410_platform_i2c *pdata; int gpios[2]; struct pinctrl *pctrl; -#ifdef CONFIG_CPU_FREQ +#if defined(CONFIG_ARM_S3C24XX_CPUFREQ) struct notifier_block freq_transition; #endif }; @@ -105,12 +143,18 @@ static struct platform_device_id s3c24xx_driver_ids[] = { }; MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids); +static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat); + #ifdef CONFIG_OF static const struct of_device_id s3c24xx_i2c_match[] = { { .compatible = "samsung,s3c2410-i2c", .data = (void *)0 }, { .compatible = "samsung,s3c2440-i2c", .data = (void *)QUIRK_S3C2440 }, { .compatible = "samsung,s3c2440-hdmiphy-i2c", .data = (void *)(QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO) }, + { .compatible = "samsung,exynos5440-i2c", + .data = (void *)(QUIRK_S3C2440 | QUIRK_NO_GPIO) }, + { .compatible = "samsung,exynos5-sata-phy-i2c", + .data = (void *)(QUIRK_S3C2440 | QUIRK_POLL | QUIRK_NO_GPIO) }, {}, }; MODULE_DEVICE_TABLE(of, s3c24xx_i2c_match); @@ -121,12 +165,12 @@ MODULE_DEVICE_TABLE(of, s3c24xx_i2c_match); * Get controller type either from device tree or platform device variant. */ -static inline unsigned int s3c24xx_get_device_quirks(struct platform_device *pdev) +static inline kernel_ulong_t s3c24xx_get_device_quirks(struct platform_device *pdev) { if (pdev->dev.of_node) { const struct of_device_id *match; match = of_match_node(s3c24xx_i2c_match, pdev->dev.of_node); - return (unsigned int)match->data; + return (kernel_ulong_t)match->data; } return platform_get_device_id(pdev)->driver_data; @@ -149,7 +193,8 @@ static inline void s3c24xx_i2c_master_complete(struct s3c24xx_i2c *i2c, int ret) if (ret) i2c->msg_idx = ret; - wake_up(&i2c->wait); + if (!(i2c->quirks & QUIRK_POLL)) + wake_up(&i2c->wait); } static inline void s3c24xx_i2c_disable_ack(struct s3c24xx_i2c *i2c) @@ -186,6 +231,22 @@ static inline void s3c24xx_i2c_enable_irq(struct s3c24xx_i2c *i2c) writel(tmp | S3C2410_IICCON_IRQEN, i2c->regs + S3C2410_IICCON); } +static bool is_ack(struct s3c24xx_i2c *i2c) +{ + int tries; + + for (tries = 50; tries; --tries) { + if (readl(i2c->regs + S3C2410_IICCON) + & S3C2410_IICCON_IRQPEND) { + if (!(readl(i2c->regs + S3C2410_IICSTAT) + & S3C2410_IICSTAT_LASTBIT)) + return true; + } + usleep_range(1000, 2000); + } + dev_err(i2c->dev, "ack was not recieved\n"); + return false; +} /* s3c24xx_i2c_message_start * @@ -230,6 +291,16 @@ static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, stat |= S3C2410_IICSTAT_START; writel(stat, i2c->regs + S3C2410_IICSTAT); + + if (i2c->quirks & QUIRK_POLL) { + while ((i2c->msg_num != 0) && is_ack(i2c)) { + i2c_s3c_irq_nextbyte(i2c, stat); + stat = readl(i2c->regs + S3C2410_IICSTAT); + + if (stat & S3C2410_IICSTAT_ARBITR) + dev_err(i2c->dev, "deal with arbitration loss\n"); + } + } } static inline void s3c24xx_i2c_stop(struct s3c24xx_i2c *i2c, int ret) @@ -307,6 +378,12 @@ static inline int is_lastmsg(struct s3c24xx_i2c *i2c) static inline int is_msglast(struct s3c24xx_i2c *i2c) { + /* msg->len is always 1 for the first byte of smbus block read. + * Actual length will be read from slave. More bytes will be + * read according to the length then. */ + if (i2c->msg->flags & I2C_M_RECV_LEN && i2c->msg->len == 1) + return 0; + return i2c->msg_ptr == i2c->msg->len-1; } @@ -446,6 +523,9 @@ static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) byte = readb(i2c->regs + S3C2410_IICDS); i2c->msg->buf[i2c->msg_ptr++] = byte; + /* Add actual length to read for smbus block read */ + if (i2c->msg->flags & I2C_M_RECV_LEN && i2c->msg->len == 1) + i2c->msg->len += byte; prepare_read: if (is_msglast(i2c)) { /* last byte of buffer */ @@ -521,6 +601,31 @@ static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id) return IRQ_HANDLED; } +/* + * Disable the bus so that we won't get any interrupts from now on, or try + * to drive any lines. This is the default state when we don't have + * anything to send/receive. + * + * If there is an event on the bus, or we have a pre-existing event at + * kernel boot time, we may not notice the event and the I2C controller + * will lock the bus with the I2C clock line low indefinitely. + */ +static inline void s3c24xx_i2c_disable_bus(struct s3c24xx_i2c *i2c) +{ + unsigned long tmp; + + /* Stop driving the I2C pins */ + tmp = readl(i2c->regs + S3C2410_IICSTAT); + tmp &= ~S3C2410_IICSTAT_TXRXEN; + writel(tmp, i2c->regs + S3C2410_IICSTAT); + + /* We don't expect any interrupts now, and don't want send acks */ + tmp = readl(i2c->regs + S3C2410_IICCON); + tmp &= ~(S3C2410_IICCON_IRQEN | S3C2410_IICCON_IRQPEND | + S3C2410_IICCON_ACKEN); + writel(tmp, i2c->regs + S3C2410_IICCON); +} + /* s3c24xx_i2c_set_master * @@ -628,6 +733,15 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, s3c24xx_i2c_enable_irq(i2c); s3c24xx_i2c_message_start(i2c, msgs); + if (i2c->quirks & QUIRK_POLL) { + ret = i2c->msg_idx; + + if (ret != num) + dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret); + + goto out; + } + timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5); ret = i2c->msg_idx; @@ -646,7 +760,11 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, s3c24xx_i2c_wait_idle(i2c); + s3c24xx_i2c_disable_bus(i2c); + out: + i2c->state = STATE_IDLE; + return ret; } @@ -773,6 +891,9 @@ static int s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got) if (div1 == 512) iiccon |= S3C2410_IICCON_TXDIV_512; + if (i2c->quirks & QUIRK_POLL) + iiccon |= S3C2410_IICCON_SCALE(2); + writel(iiccon, i2c->regs + S3C2410_IICCON); if (i2c->quirks & QUIRK_S3C2440) { @@ -795,7 +916,7 @@ static int s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got) return 0; } -#ifdef CONFIG_CPU_FREQ +#if defined(CONFIG_ARM_S3C24XX_CPUFREQ) #define freq_to_i2c(_n) container_of(_n, struct s3c24xx_i2c, freq_transition) @@ -912,7 +1033,6 @@ static void s3c24xx_i2c_dt_gpio_free(struct s3c24xx_i2c *i2c) static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c) { - unsigned long iicon = S3C2410_IICCON_IRQEN | S3C2410_IICCON_ACKEN; struct s3c2410_platform_i2c *pdata; unsigned int freq; @@ -926,12 +1046,12 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c) dev_info(i2c->dev, "slave address 0x%02x\n", pdata->slave_addr); - writel(iicon, i2c->regs + S3C2410_IICCON); + writel(0, i2c->regs + S3C2410_IICCON); + writel(0, i2c->regs + S3C2410_IICSTAT); /* we need to work out the divisors for the clock... */ if (s3c24xx_i2c_clockrate(i2c, &freq) != 0) { - writel(0, i2c->regs + S3C2410_IICCON); dev_err(i2c->dev, "cannot meet bus frequency required\n"); return -EINVAL; } @@ -939,7 +1059,8 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c) /* todo - check that the i2c lines aren't being dragged anywhere */ dev_info(i2c->dev, "bus frequency set to %d KHz\n", freq); - dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lx\n", iicon); + dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02x\n", + readl(i2c->regs + S3C2410_IICCON)); return 0; } @@ -985,7 +1106,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) int ret; if (!pdev->dev.of_node) { - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (!pdata) { dev_err(&pdev->dev, "no platform data\n"); return -EINVAL; @@ -993,16 +1114,12 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) } i2c = devm_kzalloc(&pdev->dev, sizeof(struct s3c24xx_i2c), GFP_KERNEL); - if (!i2c) { - dev_err(&pdev->dev, "no memory for state\n"); + if (!i2c) return -ENOMEM; - } i2c->pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!i2c->pdata) { - ret = -ENOMEM; - goto err_noclk; - } + if (!i2c->pdata) + return -ENOMEM; i2c->quirks = s3c24xx_get_device_quirks(pdev); if (pdata) @@ -1014,7 +1131,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) i2c->adap.owner = THIS_MODULE; i2c->adap.algo = &s3c24xx_i2c_algorithm; i2c->adap.retries = 2; - i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD | I2C_CLASS_DEPRECATED; i2c->tx_setup = 50; init_waitqueue_head(&i2c->wait); @@ -1022,33 +1139,22 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) /* find the clock and enable it */ i2c->dev = &pdev->dev; - i2c->clk = clk_get(&pdev->dev, "i2c"); + i2c->clk = devm_clk_get(&pdev->dev, "i2c"); if (IS_ERR(i2c->clk)) { dev_err(&pdev->dev, "cannot get clock\n"); - ret = -ENOENT; - goto err_noclk; + return -ENOENT; } dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk); - clk_prepare_enable(i2c->clk); /* map the registers */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - dev_err(&pdev->dev, "cannot find IO resource\n"); - ret = -ENOENT; - goto err_clk; - } + i2c->regs = devm_ioremap_resource(&pdev->dev, res); - i2c->regs = devm_request_and_ioremap(&pdev->dev, res); - - if (i2c->regs == NULL) { - dev_err(&pdev->dev, "cannot map IO\n"); - ret = -ENXIO; - goto err_clk; - } + if (IS_ERR(i2c->regs)) + return PTR_ERR(i2c->regs); dev_dbg(&pdev->dev, "registers %p (%p)\n", i2c->regs, res); @@ -1065,38 +1171,42 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) if (i2c->pdata->cfg_gpio) { i2c->pdata->cfg_gpio(to_platform_device(i2c->dev)); } else if (IS_ERR(i2c->pctrl) && s3c24xx_i2c_parse_dt_gpio(i2c)) { - ret = -EINVAL; - goto err_clk; + return -EINVAL; } /* initialise the i2c controller */ + clk_prepare_enable(i2c->clk); ret = s3c24xx_i2c_init(i2c); - if (ret != 0) - goto err_clk; - + clk_disable_unprepare(i2c->clk); + if (ret != 0) { + dev_err(&pdev->dev, "I2C controller init failed\n"); + return ret; + } /* find the IRQ for this unit (note, this relies on the init call to * ensure no current IRQs pending */ - i2c->irq = ret = platform_get_irq(pdev, 0); - if (ret <= 0) { - dev_err(&pdev->dev, "cannot find IRQ\n"); - goto err_clk; - } + if (!(i2c->quirks & QUIRK_POLL)) { + i2c->irq = ret = platform_get_irq(pdev, 0); + if (ret <= 0) { + dev_err(&pdev->dev, "cannot find IRQ\n"); + return ret; + } - ret = request_irq(i2c->irq, s3c24xx_i2c_irq, 0, - dev_name(&pdev->dev), i2c); + ret = devm_request_irq(&pdev->dev, i2c->irq, s3c24xx_i2c_irq, 0, + dev_name(&pdev->dev), i2c); - if (ret != 0) { - dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq); - goto err_clk; + if (ret != 0) { + dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq); + return ret; + } } ret = s3c24xx_i2c_register_cpufreq(i2c); if (ret < 0) { dev_err(&pdev->dev, "failed to register cpufreq notifier\n"); - goto err_irq; + return ret; } /* Note, previous versions of the driver used i2c_add_adapter() @@ -1111,31 +1221,17 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) ret = i2c_add_numbered_adapter(&i2c->adap); if (ret < 0) { dev_err(&pdev->dev, "failed to add bus to i2c core\n"); - goto err_cpufreq; + s3c24xx_i2c_deregister_cpufreq(i2c); + return ret; } - of_i2c_register_devices(&i2c->adap); platform_set_drvdata(pdev, i2c); pm_runtime_enable(&pdev->dev); pm_runtime_enable(&i2c->adap.dev); dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev)); - clk_disable_unprepare(i2c->clk); return 0; - - err_cpufreq: - s3c24xx_i2c_deregister_cpufreq(i2c); - - err_irq: - free_irq(i2c->irq, i2c); - - err_clk: - clk_disable_unprepare(i2c->clk); - clk_put(i2c->clk); - - err_noclk: - return ret; } /* s3c24xx_i2c_remove @@ -1153,10 +1249,6 @@ static int s3c24xx_i2c_remove(struct platform_device *pdev) s3c24xx_i2c_deregister_cpufreq(i2c); i2c_del_adapter(&i2c->adap); - free_irq(i2c->irq, i2c); - - clk_disable_unprepare(i2c->clk); - clk_put(i2c->clk); if (pdev->dev.of_node && IS_ERR(i2c->pctrl)) s3c24xx_i2c_dt_gpio_free(i2c); @@ -1180,10 +1272,10 @@ static int s3c24xx_i2c_resume(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev); - i2c->suspended = 0; clk_prepare_enable(i2c->clk); s3c24xx_i2c_init(i2c); clk_disable_unprepare(i2c->clk); + i2c->suspended = 0; return 0; } |
