- Clearfog: Add run-time board detection with TLV EEPROM support
  (Baruch)
This commit is contained in:
Tom Rini 2020-01-21 18:10:28 -05:00
commit 75dd53055a
15 changed files with 1510 additions and 4 deletions

View file

@ -23,3 +23,15 @@
&ahci1 {
u-boot,dm-spl;
};
&i2c0 {
u-boot,dm-spl;
eeprom@52 {
u-boot,dm-spl;
};
eeprom@53 {
u-boot,dm-spl;
};
};

View file

@ -259,6 +259,12 @@
compatible = "microchip,mcp3021";
reg = <0x4c>;
};
eeprom@52 {
compatible = "atmel,24c02";
reg = <0x52>;
pagesize = <16>;
};
};
&i2c1 {

View file

@ -99,3 +99,11 @@
status = "okay";
u-boot,dm-pre-reloc;
};
&i2c0 {
eeprom@53 {
compatible = "atmel,24c02";
reg = <0x53>;
pagesize = <16>;
};
};

View file

@ -91,6 +91,7 @@ choice
config TARGET_CLEARFOG
bool "Support ClearFog"
select 88F6820
select BOARD_LATE_INIT
config TARGET_HELIOS4
bool "Support Helios4"

View file

@ -10,6 +10,7 @@
#include <asm/io.h>
#include <asm/arch/cpu.h>
#include <asm/arch/soc.h>
#include "../common/tlv_data.h"
#include "../drivers/ddr/marvell/a38x/ddr3_init.h"
#include <../serdes/a38x/high_speed_env_spec.h>
@ -28,6 +29,19 @@ DECLARE_GLOBAL_DATA_PTR;
#define BOARD_GPP_POL_LOW 0x0
#define BOARD_GPP_POL_MID 0x0
static struct tlv_data cf_tlv_data;
static void cf_read_tlv_data(void)
{
static bool read_once;
if (read_once)
return;
read_once = true;
read_tlv_data(&cf_tlv_data);
}
static struct serdes_map board_serdes_map[] = {
{SATA0, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 0},
{SGMII1, SERDES_SPEED_1_25_GBPS, SERDES_DEFAULT_MODE, 0, 0},
@ -39,6 +53,20 @@ static struct serdes_map board_serdes_map[] = {
int hws_board_topology_load(struct serdes_map **serdes_map_array, u8 *count)
{
cf_read_tlv_data();
if (sr_product_is(&cf_tlv_data, "Clearfog GTR")) {
board_serdes_map[0].serdes_type = PEX0;
board_serdes_map[0].serdes_speed = SERDES_SPEED_5_GBPS;
board_serdes_map[0].serdes_mode = PEX_ROOT_COMPLEX_X1;
}
if (sr_product_is(&cf_tlv_data, "Clearfog Base")) {
board_serdes_map[4].serdes_type = USB3_HOST0;
board_serdes_map[4].serdes_speed = SERDES_SPEED_5_GBPS;
board_serdes_map[4].serdes_mode = SERDES_DEFAULT_MODE;
}
*serdes_map_array = board_serdes_map;
*count = ARRAY_SIZE(board_serdes_map);
return 0;
@ -68,11 +96,28 @@ static struct mv_ddr_topology_map board_topology_map = {
BUS_MASK_32BIT, /* Busses mask */
MV_DDR_CFG_DEFAULT, /* ddr configuration data source */
{ {0} }, /* raw spd data */
{0} /* timing parameters */
{0}, /* timing parameters */
{ {0} }, /* electrical configuration */
{0,}, /* electrical parameters */
0x3, /* clock enable mask */
};
struct mv_ddr_topology_map *mv_ddr_topology_map_get(void)
{
struct if_params *ifp = &board_topology_map.interface_params[0];
cf_read_tlv_data();
switch (cf_tlv_data.ram_size) {
case 4:
default:
ifp->memory_size = MV_DDR_DIE_CAP_4GBIT;
break;
case 8:
ifp->memory_size = MV_DDR_DIE_CAP_8GBIT;
break;
}
/* Return the board topology as defined in the board code */
return &board_topology_map;
}
@ -125,7 +170,16 @@ int board_init(void)
int checkboard(void)
{
puts("Board: SolidRun ClearFog\n");
char *board = "ClearFog";
cf_read_tlv_data();
if (strlen(cf_tlv_data.tlv_product_name[0]) > 0)
board = cf_tlv_data.tlv_product_name[0];
printf("Board: SolidRun %s", board);
if (strlen(cf_tlv_data.tlv_product_name[1]) > 0)
printf(", %s", cf_tlv_data.tlv_product_name[1]);
puts("\n");
return 0;
}
@ -135,3 +189,17 @@ int board_eth_init(bd_t *bis)
cpu_eth_init(bis); /* Built in controller(s) come first */
return pci_eth_init(bis);
}
int board_late_init(void)
{
cf_read_tlv_data();
if (sr_product_is(&cf_tlv_data, "Clearfog Base"))
env_set("fdtfile", "armada-388-clearfog-base.dtb");
else if (sr_product_is(&cf_tlv_data, "Clearfog GTR S4"))
env_set("fdtfile", "armada-385-clearfog-gtr-s4.dtb");
else if (sr_product_is(&cf_tlv_data, "Clearfog GTR L8"))
env_set("fdtfile", "armada-385-clearfog-gtr-l8.dtb");
return 0;
}

View file

@ -0,0 +1,5 @@
# SPDX-License-Identifier: GPL-2.0+
#
# Copyright (C) SolidRun
obj-$(CONFIG_TARGET_CLEARFOG) += tlv_data.o

View file

@ -0,0 +1,102 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2020 SolidRun
*/
#include <common.h>
#include <tlv_eeprom.h>
#include "tlv_data.h"
#define SR_TLV_CODE_RAM_SIZE 0x81
static void store_product_name(struct tlvinfo_tlv *tlv_entry,
struct tlv_data *td)
{
int len;
char *dest;
if (strlen(td->tlv_product_name[0]) == 0)
dest = td->tlv_product_name[0];
else if (strlen(td->tlv_product_name[1]) == 0)
dest = td->tlv_product_name[1];
else
return;
len = min_t(unsigned int, tlv_entry->length,
sizeof(td->tlv_product_name[0]) - 1);
memcpy(dest, tlv_entry->value, len);
}
static void parse_tlv_vendor_ext(struct tlvinfo_tlv *tlv_entry,
struct tlv_data *td)
{
u8 *val = tlv_entry->value;
u32 pen; /* IANA Private Enterprise Numbers */
if (tlv_entry->length < 5) /* 4 bytes PEN + at least 1 byte type */
return;
/* PEN is big endian */
pen = (val[0] << 24) | (val[1] << 16) | (val[2] << 8) | val[3];
/* Not a real PEN */
if (pen != 0xffffffff)
return;
if (val[4] != SR_TLV_CODE_RAM_SIZE)
return;
if (tlv_entry->length != 6)
return;
td->ram_size = val[5];
}
static void parse_tlv_data(u8 *eeprom, struct tlvinfo_header *hdr,
struct tlvinfo_tlv *entry, struct tlv_data *td)
{
unsigned int tlv_offset, tlv_len;
tlv_offset = sizeof(struct tlvinfo_header);
tlv_len = sizeof(struct tlvinfo_header) + be16_to_cpu(hdr->totallen);
while (tlv_offset < tlv_len) {
entry = (struct tlvinfo_tlv *)&eeprom[tlv_offset];
switch (entry->type) {
case TLV_CODE_PRODUCT_NAME:
store_product_name(entry, td);
break;
case TLV_CODE_VENDOR_EXT:
parse_tlv_vendor_ext(entry, td);
break;
default:
break;
}
tlv_offset += sizeof(struct tlvinfo_tlv) + entry->length;
}
}
void read_tlv_data(struct tlv_data *td)
{
u8 eeprom_data[TLV_TOTAL_LEN_MAX];
struct tlvinfo_header *tlv_hdr;
struct tlvinfo_tlv *tlv_entry;
int ret, i;
for (i = 0; i < 2; i++) {
ret = read_tlvinfo_tlv_eeprom(eeprom_data, &tlv_hdr,
&tlv_entry, i);
if (ret < 0)
continue;
parse_tlv_data(eeprom_data, tlv_hdr, tlv_entry, td);
}
}
bool sr_product_is(const struct tlv_data *td, const char *product)
{
/* Allow prefix sub-string match */
if (strncmp(td->tlv_product_name[0], product, strlen(product)) == 0)
return true;
if (strncmp(td->tlv_product_name[1], product, strlen(product)) == 0)
return true;
return false;
}

View file

@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright 2020 SolidRun
*/
#ifndef __BOARD_SR_COMMON_H_
#define __BOARD_SR_COMMON_H_
struct tlv_data {
/* Store product name of both SOM and carrier */
char tlv_product_name[2][32];
unsigned int ram_size;
};
void read_tlv_data(struct tlv_data *td);
bool sr_product_is(const struct tlv_data *td, const char *product);
#endif /* __BOARD_SR_COMMON_H_ */

View file

@ -242,6 +242,20 @@ config CMD_REGINFO
help
Register dump
config CMD_TLV_EEPROM
bool "tlv_eeprom"
depends on I2C_EEPROM
help
Display and program the system EEPROM data block in ONIE Tlvinfo
format. TLV stands for Type-Length-Value.
config SPL_CMD_TLV_EEPROM
bool "tlv_eeprom for SPL"
depends on SPL_I2C_EEPROM
select SPL_DRIVERS_MISC_SUPPORT
help
Read system EEPROM data block in ONIE Tlvinfo format from SPL.
endmenu
menu "Boot commands"

View file

@ -183,6 +183,8 @@ obj-$(CONFIG_X86) += x86/
obj-$(CONFIG_ARCH_MVEBU) += mvebu/
endif # !CONFIG_SPL_BUILD
obj-$(CONFIG_$(SPL_)CMD_TLV_EEPROM) += tlv_eeprom.o
# core command
obj-y += nvedit.o

1105
cmd/tlv_eeprom.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -29,6 +29,8 @@ CONFIG_DISPLAY_BOARDINFO_LATE=y
CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_DATA_PART_OFFSET=0x1
CONFIG_SPL_DM_GPIO=y
CONFIG_SPL_I2C_SUPPORT=y
CONFIG_CMD_TLV_EEPROM=y
CONFIG_SPL_CMD_TLV_EEPROM=y
# CONFIG_CMD_FLASH is not set
CONFIG_CMD_GPIO=y
CONFIG_CMD_I2C=y
@ -50,6 +52,8 @@ CONFIG_DM_GPIO=y
CONFIG_DM_PCA953X=y
CONFIG_DM_I2C=y
CONFIG_SYS_I2C_MVTWSI=y
CONFIG_I2C_EEPROM=y
CONFIG_SPL_I2C_EEPROM=y
CONFIG_DM_MMC=y
CONFIG_SUPPORT_EMMC_BOOT=y
CONFIG_MMC_SDHCI=y

View file

@ -280,8 +280,14 @@ int ddr3_tip_configure_cs(u32 dev_num, u32 if_id, u32 cs_num, u32 enable)
{
u32 data, addr_hi, data_high;
u32 mem_index;
u32 clk_enable;
struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
if (tm->clk_enable & (1 << cs_num))
clk_enable = 1;
else
clk_enable = enable;
if (enable == 1) {
data = (tm->interface_params[if_id].bus_width ==
MV_DDR_DEV_WIDTH_8BIT) ? 0 : 1;
@ -316,13 +322,13 @@ int ddr3_tip_configure_cs(u32 dev_num, u32 if_id, u32 cs_num, u32 enable)
case 2:
CHECK_STATUS(ddr3_tip_if_write
(dev_num, ACCESS_TYPE_UNICAST, if_id,
DUNIT_CTRL_LOW_REG, (enable << (cs_num + 11)),
DUNIT_CTRL_LOW_REG, (clk_enable << (cs_num + 11)),
1 << (cs_num + 11)));
break;
case 3:
CHECK_STATUS(ddr3_tip_if_write
(dev_num, ACCESS_TYPE_UNICAST, if_id,
DUNIT_CTRL_LOW_REG, (enable << 15), 1 << 15));
DUNIT_CTRL_LOW_REG, (clk_enable << 15), 1 << 15));
break;
}

View file

@ -124,6 +124,9 @@ struct mv_ddr_topology_map {
/* electrical parameters */
unsigned int electrical_data[MV_DDR_EDATA_LAST];
/* Clock enable mask */
u32 clk_enable;
};
enum mv_ddr_iface_mode {

152
include/tlv_eeprom.h Normal file
View file

@ -0,0 +1,152 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* See file CREDITS for list of people who contributed to this
* project.
*/
#ifndef __TLV_EEPROM_H_
#define __TLV_EEPROM_H_
/*
* The Definition of the TlvInfo EEPROM format can be found at onie.org or
* github.com/onie
*/
/*
* TlvInfo header: Layout of the header for the TlvInfo format
*
* See the end of this file for details of this eeprom format
*/
struct __attribute__ ((__packed__)) tlvinfo_header {
char signature[8]; /* 0x00 - 0x07 EEPROM Tag "TlvInfo" */
u8 version; /* 0x08 Structure version */
u16 totallen; /* 0x09 - 0x0A Length of all data which follows */
};
// Header Field Constants
#define TLV_INFO_ID_STRING "TlvInfo"
#define TLV_INFO_VERSION 0x01
#define TLV_INFO_MAX_LEN 2048
#define TLV_TOTAL_LEN_MAX (TLV_INFO_MAX_LEN - \
sizeof(struct tlvinfo_header))
/*
* TlvInfo TLV: Layout of a TLV field
*/
struct __attribute__ ((__packed__)) tlvinfo_tlv {
u8 type;
u8 length;
u8 value[0];
};
/* Maximum length of a TLV value in bytes */
#define TLV_VALUE_MAX_LEN 255
/**
* The TLV Types.
*
* Keep these in sync with tlv_code_list in cmd/tlv_eeprom.c
*/
#define TLV_CODE_PRODUCT_NAME 0x21
#define TLV_CODE_PART_NUMBER 0x22
#define TLV_CODE_SERIAL_NUMBER 0x23
#define TLV_CODE_MAC_BASE 0x24
#define TLV_CODE_MANUF_DATE 0x25
#define TLV_CODE_DEVICE_VERSION 0x26
#define TLV_CODE_LABEL_REVISION 0x27
#define TLV_CODE_PLATFORM_NAME 0x28
#define TLV_CODE_ONIE_VERSION 0x29
#define TLV_CODE_MAC_SIZE 0x2A
#define TLV_CODE_MANUF_NAME 0x2B
#define TLV_CODE_MANUF_COUNTRY 0x2C
#define TLV_CODE_VENDOR_NAME 0x2D
#define TLV_CODE_DIAG_VERSION 0x2E
#define TLV_CODE_SERVICE_TAG 0x2F
#define TLV_CODE_VENDOR_EXT 0xFD
#define TLV_CODE_CRC_32 0xFE
#if CONFIG_IS_ENABLED(CMD_TLV_EEPROM)
/**
* read_tlv_eeprom - Read the EEPROM binary data from the hardware
* @eeprom: Pointer to buffer to hold the binary data
* @offset: Offset within EEPROM block to read data from
* @len : Maximum size of buffer
* @dev : EEPROM device to read
*
* Note: this routine does not validate the EEPROM data.
*
*/
int read_tlv_eeprom(void *eeprom, int offset, int len, int dev);
/**
* write_tlv_eeprom - Write the entire EEPROM binary data to the hardware
* @eeprom: Pointer to buffer to hold the binary data
* @len : Maximum size of buffer
*
* Note: this routine does not validate the EEPROM data.
*
*/
int write_tlv_eeprom(void *eeprom, int len);
/**
* read_tlvinfo_tlv_eeprom - Read the TLV from EEPROM, and validate
* @eeprom: Pointer to buffer to hold the binary data. Must point to a buffer
* of size at least TLV_INFO_MAX_LEN.
* @hdr : Points to pointer to TLV header (output)
* @first_entry : Points to pointer to first TLV entry (output)
* @dev : EEPROM device to read
*
* Store the raw EEPROM data from EEPROM @dev in the @eeprom buffer. If TLV is
* valid set *@hdr and *@first_entry.
*
* Returns 0 when read from EEPROM is successful, and the data is valid.
* Returns <0 error value when EEPROM read fails. Return -EINVAL when TLV is
* invalid.
*
*/
int read_tlvinfo_tlv_eeprom(void *eeprom, struct tlvinfo_header **hdr,
struct tlvinfo_tlv **first_entry, int dev);
#else /* !CONFIG_IS_ENABLED(CMD_TLV_EEPROM) */
static inline int read_tlv_eeprom(void *eeprom, int offset, int len, int dev)
{
return -ENOTSUPP;
}
static inline int write_tlv_eeprom(void *eeprom, int len)
{
return -ENOTSUPP;
}
static inline int
read_tlvinfo_tlv_eeprom(void *eeprom, struct tlvinfo_header **hdr,
struct tlvinfo_tlv **first_entry, int dev)
{
return -ENOTSUPP;
}
#endif /* CONFIG_IS_ENABLED(CMD_TLV_EEPROM) */
/**
* is_valid_tlvinfo_header
*
* Perform sanity checks on the first 11 bytes of the TlvInfo EEPROM
* data pointed to by the parameter:
* 1. First 8 bytes contain null-terminated ASCII string "TlvInfo"
* 2. Version byte is 1
* 3. Total length bytes contain value which is less than or equal
* to the allowed maximum (2048-11)
*
*/
static inline bool is_valid_tlvinfo_header(struct tlvinfo_header *hdr)
{
return ((strcmp(hdr->signature, TLV_INFO_ID_STRING) == 0) &&
(hdr->version == TLV_INFO_VERSION) &&
(be16_to_cpu(hdr->totallen) <= TLV_TOTAL_LEN_MAX));
}
#endif /* __TLV_EEPROM_H_ */