On Thu, 2023-08-17 at 10:57 +0200, Teresa Remmet wrote: > Recent shipped PHYTEC SoMs come with an i2c EEPROM containing > information about the hardware such as board revision and variant. > This can be used for RAM detection and loading device tree overlays > during kernel start. > > Signed-off-by: Teresa Remmet <[email protected]>
Reviewed-by: Yannic Moog <[email protected]> Tested-by: Yannic Moog <[email protected]> > --- > Changes in v2: > - none > --- > board/phytec/common/Kconfig | 5 + > board/phytec/common/Makefile | 10 ++ > board/phytec/common/phytec_som_detection.c | 188 > +++++++++++++++++++++ > board/phytec/common/phytec_som_detection.h | 104 ++++++++++++ > 4 files changed, 307 insertions(+) > create mode 100644 board/phytec/common/Kconfig > create mode 100644 board/phytec/common/Makefile > create mode 100644 board/phytec/common/phytec_som_detection.c > create mode 100644 board/phytec/common/phytec_som_detection.h > > diff --git a/board/phytec/common/Kconfig b/board/phytec/common/Kconfig > new file mode 100644 > index 000000000000..d614d45b1d60 > --- /dev/null > +++ b/board/phytec/common/Kconfig > @@ -0,0 +1,5 @@ > +config PHYTEC_SOM_DETECTION > + bool "Support SoM detection for PHYTEC platforms" > + select SPL_CRC8 if SPL > + help > + Support of I2C EEPROM based SoM detection. > diff --git a/board/phytec/common/Makefile > b/board/phytec/common/Makefile > new file mode 100644 > index 000000000000..5fe8725ef684 > --- /dev/null > +++ b/board/phytec/common/Makefile > @@ -0,0 +1,10 @@ > +# SPDX-License-Identifier: GPL-2.0+ > +# Copyright (C) 2023 PHYTEC Messtechnik GmbH > +# Author: Teresa Remmet <[email protected]> > + > +ifdef CONFIG_SPL_BUILD > +# necessary to create built-in.o > +obj- := __dummy__.o > +endif > + > +obj-$(CONFIG_PHYTEC_SOM_DETECTION) += phytec_som_detection.o > diff --git a/board/phytec/common/phytec_som_detection.c > b/board/phytec/common/phytec_som_detection.c > new file mode 100644 > index 000000000000..366bdd4ace4b > --- /dev/null > +++ b/board/phytec/common/phytec_som_detection.c > @@ -0,0 +1,188 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > +/* > + * Copyright (C) 2023 PHYTEC Messtechnik GmbH > + * Author: Teresa Remmet <[email protected]> > + */ > + > +#include <common.h> > +#include <asm/mach-imx/mxc_i2c.h> > +#include <asm/arch/sys_proto.h> > +#include <dm/device.h> > +#include <dm/uclass.h> > +#include <i2c.h> > +#include <u-boot/crc.h> > + > +#include "phytec_som_detection.h" > + > +struct phytec_eeprom_data eeprom_data; > + > +int phytec_eeprom_data_setup_fallback(struct phytec_eeprom_data > *data, > + int bus_num, int addr, int > addr_fallback) > +{ > + int ret; > + > + ret = phytec_eeprom_data_init(data, bus_num, addr); > + if (ret) { > + pr_err("%s: init failed. Trying fall back address > 0x%x\n", > + __func__, addr_fallback); > + ret = phytec_eeprom_data_init(data, bus_num, > addr_fallback); > + } > + > + if (ret) > + pr_err("%s: EEPROM data init failed\n", __func__); > + > + return ret; > +} > + > +int phytec_eeprom_data_setup(struct phytec_eeprom_data *data, > + int bus_num, int addr) > +{ > + int ret; > + > + ret = phytec_eeprom_data_init(data, bus_num, addr); > + if (ret) > + pr_err("%s: EEPROM data init failed\n", __func__); > + > + return ret; > +} > + > +int phytec_eeprom_data_init(struct phytec_eeprom_data *data, > + int bus_num, int addr) > +{ > + int ret, i; > + unsigned int crc; > + int *ptr; > + > + if (!data) > + data = &eeprom_data; > + > +#if CONFIG_IS_ENABLED(DM_I2C) > + struct udevice *dev; > + > + 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; > + } > + > + ret = dm_i2c_read(dev, 0, (uint8_t *)data, > + sizeof(struct phytec_eeprom_data)); > + if (ret) { > + pr_err("%s: Unable to read EEPROM data\n", __func__); > + return ret; > + } > +#else > + i2c_set_bus_num(bus_num); > + ret = i2c_read(addr, 0, 2, (uint8_t *)data, > + sizeof(struct phytec_eeprom_data)); > +#endif > + > + if (data->api_rev == 0xff) { > + pr_err("%s: EEPROM is not flashed. Prototype?\n", > __func__); > + return -EINVAL; > + } > + > + ptr = (int *)data; > + for (i = 0; i < sizeof(struct phytec_eeprom_data); i += > sizeof(ptr)) > + if (*ptr != 0x0) > + break; > + > + if (i == sizeof(struct phytec_eeprom_data)) { > + pr_err("%s: EEPROM data is all zero. Erased?\n", > __func__); > + return -EINVAL; > + } > + > + /* We are done here for early revisions */ > + if (data->api_rev <= PHYTEC_API_REV1) > + return 0; > + > + crc = crc8(0, (const unsigned char *)data, > + sizeof(struct phytec_eeprom_data)); > + debug("%s: crc: %x\n", __func__, crc); > + > + if (crc) { > + pr_err("%s: CRC mismatch. EEPROM data is not > usable\n", > + __func__); > + return -EINVAL; > + } > + > + return 0; > +} > + > +void __maybe_unused phytec_print_som_info(struct phytec_eeprom_data > *data) > +{ > + struct phytec_api2_data *api2; > + char pcb_sub_rev; > + unsigned int ksp_no, sub_som_type1, sub_som_type2; > + > + if (!data) > + data = &eeprom_data; > + > + if (data->api_rev < PHYTEC_API_REV2) > + return; > + > + api2 = &data->data.data_api2; > + > + /* Calculate PCB subrevision */ > + pcb_sub_rev = api2->pcb_sub_opt_rev & 0x0f; > + pcb_sub_rev = pcb_sub_rev ? ((pcb_sub_rev - 1) + 'a') : ' '; > + > + /* print standard product string */ > + if (api2->som_type <= 1) { > + printf("SoM: %s-%03u-%s.%s PCB rev: %u%c\n", > + phytec_som_type_str[api2->som_type], api2- > >som_no, > + api2->opt, api2->bom_rev, api2->pcb_rev, > pcb_sub_rev); > + return; > + } > + /* print KSP/KSM string */ > + if (api2->som_type <= 3) { > + ksp_no = (api2->ksp_no << 8) | api2->som_no; > + printf("SoM: %s-%u ", > + phytec_som_type_str[api2->som_type], ksp_no); > + /* print standard product based KSP/KSM strings */ > + } else { > + switch (api2->som_type) { > + case 4: > + sub_som_type1 = 0; > + sub_som_type2 = 3; > + break; > + case 5: > + sub_som_type1 = 0; > + sub_som_type2 = 2; > + break; > + case 6: > + sub_som_type1 = 1; > + sub_som_type2 = 3; > + break; > + case 7: > + sub_som_type1 = 1; > + sub_som_type2 = 2; > + break; > + default: > + break; > + }; > + > + printf("SoM: %s-%03u-%s-%03u ", > + phytec_som_type_str[sub_som_type1], > + api2->som_no, > phytec_som_type_str[sub_som_type2], > + api2->ksp_no); > + } > + > + printf("Option: %s BOM rev: %s PCB rev: %u%c\n", api2->opt, > + api2->bom_rev, api2->pcb_rev, pcb_sub_rev); > +} > + > +char * __maybe_unused phytec_get_opt(struct phytec_eeprom_data *data) > +{ > + char *opt; > + > + if (!data) > + data = &eeprom_data; > + > + if (data->api_rev < PHYTEC_API_REV2) > + opt = data->data.data_api0.opt; > + else > + opt = data->data.data_api2.opt; > + > + return opt; > +} > diff --git a/board/phytec/common/phytec_som_detection.h > b/board/phytec/common/phytec_som_detection.h > new file mode 100644 > index 000000000000..01f7e4652ddb > --- /dev/null > +++ b/board/phytec/common/phytec_som_detection.h > @@ -0,0 +1,104 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +/* > + * Copyright (C) 2023 PHYTEC Messtechnik GmbH > + * Author: Teresa Remmet <[email protected]> > + */ > + > +#ifndef _PHYTEC_SOM_DETECTION_H > +#define _PHYTEC_SOM_DETECTION_H > + > +#define PHYTEC_MAX_OPTIONS 17 > +#define PHYTEC_EEPROM_INVAL 0xff > + > +#define PHYTEC_GET_OPTION(option) \ > + (((option) > '9') ? (option) - 'A' + 10 : (option) - '0') > + > +enum { > + PHYTEC_API_REV0 = 0, > + PHYTEC_API_REV1, > + PHYTEC_API_REV2, > +}; > + > +static const char * const phytec_som_type_str[] = { > + "PCM", > + "PCL", > + "KSM", > + "KSP", > +}; > + > +struct phytec_api0_data { > + u8 pcb_rev; /* PCB revision of SoM */ > + u8 som_type; /* SoM type */ > + u8 ksp_no; /* KSP no */ > + char opt[16]; /* SoM options */ > + u8 mac[6]; /* MAC address (optional) */ > + u8 pad[5]; /* padding */ > + u8 cksum; /* checksum */ > +} __packed; > + > +struct phytec_api2_data { > + u8 pcb_rev; /* PCB revision of SoM */ > + u8 pcb_sub_opt_rev; /* PCB subrevision and opt revision */ > + u8 som_type; /* SoM type */ > + u8 som_no; /* SoM number */ > + u8 ksp_no; /* KSP information */ > + char opt[PHYTEC_MAX_OPTIONS]; /* SoM options */ > + char bom_rev[2]; /* BOM revision */ > + u8 mac[6]; /* MAC address (optional) */ > + u8 crc8; /* checksum */ > +} __packed; > + > +struct phytec_eeprom_data { > + u8 api_rev; > + union { > + struct phytec_api0_data data_api0; > + struct phytec_api2_data data_api2; > + } data; > +} __packed; > + > +#if IS_ENABLED(CONFIG_PHYTEC_SOM_DETECTION) > + > +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); > +void __maybe_unused phytec_print_som_info(struct phytec_eeprom_data > *data); > + > +char * __maybe_unused phytec_get_opt(struct phytec_eeprom_data > *data); > + > +#else > + > +inline int phytec_eeprom_data_setup(struct phytec_eeprom_data *data, > + int bus_num, int addr) > +{ > + return PHYTEC_EEPROM_INVAL; > +} > + > +inline int phytec_eeprom_data_setup_fallback(struct > phytec_eeprom_data *data, > + int bus_num, int addr, > + int addr_fallback) > +{ > + return PHYTEC_EEPROM_INVAL; > +} > + > +inline int phytec_eeprom_data_init(struct phytec_eeprom_data *data, > + int bus_num, int addr) > +{ > + return PHYTEC_EEPROM_INVAL; > +} > + > +inline void __maybe_unused phytec_print_som_info(struct > phytec_eeprom_data *data) > +{ > +} > + > +inline char *__maybe_unused phytec_get_opt(struct phytec_eeprom_data > *data) > +{ > + return NULL; > +} > + > +#endif /* IS_ENABLED(CONFIG_PHYTEC_SOM_DETECTION) */ > + > +#endif /* _PHYTEC_SOM_DETECTION_H */

