Merge branch 'i2c/for-5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang: - if a host can be a client, too, the I2C core can now use it to emulate SMBus HostNotify support (STM32 and R-Car added this so far) - also for client mode, a testunit has been added. It can create rare situations on the bus, so host controllers can be tested - a binding has been added to mark the bus as "single-master". This allows for better timeout detections - new driver for Mellanox Bluefield - massive refactoring of the Tegra driver - EEPROMs recognized by the at24 driver can now have custom names - rest is driver updates * 'i2c/for-5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (80 commits) Documentation: i2c: add testunit docs to index i2c: tegra: Improve driver module description i2c: tegra: Clean up whitespaces, newlines and indentation i2c: tegra: Clean up and improve comments i2c: tegra: Clean up printk messages i2c: tegra: Clean up variable names i2c: tegra: Improve formatting of variables i2c: tegra: Check errors for both positive and negative values i2c: tegra: Factor out hardware initialization into separate function i2c: tegra: Factor out register polling into separate function i2c: tegra: Factor out packet header setup from tegra_i2c_xfer_msg() i2c: tegra: Factor out error recovery from tegra_i2c_xfer_msg() i2c: tegra: Rename wait/poll functions i2c: tegra: Remove "dma" variable from tegra_i2c_xfer_msg() i2c: tegra: Remove redundant check in tegra_i2c_issue_bus_clear() i2c: tegra: Remove likely/unlikely from the code i2c: tegra: Remove outdated barrier() i2c: tegra: Clean up variable types i2c: tegra: Reorder location of functions in the code i2c: tegra: Clean up probe function ...
This commit is contained in:
commit
b5df4b5c28
|
@ -114,6 +114,9 @@ properties:
|
|||
- const: renesas,r1ex24128
|
||||
- const: atmel,24c128
|
||||
|
||||
label:
|
||||
description: Descriptive name of the EEPROM.
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
|
|
|
@ -9,12 +9,18 @@ title: Freescale Low Power Inter IC (LPI2C) for i.MX
|
|||
maintainers:
|
||||
- Anson Huang <Anson.Huang@nxp.com>
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/i2c/i2c-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- fsl,imx7ulp-lpi2c
|
||||
- fsl,imx8qxp-lpi2c
|
||||
- fsl,imx8qm-lpi2c
|
||||
oneOf:
|
||||
- enum:
|
||||
- fsl,imx7ulp-lpi2c
|
||||
- fsl,imx8qm-lpi2c
|
||||
- items:
|
||||
- const: fsl,imx8qxp-lpi2c
|
||||
- const: fsl,imx7ulp-lpi2c
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
@ -22,23 +28,34 @@ properties:
|
|||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
assigned-clock-parents: true
|
||||
assigned-clock-rates: true
|
||||
assigned-clocks: true
|
||||
clock-frequency: true
|
||||
|
||||
clock-names:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
|
||||
additionalProperties: false
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/imx7ulp-clock.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
lpi2c7@40a50000 {
|
||||
i2c@40a50000 {
|
||||
compatible = "fsl,imx7ulp-lpi2c";
|
||||
reg = <0x40A50000 0x10000>;
|
||||
interrupt-parent = <&intc>;
|
||||
|
|
|
@ -9,6 +9,9 @@ title: Freescale Inter IC (I2C) and High Speed Inter IC (HS-I2C) for i.MX
|
|||
maintainers:
|
||||
- Wolfram Sang <wolfram@the-dreams.de>
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/i2c/i2c-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
|
@ -18,6 +21,9 @@ properties:
|
|||
- items:
|
||||
- const: fsl,imx35-i2c
|
||||
- const: fsl,imx1-i2c
|
||||
- items:
|
||||
- const: fsl,imx7d-i2c
|
||||
- const: fsl,imx21-i2c
|
||||
- items:
|
||||
- enum:
|
||||
- fsl,imx25-i2c
|
||||
|
@ -75,7 +81,7 @@ required:
|
|||
- interrupts
|
||||
- clocks
|
||||
|
||||
additionalProperties: false
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
|
|
|
@ -87,6 +87,11 @@ wants to support one of the below features, it should adapt these bindings.
|
|||
this information to detect a stalled bus more reliably, for example.
|
||||
Can not be combined with 'multi-master'.
|
||||
|
||||
- smbus
|
||||
states that additional SMBus restrictions and features apply to this bus.
|
||||
Examples of features are SMBusHostNotify and SMBusAlert. Examples of
|
||||
restrictions are more reserved addresses and timeout definitions.
|
||||
|
||||
Required properties (per child device)
|
||||
--------------------------------------
|
||||
|
||||
|
|
|
@ -17,9 +17,13 @@ properties:
|
|||
pattern: "^i2c@[0-9a-f]+$"
|
||||
|
||||
compatible:
|
||||
enum:
|
||||
- ingenic,jz4780-i2c
|
||||
- ingenic,x1000-i2c
|
||||
oneOf:
|
||||
- enum:
|
||||
- ingenic,jz4770-i2c
|
||||
- ingenic,x1000-i2c
|
||||
- items:
|
||||
- const: ingenic,jz4780-i2c
|
||||
- const: ingenic,jz4770-i2c
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
@ -60,7 +64,7 @@ examples:
|
|||
#include <dt-bindings/dma/jz4780-dma.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c@10054000 {
|
||||
compatible = "ingenic,jz4780-i2c";
|
||||
compatible = "ingenic,jz4780-i2c", "ingenic,jz4770-i2c";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x10054000 0x1000>;
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
Device tree configuration for the Mellanox I2C SMBus on BlueField SoCs
|
||||
|
||||
Required Properties:
|
||||
|
||||
- compatible : should be "mellanox,i2c-mlxbf1" or "mellanox,i2c-mlxbf2".
|
||||
|
||||
- reg : address offset and length of the device registers. The
|
||||
registers consist of the following set of resources:
|
||||
1) Smbus block registers.
|
||||
2) Cause master registers.
|
||||
3) Cause slave registers.
|
||||
4) Cause coalesce registers (if compatible isn't set
|
||||
to "mellanox,i2c-mlxbf1").
|
||||
|
||||
- interrupts : interrupt number.
|
||||
|
||||
Optional Properties:
|
||||
|
||||
- clock-frequency : bus frequency used to configure timing registers;
|
||||
allowed values are 100000, 400000 and 1000000;
|
||||
those are expressed in Hz. Default is 100000.
|
||||
|
||||
Example:
|
||||
|
||||
i2c@2804000 {
|
||||
compatible = "mellanox,i2c-mlxbf1";
|
||||
reg = <0x02804000 0x800>,
|
||||
<0x02801200 0x020>,
|
||||
<0x02801260 0x020>;
|
||||
interrupts = <57>;
|
||||
clock-frequency = <100000>;
|
||||
};
|
||||
|
||||
i2c@2808800 {
|
||||
compatible = "mellanox,i2c-mlxbf2";
|
||||
reg = <0x02808800 0x600>,
|
||||
<0x02808e00 0x020>,
|
||||
<0x02808e20 0x020>,
|
||||
<0x02808e40 0x010>;
|
||||
interrupts = <57>;
|
||||
clock-frequency = <400000>;
|
||||
};
|
|
@ -44,6 +44,7 @@ Supported adapters:
|
|||
* Intel Tiger Lake (PCH)
|
||||
* Intel Jasper Lake (SOC)
|
||||
* Intel Emmitsburg (PCH)
|
||||
* Intel Alder Lake (PCH)
|
||||
|
||||
Datasheets: Publicly available at the Intel website
|
||||
|
||||
|
|
|
@ -47,6 +47,7 @@ Slave I2C
|
|||
|
||||
slave-interface
|
||||
slave-eeprom-backend
|
||||
slave-testunit-backend
|
||||
|
||||
Advanced topics
|
||||
===============
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
================================
|
||||
Linux I2C slave testunit backend
|
||||
================================
|
||||
|
||||
by Wolfram Sang <wsa@sang-engineering.com> in 2020
|
||||
|
||||
This backend can be used to trigger test cases for I2C bus masters which
|
||||
require a remote device with certain capabilities (and which are usually not so
|
||||
easy to obtain). Examples include multi-master testing, and SMBus Host Notify
|
||||
testing. For some tests, the I2C slave controller must be able to switch
|
||||
between master and slave mode because it needs to send data, too.
|
||||
|
||||
Note that this is a device for testing and debugging. It should not be enabled
|
||||
in a production build. And while there is some versioning and we try hard to
|
||||
keep backward compatibility, there is no stable ABI guaranteed!
|
||||
|
||||
Instantiating the device is regular. Example for bus 0, address 0x30:
|
||||
|
||||
# echo "slave-testunit 0x1030" > /sys/bus/i2c/devices/i2c-0/new_device
|
||||
|
||||
After that, you will have a write-only device listening. Reads will just return
|
||||
an 8-bit version number of the testunit. When writing, the device consists of 4
|
||||
8-bit registers and all must be written to start a testcase, i.e. you must
|
||||
always write 4 bytes to the device. The registers are:
|
||||
|
||||
0x00 CMD - which test to trigger
|
||||
0x01 DATAL - configuration byte 1 for the test
|
||||
0x02 DATAH - configuration byte 2 for the test
|
||||
0x03 DELAY - delay in n * 10ms until test is started
|
||||
|
||||
Using 'i2cset' from the i2c-tools package, the generic command looks like:
|
||||
|
||||
# i2cset -y <bus_num> <testunit_address> <CMD> <DATAL> <DATAH> <DELAY> i
|
||||
|
||||
DELAY is a generic parameter which will delay the execution of the test in CMD.
|
||||
While a command is running (including the delay), new commands will not be
|
||||
acknowledged. You need to wait until the old one is completed.
|
||||
|
||||
The commands are described in the following section. An invalid command will
|
||||
result in the transfer not being acknowledged.
|
||||
|
||||
Commands
|
||||
--------
|
||||
|
||||
0x00 NOOP (reserved for future use)
|
||||
|
||||
0x01 READ_BYTES (also needs master mode)
|
||||
DATAL - address to read data from (lower 7 bits, highest bit currently unused)
|
||||
DATAH - number of bytes to read
|
||||
|
||||
This is useful to test if your bus master driver is handling multi-master
|
||||
correctly. You can trigger the testunit to read bytes from another device on
|
||||
the bus. If the bus master under test also wants to access the bus at the same
|
||||
time, the bus will be busy. Example to read 128 bytes from device 0x50 after
|
||||
50ms of delay:
|
||||
|
||||
# i2cset -y 0 0x30 0x01 0x50 0x80 0x05 i
|
||||
|
||||
0x02 SMBUS_HOST_NOTIFY (also needs master mode)
|
||||
DATAL - low byte of the status word to send
|
||||
DATAH - high byte of the status word to send
|
||||
|
||||
This test will send an SMBUS_HOST_NOTIFY message to the host. Note that the
|
||||
status word is currently ignored in the Linux Kernel. Example to send a
|
||||
notification after 10ms:
|
||||
|
||||
# i2cset -y 0 0x30 0x02 0x42 0x64 0x01 i
|
|
@ -11159,6 +11159,12 @@ W: http://www.melfas.com
|
|||
F: Documentation/devicetree/bindings/input/touchscreen/melfas_mip4.txt
|
||||
F: drivers/input/touchscreen/melfas_mip4.c
|
||||
|
||||
MELLANOX BLUEFIELD I2C DRIVER
|
||||
M: Khalil Blaiech <kblaiech@mellanox.com>
|
||||
L: linux-i2c@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/i2c/busses/i2c-mlxbf.c
|
||||
|
||||
MELLANOX ETHERNET DRIVER (mlx4_en)
|
||||
M: Tariq Toukan <tariqt@nvidia.com>
|
||||
L: netdev@vger.kernel.org
|
||||
|
|
|
@ -101,7 +101,6 @@ source "drivers/i2c/busses/Kconfig"
|
|||
config I2C_STUB
|
||||
tristate "I2C/SMBus Test Stub"
|
||||
depends on m
|
||||
default 'n'
|
||||
help
|
||||
This module may be useful to developers of SMBus client drivers,
|
||||
especially for certain kinds of sensor chips.
|
||||
|
@ -126,6 +125,14 @@ config I2C_SLAVE_EEPROM
|
|||
This backend makes Linux behave like an I2C EEPROM. Please read
|
||||
Documentation/i2c/slave-eeprom-backend.rst for further details.
|
||||
|
||||
config I2C_SLAVE_TESTUNIT
|
||||
tristate "I2C eeprom testunit driver"
|
||||
help
|
||||
This backend can be used to trigger test cases for I2C bus masters
|
||||
which require a remote device with certain capabilities, e.g.
|
||||
multi-master, SMBus Host Notify, etc. Please read
|
||||
Documentation/i2c/slave-testunit-backend.rst for further details.
|
||||
|
||||
endif
|
||||
|
||||
config I2C_DEBUG_CORE
|
||||
|
|
|
@ -16,5 +16,6 @@ obj-$(CONFIG_I2C_MUX) += i2c-mux.o
|
|||
obj-y += algos/ busses/ muxes/
|
||||
obj-$(CONFIG_I2C_STUB) += i2c-stub.o
|
||||
obj-$(CONFIG_I2C_SLAVE_EEPROM) += i2c-slave-eeprom.o
|
||||
obj-$(CONFIG_I2C_SLAVE_TESTUNIT) += i2c-slave-testunit.o
|
||||
|
||||
ccflags-$(CONFIG_I2C_DEBUG_CORE) := -DDEBUG
|
||||
|
|
|
@ -147,6 +147,7 @@ config I2C_I801
|
|||
Tiger Lake (PCH)
|
||||
Jasper Lake (SOC)
|
||||
Emmitsburg (PCH)
|
||||
Alder Lake (PCH)
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-i801.
|
||||
|
@ -730,6 +731,19 @@ config I2C_LPC2K
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-lpc2k.
|
||||
|
||||
config I2C_MLXBF
|
||||
tristate "Mellanox BlueField I2C controller"
|
||||
depends on ARM64
|
||||
help
|
||||
Enabling this option will add I2C SMBus support for Mellanox BlueField
|
||||
system.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called i2c-mlxbf.
|
||||
|
||||
This driver implements an I2C SMBus host controller and enables both
|
||||
master and slave functions.
|
||||
|
||||
config I2C_MESON
|
||||
tristate "Amlogic Meson I2C controller"
|
||||
depends on ARCH_MESON || COMPILE_TEST
|
||||
|
@ -840,7 +854,6 @@ config I2C_PASEMI
|
|||
config I2C_PCA_PLATFORM
|
||||
tristate "PCA9564/PCA9665 as platform device"
|
||||
select I2C_ALGOPCA
|
||||
default n
|
||||
help
|
||||
This driver supports a memory mapped Philips PCA9564/PCA9665
|
||||
parallel bus to I2C bus controller.
|
||||
|
@ -1026,6 +1039,7 @@ config I2C_STM32F7
|
|||
tristate "STMicroelectronics STM32F7 I2C support"
|
||||
depends on ARCH_STM32 || COMPILE_TEST
|
||||
select I2C_SLAVE
|
||||
select I2C_SMBUS
|
||||
help
|
||||
Enable this option to add support for STM32 I2C controller embedded
|
||||
in STM32F7 SoCs.
|
||||
|
@ -1181,6 +1195,8 @@ config I2C_RCAR
|
|||
tristate "Renesas R-Car I2C Controller"
|
||||
depends on ARCH_RENESAS || COMPILE_TEST
|
||||
select I2C_SLAVE
|
||||
select I2C_SMBUS
|
||||
select RESET_CONTROLLER if ARCH_RCAR_GEN3
|
||||
help
|
||||
If you say yes to this option, support will be included for the
|
||||
R-Car I2C controller.
|
||||
|
@ -1240,7 +1256,6 @@ config I2C_TAOS_EVM
|
|||
depends on TTY
|
||||
select SERIO
|
||||
select SERIO_SERPORT
|
||||
default n
|
||||
help
|
||||
This supports TAOS evaluation modules on serial port. In order to
|
||||
use this driver, you will need the inputattach tool, which is part
|
||||
|
@ -1324,7 +1339,6 @@ config I2C_PCA_ISA
|
|||
tristate "PCA9564/PCA9665 on an ISA bus"
|
||||
depends on ISA
|
||||
select I2C_ALGOPCA
|
||||
default n
|
||||
help
|
||||
This driver supports ISA boards using the Philips PCA9564/PCA9665
|
||||
parallel bus to I2C bus controller.
|
||||
|
|
|
@ -140,6 +140,7 @@ obj-$(CONFIG_I2C_BRCMSTB) += i2c-brcmstb.o
|
|||
obj-$(CONFIG_I2C_CROS_EC_TUNNEL) += i2c-cros-ec-tunnel.o
|
||||
obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o
|
||||
obj-$(CONFIG_I2C_ICY) += i2c-icy.o
|
||||
obj-$(CONFIG_I2C_MLXBF) += i2c-mlxbf.o
|
||||
obj-$(CONFIG_I2C_MLXCPLD) += i2c-mlxcpld.o
|
||||
obj-$(CONFIG_I2C_OPAL) += i2c-opal.o
|
||||
obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o
|
||||
|
|
|
@ -155,7 +155,7 @@ static int i2c_amd_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
|||
struct amd_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
|
||||
int i;
|
||||
struct i2c_msg *pmsg;
|
||||
int err;
|
||||
int err = 0;
|
||||
|
||||
/* the adapter might have been deleted while waiting for the bus lock */
|
||||
if (unlikely(!i2c_dev->common.mp2_dev))
|
||||
|
|
|
@ -421,11 +421,9 @@ static int bcm2835_i2c_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(i2c_dev->regs);
|
||||
|
||||
mclk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(mclk)) {
|
||||
if (PTR_ERR(mclk) != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "Could not get clock\n");
|
||||
return PTR_ERR(mclk);
|
||||
}
|
||||
if (IS_ERR(mclk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(mclk),
|
||||
"Could not get clock\n");
|
||||
|
||||
i2c_dev->bus_clk = bcm2835_i2c_register_div(&pdev->dev, mclk, i2c_dev);
|
||||
|
||||
|
|
|
@ -332,21 +332,15 @@ static int efm32_i2c_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "failed to determine base address\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
ddata->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
||||
if (IS_ERR(ddata->base))
|
||||
return PTR_ERR(ddata->base);
|
||||
|
||||
if (resource_size(res) < 0x42) {
|
||||
dev_err(&pdev->dev, "memory resource too small\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ddata->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(ddata->base))
|
||||
return PTR_ERR(ddata->base);
|
||||
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret <= 0) {
|
||||
if (!ret)
|
||||
|
|
|
@ -71,6 +71,7 @@
|
|||
* Tiger Lake-H (PCH) 0x43a3 32 hard yes yes yes
|
||||
* Jasper Lake (SOC) 0x4da3 32 hard yes yes yes
|
||||
* Comet Lake-V (PCH) 0xa3a3 32 hard yes yes yes
|
||||
* Alder Lake-S (PCH) 0x7aa3 32 hard yes yes yes
|
||||
*
|
||||
* Features supported by this driver:
|
||||
* Software PEC no
|
||||
|
@ -228,6 +229,7 @@
|
|||
#define PCI_DEVICE_ID_INTEL_ELKHART_LAKE_SMBUS 0x4b23
|
||||
#define PCI_DEVICE_ID_INTEL_JASPER_LAKE_SMBUS 0x4da3
|
||||
#define PCI_DEVICE_ID_INTEL_BROXTON_SMBUS 0x5ad4
|
||||
#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_S_SMBUS 0x7aa3
|
||||
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22
|
||||
#define PCI_DEVICE_ID_INTEL_WILDCATPOINT_SMBUS 0x8ca2
|
||||
#define PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS 0x8d22
|
||||
|
@ -1081,6 +1083,7 @@ static const struct pci_device_id i801_ids[] = {
|
|||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TIGERLAKE_LP_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TIGERLAKE_H_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_JASPER_LAKE_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ALDER_LAKE_S_SMBUS) },
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
|
@ -1274,6 +1277,7 @@ static const struct {
|
|||
/*
|
||||
* Additional individual entries were added after verification.
|
||||
*/
|
||||
{ "Latitude 5480", 0x29 },
|
||||
{ "Vostro V131", 0x1d },
|
||||
};
|
||||
|
||||
|
@ -1767,6 +1771,7 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|||
case PCI_DEVICE_ID_INTEL_TIGERLAKE_H_SMBUS:
|
||||
case PCI_DEVICE_ID_INTEL_JASPER_LAKE_SMBUS:
|
||||
case PCI_DEVICE_ID_INTEL_EBG_SMBUS:
|
||||
case PCI_DEVICE_ID_INTEL_ALDER_LAKE_S_SMBUS:
|
||||
priv->features |= FEATURE_BLOCK_PROC;
|
||||
priv->features |= FEATURE_I2C_BLOCK_READ;
|
||||
priv->features |= FEATURE_IRQ;
|
||||
|
|
|
@ -1159,11 +1159,9 @@ static int i2c_imx_probe(struct platform_device *pdev)
|
|||
|
||||
/* Get I2C clock */
|
||||
i2c_imx->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(i2c_imx->clk)) {
|
||||
if (PTR_ERR(i2c_imx->clk) != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "can't get I2C clock\n");
|
||||
return PTR_ERR(i2c_imx->clk);
|
||||
}
|
||||
if (IS_ERR(i2c_imx->clk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(i2c_imx->clk),
|
||||
"can't get I2C clock\n");
|
||||
|
||||
ret = clk_prepare_enable(i2c_imx->clk);
|
||||
if (ret) {
|
||||
|
@ -1171,14 +1169,6 @@ static int i2c_imx_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Request IRQ */
|
||||
ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr, IRQF_SHARED,
|
||||
pdev->name, i2c_imx);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "can't claim irq %d\n", irq);
|
||||
goto clk_disable;
|
||||
}
|
||||
|
||||
/* Init queue */
|
||||
init_waitqueue_head(&i2c_imx->queue);
|
||||
|
||||
|
@ -1197,6 +1187,14 @@ static int i2c_imx_probe(struct platform_device *pdev)
|
|||
if (ret < 0)
|
||||
goto rpm_disable;
|
||||
|
||||
/* Request IRQ */
|
||||
ret = request_threaded_irq(irq, i2c_imx_isr, NULL, IRQF_SHARED,
|
||||
pdev->name, i2c_imx);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "can't claim irq %d\n", irq);
|
||||
goto rpm_disable;
|
||||
}
|
||||
|
||||
/* Set up clock divider */
|
||||
i2c_imx->bitrate = I2C_MAX_STANDARD_MODE_FREQ;
|
||||
ret = of_property_read_u32(pdev->dev.of_node,
|
||||
|
@ -1239,13 +1237,12 @@ static int i2c_imx_probe(struct platform_device *pdev)
|
|||
|
||||
clk_notifier_unregister:
|
||||
clk_notifier_unregister(i2c_imx->clk, &i2c_imx->clk_change_nb);
|
||||
free_irq(irq, i2c_imx);
|
||||
rpm_disable:
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_set_suspended(&pdev->dev);
|
||||
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
||||
|
||||
clk_disable:
|
||||
clk_disable_unprepare(i2c_imx->clk);
|
||||
return ret;
|
||||
}
|
||||
|
@ -1253,7 +1250,7 @@ clk_disable:
|
|||
static int i2c_imx_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct imx_i2c_struct *i2c_imx = platform_get_drvdata(pdev);
|
||||
int ret;
|
||||
int irq, ret;
|
||||
|
||||
ret = pm_runtime_get_sync(&pdev->dev);
|
||||
if (ret < 0)
|
||||
|
@ -1273,6 +1270,9 @@ static int i2c_imx_remove(struct platform_device *pdev)
|
|||
imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2SR);
|
||||
|
||||
clk_notifier_unregister(i2c_imx->clk, &i2c_imx->clk_change_nb);
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq >= 0)
|
||||
free_irq(irq, i2c_imx);
|
||||
clk_disable_unprepare(i2c_imx->clk);
|
||||
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
|
|
|
@ -77,6 +77,7 @@
|
|||
#define PCI_DEVICE_ID_INTEL_S1200_SMT1 0x0c5a
|
||||
#define PCI_DEVICE_ID_INTEL_CDF_SMT 0x18ac
|
||||
#define PCI_DEVICE_ID_INTEL_DNV_SMT 0x19ac
|
||||
#define PCI_DEVICE_ID_INTEL_EBG_SMT 0x1bff
|
||||
#define PCI_DEVICE_ID_INTEL_AVOTON_SMT 0x1f15
|
||||
|
||||
#define ISMT_DESC_ENTRIES 2 /* number of descriptor entries */
|
||||
|
@ -176,14 +177,12 @@ struct ismt_priv {
|
|||
u8 buffer[I2C_SMBUS_BLOCK_MAX + 16]; /* temp R/W data buffer */
|
||||
};
|
||||
|
||||
/**
|
||||
* ismt_ids - PCI device IDs supported by this driver
|
||||
*/
|
||||
static const struct pci_device_id ismt_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_S1200_SMT0) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_S1200_SMT1) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CDF_SMT) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_DNV_SMT) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EBG_SMT) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_AVOTON_SMT) },
|
||||
{ 0, }
|
||||
};
|
||||
|
@ -197,6 +196,8 @@ MODULE_PARM_DESC(bus_speed, "Bus Speed in kHz (0 = BIOS default)");
|
|||
|
||||
/**
|
||||
* __ismt_desc_dump() - dump the contents of a specific descriptor
|
||||
* @dev: the iSMT device
|
||||
* @desc: the iSMT hardware descriptor
|
||||
*/
|
||||
static void __ismt_desc_dump(struct device *dev, const struct ismt_desc *desc)
|
||||
{
|
||||
|
@ -628,11 +629,6 @@ static u32 ismt_func(struct i2c_adapter *adap)
|
|||
I2C_FUNC_SMBUS_PEC;
|
||||
}
|
||||
|
||||
/**
|
||||
* smbus_algorithm - the adapter algorithm and supported functionality
|
||||
* @smbus_xfer: the adapter algorithm
|
||||
* @functionality: functionality supported by the adapter
|
||||
*/
|
||||
static const struct i2c_algorithm smbus_algorithm = {
|
||||
.smbus_xfer = ismt_access,
|
||||
.functionality = ismt_func,
|
||||
|
|
|
@ -752,6 +752,7 @@ static const struct ingenic_i2c_config x1000_i2c_config = {
|
|||
};
|
||||
|
||||
static const struct of_device_id jz4780_i2c_of_matches[] = {
|
||||
{ .compatible = "ingenic,jz4770-i2c", .data = &jz4780_i2c_config },
|
||||
{ .compatible = "ingenic,jz4780-i2c", .data = &jz4780_i2c_config },
|
||||
{ .compatible = "ingenic,x1000-i2c", .data = &x1000_i2c_config },
|
||||
{ /* sentinel */ }
|
||||
|
@ -856,7 +857,7 @@ static struct platform_driver jz4780_i2c_driver = {
|
|||
.remove = jz4780_i2c_remove,
|
||||
.driver = {
|
||||
.name = "jz4780-i2c",
|
||||
.of_match_table = of_match_ptr(jz4780_i2c_of_matches),
|
||||
.of_match_table = jz4780_i2c_of_matches,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -496,11 +496,10 @@ static irqreturn_t
|
|||
mv64xxx_i2c_intr(int irq, void *dev_id)
|
||||
{
|
||||
struct mv64xxx_i2c_data *drv_data = dev_id;
|
||||
unsigned long flags;
|
||||
u32 status;
|
||||
irqreturn_t rc = IRQ_NONE;
|
||||
|
||||
spin_lock_irqsave(&drv_data->lock, flags);
|
||||
spin_lock(&drv_data->lock);
|
||||
|
||||
if (drv_data->offload_enabled)
|
||||
rc = mv64xxx_i2c_intr_offload(drv_data);
|
||||
|
@ -517,7 +516,7 @@ mv64xxx_i2c_intr(int irq, void *dev_id)
|
|||
|
||||
rc = IRQ_HANDLED;
|
||||
}
|
||||
spin_unlock_irqrestore(&drv_data->lock, flags);
|
||||
spin_unlock(&drv_data->lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -125,8 +125,7 @@ static int gpu_i2c_read(struct gpu_i2c_dev *i2cd, u8 *data, u16 len)
|
|||
put_unaligned_be16(val, data);
|
||||
break;
|
||||
case 3:
|
||||
put_unaligned_be16(val >> 8, data);
|
||||
data[2] = val;
|
||||
put_unaligned_be24(val, data);
|
||||
break;
|
||||
case 4:
|
||||
put_unaligned_be32(val, data);
|
||||
|
|
|
@ -165,10 +165,9 @@ static irqreturn_t owl_i2c_interrupt(int irq, void *_dev)
|
|||
{
|
||||
struct owl_i2c_dev *i2c_dev = _dev;
|
||||
struct i2c_msg *msg = i2c_dev->msg;
|
||||
unsigned long flags;
|
||||
unsigned int stat, fifostat;
|
||||
|
||||
spin_lock_irqsave(&i2c_dev->lock, flags);
|
||||
spin_lock(&i2c_dev->lock);
|
||||
|
||||
i2c_dev->err = 0;
|
||||
|
||||
|
@ -214,7 +213,7 @@ stop:
|
|||
OWL_I2C_STAT_IRQP, true);
|
||||
|
||||
complete_all(&i2c_dev->msg_complete);
|
||||
spin_unlock_irqrestore(&i2c_dev->lock, flags);
|
||||
spin_unlock(&i2c_dev->lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
|
|
@ -210,9 +210,8 @@ static irqreturn_t geni_i2c_irq(int irq, void *dev)
|
|||
u32 dma;
|
||||
u32 val;
|
||||
struct i2c_msg *cur;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&gi2c->lock, flags);
|
||||
spin_lock(&gi2c->lock);
|
||||
m_stat = readl_relaxed(base + SE_GENI_M_IRQ_STATUS);
|
||||
rx_st = readl_relaxed(base + SE_GENI_RX_FIFO_STATUS);
|
||||
dm_tx_st = readl_relaxed(base + SE_DMA_TX_IRQ_STAT);
|
||||
|
@ -294,7 +293,7 @@ static irqreturn_t geni_i2c_irq(int irq, void *dev)
|
|||
dm_rx_st & RX_DMA_DONE || dm_rx_st & RX_RESET_DONE)
|
||||
complete(&gi2c->done);
|
||||
|
||||
spin_unlock_irqrestore(&gi2c->lock, flags);
|
||||
spin_unlock(&gi2c->lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
|
|
@ -19,7 +19,9 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-smbus.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
|
@ -105,10 +107,11 @@
|
|||
#define ID_ARBLOST (1 << 3)
|
||||
#define ID_NACK (1 << 4)
|
||||
/* persistent flags */
|
||||
#define ID_P_HOST_NOTIFY BIT(28)
|
||||
#define ID_P_REP_AFTER_RD BIT(29)
|
||||
#define ID_P_NO_RXDMA BIT(30) /* HW forbids RXDMA sometimes */
|
||||
#define ID_P_PM_BLOCKED BIT(31)
|
||||
#define ID_P_MASK GENMASK(31, 29)
|
||||
#define ID_P_MASK GENMASK(31, 28)
|
||||
|
||||
enum rcar_i2c_type {
|
||||
I2C_RCAR_GEN1,
|
||||
|
@ -140,14 +143,13 @@ struct rcar_i2c_priv {
|
|||
|
||||
struct reset_control *rstc;
|
||||
int irq;
|
||||
|
||||
struct i2c_client *host_notify_client;
|
||||
};
|
||||
|
||||
#define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent)
|
||||
#define rcar_i2c_is_recv(p) ((p)->msg->flags & I2C_M_RD)
|
||||
|
||||
#define LOOP_TIMEOUT 1024
|
||||
|
||||
|
||||
static void rcar_i2c_write(struct rcar_i2c_priv *priv, int reg, u32 val)
|
||||
{
|
||||
writel(val, priv->io + reg);
|
||||
|
@ -221,18 +223,18 @@ static void rcar_i2c_init(struct rcar_i2c_priv *priv)
|
|||
|
||||
static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
for (i = 0; i < LOOP_TIMEOUT; i++) {
|
||||
/* make sure that bus is not busy */
|
||||
if (!(rcar_i2c_read(priv, ICMCR) & FSDA))
|
||||
return 0;
|
||||
udelay(1);
|
||||
ret = readl_poll_timeout(priv->io + ICMCR, val, !(val & FSDA), 10,
|
||||
priv->adap.timeout);
|
||||
if (ret) {
|
||||
/* Waiting did not help, try to recover */
|
||||
priv->recovery_icmcr = MDBS | OBPC | FSDA | FSCL;
|
||||
ret = i2c_recover_bus(&priv->adap);
|
||||
}
|
||||
|
||||
/* Waiting did not help, try to recover */
|
||||
priv->recovery_icmcr = MDBS | OBPC | FSDA | FSCL;
|
||||
return i2c_recover_bus(&priv->adap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv)
|
||||
|
@ -760,20 +762,14 @@ static void rcar_i2c_release_dma(struct rcar_i2c_priv *priv)
|
|||
/* I2C is a special case, we need to poll the status of a reset */
|
||||
static int rcar_i2c_do_reset(struct rcar_i2c_priv *priv)
|
||||
{
|
||||
int i, ret;
|
||||
int ret;
|
||||
|
||||
ret = reset_control_reset(priv->rstc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < LOOP_TIMEOUT; i++) {
|
||||
ret = reset_control_status(priv->rstc);
|
||||
if (ret == 0)
|
||||
return 0;
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
return -ETIMEDOUT;
|
||||
return read_poll_timeout_atomic(reset_control_status, ret, ret == 0, 1,
|
||||
100, false, priv->rstc);
|
||||
}
|
||||
|
||||
static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
|
||||
|
@ -884,14 +880,21 @@ static int rcar_unreg_slave(struct i2c_client *slave)
|
|||
|
||||
static u32 rcar_i2c_func(struct i2c_adapter *adap)
|
||||
{
|
||||
struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
|
||||
|
||||
/*
|
||||
* This HW can't do:
|
||||
* I2C_SMBUS_QUICK (setting FSB during START didn't work)
|
||||
* I2C_M_NOSTART (automatically sends address after START)
|
||||
* I2C_M_IGNORE_NAK (automatically sends STOP after NAK)
|
||||
*/
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SLAVE |
|
||||
(I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
|
||||
u32 func = I2C_FUNC_I2C | I2C_FUNC_SLAVE |
|
||||
(I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
|
||||
|
||||
if (priv->flags & ID_P_HOST_NOTIFY)
|
||||
func |= I2C_FUNC_SMBUS_HOST_NOTIFY;
|
||||
|
||||
return func;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm rcar_i2c_algo = {
|
||||
|
@ -991,6 +994,8 @@ static int rcar_i2c_probe(struct platform_device *pdev)
|
|||
else
|
||||
pm_runtime_put(dev);
|
||||
|
||||
if (of_property_read_bool(dev->of_node, "smbus"))
|
||||
priv->flags |= ID_P_HOST_NOTIFY;
|
||||
|
||||
priv->irq = platform_get_irq(pdev, 0);
|
||||
ret = devm_request_irq(dev, priv->irq, rcar_i2c_irq, 0, dev_name(dev), priv);
|
||||
|
@ -1005,10 +1010,20 @@ static int rcar_i2c_probe(struct platform_device *pdev)
|
|||
if (ret < 0)
|
||||
goto out_pm_disable;
|
||||
|
||||
if (priv->flags & ID_P_HOST_NOTIFY) {
|
||||
priv->host_notify_client = i2c_new_slave_host_notify_device(adap);
|
||||
if (IS_ERR(priv->host_notify_client)) {
|
||||
ret = PTR_ERR(priv->host_notify_client);
|
||||
goto out_del_device;
|
||||
}
|
||||
}
|
||||
|
||||
dev_info(dev, "probed\n");
|
||||
|
||||
return 0;
|
||||
|
||||
out_del_device:
|
||||
i2c_del_adapter(&priv->adap);
|
||||
out_pm_put:
|
||||
pm_runtime_put(dev);
|
||||
out_pm_disable:
|
||||
|
@ -1021,6 +1036,8 @@ static int rcar_i2c_remove(struct platform_device *pdev)
|
|||
struct rcar_i2c_priv *priv = platform_get_drvdata(pdev);
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
if (priv->host_notify_client)
|
||||
i2c_free_slave_host_notify_device(priv->host_notify_client);
|
||||
i2c_del_adapter(&priv->adap);
|
||||
rcar_i2c_release_dma(priv);
|
||||
if (priv->flags & ID_P_PM_BLOCKED)
|
||||
|
|
|
@ -1312,18 +1312,13 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
|
|||
i2c->pclk = devm_clk_get(&pdev->dev, "pclk");
|
||||
}
|
||||
|
||||
if (IS_ERR(i2c->clk)) {
|
||||
ret = PTR_ERR(i2c->clk);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "Can't get bus clk: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
if (IS_ERR(i2c->pclk)) {
|
||||
ret = PTR_ERR(i2c->pclk);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "Can't get periph clk: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
if (IS_ERR(i2c->clk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(i2c->clk),
|
||||
"Can't get bus clk\n");
|
||||
|
||||
if (IS_ERR(i2c->pclk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(i2c->pclk),
|
||||
"Can't get periph clk\n");
|
||||
|
||||
ret = clk_prepare(i2c->clk);
|
||||
if (ret < 0) {
|
||||
|
|
|
@ -26,8 +26,9 @@ struct stm32_i2c_dma *stm32_i2c_dma_request(struct device *dev,
|
|||
dma->chan_tx = dma_request_chan(dev, "tx");
|
||||
if (IS_ERR(dma->chan_tx)) {
|
||||
ret = PTR_ERR(dma->chan_tx);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "can't request DMA tx channel\n");
|
||||
if (ret != -ENODEV)
|
||||
ret = dev_err_probe(dev, ret,
|
||||
"can't request DMA tx channel\n");
|
||||
goto fail_al;
|
||||
}
|
||||
|
||||
|
@ -46,8 +47,9 @@ struct stm32_i2c_dma *stm32_i2c_dma_request(struct device *dev,
|
|||
dma->chan_rx = dma_request_chan(dev, "rx");
|
||||
if (IS_ERR(dma->chan_rx)) {
|
||||
ret = PTR_ERR(dma->chan_rx);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "can't request DMA rx channel\n");
|
||||
if (ret != -ENODEV)
|
||||
ret = dev_err_probe(dev, ret,
|
||||
"can't request DMA rx channel\n");
|
||||
|
||||
goto fail_tx;
|
||||
}
|
||||
|
@ -76,8 +78,6 @@ fail_tx:
|
|||
dma_release_channel(dma->chan_tx);
|
||||
fail_al:
|
||||
devm_kfree(dev, dma);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_info(dev, "can't use DMA\n");
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
|
|
@ -797,10 +797,8 @@ static int stm32f4_i2c_probe(struct platform_device *pdev)
|
|||
|
||||
rst = devm_reset_control_get_exclusive(&pdev->dev, NULL);
|
||||
if (IS_ERR(rst)) {
|
||||
ret = PTR_ERR(rst);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "Error: Missing reset ctrl\n");
|
||||
|
||||
ret = dev_err_probe(&pdev->dev, PTR_ERR(rst),
|
||||
"Error: Missing reset ctrl\n");
|
||||
goto clk_free;
|
||||
}
|
||||
reset_control_assert(rst);
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-smbus.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
|
@ -50,6 +51,7 @@
|
|||
|
||||
/* STM32F7 I2C control 1 */
|
||||
#define STM32F7_I2C_CR1_PECEN BIT(23)
|
||||
#define STM32F7_I2C_CR1_SMBHEN BIT(20)
|
||||
#define STM32F7_I2C_CR1_WUPEN BIT(18)
|
||||
#define STM32F7_I2C_CR1_SBC BIT(16)
|
||||
#define STM32F7_I2C_CR1_RXDMAEN BIT(15)
|
||||
|
@ -150,7 +152,12 @@
|
|||
|
||||
#define STM32F7_I2C_MAX_LEN 0xff
|
||||
#define STM32F7_I2C_DMA_LEN_MIN 0x16
|
||||
#define STM32F7_I2C_MAX_SLAVE 0x2
|
||||
enum {
|
||||
STM32F7_SLAVE_HOSTNOTIFY,
|
||||
STM32F7_SLAVE_7_10_BITS_ADDR,
|
||||
STM32F7_SLAVE_7_BITS_ADDR,
|
||||
STM32F7_I2C_MAX_SLAVE
|
||||
};
|
||||
|
||||
#define STM32F7_I2C_DNF_DEFAULT 0
|
||||
#define STM32F7_I2C_DNF_MAX 16
|
||||
|
@ -301,6 +308,8 @@ struct stm32f7_i2c_msg {
|
|||
* @fmp_creg: register address for clearing Fast Mode Plus bits
|
||||
* @fmp_mask: mask for Fast Mode Plus bits in set register
|
||||
* @wakeup_src: boolean to know if the device is a wakeup source
|
||||
* @smbus_mode: states that the controller is configured in SMBus mode
|
||||
* @host_notify_client: SMBus host-notify client
|
||||
*/
|
||||
struct stm32f7_i2c_dev {
|
||||
struct i2c_adapter adap;
|
||||
|
@ -327,6 +336,8 @@ struct stm32f7_i2c_dev {
|
|||
u32 fmp_creg;
|
||||
u32 fmp_mask;
|
||||
bool wakeup_src;
|
||||
bool smbus_mode;
|
||||
struct i2c_client *host_notify_client;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -1321,11 +1332,20 @@ static int stm32f7_i2c_get_free_slave_id(struct stm32f7_i2c_dev *i2c_dev,
|
|||
int i;
|
||||
|
||||
/*
|
||||
* slave[0] supports 7-bit and 10-bit slave address
|
||||
* slave[1] supports 7-bit slave address only
|
||||
* slave[STM32F7_SLAVE_HOSTNOTIFY] support only SMBus Host address (0x8)
|
||||
* slave[STM32F7_SLAVE_7_10_BITS_ADDR] supports 7-bit and 10-bit slave address
|
||||
* slave[STM32F7_SLAVE_7_BITS_ADDR] supports 7-bit slave address only
|
||||
*/
|
||||
for (i = STM32F7_I2C_MAX_SLAVE - 1; i >= 0; i--) {
|
||||
if (i == 1 && (slave->flags & I2C_CLIENT_TEN))
|
||||
if (i2c_dev->smbus_mode && (slave->addr == 0x08)) {
|
||||
if (i2c_dev->slave[STM32F7_SLAVE_HOSTNOTIFY])
|
||||
goto fail;
|
||||
*id = STM32F7_SLAVE_HOSTNOTIFY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = STM32F7_I2C_MAX_SLAVE - 1; i > STM32F7_SLAVE_HOSTNOTIFY; i--) {
|
||||
if ((i == STM32F7_SLAVE_7_BITS_ADDR) &&
|
||||
(slave->flags & I2C_CLIENT_TEN))
|
||||
continue;
|
||||
if (!i2c_dev->slave[i]) {
|
||||
*id = i;
|
||||
|
@ -1333,6 +1353,7 @@ static int stm32f7_i2c_get_free_slave_id(struct stm32f7_i2c_dev *i2c_dev,
|
|||
}
|
||||
}
|
||||
|
||||
fail:
|
||||
dev_err(dev, "Slave 0x%x could not be registered\n", slave->addr);
|
||||
|
||||
return -EINVAL;
|
||||
|
@ -1776,7 +1797,13 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave)
|
|||
if (!stm32f7_i2c_is_slave_registered(i2c_dev))
|
||||
stm32f7_i2c_enable_wakeup(i2c_dev, true);
|
||||
|
||||
if (id == 0) {
|
||||
switch (id) {
|
||||
case 0:
|
||||
/* Slave SMBus Host */
|
||||
i2c_dev->slave[id] = slave;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
/* Configure Own Address 1 */
|
||||
oar1 = readl_relaxed(i2c_dev->base + STM32F7_I2C_OAR1);
|
||||
oar1 &= ~STM32F7_I2C_OAR1_MASK;
|
||||
|
@ -1789,7 +1816,9 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave)
|
|||
oar1 |= STM32F7_I2C_OAR1_OA1EN;
|
||||
i2c_dev->slave[id] = slave;
|
||||
writel_relaxed(oar1, i2c_dev->base + STM32F7_I2C_OAR1);
|
||||
} else if (id == 1) {
|
||||
break;
|
||||
|
||||
case 2:
|
||||
/* Configure Own Address 2 */
|
||||
oar2 = readl_relaxed(i2c_dev->base + STM32F7_I2C_OAR2);
|
||||
oar2 &= ~STM32F7_I2C_OAR2_MASK;
|
||||
|
@ -1802,7 +1831,10 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave)
|
|||
oar2 |= STM32F7_I2C_OAR2_OA2EN;
|
||||
i2c_dev->slave[id] = slave;
|
||||
writel_relaxed(oar2, i2c_dev->base + STM32F7_I2C_OAR2);
|
||||
} else {
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(dev, "I2C slave id not supported\n");
|
||||
ret = -ENODEV;
|
||||
goto pm_free;
|
||||
}
|
||||
|
@ -1843,10 +1875,10 @@ static int stm32f7_i2c_unreg_slave(struct i2c_client *slave)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (id == 0) {
|
||||
if (id == 1) {
|
||||
mask = STM32F7_I2C_OAR1_OA1EN;
|
||||
stm32f7_i2c_clr_bits(base + STM32F7_I2C_OAR1, mask);
|
||||
} else {
|
||||
} else if (id == 2) {
|
||||
mask = STM32F7_I2C_OAR2_OA2EN;
|
||||
stm32f7_i2c_clr_bits(base + STM32F7_I2C_OAR2, mask);
|
||||
}
|
||||
|
@ -1911,14 +1943,51 @@ static int stm32f7_i2c_setup_fm_plus_bits(struct platform_device *pdev,
|
|||
&i2c_dev->fmp_mask);
|
||||
}
|
||||
|
||||
static int stm32f7_i2c_enable_smbus_host(struct stm32f7_i2c_dev *i2c_dev)
|
||||
{
|
||||
struct i2c_adapter *adap = &i2c_dev->adap;
|
||||
void __iomem *base = i2c_dev->base;
|
||||
struct i2c_client *client;
|
||||
|
||||
client = i2c_new_slave_host_notify_device(adap);
|
||||
if (IS_ERR(client))
|
||||
return PTR_ERR(client);
|
||||
|
||||
i2c_dev->host_notify_client = client;
|
||||
|
||||
/* Enable SMBus Host address */
|
||||
stm32f7_i2c_set_bits(base + STM32F7_I2C_CR1, STM32F7_I2C_CR1_SMBHEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stm32f7_i2c_disable_smbus_host(struct stm32f7_i2c_dev *i2c_dev)
|
||||
{
|
||||
void __iomem *base = i2c_dev->base;
|
||||
|
||||
if (i2c_dev->host_notify_client) {
|
||||
/* Disable SMBus Host address */
|
||||
stm32f7_i2c_clr_bits(base + STM32F7_I2C_CR1,
|
||||
STM32F7_I2C_CR1_SMBHEN);
|
||||
i2c_free_slave_host_notify_device(i2c_dev->host_notify_client);
|
||||
}
|
||||
}
|
||||
|
||||
static u32 stm32f7_i2c_func(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SLAVE |
|
||||
I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
|
||||
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
|
||||
I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
|
||||
I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_PEC |
|
||||
I2C_FUNC_SMBUS_I2C_BLOCK;
|
||||
struct stm32f7_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
|
||||
|
||||
u32 func = I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SLAVE |
|
||||
I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
|
||||
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
|
||||
I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
|
||||
I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_PEC |
|
||||
I2C_FUNC_SMBUS_I2C_BLOCK;
|
||||
|
||||
if (i2c_dev->smbus_mode)
|
||||
func |= I2C_FUNC_SMBUS_HOST_NOTIFY;
|
||||
|
||||
return func;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm stm32f7_i2c_algo = {
|
||||
|
@ -1968,11 +2037,9 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
|
|||
"wakeup-source");
|
||||
|
||||
i2c_dev->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(i2c_dev->clk)) {
|
||||
if (PTR_ERR(i2c_dev->clk) != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "Failed to get controller clock\n");
|
||||
return PTR_ERR(i2c_dev->clk);
|
||||
}
|
||||
if (IS_ERR(i2c_dev->clk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(i2c_dev->clk),
|
||||
"Failed to get controller clock\n");
|
||||
|
||||
ret = clk_prepare_enable(i2c_dev->clk);
|
||||
if (ret) {
|
||||
|
@ -1982,10 +2049,8 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
|
|||
|
||||
rst = devm_reset_control_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(rst)) {
|
||||
ret = PTR_ERR(rst);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "Error: Missing reset ctrl\n");
|
||||
|
||||
ret = dev_err_probe(&pdev->dev, PTR_ERR(rst),
|
||||
"Error: Missing reset ctrl\n");
|
||||
goto clk_free;
|
||||
}
|
||||
reset_control_assert(rst);
|
||||
|
@ -2052,14 +2117,13 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
|
|||
i2c_dev->dma = stm32_i2c_dma_request(i2c_dev->dev, phy_addr,
|
||||
STM32F7_I2C_TXDR,
|
||||
STM32F7_I2C_RXDR);
|
||||
if (PTR_ERR(i2c_dev->dma) == -ENODEV)
|
||||
i2c_dev->dma = NULL;
|
||||
else if (IS_ERR(i2c_dev->dma)) {
|
||||
if (IS_ERR(i2c_dev->dma)) {
|
||||
ret = PTR_ERR(i2c_dev->dma);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to request dma error %i\n", ret);
|
||||
goto fmp_clear;
|
||||
/* DMA support is optional, only report other errors */
|
||||
if (ret != -ENODEV)
|
||||
goto fmp_clear;
|
||||
dev_dbg(i2c_dev->dev, "No DMA option: fallback using interrupts\n");
|
||||
i2c_dev->dma = NULL;
|
||||
}
|
||||
|
||||
if (i2c_dev->wakeup_src) {
|
||||
|
@ -2084,10 +2148,22 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
|
|||
|
||||
stm32f7_i2c_hw_config(i2c_dev);
|
||||
|
||||
i2c_dev->smbus_mode = of_property_read_bool(pdev->dev.of_node, "smbus");
|
||||
|
||||
ret = i2c_add_adapter(adap);
|
||||
if (ret)
|
||||
goto pm_disable;
|
||||
|
||||
if (i2c_dev->smbus_mode) {
|
||||
ret = stm32f7_i2c_enable_smbus_host(i2c_dev);
|
||||
if (ret) {
|
||||
dev_err(i2c_dev->dev,
|
||||
"failed to enable SMBus Host-Notify protocol (%d)\n",
|
||||
ret);
|
||||
goto i2c_adapter_remove;
|
||||
}
|
||||
}
|
||||
|
||||
dev_info(i2c_dev->dev, "STM32F7 I2C-%d bus adapter\n", adap->nr);
|
||||
|
||||
pm_runtime_mark_last_busy(i2c_dev->dev);
|
||||
|
@ -2095,6 +2171,9 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
|
|||
|
||||
return 0;
|
||||
|
||||
i2c_adapter_remove:
|
||||
i2c_del_adapter(adap);
|
||||
|
||||
pm_disable:
|
||||
pm_runtime_put_noidle(i2c_dev->dev);
|
||||
pm_runtime_disable(i2c_dev->dev);
|
||||
|
@ -2126,6 +2205,8 @@ static int stm32f7_i2c_remove(struct platform_device *pdev)
|
|||
{
|
||||
struct stm32f7_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
|
||||
|
||||
stm32f7_i2c_disable_smbus_host(i2c_dev);
|
||||
|
||||
i2c_del_adapter(&i2c_dev->adap);
|
||||
pm_runtime_get_sync(i2c_dev->dev);
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -46,34 +46,36 @@ enum xiic_endian {
|
|||
|
||||
/**
|
||||
* struct xiic_i2c - Internal representation of the XIIC I2C bus
|
||||
* @dev: Pointer to device structure
|
||||
* @base: Memory base of the HW registers
|
||||
* @wait: Wait queue for callers
|
||||
* @adap: Kernel adapter representation
|
||||
* @tx_msg: Messages from above to be sent
|
||||
* @lock: Mutual exclusion
|
||||
* @tx_pos: Current pos in TX message
|
||||
* @nmsgs: Number of messages in tx_msg
|
||||
* @state: See STATE_
|
||||
* @rx_msg: Current RX message
|
||||
* @rx_pos: Position within current RX message
|
||||
* @dev: Pointer to device structure
|
||||
* @base: Memory base of the HW registers
|
||||
* @wait: Wait queue for callers
|
||||
* @adap: Kernel adapter representation
|
||||
* @tx_msg: Messages from above to be sent
|
||||
* @lock: Mutual exclusion
|
||||
* @tx_pos: Current pos in TX message
|
||||
* @nmsgs: Number of messages in tx_msg
|
||||
* @rx_msg: Current RX message
|
||||
* @rx_pos: Position within current RX message
|
||||
* @endianness: big/little-endian byte order
|
||||
* @clk: Pointer to AXI4-lite input clock
|
||||
* @clk: Pointer to AXI4-lite input clock
|
||||
* @state: See STATE_
|
||||
* @singlemaster: Indicates bus is single master
|
||||
*/
|
||||
struct xiic_i2c {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
wait_queue_head_t wait;
|
||||
struct i2c_adapter adap;
|
||||
struct i2c_msg *tx_msg;
|
||||
struct mutex lock;
|
||||
unsigned int tx_pos;
|
||||
unsigned int nmsgs;
|
||||
enum xilinx_i2c_state state;
|
||||
struct i2c_msg *rx_msg;
|
||||
int rx_pos;
|
||||
enum xiic_endian endianness;
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
wait_queue_head_t wait;
|
||||
struct i2c_adapter adap;
|
||||
struct i2c_msg *tx_msg;
|
||||
struct mutex lock;
|
||||
unsigned int tx_pos;
|
||||
unsigned int nmsgs;
|
||||
struct i2c_msg *rx_msg;
|
||||
int rx_pos;
|
||||
enum xiic_endian endianness;
|
||||
struct clk *clk;
|
||||
enum xilinx_i2c_state state;
|
||||
bool singlemaster;
|
||||
};
|
||||
|
||||
|
||||
|
@ -526,6 +528,15 @@ static int xiic_busy(struct xiic_i2c *i2c)
|
|||
if (i2c->tx_msg)
|
||||
return -EBUSY;
|
||||
|
||||
/* In single master mode bus can only be busy, when in use by this
|
||||
* driver. If the register indicates bus being busy for some reason we
|
||||
* should ignore it, since bus will never be released and i2c will be
|
||||
* stuck forever.
|
||||
*/
|
||||
if (i2c->singlemaster) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* for instance if previous transfer was terminated due to TX error
|
||||
* it might be that the bus is on it's way to become available
|
||||
* give it at most 3 ms to wake
|
||||
|
@ -811,6 +822,9 @@ static int xiic_i2c_probe(struct platform_device *pdev)
|
|||
goto err_clk_dis;
|
||||
}
|
||||
|
||||
i2c->singlemaster =
|
||||
of_property_read_bool(pdev->dev.of_node, "single-master");
|
||||
|
||||
/*
|
||||
* Detect endianness
|
||||
* Try to reset the TX FIFO. Then check the EMPTY flag. If it is not
|
||||
|
|
|
@ -0,0 +1,175 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* I2C slave mode testunit
|
||||
*
|
||||
* Copyright (C) 2020 by Wolfram Sang, Sang Engineering <wsa@sang-engineering.com>
|
||||
* Copyright (C) 2020 by Renesas Electronics Corporation
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/workqueue.h> /* FIXME: is system_long_wq the best choice? */
|
||||
|
||||
#define TU_CUR_VERSION 0x01
|
||||
|
||||
enum testunit_cmds {
|
||||
TU_CMD_READ_BYTES = 1, /* save 0 for ABORT, RESET or similar */
|
||||
TU_CMD_HOST_NOTIFY,
|
||||
TU_NUM_CMDS
|
||||
};
|
||||
|
||||
enum testunit_regs {
|
||||
TU_REG_CMD,
|
||||
TU_REG_DATAL,
|
||||
TU_REG_DATAH,
|
||||
TU_REG_DELAY,
|
||||
TU_NUM_REGS
|
||||
};
|
||||
|
||||
enum testunit_flags {
|
||||
TU_FLAG_IN_PROCESS,
|
||||
};
|
||||
|
||||
struct testunit_data {
|
||||
unsigned long flags;
|
||||
u8 regs[TU_NUM_REGS];
|
||||
u8 reg_idx;
|
||||
struct i2c_client *client;
|
||||
struct delayed_work worker;
|
||||
};
|
||||
|
||||
static void i2c_slave_testunit_work(struct work_struct *work)
|
||||
{
|
||||
struct testunit_data *tu = container_of(work, struct testunit_data, worker.work);
|
||||
struct i2c_msg msg;
|
||||
u8 msgbuf[256];
|
||||
int ret = 0;
|
||||
|
||||
msg.addr = I2C_CLIENT_END;
|
||||
msg.buf = msgbuf;
|
||||
|
||||
switch (tu->regs[TU_REG_CMD]) {
|
||||
case TU_CMD_READ_BYTES:
|
||||
msg.addr = tu->regs[TU_REG_DATAL];
|
||||
msg.flags = I2C_M_RD;
|
||||
msg.len = tu->regs[TU_REG_DATAH];
|
||||
break;
|
||||
|
||||
case TU_CMD_HOST_NOTIFY:
|
||||
msg.addr = 0x08;
|
||||
msg.flags = 0;
|
||||
msg.len = 3;
|
||||
msgbuf[0] = tu->client->addr;
|
||||
msgbuf[1] = tu->regs[TU_REG_DATAL];
|
||||
msgbuf[2] = tu->regs[TU_REG_DATAH];
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (msg.addr != I2C_CLIENT_END) {
|
||||
ret = i2c_transfer(tu->client->adapter, &msg, 1);
|
||||
/* convert '0 msgs transferred' to errno */
|
||||
ret = (ret == 0) ? -EIO : ret;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
dev_err(&tu->client->dev, "CMD%02X failed (%d)\n", tu->regs[TU_REG_CMD], ret);
|
||||
|
||||
clear_bit(TU_FLAG_IN_PROCESS, &tu->flags);
|
||||
}
|
||||
|
||||
static int i2c_slave_testunit_slave_cb(struct i2c_client *client,
|
||||
enum i2c_slave_event event, u8 *val)
|
||||
{
|
||||
struct testunit_data *tu = i2c_get_clientdata(client);
|
||||
int ret = 0;
|
||||
|
||||
switch (event) {
|
||||
case I2C_SLAVE_WRITE_RECEIVED:
|
||||
if (test_bit(TU_FLAG_IN_PROCESS, &tu->flags))
|
||||
return -EBUSY;
|
||||
|
||||
if (tu->reg_idx < TU_NUM_REGS)
|
||||
tu->regs[tu->reg_idx] = *val;
|
||||
else
|
||||
ret = -EMSGSIZE;
|
||||
|
||||
if (tu->reg_idx <= TU_NUM_REGS)
|
||||
tu->reg_idx++;
|
||||
|
||||
/* TU_REG_CMD always written at this point */
|
||||
if (tu->regs[TU_REG_CMD] >= TU_NUM_CMDS)
|
||||
ret = -EINVAL;
|
||||
|
||||
break;
|
||||
|
||||
case I2C_SLAVE_STOP:
|
||||
if (tu->reg_idx == TU_NUM_REGS) {
|
||||
set_bit(TU_FLAG_IN_PROCESS, &tu->flags);
|
||||
queue_delayed_work(system_long_wq, &tu->worker,
|
||||
msecs_to_jiffies(10 * tu->regs[TU_REG_DELAY]));
|
||||
}
|
||||
fallthrough;
|
||||
|
||||
case I2C_SLAVE_WRITE_REQUESTED:
|
||||
tu->reg_idx = 0;
|
||||
break;
|
||||
|
||||
case I2C_SLAVE_READ_REQUESTED:
|
||||
case I2C_SLAVE_READ_PROCESSED:
|
||||
*val = TU_CUR_VERSION;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int i2c_slave_testunit_probe(struct i2c_client *client)
|
||||
{
|
||||
struct testunit_data *tu;
|
||||
|
||||
tu = devm_kzalloc(&client->dev, sizeof(struct testunit_data), GFP_KERNEL);
|
||||
if (!tu)
|
||||
return -ENOMEM;
|
||||
|
||||
tu->client = client;
|
||||
i2c_set_clientdata(client, tu);
|
||||
INIT_DELAYED_WORK(&tu->worker, i2c_slave_testunit_work);
|
||||
|
||||
return i2c_slave_register(client, i2c_slave_testunit_slave_cb);
|
||||
};
|
||||
|
||||
static int i2c_slave_testunit_remove(struct i2c_client *client)
|
||||
{
|
||||
struct testunit_data *tu = i2c_get_clientdata(client);
|
||||
|
||||
cancel_delayed_work_sync(&tu->worker);
|
||||
i2c_slave_unregister(client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id i2c_slave_testunit_id[] = {
|
||||
{ "slave-testunit", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, i2c_slave_testunit_id);
|
||||
|
||||
static struct i2c_driver i2c_slave_testunit_driver = {
|
||||
.driver = {
|
||||
.name = "i2c-slave-testunit",
|
||||
},
|
||||
.probe_new = i2c_slave_testunit_probe,
|
||||
.remove = i2c_slave_testunit_remove,
|
||||
.id_table = i2c_slave_testunit_id,
|
||||
};
|
||||
module_i2c_driver(i2c_slave_testunit_driver);
|
||||
|
||||
MODULE_AUTHOR("Wolfram Sang <wsa@sang-engineering.com>");
|
||||
MODULE_DESCRIPTION("I2C slave mode test unit");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -197,6 +197,113 @@ EXPORT_SYMBOL_GPL(i2c_handle_smbus_alert);
|
|||
|
||||
module_i2c_driver(smbalert_driver);
|
||||
|
||||
#if IS_ENABLED(CONFIG_I2C_SLAVE)
|
||||
#define SMBUS_HOST_NOTIFY_LEN 3
|
||||
struct i2c_slave_host_notify_status {
|
||||
u8 index;
|
||||
u8 addr;
|
||||
};
|
||||
|
||||
static int i2c_slave_host_notify_cb(struct i2c_client *client,
|
||||
enum i2c_slave_event event, u8 *val)
|
||||
{
|
||||
struct i2c_slave_host_notify_status *status = client->dev.platform_data;
|
||||
|
||||
switch (event) {
|
||||
case I2C_SLAVE_WRITE_RECEIVED:
|
||||
/* We only retrieve the first byte received (addr)
|
||||
* since there is currently no support to retrieve the data
|
||||
* parameter from the client.
|
||||
*/
|
||||
if (status->index == 0)
|
||||
status->addr = *val;
|
||||
if (status->index < U8_MAX)
|
||||
status->index++;
|
||||
break;
|
||||
case I2C_SLAVE_STOP:
|
||||
if (status->index == SMBUS_HOST_NOTIFY_LEN)
|
||||
i2c_handle_smbus_host_notify(client->adapter,
|
||||
status->addr);
|
||||
fallthrough;
|
||||
case I2C_SLAVE_WRITE_REQUESTED:
|
||||
status->index = 0;
|
||||
break;
|
||||
case I2C_SLAVE_READ_REQUESTED:
|
||||
case I2C_SLAVE_READ_PROCESSED:
|
||||
*val = 0xff;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* i2c_new_slave_host_notify_device - get a client for SMBus host-notify support
|
||||
* @adapter: the target adapter
|
||||
* Context: can sleep
|
||||
*
|
||||
* Setup handling of the SMBus host-notify protocol on a given I2C bus segment.
|
||||
*
|
||||
* Handling is done by creating a device and its callback and handling data
|
||||
* received via the SMBus host-notify address (0x8)
|
||||
*
|
||||
* This returns the client, which should be ultimately freed using
|
||||
* i2c_free_slave_host_notify_device(); or an ERRPTR to indicate an error.
|
||||
*/
|
||||
struct i2c_client *i2c_new_slave_host_notify_device(struct i2c_adapter *adapter)
|
||||
{
|
||||
struct i2c_board_info host_notify_board_info = {
|
||||
I2C_BOARD_INFO("smbus_host_notify", 0x08),
|
||||
.flags = I2C_CLIENT_SLAVE,
|
||||
};
|
||||
struct i2c_slave_host_notify_status *status;
|
||||
struct i2c_client *client;
|
||||
int ret;
|
||||
|
||||
status = kzalloc(sizeof(struct i2c_slave_host_notify_status),
|
||||
GFP_KERNEL);
|
||||
if (!status)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
host_notify_board_info.platform_data = status;
|
||||
|
||||
client = i2c_new_client_device(adapter, &host_notify_board_info);
|
||||
if (IS_ERR(client)) {
|
||||
kfree(status);
|
||||
return client;
|
||||
}
|
||||
|
||||
ret = i2c_slave_register(client, i2c_slave_host_notify_cb);
|
||||
if (ret) {
|
||||
i2c_unregister_device(client);
|
||||
kfree(status);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_new_slave_host_notify_device);
|
||||
|
||||
/**
|
||||
* i2c_free_slave_host_notify_device - free the client for SMBus host-notify
|
||||
* support
|
||||
* @client: the client to free
|
||||
* Context: can sleep
|
||||
*
|
||||
* Free the i2c_client allocated via i2c_new_slave_host_notify_device
|
||||
*/
|
||||
void i2c_free_slave_host_notify_device(struct i2c_client *client)
|
||||
{
|
||||
if (IS_ERR_OR_NULL(client))
|
||||
return;
|
||||
|
||||
i2c_slave_unregister(client);
|
||||
kfree(client->dev.platform_data);
|
||||
i2c_unregister_device(client);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_free_slave_host_notify_device);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* SPD is not part of SMBus but we include it here for convenience as the
|
||||
* target systems are the same.
|
||||
|
|
|
@ -85,18 +85,14 @@ static int i2c_mux_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
|
||||
mux->control = devm_mux_control_get(dev, NULL);
|
||||
if (IS_ERR(mux->control)) {
|
||||
if (PTR_ERR(mux->control) != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to get control-mux\n");
|
||||
return PTR_ERR(mux->control);
|
||||
}
|
||||
if (IS_ERR(mux->control))
|
||||
return dev_err_probe(dev, PTR_ERR(mux->control),
|
||||
"failed to get control-mux\n");
|
||||
|
||||
parent = mux_parent_adapter(dev);
|
||||
if (IS_ERR(parent)) {
|
||||
if (PTR_ERR(parent) != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to get i2c-parent adapter\n");
|
||||
return PTR_ERR(parent);
|
||||
}
|
||||
if (IS_ERR(parent))
|
||||
return dev_err_probe(dev, PTR_ERR(parent),
|
||||
"failed to get i2c-parent adapter\n");
|
||||
|
||||
children = of_get_child_count(np);
|
||||
|
||||
|
|
|
@ -171,13 +171,9 @@ static int i2c_mux_reg_probe(struct platform_device *pdev)
|
|||
sizeof(mux->data));
|
||||
} else {
|
||||
ret = i2c_mux_reg_probe_dt(mux, pdev);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
return ret;
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Error parsing device tree");
|
||||
return ret;
|
||||
}
|
||||
if (ret < 0)
|
||||
return dev_err_probe(&pdev->dev, ret,
|
||||
"Error parsing device tree");
|
||||
}
|
||||
|
||||
parent = i2c_get_adapter(mux->data.parent);
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
|
@ -89,6 +90,7 @@ struct at24_data {
|
|||
|
||||
struct nvmem_device *nvmem;
|
||||
struct regulator *vcc_reg;
|
||||
void (*read_post)(unsigned int off, char *buf, size_t count);
|
||||
|
||||
/*
|
||||
* Some chips tie up multiple I2C addresses; dummy devices reserve
|
||||
|
@ -121,6 +123,7 @@ MODULE_PARM_DESC(at24_write_timeout, "Time (in ms) to try writes (default 25)");
|
|||
struct at24_chip_data {
|
||||
u32 byte_len;
|
||||
u8 flags;
|
||||
void (*read_post)(unsigned int off, char *buf, size_t count);
|
||||
};
|
||||
|
||||
#define AT24_CHIP_DATA(_name, _len, _flags) \
|
||||
|
@ -128,6 +131,32 @@ struct at24_chip_data {
|
|||
.byte_len = _len, .flags = _flags, \
|
||||
}
|
||||
|
||||
#define AT24_CHIP_DATA_CB(_name, _len, _flags, _read_post) \
|
||||
static const struct at24_chip_data _name = { \
|
||||
.byte_len = _len, .flags = _flags, \
|
||||
.read_post = _read_post, \
|
||||
}
|
||||
|
||||
static void at24_read_post_vaio(unsigned int off, char *buf, size_t count)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (capable(CAP_SYS_ADMIN))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Hide VAIO private settings to regular users:
|
||||
* - BIOS passwords: bytes 0x00 to 0x0f
|
||||
* - UUID: bytes 0x10 to 0x1f
|
||||
* - Serial number: 0xc0 to 0xdf
|
||||
*/
|
||||
for (i = 0; i < count; i++) {
|
||||
if ((off + i <= 0x1f) ||
|
||||
(off + i >= 0xc0 && off + i <= 0xdf))
|
||||
buf[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* needs 8 addresses as A0-A2 are ignored */
|
||||
AT24_CHIP_DATA(at24_data_24c00, 128 / 8, AT24_FLAG_TAKE8ADDR);
|
||||
/* old variants can't be handled with this generic entry! */
|
||||
|
@ -144,6 +173,10 @@ AT24_CHIP_DATA(at24_data_24mac602, 64 / 8,
|
|||
/* spd is a 24c02 in memory DIMMs */
|
||||
AT24_CHIP_DATA(at24_data_spd, 2048 / 8,
|
||||
AT24_FLAG_READONLY | AT24_FLAG_IRUGO);
|
||||
/* 24c02_vaio is a 24c02 on some Sony laptops */
|
||||
AT24_CHIP_DATA_CB(at24_data_24c02_vaio, 2048 / 8,
|
||||
AT24_FLAG_READONLY | AT24_FLAG_IRUGO,
|
||||
at24_read_post_vaio);
|
||||
AT24_CHIP_DATA(at24_data_24c04, 4096 / 8, 0);
|
||||
AT24_CHIP_DATA(at24_data_24cs04, 16,
|
||||
AT24_FLAG_SERIAL | AT24_FLAG_READONLY);
|
||||
|
@ -177,6 +210,7 @@ static const struct i2c_device_id at24_ids[] = {
|
|||
{ "24mac402", (kernel_ulong_t)&at24_data_24mac402 },
|
||||
{ "24mac602", (kernel_ulong_t)&at24_data_24mac602 },
|
||||
{ "spd", (kernel_ulong_t)&at24_data_spd },
|
||||
{ "24c02-vaio", (kernel_ulong_t)&at24_data_24c02_vaio },
|
||||
{ "24c04", (kernel_ulong_t)&at24_data_24c04 },
|
||||
{ "24cs04", (kernel_ulong_t)&at24_data_24cs04 },
|
||||
{ "24c08", (kernel_ulong_t)&at24_data_24c08 },
|
||||
|
@ -388,7 +422,7 @@ static int at24_read(void *priv, unsigned int off, void *val, size_t count)
|
|||
struct at24_data *at24;
|
||||
struct device *dev;
|
||||
char *buf = val;
|
||||
int ret;
|
||||
int i, ret;
|
||||
|
||||
at24 = priv;
|
||||
dev = at24_base_client_dev(at24);
|
||||
|
@ -411,22 +445,22 @@ static int at24_read(void *priv, unsigned int off, void *val, size_t count)
|
|||
*/
|
||||
mutex_lock(&at24->lock);
|
||||
|
||||
while (count) {
|
||||
ret = at24_regmap_read(at24, buf, off, count);
|
||||
for (i = 0; count; i += ret, count -= ret) {
|
||||
ret = at24_regmap_read(at24, buf + i, off + i, count);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&at24->lock);
|
||||
pm_runtime_put(dev);
|
||||
return ret;
|
||||
}
|
||||
buf += ret;
|
||||
off += ret;
|
||||
count -= ret;
|
||||
}
|
||||
|
||||
mutex_unlock(&at24->lock);
|
||||
|
||||
pm_runtime_put(dev);
|
||||
|
||||
if (unlikely(at24->read_post))
|
||||
at24->read_post(off, buf, i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -654,6 +688,7 @@ static int at24_probe(struct i2c_client *client)
|
|||
at24->byte_len = byte_len;
|
||||
at24->page_size = page_size;
|
||||
at24->flags = flags;
|
||||
at24->read_post = cdata->read_post;
|
||||
at24->num_addresses = num_addresses;
|
||||
at24->offset_adj = at24_get_offset_adj(flags, byte_len);
|
||||
at24->client[0].client = client;
|
||||
|
@ -678,8 +713,30 @@ static int at24_probe(struct i2c_client *client)
|
|||
return err;
|
||||
}
|
||||
|
||||
nvmem_config.name = dev_name(dev);
|
||||
/*
|
||||
* If the 'label' property is not present for the AT24 EEPROM,
|
||||
* then nvmem_config.id is initialised to NVMEM_DEVID_AUTO,
|
||||
* and this will append the 'devid' to the name of the NVMEM
|
||||
* device. This is purely legacy and the AT24 driver has always
|
||||
* defaulted to this. However, if the 'label' property is
|
||||
* present then this means that the name is specified by the
|
||||
* firmware and this name should be used verbatim and so it is
|
||||
* not necessary to append the 'devid'.
|
||||
*/
|
||||
if (device_property_present(dev, "label")) {
|
||||
nvmem_config.id = NVMEM_DEVID_NONE;
|
||||
err = device_property_read_string(dev, "label",
|
||||
&nvmem_config.name);
|
||||
if (err)
|
||||
return err;
|
||||
} else {
|
||||
nvmem_config.id = NVMEM_DEVID_AUTO;
|
||||
nvmem_config.name = dev_name(dev);
|
||||
}
|
||||
|
||||
nvmem_config.type = NVMEM_TYPE_EEPROM;
|
||||
nvmem_config.dev = dev;
|
||||
nvmem_config.id = NVMEM_DEVID_AUTO;
|
||||
nvmem_config.read_only = !writable;
|
||||
nvmem_config.root_only = !(flags & AT24_FLAG_IRUGO);
|
||||
nvmem_config.owner = THIS_MODULE;
|
||||
|
|
|
@ -76,7 +76,7 @@ static ssize_t eeprom_read(struct file *filp, struct kobject *kobj,
|
|||
struct bin_attribute *bin_attr,
|
||||
char *buf, loff_t off, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(kobj_to_dev(kobj));
|
||||
struct i2c_client *client = kobj_to_i2c_client(kobj);
|
||||
struct eeprom_data *data = i2c_get_clientdata(client);
|
||||
u8 slice;
|
||||
|
||||
|
|
|
@ -38,6 +38,18 @@ static inline int of_i2c_setup_smbus_alert(struct i2c_adapter *adap)
|
|||
return 0;
|
||||
}
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_I2C_SMBUS) && IS_ENABLED(CONFIG_I2C_SLAVE)
|
||||
struct i2c_client *i2c_new_slave_host_notify_device(struct i2c_adapter *adapter);
|
||||
void i2c_free_slave_host_notify_device(struct i2c_client *client);
|
||||
#else
|
||||
static inline struct i2c_client *i2c_new_slave_host_notify_device(struct i2c_adapter *adapter)
|
||||
{
|
||||
return ERR_PTR(-ENOSYS);
|
||||
}
|
||||
static inline void i2c_free_slave_host_notify_device(struct i2c_client *client)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_I2C_SMBUS) && IS_ENABLED(CONFIG_DMI)
|
||||
void i2c_register_spd(struct i2c_adapter *adap);
|
||||
|
|
|
@ -344,7 +344,7 @@ const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
|
|||
|
||||
static inline struct i2c_client *kobj_to_i2c_client(struct kobject *kobj)
|
||||
{
|
||||
struct device * const dev = container_of(kobj, struct device, kobj);
|
||||
struct device * const dev = kobj_to_dev(kobj);
|
||||
return to_i2c_client(dev);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue