[coreboot-gerrit] Change in ...coreboot[master]: Work in progress: Early eMMC phase 1

Richard Spiegel (Code Review) gerrit at coreboot.org
Mon Dec 10 22:48:55 CET 2018


Richard Spiegel has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/30135


Change subject: Work in progress: Early eMMC phase 1
......................................................................

Work in progress: Early eMMC phase 1

Global objective: Early ready of eMMC by sending CMD0 and CMD1.

This phase objective: Create bh720 driver to be used by phase 2.

BUG=b:118680303
TEST=

Change-Id: I9e4f356623a00bcdd75d9ee1775ebc961807696c
Signed-off-by: Richard Spiegel <richard.spiegel at silverbackltd.com>
---
A src/drivers/emmc/README
A src/drivers/emmc/bh7720/Kconfig
A src/drivers/emmc/bh7720/Makefile.inc
A src/drivers/emmc/bh7720/bh720.c
A src/include/device/emmc/bh720.h
A src/include/device/emmc/emmc.h
6 files changed, 450 insertions(+), 0 deletions(-)



  git pull ssh://review.coreboot.org:29418/coreboot refs/changes/35/30135/1

diff --git a/src/drivers/emmc/README b/src/drivers/emmc/README
new file mode 100644
index 0000000..b95b477
--- /dev/null
+++ b/src/drivers/emmc/README
@@ -0,0 +1,51 @@
+/*
+ * Documentation for eMMC host drivers
+ *
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2018 Silverback Ltd.
+ *
+ * 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.
+ */
+
+This folder has drivers for several eMMC hosts. There's a minimum set of
+commands these drivers must implement, though it can implement more:
+emmc_driver_init -> initializes all required registers of a particular host,
+		    so that commands can be send.
+emmc_send_command -> the host sends a command to the eMMC card, status is
+		     returned.
+emmc_get_ocr -> The OCR value is read.
+
+Commands description:
+int emmc_driver_init(void *params);
+Some parameters are always the same, so they don't need to be send for code
+execution. Only parameters that are board dependent plus the base address
+should be send.
+The input is a void pointer to parameters, which is internally converted to
+a pointer to structure, defined in the chip specific header file. Inside
+this structure, the first 2 elements are common: (uint8_t) total and
+(uint16_t/uint32_t) base_address. The remaining are chip specific,.
+The return can be 0 if success, or a negative number if there's a problem.
+
+uint32_t emmc_send_command(uint8_t command, uint32_t argument);
+Using the base address from emmc_driver_init, it sends commands to the eMMC
+and return the host status at the moment the command was issued. This is only
+for 48-bits commands (start, stop, direction,CRC77, command and argument).
+
+uint32_t emmc_get_ocr(uint32_t base_address);
+This function can be called before or after PCI enumeration, as it receives
+the base address to use. If the base address is IO, the upper word should
+be 0. It assumes that CMD1 was already issued, and returns the OCR received
+from the eMMC.
+
+Generally speaking, emmc_driver_init must be used at romstage, while the
+other 2 can be romstage or ramstage, though if PCI access is needed by the
+particular chip to execute them, then they must be either romstage or have
+a device declared to be used in ramstage.
diff --git a/src/drivers/emmc/bh7720/Kconfig b/src/drivers/emmc/bh7720/Kconfig
new file mode 100644
index 0000000..27b5c80
--- /dev/null
+++ b/src/drivers/emmc/bh7720/Kconfig
@@ -0,0 +1,2 @@
+config DRIVERS_EMMC_BH720
+	bool
diff --git a/src/drivers/emmc/bh7720/Makefile.inc b/src/drivers/emmc/bh7720/Makefile.inc
new file mode 100644
index 0000000..e282a67
--- /dev/null
+++ b/src/drivers/emmc/bh7720/Makefile.inc
@@ -0,0 +1,2 @@
+romstage-$(CONFIG_DRIVERS_EMMC_BH720) += bh720.c
+ramstage-$(CONFIG_DRIVERS_EMMC_BH720) += bh720.c
diff --git a/src/drivers/emmc/bh7720/bh720.c b/src/drivers/emmc/bh7720/bh720.c
new file mode 100644
index 0000000..7dbafe4
--- /dev/null
+++ b/src/drivers/emmc/bh7720/bh720.c
@@ -0,0 +1,252 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2018 Silverback Ltd.
+ *
+ * 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 <rules.h>
+#include <device/pci_def.h>
+#include <device/emmc/emmc.h>
+#include <device/emmc/bh720.h>
+
+static uint8_t *base_ptr;
+static boolean flag = false;
+
+static void pci_set32(uint32_t dev, uint8_t reg, uint32_t bits)
+{
+	uint32_t value;
+	value = pci_read_config32(dev, reg);
+	value |= bits;
+	pci_write_config32(dev, reg, value);
+}
+
+static void pci_clear32(uint32_t dev, uint8_t reg, uint32_t bits)
+{
+	uint32_t value, mask;
+	mask = ~bits;
+	value = pci_read_config32(dev, reg);
+	value &= mask;
+	pci_write_config32(dev, reg, value);
+}
+
+static void pci_and_or32(uint32_t dev, uint8_t reg, uint32_t mask,
+			uint32_t bits)
+{
+	uint32_t value;
+	value = pci_read_config32(dev, reg);
+	value &= ~mask;
+	value |= bits;
+	pci_write_config32(dev, reg, value);
+}
+
+{
+
+static void program_base(uint32_t dev, uint32_t base)
+{
+	pci_write_config32(dev, PCI_BASE_ADDRESS_0, base);
+	pci_write_config8(dev, PCI_COMMAND,
+				PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY);
+}
+
+static void clear_base(uint32_t dev)
+{
+	pci_write_config32(dev, PCI_BASE_ADDRESS_0, 0);
+	pci_write_config8(dev, PCI_COMMAND, 0);
+}
+
+static uint32_t host_dev_from_bridge(uint32_t dev)
+{
+	uint8_t byte;
+
+	byte = pci_read_config8(dev, PCI_SECONDARY_BUS);
+	return PCI_DEV(byte, 0, 0);
+}
+
+static int program_bridge(uint32_t dev, uintptr_t base), uint32_t size)
+{
+	uint32_t value, limit, h_dev;
+	uint8_t byte = pci_read_config8(dev, PCI_HEADER_TYPE);
+
+	if ((byte & 0x7f) != PCI_HEADER_TYPE_BRIDGE)
+		return EMMC_STATUS_INVALID_PCI;
+	byte = pci_read_config8(dev, PCI_COMMAND);
+	/* If not initialized, program bridge and device */
+	if ((byte & 0x0f) != (PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY) {
+		/* Before enumeration, primary bus 0, secondary bus 1 */
+		pci_write_config32(dev, PCI_PRIMARY_BUS, 0x00010100);
+		limit = TEMP_BASE + (TEMP_SIZE - 1);
+		limit &= 0xffff0000;
+		value = (TEMP_BASE >> 16) & 0xffff;
+		value |= limit;
+		pci_write_config32(dev, PCI_MEMORY_BASE, value);
+		pci_write_config8(dev, PCI_COMMAND, PCI_COMMAND_MASTER |
+						    PCI_COMMAND_MEMORY);
+		h_dev = PCI_DEV(1, 0, 0);
+		program_base(h_dev, TEMP_BASE);
+	}
+	return EMMC_STATUS_SUCCESS;
+}
+
+static void clear_bridge(uint32_t dev)
+{
+	pci_write_config8(dev, PCI_COMMAND, 0);
+	pci_write_config32(dev, PCI_PRIMARY_BUS, 0);
+	pci_write_config32(dev, PCI_MEMORY_BASE, 0);
+}
+
+static void program_id(uint32_t dev, uint32_t id)
+{
+	pci_clear32(dev, BH720_PROTECT, BH720_PROTECT_ON);
+	pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, id);
+	pci_set32(dev, BH720_PROTECT, BH720_PROTECT_ON);
+}
+
+static int read_base(uint32_t dev, uint32_t id)
+{
+	uint32_t emmc_dev;
+	int status;
+
+	status = program_bridge(dev, (uint32_t)base_ptr, TEMP_SIZE);
+	if (status != EMMC_STATUS_SUCCESS)
+		return status;
+	emmc_dev = host_dev_from_bridge(dev);
+	base_ptr = (uint8_t *)pci_read_config32(emmc_dev, PCI_BASE_ADDRESS_0);
+	if (id)
+		program_id(emmc_dev, id);
+	return EMMC_STATUS_SUCCESS;
+}
+
+int emmc_driver_init(void *params)
+{
+	bh720_params *param = (bh720_params *)params;
+	uint32_t temp;
+	uint32_t device = param->pci_dev;
+	uint32_t subsys_id = param->subsys_vend_dev_id;
+	uint8_t total = param->total
+	uint8_t mode = DEFAUT_MODE;
+	int status = EMMC_STATUS_SUCCESS;
+
+	base_ptr = (uint8_t *)param->base_address;
+	if (total > 4)
+		mode = param->mode;
+	if (total == 0)
+		return EMMC_STATUS_INVALID_PARAM;
+	if (total == 1)
+		return read_base(device, subsys_id);
+	if ((total == 2) || (total == 3)) {
+		if (base_ptr == NULL)
+			return EMMC_STATUS_INVALID_PARAM;
+		return status;
+	}
+	if (subsys_id && (mode & MODE_VEND_DEV))
+		program_id(device, subsys_id);
+/* If romstage, no other action is allowed. */
+#if defined(__SIMPLE_DEVICE__)
+	if (mode > MODE_VEND_DEV)
+		return EMMC_STATUS_INVALID_STAGE;
+#endif
+	pci_clear32(dev, BH720_PROTECT, BH720_PROTECT_ON);
+	if (mode & MODE_DIS_SD) {
+		pci_and_or32(dev, BH720_PML1_TIMER,
+			     BH720_PML1_TIMER_MASK, BH720_PML1_TIMER_16US);
+		pci_set_32(dev, BH720_RTD3_L1, BH720_RTD3_L1_DISABLE_L1);
+		pci_set_32(dev, BH720_LINK_CTRL, BH720_LINK_CTRL_L0_ENABLE |
+						 BH720_LINK_CTRL_L1_ENABLE);
+		pci_set_32(dev, BH720_LINK_CTRL, BH720_LINK_CTRL_CLKREQ);
+		pci_clear32(dev, BH720_ENABLES, BH_720_SD_ENABLE);
+	}
+	if (mode & MODE_VCCQ_18)
+		pci_set_32(dev, BH720_PCR_EMMC_SETTING,
+						BH720_PCR_EMMC_SETTING_1_8V};
+	if (mode & MODE_POWER_SAVE) {
+		pci_set_32(dev, BH720_RTD3_L1, BH720_RTD3_L1_DISABLE_L1);
+		pci_set_32(dev, BH720_LINK_CTRL, BH720_LINK_CTRL_L0_ENABLE |
+						 BH720_LINK_CTRL_L1_ENABLE);
+		pci_set_32(dev, BH720_LINK_CTRL, BH720_LINK_CTRL_CLKREQ);
+	}
+	if (mode & MODE_L1_TIMER) {
+		pci_and_or32(dev, BH720_PML1_TIMER,
+			     BH720_PML1_TIMER_MASK, BH720_PML1_TIMER_16US);
+		pci_and_or32(dev, BH720_ASPM_TIMERS,
+			     BH720_L1_ENTRANCE_TIMER_MASK,
+			     BH720_L1_ENTRANCE_TIMER_512US);
+	}
+	if (mode & MODE_IDDQ)
+		pci_set_32(dev, BH720_PCR_CSR, BH720_PCR_CSR__IDD_SEL);
+	if (mode & MODE_TUNING)
+		pci_and_or32(dev, BH720_TUNING,
+			     BH720_TUNING_HS200_MASK,
+			     5 << BH720_TUNING_HS200_SHIFT);
+	if (mode & MODE_DLL_CLK_PHASE)
+		pci_and_or32(dev, BH720_TUNING,
+			     BH720_TUNING_SAMPLE_CLK_MASK,
+			     6 << BH720_TUNING_SAMPLE_CLK_SHIFT);
+	pci_set32(dev, BH720_PROTECT, BH720_PROTECT_ON);
+	return status;
+}
+
+uint32_t emmc_send_command(uint8_t command, uint32_t argument)
+{
+	uint32_t *arg_ptr = (uint32_t *)base_ptr + 8;
+	uint32_t *resp_ptr = (uint32_t *)base_ptr + 0x10;
+	uint16_t *cmd_ptr = (uint16_t *)base_ptr + 0x0e;
+	uint16_t cmd = (command & 0x3f) << 8;
+
+	cmd |= 0x1a; /* check index and CRC, 48 bits. */
+	if (command == 1)
+		cmd++; /* 48 bits with busy */
+	*arg_ptr = argument;
+	*cmd_ptr = cmd; /* send command */
+	udelay(5)
+	return *resp_ptr;
+}
+
+/* Todo: need to understand if there's status for CMD1 or just OCR */
+uint32_t emmc_get_ocr(uint32_t base_address)
+{
+	uint32_t *ocr_ptr = (uint32_t *)base_address + 0x10;
+	return *ocr_ptr;
+}
+
+/*
+ * If initialized with total = 1 before PCI initialization (program base
+ * address with temporary value, then use this at the end, before PCI
+ * enumeration.
+ */
+int release_base(uint32_t dev)
+{
+	uint32_t host;
+	uint8_t byte = pci_read_config8(dev, PCI_HEADER_TYPE);
+	if ((byte & 0x7f) != PCI_HEADER_TYPE_BRIDGE)
+		return EMMC_STATUS_INVALID_PCI;
+	host = host_dev_from_bridge(dev);
+	clear_base(host);
+	clear_bridge(dev);
+}
+
+/*
+ * If used temporary base before PCI enumeration, use this function to update
+ * eMMC base address, based on actual PCI enumeration.
+ */
+int update_base(uint32_t dev)
+{
+	uint32_t emmc_dev, base;
+	uint8_t byte = pci_read_config8(dev, PCI_HEADER_TYPE);
+
+	if ((byte & 0x7f) != PCI_HEADER_TYPE_BRIDGE)
+		return EMMC_STATUS_INVALID_PCI;
+	emmc_dev = host_dev_from_bridge(dev);
+	base = (0xffff & pci_read_config32(dev, PCI_MEMORY_BASE));
+	base_ptr = (uint8_t *)(base << 16);
+	program_base(emmc_dev, base << 16);
+	return EMMC_STATUS_SUCCESS;
+}
diff --git a/src/include/device/emmc/bh720.h b/src/include/device/emmc/bh720.h
new file mode 100644
index 0000000..3715d0b
--- /dev/null
+++ b/src/include/device/emmc/bh720.h
@@ -0,0 +1,102 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2018 Silverback Ltd.
+ *
+ * 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 __BH720_H__
+#define __BH720_H__
+
+#include <types.h>
+
+struct bh720_params {
+	uint8_t total;			/* Note 1 */
+	uintptr_t base_address;		/* memory address */
+	uint32_t pci_dev;		/* PCI_DEV(bus, slot, func) */
+	uint32_t subsys_vend_dev_id;	/* if 0 or total = 3, do not set */
+	uint8_t mode;			/* optional, note 2 */
+};	/* total 5 parameters, so if all parameters, total = 5 */
+/*
+ * Note 1: The value of total shold be between 3 and 5, so use value of 1 for:
+ *   A) pci_dev is relative to the bridge the host resides.
+ *   B) If the bridge has not been programmed, then program the BAR with base
+ *      adress.
+ *   C) If bridge is programmed, then read BAR0 into base_address.
+ *   D) If subsys_vend_dev_id is provided and (B) than program subsystem vendor
+ *      and device IDs.
+ *
+ * Note 2: MODE_DIS_SD, MODE_VCCQ_18, MODE_POWER_SAVE, MODE_IDDQ, MODE_TUNNING,
+ * MODE_DLL_CLK_PHASE can only be set in ramstage, with a BH720 device defined,
+ * because they access PCI config space above 0xff.
+ */
+
+#define BH720_LINK_CTRL                 0x90
+#define   BH720_LINK_CTRL_L0_ENABLE     BIT(0)
+#define   BH720_LINK_CTRL_L1_ENABLE     BIT(1)
+#define   BH720_LINK_CTRL_CLKREQ        BIT(8)
+#define BH720_PROTECT                   0xd0
+#define   BH720_PROTECT_LOCK_ON         BIT(0)
+#define   BH720_PROTECT_ON              BIT(31)
+#define BH720_ENABLES                   0xdc
+#define   BH_720_SD_ENABLE              BIT(0)
+#define BH720_PML1_TIMER		0xe0
+#define   BH720_PML1_TIMER_SHIFT	28
+#define   BH720_PML1_TIMER_MASK         (0xf << BH720_PML1_TIMER_SHIFT)
+#define   BH720_PML1_TIMER_16US         (0x3 << BH720_PML1_TIMER_SHIFT)
+#define BH720_MISC2                     0xf0
+#define   BH720_MISC2_ASPM_DISABLE      BIT(0)
+#define   BH720_MISC2_APSM_CLKREQ_L1    BIT(7)
+#define   BH720_MISC2_APSM_PHY_L1       BIT(10)
+#define   BH720_MISC2_APSM_MORE         BIT(12)
+#define BH720_ASPM_TIMERS               0xfc
+#define   BH720_L1_ENTR_TIMER_SHIFT     16
+#define   BH720_L1_ENTRANCE_TIMER_MASK  (0xf << BH720_L1_ENTR_TIMER_SHIFT)
+#define   BH720_L1_ENTRANCE_TIMER_512US (0x1000 << BH720_L1_ENTR_TIMER_SHIFT)
+#define BH720_MEM_RW_DATA               0x200
+#define BH720_MEM_RW_ADR                0x204
+#define   BH720_MEM_RW_READ             BIT(30)
+#define   BH720_MEM_RW_WRITE            BIT(31)
+#define BH720_MEM_ACCESS_EN             0x208
+#define BH720_TUNING                    0x300
+#define   BH720_TUNING_HS200_SHIFT      4
+#define   BH720_TUNING_HS200_MASK       (0xf << BH720_TUNING_HS200_SHIFT)
+#define   BH720_TUNING_SAMPLE_CLK_SHIFT 12
+#define   BH720_TUNING_SAMPLE_CLK_MASK  (0xf << BH720_TUNING_SAMPLE_CLK_SHIFT)
+#define BH720_PCR_DRV_STRENGHT_PLL      0x304
+#define   BH720_PCR_DATA_CMD_DRV_MAX    7
+#define   BH720_PCR_CLK_DRV_MAX         7
+#define BH720_PCR_EMMC_SETTING          0x308
+#define   BH720_PCR_EMMC_SETTING_1_8V   BIT(4)
+#define BH720_RTD3_L1                   0x3e0
+#define   BH720_RTD3_L1_DISABLE_L1      BIT(28)
+#define BH720_PCR_CSR                   0x3e4
+#define   BH720_PCR_CSR__IDD_SEL        BIT(19)
+#define   BH720_PCR_CSR_EMMC_MODE_SEL   BIT(22)
+
+#define TEMP_BASE			0xf4a00000
+#define TEMP_SIZE			0x1000
+
+#define MODE_VEND_DEV			BIT(0)
+#define MODE_DIS_SD			BIT(1)
+#define MODE_VCCQ_18			BIT(2)
+#define MODE_POWER_SAVE			BIT(3)
+#define MODE_L1_TIMER			BIT(4)
+#define MODE_IDDQ			BIT(5)
+#define MODE_TUNING			BIT(6)
+#define MODE_DLL_CLK_PHASE		BIT(7)
+
+#define DEFAUT_MODE			MODE_VEND_DEV
+
+/* Input is bridge device/func */
+int release_base(uint32_t dev);
+int update_base(uint32_t dev);
+#endif
diff --git a/src/include/device/emmc/emmc.h b/src/include/device/emmc/emmc.h
new file mode 100644
index 0000000..0826f53
--- /dev/null
+++ b/src/include/device/emmc/emmc.h
@@ -0,0 +1,41 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2018 Silverback Ltd.
+ *
+ * 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 __EMMC_H__
+#define __EMMC_H__
+
+#define EMMC_PARAM_BYTE			 1
+#define EMMC_PARAM_WORD			 2
+#define EMMC_PARAM_DWORD		 3
+#define EMMC_PARAM_QWORD		 4
+#define EMMC_PARAM_PCI_DEV		 5
+
+#define EMMC_STATUS_SUCCESS		 0
+#define EMMC_STATUS_INVALID_PARAM	-1
+#define EMMC_STATUS_NO_BASE_ADDRESS	-2 /* drive not initialized */
+#define EMMC_STATUS_INVALID_BASE_ADDR	-3 /* invalid base size */
+#define EMMC_STATUS_INVALID_PCI		-4 /* invalid pci */
+#define EMMC_STATUS_HOST_BUSY		-5 /* bit 31 of OCR 0b */
+#define EMMC_STATUS_INVALID_STAGE	-6 /* PCI reg > 0xff in romstage */
+
+int emmc_driver_init(void *params);
+uint32_t emmc_send_command(uint8_t command, uint32_t argument);
+uint32_t emmc_get_ocr(uint32_t base_address);
+
+int emmc_go_idle(void);
+int emmc_go_ready(uint32_t argument); /* CMD1 argument depends on board/host */
+int emmc_check_ready{uint32_t *ocr, uint32_t base_address);
+
+#endif

-- 
To view, visit https://review.coreboot.org/c/coreboot/+/30135
To unsubscribe, or for help writing mail filters, visit https://review.coreboot.org/settings

Gerrit-Project: coreboot
Gerrit-Branch: master
Gerrit-Change-Id: I9e4f356623a00bcdd75d9ee1775ebc961807696c
Gerrit-Change-Number: 30135
Gerrit-PatchSet: 1
Gerrit-Owner: Richard Spiegel <richard.spiegel at silverbackltd.com>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.coreboot.org/pipermail/coreboot-gerrit/attachments/20181210/a834476f/attachment-0001.html>


More information about the coreboot-gerrit mailing list