diff options
-rw-r--r-- | board/phytec/common/Kconfig | 5 | ||||
-rw-r--r-- | board/phytec/common/Makefile | 10 | ||||
-rw-r--r-- | board/phytec/common/phytec_som_detection.c | 188 | ||||
-rw-r--r-- | board/phytec/common/phytec_som_detection.h | 104 |
4 files changed, 307 insertions, 0 deletions
diff --git a/board/phytec/common/Kconfig b/board/phytec/common/Kconfig new file mode 100644 index 0000000000..d614d45b1d --- /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 0000000000..5fe8725ef6 --- /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 <t.remmet@phytec.de> + +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 0000000000..366bdd4ace --- /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 <t.remmet@phytec.de> + */ + +#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 0000000000..01f7e4652d --- /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 <t.remmet@phytec.de> + */ + +#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 */ |