From 16bc9bab2ab3b248f44bdf721ec83cdc21bcc32e Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Sat, 1 Apr 2017 20:33:58 -0700 Subject: soc/intel/quark: Add SD/MMC test support The SD/MMC test support consists of: * Add Kconfig value to enable the SD/MMC test support. * Add Kconfig value to enable the logging support. * Add SD/MMC controller init code and read block 0 from each partition. * Add logging code to snapshot the transactions with the SD/MMC device. * Add eMMC driver for ramstage to call test code. * Add romstage code to call test code. * Add bootblock code to call test code. TEST=Build and run on Galileo Gen2 Change-Id: I72785f0dcd466c05c1385cef166731219b583551 Signed-off-by: Lee Leahy Reviewed-on: https://review.coreboot.org/19211 Tested-by: build bot (Jenkins) Reviewed-by: Martin Roth --- src/soc/intel/quark/Kconfig | 18 ++ src/soc/intel/quark/Makefile.inc | 3 + src/soc/intel/quark/include/soc/iomap.h | 3 + src/soc/intel/quark/include/soc/pci_devs.h | 2 + src/soc/intel/quark/include/soc/storage_test.h | 53 +++++ src/soc/intel/quark/romstage/fsp2_0.c | 17 ++ src/soc/intel/quark/sd.c | 50 +++++ src/soc/intel/quark/storage_test.c | 257 +++++++++++++++++++++++++ 8 files changed, 403 insertions(+) create mode 100644 src/soc/intel/quark/include/soc/storage_test.h create mode 100644 src/soc/intel/quark/sd.c create mode 100644 src/soc/intel/quark/storage_test.c (limited to 'src/soc/intel/quark') diff --git a/src/soc/intel/quark/Kconfig b/src/soc/intel/quark/Kconfig index c697b561bd..7f95b7193a 100644 --- a/src/soc/intel/quark/Kconfig +++ b/src/soc/intel/quark/Kconfig @@ -304,4 +304,22 @@ config C_ENV_BOOTBLOCK_SIZE hex default 0x8000 +##### +# Test support +##### + +config STORAGE_TEST + bool "Test SD/MMC/eMMC card or device access" + default n + select DRIVERS_STORAGE + select SDHCI_CONTROLLER + help + Read block 0 from each parition of the storage device. User must + also enable one or both of DRIVERS_STORAGE_SD or DRIVERS_STORAGE_MMC. + +config STORAGE_LOG + bool "Log and display SD/MMC commands" + default n + depends on STORAGE_TEST + endif # SOC_INTEL_QUARK diff --git a/src/soc/intel/quark/Makefile.inc b/src/soc/intel/quark/Makefile.inc index 1d66e6beba..d0a7a932e8 100644 --- a/src/soc/intel/quark/Makefile.inc +++ b/src/soc/intel/quark/Makefile.inc @@ -33,6 +33,7 @@ verstage-$(CONFIG_ENABLE_BUILTIN_HSUART1) += uart_common.c romstage-y += i2c.c romstage-y += memmap.c romstage-y += reg_access.c +romstage-$(CONFIG_STORAGE_TEST) += storage_test.c romstage-y += tsc_freq.c romstage-$(CONFIG_ENABLE_BUILTIN_HSUART1) += uart_common.c romstage-$(CONFIG_PLATFORM_USES_FSP2_0) += reset.c @@ -56,6 +57,8 @@ ramstage-y += memmap.c ramstage-y += northcluster.c ramstage-y += reg_access.c ramstage-$(CONFIG_PLATFORM_USES_FSP2_0) += reset.c +ramstage-y += sd.c +ramstage-$(CONFIG_STORAGE_TEST) += storage_test.c ramstage-y += tsc_freq.c ramstage-$(CONFIG_ENABLE_BUILTIN_HSUART1) += uart_common.c ramstage-$(CONFIG_ENABLE_BUILTIN_HSUART1) += uart.c diff --git a/src/soc/intel/quark/include/soc/iomap.h b/src/soc/intel/quark/include/soc/iomap.h index 1224bcc156..de81a1a030 100644 --- a/src/soc/intel/quark/include/soc/iomap.h +++ b/src/soc/intel/quark/include/soc/iomap.h @@ -28,6 +28,9 @@ #define I2C_BASE_ADDRESS 0xa0020000 #define GPIO_BASE_ADDRESS 0xa0021000 +/* Temporary BAR for SD controller */ +#define SD_BASE_ADDRESS 0xa0022000 + /* * I/O port address space */ diff --git a/src/soc/intel/quark/include/soc/pci_devs.h b/src/soc/intel/quark/include/soc/pci_devs.h index fc28dfe1d1..a4e7a87fd2 100644 --- a/src/soc/intel/quark/include/soc/pci_devs.h +++ b/src/soc/intel/quark/include/soc/pci_devs.h @@ -33,8 +33,10 @@ /* IO Fabric 1 */ #define SIO1_DEV 0x14 +#define SD_MMC_DEV SIO1_DEV #define HSUART0_DEV SIO1_DEV #define HSUART1_DEV SIO1_DEV +#define SD_MMC_FUNC 0 #define HSUART0_FUNC 1 #define USB_DEV_PORT_FUNC 2 #define EHCI_FUNC 3 diff --git a/src/soc/intel/quark/include/soc/storage_test.h b/src/soc/intel/quark/include/soc/storage_test.h new file mode 100644 index 0000000000..6e970ccf3d --- /dev/null +++ b/src/soc/intel/quark/include/soc/storage_test.h @@ -0,0 +1,53 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2017 Intel Corporation + * + * 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; version 2 of the License. + * + * 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. + */ + +#ifndef __STORAGE_TEST_H__ +#define __STORAGE_TEST_H__ + +#include +#include +#include +#include + +#ifdef __SIMPLE_DEVICE__ +#define dev_t uintptr_t +#else +#define dev_t device_t +#endif /* __SIMPLE_DEVICE__ */ + +uint32_t storage_test_init(dev_t dev, uint32_t *previous_bar, + uint16_t *previous_command); +void storage_test(uint32_t bar, int full_initialization); +void storage_test_complete(dev_t dev, uint32_t previous_bar, + uint16_t previous_command); + +/* Logging support */ +struct log_entry { + struct mono_time time; + struct mmc_command cmd; + int cmd_issued; + int ret; + uint32_t response_entries; + uint32_t response[4]; +}; + +#define LOG_ENTRIES 256 + +extern struct log_entry log[LOG_ENTRIES]; +extern uint8_t log_index; +extern int log_full; +extern long log_start_time; + +#endif /* __STORAGE_TEST_H__ */ diff --git a/src/soc/intel/quark/romstage/fsp2_0.c b/src/soc/intel/quark/romstage/fsp2_0.c index d03545d759..10e44c1b68 100644 --- a/src/soc/intel/quark/romstage/fsp2_0.c +++ b/src/soc/intel/quark/romstage/fsp2_0.c @@ -19,10 +19,12 @@ #include "../chip.h" #include #include +#include #include #include #include #include +#include asmlinkage void *car_stage_c_entry(void) { @@ -34,6 +36,21 @@ asmlinkage void *car_stage_c_entry(void) post_code(0x20); console_init(); + if (IS_ENABLED(CONFIG_STORAGE_TEST)) { + uint32_t bar; + dev_t dev; + uint32_t previous_bar; + uint16_t previous_command; + + /* Enable the SD/MMC controller and run the test. Restore + * the BAR and command registers upon completion. + */ + dev = PCI_DEV(0, SD_MMC_DEV, SD_MMC_FUNC); + bar = storage_test_init(dev, &previous_bar, &previous_command); + storage_test(bar, 1); + storage_test_complete(dev, previous_bar, previous_command); + } + /* Initialize DRAM */ s3wake = fill_power_state() == ACPI_S3; fsp_memory_init(s3wake); diff --git a/src/soc/intel/quark/sd.c b/src/soc/intel/quark/sd.c new file mode 100644 index 0000000000..7b9600193c --- /dev/null +++ b/src/soc/intel/quark/sd.c @@ -0,0 +1,50 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2017 Intel Corporation + * + * 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; version 2 of the License. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static void init(struct device *dev) +{ + /* Run the SD test */ + if (IS_ENABLED(CONFIG_STORAGE_TEST)) { + uint32_t bar; + uint32_t previous_bar; + uint16_t previous_command; + + bar = storage_test_init(dev, &previous_bar, &previous_command); + storage_test(bar, 0); + storage_test_complete(dev, previous_bar, previous_command); + } +} + +static const struct device_operations device_ops = { + .read_resources = pci_dev_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .init = init, +}; + +static const struct pci_driver pmc __pci_driver = { + .ops = &device_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .device = 0x08A7, +}; diff --git a/src/soc/intel/quark/storage_test.c b/src/soc/intel/quark/storage_test.c new file mode 100644 index 0000000000..4c83dbed4a --- /dev/null +++ b/src/soc/intel/quark/storage_test.c @@ -0,0 +1,257 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2017 Intel Corporation + * + * 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; version 2 of the License. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if IS_ENABLED(CONFIG_STORAGE_LOG) +struct log_entry log[LOG_ENTRIES] CAR_GLOBAL; +uint8_t log_index CAR_GLOBAL; +int log_full CAR_GLOBAL; +long log_start_time CAR_GLOBAL; +#endif +extern uint8_t _car_drivers_storage_start; +extern uint8_t _car_drivers_storage_end; + +#define STORAGE_DEBUG BIOS_DEBUG +#define LOG_DEBUG (IS_ENABLED(CONFIG_STORAGE_LOG) ? STORAGE_DEBUG : BIOS_NEVER) + +uint32_t storage_test_init(dev_t dev, uint32_t *previous_bar, + uint16_t *previous_command) +{ + uint32_t bar; + + /* Display the vendor/device IDs */ + printk(LOG_DEBUG, "Vendor ID: 0x%04x, Device ID: 0x%04x\n", + pci_read_config16(dev, PCI_VENDOR_ID), + pci_read_config16(dev, PCI_DEVICE_ID)); + + /* Set the temporary base address */ + bar = pci_read_config32(dev, PCI_BASE_ADDRESS_0); + *previous_bar = bar; + bar &= ~PCI_BASE_ADDRESS_MEM_ATTR_MASK; + if (!bar) { + bar = SD_BASE_ADDRESS; + pci_write_config32(dev, PCI_BASE_ADDRESS_0, bar); + } + + /* Enable the SD/MMC controller */ + *previous_command = pci_read_config16(dev, PCI_COMMAND); + pci_write_config16(dev, PCI_COMMAND, *previous_command + | PCI_COMMAND_MEMORY); + + /* Return the controller address */ + return bar; +} + +void storage_test_complete(dev_t dev, uint32_t previous_bar, + uint16_t previous_command) +{ + pci_write_config16(dev, PCI_COMMAND, previous_command); + pci_write_config32(dev, PCI_BASE_ADDRESS_0, previous_bar); +} + +#if !ENV_BOOTBLOCK +static void display_log(void) +{ + /* Determine the array bounds */ + if (IS_ENABLED(CONFIG_STORAGE_LOG)) { + long delta; + uint8_t end; + uint8_t index; + uint8_t start; + + end = log_index; + start = log_full ? log_index : 0; + for (index = start; (log_full || (index != end)); index++) { + log_full = 0; + delta = log[index].time.microseconds - log_start_time; + printk(BIOS_DEBUG, "%3ld.%03ld mSec, cmd: %2d 0x%08x%s", + delta / 1000, delta % 1000, + log[index].cmd.cmdidx, + log[index].cmd.cmdarg, + log[index].cmd_issued ? "" : "(not issued)"); + if (log[index].response_entries == 1) + printk(BIOS_DEBUG, ", rsp: 0x%08x", + log[index].response[0]); + else if (log[index].response_entries == 4) + printk(BIOS_DEBUG, + ", rsp: 0x%08x.%08x.%08x.%08x", + log[index].response[3], + log[index].response[2], + log[index].response[1], + log[index].response[0]); + printk(BIOS_DEBUG, ", ret: %d\n", log[index].ret); + } + } +} + +void sdhc_log_command(struct mmc_command *cmd) +{ + if (IS_ENABLED(CONFIG_STORAGE_LOG)) { + timer_monotonic_get(&log[log_index].time); + log[log_index].cmd = *cmd; + log[log_index].cmd_issued = 0; + log[log_index].response_entries = 0; + if ((log_index == 0) && (!log_full)) + log_start_time = log[0].time.microseconds; + } +} + +void sdhc_log_command_issued(void) +{ + if (IS_ENABLED(CONFIG_STORAGE_LOG)) { + log[log_index].cmd_issued = 1; + } +} + +void sdhc_log_response(uint32_t entries, uint32_t *response) +{ + unsigned int entry; + + if (IS_ENABLED(CONFIG_STORAGE_LOG)) { + log[log_index].response_entries = entries; + for (entry = 0; entry < entries; entry++) + log[log_index].response[entry] = response[entry]; + } +} + +void sdhc_log_ret(int ret) +{ + if (IS_ENABLED(CONFIG_STORAGE_LOG)) { + log[log_index].ret = ret; + if (++log_index == 0) + log_full = 1; + } +} + +void storage_test(uint32_t bar, int full_initialization) +{ + uint64_t blocks_read; + uint8_t buffer[512]; + int err; + struct storage_media *media; + const char *name; + unsigned int partition; + unsigned int previous_partition; + struct sdhci_ctrlr *sdhci_ctrlr; + + /* Get the structure addresses */ + media = NULL; + if (ENV_ROMSTAGE) + media = car_get_var_ptr(&_car_drivers_storage_start); + else + media = cbmem_find(CBMEM_ID_STORAGE_DATA); + sdhci_ctrlr = (void *)(((uintptr_t)(media + 1) + 0x7) & ~7); + if (ENV_ROMSTAGE) + ASSERT((struct sdhci_ctrlr *)&_car_drivers_storage_end + >= (sdhci_ctrlr + 1)); + media->ctrlr = (struct sd_mmc_ctrlr *)sdhci_ctrlr; + sdhci_ctrlr->ioaddr = (void *)bar; + + /* Initialize the controller */ + if (!full_initialization) { + /* Perform fast initialization */ + sdhci_update_pointers(sdhci_ctrlr); + sdhci_display_setup(sdhci_ctrlr); + storage_display_setup(media); + } else { + /* Initialize the log */ + if (IS_ENABLED(CONFIG_STORAGE_LOG)) { + log_index = 0; + log_full = 0; + } + + printk(LOG_DEBUG, "Initializing the SD/MMC controller\n"); + err = sdhci_controller_init(sdhci_ctrlr, (void *)bar); + if (err) { + display_log(); + printk(BIOS_ERR, + "ERROR - Controller failed to initialize, err = %d\n", + err); + return; + } + + /* Initialize the SD/MMC/eMMC card or device */ + printk(LOG_DEBUG, "Initializing the device\n"); + err = storage_setup_media(media, &sdhci_ctrlr->sd_mmc_ctrlr); + if (err) { + display_log(); + printk(BIOS_ERR, + "ERROR: Device failed to initialize, err = %d\n", + err); + return; + } + display_log(); + } + + /* Save the current partition */ + previous_partition = storage_get_current_partition(media); + + /* Read block 0 from each partition */ + for (partition = 0; partition < ARRAY_SIZE(media->capacity); + partition++) { + if (media->capacity[partition] == 0) + continue; + name = storage_partition_name(media, partition); + printk(STORAGE_DEBUG, "%s%sReading block 0\n", name, + name[0] ? ": " : ""); + err = storage_set_partition(media, partition); + if (err) + continue; + blocks_read = storage_block_read(media, 0, 1, &buffer); + if (blocks_read) + hexdump(buffer, sizeof(buffer)); + } + + /* Restore the previous partition */ + storage_set_partition(media, previous_partition); +} +#endif + +#if ENV_ROMSTAGE +static void copy_storage_structures(int is_recovery) +{ + struct storage_media *media; + struct sdhci_ctrlr *sdhci_ctrlr; + size_t size; + + /* Locate the data structures in CBMEM */ + size = &_car_drivers_storage_end - &_car_drivers_storage_start; + ASSERT(size == 256); + media = cbmem_add(CBMEM_ID_STORAGE_DATA, size); + ASSERT(media != NULL); + sdhci_ctrlr = (void *)(((uintptr_t)(media + 1) + 0x7) & ~7); + ASSERT((sdhci_ctrlr + 1) + <= (struct sdhci_ctrlr *)&_car_drivers_storage_end); + + /* Migrate the data into CBMEM */ + memcpy(media, &_car_drivers_storage_start, size); + media->ctrlr = &sdhci_ctrlr->sd_mmc_ctrlr; +} + +ROMSTAGE_CBMEM_INIT_HOOK(copy_storage_structures); +#endif -- cgit v1.2.3