123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 |
- /*
- * 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);
- }
|