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@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