Merge tag 'i2c-for-7.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang:
- core: cleaner fwnode usage
- tegra: timing improvements and Tegra264 support
- lpi2c: fix SMBus block read NACK after byte count
- amd-mp2, designware, mlxbf, rtl9300, spacemit, tegra: cleanups
- designware:
- use a dedicated algorithm for AMD Navi
- replace magic numbers with named constants
- replace min_t() with min() to avoid u8 truncation
- refactor core to enable mode switching
- imx-lpi2c: add runtime PM support for IRQ and clock handling
- lan9691-i2c: add new driver
- rtl9300: use OF helpers directly and avoid fwnode handling
- spacemit: add bus reset support
- units: add HZ_PER_GHZ and use it in several i2c drivers
- at24 i2c eeprom:
- add a set of new compatibles to DT bindings
- use dev_err_probe() consistently in the driver
* tag 'i2c-for-7.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (46 commits)
i2c: imx-lpi2c: fix SMBus block read NACK after byte count
i2c: designware: Remove an unnecessary condition
i2c: designware: Enable mode swapping
i2c: designware: Combine the init functions
i2c: designware: Combine some of the common functions
i2c: designware: Use device_is_compatible() instead of custom approach
dt-bindings: eeprom: at24: Add compatible for Puya P24C128F
drivers/i2c/busses: use min() instead of min_t()
i2c: imx-lpi2c: Add runtime PM support for IRQ and clock management on i.MX8QXP/8QM
i2c: amd-mp2: clean up amd_mp2_find_device()
i2c: designware: Replace magic numbers with named constants
i2c: rtl9300: use of instead of fwnode
i2c: rtl9300: remove const cast
i2c: tegra: remove unused rst
i2c: designware: Remove not-going-to-be-supported code for Baikal SoC
i2c: spacemit: drop useless spaces
i2c: mlxbf: Use HZ_PER_KHZ in the driver
i2c: mlxbf: Remove unused bus speed definitions
i2c: core: Use dev_fwnode()
i2c: core: Replace custom implementation of device_match_fwnode()
...
diff --git a/Documentation/devicetree/bindings/eeprom/at24.yaml b/Documentation/devicetree/bindings/eeprom/at24.yaml
index c212826..ef88f46 100644
--- a/Documentation/devicetree/bindings/eeprom/at24.yaml
+++ b/Documentation/devicetree/bindings/eeprom/at24.yaml
@@ -116,6 +116,7 @@
- const: atmel,24c02
- items:
- enum:
+ - belling,bl24c04a
- giantec,gt24c04a
- onnn,cat24c04
- onnn,cat24c05
@@ -124,6 +125,7 @@
- items:
- enum:
- belling,bl24c16a
+ - belling,bl24c16f
- renesas,r1ex24016
- const: atmel,24c16
- items:
@@ -132,6 +134,7 @@
- items:
- enum:
- belling,bl24s64
+ - giantec,gt24p64a
- onnn,n24s64b
- puya,p24c64f
- const: atmel,24c64
@@ -139,6 +142,7 @@
- enum:
- giantec,gt24p128e
- giantec,gt24p128f
+ - puya,p24c128f
- renesas,r1ex24128
- samsung,s524ad0xd1
- const: atmel,24c128
diff --git a/Documentation/devicetree/bindings/i2c/atmel,at91sam-i2c.yaml b/Documentation/devicetree/bindings/i2c/atmel,at91sam-i2c.yaml
index e61cdb5..c83674c 100644
--- a/Documentation/devicetree/bindings/i2c/atmel,at91sam-i2c.yaml
+++ b/Documentation/devicetree/bindings/i2c/atmel,at91sam-i2c.yaml
@@ -26,6 +26,7 @@
- microchip,sam9x60-i2c
- items:
- enum:
+ - microchip,lan9691-i2c
- microchip,sama7d65-i2c
- microchip,sama7g5-i2c
- microchip,sam9x7-i2c
diff --git a/Documentation/devicetree/bindings/i2c/i2c-mt65xx.yaml b/Documentation/devicetree/bindings/i2c/i2c-mt65xx.yaml
index 3562ce0..ecd5783 100644
--- a/Documentation/devicetree/bindings/i2c/i2c-mt65xx.yaml
+++ b/Documentation/devicetree/bindings/i2c/i2c-mt65xx.yaml
@@ -54,6 +54,7 @@
- enum:
- mediatek,mt6878-i2c
- mediatek,mt6991-i2c
+ - mediatek,mt8189-i2c
- mediatek,mt8196-i2c
- const: mediatek,mt8188-i2c
- items:
diff --git a/Documentation/devicetree/bindings/i2c/spacemit,k1-i2c.yaml b/Documentation/devicetree/bindings/i2c/spacemit,k1-i2c.yaml
index b7220ff..5896fb1 100644
--- a/Documentation/devicetree/bindings/i2c/spacemit,k1-i2c.yaml
+++ b/Documentation/devicetree/bindings/i2c/spacemit,k1-i2c.yaml
@@ -41,6 +41,9 @@
default: 400000
maximum: 3300000
+ resets:
+ maxItems: 1
+
required:
- compatible
- reg
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 09ba55b..e11d507 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -569,24 +569,17 @@
help
This option enables support for the Synopsys DesignWare I2C adapter.
This driver includes support for the I2C host on the Synopsys
- Designware I2C adapter.
+ Designware I2C adapter, and the I2C slave when enabled (select
+ I2C_SLAVE).
To compile the driver as a module, choose M here: the module will be
called i2c-designware-core.
if I2C_DESIGNWARE_CORE
-config I2C_DESIGNWARE_SLAVE
- bool "Synopsys DesignWare Slave"
- select I2C_SLAVE
- help
- If you say yes to this option, support will be included for the
- Synopsys DesignWare I2C slave adapter.
-
config I2C_DESIGNWARE_PLATFORM
tristate "Synopsys DesignWare Platform driver"
depends on (ACPI && COMMON_CLK) || !ACPI
- select MFD_SYSCON if MIPS_BAIKAL_T1
default I2C_DESIGNWARE_CORE
help
If you say yes to this option, support will be included for the
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index fb98576..547123a 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -53,7 +53,7 @@
obj-$(CONFIG_I2C_DESIGNWARE_CORE) += i2c-designware-core.o
i2c-designware-core-y := i2c-designware-common.o
i2c-designware-core-y += i2c-designware-master.o
-i2c-designware-core-$(CONFIG_I2C_DESIGNWARE_SLAVE) += i2c-designware-slave.o
+i2c-designware-core-$(CONFIG_I2C_SLAVE) += i2c-designware-slave.o
obj-$(CONFIG_I2C_DESIGNWARE_PLATFORM) += i2c-designware-platform.o
i2c-designware-platform-y := i2c-designware-platdrv.o
i2c-designware-platform-$(CONFIG_I2C_DESIGNWARE_AMDPSP) += i2c-designware-amdpsp.o
diff --git a/drivers/i2c/busses/i2c-amd-mp2-pci.c b/drivers/i2c/busses/i2c-amd-mp2-pci.c
index 60edbab..5b41d18 100644
--- a/drivers/i2c/busses/i2c-amd-mp2-pci.c
+++ b/drivers/i2c/busses/i2c-amd-mp2-pci.c
@@ -456,18 +456,20 @@ module_pci_driver(amd_mp2_pci_driver);
struct amd_mp2_dev *amd_mp2_find_device(void)
{
+ struct amd_mp2_dev *privdata;
struct device *dev;
struct pci_dev *pci_dev;
- struct amd_mp2_dev *mp2_dev;
dev = driver_find_next_device(&amd_mp2_pci_driver.driver, NULL);
if (!dev)
return NULL;
pci_dev = to_pci_dev(dev);
- mp2_dev = (struct amd_mp2_dev *)pci_get_drvdata(pci_dev);
+ privdata = pci_get_drvdata(pci_dev);
+
put_device(dev);
- return mp2_dev;
+
+ return privdata;
}
EXPORT_SYMBOL_GPL(amd_mp2_find_device);
diff --git a/drivers/i2c/busses/i2c-designware-amdisp.c b/drivers/i2c/busses/i2c-designware-amdisp.c
index 450793d..ec9259d 100644
--- a/drivers/i2c/busses/i2c-designware-amdisp.c
+++ b/drivers/i2c/busses/i2c-designware-amdisp.c
@@ -163,8 +163,8 @@ static int amd_isp_dw_i2c_plat_runtime_resume(struct device *dev)
if (!i_dev->shared_with_punit)
i2c_dw_prepare_clk(i_dev, true);
- if (i_dev->init)
- i_dev->init(i_dev);
+
+ i2c_dw_init(i_dev);
return 0;
}
diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c
index 5b1e8f7..64654dab 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;
}
diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h
index bb5ce0a..a49263a 100644
--- a/drivers/i2c/busses/i2c-designware-core.h
+++ b/drivers/i2c/busses/i2c-designware-core.h
@@ -13,6 +13,7 @@
#include <linux/completion.h>
#include <linux/errno.h>
#include <linux/i2c.h>
+#include <linux/irqreturn.h>
#include <linux/pm.h>
#include <linux/regmap.h>
#include <linux/types.h>
@@ -42,6 +43,19 @@
#define DW_IC_DATA_CMD_FIRST_DATA_BYTE BIT(11)
/*
+ * Register access parameters
+ */
+#define DW_IC_REG_STEP_BYTES 2
+#define DW_IC_REG_WORD_SHIFT 16
+
+/*
+ * FIFO depth configuration
+ */
+#define DW_IC_FIFO_TX_FIELD GENMASK(23, 16)
+#define DW_IC_FIFO_RX_FIELD GENMASK(15, 8)
+#define DW_IC_FIFO_MIN_DEPTH 2
+
+/*
* Registers offset
*/
#define DW_IC_CON 0x00
@@ -239,7 +253,6 @@ struct reset_control;
* @semaphore_idx: Index of table with semaphore type attached to the bus. It's
* -1 if there is no semaphore.
* @shared_with_punit: true if this bus is shared with the SoC's PUNIT
- * @init: function to initialize the I2C hardware
* @set_sda_hold_time: callback to retrieve IP specific SDA hold timing
* @mode: operation mode - DW_IC_MASTER or DW_IC_SLAVE
* @rinfo: I²C GPIO recovery information
@@ -300,7 +313,6 @@ struct dw_i2c_dev {
void (*release_lock)(void);
int semaphore_idx;
bool shared_with_punit;
- int (*init)(struct dw_i2c_dev *dev);
int (*set_sda_hold_time)(struct dw_i2c_dev *dev);
int mode;
struct i2c_bus_recovery_info rinfo;
@@ -313,8 +325,6 @@ struct dw_i2c_dev {
#define ARBITRATION_SEMAPHORE BIT(2)
#define ACCESS_POLLING BIT(3)
-#define MODEL_MSCC_OCELOT BIT(8)
-#define MODEL_BAIKAL_BT1 BIT(9)
#define MODEL_AMD_NAVI_GPU BIT(10)
#define MODEL_WANGXUN_SP BIT(11)
#define MODEL_MASK GENMASK(11, 8)
@@ -333,20 +343,18 @@ struct i2c_dw_semaphore_callbacks {
int (*probe)(struct dw_i2c_dev *dev);
};
-int i2c_dw_init_regmap(struct dw_i2c_dev *dev);
u32 i2c_dw_scl_hcnt(struct dw_i2c_dev *dev, unsigned int reg, u32 ic_clk,
u32 tSYMBOL, u32 tf, int offset);
u32 i2c_dw_scl_lcnt(struct dw_i2c_dev *dev, unsigned int reg, u32 ic_clk,
u32 tLOW, u32 tf, int offset);
-int i2c_dw_set_sda_hold(struct dw_i2c_dev *dev);
u32 i2c_dw_clk_rate(struct dw_i2c_dev *dev);
int i2c_dw_prepare_clk(struct dw_i2c_dev *dev, bool prepare);
int i2c_dw_acquire_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_handle_tx_abort(struct dw_i2c_dev *dev);
-int i2c_dw_set_fifo_size(struct dw_i2c_dev *dev);
u32 i2c_dw_func(struct i2c_adapter *adap);
+irqreturn_t i2c_dw_isr_master(struct dw_i2c_dev *dev);
extern const struct dev_pm_ops i2c_dw_dev_pm_ops;
@@ -386,23 +394,27 @@ void i2c_dw_disable(struct dw_i2c_dev *dev);
extern void i2c_dw_configure_master(struct dw_i2c_dev *dev);
extern int i2c_dw_probe_master(struct dw_i2c_dev *dev);
-#if IS_ENABLED(CONFIG_I2C_DESIGNWARE_SLAVE)
+int i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
extern void i2c_dw_configure_slave(struct dw_i2c_dev *dev);
-extern int i2c_dw_probe_slave(struct dw_i2c_dev *dev);
+irqreturn_t i2c_dw_isr_slave(struct dw_i2c_dev *dev);
+int i2c_dw_reg_slave(struct i2c_client *client);
+int i2c_dw_unreg_slave(struct i2c_client *client);
#else
static inline void i2c_dw_configure_slave(struct dw_i2c_dev *dev) { }
-static inline int i2c_dw_probe_slave(struct dw_i2c_dev *dev) { return -EINVAL; }
+static inline irqreturn_t i2c_dw_isr_slave(struct dw_i2c_dev *dev) { return IRQ_NONE; }
#endif
static inline void i2c_dw_configure(struct dw_i2c_dev *dev)
{
- if (i2c_detect_slave_mode(dev->dev))
- i2c_dw_configure_slave(dev);
- else
- i2c_dw_configure_master(dev);
+ i2c_dw_configure_slave(dev);
+ i2c_dw_configure_master(dev);
}
int i2c_dw_probe(struct dw_i2c_dev *dev);
+int i2c_dw_init(struct dw_i2c_dev *dev);
+void i2c_dw_set_mode(struct dw_i2c_dev *dev, int mode);
#if IS_ENABLED(CONFIG_I2C_DESIGNWARE_BAYTRAIL)
int i2c_dw_baytrail_probe_lock_support(struct dw_i2c_dev *dev);
diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c
index 45bfca0..8ca254c 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 @@ static void i2c_dw_process_transfer(struct dw_i2c_dev *dev, unsigned int stat)
* 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 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
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);
-static const struct i2c_adapter_quirks i2c_dw_quirks = {
- .flags = I2C_AQ_NO_ZERO_LEN,
-};
+ if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU)
+ return amd_i2c_dw_xfer_quirk(dev, msgs, num);
+
+ 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");
diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
index 7be9965..4e6fe3b 100644
--- a/drivers/i2c/busses/i2c-designware-platdrv.c
+++ b/drivers/i2c/busses/i2c-designware-platdrv.c
@@ -37,70 +37,6 @@ static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev)
return clk_get_rate(dev->clk) / HZ_PER_KHZ;
}
-#ifdef CONFIG_OF
-#define BT1_I2C_CTL 0x100
-#define BT1_I2C_CTL_ADDR_MASK GENMASK(7, 0)
-#define BT1_I2C_CTL_WR BIT(8)
-#define BT1_I2C_CTL_GO BIT(31)
-#define BT1_I2C_DI 0x104
-#define BT1_I2C_DO 0x108
-
-static int bt1_i2c_read(void *context, unsigned int reg, unsigned int *val)
-{
- struct dw_i2c_dev *dev = context;
- int ret;
-
- /*
- * Note these methods shouldn't ever fail because the system controller
- * registers are memory mapped. We check the return value just in case.
- */
- ret = regmap_write(dev->sysmap, BT1_I2C_CTL,
- BT1_I2C_CTL_GO | (reg & BT1_I2C_CTL_ADDR_MASK));
- if (ret)
- return ret;
-
- return regmap_read(dev->sysmap, BT1_I2C_DO, val);
-}
-
-static int bt1_i2c_write(void *context, unsigned int reg, unsigned int val)
-{
- struct dw_i2c_dev *dev = context;
- int ret;
-
- ret = regmap_write(dev->sysmap, BT1_I2C_DI, val);
- if (ret)
- return ret;
-
- return regmap_write(dev->sysmap, BT1_I2C_CTL,
- BT1_I2C_CTL_GO | BT1_I2C_CTL_WR | (reg & BT1_I2C_CTL_ADDR_MASK));
-}
-
-static const struct regmap_config bt1_i2c_cfg = {
- .reg_bits = 32,
- .val_bits = 32,
- .reg_stride = 4,
- .fast_io = true,
- .reg_read = bt1_i2c_read,
- .reg_write = bt1_i2c_write,
- .max_register = DW_IC_COMP_TYPE,
-};
-
-static int bt1_i2c_request_regs(struct dw_i2c_dev *dev)
-{
- dev->sysmap = syscon_node_to_regmap(dev->dev->of_node->parent);
- if (IS_ERR(dev->sysmap))
- return PTR_ERR(dev->sysmap);
-
- dev->map = devm_regmap_init(dev->dev, NULL, dev, &bt1_i2c_cfg);
- return PTR_ERR_OR_ZERO(dev->map);
-}
-#else
-static int bt1_i2c_request_regs(struct dw_i2c_dev *dev)
-{
- return -ENODEV;
-}
-#endif
-
static int dw_i2c_get_parent_regmap(struct dw_i2c_dev *dev)
{
dev->map = dev_get_regmap(dev->dev->parent, NULL);
@@ -127,9 +63,6 @@ static int dw_i2c_plat_request_regs(struct dw_i2c_dev *dev)
return dw_i2c_get_parent_regmap(dev);
switch (dev->flags & MODEL_MASK) {
- case MODEL_BAIKAL_BT1:
- ret = bt1_i2c_request_regs(dev);
- break;
case MODEL_WANGXUN_SP:
ret = dw_i2c_get_parent_regmap(dev);
break;
@@ -334,9 +267,8 @@ static void dw_i2c_plat_remove(struct platform_device *pdev)
}
static const struct of_device_id dw_i2c_of_match[] = {
- { .compatible = "snps,designware-i2c", },
- { .compatible = "mscc,ocelot-i2c", .data = (void *)MODEL_MSCC_OCELOT },
- { .compatible = "baikal,bt1-sys-i2c", .data = (void *)MODEL_BAIKAL_BT1 },
+ { .compatible = "mscc,ocelot-i2c" },
+ { .compatible = "snps,designware-i2c" },
{}
};
MODULE_DEVICE_TABLE(of, dw_i2c_of_match);
diff --git a/drivers/i2c/busses/i2c-designware-slave.c b/drivers/i2c/busses/i2c-designware-slave.c
index 6eb16b7..ad0d5fb 100644
--- a/drivers/i2c/busses/i2c-designware-slave.c
+++ b/drivers/i2c/busses/i2c-designware-slave.c
@@ -21,74 +21,33 @@
#include "i2c-designware-core.h"
-static void i2c_dw_configure_fifo_slave(struct dw_i2c_dev *dev)
+int i2c_dw_reg_slave(struct i2c_client *slave)
{
- /* Configure Tx/Rx FIFO threshold levels. */
- regmap_write(dev->map, DW_IC_TX_TL, 0);
- regmap_write(dev->map, DW_IC_RX_TL, 0);
-
- /* Configure the I2C slave. */
- regmap_write(dev->map, DW_IC_CON, dev->slave_cfg);
- regmap_write(dev->map, DW_IC_INTR_MASK, DW_IC_INTR_SLAVE_MASK);
-}
-
-/**
- * i2c_dw_init_slave() - Initialize the DesignWare i2c slave hardware
- * @dev: device private data
- *
- * This function configures and enables the I2C in slave mode.
- * 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_slave(struct dw_i2c_dev *dev)
-{
+ struct dw_i2c_dev *dev = i2c_get_adapdata(slave->adapter);
int ret;
+ if (!i2c_check_functionality(slave->adapter, I2C_FUNC_SLAVE))
+ return -EOPNOTSUPP;
+ if (dev->slave)
+ return -EBUSY;
+ if (slave->flags & I2C_CLIENT_TEN)
+ return -EAFNOSUPPORT;
+
ret = i2c_dw_acquire_lock(dev);
if (ret)
return ret;
- /* Disable the adapter. */
- __i2c_dw_disable(dev);
+ pm_runtime_get_sync(dev->dev);
+ __i2c_dw_disable_nowait(dev);
+ dev->slave = slave;
+ i2c_dw_set_mode(dev, DW_IC_SLAVE);
- /* 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_slave(dev);
i2c_dw_release_lock(dev);
return 0;
}
-static int i2c_dw_reg_slave(struct i2c_client *slave)
-{
- struct dw_i2c_dev *dev = i2c_get_adapdata(slave->adapter);
-
- if (dev->slave)
- return -EBUSY;
- if (slave->flags & I2C_CLIENT_TEN)
- return -EAFNOSUPPORT;
- pm_runtime_get_sync(dev->dev);
-
- /*
- * Set slave address in the IC_SAR register,
- * the address to which the DW_apb_i2c responds.
- */
- __i2c_dw_disable_nowait(dev);
- regmap_write(dev->map, DW_IC_SAR, slave->addr);
- dev->slave = slave;
-
- __i2c_dw_enable(dev);
-
- dev->status = 0;
-
- return 0;
-}
-
-static int i2c_dw_unreg_slave(struct i2c_client *slave)
+int i2c_dw_unreg_slave(struct i2c_client *slave)
{
struct dw_i2c_dev *dev = i2c_get_adapdata(slave->adapter);
@@ -96,6 +55,7 @@ static int i2c_dw_unreg_slave(struct i2c_client *slave)
i2c_dw_disable(dev);
synchronize_irq(dev->irq);
dev->slave = NULL;
+ i2c_dw_set_mode(dev, DW_IC_MASTER);
pm_runtime_put_sync_suspend(dev->dev);
return 0;
@@ -152,9 +112,8 @@ static u32 i2c_dw_read_clear_intrbits_slave(struct dw_i2c_dev *dev)
* Interrupt service routine. This gets called whenever an I2C slave interrupt
* occurs.
*/
-static irqreturn_t i2c_dw_isr_slave(int this_irq, void *dev_id)
+irqreturn_t i2c_dw_isr_slave(struct dw_i2c_dev *dev)
{
- struct dw_i2c_dev *dev = dev_id;
unsigned int raw_stat, stat, enabled, tmp;
u8 val = 0, slave_activity;
@@ -217,68 +176,18 @@ static irqreturn_t i2c_dw_isr_slave(int this_irq, void *dev_id)
return IRQ_HANDLED;
}
-static const struct i2c_algorithm i2c_dw_algo = {
- .functionality = i2c_dw_func,
- .reg_slave = i2c_dw_reg_slave,
- .unreg_slave = i2c_dw_unreg_slave,
-};
-
void i2c_dw_configure_slave(struct dw_i2c_dev *dev)
{
- dev->functionality = I2C_FUNC_SLAVE;
+ if (dev->flags & ACCESS_POLLING)
+ return;
+
+ dev->functionality |= I2C_FUNC_SLAVE;
dev->slave_cfg = DW_IC_CON_RX_FIFO_FULL_HLD_CTRL |
DW_IC_CON_RESTART_EN | DW_IC_CON_STOP_DET_IFADDRESSED;
-
- dev->mode = DW_IC_SLAVE;
}
EXPORT_SYMBOL_GPL(i2c_dw_configure_slave);
-int i2c_dw_probe_slave(struct dw_i2c_dev *dev)
-{
- struct i2c_adapter *adap = &dev->adapter;
- int ret;
-
- dev->init = i2c_dw_init_slave;
-
- 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 = dev->init(dev);
- if (ret)
- return ret;
-
- snprintf(adap->name, sizeof(adap->name),
- "Synopsys DesignWare I2C Slave adapter");
- adap->retries = 3;
- adap->algo = &i2c_dw_algo;
- adap->dev.parent = dev->dev;
- i2c_set_adapdata(adap, dev);
-
- ret = devm_request_irq(dev->dev, dev->irq, i2c_dw_isr_slave,
- IRQF_SHARED, 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_add_numbered_adapter(adap);
- if (ret)
- dev_err(dev->dev, "failure adding adapter: %d\n", ret);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(i2c_dw_probe_slave);
-
MODULE_AUTHOR("Luis Oliveira <lolivei@synopsys.com>");
MODULE_DESCRIPTION("Synopsys DesignWare I2C bus slave adapter");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/i2c/busses/i2c-imx-lpi2c.c b/drivers/i2c/busses/i2c-imx-lpi2c.c
index d882126..a01c236 100644
--- a/drivers/i2c/busses/i2c-imx-lpi2c.c
+++ b/drivers/i2c/busses/i2c-imx-lpi2c.c
@@ -5,6 +5,7 @@
* Copyright 2016 Freescale Semiconductor, Inc.
*/
+#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/delay.h>
@@ -90,6 +91,7 @@
#define MRDR_RXEMPTY BIT(14)
#define MDER_TDDE BIT(0)
#define MDER_RDDE BIT(1)
+#define MSR_RDF_ASSERTED(x) FIELD_GET(MSR_RDF, (x))
#define SCR_SEN BIT(0)
#define SCR_RST BIT(1)
@@ -131,6 +133,7 @@
#define CHUNK_DATA 256
#define I2C_PM_TIMEOUT 10 /* ms */
+#define I2C_PM_LONG_TIMEOUT_MS 1000 /* Avoid dead lock caused by big clock prepare lock */
#define I2C_DMA_THRESHOLD 8 /* bytes */
enum lpi2c_imx_mode {
@@ -148,6 +151,11 @@ enum lpi2c_imx_pincfg {
FOUR_PIN_PP,
};
+struct imx_lpi2c_hwdata {
+ bool need_request_free_irq; /* Needed by irqsteer */
+ bool need_prepare_unprepare_clk; /* Needed by LPCG */
+};
+
struct lpi2c_imx_dma {
bool using_pio_mode;
u8 rx_cmd_buf_len;
@@ -186,6 +194,21 @@ struct lpi2c_imx_struct {
bool can_use_dma;
struct lpi2c_imx_dma *dma;
struct i2c_client *target;
+ int irq;
+ const struct imx_lpi2c_hwdata *hwdata;
+};
+
+static const struct imx_lpi2c_hwdata imx7ulp_lpi2c_hwdata = {
+};
+
+static const struct imx_lpi2c_hwdata imx8qxp_lpi2c_hwdata = {
+ .need_request_free_irq = true,
+ .need_prepare_unprepare_clk = true,
+};
+
+static const struct imx_lpi2c_hwdata imx8qm_lpi2c_hwdata = {
+ .need_request_free_irq = true,
+ .need_prepare_unprepare_clk = true,
};
#define lpi2c_imx_read_msr_poll_timeout(atomic, val, cond) \
@@ -461,7 +484,7 @@ static bool lpi2c_imx_write_txfifo(struct lpi2c_imx_struct *lpi2c_imx, bool atom
static bool lpi2c_imx_read_rxfifo(struct lpi2c_imx_struct *lpi2c_imx, bool atomic)
{
- unsigned int blocklen, remaining;
+ unsigned int remaining;
unsigned int temp, data;
do {
@@ -472,15 +495,6 @@ static bool lpi2c_imx_read_rxfifo(struct lpi2c_imx_struct *lpi2c_imx, bool atomi
lpi2c_imx->rx_buf[lpi2c_imx->delivered++] = data & 0xff;
} while (1);
- /*
- * First byte is the length of remaining packet in the SMBus block
- * data read. Add it to msgs->len.
- */
- if (lpi2c_imx->block_data) {
- blocklen = lpi2c_imx->rx_buf[0];
- lpi2c_imx->msglen += blocklen;
- }
-
remaining = lpi2c_imx->msglen - lpi2c_imx->delivered;
if (!remaining) {
@@ -493,12 +507,7 @@ static bool lpi2c_imx_read_rxfifo(struct lpi2c_imx_struct *lpi2c_imx, bool atomi
lpi2c_imx_set_rx_watermark(lpi2c_imx);
/* multiple receive commands */
- if (lpi2c_imx->block_data) {
- lpi2c_imx->block_data = 0;
- temp = remaining;
- temp |= (RECV_DATA << 8);
- writel(temp, lpi2c_imx->base + LPI2C_MTDR);
- } else if (!(lpi2c_imx->delivered & 0xff)) {
+ if (!(lpi2c_imx->delivered & 0xff)) {
temp = (remaining > CHUNK_DATA ? CHUNK_DATA : remaining) - 1;
temp |= (RECV_DATA << 8);
writel(temp, lpi2c_imx->base + LPI2C_MTDR);
@@ -536,18 +545,77 @@ static int lpi2c_imx_write_atomic(struct lpi2c_imx_struct *lpi2c_imx,
return err;
}
-static void lpi2c_imx_read_init(struct lpi2c_imx_struct *lpi2c_imx,
- struct i2c_msg *msgs)
+static unsigned int lpi2c_SMBus_block_read_length_byte(struct lpi2c_imx_struct *lpi2c_imx)
{
- unsigned int temp;
+ unsigned int data;
+
+ data = readl(lpi2c_imx->base + LPI2C_MRDR);
+ lpi2c_imx->rx_buf[lpi2c_imx->delivered++] = data & 0xff;
+
+ return data;
+}
+
+static int lpi2c_imx_read_init(struct lpi2c_imx_struct *lpi2c_imx,
+ struct i2c_msg *msgs)
+{
+ unsigned int temp, val, block_len;
+ int ret;
lpi2c_imx->rx_buf = msgs->buf;
lpi2c_imx->block_data = msgs->flags & I2C_M_RECV_LEN;
lpi2c_imx_set_rx_watermark(lpi2c_imx);
- temp = msgs->len > CHUNK_DATA ? CHUNK_DATA - 1 : msgs->len - 1;
- temp |= (RECV_DATA << 8);
- writel(temp, lpi2c_imx->base + LPI2C_MTDR);
+
+ if (!lpi2c_imx->block_data) {
+ temp = msgs->len > CHUNK_DATA ? CHUNK_DATA - 1 : msgs->len - 1;
+ temp |= (RECV_DATA << 8);
+ writel(temp, lpi2c_imx->base + LPI2C_MTDR);
+ } else {
+ /*
+ * The LPI2C controller automatically sends a NACK after the last byte of a
+ * receive command, unless the next command in MTDR is also a receive command.
+ * If MTDR is empty when a receive completes, a NACK is sent by default.
+ *
+ * To comply with the SMBus block read spec, we start with a 2-byte read:
+ * The first byte in RXFIFO is the block length. Once this byte arrives, the
+ * controller immediately updates MTDR with the next read command, ensuring
+ * continuous ACK instead of NACK.
+ *
+ * The second byte is the first block data byte. Therefore, the subsequent
+ * read command should request (block_len - 1) bytes, since one data byte
+ * has already been read.
+ */
+
+ writel((RECV_DATA << 8) | 0x01, lpi2c_imx->base + LPI2C_MTDR);
+
+ ret = readl_poll_timeout(lpi2c_imx->base + LPI2C_MSR, val,
+ MSR_RDF_ASSERTED(val), 1, 1000);
+ if (ret) {
+ dev_err(&lpi2c_imx->adapter.dev, "SMBus read count failed %d\n", ret);
+ return ret;
+ }
+
+ /* Read block length byte and confirm this SMBus transfer meets protocol */
+ block_len = lpi2c_SMBus_block_read_length_byte(lpi2c_imx);
+ if (block_len == 0 || block_len > I2C_SMBUS_BLOCK_MAX) {
+ dev_err(&lpi2c_imx->adapter.dev, "Invalid SMBus block read length\n");
+ return -EPROTO;
+ }
+
+ /*
+ * When block_len shows more bytes need to be read, update second read command to
+ * keep MTDR non-empty and ensuring continuous ACKs. Only update command register
+ * here. All block bytes will be read out at IRQ handler or lpi2c_imx_read_atomic()
+ * function.
+ */
+ if (block_len > 1)
+ writel((RECV_DATA << 8) | (block_len - 2), lpi2c_imx->base + LPI2C_MTDR);
+
+ lpi2c_imx->msglen += block_len;
+ msgs->len += block_len;
+ }
+
+ return 0;
}
static bool lpi2c_imx_read_chunk_atomic(struct lpi2c_imx_struct *lpi2c_imx)
@@ -592,6 +660,10 @@ static bool is_use_dma(struct lpi2c_imx_struct *lpi2c_imx, struct i2c_msg *msg)
if (!lpi2c_imx->can_use_dma)
return false;
+ /* DMA is not suitable for SMBus block read */
+ if (msg->flags & I2C_M_RECV_LEN)
+ return false;
+
/*
* A system-wide suspend or resume transition is in progress. LPI2C should use PIO to
* transfer data to avoid issue caused by no ready DMA HW resource.
@@ -609,10 +681,14 @@ static bool is_use_dma(struct lpi2c_imx_struct *lpi2c_imx, struct i2c_msg *msg)
static int lpi2c_imx_pio_xfer(struct lpi2c_imx_struct *lpi2c_imx,
struct i2c_msg *msg)
{
+ int ret;
+
reinit_completion(&lpi2c_imx->complete);
if (msg->flags & I2C_M_RD) {
- lpi2c_imx_read_init(lpi2c_imx, msg);
+ ret = lpi2c_imx_read_init(lpi2c_imx, msg);
+ if (ret)
+ return ret;
lpi2c_imx_intctrl(lpi2c_imx, MIER_RDIE | MIER_NDIE);
} else {
lpi2c_imx_write(lpi2c_imx, msg);
@@ -624,8 +700,12 @@ static int lpi2c_imx_pio_xfer(struct lpi2c_imx_struct *lpi2c_imx,
static int lpi2c_imx_pio_xfer_atomic(struct lpi2c_imx_struct *lpi2c_imx,
struct i2c_msg *msg)
{
+ int ret;
+
if (msg->flags & I2C_M_RD) {
- lpi2c_imx_read_init(lpi2c_imx, msg);
+ ret = lpi2c_imx_read_init(lpi2c_imx, msg);
+ if (ret)
+ return ret;
return lpi2c_imx_read_atomic(lpi2c_imx, msg);
}
@@ -1370,7 +1450,9 @@ static const struct i2c_algorithm lpi2c_imx_algo = {
};
static const struct of_device_id lpi2c_imx_of_match[] = {
- { .compatible = "fsl,imx7ulp-lpi2c" },
+ { .compatible = "fsl,imx7ulp-lpi2c", .data = &imx7ulp_lpi2c_hwdata,},
+ { .compatible = "fsl,imx8qxp-lpi2c", .data = &imx8qxp_lpi2c_hwdata,},
+ { .compatible = "fsl,imx8qm-lpi2c", .data = &imx8qm_lpi2c_hwdata,},
{ }
};
MODULE_DEVICE_TABLE(of, lpi2c_imx_of_match);
@@ -1381,19 +1463,23 @@ static int lpi2c_imx_probe(struct platform_device *pdev)
struct resource *res;
dma_addr_t phy_addr;
unsigned int temp;
- int irq, ret;
+ int ret;
lpi2c_imx = devm_kzalloc(&pdev->dev, sizeof(*lpi2c_imx), GFP_KERNEL);
if (!lpi2c_imx)
return -ENOMEM;
+ lpi2c_imx->hwdata = of_device_get_match_data(&pdev->dev);
+ if (!lpi2c_imx->hwdata)
+ return -ENODEV;
+
lpi2c_imx->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(lpi2c_imx->base))
return PTR_ERR(lpi2c_imx->base);
- irq = platform_get_irq(pdev, 0);
- if (irq < 0)
- return irq;
+ lpi2c_imx->irq = platform_get_irq(pdev, 0);
+ if (lpi2c_imx->irq < 0)
+ return lpi2c_imx->irq;
lpi2c_imx->adapter.owner = THIS_MODULE;
lpi2c_imx->adapter.algo = &lpi2c_imx_algo;
@@ -1413,10 +1499,10 @@ static int lpi2c_imx_probe(struct platform_device *pdev)
if (ret)
lpi2c_imx->bitrate = I2C_MAX_STANDARD_MODE_FREQ;
- ret = devm_request_irq(&pdev->dev, irq, lpi2c_imx_isr, IRQF_NO_SUSPEND,
+ ret = devm_request_irq(&pdev->dev, lpi2c_imx->irq, lpi2c_imx_isr, IRQF_NO_SUSPEND,
pdev->name, lpi2c_imx);
if (ret)
- return dev_err_probe(&pdev->dev, ret, "can't claim irq %d\n", irq);
+ return dev_err_probe(&pdev->dev, ret, "can't claim irq %d\n", lpi2c_imx->irq);
i2c_set_adapdata(&lpi2c_imx->adapter, lpi2c_imx);
platform_set_drvdata(pdev, lpi2c_imx);
@@ -1439,7 +1525,11 @@ static int lpi2c_imx_probe(struct platform_device *pdev)
return dev_err_probe(&pdev->dev, -EINVAL,
"can't get I2C peripheral clock rate\n");
- pm_runtime_set_autosuspend_delay(&pdev->dev, I2C_PM_TIMEOUT);
+ if (lpi2c_imx->hwdata->need_prepare_unprepare_clk)
+ pm_runtime_set_autosuspend_delay(&pdev->dev, I2C_PM_LONG_TIMEOUT_MS);
+ else
+ pm_runtime_set_autosuspend_delay(&pdev->dev, I2C_PM_TIMEOUT);
+
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_get_noresume(&pdev->dev);
pm_runtime_set_active(&pdev->dev);
@@ -1494,8 +1584,16 @@ static void lpi2c_imx_remove(struct platform_device *pdev)
static int __maybe_unused lpi2c_runtime_suspend(struct device *dev)
{
struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev);
+ bool need_prepare_unprepare_clk = lpi2c_imx->hwdata->need_prepare_unprepare_clk;
+ bool need_request_free_irq = lpi2c_imx->hwdata->need_request_free_irq;
- clk_bulk_disable(lpi2c_imx->num_clks, lpi2c_imx->clks);
+ if (need_request_free_irq)
+ devm_free_irq(dev, lpi2c_imx->irq, lpi2c_imx);
+
+ if (need_prepare_unprepare_clk)
+ clk_bulk_disable_unprepare(lpi2c_imx->num_clks, lpi2c_imx->clks);
+ else
+ clk_bulk_disable(lpi2c_imx->num_clks, lpi2c_imx->clks);
pinctrl_pm_select_sleep_state(dev);
return 0;
@@ -1504,13 +1602,32 @@ static int __maybe_unused lpi2c_runtime_suspend(struct device *dev)
static int __maybe_unused lpi2c_runtime_resume(struct device *dev)
{
struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev);
+ bool need_prepare_unprepare_clk = lpi2c_imx->hwdata->need_prepare_unprepare_clk;
+ bool need_request_free_irq = lpi2c_imx->hwdata->need_request_free_irq;
int ret;
pinctrl_pm_select_default_state(dev);
- ret = clk_bulk_enable(lpi2c_imx->num_clks, lpi2c_imx->clks);
- if (ret) {
- dev_err(dev, "failed to enable I2C clock, ret=%d\n", ret);
- return ret;
+ if (need_prepare_unprepare_clk) {
+ ret = clk_bulk_prepare_enable(lpi2c_imx->num_clks, lpi2c_imx->clks);
+ if (ret) {
+ dev_err(dev, "failed to enable I2C clock, ret=%d\n", ret);
+ return ret;
+ }
+ } else {
+ ret = clk_bulk_enable(lpi2c_imx->num_clks, lpi2c_imx->clks);
+ if (ret) {
+ dev_err(dev, "failed to enable clock %d\n", ret);
+ return ret;
+ }
+ }
+
+ if (need_request_free_irq) {
+ ret = devm_request_irq(dev, lpi2c_imx->irq, lpi2c_imx_isr, IRQF_NO_SUSPEND,
+ dev_name(dev), lpi2c_imx);
+ if (ret) {
+ dev_err(dev, "can't claim irq %d\n", lpi2c_imx->irq);
+ return ret;
+ }
}
return 0;
diff --git a/drivers/i2c/busses/i2c-k1.c b/drivers/i2c/busses/i2c-k1.c
index 8ef6d5d..6e93da5 100644
--- a/drivers/i2c/busses/i2c-k1.c
+++ b/drivers/i2c/busses/i2c-k1.c
@@ -4,12 +4,13 @@
*/
#include <linux/bitfield.h>
- #include <linux/clk.h>
- #include <linux/i2c.h>
- #include <linux/iopoll.h>
- #include <linux/module.h>
- #include <linux/of_address.h>
- #include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/i2c.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
/* spacemit i2c registers */
#define SPACEMIT_ICR 0x0 /* Control register */
@@ -534,6 +535,7 @@ static int spacemit_i2c_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *of_node = pdev->dev.of_node;
struct spacemit_i2c_dev *i2c;
+ struct reset_control *rst;
int ret;
i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
@@ -578,6 +580,11 @@ static int spacemit_i2c_probe(struct platform_device *pdev)
if (IS_ERR(clk))
return dev_err_probe(dev, PTR_ERR(clk), "failed to enable bus clock");
+ rst = devm_reset_control_get_optional_exclusive_deasserted(dev, NULL);
+ if (IS_ERR(rst))
+ return dev_err_probe(dev, PTR_ERR(rst),
+ "failed to acquire deasserted reset\n");
+
spacemit_i2c_reset(i2c);
i2c_set_adapdata(&i2c->adapt, i2c);
diff --git a/drivers/i2c/busses/i2c-mlxbf.c b/drivers/i2c/busses/i2c-mlxbf.c
index 8345f7e..6c1cfe9 100644
--- a/drivers/i2c/busses/i2c-mlxbf.c
+++ b/drivers/i2c/busses/i2c-mlxbf.c
@@ -20,6 +20,7 @@
#include <linux/platform_device.h>
#include <linux/string.h>
#include <linux/string_choices.h>
+#include <linux/units.h>
/* Defines what functionality is present. */
#define MLXBF_I2C_FUNC_SMBUS_BLOCK \
@@ -65,15 +66,13 @@
* strongly dependent on the core clock frequency of the SMBus
* Master. Default value is set to 400MHz.
*/
-#define MLXBF_I2C_TYU_PLL_OUT_FREQ (400 * 1000 * 1000)
+#define MLXBF_I2C_TYU_PLL_OUT_FREQ (400 * HZ_PER_MHZ)
/* Reference clock for Bluefield - 156 MHz. */
#define MLXBF_I2C_PLL_IN_FREQ 156250000ULL
/* Constant used to determine the PLL frequency. */
#define MLNXBF_I2C_COREPLL_CONST 16384ULL
-#define MLXBF_I2C_FREQUENCY_1GHZ 1000000000ULL
-
/* PLL registers. */
#define MLXBF_I2C_CORE_PLL_REG1 0x4
#define MLXBF_I2C_CORE_PLL_REG2 0x8
@@ -326,12 +325,6 @@
}
enum {
- MLXBF_I2C_TIMING_100KHZ = 100000,
- MLXBF_I2C_TIMING_400KHZ = 400000,
- MLXBF_I2C_TIMING_1000KHZ = 1000000,
-};
-
-enum {
MLXBF_I2C_F_READ = BIT(0),
MLXBF_I2C_F_WRITE = BIT(1),
MLXBF_I2C_F_NORESTART = BIT(3),
@@ -1083,7 +1076,7 @@ static u32 mlxbf_i2c_get_ticks(struct mlxbf_i2c_priv *priv, u64 nanoseconds,
* Frequency
*/
frequency = priv->frequency;
- ticks = div_u64(nanoseconds * frequency, MLXBF_I2C_FREQUENCY_1GHZ);
+ ticks = div_u64(nanoseconds * frequency, HZ_PER_GHZ);
/*
* The number of ticks is rounded down and if minimum is equal to 1
* then add one tick.
diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c
index aefdbee..cb4d3aa 100644
--- a/drivers/i2c/busses/i2c-mt65xx.c
+++ b/drivers/i2c/busses/i2c-mt65xx.c
@@ -24,6 +24,7 @@
#include <linux/scatterlist.h>
#include <linux/sched.h>
#include <linux/slab.h>
+#include <linux/units.h>
#define I2C_RS_TRANSFER (1 << 4)
#define I2C_ARB_LOST (1 << 3)
@@ -685,7 +686,7 @@ static int mtk_i2c_get_clk_div_restri(struct mtk_i2c *i2c,
* Check and Calculate i2c ac-timing
*
* Hardware design:
- * sample_ns = (1000000000 * (sample_cnt + 1)) / clk_src
+ * sample_ns = (HZ_PER_GHZ * (sample_cnt + 1)) / clk_src
* xxx_cnt_div = spec->min_xxx_ns / sample_ns
*
* Sample_ns is rounded down for xxx_cnt_div would be greater
@@ -701,9 +702,8 @@ static int mtk_i2c_check_ac_timing(struct mtk_i2c *i2c,
{
const struct i2c_spec_values *spec;
unsigned int su_sta_cnt, low_cnt, high_cnt, max_step_cnt;
- unsigned int sda_max, sda_min, clk_ns, max_sta_cnt = 0x3f;
- unsigned int sample_ns = div_u64(1000000000ULL * (sample_cnt + 1),
- clk_src);
+ unsigned int sda_max, sda_min, max_sta_cnt = 0x3f;
+ unsigned int clk_ns, sample_ns;
if (!i2c->dev_comp->timing_adjust)
return 0;
@@ -713,8 +713,9 @@ static int mtk_i2c_check_ac_timing(struct mtk_i2c *i2c,
spec = mtk_i2c_get_spec(check_speed);
+ sample_ns = div_u64(1ULL * HZ_PER_GHZ * (sample_cnt + 1), clk_src);
if (i2c->dev_comp->ltiming_adjust)
- clk_ns = 1000000000 / clk_src;
+ clk_ns = HZ_PER_GHZ / clk_src;
else
clk_ns = sample_ns / 2;
diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c
index 19b648f..b63ee51 100644
--- a/drivers/i2c/busses/i2c-nomadik.c
+++ b/drivers/i2c/busses/i2c-nomadik.c
@@ -31,6 +31,7 @@
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/slab.h>
+#include <linux/units.h>
#define DRIVER_NAME "nmk-i2c"
@@ -419,10 +420,10 @@ static void setup_i2c_controller(struct nmk_i2c_dev *priv)
* modes are 250ns, 100ns, 10ns respectively.
*
* As the time for one cycle T in nanoseconds is
- * T = (1/f) * 1000000000 =>
- * slsu = cycles / (1000000000 / f) + 1
+ * T = (1/f) * HZ_PER_GHZ =>
+ * slsu = cycles / (HZ_PER_GHZ / f) + 1
*/
- ns = DIV_ROUND_UP_ULL(1000000000ULL, i2c_clk);
+ ns = DIV_ROUND_UP(HZ_PER_GHZ, i2c_clk);
switch (priv->sm) {
case I2C_FREQ_MODE_FAST:
case I2C_FREQ_MODE_FAST_PLUS:
diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c
index d4e9196..fcede9f 100644
--- a/drivers/i2c/busses/i2c-rk3x.c
+++ b/drivers/i2c/busses/i2c-rk3x.c
@@ -19,6 +19,7 @@
#include <linux/of_irq.h>
#include <linux/spinlock.h>
#include <linux/clk.h>
+#include <linux/units.h>
#include <linux/wait.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
@@ -896,13 +897,12 @@ static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, unsigned long clk_rate)
clk_disable(i2c->pclk);
- t_low_ns = div_u64(((u64)calc.div_low + 1) * 8 * 1000000000, clk_rate);
- t_high_ns = div_u64(((u64)calc.div_high + 1) * 8 * 1000000000,
- clk_rate);
+ t_low_ns = div_u64(8ULL * HZ_PER_GHZ * (calc.div_low + 1), clk_rate);
+ t_high_ns = div_u64(8ULL * HZ_PER_GHZ * (calc.div_high + 1), clk_rate);
dev_dbg(i2c->dev,
- "CLK %lukhz, Req %uns, Act low %lluns high %lluns\n",
- clk_rate / 1000,
- 1000000000 / t->bus_freq_hz,
+ "CLK %lukHz, Req %luns, Act low %lluns high %lluns\n",
+ clk_rate / HZ_PER_KHZ,
+ HZ_PER_GHZ / t->bus_freq_hz,
t_low_ns, t_high_ns);
}
diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
index 4723e48..672cb97 100644
--- a/drivers/i2c/busses/i2c-rtl9300.c
+++ b/drivers/i2c/busses/i2c-rtl9300.c
@@ -129,7 +129,7 @@ static int rtl9310_i2c_select_scl(struct rtl9300_i2c *i2c, u8 scl)
static int rtl9300_i2c_config_chan(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan)
{
- struct rtl9300_i2c_drv_data *drv_data;
+ const struct rtl9300_i2c_drv_data *drv_data;
int ret;
if (i2c->sda_num == chan->sda_num)
@@ -139,7 +139,7 @@ static int rtl9300_i2c_config_chan(struct rtl9300_i2c *i2c, struct rtl9300_i2c_c
if (ret)
return ret;
- drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
+ drv_data = device_get_match_data(i2c->dev);
ret = drv_data->select_scl(i2c, i2c->scl_num);
if (ret)
return ret;
@@ -371,8 +371,7 @@ static int rtl9300_i2c_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct rtl9300_i2c *i2c;
- struct fwnode_handle *child;
- struct rtl9300_i2c_drv_data *drv_data;
+ const struct rtl9300_i2c_drv_data *drv_data;
struct reg_field fields[F_NUM_FIELDS];
u32 clock_freq, scl_num, sda_num;
int ret, i = 0;
@@ -399,7 +398,7 @@ static int rtl9300_i2c_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, i2c);
- drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
+ drv_data = device_get_match_data(i2c->dev);
if (device_get_child_node_count(dev) > drv_data->max_nchan)
return dev_err_probe(dev, -EINVAL, "Too many channels\n");
@@ -415,15 +414,15 @@ static int rtl9300_i2c_probe(struct platform_device *pdev)
return ret;
i = 0;
- device_for_each_child_node(dev, child) {
+ for_each_child_of_node_scoped(dev->of_node, child) {
struct rtl9300_i2c_chan *chan = &i2c->chans[i];
struct i2c_adapter *adap = &chan->adap;
- ret = fwnode_property_read_u32(child, "reg", &sda_num);
+ ret = of_property_read_u32(child, "reg", &sda_num);
if (ret)
return ret;
- ret = fwnode_property_read_u32(child, "clock-frequency", &clock_freq);
+ ret = of_property_read_u32(child, "clock-frequency", &clock_freq);
if (ret)
clock_freq = I2C_MAX_STANDARD_MODE_FREQ;
@@ -449,7 +448,7 @@ static int rtl9300_i2c_probe(struct platform_device *pdev)
adap->retries = 3;
adap->dev.parent = dev;
i2c_set_adapdata(adap, chan);
- adap->dev.of_node = to_of_node(child);
+ adap->dev.of_node = child;
snprintf(adap->name, sizeof(adap->name), "%s SDA%d\n", dev_name(dev), sda_num);
i++;
diff --git a/drivers/i2c/busses/i2c-st.c b/drivers/i2c/busses/i2c-st.c
index 97d70e6..751ea42 100644
--- a/drivers/i2c/busses/i2c-st.c
+++ b/drivers/i2c/busses/i2c-st.c
@@ -20,6 +20,7 @@
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
+#include <linux/units.h>
/* SSC registers */
#define SSC_BRG 0x000
@@ -285,7 +286,7 @@ static void st_i2c_hw_config(struct st_i2c_dev *i2c_dev)
writel_relaxed(val, i2c_dev->base + SSC_CTL);
rate = clk_get_rate(i2c_dev->clk);
- ns_per_clk = 1000000000 / rate;
+ ns_per_clk = HZ_PER_GHZ / rate;
/* Baudrate */
val = rate / (2 * t->rate);
diff --git a/drivers/i2c/busses/i2c-synquacer.c b/drivers/i2c/busses/i2c-synquacer.c
index 1230f51..4891d68 100644
--- a/drivers/i2c/busses/i2c-synquacer.c
+++ b/drivers/i2c/busses/i2c-synquacer.c
@@ -18,9 +18,10 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include <linux/units.h>
#define WAIT_PCLK(n, rate) \
- ndelay(DIV_ROUND_UP(DIV_ROUND_UP(1000000000, rate), n) + 10)
+ ndelay(DIV_ROUND_UP(DIV_ROUND_UP(HZ_PER_GHZ, rate), n) + 10)
/* I2C register address definitions */
#define SYNQUACER_I2C_REG_BSR (0x00 << 2) // Bus Status
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index e533460..bec619b 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -85,6 +85,7 @@
#define PACKET_HEADER0_PROTOCOL GENMASK(7, 4)
#define PACKET_HEADER0_PROTOCOL_I2C 1
+#define I2C_HEADER_HS_MODE BIT(22)
#define I2C_HEADER_CONT_ON_NAK BIT(21)
#define I2C_HEADER_READ BIT(19)
#define I2C_HEADER_10BIT_ADDR BIT(18)
@@ -136,6 +137,14 @@
#define I2C_MASTER_RESET_CNTRL 0x0a8
+#define I2C_SW_MUTEX 0x0ec
+#define I2C_SW_MUTEX_REQUEST GENMASK(3, 0)
+#define I2C_SW_MUTEX_GRANT GENMASK(7, 4)
+#define I2C_SW_MUTEX_ID_CCPLEX 9
+
+/* SW mutex acquire timeout value in microseconds. */
+#define I2C_SW_MUTEX_TIMEOUT_US (25 * USEC_PER_MSEC)
+
/* configuration load timeout in microseconds */
#define I2C_CONFIG_LOAD_TIMEOUT 1000000
@@ -196,16 +205,24 @@ enum msg_end_type {
* @has_apb_dma: Support of APBDMA on corresponding Tegra chip.
* @tlow_std_mode: Low period of the clock in standard mode.
* @thigh_std_mode: High period of the clock in standard mode.
- * @tlow_fast_fastplus_mode: Low period of the clock in fast/fast-plus modes.
- * @thigh_fast_fastplus_mode: High period of the clock in fast/fast-plus modes.
+ * @tlow_fast_mode: Low period of the clock in fast mode.
+ * @thigh_fast_mode: High period of the clock in fast mode.
+ * @tlow_fastplus_mode: Low period of the clock in fast-plus mode.
+ * @thigh_fastplus_mode: High period of the clock in fast-plus mode.
+ * @tlow_hs_mode: Low period of the clock in HS mode.
+ * @thigh_hs_mode: High period of the clock in HS mode.
* @setup_hold_time_std_mode: Setup and hold time for start and stop conditions
* in standard mode.
- * @setup_hold_time_fast_fast_plus_mode: Setup and hold time for start and stop
- * conditions in fast/fast-plus modes.
+ * @setup_hold_time_fast_mode: Setup and hold time for start and stop
+ * conditions in fast mode.
+ * @setup_hold_time_fastplus_mode: Setup and hold time for start and stop
+ * conditions in fast-plus mode.
* @setup_hold_time_hs_mode: Setup and hold time for start and stop conditions
* in HS mode.
* @has_interface_timing_reg: Has interface timing register to program the tuned
* timing settings.
+ * @enable_hs_mode_support: Enable support for high speed (HS) mode transfers.
+ * @has_mutex: Has mutex register for mutual exclusion with other firmwares or VMs.
*/
struct tegra_i2c_hw_feature {
bool has_continue_xfer_support;
@@ -224,12 +241,19 @@ struct tegra_i2c_hw_feature {
bool has_apb_dma;
u32 tlow_std_mode;
u32 thigh_std_mode;
- u32 tlow_fast_fastplus_mode;
- u32 thigh_fast_fastplus_mode;
+ u32 tlow_fast_mode;
+ u32 thigh_fast_mode;
+ u32 tlow_fastplus_mode;
+ u32 thigh_fastplus_mode;
+ u32 tlow_hs_mode;
+ u32 thigh_hs_mode;
u32 setup_hold_time_std_mode;
- u32 setup_hold_time_fast_fast_plus_mode;
+ u32 setup_hold_time_fast_mode;
+ u32 setup_hold_time_fastplus_mode;
u32 setup_hold_time_hs_mode;
bool has_interface_timing_reg;
+ bool enable_hs_mode_support;
+ bool has_mutex;
};
/**
@@ -240,7 +264,6 @@ struct tegra_i2c_hw_feature {
* @div_clk: clock reference for div clock of I2C controller
* @clocks: array of I2C controller clocks
* @nclocks: number of clocks in the array
- * @rst: reset control for the I2C controller
* @base: ioremapped registers cookie
* @base_phys: physical base address of the I2C controller
* @cont_id: I2C controller ID, used for packet header
@@ -269,7 +292,6 @@ struct tegra_i2c_dev {
struct i2c_adapter adapter;
const struct tegra_i2c_hw_feature *hw;
- struct reset_control *rst;
unsigned int cont_id;
unsigned int irq;
@@ -374,6 +396,76 @@ static void i2c_readsl(struct tegra_i2c_dev *i2c_dev, void *data,
readsl(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg), data, len);
}
+static bool tegra_i2c_mutex_acquired(struct tegra_i2c_dev *i2c_dev)
+{
+ unsigned int reg = tegra_i2c_reg_addr(i2c_dev, I2C_SW_MUTEX);
+ u32 val, id;
+
+ val = readl(i2c_dev->base + reg);
+ id = FIELD_GET(I2C_SW_MUTEX_GRANT, val);
+
+ return id == I2C_SW_MUTEX_ID_CCPLEX;
+}
+
+static bool tegra_i2c_mutex_trylock(struct tegra_i2c_dev *i2c_dev)
+{
+ unsigned int reg = tegra_i2c_reg_addr(i2c_dev, I2C_SW_MUTEX);
+ u32 val, id;
+
+ val = readl(i2c_dev->base + reg);
+ id = FIELD_GET(I2C_SW_MUTEX_GRANT, val);
+ if (id != 0 && id != I2C_SW_MUTEX_ID_CCPLEX)
+ return false;
+
+ val = FIELD_PREP(I2C_SW_MUTEX_REQUEST, I2C_SW_MUTEX_ID_CCPLEX);
+ writel(val, i2c_dev->base + reg);
+
+ return tegra_i2c_mutex_acquired(i2c_dev);
+}
+
+static int tegra_i2c_mutex_lock(struct tegra_i2c_dev *i2c_dev)
+{
+ bool locked;
+ int ret;
+
+ if (!i2c_dev->hw->has_mutex)
+ return 0;
+
+ if (i2c_dev->atomic_mode)
+ ret = read_poll_timeout_atomic(tegra_i2c_mutex_trylock, locked, locked,
+ USEC_PER_MSEC, I2C_SW_MUTEX_TIMEOUT_US,
+ false, i2c_dev);
+ else
+ ret = read_poll_timeout(tegra_i2c_mutex_trylock, locked, locked, USEC_PER_MSEC,
+ I2C_SW_MUTEX_TIMEOUT_US, false, i2c_dev);
+
+ if (ret)
+ dev_warn(i2c_dev->dev, "failed to acquire mutex\n");
+
+ return ret;
+}
+
+static int tegra_i2c_mutex_unlock(struct tegra_i2c_dev *i2c_dev)
+{
+ unsigned int reg = tegra_i2c_reg_addr(i2c_dev, I2C_SW_MUTEX);
+ u32 val, id;
+
+ if (!i2c_dev->hw->has_mutex)
+ return 0;
+
+ val = readl(i2c_dev->base + reg);
+
+ id = FIELD_GET(I2C_SW_MUTEX_GRANT, val);
+ if (id && id != I2C_SW_MUTEX_ID_CCPLEX) {
+ dev_warn(i2c_dev->dev, "unable to unlock mutex, mutex is owned by: %u\n", id);
+ return -EPERM;
+ }
+
+ writel(0, i2c_dev->base + reg);
+
+ return 0;
+}
+
static void tegra_i2c_mask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask)
{
u32 int_mask;
@@ -449,6 +541,11 @@ static int tegra_i2c_init_dma(struct tegra_i2c_dev *i2c_dev)
if (IS_VI(i2c_dev))
return 0;
+ if (!of_property_present(i2c_dev->dev->of_node, "dmas")) {
+ dev_dbg(i2c_dev->dev, "DMA not available, falling back to PIO\n");
+ return 0;
+ }
+
if (i2c_dev->hw->has_apb_dma) {
if (!IS_ENABLED(CONFIG_TEGRA20_APB_DMA)) {
dev_dbg(i2c_dev->dev, "APB DMA support not enabled\n");
@@ -634,6 +731,7 @@ static int tegra_i2c_master_reset(struct tegra_i2c_dev *i2c_dev)
static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
{
u32 val, clk_divisor, clk_multiplier, tsu_thd, tlow, thigh, non_hs_mode;
+ u32 max_bus_freq_hz;
struct i2c_timings *t = &i2c_dev->timings;
int err;
@@ -672,25 +770,40 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
if (IS_VI(i2c_dev))
tegra_i2c_vi_init(i2c_dev);
- switch (t->bus_freq_hz) {
- case I2C_MAX_STANDARD_MODE_FREQ + 1 ... I2C_MAX_FAST_MODE_PLUS_FREQ:
- default:
- tlow = i2c_dev->hw->tlow_fast_fastplus_mode;
- thigh = i2c_dev->hw->thigh_fast_fastplus_mode;
- tsu_thd = i2c_dev->hw->setup_hold_time_fast_fast_plus_mode;
+ if (i2c_dev->hw->enable_hs_mode_support)
+ max_bus_freq_hz = I2C_MAX_HIGH_SPEED_MODE_FREQ;
+ else
+ max_bus_freq_hz = I2C_MAX_FAST_MODE_PLUS_FREQ;
- if (t->bus_freq_hz > I2C_MAX_FAST_MODE_FREQ)
- non_hs_mode = i2c_dev->hw->clk_divisor_fast_plus_mode;
- else
- non_hs_mode = i2c_dev->hw->clk_divisor_fast_mode;
- break;
+ if (WARN_ON(t->bus_freq_hz > max_bus_freq_hz))
+ t->bus_freq_hz = max_bus_freq_hz;
- case 0 ... I2C_MAX_STANDARD_MODE_FREQ:
+ if (t->bus_freq_hz <= I2C_MAX_STANDARD_MODE_FREQ) {
tlow = i2c_dev->hw->tlow_std_mode;
thigh = i2c_dev->hw->thigh_std_mode;
tsu_thd = i2c_dev->hw->setup_hold_time_std_mode;
non_hs_mode = i2c_dev->hw->clk_divisor_std_mode;
- break;
+ } else if (t->bus_freq_hz <= I2C_MAX_FAST_MODE_FREQ) {
+ tlow = i2c_dev->hw->tlow_fast_mode;
+ thigh = i2c_dev->hw->thigh_fast_mode;
+ tsu_thd = i2c_dev->hw->setup_hold_time_fast_mode;
+ non_hs_mode = i2c_dev->hw->clk_divisor_fast_mode;
+ } else if (t->bus_freq_hz <= I2C_MAX_FAST_MODE_PLUS_FREQ) {
+ tlow = i2c_dev->hw->tlow_fastplus_mode;
+ thigh = i2c_dev->hw->thigh_fastplus_mode;
+ tsu_thd = i2c_dev->hw->setup_hold_time_fastplus_mode;
+ non_hs_mode = i2c_dev->hw->clk_divisor_fast_plus_mode;
+ } else {
+ /*
+ * When using HS mode, i.e. when the bus frequency is greater than fast plus mode,
+ * the non-hs timing registers will be used for sending the master code byte for
+ * transition to HS mode. Configure the non-hs timing registers for Fast Mode to
+ * send the master code byte at 400kHz.
+ */
+ tlow = i2c_dev->hw->tlow_fast_mode;
+ thigh = i2c_dev->hw->thigh_fast_mode;
+ tsu_thd = i2c_dev->hw->setup_hold_time_fast_mode;
+ non_hs_mode = i2c_dev->hw->clk_divisor_fast_mode;
}
/* make sure clock divisor programmed correctly */
@@ -712,6 +825,18 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
if (i2c_dev->hw->has_interface_timing_reg && tsu_thd)
i2c_writel(i2c_dev, tsu_thd, I2C_INTERFACE_TIMING_1);
+ /* Write HS mode registers. These will get used only for HS mode*/
+ if (i2c_dev->hw->enable_hs_mode_support) {
+ tlow = i2c_dev->hw->tlow_hs_mode;
+ thigh = i2c_dev->hw->thigh_hs_mode;
+ tsu_thd = i2c_dev->hw->setup_hold_time_hs_mode;
+
+ val = FIELD_PREP(I2C_HS_INTERFACE_TIMING_THIGH, thigh) |
+ FIELD_PREP(I2C_HS_INTERFACE_TIMING_TLOW, tlow);
+ i2c_writel(i2c_dev, val, I2C_HS_INTERFACE_TIMING_0);
+ i2c_writel(i2c_dev, tsu_thd, I2C_HS_INTERFACE_TIMING_1);
+ }
+
clk_multiplier = (tlow + thigh + 2) * (non_hs_mode + 1);
err = clk_set_rate(i2c_dev->div_clk,
@@ -1209,6 +1334,9 @@ static void tegra_i2c_push_packet_header(struct tegra_i2c_dev *i2c_dev,
if (msg->flags & I2C_M_RD)
packet_header |= I2C_HEADER_READ;
+ if (i2c_dev->timings.bus_freq_hz > I2C_MAX_FAST_MODE_PLUS_FREQ)
+ packet_header |= I2C_HEADER_HS_MODE;
+
if (i2c_dev->dma_mode && !i2c_dev->msg_read)
*dma_buf++ = packet_header;
else
@@ -1393,6 +1521,10 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
return ret;
}
+ ret = tegra_i2c_mutex_lock(i2c_dev);
+ if (ret)
+ return ret;
+
for (i = 0; i < num; i++) {
enum msg_end_type end_type = MSG_END_STOP;
@@ -1422,6 +1554,7 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
break;
}
+ ret = tegra_i2c_mutex_unlock(i2c_dev);
pm_runtime_put(i2c_dev->dev);
return ret ?: i;
@@ -1491,12 +1624,17 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = {
.has_apb_dma = true,
.tlow_std_mode = 0x4,
.thigh_std_mode = 0x2,
- .tlow_fast_fastplus_mode = 0x4,
- .thigh_fast_fastplus_mode = 0x2,
+ .tlow_fast_mode = 0x4,
+ .thigh_fast_mode = 0x2,
+ .tlow_fastplus_mode = 0x4,
+ .thigh_fastplus_mode = 0x2,
.setup_hold_time_std_mode = 0x0,
- .setup_hold_time_fast_fast_plus_mode = 0x0,
+ .setup_hold_time_fast_mode = 0x0,
+ .setup_hold_time_fastplus_mode = 0x0,
.setup_hold_time_hs_mode = 0x0,
.has_interface_timing_reg = false,
+ .enable_hs_mode_support = false,
+ .has_mutex = false,
};
static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
@@ -1516,12 +1654,17 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
.has_apb_dma = true,
.tlow_std_mode = 0x4,
.thigh_std_mode = 0x2,
- .tlow_fast_fastplus_mode = 0x4,
- .thigh_fast_fastplus_mode = 0x2,
+ .tlow_fast_mode = 0x4,
+ .thigh_fast_mode = 0x2,
+ .tlow_fastplus_mode = 0x4,
+ .thigh_fastplus_mode = 0x2,
.setup_hold_time_std_mode = 0x0,
- .setup_hold_time_fast_fast_plus_mode = 0x0,
+ .setup_hold_time_fast_mode = 0x0,
+ .setup_hold_time_fastplus_mode = 0x0,
.setup_hold_time_hs_mode = 0x0,
.has_interface_timing_reg = false,
+ .enable_hs_mode_support = false,
+ .has_mutex = false,
};
static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
@@ -1541,12 +1684,17 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
.has_apb_dma = true,
.tlow_std_mode = 0x4,
.thigh_std_mode = 0x2,
- .tlow_fast_fastplus_mode = 0x4,
- .thigh_fast_fastplus_mode = 0x2,
+ .tlow_fast_mode = 0x4,
+ .thigh_fast_mode = 0x2,
+ .tlow_fastplus_mode = 0x4,
+ .thigh_fastplus_mode = 0x2,
.setup_hold_time_std_mode = 0x0,
- .setup_hold_time_fast_fast_plus_mode = 0x0,
+ .setup_hold_time_fast_mode = 0x0,
+ .setup_hold_time_fastplus_mode = 0x0,
.setup_hold_time_hs_mode = 0x0,
.has_interface_timing_reg = false,
+ .enable_hs_mode_support = false,
+ .has_mutex = false,
};
static const struct tegra_i2c_hw_feature tegra124_i2c_hw = {
@@ -1566,12 +1714,17 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = {
.has_apb_dma = true,
.tlow_std_mode = 0x4,
.thigh_std_mode = 0x2,
- .tlow_fast_fastplus_mode = 0x4,
- .thigh_fast_fastplus_mode = 0x2,
+ .tlow_fast_mode = 0x4,
+ .thigh_fast_mode = 0x2,
+ .tlow_fastplus_mode = 0x4,
+ .thigh_fastplus_mode = 0x2,
.setup_hold_time_std_mode = 0x0,
- .setup_hold_time_fast_fast_plus_mode = 0x0,
+ .setup_hold_time_fast_mode = 0x0,
+ .setup_hold_time_fastplus_mode = 0x0,
.setup_hold_time_hs_mode = 0x0,
.has_interface_timing_reg = true,
+ .enable_hs_mode_support = false,
+ .has_mutex = false,
};
static const struct tegra_i2c_hw_feature tegra210_i2c_hw = {
@@ -1591,12 +1744,17 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = {
.has_apb_dma = true,
.tlow_std_mode = 0x4,
.thigh_std_mode = 0x2,
- .tlow_fast_fastplus_mode = 0x4,
- .thigh_fast_fastplus_mode = 0x2,
+ .tlow_fast_mode = 0x4,
+ .thigh_fast_mode = 0x2,
+ .tlow_fastplus_mode = 0x4,
+ .thigh_fastplus_mode = 0x2,
.setup_hold_time_std_mode = 0,
- .setup_hold_time_fast_fast_plus_mode = 0,
+ .setup_hold_time_fast_mode = 0,
+ .setup_hold_time_fastplus_mode = 0,
.setup_hold_time_hs_mode = 0,
.has_interface_timing_reg = true,
+ .enable_hs_mode_support = false,
+ .has_mutex = false,
};
static const struct tegra_i2c_hw_feature tegra186_i2c_hw = {
@@ -1616,12 +1774,17 @@ static const struct tegra_i2c_hw_feature tegra186_i2c_hw = {
.has_apb_dma = false,
.tlow_std_mode = 0x4,
.thigh_std_mode = 0x3,
- .tlow_fast_fastplus_mode = 0x4,
- .thigh_fast_fastplus_mode = 0x2,
+ .tlow_fast_mode = 0x4,
+ .thigh_fast_mode = 0x2,
+ .tlow_fastplus_mode = 0x4,
+ .thigh_fastplus_mode = 0x2,
.setup_hold_time_std_mode = 0,
- .setup_hold_time_fast_fast_plus_mode = 0,
+ .setup_hold_time_fast_mode = 0,
+ .setup_hold_time_fastplus_mode = 0,
.setup_hold_time_hs_mode = 0,
.has_interface_timing_reg = true,
+ .enable_hs_mode_support = false,
+ .has_mutex = false,
};
static const struct tegra_i2c_hw_feature tegra194_i2c_hw = {
@@ -1641,21 +1804,28 @@ static const struct tegra_i2c_hw_feature tegra194_i2c_hw = {
.has_apb_dma = false,
.tlow_std_mode = 0x8,
.thigh_std_mode = 0x7,
- .tlow_fast_fastplus_mode = 0x2,
- .thigh_fast_fastplus_mode = 0x2,
+ .tlow_fast_mode = 0x2,
+ .thigh_fast_mode = 0x2,
+ .tlow_fastplus_mode = 0x2,
+ .thigh_fastplus_mode = 0x2,
+ .tlow_hs_mode = 0x8,
+ .thigh_hs_mode = 0x3,
.setup_hold_time_std_mode = 0x08080808,
- .setup_hold_time_fast_fast_plus_mode = 0x02020202,
+ .setup_hold_time_fast_mode = 0x02020202,
+ .setup_hold_time_fastplus_mode = 0x02020202,
.setup_hold_time_hs_mode = 0x090909,
.has_interface_timing_reg = true,
+ .enable_hs_mode_support = true,
+ .has_mutex = false,
};
static const struct tegra_i2c_hw_feature tegra256_i2c_hw = {
.has_continue_xfer_support = true,
.has_per_pkt_xfer_complete_irq = true,
- .clk_divisor_hs_mode = 7,
+ .clk_divisor_hs_mode = 9,
.clk_divisor_std_mode = 0x7a,
.clk_divisor_fast_mode = 0x40,
- .clk_divisor_fast_plus_mode = 0x19,
+ .clk_divisor_fast_plus_mode = 0x14,
.has_config_load_reg = true,
.has_multi_master_mode = true,
.has_slcg_override_reg = true,
@@ -1666,15 +1836,55 @@ static const struct tegra_i2c_hw_feature tegra256_i2c_hw = {
.has_apb_dma = false,
.tlow_std_mode = 0x8,
.thigh_std_mode = 0x7,
- .tlow_fast_fastplus_mode = 0x3,
- .thigh_fast_fastplus_mode = 0x3,
+ .tlow_fast_mode = 0x4,
+ .thigh_fast_mode = 0x2,
+ .tlow_fastplus_mode = 0x4,
+ .thigh_fastplus_mode = 0x4,
+ .tlow_hs_mode = 0x3,
+ .thigh_hs_mode = 0x2,
.setup_hold_time_std_mode = 0x08080808,
- .setup_hold_time_fast_fast_plus_mode = 0x02020202,
+ .setup_hold_time_fast_mode = 0x04010101,
+ .setup_hold_time_fastplus_mode = 0x04020202,
+ .setup_hold_time_hs_mode = 0x030303,
+ .has_interface_timing_reg = true,
+ .enable_hs_mode_support = true,
+ .has_mutex = true,
+};
+
+static const struct tegra_i2c_hw_feature tegra264_i2c_hw = {
+ .has_continue_xfer_support = true,
+ .has_per_pkt_xfer_complete_irq = true,
+ .clk_divisor_hs_mode = 1,
+ .clk_divisor_std_mode = 0x1d,
+ .clk_divisor_fast_mode = 0x15,
+ .clk_divisor_fast_plus_mode = 0x8,
+ .has_config_load_reg = true,
+ .has_multi_master_mode = true,
+ .has_slcg_override_reg = true,
+ .has_mst_fifo = true,
+ .has_mst_reset = true,
+ .quirks = &tegra194_i2c_quirks,
+ .supports_bus_clear = true,
+ .has_apb_dma = false,
+ .tlow_std_mode = 0x8,
+ .thigh_std_mode = 0x7,
+ .tlow_fast_mode = 0x2,
+ .thigh_fast_mode = 0x2,
+ .tlow_fastplus_mode = 0x2,
+ .thigh_fastplus_mode = 0x2,
+ .tlow_hs_mode = 0x4,
+ .thigh_hs_mode = 0x2,
+ .setup_hold_time_std_mode = 0x08080808,
+ .setup_hold_time_fast_mode = 0x02020202,
+ .setup_hold_time_fastplus_mode = 0x02020202,
.setup_hold_time_hs_mode = 0x090909,
.has_interface_timing_reg = true,
+ .enable_hs_mode_support = true,
+ .has_mutex = true,
};
static const struct of_device_id tegra_i2c_of_match[] = {
+ { .compatible = "nvidia,tegra264-i2c", .data = &tegra264_i2c_hw, },
{ .compatible = "nvidia,tegra256-i2c", .data = &tegra256_i2c_hw, },
{ .compatible = "nvidia,tegra194-i2c", .data = &tegra194_i2c_hw, },
{ .compatible = "nvidia,tegra186-i2c", .data = &tegra186_i2c_hw, },
diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
index ae7e9c8..f0fb0cf 100644
--- a/drivers/i2c/i2c-core-base.c
+++ b/drivers/i2c/i2c-core-base.c
@@ -1090,7 +1090,7 @@ struct i2c_client *i2c_find_device_by_fwnode(struct fwnode_handle *fwnode)
struct i2c_client *client;
struct device *dev;
- if (!fwnode)
+ if (IS_ERR_OR_NULL(fwnode))
return NULL;
dev = bus_find_device_by_fwnode(&i2c_bus_type, fwnode);
@@ -1476,7 +1476,7 @@ static int i2c_setup_host_notify_irq_domain(struct i2c_adapter *adap)
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_HOST_NOTIFY))
return 0;
- domain = irq_domain_create_linear(adap->dev.parent->fwnode,
+ domain = irq_domain_create_linear(dev_fwnode(adap->dev.parent),
I2C_ADDR_7BITS_COUNT,
&i2c_host_notify_irq_ops, adap);
if (!domain)
@@ -1852,10 +1852,10 @@ EXPORT_SYMBOL_GPL(devm_i2c_add_adapter);
static int i2c_dev_or_parent_fwnode_match(struct device *dev, const void *data)
{
- if (dev_fwnode(dev) == data)
+ if (device_match_fwnode(dev, data))
return 1;
- if (dev->parent && dev_fwnode(dev->parent) == data)
+ if (dev->parent && device_match_fwnode(dev->parent, data))
return 1;
return 0;
@@ -1875,7 +1875,7 @@ struct i2c_adapter *i2c_find_adapter_by_fwnode(struct fwnode_handle *fwnode)
struct i2c_adapter *adapter;
struct device *dev;
- if (!fwnode)
+ if (IS_ERR_OR_NULL(fwnode))
return NULL;
dev = bus_find_device(&i2c_bus_type, NULL, fwnode,
diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c
index f721825..0200288 100644
--- a/drivers/misc/eeprom/at24.c
+++ b/drivers/misc/eeprom/at24.c
@@ -657,10 +657,8 @@ static int at24_probe(struct i2c_client *client)
if (!i2c_fn_i2c && !i2c_fn_block)
page_size = 1;
- if (!page_size) {
- dev_err(dev, "page_size must not be 0!\n");
- return -EINVAL;
- }
+ if (!page_size)
+ return dev_err_probe(dev, -EINVAL, "page_size must not be 0!\n");
if (!is_power_of_2(page_size))
dev_warn(dev, "page_size looks suspicious (no power of 2)!\n");
@@ -674,11 +672,9 @@ static int at24_probe(struct i2c_client *client)
(flags & AT24_FLAG_ADDR16) ? 65536 : 256);
}
- if ((flags & AT24_FLAG_SERIAL) && (flags & AT24_FLAG_MAC)) {
- dev_err(dev,
- "invalid device data - cannot have both AT24_FLAG_SERIAL & AT24_FLAG_MAC.");
- return -EINVAL;
- }
+ if ((flags & AT24_FLAG_SERIAL) && (flags & AT24_FLAG_MAC))
+ return dev_err_probe(dev, -EINVAL,
+ "invalid device data - cannot have both AT24_FLAG_SERIAL & AT24_FLAG_MAC.");
regmap_config.val_bits = 8;
regmap_config.reg_bits = (flags & AT24_FLAG_ADDR16) ? 16 : 8;
@@ -759,10 +755,8 @@ static int at24_probe(struct i2c_client *client)
full_power = acpi_dev_state_d0(&client->dev);
if (full_power) {
err = regulator_enable(at24->vcc_reg);
- if (err) {
- dev_err(dev, "Failed to enable vcc regulator\n");
- return err;
- }
+ if (err)
+ return dev_err_probe(dev, err, "Failed to enable vcc regulator\n");
pm_runtime_set_active(dev);
}
diff --git a/include/linux/units.h b/include/linux/units.h
index 00e15de..0c296a0 100644
--- a/include/linux/units.h
+++ b/include/linux/units.h
@@ -25,9 +25,12 @@
#define MICROHZ_PER_HZ 1000000UL
#define MILLIHZ_PER_HZ 1000UL
+/* Hz based multipliers */
#define HZ_PER_KHZ 1000UL
#define HZ_PER_MHZ 1000000UL
+#define HZ_PER_GHZ 1000000000UL
+/* kHz based multipliers */
#define KHZ_PER_MHZ 1000UL
#define KHZ_PER_GHZ 1000000UL