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.
1924 lines
60 KiB
1924 lines
60 KiB
/*
|
|
* Copyright (c) Hisilicon Technologies Co., Ltd. 2014-2021. All rights reserved.
|
|
* Description: ci drv func impl.
|
|
* Author: Hisilicon
|
|
* Create: 2014-08-02
|
|
*/
|
|
|
|
#include "drv_ci.h"
|
|
#include "soc_log.h"
|
|
#include "hal_ci.h"
|
|
#include "osal_ext.h"
|
|
#include "linux/huanglong/securec.h"
|
|
|
|
#undef LOG_MODULE_ID
|
|
#define LOG_MODULE_ID SOC_ID_CI
|
|
|
|
/* allow modules to define internel error code, from 0x1000. */
|
|
#define soc_kfree_ci(addr) osal_kfree(addr)
|
|
#define soc_vmalloc_ci(size) osal_vmalloc(size)
|
|
#define soc_vfree_ci(addr) osal_vfree(addr)
|
|
|
|
/* --------------------------- MACRO DECLARATIONS --------------------------------------- */
|
|
#define MAX_TUPPLE_BUFFER 0x100 /* maximum tupple buffer size */
|
|
|
|
#define CI_PCCD_IORESET_TIMEOUT 300 /* 3s */
|
|
#define CI_PCCD_RW_TIMEOUT 5 /* 50ms */
|
|
#define CI_PCCD_NEGBUFFER_TIMEOUT 300 /* 300ms */
|
|
|
|
#define CI_TIME_10MS 10 /* 10ms */
|
|
#define CI_TIME_1MS 1 /* 1ms */
|
|
#define CI_DELAY_AFTER_WRITE_US 5 /* us */
|
|
|
|
#define DATA_REG 0
|
|
#define COM_STAT_REG 1
|
|
#define SIZE_L_REG 2
|
|
#define SIZE_H_REG 3
|
|
|
|
#define CI_PCCD_MIN_BUFFERSIZE 16
|
|
#define CI_PCCD_MAX_BUFFERSIZE 1024
|
|
|
|
/* status register bits */
|
|
#define PCCD_RE 0x01 /* read error */
|
|
#define PCCD_WE 0x02 /* write error */
|
|
#define PCCD_FR 0x40 /* free */
|
|
#define PCCD_DA 0x80 /* Data available */
|
|
|
|
/* command register bits */
|
|
#define PCCD_SETHC 0x01 /* host control bit set */
|
|
#define PCCD_SETSW 0x02 /* size write set */
|
|
#define PCCD_SETSR 0x04 /* size read set */
|
|
#define PCCD_SETRS 0x08 /* interface reset */
|
|
#define PCCD_CLEAR 0x00 /* clear command register */
|
|
|
|
#define BITS_IN_1BYTE 8
|
|
#define BITS_IN_2BYTE 16
|
|
#define BITS_IN_3BYTE 24
|
|
#define CIS_EVEN_ADDR 2
|
|
#define IO_DATA_LEN 2
|
|
|
|
/* EN50221 specific constants */
|
|
#define STCE_EV1 "DVB_HOST"
|
|
#define STCE_PD1 "DVB_CI_MODULE"
|
|
#define STCE_EV2 "DTV_HOST"
|
|
#define STCE_PD2 "DTV_CI_MODULE"
|
|
#define STCF_LIB "DVB_CI_V1.00"
|
|
#define STCF_LIB_LEN 12
|
|
#define TPCE_IO 0x22
|
|
#define TPCE_IF 0x04
|
|
#define COR_BASE_ADDRESS_UPPERLIMIT 0xFFE
|
|
#define STCI_IFN 0x41
|
|
#define STCI_IFN_1 0x02
|
|
#define TPLLV1_MINOR 0x00
|
|
#define TPLLV1_MAJOR 0x05
|
|
#define TPLLMANFID_SIZE 0x04
|
|
#define TPCC_RFSZ_MASK 0xC0
|
|
#define TPCC_RMSZ_MASK 0x3C
|
|
#define TPCC_RASZ_MASK 0x03
|
|
#define TPCE_INDX_MASK 0x3F
|
|
#define RW_SCALE_MASK 0x1F
|
|
#define IO_ADDLINES_MASK 0x1F
|
|
#define DTYPE_NULL 0x00
|
|
#define DTYPE_RESERVED 0x0F
|
|
#define DSPEED_MASK 0x07
|
|
#define TPCE_TD_WAIT_SCALE_MASK 0x03
|
|
#define TPCE_TD_READY_SCALE_MASK 0x1C
|
|
#define TPCE_IO_RD_LENSIZE_MASK 0xC0
|
|
#define TPCE_IO_RD_ADDRSIZE_MASK 0x30
|
|
#define TPCE_IO_RD_ARNUM_MASK 0x0F
|
|
#define TPCE_MS_WINDOW_MASK 0x07
|
|
#define TPCE_MS_LENSIZE_MASK 0x18
|
|
#define TPCE_MS_CARDADDRSIZE_MASK 0x60
|
|
#define STCE_EV_TAG 0xC0
|
|
#define STCE_PD_TAG 0xC1
|
|
|
|
#define NOT_READY_WAIT 0x1F
|
|
#define WAIT_SCALE_0 0x1C
|
|
#define WAIT_SCALE_1 0x1D
|
|
#define WAIT_SCALE_2 0x1E
|
|
#define READY_SCALE_0 0x03
|
|
#define READY_SCALE_1 0x07
|
|
#define READY_SCALE_2 0x0B
|
|
#define READY_SCALE_3 0x0F
|
|
#define READY_SCALE_4 0x13
|
|
#define READY_SCALE_5 0x17
|
|
#define READY_SCALE_6 0x1b
|
|
|
|
/* tupple tag of CIS */
|
|
#define CISTPL_DEVICE 0x01
|
|
#define CISTPL_CHECKSUM 0x10
|
|
#define CISTPL_LINKTARGET 0x13
|
|
#define CISTPL_NO_LINK 0x14
|
|
#define CISTPL_VERS_1 0x15
|
|
#define CISTPL_ALTSTR 0x16
|
|
#define CISTPL_DEVICE_A 0x17
|
|
#define CISTPL_CONFIG 0x1a
|
|
#define CISTPL_CFTABLE_ENTRY 0x1b
|
|
#define CISTPL_DEVICE_OC 0x1c
|
|
#define CISTPL_DEVICE_OA 0x1d
|
|
#define CISTPL_MANFID 0x20
|
|
#define CISTPL_FUNCID 0x21
|
|
#define CISTPL_END 0xff
|
|
|
|
/* masking bit */
|
|
#define MASK_BIT_0 0x01
|
|
#define MASK_BIT_1 0x02
|
|
#define MASK_BIT_2 0x04
|
|
#define MASK_BIT_3 0x08
|
|
#define MASK_BIT_4 0x10
|
|
#define MASK_BIT_5 0x20
|
|
#define MASK_BIT_6 0x40
|
|
#define MASK_BIT_7 0x80
|
|
|
|
#define TUPPLE_DATA_MIN_LEN 6
|
|
|
|
#define INVALID_DATA_SIZE 0xFFFF
|
|
#define DBG_DUMP_LINE_SIZE 16
|
|
|
|
#define CHECK_CI_OPENED(port) do { \
|
|
if (!g_ci_drv_param[port].open) { \
|
|
soc_log_err("CI port didn't init.\n"); \
|
|
soc_err_print_u32(port); \
|
|
return SOC_ERR_CI_NOT_INIT; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define CHECK_CI_PCCD_OPENED(port, card) do { \
|
|
if ((g_ci_drv_param[port].open != TD_TRUE) \
|
|
|| (g_ci_drv_param[port].card_param[card].card_open != TD_TRUE)) { \
|
|
soc_log_err("CI card didn't init.\n"); \
|
|
soc_err_print_u32(port); \
|
|
soc_err_print_u32(card); \
|
|
return SOC_ERR_CI_NOT_INIT; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define soc_log_check_param_return(val) \
|
|
do { \
|
|
if (val) { \
|
|
soc_err_print_err_code(SOC_ERR_CI_INVALID_PARA); \
|
|
return SOC_ERR_CI_INVALID_PARA; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define check_func_ret_return(expr) \
|
|
do { \
|
|
td_s32 ret_ = (expr); \
|
|
if (ret_ != TD_SUCCESS) { \
|
|
soc_err_print_call_fun_err(expr, ret_); \
|
|
return ret_; \
|
|
} \
|
|
} while (0)
|
|
|
|
|
|
/* -------------------- STATIC STRUCTURE DECLARATIONS ----------------------------------- */
|
|
/* private Data structures***********************************************/
|
|
typedef struct {
|
|
ext_ci_pccd_volt volt; /* PCMCIA card voltage, 5v or 3v3 DC */
|
|
ext_ci_pccd_speed speed; /* PCMCIA card attribute read/write speed */
|
|
td_bool is_ciplus;
|
|
td_u32 ciprof;
|
|
} ci_pccd_attr;
|
|
|
|
/* CI PC CARD configure and management structure */
|
|
typedef struct {
|
|
td_bool card_open;
|
|
td_bool print_io_data; /* print IO data */
|
|
ci_pccd_attr attr; /* voltage, speed, ... */
|
|
td_u32 tpcc_radr; /* TPCC_RADR, will be evaluated when check CIS */
|
|
td_u8 tpce_indx; /* TPCE_INDX, will be evaluated when check CIS */
|
|
td_u8 reserve;
|
|
td_u16 buffer_size; /* save buffer size, prevent read ro write overflow */
|
|
td_u8 *buffer; /* buffer use to save IO data */
|
|
td_u32 io_cnt;
|
|
} ci_drv_pccd_parameter;
|
|
|
|
/* CI driver configure structure */
|
|
typedef struct {
|
|
td_bool open;
|
|
ci_drv_pccd_parameter card_param[EXT_CI_PCCD_MAX];
|
|
ext_ci_attr attr;
|
|
} ci_drv_parameter;
|
|
|
|
/* tupple Data structure */
|
|
typedef struct {
|
|
td_u8 tupple_tag;
|
|
td_u8 tupple_link;
|
|
td_u8 tupple_data[MAX_TUPPLE_BUFFER];
|
|
} ci_pccd_tupple, *ci_pccd_tupple_ptr;
|
|
|
|
/* ------------------------- STATIC DECLARATIONS ---------------------------------------- */
|
|
static ci_drv_parameter g_ci_drv_param[EXT_CI_PORT_MAX];
|
|
|
|
td_s32 drv_ci_init(td_void)
|
|
{
|
|
td_s32 ret;
|
|
ext_ci_port port;
|
|
|
|
soc_dbg_func_enter();
|
|
|
|
check_func_ret_return(memset_s(g_ci_drv_param, sizeof(g_ci_drv_param), 0, sizeof(g_ci_drv_param)));
|
|
|
|
for (port = EXT_CI_PORT_0; port < EXT_CI_PORT_MAX; port++) {
|
|
g_ci_drv_param[port].attr.dev_type = EXT_CI_DEV_SEL_EXTCI;
|
|
g_ci_drv_param[port].attr.dev_attr.extci.is_power_ctrl_gpio_used = TD_FALSE;
|
|
}
|
|
|
|
ret = hal_ci_init();
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(hal_ci_init, ret);
|
|
return ret;
|
|
}
|
|
|
|
soc_dbg_func_exit();
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_void drv_ci_de_init(td_void)
|
|
{
|
|
soc_dbg_func_enter();
|
|
|
|
soc_dbg_func_exit();
|
|
}
|
|
|
|
/* added begin 2012-04-24 : support various CI device */
|
|
td_s32 drv_ci_set_attr(ext_ci_port port, ext_ci_attr attr)
|
|
{
|
|
td_s32 ret;
|
|
|
|
soc_dbg_func_enter();
|
|
|
|
soc_log_check_param_return(port >= EXT_CI_PORT_1);
|
|
soc_log_check_param_return(attr.dev_type != EXT_CI_DEV_SEL_EXTCI);
|
|
|
|
g_ci_drv_param[port].attr = attr;
|
|
|
|
/*
|
|
* normally, user should call this function after open CI device
|
|
* once they open device earlier, close and open the device again.
|
|
*/
|
|
ret = hal_ci_set_attr(port, attr);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_u32(port);
|
|
soc_err_print_call_fun_err(hal_ci_set_attr, ret);
|
|
return ret;
|
|
}
|
|
|
|
soc_dbg_func_exit();
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 drv_ci_get_attr(ext_ci_port port, ext_ci_attr *attr)
|
|
{
|
|
soc_dbg_func_enter();
|
|
|
|
soc_log_check_param_return(port >= EXT_CI_PORT_1);
|
|
soc_log_check_param_return(attr == TD_NULL);
|
|
|
|
*attr = g_ci_drv_param[port].attr;
|
|
|
|
soc_dbg_func_exit();
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 drv_ci_open(ext_ci_port port)
|
|
{
|
|
td_s32 ret;
|
|
|
|
soc_dbg_func_enter();
|
|
|
|
soc_log_check_param_return(port >= EXT_CI_PORT_1);
|
|
|
|
ret = hal_ci_device_open(port);
|
|
if (ret == TD_SUCCESS) {
|
|
/* init parameters */
|
|
g_ci_drv_param[port].open = TD_TRUE;
|
|
} else {
|
|
soc_log_err("CI port open failed.\n");
|
|
soc_err_print_u32(port);
|
|
soc_err_print_call_fun_err(hal_ci_device_open, ret);
|
|
return ret;
|
|
}
|
|
|
|
soc_dbg_func_exit();
|
|
return ret;
|
|
}
|
|
|
|
td_s32 drv_ci_close(ext_ci_port port)
|
|
{
|
|
td_s32 ret;
|
|
ext_ci_pccd card;
|
|
|
|
soc_dbg_func_enter();
|
|
|
|
if (port >= EXT_CI_PORT_1) {
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
/* close all cards of this group. */
|
|
for (card = EXT_CI_PCCD_A; card < EXT_CI_PCCD_B; card++) {
|
|
hal_ci_pccd_close(port, card);
|
|
g_ci_drv_param[port].card_param[card].card_open = TD_FALSE;
|
|
soc_err_print_u32(port);
|
|
soc_err_print_u32(card);
|
|
}
|
|
|
|
ret = hal_ci_device_close(port);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(hal_ci_device_close, ret);
|
|
g_ci_drv_param[port].open = TD_FALSE;
|
|
return ret;
|
|
}
|
|
|
|
g_ci_drv_param[port].open = TD_FALSE;
|
|
|
|
soc_dbg_func_exit();
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 drv_ci_standby(ext_ci_port port)
|
|
{
|
|
td_s32 ret;
|
|
|
|
soc_dbg_func_enter();
|
|
|
|
soc_log_check_param_return(port >= EXT_CI_PORT_1);
|
|
|
|
ret = hal_ci_standby(port);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(hal_ci_standby, ret);
|
|
soc_err_print_u32(port);
|
|
return ret;
|
|
}
|
|
|
|
soc_dbg_func_exit();
|
|
return ret;
|
|
}
|
|
|
|
td_s32 drv_ci_resume(ext_ci_port port)
|
|
{
|
|
td_s32 ret;
|
|
|
|
soc_dbg_func_enter();
|
|
|
|
soc_log_check_param_return(port >= EXT_CI_PORT_1);
|
|
|
|
ret = hal_ci_resume(port);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(hal_ci_resume, ret);
|
|
soc_err_print_u32(port);
|
|
return ret;
|
|
}
|
|
|
|
soc_dbg_func_exit();
|
|
return ret;
|
|
}
|
|
|
|
td_s32 drv_ci_pccd_open(ext_ci_port port, ext_ci_pccd card)
|
|
{
|
|
td_s32 ret;
|
|
|
|
soc_dbg_func_enter();
|
|
|
|
soc_log_check_param_return(port >= EXT_CI_PORT_1);
|
|
soc_log_check_param_return(card >= EXT_CI_PCCD_B);
|
|
|
|
CHECK_CI_OPENED(port);
|
|
|
|
/* make sure open once */
|
|
if (g_ci_drv_param[port].open == TD_FALSE) {
|
|
soc_log_err("CI PCCD open failed for CI_OPEN failed.\n");
|
|
soc_err_print_u32(port);
|
|
soc_err_print_u32(card);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
ret = hal_ci_pccd_open(port, card);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("CI port PCCD open failed.\n");
|
|
soc_err_print_u32(port);
|
|
soc_err_print_u32(card);
|
|
soc_err_print_call_fun_err(hal_ci_pccd_open, ret);
|
|
return ret;
|
|
}
|
|
|
|
/* init buffer */
|
|
g_ci_drv_param[port].card_param[card].buffer = soc_vmalloc_ci(CI_PCCD_MAX_BUFFERSIZE);
|
|
if (g_ci_drv_param[port].card_param[card].buffer == TD_NULL) {
|
|
soc_log_fatal("CI port PCCD alloc memory failed.\n");
|
|
soc_err_print_u32(port);
|
|
soc_err_print_u32(card);
|
|
soc_err_print_call_fun_err(soc_vmalloc_ci, SOC_ERR_CI_NO_MEMORY);
|
|
return SOC_ERR_CI_NO_MEMORY;
|
|
}
|
|
|
|
g_ci_drv_param[port].card_param[card].buffer_size = CI_PCCD_MAX_BUFFERSIZE;
|
|
|
|
/* init parameters */
|
|
g_ci_drv_param[port].card_param[card].card_open = TD_TRUE;
|
|
soc_log_info("CI port PCCD open success\n");
|
|
|
|
soc_dbg_func_exit();
|
|
return ret;
|
|
}
|
|
|
|
td_void drv_ci_pccd_close(ext_ci_port port, ext_ci_pccd card)
|
|
{
|
|
soc_dbg_func_enter();
|
|
|
|
if ((port >= EXT_CI_PORT_1) || (card >= EXT_CI_PCCD_B)) {
|
|
soc_log_err("invalid CI port or card id.\n");
|
|
soc_err_print_u32(port);
|
|
soc_err_print_u32(card);
|
|
return;
|
|
}
|
|
|
|
if (g_ci_drv_param[port].open == TD_FALSE) {
|
|
soc_err_print_u32(port);
|
|
soc_err_print_u32(card);
|
|
return;
|
|
}
|
|
|
|
/* free buffer */
|
|
if (g_ci_drv_param[port].card_param[card].buffer != TD_NULL) {
|
|
soc_vfree_ci(g_ci_drv_param[port].card_param[card].buffer);
|
|
g_ci_drv_param[port].card_param[card].buffer = TD_NULL;
|
|
g_ci_drv_param[port].card_param[card].buffer_size = 0;
|
|
}
|
|
|
|
/* hardware close */
|
|
hal_ci_pccd_close(port, card);
|
|
|
|
g_ci_drv_param[port].card_param[card].card_open = TD_FALSE;
|
|
|
|
soc_dbg_func_exit();
|
|
}
|
|
|
|
td_s32 drv_ci_pccd_ctrl_power(ext_ci_port port, ext_ci_pccd card, ext_ci_pccd_ctrl_power ctrl_power)
|
|
{
|
|
td_s32 ret;
|
|
|
|
soc_dbg_func_enter();
|
|
|
|
soc_log_check_param_return(port >= EXT_CI_PORT_1);
|
|
soc_log_check_param_return(card >= EXT_CI_PCCD_B);
|
|
|
|
CHECK_CI_PCCD_OPENED(port, card);
|
|
|
|
ret = hal_ci_pccd_ctrl_power(port, card, ctrl_power);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ci_pccd_ctrl_power, ret);
|
|
soc_err_print_u32(port);
|
|
return ret;
|
|
}
|
|
g_ci_drv_param[port].attr.dev_attr.extci.is_power_ctrl_gpio_used =
|
|
(ctrl_power == EXT_CI_PCCD_CTRLPOWER_ON) ? TD_FALSE : TD_TRUE;
|
|
|
|
soc_dbg_func_exit();
|
|
return ret;
|
|
}
|
|
|
|
td_s32 drv_ci_pccd_reset(ext_ci_port port, ext_ci_pccd card)
|
|
{
|
|
td_s32 ret;
|
|
|
|
soc_dbg_func_enter();
|
|
|
|
soc_log_check_param_return(port >= EXT_CI_PORT_1);
|
|
soc_log_check_param_return(card >= EXT_CI_PCCD_B);
|
|
|
|
CHECK_CI_PCCD_OPENED(port, card);
|
|
|
|
ret = hal_ci_pccd_reset(port, card);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("CI PCCD reset failed.\n");
|
|
soc_err_print_call_fun_err(ci_pccd_reset, ret);
|
|
soc_err_print_u32(port);
|
|
soc_err_print_u32(card);
|
|
return ret;
|
|
}
|
|
|
|
soc_log_info("CI PCCD reset successfully\n");
|
|
|
|
soc_dbg_func_exit();
|
|
return ret;
|
|
}
|
|
|
|
td_s32 drv_ci_pccd_is_ready(ext_ci_port port, ext_ci_pccd card, ext_ci_pccd_ready* ready)
|
|
{
|
|
td_s32 ret;
|
|
|
|
soc_dbg_func_enter();
|
|
soc_log_check_param_return(port >= EXT_CI_PORT_1);
|
|
soc_log_check_param_return(card >= EXT_CI_PCCD_B);
|
|
soc_log_check_param_return(ready == TD_NULL);
|
|
|
|
CHECK_CI_PCCD_OPENED(port, card);
|
|
|
|
ret = hal_ci_pccd_ready_or_busy(port, card, ready);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("CI PCCD get ready status failed.\n");
|
|
soc_err_print_call_fun_err(hal_ci_pccd_ready_or_busy, ret);
|
|
soc_err_print_u32(port);
|
|
soc_err_print_u32(card);
|
|
return ret;
|
|
}
|
|
|
|
soc_dbg_func_exit();
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 drv_ci_pccd_detect(ext_ci_port port, ext_ci_pccd card, ext_ci_pccd_status* status)
|
|
{
|
|
td_s32 ret;
|
|
|
|
soc_dbg_func_enter();
|
|
|
|
soc_log_check_param_return(port >= EXT_CI_PORT_1);
|
|
soc_log_check_param_return(card >= EXT_CI_PCCD_B);
|
|
soc_log_check_param_return(status == TD_NULL);
|
|
|
|
CHECK_CI_PCCD_OPENED(port, card);
|
|
|
|
ret = hal_ci_pccd_detect(port, card, status);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("CI PCCD detect failed.\n");
|
|
soc_err_print_call_fun_err(ci_pccd_detect, ret);
|
|
soc_err_print_u32(port);
|
|
soc_err_print_u32(card);
|
|
return ret;
|
|
}
|
|
|
|
soc_dbg_func_exit();
|
|
return ret;
|
|
}
|
|
|
|
td_s32 drv_ci_pccd_get_status(ext_ci_port port, ext_ci_pccd card, td_u8 *value)
|
|
{
|
|
soc_dbg_func_enter();
|
|
|
|
soc_log_check_param_return(port >= EXT_CI_PORT_1);
|
|
soc_log_check_param_return(card >= EXT_CI_PCCD_B);
|
|
soc_log_check_param_return(value == TD_NULL);
|
|
|
|
CHECK_CI_PCCD_OPENED(port, card);
|
|
|
|
check_func_ret_return(hal_ci_pccd_io_read_byte(port, card, COM_STAT_REG, value));
|
|
|
|
soc_dbg_func_exit();
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 drv_ci_pccd_io_read(ext_ci_port port, ext_ci_pccd card, td_u8 *buffer, td_u32 buffer_len, td_u32 *read_len)
|
|
{
|
|
td_u16 io_len_to_read;
|
|
td_u32 counter;
|
|
td_u16 buffer_size;
|
|
td_u8 status = 0;
|
|
td_u8 data_low;
|
|
td_u8 data_high;
|
|
td_u8 *recv = TD_NULL;
|
|
|
|
soc_dbg_func_enter();
|
|
|
|
soc_log_check_param_return(port >= EXT_CI_PORT_1);
|
|
soc_log_check_param_return(card >= EXT_CI_PCCD_B);
|
|
soc_log_check_param_return(buffer == TD_NULL);
|
|
soc_log_check_param_return(read_len == TD_NULL);
|
|
|
|
CHECK_CI_PCCD_OPENED(port, card);
|
|
|
|
/* Module to Host Transfers
|
|
* Periodically the host tests the DA bit in the Status Register. If DA is '1'
|
|
* then the host reads the Size Register to find out how much data is to be transferred.
|
|
* It then reads that number of data bytes from the Data register.
|
|
* This multiple read shall not be interrupted by any other operations on the interface
|
|
* except for reads of the Status Register. When the first byte is read the module
|
|
* sets RE to '1' and sets DA to '0'. During the transfer the RE
|
|
* bit remains at '1' until the last byte is read, at which point it is set to '0'.
|
|
* If any further bytes are read then the RE bit is set to '1'.
|
|
*/
|
|
if (g_ci_drv_param[port].card_param[card].buffer == TD_NULL) {
|
|
soc_err_print_err_code(SOC_ERR_CI_IO_READ_ERR);
|
|
return SOC_ERR_CI_IO_READ_ERR;
|
|
} else {
|
|
recv = g_ci_drv_param[port].card_param[card].buffer;
|
|
}
|
|
|
|
/* DA is set ? */
|
|
check_func_ret_return(hal_ci_pccd_io_read_byte(port, card, COM_STAT_REG, &status));
|
|
if ((status & PCCD_DA) != PCCD_DA) {
|
|
soc_err_print_err_code(SOC_ERR_CI_IO_READ_ERR);
|
|
soc_err_print_u32(status);
|
|
return SOC_ERR_CI_IO_READ_ERR;
|
|
}
|
|
|
|
/* read size */
|
|
check_func_ret_return(hal_ci_pccd_io_read_byte(port, card, SIZE_L_REG, &data_low));
|
|
check_func_ret_return(hal_ci_pccd_io_read_byte(port, card, SIZE_H_REG, &data_high));
|
|
io_len_to_read = data_low | (data_high << BITS_IN_1BYTE);
|
|
|
|
/* read len must be a safe length. */
|
|
/* please invoke ci_pccd_neg_buffer_size() to negotiate buffer size. */
|
|
buffer_size = g_ci_drv_param[port].card_param[card].buffer_size;
|
|
if (io_len_to_read > buffer_size) {
|
|
soc_err_print_err_code(SOC_ERR_CI_IO_READ_ERR);
|
|
soc_err_print_u32(io_len_to_read);
|
|
return SOC_ERR_CI_IO_READ_ERR;
|
|
}
|
|
|
|
/* read io data */
|
|
for (counter = 0; counter < io_len_to_read; counter++) {
|
|
check_func_ret_return(hal_ci_pccd_io_read_byte(port, card, DATA_REG, recv + counter));
|
|
}
|
|
|
|
/* before read over, read error bit should be 1. */
|
|
check_func_ret_return(hal_ci_pccd_io_read_byte(port, card, COM_STAT_REG, &status));
|
|
if ((status & PCCD_RE) != 0x00) {
|
|
soc_log_err("Io read error, the RE is '1'.\n");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
if (g_ci_drv_param[port].card_param[card].print_io_data == TD_TRUE) {
|
|
soc_info_print_block(recv, counter);
|
|
}
|
|
|
|
if (buffer_len < counter) {
|
|
soc_log_err("the dst is lower than src, copy temp buffer to user failed.\n");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
/* copy data to user */
|
|
if (osal_copy_to_user(buffer, recv, counter)) {
|
|
soc_log_err("copy temp buffer to user failed.\n");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
if (counter > 0) {
|
|
g_ci_drv_param[port].card_param[card].io_cnt++;
|
|
}
|
|
|
|
*read_len = counter;
|
|
|
|
soc_dbg_func_exit();
|
|
return TD_SUCCESS;
|
|
} /* ci_pccd_read */
|
|
|
|
td_s32 drv_ci_pccd_io_write(ext_ci_port port, ext_ci_pccd card, const td_u8 *buffer,
|
|
td_u32 write_len, td_u32 *write_ok_len)
|
|
{
|
|
td_u32 counter;
|
|
td_u32 retry_times;
|
|
td_u16 buffer_size;
|
|
td_u8 status = 0;
|
|
td_u8 *send = TD_NULL;
|
|
|
|
soc_dbg_func_enter();
|
|
|
|
soc_log_check_param_return(port >= EXT_CI_PORT_1);
|
|
soc_log_check_param_return(card >= EXT_CI_PCCD_B);
|
|
soc_log_check_param_return(buffer == TD_NULL);
|
|
soc_log_check_param_return(write_ok_len == TD_NULL);
|
|
|
|
/* no need to write, do nothing. */
|
|
if (write_len == 0) {
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
CHECK_CI_PCCD_OPENED(port, card);
|
|
|
|
*write_ok_len = 0x00;
|
|
|
|
/* check buffer: if null, return. */
|
|
send = g_ci_drv_param[port].card_param[card].buffer;
|
|
if (send == TD_NULL) {
|
|
soc_log_err("invalid buffer,point is null.\n");
|
|
soc_err_print_err_code(SOC_ERR_CI_NO_MEMORY);
|
|
return SOC_ERR_CI_NO_MEMORY;
|
|
}
|
|
|
|
/* write len must be a safe length. */
|
|
/* please invoke ci_pccd_neg_buffer_size() to negotiate buffer size. */
|
|
buffer_size = g_ci_drv_param[port].card_param[card].buffer_size;
|
|
if (write_len > buffer_size) {
|
|
soc_err_print_err_code(SOC_ERR_CI_IO_READ_ERR);
|
|
soc_err_print_u32(write_len);
|
|
soc_err_print_u32(buffer_size);
|
|
return SOC_ERR_CI_INVALID_PARA;
|
|
}
|
|
|
|
/* copy user data */
|
|
if (osal_copy_from_user(send, buffer, write_len)) {
|
|
soc_log_err("copy buffer from user failed.\n");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
/* Host to Module Transfers
|
|
* The host sets the HC bit and then tests the FR bit. If FR is '0' then
|
|
* the interface is busy and the host must reset HC and wait a period before
|
|
* repeating the test. If FR is '1' then the host writes the number of bytes
|
|
* it wishes to send to the module into the Size register and then writes that
|
|
* number of data bytes to the Data register. This multiple write shall not be
|
|
* interrupted by any other operations on the interface except for reads of the Status
|
|
* Register. When the first byte is written the module sets WE to '1' and sets FR to '0'.
|
|
* During the transfer the WE bit remains at '1' until the last byte is written,
|
|
* at which point it is set to '0'. If any further bytes are writte then the WE bit
|
|
* is set to '1'. At the end of the transfer the host shall reset the HC bit by writing '0'
|
|
* to it. The host must test the DA bit before initiating the host-to-module cycle above
|
|
* in order to avoid deadlock in the case of a single buffer implementation in the module.
|
|
*/
|
|
/* if the module has data avalaible, you should read first */
|
|
check_func_ret_return(hal_ci_pccd_io_read_byte(port, card, COM_STAT_REG, &status));
|
|
if ((status & PCCD_DA) == PCCD_DA) {
|
|
soc_log_err("CI card write error,Data is available!\n");
|
|
soc_err_print_u32(port);
|
|
soc_err_print_u32(card);
|
|
return SOC_ERR_CI_PCCD_DEVICE_BUSY;
|
|
}
|
|
|
|
/* if the module is free, set host control and wait for free again */
|
|
for (retry_times = 0; retry_times < CI_PCCD_RW_TIMEOUT; retry_times++) {
|
|
check_func_ret_return(hal_ci_pccd_io_write_byte(port, card, COM_STAT_REG, PCCD_SETHC));
|
|
|
|
osal_msleep(1);
|
|
check_func_ret_return(hal_ci_pccd_io_read_byte(port, card, COM_STAT_REG, &status));
|
|
if ((status & PCCD_FR) == PCCD_FR) {
|
|
break;
|
|
}
|
|
|
|
check_func_ret_return(hal_ci_pccd_io_write_byte(port, card, COM_STAT_REG, PCCD_CLEAR));
|
|
osal_msleep(CI_TIME_10MS);
|
|
}
|
|
|
|
check_func_ret_return(hal_ci_pccd_io_read_byte(port, card, COM_STAT_REG, &status));
|
|
if ((status & PCCD_FR) != PCCD_FR) {
|
|
soc_log_err("ci_pccd_write() error: device is busy!\n");
|
|
return SOC_ERR_CI_PCCD_DEVICE_BUSY;
|
|
}
|
|
|
|
/* the host writes the number of bytes he wants to send to the module */
|
|
check_func_ret_return(hal_ci_pccd_io_write_byte(port, card, SIZE_L_REG, (td_u8)(write_len & 0xff)));
|
|
check_func_ret_return(hal_ci_pccd_io_write_byte(port, card, SIZE_H_REG, (td_u8)(write_len >> BITS_IN_1BYTE)));
|
|
|
|
for (counter = 0; counter < write_len; counter++) {
|
|
check_func_ret_return(hal_ci_pccd_io_write_byte(port, card, DATA_REG, send[counter]));
|
|
|
|
/* When the first byte is written the module sets WE to '1'and sets FR to '0'.
|
|
* During the transfer the WE bit remains at '1'until the last byte is written,
|
|
* at which point it is set to '0'.
|
|
*/
|
|
}
|
|
|
|
/* after write over, write error bit should be 0. */
|
|
check_func_ret_return(hal_ci_pccd_io_read_byte(port, card, COM_STAT_REG, &status));
|
|
if ((status & PCCD_WE) == PCCD_WE) {
|
|
soc_err_print_err_code(SOC_ERR_CI_IO_WRITE_ERR);
|
|
return SOC_ERR_CI_IO_WRITE_ERR;
|
|
}
|
|
|
|
/* return control to the module */
|
|
check_func_ret_return(hal_ci_pccd_io_write_byte(port, card, COM_STAT_REG, PCCD_CLEAR));
|
|
|
|
if (g_ci_drv_param[port].card_param[card].print_io_data == TD_TRUE) {
|
|
soc_info_print_block(send, counter);
|
|
}
|
|
|
|
*write_ok_len = counter;
|
|
|
|
soc_dbg_func_exit();
|
|
return TD_SUCCESS;
|
|
} /* ci_pccd_write */
|
|
|
|
static td_s32 drv_ci_pccd_read_block(ext_ci_port port, ext_ci_pccd card, td_u8 cis[MAX_CIS_SIZE],
|
|
td_u32 cibuf_len, td_u32 *len)
|
|
{
|
|
td_s32 ret;
|
|
td_u32 retry_times;
|
|
ext_ci_pccd_ready ready = EXT_CI_PCCD_BUSY;
|
|
td_u32 tupple_offset;
|
|
td_u16 offset = 0;
|
|
td_u8 num;
|
|
|
|
soc_dbg_func_enter();
|
|
|
|
if (cibuf_len > MAX_CIS_SIZE) {
|
|
soc_log_err("CIS buffer len err.\n");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
/* wait PCCARD ready */
|
|
for (retry_times = 0; retry_times < CI_PCCD_IORESET_TIMEOUT; retry_times++) {
|
|
ret = hal_ci_pccd_ready_or_busy(port, card, &ready);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(hal_ci_pccd_ready_or_busy, ret);
|
|
}
|
|
if (ready == EXT_CI_PCCD_READY) {
|
|
break;
|
|
}
|
|
osal_msleep(CI_TIME_10MS);
|
|
}
|
|
|
|
if (retry_times == CI_PCCD_IORESET_TIMEOUT) {
|
|
soc_log_err("card busy.\n");
|
|
soc_err_print_err_code(SOC_ERR_CI_PCCD_DEVICE_BUSY);
|
|
return SOC_ERR_CI_PCCD_DEVICE_BUSY;
|
|
}
|
|
|
|
while (1) {
|
|
tupple_offset = offset * CIS_EVEN_ADDR; /* CIS is valid in the even byte, so offset*2 */
|
|
|
|
if (offset >= MAX_CIS_SIZE - 1) {
|
|
soc_log_err("CIS size overflow.\n");
|
|
soc_err_print_h32(offset);
|
|
soc_err_print_err_code(SOC_ERR_CI_PCCD_CIS_READ);
|
|
return SOC_ERR_CI_PCCD_CIS_READ;
|
|
}
|
|
|
|
/* read tupple tag, 1 byte. */
|
|
hal_ci_pccd_mem_read_byte(port, card, tupple_offset, &(cis[offset]));
|
|
soc_dbg_print_h32(offset);
|
|
soc_dbg_print_h32(cis[offset]);
|
|
|
|
/* check tupple tag */
|
|
switch (cis[offset]) {
|
|
case CISTPL_DEVICE:
|
|
case CISTPL_CHECKSUM:
|
|
case CISTPL_LINKTARGET:
|
|
case CISTPL_NO_LINK:
|
|
case CISTPL_VERS_1:
|
|
case CISTPL_ALTSTR:
|
|
case CISTPL_DEVICE_A:
|
|
case CISTPL_CONFIG:
|
|
case CISTPL_CFTABLE_ENTRY:
|
|
case CISTPL_DEVICE_OC:
|
|
case CISTPL_DEVICE_OA:
|
|
case CISTPL_MANFID:
|
|
case CISTPL_FUNCID:
|
|
case CISTPL_END:
|
|
break;
|
|
default:
|
|
soc_err_print_h32(cis[offset]);
|
|
return SOC_ERR_CI_PCCD_CIS_READ;
|
|
} /* end switch */
|
|
|
|
hal_ci_pccd_mem_read_byte(port, card, tupple_offset + CIS_EVEN_ADDR, /* 2: even address */
|
|
&(cis[offset + 1])); /* read tupple link, 1 byte. */
|
|
|
|
/* if CISTPL_END, break. */
|
|
if (cis[offset] == CISTPL_END) {
|
|
offset += 1;
|
|
break;
|
|
}
|
|
|
|
/* prevent cis arrays from crossing the boundary */
|
|
if (cis[offset + 1] - 1 + CIS_EVEN_ADDR + offset >= MAX_CIS_SIZE) {
|
|
return SOC_ERR_CI_PCCD_CIS_READ;
|
|
}
|
|
|
|
/* reading the tupple data */
|
|
for (num = 0; num < cis[offset + 1]; num++) {
|
|
hal_ci_pccd_mem_read_byte(port, card, tupple_offset + 4 + num * 2, /* tag and link 2 bytes, so add 4 */
|
|
&(cis[offset + CIS_EVEN_ADDR + num]));
|
|
}
|
|
|
|
/* next tupple */
|
|
offset += CIS_EVEN_ADDR + cis[offset + 1];
|
|
}
|
|
|
|
*len = offset;
|
|
soc_dbg_func_exit();
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_s32 check_cioa_oc(ci_pccd_tupple_ptr tupple, ext_ci_pccd_volt *volt, ext_ci_pccd_speed *speed)
|
|
{
|
|
td_u8 bit1;
|
|
td_u8 bit2;
|
|
td_u8 device_type_code;
|
|
td_u8 i;
|
|
|
|
soc_dbg_func_enter();
|
|
|
|
/* other conditions info */
|
|
/* ext voltage, unsupport now */
|
|
if (MASK_BIT_7 & tupple->tupple_data[0]) {
|
|
*volt = CI_PCCD_VOLT_5V;
|
|
} else {
|
|
bit1 = tupple->tupple_data[0] & MASK_BIT_1;
|
|
bit2 = tupple->tupple_data[0] & MASK_BIT_2;
|
|
|
|
/* 00b, 5V */
|
|
if ((!bit2) && (!bit1)) {
|
|
*volt = CI_PCCD_VOLT_5V;
|
|
} else if ((!bit2) && (bit1)) { /* 01b, 3V3 */
|
|
*volt = CI_PCCD_VOLT_3V3;
|
|
} else if ((bit2) && (!bit1)) { /* 10b, X.X volt VCC, unsupport now */
|
|
*volt = CI_PCCD_VOLT_5V;
|
|
} else { /* 11b, card_bus PC card Y.Y volt VCC, unsupport now */
|
|
*volt = CI_PCCD_VOLT_5V;
|
|
}
|
|
|
|
soc_log_info("CIS_OA_OC get voltage.\n");
|
|
}
|
|
|
|
/* device info, from byte 1 */
|
|
for (i = 1; i < tupple->tupple_link; i++) {
|
|
device_type_code = tupple->tupple_data[i] >> 4; /* type code: 4bit */
|
|
soc_log_info("CIS_OA_OC get device_type_code.\n");
|
|
|
|
if ((device_type_code == DTYPE_NULL) || (device_type_code == DTYPE_RESERVED)) {
|
|
continue;
|
|
}
|
|
|
|
/* don't support DSPEED_EXT */
|
|
*speed = tupple->tupple_data[i] & DSPEED_MASK;
|
|
soc_log_info("CIS_OA_OC: speed.\n");
|
|
}
|
|
|
|
soc_dbg_func_exit();
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_u8 char_lower_case(const td_u8 ch)
|
|
{
|
|
td_u8 temp;
|
|
|
|
soc_dbg_func_enter();
|
|
if (ch >= 'A' && ch <= 'Z') {
|
|
temp = ch + ('a' - 'A');
|
|
} else {
|
|
temp = ch;
|
|
}
|
|
|
|
soc_dbg_func_exit();
|
|
return temp;
|
|
}
|
|
|
|
static const td_u8 *string_searach(const td_u8 *str, const td_u8 *sudtr, td_bool icase_sensitive)
|
|
{
|
|
td_u32 i;
|
|
td_u32 str_len;
|
|
td_u32 sustr_len;
|
|
td_u32 index;
|
|
const td_u8 *target = TD_NULL_PTR;
|
|
|
|
soc_dbg_func_enter();
|
|
|
|
str_len = strlen(str);
|
|
sustr_len = strlen(sudtr);
|
|
if (str_len == 0 || sustr_len == 0 || str_len < sustr_len) {
|
|
return TD_NULL_PTR;
|
|
}
|
|
index = 0;
|
|
target = &str[0];
|
|
if (icase_sensitive == TD_TRUE) { /* case insensitive */
|
|
for (i = 0; i < sustr_len; i++, index++) {
|
|
if (char_lower_case(str[index]) != char_lower_case(sudtr[i])) {
|
|
break;
|
|
}
|
|
}
|
|
} else { /* case sensitive */
|
|
for (i = 0; i <= sustr_len; i++, index++) {
|
|
if (str[index] != sudtr[i]) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (i == sustr_len - 1) {
|
|
soc_log_info("string_searach\n");
|
|
} else {
|
|
target = TD_NULL_PTR;
|
|
return target;
|
|
}
|
|
soc_dbg_func_exit();
|
|
return target;
|
|
}
|
|
|
|
static td_bool check_ciplus(const td_u8 *buf, td_u32 len, td_u32 *ciprof)
|
|
{
|
|
const td_u8 *target = TD_NULL_PTR;
|
|
*ciprof = 0;
|
|
|
|
soc_dbg_func_enter();
|
|
|
|
target = string_searach(buf, "$compatible[ciplus", TD_FALSE);
|
|
if (target == TD_NULL_PTR) {
|
|
return TD_FALSE;
|
|
}
|
|
soc_log_info("\"$compatible[ciplus\" appear in the additional"
|
|
" product information string\n");
|
|
target = string_searach(target, "ciprof=1", TD_FALSE);
|
|
if (target != TD_NULL_PTR) {
|
|
soc_log_info("\"ciplus\" appear in the additional product information string\n");
|
|
*ciprof = 1;
|
|
}
|
|
|
|
soc_dbg_func_exit();
|
|
return TD_TRUE;
|
|
}
|
|
|
|
/* TPLLV1_MAJOR: 0x05 TPLLV1_MINOR: 0x00 */
|
|
static td_s32 check_civers1(ci_pccd_tupple_ptr tupple, td_bool *ciplus, td_u32 *ciprof)
|
|
{
|
|
soc_dbg_func_enter();
|
|
|
|
if ((tupple->tupple_data[0] != TPLLV1_MAJOR)
|
|
|| (tupple->tupple_data[1] != TPLLV1_MINOR)) {
|
|
soc_log_err("invalid CISTPL_VERS_1\n");
|
|
return TD_FAILURE;
|
|
}
|
|
if (check_ciplus(tupple->tupple_data, MAX_TUPPLE_BUFFER, ciprof) == TD_TRUE) {
|
|
soc_log_info("check CIS, is a CI+ CAM.\n");
|
|
*ciplus = TD_TRUE;
|
|
} else {
|
|
soc_log_info("check CIS, is a CI CAM.\n");
|
|
}
|
|
|
|
soc_dbg_func_exit();
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
/* must has this tupple, just check length */
|
|
static td_s32 check_cimanf_id(ci_pccd_tupple_ptr tupple)
|
|
{
|
|
soc_dbg_func_enter();
|
|
if ((tupple->tupple_link < TPLLMANFID_SIZE)) {
|
|
soc_log_err("invalid CISTPL_MANFID\n");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
soc_dbg_func_exit();
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_u32 cis_read_tpcc_addr(td_u32 *tpcc_radr_data, const td_u8 *tupple_data, td_u32 len, td_u8 tpcc_rasz)
|
|
{
|
|
td_u32 tpcc_radr = 0;
|
|
td_u32 index = 0;
|
|
|
|
index++; /* head */
|
|
index++; /* head */
|
|
|
|
if (len < TUPPLE_DATA_MIN_LEN) {
|
|
soc_log_err("tupple_data len err!\n");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
switch (tpcc_rasz) {
|
|
case 0: { /* 0 means 1 byte */
|
|
tpcc_radr = tupple_data[index];
|
|
break;
|
|
}
|
|
case 1: { /* 1 means 2 byte */
|
|
tpcc_radr = tupple_data[index++];
|
|
tpcc_radr |= (td_u32)(tupple_data[index++]) << BITS_IN_1BYTE;
|
|
break;
|
|
}
|
|
case 2: { /* 2 means 3 byte */
|
|
tpcc_radr = tupple_data[index++];
|
|
tpcc_radr |= (td_u32)(tupple_data[index++]) << BITS_IN_1BYTE;
|
|
tpcc_radr |= (td_u32)(tupple_data[index++]) << BITS_IN_2BYTE;
|
|
break;
|
|
}
|
|
case 3: { /* 3 means 4 byte */
|
|
tpcc_radr = tupple_data[index++];
|
|
tpcc_radr |= (td_u32)(tupple_data[index++]) << BITS_IN_1BYTE;
|
|
tpcc_radr |= (td_u32)(tupple_data[index++]) << BITS_IN_2BYTE;
|
|
tpcc_radr |= (td_u32)(tupple_data[index++]) << BITS_IN_3BYTE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
*tpcc_radr_data = tpcc_radr;
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_s32 check_ciconfig(ci_pccd_tupple_ptr tupple, td_u32 *tpcc_radr)
|
|
{
|
|
td_u8 tpcc_sz;
|
|
td_u8 tpcc_rmsk;
|
|
td_u8 tpcc_rfsz;
|
|
td_u8 tpcc_rmsz;
|
|
td_u8 tpcc_rasz;
|
|
td_u32 local_tpcc_radr;
|
|
td_u8 subt_tupple_tag;
|
|
td_u8 subt_tupple_len;
|
|
td_u8 *psubt_tupple_value = TD_NULL;
|
|
td_u8 ret;
|
|
|
|
soc_dbg_func_enter();
|
|
|
|
if (tupple->tupple_link < 5) { /* 5: max len of link */
|
|
soc_log_err("invalid CISTPL_CONFIG\n");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
tpcc_sz = tupple->tupple_data[0];
|
|
|
|
/* don't care TPCC_LAST here: tupple->tupple_data[1]. */
|
|
tpcc_rfsz = tpcc_sz & TPCC_RFSZ_MASK;
|
|
tpcc_rmsz = tpcc_sz & TPCC_RMSZ_MASK;
|
|
tpcc_rasz = tpcc_sz & TPCC_RASZ_MASK;
|
|
|
|
ret = cis_read_tpcc_addr(&local_tpcc_radr, tupple->tupple_data, MAX_TUPPLE_BUFFER, tpcc_rasz);
|
|
if (ret == TD_FAILURE) {
|
|
soc_log_err("cis_read_tpcc_addr err.\n");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
/* tpcc_radr can't larger than 0xffe */
|
|
if (local_tpcc_radr > COR_BASE_ADDRESS_UPPERLIMIT) {
|
|
soc_log_err("invalid tpcc_radr.\n");
|
|
soc_err_print_u32(local_tpcc_radr);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
/* tpcc_rmsz should be 0, don't care tpcc_rmsk */
|
|
if (tpcc_rmsz != 0) {
|
|
soc_log_err("more than 1 configuration register\n");
|
|
soc_err_print_h32(tpcc_rmsz);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
tpcc_rmsk = tupple->tupple_data[tpcc_rasz + 3]; /* 3: index of mask */
|
|
|
|
/* sutupple */
|
|
subt_tupple_tag = tupple->tupple_data[tpcc_rasz + 4]; /* 4: index of tag */
|
|
subt_tupple_len = tupple->tupple_data[tpcc_rasz + 5]; /* 5: index of len */
|
|
psubt_tupple_value = tupple->tupple_data + tpcc_rasz + 6; /* 6: index of value */
|
|
|
|
/* DVB_CI_V1.00, 0x0241 */
|
|
if (!(((osal_memncmp(STCF_LIB, (td_char *)psubt_tupple_value + CIS_EVEN_ADDR, STCF_LIB_LEN)) == 0)
|
|
&& (psubt_tupple_value[1] == STCI_IFN_1) && (psubt_tupple_value[0] == STCI_IFN))) {
|
|
soc_log_err("check STCF_LIB fail.\n");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
*tpcc_radr = local_tpcc_radr;
|
|
|
|
soc_dbg_func_exit();
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_void check_citiming(td_u8 tpcefs, td_u8 **ptpce_io, td_u8 **ptpce_td)
|
|
{
|
|
/* timing */
|
|
if (tpcefs & MASK_BIT_2) {
|
|
switch ((**ptpce_td) & RW_SCALE_MASK) {
|
|
case NOT_READY_WAIT:
|
|
*ptpce_io = (*ptpce_td) + 1;
|
|
break;
|
|
case WAIT_SCALE_0:
|
|
case WAIT_SCALE_1:
|
|
case WAIT_SCALE_2:
|
|
*ptpce_io = (*ptpce_td) + CIS_EVEN_ADDR;
|
|
break;
|
|
case READY_SCALE_0:
|
|
case READY_SCALE_1:
|
|
case READY_SCALE_2:
|
|
case READY_SCALE_3:
|
|
case READY_SCALE_4:
|
|
case READY_SCALE_5:
|
|
case READY_SCALE_6:
|
|
*ptpce_io = (*ptpce_td) + CIS_EVEN_ADDR;
|
|
break;
|
|
default:
|
|
*ptpce_io = (*ptpce_td) + 1 + CIS_EVEN_ADDR;
|
|
break;
|
|
}
|
|
} else {
|
|
*ptpce_io = *ptpce_td;
|
|
soc_log_info("no timing descriptor.\n");
|
|
}
|
|
|
|
soc_log_info("no timing descriptor.\n");
|
|
}
|
|
|
|
static td_s32 check_cicf_table_entry(ci_pccd_tupple_ptr tupple, td_u8 *pcor_value)
|
|
{
|
|
td_u8 tpce_if;
|
|
td_u8 tpcefs;
|
|
td_u8 power_description_bits;
|
|
td_u8 tpce_indx;
|
|
td_u8 tpce_intface;
|
|
td_u8 tpce_default;
|
|
td_u8 tpce_pd_spsb; /* structure parameter selection byte */
|
|
td_u8 tpce_pd_spd; /* structure parameter definition */
|
|
td_u8 tpce_pd_spdx; /* extension structure parameter definition */
|
|
td_u8 tpce_io_range;
|
|
td_u8 tpce_io_bm; /* I/O bus mapping */
|
|
td_u8 tpce_io_al; /* I/O address line */
|
|
td_u8 tpce_io_rdb; /* I/O range descriptor byte */
|
|
td_u8 tpce_io_sol; /* size of Length I/O address range */
|
|
td_u8 tpce_io_soa; /* size of address I/O address range */
|
|
td_u8 tpce_io_nar; /* number of I:O address range */
|
|
td_u8 *ptpce_sutupple = TD_NULL;
|
|
td_u8 *ptpce_td = TD_NULL;
|
|
td_u8 *ptpce_io_rdffci = TD_NULL;
|
|
td_u8 *ptpce_io = TD_NULL;
|
|
td_u8 *ptpce_ir = TD_NULL;
|
|
td_u8 *ptpce_ms = TD_NULL;
|
|
td_u32 tpce_io_block_start;
|
|
td_u32 tpce_io_block_size;
|
|
td_u8 *ptpce_pd = TD_NULL;
|
|
td_s32 tpce_loop;
|
|
td_bool haio_space = TD_TRUE;
|
|
|
|
soc_dbg_func_enter();
|
|
|
|
tpce_indx = tupple->tupple_data[0];
|
|
tpce_intface = tpce_indx & MASK_BIT_7;
|
|
tpce_default = tpce_indx & MASK_BIT_6;
|
|
tpce_indx = tpce_indx & TPCE_INDX_MASK;
|
|
|
|
/* TPCE_INDX has both bits 6 (default) and 7 (intface) set. */
|
|
if (!(tpce_intface && tpce_default)) {
|
|
soc_log_err("CIS check error, the default bit or "
|
|
"intface bit in TPCE_INDX is not set.\n");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
tpce_if = tupple->tupple_data[1]; /* 1: index of tpce_if */
|
|
tpcefs = tupple->tupple_data[2]; /* 2: index of tpcefs */
|
|
ptpce_pd = tupple->tupple_data + 3; /* 3: index of ptpce_pd */
|
|
|
|
power_description_bits = tpcefs & (MASK_BIT_1 | MASK_BIT_0);
|
|
|
|
/* power configuration */
|
|
while (power_description_bits > 0) {
|
|
for (tpce_pd_spsb = *ptpce_pd++; tpce_pd_spsb > 0; tpce_pd_spsb >>= 1) {
|
|
if (tpce_pd_spsb & MASK_BIT_0) {
|
|
tpce_pd_spd = *ptpce_pd++;
|
|
|
|
while (*ptpce_pd & MASK_BIT_7) {
|
|
tpce_pd_spdx = *ptpce_pd++;
|
|
}
|
|
}
|
|
}
|
|
|
|
power_description_bits--;
|
|
soc_dbg_print_h32(power_description_bits);
|
|
}
|
|
ptpce_td = ptpce_pd;
|
|
ptpce_pd = TD_NULL;
|
|
|
|
check_citiming(tpcefs, &ptpce_io, &ptpce_td);
|
|
|
|
/* I/O space */
|
|
if (tpcefs & MASK_BIT_3) {
|
|
ptpce_ir = ptpce_io + 1;
|
|
tpce_io_range = (*ptpce_io) & (MASK_BIT_7);
|
|
tpce_io_bm = (((*ptpce_io) & (MASK_BIT_5)) | (MASK_BIT_6)) >> 5; /* 5-bits offset of tpce_io_bm */
|
|
tpce_io_al = (*ptpce_io) & IO_ADDLINES_MASK;
|
|
|
|
if (tpce_io_range) {
|
|
/* I/O range descriptor byte */
|
|
tpce_io_rdb = ptpce_io[1];
|
|
/* size of Length I/O address range */
|
|
tpce_io_sol = (tpce_io_rdb & (MASK_BIT_7 | MASK_BIT_6)) >> 6; /* 6-bits offset of tpce_io_sol */
|
|
/* size of address I/O address range */
|
|
tpce_io_soa = (tpce_io_rdb & (MASK_BIT_5 | MASK_BIT_4)) >> 4; /* 4-bits offset of tpce_io_soa */
|
|
/* number of I:O address range */
|
|
tpce_io_nar = (tpce_io_rdb & (MASK_BIT_3 | MASK_BIT_2 | MASK_BIT_1 | MASK_BIT_0)) + 1;
|
|
|
|
ptpce_io_rdffci = ptpce_io + CIS_EVEN_ADDR;
|
|
for (tpce_loop = 0; tpce_loop < tpce_io_nar; tpce_loop++) {
|
|
tpce_io_block_start = ptpce_io_rdffci[(tpce_io_sol + tpce_io_soa) * tpce_loop];
|
|
if (tpce_io_soa > 1) {
|
|
tpce_io_block_start |= ((td_u32)ptpce_io_rdffci[(tpce_io_sol
|
|
+ tpce_io_soa) * tpce_loop + 1]) << BITS_IN_1BYTE;
|
|
}
|
|
tpce_io_block_size = ptpce_io_rdffci[(tpce_io_sol + tpce_io_soa) * tpce_loop + tpce_io_soa] + 1;
|
|
}
|
|
|
|
ptpce_ir = ptpce_io_rdffci + (tpce_io_sol + tpce_io_soa) * tpce_io_nar;
|
|
} else {
|
|
ptpce_ir = ptpce_io + 1;
|
|
}
|
|
} else {
|
|
ptpce_ir = ptpce_io;
|
|
haio_space = TD_FALSE;
|
|
soc_log_err("CIS check error,no I/O space descriptor.");
|
|
soc_err_print_h32(tpcefs);
|
|
}
|
|
|
|
/* IRQ */
|
|
if (tpcefs & MASK_BIT_4) {
|
|
ptpce_ms = ptpce_ir + 1;
|
|
} else {
|
|
ptpce_ms = ptpce_ir;
|
|
}
|
|
|
|
/* memory space */
|
|
switch ((tpcefs & (MASK_BIT_6 | MASK_BIT_6)) >> 5) { /* 5-bits offset of memory space */
|
|
case 0: /* find ptpce_sutupple at index 0 */
|
|
ptpce_sutupple = ptpce_ms;
|
|
ptpce_ms = TD_NULL;
|
|
break;
|
|
case 1: /* find ptpce_sutupple at index 1 */
|
|
ptpce_sutupple = ptpce_ms + 1;
|
|
break;
|
|
case 2: /* find ptpce_sutupple at index 2 */
|
|
ptpce_sutupple = ptpce_ms + CIS_EVEN_ADDR;
|
|
break;
|
|
case 3: /* find ptpce_sutupple at index 3 */
|
|
ptpce_sutupple = ptpce_ms + 1 + CIS_EVEN_ADDR;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (haio_space) {
|
|
/* TPCE_IF = 04h - indicating custom interface 0. en50221 */
|
|
/* TPCE_IO is a 1-byte field with the value 22h. en50221 */
|
|
if (!((tpce_if == TPCE_IF) && (*ptpce_io == TPCE_IO))) {
|
|
return TD_FAILURE;
|
|
}
|
|
}
|
|
|
|
*pcor_value = tpce_indx;
|
|
|
|
soc_dbg_func_exit();
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 drv_ci_pccd_check_cis(ext_ci_port port, ext_ci_pccd card)
|
|
{
|
|
td_s32 ret;
|
|
ci_pccd_tupple current_tupple;
|
|
td_u16 offset = 0;
|
|
td_bool checked_oaoc = TD_FALSE;
|
|
td_bool checked_vers1 = TD_FALSE;
|
|
td_bool checked_manf_id = TD_FALSE;
|
|
td_bool checked_config = TD_FALSE;
|
|
td_bool checked_cf_table_entry = TD_FALSE;
|
|
td_bool checked_all = TD_FALSE;
|
|
td_u32 tpcc_radr;
|
|
td_u8 cor_value;
|
|
ext_ci_pccd_volt volt = CI_PCCD_VOLT_5V;
|
|
ext_ci_pccd_speed speed = CI_PCCD_SPEED_150NS;
|
|
td_bool iciplus = TD_FALSE;
|
|
td_u32 ciprof;
|
|
td_u8 acis[MAX_CIS_SIZE] = {0};
|
|
td_u32 cilen = sizeof(acis);
|
|
|
|
soc_dbg_func_enter();
|
|
|
|
/* valid CI and card and make sure they are opened. */
|
|
soc_log_check_param_return(port >= EXT_CI_PORT_1);
|
|
soc_log_check_param_return(card >= EXT_CI_PCCD_B);
|
|
|
|
CHECK_CI_PCCD_OPENED(port, card);
|
|
|
|
ret = drv_ci_pccd_read_block(port, card, acis, sizeof(acis), &cilen);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("read CIS block failed.\n");
|
|
soc_err_print_u32(port);
|
|
soc_err_print_u32(card);
|
|
return ret;
|
|
}
|
|
|
|
ciprof = 0;
|
|
cor_value = 0;
|
|
tpcc_radr = 0;
|
|
|
|
while (!checked_all) {
|
|
if (offset >= MAX_CIS_SIZE - 1) {
|
|
soc_log_err("Offset overflowed !\n");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
current_tupple.tupple_tag = acis[offset];
|
|
current_tupple.tupple_link = acis[offset + 1];
|
|
|
|
if (((offset + CIS_EVEN_ADDR + current_tupple.tupple_link) >= MAX_CIS_SIZE) ||
|
|
(current_tupple.tupple_link >= MAX_TUPPLE_BUFFER)) {
|
|
soc_log_err("Offset overflowed !\n");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
check_func_ret_return(memcpy_s(current_tupple.tupple_data, MAX_TUPPLE_BUFFER,
|
|
&acis[offset + CIS_EVEN_ADDR], current_tupple.tupple_link));
|
|
current_tupple.tupple_data[current_tupple.tupple_link] = '\0';
|
|
|
|
if (current_tupple.tupple_tag == CISTPL_END) {
|
|
soc_dbg_func_trace();
|
|
break;
|
|
}
|
|
|
|
if (current_tupple.tupple_link == 0) {
|
|
soc_dbg_func_trace();
|
|
break;
|
|
}
|
|
|
|
switch (current_tupple.tupple_tag) { /* check for the tupple tag */
|
|
case CISTPL_DEVICE_OA: /* fall-through */
|
|
case CISTPL_DEVICE_OC:
|
|
ret = check_cioa_oc(¤t_tupple, &volt, &speed);
|
|
if (ret == TD_SUCCESS) {
|
|
checked_oaoc = TD_TRUE;
|
|
soc_dbg_func_trace();
|
|
}
|
|
break;
|
|
|
|
case CISTPL_VERS_1:
|
|
ret = check_civers1(¤t_tupple, &iciplus, &ciprof);
|
|
if (ret == TD_SUCCESS) {
|
|
checked_vers1 = TD_TRUE;
|
|
soc_dbg_func_trace();
|
|
}
|
|
break;
|
|
|
|
case CISTPL_MANFID:
|
|
ret = check_cimanf_id(¤t_tupple);
|
|
if (ret == TD_SUCCESS) {
|
|
checked_manf_id = TD_TRUE;
|
|
soc_dbg_func_trace();
|
|
}
|
|
break;
|
|
|
|
case CISTPL_CONFIG:
|
|
ret = check_ciconfig(¤t_tupple, &tpcc_radr);
|
|
if (ret == TD_SUCCESS) {
|
|
checked_config = TD_TRUE;
|
|
soc_dbg_func_trace();
|
|
}
|
|
break;
|
|
|
|
case CISTPL_CFTABLE_ENTRY:
|
|
ret = check_cicf_table_entry(¤t_tupple, &cor_value);
|
|
if (ret == TD_SUCCESS) {
|
|
checked_cf_table_entry = TD_TRUE;
|
|
soc_dbg_func_trace();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
soc_dbg_func_trace();
|
|
break;
|
|
} /* end switch */
|
|
|
|
if (checked_oaoc && checked_vers1 && checked_manf_id && checked_config && checked_cf_table_entry) {
|
|
checked_all = TD_TRUE;
|
|
}
|
|
|
|
offset += CIS_EVEN_ADDR + current_tupple.tupple_link;
|
|
} /* end while */
|
|
if (!checked_all) {
|
|
soc_log_err("check_cis fail.\n");
|
|
g_ci_drv_param[port].card_param[card].attr.volt = CI_PCCD_VOLT_5V;
|
|
g_ci_drv_param[port].card_param[card].attr.speed = CI_PCCD_SPEED_150NS;
|
|
g_ci_drv_param[port].card_param[card].tpcc_radr = 0;
|
|
g_ci_drv_param[port].card_param[card].tpce_indx = 0;
|
|
soc_err_print_err_code(SOC_ERR_CI_PCCD_CIS_READ);
|
|
return ret;
|
|
} else {
|
|
g_ci_drv_param[port].card_param[card].attr.volt = volt;
|
|
if ((speed >= CI_PCCD_SPEED_MAX) || (speed < CI_PCCD_SPEED_250NS)) {
|
|
speed = CI_PCCD_SPEED_600NS;
|
|
}
|
|
|
|
g_ci_drv_param[port].card_param[card].attr.speed = speed;
|
|
g_ci_drv_param[port].card_param[card].attr.is_ciplus = iciplus;
|
|
g_ci_drv_param[port].card_param[card].attr.ciprof = ciprof;
|
|
g_ci_drv_param[port].card_param[card].tpcc_radr = tpcc_radr;
|
|
g_ci_drv_param[port].card_param[card].tpce_indx = cor_value;
|
|
|
|
ret = TD_SUCCESS;
|
|
}
|
|
|
|
soc_dbg_func_exit();
|
|
return ret;
|
|
} /* ci_pccd_check_cis */
|
|
|
|
/***********************************************************************************
|
|
* function: ci_pccd_write_cor
|
|
* Description: write TPCE_INDX to TPCC_RADR
|
|
* please refer to EN50221 specification.
|
|
*********************************************************************************/
|
|
td_s32 drv_ci_pccd_write_cor(ext_ci_port port, ext_ci_pccd card)
|
|
{
|
|
td_u16 addr;
|
|
td_u8 val;
|
|
|
|
soc_dbg_func_enter();
|
|
|
|
/* valid CI and card and make sure they are opened. */
|
|
soc_log_check_param_return(port >= EXT_CI_PORT_MAX);
|
|
soc_log_check_param_return(card >= EXT_CI_PCCD_B);
|
|
|
|
CHECK_CI_PCCD_OPENED(port, card);
|
|
|
|
addr = g_ci_drv_param[port].card_param[card].tpcc_radr;
|
|
val = g_ci_drv_param[port].card_param[card].tpce_indx;
|
|
|
|
if ((addr == 0x00) || (addr > COR_BASE_ADDRESS_UPPERLIMIT)) {
|
|
soc_err_print_u32(port);
|
|
soc_err_print_u32(card);
|
|
soc_log_err("invalid COR address.\n");
|
|
soc_err_print_h32(addr);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
/* if registe io_write function, use it to write directly */
|
|
hal_ci_pccd_mem_write_byte(port, card, addr, val);
|
|
|
|
/* Check write OK ? */
|
|
hal_ci_pccd_mem_read_byte(port, card, addr, &val);
|
|
if (val != g_ci_drv_param[port].card_param[card].tpce_indx) {
|
|
soc_log_err("write_cor failed.\n");
|
|
soc_err_print_err_code(SOC_ERR_CI_ATTR_WRITE_ERR);
|
|
return SOC_ERR_CI_ATTR_WRITE_ERR;
|
|
}
|
|
|
|
soc_dbg_func_exit();
|
|
return TD_SUCCESS;
|
|
} /* ci_pccd_write_cor */
|
|
|
|
/***********************************************************************************
|
|
* function: ci_pccd_io_reset
|
|
* Description: reset the IO interface
|
|
*********************************************************************************/
|
|
td_s32 drv_ci_pccd_io_reset(ext_ci_port port, ext_ci_pccd card)
|
|
{
|
|
td_u16 retry_times = 0;
|
|
td_u8 status = 0;
|
|
|
|
soc_dbg_func_enter();
|
|
|
|
/* valid CI and card and make sure they are opened. */
|
|
soc_log_check_param_return(port >= EXT_CI_PORT_MAX);
|
|
soc_log_check_param_return(card >= EXT_CI_PCCD_B);
|
|
|
|
CHECK_CI_PCCD_OPENED(port, card);
|
|
|
|
/* reset the command register */
|
|
check_func_ret_return(hal_ci_pccd_io_write_byte(port, card, COM_STAT_REG, PCCD_SETRS));
|
|
osal_msleep(CI_TIME_10MS);
|
|
|
|
/* clear the reset bit */
|
|
check_func_ret_return(hal_ci_pccd_io_write_byte(port, card, COM_STAT_REG, PCCD_CLEAR));
|
|
|
|
/* wait for free */
|
|
for (retry_times = 0; retry_times < CI_PCCD_IORESET_TIMEOUT; retry_times++) {
|
|
check_func_ret_return(hal_ci_pccd_io_read_byte(port, card, COM_STAT_REG, &status));
|
|
if (status & PCCD_FR) {
|
|
break;
|
|
}
|
|
osal_msleep(CI_TIME_10MS);
|
|
}
|
|
|
|
if (!(status & PCCD_FR)) {
|
|
soc_err_print_u32(port);
|
|
soc_err_print_u32(card);
|
|
soc_log_err("CI PCCD card ioreset timeout.\n");
|
|
soc_err_print_err_code(SOC_ERR_CI_TIMEOUT);
|
|
return SOC_ERR_CI_TIMEOUT;
|
|
}
|
|
|
|
soc_dbg_func_exit();
|
|
return TD_SUCCESS;
|
|
} /* ci_pccd_io_reset */
|
|
|
|
/***********************************************************************************
|
|
* function: ci_pccd_neg_buffer_size
|
|
* Description: negotiate about buffer size
|
|
*********************************************************************************/
|
|
td_s32 drv_ci_pccd_neg_buffer_size(ext_ci_port port, ext_ci_pccd card, td_u16 *buffer_size)
|
|
{
|
|
td_u8 size[IO_DATA_LEN] = { 0, 0 };
|
|
td_u16 retry_cnt;
|
|
td_u16 data_size;
|
|
td_u16 temp_size;
|
|
td_u16 hosize;
|
|
td_u8 buffer_count;
|
|
td_u8 data_low;
|
|
td_u8 data_high;
|
|
td_u8 status = 0;
|
|
|
|
soc_dbg_func_enter();
|
|
|
|
/* valid CI and card and make sure they are opened. */
|
|
soc_log_check_param_return(port >= EXT_CI_PORT_MAX);
|
|
soc_log_check_param_return(card >= EXT_CI_PCCD_B);
|
|
soc_log_check_param_return(buffer_size == TD_NULL);
|
|
|
|
/* get host buffer size */
|
|
hosize = *buffer_size;
|
|
|
|
CHECK_CI_PCCD_OPENED(port, card);
|
|
|
|
/* set size read bit */
|
|
check_func_ret_return(hal_ci_pccd_io_write_byte(port, card, COM_STAT_REG, PCCD_SETSR));
|
|
|
|
/* wait for Data available */
|
|
for (retry_cnt = 0; retry_cnt < CI_PCCD_NEGBUFFER_TIMEOUT; retry_cnt++) {
|
|
check_func_ret_return(hal_ci_pccd_io_read_byte(port, card, COM_STAT_REG, &status));
|
|
if (status & PCCD_DA) {
|
|
break;
|
|
}
|
|
osal_msleep(CI_TIME_1MS);
|
|
}
|
|
|
|
if (!(status & PCCD_DA)) {
|
|
soc_log_err("CI PCCD card, set SR bit time out\n");
|
|
soc_err_print_u32(port);
|
|
soc_err_print_u32(card);
|
|
soc_err_print_err_code(SOC_ERR_CI_TIMEOUT);
|
|
return SOC_ERR_CI_TIMEOUT;
|
|
}
|
|
|
|
/* read buffer size's size */
|
|
check_func_ret_return(hal_ci_pccd_io_read_byte(port, card, SIZE_L_REG, &data_low));
|
|
check_func_ret_return(hal_ci_pccd_io_read_byte(port, card, SIZE_H_REG, &data_high));
|
|
data_size = data_low | (data_high << BITS_IN_1BYTE);
|
|
|
|
if ((data_size == 0) || (data_size > IO_DATA_LEN)) {
|
|
soc_log_err("CI PCCD card, buffer size's size is error.\n");
|
|
soc_err_print_u32(port);
|
|
soc_err_print_u32(card);
|
|
soc_err_print_u32(data_size);
|
|
soc_err_print_err_code(SOC_ERR_CI_IO_READ_ERR);
|
|
return SOC_ERR_CI_IO_READ_ERR;
|
|
}
|
|
|
|
/* read buffer size */
|
|
temp_size = 0;
|
|
for (buffer_count = 0; buffer_count < data_size; buffer_count++) {
|
|
check_func_ret_return(hal_ci_pccd_io_read_byte(port, card, DATA_REG, &size[buffer_count]));
|
|
temp_size = (temp_size << BITS_IN_1BYTE) | size[buffer_count];
|
|
}
|
|
|
|
/* clear command register */
|
|
check_func_ret_return(hal_ci_pccd_io_write_byte(port, card, COM_STAT_REG, PCCD_CLEAR));
|
|
|
|
if (hosize != 0) {
|
|
if (temp_size > hosize) {
|
|
temp_size = hosize;
|
|
size[0] = (td_u8)(temp_size >> BITS_IN_1BYTE);
|
|
size[1] = (td_u8)(temp_size & 0x00ff);
|
|
data_size = IO_DATA_LEN;
|
|
}
|
|
}
|
|
|
|
if (temp_size < CI_PCCD_MIN_BUFFERSIZE) {
|
|
soc_log_err("CI PCCD card, buffer size too small.\n");
|
|
soc_err_print_u32(port);
|
|
soc_err_print_u32(card);
|
|
soc_err_print_u32(temp_size);
|
|
soc_err_print_err_code(SOC_ERR_CI_IO_READ_ERR);
|
|
return SOC_ERR_CI_IO_READ_ERR;
|
|
}
|
|
|
|
osal_msleep(CI_TIME_1MS);
|
|
|
|
/* set size write bit */
|
|
check_func_ret_return(hal_ci_pccd_io_write_byte(port, card, COM_STAT_REG, PCCD_SETSW));
|
|
|
|
/* wait for free */
|
|
for (retry_cnt = 0; retry_cnt < CI_PCCD_NEGBUFFER_TIMEOUT; retry_cnt++) {
|
|
check_func_ret_return(hal_ci_pccd_io_read_byte(port, card, COM_STAT_REG, &status));
|
|
if (status & PCCD_FR) {
|
|
break;
|
|
}
|
|
osal_msleep(CI_TIME_1MS);
|
|
}
|
|
|
|
if (!(status & PCCD_FR)) {
|
|
soc_log_err("CI PCCD card, set SW bit time out.\n");
|
|
soc_err_print_u32(port);
|
|
soc_err_print_u32(card);
|
|
soc_err_print_err_code(SOC_ERR_CI_TIMEOUT);
|
|
return SOC_ERR_CI_TIMEOUT;
|
|
}
|
|
|
|
/* set host control */
|
|
for (retry_cnt = 0; retry_cnt < CI_PCCD_NEGBUFFER_TIMEOUT; retry_cnt++) {
|
|
check_func_ret_return(hal_ci_pccd_io_write_byte(port, card, COM_STAT_REG, PCCD_SETHC));
|
|
osal_msleep(CI_TIME_1MS);
|
|
status = 0;
|
|
check_func_ret_return(hal_ci_pccd_io_read_byte(port, card, COM_STAT_REG, &status));
|
|
if (status & PCCD_FR) {
|
|
break;
|
|
}
|
|
|
|
check_func_ret_return(hal_ci_pccd_io_write_byte(port, card, COM_STAT_REG, PCCD_CLEAR));
|
|
osal_msleep(CI_TIME_1MS);
|
|
}
|
|
|
|
if (!(status & PCCD_FR)) {
|
|
soc_log_err("CI PCCD card, set HC bit time out\n");
|
|
soc_err_print_u32(port);
|
|
soc_err_print_u32(card);
|
|
soc_err_print_err_code(SOC_ERR_CI_TIMEOUT);
|
|
return SOC_ERR_CI_TIMEOUT;
|
|
}
|
|
|
|
/* write buffer size's size */
|
|
check_func_ret_return(hal_ci_pccd_io_write_byte(port, card, SIZE_L_REG, (td_u8)(data_size & 0xff)));
|
|
check_func_ret_return(hal_ci_pccd_io_write_byte(port, card, SIZE_H_REG, (td_u8)(data_size >> BITS_IN_1BYTE)));
|
|
|
|
/* write buffer size */
|
|
for (buffer_count = 0; buffer_count < data_size; buffer_count++) {
|
|
check_func_ret_return(hal_ci_pccd_io_write_byte(port, card, DATA_REG, size[buffer_count]));
|
|
status = 0;
|
|
check_func_ret_return(hal_ci_pccd_io_read_byte(port, card, COM_STAT_REG, &status));
|
|
if (!(status & PCCD_WE)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* clear command register and wait f_ree */
|
|
check_func_ret_return(hal_ci_pccd_io_write_byte(port, card, COM_STAT_REG, PCCD_CLEAR));
|
|
*buffer_size = temp_size;
|
|
|
|
/* malloc buffer: if buffer had been allocated, free it and allocate it using new buffer size. */
|
|
if (g_ci_drv_param[port].card_param[card].buffer != TD_NULL) {
|
|
soc_vfree_ci(g_ci_drv_param[port].card_param[card].buffer);
|
|
g_ci_drv_param[port].card_param[card].buffer = TD_NULL;
|
|
}
|
|
|
|
g_ci_drv_param[port].card_param[card].buffer = soc_vmalloc_ci(temp_size);
|
|
if (g_ci_drv_param[port].card_param[card].buffer != TD_NULL) {
|
|
g_ci_drv_param[port].card_param[card].buffer_size = temp_size;
|
|
} else {
|
|
soc_log_err("CI PCCD card alloc memory fail.\n");
|
|
soc_err_print_u32(port);
|
|
soc_err_print_u32(card);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
soc_dbg_func_exit();
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 drv_ci_pccd_ts_ctrl(ext_ci_port port, ext_ci_pccd card, ext_ci_pccd_tsctrl cmd,
|
|
const ext_ci_pccd_tsctrl_param *param)
|
|
{
|
|
td_s32 ret;
|
|
|
|
soc_dbg_func_enter();
|
|
|
|
soc_log_check_param_return(port >= EXT_CI_PORT_MAX);
|
|
soc_log_check_param_return(card >= EXT_CI_PCCD_B);
|
|
soc_log_check_param_return(cmd != EXT_CI_PCCD_TSCTRL_BYPASS);
|
|
soc_log_check_param_return(param == TD_NULL);
|
|
|
|
CHECK_CI_PCCD_OPENED(port, card);
|
|
|
|
ret = hal_ci_pccd_ts_by_pass(port, card, param->bypass.ts_bypass);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(hal_ci_pccd_ts_by_pass, ret);
|
|
soc_err_print_u32(port);
|
|
soc_err_print_u32(card);
|
|
return ret;
|
|
}
|
|
|
|
soc_dbg_func_exit();
|
|
return ret;
|
|
}
|
|
|
|
td_s32 drv_ci_pccd_dbg_io_print_ctrl(ext_ci_port port, ext_ci_pccd card, td_bool print)
|
|
{
|
|
soc_dbg_func_enter();
|
|
|
|
soc_log_check_param_return(port >= EXT_CI_PORT_MAX);
|
|
soc_log_check_param_return(card >= EXT_CI_PCCD_B);
|
|
|
|
g_ci_drv_param[port].card_param[card].print_io_data = print;
|
|
soc_dbg_func_exit();
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 drv_ci_pccd_get_debug_info(ext_ci_port port, ext_ci_pccd card, ci_pccd_debuginfo_ptr debug_info)
|
|
{
|
|
td_s32 ret;
|
|
|
|
soc_dbg_func_enter();
|
|
|
|
soc_log_check_param_return(port >= EXT_CI_PORT_MAX);
|
|
soc_log_check_param_return(card >= EXT_CI_PCCD_B);
|
|
soc_log_check_param_return(debug_info == NULL);
|
|
|
|
/* detect card */
|
|
ret = hal_ci_pccd_detect(port, card, &(debug_info->status));
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(ci_pccd_detect, ret);
|
|
soc_err_print_u32(port);
|
|
soc_err_print_u32(card);
|
|
return ret;
|
|
}
|
|
|
|
/* ready or busy */
|
|
ret = hal_ci_pccd_ready_or_busy(port, card, &(debug_info->ready));
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(hal_ci_pccd_ready_or_busy, ret);
|
|
soc_err_print_u32(port);
|
|
soc_err_print_u32(card);
|
|
return ret;
|
|
}
|
|
|
|
/* get TS mode */
|
|
ret = hal_ci_pccd_get_bypass_mode(port, card, &(debug_info->by_pass));
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(hal_ci_pccd_get_bypass_mode, ret);
|
|
soc_err_print_u32(port);
|
|
soc_err_print_u32(card);
|
|
return ret;
|
|
}
|
|
|
|
debug_info->is_ciplus = g_ci_drv_param[port].card_param[card].attr.is_ciplus;
|
|
debug_info->buffer_size = g_ci_drv_param[port].card_param[card].buffer_size;
|
|
debug_info->attr = g_ci_drv_param[port].attr;
|
|
debug_info->io_cnt = g_ci_drv_param[port].card_param[card].io_cnt;
|
|
|
|
soc_dbg_func_exit();
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 drv_ci_pccd_write_cor_ex(ext_ci_port port, ext_ci_pccd card, td_u16 addr, td_u8 data)
|
|
{
|
|
td_u8 cor_index;
|
|
|
|
soc_dbg_func_enter();
|
|
|
|
/* valid CI and card and make sure they are opened. */
|
|
soc_log_check_param_return(port >= EXT_CI_PORT_MAX);
|
|
soc_log_check_param_return(card >= EXT_CI_PCCD_B);
|
|
|
|
CHECK_CI_PCCD_OPENED(port, card);
|
|
|
|
if (addr > COR_BASE_ADDRESS_UPPERLIMIT) {
|
|
soc_log_err("invalid COR_RADR\n");
|
|
soc_err_print_h32(addr);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
/* writing the COR index to the COR address */
|
|
hal_ci_pccd_mem_write_byte(port, card, addr, data);
|
|
soc_log_info("card write data to addr.\n");
|
|
|
|
cor_index = 0;
|
|
hal_ci_pccd_mem_read_byte(port, card, addr, &cor_index);
|
|
if (cor_index != data) {
|
|
soc_log_err("card write data to addr failed,read cor index.\n");
|
|
soc_err_print_u32(port);
|
|
soc_err_print_h32(data);
|
|
soc_err_print_h32(addr);
|
|
soc_err_print_h32(cor_index);
|
|
return SOC_ERR_CI_ATTR_WRITE_ERR;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 drv_ci_pccd_get_cis(ext_ci_port port, ext_ci_pccd card, td_u8 *cis, td_u32 cis_len, td_u32 *cilen)
|
|
{
|
|
td_s32 ret;
|
|
td_ulong ret_cp;
|
|
td_u32 len = 0;
|
|
td_u8 acis[MAX_CIS_SIZE] = {0};
|
|
|
|
soc_log_check_param_return(port >= EXT_CI_PORT_MAX);
|
|
soc_log_check_param_return(card >= EXT_CI_PCCD_B);
|
|
soc_log_check_param_return(cilen == NULL);
|
|
|
|
CHECK_CI_PCCD_OPENED(port, card);
|
|
|
|
if (cis == TD_NULL) {
|
|
soc_log_err("invalid para.\n");
|
|
return SOC_ERR_CI_INVALID_PARA;
|
|
}
|
|
|
|
ret = drv_ci_pccd_read_block(port, card, acis, sizeof(acis), &len);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("drv_ci_pccd_read_block fail.\n");
|
|
soc_err_print_call_fun_err(drv_ci_pccd_read_block, ret);
|
|
soc_err_print_u32(port);
|
|
soc_err_print_u32(card);
|
|
soc_err_print_h32(len);
|
|
return ret;
|
|
}
|
|
|
|
if ((cis_len < len) || (len > MAX_CIS_SIZE)) {
|
|
soc_log_err("drv_ci_pccd_read_block fail.\n");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
ret_cp = osal_copy_to_user(cis, acis, len);
|
|
if (ret_cp != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(osal_copy_to_user, ret_cp);
|
|
return TD_FAILURE;
|
|
}
|
|
*cilen = len;
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|