xref: /minix3/minix/drivers/storage/mmc/emmc.c (revision 4796287659dbd9546688588ed43610eda23d9156)
107cbc27cSLeonardo Fogel #include <minix/blockdriver.h>
207cbc27cSLeonardo Fogel #include <minix/board.h>
307cbc27cSLeonardo Fogel #include <minix/log.h>
407cbc27cSLeonardo Fogel #include <minix/mmio.h>
507cbc27cSLeonardo Fogel #include <minix/spin.h>
607cbc27cSLeonardo Fogel #include <minix/syslib.h>
707cbc27cSLeonardo Fogel 
807cbc27cSLeonardo Fogel #include <sys/mman.h>
907cbc27cSLeonardo Fogel 
1007cbc27cSLeonardo Fogel #include "omap_mmc.h"
1107cbc27cSLeonardo Fogel #include "mmchost.h"
1207cbc27cSLeonardo Fogel #include "sdmmcreg.h"
1307cbc27cSLeonardo Fogel 
1407cbc27cSLeonardo Fogel /* MINIX IRQ timeout. Twice the host controller data/busy timeout @ 48MHz. */
1507cbc27cSLeonardo Fogel #define IRQ_TIMEOUT 5600000 /* 5,600,000 us */
1607cbc27cSLeonardo Fogel 
1707cbc27cSLeonardo Fogel #define MMCHS_TIMEOUT 500000 /* 500,000 us */
1807cbc27cSLeonardo Fogel 
1907cbc27cSLeonardo Fogel /* Reference clock frequency divisors: */
2007cbc27cSLeonardo Fogel #define MMCHS_SD_SYSCTL_CLKD_400KHZ 240 /* 96MHz/400kHz */
2107cbc27cSLeonardo Fogel #define MMCHS_SD_SYSCTL_CLKD_26MHZ    4 /* ceiling 96MHz/26MHz */
2207cbc27cSLeonardo Fogel #define MMCHS_SD_SYSCTL_CLKD_52MHZ    2 /* ceiling 96MHz/52MHz */
2307cbc27cSLeonardo Fogel 
2407cbc27cSLeonardo Fogel /* The host SD_DATA register is 128 words (512B). */
2507cbc27cSLeonardo Fogel #define SD_DATA_WLEN 128
2607cbc27cSLeonardo Fogel 
2707cbc27cSLeonardo Fogel /*
2807cbc27cSLeonardo Fogel  * Card initialization timeout, twice the standard:
2907cbc27cSLeonardo Fogel  * "The device must complete its initialization within 1 second of the first
3007cbc27cSLeonardo Fogel  * CMD1 issued with a valid OCR range." (MMCA, 4.41)
3107cbc27cSLeonardo Fogel  */
3207cbc27cSLeonardo Fogel #define CARD_INI_TIMEOUT 2000000 /* 2,000,000 us */
3307cbc27cSLeonardo Fogel 
3407cbc27cSLeonardo Fogel /* Card EXT_CSD register fields. */
3507cbc27cSLeonardo Fogel #define MMC_EXT_CSD_SEC_COUNT (*(uint32_t *)&card_ext_csd[212])
3607cbc27cSLeonardo Fogel #define MMC_EXT_CSD_CARD_TYPE (card_ext_csd[196])
3707cbc27cSLeonardo Fogel #define MMC_EXT_CSD_CARD_TYPE_HS_MMC_52MHZ (0x1 << 1)
3807cbc27cSLeonardo Fogel 
3907cbc27cSLeonardo Fogel /* Card intended operating voltage range: 2.7V to 3.6V */
4007cbc27cSLeonardo Fogel #define MMC_OCR_VDD_RANGE 0x00FF8000
4107cbc27cSLeonardo Fogel 
4207cbc27cSLeonardo Fogel /* Error bits in the card status (R1) response. */
4307cbc27cSLeonardo Fogel #define R1_ERROR_MASK 0xFDFFA080
4407cbc27cSLeonardo Fogel 
4507cbc27cSLeonardo Fogel /* Relative Card Address. Must be greater than 1. */
4607cbc27cSLeonardo Fogel #define RCA 0x2
4707cbc27cSLeonardo Fogel 
4807cbc27cSLeonardo Fogel /* The card sector size is 512B. */
4907cbc27cSLeonardo Fogel #define SEC_SIZE 512
5007cbc27cSLeonardo Fogel 
51*47962876SLeonardo Fogel /*
52*47962876SLeonardo Fogel  * AM335x Control Module registers CONF_GPMC_ADn.
53*47962876SLeonardo Fogel  * Configuration do multiplex CONF_GPMC_ADn to signals MMC1_DATn (Mode 1).
54*47962876SLeonardo Fogel  */
55*47962876SLeonardo Fogel #define CONF_GPMC_AD(N)  (0x800 + 4*(N))
56*47962876SLeonardo Fogel #define CONF_GPMC_AD_MASK 0x7F
57*47962876SLeonardo Fogel #define CONF_GPMC_AD_VAL  0x31
58*47962876SLeonardo Fogel 
5907cbc27cSLeonardo Fogel /* AM335x MMC1 memory map (physical start address and size). */
6007cbc27cSLeonardo Fogel #define AM335X_MMC1_BASE_ADDR 0x481D8000
6107cbc27cSLeonardo Fogel #define AM335X_MMC1_SIZE (4 << 10)
6207cbc27cSLeonardo Fogel /* AM335x MMC1 interrupt number. */
6307cbc27cSLeonardo Fogel #define AM335X_MMCSD1INT 28
6407cbc27cSLeonardo Fogel 
65*47962876SLeonardo Fogel static uint32_t bus_width;
66*47962876SLeonardo Fogel 
6707cbc27cSLeonardo Fogel /* AM335x MMCHS registers virtual addresses: virtual base + offset. */
6807cbc27cSLeonardo Fogel static struct omap_mmchs_registers *reg;
6907cbc27cSLeonardo Fogel 
7007cbc27cSLeonardo Fogel /* Card registers. */
7107cbc27cSLeonardo Fogel static uint32_t card_csd[4];
7207cbc27cSLeonardo Fogel static uint8_t card_ext_csd[512];
7307cbc27cSLeonardo Fogel 
7407cbc27cSLeonardo Fogel static uint32_t card_write_protect;
7507cbc27cSLeonardo Fogel static uint64_t card_size;
7607cbc27cSLeonardo Fogel 
7707cbc27cSLeonardo Fogel /* IRQ_HOOK_ID for SYS_IRQCTL kernel call. */
7807cbc27cSLeonardo Fogel static int hook_id = 1;
7907cbc27cSLeonardo Fogel 
8007cbc27cSLeonardo Fogel /* Initialize the log system. */
8107cbc27cSLeonardo Fogel static struct log log = {
8207cbc27cSLeonardo Fogel 	.name = "emmc",
8307cbc27cSLeonardo Fogel 	.log_level = LEVEL_INFO,
8407cbc27cSLeonardo Fogel 	.log_func = default_log,
8507cbc27cSLeonardo Fogel };
8607cbc27cSLeonardo Fogel 
8707cbc27cSLeonardo Fogel 
8807cbc27cSLeonardo Fogel /*
8907cbc27cSLeonardo Fogel  * Spin until a register flag is set, or the time runs out.
9007cbc27cSLeonardo Fogel  * Return the flag value.
9107cbc27cSLeonardo Fogel  */
9207cbc27cSLeonardo Fogel static uint32_t
spin_until_set(uint32_t address,uint32_t flag)9307cbc27cSLeonardo Fogel spin_until_set(uint32_t address, uint32_t flag)
9407cbc27cSLeonardo Fogel {
9507cbc27cSLeonardo Fogel 	spin_t s;
9607cbc27cSLeonardo Fogel 	int spin;
9707cbc27cSLeonardo Fogel 	uint32_t v;
9807cbc27cSLeonardo Fogel 
9907cbc27cSLeonardo Fogel 	spin_init(&s, MMCHS_TIMEOUT);
10007cbc27cSLeonardo Fogel 	do {
10107cbc27cSLeonardo Fogel 		spin = spin_check(&s);
10207cbc27cSLeonardo Fogel 		v = (read32(address) & flag);
10307cbc27cSLeonardo Fogel 	} while ((v == 0) && (spin == TRUE));
10407cbc27cSLeonardo Fogel 
10507cbc27cSLeonardo Fogel 	return v;
10607cbc27cSLeonardo Fogel }
10707cbc27cSLeonardo Fogel 
10807cbc27cSLeonardo Fogel /*
10907cbc27cSLeonardo Fogel  * Spin until a register flag is clear, or the time runs out.
11007cbc27cSLeonardo Fogel  * Return the flag value.
11107cbc27cSLeonardo Fogel  */
11207cbc27cSLeonardo Fogel static uint32_t
spin_until_clear(uint32_t address,uint32_t flag)11307cbc27cSLeonardo Fogel spin_until_clear(uint32_t address, uint32_t flag)
11407cbc27cSLeonardo Fogel {
11507cbc27cSLeonardo Fogel 	spin_t s;
11607cbc27cSLeonardo Fogel 	int spin;
11707cbc27cSLeonardo Fogel 	uint32_t v;
11807cbc27cSLeonardo Fogel 
11907cbc27cSLeonardo Fogel 	spin_init(&s, MMCHS_TIMEOUT);
12007cbc27cSLeonardo Fogel 	do {
12107cbc27cSLeonardo Fogel 		spin = spin_check(&s);
12207cbc27cSLeonardo Fogel 		v = (read32(address) & flag);
12307cbc27cSLeonardo Fogel 	} while ((v != 0) && (spin == TRUE));
12407cbc27cSLeonardo Fogel 
12507cbc27cSLeonardo Fogel 	return v;
12607cbc27cSLeonardo Fogel }
12707cbc27cSLeonardo Fogel 
12807cbc27cSLeonardo Fogel /*
12907cbc27cSLeonardo Fogel  * Change the bus clock frequency (divisor).
13007cbc27cSLeonardo Fogel  * Return 0 on success, a negative integer on error.
13107cbc27cSLeonardo Fogel  */
13207cbc27cSLeonardo Fogel static int
set_bus_clkd(uint32_t clkd)13307cbc27cSLeonardo Fogel set_bus_clkd(uint32_t clkd)
13407cbc27cSLeonardo Fogel {
13507cbc27cSLeonardo Fogel 	/*
13607cbc27cSLeonardo Fogel 	 * Disable the bus clock, set the clock divider, wait until the
13707cbc27cSLeonardo Fogel 	 * internal clock is stable, enable the bus clock.
13807cbc27cSLeonardo Fogel 	 */
13907cbc27cSLeonardo Fogel 	set32(reg->SYSCTL, MMCHS_SD_SYSCTL_CEN, MMCHS_SD_SYSCTL_CEN_DIS);
14007cbc27cSLeonardo Fogel 	set32(reg->SYSCTL, MMCHS_SD_SYSCTL_CLKD, clkd << 6);
14107cbc27cSLeonardo Fogel 	if (spin_until_set(reg->SYSCTL, MMCHS_SD_SYSCTL_ICS)
14207cbc27cSLeonardo Fogel 		== MMCHS_SD_SYSCTL_ICS_UNSTABLE)
14307cbc27cSLeonardo Fogel 		return -1;
14407cbc27cSLeonardo Fogel 	set32(reg->SYSCTL, MMCHS_SD_SYSCTL_CEN, MMCHS_SD_SYSCTL_CEN_EN);
14507cbc27cSLeonardo Fogel 
14607cbc27cSLeonardo Fogel 	return 0;
14707cbc27cSLeonardo Fogel }
14807cbc27cSLeonardo Fogel 
14907cbc27cSLeonardo Fogel /*
15007cbc27cSLeonardo Fogel  * Receive an interrupt request.
15107cbc27cSLeonardo Fogel  * Return 0 on success, a negative integer on error.
15207cbc27cSLeonardo Fogel  */
15307cbc27cSLeonardo Fogel static int
irq_receive(void)15407cbc27cSLeonardo Fogel irq_receive(void)
15507cbc27cSLeonardo Fogel {
15607cbc27cSLeonardo Fogel 	message m;
15707cbc27cSLeonardo Fogel 	int ipc_status;
15807cbc27cSLeonardo Fogel 
15907cbc27cSLeonardo Fogel 	while (1) {
16007cbc27cSLeonardo Fogel 		if (driver_receive(ANY, &m, &ipc_status) != OK)
16107cbc27cSLeonardo Fogel 			return -1;
16207cbc27cSLeonardo Fogel 		if (is_ipc_notify(ipc_status)
16307cbc27cSLeonardo Fogel 			&& (_ENDPOINT_P(m.m_source) == CLOCK))
16407cbc27cSLeonardo Fogel 			return -1;
16507cbc27cSLeonardo Fogel 		if (is_ipc_notify(ipc_status)
16607cbc27cSLeonardo Fogel 			&& (_ENDPOINT_P(m.m_source) == HARDWARE))
16707cbc27cSLeonardo Fogel 			return 0;
16807cbc27cSLeonardo Fogel 		/*
16907cbc27cSLeonardo Fogel 		 * m will be discarded if the driver is out of memory.
17007cbc27cSLeonardo Fogel 		 */
17107cbc27cSLeonardo Fogel 		blockdriver_mq_queue(&m, ipc_status);
17207cbc27cSLeonardo Fogel 	}
17307cbc27cSLeonardo Fogel }
17407cbc27cSLeonardo Fogel 
17507cbc27cSLeonardo Fogel /*
17607cbc27cSLeonardo Fogel  * Wait for an interrupt request.
17707cbc27cSLeonardo Fogel  * Return 0 on interrupt, a negative integer on error.
17807cbc27cSLeonardo Fogel  */
17907cbc27cSLeonardo Fogel static int
irq_wait(void)18007cbc27cSLeonardo Fogel irq_wait(void)
18107cbc27cSLeonardo Fogel {
18207cbc27cSLeonardo Fogel 	int r;
18307cbc27cSLeonardo Fogel 
18407cbc27cSLeonardo Fogel 	if (sys_irqenable(&hook_id) != OK)
18507cbc27cSLeonardo Fogel 		return -1;
18607cbc27cSLeonardo Fogel 	sys_setalarm(micros_to_ticks(IRQ_TIMEOUT), 0);
18707cbc27cSLeonardo Fogel 	r = irq_receive();
18807cbc27cSLeonardo Fogel 	sys_setalarm(0, 0);
18907cbc27cSLeonardo Fogel 	if (r < 0)
19007cbc27cSLeonardo Fogel 		sys_irqdisable(&hook_id);
19107cbc27cSLeonardo Fogel 
19207cbc27cSLeonardo Fogel 	return r;
19307cbc27cSLeonardo Fogel }
19407cbc27cSLeonardo Fogel 
19507cbc27cSLeonardo Fogel /*
19607cbc27cSLeonardo Fogel  * Software reset for mmc_cmd or mmc_dat line.
19707cbc27cSLeonardo Fogel  */
19807cbc27cSLeonardo Fogel static void
reset_mmchs_fsm(uint32_t line)19907cbc27cSLeonardo Fogel reset_mmchs_fsm(uint32_t line)
20007cbc27cSLeonardo Fogel {
20107cbc27cSLeonardo Fogel 	/*
20207cbc27cSLeonardo Fogel 	 * "The proper procedure is: (a) Set to 1 to start reset,
20307cbc27cSLeonardo Fogel 	 * (b) Poll for 1 to identify start of reset, and
20407cbc27cSLeonardo Fogel 	 * (c) Poll for 0 to identify reset is complete." (AM335x TRM)
20507cbc27cSLeonardo Fogel 	 */
20607cbc27cSLeonardo Fogel 	set32(reg->SYSCTL, line, line);
20707cbc27cSLeonardo Fogel 	spin_until_set(reg->SYSCTL, line);
20807cbc27cSLeonardo Fogel 	spin_until_clear(reg->SYSCTL, line);
20907cbc27cSLeonardo Fogel }
21007cbc27cSLeonardo Fogel 
21107cbc27cSLeonardo Fogel /*
21207cbc27cSLeonardo Fogel  * Send a command to the card.
21307cbc27cSLeonardo Fogel  * Return 0 on success, a negative integer on error.
21407cbc27cSLeonardo Fogel  */
21507cbc27cSLeonardo Fogel static int
send_cmd(uint32_t arg,uint32_t cmd)21607cbc27cSLeonardo Fogel send_cmd(uint32_t arg, uint32_t cmd)
21707cbc27cSLeonardo Fogel {
21807cbc27cSLeonardo Fogel 	uint32_t stat;
21907cbc27cSLeonardo Fogel 
22007cbc27cSLeonardo Fogel 	if (read32(reg->PSTATE)
22107cbc27cSLeonardo Fogel 		& (MMCHS_SD_PSTATE_DATI | MMCHS_SD_PSTATE_CMDI))
22207cbc27cSLeonardo Fogel 		return -1; /* Issuing of commands is not allowed. */
22307cbc27cSLeonardo Fogel 	write32(reg->ARG, arg);
22407cbc27cSLeonardo Fogel 	write32(reg->CMD, cmd);
22507cbc27cSLeonardo Fogel 	/* Wait for the command completion. */
22607cbc27cSLeonardo Fogel 	if (irq_wait() < 0)
22707cbc27cSLeonardo Fogel 		return -1;
22807cbc27cSLeonardo Fogel 	stat = read32(reg->SD_STAT);
22907cbc27cSLeonardo Fogel 	/*
23007cbc27cSLeonardo Fogel 	 * Clear only the command status/error bits. The transfer status/error
23107cbc27cSLeonardo Fogel 	 * bits (including ERRI) must be preserved.
23207cbc27cSLeonardo Fogel 	 */
23307cbc27cSLeonardo Fogel 	write32(reg->SD_STAT, MMCHS_SD_STAT_CIE
23407cbc27cSLeonardo Fogel 		| MMCHS_SD_STAT_CEB
23507cbc27cSLeonardo Fogel 		| MMCHS_SD_STAT_CCRC
23607cbc27cSLeonardo Fogel 		| MMCHS_SD_STAT_CTO
23707cbc27cSLeonardo Fogel 		| MMCHS_SD_STAT_CC);
23807cbc27cSLeonardo Fogel 	if (stat & MMCHS_SD_STAT_CTO) {
23907cbc27cSLeonardo Fogel 		reset_mmchs_fsm(MMCHS_SD_SYSCTL_SRC);
24007cbc27cSLeonardo Fogel 		return -1;
24107cbc27cSLeonardo Fogel 	}
24207cbc27cSLeonardo Fogel 
24307cbc27cSLeonardo Fogel 	return 0;
24407cbc27cSLeonardo Fogel }
24507cbc27cSLeonardo Fogel 
24607cbc27cSLeonardo Fogel /*
24707cbc27cSLeonardo Fogel  * Send a command to the card, and check for errors in the response (R1).
24807cbc27cSLeonardo Fogel  * Return 0 on success, a negative integer on error.
24907cbc27cSLeonardo Fogel  */
25007cbc27cSLeonardo Fogel static int
send_cmd_check_r1(uint32_t arg,uint32_t cmd)25107cbc27cSLeonardo Fogel send_cmd_check_r1(uint32_t arg, uint32_t cmd)
25207cbc27cSLeonardo Fogel {
25307cbc27cSLeonardo Fogel 	if (send_cmd(arg, cmd) < 0)
25407cbc27cSLeonardo Fogel 		return -1;
25507cbc27cSLeonardo Fogel 	/* Check for card errors in the card response (R1). */
25607cbc27cSLeonardo Fogel 	if (read32(reg->RSP10) & R1_ERROR_MASK)
25707cbc27cSLeonardo Fogel 		return -1;
25807cbc27cSLeonardo Fogel 
25907cbc27cSLeonardo Fogel 	return 0;
26007cbc27cSLeonardo Fogel }
26107cbc27cSLeonardo Fogel 
26207cbc27cSLeonardo Fogel /* Send CMD0 (GO_IDLE_STATE) command to the card. */
26307cbc27cSLeonardo Fogel static int
go_idle_state(void)26407cbc27cSLeonardo Fogel go_idle_state(void)
26507cbc27cSLeonardo Fogel {
26607cbc27cSLeonardo Fogel 	return send_cmd(MMC_GO_IDLE_STATE, MMC_GO_IDLE_STATE);
26707cbc27cSLeonardo Fogel }
26807cbc27cSLeonardo Fogel 
26907cbc27cSLeonardo Fogel /* Send CMD1 (SEND_OP_COND) command to the card. */
27007cbc27cSLeonardo Fogel static int
send_op_cond(void)27107cbc27cSLeonardo Fogel send_op_cond(void)
27207cbc27cSLeonardo Fogel {
27307cbc27cSLeonardo Fogel 	uint32_t cmd;
27407cbc27cSLeonardo Fogel 
27507cbc27cSLeonardo Fogel 	/* The driver is capable of handling sector type of addressing. */
27607cbc27cSLeonardo Fogel 	cmd = MMCHS_SD_CMD_INDX_CMD(MMC_SEND_OP_COND)
27707cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_RSP_TYPE_48B;
27807cbc27cSLeonardo Fogel 	return send_cmd((MMC_OCR_HCS | MMC_OCR_VDD_RANGE), cmd);
27907cbc27cSLeonardo Fogel }
28007cbc27cSLeonardo Fogel 
28107cbc27cSLeonardo Fogel /* Send CMD2 (ALL_SEND_CID) command to the card. */
28207cbc27cSLeonardo Fogel static int
all_send_cid(void)28307cbc27cSLeonardo Fogel all_send_cid(void)
28407cbc27cSLeonardo Fogel {
28507cbc27cSLeonardo Fogel 	uint32_t cmd;
28607cbc27cSLeonardo Fogel 
28707cbc27cSLeonardo Fogel 	cmd = MMCHS_SD_CMD_INDX_CMD(MMC_ALL_SEND_CID)
28807cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_CCCE_ENABLE
28907cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_RSP_TYPE_136B;
29007cbc27cSLeonardo Fogel 	return send_cmd(0, cmd);
29107cbc27cSLeonardo Fogel }
29207cbc27cSLeonardo Fogel 
29307cbc27cSLeonardo Fogel /* Send CMD3 (SET_RELATIVE_ADDR) command to the card. */
29407cbc27cSLeonardo Fogel static int
set_relative_addr(void)29507cbc27cSLeonardo Fogel set_relative_addr(void)
29607cbc27cSLeonardo Fogel {
29707cbc27cSLeonardo Fogel 	uint32_t cmd;
29807cbc27cSLeonardo Fogel 
29907cbc27cSLeonardo Fogel 	cmd = MMCHS_SD_CMD_INDX_CMD(MMC_SET_RELATIVE_ADDR)
30007cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_CICE_ENABLE
30107cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_CCCE_ENABLE
30207cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_RSP_TYPE_48B;
30307cbc27cSLeonardo Fogel 	return send_cmd_check_r1(MMC_ARG_RCA(RCA), cmd);
30407cbc27cSLeonardo Fogel }
30507cbc27cSLeonardo Fogel 
30607cbc27cSLeonardo Fogel /* Send CMD6 (SWITCH) command to the card. */
30707cbc27cSLeonardo Fogel static int
mmc_switch(uint32_t access,uint32_t index,uint32_t value)30807cbc27cSLeonardo Fogel mmc_switch(uint32_t access, uint32_t index, uint32_t value)
30907cbc27cSLeonardo Fogel {
31007cbc27cSLeonardo Fogel 	uint32_t arg, cmd;
31107cbc27cSLeonardo Fogel 
31207cbc27cSLeonardo Fogel 	/* SWITCH argument: [25:24] Access, [23:16] Index, [15:8] Value. */
31307cbc27cSLeonardo Fogel 	arg = (access << 24) | (index << 16) | (value << 8);
31407cbc27cSLeonardo Fogel 	cmd = MMCHS_SD_CMD_INDX_CMD(MMC_SWITCH)
31507cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_CICE_ENABLE
31607cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_CCCE_ENABLE
31707cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_RSP_TYPE_48B_BUSY;
31807cbc27cSLeonardo Fogel 	return send_cmd_check_r1(arg, cmd);
31907cbc27cSLeonardo Fogel }
32007cbc27cSLeonardo Fogel 
32107cbc27cSLeonardo Fogel /* Send CMD7 (SELECT_CARD) command to the card. */
32207cbc27cSLeonardo Fogel static int
select_card(void)32307cbc27cSLeonardo Fogel select_card(void)
32407cbc27cSLeonardo Fogel {
32507cbc27cSLeonardo Fogel 	uint32_t cmd;
32607cbc27cSLeonardo Fogel 
32707cbc27cSLeonardo Fogel 	cmd = MMCHS_SD_CMD_INDX_CMD(MMC_SELECT_CARD)
32807cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_CICE_ENABLE
32907cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_CCCE_ENABLE
33007cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_RSP_TYPE_48B;
33107cbc27cSLeonardo Fogel 	return send_cmd_check_r1(MMC_ARG_RCA(RCA), cmd);
33207cbc27cSLeonardo Fogel }
33307cbc27cSLeonardo Fogel 
33407cbc27cSLeonardo Fogel /* Send CMD8 (SEND_EXT_CSD) command to the card. */
33507cbc27cSLeonardo Fogel static int
send_ext_csd(void)33607cbc27cSLeonardo Fogel send_ext_csd(void)
33707cbc27cSLeonardo Fogel {
33807cbc27cSLeonardo Fogel 	uint32_t cmd;
33907cbc27cSLeonardo Fogel 
34007cbc27cSLeonardo Fogel 	cmd = MMCHS_SD_CMD_INDX_CMD(MMC_SEND_EXT_CSD)
34107cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_DP_DATA
34207cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_CICE_ENABLE
34307cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_CCCE_ENABLE
34407cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_RSP_TYPE_48B
34507cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_DDIR_READ;
34607cbc27cSLeonardo Fogel 	return send_cmd_check_r1(0, cmd);
34707cbc27cSLeonardo Fogel }
34807cbc27cSLeonardo Fogel 
34907cbc27cSLeonardo Fogel /* Send CMD9 (SEND_CSD) command to the card. */
35007cbc27cSLeonardo Fogel static int
send_csd(void)35107cbc27cSLeonardo Fogel send_csd(void)
35207cbc27cSLeonardo Fogel {
35307cbc27cSLeonardo Fogel 	uint32_t cmd;
35407cbc27cSLeonardo Fogel 
35507cbc27cSLeonardo Fogel 	cmd = MMCHS_SD_CMD_INDX_CMD(MMC_SEND_CSD)
35607cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_CCCE_ENABLE
35707cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_RSP_TYPE_136B;
35807cbc27cSLeonardo Fogel 	return send_cmd(MMC_ARG_RCA(RCA), cmd);
35907cbc27cSLeonardo Fogel }
36007cbc27cSLeonardo Fogel 
36107cbc27cSLeonardo Fogel /* Send CMD13 (SEND_STATUS) command to the card. */
36207cbc27cSLeonardo Fogel static int
send_status(void)36307cbc27cSLeonardo Fogel send_status(void)
36407cbc27cSLeonardo Fogel {
36507cbc27cSLeonardo Fogel 	uint32_t cmd;
36607cbc27cSLeonardo Fogel 
36707cbc27cSLeonardo Fogel 	cmd = MMCHS_SD_CMD_INDX_CMD(MMC_SEND_STATUS)
36807cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_CICE_ENABLE
36907cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_CCCE_ENABLE
37007cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_RSP_TYPE_48B;
37107cbc27cSLeonardo Fogel 	return send_cmd_check_r1(MMC_ARG_RCA(RCA), cmd);
37207cbc27cSLeonardo Fogel }
37307cbc27cSLeonardo Fogel 
37407cbc27cSLeonardo Fogel /* Send CMD16 (SET_BLOCKLEN) command to the card. */
37507cbc27cSLeonardo Fogel static int
set_blocklen(void)37607cbc27cSLeonardo Fogel set_blocklen(void)
37707cbc27cSLeonardo Fogel {
37807cbc27cSLeonardo Fogel 	uint32_t cmd;
37907cbc27cSLeonardo Fogel 
38007cbc27cSLeonardo Fogel 	/* Set block length to sector size (512B). */
38107cbc27cSLeonardo Fogel 	cmd = MMCHS_SD_CMD_INDX_CMD(MMC_SET_BLOCKLEN)
38207cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_CICE_ENABLE
38307cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_CCCE_ENABLE
38407cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_RSP_TYPE_48B;
38507cbc27cSLeonardo Fogel 	return send_cmd_check_r1(SEC_SIZE, cmd);
38607cbc27cSLeonardo Fogel }
38707cbc27cSLeonardo Fogel 
38807cbc27cSLeonardo Fogel /* Send CMD17 (READ_SINGLE_BLOCK) to the card. */
38907cbc27cSLeonardo Fogel static int
read_single_block(uint32_t addr)39007cbc27cSLeonardo Fogel read_single_block(uint32_t addr)
39107cbc27cSLeonardo Fogel {
39207cbc27cSLeonardo Fogel 	uint32_t cmd;
39307cbc27cSLeonardo Fogel 
39407cbc27cSLeonardo Fogel 	cmd = MMCHS_SD_CMD_INDX_CMD(MMC_READ_BLOCK_SINGLE)
39507cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_DP_DATA
39607cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_CICE_ENABLE
39707cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_CCCE_ENABLE
39807cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_RSP_TYPE_48B
39907cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_DDIR_READ;
40007cbc27cSLeonardo Fogel 	return send_cmd_check_r1(addr, cmd);
40107cbc27cSLeonardo Fogel }
40207cbc27cSLeonardo Fogel 
40307cbc27cSLeonardo Fogel /* Send CMD24 (WRITE_BLOCK) to the card. */
40407cbc27cSLeonardo Fogel static int
write_block(uint32_t addr)40507cbc27cSLeonardo Fogel write_block(uint32_t addr)
40607cbc27cSLeonardo Fogel {
40707cbc27cSLeonardo Fogel 	uint32_t cmd;
40807cbc27cSLeonardo Fogel 
40907cbc27cSLeonardo Fogel 	cmd = MMCHS_SD_CMD_INDX_CMD(MMC_WRITE_BLOCK_SINGLE)
41007cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_DP_DATA
41107cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_CICE_ENABLE
41207cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_CCCE_ENABLE
41307cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_RSP_TYPE_48B
41407cbc27cSLeonardo Fogel 		| MMCHS_SD_CMD_DDIR_WRITE;
41507cbc27cSLeonardo Fogel 	return send_cmd_check_r1(addr, cmd);
41607cbc27cSLeonardo Fogel }
41707cbc27cSLeonardo Fogel 
41807cbc27cSLeonardo Fogel /*
41907cbc27cSLeonardo Fogel  * Repeat CMD1 until the card is ready, or the time runs out.
42007cbc27cSLeonardo Fogel  * Return 0 on ready, a negative integer on error.
42107cbc27cSLeonardo Fogel  */
42207cbc27cSLeonardo Fogel static int
repeat_send_op_cond(void)42307cbc27cSLeonardo Fogel repeat_send_op_cond(void)
42407cbc27cSLeonardo Fogel {
42507cbc27cSLeonardo Fogel 	spin_t s;
42607cbc27cSLeonardo Fogel 	int spin;
42707cbc27cSLeonardo Fogel 	uint32_t card_ocr;
42807cbc27cSLeonardo Fogel 
42907cbc27cSLeonardo Fogel 	spin_init(&s, CARD_INI_TIMEOUT);
43007cbc27cSLeonardo Fogel 	do {
43107cbc27cSLeonardo Fogel 		spin = spin_check(&s);
43207cbc27cSLeonardo Fogel 		if (send_op_cond() < 0)
43307cbc27cSLeonardo Fogel 			return -1;
43407cbc27cSLeonardo Fogel 		card_ocr = read32(reg->RSP10);
43507cbc27cSLeonardo Fogel 	} while (((card_ocr & MMC_OCR_MEM_READY) == 0) && (spin == TRUE));
43607cbc27cSLeonardo Fogel 
43707cbc27cSLeonardo Fogel 	if ((card_ocr & MMC_OCR_MEM_READY) == 0)
43807cbc27cSLeonardo Fogel 		return -1; /* Card is still busy. */
43907cbc27cSLeonardo Fogel 
44007cbc27cSLeonardo Fogel 	return 0;
44107cbc27cSLeonardo Fogel }
44207cbc27cSLeonardo Fogel 
44307cbc27cSLeonardo Fogel /*
44407cbc27cSLeonardo Fogel  * Read (receive) the busy signal from the card.
44507cbc27cSLeonardo Fogel  * Return 0 on success, a negative integer on error.
44607cbc27cSLeonardo Fogel  */
44707cbc27cSLeonardo Fogel static int
read_busy(void)44807cbc27cSLeonardo Fogel read_busy(void)
44907cbc27cSLeonardo Fogel {
45007cbc27cSLeonardo Fogel 	uint32_t stat;
45107cbc27cSLeonardo Fogel 	/*
45207cbc27cSLeonardo Fogel 	 * The busy signal is optional, but the host controller will assert
45307cbc27cSLeonardo Fogel 	 * SD_STAT[1] TC even if the card does not send it.
45407cbc27cSLeonardo Fogel 	 */
45507cbc27cSLeonardo Fogel 	if (irq_wait() < 0)
45607cbc27cSLeonardo Fogel 		return -1;
45707cbc27cSLeonardo Fogel 	stat = read32(reg->SD_STAT);
45807cbc27cSLeonardo Fogel 	write32(reg->SD_STAT, MMCHS_SD_STAT_DCRC
45907cbc27cSLeonardo Fogel 		| MMCHS_SD_STAT_DTO
46007cbc27cSLeonardo Fogel 		| MMCHS_SD_STAT_TC);
46107cbc27cSLeonardo Fogel 	if (stat & MMCHS_SD_STAT_ERRI) {
46207cbc27cSLeonardo Fogel 		reset_mmchs_fsm(MMCHS_SD_SYSCTL_SRD);
46307cbc27cSLeonardo Fogel 		return -1;
46407cbc27cSLeonardo Fogel 	}
46507cbc27cSLeonardo Fogel 
46607cbc27cSLeonardo Fogel 	return 0;
46707cbc27cSLeonardo Fogel }
46807cbc27cSLeonardo Fogel 
46907cbc27cSLeonardo Fogel /*
47007cbc27cSLeonardo Fogel  * Read (receive) data from the card.
47107cbc27cSLeonardo Fogel  * Return 0 on success, a negative integer on error.
47207cbc27cSLeonardo Fogel  */
47307cbc27cSLeonardo Fogel static int
read_data(uint32_t * data)47407cbc27cSLeonardo Fogel read_data(uint32_t *data)
47507cbc27cSLeonardo Fogel {
47607cbc27cSLeonardo Fogel 	uint32_t stat, i;
47707cbc27cSLeonardo Fogel 
47807cbc27cSLeonardo Fogel 	/* Wait for BRR interrupt. */
47907cbc27cSLeonardo Fogel 	if (irq_wait() < 0)
48007cbc27cSLeonardo Fogel 		return -1;
48107cbc27cSLeonardo Fogel 	if (read32(reg->SD_STAT) & MMCHS_SD_STAT_BRR) {
48207cbc27cSLeonardo Fogel 		write32(reg->SD_STAT, MMCHS_SD_STAT_BRR);
48307cbc27cSLeonardo Fogel 		for (i=SD_DATA_WLEN; i>0; i--)
48407cbc27cSLeonardo Fogel 			*data++ = read32(reg->DATA);
48507cbc27cSLeonardo Fogel 	}
48607cbc27cSLeonardo Fogel 
48707cbc27cSLeonardo Fogel 	/* Wait for TC or ERRI interrupt. */
48807cbc27cSLeonardo Fogel 	if (irq_wait() < 0)
48907cbc27cSLeonardo Fogel 		return -1;
49007cbc27cSLeonardo Fogel 	stat = read32(reg->SD_STAT);
49107cbc27cSLeonardo Fogel 	write32(reg->SD_STAT, MMCHS_SD_STAT_DEB
49207cbc27cSLeonardo Fogel 		| MMCHS_SD_STAT_DCRC
49307cbc27cSLeonardo Fogel 		| MMCHS_SD_STAT_DTO
49407cbc27cSLeonardo Fogel 		| MMCHS_SD_STAT_TC);
49507cbc27cSLeonardo Fogel 	if (stat & MMCHS_SD_STAT_ERRI) {
49607cbc27cSLeonardo Fogel 		reset_mmchs_fsm(MMCHS_SD_SYSCTL_SRD);
49707cbc27cSLeonardo Fogel 		return -1;
49807cbc27cSLeonardo Fogel 	}
49907cbc27cSLeonardo Fogel 
50007cbc27cSLeonardo Fogel 	return 0;
50107cbc27cSLeonardo Fogel }
50207cbc27cSLeonardo Fogel 
50307cbc27cSLeonardo Fogel /*
50407cbc27cSLeonardo Fogel  * Write (send) data to the card.
50507cbc27cSLeonardo Fogel  * Return 0 on success, a negative integer on error.
50607cbc27cSLeonardo Fogel  */
50707cbc27cSLeonardo Fogel static int
write_data(uint32_t * data)50807cbc27cSLeonardo Fogel write_data(uint32_t *data)
50907cbc27cSLeonardo Fogel {
51007cbc27cSLeonardo Fogel 	uint32_t stat, i;
51107cbc27cSLeonardo Fogel 
51207cbc27cSLeonardo Fogel 	/* Wait for BWR interrupt. */
51307cbc27cSLeonardo Fogel 	if (irq_wait() < 0)
51407cbc27cSLeonardo Fogel 		return -1;
51507cbc27cSLeonardo Fogel 	if (read32(reg->SD_STAT) & MMCHS_SD_STAT_BWR) {
51607cbc27cSLeonardo Fogel 		write32(reg->SD_STAT, MMCHS_SD_STAT_BWR);
51707cbc27cSLeonardo Fogel 		for (i=SD_DATA_WLEN; i>0; i--)
51807cbc27cSLeonardo Fogel 			write32(reg->DATA, *data++);
51907cbc27cSLeonardo Fogel 	}
52007cbc27cSLeonardo Fogel 
52107cbc27cSLeonardo Fogel 	/* Wait for TC or ERRI interrupt. */
52207cbc27cSLeonardo Fogel 	if (irq_wait() < 0)
52307cbc27cSLeonardo Fogel 		return -1;
52407cbc27cSLeonardo Fogel 	stat = read32(reg->SD_STAT);
52507cbc27cSLeonardo Fogel 	write32(reg->SD_STAT, MMCHS_SD_STAT_DEB
52607cbc27cSLeonardo Fogel 		| MMCHS_SD_STAT_DCRC
52707cbc27cSLeonardo Fogel 		| MMCHS_SD_STAT_DTO
52807cbc27cSLeonardo Fogel 		| MMCHS_SD_STAT_TC);
52907cbc27cSLeonardo Fogel 	if (stat & MMCHS_SD_STAT_ERRI) {
53007cbc27cSLeonardo Fogel 		reset_mmchs_fsm(MMCHS_SD_SYSCTL_SRD);
53107cbc27cSLeonardo Fogel 		return -1;
53207cbc27cSLeonardo Fogel 	}
53307cbc27cSLeonardo Fogel 
53407cbc27cSLeonardo Fogel 	return 0;
53507cbc27cSLeonardo Fogel }
53607cbc27cSLeonardo Fogel 
53707cbc27cSLeonardo Fogel /*
53807cbc27cSLeonardo Fogel  * Read a block from the card.
53907cbc27cSLeonardo Fogel  * Return 0 on success, a negative integer on error.
54007cbc27cSLeonardo Fogel  */
54107cbc27cSLeonardo Fogel static int
cim_read_block(uint32_t addr,uint32_t * data)54207cbc27cSLeonardo Fogel cim_read_block(uint32_t addr, uint32_t *data)
54307cbc27cSLeonardo Fogel {
54407cbc27cSLeonardo Fogel 	/* Send CMD17. */
54507cbc27cSLeonardo Fogel 	if (read_single_block(addr) < 0)
54607cbc27cSLeonardo Fogel 		return -1;
54707cbc27cSLeonardo Fogel 	/* Read from the host buffer. */
54807cbc27cSLeonardo Fogel 	return read_data(data);
54907cbc27cSLeonardo Fogel }
55007cbc27cSLeonardo Fogel 
55107cbc27cSLeonardo Fogel /*
55207cbc27cSLeonardo Fogel  * Write a block to the card.
55307cbc27cSLeonardo Fogel  * Return 0 on success, a negative integer on error.
55407cbc27cSLeonardo Fogel  */
55507cbc27cSLeonardo Fogel static int
cim_write_block(uint32_t addr,uint32_t * data)55607cbc27cSLeonardo Fogel cim_write_block(uint32_t addr, uint32_t *data)
55707cbc27cSLeonardo Fogel {
55807cbc27cSLeonardo Fogel 	/* Send CMD24. */
55907cbc27cSLeonardo Fogel 	if (write_block(addr) < 0)
56007cbc27cSLeonardo Fogel 		return -1;
56107cbc27cSLeonardo Fogel 	/* Write into the host buffer. */
56207cbc27cSLeonardo Fogel 	if (write_data(data) < 0)
56307cbc27cSLeonardo Fogel 		return -1;
56407cbc27cSLeonardo Fogel 	/* CMD13. Check the result of the write operation. */
56507cbc27cSLeonardo Fogel 	return send_status();
56607cbc27cSLeonardo Fogel }
56707cbc27cSLeonardo Fogel 
56807cbc27cSLeonardo Fogel 
56907cbc27cSLeonardo Fogel /*
57007cbc27cSLeonardo Fogel  * Interface to the MINIX block device driver.
57107cbc27cSLeonardo Fogel  */
57207cbc27cSLeonardo Fogel static int
emmc_host_set_instance(struct mmc_host * host,int instance)57307cbc27cSLeonardo Fogel emmc_host_set_instance(struct mmc_host *host, int instance)
57407cbc27cSLeonardo Fogel {
57507cbc27cSLeonardo Fogel 	if (instance != 0)
57607cbc27cSLeonardo Fogel 		return EIO;
57707cbc27cSLeonardo Fogel 	return 0;
57807cbc27cSLeonardo Fogel }
57907cbc27cSLeonardo Fogel 
58007cbc27cSLeonardo Fogel /*
58107cbc27cSLeonardo Fogel  * Initialize the driver and kernel structures.
58207cbc27cSLeonardo Fogel  * Return 0 on success, a negative integer on error.
58307cbc27cSLeonardo Fogel  */
58407cbc27cSLeonardo Fogel static int
minix_init(void)58507cbc27cSLeonardo Fogel minix_init(void)
58607cbc27cSLeonardo Fogel {
58707cbc27cSLeonardo Fogel 	struct minix_mem_range mr;
58807cbc27cSLeonardo Fogel 	uint32_t v_base;
58907cbc27cSLeonardo Fogel 
59007cbc27cSLeonardo Fogel 	/*
59107cbc27cSLeonardo Fogel 	 * On the BeagleBone Black, the eMMC device is connected to MMC1.
59207cbc27cSLeonardo Fogel 	 * Add the MMC1 memory address range to the process' resources.
59307cbc27cSLeonardo Fogel 	 */
59407cbc27cSLeonardo Fogel 	mr.mr_base  = AM335X_MMC1_BASE_ADDR;
59507cbc27cSLeonardo Fogel 	mr.mr_limit = AM335X_MMC1_BASE_ADDR + AM335X_MMC1_SIZE - 1;
59607cbc27cSLeonardo Fogel 	if (sys_privctl(SELF, SYS_PRIV_ADD_MEM, &mr) != OK)
59707cbc27cSLeonardo Fogel 		return -1;
59807cbc27cSLeonardo Fogel 
59907cbc27cSLeonardo Fogel 	/* Map the MMC1 physical base address to a virtual address. */
60007cbc27cSLeonardo Fogel 	v_base = (uint32_t)vm_map_phys(SELF, (void *)mr.mr_base,
60107cbc27cSLeonardo Fogel 		AM335X_MMC1_SIZE);
60207cbc27cSLeonardo Fogel 	if (v_base == (uint32_t)MAP_FAILED)
60307cbc27cSLeonardo Fogel 		return -1;
60407cbc27cSLeonardo Fogel 
60507cbc27cSLeonardo Fogel 	/* Set the registers virtual addresses. */
60607cbc27cSLeonardo Fogel 	reg = &regs_v1;
60707cbc27cSLeonardo Fogel 	reg->SYSCONFIG += v_base;
60807cbc27cSLeonardo Fogel 	reg->SYSSTATUS += v_base;
60907cbc27cSLeonardo Fogel 	reg->CON       += v_base;
61007cbc27cSLeonardo Fogel 	reg->BLK       += v_base;
61107cbc27cSLeonardo Fogel 	reg->ARG       += v_base;
61207cbc27cSLeonardo Fogel 	reg->CMD       += v_base;
61307cbc27cSLeonardo Fogel 	reg->RSP10     += v_base;
61407cbc27cSLeonardo Fogel 	reg->RSP32     += v_base;
61507cbc27cSLeonardo Fogel 	reg->RSP54     += v_base;
61607cbc27cSLeonardo Fogel 	reg->RSP76     += v_base;
61707cbc27cSLeonardo Fogel 	reg->DATA      += v_base;
61807cbc27cSLeonardo Fogel 	reg->PSTATE    += v_base;
61907cbc27cSLeonardo Fogel 	reg->HCTL      += v_base;
62007cbc27cSLeonardo Fogel 	reg->SYSCTL    += v_base;
62107cbc27cSLeonardo Fogel 	reg->SD_STAT   += v_base;
62207cbc27cSLeonardo Fogel 	reg->IE        += v_base;
62307cbc27cSLeonardo Fogel 	reg->ISE       += v_base;
62407cbc27cSLeonardo Fogel 
62507cbc27cSLeonardo Fogel 	/* Register the MMC1 interrupt number. */
62607cbc27cSLeonardo Fogel 	if (sys_irqsetpolicy(AM335X_MMCSD1INT, 0, &hook_id) != OK)
62707cbc27cSLeonardo Fogel 		return -1;
62807cbc27cSLeonardo Fogel 
62907cbc27cSLeonardo Fogel 	return 0;
63007cbc27cSLeonardo Fogel }
63107cbc27cSLeonardo Fogel 
63207cbc27cSLeonardo Fogel /*
633*47962876SLeonardo Fogel  * Configure the Control Module registers CONF_GPMC_AD4-7.
634*47962876SLeonardo Fogel  * Multiplex pins GPMC_AD4-7 to signals MMC1_DAT4-7 (Mode 1).
635*47962876SLeonardo Fogel  * Return 0 on success, a negative integer on error.
636*47962876SLeonardo Fogel  */
637*47962876SLeonardo Fogel static int
conf_gpmc_ad(void)638*47962876SLeonardo Fogel conf_gpmc_ad(void)
639*47962876SLeonardo Fogel {
640*47962876SLeonardo Fogel 	uint32_t i;
641*47962876SLeonardo Fogel 
642*47962876SLeonardo Fogel 	for (i=4; i<8; i++) {
643*47962876SLeonardo Fogel 		if (sys_padconf(CONF_GPMC_AD(i), CONF_GPMC_AD_MASK,
644*47962876SLeonardo Fogel 			CONF_GPMC_AD_VAL) != OK)
645*47962876SLeonardo Fogel 			return -1;
646*47962876SLeonardo Fogel 	}
647*47962876SLeonardo Fogel 	return 0;
648*47962876SLeonardo Fogel }
649*47962876SLeonardo Fogel 
650*47962876SLeonardo Fogel /*
65107cbc27cSLeonardo Fogel  * Interface to the MINIX block device driver.
65207cbc27cSLeonardo Fogel  * Host controller initialization.
65307cbc27cSLeonardo Fogel  * Return 0 on success, a negative integer on error.
65407cbc27cSLeonardo Fogel  */
65507cbc27cSLeonardo Fogel static int
emmc_host_init(struct mmc_host * host)65607cbc27cSLeonardo Fogel emmc_host_init(struct mmc_host *host)
65707cbc27cSLeonardo Fogel {
65807cbc27cSLeonardo Fogel 	struct machine machine;
65907cbc27cSLeonardo Fogel 
66007cbc27cSLeonardo Fogel 	/* The eMMC is present on the BBB only. */
66107cbc27cSLeonardo Fogel 	sys_getmachine(&machine);
66207cbc27cSLeonardo Fogel 	if (!BOARD_IS_BBB(machine.board_id))
66307cbc27cSLeonardo Fogel 		return -1;
66407cbc27cSLeonardo Fogel 
66507cbc27cSLeonardo Fogel 	/* Initialize the driver and kernel structures. */
66607cbc27cSLeonardo Fogel 	if (minix_init() < 0)
66707cbc27cSLeonardo Fogel 		return -1;
66807cbc27cSLeonardo Fogel 
669*47962876SLeonardo Fogel 	/*
670*47962876SLeonardo Fogel 	 * Multiplex pins GPMC_AD4-7 to signals MMC1_DAT4-7 (Mode 1), in order
671*47962876SLeonardo Fogel 	 * to allow the use of 8-bit mode.
672*47962876SLeonardo Fogel 	 * U-Boot multiplexes only pins GPMC_AD0-3 to signals MMC1_DAT0-3.
673*47962876SLeonardo Fogel 	 */
674*47962876SLeonardo Fogel 	if (conf_gpmc_ad() < 0)
675*47962876SLeonardo Fogel 		bus_width = EXT_CSD_BUS_WIDTH_4;
676*47962876SLeonardo Fogel 	else
677*47962876SLeonardo Fogel 		bus_width = EXT_CSD_BUS_WIDTH_8;
678*47962876SLeonardo Fogel 
67907cbc27cSLeonardo Fogel 	/* Reset the host controller. */
68007cbc27cSLeonardo Fogel 	set32(reg->SYSCONFIG, MMCHS_SD_SYSCONFIG_SOFTRESET,
68107cbc27cSLeonardo Fogel 		MMCHS_SD_SYSCONFIG_SOFTRESET);
68207cbc27cSLeonardo Fogel 	if (spin_until_set(reg->SYSSTATUS, MMCHS_SD_SYSSTATUS_RESETDONE)
68307cbc27cSLeonardo Fogel 		!= MMCHS_SD_SYSSTATUS_RESETDONE)
68407cbc27cSLeonardo Fogel 		return -1;
68507cbc27cSLeonardo Fogel 
68607cbc27cSLeonardo Fogel 	/*
68707cbc27cSLeonardo Fogel 	 * SD_CAPA: "The host driver shall not modify this register after the
68807cbc27cSLeonardo Fogel 	 * initialization." (AM335x TRM)
68907cbc27cSLeonardo Fogel 	 */
69007cbc27cSLeonardo Fogel 
69107cbc27cSLeonardo Fogel 	/*
69207cbc27cSLeonardo Fogel 	 * Set the bus voltage to 3V, and turn the bus power on.
69307cbc27cSLeonardo Fogel 	 * On the BeagleBone Black, the bus voltage is pulled up to 3.3V, but
69407cbc27cSLeonardo Fogel 	 * the MMCHS supports only 1.8V or 3V.
69507cbc27cSLeonardo Fogel 	 */
69607cbc27cSLeonardo Fogel 	set32(reg->HCTL, MMCHS_SD_HCTL_SDVS, MMCHS_SD_HCTL_SDVS_VS30);
69707cbc27cSLeonardo Fogel 	set32(reg->HCTL, MMCHS_SD_HCTL_SDBP, MMCHS_SD_HCTL_SDBP_ON);
69807cbc27cSLeonardo Fogel 	if (spin_until_set(reg->HCTL, MMCHS_SD_HCTL_SDBP)
69907cbc27cSLeonardo Fogel 		== MMCHS_SD_HCTL_SDBP_OFF)
70007cbc27cSLeonardo Fogel 		return -1;
70107cbc27cSLeonardo Fogel 
70207cbc27cSLeonardo Fogel 	/* Set the bus clock frequency to FOD (400kHz). */
70307cbc27cSLeonardo Fogel 	set32(reg->SYSCTL, MMCHS_SD_SYSCTL_CLKD,
70407cbc27cSLeonardo Fogel 		MMCHS_SD_SYSCTL_CLKD_400KHZ << 6);
70507cbc27cSLeonardo Fogel 
70607cbc27cSLeonardo Fogel 	/* Set data and busy time-out: ~2,6s @ 400kHz.*/
70707cbc27cSLeonardo Fogel 	set32(reg->SYSCTL, MMCHS_SD_SYSCTL_DTO, MMCHS_SD_SYSCTL_DTO_2POW20);
70807cbc27cSLeonardo Fogel 
70907cbc27cSLeonardo Fogel 	/* Enable the internal clock. */
71007cbc27cSLeonardo Fogel 	set32(reg->SYSCTL, MMCHS_SD_SYSCTL_ICE, MMCHS_SD_SYSCTL_ICE_EN);
71107cbc27cSLeonardo Fogel 	if (spin_until_set(reg->SYSCTL, MMCHS_SD_SYSCTL_ICS)
71207cbc27cSLeonardo Fogel 		== MMCHS_SD_SYSCTL_ICS_UNSTABLE)
71307cbc27cSLeonardo Fogel 		return -1;
71407cbc27cSLeonardo Fogel 
71507cbc27cSLeonardo Fogel 	/* Enable the bus clock. */
71607cbc27cSLeonardo Fogel 	set32(reg->SYSCTL, MMCHS_SD_SYSCTL_CEN, MMCHS_SD_SYSCTL_CEN_EN);
71707cbc27cSLeonardo Fogel 
71807cbc27cSLeonardo Fogel 	/*
71907cbc27cSLeonardo Fogel 	 * Set the internal clock gating strategy to automatic, and enable
72007cbc27cSLeonardo Fogel 	 * Smart Idle mode. The host controller does not implement wake-up
72107cbc27cSLeonardo Fogel 	 * request (SWAKEUP pin is not connected).
72207cbc27cSLeonardo Fogel 	 */
72307cbc27cSLeonardo Fogel 	set32(reg->SYSCONFIG, MMCHS_SD_SYSCONFIG_AUTOIDLE,
72407cbc27cSLeonardo Fogel 		MMCHS_SD_SYSCONFIG_AUTOIDLE_EN);
72507cbc27cSLeonardo Fogel 	set32(reg->SYSCONFIG, MMCHS_SD_SYSCONFIG_SIDLEMODE,
72607cbc27cSLeonardo Fogel 		MMCHS_SD_SYSCONFIG_SIDLEMODE_IDLE);
72707cbc27cSLeonardo Fogel 
72807cbc27cSLeonardo Fogel 	/* The driver reads and writes single 512B blocks. */
72907cbc27cSLeonardo Fogel 	set32(reg->BLK, MMCHS_SD_BLK_BLEN, SEC_SIZE);
73007cbc27cSLeonardo Fogel 
73107cbc27cSLeonardo Fogel 	/* Enable interrupt status and requests. */
73207cbc27cSLeonardo Fogel 	write32(reg->IE, MMCHS_SD_IE_ERROR_MASK
73307cbc27cSLeonardo Fogel 		| MMCHS_SD_IE_BRR_ENABLE_ENABLE
73407cbc27cSLeonardo Fogel 		| MMCHS_SD_IE_BWR_ENABLE_ENABLE
73507cbc27cSLeonardo Fogel 		| MMCHS_SD_IE_TC_ENABLE_ENABLE
73607cbc27cSLeonardo Fogel 		| MMCHS_SD_IE_CC_ENABLE_ENABLE);
73707cbc27cSLeonardo Fogel 	write32(reg->ISE, MMCHS_SD_IE_ERROR_MASK
73807cbc27cSLeonardo Fogel 		| MMCHS_SD_IE_BRR_ENABLE_ENABLE
73907cbc27cSLeonardo Fogel 		| MMCHS_SD_IE_BWR_ENABLE_ENABLE
74007cbc27cSLeonardo Fogel 		| MMCHS_SD_IE_TC_ENABLE_ENABLE
74107cbc27cSLeonardo Fogel 		| MMCHS_SD_IE_CC_ENABLE_ENABLE);
74207cbc27cSLeonardo Fogel 
74307cbc27cSLeonardo Fogel 	return 0;
74407cbc27cSLeonardo Fogel }
74507cbc27cSLeonardo Fogel 
74607cbc27cSLeonardo Fogel /*
74707cbc27cSLeonardo Fogel  * Interface to the MINIX block device driver.
74807cbc27cSLeonardo Fogel  * Set the log level.
74907cbc27cSLeonardo Fogel  */
75007cbc27cSLeonardo Fogel static void
emmc_set_log_level(int level)75107cbc27cSLeonardo Fogel emmc_set_log_level(int level)
75207cbc27cSLeonardo Fogel {
75307cbc27cSLeonardo Fogel 	log.log_level = level;
75407cbc27cSLeonardo Fogel }
75507cbc27cSLeonardo Fogel 
75607cbc27cSLeonardo Fogel 
75707cbc27cSLeonardo Fogel /*
75807cbc27cSLeonardo Fogel  * Interface to the MINIX block device driver.
75907cbc27cSLeonardo Fogel  * Unused, but declared in mmchost.h.
76007cbc27cSLeonardo Fogel  */
76107cbc27cSLeonardo Fogel #if 0
76207cbc27cSLeonardo Fogel static int
76307cbc27cSLeonardo Fogel emmc_host_reset(struct mmc_host *host)
76407cbc27cSLeonardo Fogel {
76507cbc27cSLeonardo Fogel 	return 0;
76607cbc27cSLeonardo Fogel }
76707cbc27cSLeonardo Fogel #endif
76807cbc27cSLeonardo Fogel 
76907cbc27cSLeonardo Fogel /*
77007cbc27cSLeonardo Fogel  * Interface to the MINIX block device driver.
77107cbc27cSLeonardo Fogel  * Card detection.
77207cbc27cSLeonardo Fogel  */
77307cbc27cSLeonardo Fogel static int
emmc_card_detect(struct sd_slot * slot)77407cbc27cSLeonardo Fogel emmc_card_detect(struct sd_slot *slot)
77507cbc27cSLeonardo Fogel {
77607cbc27cSLeonardo Fogel 	/* The card is detected during card initialization. */
77707cbc27cSLeonardo Fogel 	return 1;
77807cbc27cSLeonardo Fogel }
77907cbc27cSLeonardo Fogel 
78007cbc27cSLeonardo Fogel /*
78107cbc27cSLeonardo Fogel  * Interface to the MINIX block device driver.
78207cbc27cSLeonardo Fogel  * Card initialization. Also, finish the MMCHS initialization.
78307cbc27cSLeonardo Fogel  * Return NULL on error.
78407cbc27cSLeonardo Fogel  */
78507cbc27cSLeonardo Fogel static struct sd_card *
emmc_card_initialize(struct sd_slot * slot)78607cbc27cSLeonardo Fogel emmc_card_initialize(struct sd_slot *slot)
78707cbc27cSLeonardo Fogel {
78807cbc27cSLeonardo Fogel 	uint32_t clkd;
78907cbc27cSLeonardo Fogel 
79007cbc27cSLeonardo Fogel 	/* CMD0 */
79107cbc27cSLeonardo Fogel 	if (go_idle_state() < 0)
79207cbc27cSLeonardo Fogel 		return NULL;
79307cbc27cSLeonardo Fogel 
79407cbc27cSLeonardo Fogel 	/*
79507cbc27cSLeonardo Fogel 	 * Set the MMC_CMD line to open drain.
79607cbc27cSLeonardo Fogel 	 * "The host starts the card identification process in open-drain mode
79707cbc27cSLeonardo Fogel 	 * with the identification clock rate FOD." (MMCA, 4.41)
79807cbc27cSLeonardo Fogel 	 */
79907cbc27cSLeonardo Fogel 	set32(reg->CON, MMCHS_SD_CON_OD, MMCHS_SD_CON_OD_OD);
80007cbc27cSLeonardo Fogel 
80107cbc27cSLeonardo Fogel 	/* CMD1 */
80207cbc27cSLeonardo Fogel 	if (repeat_send_op_cond() < 0)
80307cbc27cSLeonardo Fogel 		return NULL;
80407cbc27cSLeonardo Fogel 
80507cbc27cSLeonardo Fogel 	/* CMD2. The driver has no use for the CID. */
80607cbc27cSLeonardo Fogel 	if (all_send_cid() < 0)
80707cbc27cSLeonardo Fogel 		return NULL;
80807cbc27cSLeonardo Fogel 
80907cbc27cSLeonardo Fogel 	/* CMD3 */
81007cbc27cSLeonardo Fogel 	if (set_relative_addr() < 0)
81107cbc27cSLeonardo Fogel 		return NULL;
81207cbc27cSLeonardo Fogel 
81307cbc27cSLeonardo Fogel 	/*
81407cbc27cSLeonardo Fogel 	 * Set the MMC_CMD line to push-pull.
81507cbc27cSLeonardo Fogel 	 * "When the card is in Stand-by State, communication over the CMD and
81607cbc27cSLeonardo Fogel 	 * DAT lines will be performed in push-pull mode." (MMCA, 4.41)
81707cbc27cSLeonardo Fogel 	 */
81807cbc27cSLeonardo Fogel 	set32(reg->CON, MMCHS_SD_CON_OD, MMCHS_SD_CON_OD_PP);
81907cbc27cSLeonardo Fogel 
82007cbc27cSLeonardo Fogel 	/* CMD9 */
82107cbc27cSLeonardo Fogel 	if (send_csd() < 0)
82207cbc27cSLeonardo Fogel 		return NULL;
82307cbc27cSLeonardo Fogel 	card_csd[0] = read32(reg->RSP10);
82407cbc27cSLeonardo Fogel 	card_csd[1] = read32(reg->RSP32);
82507cbc27cSLeonardo Fogel 	card_csd[2] = read32(reg->RSP54);
82607cbc27cSLeonardo Fogel 	card_csd[3] = read32(reg->RSP76);
82707cbc27cSLeonardo Fogel 
82807cbc27cSLeonardo Fogel 	/* Card capacity for cards up to 2GB of density. */
82907cbc27cSLeonardo Fogel 	card_size = (uint64_t)MMC_CSD_CAPACITY(card_csd)
83007cbc27cSLeonardo Fogel 		<< MMC_CSD_READ_BL_LEN(card_csd);
83107cbc27cSLeonardo Fogel 
83207cbc27cSLeonardo Fogel 	card_write_protect = (SD_CSD_PERM_WRITE_PROTECT(card_csd)
83307cbc27cSLeonardo Fogel 		| SD_CSD_TMP_WRITE_PROTECT(card_csd));
83407cbc27cSLeonardo Fogel 	if (card_write_protect)
83507cbc27cSLeonardo Fogel 		log_info(&log, "the eMMC is write protected\n");
83607cbc27cSLeonardo Fogel 
83707cbc27cSLeonardo Fogel 	/* CMD7 */
83807cbc27cSLeonardo Fogel 	if (select_card() < 0)
83907cbc27cSLeonardo Fogel 		return NULL;
84007cbc27cSLeonardo Fogel 
84107cbc27cSLeonardo Fogel 	/* CMD8 */
84207cbc27cSLeonardo Fogel 	if (send_ext_csd() < 0)
84307cbc27cSLeonardo Fogel 		return NULL;
84407cbc27cSLeonardo Fogel 	/* Receive the Extended CSD register. */
84507cbc27cSLeonardo Fogel 	if (read_data((uint32_t *)card_ext_csd) < 0)
84607cbc27cSLeonardo Fogel 		return NULL;
84707cbc27cSLeonardo Fogel 
84807cbc27cSLeonardo Fogel 	/* Card capacity for densities greater than 2GB. */
84907cbc27cSLeonardo Fogel 	if (MMC_EXT_CSD_SEC_COUNT > 0)
85007cbc27cSLeonardo Fogel 		card_size = (uint64_t)MMC_EXT_CSD_SEC_COUNT * SEC_SIZE;
85107cbc27cSLeonardo Fogel 
85207cbc27cSLeonardo Fogel 	/* CMD6. Switch to high-speed mode: EXT_CSD[185] HS_TIMING = 1. */
85307cbc27cSLeonardo Fogel 	if (mmc_switch(MMC_SWITCH_MODE_WRITE_BYTE, EXT_CSD_HS_TIMING, 1) < 0)
85407cbc27cSLeonardo Fogel 		return NULL;
85507cbc27cSLeonardo Fogel 	/* Wait for the (optional) busy signal. */
85607cbc27cSLeonardo Fogel 	if (read_busy() < 0)
85707cbc27cSLeonardo Fogel 		return NULL;
85807cbc27cSLeonardo Fogel 	/* CMD13. Check the result of the SWITCH operation. */
85907cbc27cSLeonardo Fogel 	if (send_status() < 0)
86007cbc27cSLeonardo Fogel 		return NULL;
86107cbc27cSLeonardo Fogel 
86207cbc27cSLeonardo Fogel 	/* Change the bus clock frequency. */
86307cbc27cSLeonardo Fogel 	if (MMC_EXT_CSD_CARD_TYPE & MMC_EXT_CSD_CARD_TYPE_HS_MMC_52MHZ)
86407cbc27cSLeonardo Fogel 		clkd = MMCHS_SD_SYSCTL_CLKD_52MHZ; /* 48 MHz */
86507cbc27cSLeonardo Fogel 	else
86607cbc27cSLeonardo Fogel 		clkd = MMCHS_SD_SYSCTL_CLKD_26MHZ; /* 24 MHz */
86707cbc27cSLeonardo Fogel 	if (set_bus_clkd(clkd) < 0)
86807cbc27cSLeonardo Fogel 		return NULL;
86907cbc27cSLeonardo Fogel 
87007cbc27cSLeonardo Fogel 	/* Set data and busy time-out: ~ 2,8s @ 48MHz.*/
87107cbc27cSLeonardo Fogel 	set32(reg->SYSCTL, MMCHS_SD_SYSCTL_DTO, MMCHS_SD_SYSCTL_DTO_2POW27);
87207cbc27cSLeonardo Fogel 
873*47962876SLeonardo Fogel 	/* CMD6. Set data bus width. */
87407cbc27cSLeonardo Fogel 	if (mmc_switch(MMC_SWITCH_MODE_WRITE_BYTE, EXT_CSD_BUS_WIDTH,
875*47962876SLeonardo Fogel 		bus_width) < 0)
87607cbc27cSLeonardo Fogel 		return NULL;
87707cbc27cSLeonardo Fogel 	/* Wait for the (optional) busy signal. */
87807cbc27cSLeonardo Fogel 	if (read_busy() < 0)
87907cbc27cSLeonardo Fogel 		return NULL;
88007cbc27cSLeonardo Fogel 	/* CMD13. Check the result of the SWITCH operation. */
88107cbc27cSLeonardo Fogel 	if (send_status() < 0)
88207cbc27cSLeonardo Fogel 		return NULL;
88307cbc27cSLeonardo Fogel 
884*47962876SLeonardo Fogel 	/* Host controller: set data bus width. */
885*47962876SLeonardo Fogel 	if (bus_width == EXT_CSD_BUS_WIDTH_4)
88607cbc27cSLeonardo Fogel 		set32(reg->HCTL, MMCHS_SD_HCTL_DTW, MMCHS_SD_HCTL_DTW_4BIT);
887*47962876SLeonardo Fogel 	else
888*47962876SLeonardo Fogel 		set32(reg->CON, MMCHS_SD_CON_DW8, MMCHS_SD_CON_DW8_8BITS);
88907cbc27cSLeonardo Fogel 
89007cbc27cSLeonardo Fogel 	/* CMD16. Set block length to sector size (512B). */
89107cbc27cSLeonardo Fogel 	if (set_blocklen() < 0)
89207cbc27cSLeonardo Fogel 		return NULL;
89307cbc27cSLeonardo Fogel 
89407cbc27cSLeonardo Fogel 	/* Initialize the block device driver structures. */
89507cbc27cSLeonardo Fogel 	slot->card.blk_size  = SEC_SIZE;
89607cbc27cSLeonardo Fogel 	slot->card.blk_count = card_size / SEC_SIZE;
89707cbc27cSLeonardo Fogel 	slot->card.state     = SD_MODE_DATA_TRANSFER_MODE;
89807cbc27cSLeonardo Fogel 	slot->card.open_ct   = 0;
89907cbc27cSLeonardo Fogel 	memset(slot->card.part,    0, sizeof(slot->card.part));
90007cbc27cSLeonardo Fogel 	memset(slot->card.subpart, 0, sizeof(slot->card.subpart));
90107cbc27cSLeonardo Fogel 	slot->card.part[0].dv_size = card_size;
90207cbc27cSLeonardo Fogel 
90307cbc27cSLeonardo Fogel 	return &(slot->card);
90407cbc27cSLeonardo Fogel }
90507cbc27cSLeonardo Fogel 
90607cbc27cSLeonardo Fogel /*
90707cbc27cSLeonardo Fogel  * Interface to the MINIX block device driver.
90807cbc27cSLeonardo Fogel  * Card release.
90907cbc27cSLeonardo Fogel  */
91007cbc27cSLeonardo Fogel static int
emmc_card_release(struct sd_card * card)91107cbc27cSLeonardo Fogel emmc_card_release(struct sd_card *card)
91207cbc27cSLeonardo Fogel {
91307cbc27cSLeonardo Fogel 	/* Decrements the "in-use count." */
91407cbc27cSLeonardo Fogel 	card->open_ct--;
91507cbc27cSLeonardo Fogel 
91607cbc27cSLeonardo Fogel 	/*
91707cbc27cSLeonardo Fogel 	 * The block special file is closed, but the driver does not need to
91807cbc27cSLeonardo Fogel 	 * "release" the eMMC, even if the driver is unloaded.
91907cbc27cSLeonardo Fogel 	 */
92007cbc27cSLeonardo Fogel 
92107cbc27cSLeonardo Fogel 	return 0;
92207cbc27cSLeonardo Fogel }
92307cbc27cSLeonardo Fogel 
92407cbc27cSLeonardo Fogel /*
92507cbc27cSLeonardo Fogel  * Interface to the MINIX block device driver.
92607cbc27cSLeonardo Fogel  * Handle unexpected interrupts.
92707cbc27cSLeonardo Fogel  */
92807cbc27cSLeonardo Fogel static void
emmc_hw_intr(unsigned int irqs)92907cbc27cSLeonardo Fogel emmc_hw_intr(unsigned int irqs)
93007cbc27cSLeonardo Fogel {
93107cbc27cSLeonardo Fogel 	log_warn(&log, "register SD_STAT == 0x%08x\n", reg->SD_STAT);
93207cbc27cSLeonardo Fogel }
93307cbc27cSLeonardo Fogel 
93407cbc27cSLeonardo Fogel /*
93507cbc27cSLeonardo Fogel  * Interface to the MINIX block device driver.
93607cbc27cSLeonardo Fogel  * Read/write blocks.
93707cbc27cSLeonardo Fogel  * Return the number of blocks read/written, or a negative integer on error.
93807cbc27cSLeonardo Fogel  */
93907cbc27cSLeonardo Fogel static int
emmc_read_write(int (* cim_read_write)(uint32_t,uint32_t *),uint32_t blknr,uint32_t count,unsigned char * buf)94007cbc27cSLeonardo Fogel emmc_read_write(int (*cim_read_write)(uint32_t, uint32_t *),
94107cbc27cSLeonardo Fogel 	uint32_t blknr, uint32_t count, unsigned char *buf)
94207cbc27cSLeonardo Fogel {
94307cbc27cSLeonardo Fogel 	int blocks, r;
94407cbc27cSLeonardo Fogel 	uint32_t addr;
94507cbc27cSLeonardo Fogel 
94607cbc27cSLeonardo Fogel 	blocks = 0; /* count of blocks read/written. */
94707cbc27cSLeonardo Fogel 	r = 0;
94807cbc27cSLeonardo Fogel 	while ((count > 0) && (r == 0)) {
94907cbc27cSLeonardo Fogel 		/*
95007cbc27cSLeonardo Fogel 		 * Data address for media =< 2GB is byte address, and data
95107cbc27cSLeonardo Fogel 		 * address for media > 2GB is sector address.
95207cbc27cSLeonardo Fogel 		 */
95307cbc27cSLeonardo Fogel 		if (card_size <= (2U << 30))
95407cbc27cSLeonardo Fogel 			addr = blknr * SEC_SIZE;
95507cbc27cSLeonardo Fogel 		else
95607cbc27cSLeonardo Fogel 			addr = blknr;
95707cbc27cSLeonardo Fogel 
95807cbc27cSLeonardo Fogel 		r = (*cim_read_write)(addr, (uint32_t *)buf);
95907cbc27cSLeonardo Fogel 		if (r == 0) {
96007cbc27cSLeonardo Fogel 			blknr++;
96107cbc27cSLeonardo Fogel 			count--;
96207cbc27cSLeonardo Fogel 			buf += SEC_SIZE;
96307cbc27cSLeonardo Fogel 			blocks++;
96407cbc27cSLeonardo Fogel 		}
96507cbc27cSLeonardo Fogel 		else if (blocks == 0)
96607cbc27cSLeonardo Fogel 			blocks = r;
96707cbc27cSLeonardo Fogel 	}
96807cbc27cSLeonardo Fogel 
96907cbc27cSLeonardo Fogel 	return blocks;
97007cbc27cSLeonardo Fogel }
97107cbc27cSLeonardo Fogel 
97207cbc27cSLeonardo Fogel /*
97307cbc27cSLeonardo Fogel  * Interface to the MINIX block device driver.
97407cbc27cSLeonardo Fogel  * Read blocks.
97507cbc27cSLeonardo Fogel  */
97607cbc27cSLeonardo Fogel static int
emmc_read(struct sd_card * card,uint32_t blknr,uint32_t count,unsigned char * buf)97707cbc27cSLeonardo Fogel emmc_read(struct sd_card *card,
97807cbc27cSLeonardo Fogel 	uint32_t blknr, uint32_t count, unsigned char *buf)
97907cbc27cSLeonardo Fogel {
98007cbc27cSLeonardo Fogel 	return emmc_read_write(&cim_read_block, blknr, count, buf);
98107cbc27cSLeonardo Fogel }
98207cbc27cSLeonardo Fogel 
98307cbc27cSLeonardo Fogel /*
98407cbc27cSLeonardo Fogel  * Interface to the MINIX block device driver.
98507cbc27cSLeonardo Fogel  * Write blocks.
98607cbc27cSLeonardo Fogel  */
98707cbc27cSLeonardo Fogel static int
emmc_write(struct sd_card * card,uint32_t blknr,uint32_t count,unsigned char * buf)98807cbc27cSLeonardo Fogel emmc_write(struct sd_card *card,
98907cbc27cSLeonardo Fogel 	uint32_t blknr, uint32_t count, unsigned char *buf)
99007cbc27cSLeonardo Fogel {
99107cbc27cSLeonardo Fogel 	if (card_write_protect)
99207cbc27cSLeonardo Fogel 		return -1; /* The card is write protected. */
99307cbc27cSLeonardo Fogel 	return emmc_read_write(&cim_write_block, blknr, count, buf);
99407cbc27cSLeonardo Fogel }
99507cbc27cSLeonardo Fogel 
99607cbc27cSLeonardo Fogel /*
99707cbc27cSLeonardo Fogel  * Interface to the MINIX block device driver.
99807cbc27cSLeonardo Fogel  * Driver interface registration.
99907cbc27cSLeonardo Fogel  */
100007cbc27cSLeonardo Fogel void
host_initialize_host_structure_mmchs(struct mmc_host * host)100107cbc27cSLeonardo Fogel host_initialize_host_structure_mmchs(struct mmc_host *host)
100207cbc27cSLeonardo Fogel {
100307cbc27cSLeonardo Fogel 	uint32_t i;
100407cbc27cSLeonardo Fogel 
100507cbc27cSLeonardo Fogel 	/* Register the driver interface at the block device driver. */
100607cbc27cSLeonardo Fogel 	host->host_set_instance = &emmc_host_set_instance;
100707cbc27cSLeonardo Fogel 	host->host_init =         &emmc_host_init;
100807cbc27cSLeonardo Fogel 	host->set_log_level =     &emmc_set_log_level;
100907cbc27cSLeonardo Fogel 	host->host_reset =        NULL;
101007cbc27cSLeonardo Fogel 	host->card_detect =       &emmc_card_detect;
101107cbc27cSLeonardo Fogel 	host->card_initialize =   &emmc_card_initialize;
101207cbc27cSLeonardo Fogel 	host->card_release =      &emmc_card_release;
101307cbc27cSLeonardo Fogel 	host->hw_intr =           &emmc_hw_intr;
101407cbc27cSLeonardo Fogel 	host->read =              &emmc_read;
101507cbc27cSLeonardo Fogel 	host->write =             &emmc_write;
101607cbc27cSLeonardo Fogel 	for (i=0; i<MAX_SD_SLOTS; i++) {
101707cbc27cSLeonardo Fogel 		host->slot[i].host = host;
101807cbc27cSLeonardo Fogel 		host->slot[i].card.state = SD_MODE_UNINITIALIZED;
101907cbc27cSLeonardo Fogel 		host->slot[i].card.slot = &host->slot[i];
102007cbc27cSLeonardo Fogel 	}
102107cbc27cSLeonardo Fogel }
102207cbc27cSLeonardo Fogel 
102307cbc27cSLeonardo Fogel /*
102407cbc27cSLeonardo Fogel  * Interface to the MINIX block device driver.
102507cbc27cSLeonardo Fogel  * Unused, but declared in mmchost.h.
102607cbc27cSLeonardo Fogel  */
102707cbc27cSLeonardo Fogel void
host_initialize_host_structure_dummy(struct mmc_host * host)102807cbc27cSLeonardo Fogel host_initialize_host_structure_dummy(struct mmc_host *host)
102907cbc27cSLeonardo Fogel {
103007cbc27cSLeonardo Fogel }
1031