diff options
| -rw-r--r-- | drivers/synopsys/emmc/dw_mmc.c | 424 | ||||
| -rw-r--r-- | include/drivers/synopsys/dw_mmc.h | 45 | 
2 files changed, 469 insertions, 0 deletions
| diff --git a/drivers/synopsys/emmc/dw_mmc.c b/drivers/synopsys/emmc/dw_mmc.c new file mode 100644 index 00000000..5b156837 --- /dev/null +++ b/drivers/synopsys/emmc/dw_mmc.c @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2016-2017, ARM Limited and Contributors. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <arch.h> +#include <arch_helpers.h> +#include <assert.h> +#include <debug.h> +#include <delay_timer.h> +#include <dw_mmc.h> +#include <emmc.h> +#include <errno.h> +#include <mmio.h> +#include <string.h> + +#define DWMMC_CTRL			(0x00) +#define CTRL_IDMAC_EN			(1 << 25) +#define CTRL_DMA_EN			(1 << 5) +#define CTRL_INT_EN			(1 << 4) +#define CTRL_DMA_RESET			(1 << 2) +#define CTRL_FIFO_RESET			(1 << 1) +#define CTRL_RESET			(1 << 0) +#define CTRL_RESET_ALL			(CTRL_DMA_RESET | CTRL_FIFO_RESET | \ +					 CTRL_RESET) + +#define DWMMC_PWREN			(0x04) +#define DWMMC_CLKDIV			(0x08) +#define DWMMC_CLKSRC			(0x0c) +#define DWMMC_CLKENA			(0x10) +#define DWMMC_TMOUT			(0x14) +#define DWMMC_CTYPE			(0x18) +#define CTYPE_8BIT			(1 << 16) +#define CTYPE_4BIT			(1) +#define CTYPE_1BIT			(0) + +#define DWMMC_BLKSIZ			(0x1c) +#define DWMMC_BYTCNT			(0x20) +#define DWMMC_INTMASK			(0x24) +#define INT_EBE				(1 << 15) +#define INT_SBE				(1 << 13) +#define INT_HLE				(1 << 12) +#define INT_FRUN			(1 << 11) +#define INT_DRT				(1 << 9) +#define INT_RTO				(1 << 8) +#define INT_DCRC			(1 << 7) +#define INT_RCRC			(1 << 6) +#define INT_RXDR			(1 << 5) +#define INT_TXDR			(1 << 4) +#define INT_DTO				(1 << 3) +#define INT_CMD_DONE			(1 << 2) +#define INT_RE				(1 << 1) + +#define DWMMC_CMDARG			(0x28) +#define DWMMC_CMD			(0x2c) +#define CMD_START			(1 << 31) +#define CMD_USE_HOLD_REG		(1 << 29)	/* 0 if SDR50/100 */ +#define CMD_UPDATE_CLK_ONLY		(1 << 21) +#define CMD_SEND_INIT			(1 << 15) +#define CMD_STOP_ABORT_CMD		(1 << 14) +#define CMD_WAIT_PRVDATA_COMPLETE	(1 << 13) +#define CMD_WRITE			(1 << 10) +#define CMD_DATA_TRANS_EXPECT		(1 << 9) +#define CMD_CHECK_RESP_CRC		(1 << 8) +#define CMD_RESP_LEN			(1 << 7) +#define CMD_RESP_EXPECT			(1 << 6) +#define CMD(x)				(x & 0x3f) + +#define DWMMC_RESP0			(0x30) +#define DWMMC_RESP1			(0x34) +#define DWMMC_RESP2			(0x38) +#define DWMMC_RESP3			(0x3c) +#define DWMMC_RINTSTS			(0x44) +#define DWMMC_STATUS			(0x48) +#define STATUS_DATA_BUSY		(1 << 9) + +#define DWMMC_FIFOTH			(0x4c) +#define FIFOTH_TWMARK(x)		(x & 0xfff) +#define FIFOTH_RWMARK(x)		((x & 0x1ff) << 16) +#define FIFOTH_DMA_BURST_SIZE(x)	((x & 0x7) << 28) + +#define DWMMC_DEBNCE			(0x64) +#define DWMMC_BMOD			(0x80) +#define BMOD_ENABLE			(1 << 7) +#define BMOD_FB				(1 << 1) +#define BMOD_SWRESET			(1 << 0) + +#define DWMMC_DBADDR			(0x88) +#define DWMMC_IDSTS			(0x8c) +#define DWMMC_IDINTEN			(0x90) +#define DWMMC_CARDTHRCTL		(0x100) +#define CARDTHRCTL_RD_THR(x)		((x & 0xfff) << 16) +#define CARDTHRCTL_RD_THR_EN		(1 << 0) + +#define IDMAC_DES0_DIC			(1 << 1) +#define IDMAC_DES0_LD			(1 << 2) +#define IDMAC_DES0_FS			(1 << 3) +#define IDMAC_DES0_CH			(1 << 4) +#define IDMAC_DES0_ER			(1 << 5) +#define IDMAC_DES0_CES			(1 << 30) +#define IDMAC_DES0_OWN			(1 << 31) +#define IDMAC_DES1_BS1(x)		((x) & 0x1fff) +#define IDMAC_DES2_BS2(x)		(((x) & 0x1fff) << 13) + +#define DWMMC_DMA_MAX_BUFFER_SIZE	(512 * 8) + +#define DWMMC_8BIT_MODE			(1 << 6) + +#define TIMEOUT				100000 + +struct dw_idmac_desc { +	unsigned int	des0; +	unsigned int	des1; +	unsigned int	des2; +	unsigned int	des3; +}; + +static void dw_init(void); +static int dw_send_cmd(emmc_cmd_t *cmd); +static int dw_set_ios(int clk, int width); +static int dw_prepare(int lba, uintptr_t buf, size_t size); +static int dw_read(int lba, uintptr_t buf, size_t size); +static int dw_write(int lba, uintptr_t buf, size_t size); + +static const emmc_ops_t dw_mmc_ops = { +	.init		= dw_init, +	.send_cmd	= dw_send_cmd, +	.set_ios	= dw_set_ios, +	.prepare	= dw_prepare, +	.read		= dw_read, +	.write		= dw_write, +}; + +static dw_mmc_params_t dw_params; + +static void dw_update_clk(void) +{ +	unsigned int data; + +	mmio_write_32(dw_params.reg_base + DWMMC_CMD, +		      CMD_WAIT_PRVDATA_COMPLETE | CMD_UPDATE_CLK_ONLY | +		      CMD_START); +	while (1) { +		data = mmio_read_32(dw_params.reg_base + DWMMC_CMD); +		if ((data & CMD_START) == 0) +			break; +		data = mmio_read_32(dw_params.reg_base + DWMMC_RINTSTS); +		assert(data & INT_HLE); +	} +} + +static void dw_set_clk(int clk) +{ +	unsigned int data; +	int div; + +	assert(clk > 0); + +	for (div = 1; div < 256; div++) { +		if ((dw_params.clk_rate / (2 * div)) <= clk) { +			break; +		} +	} +	assert(div < 256); + +	/* wait until controller is idle */ +	do { +		data = mmio_read_32(dw_params.reg_base + DWMMC_STATUS); +	} while (data & STATUS_DATA_BUSY); + +	/* disable clock before change clock rate */ +	mmio_write_32(dw_params.reg_base + DWMMC_CLKENA, 0); +	dw_update_clk(); + +	mmio_write_32(dw_params.reg_base + DWMMC_CLKDIV, div); +	dw_update_clk(); + +	/* enable clock */ +	mmio_write_32(dw_params.reg_base + DWMMC_CLKENA, 1); +	mmio_write_32(dw_params.reg_base + DWMMC_CLKSRC, 0); +	dw_update_clk(); +} + +static void dw_init(void) +{ +	unsigned int data; +	uintptr_t base; + +	assert((dw_params.reg_base & EMMC_BLOCK_MASK) == 0); + +	base = dw_params.reg_base; +	mmio_write_32(base + DWMMC_PWREN, 1); +	mmio_write_32(base + DWMMC_CTRL, CTRL_RESET_ALL); +	do { +		data = mmio_read_32(base + DWMMC_CTRL); +	} while (data); + +	/* enable DMA in CTRL */ +	data = CTRL_INT_EN | CTRL_DMA_EN | CTRL_IDMAC_EN; +	mmio_write_32(base + DWMMC_CTRL, data); +	mmio_write_32(base + DWMMC_RINTSTS, ~0); +	mmio_write_32(base + DWMMC_INTMASK, 0); +	mmio_write_32(base + DWMMC_TMOUT, ~0); +	mmio_write_32(base + DWMMC_IDINTEN, ~0); +	mmio_write_32(base + DWMMC_BLKSIZ, EMMC_BLOCK_SIZE); +	mmio_write_32(base + DWMMC_BYTCNT, 256 * 1024); +	mmio_write_32(base + DWMMC_DEBNCE, 0x00ffffff); +	mmio_write_32(base + DWMMC_BMOD, BMOD_SWRESET); +	do { +		data = mmio_read_32(base + DWMMC_BMOD); +	} while (data & BMOD_SWRESET); +	/* enable DMA in BMOD */ +	data |= BMOD_ENABLE | BMOD_FB; +	mmio_write_32(base + DWMMC_BMOD, data); + +	udelay(100); +	dw_set_clk(EMMC_BOOT_CLK_RATE); +	udelay(100); +} + +static int dw_send_cmd(emmc_cmd_t *cmd) +{ +	unsigned int op, data, err_mask; +	uintptr_t base; +	int timeout; + +	assert(cmd); + +	base = dw_params.reg_base; + +	switch (cmd->cmd_idx) { +	case EMMC_CMD0: +		op = CMD_SEND_INIT; +		break; +	case EMMC_CMD12: +		op = CMD_STOP_ABORT_CMD; +		break; +	case EMMC_CMD13: +		op = CMD_WAIT_PRVDATA_COMPLETE; +		break; +	case EMMC_CMD8: +	case EMMC_CMD17: +	case EMMC_CMD18: +		op = CMD_DATA_TRANS_EXPECT | CMD_WAIT_PRVDATA_COMPLETE; +		break; +	case EMMC_CMD24: +	case EMMC_CMD25: +		op = CMD_WRITE | CMD_DATA_TRANS_EXPECT | +		     CMD_WAIT_PRVDATA_COMPLETE; +		break; +	default: +		op = 0; +		break; +	} +	op |= CMD_USE_HOLD_REG | CMD_START; +	switch (cmd->resp_type) { +	case 0: +		break; +	case EMMC_RESPONSE_R2: +		op |= CMD_RESP_EXPECT | CMD_CHECK_RESP_CRC | +		      CMD_RESP_LEN; +		break; +	case EMMC_RESPONSE_R3: +		op |= CMD_RESP_EXPECT; +		break; +	default: +		op |= CMD_RESP_EXPECT | CMD_CHECK_RESP_CRC; +		break; +	} +	timeout = TIMEOUT; +	do { +		data = mmio_read_32(base + DWMMC_STATUS); +		if (--timeout <= 0) +			panic(); +	} while (data & STATUS_DATA_BUSY); + +	mmio_write_32(base + DWMMC_RINTSTS, ~0); +	mmio_write_32(base + DWMMC_CMDARG, cmd->cmd_arg); +	mmio_write_32(base + DWMMC_CMD, op | cmd->cmd_idx); + +	err_mask = INT_EBE | INT_HLE | INT_RTO | INT_RCRC | INT_RE | +		   INT_DCRC | INT_DRT | INT_SBE; +	timeout = TIMEOUT; +	do { +		udelay(500); +		data = mmio_read_32(base + DWMMC_RINTSTS); + +		if (data & err_mask) +			return -EIO; +		if (data & INT_DTO) +			break; +		if (--timeout == 0) { +			ERROR("%s, RINTSTS:0x%x\n", __func__, data); +			panic(); +		} +	} while (!(data & INT_CMD_DONE)); + +	if (op & CMD_RESP_EXPECT) { +		cmd->resp_data[0] = mmio_read_32(base + DWMMC_RESP0); +		if (op & CMD_RESP_LEN) { +			cmd->resp_data[1] = mmio_read_32(base + DWMMC_RESP1); +			cmd->resp_data[2] = mmio_read_32(base + DWMMC_RESP2); +			cmd->resp_data[3] = mmio_read_32(base + DWMMC_RESP3); +		} +	} +	return 0; +} + +static int dw_set_ios(int clk, int width) +{ +	switch (width) { +	case EMMC_BUS_WIDTH_1: +		mmio_write_32(dw_params.reg_base + DWMMC_CTYPE, CTYPE_1BIT); +		break; +	case EMMC_BUS_WIDTH_4: +		mmio_write_32(dw_params.reg_base + DWMMC_CTYPE, CTYPE_4BIT); +		break; +	case EMMC_BUS_WIDTH_8: +		mmio_write_32(dw_params.reg_base + DWMMC_CTYPE, CTYPE_8BIT); +		break; +	default: +		assert(0); +	} +	dw_set_clk(clk); +	return 0; +} + +static int dw_prepare(int lba, uintptr_t buf, size_t size) +{ +	struct dw_idmac_desc *desc; +	int desc_cnt, i, last; +	uintptr_t base; + +	assert(((buf & EMMC_BLOCK_MASK) == 0) && +	       ((size % EMMC_BLOCK_SIZE) == 0) && +	       (dw_params.desc_size > 0) && +	       ((dw_params.reg_base & EMMC_BLOCK_MASK) == 0) && +	       ((dw_params.desc_base & EMMC_BLOCK_MASK) == 0) && +	       ((dw_params.desc_size & EMMC_BLOCK_MASK) == 0)); + +	desc_cnt = (size + DWMMC_DMA_MAX_BUFFER_SIZE - 1) / +		   DWMMC_DMA_MAX_BUFFER_SIZE; +	assert(desc_cnt * sizeof(struct dw_idmac_desc) < dw_params.desc_size); + +	base = dw_params.reg_base; +	desc = (struct dw_idmac_desc *)dw_params.desc_base; +	mmio_write_32(base + DWMMC_BYTCNT, size); +	mmio_write_32(base + DWMMC_RINTSTS, ~0); +	for (i = 0; i < desc_cnt; i++) { +		desc[i].des0 = IDMAC_DES0_OWN | IDMAC_DES0_CH | IDMAC_DES0_DIC; +		desc[i].des1 = IDMAC_DES1_BS1(DWMMC_DMA_MAX_BUFFER_SIZE); +		desc[i].des2 = buf + DWMMC_DMA_MAX_BUFFER_SIZE * i; +		desc[i].des3 = dw_params.desc_base + +			       (sizeof(struct dw_idmac_desc)) * (i + 1); +	} +	/* first descriptor */ +	desc->des0 |= IDMAC_DES0_FS; +	/* last descriptor */ +	last = desc_cnt - 1; +	(desc + last)->des0 |= IDMAC_DES0_LD; +	(desc + last)->des0 &= ~(IDMAC_DES0_DIC | IDMAC_DES0_CH); +	(desc + last)->des1 = IDMAC_DES1_BS1(size - (last * +				  DWMMC_DMA_MAX_BUFFER_SIZE)); +	/* set next descriptor address as 0 */ +	(desc + last)->des3 = 0; + +	mmio_write_32(base + DWMMC_DBADDR, dw_params.desc_base); +	clean_dcache_range(dw_params.desc_base, +			   desc_cnt * DWMMC_DMA_MAX_BUFFER_SIZE); + +	return 0; +} + +static int dw_read(int lba, uintptr_t buf, size_t size) +{ +	return 0; +} + +static int dw_write(int lba, uintptr_t buf, size_t size) +{ +	return 0; +} + +void dw_mmc_init(dw_mmc_params_t *params) +{ +	assert((params != 0) && +	       ((params->reg_base & EMMC_BLOCK_MASK) == 0) && +	       ((params->desc_base & EMMC_BLOCK_MASK) == 0) && +	       ((params->desc_size & EMMC_BLOCK_MASK) == 0) && +	       (params->desc_size > 0) && +	       (params->clk_rate > 0) && +	       ((params->bus_width == EMMC_BUS_WIDTH_1) || +		(params->bus_width == EMMC_BUS_WIDTH_4) || +		(params->bus_width == EMMC_BUS_WIDTH_8))); + +	memcpy(&dw_params, params, sizeof(dw_mmc_params_t)); +	emmc_init(&dw_mmc_ops, params->clk_rate, params->bus_width, +		  params->flags); +} diff --git a/include/drivers/synopsys/dw_mmc.h b/include/drivers/synopsys/dw_mmc.h new file mode 100644 index 00000000..22192f89 --- /dev/null +++ b/include/drivers/synopsys/dw_mmc.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2016-2017, ARM Limited and Contributors. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __DW_MMC_H__ +#define __DW_MMC_H__ + +typedef struct dw_mmc_params { +	uintptr_t	reg_base; +	uintptr_t	desc_base; +	size_t		desc_size; +	int		clk_rate; +	int		bus_width; +	unsigned int	flags; +} dw_mmc_params_t; + +void dw_mmc_init(dw_mmc_params_t *params); + +#endif	/* __DW_MMC_H__ */ | 
