summaryrefslogtreecommitdiff
path: root/lib/utils/i2c/dw_i2c.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/utils/i2c/dw_i2c.c')
-rw-r--r--lib/utils/i2c/dw_i2c.c190
1 files changed, 190 insertions, 0 deletions
diff --git a/lib/utils/i2c/dw_i2c.c b/lib/utils/i2c/dw_i2c.c
new file mode 100644
index 0000000..e2ffc71
--- /dev/null
+++ b/lib/utils/i2c/dw_i2c.c
@@ -0,0 +1,190 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2022 starfivetech.com
+ *
+ * Authors:
+ * Minda Chen <minda.chen@starfivetech.com>
+ */
+
+#include <sbi/riscv_io.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_timer.h>
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_string.h>
+#include <sbi/sbi_bitops.h>
+#include <sbi_utils/i2c/dw_i2c.h>
+
+#define DW_IC_CON 0x00
+#define DW_IC_TAR 0x04
+#define DW_IC_SAR 0x08
+#define DW_IC_DATA_CMD 0x10
+#define DW_IC_SS_SCL_HCNT 0x14
+#define DW_IC_SS_SCL_LCNT 0x18
+#define DW_IC_FS_SCL_HCNT 0x1c
+#define DW_IC_FS_SCL_LCNT 0x20
+#define DW_IC_HS_SCL_HCNT 0x24
+#define DW_IC_HS_SCL_LCNT 0x28
+#define DW_IC_INTR_STAT 0x2c
+#define DW_IC_INTR_MASK 0x30
+#define DW_IC_RAW_INTR_STAT 0x34
+#define DW_IC_RX_TL 0x38
+#define DW_IC_TX_TL 0x3c
+#define DW_IC_CLR_INTR 0x40
+#define DW_IC_CLR_RX_UNDER 0x44
+#define DW_IC_CLR_RX_OVER 0x48
+#define DW_IC_CLR_TX_OVER 0x4c
+#define DW_IC_CLR_RD_REQ 0x50
+#define DW_IC_CLR_TX_ABRT 0x54
+#define DW_IC_CLR_RX_DONE 0x58
+#define DW_IC_CLR_ACTIVITY 0x5c
+#define DW_IC_CLR_STOP_DET 0x60
+#define DW_IC_CLR_START_DET 0x64
+#define DW_IC_CLR_GEN_CALL 0x68
+#define DW_IC_ENABLE 0x6c
+#define DW_IC_STATUS 0x70
+#define DW_IC_TXFLR 0x74
+#define DW_IC_RXFLR 0x78
+#define DW_IC_SDA_HOLD 0x7c
+#define DW_IC_TX_ABRT_SOURCE 0x80
+#define DW_IC_ENABLE_STATUS 0x9c
+#define DW_IC_CLR_RESTART_DET 0xa8
+#define DW_IC_COMP_PARAM_1 0xf4
+#define DW_IC_COMP_VERSION 0xf8
+
+#define DW_I2C_STATUS_TXFIFO_EMPTY BIT(2)
+#define DW_I2C_STATUS_RXFIFO_NOT_EMPTY BIT(3)
+
+#define IC_DATA_CMD_READ BIT(8)
+#define IC_DATA_CMD_STOP BIT(9)
+#define IC_DATA_CMD_RESTART BIT(10)
+#define IC_INT_STATUS_STOPDET BIT(9)
+
+static inline void dw_i2c_setreg(struct dw_i2c_adapter *adap,
+ u8 reg, u32 value)
+{
+ writel(value, (void *)adap->addr + reg);
+}
+
+static inline u32 dw_i2c_getreg(struct dw_i2c_adapter *adap,
+ u32 reg)
+{
+ return readl((void *)adap->addr + reg);
+}
+
+static int dw_i2c_adapter_poll(struct dw_i2c_adapter *adap,
+ u32 mask, u32 addr,
+ bool inverted)
+{
+ unsigned int timeout = 10; /* msec */
+ int count = 0;
+ u32 val;
+
+ do {
+ val = dw_i2c_getreg(adap, addr);
+ if (inverted) {
+ if (!(val & mask))
+ return 0;
+ } else {
+ if (val & mask)
+ return 0;
+ }
+ sbi_timer_udelay(2);
+ count += 1;
+ if (count == (timeout * 1000))
+ return SBI_ETIMEDOUT;
+ } while (1);
+}
+
+#define dw_i2c_adapter_poll_rxrdy(adap) \
+ dw_i2c_adapter_poll(adap, DW_I2C_STATUS_RXFIFO_NOT_EMPTY, DW_IC_STATUS, 0)
+#define dw_i2c_adapter_poll_txfifo_ready(adap) \
+ dw_i2c_adapter_poll(adap, DW_I2C_STATUS_TXFIFO_EMPTY, DW_IC_STATUS, 0)
+
+static int dw_i2c_write_addr(struct dw_i2c_adapter *adap, u8 addr)
+{
+ dw_i2c_setreg(adap, DW_IC_ENABLE, 0);
+ dw_i2c_setreg(adap, DW_IC_TAR, addr);
+ dw_i2c_setreg(adap, DW_IC_ENABLE, 1);
+
+ return 0;
+}
+
+static int dw_i2c_adapter_read(struct i2c_adapter *ia, u8 addr,
+ u8 reg, u8 *buffer, int len)
+{
+ struct dw_i2c_adapter *adap =
+ container_of(ia, struct dw_i2c_adapter, adapter);
+ int rc;
+
+ dw_i2c_write_addr(adap, addr);
+
+ rc = dw_i2c_adapter_poll_txfifo_ready(adap);
+ if (rc)
+ return rc;
+
+ /* set register address */
+ dw_i2c_setreg(adap, DW_IC_DATA_CMD, reg);
+
+ /* set value */
+ while (len) {
+ if (len == 1)
+ dw_i2c_setreg(adap, DW_IC_DATA_CMD,
+ IC_DATA_CMD_READ | IC_DATA_CMD_STOP);
+ else
+ dw_i2c_setreg(adap, DW_IC_DATA_CMD, IC_DATA_CMD_READ);
+
+ rc = dw_i2c_adapter_poll_rxrdy(adap);
+ if (rc)
+ return rc;
+
+ *buffer = dw_i2c_getreg(adap, DW_IC_DATA_CMD) & 0xff;
+ buffer++;
+ len--;
+ }
+
+ return 0;
+}
+
+static int dw_i2c_adapter_write(struct i2c_adapter *ia, u8 addr,
+ u8 reg, u8 *buffer, int len)
+{
+ struct dw_i2c_adapter *adap =
+ container_of(ia, struct dw_i2c_adapter, adapter);
+ int rc;
+
+ dw_i2c_write_addr(adap, addr);
+
+ rc = dw_i2c_adapter_poll_txfifo_ready(adap);
+ if (rc)
+ return rc;
+
+ /* set register address */
+ dw_i2c_setreg(adap, DW_IC_DATA_CMD, reg);
+
+ while (len) {
+ rc = dw_i2c_adapter_poll_txfifo_ready(adap);
+ if (rc)
+ return rc;
+
+ if (len == 1)
+ dw_i2c_setreg(adap, DW_IC_DATA_CMD, *buffer | IC_DATA_CMD_STOP);
+ else
+ dw_i2c_setreg(adap, DW_IC_DATA_CMD, *buffer);
+
+ buffer++;
+ len--;
+ }
+ rc = dw_i2c_adapter_poll_txfifo_ready(adap);
+
+ return rc;
+}
+
+int dw_i2c_init(struct i2c_adapter *adapter, int nodeoff)
+{
+ adapter->id = nodeoff;
+ adapter->write = dw_i2c_adapter_write;
+ adapter->read = dw_i2c_adapter_read;
+
+ return i2c_adapter_add(adapter);
+}