diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-designware-common.c')
| -rw-r--r-- | drivers/i2c/busses/i2c-designware-common.c | 246 |
1 files changed, 218 insertions, 28 deletions
diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c index 5b1e8f74c4ac..64654dabbb21 100644 --- a/drivers/i2c/busses/i2c-designware-common.c +++ b/drivers/i2c/busses/i2c-designware-common.c @@ -12,6 +12,7 @@ #define DEFAULT_SYMBOL_NAMESPACE "I2C_DW_COMMON" #include <linux/acpi.h> +#include <linux/bitfield.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/device.h> @@ -34,6 +35,10 @@ #include "i2c-designware-core.h" +#define DW_IC_DEFAULT_BUS_CAPACITANCE_pF 100 +#define DW_IC_ABORT_TIMEOUT_US 10 +#define DW_IC_BUSY_POLL_TIMEOUT_US (1 * USEC_PER_MSEC) + static const char *const abort_sources[] = { [ABRT_7B_ADDR_NOACK] = "slave address not acknowledged (7bit mode)", @@ -106,7 +111,7 @@ static int dw_reg_read_word(void *context, unsigned int reg, unsigned int *val) struct dw_i2c_dev *dev = context; *val = readw(dev->base + reg) | - (readw(dev->base + reg + 2) << 16); + (readw(dev->base + reg + DW_IC_REG_STEP_BYTES) << DW_IC_REG_WORD_SHIFT); return 0; } @@ -116,7 +121,7 @@ static int dw_reg_write_word(void *context, unsigned int reg, unsigned int val) struct dw_i2c_dev *dev = context; writew(val, dev->base + reg); - writew(val >> 16, dev->base + reg + 2); + writew(val >> DW_IC_REG_WORD_SHIFT, dev->base + reg + DW_IC_REG_STEP_BYTES); return 0; } @@ -131,7 +136,7 @@ static int dw_reg_write_word(void *context, unsigned int reg, unsigned int val) * * Return: 0 on success, or negative errno otherwise. */ -int i2c_dw_init_regmap(struct dw_i2c_dev *dev) +static int i2c_dw_init_regmap(struct dw_i2c_dev *dev) { struct regmap_config map_cfg = { .reg_bits = 32, @@ -165,7 +170,7 @@ int i2c_dw_init_regmap(struct dw_i2c_dev *dev) if (reg == swab32(DW_IC_COMP_TYPE_VALUE)) { map_cfg.reg_read = dw_reg_read_swab; map_cfg.reg_write = dw_reg_write_swab; - } else if (reg == (DW_IC_COMP_TYPE_VALUE & 0x0000ffff)) { + } else if (reg == lower_16_bits(DW_IC_COMP_TYPE_VALUE)) { map_cfg.reg_read = dw_reg_read_word; map_cfg.reg_write = dw_reg_write_word; } else if (reg != DW_IC_COMP_TYPE_VALUE) { @@ -238,14 +243,10 @@ static void i2c_dw_of_configure(struct device *device) struct platform_device *pdev = to_platform_device(device); struct dw_i2c_dev *dev = dev_get_drvdata(device); - switch (dev->flags & MODEL_MASK) { - case MODEL_MSCC_OCELOT: + if (device_is_compatible(dev->dev, "mscc,ocelot-i2c")) { dev->ext = devm_platform_ioremap_resource(pdev, 1); if (!IS_ERR(dev->ext)) dev->set_sda_hold_time = mscc_twi_set_sda_hold_time; - break; - default: - break; } } @@ -358,6 +359,109 @@ static inline u32 i2c_dw_acpi_round_bus_speed(struct device *device) { return 0; #endif /* CONFIG_ACPI */ +static void i2c_dw_configure_mode(struct dw_i2c_dev *dev, int mode) +{ + switch (mode) { + case DW_IC_MASTER: + regmap_write(dev->map, DW_IC_TX_TL, dev->tx_fifo_depth / 2); + regmap_write(dev->map, DW_IC_RX_TL, 0); + regmap_write(dev->map, DW_IC_CON, dev->master_cfg); + break; + case DW_IC_SLAVE: + dev->status = 0; + regmap_write(dev->map, DW_IC_TX_TL, 0); + regmap_write(dev->map, DW_IC_RX_TL, 0); + regmap_write(dev->map, DW_IC_CON, dev->slave_cfg); + regmap_write(dev->map, DW_IC_SAR, dev->slave->addr); + regmap_write(dev->map, DW_IC_INTR_MASK, DW_IC_INTR_SLAVE_MASK); + __i2c_dw_enable(dev); + break; + default: + WARN(1, "Invalid mode %d\n", mode); + return; + } +} + +static void i2c_dw_write_timings(struct dw_i2c_dev *dev) +{ + /* 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 */ + regmap_write(dev->map, DW_IC_HS_SCL_HCNT, dev->hs_hcnt); + regmap_write(dev->map, DW_IC_HS_SCL_LCNT, dev->hs_lcnt); +} + +/** + * i2c_dw_set_mode() - Select the controller mode of operation - master or slave + * @dev: device private data + * @mode: I2C mode of operation + * + * Configures the controller to operate in @mode. This function needs to be + * called when ever a mode swap is required. + * + * Setting the slave mode does not have an effect before a slave device is + * registered. So before the slave device is registered, the controller is kept + * in master mode regardless of @mode. + * + * The controller must be disabled before this function is called. + */ +void i2c_dw_set_mode(struct dw_i2c_dev *dev, int mode) +{ + if (mode == DW_IC_SLAVE && !dev->slave) + mode = DW_IC_MASTER; + if (dev->mode == mode) + return; + + i2c_dw_configure_mode(dev, mode); + dev->mode = mode; +} + +/** + * i2c_dw_init() - Initialize the DesignWare I2C hardware + * @dev: device private data + * + * This functions configures and enables the DesigWare I2C hardware. + * + * Return: 0 on success, or negative errno otherwise. + */ +int i2c_dw_init(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); + + i2c_dw_write_timings(dev); + + /* 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_mode(dev, dev->mode); + + i2c_dw_release_lock(dev); + + return 0; +} +EXPORT_SYMBOL_GPL(i2c_dw_init); + static void i2c_dw_adjust_bus_speed(struct dw_i2c_dev *dev) { u32 acpi_speed = i2c_dw_acpi_round_bus_speed(dev->dev); @@ -384,7 +488,7 @@ int i2c_dw_fw_parse_and_configure(struct dw_i2c_dev *dev) i2c_parse_fw_timings(device, t, false); if (device_property_read_u32(device, "snps,bus-capacitance-pf", &dev->bus_capacitance_pF)) - dev->bus_capacitance_pF = 100; + dev->bus_capacitance_pF = DW_IC_DEFAULT_BUS_CAPACITANCE_pF; dev->clk_freq_optimized = device_property_read_bool(device, "snps,clk-freq-optimized"); @@ -457,7 +561,7 @@ u32 i2c_dw_scl_lcnt(struct dw_i2c_dev *dev, unsigned int reg, u32 ic_clk, return DIV_ROUND_CLOSEST_ULL((u64)ic_clk * (tLOW + tf), MICRO) - 1 + offset; } -int i2c_dw_set_sda_hold(struct dw_i2c_dev *dev) +static int i2c_dw_set_sda_hold(struct dw_i2c_dev *dev) { unsigned int reg; int ret; @@ -539,8 +643,9 @@ void __i2c_dw_disable(struct dw_i2c_dev *dev) regmap_write(dev->map, DW_IC_ENABLE, enable | DW_IC_ENABLE_ABORT); ret = regmap_read_poll_timeout(dev->map, DW_IC_ENABLE, enable, - !(enable & DW_IC_ENABLE_ABORT), 10, - 100); + !(enable & DW_IC_ENABLE_ABORT), + DW_IC_ABORT_TIMEOUT_US, + 10 * DW_IC_ABORT_TIMEOUT_US); if (ret) dev_err(dev->dev, "timeout while trying to abort current transfer\n"); } @@ -552,7 +657,7 @@ void __i2c_dw_disable(struct dw_i2c_dev *dev) * in that case this test reads zero and exits the loop. */ regmap_read(dev->map, DW_IC_ENABLE_STATUS, &status); - if ((status & 1) == 0) + if (!(status & 1)) return; /* @@ -635,7 +740,8 @@ int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev) ret = regmap_read_poll_timeout(dev->map, DW_IC_STATUS, status, !(status & DW_IC_STATUS_ACTIVITY), - 1100, 20000); + DW_IC_BUSY_POLL_TIMEOUT_US, + 20 * DW_IC_BUSY_POLL_TIMEOUT_US); if (ret) { dev_warn(dev->dev, "timeout waiting for bus ready\n"); @@ -672,7 +778,7 @@ int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev) return -EIO; } -int i2c_dw_set_fifo_size(struct dw_i2c_dev *dev) +static int i2c_dw_set_fifo_size(struct dw_i2c_dev *dev) { u32 tx_fifo_depth, rx_fifo_depth; unsigned int param; @@ -699,12 +805,12 @@ int i2c_dw_set_fifo_size(struct dw_i2c_dev *dev) if (ret) return ret; - tx_fifo_depth = ((param >> 16) & 0xff) + 1; - rx_fifo_depth = ((param >> 8) & 0xff) + 1; + tx_fifo_depth = FIELD_GET(DW_IC_FIFO_TX_FIELD, param) + 1; + rx_fifo_depth = FIELD_GET(DW_IC_FIFO_RX_FIELD, param) + 1; if (!dev->tx_fifo_depth) { dev->tx_fifo_depth = tx_fifo_depth; dev->rx_fifo_depth = rx_fifo_depth; - } else if (tx_fifo_depth >= 2) { + } else if (tx_fifo_depth >= DW_IC_FIFO_MIN_DEPTH) { dev->tx_fifo_depth = min_t(u32, dev->tx_fifo_depth, tx_fifo_depth); dev->rx_fifo_depth = min_t(u32, dev->rx_fifo_depth, @@ -741,19 +847,103 @@ void i2c_dw_disable(struct dw_i2c_dev *dev) } EXPORT_SYMBOL_GPL(i2c_dw_disable); +static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id) +{ + struct dw_i2c_dev *dev = dev_id; + + if (dev->mode == DW_IC_SLAVE) + return i2c_dw_isr_slave(dev); + + return i2c_dw_isr_master(dev); +} + +static const struct i2c_algorithm i2c_dw_algo = { + .xfer = i2c_dw_xfer, + .functionality = i2c_dw_func, +#if IS_ENABLED(CONFIG_I2C_SLAVE) + .reg_slave = i2c_dw_reg_slave, + .unreg_slave = i2c_dw_unreg_slave, +#endif +}; + +static const struct i2c_adapter_quirks i2c_dw_quirks = { + .flags = I2C_AQ_NO_ZERO_LEN, +}; + int i2c_dw_probe(struct dw_i2c_dev *dev) { + struct i2c_adapter *adap = &dev->adapter; + unsigned long irq_flags; + int ret; + device_set_node(&dev->adapter.dev, dev_fwnode(dev->dev)); - switch (dev->mode) { - case DW_IC_SLAVE: - return i2c_dw_probe_slave(dev); - case DW_IC_MASTER: - return i2c_dw_probe_master(dev); - default: - dev_err(dev->dev, "Wrong operation mode: %d\n", dev->mode); - return -EINVAL; + ret = i2c_dw_init_regmap(dev); + if (ret) + return ret; + + ret = i2c_dw_set_sda_hold(dev); + if (ret) + return ret; + + ret = i2c_dw_set_fifo_size(dev); + if (ret) + return ret; + + ret = i2c_dw_probe_master(dev); + if (ret) + return ret; + + ret = i2c_dw_init(dev); + if (ret) + return ret; + + if (!adap->name[0]) + strscpy(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); + + /* + * REVISIT: The mode check may not be necessary. + * For now keeping the flags as they were originally. + */ + if (dev->mode == DW_IC_SLAVE) + irq_flags = IRQF_SHARED; + else 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 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. + */ + ACQUIRE(pm_runtime_noresume, pm)(dev->dev); + ret = ACQUIRE_ERR(pm_runtime_noresume, &pm); + if (ret) + return ret; + + return i2c_add_numbered_adapter(adap); } EXPORT_SYMBOL_GPL(i2c_dw_probe); @@ -797,7 +987,7 @@ static int i2c_dw_runtime_resume(struct device *device) if (!dev->shared_with_punit) i2c_dw_prepare_clk(dev, true); - dev->init(dev); + i2c_dw_init(dev); return 0; } |
