summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2020-07-09 08:22:44 -0400
committerTom Rini <trini@konsulko.com>2020-07-09 08:22:44 -0400
commitd9107930af63d88c2d84560db19e65f1a51c4cbd (patch)
treed35deb928f896c3179d65765edeebca54e901ee5 /drivers
parent5fb70639cc5eea60e37e5eaaa7cc145e29527658 (diff)
parent7239a610b796b0bb8f85c5c21798596c2768cb50 (diff)
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
Diffstat (limited to 'drivers')
-rw-r--r--drivers/i2c/Kconfig18
-rw-r--r--drivers/i2c/Makefile2
-rw-r--r--drivers/i2c/i2c-cortina.c347
-rw-r--r--drivers/i2c/i2c-cortina.h87
-rw-r--r--drivers/i2c/imx_lpi2c.c22
-rw-r--r--drivers/i2c/octeon_i2c.c847
-rw-r--r--drivers/i2c/stm32f7_i2c.c73
-rw-r--r--drivers/pwm/Kconfig6
-rw-r--r--drivers/pwm/Makefile1
-rw-r--r--drivers/pwm/pwm-sifive.c172
-rw-r--r--drivers/rtc/i2c_rtc_emul.c3
-rw-r--r--drivers/rtc/pcf2127.c13
-rw-r--r--drivers/rtc/rtc-uclass.c59
-rw-r--r--drivers/rtc/sandbox_rtc.c65
14 files changed, 1655 insertions, 60 deletions
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index f8b18de8f3..87d11b663c 100644
--- a/drivers/i2c/Kconfig
+++ b/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
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index 62935b7ebc..174081e252 100644
--- a/drivers/i2c/Makefile
+++ b/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
diff --git a/drivers/i2c/i2c-cortina.c b/drivers/i2c/i2c-cortina.c
new file mode 100644
index 0000000000..036fc4282b
--- /dev/null
+++ b/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,
+};
diff --git a/drivers/i2c/i2c-cortina.h b/drivers/i2c/i2c-cortina.h
new file mode 100644
index 0000000000..7e406b580e
--- /dev/null
+++ b/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_ */
diff --git a/drivers/i2c/imx_lpi2c.c b/drivers/i2c/imx_lpi2c.c
index c8e42e05f5..b7b2aafc7f 100644
--- a/drivers/i2c/imx_lpi2c.c
+++ b/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);
diff --git a/drivers/i2c/octeon_i2c.c b/drivers/i2c/octeon_i2c.c
new file mode 100644
index 0000000000..c11d6ff93d
--- /dev/null
+++ b/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);
diff --git a/drivers/i2c/stm32f7_i2c.c b/drivers/i2c/stm32f7_i2c.c
index ada8f4095e..2f60911549 100644
--- a/drivers/i2c/stm32f7_i2c.c
+++ b/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 },
{}
};
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index edb3f0f538..61eb468cde 100644
--- a/drivers/pwm/Kconfig
+++ b/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
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 2c3a069006..0f4e84b04d 100644
--- a/drivers/pwm/Makefile
+++ b/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
diff --git a/drivers/pwm/pwm-sifive.c b/drivers/pwm/pwm-sifive.c
new file mode 100644
index 0000000000..77bc659fef
--- /dev/null
+++ b/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),
+};
diff --git a/drivers/rtc/i2c_rtc_emul.c b/drivers/rtc/i2c_rtc_emul.c
index a010af411b..7f78ff83cb 100644
--- a/drivers/rtc/i2c_rtc_emul.c
+++ b/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);
}
}
diff --git a/drivers/rtc/pcf2127.c b/drivers/rtc/pcf2127.c
index c423960b34..88ff8c52c3 100644
--- a/drivers/rtc/pcf2127.c
+++ b/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[] = {
diff --git a/drivers/rtc/rtc-uclass.c b/drivers/rtc/rtc-uclass.c
index 926cca234e..8035f7fe9c 100644
--- a/drivers/rtc/rtc-uclass.c
+++ b/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)
diff --git a/drivers/rtc/sandbox_rtc.c b/drivers/rtc/sandbox_rtc.c
index b08d758a74..77065e49c7 100644
--- a/drivers/rtc/sandbox_rtc.c
+++ b/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;