mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
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:
commit
bb436f2a85
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
};
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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#
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) {}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
725
drivers/i2c/busses/i2c-nt72676.c
Normal file
725
drivers/i2c/busses/i2c-nt72676.c
Normal 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");
|
||||
|
|
@ -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},
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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", ®)) {
|
||||
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", ®);
|
||||
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", ®)) {
|
||||
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", ®);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user