i2c: rk3x: Add auto support for rockchip

If the transfer finished, auto stop to end this transfer.

Signed-off-by: David Wu <david.wu@rock-chips.com>
Change-Id: I23cd39e63b8ce292a63c9530edde2c9b72c289cb
This commit is contained in:
David Wu
2020-07-03 11:43:52 +08:00
committed by Tao Huang
parent 3c60ce2725
commit 5cfc724687

View File

@@ -37,6 +37,7 @@
#define REG_IEN 0x18 /* interrupt enable */
#define REG_IPD 0x1c /* interrupt pending */
#define REG_FCNT 0x20 /* finished count */
#define REG_CON1 0x228 /* control register1 */
/* Data buffer offsets */
#define TXBUFFER_BASE 0x100
@@ -64,6 +65,15 @@ enum {
#define REG_CON_STA_CFG(cfg) ((cfg) << 12)
#define REG_CON_STO_CFG(cfg) ((cfg) << 14)
enum {
RK_I2C_VERSION0 = 0,
RK_I2C_VERSION1,
RK_I2C_VERSION5 = 5,
};
#define REG_CON_VERSION GENMASK_ULL(24, 16)
#define REG_CON_VERSION_SHIFT 16
/* REG_MRXADDR bits */
#define REG_MRXADDR_VALID(x) BIT(24 + (x)) /* [x*8+7:x*8] of MRX[R]ADDR valid */
@@ -80,6 +90,9 @@ enum {
/* Disable i2c all irqs */
#define IEN_ALL_DISABLE 0
#define REG_CON1_AUTO_STOP BIT(0)
#define REG_CON1_TRANSFER_AUTO_STOP BIT(1)
/* Constants */
#define WAIT_TIMEOUT 1000 /* ms */
#define DEFAULT_SCL_RATE (100 * 1000) /* Hz */
@@ -206,6 +219,7 @@ struct rk3x_i2c {
struct clk *clk;
struct clk *pclk;
struct notifier_block clk_rate_nb;
bool autostop_supported;
/* Settings */
struct i2c_timings t;
@@ -269,23 +283,55 @@ static inline void rk3x_i2c_disable(struct rk3x_i2c *i2c)
i2c_writel(i2c, val, REG_CON);
}
static bool rk3x_i2c_auto_stop(struct rk3x_i2c *i2c)
{
unsigned int len, con1 = 0;
if (!i2c->autostop_supported)
return false;
if (!i2c->is_last_msg)
goto out;
len = i2c->msg->len - i2c->processed;
if (len > 32)
goto out;
i2c->state = STATE_STOP;
con1 |= REG_CON1_TRANSFER_AUTO_STOP | REG_CON1_AUTO_STOP;
i2c_writel(i2c, con1, REG_CON1);
i2c_writel(i2c, REG_INT_STOP | REG_INT_NAKRCV, REG_IEN);
return true;
out:
return false;
}
/**
* Generate a START condition, which triggers a REG_INT_START interrupt.
*/
static void rk3x_i2c_start(struct rk3x_i2c *i2c)
{
u32 val = i2c_readl(i2c, REG_CON) & REG_CON_TUNING_MASK;
bool auto_stop = rk3x_i2c_auto_stop(i2c);
int length = 0;
/* enable appropriate interrupts */
if (i2c->mode == REG_CON_MOD_TX) {
i2c_writel(i2c, REG_INT_MBTF | REG_INT_NAKRCV, REG_IEN);
i2c->state = STATE_WRITE;
if (!auto_stop) {
i2c_writel(i2c, REG_INT_MBTF | REG_INT_NAKRCV, REG_IEN);
i2c->state = STATE_WRITE;
}
length = rk3x_i2c_fill_transmit_buf(i2c, false);
} else {
/* in any other case, we are going to be reading. */
i2c_writel(i2c, REG_INT_MBRF | REG_INT_NAKRCV, REG_IEN);
i2c->state = STATE_READ;
if (!auto_stop) {
i2c_writel(i2c, REG_INT_MBRF | REG_INT_NAKRCV, REG_IEN);
i2c->state = STATE_READ;
}
}
/* enable adapter with correct mode, send START condition */
@@ -430,6 +476,7 @@ static void rk3x_i2c_handle_write(struct rk3x_i2c *i2c, unsigned int ipd)
/* ack interrupt */
i2c_writel(i2c, REG_INT_MBTF, REG_IPD);
rk3x_i2c_auto_stop(i2c);
/* are we finished? */
if (i2c->processed == i2c->msg->len)
rk3x_i2c_stop(i2c, i2c->error);
@@ -437,20 +484,13 @@ static void rk3x_i2c_handle_write(struct rk3x_i2c *i2c, unsigned int ipd)
rk3x_i2c_fill_transmit_buf(i2c, true);
}
static void rk3x_i2c_handle_read(struct rk3x_i2c *i2c, unsigned int ipd)
static void rk3x_i2c_read(struct rk3x_i2c *i2c)
{
unsigned int i;
unsigned int len = i2c->msg->len - i2c->processed;
u32 val;
u8 byte;
/* we only care for MBRF here. */
if (!(ipd & REG_INT_MBRF))
return;
/* ack interrupt */
i2c_writel(i2c, REG_INT_MBRF, REG_IPD);
/* Can only handle a maximum of 32 bytes at a time */
if (len > 32)
len = 32;
@@ -463,7 +503,21 @@ static void rk3x_i2c_handle_read(struct rk3x_i2c *i2c, unsigned int ipd)
byte = (val >> ((i % 4) * 8)) & 0xff;
i2c->msg->buf[i2c->processed++] = byte;
}
}
static void rk3x_i2c_handle_read(struct rk3x_i2c *i2c, unsigned int ipd)
{
/* we only care for MBRF here. */
if (!(ipd & REG_INT_MBRF))
return;
/* ack interrupt */
i2c_writel(i2c, REG_INT_MBRF, REG_IPD);
/* read the data from receive buffer */
rk3x_i2c_read(i2c);
rk3x_i2c_auto_stop(i2c);
/* are we finished? */
if (i2c->processed == i2c->msg->len)
rk3x_i2c_stop(i2c, i2c->error);
@@ -482,12 +536,24 @@ static void rk3x_i2c_handle_stop(struct rk3x_i2c *i2c, unsigned int ipd)
return;
}
if (i2c->autostop_supported && !i2c->error) {
if (i2c->mode != REG_CON_MOD_TX && i2c->msg) {
if ((i2c->msg->len - i2c->processed) > 0)
rk3x_i2c_read(i2c);
}
i2c->processed = 0;
i2c->msg = NULL;
}
/* ack interrupt */
i2c_writel(i2c, REG_INT_STOP, REG_IPD);
/* disable STOP bit */
con = i2c_readl(i2c, REG_CON);
con &= ~REG_CON_STOP;
if (i2c->autostop_supported)
con &= ~REG_CON_START;
i2c_writel(i2c, con, REG_CON);
i2c->busy = false;
@@ -1062,6 +1128,8 @@ static int rk3x_i2c_setup(struct rk3x_i2c *i2c, struct i2c_msg *msgs, int num)
i2c->error = 0;
rk3x_i2c_clean_ipd(i2c);
if (i2c->autostop_supported)
i2c_writel(i2c, 0, REG_CON1);
return ret;
}
@@ -1205,6 +1273,18 @@ static int rk3x_i2c_restart_notify(struct notifier_block *this,
return NOTIFY_DONE;
}
static unsigned int rk3x_i2c_get_version(struct rk3x_i2c *i2c)
{
unsigned int version;
clk_enable(i2c->pclk);
version = i2c_readl(i2c, REG_CON) & REG_CON_VERSION;
clk_disable(i2c->pclk);
version >>= REG_CON_VERSION_SHIFT;
return version;
}
static __maybe_unused int rk3x_i2c_suspend_noirq(struct device *dev)
{
struct rk3x_i2c *i2c = dev_get_drvdata(dev);
@@ -1452,6 +1532,9 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
clk_rate = clk_get_rate(i2c->clk);
rk3x_i2c_adapt_div(i2c, clk_rate);
if (rk3x_i2c_get_version(i2c) >= RK_I2C_VERSION5)
i2c->autostop_supported = true;
ret = i2c_add_adapter(&i2c->adap);
if (ret < 0)
goto err_clk_notifier;