352 lines
7.9 KiB
C
352 lines
7.9 KiB
C
// Copyright 2020-2021 Beken
|
|
//
|
|
// Licensed 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.
|
|
|
|
#include <driver/int.h>
|
|
#include <os/mem.h>
|
|
#include <driver/qspi.h>
|
|
#include "bk_drv_model.h"
|
|
#include "bk_sys_ctrl.h"
|
|
#include "clock_driver.h"
|
|
#include "gpio_driver.h"
|
|
#include "power_driver.h"
|
|
#include "qspi_driver.h"
|
|
#include "qspi_statis.h"
|
|
#include "sys_driver.h"
|
|
#include <driver/gpio.h>
|
|
|
|
static qspi_driver_t s_qspi[SOC_QSPI_UNIT_NUM] = {
|
|
{
|
|
.hal.hw = (qspi_hw_t *)(SOC_QSPI0_REG_BASE),
|
|
},
|
|
#if (SOC_QSPI_UNIT_NUM > 1)
|
|
{
|
|
.hal.hw = (qspi_hw_t *)(SOC_QSPI1_REG_BASE),
|
|
}
|
|
#endif
|
|
};
|
|
|
|
#define QSPI_RETURN_ON_NOT_INIT() do {\
|
|
if (!s_qspi_driver_is_init) {\
|
|
QSPI_LOGE("QSPI driver not init\r\n");\
|
|
return BK_ERR_QSPI_NOT_INIT;\
|
|
}\
|
|
} while(0)
|
|
|
|
#define QSPI_RETURN_ON_ID_NOT_INIT(id) do {\
|
|
if (!s_qspi[id].id_init_bits) {\
|
|
QSPI_LOGE("QSPI not init\r\n");\
|
|
return BK_ERR_QSPI_ID_NOT_INIT;\
|
|
}\
|
|
} while(0)
|
|
|
|
|
|
static bool s_qspi_driver_is_init = false;
|
|
static qspi_callback_t s_qspi_tx_isr = {NULL};
|
|
static qspi_callback_t s_qspi_rx_isr = {NULL};
|
|
|
|
|
|
#define QSPI_SET_PIN(id) do {\
|
|
gpio_dev_unmap(QSPI##id##_LL_CSN_PIN);\
|
|
gpio_dev_unmap(QSPI##id##_LL_CLK_PIN);\
|
|
gpio_dev_unmap(QSPI##id##_LL_IO0_PIN);\
|
|
gpio_dev_unmap(QSPI##id##_LL_IO1_PIN);\
|
|
gpio_dev_unmap(QSPI##id##_LL_IO2_PIN);\
|
|
gpio_dev_unmap(QSPI##id##_LL_IO3_PIN);\
|
|
gpio_dev_map(QSPI##id##_LL_CSN_PIN, GPIO_DEV_QSPI##id##_CSN);\
|
|
gpio_dev_map(QSPI##id##_LL_CLK_PIN, GPIO_DEV_QSPI##id##_CLK);\
|
|
gpio_dev_map(QSPI##id##_LL_IO0_PIN, GPIO_DEV_QSPI##id##_IO0);\
|
|
gpio_dev_map(QSPI##id##_LL_IO1_PIN, GPIO_DEV_QSPI##id##_IO1);\
|
|
gpio_dev_map(QSPI##id##_LL_IO2_PIN, GPIO_DEV_QSPI##id##_IO2);\
|
|
gpio_dev_map(QSPI##id##_LL_IO3_PIN, GPIO_DEV_QSPI##id##_IO3);\
|
|
bk_gpio_set_capacity(QSPI##id##_LL_CSN_PIN, 3);\
|
|
bk_gpio_set_capacity(QSPI##id##_LL_CLK_PIN, 3);\
|
|
} while(0)
|
|
|
|
|
|
static void qspi_init_gpio(qspi_id_t id)
|
|
{
|
|
switch (id) {
|
|
case QSPI_ID_0:
|
|
QSPI_SET_PIN(0);
|
|
break;
|
|
#if (SOC_QSPI_UNIT_NUM > 1)
|
|
case QSPI_ID_1:
|
|
QSPI_SET_PIN(1);
|
|
break;
|
|
#endif
|
|
#if (SOC_QSPI_UNIT_NUM > 2)
|
|
case QSPI_ID_2:
|
|
QSPI_SET_PIN(2);
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void qspi_clock_enable(qspi_id_t id)
|
|
{
|
|
switch(id)
|
|
{
|
|
case QSPI_ID_0:
|
|
sys_drv_dev_clk_pwr_up(CLK_PWR_ID_QSPI_1, CLK_PWR_CTRL_PWR_UP);
|
|
break;
|
|
#if (SOC_QSPI_UNIT_NUM > 1)
|
|
case QSPI_ID_1:
|
|
sys_drv_dev_clk_pwr_up(CLK_PWR_ID_QSPI_2, CLK_PWR_CTRL_PWR_UP);
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void qspi_clock_disable(qspi_id_t id)
|
|
{
|
|
switch(id)
|
|
{
|
|
case QSPI_ID_0:
|
|
sys_drv_dev_clk_pwr_up(CLK_PWR_ID_QSPI_1, CLK_PWR_CTRL_PWR_DOWN);
|
|
break;
|
|
#if (SOC_QSPI_UNIT_NUM > 1)
|
|
case QSPI_ID_1:
|
|
sys_drv_dev_clk_pwr_up(CLK_PWR_ID_QSPI_2, CLK_PWR_CTRL_PWR_DOWN);
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void qspi_interrupt_enable(qspi_id_t id)
|
|
{
|
|
switch(id)
|
|
{
|
|
case QSPI_ID_0:
|
|
sys_drv_int_enable(QSPI0_INTERRUPT_CTRL_BIT);
|
|
break;
|
|
#if (SOC_QSPI_UNIT_NUM > 1)
|
|
case QSPI_ID_1:
|
|
sys_drv_int_enable(QSPI1_INTERRUPT_CTRL_BIT);
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void qspi_interrupt_disable(qspi_id_t id)
|
|
{
|
|
switch(id)
|
|
{
|
|
case QSPI_ID_0:
|
|
sys_drv_int_disable(QSPI0_INTERRUPT_CTRL_BIT);
|
|
break;
|
|
#if (SOC_QSPI_UNIT_NUM > 1)
|
|
case QSPI_ID_1:
|
|
sys_drv_int_disable(QSPI1_INTERRUPT_CTRL_BIT);
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 1. set clock
|
|
* 2. set gpio as qspi
|
|
* 3. enable interrupt(fiq_int_enable)
|
|
*/
|
|
static void qspi_id_init_common(qspi_id_t id)
|
|
{
|
|
qspi_clock_enable(id);
|
|
qspi_init_gpio(id);
|
|
qspi_interrupt_enable(id);
|
|
|
|
qspi_hal_init_common(&s_qspi[id].hal);
|
|
}
|
|
|
|
static void qspi_id_deinit_common(qspi_id_t id)
|
|
{
|
|
qspi_hal_deinit_common(&s_qspi[id].hal);
|
|
qspi_interrupt_disable(id);
|
|
qspi_clock_disable(id);
|
|
}
|
|
|
|
#if (!CONFIG_SYSTEM_CTRL)
|
|
static void qspi_isr(void);
|
|
#endif
|
|
|
|
bk_err_t bk_qspi_driver_init(void)
|
|
{
|
|
if (s_qspi_driver_is_init) {
|
|
return BK_OK;
|
|
}
|
|
|
|
os_memset(&s_qspi, 0, sizeof(s_qspi));
|
|
for (int id = QSPI_ID_0; id < QSPI_ID_MAX; id++) {
|
|
s_qspi[id].hal.id = id;
|
|
qspi_hal_init(&s_qspi[id].hal);
|
|
}
|
|
|
|
qspi_statis_init();
|
|
s_qspi_driver_is_init = true;
|
|
|
|
return BK_OK;
|
|
}
|
|
|
|
bk_err_t bk_qspi_driver_deinit(void)
|
|
{
|
|
if (!s_qspi_driver_is_init) {
|
|
return BK_OK;
|
|
}
|
|
|
|
for (int id = QSPI_ID_0; id < QSPI_ID_MAX; id++) {
|
|
qspi_id_deinit_common(id);
|
|
}
|
|
|
|
s_qspi_driver_is_init = false;
|
|
|
|
return BK_OK;
|
|
}
|
|
|
|
bk_err_t bk_qspi_init(qspi_id_t id, const qspi_config_t *config)
|
|
{
|
|
BK_RETURN_ON_NULL(config);
|
|
QSPI_RETURN_ON_NOT_INIT();
|
|
|
|
if (id == QSPI_ID_0) {
|
|
bk_pm_module_vote_power_ctrl(PM_POWER_SUB_MODULE_NAME_AHBP_QSPI, PM_POWER_MODULE_STATE_ON);
|
|
#if (SOC_QSPI_UNIT_NUM > 1)
|
|
} else if (id == QSPI_ID_1) {
|
|
bk_pm_module_vote_power_ctrl(PM_POWER_SUB_MODULE_NAME_AHBP_QSPI1, PM_POWER_MODULE_STATE_ON);
|
|
#endif
|
|
}
|
|
|
|
qspi_id_init_common(id);
|
|
switch (config->src_clk) {
|
|
case QSPI_SCLK_320M:
|
|
sys_drv_qspi_clk_sel(id, QSPI_CLK_320M);
|
|
break;
|
|
case QSPI_SCLK_480M:
|
|
sys_drv_qspi_clk_sel(id, QSPI_CLK_480M);
|
|
break;
|
|
default:
|
|
sys_drv_qspi_clk_sel(id, QSPI_CLK_480M);
|
|
break;
|
|
}
|
|
sys_drv_qspi_set_src_clk_div(id, config->src_clk_div);
|
|
qspi_hal_set_clk_div(&s_qspi[id].hal, config->clk_div);
|
|
s_qspi[id].id_init_bits |= BIT(0);
|
|
return BK_OK;
|
|
}
|
|
|
|
bk_err_t bk_qspi_deinit(qspi_id_t id)
|
|
{
|
|
qspi_id_deinit_common(id);
|
|
s_qspi[id].id_init_bits &= ~BIT(0);
|
|
|
|
if (id == QSPI_ID_0) {
|
|
bk_pm_module_vote_power_ctrl(PM_POWER_SUB_MODULE_NAME_AHBP_QSPI, PM_POWER_MODULE_STATE_OFF);
|
|
#if (SOC_QSPI_UNIT_NUM > 1)
|
|
} else if (id == QSPI_ID_1) {
|
|
bk_pm_module_vote_power_ctrl(PM_POWER_SUB_MODULE_NAME_AHBP_QSPI1, PM_POWER_MODULE_STATE_OFF);
|
|
#endif
|
|
}
|
|
|
|
return BK_OK;
|
|
}
|
|
|
|
bk_err_t bk_qspi_command(qspi_id_t id, const qspi_cmd_t *cmd)
|
|
{
|
|
BK_RETURN_ON_NULL(cmd);
|
|
QSPI_RETURN_ON_ID_NOT_INIT(id);
|
|
qspi_hal_command(&s_qspi[id].hal, cmd);
|
|
return BK_OK;
|
|
}
|
|
|
|
bk_err_t bk_qspi_write(qspi_id_t id, const void *data, uint32_t size)
|
|
{
|
|
BK_RETURN_ON_NULL(data);
|
|
|
|
qspi_hal_io_write(&s_qspi[id].hal, data, size);
|
|
|
|
return BK_OK;
|
|
}
|
|
|
|
bk_err_t bk_qspi_read(qspi_id_t id, void *data, uint32_t size)
|
|
{
|
|
BK_RETURN_ON_NULL(data);
|
|
|
|
qspi_hal_io_read(&s_qspi[id].hal, data, size);
|
|
|
|
return BK_OK;
|
|
}
|
|
|
|
bk_err_t bk_qspi_register_tx_isr(qspi_isr_t isr, void *param)
|
|
{
|
|
uint32_t int_level = rtos_enter_critical();
|
|
s_qspi_tx_isr.callback = isr;
|
|
s_qspi_tx_isr.param = param;
|
|
rtos_exit_critical(int_level);
|
|
return BK_OK;
|
|
}
|
|
|
|
bk_err_t bk_qspi_register_rx_isr(qspi_isr_t isr, void *param)
|
|
{
|
|
uint32_t int_level = rtos_enter_critical();
|
|
s_qspi_rx_isr.callback = isr;
|
|
s_qspi_rx_isr.param = param;
|
|
rtos_exit_critical(int_level);
|
|
return BK_OK;
|
|
}
|
|
|
|
#if (!CONFIG_SYSTEM_CTRL)
|
|
static void qspi_isr(void)
|
|
{
|
|
qspi_hal_t *hal = &s_qspi[id].hal;
|
|
uint32_t int_status = 0;
|
|
QSPI_STATIS_DEC();
|
|
QSPI_STATIS_GET(qspi_statis);
|
|
QSPI_STATIS_INC(qspi_statis->qspi_int_cnt);
|
|
|
|
int_status = qspi_hal_get_interrupt_status_before_mask(hal);
|
|
QSPI_LOGD("int_status before mask:%x\r\n", int_status);
|
|
|
|
int_status = qspi_hal_get_interrupt_status_after_mask(hal);
|
|
QSPI_LOGD("int_status after mask:%x\r\n", int_status);
|
|
qspi_hal_clear_interrupt_status(hal, int_status);
|
|
|
|
if (qspi_hal_is_sw_op_int_triggered(hal, int_status)) {
|
|
QSPI_STATIS_INC(qspi_statis->sw_op_int_cnt);
|
|
QSPI_LOGD("sw op int triggered\r\n");
|
|
qspi_hal_clear_sw_op_int(hal);
|
|
qspi_hal_stop_sw_op(hal);
|
|
if (qspi_hal_is_cur_sw_op_write_data()) {
|
|
qspi_hal_disable_ge0_tx(hal);
|
|
if (s_qspi_tx_isr.callback) {
|
|
s_qspi_tx_isr.callback(0, s_qspi_tx_isr.param);
|
|
}
|
|
}
|
|
|
|
if (qspi_hal_is_cur_sw_op_read_data()) {
|
|
qspi_hal_disable_ge1_rx(hal);
|
|
if (s_qspi_rx_isr.callback) {
|
|
s_qspi_rx_isr.callback(0, s_qspi_rx_isr.param);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|