/* * 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; }