Created
April 22, 2018 01:32
-
-
Save neheb/427d2d7dea32b53b55b75442d240b9b2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
< 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