/* * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2022 starfivetech.com * * Authors: * Minda Chen */ #include #include #include #include #include #include #include #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); }