i2c for v7.4, part 1

A mix of new hardware support, driver improvements and smaller
 core, DT binding and documentation cleanups.
 
 Core and helpers:
 
 - acpi: run ASUS and Dell touchpads at 100 kHz to avoid lag
 - core: return bool from check_functionality() and update users
 - smbus: limit block reads to the caller's buffer size
 - acpi: mark I2C transfer buffers as DMA-safe
 
 Drivers:
 
 - qcom-geni: several firmware node and runtime PM improvements
 - qcom-cci: several patches rework clock selection and timings
 - qcom-cci: enable SCL clock stretching
 - imx-lpi2c: fix DMA mapping device selection and cleanup
 - designware: tune RX FIFO thresholds to reduce interrupts
 - i801: respect SPD write protection when probing DDR5 modules
 - bcm2835: fully initialize clock configuration data
 
 Generic cleanups:
 
 - mux: simplify channel lookup and firmware node handling
 - core, busses: tidy up ACPI device ID tables
 - docs: clarify adapter lookup and fix a client driver example
 - xlnx: update maintainer for Xilinx IIC bindings
 - ljca: remove redundant adapter deletion
 
 New support and DT bindings:
 
 - add DW compatibles for Synaptics SL2610 and UltraRISC DP1000
 - allow six DMA channels for R-Car Gen5
 - add Allwinner A733 compatible to mv64xxx bindings
 - add support for Novatek NT72676 (new driver)
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQScDfrjQa34uOld1VLaeAVmJtMtbgUCatfuigAKCRDaeAVmJtMt
 bu2DAQDvyVRaTGxk7ijnmwHNXXhr9BHKtNxr1ssBpEl8qwoVxgEA2+Si+fpQeovk
 zim0KJGxCoPVJuvfbNHojSaOviTdpQw=
 =7W7U
 -----END PGP SIGNATURE-----

Merge tag 'i2c-7.4-part1' of git://git.kernel.org/pub/scm/linux/kernel/git/andi.shyti/linux

Pull i2c updates from Andi Shyti:
 "The main changes are the new Novatek NT72676 driver, the qcom-cci
  clock and timing updates and a collection of smaller driver, core and
  DT binding improvements.

  Core and helpers:
   - acpi: run ASUS and Dell touchpads at 100 kHz to avoid lag
   - core: return bool from check_functionality() and update users
   - smbus: limit block reads to the caller's buffer size
   - acpi: mark I2C transfer buffers as DMA-safe

  Drivers:
   - qcom-geni: several firmware node and runtime PM improvements
   - qcom-cci: several patches rework clock selection and timings
   - qcom-cci: enable SCL clock stretching
   - imx-lpi2c: fix DMA mapping device selection and cleanup
   - designware: tune RX FIFO thresholds to reduce interrupts
   - i801: respect SPD write protection when probing DDR5 modules
   - bcm2835: fully initialize clock configuration data

  Generic cleanups:
   - mux: simplify channel lookup and firmware node handling
   - core, busses: tidy up ACPI device ID tables
   - docs: clarify adapter lookup and fix a client driver example
   - xlnx: update maintainer for Xilinx IIC bindings
   - ljca: remove redundant adapter deletion

  New support and DT bindings:
   - add DW compatibles for Synaptics SL2610 and UltraRISC DP1000
   - allow six DMA channels for R-Car Gen5
   - add Allwinner A733 compatible to mv64xxx bindings
   - add support for Novatek NT72676 (new driver)"

* tag 'i2c-7.4-part1' of git://git.kernel.org/pub/scm/linux/kernel/git/andi.shyti/linux: (37 commits)
  i2c: imx-lpi2c: remove dmaengine_desc_free()
  i2c: busses: use named initializers for acpi_device_id
  i2c: i2c-core-acpi: use named initializers for acpi_device_id
  i2c: i801: Gate SPD instantiation on SPD Write Disable bit
  i2c: acpi: Use I2C_M_DMA_SAFE in acpi_gsb_i2c_[read|write]_bytes()
  dt-bindings: i2c: dw: Add Synaptics sl2610 i2c
  i2c: designware: size the RX FIFO threshold to the queued transfer
  i2c: imx-lpi2c: use dmaengine_get_dma_device() for DMA mapping
  docs: i2c: writing-clients: Add missing end of command
  i2c: qcom-cci: skip core clock rates the clock cannot provide
  i2c: core: document preference for i2c_get_adapter_by_fwnode()
  i2c: qcom-cci: always enable SCL clock stretching
  MAINTAINERS: Add entry for Novatek NT72676 I2C controller driver
  i2c: nt72676: Add Novatek NT72676 I2C controller driver
  dt-bindings: i2c: Add Novatek NT72676 I2C controller
  i2c: acpi: Force ASUE140D touchpads to 100 kHz
  i2c: qcom-cci: Enforce the required CCI clock rate
  i2c: qcom-cci: Share the timing table across CCI revisions
  i2c: qcom-cci: Add 19.2 MHz timings for the v2 CCI
  i2c: qcom-cci: Support per-mode CCI clock rates
  ...
This commit is contained in:
Linus Torvalds 2026-10-22 07:36:22 -10:00
commit bb436f2a85
40 changed files with 1241 additions and 311 deletions

View File

@ -34,6 +34,7 @@ properties:
- allwinner,sun50i-h616-i2c
- allwinner,sun50i-r329-i2c
- allwinner,sun55i-a523-i2c
- allwinner,sun60i-a733-i2c
- const: allwinner,sun8i-v536-i2c
- const: allwinner,sun6i-a31-i2c
- const: marvell,mv64xxx-i2c

View File

@ -0,0 +1,76 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/i2c/novatek,nt72676-i2c.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Novatek NT72676 I2C Controller
maintainers:
- Ben Huang <ben_huang@novatek.com.tw>
- Jason JJ Wu <jason_jj_wu@novatek.com.tw>
description: >
Novatek NT72676 SoC provides multiple I2C controller instances that share
the same register layout and transfer programming model.
Generic I2C controllers are directly accessible by the host CPU and driven
by a 96 MHz AHB clock.
STBC I2C controllers are driven by a 12 MHz clock from the STBC (Standby
Controller, an 8051-based microcontroller). Before accessing an STBC I2C
controller, the host CPU must authenticate with the STBC and switch
ownership of the controller to the host CPU. The presence of the
novatek,stbc-syscon property identifies an STBC I2C controller requiring
this initialization sequence.
properties:
compatible:
const: novatek,nt72676-i2c
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
maxItems: 1
clock-frequency:
default: 100000
enum: [ 100000, 400000 ]
novatek,stbc-syscon:
$ref: /schemas/types.yaml#/definitions/phandle
description: >
Phandle to the STBC system controller used to authenticate host
access and switch ownership of the STBC I2C controller to the host
CPU during initialization.
required:
- compatible
- reg
- interrupts
- clocks
allOf:
- $ref: /schemas/i2c/i2c-controller.yaml#
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c@fc040a00 {
compatible = "novatek,nt72676-i2c";
reg = <0xfc040a00 0x40>;
interrupts = <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&stbc_i2c_clock>;
clock-frequency = <400000>;
novatek,stbc-syscon = <&stbc>;
#address-cells = <1>;
#size-cells = <0>;
};

View File

@ -83,14 +83,14 @@ properties:
dmas:
minItems: 2
maxItems: 4
maxItems: 6
description:
Must contain a list of pairs of references to DMA specifiers, one for
transmission, and one for reception.
dma-names:
minItems: 2
maxItems: 4
maxItems: 6
items:
enum:
- tx

View File

@ -21,6 +21,20 @@ allOf:
properties:
reg:
maxItems: 1
- if:
properties:
compatible:
contains:
const: ultrarisc,dp1000-i2c
then:
properties:
clocks:
minItems: 2
clock-names:
minItems: 2
required:
- clocks
- clock-names
properties:
compatible:
@ -43,7 +57,9 @@ properties:
- mobileye,eyeq6lplus-i2c
- mscc,ocelot-i2c
- sophgo,sg2044-i2c
- syna,sl2610-i2c
- thead,th1520-i2c
- ultrarisc,dp1000-i2c
- const: snps,designware-i2c
reg:

View File

@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Xilinx IIC controller
maintainers:
- info@mocean-labs.com
- Michal Simek <michal.simek@amd.com>
allOf:
- $ref: /schemas/i2c/i2c-controller.yaml#

View File

@ -258,7 +258,7 @@ Driver Information
::
/* Substitute your own name and email address */
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>"
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
MODULE_DESCRIPTION("Driver for Barf Inc. Foo I2C devices");
/* a few non-GPL license types are also allowed */

View File

@ -19522,6 +19522,13 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/nolibc/linux-nolibc.git
F: tools/include/nolibc/
F: tools/testing/selftests/nolibc/
NOVATEK NT72676 I2C CONTROLLER DRIVER
M: Ben Huang <ben_huang@novatek.com.tw>
L: linux-i2c@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/i2c/novatek,nt72676-i2c.yaml
F: drivers/i2c/busses/i2c-nt72676.c
NOVATEK NVT-TS I2C TOUCHSCREEN DRIVER
M: Hans de Goede <hansg@kernel.org>
L: linux-input@vger.kernel.org

View File

@ -962,6 +962,17 @@ config I2C_NPCM
controllers.
Driver can also support slave mode (select I2C_SLAVE).
config I2C_NT72676
tristate "Novatek NT72676 I2C controller"
depends on OF
depends on COMMON_CLK || COMPILE_TEST
help
Say Y here if you want to enable I2C bus controller on
Novatek NT72676 SoC.
This driver performs fundamental read/write functions
as an I2C bus controller and supports Standard-mode and
Fast-mode. Default operation is Standard-mode.
config I2C_OCORES
tristate "OpenCores I2C Controller"
help
@ -1050,6 +1061,7 @@ config I2C_PXA_SLAVE
config I2C_QCOM_CCI
tristate "Qualcomm Camera Control Interface"
depends on ARCH_QCOM || COMPILE_TEST
select PM_OPP
help
If you say yes to this option, support will be included for the
built-in camera control interface on the Qualcomm SoCs.

View File

@ -90,6 +90,7 @@ obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o
obj-$(CONFIG_I2C_MXS) += i2c-mxs.o
obj-$(CONFIG_I2C_NOMADIK) += i2c-nomadik.o
obj-$(CONFIG_I2C_NPCM) += i2c-npcm7xx.o
obj-$(CONFIG_I2C_NT72676) += i2c-nt72676.o
obj-$(CONFIG_I2C_OCORES) += i2c-ocores.o
obj-$(CONFIG_I2C_OMAP) += i2c-omap.o
obj-$(CONFIG_I2C_OWL) += i2c-owl.o

View File

@ -354,7 +354,7 @@ static int amd_asf_probe(struct platform_device *pdev)
}
static const struct acpi_device_id amd_asf_acpi_ids[] = {
{ "AMDI001A" },
{ .id = "AMDI001A" },
{ }
};
MODULE_DEVICE_TABLE(acpi, amd_asf_acpi_ids);

View File

@ -341,7 +341,7 @@ static void i2c_amd_remove(struct platform_device *pdev)
}
static const struct acpi_device_id i2c_amd_acpi_match[] = {
{ "AMDI0011" },
{ .id = "AMDI0011" },
{ }
};
MODULE_DEVICE_TABLE(acpi, i2c_amd_acpi_match);

View File

@ -166,7 +166,7 @@ static struct clk *bcm2835_i2c_register_div(struct device *dev,
struct clk *mclk,
struct bcm2835_i2c_dev *i2c_dev)
{
struct clk_init_data init;
struct clk_init_data init = {};
struct clk_bcm2835_i2c *priv;
char name[32];
const char *mclk_name;

View File

@ -300,7 +300,7 @@ static const struct of_device_id cros_ec_i2c_of_match[] __maybe_unused = {
MODULE_DEVICE_TABLE(of, cros_ec_i2c_of_match);
static const struct acpi_device_id cros_ec_i2c_tunnel_acpi_id[] __maybe_unused = {
{ "GOOG0012", 0 },
{ .id = "GOOG0012" },
{ }
};
MODULE_DEVICE_TABLE(acpi, cros_ec_i2c_tunnel_acpi_id);

View File

@ -378,7 +378,7 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
{
struct i2c_msg *msgs = dev->msgs;
u32 intr_mask;
int tx_limit, rx_limit;
int tx_limit, rx_limit, rx_tl;
u32 buf_len = dev->tx_buf_len;
u8 *buf = dev->tx_buf;
bool need_restart = false;
@ -484,6 +484,19 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
if (dev->msg_err)
intr_mask = 0;
/*
* Size the RX FIFO threshold to the reads already queued, so the
* controller raises one RX_FULL for the whole burst instead of one per
* received byte. While messages remain to be queued, cap it so RX_FULL
* still arrives in time to drain the FIFO and let the next TX_EMPTY
* queue the rest.
*/
rx_tl = dev->rx_outstanding;
if (dev->msg_write_idx < dev->msgs_num)
rx_tl = min_t(int, rx_tl, dev->rx_fifo_depth / 2);
regmap_write(dev->map, DW_IC_RX_TL, rx_tl ? rx_tl - 1 : 0);
__i2c_dw_write_intr_mask(dev, intr_mask);
}

View File

@ -260,27 +260,27 @@ static const struct of_device_id dw_i2c_of_match[] = {
MODULE_DEVICE_TABLE(of, dw_i2c_of_match);
static const struct acpi_device_id dw_i2c_acpi_match[] = {
{ "80860F41", ACCESS_NO_IRQ_SUSPEND },
{ "808622C1", ACCESS_NO_IRQ_SUSPEND },
{ "AMD0010", ACCESS_INTR_MASK },
{ "AMDI0010", ACCESS_INTR_MASK },
{ "AMDI0019", ACCESS_INTR_MASK | ARBITRATION_SEMAPHORE },
{ "AMDI0510", 0 },
{ "APMC0D0F", 0 },
{ "FUJI200B", 0 },
{ "GOOG5000", 0 },
{ "HISI02A1", 0 },
{ "HISI02A2", 0 },
{ "HISI02A3", 0 },
{ "HJMC3001", ACCESS_INTR_MASK },
{ "HYGO0010", ACCESS_INTR_MASK },
{ "INT33C2", 0 },
{ "INT33C3", 0 },
{ "INT3432", 0 },
{ "INT3433", 0 },
{ "INTC10EF", 0 },
{ "LECA0003", 0 },
{}
{ .id = "80860F41", .driver_data = ACCESS_NO_IRQ_SUSPEND },
{ .id = "808622C1", .driver_data = ACCESS_NO_IRQ_SUSPEND },
{ .id = "AMD0010", .driver_data = ACCESS_INTR_MASK },
{ .id = "AMDI0010", .driver_data = ACCESS_INTR_MASK },
{ .id = "AMDI0019", .driver_data = ACCESS_INTR_MASK | ARBITRATION_SEMAPHORE },
{ .id = "AMDI0510" },
{ .id = "APMC0D0F" },
{ .id = "FUJI200B" },
{ .id = "GOOG5000" },
{ .id = "HISI02A1" },
{ .id = "HISI02A2" },
{ .id = "HISI02A3" },
{ .id = "HJMC3001", .driver_data = ACCESS_INTR_MASK },
{ .id = "HYGO0010", .driver_data = ACCESS_INTR_MASK },
{ .id = "INT33C2" },
{ .id = "INT33C3" },
{ .id = "INT3432" },
{ .id = "INT3433" },
{ .id = "INTC10EF" },
{ .id = "LECA0003" },
{ }
};
MODULE_DEVICE_TABLE(acpi, dw_i2c_acpi_match);

View File

@ -664,7 +664,7 @@ static const struct of_device_id i2c_gpio_dt_ids[] = {
MODULE_DEVICE_TABLE(of, i2c_gpio_dt_ids);
static const struct acpi_device_id i2c_gpio_acpi_match[] = {
{ "LOON0005" }, /* LoongArch */
{ .id = "LOON0005" }, /* LoongArch */
{ }
};
MODULE_DEVICE_TABLE(acpi, i2c_gpio_acpi_match);

View File

@ -516,7 +516,7 @@ static int hisi_i2c_probe(struct platform_device *pdev)
}
static const struct acpi_device_id hisi_i2c_acpi_ids[] = {
{ "HISI03D1", 0 },
{ .id = "HISI03D1" },
{ }
};
MODULE_DEVICE_TABLE(acpi, hisi_i2c_acpi_ids);

View File

@ -1170,6 +1170,14 @@ static void dmi_check_onboard_devices(const struct dmi_header *dm, void *adap)
}
}
static void i801_register_spd(struct i801_priv *priv)
{
if (priv->original_hstcfg & SMBHSTCFG_SPD_WD)
i2c_register_spd_write_disable(&priv->adapter);
else
i2c_register_spd_write_enable(&priv->adapter);
}
/* Register optional targets */
static void i801_probe_optional_targets(struct i801_priv *priv)
{
@ -1193,7 +1201,7 @@ static void i801_probe_optional_targets(struct i801_priv *priv)
#ifdef CONFIG_I2C_I801_MUX
if (!priv->mux_pdev)
#endif
i2c_register_spd_write_enable(&priv->adapter);
i801_register_spd(priv);
}
#else
static void __init input_apanel_init(void) {}

View File

@ -780,7 +780,7 @@ static void lpi2c_dma_unmap(struct lpi2c_imx_dma *dma)
struct dma_chan *chan = dma->dma_data_dir == DMA_FROM_DEVICE
? dma->chan_rx : dma->chan_tx;
dma_unmap_single(chan->device->dev, dma->dma_addr,
dma_unmap_single(dmaengine_get_dma_device(chan), dma->dma_addr,
dma->dma_len, dma->dma_data_dir);
dma->dma_data_dir = DMA_NONE;
@ -789,7 +789,7 @@ static void lpi2c_dma_unmap(struct lpi2c_imx_dma *dma)
static void lpi2c_cleanup_rx_cmd_dma(struct lpi2c_imx_dma *dma)
{
dmaengine_terminate_sync(dma->chan_tx);
dma_unmap_single(dma->chan_tx->device->dev, dma->dma_tx_addr,
dma_unmap_single(dmaengine_get_dma_device(dma->chan_tx), dma->dma_tx_addr,
dma->rx_cmd_buf_len, DMA_TO_DEVICE);
}
@ -817,10 +817,10 @@ static int lpi2c_dma_rx_cmd_submit(struct lpi2c_imx_struct *lpi2c_imx)
struct dma_chan *txchan = dma->chan_tx;
dma_cookie_t cookie;
dma->dma_tx_addr = dma_map_single(txchan->device->dev,
dma->dma_tx_addr = dma_map_single(dmaengine_get_dma_device(txchan),
dma->rx_cmd_buf, dma->rx_cmd_buf_len,
DMA_TO_DEVICE);
if (dma_mapping_error(txchan->device->dev, dma->dma_tx_addr)) {
if (dma_mapping_error(dmaengine_get_dma_device(txchan), dma->dma_tx_addr)) {
dev_err(&lpi2c_imx->adapter.dev, "DMA map failed, use pio\n");
return -EINVAL;
}
@ -844,14 +844,13 @@ static int lpi2c_dma_rx_cmd_submit(struct lpi2c_imx_struct *lpi2c_imx)
return 0;
desc_prepare_err_exit:
dma_unmap_single(txchan->device->dev, dma->dma_tx_addr,
dma_unmap_single(dmaengine_get_dma_device(txchan), dma->dma_tx_addr,
dma->rx_cmd_buf_len, DMA_TO_DEVICE);
return -EINVAL;
submit_err_exit:
dma_unmap_single(txchan->device->dev, dma->dma_tx_addr,
dma_unmap_single(dmaengine_get_dma_device(txchan), dma->dma_tx_addr,
dma->rx_cmd_buf_len, DMA_TO_DEVICE);
dmaengine_desc_free(rx_cmd_desc);
return -EINVAL;
}
@ -872,9 +871,9 @@ static int lpi2c_dma_submit(struct lpi2c_imx_struct *lpi2c_imx)
dma->dma_transfer_dir = DMA_MEM_TO_DEV;
}
dma->dma_addr = dma_map_single(chan->device->dev,
dma->dma_addr = dma_map_single(dmaengine_get_dma_device(chan),
dma->dma_buf, dma->dma_len, dma->dma_data_dir);
if (dma_mapping_error(chan->device->dev, dma->dma_addr)) {
if (dma_mapping_error(dmaengine_get_dma_device(chan), dma->dma_addr)) {
dev_err(&lpi2c_imx->adapter.dev, "DMA map failed, use pio\n");
return -EINVAL;
}
@ -910,7 +909,6 @@ static int lpi2c_dma_submit(struct lpi2c_imx_struct *lpi2c_imx)
submit_err_exit:
lpi2c_dma_unmap(dma);
dmaengine_desc_free(desc);
return -EINVAL;
}
@ -1085,7 +1083,7 @@ static int lpi2c_imx_dma_xfer(struct lpi2c_imx_struct *lpi2c_imx,
}
if (dma->dma_msg_flag & I2C_M_RD)
dma_unmap_single(dma->chan_tx->device->dev, dma->dma_tx_addr,
dma_unmap_single(dmaengine_get_dma_device(dma->chan_tx), dma->dma_tx_addr,
dma->rx_cmd_buf_len, DMA_TO_DEVICE);
lpi2c_dma_unmap(dma);

View File

@ -349,7 +349,7 @@ static const struct of_device_id i2c_imx_dt_ids[] = {
MODULE_DEVICE_TABLE(of, i2c_imx_dt_ids);
static const struct acpi_device_id i2c_imx_acpi_ids[] = {
{"NXP0001", .driver_data = (kernel_ulong_t)&vf610_i2c_hwdata},
{ .id = "NXP0001", .driver_data = (kernel_ulong_t)&vf610_i2c_hwdata },
{ }
};
MODULE_DEVICE_TABLE(acpi, i2c_imx_acpi_ids);

View File

@ -297,7 +297,6 @@ static int ljca_i2c_probe(struct auxiliary_device *auxdev,
device_set_node(&ljca_i2c->adap.dev, dev_fwnode(&auxdev->dev));
i2c_set_adapdata(&ljca_i2c->adap, ljca_i2c);
auxiliary_set_drvdata(auxdev, ljca_i2c);
ret = ljca_i2c_init(ljca_i2c, ljca_i2c->i2c_info->id);
if (ret)
@ -315,13 +314,6 @@ static int ljca_i2c_probe(struct auxiliary_device *auxdev,
return 0;
}
static void ljca_i2c_remove(struct auxiliary_device *auxdev)
{
struct ljca_i2c_dev *ljca_i2c = auxiliary_get_drvdata(auxdev);
i2c_del_adapter(&ljca_i2c->adap);
}
static const struct auxiliary_device_id ljca_i2c_id_table[] = {
{ "usb_ljca.ljca-i2c", 0 },
{ /* sentinel */ }
@ -330,7 +322,6 @@ MODULE_DEVICE_TABLE(auxiliary, ljca_i2c_id_table);
static struct auxiliary_driver ljca_i2c_driver = {
.probe = ljca_i2c_probe,
.remove = ljca_i2c_remove,
.id_table = ljca_i2c_id_table,
};
module_auxiliary_driver(ljca_i2c_driver);

View File

@ -356,7 +356,7 @@ static const struct of_device_id ls2x_i2c_id_table[] = {
MODULE_DEVICE_TABLE(of, ls2x_i2c_id_table);
static const struct acpi_device_id ls2x_i2c_acpi_match[] = {
{ "LOON0004" }, /* Loongson LS7A */
{ .id = "LOON0004" }, /* Loongson LS7A */
{ }
};
MODULE_DEVICE_TABLE(acpi, ls2x_i2c_acpi_match);

View File

@ -2198,10 +2198,10 @@ static struct i2c_adapter_quirks mlxbf_i2c_quirks = {
};
static const struct acpi_device_id mlxbf_i2c_acpi_ids[] = {
{ "MLNXBF03", (kernel_ulong_t)&mlxbf_i2c_chip[MLXBF_I2C_CHIP_TYPE_1] },
{ "MLNXBF23", (kernel_ulong_t)&mlxbf_i2c_chip[MLXBF_I2C_CHIP_TYPE_2] },
{ "MLNXBF31", (kernel_ulong_t)&mlxbf_i2c_chip[MLXBF_I2C_CHIP_TYPE_3] },
{},
{ .id = "MLNXBF03", .driver_data = (kernel_ulong_t)&mlxbf_i2c_chip[MLXBF_I2C_CHIP_TYPE_1] },
{ .id = "MLNXBF23", .driver_data = (kernel_ulong_t)&mlxbf_i2c_chip[MLXBF_I2C_CHIP_TYPE_2] },
{ .id = "MLNXBF31", .driver_data = (kernel_ulong_t)&mlxbf_i2c_chip[MLXBF_I2C_CHIP_TYPE_3] },
{ }
};
MODULE_DEVICE_TABLE(acpi, mlxbf_i2c_acpi_ids);

View File

@ -0,0 +1,725 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2026 Novatek Microelectronics Corp.
* Author: Ben Huang <ben_huang@novatek.com.tw>
*/
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#define I2C_REG_CTRL 0x00
#define I2C_REG_CLK 0x04
#define I2C_REG_ACK 0x08
#define I2C_REG_SIZE 0x0C
#define I2C_REG_FIFO1 0x10
#define I2C_REG_SUBADDR 0x20
#define I2C_REG_PINGPONG 0x24
#define I2C_REG_INTR 0x28
#define I2C_REG_FIFO2 0x2C
#define I2C_REG_DUTY 0x3C
#define I2C_CLR_FIFO1 BIT(7)
#define I2C_CLR_FIFO2 BIT(23)
#define I2C_BUF_LITTLE_ENDIAN BIT(13)
#define I2C_BUSY BIT(1)
#define I2C_ENABLE BIT(2)
#define I2C_REPEAT_ENABLE BIT(7)
#define I2C_READ_OPERATION BIT(8)
#define I2C_NACK BIT(24)
#define I2C_CLOCK_DUTY_ENABLE BIT(21)
#define I2C_CLOCK_STRETCH_ENABLE BIT(27)
#define I2C_MASTER_CLK_STRETCH_ENABLE BIT(28)
#define I2C_TRIGGER BIT(0)
#define I2C_IRQ_CLK_STR_TIMEOUT BIT(12)
#define I2C_IRQ_NACK BIT(11)
#define I2C_IRQ_RX_FULL BIT(10)
#define I2C_IRQ_TX_EMPTY BIT(9)
#define I2C_IRQ_FINISH BIT(8)
#define I2C_IRQ_FLAG (I2C_IRQ_CLK_STR_TIMEOUT |\
I2C_IRQ_NACK | \
I2C_IRQ_RX_FULL | \
I2C_IRQ_TX_EMPTY | \
I2C_IRQ_FINISH)
#define I2C_IRQ_CLEAR_ALL GENMASK(20, 16)
#define I2C_IRQ_ENABLE_SETTING GENMASK(4, 0)
#define I2C_SUBADDR_ENABLE BIT(6)
#define I2C_16BITSUBADDR_ENABLE BIT(16)
#define I2C_24BITSUBADDR_ENABLE BIT(17)
#define I2C_32BITSUBADDR_ENABLE BIT(18)
#define I2C_ACK_CTRL_COUNTER 0x000001E0
#define I2C_TX_EMPTY_FIFO1 BIT(6)
#define I2C_TX_EMPTY_FIFO2 BIT(22)
#define I2C_RX_FULL_FIFO1 BIT(5)
#define I2C_RX_FULL_FIFO2 BIT(21)
#define STBC_REG_PSWD 0x0204
#define STBC_REG_KEYPASS 0x0208
#define STBC_REG_I2C_SWITCH 0x0220
#define STBC_PSWD_DATA1 0x72682
#define STBC_PSWD_DATA2 0x28627
#define STBC_KEYPASS_ENABLE BIT(0)
#define STBC_AGPIO_SWITCH_TO_CPU BIT(14)
#define FIFO_CHUNK_SIZE 16
#define FIFO_WORD_BYTES 4
enum {
SUBADDR_DISABLE = 0,
SUBADDR_8BITS,
SUBADDR_16BITS,
SUBADDR_24BITS,
SUBADDR_32BITS,
};
#define SUBADDR_MAX_LEN SUBADDR_32BITS
enum {
FIFO_1 = 0,
FIFO_2,
FIFO_ALL
};
struct nvt_i2c_bus {
void __iomem *base;
struct i2c_adapter adapter;
struct device *dev;
struct completion msg_complete;
struct clk *clk;
unsigned long input_clk_rate;
unsigned int bus_clk_rate;
bool stbc_i2c;
struct regmap *stbc_regmap;
int irq;
/* lock for msg and reg protection */
spinlock_t lock;
/* used for xfer, protected by lock */
struct i2c_msg *current_msg;
int subaddr_mode;
int remaining;
int write_ptr;
int read_ptr;
int fifo_idx;
int error_code;
};
static void nt72676_i2c_writel(u32 val, struct nvt_i2c_bus *i2c, unsigned int reg)
{
writel(val, i2c->base + reg);
}
static u32 nt72676_i2c_readl(struct nvt_i2c_bus *i2c, unsigned int reg)
{
return readl(i2c->base + reg);
}
static int nvt_i2c_stbc_auth(struct nvt_i2c_bus *i2c)
{
int ret;
if (!i2c->stbc_i2c)
return 0;
ret = regmap_write(i2c->stbc_regmap, STBC_REG_PSWD, STBC_PSWD_DATA1);
if (ret)
return ret;
ret = regmap_write(i2c->stbc_regmap, STBC_REG_PSWD, STBC_PSWD_DATA2);
if (ret)
return ret;
ret = regmap_write(i2c->stbc_regmap, STBC_REG_KEYPASS, STBC_KEYPASS_ENABLE);
if (ret)
return ret;
return regmap_update_bits(i2c->stbc_regmap, STBC_REG_I2C_SWITCH,
STBC_AGPIO_SWITCH_TO_CPU, STBC_AGPIO_SWITCH_TO_CPU);
}
static void nvt_i2c_set_clk(struct nvt_i2c_bus *i2c)
{
unsigned int clk_div = 0;
unsigned int duty = 0;
clk_div = i2c->input_clk_rate / i2c->bus_clk_rate;
nt72676_i2c_writel(clk_div << 1, i2c, I2C_REG_CLK);
duty = (clk_div * 9 + 10) / 20;
nt72676_i2c_writel(duty << 16, i2c, I2C_REG_DUTY);
nt72676_i2c_writel(I2C_ACK_CTRL_COUNTER, i2c, I2C_REG_ACK);
}
static int nvt_i2c_set_subaddr(struct nvt_i2c_bus *i2c,
const struct i2c_msg *msgs,
int num)
{
unsigned int i, subaddr = 0;
/* Sub-address can only be a write with maximum size of 4 bytes. */
if (WARN_ON(num != 2))
return -EINVAL;
if (!msgs || !msgs[0].buf ||
msgs[0].len > i2c->adapter.quirks->max_comb_1st_msg_len ||
(msgs[0].flags & I2C_M_RD) || !(msgs[1].flags & I2C_M_RD) ||
msgs[0].addr != msgs[1].addr) {
dev_dbg(i2c->dev, "Invalid sub-address format\n");
return -EOPNOTSUPP;
}
i2c->subaddr_mode = msgs[0].len;
for (i = 0; i < msgs[0].len; i++)
subaddr |= (unsigned int)msgs[0].buf[i] << (8 * (msgs[0].len - 1 - i));
nt72676_i2c_writel(subaddr, i2c, I2C_REG_SUBADDR);
return 0;
}
static int nvt_i2c_init(struct nvt_i2c_bus *i2c)
{
if (i2c->stbc_i2c) {
int ret = nvt_i2c_stbc_auth(i2c);
if (ret)
return ret;
}
nt72676_i2c_writel(I2C_IRQ_CLEAR_ALL, i2c, I2C_REG_INTR);
nvt_i2c_set_clk(i2c);
nt72676_i2c_writel(I2C_BUF_LITTLE_ENDIAN, i2c, I2C_REG_PINGPONG);
return 0;
}
static void nvt_i2c_reset(struct nvt_i2c_bus *i2c)
{
nt72676_i2c_writel(I2C_IRQ_CLEAR_ALL, i2c, I2C_REG_INTR);
if (i2c->error_code) {
nt72676_i2c_writel(nt72676_i2c_readl(i2c, I2C_REG_CTRL) & ~I2C_ENABLE,
i2c, I2C_REG_CTRL);
nt72676_i2c_writel(nt72676_i2c_readl(i2c, I2C_REG_CTRL) | I2C_ENABLE,
i2c, I2C_REG_CTRL);
}
}
static void nvt_i2c_release(struct nvt_i2c_bus *i2c)
{
nt72676_i2c_writel(nt72676_i2c_readl(i2c, I2C_REG_CTRL) & ~I2C_ENABLE,
i2c, I2C_REG_CTRL);
nt72676_i2c_writel(I2C_IRQ_CLEAR_ALL, i2c, I2C_REG_INTR);
}
static int nvt_i2c_suspend(struct device *dev)
{
struct nvt_i2c_bus *i2c = dev_get_drvdata(dev);
unsigned long flags;
i2c_mark_adapter_suspended(&i2c->adapter);
spin_lock_irqsave(&i2c->lock, flags);
i2c->current_msg = NULL;
nvt_i2c_release(i2c);
spin_unlock_irqrestore(&i2c->lock, flags);
return 0;
}
static int nvt_i2c_resume(struct device *dev)
{
struct nvt_i2c_bus *i2c = dev_get_drvdata(dev);
int ret = nvt_i2c_init(i2c);
if (ret) {
dev_err(i2c->dev, "Failed to resume\n");
return ret;
}
i2c_mark_adapter_resumed(&i2c->adapter);
return 0;
}
static void nvt_i2c_clear_fifo(struct nvt_i2c_bus *i2c, unsigned int which)
{
unsigned int regval = nt72676_i2c_readl(i2c, I2C_REG_PINGPONG);
switch (which) {
case FIFO_1:
regval |= I2C_CLR_FIFO1;
break;
case FIFO_2:
regval |= I2C_CLR_FIFO2;
break;
case FIFO_ALL:
regval |= I2C_CLR_FIFO1 | I2C_CLR_FIFO2;
break;
default:
break;
}
nt72676_i2c_writel(regval, i2c, I2C_REG_PINGPONG);
}
static void nvt_i2c_write_fifo(struct nvt_i2c_bus *i2c,
unsigned int fifo_reg,
const unsigned char *buf,
unsigned int buf_offset,
unsigned int length)
{
unsigned int reg_idx = 0, copy_bytes = 0, j = 0, value = 0;
while (length > 0) {
value = 0;
copy_bytes = min(FIFO_WORD_BYTES, length);
for (j = 0; j < copy_bytes; j++)
value |= ((unsigned int)buf[buf_offset + j]) << (j * 8);
nt72676_i2c_writel(value, i2c, fifo_reg + reg_idx * 4);
buf_offset += copy_bytes;
length -= copy_bytes;
reg_idx++;
}
}
static void nvt_i2c_read_fifo(struct nvt_i2c_bus *i2c,
unsigned int fifo_reg,
unsigned char *buf,
unsigned int buf_offset,
unsigned int length)
{
unsigned int reg_idx = 0, copy_bytes = 0, j = 0, value = 0;
while (length > 0) {
value = nt72676_i2c_readl(i2c, fifo_reg + reg_idx * 4);
copy_bytes = min(FIFO_WORD_BYTES, length);
for (j = 0; j < copy_bytes; j++)
buf[buf_offset + j] = (unsigned char)(value >> (j * 8));
buf_offset += copy_bytes;
length -= copy_bytes;
reg_idx++;
}
}
static void nvt_i2c_handle(struct nvt_i2c_bus *i2c, struct i2c_msg *msg, bool is_read)
{
unsigned int bytes, fiforeg;
if (WARN_ON(!msg->buf))
return;
bytes = min(FIFO_CHUNK_SIZE, i2c->remaining);
fiforeg = i2c->fifo_idx == FIFO_1 ? I2C_REG_FIFO1 : I2C_REG_FIFO2;
if (is_read) {
nvt_i2c_read_fifo(i2c, fiforeg, msg->buf, i2c->read_ptr, bytes);
i2c->read_ptr += bytes;
} else {
nvt_i2c_write_fifo(i2c, fiforeg, msg->buf, i2c->write_ptr, bytes);
i2c->write_ptr += bytes;
}
nvt_i2c_clear_fifo(i2c, i2c->fifo_idx);
i2c->remaining -= bytes;
i2c->fifo_idx ^= 1;
}
static irqreturn_t nvt_i2c_isr(int irq, void *dev_id)
{
struct nvt_i2c_bus *i2c = dev_id;
unsigned int status, clr = 0;
struct i2c_msg *msg;
int do_complete = 0;
spin_lock(&i2c->lock);
status = nt72676_i2c_readl(i2c, I2C_REG_INTR);
/* IRQ from other I2C, ignored */
if (!(status & I2C_IRQ_FLAG)) {
spin_unlock(&i2c->lock);
return IRQ_NONE;
}
msg = i2c->current_msg;
if (!msg) {
nt72676_i2c_writel(I2C_IRQ_CLEAR_ALL, i2c, I2C_REG_INTR);
spin_unlock(&i2c->lock);
return IRQ_HANDLED;
}
if (status & I2C_IRQ_CLK_STR_TIMEOUT) {
i2c->error_code = -ETIMEDOUT;
clr |= I2C_IRQ_CLK_STR_TIMEOUT << 8;
} else if (status & I2C_IRQ_NACK) {
i2c->error_code = -ENXIO;
clr |= I2C_IRQ_NACK << 8;
} else if (status & I2C_IRQ_RX_FULL) {
if (i2c->remaining > 0)
nvt_i2c_handle(i2c, msg, true);
clr |= I2C_IRQ_RX_FULL << 8;
} else if (status & I2C_IRQ_TX_EMPTY) {
if (i2c->remaining > 0)
nvt_i2c_handle(i2c, msg, false);
clr |= I2C_IRQ_TX_EMPTY << 8;
} else if (status & I2C_IRQ_FINISH) {
if (i2c->remaining > 0 && (msg->flags & I2C_M_RD))
nvt_i2c_handle(i2c, msg, true);
clr |= I2C_IRQ_FINISH << 8;
do_complete = 1;
}
if (i2c->error_code)
do_complete = 1;
nt72676_i2c_writel(status | clr, i2c, I2C_REG_INTR);
if (do_complete)
complete(&i2c->msg_complete);
spin_unlock(&i2c->lock);
return IRQ_HANDLED;
}
static void nvt_i2c_ctrl_init(struct nvt_i2c_bus *i2c)
{
int i = 0;
nt72676_i2c_writel(0, i2c, I2C_REG_CTRL);
for (i = 0; i < 4; i++) {
nt72676_i2c_writel(0, i2c, I2C_REG_FIFO1 + i * 4);
nt72676_i2c_writel(0, i2c, I2C_REG_FIFO2 + i * 4);
}
nvt_i2c_clear_fifo(i2c, FIFO_ALL);
nt72676_i2c_writel(0, i2c, I2C_REG_SUBADDR);
i2c->subaddr_mode = SUBADDR_DISABLE;
}
static int nvt_i2c_check_msg(const struct i2c_msg *msg)
{
if (!msg || !msg->buf || !msg->len)
return -EINVAL;
return 0;
}
static void nvt_i2c_prepare_xfer(struct nvt_i2c_bus *i2c, struct i2c_msg *msg)
{
reinit_completion(&i2c->msg_complete);
i2c->remaining = msg->len;
i2c->current_msg = msg;
i2c->write_ptr = 0;
i2c->read_ptr = 0;
i2c->error_code = 0;
i2c->fifo_idx = FIFO_1;
}
static int nvt_i2c_write(struct nvt_i2c_bus *i2c, struct i2c_msg *msg)
{
int ret, offset = 0, write_bytes, fifo_num;
unsigned int ctrl_mask = 0;
unsigned long flags;
ret = nvt_i2c_check_msg(msg);
if (ret)
return ret;
spin_lock_irqsave(&i2c->lock, flags);
nvt_i2c_prepare_xfer(i2c, msg);
nt72676_i2c_writel((msg->len * 8) << 8, i2c, I2C_REG_SIZE);
/* Write FIFO data first */
for (fifo_num = FIFO_1; fifo_num < FIFO_ALL && offset < msg->len; fifo_num++) {
write_bytes = min(FIFO_CHUNK_SIZE, msg->len - offset);
nvt_i2c_write_fifo(i2c, fifo_num == FIFO_1 ? I2C_REG_FIFO1 : I2C_REG_FIFO2,
msg->buf, offset, write_bytes);
offset += write_bytes;
}
i2c->write_ptr = offset;
i2c->remaining = msg->len - offset;
i2c->fifo_idx = FIFO_1;
/* Enable interrupt */
nt72676_i2c_writel(I2C_IRQ_ENABLE_SETTING | I2C_IRQ_CLEAR_ALL,
i2c, I2C_REG_INTR);
/* Trigger */
switch (i2c->subaddr_mode) {
case SUBADDR_8BITS:
ctrl_mask |= I2C_SUBADDR_ENABLE;
break;
case SUBADDR_16BITS:
ctrl_mask |= I2C_SUBADDR_ENABLE | I2C_16BITSUBADDR_ENABLE;
break;
case SUBADDR_24BITS:
ctrl_mask |= I2C_SUBADDR_ENABLE | I2C_24BITSUBADDR_ENABLE;
break;
case SUBADDR_32BITS:
ctrl_mask |= I2C_SUBADDR_ENABLE | I2C_32BITSUBADDR_ENABLE;
break;
default:
break;
}
ctrl_mask |= (((msg->addr << 1) << 8) | I2C_ENABLE |
I2C_CLOCK_DUTY_ENABLE | I2C_CLOCK_STRETCH_ENABLE |
I2C_MASTER_CLK_STRETCH_ENABLE | I2C_TRIGGER);
nt72676_i2c_writel(ctrl_mask, i2c, I2C_REG_CTRL);
spin_unlock_irqrestore(&i2c->lock, flags);
ret = wait_for_completion_timeout(&i2c->msg_complete, i2c->adapter.timeout);
spin_lock_irqsave(&i2c->lock, flags);
if (!ret)
i2c->error_code = -ETIMEDOUT;
nvt_i2c_reset(i2c);
ret = i2c->error_code;
i2c->current_msg = NULL;
spin_unlock_irqrestore(&i2c->lock, flags);
if (ret)
dev_dbg(i2c->dev, "Write failed (err:%d); SA[0x%X]\n",
i2c->error_code, msg->addr);
return ret;
}
static int nvt_i2c_read(struct nvt_i2c_bus *i2c, struct i2c_msg *msg)
{
unsigned int ctrl_mask = 0;
unsigned long flags;
int ret;
ret = nvt_i2c_check_msg(msg);
if (ret)
return ret;
spin_lock_irqsave(&i2c->lock, flags);
nvt_i2c_prepare_xfer(i2c, msg);
nt72676_i2c_writel((msg->len * 8) << 8, i2c, I2C_REG_SIZE);
/* Enable interrupt */
nt72676_i2c_writel(I2C_IRQ_ENABLE_SETTING | I2C_IRQ_CLEAR_ALL,
i2c, I2C_REG_INTR);
/* Trigger */
switch (i2c->subaddr_mode) {
case SUBADDR_8BITS:
ctrl_mask |= I2C_SUBADDR_ENABLE;
break;
case SUBADDR_16BITS:
ctrl_mask |= I2C_SUBADDR_ENABLE | I2C_16BITSUBADDR_ENABLE;
break;
case SUBADDR_24BITS:
ctrl_mask |= I2C_SUBADDR_ENABLE | I2C_24BITSUBADDR_ENABLE;
break;
case SUBADDR_32BITS:
ctrl_mask |= I2C_SUBADDR_ENABLE | I2C_32BITSUBADDR_ENABLE;
break;
default:
break;
}
ctrl_mask |= (((msg->addr << 1) << 8) | I2C_ENABLE |
I2C_REPEAT_ENABLE | I2C_READ_OPERATION |
I2C_CLOCK_DUTY_ENABLE | I2C_CLOCK_STRETCH_ENABLE |
I2C_MASTER_CLK_STRETCH_ENABLE | I2C_TRIGGER);
nt72676_i2c_writel(ctrl_mask, i2c, I2C_REG_CTRL);
spin_unlock_irqrestore(&i2c->lock, flags);
ret = wait_for_completion_timeout(&i2c->msg_complete, i2c->adapter.timeout);
spin_lock_irqsave(&i2c->lock, flags);
if (!ret)
i2c->error_code = -ETIMEDOUT;
nvt_i2c_reset(i2c);
ret = i2c->error_code;
i2c->current_msg = NULL;
spin_unlock_irqrestore(&i2c->lock, flags);
if (ret)
dev_dbg(i2c->dev, "Read failed (err:%d); SA[0x%X]\n",
i2c->error_code, msg->addr);
return ret;
}
static int nvt_i2c_xfer(struct i2c_adapter *adap,
struct i2c_msg msgs[],
int num)
{
struct nvt_i2c_bus *i2c = i2c_get_adapdata(adap);
struct i2c_msg *msg = &msgs[0];
int ret = 0;
nvt_i2c_ctrl_init(i2c);
if (num == 2) {
ret = nvt_i2c_set_subaddr(i2c, msgs, num);
if (ret)
return ret;
msg = &msgs[1];
}
if (msg->flags & I2C_M_RD)
ret = nvt_i2c_read(i2c, msg);
else
ret = nvt_i2c_write(i2c, msg);
if (ret < 0)
return ret;
return num;
}
static u32 nvt_i2c_func(struct i2c_adapter *adap)
{
return (I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL) & ~I2C_FUNC_SMBUS_QUICK;
}
static const struct i2c_algorithm nvt_i2c_algo = {
.master_xfer = nvt_i2c_xfer,
.functionality = nvt_i2c_func,
};
static int nvt_i2c_parse_dts(struct nvt_i2c_bus *i2c)
{
struct device *dev = i2c->dev;
struct device_node *np = dev->of_node;
int ret;
/* read DTS(novatek,stbc-syscon) for STBC I2C */
i2c->stbc_i2c = of_property_present(dev->of_node, "novatek,stbc-syscon");
if (i2c->stbc_i2c) {
i2c->stbc_regmap = syscon_regmap_lookup_by_phandle(dev->of_node,
"novatek,stbc-syscon");
if (IS_ERR(i2c->stbc_regmap))
return dev_err_probe(dev, PTR_ERR(i2c->stbc_regmap),
"Failed to get STBC syscon\n");
}
/* read DTS(clock-frequency) */
ret = of_property_read_u32(np, "clock-frequency", &i2c->bus_clk_rate);
if (ret || !i2c->bus_clk_rate) {
dev_info(dev, "Not set dtb clock-frequency, set default 100kHz\n");
i2c->bus_clk_rate = I2C_MAX_STANDARD_MODE_FREQ;
}
return 0;
}
static const struct of_device_id nvt_i2c_of_match[] = {
{ .compatible = "novatek,nt72676-i2c" },
{ }
};
MODULE_DEVICE_TABLE(of, nvt_i2c_of_match);
static const struct i2c_adapter_quirks nvt_i2c_quirks = {
.flags = I2C_AQ_COMB_WRITE_THEN_READ | I2C_AQ_NO_ZERO_LEN,
.max_num_msgs = 2,
.max_write_len = 4096,
.max_read_len = 4096,
.max_comb_1st_msg_len = SUBADDR_MAX_LEN,
.max_comb_2nd_msg_len = 4096,
};
static int nvt_i2c_probe(struct platform_device *pdev)
{
struct nvt_i2c_bus *i2c;
int ret;
i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
if (!i2c)
return -ENOMEM;
i2c->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(i2c->base))
return PTR_ERR(i2c->base);
spin_lock_init(&i2c->lock);
init_completion(&i2c->msg_complete);
i2c->dev = &pdev->dev;
ret = nvt_i2c_parse_dts(i2c);
if (ret)
return ret;
i2c->clk = devm_clk_get_enabled(&pdev->dev, NULL);
if (IS_ERR(i2c->clk))
return dev_err_probe(&pdev->dev, PTR_ERR(i2c->clk),
"devm_clk_get_enabled fail\n");
i2c->input_clk_rate = clk_get_rate(i2c->clk);
if (!i2c->input_clk_rate)
return dev_err_probe(&pdev->dev, -EINVAL, "Invalid input clock rate\n");
if (i2c->input_clk_rate < i2c->bus_clk_rate)
return dev_err_probe(&pdev->dev, -EINVAL,
"Input clock rate %lu is lower than bus frequency %u\n",
i2c->input_clk_rate, i2c->bus_clk_rate);
i2c->irq = platform_get_irq(pdev, 0);
if (i2c->irq < 0)
return i2c->irq;
ret = nvt_i2c_init(i2c);
if (ret)
return dev_err_probe(&pdev->dev, ret, "nvt_i2c_init fail\n");
ret = devm_request_irq(&pdev->dev, i2c->irq, nvt_i2c_isr,
IRQF_SHARED, dev_name(&pdev->dev), i2c);
if (ret) {
nvt_i2c_release(i2c);
return dev_err_probe(&pdev->dev, ret, "devm_request_irq fail\n");
}
/* Setup I2C adapter */
i2c->adapter.owner = THIS_MODULE;
i2c->adapter.algo = &nvt_i2c_algo;
i2c->adapter.dev.of_node = pdev->dev.of_node;
i2c->adapter.dev.parent = &pdev->dev;
i2c->adapter.quirks = &nvt_i2c_quirks;
i2c->adapter.timeout = 3 * HZ;
strscpy(i2c->adapter.name, dev_name(&pdev->dev), sizeof(i2c->adapter.name));
i2c_set_adapdata(&i2c->adapter, i2c);
ret = i2c_add_adapter(&i2c->adapter);
if (ret) {
nvt_i2c_release(i2c);
return dev_err_probe(&pdev->dev, ret, "Failed to add adapter\n");
}
platform_set_drvdata(pdev, i2c);
return 0;
}
static void nvt_i2c_remove(struct platform_device *pdev)
{
struct nvt_i2c_bus *i2c = platform_get_drvdata(pdev);
unsigned long flags;
i2c_del_adapter(&i2c->adapter);
spin_lock_irqsave(&i2c->lock, flags);
nvt_i2c_release(i2c);
spin_unlock_irqrestore(&i2c->lock, flags);
}
static const struct dev_pm_ops nvt_i2c_pm_ops = {
.resume_early = nvt_i2c_resume,
.suspend_late = nvt_i2c_suspend,
};
static struct platform_driver nvt_i2c_driver = {
.probe = nvt_i2c_probe,
.remove = nvt_i2c_remove,
.driver = {
.name = "nt72676_i2c",
.pm = &nvt_i2c_pm_ops,
.of_match_table = of_match_ptr(nvt_i2c_of_match),
},
};
module_platform_driver(nvt_i2c_driver);
MODULE_DESCRIPTION("Novatek NT72676 SoC I2C Bus Driver");
MODULE_AUTHOR("Ben Huang <ben_huang@novatek.com.tw>");
MODULE_LICENSE("GPL");

View File

@ -11,6 +11,7 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/pm_opp.h>
#define CCI_HW_VERSION 0x0
#define CCI_RESET_CMD 0x004
@ -27,6 +28,7 @@
#define CCI_I2C_Mm_SDA_CTL_1(m) (0x108 + 0x100 * (m))
#define CCI_I2C_Mm_SDA_CTL_2(m) (0x10c + 0x100 * (m))
#define CCI_I2C_Mm_MISC_CTL(m) (0x110 + 0x100 * (m))
#define CCI_I2C_MISC_CTL_SCL_STRETCH_EN BIT(8)
#define CCI_I2C_Mm_READ_DATA(m) (0x118 + 0x100 * (m))
#define CCI_I2C_Mm_READ_BUF_LEVEL(m) (0x11c + 0x100 * (m))
@ -82,6 +84,13 @@ enum {
I2C_MODE_STANDARD,
I2C_MODE_FAST,
I2C_MODE_FAST_PLUS,
NUM_I2C_MODES,
};
enum {
CCI_CLK_RATE_19_2MHZ,
CCI_CLK_RATE_37_5MHZ,
NUM_CCI_CLK_RATES,
};
enum cci_i2c_queue_t {
@ -97,7 +106,6 @@ struct hw_params {
u16 thd_dat; /* data hold time */
u16 thd_sta; /* hold time (repeated) START condition */
u16 tbuf; /* bus free time between a STOP and START condition */
u8 scl_stretch_en;
u16 trdhld;
u16 tsp; /* pulse width of spikes suppressed by the input filter */
};
@ -117,7 +125,8 @@ struct cci_data {
unsigned int num_masters;
struct i2c_adapter_quirks quirks;
u16 queue_size[NUM_QUEUES];
struct hw_params params[3];
/* Highest I2C mode supported by this variant. */
u8 max_mode;
};
struct cci {
@ -127,6 +136,7 @@ struct cci {
const struct cci_data *data;
struct clk_bulk_data *clocks;
int nclocks;
struct clk *cci_clk;
struct cci_master master[NUM_MASTERS];
};
@ -225,7 +235,92 @@ static int cci_halt(struct cci *cci, u8 master_num)
return 0;
}
static void cci_init(struct cci *cci)
static const unsigned long cci_clk_rates[NUM_CCI_CLK_RATES] = {
[CCI_CLK_RATE_19_2MHZ] = 19200000,
[CCI_CLK_RATE_37_5MHZ] = 37500000,
};
static int cci_clk_rate_idx(unsigned long rate)
{
int i;
for (i = 0; i < NUM_CCI_CLK_RATES; i++)
if (cci_clk_rates[i] == rate)
return i;
return -EINVAL;
}
static const struct hw_params cci_hw_params[NUM_CCI_CLK_RATES][NUM_I2C_MODES] = {
[CCI_CLK_RATE_19_2MHZ][I2C_MODE_STANDARD] = {
.thigh = 78,
.tlow = 114,
.tsu_sto = 28,
.tsu_sta = 28,
.thd_dat = 10,
.thd_sta = 77,
.tbuf = 118,
.trdhld = 6,
.tsp = 1
},
[CCI_CLK_RATE_19_2MHZ][I2C_MODE_FAST] = {
.thigh = 20,
.tlow = 28,
.tsu_sto = 21,
.tsu_sta = 21,
.thd_dat = 13,
.thd_sta = 18,
.tbuf = 32,
.trdhld = 6,
.tsp = 3
},
[CCI_CLK_RATE_37_5MHZ][I2C_MODE_STANDARD] = {
.thigh = 201,
.tlow = 174,
.tsu_sto = 204,
.tsu_sta = 231,
.thd_dat = 22,
.thd_sta = 162,
.tbuf = 227,
.trdhld = 6,
.tsp = 3
},
[CCI_CLK_RATE_37_5MHZ][I2C_MODE_FAST] = {
.thigh = 38,
.tlow = 56,
.tsu_sto = 40,
.tsu_sta = 40,
.thd_dat = 22,
.thd_sta = 35,
.tbuf = 62,
.trdhld = 6,
.tsp = 3
},
[CCI_CLK_RATE_37_5MHZ][I2C_MODE_FAST_PLUS] = {
.thigh = 16,
.tlow = 22,
.tsu_sto = 17,
.tsu_sta = 18,
.thd_dat = 16,
.thd_sta = 15,
.tbuf = 24,
.trdhld = 3,
.tsp = 3
},
};
static const struct hw_params *cci_get_hw_params(struct cci *cci, int mode)
{
unsigned long rate = clk_get_rate(cci->cci_clk);
int ri = cci_clk_rate_idx(rate);
if (ri >= 0 && mode <= cci->data->max_mode)
return &cci_hw_params[ri][mode];
return NULL;
}
static int cci_init(struct cci *cci)
{
u32 val = CCI_IRQ_MASK_0_I2C_M0_RD_DONE |
CCI_IRQ_MASK_0_I2C_M0_Q0_REPORT |
@ -249,7 +344,12 @@ static void cci_init(struct cci *cci)
if (!cci->master[i].cci)
continue;
hw = &cci->data->params[mode];
hw = cci_get_hw_params(cci, mode);
if (!hw) {
dev_err(cci->dev, "no timing for mode %d at CCI clock %lu Hz\n",
mode, clk_get_rate(cci->cci_clk));
return -EOPNOTSUPP;
}
val = hw->thigh << 16 | hw->tlow;
writel(val, cci->base + CCI_I2C_Mm_SCL_CTL(i));
@ -263,9 +363,11 @@ static void cci_init(struct cci *cci)
val = hw->tbuf;
writel(val, cci->base + CCI_I2C_Mm_SDA_CTL_2(i));
val = hw->scl_stretch_en << 8 | hw->trdhld << 4 | hw->tsp;
val = CCI_I2C_MISC_CTL_SCL_STRETCH_EN | hw->trdhld << 4 | hw->tsp;
writel(val, cci->base + CCI_I2C_Mm_MISC_CTL(i));
}
return 0;
}
static int cci_reset(struct cci *cci)
@ -283,9 +385,7 @@ static int cci_reset(struct cci *cci)
return -ETIMEDOUT;
}
cci_init(cci);
return 0;
return cci_init(cci);
}
static int cci_run_queue(struct cci *cci, u8 master, u8 queue)
@ -471,6 +571,69 @@ static void cci_disable_clocks(struct cci *cci)
clk_bulk_disable_unprepare(cci->nclocks, cci->clocks);
}
/*
* The single CCI clock is shared by all masters, which may run in different
* modes. Pick the lowest rate that has a valid timing set for every active
* master's mode.
*/
static int cci_get_required_rate(struct cci *cci, unsigned long *rate)
{
for (int ri = 0; ri < NUM_CCI_CLK_RATES; ri++) {
bool supported = true;
if (clk_round_rate(cci->cci_clk, cci_clk_rates[ri]) != cci_clk_rates[ri])
continue;
for (int i = 0; i < cci->data->num_masters; i++) {
int mode = cci->master[i].mode;
if (!cci->master[i].cci)
continue;
if (mode > cci->data->max_mode ||
!cci_hw_params[ri][mode].thigh) {
supported = false;
break;
}
}
if (supported) {
*rate = cci_clk_rates[ri];
return 0;
}
}
return -EOPNOTSUPP;
}
static int cci_set_core_rate(struct cci *cci)
{
struct device *dev = cci->dev;
unsigned long rate;
int ret;
ret = cci_get_required_rate(cci, &rate);
if (ret) {
dev_err(dev, "no CCI clock rate satisfies all masters\n");
return ret;
}
ret = dev_pm_opp_set_rate(dev, rate);
if (ret) {
dev_err(dev, "CCI clock could not be set to %lu Hz\n", rate);
return ret;
}
/*
* Sanity: The hw_params timings are only valid at the exact
* expected rate, verify what landed on the hardware.
*/
if (clk_get_rate(cci->cci_clk) != rate)
dev_warn(dev, "CCI clock is not at expected %lu Hz\n", rate);
return 0;
}
static int __maybe_unused cci_suspend_runtime(struct device *dev)
{
struct cci *cci = dev_get_drvdata(dev);
@ -488,8 +651,7 @@ static int __maybe_unused cci_resume_runtime(struct device *dev)
if (ret)
return ret;
cci_init(cci);
return 0;
return cci_init(cci);
}
static const struct dev_pm_ops qcom_cci_pm = {
@ -577,6 +739,24 @@ static int cci_probe(struct platform_device *pdev)
return dev_err_probe(dev, -EINVAL, "not enough clocks in DT\n");
cci->nclocks = ret;
cci->cci_clk = devm_clk_get(dev, "cci");
if (IS_ERR(cci->cci_clk))
return dev_err_probe(dev, PTR_ERR(cci->cci_clk),
"failed to get CCI clock\n");
ret = devm_pm_opp_set_clkname(dev, "cci");
if (ret)
return dev_err_probe(dev, ret, "failed to set CCI OPP clk\n");
/* OPP table is optional */
ret = devm_pm_opp_of_add_table(dev);
if (ret && ret != -ENODEV)
return dev_err_probe(dev, ret, "failed to add OPP table\n");
ret = cci_set_core_rate(cci);
if (ret)
return ret;
ret = cci_enable_clocks(cci);
if (ret < 0)
return ret;
@ -651,30 +831,7 @@ static const struct cci_data cci_v1_data = {
.max_write_len = 10,
.max_read_len = 12,
},
.params[I2C_MODE_STANDARD] = {
.thigh = 78,
.tlow = 114,
.tsu_sto = 28,
.tsu_sta = 28,
.thd_dat = 10,
.thd_sta = 77,
.tbuf = 118,
.scl_stretch_en = 0,
.trdhld = 6,
.tsp = 1
},
.params[I2C_MODE_FAST] = {
.thigh = 20,
.tlow = 28,
.tsu_sto = 21,
.tsu_sta = 21,
.thd_dat = 13,
.thd_sta = 18,
.tbuf = 32,
.scl_stretch_en = 0,
.trdhld = 6,
.tsp = 3
},
.max_mode = I2C_MODE_FAST,
};
static const struct cci_data cci_v1_5_data = {
@ -684,30 +841,7 @@ static const struct cci_data cci_v1_5_data = {
.max_write_len = 10,
.max_read_len = 12,
},
.params[I2C_MODE_STANDARD] = {
.thigh = 78,
.tlow = 114,
.tsu_sto = 28,
.tsu_sta = 28,
.thd_dat = 10,
.thd_sta = 77,
.tbuf = 118,
.scl_stretch_en = 0,
.trdhld = 6,
.tsp = 1
},
.params[I2C_MODE_FAST] = {
.thigh = 20,
.tlow = 28,
.tsu_sto = 21,
.tsu_sta = 21,
.thd_dat = 13,
.thd_sta = 18,
.tbuf = 32,
.scl_stretch_en = 0,
.trdhld = 6,
.tsp = 3
},
.max_mode = I2C_MODE_FAST,
};
static const struct cci_data cci_v2_data = {
@ -717,92 +851,12 @@ static const struct cci_data cci_v2_data = {
.max_write_len = 11,
.max_read_len = 12,
},
.params[I2C_MODE_STANDARD] = {
.thigh = 201,
.tlow = 174,
.tsu_sto = 204,
.tsu_sta = 231,
.thd_dat = 22,
.thd_sta = 162,
.tbuf = 227,
.scl_stretch_en = 0,
.trdhld = 6,
.tsp = 3
},
.params[I2C_MODE_FAST] = {
.thigh = 38,
.tlow = 56,
.tsu_sto = 40,
.tsu_sta = 40,
.thd_dat = 22,
.thd_sta = 35,
.tbuf = 62,
.scl_stretch_en = 0,
.trdhld = 6,
.tsp = 3
},
.params[I2C_MODE_FAST_PLUS] = {
.thigh = 16,
.tlow = 22,
.tsu_sto = 17,
.tsu_sta = 18,
.thd_dat = 16,
.thd_sta = 15,
.tbuf = 24,
.scl_stretch_en = 0,
.trdhld = 3,
.tsp = 3
},
};
static const struct cci_data cci_msm8953_data = {
.num_masters = 2,
.queue_size = { 64, 16 },
.quirks = {
.max_write_len = 11,
.max_read_len = 12,
},
.params[I2C_MODE_STANDARD] = {
.thigh = 78,
.tlow = 114,
.tsu_sto = 28,
.tsu_sta = 28,
.thd_dat = 10,
.thd_sta = 77,
.tbuf = 118,
.scl_stretch_en = 0,
.trdhld = 6,
.tsp = 1
},
.params[I2C_MODE_FAST] = {
.thigh = 20,
.tlow = 28,
.tsu_sto = 21,
.tsu_sta = 21,
.thd_dat = 13,
.thd_sta = 18,
.tbuf = 32,
.scl_stretch_en = 0,
.trdhld = 6,
.tsp = 3
},
.params[I2C_MODE_FAST_PLUS] = {
.thigh = 16,
.tlow = 22,
.tsu_sto = 17,
.tsu_sta = 18,
.thd_dat = 16,
.thd_sta = 15,
.tbuf = 19,
.scl_stretch_en = 1,
.trdhld = 3,
.tsp = 3
},
.max_mode = I2C_MODE_FAST_PLUS,
};
static const struct of_device_id cci_dt_match[] = {
{ .compatible = "qcom,msm8226-cci", .data = &cci_v1_data},
{ .compatible = "qcom,msm8953-cci", .data = &cci_msm8953_data},
{ .compatible = "qcom,msm8953-cci", .data = &cci_v2_data},
{ .compatible = "qcom,msm8974-cci", .data = &cci_v1_5_data},
{ .compatible = "qcom,msm8996-cci", .data = &cci_v2_data},

View File

@ -995,10 +995,9 @@ static int geni_i2c_xfer(struct i2c_adapter *adap,
struct geni_i2c_dev *gi2c = i2c_get_adapdata(adap);
int ret;
ret = pm_runtime_get_sync(gi2c->se.dev);
ret = pm_runtime_resume_and_get(gi2c->se.dev);
if (ret < 0) {
dev_err(gi2c->se.dev, "error turning SE resources:%d\n", ret);
pm_runtime_put_noidle(gi2c->se.dev);
/* Set device in suspended since resume failed */
pm_runtime_set_suspended(gi2c->se.dev);
return ret;
@ -1074,10 +1073,8 @@ static int geni_i2c_init(struct geni_i2c_dev *gi2c)
int ret;
ret = pm_runtime_resume_and_get(gi2c->se.dev);
if (ret < 0) {
dev_err(gi2c->se.dev, "error turning on device :%d\n", ret);
return ret;
}
if (ret < 0)
return dev_err_probe(gi2c->se.dev, ret, "error turning on device\n");
proto = geni_se_read_proto(&gi2c->se);
if (proto == GENI_SE_INVALID_PROTO) {
@ -1177,9 +1174,6 @@ static int geni_i2c_probe(struct platform_device *pdev)
gi2c->clk_freq_out = I2C_MAX_STANDARD_MODE_FREQ;
}
if (has_acpi_companion(dev))
ACPI_COMPANION_SET(&gi2c->adap.dev, ACPI_COMPANION(dev));
gi2c->irq = platform_get_irq(pdev, 0);
if (gi2c->irq < 0)
return gi2c->irq;
@ -1205,7 +1199,7 @@ static int geni_i2c_probe(struct platform_device *pdev)
i2c_set_adapdata(&gi2c->adap, gi2c);
gi2c->adap.dev.parent = dev;
gi2c->adap.dev.of_node = dev->of_node;
device_set_node(&gi2c->adap.dev, dev_fwnode(dev));
strscpy(gi2c->adap.name, "Geni-I2C", sizeof(gi2c->adap.name));
pm_runtime_set_suspended(dev);
@ -1247,7 +1241,7 @@ static void geni_i2c_shutdown(struct platform_device *pdev)
i2c_mark_adapter_suspended(&gi2c->adap);
}
static int __maybe_unused geni_i2c_runtime_suspend(struct device *dev)
static int geni_i2c_runtime_suspend(struct device *dev)
{
int ret = 0;
struct geni_i2c_dev *gi2c = dev_get_drvdata(dev);
@ -1265,7 +1259,7 @@ static int __maybe_unused geni_i2c_runtime_suspend(struct device *dev)
return 0;
}
static int __maybe_unused geni_i2c_runtime_resume(struct device *dev)
static int geni_i2c_runtime_resume(struct device *dev)
{
int ret = 0;
struct geni_i2c_dev *gi2c = dev_get_drvdata(dev);
@ -1281,7 +1275,7 @@ static int __maybe_unused geni_i2c_runtime_resume(struct device *dev)
return 0;
}
static int __maybe_unused geni_i2c_suspend_noirq(struct device *dev)
static int geni_i2c_suspend_noirq(struct device *dev)
{
struct geni_i2c_dev *gi2c = dev_get_drvdata(dev);
int ret;
@ -1295,7 +1289,7 @@ static int __maybe_unused geni_i2c_suspend_noirq(struct device *dev)
return ret;
}
static int __maybe_unused geni_i2c_resume_noirq(struct device *dev)
static int geni_i2c_resume_noirq(struct device *dev)
{
struct geni_i2c_dev *gi2c = dev_get_drvdata(dev);
int ret;
@ -1309,9 +1303,10 @@ static int __maybe_unused geni_i2c_resume_noirq(struct device *dev)
}
static const struct dev_pm_ops geni_i2c_pm_ops = {
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(geni_i2c_suspend_noirq, geni_i2c_resume_noirq)
SET_RUNTIME_PM_OPS(geni_i2c_runtime_suspend, geni_i2c_runtime_resume,
NULL)
NOIRQ_SYSTEM_SLEEP_PM_OPS(geni_i2c_suspend_noirq,
geni_i2c_resume_noirq)
RUNTIME_PM_OPS(geni_i2c_runtime_suspend, geni_i2c_runtime_resume,
NULL)
};
static const struct geni_i2c_desc geni_i2c = {
@ -1337,8 +1332,8 @@ static const struct geni_i2c_desc sa8255p_geni_i2c = {
#ifdef CONFIG_ACPI
static const struct acpi_device_id geni_i2c_acpi_match[] = {
{ "QCOM0220", (kernel_ulong_t)&geni_i2c},
{ "QCOM0411", (kernel_ulong_t)&geni_i2c},
{ .id = "QCOM0220", .driver_data = (kernel_ulong_t)&geni_i2c },
{ .id = "QCOM0411", .driver_data = (kernel_ulong_t)&geni_i2c },
{ }
};
MODULE_DEVICE_TABLE(acpi, geni_i2c_acpi_match);

View File

@ -1688,7 +1688,7 @@ static void qup_i2c_disable_clocks(struct qup_i2c_dev *qup)
}
static const struct acpi_device_id qup_i2c_acpi_match[] = {
{ "QCOM8010"},
{ .id = "QCOM8010" },
{ }
};
MODULE_DEVICE_TABLE(acpi, qup_i2c_acpi_match);

View File

@ -42,10 +42,10 @@ static const struct smbus_methods_t ibm_smbus_methods = {
};
static const struct acpi_device_id acpi_smbus_cmi_ids[] = {
{"SMBUS01", (kernel_ulong_t)&smbus_methods},
{ACPI_SMBUS_IBM_HID, (kernel_ulong_t)&ibm_smbus_methods},
{ACPI_SMBUS_MS_HID, (kernel_ulong_t)&smbus_methods},
{"", 0}
{ .id = "SMBUS01", .driver_data = (kernel_ulong_t)&smbus_methods },
{ .id = ACPI_SMBUS_IBM_HID, .driver_data = (kernel_ulong_t)&ibm_smbus_methods },
{ .id = ACPI_SMBUS_MS_HID, .driver_data = (kernel_ulong_t)&smbus_methods },
{ }
};
MODULE_DEVICE_TABLE(acpi, acpi_smbus_cmi_ids);

View File

@ -622,8 +622,8 @@ MODULE_DEVICE_TABLE(of, synquacer_i2c_dt_ids);
#ifdef CONFIG_ACPI
static const struct acpi_device_id synquacer_i2c_acpi_ids[] = {
{ "SCX0003" },
{ /* sentinel */ }
{ .id = "SCX0003" },
{ }
};
MODULE_DEVICE_TABLE(acpi, synquacer_i2c_acpi_ids);
#endif

View File

@ -2486,10 +2486,10 @@ static const struct dev_pm_ops tegra_i2c_pm = {
};
static const struct acpi_device_id tegra_i2c_acpi_match[] = {
{.id = "NVDA0101", .driver_data = (kernel_ulong_t)&tegra210_i2c_hw},
{.id = "NVDA0201", .driver_data = (kernel_ulong_t)&tegra186_i2c_hw},
{.id = "NVDA0301", .driver_data = (kernel_ulong_t)&tegra194_i2c_hw},
{.id = "NVDA2017", .driver_data = (kernel_ulong_t)&tegra410_i2c_hw},
{ .id = "NVDA0101", .driver_data = (kernel_ulong_t)&tegra210_i2c_hw },
{ .id = "NVDA0201", .driver_data = (kernel_ulong_t)&tegra186_i2c_hw },
{ .id = "NVDA0301", .driver_data = (kernel_ulong_t)&tegra194_i2c_hw },
{ .id = "NVDA2017", .driver_data = (kernel_ulong_t)&tegra410_i2c_hw },
{ }
};
MODULE_DEVICE_TABLE(acpi, tegra_i2c_acpi_match);

View File

@ -24,12 +24,12 @@ struct usbio_i2c {
};
static const struct acpi_device_id usbio_i2c_acpi_hids[] = {
{ "INTC1008" }, /* MTL */
{ "INTC10B3" }, /* ARL */
{ "INTC10B6" }, /* LNL */
{ "INTC10D2" }, /* MTL-CVF */
{ "INTC10E3" }, /* PTL */
{ "INTC1118" }, /* NVL */
{ .id = "INTC1008" }, /* MTL */
{ .id = "INTC10B3" }, /* ARL */
{ .id = "INTC10B6" }, /* LNL */
{ .id = "INTC10D2" }, /* MTL-CVF */
{ .id = "INTC10E3" }, /* PTL */
{ .id = "INTC1118" }, /* NVL */
{ }
};

View File

@ -345,7 +345,7 @@ static const struct dev_pm_ops zxi2c_pm = {
};
static const struct acpi_device_id zxi2c_acpi_match[] = {
{"IIC1D17", 0 },
{ .id = "IIC1D17" },
{ }
};
MODULE_DEVICE_TABLE(acpi, zxi2c_acpi_match);

View File

@ -539,9 +539,9 @@ MODULE_DEVICE_TABLE(of, xgene_slimpro_i2c_dt_ids);
#ifdef CONFIG_ACPI
static const struct acpi_device_id xgene_slimpro_i2c_acpi_ids[] = {
{"APMC0D40", XGENE_SLIMPRO_I2C_V1},
{"APMC0D8B", XGENE_SLIMPRO_I2C_V2},
{}
{ .id = "APMC0D40", .driver_data = XGENE_SLIMPRO_I2C_V1 },
{ .id = "APMC0D8B", .driver_data = XGENE_SLIMPRO_I2C_V2 },
{ }
};
MODULE_DEVICE_TABLE(acpi, xgene_slimpro_i2c_acpi_ids);
#endif

View File

@ -570,9 +570,9 @@ static void xlp9xx_i2c_remove(struct platform_device *pdev)
#ifdef CONFIG_ACPI
static const struct acpi_device_id xlp9xx_i2c_acpi_ids[] = {
{"BRCM9007", 0},
{"CAV9007", 0},
{}
{ .id = "BRCM9007" },
{ .id = "CAV9007" },
{ }
};
MODULE_DEVICE_TABLE(acpi, xlp9xx_i2c_acpi_ids);
#endif

View File

@ -352,8 +352,8 @@ static const struct acpi_device_id i2c_acpi_force_400khz_device_ids[] = {
* at 400KHz (as can be expected of any recent i2c hw) so we force
* the speed of the bus to 400 KHz if a Silead device is present.
*/
{ "MSSL1680", 0 },
{}
{ .id = "MSSL1680" },
{ }
};
static const struct acpi_device_id i2c_acpi_force_100khz_device_ids[] = {
@ -371,11 +371,13 @@ static const struct acpi_device_id i2c_acpi_force_100khz_device_ids[] = {
* the device works without issues on Windows at what is expected to be
* a 400KHz frequency. The root cause of the issue is not known.
*/
{ "DLL0945", 0 },
{ "ELAN0678", 0 },
{ "ELAN06FA", 0 },
{ "ELAN1300", 0 },
{}
{ .id = "ASUE140D" },
{ .id = "DELL0A86" },
{ .id = "DLL0945" },
{ .id = "ELAN0678" },
{ .id = "ELAN06FA" },
{ .id = "ELAN1300" },
{ }
};
static acpi_status i2c_acpi_lookup_speed(acpi_handle handle, u32 level,
@ -607,7 +609,7 @@ static int acpi_gsb_i2c_read_bytes(struct i2c_client *client,
msgs[0].buf = &cmd;
msgs[1].addr = client->addr;
msgs[1].flags = client->flags | I2C_M_RD;
msgs[1].flags = client->flags | I2C_M_RD | I2C_M_DMA_SAFE;
msgs[1].len = data_len;
msgs[1].buf = buffer;
@ -648,7 +650,7 @@ static int acpi_gsb_i2c_write_bytes(struct i2c_client *client,
memcpy(buffer + 1, data, data_len);
msgs[0].addr = client->addr;
msgs[0].flags = client->flags;
msgs[0].flags = client->flags | I2C_M_DMA_SAFE;
msgs[0].len = data_len + 1;
msgs[0].buf = buffer;

View File

@ -1902,6 +1902,9 @@ static int i2c_dev_or_parent_fwnode_match(struct device *dev, const void *data)
* If no adapter can be found, or @fwnode is NULL, this returns NULL.
*
* The user must call put_device(&adapter->dev) once done with the i2c adapter.
*
* Note that i2c_get_adapter_by_fwnode(), which also takes a module reference,
* should almost always be used instead of this one.
*/
struct i2c_adapter *i2c_find_adapter_by_fwnode(struct fwnode_handle *fwnode)
{

View File

@ -208,11 +208,11 @@ s32 i2c_smbus_write_word_data(const struct i2c_client *client, u8 command,
EXPORT_SYMBOL(i2c_smbus_write_word_data);
/**
* i2c_smbus_read_block_data - SMBus "block read" protocol
* __i2c_smbus_read_block_data - SMBus "block read" protocol
* @client: Handle to slave device
* @command: Byte interpreted by slave
* @values: Byte array into which data will be read; big enough to hold
* the data returned by the slave. SMBus allows at most 32 bytes.
* @length: size of the @values array. SMBus allows at most 32 bytes
* @values: Byte array into which data will be read
*
* This executes the SMBus "block read" protocol, returning negative errno
* else the number of data bytes in the slave's response.
@ -222,22 +222,30 @@ EXPORT_SYMBOL(i2c_smbus_write_word_data);
* support this; its emulation through I2C messaging relies on a specific
* mechanism (I2C_M_RECV_LEN) which may not be implemented.
*/
s32 i2c_smbus_read_block_data(const struct i2c_client *client, u8 command,
u8 *values)
s32 __i2c_smbus_read_block_data(const struct i2c_client *client, u8 command,
u8 length, u8 *values)
{
union i2c_smbus_data data;
int ret_len;
int status;
if (length > I2C_SMBUS_BLOCK_MAX)
return -EINVAL;
status = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
I2C_SMBUS_READ, command,
I2C_SMBUS_BLOCK_DATA, &data);
if (status)
return status;
memcpy(values, &data.block[1], data.block[0]);
return data.block[0];
ret_len = data.block[0];
if (ret_len > length)
return -EMSGSIZE;
memcpy(values, &data.block[1], ret_len);
return ret_len;
}
EXPORT_SYMBOL(i2c_smbus_read_block_data);
EXPORT_SYMBOL(__i2c_smbus_read_block_data);
/**
* i2c_smbus_write_block_data - SMBus "block write" protocol

View File

@ -24,7 +24,7 @@
#include <linux/i2c-mux.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/property.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
@ -33,6 +33,7 @@ struct i2c_mux_priv {
struct i2c_adapter adap;
struct i2c_algorithm algo;
struct i2c_mux_core *muxc;
struct fwnode_handle *channel_node;
u32 chan_id;
};
@ -264,6 +265,51 @@ static const struct i2c_lock_operations i2c_parent_lock_ops = {
.unlock_bus = i2c_parent_unlock_bus,
};
static struct fwnode_handle *
i2c_mux_get_channel_node(struct i2c_mux_core *muxc, u32 chan_id)
{
struct fwnode_handle *dev_node;
struct fwnode_handle *mux_node;
struct fwnode_handle *child;
u32 reg;
int ret;
dev_node = dev_fwnode(muxc->dev);
if (!dev_node)
return NULL;
if (muxc->arbitrator)
mux_node = fwnode_get_named_child_node(dev_node, "i2c-arb");
else if (muxc->gate)
mux_node = fwnode_get_named_child_node(dev_node, "i2c-gate");
else
mux_node = fwnode_get_named_child_node(dev_node, "i2c-mux");
if (mux_node) {
/* A "reg" property indicates an old-style firmware entry. */
if (!fwnode_property_read_u32(mux_node, "reg", &reg)) {
fwnode_handle_put(mux_node);
mux_node = NULL;
}
}
if (!mux_node)
mux_node = fwnode_handle_get(dev_node);
else if (muxc->arbitrator || muxc->gate)
return mux_node;
fwnode_for_each_child_node(mux_node, child) {
ret = fwnode_property_read_u32(child, "reg", &reg);
if (ret)
continue;
if (chan_id == reg)
break;
}
fwnode_handle_put(mux_node);
return child;
}
int i2c_mux_add_adapter(struct i2c_mux_core *muxc,
u32 force_nr, u32 chan_id)
{
@ -323,48 +369,9 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc,
else
priv->adap.lock_ops = &i2c_parent_lock_ops;
/*
* Try to populate the mux adapter's of_node, expands to
* nothing if !CONFIG_OF.
*/
if (muxc->dev->of_node) {
struct device_node *dev_node = muxc->dev->of_node;
struct device_node *mux_node, *child = NULL;
u32 reg;
if (muxc->arbitrator)
mux_node = of_get_child_by_name(dev_node, "i2c-arb");
else if (muxc->gate)
mux_node = of_get_child_by_name(dev_node, "i2c-gate");
else
mux_node = of_get_child_by_name(dev_node, "i2c-mux");
if (mux_node) {
/* A "reg" property indicates an old-style DT entry */
if (!of_property_read_u32(mux_node, "reg", &reg)) {
of_node_put(mux_node);
mux_node = NULL;
}
}
if (!mux_node)
mux_node = of_node_get(dev_node);
else if (muxc->arbitrator || muxc->gate)
child = of_node_get(mux_node);
if (!child) {
for_each_child_of_node(mux_node, child) {
ret = of_property_read_u32(child, "reg", &reg);
if (ret)
continue;
if (chan_id == reg)
break;
}
}
priv->adap.dev.of_node = child;
of_node_put(mux_node);
}
/* Associate the mux adapter with its channel firmware node. */
priv->channel_node = i2c_mux_get_channel_node(muxc, chan_id);
device_set_node(&priv->adap.dev, priv->channel_node);
/*
* Associate the mux channel with an ACPI node.
@ -408,7 +415,7 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc,
return 0;
err_free_priv:
of_node_put(priv->adap.dev.of_node);
fwnode_handle_put(priv->channel_node);
kfree(priv);
return ret;
}
@ -421,7 +428,6 @@ void i2c_mux_del_adapters(struct i2c_mux_core *muxc)
while (muxc->num_adapters) {
struct i2c_adapter *adap = muxc->adapter[--muxc->num_adapters];
struct i2c_mux_priv *priv = adap->algo_data;
struct device_node *np = adap->dev.of_node;
muxc->adapter[muxc->num_adapters] = NULL;
@ -431,7 +437,7 @@ void i2c_mux_del_adapters(struct i2c_mux_core *muxc)
sysfs_remove_link(&priv->adap.dev.kobj, "mux_device");
i2c_del_adapter(adap);
of_node_put(np);
fwnode_handle_put(priv->channel_node);
kfree(priv);
}
}

View File

@ -1460,8 +1460,7 @@ static int isl7998x_probe(struct i2c_client *client)
int nr_inputs;
int ret;
ret = i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA);
if (!ret) {
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
dev_warn(&adapter->dev,
"I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
return -EIO;

View File

@ -174,8 +174,23 @@ i2c_smbus_write_word_swapped(const struct i2c_client *client,
}
/* Returns the number of read bytes */
s32 i2c_smbus_read_block_data(const struct i2c_client *client,
u8 command, u8 *values);
s32 __i2c_smbus_read_block_data(const struct i2c_client *client,
u8 command, u8 length, u8 *values);
/*
* This monstrosity allows to call i2c_smbus_read_block_data() with either
* 3 or 4 arguments and will be removed once all users have been switched
* to the 4 argument version.
*/
#define __i2c_smbus_read_block_data_3arg(client, cmd, values) \
__i2c_smbus_read_block_data(client, cmd, I2C_SMBUS_BLOCK_MAX, values)
#define __i2c_smbus_read_block_data_4arg(client, cmd, length, values) \
__i2c_smbus_read_block_data(client, cmd, length, values)
#define __i2c_smbus_read_block_data_impl(_1, _2, _3, _4, impl, ...) impl
#define i2c_smbus_read_block_data(client, cmd, varargs...) \
__i2c_smbus_read_block_data_impl(client, cmd, varargs, \
__i2c_smbus_read_block_data_4arg, \
__i2c_smbus_read_block_data_3arg) \
(client, cmd, varargs)
s32 i2c_smbus_write_block_data(const struct i2c_client *client,
u8 command, u8 length, const u8 *values);
/* Returns the number of read bytes */
@ -919,8 +934,8 @@ static inline u32 i2c_get_functionality(struct i2c_adapter *adap)
return adap->algo->functionality(adap);
}
/* Return 1 if adapter supports everything we need, 0 if not. */
static inline int i2c_check_functionality(struct i2c_adapter *adap, u32 func)
/* Return true if adapter supports everything we need, false if not. */
static inline bool i2c_check_functionality(struct i2c_adapter *adap, u32 func)
{
return (func & i2c_get_functionality(adap)) == func;
}