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:
Linus Torvalds 2025-05-30 10:07:53 -07:00
commit 883e3c9f40
65 changed files with 2126 additions and 664 deletions

View File

@ -52,6 +52,7 @@ properties:
- const: mediatek,mt8173-i2c
- items:
- enum:
- mediatek,mt6893-i2c
- mediatek,mt8195-i2c
- const: mediatek,mt8192-i2c

View File

@ -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

View File

@ -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>;
};

View File

@ -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)

View File

@ -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:

View 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>;
};

View 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>;
};
};
};
...

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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",

View File

@ -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)

View 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");

View File

@ -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);
}

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 */

View File

@ -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",

View File

@ -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;

View File

@ -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)

View File

@ -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:

View File

@ -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);

View File

@ -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;
}
}
}
}

View File

@ -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 *);

View File

@ -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:

View File

@ -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"

View File

@ -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;

View File

@ -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)) {

View File

@ -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))

View File

@ -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);

View File

@ -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,
};

View File

@ -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 */

View File

@ -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);

View File

@ -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;

View File

@ -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 */

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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)

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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)

View File

@ -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)

View File

@ -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;
}

View File

@ -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)

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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"))

View File

@ -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", &reg);
fwnode_for_each_child_node(fwnode, child) {
fwnode_property_read_u32(child, "reg", &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;

View File

@ -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;
}

View File

@ -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>");

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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:

View File

@ -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

View File

@ -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
View 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");

View File

@ -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

View File

@ -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 */

View File

@ -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;