aboutsummaryrefslogtreecommitdiff
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.c240
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;
}