ipq807x: mmc: Enabled SDHCI ADMA support

This patch enables SDHCI mode and also supports
data transfer using ADMA method.

Change-Id: Ia3187fec9024ad0972ca720cf0b9ddc6a59b906c
Signed-off-by: Vasudevan Murugesan <vmuruges@codeaurora.org>
This commit is contained in:
Vasudevan Murugesan 2017-10-27 15:59:06 +05:30 committed by Gerrit - the friendly Code Review server
parent 9bf4639e22
commit 7fd4f44ace
7 changed files with 211 additions and 5 deletions

View file

@ -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_ */

View file

@ -18,9 +18,14 @@
#include <mmc.h>
#include <asm/arch-qca-common/smem.h>
#include "qca_common.h"
#include <sdhci.h>
#ifdef CONFIG_QCA_MMC
#ifndef CONFIG_SDHCI_SUPPORT
extern qca_mmc mmc_host;
#else
extern struct sdhci_host mmc_host;
#endif
#endif
/*

View file

@ -26,11 +26,17 @@
#include <mmc.h>
#include <usb.h>
#include <linux/linkage.h>
#include <sdhci.h>
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();

View file

@ -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

View file

@ -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;
}

View file

@ -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
/*

View file

@ -12,7 +12,6 @@
#include <asm/io.h>
#include <mmc.h>
#include <asm/gpio.h>
/*
* 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);