123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277 |
- /*
- * SPDX-License-Identifier: BSD-2-Clause
- *
- * Copyright (c) 2021 YADRO
- *
- * Authors:
- * Nikita Shubin <n.shubin@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,
- uint8_t reg, uint8_t value)
- {
- writel(value, (volatile char *)adap->addr + reg);
- }
- static inline uint8_t sifive_i2c_getreg(struct sifive_i2c_adapter *adap,
- uint8_t reg)
- {
- return readl((volatile char *)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)
- {
- unsigned int timeout = 1; // [msec]
- int count = 0;
- uint8_t val;
- sbi_timer_udelay(80); // worst case if bus speed is 100 kHz
- do {
- val = sifive_i2c_getreg(adap, SIFIVE_I2C_SR);
- if (!(val & mask))
- return 0;
- sbi_timer_udelay(1);
- count += 1;
- if (count == (timeout * 1000))
- return SBI_ETIMEDOUT;
- } while (1);
- }
- #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_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_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_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.write = sifive_i2c_adapter_write;
- adapter->adapter.read = sifive_i2c_adapter_read;
- 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,
- };
|