948 lines
26 KiB
C
948 lines
26 KiB
C
#include "sys_config.h"
|
|
#include "typesdef.h"
|
|
#include "devid.h"
|
|
#include "list.h"
|
|
#include "dev.h"
|
|
#include "osal/task.h"
|
|
#include "osal/semaphore.h"
|
|
#include "osal/mutex.h"
|
|
#include "lib/sdhost/sdhost.h"
|
|
#include "hal/gpio.h"
|
|
#include "osal/irq.h"
|
|
#include "osal/string.h"
|
|
#include "osal/mutex.h"
|
|
#include "osal/irq.h"
|
|
#include "osal/task.h"
|
|
#include "osal/sleep.h"
|
|
#include "osal/timer.h"
|
|
|
|
|
|
struct hgsdh_hw
|
|
{
|
|
__IO uint32 CFG;
|
|
__IO uint32 BAUD;
|
|
__IO uint32 CPTR;
|
|
__IO uint32 DPTR;
|
|
__IO uint32 DCNT;
|
|
#if TXW81X
|
|
__IO uint32 STA;
|
|
__IO uint32 CFG1;
|
|
__IO uint32 ISMP;
|
|
#endif
|
|
};
|
|
|
|
|
|
static uint8 sdhc_cmd_rsp_buf[32]__attribute__ ((aligned(4)));
|
|
#if TXW81X
|
|
static uint8 sdhc_dat_ping_buf0[512];
|
|
static uint8 sdhc_dat_ping_buf1[512];
|
|
#endif
|
|
void delay_us(uint32 n);
|
|
|
|
struct sdh_device *sdh_irq;
|
|
|
|
static void ll_sdhc_set_bus_clk(struct hgsdh_hw *p_sdhc, uint32 sys_clk, uint32 bus_clk)
|
|
{
|
|
p_sdhc->BAUD = ((sys_clk + bus_clk - 1)/ bus_clk) - 1;
|
|
}
|
|
|
|
static void ll_sdhc_set_ioe(struct hgsdh_hw *p_sdhc, TYPE_LL_SDHC_IOE ioe)
|
|
{
|
|
if (ioe == LL_SDHC_IROF) {
|
|
p_sdhc->CFG &= ~LL_SDHC_ODAT_RISING_EDGE;
|
|
} else {
|
|
p_sdhc->CFG |= LL_SDHC_ODAT_RISING_EDGE;
|
|
}
|
|
}
|
|
|
|
static void ll_sdhc_set_dat_width(struct hgsdh_hw *p_sdhc, TYPE_LL_SDHC_DAT_WIDTH width)
|
|
{
|
|
if (width == LL_SDHC_DAT_4BIT) {
|
|
p_sdhc->CFG |= LL_SDHC_DAT_WIDTH_4BIT;
|
|
} else {
|
|
p_sdhc->CFG &= ~LL_SDHC_DAT_WIDTH_4BIT;
|
|
}
|
|
}
|
|
|
|
static void ll_sdhc_clk_suspend_ctrl(struct hgsdh_hw *p_sdhc, uint8 suspend_enable)
|
|
{
|
|
if (suspend_enable) {
|
|
p_sdhc->CFG &= ~LL_SDHC_CLK_OUT_EN;
|
|
} else {
|
|
p_sdhc->CFG |= LL_SDHC_CLK_OUT_EN;
|
|
}
|
|
}
|
|
|
|
static void ll_sdhc_post_8clk_ctrl(struct hgsdh_hw *p_sdhc, uint8 enable)
|
|
{
|
|
if (enable) {
|
|
p_sdhc->CFG |= LL_SDHC_8CLK_AFTER_EN;
|
|
//p_sdhc->CFG |= LL_SDHC_8CLK_BEFORE_EN;
|
|
} else {
|
|
p_sdhc->CFG &= ~LL_SDHC_8CLK_AFTER_EN;
|
|
//p_sdhc->CFG &= ~LL_SDHC_8CLK_BEFORE_EN;
|
|
}
|
|
}
|
|
|
|
static void ll_sdhc_before_8clk_ctrl(struct hgsdh_hw *p_sdhc, uint8 enable)
|
|
{
|
|
if (enable) {
|
|
p_sdhc->CFG |= LL_SDHC_8CLK_BEFORE_EN;
|
|
} else {
|
|
p_sdhc->CFG &= ~LL_SDHC_8CLK_BEFORE_EN;
|
|
}
|
|
}
|
|
static void ll_sdhc_8clk_ctrl(struct hgsdh_hw *p_sdhc, uint8 enable)
|
|
{
|
|
ll_sdhc_before_8clk_ctrl(p_sdhc, enable);
|
|
}
|
|
static void ll_sdhc_cmd_intr_enable(struct hgsdh_hw *p_sdhc, uint8 enable)
|
|
{
|
|
if (enable) {
|
|
p_sdhc->CFG |= LL_SDHC_CMD_INTR_EN;
|
|
} else {
|
|
p_sdhc->CFG &= ~LL_SDHC_CMD_INTR_EN;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief ll_sdhc_set_dat_width
|
|
* @param p_sdhc : sdhc register structure pointer
|
|
* @retval none
|
|
*/
|
|
static void ll_sdhc_dat_intr_enable(struct hgsdh_hw *p_sdhc, uint8 enable)
|
|
{
|
|
if (enable) {
|
|
p_sdhc->CFG |= LL_SDHC_DAT_INTR_EN;
|
|
} else {
|
|
p_sdhc->CFG &= ~LL_SDHC_DAT_INTR_EN;
|
|
}
|
|
}
|
|
|
|
static void ll_sdhc_clr_cmd_done_pending(struct hgsdh_hw *p_sdhc)
|
|
{
|
|
p_sdhc->CFG |= LL_SDHC_CMD_DONE_CLR;
|
|
}
|
|
|
|
#ifdef TXW81X
|
|
static void ll_sdhc_cmd_sample_enable(struct hgsdh_hw *p_sdhc, uint8 enable)
|
|
{
|
|
if (enable)
|
|
{
|
|
p_sdhc->CFG1 |= LL_SDHC_CMD_SMP_EN;
|
|
}else{
|
|
p_sdhc->CFG1 &= ~LL_SDHC_CMD_SMP_EN;
|
|
}
|
|
}
|
|
|
|
static void ll_sdhc_dat_sample_enable(struct hgsdh_hw *p_sdhc, uint8 enable)
|
|
{
|
|
if (enable)
|
|
{
|
|
p_sdhc->CFG1 |= LL_SDHC_DAT_SMP_EN;
|
|
}else{
|
|
p_sdhc->CFG1 &= ~LL_SDHC_DAT_SMP_EN;
|
|
}
|
|
}
|
|
|
|
static void ll_sdhc_delay_clock(struct hgsdh_hw *p_sdhc, TYPE_LL_SDHC_DELAY_SYSCLK type , uint8_t delay)
|
|
{
|
|
if (type == LL_SDHC_DLY_HALF_SYSCLK)
|
|
{
|
|
p_sdhc->CFG1 &= ~0xff;
|
|
p_sdhc->CFG1 |= LL_SDHC_DELAY_HALF_SYSCLK;
|
|
}else if(type == LL_SDHC_DLY_ONE_SYSCLK){
|
|
p_sdhc->CFG1 &= ~0xff;
|
|
p_sdhc->CFG1 |= LL_SDHC_DELAY_WHOLE_SYSCLK;
|
|
}else if(type == LL_SDHC_DLY_CHAIN){
|
|
p_sdhc->CFG1 &= ~0xff;
|
|
p_sdhc->CFG1 |= LL_SDHC_DELAY_CHAIN_EN;
|
|
p_sdhc->CFG1 |= delay;
|
|
}else{
|
|
p_sdhc->CFG1 &= ~0xff;
|
|
}
|
|
}
|
|
|
|
static void ll_sdhc_dat_overflow_stop_enable(struct hgsdh_hw *p_sdhc, uint8_t enable)
|
|
{
|
|
if (enable)
|
|
{
|
|
p_sdhc->CFG1 |= LL_SDHC_DAT_OVERFLOW_STOP_EN;
|
|
}else{
|
|
p_sdhc->CFG1 &= ~LL_SDHC_DAT_OVERFLOW_STOP_EN;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static bool ll_sdhc_get_cmd_done_pending(struct hgsdh_hw *p_sdhc)
|
|
{
|
|
return ((p_sdhc->CFG & LL_SDHC_CMD_DONE)) ? TRUE : FALSE;
|
|
}
|
|
|
|
static bool ll_sdhc_get_cmd_rsp_timeout_pending(struct hgsdh_hw *p_sdhc)
|
|
{
|
|
return (p_sdhc->CFG & LL_SDHC_RSP_TIMEOUT) ? TRUE : FALSE;
|
|
}
|
|
|
|
static bool ll_sdhc_get_cmd_rsp_err_pending(struct hgsdh_hw *p_sdhc)
|
|
{
|
|
return (p_sdhc->CFG & LL_SDHC_RSP_CRC_ERR) ? TRUE : FALSE;
|
|
}
|
|
|
|
static void ll_sdhc_clr_dat_done_pending(struct hgsdh_hw *p_sdhc)
|
|
{
|
|
p_sdhc->CFG |= LL_SDHC_DAT_DONE_CLR;
|
|
}
|
|
|
|
static bool ll_sdhc_get_dat_done_pending(struct hgsdh_hw *p_sdhc)
|
|
{
|
|
return ((p_sdhc->CFG & LL_SDHC_DAT_DONE)) ? TRUE : FALSE;
|
|
}
|
|
|
|
static bool ll_sdhc_get_rd_dat_err_pending(struct hgsdh_hw *p_sdhc)
|
|
{
|
|
return (p_sdhc->CFG & LL_SDHC_DAT_CRC_ERR) ? TRUE : FALSE;
|
|
}
|
|
|
|
static bool ll_sdhc_get_wr_dat_err_pending(struct hgsdh_hw *p_sdhc)
|
|
{
|
|
return ((p_sdhc->CFG & LL_SDHC_CRC_STA) != (2<<16)) ? TRUE : FALSE;
|
|
}
|
|
|
|
static void ll_sdhc_read_data_kick(struct hgsdh_hw *p_sdhc, uint32 addr, uint32 len)
|
|
{
|
|
p_sdhc->CFG &= ~LL_SDHC_DAT_RW_MSK;
|
|
p_sdhc->CFG &= ~LL_SDHC_CLK_OUT_EN;
|
|
ll_sdhc_clr_dat_done_pending(p_sdhc);
|
|
|
|
//os_printf("\r\nDPTR:%x\r\n",addr);
|
|
p_sdhc->DPTR = addr;
|
|
p_sdhc->DCNT = len & (~ 0x3UL);
|
|
p_sdhc->CFG |= LL_SDHC_DAT_RCV;
|
|
}
|
|
|
|
static void ll_sdhc_write_data_kick(struct hgsdh_hw *p_sdhc, uint32 addr, uint32 len)
|
|
{
|
|
p_sdhc->CFG &= ~LL_SDHC_DAT_RW_MSK;
|
|
ll_sdhc_clr_dat_done_pending(p_sdhc);
|
|
|
|
p_sdhc->DPTR = addr;
|
|
p_sdhc->DCNT = len & (~ 0x3UL);
|
|
p_sdhc->CFG |= LL_SDHC_DAT_SEND_WAIT_BUSY;
|
|
}
|
|
|
|
static bool ll_sdhc_read_data_wait(struct hgsdh_hw *p_sdhc, uint32 time_ms)
|
|
{
|
|
do {
|
|
|
|
} while (!ll_sdhc_get_dat_done_pending(p_sdhc));
|
|
|
|
|
|
if (ll_sdhc_get_rd_dat_err_pending(p_sdhc)) {
|
|
os_printf("dat err\r\n");
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static bool ll_sdhc_write_data_wait(struct hgsdh_hw *p_sdhc, uint32 time_ms)
|
|
{
|
|
uint64 time = os_jiffies();
|
|
do {
|
|
if ((os_jiffies() - time) > time_ms) {
|
|
return FALSE;
|
|
}
|
|
} while (!ll_sdhc_get_dat_done_pending(p_sdhc));
|
|
|
|
if (ll_sdhc_get_wr_dat_err_pending(p_sdhc)) {
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void hgic_sdc_irq_update(struct sdh_device *host, int32_t enable)
|
|
{
|
|
struct hgsdh *sdh_hw = (struct hgsdh*)host;
|
|
struct hgsdh_hw *hw = (struct hgsdh_hw *)sdh_hw->hw;
|
|
|
|
if (enable) {
|
|
os_printf("enable sdio irq");
|
|
} else {
|
|
os_printf("disable sdio irq");
|
|
}
|
|
#if CMD_ISR_EN
|
|
ll_sdhc_cmd_intr_enable(hw, enable);
|
|
#endif
|
|
#if DAT_ISR_EN
|
|
ll_sdhc_dat_intr_enable(hw, enable);
|
|
#endif
|
|
// ll_sdhc_dat1_intr_enable(host->p_sdhc, enable);
|
|
|
|
}
|
|
|
|
|
|
|
|
//volatile uint8 sd_8clk_open = 0;
|
|
void ll_sdhc_config(struct sdh_device *sdhost,struct hgsdh_hw * p_sdhc, TYPE_LL_SDHC_CFG *p_cfg)
|
|
{
|
|
ll_sdhc_set_bus_clk(p_sdhc, peripheral_clock_get(HG_AHB_PT_SDMMC), p_cfg->bus_clk);
|
|
ll_sdhc_set_ioe(p_sdhc, p_cfg->ioe);
|
|
ll_sdhc_set_dat_width(p_sdhc, p_cfg->width);
|
|
|
|
ll_sdhc_clk_suspend_ctrl(p_sdhc, 0);
|
|
ll_sdhc_8clk_ctrl(p_sdhc, sdhost->sd_8clk_open);
|
|
|
|
p_sdhc->CFG |= LL_SDHC_EN;
|
|
#ifdef TXW81X
|
|
p_sdhc->CFG1 |= (LL_SDHC_DAT_OVERFLOW_STOP_EN);
|
|
#endif
|
|
}
|
|
|
|
void ll_sdhc_close(struct hgsdh_hw * p_sdhc)
|
|
{
|
|
p_sdhc->CFG &= ~LL_SDHC_EN;
|
|
}
|
|
|
|
#ifdef TXW81X
|
|
void ll_sdhc_set_sample(struct hgsdh_hw *p_sdhc, uint8_t cmd_sample, uint8_t dat_sample)
|
|
{
|
|
p_sdhc->ISMP = (dat_sample << 16) | cmd_sample;
|
|
}
|
|
#endif
|
|
|
|
volatile uint8 sd_cmd_isr_get = 0;
|
|
volatile uint8 sd_dat_isr_get = 0;
|
|
|
|
int32 sdh_cmd(struct sdh_device *sdhost,struct rt_mmcsd_cmd* cmd)
|
|
{
|
|
uint32 rsp_type;
|
|
uint32 cmd_tick;
|
|
//uint32 cur_tick;
|
|
struct hgsdh *sdh_hw = (struct hgsdh*)sdhost;
|
|
struct hgsdh_hw *p_sdhc = (struct hgsdh_hw *)sdh_hw->hw;
|
|
sdhc_cmd_rsp_buf[0] = cmd->cmd_code | 0x40;
|
|
//put_unaligned_be32(cmd->arg, &sdhc_cmd_rsp_buf[1]);
|
|
sdhc_cmd_rsp_buf[1] = (cmd->arg >> 24) & 0xFF;
|
|
sdhc_cmd_rsp_buf[2] = (cmd->arg >> 16) & 0xFF;
|
|
sdhc_cmd_rsp_buf[3] = (cmd->arg >> 8) & 0xFF;
|
|
sdhc_cmd_rsp_buf[4] = (cmd->arg >> 0) & 0xFF;
|
|
p_sdhc->CPTR = (uint32)sdhc_cmd_rsp_buf;
|
|
|
|
|
|
rsp_type = ((cmd->flags & MMC_RSP_PRESENT) ? BIT(5) : 0) |
|
|
((cmd->flags & MMC_RSP_136) ? BIT(6) : 0) |
|
|
((cmd->flags & MMC_RSP_BUSY) ? BIT(7) : 0);
|
|
|
|
if (0 == rsp_type) {
|
|
rsp_type = (0x2 << 5);
|
|
}
|
|
//ll_sdhc_post_8clk_ctrl(p_sdhc, sd_8clk_open);
|
|
ll_sdhc_clr_cmd_done_pending(p_sdhc);
|
|
// os_printf("cmd:%d %x\r\n",cmd->cmd_code,(p_sdhc->CFG & ~LL_SDHC_CMD_RESP_MSK) | rsp_type);
|
|
p_sdhc->CFG = (p_sdhc->CFG & ~LL_SDHC_CMD_RESP_MSK) | rsp_type;
|
|
|
|
if(cmd->cmd_code == STOP_TRANSMISSION)
|
|
{
|
|
cmd_tick = ~0;
|
|
}
|
|
else
|
|
{
|
|
cmd_tick = 0x1fffff;
|
|
}
|
|
//ll_gpio_bit_reset(GPIOB,BIT(5));
|
|
while (!ll_sdhc_get_cmd_done_pending(p_sdhc)&&!sd_cmd_isr_get)
|
|
{
|
|
if (--cmd_tick == 0) {
|
|
//ll_gpio_bit_reset(GPIOB,BIT(5));
|
|
|
|
|
|
//sdreg(base,SDxCON1) = BIT(BIT_CPCLR);
|
|
//sdreg(base,SDxCON0) &= ~BIT(BIT_8CKE);
|
|
ll_sdhc_clr_cmd_done_pending(p_sdhc);
|
|
// ll_sdhc_post_8clk_ctrl(p_sdhc,0);
|
|
// p_sdhc->CFG &= ~(BIT(5)|BIT(6)|BIT(7));
|
|
p_sdhc->CFG &= ~BIT(0);
|
|
// os_printf ("CMD%d pending time out!resp:%x\r\n", cmd->cmd_code,rsp_type);
|
|
//ll_gpio_bit_set(GPIOB,BIT(5));
|
|
p_sdhc->CFG |= BIT(0);
|
|
|
|
//ll_sdhc_post_8clk_ctrl(p_sdhc, 0);
|
|
return -1;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
sd_cmd_isr_get = 0;
|
|
//ll_gpio_bit_set(GPIOB,BIT(5));
|
|
if ((rsp_type != (0x2 << 5)) && (ll_sdhc_get_cmd_rsp_timeout_pending(p_sdhc) || ((cmd->flags & MMC_RSP_CRC) && ll_sdhc_get_cmd_rsp_err_pending(p_sdhc))))
|
|
{
|
|
os_printf("read timeout pending set or rsp err:%d %d\r\n",ll_sdhc_get_cmd_rsp_timeout_pending(p_sdhc),ll_sdhc_get_cmd_rsp_err_pending(p_sdhc));
|
|
|
|
return -1;
|
|
}
|
|
|
|
cmd->resp[0] = (sdhc_cmd_rsp_buf[9] << 24) |
|
|
(sdhc_cmd_rsp_buf[10] << 16) |
|
|
(sdhc_cmd_rsp_buf[11] << 8) |
|
|
(sdhc_cmd_rsp_buf[12] << 0);
|
|
|
|
cmd->resp[1] = (sdhc_cmd_rsp_buf[13] << 24) |
|
|
(sdhc_cmd_rsp_buf[14] << 16) |
|
|
(sdhc_cmd_rsp_buf[15] << 8) |
|
|
(sdhc_cmd_rsp_buf[16] << 0);
|
|
|
|
cmd->resp[2] = (sdhc_cmd_rsp_buf[17] << 24) |
|
|
(sdhc_cmd_rsp_buf[18] << 16) |
|
|
(sdhc_cmd_rsp_buf[19] << 8) |
|
|
(sdhc_cmd_rsp_buf[20] << 0);
|
|
|
|
cmd->resp[3] = (sdhc_cmd_rsp_buf[21] << 24) |
|
|
(sdhc_cmd_rsp_buf[22] << 16) |
|
|
(sdhc_cmd_rsp_buf[23] << 8) |
|
|
(sdhc_cmd_rsp_buf[24] << 0);
|
|
#if 0
|
|
os_printf("\r\n");
|
|
for(cmd_tick = 0;cmd_tick < 32;cmd_tick++){
|
|
os_printf("%02x",sdhc_cmd_rsp_buf[cmd_tick]);
|
|
}
|
|
os_printf("\r\n");
|
|
#endif
|
|
// os_printf("cmd->resp:%x\r\n",cmd->resp[0]);
|
|
// ll_sdhc_post_8clk_ctrl(p_sdhc, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void sdhc_interrupt_handle(struct sdh_device *sdhost)
|
|
{
|
|
struct hgsdh *sdh_hw = (struct hgsdh*)sdhost;
|
|
struct hgsdh_hw *hw = (struct hgsdh_hw *)sdh_hw->hw;
|
|
if (ll_sdhc_get_dat_done_pending(hw)){
|
|
ll_sdhc_clr_dat_done_pending(hw);
|
|
if(sdhost->sd_opt == SD_M_R){
|
|
if(ll_sdhc_get_rd_dat_err_pending(hw))
|
|
{
|
|
if(sdhost->io_cfg.self_adaption_flag != MMCSD_SMP_EN) os_printf("read dat err pending\r\n");
|
|
sdhost->data.err = DATA_DIR_READ;
|
|
os_sema_up(&sdhost->dat_sema);
|
|
return;
|
|
}
|
|
|
|
if(sdhost->data.blks - 1)
|
|
{
|
|
sdhost->data.blks = sdhost->data.blks -1;
|
|
sdhost->data.buf = sdhost->data.buf + sdhost->data.blksize;
|
|
if(sdhost->data.psram_flag)
|
|
{
|
|
ll_sdhc_read_data_kick(hw, (uint32_t)sdhost->data.ping_buff[(sdhost->data.ping_sel+1)%2], sdhost->data.blksize);
|
|
hw_memcpy((void *)(sdhost->data.buf - sdhost->data.blksize), (void *)sdhost->data.ping_buff[sdhost->data.ping_sel++], sdhost->data.blksize);
|
|
} else {
|
|
ll_sdhc_read_data_kick(hw, (uint32_t)sdhost->data.buf, sdhost->data.blksize);
|
|
}
|
|
}else
|
|
{
|
|
os_sema_up(&sdhost->dat_sema);
|
|
}
|
|
|
|
}
|
|
else if(sdhost->sd_opt == SD_M_W)
|
|
{
|
|
if (ll_sdhc_get_wr_dat_err_pending(hw))
|
|
{
|
|
os_printf("write dat err pending\r\n");
|
|
sdhost->data.err = DATA_DIR_READ;
|
|
os_sema_up(&sdhost->dat_sema);
|
|
return;
|
|
}
|
|
|
|
if(sdhost->data.blks - 1)
|
|
{
|
|
sdhost->data.blks = sdhost->data.blks -1;
|
|
sdhost->data.buf = sdhost->data.buf + sdhost->data.blksize;
|
|
ll_sdhc_write_data_kick(hw, (uint32_t)sdhost->data.buf, sdhost->data.blksize);
|
|
}else
|
|
{
|
|
os_sema_up(&sdhost->dat_sema);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
os_sema_up(&sdhost->dat_sema); //single read/write
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SDHOST_IRQHandler_action(void *sdhost)
|
|
{
|
|
sdhc_interrupt_handle(sdhost);
|
|
}
|
|
|
|
|
|
int32 sdh_write(struct sdh_device *sdhost,uint8_t * buf)
|
|
{
|
|
struct hgsdh *sdh_hw = (struct hgsdh*)sdhost;
|
|
struct hgsdh_hw *hw = (struct hgsdh_hw *)sdh_hw->hw;
|
|
uint32_t xfer_len = sdhost->data.blksize;
|
|
|
|
#if DAT_ISR_EN
|
|
#if TXW81X
|
|
sdhost->data.psram_flag = 0;
|
|
sys_dcache_clean_range_unaligned((uint32 *)buf, (uint32_t)(sdhost->data.blks * sdhost->data.blksize));
|
|
#endif
|
|
sdhost->data.buf = buf;
|
|
ll_sdhc_write_data_kick(hw, (uint32_t)buf, xfer_len);
|
|
#else
|
|
#if TXW81X
|
|
sdhost->data.psram_flag = 0;
|
|
sys_dcache_clean_range_unaligned((uint32 *)buf, (uint32_t)(sdhost->data.blks * sdhost->data.blksize));
|
|
#endif
|
|
uint32_t blocks = sdhost->data.blks;
|
|
uint32_t size = sdhost->data.blks * sdhost->data.blksize;
|
|
do {
|
|
ll_sdhc_write_data_kick(hw, (uint32_t)buf, xfer_len);
|
|
buf += xfer_len;
|
|
if (blocks) {
|
|
blocks--;
|
|
}
|
|
|
|
if (!ll_sdhc_write_data_wait(hw, sdhost->data.timeout_ns/1000000))
|
|
{
|
|
os_printf("write timeout:%d\r\n",sdhost->data.timeout_ns);
|
|
sdhost->data.err = DATA_DIR_WRITE;
|
|
break;
|
|
}
|
|
|
|
if (ll_sdhc_get_wr_dat_err_pending(hw)) {
|
|
os_printf("write dat err pending\r\n");
|
|
sdhost->data.err = DATA_DIR_WRITE;
|
|
}
|
|
|
|
sd_dat_isr_get = 0;
|
|
} while (blocks);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int32 sdh_read(struct sdh_device *sdhost,uint8* buf)
|
|
{
|
|
struct hgsdh *sdh_hw = (struct hgsdh*)sdhost;
|
|
struct hgsdh_hw *hw = (struct hgsdh_hw *)sdh_hw->hw;
|
|
uint32_t xfer_len = sdhost->data.blksize;
|
|
sdhost->sd_8clk_open = 0;
|
|
ll_sdhc_8clk_ctrl(hw, sdhost->sd_8clk_open);
|
|
#if DAT_ISR_EN
|
|
sdhost->data.buf = buf;
|
|
#if TXW81X
|
|
sdhost->data.psram_flag = ((((uint32_t)buf) >> 24) == 0x38 || (((uint32_t)buf) >> 24) == 0x08);
|
|
if(sdhost->data.psram_flag) buf = (uint8 *)sdhost->data.ping_buff[sdhost->data.ping_sel];
|
|
#endif
|
|
ll_sdhc_read_data_kick(hw, (uint32_t)buf, xfer_len);
|
|
#else
|
|
uint32_t blocks = sdhost->data.blks;
|
|
uint32_t size = sdhost->data.blks * sdhost->data.blksize;
|
|
do {
|
|
ll_sdhc_read_data_kick(hw, (uint32_t)buf, xfer_len);
|
|
buf += xfer_len;
|
|
if (blocks) {
|
|
blocks--;
|
|
}
|
|
|
|
if (!ll_sdhc_read_data_wait(hw, sdhost->data.timeout_ns/1000000))
|
|
{
|
|
os_printf("read dat timeout\r\n");
|
|
//while(1);
|
|
sdhost->data.err = DATA_DIR_READ;
|
|
}
|
|
|
|
|
|
if (ll_sdhc_get_rd_dat_err_pending(hw)) {
|
|
os_printf("read dat err pending\r\n");
|
|
sdhost->data.err = DATA_DIR_READ;
|
|
}
|
|
sd_dat_isr_get = 0;
|
|
} while (blocks);
|
|
#endif
|
|
ll_sdhc_8clk_ctrl(hw,sdhost->sd_8clk_open);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32 sdh_complete(struct sdh_device *sdhost)
|
|
{
|
|
#if DAT_ISR_EN
|
|
int res;
|
|
int ret = 0;
|
|
res = os_sema_down(&sdhost->dat_sema, 2000);
|
|
struct hgsdh *sdh_hw = (struct hgsdh*)sdhost;
|
|
struct hgsdh_hw *hw = (struct hgsdh_hw *)sdh_hw->hw;
|
|
if(sdhost->data.err != 0 || res == 0){
|
|
if(res == 0)
|
|
{
|
|
sdhost->sd_opt = SD_OFF;
|
|
}
|
|
ret = -1;
|
|
}
|
|
if(sdhost->sd_8clk_default) sdhost->sd_8clk_open = 1;
|
|
ll_sdhc_8clk_ctrl(hw,sdhost->sd_8clk_open);
|
|
#if TXW81X
|
|
if(sdhost->data.psram_flag && (sdhost->sd_opt == SD_M_R)&& !ret) hw_memcpy((void *)(sdhost->data.buf), (void *)sdhost->data.ping_buff[sdhost->data.ping_sel++], sdhost->data.blksize);
|
|
#endif
|
|
return ret;
|
|
#endif
|
|
}
|
|
|
|
|
|
//set the sdh config
|
|
int32 sdh_open(struct sdh_device *sdhost,uint8 bus_w)
|
|
{
|
|
struct hgsdh *sdh_hw = (struct hgsdh*)sdhost;
|
|
struct hgsdh_hw *hw = (struct hgsdh_hw *)sdh_hw->hw;
|
|
|
|
if (sdhost == 0) {
|
|
os_printf("mmcsd alloc host fail");
|
|
return 0;
|
|
}
|
|
sdh_hw->opened = 0;
|
|
|
|
SYSCTRL->SYS_CON1 &= ~(BIT(20));
|
|
delay_us(10);
|
|
SYSCTRL->SYS_CON1 |= (BIT(20));
|
|
sysctrl_sdhc_clk_open();
|
|
|
|
sdh_irq = sdhost;
|
|
/* set host default attributes */
|
|
sdhost->sd_8clk_default = 1;
|
|
sdhost->sd_8clk_open = sdhost->sd_8clk_default;
|
|
sdhost->freq_min = 400 * 1000;
|
|
#ifdef TXW81X
|
|
sdhost->freq_max = 50000000;
|
|
#else
|
|
sdhost->freq_max = 24000000;
|
|
#endif
|
|
sdhost->valid_ocr = 0x00FFFF80;/* The voltage range supported is 1.65v-3.6v */
|
|
|
|
#ifdef TXW81X
|
|
if(bus_w == 4)
|
|
sdhost->flags = MMCSD_BUSWIDTH_4 | MMCSD_MUTBLKWRITE | MMCSD_SUP_SDIO_IRQ | MMCSD_SUP_HIGHSPEED;
|
|
else
|
|
sdhost->flags = MMCSD_MUTBLKWRITE | MMCSD_SUP_SDIO_IRQ | MMCSD_SUP_HIGHSPEED;
|
|
#else
|
|
if(bus_w == 4)
|
|
sdhost->flags = MMCSD_BUSWIDTH_4 | MMCSD_MUTBLKWRITE | MMCSD_SUP_SDIO_IRQ;
|
|
else
|
|
sdhost->flags = MMCSD_MUTBLKWRITE | MMCSD_SUP_SDIO_IRQ;
|
|
#endif
|
|
|
|
sdhost->max_seg_size = 4096;
|
|
sdhost->max_dma_segs = 1;
|
|
sdhost->max_blk_size = 512;
|
|
sdhost->max_blk_count = 512;
|
|
sdhost->sd_stop = 0;
|
|
sdhost->sd_opt = SD_IDLE;
|
|
sdhost->new_lba = 0;
|
|
if(sdhost->dat_sema.hdl)
|
|
{
|
|
os_sema_del(&sdhost->dat_sema);
|
|
}
|
|
os_sema_init(&sdhost->dat_sema, 0);
|
|
|
|
/* link up host and sdio */
|
|
sdhost->private_data = 0;
|
|
sdhost->data.timeout_ns = 100000000; /* 100ms */
|
|
TYPE_LL_SDHC_CFG cfg;
|
|
cfg.bus_clk = sdhost->freq_min;
|
|
cfg.ioe = LL_SDHC_IROF;//LL_SDHC_IROF;
|
|
if(bus_w == 4)
|
|
cfg.width = LL_SDHC_DAT_4BIT;
|
|
else
|
|
cfg.width = LL_SDHC_DAT_1BIT;
|
|
ll_sdhc_config(sdhost,hw, &cfg);
|
|
|
|
#ifdef TXW81X
|
|
sdhost->data.ping_buff[0] = (uint32_t)sdhc_dat_ping_buf0;
|
|
sdhost->data.ping_buff[1] = (uint32_t)sdhc_dat_ping_buf1;
|
|
sdhost->data.ping_sel = 0;
|
|
sdhost->io_cfg.dat_overflow_stop_flag = 1;
|
|
sdhost->io_cfg.self_adaption_flag = 0;
|
|
#endif
|
|
|
|
hgic_sdc_irq_update(sdhost, 1);
|
|
NVIC_EnableIRQ(SDHOST_IRQn);
|
|
request_irq(SDHOST_IRQn, SDHOST_IRQHandler_action, (void*)sdhost);
|
|
return 0;
|
|
|
|
}
|
|
|
|
static void hgsdh_set_clock(struct sdh_device *sdhost, uint32_t clk)
|
|
{
|
|
struct hgsdh *sdh_hw = (struct hgsdh*)sdhost;
|
|
struct hgsdh_hw *hw = (struct hgsdh_hw *)sdh_hw->hw;
|
|
|
|
if (clk < sdhost->freq_min) {
|
|
clk = sdhost->freq_min;
|
|
}
|
|
|
|
if (clk > sdhost->freq_max) {
|
|
clk = sdhost->freq_max;
|
|
}
|
|
|
|
if (clk > 25000000) {
|
|
ll_sdhc_set_ioe(hw, LL_SDHC_IROR);
|
|
} else {
|
|
ll_sdhc_set_ioe(hw, LL_SDHC_IROF);
|
|
}
|
|
|
|
if (clk) {
|
|
ll_sdhc_set_bus_clk(hw, sys_get_apbclk(), clk);
|
|
}
|
|
}
|
|
|
|
static void hgsdh_set_bus_width(struct hgsdh_hw *p_sdhc, uint32 bus_width)
|
|
{
|
|
switch (bus_width)
|
|
{
|
|
case MMCSD_BUS_WIDTH_1:
|
|
ll_sdhc_set_dat_width(p_sdhc, LL_SDHC_DAT_1BIT);
|
|
break;
|
|
|
|
case MMCSD_BUS_WIDTH_4:
|
|
ll_sdhc_set_dat_width(p_sdhc, LL_SDHC_DAT_4BIT);
|
|
break;
|
|
|
|
case MMCSD_BUS_WIDTH_8:
|
|
os_printf("bus errr\r\n");
|
|
break;
|
|
|
|
default:
|
|
os_printf("no support bus width :%d\r\n", bus_width);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void hgsdh_set_sample(struct hgsdh_hw *p_sdhc, TYPE_LL_SDHC_SMP_CFG type, uint32_t cmd_smp, uint32_t dat_smp)
|
|
{
|
|
switch (type)
|
|
{
|
|
case LL_SDHC_ALL_SMP_CFG_DIS:
|
|
ll_sdhc_cmd_sample_enable(p_sdhc, 0);
|
|
ll_sdhc_dat_sample_enable(p_sdhc, 0);
|
|
ll_sdhc_set_sample(p_sdhc, 0, 0);
|
|
break;
|
|
|
|
case LL_SDHC_CMD_SMP_CFG_EN:
|
|
ll_sdhc_cmd_sample_enable(p_sdhc, 1);
|
|
ll_sdhc_set_sample(p_sdhc, cmd_smp, dat_smp);
|
|
break;
|
|
|
|
case LL_SDHC_DAT_SMP_CFG_EN:
|
|
ll_sdhc_dat_sample_enable(p_sdhc, 1);
|
|
ll_sdhc_set_sample(p_sdhc, cmd_smp, dat_smp);
|
|
break;
|
|
|
|
case LL_SDHC_ALL_SMP_CFG_EN:
|
|
ll_sdhc_cmd_sample_enable(p_sdhc, 1);
|
|
ll_sdhc_dat_sample_enable(p_sdhc, 1);
|
|
ll_sdhc_set_sample(p_sdhc, cmd_smp, dat_smp);
|
|
break;
|
|
|
|
default:
|
|
os_printf("sample type : %d err!\r\n", type);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void hgsdh_set_power_mode(uint8_t mode)
|
|
{
|
|
switch (mode)
|
|
{
|
|
case MMCSD_POWER_OFF:
|
|
break;
|
|
case MMCSD_POWER_UP:
|
|
break;
|
|
case MMCSD_POWER_ON:
|
|
break;
|
|
default:
|
|
os_printf("unknown power_mode %d", mode);
|
|
break;
|
|
}
|
|
}
|
|
|
|
int32 sdh_cfg(struct sdh_device *sdhost, struct rt_mmcsd_io_cfg *io_cfg)
|
|
{
|
|
struct hgsdh *sdh_hw = (struct hgsdh*)sdhost;
|
|
struct hgsdh_hw *hw = (struct hgsdh_hw *)sdh_hw->hw;
|
|
|
|
switch (io_cfg->ioctl_type)
|
|
{
|
|
case LL_SDHC_IOCTRL_SET_CLOCK:
|
|
hgsdh_set_clock(sdhost, io_cfg->clock);
|
|
io_cfg->crc_sample_max = sys_get_apbclk() / io_cfg->clock;
|
|
break;
|
|
|
|
case LL_SDHC_IOCTRL_SET_BUS_WIDTH:
|
|
hgsdh_set_bus_width(hw, io_cfg->bus_width);
|
|
break;
|
|
|
|
#ifdef TXW81X
|
|
case LL_SDHC_IOCTRL_SET_SMP:
|
|
hgsdh_set_sample(hw, io_cfg->smp_type, io_cfg->cmd_crc_sample, io_cfg->dat_crc_sample);
|
|
break;
|
|
|
|
case LL_SDHC_IOCTRL_SET_DELAY_TYPE:
|
|
(io_cfg->delay_flag) ? (ll_sdhc_delay_clock(hw, io_cfg->delay_type, io_cfg->delay_chain_cnt)) :
|
|
(ll_sdhc_delay_clock(hw, LL_SDHC_DLY_NONE, 0));
|
|
break;
|
|
|
|
case LL_SDHC_IOCTRL_SET_DAT_OF_STOP_CLK:
|
|
ll_sdhc_dat_overflow_stop_enable(hw, io_cfg->dat_overflow_stop_flag);
|
|
break;
|
|
#endif
|
|
case LL_SDHC_IOCTRL_SET_POWER_MODE:
|
|
hgsdh_set_power_mode(io_cfg->power_mode);
|
|
break;
|
|
|
|
default:
|
|
os_printf("err ioctrl_cmd : %d\r\n", io_cfg->ioctl_type);
|
|
break;
|
|
}
|
|
|
|
if(io_cfg->ioctl_type < LL_SDHC_IOCTRL_SET_SMP)
|
|
{
|
|
os_printf("clk:%d width:%s%s%s power:%s%s%s\r\n",
|
|
io_cfg->clock,
|
|
io_cfg->bus_width == MMCSD_BUS_WIDTH_8 ? "8" : "",
|
|
io_cfg->bus_width == MMCSD_BUS_WIDTH_4 ? "4" : "",
|
|
io_cfg->bus_width == MMCSD_BUS_WIDTH_1 ? "1" : "",
|
|
io_cfg->power_mode == MMCSD_POWER_OFF ? "OFF" : "",
|
|
io_cfg->power_mode == MMCSD_POWER_UP ? "UP" : "",
|
|
io_cfg->power_mode == MMCSD_POWER_ON ? "ON" : ""
|
|
);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int32 sdh_close(struct sdh_device *sdhost)
|
|
{
|
|
struct hgsdh *sdh_hw = (struct hgsdh*)sdhost;
|
|
struct hgsdh_hw *hw = (struct hgsdh_hw *)sdh_hw->hw;
|
|
ll_sdhc_close(hw);
|
|
sdh_hw->opened = 0;
|
|
sdh_hw->dsleep = 0;
|
|
sysctrl_sdhc_clk_close();
|
|
os_sema_del(&sdhost->dat_sema);
|
|
NVIC_DisableIRQ(SDHOST_IRQn);
|
|
pin_func(sdhost->dev.dev_id,0);
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_SLEEP
|
|
int32 sdh_suspend(struct sdh_device *sdhost)
|
|
{
|
|
struct hgsdh *sdh_hw = (struct hgsdh*)sdhost;
|
|
struct hgsdh_hw *hw = (struct hgsdh_hw *)sdh_hw->hw;
|
|
struct hgsdh_hw *hw_cfg;
|
|
if (!sdh_hw->opened || sdh_hw->dsleep)
|
|
{
|
|
return RET_OK;
|
|
}
|
|
|
|
if (os_mutex_lock(&sdhost->lock, osWaitForever))
|
|
{
|
|
return RET_ERR;
|
|
}
|
|
|
|
if (0 > os_mutex_lock(&sdhost->bp_suspend_lock, 10000))
|
|
{
|
|
return RET_ERR;
|
|
}
|
|
|
|
sysctrl_sdhc_clk_close();
|
|
sdhost->cfg_backup = (uint32 *)os_malloc(sizeof(struct hgsdh_hw));
|
|
//memcpy((uint8 *)sdhost->cfg_backup,(uint8 *)hw,sizeof(struct hgsdh_hw));
|
|
hw_cfg = (struct hgsdh_hw*)sdhost->cfg_backup;
|
|
hw_cfg->BAUD = hw->BAUD;
|
|
hw_cfg->CFG = hw->CFG;
|
|
hw_cfg->CPTR = hw->CPTR;
|
|
hw_cfg->DPTR = hw->DPTR;
|
|
hw_cfg->DCNT = hw->DCNT;
|
|
#ifdef TXW81X
|
|
hw_cfg->CFG1 = hw->CFG1;
|
|
hw_cfg->ISMP = hw->ISMP;
|
|
#endif
|
|
irq_disable(sdh_hw->irq_num);
|
|
sdh_hw->dsleep = 1;
|
|
os_mutex_unlock(&sdhost->bp_suspend_lock);
|
|
return 0;
|
|
}
|
|
|
|
int32 sdh_resume(struct sdh_device *sdhost)
|
|
{
|
|
struct hgsdh *sdh_hw = (struct hgsdh*)sdhost;
|
|
struct hgsdh_hw *hw = (struct hgsdh_hw *)sdh_hw->hw;
|
|
struct hgsdh_hw *hw_cfg;
|
|
|
|
if ((!sdh_hw->opened) || (!sdh_hw->dsleep)) {
|
|
return RET_OK;
|
|
}
|
|
|
|
if (0 > os_mutex_lock(&sdhost->bp_resume_lock, 10000))
|
|
{
|
|
return RET_ERR;
|
|
}
|
|
|
|
sysctrl_sdhc_clk_open();
|
|
hw_cfg = (struct hgsdh_hw*)sdhost->cfg_backup;
|
|
//memcpy((uint8 *)hw,(uint8 *)sdhost->cfg_backup,sizeof(struct hgsdh_hw));
|
|
hw->BAUD = hw_cfg->BAUD;
|
|
hw->CPTR = hw_cfg->CPTR;
|
|
hw->DPTR = hw_cfg->DPTR;
|
|
hw->DCNT = hw_cfg->DCNT;
|
|
hw->CFG = hw_cfg->CFG;
|
|
#ifdef TXW81X
|
|
hw->CFG1 = hw_cfg->CFG1;
|
|
hw->ISMP = hw_cfg->ISMP;
|
|
#endif
|
|
irq_enable(sdh_hw->irq_num);
|
|
os_free(sdhost->cfg_backup);
|
|
sdh_hw->dsleep = 0;
|
|
os_mutex_unlock(&sdhost->bp_resume_lock);
|
|
os_mutex_unlock(&sdhost->lock);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
void hgsdh_attach(uint32 dev_id, struct hgsdh *sdhost)
|
|
{
|
|
sdhost->dev.open = sdh_open;
|
|
sdhost->dev.close = sdh_close;
|
|
sdhost->dev.iocfg = sdh_cfg;
|
|
sdhost->dev.cmd = sdh_cmd;
|
|
sdhost->dev.write = sdh_write;
|
|
sdhost->dev.read = sdh_read;
|
|
sdhost->dev.complete = sdh_complete;
|
|
|
|
memset(&sdhost->dev.dat_sema,0,sizeof(sdhost->dev.dat_sema));
|
|
#ifdef CONFIG_SLEEP
|
|
sdhost->dev.suspend = sdh_suspend;
|
|
sdhost->dev.resume = sdh_resume;
|
|
os_mutex_init(&sdhost->dev.bp_suspend_lock);
|
|
os_mutex_init(&sdhost->dev.bp_resume_lock);
|
|
#endif
|
|
os_mutex_init(&sdhost->dev.lock);
|
|
irq_disable(sdhost->irq_num);
|
|
dev_register(dev_id, (struct dev_obj *)sdhost);
|
|
}
|
|
|
|
|
|
|