Import U-Boot v2026.07 (unmodified upstream source, base for rtwo patches)

This commit is contained in:
2026-09-16 23:15:56 -03:00
commit 391981275c
38022 changed files with 8370285 additions and 0 deletions
+10
View File
@@ -0,0 +1,10 @@
# SPDX-License-Identifier: GPL-2.0+
#
# Copyright 2022 DENX Software Engineering GmbH, Philip Oberfichtner <pro@denx.de>
#
obj-y += dh_common.o
ifneq (,$(CONFIG_ARCH_MX6)$(CONFIG_ARCH_IMX8M))
obj-y += dh_imx.o
endif
+285
View File
@@ -0,0 +1,285 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2022 Marek Vasut <marex@denx.de>
* Copyright 2022 DENX Software Engineering GmbH, Philip Oberfichtner <pro@denx.de>
*/
#include <dm.h>
#include <env.h>
#include <i2c_eeprom.h>
#include <net.h>
#include <u-boot/crc.h>
#include "dh_common.h"
static int on_dh_som_serial_number(const char *name, const char *value, enum env_op op,
int flags)
{
env_set("SN", value);
return 0;
}
U_BOOT_ENV_CALLBACK(dh_som_serial_number, on_dh_som_serial_number);
static int on_SN(const char *name, const char *value, enum env_op op, int flags)
{
env_set("dh_som_serial_number", value);
return 0;
}
U_BOOT_ENV_CALLBACK(SN, on_SN);
bool dh_mac_is_in_env(const char *env)
{
unsigned char enetaddr[6];
return eth_env_get_enetaddr(env, enetaddr);
}
int dh_get_mac_is_enabled(const char *alias)
{
ofnode node = ofnode_path(alias);
if (!ofnode_valid(node))
return -EINVAL;
if (!ofnode_is_enabled(node))
return -ENODEV;
return 0;
}
int dh_read_eeprom_id_page(u8 *eeprom_buffer, const char *alias)
{
struct eeprom_id_page *eip = (struct eeprom_id_page *)eeprom_buffer;
struct udevice *dev;
size_t payload_len;
int eeprom_size;
u16 crc16_calc;
u16 crc16_eip;
u8 crc8_calc;
ofnode node;
int ret;
node = ofnode_path(alias);
ret = uclass_get_device_by_ofnode(UCLASS_I2C_EEPROM, node, &dev);
if (ret)
return ret;
eeprom_size = i2c_eeprom_size(dev);
if (eeprom_size < 0) {
printf("%s: Error getting EEPROM ID page size! ret = %d\n", __func__, ret);
return eeprom_size;
}
if (eeprom_size == 0 || eeprom_size > DH_EEPROM_ID_PAGE_MAX_SIZE) {
eeprom_size = DH_EEPROM_ID_PAGE_MAX_SIZE;
printf("Get invalid EEPROM ID page size %d bytes! Try to read %d bytes.\n",
eeprom_size, DH_EEPROM_ID_PAGE_MAX_SIZE);
}
ret = i2c_eeprom_read(dev, 0x0, eeprom_buffer, eeprom_size);
if (ret) {
printf("%s: Error reading EEPROM ID page! ret = %d\n", __func__, ret);
return ret;
}
/* Validate header ID */
if (eip->hdr.id[0] != 'D' || eip->hdr.id[1] != 'H' || eip->hdr.id[2] != 'E') {
printf("%s: Error validating header ID! (got %c%c%c (0x%02x 0x%02x 0x%02x) != expected DHE)\n",
__func__, (isascii(eip->hdr.id[0]) && isprint(eip->hdr.id[0])) ? eip->hdr.id[0] : '.',
(isascii(eip->hdr.id[1]) && isprint(eip->hdr.id[1])) ? eip->hdr.id[1] : '.',
(isascii(eip->hdr.id[2]) && isprint(eip->hdr.id[2])) ? eip->hdr.id[2] : '.',
eip->hdr.id[0], eip->hdr.id[1], eip->hdr.id[2]);
return -EINVAL;
}
/* Validate header checksum */
crc8_calc = crc8(0xff, eeprom_buffer, offsetof(struct eeprom_id_page, hdr.crc8_hdr));
if (eip->hdr.crc8_hdr != crc8_calc) {
printf("%s: Error validating header checksum! (got 0x%02x != calc 0x%02x)\n",
__func__, eip->hdr.crc8_hdr, crc8_calc);
return -EINVAL;
}
/*
* Validate header version
* The payload is defined by the version specified in the header.
* Currently only version 0x10 is defined, so take the length of
* the only defined payload as the payload length.
*/
if (eip->hdr.version != DH_EEPROM_ID_PAGE_V1_0) {
printf("%s: Error validating version! (0x%02X is not supported)\n",
__func__, eip->hdr.version);
return -EINVAL;
}
payload_len = sizeof(eip->pl);
/* Validate payload checksum */
crc16_eip = (eip->hdr.crc16_pl[1] << 8) | eip->hdr.crc16_pl[0];
crc16_calc = crc16(0xffff, eeprom_buffer + sizeof(eip->hdr), payload_len);
if (crc16_eip != crc16_calc) {
printf("%s: Error validating data checksum! (got 0x%02x != calc 0x%02x)\n",
__func__, crc16_eip, crc16_calc);
return -EINVAL;
}
return 0;
}
int dh_get_value_from_eeprom_buffer(enum eip_request_values request, u8 *data, int data_len,
struct eeprom_id_page *eip)
{
char fin_chr;
u8 soc_coded;
char soc_chr;
if (!eip)
return -EINVAL;
fin_chr = (eip->pl.item_prefix & DH_ITEM_PREFIX_FIN_BIT) ?
DH_ITEM_PREFIX_FIN_FLASHED_CHR : DH_ITEM_PREFIX_FIN_HALF_CHR;
soc_coded = eip->pl.item_prefix & 0xf;
/* Copy requested data */
switch (request) {
case DH_MAC0:
if (!is_valid_ethaddr(eip->pl.mac0))
return -EINVAL;
if (data_len >= sizeof(eip->pl.mac0))
memcpy(data, eip->pl.mac0, sizeof(eip->pl.mac0));
else
return -EINVAL;
break;
case DH_MAC1:
if (!is_valid_ethaddr(eip->pl.mac1))
return -EINVAL;
if (data_len >= sizeof(eip->pl.mac1))
memcpy(data, eip->pl.mac1, sizeof(eip->pl.mac1));
else
return -EINVAL;
break;
case DH_ITEM_NUMBER:
if (data_len < 8) /* String length must be 7 characters + string termination */
return -EINVAL;
if (soc_coded == DH_ITEM_PREFIX_NXP)
soc_chr = DH_ITEM_PREFIX_NXP_CHR;
else if (soc_coded == DH_ITEM_PREFIX_ST)
soc_chr = DH_ITEM_PREFIX_ST_CHR;
else
return -EINVAL;
snprintf(data, data_len, "%c%c%05d", fin_chr, soc_chr,
(eip->pl.item_num[0] << 16) | (eip->pl.item_num[1] << 8) |
eip->pl.item_num[2]);
break;
case DH_SERIAL_NUMBER:
/*
* data_len must be greater than the size of eip->pl.serial,
* because there is a string termination needed.
*/
if (data_len <= sizeof(eip->pl.serial))
return -EINVAL;
data[sizeof(eip->pl.serial)] = 0;
memcpy(data, eip->pl.serial, sizeof(eip->pl.serial));
break;
default:
return -EINVAL;
}
return 0;
}
int dh_get_mac_from_eeprom(unsigned char *enetaddr, const char *alias)
{
struct udevice *dev;
int ret;
ofnode node;
node = ofnode_path(alias);
if (!ofnode_valid(node)) {
printf("%s: ofnode for %s not found!", __func__, alias);
return -ENOENT;
}
ret = uclass_get_device_by_ofnode(UCLASS_I2C_EEPROM, node, &dev);
if (ret) {
printf("%s: Cannot find EEPROM! ret = %d\n", __func__, ret);
return ret;
}
ret = i2c_eeprom_read(dev, 0xfa, enetaddr, 0x6);
if (ret) {
printf("%s: Error reading EEPROM! ret = %d\n", __func__, ret);
return ret;
}
if (!is_valid_ethaddr(enetaddr)) {
printf("%s: Address read from EEPROM is invalid!\n", __func__);
return -EINVAL;
}
return 0;
}
__weak int dh_setup_mac_address(struct eeprom_id_page *eip)
{
unsigned char enetaddr[6];
if (dh_mac_is_in_env("ethaddr"))
return 0;
if (dh_get_mac_is_enabled("ethernet0"))
return 0;
if (!dh_get_value_from_eeprom_buffer(DH_MAC0, enetaddr, sizeof(enetaddr), eip))
return eth_env_set_enetaddr("ethaddr", enetaddr);
if (!dh_get_mac_from_eeprom(enetaddr, "eeprom0"))
return eth_env_set_enetaddr("ethaddr", enetaddr);
printf("%s: Unable to set mac address!\n", __func__);
return -ENXIO;
}
void dh_add_item_number_and_serial_to_env(struct eeprom_id_page *eip)
{
char *item_number_env;
char item_number[8]; /* String with 7 characters + string termination */
char *serial_env;
char serial[10]; /* String with 9 characters + string termination */
int ret;
ret = dh_get_value_from_eeprom_buffer(DH_ITEM_NUMBER, item_number, sizeof(item_number),
eip);
if (ret) {
printf("%s: Unable to get DHSOM item number from EEPROM ID page! ret = %d\n",
__func__, ret);
} else {
item_number_env = env_get("dh_som_item_number");
if (!item_number_env)
env_set("dh_som_item_number", item_number);
else if (strcmp(item_number_env, item_number))
printf("Warning: Environment dh_som_item_number differs from EEPROM ID page value (%s != %s)\n",
item_number_env, item_number);
}
ret = dh_get_value_from_eeprom_buffer(DH_SERIAL_NUMBER, serial, sizeof(serial),
eip);
if (ret) {
printf("%s: Unable to get DHSOM serial number from EEPROM ID page! ret = %d\n",
__func__, ret);
} else {
serial_env = env_get("dh_som_serial_number");
if (!serial_env)
env_set("dh_som_serial_number", serial);
else if (strcmp(serial_env, serial))
printf("Warning: Environment dh_som_serial_number differs from EEPROM ID page value (%s != %s)\n",
serial_env, serial);
}
}
+122
View File
@@ -0,0 +1,122 @@
/* SPDX-License-Identifier: GPL-2.0+
*
* Copyright 2022 DENX Software Engineering GmbH, Philip Oberfichtner <pro@denx.de>
*/
#define DH_EEPROM_ID_PAGE_MAX_SIZE 64
enum eip_request_values {
DH_MAC0,
DH_MAC1,
DH_ITEM_NUMBER,
DH_SERIAL_NUMBER,
};
/* DH item: Vendor coding */
#define DH_ITEM_PREFIX_NXP 0x01
#define DH_ITEM_PREFIX_NXP_CHR 'I'
#define DH_ITEM_PREFIX_ST 0x02
#define DH_ITEM_PREFIX_ST_CHR 'S'
/*
* DH item: Finished state coding
* Bit = 0 means half finished
* Prefix is 'H'
* Bit = 1 means finished with a customer image flashed
* Prefix is 'F'
*/
#define DH_ITEM_PREFIX_FIN_BIT BIT(7)
#define DH_ITEM_PREFIX_FIN_HALF_CHR 'H'
#define DH_ITEM_PREFIX_FIN_FLASHED_CHR 'F'
struct eeprom_id_page {
/* Header */
struct {
u8 id[3]; /* Identifier 'D', 'H', 'E' - 'D' is at index 0 */
u8 version; /* 0x10 -- Version 1.0 */
u8 crc16_pl[2]; /* Checksum payload, [1] is MSbyte */
u8 crc8_hdr; /* Checksum header */
} hdr;
/* Payload */
struct {
u8 mac0[6];
u8 mac1[6];
u8 item_prefix; /* H/F is coded in MSbits, Vendor coding starts at LSbits */
u8 item_num[3]; /* [2] is MSbyte */
u8 serial[9]; /* [8] is MSbyte */
} pl;
};
#define DH_EEPROM_ID_PAGE_V1_0 0x10
/*
* dh_mac_is_in_env - Check if MAC address is already set
*
* @env: name of environment variable
* Return: true if MAC is set, false otherwise
*/
bool dh_mac_is_in_env(const char *env);
/*
* dh_get_mac_is_enabled - Test if ethernet MAC is enabled in DT
*
* @alias: alias for ethernet MAC device tree node
* Return: 0 if OK, other value on error
*/
int dh_get_mac_is_enabled(const char *alias);
/*
* dh_get_mac_from_eeprom - Get MAC address from eeprom and write it to enetaddr
*
* @enetaddr: buffer where address is to be stored
* @alias: alias for EEPROM device tree node
* Return: 0 if OK, other value on error
*/
int dh_get_mac_from_eeprom(unsigned char *enetaddr, const char *alias);
/*
* dh_read_eeprom_id_page() - Read EEPROM ID page content into given buffer
* @eeprom_buffer: Buffer for EEPROM ID page content
* @alias: Alias for EEPROM ID page device tree node
*
* Read the content of the EEPROM ID page into the given buffer (parameter
* eeprom_buffer). The EEPROM ID page device is selected via alias device
* tree name (parameter alias). The data of the EEPROM ID page is verified.
* An error is returned for reading failures and invalid data.
*
* Return: 0 if OK, other value on error
*/
int dh_read_eeprom_id_page(u8 *eeprom_buffer, const char *alias);
/*
* dh_get_value_from_eeprom_buffer() - Get value from EEPROM buffer
* @eip_request_values: Requested value as enum
* @data: Buffer where value is to be stored
* @data_len: Length of the value buffer
* @eip: Pointer to EEPROM ID page struct from which the data is parsed
*
* Gets the value specified by the parameter eip_request_values from the EEPROM
* data struct (parameter eip). The data is written to the specified data
* buffer (parameter data). If the length of the data (parameter data_len) is
* not sufficient to copy the data into the buffer, an error is returned.
*
* Return: 0 if OK, other value on error
*/
int dh_get_value_from_eeprom_buffer(enum eip_request_values request, u8 *data, int data_len,
struct eeprom_id_page *eip);
/*
* dh_setup_mac_address - Try to get MAC address from various locations and write it to env
* @eip: ID EEPROM buffer
*
* Return: 0 if OK, other value on error
*/
int dh_setup_mac_address(struct eeprom_id_page *eip);
/*
* dh_add_item_number_and_serial_to_env - Try to get DH IDs from WLP write them to env
* @eip: ID EEPROM buffer
*
* Return: 0 if OK, other value on error
*/
void dh_add_item_number_and_serial_to_env(struct eeprom_id_page *eip);
+24
View File
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2022 Marek Vasut <marex@denx.de>
* Copyright 2022 DENX Software Engineering GmbH, Philip Oberfichtner <pro@denx.de>
*/
#include <linux/errno.h>
#include <asm/arch/imx-regs.h>
#include <asm/arch/sys_proto.h>
#include <net.h>
#include "dh_imx.h"
int dh_imx_get_mac_from_fuse(unsigned char *enetaddr, int index)
{
/*
* If IIM fuses contain valid MAC address, use it.
* The IIM MAC address fuses are NOT programmed by default.
*/
imx_get_mac_from_fuse(index, enetaddr);
if (!is_valid_ethaddr(enetaddr))
return -EINVAL;
return 0;
}
+13
View File
@@ -0,0 +1,13 @@
/* SPDX-License-Identifier: GPL-2.0+
*
* Copyright 2022 DENX Software Engineering GmbH, Philip Oberfichtner <pro@denx.de>
*/
/*
* dh_imx_get_mac_from_fuse - Get MAC address from fuse and write it to env
*
* @enetaddr: buffer where address is to be stored
* @index: index of MAC address in fuse (starts with 0)
* Return: 0 if OK, other value on error
*/
int dh_imx_get_mac_from_fuse(unsigned char *enetaddr, int index);
+12
View File
@@ -0,0 +1,12 @@
if TARGET_DHCOMIMX6
config SYS_BOARD
default "dh_imx6"
config SYS_VENDOR
default "dhelectronics"
config SYS_CONFIG_NAME
default "dh_imx6"
endif
+7
View File
@@ -0,0 +1,7 @@
DH_IMX6 BOARD
M: Andreas Geisreiter <ageisreiter@dh-electronics.de>
M: Christoph Niedermaier <cniedermaier@dh-electronics.com>
L: u-boot@dh-electronics.com
S: Maintained
N: imx6.*dh[cs]o
N: dh_imx6
+9
View File
@@ -0,0 +1,9 @@
# SPDX-License-Identifier: GPL-2.0+
#
# Copyright (C) 2017 Marek Vasut <marex@denx.de>
ifdef CONFIG_XPL_BUILD
obj-y := dh_imx6_spl.o
else
obj-y := dh_imx6.o
endif
+242
View File
@@ -0,0 +1,242 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* DHCOM DH-iMX6 PDK board support
*
* Copyright (C) 2017 Marek Vasut <marex@denx.de>
*/
#include <dm.h>
#include <image.h>
#include <init.h>
#include <net.h>
#include <asm/global_data.h>
#include <dm/device-internal.h>
#include <asm/arch/clock.h>
#include <asm/arch/crm_regs.h>
#include <asm/arch/imx-regs.h>
#include <asm/arch/iomux.h>
#include <asm/arch/mx6-pins.h>
#include <asm/arch/sys_proto.h>
#include <asm/gpio.h>
#include <asm/io.h>
#include <asm/mach-imx/boot_mode.h>
#include <asm/mach-imx/iomux-v3.h>
#include <asm/mach-imx/sata.h>
#include <ahci.h>
#include <dwc_ahsata.h>
#include <env.h>
#include <errno.h>
#include <fsl_esdhc_imx.h>
#include <fuse.h>
#include <i2c_eeprom.h>
#include <mmc.h>
#include <power/regulator.h>
#include <usb.h>
#include <linux/delay.h>
#include <usb/ehci-ci.h>
#include "../common/dh_common.h"
#include "../common/dh_imx.h"
DECLARE_GLOBAL_DATA_PTR;
int dram_init(void)
{
gd->ram_size = imx_ddr_size();
return 0;
}
/*
* Do not overwrite the console
* Use always serial for U-Boot console
*/
int overwrite_console(void)
{
return 1;
}
static int setup_fec_clock(void)
{
struct iomuxc *iomuxc_regs = (struct iomuxc *)IOMUXC_BASE_ADDR;
/* set gpr1[21] to select anatop clock */
clrsetbits_le32(&iomuxc_regs->gpr[1], 0x1 << 21, 0x1 << 21);
return enable_fec_anatop_clock(0, ENET_50MHZ);
}
#ifdef CONFIG_USB_EHCI_MX6
static void setup_usb(void)
{
/*
* Set daisy chain for otg_pin_id on MX6Q.
* For MX6DL, this bit is reserved.
*/
imx_iomux_set_gpr_register(1, 13, 1, 0);
}
int board_usb_phy_mode(int port)
{
if (port == 1)
return USB_INIT_HOST;
else
return USB_INIT_DEVICE;
}
#endif
int dh_setup_mac_address(struct eeprom_id_page *eip)
{
unsigned char enetaddr[6];
if (dh_mac_is_in_env("ethaddr"))
return 0;
if (dh_get_mac_is_enabled("ethernet0"))
return 0;
if (!dh_imx_get_mac_from_fuse(enetaddr, 0))
goto out;
if (!dh_get_mac_from_eeprom(enetaddr, "eeprom0"))
goto out;
printf("%s: Unable to get MAC address!\n", __func__);
return -ENXIO;
out:
return eth_env_set_enetaddr("ethaddr", enetaddr);
}
int board_early_init_f(void)
{
#ifdef CONFIG_USB_EHCI_MX6
setup_usb();
#endif
return 0;
}
int board_init(void)
{
struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR;
/* address of boot parameters */
gd->bd->bi_boot_params = PHYS_SDRAM + 0x100;
/* Enable eim_slow clocks */
setbits_le32(&mxc_ccm->CCGR6, 0x1 << MXC_CCM_CCGR6_EMI_SLOW_OFFSET);
setup_fec_clock();
return 0;
}
#ifdef CONFIG_CMD_BMODE
static const struct boot_mode board_boot_modes[] = {
/* 4 bit bus width */
{"sd2", MAKE_CFGVAL(0x40, 0x28, 0x00, 0x00)},
{"sd3", MAKE_CFGVAL(0x40, 0x30, 0x00, 0x00)},
/* 8 bit bus width */
{"emmc", MAKE_CFGVAL(0x60, 0x58, 0x00, 0x00)},
{NULL, 0},
};
#endif
#define HW_CODE_BIT_0 IMX_GPIO_NR(2, 19)
#define HW_CODE_BIT_1 IMX_GPIO_NR(6, 6)
#define HW_CODE_BIT_2 IMX_GPIO_NR(2, 16)
static int board_get_hwcode(void)
{
int hw_code;
gpio_request(HW_CODE_BIT_0, "HW-code-bit-0");
gpio_request(HW_CODE_BIT_1, "HW-code-bit-1");
gpio_request(HW_CODE_BIT_2, "HW-code-bit-2");
gpio_direction_input(HW_CODE_BIT_0);
gpio_direction_input(HW_CODE_BIT_1);
gpio_direction_input(HW_CODE_BIT_2);
/* HW 100 + HW 200 = 00b; HW 300 = 01b */
hw_code = ((gpio_get_value(HW_CODE_BIT_2) << 2) |
(gpio_get_value(HW_CODE_BIT_1) << 1) |
gpio_get_value(HW_CODE_BIT_0)) + 2;
return hw_code;
}
int board_late_init(void)
{
u32 hw_code;
char buf[16];
dh_setup_mac_address(NULL);
hw_code = board_get_hwcode();
switch (get_cpu_type()) {
case MXC_CPU_MX6SOLO:
snprintf(buf, sizeof(buf), "imx6s-dhcom%1d", hw_code);
break;
case MXC_CPU_MX6DL:
snprintf(buf, sizeof(buf), "imx6dl-dhcom%1d", hw_code);
break;
case MXC_CPU_MX6D:
snprintf(buf, sizeof(buf), "imx6d-dhcom%1d", hw_code);
break;
case MXC_CPU_MX6Q:
snprintf(buf, sizeof(buf), "imx6q-dhcom%1d", hw_code);
break;
default:
snprintf(buf, sizeof(buf), "UNKNOWN%1d", hw_code);
break;
}
env_set("dhcom", buf);
#ifdef CONFIG_CMD_BMODE
add_board_boot_modes(board_boot_modes);
#endif
return 0;
}
int checkboard(void)
{
puts("Board: DHCOM i.MX6\n");
return 0;
}
#ifdef CONFIG_MULTI_DTB_FIT
static int strcmp_prefix(const char *s1, const char *s2)
{
size_t n;
n = min(strlen(s1), strlen(s2));
return strncmp(s1, s2, n);
}
int board_fit_config_name_match(const char *name)
{
char *want;
char *have;
/* Test Board suffix, e.g. -dhcom-drc02 */
want = strchr(CONFIG_DEFAULT_DEVICE_TREE, '-');
have = strchr(name, '-');
if (!want || !have || strcmp(want, have))
return -EINVAL;
/* Test SoC prefix */
if (is_mx6dq() && !strcmp_prefix(name, "imx6q-"))
return 0;
if (is_mx6sdl()) {
if (!strcmp_prefix(name, "imx6s-") || !strcmp_prefix(name, "imx6dl-"))
return 0;
}
return -EINVAL;
}
#endif
+688
View File
@@ -0,0 +1,688 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* DHCOM DH-iMX6 PDK SPL support
*
* Copyright (C) 2017 Marek Vasut <marex@denx.de>
*/
#include <cpu_func.h>
#include <init.h>
#include <asm/arch/clock.h>
#include <asm/arch/crm_regs.h>
#include <asm/arch/imx-regs.h>
#include <asm/arch/iomux.h>
#include <asm/arch/mx6-ddr.h>
#include <asm/arch/mx6-pins.h>
#include <asm/arch/sys_proto.h>
#include <asm/cache.h>
#include <asm/gpio.h>
#include <asm/mach-imx/boot_mode.h>
#include <asm/mach-imx/iomux-v3.h>
#include <asm/mach-imx/mxc_i2c.h>
#include <asm/io.h>
#include <asm/sections.h>
#include <asm/system.h>
#include <errno.h>
#include <fuse.h>
#include <fsl_esdhc_imx.h>
#include <i2c.h>
#include <mmc.h>
#include <spl.h>
#include <linux/delay.h>
#define ENET_PAD_CTRL \
(PAD_CTL_PUS_100K_UP | PAD_CTL_SPEED_MED | PAD_CTL_DSE_40ohm | \
PAD_CTL_HYS)
#define GPIO_PAD_CTRL \
(PAD_CTL_HYS | PAD_CTL_SPEED_MED | PAD_CTL_DSE_40ohm)
#define SPI_PAD_CTRL \
(PAD_CTL_HYS | PAD_CTL_SPEED_MED | PAD_CTL_DSE_40ohm | \
PAD_CTL_SRE_FAST)
#define UART_PAD_CTRL \
(PAD_CTL_PUS_100K_UP | PAD_CTL_SPEED_MED | PAD_CTL_DSE_40ohm | \
PAD_CTL_SRE_FAST | PAD_CTL_HYS)
#define USDHC_PAD_CTRL \
(PAD_CTL_PUS_47K_UP | PAD_CTL_SPEED_LOW | PAD_CTL_DSE_80ohm | \
PAD_CTL_SRE_FAST | PAD_CTL_HYS)
#define OTG_PAD_CTRL \
(PAD_CTL_PUS_100K_UP | PAD_CTL_SPEED_MED | PAD_CTL_DSE_40ohm | \
PAD_CTL_SRE_SLOW | PAD_CTL_HYS)
static const struct mx6dq_iomux_ddr_regs dhcom6dq_ddr_ioregs = {
.dram_sdclk_0 = 0x00020030,
.dram_sdclk_1 = 0x00020030,
.dram_cas = 0x00020030,
.dram_ras = 0x00020030,
.dram_reset = 0x00020030,
.dram_sdcke0 = 0x00003000,
.dram_sdcke1 = 0x00003000,
.dram_sdba2 = 0x00000000,
.dram_sdodt0 = 0x00003030,
.dram_sdodt1 = 0x00003030,
.dram_sdqs0 = 0x00000030,
.dram_sdqs1 = 0x00000030,
.dram_sdqs2 = 0x00000030,
.dram_sdqs3 = 0x00000030,
.dram_sdqs4 = 0x00000030,
.dram_sdqs5 = 0x00000030,
.dram_sdqs6 = 0x00000030,
.dram_sdqs7 = 0x00000030,
.dram_dqm0 = 0x00020030,
.dram_dqm1 = 0x00020030,
.dram_dqm2 = 0x00020030,
.dram_dqm3 = 0x00020030,
.dram_dqm4 = 0x00020030,
.dram_dqm5 = 0x00020030,
.dram_dqm6 = 0x00020030,
.dram_dqm7 = 0x00020030,
};
static const struct mx6dq_iomux_grp_regs dhcom6dq_grp_ioregs = {
.grp_ddr_type = 0x000C0000,
.grp_ddrmode_ctl = 0x00020000,
.grp_ddrpke = 0x00000000,
.grp_addds = 0x00000030,
.grp_ctlds = 0x00000030,
.grp_ddrmode = 0x00020000,
.grp_b0ds = 0x00000030,
.grp_b1ds = 0x00000030,
.grp_b2ds = 0x00000030,
.grp_b3ds = 0x00000030,
.grp_b4ds = 0x00000030,
.grp_b5ds = 0x00000030,
.grp_b6ds = 0x00000030,
.grp_b7ds = 0x00000030,
};
static const struct mx6sdl_iomux_ddr_regs dhcom6sdl_ddr_ioregs = {
.dram_sdclk_0 = 0x00020030,
.dram_sdclk_1 = 0x00020030,
.dram_cas = 0x00020030,
.dram_ras = 0x00020030,
.dram_reset = 0x00020030,
.dram_sdcke0 = 0x00003000,
.dram_sdcke1 = 0x00003000,
.dram_sdba2 = 0x00000000,
.dram_sdodt0 = 0x00003030,
.dram_sdodt1 = 0x00003030,
.dram_sdqs0 = 0x00000030,
.dram_sdqs1 = 0x00000030,
.dram_sdqs2 = 0x00000030,
.dram_sdqs3 = 0x00000030,
.dram_sdqs4 = 0x00000030,
.dram_sdqs5 = 0x00000030,
.dram_sdqs6 = 0x00000030,
.dram_sdqs7 = 0x00000030,
.dram_dqm0 = 0x00020030,
.dram_dqm1 = 0x00020030,
.dram_dqm2 = 0x00020030,
.dram_dqm3 = 0x00020030,
.dram_dqm4 = 0x00020030,
.dram_dqm5 = 0x00020030,
.dram_dqm6 = 0x00020030,
.dram_dqm7 = 0x00020030,
};
static const struct mx6sdl_iomux_grp_regs dhcom6sdl_grp_ioregs = {
.grp_ddr_type = 0x000C0000,
.grp_ddrmode_ctl = 0x00020000,
.grp_ddrpke = 0x00000000,
.grp_addds = 0x00000030,
.grp_ctlds = 0x00000030,
.grp_ddrmode = 0x00020000,
.grp_b0ds = 0x00000030,
.grp_b1ds = 0x00000030,
.grp_b2ds = 0x00000030,
.grp_b3ds = 0x00000030,
.grp_b4ds = 0x00000030,
.grp_b5ds = 0x00000030,
.grp_b6ds = 0x00000030,
.grp_b7ds = 0x00000030,
};
static const struct mx6_mmdc_calibration dhcom_mmdc_calib_4x4g_1066 = {
.p0_mpwldectrl0 = 0x00150019,
.p0_mpwldectrl1 = 0x001C000B,
.p1_mpwldectrl0 = 0x00020018,
.p1_mpwldectrl1 = 0x0002000C,
.p0_mpdgctrl0 = 0x43140320,
.p0_mpdgctrl1 = 0x03080304,
.p1_mpdgctrl0 = 0x43180320,
.p1_mpdgctrl1 = 0x03100254,
.p0_mprddlctl = 0x4830383C,
.p1_mprddlctl = 0x3836323E,
.p0_mpwrdlctl = 0x3E444642,
.p1_mpwrdlctl = 0x42344442,
};
static const struct mx6_mmdc_calibration dhcom_mmdc_calib_2x4g_800 = {
.p0_mpwldectrl0 = 0x0040003C,
.p0_mpwldectrl1 = 0x0032003E,
.p0_mpdgctrl0 = 0x42350231,
.p0_mpdgctrl1 = 0x021A0218,
.p0_mprddlctl = 0x4B4B4E49,
.p0_mpwrdlctl = 0x3F3F3035,
};
static const struct mx6_mmdc_calibration dhcom_mmdc_calib_4x2g_1066 = {
.p0_mpwldectrl0 = 0x001a001a,
.p0_mpwldectrl1 = 0x00260015,
.p0_mpdgctrl0 = 0x030c0320,
.p0_mpdgctrl1 = 0x03100304,
.p0_mprddlctl = 0x432e3538,
.p0_mpwrdlctl = 0x363f423d,
.p1_mpwldectrl0 = 0x0006001e,
.p1_mpwldectrl1 = 0x00050015,
.p1_mpdgctrl0 = 0x031c0324,
.p1_mpdgctrl1 = 0x030c0258,
.p1_mprddlctl = 0x3834313f,
.p1_mpwrdlctl = 0x47374a42,
};
static const struct mx6_mmdc_calibration dhcom_mmdc_calib_4x2g_800 = {
.p0_mpwldectrl0 = 0x003A003A,
.p0_mpwldectrl1 = 0x0030002F,
.p1_mpwldectrl0 = 0x002F0038,
.p1_mpwldectrl1 = 0x00270039,
.p0_mpdgctrl0 = 0x420F020F,
.p0_mpdgctrl1 = 0x01760175,
.p1_mpdgctrl0 = 0x41640171,
.p1_mpdgctrl1 = 0x015E0160,
.p0_mprddlctl = 0x45464B4A,
.p1_mprddlctl = 0x49484A46,
.p0_mpwrdlctl = 0x40402E32,
.p1_mpwrdlctl = 0x3A3A3231,
};
static const struct mx6_mmdc_calibration dhcom_mmdc_calib_2x2g_800 = {
.p0_mpwldectrl0 = 0x0040003C,
.p0_mpwldectrl1 = 0x0032003E,
.p0_mpdgctrl0 = 0x42350231,
.p0_mpdgctrl1 = 0x021A0218,
.p0_mprddlctl = 0x4B4B4E49,
.p0_mpwrdlctl = 0x3F3F3035,
};
/*
* 2 Gbit DDR3 memory
* - NANYA #NT5CC128M16IP-DII
* - NANYA #NT5CB128M16FP-DII
*/
static const struct mx6_ddr3_cfg dhcom_mem_ddr_2g = {
.mem_speed = 1600,
.density = 2,
.width = 16,
.banks = 8,
.rowaddr = 14,
.coladdr = 10,
.pagesz = 2,
.trcd = 1375,
.trcmin = 5863,
.trasmin = 3750,
};
/*
* 4 Gbit DDR3 memory
* - Intelligent Memory #IM4G16D3EABG-125I
*/
static const struct mx6_ddr3_cfg dhcom_mem_ddr_4g = {
.mem_speed = 1600,
.density = 4,
.width = 16,
.banks = 8,
.rowaddr = 15,
.coladdr = 10,
.pagesz = 2,
.trcd = 1375,
.trcmin = 4875,
.trasmin = 3500,
};
/* DDR3 64bit */
static const struct mx6_ddr_sysinfo dhcom_ddr_64bit = {
/* width of data bus:0=16,1=32,2=64 */
.dsize = 2,
.cs_density = 32,
.ncs = 1, /* single chip select */
.cs1_mirror = 1,
.rtt_wr = 1, /* DDR3_RTT_60_OHM, RTT_Wr = RZQ/4 */
.rtt_nom = 1, /* DDR3_RTT_60_OHM, RTT_Nom = RZQ/4 */
.walat = 1, /* Write additional latency */
.ralat = 5, /* Read additional latency */
.mif3_mode = 3, /* Command prediction working mode */
.bi_on = 1, /* Bank interleaving enabled */
.sde_to_rst = 0x10, /* 14 cycles, 200us (JEDEC default) */
.rst_to_cke = 0x23, /* 33 cycles, 500us (JEDEC default) */
.refsel = 1, /* Refresh cycles at 32KHz */
.refr = 3, /* 4 refresh commands per refresh cycle */
};
/* DDR3 32bit */
static const struct mx6_ddr_sysinfo dhcom_ddr_32bit = {
/* width of data bus:0=16,1=32,2=64 */
.dsize = 1,
.cs_density = 32,
.ncs = 1, /* single chip select */
.cs1_mirror = 1,
.rtt_wr = 1, /* DDR3_RTT_60_OHM, RTT_Wr = RZQ/4 */
.rtt_nom = 1, /* DDR3_RTT_60_OHM, RTT_Nom = RZQ/4 */
.walat = 1, /* Write additional latency */
.ralat = 5, /* Read additional latency */
.mif3_mode = 3, /* Command prediction working mode */
.bi_on = 1, /* Bank interleaving enabled */
.sde_to_rst = 0x10, /* 14 cycles, 200us (JEDEC default) */
.rst_to_cke = 0x23, /* 33 cycles, 500us (JEDEC default) */
.refsel = 1, /* Refresh cycles at 32KHz */
.refr = 3, /* 4 refresh commands per refresh cycle */
};
static void ccgr_init(void)
{
struct mxc_ccm_reg *ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR;
writel(0x00C03F3F, &ccm->CCGR0);
writel(0x0030FC03, &ccm->CCGR1);
writel(0x0FFFC000, &ccm->CCGR2);
writel(0x3FF00000, &ccm->CCGR3);
writel(0x00FFF300, &ccm->CCGR4);
writel(0x0F0000C3, &ccm->CCGR5);
writel(0x000003FF, &ccm->CCGR6);
}
/* Board ID */
static iomux_v3_cfg_t const hwcode_pads[] = {
IOMUX_PADS(PAD_EIM_A19__GPIO2_IO19 | MUX_PAD_CTRL(GPIO_PAD_CTRL)),
IOMUX_PADS(PAD_EIM_A23__GPIO6_IO06 | MUX_PAD_CTRL(GPIO_PAD_CTRL)),
IOMUX_PADS(PAD_EIM_A22__GPIO2_IO16 | MUX_PAD_CTRL(GPIO_PAD_CTRL)),
};
static void setup_iomux_boardid(void)
{
/* HW code pins: Setup alternate function and configure pads */
SETUP_IOMUX_PADS(hwcode_pads);
}
/* DDR Code */
static iomux_v3_cfg_t const ddrcode_pads[] = {
IOMUX_PADS(PAD_EIM_A16__GPIO2_IO22 | MUX_PAD_CTRL(GPIO_PAD_CTRL)),
IOMUX_PADS(PAD_EIM_A17__GPIO2_IO21 | MUX_PAD_CTRL(GPIO_PAD_CTRL)),
};
static void setup_iomux_ddrcode(void)
{
/* ddr code pins */
SETUP_IOMUX_PADS(ddrcode_pads);
}
enum dhcom_ddr3_code {
DH_DDR3_SIZE_256MIB = 0x00,
DH_DDR3_SIZE_512MIB = 0x01,
DH_DDR3_SIZE_1GIB = 0x02,
DH_DDR3_SIZE_2GIB = 0x03
};
#define DDR3_CODE_BIT_0 IMX_GPIO_NR(2, 22)
#define DDR3_CODE_BIT_1 IMX_GPIO_NR(2, 21)
enum dhcom_ddr3_code dhcom_get_ddr3_code(void)
{
enum dhcom_ddr3_code ddr3_code;
gpio_request(DDR3_CODE_BIT_0, "DDR3_CODE_BIT_0");
gpio_request(DDR3_CODE_BIT_1, "DDR3_CODE_BIT_1");
gpio_direction_input(DDR3_CODE_BIT_0);
gpio_direction_input(DDR3_CODE_BIT_1);
/* 256MB = 0b00; 512MB = 0b01; 1GB = 0b10; 2GB = 0b11 */
ddr3_code = (!!gpio_get_value(DDR3_CODE_BIT_1) << 1)
| (!!gpio_get_value(DDR3_CODE_BIT_0));
return ddr3_code;
}
/* GPIO */
static iomux_v3_cfg_t const gpio_pads[] = {
IOMUX_PADS(PAD_GPIO_2__GPIO1_IO02 | MUX_PAD_CTRL(GPIO_PAD_CTRL)),
IOMUX_PADS(PAD_GPIO_4__GPIO1_IO04 | MUX_PAD_CTRL(GPIO_PAD_CTRL)),
IOMUX_PADS(PAD_GPIO_5__GPIO1_IO05 | MUX_PAD_CTRL(GPIO_PAD_CTRL)),
IOMUX_PADS(PAD_CSI0_DAT17__GPIO6_IO03 | MUX_PAD_CTRL(GPIO_PAD_CTRL)),
IOMUX_PADS(PAD_GPIO_19__GPIO4_IO05 | MUX_PAD_CTRL(GPIO_PAD_CTRL)),
IOMUX_PADS(PAD_DI0_PIN4__GPIO4_IO20 | MUX_PAD_CTRL(GPIO_PAD_CTRL)),
IOMUX_PADS(PAD_EIM_D27__GPIO3_IO27 | MUX_PAD_CTRL(GPIO_PAD_CTRL)),
IOMUX_PADS(PAD_KEY_ROW0__GPIO4_IO07 | MUX_PAD_CTRL(GPIO_PAD_CTRL)),
IOMUX_PADS(PAD_KEY_COL1__GPIO4_IO08 | MUX_PAD_CTRL(GPIO_PAD_CTRL)),
IOMUX_PADS(PAD_NANDF_CS1__GPIO6_IO14 | MUX_PAD_CTRL(GPIO_PAD_CTRL)),
IOMUX_PADS(PAD_NANDF_CS2__GPIO6_IO15 | MUX_PAD_CTRL(GPIO_PAD_CTRL)),
IOMUX_PADS(PAD_KEY_ROW1__GPIO4_IO09 | MUX_PAD_CTRL(GPIO_PAD_CTRL)),
IOMUX_PADS(PAD_SD3_DAT5__GPIO7_IO00 | MUX_PAD_CTRL(GPIO_PAD_CTRL)),
IOMUX_PADS(PAD_SD3_DAT4__GPIO7_IO01 | MUX_PAD_CTRL(GPIO_PAD_CTRL)),
IOMUX_PADS(PAD_CSI0_VSYNC__GPIO5_IO21 | MUX_PAD_CTRL(GPIO_PAD_CTRL)),
IOMUX_PADS(PAD_GPIO_18__GPIO7_IO13 | MUX_PAD_CTRL(GPIO_PAD_CTRL)),
IOMUX_PADS(PAD_SD1_CMD__GPIO1_IO18 | MUX_PAD_CTRL(GPIO_PAD_CTRL)),
IOMUX_PADS(PAD_SD1_DAT0__GPIO1_IO16 | MUX_PAD_CTRL(GPIO_PAD_CTRL)),
IOMUX_PADS(PAD_SD1_DAT1__GPIO1_IO17 | MUX_PAD_CTRL(GPIO_PAD_CTRL)),
IOMUX_PADS(PAD_SD1_DAT2__GPIO1_IO19 | MUX_PAD_CTRL(GPIO_PAD_CTRL)),
IOMUX_PADS(PAD_SD1_CLK__GPIO1_IO20 | MUX_PAD_CTRL(GPIO_PAD_CTRL)),
IOMUX_PADS(PAD_CSI0_PIXCLK__GPIO5_IO18 | MUX_PAD_CTRL(GPIO_PAD_CTRL)),
IOMUX_PADS(PAD_CSI0_MCLK__GPIO5_IO19 | MUX_PAD_CTRL(GPIO_PAD_CTRL)),
};
static void setup_iomux_gpio(void)
{
SETUP_IOMUX_PADS(gpio_pads);
}
/* Ethernet */
static iomux_v3_cfg_t const enet_pads[] = {
IOMUX_PADS(PAD_ENET_MDIO__ENET_MDIO | MUX_PAD_CTRL(ENET_PAD_CTRL)),
IOMUX_PADS(PAD_ENET_MDC__ENET_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL)),
IOMUX_PADS(PAD_ENET_TX_EN__ENET_TX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL)),
IOMUX_PADS(PAD_ENET_TXD0__ENET_TX_DATA0 | MUX_PAD_CTRL(ENET_PAD_CTRL)),
IOMUX_PADS(PAD_ENET_TXD1__ENET_TX_DATA1 | MUX_PAD_CTRL(ENET_PAD_CTRL)),
IOMUX_PADS(PAD_GPIO_16__ENET_REF_CLK | MUX_PAD_CTRL(ENET_PAD_CTRL)),
IOMUX_PADS(PAD_ENET_RX_ER__ENET_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL)),
IOMUX_PADS(PAD_ENET_RXD0__ENET_RX_DATA0 | MUX_PAD_CTRL(ENET_PAD_CTRL)),
IOMUX_PADS(PAD_ENET_RXD1__ENET_RX_DATA1 | MUX_PAD_CTRL(ENET_PAD_CTRL)),
IOMUX_PADS(PAD_ENET_CRS_DV__ENET_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL)),
/* SMSC PHY Reset */
IOMUX_PADS(PAD_EIM_WAIT__GPIO5_IO00 | MUX_PAD_CTRL(NO_PAD_CTRL)),
/* ENET_VIO_GPIO */
IOMUX_PADS(PAD_GPIO_7__GPIO1_IO07 | MUX_PAD_CTRL(NO_PAD_CTRL)),
/* ENET_Interrupt - (not used) */
IOMUX_PADS(PAD_RGMII_RD0__GPIO6_IO25 | MUX_PAD_CTRL(NO_PAD_CTRL)),
};
static void setup_iomux_enet(void)
{
SETUP_IOMUX_PADS(enet_pads);
}
/* SD interface */
static iomux_v3_cfg_t const usdhc2_pads[] = {
IOMUX_PADS(PAD_SD2_DAT0__SD2_DATA0 | MUX_PAD_CTRL(USDHC_PAD_CTRL)),
IOMUX_PADS(PAD_SD2_DAT1__SD2_DATA1 | MUX_PAD_CTRL(USDHC_PAD_CTRL)),
IOMUX_PADS(PAD_SD2_DAT2__SD2_DATA2 | MUX_PAD_CTRL(USDHC_PAD_CTRL)),
IOMUX_PADS(PAD_SD2_DAT3__SD2_DATA3 | MUX_PAD_CTRL(USDHC_PAD_CTRL)),
IOMUX_PADS(PAD_SD2_CLK__SD2_CLK | MUX_PAD_CTRL(USDHC_PAD_CTRL)),
IOMUX_PADS(PAD_SD2_CMD__SD2_CMD | MUX_PAD_CTRL(USDHC_PAD_CTRL)),
IOMUX_PADS(PAD_NANDF_CS3__GPIO6_IO16 | MUX_PAD_CTRL(NO_PAD_CTRL)), /* CD */
};
/* onboard microSD */
static iomux_v3_cfg_t const usdhc3_pads[] = {
IOMUX_PADS(PAD_SD3_DAT0__SD3_DATA0 | MUX_PAD_CTRL(USDHC_PAD_CTRL)),
IOMUX_PADS(PAD_SD3_DAT1__SD3_DATA1 | MUX_PAD_CTRL(USDHC_PAD_CTRL)),
IOMUX_PADS(PAD_SD3_DAT2__SD3_DATA2 | MUX_PAD_CTRL(USDHC_PAD_CTRL)),
IOMUX_PADS(PAD_SD3_DAT3__SD3_DATA3 | MUX_PAD_CTRL(USDHC_PAD_CTRL)),
IOMUX_PADS(PAD_SD3_CLK__SD3_CLK | MUX_PAD_CTRL(USDHC_PAD_CTRL)),
IOMUX_PADS(PAD_SD3_CMD__SD3_CMD | MUX_PAD_CTRL(USDHC_PAD_CTRL)),
IOMUX_PADS(PAD_SD3_RST__GPIO7_IO08 | MUX_PAD_CTRL(NO_PAD_CTRL)), /* CD */
};
/* eMMC */
static iomux_v3_cfg_t const usdhc4_pads[] = {
IOMUX_PADS(PAD_SD4_DAT0__SD4_DATA0 | MUX_PAD_CTRL(USDHC_PAD_CTRL)),
IOMUX_PADS(PAD_SD4_DAT1__SD4_DATA1 | MUX_PAD_CTRL(USDHC_PAD_CTRL)),
IOMUX_PADS(PAD_SD4_DAT2__SD4_DATA2 | MUX_PAD_CTRL(USDHC_PAD_CTRL)),
IOMUX_PADS(PAD_SD4_DAT3__SD4_DATA3 | MUX_PAD_CTRL(USDHC_PAD_CTRL)),
IOMUX_PADS(PAD_SD4_DAT4__SD4_DATA4 | MUX_PAD_CTRL(USDHC_PAD_CTRL)),
IOMUX_PADS(PAD_SD4_DAT5__SD4_DATA5 | MUX_PAD_CTRL(USDHC_PAD_CTRL)),
IOMUX_PADS(PAD_SD4_DAT6__SD4_DATA6 | MUX_PAD_CTRL(USDHC_PAD_CTRL)),
IOMUX_PADS(PAD_SD4_DAT7__SD4_DATA7 | MUX_PAD_CTRL(USDHC_PAD_CTRL)),
IOMUX_PADS(PAD_SD4_CLK__SD4_CLK | MUX_PAD_CTRL(USDHC_PAD_CTRL)),
IOMUX_PADS(PAD_SD4_CMD__SD4_CMD | MUX_PAD_CTRL(USDHC_PAD_CTRL)),
};
/* SD */
static void setup_iomux_sd(void)
{
SETUP_IOMUX_PADS(usdhc2_pads);
SETUP_IOMUX_PADS(usdhc3_pads);
SETUP_IOMUX_PADS(usdhc4_pads);
}
/* SPI */
static iomux_v3_cfg_t const ecspi1_pads[] = {
/* SS0 - SS of boot flash */
IOMUX_PADS(PAD_EIM_EB2__GPIO2_IO30 |
MUX_PAD_CTRL(SPI_PAD_CTRL | PAD_CTL_PUS_100K_UP)),
/* SS2 - SS of DHCOM SPI1 */
IOMUX_PADS(PAD_KEY_ROW2__GPIO4_IO11 |
MUX_PAD_CTRL(SPI_PAD_CTRL | PAD_CTL_PUS_100K_UP)),
IOMUX_PADS(PAD_EIM_D17__ECSPI1_MISO | MUX_PAD_CTRL(SPI_PAD_CTRL)),
IOMUX_PADS(PAD_EIM_D18__ECSPI1_MOSI | MUX_PAD_CTRL(SPI_PAD_CTRL)),
IOMUX_PADS(PAD_EIM_D16__ECSPI1_SCLK | MUX_PAD_CTRL(SPI_PAD_CTRL)),
};
static void setup_iomux_spi(void)
{
SETUP_IOMUX_PADS(ecspi1_pads);
}
int board_spi_cs_gpio(unsigned bus, unsigned cs)
{
if (bus == 0 && cs == 0)
return IMX_GPIO_NR(2, 30);
else
return -1;
}
/* UART */
static iomux_v3_cfg_t const uart1_pads[] = {
IOMUX_PADS(PAD_SD3_DAT7__UART1_TX_DATA | MUX_PAD_CTRL(UART_PAD_CTRL)),
IOMUX_PADS(PAD_SD3_DAT6__UART1_RX_DATA | MUX_PAD_CTRL(UART_PAD_CTRL)),
};
static void setup_iomux_uart(void)
{
SETUP_IOMUX_PADS(uart1_pads);
}
#ifdef CONFIG_FSL_USDHC
struct fsl_esdhc_cfg usdhc_cfg[1] = {
{USDHC4_BASE_ADDR},
};
int board_mmc_get_env_dev(int devno)
{
return devno - 1;
}
int board_mmc_getcd(struct mmc *mmc)
{
return 1; /* eMMC/uSDHC4 is always present */
}
int board_mmc_init(struct bd_info *bis)
{
SETUP_IOMUX_PADS(usdhc4_pads);
usdhc_cfg[0].esdhc_base = USDHC4_BASE_ADDR;
usdhc_cfg[0].sdhc_clk = mxc_get_clock(MXC_ESDHC4_CLK);
usdhc_cfg[0].max_bus_width = 8;
return fsl_esdhc_initialize(bis, &usdhc_cfg[0]);
}
#endif
/* USB */
static iomux_v3_cfg_t const usb_pads[] = {
IOMUX_PADS(PAD_GPIO_1__USB_OTG_ID | MUX_PAD_CTRL(OTG_PAD_CTRL)),
IOMUX_PADS(PAD_EIM_D31__GPIO3_IO31 | MUX_PAD_CTRL(NO_PAD_CTRL)),
};
static void setup_iomux_usb(void)
{
SETUP_IOMUX_PADS(usb_pads);
}
/* Perform DDR DRAM calibration */
static int spl_dram_perform_cal(struct mx6_ddr_sysinfo const *sysinfo)
{
int ret = 0;
#ifdef CONFIG_MX6_DDRCAL
udelay(100);
ret = mmdc_do_write_level_calibration(sysinfo);
if (ret) {
printf("DDR3: Write level calibration error [%d]\n", ret);
return ret;
}
ret = mmdc_do_dqs_calibration(sysinfo);
if (ret) {
printf("DDR3: DQS calibration error [%d]\n", ret);
return ret;
}
#endif /* CONFIG_MX6_DDRCAL */
return ret;
}
/* DRAM */
static void dhcom_spl_dram_init(void)
{
enum dhcom_ddr3_code ddr3_code = dhcom_get_ddr3_code();
if (is_mx6dq()) {
mx6dq_dram_iocfg(64, &dhcom6dq_ddr_ioregs,
&dhcom6dq_grp_ioregs);
switch (ddr3_code) {
default:
printf("imx6qd: unsupported ddr3 code %d\n", ddr3_code);
printf(" choosing 1024 MB\n");
/* fall through */
case DH_DDR3_SIZE_1GIB:
mx6_dram_cfg(&dhcom_ddr_64bit,
&dhcom_mmdc_calib_4x2g_1066,
&dhcom_mem_ddr_2g);
break;
case DH_DDR3_SIZE_2GIB:
mx6_dram_cfg(&dhcom_ddr_64bit,
&dhcom_mmdc_calib_4x4g_1066,
&dhcom_mem_ddr_4g);
break;
}
/* Perform DDR DRAM calibration */
spl_dram_perform_cal(&dhcom_ddr_64bit);
} else if (is_cpu_type(MXC_CPU_MX6DL)) {
mx6sdl_dram_iocfg(64, &dhcom6sdl_ddr_ioregs,
&dhcom6sdl_grp_ioregs);
switch (ddr3_code) {
default:
printf("imx6dl: unsupported ddr3 code %d\n", ddr3_code);
printf(" choosing 1024 MB\n");
/* fall through */
case DH_DDR3_SIZE_1GIB:
mx6_dram_cfg(&dhcom_ddr_64bit,
&dhcom_mmdc_calib_4x2g_800,
&dhcom_mem_ddr_2g);
break;
}
/* Perform DDR DRAM calibration */
spl_dram_perform_cal(&dhcom_ddr_64bit);
} else if (is_cpu_type(MXC_CPU_MX6SOLO)) {
mx6sdl_dram_iocfg(32, &dhcom6sdl_ddr_ioregs,
&dhcom6sdl_grp_ioregs);
switch (ddr3_code) {
default:
printf("imx6s: unsupported ddr3 code %d\n", ddr3_code);
printf(" choosing 512 MB\n");
/* fall through */
case DH_DDR3_SIZE_512MIB:
mx6_dram_cfg(&dhcom_ddr_32bit,
&dhcom_mmdc_calib_2x2g_800,
&dhcom_mem_ddr_2g);
break;
case DH_DDR3_SIZE_1GIB:
mx6_dram_cfg(&dhcom_ddr_32bit,
&dhcom_mmdc_calib_2x4g_800,
&dhcom_mem_ddr_4g);
break;
}
/* Perform DDR DRAM calibration */
spl_dram_perform_cal(&dhcom_ddr_32bit);
}
}
void dram_bank_mmu_setup(int bank)
{
int i;
set_section_dcache(ROMCP_ARB_BASE_ADDR >> MMU_SECTION_SHIFT, DCACHE_DEFAULT_OPTION);
set_section_dcache(IRAM_BASE_ADDR >> MMU_SECTION_SHIFT, DCACHE_DEFAULT_OPTION);
for (i = MMDC0_ARB_BASE_ADDR >> MMU_SECTION_SHIFT;
i < ((MMDC0_ARB_BASE_ADDR >> MMU_SECTION_SHIFT) +
(SZ_1G >> MMU_SECTION_SHIFT));
i++)
set_section_dcache(i, DCACHE_DEFAULT_OPTION);
}
void board_init_f(ulong dummy)
{
/* setup AIPS and disable watchdog */
arch_cpu_init();
ccgr_init();
gpr_init();
/* setup GP timer */
timer_init();
setup_iomux_boardid();
setup_iomux_ddrcode();
setup_iomux_gpio();
setup_iomux_enet();
setup_iomux_sd();
setup_iomux_spi();
setup_iomux_uart();
setup_iomux_usb();
/* UART clocks enabled and gd valid - init serial console */
preloader_console_init();
/* DDR3 initialization */
dhcom_spl_dram_init();
/* Set up early MMU tables at the beginning of DRAM and start d-cache */
gd->arch.tlb_addr = MMDC0_ARB_BASE_ADDR + SZ_32M;
gd->arch.tlb_size = PGTABLE_SIZE;
enable_caches();
/* Clear the BSS. */
memset(__bss_start, 0, __bss_end - __bss_start);
/* load/boot image from boot device */
board_init_r(NULL, 0);
}
void spl_board_prepare_for_boot(void)
{
/*
* Flush and disable dcache. Without it, the following bootstage might fail randomly because
* dirty cache lines may not have been written back to DRAM.
*
* If dcache_disable() would be omitted, the following scenario may occur:
*
* The SPL enables dcache and cachelines get populated with data. Then dcache gets disabled
* in U-Boot proper, but still contains dirty data, i.e. the corresponding DRAM locations
* have not yet been updated. When U-Boot reads these locations, it sees an (incorrect) old
* state of the content.
*
* Furthermore, the DRAM contents have likely been modified by U-Boot while dcache was
* disabled. Thus, U-Boot flushing dcache would corrupt DRAM with stale data.
*/
dcache_disable(); /* implies flush_dcache_all() */
}
+15
View File
@@ -0,0 +1,15 @@
if TARGET_IMX8MP_DH_DHCOM_PDK2
config SYS_BOARD
default "dh_imx8mp"
config SYS_VENDOR
default "dhelectronics"
config SYS_CONFIG_NAME
default "imx8mp_dhcom_pdk2"
config IMX_CONFIG
default "board/dhelectronics/dh_imx8mp/imximage-lpddr4.cfg"
endif
@@ -0,0 +1,6 @@
DH electronics DHCOM i.MX8M Plus and matching carrier boards
M: Marek Vasut <marex@denx.de>
L: u-boot@dh-electronics.com
S: Maintained
N: imx8mp.*dh[cs]o
N: dh_imx8mp
+13
View File
@@ -0,0 +1,13 @@
#
# Copyright (C) 2022 Marek Vasut <marex@denx.de>
#
# SPDX-License-Identifier: GPL-2.0+
#
ifdef CONFIG_XPL_BUILD
obj-y += spl.o lpddr4_timing_2G_32.o
else
obj-y += imx8mp_dhcom_pdk2.o
endif
obj-y += common.o
+34
View File
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2022 Marek Vasut <marex@denx.de>
*/
#include <asm/io.h>
#include <asm-generic/gpio.h>
#include "lpddr4_timing.h"
u8 dh_get_memcfg(void)
{
struct gpio_desc gpio[4];
u8 memcfg = 0;
ofnode node;
int i, ret;
node = ofnode_path("/config");
if (!ofnode_valid(node)) {
printf("%s: no /config node?\n", __func__);
return BIT(2) | BIT(0);
}
ret = gpio_request_list_by_name_nodev(node,
"dh,ram-coding-gpios",
gpio, ARRAY_SIZE(gpio),
GPIOD_IS_IN);
for (i = 0; i < ret; i++)
memcfg |= !!dm_gpio_get_value(&(gpio[i])) << i;
gpio_free_list_nodev(gpio, ret);
return memcfg;
}
@@ -0,0 +1,147 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2022 Marek Vasut <marex@denx.de>
*/
#include <asm/arch/clock.h>
#include <asm/arch/ddr.h>
#include <asm/arch/sys_proto.h>
#include <asm/io.h>
#include <dm.h>
#include <env.h>
#include <env_internal.h>
#include <i2c_eeprom.h>
#include <malloc.h>
#include <net.h>
#include <miiphy.h>
#include <power/regulator.h>
#include "lpddr4_timing.h"
#include "../common/dh_common.h"
#include "../common/dh_imx.h"
int mach_cpu_init(void)
{
icache_enable();
return 0;
}
int board_phys_sdram_size(phys_size_t *size)
{
const u8 ecc = readl(DDRC_ECCCFG0(0)) & DDRC_ECCCFG0_ECC_MODE_MASK;
u8 memcfg = dh_get_memcfg();
/* 896 kiB, i.e. 1 MiB without 12.5% reserved for in-band ECC */
*size = (u64)dh_imx8mp_dhcom_dram_size[memcfg] * (SZ_1M - (ecc ? (SZ_1M / 8) : 0));
return 0;
}
static int dh_imx8_setup_ethaddr(struct eeprom_id_page *eip)
{
unsigned char enetaddr[6];
if (dh_mac_is_in_env("ethaddr"))
return 0;
if (dh_get_mac_is_enabled("ethernet0"))
return 0;
if (!dh_imx_get_mac_from_fuse(enetaddr, 0))
goto out;
if (!dh_get_value_from_eeprom_buffer(DH_MAC0, enetaddr, sizeof(enetaddr), eip))
goto out;
if (!dh_get_mac_from_eeprom(enetaddr, "eeprom0"))
goto out;
return -ENXIO;
out:
return eth_env_set_enetaddr("ethaddr", enetaddr);
}
static int dh_imx8_setup_eth1addr(struct eeprom_id_page *eip)
{
unsigned char enetaddr[6];
if (dh_mac_is_in_env("eth1addr"))
return 0;
if (dh_get_mac_is_enabled("ethernet1"))
return 0;
if (!dh_imx_get_mac_from_fuse(enetaddr, 1))
goto out;
if (!dh_get_value_from_eeprom_buffer(DH_MAC1, enetaddr, sizeof(enetaddr), eip))
goto out;
if (!dh_get_mac_from_eeprom(enetaddr, "eeprom1"))
goto out;
/*
* Populate second ethernet MAC from first ethernet EEPROM with MAC
* address LSByte incremented by 1. This is only used on SoMs without
* second ethernet EEPROM, i.e. early prototypes.
*/
if (!dh_get_mac_from_eeprom(enetaddr, "eeprom0"))
goto increment_out;
return -ENXIO;
increment_out:
enetaddr[5]++;
out:
return eth_env_set_enetaddr("eth1addr", enetaddr);
}
int dh_setup_mac_address(struct eeprom_id_page *eip)
{
int ret;
ret = dh_imx8_setup_ethaddr(eip);
if (ret)
printf("%s: Unable to setup ethaddr! ret = %d\n", __func__, ret);
ret = dh_imx8_setup_eth1addr(eip);
if (ret)
printf("%s: Unable to setup eth1addr! ret = %d\n", __func__, ret);
return ret;
}
int board_late_init(void)
{
u8 eeprom_buffer[DH_EEPROM_ID_PAGE_MAX_SIZE] = { 0 };
struct eeprom_id_page *eip = (struct eeprom_id_page *)eeprom_buffer;
int ret;
ret = dh_read_eeprom_id_page(eeprom_buffer, "eeprom0wl");
if (ret) {
/*
* The EEPROM ID page is available on SoM rev. 200 and greater.
* For SoM rev. 100 the return value will be -ENODEV. Suppress
* the error message for that, because the absence cannot be
* treated as an error.
*/
if (ret != -ENODEV)
printf("%s: Cannot read valid data from EEPROM ID page! ret = %d\n",
__func__, ret);
dh_setup_mac_address(NULL);
} else {
dh_setup_mac_address(eip);
dh_add_item_number_and_serial_to_env(eip);
}
return 0;
}
enum env_location env_get_location(enum env_operation op, int prio)
{
return prio ? ENVL_UNKNOWN : CONFIG_IS_ENABLED(ENV_IS_IN_SPI_FLASH,
(ENVL_SPI_FLASH),
(ENVL_NOWHERE));
}
@@ -0,0 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright 2021 NXP
*/
ROM_VERSION v2
BOOT_FROM sd
LOADER u-boot-spl-ddr.bin 0x920000
@@ -0,0 +1,24 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright 2022 Marek Vasut <marex@denx.de>
*/
#ifndef __LPDDR4_TIMING_H__
#define __LPDDR4_TIMING_H__
static const u16 dh_imx8mp_dhcom_dram_size[] = {
4096, 1024, 1536, 2048, 3072, 4096, 6144, 8192
};
extern struct dram_timing_info dh_imx8mp_dhcom_dram_timing_16g_x32;
static __maybe_unused struct dram_timing_info *dh_imx8mp_dhcom_dram_timing =
&dh_imx8mp_dhcom_dram_timing_16g_x32;
void dh_imx8mp_dhcom_dram_patch_16g_x32_to_16g_x32(void);
void dh_imx8mp_dhcom_dram_patch_16g_x32_to_32g_x32_2r(void);
void dh_imx8mp_dhcom_dram_patch_16g_x32_to_32g_x32_1r(void);
u8 dh_get_memcfg(void);
#define DDRC_ECCCFG0_ECC_MODE_MASK 0x7
#endif /* __LPDDR4_TIMING_H__ */
File diff suppressed because it is too large Load Diff
+305
View File
@@ -0,0 +1,305 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2022 Marek Vasut <marex@denx.de>
*/
#include <hang.h>
#include <image.h>
#include <init.h>
#include <spl.h>
#include <asm/io.h>
#include <asm-generic/gpio.h>
#include <asm/arch/clock.h>
#include <asm/arch/ddr.h>
#include <asm/arch/imx8mp_pins.h>
#include <asm/arch/sys_proto.h>
#include <asm/mach-imx/boot_mode.h>
#include <asm/arch/ddr.h>
#include <asm/sections.h>
#include <dm/uclass.h>
#include <dm/device.h>
#include <dm/uclass-internal.h>
#include <dm/device-internal.h>
#include <linux/bitfield.h>
#include <power/pmic.h>
#include <power/pca9450.h>
#include "lpddr4_timing.h"
#define UART_PAD_CTRL (PAD_CTL_DSE6 | PAD_CTL_FSEL1)
#define WDOG_PAD_CTRL (PAD_CTL_DSE6 | PAD_CTL_ODE | PAD_CTL_PUE | PAD_CTL_PE)
static const iomux_v3_cfg_t uart_pads[] = {
MX8MP_PAD_SAI2_RXFS__UART1_DCE_TX | MUX_PAD_CTRL(UART_PAD_CTRL),
MX8MP_PAD_SAI2_RXC__UART1_DCE_RX | MUX_PAD_CTRL(UART_PAD_CTRL),
};
static const iomux_v3_cfg_t wdog_pads[] = {
MX8MP_PAD_GPIO1_IO02__WDOG1_WDOG_B | MUX_PAD_CTRL(WDOG_PAD_CTRL),
};
static bool dh_gigabit_eqos, dh_gigabit_fec;
static u8 dh_som_rev;
static void dh_imx8mp_early_init_f(void)
{
struct wdog_regs *wdog = (struct wdog_regs *)WDOG1_BASE_ADDR;
imx_iomux_v3_setup_multiple_pads(wdog_pads, ARRAY_SIZE(wdog_pads));
set_wdog_reset(wdog);
imx_iomux_v3_setup_multiple_pads(uart_pads, ARRAY_SIZE(uart_pads));
}
static int dh_imx8mp_board_power_init(void)
{
struct udevice *dev;
int ret;
ret = pmic_get("pmic@25", &dev);
if (ret == -ENODEV) {
puts("Failed to get PMIC\n");
return 0;
}
if (ret != 0)
return ret;
/* BUCKxOUT_DVS0/1 control BUCK123 output. */
pmic_reg_write(dev, PCA9450_BUCK123_DVS, 0x29);
/* Increase VDD_SOC to typical value 0.95V before first DRAM access. */
if (IS_ENABLED(CONFIG_IMX8M_VDD_SOC_850MV))
/* Set DVS0 to 0.85V for special case. */
pmic_reg_write(dev, PCA9450_BUCK1OUT_DVS0, 0x14);
else
pmic_reg_write(dev, PCA9450_BUCK1OUT_DVS0, 0x1c);
/* Set DVS1 to 0.85v for suspend. */
pmic_reg_write(dev, PCA9450_BUCK1OUT_DVS1, 0x14);
/*
* Enable DVS control through PMIC_STBY_REQ and
* set B1_ENMODE=1 (ON by PMIC_ON_REQ=H).
*/
pmic_reg_write(dev, PCA9450_BUCK1CTRL, 0x59);
/* Kernel uses OD/OD frequency for SoC. */
/* To avoid timing risk from SoC to ARM, increase VDD_ARM to OD voltage 0.95V */
pmic_reg_write(dev, PCA9450_BUCK2OUT_DVS0, 0x1c);
/* DRAM Vdd1 always FPWM */
pmic_reg_write(dev, PCA9450_BUCK5CTRL, 0x0d);
/* DRAM Vdd2/Vddq always FPWM */
pmic_reg_write(dev, PCA9450_BUCK6CTRL, 0x0d);
/* Set LDO4 and CONFIG2 to enable the I2C level translator. */
pmic_reg_write(dev, PCA9450_LDO4CTRL, 0x59);
pmic_reg_write(dev, PCA9450_CONFIG2, 0x1);
return 0;
}
typedef void (*patch_func_t)(void);
static const patch_func_t dram_patch_fn[8] = {
dh_imx8mp_dhcom_dram_patch_16g_x32_to_32g_x32_1r, /* 4096 MiB 1-rank */
NULL, /* 1024 MiB */
NULL, /* 1536 MiB */
dh_imx8mp_dhcom_dram_patch_16g_x32_to_16g_x32, /* 2048 MiB */
NULL, /* 3072 MiB */
dh_imx8mp_dhcom_dram_patch_16g_x32_to_32g_x32_2r, /* 4096 MiB 2-rank */
NULL, /* 6144 MiB */
NULL, /* 8192 MiB */
};
static void spl_dram_init(void)
{
u8 memcfg = dh_get_memcfg();
int i;
printf("DDR: %d MiB [0x%x]\n", dh_imx8mp_dhcom_dram_size[memcfg], memcfg);
if (!dram_patch_fn[memcfg]) {
printf("Unsupported DRAM strapping, trying lowest supported. MEMCFG=0x%x\n",
memcfg);
for (i = 0; i < ARRAY_SIZE(dram_patch_fn); i++)
if (dram_patch_fn[i]) /* Configuration found */
break;
}
dram_patch_fn[memcfg]();
ddr_init(dh_imx8mp_dhcom_dram_timing);
printf("DDR: Inline ECC %sabled\n",
(readl(DDRC_ECCCFG0(0)) & DDRC_ECCCFG0_ECC_MODE_MASK) ?
"en" : "dis");
}
#if IS_ENABLED(CONFIG_IMX8M_DRAM_INLINE_ECC)
static void dh_imx8mp_dhcom_dram_scrub_16g_x32(void)
{
ddrc_inline_ecc_scrub(0x0,0x3ffffff);
ddrc_inline_ecc_scrub(0x4000000,0x7ffffff);
ddrc_inline_ecc_scrub(0x8000000,0xbffffff);
ddrc_inline_ecc_scrub(0xc000000,0xfffffff);
ddrc_inline_ecc_scrub(0x10000000,0x13ffffff);
ddrc_inline_ecc_scrub(0x14000000,0x17ffffff);
ddrc_inline_ecc_scrub(0x18000000,0x1bffffff);
ddrc_inline_ecc_scrub_end(0x0,0x1fffffff);
}
static void dh_imx8mp_dhcom_dram_scrub_32g_x32(void)
{
ddrc_inline_ecc_scrub(0x0,0x7ffffff);
ddrc_inline_ecc_scrub(0x8000000,0xfffffff);
ddrc_inline_ecc_scrub(0x10000000,0x17ffffff);
ddrc_inline_ecc_scrub(0x18000000,0x1fffffff);
ddrc_inline_ecc_scrub(0x20000000,0x27ffffff);
ddrc_inline_ecc_scrub(0x28000000,0x2fffffff);
ddrc_inline_ecc_scrub(0x30000000,0x37ffffff);
ddrc_inline_ecc_scrub_end(0x0,0x3fffffff);
}
typedef void (*scrub_func_t)(void);
static const scrub_func_t dram_scrub_fn[8] = {
dh_imx8mp_dhcom_dram_scrub_32g_x32, /* 4096 MiB 1-rank */
NULL, /* 1024 MiB */
NULL, /* 1536 MiB */
dh_imx8mp_dhcom_dram_scrub_16g_x32, /* 2048 MiB */
NULL, /* 3072 MiB */
dh_imx8mp_dhcom_dram_scrub_32g_x32, /* 4096 MiB 2-rank */
NULL, /* 6144 MiB */
NULL, /* 8192 MiB */
};
void board_dram_ecc_scrub(void)
{
u8 memcfg = dh_get_memcfg();
if (!dram_scrub_fn[memcfg])
return;
dram_scrub_fn[memcfg]();
}
#endif
void spl_board_init(void)
{
/*
* Set GIC clock to 500 MHz for OD VDD_SOC. Kernel driver does not
* allow to change it. Should set the clock after PMIC setting done.
* Default is 400 MHz (system_pll1_800m with div = 2) set by ROM for
* ND VDD_SOC.
*/
clock_enable(CCGR_GIC, 0);
clock_set_target_val(GIC_CLK_ROOT, CLK_ROOT_ON | CLK_ROOT_SOURCE_SEL(5));
clock_enable(CCGR_GIC, 1);
}
int spl_board_boot_device(enum boot_device boot_dev_spl)
{
return BOOT_DEVICE_BOOTROM;
}
int board_spl_fit_append_fdt_skip(const char *name)
{
if (!dh_gigabit_eqos) { /* 1x or 2x RMII PHY SoM */
if (dh_gigabit_fec) { /* 1x RMII PHY SoM */
if (!strcmp(name, "fdt-dto-imx8mp-dhcom-som-overlay-eth1xfast"))
return 0;
} else { /* 2x RMII PHY SoM */
if (!strcmp(name, "fdt-dto-imx8mp-dhcom-som-overlay-eth2xfast"))
return 0;
if (!strcmp(name, "fdt-dto-imx8mp-dhcom-pdk-overlay-eth2xfast")) {
/* 2x RMII PHY SoM on PDK2 or PDK3 */
if (of_machine_is_compatible("dh,imx8mp-dhcom-pdk2") ||
of_machine_is_compatible("dh,imx8mp-dhcom-pdk3"))
return 0;
}
}
}
if (dh_som_rev == 0x0) { /* Prototype SoM rev.100 */
if (!strcmp(name, "fdt-dto-imx8mp-dhcom-som-overlay-rev100"))
return 0;
if (!strcmp(name, "fdt-dto-imx8mp-dhcom-pdk3-overlay-rev100") &&
of_machine_is_compatible("dh,imx8mp-dhcom-pdk3"))
return 0;
}
return 1; /* Skip this DTO */
}
static void dh_imx8mp_board_cache_config(void)
{
const void __iomem *mux_base = (void __iomem *)IOMUXC_BASE_ADDR;
const u32 mux_sion[] = {
FIELD_GET(MUX_CTRL_OFS_MASK, MX8MP_PAD_ENET_RX_CTL__GPIO1_IO24),
FIELD_GET(MUX_CTRL_OFS_MASK, MX8MP_PAD_SAI1_TXFS__GPIO4_IO10),
FIELD_GET(MUX_CTRL_OFS_MASK, MX8MP_PAD_NAND_DQS__GPIO3_IO14),
FIELD_GET(MUX_CTRL_OFS_MASK, MX8MP_PAD_SAI1_TXD7__GPIO4_IO19),
FIELD_GET(MUX_CTRL_OFS_MASK, MX8MP_PAD_SAI5_MCLK__GPIO3_IO25),
};
int i;
for (i = 0; i < ARRAY_SIZE(mux_sion); i++)
setbits_le32(mux_base + mux_sion[i], IOMUX_CONFIG_SION);
dh_gigabit_eqos = !(readl(GPIO1_BASE_ADDR) & BIT(24));
dh_gigabit_fec = !(readl(GPIO4_BASE_ADDR) & BIT(10));
dh_som_rev = !!(readl(GPIO3_BASE_ADDR) & BIT(14));
dh_som_rev |= !!(readl(GPIO4_BASE_ADDR) & BIT(19)) << 1;
dh_som_rev |= !!(readl(GPIO3_BASE_ADDR) & BIT(25)) << 2;
for (i = 0; i < ARRAY_SIZE(mux_sion); i++)
clrbits_le32(mux_base + mux_sion[i], IOMUX_CONFIG_SION);
}
void board_init_f(ulong dummy)
{
struct udevice *dev;
int ret;
arch_cpu_init();
init_uart_clk(0);
dh_imx8mp_early_init_f();
preloader_console_init();
/* Clear the BSS. */
memset(__bss_start, 0, __bss_end - __bss_start);
ret = spl_early_init();
if (ret) {
debug("spl_early_init() failed: %d\n", ret);
hang();
}
ret = uclass_get_device_by_name(UCLASS_CLK,
"clock-controller@30380000",
&dev);
if (ret < 0) {
printf("Failed to find clock node. Check device tree\n");
hang();
}
enable_tzc380();
dh_imx8mp_board_power_init();
/* DDR initialization */
spl_dram_init();
dh_imx8mp_board_cache_config();
board_init_r(NULL, 0);
}
+45
View File
@@ -0,0 +1,45 @@
if TARGET_DH_STM32MP1_PDK2
config SYS_BOARD
default "dh_stm32mp1"
config SYS_VENDOR
default "dhelectronics"
config SYS_CONFIG_NAME
default "stm32mp15_dh_dhsom"
config ENV_SECT_SIZE
default 0x10000 if ENV_IS_IN_SPI_FLASH
config ENV_OFFSET
default 0x1E0000 if ENV_IS_IN_SPI_FLASH
config ENV_OFFSET_REDUND
default 0x1F0000 if ENV_IS_IN_SPI_FLASH
source "board/st/common/Kconfig"
endif
if TARGET_DH_STM32MP13X
config SYS_BOARD
default "dh_stm32mp1"
config SYS_VENDOR
default "dhelectronics"
config SYS_CONFIG_NAME
default "stm32mp13_st_common"
config ENV_SECT_SIZE
default 0x10000 if ENV_IS_IN_SPI_FLASH
config ENV_OFFSET
default 0x3E0000 if ENV_IS_IN_SPI_FLASH
config ENV_OFFSET_REDUND
default 0x3F0000 if ENV_IS_IN_SPI_FLASH
source "board/st/common/Kconfig"
endif
@@ -0,0 +1,6 @@
DH_STM32MP1_PDK2 BOARD
M: Marek Vasut <marex@denx.de>
L: u-boot@dh-electronics.com
S: Maintained
N: stm32mp.*dh[cs]o
N: dh_stm32
+12
View File
@@ -0,0 +1,12 @@
# SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
#
# Copyright (C) 2018, STMicroelectronics - All Rights Reserved
#
obj-$(CONFIG_DEBUG_UART_BOARD_INIT) += ../../st/stm32mp1/debug_uart.o
obj-$(CONFIG_PMIC_STPMIC1) += ../../st/common/stpmic1.o
obj-y += board.o
obj-$(CONFIG_SET_DFU_ALT_INFO) += ../../st/common/stm32mp_dfu.o
obj-$(CONFIG_$(PHASE_)DFU_VIRT) += ../../st/common/stm32mp_dfu_virt.o
+908
View File
@@ -0,0 +1,908 @@
// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
/*
* Copyright (C) 2018, STMicroelectronics - All Rights Reserved
*/
#include <adc.h>
#include <log.h>
#include <net.h>
#include <asm/arch/stm32.h>
#include <asm/arch/sys_proto.h>
#include <asm/gpio.h>
#include <asm/io.h>
#include <bootm.h>
#include <clk.h>
#include <config.h>
#include <dm.h>
#include <dm/device.h>
#include <dm/uclass.h>
#include <env.h>
#include <env_internal.h>
#include <g_dnl.h>
#include <generic-phy.h>
#include <hang.h>
#include <i2c.h>
#include <i2c_eeprom.h>
#include <init.h>
#include <led.h>
#include <memalign.h>
#include <misc.h>
#include <mtd_node.h>
#include <netdev.h>
#include <phy.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/printk.h>
#include <power/regulator.h>
#include <remoteproc.h>
#include <reset.h>
#include <spl.h>
#include <syscon.h>
#include <usb.h>
#include <usb/dwc2_udc.h>
#include <watchdog.h>
#include <dm/ofnode.h>
#include "../common/dh_common.h"
#include "../../st/common/stpmic1.h"
/* SYSCFG registers */
#define SYSCFG_BOOTR 0x00
#define SYSCFG_IOCTRLSETR 0x18
#define SYSCFG_ICNR 0x1C
#define SYSCFG_CMPCR 0x20
#define SYSCFG_CMPENSETR 0x24
#define SYSCFG_BOOTR_BOOT_MASK GENMASK(2, 0)
#define SYSCFG_BOOTR_BOOTPD_SHIFT 4
#define SYSCFG_IOCTRLSETR_HSLVEN_TRACE BIT(0)
#define SYSCFG_IOCTRLSETR_HSLVEN_QUADSPI BIT(1)
#define SYSCFG_IOCTRLSETR_HSLVEN_ETH BIT(2)
#define SYSCFG_IOCTRLSETR_HSLVEN_SDMMC BIT(3)
#define SYSCFG_IOCTRLSETR_HSLVEN_SPI BIT(4)
#define SYSCFG_CMPCR_SW_CTRL BIT(1)
#define SYSCFG_CMPCR_READY BIT(8)
#define SYSCFG_CMPENSETR_MPU_EN BIT(0)
#define KS_CCR 0x08
#define KS_CCR_EEPROM BIT(9)
#define KS_BE0 BIT(12)
#define KS_BE1 BIT(13)
#define KS_CIDER 0xC0
#define CIDER_ID 0x8870
static bool dh_stm32_mac_is_in_ks8851(void)
{
struct udevice *udev;
u32 reg, cider, ccr;
char path[256];
ofnode node;
int ret;
node = ofnode_path("ethernet1");
if (!ofnode_valid(node))
return false;
if (!ofnode_device_is_compatible(node, "micrel,ks8851-mll"))
return false;
ret = ofnode_get_path(node, path, sizeof(path));
if (ret)
return false;
ret = uclass_get_device_by_of_path(UCLASS_ETH, path, &udev);
if (ret)
return false;
/*
* KS8851 with EEPROM may use custom MAC from EEPROM, read
* out the KS8851 CCR register to determine whether EEPROM
* is present. If EEPROM is present, it must contain valid
* MAC address.
*/
reg = ofnode_get_addr(node);
if (!reg)
return false;
writew(KS_BE0 | KS_BE1 | KS_CIDER, reg + 2);
cider = readw(reg);
if ((cider & 0xfff0) != CIDER_ID)
return true;
writew(KS_BE0 | KS_BE1 | KS_CCR, reg + 2);
ccr = readw(reg);
if (ccr & KS_CCR_EEPROM)
return true;
return false;
}
static int dh_stm32_get_mac_from_fuse(unsigned char *enetaddr, int index)
{
struct udevice *dev;
u8 otp[12];
int ret;
ret = uclass_get_device_by_driver(UCLASS_MISC,
DM_DRIVER_GET(stm32mp_bsec),
&dev);
if (ret)
return ret;
ret = misc_read(dev, STM32_BSEC_SHADOW(BSEC_OTP_MAC), otp, sizeof(otp));
if (ret < 0)
return ret;
memcpy(enetaddr, otp + ARP_HLEN * index, ARP_HLEN);
if (!is_valid_ethaddr(enetaddr))
return -EINVAL;
return 0;
}
static int dh_stm32_setup_ethaddr(struct eeprom_id_page *eip)
{
unsigned char enetaddr[6];
if (dh_mac_is_in_env("ethaddr"))
return 0;
if (dh_get_mac_is_enabled("ethernet0"))
return 0;
if (!dh_stm32_get_mac_from_fuse(enetaddr, 0))
goto out;
if (!dh_get_value_from_eeprom_buffer(DH_MAC0, enetaddr, sizeof(enetaddr), eip))
goto out;
if (!dh_get_mac_from_eeprom(enetaddr, "eeprom0"))
goto out;
return -ENXIO;
out:
return eth_env_set_enetaddr("ethaddr", enetaddr);
}
static int dh_stm32_setup_eth1addr(struct eeprom_id_page *eip)
{
unsigned char enetaddr[6];
if (dh_mac_is_in_env("eth1addr"))
return 0;
if (dh_get_mac_is_enabled("ethernet1"))
return 0;
if (dh_stm32_mac_is_in_ks8851())
return 0;
if (!dh_stm32_get_mac_from_fuse(enetaddr, 1))
goto out;
if (!dh_get_value_from_eeprom_buffer(DH_MAC1, enetaddr, sizeof(enetaddr), eip))
goto out;
if (!dh_get_mac_from_eeprom(enetaddr, "eeprom0"))
goto increment_out;
return -ENXIO;
increment_out:
enetaddr[5]++;
out:
return eth_env_set_enetaddr("eth1addr", enetaddr);
}
int setup_mac_address(void)
{
u8 eeprom_buffer[DH_EEPROM_ID_PAGE_MAX_SIZE] = { 0 };
struct eeprom_id_page *eip = (struct eeprom_id_page *)eeprom_buffer;
int ret;
ret = dh_read_eeprom_id_page(eeprom_buffer, "eeprom0wl");
if (ret) {
/*
* The EEPROM ID page is available on SoM rev. 200 and greater.
* For SoM rev. 100 the return value will be -ENODEV. Suppress
* the error message for that, because the absence cannot be
* treated as an error.
*/
if (ret != -ENODEV)
printf("%s: Cannot read valid data from EEPROM ID page! ret = %d\n",
__func__, ret);
eip = NULL;
} else {
dh_add_item_number_and_serial_to_env(eip);
}
if (dh_stm32_setup_ethaddr(eip))
log_err("%s: Unable to setup ethaddr!\n", __func__);
if (dh_stm32_setup_eth1addr(eip))
log_err("%s: Unable to setup eth1addr!\n", __func__);
return 0;
}
int checkboard(void)
{
char *mode;
const char *fdt_compat;
int fdt_compat_len;
if (IS_ENABLED(CONFIG_TFABOOT))
mode = "trusted";
else
mode = "basic";
printf("Board: stm32mp1 in %s mode", mode);
fdt_compat = ofnode_get_property(ofnode_root(), "compatible",
&fdt_compat_len);
if (fdt_compat && fdt_compat_len)
printf(" (%s)", fdt_compat);
puts("\n");
return 0;
}
#ifdef CONFIG_BOARD_EARLY_INIT_F
static u8 brdcode __section(".data");
static u8 ddr3code __section(".data");
static u8 somcode __section(".data");
static u32 opp_voltage_mv __section(".data");
static void board_get_coding_straps(void)
{
struct gpio_desc gpio[4];
ofnode node;
int i, ret;
brdcode = 0;
ddr3code = 0;
somcode = 0;
node = ofnode_path("/config");
if (!ofnode_valid(node)) {
printf("%s: no /config node?\n", __func__);
return;
}
ret = gpio_request_list_by_name_nodev(node, "dh,som-coding-gpios",
gpio, ARRAY_SIZE(gpio),
GPIOD_IS_IN);
for (i = 0; i < ret; i++)
somcode |= !!dm_gpio_get_value(&(gpio[i])) << i;
gpio_free_list_nodev(gpio, ret);
ret = gpio_request_list_by_name_nodev(node, "dh,ddr3-coding-gpios",
gpio, ARRAY_SIZE(gpio),
GPIOD_IS_IN);
for (i = 0; i < ret; i++)
ddr3code |= !!dm_gpio_get_value(&(gpio[i])) << i;
gpio_free_list_nodev(gpio, ret);
ret = gpio_request_list_by_name_nodev(node, "dh,board-coding-gpios",
gpio, ARRAY_SIZE(gpio),
GPIOD_IS_IN);
for (i = 0; i < ret; i++)
brdcode |= !!dm_gpio_get_value(&(gpio[i])) << i;
gpio_free_list_nodev(gpio, ret);
if (CONFIG_IS_ENABLED(DISPLAY_PRINT))
printf("Code: SoM:rev=%d,ddr3=%d Board:rev=%d\n",
somcode, ddr3code, brdcode);
}
int board_stm32mp1_ddr_config_name_match(struct udevice *dev,
const char *name)
{
if (IS_ENABLED(CONFIG_TARGET_DH_STM32MP13X)) {
if (ddr3code == 1 &&
!strcmp(name, "st,ddr3l-dhsom-1066-888-bin-g-1x2gb-533mhz"))
return 0;
if (ddr3code == 2 &&
!strcmp(name, "st,ddr3l-dhsom-1066-888-bin-g-1x4gb-533mhz"))
return 0;
}
if (IS_ENABLED(CONFIG_STM32MP15X)) {
if (ddr3code == 1 &&
!strcmp(name, "st,ddr3l-dhsom-1066-888-bin-g-2x1gb-533mhz"))
return 0;
if (ddr3code == 2 &&
!strcmp(name, "st,ddr3l-dhsom-1066-888-bin-g-2x2gb-533mhz"))
return 0;
if (ddr3code == 3 &&
!strcmp(name, "st,ddr3l-dhsom-1066-888-bin-g-2x4gb-533mhz"))
return 0;
}
return -EINVAL;
}
void board_vddcore_init(u32 voltage_mv)
{
if (IS_ENABLED(CONFIG_XPL_BUILD))
opp_voltage_mv = voltage_mv;
}
int board_early_init_f(void)
{
if (IS_ENABLED(CONFIG_XPL_BUILD))
stpmic1_init(opp_voltage_mv);
board_get_coding_straps();
return 0;
}
#ifdef CONFIG_SPL_LOAD_FIT
int board_fit_config_name_match(const char *name)
{
char *cdevice, *ndevice;
const char *compat;
compat = ofnode_get_property(ofnode_root(), "compatible", NULL);
if (!compat)
return -EINVAL;
cdevice = strchr(compat, ',');
if (!cdevice)
return -ENODEV;
cdevice++; /* Move past the comma right after vendor prefix. */
ndevice = strchr(name, '/');
if (!ndevice)
return -ENODEV;
ndevice++; /* Move past the last slash in DT path */
if (!strcmp(cdevice, ndevice))
return 0;
return -EINVAL;
}
#endif
#endif
static void board_key_check(void)
{
#if defined(CONFIG_FASTBOOT) || defined(CONFIG_CMD_STM32PROG)
ofnode node;
struct gpio_desc gpio;
enum forced_boot_mode boot_mode = BOOT_NORMAL;
node = ofnode_path("/config");
if (!ofnode_valid(node)) {
debug("%s: no /config node?\n", __func__);
return;
}
#ifdef CONFIG_FASTBOOT
if (gpio_request_by_name_nodev(node, "st,fastboot-gpios", 0,
&gpio, GPIOD_IS_IN)) {
debug("%s: could not find a /config/st,fastboot-gpios\n",
__func__);
} else {
if (dm_gpio_get_value(&gpio)) {
puts("Fastboot key pressed, ");
boot_mode = BOOT_FASTBOOT;
}
dm_gpio_free(NULL, &gpio);
}
#endif
#ifdef CONFIG_CMD_STM32PROG
if (gpio_request_by_name_nodev(node, "st,stm32prog-gpios", 0,
&gpio, GPIOD_IS_IN)) {
debug("%s: could not find a /config/st,stm32prog-gpios\n",
__func__);
} else {
if (dm_gpio_get_value(&gpio)) {
puts("STM32Programmer key pressed, ");
boot_mode = BOOT_STM32PROG;
}
dm_gpio_free(NULL, &gpio);
}
#endif
if (boot_mode != BOOT_NORMAL) {
puts("entering download mode...\n");
clrsetbits_le32(TAMP_BOOT_CONTEXT,
TAMP_BOOT_FORCED_MASK,
boot_mode);
}
#endif
}
#if defined(CONFIG_USB_GADGET) && defined(CONFIG_USB_GADGET_DWC2_OTG)
#include <usb/dwc2_udc.h>
int g_dnl_board_usb_cable_connected(void)
{
struct udevice *dwc2_udc_otg;
int ret;
ret = uclass_get_device_by_driver(UCLASS_USB_GADGET_GENERIC,
DM_DRIVER_GET(dwc2_udc_otg),
&dwc2_udc_otg);
if (!ret)
debug("dwc2_udc_otg init failed\n");
return dwc2_udc_B_session_valid(dwc2_udc_otg);
}
#define STM32MP1_G_DNL_DFU_PRODUCT_NUM 0xdf11
#define STM32MP1_G_DNL_FASTBOOT_PRODUCT_NUM 0x0afb
int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name)
{
if (!strcmp(name, "usb_dnl_dfu"))
put_unaligned(STM32MP1_G_DNL_DFU_PRODUCT_NUM, &dev->idProduct);
else if (!strcmp(name, "usb_dnl_fastboot"))
put_unaligned(STM32MP1_G_DNL_FASTBOOT_PRODUCT_NUM,
&dev->idProduct);
else
put_unaligned(CONFIG_USB_GADGET_PRODUCT_NUM, &dev->idProduct);
return 0;
}
#endif /* CONFIG_USB_GADGET */
#ifdef CONFIG_LED
static int get_led(struct udevice **dev, char *led_string)
{
const char *led_name;
int ret;
led_name = ofnode_conf_read_str(led_string);
if (!led_name) {
pr_debug("%s: could not find %s config string\n",
__func__, led_string);
return -ENOENT;
}
ret = led_get_by_label(led_name, dev);
if (ret) {
debug("%s: get=%d\n", __func__, ret);
return ret;
}
return 0;
}
static int setup_led(enum led_state_t cmd)
{
struct udevice *dev;
int ret;
ret = get_led(&dev, "u-boot,boot-led");
if (ret)
return ret;
ret = led_set_state(dev, cmd);
return ret;
}
#endif
static void __maybe_unused led_error_blink(u32 nb_blink)
{
#ifdef CONFIG_LED
int ret;
struct udevice *led;
u32 i;
#endif
if (!nb_blink)
return;
#ifdef CONFIG_LED
ret = get_led(&led, "u-boot,error-led");
if (!ret) {
/* make u-boot,error-led blinking */
/* if U32_MAX and 125ms interval, for 17.02 years */
for (i = 0; i < 2 * nb_blink; i++) {
led_set_state(led, LEDST_TOGGLE);
mdelay(125);
schedule();
}
}
#endif
/* infinite: the boot process must be stopped */
if (nb_blink == U32_MAX)
hang();
}
static void sysconf_init(void)
{
#ifndef CONFIG_TFABOOT
u8 *syscfg;
#ifdef CONFIG_DM_REGULATOR
struct udevice *pwr_dev;
struct udevice *pwr_reg;
struct udevice *dev;
int ret;
u32 otp = 0;
#endif
u32 bootr;
syscfg = (u8 *)syscon_get_first_range(STM32MP_SYSCON_SYSCFG);
/* interconnect update : select master using the port 1 */
/* LTDC = AXI_M9 */
/* GPU = AXI_M8 */
/* today information is hardcoded in U-Boot */
writel(BIT(9), syscfg + SYSCFG_ICNR);
/* disable Pull-Down for boot pin connected to VDD */
bootr = readl(syscfg + SYSCFG_BOOTR);
bootr &= ~(SYSCFG_BOOTR_BOOT_MASK << SYSCFG_BOOTR_BOOTPD_SHIFT);
bootr |= (bootr & SYSCFG_BOOTR_BOOT_MASK) << SYSCFG_BOOTR_BOOTPD_SHIFT;
writel(bootr, syscfg + SYSCFG_BOOTR);
#ifdef CONFIG_DM_REGULATOR
/* High Speed Low Voltage Pad mode Enable for SPI, SDMMC, ETH, QSPI
* and TRACE. Needed above ~50MHz and conditioned by AFMUX selection.
* The customer will have to disable this for low frequencies
* or if AFMUX is selected but the function not used, typically for
* TRACE. Otherwise, impact on power consumption.
*
* WARNING:
* enabling High Speed mode while VDD>2.7V
* with the OTP product_below_2v5 (OTP 18, BIT 13)
* erroneously set to 1 can damage the IC!
* => U-Boot set the register only if VDD < 2.7V (in DT)
* but this value need to be consistent with board design
*/
ret = uclass_get_device_by_driver(UCLASS_PMIC,
DM_DRIVER_GET(stm32mp_pwr_pmic),
&pwr_dev);
if (!ret) {
ret = uclass_get_device_by_driver(UCLASS_MISC,
DM_DRIVER_GET(stm32mp_bsec),
&dev);
if (ret) {
pr_err("Can't find stm32mp_bsec driver\n");
return;
}
ret = misc_read(dev, STM32_BSEC_SHADOW(18), &otp, 4);
if (ret > 0)
otp = otp & BIT(13);
/* get VDD = vdd-supply */
ret = device_get_supply_regulator(pwr_dev, "vdd-supply",
&pwr_reg);
/* check if VDD is Low Voltage */
if (!ret) {
if (regulator_get_value(pwr_reg) < 2700000) {
writel(SYSCFG_IOCTRLSETR_HSLVEN_TRACE |
SYSCFG_IOCTRLSETR_HSLVEN_QUADSPI |
SYSCFG_IOCTRLSETR_HSLVEN_ETH |
SYSCFG_IOCTRLSETR_HSLVEN_SDMMC |
SYSCFG_IOCTRLSETR_HSLVEN_SPI,
syscfg + SYSCFG_IOCTRLSETR);
if (!otp)
pr_err("product_below_2v5=0: HSLVEN protected by HW\n");
} else {
if (otp)
pr_err("product_below_2v5=1: HSLVEN update is destructive, no update as VDD>2.7V\n");
}
} else {
debug("VDD unknown");
}
}
#endif
/* activate automatic I/O compensation
* warning: need to ensure CSI enabled and ready in clock driver
*/
writel(SYSCFG_CMPENSETR_MPU_EN, syscfg + SYSCFG_CMPENSETR);
while (!(readl(syscfg + SYSCFG_CMPCR) & SYSCFG_CMPCR_READY))
;
clrbits_le32(syscfg + SYSCFG_CMPCR, SYSCFG_CMPCR_SW_CTRL);
#endif
}
#ifdef CONFIG_DM_REGULATOR
#define STPMIC_NVM_BUCKS_VOUT_SHR 0xfc
#define STPMIC_NVM_BUCKS_VOUT_SHR_BUCK_1V2 0
#define STPMIC_NVM_BUCKS_VOUT_SHR_BUCK_1V8 1
#define STPMIC_NVM_BUCKS_VOUT_SHR_BUCK_3V0 2
#define STPMIC_NVM_BUCKS_VOUT_SHR_BUCK_3V3 3
#define STPMIC_NVM_BUCKS_VOUT_SHR_BUCK_MASK GENMASK(1, 0)
#define STPMIC_NVM_BUCKS_VOUT_SHR_BUCK_OFFSET(n) ((((n) - 1) & 3) * 2)
static int board_get_regulator_buck3_nvm_uv_av96(int *uv)
{
struct udevice *dev;
u8 bucks_vout = 0;
const char *prop;
int len, ret;
/* Check whether this is Avenger96 board. */
prop = ofnode_get_property(ofnode_root(), "compatible", &len);
if (!prop || !len)
return -ENODEV;
if (!strstr(prop, "avenger96") && !strstr(prop, "dhcor-testbench"))
return -EINVAL;
/* Read out STPMIC1 NVM and determine default Buck3 voltage. */
ret = uclass_get_device_by_driver(UCLASS_MISC,
DM_DRIVER_GET(stpmic1_nvm),
&dev);
if (ret)
return ret;
ret = misc_read(dev, STPMIC_NVM_BUCKS_VOUT_SHR, &bucks_vout, 1);
if (ret != 1)
return -EINVAL;
bucks_vout >>= STPMIC_NVM_BUCKS_VOUT_SHR_BUCK_OFFSET(3);
bucks_vout &= STPMIC_NVM_BUCKS_VOUT_SHR_BUCK_MASK;
if (strstr(prop, "avenger96")) {
/*
* Avenger96 board comes in multiple regulator configurations:
* - rev.100 or rev.200 have Buck3 preconfigured to
* 3V3 operation on boot and contains extra Enpirion
* EP53A8LQI DCDC converter which supplies the IO.
* Reduce Buck3 voltage to 2V9 to not waste power.
* - rev.200L have Buck3 preconfigured to 1V8 operation
* and have no Enpirion EP53A8LQI DCDC anymore, the
* IO is supplied from Buck3.
*/
if (bucks_vout == STPMIC_NVM_BUCKS_VOUT_SHR_BUCK_3V3)
*uv = 2900000;
else
*uv = 1800000;
} else {
/* Testbench always respects Buck3 NVM settings */
if (bucks_vout == STPMIC_NVM_BUCKS_VOUT_SHR_BUCK_3V3)
*uv = 3300000;
else if (bucks_vout == STPMIC_NVM_BUCKS_VOUT_SHR_BUCK_3V0)
*uv = 3000000;
else if (bucks_vout == STPMIC_NVM_BUCKS_VOUT_SHR_BUCK_1V8)
*uv = 1800000;
else /* STPMIC_NVM_BUCKS_VOUT_SHR_BUCK_1V2 */
*uv = 1200000;
}
return 0;
}
static void board_init_regulator_av96(void)
{
struct udevice *rdev;
int ret, uv;
ret = board_get_regulator_buck3_nvm_uv_av96(&uv);
if (ret) /* Not Avenger96 board. */
return;
ret = regulator_get_by_devname("buck3", &rdev);
if (ret)
return;
/* Adjust Buck3 per preconfigured PMIC voltage from NVM. */
regulator_set_value(rdev, uv);
regulator_set_enable(rdev, true);
}
static void board_init_regulator(void)
{
board_init_regulator_av96();
}
#else
static inline int board_get_regulator_buck3_nvm_uv_av96(int *uv)
{
return -EINVAL;
}
static inline void board_init_regulator(void) {}
#endif
/* board dependent setup after realloc */
int board_init(void)
{
board_key_check();
board_init_regulator();
sysconf_init();
return 0;
}
int board_late_init(void)
{
char *boot_device;
#ifdef CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG
const void *fdt_compat;
int fdt_compat_len;
fdt_compat = ofnode_get_property(ofnode_root(), "compatible",
&fdt_compat_len);
if (fdt_compat && fdt_compat_len) {
if (strncmp(fdt_compat, "st,", 3) != 0)
env_set("board_name", fdt_compat);
else
env_set("board_name", fdt_compat + 3);
}
#endif
/* Check the boot-source to disable bootdelay */
boot_device = env_get("boot_device");
if (!strcmp(boot_device, "serial") || !strcmp(boot_device, "usb"))
env_set("bootdelay", "0");
#ifdef CONFIG_BOARD_EARLY_INIT_F
env_set_ulong("dh_som_rev", somcode);
env_set_ulong("dh_board_rev", brdcode);
env_set_ulong("dh_ddr3_code", ddr3code);
#endif
return 0;
}
void board_quiesce_devices(void)
{
#ifdef CONFIG_LED
setup_led(LEDST_OFF);
#endif
}
#ifdef CONFIG_TARGET_DH_STM32MP13X
enum env_location env_get_location(enum env_operation op, int prio)
{
u32 bootmode = get_bootmode();
if (prio)
return ENVL_UNKNOWN;
switch (bootmode & TAMP_BOOT_DEVICE_MASK) {
case BOOT_FLASH_SD:
case BOOT_FLASH_EMMC:
if (CONFIG_IS_ENABLED(ENV_IS_IN_MMC))
return ENVL_MMC;
else
return ENVL_NOWHERE;
case BOOT_FLASH_NOR:
if (CONFIG_IS_ENABLED(ENV_IS_IN_SPI_FLASH))
return ENVL_SPI_FLASH;
else
return ENVL_NOWHERE;
default:
return ENVL_NOWHERE;
}
}
#endif
static void dh_stm32_ks8851_fixup(void *blob)
{
struct gpio_desc ks8851intrn;
bool compatible = false;
int ks8851intrn_value;
const char *prop;
ofnode node;
int idx = 0;
int offset;
int ret;
/* Do nothing if not STM32MP15xx DHCOM SoM */
while ((prop = fdt_stringlist_get(blob, 0, "compatible", idx++, NULL))) {
if (!strstr(prop, "dhcom-som"))
continue;
compatible = true;
break;
}
if (!compatible)
return;
/*
* Read state of INTRN pull up resistor, if this pull up is populated,
* KS8851-16MLL is populated as well and should be enabled, otherwise
* it should be disabled.
*/
node = ofnode_path("/config");
if (!ofnode_valid(node))
return;
ret = gpio_request_by_name_nodev(node, "dh,mac-coding-gpios", 0,
&ks8851intrn, GPIOD_IS_IN);
if (ret)
return;
ks8851intrn_value = dm_gpio_get_value(&ks8851intrn);
dm_gpio_free(NULL, &ks8851intrn);
/* Set the 'status' property into KS8851-16MLL DT node. */
offset = fdt_path_offset(blob, "ethernet1");
ret = fdt_node_check_compatible(blob, offset, "micrel,ks8851-mll");
if (ret) /* Not compatible */
return;
/* Add a bit of extra space for new 'status' property */
ret = fdt_shrink_to_minimum(blob, 4096);
if (!ret)
return;
fdt_setprop_string(blob, offset, "status",
ks8851intrn_value ? "okay" : "disabled");
}
#if defined(CONFIG_OF_BOARD_SETUP)
int ft_board_setup(void *blob, struct bd_info *bd)
{
const char *buck3path = "/soc/i2c@5c002000/stpmic@33/regulators/buck3";
int buck3off, ret, uv;
dh_stm32_ks8851_fixup(blob);
ret = board_get_regulator_buck3_nvm_uv_av96(&uv);
if (ret) /* Not Avenger96 board, do not patch Buck3 in DT. */
return 0;
buck3off = fdt_path_offset(blob, buck3path);
if (buck3off < 0) /* No Buck3 regulator found. */
return 0;
ret = fdt_setprop_u32(blob, buck3off, "regulator-min-microvolt", uv);
if (ret < 0)
return ret;
ret = fdt_setprop_u32(blob, buck3off, "regulator-max-microvolt", uv);
if (ret < 0)
return ret;
return 0;
}
#endif
#if defined(CONFIG_XPL_BUILD)
void spl_perform_board_fixups(struct spl_image_info *spl_image)
{
dh_stm32_ks8851_fixup(spl_image_fdt_addr(spl_image));
}
#endif
static void board_copro_image_process(ulong fw_image, size_t fw_size)
{
int ret, id = 0; /* Copro id fixed to 0 as only one coproc on mp1 */
if (!rproc_is_initialized())
if (rproc_init()) {
printf("Remote Processor %d initialization failed\n",
id);
return;
}
ret = rproc_load(id, fw_image, fw_size);
printf("Load Remote Processor %d with data@addr=0x%08lx %u bytes:%s\n",
id, fw_image, fw_size, ret ? " Failed!" : " Success!");
if (!ret) {
rproc_start(id);
env_set("copro_state", "booted");
}
}
U_BOOT_FIT_LOADABLE_HANDLER(IH_TYPE_COPRO, board_copro_image_process);