diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-06-16 07:43:29 +0530 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-06-16 07:43:29 +0530 |
| commit | 2779759c090ea0e78109a0cad0a81d869adfb459 (patch) | |
| tree | db4d89445018c49d313640d347dbe1d375fc4170 | |
| parent | 31e6aeafcdde965aa10e10e93ee186520555ec3d (diff) | |
| parent | 90220ddfa8e2c93e26af2cd51d6158ca2243c622 (diff) | |
| download | linux-next-2779759c090ea0e78109a0cad0a81d869adfb459.tar.gz linux-next-2779759c090ea0e78109a0cad0a81d869adfb459.zip | |
Merge tag 'i2c-7.2-part1' of git://git.kernel.org/pub/scm/linux/kernel/git/andi.shyti/linux
Pull i2c updates from Andi Shyti:
"This pull request is mostly made of cleanups and small infrastructure
improvements across the I2C core, drivers and bindings. It also adds
support for three drivers and a few new compatibles.
Two major cleanup across drivers and core code:
- use named initializers in device ID tables
- replace dev_err() with dev_err_probe()
Drivers:
- at24: use named initializers for arrays of i2c_device_data
- at91: add MCHP_LAN966X_PCI dependency
- cadence: add shutdown callback
- k1: enable by default on SpacemiT
- mxs: improve documentation
- qcom-geni: use pm_runtime_force_suspend/resume for system sleep
- tegra:
- disable fair arbitration on non-MCTP buses
- allocate DMA buffers from the correct DMA device
- designware:
- handle active target shutdown cleanly
- add shutdown callbacks for platform and PCI drivers
- adopt the new PM_RUNTIME_ACQUIRE() helpers
DT bindings:
- convert davinci bindings to DT schema
Core and muxes:
- acpi: report missing I2C resources as -ENOENT
- gpiolib: add gpiod_is_single_ended() helper for I2C GPIO users
- i2c-mux-reg: add generic firmware node support
- a set of 10 patches from Johan Hovold fixing adapter registration
races, cleanup paths and resource management issues
New support:
- DesignWare LECA0003 (ACPI ID)
- Loongson LS2K0300 I2C controller (new driver)
- Qualcomm CCI Glymur and Shikra compatibles"
* tag 'i2c-7.2-part1' of git://git.kernel.org/pub/scm/linux/kernel/git/andi.shyti/linux: (46 commits)
i2c: mxs: add missing kernel-doc for struct mxs_i2c_dev members
i2c: qcom-geni: Use pm_runtime_force_{suspend,resume} helpers
dt-bindings: i2c: qcom-cci: Document Glymur compatible
dt-bindings: i2c: qcom-cci: Document Shikra compatible
i2c: mux: reg: use device property accessors
i2c: acpi: Return -ENOENT when no resources found in i2c_acpi_client_count()
i2c: at91: Add MCHP_LAN966X_PCI dependency
i2c: eg20t: Consistently define pci_device_ids using named initializers
i2c: designware-pcidrv: Consistently define pci_device_ids using named initializers
i2c: bcm-kona: fix spelling mistake in timeout-check comment
i2c: cadence: Add shutdown handler
i2c: tegra: Disable fair arbitration for non-MCTP buses
i2c: tegra: use dmaengine_get_dma_device() for DMA buffer allocation
i2c: busses: make K1 driver default for SpacemiT platforms
i2c: Use named initializers for arrays of i2c_device_data
i2c: core: clean up adapter registration error label
i2c: core: clean up bus id allocation
i2c: core: fix adapter deregistration race
i2c: core: fix adapter registration race
i2c: core: disable runtime PM on adapter registration failure
...
40 files changed, 1173 insertions, 500 deletions
diff --git a/Documentation/devicetree/bindings/i2c/i2c-davinci.txt b/Documentation/devicetree/bindings/i2c/i2c-davinci.txt deleted file mode 100644 index 6590501c53d4..000000000000 --- a/Documentation/devicetree/bindings/i2c/i2c-davinci.txt +++ /dev/null @@ -1,43 +0,0 @@ -* Texas Instruments Davinci/Keystone I2C - -This file provides information, what the device node for the -davinci/keystone i2c interface contains. - -Required properties: -- compatible: "ti,davinci-i2c" or "ti,keystone-i2c"; -- reg : Offset and length of the register set for the device -- clocks: I2C functional clock phandle. - For 66AK2G this property should be set per binding, - Documentation/devicetree/bindings/clock/ti,sci-clk.yaml - -SoC-specific Required Properties: - -The following are mandatory properties for Keystone 2 66AK2G SoCs only: - -- power-domains: Should contain a phandle to a PM domain provider node - and an args specifier containing the I2C device id - value. This property is as per the binding, - Documentation/devicetree/bindings/soc/ti/sci-pm-domain.yaml - -Recommended properties : -- interrupts : standard interrupt property. -- clock-frequency : desired I2C bus clock frequency in Hz. -- ti,has-pfunc: boolean; if defined, it indicates that SoC supports PFUNC - registers. PFUNC registers allow to switch I2C pins to function as - GPIOs, so they can be toggled manually. - -Example (enbw_cmc board): - i2c@1c22000 { - compatible = "ti,davinci-i2c"; - reg = <0x22000 0x1000>; - clock-frequency = <100000>; - interrupts = <15>; - interrupt-parent = <&intc>; - #address-cells = <1>; - #size-cells = <0>; - - dtt@48 { - compatible = "national,lm75"; - reg = <0x48>; - }; - }; diff --git a/Documentation/devicetree/bindings/i2c/loongson,ls2x-i2c.yaml b/Documentation/devicetree/bindings/i2c/loongson,ls2x-i2c.yaml index 67882ec6e06a..ee09c6d9c5f0 100644 --- a/Documentation/devicetree/bindings/i2c/loongson,ls2x-i2c.yaml +++ b/Documentation/devicetree/bindings/i2c/loongson,ls2x-i2c.yaml @@ -16,6 +16,7 @@ properties: compatible: enum: - loongson,ls2k-i2c + - loongson,ls2k0300-i2c - loongson,ls7a-i2c reg: @@ -24,6 +25,9 @@ properties: interrupts: maxItems: 1 + clocks: + maxItems: 1 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/i2c/qcom,i2c-cci.yaml b/Documentation/devicetree/bindings/i2c/qcom,i2c-cci.yaml index 7c497a358e1d..1e8def25ec0e 100644 --- a/Documentation/devicetree/bindings/i2c/qcom,i2c-cci.yaml +++ b/Documentation/devicetree/bindings/i2c/qcom,i2c-cci.yaml @@ -26,6 +26,7 @@ properties: - items: - enum: + - qcom,glymur-cci - qcom,kaanapali-cci - qcom,milos-cci - qcom,qcm2290-cci @@ -35,6 +36,7 @@ properties: - qcom,sc8280xp-cci - qcom,sdm670-cci - qcom,sdm845-cci + - qcom,shikra-cci - qcom,sm6150-cci - qcom,sm6350-cci - qcom,sm8250-cci @@ -134,9 +136,11 @@ allOf: compatible: contains: enum: + - qcom,glymur-cci - qcom,kaanapali-cci - qcom,qcm2290-cci - qcom,qcs8300-cci + - qcom,shikra-cci - qcom,sm8750-cci then: properties: diff --git a/Documentation/devicetree/bindings/i2c/ti,davinci-i2c.yaml b/Documentation/devicetree/bindings/i2c/ti,davinci-i2c.yaml new file mode 100644 index 000000000000..b94ac06dce36 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/ti,davinci-i2c.yaml @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/i2c/ti,davinci-i2c.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments DaVinci/Keystone I2C + +maintainers: + - Chaitanya Sabnis <chaitanya.msabnis@gmail.com> + +allOf: + - $ref: /schemas/i2c/i2c-controller.yaml# + - if: + properties: + compatible: + contains: + const: ti,keystone-i2c + then: + required: + - power-domains + +properties: + compatible: + enum: + - ti,davinci-i2c + - ti,keystone-i2c + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + power-domains: + maxItems: 1 + + ti,has-pfunc: + description: + Indicates that the SoC supports PFUNC registers, allowing I2C pins + to function as GPIOs for manual toggling. + type: boolean + +required: + - compatible + - reg + - interrupts + - clocks + +unevaluatedProperties: false + +examples: + - | + i2c@1c22000 { + compatible = "ti,davinci-i2c"; + reg = <0x01c22000 0x1000>; + clocks = <&i2c_clk>; + clock-frequency = <100000>; + interrupts = <15>; + #address-cells = <1>; + #size-cells = <0>; + + sensor@48 { + compatible = "national,lm75"; + reg = <0x48>; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index a068db0ade61..4108b6e62cc3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15069,6 +15069,7 @@ M: Binbin Zhou <zhoubinbin@loongson.cn> L: linux-i2c@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/i2c/loongson,ls2x-i2c.yaml +F: drivers/i2c/busses/i2c-ls2x-v2.c F: drivers/i2c/busses/i2c-ls2x.c LOONGSON PWM DRIVER @@ -26710,7 +26711,7 @@ M: Bartosz Golaszewski <brgl@kernel.org> L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux.git -F: Documentation/devicetree/bindings/i2c/i2c-davinci.txt +F: Documentation/devicetree/bindings/i2c/ti,davinci-i2c.yaml F: arch/arm/boot/dts/ti/davinci/ F: arch/arm/mach-davinci/ F: drivers/i2c/busses/i2c-davinci.c diff --git a/drivers/acpi/acpi_apd.c b/drivers/acpi/acpi_apd.c index bed0791c17fc..86678ee22a04 100644 --- a/drivers/acpi/acpi_apd.c +++ b/drivers/acpi/acpi_apd.c @@ -181,6 +181,12 @@ static const struct apd_device_desc hip08_spi_desc = { .setup = acpi_apd_setup, .fixed_clk_rate = 250000000, }; + +static const struct apd_device_desc leca_i2c_desc = { + .setup = acpi_apd_setup, + .fixed_clk_rate = 250000000, +}; + #endif /* CONFIG_ARM64 */ #endif @@ -251,6 +257,7 @@ static const struct acpi_device_id acpi_apd_device_ids[] = { { "HISI02A2", APD_ADDR(hip08_i2c_desc) }, { "HISI02A3", APD_ADDR(hip08_lite_i2c_desc) }, { "HISI0173", APD_ADDR(hip08_spi_desc) }, + { "LECA0003", APD_ADDR(leca_i2c_desc) }, { "NXP0001", APD_ADDR(nxp_i2c_desc) }, #endif { } diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 8c935f867a37..3123ab75600b 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -417,7 +417,7 @@ config I2C_ASPEED config I2C_AT91 tristate "Atmel AT91 I2C Two-Wire interface (TWI)" - depends on ARCH_MICROCHIP || COMPILE_TEST + depends on ARCH_MICROCHIP || MCHP_LAN966X_PCI || COMPILE_TEST help This supports the use of the I2C interface on Atmel AT91 processors. @@ -794,6 +794,7 @@ config I2C_K1 tristate "SpacemiT K1 I2C adapter" depends on ARCH_SPACEMIT || COMPILE_TEST depends on OF + default ARCH_SPACEMIT help This option enables support for the I2C interface on the SpacemiT K1 platform. @@ -850,6 +851,17 @@ config I2C_LS2X This driver can also be built as a module. If so, the module will be called i2c-ls2x. +config I2C_LS2X_V2 + tristate "Loongson-2 Fast Speed I2C adapter" + depends on LOONGARCH || COMPILE_TEST + select REGMAP_MMIO + help + If you say yes to this option, support will be included for the + I2C interface on the Loongson-2K0300 SoCs. + + This driver can also be built as a module. If so, the module + will be called i2c-ls2x-v2. + config I2C_MLXBF tristate "Mellanox BlueField I2C controller" depends on (MELLANOX_PLATFORM && ARM64) || COMPILE_TEST diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 547123ab351f..3755c54b3d82 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -80,6 +80,7 @@ obj-$(CONFIG_I2C_KEBA) += i2c-keba.o obj-$(CONFIG_I2C_KEMPLD) += i2c-kempld.o obj-$(CONFIG_I2C_LPC2K) += i2c-lpc2k.o obj-$(CONFIG_I2C_LS2X) += i2c-ls2x.o +obj-$(CONFIG_I2C_LS2X_V2) += i2c-ls2x-v2.o obj-$(CONFIG_I2C_MESON) += i2c-meson.o obj-$(CONFIG_I2C_MICROCHIP_CORE) += i2c-microchip-corei2c.o obj-$(CONFIG_I2C_MPC) += i2c-mpc.o diff --git a/drivers/i2c/busses/i2c-bcm-kona.c b/drivers/i2c/busses/i2c-bcm-kona.c index 9d8838bbd938..d4f287b31fd7 100644 --- a/drivers/i2c/busses/i2c-bcm-kona.c +++ b/drivers/i2c/busses/i2c-bcm-kona.c @@ -427,7 +427,7 @@ static int bcm_kona_i2c_write_fifo_single(struct bcm_kona_i2c_dev *dev, return -EREMOTEIO; } - /* Check if a timeout occured */ + /* Check if a timeout occurred */ if (!time_left) { dev_err(dev->device, "completion timed out\n"); return -EREMOTEIO; diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c index 0fb728ade92e..da8770182a18 100644 --- a/drivers/i2c/busses/i2c-cadence.c +++ b/drivers/i2c/busses/i2c-cadence.c @@ -1635,6 +1635,25 @@ static void cdns_i2c_remove(struct platform_device *pdev) reset_control_assert(id->reset); } +/** + * cdns_i2c_shutdown - Prepare I2C controller for system shutdown + * @pdev: Handle to the platform device structure + * + * Mark the adapter as suspended and reset the controller to ensure a clean + * handoff during system reboot or kexec. + */ +static void cdns_i2c_shutdown(struct platform_device *pdev) +{ + struct cdns_i2c *id = platform_get_drvdata(pdev); + + /* Mark the adapter as suspended to prevent further I2C transfers */ + i2c_mark_adapter_suspended(&id->adap); + + /* Reset the controller state if active - clocks are disabled when suspended */ + if (!pm_runtime_status_suspended(&pdev->dev)) + cdns_i2c_master_reset(&id->adap); +} + static struct platform_driver cdns_i2c_drv = { .driver = { .name = DRIVER_NAME, @@ -1643,6 +1662,7 @@ static struct platform_driver cdns_i2c_drv = { }, .probe = cdns_i2c_probe, .remove = cdns_i2c_remove, + .shutdown = cdns_i2c_shutdown, }; module_platform_driver(cdns_i2c_drv); diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c index 4dc57fd56170..e4dfa2ec58bb 100644 --- a/drivers/i2c/busses/i2c-designware-common.c +++ b/drivers/i2c/busses/i2c-designware-common.c @@ -633,6 +633,14 @@ void __i2c_dw_disable(struct dw_i2c_dev *dev) abort_needed = (raw_intr_stats & DW_IC_INTR_MST_ON_HOLD) || (ic_stats & DW_IC_STATUS_MASTER_HOLD_TX_FIFO_EMPTY); + + /* + * If we are in target mode and there is activity, we should also + * trigger an abort to clear the internal state machines. + */ + if (dev->mode == DW_IC_SLAVE && (ic_stats & DW_IC_STATUS_SLAVE_ACTIVITY)) + abort_needed = true; + if (abort_needed) { if (!(enable & DW_IC_ENABLE_ENABLE)) { regmap_write(dev->map, DW_IC_ENABLE, DW_IC_ENABLE_ENABLE); @@ -958,8 +966,8 @@ int i2c_dw_probe(struct dw_i2c_dev *dev) * 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); + PM_RUNTIME_ACQUIRE(dev->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); if (ret) return ret; @@ -1028,5 +1036,29 @@ EXPORT_GPL_DEV_PM_OPS(i2c_dw_dev_pm_ops) = { RUNTIME_PM_OPS(i2c_dw_runtime_suspend, i2c_dw_runtime_resume, NULL) }; +void i2c_dw_shutdown(struct dw_i2c_dev *dev) +{ + unsigned int con; + + /* + * We only need to handle shutdown for target mode to ensure + * we NACK any incoming controller requests. Controller mode cleanup + * is handled after each transfer in i2c_dw_xfer(). + */ + if (dev->mode != DW_IC_SLAVE) + return; + + /* + * To quickly NACK the controller during shutdown, we set the target + * disable bit while the controller is still enabled. + */ + regmap_read(dev->map, DW_IC_CON, &con); + con |= DW_IC_CON_SLAVE_DISABLE; + regmap_write(dev->map, DW_IC_CON, con); + + i2c_dw_disable(dev); +} +EXPORT_SYMBOL_GPL(i2c_dw_shutdown); + MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter core"); MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h index 9d8d104cc391..c71aa2dd368d 100644 --- a/drivers/i2c/busses/i2c-designware-core.h +++ b/drivers/i2c/busses/i2c-designware-core.h @@ -417,6 +417,7 @@ static inline void i2c_dw_configure(struct dw_i2c_dev *dev) int i2c_dw_probe(struct dw_i2c_dev *dev); int i2c_dw_init(struct dw_i2c_dev *dev); +void i2c_dw_shutdown(struct dw_i2c_dev *dev); void i2c_dw_set_mode(struct dw_i2c_dev *dev, int mode); #if IS_ENABLED(CONFIG_I2C_DESIGNWARE_BAYTRAIL) diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c index de929b91d5ea..7a301c8b604e 100644 --- a/drivers/i2c/busses/i2c-designware-master.c +++ b/drivers/i2c/busses/i2c-designware-master.c @@ -785,18 +785,25 @@ __i2c_dw_xfer_one_part(struct dw_i2c_dev *dev, struct i2c_msg *msgs, size_t num) * IC_RAW_INTR_STAT.MASTER_ON_HOLD holding SCL low. Check if * controller is still ACTIVE before disabling I2C. */ - if (i2c_dw_is_controller_active(dev)) - dev_err(dev->dev, "controller active\n"); - - /* - * We must disable the adapter before returning and signaling the end - * of the current transfer. Otherwise the hardware might continue - * generating interrupts which in turn causes a race condition with - * the following transfer. Needs some more investigation if the - * additional interrupts are a hardware bug or this driver doesn't - * handle them correctly yet. - */ - __i2c_dw_disable_nowait(dev); + if (i2c_dw_is_controller_active(dev)) { + /* + * If the controller is still active after the timeout, attempt a + * bus recovery to clear any potentially locked state. + */ + dev_err(dev->dev, "controller active after xfer, recovering\n"); + i2c_recover_bus(&dev->adapter); + i2c_dw_init(dev); + } else { + /* + * We must disable the adapter before returning and signaling the end + * of the current transfer. Otherwise the hardware might continue + * generating interrupts which in turn causes a race condition with + * the following transfer. Needs some more investigation if the + * additional interrupts are a hardware bug or this driver doesn't + * handle them correctly yet. + */ + __i2c_dw_disable_nowait(dev); + } if (dev->msg_err) return dev->msg_err; diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c index f21f9877c040..468287922363 100644 --- a/drivers/i2c/busses/i2c-designware-pcidrv.c +++ b/drivers/i2c/busses/i2c-designware-pcidrv.c @@ -308,58 +308,72 @@ static void i2c_dw_pci_remove(struct pci_dev *pdev) static const struct pci_device_id i2c_designware_pci_ids[] = { /* Medfield */ - { PCI_VDEVICE(INTEL, 0x0817), medfield }, - { PCI_VDEVICE(INTEL, 0x0818), medfield }, - { PCI_VDEVICE(INTEL, 0x0819), medfield }, - { PCI_VDEVICE(INTEL, 0x082C), medfield }, - { PCI_VDEVICE(INTEL, 0x082D), medfield }, - { PCI_VDEVICE(INTEL, 0x082E), medfield }, + { PCI_VDEVICE(INTEL, 0x0817), .driver_data = medfield }, + { PCI_VDEVICE(INTEL, 0x0818), .driver_data = medfield }, + { PCI_VDEVICE(INTEL, 0x0819), .driver_data = medfield }, + { PCI_VDEVICE(INTEL, 0x082C), .driver_data = medfield }, + { PCI_VDEVICE(INTEL, 0x082D), .driver_data = medfield }, + { PCI_VDEVICE(INTEL, 0x082E), .driver_data = medfield }, /* Merrifield */ - { PCI_VDEVICE(INTEL, 0x1195), merrifield }, - { PCI_VDEVICE(INTEL, 0x1196), merrifield }, + { PCI_VDEVICE(INTEL, 0x1195), .driver_data = merrifield }, + { PCI_VDEVICE(INTEL, 0x1196), .driver_data = merrifield }, /* Baytrail */ - { PCI_VDEVICE(INTEL, 0x0F41), baytrail }, - { PCI_VDEVICE(INTEL, 0x0F42), baytrail }, - { PCI_VDEVICE(INTEL, 0x0F43), baytrail }, - { PCI_VDEVICE(INTEL, 0x0F44), baytrail }, - { PCI_VDEVICE(INTEL, 0x0F45), baytrail }, - { PCI_VDEVICE(INTEL, 0x0F46), baytrail }, - { PCI_VDEVICE(INTEL, 0x0F47), baytrail }, + { PCI_VDEVICE(INTEL, 0x0F41), .driver_data = baytrail }, + { PCI_VDEVICE(INTEL, 0x0F42), .driver_data = baytrail }, + { PCI_VDEVICE(INTEL, 0x0F43), .driver_data = baytrail }, + { PCI_VDEVICE(INTEL, 0x0F44), .driver_data = baytrail }, + { PCI_VDEVICE(INTEL, 0x0F45), .driver_data = baytrail }, + { PCI_VDEVICE(INTEL, 0x0F46), .driver_data = baytrail }, + { PCI_VDEVICE(INTEL, 0x0F47), .driver_data = baytrail }, /* Haswell */ - { PCI_VDEVICE(INTEL, 0x9c61), haswell }, - { PCI_VDEVICE(INTEL, 0x9c62), haswell }, + { PCI_VDEVICE(INTEL, 0x9c61), .driver_data = haswell }, + { PCI_VDEVICE(INTEL, 0x9c62), .driver_data = haswell }, /* Braswell / Cherrytrail */ - { PCI_VDEVICE(INTEL, 0x22C1), cherrytrail }, - { PCI_VDEVICE(INTEL, 0x22C2), cherrytrail }, - { PCI_VDEVICE(INTEL, 0x22C3), cherrytrail }, - { PCI_VDEVICE(INTEL, 0x22C4), cherrytrail }, - { PCI_VDEVICE(INTEL, 0x22C5), cherrytrail }, - { PCI_VDEVICE(INTEL, 0x22C6), cherrytrail }, - { PCI_VDEVICE(INTEL, 0x22C7), cherrytrail }, + { PCI_VDEVICE(INTEL, 0x22C1), .driver_data = cherrytrail }, + { PCI_VDEVICE(INTEL, 0x22C2), .driver_data = cherrytrail }, + { PCI_VDEVICE(INTEL, 0x22C3), .driver_data = cherrytrail }, + { PCI_VDEVICE(INTEL, 0x22C4), .driver_data = cherrytrail }, + { PCI_VDEVICE(INTEL, 0x22C5), .driver_data = cherrytrail }, + { PCI_VDEVICE(INTEL, 0x22C6), .driver_data = cherrytrail }, + { PCI_VDEVICE(INTEL, 0x22C7), .driver_data = cherrytrail }, /* Elkhart Lake (PSE I2C) */ - { PCI_VDEVICE(INTEL, 0x4bb9), elkhartlake }, - { PCI_VDEVICE(INTEL, 0x4bba), elkhartlake }, - { PCI_VDEVICE(INTEL, 0x4bbb), elkhartlake }, - { PCI_VDEVICE(INTEL, 0x4bbc), elkhartlake }, - { PCI_VDEVICE(INTEL, 0x4bbd), elkhartlake }, - { PCI_VDEVICE(INTEL, 0x4bbe), elkhartlake }, - { PCI_VDEVICE(INTEL, 0x4bbf), elkhartlake }, - { PCI_VDEVICE(INTEL, 0x4bc0), elkhartlake }, + { PCI_VDEVICE(INTEL, 0x4bb9), .driver_data = elkhartlake }, + { PCI_VDEVICE(INTEL, 0x4bba), .driver_data = elkhartlake }, + { PCI_VDEVICE(INTEL, 0x4bbb), .driver_data = elkhartlake }, + { PCI_VDEVICE(INTEL, 0x4bbc), .driver_data = elkhartlake }, + { PCI_VDEVICE(INTEL, 0x4bbd), .driver_data = elkhartlake }, + { PCI_VDEVICE(INTEL, 0x4bbe), .driver_data = elkhartlake }, + { PCI_VDEVICE(INTEL, 0x4bbf), .driver_data = elkhartlake }, + { PCI_VDEVICE(INTEL, 0x4bc0), .driver_data = elkhartlake }, /* AMD NAVI */ - { PCI_VDEVICE(ATI, 0x7314), navi_amd }, - { PCI_VDEVICE(ATI, 0x73a4), navi_amd }, - { PCI_VDEVICE(ATI, 0x73e4), navi_amd }, - { PCI_VDEVICE(ATI, 0x73c4), navi_amd }, - { PCI_VDEVICE(ATI, 0x7444), navi_amd }, - { PCI_VDEVICE(ATI, 0x7464), navi_amd }, - {} + { PCI_VDEVICE(ATI, 0x7314), .driver_data = navi_amd }, + { PCI_VDEVICE(ATI, 0x73a4), .driver_data = navi_amd }, + { PCI_VDEVICE(ATI, 0x73e4), .driver_data = navi_amd }, + { PCI_VDEVICE(ATI, 0x73c4), .driver_data = navi_amd }, + { PCI_VDEVICE(ATI, 0x7444), .driver_data = navi_amd }, + { PCI_VDEVICE(ATI, 0x7464), .driver_data = navi_amd }, + { } }; MODULE_DEVICE_TABLE(pci, i2c_designware_pci_ids); +static void i2c_dw_pci_shutdown(struct pci_dev *pdev) +{ + struct dw_i2c_dev *i_dev; + + i_dev = pci_get_drvdata(pdev); + if (!i_dev) + return; + + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + i2c_dw_shutdown(i_dev); +} + static struct pci_driver dw_i2c_driver = { .name = DRIVER_NAME, .probe = i2c_dw_pci_probe, .remove = i2c_dw_pci_remove, + .shutdown = i2c_dw_pci_shutdown, .driver = { .pm = pm_ptr(&i2c_dw_dev_pm_ops), }, diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 3351c4a9ef11..6d6e81242f74 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -279,6 +279,7 @@ static const struct acpi_device_id dw_i2c_acpi_match[] = { { "INT3432", 0 }, { "INT3433", 0 }, { "INTC10EF", 0 }, + { "LECA0003", 0 }, {} }; MODULE_DEVICE_TABLE(acpi, dw_i2c_acpi_match); @@ -289,9 +290,23 @@ static const struct platform_device_id dw_i2c_platform_ids[] = { }; MODULE_DEVICE_TABLE(platform, dw_i2c_platform_ids); +static void dw_i2c_plat_shutdown(struct platform_device *pdev) +{ + struct dw_i2c_dev *i_dev; + + i_dev = platform_get_drvdata(pdev); + if (!i_dev) + return; + + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + i2c_dw_shutdown(i_dev); +} + static struct platform_driver dw_i2c_driver = { .probe = dw_i2c_plat_probe, .remove = dw_i2c_plat_remove, + .shutdown = dw_i2c_plat_shutdown, .driver = { .name = "i2c_designware", .of_match_table = dw_i2c_of_match, diff --git a/drivers/i2c/busses/i2c-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c index f83238868802..8c67ab4f2aad 100644 --- a/drivers/i2c/busses/i2c-eg20t.c +++ b/drivers/i2c/busses/i2c-eg20t.c @@ -169,11 +169,11 @@ static DEFINE_MUTEX(pch_mutex); #define PCI_DEVICE_ID_ML7831_I2C 0x8817 static const struct pci_device_id pch_pcidev_id[] = { - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_PCH_I2C), 1, }, - { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_I2C), 2, }, - { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_I2C), 1, }, - { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7831_I2C), 1, }, - {0,} + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_PCH_I2C), .driver_data = 1 }, + { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_I2C), .driver_data = 2 }, + { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_I2C), .driver_data = 1 }, + { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7831_I2C), .driver_data = 1 }, + { } }; MODULE_DEVICE_TABLE(pci, pch_pcidev_id); diff --git a/drivers/i2c/busses/i2c-icy.c b/drivers/i2c/busses/i2c-icy.c index febcb6f01d4d..55496e48ccd1 100644 --- a/drivers/i2c/busses/i2c-icy.c +++ b/drivers/i2c/busses/i2c-icy.c @@ -193,8 +193,8 @@ static void icy_remove(struct zorro_dev *z) } static const struct zorro_device_id icy_zorro_tbl[] = { - { ZORRO_ID(VMC, 15, 0), }, - { 0 } + { .id = ZORRO_ID(VMC, 15, 0) }, + { } }; MODULE_DEVICE_TABLE(zorro, icy_zorro_tbl); diff --git a/drivers/i2c/busses/i2c-ls2x-v2.c b/drivers/i2c/busses/i2c-ls2x-v2.c new file mode 100644 index 000000000000..517760d70169 --- /dev/null +++ b/drivers/i2c/busses/i2c-ls2x-v2.c @@ -0,0 +1,544 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Loongson-2K0300 I2C controller driver + * + * Copyright (C) 2025-2026 Loongson Technology Corporation Limited + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/time.h> +#include <linux/types.h> +#include <linux/units.h> + +/* Loongson-2 fast I2C offset registers */ +#define LOONGSON2_I2C_CR1 0x00 /* I2C control 1 register */ +#define LOONGSON2_I2C_CR2 0x04 /* I2C control 2 register */ +#define LOONGSON2_I2C_OAR 0x08 /* I2C slave address register */ +#define LOONGSON2_I2C_DR 0x10 /* I2C data register */ +#define LOONGSON2_I2C_SR1 0x14 /* I2C status 1 register */ +#define LOONGSON2_I2C_SR2 0x18 /* I2C status 2 register */ +#define LOONGSON2_I2C_CCR 0x1c /* I2C clock control register */ +#define LOONGSON2_I2C_TRISE 0x20 /* I2C trise register */ +#define LOONGSON2_I2C_FLTR 0x24 + +/* Bitfields of I2C control 1 register */ +#define LOONGSON2_I2C_CR1_PE BIT(0) /* Peripheral enable */ +#define LOONGSON2_I2C_CR1_START BIT(8) /* Start generation */ +#define LOONGSON2_I2C_CR1_STOP BIT(9) /* Stop generation */ +#define LOONGSON2_I2C_CR1_ACK BIT(10) /* Acknowledge enable */ +#define LOONGSON2_I2C_CR1_POS BIT(11) /* Acknowledge/PEC Position (for data reception) */ + +#define LOONGSON2_I2C_CR1_OP_MASK (LOONGSON2_I2C_CR1_START | LOONGSON2_I2C_CR1_STOP) + +/* Bitfields of I2C control 2 register */ +#define LOONGSON2_I2C_CR2_FREQ GENMASK(5, 0) /* APB Clock Frequency in MHz */ +#define LOONGSON2_I2C_CR2_ITERREN BIT(8) /* Fault-Class Interrupt Enable */ +#define LOONGSON2_I2C_CR2_ITEVTEN BIT(9) /* Event-Based Interrupt Enable */ +#define LOONGSON2_I2C_CR2_ITBUFEN BIT(10) /* Cache-Class Interrupt Enable */ + +#define LOONGSON2_I2C_CR2_INT_MASK \ + (LOONGSON2_I2C_CR2_ITBUFEN | LOONGSON2_I2C_CR2_ITEVTEN | LOONGSON2_I2C_CR2_ITERREN) + +/* Bitfields of I2C status 1 register */ +#define LOONGSON2_I2C_SR1_SB BIT(0) /* Start bit (Master mode) */ +#define LOONGSON2_I2C_SR1_ADDR BIT(1) /* Address sent (master mode) */ +#define LOONGSON2_I2C_SR1_BTF BIT(2) /* Byte transfer finished */ +#define LOONGSON2_I2C_SR1_RXNE BIT(6) /* Data register not empty (receivers) */ +#define LOONGSON2_I2C_SR1_TXE BIT(7) /* Data register empty (transmitters) */ +#define LOONGSON2_I2C_SR1_BERR BIT(8) /* Bus error */ +#define LOONGSON2_I2C_SR1_ARLO BIT(9) /* Arbitration lost (master mode) */ +#define LOONGSON2_I2C_SR1_AF BIT(10) /* Acknowledge failure */ + +#define LOONGSON2_I2C_SR1_ITEVTEN_MASK \ + (LOONGSON2_I2C_SR1_BTF | LOONGSON2_I2C_SR1_ADDR | LOONGSON2_I2C_SR1_SB) +#define LOONGSON2_I2C_SR1_ITBUFEN_MASK (LOONGSON2_I2C_SR1_TXE | LOONGSON2_I2C_SR1_RXNE) +#define LOONGSON2_I2C_SR1_ITERREN_MASK \ + (LOONGSON2_I2C_SR1_AF | LOONGSON2_I2C_SR1_ARLO | LOONGSON2_I2C_SR1_BERR) + +/* Bitfields of I2C status 2 register */ +#define LOONGSON2_I2C_SR2_MSL BIT(0) /* Master/slave */ +#define LOONGSON2_I2C_SR2_BUSY BIT(1) /* Bus busy */ +#define LOONGSON2_I2C_SR2_TRA BIT(2) /* Transmitter/receiver */ +#define LOONGSON2_I2C_SR2_GENCALL BIT(4) /* General call address (Slave mode) */ + +/* Bitfields of I2C clock control register */ +#define LOONGSON2_I2C_CCR_CCR GENMASK(11, 0) +#define LOONGSON2_I2C_CCR_DUTY BIT(14) +#define LOONGSON2_I2C_CCR_FS BIT(15) + +/* Bitfields of I2C trise register */ +#define LOONGSON2_I2C_TRISE_SCL GENMASK(5, 0) + +#define LOONGSON2_I2C_FREE_SLEEP_US 10 +#define LOONGSON2_I2C_FREE_TIMEOUT_US (2 * USEC_PER_MSEC) + +/** + * struct loongson2_i2c_msg - client specific data + * @buf: data buffer + * @count: number of bytes to be transferred + * @result: result of the transfer + * @addr: 8-bit slave addr, including r/w bit + * @stop: last I2C msg to be sent, i.e. STOP to be generated + */ +struct loongson2_i2c_msg { + u8 *buf; + u32 count; + int result; + u8 addr; + bool stop; +}; + +/** + * struct loongson2_i2c_priv - private data of the controller + * @adapter: I2C adapter for this controller + * @complete: completion of I2C message + * @clk: hw i2c clock + * @regmap: regmap of the I2C device + * @parent_rate_MHz: I2C clock parent rate + * @msg: I2C transfer information + */ +struct loongson2_i2c_priv { + struct i2c_adapter adapter; + struct completion complete; + struct clk *clk; + struct regmap *regmap; + unsigned long parent_rate_MHz; + struct loongson2_i2c_msg msg; +}; + +static void loongson2_i2c_disable_irq(struct loongson2_i2c_priv *priv) +{ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR2, LOONGSON2_I2C_CR2_INT_MASK, 0); +} + +static void loongson2_i2c_read_msg(struct loongson2_i2c_priv *priv) +{ + struct loongson2_i2c_msg *msg = &priv->msg; + u32 rbuf; + + regmap_read(priv->regmap, LOONGSON2_I2C_DR, &rbuf); + *msg->buf++ = rbuf; + msg->count--; +} + +static void loongson2_i2c_write_msg(struct loongson2_i2c_priv *priv, u8 byte) +{ + regmap_write(priv->regmap, LOONGSON2_I2C_DR, byte); +} + +static void loongson2_i2c_terminate_xfer(struct loongson2_i2c_priv *priv) +{ + struct loongson2_i2c_msg *msg = &priv->msg; + + loongson2_i2c_disable_irq(priv); + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_OP_MASK, + msg->stop ? LOONGSON2_I2C_CR1_STOP : LOONGSON2_I2C_CR1_START); + complete(&priv->complete); +} + +static void loongson2_i2c_handle_write(struct loongson2_i2c_priv *priv) +{ + struct loongson2_i2c_msg *msg = &priv->msg; + + if (msg->count) { + loongson2_i2c_write_msg(priv, *msg->buf++); + if (!--msg->count) + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR2, + LOONGSON2_I2C_CR2_ITBUFEN, 0); + } else { + loongson2_i2c_terminate_xfer(priv); + } +} + +static void loongson2_i2c_handle_rx_addr(struct loongson2_i2c_priv *priv) +{ + struct loongson2_i2c_msg *msg = &priv->msg; + + switch (msg->count) { + case 0: + loongson2_i2c_terminate_xfer(priv); + break; + case 1: + /* Enable NACK and reset POS (Acknowledge position) */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, + LOONGSON2_I2C_CR1_ACK | LOONGSON2_I2C_CR1_POS, 0); + /* Set STOP or RepSTART */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_OP_MASK, + msg->stop ? LOONGSON2_I2C_CR1_STOP : LOONGSON2_I2C_CR1_START); + break; + case 2: + /* Enable NACK */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_ACK, 0); + /* Set POS (NACK position) */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_POS, + LOONGSON2_I2C_CR1_POS); + break; + + default: + /* Enable ACK */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_ACK, + LOONGSON2_I2C_CR1_ACK); + /* Reset POS (ACK position) */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_POS, 0); + break; + } +} + +static void loongson2_i2c_isr_error(u32 status, void *data) +{ + struct loongson2_i2c_priv *priv = data; + struct loongson2_i2c_msg *msg = &priv->msg; + + /* Arbitration lost */ + if (status & LOONGSON2_I2C_SR1_ARLO) { + regmap_update_bits(priv->regmap, LOONGSON2_I2C_SR1, LOONGSON2_I2C_SR1_ARLO, 0); + msg->result = -EAGAIN; + goto out; + } + + /* + * Acknowledge failure: + * In master transmitter mode a Stop must be generated by software. + */ + if (status & LOONGSON2_I2C_SR1_AF) { + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_STOP, + LOONGSON2_I2C_CR1_STOP); + regmap_update_bits(priv->regmap, LOONGSON2_I2C_SR1, LOONGSON2_I2C_SR1_AF, 0); + msg->result = -EIO; + goto out; + } + + /* Bus error */ + if (status & LOONGSON2_I2C_SR1_BERR) { + regmap_update_bits(priv->regmap, LOONGSON2_I2C_SR1, LOONGSON2_I2C_SR1_BERR, 0); + msg->result = -EIO; + goto out; + } + +out: + loongson2_i2c_disable_irq(priv); + complete(&priv->complete); +} + +static void loongson2_i2c_handle_read(struct loongson2_i2c_priv *priv) +{ + struct loongson2_i2c_msg *msg = &priv->msg; + + switch (msg->count) { + case 1: + loongson2_i2c_disable_irq(priv); + loongson2_i2c_read_msg(priv); + complete(&priv->complete); + break; + case 2: + case 3: + /* + * For 2-byte/3-byte reception and for N-byte reception with N > 3, we have to + * wait for byte transferred finished event before reading data. + * Just disable buffer interrupt in order to avoid another system preemption due + * to RX not empty event. + */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR2, LOONGSON2_I2C_CR2_ITBUFEN, 0); + break; + default: + /* + * For N byte reception with N > 3 we directly read data register + * until N-2 data. + */ + loongson2_i2c_read_msg(priv); + break; + } +} + +static void loongson2_i2c_handle_rx_done(struct loongson2_i2c_priv *priv) +{ + struct loongson2_i2c_msg *msg = &priv->msg; + + switch (msg->count) { + case 2: + /* + * The STOP/START bit has to be set before reading the last two bytes. + * After that, we could read the last two bytes. + */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_OP_MASK, + msg->stop ? LOONGSON2_I2C_CR1_STOP : LOONGSON2_I2C_CR1_START); + + for (unsigned int i = msg->count; i > 0; i--) + loongson2_i2c_read_msg(priv); + + loongson2_i2c_disable_irq(priv); + + complete(&priv->complete); + break; + case 3: + /* + * In order to generate the NACK after the last received data byte, enable NACK + * before reading N-2 data. + */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_ACK, 0); + loongson2_i2c_read_msg(priv); + break; + default: + loongson2_i2c_read_msg(priv); + break; + } +} + +static irqreturn_t loongson2_i2c_isr_event(int irq, void *data) +{ + struct loongson2_i2c_priv *priv = data; + struct device *dev = regmap_get_device(priv->regmap); + struct loongson2_i2c_msg *msg = &priv->msg; + u32 status, ien, event, cr2, possible_status; + + regmap_read(priv->regmap, LOONGSON2_I2C_SR1, &status); + if (status & LOONGSON2_I2C_SR1_ITERREN_MASK) { + loongson2_i2c_isr_error(status, data); + return IRQ_NONE; + } + + regmap_read(priv->regmap, LOONGSON2_I2C_CR2, &cr2); + ien = cr2 & LOONGSON2_I2C_CR2_INT_MASK; + + /* Update possible_status if buffer interrupt is enabled */ + possible_status = LOONGSON2_I2C_SR1_ITEVTEN_MASK; + if (ien & LOONGSON2_I2C_CR2_ITBUFEN) + possible_status |= LOONGSON2_I2C_SR1_ITBUFEN_MASK; + + event = status & possible_status; + if (!event) { + dev_dbg(dev, "spurious evt IRQ (status=0x%08x, ien=0x%08x)\n", status, ien); + return IRQ_NONE; + } + + /* Start condition generated */ + if (event & LOONGSON2_I2C_SR1_SB) + loongson2_i2c_write_msg(priv, msg->addr); + + /* I2C Address sent */ + if (event & LOONGSON2_I2C_SR1_ADDR) { + if (msg->addr & I2C_M_RD) + loongson2_i2c_handle_rx_addr(priv); + /* Clear ADDR flag */ + regmap_read(priv->regmap, LOONGSON2_I2C_SR2, &status); + /* Enable buffer interrupts for RX/TX not empty events */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR2, LOONGSON2_I2C_CR2_ITBUFEN, + LOONGSON2_I2C_CR2_ITBUFEN); + } + + /* TX empty */ + if ((event & LOONGSON2_I2C_SR1_TXE) && !(msg->addr & I2C_M_RD)) + loongson2_i2c_handle_write(priv); + + /* RX not empty */ + if ((event & LOONGSON2_I2C_SR1_RXNE) && (msg->addr & I2C_M_RD)) + loongson2_i2c_handle_read(priv); + + /* + * The BTF (Byte Transfer finished) event occurs when: + * - in reception: a new byte is received in the shift register + * but the previous byte has not been read yet from data register + * - in transmission: a new byte should be sent but the data register + * has not been written yet + */ + if (event & LOONGSON2_I2C_SR1_BTF) { + if (msg->addr & I2C_M_RD) + loongson2_i2c_handle_rx_done(priv); + else + loongson2_i2c_handle_write(priv); + } + + return IRQ_HANDLED; +} + +static int loongson2_i2c_xfer_msg(struct loongson2_i2c_priv *priv, struct i2c_msg *msg, + bool is_stop) +{ + struct loongson2_i2c_msg *l_msg = &priv->msg; + unsigned long timeout; + + l_msg->addr = i2c_8bit_addr_from_msg(msg); + l_msg->buf = msg->buf; + l_msg->count = msg->len; + l_msg->stop = is_stop; + l_msg->result = 0; + + reinit_completion(&priv->complete); + + /* Enable events and errors interrupts */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR2, + LOONGSON2_I2C_CR2_ITEVTEN | LOONGSON2_I2C_CR2_ITERREN, + LOONGSON2_I2C_CR2_ITEVTEN | LOONGSON2_I2C_CR2_ITERREN); + + timeout = wait_for_completion_timeout(&priv->complete, priv->adapter.timeout); + if (!timeout) + return -ETIMEDOUT; + + return l_msg->result; +} + +static int loongson2_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) +{ + struct loongson2_i2c_priv *priv = i2c_get_adapdata(i2c_adap); + struct device *dev = regmap_get_device(priv->regmap); + unsigned int status; + int ret; + + /* Wait I2C bus free */ + ret = regmap_read_poll_timeout(priv->regmap, LOONGSON2_I2C_SR2, status, + !(status & LOONGSON2_I2C_SR2_BUSY), + LOONGSON2_I2C_FREE_SLEEP_US, + LOONGSON2_I2C_FREE_TIMEOUT_US); + if (ret) { + dev_dbg(dev, "The I2C bus is busy now.\n"); + return ret; + } + + /* Start generation */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_START, + LOONGSON2_I2C_CR1_START); + + for (unsigned int i = 0; i < num; i++) { + ret = loongson2_i2c_xfer_msg(priv, &msgs[i], i == num - 1); + if (ret < 0) + return ret; + } + + return num; +} + +static u32 loongson2_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm loongson2_i2c_algo = { + .xfer = loongson2_i2c_xfer, + .functionality = loongson2_i2c_func, +}; + +static int loongson2_i2c_adjust_bus_speed(struct loongson2_i2c_priv *priv) +{ + struct device *dev = regmap_get_device(priv->regmap); + struct i2c_timings i2c_t; + u32 val, freq_MHz, ccr; + + i2c_parse_fw_timings(dev, &i2c_t, true); + priv->parent_rate_MHz = clk_get_rate(priv->clk); + + if (i2c_t.bus_freq_hz == I2C_MAX_STANDARD_MODE_FREQ) { + /* Select Standard mode */ + ccr = 0; + val = DIV_ROUND_UP(priv->parent_rate_MHz, i2c_t.bus_freq_hz * 2); + } else if (i2c_t.bus_freq_hz == I2C_MAX_FAST_MODE_FREQ) { + /* Select Fast mode */ + ccr = LOONGSON2_I2C_CCR_FS; + val = DIV_ROUND_UP(priv->parent_rate_MHz, i2c_t.bus_freq_hz * 3); + } else { + return dev_err_probe(dev, -EINVAL, "Unsupported speed (%uHz)\n", i2c_t.bus_freq_hz); + } + + FIELD_MODIFY(LOONGSON2_I2C_CCR_CCR, &ccr, val); + regmap_write(priv->regmap, LOONGSON2_I2C_CCR, ccr); + + freq_MHz = DIV_ROUND_UP(priv->parent_rate_MHz, HZ_PER_MHZ); + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR2, LOONGSON2_I2C_CR2_FREQ, + FIELD_GET(LOONGSON2_I2C_CR2_FREQ, freq_MHz)); + + regmap_update_bits(priv->regmap, LOONGSON2_I2C_TRISE, LOONGSON2_I2C_TRISE_SCL, + LOONGSON2_I2C_TRISE_SCL); + + /* Enable I2C */ + regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_PE, + LOONGSON2_I2C_CR1_PE); + + return 0; +} + +static const struct regmap_config loongson2_i2c_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = LOONGSON2_I2C_TRISE, +}; + +static int loongson2_i2c_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct loongson2_i2c_priv *priv; + struct i2c_adapter *adap; + void __iomem *base; + int irq, ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + priv->regmap = devm_regmap_init_mmio(dev, base, &loongson2_i2c_regmap_config); + if (IS_ERR(priv->regmap)) + return dev_err_probe(dev, PTR_ERR(priv->regmap), "Failed to init regmap.\n"); + + priv->clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(priv->clk)) + return dev_err_probe(dev, PTR_ERR(priv->clk), "Failed to enable clock.\n"); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + adap = &priv->adapter; + adap->retries = 5; + adap->nr = pdev->id; + adap->dev.parent = dev; + adap->owner = THIS_MODULE; + adap->algo = &loongson2_i2c_algo; + adap->timeout = 2 * HZ; + device_set_node(&adap->dev, dev_fwnode(dev)); + i2c_set_adapdata(adap, priv); + strscpy(adap->name, pdev->name); + init_completion(&priv->complete); + platform_set_drvdata(pdev, priv); + + ret = loongson2_i2c_adjust_bus_speed(priv); + if (ret) + return ret; + + ret = devm_request_irq(dev, irq, loongson2_i2c_isr_event, IRQF_SHARED, pdev->name, priv); + if (ret) + return ret; + + return devm_i2c_add_adapter(dev, adap); +} + +static const struct of_device_id loongson2_i2c_id_table[] = { + { .compatible = "loongson,ls2k0300-i2c" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, loongson2_i2c_id_table); + +static struct platform_driver loongson2_i2c_driver = { + .driver = { + .name = "loongson2-i2c-v2", + .of_match_table = loongson2_i2c_id_table, + }, + .probe = loongson2_i2c_probe, +}; +module_platform_driver(loongson2_i2c_driver); + +MODULE_DESCRIPTION("Loongson-2K0300 I2C bus driver"); +MODULE_AUTHOR("Loongson Technology Corporation Limited"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c index 08c9091a1e35..4e07babea9c3 100644 --- a/drivers/i2c/busses/i2c-mxs.c +++ b/drivers/i2c/busses/i2c-mxs.c @@ -108,6 +108,14 @@ enum mxs_i2c_devtype { * @cmd_complete: completion object for transaction wait * @cmd_err: error code for last transaction * @adapter: i2c subsystem adapter node + * @timing0: I2C TIMING0 register value + * @timing1: I2C TIMING1 register value + * @timing2: I2C TIMING2 register value + * @dmach: DMA channel + * @pio_data: PIO data for DMA + * @addr_data: address data for DMA + * @sg_io: scatterlist for I/O + * @dma_read: flag indicating DMA read */ struct mxs_i2c_dev { struct device *dev; diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c index a482a4c60744..d2f5055b0b10 100644 --- a/drivers/i2c/busses/i2c-qcom-geni.c +++ b/drivers/i2c/busses/i2c-qcom-geni.c @@ -110,7 +110,6 @@ struct geni_i2c_dev { struct clk *core_clk; u32 clk_freq_out; const struct geni_i2c_clk_fld *clk_fld; - int suspended; void *dma_buf; size_t xfer_len; dma_addr_t dma_addr; @@ -1143,7 +1142,6 @@ static int geni_i2c_probe(struct platform_device *pdev) if (ret) goto err_dma; - gi2c->suspended = 1; pm_runtime_set_suspended(gi2c->se.dev); pm_runtime_set_autosuspend_delay(gi2c->se.dev, I2C_AUTO_SUSPEND_DELAY); pm_runtime_use_autosuspend(gi2c->se.dev); @@ -1200,9 +1198,6 @@ static int __maybe_unused geni_i2c_runtime_suspend(struct device *dev) if (ret) { enable_irq(gi2c->irq); return ret; - - } else { - gi2c->suspended = 1; } clk_disable_unprepare(gi2c->core_clk); @@ -1228,7 +1223,6 @@ static int __maybe_unused geni_i2c_runtime_resume(struct device *dev) goto out_clk_disable; enable_irq(gi2c->irq); - gi2c->suspended = 0; return 0; @@ -1243,21 +1237,25 @@ out_icc_disable: static int __maybe_unused geni_i2c_suspend_noirq(struct device *dev) { struct geni_i2c_dev *gi2c = dev_get_drvdata(dev); + int ret; i2c_mark_adapter_suspended(&gi2c->adap); - if (!gi2c->suspended) { - geni_i2c_runtime_suspend(dev); - pm_runtime_disable(dev); - pm_runtime_set_suspended(dev); - pm_runtime_enable(dev); - } - return 0; + ret = pm_runtime_force_suspend(dev); + if (ret) + i2c_mark_adapter_resumed(&gi2c->adap); + + return ret; } static int __maybe_unused geni_i2c_resume_noirq(struct device *dev) { struct geni_i2c_dev *gi2c = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret) + return ret; i2c_mark_adapter_resumed(&gi2c->adap); return 0; diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c index a19c3d251804..3d0638c2bc51 100644 --- a/drivers/i2c/busses/i2c-sis630.c +++ b/drivers/i2c/busses/i2c-sis630.c @@ -431,24 +431,23 @@ static int sis630_setup(struct pci_dev *sis630_dev) in acpi io space and read acpi base addr */ if (pci_read_config_byte(sis630_dev, SIS630_BIOS_CTL_REG, &b)) { - dev_err(&sis630_dev->dev, "Error: Can't read bios ctl reg\n"); - retval = -ENODEV; + retval = dev_err_probe(&sis630_dev->dev, -ENODEV, + "Error: Can't read bios ctl reg\n"); goto exit; } /* if ACPI already enabled , do nothing */ if (!(b & 0x80) && pci_write_config_byte(sis630_dev, SIS630_BIOS_CTL_REG, b | 0x80)) { - dev_err(&sis630_dev->dev, "Error: Can't enable ACPI\n"); - retval = -ENODEV; + retval = dev_err_probe(&sis630_dev->dev, -ENODEV, + "Error: Can't enable ACPI\n"); goto exit; } /* Determine the ACPI base address */ if (pci_read_config_word(sis630_dev, SIS630_ACPI_BASE_REG, &acpi_base)) { - dev_err(&sis630_dev->dev, - "Error: Can't determine ACPI base address\n"); - retval = -ENODEV; + retval = dev_err_probe(&sis630_dev->dev, -ENODEV, + "Error: Can't determine ACPI base address\n"); goto exit; } @@ -469,11 +468,10 @@ static int sis630_setup(struct pci_dev *sis630_dev) /* Everything is happy, let's grab the memory and set things up. */ if (!request_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION, sis630_driver.name)) { - dev_err(&sis630_dev->dev, - "I/O Region 0x%04x-0x%04x for SMBus already in use.\n", - smbus_base + SMB_STS, - smbus_base + SMB_STS + SIS630_SMB_IOREGION - 1); - retval = -EBUSY; + retval = dev_err_probe(&sis630_dev->dev, -EBUSY, + "I/O Region 0x%04x-0x%04x for SMBus already in use.\n", + smbus_base + SMB_STS, + smbus_base + SMB_STS + SIS630_SMB_IOREGION - 1); goto exit; } @@ -511,12 +509,9 @@ static int sis630_probe(struct pci_dev *dev, const struct pci_device_id *id) { int ret; - if (sis630_setup(dev)) { - dev_err(&dev->dev, - "SIS630 compatible bus not detected, " - "module not inserted.\n"); - return -ENODEV; - } + if (sis630_setup(dev)) + return dev_err_probe(&dev->dev, -ENODEV, + "Compatible bus not detected, module not inserted.\n"); /* set up the sysfs linkage to our parent device */ sis630_adapter.dev.parent = &dev->dev; diff --git a/drivers/i2c/busses/i2c-sis96x.c b/drivers/i2c/busses/i2c-sis96x.c index 77529dda6fcd..eee41dc9d706 100644 --- a/drivers/i2c/busses/i2c-sis96x.c +++ b/drivers/i2c/busses/i2c-sis96x.c @@ -245,23 +245,19 @@ static int sis96x_probe(struct pci_dev *dev, u16 ww = 0; int retval; - if (sis96x_smbus_base) { - dev_err(&dev->dev, "Only one device supported.\n"); - return -EBUSY; - } + if (sis96x_smbus_base) + return dev_err_probe(&dev->dev, -EBUSY, "Only one device supported.\n"); pci_read_config_word(dev, PCI_CLASS_DEVICE, &ww); - if (PCI_CLASS_SERIAL_SMBUS != ww) { - dev_err(&dev->dev, "Unsupported device class 0x%04x!\n", ww); - return -ENODEV; - } + if (ww != PCI_CLASS_SERIAL_SMBUS) + return dev_err_probe(&dev->dev, -ENODEV, + "Unsupported device class 0x%04x!\n", ww); sis96x_smbus_base = pci_resource_start(dev, SIS96x_BAR); - if (!sis96x_smbus_base) { - dev_err(&dev->dev, "SiS96x SMBus base address " - "not initialized!\n"); - return -EINVAL; - } + if (!sis96x_smbus_base) + return dev_err_probe(&dev->dev, -EINVAL, + "SiS96x SMBus base address not initialized!\n"); + dev_info(&dev->dev, "SiS96x SMBus base address: 0x%04x\n", sis96x_smbus_base); @@ -272,9 +268,9 @@ static int sis96x_probe(struct pci_dev *dev, /* Everything is happy, let's grab the memory and set things up. */ if (!request_region(sis96x_smbus_base, SMB_IOSIZE, sis96x_driver.name)) { - dev_err(&dev->dev, "SMBus registers 0x%04x-0x%04x " - "already in use!\n", sis96x_smbus_base, - sis96x_smbus_base + SMB_IOSIZE - 1); + dev_err_probe(&dev->dev, -EINVAL, + "SMBus registers 0x%04x-0x%04x already in use!\n", + sis96x_smbus_base, sis96x_smbus_base + SMB_IOSIZE - 1); sis96x_smbus_base = 0; return -EINVAL; @@ -287,7 +283,7 @@ static int sis96x_probe(struct pci_dev *dev, "SiS96x SMBus adapter at 0x%04x", sis96x_smbus_base); if ((retval = i2c_add_adapter(&sis96x_adapter))) { - dev_err(&dev->dev, "Couldn't register adapter!\n"); + dev_err_probe(&dev->dev, retval, "Couldn't register adapter!\n"); release_region(sis96x_smbus_base, SMB_IOSIZE); sis96x_smbus_base = 0; } diff --git a/drivers/i2c/busses/i2c-sprd.c b/drivers/i2c/busses/i2c-sprd.c index 1b490525d8dd..7b321a956fca 100644 --- a/drivers/i2c/busses/i2c-sprd.c +++ b/drivers/i2c/busses/i2c-sprd.c @@ -469,11 +469,10 @@ static int sprd_i2c_clk_init(struct sprd_i2c *i2c_dev) i2c_dev->adap.nr, i2c_dev->src_clk); i2c_dev->clk = devm_clk_get(i2c_dev->dev, "enable"); - if (IS_ERR(i2c_dev->clk)) { - dev_err(i2c_dev->dev, "i2c%d can't get the enable clock\n", - i2c_dev->adap.nr); - return PTR_ERR(i2c_dev->clk); - } + if (IS_ERR(i2c_dev->clk)) + return dev_err_probe(i2c_dev->dev, PTR_ERR(i2c_dev->clk), + "i2c%d can't get the enable clock\n", + i2c_dev->adap.nr); return 0; } @@ -548,13 +547,13 @@ static int sprd_i2c_probe(struct platform_device *pdev) IRQF_NO_SUSPEND | IRQF_ONESHOT, pdev->name, i2c_dev); if (ret) { - dev_err(&pdev->dev, "failed to request irq %d\n", i2c_dev->irq); + dev_err_probe(&pdev->dev, ret, "failed to request irq %d\n", i2c_dev->irq); goto err_rpm_put; } ret = i2c_add_numbered_adapter(&i2c_dev->adap); if (ret) { - dev_err(&pdev->dev, "add adapter failed\n"); + dev_err_probe(&pdev->dev, ret, "add adapter failed\n"); goto err_rpm_put; } diff --git a/drivers/i2c/busses/i2c-st.c b/drivers/i2c/busses/i2c-st.c index 751ea421caaf..3f89c2145741 100644 --- a/drivers/i2c/busses/i2c-st.c +++ b/drivers/i2c/busses/i2c-st.c @@ -775,17 +775,15 @@ static int st_i2c_of_get_deglitch(struct device_node *np, ret = of_property_read_u32(np, "st,i2c-min-scl-pulse-width-us", &i2c_dev->scl_min_width_us); - if ((ret == -ENODATA) || (ret == -EOVERFLOW)) { - dev_err(i2c_dev->dev, "st,i2c-min-scl-pulse-width-us invalid\n"); - return ret; - } + if ((ret == -ENODATA) || (ret == -EOVERFLOW)) + return dev_err_probe(i2c_dev->dev, ret, + "st,i2c-min-scl-pulse-width-us invalid\n"); ret = of_property_read_u32(np, "st,i2c-min-sda-pulse-width-us", &i2c_dev->sda_min_width_us); - if ((ret == -ENODATA) || (ret == -EOVERFLOW)) { - dev_err(i2c_dev->dev, "st,i2c-min-sda-pulse-width-us invalid\n"); - return ret; - } + if ((ret == -ENODATA) || (ret == -EOVERFLOW)) + return dev_err_probe(i2c_dev->dev, ret, + "st,i2c-min-sda-pulse-width-us invalid\n"); return 0; } @@ -808,16 +806,13 @@ static int st_i2c_probe(struct platform_device *pdev) return PTR_ERR(i2c_dev->base); i2c_dev->irq = irq_of_parse_and_map(np, 0); - if (!i2c_dev->irq) { - dev_err(&pdev->dev, "IRQ missing or invalid\n"); - return -EINVAL; - } + if (!i2c_dev->irq) + return dev_err_probe(&pdev->dev, -EINVAL, "IRQ missing or invalid\n"); i2c_dev->clk = of_clk_get_by_name(np, "ssc"); - if (IS_ERR(i2c_dev->clk)) { - dev_err(&pdev->dev, "Unable to request clock\n"); - return PTR_ERR(i2c_dev->clk); - } + if (IS_ERR(i2c_dev->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2c_dev->clk), + "Unable to request clock\n"); i2c_dev->mode = I2C_MODE_STANDARD; ret = of_property_read_u32(np, "clock-frequency", &clk_rate); @@ -829,10 +824,9 @@ static int st_i2c_probe(struct platform_device *pdev) ret = devm_request_threaded_irq(&pdev->dev, i2c_dev->irq, NULL, st_i2c_isr_thread, IRQF_ONESHOT, pdev->name, i2c_dev); - if (ret) { - dev_err(&pdev->dev, "Failed to request irq %i\n", i2c_dev->irq); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to request irq %i\n", i2c_dev->irq); pinctrl_pm_select_default_state(i2c_dev->dev); /* In case idle state available, select it */ diff --git a/drivers/i2c/busses/i2c-stm32.c b/drivers/i2c/busses/i2c-stm32.c index becf8977979f..064e47d6c96f 100644 --- a/drivers/i2c/busses/i2c-stm32.c +++ b/drivers/i2c/busses/i2c-stm32.c @@ -39,7 +39,7 @@ struct stm32_i2c_dma *stm32_i2c_dma_request(struct device *dev, dma_sconfig.direction = DMA_MEM_TO_DEV; ret = dmaengine_slave_config(dma->chan_tx, &dma_sconfig); if (ret < 0) { - dev_err(dev, "can't configure tx channel\n"); + dev_err_probe(dev, ret, "can't configure tx channel\n"); goto fail_tx; } @@ -60,7 +60,7 @@ struct stm32_i2c_dma *stm32_i2c_dma_request(struct device *dev, dma_sconfig.direction = DMA_DEV_TO_MEM; ret = dmaengine_slave_config(dma->chan_rx, &dma_sconfig); if (ret < 0) { - dev_err(dev, "can't configure rx channel\n"); + dev_err_probe(dev, ret, "can't configure rx channel\n"); goto fail_rx; } diff --git a/drivers/i2c/busses/i2c-stm32f4.c b/drivers/i2c/busses/i2c-stm32f4.c index b3d56d0aa9d0..44e8b04962bb 100644 --- a/drivers/i2c/busses/i2c-stm32f4.c +++ b/drivers/i2c/busses/i2c-stm32f4.c @@ -163,11 +163,9 @@ static int stm32f4_i2c_set_periph_clk_freq(struct stm32f4_i2c_dev *i2c_dev) * to hardware limitation */ if (freq < STM32F4_I2C_MIN_STANDARD_FREQ || - freq > STM32F4_I2C_MAX_FREQ) { - dev_err(i2c_dev->dev, - "bad parent clk freq for standard mode\n"); - return -EINVAL; - } + freq > STM32F4_I2C_MAX_FREQ) + return dev_err_probe(i2c_dev->dev, -EINVAL, + "bad parent clk freq for standard mode\n"); } else { /* * To be as close as possible to 400 kHz, the parent clk @@ -175,11 +173,9 @@ static int stm32f4_i2c_set_periph_clk_freq(struct stm32f4_i2c_dev *i2c_dev) * maximum value of 46 MHz due to hardware limitation */ if (freq < STM32F4_I2C_MIN_FAST_FREQ || - freq > STM32F4_I2C_MAX_FREQ) { - dev_err(i2c_dev->dev, - "bad parent clk freq for fast mode\n"); - return -EINVAL; - } + freq > STM32F4_I2C_MAX_FREQ) + return dev_err_probe(i2c_dev->dev, -EINVAL, + "bad parent clk freq for fast mode\n"); } cr2 |= STM32F4_I2C_CR2_FREQ(freq); @@ -772,22 +768,19 @@ static int stm32f4_i2c_probe(struct platform_device *pdev) return PTR_ERR(i2c_dev->base); irq_event = irq_of_parse_and_map(np, 0); - if (!irq_event) { - dev_err(&pdev->dev, "IRQ event missing or invalid\n"); - return -EINVAL; - } + if (!irq_event) + return dev_err_probe(&pdev->dev, -EINVAL, + "IRQ event missing or invalid\n"); irq_error = irq_of_parse_and_map(np, 1); - if (!irq_error) { - dev_err(&pdev->dev, "IRQ error missing or invalid\n"); - return -EINVAL; - } + if (!irq_error) + return dev_err_probe(&pdev->dev, -EINVAL, + "IRQ error missing or invalid\n"); i2c_dev->clk = devm_clk_get_enabled(&pdev->dev, NULL); - if (IS_ERR(i2c_dev->clk)) { - dev_err(&pdev->dev, "Failed to enable clock\n"); - return PTR_ERR(i2c_dev->clk); - } + if (IS_ERR(i2c_dev->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2c_dev->clk), + "Failed to enable clock\n"); rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (IS_ERR(rst)) @@ -807,19 +800,15 @@ static int stm32f4_i2c_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, irq_event, stm32f4_i2c_isr_event, 0, pdev->name, i2c_dev); - if (ret) { - dev_err(&pdev->dev, "Failed to request irq event %i\n", - irq_event); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to request irq event %i\n", irq_event); ret = devm_request_irq(&pdev->dev, irq_error, stm32f4_i2c_isr_error, 0, pdev->name, i2c_dev); - if (ret) { - dev_err(&pdev->dev, "Failed to request irq error %i\n", - irq_error); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to request irq error %i\n", irq_error); ret = stm32f4_i2c_hw_config(i2c_dev); if (ret) diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c index 067af255bd22..16c6e61c7e11 100644 --- a/drivers/i2c/busses/i2c-stm32f7.c +++ b/drivers/i2c/busses/i2c-stm32f7.c @@ -481,28 +481,22 @@ static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev, int ret = 0; specs = stm32f7_get_specs(setup->speed_freq); - if (specs == ERR_PTR(-EINVAL)) { - dev_err(i2c_dev->dev, "speed out of bound {%d}\n", - setup->speed_freq); - return -EINVAL; - } + if (specs == ERR_PTR(-EINVAL)) + return dev_err_probe(i2c_dev->dev, -EINVAL, "speed out of bound {%d}\n", + setup->speed_freq); if ((setup->rise_time > specs->rise_max) || - (setup->fall_time > specs->fall_max)) { - dev_err(i2c_dev->dev, - "timings out of bound Rise{%d>%d}/Fall{%d>%d}\n", - setup->rise_time, specs->rise_max, - setup->fall_time, specs->fall_max); - return -EINVAL; - } + (setup->fall_time > specs->fall_max)) + return dev_err_probe(i2c_dev->dev, -EINVAL, + "timings out of bound Rise{%d>%d}/Fall{%d>%d}\n", + setup->rise_time, specs->rise_max, + setup->fall_time, specs->fall_max); i2c_dev->dnf = DIV_ROUND_CLOSEST(i2c_dev->dnf_dt, i2cclk); - if (i2c_dev->dnf > STM32F7_I2C_DNF_MAX) { - dev_err(i2c_dev->dev, - "DNF out of bound %d/%d\n", - i2c_dev->dnf * i2cclk, STM32F7_I2C_DNF_MAX * i2cclk); - return -EINVAL; - } + if (i2c_dev->dnf > STM32F7_I2C_DNF_MAX) + return dev_err_probe(i2c_dev->dev, -EINVAL, + "DNF out of bound %d/%d\n", i2c_dev->dnf * i2cclk, + STM32F7_I2C_DNF_MAX * i2cclk); /* Analog and Digital Filters */ af_delay_min = @@ -567,8 +561,7 @@ static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev, } if (list_empty(&solutions)) { - dev_err(i2c_dev->dev, "no Prescaler solution\n"); - ret = -EPERM; + ret = dev_err_probe(i2c_dev->dev, -EPERM, "no Prescaler solution\n"); goto exit; } @@ -624,8 +617,7 @@ static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev, } if (!s) { - dev_err(i2c_dev->dev, "no solution at all\n"); - ret = -EPERM; + ret = dev_err_probe(i2c_dev->dev, -EPERM, "no solution at all\n"); goto exit; } @@ -674,11 +666,9 @@ static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev, i2c_parse_fw_timings(i2c_dev->dev, t, false); - if (t->bus_freq_hz > I2C_MAX_FAST_MODE_PLUS_FREQ) { - dev_err(i2c_dev->dev, "Invalid bus speed (%i>%i)\n", - t->bus_freq_hz, I2C_MAX_FAST_MODE_PLUS_FREQ); - return -EINVAL; - } + if (t->bus_freq_hz > I2C_MAX_FAST_MODE_PLUS_FREQ) + return dev_err_probe(i2c_dev->dev, -EINVAL, "Invalid bus speed (%i>%i)\n", + t->bus_freq_hz, I2C_MAX_FAST_MODE_PLUS_FREQ); setup->speed_freq = t->bus_freq_hz; i2c_dev->setup.rise_time = t->scl_rise_ns; @@ -686,10 +676,8 @@ static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev, i2c_dev->dnf_dt = t->digital_filter_width_ns; setup->clock_src = clk_get_rate(i2c_dev->clk); - if (!setup->clock_src) { - dev_err(i2c_dev->dev, "clock rate is 0\n"); - return -EINVAL; - } + if (!setup->clock_src) + return dev_err_probe(i2c_dev->dev, -EINVAL, "clock rate is 0\n"); if (!of_property_read_bool(i2c_dev->dev->of_node, "i2c-digital-filter")) i2c_dev->dnf_dt = STM32F7_I2C_DNF_DEFAULT; @@ -701,8 +689,8 @@ static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev, ret = stm32f7_i2c_compute_timing(i2c_dev, setup, &i2c_dev->timing); if (ret) { - dev_err(i2c_dev->dev, - "failed to compute I2C timings.\n"); + dev_err_probe(i2c_dev->dev, ret, + "failed to compute I2C timings.\n"); if (setup->speed_freq <= I2C_MAX_STANDARD_MODE_FREQ) break; setup->speed_freq = @@ -713,10 +701,8 @@ static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev, } } while (ret); - if (ret) { - dev_err(i2c_dev->dev, "Impossible to compute I2C timings.\n"); - return ret; - } + if (ret) + return dev_err_probe(i2c_dev->dev, ret, "Impossible to compute I2C timings.\n"); dev_dbg(i2c_dev->dev, "I2C Speed(%i), Clk Source(%i)\n", setup->speed_freq, setup->clock_src); @@ -2175,10 +2161,8 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) return -ENOMEM; setup = of_device_get_match_data(&pdev->dev); - if (!setup) { - dev_err(&pdev->dev, "Can't get device data\n"); - return -ENODEV; - } + if (!setup) + return dev_err_probe(&pdev->dev, -ENODEV, "Can't get device data\n"); i2c_dev->setup = *setup; i2c_dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); @@ -2279,7 +2263,7 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) ret = dev_pm_set_wake_irq(i2c_dev->dev, irq_event); if (ret) { - dev_err(i2c_dev->dev, "Failed to set wake up irq\n"); + dev_err_probe(i2c_dev->dev, ret, "Failed to set wake up irq\n"); goto clr_wakeup_capable; } } @@ -2305,9 +2289,8 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) if (i2c_dev->smbus_mode) { ret = stm32f7_i2c_enable_smbus_host(i2c_dev); if (ret) { - dev_err(i2c_dev->dev, - "failed to enable SMBus Host-Notify protocol (%d)\n", - ret); + dev_err_probe(i2c_dev->dev, ret, + "failed to enable SMBus Host-Notify protocol\n"); goto i2c_adapter_remove; } } @@ -2315,9 +2298,8 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) if (of_property_read_bool(pdev->dev.of_node, "smbus-alert")) { ret = stm32f7_i2c_enable_smbus_alert(i2c_dev); if (ret) { - dev_err(i2c_dev->dev, - "failed to enable SMBus alert protocol (%d)\n", - ret); + dev_err_probe(i2c_dev->dev, ret, + "failed to enable SMBus alert protocol\n"); goto i2c_disable_smbus_host; } } diff --git a/drivers/i2c/busses/i2c-sun6i-p2wi.c b/drivers/i2c/busses/i2c-sun6i-p2wi.c index fb5280b8cf7f..dffbe776a195 100644 --- a/drivers/i2c/busses/i2c-sun6i-p2wi.c +++ b/drivers/i2c/busses/i2c-sun6i-p2wi.c @@ -194,22 +194,16 @@ static int p2wi_probe(struct platform_device *pdev) int ret; of_property_read_u32(np, "clock-frequency", &clk_freq); - if (clk_freq > P2WI_MAX_FREQ) { - dev_err(dev, - "required clock-frequency (%u Hz) is too high (max = 6MHz)", - clk_freq); - return -EINVAL; - } + if (clk_freq > P2WI_MAX_FREQ) + return dev_err_probe(dev, -EINVAL, + "required clock-frequency (%u Hz) is too high (max = 6MHz)", + clk_freq); - if (clk_freq == 0) { - dev_err(dev, "clock-frequency is set to 0 in DT\n"); - return -EINVAL; - } + if (clk_freq == 0) + return dev_err_probe(dev, -EINVAL, "clock-frequency is set to 0 in DT\n"); - if (of_get_child_count(np) > 1) { - dev_err(dev, "P2WI only supports one target device\n"); - return -EINVAL; - } + if (of_get_child_count(np) > 1) + return dev_err_probe(dev, -EINVAL, "P2WI only supports one target device\n"); p2wi = devm_kzalloc(dev, sizeof(struct p2wi), GFP_KERNEL); if (!p2wi) @@ -226,11 +220,9 @@ static int p2wi_probe(struct platform_device *pdev) childnp = of_get_next_available_child(np, NULL); if (childnp) { ret = of_property_read_u32(childnp, "reg", &target_addr); - if (ret) { - dev_err(dev, "invalid target address on node %pOF\n", - childnp); - return -EINVAL; - } + if (ret) + return dev_err_probe(dev, -EINVAL, + "invalid target address on node %pOF\n", childnp); p2wi->target_addr = target_addr; } @@ -245,26 +237,20 @@ static int p2wi_probe(struct platform_device *pdev) return irq; p2wi->clk = devm_clk_get_enabled(dev, NULL); - if (IS_ERR(p2wi->clk)) { - ret = PTR_ERR(p2wi->clk); - dev_err(dev, "failed to enable clk: %d\n", ret); - return ret; - } + if (IS_ERR(p2wi->clk)) + return dev_err_probe(dev, PTR_ERR(p2wi->clk), + "failed to enable clk\n"); parent_clk_freq = clk_get_rate(p2wi->clk); p2wi->rstc = devm_reset_control_get_exclusive(dev, NULL); - if (IS_ERR(p2wi->rstc)) { - dev_err(dev, "failed to retrieve reset controller: %pe\n", - p2wi->rstc); - return PTR_ERR(p2wi->rstc); - } + if (IS_ERR(p2wi->rstc)) + return dev_err_probe(dev, PTR_ERR(p2wi->rstc), + "failed to retrieve reset controller\n"); ret = reset_control_deassert(p2wi->rstc); - if (ret) { - dev_err(dev, "failed to deassert reset line: %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to deassert reset line\n"); init_completion(&p2wi->complete); p2wi->adapter.dev.parent = dev; @@ -276,8 +262,7 @@ static int p2wi_probe(struct platform_device *pdev) ret = devm_request_irq(dev, irq, p2wi_interrupt, 0, pdev->name, p2wi); if (ret) { - dev_err(dev, "can't register interrupt handler irq%d: %d\n", - irq, ret); + dev_err_probe(dev, ret, "can't register interrupt handler irq%d\n", irq); goto err_reset_assert; } diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 400c9089daf0..9c281b4905eb 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -164,6 +164,7 @@ struct tegra_i2c_regs { unsigned int master_reset_cntrl; unsigned int mst_fifo_control; unsigned int mst_fifo_status; + unsigned int fairness_arb; unsigned int sw_mutex; }; @@ -272,6 +273,7 @@ static const struct tegra_i2c_regs tegra264_i2c_regs = { .master_reset_cntrl = 0x0a8, .mst_fifo_control = 0x0b4, .mst_fifo_status = 0x0b8, + .fairness_arb = 0x0e8, .sw_mutex = 0x0ec, }; @@ -300,6 +302,7 @@ static const struct tegra_i2c_regs tegra410_i2c_regs = { .master_reset_cntrl = 0x0ac, .mst_fifo_control = 0x0b8, .mst_fifo_status = 0x0bc, + .fairness_arb = 0x0ec, .sw_mutex = 0x0f0, }; @@ -379,6 +382,7 @@ enum tegra_i2c_variant { * 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. + * @has_fairarb_reg: Has fairness arbitration register for SMBUS/MCTP support. * @variant: This represents the I2C controller variant. * @regs: Register offsets for the specific SoC variant. */ @@ -412,6 +416,7 @@ struct tegra_i2c_hw_feature { bool has_interface_timing_reg; bool enable_hs_mode_support; bool has_mutex; + bool has_fairarb_reg; enum tegra_i2c_variant variant; const struct tegra_i2c_regs *regs; }; @@ -436,6 +441,7 @@ struct tegra_i2c_hw_feature { * @msg_read: indicates that the transfer is a read access * @timings: i2c timings information like bus frequency * @multimaster_mode: indicates that I2C controller is in multi-master mode + * @is_mctp: indicates that the I2C controller is used as an MCTP controller * @dma_chan: DMA channel * @dma_phys: handle to DMA resources * @dma_buf: pointer to allocated DMA buffer @@ -476,6 +482,7 @@ struct tegra_i2c_dev { void *dma_buf; bool multimaster_mode; + bool is_mctp; bool atomic_mode; bool dma_mode; bool msg_read; @@ -709,15 +716,15 @@ static int tegra_i2c_init_dma(struct tegra_i2c_dev *i2c_dev) goto err_out; } - i2c_dev->dma_dev = i2c_dev->dma_chan->device->dev; + i2c_dev->dma_dev = dmaengine_get_dma_device(i2c_dev->dma_chan); i2c_dev->dma_buf_size = i2c_dev->hw->quirks->max_write_len + I2C_PACKET_HEADER_SIZE; dma_buf = dma_alloc_coherent(i2c_dev->dma_dev, i2c_dev->dma_buf_size, &dma_phys, GFP_KERNEL | __GFP_NOWARN); if (!dma_buf) { - dev_err(i2c_dev->dev, "failed to allocate DMA buffer\n"); - err = -ENOMEM; + err = dev_err_probe(i2c_dev->dev, -ENOMEM, + "failed to allocate DMA buffer\n"); goto err_out; } @@ -729,8 +736,7 @@ static int tegra_i2c_init_dma(struct tegra_i2c_dev *i2c_dev) err_out: tegra_i2c_release_dma(i2c_dev); if (err != -EPROBE_DEFER) { - dev_err(i2c_dev->dev, "cannot use DMA: %d\n", err); - dev_err(i2c_dev->dev, "falling back to PIO\n"); + dev_err(i2c_dev->dev, "cannot use DMA, falling back to PIO\n"); return 0; } @@ -911,6 +917,10 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) if (IS_VI(i2c_dev)) tegra_i2c_vi_init(i2c_dev); + /* Disable fairness arbitration if not an MCTP controller */ + if (i2c_dev->hw->has_fairarb_reg && !i2c_dev->is_mctp) + i2c_writel(i2c_dev, 0, i2c_dev->hw->regs->fairness_arb); + if (i2c_dev->hw->enable_hs_mode_support) max_bus_freq_hz = I2C_MAX_HIGH_SPEED_MODE_FREQ; else @@ -1778,6 +1788,7 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = { .has_interface_timing_reg = false, .enable_hs_mode_support = false, .has_mutex = false, + .has_fairarb_reg = false, .variant = TEGRA_I2C_VARIANT_DEFAULT, .regs = &tegra20_i2c_regs, }; @@ -1811,6 +1822,7 @@ static const struct tegra_i2c_hw_feature tegra20_dvc_i2c_hw = { .has_interface_timing_reg = false, .enable_hs_mode_support = false, .has_mutex = false, + .has_fairarb_reg = false, .variant = TEGRA_I2C_VARIANT_DVC, .regs = &tegra20_dvc_i2c_regs, }; @@ -1844,6 +1856,7 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { .has_interface_timing_reg = false, .enable_hs_mode_support = false, .has_mutex = false, + .has_fairarb_reg = false, .variant = TEGRA_I2C_VARIANT_DEFAULT, .regs = &tegra20_i2c_regs, }; @@ -1876,6 +1889,7 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { .has_interface_timing_reg = false, .enable_hs_mode_support = false, .has_mutex = false, + .has_fairarb_reg = false, .variant = TEGRA_I2C_VARIANT_DEFAULT, .regs = &tegra20_i2c_regs, }; @@ -1908,6 +1922,7 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { .has_interface_timing_reg = true, .enable_hs_mode_support = false, .has_mutex = false, + .has_fairarb_reg = false, .variant = TEGRA_I2C_VARIANT_DEFAULT, .regs = &tegra20_i2c_regs, }; @@ -1940,6 +1955,7 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { .has_interface_timing_reg = true, .enable_hs_mode_support = false, .has_mutex = false, + .has_fairarb_reg = false, .variant = TEGRA_I2C_VARIANT_DEFAULT, .regs = &tegra20_i2c_regs, }; @@ -1973,6 +1989,7 @@ static const struct tegra_i2c_hw_feature tegra210_vi_i2c_hw = { .has_interface_timing_reg = true, .enable_hs_mode_support = false, .has_mutex = false, + .has_fairarb_reg = false, .variant = TEGRA_I2C_VARIANT_VI, .regs = &tegra210_vi_i2c_regs, }; @@ -2006,6 +2023,7 @@ static const struct tegra_i2c_hw_feature tegra186_i2c_hw = { .has_interface_timing_reg = true, .enable_hs_mode_support = false, .has_mutex = false, + .has_fairarb_reg = false, .variant = TEGRA_I2C_VARIANT_DEFAULT, .regs = &tegra20_i2c_regs, }; @@ -2040,6 +2058,7 @@ static const struct tegra_i2c_hw_feature tegra194_i2c_hw = { .has_interface_timing_reg = true, .enable_hs_mode_support = true, .has_mutex = false, + .has_fairarb_reg = false, .variant = TEGRA_I2C_VARIANT_DEFAULT, .regs = &tegra20_i2c_regs, }; @@ -2074,6 +2093,7 @@ static const struct tegra_i2c_hw_feature tegra256_i2c_hw = { .has_interface_timing_reg = true, .enable_hs_mode_support = true, .has_mutex = true, + .has_fairarb_reg = true, .variant = TEGRA_I2C_VARIANT_DEFAULT, .regs = &tegra264_i2c_regs, }; @@ -2108,6 +2128,7 @@ static const struct tegra_i2c_hw_feature tegra264_i2c_hw = { .has_interface_timing_reg = true, .enable_hs_mode_support = true, .has_mutex = true, + .has_fairarb_reg = true, .variant = TEGRA_I2C_VARIANT_DEFAULT, .regs = &tegra264_i2c_regs, }; @@ -2142,6 +2163,7 @@ static const struct tegra_i2c_hw_feature tegra410_i2c_hw = { .has_interface_timing_reg = true, .enable_hs_mode_support = true, .has_mutex = true, + .has_fairarb_reg = true, .variant = TEGRA_I2C_VARIANT_DEFAULT, .regs = &tegra410_i2c_regs, }; @@ -2174,6 +2196,7 @@ static void tegra_i2c_parse_dt(struct tegra_i2c_dev *i2c_dev) multi_mode = device_property_read_bool(i2c_dev->dev, "multi-master"); i2c_dev->multimaster_mode = multi_mode; + i2c_dev->is_mctp = device_property_present(i2c_dev->dev, "mctp-controller"); } static int tegra_i2c_init_clocks(struct tegra_i2c_dev *i2c_dev) @@ -2207,7 +2230,7 @@ static int tegra_i2c_init_clocks(struct tegra_i2c_dev *i2c_dev) err = clk_enable(i2c_dev->div_clk); if (err) { - dev_err(i2c_dev->dev, "failed to enable div-clk: %d\n", err); + dev_err_probe(i2c_dev->dev, err, "failed to enable div-clk\n"); goto unprepare_clocks; } @@ -2233,7 +2256,7 @@ static int tegra_i2c_init_hardware(struct tegra_i2c_dev *i2c_dev) ret = pm_runtime_get_sync(i2c_dev->dev); if (ret < 0) - dev_err(i2c_dev->dev, "runtime resume failed: %d\n", ret); + dev_err_probe(i2c_dev->dev, ret, "runtime resume failed\n"); else ret = tegra_i2c_init(i2c_dev); diff --git a/drivers/i2c/busses/i2c-tiny-usb.c b/drivers/i2c/busses/i2c-tiny-usb.c index 88d66593d9fc..73355a56aabf 100644 --- a/drivers/i2c/busses/i2c-tiny-usb.c +++ b/drivers/i2c/busses/i2c-tiny-usb.c @@ -254,9 +254,8 @@ static int i2c_tiny_usb_probe(struct usb_interface *interface, dev->usb_dev->bus->busnum, dev->usb_dev->devnum); if (usb_write(&dev->adapter, CMD_SET_DELAY, delay, 0, NULL, 0) != 0) { - dev_err(&dev->adapter.dev, - "failure setting delay to %dus\n", delay); - retval = -EIO; + retval = dev_err_probe(&dev->adapter.dev, -EIO, + "failure setting delay to %dus\n", delay); goto error; } diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c index 28c0e4884a7f..e5fddacae9a4 100644 --- a/drivers/i2c/i2c-core-acpi.c +++ b/drivers/i2c/i2c-core-acpi.c @@ -84,8 +84,11 @@ static int i2c_acpi_resource_count(struct acpi_resource *ares, void *data) * i2c_acpi_client_count - Count the number of I2cSerialBus resources * @adev: ACPI device * - * Returns the number of I2cSerialBus resources in the ACPI-device's + * Return: + * The number of I2cSerialBus resources in the ACPI-device's * resource-list; or a negative error code. + * + * Specifically returns -ENOENT when no resources found. */ int i2c_acpi_client_count(struct acpi_device *adev) { @@ -97,7 +100,7 @@ int i2c_acpi_client_count(struct acpi_device *adev) return ret; acpi_dev_free_resource_list(&r); - return count; + return count ?: -ENOENT; } EXPORT_SYMBOL_GPL(i2c_acpi_client_count); diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index a2132d70fb36..3ec04787a737 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -63,6 +63,7 @@ static DEFINE_MUTEX(core_lock); static DEFINE_IDR(i2c_adapter_idr); +static void i2c_deregister_clients(struct i2c_adapter *adap); static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver); static DEFINE_STATIC_KEY_FALSE(i2c_trace_msg_key); @@ -1106,8 +1107,8 @@ EXPORT_SYMBOL(i2c_find_device_by_fwnode); static const struct i2c_device_id dummy_id[] = { - { "dummy", }, - { "smbus_host_notify", }, + { .name = "dummy" }, + { .name = "smbus_host_notify" }, { } }; @@ -1515,23 +1516,48 @@ int i2c_handle_smbus_host_notify(struct i2c_adapter *adap, unsigned short addr) } EXPORT_SYMBOL_GPL(i2c_handle_smbus_host_notify); +static int i2c_allocate_adapter_id(struct i2c_adapter *adap) +{ + int id, start, end; + + if (adap->nr == -1) { + start = __i2c_first_dynamic_bus_num; + end = 0; + } else { + start = adap->nr; + end = adap->nr + 1; + } + + mutex_lock(&core_lock); + id = idr_alloc(&i2c_adapter_idr, NULL, start, end, GFP_KERNEL); + mutex_unlock(&core_lock); + if (id < 0) { + if (adap->nr != -1 && id == -ENOSPC) + id = -EBUSY; + pr_err("adapter '%s': failed to allocate id: %d\n", adap->name, id); + return id; + } + + adap->nr = id; + + return 0; +} + static int i2c_register_adapter(struct i2c_adapter *adap) { - int res = -EINVAL; + int res; /* Can't register until after driver model init */ - if (WARN_ON(!is_registered)) { - res = -EAGAIN; - goto out_list; - } + if (WARN_ON(!is_registered)) + return -EAGAIN; /* Sanity checks */ if (WARN(!adap->name[0], "i2c adapter has no name")) - goto out_list; + return -EINVAL; if (!adap->algo) { pr_err("adapter '%s': no algo supplied!\n", adap->name); - goto out_list; + return -EINVAL; } if (!adap->lock_ops) @@ -1552,14 +1578,25 @@ static int i2c_register_adapter(struct i2c_adapter *adap) if (res) { pr_err("adapter '%s': can't create Host Notify IRQs (%d)\n", adap->name, res); - goto out_list; + return res; } - dev_set_name(&adap->dev, "i2c-%d", adap->nr); + res = i2c_allocate_adapter_id(adap); + if (res) + goto err_remove_irq_domain; + + res = dev_set_name(&adap->dev, "i2c-%d", adap->nr); + if (res) + goto err_free_id; + adap->dev.bus = &i2c_bus_type; adap->dev.type = &i2c_adapter_type; device_initialize(&adap->dev); + res = i2c_init_recovery(adap); + if (res == -EPROBE_DEFER) + goto err_put_adap; + /* * This adapter can be used as a parent immediately after device_add(), * setup runtime-pm (especially ignore-children) before hand. @@ -1569,22 +1606,21 @@ static int i2c_register_adapter(struct i2c_adapter *adap) pm_suspend_ignore_children(&adap->dev, true); pm_runtime_enable(&adap->dev); + adap->debugfs = debugfs_create_dir(dev_name(&adap->dev), i2c_debugfs_root); + + mutex_lock(&core_lock); + idr_replace(&i2c_adapter_idr, adap, adap->nr); + mutex_unlock(&core_lock); + res = device_add(&adap->dev); if (res) { pr_err("adapter '%s': can't register device (%d)\n", adap->name, res); - put_device(&adap->dev); - goto out_list; + goto err_replace_id; } - adap->debugfs = debugfs_create_dir(dev_name(&adap->dev), i2c_debugfs_root); - res = i2c_setup_smbus_alert(adap); if (res) - goto out_reg; - - res = i2c_init_recovery(adap); - if (res == -EPROBE_DEFER) - goto out_reg; + goto err_deregister_clients; dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name); @@ -1603,36 +1639,27 @@ static int i2c_register_adapter(struct i2c_adapter *adap) return 0; -out_reg: +err_deregister_clients: + i2c_deregister_clients(adap); + device_del(&adap->dev); +err_replace_id: + mutex_lock(&core_lock); + idr_replace(&i2c_adapter_idr, NULL, adap->nr); + mutex_unlock(&core_lock); debugfs_remove_recursive(adap->debugfs); + pm_runtime_disable(&adap->dev); +err_put_adap: init_completion(&adap->dev_released); - device_unregister(&adap->dev); + put_device(&adap->dev); wait_for_completion(&adap->dev_released); -out_list: +err_free_id: mutex_lock(&core_lock); idr_remove(&i2c_adapter_idr, adap->nr); mutex_unlock(&core_lock); - return res; -} - -/** - * __i2c_add_numbered_adapter - i2c_add_numbered_adapter where nr is never -1 - * @adap: the adapter to register (with adap->nr initialized) - * Context: can sleep - * - * See i2c_add_numbered_adapter() for details. - */ -static int __i2c_add_numbered_adapter(struct i2c_adapter *adap) -{ - int id; - - mutex_lock(&core_lock); - id = idr_alloc(&i2c_adapter_idr, adap, adap->nr, adap->nr + 1, GFP_KERNEL); - mutex_unlock(&core_lock); - if (WARN(id < 0, "couldn't get idr")) - return id == -ENOSPC ? -EBUSY : id; +err_remove_irq_domain: + i2c_host_notify_irq_teardown(adap); - return i2c_register_adapter(adap); + return res; } /** @@ -1655,17 +1682,8 @@ int i2c_add_adapter(struct i2c_adapter *adapter) int id; id = of_alias_get_id(dev->of_node, "i2c"); - if (id >= 0) { - adapter->nr = id; - return __i2c_add_numbered_adapter(adapter); - } - - mutex_lock(&core_lock); - id = idr_alloc(&i2c_adapter_idr, adapter, - __i2c_first_dynamic_bus_num, 0, GFP_KERNEL); - mutex_unlock(&core_lock); - if (WARN(id < 0, "couldn't get idr")) - return id; + if (id < 0) + id = -1; adapter->nr = id; @@ -1701,7 +1719,7 @@ int i2c_add_numbered_adapter(struct i2c_adapter *adap) if (adap->nr == -1) /* -1 means dynamically assign bus id */ return i2c_add_adapter(adap); - return __i2c_add_numbered_adapter(adap); + return i2c_register_adapter(adap); } EXPORT_SYMBOL_GPL(i2c_add_numbered_adapter); @@ -1743,29 +1761,10 @@ static int __process_removed_adapter(struct device_driver *d, void *data) return 0; } -/** - * i2c_del_adapter - unregister I2C adapter - * @adap: the adapter being unregistered - * Context: can sleep - * - * This unregisters an I2C adapter which was previously registered - * by @i2c_add_adapter or @i2c_add_numbered_adapter. - */ -void i2c_del_adapter(struct i2c_adapter *adap) +static void i2c_deregister_clients(struct i2c_adapter *adap) { - struct i2c_adapter *found; struct i2c_client *client, *next; - /* First make sure that this adapter was ever added */ - mutex_lock(&core_lock); - found = idr_find(&i2c_adapter_idr, adap->nr); - mutex_unlock(&core_lock); - if (found != adap) { - pr_debug("attempting to delete unregistered adapter [%s]\n", adap->name); - return; - } - - i2c_acpi_remove_space_handler(adap); /* Tell drivers about this removal */ mutex_lock(&core_lock); bus_for_each_drv(&i2c_bus_type, NULL, adap, @@ -1791,6 +1790,34 @@ void i2c_del_adapter(struct i2c_adapter *adap) * them up properly, so we give them a chance to do that first. */ device_for_each_child(&adap->dev, NULL, __unregister_client); device_for_each_child(&adap->dev, NULL, __unregister_dummy); +} + +/** + * i2c_del_adapter - unregister I2C adapter + * @adap: the adapter being unregistered + * Context: can sleep + * + * This unregisters an I2C adapter which was previously registered + * by @i2c_add_adapter or @i2c_add_numbered_adapter. + */ +void i2c_del_adapter(struct i2c_adapter *adap) +{ + struct i2c_adapter *found; + + /* First make sure that this adapter was ever added */ + mutex_lock(&core_lock); + found = idr_find(&i2c_adapter_idr, adap->nr); + if (found == adap) + idr_replace(&i2c_adapter_idr, NULL, adap->nr); + mutex_unlock(&core_lock); + if (found != adap) { + pr_debug("attempting to delete unregistered adapter [%s]\n", adap->name); + return; + } + + i2c_acpi_remove_space_handler(adap); + + i2c_deregister_clients(adap); /* device name is gone after device_unregister */ dev_dbg(&adap->dev, "adapter [%s] unregistered\n", adap->name); diff --git a/drivers/i2c/i2c-slave-eeprom.c b/drivers/i2c/i2c-slave-eeprom.c index 6bc2ef650a74..226d722af662 100644 --- a/drivers/i2c/i2c-slave-eeprom.c +++ b/drivers/i2c/i2c-slave-eeprom.c @@ -191,14 +191,14 @@ static void i2c_slave_eeprom_remove(struct i2c_client *client) } static const struct i2c_device_id i2c_slave_eeprom_id[] = { - { "slave-24c02", I2C_SLAVE_DEVICE_MAGIC(2048 / 8, 0) }, - { "slave-24c02ro", I2C_SLAVE_DEVICE_MAGIC(2048 / 8, I2C_SLAVE_FLAG_RO) }, - { "slave-24c32", I2C_SLAVE_DEVICE_MAGIC(32768 / 8, I2C_SLAVE_FLAG_ADDR16) }, - { "slave-24c32ro", I2C_SLAVE_DEVICE_MAGIC(32768 / 8, I2C_SLAVE_FLAG_ADDR16 | I2C_SLAVE_FLAG_RO) }, - { "slave-24c64", I2C_SLAVE_DEVICE_MAGIC(65536 / 8, I2C_SLAVE_FLAG_ADDR16) }, - { "slave-24c64ro", I2C_SLAVE_DEVICE_MAGIC(65536 / 8, I2C_SLAVE_FLAG_ADDR16 | I2C_SLAVE_FLAG_RO) }, - { "slave-24c512", I2C_SLAVE_DEVICE_MAGIC(524288 / 8, I2C_SLAVE_FLAG_ADDR16) }, - { "slave-24c512ro", I2C_SLAVE_DEVICE_MAGIC(524288 / 8, I2C_SLAVE_FLAG_ADDR16 | I2C_SLAVE_FLAG_RO) }, + { .name = "slave-24c02", .driver_data = I2C_SLAVE_DEVICE_MAGIC(2048 / 8, 0) }, + { .name = "slave-24c02ro", .driver_data = I2C_SLAVE_DEVICE_MAGIC(2048 / 8, I2C_SLAVE_FLAG_RO) }, + { .name = "slave-24c32", .driver_data = I2C_SLAVE_DEVICE_MAGIC(32768 / 8, I2C_SLAVE_FLAG_ADDR16) }, + { .name = "slave-24c32ro", .driver_data = I2C_SLAVE_DEVICE_MAGIC(32768 / 8, I2C_SLAVE_FLAG_ADDR16 | I2C_SLAVE_FLAG_RO) }, + { .name = "slave-24c64", .driver_data = I2C_SLAVE_DEVICE_MAGIC(65536 / 8, I2C_SLAVE_FLAG_ADDR16) }, + { .name = "slave-24c64ro", .driver_data = I2C_SLAVE_DEVICE_MAGIC(65536 / 8, I2C_SLAVE_FLAG_ADDR16 | I2C_SLAVE_FLAG_RO) }, + { .name = "slave-24c512", .driver_data = I2C_SLAVE_DEVICE_MAGIC(524288 / 8, I2C_SLAVE_FLAG_ADDR16) }, + { .name = "slave-24c512ro", .driver_data = I2C_SLAVE_DEVICE_MAGIC(524288 / 8, I2C_SLAVE_FLAG_ADDR16 | I2C_SLAVE_FLAG_RO) }, { } }; MODULE_DEVICE_TABLE(i2c, i2c_slave_eeprom_id); diff --git a/drivers/i2c/i2c-slave-testunit.c b/drivers/i2c/i2c-slave-testunit.c index 871c58461ebc..4a1bf1aa504b 100644 --- a/drivers/i2c/i2c-slave-testunit.c +++ b/drivers/i2c/i2c-slave-testunit.c @@ -270,7 +270,7 @@ static void i2c_slave_testunit_remove(struct i2c_client *client) } static const struct i2c_device_id i2c_slave_testunit_id[] = { - { "slave-testunit" }, + { .name = "slave-testunit" }, { } }; MODULE_DEVICE_TABLE(i2c, i2c_slave_testunit_id); diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c index bc7bd55e6370..069f08f68bc1 100644 --- a/drivers/i2c/i2c-smbus.c +++ b/drivers/i2c/i2c-smbus.c @@ -220,7 +220,7 @@ static void smbalert_remove(struct i2c_client *ara) } static const struct i2c_device_id smbalert_ids[] = { - { "smbus_alert" }, + { .name = "smbus_alert" }, { /* LIST END */ } }; MODULE_DEVICE_TABLE(i2c, smbalert_ids); diff --git a/drivers/i2c/muxes/i2c-mux-ltc4306.c b/drivers/i2c/muxes/i2c-mux-ltc4306.c index 50fbc0d06e62..18228f5b80e8 100644 --- a/drivers/i2c/muxes/i2c-mux-ltc4306.c +++ b/drivers/i2c/muxes/i2c-mux-ltc4306.c @@ -191,8 +191,8 @@ static int ltc4306_deselect_mux(struct i2c_mux_core *muxc, u32 chan) } static const struct i2c_device_id ltc4306_id[] = { - { "ltc4305", ltc_4305 }, - { "ltc4306", ltc_4306 }, + { .name = "ltc4305", .driver_data = ltc_4305 }, + { .name = "ltc4306", .driver_data = ltc_4306 }, { } }; MODULE_DEVICE_TABLE(i2c, ltc4306_id); diff --git a/drivers/i2c/muxes/i2c-mux-pca9541.c b/drivers/i2c/muxes/i2c-mux-pca9541.c index 3d8002caf703..9a59129bc50f 100644 --- a/drivers/i2c/muxes/i2c-mux-pca9541.c +++ b/drivers/i2c/muxes/i2c-mux-pca9541.c @@ -74,8 +74,8 @@ struct pca9541 { }; static const struct i2c_device_id pca9541_id[] = { - { "pca9541" }, - {} + { .name = "pca9541" }, + { } }; MODULE_DEVICE_TABLE(i2c, pca9541_id); diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c index b9f370c9f018..8fca709ed279 100644 --- a/drivers/i2c/muxes/i2c-mux-pca954x.c +++ b/drivers/i2c/muxes/i2c-mux-pca954x.c @@ -250,24 +250,24 @@ static const struct chip_desc chips[] = { }; static const struct i2c_device_id pca954x_id[] = { - { "max7356", max_7356 }, - { "max7357", max_7357 }, - { "max7358", max_7358 }, - { "max7367", max_7367 }, - { "max7368", max_7368 }, - { "max7369", max_7369 }, - { "pca9540", pca_9540 }, - { "pca9542", pca_9542 }, - { "pca9543", pca_9543 }, - { "pca9544", pca_9544 }, - { "pca9545", pca_9545 }, - { "pca9546", pca_9546 }, - { "pca9547", pca_9547 }, - { "pca9548", pca_9548 }, - { "pca9846", pca_9846 }, - { "pca9847", pca_9847 }, - { "pca9848", pca_9848 }, - { "pca9849", pca_9849 }, + { .name = "max7356", .driver_data = max_7356 }, + { .name = "max7357", .driver_data = max_7357 }, + { .name = "max7358", .driver_data = max_7358 }, + { .name = "max7367", .driver_data = max_7367 }, + { .name = "max7368", .driver_data = max_7368 }, + { .name = "max7369", .driver_data = max_7369 }, + { .name = "pca9540", .driver_data = pca_9540 }, + { .name = "pca9542", .driver_data = pca_9542 }, + { .name = "pca9543", .driver_data = pca_9543 }, + { .name = "pca9544", .driver_data = pca_9544 }, + { .name = "pca9545", .driver_data = pca_9545 }, + { .name = "pca9546", .driver_data = pca_9546 }, + { .name = "pca9547", .driver_data = pca_9547 }, + { .name = "pca9548", .driver_data = pca_9548 }, + { .name = "pca9846", .driver_data = pca_9846 }, + { .name = "pca9847", .driver_data = pca_9847 }, + { .name = "pca9848", .driver_data = pca_9848 }, + { .name = "pca9849", .driver_data = pca_9849 }, { } }; MODULE_DEVICE_TABLE(i2c, pca954x_id); diff --git a/drivers/i2c/muxes/i2c-mux-reg.c b/drivers/i2c/muxes/i2c-mux-reg.c index 1e566ea92bc9..13da757100fe 100644 --- a/drivers/i2c/muxes/i2c-mux-reg.c +++ b/drivers/i2c/muxes/i2c-mux-reg.c @@ -11,7 +11,6 @@ #include <linux/init.h> #include <linux/io.h> #include <linux/module.h> -#include <linux/of_address.h> #include <linux/platform_data/i2c-mux-reg.h> #include <linux/platform_device.h> #include <linux/slab.h> @@ -75,37 +74,34 @@ static int i2c_mux_reg_deselect(struct i2c_mux_core *muxc, u32 chan) return 0; } -#ifdef CONFIG_OF -static int i2c_mux_reg_probe_dt(struct regmux *mux, - struct platform_device *pdev) +static int i2c_mux_reg_probe_fw(struct regmux *mux, struct device *dev) { - struct device_node *np = pdev->dev.of_node; - struct device_node *adapter_np, *child; + struct fwnode_handle *fwnode, *child; struct i2c_adapter *adapter; - struct resource res; unsigned *values; - int i = 0; + int ret, i = 0; - if (!np) + if (!dev_fwnode(dev)) return -ENODEV; - adapter_np = of_parse_phandle(np, "i2c-parent", 0); - if (!adapter_np) { - dev_err(&pdev->dev, "Cannot parse i2c-parent\n"); + fwnode = fwnode_find_reference(dev_fwnode(dev), "i2c-parent", 0); + if (IS_ERR(fwnode)) { + dev_err(dev, "missing 'i2c-parent' property\n"); return -ENODEV; } - adapter = of_find_i2c_adapter_by_node(adapter_np); - of_node_put(adapter_np); + + adapter = i2c_find_adapter_by_fwnode(fwnode); + fwnode_handle_put(fwnode); if (!adapter) return -EPROBE_DEFER; mux->data.parent = i2c_adapter_id(adapter); put_device(&adapter->dev); - mux->data.n_values = of_get_child_count(np); - if (of_property_read_bool(np, "little-endian")) { + mux->data.n_values = device_get_child_node_count(dev); + if (device_property_read_bool(dev, "little-endian")) { mux->data.little_endian = true; - } else if (of_property_read_bool(np, "big-endian")) { + } else if (device_property_read_bool(dev, "big-endian")) { mux->data.little_endian = false; } else { #if defined(__BYTE_ORDER) ? __BYTE_ORDER == __LITTLE_ENDIAN : \ @@ -118,40 +114,35 @@ static int i2c_mux_reg_probe_dt(struct regmux *mux, #error Endianness not defined? #endif } - mux->data.write_only = of_property_read_bool(np, "write-only"); + mux->data.write_only = device_property_read_bool(dev, "write-only"); - values = devm_kcalloc(&pdev->dev, - mux->data.n_values, sizeof(*mux->data.values), + values = devm_kcalloc(dev, mux->data.n_values, sizeof(*mux->data.values), GFP_KERNEL); if (!values) return -ENOMEM; - for_each_child_of_node(np, child) { - of_property_read_u32(child, "reg", values + i); + device_for_each_child_node(dev, child) { + if (is_acpi_device_node(child)) { + ret = acpi_get_local_address(ACPI_HANDLE_FWNODE(child), + &values[i]); + if (ret) { + fwnode_handle_put(child); + return dev_err_probe(dev, ret, + "Cannot get address\n"); + } + } else { + fwnode_property_read_u32(child, "reg", &values[i]); + } + i++; } mux->data.values = values; - if (!of_property_read_u32(np, "idle-state", &mux->data.idle)) + if (!device_property_read_u32(dev, "idle-state", &mux->data.idle)) mux->data.idle_in_use = true; - /* map address from "reg" if exists */ - if (of_address_to_resource(np, 0, &res) == 0) { - mux->data.reg_size = resource_size(&res); - mux->data.reg = devm_ioremap_resource(&pdev->dev, &res); - if (IS_ERR(mux->data.reg)) - return PTR_ERR(mux->data.reg); - } - return 0; } -#else -static int i2c_mux_reg_probe_dt(struct regmux *mux, - struct platform_device *pdev) -{ - return 0; -} -#endif static int i2c_mux_reg_probe(struct platform_device *pdev) { @@ -169,34 +160,29 @@ static int i2c_mux_reg_probe(struct platform_device *pdev) memcpy(&mux->data, dev_get_platdata(&pdev->dev), sizeof(mux->data)); } else { - ret = i2c_mux_reg_probe_dt(mux, pdev); + ret = i2c_mux_reg_probe_fw(mux, &pdev->dev); if (ret < 0) return dev_err_probe(&pdev->dev, ret, - "Error parsing device tree"); + "Error parsing firmware description\n"); } - parent = i2c_get_adapter(mux->data.parent); - if (!parent) - return -EPROBE_DEFER; - if (!mux->data.reg) { - dev_info(&pdev->dev, - "Register not set, using platform resource\n"); mux->data.reg = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(mux->data.reg)) { - ret = PTR_ERR(mux->data.reg); - goto err_put_parent; - } + if (IS_ERR(mux->data.reg)) + return PTR_ERR(mux->data.reg); mux->data.reg_size = resource_size(res); } if (mux->data.reg_size != 4 && mux->data.reg_size != 2 && mux->data.reg_size != 1) { dev_err(&pdev->dev, "Invalid register size\n"); - ret = -EINVAL; - goto err_put_parent; + return -EINVAL; } + parent = i2c_get_adapter(mux->data.parent); + if (!parent) + return -EPROBE_DEFER; + muxc = i2c_mux_alloc(parent, &pdev->dev, mux->data.n_values, 0, 0, i2c_mux_reg_select, NULL); if (!muxc) { diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 0200288d3a7a..5d5f357a1996 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -215,37 +215,37 @@ AT24_CHIP_DATA(at24_data_24c2048, 2097152 / 8, AT24_FLAG_ADDR16); AT24_CHIP_DATA(at24_data_INT3499, 8192 / 8, 0); static const struct i2c_device_id at24_ids[] = { - { "24c00", (kernel_ulong_t)&at24_data_24c00 }, - { "24c01", (kernel_ulong_t)&at24_data_24c01 }, - { "24cs01", (kernel_ulong_t)&at24_data_24cs01 }, - { "24c02", (kernel_ulong_t)&at24_data_24c02 }, - { "24cs02", (kernel_ulong_t)&at24_data_24cs02 }, - { "24mac402", (kernel_ulong_t)&at24_data_24mac402 }, - { "24mac602", (kernel_ulong_t)&at24_data_24mac602 }, - { "24aa025e48", (kernel_ulong_t)&at24_data_24aa025e48 }, - { "24aa025e64", (kernel_ulong_t)&at24_data_24aa025e64 }, - { "spd", (kernel_ulong_t)&at24_data_spd }, - { "24c02-vaio", (kernel_ulong_t)&at24_data_24c02_vaio }, - { "24c04", (kernel_ulong_t)&at24_data_24c04 }, - { "24cs04", (kernel_ulong_t)&at24_data_24cs04 }, - { "24c08", (kernel_ulong_t)&at24_data_24c08 }, - { "24cs08", (kernel_ulong_t)&at24_data_24cs08 }, - { "24c16", (kernel_ulong_t)&at24_data_24c16 }, - { "24cs16", (kernel_ulong_t)&at24_data_24cs16 }, - { "24c32", (kernel_ulong_t)&at24_data_24c32 }, - { "24c32d-wl", (kernel_ulong_t)&at24_data_24c32d_wlp }, - { "24cs32", (kernel_ulong_t)&at24_data_24cs32 }, - { "24c64", (kernel_ulong_t)&at24_data_24c64 }, - { "24c64-wl", (kernel_ulong_t)&at24_data_24c64d_wlp }, - { "24cs64", (kernel_ulong_t)&at24_data_24cs64 }, - { "24c128", (kernel_ulong_t)&at24_data_24c128 }, - { "24c256", (kernel_ulong_t)&at24_data_24c256 }, - { "24256e-wl", (kernel_ulong_t)&at24_data_24256e_wlp }, - { "24c512", (kernel_ulong_t)&at24_data_24c512 }, - { "24c1024", (kernel_ulong_t)&at24_data_24c1024 }, - { "24c1025", (kernel_ulong_t)&at24_data_24c1025 }, - { "24c2048", (kernel_ulong_t)&at24_data_24c2048 }, - { "at24", 0 }, + { .name = "24c00", .driver_data = (kernel_ulong_t)&at24_data_24c00 }, + { .name = "24c01", .driver_data = (kernel_ulong_t)&at24_data_24c01 }, + { .name = "24cs01", .driver_data = (kernel_ulong_t)&at24_data_24cs01 }, + { .name = "24c02", .driver_data = (kernel_ulong_t)&at24_data_24c02 }, + { .name = "24cs02", .driver_data = (kernel_ulong_t)&at24_data_24cs02 }, + { .name = "24mac402", .driver_data = (kernel_ulong_t)&at24_data_24mac402 }, + { .name = "24mac602", .driver_data = (kernel_ulong_t)&at24_data_24mac602 }, + { .name = "24aa025e48", .driver_data = (kernel_ulong_t)&at24_data_24aa025e48 }, + { .name = "24aa025e64", .driver_data = (kernel_ulong_t)&at24_data_24aa025e64 }, + { .name = "spd", .driver_data = (kernel_ulong_t)&at24_data_spd }, + { .name = "24c02-vaio", .driver_data = (kernel_ulong_t)&at24_data_24c02_vaio }, + { .name = "24c04", .driver_data = (kernel_ulong_t)&at24_data_24c04 }, + { .name = "24cs04", .driver_data = (kernel_ulong_t)&at24_data_24cs04 }, + { .name = "24c08", .driver_data = (kernel_ulong_t)&at24_data_24c08 }, + { .name = "24cs08", .driver_data = (kernel_ulong_t)&at24_data_24cs08 }, + { .name = "24c16", .driver_data = (kernel_ulong_t)&at24_data_24c16 }, + { .name = "24cs16", .driver_data = (kernel_ulong_t)&at24_data_24cs16 }, + { .name = "24c32", .driver_data = (kernel_ulong_t)&at24_data_24c32 }, + { .name = "24c32d-wl", .driver_data = (kernel_ulong_t)&at24_data_24c32d_wlp }, + { .name = "24cs32", .driver_data = (kernel_ulong_t)&at24_data_24cs32 }, + { .name = "24c64", .driver_data = (kernel_ulong_t)&at24_data_24c64 }, + { .name = "24c64-wl", .driver_data = (kernel_ulong_t)&at24_data_24c64d_wlp }, + { .name = "24cs64", .driver_data = (kernel_ulong_t)&at24_data_24cs64 }, + { .name = "24c128", .driver_data = (kernel_ulong_t)&at24_data_24c128 }, + { .name = "24c256", .driver_data = (kernel_ulong_t)&at24_data_24c256 }, + { .name = "24256e-wl", .driver_data = (kernel_ulong_t)&at24_data_24256e_wlp }, + { .name = "24c512", .driver_data = (kernel_ulong_t)&at24_data_24c512 }, + { .name = "24c1024", .driver_data = (kernel_ulong_t)&at24_data_24c1024 }, + { .name = "24c1025", .driver_data = (kernel_ulong_t)&at24_data_24c1025 }, + { .name = "24c2048", .driver_data = (kernel_ulong_t)&at24_data_24c2048 }, + { .name = "at24", .driver_data = 0 }, { /* END OF LIST */ } }; MODULE_DEVICE_TABLE(i2c, at24_ids); |
