summaryrefslogtreecommitdiff
path: root/drivers/i2c/busses/i2c-designware-master.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c/busses/i2c-designware-master.c')
-rw-r--r--drivers/i2c/busses/i2c-designware-master.c187
1 files changed, 27 insertions, 160 deletions
diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c
index 45bfca05bb30..8ca254cbb2f8 100644
--- a/drivers/i2c/busses/i2c-designware-master.c
+++ b/drivers/i2c/busses/i2c-designware-master.c
@@ -31,16 +31,6 @@
#define AMD_TIMEOUT_MAX_US 250
#define AMD_MASTERCFG_MASK GENMASK(15, 0)
-static void i2c_dw_configure_fifo_master(struct dw_i2c_dev *dev)
-{
- /* Configure Tx/Rx FIFO threshold levels */
- regmap_write(dev->map, DW_IC_TX_TL, dev->tx_fifo_depth / 2);
- regmap_write(dev->map, DW_IC_RX_TL, 0);
-
- /* Configure the I2C master */
- regmap_write(dev->map, DW_IC_CON, dev->master_cfg);
-}
-
static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
{
unsigned int comp_param1;
@@ -191,66 +181,10 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
dev->hs_hcnt, dev->hs_lcnt);
}
- ret = i2c_dw_set_sda_hold(dev);
- if (ret)
- return ret;
-
dev_dbg(dev->dev, "Bus speed: %s\n", i2c_freq_mode_string(t->bus_freq_hz));
return 0;
}
-/**
- * i2c_dw_init_master() - Initialize the DesignWare I2C master hardware
- * @dev: device private data
- *
- * This functions configures and enables the I2C master.
- * This function is called during I2C init function, and in case of timeout at
- * run time.
- *
- * Return: 0 on success, or negative errno otherwise.
- */
-static int i2c_dw_init_master(struct dw_i2c_dev *dev)
-{
- int ret;
-
- ret = i2c_dw_acquire_lock(dev);
- if (ret)
- return ret;
-
- /* Disable the adapter */
- __i2c_dw_disable(dev);
-
- /*
- * Mask SMBus interrupts to block storms from broken
- * firmware that leaves IC_SMBUS=1; the handler never
- * services them.
- */
- regmap_write(dev->map, DW_IC_SMBUS_INTR_MASK, 0);
-
- /* Write standard speed timing parameters */
- regmap_write(dev->map, DW_IC_SS_SCL_HCNT, dev->ss_hcnt);
- regmap_write(dev->map, DW_IC_SS_SCL_LCNT, dev->ss_lcnt);
-
- /* Write fast mode/fast mode plus timing parameters */
- regmap_write(dev->map, DW_IC_FS_SCL_HCNT, dev->fs_hcnt);
- regmap_write(dev->map, DW_IC_FS_SCL_LCNT, dev->fs_lcnt);
-
- /* Write high speed timing parameters if supported */
- if (dev->hs_hcnt && dev->hs_lcnt) {
- regmap_write(dev->map, DW_IC_HS_SCL_HCNT, dev->hs_hcnt);
- regmap_write(dev->map, DW_IC_HS_SCL_LCNT, dev->hs_lcnt);
- }
-
- /* Write SDA hold time if supported */
- if (dev->sda_hold_time)
- regmap_write(dev->map, DW_IC_SDA_HOLD, dev->sda_hold_time);
-
- i2c_dw_configure_fifo_master(dev);
- i2c_dw_release_lock(dev);
-
- return 0;
-}
-
static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
{
struct i2c_msg *msgs = dev->msgs;
@@ -260,6 +194,8 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
/* Disable the adapter */
__i2c_dw_disable(dev);
+ i2c_dw_set_mode(dev, DW_IC_MASTER);
+
/* If the slave address is ten bit address, enable 10BITADDR */
if (msgs[dev->msg_write_idx].flags & I2C_M_TEN) {
ic_con = DW_IC_CON_10BITADDR_MASTER;
@@ -353,14 +289,17 @@ static int i2c_dw_status(struct dw_i2c_dev *dev)
* Initiate and continue master read/write transaction with polling
* based transfer routine afterward write messages into the Tx buffer.
*/
-static int amd_i2c_dw_xfer_quirk(struct i2c_adapter *adap, struct i2c_msg *msgs, int num_msgs)
+static int amd_i2c_dw_xfer_quirk(struct dw_i2c_dev *dev, struct i2c_msg *msgs, int num_msgs)
{
- struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
int msg_wrt_idx, msg_itr_lmt, buf_len, data_idx;
int cmd = 0, status;
u8 *tx_buf;
unsigned int val;
+ ACQUIRE(pm_runtime_active_auto_try, pm)(dev->dev);
+ if (ACQUIRE_ERR(pm_runtime_active_auto_try, &pm))
+ return -ENXIO;
+
/*
* In order to enable the interrupt for UCSI i.e. AMD NAVI GPU card,
* it is mandatory to set the right value in specific register
@@ -571,7 +510,7 @@ i2c_dw_recv_len(struct dw_i2c_dev *dev, u8 len)
* after receiving the first byte.
*/
len += (flags & I2C_CLIENT_PEC) ? 2 : 1;
- dev->tx_buf_len = len - min_t(u8, len, dev->rx_outstanding);
+ dev->tx_buf_len = len - min(len, dev->rx_outstanding);
msgs[dev->msg_read_idx].len = len;
msgs[dev->msg_read_idx].flags &= ~I2C_M_RECV_LEN;
@@ -593,11 +532,12 @@ i2c_dw_read(struct dw_i2c_dev *dev)
unsigned int rx_valid;
for (; dev->msg_read_idx < dev->msgs_num; dev->msg_read_idx++) {
+ u32 flags = msgs[dev->msg_read_idx].flags;
unsigned int tmp;
u32 len;
u8 *buf;
- if (!(msgs[dev->msg_read_idx].flags & I2C_M_RD))
+ if (!(flags & I2C_M_RD))
continue;
if (!(dev->status & STATUS_READ_IN_PROGRESS)) {
@@ -611,8 +551,6 @@ i2c_dw_read(struct dw_i2c_dev *dev)
regmap_read(dev->map, DW_IC_RXFLR, &rx_valid);
for (; len > 0 && rx_valid > 0; len--, rx_valid--) {
- u32 flags = msgs[dev->msg_read_idx].flags;
-
regmap_read(dev->map, DW_IC_DATA_CMD, &tmp);
tmp &= DW_IC_DATA_CMD_DAT;
/* Ensure length byte is a valid value */
@@ -749,9 +687,8 @@ tx_aborted:
* Interrupt service routine. This gets called whenever an I2C master interrupt
* occurs.
*/
-static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
+irqreturn_t i2c_dw_isr_master(struct dw_i2c_dev *dev)
{
- struct dw_i2c_dev *dev = dev_id;
unsigned int stat, enabled;
regmap_read(dev->map, DW_IC_ENABLE, &enabled);
@@ -812,23 +749,14 @@ static int i2c_dw_wait_transfer(struct dw_i2c_dev *dev)
* Prepare controller for a transaction and call i2c_dw_xfer_msg.
*/
static int
-i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+i2c_dw_xfer_common(struct dw_i2c_dev *dev, struct i2c_msg msgs[], int num)
{
- struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
int ret;
dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num);
pm_runtime_get_sync(dev->dev);
- switch (dev->flags & MODEL_MASK) {
- case MODEL_AMD_NAVI_GPU:
- ret = amd_i2c_dw_xfer_quirk(adap, msgs, num);
- goto done_nolock;
- default:
- break;
- }
-
reinit_completion(&dev->cmd_complete);
dev->msgs = msgs;
dev->msgs_num = num;
@@ -855,9 +783,9 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
ret = i2c_dw_wait_transfer(dev);
if (ret) {
dev_err(dev->dev, "controller timed out\n");
- /* i2c_dw_init_master() implicitly disables the adapter */
+ /* i2c_dw_init() implicitly disables the adapter */
i2c_recover_bus(&dev->adapter);
- i2c_dw_init_master(dev);
+ i2c_dw_init(dev);
goto done;
}
@@ -905,6 +833,8 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
ret = -EIO;
done:
+ i2c_dw_set_mode(dev, DW_IC_SLAVE);
+
i2c_dw_release_lock(dev);
done_nolock:
@@ -913,20 +843,21 @@ done_nolock:
return ret;
}
-static const struct i2c_algorithm i2c_dw_algo = {
- .xfer = i2c_dw_xfer,
- .functionality = i2c_dw_func,
-};
+int i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+ struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
+
+ if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU)
+ return amd_i2c_dw_xfer_quirk(dev, msgs, num);
-static const struct i2c_adapter_quirks i2c_dw_quirks = {
- .flags = I2C_AQ_NO_ZERO_LEN,
-};
+ return i2c_dw_xfer_common(dev, msgs, num);
+}
void i2c_dw_configure_master(struct dw_i2c_dev *dev)
{
struct i2c_timings *t = &dev->timings;
- dev->functionality = I2C_FUNC_10BIT_ADDR | DW_IC_DEFAULT_FUNCTIONALITY;
+ dev->functionality |= I2C_FUNC_10BIT_ADDR | DW_IC_DEFAULT_FUNCTIONALITY;
dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
DW_IC_CON_RESTART_EN;
@@ -961,7 +892,7 @@ static void i2c_dw_unprepare_recovery(struct i2c_adapter *adap)
i2c_dw_prepare_clk(dev, true);
reset_control_deassert(dev->rst);
- i2c_dw_init_master(dev);
+ i2c_dw_init(dev);
}
static int i2c_dw_init_recovery_info(struct dw_i2c_dev *dev)
@@ -1005,27 +936,15 @@ static int i2c_dw_init_recovery_info(struct dw_i2c_dev *dev)
int i2c_dw_probe_master(struct dw_i2c_dev *dev)
{
- struct i2c_adapter *adap = &dev->adapter;
- unsigned long irq_flags;
unsigned int ic_con;
int ret;
init_completion(&dev->cmd_complete);
- dev->init = i2c_dw_init_master;
-
- ret = i2c_dw_init_regmap(dev);
- if (ret)
- return ret;
-
ret = i2c_dw_set_timings_master(dev);
if (ret)
return ret;
- ret = i2c_dw_set_fifo_size(dev);
- if (ret)
- return ret;
-
/* Lock the bus for accessing DW_IC_CON */
ret = i2c_dw_acquire_lock(dev);
if (ret)
@@ -1045,60 +964,8 @@ int i2c_dw_probe_master(struct dw_i2c_dev *dev)
if (ic_con & DW_IC_CON_BUS_CLEAR_CTRL)
dev->master_cfg |= DW_IC_CON_BUS_CLEAR_CTRL;
- ret = dev->init(dev);
- if (ret)
- return ret;
-
- if (!adap->name[0])
- scnprintf(adap->name, sizeof(adap->name),
- "Synopsys DesignWare I2C adapter");
- adap->retries = 3;
- adap->algo = &i2c_dw_algo;
- adap->quirks = &i2c_dw_quirks;
- adap->dev.parent = dev->dev;
- i2c_set_adapdata(adap, dev);
-
- if (dev->flags & ACCESS_NO_IRQ_SUSPEND) {
- irq_flags = IRQF_NO_SUSPEND;
- } else {
- irq_flags = IRQF_SHARED | IRQF_COND_SUSPEND;
- }
-
- ret = i2c_dw_acquire_lock(dev);
- if (ret)
- return ret;
-
- __i2c_dw_write_intr_mask(dev, 0);
- i2c_dw_release_lock(dev);
-
- if (!(dev->flags & ACCESS_POLLING)) {
- ret = devm_request_irq(dev->dev, dev->irq, i2c_dw_isr,
- irq_flags, dev_name(dev->dev), dev);
- if (ret)
- return dev_err_probe(dev->dev, ret,
- "failure requesting irq %i: %d\n",
- dev->irq, ret);
- }
-
- ret = i2c_dw_init_recovery_info(dev);
- if (ret)
- return ret;
-
- /*
- * Increment PM usage count during adapter registration in order to
- * avoid possible spurious runtime suspend when adapter device is
- * registered to the device core and immediate resume in case bus has
- * registered I2C slaves that do I2C transfers in their probe.
- */
- pm_runtime_get_noresume(dev->dev);
- ret = i2c_add_numbered_adapter(adap);
- if (ret)
- dev_err(dev->dev, "failure adding adapter: %d\n", ret);
- pm_runtime_put_noidle(dev->dev);
-
- return ret;
+ return i2c_dw_init_recovery_info(dev);
}
-EXPORT_SYMBOL_GPL(i2c_dw_probe_master);
MODULE_DESCRIPTION("Synopsys DesignWare I2C bus master adapter");
MODULE_LICENSE("GPL");