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

/*
* 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(&current_tupple, &volt, &speed);
if (ret == TD_SUCCESS) {
checked_oaoc = TD_TRUE;
soc_dbg_func_trace();
}
break;
case CISTPL_VERS_1:
ret = check_civers1(&current_tupple, &iciplus, &ciprof);
if (ret == TD_SUCCESS) {
checked_vers1 = TD_TRUE;
soc_dbg_func_trace();
}
break;
case CISTPL_MANFID:
ret = check_cimanf_id(&current_tupple);
if (ret == TD_SUCCESS) {
checked_manf_id = TD_TRUE;
soc_dbg_func_trace();
}
break;
case CISTPL_CONFIG:
ret = check_ciconfig(&current_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(&current_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;
}