Skip to content

Instantly share code, notes, and snippets.

@neheb
Created April 22, 2018 01:32
Show Gist options
  • Save neheb/427d2d7dea32b53b55b75442d240b9b2 to your computer and use it in GitHub Desktop.
Save neheb/427d2d7dea32b53b55b75442d240b9b2 to your computer and use it in GitHub Desktop.
< From d5c54ff3d1db0a4348fa04d8e78f3bf6063e3afc Mon Sep 17 00:00:00 2001
< From: John Crispin <[email protected]>
< Date: Mon, 7 Dec 2015 17:21:27 +0100
< Subject: [PATCH 45/53] i2c: add mt7621 driver
---
> I2C on MT7621/MT7628 has two modes: "auto" and "manual". Old driver uses "auto"
> mode which does not support propper ACK/NAK handling, clock stratching, repeated
> start and was limited to 32 bytes per message.
> This driver is using "manual" mode so it can support all these features. Main
> advantages over old driver are:
> - unlimited message length
> - clock stretching
> - ACK/NAK handling (i2c-detect now works)
> - repeated start sequence
6c11
< Signed-off-by: John Crispin <[email protected]>
---
> Signed-off-by: Jan Breuer <[email protected]>
40c45
< @@ -0,0 +1,433 @@
---
> @@ -0,0 +1,406 @@
45a51
> + * Copyright (C) 2018 Jan Breuer <[email protected]>
76,139c82,114
< +#define REG_SM0CFG0 0x08
< +#define REG_SM0DOUT 0x10
< +#define REG_SM0DIN 0x14
< +#define REG_SM0ST 0x18
< +#define REG_SM0AUTO 0x1C
< +#define REG_SM0CFG1 0x20
< +#define REG_SM0CFG2 0x28
< +#define REG_SM0CTL0 0x40
< +#define REG_SM0CTL1 0x44
< +#define REG_SM0D0 0x50
< +#define REG_SM0D1 0x54
< +#define REG_PINTEN 0x5C
< +#define REG_PINTST 0x60
< +#define REG_PINTCL 0x64
< +
< +/* REG_SM0CFG0 */
< +#define I2C_DEVADDR_MASK 0x7f
< +
< +/* REG_SM0ST */
< +#define I2C_DATARDY BIT(2)
< +#define I2C_SDOEMPTY BIT(1)
< +#define I2C_BUSY BIT(0)
< +
< +/* REG_SM0AUTO */
< +#define READ_CMD BIT(0)
< +
< +/* REG_SM0CFG1 */
< +#define BYTECNT_MAX 64
< +#define SET_BYTECNT(x) (x - 1)
< +
< +/* REG_SM0CFG2 */
< +#define AUTOMODE_EN BIT(0)
< +
< +/* REG_SM0CTL0 */
< +#define ODRAIN_HIGH_SM0 BIT(31)
< +#define VSYNC_SHIFT 28
< +#define VSYNC_MASK 0x3
< +#define VSYNC_PULSE (0x1 << VSYNC_SHIFT)
< +#define VSYNC_RISING (0x2 << VSYNC_SHIFT)
< +#define CLK_DIV_SHIFT 16
< +#define CLK_DIV_MASK 0xfff
< +#define DEG_CNT_SHIFT 8
< +#define DEG_CNT_MASK 0xff
< +#define WAIT_HIGH BIT(6)
< +#define DEG_EN BIT(5)
< +#define CS_STATUA BIT(4)
< +#define SCL_STATUS BIT(3)
< +#define SDA_STATUS BIT(2)
< +#define SM0_EN BIT(1)
< +#define SCL_STRECH BIT(0)
< +
< +/* REG_SM0CTL1 */
< +#define ACK_SHIFT 16
< +#define ACK_MASK 0xff
< +#define PGLEN_SHIFT 8
< +#define PGLEN_MASK 0x7
< +#define SM0_MODE_SHIFT 4
< +#define SM0_MODE_MASK 0x7
< +#define SM0_MODE_START 0x1
< +#define SM0_MODE_WRITE 0x2
< +#define SM0_MODE_STOP 0x3
< +#define SM0_MODE_READ_NACK 0x4
< +#define SM0_MODE_READ_ACK 0x5
< +#define SM0_TRI_BUSY BIT(0)
---
> +#define REG_SM0CFG2_REG 0x28
> +#define REG_SM0CTL0_REG 0x40
> +#define REG_SM0CTL1_REG 0x44
> +#define REG_SM0D0_REG 0x50
> +#define REG_SM0D1_REG 0x54
> +#define REG_PINTEN_REG 0x5C
> +#define REG_PINTST_REG 0x60
> +#define REG_PINTCL_REG 0x64
> +
> +/* REG_SM0CFG2_REG */
> +#define SM0CFG2_IS_AUTOMODE BIT(0)
> +
> +/* REG_SM0CTL0_REG */
> +#define SM0CTL0_ODRAIN BIT(31)
> +#define SM0CTL0_CLK_DIV_MASK (0x7FF << 16)
> +#define SM0CTL0_CLK_DIV_MAX 0x7ff
> +#define SM0CTL0_CS_STATUS BIT(4)
> +#define SM0CTL0_SCL_STATE BIT(3)
> +#define SM0CTL0_SDA_STATE BIT(2)
> +#define SM0CTL0_EN BIT(1)
> +#define SM0CTL0_SCL_STRETCH BIT(0)
> +
> +/* REG_SM0CTL1_REG */
> +#define SM0CTL1_ACK_MASK (0xFF << 16)
> +#define SM0CTL1_PGLEN_MASK (0x7 << 8)
> +#define SM0CTL1_PGLEN(x) (((x - 1) << 8) & SM0CTL1_PGLEN_MASK)
> +#define SM0CTL1_READ (5 << 4)
> +#define SM0CTL1_READ_LAST (4 << 4)
> +#define SM0CTL1_STOP (3 << 4)
> +#define SM0CTL1_WRITE (2 << 4)
> +#define SM0CTL1_START (1 << 4)
> +#define SM0CTL1_MODE_MASK (0x7 << 4)
> +#define SM0CTL1_TRI BIT(0)
167a143
> + int current_delay = 1;
173c149,157
< + usleep_range(DELAY_INTERVAL_US, DELAY_INTERVAL_US + 50);
---
> + if (current_delay > 0 && current_delay < 10) {
> + udelay(current_delay);
> + } else if (current_delay >= 10) {
> + usleep_range(current_delay, current_delay + 50);
> + }
> + current_delay *= current_delay;
> + if (current_delay > DELAY_INTERVAL_US) {
> + current_delay = DELAY_INTERVAL_US;
> + }
183c167
< + ret = poll_down_timeout(i2c->base + REG_SM0ST, I2C_BUSY);
---
> + ret = poll_down_timeout(i2c->base + REG_SM0CTL1_REG, SM0CTL1_TRI);
190c174
< +static int poll_up_timeout(void __iomem *addr, u32 mask)
---
> +static void mtk_i2c_reset(struct mtk_i2c *i2c)
192,202c176,183
< + unsigned long timeout = jiffies + msecs_to_jiffies(TIMEOUT_MS);
< + u32 status;
< +
< + do {
< + status = readl_relaxed(addr);
< + if (status & mask)
< + return 0;
< + usleep_range(DELAY_INTERVAL_US, DELAY_INTERVAL_US + 50);
< + } while (time_before(jiffies, timeout));
< +
< + return -ETIMEDOUT;
---
> + device_reset(i2c->adap.dev.parent);
> + barrier();
> + mtk_i2c_w32(i2c,
> + SM0CTL0_ODRAIN
> + | ((i2c->clk_div << 16) & SM0CTL0_CLK_DIV_MASK)
> + | SM0CTL0_EN
> + | SM0CTL0_SCL_STRETCH, REG_SM0CTL0_REG);
> + mtk_i2c_w32(i2c, 0, REG_SM0CFG2_REG);
205c186
< +static int mtk_i2c_wait_rx_done(struct mtk_i2c *i2c)
---
> +static void mtk_i2c_dump_reg(struct mtk_i2c *i2c)
207,213c188,194
< + int ret;
< +
< + ret = poll_up_timeout(i2c->base + REG_SM0ST, I2C_DATARDY);
< + if (ret < 0)
< + dev_dbg(i2c->dev, "rx err(%d)\n", ret);
< +
< + return ret;
---
> + dev_dbg(i2c->dev, "SM0CFG2 %08x, SM0CTL0 %08x, SM0CTL1 %08x, " \
> + "SM0D0 %08x, SM0D1 %08x\n",
> + mtk_i2c_r32(i2c, REG_SM0CFG2_REG),
> + mtk_i2c_r32(i2c, REG_SM0CTL0_REG),
> + mtk_i2c_r32(i2c, REG_SM0CTL1_REG),
> + mtk_i2c_r32(i2c, REG_SM0D0_REG),
> + mtk_i2c_r32(i2c, REG_SM0D1_REG));
216c197
< +static int mtk_i2c_wait_tx_done(struct mtk_i2c *i2c)
---
> +static int mtk_i2c_check_ack(struct mtk_i2c *i2c, u32 expected)
218,224c199,201
< + int ret;
< +
< + ret = poll_up_timeout(i2c->base + REG_SM0ST, I2C_SDOEMPTY);
< + if (ret < 0)
< + dev_dbg(i2c->dev, "tx err(%d)\n", ret);
< +
< + return ret;
---
> + u32 ack = readl_relaxed(i2c->base + REG_SM0CTL1_REG);
> + u32 ack_expected = (expected << 16) & SM0CTL1_ACK_MASK;
> + return ((ack & ack_expected) == ack_expected) ? 0 : -ENXIO;
227c204
< +static void mtk_i2c_reset(struct mtk_i2c *i2c)
---
> +static int mtk_i2c_master_start(struct mtk_i2c *i2c)
229,239c206,207
< + u32 reg;
< + device_reset(i2c->adap.dev.parent);
< + barrier();
< +
< + /* ctrl0 */
< + reg = ODRAIN_HIGH_SM0 | VSYNC_PULSE | (i2c->clk_div << CLK_DIV_SHIFT) |
< + WAIT_HIGH | SM0_EN;
< + mtk_i2c_w32(i2c, reg, REG_SM0CTL0);
< +
< + /* auto mode */
< + mtk_i2c_w32(i2c, AUTOMODE_EN, REG_SM0CFG2);
---
> + mtk_i2c_w32(i2c, SM0CTL1_START | SM0CTL1_TRI, REG_SM0CTL1_REG);
> + return mtk_i2c_wait_idle(i2c);
242c210
< +static void mtk_i2c_dump_reg(struct mtk_i2c *i2c)
---
> +static int mtk_i2c_master_stop(struct mtk_i2c *i2c)
244,255c212,213
< + dev_dbg(i2c->dev, "cfg0 %08x, dout %08x, din %08x, " \
< + "status %08x, auto %08x, cfg1 %08x, " \
< + "cfg2 %08x, ctl0 %08x, ctl1 %08x\n",
< + mtk_i2c_r32(i2c, REG_SM0CFG0),
< + mtk_i2c_r32(i2c, REG_SM0DOUT),
< + mtk_i2c_r32(i2c, REG_SM0DIN),
< + mtk_i2c_r32(i2c, REG_SM0ST),
< + mtk_i2c_r32(i2c, REG_SM0AUTO),
< + mtk_i2c_r32(i2c, REG_SM0CFG1),
< + mtk_i2c_r32(i2c, REG_SM0CFG2),
< + mtk_i2c_r32(i2c, REG_SM0CTL0),
< + mtk_i2c_r32(i2c, REG_SM0CTL1));
---
> + mtk_i2c_w32(i2c, SM0CTL1_STOP | SM0CTL1_TRI, REG_SM0CTL1_REG);
> + return mtk_i2c_wait_idle(i2c);
257a216,220
> +static int mtk_i2c_master_cmd(struct mtk_i2c *i2c, u32 cmd, int page_len)
> +{
> + mtk_i2c_w32(i2c, cmd | SM0CTL1_TRI | SM0CTL1_PGLEN(page_len), REG_SM0CTL1_REG);
> + return mtk_i2c_wait_idle(i2c);
> +}
263c226,227
< + int i, j, ret;
---
> + u16 addr;
> + int i, j, ret, len, page_len;
264a229
> + u32 data[2];
270d234
< + cmd = 0;
278a243,247
> + /* start sequence */
> + if ((ret = mtk_i2c_master_start(i2c)))
> + goto err_timeout;
> +
> + /* write address */
280,281c249,256
< + dev_dbg(i2c->dev, "10 bits addr not supported\n");
< + return -EINVAL;
---
> + /* 10 bits address */
> + addr = 0xf0 | ((pmsg->addr >> 7) & 0x06);
> + addr |= (pmsg->addr & 0xff) << 8;
> + if (pmsg->flags & I2C_M_RD)
> + addr |= 1;
> + mtk_i2c_w32(i2c, addr, REG_SM0D0_REG);
> + if ((ret = mtk_i2c_master_cmd(i2c, SM0CTL1_WRITE, 2)))
> + goto err_timeout;
284,285c259,264
< + mtk_i2c_w32(i2c, pmsg->addr & I2C_DEVADDR_MASK,
< + REG_SM0CFG0);
---
> + addr = pmsg->addr << 1;
> + if (pmsg->flags & I2C_M_RD)
> + addr |= 1;
> + mtk_i2c_w32(i2c, addr, REG_SM0D0_REG);
> + if ((ret = mtk_i2c_master_cmd(i2c, SM0CTL1_WRITE, 1)))
> + goto err_timeout;
288,294c267,271
< + /* buffer length */
< + if (pmsg->len == 0) {
< + dev_dbg(i2c->dev, "length is 0\n");
< + return -EINVAL;
< + } else
< + mtk_i2c_w32(i2c, SET_BYTECNT(pmsg->len),
< + REG_SM0CFG1);
---
> + /* check address ACK */
> + if (!(pmsg->flags & I2C_M_IGNORE_NAK)) {
> + if ((ret = mtk_i2c_check_ack(i2c, BIT(0))))
> + goto err_ack;
> + }
295a273
> + /* transfer data */
297,321c275,297
< + if (pmsg->flags & I2C_M_RD) {
< + cmd |= READ_CMD;
< + /* start transfer */
< + barrier();
< + mtk_i2c_w32(i2c, cmd, REG_SM0AUTO);
< + do {
< + /* wait */
< + if ((ret = mtk_i2c_wait_rx_done(i2c)))
< + goto err_timeout;
< + /* read data */
< + if (pmsg->len)
< + pmsg->buf[j] = mtk_i2c_r32(i2c,
< + REG_SM0DIN);
< + j++;
< + } while (j < pmsg->len);
< + } else {
< + do {
< + /* write data */
< + if (pmsg->len)
< + mtk_i2c_w32(i2c, pmsg->buf[j],
< + REG_SM0DOUT);
< + /* start transfer */
< + if (j == 0) {
< + barrier();
< + mtk_i2c_w32(i2c, cmd, REG_SM0AUTO);
---
> + for (len = pmsg->len; len > 0; len -= 8) {
> + page_len = (len >= 8) ? 8 : len;
> +
> + if (pmsg->flags & I2C_M_RD) {
> + cmd = (len > 8) ? SM0CTL1_READ : SM0CTL1_READ_LAST;
> + } else {
> + memcpy(data, &pmsg->buf[j], page_len);
> + mtk_i2c_w32(i2c, data[0], REG_SM0D0_REG);
> + mtk_i2c_w32(i2c, data[1], REG_SM0D1_REG);
> + cmd = SM0CTL1_WRITE;
> + }
> +
> + if ((ret = mtk_i2c_master_cmd(i2c, cmd, page_len)))
> + goto err_timeout;
> +
> + if (pmsg->flags & I2C_M_RD) {
> + data[0] = mtk_i2c_r32(i2c, REG_SM0D0_REG);
> + data[1] = mtk_i2c_r32(i2c, REG_SM0D1_REG);
> + memcpy(&pmsg->buf[j], data, page_len);
> + } else {
> + if (!(pmsg->flags & I2C_M_IGNORE_NAK)) {
> + if ((ret = mtk_i2c_check_ack(i2c, (1 << page_len) - 1)))
> + goto err_ack;
323,327c299,300
< + /* wait */
< + if ((ret = mtk_i2c_wait_tx_done(i2c)))
< + goto err_timeout;
< + j++;
< + } while (j < pmsg->len);
---
> + }
> + j += 8;
329a303,306
> +
> + if ((ret = mtk_i2c_master_stop(i2c)))
> + goto err_timeout;
> +
334a312,316
> +err_ack:
> + if ((ret = mtk_i2c_master_stop(i2c)))
> + goto err_timeout;
> + return -ENXIO;
> +
358,362d339
< +static struct i2c_adapter_quirks mtk_i2c_quirks = {
< + .max_write_len = BYTECNT_MAX,
< + .max_read_len = BYTECNT_MAX,
< +};
< +
365,367c342,346
< + i2c->clk_div = clk_get_rate(i2c->clk) / i2c->cur_clk;
< + if (i2c->clk_div > CLK_DIV_MASK)
< + i2c->clk_div = CLK_DIV_MASK;
---
> + i2c->clk_div = clk_get_rate(i2c->clk) / i2c->cur_clk - 1;
> + if (i2c->clk_div < 99)
> + i2c->clk_div = 99;
> + if (i2c->clk_div > SM0CTL0_CLK_DIV_MAX)
> + i2c->clk_div = SM0CTL0_CLK_DIV_MAX;
419d397
< + adap->quirks = &mtk_i2c_quirks;
432c410
< + dev_info(&pdev->dev, "clock %uKHz, re-start not support\n",
---
> + dev_info(&pdev->dev, "clock %u kHz\n",
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment