123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520 |
- /*
- * This file is part of FH8620 BSP for RT-Thread distribution.
- *
- * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd.
- * All rights reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Visit http://www.fullhan.com to get contact with Fullhan.
- *
- * Change Logs:
- * Date Author Notes
- */
-
- #include <rtdevice.h>
- #include <rthw.h>
- #include "i2c.h"
- #include "inc/fh_driverlib.h"
- #include "board_info.h"
- //#define FH_I2C_DEBUG
- #ifdef FH_I2C_DEBUG
- #define PRINT_I2C_DBG(fmt, args...) \
- do \
- { \
- rt_kprintf("FH_I2C_DEBUG: "); \
- rt_kprintf(fmt, ## args); \
- } \
- while(0)
- #else
- #define PRINT_I2C_DBG(fmt, args...) do { } while (0)
- #endif
- static void fh_i2c_xfer_init(struct rt_i2c_bus_device *dev, struct rt_i2c_msg msgs[], rt_uint32_t num)
- {
- struct i2c_driver *i2c_drv = (struct i2c_driver *)dev->priv;
- struct fh_i2c_obj *i2c_obj = (struct fh_i2c_obj *)i2c_drv->priv;
- rt_uint32_t ic_con;
- /* if the slave address is ten bit address, ERROR*/
- if (msgs[i2c_drv->msg_write_idx].flags & I2C_M_TEN)
- {
- rt_kprintf("ERROR: %s, ten bit address is NOT supported\n", __func__);
- return;
- }
- /* Disable the adapter */
- I2C_WaitMasterIdle(i2c_obj);
- I2C_Enable(i2c_obj, RT_FALSE);
- /* set the slave (target) address */
- I2C_SetSlaveAddress(i2c_obj, msgs[i2c_drv->msg_write_idx].addr);
- /* Enable interrupts */
- I2C_SetInterruptMask(i2c_obj, DW_IC_INTR_DEFAULT_MASK);
- /* Enable the adapter */
- I2C_Enable(i2c_obj, RT_TRUE);
- }
- static rt_size_t fh_i2c_xfer(struct rt_i2c_bus_device *dev,
- struct rt_i2c_msg msgs[], rt_uint32_t num)
- {
- struct i2c_driver *i2c_drv = (struct i2c_driver *)dev->priv;
- struct fh_i2c_obj *i2c_obj = (struct fh_i2c_obj *)i2c_drv->priv;
- int ret;
- struct rt_i2c_msg *pmsg = RT_NULL;
- PRINT_I2C_DBG(">>>>>>>>>>>>>%s start\n", __func__);
- rt_completion_init(&i2c_drv->transfer_completion);
- ret = rt_mutex_take(i2c_drv->lock, RT_WAITING_FOREVER );
- i2c_drv->msgs = msgs;
- i2c_drv->msgs_num = num;
- i2c_drv->msg_read_idx = 0;
- i2c_drv->msg_write_idx = 0;
- i2c_drv->cmd_err = 0;
- i2c_drv->msg_err = 0;
- i2c_drv->status = STATUS_IDLE;
- i2c_obj->abort_source = 0;
- ret = I2C_WaitDeviceIdle(i2c_obj);
- if (ret < 0)
- {
- //I2C_SetDataCmd(i2c_obj, 0x200);
- //goto done;
- }
- fh_i2c_xfer_init(dev, msgs, num);
- ret = rt_completion_wait(&i2c_drv->transfer_completion, RT_TICK_PER_SECOND);
- PRINT_I2C_DBG("%s transfer finished\n", "rt_completion_wait");
- if(ret)
- {
- rt_kprintf("ERROR: %s, transfer timeout\n", __func__);
- I2C_SetDataCmd(i2c_obj, 0x200);
- I2C_Init(i2c_obj);
- ret = -RT_ETIMEOUT;
- goto done;
- }
- if (i2c_drv->msg_err)
- {
- rt_kprintf("i2c_priv->msg_err: %d\n", i2c_drv->msg_err);
- ret = i2c_drv->msg_err;
- goto done;
- }
- /* no error */
- if (!i2c_drv->cmd_err)
- {
- /* Disable the adapter */
- I2C_WaitMasterIdle(i2c_obj);
- I2C_Enable(i2c_obj, RT_FALSE);
- ret = num;
- goto done;
- }
- /* We have an error */
- if (i2c_drv->cmd_err == DW_IC_ERR_TX_ABRT)
- {
- rt_kprintf("ERROR: %s, i2c_priv>cmd_err == DW_IC_ERR_TX_ABRT\n", __func__);
- ret = I2C_HandleTxAbort(i2c_obj);
- goto done;
- }
- ret = 1;
- done:
- I2C_Enable(i2c_obj, RT_FALSE);
- rt_mutex_release(i2c_drv->lock);
- PRINT_I2C_DBG(">>>>>>>>>>>>>%s end\n", __func__);
- return ret;
- }
- /*
- * Initiate (and continue) low level master read/write transaction.
- * This function is only called from i2c_fh_isr, and pumping i2c_msg
- * messages into the tx buffer. Even if the size of i2c_msg data is
- * longer than the size of the tx buffer, it handles everything.
- */
- static void i2c_fh_xfer_msg(struct rt_i2c_bus_device *dev)
- {
- struct i2c_driver *i2c_drv = (struct i2c_driver *)dev->priv;
- struct fh_i2c_obj *i2c_obj = (struct fh_i2c_obj *)i2c_drv->priv;
- struct rt_i2c_msg *msgs = i2c_drv->msgs;
- rt_uint32_t intr_mask, cmd;
- int tx_limit, rx_limit;
- rt_uint32_t addr = msgs[i2c_drv->msg_write_idx].addr;
- rt_uint32_t buf_len = i2c_drv->tx_buf_len;
- rt_uint8_t *buf = i2c_drv->tx_buf;
- PRINT_I2C_DBG("%s start, msgs_num: %d, write_idx: %d\n", __func__, i2c_drv->msgs_num, i2c_drv->msg_write_idx);
- intr_mask = DW_IC_INTR_DEFAULT_MASK;
- for (; i2c_drv->msg_write_idx < i2c_drv->msgs_num; i2c_drv->msg_write_idx++)
- {
- /*
- * if target address has changed, we need to
- * reprogram the target address in the i2c
- * adapter when we are done with this transfer
- */
- if (msgs[i2c_drv->msg_write_idx].addr != addr) {
- rt_kprintf(
- "ERROR: %s, invalid target address\n", __func__);
- i2c_drv->msg_err = 1;
- break;
- }
- if (msgs[i2c_drv->msg_write_idx].len == 0) {
- rt_kprintf(
- "ERROR: %s, invalid message length\n", __func__);
- i2c_drv->msg_err = 1;
- break;
- }
- if (!(i2c_drv->status & STATUS_WRITE_IN_PROGRESS))
- {
- /* new i2c_msg */
- buf = msgs[i2c_drv->msg_write_idx].buf;
- buf_len = msgs[i2c_drv->msg_write_idx].len;
- PRINT_I2C_DBG("new msg: len: %d, buf: 0x%x\n", buf_len, buf[0]);
- }
- tx_limit = i2c_obj->config.tx_fifo_depth - I2C_GetTransmitFifoLevel(i2c_obj);
- rx_limit = i2c_obj->config.rx_fifo_depth - I2C_GetReceiveFifoLevel(i2c_obj);
- while (buf_len > 0 && tx_limit > 0 && rx_limit > 0)
- {
- if (msgs[i2c_drv->msg_write_idx].flags & RT_I2C_RD)
- {
- cmd = 0x100;
- rx_limit--;
- }
- else
- {
- cmd = *buf++;
- }
- tx_limit--; buf_len--;
- if(!buf_len)
- {
- //2015-11-8 ar0130 bug fixed
- while(I2C_GetTransmitFifoLevel(i2c_obj));
- cmd |= 0x200;
- }
- I2C_SetDataCmd(i2c_obj, cmd);
- }
- i2c_drv->tx_buf = buf;
- i2c_drv->tx_buf_len = buf_len;
- if (buf_len > 0)
- {
- /* more bytes to be written */
- i2c_drv->status |= STATUS_WRITE_IN_PROGRESS;
- break;
- }
- else
- {
- i2c_drv->status &= ~STATUS_WRITE_IN_PROGRESS;
- }
- }
- /*
- * If i2c_msg index search is completed, we don't need TX_EMPTY
- * interrupt any more.
- */
- if (i2c_drv->msg_write_idx == i2c_drv->msgs_num)
- intr_mask &= ~DW_IC_INTR_TX_EMPTY;
- if (i2c_drv->msg_err)
- {
- rt_kprintf("ERROR: %s, msg_err: %d\n", __func__, i2c_drv->msg_err);
- intr_mask = 0;
- }
- I2C_SetInterruptMask(i2c_obj, intr_mask);
- PRINT_I2C_DBG("%s end\n", __func__);
- }
- static void i2c_fh_read(struct rt_i2c_bus_device *dev)
- {
- struct i2c_driver *i2c_drv = (struct i2c_driver *)dev->priv;
- struct fh_i2c_obj *i2c_obj = (struct fh_i2c_obj *)i2c_drv->priv;
- struct rt_i2c_msg *msgs = i2c_drv->msgs;
- int rx_valid;
- PRINT_I2C_DBG("%s start, msgs_num: %d, read_idx: %d\n", __func__, i2c_drv->msgs_num, i2c_drv->msg_read_idx);
- for (; i2c_drv->msg_read_idx < i2c_drv->msgs_num; i2c_drv->msg_read_idx++)
- {
- rt_uint32_t len;
- rt_uint8_t *buf;
- if (!(msgs[i2c_drv->msg_read_idx].flags & RT_I2C_RD))
- continue;
- if (!(i2c_drv->status & STATUS_READ_IN_PROGRESS))
- {
- len = msgs[i2c_drv->msg_read_idx].len;
- buf = msgs[i2c_drv->msg_read_idx].buf;
- }
- else
- {
- PRINT_I2C_DBG("STATUS_READ_IN_PROGRESS\n");
- len = i2c_drv->rx_buf_len;
- buf = i2c_drv->rx_buf;
- }
- rx_valid = I2C_GetReceiveFifoLevel(i2c_obj);
- if(rx_valid == 0)
- {
- rt_kprintf("ERROR: %s, rx_valid == 0\n", __func__);
- }
- PRINT_I2C_DBG("%s, len=%d, rx_valid=%d\n", __func__, len, rx_valid);
- for (; len > 0 && rx_valid > 0; len--, rx_valid--)
- {
- *buf++ = I2C_GetData(i2c_obj);
- }
- PRINT_I2C_DBG("i2c_fh_read, len: %d, buf[0]: 0x%x\n", msgs[i2c_drv->msg_read_idx].len, msgs[i2c_drv->msg_read_idx].buf[0]);
- if (len > 0)
- {
- PRINT_I2C_DBG("len > 0\n");
- i2c_drv->status |= STATUS_READ_IN_PROGRESS;
- i2c_drv->rx_buf_len = len;
- i2c_drv->rx_buf = buf;
- return;
- }
- else
- i2c_drv->status &= ~STATUS_READ_IN_PROGRESS;
- }
- PRINT_I2C_DBG("%s end\n", __func__);
- }
- /*
- * Interrupt service routine. This gets called whenever an I2C interrupt
- * occurs.
- */
- static void fh_i2c_interrupt(int this_irq, void *dev_id)
- {
- struct i2c_driver *i2c_drv = dev_id;
- struct rt_i2c_bus_device *i2c_bus_dev = i2c_drv->i2c_bus_dev;
- struct fh_i2c_obj *i2c_obj = (struct fh_i2c_obj *)i2c_drv->priv;
- rt_uint32_t stat;
- stat = I2C_ClearAndGetInterrupts(i2c_obj);
- PRINT_I2C_DBG("status: 0x%x, mask: 0x%x\n", stat, I2C_GetInterruptMask(i2c_obj));
- if (stat & DW_IC_INTR_TX_ABRT)
- {
- PRINT_I2C_DBG("DW_IC_INTR_TX_ABRT\n");
- i2c_drv->cmd_err |= DW_IC_ERR_TX_ABRT;
- i2c_drv->status = STATUS_IDLE;
- /*
- * Anytime TX_ABRT is set, the contents of the tx/rx
- * buffers are flushed. Make sure to skip them.
- */
- I2C_SetInterruptMask(i2c_obj, 0);
- goto tx_aborted;
- }
- if (stat & DW_IC_INTR_RX_FULL)
- {
- i2c_fh_read(i2c_bus_dev);
- }
- if (stat & DW_IC_INTR_TX_EMPTY)
- {
- i2c_fh_xfer_msg(i2c_bus_dev);
- }
- /*
- * No need to modify or disable the interrupt mask here.
- * i2c_fh_xfer_msg() will take care of it according to
- * the current transmit status.
- */
- tx_aborted:
- if ((stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET)) || i2c_drv->msg_err)
- rt_completion_done(&i2c_drv->transfer_completion);
- }
- static const struct rt_i2c_bus_device_ops fh_i2c_ops =
- {
- .master_xfer = fh_i2c_xfer,
- };
- int fh_i2c_probe(void *priv_data)
- {
- int ret;
- struct i2c_driver *i2c_drv;
- struct rt_i2c_bus_device *i2c_bus_dev;
- struct fh_i2c_obj *i2c_obj = (struct fh_i2c_obj *)priv_data;
- char i2c_dev_name[5] = {0};
- PRINT_I2C_DBG("%s start\n", __func__);
- i2c_bus_dev = (struct rt_i2c_bus_device*)rt_malloc(sizeof(struct rt_i2c_bus_device));
- rt_memset(i2c_bus_dev, 0, sizeof(struct rt_i2c_bus_device));
- i2c_bus_dev->ops = &fh_i2c_ops;
- rt_sprintf(i2c_dev_name, "%s%d", "i2c", i2c_obj->id);
- ret = rt_i2c_bus_device_register(i2c_bus_dev, i2c_dev_name);
- if (ret != RT_EOK)
- {
- rt_kprintf("ERROR:rt_spi_bus_register failed, ret=%d\n", ret);
- return -RT_ENOMEM;
- }
- //priv struct init
- i2c_drv = (struct i2c_driver*)rt_malloc(sizeof(struct i2c_driver));
- rt_memset(i2c_drv, 0, sizeof(struct i2c_driver));
- i2c_drv->i2c_bus_dev = i2c_bus_dev;
- i2c_drv->priv = priv_data;
- i2c_bus_dev->priv = i2c_drv;
- i2c_drv->lock = rt_mutex_create("i2c_mux", RT_IPC_FLAG_FIFO);
- if(i2c_obj->id == 0){
- rt_hw_interrupt_install(i2c_obj->irq, fh_i2c_interrupt,
- (void *)i2c_drv, "i2c_0");
- }
- else if(i2c_obj->id == 1){
- rt_hw_interrupt_install(i2c_obj->irq, fh_i2c_interrupt,
- (void *)i2c_drv, "i2c_1");
- }
- rt_hw_interrupt_umask(i2c_obj->irq);
- //fixme: get from clk tree
- i2c_obj->input_clock = 15000;
- I2C_Init(i2c_obj);
- PRINT_I2C_DBG("%s end\n", __func__);
- return ret;
- }
- int fh_i2c_exit(void *priv_data)
- {
- return 0;
- }
- struct fh_board_ops i2c_driver_ops =
- {
- .probe = fh_i2c_probe,
- .exit = fh_i2c_exit,
- };
- void rt_hw_i2c_init(void)
- {
- int ret;
- PRINT_I2C_DBG("%s start\n", __func__);
- fh_board_driver_register("i2c", &i2c_driver_ops);
- PRINT_I2C_DBG("%s end\n", __func__);
- //fixme: never release?
- }
- static rt_err_t fh_i2c_read_reg(struct rt_i2c_bus_device *fh81_i2c,
- rt_uint16_t reg, rt_uint8_t *data) {
- struct rt_i2c_msg msg[2];
- rt_uint8_t send_buf[2];
- rt_uint8_t recv_buf[1] = {0};
- PRINT_I2C_DBG("%s start\n", __func__);
- // send_buf[0] = ((reg >> 8) & 0xff);
- send_buf[0] = (reg & 0xFF);
- msg[0].addr = 0x51;
- msg[0].flags = RT_I2C_WR;
- msg[0].len = 1;
- msg[0].buf = send_buf;
- msg[1].addr = 0x51;
- msg[1].flags = RT_I2C_RD;
- msg[1].len = 1;
- msg[1].buf = recv_buf;
- rt_i2c_transfer(fh81_i2c, msg, 2);
- *data = recv_buf[0];
- return RT_EOK;
- }
- static rt_err_t fh_i2c_write_reg(struct rt_i2c_bus_device *fh81_i2c,
- rt_uint16_t reg, rt_uint8_t data) {
- struct rt_i2c_msg msg;
- rt_uint8_t send_buf[3];
- PRINT_I2C_DBG("%s start\n", __func__);
- // send_buf[0] = ((reg >> 8) & 0xff);
- send_buf[1] = (reg & 0xFF);
- send_buf[2] = data;
- msg.addr = 0x51;
- msg.flags = RT_I2C_WR;
- msg.len = 2;
- msg.buf = send_buf;
- rt_i2c_transfer(fh81_i2c, &msg, 1);
- PRINT_I2C_DBG("%s end\n", __func__);
- return RT_EOK;
- }
- void i2c_test_sensor() {
- struct rt_i2c_bus_device *fh81_i2c;
- struct rt_i2c_msg msg[2];
- rt_uint8_t data[1] = { 0x00 };
- fh81_i2c = rt_i2c_bus_device_find("i2c1");
- fh_i2c_write_reg(fh81_i2c, 0x04, 0x02);
- fh_i2c_read_reg(fh81_i2c, 0x02, data);
- rt_kprintf("data read from 0x3038 is 0x%x\r\n", data[0]);
- PRINT_I2C_DBG("%s end\n", __func__);
- }
- #ifdef RT_USING_FINSH
- #include <finsh.h>
- FINSH_FUNCTION_EXPORT(i2c_test_sensor, sensor i2c test);
- #endif
|