mirror of
https://github.com/Fishwaldo/u-boot.git
synced 2025-03-18 13:11:31 +00:00
Merge branch 'master' of git://git.denx.de/u-boot-mmc
This commit is contained in:
commit
1cd2000698
12 changed files with 1427 additions and 27 deletions
|
@ -28,6 +28,12 @@
|
|||
#define MSTP10_BITS 0xFFFEFFE0
|
||||
#define MSTP11_BITS 0x00000000
|
||||
|
||||
/* SDHI */
|
||||
#define CONFIG_SYS_SH_SDHI1_BASE 0xEE120000
|
||||
#define CONFIG_SYS_SH_SDHI2_BASE 0xEE140000
|
||||
#define CONFIG_SYS_SH_SDHI3_BASE 0xEE160000
|
||||
#define CONFIG_SYS_SH_SDHI_NR_CHANNEL 4
|
||||
|
||||
#define R8A7790_CUT_ES2X 2
|
||||
#define IS_R8A7790_ES2() \
|
||||
(rmobile_get_cpu_rev_integer() == R8A7790_CUT_ES2X)
|
||||
|
|
|
@ -17,6 +17,11 @@
|
|||
/* SH-I2C */
|
||||
#define CONFIG_SYS_I2C_SH_BASE2 0xE60B0000
|
||||
|
||||
/* SDHI */
|
||||
#define CONFIG_SYS_SH_SDHI1_BASE 0xEE140000
|
||||
#define CONFIG_SYS_SH_SDHI2_BASE 0xEE160000
|
||||
#define CONFIG_SYS_SH_SDHI_NR_CHANNEL 3
|
||||
|
||||
#define DBSC3_1_QOS_R0_BASE 0xE67A1000
|
||||
#define DBSC3_1_QOS_R1_BASE 0xE67A1100
|
||||
#define DBSC3_1_QOS_R2_BASE 0xE67A1200
|
||||
|
|
|
@ -18,6 +18,11 @@
|
|||
/* SH-I2C */
|
||||
#define CONFIG_SYS_I2C_SH_BASE2 0xE60B0000
|
||||
|
||||
/* SDHI */
|
||||
#define CONFIG_SYS_SH_SDHI1_BASE 0xEE140000
|
||||
#define CONFIG_SYS_SH_SDHI2_BASE 0xEE160000
|
||||
#define CONFIG_SYS_SH_SDHI_NR_CHANNEL 3
|
||||
|
||||
#define DBSC3_1_QOS_R0_BASE 0xE67A1000
|
||||
#define DBSC3_1_QOS_R1_BASE 0xE67A1100
|
||||
#define DBSC3_1_QOS_R2_BASE 0xE67A1200
|
||||
|
|
|
@ -27,4 +27,9 @@
|
|||
#define MSTP10_BITS 0xFFFEFFE0
|
||||
#define MSTP11_BITS 0x000001C0
|
||||
|
||||
/* SDHI */
|
||||
#define CONFIG_SYS_SH_SDHI1_BASE 0xEE140000
|
||||
#define CONFIG_SYS_SH_SDHI2_BASE 0xEE160000
|
||||
#define CONFIG_SYS_SH_SDHI_NR_CHANNEL 3
|
||||
|
||||
#endif /* __ASM_ARCH_R8A7794_H */
|
||||
|
|
|
@ -82,6 +82,9 @@
|
|||
#define CONFIG_SYS_RCAR_I2C2_BASE 0xE6530000
|
||||
#define CONFIG_SYS_RCAR_I2C3_BASE 0xE6540000
|
||||
|
||||
/* SDHI */
|
||||
#define CONFIG_SYS_SH_SDHI0_BASE 0xEE100000
|
||||
|
||||
#define S3C_BASE 0xE6784000
|
||||
#define S3C_INT_BASE 0xE6784A00
|
||||
#define S3C_MEDIA_BASE 0xE6784B00
|
||||
|
|
168
arch/arm/include/asm/arch-rmobile/sh_sdhi.h
Normal file
168
arch/arm/include/asm/arch-rmobile/sh_sdhi.h
Normal file
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* drivers/mmc/sh-sdhi.h
|
||||
*
|
||||
* SD/MMC driver for Reneas rmobile ARM SoCs
|
||||
*
|
||||
* Copyright (C) 2013-2014 Renesas Electronics Corporation
|
||||
* Copyright (C) 2008-2009 Renesas Solutions Corp.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#ifndef _SH_SDHI_H
|
||||
#define _SH_SDHI_H
|
||||
|
||||
#define SDHI_CMD (0x0000 >> 1)
|
||||
#define SDHI_PORTSEL (0x0004 >> 1)
|
||||
#define SDHI_ARG0 (0x0008 >> 1)
|
||||
#define SDHI_ARG1 (0x000C >> 1)
|
||||
#define SDHI_STOP (0x0010 >> 1)
|
||||
#define SDHI_SECCNT (0x0014 >> 1)
|
||||
#define SDHI_RSP00 (0x0018 >> 1)
|
||||
#define SDHI_RSP01 (0x001C >> 1)
|
||||
#define SDHI_RSP02 (0x0020 >> 1)
|
||||
#define SDHI_RSP03 (0x0024 >> 1)
|
||||
#define SDHI_RSP04 (0x0028 >> 1)
|
||||
#define SDHI_RSP05 (0x002C >> 1)
|
||||
#define SDHI_RSP06 (0x0030 >> 1)
|
||||
#define SDHI_RSP07 (0x0034 >> 1)
|
||||
#define SDHI_INFO1 (0x0038 >> 1)
|
||||
#define SDHI_INFO2 (0x003C >> 1)
|
||||
#define SDHI_INFO1_MASK (0x0040 >> 1)
|
||||
#define SDHI_INFO2_MASK (0x0044 >> 1)
|
||||
#define SDHI_CLK_CTRL (0x0048 >> 1)
|
||||
#define SDHI_SIZE (0x004C >> 1)
|
||||
#define SDHI_OPTION (0x0050 >> 1)
|
||||
#define SDHI_ERR_STS1 (0x0058 >> 1)
|
||||
#define SDHI_ERR_STS2 (0x005C >> 1)
|
||||
#define SDHI_BUF0 (0x0060 >> 1)
|
||||
#define SDHI_SDIO_MODE (0x0068 >> 1)
|
||||
#define SDHI_SDIO_INFO1 (0x006C >> 1)
|
||||
#define SDHI_SDIO_INFO1_MASK (0x0070 >> 1)
|
||||
#define SDHI_CC_EXT_MODE (0x01B0 >> 1)
|
||||
#define SDHI_SOFT_RST (0x01C0 >> 1)
|
||||
#define SDHI_VERSION (0x01C4 >> 1)
|
||||
#define SDHI_HOST_MODE (0x01C8 >> 1)
|
||||
#define SDHI_SDIF_MODE (0x01CC >> 1)
|
||||
#define SDHI_EXT_SWAP (0x01E0 >> 1)
|
||||
#define SDHI_SD_DMACR (0x0324 >> 1)
|
||||
|
||||
/* SDHI CMD VALUE */
|
||||
#define CMD_MASK 0x0000ffff
|
||||
#define SDHI_APP 0x0040
|
||||
#define SDHI_SD_APP_SEND_SCR 0x0073
|
||||
#define SDHI_SD_SWITCH 0x1C06
|
||||
|
||||
/* SDHI_PORTSEL */
|
||||
#define USE_1PORT (1 << 8) /* 1 port */
|
||||
|
||||
/* SDHI_ARG */
|
||||
#define ARG0_MASK 0x0000ffff
|
||||
#define ARG1_MASK 0x0000ffff
|
||||
|
||||
/* SDHI_STOP */
|
||||
#define STOP_SEC_ENABLE (1 << 8)
|
||||
|
||||
/* SDHI_INFO1 */
|
||||
#define INFO1_RESP_END (1 << 0)
|
||||
#define INFO1_ACCESS_END (1 << 2)
|
||||
#define INFO1_CARD_RE (1 << 3)
|
||||
#define INFO1_CARD_IN (1 << 4)
|
||||
#define INFO1_ISD0CD (1 << 5)
|
||||
#define INFO1_WRITE_PRO (1 << 7)
|
||||
#define INFO1_DATA3_CARD_RE (1 << 8)
|
||||
#define INFO1_DATA3_CARD_IN (1 << 9)
|
||||
#define INFO1_DATA3 (1 << 10)
|
||||
|
||||
/* SDHI_INFO2 */
|
||||
#define INFO2_CMD_ERROR (1 << 0)
|
||||
#define INFO2_CRC_ERROR (1 << 1)
|
||||
#define INFO2_END_ERROR (1 << 2)
|
||||
#define INFO2_TIMEOUT (1 << 3)
|
||||
#define INFO2_BUF_ILL_WRITE (1 << 4)
|
||||
#define INFO2_BUF_ILL_READ (1 << 5)
|
||||
#define INFO2_RESP_TIMEOUT (1 << 6)
|
||||
#define INFO2_SDDAT0 (1 << 7)
|
||||
#define INFO2_BRE_ENABLE (1 << 8)
|
||||
#define INFO2_BWE_ENABLE (1 << 9)
|
||||
#define INFO2_CBUSY (1 << 14)
|
||||
#define INFO2_ILA (1 << 15)
|
||||
#define INFO2_ALL_ERR (0x807f)
|
||||
|
||||
/* SDHI_INFO1_MASK */
|
||||
#define INFO1M_RESP_END (1 << 0)
|
||||
#define INFO1M_ACCESS_END (1 << 2)
|
||||
#define INFO1M_CARD_RE (1 << 3)
|
||||
#define INFO1M_CARD_IN (1 << 4)
|
||||
#define INFO1M_DATA3_CARD_RE (1 << 8)
|
||||
#define INFO1M_DATA3_CARD_IN (1 << 9)
|
||||
#define INFO1M_ALL (0xffff)
|
||||
#define INFO1M_SET (INFO1M_RESP_END | \
|
||||
INFO1M_ACCESS_END | \
|
||||
INFO1M_DATA3_CARD_RE | \
|
||||
INFO1M_DATA3_CARD_IN)
|
||||
|
||||
/* SDHI_INFO2_MASK */
|
||||
#define INFO2M_CMD_ERROR (1 << 0)
|
||||
#define INFO2M_CRC_ERROR (1 << 1)
|
||||
#define INFO2M_END_ERROR (1 << 2)
|
||||
#define INFO2M_TIMEOUT (1 << 3)
|
||||
#define INFO2M_BUF_ILL_WRITE (1 << 4)
|
||||
#define INFO2M_BUF_ILL_READ (1 << 5)
|
||||
#define INFO2M_RESP_TIMEOUT (1 << 6)
|
||||
#define INFO2M_BRE_ENABLE (1 << 8)
|
||||
#define INFO2M_BWE_ENABLE (1 << 9)
|
||||
#define INFO2M_ILA (1 << 15)
|
||||
#define INFO2M_ALL (0xffff)
|
||||
#define INFO2M_ALL_ERR (0x807f)
|
||||
|
||||
/* SDHI_CLK_CTRL */
|
||||
#define CLK_ENABLE (1 << 8)
|
||||
|
||||
/* SDHI_OPTION */
|
||||
#define OPT_BUS_WIDTH_1 (1 << 15) /* bus width = 1 bit */
|
||||
|
||||
/* SDHI_ERR_STS1 */
|
||||
#define ERR_STS1_CRC_ERROR ((1 << 11) | (1 << 10) | (1 << 9) | \
|
||||
(1 << 8) | (1 << 5))
|
||||
#define ERR_STS1_CMD_ERROR ((1 << 4) | (1 << 3) | (1 << 2) | \
|
||||
(1 << 1) | (1 << 0))
|
||||
|
||||
/* SDHI_ERR_STS2 */
|
||||
#define ERR_STS2_RES_TIMEOUT (1 << 0)
|
||||
#define ERR_STS2_RES_STOP_TIMEOUT ((1 << 0) | (1 << 1))
|
||||
#define ERR_STS2_SYS_ERROR ((1 << 6) | (1 << 5) | (1 << 4) | \
|
||||
(1 << 3) | (1 << 2) | (1 << 1) | \
|
||||
(1 << 0))
|
||||
|
||||
/* SDHI_SDIO_MODE */
|
||||
#define SDIO_MODE_ON (1 << 0)
|
||||
#define SDIO_MODE_OFF (0 << 0)
|
||||
|
||||
/* SDHI_SDIO_INFO1 */
|
||||
#define SDIO_INFO1_IOIRQ (1 << 0)
|
||||
#define SDIO_INFO1_EXPUB52 (1 << 14)
|
||||
#define SDIO_INFO1_EXWT (1 << 15)
|
||||
|
||||
/* SDHI_SDIO_INFO1_MASK */
|
||||
#define SDIO_INFO1M_CLEAR ((1 << 1) | (1 << 2))
|
||||
#define SDIO_INFO1M_ON ((1 << 15) | (1 << 14) | (1 << 2) | \
|
||||
(1 << 1) | (1 << 0))
|
||||
|
||||
/* SDHI_EXT_SWAP */
|
||||
#define SET_SWAP ((1 << 6) | (1 << 7)) /* SWAP */
|
||||
|
||||
/* SDHI_SOFT_RST */
|
||||
#define SOFT_RST_ON (0 << 0)
|
||||
#define SOFT_RST_OFF (1 << 0)
|
||||
|
||||
#define CLKDEV_SD_DATA 25000000 /* 25 MHz */
|
||||
#define CLKDEV_HS_DATA 50000000 /* 50 MHz */
|
||||
#define CLKDEV_MMC_DATA 20000000 /* 20MHz */
|
||||
#define CLKDEV_INIT 400000 /* 100 - 400 KHz */
|
||||
|
||||
/* For quirk */
|
||||
#define SH_SDHI_QUIRK_16BIT_BUF (1)
|
||||
int sh_sdhi_init(unsigned long addr, int ch, unsigned long quirks);
|
||||
|
||||
#endif /* _SH_SDHI_H */
|
205
common/cmd_mmc.c
205
common/cmd_mmc.c
|
@ -73,6 +73,8 @@ U_BOOT_CMD(
|
|||
|
||||
static void print_mmcinfo(struct mmc *mmc)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("Device: %s\n", mmc->cfg->name);
|
||||
printf("Manufacturer ID: %x\n", mmc->cid[0] >> 24);
|
||||
printf("OEM: %x\n", (mmc->cid[0] >> 8) & 0xffff);
|
||||
|
@ -92,6 +94,48 @@ static void print_mmcinfo(struct mmc *mmc)
|
|||
|
||||
printf("Bus Width: %d-bit%s\n", mmc->bus_width,
|
||||
mmc->ddr_mode ? " DDR" : "");
|
||||
|
||||
puts("Erase Group Size: ");
|
||||
print_size(((u64)mmc->erase_grp_size) << 9, "\n");
|
||||
|
||||
if (!IS_SD(mmc) && mmc->version >= MMC_VERSION_4_41) {
|
||||
bool has_enh = (mmc->part_support & ENHNCD_SUPPORT) != 0;
|
||||
bool usr_enh = has_enh && (mmc->part_attr & EXT_CSD_ENH_USR);
|
||||
|
||||
puts("HC WP Group Size: ");
|
||||
print_size(((u64)mmc->hc_wp_grp_size) << 9, "\n");
|
||||
|
||||
puts("User Capacity: ");
|
||||
print_size(mmc->capacity_user, usr_enh ? " ENH" : "");
|
||||
if (mmc->wr_rel_set & EXT_CSD_WR_DATA_REL_USR)
|
||||
puts(" WRREL\n");
|
||||
else
|
||||
putc('\n');
|
||||
if (usr_enh) {
|
||||
puts("User Enhanced Start: ");
|
||||
print_size(mmc->enh_user_start, "\n");
|
||||
puts("User Enhanced Size: ");
|
||||
print_size(mmc->enh_user_size, "\n");
|
||||
}
|
||||
puts("Boot Capacity: ");
|
||||
print_size(mmc->capacity_boot, has_enh ? " ENH\n" : "\n");
|
||||
puts("RPMB Capacity: ");
|
||||
print_size(mmc->capacity_rpmb, has_enh ? " ENH\n" : "\n");
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mmc->capacity_gp); i++) {
|
||||
bool is_enh = has_enh &&
|
||||
(mmc->part_attr & EXT_CSD_ENH_GP(i));
|
||||
if (mmc->capacity_gp[i]) {
|
||||
printf("GP%i Capacity: ", i+1);
|
||||
print_size(mmc->capacity_gp[i],
|
||||
is_enh ? " ENH" : "");
|
||||
if (mmc->wr_rel_set & EXT_CSD_WR_DATA_REL_GP(i))
|
||||
puts(" WRREL\n");
|
||||
else
|
||||
putc('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
static struct mmc *init_mmc_device(int dev, bool force_init)
|
||||
{
|
||||
|
@ -444,6 +488,157 @@ static int do_mmc_list(cmd_tbl_t *cmdtp, int flag,
|
|||
print_mmc_devices('\n');
|
||||
return CMD_RET_SUCCESS;
|
||||
}
|
||||
|
||||
static int parse_hwpart_user(struct mmc_hwpart_conf *pconf,
|
||||
int argc, char * const argv[])
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
memset(&pconf->user, 0, sizeof(pconf->user));
|
||||
|
||||
while (i < argc) {
|
||||
if (!strcmp(argv[i], "enh")) {
|
||||
if (i + 2 >= argc)
|
||||
return -1;
|
||||
pconf->user.enh_start =
|
||||
simple_strtoul(argv[i+1], NULL, 10);
|
||||
pconf->user.enh_size =
|
||||
simple_strtoul(argv[i+2], NULL, 10);
|
||||
i += 3;
|
||||
} else if (!strcmp(argv[i], "wrrel")) {
|
||||
if (i + 1 >= argc)
|
||||
return -1;
|
||||
pconf->user.wr_rel_change = 1;
|
||||
if (!strcmp(argv[i+1], "on"))
|
||||
pconf->user.wr_rel_set = 1;
|
||||
else if (!strcmp(argv[i+1], "off"))
|
||||
pconf->user.wr_rel_set = 0;
|
||||
else
|
||||
return -1;
|
||||
i += 2;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
static int parse_hwpart_gp(struct mmc_hwpart_conf *pconf, int pidx,
|
||||
int argc, char * const argv[])
|
||||
{
|
||||
int i;
|
||||
|
||||
memset(&pconf->gp_part[pidx], 0, sizeof(pconf->gp_part[pidx]));
|
||||
|
||||
if (1 >= argc)
|
||||
return -1;
|
||||
pconf->gp_part[pidx].size = simple_strtoul(argv[0], NULL, 10);
|
||||
|
||||
i = 1;
|
||||
while (i < argc) {
|
||||
if (!strcmp(argv[i], "enh")) {
|
||||
pconf->gp_part[pidx].enhanced = 1;
|
||||
i += 1;
|
||||
} else if (!strcmp(argv[i], "wrrel")) {
|
||||
if (i + 1 >= argc)
|
||||
return -1;
|
||||
pconf->gp_part[pidx].wr_rel_change = 1;
|
||||
if (!strcmp(argv[i+1], "on"))
|
||||
pconf->gp_part[pidx].wr_rel_set = 1;
|
||||
else if (!strcmp(argv[i+1], "off"))
|
||||
pconf->gp_part[pidx].wr_rel_set = 0;
|
||||
else
|
||||
return -1;
|
||||
i += 2;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
static int do_mmc_hwpartition(cmd_tbl_t *cmdtp, int flag,
|
||||
int argc, char * const argv[])
|
||||
{
|
||||
struct mmc *mmc;
|
||||
struct mmc_hwpart_conf pconf = { };
|
||||
enum mmc_hwpart_conf_mode mode = MMC_HWPART_CONF_CHECK;
|
||||
int i, r, pidx;
|
||||
|
||||
mmc = init_mmc_device(curr_device, false);
|
||||
if (!mmc)
|
||||
return CMD_RET_FAILURE;
|
||||
|
||||
if (argc < 1)
|
||||
return CMD_RET_USAGE;
|
||||
i = 1;
|
||||
while (i < argc) {
|
||||
if (!strcmp(argv[i], "user")) {
|
||||
i++;
|
||||
r = parse_hwpart_user(&pconf, argc-i, &argv[i]);
|
||||
if (r < 0)
|
||||
return CMD_RET_USAGE;
|
||||
i += r;
|
||||
} else if (!strncmp(argv[i], "gp", 2) &&
|
||||
strlen(argv[i]) == 3 &&
|
||||
argv[i][2] >= '1' && argv[i][2] <= '4') {
|
||||
pidx = argv[i][2] - '1';
|
||||
i++;
|
||||
r = parse_hwpart_gp(&pconf, pidx, argc-i, &argv[i]);
|
||||
if (r < 0)
|
||||
return CMD_RET_USAGE;
|
||||
i += r;
|
||||
} else if (!strcmp(argv[i], "check")) {
|
||||
mode = MMC_HWPART_CONF_CHECK;
|
||||
i++;
|
||||
} else if (!strcmp(argv[i], "set")) {
|
||||
mode = MMC_HWPART_CONF_SET;
|
||||
i++;
|
||||
} else if (!strcmp(argv[i], "complete")) {
|
||||
mode = MMC_HWPART_CONF_COMPLETE;
|
||||
i++;
|
||||
} else {
|
||||
return CMD_RET_USAGE;
|
||||
}
|
||||
}
|
||||
|
||||
puts("Partition configuration:\n");
|
||||
if (pconf.user.enh_size) {
|
||||
puts("\tUser Enhanced Start: ");
|
||||
print_size(((u64)pconf.user.enh_start) << 9, "\n");
|
||||
puts("\tUser Enhanced Size: ");
|
||||
print_size(((u64)pconf.user.enh_size) << 9, "\n");
|
||||
} else {
|
||||
puts("\tNo enhanced user data area\n");
|
||||
}
|
||||
if (pconf.user.wr_rel_change)
|
||||
printf("\tUser partition write reliability: %s\n",
|
||||
pconf.user.wr_rel_set ? "on" : "off");
|
||||
for (pidx = 0; pidx < 4; pidx++) {
|
||||
if (pconf.gp_part[pidx].size) {
|
||||
printf("\tGP%i Capacity: ", pidx+1);
|
||||
print_size(((u64)pconf.gp_part[pidx].size) << 9,
|
||||
pconf.gp_part[pidx].enhanced ?
|
||||
" ENH\n" : "\n");
|
||||
} else {
|
||||
printf("\tNo GP%i partition\n", pidx+1);
|
||||
}
|
||||
if (pconf.gp_part[pidx].wr_rel_change)
|
||||
printf("\tGP%i write reliability: %s\n", pidx+1,
|
||||
pconf.gp_part[pidx].wr_rel_set ? "on" : "off");
|
||||
}
|
||||
|
||||
if (!mmc_hwpart_config(mmc, &pconf, mode)) {
|
||||
if (mode == MMC_HWPART_CONF_COMPLETE)
|
||||
puts("Partitioning successful, "
|
||||
"power-cycle to make effective\n");
|
||||
return CMD_RET_SUCCESS;
|
||||
} else {
|
||||
puts("Failed!\n");
|
||||
return CMD_RET_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SUPPORT_EMMC_BOOT
|
||||
static int do_mmc_bootbus(cmd_tbl_t *cmdtp, int flag,
|
||||
int argc, char * const argv[])
|
||||
|
@ -601,6 +796,7 @@ static cmd_tbl_t cmd_mmc[] = {
|
|||
U_BOOT_CMD_MKENT(part, 1, 1, do_mmc_part, "", ""),
|
||||
U_BOOT_CMD_MKENT(dev, 3, 0, do_mmc_dev, "", ""),
|
||||
U_BOOT_CMD_MKENT(list, 1, 1, do_mmc_list, "", ""),
|
||||
U_BOOT_CMD_MKENT(hwpartition, 28, 0, do_mmc_hwpartition, "", ""),
|
||||
#ifdef CONFIG_SUPPORT_EMMC_BOOT
|
||||
U_BOOT_CMD_MKENT(bootbus, 5, 0, do_mmc_bootbus, "", ""),
|
||||
U_BOOT_CMD_MKENT(bootpart-resize, 4, 0, do_mmc_boot_resize, "", ""),
|
||||
|
@ -640,7 +836,7 @@ static int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
|||
}
|
||||
|
||||
U_BOOT_CMD(
|
||||
mmc, 7, 1, do_mmcops,
|
||||
mmc, 29, 1, do_mmcops,
|
||||
"MMC sub system",
|
||||
"info - display info of the current MMC device\n"
|
||||
"mmc read addr blk# cnt\n"
|
||||
|
@ -650,6 +846,13 @@ U_BOOT_CMD(
|
|||
"mmc part - lists available partition on current mmc device\n"
|
||||
"mmc dev [dev] [part] - show or set current mmc device [partition]\n"
|
||||
"mmc list - lists available devices\n"
|
||||
"mmc hwpartition [args...] - does hardware partitioning\n"
|
||||
" arguments (sizes in 512-byte blocks):\n"
|
||||
" [user [enh start cnt] [wrrel {on|off}]] - sets user data area attributes\n"
|
||||
" [gp1|gp2|gp3|gp4 cnt [enh] [wrrel {on|off}]] - general purpose partition\n"
|
||||
" [check|set|complete] - mode, complete set partitioning completed\n"
|
||||
" WARNING: Partitioning is a write-once setting once it is set to complete.\n"
|
||||
" Power cycling is required to initialize partitions after set to complete.\n"
|
||||
#ifdef CONFIG_SUPPORT_EMMC_BOOT
|
||||
"mmc bootbus dev boot_bus_width reset_boot_bus_width boot_mode\n"
|
||||
" - Set the BOOT_BUS_WIDTH field of the specified device\n"
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
menu "MMC Host controller Support"
|
||||
|
||||
config SH_SDHI
|
||||
bool "SuperH/Renesas ARM SoCs on-chip SDHI host controller support"
|
||||
depends on RMOBILE
|
||||
help
|
||||
Support for the on-chip SDHI host controller on SuperH/Renesas ARM SoCs platform
|
||||
|
||||
endmenu
|
|
@ -30,6 +30,7 @@ obj-$(CONFIG_S3C_SDI) += s3c_sdi.o
|
|||
obj-$(CONFIG_S5P_SDHCI) += s5p_sdhci.o
|
||||
obj-$(CONFIG_SDHCI) += sdhci.o
|
||||
obj-$(CONFIG_SH_MMCIF) += sh_mmcif.o
|
||||
obj-$(CONFIG_SH_SDHI) += sh_sdhi.o
|
||||
obj-$(CONFIG_SOCFPGA_DWMMC) += socfpga_dw_mmc.o
|
||||
obj-$(CONFIG_SPEAR_SDHCI) += spear_sdhci.o
|
||||
obj-$(CONFIG_TEGRA_MMC) += tegra_mmc.o
|
||||
|
|
|
@ -486,7 +486,7 @@ static int mmc_change_freq(struct mmc *mmc)
|
|||
char cardtype;
|
||||
int err;
|
||||
|
||||
mmc->card_caps = MMC_MODE_4BIT | MMC_MODE_8BIT;
|
||||
mmc->card_caps = 0;
|
||||
|
||||
if (mmc_host_is_spi(mmc))
|
||||
return 0;
|
||||
|
@ -495,6 +495,8 @@ static int mmc_change_freq(struct mmc *mmc)
|
|||
if (mmc->version < MMC_VERSION_4)
|
||||
return 0;
|
||||
|
||||
mmc->card_caps |= MMC_MODE_4BIT | MMC_MODE_8BIT;
|
||||
|
||||
err = mmc_send_ext_csd(mmc, ext_csd);
|
||||
|
||||
if (err)
|
||||
|
@ -605,6 +607,200 @@ int mmc_switch_part(int dev_num, unsigned int part_num)
|
|||
return ret;
|
||||
}
|
||||
|
||||
int mmc_hwpart_config(struct mmc *mmc,
|
||||
const struct mmc_hwpart_conf *conf,
|
||||
enum mmc_hwpart_conf_mode mode)
|
||||
{
|
||||
u8 part_attrs = 0;
|
||||
u32 enh_size_mult;
|
||||
u32 enh_start_addr;
|
||||
u32 gp_size_mult[4];
|
||||
u32 max_enh_size_mult;
|
||||
u32 tot_enh_size_mult = 0;
|
||||
u8 wr_rel_set;
|
||||
int i, pidx, err;
|
||||
ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
|
||||
|
||||
if (mode < MMC_HWPART_CONF_CHECK || mode > MMC_HWPART_CONF_COMPLETE)
|
||||
return -EINVAL;
|
||||
|
||||
if (IS_SD(mmc) || (mmc->version < MMC_VERSION_4_41)) {
|
||||
printf("eMMC >= 4.4 required for enhanced user data area\n");
|
||||
return -EMEDIUMTYPE;
|
||||
}
|
||||
|
||||
if (!(mmc->part_support & PART_SUPPORT)) {
|
||||
printf("Card does not support partitioning\n");
|
||||
return -EMEDIUMTYPE;
|
||||
}
|
||||
|
||||
if (!mmc->hc_wp_grp_size) {
|
||||
printf("Card does not define HC WP group size\n");
|
||||
return -EMEDIUMTYPE;
|
||||
}
|
||||
|
||||
/* check partition alignment and total enhanced size */
|
||||
if (conf->user.enh_size) {
|
||||
if (conf->user.enh_size % mmc->hc_wp_grp_size ||
|
||||
conf->user.enh_start % mmc->hc_wp_grp_size) {
|
||||
printf("User data enhanced area not HC WP group "
|
||||
"size aligned\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
part_attrs |= EXT_CSD_ENH_USR;
|
||||
enh_size_mult = conf->user.enh_size / mmc->hc_wp_grp_size;
|
||||
if (mmc->high_capacity) {
|
||||
enh_start_addr = conf->user.enh_start;
|
||||
} else {
|
||||
enh_start_addr = (conf->user.enh_start << 9);
|
||||
}
|
||||
} else {
|
||||
enh_size_mult = 0;
|
||||
enh_start_addr = 0;
|
||||
}
|
||||
tot_enh_size_mult += enh_size_mult;
|
||||
|
||||
for (pidx = 0; pidx < 4; pidx++) {
|
||||
if (conf->gp_part[pidx].size % mmc->hc_wp_grp_size) {
|
||||
printf("GP%i partition not HC WP group size "
|
||||
"aligned\n", pidx+1);
|
||||
return -EINVAL;
|
||||
}
|
||||
gp_size_mult[pidx] = conf->gp_part[pidx].size / mmc->hc_wp_grp_size;
|
||||
if (conf->gp_part[pidx].size && conf->gp_part[pidx].enhanced) {
|
||||
part_attrs |= EXT_CSD_ENH_GP(pidx);
|
||||
tot_enh_size_mult += gp_size_mult[pidx];
|
||||
}
|
||||
}
|
||||
|
||||
if (part_attrs && ! (mmc->part_support & ENHNCD_SUPPORT)) {
|
||||
printf("Card does not support enhanced attribute\n");
|
||||
return -EMEDIUMTYPE;
|
||||
}
|
||||
|
||||
err = mmc_send_ext_csd(mmc, ext_csd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
max_enh_size_mult =
|
||||
(ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT+2] << 16) +
|
||||
(ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT+1] << 8) +
|
||||
ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT];
|
||||
if (tot_enh_size_mult > max_enh_size_mult) {
|
||||
printf("Total enhanced size exceeds maximum (%u > %u)\n",
|
||||
tot_enh_size_mult, max_enh_size_mult);
|
||||
return -EMEDIUMTYPE;
|
||||
}
|
||||
|
||||
/* The default value of EXT_CSD_WR_REL_SET is device
|
||||
* dependent, the values can only be changed if the
|
||||
* EXT_CSD_HS_CTRL_REL bit is set. The values can be
|
||||
* changed only once and before partitioning is completed. */
|
||||
wr_rel_set = ext_csd[EXT_CSD_WR_REL_SET];
|
||||
if (conf->user.wr_rel_change) {
|
||||
if (conf->user.wr_rel_set)
|
||||
wr_rel_set |= EXT_CSD_WR_DATA_REL_USR;
|
||||
else
|
||||
wr_rel_set &= ~EXT_CSD_WR_DATA_REL_USR;
|
||||
}
|
||||
for (pidx = 0; pidx < 4; pidx++) {
|
||||
if (conf->gp_part[pidx].wr_rel_change) {
|
||||
if (conf->gp_part[pidx].wr_rel_set)
|
||||
wr_rel_set |= EXT_CSD_WR_DATA_REL_GP(pidx);
|
||||
else
|
||||
wr_rel_set &= ~EXT_CSD_WR_DATA_REL_GP(pidx);
|
||||
}
|
||||
}
|
||||
|
||||
if (wr_rel_set != ext_csd[EXT_CSD_WR_REL_SET] &&
|
||||
!(ext_csd[EXT_CSD_WR_REL_PARAM] & EXT_CSD_HS_CTRL_REL)) {
|
||||
puts("Card does not support host controlled partition write "
|
||||
"reliability settings\n");
|
||||
return -EMEDIUMTYPE;
|
||||
}
|
||||
|
||||
if (ext_csd[EXT_CSD_PARTITION_SETTING] &
|
||||
EXT_CSD_PARTITION_SETTING_COMPLETED) {
|
||||
printf("Card already partitioned\n");
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
if (mode == MMC_HWPART_CONF_CHECK)
|
||||
return 0;
|
||||
|
||||
/* Partitioning requires high-capacity size definitions */
|
||||
if (!(ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x01)) {
|
||||
err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_ERASE_GROUP_DEF, 1);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
ext_csd[EXT_CSD_ERASE_GROUP_DEF] = 1;
|
||||
|
||||
/* update erase group size to be high-capacity */
|
||||
mmc->erase_grp_size =
|
||||
ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * 1024;
|
||||
|
||||
}
|
||||
|
||||
/* all OK, write the configuration */
|
||||
for (i = 0; i < 4; i++) {
|
||||
err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_ENH_START_ADDR+i,
|
||||
(enh_start_addr >> (i*8)) & 0xFF);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
for (i = 0; i < 3; i++) {
|
||||
err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_ENH_SIZE_MULT+i,
|
||||
(enh_size_mult >> (i*8)) & 0xFF);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
for (pidx = 0; pidx < 4; pidx++) {
|
||||
for (i = 0; i < 3; i++) {
|
||||
err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_GP_SIZE_MULT+pidx*3+i,
|
||||
(gp_size_mult[pidx] >> (i*8)) & 0xFF);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_PARTITIONS_ATTRIBUTE, part_attrs);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (mode == MMC_HWPART_CONF_SET)
|
||||
return 0;
|
||||
|
||||
/* The WR_REL_SET is a write-once register but shall be
|
||||
* written before setting PART_SETTING_COMPLETED. As it is
|
||||
* write-once we can only write it when completing the
|
||||
* partitioning. */
|
||||
if (wr_rel_set != ext_csd[EXT_CSD_WR_REL_SET]) {
|
||||
err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_WR_REL_SET, wr_rel_set);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Setting PART_SETTING_COMPLETED confirms the partition
|
||||
* configuration but it only becomes effective after power
|
||||
* cycle, so we do not adjust the partition related settings
|
||||
* in the mmc struct. */
|
||||
|
||||
err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_PARTITION_SETTING,
|
||||
EXT_CSD_PARTITION_SETTING_COMPLETED);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mmc_getcd(struct mmc *mmc)
|
||||
{
|
||||
int cd;
|
||||
|
@ -818,6 +1014,8 @@ static int mmc_startup(struct mmc *mmc)
|
|||
ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
|
||||
ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN);
|
||||
int timeout = 1000;
|
||||
bool has_parts = false;
|
||||
bool part_completed;
|
||||
|
||||
#ifdef CONFIG_MMC_SPI_CRC_ON
|
||||
if (mmc_host_is_spi(mmc)) { /* enable CRC check for spi */
|
||||
|
@ -970,7 +1168,9 @@ static int mmc_startup(struct mmc *mmc)
|
|||
if (!IS_SD(mmc) && (mmc->version >= MMC_VERSION_4)) {
|
||||
/* check ext_csd version and capacity */
|
||||
err = mmc_send_ext_csd(mmc, ext_csd);
|
||||
if (!err && (ext_csd[EXT_CSD_REV] >= 2)) {
|
||||
if (err)
|
||||
return err;
|
||||
if (ext_csd[EXT_CSD_REV] >= 2) {
|
||||
/*
|
||||
* According to the JEDEC Standard, the value of
|
||||
* ext_csd's capacity is valid if the value is more
|
||||
|
@ -1006,13 +1206,70 @@ static int mmc_startup(struct mmc *mmc)
|
|||
break;
|
||||
}
|
||||
|
||||
/* The partition data may be non-zero but it is only
|
||||
* effective if PARTITION_SETTING_COMPLETED is set in
|
||||
* EXT_CSD, so ignore any data if this bit is not set,
|
||||
* except for enabling the high-capacity group size
|
||||
* definition (see below). */
|
||||
part_completed = !!(ext_csd[EXT_CSD_PARTITION_SETTING] &
|
||||
EXT_CSD_PARTITION_SETTING_COMPLETED);
|
||||
|
||||
/* store the partition info of emmc */
|
||||
mmc->part_support = ext_csd[EXT_CSD_PARTITIONING_SUPPORT];
|
||||
if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) ||
|
||||
ext_csd[EXT_CSD_BOOT_MULT])
|
||||
mmc->part_config = ext_csd[EXT_CSD_PART_CONF];
|
||||
if (part_completed &&
|
||||
(ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & ENHNCD_SUPPORT))
|
||||
mmc->part_attr = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE];
|
||||
|
||||
mmc->capacity_boot = ext_csd[EXT_CSD_BOOT_MULT] << 17;
|
||||
|
||||
mmc->capacity_rpmb = ext_csd[EXT_CSD_RPMB_MULT] << 17;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
int idx = EXT_CSD_GP_SIZE_MULT + i * 3;
|
||||
uint mult = (ext_csd[idx + 2] << 16) +
|
||||
(ext_csd[idx + 1] << 8) + ext_csd[idx];
|
||||
if (mult)
|
||||
has_parts = true;
|
||||
if (!part_completed)
|
||||
continue;
|
||||
mmc->capacity_gp[i] = mult;
|
||||
mmc->capacity_gp[i] *=
|
||||
ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
|
||||
mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
|
||||
mmc->capacity_gp[i] <<= 19;
|
||||
}
|
||||
|
||||
if (part_completed) {
|
||||
mmc->enh_user_size =
|
||||
(ext_csd[EXT_CSD_ENH_SIZE_MULT+2] << 16) +
|
||||
(ext_csd[EXT_CSD_ENH_SIZE_MULT+1] << 8) +
|
||||
ext_csd[EXT_CSD_ENH_SIZE_MULT];
|
||||
mmc->enh_user_size *= ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
|
||||
mmc->enh_user_size *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
|
||||
mmc->enh_user_size <<= 19;
|
||||
mmc->enh_user_start =
|
||||
(ext_csd[EXT_CSD_ENH_START_ADDR+3] << 24) +
|
||||
(ext_csd[EXT_CSD_ENH_START_ADDR+2] << 16) +
|
||||
(ext_csd[EXT_CSD_ENH_START_ADDR+1] << 8) +
|
||||
ext_csd[EXT_CSD_ENH_START_ADDR];
|
||||
if (mmc->high_capacity)
|
||||
mmc->enh_user_start <<= 9;
|
||||
}
|
||||
|
||||
/*
|
||||
* Host needs to enable ERASE_GRP_DEF bit if device is
|
||||
* partitioned. This bit will be lost every time after a reset
|
||||
* or power off. This will affect erase size.
|
||||
*/
|
||||
if (part_completed)
|
||||
has_parts = true;
|
||||
if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) &&
|
||||
(ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & PART_ENH_ATTRIB)) {
|
||||
(ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & PART_ENH_ATTRIB))
|
||||
has_parts = true;
|
||||
if (has_parts) {
|
||||
err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_ERASE_GROUP_DEF, 1);
|
||||
|
||||
|
@ -1020,19 +1277,18 @@ static int mmc_startup(struct mmc *mmc)
|
|||
return err;
|
||||
else
|
||||
ext_csd[EXT_CSD_ERASE_GROUP_DEF] = 1;
|
||||
}
|
||||
|
||||
if (ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x01) {
|
||||
/* Read out group size from ext_csd */
|
||||
mmc->erase_grp_size =
|
||||
ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] *
|
||||
MMC_MAX_BLOCK_LEN * 1024;
|
||||
ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * 1024;
|
||||
/*
|
||||
* if high capacity and partition setting completed
|
||||
* SEC_COUNT is valid even if it is smaller than 2 GiB
|
||||
* JEDEC Standard JESD84-B45, 6.2.4
|
||||
*/
|
||||
if (mmc->high_capacity &&
|
||||
(ext_csd[EXT_CSD_PARTITION_SETTING] &
|
||||
EXT_CSD_PARTITION_SETTING_COMPLETED)) {
|
||||
if (mmc->high_capacity && part_completed) {
|
||||
capacity = (ext_csd[EXT_CSD_SEC_CNT]) |
|
||||
(ext_csd[EXT_CSD_SEC_CNT + 1] << 8) |
|
||||
(ext_csd[EXT_CSD_SEC_CNT + 2] << 16) |
|
||||
|
@ -1049,23 +1305,11 @@ static int mmc_startup(struct mmc *mmc)
|
|||
* (erase_gmul + 1);
|
||||
}
|
||||
|
||||
/* store the partition info of emmc */
|
||||
if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) ||
|
||||
ext_csd[EXT_CSD_BOOT_MULT])
|
||||
mmc->part_config = ext_csd[EXT_CSD_PART_CONF];
|
||||
mmc->hc_wp_grp_size = 1024
|
||||
* ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]
|
||||
* ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
|
||||
|
||||
mmc->capacity_boot = ext_csd[EXT_CSD_BOOT_MULT] << 17;
|
||||
|
||||
mmc->capacity_rpmb = ext_csd[EXT_CSD_RPMB_MULT] << 17;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
int idx = EXT_CSD_GP_SIZE_MULT + i * 3;
|
||||
mmc->capacity_gp[i] = (ext_csd[idx + 2] << 16) +
|
||||
(ext_csd[idx + 1] << 8) + ext_csd[idx];
|
||||
mmc->capacity_gp[i] *=
|
||||
ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
|
||||
mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
|
||||
}
|
||||
mmc->wr_rel_set = ext_csd[EXT_CSD_WR_REL_SET];
|
||||
}
|
||||
|
||||
err = mmc_set_capacity(mmc, mmc->part_num);
|
||||
|
@ -1107,7 +1351,8 @@ static int mmc_startup(struct mmc *mmc)
|
|||
mmc->tran_speed = 50000000;
|
||||
else
|
||||
mmc->tran_speed = 25000000;
|
||||
} else {
|
||||
} else if (mmc->version >= MMC_VERSION_4) {
|
||||
/* Only version 4 of MMC supports wider bus widths */
|
||||
int idx;
|
||||
|
||||
/* An array of possible bus widths in order of preference */
|
||||
|
@ -1138,6 +1383,18 @@ static int mmc_startup(struct mmc *mmc)
|
|||
unsigned int extw = ext_csd_bits[idx];
|
||||
unsigned int caps = ext_to_hostcaps[extw];
|
||||
|
||||
/*
|
||||
* If the bus width is still not changed,
|
||||
* don't try to set the default again.
|
||||
* Otherwise, recover from switch attempts
|
||||
* by switching to 1-bit bus width.
|
||||
*/
|
||||
if (extw == EXT_CSD_BUS_WIDTH_1 &&
|
||||
mmc->bus_width == 1) {
|
||||
err = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check to make sure the card and controller support
|
||||
* these capabilities
|
||||
|
|
695
drivers/mmc/sh_sdhi.c
Normal file
695
drivers/mmc/sh_sdhi.c
Normal file
|
@ -0,0 +1,695 @@
|
|||
/*
|
||||
* drivers/mmc/sh_sdhi.c
|
||||
*
|
||||
* SD/MMC driver for Renesas rmobile ARM SoCs.
|
||||
*
|
||||
* Copyright (C) 2011,2013-2014 Renesas Electronics Corporation
|
||||
* Copyright (C) 2014 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com>
|
||||
* Copyright (C) 2008-2009 Renesas Solutions Corp.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <malloc.h>
|
||||
#include <mmc.h>
|
||||
#include <asm/errno.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/rmobile.h>
|
||||
#include <asm/arch/sh_sdhi.h>
|
||||
|
||||
#define DRIVER_NAME "sh-sdhi"
|
||||
|
||||
struct sh_sdhi_host {
|
||||
unsigned long addr;
|
||||
int ch;
|
||||
int bus_shift;
|
||||
unsigned long quirks;
|
||||
unsigned char wait_int;
|
||||
unsigned char sd_error;
|
||||
unsigned char detect_waiting;
|
||||
};
|
||||
static inline void sh_sdhi_writew(struct sh_sdhi_host *host, int reg, u16 val)
|
||||
{
|
||||
writew(val, host->addr + (reg << host->bus_shift));
|
||||
}
|
||||
|
||||
static inline u16 sh_sdhi_readw(struct sh_sdhi_host *host, int reg)
|
||||
{
|
||||
return readw(host->addr + (reg << host->bus_shift));
|
||||
}
|
||||
|
||||
static void *mmc_priv(struct mmc *mmc)
|
||||
{
|
||||
return (void *)mmc->priv;
|
||||
}
|
||||
|
||||
static void sh_sdhi_detect(struct sh_sdhi_host *host)
|
||||
{
|
||||
sh_sdhi_writew(host, SDHI_OPTION,
|
||||
OPT_BUS_WIDTH_1 | sh_sdhi_readw(host, SDHI_OPTION));
|
||||
|
||||
host->detect_waiting = 0;
|
||||
}
|
||||
|
||||
static int sh_sdhi_intr(void *dev_id)
|
||||
{
|
||||
struct sh_sdhi_host *host = dev_id;
|
||||
int state1 = 0, state2 = 0;
|
||||
|
||||
state1 = sh_sdhi_readw(host, SDHI_INFO1);
|
||||
state2 = sh_sdhi_readw(host, SDHI_INFO2);
|
||||
|
||||
debug("%s: state1 = %x, state2 = %x\n", __func__, state1, state2);
|
||||
|
||||
/* CARD Insert */
|
||||
if (state1 & INFO1_CARD_IN) {
|
||||
sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_CARD_IN);
|
||||
if (!host->detect_waiting) {
|
||||
host->detect_waiting = 1;
|
||||
sh_sdhi_detect(host);
|
||||
}
|
||||
sh_sdhi_writew(host, SDHI_INFO1_MASK, INFO1M_RESP_END |
|
||||
INFO1M_ACCESS_END | INFO1M_CARD_IN |
|
||||
INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
|
||||
return -EAGAIN;
|
||||
}
|
||||
/* CARD Removal */
|
||||
if (state1 & INFO1_CARD_RE) {
|
||||
sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_CARD_RE);
|
||||
if (!host->detect_waiting) {
|
||||
host->detect_waiting = 1;
|
||||
sh_sdhi_detect(host);
|
||||
}
|
||||
sh_sdhi_writew(host, SDHI_INFO1_MASK, INFO1M_RESP_END |
|
||||
INFO1M_ACCESS_END | INFO1M_CARD_RE |
|
||||
INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
|
||||
sh_sdhi_writew(host, SDHI_SDIO_INFO1_MASK, SDIO_INFO1M_ON);
|
||||
sh_sdhi_writew(host, SDHI_SDIO_MODE, SDIO_MODE_OFF);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (state2 & INFO2_ALL_ERR) {
|
||||
sh_sdhi_writew(host, SDHI_INFO2,
|
||||
(unsigned short)~(INFO2_ALL_ERR));
|
||||
sh_sdhi_writew(host, SDHI_INFO2_MASK,
|
||||
INFO2M_ALL_ERR |
|
||||
sh_sdhi_readw(host, SDHI_INFO2_MASK));
|
||||
host->sd_error = 1;
|
||||
host->wait_int = 1;
|
||||
return 0;
|
||||
}
|
||||
/* Respons End */
|
||||
if (state1 & INFO1_RESP_END) {
|
||||
sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_RESP_END);
|
||||
sh_sdhi_writew(host, SDHI_INFO1_MASK,
|
||||
INFO1M_RESP_END |
|
||||
sh_sdhi_readw(host, SDHI_INFO1_MASK));
|
||||
host->wait_int = 1;
|
||||
return 0;
|
||||
}
|
||||
/* SD_BUF Read Enable */
|
||||
if (state2 & INFO2_BRE_ENABLE) {
|
||||
sh_sdhi_writew(host, SDHI_INFO2, ~INFO2_BRE_ENABLE);
|
||||
sh_sdhi_writew(host, SDHI_INFO2_MASK,
|
||||
INFO2M_BRE_ENABLE | INFO2M_BUF_ILL_READ |
|
||||
sh_sdhi_readw(host, SDHI_INFO2_MASK));
|
||||
host->wait_int = 1;
|
||||
return 0;
|
||||
}
|
||||
/* SD_BUF Write Enable */
|
||||
if (state2 & INFO2_BWE_ENABLE) {
|
||||
sh_sdhi_writew(host, SDHI_INFO2, ~INFO2_BWE_ENABLE);
|
||||
sh_sdhi_writew(host, SDHI_INFO2_MASK,
|
||||
INFO2_BWE_ENABLE | INFO2M_BUF_ILL_WRITE |
|
||||
sh_sdhi_readw(host, SDHI_INFO2_MASK));
|
||||
host->wait_int = 1;
|
||||
return 0;
|
||||
}
|
||||
/* Access End */
|
||||
if (state1 & INFO1_ACCESS_END) {
|
||||
sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_ACCESS_END);
|
||||
sh_sdhi_writew(host, SDHI_INFO1_MASK,
|
||||
INFO1_ACCESS_END |
|
||||
sh_sdhi_readw(host, SDHI_INFO1_MASK));
|
||||
host->wait_int = 1;
|
||||
return 0;
|
||||
}
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
static int sh_sdhi_wait_interrupt_flag(struct sh_sdhi_host *host)
|
||||
{
|
||||
int timeout = 10000000;
|
||||
|
||||
while (1) {
|
||||
timeout--;
|
||||
if (timeout < 0) {
|
||||
debug(DRIVER_NAME": %s timeout\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!sh_sdhi_intr(host))
|
||||
break;
|
||||
|
||||
udelay(1); /* 1 usec */
|
||||
}
|
||||
|
||||
return 1; /* Return value: NOT 0 = complete waiting */
|
||||
}
|
||||
|
||||
static int sh_sdhi_clock_control(struct sh_sdhi_host *host, unsigned long clk)
|
||||
{
|
||||
u32 clkdiv, i, timeout;
|
||||
|
||||
if (sh_sdhi_readw(host, SDHI_INFO2) & (1 << 14)) {
|
||||
printf(DRIVER_NAME": Busy state ! Cannot change the clock\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
sh_sdhi_writew(host, SDHI_CLK_CTRL,
|
||||
~CLK_ENABLE & sh_sdhi_readw(host, SDHI_CLK_CTRL));
|
||||
|
||||
if (clk == 0)
|
||||
return -EIO;
|
||||
|
||||
clkdiv = 0x80;
|
||||
i = CONFIG_SH_SDHI_FREQ >> (0x8 + 1);
|
||||
for (; clkdiv && clk >= (i << 1); (clkdiv >>= 1))
|
||||
i <<= 1;
|
||||
|
||||
sh_sdhi_writew(host, SDHI_CLK_CTRL, clkdiv);
|
||||
|
||||
timeout = 100000;
|
||||
/* Waiting for SD Bus busy to be cleared */
|
||||
while (timeout--) {
|
||||
if ((sh_sdhi_readw(host, SDHI_INFO2) & 0x2000))
|
||||
break;
|
||||
}
|
||||
|
||||
if (timeout)
|
||||
sh_sdhi_writew(host, SDHI_CLK_CTRL,
|
||||
CLK_ENABLE | sh_sdhi_readw(host, SDHI_CLK_CTRL));
|
||||
else
|
||||
return -EBUSY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sh_sdhi_sync_reset(struct sh_sdhi_host *host)
|
||||
{
|
||||
u32 timeout;
|
||||
sh_sdhi_writew(host, SDHI_SOFT_RST, SOFT_RST_ON);
|
||||
sh_sdhi_writew(host, SDHI_SOFT_RST, SOFT_RST_OFF);
|
||||
sh_sdhi_writew(host, SDHI_CLK_CTRL,
|
||||
CLK_ENABLE | sh_sdhi_readw(host, SDHI_CLK_CTRL));
|
||||
|
||||
timeout = 100000;
|
||||
while (timeout--) {
|
||||
if (!(sh_sdhi_readw(host, SDHI_INFO2) & INFO2_CBUSY))
|
||||
break;
|
||||
udelay(100);
|
||||
}
|
||||
|
||||
if (!timeout)
|
||||
return -EBUSY;
|
||||
|
||||
if (host->quirks & SH_SDHI_QUIRK_16BIT_BUF)
|
||||
sh_sdhi_writew(host, SDHI_HOST_MODE, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sh_sdhi_error_manage(struct sh_sdhi_host *host)
|
||||
{
|
||||
unsigned short e_state1, e_state2;
|
||||
int ret;
|
||||
|
||||
host->sd_error = 0;
|
||||
host->wait_int = 0;
|
||||
|
||||
e_state1 = sh_sdhi_readw(host, SDHI_ERR_STS1);
|
||||
e_state2 = sh_sdhi_readw(host, SDHI_ERR_STS2);
|
||||
if (e_state2 & ERR_STS2_SYS_ERROR) {
|
||||
if (e_state2 & ERR_STS2_RES_STOP_TIMEOUT)
|
||||
ret = TIMEOUT;
|
||||
else
|
||||
ret = -EILSEQ;
|
||||
debug("%s: ERR_STS2 = %04x\n",
|
||||
DRIVER_NAME, sh_sdhi_readw(host, SDHI_ERR_STS2));
|
||||
sh_sdhi_sync_reset(host);
|
||||
|
||||
sh_sdhi_writew(host, SDHI_INFO1_MASK,
|
||||
INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
|
||||
return ret;
|
||||
}
|
||||
if (e_state1 & ERR_STS1_CRC_ERROR || e_state1 & ERR_STS1_CMD_ERROR)
|
||||
ret = -EILSEQ;
|
||||
else
|
||||
ret = TIMEOUT;
|
||||
|
||||
debug("%s: ERR_STS1 = %04x\n",
|
||||
DRIVER_NAME, sh_sdhi_readw(host, SDHI_ERR_STS1));
|
||||
sh_sdhi_sync_reset(host);
|
||||
sh_sdhi_writew(host, SDHI_INFO1_MASK,
|
||||
INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sh_sdhi_single_read(struct sh_sdhi_host *host, struct mmc_data *data)
|
||||
{
|
||||
long time;
|
||||
unsigned short blocksize, i;
|
||||
unsigned short *p = (unsigned short *)data->dest;
|
||||
|
||||
if ((unsigned long)p & 0x00000001) {
|
||||
debug(DRIVER_NAME": %s: The data pointer is unaligned.",
|
||||
__func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
host->wait_int = 0;
|
||||
sh_sdhi_writew(host, SDHI_INFO2_MASK,
|
||||
~(INFO2M_BRE_ENABLE | INFO2M_BUF_ILL_READ) &
|
||||
sh_sdhi_readw(host, SDHI_INFO2_MASK));
|
||||
sh_sdhi_writew(host, SDHI_INFO1_MASK,
|
||||
~INFO1M_ACCESS_END &
|
||||
sh_sdhi_readw(host, SDHI_INFO1_MASK));
|
||||
time = sh_sdhi_wait_interrupt_flag(host);
|
||||
if (time == 0 || host->sd_error != 0)
|
||||
return sh_sdhi_error_manage(host);
|
||||
|
||||
host->wait_int = 0;
|
||||
blocksize = sh_sdhi_readw(host, SDHI_SIZE);
|
||||
for (i = 0; i < blocksize / 2; i++)
|
||||
*p++ = sh_sdhi_readw(host, SDHI_BUF0);
|
||||
|
||||
time = sh_sdhi_wait_interrupt_flag(host);
|
||||
if (time == 0 || host->sd_error != 0)
|
||||
return sh_sdhi_error_manage(host);
|
||||
|
||||
host->wait_int = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sh_sdhi_multi_read(struct sh_sdhi_host *host, struct mmc_data *data)
|
||||
{
|
||||
long time;
|
||||
unsigned short blocksize, i, sec;
|
||||
unsigned short *p = (unsigned short *)data->dest;
|
||||
|
||||
if ((unsigned long)p & 0x00000001) {
|
||||
debug(DRIVER_NAME": %s: The data pointer is unaligned.",
|
||||
__func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
debug("%s: blocks = %d, blocksize = %d\n",
|
||||
__func__, data->blocks, data->blocksize);
|
||||
|
||||
host->wait_int = 0;
|
||||
for (sec = 0; sec < data->blocks; sec++) {
|
||||
sh_sdhi_writew(host, SDHI_INFO2_MASK,
|
||||
~(INFO2M_BRE_ENABLE | INFO2M_BUF_ILL_READ) &
|
||||
sh_sdhi_readw(host, SDHI_INFO2_MASK));
|
||||
|
||||
time = sh_sdhi_wait_interrupt_flag(host);
|
||||
if (time == 0 || host->sd_error != 0)
|
||||
return sh_sdhi_error_manage(host);
|
||||
|
||||
host->wait_int = 0;
|
||||
blocksize = sh_sdhi_readw(host, SDHI_SIZE);
|
||||
for (i = 0; i < blocksize / 2; i++)
|
||||
*p++ = sh_sdhi_readw(host, SDHI_BUF0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sh_sdhi_single_write(struct sh_sdhi_host *host,
|
||||
struct mmc_data *data)
|
||||
{
|
||||
long time;
|
||||
unsigned short blocksize, i;
|
||||
const unsigned short *p = (const unsigned short *)data->src;
|
||||
|
||||
if ((unsigned long)p & 0x00000001) {
|
||||
debug(DRIVER_NAME": %s: The data pointer is unaligned.",
|
||||
__func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
debug("%s: blocks = %d, blocksize = %d\n",
|
||||
__func__, data->blocks, data->blocksize);
|
||||
|
||||
host->wait_int = 0;
|
||||
sh_sdhi_writew(host, SDHI_INFO2_MASK,
|
||||
~(INFO2M_BWE_ENABLE | INFO2M_BUF_ILL_WRITE) &
|
||||
sh_sdhi_readw(host, SDHI_INFO2_MASK));
|
||||
sh_sdhi_writew(host, SDHI_INFO1_MASK,
|
||||
~INFO1M_ACCESS_END &
|
||||
sh_sdhi_readw(host, SDHI_INFO1_MASK));
|
||||
|
||||
time = sh_sdhi_wait_interrupt_flag(host);
|
||||
if (time == 0 || host->sd_error != 0)
|
||||
return sh_sdhi_error_manage(host);
|
||||
|
||||
host->wait_int = 0;
|
||||
blocksize = sh_sdhi_readw(host, SDHI_SIZE);
|
||||
for (i = 0; i < blocksize / 2; i++)
|
||||
sh_sdhi_writew(host, SDHI_BUF0, *p++);
|
||||
|
||||
time = sh_sdhi_wait_interrupt_flag(host);
|
||||
if (time == 0 || host->sd_error != 0)
|
||||
return sh_sdhi_error_manage(host);
|
||||
|
||||
host->wait_int = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sh_sdhi_multi_write(struct sh_sdhi_host *host, struct mmc_data *data)
|
||||
{
|
||||
long time;
|
||||
unsigned short i, sec, blocksize;
|
||||
const unsigned short *p = (const unsigned short *)data->src;
|
||||
|
||||
debug("%s: blocks = %d, blocksize = %d\n",
|
||||
__func__, data->blocks, data->blocksize);
|
||||
|
||||
host->wait_int = 0;
|
||||
for (sec = 0; sec < data->blocks; sec++) {
|
||||
sh_sdhi_writew(host, SDHI_INFO2_MASK,
|
||||
~(INFO2M_BWE_ENABLE | INFO2M_BUF_ILL_WRITE) &
|
||||
sh_sdhi_readw(host, SDHI_INFO2_MASK));
|
||||
|
||||
time = sh_sdhi_wait_interrupt_flag(host);
|
||||
if (time == 0 || host->sd_error != 0)
|
||||
return sh_sdhi_error_manage(host);
|
||||
|
||||
host->wait_int = 0;
|
||||
blocksize = sh_sdhi_readw(host, SDHI_SIZE);
|
||||
for (i = 0; i < blocksize / 2; i++)
|
||||
sh_sdhi_writew(host, SDHI_BUF0, *p++);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sh_sdhi_get_response(struct sh_sdhi_host *host, struct mmc_cmd *cmd)
|
||||
{
|
||||
unsigned short i, j, cnt = 1;
|
||||
unsigned short resp[8];
|
||||
unsigned long *p1, *p2;
|
||||
|
||||
if (cmd->resp_type & MMC_RSP_136) {
|
||||
cnt = 4;
|
||||
resp[0] = sh_sdhi_readw(host, SDHI_RSP00);
|
||||
resp[1] = sh_sdhi_readw(host, SDHI_RSP01);
|
||||
resp[2] = sh_sdhi_readw(host, SDHI_RSP02);
|
||||
resp[3] = sh_sdhi_readw(host, SDHI_RSP03);
|
||||
resp[4] = sh_sdhi_readw(host, SDHI_RSP04);
|
||||
resp[5] = sh_sdhi_readw(host, SDHI_RSP05);
|
||||
resp[6] = sh_sdhi_readw(host, SDHI_RSP06);
|
||||
resp[7] = sh_sdhi_readw(host, SDHI_RSP07);
|
||||
|
||||
/* SDHI REGISTER SPECIFICATION */
|
||||
for (i = 7, j = 6; i > 0; i--) {
|
||||
resp[i] = (resp[i] << 8) & 0xff00;
|
||||
resp[i] |= (resp[j--] >> 8) & 0x00ff;
|
||||
}
|
||||
resp[0] = (resp[0] << 8) & 0xff00;
|
||||
|
||||
/* SDHI REGISTER SPECIFICATION */
|
||||
p1 = ((unsigned long *)resp) + 3;
|
||||
|
||||
} else {
|
||||
resp[0] = sh_sdhi_readw(host, SDHI_RSP00);
|
||||
resp[1] = sh_sdhi_readw(host, SDHI_RSP01);
|
||||
|
||||
p1 = ((unsigned long *)resp);
|
||||
}
|
||||
|
||||
p2 = (unsigned long *)cmd->response;
|
||||
#if defined(__BIG_ENDIAN_BITFIELD)
|
||||
for (i = 0; i < cnt; i++) {
|
||||
*p2++ = ((*p1 >> 16) & 0x0000ffff) |
|
||||
((*p1 << 16) & 0xffff0000);
|
||||
p1--;
|
||||
}
|
||||
#else
|
||||
for (i = 0; i < cnt; i++)
|
||||
*p2++ = *p1--;
|
||||
#endif /* __BIG_ENDIAN_BITFIELD */
|
||||
}
|
||||
|
||||
static unsigned short sh_sdhi_set_cmd(struct sh_sdhi_host *host,
|
||||
struct mmc_data *data, unsigned short opc)
|
||||
{
|
||||
switch (opc) {
|
||||
case SD_CMD_APP_SEND_OP_COND:
|
||||
case SD_CMD_APP_SEND_SCR:
|
||||
opc |= SDHI_APP;
|
||||
break;
|
||||
case SD_CMD_APP_SET_BUS_WIDTH:
|
||||
/* SD_APP_SET_BUS_WIDTH*/
|
||||
if (!data)
|
||||
opc |= SDHI_APP;
|
||||
else /* SD_SWITCH */
|
||||
opc = SDHI_SD_SWITCH;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return opc;
|
||||
}
|
||||
|
||||
static unsigned short sh_sdhi_data_trans(struct sh_sdhi_host *host,
|
||||
struct mmc_data *data, unsigned short opc)
|
||||
{
|
||||
unsigned short ret;
|
||||
|
||||
switch (opc) {
|
||||
case MMC_CMD_READ_MULTIPLE_BLOCK:
|
||||
ret = sh_sdhi_multi_read(host, data);
|
||||
break;
|
||||
case MMC_CMD_WRITE_MULTIPLE_BLOCK:
|
||||
ret = sh_sdhi_multi_write(host, data);
|
||||
break;
|
||||
case MMC_CMD_WRITE_SINGLE_BLOCK:
|
||||
ret = sh_sdhi_single_write(host, data);
|
||||
break;
|
||||
case MMC_CMD_READ_SINGLE_BLOCK:
|
||||
case SDHI_SD_APP_SEND_SCR:
|
||||
case SDHI_SD_SWITCH: /* SD_SWITCH */
|
||||
ret = sh_sdhi_single_read(host, data);
|
||||
break;
|
||||
default:
|
||||
printf(DRIVER_NAME": SD: NOT SUPPORT CMD = d'%04d\n", opc);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sh_sdhi_start_cmd(struct sh_sdhi_host *host,
|
||||
struct mmc_data *data, struct mmc_cmd *cmd)
|
||||
{
|
||||
long time;
|
||||
unsigned short opc = cmd->cmdidx;
|
||||
int ret = 0;
|
||||
unsigned long timeout;
|
||||
|
||||
debug("opc = %d, arg = %x, resp_type = %x\n",
|
||||
opc, cmd->cmdarg, cmd->resp_type);
|
||||
|
||||
if (opc == MMC_CMD_STOP_TRANSMISSION) {
|
||||
/* SDHI sends the STOP command automatically by STOP reg */
|
||||
sh_sdhi_writew(host, SDHI_INFO1_MASK, ~INFO1M_ACCESS_END &
|
||||
sh_sdhi_readw(host, SDHI_INFO1_MASK));
|
||||
|
||||
time = sh_sdhi_wait_interrupt_flag(host);
|
||||
if (time == 0 || host->sd_error != 0)
|
||||
return sh_sdhi_error_manage(host);
|
||||
|
||||
sh_sdhi_get_response(host, cmd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (data) {
|
||||
if ((opc == MMC_CMD_READ_MULTIPLE_BLOCK) ||
|
||||
opc == MMC_CMD_WRITE_MULTIPLE_BLOCK) {
|
||||
sh_sdhi_writew(host, SDHI_STOP, STOP_SEC_ENABLE);
|
||||
sh_sdhi_writew(host, SDHI_SECCNT, data->blocks);
|
||||
}
|
||||
sh_sdhi_writew(host, SDHI_SIZE, data->blocksize);
|
||||
}
|
||||
opc = sh_sdhi_set_cmd(host, data, opc);
|
||||
|
||||
/*
|
||||
* U-boot cannot use interrupt.
|
||||
* So this flag may not be clear by timing
|
||||
*/
|
||||
sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_RESP_END);
|
||||
|
||||
sh_sdhi_writew(host, SDHI_INFO1_MASK,
|
||||
INFO1M_RESP_END | sh_sdhi_readw(host, SDHI_INFO1_MASK));
|
||||
sh_sdhi_writew(host, SDHI_ARG0,
|
||||
(unsigned short)(cmd->cmdarg & ARG0_MASK));
|
||||
sh_sdhi_writew(host, SDHI_ARG1,
|
||||
(unsigned short)((cmd->cmdarg >> 16) & ARG1_MASK));
|
||||
|
||||
timeout = 100000;
|
||||
/* Waiting for SD Bus busy to be cleared */
|
||||
while (timeout--) {
|
||||
if ((sh_sdhi_readw(host, SDHI_INFO2) & 0x2000))
|
||||
break;
|
||||
}
|
||||
|
||||
sh_sdhi_writew(host, SDHI_CMD, (unsigned short)(opc & CMD_MASK));
|
||||
|
||||
host->wait_int = 0;
|
||||
sh_sdhi_writew(host, SDHI_INFO1_MASK,
|
||||
~INFO1M_RESP_END & sh_sdhi_readw(host, SDHI_INFO1_MASK));
|
||||
sh_sdhi_writew(host, SDHI_INFO2_MASK,
|
||||
~(INFO2M_CMD_ERROR | INFO2M_CRC_ERROR |
|
||||
INFO2M_END_ERROR | INFO2M_TIMEOUT |
|
||||
INFO2M_RESP_TIMEOUT | INFO2M_ILA) &
|
||||
sh_sdhi_readw(host, SDHI_INFO2_MASK));
|
||||
|
||||
time = sh_sdhi_wait_interrupt_flag(host);
|
||||
if (!time)
|
||||
return sh_sdhi_error_manage(host);
|
||||
|
||||
if (host->sd_error) {
|
||||
switch (cmd->cmdidx) {
|
||||
case MMC_CMD_ALL_SEND_CID:
|
||||
case MMC_CMD_SELECT_CARD:
|
||||
case SD_CMD_SEND_IF_COND:
|
||||
case MMC_CMD_APP_CMD:
|
||||
ret = TIMEOUT;
|
||||
break;
|
||||
default:
|
||||
debug(DRIVER_NAME": Cmd(d'%d) err\n", opc);
|
||||
debug(DRIVER_NAME": cmdidx = %d\n", cmd->cmdidx);
|
||||
ret = sh_sdhi_error_manage(host);
|
||||
break;
|
||||
}
|
||||
host->sd_error = 0;
|
||||
host->wait_int = 0;
|
||||
return ret;
|
||||
}
|
||||
if (sh_sdhi_readw(host, SDHI_INFO1) & INFO1_RESP_END)
|
||||
return -EINVAL;
|
||||
|
||||
if (host->wait_int) {
|
||||
sh_sdhi_get_response(host, cmd);
|
||||
host->wait_int = 0;
|
||||
}
|
||||
if (data)
|
||||
ret = sh_sdhi_data_trans(host, data, opc);
|
||||
|
||||
debug("ret = %d, resp = %08x, %08x, %08x, %08x\n",
|
||||
ret, cmd->response[0], cmd->response[1],
|
||||
cmd->response[2], cmd->response[3]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sh_sdhi_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
|
||||
struct mmc_data *data)
|
||||
{
|
||||
struct sh_sdhi_host *host = mmc_priv(mmc);
|
||||
int ret;
|
||||
|
||||
host->sd_error = 0;
|
||||
|
||||
ret = sh_sdhi_start_cmd(host, data, cmd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sh_sdhi_set_ios(struct mmc *mmc)
|
||||
{
|
||||
int ret;
|
||||
struct sh_sdhi_host *host = mmc_priv(mmc);
|
||||
|
||||
ret = sh_sdhi_clock_control(host, mmc->clock);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
if (mmc->bus_width == 4)
|
||||
sh_sdhi_writew(host, SDHI_OPTION, ~OPT_BUS_WIDTH_1 &
|
||||
sh_sdhi_readw(host, SDHI_OPTION));
|
||||
else
|
||||
sh_sdhi_writew(host, SDHI_OPTION, OPT_BUS_WIDTH_1 |
|
||||
sh_sdhi_readw(host, SDHI_OPTION));
|
||||
|
||||
debug("clock = %d, buswidth = %d\n", mmc->clock, mmc->bus_width);
|
||||
}
|
||||
|
||||
static int sh_sdhi_initialize(struct mmc *mmc)
|
||||
{
|
||||
struct sh_sdhi_host *host = mmc_priv(mmc);
|
||||
int ret = sh_sdhi_sync_reset(host);
|
||||
|
||||
sh_sdhi_writew(host, SDHI_PORTSEL, USE_1PORT);
|
||||
|
||||
#if defined(__BIG_ENDIAN_BITFIELD)
|
||||
sh_sdhi_writew(host, SDHI_EXT_SWAP, SET_SWAP);
|
||||
#endif
|
||||
|
||||
sh_sdhi_writew(host, SDHI_INFO1_MASK, INFO1M_RESP_END |
|
||||
INFO1M_ACCESS_END | INFO1M_CARD_RE |
|
||||
INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct mmc_ops sh_sdhi_ops = {
|
||||
.send_cmd = sh_sdhi_send_cmd,
|
||||
.set_ios = sh_sdhi_set_ios,
|
||||
.init = sh_sdhi_initialize,
|
||||
};
|
||||
|
||||
static struct mmc_config sh_sdhi_cfg = {
|
||||
.name = DRIVER_NAME,
|
||||
.ops = &sh_sdhi_ops,
|
||||
.f_min = CLKDEV_INIT,
|
||||
.f_max = CLKDEV_HS_DATA,
|
||||
.voltages = MMC_VDD_32_33 | MMC_VDD_33_34,
|
||||
.host_caps = MMC_MODE_4BIT | MMC_MODE_HS,
|
||||
.part_type = PART_TYPE_DOS,
|
||||
.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT,
|
||||
};
|
||||
|
||||
int sh_sdhi_init(unsigned long addr, int ch, unsigned long quirks)
|
||||
{
|
||||
int ret = 0;
|
||||
struct mmc *mmc;
|
||||
struct sh_sdhi_host *host = NULL;
|
||||
|
||||
if (ch >= CONFIG_SYS_SH_SDHI_NR_CHANNEL)
|
||||
return -ENODEV;
|
||||
|
||||
host = malloc(sizeof(struct sh_sdhi_host));
|
||||
if (!host)
|
||||
return -ENOMEM;
|
||||
|
||||
mmc = mmc_create(&sh_sdhi_cfg, host);
|
||||
if (!mmc) {
|
||||
ret = -1;
|
||||
goto error;
|
||||
}
|
||||
|
||||
host->ch = ch;
|
||||
host->addr = addr;
|
||||
host->quirks = quirks;
|
||||
|
||||
if (host->quirks & SH_SDHI_QUIRK_16BIT_BUF)
|
||||
host->bus_shift = 1;
|
||||
|
||||
return ret;
|
||||
error:
|
||||
if (host)
|
||||
free(host);
|
||||
return ret;
|
||||
}
|
|
@ -147,11 +147,16 @@
|
|||
/*
|
||||
* EXT_CSD fields
|
||||
*/
|
||||
#define EXT_CSD_ENH_START_ADDR 136 /* R/W */
|
||||
#define EXT_CSD_ENH_SIZE_MULT 140 /* R/W */
|
||||
#define EXT_CSD_GP_SIZE_MULT 143 /* R/W */
|
||||
#define EXT_CSD_PARTITION_SETTING 155 /* R/W */
|
||||
#define EXT_CSD_PARTITIONS_ATTRIBUTE 156 /* R/W */
|
||||
#define EXT_CSD_MAX_ENH_SIZE_MULT 157 /* R */
|
||||
#define EXT_CSD_PARTITIONING_SUPPORT 160 /* RO */
|
||||
#define EXT_CSD_RST_N_FUNCTION 162 /* R/W */
|
||||
#define EXT_CSD_WR_REL_PARAM 166 /* R */
|
||||
#define EXT_CSD_WR_REL_SET 167 /* R/W */
|
||||
#define EXT_CSD_RPMB_MULT 168 /* RO */
|
||||
#define EXT_CSD_ERASE_GROUP_DEF 175 /* R/W */
|
||||
#define EXT_CSD_BOOT_BUS_WIDTH 177
|
||||
|
@ -201,6 +206,14 @@
|
|||
|
||||
#define EXT_CSD_PARTITION_SETTING_COMPLETED (1 << 0)
|
||||
|
||||
#define EXT_CSD_ENH_USR (1 << 0) /* user data area is enhanced */
|
||||
#define EXT_CSD_ENH_GP(x) (1 << ((x)+1)) /* GP part (x+1) is enhanced */
|
||||
|
||||
#define EXT_CSD_HS_CTRL_REL (1 << 0) /* host controlled WR_REL_SET */
|
||||
|
||||
#define EXT_CSD_WR_DATA_REL_USR (1 << 0) /* user data area WR_REL */
|
||||
#define EXT_CSD_WR_DATA_REL_GP(x) (1 << ((x)+1)) /* GP part (x+1) WR_REL */
|
||||
|
||||
#define R1_ILLEGAL_COMMAND (1 << 22)
|
||||
#define R1_APP_CMD (1 << 5)
|
||||
|
||||
|
@ -224,6 +237,7 @@
|
|||
#define MMCPART_NOAVAILABLE (0xff)
|
||||
#define PART_ACCESS_MASK (0x7)
|
||||
#define PART_SUPPORT (0x1)
|
||||
#define ENHNCD_SUPPORT (0x2)
|
||||
#define PART_ENH_ATTRIB (0x1f)
|
||||
|
||||
/* Maximum block size for MMC */
|
||||
|
@ -302,17 +316,23 @@ struct mmc {
|
|||
uint csd[4];
|
||||
uint cid[4];
|
||||
ushort rca;
|
||||
u8 part_support;
|
||||
u8 part_attr;
|
||||
u8 wr_rel_set;
|
||||
char part_config;
|
||||
char part_num;
|
||||
uint tran_speed;
|
||||
uint read_bl_len;
|
||||
uint write_bl_len;
|
||||
uint erase_grp_size;
|
||||
uint erase_grp_size; /* in 512-byte sectors */
|
||||
uint hc_wp_grp_size; /* in 512-byte sectors */
|
||||
u64 capacity;
|
||||
u64 capacity_user;
|
||||
u64 capacity_boot;
|
||||
u64 capacity_rpmb;
|
||||
u64 capacity_gp[4];
|
||||
u64 enh_user_start;
|
||||
u64 enh_user_size;
|
||||
block_dev_desc_t block_dev;
|
||||
char op_cond_pending; /* 1 if we are waiting on an op_cond command */
|
||||
char init_in_progress; /* 1 if we have done mmc_start_init() */
|
||||
|
@ -321,6 +341,27 @@ struct mmc {
|
|||
int ddr_mode;
|
||||
};
|
||||
|
||||
struct mmc_hwpart_conf {
|
||||
struct {
|
||||
uint enh_start; /* in 512-byte sectors */
|
||||
uint enh_size; /* in 512-byte sectors, if 0 no enh area */
|
||||
unsigned wr_rel_change : 1;
|
||||
unsigned wr_rel_set : 1;
|
||||
} user;
|
||||
struct {
|
||||
uint size; /* in 512-byte sectors */
|
||||
unsigned enhanced : 1;
|
||||
unsigned wr_rel_change : 1;
|
||||
unsigned wr_rel_set : 1;
|
||||
} gp_part[4];
|
||||
};
|
||||
|
||||
enum mmc_hwpart_conf_mode {
|
||||
MMC_HWPART_CONF_CHECK,
|
||||
MMC_HWPART_CONF_SET,
|
||||
MMC_HWPART_CONF_COMPLETE,
|
||||
};
|
||||
|
||||
int mmc_register(struct mmc *mmc);
|
||||
struct mmc *mmc_create(const struct mmc_config *cfg, void *priv);
|
||||
void mmc_destroy(struct mmc *mmc);
|
||||
|
@ -333,6 +374,8 @@ int mmc_set_dev(int dev_num);
|
|||
void print_mmc_devices(char separator);
|
||||
int get_mmc_num(void);
|
||||
int mmc_switch_part(int dev_num, unsigned int part_num);
|
||||
int mmc_hwpart_config(struct mmc *mmc, const struct mmc_hwpart_conf *conf,
|
||||
enum mmc_hwpart_conf_mode mode);
|
||||
int mmc_getcd(struct mmc *mmc);
|
||||
int board_mmc_getcd(struct mmc *mmc);
|
||||
int mmc_getwp(struct mmc *mmc);
|
||||
|
|
Loading…
Add table
Reference in a new issue