Merge branch 'i2c/for-5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux

Pull i2c updates from Wolfram Sang:

 - if a host can be a client, too, the I2C core can now use it to
   emulate SMBus HostNotify support (STM32 and R-Car added this so far)

 - also for client mode, a testunit has been added. It can create rare
   situations on the bus, so host controllers can be tested

 - a binding has been added to mark the bus as "single-master". This
   allows for better timeout detections

 - new driver for Mellanox Bluefield

 - massive refactoring of the Tegra driver

 - EEPROMs recognized by the at24 driver can now have custom names

 - rest is driver updates

* 'i2c/for-5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (80 commits)
  Documentation: i2c: add testunit docs to index
  i2c: tegra: Improve driver module description
  i2c: tegra: Clean up whitespaces, newlines and indentation
  i2c: tegra: Clean up and improve comments
  i2c: tegra: Clean up printk messages
  i2c: tegra: Clean up variable names
  i2c: tegra: Improve formatting of variables
  i2c: tegra: Check errors for both positive and negative values
  i2c: tegra: Factor out hardware initialization into separate function
  i2c: tegra: Factor out register polling into separate function
  i2c: tegra: Factor out packet header setup from tegra_i2c_xfer_msg()
  i2c: tegra: Factor out error recovery from tegra_i2c_xfer_msg()
  i2c: tegra: Rename wait/poll functions
  i2c: tegra: Remove "dma" variable from tegra_i2c_xfer_msg()
  i2c: tegra: Remove redundant check in tegra_i2c_issue_bus_clear()
  i2c: tegra: Remove likely/unlikely from the code
  i2c: tegra: Remove outdated barrier()
  i2c: tegra: Clean up variable types
  i2c: tegra: Reorder location of functions in the code
  i2c: tegra: Clean up probe function
  ...
This commit is contained in:
Linus Torvalds
2020-10-21 10:54:05 -07:00
41 changed files with 4020 additions and 931 deletions

View File

@@ -101,7 +101,6 @@ source "drivers/i2c/busses/Kconfig"
config I2C_STUB
tristate "I2C/SMBus Test Stub"
depends on m
default 'n'
help
This module may be useful to developers of SMBus client drivers,
especially for certain kinds of sensor chips.
@@ -126,6 +125,14 @@ config I2C_SLAVE_EEPROM
This backend makes Linux behave like an I2C EEPROM. Please read
Documentation/i2c/slave-eeprom-backend.rst for further details.
config I2C_SLAVE_TESTUNIT
tristate "I2C eeprom testunit driver"
help
This backend can be used to trigger test cases for I2C bus masters
which require a remote device with certain capabilities, e.g.
multi-master, SMBus Host Notify, etc. Please read
Documentation/i2c/slave-testunit-backend.rst for further details.
endif
config I2C_DEBUG_CORE

View File

@@ -16,5 +16,6 @@ obj-$(CONFIG_I2C_MUX) += i2c-mux.o
obj-y += algos/ busses/ muxes/
obj-$(CONFIG_I2C_STUB) += i2c-stub.o
obj-$(CONFIG_I2C_SLAVE_EEPROM) += i2c-slave-eeprom.o
obj-$(CONFIG_I2C_SLAVE_TESTUNIT) += i2c-slave-testunit.o
ccflags-$(CONFIG_I2C_DEBUG_CORE) := -DDEBUG

View File

@@ -147,6 +147,7 @@ config I2C_I801
Tiger Lake (PCH)
Jasper Lake (SOC)
Emmitsburg (PCH)
Alder Lake (PCH)
This driver can also be built as a module. If so, the module
will be called i2c-i801.
@@ -730,6 +731,19 @@ config I2C_LPC2K
This driver can also be built as a module. If so, the module
will be called i2c-lpc2k.
config I2C_MLXBF
tristate "Mellanox BlueField I2C controller"
depends on ARM64
help
Enabling this option will add I2C SMBus support for Mellanox BlueField
system.
This driver can also be built as a module. If so, the module will be
called i2c-mlxbf.
This driver implements an I2C SMBus host controller and enables both
master and slave functions.
config I2C_MESON
tristate "Amlogic Meson I2C controller"
depends on ARCH_MESON || COMPILE_TEST
@@ -840,7 +854,6 @@ config I2C_PASEMI
config I2C_PCA_PLATFORM
tristate "PCA9564/PCA9665 as platform device"
select I2C_ALGOPCA
default n
help
This driver supports a memory mapped Philips PCA9564/PCA9665
parallel bus to I2C bus controller.
@@ -1026,6 +1039,7 @@ config I2C_STM32F7
tristate "STMicroelectronics STM32F7 I2C support"
depends on ARCH_STM32 || COMPILE_TEST
select I2C_SLAVE
select I2C_SMBUS
help
Enable this option to add support for STM32 I2C controller embedded
in STM32F7 SoCs.
@@ -1181,6 +1195,8 @@ config I2C_RCAR
tristate "Renesas R-Car I2C Controller"
depends on ARCH_RENESAS || COMPILE_TEST
select I2C_SLAVE
select I2C_SMBUS
select RESET_CONTROLLER if ARCH_RCAR_GEN3
help
If you say yes to this option, support will be included for the
R-Car I2C controller.
@@ -1240,7 +1256,6 @@ config I2C_TAOS_EVM
depends on TTY
select SERIO
select SERIO_SERPORT
default n
help
This supports TAOS evaluation modules on serial port. In order to
use this driver, you will need the inputattach tool, which is part
@@ -1324,7 +1339,6 @@ config I2C_PCA_ISA
tristate "PCA9564/PCA9665 on an ISA bus"
depends on ISA
select I2C_ALGOPCA
default n
help
This driver supports ISA boards using the Philips PCA9564/PCA9665
parallel bus to I2C bus controller.

View File

@@ -140,6 +140,7 @@ obj-$(CONFIG_I2C_BRCMSTB) += i2c-brcmstb.o
obj-$(CONFIG_I2C_CROS_EC_TUNNEL) += i2c-cros-ec-tunnel.o
obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o
obj-$(CONFIG_I2C_ICY) += i2c-icy.o
obj-$(CONFIG_I2C_MLXBF) += i2c-mlxbf.o
obj-$(CONFIG_I2C_MLXCPLD) += i2c-mlxcpld.o
obj-$(CONFIG_I2C_OPAL) += i2c-opal.o
obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o

View File

@@ -155,7 +155,7 @@ static int i2c_amd_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
struct amd_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
int i;
struct i2c_msg *pmsg;
int err;
int err = 0;
/* the adapter might have been deleted while waiting for the bus lock */
if (unlikely(!i2c_dev->common.mp2_dev))

View File

@@ -421,11 +421,9 @@ static int bcm2835_i2c_probe(struct platform_device *pdev)
return PTR_ERR(i2c_dev->regs);
mclk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(mclk)) {
if (PTR_ERR(mclk) != -EPROBE_DEFER)
dev_err(&pdev->dev, "Could not get clock\n");
return PTR_ERR(mclk);
}
if (IS_ERR(mclk))
return dev_err_probe(&pdev->dev, PTR_ERR(mclk),
"Could not get clock\n");
i2c_dev->bus_clk = bcm2835_i2c_register_div(&pdev->dev, mclk, i2c_dev);

View File

@@ -332,21 +332,15 @@ static int efm32_i2c_probe(struct platform_device *pdev)
return ret;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "failed to determine base address\n");
return -ENODEV;
}
ddata->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(ddata->base))
return PTR_ERR(ddata->base);
if (resource_size(res) < 0x42) {
dev_err(&pdev->dev, "memory resource too small\n");
return -EINVAL;
}
ddata->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(ddata->base))
return PTR_ERR(ddata->base);
ret = platform_get_irq(pdev, 0);
if (ret <= 0) {
if (!ret)

View File

@@ -71,6 +71,7 @@
* Tiger Lake-H (PCH) 0x43a3 32 hard yes yes yes
* Jasper Lake (SOC) 0x4da3 32 hard yes yes yes
* Comet Lake-V (PCH) 0xa3a3 32 hard yes yes yes
* Alder Lake-S (PCH) 0x7aa3 32 hard yes yes yes
*
* Features supported by this driver:
* Software PEC no
@@ -228,6 +229,7 @@
#define PCI_DEVICE_ID_INTEL_ELKHART_LAKE_SMBUS 0x4b23
#define PCI_DEVICE_ID_INTEL_JASPER_LAKE_SMBUS 0x4da3
#define PCI_DEVICE_ID_INTEL_BROXTON_SMBUS 0x5ad4
#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_S_SMBUS 0x7aa3
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22
#define PCI_DEVICE_ID_INTEL_WILDCATPOINT_SMBUS 0x8ca2
#define PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS 0x8d22
@@ -1081,6 +1083,7 @@ static const struct pci_device_id i801_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TIGERLAKE_LP_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TIGERLAKE_H_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_JASPER_LAKE_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ALDER_LAKE_S_SMBUS) },
{ 0, }
};
@@ -1274,6 +1277,7 @@ static const struct {
/*
* Additional individual entries were added after verification.
*/
{ "Latitude 5480", 0x29 },
{ "Vostro V131", 0x1d },
};
@@ -1767,6 +1771,7 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
case PCI_DEVICE_ID_INTEL_TIGERLAKE_H_SMBUS:
case PCI_DEVICE_ID_INTEL_JASPER_LAKE_SMBUS:
case PCI_DEVICE_ID_INTEL_EBG_SMBUS:
case PCI_DEVICE_ID_INTEL_ALDER_LAKE_S_SMBUS:
priv->features |= FEATURE_BLOCK_PROC;
priv->features |= FEATURE_I2C_BLOCK_READ;
priv->features |= FEATURE_IRQ;

View File

@@ -1159,11 +1159,9 @@ static int i2c_imx_probe(struct platform_device *pdev)
/* Get I2C clock */
i2c_imx->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(i2c_imx->clk)) {
if (PTR_ERR(i2c_imx->clk) != -EPROBE_DEFER)
dev_err(&pdev->dev, "can't get I2C clock\n");
return PTR_ERR(i2c_imx->clk);
}
if (IS_ERR(i2c_imx->clk))
return dev_err_probe(&pdev->dev, PTR_ERR(i2c_imx->clk),
"can't get I2C clock\n");
ret = clk_prepare_enable(i2c_imx->clk);
if (ret) {
@@ -1171,14 +1169,6 @@ static int i2c_imx_probe(struct platform_device *pdev)
return ret;
}
/* Request IRQ */
ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr, IRQF_SHARED,
pdev->name, i2c_imx);
if (ret) {
dev_err(&pdev->dev, "can't claim irq %d\n", irq);
goto clk_disable;
}
/* Init queue */
init_waitqueue_head(&i2c_imx->queue);
@@ -1197,6 +1187,14 @@ static int i2c_imx_probe(struct platform_device *pdev)
if (ret < 0)
goto rpm_disable;
/* Request IRQ */
ret = request_threaded_irq(irq, i2c_imx_isr, NULL, IRQF_SHARED,
pdev->name, i2c_imx);
if (ret) {
dev_err(&pdev->dev, "can't claim irq %d\n", irq);
goto rpm_disable;
}
/* Set up clock divider */
i2c_imx->bitrate = I2C_MAX_STANDARD_MODE_FREQ;
ret = of_property_read_u32(pdev->dev.of_node,
@@ -1239,13 +1237,12 @@ static int i2c_imx_probe(struct platform_device *pdev)
clk_notifier_unregister:
clk_notifier_unregister(i2c_imx->clk, &i2c_imx->clk_change_nb);
free_irq(irq, i2c_imx);
rpm_disable:
pm_runtime_put_noidle(&pdev->dev);
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
pm_runtime_dont_use_autosuspend(&pdev->dev);
clk_disable:
clk_disable_unprepare(i2c_imx->clk);
return ret;
}
@@ -1253,7 +1250,7 @@ clk_disable:
static int i2c_imx_remove(struct platform_device *pdev)
{
struct imx_i2c_struct *i2c_imx = platform_get_drvdata(pdev);
int ret;
int irq, ret;
ret = pm_runtime_get_sync(&pdev->dev);
if (ret < 0)
@@ -1273,6 +1270,9 @@ static int i2c_imx_remove(struct platform_device *pdev)
imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2SR);
clk_notifier_unregister(i2c_imx->clk, &i2c_imx->clk_change_nb);
irq = platform_get_irq(pdev, 0);
if (irq >= 0)
free_irq(irq, i2c_imx);
clk_disable_unprepare(i2c_imx->clk);
pm_runtime_put_noidle(&pdev->dev);

View File

@@ -77,6 +77,7 @@
#define PCI_DEVICE_ID_INTEL_S1200_SMT1 0x0c5a
#define PCI_DEVICE_ID_INTEL_CDF_SMT 0x18ac
#define PCI_DEVICE_ID_INTEL_DNV_SMT 0x19ac
#define PCI_DEVICE_ID_INTEL_EBG_SMT 0x1bff
#define PCI_DEVICE_ID_INTEL_AVOTON_SMT 0x1f15
#define ISMT_DESC_ENTRIES 2 /* number of descriptor entries */
@@ -176,14 +177,12 @@ struct ismt_priv {
u8 buffer[I2C_SMBUS_BLOCK_MAX + 16]; /* temp R/W data buffer */
};
/**
* ismt_ids - PCI device IDs supported by this driver
*/
static const struct pci_device_id ismt_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_S1200_SMT0) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_S1200_SMT1) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CDF_SMT) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_DNV_SMT) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EBG_SMT) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_AVOTON_SMT) },
{ 0, }
};
@@ -197,6 +196,8 @@ MODULE_PARM_DESC(bus_speed, "Bus Speed in kHz (0 = BIOS default)");
/**
* __ismt_desc_dump() - dump the contents of a specific descriptor
* @dev: the iSMT device
* @desc: the iSMT hardware descriptor
*/
static void __ismt_desc_dump(struct device *dev, const struct ismt_desc *desc)
{
@@ -628,11 +629,6 @@ static u32 ismt_func(struct i2c_adapter *adap)
I2C_FUNC_SMBUS_PEC;
}
/**
* smbus_algorithm - the adapter algorithm and supported functionality
* @smbus_xfer: the adapter algorithm
* @functionality: functionality supported by the adapter
*/
static const struct i2c_algorithm smbus_algorithm = {
.smbus_xfer = ismt_access,
.functionality = ismt_func,

View File

@@ -752,6 +752,7 @@ static const struct ingenic_i2c_config x1000_i2c_config = {
};
static const struct of_device_id jz4780_i2c_of_matches[] = {
{ .compatible = "ingenic,jz4770-i2c", .data = &jz4780_i2c_config },
{ .compatible = "ingenic,jz4780-i2c", .data = &jz4780_i2c_config },
{ .compatible = "ingenic,x1000-i2c", .data = &x1000_i2c_config },
{ /* sentinel */ }
@@ -856,7 +857,7 @@ static struct platform_driver jz4780_i2c_driver = {
.remove = jz4780_i2c_remove,
.driver = {
.name = "jz4780-i2c",
.of_match_table = of_match_ptr(jz4780_i2c_of_matches),
.of_match_table = jz4780_i2c_of_matches,
},
};

File diff suppressed because it is too large Load Diff

View File

@@ -496,11 +496,10 @@ static irqreturn_t
mv64xxx_i2c_intr(int irq, void *dev_id)
{
struct mv64xxx_i2c_data *drv_data = dev_id;
unsigned long flags;
u32 status;
irqreturn_t rc = IRQ_NONE;
spin_lock_irqsave(&drv_data->lock, flags);
spin_lock(&drv_data->lock);
if (drv_data->offload_enabled)
rc = mv64xxx_i2c_intr_offload(drv_data);
@@ -517,7 +516,7 @@ mv64xxx_i2c_intr(int irq, void *dev_id)
rc = IRQ_HANDLED;
}
spin_unlock_irqrestore(&drv_data->lock, flags);
spin_unlock(&drv_data->lock);
return rc;
}

View File

@@ -125,8 +125,7 @@ static int gpu_i2c_read(struct gpu_i2c_dev *i2cd, u8 *data, u16 len)
put_unaligned_be16(val, data);
break;
case 3:
put_unaligned_be16(val >> 8, data);
data[2] = val;
put_unaligned_be24(val, data);
break;
case 4:
put_unaligned_be32(val, data);

View File

@@ -165,10 +165,9 @@ static irqreturn_t owl_i2c_interrupt(int irq, void *_dev)
{
struct owl_i2c_dev *i2c_dev = _dev;
struct i2c_msg *msg = i2c_dev->msg;
unsigned long flags;
unsigned int stat, fifostat;
spin_lock_irqsave(&i2c_dev->lock, flags);
spin_lock(&i2c_dev->lock);
i2c_dev->err = 0;
@@ -214,7 +213,7 @@ stop:
OWL_I2C_STAT_IRQP, true);
complete_all(&i2c_dev->msg_complete);
spin_unlock_irqrestore(&i2c_dev->lock, flags);
spin_unlock(&i2c_dev->lock);
return IRQ_HANDLED;
}

View File

@@ -210,9 +210,8 @@ static irqreturn_t geni_i2c_irq(int irq, void *dev)
u32 dma;
u32 val;
struct i2c_msg *cur;
unsigned long flags;
spin_lock_irqsave(&gi2c->lock, flags);
spin_lock(&gi2c->lock);
m_stat = readl_relaxed(base + SE_GENI_M_IRQ_STATUS);
rx_st = readl_relaxed(base + SE_GENI_RX_FIFO_STATUS);
dm_tx_st = readl_relaxed(base + SE_DMA_TX_IRQ_STAT);
@@ -294,7 +293,7 @@ static irqreturn_t geni_i2c_irq(int irq, void *dev)
dm_rx_st & RX_DMA_DONE || dm_rx_st & RX_RESET_DONE)
complete(&gi2c->done);
spin_unlock_irqrestore(&gi2c->lock, flags);
spin_unlock(&gi2c->lock);
return IRQ_HANDLED;
}

View File

@@ -19,7 +19,9 @@
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/i2c.h>
#include <linux/i2c-smbus.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_device.h>
@@ -105,10 +107,11 @@
#define ID_ARBLOST (1 << 3)
#define ID_NACK (1 << 4)
/* persistent flags */
#define ID_P_HOST_NOTIFY BIT(28)
#define ID_P_REP_AFTER_RD BIT(29)
#define ID_P_NO_RXDMA BIT(30) /* HW forbids RXDMA sometimes */
#define ID_P_PM_BLOCKED BIT(31)
#define ID_P_MASK GENMASK(31, 29)
#define ID_P_MASK GENMASK(31, 28)
enum rcar_i2c_type {
I2C_RCAR_GEN1,
@@ -140,14 +143,13 @@ struct rcar_i2c_priv {
struct reset_control *rstc;
int irq;
struct i2c_client *host_notify_client;
};
#define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent)
#define rcar_i2c_is_recv(p) ((p)->msg->flags & I2C_M_RD)
#define LOOP_TIMEOUT 1024
static void rcar_i2c_write(struct rcar_i2c_priv *priv, int reg, u32 val)
{
writel(val, priv->io + reg);
@@ -221,18 +223,18 @@ static void rcar_i2c_init(struct rcar_i2c_priv *priv)
static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv)
{
int i;
int ret;
u32 val;
for (i = 0; i < LOOP_TIMEOUT; i++) {
/* make sure that bus is not busy */
if (!(rcar_i2c_read(priv, ICMCR) & FSDA))
return 0;
udelay(1);
ret = readl_poll_timeout(priv->io + ICMCR, val, !(val & FSDA), 10,
priv->adap.timeout);
if (ret) {
/* Waiting did not help, try to recover */
priv->recovery_icmcr = MDBS | OBPC | FSDA | FSCL;
ret = i2c_recover_bus(&priv->adap);
}
/* Waiting did not help, try to recover */
priv->recovery_icmcr = MDBS | OBPC | FSDA | FSCL;
return i2c_recover_bus(&priv->adap);
return ret;
}
static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv)
@@ -760,20 +762,14 @@ static void rcar_i2c_release_dma(struct rcar_i2c_priv *priv)
/* I2C is a special case, we need to poll the status of a reset */
static int rcar_i2c_do_reset(struct rcar_i2c_priv *priv)
{
int i, ret;
int ret;
ret = reset_control_reset(priv->rstc);
if (ret)
return ret;
for (i = 0; i < LOOP_TIMEOUT; i++) {
ret = reset_control_status(priv->rstc);
if (ret == 0)
return 0;
udelay(1);
}
return -ETIMEDOUT;
return read_poll_timeout_atomic(reset_control_status, ret, ret == 0, 1,
100, false, priv->rstc);
}
static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
@@ -884,14 +880,21 @@ static int rcar_unreg_slave(struct i2c_client *slave)
static u32 rcar_i2c_func(struct i2c_adapter *adap)
{
struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
/*
* This HW can't do:
* I2C_SMBUS_QUICK (setting FSB during START didn't work)
* I2C_M_NOSTART (automatically sends address after START)
* I2C_M_IGNORE_NAK (automatically sends STOP after NAK)
*/
return I2C_FUNC_I2C | I2C_FUNC_SLAVE |
(I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
u32 func = I2C_FUNC_I2C | I2C_FUNC_SLAVE |
(I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
if (priv->flags & ID_P_HOST_NOTIFY)
func |= I2C_FUNC_SMBUS_HOST_NOTIFY;
return func;
}
static const struct i2c_algorithm rcar_i2c_algo = {
@@ -991,6 +994,8 @@ static int rcar_i2c_probe(struct platform_device *pdev)
else
pm_runtime_put(dev);
if (of_property_read_bool(dev->of_node, "smbus"))
priv->flags |= ID_P_HOST_NOTIFY;
priv->irq = platform_get_irq(pdev, 0);
ret = devm_request_irq(dev, priv->irq, rcar_i2c_irq, 0, dev_name(dev), priv);
@@ -1005,10 +1010,20 @@ static int rcar_i2c_probe(struct platform_device *pdev)
if (ret < 0)
goto out_pm_disable;
if (priv->flags & ID_P_HOST_NOTIFY) {
priv->host_notify_client = i2c_new_slave_host_notify_device(adap);
if (IS_ERR(priv->host_notify_client)) {
ret = PTR_ERR(priv->host_notify_client);
goto out_del_device;
}
}
dev_info(dev, "probed\n");
return 0;
out_del_device:
i2c_del_adapter(&priv->adap);
out_pm_put:
pm_runtime_put(dev);
out_pm_disable:
@@ -1021,6 +1036,8 @@ static int rcar_i2c_remove(struct platform_device *pdev)
struct rcar_i2c_priv *priv = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
if (priv->host_notify_client)
i2c_free_slave_host_notify_device(priv->host_notify_client);
i2c_del_adapter(&priv->adap);
rcar_i2c_release_dma(priv);
if (priv->flags & ID_P_PM_BLOCKED)

View File

@@ -1312,18 +1312,13 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
i2c->pclk = devm_clk_get(&pdev->dev, "pclk");
}
if (IS_ERR(i2c->clk)) {
ret = PTR_ERR(i2c->clk);
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev, "Can't get bus clk: %d\n", ret);
return ret;
}
if (IS_ERR(i2c->pclk)) {
ret = PTR_ERR(i2c->pclk);
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev, "Can't get periph clk: %d\n", ret);
return ret;
}
if (IS_ERR(i2c->clk))
return dev_err_probe(&pdev->dev, PTR_ERR(i2c->clk),
"Can't get bus clk\n");
if (IS_ERR(i2c->pclk))
return dev_err_probe(&pdev->dev, PTR_ERR(i2c->pclk),
"Can't get periph clk\n");
ret = clk_prepare(i2c->clk);
if (ret < 0) {

View File

@@ -26,8 +26,9 @@ struct stm32_i2c_dma *stm32_i2c_dma_request(struct device *dev,
dma->chan_tx = dma_request_chan(dev, "tx");
if (IS_ERR(dma->chan_tx)) {
ret = PTR_ERR(dma->chan_tx);
if (ret != -EPROBE_DEFER)
dev_err(dev, "can't request DMA tx channel\n");
if (ret != -ENODEV)
ret = dev_err_probe(dev, ret,
"can't request DMA tx channel\n");
goto fail_al;
}
@@ -46,8 +47,9 @@ struct stm32_i2c_dma *stm32_i2c_dma_request(struct device *dev,
dma->chan_rx = dma_request_chan(dev, "rx");
if (IS_ERR(dma->chan_rx)) {
ret = PTR_ERR(dma->chan_rx);
if (ret != -EPROBE_DEFER)
dev_err(dev, "can't request DMA rx channel\n");
if (ret != -ENODEV)
ret = dev_err_probe(dev, ret,
"can't request DMA rx channel\n");
goto fail_tx;
}
@@ -76,8 +78,6 @@ fail_tx:
dma_release_channel(dma->chan_tx);
fail_al:
devm_kfree(dev, dma);
if (ret != -EPROBE_DEFER)
dev_info(dev, "can't use DMA\n");
return ERR_PTR(ret);
}

View File

@@ -797,10 +797,8 @@ static int stm32f4_i2c_probe(struct platform_device *pdev)
rst = devm_reset_control_get_exclusive(&pdev->dev, NULL);
if (IS_ERR(rst)) {
ret = PTR_ERR(rst);
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev, "Error: Missing reset ctrl\n");
ret = dev_err_probe(&pdev->dev, PTR_ERR(rst),
"Error: Missing reset ctrl\n");
goto clk_free;
}
reset_control_assert(rst);

View File

@@ -18,6 +18,7 @@
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/i2c-smbus.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
@@ -50,6 +51,7 @@
/* STM32F7 I2C control 1 */
#define STM32F7_I2C_CR1_PECEN BIT(23)
#define STM32F7_I2C_CR1_SMBHEN BIT(20)
#define STM32F7_I2C_CR1_WUPEN BIT(18)
#define STM32F7_I2C_CR1_SBC BIT(16)
#define STM32F7_I2C_CR1_RXDMAEN BIT(15)
@@ -150,7 +152,12 @@
#define STM32F7_I2C_MAX_LEN 0xff
#define STM32F7_I2C_DMA_LEN_MIN 0x16
#define STM32F7_I2C_MAX_SLAVE 0x2
enum {
STM32F7_SLAVE_HOSTNOTIFY,
STM32F7_SLAVE_7_10_BITS_ADDR,
STM32F7_SLAVE_7_BITS_ADDR,
STM32F7_I2C_MAX_SLAVE
};
#define STM32F7_I2C_DNF_DEFAULT 0
#define STM32F7_I2C_DNF_MAX 16
@@ -301,6 +308,8 @@ struct stm32f7_i2c_msg {
* @fmp_creg: register address for clearing Fast Mode Plus bits
* @fmp_mask: mask for Fast Mode Plus bits in set register
* @wakeup_src: boolean to know if the device is a wakeup source
* @smbus_mode: states that the controller is configured in SMBus mode
* @host_notify_client: SMBus host-notify client
*/
struct stm32f7_i2c_dev {
struct i2c_adapter adap;
@@ -327,6 +336,8 @@ struct stm32f7_i2c_dev {
u32 fmp_creg;
u32 fmp_mask;
bool wakeup_src;
bool smbus_mode;
struct i2c_client *host_notify_client;
};
/*
@@ -1321,11 +1332,20 @@ static int stm32f7_i2c_get_free_slave_id(struct stm32f7_i2c_dev *i2c_dev,
int i;
/*
* slave[0] supports 7-bit and 10-bit slave address
* slave[1] supports 7-bit slave address only
* slave[STM32F7_SLAVE_HOSTNOTIFY] support only SMBus Host address (0x8)
* slave[STM32F7_SLAVE_7_10_BITS_ADDR] supports 7-bit and 10-bit slave address
* slave[STM32F7_SLAVE_7_BITS_ADDR] supports 7-bit slave address only
*/
for (i = STM32F7_I2C_MAX_SLAVE - 1; i >= 0; i--) {
if (i == 1 && (slave->flags & I2C_CLIENT_TEN))
if (i2c_dev->smbus_mode && (slave->addr == 0x08)) {
if (i2c_dev->slave[STM32F7_SLAVE_HOSTNOTIFY])
goto fail;
*id = STM32F7_SLAVE_HOSTNOTIFY;
return 0;
}
for (i = STM32F7_I2C_MAX_SLAVE - 1; i > STM32F7_SLAVE_HOSTNOTIFY; i--) {
if ((i == STM32F7_SLAVE_7_BITS_ADDR) &&
(slave->flags & I2C_CLIENT_TEN))
continue;
if (!i2c_dev->slave[i]) {
*id = i;
@@ -1333,6 +1353,7 @@ static int stm32f7_i2c_get_free_slave_id(struct stm32f7_i2c_dev *i2c_dev,
}
}
fail:
dev_err(dev, "Slave 0x%x could not be registered\n", slave->addr);
return -EINVAL;
@@ -1776,7 +1797,13 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave)
if (!stm32f7_i2c_is_slave_registered(i2c_dev))
stm32f7_i2c_enable_wakeup(i2c_dev, true);
if (id == 0) {
switch (id) {
case 0:
/* Slave SMBus Host */
i2c_dev->slave[id] = slave;
break;
case 1:
/* Configure Own Address 1 */
oar1 = readl_relaxed(i2c_dev->base + STM32F7_I2C_OAR1);
oar1 &= ~STM32F7_I2C_OAR1_MASK;
@@ -1789,7 +1816,9 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave)
oar1 |= STM32F7_I2C_OAR1_OA1EN;
i2c_dev->slave[id] = slave;
writel_relaxed(oar1, i2c_dev->base + STM32F7_I2C_OAR1);
} else if (id == 1) {
break;
case 2:
/* Configure Own Address 2 */
oar2 = readl_relaxed(i2c_dev->base + STM32F7_I2C_OAR2);
oar2 &= ~STM32F7_I2C_OAR2_MASK;
@@ -1802,7 +1831,10 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave)
oar2 |= STM32F7_I2C_OAR2_OA2EN;
i2c_dev->slave[id] = slave;
writel_relaxed(oar2, i2c_dev->base + STM32F7_I2C_OAR2);
} else {
break;
default:
dev_err(dev, "I2C slave id not supported\n");
ret = -ENODEV;
goto pm_free;
}
@@ -1843,10 +1875,10 @@ static int stm32f7_i2c_unreg_slave(struct i2c_client *slave)
if (ret < 0)
return ret;
if (id == 0) {
if (id == 1) {
mask = STM32F7_I2C_OAR1_OA1EN;
stm32f7_i2c_clr_bits(base + STM32F7_I2C_OAR1, mask);
} else {
} else if (id == 2) {
mask = STM32F7_I2C_OAR2_OA2EN;
stm32f7_i2c_clr_bits(base + STM32F7_I2C_OAR2, mask);
}
@@ -1911,14 +1943,51 @@ static int stm32f7_i2c_setup_fm_plus_bits(struct platform_device *pdev,
&i2c_dev->fmp_mask);
}
static int stm32f7_i2c_enable_smbus_host(struct stm32f7_i2c_dev *i2c_dev)
{
struct i2c_adapter *adap = &i2c_dev->adap;
void __iomem *base = i2c_dev->base;
struct i2c_client *client;
client = i2c_new_slave_host_notify_device(adap);
if (IS_ERR(client))
return PTR_ERR(client);
i2c_dev->host_notify_client = client;
/* Enable SMBus Host address */
stm32f7_i2c_set_bits(base + STM32F7_I2C_CR1, STM32F7_I2C_CR1_SMBHEN);
return 0;
}
static void stm32f7_i2c_disable_smbus_host(struct stm32f7_i2c_dev *i2c_dev)
{
void __iomem *base = i2c_dev->base;
if (i2c_dev->host_notify_client) {
/* Disable SMBus Host address */
stm32f7_i2c_clr_bits(base + STM32F7_I2C_CR1,
STM32F7_I2C_CR1_SMBHEN);
i2c_free_slave_host_notify_device(i2c_dev->host_notify_client);
}
}
static u32 stm32f7_i2c_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SLAVE |
I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_PEC |
I2C_FUNC_SMBUS_I2C_BLOCK;
struct stm32f7_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
u32 func = I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SLAVE |
I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_PEC |
I2C_FUNC_SMBUS_I2C_BLOCK;
if (i2c_dev->smbus_mode)
func |= I2C_FUNC_SMBUS_HOST_NOTIFY;
return func;
}
static const struct i2c_algorithm stm32f7_i2c_algo = {
@@ -1968,11 +2037,9 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
"wakeup-source");
i2c_dev->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(i2c_dev->clk)) {
if (PTR_ERR(i2c_dev->clk) != -EPROBE_DEFER)
dev_err(&pdev->dev, "Failed to get controller clock\n");
return PTR_ERR(i2c_dev->clk);
}
if (IS_ERR(i2c_dev->clk))
return dev_err_probe(&pdev->dev, PTR_ERR(i2c_dev->clk),
"Failed to get controller clock\n");
ret = clk_prepare_enable(i2c_dev->clk);
if (ret) {
@@ -1982,10 +2049,8 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
rst = devm_reset_control_get(&pdev->dev, NULL);
if (IS_ERR(rst)) {
ret = PTR_ERR(rst);
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev, "Error: Missing reset ctrl\n");
ret = dev_err_probe(&pdev->dev, PTR_ERR(rst),
"Error: Missing reset ctrl\n");
goto clk_free;
}
reset_control_assert(rst);
@@ -2052,14 +2117,13 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
i2c_dev->dma = stm32_i2c_dma_request(i2c_dev->dev, phy_addr,
STM32F7_I2C_TXDR,
STM32F7_I2C_RXDR);
if (PTR_ERR(i2c_dev->dma) == -ENODEV)
i2c_dev->dma = NULL;
else if (IS_ERR(i2c_dev->dma)) {
if (IS_ERR(i2c_dev->dma)) {
ret = PTR_ERR(i2c_dev->dma);
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev,
"Failed to request dma error %i\n", ret);
goto fmp_clear;
/* DMA support is optional, only report other errors */
if (ret != -ENODEV)
goto fmp_clear;
dev_dbg(i2c_dev->dev, "No DMA option: fallback using interrupts\n");
i2c_dev->dma = NULL;
}
if (i2c_dev->wakeup_src) {
@@ -2084,10 +2148,22 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
stm32f7_i2c_hw_config(i2c_dev);
i2c_dev->smbus_mode = of_property_read_bool(pdev->dev.of_node, "smbus");
ret = i2c_add_adapter(adap);
if (ret)
goto pm_disable;
if (i2c_dev->smbus_mode) {
ret = stm32f7_i2c_enable_smbus_host(i2c_dev);
if (ret) {
dev_err(i2c_dev->dev,
"failed to enable SMBus Host-Notify protocol (%d)\n",
ret);
goto i2c_adapter_remove;
}
}
dev_info(i2c_dev->dev, "STM32F7 I2C-%d bus adapter\n", adap->nr);
pm_runtime_mark_last_busy(i2c_dev->dev);
@@ -2095,6 +2171,9 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
return 0;
i2c_adapter_remove:
i2c_del_adapter(adap);
pm_disable:
pm_runtime_put_noidle(i2c_dev->dev);
pm_runtime_disable(i2c_dev->dev);
@@ -2126,6 +2205,8 @@ static int stm32f7_i2c_remove(struct platform_device *pdev)
{
struct stm32f7_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
stm32f7_i2c_disable_smbus_host(i2c_dev);
i2c_del_adapter(&i2c_dev->adap);
pm_runtime_get_sync(i2c_dev->dev);

File diff suppressed because it is too large Load Diff

View File

@@ -46,34 +46,36 @@ enum xiic_endian {
/**
* struct xiic_i2c - Internal representation of the XIIC I2C bus
* @dev: Pointer to device structure
* @base: Memory base of the HW registers
* @wait: Wait queue for callers
* @adap: Kernel adapter representation
* @tx_msg: Messages from above to be sent
* @lock: Mutual exclusion
* @tx_pos: Current pos in TX message
* @nmsgs: Number of messages in tx_msg
* @state: See STATE_
* @rx_msg: Current RX message
* @rx_pos: Position within current RX message
* @dev: Pointer to device structure
* @base: Memory base of the HW registers
* @wait: Wait queue for callers
* @adap: Kernel adapter representation
* @tx_msg: Messages from above to be sent
* @lock: Mutual exclusion
* @tx_pos: Current pos in TX message
* @nmsgs: Number of messages in tx_msg
* @rx_msg: Current RX message
* @rx_pos: Position within current RX message
* @endianness: big/little-endian byte order
* @clk: Pointer to AXI4-lite input clock
* @clk: Pointer to AXI4-lite input clock
* @state: See STATE_
* @singlemaster: Indicates bus is single master
*/
struct xiic_i2c {
struct device *dev;
void __iomem *base;
wait_queue_head_t wait;
struct i2c_adapter adap;
struct i2c_msg *tx_msg;
struct mutex lock;
unsigned int tx_pos;
unsigned int nmsgs;
enum xilinx_i2c_state state;
struct i2c_msg *rx_msg;
int rx_pos;
enum xiic_endian endianness;
struct device *dev;
void __iomem *base;
wait_queue_head_t wait;
struct i2c_adapter adap;
struct i2c_msg *tx_msg;
struct mutex lock;
unsigned int tx_pos;
unsigned int nmsgs;
struct i2c_msg *rx_msg;
int rx_pos;
enum xiic_endian endianness;
struct clk *clk;
enum xilinx_i2c_state state;
bool singlemaster;
};
@@ -526,6 +528,15 @@ static int xiic_busy(struct xiic_i2c *i2c)
if (i2c->tx_msg)
return -EBUSY;
/* In single master mode bus can only be busy, when in use by this
* driver. If the register indicates bus being busy for some reason we
* should ignore it, since bus will never be released and i2c will be
* stuck forever.
*/
if (i2c->singlemaster) {
return 0;
}
/* for instance if previous transfer was terminated due to TX error
* it might be that the bus is on it's way to become available
* give it at most 3 ms to wake
@@ -811,6 +822,9 @@ static int xiic_i2c_probe(struct platform_device *pdev)
goto err_clk_dis;
}
i2c->singlemaster =
of_property_read_bool(pdev->dev.of_node, "single-master");
/*
* Detect endianness
* Try to reset the TX FIFO. Then check the EMPTY flag. If it is not

View File

@@ -0,0 +1,175 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* I2C slave mode testunit
*
* Copyright (C) 2020 by Wolfram Sang, Sang Engineering <wsa@sang-engineering.com>
* Copyright (C) 2020 by Renesas Electronics Corporation
*/
#include <linux/bitops.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/workqueue.h> /* FIXME: is system_long_wq the best choice? */
#define TU_CUR_VERSION 0x01
enum testunit_cmds {
TU_CMD_READ_BYTES = 1, /* save 0 for ABORT, RESET or similar */
TU_CMD_HOST_NOTIFY,
TU_NUM_CMDS
};
enum testunit_regs {
TU_REG_CMD,
TU_REG_DATAL,
TU_REG_DATAH,
TU_REG_DELAY,
TU_NUM_REGS
};
enum testunit_flags {
TU_FLAG_IN_PROCESS,
};
struct testunit_data {
unsigned long flags;
u8 regs[TU_NUM_REGS];
u8 reg_idx;
struct i2c_client *client;
struct delayed_work worker;
};
static void i2c_slave_testunit_work(struct work_struct *work)
{
struct testunit_data *tu = container_of(work, struct testunit_data, worker.work);
struct i2c_msg msg;
u8 msgbuf[256];
int ret = 0;
msg.addr = I2C_CLIENT_END;
msg.buf = msgbuf;
switch (tu->regs[TU_REG_CMD]) {
case TU_CMD_READ_BYTES:
msg.addr = tu->regs[TU_REG_DATAL];
msg.flags = I2C_M_RD;
msg.len = tu->regs[TU_REG_DATAH];
break;
case TU_CMD_HOST_NOTIFY:
msg.addr = 0x08;
msg.flags = 0;
msg.len = 3;
msgbuf[0] = tu->client->addr;
msgbuf[1] = tu->regs[TU_REG_DATAL];
msgbuf[2] = tu->regs[TU_REG_DATAH];
break;
default:
break;
}
if (msg.addr != I2C_CLIENT_END) {
ret = i2c_transfer(tu->client->adapter, &msg, 1);
/* convert '0 msgs transferred' to errno */
ret = (ret == 0) ? -EIO : ret;
}
if (ret < 0)
dev_err(&tu->client->dev, "CMD%02X failed (%d)\n", tu->regs[TU_REG_CMD], ret);
clear_bit(TU_FLAG_IN_PROCESS, &tu->flags);
}
static int i2c_slave_testunit_slave_cb(struct i2c_client *client,
enum i2c_slave_event event, u8 *val)
{
struct testunit_data *tu = i2c_get_clientdata(client);
int ret = 0;
switch (event) {
case I2C_SLAVE_WRITE_RECEIVED:
if (test_bit(TU_FLAG_IN_PROCESS, &tu->flags))
return -EBUSY;
if (tu->reg_idx < TU_NUM_REGS)
tu->regs[tu->reg_idx] = *val;
else
ret = -EMSGSIZE;
if (tu->reg_idx <= TU_NUM_REGS)
tu->reg_idx++;
/* TU_REG_CMD always written at this point */
if (tu->regs[TU_REG_CMD] >= TU_NUM_CMDS)
ret = -EINVAL;
break;
case I2C_SLAVE_STOP:
if (tu->reg_idx == TU_NUM_REGS) {
set_bit(TU_FLAG_IN_PROCESS, &tu->flags);
queue_delayed_work(system_long_wq, &tu->worker,
msecs_to_jiffies(10 * tu->regs[TU_REG_DELAY]));
}
fallthrough;
case I2C_SLAVE_WRITE_REQUESTED:
tu->reg_idx = 0;
break;
case I2C_SLAVE_READ_REQUESTED:
case I2C_SLAVE_READ_PROCESSED:
*val = TU_CUR_VERSION;
break;
}
return ret;
}
static int i2c_slave_testunit_probe(struct i2c_client *client)
{
struct testunit_data *tu;
tu = devm_kzalloc(&client->dev, sizeof(struct testunit_data), GFP_KERNEL);
if (!tu)
return -ENOMEM;
tu->client = client;
i2c_set_clientdata(client, tu);
INIT_DELAYED_WORK(&tu->worker, i2c_slave_testunit_work);
return i2c_slave_register(client, i2c_slave_testunit_slave_cb);
};
static int i2c_slave_testunit_remove(struct i2c_client *client)
{
struct testunit_data *tu = i2c_get_clientdata(client);
cancel_delayed_work_sync(&tu->worker);
i2c_slave_unregister(client);
return 0;
}
static const struct i2c_device_id i2c_slave_testunit_id[] = {
{ "slave-testunit", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, i2c_slave_testunit_id);
static struct i2c_driver i2c_slave_testunit_driver = {
.driver = {
.name = "i2c-slave-testunit",
},
.probe_new = i2c_slave_testunit_probe,
.remove = i2c_slave_testunit_remove,
.id_table = i2c_slave_testunit_id,
};
module_i2c_driver(i2c_slave_testunit_driver);
MODULE_AUTHOR("Wolfram Sang <wsa@sang-engineering.com>");
MODULE_DESCRIPTION("I2C slave mode test unit");
MODULE_LICENSE("GPL v2");

View File

@@ -197,6 +197,113 @@ EXPORT_SYMBOL_GPL(i2c_handle_smbus_alert);
module_i2c_driver(smbalert_driver);
#if IS_ENABLED(CONFIG_I2C_SLAVE)
#define SMBUS_HOST_NOTIFY_LEN 3
struct i2c_slave_host_notify_status {
u8 index;
u8 addr;
};
static int i2c_slave_host_notify_cb(struct i2c_client *client,
enum i2c_slave_event event, u8 *val)
{
struct i2c_slave_host_notify_status *status = client->dev.platform_data;
switch (event) {
case I2C_SLAVE_WRITE_RECEIVED:
/* We only retrieve the first byte received (addr)
* since there is currently no support to retrieve the data
* parameter from the client.
*/
if (status->index == 0)
status->addr = *val;
if (status->index < U8_MAX)
status->index++;
break;
case I2C_SLAVE_STOP:
if (status->index == SMBUS_HOST_NOTIFY_LEN)
i2c_handle_smbus_host_notify(client->adapter,
status->addr);
fallthrough;
case I2C_SLAVE_WRITE_REQUESTED:
status->index = 0;
break;
case I2C_SLAVE_READ_REQUESTED:
case I2C_SLAVE_READ_PROCESSED:
*val = 0xff;
break;
}
return 0;
}
/**
* i2c_new_slave_host_notify_device - get a client for SMBus host-notify support
* @adapter: the target adapter
* Context: can sleep
*
* Setup handling of the SMBus host-notify protocol on a given I2C bus segment.
*
* Handling is done by creating a device and its callback and handling data
* received via the SMBus host-notify address (0x8)
*
* This returns the client, which should be ultimately freed using
* i2c_free_slave_host_notify_device(); or an ERRPTR to indicate an error.
*/
struct i2c_client *i2c_new_slave_host_notify_device(struct i2c_adapter *adapter)
{
struct i2c_board_info host_notify_board_info = {
I2C_BOARD_INFO("smbus_host_notify", 0x08),
.flags = I2C_CLIENT_SLAVE,
};
struct i2c_slave_host_notify_status *status;
struct i2c_client *client;
int ret;
status = kzalloc(sizeof(struct i2c_slave_host_notify_status),
GFP_KERNEL);
if (!status)
return ERR_PTR(-ENOMEM);
host_notify_board_info.platform_data = status;
client = i2c_new_client_device(adapter, &host_notify_board_info);
if (IS_ERR(client)) {
kfree(status);
return client;
}
ret = i2c_slave_register(client, i2c_slave_host_notify_cb);
if (ret) {
i2c_unregister_device(client);
kfree(status);
return ERR_PTR(ret);
}
return client;
}
EXPORT_SYMBOL_GPL(i2c_new_slave_host_notify_device);
/**
* i2c_free_slave_host_notify_device - free the client for SMBus host-notify
* support
* @client: the client to free
* Context: can sleep
*
* Free the i2c_client allocated via i2c_new_slave_host_notify_device
*/
void i2c_free_slave_host_notify_device(struct i2c_client *client)
{
if (IS_ERR_OR_NULL(client))
return;
i2c_slave_unregister(client);
kfree(client->dev.platform_data);
i2c_unregister_device(client);
}
EXPORT_SYMBOL_GPL(i2c_free_slave_host_notify_device);
#endif
/*
* SPD is not part of SMBus but we include it here for convenience as the
* target systems are the same.

View File

@@ -85,18 +85,14 @@ static int i2c_mux_probe(struct platform_device *pdev)
return -ENOMEM;
mux->control = devm_mux_control_get(dev, NULL);
if (IS_ERR(mux->control)) {
if (PTR_ERR(mux->control) != -EPROBE_DEFER)
dev_err(dev, "failed to get control-mux\n");
return PTR_ERR(mux->control);
}
if (IS_ERR(mux->control))
return dev_err_probe(dev, PTR_ERR(mux->control),
"failed to get control-mux\n");
parent = mux_parent_adapter(dev);
if (IS_ERR(parent)) {
if (PTR_ERR(parent) != -EPROBE_DEFER)
dev_err(dev, "failed to get i2c-parent adapter\n");
return PTR_ERR(parent);
}
if (IS_ERR(parent))
return dev_err_probe(dev, PTR_ERR(parent),
"failed to get i2c-parent adapter\n");
children = of_get_child_count(np);

View File

@@ -171,13 +171,9 @@ static int i2c_mux_reg_probe(struct platform_device *pdev)
sizeof(mux->data));
} else {
ret = i2c_mux_reg_probe_dt(mux, pdev);
if (ret == -EPROBE_DEFER)
return ret;
if (ret < 0) {
dev_err(&pdev->dev, "Error parsing device tree");
return ret;
}
if (ret < 0)
return dev_err_probe(&pdev->dev, ret,
"Error parsing device tree");
}
parent = i2c_get_adapter(mux->data.parent);