mirror of
git://git.yoctoproject.org/linux-yocto.git
synced 2025-10-23 07:23:12 +02:00
i2c-for-6.16-rc1
i2c-core updates - move towards using the 'fwnode' handle instead of 'of_node' (meaning 'of_node' even gets removed from i2c_board_info) - add support for Write Disable-aware SPD eeproms - minor stuff (use new helpers, typo fixes) i2c-atr (address translator) updates - support per-channel alias pools - added support for dynamic address translation (also adds FPC202 driver as its user) - add 'static' and 'passthrough' flags i2c-host updates Cleanups and refactorings - Many drivers switched to dev_err_probe() - Generic cleanups applied to designware, iproc, ismt, mlxbf, npcm7xx, qcom-geni, pasemi, and thunderx - davinci: declare I2C mangling support among I2C features - designware: clean up DTS handling - designware: fix PM runtime on driver unregister - imx: improve error logging during probe - lpc2k: improve checks in probe error path - xgene-slimpro: improve PCC shared memory handling - pasemi: improve error handling in reset, smbus clear, timeouts - tegra: validate buffer length during transfers - wmt: convert binding to YAML format Improvements and extended support: - microchip-core: add SMBus support - mlxbf: add support for repeated start in block transfers - mlxbf: improve timer configuration - npcm: attempt clock toggle recovery before failing init - octeon: add support for block mode operations - pasemi: add support for unjam device feature - riic: add support for bus recovery New device support: - MediaTek Dimensity 1200 (MT6893) - Sophgo SG2044 - Renesas RZ/V2N (R9A09G056) - Rockchip RK3528 - AMD ISP (new driver) -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEOZGx6rniZ1Gk92RdFA3kzBSgKbYFAmg510gACgkQFA3kzBSg KbaBzxAAi76xSI4q4fW4OPJhRLhTJvXKWfXQ25yBLUNKxa623vnqAJw3w3TiCULu BjooVdRDKSPjOSkGkt5ArnSh+fIZi182ZaxrnMNChUxA4VmmtnxkrIkCuxq0Sn7J J1Oye1tnsEDPzo185khdmOb1JVBqvKax1nFopJQ7mBBC7qlEYcOxBGkLiImTsaQR sqZNNHs7f32JJnG1gXvTI/l4xXvxUCaIJikrqHMW7v7O6NpjyMABf+3nzvMV6WPf 4ioCAm8VoUxQkTkgzsjbCqsZBZRzsDTw0nWrJQgXmIBSsQC+ccP+HMe/B84HeoRO JorF03yBIsGuhENBFK86cNdCAS/gRSrZe4AgbhAOMXo5u21r02P1CF0fIlNtqKc3 0h8kOLHHsuR+pgnVWU9ebjtPnMNCoIcCwiyVX2QgYL2yww69SKRxYVDfHnzTG4kQ orBHKsJVLBDCKW1b49XIKZ6D5SMPOj/XB1ZuJTzVLGLyFW8j3wBeOPeAkaZgq9id bxagSbTCgntJgw7S64a/7MCaSesxpi9AdWxWbTkDbCiU/kodunl8gwGE1UAzweZd QxNags9A9vGJZ2BQrN9//P+/xi5XTV7GjflXOiw61c1tOYhHf2UB6D6qYzmDM+Qz X8n6rBp0uc6792mYVCXsjRXSYjvOtfk4+TjvH905k3wg1eXvkWQ= =clhN -----END PGP SIGNATURE----- Merge tag 'i2c-for-6.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux Pull i2c updates from Wolfram Sang: "Core updates: - move towards using the 'fwnode' handle instead of 'of_node' (meaning 'of_node' even gets removed from i2c_board_info) - add support for Write Disable-aware SPD eeproms - minor stuff (use new helpers, typo fixes) i2c-atr (address translator) updates: - support per-channel alias pools - added support for dynamic address translation (also adds FPC202 driver as its user) - add 'static' and 'passthrough' flags Cleanups and refactorings - Many drivers switched to dev_err_probe() - Generic cleanups applied to designware, iproc, ismt, mlxbf, npcm7xx, qcom-geni, pasemi, and thunderx - davinci: declare I2C mangling support among I2C features - designware: clean up DTS handling - designware: fix PM runtime on driver unregister - imx: improve error logging during probe - lpc2k: improve checks in probe error path - xgene-slimpro: improve PCC shared memory handling - pasemi: improve error handling in reset, smbus clear, timeouts - tegra: validate buffer length during transfers - wmt: convert binding to YAML format Improvements and extended support: - microchip-core: add SMBus support - mlxbf: add support for repeated start in block transfers - mlxbf: improve timer configuration - npcm: attempt clock toggle recovery before failing init - octeon: add support for block mode operations - pasemi: add support for unjam device feature - riic: add support for bus recovery New device support: - MediaTek Dimensity 1200 (MT6893) - Sophgo SG2044 - Renesas RZ/V2N (R9A09G056) - Rockchip RK3528 - AMD ISP (new driver)" * tag 'i2c-for-6.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (89 commits) i2c: Use str_read_write() helper i2c: mlxbf: avoid 64-bit division i2c: viai2c-wmt: Replace dev_err() with dev_err_probe() in probe function i2c: designware: Don't warn about missing get_clk_rate_khz i2c: designware: Invoke runtime suspend on quick slave re-registration i2c-mlxbf: Improve I2C bus timing configuration i2c-mlxbf: Add repeated start condition support i2c: xgene-slimpro: Replace dev_err() with dev_err_probe() in probe function dt-bindings: i2c: i2c-wmt: Convert to YAML i2c: microchip-corei2c: add smbus support i2c: mlxbf: Allow build with COMPILE_TEST i2c: I2C_DESIGNWARE_AMDISP should depend on DRM_AMD_ISP i2c: atr: add passthrough flag i2c: atr: add static flag i2c: atr: allow replacing mappings in attach_addr() i2c: atr: deduplicate logic in attach_addr() i2c: atr: do not create mapping in detach_addr() i2c: atr: split up i2c_atr_get_mapping_by_addr() i2c: atr: find_mapping() -> get_mapping() i2c: atr: Fix lockdep for nested ATRs ...
This commit is contained in:
commit
883e3c9f40
|
@ -52,6 +52,7 @@ properties:
|
|||
- const: mediatek,mt8173-i2c
|
||||
- items:
|
||||
- enum:
|
||||
- mediatek,mt6893-i2c
|
||||
- mediatek,mt8195-i2c
|
||||
- const: mediatek,mt8192-i2c
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ properties:
|
|||
- rockchip,px30-i2c
|
||||
- rockchip,rk3308-i2c
|
||||
- rockchip,rk3328-i2c
|
||||
- rockchip,rk3528-i2c
|
||||
- rockchip,rk3562-i2c
|
||||
- rockchip,rk3568-i2c
|
||||
- rockchip,rk3576-i2c
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
* Wondermedia I2C Controller
|
||||
|
||||
Required properties :
|
||||
|
||||
- compatible : should be "wm,wm8505-i2c"
|
||||
- reg : Offset and length of the register set for the device
|
||||
- interrupts : <IRQ> where IRQ is the interrupt number
|
||||
- clocks : phandle to the I2C clock source
|
||||
|
||||
Optional properties :
|
||||
|
||||
- clock-frequency : desired I2C bus clock frequency in Hz.
|
||||
Valid values are 100000 and 400000.
|
||||
Default to 100000 if not specified, or invalid value.
|
||||
|
||||
Example :
|
||||
|
||||
i2c_0: i2c@d8280000 {
|
||||
compatible = "wm,wm8505-i2c";
|
||||
reg = <0xd8280000 0x1000>;
|
||||
interrupts = <19>;
|
||||
clocks = <&clki2c0>;
|
||||
clock-frequency = <400000>;
|
||||
};
|
|
@ -29,6 +29,7 @@ properties:
|
|||
- enum:
|
||||
- renesas,riic-r9a08g045 # RZ/G3S
|
||||
- renesas,riic-r9a09g047 # RZ/G3E
|
||||
- renesas,riic-r9a09g056 # RZ/V2N
|
||||
- const: renesas,riic-r9a09g057 # RZ/V2H(P)
|
||||
|
||||
- const: renesas,riic-r9a09g057 # RZ/V2H(P)
|
||||
|
|
|
@ -32,15 +32,13 @@ properties:
|
|||
- const: renesas,r9a06g032-i2c # RZ/N1D
|
||||
- const: renesas,rzn1-i2c # RZ/N1
|
||||
- const: snps,designware-i2c
|
||||
- description: Microsemi Ocelot SoCs I2C controller
|
||||
items:
|
||||
- const: mscc,ocelot-i2c
|
||||
- const: snps,designware-i2c
|
||||
- description: Baikal-T1 SoC System I2C controller
|
||||
const: baikal,bt1-sys-i2c
|
||||
- description: T-HEAD TH1520 SoCs I2C controller
|
||||
items:
|
||||
- const: thead,th1520-i2c
|
||||
- items:
|
||||
- enum:
|
||||
- mscc,ocelot-i2c
|
||||
- sophgo,sg2044-i2c
|
||||
- thead,th1520-i2c
|
||||
- const: snps,designware-i2c
|
||||
|
||||
reg:
|
||||
|
|
47
Documentation/devicetree/bindings/i2c/wm,wm8505-i2c.yaml
Normal file
47
Documentation/devicetree/bindings/i2c/wm,wm8505-i2c.yaml
Normal file
|
@ -0,0 +1,47 @@
|
|||
# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/i2c/wm,wm8505-i2c.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: I2C Controller on WonderMedia WM8505 and related SoCs
|
||||
|
||||
maintainers:
|
||||
- Alexey Charkov <alchark@gmail.com>
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/i2c/i2c-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: wm,wm8505-i2c
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-frequency:
|
||||
enum: [100000, 400000]
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c_0: i2c@d8280000 {
|
||||
compatible = "wm,wm8505-i2c";
|
||||
reg = <0xd8280000 0x1000>;
|
||||
interrupts = <19>;
|
||||
clocks = <&clki2c0>;
|
||||
clock-frequency = <400000>;
|
||||
};
|
94
Documentation/devicetree/bindings/misc/ti,fpc202.yaml
Normal file
94
Documentation/devicetree/bindings/misc/ti,fpc202.yaml
Normal file
|
@ -0,0 +1,94 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/misc/ti,fpc202.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: TI FPC202 dual port controller with expanded IOs
|
||||
|
||||
maintainers:
|
||||
- Romain Gantois <romain.gantois@bootlin.com>
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/i2c/i2c-atr.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: ti,fpc202
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
gpio-controller: true
|
||||
|
||||
"#gpio-cells":
|
||||
const: 2
|
||||
|
||||
enable-gpios:
|
||||
description:
|
||||
Specifier for the GPIO connected to the EN pin.
|
||||
maxItems: 1
|
||||
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
"^i2c@[0-1]$":
|
||||
$ref: /schemas/i2c/i2c-controller.yaml#
|
||||
description: Downstream device ports 0 and 1
|
||||
|
||||
properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
description:
|
||||
Downstream port ID
|
||||
|
||||
required:
|
||||
- "#address-cells"
|
||||
- "#size-cells"
|
||||
- reg
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- gpio-controller
|
||||
- "#gpio-cells"
|
||||
- "#address-cells"
|
||||
- "#size-cells"
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
i2c-atr@f {
|
||||
compatible = "ti,fpc202";
|
||||
reg = <0xf>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
|
||||
i2c@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0>;
|
||||
};
|
||||
|
||||
i2c@1 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
|
@ -84,7 +84,7 @@ Remarks:
|
|||
\|
|
||||
|
||||
must be 74HC05, they must be open collector output.
|
||||
- All resitors are 10k.
|
||||
- All resistors are 10k.
|
||||
- Pins 18-25 of the parallel port connected to GND.
|
||||
- Pins 4-9 (D2-D7) could be used as VDD is the driver drives them high.
|
||||
The ADM1032 evaluation board uses D4-D7. Beware that the amount of
|
||||
|
|
16
MAINTAINERS
16
MAINTAINERS
|
@ -3480,7 +3480,7 @@ M: Alexey Charkov <alchark@gmail.com>
|
|||
M: Krzysztof Kozlowski <krzk@kernel.org>
|
||||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
S: Odd Fixes
|
||||
F: Documentation/devicetree/bindings/i2c/i2c-wmt.txt
|
||||
F: Documentation/devicetree/bindings/i2c/wm,wm8505-i2c.yaml
|
||||
F: Documentation/devicetree/bindings/interrupt-controller/via,vt8500-intc.yaml
|
||||
F: Documentation/devicetree/bindings/pwm/via,vt8500-pwm.yaml
|
||||
F: arch/arm/boot/dts/vt8500/
|
||||
|
@ -23872,6 +23872,13 @@ L: linux-i2c@vger.kernel.org
|
|||
S: Supported
|
||||
F: drivers/i2c/busses/i2c-designware-*
|
||||
|
||||
SYNOPSYS DESIGNWARE I2C DRIVER - AMDISP
|
||||
M: Nirujogi Pratap <pratap.nirujogi@amd.com>
|
||||
M: Bin Du <bin.du@amd.com>
|
||||
L: linux-i2c@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/i2c/busses/i2c-designware-amdisp.c
|
||||
|
||||
SYNOPSYS DESIGNWARE MMC/SD/SDIO DRIVER
|
||||
M: Jaehoon Chung <jh80.chung@samsung.com>
|
||||
L: linux-mmc@vger.kernel.org
|
||||
|
@ -24624,6 +24631,13 @@ F: drivers/misc/tifm*
|
|||
F: drivers/mmc/host/tifm_sd.c
|
||||
F: include/linux/tifm.h
|
||||
|
||||
TI FPC202 DUAL PORT CONTROLLER
|
||||
M: Romain Gantois <romain.gantois@bootlin.com>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/misc/ti,fpc202.yaml
|
||||
F: drivers/misc/ti_fpc202.c
|
||||
|
||||
TI FPD-LINK DRIVERS
|
||||
M: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
|
||||
L: linux-media@vger.kernel.org
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/errno.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-algo-pcf.h>
|
||||
#include <linux/string_choices.h>
|
||||
#include "i2c-algo-pcf.h"
|
||||
|
||||
|
||||
|
@ -316,7 +317,7 @@ static int pcf_xfer(struct i2c_adapter *i2c_adap,
|
|||
pmsg = &msgs[i];
|
||||
|
||||
DEB2(printk(KERN_DEBUG "i2c-algo-pcf.o: Doing %s %d bytes to 0x%02x - %d of %d messages\n",
|
||||
pmsg->flags & I2C_M_RD ? "read" : "write",
|
||||
str_read_write(pmsg->flags & I2C_M_RD),
|
||||
pmsg->len, pmsg->addr, i + 1, num);)
|
||||
|
||||
ret = pcf_doAddress(adap, pmsg);
|
||||
|
|
|
@ -592,6 +592,17 @@ config I2C_DESIGNWARE_PLATFORM
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-designware-platform.
|
||||
|
||||
config I2C_DESIGNWARE_AMDISP
|
||||
tristate "Synopsys DesignWare Platform for AMDISP"
|
||||
depends on DRM_AMD_ISP || COMPILE_TEST
|
||||
depends on I2C_DESIGNWARE_CORE
|
||||
help
|
||||
If you say yes to this option, support will be included for the
|
||||
AMDISP Synopsys DesignWare I2C adapter.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called amd_isp_i2c_designware.
|
||||
|
||||
config I2C_DESIGNWARE_AMDPSP
|
||||
bool "AMD PSP I2C semaphore support"
|
||||
depends on ACPI
|
||||
|
@ -845,7 +856,7 @@ config I2C_LS2X
|
|||
|
||||
config I2C_MLXBF
|
||||
tristate "Mellanox BlueField I2C controller"
|
||||
depends on MELLANOX_PLATFORM && ARM64
|
||||
depends on (MELLANOX_PLATFORM && ARM64) || COMPILE_TEST
|
||||
depends on ACPI
|
||||
select I2C_SLAVE
|
||||
help
|
||||
|
|
|
@ -58,6 +58,7 @@ obj-$(CONFIG_I2C_DESIGNWARE_PLATFORM) += i2c-designware-platform.o
|
|||
i2c-designware-platform-y := i2c-designware-platdrv.o
|
||||
i2c-designware-platform-$(CONFIG_I2C_DESIGNWARE_AMDPSP) += i2c-designware-amdpsp.o
|
||||
i2c-designware-platform-$(CONFIG_I2C_DESIGNWARE_BAYTRAIL) += i2c-designware-baytrail.o
|
||||
obj-$(CONFIG_I2C_DESIGNWARE_AMDISP) += i2c-designware-amdisp.o
|
||||
obj-$(CONFIG_I2C_DESIGNWARE_PCI) += i2c-designware-pci.o
|
||||
i2c-designware-pci-y := i2c-designware-pcidrv.o
|
||||
obj-$(CONFIG_I2C_DIGICOLOR) += i2c-digicolor.o
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/string_choices.h>
|
||||
|
||||
#include "i2c-at91.h"
|
||||
|
||||
|
@ -523,7 +524,7 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev)
|
|||
*/
|
||||
|
||||
dev_dbg(dev->dev, "transfer: %s %zu bytes.\n",
|
||||
(dev->msg->flags & I2C_M_RD) ? "read" : "write", dev->buf_len);
|
||||
str_read_write(dev->msg->flags & I2C_M_RD), dev->buf_len);
|
||||
|
||||
reinit_completion(&dev->cmd_complete);
|
||||
dev->transfer_status = 0;
|
||||
|
|
|
@ -224,11 +224,6 @@ static void slave_rx_tasklet_fn(unsigned long);
|
|||
| BIT(IS_S_TX_UNDERRUN_SHIFT) | BIT(IS_S_RX_FIFO_FULL_SHIFT)\
|
||||
| BIT(IS_S_RX_THLD_SHIFT))
|
||||
|
||||
static int bcm_iproc_i2c_reg_slave(struct i2c_client *slave);
|
||||
static int bcm_iproc_i2c_unreg_slave(struct i2c_client *slave);
|
||||
static void bcm_iproc_i2c_enable_disable(struct bcm_iproc_i2c_dev *iproc_i2c,
|
||||
bool enable);
|
||||
|
||||
static inline u32 iproc_i2c_rd_reg(struct bcm_iproc_i2c_dev *iproc_i2c,
|
||||
u32 offset)
|
||||
{
|
||||
|
@ -264,8 +259,8 @@ static inline void iproc_i2c_wr_reg(struct bcm_iproc_i2c_dev *iproc_i2c,
|
|||
}
|
||||
}
|
||||
|
||||
static void bcm_iproc_i2c_slave_init(
|
||||
struct bcm_iproc_i2c_dev *iproc_i2c, bool need_reset)
|
||||
static void bcm_iproc_i2c_slave_init(struct bcm_iproc_i2c_dev *iproc_i2c,
|
||||
bool need_reset)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
|
@ -276,8 +271,8 @@ static void bcm_iproc_i2c_slave_init(
|
|||
val |= BIT(CFG_RESET_SHIFT);
|
||||
iproc_i2c_wr_reg(iproc_i2c, CFG_OFFSET, val);
|
||||
|
||||
/* wait 100 usec per spec */
|
||||
udelay(100);
|
||||
/* wait approximately 100 usec as per spec */
|
||||
usleep_range(100, 200);
|
||||
|
||||
/* bring controller out of reset */
|
||||
val &= ~(BIT(CFG_RESET_SHIFT));
|
||||
|
@ -316,6 +311,19 @@ static void bcm_iproc_i2c_slave_init(
|
|||
iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val);
|
||||
}
|
||||
|
||||
static void bcm_iproc_i2c_enable_disable(struct bcm_iproc_i2c_dev *iproc_i2c,
|
||||
bool enable)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = iproc_i2c_rd_reg(iproc_i2c, CFG_OFFSET);
|
||||
if (enable)
|
||||
val |= BIT(CFG_EN_SHIFT);
|
||||
else
|
||||
val &= ~BIT(CFG_EN_SHIFT);
|
||||
iproc_i2c_wr_reg(iproc_i2c, CFG_OFFSET, val);
|
||||
}
|
||||
|
||||
static bool bcm_iproc_i2c_check_slave_status
|
||||
(struct bcm_iproc_i2c_dev *iproc_i2c, u32 status)
|
||||
{
|
||||
|
@ -438,7 +446,6 @@ static bool bcm_iproc_i2c_slave_isr(struct bcm_iproc_i2c_dev *iproc_i2c,
|
|||
u32 val;
|
||||
u8 value;
|
||||
|
||||
|
||||
if (status & BIT(IS_S_TX_UNDERRUN_SHIFT)) {
|
||||
iproc_i2c->tx_underrun++;
|
||||
if (iproc_i2c->tx_underrun == 1)
|
||||
|
@ -542,7 +549,7 @@ static bool bcm_iproc_i2c_slave_isr(struct bcm_iproc_i2c_dev *iproc_i2c,
|
|||
static void bcm_iproc_i2c_read_valid_bytes(struct bcm_iproc_i2c_dev *iproc_i2c)
|
||||
{
|
||||
struct i2c_msg *msg = iproc_i2c->msg;
|
||||
uint32_t val;
|
||||
u32 val;
|
||||
|
||||
/* Read valid data from RX FIFO */
|
||||
while (iproc_i2c->rx_bytes < msg->len) {
|
||||
|
@ -688,8 +695,8 @@ static void bcm_iproc_i2c_init(struct bcm_iproc_i2c_dev *iproc_i2c)
|
|||
val &= ~(BIT(CFG_EN_SHIFT));
|
||||
iproc_i2c_wr_reg(iproc_i2c, CFG_OFFSET, val);
|
||||
|
||||
/* wait 100 usec per spec */
|
||||
udelay(100);
|
||||
/* wait approximately 100 usec as per spec */
|
||||
usleep_range(100, 200);
|
||||
|
||||
/* bring controller out of reset */
|
||||
val &= ~(BIT(CFG_RESET_SHIFT));
|
||||
|
@ -708,19 +715,6 @@ static void bcm_iproc_i2c_init(struct bcm_iproc_i2c_dev *iproc_i2c)
|
|||
iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, 0xffffffff);
|
||||
}
|
||||
|
||||
static void bcm_iproc_i2c_enable_disable(struct bcm_iproc_i2c_dev *iproc_i2c,
|
||||
bool enable)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = iproc_i2c_rd_reg(iproc_i2c, CFG_OFFSET);
|
||||
if (enable)
|
||||
val |= BIT(CFG_EN_SHIFT);
|
||||
else
|
||||
val &= ~BIT(CFG_EN_SHIFT);
|
||||
iproc_i2c_wr_reg(iproc_i2c, CFG_OFFSET, val);
|
||||
}
|
||||
|
||||
static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *iproc_i2c,
|
||||
struct i2c_msg *msg)
|
||||
{
|
||||
|
@ -734,31 +728,31 @@ static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *iproc_i2c,
|
|||
return 0;
|
||||
|
||||
case M_CMD_STATUS_LOST_ARB:
|
||||
dev_dbg(iproc_i2c->device, "lost bus arbitration\n");
|
||||
dev_err(iproc_i2c->device, "lost bus arbitration\n");
|
||||
return -EAGAIN;
|
||||
|
||||
case M_CMD_STATUS_NACK_ADDR:
|
||||
dev_dbg(iproc_i2c->device, "NAK addr:0x%02x\n", msg->addr);
|
||||
dev_err(iproc_i2c->device, "NAK addr:0x%02x\n", msg->addr);
|
||||
return -ENXIO;
|
||||
|
||||
case M_CMD_STATUS_NACK_DATA:
|
||||
dev_dbg(iproc_i2c->device, "NAK data\n");
|
||||
dev_err(iproc_i2c->device, "NAK data\n");
|
||||
return -ENXIO;
|
||||
|
||||
case M_CMD_STATUS_TIMEOUT:
|
||||
dev_dbg(iproc_i2c->device, "bus timeout\n");
|
||||
dev_err(iproc_i2c->device, "bus timeout\n");
|
||||
return -ETIMEDOUT;
|
||||
|
||||
case M_CMD_STATUS_FIFO_UNDERRUN:
|
||||
dev_dbg(iproc_i2c->device, "FIFO under-run\n");
|
||||
dev_err(iproc_i2c->device, "FIFO under-run\n");
|
||||
return -ENXIO;
|
||||
|
||||
case M_CMD_STATUS_RX_FIFO_FULL:
|
||||
dev_dbg(iproc_i2c->device, "RX FIFO full\n");
|
||||
dev_err(iproc_i2c->device, "RX FIFO full\n");
|
||||
return -ETIMEDOUT;
|
||||
|
||||
default:
|
||||
dev_dbg(iproc_i2c->device, "unknown error code=%d\n", val);
|
||||
dev_err(iproc_i2c->device, "unknown error code=%d\n", val);
|
||||
|
||||
/* re-initialize i2c for recovery */
|
||||
bcm_iproc_i2c_enable_disable(iproc_i2c, false);
|
||||
|
@ -833,7 +827,7 @@ static int bcm_iproc_i2c_xfer_wait(struct bcm_iproc_i2c_dev *iproc_i2c,
|
|||
* The i2c quirks are set to enforce this rule.
|
||||
*/
|
||||
static int bcm_iproc_i2c_xfer_internal(struct bcm_iproc_i2c_dev *iproc_i2c,
|
||||
struct i2c_msg *msgs, bool process_call)
|
||||
struct i2c_msg *msgs, bool process_call)
|
||||
{
|
||||
int i;
|
||||
u8 addr;
|
||||
|
@ -842,8 +836,8 @@ static int bcm_iproc_i2c_xfer_internal(struct bcm_iproc_i2c_dev *iproc_i2c,
|
|||
struct i2c_msg *msg = &msgs[0];
|
||||
|
||||
/* check if bus is busy */
|
||||
if (!!(iproc_i2c_rd_reg(iproc_i2c,
|
||||
M_CMD_OFFSET) & BIT(M_CMD_START_BUSY_SHIFT))) {
|
||||
if (iproc_i2c_rd_reg(iproc_i2c,
|
||||
M_CMD_OFFSET) & BIT(M_CMD_START_BUSY_SHIFT)) {
|
||||
dev_warn(iproc_i2c->device, "bus is busy\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
@ -970,14 +964,14 @@ static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter,
|
|||
|
||||
ret = bcm_iproc_i2c_xfer_internal(iproc_i2c, msgs, process_call);
|
||||
if (ret) {
|
||||
dev_dbg(iproc_i2c->device, "xfer failed\n");
|
||||
dev_err(iproc_i2c->device, "xfer failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
static uint32_t bcm_iproc_i2c_functionality(struct i2c_adapter *adap)
|
||||
static u32 bcm_iproc_i2c_functionality(struct i2c_adapter *adap)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
|
@ -989,6 +983,63 @@ static uint32_t bcm_iproc_i2c_functionality(struct i2c_adapter *adap)
|
|||
return val;
|
||||
}
|
||||
|
||||
static int bcm_iproc_i2c_reg_slave(struct i2c_client *slave)
|
||||
{
|
||||
struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(slave->adapter);
|
||||
|
||||
if (iproc_i2c->slave)
|
||||
return -EBUSY;
|
||||
|
||||
if (slave->flags & I2C_CLIENT_TEN)
|
||||
return -EAFNOSUPPORT;
|
||||
|
||||
iproc_i2c->slave = slave;
|
||||
|
||||
tasklet_init(&iproc_i2c->slave_rx_tasklet, slave_rx_tasklet_fn,
|
||||
(unsigned long)iproc_i2c);
|
||||
|
||||
bcm_iproc_i2c_slave_init(iproc_i2c, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcm_iproc_i2c_unreg_slave(struct i2c_client *slave)
|
||||
{
|
||||
struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(slave->adapter);
|
||||
u32 tmp;
|
||||
|
||||
if (!iproc_i2c->slave)
|
||||
return -EINVAL;
|
||||
|
||||
disable_irq(iproc_i2c->irq);
|
||||
|
||||
tasklet_kill(&iproc_i2c->slave_rx_tasklet);
|
||||
|
||||
/* disable all slave interrupts */
|
||||
tmp = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET);
|
||||
tmp &= ~(IE_S_ALL_INTERRUPT_MASK <<
|
||||
IE_S_ALL_INTERRUPT_SHIFT);
|
||||
iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, tmp);
|
||||
|
||||
/* Erase the slave address programmed */
|
||||
tmp = iproc_i2c_rd_reg(iproc_i2c, S_CFG_SMBUS_ADDR_OFFSET);
|
||||
tmp &= ~BIT(S_CFG_EN_NIC_SMB_ADDR3_SHIFT);
|
||||
iproc_i2c_wr_reg(iproc_i2c, S_CFG_SMBUS_ADDR_OFFSET, tmp);
|
||||
|
||||
/* flush TX/RX FIFOs */
|
||||
tmp = (BIT(S_FIFO_RX_FLUSH_SHIFT) | BIT(S_FIFO_TX_FLUSH_SHIFT));
|
||||
iproc_i2c_wr_reg(iproc_i2c, S_FIFO_CTRL_OFFSET, tmp);
|
||||
|
||||
/* clear all pending slave interrupts */
|
||||
iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, ISR_MASK_SLAVE);
|
||||
|
||||
iproc_i2c->slave = NULL;
|
||||
|
||||
enable_irq(iproc_i2c->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_algorithm bcm_iproc_algo = {
|
||||
.master_xfer = bcm_iproc_i2c_xfer,
|
||||
.functionality = bcm_iproc_i2c_functionality,
|
||||
|
@ -1010,21 +1061,18 @@ static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *iproc_i2c)
|
|||
"clock-frequency", &bus_speed);
|
||||
if (ret < 0) {
|
||||
dev_info(iproc_i2c->device,
|
||||
"unable to interpret clock-frequency DT property\n");
|
||||
"unable to interpret clock-frequency DT property\n");
|
||||
bus_speed = I2C_MAX_STANDARD_MODE_FREQ;
|
||||
}
|
||||
|
||||
if (bus_speed < I2C_MAX_STANDARD_MODE_FREQ) {
|
||||
dev_err(iproc_i2c->device, "%d Hz bus speed not supported\n",
|
||||
bus_speed);
|
||||
dev_err(iproc_i2c->device,
|
||||
"valid speeds are 100khz and 400khz\n");
|
||||
return -EINVAL;
|
||||
} else if (bus_speed < I2C_MAX_FAST_MODE_FREQ) {
|
||||
if (bus_speed < I2C_MAX_STANDARD_MODE_FREQ)
|
||||
return dev_err_probe(iproc_i2c->device, -EINVAL,
|
||||
"%d Hz not supported (out of 100-400 kHz range)\n",
|
||||
bus_speed);
|
||||
else if (bus_speed < I2C_MAX_FAST_MODE_FREQ)
|
||||
bus_speed = I2C_MAX_STANDARD_MODE_FREQ;
|
||||
} else {
|
||||
else
|
||||
bus_speed = I2C_MAX_FAST_MODE_FREQ;
|
||||
}
|
||||
|
||||
iproc_i2c->bus_speed = bus_speed;
|
||||
val = iproc_i2c_rd_reg(iproc_i2c, TIM_CFG_OFFSET);
|
||||
|
@ -1039,9 +1087,9 @@ static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *iproc_i2c)
|
|||
|
||||
static int bcm_iproc_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
int irq, ret = 0;
|
||||
struct bcm_iproc_i2c_dev *iproc_i2c;
|
||||
struct i2c_adapter *adap;
|
||||
int irq, ret;
|
||||
|
||||
iproc_i2c = devm_kzalloc(&pdev->dev, sizeof(*iproc_i2c),
|
||||
GFP_KERNEL);
|
||||
|
@ -1066,11 +1114,9 @@ static int bcm_iproc_i2c_probe(struct platform_device *pdev)
|
|||
ret = of_property_read_u32(iproc_i2c->device->of_node,
|
||||
"brcm,ape-hsls-addr-mask",
|
||||
&iproc_i2c->ape_addr_mask);
|
||||
if (ret < 0) {
|
||||
dev_err(iproc_i2c->device,
|
||||
"'brcm,ape-hsls-addr-mask' missing\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (ret < 0)
|
||||
return dev_err_probe(iproc_i2c->device, ret,
|
||||
"'brcm,ape-hsls-addr-mask' missing\n");
|
||||
|
||||
spin_lock_init(&iproc_i2c->idm_lock);
|
||||
|
||||
|
@ -1090,11 +1136,9 @@ static int bcm_iproc_i2c_probe(struct platform_device *pdev)
|
|||
ret = devm_request_irq(iproc_i2c->device, irq,
|
||||
bcm_iproc_i2c_isr, 0, pdev->name,
|
||||
iproc_i2c);
|
||||
if (ret < 0) {
|
||||
dev_err(iproc_i2c->device,
|
||||
"unable to request irq %i\n", irq);
|
||||
return ret;
|
||||
}
|
||||
if (ret < 0)
|
||||
return dev_err_probe(iproc_i2c->device, ret,
|
||||
"unable to request irq %i\n", irq);
|
||||
|
||||
iproc_i2c->irq = irq;
|
||||
} else {
|
||||
|
@ -1106,9 +1150,8 @@ static int bcm_iproc_i2c_probe(struct platform_device *pdev)
|
|||
|
||||
adap = &iproc_i2c->adapter;
|
||||
i2c_set_adapdata(adap, iproc_i2c);
|
||||
snprintf(adap->name, sizeof(adap->name),
|
||||
"Broadcom iProc (%s)",
|
||||
of_node_full_name(iproc_i2c->device->of_node));
|
||||
snprintf(adap->name, sizeof(adap->name), "Broadcom iProc (%s)",
|
||||
of_node_full_name(iproc_i2c->device->of_node));
|
||||
adap->algo = &bcm_iproc_algo;
|
||||
adap->quirks = &bcm_iproc_i2c_quirks;
|
||||
adap->dev.parent = &pdev->dev;
|
||||
|
@ -1182,62 +1225,6 @@ static const struct dev_pm_ops bcm_iproc_i2c_pm_ops = {
|
|||
.resume_early = &bcm_iproc_i2c_resume
|
||||
};
|
||||
|
||||
static int bcm_iproc_i2c_reg_slave(struct i2c_client *slave)
|
||||
{
|
||||
struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(slave->adapter);
|
||||
|
||||
if (iproc_i2c->slave)
|
||||
return -EBUSY;
|
||||
|
||||
if (slave->flags & I2C_CLIENT_TEN)
|
||||
return -EAFNOSUPPORT;
|
||||
|
||||
iproc_i2c->slave = slave;
|
||||
|
||||
tasklet_init(&iproc_i2c->slave_rx_tasklet, slave_rx_tasklet_fn,
|
||||
(unsigned long)iproc_i2c);
|
||||
|
||||
bcm_iproc_i2c_slave_init(iproc_i2c, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcm_iproc_i2c_unreg_slave(struct i2c_client *slave)
|
||||
{
|
||||
u32 tmp;
|
||||
struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(slave->adapter);
|
||||
|
||||
if (!iproc_i2c->slave)
|
||||
return -EINVAL;
|
||||
|
||||
disable_irq(iproc_i2c->irq);
|
||||
|
||||
tasklet_kill(&iproc_i2c->slave_rx_tasklet);
|
||||
|
||||
/* disable all slave interrupts */
|
||||
tmp = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET);
|
||||
tmp &= ~(IE_S_ALL_INTERRUPT_MASK <<
|
||||
IE_S_ALL_INTERRUPT_SHIFT);
|
||||
iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, tmp);
|
||||
|
||||
/* Erase the slave address programmed */
|
||||
tmp = iproc_i2c_rd_reg(iproc_i2c, S_CFG_SMBUS_ADDR_OFFSET);
|
||||
tmp &= ~BIT(S_CFG_EN_NIC_SMB_ADDR3_SHIFT);
|
||||
iproc_i2c_wr_reg(iproc_i2c, S_CFG_SMBUS_ADDR_OFFSET, tmp);
|
||||
|
||||
/* flush TX/RX FIFOs */
|
||||
tmp = (BIT(S_FIFO_RX_FLUSH_SHIFT) | BIT(S_FIFO_TX_FLUSH_SHIFT));
|
||||
iproc_i2c_wr_reg(iproc_i2c, S_FIFO_CTRL_OFFSET, tmp);
|
||||
|
||||
/* clear all pending slave interrupts */
|
||||
iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, ISR_MASK_SLAVE);
|
||||
|
||||
iproc_i2c->slave = NULL;
|
||||
|
||||
enable_irq(iproc_i2c->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id bcm_iproc_i2c_of_match[] = {
|
||||
{
|
||||
.compatible = "brcm,iproc-i2c",
|
||||
|
|
|
@ -551,7 +551,8 @@ out:
|
|||
|
||||
static u32 i2c_davinci_func(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
|
||||
I2C_FUNC_PROTOCOL_MANGLING;
|
||||
}
|
||||
|
||||
static void terminate_read(struct davinci_i2c_dev *dev)
|
||||
|
|
205
drivers/i2c/busses/i2c-designware-amdisp.c
Normal file
205
drivers/i2c/busses/i2c-designware-amdisp.c
Normal file
|
@ -0,0 +1,205 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Based on Synopsys DesignWare I2C adapter driver.
|
||||
*
|
||||
* Copyright (C) 2025 Advanced Micro Devices, Inc.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include "i2c-designware-core.h"
|
||||
|
||||
#define DRV_NAME "amd_isp_i2c_designware"
|
||||
#define AMD_ISP_I2C_INPUT_CLK 100 /* Mhz */
|
||||
|
||||
static void amd_isp_dw_i2c_plat_pm_cleanup(struct dw_i2c_dev *i2c_dev)
|
||||
{
|
||||
pm_runtime_disable(i2c_dev->dev);
|
||||
|
||||
if (i2c_dev->shared_with_punit)
|
||||
pm_runtime_put_noidle(i2c_dev->dev);
|
||||
}
|
||||
|
||||
static inline u32 amd_isp_dw_i2c_get_clk_rate(struct dw_i2c_dev *i2c_dev)
|
||||
{
|
||||
return AMD_ISP_I2C_INPUT_CLK * 1000;
|
||||
}
|
||||
|
||||
static int amd_isp_dw_i2c_plat_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct dw_i2c_dev *isp_i2c_dev;
|
||||
struct i2c_adapter *adap;
|
||||
int ret;
|
||||
|
||||
isp_i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*isp_i2c_dev), GFP_KERNEL);
|
||||
if (!isp_i2c_dev)
|
||||
return -ENOMEM;
|
||||
isp_i2c_dev->dev = &pdev->dev;
|
||||
|
||||
pdev->dev.init_name = DRV_NAME;
|
||||
|
||||
/*
|
||||
* Use the polling mode to send/receive the data, because
|
||||
* no IRQ connection from ISP I2C
|
||||
*/
|
||||
isp_i2c_dev->flags |= ACCESS_POLLING;
|
||||
platform_set_drvdata(pdev, isp_i2c_dev);
|
||||
|
||||
isp_i2c_dev->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(isp_i2c_dev->base))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(isp_i2c_dev->base),
|
||||
"failed to get IOMEM resource\n");
|
||||
|
||||
isp_i2c_dev->get_clk_rate_khz = amd_isp_dw_i2c_get_clk_rate;
|
||||
ret = i2c_dw_fw_parse_and_configure(isp_i2c_dev);
|
||||
if (ret)
|
||||
return dev_err_probe(&pdev->dev, ret,
|
||||
"failed to parse i2c dw fwnode and configure\n");
|
||||
|
||||
i2c_dw_configure(isp_i2c_dev);
|
||||
|
||||
adap = &isp_i2c_dev->adapter;
|
||||
adap->owner = THIS_MODULE;
|
||||
ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev));
|
||||
adap->dev.of_node = pdev->dev.of_node;
|
||||
/* use dynamically allocated adapter id */
|
||||
adap->nr = -1;
|
||||
|
||||
if (isp_i2c_dev->flags & ACCESS_NO_IRQ_SUSPEND)
|
||||
dev_pm_set_driver_flags(&pdev->dev,
|
||||
DPM_FLAG_SMART_PREPARE);
|
||||
else
|
||||
dev_pm_set_driver_flags(&pdev->dev,
|
||||
DPM_FLAG_SMART_PREPARE |
|
||||
DPM_FLAG_SMART_SUSPEND);
|
||||
|
||||
device_enable_async_suspend(&pdev->dev);
|
||||
|
||||
if (isp_i2c_dev->shared_with_punit)
|
||||
pm_runtime_get_noresume(&pdev->dev);
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
|
||||
ret = i2c_dw_probe(isp_i2c_dev);
|
||||
if (ret) {
|
||||
dev_err_probe(&pdev->dev, ret, "i2c_dw_probe failed\n");
|
||||
goto error_release_rpm;
|
||||
}
|
||||
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
error_release_rpm:
|
||||
amd_isp_dw_i2c_plat_pm_cleanup(isp_i2c_dev);
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void amd_isp_dw_i2c_plat_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dw_i2c_dev *isp_i2c_dev = platform_get_drvdata(pdev);
|
||||
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
|
||||
i2c_del_adapter(&isp_i2c_dev->adapter);
|
||||
|
||||
i2c_dw_disable(isp_i2c_dev);
|
||||
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
amd_isp_dw_i2c_plat_pm_cleanup(isp_i2c_dev);
|
||||
}
|
||||
|
||||
static int amd_isp_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 amd_isp_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;
|
||||
|
||||
i2c_dw_disable(i_dev);
|
||||
i2c_dw_prepare_clk(i_dev, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amd_isp_dw_i2c_plat_suspend(struct device *dev)
|
||||
{
|
||||
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
if (!i_dev)
|
||||
return -ENODEV;
|
||||
|
||||
ret = amd_isp_dw_i2c_plat_runtime_suspend(dev);
|
||||
if (!ret)
|
||||
i2c_mark_adapter_suspended(&i_dev->adapter);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int amd_isp_dw_i2c_plat_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
|
||||
|
||||
if (!i_dev)
|
||||
return -ENODEV;
|
||||
|
||||
if (!i_dev->shared_with_punit)
|
||||
i2c_dw_prepare_clk(i_dev, true);
|
||||
if (i_dev->init)
|
||||
i_dev->init(i_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amd_isp_dw_i2c_plat_resume(struct device *dev)
|
||||
{
|
||||
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
|
||||
|
||||
amd_isp_dw_i2c_plat_runtime_resume(dev);
|
||||
i2c_mark_adapter_resumed(&i_dev->adapter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops amd_isp_dw_i2c_dev_pm_ops = {
|
||||
.prepare = pm_sleep_ptr(amd_isp_dw_i2c_plat_prepare),
|
||||
LATE_SYSTEM_SLEEP_PM_OPS(amd_isp_dw_i2c_plat_suspend, amd_isp_dw_i2c_plat_resume)
|
||||
RUNTIME_PM_OPS(amd_isp_dw_i2c_plat_runtime_suspend, amd_isp_dw_i2c_plat_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
/* Work with hotplug and coldplug */
|
||||
MODULE_ALIAS("platform:amd_isp_i2c_designware");
|
||||
|
||||
static struct platform_driver amd_isp_dw_i2c_driver = {
|
||||
.probe = amd_isp_dw_i2c_plat_probe,
|
||||
.remove = amd_isp_dw_i2c_plat_remove,
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.pm = pm_ptr(&amd_isp_dw_i2c_dev_pm_ops),
|
||||
},
|
||||
};
|
||||
module_platform_driver(amd_isp_dw_i2c_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter in AMD ISP");
|
||||
MODULE_IMPORT_NS("I2C_DW");
|
||||
MODULE_IMPORT_NS("I2C_DW_COMMON");
|
||||
MODULE_AUTHOR("Venkata Narendra Kumar Gutta <vengutta@amd.com>");
|
||||
MODULE_AUTHOR("Pratap Nirujogi <pratap.nirujogi@amd.com>");
|
||||
MODULE_AUTHOR("Bin Du <bin.du@amd.com>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -572,8 +572,10 @@ u32 i2c_dw_clk_rate(struct dw_i2c_dev *dev)
|
|||
* Clock is not necessary if we got LCNT/HCNT values directly from
|
||||
* the platform code.
|
||||
*/
|
||||
if (WARN_ON_ONCE(!dev->get_clk_rate_khz))
|
||||
if (!dev->get_clk_rate_khz) {
|
||||
dev_dbg_once(dev->dev, "Callback get_clk_rate_khz() is not defined\n");
|
||||
return 0;
|
||||
}
|
||||
return dev->get_clk_rate_khz(dev);
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
|
||||
static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev)
|
||||
{
|
||||
return clk_get_rate(dev->clk) / KILO;
|
||||
return clk_get_rate(dev->clk) / HZ_PER_KHZ;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
|
|
@ -96,7 +96,7 @@ static int i2c_dw_unreg_slave(struct i2c_client *slave)
|
|||
i2c_dw_disable(dev);
|
||||
synchronize_irq(dev->irq);
|
||||
dev->slave = NULL;
|
||||
pm_runtime_put(dev->dev);
|
||||
pm_runtime_put_sync_suspend(dev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1180,7 +1180,7 @@ static void i801_probe_optional_targets(struct i801_priv *priv)
|
|||
#ifdef CONFIG_I2C_I801_MUX
|
||||
if (!priv->mux_pdev)
|
||||
#endif
|
||||
i2c_register_spd(&priv->adapter);
|
||||
i2c_register_spd_write_enable(&priv->adapter);
|
||||
}
|
||||
#else
|
||||
static void __init input_apanel_init(void) {}
|
||||
|
@ -1283,7 +1283,7 @@ static int i801_notifier_call(struct notifier_block *nb, unsigned long action,
|
|||
return NOTIFY_DONE;
|
||||
|
||||
/* Call i2c_register_spd for muxed child segments */
|
||||
i2c_register_spd(to_i2c_adapter(dev));
|
||||
i2c_register_spd_write_enable(to_i2c_adapter(dev));
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
|
|
@ -1711,11 +1711,11 @@ static int i2c_imx_probe(struct platform_device *pdev)
|
|||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
return dev_err_probe(&pdev->dev, irq, "can't get IRQ\n");
|
||||
|
||||
base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(base), "can't get IO memory\n");
|
||||
|
||||
phy_addr = (dma_addr_t)res->start;
|
||||
i2c_imx = devm_kzalloc(&pdev->dev, sizeof(*i2c_imx), GFP_KERNEL);
|
||||
|
@ -1810,13 +1810,15 @@ static int i2c_imx_probe(struct platform_device *pdev)
|
|||
*/
|
||||
ret = i2c_imx_dma_request(i2c_imx, phy_addr);
|
||||
if (ret) {
|
||||
if (ret == -EPROBE_DEFER)
|
||||
if (ret == -EPROBE_DEFER) {
|
||||
dev_err_probe(&pdev->dev, ret, "can't get DMA channels\n");
|
||||
goto clk_notifier_unregister;
|
||||
else if (ret == -ENODEV)
|
||||
} else if (ret == -ENODEV) {
|
||||
dev_dbg(&pdev->dev, "Only use PIO mode\n");
|
||||
else
|
||||
} else {
|
||||
dev_warn(&pdev->dev, "Failed to setup DMA (%pe), only use PIO mode\n",
|
||||
ERR_PTR(ret));
|
||||
}
|
||||
}
|
||||
|
||||
/* Add I2C adapter */
|
||||
|
|
|
@ -933,7 +933,7 @@ ismt_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|||
return err;
|
||||
}
|
||||
|
||||
err = pci_request_region(pdev, SMBBAR, ismt_driver.name);
|
||||
err = pcim_request_region(pdev, SMBBAR, ismt_driver.name);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to request SMBus region 0x%lx-0x%lx\n",
|
||||
|
|
|
@ -442,8 +442,13 @@ static int i2c_lpc2k_suspend(struct device *dev)
|
|||
static int i2c_lpc2k_resume(struct device *dev)
|
||||
{
|
||||
struct lpc2k_i2c *i2c = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
clk_enable(i2c->clk);
|
||||
ret = clk_enable(i2c->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable clock.\n");
|
||||
return ret;
|
||||
}
|
||||
i2c_lpc2k_reset(i2c);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -76,6 +76,8 @@
|
|||
#define CORE_I2C_FREQ (0x14)
|
||||
#define CORE_I2C_GLITCHREG (0x18)
|
||||
#define CORE_I2C_SLAVE1_ADDR (0x1c)
|
||||
#define CORE_I2C_SMBUS_MSG_WR (0x0)
|
||||
#define CORE_I2C_SMBUS_MSG_RD (0x1)
|
||||
|
||||
#define PCLK_DIV_960 (CTRL_CR2)
|
||||
#define PCLK_DIV_256 (0)
|
||||
|
@ -424,9 +426,109 @@ static u32 mchp_corei2c_func(struct i2c_adapter *adap)
|
|||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||
}
|
||||
|
||||
static int mchp_corei2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags,
|
||||
char read_write, u8 command,
|
||||
int size, union i2c_smbus_data *data)
|
||||
{
|
||||
struct i2c_msg msgs[2];
|
||||
struct mchp_corei2c_dev *idev = i2c_get_adapdata(adap);
|
||||
u8 tx_buf[I2C_SMBUS_BLOCK_MAX + 2];
|
||||
u8 rx_buf[I2C_SMBUS_BLOCK_MAX + 1];
|
||||
int num_msgs = 1;
|
||||
|
||||
msgs[CORE_I2C_SMBUS_MSG_WR].addr = addr;
|
||||
msgs[CORE_I2C_SMBUS_MSG_WR].flags = 0;
|
||||
|
||||
if (read_write == I2C_SMBUS_READ && size <= I2C_SMBUS_BYTE)
|
||||
msgs[CORE_I2C_SMBUS_MSG_WR].flags = I2C_M_RD;
|
||||
|
||||
if (read_write == I2C_SMBUS_WRITE && size <= I2C_SMBUS_WORD_DATA)
|
||||
msgs[CORE_I2C_SMBUS_MSG_WR].len = size;
|
||||
|
||||
if (read_write == I2C_SMBUS_WRITE && size > I2C_SMBUS_BYTE) {
|
||||
msgs[CORE_I2C_SMBUS_MSG_WR].buf = tx_buf;
|
||||
msgs[CORE_I2C_SMBUS_MSG_WR].buf[0] = command;
|
||||
}
|
||||
|
||||
if (read_write == I2C_SMBUS_READ && size >= I2C_SMBUS_BYTE_DATA) {
|
||||
msgs[CORE_I2C_SMBUS_MSG_WR].buf = tx_buf;
|
||||
msgs[CORE_I2C_SMBUS_MSG_WR].buf[0] = command;
|
||||
msgs[CORE_I2C_SMBUS_MSG_RD].addr = addr;
|
||||
msgs[CORE_I2C_SMBUS_MSG_RD].flags = I2C_M_RD;
|
||||
num_msgs = 2;
|
||||
}
|
||||
|
||||
if (read_write == I2C_SMBUS_READ && size > I2C_SMBUS_QUICK)
|
||||
msgs[CORE_I2C_SMBUS_MSG_WR].len = 1;
|
||||
|
||||
switch (size) {
|
||||
case I2C_SMBUS_QUICK:
|
||||
msgs[CORE_I2C_SMBUS_MSG_WR].buf = NULL;
|
||||
return 0;
|
||||
case I2C_SMBUS_BYTE:
|
||||
if (read_write == I2C_SMBUS_WRITE)
|
||||
msgs[CORE_I2C_SMBUS_MSG_WR].buf = &command;
|
||||
else
|
||||
msgs[CORE_I2C_SMBUS_MSG_WR].buf = &data->byte;
|
||||
break;
|
||||
case I2C_SMBUS_BYTE_DATA:
|
||||
if (read_write == I2C_SMBUS_WRITE) {
|
||||
msgs[CORE_I2C_SMBUS_MSG_WR].buf[1] = data->byte;
|
||||
} else {
|
||||
msgs[CORE_I2C_SMBUS_MSG_RD].len = size - 1;
|
||||
msgs[CORE_I2C_SMBUS_MSG_RD].buf = &data->byte;
|
||||
}
|
||||
break;
|
||||
case I2C_SMBUS_WORD_DATA:
|
||||
if (read_write == I2C_SMBUS_WRITE) {
|
||||
msgs[CORE_I2C_SMBUS_MSG_WR].buf[1] = data->word & 0xFF;
|
||||
msgs[CORE_I2C_SMBUS_MSG_WR].buf[2] = (data->word >> 8) & 0xFF;
|
||||
} else {
|
||||
msgs[CORE_I2C_SMBUS_MSG_RD].len = size - 1;
|
||||
msgs[CORE_I2C_SMBUS_MSG_RD].buf = rx_buf;
|
||||
}
|
||||
break;
|
||||
case I2C_SMBUS_BLOCK_DATA:
|
||||
if (read_write == I2C_SMBUS_WRITE) {
|
||||
int data_len;
|
||||
|
||||
data_len = data->block[0];
|
||||
msgs[CORE_I2C_SMBUS_MSG_WR].len = data_len + 2;
|
||||
for (int i = 0; i <= data_len; i++)
|
||||
msgs[CORE_I2C_SMBUS_MSG_WR].buf[i + 1] = data->block[i];
|
||||
} else {
|
||||
msgs[CORE_I2C_SMBUS_MSG_RD].len = I2C_SMBUS_BLOCK_MAX + 1;
|
||||
msgs[CORE_I2C_SMBUS_MSG_RD].buf = rx_buf;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
mchp_corei2c_xfer(&idev->adapter, msgs, num_msgs);
|
||||
if (read_write == I2C_SMBUS_WRITE || size <= I2C_SMBUS_BYTE_DATA)
|
||||
return 0;
|
||||
|
||||
switch (size) {
|
||||
case I2C_SMBUS_WORD_DATA:
|
||||
data->word = (rx_buf[0] | (rx_buf[1] << 8));
|
||||
break;
|
||||
case I2C_SMBUS_BLOCK_DATA:
|
||||
if (rx_buf[0] > I2C_SMBUS_BLOCK_MAX)
|
||||
rx_buf[0] = I2C_SMBUS_BLOCK_MAX;
|
||||
/* As per protocol first member of block is size of the block. */
|
||||
for (int i = 0; i <= rx_buf[0]; i++)
|
||||
data->block[i] = rx_buf[i];
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm mchp_corei2c_algo = {
|
||||
.master_xfer = mchp_corei2c_xfer,
|
||||
.functionality = mchp_corei2c_func,
|
||||
.smbus_xfer = mchp_corei2c_smbus_xfer,
|
||||
};
|
||||
|
||||
static int mchp_corei2c_probe(struct platform_device *pdev)
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/string_choices.h>
|
||||
|
||||
/* Defines what functionality is present. */
|
||||
#define MLXBF_I2C_FUNC_SMBUS_BLOCK \
|
||||
|
@ -197,6 +198,7 @@
|
|||
|
||||
#define MLXBF_I2C_MASK_8 GENMASK(7, 0)
|
||||
#define MLXBF_I2C_MASK_16 GENMASK(15, 0)
|
||||
#define MLXBF_I2C_MASK_32 GENMASK(31, 0)
|
||||
|
||||
#define MLXBF_I2C_MST_ADDR_OFFSET 0x200
|
||||
|
||||
|
@ -223,7 +225,7 @@
|
|||
|
||||
#define MLXBF_I2C_MASTER_ENABLE \
|
||||
(MLXBF_I2C_MASTER_LOCK_BIT | MLXBF_I2C_MASTER_BUSY_BIT | \
|
||||
MLXBF_I2C_MASTER_START_BIT | MLXBF_I2C_MASTER_STOP_BIT)
|
||||
MLXBF_I2C_MASTER_START_BIT)
|
||||
|
||||
#define MLXBF_I2C_MASTER_ENABLE_WRITE \
|
||||
(MLXBF_I2C_MASTER_ENABLE | MLXBF_I2C_MASTER_CTL_WRITE_BIT)
|
||||
|
@ -337,6 +339,7 @@ enum {
|
|||
MLXBF_I2C_F_SMBUS_BLOCK = BIT(5),
|
||||
MLXBF_I2C_F_SMBUS_PEC = BIT(6),
|
||||
MLXBF_I2C_F_SMBUS_PROCESS_CALL = BIT(7),
|
||||
MLXBF_I2C_F_WRITE_WITHOUT_STOP = BIT(8),
|
||||
};
|
||||
|
||||
/* Mellanox BlueField chip type. */
|
||||
|
@ -637,16 +640,19 @@ static void mlxbf_i2c_smbus_read_data(struct mlxbf_i2c_priv *priv,
|
|||
}
|
||||
|
||||
static int mlxbf_i2c_smbus_enable(struct mlxbf_i2c_priv *priv, u8 slave,
|
||||
u8 len, u8 block_en, u8 pec_en, bool read)
|
||||
u8 len, u8 block_en, u8 pec_en, bool read,
|
||||
bool stop)
|
||||
{
|
||||
u32 command;
|
||||
u32 command = 0;
|
||||
|
||||
/* Set Master GW control word. */
|
||||
if (stop)
|
||||
command |= MLXBF_I2C_MASTER_STOP_BIT;
|
||||
if (read) {
|
||||
command = MLXBF_I2C_MASTER_ENABLE_READ;
|
||||
command |= MLXBF_I2C_MASTER_ENABLE_READ;
|
||||
command |= rol32(len, MLXBF_I2C_MASTER_READ_SHIFT);
|
||||
} else {
|
||||
command = MLXBF_I2C_MASTER_ENABLE_WRITE;
|
||||
command |= MLXBF_I2C_MASTER_ENABLE_WRITE;
|
||||
command |= rol32(len, MLXBF_I2C_MASTER_WRITE_SHIFT);
|
||||
}
|
||||
command |= rol32(slave, MLXBF_I2C_MASTER_SLV_ADDR_SHIFT);
|
||||
|
@ -681,8 +687,10 @@ mlxbf_i2c_smbus_start_transaction(struct mlxbf_i2c_priv *priv,
|
|||
u8 op_idx, data_idx, data_len, write_len, read_len;
|
||||
struct mlxbf_i2c_smbus_operation *operation;
|
||||
u8 read_en, write_en, block_en, pec_en;
|
||||
u8 slave, flags, addr;
|
||||
bool stop_after_write = true;
|
||||
u8 slave, addr;
|
||||
u8 *read_buf;
|
||||
u32 flags;
|
||||
u32 bits;
|
||||
int ret;
|
||||
|
||||
|
@ -754,7 +762,16 @@ mlxbf_i2c_smbus_start_transaction(struct mlxbf_i2c_priv *priv,
|
|||
memcpy(data_desc + data_idx,
|
||||
operation->buffer, operation->length);
|
||||
data_idx += operation->length;
|
||||
|
||||
/*
|
||||
* The stop condition can be skipped when writing on the bus
|
||||
* to implement a repeated start condition on the next read
|
||||
* as required for several SMBus and I2C operations.
|
||||
*/
|
||||
if (flags & MLXBF_I2C_F_WRITE_WITHOUT_STOP)
|
||||
stop_after_write = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* We assume that read operations are performed only once per
|
||||
* SMBus transaction. *TBD* protect this statement so it won't
|
||||
|
@ -780,7 +797,7 @@ mlxbf_i2c_smbus_start_transaction(struct mlxbf_i2c_priv *priv,
|
|||
|
||||
if (write_en) {
|
||||
ret = mlxbf_i2c_smbus_enable(priv, slave, write_len, block_en,
|
||||
pec_en, 0);
|
||||
pec_en, 0, stop_after_write);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
}
|
||||
|
@ -790,7 +807,7 @@ mlxbf_i2c_smbus_start_transaction(struct mlxbf_i2c_priv *priv,
|
|||
mlxbf_i2c_smbus_write_data(priv, (const u8 *)&addr, 1,
|
||||
MLXBF_I2C_MASTER_DATA_DESC_ADDR, true);
|
||||
ret = mlxbf_i2c_smbus_enable(priv, slave, read_len, block_en,
|
||||
pec_en, 1);
|
||||
pec_en, 1, true);
|
||||
if (!ret) {
|
||||
/* Get Master GW data descriptor. */
|
||||
mlxbf_i2c_smbus_read_data(priv, data_desc, read_len + 1,
|
||||
|
@ -896,6 +913,9 @@ mlxbf_i2c_smbus_i2c_block_func(struct mlxbf_i2c_smbus_request *request,
|
|||
request->operation[0].flags |= pec_check ? MLXBF_I2C_F_SMBUS_PEC : 0;
|
||||
request->operation[0].buffer = command;
|
||||
|
||||
if (read)
|
||||
request->operation[0].flags |= MLXBF_I2C_F_WRITE_WITHOUT_STOP;
|
||||
|
||||
/*
|
||||
* As specified in the standard, the max number of bytes to read/write
|
||||
* per block operation is 32 bytes. In Golan code, the controller can
|
||||
|
@ -1063,7 +1083,7 @@ static u32 mlxbf_i2c_get_ticks(struct mlxbf_i2c_priv *priv, u64 nanoseconds,
|
|||
* Frequency
|
||||
*/
|
||||
frequency = priv->frequency;
|
||||
ticks = (nanoseconds * frequency) / MLXBF_I2C_FREQUENCY_1GHZ;
|
||||
ticks = div_u64(nanoseconds * frequency, MLXBF_I2C_FREQUENCY_1GHZ);
|
||||
/*
|
||||
* The number of ticks is rounded down and if minimum is equal to 1
|
||||
* then add one tick.
|
||||
|
@ -1130,7 +1150,8 @@ static void mlxbf_i2c_set_timings(struct mlxbf_i2c_priv *priv,
|
|||
MLXBF_I2C_MASK_16, MLXBF_I2C_SHIFT_16);
|
||||
writel(timer, priv->timer->io + MLXBF_I2C_SMBUS_THIGH_MAX_TBUF);
|
||||
|
||||
timer = timings->timeout;
|
||||
timer = mlxbf_i2c_set_timer(priv, timings->timeout, false,
|
||||
MLXBF_I2C_MASK_32, MLXBF_I2C_SHIFT_0);
|
||||
writel(timer, priv->timer->io + MLXBF_I2C_SMBUS_SCL_LOW_TIMEOUT);
|
||||
}
|
||||
|
||||
|
@ -1140,11 +1161,7 @@ enum mlxbf_i2c_timings_config {
|
|||
MLXBF_I2C_TIMING_CONFIG_1000KHZ,
|
||||
};
|
||||
|
||||
/*
|
||||
* Note that the mlxbf_i2c_timings->timeout value is not related to the
|
||||
* bus frequency, it is impacted by the time it takes the driver to
|
||||
* complete data transmission before transaction abort.
|
||||
*/
|
||||
/* Timing values are in nanoseconds */
|
||||
static const struct mlxbf_i2c_timings mlxbf_i2c_timings[] = {
|
||||
[MLXBF_I2C_TIMING_CONFIG_100KHZ] = {
|
||||
.scl_high = 4810,
|
||||
|
@ -1159,8 +1176,8 @@ static const struct mlxbf_i2c_timings mlxbf_i2c_timings[] = {
|
|||
.scl_fall = 50,
|
||||
.hold_data = 300,
|
||||
.buf = 20000,
|
||||
.thigh_max = 5000,
|
||||
.timeout = 106500
|
||||
.thigh_max = 50000,
|
||||
.timeout = 35000000
|
||||
},
|
||||
[MLXBF_I2C_TIMING_CONFIG_400KHZ] = {
|
||||
.scl_high = 1011,
|
||||
|
@ -1175,24 +1192,24 @@ static const struct mlxbf_i2c_timings mlxbf_i2c_timings[] = {
|
|||
.scl_fall = 50,
|
||||
.hold_data = 300,
|
||||
.buf = 20000,
|
||||
.thigh_max = 5000,
|
||||
.timeout = 106500
|
||||
.thigh_max = 50000,
|
||||
.timeout = 35000000
|
||||
},
|
||||
[MLXBF_I2C_TIMING_CONFIG_1000KHZ] = {
|
||||
.scl_high = 600,
|
||||
.scl_low = 1300,
|
||||
.scl_high = 383,
|
||||
.scl_low = 460,
|
||||
.hold_start = 600,
|
||||
.setup_start = 600,
|
||||
.setup_stop = 600,
|
||||
.setup_data = 100,
|
||||
.setup_start = 260,
|
||||
.setup_stop = 260,
|
||||
.setup_data = 50,
|
||||
.sda_rise = 50,
|
||||
.sda_fall = 50,
|
||||
.scl_rise = 50,
|
||||
.scl_fall = 50,
|
||||
.hold_data = 300,
|
||||
.buf = 20000,
|
||||
.thigh_max = 5000,
|
||||
.timeout = 106500
|
||||
.buf = 500,
|
||||
.thigh_max = 50000,
|
||||
.timeout = 35000000
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1443,9 +1460,8 @@ static u64 mlxbf_i2c_calculate_freq_from_tyu(struct mlxbf_i2c_resource *corepll_
|
|||
* and PadFrequency, respectively.
|
||||
*/
|
||||
core_frequency = MLXBF_I2C_PLL_IN_FREQ * (++core_f);
|
||||
core_frequency /= (++core_r) * (++core_od);
|
||||
|
||||
return core_frequency;
|
||||
return div_u64(core_frequency, (++core_r) * (++core_od));
|
||||
}
|
||||
|
||||
static u64 mlxbf_i2c_calculate_freq_from_yu(struct mlxbf_i2c_resource *corepll_res)
|
||||
|
@ -1474,9 +1490,8 @@ static u64 mlxbf_i2c_calculate_freq_from_yu(struct mlxbf_i2c_resource *corepll_r
|
|||
* and PadFrequency, respectively.
|
||||
*/
|
||||
corepll_frequency = (MLXBF_I2C_PLL_IN_FREQ * core_f) / MLNXBF_I2C_COREPLL_CONST;
|
||||
corepll_frequency /= (++core_r) * (++core_od);
|
||||
|
||||
return corepll_frequency;
|
||||
return div_u64(corepll_frequency, (++core_r) * (++core_od));
|
||||
}
|
||||
|
||||
static int mlxbf_i2c_calculate_corepll_freq(struct platform_device *pdev,
|
||||
|
@ -2038,21 +2053,21 @@ static s32 mlxbf_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr,
|
|||
read ? &data->byte : &command, read,
|
||||
pec);
|
||||
dev_dbg(&adap->dev, "smbus %s byte, slave 0x%02x.\n",
|
||||
read ? "read" : "write", addr);
|
||||
str_read_write(read), addr);
|
||||
break;
|
||||
|
||||
case I2C_SMBUS_BYTE_DATA:
|
||||
mlxbf_i2c_smbus_data_byte_func(&request, &command, &data->byte,
|
||||
read, pec);
|
||||
dev_dbg(&adap->dev, "smbus %s byte data at 0x%02x, slave 0x%02x.\n",
|
||||
read ? "read" : "write", command, addr);
|
||||
str_read_write(read), command, addr);
|
||||
break;
|
||||
|
||||
case I2C_SMBUS_WORD_DATA:
|
||||
mlxbf_i2c_smbus_data_word_func(&request, &command,
|
||||
(u8 *)&data->word, read, pec);
|
||||
dev_dbg(&adap->dev, "smbus %s word data at 0x%02x, slave 0x%02x.\n",
|
||||
read ? "read" : "write", command, addr);
|
||||
str_read_write(read), command, addr);
|
||||
break;
|
||||
|
||||
case I2C_SMBUS_I2C_BLOCK_DATA:
|
||||
|
@ -2060,7 +2075,7 @@ static s32 mlxbf_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr,
|
|||
mlxbf_i2c_smbus_i2c_block_func(&request, &command, data->block,
|
||||
&byte_cnt, read, pec);
|
||||
dev_dbg(&adap->dev, "i2c %s block data, %d bytes at 0x%02x, slave 0x%02x.\n",
|
||||
read ? "read" : "write", byte_cnt, command, addr);
|
||||
str_read_write(read), byte_cnt, command, addr);
|
||||
break;
|
||||
|
||||
case I2C_SMBUS_BLOCK_DATA:
|
||||
|
@ -2068,7 +2083,7 @@ static s32 mlxbf_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr,
|
|||
mlxbf_i2c_smbus_block_func(&request, &command, data->block,
|
||||
&byte_cnt, read, pec);
|
||||
dev_dbg(&adap->dev, "smbus %s block data, %d bytes at 0x%02x, slave 0x%02x.\n",
|
||||
read ? "read" : "write", byte_cnt, command, addr);
|
||||
str_read_write(read), byte_cnt, command, addr);
|
||||
break;
|
||||
|
||||
case I2C_FUNC_SMBUS_PROC_CALL:
|
||||
|
|
|
@ -1115,14 +1115,10 @@ static void npcm_i2c_master_abort(struct npcm_i2c *bus)
|
|||
#if IS_ENABLED(CONFIG_I2C_SLAVE)
|
||||
static u8 npcm_i2c_get_slave_addr(struct npcm_i2c *bus, enum i2c_addr addr_type)
|
||||
{
|
||||
u8 slave_add;
|
||||
|
||||
if (addr_type > I2C_SLAVE_ADDR2 && addr_type <= I2C_SLAVE_ADDR10)
|
||||
dev_err(bus->dev, "get slave: try to use more than 2 SA not supported\n");
|
||||
|
||||
slave_add = ioread8(bus->reg + npcm_i2caddr[(int)addr_type]);
|
||||
|
||||
return slave_add;
|
||||
return ioread8(bus->reg + npcm_i2caddr[addr_type]);
|
||||
}
|
||||
|
||||
static int npcm_i2c_remove_slave_addr(struct npcm_i2c *bus, u8 slave_add)
|
||||
|
@ -2178,10 +2174,14 @@ static int npcm_i2c_init_module(struct npcm_i2c *bus, enum i2c_mode mode,
|
|||
|
||||
/* Check HW is OK: SDA and SCL should be high at this point. */
|
||||
if ((npcm_i2c_get_SDA(&bus->adap) == 0) || (npcm_i2c_get_SCL(&bus->adap) == 0)) {
|
||||
dev_err(bus->dev, "I2C%d init fail: lines are low\n", bus->num);
|
||||
dev_err(bus->dev, "SDA=%d SCL=%d\n", npcm_i2c_get_SDA(&bus->adap),
|
||||
npcm_i2c_get_SCL(&bus->adap));
|
||||
return -ENXIO;
|
||||
dev_warn(bus->dev, " I2C%d SDA=%d SCL=%d, attempting to recover\n", bus->num,
|
||||
npcm_i2c_get_SDA(&bus->adap), npcm_i2c_get_SCL(&bus->adap));
|
||||
if (npcm_i2c_recovery_tgclk(&bus->adap)) {
|
||||
dev_err(bus->dev, "I2C%d init fail: SDA=%d SCL=%d\n",
|
||||
bus->num, npcm_i2c_get_SDA(&bus->adap),
|
||||
npcm_i2c_get_SCL(&bus->adap));
|
||||
return -ENXIO;
|
||||
}
|
||||
}
|
||||
|
||||
npcm_i2c_int_enable(bus, true);
|
||||
|
|
|
@ -135,6 +135,32 @@ static void octeon_i2c_hlc_disable(struct octeon_i2c *i2c)
|
|||
octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB);
|
||||
}
|
||||
|
||||
static void octeon_i2c_block_enable(struct octeon_i2c *i2c)
|
||||
{
|
||||
u64 mode;
|
||||
|
||||
if (i2c->block_enabled || !OCTEON_REG_BLOCK_CTL(i2c))
|
||||
return;
|
||||
|
||||
i2c->block_enabled = true;
|
||||
mode = __raw_readq(i2c->twsi_base + OCTEON_REG_MODE(i2c));
|
||||
mode |= TWSX_MODE_BLOCK_MODE;
|
||||
octeon_i2c_writeq_flush(mode, i2c->twsi_base + OCTEON_REG_MODE(i2c));
|
||||
}
|
||||
|
||||
static void octeon_i2c_block_disable(struct octeon_i2c *i2c)
|
||||
{
|
||||
u64 mode;
|
||||
|
||||
if (!i2c->block_enabled || !OCTEON_REG_BLOCK_CTL(i2c))
|
||||
return;
|
||||
|
||||
i2c->block_enabled = false;
|
||||
mode = __raw_readq(i2c->twsi_base + OCTEON_REG_MODE(i2c));
|
||||
mode &= ~TWSX_MODE_BLOCK_MODE;
|
||||
octeon_i2c_writeq_flush(mode, i2c->twsi_base + OCTEON_REG_MODE(i2c));
|
||||
}
|
||||
|
||||
/**
|
||||
* octeon_i2c_hlc_wait - wait for an HLC operation to complete
|
||||
* @i2c: The struct octeon_i2c
|
||||
|
@ -281,6 +307,7 @@ static int octeon_i2c_start(struct octeon_i2c *i2c)
|
|||
u8 stat;
|
||||
|
||||
octeon_i2c_hlc_disable(i2c);
|
||||
octeon_i2c_block_disable(i2c);
|
||||
|
||||
octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB | TWSI_CTL_STA);
|
||||
ret = octeon_i2c_wait(i2c);
|
||||
|
@ -604,6 +631,125 @@ err:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* octeon_i2c_hlc_block_comp_read - high-level-controller composite block read
|
||||
* @i2c: The struct octeon_i2c
|
||||
* @msgs: msg[0] contains address, place read data into msg[1]
|
||||
*
|
||||
* i2c core command is constructed and written into the SW_TWSI register.
|
||||
* The execution of the command will result in requested data being
|
||||
* placed into a FIFO buffer, ready to be read.
|
||||
* Used in the case where the i2c xfer is for greater than 8 bytes of read data.
|
||||
*
|
||||
* Returns: 0 on success, otherwise a negative errno.
|
||||
*/
|
||||
static int octeon_i2c_hlc_block_comp_read(struct octeon_i2c *i2c, struct i2c_msg *msgs)
|
||||
{
|
||||
int ret;
|
||||
u16 len, i;
|
||||
u64 cmd;
|
||||
|
||||
octeon_i2c_hlc_enable(i2c);
|
||||
octeon_i2c_block_enable(i2c);
|
||||
|
||||
/* Write (size - 1) into block control register */
|
||||
len = msgs[1].len - 1;
|
||||
octeon_i2c_writeq_flush((u64)len, i2c->twsi_base + OCTEON_REG_BLOCK_CTL(i2c));
|
||||
|
||||
/* Prepare core command */
|
||||
cmd = SW_TWSI_V | SW_TWSI_R | SW_TWSI_SOVR | SW_TWSI_OP_7_IA;
|
||||
cmd |= (u64)(msgs[0].addr & 0x7full) << SW_TWSI_ADDR_SHIFT;
|
||||
|
||||
/* Send core command */
|
||||
ret = octeon_i2c_hlc_read_cmd(i2c, msgs[0], cmd);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
cmd = __raw_readq(i2c->twsi_base + OCTEON_REG_SW_TWSI(i2c));
|
||||
if ((cmd & SW_TWSI_R) == 0) {
|
||||
octeon_i2c_block_disable(i2c);
|
||||
return octeon_i2c_check_status(i2c, false);
|
||||
}
|
||||
|
||||
/* read data in FIFO */
|
||||
octeon_i2c_writeq_flush(TWSX_BLOCK_STS_RESET_PTR,
|
||||
i2c->twsi_base + OCTEON_REG_BLOCK_STS(i2c));
|
||||
for (i = 0; i <= len; i += 8) {
|
||||
/* Byte-swap FIFO data and copy into msg buffer */
|
||||
__be64 rd = cpu_to_be64(__raw_readq(i2c->twsi_base + OCTEON_REG_BLOCK_FIFO(i2c)));
|
||||
|
||||
memcpy(&msgs[1].buf[i], &rd, min(8, msgs[1].len - i));
|
||||
}
|
||||
|
||||
err:
|
||||
octeon_i2c_block_disable(i2c);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* octeon_i2c_hlc_block_comp_write - high-level-controller composite block write
|
||||
* @i2c: The struct octeon_i2c
|
||||
* @msgs: msg[0] contains address, msg[1] contains data to be written
|
||||
*
|
||||
* i2c core command is constructed and write data is written into the FIFO buffer.
|
||||
* The execution of the command will result in HW write, using the data in FIFO.
|
||||
* Used in the case where the i2c xfer is for greater than 8 bytes of write data.
|
||||
*
|
||||
* Returns: 0 on success, otherwise a negative errno.
|
||||
*/
|
||||
static int octeon_i2c_hlc_block_comp_write(struct octeon_i2c *i2c, struct i2c_msg *msgs)
|
||||
{
|
||||
bool set_ext;
|
||||
int ret;
|
||||
u16 len, i;
|
||||
u64 cmd, ext = 0;
|
||||
|
||||
octeon_i2c_hlc_enable(i2c);
|
||||
octeon_i2c_block_enable(i2c);
|
||||
|
||||
/* Write (size - 1) into block control register */
|
||||
len = msgs[1].len - 1;
|
||||
octeon_i2c_writeq_flush((u64)len, i2c->twsi_base + OCTEON_REG_BLOCK_CTL(i2c));
|
||||
|
||||
/* Prepare core command */
|
||||
cmd = SW_TWSI_V | SW_TWSI_SOVR | SW_TWSI_OP_7_IA;
|
||||
cmd |= (u64)(msgs[0].addr & 0x7full) << SW_TWSI_ADDR_SHIFT;
|
||||
|
||||
/* Set parameters for extended message (if required) */
|
||||
set_ext = octeon_i2c_hlc_ext(i2c, msgs[0], &cmd, &ext);
|
||||
|
||||
/* Write msg into FIFO buffer */
|
||||
octeon_i2c_writeq_flush(TWSX_BLOCK_STS_RESET_PTR,
|
||||
i2c->twsi_base + OCTEON_REG_BLOCK_STS(i2c));
|
||||
for (i = 0; i <= len; i += 8) {
|
||||
__be64 buf = 0;
|
||||
|
||||
/* Copy 8 bytes or remaining bytes from message buffer */
|
||||
memcpy(&buf, &msgs[1].buf[i], min(8, msgs[1].len - i));
|
||||
|
||||
/* Byte-swap message data and write into FIFO */
|
||||
buf = cpu_to_be64(buf);
|
||||
octeon_i2c_writeq_flush((u64)buf, i2c->twsi_base + OCTEON_REG_BLOCK_FIFO(i2c));
|
||||
}
|
||||
if (set_ext)
|
||||
octeon_i2c_writeq_flush(ext, i2c->twsi_base + OCTEON_REG_SW_TWSI_EXT(i2c));
|
||||
|
||||
/* Send command to core (send data in FIFO) */
|
||||
ret = octeon_i2c_hlc_cmd_send(i2c, cmd);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
cmd = __raw_readq(i2c->twsi_base + OCTEON_REG_SW_TWSI(i2c));
|
||||
if ((cmd & SW_TWSI_R) == 0) {
|
||||
octeon_i2c_block_disable(i2c);
|
||||
return octeon_i2c_check_status(i2c, false);
|
||||
}
|
||||
|
||||
err:
|
||||
octeon_i2c_block_disable(i2c);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* octeon_i2c_xfer - The driver's xfer function
|
||||
* @adap: Pointer to the i2c_adapter structure
|
||||
|
@ -630,13 +776,21 @@ int octeon_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
|||
if ((msgs[0].flags & I2C_M_RD) == 0 &&
|
||||
(msgs[1].flags & I2C_M_RECV_LEN) == 0 &&
|
||||
msgs[0].len > 0 && msgs[0].len <= 2 &&
|
||||
msgs[1].len > 0 && msgs[1].len <= 8 &&
|
||||
msgs[1].len > 0 &&
|
||||
msgs[0].addr == msgs[1].addr) {
|
||||
if (msgs[1].flags & I2C_M_RD)
|
||||
ret = octeon_i2c_hlc_comp_read(i2c, msgs);
|
||||
else
|
||||
ret = octeon_i2c_hlc_comp_write(i2c, msgs);
|
||||
goto out;
|
||||
if (msgs[1].len <= 8) {
|
||||
if (msgs[1].flags & I2C_M_RD)
|
||||
ret = octeon_i2c_hlc_comp_read(i2c, msgs);
|
||||
else
|
||||
ret = octeon_i2c_hlc_comp_write(i2c, msgs);
|
||||
goto out;
|
||||
} else if (msgs[1].len <= 1024 && OCTEON_REG_BLOCK_CTL(i2c)) {
|
||||
if (msgs[1].flags & I2C_M_RD)
|
||||
ret = octeon_i2c_hlc_block_comp_read(i2c, msgs);
|
||||
else
|
||||
ret = octeon_i2c_hlc_block_comp_write(i2c, msgs);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,18 +96,28 @@ struct octeon_i2c_reg_offset {
|
|||
unsigned int twsi_int;
|
||||
unsigned int sw_twsi_ext;
|
||||
unsigned int mode;
|
||||
unsigned int block_ctl;
|
||||
unsigned int block_sts;
|
||||
unsigned int block_fifo;
|
||||
};
|
||||
|
||||
#define OCTEON_REG_SW_TWSI(x) ((x)->roff.sw_twsi)
|
||||
#define OCTEON_REG_TWSI_INT(x) ((x)->roff.twsi_int)
|
||||
#define OCTEON_REG_SW_TWSI_EXT(x) ((x)->roff.sw_twsi_ext)
|
||||
#define OCTEON_REG_MODE(x) ((x)->roff.mode)
|
||||
#define OCTEON_REG_BLOCK_CTL(x) ((x)->roff.block_ctl)
|
||||
#define OCTEON_REG_BLOCK_STS(x) ((x)->roff.block_sts)
|
||||
#define OCTEON_REG_BLOCK_FIFO(x) ((x)->roff.block_fifo)
|
||||
|
||||
/* Set REFCLK_SRC and HS_MODE in TWSX_MODE register */
|
||||
/* TWSX_MODE register */
|
||||
#define TWSX_MODE_REFCLK_SRC BIT(4)
|
||||
#define TWSX_MODE_BLOCK_MODE BIT(2)
|
||||
#define TWSX_MODE_HS_MODE BIT(0)
|
||||
#define TWSX_MODE_HS_MASK (TWSX_MODE_REFCLK_SRC | TWSX_MODE_HS_MODE)
|
||||
|
||||
/* TWSX_BLOCK_STS register */
|
||||
#define TWSX_BLOCK_STS_RESET_PTR BIT(0)
|
||||
|
||||
/* Set BUS_MON_RST to reset bus monitor */
|
||||
#define BUS_MON_RST_MASK BIT(3)
|
||||
|
||||
|
@ -123,6 +133,7 @@ struct octeon_i2c {
|
|||
void __iomem *twsi_base;
|
||||
struct device *dev;
|
||||
bool hlc_enabled;
|
||||
bool block_enabled;
|
||||
bool broken_irq_mode;
|
||||
bool broken_irq_check;
|
||||
void (*int_enable)(struct octeon_i2c *);
|
||||
|
|
|
@ -5,22 +5,24 @@
|
|||
* SMBus host driver for PA Semi PWRficient
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/stddef.h>
|
||||
|
||||
#include "i2c-pasemi-core.h"
|
||||
|
||||
/* Register offsets */
|
||||
#define REG_MTXFIFO 0x00
|
||||
#define REG_MRXFIFO 0x04
|
||||
#define REG_XFSTA 0x0c
|
||||
#define REG_SMSTA 0x14
|
||||
#define REG_IMASK 0x18
|
||||
#define REG_CTL 0x1c
|
||||
|
@ -52,6 +54,12 @@
|
|||
#define CTL_UJM BIT(8)
|
||||
#define CTL_CLK_M GENMASK(7, 0)
|
||||
|
||||
/*
|
||||
* The hardware (supposedly) has a 25ms timeout for clock stretching, thus
|
||||
* use 100ms here which should be plenty.
|
||||
*/
|
||||
#define PASEMI_TRANSFER_TIMEOUT_MS 100
|
||||
|
||||
static inline void reg_write(struct pasemi_smbus *smbus, int reg, int val)
|
||||
{
|
||||
dev_dbg(smbus->dev, "smbus write reg %x val %08x\n", reg, val);
|
||||
|
@ -71,7 +79,7 @@ static inline int reg_read(struct pasemi_smbus *smbus, int reg)
|
|||
|
||||
static void pasemi_reset(struct pasemi_smbus *smbus)
|
||||
{
|
||||
u32 val = (CTL_MTR | CTL_MRR | (smbus->clk_div & CTL_CLK_M));
|
||||
u32 val = (CTL_MTR | CTL_MRR | CTL_UJM | (smbus->clk_div & CTL_CLK_M));
|
||||
|
||||
if (smbus->hw_rev >= 6)
|
||||
val |= CTL_EN;
|
||||
|
@ -80,43 +88,102 @@ static void pasemi_reset(struct pasemi_smbus *smbus)
|
|||
reinit_completion(&smbus->irq_completion);
|
||||
}
|
||||
|
||||
static void pasemi_smb_clear(struct pasemi_smbus *smbus)
|
||||
static int pasemi_smb_clear(struct pasemi_smbus *smbus)
|
||||
{
|
||||
unsigned int status;
|
||||
int ret;
|
||||
|
||||
status = reg_read(smbus, REG_SMSTA);
|
||||
/* First wait for the bus to go idle */
|
||||
ret = readx_poll_timeout(ioread32, smbus->ioaddr + REG_SMSTA,
|
||||
status, !(status & (SMSTA_XIP | SMSTA_JAM)),
|
||||
USEC_PER_MSEC,
|
||||
USEC_PER_MSEC * PASEMI_TRANSFER_TIMEOUT_MS);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(smbus->dev, "Bus is still stuck (status 0x%08x xfstatus 0x%08x)\n",
|
||||
status, reg_read(smbus, REG_XFSTA));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* If any badness happened or there is data in the FIFOs, reset the FIFOs */
|
||||
if ((status & (SMSTA_MRNE | SMSTA_JMD | SMSTA_MTO | SMSTA_TOM | SMSTA_MTN | SMSTA_MTA)) ||
|
||||
!(status & SMSTA_MTE)) {
|
||||
dev_warn(smbus->dev, "Issuing reset due to status 0x%08x (xfstatus 0x%08x)\n",
|
||||
status, reg_read(smbus, REG_XFSTA));
|
||||
pasemi_reset(smbus);
|
||||
}
|
||||
|
||||
/* Clear the flags */
|
||||
reg_write(smbus, REG_SMSTA, status);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pasemi_smb_waitready(struct pasemi_smbus *smbus)
|
||||
{
|
||||
int timeout = 100;
|
||||
unsigned int status;
|
||||
|
||||
if (smbus->use_irq) {
|
||||
reinit_completion(&smbus->irq_completion);
|
||||
reg_write(smbus, REG_IMASK, SMSTA_XEN | SMSTA_MTN);
|
||||
wait_for_completion_timeout(&smbus->irq_completion, msecs_to_jiffies(100));
|
||||
int ret = wait_for_completion_timeout(
|
||||
&smbus->irq_completion,
|
||||
msecs_to_jiffies(PASEMI_TRANSFER_TIMEOUT_MS));
|
||||
reg_write(smbus, REG_IMASK, 0);
|
||||
status = reg_read(smbus, REG_SMSTA);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(smbus->dev,
|
||||
"Completion wait failed with %d, status 0x%08x\n",
|
||||
ret, status);
|
||||
return ret;
|
||||
} else if (ret == 0) {
|
||||
dev_err(smbus->dev, "Timeout, status 0x%08x\n", status);
|
||||
return -ETIME;
|
||||
}
|
||||
} else {
|
||||
status = reg_read(smbus, REG_SMSTA);
|
||||
while (!(status & SMSTA_XEN) && timeout--) {
|
||||
msleep(1);
|
||||
status = reg_read(smbus, REG_SMSTA);
|
||||
int ret = readx_poll_timeout(
|
||||
ioread32, smbus->ioaddr + REG_SMSTA,
|
||||
status, status & SMSTA_XEN,
|
||||
USEC_PER_MSEC,
|
||||
USEC_PER_MSEC * PASEMI_TRANSFER_TIMEOUT_MS);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(smbus->dev, "Timeout, status 0x%08x\n", status);
|
||||
return -ETIME;
|
||||
}
|
||||
}
|
||||
|
||||
/* Got NACK? */
|
||||
if (status & SMSTA_MTN)
|
||||
return -ENXIO;
|
||||
/* Controller timeout? */
|
||||
if (status & SMSTA_TOM) {
|
||||
dev_err(smbus->dev, "Controller timeout, status 0x%08x\n", status);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (timeout < 0) {
|
||||
dev_warn(smbus->dev, "Timeout, status 0x%08x\n", status);
|
||||
reg_write(smbus, REG_SMSTA, status);
|
||||
/* Peripheral timeout? */
|
||||
if (status & SMSTA_MTO) {
|
||||
dev_err(smbus->dev, "Peripheral timeout, status 0x%08x\n", status);
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
/* Still stuck in a transaction? */
|
||||
if (status & SMSTA_XIP) {
|
||||
dev_err(smbus->dev, "Bus stuck, status 0x%08x\n", status);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Arbitration loss? */
|
||||
if (status & SMSTA_MTA) {
|
||||
dev_err(smbus->dev, "Arbitration loss, status 0x%08x\n", status);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* Got NACK? */
|
||||
if (status & SMSTA_MTN) {
|
||||
dev_err(smbus->dev, "NACK, status 0x%08x\n", status);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
/* Clear XEN */
|
||||
reg_write(smbus, REG_SMSTA, SMSTA_XEN);
|
||||
|
||||
|
@ -177,9 +244,9 @@ static int pasemi_i2c_xfer(struct i2c_adapter *adapter,
|
|||
struct pasemi_smbus *smbus = adapter->algo_data;
|
||||
int ret, i;
|
||||
|
||||
pasemi_smb_clear(smbus);
|
||||
|
||||
ret = 0;
|
||||
ret = pasemi_smb_clear(smbus);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < num && !ret; i++)
|
||||
ret = pasemi_i2c_xfer_msg(adapter, &msgs[i], (i == (num - 1)));
|
||||
|
@ -200,7 +267,9 @@ static int pasemi_smb_xfer(struct i2c_adapter *adapter,
|
|||
addr <<= 1;
|
||||
read_flag = read_write == I2C_SMBUS_READ;
|
||||
|
||||
pasemi_smb_clear(smbus);
|
||||
err = pasemi_smb_clear(smbus);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
switch (size) {
|
||||
case I2C_SMBUS_QUICK:
|
||||
|
|
|
@ -5,15 +5,15 @@
|
|||
* SMBus host driver for PA Semi PWRficient
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/stddef.h>
|
||||
|
||||
#include "i2c-pasemi-core.h"
|
||||
|
||||
|
|
|
@ -971,7 +971,7 @@ static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba,
|
|||
* This would allow the ee1004 to be probed incorrectly.
|
||||
*/
|
||||
if (port == 0)
|
||||
i2c_register_spd(adap);
|
||||
i2c_register_spd_write_enable(adap);
|
||||
|
||||
*padap = adap;
|
||||
return 0;
|
||||
|
|
|
@ -349,7 +349,7 @@ static void i2c_powermac_register_devices(struct i2c_adapter *adap,
|
|||
/* Fill out the rest of the info structure */
|
||||
info.addr = addr;
|
||||
info.irq = irq_of_parse_and_map(node, 0);
|
||||
info.of_node = of_node_get(node);
|
||||
info.fwnode = of_fwnode_handle(of_node_get(node));
|
||||
|
||||
newdev = i2c_new_client_device(adap, &info);
|
||||
if (IS_ERR(newdev)) {
|
||||
|
|
|
@ -71,7 +71,6 @@ enum geni_i2c_err_code {
|
|||
<< 5)
|
||||
|
||||
#define I2C_AUTO_SUSPEND_DELAY 250
|
||||
#define KHZ(freq) (1000 * freq)
|
||||
#define PACKING_BYTES_PW 4
|
||||
|
||||
#define ABORT_TIMEOUT HZ
|
||||
|
@ -148,18 +147,18 @@ struct geni_i2c_clk_fld {
|
|||
* source_clock = 19.2 MHz
|
||||
*/
|
||||
static const struct geni_i2c_clk_fld geni_i2c_clk_map_19p2mhz[] = {
|
||||
{KHZ(100), 7, 10, 12, 26},
|
||||
{KHZ(400), 2, 5, 11, 22},
|
||||
{KHZ(1000), 1, 2, 8, 18},
|
||||
{},
|
||||
{ I2C_MAX_STANDARD_MODE_FREQ, 7, 10, 12, 26 },
|
||||
{ I2C_MAX_FAST_MODE_FREQ, 2, 5, 11, 22 },
|
||||
{ I2C_MAX_FAST_MODE_PLUS_FREQ, 1, 2, 8, 18 },
|
||||
{}
|
||||
};
|
||||
|
||||
/* source_clock = 32 MHz */
|
||||
static const struct geni_i2c_clk_fld geni_i2c_clk_map_32mhz[] = {
|
||||
{KHZ(100), 8, 14, 18, 40},
|
||||
{KHZ(400), 4, 3, 11, 20},
|
||||
{KHZ(1000), 2, 3, 6, 15},
|
||||
{},
|
||||
{ I2C_MAX_STANDARD_MODE_FREQ, 8, 14, 18, 40 },
|
||||
{ I2C_MAX_FAST_MODE_FREQ, 4, 3, 11, 20 },
|
||||
{ I2C_MAX_FAST_MODE_PLUS_FREQ, 2, 3, 6, 15 },
|
||||
{}
|
||||
};
|
||||
|
||||
static int geni_i2c_clk_map_idx(struct geni_i2c_dev *gi2c)
|
||||
|
@ -812,7 +811,7 @@ static int geni_i2c_probe(struct platform_device *pdev)
|
|||
&gi2c->clk_freq_out);
|
||||
if (ret) {
|
||||
dev_info(dev, "Bus frequency not specified, default to 100kHz.\n");
|
||||
gi2c->clk_freq_out = KHZ(100);
|
||||
gi2c->clk_freq_out = I2C_MAX_STANDARD_MODE_FREQ;
|
||||
}
|
||||
|
||||
if (has_acpi_companion(dev))
|
||||
|
|
|
@ -52,6 +52,8 @@
|
|||
#define ICCR1_ICE BIT(7)
|
||||
#define ICCR1_IICRST BIT(6)
|
||||
#define ICCR1_SOWP BIT(4)
|
||||
#define ICCR1_SCLO BIT(3)
|
||||
#define ICCR1_SDAO BIT(2)
|
||||
#define ICCR1_SCLI BIT(1)
|
||||
#define ICCR1_SDAI BIT(0)
|
||||
|
||||
|
@ -151,11 +153,11 @@ static int riic_bus_barrier(struct riic_dev *riic)
|
|||
ret = readb_poll_timeout(riic->base + riic->info->regs[RIIC_ICCR2], val,
|
||||
!(val & ICCR2_BBSY), 10, riic->adapter.timeout);
|
||||
if (ret)
|
||||
return ret;
|
||||
return i2c_recover_bus(&riic->adapter);
|
||||
|
||||
if ((riic_readb(riic, RIIC_ICCR1) & (ICCR1_SDAI | ICCR1_SCLI)) !=
|
||||
(ICCR1_SDAI | ICCR1_SCLI))
|
||||
return -EBUSY;
|
||||
return i2c_recover_bus(&riic->adapter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -439,6 +441,52 @@ static int riic_init_hw(struct riic_dev *riic)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int riic_get_scl(struct i2c_adapter *adap)
|
||||
{
|
||||
struct riic_dev *riic = i2c_get_adapdata(adap);
|
||||
|
||||
return !!(riic_readb(riic, RIIC_ICCR1) & ICCR1_SCLI);
|
||||
}
|
||||
|
||||
static int riic_get_sda(struct i2c_adapter *adap)
|
||||
{
|
||||
struct riic_dev *riic = i2c_get_adapdata(adap);
|
||||
|
||||
return !!(riic_readb(riic, RIIC_ICCR1) & ICCR1_SDAI);
|
||||
}
|
||||
|
||||
static void riic_set_scl(struct i2c_adapter *adap, int val)
|
||||
{
|
||||
struct riic_dev *riic = i2c_get_adapdata(adap);
|
||||
|
||||
if (val)
|
||||
riic_clear_set_bit(riic, ICCR1_SOWP, ICCR1_SCLO, RIIC_ICCR1);
|
||||
else
|
||||
riic_clear_set_bit(riic, ICCR1_SOWP | ICCR1_SCLO, 0, RIIC_ICCR1);
|
||||
|
||||
riic_clear_set_bit(riic, 0, ICCR1_SOWP, RIIC_ICCR1);
|
||||
}
|
||||
|
||||
static void riic_set_sda(struct i2c_adapter *adap, int val)
|
||||
{
|
||||
struct riic_dev *riic = i2c_get_adapdata(adap);
|
||||
|
||||
if (val)
|
||||
riic_clear_set_bit(riic, ICCR1_SOWP, ICCR1_SDAO, RIIC_ICCR1);
|
||||
else
|
||||
riic_clear_set_bit(riic, ICCR1_SOWP | ICCR1_SDAO, 0, RIIC_ICCR1);
|
||||
|
||||
riic_clear_set_bit(riic, 0, ICCR1_SOWP, RIIC_ICCR1);
|
||||
}
|
||||
|
||||
static struct i2c_bus_recovery_info riic_bri = {
|
||||
.recover_bus = i2c_generic_scl_recovery,
|
||||
.get_scl = riic_get_scl,
|
||||
.set_scl = riic_set_scl,
|
||||
.get_sda = riic_get_sda,
|
||||
.set_sda = riic_set_sda,
|
||||
};
|
||||
|
||||
static const struct riic_irq_desc riic_irqs[] = {
|
||||
{ .res_num = 0, .isr = riic_tend_isr, .name = "riic-tend" },
|
||||
{ .res_num = 1, .isr = riic_rdrf_isr, .name = "riic-rdrf" },
|
||||
|
@ -495,6 +543,7 @@ static int riic_i2c_probe(struct platform_device *pdev)
|
|||
adap->algo = &riic_algo;
|
||||
adap->dev.parent = dev;
|
||||
adap->dev.of_node = dev->of_node;
|
||||
adap->bus_recovery_info = &riic_bri;
|
||||
|
||||
init_completion(&riic->msg_done);
|
||||
|
||||
|
|
|
@ -402,7 +402,7 @@ static const struct i2c_adapter_quirks rzv2m_i2c_quirks = {
|
|||
.flags = I2C_AQ_NO_ZERO_LEN,
|
||||
};
|
||||
|
||||
static struct i2c_algorithm rzv2m_i2c_algo = {
|
||||
static const struct i2c_algorithm rzv2m_i2c_algo = {
|
||||
.xfer = rzv2m_i2c_xfer,
|
||||
.functionality = rzv2m_i2c_func,
|
||||
};
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string_choices.h>
|
||||
|
||||
/* Transmit operation: */
|
||||
/* */
|
||||
|
@ -409,7 +410,7 @@ static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id)
|
|||
pd->sr |= sr; /* remember state */
|
||||
|
||||
dev_dbg(pd->dev, "i2c_isr 0x%02x 0x%02x %s %d %d!\n", sr, pd->sr,
|
||||
(pd->msg->flags & I2C_M_RD) ? "read" : "write",
|
||||
str_read_write(pd->msg->flags & I2C_M_RD),
|
||||
pd->pos, pd->msg->len);
|
||||
|
||||
/* Kick off TxDMA after preface was done */
|
||||
|
|
|
@ -1395,6 +1395,11 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
|
|||
ret = tegra_i2c_xfer_msg(i2c_dev, &msgs[i], MSG_END_CONTINUE);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
/* Validate message length before proceeding */
|
||||
if (msgs[i].buf[0] == 0 || msgs[i].buf[0] > I2C_SMBUS_BLOCK_MAX)
|
||||
break;
|
||||
|
||||
/* Set the msg length from first byte */
|
||||
msgs[i].len += msgs[i].buf[0];
|
||||
dev_dbg(i2c_dev->dev, "reading %d bytes\n", msgs[i].len);
|
||||
|
|
|
@ -168,6 +168,9 @@ static int thunder_i2c_probe_pci(struct pci_dev *pdev,
|
|||
i2c->roff.twsi_int = 0x1010;
|
||||
i2c->roff.sw_twsi_ext = 0x1018;
|
||||
i2c->roff.mode = 0x1038;
|
||||
i2c->roff.block_ctl = 0x1048;
|
||||
i2c->roff.block_sts = 0x1050;
|
||||
i2c->roff.block_fifo = 0x1058;
|
||||
|
||||
i2c->dev = dev;
|
||||
pci_set_drvdata(pdev, i2c);
|
||||
|
@ -175,7 +178,7 @@ static int thunder_i2c_probe_pci(struct pci_dev *pdev,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pci_request_regions(pdev, DRV_NAME);
|
||||
ret = pcim_request_all_regions(pdev, DRV_NAME);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <linux/errno.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string_choices.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/* include interfaces to usb layer */
|
||||
|
@ -71,7 +72,7 @@ static int usb_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
|
|||
|
||||
dev_dbg(&adapter->dev,
|
||||
" %d: %s (flags %d) %d bytes to 0x%02x\n",
|
||||
i, pmsg->flags & I2C_M_RD ? "read" : "write",
|
||||
i, str_read_write(pmsg->flags & I2C_M_RD),
|
||||
pmsg->flags, pmsg->len, pmsg->addr);
|
||||
|
||||
/* and directly send the message */
|
||||
|
|
|
@ -532,22 +532,16 @@ static int uniphier_fi2c_probe(struct platform_device *pdev)
|
|||
if (of_property_read_u32(dev->of_node, "clock-frequency", &bus_speed))
|
||||
bus_speed = I2C_MAX_STANDARD_MODE_FREQ;
|
||||
|
||||
if (!bus_speed || bus_speed > I2C_MAX_FAST_MODE_FREQ) {
|
||||
dev_err(dev, "invalid clock-frequency %d\n", bus_speed);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!bus_speed || bus_speed > I2C_MAX_FAST_MODE_FREQ)
|
||||
return dev_err_probe(dev, -EINVAL, "invalid clock-frequency %d\n", bus_speed);
|
||||
|
||||
priv->clk = devm_clk_get_enabled(dev, NULL);
|
||||
if (IS_ERR(priv->clk)) {
|
||||
dev_err(dev, "failed to enable clock\n");
|
||||
return PTR_ERR(priv->clk);
|
||||
}
|
||||
if (IS_ERR(priv->clk))
|
||||
return dev_err_probe(dev, PTR_ERR(priv->clk), "failed to enable clock\n");
|
||||
|
||||
clk_rate = clk_get_rate(priv->clk);
|
||||
if (!clk_rate) {
|
||||
dev_err(dev, "input clock rate should not be zero\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!clk_rate)
|
||||
return dev_err_probe(dev, -EINVAL, "input clock rate should not be zero\n");
|
||||
|
||||
priv->clk_cycle = clk_rate / bus_speed;
|
||||
init_completion(&priv->comp);
|
||||
|
@ -565,10 +559,8 @@ static int uniphier_fi2c_probe(struct platform_device *pdev)
|
|||
|
||||
ret = devm_request_irq(dev, irq, uniphier_fi2c_interrupt, 0,
|
||||
pdev->name, priv);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request irq %d\n", irq);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to request irq %d\n", irq);
|
||||
|
||||
return i2c_add_adapter(&priv->adap);
|
||||
}
|
||||
|
|
|
@ -327,22 +327,16 @@ static int uniphier_i2c_probe(struct platform_device *pdev)
|
|||
if (of_property_read_u32(dev->of_node, "clock-frequency", &bus_speed))
|
||||
bus_speed = I2C_MAX_STANDARD_MODE_FREQ;
|
||||
|
||||
if (!bus_speed || bus_speed > I2C_MAX_FAST_MODE_FREQ) {
|
||||
dev_err(dev, "invalid clock-frequency %d\n", bus_speed);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!bus_speed || bus_speed > I2C_MAX_FAST_MODE_FREQ)
|
||||
return dev_err_probe(dev, -EINVAL, "invalid clock-frequency %d\n", bus_speed);
|
||||
|
||||
priv->clk = devm_clk_get_enabled(dev, NULL);
|
||||
if (IS_ERR(priv->clk)) {
|
||||
dev_err(dev, "failed to enable clock\n");
|
||||
return PTR_ERR(priv->clk);
|
||||
}
|
||||
if (IS_ERR(priv->clk))
|
||||
return dev_err_probe(dev, PTR_ERR(priv->clk), "failed to enable clock\n");
|
||||
|
||||
clk_rate = clk_get_rate(priv->clk);
|
||||
if (!clk_rate) {
|
||||
dev_err(dev, "input clock rate should not be zero\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!clk_rate)
|
||||
return dev_err_probe(dev, -EINVAL, "input clock rate should not be zero\n");
|
||||
|
||||
priv->clk_cycle = clk_rate / bus_speed;
|
||||
init_completion(&priv->comp);
|
||||
|
@ -359,10 +353,8 @@ static int uniphier_i2c_probe(struct platform_device *pdev)
|
|||
|
||||
ret = devm_request_irq(dev, irq, uniphier_i2c_interrupt, 0, pdev->name,
|
||||
priv);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request irq %d\n", irq);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to request irq %d\n", irq);
|
||||
|
||||
return i2c_add_adapter(&priv->adap);
|
||||
}
|
||||
|
|
|
@ -89,10 +89,9 @@ static int vt586b_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|||
u8 rev;
|
||||
int res;
|
||||
|
||||
if (pm_io_base) {
|
||||
dev_err(&dev->dev, "i2c-via: Will only support one host\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
if (pm_io_base)
|
||||
return dev_err_probe(&dev->dev, -ENODEV,
|
||||
"Will only support one host\n");
|
||||
|
||||
pci_read_config_byte(dev, PM_CFG_REVID, &rev);
|
||||
|
||||
|
@ -113,10 +112,10 @@ static int vt586b_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|||
pci_read_config_word(dev, base, &pm_io_base);
|
||||
pm_io_base &= (0xff << 8);
|
||||
|
||||
if (!request_region(I2C_DIR, IOSPACE, vt586b_driver.name)) {
|
||||
dev_err(&dev->dev, "IO 0x%x-0x%x already in use\n", I2C_DIR, I2C_DIR + IOSPACE);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (!request_region(I2C_DIR, IOSPACE, vt586b_driver.name))
|
||||
return dev_err_probe(&dev->dev, -ENODEV,
|
||||
"IO 0x%x-0x%x already in use\n",
|
||||
I2C_DIR, I2C_DIR + IOSPACE);
|
||||
|
||||
outb(inb(I2C_DIR) & ~(I2C_SDA | I2C_SCL), I2C_DIR);
|
||||
outb(inb(I2C_OUT) & ~(I2C_SDA | I2C_SCL), I2C_OUT);
|
||||
|
|
|
@ -44,16 +44,13 @@ static int wmt_i2c_reset_hardware(struct viai2c *i2c)
|
|||
int err;
|
||||
|
||||
err = clk_prepare_enable(i2c->clk);
|
||||
if (err) {
|
||||
dev_err(i2c->dev, "failed to enable clock\n");
|
||||
return err;
|
||||
}
|
||||
if (err)
|
||||
return dev_err_probe(i2c->dev, err, "failed to enable clock\n");
|
||||
|
||||
err = clk_set_rate(i2c->clk, 20000000);
|
||||
if (err) {
|
||||
dev_err(i2c->dev, "failed to set clock = 20Mhz\n");
|
||||
clk_disable_unprepare(i2c->clk);
|
||||
return err;
|
||||
return dev_err_probe(i2c->dev, err, "failed to set clock = 20Mhz\n");
|
||||
}
|
||||
|
||||
writew(0, i2c->base + VIAI2C_REG_CR);
|
||||
|
@ -121,10 +118,9 @@ static int wmt_i2c_probe(struct platform_device *pdev)
|
|||
"failed to request irq %i\n", i2c->irq);
|
||||
|
||||
i2c->clk = of_clk_get(np, 0);
|
||||
if (IS_ERR(i2c->clk)) {
|
||||
dev_err(&pdev->dev, "unable to request clock\n");
|
||||
return PTR_ERR(i2c->clk);
|
||||
}
|
||||
if (IS_ERR(i2c->clk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(i2c->clk),
|
||||
"unable to request clock\n");
|
||||
|
||||
err = of_property_read_u32(np, "clock-frequency", &clk_rate);
|
||||
if (!err && clk_rate == I2C_MAX_FAST_MODE_FREQ)
|
||||
|
@ -139,10 +135,8 @@ static int wmt_i2c_probe(struct platform_device *pdev)
|
|||
adap->dev.of_node = pdev->dev.of_node;
|
||||
|
||||
err = wmt_i2c_reset_hardware(i2c);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "error initializing hardware\n");
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = i2c_add_adapter(adap);
|
||||
if (err)
|
||||
|
|
|
@ -330,30 +330,27 @@ static int vt596_probe(struct pci_dev *pdev,
|
|||
SMBHSTCFG = 0x84;
|
||||
} else {
|
||||
/* no matches at all */
|
||||
dev_err(&pdev->dev, "Cannot configure "
|
||||
"SMBus I/O Base address\n");
|
||||
return -ENODEV;
|
||||
return dev_err_probe(&pdev->dev, -ENODEV,
|
||||
"Cannot configure "
|
||||
"SMBus I/O Base address\n");
|
||||
}
|
||||
}
|
||||
|
||||
vt596_smba &= 0xfff0;
|
||||
if (vt596_smba == 0) {
|
||||
dev_err(&pdev->dev, "SMBus base address "
|
||||
"uninitialized - upgrade BIOS or use "
|
||||
"force_addr=0xaddr\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
if (vt596_smba == 0)
|
||||
return dev_err_probe(&pdev->dev, -ENODEV, "SMBus base address "
|
||||
"uninitialized - upgrade BIOS or use "
|
||||
"force_addr=0xaddr\n");
|
||||
|
||||
found:
|
||||
error = acpi_check_region(vt596_smba, 8, vt596_driver.name);
|
||||
if (error)
|
||||
return -ENODEV;
|
||||
|
||||
if (!request_region(vt596_smba, 8, vt596_driver.name)) {
|
||||
dev_err(&pdev->dev, "SMBus region 0x%x already in use!\n",
|
||||
vt596_smba);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (!request_region(vt596_smba, 8, vt596_driver.name))
|
||||
return dev_err_probe(&pdev->dev, -ENODEV,
|
||||
"SMBus region 0x%x already in use!\n",
|
||||
vt596_smba);
|
||||
|
||||
pci_read_config_byte(pdev, SMBHSTCFG, &temp);
|
||||
/* If force_addr is set, we program the new address here. Just to make
|
||||
|
@ -375,10 +372,10 @@ found:
|
|||
pci_write_config_byte(pdev, SMBHSTCFG, temp | 0x01);
|
||||
dev_info(&pdev->dev, "Enabling SMBus device\n");
|
||||
} else {
|
||||
dev_err(&pdev->dev, "SMBUS: Error: Host SMBus "
|
||||
"controller not enabled! - upgrade BIOS or "
|
||||
"use force=1\n");
|
||||
error = -ENODEV;
|
||||
error = dev_err_probe(&pdev->dev, -ENODEV,
|
||||
"SMBUS: Error: Host SMBus "
|
||||
"controller not enabled! - "
|
||||
"upgrade BIOS or use force=1\n");
|
||||
goto release_region;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/errno.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string_choices.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
@ -278,7 +279,7 @@ static int vprbrd_i2c_xfer(struct i2c_adapter *i2c, struct i2c_msg *msgs,
|
|||
|
||||
dev_dbg(&i2c->dev,
|
||||
" %d: %s (flags %d) %d bytes to 0x%02x\n",
|
||||
i, pmsg->flags & I2C_M_RD ? "read" : "write",
|
||||
i, str_read_write(pmsg->flags & I2C_M_RD),
|
||||
pmsg->flags, pmsg->len, pmsg->addr);
|
||||
|
||||
mutex_lock(&vb->lock);
|
||||
|
@ -384,15 +385,13 @@ static int vprbrd_i2c_probe(struct platform_device *pdev)
|
|||
VPRBRD_USB_REQUEST_I2C_FREQ, VPRBRD_USB_TYPE_OUT,
|
||||
0x0000, 0x0000, &vb_i2c->bus_freq_param, 1,
|
||||
VPRBRD_USB_TIMEOUT_MS);
|
||||
if (ret != 1) {
|
||||
dev_err(&pdev->dev, "failure setting i2c_bus_freq to %d\n",
|
||||
i2c_bus_freq);
|
||||
return -EIO;
|
||||
}
|
||||
if (ret != 1)
|
||||
return dev_err_probe(&pdev->dev, -EIO,
|
||||
"failure setting i2c_bus_freq to %d\n",
|
||||
i2c_bus_freq);
|
||||
} else {
|
||||
dev_err(&pdev->dev,
|
||||
"invalid i2c_bus_freq setting:%d\n", i2c_bus_freq);
|
||||
return -EIO;
|
||||
return dev_err_probe(&pdev->dev, -EIO,
|
||||
"invalid i2c_bus_freq setting:%d\n", i2c_bus_freq);
|
||||
}
|
||||
|
||||
vb_i2c->i2c.dev.parent = &pdev->dev;
|
||||
|
|
|
@ -192,10 +192,9 @@ static int virtio_i2c_probe(struct virtio_device *vdev)
|
|||
struct virtio_i2c *vi;
|
||||
int ret;
|
||||
|
||||
if (!virtio_has_feature(vdev, VIRTIO_I2C_F_ZERO_LENGTH_REQUEST)) {
|
||||
dev_err(&vdev->dev, "Zero-length request feature is mandatory\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!virtio_has_feature(vdev, VIRTIO_I2C_F_ZERO_LENGTH_REQUEST))
|
||||
return dev_err_probe(&vdev->dev, -EINVAL,
|
||||
"Zero-length request feature is mandatory\n");
|
||||
|
||||
vi = devm_kzalloc(&vdev->dev, sizeof(*vi), GFP_KERNEL);
|
||||
if (!vi)
|
||||
|
|
|
@ -101,8 +101,6 @@ struct slimpro_i2c_dev {
|
|||
struct completion rd_complete;
|
||||
u8 dma_buffer[I2C_SMBUS_BLOCK_MAX + 1]; /* dma_buffer[0] is used for length */
|
||||
u32 *resp_msg;
|
||||
phys_addr_t comm_base_addr;
|
||||
void *pcc_comm_addr;
|
||||
};
|
||||
|
||||
#define to_slimpro_i2c_dev(cl) \
|
||||
|
@ -148,7 +146,8 @@ static void slimpro_i2c_rx_cb(struct mbox_client *cl, void *mssg)
|
|||
static void slimpro_i2c_pcc_rx_cb(struct mbox_client *cl, void *msg)
|
||||
{
|
||||
struct slimpro_i2c_dev *ctx = to_slimpro_i2c_dev(cl);
|
||||
struct acpi_pcct_shared_memory *generic_comm_base = ctx->pcc_comm_addr;
|
||||
struct acpi_pcct_shared_memory __iomem *generic_comm_base =
|
||||
ctx->pcc_chan->shmem;
|
||||
|
||||
/* Check if platform sends interrupt */
|
||||
if (!xgene_word_tst_and_clr(&generic_comm_base->status,
|
||||
|
@ -169,7 +168,8 @@ static void slimpro_i2c_pcc_rx_cb(struct mbox_client *cl, void *msg)
|
|||
|
||||
static void slimpro_i2c_pcc_tx_prepare(struct slimpro_i2c_dev *ctx, u32 *msg)
|
||||
{
|
||||
struct acpi_pcct_shared_memory *generic_comm_base = ctx->pcc_comm_addr;
|
||||
struct acpi_pcct_shared_memory __iomem *generic_comm_base =
|
||||
ctx->pcc_chan->shmem;
|
||||
u32 *ptr = (void *)(generic_comm_base + 1);
|
||||
u16 status;
|
||||
int i;
|
||||
|
@ -457,22 +457,18 @@ static int xgene_slimpro_i2c_probe(struct platform_device *pdev)
|
|||
cl->tx_block = true;
|
||||
cl->rx_callback = slimpro_i2c_rx_cb;
|
||||
ctx->mbox_chan = mbox_request_channel(cl, MAILBOX_I2C_INDEX);
|
||||
if (IS_ERR(ctx->mbox_chan)) {
|
||||
dev_err(&pdev->dev, "i2c mailbox channel request failed\n");
|
||||
return PTR_ERR(ctx->mbox_chan);
|
||||
}
|
||||
if (IS_ERR(ctx->mbox_chan))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(ctx->mbox_chan),
|
||||
"i2c mailbox channel request failed\n");
|
||||
} else {
|
||||
struct pcc_mbox_chan *pcc_chan;
|
||||
const struct acpi_device_id *acpi_id;
|
||||
int version = XGENE_SLIMPRO_I2C_V1;
|
||||
|
||||
acpi_id = acpi_match_device(pdev->dev.driver->acpi_match_table,
|
||||
&pdev->dev);
|
||||
if (!acpi_id)
|
||||
return -EINVAL;
|
||||
|
||||
version = (int)acpi_id->driver_data;
|
||||
|
||||
if (device_property_read_u32(&pdev->dev, "pcc-channel",
|
||||
&ctx->mbox_idx))
|
||||
ctx->mbox_idx = MAILBOX_I2C_INDEX;
|
||||
|
@ -480,48 +476,19 @@ static int xgene_slimpro_i2c_probe(struct platform_device *pdev)
|
|||
cl->tx_block = false;
|
||||
cl->rx_callback = slimpro_i2c_pcc_rx_cb;
|
||||
pcc_chan = pcc_mbox_request_channel(cl, ctx->mbox_idx);
|
||||
if (IS_ERR(pcc_chan)) {
|
||||
dev_err(&pdev->dev, "PCC mailbox channel request failed\n");
|
||||
return PTR_ERR(pcc_chan);
|
||||
}
|
||||
if (IS_ERR(pcc_chan))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(pcc_chan),
|
||||
"PCC mailbox channel request failed\n");
|
||||
|
||||
ctx->pcc_chan = pcc_chan;
|
||||
ctx->mbox_chan = pcc_chan->mchan;
|
||||
|
||||
if (!ctx->mbox_chan->mbox->txdone_irq) {
|
||||
dev_err(&pdev->dev, "PCC IRQ not supported\n");
|
||||
rc = -ENOENT;
|
||||
rc = dev_err_probe(&pdev->dev, -ENOENT,
|
||||
"PCC IRQ not supported\n");
|
||||
goto mbox_err;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the shared communication region
|
||||
* for the OS and Platform to communicate over.
|
||||
*/
|
||||
ctx->comm_base_addr = pcc_chan->shmem_base_addr;
|
||||
if (ctx->comm_base_addr) {
|
||||
if (version == XGENE_SLIMPRO_I2C_V2)
|
||||
ctx->pcc_comm_addr = memremap(
|
||||
ctx->comm_base_addr,
|
||||
pcc_chan->shmem_size,
|
||||
MEMREMAP_WT);
|
||||
else
|
||||
ctx->pcc_comm_addr = memremap(
|
||||
ctx->comm_base_addr,
|
||||
pcc_chan->shmem_size,
|
||||
MEMREMAP_WB);
|
||||
} else {
|
||||
dev_err(&pdev->dev, "Failed to get PCC comm region\n");
|
||||
rc = -ENOENT;
|
||||
goto mbox_err;
|
||||
}
|
||||
|
||||
if (!ctx->pcc_comm_addr) {
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to ioremap PCC comm region\n");
|
||||
rc = -ENOMEM;
|
||||
goto mbox_err;
|
||||
}
|
||||
}
|
||||
rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
|
||||
if (rc)
|
||||
|
|
|
@ -1489,7 +1489,7 @@ static int xiic_i2c_probe(struct platform_device *pdev)
|
|||
pdev->name, i2c);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Cannot claim IRQ\n");
|
||||
dev_err_probe(&pdev->dev, ret, "Cannot claim IRQ\n");
|
||||
goto err_pm_disable;
|
||||
}
|
||||
|
||||
|
@ -1510,7 +1510,7 @@ static int xiic_i2c_probe(struct platform_device *pdev)
|
|||
|
||||
ret = xiic_reinit(i2c);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Cannot xiic_reinit\n");
|
||||
dev_err_probe(&pdev->dev, ret, "Cannot xiic_reinit\n");
|
||||
goto err_pm_disable;
|
||||
}
|
||||
|
||||
|
|
|
@ -500,10 +500,8 @@ static int scx200_probe(struct platform_device *pdev)
|
|||
struct resource *res;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "can't fetch device resource info\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
if (!res)
|
||||
return dev_err_probe(&pdev->dev, -ENODEV, "can't fetch device resource info\n");
|
||||
|
||||
iface = scx200_create_dev("CS5535", res->start, 0, &pdev->dev);
|
||||
if (!iface)
|
||||
|
|
|
@ -16,32 +16,65 @@
|
|||
#include <linux/property.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/lockdep.h>
|
||||
|
||||
#define ATR_MAX_ADAPTERS 100 /* Just a sanity limit */
|
||||
#define ATR_MAX_SYMLINK_LEN 11 /* Longest name is 10 chars: "channel-99" */
|
||||
|
||||
/**
|
||||
* struct i2c_atr_alias_pair - Holds the alias assigned to a client.
|
||||
* struct i2c_atr_alias_pair - Holds the alias assigned to a client address.
|
||||
* @node: List node
|
||||
* @client: Pointer to the client on the child bus
|
||||
* @addr: Address of the client on the child bus.
|
||||
* @alias: I2C alias address assigned by the driver.
|
||||
* This is the address that will be used to issue I2C transactions
|
||||
* on the parent (physical) bus.
|
||||
* @fixed: Alias pair cannot be replaced during dynamic address attachment.
|
||||
* This flag is necessary for situations where a single I2C transaction
|
||||
* contains more distinct target addresses than the ATR channel can handle.
|
||||
* It marks addresses that have already been attached to an alias so
|
||||
* that their alias pair is not evicted by a subsequent address in the same
|
||||
* transaction.
|
||||
*
|
||||
*/
|
||||
struct i2c_atr_alias_pair {
|
||||
struct list_head node;
|
||||
const struct i2c_client *client;
|
||||
bool fixed;
|
||||
u16 addr;
|
||||
u16 alias;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct i2c_atr_alias_pool - Pool of client aliases available for an ATR.
|
||||
* @size: Total number of aliases
|
||||
* @shared: Indicates if this alias pool is shared by multiple channels
|
||||
*
|
||||
* @lock: Lock protecting @aliases and @use_mask
|
||||
* @aliases: Array of aliases, must hold exactly @size elements
|
||||
* @use_mask: Mask of used aliases
|
||||
*/
|
||||
struct i2c_atr_alias_pool {
|
||||
size_t size;
|
||||
bool shared;
|
||||
|
||||
/* Protects aliases and use_mask */
|
||||
spinlock_t lock;
|
||||
u16 *aliases;
|
||||
unsigned long *use_mask;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct i2c_atr_chan - Data for a channel.
|
||||
* @adap: The &struct i2c_adapter for the channel
|
||||
* @atr: The parent I2C ATR
|
||||
* @chan_id: The ID of this channel
|
||||
* @alias_list: List of @struct i2c_atr_alias_pair containing the
|
||||
* @alias_pairs_lock: Mutex protecting @alias_pairs
|
||||
* @alias_pairs_lock_key: Lock key for @alias_pairs_lock
|
||||
* @alias_pairs: List of @struct i2c_atr_alias_pair containing the
|
||||
* assigned aliases
|
||||
* @alias_pool: Pool of available client aliases
|
||||
*
|
||||
* @orig_addrs_lock: Mutex protecting @orig_addrs
|
||||
* @orig_addrs_lock_key: Lock key for @orig_addrs_lock
|
||||
* @orig_addrs: Buffer used to store the original addresses during transmit
|
||||
* @orig_addrs_size: Size of @orig_addrs
|
||||
*/
|
||||
|
@ -50,10 +83,15 @@ struct i2c_atr_chan {
|
|||
struct i2c_atr *atr;
|
||||
u32 chan_id;
|
||||
|
||||
struct list_head alias_list;
|
||||
/* Lock alias_pairs during attach/detach */
|
||||
struct mutex alias_pairs_lock;
|
||||
struct lock_class_key alias_pairs_lock_key;
|
||||
struct list_head alias_pairs;
|
||||
struct i2c_atr_alias_pool *alias_pool;
|
||||
|
||||
/* Lock orig_addrs during xfer */
|
||||
struct mutex orig_addrs_lock;
|
||||
struct lock_class_key orig_addrs_lock_key;
|
||||
u16 *orig_addrs;
|
||||
unsigned int orig_addrs_size;
|
||||
};
|
||||
|
@ -66,11 +104,10 @@ struct i2c_atr_chan {
|
|||
* @priv: Private driver data, set with i2c_atr_set_driver_data()
|
||||
* @algo: The &struct i2c_algorithm for adapters
|
||||
* @lock: Lock for the I2C bus segment (see &struct i2c_lock_operations)
|
||||
* @lock_key: Lock key for @lock
|
||||
* @max_adapters: Maximum number of adapters this I2C ATR can have
|
||||
* @num_aliases: Number of aliases in the aliases array
|
||||
* @aliases: The aliases array
|
||||
* @alias_mask_lock: Lock protecting alias_use_mask
|
||||
* @alias_use_mask: Bitmask for used aliases in aliases array
|
||||
* @flags: Flags for ATR
|
||||
* @alias_pool: Optional common pool of available client aliases
|
||||
* @i2c_nb: Notifier for remote client add & del events
|
||||
* @adapter: Array of adapters
|
||||
*/
|
||||
|
@ -84,27 +121,135 @@ struct i2c_atr {
|
|||
struct i2c_algorithm algo;
|
||||
/* lock for the I2C bus segment (see struct i2c_lock_operations) */
|
||||
struct mutex lock;
|
||||
struct lock_class_key lock_key;
|
||||
int max_adapters;
|
||||
u32 flags;
|
||||
|
||||
size_t num_aliases;
|
||||
const u16 *aliases;
|
||||
/* Protects alias_use_mask */
|
||||
spinlock_t alias_mask_lock;
|
||||
unsigned long *alias_use_mask;
|
||||
struct i2c_atr_alias_pool *alias_pool;
|
||||
|
||||
struct notifier_block i2c_nb;
|
||||
|
||||
struct i2c_adapter *adapter[] __counted_by(max_adapters);
|
||||
};
|
||||
|
||||
static struct i2c_atr_alias_pair *
|
||||
i2c_atr_find_mapping_by_client(const struct list_head *list,
|
||||
const struct i2c_client *client)
|
||||
static struct i2c_atr_alias_pool *i2c_atr_alloc_alias_pool(size_t num_aliases, bool shared)
|
||||
{
|
||||
struct i2c_atr_alias_pool *alias_pool;
|
||||
int ret;
|
||||
|
||||
alias_pool = kzalloc(sizeof(*alias_pool), GFP_KERNEL);
|
||||
if (!alias_pool)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
alias_pool->size = num_aliases;
|
||||
|
||||
alias_pool->aliases = kcalloc(num_aliases, sizeof(*alias_pool->aliases), GFP_KERNEL);
|
||||
if (!alias_pool->aliases) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free_alias_pool;
|
||||
}
|
||||
|
||||
alias_pool->use_mask = bitmap_zalloc(num_aliases, GFP_KERNEL);
|
||||
if (!alias_pool->use_mask) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free_aliases;
|
||||
}
|
||||
|
||||
alias_pool->shared = shared;
|
||||
|
||||
spin_lock_init(&alias_pool->lock);
|
||||
|
||||
return alias_pool;
|
||||
|
||||
err_free_aliases:
|
||||
kfree(alias_pool->aliases);
|
||||
err_free_alias_pool:
|
||||
kfree(alias_pool);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static void i2c_atr_free_alias_pool(struct i2c_atr_alias_pool *alias_pool)
|
||||
{
|
||||
bitmap_free(alias_pool->use_mask);
|
||||
kfree(alias_pool->aliases);
|
||||
kfree(alias_pool);
|
||||
}
|
||||
|
||||
/* Must be called with alias_pairs_lock held */
|
||||
static struct i2c_atr_alias_pair *i2c_atr_create_c2a(struct i2c_atr_chan *chan,
|
||||
u16 alias, u16 addr)
|
||||
{
|
||||
struct i2c_atr_alias_pair *c2a;
|
||||
|
||||
list_for_each_entry(c2a, list, node) {
|
||||
if (c2a->client == client)
|
||||
lockdep_assert_held(&chan->alias_pairs_lock);
|
||||
|
||||
c2a = kzalloc(sizeof(*c2a), GFP_KERNEL);
|
||||
if (!c2a)
|
||||
return NULL;
|
||||
|
||||
c2a->addr = addr;
|
||||
c2a->alias = alias;
|
||||
|
||||
list_add(&c2a->node, &chan->alias_pairs);
|
||||
|
||||
return c2a;
|
||||
}
|
||||
|
||||
/* Must be called with alias_pairs_lock held */
|
||||
static void i2c_atr_destroy_c2a(struct i2c_atr_alias_pair **pc2a)
|
||||
{
|
||||
list_del(&(*pc2a)->node);
|
||||
kfree(*pc2a);
|
||||
*pc2a = NULL;
|
||||
}
|
||||
|
||||
static int i2c_atr_reserve_alias(struct i2c_atr_alias_pool *alias_pool)
|
||||
{
|
||||
unsigned long idx;
|
||||
u16 alias;
|
||||
|
||||
spin_lock(&alias_pool->lock);
|
||||
|
||||
idx = find_first_zero_bit(alias_pool->use_mask, alias_pool->size);
|
||||
if (idx >= alias_pool->size) {
|
||||
spin_unlock(&alias_pool->lock);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
set_bit(idx, alias_pool->use_mask);
|
||||
|
||||
alias = alias_pool->aliases[idx];
|
||||
|
||||
spin_unlock(&alias_pool->lock);
|
||||
return alias;
|
||||
}
|
||||
|
||||
static void i2c_atr_release_alias(struct i2c_atr_alias_pool *alias_pool, u16 alias)
|
||||
{
|
||||
unsigned int idx;
|
||||
|
||||
spin_lock(&alias_pool->lock);
|
||||
|
||||
for (idx = 0; idx < alias_pool->size; ++idx) {
|
||||
if (alias_pool->aliases[idx] == alias) {
|
||||
clear_bit(idx, alias_pool->use_mask);
|
||||
spin_unlock(&alias_pool->lock);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock(&alias_pool->lock);
|
||||
}
|
||||
|
||||
static struct i2c_atr_alias_pair *
|
||||
i2c_atr_find_mapping_by_addr(struct i2c_atr_chan *chan, u16 addr)
|
||||
{
|
||||
struct i2c_atr_alias_pair *c2a;
|
||||
|
||||
lockdep_assert_held(&chan->alias_pairs_lock);
|
||||
|
||||
list_for_each_entry(c2a, &chan->alias_pairs, node) {
|
||||
if (c2a->addr == addr)
|
||||
return c2a;
|
||||
}
|
||||
|
||||
|
@ -112,18 +257,107 @@ i2c_atr_find_mapping_by_client(const struct list_head *list,
|
|||
}
|
||||
|
||||
static struct i2c_atr_alias_pair *
|
||||
i2c_atr_find_mapping_by_addr(const struct list_head *list, u16 phys_addr)
|
||||
i2c_atr_create_mapping_by_addr(struct i2c_atr_chan *chan, u16 addr)
|
||||
{
|
||||
struct i2c_atr *atr = chan->atr;
|
||||
struct i2c_atr_alias_pair *c2a;
|
||||
u16 alias;
|
||||
int ret;
|
||||
|
||||
list_for_each_entry(c2a, list, node) {
|
||||
if (c2a->client->addr == phys_addr)
|
||||
return c2a;
|
||||
lockdep_assert_held(&chan->alias_pairs_lock);
|
||||
|
||||
ret = i2c_atr_reserve_alias(chan->alias_pool);
|
||||
if (ret < 0)
|
||||
return NULL;
|
||||
|
||||
alias = ret;
|
||||
|
||||
c2a = i2c_atr_create_c2a(chan, alias, addr);
|
||||
if (!c2a)
|
||||
goto err_release_alias;
|
||||
|
||||
ret = atr->ops->attach_addr(atr, chan->chan_id, c2a->addr, c2a->alias);
|
||||
if (ret) {
|
||||
dev_err(atr->dev, "failed to attach 0x%02x on channel %d: err %d\n",
|
||||
addr, chan->chan_id, ret);
|
||||
goto err_del_c2a;
|
||||
}
|
||||
|
||||
return c2a;
|
||||
|
||||
err_del_c2a:
|
||||
i2c_atr_destroy_c2a(&c2a);
|
||||
err_release_alias:
|
||||
i2c_atr_release_alias(chan->alias_pool, alias);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct i2c_atr_alias_pair *
|
||||
i2c_atr_replace_mapping_by_addr(struct i2c_atr_chan *chan, u16 addr)
|
||||
{
|
||||
struct i2c_atr *atr = chan->atr;
|
||||
struct i2c_atr_alias_pair *c2a;
|
||||
struct list_head *alias_pairs;
|
||||
bool found = false;
|
||||
u16 alias;
|
||||
int ret;
|
||||
|
||||
lockdep_assert_held(&chan->alias_pairs_lock);
|
||||
|
||||
alias_pairs = &chan->alias_pairs;
|
||||
|
||||
if (unlikely(list_empty(alias_pairs)))
|
||||
return NULL;
|
||||
|
||||
list_for_each_entry_reverse(c2a, alias_pairs, node) {
|
||||
if (!c2a->fixed) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return NULL;
|
||||
|
||||
atr->ops->detach_addr(atr, chan->chan_id, c2a->addr);
|
||||
c2a->addr = addr;
|
||||
|
||||
list_move(&c2a->node, alias_pairs);
|
||||
|
||||
alias = c2a->alias;
|
||||
|
||||
ret = atr->ops->attach_addr(atr, chan->chan_id, c2a->addr, c2a->alias);
|
||||
if (ret) {
|
||||
dev_err(atr->dev, "failed to attach 0x%02x on channel %d: err %d\n",
|
||||
addr, chan->chan_id, ret);
|
||||
i2c_atr_destroy_c2a(&c2a);
|
||||
i2c_atr_release_alias(chan->alias_pool, alias);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return c2a;
|
||||
}
|
||||
|
||||
static struct i2c_atr_alias_pair *
|
||||
i2c_atr_get_mapping_by_addr(struct i2c_atr_chan *chan, u16 addr)
|
||||
{
|
||||
struct i2c_atr *atr = chan->atr;
|
||||
struct i2c_atr_alias_pair *c2a;
|
||||
|
||||
c2a = i2c_atr_find_mapping_by_addr(chan, addr);
|
||||
if (c2a)
|
||||
return c2a;
|
||||
|
||||
if (atr->flags & I2C_ATR_F_STATIC)
|
||||
return NULL;
|
||||
|
||||
c2a = i2c_atr_create_mapping_by_addr(chan, addr);
|
||||
if (c2a)
|
||||
return c2a;
|
||||
|
||||
return i2c_atr_replace_mapping_by_addr(chan, addr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Replace all message addresses with their aliases, saving the original
|
||||
* addresses.
|
||||
|
@ -136,7 +370,7 @@ static int i2c_atr_map_msgs(struct i2c_atr_chan *chan, struct i2c_msg *msgs,
|
|||
{
|
||||
struct i2c_atr *atr = chan->atr;
|
||||
static struct i2c_atr_alias_pair *c2a;
|
||||
int i;
|
||||
int i, ret = 0;
|
||||
|
||||
/* Ensure we have enough room to save the original addresses */
|
||||
if (unlikely(chan->orig_addrs_size < num)) {
|
||||
|
@ -152,25 +386,36 @@ static int i2c_atr_map_msgs(struct i2c_atr_chan *chan, struct i2c_msg *msgs,
|
|||
chan->orig_addrs_size = num;
|
||||
}
|
||||
|
||||
mutex_lock(&chan->alias_pairs_lock);
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
chan->orig_addrs[i] = msgs[i].addr;
|
||||
|
||||
c2a = i2c_atr_find_mapping_by_addr(&chan->alias_list,
|
||||
msgs[i].addr);
|
||||
c2a = i2c_atr_get_mapping_by_addr(chan, msgs[i].addr);
|
||||
|
||||
if (!c2a) {
|
||||
if (atr->flags & I2C_ATR_F_PASSTHROUGH)
|
||||
continue;
|
||||
|
||||
dev_err(atr->dev, "client 0x%02x not mapped!\n",
|
||||
msgs[i].addr);
|
||||
|
||||
while (i--)
|
||||
msgs[i].addr = chan->orig_addrs[i];
|
||||
|
||||
return -ENXIO;
|
||||
ret = -ENXIO;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
// Prevent c2a from being overwritten by another client in this transaction
|
||||
c2a->fixed = true;
|
||||
|
||||
msgs[i].addr = c2a->alias;
|
||||
}
|
||||
|
||||
return 0;
|
||||
out_unlock:
|
||||
mutex_unlock(&chan->alias_pairs_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -183,10 +428,24 @@ static int i2c_atr_map_msgs(struct i2c_atr_chan *chan, struct i2c_msg *msgs,
|
|||
static void i2c_atr_unmap_msgs(struct i2c_atr_chan *chan, struct i2c_msg *msgs,
|
||||
int num)
|
||||
{
|
||||
struct i2c_atr_alias_pair *c2a;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num; i++)
|
||||
msgs[i].addr = chan->orig_addrs[i];
|
||||
|
||||
mutex_lock(&chan->alias_pairs_lock);
|
||||
|
||||
if (unlikely(list_empty(&chan->alias_pairs)))
|
||||
goto out_unlock;
|
||||
|
||||
// unfix c2a entries so that subsequent transfers can reuse their aliases
|
||||
list_for_each_entry(c2a, &chan->alias_pairs, node) {
|
||||
c2a->fixed = false;
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&chan->alias_pairs_lock);
|
||||
}
|
||||
|
||||
static int i2c_atr_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||
|
@ -224,14 +483,23 @@ static int i2c_atr_smbus_xfer(struct i2c_adapter *adap, u16 addr,
|
|||
struct i2c_atr *atr = chan->atr;
|
||||
struct i2c_adapter *parent = atr->parent;
|
||||
struct i2c_atr_alias_pair *c2a;
|
||||
u16 alias;
|
||||
|
||||
c2a = i2c_atr_find_mapping_by_addr(&chan->alias_list, addr);
|
||||
if (!c2a) {
|
||||
mutex_lock(&chan->alias_pairs_lock);
|
||||
|
||||
c2a = i2c_atr_get_mapping_by_addr(chan, addr);
|
||||
|
||||
if (!c2a && !(atr->flags & I2C_ATR_F_PASSTHROUGH)) {
|
||||
dev_err(atr->dev, "client 0x%02x not mapped!\n", addr);
|
||||
mutex_unlock(&chan->alias_pairs_lock);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
return i2c_smbus_xfer(parent, c2a->alias, flags, read_write, command,
|
||||
alias = c2a ? c2a->alias : addr;
|
||||
|
||||
mutex_unlock(&chan->alias_pairs_lock);
|
||||
|
||||
return i2c_smbus_xfer(parent, alias, flags, read_write, command,
|
||||
size, data);
|
||||
}
|
||||
|
||||
|
@ -273,112 +541,60 @@ static const struct i2c_lock_operations i2c_atr_lock_ops = {
|
|||
.unlock_bus = i2c_atr_unlock_bus,
|
||||
};
|
||||
|
||||
static int i2c_atr_reserve_alias(struct i2c_atr *atr)
|
||||
{
|
||||
unsigned long idx;
|
||||
|
||||
spin_lock(&atr->alias_mask_lock);
|
||||
|
||||
idx = find_first_zero_bit(atr->alias_use_mask, atr->num_aliases);
|
||||
if (idx >= atr->num_aliases) {
|
||||
spin_unlock(&atr->alias_mask_lock);
|
||||
dev_err(atr->dev, "failed to find a free alias\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
set_bit(idx, atr->alias_use_mask);
|
||||
|
||||
spin_unlock(&atr->alias_mask_lock);
|
||||
|
||||
return atr->aliases[idx];
|
||||
}
|
||||
|
||||
static void i2c_atr_release_alias(struct i2c_atr *atr, u16 alias)
|
||||
{
|
||||
unsigned int idx;
|
||||
|
||||
spin_lock(&atr->alias_mask_lock);
|
||||
|
||||
for (idx = 0; idx < atr->num_aliases; ++idx) {
|
||||
if (atr->aliases[idx] == alias) {
|
||||
clear_bit(idx, atr->alias_use_mask);
|
||||
spin_unlock(&atr->alias_mask_lock);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock(&atr->alias_mask_lock);
|
||||
|
||||
/* This should never happen */
|
||||
dev_warn(atr->dev, "Unable to find mapped alias\n");
|
||||
}
|
||||
|
||||
static int i2c_atr_attach_client(struct i2c_adapter *adapter,
|
||||
const struct i2c_client *client)
|
||||
static int i2c_atr_attach_addr(struct i2c_adapter *adapter,
|
||||
u16 addr)
|
||||
{
|
||||
struct i2c_atr_chan *chan = adapter->algo_data;
|
||||
struct i2c_atr *atr = chan->atr;
|
||||
struct i2c_atr_alias_pair *c2a;
|
||||
u16 alias;
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
ret = i2c_atr_reserve_alias(atr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
mutex_lock(&chan->alias_pairs_lock);
|
||||
|
||||
alias = ret;
|
||||
c2a = i2c_atr_create_mapping_by_addr(chan, addr);
|
||||
if (!c2a && !(atr->flags & I2C_ATR_F_STATIC))
|
||||
c2a = i2c_atr_replace_mapping_by_addr(chan, addr);
|
||||
|
||||
c2a = kzalloc(sizeof(*c2a), GFP_KERNEL);
|
||||
if (!c2a) {
|
||||
ret = -ENOMEM;
|
||||
goto err_release_alias;
|
||||
dev_err(atr->dev, "failed to find a free alias\n");
|
||||
ret = -EBUSY;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
ret = atr->ops->attach_client(atr, chan->chan_id, client, alias);
|
||||
if (ret)
|
||||
goto err_free;
|
||||
|
||||
dev_dbg(atr->dev, "chan%u: client 0x%02x mapped at alias 0x%02x (%s)\n",
|
||||
chan->chan_id, client->addr, alias, client->name);
|
||||
|
||||
c2a->client = client;
|
||||
c2a->alias = alias;
|
||||
list_add(&c2a->node, &chan->alias_list);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free:
|
||||
kfree(c2a);
|
||||
err_release_alias:
|
||||
i2c_atr_release_alias(atr, alias);
|
||||
dev_dbg(atr->dev, "chan%u: using alias 0x%02x for addr 0x%02x\n",
|
||||
chan->chan_id, c2a->alias, addr);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&chan->alias_pairs_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void i2c_atr_detach_client(struct i2c_adapter *adapter,
|
||||
const struct i2c_client *client)
|
||||
static void i2c_atr_detach_addr(struct i2c_adapter *adapter,
|
||||
u16 addr)
|
||||
{
|
||||
struct i2c_atr_chan *chan = adapter->algo_data;
|
||||
struct i2c_atr *atr = chan->atr;
|
||||
struct i2c_atr_alias_pair *c2a;
|
||||
|
||||
atr->ops->detach_client(atr, chan->chan_id, client);
|
||||
atr->ops->detach_addr(atr, chan->chan_id, addr);
|
||||
|
||||
c2a = i2c_atr_find_mapping_by_client(&chan->alias_list, client);
|
||||
mutex_lock(&chan->alias_pairs_lock);
|
||||
|
||||
c2a = i2c_atr_find_mapping_by_addr(chan, addr);
|
||||
if (!c2a) {
|
||||
/* This should never happen */
|
||||
dev_warn(atr->dev, "Unable to find address mapping\n");
|
||||
mutex_unlock(&chan->alias_pairs_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
i2c_atr_release_alias(atr, c2a->alias);
|
||||
i2c_atr_release_alias(chan->alias_pool, c2a->alias);
|
||||
|
||||
dev_dbg(atr->dev,
|
||||
"chan%u: client 0x%02x unmapped from alias 0x%02x (%s)\n",
|
||||
chan->chan_id, client->addr, c2a->alias, client->name);
|
||||
"chan%u: detached alias 0x%02x from addr 0x%02x\n",
|
||||
chan->chan_id, c2a->alias, addr);
|
||||
|
||||
list_del(&c2a->node);
|
||||
kfree(c2a);
|
||||
i2c_atr_destroy_c2a(&c2a);
|
||||
|
||||
mutex_unlock(&chan->alias_pairs_lock);
|
||||
}
|
||||
|
||||
static int i2c_atr_bus_notifier_call(struct notifier_block *nb,
|
||||
|
@ -405,7 +621,7 @@ static int i2c_atr_bus_notifier_call(struct notifier_block *nb,
|
|||
|
||||
switch (event) {
|
||||
case BUS_NOTIFY_ADD_DEVICE:
|
||||
ret = i2c_atr_attach_client(client->adapter, client);
|
||||
ret = i2c_atr_attach_addr(client->adapter, client->addr);
|
||||
if (ret)
|
||||
dev_err(atr->dev,
|
||||
"Failed to attach remote client '%s': %d\n",
|
||||
|
@ -413,7 +629,7 @@ static int i2c_atr_bus_notifier_call(struct notifier_block *nb,
|
|||
break;
|
||||
|
||||
case BUS_NOTIFY_REMOVED_DEVICE:
|
||||
i2c_atr_detach_client(client->adapter, client);
|
||||
i2c_atr_detach_addr(client->adapter, client->addr);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -425,29 +641,43 @@ static int i2c_atr_bus_notifier_call(struct notifier_block *nb,
|
|||
|
||||
static int i2c_atr_parse_alias_pool(struct i2c_atr *atr)
|
||||
{
|
||||
struct i2c_atr_alias_pool *alias_pool;
|
||||
struct device *dev = atr->dev;
|
||||
unsigned long *alias_use_mask;
|
||||
size_t num_aliases;
|
||||
unsigned int i;
|
||||
u32 *aliases32;
|
||||
u16 *aliases16;
|
||||
int ret;
|
||||
|
||||
ret = fwnode_property_count_u32(dev_fwnode(dev), "i2c-alias-pool");
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to count 'i2c-alias-pool' property: %d\n",
|
||||
ret);
|
||||
if (!fwnode_property_present(dev_fwnode(dev), "i2c-alias-pool")) {
|
||||
num_aliases = 0;
|
||||
} else {
|
||||
ret = fwnode_property_count_u32(dev_fwnode(dev), "i2c-alias-pool");
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to count 'i2c-alias-pool' property: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
num_aliases = ret;
|
||||
}
|
||||
|
||||
alias_pool = i2c_atr_alloc_alias_pool(num_aliases, true);
|
||||
if (IS_ERR(alias_pool)) {
|
||||
ret = PTR_ERR(alias_pool);
|
||||
dev_err(dev, "Failed to allocate alias pool, err %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
num_aliases = ret;
|
||||
atr->alias_pool = alias_pool;
|
||||
|
||||
if (!num_aliases)
|
||||
if (!alias_pool->size)
|
||||
return 0;
|
||||
|
||||
aliases32 = kcalloc(num_aliases, sizeof(*aliases32), GFP_KERNEL);
|
||||
if (!aliases32)
|
||||
return -ENOMEM;
|
||||
if (!aliases32) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free_alias_pool;
|
||||
}
|
||||
|
||||
ret = fwnode_property_read_u32_array(dev_fwnode(dev), "i2c-alias-pool",
|
||||
aliases32, num_aliases);
|
||||
|
@ -457,48 +687,33 @@ static int i2c_atr_parse_alias_pool(struct i2c_atr *atr)
|
|||
goto err_free_aliases32;
|
||||
}
|
||||
|
||||
aliases16 = kcalloc(num_aliases, sizeof(*aliases16), GFP_KERNEL);
|
||||
if (!aliases16) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free_aliases32;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_aliases; i++) {
|
||||
if (!(aliases32[i] & 0xffff0000)) {
|
||||
aliases16[i] = aliases32[i];
|
||||
alias_pool->aliases[i] = aliases32[i];
|
||||
continue;
|
||||
}
|
||||
|
||||
dev_err(dev, "Failed to parse 'i2c-alias-pool' property: I2C flags are not supported\n");
|
||||
ret = -EINVAL;
|
||||
goto err_free_aliases16;
|
||||
}
|
||||
|
||||
alias_use_mask = bitmap_zalloc(num_aliases, GFP_KERNEL);
|
||||
if (!alias_use_mask) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free_aliases16;
|
||||
goto err_free_aliases32;
|
||||
}
|
||||
|
||||
kfree(aliases32);
|
||||
|
||||
atr->num_aliases = num_aliases;
|
||||
atr->aliases = aliases16;
|
||||
atr->alias_use_mask = alias_use_mask;
|
||||
|
||||
dev_dbg(dev, "i2c-alias-pool has %zu aliases", atr->num_aliases);
|
||||
dev_dbg(dev, "i2c-alias-pool has %zu aliases\n", alias_pool->size);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_aliases16:
|
||||
kfree(aliases16);
|
||||
err_free_aliases32:
|
||||
kfree(aliases32);
|
||||
err_free_alias_pool:
|
||||
i2c_atr_free_alias_pool(alias_pool);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct i2c_atr *i2c_atr_new(struct i2c_adapter *parent, struct device *dev,
|
||||
const struct i2c_atr_ops *ops, int max_adapters)
|
||||
const struct i2c_atr_ops *ops, int max_adapters,
|
||||
u32 flags)
|
||||
{
|
||||
struct i2c_atr *atr;
|
||||
int ret;
|
||||
|
@ -506,20 +721,21 @@ struct i2c_atr *i2c_atr_new(struct i2c_adapter *parent, struct device *dev,
|
|||
if (max_adapters > ATR_MAX_ADAPTERS)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (!ops || !ops->attach_client || !ops->detach_client)
|
||||
if (!ops || !ops->attach_addr || !ops->detach_addr)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
atr = kzalloc(struct_size(atr, adapter, max_adapters), GFP_KERNEL);
|
||||
if (!atr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
mutex_init(&atr->lock);
|
||||
spin_lock_init(&atr->alias_mask_lock);
|
||||
lockdep_register_key(&atr->lock_key);
|
||||
mutex_init_with_key(&atr->lock, &atr->lock_key);
|
||||
|
||||
atr->parent = parent;
|
||||
atr->dev = dev;
|
||||
atr->ops = ops;
|
||||
atr->max_adapters = max_adapters;
|
||||
atr->flags = flags;
|
||||
|
||||
if (parent->algo->master_xfer)
|
||||
atr->algo.master_xfer = i2c_atr_master_xfer;
|
||||
|
@ -534,15 +750,15 @@ struct i2c_atr *i2c_atr_new(struct i2c_adapter *parent, struct device *dev,
|
|||
atr->i2c_nb.notifier_call = i2c_atr_bus_notifier_call;
|
||||
ret = bus_register_notifier(&i2c_bus_type, &atr->i2c_nb);
|
||||
if (ret)
|
||||
goto err_free_aliases;
|
||||
goto err_free_alias_pool;
|
||||
|
||||
return atr;
|
||||
|
||||
err_free_aliases:
|
||||
bitmap_free(atr->alias_use_mask);
|
||||
kfree(atr->aliases);
|
||||
err_free_alias_pool:
|
||||
i2c_atr_free_alias_pool(atr->alias_pool);
|
||||
err_destroy_mutex:
|
||||
mutex_destroy(&atr->lock);
|
||||
lockdep_unregister_key(&atr->lock_key);
|
||||
kfree(atr);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
|
@ -557,22 +773,22 @@ void i2c_atr_delete(struct i2c_atr *atr)
|
|||
WARN_ON(atr->adapter[i]);
|
||||
|
||||
bus_unregister_notifier(&i2c_bus_type, &atr->i2c_nb);
|
||||
bitmap_free(atr->alias_use_mask);
|
||||
kfree(atr->aliases);
|
||||
i2c_atr_free_alias_pool(atr->alias_pool);
|
||||
mutex_destroy(&atr->lock);
|
||||
lockdep_unregister_key(&atr->lock_key);
|
||||
kfree(atr);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(i2c_atr_delete, "I2C_ATR");
|
||||
|
||||
int i2c_atr_add_adapter(struct i2c_atr *atr, u32 chan_id,
|
||||
struct device *adapter_parent,
|
||||
struct fwnode_handle *bus_handle)
|
||||
int i2c_atr_add_adapter(struct i2c_atr *atr, struct i2c_atr_adap_desc *desc)
|
||||
{
|
||||
struct fwnode_handle *bus_handle = desc->bus_handle;
|
||||
struct i2c_adapter *parent = atr->parent;
|
||||
struct device *dev = atr->dev;
|
||||
struct i2c_atr_chan *chan;
|
||||
char symlink_name[ATR_MAX_SYMLINK_LEN];
|
||||
int ret;
|
||||
struct device *dev = atr->dev;
|
||||
u32 chan_id = desc->chan_id;
|
||||
struct i2c_atr_chan *chan;
|
||||
int ret, idx;
|
||||
|
||||
if (chan_id >= atr->max_adapters) {
|
||||
dev_err(dev, "No room for more i2c-atr adapters\n");
|
||||
|
@ -588,20 +804,23 @@ int i2c_atr_add_adapter(struct i2c_atr *atr, u32 chan_id,
|
|||
if (!chan)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!adapter_parent)
|
||||
adapter_parent = dev;
|
||||
if (!desc->parent)
|
||||
desc->parent = dev;
|
||||
|
||||
chan->atr = atr;
|
||||
chan->chan_id = chan_id;
|
||||
INIT_LIST_HEAD(&chan->alias_list);
|
||||
mutex_init(&chan->orig_addrs_lock);
|
||||
INIT_LIST_HEAD(&chan->alias_pairs);
|
||||
lockdep_register_key(&chan->alias_pairs_lock_key);
|
||||
lockdep_register_key(&chan->orig_addrs_lock_key);
|
||||
mutex_init_with_key(&chan->alias_pairs_lock, &chan->alias_pairs_lock_key);
|
||||
mutex_init_with_key(&chan->orig_addrs_lock, &chan->orig_addrs_lock_key);
|
||||
|
||||
snprintf(chan->adap.name, sizeof(chan->adap.name), "i2c-%d-atr-%d",
|
||||
i2c_adapter_id(parent), chan_id);
|
||||
chan->adap.owner = THIS_MODULE;
|
||||
chan->adap.algo = &atr->algo;
|
||||
chan->adap.algo_data = chan;
|
||||
chan->adap.dev.parent = adapter_parent;
|
||||
chan->adap.dev.parent = desc->parent;
|
||||
chan->adap.retries = parent->retries;
|
||||
chan->adap.timeout = parent->timeout;
|
||||
chan->adap.quirks = parent->quirks;
|
||||
|
@ -628,13 +847,26 @@ int i2c_atr_add_adapter(struct i2c_atr *atr, u32 chan_id,
|
|||
fwnode_handle_put(atr_node);
|
||||
}
|
||||
|
||||
if (desc->num_aliases > 0) {
|
||||
chan->alias_pool = i2c_atr_alloc_alias_pool(desc->num_aliases, false);
|
||||
if (IS_ERR(chan->alias_pool)) {
|
||||
ret = PTR_ERR(chan->alias_pool);
|
||||
goto err_fwnode_put;
|
||||
}
|
||||
|
||||
for (idx = 0; idx < desc->num_aliases; idx++)
|
||||
chan->alias_pool->aliases[idx] = desc->aliases[idx];
|
||||
} else {
|
||||
chan->alias_pool = atr->alias_pool;
|
||||
}
|
||||
|
||||
atr->adapter[chan_id] = &chan->adap;
|
||||
|
||||
ret = i2c_add_adapter(&chan->adap);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to add atr-adapter %u (error=%d)\n",
|
||||
chan_id, ret);
|
||||
goto err_fwnode_put;
|
||||
goto err_free_alias_pool;
|
||||
}
|
||||
|
||||
snprintf(symlink_name, sizeof(symlink_name), "channel-%u",
|
||||
|
@ -651,9 +883,15 @@ int i2c_atr_add_adapter(struct i2c_atr *atr, u32 chan_id,
|
|||
|
||||
return 0;
|
||||
|
||||
err_free_alias_pool:
|
||||
if (!chan->alias_pool->shared)
|
||||
i2c_atr_free_alias_pool(chan->alias_pool);
|
||||
err_fwnode_put:
|
||||
fwnode_handle_put(dev_fwnode(&chan->adap.dev));
|
||||
mutex_destroy(&chan->orig_addrs_lock);
|
||||
mutex_destroy(&chan->alias_pairs_lock);
|
||||
lockdep_unregister_key(&chan->orig_addrs_lock_key);
|
||||
lockdep_unregister_key(&chan->alias_pairs_lock_key);
|
||||
kfree(chan);
|
||||
return ret;
|
||||
}
|
||||
|
@ -683,10 +921,16 @@ void i2c_atr_del_adapter(struct i2c_atr *atr, u32 chan_id)
|
|||
|
||||
i2c_del_adapter(adap);
|
||||
|
||||
if (!chan->alias_pool->shared)
|
||||
i2c_atr_free_alias_pool(chan->alias_pool);
|
||||
|
||||
atr->adapter[chan_id] = NULL;
|
||||
|
||||
fwnode_handle_put(fwnode);
|
||||
mutex_destroy(&chan->orig_addrs_lock);
|
||||
mutex_destroy(&chan->alias_pairs_lock);
|
||||
lockdep_unregister_key(&chan->orig_addrs_lock_key);
|
||||
lockdep_unregister_key(&chan->alias_pairs_lock_key);
|
||||
kfree(chan->orig_addrs);
|
||||
kfree(chan);
|
||||
}
|
||||
|
|
|
@ -26,14 +26,13 @@
|
|||
#include <linux/idr.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqflags.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/jump_label.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/pinctrl/devinfo.h>
|
||||
#include <linux/pm_domain.h>
|
||||
|
@ -42,6 +41,7 @@
|
|||
#include <linux/property.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string_choices.h>
|
||||
|
||||
#include "i2c-core.h"
|
||||
|
||||
|
@ -490,6 +490,7 @@ static int i2c_smbus_host_notify_to_irq(const struct i2c_client *client)
|
|||
|
||||
static int i2c_device_probe(struct device *dev)
|
||||
{
|
||||
struct fwnode_handle *fwnode = dev_fwnode(dev);
|
||||
struct i2c_client *client = i2c_verify_client(dev);
|
||||
struct i2c_driver *driver;
|
||||
bool do_power_on;
|
||||
|
@ -508,11 +509,11 @@ static int i2c_device_probe(struct device *dev)
|
|||
/* Keep adapter active when Host Notify is required */
|
||||
pm_runtime_get_sync(&client->adapter->dev);
|
||||
irq = i2c_smbus_host_notify_to_irq(client);
|
||||
} else if (dev->of_node) {
|
||||
irq = of_irq_get_byname(dev->of_node, "irq");
|
||||
} else if (is_of_node(fwnode)) {
|
||||
irq = fwnode_irq_get_byname(fwnode, "irq");
|
||||
if (irq == -EINVAL || irq == -ENODATA)
|
||||
irq = of_irq_get(dev->of_node, 0);
|
||||
} else if (ACPI_COMPANION(dev)) {
|
||||
irq = fwnode_irq_get(fwnode, 0);
|
||||
} else if (is_acpi_device_node(fwnode)) {
|
||||
bool wake_capable;
|
||||
|
||||
irq = i2c_acpi_get_irq(client, &wake_capable);
|
||||
|
@ -520,7 +521,7 @@ static int i2c_device_probe(struct device *dev)
|
|||
client->flags |= I2C_CLIENT_WAKE;
|
||||
}
|
||||
if (irq == -EPROBE_DEFER) {
|
||||
status = irq;
|
||||
status = dev_err_probe(dev, irq, "can't get irq\n");
|
||||
goto put_sync_adapter;
|
||||
}
|
||||
|
||||
|
@ -546,9 +547,9 @@ static int i2c_device_probe(struct device *dev)
|
|||
if (client->flags & I2C_CLIENT_WAKE) {
|
||||
int wakeirq;
|
||||
|
||||
wakeirq = of_irq_get_byname(dev->of_node, "wakeup");
|
||||
wakeirq = fwnode_irq_get_byname(fwnode, "wakeup");
|
||||
if (wakeirq == -EPROBE_DEFER) {
|
||||
status = wakeirq;
|
||||
status = dev_err_probe(dev, wakeirq, "can't get wakeirq\n");
|
||||
goto put_sync_adapter;
|
||||
}
|
||||
|
||||
|
@ -567,7 +568,7 @@ static int i2c_device_probe(struct device *dev)
|
|||
|
||||
dev_dbg(dev, "probe\n");
|
||||
|
||||
status = of_clk_set_defaults(dev->of_node, false);
|
||||
status = of_clk_set_defaults(to_of_node(fwnode), false);
|
||||
if (status < 0)
|
||||
goto err_clear_wakeup_irq;
|
||||
|
||||
|
@ -961,6 +962,7 @@ static void i2c_unlock_addr(struct i2c_adapter *adap, unsigned short addr,
|
|||
struct i2c_client *
|
||||
i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
|
||||
{
|
||||
struct fwnode_handle *fwnode = info->fwnode;
|
||||
struct i2c_client *client;
|
||||
bool need_put = false;
|
||||
int status;
|
||||
|
@ -1001,18 +1003,18 @@ i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *inf
|
|||
client->dev.parent = &client->adapter->dev;
|
||||
client->dev.bus = &i2c_bus_type;
|
||||
client->dev.type = &i2c_client_type;
|
||||
client->dev.of_node = of_node_get(info->of_node);
|
||||
client->dev.fwnode = info->fwnode;
|
||||
|
||||
device_enable_async_suspend(&client->dev);
|
||||
|
||||
device_set_node(&client->dev, fwnode_handle_get(fwnode));
|
||||
|
||||
if (info->swnode) {
|
||||
status = device_add_software_node(&client->dev, info->swnode);
|
||||
if (status) {
|
||||
dev_err(&adap->dev,
|
||||
"Failed to add software node to client %s: %d\n",
|
||||
client->name, status);
|
||||
goto out_err_put_of_node;
|
||||
goto out_err_put_fwnode;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1031,8 +1033,8 @@ i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *inf
|
|||
out_remove_swnode:
|
||||
device_remove_software_node(&client->dev);
|
||||
need_put = true;
|
||||
out_err_put_of_node:
|
||||
of_node_put(info->of_node);
|
||||
out_err_put_fwnode:
|
||||
fwnode_handle_put(fwnode);
|
||||
out_err:
|
||||
dev_err(&adap->dev,
|
||||
"Failed to register i2c client %s at 0x%02x (%d)\n",
|
||||
|
@ -1054,16 +1056,17 @@ EXPORT_SYMBOL_GPL(i2c_new_client_device);
|
|||
*/
|
||||
void i2c_unregister_device(struct i2c_client *client)
|
||||
{
|
||||
struct fwnode_handle *fwnode;
|
||||
|
||||
if (IS_ERR_OR_NULL(client))
|
||||
return;
|
||||
|
||||
if (client->dev.of_node) {
|
||||
of_node_clear_flag(client->dev.of_node, OF_POPULATED);
|
||||
of_node_put(client->dev.of_node);
|
||||
}
|
||||
|
||||
if (ACPI_COMPANION(&client->dev))
|
||||
acpi_device_clear_enumerated(ACPI_COMPANION(&client->dev));
|
||||
fwnode = dev_fwnode(&client->dev);
|
||||
if (is_of_node(fwnode))
|
||||
of_node_clear_flag(to_of_node(fwnode), OF_POPULATED);
|
||||
else if (is_acpi_device_node(fwnode))
|
||||
acpi_device_clear_enumerated(to_acpi_device_node(fwnode));
|
||||
fwnode_handle_put(fwnode);
|
||||
|
||||
device_remove_software_node(&client->dev);
|
||||
device_unregister(&client->dev);
|
||||
|
@ -1209,11 +1212,9 @@ struct i2c_client *i2c_new_ancillary_device(struct i2c_client *client,
|
|||
u32 addr = default_addr;
|
||||
int i;
|
||||
|
||||
if (np) {
|
||||
i = of_property_match_string(np, "reg-names", name);
|
||||
if (i >= 0)
|
||||
of_property_read_u32_index(np, "reg", i, &addr);
|
||||
}
|
||||
i = of_property_match_string(np, "reg-names", name);
|
||||
if (i >= 0)
|
||||
of_property_read_u32_index(np, "reg", i, &addr);
|
||||
|
||||
dev_dbg(&client->adapter->dev, "Address for %s : 0x%x\n", name, addr);
|
||||
return i2c_new_dummy_device(client->adapter, addr);
|
||||
|
@ -1651,12 +1652,10 @@ int i2c_add_adapter(struct i2c_adapter *adapter)
|
|||
struct device *dev = &adapter->dev;
|
||||
int id;
|
||||
|
||||
if (dev->of_node) {
|
||||
id = of_alias_get_id(dev->of_node, "i2c");
|
||||
if (id >= 0) {
|
||||
adapter->nr = id;
|
||||
return __i2c_add_numbered_adapter(adapter);
|
||||
}
|
||||
id = of_alias_get_id(dev->of_node, "i2c");
|
||||
if (id >= 0) {
|
||||
adapter->nr = id;
|
||||
return __i2c_add_numbered_adapter(adapter);
|
||||
}
|
||||
|
||||
mutex_lock(&core_lock);
|
||||
|
@ -2146,7 +2145,7 @@ static int i2c_quirk_error(struct i2c_adapter *adap, struct i2c_msg *msg, char *
|
|||
{
|
||||
dev_err_ratelimited(&adap->dev, "adapter quirk: %s (addr 0x%04x, size %u, %s)\n",
|
||||
err_msg, msg->addr, msg->len,
|
||||
msg->flags & I2C_M_RD ? "read" : "write");
|
||||
str_read_write(msg->flags & I2C_M_RD));
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
|
|
|
@ -49,7 +49,6 @@ int of_i2c_get_board_info(struct device *dev, struct device_node *node,
|
|||
}
|
||||
|
||||
info->addr = addr;
|
||||
info->of_node = node;
|
||||
info->fwnode = of_fwnode_handle(node);
|
||||
|
||||
if (of_property_read_bool(node, "host-notify"))
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/property.h>
|
||||
|
||||
#include "i2c-core.h"
|
||||
|
||||
|
@ -108,15 +109,18 @@ EXPORT_SYMBOL_GPL(i2c_slave_event);
|
|||
*/
|
||||
bool i2c_detect_slave_mode(struct device *dev)
|
||||
{
|
||||
if (IS_BUILTIN(CONFIG_OF) && dev->of_node) {
|
||||
struct fwnode_handle *fwnode = dev_fwnode(dev);
|
||||
|
||||
if (is_of_node(fwnode)) {
|
||||
struct fwnode_handle *child __free(fwnode_handle) = NULL;
|
||||
u32 reg;
|
||||
|
||||
for_each_child_of_node_scoped(dev->of_node, child) {
|
||||
of_property_read_u32(child, "reg", ®);
|
||||
fwnode_for_each_child_node(fwnode, child) {
|
||||
fwnode_property_read_u32(child, "reg", ®);
|
||||
if (reg & I2C_OWN_SLAVE_ADDRESS)
|
||||
return true;
|
||||
}
|
||||
} else if (IS_BUILTIN(CONFIG_ACPI) && ACPI_HANDLE(dev)) {
|
||||
} else if (is_acpi_device_node(fwnode)) {
|
||||
dev_dbg(dev, "ACPI slave is not supported yet\n");
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/i2c-smbus.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string_choices.h>
|
||||
|
||||
#include "i2c-core.h"
|
||||
|
||||
|
@ -433,7 +434,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
|
|||
case I2C_SMBUS_I2C_BLOCK_DATA:
|
||||
if (data->block[0] > I2C_SMBUS_BLOCK_MAX) {
|
||||
dev_err(&adapter->dev, "Invalid block %s size %d\n",
|
||||
read_write == I2C_SMBUS_READ ? "read" : "write",
|
||||
str_read_write(read_write == I2C_SMBUS_READ),
|
||||
data->block[0]);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
|
@ -372,12 +372,13 @@ EXPORT_SYMBOL_GPL(i2c_free_slave_host_notify_device);
|
|||
* - Only works on systems with 1 to 8 memory slots
|
||||
*/
|
||||
#if IS_ENABLED(CONFIG_DMI)
|
||||
void i2c_register_spd(struct i2c_adapter *adap)
|
||||
static void i2c_register_spd(struct i2c_adapter *adap, bool write_disabled)
|
||||
{
|
||||
int n, slot_count = 0, dimm_count = 0;
|
||||
u16 handle;
|
||||
u8 common_mem_type = 0x0, mem_type;
|
||||
u64 mem_size;
|
||||
bool instantiate = true;
|
||||
const char *name;
|
||||
|
||||
while ((handle = dmi_memdev_handle(slot_count)) != 0xffff) {
|
||||
|
@ -438,6 +439,7 @@ void i2c_register_spd(struct i2c_adapter *adap)
|
|||
case 0x22: /* DDR5 */
|
||||
case 0x23: /* LPDDR5 */
|
||||
name = "spd5118";
|
||||
instantiate = !write_disabled;
|
||||
break;
|
||||
default:
|
||||
dev_info(&adap->dev,
|
||||
|
@ -461,6 +463,9 @@ void i2c_register_spd(struct i2c_adapter *adap)
|
|||
addr_list[0] = 0x50 + n;
|
||||
addr_list[1] = I2C_CLIENT_END;
|
||||
|
||||
if (!instantiate)
|
||||
continue;
|
||||
|
||||
if (!IS_ERR(i2c_new_scanned_device(adap, &info, addr_list, NULL))) {
|
||||
dev_info(&adap->dev,
|
||||
"Successfully instantiated SPD at 0x%hx\n",
|
||||
|
@ -469,7 +474,19 @@ void i2c_register_spd(struct i2c_adapter *adap)
|
|||
}
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_register_spd);
|
||||
|
||||
void i2c_register_spd_write_disable(struct i2c_adapter *adap)
|
||||
{
|
||||
i2c_register_spd(adap, true);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_register_spd_write_disable);
|
||||
|
||||
void i2c_register_spd_write_enable(struct i2c_adapter *adap)
|
||||
{
|
||||
i2c_register_spd(adap, false);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_register_spd_write_enable);
|
||||
|
||||
#endif
|
||||
|
||||
MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
|
||||
|
|
|
@ -85,13 +85,13 @@ static int ltc4306_gpio_get(struct gpio_chip *chip, unsigned int offset)
|
|||
return !!(val & BIT(1 - offset));
|
||||
}
|
||||
|
||||
static void ltc4306_gpio_set(struct gpio_chip *chip, unsigned int offset,
|
||||
int value)
|
||||
static int ltc4306_gpio_set(struct gpio_chip *chip, unsigned int offset,
|
||||
int value)
|
||||
{
|
||||
struct ltc4306 *data = gpiochip_get_data(chip);
|
||||
|
||||
regmap_update_bits(data->regmap, LTC_REG_CONFIG, BIT(5 - offset),
|
||||
value ? BIT(5 - offset) : 0);
|
||||
return regmap_update_bits(data->regmap, LTC_REG_CONFIG,
|
||||
BIT(5 - offset), value ? BIT(5 - offset) : 0);
|
||||
}
|
||||
|
||||
static int ltc4306_gpio_get_direction(struct gpio_chip *chip,
|
||||
|
@ -164,7 +164,7 @@ static int ltc4306_gpio_init(struct ltc4306 *data)
|
|||
data->gpiochip.direction_input = ltc4306_gpio_direction_input;
|
||||
data->gpiochip.direction_output = ltc4306_gpio_direction_output;
|
||||
data->gpiochip.get = ltc4306_gpio_get;
|
||||
data->gpiochip.set = ltc4306_gpio_set;
|
||||
data->gpiochip.set_rv = ltc4306_gpio_set;
|
||||
data->gpiochip.set_config = ltc4306_gpio_set_config;
|
||||
data->gpiochip.owner = THIS_MODULE;
|
||||
|
||||
|
|
|
@ -707,6 +707,7 @@ static int ub913_i2c_master_init(struct ub913_data *priv)
|
|||
static int ub913_add_i2c_adapter(struct ub913_data *priv)
|
||||
{
|
||||
struct device *dev = &priv->client->dev;
|
||||
struct i2c_atr_adap_desc desc = { };
|
||||
struct fwnode_handle *i2c_handle;
|
||||
int ret;
|
||||
|
||||
|
@ -714,8 +715,12 @@ static int ub913_add_i2c_adapter(struct ub913_data *priv)
|
|||
if (!i2c_handle)
|
||||
return 0;
|
||||
|
||||
ret = i2c_atr_add_adapter(priv->plat_data->atr, priv->plat_data->port,
|
||||
dev, i2c_handle);
|
||||
desc.chan_id = priv->plat_data->port;
|
||||
desc.parent = dev;
|
||||
desc.bus_handle = i2c_handle;
|
||||
desc.num_aliases = 0;
|
||||
|
||||
ret = i2c_atr_add_adapter(priv->plat_data->atr, &desc);
|
||||
|
||||
fwnode_handle_put(i2c_handle);
|
||||
|
||||
|
|
|
@ -1102,6 +1102,7 @@ static int ub953_register_clkout(struct ub953_data *priv)
|
|||
static int ub953_add_i2c_adapter(struct ub953_data *priv)
|
||||
{
|
||||
struct device *dev = &priv->client->dev;
|
||||
struct i2c_atr_adap_desc desc = { };
|
||||
struct fwnode_handle *i2c_handle;
|
||||
int ret;
|
||||
|
||||
|
@ -1109,8 +1110,12 @@ static int ub953_add_i2c_adapter(struct ub953_data *priv)
|
|||
if (!i2c_handle)
|
||||
return 0;
|
||||
|
||||
ret = i2c_atr_add_adapter(priv->plat_data->atr, priv->plat_data->port,
|
||||
dev, i2c_handle);
|
||||
desc.chan_id = priv->plat_data->port;
|
||||
desc.parent = dev;
|
||||
desc.bus_handle = i2c_handle;
|
||||
desc.num_aliases = 0;
|
||||
|
||||
ret = i2c_atr_add_adapter(priv->plat_data->atr, &desc);
|
||||
|
||||
fwnode_handle_put(i2c_handle);
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/fwnode.h>
|
||||
|
@ -521,7 +522,9 @@ struct ub960_rxport {
|
|||
};
|
||||
} eq;
|
||||
|
||||
const struct i2c_client *aliased_clients[UB960_MAX_PORT_ALIASES];
|
||||
/* lock for aliased_addrs and associated registers */
|
||||
struct mutex aliased_addrs_lock;
|
||||
u16 aliased_addrs[UB960_MAX_PORT_ALIASES];
|
||||
};
|
||||
|
||||
struct ub960_asd {
|
||||
|
@ -1264,8 +1267,8 @@ static int ub960_reset(struct ub960_data *priv, bool reset_regs)
|
|||
* I2C-ATR (address translator)
|
||||
*/
|
||||
|
||||
static int ub960_atr_attach_client(struct i2c_atr *atr, u32 chan_id,
|
||||
const struct i2c_client *client, u16 alias)
|
||||
static int ub960_atr_attach_addr(struct i2c_atr *atr, u32 chan_id,
|
||||
u16 addr, u16 alias)
|
||||
{
|
||||
struct ub960_data *priv = i2c_atr_get_driver_data(atr);
|
||||
struct ub960_rxport *rxport = priv->rxports[chan_id];
|
||||
|
@ -1273,20 +1276,22 @@ static int ub960_atr_attach_client(struct i2c_atr *atr, u32 chan_id,
|
|||
unsigned int reg_idx;
|
||||
int ret = 0;
|
||||
|
||||
for (reg_idx = 0; reg_idx < ARRAY_SIZE(rxport->aliased_clients); reg_idx++) {
|
||||
if (!rxport->aliased_clients[reg_idx])
|
||||
guard(mutex)(&rxport->aliased_addrs_lock);
|
||||
|
||||
for (reg_idx = 0; reg_idx < ARRAY_SIZE(rxport->aliased_addrs); reg_idx++) {
|
||||
if (!rxport->aliased_addrs[reg_idx])
|
||||
break;
|
||||
}
|
||||
|
||||
if (reg_idx == ARRAY_SIZE(rxport->aliased_clients)) {
|
||||
if (reg_idx == ARRAY_SIZE(rxport->aliased_addrs)) {
|
||||
dev_err(dev, "rx%u: alias pool exhausted\n", rxport->nport);
|
||||
return -EADDRNOTAVAIL;
|
||||
}
|
||||
|
||||
rxport->aliased_clients[reg_idx] = client;
|
||||
rxport->aliased_addrs[reg_idx] = addr;
|
||||
|
||||
ub960_rxport_write(priv, chan_id, UB960_RR_SLAVE_ID(reg_idx),
|
||||
client->addr << 1, &ret);
|
||||
addr << 1, &ret);
|
||||
ub960_rxport_write(priv, chan_id, UB960_RR_SLAVE_ALIAS(reg_idx),
|
||||
alias << 1, &ret);
|
||||
|
||||
|
@ -1294,13 +1299,13 @@ static int ub960_atr_attach_client(struct i2c_atr *atr, u32 chan_id,
|
|||
return ret;
|
||||
|
||||
dev_dbg(dev, "rx%u: client 0x%02x assigned alias 0x%02x at slot %u\n",
|
||||
rxport->nport, client->addr, alias, reg_idx);
|
||||
rxport->nport, addr, alias, reg_idx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ub960_atr_detach_client(struct i2c_atr *atr, u32 chan_id,
|
||||
const struct i2c_client *client)
|
||||
static void ub960_atr_detach_addr(struct i2c_atr *atr, u32 chan_id,
|
||||
u16 addr)
|
||||
{
|
||||
struct ub960_data *priv = i2c_atr_get_driver_data(atr);
|
||||
struct ub960_rxport *rxport = priv->rxports[chan_id];
|
||||
|
@ -1308,34 +1313,36 @@ static void ub960_atr_detach_client(struct i2c_atr *atr, u32 chan_id,
|
|||
unsigned int reg_idx;
|
||||
int ret;
|
||||
|
||||
for (reg_idx = 0; reg_idx < ARRAY_SIZE(rxport->aliased_clients); reg_idx++) {
|
||||
if (rxport->aliased_clients[reg_idx] == client)
|
||||
guard(mutex)(&rxport->aliased_addrs_lock);
|
||||
|
||||
for (reg_idx = 0; reg_idx < ARRAY_SIZE(rxport->aliased_addrs); reg_idx++) {
|
||||
if (rxport->aliased_addrs[reg_idx] == addr)
|
||||
break;
|
||||
}
|
||||
|
||||
if (reg_idx == ARRAY_SIZE(rxport->aliased_clients)) {
|
||||
if (reg_idx == ARRAY_SIZE(rxport->aliased_addrs)) {
|
||||
dev_err(dev, "rx%u: client 0x%02x is not mapped!\n",
|
||||
rxport->nport, client->addr);
|
||||
rxport->nport, addr);
|
||||
return;
|
||||
}
|
||||
|
||||
rxport->aliased_clients[reg_idx] = NULL;
|
||||
rxport->aliased_addrs[reg_idx] = 0;
|
||||
|
||||
ret = ub960_rxport_write(priv, chan_id, UB960_RR_SLAVE_ALIAS(reg_idx),
|
||||
0, NULL);
|
||||
if (ret) {
|
||||
dev_err(dev, "rx%u: unable to fully unmap client 0x%02x: %d\n",
|
||||
rxport->nport, client->addr, ret);
|
||||
rxport->nport, addr, ret);
|
||||
return;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "rx%u: client 0x%02x released at slot %u\n", rxport->nport,
|
||||
client->addr, reg_idx);
|
||||
addr, reg_idx);
|
||||
}
|
||||
|
||||
static const struct i2c_atr_ops ub960_atr_ops = {
|
||||
.attach_client = ub960_atr_attach_client,
|
||||
.detach_client = ub960_atr_detach_client,
|
||||
.attach_addr = ub960_atr_attach_addr,
|
||||
.detach_addr = ub960_atr_detach_addr,
|
||||
};
|
||||
|
||||
static int ub960_init_atr(struct ub960_data *priv)
|
||||
|
@ -1344,7 +1351,7 @@ static int ub960_init_atr(struct ub960_data *priv)
|
|||
struct i2c_adapter *parent_adap = priv->client->adapter;
|
||||
|
||||
priv->atr = i2c_atr_new(parent_adap, dev, &ub960_atr_ops,
|
||||
priv->hw_data->num_rxports);
|
||||
priv->hw_data->num_rxports, 0);
|
||||
if (IS_ERR(priv->atr))
|
||||
return PTR_ERR(priv->atr);
|
||||
|
||||
|
@ -2173,7 +2180,6 @@ static int ub960_rxport_add_serializer(struct ub960_data *priv, u8 nport)
|
|||
struct device *dev = &priv->client->dev;
|
||||
struct ds90ub9xx_platform_data *ser_pdata = &rxport->ser.pdata;
|
||||
struct i2c_board_info ser_info = {
|
||||
.of_node = to_of_node(rxport->ser.fwnode),
|
||||
.fwnode = rxport->ser.fwnode,
|
||||
.platform_data = ser_pdata,
|
||||
};
|
||||
|
@ -4374,6 +4380,8 @@ static void ub960_rxport_free_ports(struct ub960_data *priv)
|
|||
fwnode_handle_put(it.rxport->source.ep_fwnode);
|
||||
fwnode_handle_put(it.rxport->ser.fwnode);
|
||||
|
||||
mutex_destroy(&it.rxport->aliased_addrs_lock);
|
||||
|
||||
kfree(it.rxport);
|
||||
priv->rxports[it.nport] = NULL;
|
||||
}
|
||||
|
@ -4602,6 +4610,8 @@ static int ub960_parse_dt_rxport(struct ub960_data *priv, unsigned int nport,
|
|||
if (ret)
|
||||
goto err_put_remote_fwnode;
|
||||
|
||||
mutex_init(&rxport->aliased_addrs_lock);
|
||||
|
||||
return 0;
|
||||
|
||||
err_put_remote_fwnode:
|
||||
|
|
|
@ -114,6 +114,18 @@ config RPMB
|
|||
|
||||
If unsure, select N.
|
||||
|
||||
config TI_FPC202
|
||||
tristate "TI FPC202 Dual Port Controller"
|
||||
depends on I2C
|
||||
select GPIOLIB
|
||||
select I2C_ATR
|
||||
help
|
||||
If you say yes here you get support for the Texas Instruments FPC202
|
||||
Dual Port Controller.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called fpc202.
|
||||
|
||||
config TIFM_CORE
|
||||
tristate "TI Flash Media interface support"
|
||||
depends on PCI
|
||||
|
|
|
@ -12,6 +12,7 @@ obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o
|
|||
obj-$(CONFIG_DUMMY_IRQ) += dummy-irq.o
|
||||
obj-$(CONFIG_ICS932S401) += ics932s401.o
|
||||
obj-$(CONFIG_LKDTM) += lkdtm/
|
||||
obj-$(CONFIG_TI_FPC202) += ti_fpc202.o
|
||||
obj-$(CONFIG_TIFM_CORE) += tifm_core.o
|
||||
obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o
|
||||
obj-$(CONFIG_PHANTOM) += phantom.o
|
||||
|
|
438
drivers/misc/ti_fpc202.c
Normal file
438
drivers/misc/ti_fpc202.c
Normal file
|
@ -0,0 +1,438 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* ti_fpc202.c - FPC202 Dual Port Controller driver
|
||||
*
|
||||
* Copyright (C) 2024 Bootlin
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-atr.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#define FPC202_NUM_PORTS 2
|
||||
#define FPC202_ALIASES_PER_PORT 2
|
||||
|
||||
/*
|
||||
* GPIO: port mapping
|
||||
*
|
||||
* 0: P0_S0_IN_A
|
||||
* 1: P0_S1_IN_A
|
||||
* 2: P1_S0_IN_A
|
||||
* 3: P1_S1_IN_A
|
||||
* 4: P0_S0_IN_B
|
||||
* ...
|
||||
* 8: P0_S0_IN_C
|
||||
* ...
|
||||
* 12: P0_S0_OUT_A
|
||||
* ...
|
||||
* 16: P0_S0_OUT_B
|
||||
* ...
|
||||
* 19: P1_S1_OUT_B
|
||||
*
|
||||
*/
|
||||
|
||||
#define FPC202_GPIO_COUNT 20
|
||||
#define FPC202_GPIO_P0_S0_IN_B 4
|
||||
#define FPC202_GPIO_P0_S0_OUT_A 12
|
||||
|
||||
#define FPC202_REG_IN_A_INT 0x6
|
||||
#define FPC202_REG_IN_C_IN_B 0x7
|
||||
#define FPC202_REG_OUT_A_OUT_B 0x8
|
||||
|
||||
#define FPC202_REG_OUT_A_OUT_B_VAL 0xa
|
||||
|
||||
#define FPC202_REG_MOD_DEV(port, dev) (0xb4 + ((port) * 4) + (dev))
|
||||
#define FPC202_REG_AUX_DEV(port, dev) (0xb6 + ((port) * 4) + (dev))
|
||||
|
||||
/*
|
||||
* The FPC202 doesn't support turning off address translation on a single port.
|
||||
* So just set an invalid I2C address as the translation target when no client
|
||||
* address is attached.
|
||||
*/
|
||||
#define FPC202_REG_DEV_INVALID 0
|
||||
|
||||
/* Even aliases are assigned to device 0 and odd aliases to device 1 */
|
||||
#define fpc202_dev_num_from_alias(alias) ((alias) % 2)
|
||||
|
||||
struct fpc202_priv {
|
||||
struct i2c_client *client;
|
||||
struct i2c_atr *atr;
|
||||
struct gpio_desc *en_gpio;
|
||||
struct gpio_chip gpio;
|
||||
|
||||
/* Lock REG_MOD/AUX_DEV and addr_caches during attach/detach */
|
||||
struct mutex reg_dev_lock;
|
||||
|
||||
/* Cached device addresses for both ports and their devices */
|
||||
u8 addr_caches[2][2];
|
||||
|
||||
/* Keep track of which ports were probed */
|
||||
DECLARE_BITMAP(probed_ports, FPC202_NUM_PORTS);
|
||||
};
|
||||
|
||||
static void fpc202_fill_alias_table(struct i2c_client *client, u16 *aliases, int port_id)
|
||||
{
|
||||
u16 first_alias;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* There is a predefined list of aliases for each FPC202 I2C
|
||||
* self-address. This allows daisy-chained FPC202 units to
|
||||
* automatically take on different sets of aliases.
|
||||
* Each port of an FPC202 unit is assigned two aliases from this list.
|
||||
*/
|
||||
first_alias = 0x10 + 4 * port_id + 8 * ((u16)client->addr - 2);
|
||||
|
||||
for (i = 0; i < FPC202_ALIASES_PER_PORT; i++)
|
||||
aliases[i] = first_alias + i;
|
||||
}
|
||||
|
||||
static int fpc202_gpio_get_dir(int offset)
|
||||
{
|
||||
return offset < FPC202_GPIO_P0_S0_OUT_A ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT;
|
||||
}
|
||||
|
||||
static int fpc202_read(struct fpc202_priv *priv, u8 reg)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = i2c_smbus_read_byte_data(priv->client, reg);
|
||||
return val;
|
||||
}
|
||||
|
||||
static int fpc202_write(struct fpc202_priv *priv, u8 reg, u8 value)
|
||||
{
|
||||
return i2c_smbus_write_byte_data(priv->client, reg, value);
|
||||
}
|
||||
|
||||
static void fpc202_set_enable(struct fpc202_priv *priv, int enable)
|
||||
{
|
||||
if (!priv->en_gpio)
|
||||
return;
|
||||
|
||||
gpiod_set_value(priv->en_gpio, enable);
|
||||
}
|
||||
|
||||
static void fpc202_gpio_set(struct gpio_chip *chip, unsigned int offset,
|
||||
int value)
|
||||
{
|
||||
struct fpc202_priv *priv = gpiochip_get_data(chip);
|
||||
int ret;
|
||||
u8 val;
|
||||
|
||||
if (fpc202_gpio_get_dir(offset) == GPIO_LINE_DIRECTION_IN)
|
||||
return;
|
||||
|
||||
ret = fpc202_read(priv, FPC202_REG_OUT_A_OUT_B_VAL);
|
||||
if (ret < 0) {
|
||||
dev_err(&priv->client->dev, "Failed to set GPIO %d value! err %d\n", offset, ret);
|
||||
return;
|
||||
}
|
||||
|
||||
val = (u8)ret;
|
||||
|
||||
if (value)
|
||||
val |= BIT(offset - FPC202_GPIO_P0_S0_OUT_A);
|
||||
else
|
||||
val &= ~BIT(offset - FPC202_GPIO_P0_S0_OUT_A);
|
||||
|
||||
fpc202_write(priv, FPC202_REG_OUT_A_OUT_B_VAL, val);
|
||||
}
|
||||
|
||||
static int fpc202_gpio_get(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
struct fpc202_priv *priv = gpiochip_get_data(chip);
|
||||
u8 reg, bit;
|
||||
int ret;
|
||||
|
||||
if (offset < FPC202_GPIO_P0_S0_IN_B) {
|
||||
reg = FPC202_REG_IN_A_INT;
|
||||
bit = BIT(4 + offset);
|
||||
} else if (offset < FPC202_GPIO_P0_S0_OUT_A) {
|
||||
reg = FPC202_REG_IN_C_IN_B;
|
||||
bit = BIT(offset - FPC202_GPIO_P0_S0_IN_B);
|
||||
} else {
|
||||
reg = FPC202_REG_OUT_A_OUT_B_VAL;
|
||||
bit = BIT(offset - FPC202_GPIO_P0_S0_OUT_A);
|
||||
}
|
||||
|
||||
ret = fpc202_read(priv, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return !!(((u8)ret) & bit);
|
||||
}
|
||||
|
||||
static int fpc202_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
if (fpc202_gpio_get_dir(offset) == GPIO_LINE_DIRECTION_OUT)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fpc202_gpio_direction_output(struct gpio_chip *chip, unsigned int offset,
|
||||
int value)
|
||||
{
|
||||
struct fpc202_priv *priv = gpiochip_get_data(chip);
|
||||
int ret;
|
||||
u8 val;
|
||||
|
||||
if (fpc202_gpio_get_dir(offset) == GPIO_LINE_DIRECTION_IN)
|
||||
return -EINVAL;
|
||||
|
||||
fpc202_gpio_set(chip, offset, value);
|
||||
|
||||
ret = fpc202_read(priv, FPC202_REG_OUT_A_OUT_B);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val = (u8)ret | BIT(offset - FPC202_GPIO_P0_S0_OUT_A);
|
||||
|
||||
return fpc202_write(priv, FPC202_REG_OUT_A_OUT_B, val);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the translation table entry associated with a port and device number.
|
||||
*
|
||||
* Each downstream port of the FPC202 has two fixed aliases corresponding to
|
||||
* device numbers 0 and 1. If one of these aliases is found in an incoming I2C
|
||||
* transfer, it will be translated to the address given by the corresponding
|
||||
* translation table entry.
|
||||
*/
|
||||
static int fpc202_write_dev_addr(struct fpc202_priv *priv, u32 port_id, int dev_num, u16 addr)
|
||||
{
|
||||
int ret, reg_mod, reg_aux;
|
||||
u8 val;
|
||||
|
||||
guard(mutex)(&priv->reg_dev_lock);
|
||||
|
||||
reg_mod = FPC202_REG_MOD_DEV(port_id, dev_num);
|
||||
reg_aux = FPC202_REG_AUX_DEV(port_id, dev_num);
|
||||
val = addr & 0x7f;
|
||||
|
||||
ret = fpc202_write(priv, reg_mod, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* The FPC202 datasheet is unclear about the role of the AUX registers.
|
||||
* Empirically, writing to them as well seems to be necessary for
|
||||
* address translation to function properly.
|
||||
*/
|
||||
ret = fpc202_write(priv, reg_aux, val);
|
||||
|
||||
priv->addr_caches[port_id][dev_num] = val;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fpc202_attach_addr(struct i2c_atr *atr, u32 chan_id,
|
||||
u16 addr, u16 alias)
|
||||
{
|
||||
struct fpc202_priv *priv = i2c_atr_get_driver_data(atr);
|
||||
|
||||
dev_dbg(&priv->client->dev, "attaching address 0x%02x to alias 0x%02x\n", addr, alias);
|
||||
|
||||
return fpc202_write_dev_addr(priv, chan_id, fpc202_dev_num_from_alias(alias), addr);
|
||||
}
|
||||
|
||||
static void fpc202_detach_addr(struct i2c_atr *atr, u32 chan_id,
|
||||
u16 addr)
|
||||
{
|
||||
struct fpc202_priv *priv = i2c_atr_get_driver_data(atr);
|
||||
int dev_num, reg_mod, val;
|
||||
|
||||
for (dev_num = 0; dev_num < 2; dev_num++) {
|
||||
reg_mod = FPC202_REG_MOD_DEV(chan_id, dev_num);
|
||||
|
||||
mutex_lock(&priv->reg_dev_lock);
|
||||
|
||||
val = priv->addr_caches[chan_id][dev_num];
|
||||
|
||||
mutex_unlock(&priv->reg_dev_lock);
|
||||
|
||||
if (val < 0) {
|
||||
dev_err(&priv->client->dev, "failed to read register 0x%x while detaching address 0x%02x\n",
|
||||
reg_mod, addr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (val == (addr & 0x7f)) {
|
||||
fpc202_write_dev_addr(priv, chan_id, dev_num, FPC202_REG_DEV_INVALID);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const struct i2c_atr_ops fpc202_atr_ops = {
|
||||
.attach_addr = fpc202_attach_addr,
|
||||
.detach_addr = fpc202_detach_addr,
|
||||
};
|
||||
|
||||
static int fpc202_probe_port(struct fpc202_priv *priv, struct device_node *i2c_handle, int port_id)
|
||||
{
|
||||
u16 aliases[FPC202_ALIASES_PER_PORT] = { };
|
||||
struct device *dev = &priv->client->dev;
|
||||
struct i2c_atr_adap_desc desc = { };
|
||||
int ret = 0;
|
||||
|
||||
desc.chan_id = port_id;
|
||||
desc.parent = dev;
|
||||
desc.bus_handle = of_node_to_fwnode(i2c_handle);
|
||||
desc.num_aliases = FPC202_ALIASES_PER_PORT;
|
||||
|
||||
fpc202_fill_alias_table(priv->client, aliases, port_id);
|
||||
desc.aliases = aliases;
|
||||
|
||||
ret = i2c_atr_add_adapter(priv->atr, &desc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
set_bit(port_id, priv->probed_ports);
|
||||
|
||||
ret = fpc202_write_dev_addr(priv, port_id, 0, FPC202_REG_DEV_INVALID);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return fpc202_write_dev_addr(priv, port_id, 1, FPC202_REG_DEV_INVALID);
|
||||
}
|
||||
|
||||
static void fpc202_remove_port(struct fpc202_priv *priv, int port_id)
|
||||
{
|
||||
i2c_atr_del_adapter(priv->atr, port_id);
|
||||
clear_bit(port_id, priv->probed_ports);
|
||||
}
|
||||
|
||||
static int fpc202_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct device_node *i2c_handle;
|
||||
struct fpc202_priv *priv;
|
||||
int ret, port_id;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&priv->reg_dev_lock);
|
||||
|
||||
priv->client = client;
|
||||
i2c_set_clientdata(client, priv);
|
||||
|
||||
priv->en_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(priv->en_gpio)) {
|
||||
ret = PTR_ERR(priv->en_gpio);
|
||||
dev_err(dev, "failed to fetch enable GPIO! err %d\n", ret);
|
||||
goto destroy_mutex;
|
||||
}
|
||||
|
||||
priv->gpio.label = "gpio-fpc202";
|
||||
priv->gpio.base = -1;
|
||||
priv->gpio.direction_input = fpc202_gpio_direction_input;
|
||||
priv->gpio.direction_output = fpc202_gpio_direction_output;
|
||||
priv->gpio.set = fpc202_gpio_set;
|
||||
priv->gpio.get = fpc202_gpio_get;
|
||||
priv->gpio.ngpio = FPC202_GPIO_COUNT;
|
||||
priv->gpio.parent = dev;
|
||||
priv->gpio.owner = THIS_MODULE;
|
||||
|
||||
ret = gpiochip_add_data(&priv->gpio, priv);
|
||||
if (ret) {
|
||||
priv->gpio.parent = NULL;
|
||||
dev_err(dev, "failed to add gpiochip err %d\n", ret);
|
||||
goto disable_gpio;
|
||||
}
|
||||
|
||||
priv->atr = i2c_atr_new(client->adapter, dev, &fpc202_atr_ops, 2, 0);
|
||||
if (IS_ERR(priv->atr)) {
|
||||
ret = PTR_ERR(priv->atr);
|
||||
dev_err(dev, "failed to create i2c atr err %d\n", ret);
|
||||
goto disable_gpio;
|
||||
}
|
||||
|
||||
i2c_atr_set_driver_data(priv->atr, priv);
|
||||
|
||||
bitmap_zero(priv->probed_ports, FPC202_NUM_PORTS);
|
||||
|
||||
for_each_child_of_node(dev->of_node, i2c_handle) {
|
||||
ret = of_property_read_u32(i2c_handle, "reg", &port_id);
|
||||
if (ret) {
|
||||
if (ret == -EINVAL)
|
||||
continue;
|
||||
|
||||
dev_err(dev, "failed to read 'reg' property of child node, err %d\n", ret);
|
||||
goto unregister_chans;
|
||||
}
|
||||
|
||||
if (port_id > FPC202_NUM_PORTS) {
|
||||
dev_err(dev, "port ID %d is out of range!\n", port_id);
|
||||
ret = -EINVAL;
|
||||
goto unregister_chans;
|
||||
}
|
||||
|
||||
ret = fpc202_probe_port(priv, i2c_handle, port_id);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to probe port %d, err %d\n", port_id, ret);
|
||||
goto unregister_chans;
|
||||
}
|
||||
}
|
||||
|
||||
goto out;
|
||||
|
||||
unregister_chans:
|
||||
for_each_set_bit(port_id, priv->probed_ports, FPC202_NUM_PORTS)
|
||||
fpc202_remove_port(priv, port_id);
|
||||
|
||||
i2c_atr_delete(priv->atr);
|
||||
disable_gpio:
|
||||
fpc202_set_enable(priv, 0);
|
||||
gpiochip_remove(&priv->gpio);
|
||||
destroy_mutex:
|
||||
mutex_destroy(&priv->reg_dev_lock);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void fpc202_remove(struct i2c_client *client)
|
||||
{
|
||||
struct fpc202_priv *priv = i2c_get_clientdata(client);
|
||||
int port_id;
|
||||
|
||||
for_each_set_bit(port_id, priv->probed_ports, FPC202_NUM_PORTS)
|
||||
fpc202_remove_port(priv, port_id);
|
||||
|
||||
mutex_destroy(&priv->reg_dev_lock);
|
||||
|
||||
i2c_atr_delete(priv->atr);
|
||||
|
||||
fpc202_set_enable(priv, 0);
|
||||
gpiochip_remove(&priv->gpio);
|
||||
}
|
||||
|
||||
static const struct of_device_id fpc202_of_match[] = {
|
||||
{ .compatible = "ti,fpc202" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fpc202_of_match);
|
||||
|
||||
static struct i2c_driver fpc202_driver = {
|
||||
.driver = {
|
||||
.name = "fpc202",
|
||||
.of_match_table = fpc202_of_match,
|
||||
},
|
||||
.probe = fpc202_probe,
|
||||
.remove = fpc202_remove,
|
||||
};
|
||||
|
||||
module_i2c_driver(fpc202_driver);
|
||||
|
||||
MODULE_AUTHOR("Romain Gantois <romain.gantois@bootlin.com>");
|
||||
MODULE_DESCRIPTION("TI FPC202 Dual Port Controller driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS("I2C_ATR");
|
|
@ -18,22 +18,60 @@ struct device;
|
|||
struct fwnode_handle;
|
||||
struct i2c_atr;
|
||||
|
||||
/**
|
||||
* enum i2c_atr_flags - Flags for an I2C ATR driver
|
||||
*
|
||||
* @I2C_ATR_F_STATIC: ATR does not support dynamic mapping, use static mapping.
|
||||
* Mappings will only be added or removed as a result of
|
||||
* devices being added or removed from a child bus.
|
||||
* The ATR pool will have to be big enough to accomodate all
|
||||
* devices expected to be added to the child buses.
|
||||
* @I2C_ATR_F_PASSTHROUGH: Allow unmapped incoming addresses to pass through
|
||||
*/
|
||||
enum i2c_atr_flags {
|
||||
I2C_ATR_F_STATIC = BIT(0),
|
||||
I2C_ATR_F_PASSTHROUGH = BIT(1),
|
||||
};
|
||||
|
||||
/**
|
||||
* struct i2c_atr_ops - Callbacks from ATR to the device driver.
|
||||
* @attach_client: Notify the driver of a new device connected on a child
|
||||
* bus, with the alias assigned to it. The driver must
|
||||
* configure the hardware to use the alias.
|
||||
* @detach_client: Notify the driver of a device getting disconnected. The
|
||||
* driver must configure the hardware to stop using the
|
||||
* alias.
|
||||
* @attach_addr: Notify the driver of a new device connected on a child
|
||||
* bus, with the alias assigned to it. The driver must
|
||||
* configure the hardware to use the alias.
|
||||
* @detach_addr: Notify the driver of a device getting disconnected. The
|
||||
* driver must configure the hardware to stop using the
|
||||
* alias.
|
||||
*
|
||||
* All these functions return 0 on success, a negative error code otherwise.
|
||||
*/
|
||||
struct i2c_atr_ops {
|
||||
int (*attach_client)(struct i2c_atr *atr, u32 chan_id,
|
||||
const struct i2c_client *client, u16 alias);
|
||||
void (*detach_client)(struct i2c_atr *atr, u32 chan_id,
|
||||
const struct i2c_client *client);
|
||||
int (*attach_addr)(struct i2c_atr *atr, u32 chan_id,
|
||||
u16 addr, u16 alias);
|
||||
void (*detach_addr)(struct i2c_atr *atr, u32 chan_id,
|
||||
u16 addr);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct i2c_atr_adap_desc - An ATR downstream bus descriptor
|
||||
* @chan_id: Index of the new adapter (0 .. max_adapters-1). This value is
|
||||
* passed to the callbacks in `struct i2c_atr_ops`.
|
||||
* @parent: The device used as the parent of the new i2c adapter, or NULL
|
||||
* to use the i2c-atr device as the parent.
|
||||
* @bus_handle: The fwnode handle that points to the adapter's i2c
|
||||
* peripherals, or NULL.
|
||||
* @num_aliases: The number of aliases in this adapter's private alias pool. Set
|
||||
* to zero if this adapter uses the ATR's global alias pool.
|
||||
* @aliases: An optional array of private aliases used by the adapter
|
||||
* instead of the ATR's global pool of aliases. Must contain
|
||||
* exactly num_aliases entries if num_aliases > 0, is ignored
|
||||
* otherwise.
|
||||
*/
|
||||
struct i2c_atr_adap_desc {
|
||||
u32 chan_id;
|
||||
struct device *parent;
|
||||
struct fwnode_handle *bus_handle;
|
||||
size_t num_aliases;
|
||||
u16 *aliases;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -42,6 +80,7 @@ struct i2c_atr_ops {
|
|||
* @dev: The device acting as an ATR
|
||||
* @ops: Driver-specific callbacks
|
||||
* @max_adapters: Maximum number of child adapters
|
||||
* @flags: Flags for ATR
|
||||
*
|
||||
* The new ATR helper is connected to the parent adapter but has no child
|
||||
* adapters. Call i2c_atr_add_adapter() to add some.
|
||||
|
@ -51,7 +90,8 @@ struct i2c_atr_ops {
|
|||
* Return: pointer to the new ATR helper object, or ERR_PTR
|
||||
*/
|
||||
struct i2c_atr *i2c_atr_new(struct i2c_adapter *parent, struct device *dev,
|
||||
const struct i2c_atr_ops *ops, int max_adapters);
|
||||
const struct i2c_atr_ops *ops, int max_adapters,
|
||||
u32 flags);
|
||||
|
||||
/**
|
||||
* i2c_atr_delete - Delete an I2C ATR helper.
|
||||
|
@ -65,12 +105,7 @@ void i2c_atr_delete(struct i2c_atr *atr);
|
|||
/**
|
||||
* i2c_atr_add_adapter - Create a child ("downstream") I2C bus.
|
||||
* @atr: The I2C ATR
|
||||
* @chan_id: Index of the new adapter (0 .. max_adapters-1). This value is
|
||||
* passed to the callbacks in `struct i2c_atr_ops`.
|
||||
* @adapter_parent: The device used as the parent of the new i2c adapter, or NULL
|
||||
* to use the i2c-atr device as the parent.
|
||||
* @bus_handle: The fwnode handle that points to the adapter's i2c
|
||||
* peripherals, or NULL.
|
||||
* @desc: An ATR adapter descriptor
|
||||
*
|
||||
* After calling this function a new i2c bus will appear. Adding and removing
|
||||
* devices on the downstream bus will result in calls to the
|
||||
|
@ -85,9 +120,7 @@ void i2c_atr_delete(struct i2c_atr *atr);
|
|||
*
|
||||
* Return: 0 on success, a negative error code otherwise.
|
||||
*/
|
||||
int i2c_atr_add_adapter(struct i2c_atr *atr, u32 chan_id,
|
||||
struct device *adapter_parent,
|
||||
struct fwnode_handle *bus_handle);
|
||||
int i2c_atr_add_adapter(struct i2c_atr *atr, struct i2c_atr_adap_desc *desc);
|
||||
|
||||
/**
|
||||
* i2c_atr_del_adapter - Remove a child ("downstream") I2C bus added by
|
||||
|
|
|
@ -44,9 +44,11 @@ static inline void i2c_free_slave_host_notify_device(struct i2c_client *client)
|
|||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_I2C_SMBUS) && IS_ENABLED(CONFIG_DMI)
|
||||
void i2c_register_spd(struct i2c_adapter *adap);
|
||||
void i2c_register_spd_write_disable(struct i2c_adapter *adap);
|
||||
void i2c_register_spd_write_enable(struct i2c_adapter *adap);
|
||||
#else
|
||||
static inline void i2c_register_spd(struct i2c_adapter *adap) { }
|
||||
static inline void i2c_register_spd_write_disable(struct i2c_adapter *adap) { }
|
||||
static inline void i2c_register_spd_write_enable(struct i2c_adapter *adap) { }
|
||||
#endif
|
||||
|
||||
#endif /* _LINUX_I2C_SMBUS_H */
|
||||
|
|
|
@ -405,7 +405,6 @@ static inline bool i2c_detect_slave_mode(struct device *dev) { return false; }
|
|||
* @addr: stored in i2c_client.addr
|
||||
* @dev_name: Overrides the default <busnr>-<addr> dev_name if set
|
||||
* @platform_data: stored in i2c_client.dev.platform_data
|
||||
* @of_node: pointer to OpenFirmware device node
|
||||
* @fwnode: device node supplied by the platform firmware
|
||||
* @swnode: software node for the device
|
||||
* @resources: resources associated with the device
|
||||
|
@ -429,7 +428,6 @@ struct i2c_board_info {
|
|||
unsigned short addr;
|
||||
const char *dev_name;
|
||||
void *platform_data;
|
||||
struct device_node *of_node;
|
||||
struct fwnode_handle *fwnode;
|
||||
const struct software_node *swnode;
|
||||
const struct resource *resources;
|
||||
|
|
Loading…
Reference in New Issue
Block a user