Browse Source

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

i2c changes for v2020.10
- Add support for I2C controllers found on Octeon II/III and Octeon TX
  TX2 SoC platforms.
- Add I2C controller support for Cortina Access CAxxxx SoCs
- new rtc methods, rtc command, and tests
- imx_lpi2c: Improve the codes to use private data
- stm32f7_i2c.c: Add new compatible "st,stm32mp15-i2c"
- stm32f7_i2c.c: Add Fast Mode Plus support
- pwm: Add PWM driver for SiFive SoC
Tom Rini 3 years ago
parent
commit
d9107930af

+ 4 - 0
MAINTAINERS

@@ -182,6 +182,8 @@ F:	drivers/gpio/cortina_gpio.c
 F:	drivers/watchdog/cortina_wdt.c
 F:	drivers/serial/serial_cortina.c
 F:	drivers/mmc/ca_dw_mmc.c
+F:	drivers/i2c/i2c-cortina.c
+F:	drivers/i2c/i2c-cortina.h
 
 ARM/CZ.NIC TURRIS MOX SUPPORT
 M:	Marek Behun <marek.behun@nic.cz>
@@ -740,6 +742,8 @@ F:	drivers/gpio/cortina_gpio.c
 F:	drivers/watchdog/cortina_wdt.c
 F:	drivers/serial/serial_cortina.c
 F:	drivers/mmc/ca_dw_mmc.c
+F:	drivers/i2c/i2c-cortina.c
+F:	drivers/i2c/i2c-cortina.h
 
 MIPS MSCC
 M:	Gregory CLEMENT <gregory.clement@bootlin.com>

+ 5 - 0
arch/sandbox/include/asm/rtc.h

@@ -21,6 +21,11 @@ enum {
 
 	REG_RESET	= 0x20,
 
+	REG_AUX0	= 0x30,
+	REG_AUX1,
+	REG_AUX2,
+	REG_AUX3,
+
 	REG_COUNT	= 0x80,
 };
 

+ 6 - 0
cmd/Kconfig

@@ -1739,6 +1739,12 @@ config CMD_DATE
 	  Enable the 'date' command for getting/setting the time/date in RTC
 	  devices.
 
+config CMD_RTC
+	bool "rtc"
+	depends on DM_RTC
+	help
+	  Enable the 'rtc' command for low-level access to RTC devices.
+
 config CMD_TIME
 	bool "time"
 	help

+ 1 - 0
cmd/Makefile

@@ -122,6 +122,7 @@ obj-$(CONFIG_CMD_REISER) += reiser.o
 obj-$(CONFIG_CMD_REMOTEPROC) += remoteproc.o
 obj-$(CONFIG_CMD_RNG) += rng.o
 obj-$(CONFIG_CMD_ROCKUSB) += rockusb.o
+obj-$(CONFIG_CMD_RTC) += rtc.o
 obj-$(CONFIG_SANDBOX) += host.o
 obj-$(CONFIG_CMD_SATA) += sata.o
 obj-$(CONFIG_CMD_NVME) += nvme.o

+ 167 - 0
cmd/rtc.c

@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <common.h>
+#include <command.h>
+#include <dm.h>
+#include <hexdump.h>
+#include <i2c.h>
+#include <mapmem.h>
+#include <rtc.h>
+
+#define MAX_RTC_BYTES 32
+
+static int do_rtc_read(struct udevice *dev, int argc, char * const argv[])
+{
+	u8 buf[MAX_RTC_BYTES];
+	int reg, len, ret, r;
+
+	if (argc < 2 || argc > 3)
+		return CMD_RET_USAGE;
+
+	reg = simple_strtoul(argv[0], NULL, 16);
+	len = simple_strtoul(argv[1], NULL, 16);
+
+	if (argc == 3) {
+		u8 *addr;
+
+		addr = map_sysmem(simple_strtoul(argv[2], NULL, 16), len);
+		ret = dm_rtc_read(dev, reg, addr, len);
+		unmap_sysmem(addr);
+		if (ret) {
+			printf("dm_rtc_read() failed: %d\n", ret);
+			return CMD_RET_FAILURE;
+		}
+		return CMD_RET_SUCCESS;
+	}
+
+	while (len) {
+		r = min_t(int, len, sizeof(buf));
+		ret = dm_rtc_read(dev, reg, buf, r);
+		if (ret) {
+			printf("dm_rtc_read() failed: %d\n", ret);
+			return CMD_RET_FAILURE;
+		}
+		print_buffer(reg, buf, 1, r, 0);
+		len -= r;
+		reg += r;
+	}
+
+	return CMD_RET_SUCCESS;
+}
+
+static int do_rtc_write(struct udevice *dev, int argc, char * const argv[])
+{
+	u8 buf[MAX_RTC_BYTES];
+	int reg, len, ret;
+	const char *s;
+	int slen;
+
+	if (argc < 2 || argc > 3)
+		return CMD_RET_USAGE;
+
+	reg = simple_strtoul(argv[0], NULL, 16);
+
+	if (argc == 3) {
+		u8 *addr;
+
+		len = simple_strtoul(argv[1], NULL, 16);
+		addr = map_sysmem(simple_strtoul(argv[2], NULL, 16), len);
+		ret = dm_rtc_write(dev, reg, addr, len);
+		unmap_sysmem(addr);
+		if (ret) {
+			printf("dm_rtc_write() failed: %d\n", ret);
+			return CMD_RET_FAILURE;
+		}
+		return CMD_RET_SUCCESS;
+	}
+
+	s = argv[1];
+	slen = strlen(s);
+
+	if (slen % 2) {
+		printf("invalid hex string\n");
+		return CMD_RET_FAILURE;
+	}
+
+	while (slen) {
+		len = min_t(int, slen / 2, sizeof(buf));
+		if (hex2bin(buf, s, len)) {
+			printf("invalid hex string\n");
+			return CMD_RET_FAILURE;
+		}
+
+		ret = dm_rtc_write(dev, reg, buf, len);
+		if (ret) {
+			printf("dm_rtc_write() failed: %d\n", ret);
+			return CMD_RET_FAILURE;
+		}
+		s += 2 * len;
+		slen -= 2 * len;
+	}
+
+	return CMD_RET_SUCCESS;
+}
+
+int do_rtc(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
+{
+	static int curr_rtc;
+	struct udevice *dev;
+	int ret, idx;
+
+	if (argc < 2)
+		return CMD_RET_USAGE;
+
+	argc--;
+	argv++;
+
+	if (!strcmp(argv[0], "list")) {
+		struct uclass *uc;
+
+		idx = 0;
+		uclass_id_foreach_dev(UCLASS_RTC, dev, uc) {
+			printf("RTC #%d - %s\n", idx++, dev->name);
+		}
+		if (!idx) {
+			printf("*** no RTC devices available ***\n");
+			return CMD_RET_FAILURE;
+		}
+		return CMD_RET_SUCCESS;
+	}
+
+	idx = curr_rtc;
+	if (!strcmp(argv[0], "dev") && argc >= 2)
+		idx = simple_strtoul(argv[1], NULL, 10);
+
+	ret = uclass_get_device(UCLASS_RTC, idx, &dev);
+	if (ret) {
+		printf("Cannot find RTC #%d: err=%d\n", idx, ret);
+		return CMD_RET_FAILURE;
+	}
+
+	if (!strcmp(argv[0], "dev")) {
+		/* Show the existing or newly selected RTC */
+		if (argc >= 2)
+			curr_rtc = idx;
+		printf("RTC #%d - %s\n", idx, dev->name);
+		return CMD_RET_SUCCESS;
+	}
+
+	if (!strcmp(argv[0], "read"))
+		return do_rtc_read(dev, argc - 1, argv + 1);
+
+	if (!strcmp(argv[0], "write"))
+		return do_rtc_write(dev, argc - 1, argv + 1);
+
+	return CMD_RET_USAGE;
+}
+
+U_BOOT_CMD(
+	rtc,	5,	0,	do_rtc,
+	"RTC subsystem",
+	"list                        - show available rtc devices\n"
+	"rtc dev [n]                     - show or set current rtc device\n"
+	"rtc read <reg> <count>          - read and display 8-bit registers starting at <reg>\n"
+	"rtc read <reg> <count> <addr>   - read 8-bit registers starting at <reg> to memory <addr>\n"
+	"rtc write <reg> <hexstring>     - write 8-bit registers starting at <reg>\n"
+	"rtc write <reg> <count> <addr>  - write from memory <addr> to 8-bit registers starting at <reg>\n"
+);

+ 3 - 0
configs/cortina_presidio-asic-emmc_defconfig

@@ -10,6 +10,7 @@ CONFIG_SHOW_BOOT_PROGRESS=y
 CONFIG_BOOTDELAY=3
 CONFIG_BOARD_EARLY_INIT_R=y
 CONFIG_SYS_PROMPT="G3#"
+CONFIG_CMD_I2C=y
 CONFIG_CMD_MMC=y
 CONFIG_CMD_PART=y
 CONFIG_CMD_WDT=y
@@ -24,6 +25,8 @@ CONFIG_DEFAULT_DEVICE_TREE="ca-presidio-engboard"
 # CONFIG_NET is not set
 CONFIG_DM=y
 CONFIG_CORTINA_GPIO=y
+CONFIG_DM_I2C=y
+CONFIG_SYS_I2C_CA=y
 CONFIG_DM_MMC=y
 CONFIG_MMC_DW=y
 CONFIG_MMC_DW_CORTINA=y

+ 1 - 0
configs/sandbox64_defconfig

@@ -60,6 +60,7 @@ CONFIG_CMD_LINK_LOCAL=y
 CONFIG_CMD_ETHSW=y
 CONFIG_CMD_BMP=y
 CONFIG_CMD_EFIDEBUG=y
+CONFIG_CMD_RTC=y
 CONFIG_CMD_TIME=y
 CONFIG_CMD_TIMER=y
 CONFIG_CMD_SOUND=y

+ 1 - 0
configs/sandbox_defconfig

@@ -69,6 +69,7 @@ CONFIG_CMD_ETHSW=y
 CONFIG_CMD_BMP=y
 CONFIG_CMD_BOOTCOUNT=y
 CONFIG_CMD_EFIDEBUG=y
+CONFIG_CMD_RTC=y
 CONFIG_CMD_TIME=y
 CONFIG_CMD_TIMER=y
 CONFIG_CMD_SOUND=y

+ 1 - 0
configs/sandbox_flattree_defconfig

@@ -49,6 +49,7 @@ CONFIG_CMD_SNTP=y
 CONFIG_CMD_DNS=y
 CONFIG_CMD_LINK_LOCAL=y
 CONFIG_CMD_EFIDEBUG=y
+CONFIG_CMD_RTC=y
 CONFIG_CMD_TIME=y
 CONFIG_CMD_TIMER=y
 CONFIG_CMD_SOUND=y

+ 18 - 0
doc/device-tree-bindings/i2c/i2c-cortina.txt

@@ -0,0 +1,18 @@
+* I2C for Cortina platforms
+
+Required properties :
+- compatible : Must be "cortina,ca-i2c"
+- reg : Offset and length of the register set for the device
+
+Recommended properties :
+- clock-frequency : desired I2C bus clock frequency in Hz. If not specified,
+		    default value is 100000. Possible values are 100000,
+		    400000 and 1000000.
+
+Examples :
+
+	i2c: i2c@f4329120 {
+		compatible = "cortina,ca-i2c";
+		reg = <0x0 0xf4329120 0x28>;
+		clock-frequency = <400000>;
+	};

+ 24 - 0
doc/device-tree-bindings/i2c/octeon-i2c.txt

@@ -0,0 +1,24 @@
+* I2C controller embedded in Marvell Octeon platforms
+
+Required properties :
+- compatible : Must be "cavium,octeon-7890-twsi" or a compatible string
+- reg : Offset and length of the register set for the device
+- clocks: Must contain the input clock of the I2C instance
+- #address-cells = <1>;
+- #size-cells = <0>;
+
+Optional properties :
+- clock-frequency : Desired I2C bus clock frequency in Hz. If not specified,
+  the default 100 kHz frequency will be used. As only Normal, Fast and Fast+
+  modes are implemented, possible values are 100000, 400000 and 1000000.
+
+Example :
+
+	i2c0: i2c@1180000001000 {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		compatible = "cavium,octeon-7890-twsi";
+		reg = <0x11800 0x00001000 0x0 0x200>;
+		clock-frequency = <100000>;
+		clocks = <&sclk>;
+	};

+ 31 - 0
doc/device-tree-bindings/pwm/pwm-sifive.txt

@@ -0,0 +1,31 @@
+SiFive PWM controller
+
+Unlike most other PWM controllers, the SiFive PWM controller currently only
+supports one period for all channels in the PWM. All PWMs need to run at
+the same period. The period also has significant restrictions on the values
+it can achieve, which the driver rounds to the nearest achievable period.
+PWM RTL that corresponds to the IP block version numbers can be found
+here:
+
+https://github.com/sifive/sifive-blocks/tree/master/src/main/scala/devices/pwm
+
+Required properties:
+- compatible: Should be "sifive,<chip>-pwm" and "sifive,pwm<version>".
+  Supported compatible strings are: "sifive,fu540-c000-pwm" for the SiFive
+  PWM v0 as integrated onto the SiFive FU540 chip, and "sifive,pwm0" for the
+  SiFive PWM v0 IP block with no chip integration tweaks.
+- reg: physical base address and length of the controller's registers
+- clocks: Should contain a clock identifier for the PWM's parent clock.
+- #pwm-cells: Should be 3.
+- interrupts: one interrupt per PWM channel
+
+Examples:
+
+pwm:  pwm@10020000 {
+	compatible = "sifive,fu540-c000-pwm", "sifive,pwm0";
+	reg = <0x0 0x10020000 0x0 0x1000>;
+	clocks = <&tlclk>;
+	interrupt-parent = <&plic>;
+	interrupts = <42 43 44 45>;
+	#pwm-cells = <3>;
+};

+ 18 - 0
drivers/i2c/Kconfig

@@ -93,6 +93,14 @@ config SYS_I2C_CADENCE
 	  Say yes here to select Cadence I2C Host Controller. This controller is
 	  e.g. used by Xilinx Zynq.
 
+config SYS_I2C_CA
+	tristate "Cortina-Access I2C Controller"
+	depends on DM_I2C && CORTINA_PLATFORM
+	default n
+	help
+	  Add support for the Cortina Access I2C host controller.
+	  Say yes here to select Cortina-Access I2C Host Controller.
+
 config SYS_I2C_DAVINCI
 	bool "Davinci I2C Controller"
 	depends on (ARCH_KEYSTONE || ARCH_DAVINCI)
@@ -374,6 +382,16 @@ config SYS_I2C_SANDBOX
 	  bus. Devices can be attached to the bus using the device tree
 	  which specifies the driver to use.  See sandbox.dts as an example.
 
+config SYS_I2C_OCTEON
+	bool "Octeon II/III/TX/TX2 I2C driver"
+	depends on (ARCH_OCTEON || ARCH_OCTEONTX || ARCH_OCTEONTX2) && DM_I2C
+	default y
+	help
+	  Add support for the Marvell Octeon I2C driver. This is used with
+	  various Octeon parts such as Octeon II/III and OcteonTX/TX2. All
+	  chips have several I2C ports and all are provided, controlled by
+	  the device tree.
+
 config SYS_I2C_S3C24X0
 	bool "Samsung I2C driver"
 	depends on ARCH_EXYNOS4 && DM_I2C

+ 2 - 0
drivers/i2c/Makefile

@@ -12,6 +12,7 @@ obj-$(CONFIG_SYS_I2C) += i2c_core.o
 obj-$(CONFIG_SYS_I2C_ASPEED) += ast_i2c.o
 obj-$(CONFIG_SYS_I2C_AT91) += at91_i2c.o
 obj-$(CONFIG_SYS_I2C_CADENCE) += i2c-cdns.o
+obj-$(CONFIG_SYS_I2C_CA) += i2c-cortina.o
 obj-$(CONFIG_SYS_I2C_DAVINCI) += davinci_i2c.o
 obj-$(CONFIG_SYS_I2C_DW) += designware_i2c.o
 ifdef CONFIG_DM_PCI
@@ -27,6 +28,7 @@ obj-$(CONFIG_SYS_I2C_LPC32XX) += lpc32xx_i2c.o
 obj-$(CONFIG_SYS_I2C_MESON) += meson_i2c.o
 obj-$(CONFIG_SYS_I2C_MVTWSI) += mvtwsi.o
 obj-$(CONFIG_SYS_I2C_MXC) += mxc_i2c.o
+obj-$(CONFIG_SYS_I2C_OCTEON) += octeon_i2c.o
 obj-$(CONFIG_SYS_I2C_OMAP24XX) += omap24xx_i2c.o
 obj-$(CONFIG_SYS_I2C_RCAR_I2C) += rcar_i2c.o
 obj-$(CONFIG_SYS_I2C_RCAR_IIC) += rcar_iic.o

+ 347 - 0
drivers/i2c/i2c-cortina.c

@@ -0,0 +1,347 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2020
+ * Arthur Li, Cortina Access, arthur.li@cortina-access.com.
+ */
+
+#include <common.h>
+#include <i2c.h>
+#include <log.h>
+#include <asm/io.h>
+#include <dm.h>
+#include <mapmem.h>
+#include "i2c-cortina.h"
+
+static void set_speed(struct i2c_regs *regs, int i2c_spd)
+{
+	union ca_biw_cfg i2c_cfg;
+
+	i2c_cfg.wrd = readl(&regs->i2c_cfg);
+	i2c_cfg.bf.core_en = 0;
+	writel(i2c_cfg.wrd, &regs->i2c_cfg);
+
+	switch (i2c_spd) {
+	case IC_SPEED_MODE_FAST_PLUS:
+		i2c_cfg.bf.prer = CORTINA_PER_IO_FREQ /
+				  (5 * I2C_SPEED_FAST_PLUS_RATE) - 1;
+		break;
+
+	case IC_SPEED_MODE_STANDARD:
+		i2c_cfg.bf.prer = CORTINA_PER_IO_FREQ /
+				  (5 * I2C_SPEED_STANDARD_RATE) - 1;
+		break;
+
+	case IC_SPEED_MODE_FAST:
+	default:
+		i2c_cfg.bf.prer = CORTINA_PER_IO_FREQ /
+				  (5 * I2C_SPEED_FAST_RATE) - 1;
+		break;
+	}
+
+	i2c_cfg.bf.core_en = 1;
+	writel(i2c_cfg.wrd, &regs->i2c_cfg);
+}
+
+static int ca_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
+{
+	struct ca_i2c *priv = dev_get_priv(bus);
+	int i2c_spd;
+
+	if (speed >= I2C_SPEED_FAST_PLUS_RATE) {
+		i2c_spd = IC_SPEED_MODE_FAST_PLUS;
+		priv->speed = I2C_SPEED_FAST_PLUS_RATE;
+	} else if (speed >= I2C_SPEED_FAST_RATE) {
+		i2c_spd = IC_SPEED_MODE_FAST;
+		priv->speed = I2C_SPEED_FAST_RATE;
+	} else {
+		i2c_spd = IC_SPEED_MODE_STANDARD;
+		priv->speed = I2C_SPEED_STANDARD_RATE;
+	}
+
+	set_speed(priv->regs, i2c_spd);
+
+	return 0;
+}
+
+static int ca_i2c_get_bus_speed(struct udevice *bus)
+{
+	struct ca_i2c *priv = dev_get_priv(bus);
+
+	return priv->speed;
+}
+
+static void ca_i2c_init(struct i2c_regs *regs)
+{
+	union ca_biw_cfg i2c_cfg;
+
+	i2c_cfg.wrd = readl(&regs->i2c_cfg);
+	i2c_cfg.bf.core_en = 0;
+	i2c_cfg.bf.biw_soft_reset = 1;
+	writel(i2c_cfg.wrd, &regs->i2c_cfg);
+	mdelay(10);
+	i2c_cfg.bf.biw_soft_reset = 0;
+	writel(i2c_cfg.wrd, &regs->i2c_cfg);
+
+	set_speed(regs, IC_SPEED_MODE_STANDARD);
+
+	i2c_cfg.wrd = readl(&regs->i2c_cfg);
+	i2c_cfg.bf.core_en = 1;
+	writel(i2c_cfg.wrd, &regs->i2c_cfg);
+}
+
+static int i2c_wait_complete(struct i2c_regs *regs)
+{
+	union ca_biw_ctrl i2c_ctrl;
+	unsigned long start_time_bb = get_timer(0);
+
+	i2c_ctrl.wrd = readl(&regs->i2c_ctrl);
+
+	while (i2c_ctrl.bf.biwdone == 0) {
+		i2c_ctrl.wrd = readl(&regs->i2c_ctrl);
+
+		if (get_timer(start_time_bb) >
+		   (unsigned long)(I2C_BYTE_TO_BB)) {
+			printf("%s not done!!!\n", __func__);
+			return -ETIMEDOUT;
+		}
+	}
+
+	/* Clear done bit */
+	writel(i2c_ctrl.wrd, &regs->i2c_ctrl);
+
+	return 0;
+}
+
+static void i2c_setaddress(struct i2c_regs *regs, unsigned int i2c_addr,
+			   int write_read)
+{
+	writel(i2c_addr | write_read, &regs->i2c_txr);
+
+	writel(BIW_CTRL_START | BIW_CTRL_WRITE,
+	       &regs->i2c_ctrl);
+
+	i2c_wait_complete(regs);
+}
+
+static int i2c_wait_for_bus_busy(struct i2c_regs *regs)
+{
+	union ca_biw_ack i2c_ack;
+	unsigned long start_time_bb = get_timer(0);
+
+	i2c_ack.wrd = readl(&regs->i2c_ack);
+
+	while (i2c_ack.bf.biw_busy) {
+		i2c_ack.wrd = readl(&regs->i2c_ack);
+
+		if (get_timer(start_time_bb) >
+		   (unsigned long)(I2C_BYTE_TO_BB)) {
+			printf("%s: timeout!\n", __func__);
+			return -ETIMEDOUT;
+		}
+	}
+
+	return 0;
+}
+
+static int i2c_xfer_init(struct i2c_regs *regs, uint8_t chip, uint addr,
+			 int alen, int write_read)
+{
+	int addr_len = alen;
+
+	if (i2c_wait_for_bus_busy(regs))
+		return 1;
+
+	/* First cycle must write addr + offset */
+	chip = ((chip & 0x7F) << 1);
+	if (alen == 0 && write_read == I2C_CMD_RD)
+		i2c_setaddress(regs, chip, I2C_CMD_RD);
+	else
+		i2c_setaddress(regs, chip, I2C_CMD_WT);
+
+	while (alen) {
+		alen--;
+		writel(addr, &regs->i2c_txr);
+		if (write_read == I2C_CMD_RD)
+			writel(BIW_CTRL_WRITE | BIW_CTRL_STOP,
+			       &regs->i2c_ctrl);
+		else
+			writel(BIW_CTRL_WRITE, &regs->i2c_ctrl);
+		i2c_wait_complete(regs);
+	}
+
+	/* Send address again with Read flag if it's read command */
+	if (write_read == I2C_CMD_RD && addr_len > 0)
+		i2c_setaddress(regs, chip, I2C_CMD_RD);
+
+	return 0;
+}
+
+static int i2c_xfer_finish(struct i2c_regs *regs)
+{
+	/* Dummy read makes bus free */
+	writel(BIW_CTRL_READ | BIW_CTRL_STOP, &regs->i2c_ctrl);
+	i2c_wait_complete(regs);
+
+	if (i2c_wait_for_bus_busy(regs)) {
+		printf("Timed out waiting for bus\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static int ca_i2c_read(struct i2c_regs *regs, uint8_t chip, uint addr,
+		       int alen, uint8_t *buffer, int len)
+{
+	unsigned long start_time_rx;
+	int rc = 0;
+
+	rc = i2c_xfer_init(regs, chip, addr, alen, I2C_CMD_RD);
+	if (rc)
+		return rc;
+
+	start_time_rx = get_timer(0);
+	while (len) {
+		/* ACK_IN is ack value to send during read.
+		 * ack high only on the very last byte!
+		 */
+		if (len == 1)
+			writel(BIW_CTRL_READ | BIW_CTRL_ACK_IN | BIW_CTRL_STOP,
+			       &regs->i2c_ctrl);
+		else
+			writel(BIW_CTRL_READ, &regs->i2c_ctrl);
+
+		rc = i2c_wait_complete(regs);
+		udelay(1);
+
+		if (rc == 0) {
+			*buffer++ =
+				(uchar) readl(&regs->i2c_rxr);
+			len--;
+			start_time_rx = get_timer(0);
+
+		} else if (get_timer(start_time_rx) > I2C_BYTE_TO) {
+			return -ETIMEDOUT;
+		}
+	}
+	i2c_xfer_finish(regs);
+	return rc;
+}
+
+static int ca_i2c_write(struct i2c_regs *regs, uint8_t chip, uint addr,
+			int alen, uint8_t *buffer, int len)
+{
+	int rc, nb = len;
+	unsigned long start_time_tx;
+
+	rc = i2c_xfer_init(regs, chip, addr, alen, I2C_CMD_WT);
+	if (rc)
+		return rc;
+
+	start_time_tx = get_timer(0);
+	while (len) {
+		writel(*buffer, &regs->i2c_txr);
+		if (len == 1)
+			writel(BIW_CTRL_WRITE | BIW_CTRL_STOP,
+			       &regs->i2c_ctrl);
+		else
+			writel(BIW_CTRL_WRITE, &regs->i2c_ctrl);
+
+		rc = i2c_wait_complete(regs);
+
+		if (rc == 0) {
+			len--;
+			buffer++;
+			start_time_tx = get_timer(0);
+		} else if (get_timer(start_time_tx) > (nb * I2C_BYTE_TO)) {
+			return -ETIMEDOUT;
+		}
+	}
+
+	return 0;
+}
+
+static int ca_i2c_probe_chip(struct udevice *bus, uint chip_addr,
+			     uint chip_flags)
+{
+	struct ca_i2c *priv = dev_get_priv(bus);
+	int ret;
+	u32 tmp;
+
+	/* Try to read the first location of the chip */
+	ret = ca_i2c_read(priv->regs, chip_addr, 0, 1, (uchar *)&tmp, 1);
+	if (ret)
+		ca_i2c_init(priv->regs);
+
+	return ret;
+}
+
+static int ca_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs)
+{
+	struct ca_i2c *priv = dev_get_priv(bus);
+	int ret;
+
+	debug("i2c_xfer: %d messages\n", nmsgs);
+	for (; nmsgs > 0; nmsgs--, msg++) {
+		debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len);
+		if (msg->flags & I2C_M_RD)
+			ret = ca_i2c_read(priv->regs, msg->addr, 0, 0,
+					  msg->buf, msg->len);
+		else
+			ret = ca_i2c_write(priv->regs, msg->addr, 0, 0,
+					   msg->buf, msg->len);
+
+		if (ret) {
+			printf("i2c_xfer: %s error\n",
+			       msg->flags & I2C_M_RD ? "read" : "write");
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static const struct dm_i2c_ops ca_i2c_ops = {
+	.xfer		= ca_i2c_xfer,
+	.probe_chip	= ca_i2c_probe_chip,
+	.set_bus_speed	= ca_i2c_set_bus_speed,
+	.get_bus_speed	= ca_i2c_get_bus_speed,
+};
+
+static const struct udevice_id ca_i2c_ids[] = {
+	{ .compatible = "cortina,ca-i2c", },
+	{ }
+};
+
+static int ca_i2c_probe(struct udevice *bus)
+{
+	struct ca_i2c *priv = dev_get_priv(bus);
+
+	ca_i2c_init(priv->regs);
+
+	return 0;
+}
+
+static int ca_i2c_ofdata_to_platdata(struct udevice *bus)
+{
+	struct ca_i2c *priv = dev_get_priv(bus);
+
+	priv->regs = map_sysmem(dev_read_addr(bus), sizeof(struct i2c_regs));
+	if (!priv->regs) {
+		printf("I2C: base address is invalid\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+U_BOOT_DRIVER(i2c_cortina) = {
+	.name	= "i2c_cortina",
+	.id	= UCLASS_I2C,
+	.of_match = ca_i2c_ids,
+	.ofdata_to_platdata = ca_i2c_ofdata_to_platdata,
+	.probe	= ca_i2c_probe,
+	.priv_auto_alloc_size = sizeof(struct ca_i2c),
+	.ops	= &ca_i2c_ops,
+	.flags  = DM_FLAG_PRE_RELOC,
+};

+ 87 - 0
drivers/i2c/i2c-cortina.h

@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (C) Copyright 2019
+ * Cortina Access, <www.cortina-access.com>
+ */
+
+#ifndef __CA_I2C_H_
+#define __CA_I2C_H_
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+
+#if !defined(__ASSEMBLER__) && !defined(__ASSEMBLY__)
+struct i2c_regs {
+	u32 i2c_cfg;
+	u32 i2c_ctrl;
+	u32 i2c_txr;
+	u32 i2c_rxr;
+	u32 i2c_ack;
+	u32 i2c_ie0;
+	u32 i2c_int0;
+	u32 i2c_ie1;
+	u32 i2c_int1;
+	u32 i2c_stat;
+};
+
+union ca_biw_cfg {
+	struct biw_cfg {
+		u32 core_en		: 1;
+		u32 biw_soft_reset	: 1;
+		u32 busywait_en		: 1;
+		u32 stretch_en		: 1;
+		u32 arb_en		: 1;
+		u32 clksync_en		: 1;
+		u32 rsrvd1		: 2;
+		u32 spike_cnt		: 4;
+		u32 rsrvd2		: 4;
+		u32 prer		: 16;
+	} bf;
+	unsigned int wrd;
+};
+
+union ca_biw_ctrl {
+	struct biw_ctrl {
+		u32 biwdone	: 1;
+		u32 rsrvd1	: 2;
+		u32 ack_in	: 1;
+		u32 write	: 1;
+		u32 read	: 1;
+		u32 stop	: 1;
+		u32 start	: 1;
+		u32 rsrvd2	: 24;
+	} bf;
+	unsigned int wrd;
+};
+
+union ca_biw_ack {
+	struct biw_ack {
+		u32 al		:1;
+		u32 biw_busy	:1;
+		u32 ack_out	:1;
+		u32 rsrvd1	:29;
+	} bf;
+	unsigned int wrd;
+};
+#endif /* !__ASSEMBLER__*/
+
+struct ca_i2c {
+	struct i2c_regs *regs;
+	unsigned int speed;
+};
+
+#define I2C_CMD_WT			0
+#define I2C_CMD_RD			1
+
+#define BIW_CTRL_DONE		BIT(0)
+#define BIW_CTRL_ACK_IN		BIT(3)
+#define BIW_CTRL_WRITE		BIT(4)
+#define BIW_CTRL_READ		BIT(5)
+#define BIW_CTRL_STOP		BIT(6)
+#define BIW_CTRL_START		BIT(7)
+
+#define I2C_BYTE_TO		(CONFIG_SYS_HZ / 500)
+#define I2C_STOPDET_TO		(CONFIG_SYS_HZ / 500)
+#define I2C_BYTE_TO_BB		(10)
+
+#endif							/* __CA_I2C_H_ */

+ 11 - 11
drivers/i2c/imx_lpi2c.c

@@ -97,7 +97,8 @@ static int bus_i2c_wait_for_tx_ready(struct imx_lpi2c_reg *regs)
 
 static int bus_i2c_send(struct udevice *bus, u8 *txbuf, int len)
 {
-	struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)devfdt_get_addr(bus);
+	struct imx_lpi2c_bus *i2c_bus = dev_get_priv(bus);
+	struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)(i2c_bus->base);
 	lpi2c_status_t result = LPI2C_SUCESS;
 
 	/* empty tx */
@@ -118,7 +119,8 @@ static int bus_i2c_send(struct udevice *bus, u8 *txbuf, int len)
 
 static int bus_i2c_receive(struct udevice *bus, u8 *rxbuf, int len)
 {
-	struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)devfdt_get_addr(bus);
+	struct imx_lpi2c_bus *i2c_bus = dev_get_priv(bus);
+	struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)(i2c_bus->base);
 	lpi2c_status_t result = LPI2C_SUCESS;
 	u32 val;
 	ulong start_time = get_timer(0);
@@ -162,8 +164,8 @@ static int bus_i2c_receive(struct udevice *bus, u8 *rxbuf, int len)
 static int bus_i2c_start(struct udevice *bus, u8 addr, u8 dir)
 {
 	lpi2c_status_t result;
-	struct imx_lpi2c_reg *regs =
-		(struct imx_lpi2c_reg *)devfdt_get_addr(bus);
+	struct imx_lpi2c_bus *i2c_bus = dev_get_priv(bus);
+	struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)(i2c_bus->base);
 	u32 val;
 
 	result = imx_lpci2c_check_busy_bus(regs);
@@ -199,8 +201,8 @@ static int bus_i2c_start(struct udevice *bus, u8 addr, u8 dir)
 static int bus_i2c_stop(struct udevice *bus)
 {
 	lpi2c_status_t result;
-	struct imx_lpi2c_reg *regs =
-		(struct imx_lpi2c_reg *)devfdt_get_addr(bus);
+	struct imx_lpi2c_bus *i2c_bus = dev_get_priv(bus);
+	struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)(i2c_bus->base);
 	u32 status;
 	ulong start_time;
 
@@ -271,7 +273,7 @@ u32 __weak imx_get_i2cclk(u32 i2c_num)
 static int bus_i2c_set_bus_speed(struct udevice *bus, int speed)
 {
 	struct imx_lpi2c_bus *i2c_bus = dev_get_priv(bus);
-	struct imx_lpi2c_reg *regs;
+	struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)(i2c_bus->base);
 	u32 val;
 	u32 preescale = 0, best_pre = 0, clkhi = 0;
 	u32 best_clkhi = 0, abs_error = 0, rate;
@@ -280,8 +282,6 @@ static int bus_i2c_set_bus_speed(struct udevice *bus, int speed)
 	bool mode;
 	int i;
 
-	regs = (struct imx_lpi2c_reg *)devfdt_get_addr(bus);
-
 	if (IS_ENABLED(CONFIG_CLK)) {
 		clock_rate = clk_get_rate(&i2c_bus->per_clk);
 		if (clock_rate <= 0) {
@@ -348,11 +348,11 @@ static int bus_i2c_set_bus_speed(struct udevice *bus, int speed)
 
 static int bus_i2c_init(struct udevice *bus, int speed)
 {
-	struct imx_lpi2c_reg *regs;
 	u32 val;
 	int ret;
 
-	regs = (struct imx_lpi2c_reg *)devfdt_get_addr(bus);
+	struct imx_lpi2c_bus *i2c_bus = dev_get_priv(bus);
+	struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)(i2c_bus->base);
 	/* reset peripheral */
 	writel(LPI2C_MCR_RST_MASK, &regs->mcr);
 	writel(0x0, &regs->mcr);

+ 847 - 0
drivers/i2c/octeon_i2c.c

@@ -0,0 +1,847 @@
+// SPDX-License-Identifier:    GPL-2.0
+/*
+ * Copyright (C) 2018 Marvell International Ltd.
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <i2c.h>
+#include <pci_ids.h>
+#include <asm/io.h>
+#include <linux/bitfield.h>
+#include <linux/compat.h>
+#include <linux/delay.h>
+
+#define TWSI_SW_TWSI		0x00
+#define TWSI_TWSI_SW		0x08
+#define TWSI_INT		0x10
+#define TWSI_SW_TWSI_EXT	0x18
+
+#define TWSI_SW_DATA_MASK	GENMASK_ULL(31, 0)
+#define TWSI_SW_EOP_IA_MASK	GENMASK_ULL(34, 32)
+#define TWSI_SW_IA_MASK		GENMASK_ULL(39, 35)
+#define TWSI_SW_ADDR_MASK	GENMASK_ULL(49, 40)
+#define TWSI_SW_SCR_MASK	GENMASK_ULL(51, 50)
+#define TWSI_SW_SIZE_MASK	GENMASK_ULL(54, 52)
+#define TWSI_SW_SOVR		BIT_ULL(55)
+#define TWSI_SW_R		BIT_ULL(56)
+#define TWSI_SW_OP_MASK		GENMASK_ULL(60, 57)
+#define TWSI_SW_EIA		GENMASK_ULL(61)
+#define TWSI_SW_SLONLY		BIT_ULL(62)
+#define TWSI_SW_V		BIT_ULL(63)
+
+#define TWSI_INT_SDA_OVR	BIT_ULL(8)
+#define TWSI_INT_SCL_OVR	BIT_ULL(9)
+#define TWSI_INT_SDA		BIT_ULL(10)
+#define TWSI_INT_SCL		BIT_ULL(11)
+
+enum {
+	TWSI_OP_WRITE	= 0,
+	TWSI_OP_READ	= 1,
+};
+
+enum {
+	TWSI_EOP_SLAVE_ADDR = 0,
+	TWSI_EOP_CLK_CTL = 3,
+	TWSI_SW_EOP_IA   = 6,
+};
+
+enum {
+	TWSI_SLAVEADD     = 0,
+	TWSI_DATA         = 1,
+	TWSI_CTL          = 2,
+	TWSI_CLKCTL       = 3,
+	TWSI_STAT         = 3,
+	TWSI_SLAVEADD_EXT = 4,
+	TWSI_RST          = 7,
+};
+
+enum {
+	TWSI_CTL_AAK	= BIT(2),
+	TWSI_CTL_IFLG	= BIT(3),
+	TWSI_CTL_STP	= BIT(4),
+	TWSI_CTL_STA	= BIT(5),
+	TWSI_CTL_ENAB	= BIT(6),
+	TWSI_CTL_CE	= BIT(7),
+};
+
+/*
+ * Internal errors. When debugging is enabled, the driver will report the
+ * error number and the user / developer can check the table below for the
+ * detailed error description.
+ */
+enum {
+	/** Bus error */
+	TWSI_STAT_BUS_ERROR		= 0x00,
+	/** Start condition transmitted */
+	TWSI_STAT_START			= 0x08,
+	/** Repeat start condition transmitted */
+	TWSI_STAT_RSTART		= 0x10,
+	/** Address + write bit transmitted, ACK received */
+	TWSI_STAT_TXADDR_ACK		= 0x18,
+	/** Address + write bit transmitted, /ACK received */
+	TWSI_STAT_TXADDR_NAK		= 0x20,
+	/** Data byte transmitted in master mode, ACK received */
+	TWSI_STAT_TXDATA_ACK		= 0x28,
+	/** Data byte transmitted in master mode, ACK received */
+	TWSI_STAT_TXDATA_NAK		= 0x30,
+	/** Arbitration lost in address or data byte */
+	TWSI_STAT_TX_ARB_LOST		= 0x38,
+	/** Address + read bit transmitted, ACK received */
+	TWSI_STAT_RXADDR_ACK		= 0x40,
+	/** Address + read bit transmitted, /ACK received */
+	TWSI_STAT_RXADDR_NAK		= 0x48,
+	/** Data byte received in master mode, ACK transmitted */
+	TWSI_STAT_RXDATA_ACK_SENT	= 0x50,
+	/** Data byte received, NACK transmitted */
+	TWSI_STAT_RXDATA_NAK_SENT	= 0x58,
+	/** Slave address received, sent ACK */
+	TWSI_STAT_SLAVE_RXADDR_ACK	= 0x60,
+	/**
+	 * Arbitration lost in address as master, slave address + write bit
+	 * received, ACK transmitted
+	 */
+	TWSI_STAT_TX_ACK_ARB_LOST	= 0x68,
+	/** General call address received, ACK transmitted */
+	TWSI_STAT_RX_GEN_ADDR_ACK	= 0x70,
+	/**
+	 * Arbitration lost in address as master, general call address
+	 * received, ACK transmitted
+	 */
+	TWSI_STAT_RX_GEN_ADDR_ARB_LOST	= 0x78,
+	/** Data byte received after slave address received, ACK transmitted */
+	TWSI_STAT_SLAVE_RXDATA_ACK	= 0x80,
+	/** Data byte received after slave address received, /ACK transmitted */
+	TWSI_STAT_SLAVE_RXDATA_NAK	= 0x88,
+	/**
+	 * Data byte received after general call address received, ACK
+	 * transmitted
+	 */
+	TWSI_STAT_GEN_RXADDR_ACK	= 0x90,
+	/**
+	 * Data byte received after general call address received, /ACK
+	 * transmitted
+	 */
+	TWSI_STAT_GEN_RXADDR_NAK	= 0x98,
+	/** STOP or repeated START condition received in slave mode */
+	TWSI_STAT_STOP_MULTI_START	= 0xa0,
+	/** Slave address + read bit received, ACK transmitted */
+	TWSI_STAT_SLAVE_RXADDR2_ACK	= 0xa8,
+	/**
+	 * Arbitration lost in address as master, slave address + read bit
+	 * received, ACK transmitted
+	 */
+	TWSI_STAT_RXDATA_ACK_ARB_LOST	= 0xb0,
+	/** Data byte transmitted in slave mode, ACK received */
+	TWSI_STAT_SLAVE_TXDATA_ACK	= 0xb8,
+	/** Data byte transmitted in slave mode, /ACK received */
+	TWSI_STAT_SLAVE_TXDATA_NAK	= 0xc0,
+	/** Last byte transmitted in slave mode, ACK received */
+	TWSI_STAT_SLAVE_TXDATA_END_ACK	= 0xc8,
+	/** Second address byte + write bit transmitted, ACK received */
+	TWSI_STAT_TXADDR2DATA_ACK	= 0xd0,
+	/** Second address byte + write bit transmitted, /ACK received */
+	TWSI_STAT_TXADDR2DATA_NAK	= 0xd8,
+	/** No relevant status information */
+	TWSI_STAT_IDLE			= 0xf8
+};
+
+#define CONFIG_SYS_I2C_OCTEON_SLAVE_ADDR	0x77
+
+enum {
+	PROBE_PCI = 0,		/* PCI based probing */
+	PROBE_DT,		/* DT based probing */
+};
+
+enum {
+	CLK_METHOD_OCTEON = 0,
+	CLK_METHOD_OCTEONTX2,
+};
+
+/**
+ * struct octeon_i2c_data - SoC specific data of this driver
+ *
+ * @probe:	Probing of this SoC (DT vs PCI)
+ * @reg_offs:	Register offset
+ * @thp:	THP define for divider calculation
+ * @clk_method:	Clock calculation method
+ */
+struct octeon_i2c_data {
+	int probe;
+	u32 reg_offs;
+	int thp;
+	int clk_method;
+};
+
+/**
+ * struct octeon_twsi - Private data of this driver
+ *
+ * @base:	Base address of i2c registers
+ * @data:	Pointer to SoC specific data struct
+ */
+struct octeon_twsi {
+	void __iomem *base;
+	const struct octeon_i2c_data *data;
+	struct clk clk;
+};
+
+static void twsi_unblock(void *base);
+static int twsi_stop(void *base);
+
+/**
+ * Returns true if we lost arbitration
+ *
+ * @code	status code
+ * @final_read	true if this is the final read operation
+ * @return	true if arbitration has been lost, false if it hasn't been lost.
+ */
+static int twsi_i2c_lost_arb(u8 code, int final_read)
+{
+	switch (code) {
+	case TWSI_STAT_TX_ARB_LOST:
+	case TWSI_STAT_TX_ACK_ARB_LOST:
+	case TWSI_STAT_RX_GEN_ADDR_ARB_LOST:
+	case TWSI_STAT_RXDATA_ACK_ARB_LOST:
+		/* Arbitration lost */
+		return -EAGAIN;
+
+	case TWSI_STAT_SLAVE_RXADDR_ACK:
+	case TWSI_STAT_RX_GEN_ADDR_ACK:
+	case TWSI_STAT_GEN_RXADDR_ACK:
+	case TWSI_STAT_GEN_RXADDR_NAK:
+		/* Being addressed as slave, should back off and listen */
+		return -EIO;
+
+	case TWSI_STAT_SLAVE_RXDATA_ACK:
+	case TWSI_STAT_SLAVE_RXDATA_NAK:
+	case TWSI_STAT_STOP_MULTI_START:
+	case TWSI_STAT_SLAVE_RXADDR2_ACK:
+	case TWSI_STAT_SLAVE_TXDATA_ACK:
+	case TWSI_STAT_SLAVE_TXDATA_NAK:
+	case TWSI_STAT_SLAVE_TXDATA_END_ACK:
+		/* Core busy as slave */
+		return  -EIO;
+
+	case TWSI_STAT_RXDATA_ACK_SENT:
+		/* Ack allowed on pre-terminal bytes only */
+		if (!final_read)
+			return 0;
+		return -EAGAIN;
+
+	case TWSI_STAT_RXDATA_NAK_SENT:
+		/* NAK allowed on terminal byte only */
+		if (!final_read)
+			return 0;
+		return -EAGAIN;
+
+	case TWSI_STAT_TXDATA_NAK:
+	case TWSI_STAT_TXADDR_NAK:
+	case TWSI_STAT_RXADDR_NAK:
+	case TWSI_STAT_TXADDR2DATA_NAK:
+		return -EAGAIN;
+	}
+
+	return 0;
+}
+
+/**
+ * Writes to the MIO_TWS(0..5)_SW_TWSI register
+ *
+ * @base	Base address of i2c registers
+ * @val		value to write
+ * @return	0 for success, otherwise error
+ */
+static u64 twsi_write_sw(void __iomem *base, u64 val)
+{
+	unsigned long start = get_timer(0);
+
+	val &= ~TWSI_SW_R;
+	val |= TWSI_SW_V;
+
+	debug("%s(%p, 0x%llx)\n", __func__, base, val);
+	writeq(val, base + TWSI_SW_TWSI);
+	do {
+		val = readq(base + TWSI_SW_TWSI);
+	} while ((val & TWSI_SW_V) && (get_timer(start) < 50));
+
+	if (val & TWSI_SW_V)
+		debug("%s: timed out\n", __func__);
+	return val;
+}
+
+/**
+ * Reads the MIO_TWS(0..5)_SW_TWSI register
+ *
+ * @base	Base address of i2c registers
+ * @val		value for eia and op, etc. to read
+ * @return	value of the register
+ */
+static u64 twsi_read_sw(void __iomem *base, u64 val)
+{
+	unsigned long start = get_timer(0);
+
+	val |= TWSI_SW_R | TWSI_SW_V;
+
+	debug("%s(%p, 0x%llx)\n", __func__, base, val);
+	writeq(val, base + TWSI_SW_TWSI);
+
+	do {
+		val = readq(base + TWSI_SW_TWSI);
+	} while ((val & TWSI_SW_V) && (get_timer(start) < 50));
+
+	if (val & TWSI_SW_V)
+		debug("%s: Error writing 0x%llx\n", __func__, val);
+
+	debug("%s: Returning 0x%llx\n", __func__, val);
+	return val;
+}
+
+/**
+ * Write control register
+ *
+ * @base	Base address for i2c registers
+ * @data	data to write
+ */
+static void twsi_write_ctl(void __iomem *base, u8 data)
+{
+	u64 val;
+
+	debug("%s(%p, 0x%x)\n", __func__, base, data);
+	val = data | FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_CTL) |
+		FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
+	twsi_write_sw(base, val);
+}
+
+/**
+ * Reads the TWSI Control Register
+ *
+ * @base	Base address for i2c
+ * @return	8-bit TWSI control register
+ */
+static u8 twsi_read_ctl(void __iomem *base)
+{
+	u64 val;
+
+	val = FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_CTL) |
+		FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
+	val = twsi_read_sw(base, val);
+
+	debug("%s(%p): 0x%x\n", __func__, base, (u8)val);
+	return (u8)val;
+}
+
+/**
+ * Read i2c status register
+ *
+ * @base	Base address of i2c registers
+ * @return	value of status register
+ */
+static u8 twsi_read_status(void __iomem *base)
+{
+	u64 val;
+
+	val = FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_STAT) |
+		FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
+
+	return twsi_read_sw(base, val);
+}
+
+/**
+ * Waits for an i2c operation to complete
+ *
+ * @param	base	Base address of registers
+ * @return	0 for success, 1 if timeout
+ */
+static int twsi_wait(void __iomem *base)
+{
+	unsigned long start = get_timer(0);
+	u8 twsi_ctl;
+
+	debug("%s(%p)\n", __func__, base);
+	do {
+		twsi_ctl = twsi_read_ctl(base);
+		twsi_ctl &= TWSI_CTL_IFLG;
+	} while (!twsi_ctl && get_timer(start) < 50);
+
+	debug("  return: %u\n", !twsi_ctl);
+	return !twsi_ctl;
+}
+
+/**
+ * Unsticks the i2c bus
+ *
+ * @base	base address of registers
+ */
+static int twsi_start_unstick(void __iomem *base)
+{
+	twsi_stop(base);
+	twsi_unblock(base);
+
+	return 0;
+}
+
+/**
+ * Sends an i2c start condition
+ *
+ * @base	base address of registers
+ * @return	0 for success, otherwise error
+ */
+static int twsi_start(void __iomem *base)
+{
+	int ret;
+	u8 stat;
+
+	debug("%s(%p)\n", __func__, base);
+	twsi_write_ctl(base, TWSI_CTL_STA | TWSI_CTL_ENAB);
+	ret = twsi_wait(base);
+	if (ret) {
+		stat = twsi_read_status(base);
+		debug("%s: ret: 0x%x, status: 0x%x\n", __func__, ret, stat);
+		switch (stat) {
+		case TWSI_STAT_START:
+		case TWSI_STAT_RSTART:
+			return 0;
+		case TWSI_STAT_RXADDR_ACK:
+		default:
+			return twsi_start_unstick(base);
+		}
+	}
+
+	debug("%s: success\n", __func__);
+	return 0;
+}
+
+/**
+ * Sends an i2c stop condition
+ *
+ * @base	register base address
+ * @return	0 for success, -1 if error
+ */
+static int twsi_stop(void __iomem *base)
+{
+	u8 stat;
+
+	twsi_write_ctl(base, TWSI_CTL_STP | TWSI_CTL_ENAB);
+
+	stat = twsi_read_status(base);
+	if (stat != TWSI_STAT_IDLE) {
+		debug("%s: Bad status on bus@%p\n", __func__, base);
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
+ * Writes data to the i2c bus
+ *
+ * @base	register base address
+ * @slave_addr	address of slave to write to
+ * @buffer	Pointer to buffer to write
+ * @length	Number of bytes in buffer to write
+ * @return	0 for success, otherwise error
+ */
+static int twsi_write_data(void __iomem *base, u8  slave_addr,
+			   u8 *buffer, unsigned int length)
+{
+	unsigned int curr = 0;
+	u64 val;
+	int ret;
+
+	debug("%s(%p, 0x%x, %p, 0x%x)\n", __func__, base, slave_addr,
+	      buffer, length);
+	ret = twsi_start(base);
+	if (ret) {
+		debug("%s: Could not start BUS transaction\n", __func__);
+		return -1;
+	}
+
+	ret = twsi_wait(base);
+	if (ret) {
+		debug("%s: wait failed\n", __func__);
+		return ret;
+	}
+
+	val = (u32)(slave_addr << 1) | TWSI_OP_WRITE |
+		FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_DATA) |
+		FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
+	twsi_write_sw(base, val);
+	twsi_write_ctl(base, TWSI_CTL_ENAB);
+
+	debug("%s: Waiting\n", __func__);
+	ret = twsi_wait(base);
+	if (ret) {
+		debug("%s: Timed out writing slave address 0x%x to target\n",
+		      __func__, slave_addr);
+		return ret;
+	}
+
+	ret = twsi_read_status(base);
+	debug("%s: status: 0x%x\n", __func__, ret);
+	if (ret != TWSI_STAT_TXADDR_ACK) {
+		debug("%s: status: 0x%x\n", __func__, ret);
+		twsi_stop(base);
+		return twsi_i2c_lost_arb(ret, 0);
+	}
+
+	while (curr < length) {
+		val = buffer[curr++] |
+			FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_DATA) |
+			FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
+		twsi_write_sw(base, val);
+		twsi_write_ctl(base, TWSI_CTL_ENAB);
+
+		debug("%s: Writing 0x%llx\n", __func__, val);
+
+		ret = twsi_wait(base);
+		if (ret) {
+			debug("%s: Timed out writing data to 0x%x\n",
+			      __func__, slave_addr);
+			return ret;
+		}
+		ret = twsi_read_status(base);
+		debug("%s: status: 0x%x\n", __func__, ret);
+	}
+
+	debug("%s: Stopping\n", __func__);
+	return twsi_stop(base);
+}
+
+/**
+ * Manually clear the I2C bus and send a stop
+ *
+ * @base	register base address
+ */
+static void twsi_unblock(void __iomem *base)
+{
+	int i;
+
+	for (i = 0; i < 9; i++) {
+		writeq(0, base + TWSI_INT);
+		udelay(5);
+		writeq(TWSI_INT_SCL_OVR, base + TWSI_INT);
+		udelay(5);
+	}
+	writeq(TWSI_INT_SCL_OVR | TWSI_INT_SDA_OVR, base + TWSI_INT);
+	udelay(5);
+	writeq(TWSI_INT_SDA_OVR, base + TWSI_INT);
+	udelay(5);
+	writeq(0, base + TWSI_INT);
+	udelay(5);
+}
+
+/**
+ * Performs a read transaction on the i2c bus
+ *
+ * @base	Base address of twsi registers
+ * @slave_addr	i2c bus address to read from
+ * @buffer	buffer to read into
+ * @length	number of bytes to read
+ * @return	0 for success, otherwise error
+ */
+static int twsi_read_data(void __iomem *base, u8 slave_addr,
+			  u8 *buffer, unsigned int length)
+{
+	unsigned int curr = 0;
+	u64 val;
+	int ret;
+
+	debug("%s(%p, 0x%x, %p, %u)\n", __func__, base, slave_addr,
+	      buffer, length);
+	ret = twsi_start(base);
+	if (ret) {
+		debug("%s: start failed\n", __func__);
+		return ret;
+	}
+
+	ret = twsi_wait(base);
+	if (ret) {
+		debug("%s: wait failed\n", __func__);
+		return ret;
+	}
+
+	val = (u32)(slave_addr << 1) | TWSI_OP_READ |
+		FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_DATA) |
+		FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
+	twsi_write_sw(base, val);
+	twsi_write_ctl(base, TWSI_CTL_ENAB);
+
+	ret = twsi_wait(base);
+	if (ret) {
+		debug("%s: waiting for sending addr failed\n", __func__);
+		return ret;
+	}
+
+	ret = twsi_read_status(base);
+	debug("%s: status: 0x%x\n", __func__, ret);
+	if (ret != TWSI_STAT_RXADDR_ACK) {
+		debug("%s: status: 0x%x\n", __func__, ret);
+		twsi_stop(base);
+		return twsi_i2c_lost_arb(ret, 0);
+	}
+
+	while (curr < length) {
+		twsi_write_ctl(base, TWSI_CTL_ENAB |
+			       ((curr < length - 1) ? TWSI_CTL_AAK : 0));
+
+		ret = twsi_wait(base);
+		if (ret) {
+			debug("%s: waiting for data failed\n", __func__);
+			return ret;
+		}
+
+		val = twsi_read_sw(base, val);
+		buffer[curr++] = (u8)val;
+	}
+
+	twsi_stop(base);
+
+	return 0;
+}
+
+/**
+ * Calculate the divisor values
+ *
+ * @speed	Speed to set
+ * @m_div	Pointer to M divisor
+ * @n_div	Pointer to N divisor
+ * @return	0 for success, otherwise error
+ */
+static void twsi_calc_div(struct udevice *bus, ulong sclk, unsigned int speed,
+			  int *m_div, int *n_div)
+{
+	struct octeon_twsi *twsi = dev_get_priv(bus);
+	int thp = twsi->data->thp;
+	int tclk, fsamp;
+	int ndiv, mdiv;
+
+	if (twsi->data->clk_method == CLK_METHOD_OCTEON) {
+		tclk = sclk / (2 * (thp + 1));
+	} else {
+		/* Refclk src in mode register defaults to 100MHz clock */
+		sclk = 100000000; /* 100 Mhz */
+		tclk = sclk / (thp + 2);
+	}
+	debug("%s( io_clock %lu tclk %u)\n", __func__, sclk, tclk);
+
+	/*
+	 * Compute the clocks M divider:
+	 *
+	 * TWSI freq = (core freq) / (10 x (M+1) x 2 * (thp+1) x 2^N)
+	 * M = ((core freq) / (10 x (TWSI freq) x 2 * (thp+1) x 2^N)) - 1
+	 *
+	 * For OcteonTX2 -
+	 * TWSI freq = (core freq) / (10 x (M+1) x (thp+2) x 2^N)
+	 * M = ((core freq) / (10 x (TWSI freq) x (thp+2) x 2^N)) - 1
+	 */
+	for (ndiv = 0; ndiv < 8; ndiv++) {
+		fsamp = tclk / (1 << ndiv);
+		mdiv = fsamp / speed / 10;
+		mdiv -= 1;
+		if (mdiv < 16)
+			break;
+	}
+
+	*m_div = mdiv;
+	*n_div = ndiv;
+}
+
+/**
+ * Init I2C controller
+ *
+ * @base	Base address of twsi registers
+ * @slave_addr	I2C slave address to configure this controller to
+ * @return	0 for success, otherwise error
+ */
+static int twsi_init(void __iomem *base, int slaveaddr)
+{
+	u64 val;
+
+	debug("%s (%p, 0x%x)\n", __func__, base, slaveaddr);
+
+	val = slaveaddr << 1 |
+		FIELD_PREP(TWSI_SW_EOP_IA_MASK, 0) |
+		FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA) |
+		TWSI_SW_V;
+	twsi_write_sw(base, val);
+
+	/* Set slave address */
+	val = slaveaddr |
+		FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_EOP_SLAVE_ADDR) |
+		FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA) |
+		TWSI_SW_V;
+	twsi_write_sw(base, val);
+
+	return 0;
+}
+
+/**
+ * Transfers data over the i2c bus
+ *
+ * @bus		i2c bus to transfer data over
+ * @msg		Array of i2c messages
+ * @nmsgs	Number of messages to send/receive
+ * @return	0 for success, otherwise error
+ */
+static int octeon_i2c_xfer(struct udevice *bus, struct i2c_msg *msg,
+			   int nmsgs)
+{
+	struct octeon_twsi *twsi = dev_get_priv(bus);
+	int ret;
+	int i;
+
+	debug("%s: %d messages\n", __func__, nmsgs);
+	for (i = 0; i < nmsgs; i++, msg++) {
+		debug("%s: chip=0x%x, len=0x%x\n", __func__, msg->addr,
+		      msg->len);
+
+		if (msg->flags & I2C_M_RD) {
+			debug("%s: Reading data\n", __func__);
+			ret = twsi_read_data(twsi->base, msg->addr,
+					     msg->buf, msg->len);
+		} else {
+			debug("%s: Writing data\n", __func__);
+			ret = twsi_write_data(twsi->base, msg->addr,
+					      msg->buf, msg->len);
+		}
+		if (ret) {
+			debug("%s: error sending\n", __func__);
+			return -EREMOTEIO;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * Set I2C bus speed
+ *
+ * @bus		i2c bus to transfer data over
+ * @speed	Speed in Hz to set
+ * @return	0 for success, otherwise error
+ */
+static int octeon_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
+{
+	struct octeon_twsi *twsi = dev_get_priv(bus);
+	int m_div, n_div;
+	ulong clk_rate;
+	u64 val;
+
+	debug("%s(%p, %u)\n", __func__, bus, speed);
+
+	clk_rate = clk_get_rate(&twsi->clk);
+	if (IS_ERR_VALUE(clk_rate))
+		return -EINVAL;
+
+	twsi_calc_div(bus, clk_rate, speed, &m_div, &n_div);
+	if (m_div >= 16)
+		return -1;
+
+	val = (u32)(((m_div & 0xf) << 3) | ((n_div & 0x7) << 0)) |
+		FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_CLKCTL) |
+		FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA) |
+		TWSI_SW_V;
+	/* Only init non-slave ports */
+	writeq(val, twsi->base + TWSI_SW_TWSI);
+
+	debug("%s: Wrote 0x%llx to sw_twsi\n", __func__, val);
+	return 0;
+}
+
+/**
+ * Driver probe function
+ *
+ * @dev		I2C device to probe
+ * @return	0 for success, otherwise error
+ */
+static int octeon_i2c_probe(struct udevice *dev)
+{
+	struct octeon_twsi *twsi = dev_get_priv(dev);
+	u32 i2c_slave_addr;
+	int ret;
+
+	twsi->data = (const struct octeon_i2c_data *)dev_get_driver_data(dev);
+
+	if (twsi->data->probe == PROBE_PCI) {
+		pci_dev_t bdf = dm_pci_get_bdf(dev);
+
+		debug("TWSI PCI device: %x\n", bdf);
+		dev->req_seq = PCI_FUNC(bdf);
+
+		twsi->base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0,
+					    PCI_REGION_MEM);
+	} else {
+		twsi->base = dev_remap_addr(dev);
+	}
+	twsi->base += twsi->data->reg_offs;
+
+	i2c_slave_addr = dev_read_u32_default(dev, "i2c-sda-hold-time-ns",
+					      CONFIG_SYS_I2C_OCTEON_SLAVE_ADDR);
+
+	ret = clk_get_by_index(dev, 0, &twsi->clk);
+	if (ret < 0)
+		return ret;
+
+	ret = clk_enable(&twsi->clk);
+	if (ret)
+		return ret;
+
+	debug("TWSI bus %d at %p\n", dev->seq, twsi->base);
+
+	/* Start with standard speed, real speed set via DT or cmd */
+	return twsi_init(twsi->base, i2c_slave_addr);
+}
+
+static const struct dm_i2c_ops octeon_i2c_ops = {
+	.xfer		= octeon_i2c_xfer,
+	.set_bus_speed	= octeon_i2c_set_bus_speed,
+};
+
+static const struct octeon_i2c_data i2c_octeon_data = {
+	.probe = PROBE_DT,
+	.reg_offs = 0x0000,
+	.thp = 3,
+	.clk_method = CLK_METHOD_OCTEON,
+};
+
+static const struct octeon_i2c_data i2c_octeontx_data = {
+	.probe = PROBE_PCI,
+	.reg_offs = 0x8000,
+	.thp = 3,
+	.clk_method = CLK_METHOD_OCTEON,
+};
+
+static const struct octeon_i2c_data i2c_octeontx2_data = {
+	.probe = PROBE_PCI,
+	.reg_offs = 0x8000,
+	.thp = 24,
+	.clk_method = CLK_METHOD_OCTEONTX2,
+};
+
+static const struct udevice_id octeon_i2c_ids[] = {
+	{ .compatible = "cavium,octeon-7890-twsi",
+	  .data = (ulong)&i2c_octeon_data },
+	{ .compatible = "cavium,thunder-8890-twsi",
+	  .data = (ulong)&i2c_octeontx_data },
+	{ .compatible = "cavium,thunder2-99xx-twsi",
+	  .data = (ulong)&i2c_octeontx2_data },
+	{ }
+};
+
+U_BOOT_DRIVER(octeon_pci_twsi) = {
+	.name	= "i2c_octeon",
+	.id	= UCLASS_I2C,
+	.of_match = octeon_i2c_ids,
+	.probe	= octeon_i2c_probe,
+	.priv_auto_alloc_size = sizeof(struct octeon_twsi),
+	.ops	= &octeon_i2c_ops,
+};
+
+static struct pci_device_id octeon_twsi_supported[] = {
+	{ PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_TWSI),
+	  .driver_data = (ulong)&i2c_octeontx2_data },
+	{ },
+};
+
+U_BOOT_PCI_DEVICE(octeon_pci_twsi, octeon_twsi_supported);

+ 73 - 0
drivers/i2c/stm32f7_i2c.c

@@ -8,7 +8,9 @@
 #include <dm.h>
 #include <i2c.h>
 #include <log.h>
+#include <regmap.h>
 #include <reset.h>
+#include <syscon.h>
 #include <linux/bitops.h>
 #include <linux/delay.h>
 
@@ -154,6 +156,7 @@ struct stm32_i2c_spec {
  * @fall_time: Fall time (ns)
  * @dnf: Digital filter coefficient (0-16)
  * @analog_filter: Analog filter delay (On/Off)
+ * @fmp_clr_offset: Fast Mode Plus clear register offset from set register
  */
 struct stm32_i2c_setup {
 	u32 speed_freq;
@@ -162,6 +165,7 @@ struct stm32_i2c_setup {
 	u32 fall_time;
 	u8 dnf;
 	bool analog_filter;
+	u32 fmp_clr_offset;
 };
 
 /**
@@ -181,11 +185,26 @@ struct stm32_i2c_timings {
 	u8 scll;
 };
 
+/**
+ * struct stm32_i2c_priv - private data of the controller
+ * @regs: I2C registers address
+ * @clk: hw i2c clock
+ * @setup: I2C timing setup parameters
+ * @speed: I2C clock frequency of the controller. Standard, Fast or Fast+
+ * @regmap: holds SYSCFG phandle for Fast Mode Plus bit
+ * @regmap_sreg: register address for setting Fast Mode Plus bits
+ * @regmap_creg: register address for clearing Fast Mode Plus bits
+ * @regmap_mask: mask for Fast Mode Plus bits
+ */
 struct stm32_i2c_priv {
 	struct stm32_i2c_regs *regs;
 	struct clk clk;
 	struct stm32_i2c_setup *setup;
 	u32 speed;
+	struct regmap *regmap;
+	u32 regmap_sreg;
+	u32 regmap_creg;
+	u32 regmap_mask;
 };
 
 static const struct stm32_i2c_spec i2c_specs[] = {
@@ -237,6 +256,14 @@ static const struct stm32_i2c_setup stm32f7_setup = {
 	.analog_filter = STM32_I2C_ANALOG_FILTER_ENABLE,
 };
 
+static const struct stm32_i2c_setup stm32mp15_setup = {
+	.rise_time = STM32_I2C_RISE_TIME_DEFAULT,
+	.fall_time = STM32_I2C_FALL_TIME_DEFAULT,
+	.dnf = STM32_I2C_DNF_DEFAULT,
+	.analog_filter = STM32_I2C_ANALOG_FILTER_ENABLE,
+	.fmp_clr_offset = 0x40,
+};
+
 static int stm32_i2c_check_device_busy(struct stm32_i2c_priv *i2c_priv)
 {
 	struct stm32_i2c_regs *regs = i2c_priv->regs;
@@ -761,6 +788,29 @@ static int stm32_i2c_setup_timing(struct stm32_i2c_priv *i2c_priv,
 	return 0;
 }
 
+static int stm32_i2c_write_fm_plus_bits(struct stm32_i2c_priv *i2c_priv)
+{
+	int ret;
+	bool enable = i2c_priv->speed > I2C_SPEED_FAST_RATE;
+
+	/* Optional */
+	if (IS_ERR_OR_NULL(i2c_priv->regmap))
+		return 0;
+
+	if (i2c_priv->regmap_sreg == i2c_priv->regmap_creg)
+		ret = regmap_update_bits(i2c_priv->regmap,
+					 i2c_priv->regmap_sreg,
+					 i2c_priv->regmap_mask,
+					 enable ? i2c_priv->regmap_mask : 0);
+	else
+		ret = regmap_write(i2c_priv->regmap,
+				   enable ? i2c_priv->regmap_sreg :
+					    i2c_priv->regmap_creg,
+				   i2c_priv->regmap_mask);
+
+	return ret;
+}
+
 static int stm32_i2c_hw_config(struct stm32_i2c_priv *i2c_priv)
 {
 	struct stm32_i2c_regs *regs = i2c_priv->regs;
@@ -775,6 +825,11 @@ static int stm32_i2c_hw_config(struct stm32_i2c_priv *i2c_priv)
 	/* Disable I2C */
 	clrbits_le32(&regs->cr1, STM32_I2C_CR1_PE);
 
+	/* Setup Fast mode plus if necessary */
+	ret = stm32_i2c_write_fm_plus_bits(i2c_priv);
+	if (ret)
+		return ret;
+
 	/* Timing settings */
 	timing |= STM32_I2C_TIMINGR_PRESC(t.presc);
 	timing |= STM32_I2C_TIMINGR_SCLDEL(t.scldel);
@@ -850,6 +905,7 @@ static int stm32_ofdata_to_platdata(struct udevice *dev)
 {
 	struct stm32_i2c_priv *i2c_priv = dev_get_priv(dev);
 	u32 rise_time, fall_time;
+	int ret;
 
 	i2c_priv->setup = (struct stm32_i2c_setup *)dev_get_driver_data(dev);
 	if (!i2c_priv->setup)
@@ -863,6 +919,22 @@ static int stm32_ofdata_to_platdata(struct udevice *dev)
 	if (fall_time)
 		i2c_priv->setup->fall_time = fall_time;
 
+	/* Optional */
+	i2c_priv->regmap = syscon_regmap_lookup_by_phandle(dev,
+							   "st,syscfg-fmp");
+	if (!IS_ERR(i2c_priv->regmap)) {
+		u32 fmp[3];
+
+		ret = dev_read_u32_array(dev, "st,syscfg-fmp", fmp, 3);
+		if (ret)
+			return ret;
+
+		i2c_priv->regmap_sreg = fmp[1];
+		i2c_priv->regmap_creg = fmp[1] +
+					i2c_priv->setup->fmp_clr_offset;
+		i2c_priv->regmap_mask = fmp[2];
+	}
+
 	return 0;
 }
 
@@ -873,6 +945,7 @@ static const struct dm_i2c_ops stm32_i2c_ops = {
 
 static const struct udevice_id stm32_i2c_of_match[] = {
 	{ .compatible = "st,stm32f7-i2c", .data = (ulong)&stm32f7_setup },
+	{ .compatible = "st,stm32mp15-i2c", .data = (ulong)&stm32mp15_setup },
 	{}
 };
 

+ 6 - 0
drivers/pwm/Kconfig

@@ -47,6 +47,12 @@ config PWM_SANDBOX
 	  useful. The PWM can be enabled but is not connected to any outputs
 	  so this is not very useful.
 
+config PWM_SIFIVE
+	bool "Enable support for SiFive PWM"
+	depends on DM_PWM
+	help
+	  This PWM is found SiFive's FU540 and other SoCs.
+
 config PWM_TEGRA
 	bool "Enable support for the Tegra PWM"
 	depends on DM_PWM

+ 1 - 0
drivers/pwm/Makefile

@@ -15,5 +15,6 @@ obj-$(CONFIG_PWM_IMX)		+= pwm-imx.o pwm-imx-util.o
 obj-$(CONFIG_PWM_MTK)		+= pwm-mtk.o
 obj-$(CONFIG_PWM_ROCKCHIP)	+= rk_pwm.o
 obj-$(CONFIG_PWM_SANDBOX)	+= sandbox_pwm.o
+obj-$(CONFIG_PWM_SIFIVE)	+= pwm-sifive.o
 obj-$(CONFIG_PWM_TEGRA)		+= tegra_pwm.o
 obj-$(CONFIG_PWM_SUNXI)		+= sunxi_pwm.o

+ 172 - 0
drivers/pwm/pwm-sifive.c

@@ -0,0 +1,172 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 SiFive, Inc
+ * For SiFive's PWM IP block documentation please refer Chapter 14 of
+ * Reference Manual : https://static.dev.sifive.com/FU540-C000-v1.0.pdf
+ *
+ * Limitations:
+ * - When changing both duty cycle and period, we cannot prevent in
+ *   software that the output might produce a period with mixed
+ *   settings (new period length and old duty cycle).
+ * - The hardware cannot generate a 100% duty cycle.
+ * - The hardware generates only inverted output.
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <div64.h>
+#include <dm.h>
+#include <pwm.h>
+#include <regmap.h>
+#include <linux/io.h>
+#include <linux/log2.h>
+#include <linux/bitfield.h>
+
+/* PWMCFG fields */
+#define PWM_SIFIVE_PWMCFG_SCALE         GENMASK(3, 0)
+#define PWM_SIFIVE_PWMCFG_STICKY        BIT(8)
+#define PWM_SIFIVE_PWMCFG_ZERO_CMP      BIT(9)
+#define PWM_SIFIVE_PWMCFG_DEGLITCH      BIT(10)
+#define PWM_SIFIVE_PWMCFG_EN_ALWAYS     BIT(12)
+#define PWM_SIFIVE_PWMCFG_EN_ONCE       BIT(13)
+#define PWM_SIFIVE_PWMCFG_CENTER        BIT(16)
+#define PWM_SIFIVE_PWMCFG_GANG          BIT(24)
+#define PWM_SIFIVE_PWMCFG_IP            BIT(28)
+
+/* PWM_SIFIVE_SIZE_PWMCMP is used to calculate offset for pwmcmpX registers */
+#define PWM_SIFIVE_SIZE_PWMCMP          4
+#define PWM_SIFIVE_CMPWIDTH             16
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct pwm_sifive_regs {
+	unsigned long cfg;
+	unsigned long cnt;
+	unsigned long pwms;
+	unsigned long cmp0;
+};
+
+struct pwm_sifive_data {
+	struct pwm_sifive_regs regs;
+};
+
+struct pwm_sifive_priv {
+	void __iomem *base;
+	ulong freq;
+	const struct pwm_sifive_data *data;
+};
+
+static int pwm_sifive_set_config(struct udevice *dev, uint channel,
+				 uint period_ns, uint duty_ns)
+{
+	struct pwm_sifive_priv *priv = dev_get_priv(dev);
+	const struct pwm_sifive_regs *regs = &priv->data->regs;
+	unsigned long scale_pow;
+	unsigned long long num;
+	u32 scale, val = 0, frac;
+
+	debug("%s: period_ns=%u, duty_ns=%u\n", __func__, period_ns, duty_ns);
+
+	/*
+	 * The PWM unit is used with pwmzerocmp=0, so the only way to modify the
+	 * period length is using pwmscale which provides the number of bits the
+	 * counter is shifted before being feed to the comparators. A period
+	 * lasts (1 << (PWM_SIFIVE_CMPWIDTH + pwmscale)) clock ticks.
+	 * (1 << (PWM_SIFIVE_CMPWIDTH + scale)) * 10^9/rate = period
+	 */
+	scale_pow = lldiv((uint64_t)priv->freq * period_ns, 1000000000);
+	scale = clamp(ilog2(scale_pow) - PWM_SIFIVE_CMPWIDTH, 0, 0xf);
+	val |= FIELD_PREP(PWM_SIFIVE_PWMCFG_SCALE, scale);
+
+	/*
+	 * The problem of output producing mixed setting as mentioned at top,
+	 * occurs here. To minimize the window for this problem, we are
+	 * calculating the register values first and then writing them
+	 * consecutively
+	 */
+	num = (u64)duty_ns * (1U << PWM_SIFIVE_CMPWIDTH);
+	frac = DIV_ROUND_CLOSEST_ULL(num, period_ns);
+	frac = min(frac, (1U << PWM_SIFIVE_CMPWIDTH) - 1);
+
+	writel(val, priv->base + regs->cfg);
+	writel(frac, priv->base + regs->cmp0 + channel *
+	       PWM_SIFIVE_SIZE_PWMCMP);
+
+	return 0;
+}
+
+static int pwm_sifive_set_enable(struct udevice *dev, uint channel, bool enable)
+{
+	struct pwm_sifive_priv *priv = dev_get_priv(dev);
+	const struct pwm_sifive_regs *regs = &priv->data->regs;
+	u32 val;
+
+	debug("%s: Enable '%s'\n", __func__, dev->name);
+
+	if (enable) {
+		val = readl(priv->base + regs->cfg);
+		val |= PWM_SIFIVE_PWMCFG_EN_ALWAYS;
+		writel(val, priv->base + regs->cfg);
+	} else {
+		writel(0, priv->base + regs->cmp0 + channel *
+		       PWM_SIFIVE_SIZE_PWMCMP);
+	}
+
+	return 0;
+}
+
+static int pwm_sifive_ofdata_to_platdata(struct udevice *dev)
+{
+	struct pwm_sifive_priv *priv = dev_get_priv(dev);
+
+	priv->base = dev_read_addr_ptr(dev);
+
+	return 0;
+}
+
+static int pwm_sifive_probe(struct udevice *dev)
+{
+	struct pwm_sifive_priv *priv = dev_get_priv(dev);
+	struct clk clk;
+	int ret = 0;
+
+	ret = clk_get_by_index(dev, 0, &clk);
+	if (ret < 0) {
+		debug("%s get clock fail!\n", __func__);
+		return -EINVAL;
+	}
+
+	priv->freq = clk_get_rate(&clk);
+	priv->data = (struct pwm_sifive_data *)dev_get_driver_data(dev);
+
+	return 0;
+}
+
+static const struct pwm_ops pwm_sifive_ops = {
+	.set_config	= pwm_sifive_set_config,
+	.set_enable	= pwm_sifive_set_enable,
+};
+
+static const struct pwm_sifive_data pwm_data = {
+	.regs = {
+		.cfg = 0x00,
+		.cnt = 0x08,
+		.pwms = 0x10,
+		.cmp0 = 0x20,
+	},
+};
+
+static const struct udevice_id pwm_sifive_ids[] = {
+	{ .compatible = "sifive,pwm0", .data = (ulong)&pwm_data},
+	{ }
+};
+
+U_BOOT_DRIVER(pwm_sifive) = {
+	.name	= "pwm_sifive",
+	.id	= UCLASS_PWM,
+	.of_match = pwm_sifive_ids,
+	.ops	= &pwm_sifive_ops,
+	.ofdata_to_platdata     = pwm_sifive_ofdata_to_platdata,
+	.probe		= pwm_sifive_probe,
+	.priv_auto_alloc_size	= sizeof(struct pwm_sifive_priv),
+};

+ 2 - 1
drivers/rtc/i2c_rtc_emul.c

@@ -197,7 +197,8 @@ static int sandbox_i2c_rtc_xfer(struct udevice *emul, struct i2c_msg *msg,
 
 			/* Write the register */
 			memcpy(plat->reg + offset, ptr, len);
-			if (offset == REG_RESET)
+			/* If the reset register was written to, do reset. */
+			if (offset <= REG_RESET && REG_RESET < offset + len)
 				reset_time(emul);
 		}
 	}

+ 10 - 3
drivers/rtc/pcf2127.c

@@ -23,8 +23,7 @@
 #define PCF2127_REG_MO		0x08
 #define PCF2127_REG_YR		0x09
 
-static int pcf2127_read_reg(struct udevice *dev, uint offset,
-			    u8 *buffer, int len)
+static int pcf2127_rtc_read(struct udevice *dev, uint offset, u8 *buffer, uint len)
 {
 	struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
 	struct i2c_msg msg;
@@ -44,6 +43,12 @@ static int pcf2127_read_reg(struct udevice *dev, uint offset,
 	return dm_i2c_xfer(dev, &msg, 1);
 }
 
+static int pcf2127_rtc_write(struct udevice *dev, uint offset,
+			     const u8 *buffer, uint len)
+{
+	return dm_i2c_write(dev, offset, buffer, len);
+}
+
 static int pcf2127_rtc_set(struct udevice *dev, const struct rtc_time *tm)
 {
 	uchar buf[7] = {0};
@@ -73,7 +78,7 @@ static int pcf2127_rtc_get(struct udevice *dev, struct rtc_time *tm)
 	int ret = 0;
 	uchar buf[10] = { PCF2127_REG_CTRL1 };
 
-	ret = pcf2127_read_reg(dev, PCF2127_REG_CTRL1, buf, sizeof(buf));
+	ret = pcf2127_rtc_read(dev, PCF2127_REG_CTRL1, buf, sizeof(buf));
 	if (ret < 0)
 		return ret;
 
@@ -110,6 +115,8 @@ static const struct rtc_ops pcf2127_rtc_ops = {
 	.get = pcf2127_rtc_get,
 	.set = pcf2127_rtc_set,
 	.reset = pcf2127_rtc_reset,
+	.read = pcf2127_rtc_read,
+	.write = pcf2127_rtc_write,
 };
 
 static const struct udevice_id pcf2127_rtc_ids[] = {

+ 55 - 4
drivers/rtc/rtc-uclass.c

@@ -40,24 +40,75 @@ int dm_rtc_reset(struct udevice *dev)
 	return ops->reset(dev);
 }
 
-int rtc_read8(struct udevice *dev, unsigned int reg)
+int dm_rtc_read(struct udevice *dev, unsigned int reg, u8 *buf, unsigned int len)
 {
 	struct rtc_ops *ops = rtc_get_ops(dev);
 
 	assert(ops);
+	if (ops->read)
+		return ops->read(dev, reg, buf, len);
 	if (!ops->read8)
 		return -ENOSYS;
-	return ops->read8(dev, reg);
+	while (len--) {
+		int ret = ops->read8(dev, reg++);
+
+		if (ret < 0)
+			return ret;
+		*buf++ = ret;
+	}
+	return 0;
 }
 
-int rtc_write8(struct udevice *dev, unsigned int reg, int val)
+int dm_rtc_write(struct udevice *dev, unsigned int reg,
+		 const u8 *buf, unsigned int len)
 {
 	struct rtc_ops *ops = rtc_get_ops(dev);
 
 	assert(ops);
+	if (ops->write)
+		return ops->write(dev, reg, buf, len);
 	if (!ops->write8)
 		return -ENOSYS;
-	return ops->write8(dev, reg, val);
+	while (len--) {
+		int ret = ops->write8(dev, reg++, *buf++);
+
+		if (ret < 0)
+			return ret;
+	}
+	return 0;
+}
+
+int rtc_read8(struct udevice *dev, unsigned int reg)
+{
+	struct rtc_ops *ops = rtc_get_ops(dev);
+
+	assert(ops);
+	if (ops->read8)
+		return ops->read8(dev, reg);
+	if (ops->read) {
+		u8 buf[1];
+		int ret = ops->read(dev, reg, buf, 1);
+
+		if (ret < 0)
+			return ret;
+		return buf[0];
+	}
+	return -ENOSYS;
+}
+
+int rtc_write8(struct udevice *dev, unsigned int reg, int val)
+{
+	struct rtc_ops *ops = rtc_get_ops(dev);
+
+	assert(ops);
+	if (ops->write8)
+		return ops->write8(dev, reg, val);
+	if (ops->write) {
+		u8 buf[1] = { val };
+
+		return ops->write(dev, reg, buf, 1);
+	}
+	return -ENOSYS;
 }
 
 int rtc_read16(struct udevice *dev, unsigned int reg, u16 *valuep)

+ 24 - 41
drivers/rtc/sandbox_rtc.c

@@ -14,55 +14,38 @@
 
 static int sandbox_rtc_get(struct udevice *dev, struct rtc_time *time)
 {
-	time->tm_sec = dm_i2c_reg_read(dev, REG_SEC);
-	if (time->tm_sec < 0)
-		return time->tm_sec;
-	time->tm_min = dm_i2c_reg_read(dev, REG_MIN);
-	if (time->tm_min < 0)
-		return time->tm_min;
-	time->tm_hour = dm_i2c_reg_read(dev, REG_HOUR);
-	if (time->tm_hour < 0)
-		return time->tm_hour;
-	time->tm_mday = dm_i2c_reg_read(dev, REG_MDAY);
-	if (time->tm_mday < 0)
-		return time->tm_mday;
-	time->tm_mon = dm_i2c_reg_read(dev, REG_MON);
-	if (time->tm_mon < 0)
-		return time->tm_mon;
-	time->tm_year = dm_i2c_reg_read(dev, REG_YEAR);
-	if (time->tm_year < 0)
-		return time->tm_year;
-	time->tm_year += 1900;
-	time->tm_wday = dm_i2c_reg_read(dev, REG_WDAY);
-	if (time->tm_wday < 0)
-		return time->tm_wday;
+	u8 buf[7];
+	int ret;
+
+	ret = dm_i2c_read(dev, REG_SEC, buf, sizeof(buf));
+	if (ret < 0)
+		return ret;
+
+	time->tm_sec  = buf[REG_SEC - REG_SEC];
+	time->tm_min  = buf[REG_MIN - REG_SEC];
+	time->tm_hour = buf[REG_HOUR - REG_SEC];
+	time->tm_mday = buf[REG_MDAY - REG_SEC];
+	time->tm_mon  = buf[REG_MON - REG_SEC];
+	time->tm_year = buf[REG_YEAR - REG_SEC] + 1900;
+	time->tm_wday = buf[REG_WDAY - REG_SEC];
 
 	return 0;
 }
 
 static int sandbox_rtc_set(struct udevice *dev, const struct rtc_time *time)
 {
+	u8 buf[7];
 	int ret;
 
-	ret = dm_i2c_reg_write(dev, REG_SEC, time->tm_sec);
-	if (ret < 0)
-		return ret;
-	ret = dm_i2c_reg_write(dev, REG_MIN, time->tm_min);
-	if (ret < 0)
-		return ret;
-	ret = dm_i2c_reg_write(dev, REG_HOUR, time->tm_hour);
-	if (ret < 0)
-		return ret;
-	ret = dm_i2c_reg_write(dev, REG_MDAY, time->tm_mday);
-	if (ret < 0)
-		return ret;
-	ret = dm_i2c_reg_write(dev, REG_MON, time->tm_mon);
-	if (ret < 0)
-		return ret;
-	ret = dm_i2c_reg_write(dev, REG_YEAR, time->tm_year - 1900);
-	if (ret < 0)
-		return ret;
-	ret = dm_i2c_reg_write(dev, REG_WDAY, time->tm_wday);
+	buf[REG_SEC - REG_SEC]  = time->tm_sec;
+	buf[REG_MIN - REG_SEC]  = time->tm_min;
+	buf[REG_HOUR - REG_SEC] = time->tm_hour;
+	buf[REG_MDAY - REG_SEC] = time->tm_mday;
+	buf[REG_MON  - REG_SEC] = time->tm_mon;
+	buf[REG_YEAR - REG_SEC] = time->tm_year - 1900;
+	buf[REG_WDAY - REG_SEC] = time->tm_wday;
+
+	ret = dm_i2c_write(dev, REG_SEC, buf, sizeof(buf));
 	if (ret < 0)
 		return ret;
 

+ 47 - 0
include/rtc.h

@@ -55,6 +55,30 @@ struct rtc_ops {
 	 */
 	int (*reset)(struct udevice *dev);
 
+	/**
+	 * read() - Read multiple 8-bit registers
+	 *
+	 * @dev:	Device to read from
+	 * @reg:	First register to read
+	 * @buf:	Output buffer
+	 * @len:	Number of registers to read
+	 * @return 0 if OK, -ve on error
+	 */
+	int (*read)(struct udevice *dev, unsigned int reg,
+		    u8 *buf, unsigned int len);
+
+	/**
+	 * write() - Write multiple 8-bit registers
+	 *
+	 * @dev:	Device to write to
+	 * @reg:	First register to write
+	 * @buf:	Input buffer
+	 * @len:	Number of registers to write
+	 * @return 0 if OK, -ve on error
+	 */
+	int (*write)(struct udevice *dev, unsigned int reg,
+		     const u8 *buf, unsigned int len);
+
 	/**
 	 * read8() - Read an 8-bit register
 	 *
@@ -109,6 +133,29 @@ int dm_rtc_set(struct udevice *dev, struct rtc_time *time);
  */
 int dm_rtc_reset(struct udevice *dev);
 
+/**
+ * dm_rtc_read() - Read multiple 8-bit registers
+ *
+ * @dev:	Device to read from
+ * @reg:	First register to read
+ * @buf:	Output buffer
+ * @len:	Number of registers to read
+ * @return 0 if OK, -ve on error
+ */
+int dm_rtc_read(struct udevice *dev, unsigned int reg, u8 *buf, unsigned int len);
+
+/**
+ * dm_rtc_write() - Write multiple 8-bit registers
+ *
+ * @dev:	Device to write to
+ * @reg:	First register to write
+ * @buf:	Input buffer
+ * @len:	Number of registers to write
+ * @return 0 if OK, -ve on error
+ */
+int dm_rtc_write(struct udevice *dev, unsigned int reg,
+		 const u8 *buf, unsigned int len);
+
 /**
  * rtc_read8() - Read an 8-bit register
  *

+ 117 - 1
test/dm/rtc.c

@@ -5,11 +5,13 @@
  */
 
 #include <common.h>
+#include <console.h>
 #include <dm.h>
 #include <i2c.h>
 #include <log.h>
 #include <rtc.h>
 #include <asm/io.h>
+#include <asm/rtc.h>
 #include <asm/test.h>
 #include <dm/test.h>
 #include <test/ut.h>
@@ -70,7 +72,20 @@ static int dm_test_rtc_set_get(struct unit_test_state *uts)
 	old_base_time = sandbox_i2c_rtc_get_set_base_time(emul, -1);
 
 	memset(&time, '\0', sizeof(time));
-	time.tm_mday = 25;
+	time.tm_mday = 3;
+	time.tm_mon = 6;
+	time.tm_year = 2004;
+	time.tm_sec = 0;
+	time.tm_min = 18;
+	time.tm_hour = 18;
+	ut_assertok(dm_rtc_set(dev, &time));
+
+	memset(&cmp, '\0', sizeof(cmp));
+	ut_assertok(dm_rtc_get(dev, &cmp));
+	ut_assertok(cmp_times(&time, &cmp, true));
+
+	memset(&time, '\0', sizeof(time));
+	time.tm_mday = 31;
 	time.tm_mon = 8;
 	time.tm_year = 2004;
 	time.tm_sec = 0;
@@ -117,6 +132,107 @@ static int dm_test_rtc_set_get(struct unit_test_state *uts)
 }
 DM_TEST(dm_test_rtc_set_get, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
 
+static int dm_test_rtc_read_write(struct unit_test_state *uts)
+{
+	struct rtc_time time;
+	struct udevice *dev, *emul;
+	long old_offset;
+	u8 buf[4], reg;
+
+	ut_assertok(uclass_get_device(UCLASS_RTC, 0, &dev));
+
+	memcpy(buf, "car", 4);
+	ut_assertok(dm_rtc_write(dev, REG_AUX0, buf, 4));
+	memset(buf, '\0', sizeof(buf));
+	ut_assertok(dm_rtc_read(dev, REG_AUX0, buf, 4));
+	ut_asserteq(memcmp(buf, "car", 4), 0);
+
+	reg = 'b';
+	ut_assertok(dm_rtc_write(dev, REG_AUX0, &reg, 1));
+	memset(buf, '\0', sizeof(buf));
+	ut_assertok(dm_rtc_read(dev, REG_AUX0, buf, 4));
+	ut_asserteq(memcmp(buf, "bar", 4), 0);
+
+	reg = 't';
+	ut_assertok(dm_rtc_write(dev, REG_AUX2, &reg, 1));
+	memset(buf, '\0', sizeof(buf));
+	ut_assertok(dm_rtc_read(dev, REG_AUX1, buf, 3));
+	ut_asserteq(memcmp(buf, "at", 3), 0);
+
+	ut_assertok(i2c_emul_find(dev, &emul));
+	ut_assert(emul != NULL);
+
+	old_offset = sandbox_i2c_rtc_set_offset(emul, false, 0);
+	ut_assertok(dm_rtc_get(dev, &time));
+
+	ut_assertok(dm_rtc_read(dev, REG_SEC, &reg, 1));
+	ut_asserteq(time.tm_sec, reg);
+	ut_assertok(dm_rtc_read(dev, REG_MDAY, &reg, 1));
+	ut_asserteq(time.tm_mday, reg);
+
+	sandbox_i2c_rtc_set_offset(emul, true, old_offset);
+
+	return 0;
+}
+DM_TEST(dm_test_rtc_read_write, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test 'rtc list' command */
+static int dm_test_rtc_cmd_list(struct unit_test_state *uts)
+{
+	console_record_reset();
+
+	run_command("rtc list", 0);
+	ut_assert_nextline("RTC #0 - rtc@43");
+	ut_assert_nextline("RTC #1 - rtc@61");
+	ut_assert_console_end();
+
+	return 0;
+}
+DM_TEST(dm_test_rtc_cmd_list, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test 'rtc read' and 'rtc write' commands */
+static int dm_test_rtc_cmd_rw(struct unit_test_state *uts)
+{
+	console_record_reset();
+
+	run_command("rtc dev 0", 0);
+	ut_assert_nextline("RTC #0 - rtc@43");
+	ut_assert_console_end();
+
+	run_command("rtc write 0x30 aabb", 0);
+	ut_assert_console_end();
+
+	run_command("rtc read 0x30 2", 0);
+	ut_assert_nextline("00000030: aa bb                                              ..");
+	ut_assert_console_end();
+
+	run_command("rtc dev 1", 0);
+	ut_assert_nextline("RTC #1 - rtc@61");
+	ut_assert_console_end();
+
+	run_command("rtc write 0x30 ccdd", 0);
+	ut_assert_console_end();
+
+	run_command("rtc read 0x30 2", 0);
+	ut_assert_nextline("00000030: cc dd                                              ..");
+	ut_assert_console_end();
+
+	/*
+	 * Switch back to device #0, check that its aux registers
+	 * still have the same values.
+	 */
+	run_command("rtc dev 0", 0);
+	ut_assert_nextline("RTC #0 - rtc@43");
+	ut_assert_console_end();
+
+	run_command("rtc read 0x30 2", 0);
+	ut_assert_nextline("00000030: aa bb                                              ..");
+	ut_assert_console_end();
+
+	return 0;
+}
+DM_TEST(dm_test_rtc_cmd_rw, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
 /* Reset the time */
 static int dm_test_rtc_reset(struct unit_test_state *uts)
 {