123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574 |
- /**************************************************************************//**
- *
- * @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved.
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2021-04-20 Wayne First version
- ******************************************************************************/
- #include <rtconfig.h>
- #if defined( BSP_USING_I2C)
- #include <rtdevice.h>
- #include "NuMicro.h"
- //#include <drv_i2c.h>
- #include <drv_sys.h>
- /* Private define ---------------------------------------------------------------*/
- #define LOG_TAG "drv.i2c"
- #define DBG_ENABLE
- #define DBG_SECTION_NAME LOG_TAG
- #define DBG_LEVEL DBG_ERROR
- #define DBG_COLOR
- #include <rtdbg.h>
- #define I2C_REG_WRITE(dev, addr, byte) outpw(dev->base + addr, byte)
- #define I2C_REG_READ(dev, addr) inpw(dev->base + addr)
- #define I2C_DISABLE(dev) I2C_REG_WRITE(dev, I2C_CSR, 0x00) /* Disable i2c core and interrupt */
- #define I2C_ENABLE(dev) I2C_REG_WRITE(dev, I2C_CSR, 0x3) /* Enable i2c core and interrupt */
- #define I2C_ISBUSFREE(dev) (((I2C_REG_READ(dev, I2C_SWR) & 0x18) == 0x18 && (I2C_REG_READ(dev, I2C_CSR) & 0x0400) == 0) ? 1 : 0)
- #define I2C_SIGNAL_TIMEOUT 5000
- enum
- {
- I2C_START = -1,
- #if defined(BSP_USING_I2C0)
- I2C0_IDX,
- #endif
- #if defined(BSP_USING_I2C1)
- I2C1_IDX,
- #endif
- I2C_CNT
- };
- /* Private typedef --------------------------------------------------------------*/
- typedef struct
- {
- int32_t base; /* i2c bus number */
- volatile int32_t state;
- int32_t addr;
- uint32_t last_error;
- int32_t bNackValid;
- uint32_t subaddr;
- int32_t subaddr_len;
- volatile uint32_t pos;
- volatile uint32_t len;
- uint8_t *buffer;
- struct rt_completion signal;
- } nu_i2c_dev;
- typedef nu_i2c_dev *nu_i2c_dev_t;
- typedef struct
- {
- struct rt_i2c_bus_device parent;
- char *name;
- IRQn_Type irqn;
- E_SYS_IPRST rstidx;
- E_SYS_IPCLK clkidx;
- struct rt_i2c_msg *cur_i2c_msg;
- nu_i2c_dev dev;
- } nu_i2c_bus ;
- typedef nu_i2c_bus *nu_i2c_bus_t;
- /* Private variables ------------------------------------------------------------*/
- static nu_i2c_bus nu_i2c_arr [ ] =
- {
- #if defined(BSP_USING_I2C0)
- {
- .dev =
- {
- .base = I2C0_BA,
- },
- .name = "i2c0",
- .irqn = IRQ_I2C0,
- .rstidx = I2C0RST,
- .clkidx = I2C0CKEN,
- },
- #endif
- #if defined(BSP_USING_I2C1)
- {
- .dev =
- {
- .base = I2C1_BA,
- },
- .name = "i2c1",
- .irqn = IRQ_I2C1,
- .rstidx = I2C1RST,
- .clkidx = I2C1CKEN,
- },
- #endif
- };
- /* Private functions ------------------------------------------------------------*/
- /**
- * @brief Set i2c interface speed
- * @param[in] dev i2c device structure pointer
- * @param[in] sp i2c speed
- * @return 0 or I2C_ERR_NOTTY
- */
- static int32_t nu_i2c_set_speed(nu_i2c_dev_t psNuI2cDev, int32_t sp)
- {
- uint32_t d;
- if ((sp != 100) && (sp != 400))
- return (I2C_ERR_NOTTY);
- d = (sysGetClock(SYS_PCLK) * 1000) / (sp * 5) - 1;
- I2C_REG_WRITE(psNuI2cDev, I2C_DIVIDER, d & 0xffff);
- return 0;
- }
- /**
- * @brief Configure i2c command
- * @param[in] dev i2c device structure pointer
- * @param[in] cmd command
- * @return None
- */
- static void nu_i2c_command(nu_i2c_dev_t psNuI2cDev, int32_t cmd)
- {
- psNuI2cDev->bNackValid = (cmd & I2C_CMD_WRITE) ? 1 : 0;
- I2C_REG_WRITE(psNuI2cDev, I2C_CMDR, cmd);
- }
- /**
- * @brief Configure slave address data
- * @param[in] dev i2c device structure pointer
- * @param[in] mode could be write or read
- * @return None
- */
- static void nu_i2c_calculate_address(nu_i2c_dev_t psNuI2cDev, int32_t mode)
- {
- int32_t i;
- uint32_t subaddr = psNuI2cDev->subaddr;
- psNuI2cDev->buffer[0] = (((psNuI2cDev->addr << 1) & 0xfe) | I2C_WRITE) & 0xff;
- for (i = psNuI2cDev->subaddr_len; i > 0; i--)
- {
- psNuI2cDev->buffer[i] = subaddr & 0xff;
- subaddr >>= 8;
- }
- if (mode == I2C_STATE_READ)
- {
- i = psNuI2cDev->subaddr_len + 1;
- psNuI2cDev->buffer[i] = (((psNuI2cDev->addr << 1) & 0xfe)) | I2C_READ;
- }
- }
- /**
- * @brief Reset some variables
- * @param[in] dev i2c device structure pointer
- * @return None
- */
- static void nu_i2c_reset(nu_i2c_dev_t psNuI2cDev)
- {
- psNuI2cDev->addr = -1;
- psNuI2cDev->last_error = 0;
- psNuI2cDev->subaddr = 0;
- psNuI2cDev->subaddr_len = 0;
- }
- static void nu_i2c_isr(int vector, void *param)
- {
- nu_i2c_bus_t psNuI2CBus = (nu_i2c_bus_t)param;
- nu_i2c_dev_t psNuI2CDev = (nu_i2c_dev_t)&psNuI2CBus->dev;
- struct rt_i2c_msg *pmsg = psNuI2CBus->cur_i2c_msg;
- uint32_t msg_flag = pmsg->flags;
- uint32_t csr, val;
- csr = I2C_REG_READ(psNuI2CDev, I2C_CSR);
- csr |= 0x04;
- /* Clear interrupt flag */
- I2C_REG_WRITE(psNuI2CDev, I2C_CSR, csr);
- if (psNuI2CDev->state == I2C_STATE_NOP)
- return;
- /* NACK only valid in WRITE */
- if ((csr & 0x800) && psNuI2CDev->bNackValid && !(msg_flag & RT_I2C_IGNORE_NACK))
- {
- rt_kprintf("I2C W/ NACK\n");
- psNuI2CDev->last_error = I2C_ERR_NACK;
- nu_i2c_command(psNuI2CDev, I2C_CMD_STOP);
- psNuI2CDev->state = I2C_STATE_NOP;
- rt_completion_done(&psNuI2CDev->signal);
- }
- /* Arbitration lost */
- else if (csr & 0x200)
- {
- rt_kprintf("Arbitration lost\n");
- psNuI2CDev->last_error = I2C_ERR_LOSTARBITRATION;
- psNuI2CDev->state = I2C_STATE_NOP;
- rt_completion_done(&psNuI2CDev->signal);
- }
- /* Transmit complete */
- else if (!(csr & 0x100))
- {
- /* Send address state */
- if (psNuI2CDev->pos < psNuI2CDev->subaddr_len + 1)
- {
- val = psNuI2CDev->buffer[psNuI2CDev->pos++] & 0xff;
- I2C_REG_WRITE(psNuI2CDev, I2C_TxR, val);
- nu_i2c_command(psNuI2CDev, I2C_CMD_WRITE);
- }
- else if (psNuI2CDev->state == I2C_STATE_READ)
- {
- /* Sub-address send over, begin restart a read command */
- if (psNuI2CDev->pos == psNuI2CDev->subaddr_len + 1)
- {
- val = psNuI2CDev->buffer[psNuI2CDev->pos++];
- I2C_REG_WRITE(psNuI2CDev, I2C_TxR, val);
- nu_i2c_command(psNuI2CDev, I2C_CMD_START | I2C_CMD_WRITE);
- }
- else
- {
- psNuI2CDev->buffer[psNuI2CDev->pos++] = I2C_REG_READ(psNuI2CDev, I2C_RxR) & 0xff;
- if (psNuI2CDev->pos < psNuI2CDev->len)
- {
- /* Last character */
- if (psNuI2CDev->pos == psNuI2CDev->len - 1)
- nu_i2c_command(psNuI2CDev, I2C_CMD_READ | I2C_CMD_STOP | I2C_CMD_NACK);
- else
- nu_i2c_command(psNuI2CDev, I2C_CMD_READ);
- }
- else
- {
- psNuI2CDev->state = I2C_STATE_NOP;
- rt_completion_done(&psNuI2CDev->signal);
- }
- }
- }
- /* Write data */
- else if (psNuI2CDev->state == I2C_STATE_WRITE)
- {
- if (psNuI2CDev->pos < psNuI2CDev->len)
- {
- val = psNuI2CDev->buffer[psNuI2CDev->pos];
- I2C_REG_WRITE(psNuI2CDev, I2C_TxR, val);
- /* Last character */
- if (psNuI2CDev->pos == psNuI2CDev->len - 1)
- nu_i2c_command(psNuI2CDev, I2C_CMD_WRITE | I2C_CMD_STOP);
- else
- nu_i2c_command(psNuI2CDev, I2C_CMD_WRITE);
- psNuI2CDev->pos ++;
- }
- else
- {
- psNuI2CDev->state = I2C_STATE_NOP;
- rt_completion_done(&psNuI2CDev->signal);
- }
- }
- }
- }
- /**
- * @brief Read data from I2C slave.
- * @param[in] psNuI2cDev is interface structure pointer.
- * @param[in] pmsg is pointer of rt i2c message structure.
- * @return read status.
- * @retval >0 length when success.
- * @retval I2C_ERR_BUSY Interface busy.
- * @retval I2C_ERR_IO Interface not opened.
- * @retval I2C_ERR_NODEV No such device.
- * @retval I2C_ERR_NACK Slave returns an erroneous ACK.
- * @retval I2C_ERR_LOSTARBITRATION arbitration lost happen.
- */
- static int32_t nu_i2c_read(nu_i2c_dev_t psNuI2cDev, struct rt_i2c_msg *pmsg)
- {
- uint8_t *buf = pmsg->buf;
- uint32_t len = pmsg->len;
- RT_ASSERT(len);
- RT_ASSERT(buf);
- if (len > I2C_MAX_BUF_LEN - 10)
- len = I2C_MAX_BUF_LEN - 10;
- psNuI2cDev->state = I2C_STATE_READ;
- psNuI2cDev->pos = 1;
- /* Current ISR design will get one garbage byte */
- /* plus 1 unused char */
- psNuI2cDev->len = psNuI2cDev->subaddr_len + 1 + len + 2;
- psNuI2cDev->last_error = 0;
- /* Get slave address */
- nu_i2c_calculate_address(psNuI2cDev, I2C_STATE_READ);
- /* Enable I2C-EN */
- I2C_ENABLE(psNuI2cDev);
- /* Send first byte to transfer the message. */
- I2C_REG_WRITE(psNuI2cDev, I2C_TxR, psNuI2cDev->buffer[0] & 0xff);
- if (!I2C_ISBUSFREE(psNuI2cDev))
- return (I2C_ERR_BUSY);
- rt_completion_init(&psNuI2cDev->signal);
- nu_i2c_command(psNuI2cDev, I2C_CMD_START | I2C_CMD_WRITE);
- if ((RT_EOK == rt_completion_wait(&psNuI2cDev->signal, I2C_SIGNAL_TIMEOUT)))
- {
- rt_memcpy(buf, psNuI2cDev->buffer + psNuI2cDev->subaddr_len + 3, len);
- psNuI2cDev->subaddr += len;
- }
- else
- {
- rt_kprintf("[%s]Wait signal timeout.\n", __func__);
- len = 0;
- }
- /* Disable I2C-EN */
- I2C_DISABLE(psNuI2cDev);
- if (psNuI2cDev->last_error)
- return (psNuI2cDev->last_error);
- return len;
- }
- /**
- * @brief Write data from I2C slave.
- * @param[in] psNuI2cDev is interface structure pointer.
- * @param[in] pmsg is pointer of rt i2c message structure.
- * @return write status.
- * @retval >0 length when success.
- * @retval I2C_ERR_BUSY Interface busy.
- * @retval I2C_ERR_IO Interface not opened.
- * @retval I2C_ERR_NODEV No such device.
- * @retval I2C_ERR_NACK Slave returns an erroneous ACK.
- * @retval I2C_ERR_LOSTARBITRATION arbitration lost happen.
- */
- static int32_t nu_i2c_write(nu_i2c_dev_t psNuI2cDev, struct rt_i2c_msg *pmsg)
- {
- uint8_t *buf = pmsg->buf;
- uint32_t len = pmsg->len;
- RT_ASSERT(len);
- RT_ASSERT(buf);
- if (len > I2C_MAX_BUF_LEN - 10)
- len = I2C_MAX_BUF_LEN - 10;
- rt_memcpy(psNuI2cDev->buffer + psNuI2cDev->subaddr_len + 1, buf, len);
- psNuI2cDev->state = I2C_STATE_WRITE;
- psNuI2cDev->pos = 1;
- psNuI2cDev->len = psNuI2cDev->subaddr_len + 1 + len;
- psNuI2cDev->last_error = 0;
- /* Get slave address */
- nu_i2c_calculate_address(psNuI2cDev, I2C_STATE_WRITE);
- /* Enable I2C-EN */
- I2C_ENABLE(psNuI2cDev);
- /* Send first byte to transfer the message. */
- I2C_REG_WRITE(psNuI2cDev, I2C_TxR, psNuI2cDev->buffer[0] & 0xff);
- if (!I2C_ISBUSFREE(psNuI2cDev))
- return (I2C_ERR_BUSY);
- rt_completion_init(&psNuI2cDev->signal);
- nu_i2c_command(psNuI2cDev, I2C_CMD_START | I2C_CMD_WRITE);
- if ((RT_EOK == rt_completion_wait(&psNuI2cDev->signal, I2C_SIGNAL_TIMEOUT)))
- {
- psNuI2cDev->subaddr += len;
- }
- else
- {
- rt_kprintf("[%s]Wait signal timeout.\n", __func__);
- len = 0;
- }
- /* Disable I2C-EN */
- I2C_DISABLE(psNuI2cDev);
- if (psNuI2cDev->last_error)
- return (psNuI2cDev->last_error);
- return len;
- }
- /**
- * @brief Support some I2C driver commands for application.
- * @param[in] psNuI2cDev is interface structure pointer.
- * @param[in] cmd is command.
- * @param[in] arg0 is the first argument of command.
- * @param[in] arg1 is the second argument of command.
- * @return command status.
- * @retval 0 Success.
- * @retval I2C_ERR_IO Interface not opened.
- * @retval I2C_ERR_NODEV No such device.
- * @retval I2C_ERR_NOTTY Command not support, or parameter error.
- */
- static int32_t nu_i2c_ioctl(nu_i2c_dev_t psNuI2cDev, uint32_t cmd, uint32_t arg0, uint32_t arg1)
- {
- switch (cmd)
- {
- case I2C_IOC_SET_DEV_ADDRESS:
- psNuI2cDev->addr = arg0;
- break;
- case I2C_IOC_SET_SPEED:
- return nu_i2c_set_speed(psNuI2cDev, (int32_t)arg0);
- case I2C_IOC_SET_SUB_ADDRESS:
- if (arg1 > 4)
- {
- return (I2C_ERR_NOTTY);
- }
- psNuI2cDev->subaddr = arg0;
- psNuI2cDev->subaddr_len = arg1;
- break;
- default:
- return (I2C_ERR_NOTTY);
- }
- return (0);
- }
- static rt_size_t nu_i2c_mst_xfer(struct rt_i2c_bus_device *bus,
- struct rt_i2c_msg msgs[],
- rt_uint32_t num)
- {
- nu_i2c_bus_t psNuI2cBus;
- nu_i2c_dev_t psNuI2cDev;
- rt_size_t i;
- rt_err_t ret;
- struct rt_i2c_msg *pmsg;
- RT_ASSERT(bus);
- psNuI2cBus = (nu_i2c_bus_t) bus;
- psNuI2cDev = &psNuI2cBus->dev;
- for (i = 0; i < num; i++)
- {
- if (!I2C_ISBUSFREE(psNuI2cDev))
- break;
- pmsg = psNuI2cBus->cur_i2c_msg = &msgs[i];
- /* Not support 10bit. */
- if ((pmsg->flags & RT_I2C_ADDR_10BIT)
- || (pmsg->len == 0))
- break;
- /* Set device address */
- nu_i2c_reset(psNuI2cDev);
- nu_i2c_ioctl(psNuI2cDev, I2C_IOC_SET_DEV_ADDRESS, pmsg->addr, 0);
- if (pmsg->flags & RT_I2C_RD)
- {
- ret = nu_i2c_read(psNuI2cDev, pmsg);
- }
- else
- {
- ret = nu_i2c_write(psNuI2cDev, pmsg);
- }
- if (ret != pmsg->len) break;
- }
- return i;
- }
- static rt_err_t nu_i2c_bus_control(struct rt_i2c_bus_device *bus, rt_uint32_t u32Cmd, rt_uint32_t u32Value)
- {
- nu_i2c_bus_t psNuI2cBus;
- nu_i2c_dev_t psNuI2cDev;
- RT_ASSERT(bus);
- psNuI2cBus = (nu_i2c_bus_t) bus;
- psNuI2cDev = &psNuI2cBus->dev;
- switch (u32Cmd)
- {
- case RT_I2C_DEV_CTRL_CLK:
- nu_i2c_set_speed(psNuI2cDev, (int32_t)u32Value);
- break;
- default:
- return -RT_EIO;
- }
- return RT_EOK;
- }
- static const struct rt_i2c_bus_device_ops nu_i2c_ops =
- {
- .master_xfer = nu_i2c_mst_xfer,
- .slave_xfer = NULL,
- .i2c_bus_control = nu_i2c_bus_control,
- };
- /* Public functions -------------------------------------------------------------*/
- int rt_hw_i2c_init(void)
- {
- int i;
- rt_err_t ret;
- for (i = (I2C_START + 1); i < I2C_CNT; i++)
- {
- nu_i2c_dev_t psNuI2cDev = &nu_i2c_arr[i].dev;
- nu_i2c_arr[i].parent.ops = &nu_i2c_ops;
- psNuI2cDev->buffer = rt_malloc(I2C_MAX_BUF_LEN);
- RT_ASSERT(psNuI2cDev->buffer);
- /* Enable I2C engine clock and reset. */
- nu_sys_ipclk_enable(nu_i2c_arr[i].clkidx);
- nu_sys_ip_reset(nu_i2c_arr[i].rstidx);
- nu_i2c_ioctl(psNuI2cDev, I2C_IOC_SET_SPEED, 100, 0);
- /* Register ISR and Respond IRQ. */
- rt_hw_interrupt_install(nu_i2c_arr[i].irqn, nu_i2c_isr, &nu_i2c_arr[i], nu_i2c_arr[i].name);
- rt_hw_interrupt_umask(nu_i2c_arr[i].irqn);
- ret = rt_i2c_bus_device_register(&nu_i2c_arr[i].parent, nu_i2c_arr[i].name);
- RT_ASSERT(RT_EOK == ret);
- }
- return 0;
- }
- INIT_DEVICE_EXPORT(rt_hw_i2c_init);
- #endif /* BSP_USING_I2C */
|