i2c: designware: Convert driver to using regmap API
Seeing the DW I2C driver is using flags-based accessors with two conditional clauses it would be better to replace them with the regmap API IO methods and to initialize the regmap object with read/write callbacks specific to the controller registers map implementation. This will be also handy for the drivers with non-standard registers mapping (like an embedded into the Baikal-T1 System Controller DW I2C block, which glue-driver is a part of this series). As before the driver tries to detect the mapping setup at probe stage and creates a regmap object accordingly, which will be used by the rest of the code to correctly access the controller registers. In two places it was appropriate to convert the hand-written read-modify-write and read-poll-loop design patterns to the corresponding regmap API ready-to-use methods. Note the regmap IO methods return value is checked only at the probe stage. The rest of the code won't do this because basically we have MMIO-based regmap so non of the read/write methods can fail (this also won't be needed for the Baikal-T1-specific I2C controller). Suggested-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru> Tested-by: Jarkko Nikula <jarkko.nikula@linux.intel.com> Acked-by: Jarkko Nikula <jarkko.nikula@linux.intel.com> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> [wsa: fix type of 'rx_valid' and remove outdated kdoc var description] Signed-off-by: Wolfram Sang <wsa@kernel.org>
This commit is contained in:
parent
c615f5c65f
commit
0daede80f8
@ -526,6 +526,7 @@ config I2C_DAVINCI
|
|||||||
|
|
||||||
config I2C_DESIGNWARE_CORE
|
config I2C_DESIGNWARE_CORE
|
||||||
tristate
|
tristate
|
||||||
|
select REGMAP
|
||||||
|
|
||||||
config I2C_DESIGNWARE_SLAVE
|
config I2C_DESIGNWARE_SLAVE
|
||||||
bool "Synopsys DesignWare Slave"
|
bool "Synopsys DesignWare Slave"
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
#include <linux/swab.h>
|
#include <linux/swab.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
|
||||||
@ -57,66 +58,122 @@ static char *abort_sources[] = {
|
|||||||
"incorrect slave-transmitter mode configuration",
|
"incorrect slave-transmitter mode configuration",
|
||||||
};
|
};
|
||||||
|
|
||||||
u32 dw_readl(struct dw_i2c_dev *dev, int offset)
|
static int dw_reg_read(void *context, unsigned int reg, unsigned int *val)
|
||||||
{
|
{
|
||||||
u32 value;
|
struct dw_i2c_dev *dev = context;
|
||||||
|
|
||||||
if (dev->flags & ACCESS_16BIT)
|
*val = readl_relaxed(dev->base + reg);
|
||||||
value = readw_relaxed(dev->base + offset) |
|
|
||||||
(readw_relaxed(dev->base + offset + 2) << 16);
|
|
||||||
else
|
|
||||||
value = readl_relaxed(dev->base + offset);
|
|
||||||
|
|
||||||
if (dev->flags & ACCESS_SWAP)
|
return 0;
|
||||||
return swab32(value);
|
|
||||||
else
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void dw_writel(struct dw_i2c_dev *dev, u32 b, int offset)
|
static int dw_reg_write(void *context, unsigned int reg, unsigned int val)
|
||||||
{
|
{
|
||||||
if (dev->flags & ACCESS_SWAP)
|
struct dw_i2c_dev *dev = context;
|
||||||
b = swab32(b);
|
|
||||||
|
|
||||||
if (dev->flags & ACCESS_16BIT) {
|
writel_relaxed(val, dev->base + reg);
|
||||||
writew_relaxed((u16)b, dev->base + offset);
|
|
||||||
writew_relaxed((u16)(b >> 16), dev->base + offset + 2);
|
return 0;
|
||||||
} else {
|
}
|
||||||
writel_relaxed(b, dev->base + offset);
|
|
||||||
}
|
static int dw_reg_read_swab(void *context, unsigned int reg, unsigned int *val)
|
||||||
|
{
|
||||||
|
struct dw_i2c_dev *dev = context;
|
||||||
|
|
||||||
|
*val = swab32(readl_relaxed(dev->base + reg));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dw_reg_write_swab(void *context, unsigned int reg, unsigned int val)
|
||||||
|
{
|
||||||
|
struct dw_i2c_dev *dev = context;
|
||||||
|
|
||||||
|
writel_relaxed(swab32(val), dev->base + reg);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dw_reg_read_word(void *context, unsigned int reg, unsigned int *val)
|
||||||
|
{
|
||||||
|
struct dw_i2c_dev *dev = context;
|
||||||
|
|
||||||
|
*val = readw_relaxed(dev->base + reg) |
|
||||||
|
(readw_relaxed(dev->base + reg + 2) << 16);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dw_reg_write_word(void *context, unsigned int reg, unsigned int val)
|
||||||
|
{
|
||||||
|
struct dw_i2c_dev *dev = context;
|
||||||
|
|
||||||
|
writew_relaxed(val, dev->base + reg);
|
||||||
|
writew_relaxed(val >> 16, dev->base + reg + 2);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* i2c_dw_set_reg_access() - Set register access flags
|
* i2c_dw_init_regmap() - Initialize registers map
|
||||||
* @dev: device private data
|
* @dev: device private data
|
||||||
*
|
*
|
||||||
* Autodetects needed register access mode and sets access flags accordingly.
|
* Autodetects needed register access mode and creates the regmap with
|
||||||
* This must be called before doing any other register access.
|
* corresponding read/write callbacks. This must be called before doing any
|
||||||
|
* other register access.
|
||||||
*/
|
*/
|
||||||
int i2c_dw_set_reg_access(struct dw_i2c_dev *dev)
|
int i2c_dw_init_regmap(struct dw_i2c_dev *dev)
|
||||||
{
|
{
|
||||||
|
struct regmap_config map_cfg = {
|
||||||
|
.reg_bits = 32,
|
||||||
|
.val_bits = 32,
|
||||||
|
.reg_stride = 4,
|
||||||
|
.disable_locking = true,
|
||||||
|
.reg_read = dw_reg_read,
|
||||||
|
.reg_write = dw_reg_write,
|
||||||
|
.max_register = DW_IC_COMP_TYPE,
|
||||||
|
};
|
||||||
u32 reg;
|
u32 reg;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Skip detecting the registers map configuration if the regmap has
|
||||||
|
* already been provided by a higher code.
|
||||||
|
*/
|
||||||
|
if (dev->map)
|
||||||
|
return 0;
|
||||||
|
|
||||||
ret = i2c_dw_acquire_lock(dev);
|
ret = i2c_dw_acquire_lock(dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
reg = dw_readl(dev, DW_IC_COMP_TYPE);
|
reg = readl(dev->base + DW_IC_COMP_TYPE);
|
||||||
i2c_dw_release_lock(dev);
|
i2c_dw_release_lock(dev);
|
||||||
|
|
||||||
if (reg == swab32(DW_IC_COMP_TYPE_VALUE)) {
|
if (reg == swab32(DW_IC_COMP_TYPE_VALUE)) {
|
||||||
/* Configure register endianness access */
|
map_cfg.reg_read = dw_reg_read_swab;
|
||||||
dev->flags |= ACCESS_SWAP;
|
map_cfg.reg_write = dw_reg_write_swab;
|
||||||
} else if (reg == (DW_IC_COMP_TYPE_VALUE & 0x0000ffff)) {
|
} else if (reg == (DW_IC_COMP_TYPE_VALUE & 0x0000ffff)) {
|
||||||
/* Configure register access mode 16bit */
|
map_cfg.reg_read = dw_reg_read_word;
|
||||||
dev->flags |= ACCESS_16BIT;
|
map_cfg.reg_write = dw_reg_write_word;
|
||||||
} else if (reg != DW_IC_COMP_TYPE_VALUE) {
|
} else if (reg != DW_IC_COMP_TYPE_VALUE) {
|
||||||
dev_err(dev->dev,
|
dev_err(dev->dev,
|
||||||
"Unknown Synopsys component type: 0x%08x\n", reg);
|
"Unknown Synopsys component type: 0x%08x\n", reg);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note we'll check the return value of the regmap IO accessors only
|
||||||
|
* at the probe stage. The rest of the code won't do this because
|
||||||
|
* basically we have MMIO-based regmap so non of the read/write methods
|
||||||
|
* can fail.
|
||||||
|
*/
|
||||||
|
dev->map = devm_regmap_init(dev->dev, NULL, dev, &map_cfg);
|
||||||
|
if (IS_ERR(dev->map)) {
|
||||||
|
dev_err(dev->dev, "Failed to init the registers map\n");
|
||||||
|
return PTR_ERR(dev->map);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,11 +384,17 @@ int i2c_dw_set_sda_hold(struct dw_i2c_dev *dev)
|
|||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* Configure SDA Hold Time if required */
|
/* Configure SDA Hold Time if required */
|
||||||
reg = dw_readl(dev, DW_IC_COMP_VERSION);
|
ret = regmap_read(dev->map, DW_IC_COMP_VERSION, ®);
|
||||||
|
if (ret)
|
||||||
|
goto err_release_lock;
|
||||||
|
|
||||||
if (reg >= DW_IC_SDA_HOLD_MIN_VERS) {
|
if (reg >= DW_IC_SDA_HOLD_MIN_VERS) {
|
||||||
if (!dev->sda_hold_time) {
|
if (!dev->sda_hold_time) {
|
||||||
/* Keep previous hold time setting if no one set it */
|
/* Keep previous hold time setting if no one set it */
|
||||||
dev->sda_hold_time = dw_readl(dev, DW_IC_SDA_HOLD);
|
ret = regmap_read(dev->map, DW_IC_SDA_HOLD,
|
||||||
|
&dev->sda_hold_time);
|
||||||
|
if (ret)
|
||||||
|
goto err_release_lock;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -355,14 +418,16 @@ int i2c_dw_set_sda_hold(struct dw_i2c_dev *dev)
|
|||||||
dev->sda_hold_time = 0;
|
dev->sda_hold_time = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err_release_lock:
|
||||||
i2c_dw_release_lock(dev);
|
i2c_dw_release_lock(dev);
|
||||||
|
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void __i2c_dw_disable(struct dw_i2c_dev *dev)
|
void __i2c_dw_disable(struct dw_i2c_dev *dev)
|
||||||
{
|
{
|
||||||
int timeout = 100;
|
int timeout = 100;
|
||||||
|
u32 status;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
__i2c_dw_disable_nowait(dev);
|
__i2c_dw_disable_nowait(dev);
|
||||||
@ -370,7 +435,8 @@ void __i2c_dw_disable(struct dw_i2c_dev *dev)
|
|||||||
* The enable status register may be unimplemented, but
|
* The enable status register may be unimplemented, but
|
||||||
* in that case this test reads zero and exits the loop.
|
* in that case this test reads zero and exits the loop.
|
||||||
*/
|
*/
|
||||||
if ((dw_readl(dev, DW_IC_ENABLE_STATUS) & 1) == 0)
|
regmap_read(dev->map, DW_IC_ENABLE_STATUS, &status);
|
||||||
|
if ((status & 1) == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -449,22 +515,23 @@ void i2c_dw_release_lock(struct dw_i2c_dev *dev)
|
|||||||
*/
|
*/
|
||||||
int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev)
|
int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev)
|
||||||
{
|
{
|
||||||
int timeout = TIMEOUT;
|
u32 status;
|
||||||
|
int ret;
|
||||||
|
|
||||||
while (dw_readl(dev, DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY) {
|
ret = regmap_read_poll_timeout(dev->map, DW_IC_STATUS, status,
|
||||||
if (timeout <= 0) {
|
!(status & DW_IC_STATUS_ACTIVITY),
|
||||||
dev_warn(dev->dev, "timeout waiting for bus ready\n");
|
1100, 20000);
|
||||||
i2c_recover_bus(&dev->adapter);
|
if (ret) {
|
||||||
|
dev_warn(dev->dev, "timeout waiting for bus ready\n");
|
||||||
|
|
||||||
if (dw_readl(dev, DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY)
|
i2c_recover_bus(&dev->adapter);
|
||||||
return -ETIMEDOUT;
|
|
||||||
return 0;
|
regmap_read(dev->map, DW_IC_STATUS, &status);
|
||||||
}
|
if (!(status & DW_IC_STATUS_ACTIVITY))
|
||||||
timeout--;
|
ret = 0;
|
||||||
usleep_range(1000, 1100);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev)
|
int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev)
|
||||||
@ -490,15 +557,19 @@ int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev)
|
|||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
void i2c_dw_set_fifo_size(struct dw_i2c_dev *dev)
|
int i2c_dw_set_fifo_size(struct dw_i2c_dev *dev)
|
||||||
{
|
{
|
||||||
u32 param, tx_fifo_depth, rx_fifo_depth;
|
u32 param, tx_fifo_depth, rx_fifo_depth;
|
||||||
|
int ret;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Try to detect the FIFO depth if not set by interface driver,
|
* Try to detect the FIFO depth if not set by interface driver,
|
||||||
* the depth could be from 2 to 256 from HW spec.
|
* the depth could be from 2 to 256 from HW spec.
|
||||||
*/
|
*/
|
||||||
param = dw_readl(dev, DW_IC_COMP_PARAM_1);
|
ret = regmap_read(dev->map, DW_IC_COMP_PARAM_1, ¶m);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
tx_fifo_depth = ((param >> 16) & 0xff) + 1;
|
tx_fifo_depth = ((param >> 16) & 0xff) + 1;
|
||||||
rx_fifo_depth = ((param >> 8) & 0xff) + 1;
|
rx_fifo_depth = ((param >> 8) & 0xff) + 1;
|
||||||
if (!dev->tx_fifo_depth) {
|
if (!dev->tx_fifo_depth) {
|
||||||
@ -510,6 +581,8 @@ void i2c_dw_set_fifo_size(struct dw_i2c_dev *dev)
|
|||||||
dev->rx_fifo_depth = min_t(u32, dev->rx_fifo_depth,
|
dev->rx_fifo_depth = min_t(u32, dev->rx_fifo_depth,
|
||||||
rx_fifo_depth);
|
rx_fifo_depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 i2c_dw_func(struct i2c_adapter *adap)
|
u32 i2c_dw_func(struct i2c_adapter *adap)
|
||||||
@ -521,17 +594,19 @@ u32 i2c_dw_func(struct i2c_adapter *adap)
|
|||||||
|
|
||||||
void i2c_dw_disable(struct dw_i2c_dev *dev)
|
void i2c_dw_disable(struct dw_i2c_dev *dev)
|
||||||
{
|
{
|
||||||
|
u32 dummy;
|
||||||
|
|
||||||
/* Disable controller */
|
/* Disable controller */
|
||||||
__i2c_dw_disable(dev);
|
__i2c_dw_disable(dev);
|
||||||
|
|
||||||
/* Disable all interrupts */
|
/* Disable all interrupts */
|
||||||
dw_writel(dev, 0, DW_IC_INTR_MASK);
|
regmap_write(dev->map, DW_IC_INTR_MASK, 0);
|
||||||
dw_readl(dev, DW_IC_CLR_INTR);
|
regmap_read(dev->map, DW_IC_CLR_INTR, &dummy);
|
||||||
}
|
}
|
||||||
|
|
||||||
void i2c_dw_disable_int(struct dw_i2c_dev *dev)
|
void i2c_dw_disable_int(struct dw_i2c_dev *dev)
|
||||||
{
|
{
|
||||||
dw_writel(dev, 0, DW_IC_INTR_MASK);
|
regmap_write(dev->map, DW_IC_INTR_MASK, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter core");
|
MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter core");
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include <linux/dev_printk.h>
|
#include <linux/dev_printk.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
|
||||||
#define DW_IC_DEFAULT_FUNCTIONALITY (I2C_FUNC_I2C | \
|
#define DW_IC_DEFAULT_FUNCTIONALITY (I2C_FUNC_I2C | \
|
||||||
@ -126,8 +127,6 @@
|
|||||||
#define STATUS_WRITE_IN_PROGRESS 0x1
|
#define STATUS_WRITE_IN_PROGRESS 0x1
|
||||||
#define STATUS_READ_IN_PROGRESS 0x2
|
#define STATUS_READ_IN_PROGRESS 0x2
|
||||||
|
|
||||||
#define TIMEOUT 20 /* ms */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* operation modes
|
* operation modes
|
||||||
*/
|
*/
|
||||||
@ -183,7 +182,9 @@ struct reset_control;
|
|||||||
/**
|
/**
|
||||||
* struct dw_i2c_dev - private i2c-designware data
|
* struct dw_i2c_dev - private i2c-designware data
|
||||||
* @dev: driver model device node
|
* @dev: driver model device node
|
||||||
|
* @map: IO registers map
|
||||||
* @base: IO registers pointer
|
* @base: IO registers pointer
|
||||||
|
* @ext: Extended IO registers pointer
|
||||||
* @cmd_complete: tx completion indicator
|
* @cmd_complete: tx completion indicator
|
||||||
* @clk: input reference clock
|
* @clk: input reference clock
|
||||||
* @pclk: clock required to access the registers
|
* @pclk: clock required to access the registers
|
||||||
@ -233,6 +234,7 @@ struct reset_control;
|
|||||||
*/
|
*/
|
||||||
struct dw_i2c_dev {
|
struct dw_i2c_dev {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
|
struct regmap *map;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
void __iomem *ext;
|
void __iomem *ext;
|
||||||
struct completion cmd_complete;
|
struct completion cmd_complete;
|
||||||
@ -284,17 +286,13 @@ struct dw_i2c_dev {
|
|||||||
bool suspended;
|
bool suspended;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define ACCESS_SWAP 0x00000001
|
#define ACCESS_INTR_MASK 0x00000001
|
||||||
#define ACCESS_16BIT 0x00000002
|
#define ACCESS_NO_IRQ_SUSPEND 0x00000002
|
||||||
#define ACCESS_INTR_MASK 0x00000004
|
|
||||||
#define ACCESS_NO_IRQ_SUSPEND 0x00000008
|
|
||||||
|
|
||||||
#define MODEL_MSCC_OCELOT 0x00000100
|
#define MODEL_MSCC_OCELOT 0x00000100
|
||||||
#define MODEL_MASK 0x00000f00
|
#define MODEL_MASK 0x00000f00
|
||||||
|
|
||||||
u32 dw_readl(struct dw_i2c_dev *dev, int offset);
|
int i2c_dw_init_regmap(struct dw_i2c_dev *dev);
|
||||||
void dw_writel(struct dw_i2c_dev *dev, u32 b, int offset);
|
|
||||||
int i2c_dw_set_reg_access(struct dw_i2c_dev *dev);
|
|
||||||
u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset);
|
u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset);
|
||||||
u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset);
|
u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset);
|
||||||
int i2c_dw_set_sda_hold(struct dw_i2c_dev *dev);
|
int i2c_dw_set_sda_hold(struct dw_i2c_dev *dev);
|
||||||
@ -304,19 +302,19 @@ int i2c_dw_acquire_lock(struct dw_i2c_dev *dev);
|
|||||||
void i2c_dw_release_lock(struct dw_i2c_dev *dev);
|
void i2c_dw_release_lock(struct dw_i2c_dev *dev);
|
||||||
int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev);
|
int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev);
|
||||||
int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev);
|
int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev);
|
||||||
void i2c_dw_set_fifo_size(struct dw_i2c_dev *dev);
|
int i2c_dw_set_fifo_size(struct dw_i2c_dev *dev);
|
||||||
u32 i2c_dw_func(struct i2c_adapter *adap);
|
u32 i2c_dw_func(struct i2c_adapter *adap);
|
||||||
void i2c_dw_disable(struct dw_i2c_dev *dev);
|
void i2c_dw_disable(struct dw_i2c_dev *dev);
|
||||||
void i2c_dw_disable_int(struct dw_i2c_dev *dev);
|
void i2c_dw_disable_int(struct dw_i2c_dev *dev);
|
||||||
|
|
||||||
static inline void __i2c_dw_enable(struct dw_i2c_dev *dev)
|
static inline void __i2c_dw_enable(struct dw_i2c_dev *dev)
|
||||||
{
|
{
|
||||||
dw_writel(dev, 1, DW_IC_ENABLE);
|
regmap_write(dev->map, DW_IC_ENABLE, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void __i2c_dw_disable_nowait(struct dw_i2c_dev *dev)
|
static inline void __i2c_dw_disable_nowait(struct dw_i2c_dev *dev)
|
||||||
{
|
{
|
||||||
dw_writel(dev, 0, DW_IC_ENABLE);
|
regmap_write(dev->map, DW_IC_ENABLE, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void __i2c_dw_disable(struct dw_i2c_dev *dev);
|
void __i2c_dw_disable(struct dw_i2c_dev *dev);
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
#include <linux/reset.h>
|
#include <linux/reset.h>
|
||||||
|
|
||||||
#include "i2c-designware-core.h"
|
#include "i2c-designware-core.h"
|
||||||
@ -25,11 +26,11 @@
|
|||||||
static void i2c_dw_configure_fifo_master(struct dw_i2c_dev *dev)
|
static void i2c_dw_configure_fifo_master(struct dw_i2c_dev *dev)
|
||||||
{
|
{
|
||||||
/* Configure Tx/Rx FIFO threshold levels */
|
/* Configure Tx/Rx FIFO threshold levels */
|
||||||
dw_writel(dev, dev->tx_fifo_depth / 2, DW_IC_TX_TL);
|
regmap_write(dev->map, DW_IC_TX_TL, dev->tx_fifo_depth / 2);
|
||||||
dw_writel(dev, 0, DW_IC_RX_TL);
|
regmap_write(dev->map, DW_IC_RX_TL, 0);
|
||||||
|
|
||||||
/* Configure the I2C master */
|
/* Configure the I2C master */
|
||||||
dw_writel(dev, dev->master_cfg, DW_IC_CON);
|
regmap_write(dev->map, DW_IC_CON, dev->master_cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
|
static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
|
||||||
@ -44,8 +45,11 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
|
|||||||
ret = i2c_dw_acquire_lock(dev);
|
ret = i2c_dw_acquire_lock(dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
comp_param1 = dw_readl(dev, DW_IC_COMP_PARAM_1);
|
|
||||||
|
ret = regmap_read(dev->map, DW_IC_COMP_PARAM_1, &comp_param1);
|
||||||
i2c_dw_release_lock(dev);
|
i2c_dw_release_lock(dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
/* Set standard and fast speed dividers for high/low periods */
|
/* Set standard and fast speed dividers for high/low periods */
|
||||||
sda_falling_time = t->sda_fall_ns ?: 300; /* ns */
|
sda_falling_time = t->sda_fall_ns ?: 300; /* ns */
|
||||||
@ -187,22 +191,22 @@ static int i2c_dw_init_master(struct dw_i2c_dev *dev)
|
|||||||
__i2c_dw_disable(dev);
|
__i2c_dw_disable(dev);
|
||||||
|
|
||||||
/* Write standard speed timing parameters */
|
/* Write standard speed timing parameters */
|
||||||
dw_writel(dev, dev->ss_hcnt, DW_IC_SS_SCL_HCNT);
|
regmap_write(dev->map, DW_IC_SS_SCL_HCNT, dev->ss_hcnt);
|
||||||
dw_writel(dev, dev->ss_lcnt, DW_IC_SS_SCL_LCNT);
|
regmap_write(dev->map, DW_IC_SS_SCL_LCNT, dev->ss_lcnt);
|
||||||
|
|
||||||
/* Write fast mode/fast mode plus timing parameters */
|
/* Write fast mode/fast mode plus timing parameters */
|
||||||
dw_writel(dev, dev->fs_hcnt, DW_IC_FS_SCL_HCNT);
|
regmap_write(dev->map, DW_IC_FS_SCL_HCNT, dev->fs_hcnt);
|
||||||
dw_writel(dev, dev->fs_lcnt, DW_IC_FS_SCL_LCNT);
|
regmap_write(dev->map, DW_IC_FS_SCL_LCNT, dev->fs_lcnt);
|
||||||
|
|
||||||
/* Write high speed timing parameters if supported */
|
/* Write high speed timing parameters if supported */
|
||||||
if (dev->hs_hcnt && dev->hs_lcnt) {
|
if (dev->hs_hcnt && dev->hs_lcnt) {
|
||||||
dw_writel(dev, dev->hs_hcnt, DW_IC_HS_SCL_HCNT);
|
regmap_write(dev->map, DW_IC_HS_SCL_HCNT, dev->hs_hcnt);
|
||||||
dw_writel(dev, dev->hs_lcnt, DW_IC_HS_SCL_LCNT);
|
regmap_write(dev->map, DW_IC_HS_SCL_LCNT, dev->hs_lcnt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Write SDA hold time if supported */
|
/* Write SDA hold time if supported */
|
||||||
if (dev->sda_hold_time)
|
if (dev->sda_hold_time)
|
||||||
dw_writel(dev, dev->sda_hold_time, DW_IC_SDA_HOLD);
|
regmap_write(dev->map, DW_IC_SDA_HOLD, dev->sda_hold_time);
|
||||||
|
|
||||||
i2c_dw_configure_fifo_master(dev);
|
i2c_dw_configure_fifo_master(dev);
|
||||||
i2c_dw_release_lock(dev);
|
i2c_dw_release_lock(dev);
|
||||||
@ -213,15 +217,15 @@ static int i2c_dw_init_master(struct dw_i2c_dev *dev)
|
|||||||
static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
|
static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
|
||||||
{
|
{
|
||||||
struct i2c_msg *msgs = dev->msgs;
|
struct i2c_msg *msgs = dev->msgs;
|
||||||
u32 ic_con, ic_tar = 0;
|
u32 ic_con = 0, ic_tar = 0;
|
||||||
|
u32 dummy;
|
||||||
|
|
||||||
/* Disable the adapter */
|
/* Disable the adapter */
|
||||||
__i2c_dw_disable(dev);
|
__i2c_dw_disable(dev);
|
||||||
|
|
||||||
/* If the slave address is ten bit address, enable 10BITADDR */
|
/* If the slave address is ten bit address, enable 10BITADDR */
|
||||||
ic_con = dw_readl(dev, DW_IC_CON);
|
|
||||||
if (msgs[dev->msg_write_idx].flags & I2C_M_TEN) {
|
if (msgs[dev->msg_write_idx].flags & I2C_M_TEN) {
|
||||||
ic_con |= DW_IC_CON_10BITADDR_MASTER;
|
ic_con = DW_IC_CON_10BITADDR_MASTER;
|
||||||
/*
|
/*
|
||||||
* If I2C_DYNAMIC_TAR_UPDATE is set, the 10-bit addressing
|
* If I2C_DYNAMIC_TAR_UPDATE is set, the 10-bit addressing
|
||||||
* mode has to be enabled via bit 12 of IC_TAR register.
|
* mode has to be enabled via bit 12 of IC_TAR register.
|
||||||
@ -229,17 +233,17 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
|
|||||||
* detected from registers.
|
* detected from registers.
|
||||||
*/
|
*/
|
||||||
ic_tar = DW_IC_TAR_10BITADDR_MASTER;
|
ic_tar = DW_IC_TAR_10BITADDR_MASTER;
|
||||||
} else {
|
|
||||||
ic_con &= ~DW_IC_CON_10BITADDR_MASTER;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dw_writel(dev, ic_con, DW_IC_CON);
|
regmap_update_bits(dev->map, DW_IC_CON, DW_IC_CON_10BITADDR_MASTER,
|
||||||
|
ic_con);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set the slave (target) address and enable 10-bit addressing mode
|
* Set the slave (target) address and enable 10-bit addressing mode
|
||||||
* if applicable.
|
* if applicable.
|
||||||
*/
|
*/
|
||||||
dw_writel(dev, msgs[dev->msg_write_idx].addr | ic_tar, DW_IC_TAR);
|
regmap_write(dev->map, DW_IC_TAR,
|
||||||
|
msgs[dev->msg_write_idx].addr | ic_tar);
|
||||||
|
|
||||||
/* Enforce disabled interrupts (due to HW issues) */
|
/* Enforce disabled interrupts (due to HW issues) */
|
||||||
i2c_dw_disable_int(dev);
|
i2c_dw_disable_int(dev);
|
||||||
@ -248,11 +252,11 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
|
|||||||
__i2c_dw_enable(dev);
|
__i2c_dw_enable(dev);
|
||||||
|
|
||||||
/* Dummy read to avoid the register getting stuck on Bay Trail */
|
/* Dummy read to avoid the register getting stuck on Bay Trail */
|
||||||
dw_readl(dev, DW_IC_ENABLE_STATUS);
|
regmap_read(dev->map, DW_IC_ENABLE_STATUS, &dummy);
|
||||||
|
|
||||||
/* Clear and enable interrupts */
|
/* Clear and enable interrupts */
|
||||||
dw_readl(dev, DW_IC_CLR_INTR);
|
regmap_read(dev->map, DW_IC_CLR_INTR, &dummy);
|
||||||
dw_writel(dev, DW_IC_INTR_MASTER_MASK, DW_IC_INTR_MASK);
|
regmap_write(dev->map, DW_IC_INTR_MASK, DW_IC_INTR_MASTER_MASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -271,6 +275,7 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
|
|||||||
u32 buf_len = dev->tx_buf_len;
|
u32 buf_len = dev->tx_buf_len;
|
||||||
u8 *buf = dev->tx_buf;
|
u8 *buf = dev->tx_buf;
|
||||||
bool need_restart = false;
|
bool need_restart = false;
|
||||||
|
unsigned int flr;
|
||||||
|
|
||||||
intr_mask = DW_IC_INTR_MASTER_MASK;
|
intr_mask = DW_IC_INTR_MASTER_MASK;
|
||||||
|
|
||||||
@ -303,8 +308,11 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
|
|||||||
need_restart = true;
|
need_restart = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
tx_limit = dev->tx_fifo_depth - dw_readl(dev, DW_IC_TXFLR);
|
regmap_read(dev->map, DW_IC_TXFLR, &flr);
|
||||||
rx_limit = dev->rx_fifo_depth - dw_readl(dev, DW_IC_RXFLR);
|
tx_limit = dev->tx_fifo_depth - flr;
|
||||||
|
|
||||||
|
regmap_read(dev->map, DW_IC_RXFLR, &flr);
|
||||||
|
rx_limit = dev->rx_fifo_depth - flr;
|
||||||
|
|
||||||
while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) {
|
while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) {
|
||||||
u32 cmd = 0;
|
u32 cmd = 0;
|
||||||
@ -337,11 +345,14 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
|
|||||||
if (dev->rx_outstanding >= dev->rx_fifo_depth)
|
if (dev->rx_outstanding >= dev->rx_fifo_depth)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
dw_writel(dev, cmd | 0x100, DW_IC_DATA_CMD);
|
regmap_write(dev->map, DW_IC_DATA_CMD,
|
||||||
|
cmd | 0x100);
|
||||||
rx_limit--;
|
rx_limit--;
|
||||||
dev->rx_outstanding++;
|
dev->rx_outstanding++;
|
||||||
} else
|
} else {
|
||||||
dw_writel(dev, cmd | *buf++, DW_IC_DATA_CMD);
|
regmap_write(dev->map, DW_IC_DATA_CMD,
|
||||||
|
cmd | *buf++);
|
||||||
|
}
|
||||||
tx_limit--; buf_len--;
|
tx_limit--; buf_len--;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -371,7 +382,7 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
|
|||||||
if (dev->msg_err)
|
if (dev->msg_err)
|
||||||
intr_mask = 0;
|
intr_mask = 0;
|
||||||
|
|
||||||
dw_writel(dev, intr_mask, DW_IC_INTR_MASK);
|
regmap_write(dev->map, DW_IC_INTR_MASK, intr_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
static u8
|
static u8
|
||||||
@ -396,10 +407,10 @@ static void
|
|||||||
i2c_dw_read(struct dw_i2c_dev *dev)
|
i2c_dw_read(struct dw_i2c_dev *dev)
|
||||||
{
|
{
|
||||||
struct i2c_msg *msgs = dev->msgs;
|
struct i2c_msg *msgs = dev->msgs;
|
||||||
int rx_valid;
|
unsigned int rx_valid;
|
||||||
|
|
||||||
for (; dev->msg_read_idx < dev->msgs_num; dev->msg_read_idx++) {
|
for (; dev->msg_read_idx < dev->msgs_num; dev->msg_read_idx++) {
|
||||||
u32 len;
|
u32 len, tmp;
|
||||||
u8 *buf;
|
u8 *buf;
|
||||||
|
|
||||||
if (!(msgs[dev->msg_read_idx].flags & I2C_M_RD))
|
if (!(msgs[dev->msg_read_idx].flags & I2C_M_RD))
|
||||||
@ -413,18 +424,18 @@ i2c_dw_read(struct dw_i2c_dev *dev)
|
|||||||
buf = dev->rx_buf;
|
buf = dev->rx_buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
rx_valid = dw_readl(dev, DW_IC_RXFLR);
|
regmap_read(dev->map, DW_IC_RXFLR, &rx_valid);
|
||||||
|
|
||||||
for (; len > 0 && rx_valid > 0; len--, rx_valid--) {
|
for (; len > 0 && rx_valid > 0; len--, rx_valid--) {
|
||||||
u32 flags = msgs[dev->msg_read_idx].flags;
|
u32 flags = msgs[dev->msg_read_idx].flags;
|
||||||
|
|
||||||
*buf = dw_readl(dev, DW_IC_DATA_CMD);
|
regmap_read(dev->map, DW_IC_DATA_CMD, &tmp);
|
||||||
/* Ensure length byte is a valid value */
|
/* Ensure length byte is a valid value */
|
||||||
if (flags & I2C_M_RECV_LEN &&
|
if (flags & I2C_M_RECV_LEN &&
|
||||||
*buf <= I2C_SMBUS_BLOCK_MAX && *buf > 0) {
|
tmp <= I2C_SMBUS_BLOCK_MAX && tmp > 0) {
|
||||||
len = i2c_dw_recv_len(dev, *buf);
|
len = i2c_dw_recv_len(dev, tmp);
|
||||||
}
|
}
|
||||||
buf++;
|
*buf++ = tmp;
|
||||||
dev->rx_outstanding--;
|
dev->rx_outstanding--;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -542,7 +553,7 @@ static const struct i2c_adapter_quirks i2c_dw_quirks = {
|
|||||||
|
|
||||||
static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
|
static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
|
||||||
{
|
{
|
||||||
u32 stat;
|
u32 stat, dummy;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The IC_INTR_STAT register just indicates "enabled" interrupts.
|
* The IC_INTR_STAT register just indicates "enabled" interrupts.
|
||||||
@ -550,47 +561,47 @@ static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
|
|||||||
* in the IC_RAW_INTR_STAT register.
|
* in the IC_RAW_INTR_STAT register.
|
||||||
*
|
*
|
||||||
* That is,
|
* That is,
|
||||||
* stat = dw_readl(IC_INTR_STAT);
|
* stat = readl(IC_INTR_STAT);
|
||||||
* equals to,
|
* equals to,
|
||||||
* stat = dw_readl(IC_RAW_INTR_STAT) & dw_readl(IC_INTR_MASK);
|
* stat = readl(IC_RAW_INTR_STAT) & readl(IC_INTR_MASK);
|
||||||
*
|
*
|
||||||
* The raw version might be useful for debugging purposes.
|
* The raw version might be useful for debugging purposes.
|
||||||
*/
|
*/
|
||||||
stat = dw_readl(dev, DW_IC_INTR_STAT);
|
regmap_read(dev->map, DW_IC_INTR_STAT, &stat);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Do not use the IC_CLR_INTR register to clear interrupts, or
|
* Do not use the IC_CLR_INTR register to clear interrupts, or
|
||||||
* you'll miss some interrupts, triggered during the period from
|
* you'll miss some interrupts, triggered during the period from
|
||||||
* dw_readl(IC_INTR_STAT) to dw_readl(IC_CLR_INTR).
|
* readl(IC_INTR_STAT) to readl(IC_CLR_INTR).
|
||||||
*
|
*
|
||||||
* Instead, use the separately-prepared IC_CLR_* registers.
|
* Instead, use the separately-prepared IC_CLR_* registers.
|
||||||
*/
|
*/
|
||||||
if (stat & DW_IC_INTR_RX_UNDER)
|
if (stat & DW_IC_INTR_RX_UNDER)
|
||||||
dw_readl(dev, DW_IC_CLR_RX_UNDER);
|
regmap_read(dev->map, DW_IC_CLR_RX_UNDER, &dummy);
|
||||||
if (stat & DW_IC_INTR_RX_OVER)
|
if (stat & DW_IC_INTR_RX_OVER)
|
||||||
dw_readl(dev, DW_IC_CLR_RX_OVER);
|
regmap_read(dev->map, DW_IC_CLR_RX_OVER, &dummy);
|
||||||
if (stat & DW_IC_INTR_TX_OVER)
|
if (stat & DW_IC_INTR_TX_OVER)
|
||||||
dw_readl(dev, DW_IC_CLR_TX_OVER);
|
regmap_read(dev->map, DW_IC_CLR_TX_OVER, &dummy);
|
||||||
if (stat & DW_IC_INTR_RD_REQ)
|
if (stat & DW_IC_INTR_RD_REQ)
|
||||||
dw_readl(dev, DW_IC_CLR_RD_REQ);
|
regmap_read(dev->map, DW_IC_CLR_RD_REQ, &dummy);
|
||||||
if (stat & DW_IC_INTR_TX_ABRT) {
|
if (stat & DW_IC_INTR_TX_ABRT) {
|
||||||
/*
|
/*
|
||||||
* The IC_TX_ABRT_SOURCE register is cleared whenever
|
* The IC_TX_ABRT_SOURCE register is cleared whenever
|
||||||
* the IC_CLR_TX_ABRT is read. Preserve it beforehand.
|
* the IC_CLR_TX_ABRT is read. Preserve it beforehand.
|
||||||
*/
|
*/
|
||||||
dev->abort_source = dw_readl(dev, DW_IC_TX_ABRT_SOURCE);
|
regmap_read(dev->map, DW_IC_TX_ABRT_SOURCE, &dev->abort_source);
|
||||||
dw_readl(dev, DW_IC_CLR_TX_ABRT);
|
regmap_read(dev->map, DW_IC_CLR_TX_ABRT, &dummy);
|
||||||
}
|
}
|
||||||
if (stat & DW_IC_INTR_RX_DONE)
|
if (stat & DW_IC_INTR_RX_DONE)
|
||||||
dw_readl(dev, DW_IC_CLR_RX_DONE);
|
regmap_read(dev->map, DW_IC_CLR_RX_DONE, &dummy);
|
||||||
if (stat & DW_IC_INTR_ACTIVITY)
|
if (stat & DW_IC_INTR_ACTIVITY)
|
||||||
dw_readl(dev, DW_IC_CLR_ACTIVITY);
|
regmap_read(dev->map, DW_IC_CLR_ACTIVITY, &dummy);
|
||||||
if (stat & DW_IC_INTR_STOP_DET)
|
if (stat & DW_IC_INTR_STOP_DET)
|
||||||
dw_readl(dev, DW_IC_CLR_STOP_DET);
|
regmap_read(dev->map, DW_IC_CLR_STOP_DET, &dummy);
|
||||||
if (stat & DW_IC_INTR_START_DET)
|
if (stat & DW_IC_INTR_START_DET)
|
||||||
dw_readl(dev, DW_IC_CLR_START_DET);
|
regmap_read(dev->map, DW_IC_CLR_START_DET, &dummy);
|
||||||
if (stat & DW_IC_INTR_GEN_CALL)
|
if (stat & DW_IC_INTR_GEN_CALL)
|
||||||
dw_readl(dev, DW_IC_CLR_GEN_CALL);
|
regmap_read(dev->map, DW_IC_CLR_GEN_CALL, &dummy);
|
||||||
|
|
||||||
return stat;
|
return stat;
|
||||||
}
|
}
|
||||||
@ -612,7 +623,7 @@ static int i2c_dw_irq_handler_master(struct dw_i2c_dev *dev)
|
|||||||
* Anytime TX_ABRT is set, the contents of the tx/rx
|
* Anytime TX_ABRT is set, the contents of the tx/rx
|
||||||
* buffers are flushed. Make sure to skip them.
|
* buffers are flushed. Make sure to skip them.
|
||||||
*/
|
*/
|
||||||
dw_writel(dev, 0, DW_IC_INTR_MASK);
|
regmap_write(dev->map, DW_IC_INTR_MASK, 0);
|
||||||
goto tx_aborted;
|
goto tx_aborted;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -633,9 +644,9 @@ tx_aborted:
|
|||||||
complete(&dev->cmd_complete);
|
complete(&dev->cmd_complete);
|
||||||
else if (unlikely(dev->flags & ACCESS_INTR_MASK)) {
|
else if (unlikely(dev->flags & ACCESS_INTR_MASK)) {
|
||||||
/* Workaround to trigger pending interrupt */
|
/* Workaround to trigger pending interrupt */
|
||||||
stat = dw_readl(dev, DW_IC_INTR_MASK);
|
regmap_read(dev->map, DW_IC_INTR_MASK, &stat);
|
||||||
i2c_dw_disable_int(dev);
|
i2c_dw_disable_int(dev);
|
||||||
dw_writel(dev, stat, DW_IC_INTR_MASK);
|
regmap_write(dev->map, DW_IC_INTR_MASK, stat);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -646,8 +657,8 @@ static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
|
|||||||
struct dw_i2c_dev *dev = dev_id;
|
struct dw_i2c_dev *dev = dev_id;
|
||||||
u32 stat, enabled;
|
u32 stat, enabled;
|
||||||
|
|
||||||
enabled = dw_readl(dev, DW_IC_ENABLE);
|
regmap_read(dev->map, DW_IC_ENABLE, &enabled);
|
||||||
stat = dw_readl(dev, DW_IC_RAW_INTR_STAT);
|
regmap_read(dev->map, DW_IC_RAW_INTR_STAT, &stat);
|
||||||
dev_dbg(dev->dev, "enabled=%#x stat=%#x\n", enabled, stat);
|
dev_dbg(dev->dev, "enabled=%#x stat=%#x\n", enabled, stat);
|
||||||
if (!enabled || !(stat & ~DW_IC_INTR_ACTIVITY))
|
if (!enabled || !(stat & ~DW_IC_INTR_ACTIVITY))
|
||||||
return IRQ_NONE;
|
return IRQ_NONE;
|
||||||
@ -739,7 +750,7 @@ int i2c_dw_probe_master(struct dw_i2c_dev *dev)
|
|||||||
dev->disable = i2c_dw_disable;
|
dev->disable = i2c_dw_disable;
|
||||||
dev->disable_int = i2c_dw_disable_int;
|
dev->disable_int = i2c_dw_disable_int;
|
||||||
|
|
||||||
ret = i2c_dw_set_reg_access(dev);
|
ret = i2c_dw_init_regmap(dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -747,7 +758,9 @@ int i2c_dw_probe_master(struct dw_i2c_dev *dev)
|
|||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
i2c_dw_set_fifo_size(dev);
|
ret = i2c_dw_set_fifo_size(dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
ret = dev->init(dev);
|
ret = dev->init(dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -14,18 +14,19 @@
|
|||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
|
||||||
#include "i2c-designware-core.h"
|
#include "i2c-designware-core.h"
|
||||||
|
|
||||||
static void i2c_dw_configure_fifo_slave(struct dw_i2c_dev *dev)
|
static void i2c_dw_configure_fifo_slave(struct dw_i2c_dev *dev)
|
||||||
{
|
{
|
||||||
/* Configure Tx/Rx FIFO threshold levels. */
|
/* Configure Tx/Rx FIFO threshold levels. */
|
||||||
dw_writel(dev, 0, DW_IC_TX_TL);
|
regmap_write(dev->map, DW_IC_TX_TL, 0);
|
||||||
dw_writel(dev, 0, DW_IC_RX_TL);
|
regmap_write(dev->map, DW_IC_RX_TL, 0);
|
||||||
|
|
||||||
/* Configure the I2C slave. */
|
/* Configure the I2C slave. */
|
||||||
dw_writel(dev, dev->slave_cfg, DW_IC_CON);
|
regmap_write(dev->map, DW_IC_CON, dev->slave_cfg);
|
||||||
dw_writel(dev, DW_IC_INTR_SLAVE_MASK, DW_IC_INTR_MASK);
|
regmap_write(dev->map, DW_IC_INTR_MASK, DW_IC_INTR_SLAVE_MASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -49,7 +50,7 @@ static int i2c_dw_init_slave(struct dw_i2c_dev *dev)
|
|||||||
|
|
||||||
/* Write SDA hold time if supported */
|
/* Write SDA hold time if supported */
|
||||||
if (dev->sda_hold_time)
|
if (dev->sda_hold_time)
|
||||||
dw_writel(dev, dev->sda_hold_time, DW_IC_SDA_HOLD);
|
regmap_write(dev->map, DW_IC_SDA_HOLD, dev->sda_hold_time);
|
||||||
|
|
||||||
i2c_dw_configure_fifo_slave(dev);
|
i2c_dw_configure_fifo_slave(dev);
|
||||||
i2c_dw_release_lock(dev);
|
i2c_dw_release_lock(dev);
|
||||||
@ -72,7 +73,7 @@ static int i2c_dw_reg_slave(struct i2c_client *slave)
|
|||||||
* the address to which the DW_apb_i2c responds.
|
* the address to which the DW_apb_i2c responds.
|
||||||
*/
|
*/
|
||||||
__i2c_dw_disable_nowait(dev);
|
__i2c_dw_disable_nowait(dev);
|
||||||
dw_writel(dev, slave->addr, DW_IC_SAR);
|
regmap_write(dev->map, DW_IC_SAR, slave->addr);
|
||||||
dev->slave = slave;
|
dev->slave = slave;
|
||||||
|
|
||||||
__i2c_dw_enable(dev);
|
__i2c_dw_enable(dev);
|
||||||
@ -103,7 +104,7 @@ static int i2c_dw_unreg_slave(struct i2c_client *slave)
|
|||||||
|
|
||||||
static u32 i2c_dw_read_clear_intrbits_slave(struct dw_i2c_dev *dev)
|
static u32 i2c_dw_read_clear_intrbits_slave(struct dw_i2c_dev *dev)
|
||||||
{
|
{
|
||||||
u32 stat;
|
u32 stat, dummy;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The IC_INTR_STAT register just indicates "enabled" interrupts.
|
* The IC_INTR_STAT register just indicates "enabled" interrupts.
|
||||||
@ -111,39 +112,39 @@ static u32 i2c_dw_read_clear_intrbits_slave(struct dw_i2c_dev *dev)
|
|||||||
* in the IC_RAW_INTR_STAT register.
|
* in the IC_RAW_INTR_STAT register.
|
||||||
*
|
*
|
||||||
* That is,
|
* That is,
|
||||||
* stat = dw_readl(IC_INTR_STAT);
|
* stat = readl(IC_INTR_STAT);
|
||||||
* equals to,
|
* equals to,
|
||||||
* stat = dw_readl(IC_RAW_INTR_STAT) & dw_readl(IC_INTR_MASK);
|
* stat = readl(IC_RAW_INTR_STAT) & readl(IC_INTR_MASK);
|
||||||
*
|
*
|
||||||
* The raw version might be useful for debugging purposes.
|
* The raw version might be useful for debugging purposes.
|
||||||
*/
|
*/
|
||||||
stat = dw_readl(dev, DW_IC_INTR_STAT);
|
regmap_read(dev->map, DW_IC_INTR_STAT, &stat);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Do not use the IC_CLR_INTR register to clear interrupts, or
|
* Do not use the IC_CLR_INTR register to clear interrupts, or
|
||||||
* you'll miss some interrupts, triggered during the period from
|
* you'll miss some interrupts, triggered during the period from
|
||||||
* dw_readl(IC_INTR_STAT) to dw_readl(IC_CLR_INTR).
|
* readl(IC_INTR_STAT) to readl(IC_CLR_INTR).
|
||||||
*
|
*
|
||||||
* Instead, use the separately-prepared IC_CLR_* registers.
|
* Instead, use the separately-prepared IC_CLR_* registers.
|
||||||
*/
|
*/
|
||||||
if (stat & DW_IC_INTR_TX_ABRT)
|
if (stat & DW_IC_INTR_TX_ABRT)
|
||||||
dw_readl(dev, DW_IC_CLR_TX_ABRT);
|
regmap_read(dev->map, DW_IC_CLR_TX_ABRT, &dummy);
|
||||||
if (stat & DW_IC_INTR_RX_UNDER)
|
if (stat & DW_IC_INTR_RX_UNDER)
|
||||||
dw_readl(dev, DW_IC_CLR_RX_UNDER);
|
regmap_read(dev->map, DW_IC_CLR_RX_UNDER, &dummy);
|
||||||
if (stat & DW_IC_INTR_RX_OVER)
|
if (stat & DW_IC_INTR_RX_OVER)
|
||||||
dw_readl(dev, DW_IC_CLR_RX_OVER);
|
regmap_read(dev->map, DW_IC_CLR_RX_OVER, &dummy);
|
||||||
if (stat & DW_IC_INTR_TX_OVER)
|
if (stat & DW_IC_INTR_TX_OVER)
|
||||||
dw_readl(dev, DW_IC_CLR_TX_OVER);
|
regmap_read(dev->map, DW_IC_CLR_TX_OVER, &dummy);
|
||||||
if (stat & DW_IC_INTR_RX_DONE)
|
if (stat & DW_IC_INTR_RX_DONE)
|
||||||
dw_readl(dev, DW_IC_CLR_RX_DONE);
|
regmap_read(dev->map, DW_IC_CLR_RX_DONE, &dummy);
|
||||||
if (stat & DW_IC_INTR_ACTIVITY)
|
if (stat & DW_IC_INTR_ACTIVITY)
|
||||||
dw_readl(dev, DW_IC_CLR_ACTIVITY);
|
regmap_read(dev->map, DW_IC_CLR_ACTIVITY, &dummy);
|
||||||
if (stat & DW_IC_INTR_STOP_DET)
|
if (stat & DW_IC_INTR_STOP_DET)
|
||||||
dw_readl(dev, DW_IC_CLR_STOP_DET);
|
regmap_read(dev->map, DW_IC_CLR_STOP_DET, &dummy);
|
||||||
if (stat & DW_IC_INTR_START_DET)
|
if (stat & DW_IC_INTR_START_DET)
|
||||||
dw_readl(dev, DW_IC_CLR_START_DET);
|
regmap_read(dev->map, DW_IC_CLR_START_DET, &dummy);
|
||||||
if (stat & DW_IC_INTR_GEN_CALL)
|
if (stat & DW_IC_INTR_GEN_CALL)
|
||||||
dw_readl(dev, DW_IC_CLR_GEN_CALL);
|
regmap_read(dev->map, DW_IC_CLR_GEN_CALL, &dummy);
|
||||||
|
|
||||||
return stat;
|
return stat;
|
||||||
}
|
}
|
||||||
@ -155,14 +156,14 @@ static u32 i2c_dw_read_clear_intrbits_slave(struct dw_i2c_dev *dev)
|
|||||||
|
|
||||||
static int i2c_dw_irq_handler_slave(struct dw_i2c_dev *dev)
|
static int i2c_dw_irq_handler_slave(struct dw_i2c_dev *dev)
|
||||||
{
|
{
|
||||||
u32 raw_stat, stat, enabled;
|
u32 raw_stat, stat, enabled, tmp;
|
||||||
u8 val, slave_activity;
|
u8 val = 0, slave_activity;
|
||||||
|
|
||||||
stat = dw_readl(dev, DW_IC_INTR_STAT);
|
regmap_read(dev->map, DW_IC_INTR_STAT, &stat);
|
||||||
enabled = dw_readl(dev, DW_IC_ENABLE);
|
regmap_read(dev->map, DW_IC_ENABLE, &enabled);
|
||||||
raw_stat = dw_readl(dev, DW_IC_RAW_INTR_STAT);
|
regmap_read(dev->map, DW_IC_RAW_INTR_STAT, &raw_stat);
|
||||||
slave_activity = ((dw_readl(dev, DW_IC_STATUS) &
|
regmap_read(dev->map, DW_IC_STATUS, &tmp);
|
||||||
DW_IC_STATUS_SLAVE_ACTIVITY) >> 6);
|
slave_activity = ((tmp & DW_IC_STATUS_SLAVE_ACTIVITY) >> 6);
|
||||||
|
|
||||||
if (!enabled || !(raw_stat & ~DW_IC_INTR_ACTIVITY) || !dev->slave)
|
if (!enabled || !(raw_stat & ~DW_IC_INTR_ACTIVITY) || !dev->slave)
|
||||||
return 0;
|
return 0;
|
||||||
@ -177,7 +178,8 @@ static int i2c_dw_irq_handler_slave(struct dw_i2c_dev *dev)
|
|||||||
if (stat & DW_IC_INTR_RD_REQ) {
|
if (stat & DW_IC_INTR_RD_REQ) {
|
||||||
if (slave_activity) {
|
if (slave_activity) {
|
||||||
if (stat & DW_IC_INTR_RX_FULL) {
|
if (stat & DW_IC_INTR_RX_FULL) {
|
||||||
val = dw_readl(dev, DW_IC_DATA_CMD);
|
regmap_read(dev->map, DW_IC_DATA_CMD, &tmp);
|
||||||
|
val = tmp;
|
||||||
|
|
||||||
if (!i2c_slave_event(dev->slave,
|
if (!i2c_slave_event(dev->slave,
|
||||||
I2C_SLAVE_WRITE_RECEIVED,
|
I2C_SLAVE_WRITE_RECEIVED,
|
||||||
@ -185,24 +187,24 @@ static int i2c_dw_irq_handler_slave(struct dw_i2c_dev *dev)
|
|||||||
dev_vdbg(dev->dev, "Byte %X acked!",
|
dev_vdbg(dev->dev, "Byte %X acked!",
|
||||||
val);
|
val);
|
||||||
}
|
}
|
||||||
dw_readl(dev, DW_IC_CLR_RD_REQ);
|
regmap_read(dev->map, DW_IC_CLR_RD_REQ, &tmp);
|
||||||
stat = i2c_dw_read_clear_intrbits_slave(dev);
|
stat = i2c_dw_read_clear_intrbits_slave(dev);
|
||||||
} else {
|
} else {
|
||||||
dw_readl(dev, DW_IC_CLR_RD_REQ);
|
regmap_read(dev->map, DW_IC_CLR_RD_REQ, &tmp);
|
||||||
dw_readl(dev, DW_IC_CLR_RX_UNDER);
|
regmap_read(dev->map, DW_IC_CLR_RX_UNDER, &tmp);
|
||||||
stat = i2c_dw_read_clear_intrbits_slave(dev);
|
stat = i2c_dw_read_clear_intrbits_slave(dev);
|
||||||
}
|
}
|
||||||
if (!i2c_slave_event(dev->slave,
|
if (!i2c_slave_event(dev->slave,
|
||||||
I2C_SLAVE_READ_REQUESTED,
|
I2C_SLAVE_READ_REQUESTED,
|
||||||
&val))
|
&val))
|
||||||
dw_writel(dev, val, DW_IC_DATA_CMD);
|
regmap_write(dev->map, DW_IC_DATA_CMD, val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stat & DW_IC_INTR_RX_DONE) {
|
if (stat & DW_IC_INTR_RX_DONE) {
|
||||||
if (!i2c_slave_event(dev->slave, I2C_SLAVE_READ_PROCESSED,
|
if (!i2c_slave_event(dev->slave, I2C_SLAVE_READ_PROCESSED,
|
||||||
&val))
|
&val))
|
||||||
dw_readl(dev, DW_IC_CLR_RX_DONE);
|
regmap_read(dev->map, DW_IC_CLR_RX_DONE, &tmp);
|
||||||
|
|
||||||
i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &val);
|
i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &val);
|
||||||
stat = i2c_dw_read_clear_intrbits_slave(dev);
|
stat = i2c_dw_read_clear_intrbits_slave(dev);
|
||||||
@ -210,7 +212,8 @@ static int i2c_dw_irq_handler_slave(struct dw_i2c_dev *dev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (stat & DW_IC_INTR_RX_FULL) {
|
if (stat & DW_IC_INTR_RX_FULL) {
|
||||||
val = dw_readl(dev, DW_IC_DATA_CMD);
|
regmap_read(dev->map, DW_IC_DATA_CMD, &tmp);
|
||||||
|
val = tmp;
|
||||||
if (!i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_RECEIVED,
|
if (!i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_RECEIVED,
|
||||||
&val))
|
&val))
|
||||||
dev_vdbg(dev->dev, "Byte %X acked!", val);
|
dev_vdbg(dev->dev, "Byte %X acked!", val);
|
||||||
@ -263,7 +266,7 @@ int i2c_dw_probe_slave(struct dw_i2c_dev *dev)
|
|||||||
dev->disable = i2c_dw_disable;
|
dev->disable = i2c_dw_disable;
|
||||||
dev->disable_int = i2c_dw_disable_int;
|
dev->disable_int = i2c_dw_disable_int;
|
||||||
|
|
||||||
ret = i2c_dw_set_reg_access(dev);
|
ret = i2c_dw_init_regmap(dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -271,7 +274,9 @@ int i2c_dw_probe_slave(struct dw_i2c_dev *dev)
|
|||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
i2c_dw_set_fifo_size(dev);
|
ret = i2c_dw_set_fifo_size(dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
ret = dev->init(dev);
|
ret = dev->init(dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user