summaryrefslogtreecommitdiffstats
path: root/drivers/i2c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2021-07-04 11:47:18 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2021-07-04 11:47:18 -0700
commit855ff900b8605df2b9ea309534cd2f02dc0c4cb8 (patch)
treec6f38bbb17d8702c7a692b383be94a59d451922b /drivers/i2c
parentd2500a0c0e73d4387cde9185edcdf397f52e428b (diff)
parent9d6336831bdc78e5207eaf147cc17228b5e984c3 (diff)
downloadlinux-855ff900b8605df2b9ea309534cd2f02dc0c4cb8.tar.bz2
Merge branch 'i2c/for-mergewindow' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang: - core supports now bus regulators controlling power for SCL/SDA - quite some DT binding conversions to YAML - added a seperate DT binding for the optional SMBus Alert feature - documentation with examples how to deal with I2C sysfs files - some bigger rework for the i801 driver - and a few usual driver updates * 'i2c/for-mergewindow' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (42 commits) i2c: ali1535: mention that the device should not be disabled i2c: mpc: Restore reread of I2C status register i2c: core-smbus: Expose PEC calculate function for generic use Documentation: i2c: Add doc for I2C sysfs i2c: core: Disable client irq on reboot/shutdown dt-bindings: i2c: update bindings for MT8195 SoC i2c: imx: Fix some checkpatch warnings i2c: davinci: Simplify with dev_err_probe() i2c: cadence: Simplify with dev_err_probe() i2c: xiic: Simplify with dev_err_probe() i2c: cadence: Clear HOLD bit before xfer_size register rolls over dt-bindings: i2c: ce4100: Replace "ti,pcf8575" by "nxp,pcf8575" i2c: i801: Improve i801_setup_hstcfg i2c: i801: Use driver name constant instead of function dev_driver_string i2c: i801: Simplify initialization of i2c_board_info in i801_probe_optional_slaves i2c: i801: Improve status polling i2c: cht-wc: Replace of_node by NULL i2c: riic: Add RZ/G2L support dt-bindings: i2c: renesas,riic: Document RZ/G2L I2C controller dt-bindings: i2c: renesas,iic: Convert to json-schema ...
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/busses/i2c-ali1535.c5
-rw-r--r--drivers/i2c/busses/i2c-aspeed.c12
-rw-r--r--drivers/i2c/busses/i2c-cadence.c57
-rw-r--r--drivers/i2c/busses/i2c-cht-wc.c3
-rw-r--r--drivers/i2c/busses/i2c-davinci.c5
-rw-r--r--drivers/i2c/busses/i2c-i801.c136
-rw-r--r--drivers/i2c/busses/i2c-imx.c19
-rw-r--r--drivers/i2c/busses/i2c-mpc.c2
-rw-r--r--drivers/i2c/busses/i2c-mt65xx.c9
-rw-r--r--drivers/i2c/busses/i2c-qcom-cci.c1
-rw-r--r--drivers/i2c/busses/i2c-rcar.c1
-rw-r--r--drivers/i2c/busses/i2c-riic.c23
-rw-r--r--drivers/i2c/busses/i2c-stm32f7.c73
-rw-r--r--drivers/i2c/busses/i2c-xiic.c9
-rw-r--r--drivers/i2c/i2c-core-base.c108
-rw-r--r--drivers/i2c/i2c-core-smbus.c12
16 files changed, 341 insertions, 134 deletions
diff --git a/drivers/i2c/busses/i2c-ali1535.c b/drivers/i2c/busses/i2c-ali1535.c
index fb93152845f4..ee83c4581bce 100644
--- a/drivers/i2c/busses/i2c-ali1535.c
+++ b/drivers/i2c/busses/i2c-ali1535.c
@@ -508,6 +508,11 @@ static void ali1535_remove(struct pci_dev *dev)
{
i2c_del_adapter(&ali1535_adapter);
release_region(ali1535_smba, ALI1535_SMB_IOSIZE);
+
+ /*
+ * do not call pci_disable_device(dev) since it can cause hard hangs on
+ * some systems during power-off
+ */
}
static struct pci_driver ali1535_driver = {
diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
index 724bf30600d6..67e8b97c0c95 100644
--- a/drivers/i2c/busses/i2c-aspeed.c
+++ b/drivers/i2c/busses/i2c-aspeed.c
@@ -727,10 +727,14 @@ static void __aspeed_i2c_reg_slave(struct aspeed_i2c_bus *bus, u16 slave_addr)
{
u32 addr_reg_val, func_ctrl_reg_val;
- /* Set slave addr. */
- addr_reg_val = readl(bus->base + ASPEED_I2C_DEV_ADDR_REG);
- addr_reg_val &= ~ASPEED_I2CD_DEV_ADDR_MASK;
- addr_reg_val |= slave_addr & ASPEED_I2CD_DEV_ADDR_MASK;
+ /*
+ * Set slave addr. Reserved bits can all safely be written with zeros
+ * on all of ast2[456]00, so zero everything else to ensure we only
+ * enable a single slave address (ast2500 has two, ast2600 has three,
+ * the enable bits for which are also in this register) so that we don't
+ * end up with additional phantom devices responding on the bus.
+ */
+ addr_reg_val = slave_addr & ASPEED_I2CD_DEV_ADDR_MASK;
writel(addr_reg_val, bus->base + ASPEED_I2C_DEV_ADDR_REG);
/* Turn on slave mode. */
diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c
index 66aafa7d1123..20aa3398e642 100644
--- a/drivers/i2c/busses/i2c-cadence.c
+++ b/drivers/i2c/busses/i2c-cadence.c
@@ -578,6 +578,11 @@ static void cdns_i2c_mrecv(struct cdns_i2c *id)
{
unsigned int ctrl_reg;
unsigned int isr_status;
+ unsigned long flags;
+ bool hold_clear = false;
+ bool irq_save = false;
+
+ u32 addr;
id->p_recv_buf = id->p_msg->buf;
id->recv_count = id->p_msg->len;
@@ -618,14 +623,43 @@ static void cdns_i2c_mrecv(struct cdns_i2c *id)
cdns_i2c_writereg(id->recv_count, CDNS_I2C_XFER_SIZE_OFFSET);
}
- /* Set the slave address in address register - triggers operation */
- cdns_i2c_writereg(id->p_msg->addr & CDNS_I2C_ADDR_MASK,
- CDNS_I2C_ADDR_OFFSET);
- /* Clear the bus hold flag if bytes to receive is less than FIFO size */
+ /* Determine hold_clear based on number of bytes to receive and hold flag */
if (!id->bus_hold_flag &&
- ((id->p_msg->flags & I2C_M_RECV_LEN) != I2C_M_RECV_LEN) &&
- (id->recv_count <= CDNS_I2C_FIFO_DEPTH))
- cdns_i2c_clear_bus_hold(id);
+ ((id->p_msg->flags & I2C_M_RECV_LEN) != I2C_M_RECV_LEN) &&
+ (id->recv_count <= CDNS_I2C_FIFO_DEPTH)) {
+ if (cdns_i2c_readreg(CDNS_I2C_CR_OFFSET) & CDNS_I2C_CR_HOLD) {
+ hold_clear = true;
+ if (id->quirks & CDNS_I2C_BROKEN_HOLD_BIT)
+ irq_save = true;
+ }
+ }
+
+ addr = id->p_msg->addr;
+ addr &= CDNS_I2C_ADDR_MASK;
+
+ if (hold_clear) {
+ ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET) & ~CDNS_I2C_CR_HOLD;
+ /*
+ * In case of Xilinx Zynq SOC, clear the HOLD bit before transfer size
+ * register reaches '0'. This is an IP bug which causes transfer size
+ * register overflow to 0xFF. To satisfy this timing requirement,
+ * disable the interrupts on current processor core between register
+ * writes to slave address register and control register.
+ */
+ if (irq_save)
+ local_irq_save(flags);
+
+ cdns_i2c_writereg(addr, CDNS_I2C_ADDR_OFFSET);
+ cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET);
+ /* Read it back to avoid bufferring and make sure write happens */
+ cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
+
+ if (irq_save)
+ local_irq_restore(flags);
+ } else {
+ cdns_i2c_writereg(addr, CDNS_I2C_ADDR_OFFSET);
+ }
+
cdns_i2c_writereg(CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET);
}
@@ -1217,11 +1251,10 @@ static int cdns_i2c_probe(struct platform_device *pdev)
"Cadence I2C at %08lx", (unsigned long)r_mem->start);
id->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(id->clk)) {
- if (PTR_ERR(id->clk) != -EPROBE_DEFER)
- dev_err(&pdev->dev, "input clock not found.\n");
- return PTR_ERR(id->clk);
- }
+ if (IS_ERR(id->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(id->clk),
+ "input clock not found.\n");
+
ret = clk_prepare_enable(id->clk);
if (ret)
dev_err(&pdev->dev, "Unable to enable clock.\n");
diff --git a/drivers/i2c/busses/i2c-cht-wc.c b/drivers/i2c/busses/i2c-cht-wc.c
index 08f491ea21ac..1cf68f85b2e1 100644
--- a/drivers/i2c/busses/i2c-cht-wc.c
+++ b/drivers/i2c/busses/i2c-cht-wc.c
@@ -354,8 +354,7 @@ static int cht_wc_i2c_adap_i2c_probe(struct platform_device *pdev)
return ret;
/* Alloc and register client IRQ */
- adap->irq_domain = irq_domain_add_linear(pdev->dev.of_node, 1,
- &irq_domain_simple_ops, NULL);
+ adap->irq_domain = irq_domain_add_linear(NULL, 1, &irq_domain_simple_ops, NULL);
if (!adap->irq_domain)
return -ENOMEM;
diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c
index 232a7679b69b..e9d07323c604 100644
--- a/drivers/i2c/busses/i2c-davinci.c
+++ b/drivers/i2c/busses/i2c-davinci.c
@@ -768,10 +768,7 @@ static int davinci_i2c_probe(struct platform_device *pdev)
if (irq <= 0) {
if (!irq)
irq = -ENXIO;
- if (irq != -EPROBE_DEFER)
- dev_err(&pdev->dev,
- "can't get irq resource ret=%d\n", irq);
- return irq;
+ return dev_err_probe(&pdev->dev, irq, "can't get irq resource\n");
}
dev = devm_kzalloc(&pdev->dev, sizeof(struct davinci_i2c_dev),
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index 04a1e38f2a6f..aa3f60e69230 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -88,6 +88,8 @@
* See the file Documentation/i2c/busses/i2c-i801.rst for details.
*/
+#define DRV_NAME "i801_smbus"
+
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/pci.h>
@@ -103,7 +105,7 @@
#include <linux/dmi.h>
#include <linux/slab.h>
#include <linux/string.h>
-#include <linux/wait.h>
+#include <linux/completion.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/platform_data/itco_wdt.h>
@@ -131,8 +133,6 @@
/* PCI Address Constants */
#define SMBBAR 4
-#define SMBPCICTL 0x004
-#define SMBPCISTS 0x006
#define SMBHSTCFG 0x040
#define TCOBASE 0x050
#define TCOCTL 0x054
@@ -141,12 +141,6 @@
#define SBREG_SMBCTRL 0xc6000c
#define SBREG_SMBCTRL_DNV 0xcf000c
-/* Host status bits for SMBPCISTS */
-#define SMBPCISTS_INTS BIT(3)
-
-/* Control bits for SMBPCICTL */
-#define SMBPCICTL_INTDIS BIT(10)
-
/* Host configuration bits for SMBHSTCFG */
#define SMBHSTCFG_HST_EN BIT(0)
#define SMBHSTCFG_SMB_SMI_EN BIT(1)
@@ -164,9 +158,6 @@
#define SMBAUXCTL_CRC BIT(0)
#define SMBAUXCTL_E32B BIT(1)
-/* Other settings */
-#define MAX_RETRIES 400
-
/* I801 command constants */
#define I801_QUICK 0x00
#define I801_BYTE 0x04
@@ -270,7 +261,7 @@ struct i801_priv {
unsigned int features;
/* isr processing */
- wait_queue_head_t waitq;
+ struct completion done;
u8 status;
/* Command state used by isr for byte-by-byte block transactions */
@@ -453,67 +444,53 @@ static int i801_check_post(struct i801_priv *priv, int status)
/* Wait for BUSY being cleared and either INTR or an error flag being set */
static int i801_wait_intr(struct i801_priv *priv)
{
- int timeout = 0;
- int status;
+ unsigned long timeout = jiffies + priv->adapter.timeout;
+ int status, busy;
- /* We will always wait for a fraction of a second! */
do {
usleep_range(250, 500);
status = inb_p(SMBHSTSTS(priv));
- } while (((status & SMBHSTSTS_HOST_BUSY) ||
- !(status & (STATUS_ERROR_FLAGS | SMBHSTSTS_INTR))) &&
- (timeout++ < MAX_RETRIES));
+ busy = status & SMBHSTSTS_HOST_BUSY;
+ status &= STATUS_ERROR_FLAGS | SMBHSTSTS_INTR;
+ if (!busy && status)
+ return status;
+ } while (time_is_after_eq_jiffies(timeout));
- if (timeout > MAX_RETRIES) {
- dev_dbg(&priv->pci_dev->dev, "INTR Timeout!\n");
- return -ETIMEDOUT;
- }
- return status & (STATUS_ERROR_FLAGS | SMBHSTSTS_INTR);
+ return -ETIMEDOUT;
}
/* Wait for either BYTE_DONE or an error flag being set */
static int i801_wait_byte_done(struct i801_priv *priv)
{
- int timeout = 0;
+ unsigned long timeout = jiffies + priv->adapter.timeout;
int status;
- /* We will always wait for a fraction of a second! */
do {
usleep_range(250, 500);
status = inb_p(SMBHSTSTS(priv));
- } while (!(status & (STATUS_ERROR_FLAGS | SMBHSTSTS_BYTE_DONE)) &&
- (timeout++ < MAX_RETRIES));
+ if (status & (STATUS_ERROR_FLAGS | SMBHSTSTS_BYTE_DONE))
+ return status & STATUS_ERROR_FLAGS;
+ } while (time_is_after_eq_jiffies(timeout));
- if (timeout > MAX_RETRIES) {
- dev_dbg(&priv->pci_dev->dev, "BYTE_DONE Timeout!\n");
- return -ETIMEDOUT;
- }
- return status & STATUS_ERROR_FLAGS;
+ return -ETIMEDOUT;
}
static int i801_transaction(struct i801_priv *priv, int xact)
{
int status;
- int result;
+ unsigned long result;
const struct i2c_adapter *adap = &priv->adapter;
- result = i801_check_pre(priv);
- if (result < 0)
- return result;
+ status = i801_check_pre(priv);
+ if (status < 0)
+ return status;
if (priv->features & FEATURE_IRQ) {
+ reinit_completion(&priv->done);
outb_p(xact | SMBHSTCNT_INTREN | SMBHSTCNT_START,
SMBHSTCNT(priv));
- result = wait_event_timeout(priv->waitq,
- (status = priv->status),
- adap->timeout);
- if (!result) {
- status = -ETIMEDOUT;
- dev_warn(&priv->pci_dev->dev,
- "Timeout waiting for interrupt!\n");
- }
- priv->status = 0;
- return i801_check_post(priv, status);
+ result = wait_for_completion_timeout(&priv->done, adap->timeout);
+ return i801_check_post(priv, result ? priv->status : -ETIMEDOUT);
}
/* the current contents of SMBHSTCNT can be overwritten, since PEC,
@@ -638,7 +615,7 @@ static irqreturn_t i801_host_notify_isr(struct i801_priv *priv)
* DEV_ERR - Invalid command, NAK or communication timeout
* BUS_ERR - SMI# transaction collision
* FAILED - transaction was canceled due to a KILL request
- * When any of these occur, update ->status and wake up the waitq.
+ * When any of these occur, update ->status and signal completion.
* ->status must be cleared before kicking off the next transaction.
*
* 2) For byte-by-byte (I2C read/write) transactions, one BYTE_DONE interrupt
@@ -653,8 +630,8 @@ static irqreturn_t i801_isr(int irq, void *dev_id)
u8 status;
/* Confirm this is our interrupt */
- pci_read_config_word(priv->pci_dev, SMBPCISTS, &pcists);
- if (!(pcists & SMBPCISTS_INTS))
+ pci_read_config_word(priv->pci_dev, PCI_STATUS, &pcists);
+ if (!(pcists & PCI_STATUS_INTERRUPT))
return IRQ_NONE;
if (priv->features & FEATURE_HOST_NOTIFY) {
@@ -675,7 +652,7 @@ static irqreturn_t i801_isr(int irq, void *dev_id)
if (status) {
outb_p(status, SMBHSTSTS(priv));
priv->status = status;
- wake_up(&priv->waitq);
+ complete(&priv->done);
}
return IRQ_HANDLED;
@@ -694,15 +671,15 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
int i, len;
int smbcmd;
int status;
- int result;
+ unsigned long result;
const struct i2c_adapter *adap = &priv->adapter;
if (command == I2C_SMBUS_BLOCK_PROC_CALL)
return -EOPNOTSUPP;
- result = i801_check_pre(priv);
- if (result < 0)
- return result;
+ status = i801_check_pre(priv);
+ if (status < 0)
+ return status;
len = data->block[0];
@@ -726,17 +703,10 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
priv->count = 0;
priv->data = &data->block[1];
+ reinit_completion(&priv->done);
outb_p(priv->cmd | SMBHSTCNT_START, SMBHSTCNT(priv));
- result = wait_event_timeout(priv->waitq,
- (status = priv->status),
- adap->timeout);
- if (!result) {
- status = -ETIMEDOUT;
- dev_warn(&priv->pci_dev->dev,
- "Timeout waiting for interrupt!\n");
- }
- priv->status = 0;
- return i801_check_post(priv, status);
+ result = wait_for_completion_timeout(&priv->done, adap->timeout);
+ return i801_check_post(priv, result ? priv->status : -ETIMEDOUT);
}
for (i = 1; i <= len; i++) {
@@ -1322,11 +1292,11 @@ static void i801_probe_optional_slaves(struct i801_priv *priv)
return;
if (apanel_addr) {
- struct i2c_board_info info;
+ struct i2c_board_info info = {
+ .addr = apanel_addr,
+ .type = "fujitsu_apanel",
+ };
- memset(&info, 0, sizeof(struct i2c_board_info));
- info.addr = apanel_addr;
- strlcpy(info.type, "fujitsu_apanel", I2C_NAME_SIZE);
i2c_new_client_device(&priv->adapter, &info);
}
@@ -1715,19 +1685,17 @@ static inline int i801_acpi_probe(struct i801_priv *priv) { return 0; }
static inline void i801_acpi_remove(struct i801_priv *priv) { }
#endif
-static unsigned char i801_setup_hstcfg(struct i801_priv *priv)
+static void i801_setup_hstcfg(struct i801_priv *priv)
{
unsigned char hstcfg = priv->original_hstcfg;
hstcfg &= ~SMBHSTCFG_I2C_EN; /* SMBus timing */
hstcfg |= SMBHSTCFG_HST_EN;
pci_write_config_byte(priv->pci_dev, SMBHSTCFG, hstcfg);
- return hstcfg;
}
static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
- unsigned char temp;
int err, i;
struct i801_priv *priv;
@@ -1838,8 +1806,7 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
if (i801_acpi_probe(priv))
return -ENODEV;
- err = pcim_iomap_regions(dev, 1 << SMBBAR,
- dev_driver_string(&dev->dev));
+ err = pcim_iomap_regions(dev, 1 << SMBBAR, DRV_NAME);
if (err) {
dev_err(&dev->dev,
"Failed to request SMBus region 0x%lx-0x%Lx\n",
@@ -1850,16 +1817,16 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
}
pci_read_config_byte(priv->pci_dev, SMBHSTCFG, &priv->original_hstcfg);
- temp = i801_setup_hstcfg(priv);
+ i801_setup_hstcfg(priv);
if (!(priv->original_hstcfg & SMBHSTCFG_HST_EN))
dev_info(&dev->dev, "Enabling SMBus device\n");
- if (temp & SMBHSTCFG_SMB_SMI_EN) {
+ if (priv->original_hstcfg & SMBHSTCFG_SMB_SMI_EN) {
dev_dbg(&dev->dev, "SMBus using interrupt SMI#\n");
/* Disable SMBus interrupt feature if SMBus using SMI# */
priv->features &= ~FEATURE_IRQ;
}
- if (temp & SMBHSTCFG_SPD_WD)
+ if (priv->original_hstcfg & SMBHSTCFG_SPD_WD)
dev_info(&dev->dev, "SPD Write Disable is set\n");
/* Clear special mode bits */
@@ -1881,24 +1848,23 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
u16 pcictl, pcists;
/* Complain if an interrupt is already pending */
- pci_read_config_word(priv->pci_dev, SMBPCISTS, &pcists);
- if (pcists & SMBPCISTS_INTS)
+ pci_read_config_word(priv->pci_dev, PCI_STATUS, &pcists);
+ if (pcists & PCI_STATUS_INTERRUPT)
dev_warn(&dev->dev, "An interrupt is pending!\n");
/* Check if interrupts have been disabled */
- pci_read_config_word(priv->pci_dev, SMBPCICTL, &pcictl);
- if (pcictl & SMBPCICTL_INTDIS) {
+ pci_read_config_word(priv->pci_dev, PCI_COMMAND, &pcictl);
+ if (pcictl & PCI_COMMAND_INTX_DISABLE) {
dev_info(&dev->dev, "Interrupts are disabled\n");
priv->features &= ~FEATURE_IRQ;
}
}
if (priv->features & FEATURE_IRQ) {
- init_waitqueue_head(&priv->waitq);
+ init_completion(&priv->done);
err = devm_request_irq(&dev->dev, dev->irq, i801_isr,
- IRQF_SHARED,
- dev_driver_string(&dev->dev), priv);
+ IRQF_SHARED, DRV_NAME, priv);
if (err) {
dev_err(&dev->dev, "Failed to allocate irq %d: %d\n",
dev->irq, err);
@@ -1988,7 +1954,7 @@ static int i801_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(i801_pm_ops, i801_suspend, i801_resume);
static struct pci_driver i801_driver = {
- .name = "i801_smbus",
+ .name = DRV_NAME,
.id_table = i801_ids,
.probe = i801_probe,
.remove = i801_remove,
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index dc5ca71906db..d5b5f084a27d 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -170,11 +170,11 @@ enum imx_i2c_type {
struct imx_i2c_hwdata {
enum imx_i2c_type devtype;
- unsigned regshift;
+ unsigned int regshift;
struct imx_i2c_clk_pair *clk_div;
- unsigned ndivs;
- unsigned i2sr_clr_opcode;
- unsigned i2cr_ien_opcode;
+ unsigned int ndivs;
+ unsigned int i2sr_clr_opcode;
+ unsigned int i2cr_ien_opcode;
};
struct imx_i2c_dma {
@@ -452,8 +452,6 @@ static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy, bool a
unsigned long orig_jiffies = jiffies;
unsigned int temp;
- dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
-
while (1) {
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
@@ -599,8 +597,6 @@ static int i2c_imx_start(struct imx_i2c_struct *i2c_imx, bool atomic)
unsigned int temp = 0;
int result;
- dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
-
imx_i2c_write_reg(i2c_imx->ifdr, i2c_imx, IMX_I2C_IFDR);
/* Enable I2C controller */
imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);
@@ -635,7 +631,6 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx, bool atomic)
if (!i2c_imx->stopped) {
/* Stop I2C transaction */
- dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
if (!(temp & I2CR_MSTA))
i2c_imx->stopped = 1;
@@ -1167,8 +1162,6 @@ static int i2c_imx_xfer_common(struct i2c_adapter *adapter,
bool is_lastmsg = false;
struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter);
- dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
-
/* Start I2C transfer */
result = i2c_imx_start(i2c_imx, atomic);
if (result) {
@@ -1371,8 +1364,6 @@ static int i2c_imx_probe(struct platform_device *pdev)
dma_addr_t phy_addr;
const struct imx_i2c_hwdata *match;
- dev_dbg(&pdev->dev, "<%s>\n", __func__);
-
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
@@ -1395,7 +1386,7 @@ static int i2c_imx_probe(struct platform_device *pdev)
platform_get_device_id(pdev)->driver_data;
/* Setup i2c_imx driver structure */
- strlcpy(i2c_imx->adapter.name, pdev->name, sizeof(i2c_imx->adapter.name));
+ strscpy(i2c_imx->adapter.name, pdev->name, sizeof(i2c_imx->adapter.name));
i2c_imx->adapter.owner = THIS_MODULE;
i2c_imx->adapter.algo = &i2c_imx_algo;
i2c_imx->adapter.dev.parent = &pdev->dev;
diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c
index dcca9c2396db..6d5014ebaab5 100644
--- a/drivers/i2c/busses/i2c-mpc.c
+++ b/drivers/i2c/busses/i2c-mpc.c
@@ -635,6 +635,8 @@ static irqreturn_t mpc_i2c_isr(int irq, void *dev_id)
status = readb(i2c->base + MPC_I2C_SR);
if (status & CSR_MIF) {
+ /* Read again to allow register to stabilise */
+ status = readb(i2c->base + MPC_I2C_SR);
writeb(0, i2c->base + MPC_I2C_SR);
mpc_i2c_do_intr(i2c, status);
return IRQ_HANDLED;
diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c
index 4e9fb6b44436..4ca716e09149 100644
--- a/drivers/i2c/busses/i2c-mt65xx.c
+++ b/drivers/i2c/busses/i2c-mt65xx.c
@@ -1225,6 +1225,13 @@ static int mtk_i2c_probe(struct platform_device *pdev)
i2c->adap.quirks = i2c->dev_comp->quirks;
i2c->adap.timeout = 2 * HZ;
i2c->adap.retries = 1;
+ i2c->adap.bus_regulator = devm_regulator_get_optional(&pdev->dev, "vbus");
+ if (IS_ERR(i2c->adap.bus_regulator)) {
+ if (PTR_ERR(i2c->adap.bus_regulator) == -ENODEV)
+ i2c->adap.bus_regulator = NULL;
+ else
+ return PTR_ERR(i2c->adap.bus_regulator);
+ }
ret = mtk_i2c_parse_dt(pdev->dev.of_node, i2c);
if (ret)
@@ -1286,7 +1293,7 @@ static int mtk_i2c_probe(struct platform_device *pdev)
ret = devm_request_irq(&pdev->dev, irq, mtk_i2c_irq,
IRQF_NO_SUSPEND | IRQF_TRIGGER_NONE,
- I2C_DRV_NAME, i2c);
+ dev_name(&pdev->dev), i2c);
if (ret < 0) {
dev_err(&pdev->dev,
"Request I2C IRQ %d fail\n", irq);
diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c
index c63d5545fc2a..c1de8eb66169 100644
--- a/drivers/i2c/busses/i2c-qcom-cci.c
+++ b/drivers/i2c/busses/i2c-qcom-cci.c
@@ -769,6 +769,7 @@ static const struct of_device_id cci_dt_match[] = {
{ .compatible = "qcom,msm8916-cci", .data = &cci_v1_data},
{ .compatible = "qcom,msm8996-cci", .data = &cci_v2_data},
{ .compatible = "qcom,sdm845-cci", .data = &cci_v2_data},
+ { .compatible = "qcom,sm8250-cci", .data = &cci_v2_data},
{}
};
MODULE_DEVICE_TABLE(of, cci_dt_match);
diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c
index 327c092a4130..bff9913c37b8 100644
--- a/drivers/i2c/busses/i2c-rcar.c
+++ b/drivers/i2c/busses/i2c-rcar.c
@@ -1013,7 +1013,6 @@ static const struct of_device_id rcar_i2c_dt_ids[] = {
{ .compatible = "renesas,i2c-r8a7794", .data = (void *)I2C_RCAR_GEN2 },
{ .compatible = "renesas,i2c-r8a7795", .data = (void *)I2C_RCAR_GEN3 },
{ .compatible = "renesas,i2c-r8a7796", .data = (void *)I2C_RCAR_GEN3 },
- { .compatible = "renesas,i2c-rcar", .data = (void *)I2C_RCAR_GEN1 }, /* Deprecated */
{ .compatible = "renesas,rcar-gen1-i2c", .data = (void *)I2C_RCAR_GEN1 },
{ .compatible = "renesas,rcar-gen2-i2c", .data = (void *)I2C_RCAR_GEN2 },
{ .compatible = "renesas,rcar-gen3-i2c", .data = (void *)I2C_RCAR_GEN3 },
diff --git a/drivers/i2c/busses/i2c-riic.c b/drivers/i2c/busses/i2c-riic.c
index 4eccc0f69861..78b84445ee6a 100644
--- a/drivers/i2c/busses/i2c-riic.c
+++ b/drivers/i2c/busses/i2c-riic.c
@@ -42,8 +42,10 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/reset.h>
#define RIIC_ICCR1 0x00
#define RIIC_ICCR2 0x04
@@ -86,6 +88,11 @@
#define RIIC_INIT_MSG -1
+enum riic_type {
+ RIIC_RZ_A,
+ RIIC_RZ_G2L,
+};
+
struct riic_dev {
void __iomem *base;
u8 *buf;
@@ -395,7 +402,9 @@ static int riic_i2c_probe(struct platform_device *pdev)
struct i2c_adapter *adap;
struct resource *res;
struct i2c_timings i2c_t;
+ struct reset_control *rstc;
int i, ret;
+ enum riic_type type;
riic = devm_kzalloc(&pdev->dev, sizeof(*riic), GFP_KERNEL);
if (!riic)
@@ -412,6 +421,17 @@ static int riic_i2c_probe(struct platform_device *pdev)
return PTR_ERR(riic->clk);
}
+ type = (enum riic_type)of_device_get_match_data(&pdev->dev);
+ if (type == RIIC_RZ_G2L) {
+ rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
+ if (IS_ERR(rstc)) {
+ dev_err(&pdev->dev, "Error: missing reset ctrl\n");
+ return PTR_ERR(rstc);
+ }
+
+ reset_control_deassert(rstc);
+ }
+
for (i = 0; i < ARRAY_SIZE(riic_irqs); i++) {
res = platform_get_resource(pdev, IORESOURCE_IRQ, riic_irqs[i].res_num);
if (!res)
@@ -472,7 +492,8 @@ static int riic_i2c_remove(struct platform_device *pdev)
}
static const struct of_device_id riic_i2c_dt_ids[] = {
- { .compatible = "renesas,riic-rz" },
+ { .compatible = "renesas,riic-r9a07g044", .data = (void *)RIIC_RZ_G2L },
+ { .compatible = "renesas,riic-rz", .data = (void *)RIIC_RZ_A },
{ /* Sentinel */ },
};
diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c
index 0138317ea600..b9b19a2a2ffa 100644
--- a/drivers/i2c/busses/i2c-stm32f7.c
+++ b/drivers/i2c/busses/i2c-stm32f7.c
@@ -51,6 +51,7 @@
/* STM32F7 I2C control 1 */
#define STM32F7_I2C_CR1_PECEN BIT(23)
+#define STM32F7_I2C_CR1_ALERTEN BIT(22)
#define STM32F7_I2C_CR1_SMBHEN BIT(20)
#define STM32F7_I2C_CR1_WUPEN BIT(18)
#define STM32F7_I2C_CR1_SBC BIT(16)
@@ -125,6 +126,7 @@
(((n) & STM32F7_I2C_ISR_ADDCODE_MASK) >> 17)
#define STM32F7_I2C_ISR_DIR BIT(16)
#define STM32F7_I2C_ISR_BUSY BIT(15)
+#define STM32F7_I2C_ISR_ALERT BIT(13)
#define STM32F7_I2C_ISR_PECERR BIT(11)
#define STM32F7_I2C_ISR_ARLO BIT(9)
#define STM32F7_I2C_ISR_BERR BIT(8)
@@ -138,6 +140,7 @@
#define STM32F7_I2C_ISR_TXE BIT(0)
/* STM32F7 I2C Interrupt Clear */
+#define STM32F7_I2C_ICR_ALERTCF BIT(13)
#define STM32F7_I2C_ICR_PECCF BIT(11)
#define STM32F7_I2C_ICR_ARLOCF BIT(9)
#define STM32F7_I2C_ICR_BERRCF BIT(8)
@@ -279,6 +282,17 @@ struct stm32f7_i2c_msg {
};
/**
+ * struct stm32f7_i2c_alert - SMBus alert specific data
+ * @setup: platform data for the smbus_alert i2c client
+ * @ara: I2C slave device used to respond to the SMBus Alert with Alert
+ * Response Address
+ */
+struct stm32f7_i2c_alert {
+ struct i2c_smbus_alert_setup setup;
+ struct i2c_client *ara;
+};
+
+/**
* struct stm32f7_i2c_dev - private data of the controller
* @adap: I2C adapter for this controller
* @dev: device for this controller
@@ -310,6 +324,7 @@ struct stm32f7_i2c_msg {
* @analog_filter: boolean to indicate enabling of the analog filter
* @dnf_dt: value of digital filter requested via dt
* @dnf: value of digital filter to apply
+ * @alert: SMBus alert specific data
*/
struct stm32f7_i2c_dev {
struct i2c_adapter adap;
@@ -341,6 +356,7 @@ struct stm32f7_i2c_dev {
bool analog_filter;
u32 dnf_dt;
u32 dnf;
+ struct stm32f7_i2c_alert *alert;
};
/*
@@ -1624,6 +1640,13 @@ static irqreturn_t stm32f7_i2c_isr_error(int irq, void *data)
f7_msg->result = -EINVAL;
}
+ if (status & STM32F7_I2C_ISR_ALERT) {
+ dev_dbg(dev, "<%s>: SMBus alert received\n", __func__);
+ writel_relaxed(STM32F7_I2C_ICR_ALERTCF, base + STM32F7_I2C_ICR);
+ i2c_handle_smbus_alert(i2c_dev->alert->ara);
+ return IRQ_HANDLED;
+ }
+
if (!i2c_dev->slave_running) {
u32 mask;
/* Disable interrupts */
@@ -1990,6 +2013,42 @@ static void stm32f7_i2c_disable_smbus_host(struct stm32f7_i2c_dev *i2c_dev)
}
}
+static int stm32f7_i2c_enable_smbus_alert(struct stm32f7_i2c_dev *i2c_dev)
+{
+ struct stm32f7_i2c_alert *alert;
+ struct i2c_adapter *adap = &i2c_dev->adap;
+ struct device *dev = i2c_dev->dev;
+ void __iomem *base = i2c_dev->base;
+
+ alert = devm_kzalloc(dev, sizeof(*alert), GFP_KERNEL);
+ if (!alert)
+ return -ENOMEM;
+
+ alert->ara = i2c_new_smbus_alert_device(adap, &alert->setup);
+ if (IS_ERR(alert->ara))
+ return PTR_ERR(alert->ara);
+
+ i2c_dev->alert = alert;
+
+ /* Enable SMBus Alert */
+ stm32f7_i2c_set_bits(base + STM32F7_I2C_CR1, STM32F7_I2C_CR1_ALERTEN);
+
+ return 0;
+}
+
+static void stm32f7_i2c_disable_smbus_alert(struct stm32f7_i2c_dev *i2c_dev)
+{
+ struct stm32f7_i2c_alert *alert = i2c_dev->alert;
+ void __iomem *base = i2c_dev->base;
+
+ if (alert) {
+ /* Disable SMBus Alert */
+ stm32f7_i2c_clr_bits(base + STM32F7_I2C_CR1,
+ STM32F7_I2C_CR1_ALERTEN);
+ i2c_unregister_device(alert->ara);
+ }
+}
+
static u32 stm32f7_i2c_func(struct i2c_adapter *adap)
{
struct stm32f7_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
@@ -2173,6 +2232,16 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
}
}
+ if (of_property_read_bool(pdev->dev.of_node, "smbus-alert")) {
+ ret = stm32f7_i2c_enable_smbus_alert(i2c_dev);
+ if (ret) {
+ dev_err(i2c_dev->dev,
+ "failed to enable SMBus alert protocol (%d)\n",
+ ret);
+ goto i2c_disable_smbus_host;
+ }
+ }
+
dev_info(i2c_dev->dev, "STM32F7 I2C-%d bus adapter\n", adap->nr);
pm_runtime_mark_last_busy(i2c_dev->dev);
@@ -2180,6 +2249,9 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
return 0;
+i2c_disable_smbus_host:
+ stm32f7_i2c_disable_smbus_host(i2c_dev);
+
i2c_adapter_remove:
i2c_del_adapter(adap);
@@ -2214,6 +2286,7 @@ static int stm32f7_i2c_remove(struct platform_device *pdev)
{
struct stm32f7_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
+ stm32f7_i2c_disable_smbus_alert(i2c_dev);
stm32f7_i2c_disable_smbus_host(i2c_dev);
i2c_del_adapter(&i2c_dev->adap);
diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c
index 2a8568b97c14..bb93db98404e 100644
--- a/drivers/i2c/busses/i2c-xiic.c
+++ b/drivers/i2c/busses/i2c-xiic.c
@@ -798,11 +798,10 @@ static int xiic_i2c_probe(struct platform_device *pdev)
init_waitqueue_head(&i2c->wait);
i2c->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(i2c->clk)) {
- if (PTR_ERR(i2c->clk) != -EPROBE_DEFER)
- dev_err(&pdev->dev, "input clock not found.\n");
- return PTR_ERR(i2c->clk);
- }
+ if (IS_ERR(i2c->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(i2c->clk),
+ "input clock not found.\n");
+
ret = clk_prepare_enable(i2c->clk);
if (ret) {
dev_err(&pdev->dev, "Unable to enable clock.\n");
diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
index 5a97e4a02fa2..84f12bf90644 100644
--- a/drivers/i2c/i2c-core-base.c
+++ b/drivers/i2c/i2c-core-base.c
@@ -24,6 +24,7 @@
#include <linux/i2c-smbus.h>
#include <linux/idr.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/irqflags.h>
#include <linux/jump_label.h>
#include <linux/kernel.h>
@@ -399,7 +400,8 @@ static int i2c_gpio_init_recovery(struct i2c_adapter *adap)
static int i2c_init_recovery(struct i2c_adapter *adap)
{
struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
- char *err_str, *err_level = KERN_ERR;
+ bool is_error_level = true;
+ char *err_str;
if (!bri)
return 0;
@@ -409,7 +411,7 @@ static int i2c_init_recovery(struct i2c_adapter *adap)
if (!bri->recover_bus) {
err_str = "no suitable method provided";
- err_level = KERN_DEBUG;
+ is_error_level = false;
goto err;
}
@@ -436,7 +438,10 @@ static int i2c_init_recovery(struct i2c_adapter *adap)
return 0;
err:
- dev_printk(err_level, &adap->dev, "Not using recovery: %s\n", err_str);
+ if (is_error_level)
+ dev_err(&adap->dev, "Not using recovery: %s\n", err_str);
+ else
+ dev_dbg(&adap->dev, "Not using recovery: %s\n", err_str);
adap->bus_recovery_info = NULL;
return -EINVAL;
@@ -461,12 +466,14 @@ static int i2c_smbus_host_notify_to_irq(const struct i2c_client *client)
static int i2c_device_probe(struct device *dev)
{
struct i2c_client *client = i2c_verify_client(dev);
+ struct i2c_adapter *adap;
struct i2c_driver *driver;
int status;
if (!client)
return 0;
+ adap = client->adapter;
client->irq = client->init_irq;
if (!client->irq) {
@@ -532,6 +539,14 @@ static int i2c_device_probe(struct device *dev)
dev_dbg(dev, "probe\n");
+ if (adap->bus_regulator) {
+ status = regulator_enable(adap->bus_regulator);
+ if (status < 0) {
+ dev_err(&adap->dev, "Failed to enable bus regulator\n");
+ goto err_clear_wakeup_irq;
+ }
+ }
+
status = of_clk_set_defaults(dev->of_node, false);
if (status < 0)
goto err_clear_wakeup_irq;
@@ -589,8 +604,10 @@ put_sync_adapter:
static int i2c_device_remove(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
+ struct i2c_adapter *adap;
struct i2c_driver *driver;
+ adap = client->adapter;
driver = to_i2c_driver(dev->driver);
if (driver->remove) {
int status;
@@ -605,6 +622,8 @@ static int i2c_device_remove(struct device *dev)
devres_release_group(&client->dev, client->devres_group_id);
dev_pm_domain_detach(&client->dev, true);
+ if (!pm_runtime_status_suspended(&client->dev) && adap->bus_regulator)
+ regulator_disable(adap->bus_regulator);
dev_pm_clear_wake_irq(&client->dev);
device_init_wakeup(&client->dev, false);
@@ -617,6 +636,86 @@ static int i2c_device_remove(struct device *dev)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int i2c_resume_early(struct device *dev)
+{
+ struct i2c_client *client = i2c_verify_client(dev);
+ int err;
+
+ if (!client)
+ return 0;
+
+ if (pm_runtime_status_suspended(&client->dev) &&
+ client->adapter->bus_regulator) {
+ err = regulator_enable(client->adapter->bus_regulator);
+ if (err)
+ return err;
+ }
+
+ return pm_generic_resume_early(&client->dev);
+}
+
+static int i2c_suspend_late(struct device *dev)
+{
+ struct i2c_client *client = i2c_verify_client(dev);
+ int err;
+
+ if (!client)
+ return 0;
+
+ err = pm_generic_suspend_late(&client->dev);
+ if (err)
+ return err;
+
+ if (!pm_runtime_status_suspended(&client->dev) &&
+ client->adapter->bus_regulator)
+ return regulator_disable(client->adapter->bus_regulator);
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_PM
+static int i2c_runtime_resume(struct device *dev)
+{
+ struct i2c_client *client = i2c_verify_client(dev);
+ int err;
+
+ if (!client)
+ return 0;
+
+ if (client->adapter->bus_regulator) {
+ err = regulator_enable(client->adapter->bus_regulator);
+ if (err)
+ return err;
+ }
+
+ return pm_generic_runtime_resume(&client->dev);
+}
+
+static int i2c_runtime_suspend(struct device *dev)
+{
+ struct i2c_client *client = i2c_verify_client(dev);
+ int err;
+
+ if (!client)
+ return 0;
+
+ err = pm_generic_runtime_suspend(&client->dev);
+ if (err)
+ return err;
+
+ if (client->adapter->bus_regulator)
+ return regulator_disable(client->adapter->bus_regulator);
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops i2c_device_pm = {
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(i2c_suspend_late, i2c_resume_early)
+ SET_RUNTIME_PM_OPS(i2c_runtime_suspend, i2c_runtime_resume, NULL)
+};
+
static void i2c_device_shutdown(struct device *dev)
{
struct i2c_client *client = i2c_verify_client(dev);
@@ -627,6 +726,8 @@ static void i2c_device_shutdown(struct device *dev)
driver = to_i2c_driver(dev->driver);
if (driver->shutdown)
driver->shutdown(client);
+ else if (client->irq > 0)
+ disable_irq(client->irq);
}
static void i2c_client_dev_release(struct device *dev)
@@ -674,6 +775,7 @@ struct bus_type i2c_bus_type = {
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
+ .pm = &i2c_device_pm,
};
EXPORT_SYMBOL_GPL(i2c_bus_type);
diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c
index d2d32c0fd8c3..e5b2d1465e7e 100644
--- a/drivers/i2c/i2c-core-smbus.c
+++ b/drivers/i2c/i2c-core-smbus.c
@@ -37,8 +37,15 @@ static u8 crc8(u16 data)
return (u8)(data >> 8);
}
-/* Incremental CRC8 over count bytes in the array pointed to by p */
-static u8 i2c_smbus_pec(u8 crc, u8 *p, size_t count)
+/**
+ * i2c_smbus_pec - Incremental CRC8 over the given input data array
+ * @crc: previous return crc8 value
+ * @p: pointer to data buffer.
+ * @count: number of bytes in data buffer.
+ *
+ * Incremental CRC8 over count bytes in the array pointed to by p
+ */
+u8 i2c_smbus_pec(u8 crc, u8 *p, size_t count)
{
int i;
@@ -46,6 +53,7 @@ static u8 i2c_smbus_pec(u8 crc, u8 *p, size_t count)
crc = crc8((crc ^ p[i]) << 8);
return crc;
}
+EXPORT_SYMBOL(i2c_smbus_pec);
/* Assume a 7-bit address, which is reasonable for SMBus */
static u8 i2c_smbus_msg_pec(u8 pec, struct i2c_msg *msg)