mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 03:24:45 +01:00
i2c-for-7.0-rc1
- 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
- designware: replace magic numbers with named constants
- designware: replace min_t() with min() to avoid u8 truncation
- designware: 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 updates for v7.0-rc1
- add a set of new compatibles to DT bindings
- use dev_err_probe() consistently in the driver
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEEOZGx6rniZ1Gk92RdFA3kzBSgKbYFAmmRmeMACgkQFA3kzBSg
KbbLLA//XoOh4GtD6LA7Uh9GNgeTcLNJf08UehPby+8w6urx0yieGk0YVggmJtqH
Uav8jDxyVGMZsGHIksnF/a5olFzvCfy6rUQEYHL9I+EWaufLCmQRk8lcjWUv6Z+s
6q50r8WXIFZTc+h4V0chHf4L5TLjJNvV+TBHm3maAxhJy+UloF3kdS6QlczAmbD9
gigilvZrpkqRC4FPPLdqM8v7losA9TRQDCYAyghcGf7AAxj+FFjpWPSSLEa9eb7d
QszFS3+d3H5EfJ4ONs818XSxrQNJr/gdlgBAdENskJPfTwcgdTZV8ySRxRC/BnFq
Z1kostoABrhHYvK72Z+nOaLD+sFwPHM/XIUHXuMsQNkr4yj+R+aDUs4JB4BV8LX1
Fc03u8r9vSUszpcIgk38t5gTdXLt6eybertv5HJ9WDHvZq1TCMw97Lk7zdWN6Q9A
pmG4RNZnpG/OMrCkzhX/gA0nOBZMWOOplKNV6uhy+k78g6pSy772iBAY5YkW0VFZ
E+GYHeFlEsSPh37zbjWcfZl3r0eEk3BY/19t4drxS9WknUcu8dY3QQPC8TXOYMwA
gx42HudGD4Yq2lbFekiAcz0rx5p399ihRPrjKm8E8S8TR8Slrk6tAh2ZHq/TMi9R
arELjn6EMMuAjRIlsd0LYJn4suGILPFiLQXgRArfGu2zGCmM8rQ=
=gCyg
-----END PGP SIGNATURE-----
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()
...
This commit is contained in:
commit
bb7a3fc2c9
26 changed files with 783 additions and 542 deletions
|
|
@ -116,6 +116,7 @@ properties:
|
|||
- const: atmel,24c02
|
||||
- items:
|
||||
- enum:
|
||||
- belling,bl24c04a
|
||||
- giantec,gt24c04a
|
||||
- onnn,cat24c04
|
||||
- onnn,cat24c05
|
||||
|
|
@ -124,6 +125,7 @@ properties:
|
|||
- items:
|
||||
- enum:
|
||||
- belling,bl24c16a
|
||||
- belling,bl24c16f
|
||||
- renesas,r1ex24016
|
||||
- const: atmel,24c16
|
||||
- items:
|
||||
|
|
@ -132,6 +134,7 @@ properties:
|
|||
- items:
|
||||
- enum:
|
||||
- belling,bl24s64
|
||||
- giantec,gt24p64a
|
||||
- onnn,n24s64b
|
||||
- puya,p24c64f
|
||||
- const: atmel,24c64
|
||||
|
|
@ -139,6 +142,7 @@ properties:
|
|||
- enum:
|
||||
- giantec,gt24p128e
|
||||
- giantec,gt24p128f
|
||||
- puya,p24c128f
|
||||
- renesas,r1ex24128
|
||||
- samsung,s524ad0xd1
|
||||
- const: atmel,24c128
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ properties:
|
|||
- microchip,sam9x60-i2c
|
||||
- items:
|
||||
- enum:
|
||||
- microchip,lan9691-i2c
|
||||
- microchip,sama7d65-i2c
|
||||
- microchip,sama7g5-i2c
|
||||
- microchip,sam9x7-i2c
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ properties:
|
|||
- enum:
|
||||
- mediatek,mt6878-i2c
|
||||
- mediatek,mt6991-i2c
|
||||
- mediatek,mt8189-i2c
|
||||
- mediatek,mt8196-i2c
|
||||
- const: mediatek,mt8188-i2c
|
||||
- items:
|
||||
|
|
|
|||
|
|
@ -41,6 +41,9 @@ properties:
|
|||
default: 400000
|
||||
maximum: 3300000
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
|
|
|||
|
|
@ -569,24 +569,17 @@ config I2C_DESIGNWARE_CORE
|
|||
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
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ obj-$(CONFIG_I2C_DAVINCI) += i2c-davinci.o
|
|||
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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -41,6 +42,19 @@
|
|||
#define DW_IC_DATA_CMD_DAT GENMASK(7, 0)
|
||||
#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
|
||||
*/
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -31,16 +31,6 @@
|
|||
#define AMD_TIMEOUT_MAX_US 250
|
||||
#define AMD_MASTERCFG_MASK GENMASK(15, 0)
|
||||
|
||||
static void i2c_dw_configure_fifo_master(struct dw_i2c_dev *dev)
|
||||
{
|
||||
/* Configure Tx/Rx FIFO threshold levels */
|
||||
regmap_write(dev->map, DW_IC_TX_TL, dev->tx_fifo_depth / 2);
|
||||
regmap_write(dev->map, DW_IC_RX_TL, 0);
|
||||
|
||||
/* Configure the I2C master */
|
||||
regmap_write(dev->map, DW_IC_CON, dev->master_cfg);
|
||||
}
|
||||
|
||||
static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
|
||||
{
|
||||
unsigned int comp_param1;
|
||||
|
|
@ -191,66 +181,10 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
|
|||
dev->hs_hcnt, dev->hs_lcnt);
|
||||
}
|
||||
|
||||
ret = i2c_dw_set_sda_hold(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_dbg(dev->dev, "Bus speed: %s\n", i2c_freq_mode_string(t->bus_freq_hz));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* i2c_dw_init_master() - Initialize the DesignWare I2C master hardware
|
||||
* @dev: device private data
|
||||
*
|
||||
* This functions configures and enables the I2C master.
|
||||
* This function is called during I2C init function, and in case of timeout at
|
||||
* run time.
|
||||
*
|
||||
* Return: 0 on success, or negative errno otherwise.
|
||||
*/
|
||||
static int i2c_dw_init_master(struct dw_i2c_dev *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_dw_acquire_lock(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Disable the adapter */
|
||||
__i2c_dw_disable(dev);
|
||||
|
||||
/*
|
||||
* Mask SMBus interrupts to block storms from broken
|
||||
* firmware that leaves IC_SMBUS=1; the handler never
|
||||
* services them.
|
||||
*/
|
||||
regmap_write(dev->map, DW_IC_SMBUS_INTR_MASK, 0);
|
||||
|
||||
/* Write standard speed timing parameters */
|
||||
regmap_write(dev->map, DW_IC_SS_SCL_HCNT, dev->ss_hcnt);
|
||||
regmap_write(dev->map, DW_IC_SS_SCL_LCNT, dev->ss_lcnt);
|
||||
|
||||
/* Write fast mode/fast mode plus timing parameters */
|
||||
regmap_write(dev->map, DW_IC_FS_SCL_HCNT, dev->fs_hcnt);
|
||||
regmap_write(dev->map, DW_IC_FS_SCL_LCNT, dev->fs_lcnt);
|
||||
|
||||
/* Write high speed timing parameters if supported */
|
||||
if (dev->hs_hcnt && dev->hs_lcnt) {
|
||||
regmap_write(dev->map, DW_IC_HS_SCL_HCNT, dev->hs_hcnt);
|
||||
regmap_write(dev->map, DW_IC_HS_SCL_LCNT, dev->hs_lcnt);
|
||||
}
|
||||
|
||||
/* Write SDA hold time if supported */
|
||||
if (dev->sda_hold_time)
|
||||
regmap_write(dev->map, DW_IC_SDA_HOLD, dev->sda_hold_time);
|
||||
|
||||
i2c_dw_configure_fifo_master(dev);
|
||||
i2c_dw_release_lock(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
|
||||
{
|
||||
struct i2c_msg *msgs = dev->msgs;
|
||||
|
|
@ -260,6 +194,8 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
|
|||
/* Disable the adapter */
|
||||
__i2c_dw_disable(dev);
|
||||
|
||||
i2c_dw_set_mode(dev, DW_IC_MASTER);
|
||||
|
||||
/* If the slave address is ten bit address, enable 10BITADDR */
|
||||
if (msgs[dev->msg_write_idx].flags & I2C_M_TEN) {
|
||||
ic_con = DW_IC_CON_10BITADDR_MASTER;
|
||||
|
|
@ -353,14 +289,17 @@ static int i2c_dw_status(struct dw_i2c_dev *dev)
|
|||
* Initiate and continue master read/write transaction with polling
|
||||
* based transfer routine afterward write messages into the Tx buffer.
|
||||
*/
|
||||
static int amd_i2c_dw_xfer_quirk(struct i2c_adapter *adap, struct i2c_msg *msgs, int num_msgs)
|
||||
static int amd_i2c_dw_xfer_quirk(struct dw_i2c_dev *dev, struct i2c_msg *msgs, int num_msgs)
|
||||
{
|
||||
struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
|
||||
int msg_wrt_idx, msg_itr_lmt, buf_len, data_idx;
|
||||
int cmd = 0, status;
|
||||
u8 *tx_buf;
|
||||
unsigned int val;
|
||||
|
||||
ACQUIRE(pm_runtime_active_auto_try, pm)(dev->dev);
|
||||
if (ACQUIRE_ERR(pm_runtime_active_auto_try, &pm))
|
||||
return -ENXIO;
|
||||
|
||||
/*
|
||||
* In order to enable the interrupt for UCSI i.e. AMD NAVI GPU card,
|
||||
* it is mandatory to set the right value in specific register
|
||||
|
|
@ -571,7 +510,7 @@ i2c_dw_recv_len(struct dw_i2c_dev *dev, u8 len)
|
|||
* after receiving the first byte.
|
||||
*/
|
||||
len += (flags & I2C_CLIENT_PEC) ? 2 : 1;
|
||||
dev->tx_buf_len = len - min_t(u8, len, dev->rx_outstanding);
|
||||
dev->tx_buf_len = len - min(len, dev->rx_outstanding);
|
||||
msgs[dev->msg_read_idx].len = len;
|
||||
msgs[dev->msg_read_idx].flags &= ~I2C_M_RECV_LEN;
|
||||
|
||||
|
|
@ -593,11 +532,12 @@ i2c_dw_read(struct dw_i2c_dev *dev)
|
|||
unsigned int rx_valid;
|
||||
|
||||
for (; dev->msg_read_idx < dev->msgs_num; dev->msg_read_idx++) {
|
||||
u32 flags = msgs[dev->msg_read_idx].flags;
|
||||
unsigned int tmp;
|
||||
u32 len;
|
||||
u8 *buf;
|
||||
|
||||
if (!(msgs[dev->msg_read_idx].flags & I2C_M_RD))
|
||||
if (!(flags & I2C_M_RD))
|
||||
continue;
|
||||
|
||||
if (!(dev->status & STATUS_READ_IN_PROGRESS)) {
|
||||
|
|
@ -611,8 +551,6 @@ i2c_dw_read(struct dw_i2c_dev *dev)
|
|||
regmap_read(dev->map, DW_IC_RXFLR, &rx_valid);
|
||||
|
||||
for (; len > 0 && rx_valid > 0; len--, rx_valid--) {
|
||||
u32 flags = msgs[dev->msg_read_idx].flags;
|
||||
|
||||
regmap_read(dev->map, DW_IC_DATA_CMD, &tmp);
|
||||
tmp &= DW_IC_DATA_CMD_DAT;
|
||||
/* Ensure length byte is a valid value */
|
||||
|
|
@ -749,9 +687,8 @@ tx_aborted:
|
|||
* Interrupt service routine. This gets called whenever an I2C master interrupt
|
||||
* occurs.
|
||||
*/
|
||||
static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
|
||||
irqreturn_t i2c_dw_isr_master(struct dw_i2c_dev *dev)
|
||||
{
|
||||
struct dw_i2c_dev *dev = dev_id;
|
||||
unsigned int stat, enabled;
|
||||
|
||||
regmap_read(dev->map, DW_IC_ENABLE, &enabled);
|
||||
|
|
@ -812,23 +749,14 @@ static int i2c_dw_wait_transfer(struct dw_i2c_dev *dev)
|
|||
* Prepare controller for a transaction and call i2c_dw_xfer_msg.
|
||||
*/
|
||||
static int
|
||||
i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
||||
i2c_dw_xfer_common(struct dw_i2c_dev *dev, struct i2c_msg msgs[], int num)
|
||||
{
|
||||
struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
|
||||
int ret;
|
||||
|
||||
dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num);
|
||||
|
||||
pm_runtime_get_sync(dev->dev);
|
||||
|
||||
switch (dev->flags & MODEL_MASK) {
|
||||
case MODEL_AMD_NAVI_GPU:
|
||||
ret = amd_i2c_dw_xfer_quirk(adap, msgs, num);
|
||||
goto done_nolock;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
reinit_completion(&dev->cmd_complete);
|
||||
dev->msgs = msgs;
|
||||
dev->msgs_num = num;
|
||||
|
|
@ -855,9 +783,9 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
|||
ret = i2c_dw_wait_transfer(dev);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "controller timed out\n");
|
||||
/* i2c_dw_init_master() implicitly disables the adapter */
|
||||
/* i2c_dw_init() implicitly disables the adapter */
|
||||
i2c_recover_bus(&dev->adapter);
|
||||
i2c_dw_init_master(dev);
|
||||
i2c_dw_init(dev);
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
|
@ -905,6 +833,8 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
|||
ret = -EIO;
|
||||
|
||||
done:
|
||||
i2c_dw_set_mode(dev, DW_IC_SLAVE);
|
||||
|
||||
i2c_dw_release_lock(dev);
|
||||
|
||||
done_nolock:
|
||||
|
|
@ -913,20 +843,21 @@ done_nolock:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm i2c_dw_algo = {
|
||||
.xfer = i2c_dw_xfer,
|
||||
.functionality = i2c_dw_func,
|
||||
};
|
||||
int i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
||||
{
|
||||
struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
|
||||
|
||||
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");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -21,74 +21,33 @@
|
|||
|
||||
#include "i2c-designware-core.h"
|
||||
|
||||
static void i2c_dw_configure_fifo_slave(struct dw_i2c_dev *dev)
|
||||
{
|
||||
/* 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)
|
||||
int i2c_dw_reg_slave(struct i2c_client *slave)
|
||||
{
|
||||
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");
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -325,12 +324,6 @@
|
|||
.name = (str) \
|
||||
}
|
||||
|
||||
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),
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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++;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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, },
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue