diff --git a/arch/arm/include/asm/arch-qca-common/qca_common.h b/arch/arm/include/asm/arch-qca-common/qca_common.h index 2a7ea337e4..cf34f03e13 100644 --- a/arch/arm/include/asm/arch-qca-common/qca_common.h +++ b/arch/arm/include/asm/arch-qca-common/qca_common.h @@ -68,8 +68,11 @@ extern struct dumpinfo_t dumpinfo_s[]; extern int dump_entries_s; #define MSM_SDC1_BASE 0x7824000 +#define MSM_SDC1_MCI_HC_MODE 0x7824078 +#define MSM_SDC1_SDHCI_BASE 0x7824900 #define MMC_IDENTIFY_MODE 0 #define MMC_DATA_TRANSFER_MODE 1 +#define MMC_DATA_TRANSFER_SDHCI_MODE 2 #define MMC_MODE_HC 0x800 #endif /* __QCA_COMMON_H_ */ diff --git a/board/qca/arm/common/ethaddr.c b/board/qca/arm/common/ethaddr.c index 2ca7f9645a..76496ad445 100644 --- a/board/qca/arm/common/ethaddr.c +++ b/board/qca/arm/common/ethaddr.c @@ -18,9 +18,14 @@ #include #include #include "qca_common.h" +#include #ifdef CONFIG_QCA_MMC +#ifndef CONFIG_SDHCI_SUPPORT extern qca_mmc mmc_host; +#else +extern struct sdhci_host mmc_host; +#endif #endif /* diff --git a/board/qca/arm/ipq807x/ipq807x.c b/board/qca/arm/ipq807x/ipq807x.c index e6801eb0da..e055b42158 100644 --- a/board/qca/arm/ipq807x/ipq807x.c +++ b/board/qca/arm/ipq807x/ipq807x.c @@ -26,11 +26,17 @@ #include #include #include +#include DECLARE_GLOBAL_DATA_PTR; #define GCNT_PSHOLD 0x004AB000 + +#ifndef CONFIG_SDHCI_SUPPORT qca_mmc mmc_host; +#else +struct sdhci_host mmc_host; +#endif extern asmlinkage void __invoke_psci_fn_smc(unsigned long, unsigned long, unsigned long, unsigned long); @@ -238,6 +244,17 @@ void emmc_clock_config(int mode) /* Delay for clock operation complete */ udelay(10); } + if (mode == MMC_DATA_TRANSFER_SDHCI_MODE) { + /* PLL0 - 192Mhz */ + writel(0x20B, GCC_SDCC1_APPS_CFG_RCGR); + /* Delay for clock operation complete */ + udelay(10); + writel(0x1, GCC_SDCC1_APPS_M); + writel(0xFC, GCC_SDCC1_APPS_N); + writel(0xFD, GCC_SDCC1_APPS_D); + /* Delay for clock operation complete */ + udelay(10); + } /* Update APPS_CMD_RCGR to reflect source selection */ writel(readl(GCC_SDCC1_APPS_CMD_RCGR)|0x1, GCC_SDCC1_APPS_CMD_RCGR); /* Add 10us delay for clock update to complete */ @@ -255,6 +272,21 @@ void board_mmc_deinit(void) emmc_clock_disable(); } +void emmc_clock_reset(void) +{ + writel(0x1, GCC_SDCC1_BCR); + udelay(10); + writel(0x0, GCC_SDCC1_BCR); +} + +void emmc_sdhci_init(void) +{ + writel(readl(MSM_SDC1_MCI_HC_MODE) & (~0x1), MSM_SDC1_MCI_HC_MODE); + writel(readl(MSM_SDC1_BASE) | (1 << 7), MSM_SDC1_BASE); //SW_RST + udelay(10); + writel(readl(MSM_SDC1_MCI_HC_MODE) | (0x1), MSM_SDC1_MCI_HC_MODE); +} + void eth_clock_enable(void) { int tlmm_base = 0x1025000; @@ -384,11 +416,30 @@ int board_mmc_init(bd_t *bis) int ret; qca_smem_flash_info_t *sfi = &qca_smem_flash_info; +#ifndef CONFIG_SDHCI_SUPPORT mmc_host.base = MSM_SDC1_BASE; mmc_host.clk_mode = MMC_IDENTIFY_MODE; emmc_clock_config(mmc_host.clk_mode); ret = qca_mmc_init(bis, &mmc_host); +#else + mmc_host.ioaddr = (void *)MSM_SDC1_SDHCI_BASE; + mmc_host.voltages = MMC_VDD_165_195; + mmc_host.version = SDHCI_SPEC_300; + mmc_host.cfg.part_type = PART_TYPE_EFI; + mmc_host.quirks = SDHCI_QUIRK_BROKEN_VOLTAGE; + + emmc_clock_disable(); + emmc_clock_reset(); + udelay(10); + emmc_clock_config(MMC_DATA_TRANSFER_SDHCI_MODE); + emmc_sdhci_init(); + + if (add_sdhci(&mmc_host, 200000000, 400000)) { + printf("add_sdhci fail!\n"); + return -1; + } +#endif if (!ret && sfi->flash_type == SMEM_BOOT_MMC_FLASH) { ret = board_mmc_env_init(); diff --git a/board/qca/arm/ipq807x/ipq807x.h b/board/qca/arm/ipq807x/ipq807x.h index 84e4d09027..58c222fe5b 100644 --- a/board/qca/arm/ipq807x/ipq807x.h +++ b/board/qca/arm/ipq807x/ipq807x.h @@ -29,6 +29,7 @@ #define GCC_SDCC1_APPS_N 0x1842010 #define GCC_SDCC1_APPS_D 0x1842014 #define GCC_BLSP1_UART1_APPS_CBCR 0x0180203c +#define GCC_SDCC1_BCR 0x01842000 #define GCC_BLSP1_UART2_APPS_CFG_RCGR 0x01803038 #define GCC_BLSP1_UART2_APPS_M 0x0180303C diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 02d71b9344..260f49f3ee 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -19,6 +19,8 @@ void *aligned_buffer = (void *)CONFIG_FIXED_SDHCI_ALIGNED_BUFFER; void *aligned_buffer; #endif +#define CACHE_LINE_SIZE (CONFIG_SYS_CACHELINE_SIZE) + static void sdhci_reset(struct sdhci_host *host, u8 mask) { unsigned long timeout; @@ -67,6 +69,103 @@ static void sdhci_transfer_pio(struct sdhci_host *host, struct mmc_data *data) } } +#ifdef CONFIG_MMC_ADMA +static struct adma_desc *sdhci_prepare_descriptors(void *data, uint32_t len) +{ + struct adma_desc *list; + uint32_t list_len = 0; + uint32_t remain = 0; + uint32_t i; + uint32_t table_len = 0; + + if (len <= SDHCI_ADMA_DESC_LINE_SZ) { + list = (struct adma_desc *) memalign(CACHE_LINE_SIZE, sizeof(struct adma_desc)); + + if (!list) { + printf("Error allocating memory\n"); + assert(0); + } + + list[0].addr = (uint32_t)data; + list[0].len = (len < SDHCI_ADMA_DESC_LINE_SZ) ? len : (SDHCI_ADMA_DESC_LINE_SZ & 0xffff); + list[0].tran_att = SDHCI_ADMA_TRANS_VALID | SDHCI_ADMA_TRANS_DATA + | SDHCI_ADMA_TRANS_END; + + invalidate_dcache_range((uint32_t)list & ~(CACHE_LINE_SIZE - 1), + ALIGN((uint32_t)list + sizeof(struct adma_desc),CACHE_LINE_SIZE)); + + } else { + list_len = len / SDHCI_ADMA_DESC_LINE_SZ; + remain = len - (list_len * SDHCI_ADMA_DESC_LINE_SZ); + + if (remain) + list_len++; + + table_len = (list_len * sizeof(struct adma_desc)); + + list = (struct adma_desc *) memalign( CACHE_LINE_SIZE, table_len); + + if (!list) { + printf("Allocating memory failed\n"); + assert(0); + } + + memset((void *) list, 0, table_len); + + for (i = 0; i < (list_len - 1); i++) { + list[i].addr = (uint32_t)data; + list[i].len = (SDHCI_ADMA_DESC_LINE_SZ & 0xffff); + list[i].tran_att = SDHCI_ADMA_TRANS_VALID | SDHCI_ADMA_TRANS_DATA; + data += SDHCI_ADMA_DESC_LINE_SZ; + len -= SDHCI_ADMA_DESC_LINE_SZ; + } + + list[list_len - 1].addr = (uint32_t)data; + list[list_len - 1].len = (len < SDHCI_ADMA_DESC_LINE_SZ) ? len : (SDHCI_ADMA_DESC_LINE_SZ & 0xffff); + list[list_len - 1].tran_att = SDHCI_ADMA_TRANS_VALID | SDHCI_ADMA_TRANS_DATA | + SDHCI_ADMA_TRANS_END; + } + + invalidate_dcache_range((uint32_t)list & ~(CACHE_LINE_SIZE - 1), + ALIGN((uint32_t)list + table_len,CACHE_LINE_SIZE)); + + return list; +} + +struct adma_desc *sdhci_adma_transfer(struct sdhci_host *host, struct mmc_data *data) +{ + uint32_t sz; + void *dataptr; + struct adma_desc *adma_addr; + + dataptr = data->dest; + + if (data->blocksize) + sz = data->blocks * data->blocksize; + else + sz = data->blocks * SDHCI_MMC_BLK_SZ; + + /* Prepare adma descriptors */ + adma_addr = sdhci_prepare_descriptors(dataptr, sz); + + /* Write adma address to adma register */ + sdhci_writel(host, (uint32_t) adma_addr, SDHCI_ADM_ADDR_REG); + + /* Write the block size */ + if (data->blocksize) + sdhci_writew(host, data->blocksize, SDHCI_BLOCK_SIZE); + else + sdhci_writew(host, SDHCI_MMC_BLK_SZ, SDHCI_BLOCK_SIZE); + + /* + * Write block count in block count register + */ + sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT); + + return adma_addr; +} +#endif + static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data, unsigned int start_addr) { @@ -223,6 +322,11 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, sdhci_writel(host, start_addr, SDHCI_DMA_ADDRESS); mode |= SDHCI_TRNS_DMA; #endif + +#ifdef CONFIG_MMC_ADMA + mode |= SDHCI_TRNS_DMA; + sdhci_adma_transfer(host, data); +#endif sdhci_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, data->blocksize), SDHCI_BLOCK_SIZE); @@ -236,6 +340,7 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, #ifdef CONFIG_MMC_SDMA flush_cache(start_addr, trans_bytes); #endif + udelay(5); sdhci_writew(host, SDHCI_MAKE_CMD(cmd->cmdidx, flags), SDHCI_COMMAND); start = get_timer(0); do { @@ -440,7 +545,7 @@ static int sdhci_init(struct mmc *mmc) } } - sdhci_set_power(host, fls(mmc->cfg->voltages) - 1); + sdhci_set_power(host, fls(host->cfg.voltages) - 1); if (host->quirks & SDHCI_QUIRK_NO_CD) { unsigned int status; @@ -525,7 +630,7 @@ int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk) if (host->quirks & SDHCI_QUIRK_BROKEN_VOLTAGE) host->cfg.voltages |= host->voltages; - host->cfg.host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT; + host->cfg.host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_8BIT; if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) { if (caps & SDHCI_CAN_DO_8BIT) host->cfg.host_caps |= MMC_MODE_8BIT; @@ -543,5 +648,9 @@ int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk) return -1; } + host->dev_num = host->mmc->block_dev.dev; + host->mmc->has_init = 0; + host->mmc->init_in_progress = 0; + return 0; } diff --git a/include/configs/ipq807x.h b/include/configs/ipq807x.h index 9212f7466f..bad69d47b6 100644 --- a/include/configs/ipq807x.h +++ b/include/configs/ipq807x.h @@ -163,6 +163,8 @@ extern loff_t board_env_size; #define CONFIG_EFI_PARTITION #define CONFIG_ENV_IS_IN_MMC #define CONFIG_SYS_MMC_ENV_DEV 0 +#define CONFIG_SDHCI_SUPPORT +#define CONFIG_MMC_ADMA #endif /* diff --git a/include/sdhci.h b/include/sdhci.h index 23893b5740..9d7585f54c 100644 --- a/include/sdhci.h +++ b/include/sdhci.h @@ -12,7 +12,6 @@ #include #include -#include /* * Controller registers @@ -194,6 +193,8 @@ #define SDHCI_SPEC_300 2 #define SDHCI_GET_VERSION(x) (x->version & SDHCI_SPEC_VER_MASK) +#define SDHCI_ADM_ADDR_REG (0x058) +#define SDHCI_BLKSZ_REG (0x004) /* * End of controller registers. @@ -215,6 +216,32 @@ #define SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER (1 << 7) #define SDHCI_QUIRK_USE_WIDE8 (1 << 8) +/* + * Adma related macros + */ +#define SDHCI_BLK_LEN_MASK 0x00030000 +#define SDHCI_BLK_LEN_BIT 16 +#define SDHCI_BLK_ADMA_MASK 0x00080000 +#define SDHCI_INT_STS_TRANS_COMPLETE BIT(1) +#define SDHCI_STATE_CMD_DAT_MASK 0x0003 +#define SDHCI_INT_STS_CMD_COMPLETE BIT(0) +#define SDHCI_ERR_INT_STAT_MASK 0x8000 +#define SDHCI_ADMA_DESC_LINE_SZ 65536 +#define SDHCI_ADMA_MAX_TRANS_SZ (65535 * 512) +#define SDHCI_ADMA_TRANS_VALID BIT(0) +#define SDHCI_ADMA_TRANS_END BIT(1) +#define SDHCI_ADMA_TRANS_DATA BIT(5) +#define SDHCI_MMC_BLK_SZ 512 +#define SDHCI_MMC_CUR_BLK_CNT_BIT 16 +#define SDHCI_MMC_BLK_SZ_BIT 0 +#define SDHCI_TRANS_MULTI BIT(5) +#define SDHCI_TRANS_SINGLE (0 << 5) +#define SDHCI_BLK_CNT_EN BIT(1) +#define SDHCI_DMA_EN BIT(0) +#define SDHCI_AUTO_CMD23_EN BIT(3) +#define SDHCI_AUTO_CMD12_EN BIT(2) +#define SDHCI_ADMA_32BIT BIT(4) + /* to make gcc happy */ struct sdhci_host; @@ -234,6 +261,15 @@ struct sdhci_ops { #endif }; +/* + * Descriptor table for adma + */ +struct adma_desc { + uint16_t tran_att; + uint16_t len; + uint32_t addr; +}; + struct sdhci_host { char *name; void *ioaddr; @@ -246,8 +282,7 @@ struct sdhci_host { int index; int bus_width; - struct gpio_desc pwr_gpio; /* Power GPIO */ - struct gpio_desc cd_gpio; /* Card Detect GPIO */ + int dev_num; void (*set_control_reg)(struct sdhci_host *host); void (*set_clock)(int dev_index, unsigned int div);