mirror of
https://github.com/torvalds/linux.git
synced 2024-11-21 19:41:42 +00:00
i2c-for-6.12-rc1
I2C core ======== After 15 years of deprecation, the I2C_COMPAT symbol has finally been removed. Also client addresses are now locked during initialization to prevent race conditions between different kinds of instantiation. Scoped foreach OF child loops are now used. And the testunit has received some cleanups and documentation improvements as well as two new tests, one for repeated start and one for triggering SMBusAlert interrupts. I2C host drivers ================ The DesignWare and the Renesas I2C drivers have received most of the changes in this pull request. The first has has undergone through a series of cleanups that have been sent to the mailing list a year ago for the first time and finally get merged in this pull request. They are many, from typos (e.g. i2/i2c), to cosmetics, to refactoring (e.g. move inline functions to librarieas) and many others. Besides that, all the DesignWare Kconfig options have been grouped under the I2C_DESIGNWARE_CORE and this required some adaptation in many of the kernel configuration files for different arm and mips boards. Follows the list of the rest of the changes grouped by type of change. Cleanups -------- The Qualcomm Geni platform improves the exit path in the runtime resume function. The Intel LJCA driver loses "target_addr" parameter in ljca_i2c_stop() because it was unused. The MediaTek controller intializes the restart_flag in the transfer function using the ternary conditional operator ("? :") instead of initializing it in different parts. Constified a few global data structures in the virtio driver. The Renesas driver simplifies the bus speed handling in the init function making it more readable. Improved an if/else statement in probe function of the Renesas R-Car driver. The iMX/MXC driver switches to using the RUNTIME_PM_OPS() instead of SET_RUNTIME_PM_OPS(). Still in the iMX/MXC driver a comma ',' has been replaced by a semicolon ';', while in different drivers the ',' has been removed from the '{ }' delimiters. Finally three devm_clk_get_enabled() have been used to simplify the devm_clk_get/clk_prepare_enable tuple in the Renesas EMEV2, Ingenic and MPC drivers. Refactors --------- The Nuvoton fixes a potential out of boundary array access. This is not a bug fix because the issue could never occur due to hardware not having the properties listed in the array. The change makes the driver more future proof and, at the same time, silences code analyzers. Improvements ------------ The Renesas I2C (riic) driver undergoes several patches improving the runtime power management handling. The Intel i801 driver uses a more descriptive adapter's name to show the presence of the IDF feature. In the Intel Denverton (ismt) adapter the pending transactions are killed when irq's can't complete their handling, triggering a timeout. This could have been considered as a bug fix, but because, standing to Vasily, it's very sporadic, I preferred considering the patch rather as an improvement. New Feature ----------- The Renesas I2C (riic) driver now supports the fast mode plus. New support ----------- Added support for: - Renesas R9A08G045 - Rockchip RK3576 - KEBA I2C - Theobroma Systems Mule Multiplexer. The Keba comes with a new driver, i2c-keba.c. The Mule is an i2c multiplexer and it also comes with a new driver, mux/i2c-mux-mule.c. Core patch ---------- This pull request includes also a patch in the I2C framework, in i2c-core-base.c where the runtime PM functions have been replaced in order to allow to be accessed during the device add. Devicetree ---------- Some cleanups in the devicetree, as well. nVidia and Qualcomm bindings improve their "if:then:" blocks. While the aspeed binding loses the "multi-master" property because it was redundant. The i2c-sprd binding has been converted to YAML. AT24 updates ============ - document a new model from giantec in DT bindings -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEOZGx6rniZ1Gk92RdFA3kzBSgKbYFAmbxGRsACgkQFA3kzBSg KbY2IhAApRRZHNmxxcMRmxbDnbNweJdRGbtADEdkLMDui8oeK9SJHgScD0bR308p pkHEvyOg7UiW0N4wkaDK0YpORIrGSHys2DqIC05/OLPLONz2Ry/qKuNkisZBxo6l oP9uVKDMuCHgZq7xsxqupmpefMJ0m8XBGoMKPLbyBMu7ga4vB8o4uEQZfQLKs3YP mFm4plZvECCVPgJ5/bp43cFFmhfPLTd088k9/XzFwB730uXPO6VsBuaYzQ7tMOR3 NQCmh/8sFJmVlJvkTnQ5QRNTo2zn+hNmjV1avFJwo5lqz35TmfpVR/+TjYPwi9v6 7H5KjHrIxQHmeaLwm94wOuJSriFzQ3DUQkxvH7vRxXDef+6nTRdD6xC+zxePKLXo R4dYslP+5yXvtPYHonJUTXXZkfug58iO7W6Isc/5ody1y4FD22daTG5HXuWRlaAP 7O0kiyQmrwy5IZCqpwVPBJ7f+dpZzpCVP0OyXeHVXyK61rZT4zG9FvEiLQYjmYOn MOSbddFm5yQRu+OB8GVmYKlVlCG0S+Y11fFMCO/yJZQJqLXZm2AbonwB8sz0OqZu 4zDgXg+z3Xy/Go6/FFfjltoWq/9dYzzFzUi0oB6rm0U/pFawtCtAYhfCodzJDZI4 QvzALJuFWwQZjGNmqDVfYABcX8wFxE8zAteQy+htu0Fn7qSV7GU= =DTNY -----END PGP SIGNATURE----- Merge tag 'i2c-for-6.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux Pull i2c updates from Wolfram Sang: "I2C core: - finally remove the I2C_COMPAT symbol after 15 years of deprecation - lock client addresses during initialization to prevent race conditions between different kinds of instantiation - use scoped foreach OF child loops - testunit cleanups and documentation improvements, as well as two new tests, one for repeated start and one for triggering SMBusAlert interrupts I2C host drivers: - DesignWare and Renesas I2C driver updates. The first has has undergone through a series of cleanups that have been sent to the mailing list a year ago for the first time and finally get merged in this pull request. They are many, from typos (e.g. i2/i2c), to cosmetics, to refactoring (e.g. move inline functions to librarieas) and many others. - all the DesignWare Kconfig options have been grouped under the I2C_DESIGNWARE_CORE and this required some adaptation in many of the kernel configuration files for different arm and mips boards Cleanups: - improve the exit path in the runtime resume function for the Qualcomm Geni platform - get rid of the unused "target_addr" parameter in the Intel LJCA driver - intialize the restart_flag in the MediaTek controller in one single place - constify a few global data structures in the virtio driver - simplify the bus speed handling in the Renesas driver init function making it more readable - improved probe function of the Renesas R-Car driver - switch the iMX/MXC driver to use RUNTIME_PM_OPS() instead of SET_RUNTIME_PM_OPS() - iMX/MXC driver cleanups - use devm_clk_get_enabled() to simplify the Renesas EMEV2, Ingenic and MPC drivers Refactoring: - Fix a potential out of boundary array access in the Nuvoton driver. This is not a bug fix because the issue could never occur due to hardware not having the properties listed in the array. The change makes the driver more future proof and, at the same time, silences code analyzers. Improvements: - several patches improving the runtime power management handling of the Renesas I2C (riic) driver - use a more descriptive adapter name in the Intel i801 driver to show the presence of the IDF feature - kill pending transactions when irq's can't complete their handling in the Intel Denverton (ismt) driver, triggering a timeout New Feature: - support fast mode plus in the Renesas I2C (riic) driver New support: - Added support for: - Renesas R9A08G045 - Rockchip RK3576 - KEBA I2C - Theobroma Systems Mule Multiplexer. - new i2c-keba.c driver - new driver for The Mule i2c multiplexer Core I2C framework: - move runtime PM functions in order to allow them to be accessed during device add Devicetree: - nVidia and Qualcomm binding improvements - get rid of redundant "multi-master" property in the aspeed binding - convert i2c-sprd binding to YAML AT24 updates: - document a new model from giantec in DT bindings" * tag 'i2c-for-6.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (69 commits) i2c: designware: Use pci_get_drvdata() i2c: designware: Propagate firmware node i2c: designware: Uninline i2c_dw_probe() i2c: ljca: Remove unused "target_addr" parameter i2c: keba: Add KEBA I2C controller support i2c: i801: Use a different adapter-name for IDF adapters i2c: core: Setup i2c_adapter runtime-pm before calling device_add() dt-bindings: i2c: i2c-sprd: convert to YAML i2c: ismt: kill transaction in hardware on timeout i2c: designware: Group all DesignWare drivers under a single option net: txgbe: Fix I2C Kconfig dependencies RISC-V: configs: enable I2C_DESIGNWARE_CORE with I2C_DESIGNWARE_PLATFORM mips: configs: enable I2C_DESIGNWARE_CORE with I2C_DESIGNWARE_PLATFORM arm64: defconfig: enable I2C_DESIGNWARE_CORE with I2C_DESIGNWARE_PLATFORM ARM: configs: enable I2C_DESIGNWARE_CORE with I2C_DESIGNWARE_PLATFORM ARC: configs: enable I2C_DESIGNWARE_CORE with I2C_DESIGNWARE_PLATFORM i2c: virtio: Constify struct i2c_algorithm and struct virtio_device_id i2c: rcar: tidyup priv->devtype handling on rcar_i2c_probe() i2c: imx: Convert comma to semicolon i2c: jz4780: Use devm_clk_get_enabled() helpers ...
This commit is contained in:
commit
4e2c9cd7dc
@ -116,6 +116,7 @@ properties:
|
||||
- const: atmel,24c02
|
||||
- items:
|
||||
- enum:
|
||||
- giantec,gt24c04a
|
||||
- onnn,cat24c04
|
||||
- onnn,cat24c05
|
||||
- rohm,br24g04
|
||||
|
@ -44,11 +44,6 @@ properties:
|
||||
description: frequency of the bus clock in Hz defaults to 100 kHz when not
|
||||
specified
|
||||
|
||||
multi-master:
|
||||
type: boolean
|
||||
description:
|
||||
states that there is another master active on this bus
|
||||
|
||||
required:
|
||||
- reg
|
||||
- compatible
|
||||
|
@ -38,6 +38,7 @@ properties:
|
||||
- rockchip,rk3308-i2c
|
||||
- rockchip,rk3328-i2c
|
||||
- rockchip,rk3568-i2c
|
||||
- rockchip,rk3576-i2c
|
||||
- rockchip,rk3588-i2c
|
||||
- rockchip,rv1126-i2c
|
||||
- const: rockchip,rk3399-i2c
|
||||
|
@ -1,31 +0,0 @@
|
||||
I2C for Spreadtrum platforms
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "sprd,sc9860-i2c".
|
||||
- reg: Specify the physical base address of the controller and length
|
||||
of memory mapped region.
|
||||
- interrupts: Should contain I2C interrupt.
|
||||
- clock-names: Should contain following entries:
|
||||
"i2c" for I2C clock,
|
||||
"source" for I2C source (parent) clock,
|
||||
"enable" for I2C module enable clock.
|
||||
- clocks: Should contain a clock specifier for each entry in clock-names.
|
||||
- clock-frequency: Contains desired I2C bus clock frequency in Hz.
|
||||
- #address-cells: Should be 1 to describe address cells for I2C device address.
|
||||
- #size-cells: Should be 0 means no size cell for I2C device address.
|
||||
|
||||
Optional properties:
|
||||
- Child nodes conforming to I2C bus binding
|
||||
|
||||
Examples:
|
||||
i2c0: i2c@70500000 {
|
||||
compatible = "sprd,sc9860-i2c";
|
||||
reg = <0 0x70500000 0 0x1000>;
|
||||
interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clock-names = "i2c", "source", "enable";
|
||||
clocks = <&clk_i2c3>, <&ext_26m>, <&clk_ap_apb_gates 11>;
|
||||
clock-frequency = <400000>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
|
@ -103,6 +103,9 @@ properties:
|
||||
items:
|
||||
- const: i2c
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
dmas:
|
||||
items:
|
||||
- description: DMA channel for the reception FIFO
|
||||
@ -124,6 +127,8 @@ allOf:
|
||||
- nvidia,tegra30-i2c
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 2
|
||||
clock-names:
|
||||
items:
|
||||
- const: div-clk
|
||||
@ -133,20 +138,13 @@ allOf:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: nvidia,tegra114-i2c
|
||||
then:
|
||||
properties:
|
||||
clock-names:
|
||||
items:
|
||||
- const: div-clk
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: nvidia,tegra210-i2c
|
||||
enum:
|
||||
- nvidia,tegra114-i2c
|
||||
- nvidia,tegra210-i2c
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 1
|
||||
clock-names:
|
||||
items:
|
||||
- const: div-clk
|
||||
@ -158,6 +156,8 @@ allOf:
|
||||
const: nvidia,tegra210-i2c-vi
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 2
|
||||
clock-names:
|
||||
items:
|
||||
- const: div-clk
|
||||
@ -165,6 +165,9 @@ allOf:
|
||||
power-domains:
|
||||
items:
|
||||
- description: phandle to the VENC power domain
|
||||
else:
|
||||
properties:
|
||||
power-domains: false
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
|
@ -130,6 +130,7 @@ allOf:
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 4
|
||||
maxItems: 4
|
||||
clock-names:
|
||||
items:
|
||||
|
@ -25,6 +25,10 @@ properties:
|
||||
- renesas,riic-r9a07g054 # RZ/V2L
|
||||
- const: renesas,riic-rz # RZ/A or RZ/G2L
|
||||
|
||||
- items:
|
||||
- const: renesas,riic-r9a08g045 # RZ/G3S
|
||||
- const: renesas,riic-r9a09g057 # RZ/V2H(P)
|
||||
|
||||
- const: renesas,riic-r9a09g057 # RZ/V2H(P)
|
||||
|
||||
reg:
|
||||
|
65
Documentation/devicetree/bindings/i2c/sprd,sc9860-i2c.yaml
Normal file
65
Documentation/devicetree/bindings/i2c/sprd,sc9860-i2c.yaml
Normal file
@ -0,0 +1,65 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/i2c/sprd,sc9860-i2c.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Spreadtrum SC9860 I2C controller
|
||||
|
||||
maintainers:
|
||||
- Orson Zhai <orsonzhai@gmail.com>
|
||||
- Baolin Wang <baolin.wang7@gmail.com>
|
||||
- Chunyan Zhang <zhang.lyra@gmail.com>
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/i2c/i2c-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: sprd,sc9860-i2c
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: I2C clock
|
||||
- description: I2C source (parent) clock
|
||||
- description: I2C module enable clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: i2c
|
||||
- const: source
|
||||
- const: enable
|
||||
|
||||
clock-frequency: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
- clock-frequency
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c@70500000 {
|
||||
compatible = "sprd,sc9860-i2c";
|
||||
reg = <0x70500000 0x1000>;
|
||||
interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clk_i2c3>, <&ext_26m>, <&clk_ap_apb_gates 11>;
|
||||
clock-names = "i2c", "source", "enable";
|
||||
clock-frequency = <400000>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
69
Documentation/devicetree/bindings/i2c/tsd,mule-i2c-mux.yaml
Normal file
69
Documentation/devicetree/bindings/i2c/tsd,mule-i2c-mux.yaml
Normal file
@ -0,0 +1,69 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/i2c/tsd,mule-i2c-mux.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Theobroma Systems Mule I2C multiplexer
|
||||
|
||||
maintainers:
|
||||
- Farouk Bouabid <farouk.bouabid@cherry.de>
|
||||
- Quentin Schulz <quentin.schulz@cherry.de>
|
||||
|
||||
description: |
|
||||
Theobroma Systems Mule is an MCU that emulates a set of I2C devices, among
|
||||
which devices that are reachable through an I2C-mux. The devices on the mux
|
||||
can be selected by writing the appropriate device number to an I2C config
|
||||
register.
|
||||
|
||||
|
||||
+--------------------------------------------------+
|
||||
| Mule |
|
||||
0x18| +---------------+ |
|
||||
-------->|Config register|----+ |
|
||||
| +---------------+ | |
|
||||
| V_ |
|
||||
| | \ +--------+ |
|
||||
| | \-------->| dev #0 | |
|
||||
| | | +--------+ |
|
||||
0x6f| | M |-------->| dev #1 | |
|
||||
---------------------------->| U | +--------+ |
|
||||
| | X |-------->| dev #2 | |
|
||||
| | | +--------+ |
|
||||
| | /-------->| dev #3 | |
|
||||
| |__/ +--------+ |
|
||||
+--------------------------------------------------+
|
||||
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/i2c/i2c-mux.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: tsd,mule-i2c-mux
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c-mux {
|
||||
compatible = "tsd,mule-i2c-mux";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
i2c@0 {
|
||||
reg = <0x0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
rtc@6f {
|
||||
compatible = "isil,isl1208";
|
||||
reg = <0x6f>;
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
||||
|
@ -20,11 +20,25 @@ 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, except for some "partial" commands, all registers must be
|
||||
written to start a testcase, i.e. you usually write 4 bytes to the device. The
|
||||
registers are:
|
||||
Or using firmware nodes. Here is a devicetree example (note this is only a
|
||||
debug device, so there are no official DT bindings)::
|
||||
|
||||
&i2c0 {
|
||||
...
|
||||
|
||||
testunit@30 {
|
||||
compatible = "slave-testunit";
|
||||
reg = <(0x30 | I2C_OWN_SLAVE_ADDRESS)>;
|
||||
};
|
||||
};
|
||||
|
||||
After that, you will have the device listening. Reading will return a single
|
||||
byte. Its value is 0 if the testunit is idle, otherwise the command number of
|
||||
the currently running command.
|
||||
|
||||
When writing, the device consists of 4 8-bit registers and, except for some
|
||||
"partial" commands, all registers must be written to start a testcase, i.e. you
|
||||
usually write 4 bytes to the device. The registers are:
|
||||
|
||||
.. csv-table::
|
||||
:header: "Offset", "Name", "Description"
|
||||
@ -75,7 +89,7 @@ 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
|
||||
# i2cset -y 0 0x30 1 0x50 0x80 5 i
|
||||
|
||||
0x02 SMBUS_HOST_NOTIFY
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -95,9 +109,9 @@ bytes from device 0x50 after 50ms of delay::
|
||||
|
||||
Also needs master mode. 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::
|
||||
Example to send a notification with status word 0x6442 after 10ms::
|
||||
|
||||
# i2cset -y 0 0x30 0x02 0x42 0x64 0x01 i
|
||||
# i2cset -y 0 0x30 2 0x42 0x64 1 i
|
||||
|
||||
If the host controller supports HostNotify, this message with debug level
|
||||
should appear (Linux 6.11 and later)::
|
||||
@ -116,7 +130,7 @@ should appear (Linux 6.11 and later)::
|
||||
- DELAY
|
||||
|
||||
* - 0x03
|
||||
- must be '1', i.e. one further byte will be written
|
||||
- 0x01 (i.e. one further byte will be written)
|
||||
- number of bytes to be sent back
|
||||
- leave out, partial command!
|
||||
|
||||
@ -131,5 +145,91 @@ from length-1 to 0. Here is an example which emulates
|
||||
i2c_smbus_block_process_call() using i2ctransfer (you need i2c-tools v4.2 or
|
||||
later)::
|
||||
|
||||
# i2ctransfer -y 0 w3@0x30 0x03 0x01 0x10 r?
|
||||
# i2ctransfer -y 0 w3@0x30 3 1 0x10 r?
|
||||
0x10 0x0f 0x0e 0x0d 0x0c 0x0b 0x0a 0x09 0x08 0x07 0x06 0x05 0x04 0x03 0x02 0x01 0x00
|
||||
|
||||
0x04 GET_VERSION_WITH_REP_START
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
|
||||
* - CMD
|
||||
- DATAL
|
||||
- DATAH
|
||||
- DELAY
|
||||
|
||||
* - 0x04
|
||||
- currently unused
|
||||
- currently unused
|
||||
- leave out, partial command!
|
||||
|
||||
Partial command. After sending this command, the testunit will reply to a read
|
||||
message with a NUL terminated version string based on UTS_RELEASE. The first
|
||||
character is always a 'v' and the length of the version string is at maximum
|
||||
128 bytes. However, it will only respond if the read message is connected to
|
||||
the write message via repeated start. If your controller driver handles
|
||||
repeated start correctly, this will work::
|
||||
|
||||
# i2ctransfer -y 0 w3@0x30 4 0 0 r128
|
||||
0x76 0x36 0x2e 0x31 0x31 0x2e 0x30 0x2d 0x72 0x63 0x31 0x2d 0x30 0x30 0x30 0x30 ...
|
||||
|
||||
If you have i2c-tools 4.4 or later, you can print out the data right away::
|
||||
|
||||
# i2ctransfer -y -b 0 w3@0x30 4 0 0 r128
|
||||
v6.11.0-rc1-00009-gd37a1b4d3fd0
|
||||
|
||||
STOP/START combinations between the two messages will *not* work because they
|
||||
are not equivalent to a REPEATED START. As an example, this returns just the
|
||||
default response::
|
||||
|
||||
# i2cset -y 0 0x30 4 0 0 i; i2cget -y 0 0x30
|
||||
0x00
|
||||
|
||||
0x05 SMBUS_ALERT_REQUEST
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
|
||||
* - CMD
|
||||
- DATAL
|
||||
- DATAH
|
||||
- DELAY
|
||||
|
||||
* - 0x05
|
||||
- response value (7 MSBs interpreted as I2C address)
|
||||
- currently unused
|
||||
- n * 10ms
|
||||
|
||||
This test raises an interrupt via the SMBAlert pin which the host controller
|
||||
must handle. The pin must be connected to the testunit as a GPIO. GPIO access
|
||||
is not allowed to sleep. Currently, this can only be described using firmware
|
||||
nodes. So, for devicetree, you would add something like this to the testunit
|
||||
node::
|
||||
|
||||
gpios = <&gpio1 24 GPIO_ACTIVE_LOW>;
|
||||
|
||||
The following command will trigger the alert with a response of 0xc9 after 1
|
||||
second of delay::
|
||||
|
||||
# i2cset -y 0 0x30 5 0xc9 0x00 100 i
|
||||
|
||||
If the host controller supports SMBusAlert, this message with debug level
|
||||
should appear::
|
||||
|
||||
smbus_alert 0-000c: SMBALERT# from dev 0x64, flag 1
|
||||
|
||||
This message may appear more than once because the testunit is software not
|
||||
hardware and, thus, may not be able to react to the response of the host fast
|
||||
enough. The interrupt count should increase only by one, though::
|
||||
|
||||
# cat /proc/interrupts | grep smbus_alert
|
||||
93: 1 gpio-rcar 26 Edge smbus_alert
|
||||
|
||||
If the host does not respond to the alert within 1 second, the test will be
|
||||
aborted and the testunit will report an error.
|
||||
|
||||
For this test, the testunit will shortly drop its assigned address and listen
|
||||
on the SMBus Alert Response Address (0x0c). It will reassign its original
|
||||
address afterwards.
|
||||
|
@ -66,6 +66,7 @@ CONFIG_SERIAL_OF_PLATFORM=y
|
||||
# CONFIG_HW_RANDOM is not set
|
||||
CONFIG_I2C=y
|
||||
CONFIG_I2C_CHARDEV=y
|
||||
CONFIG_I2C_DESIGNWARE_CORE=y
|
||||
CONFIG_I2C_DESIGNWARE_PLATFORM=y
|
||||
# CONFIG_HWMON is not set
|
||||
CONFIG_DRM=m
|
||||
|
@ -66,6 +66,7 @@ CONFIG_SERIAL_OF_PLATFORM=y
|
||||
# CONFIG_HW_RANDOM is not set
|
||||
CONFIG_I2C=y
|
||||
CONFIG_I2C_CHARDEV=y
|
||||
CONFIG_I2C_DESIGNWARE_CORE=y
|
||||
CONFIG_I2C_DESIGNWARE_PLATFORM=y
|
||||
# CONFIG_HWMON is not set
|
||||
CONFIG_FB=y
|
||||
|
@ -66,6 +66,7 @@ CONFIG_SERIAL_OF_PLATFORM=y
|
||||
# CONFIG_HW_RANDOM is not set
|
||||
CONFIG_I2C=y
|
||||
CONFIG_I2C_CHARDEV=y
|
||||
CONFIG_I2C_DESIGNWARE_CORE=y
|
||||
CONFIG_I2C_DESIGNWARE_PLATFORM=y
|
||||
# CONFIG_HWMON is not set
|
||||
CONFIG_DRM=m
|
||||
|
@ -60,6 +60,7 @@ CONFIG_SERIAL_8250_DW=y
|
||||
# CONFIG_HW_RANDOM is not set
|
||||
CONFIG_I2C=y
|
||||
# CONFIG_I2C_COMPAT is not set
|
||||
CONFIG_I2C_DESIGNWARE_CORE=y
|
||||
CONFIG_I2C_DESIGNWARE_PLATFORM=y
|
||||
CONFIG_GPIO_SYSFS=y
|
||||
# CONFIG_HWMON is not set
|
||||
|
@ -43,6 +43,7 @@ CONFIG_SERIAL_8250_DW=y
|
||||
CONFIG_SERIAL_OF_PLATFORM=y
|
||||
CONFIG_SERIAL_AMBA_PL011=y
|
||||
CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
|
||||
CONFIG_I2C_DESIGNWARE_CORE=y
|
||||
CONFIG_I2C_DESIGNWARE_PLATFORM=y
|
||||
CONFIG_SPI=y
|
||||
CONFIG_SPI_PL022=y
|
||||
|
@ -413,6 +413,7 @@ CONFIG_I2C_AT91=m
|
||||
CONFIG_I2C_BCM2835=y
|
||||
CONFIG_I2C_CADENCE=y
|
||||
CONFIG_I2C_DAVINCI=y
|
||||
CONFIG_I2C_DESIGNWARE_CORE=y
|
||||
CONFIG_I2C_DESIGNWARE_PLATFORM=y
|
||||
CONFIG_I2C_DIGICOLOR=m
|
||||
CONFIG_I2C_EMEV2=m
|
||||
|
@ -277,6 +277,7 @@ CONFIG_HW_RANDOM=y
|
||||
CONFIG_I2C_CHARDEV=m
|
||||
CONFIG_I2C_MUX_PCA954x=m
|
||||
CONFIG_I2C_MUX_PINCTRL=m
|
||||
CONFIG_I2C_DESIGNWARE_CORE=m
|
||||
CONFIG_I2C_DESIGNWARE_PLATFORM=m
|
||||
CONFIG_I2C_GPIO=y
|
||||
CONFIG_I2C_PXA_SLAVE=y
|
||||
|
@ -83,6 +83,7 @@ CONFIG_SERIAL_8250_RUNTIME_UARTS=2
|
||||
CONFIG_SERIAL_8250_DW=y
|
||||
CONFIG_I2C=y
|
||||
CONFIG_I2C_CHARDEV=y
|
||||
CONFIG_I2C_DESIGNWARE_CORE=y
|
||||
CONFIG_I2C_DESIGNWARE_PLATFORM=y
|
||||
CONFIG_SPI=y
|
||||
CONFIG_SPI_CADENCE_QUADSPI=y
|
||||
|
@ -62,6 +62,7 @@ CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
|
||||
# CONFIG_HW_RANDOM is not set
|
||||
CONFIG_RAW_DRIVER=y
|
||||
CONFIG_I2C=y
|
||||
CONFIG_I2C_DESIGNWARE_CORE=y
|
||||
CONFIG_I2C_DESIGNWARE_PLATFORM=y
|
||||
CONFIG_SPI=y
|
||||
CONFIG_SPI_PL022=y
|
||||
|
@ -42,6 +42,7 @@ CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
|
||||
# CONFIG_HW_RANDOM is not set
|
||||
CONFIG_RAW_DRIVER=y
|
||||
CONFIG_I2C=y
|
||||
CONFIG_I2C_DESIGNWARE_CORE=y
|
||||
CONFIG_I2C_DESIGNWARE_PLATFORM=y
|
||||
CONFIG_SPI=y
|
||||
CONFIG_SPI_PL022=y
|
||||
|
@ -33,6 +33,7 @@ CONFIG_STMMAC_ETH=y
|
||||
CONFIG_SERIAL_AMBA_PL011=y
|
||||
CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
|
||||
CONFIG_I2C=y
|
||||
CONFIG_I2C_DESIGNWARE_CORE=y
|
||||
CONFIG_I2C_DESIGNWARE_PLATFORM=y
|
||||
CONFIG_SPI=y
|
||||
CONFIG_SPI_PL022=y
|
||||
|
@ -518,6 +518,7 @@ CONFIG_I2C_MUX=y
|
||||
CONFIG_I2C_MUX_PCA954x=y
|
||||
CONFIG_I2C_BCM2835=m
|
||||
CONFIG_I2C_CADENCE=m
|
||||
CONFIG_I2C_DESIGNWARE_CORE=y
|
||||
CONFIG_I2C_DESIGNWARE_PLATFORM=y
|
||||
CONFIG_I2C_GPIO=m
|
||||
CONFIG_I2C_IMX=y
|
||||
|
@ -31,6 +31,7 @@ CONFIG_MICROSEMI_PHY=y
|
||||
CONFIG_I2C=y
|
||||
CONFIG_I2C_CHARDEV=y
|
||||
CONFIG_I2C_MUX=y
|
||||
CONFIG_I2C_DESIGNWARE_CORE=y
|
||||
CONFIG_I2C_DESIGNWARE_PLATFORM=y
|
||||
|
||||
CONFIG_SPI=y
|
||||
|
@ -157,6 +157,7 @@ CONFIG_HW_RANDOM_VIRTIO=y
|
||||
CONFIG_HW_RANDOM_JH7110=m
|
||||
CONFIG_I2C=y
|
||||
CONFIG_I2C_CHARDEV=m
|
||||
CONFIG_I2C_DESIGNWARE_CORE=y
|
||||
CONFIG_I2C_DESIGNWARE_PLATFORM=y
|
||||
CONFIG_I2C_MV64XXX=m
|
||||
CONFIG_I2C_RIIC=y
|
||||
|
@ -58,6 +58,7 @@ CONFIG_I2C=y
|
||||
# CONFIG_I2C_COMPAT is not set
|
||||
CONFIG_I2C_CHARDEV=y
|
||||
# CONFIG_I2C_HELPER_AUTO is not set
|
||||
CONFIG_I2C_DESIGNWARE_CORE=y
|
||||
CONFIG_I2C_DESIGNWARE_PLATFORM=y
|
||||
CONFIG_SPI=y
|
||||
# CONFIG_SPI_MEM is not set
|
||||
|
@ -50,6 +50,7 @@ CONFIG_DEVTMPFS_MOUNT=y
|
||||
CONFIG_I2C=y
|
||||
CONFIG_I2C_CHARDEV=y
|
||||
# CONFIG_I2C_HELPER_AUTO is not set
|
||||
CONFIG_I2C_DESIGNWARE_CORE=y
|
||||
CONFIG_I2C_DESIGNWARE_PLATFORM=y
|
||||
CONFIG_SPI=y
|
||||
# CONFIG_SPI_MEM is not set
|
||||
|
@ -40,14 +40,6 @@ config I2C_BOARDINFO
|
||||
bool
|
||||
default y
|
||||
|
||||
config I2C_COMPAT
|
||||
bool "Enable compatibility bits for old user-space"
|
||||
default y
|
||||
help
|
||||
Say Y here if you intend to run lm-sensors 3.1.1 or older, or any
|
||||
other user-space package which expects i2c adapters to be class
|
||||
devices. If you don't know, say Y.
|
||||
|
||||
config I2C_CHARDEV
|
||||
tristate "I2C device interface"
|
||||
help
|
||||
|
@ -559,28 +559,33 @@ config I2C_DAVINCI
|
||||
For details please see http://www.ti.com/davinci
|
||||
|
||||
config I2C_DESIGNWARE_CORE
|
||||
tristate
|
||||
tristate "Synopsys DesignWare I2C adapter"
|
||||
select REGMAP
|
||||
help
|
||||
This option enables support for the Synopsys DesignWare I2C adapter.
|
||||
This driver includes support for the I2C host on the Synopsys
|
||||
Designware I2C adapter.
|
||||
|
||||
To compile the driver as a module, choose M here: the module will be
|
||||
called i2c-designware-core.
|
||||
|
||||
if I2C_DESIGNWARE_CORE
|
||||
|
||||
config I2C_DESIGNWARE_SLAVE
|
||||
bool "Synopsys DesignWare Slave"
|
||||
depends on I2C_DESIGNWARE_CORE
|
||||
select I2C_SLAVE
|
||||
help
|
||||
If you say yes to this option, support will be included for the
|
||||
Synopsys DesignWare I2C slave adapter.
|
||||
|
||||
This is not a standalone module, this module compiles together with
|
||||
i2c-designware-core.
|
||||
|
||||
config I2C_DESIGNWARE_PLATFORM
|
||||
tristate "Synopsys DesignWare Platform"
|
||||
tristate "Synopsys DesignWare Platform driver"
|
||||
depends on (ACPI && COMMON_CLK) || !ACPI
|
||||
select I2C_DESIGNWARE_CORE
|
||||
select MFD_SYSCON if MIPS_BAIKAL_T1
|
||||
default I2C_DESIGNWARE_CORE
|
||||
help
|
||||
If you say yes to this option, support will be included for the
|
||||
Synopsys DesignWare I2C adapter.
|
||||
Synopsys DesignWare I2C adapters on the platform bus.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-designware-platform.
|
||||
@ -613,17 +618,19 @@ config I2C_DESIGNWARE_BAYTRAIL
|
||||
a BayTrail system using the AXP288.
|
||||
|
||||
config I2C_DESIGNWARE_PCI
|
||||
tristate "Synopsys DesignWare PCI"
|
||||
tristate "Synopsys DesignWare PCI driver"
|
||||
depends on PCI
|
||||
select I2C_DESIGNWARE_CORE
|
||||
select I2C_CCGX_UCSI
|
||||
help
|
||||
If you say yes to this option, support will be included for the
|
||||
Synopsys DesignWare I2C adapter. Only master mode is supported.
|
||||
Synopsys DesignWare I2C adapters on the PCI bus. Only master mode is
|
||||
supported.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-designware-pci.
|
||||
|
||||
endif
|
||||
|
||||
config I2C_DIGICOLOR
|
||||
tristate "Conexant Digicolor I2C driver"
|
||||
depends on ARCH_DIGICOLOR || COMPILE_TEST
|
||||
@ -772,6 +779,17 @@ config I2C_JZ4780
|
||||
|
||||
If you don't know what to do here, say N.
|
||||
|
||||
config I2C_KEBA
|
||||
tristate "KEBA I2C controller support"
|
||||
depends on HAS_IOMEM
|
||||
select AUXILIARY_BUS
|
||||
help
|
||||
This driver supports the I2C controller found in KEBA system FPGA
|
||||
devices.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-keba.
|
||||
|
||||
config I2C_KEMPLD
|
||||
tristate "Kontron COM I2C Controller"
|
||||
depends on MFD_KEMPLD
|
||||
|
@ -76,6 +76,7 @@ obj-$(CONFIG_I2C_IMX) += i2c-imx.o
|
||||
obj-$(CONFIG_I2C_IMX_LPI2C) += i2c-imx-lpi2c.o
|
||||
obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o
|
||||
obj-$(CONFIG_I2C_JZ4780) += i2c-jz4780.o
|
||||
obj-$(CONFIG_I2C_KEBA) += i2c-keba.o
|
||||
obj-$(CONFIG_I2C_KEMPLD) += i2c-kempld.o
|
||||
obj-$(CONFIG_I2C_LPC2K) += i2c-lpc2k.o
|
||||
obj-$(CONFIG_I2C_LS2X) += i2c-ls2x.o
|
||||
|
@ -479,9 +479,8 @@ static struct i2c_adapter ali1535_adapter = {
|
||||
|
||||
static const struct pci_device_id ali1535_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101) },
|
||||
{ },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, ali1535_ids);
|
||||
|
||||
static int ali1535_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
|
@ -340,7 +340,7 @@ static void i2c_amd_remove(struct platform_device *pdev)
|
||||
|
||||
static const struct acpi_device_id i2c_amd_acpi_match[] = {
|
||||
{ "AMDI0011" },
|
||||
{ },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, i2c_amd_acpi_match);
|
||||
|
||||
|
@ -991,7 +991,7 @@ static const struct of_device_id aspeed_i2c_bus_of_table[] = {
|
||||
.compatible = "aspeed,ast2600-i2c-bus",
|
||||
.data = aspeed_i2c_25xx_get_clk_reg_val,
|
||||
},
|
||||
{ },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, aspeed_i2c_bus_of_table);
|
||||
|
||||
|
@ -20,12 +20,17 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/swab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/units.h>
|
||||
|
||||
#define DEFAULT_SYMBOL_NAMESPACE I2C_DW_COMMON
|
||||
|
||||
#include "i2c-designware-core.h"
|
||||
|
||||
static char *abort_sources[] = {
|
||||
@ -188,7 +193,7 @@ static const u32 supported_speeds[] = {
|
||||
I2C_MAX_STANDARD_MODE_FREQ,
|
||||
};
|
||||
|
||||
int i2c_dw_validate_speed(struct dw_i2c_dev *dev)
|
||||
static int i2c_dw_validate_speed(struct dw_i2c_dev *dev)
|
||||
{
|
||||
struct i2c_timings *t = &dev->timings;
|
||||
unsigned int i;
|
||||
@ -208,7 +213,44 @@ int i2c_dw_validate_speed(struct dw_i2c_dev *dev)
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_dw_validate_speed);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define MSCC_ICPU_CFG_TWI_DELAY 0x0
|
||||
#define MSCC_ICPU_CFG_TWI_DELAY_ENABLE BIT(0)
|
||||
#define MSCC_ICPU_CFG_TWI_SPIKE_FILTER 0x4
|
||||
|
||||
static int mscc_twi_set_sda_hold_time(struct dw_i2c_dev *dev)
|
||||
{
|
||||
writel((dev->sda_hold_time << 1) | MSCC_ICPU_CFG_TWI_DELAY_ENABLE,
|
||||
dev->ext + MSCC_ICPU_CFG_TWI_DELAY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void i2c_dw_of_configure(struct device *device)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(device);
|
||||
struct dw_i2c_dev *dev = dev_get_drvdata(device);
|
||||
|
||||
switch (dev->flags & MODEL_MASK) {
|
||||
case MODEL_MSCC_OCELOT:
|
||||
dev->ext = devm_platform_ioremap_resource(pdev, 1);
|
||||
if (!IS_ERR(dev->ext))
|
||||
dev->set_sda_hold_time = mscc_twi_set_sda_hold_time;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#else /* CONFIG_OF */
|
||||
|
||||
static inline void i2c_dw_of_configure(struct device *device) { }
|
||||
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
|
||||
@ -255,7 +297,7 @@ static void i2c_dw_acpi_params(struct device *device, char method[],
|
||||
kfree(buf.pointer);
|
||||
}
|
||||
|
||||
int i2c_dw_acpi_configure(struct device *device)
|
||||
static void i2c_dw_acpi_configure(struct device *device)
|
||||
{
|
||||
struct dw_i2c_dev *dev = dev_get_drvdata(device);
|
||||
struct i2c_timings *t = &dev->timings;
|
||||
@ -285,10 +327,7 @@ int i2c_dw_acpi_configure(struct device *device)
|
||||
dev->sda_hold_time = fs_ht;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_dw_acpi_configure);
|
||||
|
||||
static u32 i2c_dw_acpi_round_bus_speed(struct device *device)
|
||||
{
|
||||
@ -310,11 +349,13 @@ static u32 i2c_dw_acpi_round_bus_speed(struct device *device)
|
||||
|
||||
#else /* CONFIG_ACPI */
|
||||
|
||||
static inline void i2c_dw_acpi_configure(struct device *device) { }
|
||||
|
||||
static inline u32 i2c_dw_acpi_round_bus_speed(struct device *device) { return 0; }
|
||||
|
||||
#endif /* CONFIG_ACPI */
|
||||
|
||||
void i2c_dw_adjust_bus_speed(struct dw_i2c_dev *dev)
|
||||
static void i2c_dw_adjust_bus_speed(struct dw_i2c_dev *dev)
|
||||
{
|
||||
u32 acpi_speed = i2c_dw_acpi_round_bus_speed(dev->dev);
|
||||
struct i2c_timings *t = &dev->timings;
|
||||
@ -330,10 +371,47 @@ void i2c_dw_adjust_bus_speed(struct dw_i2c_dev *dev)
|
||||
else
|
||||
t->bus_freq_hz = I2C_MAX_FAST_MODE_FREQ;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_dw_adjust_bus_speed);
|
||||
|
||||
u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset)
|
||||
int i2c_dw_fw_parse_and_configure(struct dw_i2c_dev *dev)
|
||||
{
|
||||
struct i2c_timings *t = &dev->timings;
|
||||
struct device *device = dev->dev;
|
||||
struct fwnode_handle *fwnode = dev_fwnode(device);
|
||||
|
||||
i2c_parse_fw_timings(device, t, false);
|
||||
|
||||
i2c_dw_adjust_bus_speed(dev);
|
||||
|
||||
if (is_of_node(fwnode))
|
||||
i2c_dw_of_configure(device);
|
||||
else if (is_acpi_node(fwnode))
|
||||
i2c_dw_acpi_configure(device);
|
||||
|
||||
return i2c_dw_validate_speed(dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_dw_fw_parse_and_configure);
|
||||
|
||||
static u32 i2c_dw_read_scl_reg(struct dw_i2c_dev *dev, u32 reg)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = i2c_dw_acquire_lock(dev);
|
||||
if (ret)
|
||||
return 0;
|
||||
|
||||
ret = regmap_read(dev->map, reg, &val);
|
||||
i2c_dw_release_lock(dev);
|
||||
|
||||
return ret ? 0 : val;
|
||||
}
|
||||
|
||||
u32 i2c_dw_scl_hcnt(struct dw_i2c_dev *dev, unsigned int reg, u32 ic_clk,
|
||||
u32 tSYMBOL, u32 tf, int cond, int offset)
|
||||
{
|
||||
if (!ic_clk)
|
||||
return i2c_dw_read_scl_reg(dev, reg);
|
||||
|
||||
/*
|
||||
* DesignWare I2C core doesn't seem to have solid strategy to meet
|
||||
* the tHD;STA timing spec. Configuring _HCNT based on tHIGH spec
|
||||
@ -372,8 +450,12 @@ u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset)
|
||||
3 + offset;
|
||||
}
|
||||
|
||||
u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset)
|
||||
u32 i2c_dw_scl_lcnt(struct dw_i2c_dev *dev, unsigned int reg, u32 ic_clk,
|
||||
u32 tLOW, u32 tf, int offset)
|
||||
{
|
||||
if (!ic_clk)
|
||||
return i2c_dw_read_scl_reg(dev, reg);
|
||||
|
||||
/*
|
||||
* Conditional expression:
|
||||
*
|
||||
@ -653,6 +735,84 @@ void i2c_dw_disable(struct dw_i2c_dev *dev)
|
||||
|
||||
i2c_dw_release_lock(dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_dw_disable);
|
||||
|
||||
int i2c_dw_probe(struct dw_i2c_dev *dev)
|
||||
{
|
||||
device_set_node(&dev->adapter.dev, dev_fwnode(dev->dev));
|
||||
|
||||
switch (dev->mode) {
|
||||
case DW_IC_SLAVE:
|
||||
return i2c_dw_probe_slave(dev);
|
||||
case DW_IC_MASTER:
|
||||
return i2c_dw_probe_master(dev);
|
||||
default:
|
||||
dev_err(dev->dev, "Wrong operation mode: %d\n", dev->mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_dw_probe);
|
||||
|
||||
static int i2c_dw_prepare(struct device *device)
|
||||
{
|
||||
/*
|
||||
* If the ACPI companion device object is present for this device,
|
||||
* it may be accessed during suspend and resume of other devices via
|
||||
* I2C operation regions, so tell the PM core and middle layers to
|
||||
* avoid skipping system suspend/resume callbacks for it in that case.
|
||||
*/
|
||||
return !has_acpi_companion(device);
|
||||
}
|
||||
|
||||
static int i2c_dw_runtime_suspend(struct device *device)
|
||||
{
|
||||
struct dw_i2c_dev *dev = dev_get_drvdata(device);
|
||||
|
||||
if (dev->shared_with_punit)
|
||||
return 0;
|
||||
|
||||
i2c_dw_disable(dev);
|
||||
i2c_dw_prepare_clk(dev, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_dw_suspend(struct device *device)
|
||||
{
|
||||
struct dw_i2c_dev *dev = dev_get_drvdata(device);
|
||||
|
||||
i2c_mark_adapter_suspended(&dev->adapter);
|
||||
|
||||
return i2c_dw_runtime_suspend(device);
|
||||
}
|
||||
|
||||
static int i2c_dw_runtime_resume(struct device *device)
|
||||
{
|
||||
struct dw_i2c_dev *dev = dev_get_drvdata(device);
|
||||
|
||||
if (!dev->shared_with_punit)
|
||||
i2c_dw_prepare_clk(dev, true);
|
||||
|
||||
dev->init(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_dw_resume(struct device *device)
|
||||
{
|
||||
struct dw_i2c_dev *dev = dev_get_drvdata(device);
|
||||
|
||||
i2c_dw_runtime_resume(device);
|
||||
i2c_mark_adapter_resumed(&dev->adapter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_GPL_DEV_PM_OPS(i2c_dw_dev_pm_ops) = {
|
||||
.prepare = pm_sleep_ptr(i2c_dw_prepare),
|
||||
LATE_SYSTEM_SLEEP_PM_OPS(i2c_dw_suspend, i2c_dw_resume)
|
||||
RUNTIME_PM_OPS(i2c_dw_runtime_suspend, i2c_dw_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter core");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -10,11 +10,10 @@
|
||||
*/
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/compiler_types.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/dev_printk.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
@ -237,7 +236,6 @@ struct reset_control;
|
||||
* @semaphore_idx: Index of table with semaphore type attached to the bus. It's
|
||||
* -1 if there is no semaphore.
|
||||
* @shared_with_punit: true if this bus is shared with the SoCs PUNIT
|
||||
* @disable: function to disable the controller
|
||||
* @init: function to initialize the I2C hardware
|
||||
* @set_sda_hold_time: callback to retrieve IP specific SDA hold timing
|
||||
* @mode: operation mode - DW_IC_MASTER or DW_IC_SLAVE
|
||||
@ -295,7 +293,6 @@ struct dw_i2c_dev {
|
||||
void (*release_lock)(void);
|
||||
int semaphore_idx;
|
||||
bool shared_with_punit;
|
||||
void (*disable)(struct dw_i2c_dev *dev);
|
||||
int (*init)(struct dw_i2c_dev *dev);
|
||||
int (*set_sda_hold_time)(struct dw_i2c_dev *dev);
|
||||
int mode;
|
||||
@ -329,8 +326,10 @@ struct i2c_dw_semaphore_callbacks {
|
||||
};
|
||||
|
||||
int i2c_dw_init_regmap(struct dw_i2c_dev *dev);
|
||||
u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset);
|
||||
u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset);
|
||||
u32 i2c_dw_scl_hcnt(struct dw_i2c_dev *dev, unsigned int reg, u32 ic_clk,
|
||||
u32 tSYMBOL, u32 tf, int cond, int offset);
|
||||
u32 i2c_dw_scl_lcnt(struct dw_i2c_dev *dev, unsigned int reg, u32 ic_clk,
|
||||
u32 tLOW, u32 tf, int offset);
|
||||
int i2c_dw_set_sda_hold(struct dw_i2c_dev *dev);
|
||||
u32 i2c_dw_clk_rate(struct dw_i2c_dev *dev);
|
||||
int i2c_dw_prepare_clk(struct dw_i2c_dev *dev, bool prepare);
|
||||
@ -340,7 +339,8 @@ int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev);
|
||||
int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev);
|
||||
int i2c_dw_set_fifo_size(struct dw_i2c_dev *dev);
|
||||
u32 i2c_dw_func(struct i2c_adapter *adap);
|
||||
void i2c_dw_disable(struct dw_i2c_dev *dev);
|
||||
|
||||
extern const struct dev_pm_ops i2c_dw_dev_pm_ops;
|
||||
|
||||
static inline void __i2c_dw_enable(struct dw_i2c_dev *dev)
|
||||
{
|
||||
@ -373,6 +373,7 @@ static inline void __i2c_dw_read_intr_mask(struct dw_i2c_dev *dev,
|
||||
}
|
||||
|
||||
void __i2c_dw_disable(struct dw_i2c_dev *dev);
|
||||
void i2c_dw_disable(struct dw_i2c_dev *dev);
|
||||
|
||||
extern void i2c_dw_configure_master(struct dw_i2c_dev *dev);
|
||||
extern int i2c_dw_probe_master(struct dw_i2c_dev *dev);
|
||||
@ -385,19 +386,6 @@ static inline void i2c_dw_configure_slave(struct dw_i2c_dev *dev) { }
|
||||
static inline int i2c_dw_probe_slave(struct dw_i2c_dev *dev) { return -EINVAL; }
|
||||
#endif
|
||||
|
||||
static inline int i2c_dw_probe(struct dw_i2c_dev *dev)
|
||||
{
|
||||
switch (dev->mode) {
|
||||
case DW_IC_SLAVE:
|
||||
return i2c_dw_probe_slave(dev);
|
||||
case DW_IC_MASTER:
|
||||
return i2c_dw_probe_master(dev);
|
||||
default:
|
||||
dev_err(dev->dev, "Wrong operation mode: %d\n", dev->mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void i2c_dw_configure(struct dw_i2c_dev *dev)
|
||||
{
|
||||
if (i2c_detect_slave_mode(dev->dev))
|
||||
@ -406,6 +394,8 @@ static inline void i2c_dw_configure(struct dw_i2c_dev *dev)
|
||||
i2c_dw_configure_master(dev);
|
||||
}
|
||||
|
||||
int i2c_dw_probe(struct dw_i2c_dev *dev);
|
||||
|
||||
#if IS_ENABLED(CONFIG_I2C_DESIGNWARE_BAYTRAIL)
|
||||
int i2c_dw_baytrail_probe_lock_support(struct dw_i2c_dev *dev);
|
||||
#endif
|
||||
@ -414,11 +404,4 @@ int i2c_dw_baytrail_probe_lock_support(struct dw_i2c_dev *dev);
|
||||
int i2c_dw_amdpsp_probe_lock_support(struct dw_i2c_dev *dev);
|
||||
#endif
|
||||
|
||||
int i2c_dw_validate_speed(struct dw_i2c_dev *dev);
|
||||
void i2c_dw_adjust_bus_speed(struct dw_i2c_dev *dev);
|
||||
|
||||
#if IS_ENABLED(CONFIG_ACPI)
|
||||
int i2c_dw_acpi_configure(struct device *device);
|
||||
#else
|
||||
static inline int i2c_dw_acpi_configure(struct device *device) { return -ENODEV; }
|
||||
#endif
|
||||
int i2c_dw_fw_parse_and_configure(struct dw_i2c_dev *dev);
|
||||
|
@ -22,6 +22,8 @@
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#define DEFAULT_SYMBOL_NAMESPACE I2C_DW
|
||||
|
||||
#include "i2c-designware-core.h"
|
||||
|
||||
#define AMD_TIMEOUT_MIN_US 25
|
||||
@ -64,13 +66,17 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
|
||||
if (!dev->ss_hcnt || !dev->ss_lcnt) {
|
||||
ic_clk = i2c_dw_clk_rate(dev);
|
||||
dev->ss_hcnt =
|
||||
i2c_dw_scl_hcnt(ic_clk,
|
||||
i2c_dw_scl_hcnt(dev,
|
||||
DW_IC_SS_SCL_HCNT,
|
||||
ic_clk,
|
||||
4000, /* tHD;STA = tHIGH = 4.0 us */
|
||||
sda_falling_time,
|
||||
0, /* 0: DW default, 1: Ideal */
|
||||
0); /* No offset */
|
||||
dev->ss_lcnt =
|
||||
i2c_dw_scl_lcnt(ic_clk,
|
||||
i2c_dw_scl_lcnt(dev,
|
||||
DW_IC_SS_SCL_LCNT,
|
||||
ic_clk,
|
||||
4700, /* tLOW = 4.7 us */
|
||||
scl_falling_time,
|
||||
0); /* No offset */
|
||||
@ -94,13 +100,17 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
|
||||
} else {
|
||||
ic_clk = i2c_dw_clk_rate(dev);
|
||||
dev->fs_hcnt =
|
||||
i2c_dw_scl_hcnt(ic_clk,
|
||||
i2c_dw_scl_hcnt(dev,
|
||||
DW_IC_FS_SCL_HCNT,
|
||||
ic_clk,
|
||||
260, /* tHIGH = 260 ns */
|
||||
sda_falling_time,
|
||||
0, /* DW default */
|
||||
0); /* No offset */
|
||||
dev->fs_lcnt =
|
||||
i2c_dw_scl_lcnt(ic_clk,
|
||||
i2c_dw_scl_lcnt(dev,
|
||||
DW_IC_FS_SCL_LCNT,
|
||||
ic_clk,
|
||||
500, /* tLOW = 500 ns */
|
||||
scl_falling_time,
|
||||
0); /* No offset */
|
||||
@ -114,13 +124,17 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
|
||||
if (!dev->fs_hcnt || !dev->fs_lcnt) {
|
||||
ic_clk = i2c_dw_clk_rate(dev);
|
||||
dev->fs_hcnt =
|
||||
i2c_dw_scl_hcnt(ic_clk,
|
||||
i2c_dw_scl_hcnt(dev,
|
||||
DW_IC_FS_SCL_HCNT,
|
||||
ic_clk,
|
||||
600, /* tHD;STA = tHIGH = 0.6 us */
|
||||
sda_falling_time,
|
||||
0, /* 0: DW default, 1: Ideal */
|
||||
0); /* No offset */
|
||||
dev->fs_lcnt =
|
||||
i2c_dw_scl_lcnt(ic_clk,
|
||||
i2c_dw_scl_lcnt(dev,
|
||||
DW_IC_FS_SCL_LCNT,
|
||||
ic_clk,
|
||||
1300, /* tLOW = 1.3 us */
|
||||
scl_falling_time,
|
||||
0); /* No offset */
|
||||
@ -142,13 +156,17 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
|
||||
} else if (!dev->hs_hcnt || !dev->hs_lcnt) {
|
||||
ic_clk = i2c_dw_clk_rate(dev);
|
||||
dev->hs_hcnt =
|
||||
i2c_dw_scl_hcnt(ic_clk,
|
||||
i2c_dw_scl_hcnt(dev,
|
||||
DW_IC_HS_SCL_HCNT,
|
||||
ic_clk,
|
||||
160, /* tHIGH = 160 ns */
|
||||
sda_falling_time,
|
||||
0, /* DW default */
|
||||
0); /* No offset */
|
||||
dev->hs_lcnt =
|
||||
i2c_dw_scl_lcnt(ic_clk,
|
||||
i2c_dw_scl_lcnt(dev,
|
||||
DW_IC_HS_SCL_LCNT,
|
||||
ic_clk,
|
||||
320, /* tLOW = 320 ns */
|
||||
scl_falling_time,
|
||||
0); /* No offset */
|
||||
@ -931,7 +949,6 @@ int i2c_dw_probe_master(struct dw_i2c_dev *dev)
|
||||
init_completion(&dev->cmd_complete);
|
||||
|
||||
dev->init = i2c_dw_init_master;
|
||||
dev->disable = i2c_dw_disable;
|
||||
|
||||
ret = i2c_dw_init_regmap(dev);
|
||||
if (ret)
|
||||
@ -1021,3 +1038,4 @@ EXPORT_SYMBOL_GPL(i2c_dw_probe_master);
|
||||
|
||||
MODULE_DESCRIPTION("Synopsys DesignWare I2C bus master adapter");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(I2C_DW_COMMON);
|
||||
|
@ -9,7 +9,6 @@
|
||||
* Copyright (C) 2009 Provigent Ltd.
|
||||
* Copyright (C) 2011, 2015, 2016 Intel Corporation.
|
||||
*/
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/errno.h>
|
||||
@ -19,6 +18,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/sched.h>
|
||||
@ -102,7 +102,7 @@ static u32 mfld_get_clk_rate_khz(struct dw_i2c_dev *dev)
|
||||
|
||||
static int mfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
|
||||
{
|
||||
struct dw_i2c_dev *dev = dev_get_drvdata(&pdev->dev);
|
||||
struct dw_i2c_dev *dev = pci_get_drvdata(pdev);
|
||||
|
||||
switch (pdev->device) {
|
||||
case 0x0817:
|
||||
@ -152,7 +152,7 @@ static u32 navi_amd_get_clk_rate_khz(struct dw_i2c_dev *dev)
|
||||
|
||||
static int navi_amd_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
|
||||
{
|
||||
struct dw_i2c_dev *dev = dev_get_drvdata(&pdev->dev);
|
||||
struct dw_i2c_dev *dev = pci_get_drvdata(pdev);
|
||||
|
||||
dev->flags |= MODEL_AMD_NAVI_GPU | ACCESS_POLLING;
|
||||
dev->timings.bus_freq_hz = I2C_MAX_STANDARD_MODE_FREQ;
|
||||
@ -194,47 +194,6 @@ static struct dw_pci_controller dw_pci_controllers[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static int __maybe_unused i2c_dw_pci_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
|
||||
|
||||
i_dev->disable(i_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused i2c_dw_pci_suspend(struct device *dev)
|
||||
{
|
||||
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
|
||||
|
||||
i2c_mark_adapter_suspended(&i_dev->adapter);
|
||||
|
||||
return i2c_dw_pci_runtime_suspend(dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused i2c_dw_pci_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
|
||||
|
||||
return i_dev->init(i_dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused i2c_dw_pci_resume(struct device *dev)
|
||||
{
|
||||
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = i2c_dw_pci_runtime_resume(dev);
|
||||
|
||||
i2c_mark_adapter_resumed(&i_dev->adapter);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops i2c_dw_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(i2c_dw_pci_suspend, i2c_dw_pci_resume)
|
||||
SET_RUNTIME_PM_OPS(i2c_dw_pci_runtime_suspend, i2c_dw_pci_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct property_entry dgpu_properties[] = {
|
||||
/* USB-C doesn't power the system */
|
||||
PROPERTY_ENTRY_U8("scope", POWER_SUPPLY_SCOPE_DEVICE),
|
||||
@ -253,7 +212,6 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
|
||||
int r;
|
||||
struct dw_pci_controller *controller;
|
||||
struct dw_scl_sda_cfg *cfg;
|
||||
struct i2c_timings *t;
|
||||
|
||||
if (id->driver_data >= ARRAY_SIZE(dw_pci_controllers))
|
||||
return dev_err_probe(&pdev->dev, -EINVAL,
|
||||
@ -288,29 +246,17 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
|
||||
dev->irq = pci_irq_vector(pdev, 0);
|
||||
dev->flags |= controller->flags;
|
||||
|
||||
t = &dev->timings;
|
||||
i2c_parse_fw_timings(&pdev->dev, t, false);
|
||||
|
||||
pci_set_drvdata(pdev, dev);
|
||||
|
||||
if (controller->setup) {
|
||||
r = controller->setup(pdev, controller);
|
||||
if (r) {
|
||||
pci_free_irq_vectors(pdev);
|
||||
if (r)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
i2c_dw_adjust_bus_speed(dev);
|
||||
|
||||
if (has_acpi_companion(&pdev->dev))
|
||||
i2c_dw_acpi_configure(&pdev->dev);
|
||||
|
||||
r = i2c_dw_validate_speed(dev);
|
||||
if (r) {
|
||||
pci_free_irq_vectors(pdev);
|
||||
r = i2c_dw_fw_parse_and_configure(dev);
|
||||
if (r)
|
||||
return r;
|
||||
}
|
||||
|
||||
i2c_dw_configure(dev);
|
||||
|
||||
@ -326,14 +272,11 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
|
||||
adap = &dev->adapter;
|
||||
adap->owner = THIS_MODULE;
|
||||
adap->class = 0;
|
||||
ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev));
|
||||
adap->nr = controller->bus_num;
|
||||
|
||||
r = i2c_dw_probe(dev);
|
||||
if (r) {
|
||||
pci_free_irq_vectors(pdev);
|
||||
if (r)
|
||||
return r;
|
||||
}
|
||||
|
||||
if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU) {
|
||||
dev->slave = i2c_new_ccgx_ucsi(&dev->adapter, dev->irq, &dgpu_node);
|
||||
@ -354,16 +297,15 @@ static void i2c_dw_pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct dw_i2c_dev *dev = pci_get_drvdata(pdev);
|
||||
|
||||
dev->disable(dev);
|
||||
i2c_dw_disable(dev);
|
||||
|
||||
pm_runtime_forbid(&pdev->dev);
|
||||
pm_runtime_get_noresume(&pdev->dev);
|
||||
|
||||
i2c_del_adapter(&dev->adapter);
|
||||
devm_free_irq(&pdev->dev, dev->irq, dev);
|
||||
pci_free_irq_vectors(pdev);
|
||||
}
|
||||
|
||||
static const struct pci_device_id i2_designware_pci_ids[] = {
|
||||
static const struct pci_device_id i2c_designware_pci_ids[] = {
|
||||
/* Medfield */
|
||||
{ PCI_VDEVICE(INTEL, 0x0817), medfield },
|
||||
{ PCI_VDEVICE(INTEL, 0x0818), medfield },
|
||||
@ -409,21 +351,23 @@ static const struct pci_device_id i2_designware_pci_ids[] = {
|
||||
{ PCI_VDEVICE(ATI, 0x73c4), navi_amd },
|
||||
{ PCI_VDEVICE(ATI, 0x7444), navi_amd },
|
||||
{ PCI_VDEVICE(ATI, 0x7464), navi_amd },
|
||||
{ 0,}
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, i2_designware_pci_ids);
|
||||
MODULE_DEVICE_TABLE(pci, i2c_designware_pci_ids);
|
||||
|
||||
static struct pci_driver dw_i2c_driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.id_table = i2_designware_pci_ids,
|
||||
.probe = i2c_dw_pci_probe,
|
||||
.remove = i2c_dw_pci_remove,
|
||||
.driver = {
|
||||
.pm = &i2c_dw_pm_ops,
|
||||
.pm = pm_ptr(&i2c_dw_dev_pm_ops),
|
||||
},
|
||||
.id_table = i2c_designware_pci_ids,
|
||||
};
|
||||
module_pci_driver(dw_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
|
||||
MODULE_DESCRIPTION("Synopsys DesignWare PCI I2C bus adapter");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(I2C_DW);
|
||||
MODULE_IMPORT_NS(I2C_DW_COMMON);
|
||||
|
@ -8,7 +8,6 @@
|
||||
* Copyright (C) 2007 MontaVista Software Inc.
|
||||
* Copyright (C) 2009 Provigent Ltd.
|
||||
*/
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
@ -21,7 +20,6 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
@ -30,7 +28,6 @@
|
||||
#include <linux/reset.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/units.h>
|
||||
|
||||
#include "i2c-designware-core.h"
|
||||
@ -40,29 +37,6 @@ static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev)
|
||||
return clk_get_rate(dev->clk) / KILO;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id dw_i2c_acpi_match[] = {
|
||||
{ "INT33C2", 0 },
|
||||
{ "INT33C3", 0 },
|
||||
{ "INT3432", 0 },
|
||||
{ "INT3433", 0 },
|
||||
{ "INTC10EF", 0 },
|
||||
{ "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 },
|
||||
{ "HISI02A1", 0 },
|
||||
{ "HISI02A2", 0 },
|
||||
{ "HISI02A3", 0 },
|
||||
{ "HYGO0010", ACCESS_INTR_MASK },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, dw_i2c_acpi_match);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
#define BT1_I2C_CTL 0x100
|
||||
#define BT1_I2C_CTL_ADDR_MASK GENMASK(7, 0)
|
||||
@ -120,53 +94,11 @@ static int bt1_i2c_request_regs(struct dw_i2c_dev *dev)
|
||||
dev->map = devm_regmap_init(dev->dev, NULL, dev, &bt1_i2c_cfg);
|
||||
return PTR_ERR_OR_ZERO(dev->map);
|
||||
}
|
||||
|
||||
#define MSCC_ICPU_CFG_TWI_DELAY 0x0
|
||||
#define MSCC_ICPU_CFG_TWI_DELAY_ENABLE BIT(0)
|
||||
#define MSCC_ICPU_CFG_TWI_SPIKE_FILTER 0x4
|
||||
|
||||
static int mscc_twi_set_sda_hold_time(struct dw_i2c_dev *dev)
|
||||
{
|
||||
writel((dev->sda_hold_time << 1) | MSCC_ICPU_CFG_TWI_DELAY_ENABLE,
|
||||
dev->ext + MSCC_ICPU_CFG_TWI_DELAY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_i2c_of_configure(struct platform_device *pdev)
|
||||
{
|
||||
struct dw_i2c_dev *dev = platform_get_drvdata(pdev);
|
||||
|
||||
switch (dev->flags & MODEL_MASK) {
|
||||
case MODEL_MSCC_OCELOT:
|
||||
dev->ext = devm_platform_ioremap_resource(pdev, 1);
|
||||
if (!IS_ERR(dev->ext))
|
||||
dev->set_sda_hold_time = mscc_twi_set_sda_hold_time;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id dw_i2c_of_match[] = {
|
||||
{ .compatible = "snps,designware-i2c", },
|
||||
{ .compatible = "mscc,ocelot-i2c", .data = (void *)MODEL_MSCC_OCELOT },
|
||||
{ .compatible = "baikal,bt1-sys-i2c", .data = (void *)MODEL_BAIKAL_BT1 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dw_i2c_of_match);
|
||||
#else
|
||||
static int bt1_i2c_request_regs(struct dw_i2c_dev *dev)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int dw_i2c_of_configure(struct platform_device *pdev)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int txgbe_i2c_request_regs(struct dw_i2c_dev *dev)
|
||||
@ -238,11 +170,9 @@ static int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev)
|
||||
int i = 0;
|
||||
int ret;
|
||||
|
||||
ptr = i2c_dw_semaphore_cb_table;
|
||||
|
||||
dev->semaphore_idx = -1;
|
||||
|
||||
while (ptr->probe) {
|
||||
for (ptr = i2c_dw_semaphore_cb_table; ptr->probe; ptr++) {
|
||||
ret = ptr->probe(dev);
|
||||
if (ret) {
|
||||
/*
|
||||
@ -254,7 +184,6 @@ static int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev)
|
||||
return ret;
|
||||
|
||||
i++;
|
||||
ptr++;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -278,7 +207,6 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct i2c_adapter *adap;
|
||||
struct dw_i2c_dev *dev;
|
||||
struct i2c_timings *t;
|
||||
int irq, ret;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
@ -307,18 +235,7 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
|
||||
|
||||
reset_control_deassert(dev->rst);
|
||||
|
||||
t = &dev->timings;
|
||||
i2c_parse_fw_timings(&pdev->dev, t, false);
|
||||
|
||||
i2c_dw_adjust_bus_speed(dev);
|
||||
|
||||
if (pdev->dev.of_node)
|
||||
dw_i2c_of_configure(pdev);
|
||||
|
||||
if (has_acpi_companion(&pdev->dev))
|
||||
i2c_dw_acpi_configure(&pdev->dev);
|
||||
|
||||
ret = i2c_dw_validate_speed(dev);
|
||||
ret = i2c_dw_fw_parse_and_configure(dev);
|
||||
if (ret)
|
||||
goto exit_reset;
|
||||
|
||||
@ -346,6 +263,7 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
|
||||
goto exit_reset;
|
||||
|
||||
if (dev->clk) {
|
||||
struct i2c_timings *t = &dev->timings;
|
||||
u64 clk_khz;
|
||||
|
||||
dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz;
|
||||
@ -360,8 +278,6 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
|
||||
adap->owner = THIS_MODULE;
|
||||
adap->class = dmi_check_system(dw_i2c_hwmon_class_dmi) ?
|
||||
I2C_CLASS_HWMON : I2C_CLASS_DEPRECATED;
|
||||
ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev));
|
||||
adap->dev.of_node = pdev->dev.of_node;
|
||||
adap->nr = -1;
|
||||
|
||||
if (dev->flags & ACCESS_NO_IRQ_SUSPEND) {
|
||||
@ -408,7 +324,7 @@ static void dw_i2c_plat_remove(struct platform_device *pdev)
|
||||
|
||||
i2c_del_adapter(&dev->adapter);
|
||||
|
||||
dev->disable(dev);
|
||||
i2c_dw_disable(dev);
|
||||
|
||||
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
@ -419,66 +335,34 @@ static void dw_i2c_plat_remove(struct platform_device *pdev)
|
||||
reset_control_assert(dev->rst);
|
||||
}
|
||||
|
||||
static int dw_i2c_plat_prepare(struct device *dev)
|
||||
{
|
||||
/*
|
||||
* If the ACPI companion device object is present for this device, it
|
||||
* may be accessed during suspend and resume of other devices via I2C
|
||||
* operation regions, so tell the PM core and middle layers to avoid
|
||||
* skipping system suspend/resume callbacks for it in that case.
|
||||
*/
|
||||
return !has_acpi_companion(dev);
|
||||
}
|
||||
|
||||
static int dw_i2c_plat_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
|
||||
|
||||
if (i_dev->shared_with_punit)
|
||||
return 0;
|
||||
|
||||
i_dev->disable(i_dev);
|
||||
i2c_dw_prepare_clk(i_dev, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_i2c_plat_suspend(struct device *dev)
|
||||
{
|
||||
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
|
||||
|
||||
i2c_mark_adapter_suspended(&i_dev->adapter);
|
||||
|
||||
return dw_i2c_plat_runtime_suspend(dev);
|
||||
}
|
||||
|
||||
static int dw_i2c_plat_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
|
||||
|
||||
if (!i_dev->shared_with_punit)
|
||||
i2c_dw_prepare_clk(i_dev, true);
|
||||
|
||||
i_dev->init(i_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_i2c_plat_resume(struct device *dev)
|
||||
{
|
||||
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
|
||||
|
||||
dw_i2c_plat_runtime_resume(dev);
|
||||
i2c_mark_adapter_resumed(&i_dev->adapter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops dw_i2c_dev_pm_ops = {
|
||||
.prepare = pm_sleep_ptr(dw_i2c_plat_prepare),
|
||||
LATE_SYSTEM_SLEEP_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume)
|
||||
RUNTIME_PM_OPS(dw_i2c_plat_runtime_suspend, dw_i2c_plat_runtime_resume, NULL)
|
||||
static const struct of_device_id dw_i2c_of_match[] = {
|
||||
{ .compatible = "snps,designware-i2c", },
|
||||
{ .compatible = "mscc,ocelot-i2c", .data = (void *)MODEL_MSCC_OCELOT },
|
||||
{ .compatible = "baikal,bt1-sys-i2c", .data = (void *)MODEL_BAIKAL_BT1 },
|
||||
{}
|
||||
};
|
||||
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 },
|
||||
{ "HISI02A1", 0 },
|
||||
{ "HISI02A2", 0 },
|
||||
{ "HISI02A3", 0 },
|
||||
{ "HYGO0010", ACCESS_INTR_MASK },
|
||||
{ "INT33C2", 0 },
|
||||
{ "INT33C3", 0 },
|
||||
{ "INT3432", 0 },
|
||||
{ "INT3433", 0 },
|
||||
{ "INTC10EF", 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, dw_i2c_acpi_match);
|
||||
|
||||
static const struct platform_device_id dw_i2c_platform_ids[] = {
|
||||
{ "i2c_designware" },
|
||||
@ -491,9 +375,9 @@ static struct platform_driver dw_i2c_driver = {
|
||||
.remove_new = dw_i2c_plat_remove,
|
||||
.driver = {
|
||||
.name = "i2c_designware",
|
||||
.of_match_table = of_match_ptr(dw_i2c_of_match),
|
||||
.acpi_match_table = ACPI_PTR(dw_i2c_acpi_match),
|
||||
.pm = pm_ptr(&dw_i2c_dev_pm_ops),
|
||||
.of_match_table = dw_i2c_of_match,
|
||||
.acpi_match_table = dw_i2c_acpi_match,
|
||||
.pm = pm_ptr(&i2c_dw_dev_pm_ops),
|
||||
},
|
||||
.id_table = dw_i2c_platform_ids,
|
||||
};
|
||||
@ -513,3 +397,5 @@ module_exit(dw_i2c_exit_driver);
|
||||
MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
|
||||
MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(I2C_DW);
|
||||
MODULE_IMPORT_NS(I2C_DW_COMMON);
|
||||
|
@ -16,6 +16,8 @@
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define DEFAULT_SYMBOL_NAMESPACE I2C_DW
|
||||
|
||||
#include "i2c-designware-core.h"
|
||||
|
||||
static void i2c_dw_configure_fifo_slave(struct dw_i2c_dev *dev)
|
||||
@ -88,7 +90,7 @@ static int i2c_dw_unreg_slave(struct i2c_client *slave)
|
||||
struct dw_i2c_dev *dev = i2c_get_adapdata(slave->adapter);
|
||||
|
||||
regmap_write(dev->map, DW_IC_INTR_MASK, 0);
|
||||
dev->disable(dev);
|
||||
i2c_dw_disable(dev);
|
||||
synchronize_irq(dev->irq);
|
||||
dev->slave = NULL;
|
||||
pm_runtime_put(dev->dev);
|
||||
@ -235,7 +237,6 @@ int i2c_dw_probe_slave(struct dw_i2c_dev *dev)
|
||||
int ret;
|
||||
|
||||
dev->init = i2c_dw_init_slave;
|
||||
dev->disable = i2c_dw_disable;
|
||||
|
||||
ret = i2c_dw_init_regmap(dev);
|
||||
if (ret)
|
||||
@ -279,3 +280,4 @@ EXPORT_SYMBOL_GPL(i2c_dw_probe_slave);
|
||||
MODULE_AUTHOR("Luis Oliveira <lolivei@synopsys.com>");
|
||||
MODULE_DESCRIPTION("Synopsys DesignWare I2C bus slave adapter");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_IMPORT_NS(I2C_DW_COMMON);
|
||||
|
@ -357,7 +357,7 @@ static void dc_i2c_remove(struct platform_device *pdev)
|
||||
|
||||
static const struct of_device_id dc_i2c_match[] = {
|
||||
{ .compatible = "cnxt,cx92755-i2c" },
|
||||
{ },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dc_i2c_match);
|
||||
|
||||
|
@ -67,7 +67,6 @@ struct em_i2c_device {
|
||||
void __iomem *base;
|
||||
struct i2c_adapter adap;
|
||||
struct completion msg_done;
|
||||
struct clk *sclk;
|
||||
struct i2c_client *slave;
|
||||
int irq;
|
||||
};
|
||||
@ -361,6 +360,7 @@ static const struct i2c_algorithm em_i2c_algo = {
|
||||
static int em_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct em_i2c_device *priv;
|
||||
struct clk *sclk;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
@ -373,13 +373,9 @@ static int em_i2c_probe(struct platform_device *pdev)
|
||||
|
||||
strscpy(priv->adap.name, "EMEV2 I2C", sizeof(priv->adap.name));
|
||||
|
||||
priv->sclk = devm_clk_get(&pdev->dev, "sclk");
|
||||
if (IS_ERR(priv->sclk))
|
||||
return PTR_ERR(priv->sclk);
|
||||
|
||||
ret = clk_prepare_enable(priv->sclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
sclk = devm_clk_get_enabled(&pdev->dev, "sclk");
|
||||
if (IS_ERR(sclk))
|
||||
return PTR_ERR(sclk);
|
||||
|
||||
priv->adap.timeout = msecs_to_jiffies(100);
|
||||
priv->adap.retries = 5;
|
||||
@ -397,26 +393,22 @@ static int em_i2c_probe(struct platform_device *pdev)
|
||||
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret < 0)
|
||||
goto err_clk;
|
||||
return ret;
|
||||
priv->irq = ret;
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, priv->irq, em_i2c_irq_handler, 0,
|
||||
"em_i2c", priv);
|
||||
if (ret)
|
||||
goto err_clk;
|
||||
return ret;
|
||||
|
||||
ret = i2c_add_adapter(&priv->adap);
|
||||
|
||||
if (ret)
|
||||
goto err_clk;
|
||||
return ret;
|
||||
|
||||
dev_info(&pdev->dev, "Added i2c controller %d, irq %d\n", priv->adap.nr,
|
||||
priv->irq);
|
||||
|
||||
return 0;
|
||||
|
||||
err_clk:
|
||||
clk_disable_unprepare(priv->sclk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void em_i2c_remove(struct platform_device *dev)
|
||||
@ -424,7 +416,6 @@ static void em_i2c_remove(struct platform_device *dev)
|
||||
struct em_i2c_device *priv = platform_get_drvdata(dev);
|
||||
|
||||
i2c_del_adapter(&priv->adap);
|
||||
clk_disable_unprepare(priv->sclk);
|
||||
}
|
||||
|
||||
static const struct of_device_id em_i2c_ids[] = {
|
||||
|
@ -1763,8 +1763,15 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
|
||||
i801_add_tco(priv);
|
||||
|
||||
/*
|
||||
* adapter.name is used by platform code to find the main I801 adapter
|
||||
* to instantiante i2c_clients, do not change.
|
||||
*/
|
||||
snprintf(priv->adapter.name, sizeof(priv->adapter.name),
|
||||
"SMBus I801 adapter at %04lx", priv->smba);
|
||||
"SMBus %s adapter at %04lx",
|
||||
(priv->features & FEATURE_IDF) ? "I801 IDF" : "I801",
|
||||
priv->smba);
|
||||
|
||||
err = i2c_add_adapter(&priv->adapter);
|
||||
if (err) {
|
||||
platform_device_unregister(priv->tco_pdev);
|
||||
|
@ -559,7 +559,7 @@ static const struct i2c_algorithm lpi2c_imx_algo = {
|
||||
|
||||
static const struct of_device_id lpi2c_imx_of_match[] = {
|
||||
{ .compatible = "fsl,imx7ulp-lpi2c" },
|
||||
{ },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, lpi2c_imx_of_match);
|
||||
|
||||
|
@ -687,7 +687,7 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx, bool atomic)
|
||||
i2c_imx_bus_busy(i2c_imx, 0, atomic);
|
||||
|
||||
/* Disable I2C controller */
|
||||
temp = i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
|
||||
temp = i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN;
|
||||
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
||||
}
|
||||
|
||||
@ -1549,7 +1549,7 @@ static void i2c_imx_remove(struct platform_device *pdev)
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused i2c_imx_runtime_suspend(struct device *dev)
|
||||
static int i2c_imx_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev);
|
||||
|
||||
@ -1558,7 +1558,7 @@ static int __maybe_unused i2c_imx_runtime_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused i2c_imx_runtime_resume(struct device *dev)
|
||||
static int i2c_imx_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
@ -1571,8 +1571,7 @@ static int __maybe_unused i2c_imx_runtime_resume(struct device *dev)
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops i2c_imx_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(i2c_imx_runtime_suspend,
|
||||
i2c_imx_runtime_resume, NULL)
|
||||
RUNTIME_PM_OPS(i2c_imx_runtime_suspend, i2c_imx_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver i2c_imx_driver = {
|
||||
@ -1580,7 +1579,7 @@ static struct platform_driver i2c_imx_driver = {
|
||||
.remove_new = i2c_imx_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.pm = &i2c_imx_pm_ops,
|
||||
.pm = pm_ptr(&i2c_imx_pm_ops),
|
||||
.of_match_table = i2c_imx_dt_ids,
|
||||
.acpi_match_table = i2c_imx_acpi_ids,
|
||||
},
|
||||
|
@ -381,6 +381,15 @@ static int ismt_process_desc(const struct ismt_desc *desc,
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/**
|
||||
* ismt_kill_transaction() - kill current transaction
|
||||
* @priv: iSMT private data
|
||||
*/
|
||||
static void ismt_kill_transaction(struct ismt_priv *priv)
|
||||
{
|
||||
writel(ISMT_GCTRL_KILL, priv->smba + ISMT_GR_GCTRL);
|
||||
}
|
||||
|
||||
/**
|
||||
* ismt_access() - process an SMBus command
|
||||
* @adap: the i2c host adapter
|
||||
@ -623,6 +632,7 @@ static int ismt_access(struct i2c_adapter *adap, u16 addr,
|
||||
dma_unmap_single(dev, dma_addr, dma_size, dma_direction);
|
||||
|
||||
if (unlikely(!time_left)) {
|
||||
ismt_kill_transaction(priv);
|
||||
ret = -ETIMEDOUT;
|
||||
goto out;
|
||||
}
|
||||
|
@ -792,26 +792,22 @@ static int jz4780_i2c_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, i2c);
|
||||
|
||||
i2c->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
i2c->clk = devm_clk_get_enabled(&pdev->dev, NULL);
|
||||
if (IS_ERR(i2c->clk))
|
||||
return PTR_ERR(i2c->clk);
|
||||
|
||||
ret = clk_prepare_enable(i2c->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency",
|
||||
&clk_freq);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "clock-frequency not specified in DT\n");
|
||||
goto err;
|
||||
return ret;
|
||||
}
|
||||
|
||||
i2c->speed = clk_freq / 1000;
|
||||
if (i2c->speed == 0) {
|
||||
ret = -EINVAL;
|
||||
dev_err(&pdev->dev, "clock-frequency minimum is 1000\n");
|
||||
goto err;
|
||||
return ret;
|
||||
}
|
||||
jz4780_i2c_set_speed(i2c);
|
||||
|
||||
@ -827,29 +823,25 @@ static int jz4780_i2c_probe(struct platform_device *pdev)
|
||||
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
return ret;
|
||||
i2c->irq = ret;
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, i2c->irq, jz4780_i2c_irq, 0,
|
||||
dev_name(&pdev->dev), i2c);
|
||||
if (ret)
|
||||
goto err;
|
||||
return ret;
|
||||
|
||||
ret = i2c_add_adapter(&i2c->adap);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
clk_disable_unprepare(i2c->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void jz4780_i2c_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct jz4780_i2c *i2c = platform_get_drvdata(pdev);
|
||||
|
||||
clk_disable_unprepare(i2c->clk);
|
||||
i2c_del_adapter(&i2c->adap);
|
||||
}
|
||||
|
||||
|
598
drivers/i2c/busses/i2c-keba.c
Normal file
598
drivers/i2c/busses/i2c-keba.c
Normal file
@ -0,0 +1,598 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) KEBA Industrial Automation Gmbh 2024
|
||||
*
|
||||
* Driver for KEBA I2C controller FPGA IP core
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/misc/keba.h>
|
||||
|
||||
#define KI2C "i2c-keba"
|
||||
|
||||
#define KI2C_CAPABILITY_REG 0x02
|
||||
#define KI2C_CAPABILITY_CRYPTO 0x01
|
||||
#define KI2C_CAPABILITY_DC 0x02
|
||||
|
||||
#define KI2C_CONTROL_REG 0x04
|
||||
#define KI2C_CONTROL_MEN 0x01
|
||||
#define KI2C_CONTROL_MSTA 0x02
|
||||
#define KI2C_CONTROL_RSTA 0x04
|
||||
#define KI2C_CONTROL_MTX 0x08
|
||||
#define KI2C_CONTROL_TXAK 0x10
|
||||
#define KI2C_CONTROL_DISABLE 0x00
|
||||
|
||||
#define KI2C_CONTROL_DC_REG 0x05
|
||||
#define KI2C_CONTROL_DC_SDA 0x01
|
||||
#define KI2C_CONTROL_DC_SCL 0x02
|
||||
|
||||
#define KI2C_STATUS_REG 0x08
|
||||
#define KI2C_STATUS_IN_USE 0x01
|
||||
#define KI2C_STATUS_ACK_CYC 0x02
|
||||
#define KI2C_STATUS_RXAK 0x04
|
||||
#define KI2C_STATUS_MCF 0x08
|
||||
|
||||
#define KI2C_STATUS_DC_REG 0x09
|
||||
#define KI2C_STATUS_DC_SDA 0x01
|
||||
#define KI2C_STATUS_DC_SCL 0x02
|
||||
|
||||
#define KI2C_DATA_REG 0x0c
|
||||
|
||||
#define KI2C_INUSE_SLEEP_US (2 * USEC_PER_MSEC)
|
||||
#define KI2C_INUSE_TIMEOUT_US (10 * USEC_PER_SEC)
|
||||
|
||||
#define KI2C_POLL_DELAY_US 5
|
||||
|
||||
struct ki2c {
|
||||
struct keba_i2c_auxdev *auxdev;
|
||||
void __iomem *base;
|
||||
struct i2c_adapter adapter;
|
||||
|
||||
struct i2c_client **client;
|
||||
int client_size;
|
||||
};
|
||||
|
||||
static int ki2c_inuse_lock(struct ki2c *ki2c)
|
||||
{
|
||||
u8 sts;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* The I2C controller has an IN_USE bit for locking access to the
|
||||
* controller. This enables the use of I2C controller by other none
|
||||
* Linux processors.
|
||||
*
|
||||
* If the I2C controller is free, then the first read returns
|
||||
* IN_USE == 0. After that the I2C controller is locked and further
|
||||
* reads of IN_USE return 1.
|
||||
*
|
||||
* The I2C controller is unlocked by writing 1 into IN_USE.
|
||||
*
|
||||
* The IN_USE bit acts as a hardware semaphore for the I2C controller.
|
||||
* Poll for semaphore, but sleep while polling to free the CPU.
|
||||
*/
|
||||
ret = readb_poll_timeout(ki2c->base + KI2C_STATUS_REG,
|
||||
sts, (sts & KI2C_STATUS_IN_USE) == 0,
|
||||
KI2C_INUSE_SLEEP_US, KI2C_INUSE_TIMEOUT_US);
|
||||
if (ret)
|
||||
dev_err(&ki2c->auxdev->auxdev.dev, "%s err!\n", __func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ki2c_inuse_unlock(struct ki2c *ki2c)
|
||||
{
|
||||
/* unlock the controller by writing 1 into IN_USE */
|
||||
iowrite8(KI2C_STATUS_IN_USE, ki2c->base + KI2C_STATUS_REG);
|
||||
}
|
||||
|
||||
static int ki2c_wait_for_bit(void __iomem *addr, u8 mask, unsigned long timeout)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
return readb_poll_timeout(addr, val, (val & mask), KI2C_POLL_DELAY_US,
|
||||
jiffies_to_usecs(timeout));
|
||||
}
|
||||
|
||||
static int ki2c_wait_for_mcf(struct ki2c *ki2c)
|
||||
{
|
||||
return ki2c_wait_for_bit(ki2c->base + KI2C_STATUS_REG, KI2C_STATUS_MCF,
|
||||
ki2c->adapter.timeout);
|
||||
}
|
||||
|
||||
static int ki2c_wait_for_data(struct ki2c *ki2c)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ki2c_wait_for_mcf(ki2c);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ki2c_wait_for_bit(ki2c->base + KI2C_STATUS_REG,
|
||||
KI2C_STATUS_ACK_CYC,
|
||||
ki2c->adapter.timeout);
|
||||
}
|
||||
|
||||
static int ki2c_wait_for_data_ack(struct ki2c *ki2c)
|
||||
{
|
||||
unsigned int reg;
|
||||
int ret;
|
||||
|
||||
ret = ki2c_wait_for_data(ki2c);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* RXAK == 0 means ACK reveived */
|
||||
reg = ioread8(ki2c->base + KI2C_STATUS_REG);
|
||||
if (reg & KI2C_STATUS_RXAK)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ki2c_has_capability(struct ki2c *ki2c, unsigned int cap)
|
||||
{
|
||||
unsigned int reg = ioread8(ki2c->base + KI2C_CAPABILITY_REG);
|
||||
|
||||
return (reg & cap) != 0;
|
||||
}
|
||||
|
||||
static int ki2c_get_scl(struct ki2c *ki2c)
|
||||
{
|
||||
unsigned int reg = ioread8(ki2c->base + KI2C_STATUS_DC_REG);
|
||||
|
||||
/* capability KI2C_CAPABILITY_DC required */
|
||||
return (reg & KI2C_STATUS_DC_SCL) != 0;
|
||||
}
|
||||
|
||||
static int ki2c_get_sda(struct ki2c *ki2c)
|
||||
{
|
||||
unsigned int reg = ioread8(ki2c->base + KI2C_STATUS_DC_REG);
|
||||
|
||||
/* capability KI2C_CAPABILITY_DC required */
|
||||
return (reg & KI2C_STATUS_DC_SDA) != 0;
|
||||
}
|
||||
|
||||
static void ki2c_set_scl(struct ki2c *ki2c, int val)
|
||||
{
|
||||
u8 control_dc;
|
||||
|
||||
/* capability KI2C_CAPABILITY_DC and KI2C_CONTROL_MEN = 0 reqired */
|
||||
control_dc = ioread8(ki2c->base + KI2C_CONTROL_DC_REG);
|
||||
if (val)
|
||||
control_dc |= KI2C_CONTROL_DC_SCL;
|
||||
else
|
||||
control_dc &= ~KI2C_CONTROL_DC_SCL;
|
||||
iowrite8(control_dc, ki2c->base + KI2C_CONTROL_DC_REG);
|
||||
}
|
||||
|
||||
/*
|
||||
* Resetting bus bitwise is done by checking SDA and applying clock cycles as
|
||||
* long as SDA is low. 9 clock cycles are applied at most.
|
||||
*
|
||||
* Clock cycles are generated and udelay() determines the duration of clock
|
||||
* cycles. Generated clock rate is 100 KHz and so duration of both clock levels
|
||||
* is: delay in ns = (10^6 / 100) / 2
|
||||
*/
|
||||
#define KI2C_RECOVERY_CLK_CNT (9 * 2)
|
||||
#define KI2C_RECOVERY_UDELAY 5
|
||||
static int ki2c_reset_bus_bitwise(struct ki2c *ki2c)
|
||||
{
|
||||
int val = 1;
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
/* disable I2C controller (MEN = 0) to get direct access to SCL/SDA */
|
||||
iowrite8(0, ki2c->base + KI2C_CONTROL_REG);
|
||||
|
||||
/* generate clock cycles */
|
||||
ki2c_set_scl(ki2c, val);
|
||||
udelay(KI2C_RECOVERY_UDELAY);
|
||||
for (i = 0; i < KI2C_RECOVERY_CLK_CNT; i++) {
|
||||
if (val) {
|
||||
/* SCL shouldn't be low here */
|
||||
if (!ki2c_get_scl(ki2c)) {
|
||||
dev_err(&ki2c->auxdev->auxdev.dev,
|
||||
"SCL is stuck low!\n");
|
||||
ret = -EBUSY;
|
||||
break;
|
||||
}
|
||||
|
||||
/* break if SDA is high */
|
||||
if (ki2c_get_sda(ki2c))
|
||||
break;
|
||||
}
|
||||
|
||||
val = !val;
|
||||
ki2c_set_scl(ki2c, val);
|
||||
udelay(KI2C_RECOVERY_UDELAY);
|
||||
}
|
||||
|
||||
if (!ki2c_get_sda(ki2c)) {
|
||||
dev_err(&ki2c->auxdev->auxdev.dev, "SDA is still low!\n");
|
||||
ret = -EBUSY;
|
||||
}
|
||||
|
||||
/* reenable controller */
|
||||
iowrite8(KI2C_CONTROL_MEN, ki2c->base + KI2C_CONTROL_REG);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Resetting bus bytewise is done by writing start bit, 9 data bits and stop
|
||||
* bit.
|
||||
*
|
||||
* This is not 100% safe. If target is an EEPROM and a write access was
|
||||
* interrupted during the ACK cycle, this approach might not be able to recover
|
||||
* the bus. The reason is, that after the 9 clock cycles the EEPROM will be in
|
||||
* ACK cycle again and will hold SDA low like it did before the start of the
|
||||
* routine. Furthermore the EEPROM might get written one additional byte with
|
||||
* 0xff into it. Thus, use bitwise approach whenever possible, especially when
|
||||
* EEPROMs are on the bus.
|
||||
*/
|
||||
static int ki2c_reset_bus_bytewise(struct ki2c *ki2c)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* hold data line high for 9 clock cycles */
|
||||
iowrite8(0xFF, ki2c->base + KI2C_DATA_REG);
|
||||
|
||||
/* create start condition */
|
||||
iowrite8(KI2C_CONTROL_MEN | KI2C_CONTROL_MTX | KI2C_CONTROL_MSTA | KI2C_CONTROL_TXAK,
|
||||
ki2c->base + KI2C_CONTROL_REG);
|
||||
ret = ki2c_wait_for_mcf(ki2c);
|
||||
if (ret < 0) {
|
||||
dev_err(&ki2c->auxdev->auxdev.dev, "Start condition failed\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* create stop condition */
|
||||
iowrite8(KI2C_CONTROL_MEN | KI2C_CONTROL_MTX | KI2C_CONTROL_TXAK,
|
||||
ki2c->base + KI2C_CONTROL_REG);
|
||||
ret = ki2c_wait_for_mcf(ki2c);
|
||||
if (ret < 0)
|
||||
dev_err(&ki2c->auxdev->auxdev.dev, "Stop condition failed\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ki2c_reset_bus(struct ki2c *ki2c)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ki2c_inuse_lock(ki2c);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* If the I2C controller is capable of direct control of SCL/SDA, then a
|
||||
* bitwise reset is used. Otherwise fall back to bytewise reset.
|
||||
*/
|
||||
if (ki2c_has_capability(ki2c, KI2C_CAPABILITY_DC))
|
||||
ret = ki2c_reset_bus_bitwise(ki2c);
|
||||
else
|
||||
ret = ki2c_reset_bus_bytewise(ki2c);
|
||||
|
||||
ki2c_inuse_unlock(ki2c);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ki2c_write_target_addr(struct ki2c *ki2c, struct i2c_msg *m)
|
||||
{
|
||||
u8 addr;
|
||||
|
||||
addr = m->addr << 1;
|
||||
/* Bit 0 signals RD/WR */
|
||||
if (m->flags & I2C_M_RD)
|
||||
addr |= 0x01;
|
||||
|
||||
iowrite8(addr, ki2c->base + KI2C_DATA_REG);
|
||||
}
|
||||
|
||||
static int ki2c_start_addr(struct ki2c *ki2c, struct i2c_msg *m)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Store target address byte in the controller. This has to be done
|
||||
* before sending START condition.
|
||||
*/
|
||||
ki2c_write_target_addr(ki2c, m);
|
||||
|
||||
/* enable controller for TX */
|
||||
iowrite8(KI2C_CONTROL_MEN | KI2C_CONTROL_MTX,
|
||||
ki2c->base + KI2C_CONTROL_REG);
|
||||
|
||||
/* send START condition and target address byte */
|
||||
iowrite8(KI2C_CONTROL_MEN | KI2C_CONTROL_MTX | KI2C_CONTROL_MSTA,
|
||||
ki2c->base + KI2C_CONTROL_REG);
|
||||
|
||||
ret = ki2c_wait_for_data_ack(ki2c);
|
||||
if (ret < 0)
|
||||
/*
|
||||
* For EEPROMs this is normal behavior during internal write
|
||||
* operation.
|
||||
*/
|
||||
dev_dbg(&ki2c->auxdev->auxdev.dev,
|
||||
"%s wait for ACK err at 0x%02x!\n", __func__, m->addr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ki2c_repstart_addr(struct ki2c *ki2c, struct i2c_msg *m)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* repeated start and write is not supported */
|
||||
if ((m->flags & I2C_M_RD) == 0) {
|
||||
dev_err(&ki2c->auxdev->auxdev.dev,
|
||||
"Repeated start not supported for writes\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* send repeated start */
|
||||
iowrite8(KI2C_CONTROL_MEN | KI2C_CONTROL_MSTA | KI2C_CONTROL_RSTA,
|
||||
ki2c->base + KI2C_CONTROL_REG);
|
||||
|
||||
ret = ki2c_wait_for_mcf(ki2c);
|
||||
if (ret < 0) {
|
||||
dev_err(&ki2c->auxdev->auxdev.dev,
|
||||
"%s wait for MCF err at 0x%02x!\n", __func__, m->addr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* write target-address byte */
|
||||
ki2c_write_target_addr(ki2c, m);
|
||||
|
||||
ret = ki2c_wait_for_data_ack(ki2c);
|
||||
if (ret < 0)
|
||||
dev_err(&ki2c->auxdev->auxdev.dev,
|
||||
"%s wait for ACK err at 0x%02x!\n", __func__, m->addr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ki2c_stop(struct ki2c *ki2c)
|
||||
{
|
||||
iowrite8(KI2C_CONTROL_MEN, ki2c->base + KI2C_CONTROL_REG);
|
||||
ki2c_wait_for_mcf(ki2c);
|
||||
}
|
||||
|
||||
static int ki2c_write(struct ki2c *ki2c, const u8 *data, int len)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
/* write data byte */
|
||||
iowrite8(data[i], ki2c->base + KI2C_DATA_REG);
|
||||
|
||||
ret = ki2c_wait_for_data_ack(ki2c);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ki2c_read(struct ki2c *ki2c, u8 *data, int len)
|
||||
{
|
||||
u8 control;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
if (len == 0)
|
||||
return 0; /* nothing to do */
|
||||
|
||||
control = KI2C_CONTROL_MEN | KI2C_CONTROL_MSTA;
|
||||
|
||||
/* if just one byte => send tx-nack after transfer */
|
||||
if (len == 1)
|
||||
control |= KI2C_CONTROL_TXAK;
|
||||
|
||||
iowrite8(control, ki2c->base + KI2C_CONTROL_REG);
|
||||
|
||||
/* dummy read to start transfer on bus */
|
||||
ioread8(ki2c->base + KI2C_DATA_REG);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
ret = ki2c_wait_for_data(ki2c);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (i == len - 2)
|
||||
/* send tx-nack after transfer of last byte */
|
||||
iowrite8(KI2C_CONTROL_MEN | KI2C_CONTROL_MSTA | KI2C_CONTROL_TXAK,
|
||||
ki2c->base + KI2C_CONTROL_REG);
|
||||
else if (i == len - 1)
|
||||
/*
|
||||
* switch to TX on last byte, so that reading DATA
|
||||
* register does not trigger another read transfer
|
||||
*/
|
||||
iowrite8(KI2C_CONTROL_MEN | KI2C_CONTROL_MSTA | KI2C_CONTROL_MTX,
|
||||
ki2c->base + KI2C_CONTROL_REG);
|
||||
|
||||
/* read byte and start next transfer (if not last byte) */
|
||||
data[i] = ioread8(ki2c->base + KI2C_DATA_REG);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int ki2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
||||
{
|
||||
struct ki2c *ki2c = i2c_get_adapdata(adap);
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
ret = ki2c_inuse_lock(ki2c);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
struct i2c_msg *m = &msgs[i];
|
||||
|
||||
if (i == 0)
|
||||
ret = ki2c_start_addr(ki2c, m);
|
||||
else
|
||||
ret = ki2c_repstart_addr(ki2c, m);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
if (m->flags & I2C_M_RD)
|
||||
ret = ki2c_read(ki2c, m->buf, m->len);
|
||||
else
|
||||
ret = ki2c_write(ki2c, m->buf, m->len);
|
||||
if (ret < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
ki2c_stop(ki2c);
|
||||
|
||||
ki2c_inuse_unlock(ki2c);
|
||||
|
||||
return ret < 0 ? ret : num;
|
||||
}
|
||||
|
||||
static void ki2c_unregister_devices(struct ki2c *ki2c)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ki2c->client_size; i++) {
|
||||
struct i2c_client *client = ki2c->client[i];
|
||||
|
||||
if (client)
|
||||
i2c_unregister_device(client);
|
||||
}
|
||||
}
|
||||
|
||||
static int ki2c_register_devices(struct ki2c *ki2c)
|
||||
{
|
||||
struct i2c_board_info *info = ki2c->auxdev->info;
|
||||
int i;
|
||||
|
||||
/* register all known I2C devices */
|
||||
for (i = 0; i < ki2c->client_size; i++) {
|
||||
struct i2c_client *client;
|
||||
unsigned short const addr_list[2] = { info[i].addr,
|
||||
I2C_CLIENT_END };
|
||||
|
||||
client = i2c_new_scanned_device(&ki2c->adapter, &info[i],
|
||||
addr_list, NULL);
|
||||
if (!IS_ERR(client)) {
|
||||
ki2c->client[i] = client;
|
||||
} else if (PTR_ERR(client) != -ENODEV) {
|
||||
ki2c->client_size = i;
|
||||
ki2c_unregister_devices(ki2c);
|
||||
|
||||
return PTR_ERR(client);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 ki2c_func(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm ki2c_algo = {
|
||||
.master_xfer = ki2c_xfer,
|
||||
.functionality = ki2c_func,
|
||||
};
|
||||
|
||||
static int ki2c_probe(struct auxiliary_device *auxdev,
|
||||
const struct auxiliary_device_id *id)
|
||||
{
|
||||
struct device *dev = &auxdev->dev;
|
||||
struct i2c_adapter *adap;
|
||||
struct ki2c *ki2c;
|
||||
int ret;
|
||||
|
||||
ki2c = devm_kzalloc(dev, sizeof(*ki2c), GFP_KERNEL);
|
||||
if (!ki2c)
|
||||
return -ENOMEM;
|
||||
ki2c->auxdev = container_of(auxdev, struct keba_i2c_auxdev, auxdev);
|
||||
ki2c->client = devm_kcalloc(dev, ki2c->auxdev->info_size,
|
||||
sizeof(*ki2c->client), GFP_KERNEL);
|
||||
if (!ki2c->client)
|
||||
return -ENOMEM;
|
||||
ki2c->client_size = ki2c->auxdev->info_size;
|
||||
auxiliary_set_drvdata(auxdev, ki2c);
|
||||
|
||||
ki2c->base = devm_ioremap_resource(dev, &ki2c->auxdev->io);
|
||||
if (IS_ERR(ki2c->base))
|
||||
return PTR_ERR(ki2c->base);
|
||||
|
||||
adap = &ki2c->adapter;
|
||||
strscpy(adap->name, "KEBA I2C adapter", sizeof(adap->name));
|
||||
adap->owner = THIS_MODULE;
|
||||
adap->class = I2C_CLASS_HWMON;
|
||||
adap->algo = &ki2c_algo;
|
||||
adap->dev.parent = dev;
|
||||
|
||||
i2c_set_adapdata(adap, ki2c);
|
||||
|
||||
/* enable controller */
|
||||
iowrite8(KI2C_CONTROL_MEN, ki2c->base + KI2C_CONTROL_REG);
|
||||
|
||||
/* reset bus before probing I2C devices */
|
||||
ret = ki2c_reset_bus(ki2c);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = devm_i2c_add_adapter(dev, adap);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to add adapter (%d)!\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = ki2c_register_devices(ki2c);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register devices (%d)!\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
iowrite8(KI2C_CONTROL_DISABLE, ki2c->base + KI2C_CONTROL_REG);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ki2c_remove(struct auxiliary_device *auxdev)
|
||||
{
|
||||
struct ki2c *ki2c = auxiliary_get_drvdata(auxdev);
|
||||
|
||||
ki2c_unregister_devices(ki2c);
|
||||
|
||||
/* disable controller */
|
||||
iowrite8(KI2C_CONTROL_DISABLE, ki2c->base + KI2C_CONTROL_REG);
|
||||
|
||||
auxiliary_set_drvdata(auxdev, NULL);
|
||||
}
|
||||
|
||||
static const struct auxiliary_device_id ki2c_devtype_aux[] = {
|
||||
{ .name = "keba.i2c" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(auxiliary, ki2c_devtype_aux);
|
||||
|
||||
static struct auxiliary_driver ki2c_driver_aux = {
|
||||
.name = KI2C,
|
||||
.id_table = ki2c_devtype_aux,
|
||||
.probe = ki2c_probe,
|
||||
.remove = ki2c_remove,
|
||||
};
|
||||
module_auxiliary_driver(ki2c_driver_aux);
|
||||
|
||||
MODULE_AUTHOR("Gerhard Engleder <eg@keba.com>");
|
||||
MODULE_DESCRIPTION("KEBA I2C bus controller driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -107,7 +107,7 @@ static int ljca_i2c_start(struct ljca_i2c_dev *ljca_i2c, u8 target_addr,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ljca_i2c_stop(struct ljca_i2c_dev *ljca_i2c, u8 target_addr)
|
||||
static void ljca_i2c_stop(struct ljca_i2c_dev *ljca_i2c)
|
||||
{
|
||||
struct ljca_i2c_rw_packet *w_packet =
|
||||
(struct ljca_i2c_rw_packet *)ljca_i2c->obuf;
|
||||
@ -178,7 +178,7 @@ static int ljca_i2c_read(struct ljca_i2c_dev *ljca_i2c, u8 target_addr, u8 *data
|
||||
if (!ret)
|
||||
ret = ljca_i2c_pure_read(ljca_i2c, data, len);
|
||||
|
||||
ljca_i2c_stop(ljca_i2c, target_addr);
|
||||
ljca_i2c_stop(ljca_i2c);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -222,7 +222,7 @@ static int ljca_i2c_write(struct ljca_i2c_dev *ljca_i2c, u8 target_addr,
|
||||
if (!ret)
|
||||
ret = ljca_i2c_pure_write(ljca_i2c, data, len);
|
||||
|
||||
ljca_i2c_stop(ljca_i2c, target_addr);
|
||||
ljca_i2c_stop(ljca_i2c);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -88,7 +88,6 @@ struct mpc_i2c {
|
||||
int irq;
|
||||
u32 real_clk;
|
||||
u8 fdr, dfsrr;
|
||||
struct clk *clk_per;
|
||||
u32 cntl_bits;
|
||||
enum mpc_i2c_action action;
|
||||
struct i2c_msg *msgs;
|
||||
@ -779,7 +778,6 @@ static int fsl_i2c_probe(struct platform_device *op)
|
||||
struct clk *clk;
|
||||
int result;
|
||||
u32 clock;
|
||||
int err;
|
||||
|
||||
i2c = devm_kzalloc(&op->dev, sizeof(*i2c), GFP_KERNEL);
|
||||
if (!i2c)
|
||||
@ -809,18 +807,12 @@ static int fsl_i2c_probe(struct platform_device *op)
|
||||
* enable clock for the I2C peripheral (non fatal),
|
||||
* keep a reference upon successful allocation
|
||||
*/
|
||||
clk = devm_clk_get_optional(&op->dev, NULL);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
err = clk_prepare_enable(clk);
|
||||
if (err) {
|
||||
clk = devm_clk_get_optional_enabled(&op->dev, NULL);
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&op->dev, "failed to enable clock\n");
|
||||
return err;
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
|
||||
i2c->clk_per = clk;
|
||||
|
||||
if (of_property_read_bool(op->dev.of_node, "fsl,preserve-clocking")) {
|
||||
clock = MPC_I2C_CLOCK_PRESERVE;
|
||||
} else {
|
||||
@ -876,14 +868,9 @@ static int fsl_i2c_probe(struct platform_device *op)
|
||||
|
||||
result = i2c_add_numbered_adapter(&i2c->adap);
|
||||
if (result)
|
||||
goto fail_add;
|
||||
return result;
|
||||
|
||||
return 0;
|
||||
|
||||
fail_add:
|
||||
clk_disable_unprepare(i2c->clk_per);
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
static void fsl_i2c_remove(struct platform_device *op)
|
||||
@ -891,8 +878,6 @@ static void fsl_i2c_remove(struct platform_device *op)
|
||||
struct mpc_i2c *i2c = platform_get_drvdata(op);
|
||||
|
||||
i2c_del_adapter(&i2c->adap);
|
||||
|
||||
clk_disable_unprepare(i2c->clk_per);
|
||||
};
|
||||
|
||||
static int __maybe_unused mpc_i2c_suspend(struct device *dev)
|
||||
|
@ -1306,12 +1306,9 @@ err_exit:
|
||||
static irqreturn_t mtk_i2c_irq(int irqno, void *dev_id)
|
||||
{
|
||||
struct mtk_i2c *i2c = dev_id;
|
||||
u16 restart_flag = 0;
|
||||
u16 restart_flag = i2c->auto_restart ? I2C_RS_TRANSFER : 0;
|
||||
u16 intr_stat;
|
||||
|
||||
if (i2c->auto_restart)
|
||||
restart_flag = I2C_RS_TRANSFER;
|
||||
|
||||
intr_stat = mtk_i2c_readw(i2c, OFFSET_INTR_STAT);
|
||||
mtk_i2c_writew(i2c, intr_stat, OFFSET_INTR_STAT);
|
||||
|
||||
|
@ -136,11 +136,13 @@ enum i2c_addr {
|
||||
* Since the addr regs are sprinkled all over the address space,
|
||||
* use this array to get the address or each register.
|
||||
*/
|
||||
#define I2C_NUM_OWN_ADDR 2
|
||||
#define I2C_NUM_OWN_ADDR 10
|
||||
#define I2C_NUM_OWN_ADDR_SUPPORTED 2
|
||||
|
||||
static const int npcm_i2caddr[I2C_NUM_OWN_ADDR] = {
|
||||
NPCM_I2CADDR1, NPCM_I2CADDR2,
|
||||
NPCM_I2CADDR1, NPCM_I2CADDR2, NPCM_I2CADDR3, NPCM_I2CADDR4,
|
||||
NPCM_I2CADDR5, NPCM_I2CADDR6, NPCM_I2CADDR7, NPCM_I2CADDR8,
|
||||
NPCM_I2CADDR9, NPCM_I2CADDR10,
|
||||
};
|
||||
#endif
|
||||
|
||||
|
@ -1261,7 +1261,7 @@ static const struct of_device_id omap_i2c_of_match[] = {
|
||||
.compatible = "ti,omap2420-i2c",
|
||||
.data = &omap2420_pdata,
|
||||
},
|
||||
{ },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, omap_i2c_of_match);
|
||||
#endif
|
||||
|
@ -146,7 +146,7 @@ static const struct dmi_system_id piix4_dmi_ibm[] = {
|
||||
.ident = "IBM",
|
||||
.matches = { DMI_MATCH(DMI_SYS_VENDOR, "IBM"), },
|
||||
},
|
||||
{ },
|
||||
{ }
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -721,7 +721,7 @@ static void i2c_pnx_remove(struct platform_device *pdev)
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id i2c_pnx_of_match[] = {
|
||||
{ .compatible = "nxp,pnx-i2c" },
|
||||
{ },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, i2c_pnx_of_match);
|
||||
#endif
|
||||
|
@ -135,7 +135,7 @@ err_dev_add:
|
||||
|
||||
static const struct pci_device_id ce4100_i2c_devices[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x2e68)},
|
||||
{ },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct pci_driver ce4100_i2c_driver = {
|
||||
|
@ -218,7 +218,7 @@ static const struct platform_device_id i2c_pxa_id_table[] = {
|
||||
{ "ce4100-i2c", REGS_CE4100 },
|
||||
{ "pxa910-i2c", REGS_PXA910 },
|
||||
{ "armada-3700-i2c", REGS_A3700 },
|
||||
{ },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, i2c_pxa_id_table);
|
||||
|
||||
|
@ -721,7 +721,7 @@ static const struct i2c_algorithm geni_i2c_algo = {
|
||||
static const struct acpi_device_id geni_i2c_acpi_match[] = {
|
||||
{ "QCOM0220"},
|
||||
{ "QCOM0411" },
|
||||
{ },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, geni_i2c_acpi_match);
|
||||
#endif
|
||||
@ -984,21 +984,24 @@ static int __maybe_unused geni_i2c_runtime_resume(struct device *dev)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(gi2c->core_clk);
|
||||
if (ret) {
|
||||
geni_icc_disable(&gi2c->se);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
goto out_icc_disable;
|
||||
|
||||
ret = geni_se_resources_on(&gi2c->se);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(gi2c->core_clk);
|
||||
geni_icc_disable(&gi2c->se);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
goto out_clk_disable;
|
||||
|
||||
enable_irq(gi2c->irq);
|
||||
gi2c->suspended = 0;
|
||||
|
||||
return 0;
|
||||
|
||||
out_clk_disable:
|
||||
clk_disable_unprepare(gi2c->core_clk);
|
||||
out_icc_disable:
|
||||
geni_icc_disable(&gi2c->se);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused geni_i2c_suspend_noirq(struct device *dev)
|
||||
|
@ -1648,7 +1648,7 @@ static void qup_i2c_disable_clocks(struct qup_i2c_dev *qup)
|
||||
|
||||
static const struct acpi_device_id qup_i2c_acpi_match[] = {
|
||||
{ "QCOM8010"},
|
||||
{ },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, qup_i2c_acpi_match);
|
||||
|
||||
|
@ -1164,11 +1164,6 @@ static int rcar_i2c_probe(struct platform_device *pdev)
|
||||
rcar_i2c_init(priv);
|
||||
rcar_i2c_reset_slave(priv);
|
||||
|
||||
if (priv->devtype < I2C_RCAR_GEN3) {
|
||||
irqflags |= IRQF_NO_THREAD;
|
||||
irqhandler = rcar_i2c_gen2_irq;
|
||||
}
|
||||
|
||||
/* Stay always active when multi-master to keep arbitration working */
|
||||
if (of_property_read_bool(dev->of_node, "multi-master"))
|
||||
priv->flags |= ID_P_PM_BLOCKED;
|
||||
@ -1178,8 +1173,11 @@ static int rcar_i2c_probe(struct platform_device *pdev)
|
||||
if (of_property_read_bool(dev->of_node, "smbus"))
|
||||
priv->flags |= ID_P_HOST_NOTIFY;
|
||||
|
||||
/* R-Car Gen3+ needs a reset before every transfer */
|
||||
if (priv->devtype >= I2C_RCAR_GEN3) {
|
||||
if (priv->devtype < I2C_RCAR_GEN3) {
|
||||
irqflags |= IRQF_NO_THREAD;
|
||||
irqhandler = rcar_i2c_gen2_irq;
|
||||
} else {
|
||||
/* R-Car Gen3+ needs a reset before every transfer */
|
||||
priv->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
|
||||
if (IS_ERR(priv->rstc)) {
|
||||
ret = PTR_ERR(priv->rstc);
|
||||
|
@ -63,6 +63,8 @@
|
||||
#define ICMR3_ACKWP 0x10
|
||||
#define ICMR3_ACKBT 0x08
|
||||
|
||||
#define ICFER_FMPE 0x80
|
||||
|
||||
#define ICIER_TIE 0x80
|
||||
#define ICIER_TEIE 0x40
|
||||
#define ICIER_RIE 0x20
|
||||
@ -80,6 +82,7 @@ enum riic_reg_list {
|
||||
RIIC_ICCR2,
|
||||
RIIC_ICMR1,
|
||||
RIIC_ICMR3,
|
||||
RIIC_ICFER,
|
||||
RIIC_ICSER,
|
||||
RIIC_ICIER,
|
||||
RIIC_ICSR2,
|
||||
@ -91,7 +94,8 @@ enum riic_reg_list {
|
||||
};
|
||||
|
||||
struct riic_of_data {
|
||||
u8 regs[RIIC_REG_END];
|
||||
const u8 *regs;
|
||||
bool fast_mode_plus;
|
||||
};
|
||||
|
||||
struct riic_dev {
|
||||
@ -105,6 +109,8 @@ struct riic_dev {
|
||||
struct completion msg_done;
|
||||
struct i2c_adapter adapter;
|
||||
struct clk *clk;
|
||||
struct reset_control *rstc;
|
||||
struct i2c_timings i2c_t;
|
||||
};
|
||||
|
||||
struct riic_irq_desc {
|
||||
@ -131,11 +137,14 @@ static inline void riic_clear_set_bit(struct riic_dev *riic, u8 clear, u8 set, u
|
||||
static int riic_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
||||
{
|
||||
struct riic_dev *riic = i2c_get_adapdata(adap);
|
||||
struct device *dev = adap->dev.parent;
|
||||
unsigned long time_left;
|
||||
int i;
|
||||
int i, ret;
|
||||
u8 start_bit;
|
||||
|
||||
pm_runtime_get_sync(adap->dev.parent);
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (riic_readb(riic, RIIC_ICCR2) & ICCR2_BBSY) {
|
||||
riic->err = -EBUSY;
|
||||
@ -168,7 +177,8 @@ static int riic_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
||||
}
|
||||
|
||||
out:
|
||||
pm_runtime_put(adap->dev.parent);
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
||||
return riic->err ?: num;
|
||||
}
|
||||
@ -298,21 +308,21 @@ static const struct i2c_algorithm riic_algo = {
|
||||
.functionality = riic_func,
|
||||
};
|
||||
|
||||
static int riic_init_hw(struct riic_dev *riic, struct i2c_timings *t)
|
||||
static int riic_init_hw(struct riic_dev *riic)
|
||||
{
|
||||
int ret = 0;
|
||||
int ret;
|
||||
unsigned long rate;
|
||||
int total_ticks, cks, brl, brh;
|
||||
struct i2c_timings *t = &riic->i2c_t;
|
||||
struct device *dev = riic->adapter.dev.parent;
|
||||
bool fast_mode_plus = riic->info->fast_mode_plus;
|
||||
u32 max_freq = fast_mode_plus ? I2C_MAX_FAST_MODE_PLUS_FREQ
|
||||
: I2C_MAX_FAST_MODE_FREQ;
|
||||
|
||||
pm_runtime_get_sync(riic->adapter.dev.parent);
|
||||
|
||||
if (t->bus_freq_hz > I2C_MAX_FAST_MODE_FREQ) {
|
||||
dev_err(&riic->adapter.dev,
|
||||
"unsupported bus speed (%dHz). %d max\n",
|
||||
t->bus_freq_hz, I2C_MAX_FAST_MODE_FREQ);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (t->bus_freq_hz > max_freq)
|
||||
return dev_err_probe(&riic->adapter.dev, -EINVAL,
|
||||
"unsupported bus speed %uHz (%u max)\n",
|
||||
t->bus_freq_hz, max_freq);
|
||||
|
||||
rate = clk_get_rate(riic->clk);
|
||||
|
||||
@ -349,8 +359,7 @@ static int riic_init_hw(struct riic_dev *riic, struct i2c_timings *t)
|
||||
if (brl > (0x1F + 3)) {
|
||||
dev_err(&riic->adapter.dev, "invalid speed (%lu). Too slow.\n",
|
||||
(unsigned long)t->bus_freq_hz);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
brh = total_ticks - brl;
|
||||
@ -382,6 +391,10 @@ static int riic_init_hw(struct riic_dev *riic, struct i2c_timings *t)
|
||||
t->scl_fall_ns / (1000000000 / rate),
|
||||
t->scl_rise_ns / (1000000000 / rate), cks, brl, brh);
|
||||
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Changing the order of accessing IICRST and ICE may break things! */
|
||||
riic_writeb(riic, ICCR1_IICRST | ICCR1_SOWP, RIIC_ICCR1);
|
||||
riic_clear_set_bit(riic, 0, ICCR1_ICE, RIIC_ICCR1);
|
||||
@ -393,11 +406,14 @@ static int riic_init_hw(struct riic_dev *riic, struct i2c_timings *t)
|
||||
riic_writeb(riic, 0, RIIC_ICSER);
|
||||
riic_writeb(riic, ICMR3_ACKWP | ICMR3_RDRFS, RIIC_ICMR3);
|
||||
|
||||
if (fast_mode_plus && t->bus_freq_hz > I2C_MAX_FAST_MODE_FREQ)
|
||||
riic_clear_set_bit(riic, 0, ICFER_FMPE, RIIC_ICFER);
|
||||
|
||||
riic_clear_set_bit(riic, ICCR1_IICRST, 0, RIIC_ICCR1);
|
||||
|
||||
out:
|
||||
pm_runtime_put(riic->adapter.dev.parent);
|
||||
return ret;
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct riic_irq_desc riic_irqs[] = {
|
||||
@ -415,13 +431,12 @@ static void riic_reset_control_assert(void *data)
|
||||
|
||||
static int riic_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct riic_dev *riic;
|
||||
struct i2c_adapter *adap;
|
||||
struct i2c_timings i2c_t;
|
||||
struct reset_control *rstc;
|
||||
int i, ret;
|
||||
|
||||
riic = devm_kzalloc(&pdev->dev, sizeof(*riic), GFP_KERNEL);
|
||||
riic = devm_kzalloc(dev, sizeof(*riic), GFP_KERNEL);
|
||||
if (!riic)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -429,22 +444,22 @@ static int riic_i2c_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(riic->base))
|
||||
return PTR_ERR(riic->base);
|
||||
|
||||
riic->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
riic->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(riic->clk)) {
|
||||
dev_err(&pdev->dev, "missing controller clock");
|
||||
dev_err(dev, "missing controller clock");
|
||||
return PTR_ERR(riic->clk);
|
||||
}
|
||||
|
||||
rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL);
|
||||
if (IS_ERR(rstc))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(rstc),
|
||||
riic->rstc = devm_reset_control_get_optional_exclusive(dev, NULL);
|
||||
if (IS_ERR(riic->rstc))
|
||||
return dev_err_probe(dev, PTR_ERR(riic->rstc),
|
||||
"Error: missing reset ctrl\n");
|
||||
|
||||
ret = reset_control_deassert(rstc);
|
||||
ret = reset_control_deassert(riic->rstc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action_or_reset(&pdev->dev, riic_reset_control_assert, rstc);
|
||||
ret = devm_add_action_or_reset(dev, riic_reset_control_assert, riic->rstc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -453,31 +468,34 @@ static int riic_i2c_probe(struct platform_device *pdev)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, ret, riic_irqs[i].isr,
|
||||
ret = devm_request_irq(dev, ret, riic_irqs[i].isr,
|
||||
0, riic_irqs[i].name, riic);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to request irq %s\n", riic_irqs[i].name);
|
||||
dev_err(dev, "failed to request irq %s\n", riic_irqs[i].name);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
riic->info = of_device_get_match_data(&pdev->dev);
|
||||
riic->info = of_device_get_match_data(dev);
|
||||
|
||||
adap = &riic->adapter;
|
||||
i2c_set_adapdata(adap, riic);
|
||||
strscpy(adap->name, "Renesas RIIC adapter", sizeof(adap->name));
|
||||
adap->owner = THIS_MODULE;
|
||||
adap->algo = &riic_algo;
|
||||
adap->dev.parent = &pdev->dev;
|
||||
adap->dev.of_node = pdev->dev.of_node;
|
||||
adap->dev.parent = dev;
|
||||
adap->dev.of_node = dev->of_node;
|
||||
|
||||
init_completion(&riic->msg_done);
|
||||
|
||||
i2c_parse_fw_timings(&pdev->dev, &i2c_t, true);
|
||||
i2c_parse_fw_timings(dev, &riic->i2c_t, true);
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
/* Default 0 to save power. Can be overridden via sysfs for lower latency. */
|
||||
pm_runtime_set_autosuspend_delay(dev, 0);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
ret = riic_init_hw(riic, &i2c_t);
|
||||
ret = riic_init_hw(riic);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
@ -487,60 +505,127 @@ static int riic_i2c_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, riic);
|
||||
|
||||
dev_info(&pdev->dev, "registered with %dHz bus speed\n",
|
||||
i2c_t.bus_freq_hz);
|
||||
dev_info(dev, "registered with %dHz bus speed\n", riic->i2c_t.bus_freq_hz);
|
||||
return 0;
|
||||
|
||||
out:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_dont_use_autosuspend(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void riic_i2c_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct riic_dev *riic = platform_get_drvdata(pdev);
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret;
|
||||
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
riic_writeb(riic, 0, RIIC_ICIER);
|
||||
pm_runtime_put(&pdev->dev);
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
if (!ret) {
|
||||
riic_writeb(riic, 0, RIIC_ICIER);
|
||||
pm_runtime_put(dev);
|
||||
}
|
||||
i2c_del_adapter(&riic->adapter);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_dont_use_autosuspend(dev);
|
||||
}
|
||||
|
||||
static const u8 riic_rz_a_regs[RIIC_REG_END] = {
|
||||
[RIIC_ICCR1] = 0x00,
|
||||
[RIIC_ICCR2] = 0x04,
|
||||
[RIIC_ICMR1] = 0x08,
|
||||
[RIIC_ICMR3] = 0x10,
|
||||
[RIIC_ICFER] = 0x14,
|
||||
[RIIC_ICSER] = 0x18,
|
||||
[RIIC_ICIER] = 0x1c,
|
||||
[RIIC_ICSR2] = 0x24,
|
||||
[RIIC_ICBRL] = 0x34,
|
||||
[RIIC_ICBRH] = 0x38,
|
||||
[RIIC_ICDRT] = 0x3c,
|
||||
[RIIC_ICDRR] = 0x40,
|
||||
};
|
||||
|
||||
static const struct riic_of_data riic_rz_a_info = {
|
||||
.regs = {
|
||||
[RIIC_ICCR1] = 0x00,
|
||||
[RIIC_ICCR2] = 0x04,
|
||||
[RIIC_ICMR1] = 0x08,
|
||||
[RIIC_ICMR3] = 0x10,
|
||||
[RIIC_ICSER] = 0x18,
|
||||
[RIIC_ICIER] = 0x1c,
|
||||
[RIIC_ICSR2] = 0x24,
|
||||
[RIIC_ICBRL] = 0x34,
|
||||
[RIIC_ICBRH] = 0x38,
|
||||
[RIIC_ICDRT] = 0x3c,
|
||||
[RIIC_ICDRR] = 0x40,
|
||||
},
|
||||
.regs = riic_rz_a_regs,
|
||||
.fast_mode_plus = true,
|
||||
};
|
||||
|
||||
static const struct riic_of_data riic_rz_a1h_info = {
|
||||
.regs = riic_rz_a_regs,
|
||||
};
|
||||
|
||||
static const u8 riic_rz_v2h_regs[RIIC_REG_END] = {
|
||||
[RIIC_ICCR1] = 0x00,
|
||||
[RIIC_ICCR2] = 0x01,
|
||||
[RIIC_ICMR1] = 0x02,
|
||||
[RIIC_ICMR3] = 0x04,
|
||||
[RIIC_ICFER] = 0x05,
|
||||
[RIIC_ICSER] = 0x06,
|
||||
[RIIC_ICIER] = 0x07,
|
||||
[RIIC_ICSR2] = 0x09,
|
||||
[RIIC_ICBRL] = 0x10,
|
||||
[RIIC_ICBRH] = 0x11,
|
||||
[RIIC_ICDRT] = 0x12,
|
||||
[RIIC_ICDRR] = 0x13,
|
||||
};
|
||||
|
||||
static const struct riic_of_data riic_rz_v2h_info = {
|
||||
.regs = {
|
||||
[RIIC_ICCR1] = 0x00,
|
||||
[RIIC_ICCR2] = 0x01,
|
||||
[RIIC_ICMR1] = 0x02,
|
||||
[RIIC_ICMR3] = 0x04,
|
||||
[RIIC_ICSER] = 0x06,
|
||||
[RIIC_ICIER] = 0x07,
|
||||
[RIIC_ICSR2] = 0x09,
|
||||
[RIIC_ICBRL] = 0x10,
|
||||
[RIIC_ICBRH] = 0x11,
|
||||
[RIIC_ICDRT] = 0x12,
|
||||
[RIIC_ICDRR] = 0x13,
|
||||
},
|
||||
.regs = riic_rz_v2h_regs,
|
||||
.fast_mode_plus = true,
|
||||
};
|
||||
|
||||
static int riic_i2c_suspend(struct device *dev)
|
||||
{
|
||||
struct riic_dev *riic = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
i2c_mark_adapter_suspended(&riic->adapter);
|
||||
|
||||
/* Disable output on SDA, SCL pins. */
|
||||
riic_clear_set_bit(riic, ICCR1_ICE, 0, RIIC_ICCR1);
|
||||
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_sync(dev);
|
||||
|
||||
return reset_control_assert(riic->rstc);
|
||||
}
|
||||
|
||||
static int riic_i2c_resume(struct device *dev)
|
||||
{
|
||||
struct riic_dev *riic = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = reset_control_deassert(riic->rstc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = riic_init_hw(riic);
|
||||
if (ret) {
|
||||
/*
|
||||
* In case this happens there is no way to recover from this
|
||||
* state. The driver will remain loaded. We want to avoid
|
||||
* keeping the reset line de-asserted for no reason.
|
||||
*/
|
||||
reset_control_assert(riic->rstc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
i2c_mark_adapter_resumed(&riic->adapter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops riic_i2c_pm_ops = {
|
||||
SYSTEM_SLEEP_PM_OPS(riic_i2c_suspend, riic_i2c_resume)
|
||||
};
|
||||
|
||||
static const struct of_device_id riic_i2c_dt_ids[] = {
|
||||
{ .compatible = "renesas,riic-rz", .data = &riic_rz_a_info },
|
||||
{ .compatible = "renesas,riic-r7s72100", .data = &riic_rz_a1h_info, },
|
||||
{ .compatible = "renesas,riic-r9a09g057", .data = &riic_rz_v2h_info },
|
||||
{ /* Sentinel */ },
|
||||
};
|
||||
@ -551,6 +636,7 @@ static struct platform_driver riic_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "i2c-riic",
|
||||
.of_match_table = riic_i2c_dt_ids,
|
||||
.pm = pm_ptr(&riic_i2c_pm_ops),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -130,7 +130,7 @@ static const struct platform_device_id s3c24xx_driver_ids[] = {
|
||||
}, {
|
||||
.name = "s3c2440-hdmiphy-i2c",
|
||||
.driver_data = QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO,
|
||||
}, { },
|
||||
}, { }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);
|
||||
|
||||
|
@ -182,7 +182,7 @@ static u32 virtio_i2c_func(struct i2c_adapter *adap)
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||
}
|
||||
|
||||
static struct i2c_algorithm virtio_algorithm = {
|
||||
static const struct i2c_algorithm virtio_algorithm = {
|
||||
.xfer = virtio_i2c_xfer,
|
||||
.functionality = virtio_i2c_func,
|
||||
};
|
||||
@ -237,7 +237,7 @@ static void virtio_i2c_remove(struct virtio_device *vdev)
|
||||
virtio_i2c_del_vqs(vdev);
|
||||
}
|
||||
|
||||
static struct virtio_device_id id_table[] = {
|
||||
static const struct virtio_device_id id_table[] = {
|
||||
{ VIRTIO_ID_I2C_ADAPTER, VIRTIO_DEV_ANY_ID },
|
||||
{}
|
||||
};
|
||||
|
@ -915,6 +915,27 @@ int i2c_dev_irq_from_resources(const struct resource *resources,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Serialize device instantiation in case it can be instantiated explicitly
|
||||
* and by auto-detection
|
||||
*/
|
||||
static int i2c_lock_addr(struct i2c_adapter *adap, unsigned short addr,
|
||||
unsigned short flags)
|
||||
{
|
||||
if (!(flags & I2C_CLIENT_TEN) &&
|
||||
test_and_set_bit(addr, adap->addrs_in_instantiation))
|
||||
return -EBUSY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void i2c_unlock_addr(struct i2c_adapter *adap, unsigned short addr,
|
||||
unsigned short flags)
|
||||
{
|
||||
if (!(flags & I2C_CLIENT_TEN))
|
||||
clear_bit(addr, adap->addrs_in_instantiation);
|
||||
}
|
||||
|
||||
/**
|
||||
* i2c_new_client_device - instantiate an i2c device
|
||||
* @adap: the adapter managing the device
|
||||
@ -962,6 +983,10 @@ i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *inf
|
||||
goto out_err_silent;
|
||||
}
|
||||
|
||||
status = i2c_lock_addr(adap, client->addr, client->flags);
|
||||
if (status)
|
||||
goto out_err_silent;
|
||||
|
||||
/* Check for address business */
|
||||
status = i2c_check_addr_busy(adap, i2c_encode_flags_to_addr(client));
|
||||
if (status)
|
||||
@ -993,6 +1018,8 @@ i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *inf
|
||||
dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
|
||||
client->name, dev_name(&client->dev));
|
||||
|
||||
i2c_unlock_addr(adap, client->addr, client->flags);
|
||||
|
||||
return client;
|
||||
|
||||
out_remove_swnode:
|
||||
@ -1004,6 +1031,7 @@ out_err:
|
||||
dev_err(&adap->dev,
|
||||
"Failed to register i2c client %s at 0x%02x (%d)\n",
|
||||
client->name, client->addr, status);
|
||||
i2c_unlock_addr(adap, client->addr, client->flags);
|
||||
out_err_silent:
|
||||
if (need_put)
|
||||
put_device(&client->dev);
|
||||
@ -1068,7 +1096,7 @@ EXPORT_SYMBOL(i2c_find_device_by_fwnode);
|
||||
static const struct i2c_device_id dummy_id[] = {
|
||||
{ "dummy", },
|
||||
{ "smbus_host_notify", },
|
||||
{ },
|
||||
{ }
|
||||
};
|
||||
|
||||
static int dummy_probe(struct i2c_client *client)
|
||||
@ -1367,10 +1395,6 @@ struct i2c_adapter *i2c_verify_adapter(struct device *dev)
|
||||
}
|
||||
EXPORT_SYMBOL(i2c_verify_adapter);
|
||||
|
||||
#ifdef CONFIG_I2C_COMPAT
|
||||
static struct class_compat *i2c_adapter_compat_class;
|
||||
#endif
|
||||
|
||||
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
|
||||
{
|
||||
struct i2c_devinfo *devinfo;
|
||||
@ -1524,7 +1548,18 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
|
||||
dev_set_name(&adap->dev, "i2c-%d", adap->nr);
|
||||
adap->dev.bus = &i2c_bus_type;
|
||||
adap->dev.type = &i2c_adapter_type;
|
||||
res = device_register(&adap->dev);
|
||||
device_initialize(&adap->dev);
|
||||
|
||||
/*
|
||||
* This adapter can be used as a parent immediately after device_add(),
|
||||
* setup runtime-pm (especially ignore-children) before hand.
|
||||
*/
|
||||
device_enable_async_suspend(&adap->dev);
|
||||
pm_runtime_no_callbacks(&adap->dev);
|
||||
pm_suspend_ignore_children(&adap->dev, true);
|
||||
pm_runtime_enable(&adap->dev);
|
||||
|
||||
res = device_add(&adap->dev);
|
||||
if (res) {
|
||||
pr_err("adapter '%s': can't register device (%d)\n", adap->name, res);
|
||||
goto out_list;
|
||||
@ -1536,25 +1571,12 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
|
||||
if (res)
|
||||
goto out_reg;
|
||||
|
||||
device_enable_async_suspend(&adap->dev);
|
||||
pm_runtime_no_callbacks(&adap->dev);
|
||||
pm_suspend_ignore_children(&adap->dev, true);
|
||||
pm_runtime_enable(&adap->dev);
|
||||
|
||||
res = i2c_init_recovery(adap);
|
||||
if (res == -EPROBE_DEFER)
|
||||
goto out_reg;
|
||||
|
||||
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
|
||||
|
||||
#ifdef CONFIG_I2C_COMPAT
|
||||
res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,
|
||||
adap->dev.parent);
|
||||
if (res)
|
||||
dev_warn(&adap->dev,
|
||||
"Failed to create compatibility class link\n");
|
||||
#endif
|
||||
|
||||
/* create pre-declared device nodes */
|
||||
of_i2c_register_devices(adap);
|
||||
i2c_acpi_install_space_handler(adap);
|
||||
@ -1761,11 +1783,6 @@ void i2c_del_adapter(struct i2c_adapter *adap)
|
||||
device_for_each_child(&adap->dev, NULL, __unregister_client);
|
||||
device_for_each_child(&adap->dev, NULL, __unregister_dummy);
|
||||
|
||||
#ifdef CONFIG_I2C_COMPAT
|
||||
class_compat_remove_link(i2c_adapter_compat_class, &adap->dev,
|
||||
adap->dev.parent);
|
||||
#endif
|
||||
|
||||
/* device name is gone after device_unregister */
|
||||
dev_dbg(&adap->dev, "adapter [%s] unregistered\n", adap->name);
|
||||
|
||||
@ -2074,13 +2091,6 @@ static int __init i2c_init(void)
|
||||
|
||||
i2c_debugfs_root = debugfs_create_dir("i2c", NULL);
|
||||
|
||||
#ifdef CONFIG_I2C_COMPAT
|
||||
i2c_adapter_compat_class = class_compat_register("i2c-adapter");
|
||||
if (!i2c_adapter_compat_class) {
|
||||
retval = -ENOMEM;
|
||||
goto bus_err;
|
||||
}
|
||||
#endif
|
||||
retval = i2c_add_driver(&dummy_driver);
|
||||
if (retval)
|
||||
goto class_err;
|
||||
@ -2093,10 +2103,6 @@ static int __init i2c_init(void)
|
||||
return 0;
|
||||
|
||||
class_err:
|
||||
#ifdef CONFIG_I2C_COMPAT
|
||||
class_compat_unregister(i2c_adapter_compat_class);
|
||||
bus_err:
|
||||
#endif
|
||||
is_registered = false;
|
||||
bus_unregister(&i2c_bus_type);
|
||||
return retval;
|
||||
@ -2109,9 +2115,6 @@ static void __exit i2c_exit(void)
|
||||
if (IS_ENABLED(CONFIG_OF_DYNAMIC))
|
||||
WARN_ON(of_reconfig_notifier_unregister(&i2c_of_notifier));
|
||||
i2c_del_driver(&dummy_driver);
|
||||
#ifdef CONFIG_I2C_COMPAT
|
||||
class_compat_unregister(i2c_adapter_compat_class);
|
||||
#endif
|
||||
debugfs_remove_recursive(i2c_debugfs_root);
|
||||
bus_unregister(&i2c_bus_type);
|
||||
tracepoint_synchronize_unregister();
|
||||
|
@ -109,15 +109,12 @@ EXPORT_SYMBOL_GPL(i2c_slave_event);
|
||||
bool i2c_detect_slave_mode(struct device *dev)
|
||||
{
|
||||
if (IS_BUILTIN(CONFIG_OF) && dev->of_node) {
|
||||
struct device_node *child;
|
||||
u32 reg;
|
||||
|
||||
for_each_child_of_node(dev->of_node, child) {
|
||||
for_each_child_of_node_scoped(dev->of_node, child) {
|
||||
of_property_read_u32(child, "reg", ®);
|
||||
if (reg & I2C_OWN_SLAVE_ADDRESS) {
|
||||
of_node_put(child);
|
||||
if (reg & I2C_OWN_SLAVE_ADDRESS)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (IS_BUILTIN(CONFIG_ACPI) && ACPI_HANDLE(dev)) {
|
||||
dev_dbg(dev, "ACPI slave is not supported yet\n");
|
||||
|
@ -6,7 +6,10 @@
|
||||
* Copyright (C) 2020 by Renesas Electronics Corporation
|
||||
*/
|
||||
|
||||
#include <generated/utsrelease.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
@ -14,12 +17,14 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/workqueue.h> /* FIXME: is system_long_wq the best choice? */
|
||||
|
||||
#define TU_CUR_VERSION 0x01
|
||||
#define TU_VERSION_MAX_LENGTH 128
|
||||
|
||||
enum testunit_cmds {
|
||||
TU_CMD_READ_BYTES = 1, /* save 0 for ABORT, RESET or similar */
|
||||
TU_CMD_SMBUS_HOST_NOTIFY,
|
||||
TU_CMD_SMBUS_BLOCK_PROC_CALL,
|
||||
TU_CMD_GET_VERSION_WITH_REP_START,
|
||||
TU_CMD_SMBUS_ALERT_REQUEST,
|
||||
TU_NUM_CMDS
|
||||
};
|
||||
|
||||
@ -39,50 +44,38 @@ struct testunit_data {
|
||||
unsigned long flags;
|
||||
u8 regs[TU_NUM_REGS];
|
||||
u8 reg_idx;
|
||||
u8 read_idx;
|
||||
struct i2c_client *client;
|
||||
struct delayed_work worker;
|
||||
struct gpio_desc *gpio;
|
||||
struct completion alert_done;
|
||||
};
|
||||
|
||||
static void i2c_slave_testunit_work(struct work_struct *work)
|
||||
static char tu_version_info[] = "v" UTS_RELEASE "\n\0";
|
||||
|
||||
static int i2c_slave_testunit_smbalert_cb(struct i2c_client *client,
|
||||
enum i2c_slave_event event, u8 *val)
|
||||
{
|
||||
struct testunit_data *tu = container_of(work, struct testunit_data, worker.work);
|
||||
struct i2c_msg msg;
|
||||
u8 msgbuf[256];
|
||||
int ret = 0;
|
||||
struct testunit_data *tu = i2c_get_clientdata(client);
|
||||
|
||||
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];
|
||||
switch (event) {
|
||||
case I2C_SLAVE_READ_PROCESSED:
|
||||
gpiod_set_value(tu->gpio, 0);
|
||||
fallthrough;
|
||||
case I2C_SLAVE_READ_REQUESTED:
|
||||
*val = tu->regs[TU_REG_DATAL];
|
||||
break;
|
||||
|
||||
case TU_CMD_SMBUS_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];
|
||||
case I2C_SLAVE_STOP:
|
||||
complete(&tu->alert_done);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
case I2C_SLAVE_WRITE_REQUESTED:
|
||||
case I2C_SLAVE_WRITE_RECEIVED:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_slave_testunit_slave_cb(struct i2c_client *client,
|
||||
@ -91,9 +84,20 @@ static int i2c_slave_testunit_slave_cb(struct i2c_client *client,
|
||||
struct testunit_data *tu = i2c_get_clientdata(client);
|
||||
bool is_proc_call = tu->reg_idx == 3 && tu->regs[TU_REG_DATAL] == 1 &&
|
||||
tu->regs[TU_REG_CMD] == TU_CMD_SMBUS_BLOCK_PROC_CALL;
|
||||
bool is_get_version = tu->reg_idx == 3 &&
|
||||
tu->regs[TU_REG_CMD] == TU_CMD_GET_VERSION_WITH_REP_START;
|
||||
int ret = 0;
|
||||
|
||||
switch (event) {
|
||||
case I2C_SLAVE_WRITE_REQUESTED:
|
||||
if (test_bit(TU_FLAG_IN_PROCESS, &tu->flags))
|
||||
return -EBUSY;
|
||||
|
||||
memset(tu->regs, 0, TU_NUM_REGS);
|
||||
tu->reg_idx = 0;
|
||||
tu->read_idx = 0;
|
||||
break;
|
||||
|
||||
case I2C_SLAVE_WRITE_RECEIVED:
|
||||
if (test_bit(TU_FLAG_IN_PROCESS, &tu->flags))
|
||||
return -EBUSY;
|
||||
@ -127,27 +131,93 @@ static int i2c_slave_testunit_slave_cb(struct i2c_client *client,
|
||||
tu->reg_idx = 0;
|
||||
break;
|
||||
|
||||
case I2C_SLAVE_WRITE_REQUESTED:
|
||||
if (test_bit(TU_FLAG_IN_PROCESS, &tu->flags))
|
||||
return -EBUSY;
|
||||
|
||||
memset(tu->regs, 0, TU_NUM_REGS);
|
||||
tu->reg_idx = 0;
|
||||
break;
|
||||
|
||||
case I2C_SLAVE_READ_PROCESSED:
|
||||
if (is_proc_call && tu->regs[TU_REG_DATAH])
|
||||
/* Advance until we reach the NUL character */
|
||||
if (is_get_version && tu_version_info[tu->read_idx] != 0)
|
||||
tu->read_idx++;
|
||||
else if (is_proc_call && tu->regs[TU_REG_DATAH])
|
||||
tu->regs[TU_REG_DATAH]--;
|
||||
|
||||
fallthrough;
|
||||
|
||||
case I2C_SLAVE_READ_REQUESTED:
|
||||
*val = is_proc_call ? tu->regs[TU_REG_DATAH] : TU_CUR_VERSION;
|
||||
if (is_get_version)
|
||||
*val = tu_version_info[tu->read_idx];
|
||||
else if (is_proc_call)
|
||||
*val = tu->regs[TU_REG_DATAH];
|
||||
else
|
||||
*val = test_bit(TU_FLAG_IN_PROCESS, &tu->flags) ?
|
||||
tu->regs[TU_REG_CMD] : 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void i2c_slave_testunit_work(struct work_struct *work)
|
||||
{
|
||||
struct testunit_data *tu = container_of(work, struct testunit_data, worker.work);
|
||||
unsigned long time_left;
|
||||
struct i2c_msg msg;
|
||||
u8 msgbuf[256];
|
||||
u16 orig_addr;
|
||||
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_SMBUS_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;
|
||||
|
||||
case TU_CMD_SMBUS_ALERT_REQUEST:
|
||||
i2c_slave_unregister(tu->client);
|
||||
orig_addr = tu->client->addr;
|
||||
tu->client->addr = 0x0c;
|
||||
ret = i2c_slave_register(tu->client, i2c_slave_testunit_smbalert_cb);
|
||||
if (ret)
|
||||
goto out_smbalert;
|
||||
|
||||
reinit_completion(&tu->alert_done);
|
||||
gpiod_set_value(tu->gpio, 1);
|
||||
time_left = wait_for_completion_timeout(&tu->alert_done, HZ);
|
||||
if (!time_left)
|
||||
ret = -ETIMEDOUT;
|
||||
|
||||
i2c_slave_unregister(tu->client);
|
||||
out_smbalert:
|
||||
tu->client->addr = orig_addr;
|
||||
i2c_slave_register(tu->client, i2c_slave_testunit_slave_cb);
|
||||
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_probe(struct i2c_client *client)
|
||||
{
|
||||
struct testunit_data *tu;
|
||||
@ -158,8 +228,18 @@ static int i2c_slave_testunit_probe(struct i2c_client *client)
|
||||
|
||||
tu->client = client;
|
||||
i2c_set_clientdata(client, tu);
|
||||
init_completion(&tu->alert_done);
|
||||
INIT_DELAYED_WORK(&tu->worker, i2c_slave_testunit_work);
|
||||
|
||||
tu->gpio = devm_gpiod_get_index_optional(&client->dev, NULL, 0, GPIOD_OUT_LOW);
|
||||
if (gpiod_cansleep(tu->gpio)) {
|
||||
dev_err(&client->dev, "GPIO access which may sleep is not allowed\n");
|
||||
return -EDEADLK;
|
||||
}
|
||||
|
||||
if (sizeof(tu_version_info) > TU_VERSION_MAX_LENGTH)
|
||||
tu_version_info[TU_VERSION_MAX_LENGTH - 1] = 0;
|
||||
|
||||
return i2c_slave_register(client, i2c_slave_testunit_slave_cb);
|
||||
};
|
||||
|
||||
|
@ -119,4 +119,20 @@ config I2C_MUX_MLXCPLD
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-mux-mlxcpld.
|
||||
|
||||
config I2C_MUX_MULE
|
||||
tristate "Theobroma Systems Mule I2C device multiplexer"
|
||||
depends on OF && SENSORS_AMC6821
|
||||
help
|
||||
Mule is an MCU that emulates a set of I2C devices, among which
|
||||
devices that are reachable through an I2C-mux. The devices on the mux
|
||||
can be selected by writing the appropriate device number to an I2C
|
||||
configuration register.
|
||||
|
||||
If you say yes to this option, support will be included for a
|
||||
Theobroma Systems Mule I2C multiplexer. This driver provides access to
|
||||
I2C devices connected on this mux.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-mux-mule.
|
||||
|
||||
endmenu
|
||||
|
@ -10,6 +10,7 @@ obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o
|
||||
obj-$(CONFIG_I2C_MUX_GPMUX) += i2c-mux-gpmux.o
|
||||
obj-$(CONFIG_I2C_MUX_LTC4306) += i2c-mux-ltc4306.o
|
||||
obj-$(CONFIG_I2C_MUX_MLXCPLD) += i2c-mux-mlxcpld.o
|
||||
obj-$(CONFIG_I2C_MUX_MULE) += i2c-mux-mule.o
|
||||
obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o
|
||||
obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o
|
||||
obj-$(CONFIG_I2C_MUX_PINCTRL) += i2c-mux-pinctrl.o
|
||||
|
148
drivers/i2c/muxes/i2c-mux-mule.c
Normal file
148
drivers/i2c/muxes/i2c-mux-mule.c
Normal file
@ -0,0 +1,148 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Theobroma Systems Mule I2C device multiplexer
|
||||
*
|
||||
* Copyright (C) 2024 Theobroma Systems Design und Consulting GmbH
|
||||
*/
|
||||
|
||||
#include <linux/i2c-mux.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define MULE_I2C_MUX_CONFIG_REG 0xff
|
||||
#define MULE_I2C_MUX_DEFAULT_DEV 0x0
|
||||
|
||||
struct mule_i2c_reg_mux {
|
||||
struct regmap *regmap;
|
||||
};
|
||||
|
||||
static int mule_i2c_mux_select(struct i2c_mux_core *muxc, u32 dev)
|
||||
{
|
||||
struct mule_i2c_reg_mux *mux = muxc->priv;
|
||||
|
||||
return regmap_write(mux->regmap, MULE_I2C_MUX_CONFIG_REG, dev);
|
||||
}
|
||||
|
||||
static int mule_i2c_mux_deselect(struct i2c_mux_core *muxc, u32 dev)
|
||||
{
|
||||
return mule_i2c_mux_select(muxc, MULE_I2C_MUX_DEFAULT_DEV);
|
||||
}
|
||||
|
||||
static void mule_i2c_mux_remove(void *data)
|
||||
{
|
||||
struct i2c_mux_core *muxc = data;
|
||||
|
||||
i2c_mux_del_adapters(muxc);
|
||||
|
||||
mule_i2c_mux_deselect(muxc, MULE_I2C_MUX_DEFAULT_DEV);
|
||||
}
|
||||
|
||||
static int mule_i2c_mux_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *mux_dev = &pdev->dev;
|
||||
struct mule_i2c_reg_mux *priv;
|
||||
struct i2c_client *client;
|
||||
struct i2c_mux_core *muxc;
|
||||
struct device_node *dev;
|
||||
unsigned int readback;
|
||||
int ndev, ret;
|
||||
bool old_fw;
|
||||
|
||||
/* Count devices on the mux */
|
||||
ndev = of_get_child_count(mux_dev->of_node);
|
||||
dev_dbg(mux_dev, "%d devices on the mux\n", ndev);
|
||||
|
||||
client = to_i2c_client(mux_dev->parent);
|
||||
|
||||
muxc = i2c_mux_alloc(client->adapter, mux_dev, ndev, sizeof(*priv),
|
||||
I2C_MUX_LOCKED, mule_i2c_mux_select, mule_i2c_mux_deselect);
|
||||
if (!muxc)
|
||||
return -ENOMEM;
|
||||
|
||||
priv = i2c_mux_priv(muxc);
|
||||
|
||||
priv->regmap = dev_get_regmap(mux_dev->parent, NULL);
|
||||
if (IS_ERR(priv->regmap))
|
||||
return dev_err_probe(mux_dev, PTR_ERR(priv->regmap),
|
||||
"No parent i2c register map\n");
|
||||
|
||||
platform_set_drvdata(pdev, muxc);
|
||||
|
||||
/*
|
||||
* MULE_I2C_MUX_DEFAULT_DEV is guaranteed to exist on all old and new
|
||||
* mule fw. Mule fw without mux support will accept write ops to the
|
||||
* config register, but readback returns 0xff (register not updated).
|
||||
*/
|
||||
ret = mule_i2c_mux_select(muxc, MULE_I2C_MUX_DEFAULT_DEV);
|
||||
if (ret)
|
||||
return dev_err_probe(mux_dev, ret,
|
||||
"Failed to write config register\n");
|
||||
|
||||
ret = regmap_read(priv->regmap, MULE_I2C_MUX_CONFIG_REG, &readback);
|
||||
if (ret)
|
||||
return dev_err_probe(mux_dev, ret,
|
||||
"Failed to read config register\n");
|
||||
|
||||
old_fw = (readback != MULE_I2C_MUX_DEFAULT_DEV);
|
||||
|
||||
ret = devm_add_action_or_reset(mux_dev, mule_i2c_mux_remove, muxc);
|
||||
if (ret)
|
||||
return dev_err_probe(mux_dev, ret,
|
||||
"Failed to register mux remove\n");
|
||||
|
||||
/* Create device adapters */
|
||||
for_each_child_of_node(mux_dev->of_node, dev) {
|
||||
u32 reg;
|
||||
|
||||
ret = of_property_read_u32(dev, "reg", ®);
|
||||
if (ret)
|
||||
return dev_err_probe(mux_dev, ret,
|
||||
"No reg property found for %s\n",
|
||||
of_node_full_name(dev));
|
||||
|
||||
if (old_fw && reg != 0) {
|
||||
dev_warn(mux_dev,
|
||||
"Mux is not supported, please update Mule FW\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = mule_i2c_mux_select(muxc, reg);
|
||||
if (ret) {
|
||||
dev_warn(mux_dev,
|
||||
"Device %d not supported, please update Mule FW\n", reg);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = i2c_mux_add_adapter(muxc, 0, reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
mule_i2c_mux_deselect(muxc, MULE_I2C_MUX_DEFAULT_DEV);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id mule_i2c_mux_of_match[] = {
|
||||
{ .compatible = "tsd,mule-i2c-mux", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mule_i2c_mux_of_match);
|
||||
|
||||
static struct platform_driver mule_i2c_mux_driver = {
|
||||
.driver = {
|
||||
.name = "mule-i2c-mux",
|
||||
.of_match_table = mule_i2c_mux_of_match,
|
||||
},
|
||||
.probe = mule_i2c_mux_probe,
|
||||
};
|
||||
|
||||
module_platform_driver(mule_i2c_mux_driver);
|
||||
|
||||
MODULE_AUTHOR("Farouk Bouabid <farouk.bouabid@cherry.de>");
|
||||
MODULE_DESCRIPTION("I2C mux driver for Theobroma Systems Mule");
|
||||
MODULE_LICENSE("GPL");
|
@ -41,10 +41,9 @@ config TXGBE
|
||||
tristate "Wangxun(R) 10GbE PCI Express adapters support"
|
||||
depends on PCI
|
||||
depends on COMMON_CLK
|
||||
depends on I2C_DESIGNWARE_PLATFORM
|
||||
select MARVELL_10G_PHY
|
||||
select REGMAP
|
||||
select I2C
|
||||
select I2C_DESIGNWARE_PLATFORM
|
||||
select PHYLINK
|
||||
select HWMON if TXGBE=y
|
||||
select SFP
|
||||
|
@ -761,6 +761,9 @@ struct i2c_adapter {
|
||||
struct regulator *bus_regulator;
|
||||
|
||||
struct dentry *debugfs;
|
||||
|
||||
/* 7bit address space */
|
||||
DECLARE_BITMAP(addrs_in_instantiation, 1 << 7);
|
||||
};
|
||||
#define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user