diff options
31 files changed, 674 insertions, 259 deletions
diff --git a/Documentation/devicetree/bindings/i2c/i2c-meson.txt b/Documentation/devicetree/bindings/i2c/i2c-meson.txt index 386357d1aab0..611b934c7e10 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-meson.txt +++ b/Documentation/devicetree/bindings/i2c/i2c-meson.txt @@ -8,6 +8,8 @@ Required properties: - #address-cells: should be <1> - #size-cells: should be <0> +For details regarding the following core I2C bindings see also i2c.txt. + Optional properties: - clock-frequency: the desired I2C bus clock frequency in Hz; in absence of this property the default value is used (100 kHz). diff --git a/Documentation/devicetree/bindings/i2c/i2c-mux-ltc4306.txt b/Documentation/devicetree/bindings/i2c/i2c-mux-ltc4306.txt new file mode 100644 index 000000000000..1e98c6b3a721 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-mux-ltc4306.txt @@ -0,0 +1,61 @@ +* Linear Technology / Analog Devices I2C bus switch + +Required Properties: + + - compatible: Must contain one of the following. + "lltc,ltc4305", "lltc,ltc4306" + - reg: The I2C address of the device. + + The following required properties are defined externally: + + - Standard I2C mux properties. See i2c-mux.txt in this directory. + - I2C child bus nodes. See i2c-mux.txt in this directory. + +Optional Properties: + + - enable-gpios: Reference to the GPIO connected to the enable input. + - i2c-mux-idle-disconnect: Boolean; if defined, forces mux to disconnect all + children in idle state. This is necessary for example, if there are several + multiplexers on the bus and the devices behind them use same I2C addresses. + - gpio-controller: Marks the device node as a GPIO Controller. + - #gpio-cells: Should be two. The first cell is the pin number and + the second cell is used to specify flags. + See ../gpio/gpio.txt for more information. + - ltc,downstream-accelerators-enable: Enables the rise time accelerators + on the downstream port. + - ltc,upstream-accelerators-enable: Enables the rise time accelerators + on the upstream port. + +Example: + + ltc4306: i2c-mux@4a { + compatible = "lltc,ltc4306"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x4a>; + + gpio-controller; + #gpio-cells = <2>; + + i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + eeprom@50 { + compatible = "at,24c02"; + reg = <0x50>; + }; + }; + + i2c@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + + eeprom@50 { + compatible = "at,24c02"; + reg = <0x50>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/i2c/i2c-rk3x.txt b/Documentation/devicetree/bindings/i2c/i2c-rk3x.txt index bbc5a1ed5fa1..e18445d0980c 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-rk3x.txt +++ b/Documentation/devicetree/bindings/i2c/i2c-rk3x.txt @@ -11,6 +11,7 @@ Required properties : - "rockchip,rk3188-i2c": for rk3188 - "rockchip,rk3228-i2c": for rk3228 - "rockchip,rk3288-i2c": for rk3288 + - "rockchip,rk3328-i2c", "rockchip,rk3399-i2c": for rk3328 - "rockchip,rk3399-i2c": for rk3399 - interrupts : interrupt number - clocks: See ../clock/clock-bindings.txt diff --git a/MAINTAINERS b/MAINTAINERS index 7046de618fdd..95803b7b61be 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7775,6 +7775,14 @@ S: Maintained F: Documentation/hwmon/ltc4261 F: drivers/hwmon/ltc4261.c +LTC4306 I2C MULTIPLEXER DRIVER +M: Michael Hennerich <michael.hennerich@analog.com> +W: http://ez.analog.com/community/linux-device-drivers +L: linux-i2c@vger.kernel.org +S: Supported +F: drivers/i2c/muxes/i2c-mux-ltc4306.c +F: Documentation/devicetree/bindings/i2c/i2c-mux-ltc4306.txt + LTP (Linux Test Project) M: Mike Frysinger <vapier@gentoo.org> M: Cyril Hrubis <chrubis@suse.cz> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 8adc0f1d7ad0..144cbadc7c72 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -933,6 +933,7 @@ config I2C_TEGRA config I2C_TEGRA_BPMP tristate "NVIDIA Tegra BPMP I2C controller" depends on TEGRA_BPMP + default y help If you say yes to this option, support will be included for the I2C controller embedded in NVIDIA Tegra SoCs accessed via the BPMP. @@ -1021,7 +1022,7 @@ config I2C_XLR config I2C_XLP9XX tristate "XLP9XX I2C support" - depends on CPU_XLP || ARCH_VULCAN || COMPILE_TEST + depends on CPU_XLP || ARCH_THUNDER2 || COMPILE_TEST help This driver enables support for the on-chip I2C interface of the Broadcom XLP9xx/XLP5xx MIPS and Vulcan ARM64 processors. diff --git a/drivers/i2c/busses/i2c-designware-baytrail.c b/drivers/i2c/busses/i2c-designware-baytrail.c index 1749a0f5a9fa..dbda8c9c8a1c 100644 --- a/drivers/i2c/busses/i2c-designware-baytrail.c +++ b/drivers/i2c/busses/i2c-designware-baytrail.c @@ -22,7 +22,7 @@ #include "i2c-designware-core.h" -#define SEMAPHORE_TIMEOUT 100 +#define SEMAPHORE_TIMEOUT 500 #define PUNIT_SEMAPHORE 0x7 #define PUNIT_SEMAPHORE_CHT 0x10e #define PUNIT_SEMAPHORE_BIT BIT(0) @@ -70,7 +70,7 @@ static void reset_semaphore(struct dw_i2c_dev *dev) static int baytrail_i2c_acquire(struct dw_i2c_dev *dev) { - u32 addr = get_sem_addr(dev); + u32 addr; u32 sem = PUNIT_SEMAPHORE_ACQUIRE; int ret; unsigned long start, end; @@ -94,6 +94,8 @@ static int baytrail_i2c_acquire(struct dw_i2c_dev *dev) */ pm_qos_update_request(&dev->pm_qos, 0); + addr = get_sem_addr(dev); + /* host driver writes to side band semaphore register */ ret = iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_REG_WRITE, addr, sem); if (ret) { @@ -170,7 +172,7 @@ int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev) dev_info(dev->dev, "I2C bus managed by PUNIT\n"); dev->acquire_lock = baytrail_i2c_acquire; dev->release_lock = baytrail_i2c_release; - dev->pm_runtime_disabled = true; + dev->pm_disabled = true; pm_qos_add_request(&dev->pm_qos, PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c index 15a534818d4f..c453717b753b 100644 --- a/drivers/i2c/busses/i2c-designware-core.c +++ b/drivers/i2c/busses/i2c-designware-core.c @@ -960,6 +960,7 @@ EXPORT_SYMBOL_GPL(i2c_dw_read_comp_param); int i2c_dw_probe(struct dw_i2c_dev *dev) { struct i2c_adapter *adap = &dev->adapter; + unsigned long irq_flags; int r; init_completion(&dev->cmd_complete); @@ -975,9 +976,15 @@ int i2c_dw_probe(struct dw_i2c_dev *dev) adap->dev.parent = dev->dev; i2c_set_adapdata(adap, dev); + if (dev->pm_disabled) { + dev_pm_syscore_device(dev->dev, true); + irq_flags = IRQF_NO_SUSPEND; + } else { + irq_flags = IRQF_SHARED | IRQF_COND_SUSPEND; + } + i2c_dw_disable_int(dev); - r = devm_request_irq(dev->dev, dev->irq, i2c_dw_isr, - IRQF_SHARED | IRQF_COND_SUSPEND, + r = devm_request_irq(dev->dev, dev->irq, i2c_dw_isr, irq_flags, dev_name(dev->dev), dev); if (r) { dev_err(dev->dev, "failure requesting irq %i: %d\n", diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h index 846ea57f85af..a7cf429daf60 100644 --- a/drivers/i2c/busses/i2c-designware-core.h +++ b/drivers/i2c/busses/i2c-designware-core.h @@ -79,7 +79,7 @@ * @pm_qos: pm_qos_request used while holding a hardware lock on the bus * @acquire_lock: function to acquire a hardware lock on the bus * @release_lock: function to release a hardware lock on the bus - * @pm_runtime_disabled: true if pm runtime is disabled + * @pm_disabled: true if power-management should be disabled for this i2c-bus * * HCNT and LCNT parameters can be used if the platform knows more accurate * values than the one computed based only on the input clock frequency. @@ -128,7 +128,7 @@ struct dw_i2c_dev { struct pm_qos_request pm_qos; int (*acquire_lock)(struct dw_i2c_dev *dev); void (*release_lock)(struct dw_i2c_dev *dev); - bool pm_runtime_disabled; + bool pm_disabled; }; #define ACCESS_SWAP 0x00000001 diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index d8665098dce9..5a4eb6b6bd92 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -85,8 +85,7 @@ static void dw_i2c_acpi_params(struct platform_device *pdev, char method[], *hcnt = (u16)objs[0].integer.value; *lcnt = (u16)objs[1].integer.value; - if (sda_hold) - *sda_hold = (u32)objs[2].integer.value; + *sda_hold = (u32)objs[2].integer.value; } kfree(buf.pointer); @@ -95,26 +94,55 @@ static void dw_i2c_acpi_params(struct platform_device *pdev, char method[], static int dw_i2c_acpi_configure(struct platform_device *pdev) { struct dw_i2c_dev *dev = platform_get_drvdata(pdev); + acpi_handle handle = ACPI_HANDLE(&pdev->dev); const struct acpi_device_id *id; + struct acpi_device *adev; + const char *uid; dev->adapter.nr = -1; dev->tx_fifo_depth = 32; dev->rx_fifo_depth = 32; /* - * Try to get SDA hold time and *CNT values from an ACPI method if - * it exists for both supported speed modes. + * Try to get SDA hold time and *CNT values from an ACPI method for + * selected speed modes. */ - dw_i2c_acpi_params(pdev, "SSCN", &dev->ss_hcnt, &dev->ss_lcnt, NULL); - dw_i2c_acpi_params(pdev, "FMCN", &dev->fs_hcnt, &dev->fs_lcnt, - &dev->sda_hold_time); - dw_i2c_acpi_params(pdev, "FPCN", &dev->fp_hcnt, &dev->fp_lcnt, NULL); - dw_i2c_acpi_params(pdev, "HSCN", &dev->hs_hcnt, &dev->hs_lcnt, NULL); + switch (dev->clk_freq) { + case 100000: + dw_i2c_acpi_params(pdev, "SSCN", &dev->ss_hcnt, &dev->ss_lcnt, + &dev->sda_hold_time); + break; + case 1000000: + dw_i2c_acpi_params(pdev, "FPCN", &dev->fp_hcnt, &dev->fp_lcnt, + &dev->sda_hold_time); + break; + case 3400000: + dw_i2c_acpi_params(pdev, "HSCN", &dev->hs_hcnt, &dev->hs_lcnt, + &dev->sda_hold_time); + break; + case 400000: + default: + dw_i2c_acpi_params(pdev, "FMCN", &dev->fs_hcnt, &dev->fs_lcnt, + &dev->sda_hold_time); + break; + } id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev); if (id && id->driver_data) dev->flags |= (u32)id->driver_data; + if (acpi_bus_get_device(handle, &adev)) + return -ENODEV; + + /* + * Cherrytrail I2C7 gets used for the PMIC which gets accessed + * through ACPI opregions during late suspend / early resume + * disable pm for it. + */ + uid = adev->pnp.unique_id; + if ((dev->flags & MODEL_CHERRYTRAIL) && !strcmp(uid, "7")) + dev->pm_disabled = true; + return 0; } @@ -286,7 +314,7 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev)); adap->dev.of_node = pdev->dev.of_node; - if (dev->pm_runtime_disabled) { + if (dev->pm_disabled) { pm_runtime_forbid(&pdev->dev); } else { pm_runtime_set_autosuspend_delay(&pdev->dev, 1000); @@ -302,7 +330,7 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) return r; exit_probe: - if (!dev->pm_runtime_disabled) + if (!dev->pm_disabled) pm_runtime_disable(&pdev->dev); exit_reset: if (!IS_ERR_OR_NULL(dev->rst)) @@ -322,7 +350,7 @@ static int dw_i2c_plat_remove(struct platform_device *pdev) pm_runtime_dont_use_autosuspend(&pdev->dev); pm_runtime_put_sync(&pdev->dev); - if (!dev->pm_runtime_disabled) + if (!dev->pm_disabled) pm_runtime_disable(&pdev->dev); if (!IS_ERR_OR_NULL(dev->rst)) reset_control_assert(dev->rst); @@ -374,9 +402,7 @@ static int dw_i2c_plat_resume(struct device *dev) struct dw_i2c_dev *i_dev = platform_get_drvdata(pdev); i2c_dw_plat_prepare_clk(i_dev, true); - - if (!i_dev->pm_runtime_disabled) - i2c_dw_init(i_dev); + i2c_dw_init(i_dev); return 0; } diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c index 736a82472101..23ed4d67ecad 100644 --- a/drivers/i2c/busses/i2c-exynos5.c +++ b/drivers/i2c/busses/i2c-exynos5.c @@ -22,6 +22,7 @@ #include <linux/slab.h> #include <linux/io.h> #include <linux/of_address.h> +#include <linux/of_device.h> #include <linux/of_irq.h> #include <linux/spinlock.h> @@ -168,8 +169,6 @@ */ #define HSI2C_HS_TX_CLOCK 1000000 #define HSI2C_FS_TX_CLOCK 100000 -#define HSI2C_HIGH_SPD 1 -#define HSI2C_FAST_SPD 0 #define EXYNOS5_I2C_TIMEOUT (msecs_to_jiffies(1000)) @@ -200,18 +199,10 @@ struct exynos5_i2c { int trans_done; /* Controller operating frequency */ - unsigned int fs_clock; - unsigned int hs_clock; - - /* - * HSI2C Controller can operate in - * 1. High speed upto 3.4Mbps - * 2. Fast speed upto 1Mbps - */ - int speed_mode; + unsigned int op_clock; /* Version of HS-I2C Hardware */ - struct exynos_hsi2c_variant *variant; + const struct exynos_hsi2c_variant *variant; }; /** @@ -257,15 +248,6 @@ static const struct of_device_id exynos5_i2c_match[] = { }; MODULE_DEVICE_TABLE(of, exynos5_i2c_match); -static inline struct exynos_hsi2c_variant *exynos5_i2c_get_variant - (struct platform_device *pdev) -{ - const struct of_device_id *match; - - match = of_match_node(exynos5_i2c_match, pdev->dev.of_node); - return (struct exynos_hsi2c_variant *)match->data; -} - static void exynos5_i2c_clr_pend_irq(struct exynos5_i2c *i2c) { writel(readl(i2c->regs + HSI2C_INT_STATUS), @@ -279,7 +261,7 @@ static void exynos5_i2c_clr_pend_irq(struct exynos5_i2c *i2c) * Returns 0 on success, -EINVAL if the cycle length cannot * be calculated. */ -static int exynos5_i2c_set_timing(struct exynos5_i2c *i2c, int mode) +static int exynos5_i2c_set_timing(struct exynos5_i2c *i2c, bool hs_timings) { u32 i2c_timing_s1; u32 i2c_timing_s2; @@ -292,9 +274,10 @@ static int exynos5_i2c_set_timing(struct exynos5_i2c *i2c, int mode) unsigned int t_sr_release; unsigned int t_ftl_cycle; unsigned int clkin = clk_get_rate(i2c->clk); - unsigned int div, utemp0 = 0, utemp1 = 0, clk_cycle; - unsigned int op_clk = (mode == HSI2C_HIGH_SPD) ? - i2c->hs_clock : i2c->fs_clock; + unsigned int op_clk = hs_timings ? i2c->op_clock : + (i2c->op_clock >= HSI2C_HS_TX_CLOCK) ? HSI2C_FS_TX_CLOCK : + i2c->op_clock; + int div, clk_cycle, temp; /* * In case of HSI2C controller in Exynos5 series @@ -305,33 +288,22 @@ static int exynos5_i2c_set_timing(struct exynos5_i2c *i2c, int mode) * FPCLK / FI2C = * (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2) + 8 + FLT_CYCLE * - * utemp0 = (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2) - * utemp1 = (TSCLK_L + TSCLK_H + 2) + * clk_cycle := TSCLK_L + TSCLK_H + * temp := (CLK_DIV + 1) * (clk_cycle + 2) + * + * Constraints: 4 <= temp, 0 <= CLK_DIV < 256, 2 <= clk_cycle <= 510 + * */ t_ftl_cycle = (readl(i2c->regs + HSI2C_CONF) >> 16) & 0x7; - utemp0 = (clkin / op_clk) - 8; - - if (i2c->variant->hw == HSI2C_EXYNOS7) - utemp0 -= t_ftl_cycle; - else - utemp0 -= 2 * t_ftl_cycle; - - /* CLK_DIV max is 256 */ - for (div = 0; div < 256; div++) { - utemp1 = utemp0 / (div + 1); - - /* - * SCL_L and SCL_H each has max value of 255 - * Hence, For the clk_cycle to the have right value - * utemp1 has to be less then 512 and more than 4. - */ - if ((utemp1 < 512) && (utemp1 > 4)) { - clk_cycle = utemp1 - 2; - break; - } else if (div == 255) { - dev_warn(i2c->dev, "Failed to calculate divisor"); - return -EINVAL; - } + temp = clkin / op_clk - 8 - t_ftl_cycle; + if (i2c->variant->hw != HSI2C_EXYNOS7) + temp -= t_ftl_cycle; + div = temp / 512; + clk_cycle = temp / (div + 1) - 2; + if (temp < 4 || div >= 256 || clk_cycle < 2) { + dev_err(i2c->dev, "%s clock set-up failed\n", + hs_timings ? "HS" : "FS"); + return -EINVAL; } t_scl_l = clk_cycle / 2; @@ -356,7 +328,7 @@ static int exynos5_i2c_set_timing(struct exynos5_i2c *i2c, int mode) div, t_sr_release); dev_dbg(i2c->dev, "tDATA_HD: %X\n", t_data_hd); - if (mode == HSI2C_HIGH_SPD) { + if (hs_timings) { writel(i2c_timing_s1, i2c->regs + HSI2C_TIMING_HS1); writel(i2c_timing_s2, i2c->regs + HSI2C_TIMING_HS2); writel(i2c_timing_s3, i2c->regs + HSI2C_TIMING_HS3); @@ -372,24 +344,13 @@ static int exynos5_i2c_set_timing(struct exynos5_i2c *i2c, int mode) static int exynos5_hsi2c_clock_setup(struct exynos5_i2c *i2c) { - /* - * Configure the Fast speed timing values - * Even the High Speed mode initially starts with Fast mode - */ - if (exynos5_i2c_set_timing(i2c, HSI2C_FAST_SPD)) { - dev_err(i2c->dev, "HSI2C FS Clock set up failed\n"); - return -EINVAL; - } + /* always set Fast Speed timings */ + int ret = exynos5_i2c_set_timing(i2c, false); - /* configure the High speed timing values */ - if (i2c->speed_mode == HSI2C_HIGH_SPD) { - if (exynos5_i2c_set_timing(i2c, HSI2C_HIGH_SPD)) { - dev_err(i2c->dev, "HSI2C HS Clock set up failed\n"); - return -EINVAL; - } - } + if (ret < 0 || i2c->op_clock < HSI2C_HS_TX_CLOCK) + return ret; - return 0; + return exynos5_i2c_set_timing(i2c, true); } /* @@ -409,7 +370,7 @@ static void exynos5_i2c_init(struct exynos5_i2c *i2c) i2c->regs + HSI2C_CTL); writel(HSI2C_TRAILING_COUNT, i2c->regs + HSI2C_TRAILIG_CTL); - if (i2c->speed_mode == HSI2C_HIGH_SPD) { + if (i2c->op_clock >= HSI2C_HS_TX_CLOCK) { writel(HSI2C_MASTER_ID(MASTER_ID(i2c->adap.nr)), i2c->regs + HSI2C_ADDR); i2c_conf |= HSI2C_HS_MODE; @@ -747,26 +708,14 @@ static int exynos5_i2c_probe(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; struct exynos5_i2c *i2c; struct resource *mem; - unsigned int op_clock; int ret; i2c = devm_kzalloc(&pdev->dev, sizeof(struct exynos5_i2c), GFP_KERNEL); if (!i2c) return -ENOMEM; - if (of_property_read_u32(np, "clock-frequency", &op_clock)) { - i2c->speed_mode = HSI2C_FAST_SPD; - i2c->fs_clock = HSI2C_FS_TX_CLOCK; - } else { - if (op_clock >= HSI2C_HS_TX_CLOCK) { - i2c->speed_mode = HSI2C_HIGH_SPD; - i2c->fs_clock = HSI2C_FS_TX_CLOCK; - i2c->hs_clock = op_clock; - } else { - i2c->speed_mode = HSI2C_FAST_SPD; - i2c->fs_clock = op_clock; - } - } + if (of_property_read_u32(np, "clock-frequency", &i2c->op_clock)) + i2c->op_clock = HSI2C_FS_TX_CLOCK; strlcpy(i2c->adap.name, "exynos5-i2c", sizeof(i2c->adap.name)); i2c->adap.owner = THIS_MODULE; @@ -817,8 +766,7 @@ static int exynos5_i2c_probe(struct platform_device *pdev) goto err_clk; } - /* Need to check the variant before setting up. */ - i2c->variant = exynos5_i2c_get_variant(pdev); + i2c->variant = of_device_get_match_data(&pdev->dev); ret = exynos5_hsi2c_clock_setup(i2c); if (ret) diff --git a/drivers/i2c/busses/i2c-img-scb.c b/drivers/i2c/busses/i2c-img-scb.c index db8e8b40569d..84fb35f6837f 100644 --- a/drivers/i2c/busses/i2c-img-scb.c +++ b/drivers/i2c/busses/i2c-img-scb.c @@ -1362,9 +1362,8 @@ static int img_i2c_probe(struct platform_device *pdev) } /* Set up the exception check timer */ - init_timer(&i2c->check_timer); - i2c->check_timer.function = img_i2c_check_timer; - i2c->check_timer.data = (unsigned long)i2c; + setup_timer(&i2c->check_timer, img_i2c_check_timer, + (unsigned long)i2c); i2c->bitrate = timings[0].max_bitrate; if (!of_property_read_u32(node, "clock-frequency", &val)) diff --git a/drivers/i2c/busses/i2c-meson.c b/drivers/i2c/busses/i2c-meson.c index 73b97c71a484..88d15b92ec35 100644 --- a/drivers/i2c/busses/i2c-meson.c +++ b/drivers/i2c/busses/i2c-meson.c @@ -35,10 +35,11 @@ #define REG_CTRL_STATUS BIT(2) #define REG_CTRL_ERROR BIT(3) #define REG_CTRL_CLKDIV_SHIFT 12 -#define REG_CTRL_CLKDIV_MASK ((BIT(10) - 1) << REG_CTRL_CLKDIV_SHIFT) +#define REG_CTRL_CLKDIV_MASK GENMASK(21, 12) +#define REG_CTRL_CLKDIVEXT_SHIFT 28 +#define REG_CTRL_CLKDIVEXT_MASK GENMASK(29, 28) #define I2C_TIMEOUT_MS 500 -#define DEFAULT_FREQ 100000 enum { TOKEN_END = 0, @@ -54,7 +55,6 @@ enum { STATE_IDLE, STATE_READ, STATE_WRITE, - STATE_STOP, }; /** @@ -73,7 +73,6 @@ enum { * @error: Flag set when an error is received * @lock: To avoid race conditions between irq handler and xfer code * @done: Completion used to wait for transfer termination - * @frequency: Operating frequency of I2C bus clock * @tokens: Sequence of tokens to be written to the device * @num_tokens: Number of tokens */ @@ -82,7 +81,6 @@ struct meson_i2c { struct device *dev; void __iomem *regs; struct clk *clk; - int irq; struct i2c_msg *msg; int state; @@ -93,7 +91,6 @@ struct meson_i2c { spinlock_t lock; struct completion done; - unsigned int frequency; u32 tokens[2]; int num_tokens; }; @@ -126,23 +123,27 @@ static void meson_i2c_add_token(struct meson_i2c *i2c, int token) i2c->num_tokens++; } -static void meson_i2c_write_tokens(struct meson_i2c *i2c) -{ - writel(i2c->tokens[0], i2c->regs + REG_TOK_LIST0); - writel(i2c->tokens[1], i2c->regs + REG_TOK_LIST1); -} - -static void meson_i2c_set_clk_div(struct meson_i2c *i2c) +static void meson_i2c_set_clk_div(struct meson_i2c *i2c, unsigned int freq) { unsigned long clk_rate = clk_get_rate(i2c->clk); unsigned int div; - div = DIV_ROUND_UP(clk_rate, i2c->frequency * 4); + div = DIV_ROUND_UP(clk_rate, freq * 4); + + /* clock divider has 12 bits */ + if (div >= (1 << 12)) { + dev_err(i2c->dev, "requested bus frequency too low\n"); + div = (1 << 12) - 1; + } + meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_CLKDIV_MASK, - div << REG_CTRL_CLKDIV_SHIFT); + (div & GENMASK(9, 0)) << REG_CTRL_CLKDIV_SHIFT); + + meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_CLKDIVEXT_MASK, + (div >> 10) << REG_CTRL_CLKDIVEXT_SHIFT); dev_dbg(i2c->dev, "%s: clk %lu, freq %u, div %u\n", __func__, - clk_rate, i2c->frequency, div); + clk_rate, freq, div); } static void meson_i2c_get_data(struct meson_i2c *i2c, char *buf, int len) @@ -156,10 +157,10 @@ static void meson_i2c_get_data(struct meson_i2c *i2c, char *buf, int len) dev_dbg(i2c->dev, "%s: data %08x %08x len %d\n", __func__, rdata0, rdata1, len); - for (i = 0; i < min_t(int, 4, len); i++) + for (i = 0; i < min(4, len); i++) *buf++ = (rdata0 >> i * 8) & 0xff; - for (i = 4; i < min_t(int, 8, len); i++) + for (i = 4; i < min(8, len); i++) *buf++ = (rdata1 >> (i - 4) * 8) & 0xff; } @@ -168,10 +169,10 @@ static void meson_i2c_put_data(struct meson_i2c *i2c, char *buf, int len) u32 wdata0 = 0, wdata1 = 0; int i; - for (i = 0; i < min_t(int, 4, len); i++) + for (i = 0; i < min(4, len); i++) wdata0 |= *buf++ << (i * 8); - for (i = 4; i < min_t(int, 8, len); i++) + for (i = 4; i < min(8, len); i++) wdata1 |= *buf++ << ((i - 4) * 8); writel(wdata0, i2c->regs + REG_TOK_WDATA0); @@ -186,7 +187,7 @@ static void meson_i2c_prepare_xfer(struct meson_i2c *i2c) bool write = !(i2c->msg->flags & I2C_M_RD); int i; - i2c->count = min_t(int, i2c->msg->len - i2c->pos, 8); + i2c->count = min(i2c->msg->len - i2c->pos, 8); for (i = 0; i < i2c->count - 1; i++) meson_i2c_add_token(i2c, TOKEN_DATA); @@ -200,19 +201,12 @@ static void meson_i2c_prepare_xfer(struct meson_i2c *i2c) if (write) meson_i2c_put_data(i2c, i2c->msg->buf + i2c->pos, i2c->count); -} - -static void meson_i2c_stop(struct meson_i2c *i2c) -{ - dev_dbg(i2c->dev, "%s: last %d\n", __func__, i2c->last); - if (i2c->last) { - i2c->state = STATE_STOP; + if (i2c->last && i2c->pos + i2c->count >= i2c->msg->len) meson_i2c_add_token(i2c, TOKEN_STOP); - } else { - i2c->state = STATE_IDLE; - complete(&i2c->done); - } + + writel(i2c->tokens[0], i2c->regs + REG_TOK_LIST0); + writel(i2c->tokens[1], i2c->regs + REG_TOK_LIST1); } static irqreturn_t meson_i2c_irq(int irqno, void *dev_id) @@ -223,12 +217,18 @@ static irqreturn_t meson_i2c_irq(int irqno, void *dev_id) spin_lock(&i2c->lock); meson_i2c_reset_tokens(i2c); + meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_START, 0); ctrl = readl(i2c->regs + REG_CTRL); dev_dbg(i2c->dev, "irq: state %d, pos %d, count %d, ctrl %08x\n", i2c->state, i2c->pos, i2c->count, ctrl); - if (ctrl & REG_CTRL_ERROR && i2c->state != STATE_IDLE) { + if (i2c->state == STATE_IDLE) { + spin_unlock(&i2c->lock); + return IRQ_NONE; + } + + if (ctrl & REG_CTRL_ERROR) { /* * The bit is set when the IGNORE_NAK bit is cleared * and the device didn't respond. In this case, the @@ -242,48 +242,21 @@ static irqreturn_t meson_i2c_irq(int irqno, void *dev_id) goto out; } - switch (i2c->state) { - case STATE_READ: - if (i2c->count > 0) { - meson_i2c_get_data(i2c, i2c->msg->buf + i2c->pos, - i2c->count); - i2c->pos += i2c->count; - } - - if (i2c->pos >= i2c->msg->len) { - meson_i2c_stop(i2c); - break; - } - - meson_i2c_prepare_xfer(i2c); - break; - case STATE_WRITE: - i2c->pos += i2c->count; + if (i2c->state == STATE_READ && i2c->count) + meson_i2c_get_data(i2c, i2c->msg->buf + i2c->pos, i2c->count); - if (i2c->pos >= i2c->msg->len) { - meson_i2c_stop(i2c); - break; - } + i2c->pos += i2c->count; - meson_i2c_prepare_xfer(i2c); - break; - case STATE_STOP: + if (i2c->pos >= i2c->msg->len) { i2c->state = STATE_IDLE; complete(&i2c->done); - break; - case STATE_IDLE: - break; + goto out; } + /* Restart the processing */ + meson_i2c_prepare_xfer(i2c); + meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_START, REG_CTRL_START); out: - if (i2c->state != STATE_IDLE) { - /* Restart the processing */ - meson_i2c_write_tokens(i2c); - meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_START, 0); - meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_START, - REG_CTRL_START); - } - spin_unlock(&i2c->lock); return IRQ_HANDLED; @@ -323,7 +296,6 @@ static int meson_i2c_xfer_msg(struct meson_i2c *i2c, struct i2c_msg *msg, i2c->state = (msg->flags & I2C_M_RD) ? STATE_READ : STATE_WRITE; meson_i2c_prepare_xfer(i2c); - meson_i2c_write_tokens(i2c); reinit_completion(&i2c->done); /* Start the transfer */ @@ -359,21 +331,19 @@ static int meson_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { struct meson_i2c *i2c = adap->algo_data; - int i, ret = 0, count = 0; + int i, ret = 0; clk_enable(i2c->clk); - meson_i2c_set_clk_div(i2c); for (i = 0; i < num; i++) { ret = meson_i2c_xfer_msg(i2c, msgs + i, i == num - 1); if (ret) break; - count++; } clk_disable(i2c->clk); - return ret ? ret : count; + return ret ?: i; } static u32 meson_i2c_func(struct i2c_adapter *adap) @@ -391,15 +361,14 @@ static int meson_i2c_probe(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; struct meson_i2c *i2c; struct resource *mem; - int ret = 0; + struct i2c_timings timings; + int irq, ret = 0; i2c = devm_kzalloc(&pdev->dev, sizeof(struct meson_i2c), GFP_KERNEL); if (!i2c) return -ENOMEM; - if (of_property_read_u32(pdev->dev.of_node, "clock-frequency", - &i2c->frequency)) - i2c->frequency = DEFAULT_FREQ; + i2c_parse_fw_timings(&pdev->dev, &timings, true); i2c->dev = &pdev->dev; platform_set_drvdata(pdev, i2c); @@ -418,14 +387,13 @@ static int meson_i2c_probe(struct platform_device *pdev) if (IS_ERR(i2c->regs)) return PTR_ERR(i2c->regs); - i2c->irq = platform_get_irq(pdev, 0); - if (i2c->irq < 0) { + irq = platform_get_irq(pdev, 0); + if (irq < 0) { dev_err(&pdev->dev, "can't find IRQ\n"); - return i2c->irq; + return irq; } - ret = devm_request_irq(&pdev->dev, i2c->irq, meson_i2c_irq, - 0, dev_name(&pdev->dev), i2c); + ret = devm_request_irq(&pdev->dev, irq, meson_i2c_irq, 0, NULL, i2c); if (ret < 0) { dev_err(&pdev->dev, "can't request IRQ\n"); return ret; @@ -457,6 +425,8 @@ static int meson_i2c_probe(struct platform_device *pdev) return ret; } + meson_i2c_set_clk_div(i2c, timings.bus_freq_hz); + return 0; } diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index a50bd6891e27..cf737ec8563b 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -823,13 +823,10 @@ mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data, drv_data->rstc = devm_reset_control_get_optional(dev, NULL); if (IS_ERR(drv_data->rstc)) { - if (PTR_ERR(drv_data->rstc) == -EPROBE_DEFER) { - rc = -EPROBE_DEFER; - goto out; - } - } else { - reset_control_deassert(drv_data->rstc); + rc = PTR_ERR(drv_data->rstc); + goto out; } + reset_control_deassert(drv_data->rstc); /* Its not yet defined how timeouts will be specified in device tree. * So hard code the value to 1 second. @@ -951,8 +948,7 @@ mv64xxx_i2c_probe(struct platform_device *pd) exit_free_irq: free_irq(drv_data->irq, drv_data); exit_reset: - if (!IS_ERR_OR_NULL(drv_data->rstc)) - reset_control_assert(drv_data->rstc); + reset_control_assert(drv_data->rstc); exit_clk: /* Not all platforms have a clk */ if (!IS_ERR(drv_data->clk)) @@ -968,8 +964,7 @@ mv64xxx_i2c_remove(struct platform_device *dev) i2c_del_adapter(&drv_data->adapter); free_irq(drv_data->irq, drv_data); - if (!IS_ERR_OR_NULL(drv_data->rstc)) - reset_control_assert(drv_data->rstc); + reset_control_assert(drv_data->rstc); /* Not all platforms have a clk */ if (!IS_ERR(drv_data->clk)) clk_disable_unprepare(drv_data->clk); diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index 26f2ff22e97e..214bf2835d1f 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -700,6 +700,8 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap, pm_runtime_get_sync(dev); + rcar_i2c_init(priv); + ret = rcar_i2c_bus_barrier(priv); if (ret < 0) goto out; @@ -751,6 +753,7 @@ static int rcar_reg_slave(struct i2c_client *slave) if (slave->flags & I2C_CLIENT_TEN) return -EAFNOSUPPORT; + /* Keep device active for slave address detection logic */ pm_runtime_get_sync(rcar_i2c_priv_to_dev(priv)); priv->slave = slave; @@ -854,15 +857,14 @@ static int rcar_i2c_probe(struct platform_device *pdev) priv->dma_direction = DMA_NONE; priv->dma_rx = priv->dma_tx = ERR_PTR(-EPROBE_DEFER); + /* Activate device for clock calculation */ pm_runtime_enable(dev); pm_runtime_get_sync(dev); ret = rcar_i2c_clock_calculate(priv, &i2c_t); if (ret < 0) goto out_pm_put; - rcar_i2c_init(priv); - - /* Don't suspend when multi-master to keep arbitration working */ + /* Stay always active when multi-master to keep arbitration working */ if (of_property_read_bool(dev->of_node, "multi-master")) priv->flags |= ID_P_PM_BLOCKED; else diff --git a/drivers/i2c/busses/i2c-scmi.c b/drivers/i2c/busses/i2c-scmi.c index dfc98df7b1b6..7aa7b9cb6203 100644 --- a/drivers/i2c/busses/i2c-scmi.c +++ b/drivers/i2c/busses/i2c-scmi.c @@ -18,6 +18,9 @@ #define ACPI_SMBUS_HC_CLASS "smbus" #define ACPI_SMBUS_HC_DEVICE_NAME "cmi" +/* SMBUS HID definition as supported by Microsoft Windows */ +#define ACPI_SMBUS_MS_HID "SMB0001" + ACPI_MODULE_NAME("smbus_cmi"); struct smbus_methods_t { @@ -51,6 +54,7 @@ static const struct smbus_methods_t ibm_smbus_methods = { static const struct acpi_device_id acpi_smbus_cmi_ids[] = { {"SMBUS01", (kernel_ulong_t)&smbus_methods}, {ACPI_SMBUS_IBM_HID, (kernel_ulong_t)&ibm_smbus_methods}, + {ACPI_SMBUS_MS_HID, (kernel_ulong_t)&smbus_methods}, {"", 0} }; MODULE_DEVICE_TABLE(acpi, acpi_smbus_cmi_ids); diff --git a/drivers/i2c/busses/i2c-tegra-bpmp.c b/drivers/i2c/busses/i2c-tegra-bpmp.c index 9eed69d5e17e..f6cd35d0a2ac 100644 --- a/drivers/i2c/busses/i2c-tegra-bpmp.c +++ b/drivers/i2c/busses/i2c-tegra-bpmp.c @@ -340,7 +340,7 @@ static struct platform_driver tegra_bpmp_i2c_driver = { }; module_platform_driver(tegra_bpmp_i2c_driver); -MODULE_DESCRIPTION("NVIDIA Tegra BPMP I2C bus contoller driver"); +MODULE_DESCRIPTION("NVIDIA Tegra BPMP I2C bus controller driver"); MODULE_AUTHOR("Shardar Shariff Md <smohammed@nvidia.com>"); MODULE_AUTHOR("Juha-Matti Tilli"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-thunderx-pcidrv.c b/drivers/i2c/busses/i2c-thunderx-pcidrv.c index 1d4c2beacf2e..ea35a895b568 100644 --- a/drivers/i2c/busses/i2c-thunderx-pcidrv.c +++ b/drivers/i2c/busses/i2c-thunderx-pcidrv.c @@ -85,17 +85,23 @@ static void thunder_i2c_clock_enable(struct device *dev, struct octeon_i2c *i2c) { int ret; - i2c->clk = clk_get(dev, NULL); - if (IS_ERR(i2c->clk)) { - i2c->clk = NULL; - goto skip; + if (acpi_disabled) { + /* DT */ + i2c->clk = clk_get(dev, NULL); + if (IS_ERR(i2c->clk)) { + i2c->clk = NULL; + goto skip; + } + + ret = clk_prepare_enable(i2c->clk); + if (ret) + goto skip; + i2c->sys_freq = clk_get_rate(i2c->clk); + } else { + /* ACPI */ + device_property_read_u32(dev, "sclk", &i2c->sys_freq); } - ret = clk_prepare_enable(i2c->clk); - if (ret) - goto skip; - i2c->sys_freq = clk_get_rate(i2c->clk); - skip: if (!i2c->sys_freq) i2c->sys_freq = SYS_FREQ_DEFAULT; @@ -205,6 +211,7 @@ static int thunder_i2c_probe_pci(struct pci_dev *pdev, i2c->adap = thunderx_i2c_ops; i2c->adap.retries = 5; + i2c->adap.class = I2C_CLASS_HWMON; i2c->adap.bus_recovery_info = &octeon_i2c_recovery_info; i2c->adap.dev.parent = dev; i2c->adap.dev.of_node = pdev->dev.of_node; diff --git a/drivers/i2c/busses/i2c-xlp9xx.c b/drivers/i2c/busses/i2c-xlp9xx.c index 66b464d52c9c..ae80228104e9 100644 --- a/drivers/i2c/busses/i2c-xlp9xx.c +++ b/drivers/i2c/busses/i2c-xlp9xx.c @@ -432,6 +432,7 @@ MODULE_DEVICE_TABLE(of, xlp9xx_i2c_of_match); #ifdef CONFIG_ACPI static const struct acpi_device_id xlp9xx_i2c_acpi_ids[] = { {"BRCM9007", 0}, + {"CAV9007", 0}, {} }; MODULE_DEVICE_TABLE(acpi, xlp9xx_i2c_acpi_ids); diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index d2402bbf6729..7a065c4260f3 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -112,6 +112,8 @@ struct i2c_acpi_lookup { acpi_handle adapter_handle; acpi_handle device_handle; acpi_handle search_handle; + int n; + int index; u32 speed; u32 min_speed; }; @@ -130,6 +132,9 @@ static int i2c_acpi_fill_info(struct acpi_resource *ares, void *data) if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_I2C) return 1; + if (lookup->index != -1 && lookup->n++ != lookup->index) + return 1; + status = acpi_get_handle(lookup->device_handle, sb->resource_source.string_ptr, &lookup->adapter_handle); @@ -182,6 +187,7 @@ static int i2c_acpi_get_info(struct acpi_device *adev, memset(&lookup, 0, sizeof(lookup)); lookup.info = info; + lookup.index = -1; ret = i2c_acpi_do_lookup(adev, &lookup); if (ret) @@ -328,6 +334,7 @@ u32 i2c_acpi_find_bus_speed(struct device *dev) lookup.search_handle = ACPI_HANDLE(dev); lookup.min_speed = UINT_MAX; lookup.info = &dummy; + lookup.index = -1; status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, I2C_ACPI_MAX_SCAN_DEPTH, @@ -414,6 +421,55 @@ static int i2c_acpi_notify(struct notifier_block *nb, unsigned long value, static struct notifier_block i2c_acpi_notifier = { .notifier_call = i2c_acpi_notify, }; + +/** + * i2c_acpi_new_device - Create i2c-client for the Nth I2cSerialBus resource + * @dev: Device owning the ACPI resources to get the client from + * @index: Index of ACPI resource to get + * @info: describes the I2C device; note this is modified (addr gets set) + * Context: can sleep + * + * By default the i2c subsys creates an i2c-client for the first I2cSerialBus + * resource of an acpi_device, but some acpi_devices have multiple I2cSerialBus + * resources, in that case this function can be used to create an i2c-client + * for other I2cSerialBus resources in the Current Resource Settings table. + * + * Also see i2c_new_device, which this function calls to create the i2c-client. + * + * Returns a pointer to the new i2c-client, or NULL if the adapter is not found. + */ +struct i2c_client *i2c_acpi_new_device(struct device *dev, int index, + struct i2c_board_info *info) +{ + struct i2c_acpi_lookup lookup; + struct i2c_adapter *adapter; + struct acpi_device *adev; + LIST_HEAD(resource_list); + int ret; + + adev = ACPI_COMPANION(dev); + if (!adev) + return NULL; + + memset(&lookup, 0, sizeof(lookup)); + lookup.info = info; + lookup.device_handle = acpi_device_handle(adev); + lookup.index = index; + + ret = acpi_dev_get_resources(adev, &resource_list, + i2c_acpi_fill_info, &lookup); + acpi_dev_free_resource_list(&resource_list); + + if (ret < 0 || !info->addr) + return NULL; + + adapter = i2c_acpi_find_adapter_by_handle(lookup.adapter_handle); + if (!adapter) + return NULL; + + return i2c_new_device(adapter, info); +} +EXPORT_SYMBOL_GPL(i2c_acpi_new_device); #else /* CONFIG_ACPI */ static inline void i2c_acpi_register_devices(struct i2c_adapter *adap) { } extern struct notifier_block i2c_acpi_notifier; @@ -929,7 +985,9 @@ static int i2c_device_probe(struct device *dev) if (!client) return 0; - if (!client->irq) { + driver = to_i2c_driver(dev->driver); + + if (!client->irq && !driver->disable_i2c_core_irq_mapping) { int irq = -ENOENT; if (client->flags & I2C_CLIENT_HOST_NOTIFY) { @@ -951,8 +1009,6 @@ static int i2c_device_probe(struct device *dev) client->irq = irq; } - driver = to_i2c_driver(dev->driver); - /* * An I2C ID table is not mandatory, if and only if, a suitable Device * Tree match table entry is supplied for the probing device. diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index 2178266bca79..26f7237558ba 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -395,13 +395,16 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc, if (force_nr) { priv->adap.nr = force_nr; ret = i2c_add_numbered_adapter(&priv->adap); + dev_err(&parent->dev, + "failed to add mux-adapter %u as bus %u (error=%d)\n", + chan_id, force_nr, ret); } else { ret = i2c_add_adapter(&priv->adap); + dev_err(&parent->dev, + "failed to add mux-adapter %u (error=%d)\n", + chan_id, ret); } if (ret < 0) { - dev_err(&parent->dev, - "failed to add mux-adapter (error=%d)\n", - ret); kfree(priv); return ret; } diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig index 10b3d17ae3ea..1e160fc37ecc 100644 --- a/drivers/i2c/muxes/Kconfig +++ b/drivers/i2c/muxes/Kconfig @@ -30,6 +30,17 @@ config I2C_MUX_GPIO This driver can also be built as a module. If so, the module will be called i2c-mux-gpio. +config I2C_MUX_LTC4306 + tristate "LTC LTC4306/5 I2C multiplexer" + select GPIOLIB + select REGMAP_I2C + help + If you say yes here you get support for the Analog Devices + LTC4306 or LTC4305 I2C mux/switch devices. + + This driver can also be built as a module. If so, the module + will be called i2c-mux-ltc4306. + config I2C_MUX_PCA9541 tristate "NXP PCA9541 I2C Master Selector" help diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile index 9948fa45037f..ff7618cd5312 100644 --- a/drivers/i2c/muxes/Makefile +++ b/drivers/i2c/muxes/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE) += i2c-arb-gpio-challenge.o obj-$(CONFIG_I2C_DEMUX_PINCTRL) += i2c-demux-pinctrl.o obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o +obj-$(CONFIG_I2C_MUX_LTC4306) += i2c-mux-ltc4306.o obj-$(CONFIG_I2C_MUX_MLXCPLD) += i2c-mux-mlxcpld.o obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o diff --git a/drivers/i2c/muxes/i2c-arb-gpio-challenge.c b/drivers/i2c/muxes/i2c-arb-gpio-challenge.c index 86fc2d4c081b..812b8cff265f 100644 --- a/drivers/i2c/muxes/i2c-arb-gpio-challenge.c +++ b/drivers/i2c/muxes/i2c-arb-gpio-challenge.c @@ -202,10 +202,8 @@ static int i2c_arbitrator_probe(struct platform_device *pdev) /* Actually add the mux adapter */ ret = i2c_mux_add_adapter(muxc, 0, 0, 0); - if (ret) { - dev_err(dev, "Failed to add adapter\n"); + if (ret) i2c_put_adapter(muxc->parent); - } return ret; } diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c index 655684d621a4..1a9973ede443 100644 --- a/drivers/i2c/muxes/i2c-mux-gpio.c +++ b/drivers/i2c/muxes/i2c-mux-gpio.c @@ -245,10 +245,8 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev) unsigned int class = mux->data.classes ? mux->data.classes[i] : 0; ret = i2c_mux_add_adapter(muxc, nr, mux->data.values[i], class); - if (ret) { - dev_err(&pdev->dev, "Failed to add adapter %d\n", i); + if (ret) goto add_adapter_failed; - } } dev_info(&pdev->dev, "%d port mux on %s adapter\n", diff --git a/drivers/i2c/muxes/i2c-mux-ltc4306.c b/drivers/i2c/muxes/i2c-mux-ltc4306.c new file mode 100644 index 000000000000..311b1cced0c0 --- /dev/null +++ b/drivers/i2c/muxes/i2c-mux-ltc4306.c @@ -0,0 +1,322 @@ +/* + * Linear Technology LTC4306 and LTC4305 I2C multiplexer/switch + * + * Copyright (C) 2017 Analog Devices Inc. + * + * Licensed under the GPL-2. + * + * Based on: i2c-mux-pca954x.c + * + * Datasheet: http://cds.linear.com/docs/en/datasheet/4306.pdf + */ + +#include <linux/gpio/consumer.h> +#include <linux/gpio/driver.h> +#include <linux/i2c-mux.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#define LTC4305_MAX_NCHANS 2 +#define LTC4306_MAX_NCHANS 4 + +#define LTC_REG_STATUS 0x0 +#define LTC_REG_CONFIG 0x1 +#define LTC_REG_MODE 0x2 +#define LTC_REG_SWITCH 0x3 + +#define LTC_DOWNSTREAM_ACCL_EN BIT(6) +#define LTC_UPSTREAM_ACCL_EN BIT(7) + +#define LTC_GPIO_ALL_INPUT 0xC0 +#define LTC_SWITCH_MASK 0xF0 + +enum ltc_type { + ltc_4305, + ltc_4306, +}; + +struct chip_desc { + u8 nchans; + u8 num_gpios; +}; + +struct ltc4306 { + struct regmap *regmap; + struct gpio_chip gpiochip; + const struct chip_desc *chip; +}; + +static const struct chip_desc chips[] = { + [ltc_4305] = { + .nchans = LTC4305_MAX_NCHANS, + }, + [ltc_4306] = { + .nchans = LTC4306_MAX_NCHANS, + .num_gpios = 2, + }, +}; + +static bool ltc4306_is_volatile_reg(struct device *dev, unsigned int reg) +{ + return (reg == LTC_REG_CONFIG) ? true : false; +} + +static const struct regmap_config ltc4306_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = LTC_REG_SWITCH, + .volatile_reg = ltc4306_is_volatile_reg, + .cache_type = REGCACHE_FLAT, +}; + +static int ltc4306_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct ltc4306 *data = gpiochip_get_data(chip); + unsigned int val; + int ret; + + ret = regmap_read(data->regmap, LTC_REG_CONFIG, &val); + if (ret < 0) + return ret; + + return !!(val & BIT(1 - offset)); +} + +static void ltc4306_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct ltc4306 *data = gpiochip_get_data(chip); + + regmap_update_bits(data->regmap, LTC_REG_CONFIG, BIT(5 - offset), + value ? BIT(5 - offset) : 0); +} + +static int ltc4306_gpio_get_direction(struct gpio_chip *chip, + unsigned int offset) +{ + struct ltc4306 *data = gpiochip_get_data(chip); + unsigned int val; + int ret; + + ret = regmap_read(data->regmap, LTC_REG_MODE, &val); + if (ret < 0) + return ret; + + return !!(val & BIT(7 - offset)); +} + +static int ltc4306_gpio_direction_input(struct gpio_chip *chip, + unsigned int offset) +{ + struct ltc4306 *data = gpiochip_get_data(chip); + + return regmap_update_bits(data->regmap, LTC_REG_MODE, + BIT(7 - offset), BIT(7 - offset)); +} + +static int ltc4306_gpio_direction_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct ltc4306 *data = gpiochip_get_data(chip); + + ltc4306_gpio_set(chip, offset, value); + return regmap_update_bits(data->regmap, LTC_REG_MODE, + BIT(7 - offset), 0); +} + +static int ltc4306_gpio_set_config(struct gpio_chip *chip, + unsigned int offset, unsigned long config) +{ + struct ltc4306 *data = gpiochip_get_data(chip); + unsigned int val; + + switch (pinconf_to_config_param(config)) { + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + val = 0; + break; + case PIN_CONFIG_DRIVE_PUSH_PULL: + val = BIT(4 - offset); + break; + default: + return -ENOTSUPP; + } + + return regmap_update_bits(data->regmap, LTC_REG_MODE, + BIT(4 - offset), val); +} + +static int ltc4306_gpio_init(struct ltc4306 *data) +{ + struct device *dev = regmap_get_device(data->regmap); + + if (!data->chip->num_gpios) + return 0; + + data->gpiochip.label = dev_name(dev); + data->gpiochip.base = -1; + data->gpiochip.ngpio = data->chip->num_gpios; + data->gpiochip.parent = dev; + data->gpiochip.can_sleep = true; + data->gpiochip.get_direction = ltc4306_gpio_get_direction; + data->gpiochip.direction_input = ltc4306_gpio_direction_input; + data->gpiochip.direction_output = ltc4306_gpio_direction_output; + data->gpiochip.get = ltc4306_gpio_get; + data->gpiochip.set = ltc4306_gpio_set; + data->gpiochip.set_config = ltc4306_gpio_set_config; + data->gpiochip.owner = THIS_MODULE; + + /* gpiolib assumes all GPIOs default input */ + regmap_write(data->regmap, LTC_REG_MODE, LTC_GPIO_ALL_INPUT); + + return devm_gpiochip_add_data(dev, &data->gpiochip, data); +} + +static int ltc4306_select_mux(struct i2c_mux_core *muxc, u32 chan) +{ + struct ltc4306 *data = i2c_mux_priv(muxc); + + return regmap_update_bits(data->regmap, LTC_REG_SWITCH, + LTC_SWITCH_MASK, BIT(7 - chan)); +} + +static int ltc4306_deselect_mux(struct i2c_mux_core *muxc, u32 chan) +{ + struct ltc4306 *data = i2c_mux_priv(muxc); + + return regmap_update_bits(data->regmap, LTC_REG_SWITCH, + LTC_SWITCH_MASK, 0); +} + +static const struct i2c_device_id ltc4306_id[] = { + { "ltc4305", ltc_4305 }, + { "ltc4306", ltc_4306 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ltc4306_id); + +static const struct of_device_id ltc4306_of_match[] = { + { .compatible = "lltc,ltc4305", .data = &chips[ltc_4305] }, + { .compatible = "lltc,ltc4306", .data = &chips[ltc_4306] }, + { } +}; +MODULE_DEVICE_TABLE(of, ltc4306_of_match); + +static int ltc4306_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent); + const struct chip_desc *chip; + struct i2c_mux_core *muxc; + struct ltc4306 *data; + struct gpio_desc *gpio; + bool idle_disc; + unsigned int val = 0; + int num, ret; + + chip = of_device_get_match_data(&client->dev); + + if (!chip) + chip = &chips[id->driver_data]; + + idle_disc = device_property_read_bool(&client->dev, + "i2c-mux-idle-disconnect"); + + muxc = i2c_mux_alloc(adap, &client->dev, + chip->nchans, sizeof(*data), + I2C_MUX_LOCKED, ltc4306_select_mux, + idle_disc ? ltc4306_deselect_mux : NULL); + if (!muxc) + return -ENOMEM; + data = i2c_mux_priv(muxc); + data->chip = chip; + + i2c_set_clientdata(client, muxc); + + data->regmap = devm_regmap_init_i2c(client, <c4306_regmap_config); + if (IS_ERR(data->regmap)) { + ret = PTR_ERR(data->regmap); + dev_err(&client->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + /* Reset and enable the mux if an enable GPIO is specified. */ + gpio = devm_gpiod_get_optional(&client->dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(gpio)) + return PTR_ERR(gpio); + + if (gpio) { + udelay(1); + gpiod_set_value(gpio, 1); + } + + /* + * Write the mux register at addr to verify + * that the mux is in fact present. This also + * initializes the mux to disconnected state. + */ + if (regmap_write(data->regmap, LTC_REG_SWITCH, 0) < 0) { + dev_warn(&client->dev, "probe failed\n"); + return -ENODEV; + } + + if (device_property_read_bool(&client->dev, + "ltc,downstream-accelerators-enable")) + val |= LTC_DOWNSTREAM_ACCL_EN; + + if (device_property_read_bool(&client->dev, + "ltc,upstream-accelerators-enable")) + val |= LTC_UPSTREAM_ACCL_EN; + + if (regmap_write(data->regmap, LTC_REG_CONFIG, val) < 0) + return -ENODEV; + + ret = ltc4306_gpio_init(data); + if (ret < 0) + return ret; + + /* Now create an adapter for each channel */ + for (num = 0; num < chip->nchans; num++) { + ret = i2c_mux_add_adapter(muxc, 0, num, 0); + if (ret) { + i2c_mux_del_adapters(muxc); + return ret; + } + } + + dev_info(&client->dev, + "registered %d multiplexed busses for I2C switch %s\n", + num, client->name); + + return 0; +} + +static int ltc4306_remove(struct i2c_client *client) +{ + struct i2c_mux_core *muxc = i2c_get_clientdata(client); + + i2c_mux_del_adapters(muxc); + + return 0; +} + +static struct i2c_driver ltc4306_driver = { + .driver = { + .name = "ltc4306", + .of_match_table = of_match_ptr(ltc4306_of_match), + }, + .probe = ltc4306_probe, + .remove = ltc4306_remove, + .id_table = ltc4306_id, +}; + +module_i2c_driver(ltc4306_driver); + +MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); +MODULE_DESCRIPTION("Linear Technology LTC4306, LTC4305 I2C mux/switch driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/muxes/i2c-mux-pca9541.c b/drivers/i2c/muxes/i2c-mux-pca9541.c index 77840f7845a1..9e318c9516c7 100644 --- a/drivers/i2c/muxes/i2c-mux-pca9541.c +++ b/drivers/i2c/muxes/i2c-mux-pca9541.c @@ -369,10 +369,8 @@ static int pca9541_probe(struct i2c_client *client, i2c_set_clientdata(client, muxc); ret = i2c_mux_add_adapter(muxc, force, 0, 0); - if (ret) { - dev_err(&client->dev, "failed to register master selector\n"); + if (ret) return ret; - } dev_info(&client->dev, "registered master selector for I2C %s\n", client->name); diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c index ad31d21da316..f1751c290af6 100644 --- a/drivers/i2c/muxes/i2c-mux-pca954x.c +++ b/drivers/i2c/muxes/i2c-mux-pca954x.c @@ -84,7 +84,7 @@ struct pca954x { struct irq_domain *irq; unsigned int irq_mask; - spinlock_t lock; + raw_spinlock_t lock; }; /* Provide specs for the PCA954x types we know about */ @@ -252,13 +252,13 @@ static void pca954x_irq_mask(struct irq_data *idata) unsigned int pos = idata->hwirq; unsigned long flags; - spin_lock_irqsave(&data->lock, flags); + raw_spin_lock_irqsave(&data->lock, flags); data->irq_mask &= ~BIT(pos); if (!data->irq_mask) disable_irq(data->client->irq); - spin_unlock_irqrestore(&data->lock, flags); + raw_spin_unlock_irqrestore(&data->lock, flags); } static void pca954x_irq_unmask(struct irq_data *idata) @@ -267,13 +267,13 @@ static void pca954x_irq_unmask(struct irq_data *idata) unsigned int pos = idata->hwirq; unsigned long flags; - spin_lock_irqsave(&data->lock, flags); + raw_spin_lock_irqsave(&data->lock, flags); if (!data->irq_mask) enable_irq(data->client->irq); data->irq_mask |= BIT(pos); - spin_unlock_irqrestore(&data->lock, flags); + raw_spin_unlock_irqrestore(&data->lock, flags); } static int pca954x_irq_set_type(struct irq_data *idata, unsigned int type) @@ -299,7 +299,7 @@ static int pca954x_irq_setup(struct i2c_mux_core *muxc) if (!data->chip->has_irq || client->irq <= 0) return 0; - spin_lock_init(&data->lock); + raw_spin_lock_init(&data->lock); data->irq = irq_domain_add_linear(client->dev.of_node, data->chip->nchans, @@ -413,13 +413,8 @@ static int pca954x_probe(struct i2c_client *client, idle_disconnect_dt) << num; ret = i2c_mux_add_adapter(muxc, force, num, class); - - if (ret) { - dev_err(&client->dev, - "failed to register multiplexed adapter" - " %d as bus %d\n", num, force); + if (ret) goto fail_del_adapters; - } } dev_info(&client->dev, diff --git a/drivers/i2c/muxes/i2c-mux-pinctrl.c b/drivers/i2c/muxes/i2c-mux-pinctrl.c index 35bb775e1b74..7c0c264b07bc 100644 --- a/drivers/i2c/muxes/i2c-mux-pinctrl.c +++ b/drivers/i2c/muxes/i2c-mux-pinctrl.c @@ -245,10 +245,8 @@ static int i2c_mux_pinctrl_probe(struct platform_device *pdev) (mux->pdata->base_bus_num + i) : 0; ret = i2c_mux_add_adapter(muxc, bus, i, 0); - if (ret) { - dev_err(&pdev->dev, "Failed to add adapter %d\n", i); + if (ret) goto err_del_adapter; - } } return 0; diff --git a/drivers/i2c/muxes/i2c-mux-reg.c b/drivers/i2c/muxes/i2c-mux-reg.c index c6a90b4a9c62..406d5059072c 100644 --- a/drivers/i2c/muxes/i2c-mux-reg.c +++ b/drivers/i2c/muxes/i2c-mux-reg.c @@ -222,10 +222,8 @@ static int i2c_mux_reg_probe(struct platform_device *pdev) class = mux->data.classes ? mux->data.classes[i] : 0; ret = i2c_mux_add_adapter(muxc, nr, mux->data.values[i], class); - if (ret) { - dev_err(&pdev->dev, "Failed to add adapter %d\n", i); + if (ret) goto add_adapter_failed; - } } dev_dbg(&pdev->dev, "%d port mux on %s adapter\n", diff --git a/drivers/media/usb/cx231xx/cx231xx-i2c.c b/drivers/media/usb/cx231xx/cx231xx-i2c.c index 35e9acfe63d3..dff514e147da 100644 --- a/drivers/media/usb/cx231xx/cx231xx-i2c.c +++ b/drivers/media/usb/cx231xx/cx231xx-i2c.c @@ -576,17 +576,10 @@ int cx231xx_i2c_mux_create(struct cx231xx *dev) int cx231xx_i2c_mux_register(struct cx231xx *dev, int mux_no) { - int rc; - - rc = i2c_mux_add_adapter(dev->muxc, - 0, - mux_no /* chan_id */, - 0 /* class */); - if (rc) - dev_warn(dev->dev, - "i2c mux %d register FAILED\n", mux_no); - - return rc; + return i2c_mux_add_adapter(dev->muxc, + 0, + mux_no /* chan_id */, + 0 /* class */); } void cx231xx_i2c_mux_unregister(struct cx231xx *dev) diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 6b183521c616..3a57e3dc9bec 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -149,6 +149,7 @@ enum i2c_alert_protocol { * @detect: Callback for device detection * @address_list: The I2C addresses to probe (for detect) * @clients: List of detected clients we created (for i2c-core use only) + * @disable_i2c_core_irq_mapping: Tell the i2c-core to not do irq-mapping * * The driver.owner field should be set to the module owner of this driver. * The driver.name field should be set to the name of this driver. @@ -212,6 +213,8 @@ struct i2c_driver { int (*detect)(struct i2c_client *, struct i2c_board_info *); const unsigned short *address_list; struct list_head clients; + + bool disable_i2c_core_irq_mapping; }; #define to_i2c_driver(d) container_of(d, struct i2c_driver, driver) @@ -824,11 +827,18 @@ static inline const struct of_device_id #if IS_ENABLED(CONFIG_ACPI) u32 i2c_acpi_find_bus_speed(struct device *dev); +struct i2c_client *i2c_acpi_new_device(struct device *dev, int index, + struct i2c_board_info *info); #else static inline u32 i2c_acpi_find_bus_speed(struct device *dev) { return 0; } +static inline struct i2c_client *i2c_acpi_new_device(struct device *dev, + int index, struct i2c_board_info *info) +{ + return NULL; +} #endif /* CONFIG_ACPI */ #endif /* _LINUX_I2C_H */ |