Lee Leahy has uploaded a new change for review. ( https://review.coreboot.org/19015 )
Change subject: drivers/storage: Split out ADMA support ......................................................................
drivers/storage: Split out ADMA support
On x86 sysems DMA is only supported to RAM, early stages must do programed I/O to send/receive data from the SD/MMC device. Use Kconfig values to enable DMA during early boot stages.
TEST=Build and run on reef
Change-Id: I70aad97c74aa8a72e50627740d96fecb7485dde4 Signed-off-by: Lee Leahy Leroy.P.Leahy@intel.com --- M src/drivers/storage/Kconfig M src/drivers/storage/Makefile.inc M src/drivers/storage/sdhci.c A src/drivers/storage/sdhci_adma.c M src/include/device/sdhci.h 5 files changed, 239 insertions(+), 178 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/15/19015/1
diff --git a/src/drivers/storage/Kconfig b/src/drivers/storage/Kconfig index 5774843..03e8bce 100644 --- a/src/drivers/storage/Kconfig +++ b/src/drivers/storage/Kconfig @@ -31,6 +31,24 @@ help Display MMC commands and responses
+config SDHCI_ADMA_IN_BOOTBLOCK + bool + default n + help + Determine if bootblock is able to use ADMA2 or ADMA64 + +config SDHCI_ADMA_IN_ROMSTAGE + bool + default n + help + Determine if romstage is able to use ADMA2 or ADMA64 + +config SDHCI_ADMA_IN_VERSTAGE + bool + default n + help + Determine if verstage is able to use ADMA2 or ADMA64 + config SDHCI_DEBUG bool "Debug SD/MMC host controller" default n diff --git a/src/drivers/storage/Makefile.inc b/src/drivers/storage/Makefile.inc index 460d4f8..47dfb4f 100644 --- a/src/drivers/storage/Makefile.inc +++ b/src/drivers/storage/Makefile.inc @@ -26,13 +26,11 @@ romstage-y += mmc.c romstage-y += pci_sdhci.c romstage-y += sdhci.c - -postcar-y += mmc.c -postcar-y += pci_sdhci.c -postcar-y += sdhci.c +romstage-$(CONFIG_SDHCI_ADMA_IN_ROMSTAGE) += sdhci_adma.c
ramstage-y += mmc.c ramstage-y += pci_sdhci.c ramstage-y += sdhci.c +ramstage-y += sdhci_adma.c
endif # CONFIG_DRIVERS_STORAGE diff --git a/src/drivers/storage/sdhci.c b/src/drivers/storage/sdhci.c index f4fe256..cccae17 100644 --- a/src/drivers/storage/sdhci.c +++ b/src/drivers/storage/sdhci.c @@ -30,7 +30,12 @@ #include <string.h> #include <timer.h>
-static void sdhci_reset(SdhciHost *host, u8 mask) +#define DMA_AVAILABLE ((CONFIG_SDHCI_ADMA_IN_BOOTBLOCK && ENV_BOOTBLOCK) \ + || (CONFIG_SDHCI_ADMA_IN_VERSTAGE && ENV_VERSTAGE) \ + || (CONFIG_SDHCI_ADMA_IN_ROMSTAGE && ENV_ROMSTAGE) \ + || ENV_POSTCAR || ENV_RAMSTAGE) + +void sdhci_reset(SdhciHost *host, u8 mask) { unsigned long timeout; struct mono_time current, end; @@ -54,7 +59,7 @@ } }
-static void sdhci_cmd_done(SdhciHost *host, MmcCommand *cmd) +void sdhci_cmd_done(SdhciHost *host, MmcCommand *cmd) { int i; if (cmd->resp_type & MMC_RSP_136) { @@ -115,165 +120,6 @@ } } while (!(stat & SDHCI_INT_DATA_END)); return 0; -} - -static void sdhci_alloc_adma_descs(SdhciHost *host, u32 need_descriptors) -{ - if (host->adma_descs) { - if (host->adma_desc_count < need_descriptors) { - /* Previously allocated array is too small */ - free(host->adma_descs); - host->adma_desc_count = 0; - host->adma_descs = NULL; - } - } - - /* use dma_malloc() to make sure we get the coherent/uncached memory */ - if (!host->adma_descs) { - host->adma_descs = malloc(need_descriptors * - sizeof(*host->adma_descs)); - if (host->adma_descs == NULL) - die("fail to malloc adma_descs\n"); - host->adma_desc_count = need_descriptors; - } - - memset(host->adma_descs, 0, sizeof(*host->adma_descs) * - need_descriptors); -} - -static void sdhci_alloc_adma64_descs(SdhciHost *host, u32 need_descriptors) -{ - if (host->adma64_descs) { - if (host->adma_desc_count < need_descriptors) { - /* Previously allocated array is too small */ - free(host->adma64_descs); - host->adma_desc_count = 0; - host->adma64_descs = NULL; - } - } - - /* use dma_malloc() to make sure we get the coherent/uncached memory */ - if (!host->adma64_descs) { - host->adma64_descs = malloc(need_descriptors * - sizeof(*host->adma64_descs)); - if (host->adma64_descs == NULL) - die("fail to malloc adma64_descs\n"); - - host->adma_desc_count = need_descriptors; - } - - memset(host->adma64_descs, 0, sizeof(*host->adma64_descs) * - need_descriptors); -} - -static int sdhci_setup_adma(SdhciHost *host, MmcData *data) -{ - int i, togo, need_descriptors; - char *buffer_data; - u16 attributes; - - togo = data->blocks * data->blocksize; - if (!togo) { - printf("%s: MmcData corrupted: %d blocks of %d bytes\n", - __func__, data->blocks, data->blocksize); - return -1; - } - - need_descriptors = 1 + togo / SDHCI_MAX_PER_DESCRIPTOR; - - if (host->dma64) - sdhci_alloc_adma64_descs(host, need_descriptors); - else - sdhci_alloc_adma_descs(host, need_descriptors); - buffer_data = data->dest; - - /* Now set up the descriptor chain. */ - for (i = 0; togo; i++) { - unsigned int desc_length; - - if (togo < SDHCI_MAX_PER_DESCRIPTOR) - desc_length = togo; - else - desc_length = SDHCI_MAX_PER_DESCRIPTOR; - togo -= desc_length; - - attributes = SDHCI_ADMA_VALID | SDHCI_ACT_TRAN; - if (togo == 0) - attributes |= SDHCI_ADMA_END; - - if (host->dma64) { - host->adma64_descs[i].addr = (uintptr_t) buffer_data; - host->adma64_descs[i].addr_hi = 0; - host->adma64_descs[i].length = desc_length; - host->adma64_descs[i].attributes = attributes; - - } else { - host->adma_descs[i].addr = (uintptr_t) buffer_data; - host->adma_descs[i].length = desc_length; - host->adma_descs[i].attributes = attributes; - } - - buffer_data += desc_length; - } - - if (host->dma64) - sdhci_writel(host, (uintptr_t) host->adma64_descs, - SDHCI_ADMA_ADDRESS); - else - sdhci_writel(host, (uintptr_t) host->adma_descs, - SDHCI_ADMA_ADDRESS); - - return 0; -} - -static int sdhci_complete_adma(SdhciHost *host, MmcCommand *cmd) -{ - int retry; - u32 stat = 0, mask; - - mask = SDHCI_INT_RESPONSE | SDHCI_INT_ERROR; - - retry = 10000; /* Command should be done in way less than 10 ms. */ - while (--retry) { - stat = sdhci_readl(host, SDHCI_INT_STATUS); - if (stat & mask) - break; - udelay(1); - } - - sdhci_writel(host, SDHCI_INT_RESPONSE, SDHCI_INT_STATUS); - - if (retry && !(stat & SDHCI_INT_ERROR)) { - /* Command OK, let's wait for data transfer completion. */ - mask = SDHCI_INT_DATA_END | - SDHCI_INT_ERROR | SDHCI_INT_ADMA_ERROR; - - /* Transfer should take 10 seconds tops. */ - retry = 10 * 1000 * 1000; - while (--retry) { - stat = sdhci_readl(host, SDHCI_INT_STATUS); - if (stat & mask) - break; - udelay(1); - } - - sdhci_writel(host, stat, SDHCI_INT_STATUS); - if (retry && !(stat & SDHCI_INT_ERROR)) { - sdhci_cmd_done(host, cmd); - return 0; - } - } - - printf("%s: transfer error, stat %#x, adma error %#x, retry %d\n", - __func__, stat, sdhci_readl(host, SDHCI_ADMA_ERROR), retry); - - sdhci_reset(host, SDHCI_RESET_CMD); - sdhci_reset(host, SDHCI_RESET_DATA); - - if (stat & SDHCI_INT_TIMEOUT) - return MMC_TIMEOUT; - else - return MMC_COMM_ERR; }
static int sdhci_send_command(MmcCtrlr *mmc_ctrl, MmcCommand *cmd, @@ -343,11 +189,12 @@
sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT);
- if (host->host_caps & MMC_AUTO_CMD12) { - if (sdhci_setup_adma(host, data)) - return -1; - - mode |= SDHCI_TRNS_DMA; + if (DMA_AVAILABLE) { + if (host->host_caps & MMC_AUTO_CMD12) { + if (sdhci_setup_adma(host, data)) + return -1; + mode |= SDHCI_TRNS_DMA; + } } sdhci_writew(host, mode, SDHCI_TRANSFER_MODE); } @@ -355,8 +202,10 @@ sdhci_writel(host, cmd->cmdarg, SDHCI_ARGUMENT); sdhci_writew(host, SDHCI_MAKE_CMD(cmd->cmdidx, flags), SDHCI_COMMAND);
- if (data && (host->host_caps & MMC_AUTO_CMD12)) - return sdhci_complete_adma(host, cmd); + if (DMA_AVAILABLE) { + if (data && (host->host_caps & MMC_AUTO_CMD12)) + return sdhci_complete_adma(host, cmd); + }
timer_monotonic_get(¤t); end = current; @@ -619,12 +468,14 @@
sdhci_set_uhs_signaling(host, mmc_ctrlr->timing);
- if (host->host_caps & MMC_AUTO_CMD12) { - ctrl &= ~SDHCI_CTRL_DMA_MASK; - if (host->dma64) - ctrl |= SDHCI_CTRL_ADMA64; - else - ctrl |= SDHCI_CTRL_ADMA32; + if (DMA_AVAILABLE) { + if (host->host_caps & MMC_AUTO_CMD12) { + ctrl &= ~SDHCI_CTRL_DMA_MASK; + if (host->dma64) + ctrl |= SDHCI_CTRL_ADMA64; + else + ctrl |= SDHCI_CTRL_ADMA32; + } }
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); diff --git a/src/drivers/storage/sdhci_adma.c b/src/drivers/storage/sdhci_adma.c new file mode 100644 index 0000000..61ab5c3 --- /dev/null +++ b/src/drivers/storage/sdhci_adma.c @@ -0,0 +1,190 @@ +/* + * Copyright 2011, Marvell Semiconductor Inc. + * Lei Wen leiwen@marvell.com + * + * Copyrigit 2017 Intel Corporation + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Back ported to the 8xx platform (from the 8260 platform) by + * Murray.Jensen@cmst.csiro.au, 27-Jan-01. + */ + +#include <assert.h> +#include <delay.h> +#include <device/mmc.h> +#include <device/sdhci.h> +#include <endian.h> +#include "storage.h" +#include <string.h> +#include <timer.h> + +static void sdhci_alloc_adma_descs(SdhciHost *host, u32 need_descriptors) +{ + if (host->adma_descs) { + if (host->adma_desc_count < need_descriptors) { + /* Previously allocated array is too small */ + free(host->adma_descs); + host->adma_desc_count = 0; + host->adma_descs = NULL; + } + } + + /* use dma_malloc() to make sure we get the coherent/uncached memory */ + if (!host->adma_descs) { + host->adma_descs = malloc(need_descriptors * + sizeof(*host->adma_descs)); + if (host->adma_descs == NULL) + die("fail to malloc adma_descs\n"); + host->adma_desc_count = need_descriptors; + } + + memset(host->adma_descs, 0, sizeof(*host->adma_descs) * + need_descriptors); +} + +static void sdhci_alloc_adma64_descs(SdhciHost *host, u32 need_descriptors) +{ + if (host->adma64_descs) { + if (host->adma_desc_count < need_descriptors) { + /* Previously allocated array is too small */ + free(host->adma64_descs); + host->adma_desc_count = 0; + host->adma64_descs = NULL; + } + } + + /* use dma_malloc() to make sure we get the coherent/uncached memory */ + if (!host->adma64_descs) { + host->adma64_descs = malloc(need_descriptors * + sizeof(*host->adma64_descs)); + if (host->adma64_descs == NULL) + die("fail to malloc adma64_descs\n"); + + host->adma_desc_count = need_descriptors; + } + + memset(host->adma64_descs, 0, sizeof(*host->adma64_descs) * + need_descriptors); +} + +int sdhci_setup_adma(SdhciHost *host, MmcData *data) +{ + int i, togo, need_descriptors; + char *buffer_data; + u16 attributes; + + togo = data->blocks * data->blocksize; + if (!togo) { + printf("%s: MmcData corrupted: %d blocks of %d bytes\n", + __func__, data->blocks, data->blocksize); + return -1; + } + + need_descriptors = 1 + togo / SDHCI_MAX_PER_DESCRIPTOR; + + if (host->dma64) + sdhci_alloc_adma64_descs(host, need_descriptors); + else + sdhci_alloc_adma_descs(host, need_descriptors); + buffer_data = data->dest; + + /* Now set up the descriptor chain. */ + for (i = 0; togo; i++) { + unsigned int desc_length; + + if (togo < SDHCI_MAX_PER_DESCRIPTOR) + desc_length = togo; + else + desc_length = SDHCI_MAX_PER_DESCRIPTOR; + togo -= desc_length; + + attributes = SDHCI_ADMA_VALID | SDHCI_ACT_TRAN; + if (togo == 0) + attributes |= SDHCI_ADMA_END; + + if (host->dma64) { + host->adma64_descs[i].addr = (uintptr_t) buffer_data; + host->adma64_descs[i].addr_hi = 0; + host->adma64_descs[i].length = desc_length; + host->adma64_descs[i].attributes = attributes; + + } else { + host->adma_descs[i].addr = (uintptr_t) buffer_data; + host->adma_descs[i].length = desc_length; + host->adma_descs[i].attributes = attributes; + } + + buffer_data += desc_length; + } + + if (host->dma64) + sdhci_writel(host, (uintptr_t) host->adma64_descs, + SDHCI_ADMA_ADDRESS); + else + sdhci_writel(host, (uintptr_t) host->adma_descs, + SDHCI_ADMA_ADDRESS); + + return 0; +} + +int sdhci_complete_adma(SdhciHost *host, MmcCommand *cmd) +{ + int retry; + u32 stat = 0, mask; + + mask = SDHCI_INT_RESPONSE | SDHCI_INT_ERROR; + + retry = 10000; /* Command should be done in way less than 10 ms. */ + while (--retry) { + stat = sdhci_readl(host, SDHCI_INT_STATUS); + if (stat & mask) + break; + udelay(1); + } + + sdhci_writel(host, SDHCI_INT_RESPONSE, SDHCI_INT_STATUS); + + if (retry && !(stat & SDHCI_INT_ERROR)) { + /* Command OK, let's wait for data transfer completion. */ + mask = SDHCI_INT_DATA_END | + SDHCI_INT_ERROR | SDHCI_INT_ADMA_ERROR; + + /* Transfer should take 10 seconds tops. */ + retry = 10 * 1000 * 1000; + while (--retry) { + stat = sdhci_readl(host, SDHCI_INT_STATUS); + if (stat & mask) + break; + udelay(1); + } + + sdhci_writel(host, stat, SDHCI_INT_STATUS); + if (retry && !(stat & SDHCI_INT_ERROR)) { + sdhci_cmd_done(host, cmd); + return 0; + } + } + + printf("%s: transfer error, stat %#x, adma error %#x, retry %d\n", + __func__, stat, sdhci_readl(host, SDHCI_ADMA_ERROR), retry); + + sdhci_reset(host, SDHCI_RESET_CMD); + sdhci_reset(host, SDHCI_RESET_DATA); + + if (stat & SDHCI_INT_TIMEOUT) + return MMC_TIMEOUT; + else + return MMC_COMM_ERR; +} diff --git a/src/include/device/sdhci.h b/src/include/device/sdhci.h index 8976db6..7f59085 100644 --- a/src/include/device/sdhci.h +++ b/src/include/device/sdhci.h @@ -361,6 +361,10 @@ return readb(host->ioaddr + reg); }
+void sdhci_reset(SdhciHost *host, u8 mask); +void sdhci_cmd_done(SdhciHost *host, MmcCommand *cmd); +int sdhci_setup_adma(SdhciHost *host, MmcData *data); +int sdhci_complete_adma(SdhciHost *host, MmcCommand *cmd); int add_sdhci(SdhciHost *host); int sdhci_host_init(SdhciHost *host, void *ioaddr, int platform_info, int clock_min, int clock_max, int clock_base);