Browse Source

Merge tag 'for-v2020.07' of https://gitlab.denx.de/u-boot/custodians/u-boot-i2c

i2c changes for 2020.07
- add new i2c driver for Broadcom iproc-based socs
- fix cmd: eeprom: Staticize eeprom_i2c_bus
- i2c: muxes: pca954x: add PCA9546 variant
Tom Rini 4 years ago
parent
commit
1bf65142b3

+ 1 - 1
cmd/eeprom.c

@@ -61,7 +61,7 @@
 #endif
 #endif
 
 
 #if defined(CONFIG_DM_I2C)
 #if defined(CONFIG_DM_I2C)
-int eeprom_i2c_bus;
+static int eeprom_i2c_bus;
 #endif
 #endif
 
 
 __weak int eeprom_write_enable(unsigned dev_addr, int state)
 __weak int eeprom_write_enable(unsigned dev_addr, int state)

+ 8 - 0
drivers/i2c/Kconfig

@@ -71,6 +71,14 @@ config SYS_I2C_AT91
 	  i2c-gpio driver unless your system can cope with this limitation.
 	  i2c-gpio driver unless your system can cope with this limitation.
 	  Binding info: doc/device-tree-bindings/i2c/i2c-at91.txt
 	  Binding info: doc/device-tree-bindings/i2c/i2c-at91.txt
 
 
+config SYS_I2C_IPROC
+	bool "Broadcom I2C driver"
+	depends on DM_I2C
+	help
+	  Broadcom I2C driver.
+	  Add support for Broadcom I2C driver.
+	  Say yes here to to enable the Broadco I2C driver.
+
 config SYS_I2C_FSL
 config SYS_I2C_FSL
        bool "Freescale I2C bus driver"
        bool "Freescale I2C bus driver"
        depends on DM_I2C
        depends on DM_I2C

+ 1 - 0
drivers/i2c/Makefile

@@ -21,6 +21,7 @@ obj-$(CONFIG_SYS_I2C_FSL) += fsl_i2c.o
 obj-$(CONFIG_SYS_I2C_IHS) += ihs_i2c.o
 obj-$(CONFIG_SYS_I2C_IHS) += ihs_i2c.o
 obj-$(CONFIG_SYS_I2C_INTEL) += intel_i2c.o
 obj-$(CONFIG_SYS_I2C_INTEL) += intel_i2c.o
 obj-$(CONFIG_SYS_I2C_IMX_LPI2C) += imx_lpi2c.o
 obj-$(CONFIG_SYS_I2C_IMX_LPI2C) += imx_lpi2c.o
+obj-$(CONFIG_SYS_I2C_IPROC) += iproc_i2c.o
 obj-$(CONFIG_SYS_I2C_KONA) += kona_i2c.o
 obj-$(CONFIG_SYS_I2C_KONA) += kona_i2c.o
 obj-$(CONFIG_SYS_I2C_LPC32XX) += lpc32xx_i2c.o
 obj-$(CONFIG_SYS_I2C_LPC32XX) += lpc32xx_i2c.o
 obj-$(CONFIG_SYS_I2C_MESON) += meson_i2c.o
 obj-$(CONFIG_SYS_I2C_MESON) += meson_i2c.o

+ 713 - 0
drivers/i2c/iproc_i2c.c

@@ -0,0 +1,713 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Broadcom
+ *
+ */
+
+#include <asm/io.h>
+#include <common.h>
+#include <config.h>
+#include <dm.h>
+#include "errno.h"
+#include <i2c.h>
+#include "iproc_i2c.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct iproc_i2c_regs {
+	u32 cfg_reg;
+	u32 timg_cfg;
+	u32 addr_reg;
+	u32 mstr_fifo_ctrl;
+	u32 slv_fifo_ctrl;
+	u32 bitbng_ctrl;
+	u32 blnks[6]; /* Not to be used */
+	u32 mstr_cmd;
+	u32 slv_cmd;
+	u32 evt_en;
+	u32 evt_sts;
+	u32 mstr_datawr;
+	u32 mstr_datard;
+	u32 slv_datawr;
+	u32 slv_datard;
+};
+
+struct iproc_i2c {
+	struct iproc_i2c_regs __iomem *base; /* register base */
+	int bus_speed;
+	int i2c_init_done;
+};
+
+/* Function to read a value from specified register. */
+static unsigned int iproc_i2c_reg_read(u32 *reg_addr)
+{
+	unsigned int val;
+
+	val = readl((void *)(reg_addr));
+	return cpu_to_le32(val);
+}
+
+/* Function to write a value ('val') in to a specified register. */
+static int iproc_i2c_reg_write(u32 *reg_addr, unsigned int val)
+{
+	val = cpu_to_le32(val);
+	writel(val, (void *)(reg_addr));
+	return  0;
+}
+
+#if defined(DEBUG)
+static int iproc_dump_i2c_regs(struct iproc_i2c *bus_prvdata)
+{
+	struct iproc_i2c_regs *base = bus_prvdata->base;
+	unsigned int regval;
+
+	debug("\n----------------------------------------------\n");
+	debug("%s: Dumping SMBus registers...\n", __func__);
+
+	regval = iproc_i2c_reg_read(&base->cfg_reg);
+	debug("CCB_SMB_CFG_REG=0x%08X\n", regval);
+
+	regval = iproc_i2c_reg_read(&base->timg_cfg);
+	debug("CCB_SMB_TIMGCFG_REG=0x%08X\n", regval);
+
+	regval = iproc_i2c_reg_read(&base->addr_reg);
+	debug("CCB_SMB_ADDR_REG=0x%08X\n", regval);
+
+	regval = iproc_i2c_reg_read(&base->mstr_fifo_ctrl);
+	debug("CCB_SMB_MSTRFIFOCTL_REG=0x%08X\n", regval);
+
+	regval = iproc_i2c_reg_read(&base->slv_fifo_ctrl);
+	debug("CCB_SMB_SLVFIFOCTL_REG=0x%08X\n", regval);
+
+	regval = iproc_i2c_reg_read(&base->bitbng_ctrl);
+	debug("CCB_SMB_BITBANGCTL_REG=0x%08X\n", regval);
+
+	regval = iproc_i2c_reg_read(&base->mstr_cmd);
+	debug("CCB_SMB_MSTRCMD_REG=0x%08X\n", regval);
+
+	regval = iproc_i2c_reg_read(&base->slv_cmd);
+	debug("CCB_SMB_SLVCMD_REG=0x%08X\n", regval);
+
+	regval = iproc_i2c_reg_read(&base->evt_en);
+	debug("CCB_SMB_EVTEN_REG=0x%08X\n", regval);
+
+	regval = iproc_i2c_reg_read(&base->evt_sts);
+	debug("CCB_SMB_EVTSTS_REG=0x%08X\n", regval);
+
+	regval = iproc_i2c_reg_read(&base->mstr_datawr);
+	debug("CCB_SMB_MSTRDATAWR_REG=0x%08X\n", regval);
+
+	regval = iproc_i2c_reg_read(&base->mstr_datard);
+	debug("CCB_SMB_MSTRDATARD_REG=0x%08X\n", regval);
+
+	regval = iproc_i2c_reg_read(&base->slv_datawr);
+	debug("CCB_SMB_SLVDATAWR_REG=0x%08X\n", regval);
+
+	regval = iproc_i2c_reg_read(&base->slv_datard);
+	debug("CCB_SMB_SLVDATARD_REG=0x%08X\n", regval);
+
+	debug("----------------------------------------------\n\n");
+	return 0;
+}
+#else
+static int iproc_dump_i2c_regs(struct iproc_i2c *bus_prvdata)
+{
+	return 0;
+}
+#endif
+
+/*
+ * Function to ensure that the previous transaction was completed before
+ * initiating a new transaction. It can also be used in polling mode to
+ * check status of completion of a command
+ */
+static int iproc_i2c_startbusy_wait(struct iproc_i2c *bus_prvdata)
+{
+	struct iproc_i2c_regs *base = bus_prvdata->base;
+	unsigned int regval;
+
+	regval = iproc_i2c_reg_read(&base->mstr_cmd);
+
+	/* Check if an operation is in progress. During probe it won't be.
+	 * But when shutdown/remove was called we want to make sure that
+	 * the transaction in progress completed
+	 */
+	if (regval & CCB_SMB_MSTRSTARTBUSYCMD_MASK) {
+		unsigned int i = 0;
+
+		do {
+			mdelay(10);
+			i++;
+			regval = iproc_i2c_reg_read(&base->mstr_cmd);
+
+			/* If start-busy bit cleared, exit the loop */
+		} while ((regval & CCB_SMB_MSTRSTARTBUSYCMD_MASK) &&
+			 (i < IPROC_SMB_MAX_RETRIES));
+
+		if (i >= IPROC_SMB_MAX_RETRIES) {
+			pr_err("%s: START_BUSY bit didn't clear, exiting\n",
+			       __func__);
+			return -ETIMEDOUT;
+		}
+	}
+	return 0;
+}
+
+/*
+ * This function set clock frequency for SMBus block. As per hardware
+ * engineering, the clock frequency can be changed dynamically.
+ */
+static int iproc_i2c_set_clk_freq(struct iproc_i2c *bus_prvdata)
+{
+	struct iproc_i2c_regs *base = bus_prvdata->base;
+	unsigned int regval;
+
+	regval = iproc_i2c_reg_read(&base->timg_cfg);
+
+	switch (bus_prvdata->bus_speed) {
+	case I2C_SPEED_STANDARD_RATE:
+		regval &= ~CCB_SMB_TIMGCFG_MODE400_MASK;
+		break;
+
+	case I2C_SPEED_FAST_RATE:
+		regval |= CCB_SMB_TIMGCFG_MODE400_MASK;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	iproc_i2c_reg_write(&base->timg_cfg, regval);
+	return 0;
+}
+
+static int iproc_i2c_init(struct udevice *bus)
+{
+	struct iproc_i2c *bus_prvdata = dev_get_priv(bus);
+	struct iproc_i2c_regs *base = bus_prvdata->base;
+	unsigned int regval;
+
+	debug("\nEntering %s\n", __func__);
+
+	/* Put controller in reset */
+	regval = iproc_i2c_reg_read(&base->cfg_reg);
+	regval |= CCB_SMB_CFG_RST_MASK;
+	regval &= ~CCB_SMB_CFG_SMBEN_MASK;
+	iproc_i2c_reg_write(&base->cfg_reg, regval);
+
+	/* Wait 100 usec as per spec */
+	udelay(100);
+
+	/* bring controller out of reset */
+	regval &= ~CCB_SMB_CFG_RST_MASK;
+	iproc_i2c_reg_write(&base->cfg_reg, regval);
+
+	/* Flush Tx, Rx FIFOs. Note we are setting the Rx FIFO threshold to 0.
+	 * May be OK since we are setting RX_EVENT and RX_FIFO_FULL interrupts
+	 */
+	regval = CCB_SMB_MSTRRXFIFOFLSH_MASK | CCB_SMB_MSTRTXFIFOFLSH_MASK;
+	iproc_i2c_reg_write(&base->mstr_fifo_ctrl, regval);
+
+	/* Enable SMbus block. Note, we are setting MASTER_RETRY_COUNT to zero
+	 * since there will be only one master
+	 */
+	regval = iproc_i2c_reg_read(&base->cfg_reg);
+	regval |= CCB_SMB_CFG_SMBEN_MASK;
+	iproc_i2c_reg_write(&base->cfg_reg, regval);
+
+	/* Set default clock frequency */
+	iproc_i2c_set_clk_freq(bus_prvdata);
+
+	/* Disable intrs */
+	iproc_i2c_reg_write(&base->evt_en, 0);
+
+	/* Clear intrs (W1TC) */
+	regval = iproc_i2c_reg_read(&base->evt_sts);
+	iproc_i2c_reg_write(&base->evt_sts, regval);
+
+	bus_prvdata->i2c_init_done = 1;
+
+	iproc_dump_i2c_regs(bus_prvdata);
+	debug("%s: Init successful\n", __func__);
+
+	return 0;
+}
+
+/*
+ * This function copies data to SMBus's Tx FIFO. Valid for write transactions
+ * only
+ *
+ * base_addr: Mapped address of this SMBus instance
+ * dev_addr:  SMBus (I2C) device address. We are assuming 7-bit addresses
+ *            initially
+ * info:   Data to copy in to Tx FIFO. For read commands, the size should be
+ *         set to zero by the caller
+ *
+ */
+static void iproc_i2c_write_trans_data(struct iproc_i2c *bus_prvdata,
+				       unsigned short dev_addr,
+				       struct iproc_xact_info *info)
+{
+	struct iproc_i2c_regs *base = bus_prvdata->base;
+	unsigned int regval;
+	unsigned int i;
+	unsigned int num_data_bytes = 0;
+
+	debug("%s: dev_addr=0x%X cmd_valid=%d cmd=0x%02x size=%u proto=%d buf[] %x\n",
+	      __func__, dev_addr, info->cmd_valid,
+	      info->command, info->size, info->smb_proto, info->data[0]);
+
+	/* Write SMBus device address first */
+	/* Note, we are assuming 7-bit addresses for now. For 10-bit addresses,
+	 * we may have one more write to send the upper 3 bits of 10-bit addr
+	 */
+	iproc_i2c_reg_write(&base->mstr_datawr, dev_addr);
+
+	/* If the protocol needs command code, copy it */
+	if (info->cmd_valid)
+		iproc_i2c_reg_write(&base->mstr_datawr, info->command);
+
+	/* Depending on the SMBus protocol, we need to write additional
+	 * transaction data in to Tx FIFO. Refer to section 5.5 of SMBus
+	 * spec for sequence for a transaction
+	 */
+	switch (info->smb_proto) {
+	case SMBUS_PROT_RECV_BYTE:
+		/* No additional data to be written */
+		num_data_bytes = 0;
+		break;
+
+	case SMBUS_PROT_SEND_BYTE:
+		num_data_bytes = info->size;
+		break;
+
+	case SMBUS_PROT_RD_BYTE:
+	case SMBUS_PROT_RD_WORD:
+	case SMBUS_PROT_BLK_RD:
+		/* Write slave address with R/W~ set (bit #0) */
+		iproc_i2c_reg_write(&base->mstr_datawr,
+				    dev_addr | 0x1);
+		num_data_bytes = 0;
+		break;
+
+	case SMBUS_PROT_BLK_WR_BLK_RD_PROC_CALL:
+		iproc_i2c_reg_write(&base->mstr_datawr,
+				    dev_addr | 0x1 |
+				    CCB_SMB_MSTRWRSTS_MASK);
+		num_data_bytes = 0;
+		break;
+
+	case SMBUS_PROT_WR_BYTE:
+	case SMBUS_PROT_WR_WORD:
+		/* No additional bytes to be written.
+		 * Data portion is written in the
+		 * 'for' loop below
+		 */
+		num_data_bytes = info->size;
+		break;
+
+	case SMBUS_PROT_BLK_WR:
+		/* 3rd byte is byte count */
+		iproc_i2c_reg_write(&base->mstr_datawr, info->size);
+		num_data_bytes = info->size;
+		break;
+
+	default:
+		return;
+	}
+
+	/* Copy actual data from caller, next. In general, for reads,
+	 * no data is copied
+	 */
+	for (i = 0; num_data_bytes; --num_data_bytes, i++) {
+		/* For the last byte, set MASTER_WR_STATUS bit */
+		regval = (num_data_bytes == 1) ?
+			 info->data[i] | CCB_SMB_MSTRWRSTS_MASK :
+			 info->data[i];
+
+		iproc_i2c_reg_write(&base->mstr_datawr, regval);
+	}
+}
+
+static int iproc_i2c_data_send(struct iproc_i2c *bus_prvdata,
+			       unsigned short addr,
+			       struct iproc_xact_info *info)
+{
+	struct iproc_i2c_regs *base = bus_prvdata->base;
+	int rc, retry = 3;
+	unsigned int regval;
+
+	/* Make sure the previous transaction completed */
+	rc = iproc_i2c_startbusy_wait(bus_prvdata);
+
+	if (rc < 0) {
+		pr_err("%s: Send: bus is busy, exiting\n", __func__);
+		return rc;
+	}
+
+	/* Write transaction bytes to Tx FIFO */
+	iproc_i2c_write_trans_data(bus_prvdata, addr, info);
+
+	/* Program master command register (0x30) with protocol type and set
+	 * start_busy_command bit to initiate the write transaction
+	 */
+	regval = (info->smb_proto << CCB_SMB_MSTRSMBUSPROTO_SHIFT) |
+		 CCB_SMB_MSTRSTARTBUSYCMD_MASK;
+
+	iproc_i2c_reg_write(&base->mstr_cmd, regval);
+
+	/* Check for Master status */
+	regval = iproc_i2c_reg_read(&base->mstr_cmd);
+	while (regval & CCB_SMB_MSTRSTARTBUSYCMD_MASK) {
+		mdelay(10);
+		if (retry-- <= 0)
+			break;
+		regval = iproc_i2c_reg_read(&base->mstr_cmd);
+	}
+
+	/* If start_busy bit cleared, check if there are any errors */
+	if (!(regval & CCB_SMB_MSTRSTARTBUSYCMD_MASK)) {
+		/* start_busy bit cleared, check master_status field now */
+		regval &= CCB_SMB_MSTRSTS_MASK;
+		regval >>= CCB_SMB_MSTRSTS_SHIFT;
+
+		if (regval != MSTR_STS_XACT_SUCCESS) {
+			/* Error We can flush Tx FIFO here */
+			pr_err("%s: ERROR: Error in transaction %u, exiting\n",
+			       __func__, regval);
+			return -EREMOTEIO;
+		}
+	}
+
+	return 0;
+}
+
+static int iproc_i2c_data_recv(struct iproc_i2c *bus_prvdata,
+			       unsigned short addr,
+			       struct iproc_xact_info *info,
+			       unsigned int *num_bytes_read)
+{
+	struct iproc_i2c_regs *base = bus_prvdata->base;
+	int rc, retry = 3;
+	unsigned int regval;
+
+	/* Make sure the previous transaction completed */
+	rc = iproc_i2c_startbusy_wait(bus_prvdata);
+
+	if (rc < 0) {
+		pr_err("%s: Receive: Bus is busy, exiting\n", __func__);
+		return rc;
+	}
+
+	/* Program all transaction bytes into master Tx FIFO */
+	iproc_i2c_write_trans_data(bus_prvdata, addr, info);
+
+	/* Program master command register (0x30) with protocol type and set
+	 * start_busy_command bit to initiate the write transaction
+	 */
+	regval = (info->smb_proto << CCB_SMB_MSTRSMBUSPROTO_SHIFT) |
+		 CCB_SMB_MSTRSTARTBUSYCMD_MASK | info->size;
+
+	iproc_i2c_reg_write(&base->mstr_cmd, regval);
+
+	/* Check for Master status */
+	regval = iproc_i2c_reg_read(&base->mstr_cmd);
+	while (regval & CCB_SMB_MSTRSTARTBUSYCMD_MASK) {
+		udelay(1000);
+		if (retry-- <= 0)
+			break;
+		regval = iproc_i2c_reg_read(&base->mstr_cmd);
+	}
+
+	/* If start_busy bit cleared, check if there are any errors */
+	if (!(regval & CCB_SMB_MSTRSTARTBUSYCMD_MASK)) {
+		/* start_busy bit cleared, check master_status field now */
+		regval &= CCB_SMB_MSTRSTS_MASK;
+		regval >>= CCB_SMB_MSTRSTS_SHIFT;
+
+		if (regval != MSTR_STS_XACT_SUCCESS) {
+			/* We can flush Tx FIFO here */
+			pr_err("%s: Error in transaction %d, exiting\n",
+			       __func__, regval);
+			return -EREMOTEIO;
+		}
+	}
+
+	/* Read received byte(s), after TX out address etc */
+	regval = iproc_i2c_reg_read(&base->mstr_datard);
+
+	/* For block read, protocol (hw) returns byte count,
+	 * as the first byte
+	 */
+	if (info->smb_proto == SMBUS_PROT_BLK_RD) {
+		int i;
+
+		*num_bytes_read = regval & CCB_SMB_MSTRRDDATA_MASK;
+
+		/* Limit to reading a max of 32 bytes only; just a safeguard.
+		 * If # bytes read is a number > 32, check transaction set up,
+		 * and contact hw engg. Assumption: PEC is disabled
+		 */
+		for (i = 0;
+		     (i < *num_bytes_read) && (i < I2C_SMBUS_BLOCK_MAX);
+		     i++) {
+			/* Read Rx FIFO for data bytes */
+			regval = iproc_i2c_reg_read(&base->mstr_datard);
+			info->data[i] = regval & CCB_SMB_MSTRRDDATA_MASK;
+		}
+	} else {
+		/* 1 Byte data */
+		*info->data = regval & CCB_SMB_MSTRRDDATA_MASK;
+		*num_bytes_read = 1;
+	}
+
+	return 0;
+}
+
+static int i2c_write_byte(struct iproc_i2c *bus_prvdata,
+			  u8 devaddr, u8 regoffset, u8 value)
+{
+	int rc;
+	struct iproc_xact_info info;
+
+	devaddr <<= 1;
+
+	info.cmd_valid = 1;
+	info.command = (unsigned char)regoffset;
+	info.data = &value;
+	info.size = 1;
+	info.flags = 0;
+	info.smb_proto = SMBUS_PROT_WR_BYTE;
+	/* Refer to i2c_smbus_write_byte params passed. */
+	rc = iproc_i2c_data_send(bus_prvdata, devaddr, &info);
+
+	if (rc < 0) {
+		pr_err("%s: %s error accessing device 0x%X\n",
+		       __func__, "Write", devaddr);
+		return -EREMOTEIO;
+	}
+
+	return 0;
+}
+
+int i2c_write(struct udevice *bus,
+	      uchar chip, uint regaddr, int alen, uchar *buffer, int len)
+{
+	struct iproc_i2c *bus_prvdata = dev_get_priv(bus);
+	int i, data_len;
+	u8 *data;
+
+	if (len > 256) {
+		pr_err("I2C write: address out of range\n");
+		return 1;
+	}
+
+	if (len < 1) {
+		pr_err("I2C write: Need offset addr and value\n");
+		return 1;
+	}
+
+	/* buffer contains offset addr followed by value to be written */
+	regaddr = buffer[0];
+	data = &buffer[1];
+	data_len = len - 1;
+
+	for (i = 0; i < data_len; i++) {
+		if (i2c_write_byte(bus_prvdata, chip, regaddr + i, data[i])) {
+			pr_err("I2C write (%d): I/O error\n", i);
+			iproc_i2c_init(bus);
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+static int i2c_read_byte(struct iproc_i2c *bus_prvdata,
+			 u8 devaddr, u8 regoffset, u8 *value)
+{
+	int rc;
+	struct iproc_xact_info info;
+	unsigned int num_bytes_read = 0;
+
+	devaddr <<= 1;
+
+	info.cmd_valid = 1;
+	info.command = (unsigned char)regoffset;
+	info.data = value;
+	info.size = 1;
+	info.flags = 0;
+	info.smb_proto = SMBUS_PROT_RD_BYTE;
+	/* Refer to i2c_smbus_read_byte for params passed. */
+	rc = iproc_i2c_data_recv(bus_prvdata, devaddr, &info, &num_bytes_read);
+
+	if (rc < 0) {
+		pr_err("%s: %s error accessing device 0x%X\n",
+		       __func__, "Read", devaddr);
+		return -EREMOTEIO;
+	}
+
+	return 0;
+}
+
+int i2c_read(struct udevice *bus,
+	     uchar chip, uint addr, int alen, uchar *buffer, int len)
+{
+	struct iproc_i2c *bus_prvdata = dev_get_priv(bus);
+	int i;
+
+	if (len > 256) {
+		pr_err("I2C read: address out of range\n");
+		return 1;
+	}
+
+	for (i = 0; i < len; i++) {
+		if (i2c_read_byte(bus_prvdata, chip, addr + i, &buffer[i])) {
+			pr_err("I2C read: I/O error\n");
+			iproc_i2c_init(bus);
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+static int iproc_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs)
+{
+	int ret = 0;
+
+	debug("%s: %d messages\n", __func__, nmsgs);
+
+	for (; nmsgs > 0; nmsgs--, msg++) {
+		if (msg->flags & I2C_M_RD)
+			ret = i2c_read(bus, msg->addr, 0, 0,
+				       msg->buf, msg->len);
+		else
+			ret = i2c_write(bus, msg->addr, 0, 0,
+					msg->buf, msg->len);
+	}
+
+	return ret;
+}
+
+static int iproc_i2c_probe_chip(struct udevice *bus, uint chip_addr,
+				uint chip_flags)
+{
+	struct iproc_i2c *bus_prvdata = dev_get_priv(bus);
+	struct iproc_i2c_regs *base = bus_prvdata->base;
+	u32 regval;
+
+	debug("\n%s: Entering chip probe\n", __func__);
+
+	/* Init internal regs, disable intrs (and then clear intrs), set fifo
+	 * thresholds, etc.
+	 */
+	if (!bus_prvdata->i2c_init_done)
+		iproc_i2c_init(bus);
+
+	regval = (chip_addr << 1);
+	iproc_i2c_reg_write(&base->mstr_datawr, regval);
+	regval = ((SMBUS_PROT_QUICK_CMD << CCB_SMB_MSTRSMBUSPROTO_SHIFT) |
+			(1 << CCB_SMB_MSTRSTARTBUSYCMD_SHIFT));
+	iproc_i2c_reg_write(&base->mstr_cmd, regval);
+
+	do {
+		udelay(100);
+		regval = iproc_i2c_reg_read(&base->mstr_cmd);
+		regval &= CCB_SMB_MSTRSTARTBUSYCMD_MASK;
+	}  while (regval);
+
+	regval = iproc_i2c_reg_read(&base->mstr_cmd);
+
+	if ((regval & CCB_SMB_MSTRSTS_MASK) != 0)
+		return -1;
+
+	iproc_dump_i2c_regs(bus_prvdata);
+	debug("%s: chip probe successful\n", __func__);
+
+	return 0;
+}
+
+static int iproc_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
+{
+	struct iproc_i2c *bus_prvdata = dev_get_priv(bus);
+
+	bus_prvdata->bus_speed = speed;
+	return iproc_i2c_set_clk_freq(bus_prvdata);
+}
+
+/**
+ * i2c_get_bus_speed - get i2c bus speed
+ *
+ * This function returns the speed of operation in Hz
+ */
+int iproc_i2c_get_bus_speed(struct udevice *bus)
+{
+	struct iproc_i2c *bus_prvdata = dev_get_priv(bus);
+	struct iproc_i2c_regs *base = bus_prvdata->base;
+	unsigned int regval;
+	int ret = 0;
+
+	regval = iproc_i2c_reg_read(&base->timg_cfg);
+	regval = (regval & CCB_SMB_TIMGCFG_MODE400_MASK) >>
+		  CCB_SMB_TIMGCFG_MODE400_SHIFT;
+
+	switch (regval) {
+	case 0:
+		ret = I2C_SPEED_STANDARD_RATE;
+		break;
+	case 1:
+		ret = I2C_SPEED_FAST_RATE;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int iproc_i2c_probe(struct udevice *bus)
+{
+	return iproc_i2c_init(bus);
+}
+
+static int iproc_i2c_ofdata_to_platdata(struct udevice *bus)
+{
+	struct iproc_i2c *bus_prvdata = dev_get_priv(bus);
+	int node = dev_of_offset(bus);
+	const void *blob = gd->fdt_blob;
+
+	bus_prvdata->base = map_physmem(devfdt_get_addr(bus),
+					sizeof(void *),
+					MAP_NOCACHE);
+
+	bus_prvdata->bus_speed =
+		fdtdec_get_int(blob, node, "bus-frequency",
+			       I2C_SPEED_STANDARD_RATE);
+
+	return 0;
+}
+
+static const struct dm_i2c_ops iproc_i2c_ops = {
+	.xfer		= iproc_i2c_xfer,
+	.probe_chip	= iproc_i2c_probe_chip,
+	.set_bus_speed	= iproc_i2c_set_bus_speed,
+	.get_bus_speed	= iproc_i2c_get_bus_speed,
+};
+
+static const struct udevice_id iproc_i2c_ids[] = {
+	{ .compatible = "brcm,iproc-i2c" },
+	{ }
+};
+
+U_BOOT_DRIVER(iproc_i2c) = {
+	.name	= "iproc_i2c",
+	.id	= UCLASS_I2C,
+	.of_match = iproc_i2c_ids,
+	.ofdata_to_platdata = iproc_i2c_ofdata_to_platdata,
+	.probe	= iproc_i2c_probe,
+	.priv_auto_alloc_size = sizeof(struct iproc_i2c),
+	.ops	= &iproc_i2c_ops,
+	.flags  = DM_FLAG_PRE_RELOC,
+};

+ 335 - 0
drivers/i2c/iproc_i2c.h

@@ -0,0 +1,335 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2018 Broadcom
+ *
+ */
+
+#ifndef __IPROC_I2C_H__
+#define __IPROC_I2C_H__
+
+/* Registers */
+#define CCB_SMB_CFG_REG 0x0
+
+#define CCB_SMB_CFG_RST_MASK                         0x80000000
+#define CCB_SMB_CFG_RST_SHIFT                        31
+
+#define CCB_SMB_CFG_SMBEN_MASK                       0x40000000
+#define CCB_SMB_CFG_SMBEN_SHIFT                      30
+
+#define CCB_SMB_CFG_BITBANGEN_MASK                   0x20000000
+#define CCB_SMB_CFG_BITBANGEN_SHIFT                  29
+
+#define CCB_SMB_CFG_EN_NIC_SMBADDR0_MASK             0x10000000
+#define CCB_SMB_CFG_EN_NIC_SMBADDR0_SHIFT            28
+
+#define CCB_SMB_CFG_PROMISCMODE_MASK                 0x08000000
+#define CCB_SMB_CFG_PROMISCMODE_SHIFT                27
+
+#define CCB_SMB_CFG_TSTMPCNTEN_MASK                  0x04000000
+#define CCB_SMB_CFG_TSTMPCNTEN_SHIFT                 26
+
+#define CCB_SMB_CFG_MSTRRTRYCNT_MASK                 0x000F0000
+#define CCB_SMB_CFG_MSTRRTRYCNT_SHIFT                16
+
+#define CCB_SMB_TIMGCFG_REG 0x4
+
+#define CCB_SMB_TIMGCFG_MODE400_MASK                 0x80000000
+#define CCB_SMB_TIMGCFG_MODE400_SHIFT                31
+
+#define CCB_SMB_TIMGCFG_RNDSLVSTR_MASK               0x7F000000
+#define CCB_SMB_TIMGCFG_RNDSLVSTR_SHIFT              24
+
+#define CCB_SMB_TIMGCFG_PERSLVSTR_MASK               0x00FF0000
+#define CCB_SMB_TIMGCFG_PERSLVSTR_SHIFT              16
+
+#define CCB_SMB_TIMGCFG_IDLTIME_MASK                 0x0000FF00
+#define CCB_SMB_TIMGCFG_IDLTIME_SHIFT                8
+
+#define CCB_SMB_ADDR_REG 0x8
+
+#define CCB_SMB_EN_NIC_SMBADDR3_MASK                 0x80000000
+#define CCB_SMB_EN_NIC_SMBADDR3_SHIFT                31
+
+#define CCB_SMB_NIC_SMBADDR3_MASK                    0x7F000000
+#define CCB_SMB_NIC_SMBADDR3_SHIFT                   24
+
+#define CCB_SMB_EN_NIC_SMBADDR2_MASK                 0x00800000
+#define CCB_SMB_EN_NIC_SMBADDR2_SHIFT                23
+
+#define CCB_SMB_NIC_SMBADDR2_MASK                    0x007F0000
+#define CCB_SMB_NIC_SMBADDR2_SHIFT                   16
+
+#define CCB_SMB_EN_NIC_SMBADDR1_MASK                 0x00008000
+#define CCB_SMB_EN_NIC_SMBADDR1_SHIFT                15
+
+#define CCB_SMB_NIC_SMBADDR1_MASK                    0x00007F00
+#define CCB_SMB_NIC_SMBADDR1_SHIFT                   8
+
+#define CCB_SMB_EN_NIC_SMBADDR0_MASK                 0x00000080
+#define CCB_SMB_EN_NIC_SMBADDR0_SHIFT                7
+
+#define CCB_SMB_NIC_SMBADDR0_MASK                    0x0000007F
+#define CCB_SMB_NIC_SMBADDR0_SHIFT                   0
+
+#define CCB_SMB_MSTRFIFOCTL_REG 0xC
+
+#define CCB_SMB_MSTRRXFIFOFLSH_MASK                  0x80000000
+#define CCB_SMB_MSTRRXFIFOFLSH_SHIFT                 31
+
+#define CCB_SMB_MSTRTXFIFOFLSH_MASK                  0x40000000
+#define CCB_SMB_MSTRTXFIFOFLSH_SHIFT                 30
+
+#define CCB_SMB_MSTRRXPKTCNT_MASK                    0x007F0000
+#define CCB_SMB_MSTRRXPKTCNT_SHIFT                   16
+
+#define CCB_SMB_MSTRRXFIFOTHR_MASK                   0x00003F00
+#define CCB_SMB_MSTRRXFIFOTHR_SHIFT                  8
+
+#define CCB_SMB_SLVFIFOCTL_REG 0x10
+
+#define CCB_SMB_SLVRXFIFOFLSH_MASK                   0x80000000
+#define CCB_SMB_SLVRXFIFOFLSH_SHIFT                  31
+
+#define CCB_SMB_SLVTXFIFOFLSH_MASK                   0x40000000
+#define CCB_SMB_SLVTXFIFOFLSH_SHIFT                  30
+
+#define CCB_SMB_SLVRXPKTCNT_MASK                     0x007F0000
+#define CCB_SMB_SLVRXPKTCNT_SHIFT                    16
+
+#define CCB_SMB_SLVRXFIFOTHR_MASK                    0x00003F00
+#define CCB_SMB_SLVRXFIFOTHR_SHIFT                   8
+
+#define CCB_SMB_BITBANGCTL_REG 0x14
+
+#define CCB_SMB_SMBCLKIN_MASK                        0x80000000
+#define CCB_SMB_SMBCLKIN_SHIFT                       31
+
+#define CCB_SMB_SMBCLKOUTEN_MASK                     0x40000000
+#define CCB_SMB_SMBCLKOUTEN_SHIFT                    30
+
+#define CCB_SMB_SMBDATAIN_MASK                       0x20000000
+#define CCB_SMB_SMBDATAIN_SHIFT                      29
+
+#define CCB_SMB_SMBDATAOUTEN_MASK                    0x10000000
+#define CCB_SMB_SMBDATAOUTEN_SHIFT                   28
+
+#define CCB_SMB_MSTRCMD_REG 0x30
+
+#define CCB_SMB_MSTRSTARTBUSYCMD_MASK                0x80000000
+#define CCB_SMB_MSTRSTARTBUSYCMD_SHIFT               31
+
+#define CCB_SMB_MSTRABORT_MASK                       0x40000000
+#define CCB_SMB_MSTRABORT_SHIFT                      30
+
+#define CCB_SMB_MSTRSTS_MASK                         0x0E000000
+#define CCB_SMB_MSTRSTS_SHIFT                        25
+
+#define CCB_SMB_MSTRSMBUSPROTO_MASK                  0x00001E00
+#define CCB_SMB_MSTRSMBUSPROTO_SHIFT                 9
+
+#define CCB_SMB_MSTRPEC_MASK                         0x00000100
+#define CCB_SMB_MSTRPEC_SHIFT                        8
+
+#define CCB_SMB_MSTRRDBYTECNT_MASK                   0x000000FF
+#define CCB_SMB_MSTRRDBYTECNT_SHIFT                  0
+
+#define CCB_SMB_SLVCMD_REG 0x34
+
+#define CCB_SMB_SLVSTARTBUSYCMD_MASK                 0x80000000
+#define CCB_SMB_SLVSTARTBUSYCMD_SHIFT                31
+
+#define CCB_SMB_SLVABORT_MASK                        0x40000000
+#define CCB_SMB_SLVABORT_SHIFT                       30
+
+#define CCB_SMB_SLVSTS_MASK                          0x03800000
+#define CCB_SMB_SLVSTS_SHIFT                         23
+
+#define CCB_SMB_SLVPEC_MASK                          0x00000100
+#define CCB_SMB_SLVPEC_SHIFT                         8
+
+#define CCB_SMB_EVTEN_REG 0x38
+
+#define CCB_SMB_MSTRRXFIFOFULLEN_MASK                0x80000000
+#define CCB_SMB_MSTRRXFIFOFULLEN_SHIFT               31
+
+#define CCB_SMB_MSTRRXFIFOTHRHITEN_MASK              0x40000000
+#define CCB_SMB_MSTRRXFIFOTHRHITEN_SHIFT             30
+
+#define CCB_SMB_MSTRRXEVTEN_MASK                     0x20000000
+#define CCB_SMB_MSTRRXEVTEN_SHIFT                    29
+
+#define CCB_SMB_MSTRSTARTBUSYEN_MASK                 0x10000000
+#define CCB_SMB_MSTRSTARTBUSYEN_SHIFT                28
+
+#define CCB_SMB_MSTRTXUNDEN_MASK                     0x08000000
+#define CCB_SMB_MSTRTXUNDEN_SHIFT                    27
+
+#define CCB_SMB_SLVRXFIFOFULLEN_MASK                 0x04000000
+#define CCB_SMB_SLVRXFIFOFULLEN_SHIFT                26
+
+#define CCB_SMB_SLVRXFIFOTHRHITEN_MASK               0x02000000
+#define CCB_SMB_SLVRXFIFOTHRHITEN_SHIFT              25
+
+#define CCB_SMB_SLVRXEVTEN_MASK                      0x01000000
+#define CCB_SMB_SLVRXEVTEN_SHIFT                     24
+
+#define CCB_SMB_SLVSTARTBUSYEN_MASK                  0x00800000
+#define CCB_SMB_SLVSTARTBUSYEN_SHIFT                 23
+
+#define CCB_SMB_SLVTXUNDEN_MASK                      0x00400000
+#define CCB_SMB_SLVTXUNDEN_SHIFT                     22
+
+#define CCB_SMB_SLVRDEVTEN_MASK                      0x00200000
+#define CCB_SMB_SLVRDEVTEN_SHIFT                     21
+
+#define CCB_SMB_EVTSTS_REG 0x3C
+
+#define CCB_SMB_MSTRRXFIFOFULLSTS_MASK               0x80000000
+#define CCB_SMB_MSTRRXFIFOFULLSTS_SHIFT              31
+
+#define CCB_SMB_MSTRRXFIFOTHRHITSTS_MASK             0x40000000
+#define CCB_SMB_MSTRRXFIFOTHRHITSTS_SHIFT            30
+
+#define CCB_SMB_MSTRRXEVTSTS_MASK                    0x20000000
+#define CCB_SMB_MSTRRXEVTSTS_SHIFT                   29
+
+#define CCB_SMB_MSTRSTARTBUSYSTS_MASK                0x10000000
+#define CCB_SMB_MSTRSTARTBUSYSTS_SHIFT               28
+
+#define CCB_SMB_MSTRTXUNDSTS_MASK                    0x08000000
+#define CCB_SMB_MSTRTXUNDSTS_SHIFT                   27
+
+#define CCB_SMB_SLVRXFIFOFULLSTS_MASK                0x04000000
+#define CCB_SMB_SLVRXFIFOFULLSTS_SHIFT               26
+
+#define CCB_SMB_SLVRXFIFOTHRHITSTS_MASK              0x02000000
+#define CCB_SMB_SLVRXFIFOTHRHITSTS_SHIFT             25
+
+#define CCB_SMB_SLVRXEVTSTS_MASK                     0x01000000
+#define CCB_SMB_SLVRXEVTSTS_SHIFT                    24
+
+#define CCB_SMB_SLVSTARTBUSYSTS_MASK                 0x00800000
+#define CCB_SMB_SLVSTARTBUSYSTS_SHIFT                23
+
+#define CCB_SMB_SLVTXUNDSTS_MASK                     0x00400000
+#define CCB_SMB_SLVTXUNDSTS_SHIFT                    22
+
+#define CCB_SMB_SLVRDEVTSTS_MASK                     0x00200000
+#define CCB_SMB_SLVRDEVTSTS_SHIFT                    21
+
+#define CCB_SMB_MSTRDATAWR_REG 0x40
+
+#define CCB_SMB_MSTRWRSTS_MASK                       0x80000000
+#define CCB_SMB_MSTRWRSTS_SHIFT                      31
+
+#define CCB_SMB_MSTRWRDATA_MASK                      0x000000FF
+#define CCB_SMB_MSTRWRDATA_SHIFT                     0
+
+#define CCB_SMB_MSTRDATARD_REG 0x44
+
+#define CCB_SMB_MSTRRDSTS_MASK                       0xC0000000
+#define CCB_SMB_MSTRRDSTS_SHIFT                      30
+
+#define CCB_SMB_MSTRRDPECERR_MASK                    0x20000000
+#define CCB_SMB_MSTRRDPECERR_SHIFT                   29
+
+#define CCB_SMB_MSTRRDDATA_MASK                      0x000000FF
+#define CCB_SMB_MSTRRDDATA_SHIFT                     0
+
+#define CCB_SMB_SLVDATAWR_REG 0x48
+
+#define CCB_SMB_SLVWRSTS_MASK                        0x80000000
+#define CCB_SMB_SLVWRSTS_SHIFT                       31
+
+#define CCB_SMB_SLVWRDATA_MASK                       0x000000FF
+#define CCB_SMB_SLVWRDATA_SHIFT                      0
+
+#define CCB_SMB_SLVDATARD_REG 0x4C
+
+#define CCB_SMB_SLVRDSTS_MASK                        0xC0000000
+#define CCB_SMB_SLVRDSTS_SHIFT                       30
+
+#define CCB_SMB_SLVRDERRSTS_MASK                     0x30000000
+#define CCB_SMB_SLVRDERRSTS_SHIFT                    28
+
+#define CCB_SMB_SLVRDDATA_MASK                       0x000000FF
+#define CCB_SMB_SLVRDDATA_SHIFT                      0
+
+/* --Registers-- */
+
+/* Transaction error codes defined in Master command register (0x30) */
+#define MSTR_STS_XACT_SUCCESS          0
+#define MSTR_STS_LOST_ARB              1
+#define MSTR_STS_NACK_FIRST_BYTE       2
+
+/* NACK on a byte other than
+ * the first byte
+ */
+#define MSTR_STS_NACK_NON_FIRST_BYTE   3
+
+#define MSTR_STS_TTIMEOUT_EXCEEDED     4
+#define MSTR_STS_TX_TLOW_MEXT_EXCEEDED 5
+#define MSTR_STS_RX_TLOW_MEXT_EXCEEDED 6
+
+/* SMBUS protocol values defined in register 0x30 */
+#define SMBUS_PROT_QUICK_CMD               0
+#define SMBUS_PROT_SEND_BYTE               1
+#define SMBUS_PROT_RECV_BYTE               2
+#define SMBUS_PROT_WR_BYTE                 3
+#define SMBUS_PROT_RD_BYTE                 4
+#define SMBUS_PROT_WR_WORD                 5
+#define SMBUS_PROT_RD_WORD                 6
+#define SMBUS_PROT_BLK_WR                  7
+#define SMBUS_PROT_BLK_RD                  8
+#define SMBUS_PROT_PROC_CALL               9
+#define SMBUS_PROT_BLK_WR_BLK_RD_PROC_CALL 10
+
+/* SMBUS Block speed mode */
+#define SMBUS_BLOCK_MODE_100		0
+#define SMBUS_BLOCK_MODE_400		1
+
+#define BUS_BUSY_COUNT         100000 /* Number can be changed later */
+#define IPROC_I2C_INVALID_ADDR 0xFF
+#define IPROC_SMB_MAX_RETRIES   35
+#define I2C_SMBUS_BLOCK_MAX     32
+#define GETREGFLDVAL(regval, mask, startbit) \
+		    (((regval) & (mask)) >> (startbit))
+
+/* This enum will be used to notify the user of status of a data transfer
+ * request
+ */
+enum iproc_smb_error_code {
+	I2C_NO_ERR = 0,
+	I2C_TIMEOUT_ERR = 1,
+
+	/* Invalid parameter(s) passed to the driver */
+	I2C_INVALID_PARAM_ERR = 2,
+
+	/* The driver API was called before the present
+	 * transfer was completed
+	 */
+	I2C_OPER_IN_PROGRESS = 3,
+
+	/* Transfer aborted unexpectedly, for example a NACK
+	 * received, before last byte was read/written
+	 */
+	I2C_OPER_ABORT_ERR = 4,
+
+	/* Feature or function not supported
+	 * (e.g., 10-bit addresses, or clock speeds
+	 * other than 100KHz, 400KHz)
+	 */
+	I2C_FUNC_NOT_SUPPORTED = 5,
+};
+
+/* Structure used to pass information to read/write functions. */
+struct iproc_xact_info {
+	unsigned char command;
+	unsigned char *data;
+	unsigned int size;
+	unsigned short flags; /* used for specifying PEC, 10-bit addresses */
+	unsigned char smb_proto; /* SMBus protocol */
+	unsigned int cmd_valid; /* true if command is valid else false */
+};
+
+#endif /* __IPROC_I2C_H__ */

+ 2 - 2
drivers/i2c/muxes/Kconfig

@@ -33,8 +33,8 @@ config I2C_MUX_PCA954x
 	  devices. It is x width I2C multiplexer which enables to partitioning
 	  devices. It is x width I2C multiplexer which enables to partitioning
 	  I2C bus and connect multiple devices with the same address to the same
 	  I2C bus and connect multiple devices with the same address to the same
 	  I2C controller where driver handles proper routing to target i2c
 	  I2C controller where driver handles proper routing to target i2c
-	  device. Supported chips are PCA9543, PCA9544, PCA9547, PCA9548 and
-	  PCA9646.
+	  device. Supported chips are PCA9543, PCA9544, PCA9546, PCA9547,
+	  PCA9548 and PCA9646.
 
 
 config I2C_MUX_GPIO
 config I2C_MUX_GPIO
         tristate "GPIO-based I2C multiplexer"
         tristate "GPIO-based I2C multiplexer"

+ 6 - 0
drivers/i2c/muxes/pca954x.c

@@ -18,6 +18,7 @@ DECLARE_GLOBAL_DATA_PTR;
 enum pca_type {
 enum pca_type {
 	PCA9543,
 	PCA9543,
 	PCA9544,
 	PCA9544,
+	PCA9546,
 	PCA9547,
 	PCA9547,
 	PCA9548,
 	PCA9548,
 	PCA9646
 	PCA9646
@@ -48,6 +49,10 @@ static const struct chip_desc chips[] = {
 		.muxtype = pca954x_ismux,
 		.muxtype = pca954x_ismux,
 		.width = 4,
 		.width = 4,
 	},
 	},
+	[PCA9546] = {
+		.muxtype = pca954x_isswi,
+		.width = 4,
+	},
 	[PCA9547] = {
 	[PCA9547] = {
 		.enable = 0x8,
 		.enable = 0x8,
 		.muxtype = pca954x_ismux,
 		.muxtype = pca954x_ismux,
@@ -95,6 +100,7 @@ static const struct i2c_mux_ops pca954x_ops = {
 static const struct udevice_id pca954x_ids[] = {
 static const struct udevice_id pca954x_ids[] = {
 	{ .compatible = "nxp,pca9543", .data = PCA9543 },
 	{ .compatible = "nxp,pca9543", .data = PCA9543 },
 	{ .compatible = "nxp,pca9544", .data = PCA9544 },
 	{ .compatible = "nxp,pca9544", .data = PCA9544 },
+	{ .compatible = "nxp,pca9546", .data = PCA9546 },
 	{ .compatible = "nxp,pca9547", .data = PCA9547 },
 	{ .compatible = "nxp,pca9547", .data = PCA9547 },
 	{ .compatible = "nxp,pca9548", .data = PCA9548 },
 	{ .compatible = "nxp,pca9548", .data = PCA9548 },
 	{ .compatible = "nxp,pca9646", .data = PCA9646 },
 	{ .compatible = "nxp,pca9646", .data = PCA9646 },