help
Support of I2C EEPROM based SoM detection. Supported
for PHYTEC i.MX8MM/i.MX8MP boards
+
+config PHYTEC_AM62_SOM_DETECTION
+ bool "Support SoM detection for AM62x PHYTEC platforms"
+ depends on (TARGET_PHYCORE_AM62X_A53 || TARGET_PHYCORE_AM62X_R5) && \
+ PHYTEC_SOM_DETECTION
+ default y
+ help
+ Support of I2C EEPROM based SoM detection. Supported
+ for PHYTEC AM62x boards.
+
+config PHYTEC_AM64_SOM_DETECTION
+ bool "Support SoM detection for AM64x PHYTEC platforms"
+ depends on (TARGET_PHYCORE_AM64X_A53 || TARGET_PHYCORE_AM64X_R5) && \
+ PHYTEC_SOM_DETECTION
+ default y
+ help
+ Support of I2C EEPROM based SoM detection. Supported
+ for PHYTEC AM64x boards.
endif
obj-y += phytec_som_detection.o
+obj-$(CONFIG_ARCH_K3) += am6_som_detection.o
obj-$(CONFIG_ARCH_IMX8M) += imx8m_som_detection.o
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2024 PHYTEC Messtechnik GmbH
+ */
+
+#include <asm/arch/hardware.h>
+
+#include "am6_som_detection.h"
+
+extern struct phytec_eeprom_data eeprom_data;
+
+#if IS_ENABLED(CONFIG_PHYTEC_AM62_SOM_DETECTION) || \
+ IS_ENABLED(CONFIG_PHYTEC_AM64_SOM_DETECTION)
+
+/* Check if the SoM is actually one of the following products:
+ * - phyCORE-AM62x
+ * - phyCORE-AM64x
+ *
+ * Returns 0 in case it's a known SoM. Otherwise, returns -1.
+ */
+int phytec_am6_detect(struct phytec_eeprom_data *data)
+{
+ char *opt;
+ u8 som;
+
+ if (!data)
+ data = &eeprom_data;
+
+ /* We cannot do the check for early API revisions */
+ if (!data->valid || data->payload.api_rev < PHYTEC_API_REV2)
+ return -1;
+
+ som = data->payload.data.data_api2.som_no;
+ debug("%s: som id: %u\n", __func__, som);
+
+ opt = phytec_get_opt(data);
+ if (!opt)
+ return -1;
+
+ if (som == PHYTEC_AM62X_SOM && soc_is_am62x())
+ return 0;
+
+ if (som == PHYTEC_AM64X_SOM && soc_is_am64x())
+ return 0;
+
+ return -1;
+}
+
+static u8 phytec_check_opt(struct phytec_eeprom_data *data, u8 option)
+{
+ char *opt;
+
+ if (!data)
+ data = &eeprom_data;
+
+ if (!data->valid || data->payload.api_rev < PHYTEC_API_REV2)
+ return PHYTEC_EEPROM_INVAL;
+
+ if (option > 8)
+ return PHYTEC_EEPROM_INVAL;
+
+ opt = phytec_get_opt(data);
+ if (opt)
+ return PHYTEC_GET_OPTION(opt[option]);
+ return PHYTEC_EEPROM_INVAL;
+}
+
+/*
+ * Reads LPDDR4 ram size from EEPROM.
+ *
+ * returns:
+ * - The size
+ * - PHYTEC_EEPROM_INVAL when the data is invalid.
+ */
+u8 __maybe_unused phytec_get_am62_ddr_size(struct phytec_eeprom_data *data)
+{
+ u8 ddr_id = phytec_check_opt(data, 3);
+
+ pr_debug("%s: ddr id: %u\n", __func__, ddr_id);
+ return ddr_id;
+}
+
+/*
+ * Reads SPI-NOR flash size and type from EEPROM.
+ *
+ * returns:
+ * - PHYTEC_EEPROM_VALUE_X if no SPI is poulated.
+ * - Otherwise a board depended code for the size.
+ * - PHYTEC_EEPROM_INVAL when the data is invalid.
+ */
+u8 __maybe_unused phytec_get_am62_spi(struct phytec_eeprom_data *data)
+{
+ u8 spi = phytec_check_opt(data, 5);
+
+ pr_debug("%s: spi: %u\n", __func__, spi);
+ return spi;
+}
+
+/*
+ * Reads Ethernet phy information from EEPROM.
+ *
+ * returns:
+ * - 0x0 no ethernet phy is populated.
+ * - 0x1 if 10/100/1000 MBit Phy is populated.
+ * - PHYTEC_EEPROM_INVAL when the data is invalid.
+ */
+u8 __maybe_unused phytec_get_am62_eth(struct phytec_eeprom_data *data)
+{
+ u8 eth = phytec_check_opt(data, 6);
+
+ pr_debug("%s: eth: %u\n", __func__, eth);
+ return eth;
+}
+
+/*
+ * Reads RTC information from EEPROM.
+ *
+ * returns:
+ * - 0 if no RTC is poulated.
+ * - 1 if it is populated.
+ * - PHYTEC_EEPROM_INVAL when the data is invalid.
+ */
+u8 __maybe_unused phytec_get_am62_rtc(struct phytec_eeprom_data *data)
+{
+ u8 rtc = phytec_check_opt(data, 7);
+
+ pr_debug("%s: rtc: %u\n", __func__, rtc);
+ return rtc;
+}
+
+#else
+
+inline int __maybe_unused phytec_am62_detect(struct phytec_eeprom_data *data)
+{
+ return -1;
+}
+
+inline u8 __maybe_unused
+phytec_get_am62_ddr_size(struct phytec_eeprom_data *data)
+{
+ return PHYTEC_EEPROM_INVAL;
+}
+
+inline u8 __maybe_unused phytec_get_am62_spi(struct phytec_eeprom_data *data)
+{
+ return PHYTEC_EEPROM_INVAL;
+}
+
+inline u8 __maybe_unused phytec_get_am62_eth(struct phytec_eeprom_data *data)
+{
+ return PHYTEC_EEPROM_INVAL;
+}
+
+inline u8 __maybe_unused phytec_get_am62_rtc(struct phytec_eeprom_data *data)
+{
+ return PHYTEC_EEPROM_INVAL;
+}
+#endif
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2024 PHYTEC Messtechnik GmbH
+ */
+
+#ifndef _PHYTEC_AM6_SOM_DETECTION_H
+#define _PHYTEC_AM6_SOM_DETECTION_H
+
+#include "phytec_som_detection.h"
+
+#define PHYTEC_AM62X_SOM 71
+#define PHYTEC_AM64X_SOM 72
+#define PHYTEC_EEPROM_VALUE_X 0x21
+#define PHYTEC_EEPROM_NOR_FLASH_64MB_QSPI 0xC
+
+int __maybe_unused phytec_am6_detect(struct phytec_eeprom_data *data);
+u8 __maybe_unused phytec_get_am6_ddr_size(struct phytec_eeprom_data *data);
+u8 __maybe_unused phytec_get_am6_spi(struct phytec_eeprom_data *data);
+u8 __maybe_unused phytec_get_am6_eth(struct phytec_eeprom_data *data);
+u8 __maybe_unused phytec_get_am6_rtc(struct phytec_eeprom_data *data);
+
+static inline int phytec_am6_is_qspi(struct phytec_eeprom_data *data)
+{
+ u8 spi = phytec_get_am6_spi(data);
+
+ if (spi == PHYTEC_EEPROM_VALUE_X)
+ return 0;
+ return spi <= PHYTEC_EEPROM_NOR_FLASH_64MB_QSPI;
+}
+
+static inline int phytec_am6_is_ospi(struct phytec_eeprom_data *data)
+{
+ return phytec_get_am6_spi(data) > PHYTEC_EEPROM_NOR_FLASH_64MB_QSPI;
+}
+#endif /* _PHYTEC_AM6_SOM_DETECTION_H */
data = &eeprom_data;
/* We can not do the check for early API revisions */
- if (data->api_rev < PHYTEC_API_REV2)
+ if (!data->valid || data->payload.api_rev < PHYTEC_API_REV2)
return -1;
- som = data->data.data_api2.som_no;
+ som = data->payload.data.data_api2.som_no;
debug("%s: som id: %u\n", __func__, som);
opt = phytec_get_opt(data);
if (!data)
data = &eeprom_data;
+ if (!data->valid || data->payload.api_rev < PHYTEC_API_REV2)
+ return PHYTEC_EEPROM_INVAL;
+
opt = phytec_get_opt(data);
if (opt)
ddr_id = PHYTEC_GET_OPTION(opt[2]);
if (!data)
data = &eeprom_data;
- if (data->api_rev < PHYTEC_API_REV2)
+ if (!data->valid || data->payload.api_rev < PHYTEC_API_REV2)
return PHYTEC_EEPROM_INVAL;
opt = phytec_get_opt(data);
if (!data)
data = &eeprom_data;
- if (data->api_rev < PHYTEC_API_REV2)
+ if (!data->valid || data->payload.api_rev < PHYTEC_API_REV2)
return PHYTEC_EEPROM_INVAL;
opt = phytec_get_opt(data);
if (!data)
data = &eeprom_data;
- if (data->api_rev < PHYTEC_API_REV2)
+ if (!data->valid || data->payload.api_rev < PHYTEC_API_REV2)
return PHYTEC_EEPROM_INVAL;
opt = phytec_get_opt(data);
#include <dm/uclass.h>
#include <i2c.h>
#include <u-boot/crc.h>
+#include <malloc.h>
+#include <extension_board.h>
#include "phytec_som_detection.h"
{
int ret, i;
unsigned int crc;
- int *ptr;
+ u8 *ptr;
+ const unsigned int payload_size = sizeof(struct phytec_eeprom_payload);
if (!data)
data = &eeprom_data;
ret = i2c_get_chip_for_busnum(bus_num, addr, 2, &dev);
if (ret) {
pr_err("%s: i2c EEPROM not found: %i.\n", __func__, ret);
- return ret;
+ goto err;
}
- ret = dm_i2c_read(dev, 0, (uint8_t *)data,
- sizeof(struct phytec_eeprom_data));
+ ret = dm_i2c_read(dev, 0, (uint8_t *)data, payload_size);
if (ret) {
- pr_err("%s: Unable to read EEPROM data\n", __func__);
- return ret;
+ pr_err("%s: Unable to read EEPROM data: %i\n", __func__, ret);
+ goto err;
}
#else
i2c_set_bus_num(bus_num);
sizeof(struct phytec_eeprom_data));
#endif
- if (data->api_rev == 0xff) {
+ if (data->payload.api_rev == 0xff) {
pr_err("%s: EEPROM is not flashed. Prototype?\n", __func__);
- return -EINVAL;
+ ret = -EINVAL;
+ goto err;
}
- ptr = (int *)data;
- for (i = 0; i < sizeof(struct phytec_eeprom_data); i++)
+ ptr = (u8 *)data;
+ for (i = 0; i < payload_size; ++i)
if (ptr[i] != 0x0)
break;
- if (i == sizeof(struct phytec_eeprom_data)) {
+ if (i == payload_size) {
pr_err("%s: EEPROM data is all zero. Erased?\n", __func__);
- return -EINVAL;
+ ret = -EINVAL;
+ goto err;
}
/* We are done here for early revisions */
- if (data->api_rev <= PHYTEC_API_REV1)
+ if (data->payload.api_rev <= PHYTEC_API_REV1) {
+ data->valid = true;
return 0;
+ }
- crc = crc8(0, (const unsigned char *)data,
- sizeof(struct phytec_eeprom_data));
+ crc = crc8(0, (const unsigned char *)&data->payload, payload_size);
debug("%s: crc: %x\n", __func__, crc);
if (crc) {
- pr_err("%s: CRC mismatch. EEPROM data is not usable\n",
+ pr_err("%s: CRC mismatch. EEPROM data is not usable.\n",
__func__);
- return -EINVAL;
+ ret = -EINVAL;
+ goto err;
}
+ data->valid = true;
return 0;
+err:
+ data->valid = false;
+ return ret;
}
void __maybe_unused phytec_print_som_info(struct phytec_eeprom_data *data)
if (!data)
data = &eeprom_data;
- if (data->api_rev < PHYTEC_API_REV2)
+ if (!data->valid || data->payload.api_rev < PHYTEC_API_REV2)
return;
- api2 = &data->data.data_api2;
+ api2 = &data->payload.data.data_api2;
/* Calculate PCB subrevision */
pcb_sub_rev = api2->pcb_sub_opt_rev & 0x0f;
if (!data)
data = &eeprom_data;
- if (data->api_rev < PHYTEC_API_REV2)
- opt = data->data.data_api0.opt;
+ if (!data->valid)
+ return NULL;
+
+ if (data->payload.api_rev < PHYTEC_API_REV2)
+ opt = data->payload.data.data_api0.opt;
else
- opt = data->data.data_api2.opt;
+ opt = data->payload.data.data_api2.opt;
return opt;
}
if (!data)
data = &eeprom_data;
- if (data->api_rev < PHYTEC_API_REV2)
+ if (!data->valid || data->payload.api_rev < PHYTEC_API_REV2)
return PHYTEC_EEPROM_INVAL;
- api2 = &data->data.data_api2;
+ api2 = &data->payload.data.data_api2;
return api2->pcb_rev;
}
{
if (!data)
data = &eeprom_data;
- if (data->api_rev < PHYTEC_API_REV2)
+
+ if (!data->valid || data->payload.api_rev < PHYTEC_API_REV2)
return PHYTEC_EEPROM_INVAL;
- return data->data.data_api2.som_type;
+ return data->payload.data.data_api2.som_type;
}
+#if IS_ENABLED(CONFIG_CMD_EXTENSION)
+struct extension *phytec_add_extension(const char *name, const char *overlay,
+ const char *other)
+{
+ struct extension *extension;
+
+ if (strlen(overlay) > sizeof(extension->overlay)) {
+ pr_err("Overlay name %s is longer than %lu.\n", overlay,
+ sizeof(extension->overlay));
+ return NULL;
+ }
+
+ extension = calloc(1, sizeof(struct extension));
+ snprintf(extension->name, sizeof(extension->name), name);
+ snprintf(extension->overlay, sizeof(extension->overlay), overlay);
+ snprintf(extension->other, sizeof(extension->other), other);
+ snprintf(extension->owner, sizeof(extension->owner), "PHYTEC");
+
+ return extension;
+}
+#endif /* IS_ENABLED(CONFIG_CMD_EXTENSION) */
+
#else
inline int phytec_eeprom_data_setup(struct phytec_eeprom_data *data,
return PHYTEC_EEPROM_INVAL;
}
+#if IS_ENABLED(CONFIG_CMD_EXTENSION)
+inline struct extension *phytec_add_extension(const char *name,
+ const char *overlay,
+ const char *other)
+{
+ return NULL;
+}
+#endif /* IS_ENABLED(CONFIG_CMD_EXTENSION) */
+
#endif /* IS_ENABLED(CONFIG_PHYTEC_SOM_DETECTION) */
u8 crc8; /* checksum */
} __packed;
-struct phytec_eeprom_data {
+struct phytec_eeprom_payload {
u8 api_rev;
union {
struct phytec_api0_data data_api0;
} data;
} __packed;
+struct phytec_eeprom_data {
+ struct phytec_eeprom_payload payload;
+ bool valid;
+};
+
int phytec_eeprom_data_setup_fallback(struct phytec_eeprom_data *data,
int bus_num, int addr,
int addr_fallback);
int phytec_eeprom_data_setup(struct phytec_eeprom_data *data,
int bus_num, int addr);
-int phytec_eeprom_data_init(struct phytec_eeprom_data *data,
- int bus_num, int addr);
+int phytec_eeprom_data_init(struct phytec_eeprom_data *data, int bus_num,
+ int addr);
void __maybe_unused phytec_print_som_info(struct phytec_eeprom_data *data);
char * __maybe_unused phytec_get_opt(struct phytec_eeprom_data *data);
u8 __maybe_unused phytec_get_rev(struct phytec_eeprom_data *data);
u8 __maybe_unused phytec_get_som_type(struct phytec_eeprom_data *data);
+#if IS_ENABLED(CONFIG_CMD_EXTENSION)
+struct extension *phytec_add_extension(const char *name, const char *overlay,
+ const char *other);
+#endif /* IS_ENABLED(CONFIG_CMD_EXTENSION) */
+
#endif /* _PHYTEC_SOM_DETECTION_H */
config SYS_CONFIG_NAME
default "phycore_am62x"
+source "board/phytec/common/Kconfig"
+
endif
if TARGET_PHYCORE_AM62X_R5
config SPL_LDSCRIPT
default "arch/arm/mach-omap2/u-boot-spl.lds"
+source "board/phytec/common/Kconfig"
+
endif
config SYS_CONFIG_NAME
default "phycore_am64x"
+source "board/phytec/common/Kconfig"
+
endif
if TARGET_PHYCORE_AM64X_R5
config SYS_CONFIG_NAME
default "phycore_am64x"
+source "board/phytec/common/Kconfig"
+
endif