diff options
| -rw-r--r-- | README | 9 | ||||
| -rw-r--r-- | drivers/mmc/Makefile | 1 | ||||
| -rw-r--r-- | drivers/mmc/sh_mmcif.c | 608 | ||||
| -rw-r--r-- | drivers/mmc/sh_mmcif.h | 238 | 
4 files changed, 856 insertions, 0 deletions
| @@ -1054,6 +1054,15 @@ The following options need to be configured:  		enabled with CONFIG_CMD_MMC. The MMC driver also works with  		the FAT fs. This is enabled with CONFIG_CMD_FAT. +		CONFIG_SH_MMCIF +		Support for Renesas on-chip MMCIF controller + +			CONFIG_SH_MMCIF_ADDR +			Define the base address of MMCIF registers + +			CONFIG_SH_MMCIF_CLK +			Define the clock frequency for MMCIF +  - Journaling Flash filesystem support:  		CONFIG_JFFS2_NAND, CONFIG_JFFS2_NAND_OFF, CONFIG_JFFS2_NAND_SIZE,  		CONFIG_JFFS2_NAND_DEV diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 14f66877d..3968c14bb 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -40,6 +40,7 @@ COBJS-$(CONFIG_OMAP_HSMMC) += omap_hsmmc.o  COBJS-$(CONFIG_PXA_MMC) += pxa_mmc.o  COBJS-$(CONFIG_S5P_MMC) += s5p_mmc.o  COBJS-$(CONFIG_SDHCI) += sdhci.o +COBJS-$(CONFIG_SH_MMCIF) += sh_mmcif.o  COBJS-$(CONFIG_TEGRA2_MMC) += tegra2_mmc.o  COBJS	:= $(COBJS-y) diff --git a/drivers/mmc/sh_mmcif.c b/drivers/mmc/sh_mmcif.c new file mode 100644 index 000000000..567e2cb61 --- /dev/null +++ b/drivers/mmc/sh_mmcif.c @@ -0,0 +1,608 @@ +/* + * MMCIF driver. + * + * Copyright (C)  2011 Renesas Solutions Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + */ + +#include <config.h> +#include <common.h> +#include <watchdog.h> +#include <command.h> +#include <mmc.h> +#include <malloc.h> +#include <asm/errno.h> +#include <asm/io.h> +#include "sh_mmcif.h" + +#define DRIVER_NAME	"sh_mmcif" + +static void *mmc_priv(struct mmc *mmc) +{ +	return (void *)mmc->priv; +} + +static int sh_mmcif_intr(void *dev_id) +{ +	struct sh_mmcif_host *host = dev_id; +	u32 state = 0; + +	state = sh_mmcif_read(&host->regs->ce_int); +	state &= sh_mmcif_read(&host->regs->ce_int_mask); + +	if (state & INT_RBSYE) { +		sh_mmcif_write(~(INT_RBSYE | INT_CRSPE), &host->regs->ce_int); +		sh_mmcif_bitclr(MASK_MRBSYE, &host->regs->ce_int_mask); +		goto end; +	} else if (state & INT_CRSPE) { +		sh_mmcif_write(~INT_CRSPE, &host->regs->ce_int); +		sh_mmcif_bitclr(MASK_MCRSPE, &host->regs->ce_int_mask); +		/* one more interrupt (INT_RBSYE) */ +		if (sh_mmcif_read(&host->regs->ce_cmd_set) & CMD_SET_RBSY) +			return -EAGAIN; +		goto end; +	} else if (state & INT_BUFREN) { +		sh_mmcif_write(~INT_BUFREN, &host->regs->ce_int); +		sh_mmcif_bitclr(MASK_MBUFREN, &host->regs->ce_int_mask); +		goto end; +	} else if (state & INT_BUFWEN) { +		sh_mmcif_write(~INT_BUFWEN, &host->regs->ce_int); +		sh_mmcif_bitclr(MASK_MBUFWEN, &host->regs->ce_int_mask); +		goto end; +	} else if (state & INT_CMD12DRE) { +		sh_mmcif_write(~(INT_CMD12DRE | INT_CMD12RBE | INT_CMD12CRE | +				  INT_BUFRE), &host->regs->ce_int); +		sh_mmcif_bitclr(MASK_MCMD12DRE, &host->regs->ce_int_mask); +		goto end; +	} else if (state & INT_BUFRE) { +		sh_mmcif_write(~INT_BUFRE, &host->regs->ce_int); +		sh_mmcif_bitclr(MASK_MBUFRE, &host->regs->ce_int_mask); +		goto end; +	} else if (state & INT_DTRANE) { +		sh_mmcif_write(~INT_DTRANE, &host->regs->ce_int); +		sh_mmcif_bitclr(MASK_MDTRANE, &host->regs->ce_int_mask); +		goto end; +	} else if (state & INT_CMD12RBE) { +		sh_mmcif_write(~(INT_CMD12RBE | INT_CMD12CRE), +				&host->regs->ce_int); +		sh_mmcif_bitclr(MASK_MCMD12RBE, &host->regs->ce_int_mask); +		goto end; +	} else if (state & INT_ERR_STS) { +		/* err interrupts */ +		sh_mmcif_write(~state, &host->regs->ce_int); +		sh_mmcif_bitclr(state, &host->regs->ce_int_mask); +		goto err; +	} else +		return -EAGAIN; + +err: +	host->sd_error = 1; +	debug("%s: int err state = %08x\n", DRIVER_NAME, state); +end: +	host->wait_int = 1; +	return 0; +} + +static int mmcif_wait_interrupt_flag(struct sh_mmcif_host *host) +{ +	int timeout = 10000000; + +	while (1) { +		timeout--; +		if (timeout < 0) { +			printf("timeout\n"); +			return 0; +		} + +		if (!sh_mmcif_intr(host)) +			break; + +		udelay(1);	/* 1 usec */ +	} + +	return 1;	/* Return value: NOT 0 = complete waiting */ +} + +static void sh_mmcif_clock_control(struct sh_mmcif_host *host, unsigned int clk) +{ +	int i; + +	sh_mmcif_bitclr(CLK_ENABLE, &host->regs->ce_clk_ctrl); +	sh_mmcif_bitclr(CLK_CLEAR, &host->regs->ce_clk_ctrl); + +	if (!clk) +		return; +	if (clk == CLKDEV_EMMC_DATA) { +		sh_mmcif_bitset(CLK_PCLK, &host->regs->ce_clk_ctrl); +	} else { +		for (i = 1; (unsigned int)host->clk / (1 << i) >= clk; i++) +			; +		sh_mmcif_bitset((i - 1) << 16, &host->regs->ce_clk_ctrl); +	} +	sh_mmcif_bitset(CLK_ENABLE, &host->regs->ce_clk_ctrl); +} + +static void sh_mmcif_sync_reset(struct sh_mmcif_host *host) +{ +	u32 tmp; + +	tmp = sh_mmcif_read(&host->regs->ce_clk_ctrl) & (CLK_ENABLE | +							 CLK_CLEAR); + +	sh_mmcif_write(SOFT_RST_ON, &host->regs->ce_version); +	sh_mmcif_write(SOFT_RST_OFF, &host->regs->ce_version); +	sh_mmcif_bitset(tmp | SRSPTO_256 | SRBSYTO_29 | SRWDTO_29 | SCCSTO_29, +			&host->regs->ce_clk_ctrl); +	/* byte swap on */ +	sh_mmcif_bitset(BUF_ACC_ATYP, &host->regs->ce_buf_acc); +} + +static int sh_mmcif_error_manage(struct sh_mmcif_host *host) +{ +	u32 state1, state2; +	int ret, timeout = 10000000; + +	host->sd_error = 0; +	host->wait_int = 0; + +	state1 = sh_mmcif_read(&host->regs->ce_host_sts1); +	state2 = sh_mmcif_read(&host->regs->ce_host_sts2); +	debug("%s: ERR HOST_STS1 = %08x\n", \ +			DRIVER_NAME, sh_mmcif_read(&host->regs->ce_host_sts1)); +	debug("%s: ERR HOST_STS2 = %08x\n", \ +			DRIVER_NAME, sh_mmcif_read(&host->regs->ce_host_sts2)); + +	if (state1 & STS1_CMDSEQ) { +		debug("%s: Forced end of command sequence\n", DRIVER_NAME); +		sh_mmcif_bitset(CMD_CTRL_BREAK, &host->regs->ce_cmd_ctrl); +		sh_mmcif_bitset(~CMD_CTRL_BREAK, &host->regs->ce_cmd_ctrl); +		while (1) { +			timeout--; +			if (timeout < 0) { +				printf(DRIVER_NAME": Forceed end of " \ +					"command sequence timeout err\n"); +				return -EILSEQ; +			} +			if (!(sh_mmcif_read(&host->regs->ce_host_sts1) +								& STS1_CMDSEQ)) +				break; +		} +		sh_mmcif_sync_reset(host); +		return -EILSEQ; +	} + +	if (state2 & STS2_CRC_ERR) +		ret = -EILSEQ; +	else if (state2 & STS2_TIMEOUT_ERR) +		ret = TIMEOUT; +	else +		ret = -EILSEQ; +	return ret; +} + +static int sh_mmcif_single_read(struct sh_mmcif_host *host, +				struct mmc_data *data) +{ +	long time; +	u32 blocksize, i; +	unsigned long *p = (unsigned long *)data->dest; + +	if ((unsigned long)p & 0x00000001) { +		printf("%s: The data pointer is unaligned.", __func__); +		return -EIO; +	} + +	host->wait_int = 0; + +	/* buf read enable */ +	sh_mmcif_bitset(MASK_MBUFREN, &host->regs->ce_int_mask); +	time = mmcif_wait_interrupt_flag(host); +	if (time == 0 || host->sd_error != 0) +		return sh_mmcif_error_manage(host); + +	host->wait_int = 0; +	blocksize = (BLOCK_SIZE_MASK & +			sh_mmcif_read(&host->regs->ce_block_set)) + 3; +	for (i = 0; i < blocksize / 4; i++) +		*p++ = sh_mmcif_read(&host->regs->ce_data); + +	/* buffer read end */ +	sh_mmcif_bitset(MASK_MBUFRE, &host->regs->ce_int_mask); +	time = mmcif_wait_interrupt_flag(host); +	if (time == 0 || host->sd_error != 0) +		return sh_mmcif_error_manage(host); + +	host->wait_int = 0; +	return 0; +} + +static int sh_mmcif_multi_read(struct sh_mmcif_host *host, +				struct mmc_data *data) +{ +	long time; +	u32 blocksize, i, j; +	unsigned long *p = (unsigned long *)data->dest; + +	if ((unsigned long)p & 0x00000001) { +		printf("%s: The data pointer is unaligned.", __func__); +		return -EIO; +	} + +	host->wait_int = 0; +	blocksize = BLOCK_SIZE_MASK & sh_mmcif_read(&host->regs->ce_block_set); +	for (j = 0; j < data->blocks; j++) { +		sh_mmcif_bitset(MASK_MBUFREN, &host->regs->ce_int_mask); +		time = mmcif_wait_interrupt_flag(host); +		if (time == 0 || host->sd_error != 0) +			return sh_mmcif_error_manage(host); + +		host->wait_int = 0; +		for (i = 0; i < blocksize / 4; i++) +			*p++ = sh_mmcif_read(&host->regs->ce_data); + +		WATCHDOG_RESET(); +	} +	return 0; +} + +static int sh_mmcif_single_write(struct sh_mmcif_host *host, +				 struct mmc_data *data) +{ +	long time; +	u32 blocksize, i; +	const unsigned long *p = (unsigned long *)data->dest; + +	if ((unsigned long)p & 0x00000001) { +		printf("%s: The data pointer is unaligned.", __func__); +		return -EIO; +	} + +	host->wait_int = 0; +	sh_mmcif_bitset(MASK_MBUFWEN, &host->regs->ce_int_mask); + +	time = mmcif_wait_interrupt_flag(host); +	if (time == 0 || host->sd_error != 0) +		return sh_mmcif_error_manage(host); + +	host->wait_int = 0; +	blocksize = (BLOCK_SIZE_MASK & +			sh_mmcif_read(&host->regs->ce_block_set)) + 3; +	for (i = 0; i < blocksize / 4; i++) +		sh_mmcif_write(*p++, &host->regs->ce_data); + +	/* buffer write end */ +	sh_mmcif_bitset(MASK_MDTRANE, &host->regs->ce_int_mask); + +	time = mmcif_wait_interrupt_flag(host); +	if (time == 0 || host->sd_error != 0) +		return sh_mmcif_error_manage(host); + +	host->wait_int = 0; +	return 0; +} + +static int sh_mmcif_multi_write(struct sh_mmcif_host *host, +				struct mmc_data *data) +{ +	long time; +	u32 i, j, blocksize; +	const unsigned long *p = (unsigned long *)data->dest; + +	if ((unsigned long)p & 0x00000001) { +		printf("%s: The data pointer is unaligned.", __func__); +		return -EIO; +	} + +	host->wait_int = 0; +	blocksize = BLOCK_SIZE_MASK & sh_mmcif_read(&host->regs->ce_block_set); +	for (j = 0; j < data->blocks; j++) { +		sh_mmcif_bitset(MASK_MBUFWEN, &host->regs->ce_int_mask); + +		time = mmcif_wait_interrupt_flag(host); + +		if (time == 0 || host->sd_error != 0) +			return sh_mmcif_error_manage(host); + +		host->wait_int = 0; +		for (i = 0; i < blocksize / 4; i++) +			sh_mmcif_write(*p++, &host->regs->ce_data); + +		WATCHDOG_RESET(); +	} +	return 0; +} + +static void sh_mmcif_get_response(struct sh_mmcif_host *host, +					struct mmc_cmd *cmd) +{ +	if (cmd->resp_type & MMC_RSP_136) { +		cmd->response[0] = sh_mmcif_read(&host->regs->ce_resp3); +		cmd->response[1] = sh_mmcif_read(&host->regs->ce_resp2); +		cmd->response[2] = sh_mmcif_read(&host->regs->ce_resp1); +		cmd->response[3] = sh_mmcif_read(&host->regs->ce_resp0); +		debug(" RESP %08x, %08x, %08x, %08x\n", cmd->response[0], +			 cmd->response[1], cmd->response[2], cmd->response[3]); +	} else { +		cmd->response[0] = sh_mmcif_read(&host->regs->ce_resp0); +	} +} + +static void sh_mmcif_get_cmd12response(struct sh_mmcif_host *host, +					struct mmc_cmd *cmd) +{ +	cmd->response[0] = sh_mmcif_read(&host->regs->ce_resp_cmd12); +} + +static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host, +				struct mmc_data *data, struct mmc_cmd *cmd) +{ +	u32 tmp = 0; +	u32 opc = cmd->cmdidx; + +	/* Response Type check */ +	switch (cmd->resp_type) { +	case MMC_RSP_NONE: +		tmp |= CMD_SET_RTYP_NO; +		break; +	case MMC_RSP_R1: +	case MMC_RSP_R1b: +	case MMC_RSP_R3: +		tmp |= CMD_SET_RTYP_6B; +		break; +	case MMC_RSP_R2: +		tmp |= CMD_SET_RTYP_17B; +		break; +	default: +		printf(DRIVER_NAME": Not support type response.\n"); +		break; +	} + +	/* RBSY */ +	if (opc == MMC_CMD_SWITCH) +		tmp |= CMD_SET_RBSY; + +	/* WDAT / DATW */ +	if (host->data) { +		tmp |= CMD_SET_WDAT; +		switch (host->bus_width) { +		case MMC_BUS_WIDTH_1: +			tmp |= CMD_SET_DATW_1; +			break; +		case MMC_BUS_WIDTH_4: +			tmp |= CMD_SET_DATW_4; +			break; +		case MMC_BUS_WIDTH_8: +			tmp |= CMD_SET_DATW_8; +			break; +		default: +			printf(DRIVER_NAME": Not support bus width.\n"); +			break; +		} +	} +	/* DWEN */ +	if (opc == MMC_CMD_WRITE_SINGLE_BLOCK || +	    opc == MMC_CMD_WRITE_MULTIPLE_BLOCK) +		tmp |= CMD_SET_DWEN; +	/* CMLTE/CMD12EN */ +	if (opc == MMC_CMD_READ_MULTIPLE_BLOCK || +	    opc == MMC_CMD_WRITE_MULTIPLE_BLOCK) { +		tmp |= CMD_SET_CMLTE | CMD_SET_CMD12EN; +		sh_mmcif_bitset(data->blocks << 16, &host->regs->ce_block_set); +	} +	/* RIDXC[1:0] check bits */ +	if (opc == MMC_CMD_SEND_OP_COND || opc == MMC_CMD_ALL_SEND_CID || +	    opc == MMC_CMD_SEND_CSD || opc == MMC_CMD_SEND_CID) +		tmp |= CMD_SET_RIDXC_BITS; +	/* RCRC7C[1:0] check bits */ +	if (opc == MMC_CMD_SEND_OP_COND) +		tmp |= CMD_SET_CRC7C_BITS; +	/* RCRC7C[1:0] internal CRC7 */ +	if (opc == MMC_CMD_ALL_SEND_CID || +		opc == MMC_CMD_SEND_CSD || opc == MMC_CMD_SEND_CID) +		tmp |= CMD_SET_CRC7C_INTERNAL; + +	return opc = ((opc << 24) | tmp); +} + +static u32 sh_mmcif_data_trans(struct sh_mmcif_host *host, +				struct mmc_data *data, u16 opc) +{ +	u32 ret; + +	switch (opc) { +	case MMC_CMD_READ_MULTIPLE_BLOCK: +		ret = sh_mmcif_multi_read(host, data); +		break; +	case MMC_CMD_WRITE_MULTIPLE_BLOCK: +		ret = sh_mmcif_multi_write(host, data); +		break; +	case MMC_CMD_WRITE_SINGLE_BLOCK: +		ret = sh_mmcif_single_write(host, data); +		break; +	case MMC_CMD_READ_SINGLE_BLOCK: +	case MMC_CMD_SEND_EXT_CSD: +		ret = sh_mmcif_single_read(host, data); +		break; +	default: +		printf(DRIVER_NAME": NOT SUPPORT CMD = d'%08d\n", opc); +		ret = -EINVAL; +		break; +	} +	return ret; +} + +static int sh_mmcif_start_cmd(struct sh_mmcif_host *host, +				struct mmc_data *data, struct mmc_cmd *cmd) +{ +	long time; +	int ret = 0, mask = 0; +	u32 opc = cmd->cmdidx; + +	if (opc == MMC_CMD_STOP_TRANSMISSION) { +		/* MMCIF sends the STOP command automatically */ +		if (host->last_cmd == MMC_CMD_READ_MULTIPLE_BLOCK) +			sh_mmcif_bitset(MASK_MCMD12DRE, +					&host->regs->ce_int_mask); +		else +			sh_mmcif_bitset(MASK_MCMD12RBE, +					&host->regs->ce_int_mask); + +		time = mmcif_wait_interrupt_flag(host); +		if (time == 0 || host->sd_error != 0) +			return sh_mmcif_error_manage(host); + +		sh_mmcif_get_cmd12response(host, cmd); +		return 0; +	} +	if (opc == MMC_CMD_SWITCH) +		mask = MASK_MRBSYE; +	else +		mask = MASK_MCRSPE; + +	mask |=	MASK_MCMDVIO | MASK_MBUFVIO | MASK_MWDATERR | +		MASK_MRDATERR | MASK_MRIDXERR | MASK_MRSPERR | +		MASK_MCCSTO | MASK_MCRCSTO | MASK_MWDATTO | +		MASK_MRDATTO | MASK_MRBSYTO | MASK_MRSPTO; + +	if (host->data) { +		sh_mmcif_write(0, &host->regs->ce_block_set); +		sh_mmcif_write(data->blocksize, &host->regs->ce_block_set); +	} +	opc = sh_mmcif_set_cmd(host, data, cmd); + +	sh_mmcif_write(INT_START_MAGIC, &host->regs->ce_int); +	sh_mmcif_write(mask, &host->regs->ce_int_mask); + +	debug("CMD%d ARG:%08x\n", cmd->cmdidx, cmd->cmdarg); +	/* set arg */ +	sh_mmcif_write(cmd->cmdarg, &host->regs->ce_arg); +	host->wait_int = 0; +	/* set cmd */ +	sh_mmcif_write(opc, &host->regs->ce_cmd_set); + +	time = mmcif_wait_interrupt_flag(host); +	if (time == 0) +		return sh_mmcif_error_manage(host); + +	if (host->sd_error) { +		switch (cmd->cmdidx) { +		case MMC_CMD_ALL_SEND_CID: +		case MMC_CMD_SELECT_CARD: +		case MMC_CMD_APP_CMD: +			ret = TIMEOUT; +			break; +		default: +			printf(DRIVER_NAME": Cmd(d'%d) err\n", cmd->cmdidx); +			ret = sh_mmcif_error_manage(host); +			break; +		} +		host->sd_error = 0; +		host->wait_int = 0; +		return ret; +	} + +	/* if no response */ +	if (!(opc & 0x00C00000)) +		return 0; + +	if (host->wait_int == 1) { +		sh_mmcif_get_response(host, cmd); +		host->wait_int = 0; +	} +	if (host->data) +		ret = sh_mmcif_data_trans(host, data, cmd->cmdidx); +	host->last_cmd = cmd->cmdidx; + +	return ret; +} + +static int sh_mmcif_request(struct mmc *mmc, struct mmc_cmd *cmd, +			    struct mmc_data *data) +{ +	struct sh_mmcif_host *host = mmc_priv(mmc); +	int ret; + +	WATCHDOG_RESET(); + +	switch (cmd->cmdidx) { +	case MMC_CMD_APP_CMD: +		return TIMEOUT; +	case MMC_CMD_SEND_EXT_CSD: /* = SD_SEND_IF_COND (8) */ +		if (data) +			/* ext_csd */ +			break; +		else +			/* send_if_cond cmd (not support) */ +			return TIMEOUT; +	default: +		break; +	} +	host->sd_error = 0; +	host->data = data; +	ret = sh_mmcif_start_cmd(host, data, cmd); +	host->data = NULL; + +	return ret; +} + +static void sh_mmcif_set_ios(struct mmc *mmc) +{ +	struct sh_mmcif_host *host = mmc_priv(mmc); + +	if (mmc->clock) +		sh_mmcif_clock_control(host, mmc->clock); + +	if (mmc->bus_width == 8) +		host->bus_width = MMC_BUS_WIDTH_8; +	else if (mmc->bus_width == 4) +		host->bus_width = MMC_BUS_WIDTH_4; +	else +		host->bus_width = MMC_BUS_WIDTH_1; + +	debug("clock = %d, buswidth = %d\n", mmc->clock, mmc->bus_width); +} + +static int sh_mmcif_init(struct mmc *mmc) +{ +	struct sh_mmcif_host *host = mmc_priv(mmc); + +	sh_mmcif_sync_reset(host); +	sh_mmcif_write(MASK_ALL, &host->regs->ce_int_mask); +	return 0; +} + +int mmcif_mmc_init(void) +{ +	int ret = 0; +	struct mmc *mmc; +	struct sh_mmcif_host *host = NULL; + +	mmc = malloc(sizeof(struct mmc)); +	if (!mmc) +		ret = -ENOMEM; +	memset(mmc, 0, sizeof(*mmc)); +	host = malloc(sizeof(struct sh_mmcif_host)); +	if (!host) +		ret = -ENOMEM; +	memset(host, 0, sizeof(*host)); + +	mmc->f_min = CLKDEV_MMC_INIT; +	mmc->f_max = CLKDEV_EMMC_DATA; +	mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; +	mmc->host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT | +			 MMC_MODE_8BIT; +	memcpy(mmc->name, DRIVER_NAME, sizeof(DRIVER_NAME)); +	mmc->send_cmd = sh_mmcif_request; +	mmc->set_ios = sh_mmcif_set_ios; +	mmc->init = sh_mmcif_init; +	host->regs = (struct sh_mmcif_regs *)CONFIG_SH_MMCIF_ADDR; +	host->clk = CONFIG_SH_MMCIF_CLK; +	mmc->priv = host; + +	mmc_register(mmc); + +	return ret; +} diff --git a/drivers/mmc/sh_mmcif.h b/drivers/mmc/sh_mmcif.h new file mode 100644 index 000000000..bd6fbf7c6 --- /dev/null +++ b/drivers/mmc/sh_mmcif.h @@ -0,0 +1,238 @@ +/* + * MMCIF driver. + * + * Copyright (C)  2011 Renesas Solutions Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + */ + +#ifndef _SH_MMCIF_H_ +#define _SH_MMCIF_H_ + +struct sh_mmcif_regs { +	unsigned long ce_cmd_set; +	unsigned long reserved; +	unsigned long ce_arg; +	unsigned long ce_arg_cmd12; +	unsigned long ce_cmd_ctrl; +	unsigned long ce_block_set; +	unsigned long ce_clk_ctrl; +	unsigned long ce_buf_acc; +	unsigned long ce_resp3; +	unsigned long ce_resp2; +	unsigned long ce_resp1; +	unsigned long ce_resp0; +	unsigned long ce_resp_cmd12; +	unsigned long ce_data; +	unsigned long reserved2[2]; +	unsigned long ce_int; +	unsigned long ce_int_mask; +	unsigned long ce_host_sts1; +	unsigned long ce_host_sts2; +	unsigned long reserved3[11]; +	unsigned long ce_version; +}; + +/* CE_CMD_SET */ +#define CMD_MASK		0x3f000000 +#define CMD_SET_RTYP_NO		((0 << 23) | (0 << 22)) +/* R1/R1b/R3/R4/R5 */ +#define CMD_SET_RTYP_6B		((0 << 23) | (1 << 22)) +/* R2 */ +#define CMD_SET_RTYP_17B	((1 << 23) | (0 << 22)) +/* R1b */ +#define CMD_SET_RBSY		(1 << 21) +#define CMD_SET_CCSEN		(1 << 20) +/* 1: on data, 0: no data */ +#define CMD_SET_WDAT		(1 << 19) +/* 1: write to card, 0: read from card */ +#define CMD_SET_DWEN		(1 << 18) +/* 1: multi block trans, 0: single */ +#define CMD_SET_CMLTE		(1 << 17) +/* 1: CMD12 auto issue */ +#define CMD_SET_CMD12EN		(1 << 16) +/* index check */ +#define CMD_SET_RIDXC_INDEX	((0 << 15) | (0 << 14)) +/* check bits check */ +#define CMD_SET_RIDXC_BITS	((0 << 15) | (1 << 14)) +/* no check */ +#define CMD_SET_RIDXC_NO	((1 << 15) | (0 << 14)) +/* 1: CRC7 check*/ +#define CMD_SET_CRC7C		((0 << 13) | (0 << 12)) +/* 1: check bits check*/ +#define CMD_SET_CRC7C_BITS	((0 << 13) | (1 << 12)) +/* 1: internal CRC7 check*/ +#define CMD_SET_CRC7C_INTERNAL	((1 << 13) | (0 << 12)) +/* 1: CRC16 check*/ +#define CMD_SET_CRC16C		(1 << 10) +/* 1: not receive CRC status */ +#define CMD_SET_CRCSTE		(1 << 8) +/* 1: tran mission bit "Low" */ +#define CMD_SET_TBIT		(1 << 7) +/* 1: open/drain */ +#define CMD_SET_OPDM		(1 << 6) +#define CMD_SET_CCSH		(1 << 5) +/* 1bit */ +#define CMD_SET_DATW_1		((0 << 1) | (0 << 0)) +/* 4bit */ +#define CMD_SET_DATW_4		((0 << 1) | (1 << 0)) +/* 8bit */ +#define CMD_SET_DATW_8		((1 << 1) | (0 << 0)) + +/* CE_CMD_CTRL */ +#define CMD_CTRL_BREAK		(1 << 0) + +/* CE_BLOCK_SET */ +#define BLOCK_SIZE_MASK		0x0000ffff + +/* CE_CLK_CTRL */ +#define CLK_ENABLE		(1 << 24) +#define CLK_CLEAR		((1 << 19) | (1 << 18) | (1 << 17) | (1 << 16)) +#define CLK_PCLK		((1 << 19) | (1 << 18) | (1 << 17) | (1 << 16)) +/* respons timeout */ +#define SRSPTO_256		((1 << 13) | (0 << 12)) +/* respons busy timeout */ +#define SRBSYTO_29		((1 << 11) | (1 << 10) | (1 << 9) | (1 << 8)) +/* read/write timeout */ +#define SRWDTO_29		((1 << 7) | (1 << 6) | (1 << 5) | (1 << 4)) +/* ccs timeout */ +#define SCCSTO_29		((1 << 3) | (1 << 2) | (1 << 1) | (1 << 0)) + +/* CE_BUF_ACC */ +#define BUF_ACC_DMAWEN		(1 << 25) +#define BUF_ACC_DMAREN		(1 << 24) +#define BUF_ACC_BUSW_32		(0 << 17) +#define BUF_ACC_BUSW_16		(1 << 17) +#define BUF_ACC_ATYP		(1 << 16) + +/* CE_INT */ +#define INT_CCSDE		(1 << 29) +#define INT_CMD12DRE		(1 << 26) +#define INT_CMD12RBE		(1 << 25) +#define INT_CMD12CRE		(1 << 24) +#define INT_DTRANE		(1 << 23) +#define INT_BUFRE		(1 << 22) +#define INT_BUFWEN		(1 << 21) +#define INT_BUFREN		(1 << 20) +#define INT_CCSRCV		(1 << 19) +#define INT_RBSYE		(1 << 17) +#define INT_CRSPE		(1 << 16) +#define INT_CMDVIO		(1 << 15) +#define INT_BUFVIO		(1 << 14) +#define INT_WDATERR		(1 << 11) +#define INT_RDATERR		(1 << 10) +#define INT_RIDXERR		(1 << 9) +#define INT_RSPERR		(1 << 8) +#define INT_CCSTO		(1 << 5) +#define INT_CRCSTO		(1 << 4) +#define INT_WDATTO		(1 << 3) +#define INT_RDATTO		(1 << 2) +#define INT_RBSYTO		(1 << 1) +#define INT_RSPTO		(1 << 0) +#define INT_ERR_STS		(INT_CMDVIO | INT_BUFVIO | INT_WDATERR |  \ +				 INT_RDATERR | INT_RIDXERR | INT_RSPERR | \ +				 INT_CCSTO | INT_CRCSTO | INT_WDATTO |	  \ +				 INT_RDATTO | INT_RBSYTO | INT_RSPTO) +#define INT_START_MAGIC		0xD80430C0 + +/* CE_INT_MASK */ +#define MASK_ALL		0x00000000 +#define MASK_MCCSDE		(1 << 29) +#define MASK_MCMD12DRE		(1 << 26) +#define MASK_MCMD12RBE		(1 << 25) +#define MASK_MCMD12CRE		(1 << 24) +#define MASK_MDTRANE		(1 << 23) +#define MASK_MBUFRE		(1 << 22) +#define MASK_MBUFWEN		(1 << 21) +#define MASK_MBUFREN		(1 << 20) +#define MASK_MCCSRCV		(1 << 19) +#define MASK_MRBSYE		(1 << 17) +#define MASK_MCRSPE		(1 << 16) +#define MASK_MCMDVIO		(1 << 15) +#define MASK_MBUFVIO		(1 << 14) +#define MASK_MWDATERR		(1 << 11) +#define MASK_MRDATERR		(1 << 10) +#define MASK_MRIDXERR		(1 << 9) +#define MASK_MRSPERR		(1 << 8) +#define MASK_MCCSTO		(1 << 5) +#define MASK_MCRCSTO		(1 << 4) +#define MASK_MWDATTO		(1 << 3) +#define MASK_MRDATTO		(1 << 2) +#define MASK_MRBSYTO		(1 << 1) +#define MASK_MRSPTO		(1 << 0) + +/* CE_HOST_STS1 */ +#define STS1_CMDSEQ		(1 << 31) + +/* CE_HOST_STS2 */ +#define STS2_CRCSTE		(1 << 31) +#define STS2_CRC16E		(1 << 30) +#define STS2_AC12CRCE		(1 << 29) +#define STS2_RSPCRC7E		(1 << 28) +#define STS2_CRCSTEBE		(1 << 27) +#define STS2_RDATEBE		(1 << 26) +#define STS2_AC12REBE		(1 << 25) +#define STS2_RSPEBE		(1 << 24) +#define STS2_AC12IDXE		(1 << 23) +#define STS2_RSPIDXE		(1 << 22) +#define STS2_CCSTO		(1 << 15) +#define STS2_RDATTO		(1 << 14) +#define STS2_DATBSYTO		(1 << 13) +#define STS2_CRCSTTO		(1 << 12) +#define STS2_AC12BSYTO		(1 << 11) +#define STS2_RSPBSYTO		(1 << 10) +#define STS2_AC12RSPTO		(1 << 9) +#define STS2_RSPTO		(1 << 8) + +#define STS2_CRC_ERR		(STS2_CRCSTE | STS2_CRC16E |		\ +				 STS2_AC12CRCE | STS2_RSPCRC7E | STS2_CRCSTEBE) +#define STS2_TIMEOUT_ERR	(STS2_CCSTO | STS2_RDATTO |		\ +				 STS2_DATBSYTO | STS2_CRCSTTO |		\ +				 STS2_AC12BSYTO | STS2_RSPBSYTO |	\ +				 STS2_AC12RSPTO | STS2_RSPTO) + +/* CE_VERSION */ +#define SOFT_RST_ON		(1 << 31) +#define SOFT_RST_OFF		(0 << 31) + +#define CLKDEV_EMMC_DATA	52000000	/* 52MHz */ +#define	CLKDEV_MMC_INIT		400000		/* 100 - 400 KHz */ + +#define MMC_BUS_WIDTH_1		0 +#define MMC_BUS_WIDTH_4		2 +#define MMC_BUS_WIDTH_8		3 + +struct sh_mmcif_host { +	struct mmc_data		*data; +	struct sh_mmcif_regs	*regs; +	unsigned int		clk; +	int			bus_width; +	u16			wait_int; +	u16			sd_error; +	u8			last_cmd; +}; + +static inline u32 sh_mmcif_read(unsigned long *reg) +{ +	return readl(reg); +} + +static inline void sh_mmcif_write(u32 val, unsigned long *reg) +{ +	writel(val, reg); +} + +static inline void sh_mmcif_bitset(u32 val, unsigned long *reg) +{ +	sh_mmcif_write(val | sh_mmcif_read(reg), reg); +} + +static inline void sh_mmcif_bitclr(u32 val, unsigned long *reg) +{ +	sh_mmcif_write(~val & sh_mmcif_read(reg), reg); +} + +#endif /* _SH_MMCIF_H_ */ |