diff options
Diffstat (limited to '0024-sunxi-mmc-support.patch')
-rw-r--r-- | 0024-sunxi-mmc-support.patch | 797 |
1 files changed, 797 insertions, 0 deletions
diff --git a/0024-sunxi-mmc-support.patch b/0024-sunxi-mmc-support.patch new file mode 100644 index 0000000..a10326b --- /dev/null +++ b/0024-sunxi-mmc-support.patch @@ -0,0 +1,797 @@ +From ec8c2c931fda9e06eee7203c72025f6ecd25ef7f Mon Sep 17 00:00:00 2001 +From: Ian Campbell <ijc@hellion.org.uk> +Date: Fri, 18 Apr 2014 19:05:48 +0100 +Subject: [PATCH 24/36] sunxi: mmc support + +This adds support for the MMC controller on the Allwinner A20 (sun7i) +processor. + +Signed-off-by: Henrik Nordstrom <henrik@henriknordstrom.net> +Signed-off-by: Luke Leighton <lkcl@lkcl.net> +Signed-off-by: Oliver Schinagl <oliver@schinagl.nl> +Signed-off-by: Wills Wang <wills.wang.open@gmail.com> +Signed-off-by: Ian Campbell <ijc@hellion.org.uk> +Cc: Stefan Roese <sr@denx.de> +Cc: Tom Cubie <Mr.hipboi@gmail.com> +Cc: Aaron Maoye <leafy.myeh@allwinnertech.com> +Cc: Pantelis Antoniou <panto@antoniou-consulting.com> +--- + arch/arm/include/asm/arch-sunxi/mmc.h | 124 ++++++++ + board/sunxi/board.c | 13 + + drivers/mmc/Makefile | 1 + + drivers/mmc/sunxi_mmc.c | 559 ++++++++++++++++++++++++++++++++++ + include/configs/sunxi-common.h | 11 + + 5 files changed, 708 insertions(+) + create mode 100644 arch/arm/include/asm/arch-sunxi/mmc.h + create mode 100644 drivers/mmc/sunxi_mmc.c + +diff --git a/arch/arm/include/asm/arch-sunxi/mmc.h b/arch/arm/include/asm/arch-sunxi/mmc.h +new file mode 100644 +index 0000000..53196e3 +--- /dev/null ++++ b/arch/arm/include/asm/arch-sunxi/mmc.h +@@ -0,0 +1,124 @@ ++/* ++ * (C) Copyright 2007-2011 ++ * Allwinner Technology Co., Ltd. <www.allwinnertech.com> ++ * Aaron <leafy.myeh@allwinnertech.com> ++ * ++ * MMC register definition for allwinner sunxi platform. ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++ ++#ifndef _SUNXI_MMC_H ++#define _SUNXI_MMC_H ++ ++#include <linux/types.h> ++ ++struct sunxi_mmc { ++ u32 gctrl; /* 0x00 global control */ ++ u32 clkcr; /* 0x04 clock control */ ++ u32 timeout; /* 0x08 time out */ ++ u32 width; /* 0x0c bus width */ ++ u32 blksz; /* 0x10 block size */ ++ u32 bytecnt; /* 0x14 byte count */ ++ u32 cmd; /* 0x18 command */ ++ u32 arg; /* 0x1c argument */ ++ u32 resp0; /* 0x20 response 0 */ ++ u32 resp1; /* 0x24 response 1 */ ++ u32 resp2; /* 0x28 response 2 */ ++ u32 resp3; /* 0x2c response 3 */ ++ u32 imask; /* 0x30 interrupt mask */ ++ u32 mint; /* 0x34 masked interrupt status */ ++ u32 rint; /* 0x38 raw interrupt status */ ++ u32 status; /* 0x3c status */ ++ u32 ftrglevel; /* 0x40 FIFO threshold watermark*/ ++ u32 funcsel; /* 0x44 function select */ ++ u32 cbcr; /* 0x48 CIU byte count */ ++ u32 bbcr; /* 0x4c BIU byte count */ ++ u32 dbgc; /* 0x50 debug enable */ ++ u32 res0[11]; ++ u32 dmac; /* 0x80 internal DMA control */ ++ u32 dlba; /* 0x84 internal DMA descr list base address */ ++ u32 idst; /* 0x88 internal DMA status */ ++ u32 idie; /* 0x8c internal DMA interrupt enable */ ++ u32 chda; /* 0x90 */ ++ u32 cbda; /* 0x94 */ ++ u32 res1[26]; ++ u32 fifo; /* 0x100 FIFO access address */ ++}; ++ ++#define SUNXI_MMC_CLK_POWERSAVE (0x1 << 17) ++#define SUNXI_MMC_CLK_ENABLE (0x1 << 16) ++#define SUNXI_MMC_CLK_DIVIDER_MASK (0xff) ++ ++#define SUNXI_MMC_GCTRL_SOFT_RESET (0x1 << 0) ++#define SUNXI_MMC_GCTRL_FIFO_RESET (0x1 << 1) ++#define SUNXI_MMC_GCTRL_DMA_RESET (0x1 << 2) ++#define SUNXI_MMC_GCTRL_RESET (SUNXI_MMC_GCTRL_SOFT_RESET|\ ++ SUNXI_MMC_GCTRL_FIFO_RESET|\ ++ SUNXI_MMC_GCTRL_DMA_RESET) ++#define SUNXI_MMC_GCTRL_DMA_ENABLE (0x1 << 5) ++#define SUNXI_MMC_GCTRL_ACCESS_BY_AHB (0x1 << 31) ++ ++#define SUNXI_MMC_CMD_RESP_EXPIRE (0x1 << 6) ++#define SUNXI_MMC_CMD_LONG_RESPONSE (0x1 << 7) ++#define SUNXI_MMC_CMD_CHK_RESPONSE_CRC (0x1 << 8) ++#define SUNXI_MMC_CMD_DATA_EXPIRE (0x1 << 9) ++#define SUNXI_MMC_CMD_WRITE (0x1 << 10) ++#define SUNXI_MMC_CMD_AUTO_STOP (0x1 << 12) ++#define SUNXI_MMC_CMD_WAIT_PRE_OVER (0x1 << 13) ++#define SUNXI_MMC_CMD_SEND_INIT_SEQ (0x1 << 15) ++#define SUNXI_MMC_CMD_UPCLK_ONLY (0x1 << 21) ++#define SUNXI_MMC_CMD_START (0x1 << 31) ++ ++#define SUNXI_MMC_RINT_RESP_ERROR (0x1 << 1) ++#define SUNXI_MMC_RINT_COMMAND_DONE (0x1 << 2) ++#define SUNXI_MMC_RINT_DATA_OVER (0x1 << 3) ++#define SUNXI_MMC_RINT_TX_DATA_REQUEST (0x1 << 4) ++#define SUNXI_MMC_RINT_RX_DATA_REQUEST (0x1 << 5) ++#define SUNXI_MMC_RINT_RESP_CRC_ERROR (0x1 << 6) ++#define SUNXI_MMC_RINT_DATA_CRC_ERROR (0x1 << 7) ++#define SUNXI_MMC_RINT_RESP_TIMEOUT (0x1 << 8) ++#define SUNXI_MMC_RINT_DATA_TIMEOUT (0x1 << 9) ++#define SUNXI_MMC_RINT_VOLTAGE_CHANGE_DONE (0x1 << 10) ++#define SUNXI_MMC_RINT_FIFO_RUN_ERROR (0x1 << 11) ++#define SUNXI_MMC_RINT_HARD_WARE_LOCKED (0x1 << 12) ++#define SUNXI_MMC_RINT_START_BIT_ERROR (0x1 << 13) ++#define SUNXI_MMC_RINT_AUTO_COMMAND_DONE (0x1 << 14) ++#define SUNXI_MMC_RINT_END_BIT_ERROR (0x1 << 15) ++#define SUNXI_MMC_RINT_SDIO_INTERRUPT (0x1 << 16) ++#define SUNXI_MMC_RINT_CARD_INSERT (0x1 << 30) ++#define SUNXI_MMC_RINT_CARD_REMOVE (0x1 << 31) ++#define SUNXI_MMC_RINT_INTERRUPT_ERROR_BIT \ ++ (SUNXI_MMC_RINT_RESP_ERROR | \ ++ SUNXI_MMC_RINT_RESP_CRC_ERROR | \ ++ SUNXI_MMC_RINT_DATA_CRC_ERROR | \ ++ SUNXI_MMC_RINT_RESP_TIMEOUT | \ ++ SUNXI_MMC_RINT_DATA_TIMEOUT | \ ++ SUNXI_MMC_RINT_VOLTAGE_CHANGE_DONE | \ ++ SUNXI_MMC_RINT_FIFO_RUN_ERROR | \ ++ SUNXI_MMC_RINT_HARD_WARE_LOCKED | \ ++ SUNXI_MMC_RINT_START_BIT_ERROR | \ ++ SUNXI_MMC_RINT_END_BIT_ERROR) /* 0xbfc2 */ ++#define SUNXI_MMC_RINT_INTERRUPT_DONE_BIT \ ++ (SUNXI_MMC_RINT_AUTO_COMMAND_DONE | \ ++ SUNXI_MMC_RINT_DATA_OVER | \ ++ SUNXI_MMC_RINT_COMMAND_DONE | \ ++ SUNXI_MMC_RINT_VOLTAGE_CHANGE_DONE) ++ ++#define SUNXI_MMC_STATUS_RXWL_FLAG (0x1 << 0) ++#define SUNXI_MMC_STATUS_TXWL_FLAG (0x1 << 1) ++#define SUNXI_MMC_STATUS_FIFO_EMPTY (0x1 << 2) ++#define SUNXI_MMC_STATUS_FIFO_FULL (0x1 << 3) ++#define SUNXI_MMC_STATUS_CARD_PRESENT (0x1 << 8) ++#define SUNXI_MMC_STATUS_CARD_DATA_BUSY (0x1 << 9) ++#define SUNXI_MMC_STATUS_DATA_FSM_BUSY (0x1 << 10) ++ ++#define SUNXI_MMC_IDMAC_RESET (0x1 << 0) ++#define SUNXI_MMC_IDMAC_FIXBURST (0x1 << 1) ++#define SUNXI_MMC_IDMAC_ENABLE (0x1 << 7) ++ ++#define SUNXI_MMC_IDIE_TXIRQ (0x1 << 0) ++#define SUNXI_MMC_IDIE_RXIRQ (0x1 << 1) ++ ++int sunxi_mmc_init(int sdc_no); ++#endif /* _SUNXI_MMC_H */ +diff --git a/board/sunxi/board.c b/board/sunxi/board.c +index 328334a..06c9264 100644 +--- a/board/sunxi/board.c ++++ b/board/sunxi/board.c +@@ -14,6 +14,7 @@ + #include <common.h> + #include <asm/arch/clock.h> + #include <asm/arch/dram.h> ++#include <asm/arch/mmc.h> + + DECLARE_GLOBAL_DATA_PTR; + +@@ -43,6 +44,18 @@ int dram_init(void) + return 0; + } + ++#ifdef CONFIG_GENERIC_MMC ++int board_mmc_init(bd_t *bis) ++{ ++ sunxi_mmc_init(CONFIG_MMC_SUNXI_SLOT); ++#if !defined (CONFIG_SPL_BUILD) && defined (CONFIG_MMC_SUNXI_SLOT_EXTRA) ++ sunxi_mmc_init(CONFIG_MMC_SUNXI_SLOT_EXTRA); ++#endif ++ ++ return 0; ++} ++#endif ++ + #ifdef CONFIG_SPL_BUILD + void sunxi_board_init(void) + { +diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile +index 931922b..5e67098 100644 +--- a/drivers/mmc/Makefile ++++ b/drivers/mmc/Makefile +@@ -28,6 +28,7 @@ obj-$(CONFIG_SPEAR_SDHCI) += spear_sdhci.o + obj-$(CONFIG_TEGRA_MMC) += tegra_mmc.o + obj-$(CONFIG_DWMMC) += dw_mmc.o + obj-$(CONFIG_EXYNOS_DWMMC) += exynos_dw_mmc.o ++obj-$(CONFIG_MMC_SUNXI) += sunxi_mmc.o + obj-$(CONFIG_ZYNQ_SDHCI) += zynq_sdhci.o + obj-$(CONFIG_SOCFPGA_DWMMC) += socfpga_dw_mmc.o + ifdef CONFIG_SPL_BUILD +diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c +new file mode 100644 +index 0000000..f002694 +--- /dev/null ++++ b/drivers/mmc/sunxi_mmc.c +@@ -0,0 +1,559 @@ ++/* ++ * (C) Copyright 2007-2011 ++ * Allwinner Technology Co., Ltd. <www.allwinnertech.com> ++ * Aaron <leafy.myeh@allwinnertech.com> ++ * ++ * MMC driver for allwinner sunxi platform. ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++ ++#include <common.h> ++#include <malloc.h> ++#include <mmc.h> ++#include <asm/io.h> ++#include <asm/arch/clock.h> ++#include <asm/arch/cpu.h> ++#include <asm/arch/gpio.h> ++#include <asm/arch/mmc.h> ++ ++struct sunxi_mmc_des { ++ u32 reserved1_1:1; ++ u32 dic:1; /* disable interrupt on completion */ ++ u32 last_des:1; /* 1-this data buffer is the last buffer */ ++ u32 first_des:1; /* 1-data buffer is the first buffer, ++ 0-data buffer contained in the next ++ descriptor is 1st buffer */ ++ u32 des_chain:1; /* 1-the 2nd address in the descriptor is the ++ next descriptor address */ ++ u32 end_of_ring:1; /* 1-last descriptor flag when using dual ++ data buffer in descriptor */ ++ u32 reserved1_2:24; ++ u32 card_err_sum:1; /* transfer error flag */ ++ u32 own:1; /* des owner:1-idma owns it, 0-host owns it */ ++#define SDXC_DES_NUM_SHIFT 16 ++#define SDXC_DES_BUFFER_MAX_LEN (1 << SDXC_DES_NUM_SHIFT) ++ u32 data_buf1_sz:16; ++ u32 data_buf2_sz:16; ++ u32 buf_addr_ptr1; ++ u32 buf_addr_ptr2; ++}; ++ ++struct sunxi_mmc_host { ++ unsigned mmc_no; ++ uint32_t *mclkreg; ++ unsigned database; ++ unsigned fatal_err; ++ unsigned mod_clk; ++ struct sunxi_mmc *reg; ++ struct mmc_config cfg; ++}; ++ ++/* support 4 mmc hosts */ ++struct sunxi_mmc_host mmc_host[4]; ++ ++static int mmc_resource_init(int sdc_no) ++{ ++ struct sunxi_mmc_host *mmchost = &mmc_host[sdc_no]; ++ struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; ++ ++ debug("init mmc %d resource\n", sdc_no); ++ ++ switch (sdc_no) { ++ case 0: ++ mmchost->reg = (struct sunxi_mmc *)SUNXI_MMC0_BASE; ++ mmchost->mclkreg = &ccm->sd0_clk_cfg; ++ break; ++ case 1: ++ mmchost->reg = (struct sunxi_mmc *)SUNXI_MMC1_BASE; ++ mmchost->mclkreg = &ccm->sd1_clk_cfg; ++ break; ++ case 2: ++ mmchost->reg = (struct sunxi_mmc *)SUNXI_MMC2_BASE; ++ mmchost->mclkreg = &ccm->sd2_clk_cfg; ++ break; ++ case 3: ++ mmchost->reg = (struct sunxi_mmc *)SUNXI_MMC3_BASE; ++ mmchost->mclkreg = &ccm->sd3_clk_cfg; ++ break; ++ default: ++ printf("Wrong mmc number %d\n", sdc_no); ++ return -1; ++ } ++ mmchost->database = (unsigned int)mmchost->reg + 0x100; ++ mmchost->mmc_no = sdc_no; ++ ++ return 0; ++} ++ ++static int mmc_clk_io_on(int sdc_no) ++{ ++ unsigned int pin; ++ unsigned int rval; ++ unsigned int pll_clk; ++ unsigned int divider; ++ struct sunxi_mmc_host *mmchost = &mmc_host[sdc_no]; ++ struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; ++ ++ debug("init mmc %d clock and io\n", sdc_no); ++ ++ /* config gpio */ ++ switch (sdc_no) { ++ case 0: ++ /* D1-PF0, D0-PF1, CLK-PF2, CMD-PF3, D3-PF4, D4-PF5 */ ++ for (pin = SUNXI_GPF(0); pin <= SUNXI_GPF(5); pin++) { ++ sunxi_gpio_set_cfgpin(pin, SUNXI_GPF0_SDC0); ++ sunxi_gpio_set_pull(pin, SUNXI_GPIO_PULL_UP); ++ sunxi_gpio_set_drv(pin, 2); ++ } ++ break; ++ ++ case 1: ++#if CONFIG_MMC1_PG ++ /* PG0-CMD, PG1-CLK, PG2~5-D0~3 : 4 */ ++ for (pin = SUNXI_GPG(0); pin <= SUNXI_GPG(5); pin++) { ++ sunxi_gpio_set_cfgpin(pin, SUN4I_GPG0_SDC1); ++ sunxi_gpio_set_pull(pin, SUNXI_GPIO_PULL_UP); ++ sunxi_gpio_set_drv(pin, 2); ++ } ++#else ++ /* PH22-CMD, PH23-CLK, PH24~27-D0~D3 : 5 */ ++ for (pin = SUNXI_GPH(22); pin <= SUNXI_GPH(27); pin++) { ++ sunxi_gpio_set_cfgpin(pin, SUN4I_GPH22_SDC1); ++ sunxi_gpio_set_pull(pin, SUNXI_GPIO_PULL_UP); ++ sunxi_gpio_set_drv(pin, 2); ++ } ++#endif ++ break; ++ ++ case 2: ++ /* CMD-PC6, CLK-PC7, D0-PC8, D1-PC9, D2-PC10, D3-PC11 */ ++ for (pin = SUNXI_GPC(6); pin <= SUNXI_GPC(11); pin++) { ++ sunxi_gpio_set_cfgpin(pin, SUNXI_GPC6_SDC2); ++ sunxi_gpio_set_pull(pin, SUNXI_GPIO_PULL_UP); ++ sunxi_gpio_set_drv(pin, 2); ++ } ++ break; ++ ++ case 3: ++ /* PI4-CMD, PI5-CLK, PI6~9-D0~D3 : 2 */ ++ for (pin = SUNXI_GPI(4); pin <= SUNXI_GPI(9); pin++) { ++ sunxi_gpio_set_cfgpin(pin, SUN4I_GPI4_SDC3); ++ sunxi_gpio_set_pull(pin, SUNXI_GPIO_PULL_UP); ++ sunxi_gpio_set_drv(pin, 2); ++ } ++ break; ++ ++ default: ++ return -1; ++ } ++ ++ /* config ahb clock */ ++ rval = readl(&ccm->ahb_gate0); ++ rval |= 1 << AHB_GATE_OFFSET_MMC(sdc_no); ++ writel(rval, &ccm->ahb_gate0); ++ ++ /* config mod clock */ ++ pll_clk = clock_get_pll6(); ++ /* should be close to 100 MHz but no more, so round up */ ++ divider = ((pll_clk + 99999999) / 100000000) - 1; ++ writel(CCM_MMC_CTRL_ENABLE | CCM_MMC_CTRL_PLL6 | divider, ++ mmchost->mclkreg); ++ mmchost->mod_clk = pll_clk / (divider + 1); ++ ++ return 0; ++} ++ ++static int mmc_update_clk(struct mmc *mmc) ++{ ++ struct sunxi_mmc_host *mmchost = mmc->priv; ++ unsigned int cmd; ++ unsigned timeout_msecs = 2000; ++ ++ cmd = SUNXI_MMC_CMD_START | ++ SUNXI_MMC_CMD_UPCLK_ONLY | ++ SUNXI_MMC_CMD_WAIT_PRE_OVER; ++ writel(cmd, &mmchost->reg->cmd); ++ while (readl(&mmchost->reg->cmd) & SUNXI_MMC_CMD_START) { ++ if (!timeout_msecs--) ++ return -1; ++ udelay(1000); ++ } ++ ++ /* clock update sets various irq status bits, clear these */ ++ writel(readl(&mmchost->reg->rint), &mmchost->reg->rint); ++ ++ return 0; ++} ++ ++static int mmc_config_clock(struct mmc *mmc, unsigned div) ++{ ++ struct sunxi_mmc_host *mmchost = mmc->priv; ++ unsigned rval = readl(&mmchost->reg->clkcr); ++ ++ /* Disable Clock */ ++ rval &= ~SUNXI_MMC_CLK_ENABLE; ++ writel(rval, &mmchost->reg->clkcr); ++ if (mmc_update_clk(mmc)) ++ return -1; ++ ++ /* Change Divider Factor */ ++ rval &= ~SUNXI_MMC_CLK_DIVIDER_MASK; ++ rval |= div; ++ writel(rval, &mmchost->reg->clkcr); ++ if (mmc_update_clk(mmc)) ++ return -1; ++ /* Re-enable Clock */ ++ rval |= SUNXI_MMC_CLK_ENABLE; ++ writel(rval, &mmchost->reg->clkcr); ++ ++ if (mmc_update_clk(mmc)) ++ return -1; ++ ++ return 0; ++} ++ ++static void mmc_set_ios(struct mmc *mmc) ++{ ++ struct sunxi_mmc_host *mmchost = mmc->priv; ++ unsigned int clkdiv = 0; ++ ++ debug("set ios: bus_width: %x, clock: %d, mod_clk: %d\n", ++ mmc->bus_width, mmc->clock, mmchost->mod_clk); ++ ++ /* Change clock first */ ++ clkdiv = (mmchost->mod_clk + (mmc->clock >> 1)) / mmc->clock / 2; ++ if (mmc->clock) { ++ if (mmc_config_clock(mmc, clkdiv)) { ++ mmchost->fatal_err = 1; ++ return; ++ } ++ } ++ ++ /* Change bus width */ ++ if (mmc->bus_width == 8) ++ writel(0x2, &mmchost->reg->width); ++ else if (mmc->bus_width == 4) ++ writel(0x1, &mmchost->reg->width); ++ else ++ writel(0x0, &mmchost->reg->width); ++} ++ ++static int mmc_core_init(struct mmc *mmc) ++{ ++ struct sunxi_mmc_host *mmchost = mmc->priv; ++ ++ /* Reset controller */ ++ writel(SUNXI_MMC_GCTRL_RESET, &mmchost->reg->gctrl); ++ ++ return 0; ++} ++ ++static int mmc_trans_data_by_cpu(struct mmc *mmc, struct mmc_data *data) ++{ ++ struct sunxi_mmc_host *mmchost = mmc->priv; ++ const int reading = !!(data->flags & MMC_DATA_READ); ++ const uint32_t status_bit = reading ? SUNXI_MMC_STATUS_FIFO_EMPTY : ++ SUNXI_MMC_STATUS_FIFO_FULL; ++ unsigned i; ++ unsigned byte_cnt = data->blocksize * data->blocks; ++ unsigned timeout_msecs = 2000; ++ unsigned *buff = (unsigned int *)(reading ? data->dest : data->src); ++ ++ for (i = 0; i < (byte_cnt >> 2); i++) { ++ while (readl(&mmchost->reg->status) & status_bit) { ++ if (!timeout_msecs--) ++ return -1; ++ udelay(1000); ++ } ++ ++ if (reading) ++ buff[i] = readl(mmchost->database); ++ else ++ writel(buff[i], mmchost->database); ++ } ++ ++ return 0; ++} ++ ++static int mmc_trans_data_by_dma(struct mmc *mmc, struct mmc_data *data) ++{ ++ struct sunxi_mmc_host *mmchost = mmc->priv; ++ unsigned byte_cnt = data->blocksize * data->blocks; ++ unsigned char *buff; ++ unsigned des_idx = 0; ++ unsigned buff_frag_num = ++ (byte_cnt + SDXC_DES_BUFFER_MAX_LEN - 1) >> SDXC_DES_NUM_SHIFT; ++ unsigned remain; ++ unsigned i, rval; ++ ALLOC_CACHE_ALIGN_BUFFER(struct sunxi_mmc_des, pdes, buff_frag_num); ++ ++ buff = data->flags & MMC_DATA_READ ? ++ (unsigned char *)data->dest : (unsigned char *)data->src; ++ remain = byte_cnt & (SDXC_DES_BUFFER_MAX_LEN - 1); ++ ++ flush_cache((unsigned long)buff, (unsigned long)byte_cnt); ++ for (i = 0; i < buff_frag_num; i++, des_idx++) { ++ memset((void *)&pdes[des_idx], 0, sizeof(struct sunxi_mmc_des)); ++ pdes[des_idx].des_chain = 1; ++ pdes[des_idx].own = 1; ++ pdes[des_idx].dic = 1; ++ if (buff_frag_num > 1 && i != buff_frag_num - 1) ++ pdes[des_idx].data_buf1_sz = 0; /* 0 == max_len */ ++ else ++ pdes[des_idx].data_buf1_sz = remain; ++ ++ pdes[des_idx].buf_addr_ptr1 = ++ (u32) buff + i * SDXC_DES_BUFFER_MAX_LEN; ++ if (i == 0) ++ pdes[des_idx].first_des = 1; ++ ++ if (i == buff_frag_num - 1) { ++ pdes[des_idx].dic = 0; ++ pdes[des_idx].last_des = 1; ++ pdes[des_idx].end_of_ring = 1; ++ pdes[des_idx].buf_addr_ptr2 = 0; ++ } else { ++ pdes[des_idx].buf_addr_ptr2 = (u32)&pdes[des_idx + 1]; ++ } ++ } ++ flush_cache((unsigned long)pdes, ++ sizeof(struct sunxi_mmc_des) * (des_idx + 1)); ++ ++ rval = readl(&mmchost->reg->gctrl); ++ /* Enable DMA */ ++ writel(rval | SUNXI_MMC_GCTRL_DMA_RESET | SUNXI_MMC_GCTRL_DMA_ENABLE, ++ &mmchost->reg->gctrl); ++ /* Reset iDMA */ ++ writel(SUNXI_MMC_IDMAC_RESET, &mmchost->reg->dmac); ++ /* Enable iDMA */ ++ writel(SUNXI_MMC_IDMAC_FIXBURST | SUNXI_MMC_IDMAC_ENABLE, ++ &mmchost->reg->dmac); ++ rval = readl(&mmchost->reg->idie) & ++ ~(SUNXI_MMC_IDIE_TXIRQ|SUNXI_MMC_IDIE_RXIRQ); ++ if (data->flags & MMC_DATA_WRITE) ++ rval |= SUNXI_MMC_IDIE_TXIRQ; ++ else ++ rval |= SUNXI_MMC_IDIE_RXIRQ; ++ writel(rval, &mmchost->reg->idie); ++ writel((u32) pdes, &mmchost->reg->dlba); ++ writel((0x2 << 28) | (0x7 << 16) | (0x01 << 3), ++ &mmchost->reg->ftrglevel); ++ ++ return 0; ++} ++ ++static void mmc_enable_dma_accesses(struct mmc *mmc, int dma) ++{ ++ struct sunxi_mmc_host *mmchost = mmc->priv; ++ ++ unsigned int gctrl = readl(&mmchost->reg->gctrl); ++ if (dma) ++ gctrl &= ~SUNXI_MMC_GCTRL_ACCESS_BY_AHB; ++ else ++ gctrl |= SUNXI_MMC_GCTRL_ACCESS_BY_AHB; ++ writel(gctrl, &mmchost->reg->gctrl); ++} ++ ++static int mmc_rint_wait(struct mmc *mmc, unsigned int timeout_msecs, ++ unsigned int done_bit, const char *what) ++{ ++ struct sunxi_mmc_host *mmchost = mmc->priv; ++ unsigned int status; ++ ++ do { ++ status = readl(&mmchost->reg->rint); ++ if (!timeout_msecs-- || ++ (status & SUNXI_MMC_RINT_INTERRUPT_ERROR_BIT)) { ++ debug("%s timeout %x\n", what, ++ status & SUNXI_MMC_RINT_INTERRUPT_ERROR_BIT); ++ return TIMEOUT; ++ } ++ udelay(1000); ++ } while (!(status & done_bit)); ++ ++ return 0; ++} ++ ++static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, ++ struct mmc_data *data) ++{ ++ struct sunxi_mmc_host *mmchost = mmc->priv; ++ unsigned int cmdval = SUNXI_MMC_CMD_START; ++ unsigned int timeout_msecs; ++ int error = 0; ++ unsigned int status = 0; ++ unsigned int usedma = 0; ++ unsigned int bytecnt = 0; ++ ++ if (mmchost->fatal_err) ++ return -1; ++ if (cmd->resp_type & MMC_RSP_BUSY) ++ debug("mmc cmd %d check rsp busy\n", cmd->cmdidx); ++ if (cmd->cmdidx == 12) ++ return 0; ++ ++ if (!cmd->cmdidx) ++ cmdval |= SUNXI_MMC_CMD_SEND_INIT_SEQ; ++ if (cmd->resp_type & MMC_RSP_PRESENT) ++ cmdval |= SUNXI_MMC_CMD_RESP_EXPIRE; ++ if (cmd->resp_type & MMC_RSP_136) ++ cmdval |= SUNXI_MMC_CMD_LONG_RESPONSE; ++ if (cmd->resp_type & MMC_RSP_CRC) ++ cmdval |= SUNXI_MMC_CMD_CHK_RESPONSE_CRC; ++ ++ if (data) { ++ if ((u32) data->dest & 0x3) { ++ error = -1; ++ goto out; ++ } ++ ++ cmdval |= SUNXI_MMC_CMD_DATA_EXPIRE|SUNXI_MMC_CMD_WAIT_PRE_OVER; ++ if (data->flags & MMC_DATA_WRITE) ++ cmdval |= SUNXI_MMC_CMD_WRITE; ++ if (data->blocks > 1) ++ cmdval |= SUNXI_MMC_CMD_AUTO_STOP; ++ writel(data->blocksize, &mmchost->reg->blksz); ++ writel(data->blocks * data->blocksize, &mmchost->reg->bytecnt); ++ } ++ ++ debug("mmc %d, cmd %d(0x%08x), arg 0x%08x\n", mmchost->mmc_no, ++ cmd->cmdidx, cmdval | cmd->cmdidx, cmd->cmdarg); ++ writel(cmd->cmdarg, &mmchost->reg->arg); ++ ++ if (!data) ++ writel(cmdval | cmd->cmdidx, &mmchost->reg->cmd); ++ ++ /* ++ * transfer data and check status ++ * STATREG[2] : FIFO empty ++ * STATREG[3] : FIFO full ++ */ ++ if (data) { ++ int ret = 0; ++ ++ bytecnt = data->blocksize * data->blocks; ++ debug("trans data %d bytes\n", bytecnt); ++#if defined(CONFIG_MMC_SUNXI_USE_DMA) && !defined(CONFIG_SPL_BUILD) ++ if (bytecnt > 64) { ++#else ++ if (0) { ++#endif ++ usedma = 1; ++ mmc_enable_dma_accesses(mmc, 1); ++ ret = mmc_trans_data_by_dma(mmc, data); ++ writel(cmdval | cmd->cmdidx, &mmchost->reg->cmd); ++ } else { ++ mmc_enable_dma_accesses(mmc, 0); ++ writel(cmdval | cmd->cmdidx, &mmchost->reg->cmd); ++ ret = mmc_trans_data_by_cpu(mmc, data); ++ } ++ if (ret) { ++ error = readl(&mmchost->reg->rint) & \ ++ SUNXI_MMC_RINT_INTERRUPT_ERROR_BIT; ++ error = TIMEOUT; ++ goto out; ++ } ++ } ++ ++ error = mmc_rint_wait(mmc, 0xfffff, SUNXI_MMC_RINT_COMMAND_DONE, "cmd"); ++ if (error) ++ goto out; ++ ++ if (data) { ++ timeout_msecs = usedma ? 120 * bytecnt : 120; ++ debug("cacl timeout %x msec\n", timeout_msecs); ++ error = mmc_rint_wait(mmc, timeout_msecs, ++ data->blocks > 1 ? ++ SUNXI_MMC_RINT_AUTO_COMMAND_DONE : ++ SUNXI_MMC_RINT_DATA_OVER, ++ "data"); ++ if (error) ++ goto out; ++ } ++ ++ if (cmd->resp_type & MMC_RSP_BUSY) { ++ timeout_msecs = 2000; ++ do { ++ status = readl(&mmchost->reg->status); ++ if (!timeout_msecs--) { ++ debug("busy timeout\n"); ++ error = TIMEOUT; ++ goto out; ++ } ++ udelay(1000); ++ } while (status & SUNXI_MMC_STATUS_CARD_DATA_BUSY); ++ } ++ ++ if (cmd->resp_type & MMC_RSP_136) { ++ cmd->response[0] = readl(&mmchost->reg->resp3); ++ cmd->response[1] = readl(&mmchost->reg->resp2); ++ cmd->response[2] = readl(&mmchost->reg->resp1); ++ cmd->response[3] = readl(&mmchost->reg->resp0); ++ debug("mmc resp 0x%08x 0x%08x 0x%08x 0x%08x\n", ++ cmd->response[3], cmd->response[2], ++ cmd->response[1], cmd->response[0]); ++ } else { ++ cmd->response[0] = readl(&mmchost->reg->resp0); ++ debug("mmc resp 0x%08x\n", cmd->response[0]); ++ } ++out: ++ if (data && usedma) { ++ /* IDMASTAREG ++ * IDST[0] : idma tx int ++ * IDST[1] : idma rx int ++ * IDST[2] : idma fatal bus error ++ * IDST[4] : idma descriptor invalid ++ * IDST[5] : idma error summary ++ * IDST[8] : idma normal interrupt sumary ++ * IDST[9] : idma abnormal interrupt sumary ++ */ ++ status = readl(&mmchost->reg->idst); ++ writel(status, &mmchost->reg->idst); ++ writel(0, &mmchost->reg->idie); ++ writel(0, &mmchost->reg->dmac); ++ writel(readl(&mmchost->reg->gctrl) & ~SUNXI_MMC_GCTRL_DMA_ENABLE, ++ &mmchost->reg->gctrl); ++ } ++ if (error < 0) { ++ writel(SUNXI_MMC_GCTRL_RESET, &mmchost->reg->gctrl); ++ mmc_update_clk(mmc); ++ } ++ writel(0xffffffff, &mmchost->reg->rint); ++ writel(readl(&mmchost->reg->gctrl) | SUNXI_MMC_GCTRL_FIFO_RESET, ++ &mmchost->reg->gctrl); ++ ++ return error; ++} ++ ++static const struct mmc_ops sunxi_mmc_ops = { ++ .send_cmd = mmc_send_cmd, ++ .set_ios = mmc_set_ios, ++ .init = mmc_core_init, ++}; ++ ++int sunxi_mmc_init(int sdc_no) ++{ ++ struct mmc_config *cfg = &mmc_host[sdc_no].cfg; ++ ++ memset(&mmc_host[sdc_no], 0, sizeof(struct sunxi_mmc_host)); ++ ++ cfg->name = "SUNXI SD/MMC"; ++ cfg->ops = &sunxi_mmc_ops; ++ ++ cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; ++ cfg->host_caps = MMC_MODE_4BIT; ++ cfg->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; ++ cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; ++ ++ cfg->f_min = 400000; ++ cfg->f_max = 52000000; ++ ++ mmc_resource_init(sdc_no); ++ mmc_clk_io_on(sdc_no); ++ ++ if (mmc_create(cfg, &mmc_host[sdc_no]) == NULL) ++ return -1; ++ ++ return 0; ++} +diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h +index b76c3b0..ede3d50 100644 +--- a/include/configs/sunxi-common.h ++++ b/include/configs/sunxi-common.h +@@ -59,6 +59,16 @@ + #define CONFIG_CMDLINE_TAG + #define CONFIG_INITRD_TAG + ++/* mmc config */ ++#define CONFIG_MMC ++#define CONFIG_GENERIC_MMC ++#define CONFIG_CMD_MMC ++#define CONFIG_MMC_SUNXI ++#define CONFIG_MMC_SUNXI_SLOT 0 ++#define CONFIG_MMC_SUNXI_USE_DMA ++#define CONFIG_ENV_IS_IN_MMC ++#define CONFIG_SYS_MMC_ENV_DEV 0 /* first detected MMC controller */ ++ + /* 4MB of malloc() pool */ + #define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + (4 << 20)) + +@@ -94,6 +104,7 @@ + #define CONFIG_SYS_MONITOR_LEN (512 << 10) /* 512 KiB */ + #define CONFIG_IDENT_STRING " Allwinner Technology" + ++#define CONFIG_ENV_OFFSET (544 << 10) /* (8 + 24 + 512) KiB */ + #define CONFIG_ENV_SIZE (128 << 10) /* 128 KiB */ + + #define CONFIG_EXTRA_ENV_SETTINGS \ +-- +1.9.0 + |