liangzhanggb commented on a change in pull request #2669: URL: https://github.com/apache/incubator-nuttx/pull/2669#discussion_r557015114
########## File path: arch/risc-v/src/bl602/bl602_i2c.c ########## @@ -0,0 +1,1049 @@ +/**************************************************************************** + * arch/risc-v/src/bl602/bl602_i2c.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdbool.h> +#include <errno.h> +#include <debug.h> +#include <time.h> + +#include <nuttx/arch.h> +#include <nuttx/irq.h> +#include <nuttx/clock.h> +#include <nuttx/semaphore.h> +#include <nuttx/i2c/i2c_master.h> + +#include <arch/board/board.h> +#include "riscv_arch.h" + +#include "hardware/bl602_i2c.h" +#include "hardware/bl602_glb.h" +#include "bl602_gpio.h" +#include "bl602_i2c.h" +#include "bl602_glb.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +#define I2C0 0 + +#define PUT_UINT32_LE(n, b, i) \ + { \ + (b)[(i)] = (uint8_t)((n)); \ + (b)[(i) + 1] = (uint8_t)((n) >> 8); \ + (b)[(i) + 2] = (uint8_t)((n) >> 16); \ + (b)[(i) + 3] = (uint8_t)((n) >> 24); \ + } + +#define SYS_TEMCORE_CLOCK_GET (*((volatile uint32_t *)(0x4000F108))) + +/* I2C state */ + +#define EV_I2C_END_INT 0 +#define EV_I2C_TXF_INT 1 +#define EV_I2C_RXF_INT 3 +#define EV_I2C_FER_INT 4 +#define EV_I2C_ARB_INT 5 +#define EV_I2C_NAK_INT 6 +#define EV_I2C_UNKNOW_INT 0xff + +/* I2C Device hardware configuration */ + +struct bl602_i2c_config_s +{ + uint32_t reg_base; /* I2C register base address */ + uint8_t irq; /* Interrupt ID */ + uint32_t clk_freq; /* i2c freq */ +}; + +/* I2C Device Private Data */ + +struct bl602_i2c_priv_s +{ + const struct i2c_ops_s *ops; /* Standard I2C operations */ + + /* Port configuration */ + + const struct bl602_i2c_config_s *config; + + uint8_t subflag; /* Sub address flag */ + uint32_t subaddr; /* Sub address */ + uint8_t sublen; /* Sub address length */ + sem_t sem_excl; /* Mutual exclusion semaphore */ + sem_t sem_isr; /* Interrupt wait semaphore */ + + /* I2C work state */ + + uint8_t i2cstate; + + struct i2c_msg_s *msgv; /* Message list */ + + uint8_t msgid; /* Current message ID */ + ssize_t bytes; /* Processed data bytes */ + int refs; /* Reference count */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int bl602_i2c_transfer(FAR struct i2c_master_s *dev, + FAR struct i2c_msg_s * msgs, + int count); + +#ifdef CONFIG_I2C_RESET +static int bl602_i2c_reset(FAR struct i2c_master_s *dev); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* I2C interface */ + +static const struct i2c_ops_s bl602_i2c_ops = +{ + .transfer = bl602_i2c_transfer, +#ifdef CONFIG_I2C_RESET + .reset = bl602_i2c_reset +#endif +}; + +/* I2C device structures */ + +#ifdef CONFIG_BL602_I2C0 +static const struct bl602_i2c_config_s bl602_i2c0_config = +{ + .reg_base = BL602_I2C_BASE, + .irq = BL602_IRQ_I2C, + .clk_freq = 500, +}; + +static struct bl602_i2c_priv_s bl602_i2c0_priv = +{ + .ops = &bl602_i2c_ops, + .config = &bl602_i2c0_config, + .subaddr = 0, + .sublen = 0, + .i2cstate = EV_I2C_END_INT, + .msgv = NULL, + .msgid = 0, + .bytes = 0, +}; +#endif /* CONFIG_BL602_I2C0 */ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: bl602_i2c_send_data + * + * Description: + * Send I2C data + * + ****************************************************************************/ + +static void bl602_i2c_send_data(FAR struct bl602_i2c_priv_s *priv) +{ + uint32_t temp = 0; + uint32_t val = 0; + int i; + int count; + struct i2c_msg_s *msg = &priv->msgv[priv->msgid]; + + count = msg->length - priv->bytes; + if (count >= 4) + { + count = 4; + } + + for (i = 0; i < count; i++) + { + val = *(msg->buffer + priv->bytes + i); + temp += val << i * 8; + } + + putreg32(temp, BL602_I2C_FIFO_WDATA); + priv->bytes += count; +} + +/**************************************************************************** + * Name: bl602_i2c_recvdata + * + * Description: + * Receive I2C data + * + ****************************************************************************/ + +static void bl602_i2c_recvdata(struct bl602_i2c_priv_s *priv) +{ + uint32_t temp = 0; + int i = 0; + int count; + struct i2c_msg_s *msg = &priv->msgv[priv->msgid]; + + count = msg->length - priv->bytes; + temp = getreg32(BL602_I2C_FIFO_RDATA); + if (count >= 4) + { + PUT_UINT32_LE(temp, msg->buffer, priv->bytes); + count = 4; + } + else if (count < 4) + { + for (i = 0; i < count; i++) + { + msg->buffer[priv->bytes + i] = (temp & 0xff); + temp = (temp >> 8); + } + } + + priv->bytes += count; +} + +/**************************************************************************** + * Name: bl602_i2c_sem_init + * + * Description: + * Initialize semaphores + * + ****************************************************************************/ + +static void bl602_i2c_sem_init(FAR struct bl602_i2c_priv_s *priv) +{ + nxsem_init(&priv->sem_excl, 0, 1); + + /* This semaphore is used for signaling and, hence, should not have + * priority inheritance enabled. + */ + + nxsem_init(&priv->sem_isr, 0, 1); + nxsem_set_protocol(&priv->sem_isr, SEM_PRIO_NONE); +} + +/**************************************************************************** + * Name: bl602_i2c_clear_status + * + * Description: + * clear i2c status + * + ****************************************************************************/ + +static void bl602_i2c_clear_status(int i2cx) +{ + if (i2cx == I2C0) + { + modifyreg32(BL602_I2C_INT_STS, 0, I2C_INT_STS_CR_END_CLR + | I2C_INT_STS_CR_NAK_CLR | I2C_INT_STS_CR_ARB_CLR); + } + else + { + printf("port error\r\n"); + } +} + +/**************************************************************************** + * Name: bl602_i2c_config_para + * + * Description: + * config i2c param + * + ****************************************************************************/ + +static void bl602_i2c_config_para(struct bl602_i2c_priv_s *priv) +{ + struct i2c_msg_s *msg = &priv->msgv[priv->msgid]; + + if (msg->flags & I2C_M_READ) + { + modifyreg32(BL602_I2C_CONFIG, 0, I2C_CONFIG_CR_I2C_PKT_DIR); + } + else + { + modifyreg32(BL602_I2C_CONFIG, I2C_CONFIG_CR_I2C_PKT_DIR, 0); + } + + modifyreg32(BL602_I2C_CONFIG, + I2C_CONFIG_CR_I2C_SLV_ADDR_MASK, + msg->addr << I2C_CONFIG_CR_I2C_SLV_ADDR_SHIFT); + if (priv->subflag > 0) + { + modifyreg32(BL602_I2C_CONFIG, 0, I2C_CONFIG_CR_I2C_SUB_ADDR_EN); + modifyreg32(BL602_I2C_CONFIG, + I2C_CONFIG_CR_I2C_SUB_ADDR_BC_MASK, + (priv->sublen - 1) << I2C_CONFIG_CR_I2C_SUB_ADDR_BC_SHIFT); + } + else + { + modifyreg32(BL602_I2C_CONFIG, I2C_CONFIG_CR_I2C_SUB_ADDR_EN, 0); + } + + modifyreg32(BL602_I2C_CONFIG, + I2C_CONFIG_CR_I2C_PKT_LEN_MASK, + (msg->length - 1) << I2C_CONFIG_CR_I2C_PKT_LEN_SHIFT); + + if (priv->subflag > 0) + { + putreg32(priv->subaddr, BL602_I2C_SUB_ADDR); + } +} + +/**************************************************************************** + * Name: bl602_i2c_intmask + * + * Description: + * Mask/Unmask the I2C interrupt + * + ****************************************************************************/ + +static void bl602_i2c_intmask(uint8_t int_type, uint8_t int_mask) +{ + switch (int_type) + { + case I2C_TRANS_END_INT: + if (int_mask == 0) + { + /* UNMASK(Enable) this interrupt */ + + modifyreg32(BL602_I2C_INT_STS, 0, I2C_INT_STS_CR_END_EN); + modifyreg32(BL602_I2C_INT_STS, I2C_INT_STS_CR_END_MASK, 0); + } + else + { + /* MASK(Disable) this interrupt */ + + modifyreg32(BL602_I2C_INT_STS, I2C_INT_STS_CR_END_EN, 0); + modifyreg32(BL602_I2C_INT_STS, 0, I2C_INT_STS_CR_END_MASK); + } + break; + case I2C_TX_FIFO_READY_INT: + if (int_mask == 0) + { + /* UNMASK(Enable) this interrupt */ + + modifyreg32(BL602_I2C_INT_STS, 0, I2C_INT_STS_CR_TXF_EN); + modifyreg32(BL602_I2C_INT_STS, I2C_INT_STS_CR_TXF_MASK, 0); + } + else + { + /* MASK(Disable) this interrupt */ + + modifyreg32(BL602_I2C_INT_STS, I2C_INT_STS_CR_TXF_EN, 0); + modifyreg32(BL602_I2C_INT_STS, 0, I2C_INT_STS_CR_TXF_MASK); + } + break; + case I2C_RX_FIFO_READY_INT: + if (int_mask == 0) + { + /* UNMASK(Enable) this interrupt */ + + modifyreg32(BL602_I2C_INT_STS, 0, I2C_INT_STS_CR_RXF_EN); + modifyreg32(BL602_I2C_INT_STS, I2C_INT_STS_CR_RXF_MASK, 0); + } + else + { + /* MASK(Disable) this interrupt */ + + modifyreg32(BL602_I2C_INT_STS, I2C_INT_STS_CR_RXF_EN, 0); + modifyreg32(BL602_I2C_INT_STS, 0, I2C_INT_STS_CR_RXF_MASK); + } + break; + case I2C_NACK_RECV_INT: + if (int_mask == 0) + { + /* UNMASK(Enable) this interrupt */ + + modifyreg32(BL602_I2C_INT_STS, 0, I2C_INT_STS_CR_NAK_EN); + modifyreg32(BL602_I2C_INT_STS, I2C_INT_STS_CR_NAK_MASK, 0); + } + else + { + /* MASK(Disable) this interrupt */ + + modifyreg32(BL602_I2C_INT_STS, I2C_INT_STS_CR_NAK_EN, 0); + modifyreg32(BL602_I2C_INT_STS, 0, I2C_INT_STS_CR_NAK_MASK); + } + break; + case I2C_ARB_LOST_INT: + if (int_mask == 0) + { + /* UNMASK(Enable) this interrupt */ + + modifyreg32(BL602_I2C_INT_STS, 0, I2C_INT_STS_CR_ARB_EN); + modifyreg32(BL602_I2C_INT_STS, I2C_INT_STS_CR_ARB_MASK, 0); + } + else + { + /* MASK(Disable) this interrupt */ + + modifyreg32(BL602_I2C_INT_STS, I2C_INT_STS_CR_ARB_EN, 0); + modifyreg32(BL602_I2C_INT_STS, 0, I2C_INT_STS_CR_ARB_MASK); + } + break; + case I2C_FIFO_ERR_INT: + if (int_mask == 0) + { + /* UNMASK(Enable) this interrupt */ + + modifyreg32(BL602_I2C_INT_STS, 0, I2C_INT_STS_CR_FER_EN); + modifyreg32(BL602_I2C_INT_STS, I2C_INT_STS_CR_FER_MASK, 0); + } + else + { + /* MASK(Disable) this interrupt */ + + modifyreg32(BL602_I2C_INT_STS, I2C_INT_STS_CR_FER_EN, 0); + modifyreg32(BL602_I2C_INT_STS, 0, I2C_INT_STS_CR_FER_MASK); + } + break; + case I2C_INT_ALL: + if (int_mask == 0) + { + /* UNMASK(Enable) this interrupt */ + + modifyreg32(BL602_I2C_INT_STS, 0, I2C_INT_STS_CR_END_EN + | I2C_INT_STS_CR_TXF_EN | I2C_INT_STS_CR_RXF_EN + | I2C_INT_STS_CR_NAK_EN | I2C_INT_STS_CR_ARB_EN + | I2C_INT_STS_CR_FER_EN); + + modifyreg32(BL602_I2C_INT_STS, I2C_INT_STS_CR_END_MASK + | I2C_INT_STS_CR_TXF_MASK | I2C_INT_STS_CR_RXF_MASK + | I2C_INT_STS_CR_NAK_MASK | I2C_INT_STS_CR_ARB_MASK + | I2C_INT_STS_CR_FER_MASK, 0); + } + else + { + /* MASK(Disable) this interrupt */ + + modifyreg32(BL602_I2C_INT_STS, I2C_INT_STS_CR_END_EN + | I2C_INT_STS_CR_TXF_EN | I2C_INT_STS_CR_RXF_EN + | I2C_INT_STS_CR_NAK_EN | I2C_INT_STS_CR_ARB_EN + | I2C_INT_STS_CR_FER_EN, 0); + + modifyreg32(BL602_I2C_INT_STS, 0, I2C_INT_STS_CR_END_MASK + | I2C_INT_STS_CR_TXF_MASK | I2C_INT_STS_CR_RXF_MASK + | I2C_INT_STS_CR_NAK_MASK | I2C_INT_STS_CR_ARB_MASK + | I2C_INT_STS_CR_FER_MASK); + } + break; + + default: + break; + } +} + +/**************************************************************************** + * Name: bl602_i2c_enable + * + * Description: + * i2c enable + * + ****************************************************************************/ + +static void bl602_i2c_enable(void) +{ + modifyreg32(BL602_I2C_FIFO_CONFIG_0, 0, I2C_FIFO_CONFIG_0_TX_FIFO_CLR); + modifyreg32(BL602_I2C_FIFO_CONFIG_0, 0, I2C_FIFO_CONFIG_0_RX_FIFO_CLR); + modifyreg32(BL602_I2C_CONFIG, 0, I2C_CONFIG_CR_I2C_M_EN); +} + +/**************************************************************************** + * Name: bl602_i2c_transfer_enable + * + * Description: + * i2c transfer enable + * + ****************************************************************************/ + +static void bl602_i2c_transfer_enable(struct bl602_i2c_priv_s *priv) +{ + struct i2c_msg_s *msg = &priv->msgv[priv->msgid]; + + if (msg->flags & I2C_M_READ) + { + bl602_i2c_intmask(I2C_RX_FIFO_READY_INT, 0); + } + else + { + bl602_i2c_intmask(I2C_TX_FIFO_READY_INT, 0); + } + + bl602_i2c_intmask(I2C_TRANS_END_INT, 0); + bl602_i2c_intmask(I2C_FIFO_ERR_INT, 0); + bl602_i2c_intmask(I2C_ARB_LOST_INT, 0); + bl602_i2c_intmask(I2C_NACK_RECV_INT, 0); + + bl602_i2c_enable(); +} + +/**************************************************************************** + * Name: bl602_i2c_start_transfer + * + * Description: + * Send I2C start signal + * + ****************************************************************************/ + +static void bl602_i2c_start_transfer(FAR struct bl602_i2c_priv_s *priv) +{ + bl602_i2c_clear_status(I2C0); + bl602_i2c_config_para(priv); + bl602_i2c_transfer_enable(priv); +} + +/**************************************************************************** + * Device Driver Operations + ****************************************************************************/ + +/**************************************************************************** + * Name: bl602_i2c_setsclsync + * + * Description: + * set i2c scl sync + * + ****************************************************************************/ + +static void bl602_i2c_setsclsync(uint8_t enable) +{ + if (enable) + { + modifyreg32(BL602_I2C_CONFIG, 0, I2C_CONFIG_CR_I2C_SCL_SYNC_EN); + } + else + { + modifyreg32(BL602_I2C_CONFIG, I2C_CONFIG_CR_I2C_SCL_SYNC_EN, 0); + } +} + +/**************************************************************************** + * Name: bl602_i2c_setprd + * + * Description: + * set i2c prd + * + ****************************************************************************/ + +static void bl602_i2c_setprd(uint8_t phase) +{ + modifyreg32(BL602_I2C_PRD_START, I2C_PRD_START_CR_PRD_S_PH_0_MASK, phase); + modifyreg32(BL602_I2C_PRD_START, + I2C_PRD_START_CR_PRD_S_PH_1_MASK, + phase << I2C_PRD_START_CR_PRD_S_PH_1_SHIFT); + modifyreg32(BL602_I2C_PRD_START, + I2C_PRD_START_CR_PRD_S_PH_2_MASK, + phase << I2C_PRD_START_CR_PRD_S_PH_2_SHIFT); + modifyreg32(BL602_I2C_PRD_START, + I2C_PRD_START_CR_PRD_S_PH_3_MASK, + phase << I2C_PRD_START_CR_PRD_S_PH_3_SHIFT); + + modifyreg32(BL602_I2C_PRD_STOP, I2C_PRD_STOP_CR_PRD_P_PH_0_MASK, phase); + modifyreg32(BL602_I2C_PRD_STOP, + I2C_PRD_STOP_CR_PRD_P_PH_1_MASK, + phase << I2C_PRD_STOP_CR_PRD_P_PH_1_SHIFT); + modifyreg32(BL602_I2C_PRD_STOP, + I2C_PRD_STOP_CR_PRD_P_PH_2_MASK, + phase << I2C_PRD_STOP_CR_PRD_P_PH_2_SHIFT); + modifyreg32(BL602_I2C_PRD_STOP, + I2C_PRD_STOP_CR_PRD_P_PH_3_MASK, + phase << I2C_PRD_STOP_CR_PRD_P_PH_3_SHIFT); + + modifyreg32(BL602_I2C_PRD_DATA, I2C_PRD_DATA_CR_PRD_D_PH_0_MASK, phase); + modifyreg32(BL602_I2C_PRD_DATA, + I2C_PRD_DATA_CR_PRD_D_PH_1_MASK, + phase << I2C_PRD_DATA_CR_PRD_D_PH_1_SHIFT); + modifyreg32(BL602_I2C_PRD_DATA, + I2C_PRD_DATA_CR_PRD_D_PH_2_MASK, + phase << I2C_PRD_DATA_CR_PRD_D_PH_2_SHIFT); + modifyreg32(BL602_I2C_PRD_DATA, + I2C_PRD_DATA_CR_PRD_D_PH_3_MASK, + phase << I2C_PRD_DATA_CR_PRD_D_PH_3_SHIFT); +} + +/**************************************************************************** + * Name: bl602_set_i2c_clk + * + * Description: + * set I2C clock. + * + ****************************************************************************/ + +void bl602_set_i2c_clk(uint8_t enable, uint8_t div) +{ + modifyreg32(BL602_CLK_CFG3, + CLK_CFG3_I2C_CLK_DIV_MASK, + div << CLK_CFG3_I2C_CLK_DIV_SHIFT); + + if (enable) + { + modifyreg32(BL602_CLK_CFG3, 0, CLK_CFG3_I2C_CLK_EN); + } + else + { + modifyreg32(BL602_CLK_CFG3, CLK_CFG3_I2C_CLK_EN, 0); + } +} + +/**************************************************************************** + * Name: bl602_i2c_clockset + * + * Description: + * set i2c clock + * + ****************************************************************************/ + +static void bl602_i2c_clockset(uint32_t clk) +{ + uint8_t bclk_div = 0; + + bclk_div = bl602_glb_get_bclk_div(); + if (clk >= 100000) + { + bl602_set_i2c_clk(1, 0); + bl602_i2c_setprd((SYS_TEMCORE_CLOCK_GET / (bclk_div + 1)) / (clk * 4) - + 1); + } + else if (clk >= 8000) Review comment: BL602 I2c clk Ranges: 305Hz~3MHz 100~400K, test pass( use device) 3MHz, test pass (use oscilloscope ) ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: us...@infra.apache.org