diff mbox series

[v2,4/5] lib: utils/i2c: Add minimal SiFive I2C driver

Message ID 20211015131925.22585-5-nikita.shubin@maquefel.me
State Superseded
Headers show
Series I2C framework, reboot Unmatched via PMIC | expand

Commit Message

Nikita Shubin Oct. 15, 2021, 1:19 p.m. UTC
From: Nikita Shubin <n.shubin@yadro.com>

Minimum SiFive I2C driver to read/send bytes over I2C bus.

This allows querying information and perform operation of onboard PMIC,
as well as power-off and reset.

Signed-off-by: Nikita Shubin <n.shubin@yadro.com>
---
v1 -> v2:
- setreg/getreg add sifive_i2c_ prefix
- use sbi_timer_mdelay for delay
- renamed send to write
- reworked read/write to support buffers
- renamed read/write to smbus_read/smbus_write
---
 lib/utils/i2c/fdt_i2c.c        |   3 +
 lib/utils/i2c/fdt_i2c_sifive.c | 271 +++++++++++++++++++++++++++++++++
 lib/utils/i2c/objects.mk       |   1 +
 3 files changed, 275 insertions(+)
 create mode 100644 lib/utils/i2c/fdt_i2c_sifive.c

Comments

Alexandre Ghiti Oct. 19, 2021, 12:34 p.m. UTC | #1
On Fri, Oct 15, 2021 at 3:20 PM Nikita Shubin <nikita.shubin@maquefel.me> wrote:
>
> From: Nikita Shubin <n.shubin@yadro.com>
>
> Minimum SiFive I2C driver to read/send bytes over I2C bus.
>
> This allows querying information and perform operation of onboard PMIC,
> as well as power-off and reset.
>
> Signed-off-by: Nikita Shubin <n.shubin@yadro.com>
> ---
> v1 -> v2:
> - setreg/getreg add sifive_i2c_ prefix
> - use sbi_timer_mdelay for delay
> - renamed send to write
> - reworked read/write to support buffers
> - renamed read/write to smbus_read/smbus_write
> ---
>  lib/utils/i2c/fdt_i2c.c        |   3 +
>  lib/utils/i2c/fdt_i2c_sifive.c | 271 +++++++++++++++++++++++++++++++++
>  lib/utils/i2c/objects.mk       |   1 +
>  3 files changed, 275 insertions(+)
>  create mode 100644 lib/utils/i2c/fdt_i2c_sifive.c
>
> diff --git a/lib/utils/i2c/fdt_i2c.c b/lib/utils/i2c/fdt_i2c.c
> index fce6d73..bbf9df6 100644
> --- a/lib/utils/i2c/fdt_i2c.c
> +++ b/lib/utils/i2c/fdt_i2c.c
> @@ -18,7 +18,10 @@
>
>  #include <sbi/sbi_console.h>
>
> +extern struct fdt_i2c_adapter fdt_i2c_adapter_sifive;
> +
>  static struct fdt_i2c_adapter *i2c_adapter_drivers[] = {
> +       &fdt_i2c_adapter_sifive
>  };
>
>  static struct fdt_i2c_adapter
> diff --git a/lib/utils/i2c/fdt_i2c_sifive.c b/lib/utils/i2c/fdt_i2c_sifive.c
> new file mode 100644
> index 0000000..aeb67ee
> --- /dev/null
> +++ b/lib/utils/i2c/fdt_i2c_sifive.c
> @@ -0,0 +1,271 @@
> +/*
> + * SPDX-License-Identifier: BSD-2-Clause
> + *
> + * Copyright (c) 2021 YADRO
> + *
> + * Authors:
> + *   Nikita Shubin <nshubin@yadro.com>
> + */
> +
> +#include <sbi/riscv_io.h>
> +#include <sbi/sbi_error.h>
> +#include <sbi/sbi_timer.h>
> +#include <sbi_utils/fdt/fdt_helper.h>
> +#include <sbi_utils/i2c/fdt_i2c.h>
> +
> +#define SIFIVE_I2C_ADAPTER_MAX 2
> +
> +#define SIFIVE_I2C_PRELO       0x00
> +#define SIFIVE_I2C_PREHI       0x04
> +#define SIFIVE_I2C_CTR         0x08
> +#define SIFIVE_I2C_TXR         0x00c
> +#define SIFIVE_I2C_RXR         SIFIVE_I2C_TXR
> +#define SIFIVE_I2C_CR          0x010
> +#define SIFIVE_I2C_SR          SIFIVE_I2C_CR
> +
> +#define SIFIVE_I2C_CTR_IEN     (1 << 6)
> +#define SIFIVE_I2C_CTR_EN      (1 << 7)
> +
> +#define SIFIVE_I2C_CMD_IACK    (1 << 0)
> +#define SIFIVE_I2C_CMD_ACK     (1 << 3)
> +#define SIFIVE_I2C_CMD_WR      (1 << 4)
> +#define SIFIVE_I2C_CMD_RD      (1 << 5)
> +#define SIFIVE_I2C_CMD_STO     (1 << 6)
> +#define SIFIVE_I2C_CMD_STA     (1 << 7)
> +
> +#define SIFIVE_I2C_STATUS_IF   (1 << 0)
> +#define SIFIVE_I2C_STATUS_TIP  (1 << 1)
> +#define SIFIVE_I2C_STATUS_AL   (1 << 5)
> +#define SIFIVE_I2C_STATUS_BUSY (1 << 6)
> +#define SIFIVE_I2C_STATUS_RXACK        (1 << 7)
> +
> +#define SIFIVE_I2C_WRITE_BIT   (0 << 0)
> +#define SIFIVE_I2C_READ_BIT    (1 << 0)
> +
> +struct sifive_i2c_adapter {
> +       unsigned long addr;
> +       struct i2c_adapter adapter;
> +};
> +
> +static unsigned int sifive_i2c_adapter_count;
> +static struct sifive_i2c_adapter sifive_i2c_adapter_array[SIFIVE_I2C_ADAPTER_MAX];
> +
> +extern struct fdt_i2c_adapter fdt_i2c_adapter_sifive;
> +
> +static inline void sifive_i2c_setreg(struct sifive_i2c_adapter *adap,
> +                                    int reg, u8 value)
> +{
> +       writel(value, (volatile void *)adap->addr + reg);
> +}
> +
> +static inline u8 sifive_i2c_getreg(struct sifive_i2c_adapter *adap, int reg)
> +{
> +       return readl((volatile void *)adap->addr + reg);
> +}
> +

reg is defined as a uint8_t in calling functions and here as an int.

> +static int sifive_i2c_adapter_rxack(struct sifive_i2c_adapter *adap)
> +{
> +       uint8_t val = sifive_i2c_getreg(adap, SIFIVE_I2C_SR);
> +
> +       if (val & SIFIVE_I2C_STATUS_RXACK)
> +               return SBI_EIO;
> +
> +       return 0;
> +}
> +
> +static int sifive_i2c_adapter_poll(struct sifive_i2c_adapter *adap, uint32_t mask)
> +{
> +       int max_retry = 5;
> +       uint8_t val;
> +
> +       do {
> +               val = sifive_i2c_getreg(adap, SIFIVE_I2C_SR);
> +               sbi_timer_mdelay(100);
> +       } while ((val & mask) && (max_retry--) > 0);
> +
> +       if (max_retry <= 0)
> +               return SBI_ETIMEDOUT;
> +
> +       return 0;
> +}
> +
> +#define sifive_i2c_adapter_poll_tip(adap) sifive_i2c_adapter_poll(adap, SIFIVE_I2C_STATUS_TIP)
> +#define sifive_i2c_adapter_poll_busy(adap) sifive_i2c_adapter_poll(adap, SIFIVE_I2C_STATUS_BUSY)
> +
> +static int sifive_i2c_adapter_start(struct sifive_i2c_adapter *adap, uint8_t addr, uint8_t bit)
> +{
> +       uint8_t val = (addr << 1) | bit;
> +
> +       sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, val);
> +       val = SIFIVE_I2C_CMD_STA | SIFIVE_I2C_CMD_WR | SIFIVE_I2C_CMD_IACK;
> +       sifive_i2c_setreg(adap, SIFIVE_I2C_CR, val);
> +
> +       return sifive_i2c_adapter_poll_tip(adap);
> +}
> +
> +static int sifive_i2c_adapter_smbus_write(struct i2c_adapter *ia,
> +                                       uint8_t addr, uint8_t reg,
> +                                       uint8_t *buffer, int len)
> +{
> +       struct sifive_i2c_adapter *adap =
> +               container_of(ia, struct sifive_i2c_adapter, adapter);
> +       int rc = sifive_i2c_adapter_start(adap, addr, SIFIVE_I2C_WRITE_BIT);
> +
> +       if (rc)
> +               return rc;
> +
> +       rc = sifive_i2c_adapter_rxack(adap);
> +       if (rc)
> +               return rc;
> +
> +       /* set register address */
> +       sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, reg);
> +       sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_WR | SIFIVE_I2C_CMD_IACK);
> +       rc = sifive_i2c_adapter_poll_tip(adap);
> +       if (rc)
> +               return rc;
> +
> +       rc = sifive_i2c_adapter_rxack(adap);
> +       if (rc)
> +               return rc;
> +
> +       /* set value */
> +       while (len) {
> +               sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, *buffer);
> +               sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_WR | SIFIVE_I2C_CMD_IACK);
> +
> +               rc = sifive_i2c_adapter_poll_tip(adap);
> +               if (rc)
> +                       return rc;
> +
> +               rc = sifive_i2c_adapter_rxack(adap);
> +               if (rc)
> +                       return rc;
> +
> +               buffer++;
> +               len--;
> +       }
> +
> +       sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_STO | SIFIVE_I2C_CMD_IACK);
> +
> +       /* poll BUSY instead of ACK*/
> +       rc = sifive_i2c_adapter_poll_busy(adap);
> +       if (rc)
> +               return rc;
> +
> +       sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_IACK);
> +
> +       return 0;
> +}
> +
> +static int sifive_i2c_adapter_smbus_read(struct i2c_adapter *ia,
> +                                       uint8_t addr, uint8_t reg,
> +                                       uint8_t *buffer, int len)
> +{
> +       struct sifive_i2c_adapter *adap =
> +               container_of(ia, struct sifive_i2c_adapter, adapter);
> +       int rc;
> +
> +       rc = sifive_i2c_adapter_start(adap, addr, SIFIVE_I2C_WRITE_BIT);
> +       if (rc)
> +               return rc;
> +
> +       rc = sifive_i2c_adapter_rxack(adap);
> +       if (rc)
> +               return rc;
> +
> +       sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, reg);
> +       sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_WR | SIFIVE_I2C_CMD_IACK);
> +
> +       rc = sifive_i2c_adapter_poll_tip(adap);
> +       if (rc)
> +               return rc;
> +
> +       rc = sifive_i2c_adapter_rxack(adap);
> +       if (rc)
> +               return rc;
> +
> +       /* setting addr with high 0 bit */
> +       rc = sifive_i2c_adapter_start(adap, addr, SIFIVE_I2C_READ_BIT);
> +       if (rc)
> +               return rc;
> +
> +       rc = sifive_i2c_adapter_rxack(adap);
> +       if (rc)
> +               return rc;
> +
> +       while (len) {
> +               if (len == 1)
> +                       sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_ACK | SIFIVE_I2C_CMD_RD | SIFIVE_I2C_CMD_IACK);
> +               else
> +                       sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_RD | SIFIVE_I2C_CMD_IACK);
> +
> +               rc = sifive_i2c_adapter_poll_tip(adap);
> +               if (rc)
> +                       return rc;
> +
> +               *buffer = sifive_i2c_getreg(adap, SIFIVE_I2C_RXR);
> +               buffer++;
> +               len--;
> +       }
> +
> +       sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_STO | SIFIVE_I2C_CMD_IACK);
> +       rc = sifive_i2c_adapter_poll_busy(adap);
> +       if (rc)
> +               return rc;
> +
> +       sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_IACK);
> +
> +       return 0;
> +}
> +
> +static int sifive_i2c_adapter_configure(struct i2c_adapter *ia)
> +{
> +       struct sifive_i2c_adapter *adap =
> +               container_of(ia, struct sifive_i2c_adapter, adapter);
> +
> +       /* enable controller/disable interrupts */
> +       sifive_i2c_setreg(adap, SIFIVE_I2C_CTR, SIFIVE_I2C_CTR_EN);
> +
> +       return 0;
> +}
> +
> +static int sifive_i2c_init(void *fdt, int nodeoff,
> +                           const struct fdt_match *match)
> +{
> +       int rc;
> +       struct sifive_i2c_adapter *adapter;
> +       uint64_t addr;
> +
> +       if (sifive_i2c_adapter_count >= SIFIVE_I2C_ADAPTER_MAX)
> +               return SBI_ENOSPC;
> +
> +       adapter = &sifive_i2c_adapter_array[sifive_i2c_adapter_count];
> +
> +       rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &addr, NULL);
> +       if (rc)
> +               return rc;
> +
> +       adapter->addr = addr;
> +       adapter->adapter.driver = &fdt_i2c_adapter_sifive;
> +       adapter->adapter.id = nodeoff;
> +       adapter->adapter.smbus_write = sifive_i2c_adapter_smbus_write;
> +       adapter->adapter.smbus_read = sifive_i2c_adapter_smbus_read;
> +       adapter->adapter.configure = sifive_i2c_adapter_configure;
> +       rc = i2c_adapter_add(&adapter->adapter);
> +       if (rc)
> +               return rc;
> +
> +       sifive_i2c_adapter_count++;
> +       return 0;
> +}
> +
> +static const struct fdt_match sifive_i2c_match[] = {
> +       { .compatible = "sifive,i2c0" },
> +       { },
> +};
> +
> +struct fdt_i2c_adapter fdt_i2c_adapter_sifive = {
> +       .match_table = sifive_i2c_match,
> +       .init = sifive_i2c_init,
> +};
> diff --git a/lib/utils/i2c/objects.mk b/lib/utils/i2c/objects.mk
> index 06baa65..d52ab18 100644
> --- a/lib/utils/i2c/objects.mk
> +++ b/lib/utils/i2c/objects.mk
> @@ -9,3 +9,4 @@
>
>  libsbiutils-objs-y += i2c/i2c.o
>  libsbiutils-objs-y += i2c/fdt_i2c.o
> +libsbiutils-objs-y += i2c/fdt_i2c_sifive.o
> --
> 2.31.1
>
>
> --
> opensbi mailing list
> opensbi@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/opensbi
Nikita Shubin Oct. 20, 2021, 6:51 a.m. UTC | #2
Hello Alexandre!

On Tue, 19 Oct 2021 14:34:30 +0200
Alexandre Ghiti <alexandre.ghiti@canonical.com> wrote:

> > +static inline void sifive_i2c_setreg(struct sifive_i2c_adapter
> > *adap,
> > +                                    int reg, u8 value)
> > +{
> > +       writel(value, (volatile void *)adap->addr + reg);
> > +}
> > +
> > +static inline u8 sifive_i2c_getreg(struct sifive_i2c_adapter
> > *adap, int reg) +{
> > +       return readl((volatile void *)adap->addr + reg);
> > +}
> > +  
> 
> reg is defined as a uint8_t in calling functions and here as an int.

Agreed.
diff mbox series

Patch

diff --git a/lib/utils/i2c/fdt_i2c.c b/lib/utils/i2c/fdt_i2c.c
index fce6d73..bbf9df6 100644
--- a/lib/utils/i2c/fdt_i2c.c
+++ b/lib/utils/i2c/fdt_i2c.c
@@ -18,7 +18,10 @@ 
 
 #include <sbi/sbi_console.h>
 
+extern struct fdt_i2c_adapter fdt_i2c_adapter_sifive;
+
 static struct fdt_i2c_adapter *i2c_adapter_drivers[] = {
+	&fdt_i2c_adapter_sifive
 };
 
 static struct fdt_i2c_adapter
diff --git a/lib/utils/i2c/fdt_i2c_sifive.c b/lib/utils/i2c/fdt_i2c_sifive.c
new file mode 100644
index 0000000..aeb67ee
--- /dev/null
+++ b/lib/utils/i2c/fdt_i2c_sifive.c
@@ -0,0 +1,271 @@ 
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2021 YADRO
+ *
+ * Authors:
+ *   Nikita Shubin <nshubin@yadro.com>
+ */
+
+#include <sbi/riscv_io.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_timer.h>
+#include <sbi_utils/fdt/fdt_helper.h>
+#include <sbi_utils/i2c/fdt_i2c.h>
+
+#define SIFIVE_I2C_ADAPTER_MAX	2
+
+#define SIFIVE_I2C_PRELO	0x00
+#define SIFIVE_I2C_PREHI	0x04
+#define SIFIVE_I2C_CTR		0x08
+#define SIFIVE_I2C_TXR		0x00c
+#define SIFIVE_I2C_RXR		SIFIVE_I2C_TXR
+#define SIFIVE_I2C_CR		0x010
+#define SIFIVE_I2C_SR		SIFIVE_I2C_CR
+
+#define SIFIVE_I2C_CTR_IEN	(1 << 6)
+#define SIFIVE_I2C_CTR_EN	(1 << 7)
+
+#define SIFIVE_I2C_CMD_IACK	(1 << 0)
+#define SIFIVE_I2C_CMD_ACK	(1 << 3)
+#define SIFIVE_I2C_CMD_WR	(1 << 4)
+#define SIFIVE_I2C_CMD_RD	(1 << 5)
+#define SIFIVE_I2C_CMD_STO	(1 << 6)
+#define SIFIVE_I2C_CMD_STA	(1 << 7)
+
+#define SIFIVE_I2C_STATUS_IF	(1 << 0)
+#define SIFIVE_I2C_STATUS_TIP	(1 << 1)
+#define SIFIVE_I2C_STATUS_AL	(1 << 5)
+#define SIFIVE_I2C_STATUS_BUSY	(1 << 6)
+#define SIFIVE_I2C_STATUS_RXACK	(1 << 7)
+
+#define SIFIVE_I2C_WRITE_BIT	(0 << 0)
+#define SIFIVE_I2C_READ_BIT	(1 << 0)
+
+struct sifive_i2c_adapter {
+	unsigned long addr;
+	struct i2c_adapter adapter;
+};
+
+static unsigned int sifive_i2c_adapter_count;
+static struct sifive_i2c_adapter sifive_i2c_adapter_array[SIFIVE_I2C_ADAPTER_MAX];
+
+extern struct fdt_i2c_adapter fdt_i2c_adapter_sifive;
+
+static inline void sifive_i2c_setreg(struct sifive_i2c_adapter *adap,
+				     int reg, u8 value)
+{
+	writel(value, (volatile void *)adap->addr + reg);
+}
+
+static inline u8 sifive_i2c_getreg(struct sifive_i2c_adapter *adap, int reg)
+{
+	return readl((volatile void *)adap->addr + reg);
+}
+
+static int sifive_i2c_adapter_rxack(struct sifive_i2c_adapter *adap)
+{
+	uint8_t val = sifive_i2c_getreg(adap, SIFIVE_I2C_SR);
+
+	if (val & SIFIVE_I2C_STATUS_RXACK)
+		return SBI_EIO;
+
+	return 0;
+}
+
+static int sifive_i2c_adapter_poll(struct sifive_i2c_adapter *adap, uint32_t mask)
+{
+	int max_retry = 5;
+	uint8_t val;
+
+	do {
+		val = sifive_i2c_getreg(adap, SIFIVE_I2C_SR);
+		sbi_timer_mdelay(100);
+	} while ((val & mask) && (max_retry--) > 0);
+
+	if (max_retry <= 0)
+		return SBI_ETIMEDOUT;
+
+	return 0;
+}
+
+#define sifive_i2c_adapter_poll_tip(adap) sifive_i2c_adapter_poll(adap, SIFIVE_I2C_STATUS_TIP)
+#define sifive_i2c_adapter_poll_busy(adap) sifive_i2c_adapter_poll(adap, SIFIVE_I2C_STATUS_BUSY)
+
+static int sifive_i2c_adapter_start(struct sifive_i2c_adapter *adap, uint8_t addr, uint8_t bit)
+{
+	uint8_t val = (addr << 1) | bit;
+
+	sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, val);
+	val = SIFIVE_I2C_CMD_STA | SIFIVE_I2C_CMD_WR | SIFIVE_I2C_CMD_IACK;
+	sifive_i2c_setreg(adap, SIFIVE_I2C_CR, val);
+
+	return sifive_i2c_adapter_poll_tip(adap);
+}
+
+static int sifive_i2c_adapter_smbus_write(struct i2c_adapter *ia,
+					uint8_t addr, uint8_t reg,
+					uint8_t *buffer, int len)
+{
+	struct sifive_i2c_adapter *adap =
+		container_of(ia, struct sifive_i2c_adapter, adapter);
+	int rc = sifive_i2c_adapter_start(adap, addr, SIFIVE_I2C_WRITE_BIT);
+
+	if (rc)
+		return rc;
+
+	rc = sifive_i2c_adapter_rxack(adap);
+	if (rc)
+		return rc;
+
+	/* set register address */
+	sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, reg);
+	sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_WR | SIFIVE_I2C_CMD_IACK);
+	rc = sifive_i2c_adapter_poll_tip(adap);
+	if (rc)
+		return rc;
+
+	rc = sifive_i2c_adapter_rxack(adap);
+	if (rc)
+		return rc;
+
+	/* set value */
+	while (len) {
+		sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, *buffer);
+		sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_WR | SIFIVE_I2C_CMD_IACK);
+
+		rc = sifive_i2c_adapter_poll_tip(adap);
+		if (rc)
+			return rc;
+
+		rc = sifive_i2c_adapter_rxack(adap);
+		if (rc)
+			return rc;
+
+		buffer++;
+		len--;
+	}
+
+	sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_STO | SIFIVE_I2C_CMD_IACK);
+
+	/* poll BUSY instead of ACK*/
+	rc = sifive_i2c_adapter_poll_busy(adap);
+	if (rc)
+		return rc;
+
+	sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_IACK);
+
+	return 0;
+}
+
+static int sifive_i2c_adapter_smbus_read(struct i2c_adapter *ia,
+					uint8_t addr, uint8_t reg,
+					uint8_t *buffer, int len)
+{
+	struct sifive_i2c_adapter *adap =
+		container_of(ia, struct sifive_i2c_adapter, adapter);
+	int rc;
+
+	rc = sifive_i2c_adapter_start(adap, addr, SIFIVE_I2C_WRITE_BIT);
+	if (rc)
+		return rc;
+
+	rc = sifive_i2c_adapter_rxack(adap);
+	if (rc)
+		return rc;
+
+	sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, reg);
+	sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_WR | SIFIVE_I2C_CMD_IACK);
+
+	rc = sifive_i2c_adapter_poll_tip(adap);
+	if (rc)
+		return rc;
+
+	rc = sifive_i2c_adapter_rxack(adap);
+	if (rc)
+		return rc;
+
+	/* setting addr with high 0 bit */
+	rc = sifive_i2c_adapter_start(adap, addr, SIFIVE_I2C_READ_BIT);
+	if (rc)
+		return rc;
+
+	rc = sifive_i2c_adapter_rxack(adap);
+	if (rc)
+		return rc;
+
+	while (len) {
+		if (len == 1)
+			sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_ACK | SIFIVE_I2C_CMD_RD | SIFIVE_I2C_CMD_IACK);
+		else
+			sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_RD | SIFIVE_I2C_CMD_IACK);
+
+		rc = sifive_i2c_adapter_poll_tip(adap);
+		if (rc)
+			return rc;
+
+		*buffer = sifive_i2c_getreg(adap, SIFIVE_I2C_RXR);
+		buffer++;
+		len--;
+	}
+
+	sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_STO | SIFIVE_I2C_CMD_IACK);
+	rc = sifive_i2c_adapter_poll_busy(adap);
+	if (rc)
+		return rc;
+
+	sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_IACK);
+
+	return 0;
+}
+
+static int sifive_i2c_adapter_configure(struct i2c_adapter *ia)
+{
+	struct sifive_i2c_adapter *adap =
+		container_of(ia, struct sifive_i2c_adapter, adapter);
+
+	/* enable controller/disable interrupts */
+	sifive_i2c_setreg(adap, SIFIVE_I2C_CTR, SIFIVE_I2C_CTR_EN);
+
+	return 0;
+}
+
+static int sifive_i2c_init(void *fdt, int nodeoff,
+			    const struct fdt_match *match)
+{
+	int rc;
+	struct sifive_i2c_adapter *adapter;
+	uint64_t addr;
+
+	if (sifive_i2c_adapter_count >= SIFIVE_I2C_ADAPTER_MAX)
+		return SBI_ENOSPC;
+
+	adapter = &sifive_i2c_adapter_array[sifive_i2c_adapter_count];
+
+	rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &addr, NULL);
+	if (rc)
+		return rc;
+
+	adapter->addr = addr;
+	adapter->adapter.driver = &fdt_i2c_adapter_sifive;
+	adapter->adapter.id = nodeoff;
+	adapter->adapter.smbus_write = sifive_i2c_adapter_smbus_write;
+	adapter->adapter.smbus_read = sifive_i2c_adapter_smbus_read;
+	adapter->adapter.configure = sifive_i2c_adapter_configure;
+	rc = i2c_adapter_add(&adapter->adapter);
+	if (rc)
+		return rc;
+
+	sifive_i2c_adapter_count++;
+	return 0;
+}
+
+static const struct fdt_match sifive_i2c_match[] = {
+	{ .compatible = "sifive,i2c0" },
+	{ },
+};
+
+struct fdt_i2c_adapter fdt_i2c_adapter_sifive = {
+	.match_table = sifive_i2c_match,
+	.init = sifive_i2c_init,
+};
diff --git a/lib/utils/i2c/objects.mk b/lib/utils/i2c/objects.mk
index 06baa65..d52ab18 100644
--- a/lib/utils/i2c/objects.mk
+++ b/lib/utils/i2c/objects.mk
@@ -9,3 +9,4 @@ 
 
 libsbiutils-objs-y += i2c/i2c.o
 libsbiutils-objs-y += i2c/fdt_i2c.o
+libsbiutils-objs-y += i2c/fdt_i2c_sifive.o