/*************************************************************************** * * Copyright 2015-2019 BES. * All rights reserved. All unpublished rights reserved. * * No part of this work may be used or reproduced in any form or by any * means, or stored in a database or retrieval system, without prior written * permission of BES. * * Use of this work is governed by a license granted by BES. * This work contains confidential and proprietary information of * BES. which is protected by copyright, trade secret, * trademark and other intellectual property rights. * ****************************************************************************/ #include "plat_types.h" #include "plat_addr_map.h" #include "string.h" #include "hal_i2c.h" #include "hal_i2cip.h" #include "hal_timer.h" #include "hal_dma.h" #include "hal_trace.h" #include "hal_cmu.h" #include "cmsis_nvic.h" #ifdef RTOS #include "cmsis_os.h" #endif #ifdef I2C_DEBUG #define HAL_I2C_TRACE(attr, str, ...) TRACE(attr, str, ##__VA_ARGS__) #define HAL_I2C_ERROR(attr, str, ...) TRACE(attr, str, ##__VA_ARGS__) #else #define HAL_I2C_TRACE(attr, str, ...) TRACE_DUMMY(attr, str, ##__VA_ARGS__) #define HAL_I2C_ERROR(attr, str, ...) TRACE(attr, str, ##__VA_ARGS__) #endif #ifdef I2C_SENSOR_ENGINE #ifndef I2C_USE_DMA #define I2C_USE_DMA #endif #endif #define HAL_I2C_TX_TL I2CIP_TX_TL_QUARTER #define HAL_I2C_RX_TL I2CIP_RX_TL_THREE_QUARTER #define HAL_I2C_DMA_TX_TL I2CIP_TX_TL_HALF #define HAL_I2C_DMA_RX_TL I2CIP_DMA_TX_TL_1_BYTE #define HAL_I2C_DMA_TX 1 #define HAL_I2C_DMA_RX 0 #define HAL_I2C_RESTART 1 #define HAL_I2C_NO_RESTART 0 #define HAL_I2C_YES 1 #define HAL_I2C_NO 0 /* state machine */ #ifdef I2C_SM_TASK_NUM #define HAL_I2C_SM_TASK_NUM_MAX I2C_SM_TASK_NUM #else #define HAL_I2C_SM_TASK_NUM_MAX 2 #endif #ifdef I2C_SM_DMA_BUF_SIZE #define HAL_I2C_SM_DMA_BUF_LEN_MAX I2C_SM_DMA_BUF_SIZE #else #define HAL_I2C_SM_DMA_BUF_LEN_MAX 1024 #endif /* i2c synchronization timeout ms */ #define HAL_I2C_SYNC_TM_MS (2000) #define HAL_I2C_DLY_MS (1) #define HAL_I2C_WAIT_ACT_MS (1000) #define HAL_I2C_WAIT_TFE_MS (1000) #define HAL_I2C_WAIT_TFNF_MS (1000) #define HAL_I2C_WAIT_RFNE_MS (1000) enum HAL_I2C_SM_STATE_T { HAL_I2C_SM_CLOSED = 0, HAL_I2C_SM_IDLE, HAL_I2C_SM_RUNNING, }; enum HAL_I2C_SM_TASK_ACTION_T { HAL_I2C_SM_TASK_ACTION_NONE = 0, HAL_I2C_SM_TASK_ACTION_M_SEND, HAL_I2C_SM_TASK_ACTION_M_RECV, }; enum HAL_I2C_SM_TASK_STATE_T { HAL_I2C_SM_TASK_STATE_START = (1 << 0), HAL_I2C_SM_TASK_STATE_STOP = (1 << 1), HAL_I2C_SM_TASK_STATE_TX_ABRT = (1 << 2), HAL_I2C_SM_TASK_STATE_ACT = (1 << 3), HAL_I2C_SM_TASK_STATE_FIFO_ERR = (1 << 4), }; struct HAL_I2C_SM_TASK_T { /* send or recv buffer */ const uint8_t *tx_buf; uint8_t *rx_buf; uint16_t tx_txn_len; uint16_t rx_txn_len; uint16_t txn_cnt; uint16_t tx_pos; uint16_t rx_pos; uint16_t rx_cmd_sent; /* device control */ uint8_t stop; uint8_t restart_after_write; uint16_t target_addr; /* task control */ uint32_t state; uint32_t action; /* system control */ /* TODO : os and non os version */ volatile uint32_t lock; uint32_t transfer_id; uint32_t errcode; HAL_I2C_TRANSFER_HANDLER_T handler; }; struct HAL_I2C_SM_T { /* device related */ struct HAL_I2C_CONFIG_T cfg; enum HAL_I2C_SM_STATE_T state; /* state machine related */ #if defined(I2C_TASK_MODE) || defined(I2C_SENSOR_ENGINE) uint8_t in_task; uint8_t out_task; uint8_t task_count; struct HAL_I2C_SM_TASK_T task[HAL_I2C_SM_TASK_NUM_MAX]; /* dma related */ #ifdef I2C_USE_DMA struct HAL_DMA_CH_CFG_T tx_dma_cfg; struct HAL_DMA_CH_CFG_T rx_dma_cfg; /* i2cip cmd_data use 16bit and read action is driven by write action */ /* when use dma to read from i2c, we need to use another dma to write cmd/stop/restart */ /* cmd/stop/restart + data use 16bit width, so dma buffer are 2 times of orgin data buffer */ uint16_t dma_tx_buf[HAL_I2C_SM_DMA_BUF_LEN_MAX / 2]; #endif #endif }; /* state machine end */ struct HAL_I2C_MOD_NAME_T { }; struct HAL_I2C_HW_DESC_T { uint32_t base; enum HAL_CMU_MOD_ID_T mod; enum HAL_CMU_MOD_ID_T apb; IRQn_Type irq; #ifdef I2C_USE_DMA enum HAL_DMA_PERIPH_T rx_periph; enum HAL_DMA_PERIPH_T tx_periph; #endif }; static const struct HAL_I2C_HW_DESC_T i2c_desc[HAL_I2C_ID_NUM] = { { .base = I2C0_BASE, .mod = HAL_CMU_MOD_O_I2C0, .apb = HAL_CMU_MOD_P_I2C0, .irq = I2C0_IRQn, #ifdef I2C_USE_DMA .rx_periph = HAL_GPDMA_I2C0_RX, .tx_periph = HAL_GPDMA_I2C0_TX, #endif }, #if (CHIP_HAS_I2C > 1) { .base = I2C1_BASE, .mod = HAL_CMU_MOD_O_I2C1, .apb = HAL_CMU_MOD_P_I2C1, .irq = I2C1_IRQn, #ifdef I2C_USE_DMA .rx_periph = HAL_GPDMA_I2C1_RX, .tx_periph = HAL_GPDMA_I2C1_TX, #endif }, #endif }; static const char * const invalid_id = "Invalid I2C ID: %d"; static struct HAL_I2C_SM_T hal_i2c_sm[HAL_I2C_ID_NUM]; /* simple mode */ #ifdef I2C_SIMPLE_MODE static void hal_i2c_simple_proc(enum HAL_I2C_ID_T id); static HAL_I2C_INT_HANDLER_T hal_i2c_int_handlers[HAL_I2C_ID_NUM] = {NULL}; #endif /* simple mode end */ #ifdef I2C_SENSOR_ENGINE static enum HAL_SENSOR_ENGINE_ID_T i2c_sensor_id[HAL_I2C_ID_NUM]; static HAL_I2C_SENSOR_ENG_HANDLER_T i2c_sensor_handler[HAL_I2C_ID_NUM]; #ifdef I2C_SENSOR_ENGINE static void hal_i2c_sensor_eng_proc(enum HAL_I2C_ID_T id); #endif #endif static uint32_t _i2c_get_base(enum HAL_I2C_ID_T id) { return i2c_desc[id].base; } static void POSSIBLY_UNUSED hal_i2c_delay_ms(int ms) { osDelay(ms); } static uint32_t _i2c_adjust_period_cnt(uint32_t period_cnt, uint16_t trising_ns, uint16_t tfalling_ns, uint16_t pclk_mhz) { uint32_t old_period_cnt; uint16_t rising_falling_cycle; old_period_cnt = period_cnt; // Round down the rising and falling cycle, so that the period count is rounded up, // and the SCL freq is always <= the requested freq. rising_falling_cycle = ((trising_ns + tfalling_ns) * pclk_mhz) / 1000; if (period_cnt > rising_falling_cycle) { period_cnt -= rising_falling_cycle; } else { period_cnt = 0; } HAL_I2C_TRACE(5,"%s: period_cnt=%u->%u trising_ns=%u tfalling_ns=%u", __FUNCTION__, old_period_cnt, period_cnt, trising_ns, tfalling_ns); return period_cnt; } static void _i2c_get_clk_cnt(uint32_t period_cnt, uint16_t tlow_ns, uint16_t thigh_ns, uint16_t spklen, uint16_t pclk_mhz, uint16_t *plcnt, uint16_t *phcnt) { #define IC_SCL_LOW_CYCLE_ADD (1) // NOTE: H/w spec says that (6 + spklen) cycles is added for SCL high interval, but tests show that 1 more cycle is needed #define IC_SCL_HIGH_CYCLE_ADD (6 + spklen + 1) #define MIN_IC_SCL_LCNT (7 + spklen + IC_SCL_LOW_CYCLE_ADD) #define MIN_IC_SCL_HCNT (5 + spklen + IC_SCL_HIGH_CYCLE_ADD) uint32_t lcnt, hcnt; uint16_t min_lcnt, min_hcnt; HAL_I2C_TRACE(6,"%s: period_cnt=%u tlow_ns=%u thigh_ns=%u spklen=%u pclk_mhz=%u", __FUNCTION__, period_cnt, tlow_ns, thigh_ns, spklen, pclk_mhz); min_lcnt = (tlow_ns * pclk_mhz + 1000 - 1) / 1000; if (min_lcnt < MIN_IC_SCL_LCNT) { min_lcnt = MIN_IC_SCL_LCNT; } min_hcnt = (thigh_ns * pclk_mhz + 1000 - 1) / 1000; if (min_hcnt < MIN_IC_SCL_HCNT) { min_hcnt = MIN_IC_SCL_HCNT; } if (min_lcnt + min_hcnt > period_cnt) { HAL_I2C_ERROR(5,"%s:WARNING: period_cnt=%u too small: min_lcnt=%u min_hcnt=%u pclk_mhz=%u", __FUNCTION__, period_cnt, min_lcnt, min_hcnt, pclk_mhz); lcnt = min_lcnt; hcnt = min_hcnt; } else { lcnt = (period_cnt + 1) / 2; if (min_lcnt >= min_hcnt) { if (lcnt < min_lcnt) { lcnt = min_lcnt; } hcnt = period_cnt - lcnt; if (hcnt < min_hcnt) { hcnt = min_hcnt; } } else { hcnt = lcnt; if (hcnt < min_hcnt) { hcnt = min_hcnt; } lcnt = period_cnt - hcnt; if (lcnt < min_lcnt) { lcnt = min_lcnt; } } } lcnt -= IC_SCL_LOW_CYCLE_ADD; hcnt -= IC_SCL_HIGH_CYCLE_ADD; ASSERT(lcnt <= UINT16_MAX && hcnt <= UINT16_MAX, "%s: lcnt=%u or hcnt=%u overflow", __FUNCTION__, lcnt, hcnt); *plcnt = lcnt; *phcnt = hcnt; } static void _i2c_set_speed(enum HAL_I2C_ID_T id, int speed_mode, int speed) { #define MAX_HS_SPK_NS 10 #define MAX_FSP_SPK_NS 50 #define MAX_FS_SPK_NS 50 #define SS_THOLD_NS 1800 // [300, 3450] #define FS_THOLD_NS 600 // [300, 900] #define FSP_THOLD_NS 300 // [ 0, -] #define HS_100PF_THOLD_NS 30 // [ 0, 70] #define HS_400PF_THOLD_NS 70 // [ 0, 150] #define SS_TRISING_NS 300 // [ 0, 1000] #define FS_TRISING_NS 50 // [ 20, 300] #define FSP_TRISING_NS 30 // [ 0, 120] #define HS_100PF_TRISING_NS 30 // [ 10, 40] for SCL, [ 20, 80] for SDA #define HS_400PF_TRISING_NS 30 // [ 10, 80] for SCL, [ 20, 160] for SDA #define SS_TFALLING_NS 30 // [ 0, 300] #define FS_TFALLING_NS 30 // [ 20, 300] #define FSP_TFALLING_NS 30 // [ 20, 120] #define HS_100PF_TFALLING_NS 30 // [ 10, 40] for SCL, [ 20, 80] for SDA #define HS_400PF_TFALLING_NS 30 // [ 10, 80] for SCL, [ 20, 160] for SDA #define MIN_SS_TLOW_NS 4700 #define MIN_SS_THIGH_NS 4000 #define MIN_FS_TLOW_NS 1300 #define MIN_FS_THIGH_NS 600 #define MIN_FSP_TLOW_NS 500 #define MIN_FSP_THIGH_NS 260 #define MIN_HS_100PF_TLOW_NS 160 #define MIN_HS_100PF_THIGH_NS 60 #define MIN_HS_400PF_TLOW_NS 320 #define MIN_HS_400PF_THIGH_NS 120 // Round down the spike suppression limit value #define GET_SPKLEN_VAL(s) ((s) * pclk_mhz / 1000) uint32_t reg_base = _i2c_get_base(id); uint32_t min_mclk, pclk, period_cnt; uint16_t lcnt, hcnt, hold_cycle, spklen; uint16_t tlow_ns, thigh_ns, thold_ns, trising_ns, tfalling_ns; uint8_t spk_ns; uint16_t pclk_mhz; if (speed_mode == IC_SPEED_MODE_MAX) { min_mclk = speed * 50; } else { min_mclk = speed * 40; } pclk = 0; #ifdef PERIPH_PLL_FREQ if (PERIPH_PLL_FREQ / 2 > 2 * hal_cmu_get_crystal_freq()) { // Init to OSC_X2 pclk = 2 * hal_cmu_get_crystal_freq(); if (min_mclk > pclk) { pclk = PERIPH_PLL_FREQ / 2; hal_cmu_i2c_set_div(2); } else { pclk = 0; } } #endif if (pclk == 0) { enum HAL_CMU_PERIPH_FREQ_T periph_freq; // Init to OSC pclk = hal_cmu_get_crystal_freq(); if (min_mclk > pclk) { pclk *= 2; periph_freq = HAL_CMU_PERIPH_FREQ_52M; } else { periph_freq = HAL_CMU_PERIPH_FREQ_26M; } // NOTE: All I2C controllers share the same module clock configuration hal_cmu_i2c_set_freq(periph_freq); } pclk_mhz = pclk / 1000000; period_cnt = (pclk + speed - 1) / speed; switch (speed_mode) { case IC_SPEED_MODE_MAX: spk_ns = MAX_HS_SPK_NS; tlow_ns = MIN_HS_100PF_TLOW_NS; thigh_ns = MIN_HS_100PF_THIGH_NS; thold_ns = HS_100PF_THOLD_NS; trising_ns = HS_100PF_TRISING_NS; tfalling_ns = HS_100PF_TFALLING_NS; spklen = GET_SPKLEN_VAL(spk_ns); if (spklen == 0) { spklen = 1; } i2cip_w_hs_spklen(reg_base, spklen); period_cnt = _i2c_adjust_period_cnt(period_cnt, trising_ns, tfalling_ns, pclk_mhz); _i2c_get_clk_cnt(period_cnt, tlow_ns, thigh_ns, spklen, pclk_mhz, &lcnt, &hcnt); i2cip_w_high_speed_hcnt(reg_base, hcnt); i2cip_w_high_speed_lcnt(reg_base, lcnt); i2cip_w_speed(reg_base, I2CIP_HIGH_SPEED_MASK); // Continue to config fast mode #ifdef I2S_FSP_MODE period_cnt = (pclk + I2C_FSP_SPEED - 1) / I2C_FSP_SPEED; #else period_cnt = (pclk + I2C_FAST_SPEED - 1) / I2C_FAST_SPEED; #endif case IC_SPEED_MODE_FAST: #ifdef I2S_FSP_MODE if (speed > I2C_FAST_SPEED) { spk_ns = MAX_FSP_SPK_NS; tlow_ns = MIN_FSP_TLOW_NS; thigh_ns = MIN_FSP_THIGH_NS; thold_ns = FSP_THOLD_NS; trising_ns = FSP_TRISING_NS; tfalling_ns = FSP_TFALLING_NS; } else #endif { spk_ns = MAX_FS_SPK_NS; tlow_ns = MIN_FS_TLOW_NS; thigh_ns = MIN_FS_THIGH_NS; trising_ns = FS_TRISING_NS; tfalling_ns = FS_TFALLING_NS; } spklen = GET_SPKLEN_VAL(spk_ns); if (spklen == 0) { spklen = 1; } i2cip_w_fs_spklen(reg_base, spklen); period_cnt = _i2c_adjust_period_cnt(period_cnt, trising_ns, tfalling_ns, pclk_mhz); _i2c_get_clk_cnt(period_cnt, tlow_ns, thigh_ns, spklen, pclk_mhz, &lcnt, &hcnt); i2cip_w_fast_speed_hcnt(reg_base, hcnt); i2cip_w_fast_speed_lcnt(reg_base, lcnt); // Update sda hold time and speed mode if in fast mode // Skip if in high speed mode if (speed_mode == IC_SPEED_MODE_FAST) { thold_ns = FS_THOLD_NS; i2cip_w_speed(reg_base, I2CIP_FAST_SPEED_MASK); } break; case IC_SPEED_MODE_STANDARD: default: spk_ns = MAX_FS_SPK_NS; tlow_ns = MIN_SS_TLOW_NS; thigh_ns = MIN_SS_THIGH_NS; thold_ns = SS_THOLD_NS; trising_ns = SS_TRISING_NS; tfalling_ns = SS_TFALLING_NS; spklen = GET_SPKLEN_VAL(spk_ns); if (spklen == 0) { spklen = 1; } i2cip_w_fs_spklen(reg_base, spklen); period_cnt = _i2c_adjust_period_cnt(period_cnt, trising_ns, tfalling_ns, pclk_mhz); _i2c_get_clk_cnt(period_cnt, tlow_ns, thigh_ns, spklen, pclk_mhz, &lcnt, &hcnt); i2cip_w_standard_speed_hcnt(reg_base, hcnt); i2cip_w_standard_speed_lcnt(reg_base, lcnt); i2cip_w_speed(reg_base, I2CIP_STANDARD_SPEED_MASK); break; } // Master mode: min = 1; slave mode: min = (spklen + 7) hold_cycle = (thold_ns * pclk_mhz + 1000 - 1) / 1000; i2cip_w_sda_hold_time(reg_base, hold_cycle); HAL_I2C_TRACE(1,"crystal freq=%d", pclk); HAL_I2C_TRACE(5,"i2c-%d mode=%d lcnt=%d hcnt=%d hold=%d", id, speed_mode, lcnt, hcnt, hold_cycle); } static uint8_t _i2c_set_bus_speed(enum HAL_I2C_ID_T id, unsigned int speed) { uint32_t speed_mode; if (speed > I2C_FSP_SPEED) speed_mode = IC_SPEED_MODE_MAX; else if (speed > I2C_FAST_SPEED) #ifdef I2S_FSP_MODE speed_mode = IC_SPEED_MODE_FAST; #else speed_mode = IC_SPEED_MODE_MAX; #endif else if (speed > I2C_STANDARD_SPEED) speed_mode = IC_SPEED_MODE_FAST; else speed_mode = IC_SPEED_MODE_STANDARD; _i2c_set_speed(id, speed_mode, speed); return 0; } /* state machine related */ static void hal_i2c_sm_init(enum HAL_I2C_ID_T id, const struct HAL_I2C_CONFIG_T *cfg) { memcpy(&hal_i2c_sm[id].cfg, cfg, sizeof(*cfg)); hal_i2c_sm[id].state = HAL_I2C_SM_IDLE; #if defined(I2C_TASK_MODE) || defined(I2C_SENSOR_ENGINE) hal_i2c_sm[id].in_task = 0; hal_i2c_sm[id].out_task = 0; hal_i2c_sm[id].task_count = 0; #ifdef I2C_USE_DMA hal_i2c_sm[id].tx_dma_cfg.ch = HAL_DMA_CHAN_NONE; hal_i2c_sm[id].rx_dma_cfg.ch = HAL_DMA_CHAN_NONE; #else hal_i2c_sm[id].cfg.use_dma = 0; #endif #endif } #if defined(I2C_TASK_MODE) || defined(I2C_SENSOR_ENGINE) static uint32_t hal_i2c_sm_commit(enum HAL_I2C_ID_T id, const uint8_t *tx_buf, uint16_t tx_txn_len, uint8_t *rx_buf, uint16_t rx_txn_len, uint16_t txn_cnt, uint16_t target_addr, uint32_t action, uint32_t transfer_id, HAL_I2C_TRANSFER_HANDLER_T handler) { uint32_t cur = hal_i2c_sm[id].in_task; hal_i2c_sm[id].task[cur].tx_buf = tx_buf; hal_i2c_sm[id].task[cur].rx_buf = rx_buf; hal_i2c_sm[id].task[cur].stop = 1; hal_i2c_sm[id].task[cur].lock = 1; hal_i2c_sm[id].task[cur].state = 0; hal_i2c_sm[id].task[cur].rx_txn_len = rx_txn_len; hal_i2c_sm[id].task[cur].tx_txn_len = tx_txn_len; hal_i2c_sm[id].task[cur].txn_cnt = txn_cnt; hal_i2c_sm[id].task[cur].tx_pos = 0; hal_i2c_sm[id].task[cur].rx_pos = 0; hal_i2c_sm[id].task[cur].rx_cmd_sent = 0; hal_i2c_sm[id].task[cur].errcode = 0; hal_i2c_sm[id].task[cur].action = action; hal_i2c_sm[id].task[cur].handler = handler; hal_i2c_sm[id].task[cur].restart_after_write = 1; hal_i2c_sm[id].task[cur].target_addr = target_addr; hal_i2c_sm[id].task[cur].transfer_id = transfer_id; hal_i2c_sm[id].in_task = (cur + 1) % HAL_I2C_SM_TASK_NUM_MAX; hal_i2c_sm[id].task_count++; return cur; } static void hal_i2c_sm_done(enum HAL_I2C_ID_T id) { hal_i2c_sm[id].in_task = 0; hal_i2c_sm[id].out_task = 0; hal_i2c_sm[id].task_count = 0; hal_i2c_sm[id].state = HAL_I2C_SM_IDLE; HAL_I2C_TRACE(1,"%s", __func__); } static enum HAL_I2C_SM_TASK_STATE_T _i2c_chk_clr_task_error(uint32_t reg_base, uint32_t ip_int_status, uint32_t tx_abrt_source) { enum HAL_I2C_SM_TASK_STATE_T state = 0; uint32_t tmp1; /* tx abort interrupt */ if (ip_int_status & I2CIP_INT_STATUS_TX_ABRT_MASK) { state |= HAL_I2C_SM_TASK_STATE_TX_ABRT; /* sbyte_norstrt is special to clear : restart disable but user want to send a restart */ /* to fix this bit : enable restart , clear speical , clear gc_or_start bit temporary */ tmp1 = i2cip_r_target_address_reg(reg_base); if (tx_abrt_source & I2CIP_TX_ABRT_SOURCE_ABRT_SBYTE_NORSTRT_MASK) { i2cip_w_restart(reg_base, HAL_I2C_YES); i2cip_w_special_bit(reg_base, HAL_I2C_NO); i2cip_w_gc_or_start_bit(reg_base, HAL_I2C_NO); } i2cip_r_clr_tx_abrt(reg_base); /* restore register after clear */ if (tx_abrt_source & I2CIP_TX_ABRT_SOURCE_ABRT_SBYTE_NORSTRT_MASK) { i2cip_w_target_address_reg(reg_base, tmp1); } } /* tx overflow interrupt */ if (ip_int_status & I2CIP_INT_MASK_TX_OVER_MASK) { state |= HAL_I2C_SM_TASK_STATE_FIFO_ERR; i2cip_r_clr_tx_over(reg_base); } /* rx overflow interrupt */ if (ip_int_status & I2CIP_INT_MASK_RX_OVER_MASK) { state |= HAL_I2C_SM_TASK_STATE_FIFO_ERR; i2cip_r_clr_rx_over(reg_base); } /* rx underflow interrupt */ if (ip_int_status & I2CIP_INT_MASK_RX_UNDER_MASK) { state |= HAL_I2C_SM_TASK_STATE_FIFO_ERR; i2cip_r_clr_rx_under(reg_base); } return state; } static void _i2c_show_error_code(uint32_t errcode) { #ifdef DEBUG if (errcode & HAL_I2C_ERRCODE_SLVRD_INTX) HAL_I2C_ERROR(0,"i2c err : HAL_I2C_ERRCODE_SLVRD_INTX"); if (errcode & HAL_I2C_ERRCODE_SLV_ARBLOST) HAL_I2C_ERROR(0,"i2c err : HAL_I2C_ERRCODE_SLV_ARBLOST"); if (errcode & HAL_I2C_ERRCODE_SLVFLUSH_TXFIFO) HAL_I2C_ERROR(0,"i2c err : HAL_I2C_ERRCODE_SLVFLUSH_TXFIFO"); if (errcode & HAL_I2C_ERRCODE_MASTER_DIS) HAL_I2C_ERROR(0,"i2c err : HAL_I2C_ERRCODE_MASTER_DIS"); if (errcode & HAL_I2C_ERRCODE_10B_RD_NORSTRT) HAL_I2C_ERROR(0,"i2c err : HAL_I2C_ERRCODE_10B_RD_NORSTRT"); if (errcode & HAL_I2C_ERRCODE_SBYTE_NORSTRT) HAL_I2C_ERROR(0,"i2c err : HAL_I2C_ERRCODE_SBYTE_NORSTRT"); if (errcode & HAL_I2C_ERRCODE_HS_NORSTRT) HAL_I2C_ERROR(0,"i2c err : HAL_I2C_ERRCODE_HS_NORSTRT"); if (errcode & HAL_I2C_ERRCODE_SBYTE_ACKDET) HAL_I2C_ERROR(0,"i2c err : HAL_I2C_ERRCODE_SBYTE_ACKDET"); if (errcode & HAL_I2C_ERRCODE_HS_ACKDET) HAL_I2C_ERROR(0,"i2c err : HAL_I2C_ERRCODE_HS_ACKDET"); if (errcode & HAL_I2C_ERRCODE_GCALL_READ) HAL_I2C_ERROR(0,"i2c err : HAL_I2C_ERRCODE_GCALL_READ"); if (errcode & HAL_I2C_ERRCODE_GCALL_NOACK) HAL_I2C_ERROR(0,"i2c err : HAL_I2C_ERRCODE_GCALL_NOACK"); if (errcode & HAL_I2C_ERRCODE_TXDATA_NOACK) HAL_I2C_ERROR(0,"i2c err : HAL_I2C_ERRCODE_TXDATA_NOACK"); if (errcode & HAL_I2C_ERRCODE_10ADDR2_NOACK) HAL_I2C_ERROR(0,"i2c err : HAL_I2C_ERRCODE_10ADDR2_NOACK"); if (errcode & HAL_I2C_ERRCODE_10ADDR1_NOACK) HAL_I2C_ERROR(0,"i2c err : HAL_I2C_ERRCODE_10ADDR1_NOACK"); if (errcode & HAL_I2C_ERRCODE_7B_ADDR_NOACK) HAL_I2C_ERROR(0,"i2c err : HAL_I2C_ERRCODE_7B_ADDR_NOACK"); if (errcode & HAL_I2C_ERRCODE_INV_PARAM) HAL_I2C_ERROR(0,"i2c err : HAL_I2C_ERRCODE_INV_PARAM"); if (errcode & HAL_I2C_ERRCODE_IN_USE) HAL_I2C_ERROR(0,"i2c err : HAL_I2C_ERRCODE_IN_USE"); if (errcode & HAL_I2C_ERRCODE_FIFO_ERR) HAL_I2C_ERROR(0,"i2c err : HAL_I2C_ERRCODE_FIFO_ERR"); #endif } #ifdef I2C_USE_DMA static void hal_i2c_tx_dma_handler(uint8_t chan, uint32_t remain_tsize, uint32_t error, struct HAL_DMA_DESC_T *lli) { enum HAL_I2C_ID_T id; for (id = HAL_I2C_ID_0; id < HAL_I2C_ID_NUM; id++) { if (hal_i2c_sm[id].tx_dma_cfg.ch == chan) { } } } static void hal_i2c_rx_dma_handler(uint8_t chan, uint32_t remain_tsize, uint32_t error, struct HAL_DMA_DESC_T *lli) { enum HAL_I2C_ID_T id; for (id = HAL_I2C_ID_0; id < HAL_I2C_ID_NUM; id++) { if (hal_i2c_sm[id].rx_dma_cfg.ch == chan) { } } } static void hal_i2c_dma_release(enum HAL_I2C_ID_T id) { uint32_t reg_base; struct HAL_DMA_CH_CFG_T *tx_dma_cfg = NULL, *rx_dma_cfg = NULL; if (hal_i2c_sm[id].cfg.use_dma == 0) { return; } reg_base = _i2c_get_base(id); i2cip_w_tx_dma_enable(reg_base, HAL_I2C_NO); i2cip_w_rx_dma_enable(reg_base, HAL_I2C_NO); tx_dma_cfg = &hal_i2c_sm[id].tx_dma_cfg; rx_dma_cfg = &hal_i2c_sm[id].rx_dma_cfg; HAL_I2C_TRACE(1,"i2c tx free dma ch %d", tx_dma_cfg->ch); if (tx_dma_cfg->ch != HAL_DMA_CHAN_NONE) { hal_gpdma_cancel(tx_dma_cfg->ch); hal_gpdma_free_chan(tx_dma_cfg->ch); tx_dma_cfg->ch = HAL_DMA_CHAN_NONE; } HAL_I2C_TRACE(1,"i2c rx free dma ch %d", rx_dma_cfg->ch); if (rx_dma_cfg->ch != HAL_DMA_CHAN_NONE) { hal_gpdma_cancel(rx_dma_cfg->ch); hal_gpdma_free_chan(rx_dma_cfg->ch); rx_dma_cfg->ch = HAL_DMA_CHAN_NONE; } } static void hal_i2c_dma_config(enum HAL_I2C_ID_T id, const struct HAL_I2C_SM_TASK_T *out_task) { uint32_t reg_base; uint32_t i, txn; uint32_t txn_start, org_start; struct HAL_DMA_CH_CFG_T *tx_dma_cfg = NULL, *rx_dma_cfg = NULL; enum HAL_I2C_SM_TASK_ACTION_T action; uint32_t total_tx_len; if (hal_i2c_sm[id].cfg.use_dma == 0) { return; } reg_base = _i2c_get_base(id); action = out_task->action; total_tx_len = out_task->tx_txn_len * out_task->txn_cnt; if (action == HAL_I2C_SM_TASK_ACTION_M_RECV) { total_tx_len += out_task->rx_txn_len * out_task->txn_cnt; } ASSERT(total_tx_len < ARRAY_SIZE(hal_i2c_sm[id].dma_tx_buf), "%s: xfer size too large: action=%d total_tx_len=%u (should <= %u)", __FUNCTION__, action, total_tx_len, ARRAY_SIZE(hal_i2c_sm[id].dma_tx_buf)); i2cip_w_tx_dma_enable(reg_base, HAL_I2C_YES); i2cip_w_tx_dma_tl(reg_base, HAL_I2C_DMA_TX_TL); tx_dma_cfg = &hal_i2c_sm[id].tx_dma_cfg; memset(tx_dma_cfg, 0, sizeof(*tx_dma_cfg)); tx_dma_cfg->dst = 0; // useless tx_dma_cfg->dst_bsize = HAL_DMA_BSIZE_1; tx_dma_cfg->dst_periph = i2c_desc[id].tx_periph; tx_dma_cfg->dst_width = HAL_DMA_WIDTH_HALFWORD; tx_dma_cfg->handler = hal_i2c_tx_dma_handler; tx_dma_cfg->src_bsize = HAL_DMA_BSIZE_1; tx_dma_cfg->src_periph = 0; // useless tx_dma_cfg->src_tsize = total_tx_len; tx_dma_cfg->src_width = HAL_DMA_WIDTH_HALFWORD; tx_dma_cfg->try_burst = 1; tx_dma_cfg->type = HAL_DMA_FLOW_M2P_DMA; tx_dma_cfg->src = (uint32_t)hal_i2c_sm[id].dma_tx_buf; tx_dma_cfg->ch = hal_gpdma_get_chan(tx_dma_cfg->dst_periph, HAL_DMA_HIGH_PRIO); HAL_I2C_TRACE(1,"i2c tx get dma ch %d", tx_dma_cfg->ch); ASSERT(tx_dma_cfg->ch != HAL_DMA_CHAN_NONE, "I2C: Failed to get tx dma chan"); HAL_I2C_TRACE(3,"tx size %d cnt %d total tx size %d", out_task->tx_txn_len, out_task->txn_cnt, total_tx_len); memset(&hal_i2c_sm[id].dma_tx_buf[0], 0, sizeof(hal_i2c_sm[id].dma_tx_buf)); for (txn = 0; txn < out_task->txn_cnt; txn++) { org_start = out_task->tx_txn_len * txn; txn_start = (out_task->tx_txn_len + out_task->rx_txn_len) * txn; for (i = 0; i < out_task->tx_txn_len; i++) { // lo byte of short : for data // hi byte of short : for cmd/stop/restart hal_i2c_sm[id].dma_tx_buf[txn_start + i] = out_task->tx_buf[org_start + i]; } if (txn && out_task->restart_after_write) { hal_i2c_sm[id].dma_tx_buf[txn_start] |= I2CIP_CMD_DATA_RESTART_MASK; } } if (action == HAL_I2C_SM_TASK_ACTION_M_RECV) { i2cip_w_rx_dma_enable(reg_base, HAL_I2C_YES); i2cip_w_rx_dma_tl(reg_base, HAL_I2C_DMA_RX_TL); for (txn = 0; txn < out_task->txn_cnt; txn++) { txn_start = (out_task->tx_txn_len + out_task->rx_txn_len) * txn + out_task->tx_txn_len; for (i = 0; i < out_task->rx_txn_len; i++) { // lo byte of short : for data // hi byte of short : for cmd/stop/restart hal_i2c_sm[id].dma_tx_buf[txn_start + i] = I2CIP_CMD_DATA_CMD_READ_MASK; } if (out_task->restart_after_write) { hal_i2c_sm[id].dma_tx_buf[txn_start] |= I2CIP_CMD_DATA_RESTART_MASK; } } HAL_I2C_TRACE(2,"rx size %d cnt %d", out_task->rx_txn_len, out_task->txn_cnt); rx_dma_cfg = &hal_i2c_sm[id].rx_dma_cfg; memset(rx_dma_cfg, 0, sizeof(*rx_dma_cfg)); rx_dma_cfg->dst = (uint32_t)(out_task->rx_buf); rx_dma_cfg->dst_bsize = HAL_DMA_BSIZE_1; rx_dma_cfg->dst_periph = 0; // useless rx_dma_cfg->dst_width = HAL_DMA_WIDTH_BYTE; rx_dma_cfg->handler = hal_i2c_rx_dma_handler; rx_dma_cfg->src_bsize = HAL_DMA_BSIZE_1; rx_dma_cfg->src_periph = i2c_desc[id].rx_periph; rx_dma_cfg->src_tsize = out_task->rx_txn_len * out_task->txn_cnt; rx_dma_cfg->src_width = HAL_DMA_WIDTH_BYTE; rx_dma_cfg->try_burst = 0; rx_dma_cfg->src = 0; // useless rx_dma_cfg->type = HAL_DMA_FLOW_P2M_DMA; rx_dma_cfg->ch = hal_gpdma_get_chan(rx_dma_cfg->src_periph, HAL_DMA_HIGH_PRIO); HAL_I2C_TRACE(1,"i2c rx get dma ch %d", rx_dma_cfg->ch); ASSERT(tx_dma_cfg->ch != HAL_DMA_CHAN_NONE, "I2C: Failed to get rx dma chan"); } if (out_task->stop) { hal_i2c_sm[id].dma_tx_buf[total_tx_len - 1] |= I2CIP_CMD_DATA_STOP_MASK; } } #endif #endif #ifdef I2C_TASK_MODE static void hal_i2c_sm_done_task(enum HAL_I2C_ID_T id) { struct HAL_I2C_SM_TASK_T *task; uint32_t reg_base = _i2c_get_base(id); task = &(hal_i2c_sm[id].task[hal_i2c_sm[id].out_task]); if (task->errcode) { HAL_I2C_ERROR(1,"i2c err : 0x%X", task->errcode); _i2c_show_error_code(task->errcode); } if (task->stop || task->errcode) { HAL_I2C_TRACE(0,"disable i2c"); i2cip_w_enable(reg_base, HAL_I2C_NO); } #ifdef I2C_USE_DMA if (hal_i2c_sm[id].cfg.use_dma) { hal_i2c_dma_release(id); } #endif if (hal_i2c_sm[id].cfg.use_sync) { /* FIXME : os and non-os - different proc */ task->lock = 0; } else { if (task->handler) { task->handler(id, task->transfer_id, task->tx_buf, task->tx_txn_len * task->txn_cnt, task->rx_buf, task->rx_txn_len * task->txn_cnt, task->errcode); } } hal_i2c_sm[id].out_task = (hal_i2c_sm[id].out_task+1)%HAL_I2C_SM_TASK_NUM_MAX; --hal_i2c_sm[id].task_count; } static void hal_i2c_sm_next_task(enum HAL_I2C_ID_T id) { uint32_t out_task_index; uint32_t reg_base, reinit; enum HAL_I2C_SM_TASK_ACTION_T action; struct HAL_I2C_SM_TASK_T *out_task = 0; reg_base = _i2c_get_base(id); if (hal_i2c_sm[id].task_count <= 0) { HAL_I2C_TRACE(0,"no task"); hal_i2c_sm_done(id); return; } out_task_index = hal_i2c_sm[id].out_task; out_task = &(hal_i2c_sm[id].task[out_task_index]); action = out_task->action; /* tx : quarter trigger TX EMPTY INT */ i2cip_w_tx_threshold(reg_base, HAL_I2C_TX_TL); if (action == HAL_I2C_SM_TASK_ACTION_M_RECV) { /* rx : three quarter trigger RX FULL INT */ i2cip_w_rx_threshold(reg_base, HAL_I2C_RX_TL); } #ifdef I2C_USE_DMA if (hal_i2c_sm[id].cfg.use_dma) { i2cip_init_int_mask(reg_base, I2CIP_INT_MASK_STOP_DET_MASK|I2CIP_INT_MASK_ERROR_MASK); /* prepare for dma operation */ hal_i2c_dma_config(id, out_task); } else #endif { /* open all interrupt */ i2cip_init_int_mask(reg_base, (I2CIP_INT_MASK_ALL & ~(I2CIP_INT_MASK_START_DET_MASK | I2CIP_INT_MASK_ACTIVITY_MASK))); } reinit = i2cip_r_enable_status(reg_base); /* not enable : reconfig i2cip with new-task params */ /* enable : same operation with pre task */ if (!(reinit & I2CIP_ENABLE_STATUS_ENABLE_MASK)) { HAL_I2C_TRACE(0,"enable i2c"); i2cip_w_restart(reg_base, HAL_I2C_YES); i2cip_w_target_address(reg_base, out_task->target_addr); i2cip_w_enable(reg_base, HAL_I2C_YES); } #ifdef I2C_USE_DMA if (hal_i2c_sm[id].cfg.use_dma) { if (action == HAL_I2C_SM_TASK_ACTION_M_RECV) { HAL_I2C_TRACE(0,"enable rx dma"); hal_gpdma_start(&hal_i2c_sm[id].rx_dma_cfg); } HAL_I2C_TRACE(0,"enable tx dma"); hal_gpdma_start(&hal_i2c_sm[id].tx_dma_cfg); } #endif } static uint32_t hal_i2c_sm_wait_task_if_need(enum HAL_I2C_ID_T id, uint32_t task_idx, uint32_t tm_ms) { int tmcnt; struct HAL_I2C_SM_TASK_T *task = 0; /* FIXME : task_id maybe invalid cause so-fast device operation */ task = &(hal_i2c_sm[id].task[task_idx]); if (!(hal_i2c_sm[id].cfg.use_sync)) return 0; /* FIXME : os and non-os - different proc */ tmcnt = tm_ms / HAL_I2C_DLY_MS; while(1) { uint32_t lock = task->lock; if (!lock) break; if (!tmcnt) { HAL_I2C_TRACE(1,"wait lock timeout %d ms", tm_ms); task->errcode = HAL_I2C_ERRCODE_SYNC_TIMEOUT; hal_i2c_sm_done_task(id); hal_i2c_sm_next_task(id); break; } hal_i2c_delay_ms(HAL_I2C_DLY_MS); tmcnt--; } return task->errcode; } static void hal_i2c_sm_kickoff(enum HAL_I2C_ID_T id) { if (hal_i2c_sm[id].state == HAL_I2C_SM_IDLE) { hal_i2c_sm[id].state = HAL_I2C_SM_RUNNING; hal_i2c_sm_next_task(id); } } static void hal_i2c_sm_proc(enum HAL_I2C_ID_T id) { uint32_t reg_base = 0; enum HAL_I2C_SM_STATE_T state; enum HAL_I2C_SM_TASK_STATE_T task_state; struct HAL_I2C_SM_TASK_T *task; uint32_t i = 0, restart = 0, stop = 0, data = 0; uint32_t ip_int_status = 0, tx_abrt_source = 0; uint8_t rx_limit, tx_limit, rx_cnt, tx_cnt; uint32_t total_tx_len, total_rx_len; uint32_t txn_idx, txn_pos; reg_base = _i2c_get_base(id); state = hal_i2c_sm[id].state; task = &(hal_i2c_sm[id].task[hal_i2c_sm[id].out_task]); ip_int_status = i2cip_r_int_status(reg_base); tx_abrt_source = i2cip_r_tx_abrt_source(reg_base); HAL_I2C_TRACE(4,"id:%d, ip_int_status=0x%X tx_abrt_source=0x%X state=%d", id, ip_int_status, tx_abrt_source, state); task_state = _i2c_chk_clr_task_error(reg_base, ip_int_status, tx_abrt_source); if (state != HAL_I2C_SM_RUNNING) { HAL_I2C_ERROR(3,"*** WARNING: No i2c task running: id=%d ip_int_status=0x%X tx_abrt_source=0x%X", id, ip_int_status, tx_abrt_source); i2cip_r_clr_all_intr(reg_base); return; } HAL_I2C_TRACE(1,"RUNNING action=%d", task->action); task->state |= task_state; if (task->state & (HAL_I2C_SM_TASK_STATE_TX_ABRT | HAL_I2C_SM_TASK_STATE_FIFO_ERR)) { HAL_I2C_ERROR(5,"*** ERROR:%s: id=%d task_state=0x%X ip_int_status=0x%X tx_abrt_source=0x%X", __func__, id, task->state, ip_int_status, tx_abrt_source); task->errcode = tx_abrt_source; if (task->state & HAL_I2C_SM_TASK_STATE_FIFO_ERR) { task->errcode |= HAL_I2C_ERRCODE_FIFO_ERR; } /* done task on any error */ hal_i2c_sm_done_task(id); hal_i2c_sm_next_task(id); return; } /* stop det interrupt */ if (ip_int_status & I2CIP_INT_STATUS_STOP_DET_MASK) { task->state |= HAL_I2C_SM_TASK_STATE_STOP; i2cip_r_clr_stop_det(reg_base); } /* start det interrupt */ if (ip_int_status & I2CIP_INT_STATUS_START_DET_MASK) { task->state |= HAL_I2C_SM_TASK_STATE_START; i2cip_r_clr_start_det(reg_base); } /* activeity det interrupt */ if (ip_int_status & I2CIP_INT_STATUS_ACTIVITY_MASK) { task->state |= HAL_I2C_SM_TASK_STATE_ACT; i2cip_r_clr_activity(reg_base); } switch (task->action) { case HAL_I2C_SM_TASK_ACTION_M_SEND: { if (hal_i2c_sm[id].cfg.use_dma == 0) { /* tx empty : means tx fifo is at or below IC_TX_TL : need to write more data : we can NOT clear this bit, cleared by hw */ if (ip_int_status & I2CIP_INT_STATUS_TX_EMPTY_MASK) { total_tx_len = task->tx_txn_len * task->txn_cnt; tx_limit = i2cip_r_tx_fifo_level(reg_base); if (tx_limit < I2CIP_TX_FIFO_DEPTH) { tx_limit = I2CIP_TX_FIFO_DEPTH - tx_limit; } else { tx_limit = 0; } HAL_I2C_TRACE(4,"m_send: tx_pos=%d tx_txn_len=%d cnt=%d tx_limit=%d", task->tx_pos, task->tx_txn_len, task->txn_cnt, tx_limit); for (i = task->tx_pos, tx_cnt = 0; ((i < total_tx_len) && (tx_cnt < tx_limit)); ++i, ++tx_cnt) { /* last byte : we need to decide stop */ if (i == total_tx_len - 1) { stop = task->stop ? I2CIP_CMD_DATA_STOP_MASK : 0; } else { stop = 0; } if (task->txn_cnt == 1) { txn_pos = i; } else { txn_pos = i % task->tx_txn_len; } /* first byte : need to decide restart */ if (i && task->restart_after_write && txn_pos == 0) { restart = I2CIP_CMD_DATA_RESTART_MASK; } else { restart = 0; } /* write data to FIFO */ i2cip_w_cmd_data(reg_base, task->tx_buf[i] | I2CIP_CMD_DATA_CMD_WRITE_MASK | restart | stop); HAL_I2C_TRACE(3,"m_send: data=0x%02X restart=0x%X stop=0x%X", task->tx_buf[i], restart, stop); } task->tx_pos = i; /* all write action done : do NOT need tx empty int */ if (task->tx_pos == total_tx_len) { i2cip_clear_int_mask(reg_base, I2CIP_INT_MASK_TX_EMPTY_MASK); } HAL_I2C_TRACE(1,"m_send: i2c status=0x%X", i2cip_r_status(reg_base)); } } /* stop condition : done task */ if (task->state & HAL_I2C_SM_TASK_STATE_STOP) { HAL_I2C_TRACE(1,"m_send: task->state:0x%X", task->state); hal_i2c_sm_done_task(id); hal_i2c_sm_next_task(id); } break; } case HAL_I2C_SM_TASK_ACTION_M_RECV: { if (hal_i2c_sm[id].cfg.use_dma == 0) { /* rx full : need to read */ if (ip_int_status & I2CIP_INT_STATUS_RX_FULL_MASK) { total_rx_len = task->rx_txn_len * task->txn_cnt; rx_limit = i2cip_r_rx_fifo_level(reg_base); HAL_I2C_TRACE(4,"m_recv:full: rx_pos=%d rx_txn_len=%d cnt=%d rx_limit=%d", task->rx_pos, task->rx_txn_len, task->txn_cnt, rx_limit); for (i = task->rx_pos, rx_cnt = 0; ((i < total_rx_len) && (rx_cnt < rx_limit)); ++i, ++rx_cnt) { task->rx_buf[i] = i2cip_r_cmd_data(reg_base); HAL_I2C_TRACE(2,"m_recv:full: rx_buf[%d] 0x%X", i, task->rx_buf[i]); } task->rx_pos = i; } /* tx empty : means tx fifo is at or below IC_TX_TL : need to write more data : we can NOT clear this bit, cleared by hw */ if (ip_int_status & I2CIP_INT_STATUS_TX_EMPTY_MASK) { total_tx_len = (task->tx_txn_len + task->rx_txn_len) * task->txn_cnt; tx_limit = i2cip_r_tx_fifo_level(reg_base); if (tx_limit < I2CIP_TX_FIFO_DEPTH) { tx_limit = I2CIP_TX_FIFO_DEPTH - tx_limit; } else { tx_limit = 0; } rx_limit = task->rx_cmd_sent - task->rx_pos + 1; if (rx_limit < I2CIP_RX_FIFO_DEPTH) { rx_limit = I2CIP_RX_FIFO_DEPTH - rx_limit; } else { rx_limit = 0; } HAL_I2C_TRACE(6,"m_recv:txEmpty: tx_pos=%d tx_txn_len=%d rx_txn_len=%d cnt=%d tx_limit=%d rx_limit=%d", task->tx_pos, task->tx_txn_len, task->rx_txn_len, task->txn_cnt, tx_limit, rx_limit); if (tx_limit > rx_limit) { tx_limit = rx_limit; } for (i = task->tx_pos, tx_cnt = 0; ((i < total_tx_len) && (tx_cnt < tx_limit)); ++i, ++tx_cnt) { /* last byte : we need to decide stop */ if (i == (total_tx_len - 1)) { stop = task->stop ? I2CIP_CMD_DATA_STOP_MASK : 0; } else { stop = 0; } if (task->txn_cnt == 1) { txn_idx = 0; txn_pos = i; } else { txn_idx = i / (task->tx_txn_len + task->rx_txn_len); txn_pos = i % (task->tx_txn_len + task->rx_txn_len); } /* first byte : need to decide restart */ if (i && task->restart_after_write && (txn_pos == 0 || txn_pos == task->tx_txn_len)) { restart = I2CIP_CMD_DATA_RESTART_MASK; } else { restart = 0; } /* real write data */ if (txn_pos < task->tx_txn_len) { if (task->txn_cnt == 1) { data = task->tx_buf[txn_pos]; } else { data = task->tx_buf[txn_idx * task->tx_txn_len + txn_pos]; } } else { data = I2CIP_CMD_DATA_CMD_READ_MASK; task->rx_cmd_sent++; } i2cip_w_cmd_data(reg_base, data | restart | stop); HAL_I2C_TRACE(4,"m_recv:tx: data[%u]=0x%02X restart=0x%X stop=0x%X", i, data, restart, stop); } task->tx_pos = i; /* all write action done */ if (task->tx_pos == total_tx_len) { i2cip_clear_int_mask(reg_base, I2CIP_INT_MASK_TX_EMPTY_MASK); } } } /* stop condition : need to read out all rx fifo */ if (task->state & HAL_I2C_SM_TASK_STATE_STOP) { if (hal_i2c_sm[id].cfg.use_dma) { #ifdef I2C_USE_DMA if (hal_i2c_sm[id].rx_dma_cfg.ch == HAL_DMA_CHAN_NONE) { HAL_I2C_TRACE(0,"m_recv:stop:WARNING: bad rx dma chan!"); } else { if (hal_dma_chan_busy(hal_i2c_sm[id].rx_dma_cfg.ch)) { HAL_I2C_TRACE(0,"m_recv:stop:WARNING: rx dma not finished yet!"); } } #endif } else { rx_limit = i2cip_r_rx_fifo_level(reg_base); HAL_I2C_TRACE(1,"m_recv:stop: rx_limit=%d", rx_limit); for (i = task->rx_pos, rx_cnt = 0; ((rx_cnt < rx_limit) && (i < task->rx_txn_len)); ++i, ++rx_cnt) { task->rx_buf[i] = i2cip_r_cmd_data(reg_base); HAL_I2C_TRACE(2,"m_recv:stop: rx_buf[%d] 0x%X", i, task->rx_buf[i]); } task->rx_pos = i; if (task->rx_pos != task->rx_txn_len * task->txn_cnt) { HAL_I2C_TRACE(3,"m_recv:stop:WARNING: rx_pos(%u) != rx_txn_len(%u) * txn_cnt(%u)", task->rx_pos, task->rx_txn_len, task->txn_cnt); } } hal_i2c_sm_done_task(id); hal_i2c_sm_next_task(id); } break; } default: break; } } void hal_i2c_irq_handler(void) { enum HAL_I2C_ID_T id; for (id = HAL_I2C_ID_0; id < HAL_I2C_ID_NUM; id++) { if (NVIC_GetActive(i2c_desc[id].irq)) { hal_i2c_sm_proc(id); } } } #endif #ifdef I2C_SIMPLE_MODE void hal_i2c_irq_handler_s(void) { enum HAL_I2C_ID_T id; for (id = HAL_I2C_ID_0; id < HAL_I2C_ID_NUM; id++) { if (NVIC_GetActive(i2c_desc[id].irq)) { hal_i2c_simple_proc(id); } } } #endif #ifdef I2C_SENSOR_ENGINE void hal_i2c_irq_handler_sensor_eng(void) { enum HAL_I2C_ID_T id; for (id = HAL_I2C_ID_0; id < HAL_I2C_ID_NUM; id++) { if (NVIC_GetActive(i2c_desc[id].irq)) { hal_i2c_sensor_eng_proc(id); } } } #endif static int POSSIBLY_UNUSED hal_i2c_nvic_setup_irq(enum HAL_I2C_ID_T id, uint32_t handler) { IRQn_Type irqt = i2c_desc[id].irq; NVIC_SetVector(irqt, handler); NVIC_SetPriority(irqt, IRQ_PRIORITY_NORMAL); return 0; } static int hal_i2c_nvic_enable_irq(enum HAL_I2C_ID_T id, int enabled) { IRQn_Type irqt = i2c_desc[id].irq; if (enabled) { NVIC_ClearPendingIRQ(irqt); NVIC_EnableIRQ(irqt); } else { NVIC_DisableIRQ(irqt); NVIC_ClearPendingIRQ(irqt); } return 0; } uint32_t hal_i2c_open(enum HAL_I2C_ID_T id, const struct HAL_I2C_CONFIG_T *cfg) { uint32_t reg_base; ASSERT(id < HAL_I2C_ID_NUM, invalid_id, id); if (hal_i2c_sm[id].state != HAL_I2C_SM_CLOSED) { return HAL_I2C_ERRCODE_IN_USE; } hal_i2c_sm_init(id, cfg); hal_cmu_clock_enable(i2c_desc[id].mod); hal_cmu_clock_enable(i2c_desc[id].apb); hal_cmu_reset_clear(i2c_desc[id].mod); hal_cmu_reset_clear(i2c_desc[id].apb); reg_base = _i2c_get_base(id); HAL_I2C_TRACE(2,"i2c id=%d reg_base=0x%X", id, reg_base); i2cip_w_enable(reg_base, HAL_I2C_NO); i2cip_w_target_address(reg_base, 0x18); /* clear */ i2cip_w_clear_ctrl(reg_base); /* as master */ if (cfg->as_master) { i2cip_w_disable_slave(reg_base, HAL_I2C_YES); i2cip_w_master_mode(reg_base, HAL_I2C_YES); } else { i2cip_w_disable_slave(reg_base, HAL_I2C_NO); i2cip_w_master_mode(reg_base, HAL_I2C_NO); /* address as slave */ i2cip_w_address_as_slave(reg_base, cfg->addr_as_slave); } /* speed */ _i2c_set_bus_speed(id, cfg->speed); if (0) { #ifdef I2C_SIMPLE_MODE } else if (cfg->mode == HAL_I2C_API_MODE_SIMPLE) { HAL_I2C_TRACE(0,"simple mode"); hal_i2c_nvic_setup_irq(id, (uint32_t)hal_i2c_irq_handler_s); /* only open slave related int, polling in master */ /* read req (master read us), rx full (master write us) */ if (cfg->as_master) { i2cip_init_int_mask(reg_base, I2CIP_INT_UNMASK_ALL); } else { i2cip_init_int_mask(reg_base, I2CIP_INT_MASK_RD_REQ_MASK|I2CIP_INT_MASK_RX_FULL_MASK); } /* rx threshold, 1 byte trigger RX_FULL */ i2cip_w_rx_threshold(reg_base, I2CIP_RX_TL_1_BYTE); /* tx threshold, 1 byte trigger TX_EMPTY */ i2cip_w_tx_threshold(reg_base, I2CIP_TX_TL_1_BYTE); /* enable restart */ i2cip_w_restart(reg_base, HAL_I2C_YES); /* enable i2c */ i2cip_w_enable(reg_base, HAL_I2C_YES); #endif #ifdef I2C_TASK_MODE } else if (cfg->mode == HAL_I2C_API_MODE_TASK) { /* only use task irq handler when master mode */ HAL_I2C_TRACE(0,"task mode"); if (!cfg->as_master) { HAL_I2C_TRACE(0,"not as master"); return HAL_I2C_ERRCODE_INV_PARAM; } #ifndef I2C_USE_DMA if (cfg->use_dma) { HAL_I2C_TRACE(0,"using DMA when I2C_USE_DMA is NOT enabled"); return HAL_I2C_ERRCODE_INV_PARAM; } #endif hal_i2c_nvic_setup_irq(id, (uint32_t)hal_i2c_irq_handler); #endif #ifdef I2C_SENSOR_ENGINE } else if (cfg->mode == HAL_I2C_API_MODE_SENSOR_ENGINE) { HAL_I2C_TRACE(0,"sensor engine mode"); ASSERT(cfg->as_master, "%s: i2c master should be used with sensor engine", __FUNCTION__); ASSERT(cfg->use_dma, "%s: i2c dma should be used with sensor engine", __FUNCTION__); hal_i2c_nvic_setup_irq(id, (uint32_t)hal_i2c_irq_handler_sensor_eng); #endif } else { ASSERT(false, "%s: Bad i2c api mode: %u", __FUNCTION__, cfg->mode); } hal_i2c_nvic_enable_irq(id, HAL_I2C_YES); return 0; } uint32_t hal_i2c_close(enum HAL_I2C_ID_T id) { uint32_t reg_base; ASSERT(id < HAL_I2C_ID_NUM, invalid_id, id); if (hal_i2c_sm[id].state == HAL_I2C_SM_CLOSED) { return 0; } reg_base = _i2c_get_base(id); hal_i2c_nvic_enable_irq(id, HAL_I2C_NO); i2cip_w_enable(reg_base, HAL_I2C_NO); #if defined(I2C_TASK_MODE) || defined(I2C_SENSOR_ENGINE) #ifdef I2C_USE_DMA hal_i2c_dma_release(id); #endif #endif hal_cmu_reset_set(i2c_desc[id].apb); hal_cmu_reset_set(i2c_desc[id].mod); hal_cmu_clock_disable(i2c_desc[id].apb); hal_cmu_clock_disable(i2c_desc[id].mod); hal_i2c_sm[id].state = HAL_I2C_SM_CLOSED; return 0; } /* task mode */ #ifdef I2C_TASK_MODE uint32_t hal_i2c_task_msend(enum HAL_I2C_ID_T id, uint16_t device_addr, const uint8_t *tx_buf, uint16_t tx_item_len, uint16_t item_cnt, uint32_t transfer_id, HAL_I2C_TRANSFER_HANDLER_T handler) { uint32_t task_idx; ASSERT(id < HAL_I2C_ID_NUM, invalid_id, id); if (hal_i2c_sm[id].cfg.mode != HAL_I2C_API_MODE_TASK) { HAL_I2C_TRACE(0,"send: not task mode"); return HAL_I2C_ERRCODE_INV_PARAM; } task_idx = hal_i2c_sm_commit(id, tx_buf, tx_item_len, NULL, 0, item_cnt, device_addr, HAL_I2C_SM_TASK_ACTION_M_SEND, transfer_id, handler); hal_i2c_sm_kickoff(id); return hal_i2c_sm_wait_task_if_need(id, task_idx, HAL_I2C_SYNC_TM_MS); } uint32_t hal_i2c_task_send(enum HAL_I2C_ID_T id, uint16_t device_addr, const uint8_t *tx_buf, uint16_t tx_len, uint32_t transfer_id, HAL_I2C_TRANSFER_HANDLER_T handler) { return hal_i2c_task_msend(id, device_addr, tx_buf, tx_len, 1, transfer_id, handler); } uint32_t hal_i2c_task_mrecv(enum HAL_I2C_ID_T id, uint16_t device_addr, const uint8_t *tx_buf, uint16_t tx_item_len, uint8_t *rx_buf, uint16_t rx_item_len, uint16_t item_cnt, uint32_t transfer_id, HAL_I2C_TRANSFER_HANDLER_T handler) { uint32_t task_idx; ASSERT(id < HAL_I2C_ID_NUM, invalid_id, id); if (hal_i2c_sm[id].cfg.mode != HAL_I2C_API_MODE_TASK) { HAL_I2C_TRACE(0,"recv: not task mode"); return HAL_I2C_ERRCODE_INV_PARAM; } task_idx = hal_i2c_sm_commit(id, tx_buf, tx_item_len, rx_buf, rx_item_len, item_cnt, device_addr, HAL_I2C_SM_TASK_ACTION_M_RECV, transfer_id, handler); hal_i2c_sm_kickoff(id); return hal_i2c_sm_wait_task_if_need(id, task_idx, HAL_I2C_SYNC_TM_MS); } uint32_t hal_i2c_task_recv(enum HAL_I2C_ID_T id, uint16_t device_addr, const uint8_t *tx_buf, uint16_t tx_len, uint8_t *rx_buf, uint16_t rx_len, uint32_t transfer_id, HAL_I2C_TRANSFER_HANDLER_T handler) { return hal_i2c_task_mrecv(id, device_addr, tx_buf, tx_len, rx_buf, rx_len, 1, transfer_id, handler); } uint32_t hal_i2c_send(enum HAL_I2C_ID_T id, uint32_t device_addr, uint8_t *buf, uint32_t reg_len, uint32_t value_len, uint32_t transfer_id, HAL_I2C_TRANSFER_HANDLER_T handler) { return hal_i2c_task_send(id, device_addr, buf, reg_len + value_len, transfer_id, handler); } uint32_t hal_i2c_recv(enum HAL_I2C_ID_T id, uint32_t device_addr, uint8_t *buf, uint32_t reg_len, uint32_t value_len, uint8_t restart_after_write, uint32_t transfer_id, HAL_I2C_TRANSFER_HANDLER_T handler) { return hal_i2c_task_recv(id, device_addr, buf, reg_len, buf + reg_len, value_len, transfer_id, handler); } #endif /* task mode end */ /* simple mode */ #ifdef I2C_SIMPLE_MODE static void hal_i2c_simple_proc(enum HAL_I2C_ID_T id) { uint32_t reg_base = _i2c_get_base(id); uint32_t irq_status = i2cip_r_raw_int_status(reg_base); uint32_t abt_source = i2cip_r_tx_abrt_source(reg_base); HAL_I2C_INT_HANDLER_T h = hal_i2c_int_handlers[id]; i2cip_r_clr_all_intr(reg_base); if (h) { HAL_I2C_TRACE(3,"%s:irq=0x%X abt=0x%X", __func__, irq_status, abt_source); h(id, irq_status, abt_source); } // TODO: clean irq after callback done ? i2cip_r_clr_all_intr(reg_base); HAL_I2C_TRACE(3,"%s:0x%X 0x%X", __func__, i2cip_r_status(reg_base), i2cip_r_raw_int_status(reg_base)); } static uint32_t _i2c_check_tx_abrt_error(enum HAL_I2C_ID_T id) { uint32_t reg_base = _i2c_get_base(id); if (i2cip_r_raw_int_status(reg_base) & I2CIP_RAW_INT_STATUS_TX_ABRT_MASK) return i2cip_r_tx_abrt_source(reg_base); return 0; } uint32_t hal_i2c_slv_write(enum HAL_I2C_ID_T id, uint8_t *buf, uint32_t len, uint32_t *act_write) { uint32_t reg_base = 0, i = 0, ret = 0; reg_base = _i2c_get_base(id); *act_write = 0; for (i = 0; i < len;) { /* Tx Fifo Not Full */ if (i2cip_r_status(reg_base) & I2CIP_STATUS_TFNF_MASK) { i2cip_w_cmd_data(reg_base, buf[i]); ++(*act_write); ++i; } else { /* wait for TFNF & not rx done & no error */ while(!(i2cip_r_status(reg_base) & I2CIP_STATUS_TFNF_MASK) && !(i2cip_r_raw_int_status(reg_base) & I2CIP_RAW_INT_STATUS_RX_DONE_MASK) && !(ret = _i2c_check_tx_abrt_error(reg_base))); if (ret || (i2cip_r_raw_int_status(reg_base) & I2CIP_RAW_INT_STATUS_RX_DONE_MASK)) break; } } /* FIXME : tx empty is not end of transmit, i2cip may transmit the last byte */ while(!(i2cip_r_status(reg_base) & I2CIP_STATUS_TFE_SHIFT) && !(ret = _i2c_check_tx_abrt_error(reg_base))); /* wait to idle */ /* FIXME : time out */ while(i2cip_r_status(reg_base) & I2CIP_STATUS_ACT_MASK); return ret; } uint32_t hal_i2c_slv_read(enum HAL_I2C_ID_T id, uint8_t *buf, uint32_t len, uint32_t *act_read) { uint32_t reg_base = 0, i = 0, ret = 0, depth = 0; reg_base = _i2c_get_base(id); *act_read = 0; /* slave mode : just read */ for (i = 0; i < len;) { /* Rx Fifo Not Empty */ if (i2cip_r_status(reg_base) & I2CIP_STATUS_RFNE_MASK) { buf[i] = i2cip_r_cmd_data(reg_base); ++(*act_read); ++i; } else { /* wait for RFNE & no stop & no error */ while(!(i2cip_r_status(reg_base) & I2CIP_STATUS_RFNE_MASK) && !(i2cip_r_raw_int_status(reg_base) & I2CIP_RAW_INT_STATUS_STOP_DET_MASK) && !(ret = _i2c_check_tx_abrt_error(reg_base))) { } if (ret || (i2cip_r_raw_int_status(reg_base) & I2CIP_RAW_INT_STATUS_STOP_DET_MASK)) { HAL_I2C_TRACE(2,"drv:i2c slave read ret 0x%X, raw status 0x%X", ret, i2cip_r_raw_int_status(reg_base)); break; } } } /* may left some bytes in rx fifo */ depth = i2cip_r_rx_fifo_level(reg_base); HAL_I2C_TRACE(1,"drv:i2c slave read depth %d", depth); while (depth > 0 && i < len) { buf[i] = i2cip_r_cmd_data(reg_base); ++(*act_read); ++i; --depth; } /* wait to idle */ while(i2cip_r_status(reg_base) & I2CIP_STATUS_ACT_MASK); return 0; } static void i2c_clear_special_tx_abrt(uint32_t reg_base) { uint32_t abrt = i2cip_r_tx_abrt_source(reg_base); i2cip_r_clr_tx_abrt(reg_base); if (abrt & I2CIP_TX_ABRT_SOURCE_ABRT_SBYTE_NORSTRT_MASK) { i2cip_w_restart(reg_base, HAL_I2C_YES); i2cip_w_special_bit(reg_base, HAL_I2C_NO); i2cip_w_gc_or_start_bit(reg_base, HAL_I2C_NO); i2cip_r_clr_tx_abrt(reg_base); } } static uint32_t _i2c_check_raw_int_status(uint32_t reg_base, uint32_t mask) { uint32_t regval = i2cip_r_raw_int_status(reg_base); HAL_I2C_TRACE(3,"%s: raw status=0x%X mask=0x%X", __func__, regval, mask); return regval & mask; } uint32_t hal_i2c_mst_write(enum HAL_I2C_ID_T id, uint32_t dev_addr, const uint8_t *buf, uint32_t len, uint32_t *act_write, uint32_t restart, uint32_t stop, uint32_t yield) { uint32_t i, tar, wrcnt, ret = 0; uint32_t reg_base; uint32_t start_time, timeout; uint32_t res, sto; ASSERT(id < HAL_I2C_ID_NUM, invalid_id, id); if (hal_i2c_sm[id].cfg.mode != HAL_I2C_API_MODE_SIMPLE) { HAL_I2C_TRACE(0,"mst_write: not simple mode"); return HAL_I2C_ERRCODE_INV_PARAM; } HAL_I2C_TRACE(7,"%s:id=%d addr=0x%02X buf=0x%X len=%d res=%d sto=%d", __func__, id, dev_addr, (int)buf, len, restart, stop); reg_base = _i2c_get_base(id); //update TAR tar = i2cip_r_target_address_reg(reg_base); if (tar != dev_addr) { timeout = MS_TO_TICKS(HAL_I2C_WAIT_ACT_MS); start_time = hal_sys_timer_get(); while((i2cip_r_status(reg_base) & I2CIP_STATUS_ACT_MASK)) { if (hal_sys_timer_get() - start_time >= timeout) { HAL_I2C_TRACE(1,"%s:wait bus idle timeout", __func__); ret = HAL_I2C_ERRCODE_ACT_TIMEOUT; goto exit; } if (yield) { hal_i2c_delay_ms(HAL_I2C_DLY_MS); } } i2cip_w_enable(reg_base, HAL_I2C_NO); i2cip_w_target_address(reg_base, dev_addr); i2cip_w_enable(reg_base, HAL_I2C_YES); HAL_I2C_TRACE(3,"%s:update tar to 0x%02X from 0x%02X", __func__, dev_addr, tar); } // check error if (_i2c_check_raw_int_status(reg_base, I2CIP_RAW_INT_STATUS_TX_ABRT_MASK | I2CIP_RAW_INT_STATUS_TX_OVER_MASK | I2CIP_RAW_INT_STATUS_RX_OVER_MASK | I2CIP_RAW_INT_STATUS_RX_UNDER_MASK)) { i2cip_r_clr_all_intr(reg_base); i2c_clear_special_tx_abrt(reg_base); HAL_I2C_TRACE(1,"clear error status done before xfer: status=0x%X", i2cip_r_raw_int_status(reg_base)); } // start to transmit wrcnt = 0; for (i = 0; i < len; i++) { if (i == 0) { res = restart ? I2CIP_CMD_DATA_RESTART_MASK : 0; } else { res = 0; } if (i == (len - 1)) { sto = stop ? I2CIP_CMD_DATA_STOP_MASK : 0; } else { sto = 0; } timeout = MS_TO_TICKS(HAL_I2C_WAIT_TFNF_MS); start_time = hal_sys_timer_get(); while (!(i2cip_r_status(reg_base) & I2CIP_STATUS_TFNF_MASK)) { ret = _i2c_check_raw_int_status(reg_base, I2CIP_RAW_INT_STATUS_TX_ABRT_MASK); if (ret) { i2c_clear_special_tx_abrt(reg_base); HAL_I2C_TRACE(2,"%s:error 0x%X", __func__, ret); goto exit; } if (hal_sys_timer_get() - start_time >= timeout) { HAL_I2C_TRACE(1,"%s:wait tfnf timeout", __func__); ret = HAL_I2C_ERRCODE_TFNF_TIMEOUT; goto exit; } if (yield) { hal_i2c_delay_ms(HAL_I2C_DLY_MS); } } i2cip_w_cmd_data(reg_base, buf[i] | res | sto | I2CIP_CMD_DATA_CMD_WRITE_MASK); HAL_I2C_TRACE(4,"send write cmd: data[%d]=0x%02X res=0x%X sto=0x%X", i, buf[i], res, sto); wrcnt++; } if (act_write) *act_write = wrcnt; if (stop) { // wait bus idle timeout = MS_TO_TICKS(HAL_I2C_WAIT_ACT_MS); start_time = hal_sys_timer_get(); while (i2cip_r_status(reg_base) & I2CIP_STATUS_ACT_MASK) { if (hal_sys_timer_get() - start_time >= timeout) { if (_i2c_check_raw_int_status(reg_base, I2CIP_RAW_INT_STATUS_TX_ABRT_MASK)) { i2cip_r_clr_all_intr(reg_base); i2c_clear_special_tx_abrt(reg_base); HAL_I2C_TRACE(0,"clear error status done after xfer"); } HAL_I2C_TRACE(1,"%s:wait act timeout", __func__); ret = HAL_I2C_ERRCODE_ACT_TIMEOUT; goto exit; } if (yield) { hal_i2c_delay_ms(HAL_I2C_DLY_MS); } } } else { // wait until txfifo empty timeout = MS_TO_TICKS(HAL_I2C_WAIT_TFE_MS); start_time = hal_sys_timer_get(); while(!(i2cip_r_status(reg_base) & I2CIP_STATUS_TFE_MASK)) { if (hal_sys_timer_get() - start_time >= timeout) { HAL_I2C_TRACE(1,"%s:wait tfe timeout", __func__); ret = HAL_I2C_ERRCODE_TFE_TIMEOUT; goto exit; } if (yield) { hal_i2c_delay_ms(HAL_I2C_DLY_MS); } } } HAL_I2C_TRACE(1,"%s:done", __func__); return 0; exit: HAL_I2C_TRACE(3,"%s:error=0x%X, status=0x%X", __func__, ret, i2cip_r_raw_int_status(reg_base)); return ret; } uint32_t hal_i2c_mst_read(enum HAL_I2C_ID_T id, uint32_t dev_addr, uint8_t *buf, uint32_t len, uint32_t *act_read, uint32_t restart, uint32_t stop, uint32_t yield) { uint32_t i, j, tar, rdcnt, wrcnt, ret = 0; uint32_t reg_base; uint32_t start_time, timeout; uint32_t res, sto; uint8_t tmp; uint8_t rx_ongoing, tx_limit; ASSERT(id < HAL_I2C_ID_NUM, invalid_id, id); if (hal_i2c_sm[id].cfg.mode != HAL_I2C_API_MODE_SIMPLE) { HAL_I2C_TRACE(0,"mst_read: not simple mode"); return HAL_I2C_ERRCODE_INV_PARAM; } HAL_I2C_TRACE(7,"%s:id=%d addr=0x%02X buf=0x%X len=%d res=%d sto=%d", __func__, id, dev_addr, (int)buf, len, restart, stop); reg_base = _i2c_get_base(id); // update TAR tar = i2cip_r_target_address_reg(reg_base); if (tar != dev_addr) { timeout = MS_TO_TICKS(HAL_I2C_WAIT_ACT_MS); start_time = hal_sys_timer_get(); while((i2cip_r_status(reg_base) & I2CIP_STATUS_ACT_MASK)) { if (hal_sys_timer_get() - start_time >= timeout) { HAL_I2C_TRACE(1,"%s:wait bus idle timeout", __func__); ret = HAL_I2C_ERRCODE_ACT_TIMEOUT; goto exit; } if (yield) { hal_i2c_delay_ms(HAL_I2C_DLY_MS); } } i2cip_w_enable(reg_base, HAL_I2C_NO); i2cip_w_target_address(reg_base, dev_addr); i2cip_w_enable(reg_base, HAL_I2C_YES); HAL_I2C_TRACE(3,"%s:update tar to 0x%02X from 0x%02X", __func__, dev_addr, tar); } // clear fifo by reading j = i2cip_r_rx_fifo_level(reg_base); if (j) { for (i = 0;i < j; i++) { tmp = i2cip_r_cmd_data(reg_base); HAL_I2C_TRACE(2,"%s:discard data 0x%02X", __func__, tmp); } } // check error if (_i2c_check_raw_int_status(reg_base, I2CIP_RAW_INT_STATUS_TX_ABRT_MASK | I2CIP_RAW_INT_STATUS_TX_OVER_MASK | I2CIP_RAW_INT_STATUS_RX_OVER_MASK | I2CIP_RAW_INT_STATUS_RX_UNDER_MASK)) { i2cip_r_clr_all_intr(reg_base); i2c_clear_special_tx_abrt(reg_base); HAL_I2C_TRACE(1,"clear error status done before xfer, status=0x%X", i2cip_r_raw_int_status(reg_base)); } // read data timeout = MS_TO_TICKS(HAL_I2C_WAIT_RFNE_MS); start_time = hal_sys_timer_get(); wrcnt = 0; rdcnt = 0; while(rdcnt < len) { // send reading cmd rx_ongoing = i2cip_r_tx_fifo_level(reg_base) + i2cip_r_rx_fifo_level(reg_base) + 1; if (rx_ongoing < I2CIP_RX_FIFO_DEPTH) { tx_limit = I2CIP_RX_FIFO_DEPTH - rx_ongoing; } else { tx_limit = 0; } for(i = 0; i < tx_limit && wrcnt < len; i++, wrcnt++) { if (!(i2cip_r_status(reg_base) & I2CIP_STATUS_TFNF_MASK)) { break; } if (wrcnt == 0) { res = restart ? I2CIP_CMD_DATA_RESTART_MASK : 0; } else { res = 0; } if (wrcnt == len - 1) { sto = stop ? I2CIP_CMD_DATA_STOP_MASK : 0; } else { sto = 0; } i2cip_w_cmd_data(reg_base, I2CIP_CMD_DATA_CMD_READ_MASK | res | sto); HAL_I2C_TRACE(3,"send read cmd: [%u] res=0x%X, sto=0x%X", wrcnt, res, sto); } if (i2cip_r_status(reg_base) & I2CIP_STATUS_RFNE_MASK) { tmp = i2cip_r_cmd_data(reg_base); HAL_I2C_TRACE(2,"i2c recv [%u] 0x%02X", rdcnt, tmp); if (buf) { buf[rdcnt++] = tmp; } start_time = hal_sys_timer_get(); } else { if (hal_sys_timer_get() - start_time >= timeout) { HAL_I2C_TRACE(1,"%s:wait rfne timeout", __func__); ret = HAL_I2C_ERRCODE_RFNE_TIMEOUT; goto exit; } if (yield) { hal_i2c_delay_ms(HAL_I2C_DLY_MS); } } } if (act_read) *act_read = rdcnt; if (stop) { timeout = MS_TO_TICKS(HAL_I2C_WAIT_ACT_MS); start_time = hal_sys_timer_get(); while (i2cip_r_status(reg_base) & I2CIP_STATUS_ACT_MASK){ if (hal_sys_timer_get() - start_time >= timeout) { HAL_I2C_TRACE(1,"%s:wait act timeout", __func__); ret = HAL_I2C_ERRCODE_ACT_TIMEOUT; goto exit; } if (yield) { hal_i2c_delay_ms(HAL_I2C_DLY_MS); } } } HAL_I2C_TRACE(1,"%s:done", __func__); return 0; exit: HAL_I2C_TRACE(3,"%s:error=0x%X, status=0x%X", __func__, ret, i2cip_r_raw_int_status(reg_base)); return ret; } uint32_t hal_i2c_simple_send(enum HAL_I2C_ID_T id, uint16_t device_addr, const uint8_t *tx_buf, uint16_t tx_len) { return hal_i2c_mst_write(id, device_addr, tx_buf, tx_len, NULL, true, true, false); } uint32_t hal_i2c_simple_recv(enum HAL_I2C_ID_T id, uint16_t device_addr, const uint8_t *tx_buf, uint16_t tx_len, uint8_t *rx_buf, uint16_t rx_len) { int ret; ret = hal_i2c_mst_write(id, device_addr, tx_buf, tx_len, NULL, true, false, false); if (ret) { HAL_I2C_TRACE(2,"%s: mst_write failed: ret=%d", __func__, ret); return ret; } return hal_i2c_mst_read(id, device_addr, rx_buf, rx_len, NULL, true, true, false); } uint32_t hal_i2c_set_interrupt_handler(enum HAL_I2C_ID_T id, HAL_I2C_INT_HANDLER_T handler) { hal_i2c_int_handlers[id] = handler; return 0; } #endif /* simple mode end */ /* sensor engine mode */ #ifdef I2C_SENSOR_ENGINE static void hal_i2c_sensor_eng_proc(enum HAL_I2C_ID_T id) { HAL_I2C_TRACE(2,"%s: id=%d", __func__, id); uint32_t reg_base = _i2c_get_base(id); uint32_t irq_status = i2cip_r_int_status(reg_base); uint32_t POSSIBLY_UNUSED irq_raw_status = i2cip_r_raw_int_status(reg_base); uint32_t abt_source = i2cip_r_tx_abrt_source(reg_base); enum HAL_I2C_SM_TASK_STATE_T task_state; task_state = _i2c_chk_clr_task_error(reg_base, irq_status, abt_source); HAL_I2C_TRACE(5,"%s: id=%d irq=0x%X/0x%X abt=0x%X", __func__, id, irq_status, irq_raw_status, abt_source); _i2c_show_error_code(abt_source); if (task_state & (HAL_I2C_SM_TASK_STATE_TX_ABRT | HAL_I2C_SM_TASK_STATE_FIFO_ERR)) { HAL_I2C_ERROR(6,"*** Error:%s: id=%d task_state=0x%X irq=0x%X/0x%X abt=0x%X", __func__, id, task_state, irq_status, irq_raw_status, abt_source); hal_i2c_sensor_engine_stop(i2c_sensor_id[id]); } } static void _sensor_irq_handler(enum HAL_SENSOR_ENGINE_ID_T id, enum HAL_SENSOR_ENGINE_DEVICE_T device, const uint8_t *buf, uint32_t len) { enum HAL_I2C_ID_T i2c_id; i2c_id = HAL_I2C_ID_0 + (device - HAL_SENSOR_ENGINE_DEVICE_I2C0); ASSERT(i2c_id < HAL_I2C_ID_NUM, invalid_id, i2c_id); if (i2c_sensor_handler[i2c_id]) { i2c_sensor_handler[i2c_id](i2c_id, buf, len); } } uint32_t hal_i2c_sensor_engine_start(enum HAL_I2C_ID_T id, const struct HAL_I2C_SENSOR_ENGINE_CONFIG_T *cfg) { enum HAL_I2C_SM_TASK_ACTION_T action; const struct HAL_I2C_SM_TASK_T *out_task; struct HAL_SENSOR_ENGINE_CFG_T sensor_cfg; uint32_t reg_base; HAL_I2C_ERROR(2,"%s: id=%d", __func__, id); ASSERT(id < HAL_I2C_ID_NUM, invalid_id, id); if (hal_i2c_sm[id].cfg.mode != HAL_I2C_API_MODE_SENSOR_ENGINE) { HAL_I2C_TRACE(1,"i2c-se start: not sensor engine mode: %d", hal_i2c_sm[id].cfg.mode); return HAL_I2C_ERRCODE_INV_PARAM; } if (hal_i2c_sm[id].state != HAL_I2C_SM_IDLE) { HAL_I2C_TRACE(1,"i2c-se start: not idle: %d", hal_i2c_sm[id].state); return HAL_I2C_ERRCODE_INV_PARAM; } reg_base = _i2c_get_base(id); action = HAL_I2C_SM_TASK_ACTION_M_RECV; hal_i2c_sm[id].state = HAL_I2C_SM_RUNNING; hal_i2c_sm_commit(id, cfg->write_buf, cfg->write_txn_len, cfg->read_buf, cfg->read_txn_len, cfg->txn_cnt, cfg->target_addr, action, 0, NULL); out_task = &(hal_i2c_sm[id].task[hal_i2c_sm[id].out_task]); hal_i2c_dma_config(id, out_task); i2c_sensor_id[id] = cfg->id; i2c_sensor_handler[id] = cfg->handler; memset(&sensor_cfg, 0, sizeof(sensor_cfg)); sensor_cfg.id = cfg->id; sensor_cfg.device = (id == HAL_I2C_ID_0) ? HAL_SENSOR_ENGINE_DEVICE_I2C0 : HAL_SENSOR_ENGINE_DEVICE_I2C1; sensor_cfg.trigger_type = cfg->trigger_type; sensor_cfg.trigger_gpio = cfg->trigger_gpio; sensor_cfg.period_us = cfg->period_us; sensor_cfg.device_address = cfg->target_addr; sensor_cfg.tx_dma_cfg = &hal_i2c_sm[id].tx_dma_cfg; sensor_cfg.rx_dma_cfg = &hal_i2c_sm[id].rx_dma_cfg; sensor_cfg.rx_burst_len = cfg->read_txn_len * cfg->txn_cnt; sensor_cfg.rx_burst_cnt = cfg->read_burst_cnt; sensor_cfg.handler = _sensor_irq_handler;; #ifdef I2C_VAD sensor_cfg.data_to_vad = 1; i2cip_w_data_to_vad(reg_base, HAL_I2C_YES); #endif hal_sensor_engine_open(&sensor_cfg); /* tx : quarter trigger TX EMPTY INT */ i2cip_w_tx_threshold(reg_base, HAL_I2C_TX_TL); if (action == HAL_I2C_SM_TASK_ACTION_M_RECV) { /* rx : three quarter trigger RX FULL INT */ i2cip_w_rx_threshold(reg_base, HAL_I2C_RX_TL); } i2cip_init_int_mask(reg_base, I2CIP_INT_MASK_ERROR_MASK); i2cip_w_restart(reg_base, HAL_I2C_YES); i2cip_w_target_address(reg_base, out_task->target_addr); return 0; } uint32_t hal_i2c_sensor_engine_stop(enum HAL_I2C_ID_T id) { uint32_t reg_base; uint32_t irq_status; uint32_t abt_source; HAL_I2C_ERROR(2,"%s: id=%d", __func__, id); ASSERT(id < HAL_I2C_ID_NUM, invalid_id, id); if (hal_i2c_sm[id].cfg.mode != HAL_I2C_API_MODE_SENSOR_ENGINE) { HAL_I2C_TRACE(1,"i2c-se stop: not sensor engine mode: %d", hal_i2c_sm[id].cfg.mode); return HAL_I2C_ERRCODE_INV_PARAM; } if (hal_i2c_sm[id].state != HAL_I2C_SM_RUNNING) { HAL_I2C_TRACE(1,"i2c-se stop: not running: %d", hal_i2c_sm[id].state); return HAL_I2C_ERRCODE_INV_PARAM; } hal_sensor_engine_close(i2c_sensor_id[id]); hal_i2c_dma_release(id); reg_base = _i2c_get_base(id); HAL_I2C_TRACE(0,"disable i2c"); i2cip_w_enable(reg_base, HAL_I2C_NO); irq_status = i2cip_r_int_status(reg_base); abt_source = i2cip_r_tx_abrt_source(reg_base); _i2c_chk_clr_task_error(reg_base, irq_status, abt_source); #ifdef I2C_VAD i2cip_w_data_to_vad(reg_base, HAL_I2C_NO); #endif hal_i2c_sm_done(id); return 0; } #endif /* sensor engine mode end */ /* gpio iic mode */ #include "hal_gpio.h" static uint8_t g_i2c_open = false; #define DURATION_INIT_1 600 #define DURATION_INIT_2 600 #define DURATION_INIT_3 600 #define DURATION_START_1 600 #define DURATION_START_2 600 #define DURATION_START_3 600 #define DURATION_STOP_1 800 #define DURATION_STOP_2 600 #define DURATION_STOP_3 1300 #define DURATION_HIGH 900 #define DURATION_LOW 600 #define HAL_GPIO_I2C_DELAY(duration) hal_sys_timer_delay_ns(duration); struct HAL_GPIO_I2C_CONFIG_T hal_gpio_i2c_cfg; inline void GPIO_InitIO(uint8_t port, uint8_t direction, uint8_t val_for_out) { hal_gpio_pin_set_dir((enum HAL_GPIO_PIN_T)port, (enum HAL_GPIO_DIR_T)direction, val_for_out); } inline void GPIO_WriteIO(uint8_t port, uint8_t data) { if(data){ hal_gpio_pin_set((enum HAL_GPIO_PIN_T)port); }else{ hal_gpio_pin_clr((enum HAL_GPIO_PIN_T)port); } } inline uint8_t GPIO_ReadIO(uint8_t port) { uint8_t level = 0; level = hal_gpio_pin_get_val((enum HAL_GPIO_PIN_T)port); return level; } // //------------------------------------------------------------------- // Function: gpio_i2c_init // Purpose: This function is used to init I2C port of the device // In: // Return: bool //------------------------------------------------------------------- static int hal_gpio_i2c_initialize(const struct HAL_GPIO_I2C_CONFIG_T *cfg) { struct HAL_IOMUX_PIN_FUNCTION_MAP hal_gpio_i2c_iomux_cfg[] = { {HAL_IOMUX_PIN_NUM, HAL_IOMUX_FUNC_GPIO, HAL_IOMUX_PIN_VOLTAGE_VIO, HAL_IOMUX_PIN_PULLUP_ENABLE}, {HAL_IOMUX_PIN_NUM, HAL_IOMUX_FUNC_GPIO, HAL_IOMUX_PIN_VOLTAGE_VIO, HAL_IOMUX_PIN_PULLUP_ENABLE}, }; hal_gpio_i2c_cfg.scl = cfg->scl; hal_gpio_i2c_cfg.sda = cfg->sda; hal_gpio_i2c_iomux_cfg[0].pin = (enum HAL_IOMUX_PIN_T)hal_gpio_i2c_cfg.scl; hal_gpio_i2c_iomux_cfg[1].pin = (enum HAL_IOMUX_PIN_T)hal_gpio_i2c_cfg.sda; hal_iomux_init((struct HAL_IOMUX_PIN_FUNCTION_MAP *)hal_gpio_i2c_iomux_cfg, sizeof(hal_gpio_i2c_iomux_cfg)/sizeof(struct HAL_IOMUX_PIN_FUNCTION_MAP)); HAL_I2C_TRACE(2,"hal_gpio_i2c_initialize scl=%d sda=%d", hal_gpio_i2c_cfg.scl , hal_gpio_i2c_cfg.sda); //iTemp = hal_gpio_i2c_cfg.scl | hal_gpio_i2c_cfg.sda ; // Set the GPIO pin to output status GPIO_InitIO(hal_gpio_i2c_cfg.scl, 1, 1); GPIO_InitIO(hal_gpio_i2c_cfg.sda, 1, 1); HAL_GPIO_I2C_DELAY(DURATION_INIT_1); // Make the I2C bus in idle status GPIO_WriteIO(hal_gpio_i2c_cfg.scl,1); HAL_GPIO_I2C_DELAY(DURATION_INIT_1); GPIO_WriteIO(hal_gpio_i2c_cfg.sda ,1); HAL_GPIO_I2C_DELAY(DURATION_INIT_1); for(byte i=0;i<30;i++) { GPIO_WriteIO(hal_gpio_i2c_cfg.scl,0); /* low */ HAL_GPIO_I2C_DELAY(DURATION_LOW); GPIO_WriteIO(hal_gpio_i2c_cfg.scl,1); HAL_GPIO_I2C_DELAY(DURATION_HIGH); } return 0; } static void hal_gpio_i2c_start(void) /* start or re-start */ { GPIO_InitIO(hal_gpio_i2c_cfg.sda, 1, 1); GPIO_WriteIO(hal_gpio_i2c_cfg.sda ,1); GPIO_WriteIO(hal_gpio_i2c_cfg.scl,1); HAL_GPIO_I2C_DELAY(DURATION_START_1); GPIO_WriteIO(hal_gpio_i2c_cfg.sda ,0); HAL_GPIO_I2C_DELAY(DURATION_START_2); GPIO_WriteIO(hal_gpio_i2c_cfg.scl,0); HAL_GPIO_I2C_DELAY(DURATION_START_3);/* start condition */ } static void hal_gpio_i2c_stop(void) { GPIO_WriteIO(hal_gpio_i2c_cfg.scl,0); HAL_GPIO_I2C_DELAY(DURATION_LOW); GPIO_InitIO(hal_gpio_i2c_cfg.sda, 1, 0); GPIO_WriteIO(hal_gpio_i2c_cfg.sda ,0); HAL_GPIO_I2C_DELAY(DURATION_STOP_1); GPIO_WriteIO(hal_gpio_i2c_cfg.scl,1); HAL_GPIO_I2C_DELAY(DURATION_STOP_2); GPIO_WriteIO(hal_gpio_i2c_cfg.sda ,1); /* stop condition */ HAL_GPIO_I2C_DELAY(DURATION_STOP_3); } static uint8_t hal_gpio_i2c_txbyte(uint8_t data) /* return 0 --> ack */ { int i; uint8_t temp_value = 0; for(i=7; (i>=0)&&(i<=7); i--) { GPIO_WriteIO(hal_gpio_i2c_cfg.scl,0); /* low */ HAL_GPIO_I2C_DELAY(DURATION_LOW); if(i==7)GPIO_InitIO(hal_gpio_i2c_cfg.sda, 1, 0); HAL_GPIO_I2C_DELAY(DURATION_LOW); GPIO_WriteIO(hal_gpio_i2c_cfg.sda ,((data>>i)&0x01)); HAL_GPIO_I2C_DELAY(DURATION_LOW/2); GPIO_WriteIO(hal_gpio_i2c_cfg.scl, 1); /* high */ HAL_GPIO_I2C_DELAY(DURATION_HIGH); } GPIO_WriteIO(hal_gpio_i2c_cfg.scl,0); /* low */ HAL_GPIO_I2C_DELAY(DURATION_LOW); GPIO_InitIO(hal_gpio_i2c_cfg.sda, 0, 1);/* input */ HAL_GPIO_I2C_DELAY(DURATION_LOW/2); GPIO_WriteIO(hal_gpio_i2c_cfg.scl,1); /* high */ HAL_GPIO_I2C_DELAY(DURATION_HIGH); temp_value = GPIO_ReadIO(hal_gpio_i2c_cfg.sda ); GPIO_WriteIO(hal_gpio_i2c_cfg.scl,0); /* low */ HAL_GPIO_I2C_DELAY(DURATION_LOW); return temp_value; } static void hal_gpio_i2c_rxbyte(uint8_t *data, uint8_t ack) { int i; uint32_t dataCache; dataCache = 0; for(i=7; (i>=0)&&(i<=7); i--) { GPIO_WriteIO(hal_gpio_i2c_cfg.scl,0); HAL_GPIO_I2C_DELAY(DURATION_LOW); if(i==7)GPIO_InitIO(hal_gpio_i2c_cfg.sda, 0, 1); HAL_GPIO_I2C_DELAY(DURATION_LOW); GPIO_WriteIO(hal_gpio_i2c_cfg.scl,1); HAL_GPIO_I2C_DELAY(DURATION_HIGH); dataCache |= (GPIO_ReadIO(hal_gpio_i2c_cfg.sda )<> 7)); hal_gpio_i2c_txbyte(device_addr & 0xFF); } else { hal_gpio_i2c_txbyte((device_addr & 0x7F) << 1); } for (i = 0; i < tx_len; i++, tx_buf++) { hal_gpio_i2c_txbyte(*tx_buf); } hal_gpio_i2c_stop(); return 0; } uint32_t hal_gpio_i2c_simple_recv(uint32_t device_addr, const uint8_t *tx_buf, uint16_t tx_len, uint8_t *rx_buf, uint16_t rx_len) { uint8_t tempdata; uint32_t i; if (tx_len) { hal_gpio_i2c_start(); if (device_addr & HAL_I2C_10BITADDR_MASK) { hal_gpio_i2c_txbyte(((device_addr & 0x200) >> 7) | 0xF0); hal_gpio_i2c_txbyte(device_addr & 0xFF); } else { hal_gpio_i2c_txbyte((device_addr & 0x7F) << 1); } for (i = 0; i < tx_len; i++, tx_buf++) { hal_gpio_i2c_txbyte(*tx_buf); } } hal_gpio_i2c_start(); if (device_addr & HAL_I2C_10BITADDR_MASK) { hal_gpio_i2c_txbyte(((device_addr & 0x200) >> 7) | 0xF1); } else { hal_gpio_i2c_txbyte(((device_addr & 0x7F) << 1) | 1); } for (i = 0; i < rx_len; i++, rx_buf++) { hal_gpio_i2c_rxbyte(&tempdata, (i == rx_len - 1)); *rx_buf = tempdata; } hal_gpio_i2c_stop(); return 0; } #define touch_ic_device_addr 0x60 unsigned char I2C_WriteByte(unsigned char reg, unsigned char data) { unsigned char result; unsigned char buff[2]; buff[0] = reg; buff[1] = data; result = hal_gpio_i2c_simple_send((unsigned char)touch_ic_device_addr,buff,2); return result; } unsigned char I2C_ReadByte(unsigned char reg, unsigned char* data) { unsigned char result; result = hal_gpio_i2c_simple_recv((unsigned char)touch_ic_device_addr,®,1,data,1); return result; } int hal_gpio_i2c_open(const struct HAL_GPIO_I2C_CONFIG_T *cfg) { static bool i2cInitDone = false; int lock; if (g_i2c_open == true) { return -1; } lock = int_lock(); g_i2c_open = 0; int_unlock(lock); if (!i2cInitDone) { hal_gpio_i2c_initialize(cfg); i2cInitDone = 0; } return 0; } int hal_gpio_i2c_close(void) { int lock; if (g_i2c_open == false) { return false; } lock = int_lock(); g_i2c_open = false; int_unlock(lock); return 0; } uint32_t hal_gpio_i2c_send(uint32_t device_addr, const uint8_t *buf, uint32_t reg_len, uint32_t value_len) { return hal_gpio_i2c_simple_send(device_addr, buf, reg_len + value_len); } uint32_t hal_gpio_i2c_recv(uint32_t device_addr, uint8_t *buf, uint32_t reg_len, uint32_t value_len, uint8_t restart_after_write) { return hal_gpio_i2c_simple_recv(device_addr, buf, reg_len, buf+reg_len, value_len); } /* gpio iic end */ int app_i2c_demo_init(void) { static struct HAL_GPIO_I2C_CONFIG_T i2c_cfg={ HAL_GPIO_PIN_P2_0, HAL_GPIO_PIN_P2_1, 0, }; hal_gpio_i2c_open(&i2c_cfg); return 0; }