You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
720 lines
19 KiB
720 lines
19 KiB
/*
|
|
* Copyright (c) Hisilicon Technologies Co., Ltd. 2014-2021. All rights reserved.
|
|
* Description: ci hal func impl.
|
|
* Author: Hisilicon
|
|
* Create: 2014-08-02
|
|
*/
|
|
|
|
#include "soc_log.h"
|
|
#include "drv_gpio_ext.h"
|
|
#include "hal_ci.h"
|
|
#include "hal_ci_reg.h"
|
|
#include "osal_ext.h"
|
|
#include "soc_errno.h"
|
|
#include "drv_ci.h"
|
|
#include "linux/huanglong/securec.h"
|
|
|
|
#undef LOG_MODULE_ID
|
|
#define LOG_MODULE_ID SOC_ID_CI
|
|
|
|
#define CI_TIME_10MS 10 /* 10ms */
|
|
#define CI_PCCD_READY_COUNT 5
|
|
#define CI_PCCD_DETECT_MAX 5
|
|
#define CI_PCCD_DETECT_MIN 3
|
|
#define CI_PCCD_RESUME_ABSENT_COUNT 10
|
|
|
|
#define DAIE 0x80
|
|
#define FRIE 0x40
|
|
#define COM_STAT_REG 1
|
|
|
|
typedef enum {
|
|
CMD_TYPE_IOW = 0,
|
|
CMD_TYPE_IOR,
|
|
CMD_TYPE_MW,
|
|
CMD_TYPE_MR,
|
|
} cmd_type_list;
|
|
|
|
typedef enum {
|
|
EXT_CI_PCCD_RUN_STEP_RESUME,
|
|
EXT_CI_PCCD_RUN_STEP_STANDBY,
|
|
EXT_CI_PCCD_RUN_STEP_RUNNING,
|
|
EXT_CI_PCCD_RUN_STEP_CRAD_BUSY,
|
|
EXT_CI_PCCD_RUN_STEP_BUIT,
|
|
} ext_ci_pccd_run_step;
|
|
|
|
#define write_reg(addr, value) osal_writel((value), (addr))
|
|
#define read_reg(addr) osal_readl(addr)
|
|
#define ci_write_reg(offset, value) write_reg(g_ci_reg_base + (offset), value)
|
|
#define ci_read_reg(offset) read_reg(g_ci_reg_base + (offset))
|
|
#define crg_write_reg(offset, value) write_reg(g_crg_reg_base + (offset), value)
|
|
#define crg_read_reg(offset) read_reg(g_crg_reg_base + (offset))
|
|
#define peri_write_reg(offset, value) write_reg(g_peri_reg_base + (offset), value)
|
|
#define peri_read_reg(offset) read_reg(g_peri_reg_base + (offset))
|
|
|
|
#define EXT_CI_CHECK_PCCD_VALID(card) do { \
|
|
if ((card) >= EXT_CI_PCCD_MAX) { \
|
|
soc_log_err("invalid card id:%d.\n", card); \
|
|
return SOC_ERR_CI_INVALID_PARA; \
|
|
} \
|
|
} while (0)
|
|
|
|
typedef struct {
|
|
td_bool is_power_ctrl_gpio_used;
|
|
td_u32 power_ctrl_gpio_no[EXT_CI_PCCD_MAX];
|
|
td_bool ts_by_pass[EXT_CI_PCCD_MAX];
|
|
ext_ci_pccd_run_step run_step[EXT_CI_PCCD_MAX];
|
|
ext_ci_pccd_status status[EXT_CI_PCCD_MAX];
|
|
td_u32 resume_absent_cnt[EXT_CI_PCCD_MAX];
|
|
} ci_param;
|
|
|
|
static td_void *g_ci_reg_base = TD_NULL;
|
|
static td_void *g_crg_reg_base = TD_NULL;
|
|
static td_void *g_peri_reg_base = TD_NULL;
|
|
static gpio_ext_func* g_gpio_func = TD_NULL;
|
|
static ci_param g_ci_param[EXT_CI_PORT_MAX] = {{0}};
|
|
|
|
td_s32 ci_wait_done(td_u8 *data)
|
|
{
|
|
td_u32 time_out = 20000;
|
|
cmd0_rdata rdata;
|
|
|
|
if (data == TD_NULL) {
|
|
soc_err_print_err_code(SOC_ERR_CI_INVALID_PARA);
|
|
return SOC_ERR_CI_INVALID_PARA;
|
|
}
|
|
*data = 0x00;
|
|
rdata.u32 = ci_read_reg(CMD0_RDATA);
|
|
|
|
while (time_out > 0) {
|
|
rdata.u32 = ci_read_reg(CMD0_RDATA);
|
|
if (rdata.bits.done == 1) {
|
|
break;
|
|
}
|
|
time_out--;
|
|
}
|
|
|
|
if (time_out == 0) {
|
|
soc_log_err("wait data read timeout.\n");
|
|
return SOC_ERR_CI_TIMEOUT;
|
|
}
|
|
|
|
*data = rdata.bits.rdata;
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 ci_base_addr_remap(td_void)
|
|
{
|
|
td_u32 ci_phy_base_addr = 0;
|
|
td_s32 ret;
|
|
|
|
ret = osal_dts_get_u32_byname("huanglong,ci", "ci_phy_base_addr", &ci_phy_base_addr);
|
|
if (ret < 0) {
|
|
soc_log_err("CI get ci_phy_base_addr failed, ret[0x%x]\n", ret);
|
|
return TD_FAILURE;
|
|
}
|
|
g_ci_reg_base = (td_void *)osal_ioremap_nocache(ci_phy_base_addr, 0x1000);
|
|
if (g_ci_reg_base == TD_NULL) {
|
|
soc_log_err("CI reg base ioremap_nocache failed.\n");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
g_crg_reg_base = (td_void *)osal_ioremap_nocache(PERI_CRG_BASE, 0x1000);
|
|
if (g_crg_reg_base == TD_NULL) {
|
|
soc_log_err("CRG base ioremap_nocache failed.\n");
|
|
osal_iounmap(g_ci_reg_base, OSAL_IOUNMAP_SIZE);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
g_peri_reg_base = (td_void *)osal_ioremap_nocache(PERI_CTRL_BASE, 0x1000);
|
|
if (g_peri_reg_base == TD_NULL) {
|
|
soc_log_err("CRG base ioremap_nocache failed.\n");
|
|
osal_iounmap(g_ci_reg_base, OSAL_IOUNMAP_SIZE);
|
|
osal_iounmap(g_crg_reg_base, OSAL_IOUNMAP_SIZE);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_void ci_base_addr_unmap(td_void)
|
|
{
|
|
if (g_ci_reg_base != TD_NULL) {
|
|
osal_iounmap(g_ci_reg_base, OSAL_IOUNMAP_SIZE);
|
|
g_ci_reg_base = TD_NULL;
|
|
}
|
|
if (g_crg_reg_base != TD_NULL) {
|
|
osal_iounmap(g_crg_reg_base, OSAL_IOUNMAP_SIZE);
|
|
g_crg_reg_base = TD_NULL;
|
|
}
|
|
if (g_peri_reg_base != TD_NULL) {
|
|
osal_iounmap(g_peri_reg_base, OSAL_IOUNMAP_SIZE);
|
|
g_peri_reg_base = TD_NULL;
|
|
}
|
|
}
|
|
|
|
td_s32 ci_init(td_void)
|
|
{
|
|
td_s32 ret;
|
|
peri_crg98 crg98;
|
|
ci_inf_set inf_set;
|
|
cmd_cfg cfg;
|
|
slave_mode mode;
|
|
|
|
crg98.u32 = crg_read_reg(PERI_CRG98);
|
|
|
|
/* open clcok */
|
|
crg98.bits.ci_cken = 1;
|
|
crg_write_reg(PERI_CRG98, crg98.u32);
|
|
osal_msleep(1);
|
|
|
|
/* assert reset */
|
|
crg98.bits.ci_srst_req = 1;
|
|
crg_write_reg(PERI_CRG98, crg98.u32);
|
|
osal_msleep(1);
|
|
|
|
/* remove reset */
|
|
crg98.bits.ci_srst_req = 0;
|
|
crg_write_reg(PERI_CRG98, crg98.u32);
|
|
osal_msleep(1);
|
|
|
|
/* enable pin output */
|
|
inf_set.u32 = ci_read_reg(CI_INF_SET);
|
|
inf_set.bits.sw_com_oen = 0;
|
|
inf_set.bits.sw_card2_com_oen = 0;
|
|
inf_set.bits.sw_rst_oen = 0;
|
|
inf_set.bits.sw_card2_rst_oen = 0;
|
|
ci_write_reg(CI_INF_SET, inf_set.u32);
|
|
|
|
/* check ready single but don't check inpackn single */
|
|
cfg.u32 = ci_read_reg(CMD_CFG);
|
|
cfg.bits.inpackn_check = 0;
|
|
cfg.bits.rdy_check = 1;
|
|
ci_write_reg(CMD_CFG, cfg.u32);
|
|
|
|
/* indirect mode */
|
|
mode.u32 = ci_read_reg(SLAVE_MODE);
|
|
mode.bits.slave_mode = 0;
|
|
ci_write_reg(SLAVE_MODE, mode.u32);
|
|
|
|
ret = hal_ci_pccd_ts_by_pass(EXT_CI_PORT_0, EXT_CI_PCCD_A, TD_TRUE);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(hal_ci_pccd_ts_by_pass, ret);
|
|
return ret;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 hal_ci_init(td_void)
|
|
{
|
|
td_s32 ret;
|
|
td_u32 i;
|
|
|
|
ret = ci_base_addr_remap();
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ci_base_addr_remap, ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = ci_init();
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ci_init, ret);
|
|
return ret;
|
|
}
|
|
|
|
g_gpio_func = TD_NULL;
|
|
ret = osal_exportfunc_get(SOC_ID_GPIO, (td_void **)&g_gpio_func);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(osal_exportfunc_get, ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = memset_s(&g_ci_param, sizeof(g_ci_param), 0x00, sizeof(g_ci_param));
|
|
if (ret != EOK) {
|
|
return ret;
|
|
}
|
|
|
|
for (i = 0; i < EXT_CI_PCCD_MAX; i++) {
|
|
g_ci_param[EXT_CI_PORT_0].run_step[i] = EXT_CI_PCCD_RUN_STEP_RUNNING;
|
|
g_ci_param[EXT_CI_PORT_0].ts_by_pass[i] = TD_TRUE;
|
|
g_ci_param[EXT_CI_PORT_0].status[i] = EXT_CI_PCCD_STATUS_MAX;
|
|
g_ci_param[EXT_CI_PORT_0].is_power_ctrl_gpio_used = TD_FALSE;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_void hal_ci_deinit(td_void)
|
|
{
|
|
peri_crg98 crg98;
|
|
|
|
crg98.u32 = crg_read_reg(PERI_CRG98);
|
|
|
|
/* assert reset */
|
|
crg98.bits.ci_srst_req = 1;
|
|
crg_write_reg(PERI_CRG98, crg98.u32);
|
|
osal_msleep(1);
|
|
|
|
/* close clcok */
|
|
crg98.bits.ci_cken = 0;
|
|
crg_write_reg(PERI_CRG98, crg98.u32);
|
|
osal_msleep(1);
|
|
|
|
ci_base_addr_unmap();
|
|
|
|
return;
|
|
}
|
|
|
|
td_s32 hal_ci_device_open(ext_ci_port port)
|
|
{
|
|
if (port != EXT_CI_PORT_0) {
|
|
soc_log_err("only support EXT_CI_PORT_0 now.\n");
|
|
return SOC_ERR_CI_UNSUPPORT;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 hal_ci_device_close(ext_ci_port port)
|
|
{
|
|
if (port != EXT_CI_PORT_0) {
|
|
soc_log_err("only support EXT_CI_PORT_0 now.\n");
|
|
return SOC_ERR_CI_UNSUPPORT;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 hal_ci_set_attr(ext_ci_port port, ext_ci_attr attr)
|
|
{
|
|
if (port != EXT_CI_PORT_0) {
|
|
soc_log_err("only support EXT_CI_PORT_0 now.\n");
|
|
return SOC_ERR_CI_UNSUPPORT;
|
|
}
|
|
|
|
g_ci_param[port].is_power_ctrl_gpio_used = attr.dev_attr.extci.is_power_ctrl_gpio_used;
|
|
g_ci_param[port].power_ctrl_gpio_no[EXT_CI_PCCD_A] = attr.dev_attr.extci.power_ctrl_gpio_no[EXT_CI_PCCD_A];
|
|
g_ci_param[port].power_ctrl_gpio_no[EXT_CI_PCCD_B] = attr.dev_attr.extci.power_ctrl_gpio_no[EXT_CI_PCCD_B];
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 hal_ci_pccd_open(ext_ci_port port, ext_ci_pccd card)
|
|
{
|
|
if (port != EXT_CI_PORT_0) {
|
|
soc_log_err("only support EXT_CI_PORT_0 now.\n");
|
|
return SOC_ERR_CI_UNSUPPORT;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_void hal_ci_pccd_close(ext_ci_port port, ext_ci_pccd card)
|
|
{
|
|
}
|
|
|
|
static td_void hal_pccd_select(ext_ci_pccd card)
|
|
{
|
|
ci_inf_set inf_set;
|
|
|
|
inf_set.u32 = ci_read_reg(CI_INF_SET);
|
|
inf_set.bits.card2_sel = card;
|
|
ci_write_reg(CI_INF_SET, inf_set.u32);
|
|
}
|
|
|
|
static td_s32 ci_pccd_byte_rw(ext_ci_port port, ext_ci_pccd card, cmd_type_list type, td_u32 address, td_u8 *value)
|
|
{
|
|
td_s32 ret;
|
|
cmd0_set cmd;
|
|
td_u8 out = 0;
|
|
|
|
if (port != EXT_CI_PORT_0) {
|
|
soc_log_err("only support EXT_CI_PORT_0 now.\n");
|
|
return SOC_ERR_CI_UNSUPPORT;
|
|
}
|
|
EXT_CI_CHECK_PCCD_VALID(card);
|
|
|
|
if (g_ci_param[port].run_step[card] == EXT_CI_PCCD_RUN_STEP_RESUME) {
|
|
soc_log_err("Resume from standby, will re-insert CAM soon.\n");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
if (g_ci_param[port].run_step[card] == EXT_CI_PCCD_RUN_STEP_CRAD_BUSY) {
|
|
/* card is busy means that card may be dead,
|
|
* to avoid keep printing error messages,
|
|
* here just return 0 with TD_SUCCESS.
|
|
*/
|
|
*value = 0x00;
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
hal_pccd_select(card);
|
|
cmd.u32 = ci_read_reg(CMD0_SET);
|
|
cmd.bits.cmd_type = type;
|
|
cmd.bits.addr = address;
|
|
cmd.bits.wdata = *value;
|
|
ci_write_reg(CMD0_SET, cmd.u32);
|
|
|
|
/* wait done */
|
|
ret = ci_wait_done(&out);
|
|
if (ret != TD_SUCCESS) {
|
|
g_ci_param[port].run_step[card] = EXT_CI_PCCD_RUN_STEP_CRAD_BUSY;
|
|
soc_err_print_call_fun_err(ci_wait_done, ret);
|
|
return ret;
|
|
}
|
|
|
|
*value = out;
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 hal_ci_pccd_io_read_byte(ext_ci_port port, ext_ci_pccd card, td_u32 address, td_u8 *value)
|
|
{
|
|
return ci_pccd_byte_rw(port, card, CMD_TYPE_IOR, address, value);
|
|
}
|
|
|
|
td_s32 hal_ci_pccd_io_write_byte(ext_ci_port port, ext_ci_pccd card, td_u32 address, td_u8 value)
|
|
{
|
|
if (address == COM_STAT_REG) {
|
|
value |= (DAIE | FRIE);
|
|
}
|
|
return ci_pccd_byte_rw(port, card, CMD_TYPE_IOW, address, &value);
|
|
}
|
|
|
|
td_s32 hal_ci_pccd_mem_read_byte(ext_ci_port port, ext_ci_pccd card, td_u32 address, td_u8 *value)
|
|
{
|
|
return ci_pccd_byte_rw(port, card, CMD_TYPE_MR, address, value);
|
|
}
|
|
|
|
td_s32 hal_ci_pccd_mem_write_byte(ext_ci_port port, ext_ci_pccd card, td_u32 address, td_u8 value)
|
|
{
|
|
return ci_pccd_byte_rw(port, card, CMD_TYPE_MW, address, &value);
|
|
}
|
|
|
|
td_u32 ci_pccd0_detect_once(td_u32 cd2_dis)
|
|
{
|
|
dbg_in_sig in_sig;
|
|
|
|
in_sig.u32 = ci_read_reg(DBG_IN_SIG);
|
|
|
|
/* check cd1, 0: card present */
|
|
if (in_sig.bits.cd1 == 0x01) {
|
|
/* card0 absent */
|
|
return 0;
|
|
}
|
|
|
|
if (cd2_dis == 1) {
|
|
/* disregard cd2, direct return present when cd1 = 0 */
|
|
return 1;
|
|
}
|
|
|
|
if (in_sig.bits.cd2 == 0x00) {
|
|
/* card0 present */
|
|
return 1;
|
|
}
|
|
|
|
/* card0 absent */
|
|
return 0;
|
|
}
|
|
|
|
td_u32 ci_pccd1_detect_once(td_u32 cd2_dis)
|
|
{
|
|
ci_inf_set inf_set;
|
|
|
|
/* check cd1, 0: card present */
|
|
inf_set.u32 = ci_read_reg(CI_INF_SET);
|
|
if (inf_set.bits.card2_cd1_n == 0x01) {
|
|
/* card1 absent */
|
|
return 0;
|
|
}
|
|
|
|
if (cd2_dis == 1) {
|
|
/* disregard cd2, direct return present when cd1 = 0 */
|
|
return 1;
|
|
}
|
|
|
|
if (inf_set.bits.card2_cd2_n == 0x00) {
|
|
/* card1 present */
|
|
return 1;
|
|
}
|
|
|
|
/* card1 absent */
|
|
return 0;
|
|
}
|
|
|
|
td_s32 hal_ci_pccd_detect(ext_ci_port port, ext_ci_pccd card, ext_ci_pccd_status* status)
|
|
{
|
|
td_u32 try_count;
|
|
td_u32 present_count = 0;
|
|
ci_inf_cmd_mode cmd_mode;
|
|
|
|
if (port != EXT_CI_PORT_0) {
|
|
soc_log_err("only support EXT_CI_PORT_0 now.\n");
|
|
return SOC_ERR_CI_UNSUPPORT;
|
|
}
|
|
EXT_CI_CHECK_PCCD_VALID(card);
|
|
if (status == TD_NULL) {
|
|
soc_err_print_err_code(SOC_ERR_CI_INVALID_PARA);
|
|
return SOC_ERR_CI_INVALID_PARA;
|
|
}
|
|
|
|
if (g_ci_param[port].run_step[card] == EXT_CI_PCCD_RUN_STEP_RESUME) {
|
|
/* assert card absent after resume to force a higher level CAM initialization */
|
|
*status = EXT_CI_PCCD_STATUS_ABSENT;
|
|
|
|
if (g_ci_param[port].resume_absent_cnt[card] == 0) {
|
|
g_ci_param[port].run_step[card] = EXT_CI_PCCD_RUN_STEP_RUNNING;
|
|
} else {
|
|
g_ci_param[port].resume_absent_cnt[card] -= 1;
|
|
}
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
cmd_mode.u32 = ci_read_reg(CI_INF_CMD_MODE);
|
|
for (try_count = 0; try_count < CI_PCCD_DETECT_MAX; try_count++) {
|
|
if (card == EXT_CI_PCCD_A) {
|
|
present_count += ci_pccd0_detect_once(cmd_mode.bits.cd2_dis);
|
|
} else {
|
|
present_count += ci_pccd1_detect_once(cmd_mode.bits.cd2_dis);
|
|
}
|
|
osal_msleep(CI_TIME_10MS);
|
|
}
|
|
|
|
if (present_count >= CI_PCCD_DETECT_MIN) {
|
|
*status = EXT_CI_PCCD_STATUS_PRESENT;
|
|
} else {
|
|
*status = EXT_CI_PCCD_STATUS_ABSENT;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 hal_ci_pccd_ready_or_busy(ext_ci_port port, ext_ci_pccd card, ext_ci_pccd_ready* ready)
|
|
{
|
|
td_u32 elapsed_time;
|
|
dbg_in_sig in_sig;
|
|
|
|
if (port != EXT_CI_PORT_0) {
|
|
soc_log_err("only support EXT_CI_PORT_0 now.\n");
|
|
return SOC_ERR_CI_UNSUPPORT;
|
|
}
|
|
EXT_CI_CHECK_PCCD_VALID(card);
|
|
if (ready == TD_NULL) {
|
|
soc_err_print_err_code(SOC_ERR_CI_INVALID_PARA);
|
|
return SOC_ERR_CI_INVALID_PARA;
|
|
}
|
|
*ready = EXT_CI_PCCD_BUSY;
|
|
|
|
/* the host shall explicitly check for the READY signal until it is set by the
|
|
* module or until a timeout of 5s has expired.
|
|
*/
|
|
for (elapsed_time = 0; elapsed_time < CI_PCCD_READY_COUNT; elapsed_time++) {
|
|
in_sig.u32 = ci_read_reg(DBG_IN_SIG);
|
|
|
|
if (card == EXT_CI_PCCD_A) {
|
|
if (in_sig.bits.rdy == 1) {
|
|
*ready = EXT_CI_PCCD_READY;
|
|
break;
|
|
}
|
|
} else {
|
|
if (in_sig.bits.card2_ready == 1) {
|
|
*ready = EXT_CI_PCCD_READY;
|
|
break;
|
|
}
|
|
}
|
|
osal_msleep(CI_TIME_10MS);
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 hal_ci_pccd_reset(ext_ci_port port, ext_ci_pccd card)
|
|
{
|
|
ci_inf_set inf_set;
|
|
|
|
if (port != EXT_CI_PORT_0) {
|
|
soc_log_err("only support EXT_CI_PORT_0 now.\n");
|
|
return SOC_ERR_CI_UNSUPPORT;
|
|
}
|
|
EXT_CI_CHECK_PCCD_VALID(card);
|
|
|
|
inf_set.u32 = ci_read_reg(CI_INF_SET);
|
|
|
|
if (card == EXT_CI_PCCD_A) {
|
|
inf_set.bits.set_ci_reset = 1;
|
|
ci_write_reg(CI_INF_SET, inf_set.u32);
|
|
osal_msleep(CI_TIME_10MS);
|
|
|
|
inf_set.bits.set_ci_reset = 0;
|
|
ci_write_reg(CI_INF_SET, inf_set.u32);
|
|
osal_msleep(CI_TIME_10MS);
|
|
} else {
|
|
inf_set.bits.set_card2_reset = 1;
|
|
ci_write_reg(CI_INF_SET, inf_set.u32);
|
|
osal_msleep(CI_TIME_10MS);
|
|
|
|
inf_set.bits.set_card2_reset = 0;
|
|
ci_write_reg(CI_INF_SET, inf_set.u32);
|
|
osal_msleep(CI_TIME_10MS);
|
|
}
|
|
|
|
/* enter running state after reset */
|
|
g_ci_param[EXT_CI_PORT_0].run_step[card] = EXT_CI_PCCD_RUN_STEP_RUNNING;
|
|
|
|
soc_log_info("reset CAM OK!\n");
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* power ON/OFF
|
|
* notice: Current solution only support power control on CI port, but not for each PCCD.
|
|
* so, if you call power off but some cards are present, will return SOC_ERR_CI_CANNOT_POWEROFF.
|
|
*/
|
|
td_s32 hal_ci_pccd_ctrl_power(ext_ci_port port, ext_ci_pccd card, ext_ci_pccd_ctrl_power ctrl_power)
|
|
{
|
|
td_s32 ret, ret_bak;
|
|
td_u32 gpio_no;
|
|
td_u32 gpio_val;
|
|
|
|
if (port != EXT_CI_PORT_0) {
|
|
soc_log_err("only support EXT_CI_PORT_0 now.\n");
|
|
return SOC_ERR_CI_UNSUPPORT;
|
|
}
|
|
|
|
EXT_CI_CHECK_PCCD_VALID(card);
|
|
|
|
if (g_ci_param[port].is_power_ctrl_gpio_used == TD_FALSE) {
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
if ((g_gpio_func == TD_NULL) || (g_gpio_func->pfn_gpio_direction_set_bit == TD_NULL)
|
|
|| (g_gpio_func->pfn_gpio_write_bit == TD_NULL)) {
|
|
soc_log_err("gpio func invalid.\n");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
gpio_no = g_ci_param[port].power_ctrl_gpio_no[card];
|
|
gpio_val = (ctrl_power == EXT_CI_PCCD_CTRLPOWER_ON ? 1 : 0);
|
|
|
|
ret = g_gpio_func->pfn_gpio_direction_set_bit(gpio_no, 0);
|
|
osal_msleep(1);
|
|
ret_bak = g_gpio_func->pfn_gpio_write_bit(gpio_no, gpio_val);
|
|
ret = (td_s32)((td_u32)ret | (td_u32)ret_bak);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("power off device fail, GPIO.\n");
|
|
soc_err_print_h32(gpio_no);
|
|
return ret;
|
|
}
|
|
|
|
osal_msleep(CI_TIME_10MS);
|
|
soc_log_info("power card %d, gpio_no 0x%02x, gpio_val %d\n", card, gpio_no, gpio_val);
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 hal_ci_pccd_ts_by_pass(ext_ci_port port, ext_ci_pccd card, td_bool by_pass)
|
|
{
|
|
peri_io_oen io_oen;
|
|
|
|
if (port != EXT_CI_PORT_0) {
|
|
soc_log_err("only support EXT_CI_PORT_0 now.\n");
|
|
return SOC_ERR_CI_UNSUPPORT;
|
|
}
|
|
EXT_CI_CHECK_PCCD_VALID(card);
|
|
|
|
io_oen.u32 = peri_read_reg(PERI_IO_OEN);
|
|
|
|
if (card == EXT_CI_PCCD_A) {
|
|
io_oen.bits.peri_tso_loop_sel = (by_pass == TD_TRUE) ? 1 : 0;
|
|
peri_write_reg(PERI_IO_OEN, io_oen.u32);
|
|
} else if (card == EXT_CI_PCCD_B) {
|
|
/*
|
|
* tuner->demod->card_b->TSI1->demux->decode
|
|
* card B direct link to demod chip, so can't bypass the TS.
|
|
*/
|
|
soc_log_err("card_id == EXT_CI_PCCD_B\n");
|
|
} else {
|
|
soc_log_err("only support card A and card B.\n");
|
|
return SOC_ERR_CI_UNSUPPORT;
|
|
}
|
|
|
|
g_ci_param[port].ts_by_pass[card] = by_pass;
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 hal_ci_pccd_get_bypass_mode(ext_ci_port port, ext_ci_pccd card, td_bool *bypass)
|
|
{
|
|
peri_io_oen io_oen;
|
|
|
|
if (bypass == TD_NULL) {
|
|
soc_err_print_err_code(SOC_ERR_CI_INVALID_PARA);
|
|
return SOC_ERR_CI_INVALID_PARA;
|
|
}
|
|
|
|
if (card == EXT_CI_PCCD_A) {
|
|
io_oen.u32 = peri_read_reg(PERI_IO_OEN);
|
|
*bypass = (io_oen.bits.peri_tso_loop_sel == 1) ? TD_TRUE : TD_FALSE;
|
|
} else {
|
|
*bypass = TD_FALSE;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
/* low power */
|
|
td_s32 hal_ci_standby(ext_ci_port port)
|
|
{
|
|
td_s32 ret;
|
|
ext_ci_pccd card = 0;
|
|
|
|
if (port != EXT_CI_PORT_0) {
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
for (card = EXT_CI_PCCD_A; card < EXT_CI_PCCD_MAX; card++) {
|
|
g_ci_param[port].run_step[card] = EXT_CI_PCCD_RUN_STEP_STANDBY;
|
|
ret = hal_ci_pccd_ctrl_power(port, card, EXT_CI_PCCD_CTRLPOWER_OFF);
|
|
if (ret != TD_SUCCESS) {
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
/* resume CI */
|
|
td_s32 hal_ci_resume(ext_ci_port port)
|
|
{
|
|
td_s32 ret;
|
|
ext_ci_pccd card = 0;
|
|
|
|
if (port != EXT_CI_PORT_0) {
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
ret = ci_init();
|
|
if (ret != TD_SUCCESS) {
|
|
return ret;
|
|
}
|
|
|
|
for (card = EXT_CI_PCCD_A; card < EXT_CI_PCCD_MAX; card++) {
|
|
/* during standby CAM may be always power on,
|
|
* but host CI is power down,
|
|
* so the CAM maybe died as not receive any respond from host
|
|
* we need to reset CAM after resume.
|
|
*/
|
|
ret = hal_ci_pccd_reset(port, card);
|
|
if (ret != TD_SUCCESS) {
|
|
return ret;
|
|
}
|
|
g_ci_param[port].run_step[card] = EXT_CI_PCCD_RUN_STEP_RESUME;
|
|
g_ci_param[port].resume_absent_cnt[card] = CI_PCCD_RESUME_ABSENT_COUNT;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|