summaryrefslogtreecommitdiff
path: root/src/soc/intel/common/block
diff options
context:
space:
mode:
Diffstat (limited to 'src/soc/intel/common/block')
-rw-r--r--src/soc/intel/common/block/include/intelblocks/early_mmc.h80
-rw-r--r--src/soc/intel/common/block/scs/Kconfig10
-rw-r--r--src/soc/intel/common/block/scs/Makefile.inc1
-rw-r--r--src/soc/intel/common/block/scs/early_mmc.c166
4 files changed, 257 insertions, 0 deletions
diff --git a/src/soc/intel/common/block/include/intelblocks/early_mmc.h b/src/soc/intel/common/block/include/intelblocks/early_mmc.h
new file mode 100644
index 0000000000..69be40cf27
--- /dev/null
+++ b/src/soc/intel/common/block/include/intelblocks/early_mmc.h
@@ -0,0 +1,80 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2018 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 SOC_INTEL_COMMON_BLOCK_EARLY_MMC_H
+#define SOC_INTEL_COMMON_BLOCK_EARLY_MMC_H
+
+#include <stdint.h>
+
+/*
+ * Following should be defined in soc/iomap.h
+ * PRERAM_MMC_BASE_ADDRESS - Provide an address to setup emmc controller's
+ PCI BAR.
+ */
+
+/*
+ * Structure for the following delay registers
+ * emmc_tx_data_cntl1: Tx Delay Control 1 (Tx_DATA_dly_1)-Offset 824h
+ * emmc_tx_data_cntl2: Tx Delay Control 2 (Tx_DATA_dly_2)-Offset 828h
+ * emmc_rx_cmd_data_cntl1: Rx CMD Data Delay Control 1
+ * (Rx_CMD_Data_dly_1)-Offset 82Ch
+ * emmc_rx_cmd_data_cntl2: Rx CMD Data Path Delay Control 2
+ * (Rx_CMD_Data_dly_2)-Offset 834h
+ * emmc_rx_strobe_cntl: Rx Strobe Delay Control
+ * (Rx_Strobe_Ctrl_Path)-Offset 830h
+ * emmc_tx_cmd_cntl: Tx CMD Delay Control (Tx_CMD_dly)-Offset 820h
+ */
+struct mmc_dll_params {
+ uint32_t emmc_tx_data_cntl1;
+ uint32_t emmc_tx_data_cntl2;
+ uint32_t emmc_rx_cmd_data_cntl1;
+ uint32_t emmc_rx_cmd_data_cntl2;
+ uint32_t emmc_rx_strobe_cntl;
+ uint32_t emmc_tx_cmd_cntl;
+};
+
+/*
+ * SOC specific API to get mmc min max frequencies.
+ * returns 0, if able to get f_min, f_max; otherwise returns -1
+ */
+int soc_get_mmc_frequencies(uint32_t *f_min, uint32_t *f_max);
+/*
+ * SOC specific API to configure mmc gpios.
+ * returns 0, if able to configure gpios; otherwise returns -1
+ */
+int soc_configure_mmc_gpios(void);
+/*
+ * SOC specific API to get mmc delay register settings.
+ * returns 0, if able to get register settings; otherwise returns -1
+ */
+int soc_get_mmc_dll(struct mmc_dll_params *params);
+
+#if IS_ENABLED(CONFIG_SOC_INTEL_COMMON_EARLY_MMC_WAKE)
+/*
+ * Initializes sdhci / mmc controller and sends CMD0, CMD1 to emmc card.
+ * In case of success: It returns 0 and adds cbmem entry CBMEM_ID_MMC_STATUS
+ * and sets it to 1. Payload can start by sending CMD1, there is no need to
+ * send CMD0 and wait for the card to be ready.
+ * In case of failure: It returns -1 and doesn't add cbmem entry. Payload
+ * should do complete initialization starting with CMD0.
+ */
+int early_mmc_wake_hw(void);
+#else
+static inline int early_mmc_wake_hw(void)
+{
+ return -1;
+}
+#endif /* CONFIG_SOC_INTEL_COMMON_EARLY_MMC_WAKE */
+#endif /* SOC_INTEL_COMMON_BLOCK_EARLY_MMC_H */
diff --git a/src/soc/intel/common/block/scs/Kconfig b/src/soc/intel/common/block/scs/Kconfig
index 0a402137bc..06ad8e4fa8 100644
--- a/src/soc/intel/common/block/scs/Kconfig
+++ b/src/soc/intel/common/block/scs/Kconfig
@@ -2,3 +2,13 @@ config SOC_INTEL_COMMON_BLOCK_SCS
bool
help
Intel Processor common storage and communication subsystem support
+
+config SOC_INTEL_COMMON_EARLY_MMC_WAKE
+ bool
+ default n
+ select COMMONLIB_STORAGE
+ select COMMONLIB_STORAGE_MMC
+ select SDHCI_CONTROLLER
+ help
+ Send CMD1 early in romstage to improve boot time. It requires emmc
+ DLL tuning parameters to be added to devicetree.cb
diff --git a/src/soc/intel/common/block/scs/Makefile.inc b/src/soc/intel/common/block/scs/Makefile.inc
index 1c2a6c6692..1160802d03 100644
--- a/src/soc/intel/common/block/scs/Makefile.inc
+++ b/src/soc/intel/common/block/scs/Makefile.inc
@@ -1 +1,2 @@
ramstage-$(CONFIG_SOC_INTEL_COMMON_BLOCK_SCS) += sd.c
+romstage-$(CONFIG_SOC_INTEL_COMMON_EARLY_MMC_WAKE) += early_mmc.c
diff --git a/src/soc/intel/common/block/scs/early_mmc.c b/src/soc/intel/common/block/scs/early_mmc.c
new file mode 100644
index 0000000000..4b15bb48bb
--- /dev/null
+++ b/src/soc/intel/common/block/scs/early_mmc.c
@@ -0,0 +1,166 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2018 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 <arch/acpi.h>
+#include <arch/early_variables.h>
+#include <cbmem.h>
+#include <commonlib/storage/sd_mmc.h>
+#include <commonlib/sd_mmc_ctrlr.h>
+#include <commonlib/sdhci.h>
+#include <compiler.h>
+#include <console/console.h>
+#include <device/pci.h>
+#include <intelblocks/early_mmc.h>
+#include <soc/iomap.h>
+#include <soc/pci_devs.h>
+#include <string.h>
+
+#define EMMC_TX_CMD_CNTL_OFFSET 0x820
+#define EMMC_TX_DATA_CNTL1_OFFSET 0x824
+#define EMMC_TX_DATA_CNTL2_OFFSET 0x828
+#define EMMC_RX_CMD_DATA_CNTL1_OFFSET 0x82C
+#define EMMC_RX_STROBE_CNTL_OFFSET 0x830
+#define EMMC_RX_CMD_DATA_CNTL2_OFFSET 0x834
+
+void soc_sd_mmc_controller_quirks(struct sd_mmc_ctrlr *ctrlr)
+{
+ uint32_t f_min, f_max;
+
+ if (soc_get_mmc_frequencies(&f_min, &f_max) < 0) {
+ printk(BIOS_ERR,
+ "MMC early init: failed to get mmc frequencies\n");
+ return;
+ }
+
+ ctrlr->f_min = f_min;
+ ctrlr->f_max = f_max;
+}
+
+static void enable_mmc_controller_bar(void)
+{
+ pci_write_config32(PCH_DEV_EMMC, PCI_BASE_ADDRESS_0,
+ PRERAM_MMC_BASE_ADDRESS);
+ pci_write_config32(PCH_DEV_EMMC, PCI_COMMAND,
+ PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY);
+}
+
+static void disable_mmc_controller_bar(void)
+{
+ pci_write_config32(PCH_DEV_EMMC, PCI_BASE_ADDRESS_0, 0);
+ pci_write_config32(PCH_DEV_EMMC, PCI_COMMAND,
+ ~(PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY));
+}
+
+static int set_mmc_dll(void *ioaddr)
+{
+ struct mmc_dll_params dll_params;
+
+ if (soc_get_mmc_dll(&dll_params) < 0) {
+ printk(BIOS_ERR,
+ "MMC early init: failed to get mmc DLL parameters\n");
+ return -1;
+ }
+
+ write32(ioaddr + EMMC_TX_DATA_CNTL1_OFFSET,
+ dll_params.emmc_tx_data_cntl1);
+ write32(ioaddr + EMMC_TX_DATA_CNTL2_OFFSET,
+ dll_params.emmc_tx_data_cntl2);
+ write32(ioaddr + EMMC_RX_CMD_DATA_CNTL1_OFFSET,
+ dll_params.emmc_rx_cmd_data_cntl1);
+ write32(ioaddr + EMMC_RX_CMD_DATA_CNTL2_OFFSET,
+ dll_params.emmc_rx_cmd_data_cntl2);
+ write32(ioaddr + EMMC_RX_STROBE_CNTL_OFFSET,
+ dll_params.emmc_rx_strobe_cntl);
+ write32(ioaddr + EMMC_TX_CMD_CNTL_OFFSET,
+ dll_params.emmc_tx_cmd_cntl);
+
+ return 0;
+}
+
+static void set_early_mmc_wake_status(int32_t status)
+{
+ int32_t *ms_cbmem;
+
+ ms_cbmem = cbmem_add(CBMEM_ID_MMC_STATUS, sizeof(int));
+
+ if (ms_cbmem == NULL) {
+ printk(BIOS_ERR,
+ "%s: Failed to add early mmc wake status to cbmem!\n",
+ __func__);
+ return;
+ }
+
+ *ms_cbmem = status;
+}
+
+int early_mmc_wake_hw(void)
+{
+ struct storage_media media;
+ struct sd_mmc_ctrlr *mmc_ctrlr;
+ struct sdhci_ctrlr *sdhci_ctrlr;
+ int err;
+
+ if (acpi_is_wakeup_s3())
+ return -1;
+
+ /* Configure mmc gpios */
+ if (soc_configure_mmc_gpios() < 0) {
+ printk(BIOS_ERR,
+ "%s: MMC early init: failed to configure mmc gpios\n",
+ __func__);
+ return -1;
+ }
+ /* Setup pci bar */
+ enable_mmc_controller_bar();
+
+ /* Initialize sdhci */
+ mmc_ctrlr = new_pci_sdhci_controller(PCH_DEV_EMMC);
+ if (mmc_ctrlr == NULL)
+ goto out_err;
+
+ sdhci_ctrlr = container_of(mmc_ctrlr, struct sdhci_ctrlr, sd_mmc_ctrlr);
+
+ /* set emmc DLL tuning parameters */
+ if (set_mmc_dll(sdhci_ctrlr->ioaddr) < 0)
+ goto out_err;
+
+ memset(&media, 0, sizeof(media));
+ media.ctrlr = mmc_ctrlr;
+ SET_BUS_WIDTH(mmc_ctrlr, 1);
+ /*
+ * Set clock to 1 so that the driver can choose minimum frequency
+ * possible
+ */
+ SET_CLOCK(mmc_ctrlr, 1);
+
+ /* Reset emmc, send CMD0 */
+ if (sd_mmc_go_idle(&media))
+ goto out_err;
+
+ /* Send CMD1 */
+ err = mmc_send_op_cond(&media);
+ if (err != 0 && err != CARD_IN_PROGRESS)
+ goto out_err;
+
+ disable_mmc_controller_bar();
+
+ set_early_mmc_wake_status(1);
+ return 0;
+
+out_err:
+
+ disable_mmc_controller_bar();
+ return -1;
+}