1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (C) 2023 PHYTEC Messtechnik GmbH 4 * Author: Teresa Remmet <t.remmet@phytec.de> 5 */ 6 7#include <common.h> 8#include <dm/device.h> 9#include <dm/uclass.h> 10#include <i2c.h> 11#include <u-boot/crc.h> 12#include <malloc.h> 13#include <extension_board.h> 14 15#include "phytec_som_detection.h" 16 17struct phytec_eeprom_data eeprom_data; 18 19#if IS_ENABLED(CONFIG_PHYTEC_SOM_DETECTION) 20 21int phytec_eeprom_data_setup_fallback(struct phytec_eeprom_data *data, 22 int bus_num, int addr, int addr_fallback) 23{ 24 int ret; 25 26 ret = phytec_eeprom_data_init(data, bus_num, addr); 27 if (ret) { 28 pr_err("%s: init failed. Trying fall back address 0x%x\n", 29 __func__, addr_fallback); 30 ret = phytec_eeprom_data_init(data, bus_num, addr_fallback); 31 } 32 33 if (ret) 34 pr_err("%s: EEPROM data init failed\n", __func__); 35 36 return ret; 37} 38 39int phytec_eeprom_data_setup(struct phytec_eeprom_data *data, 40 int bus_num, int addr) 41{ 42 int ret; 43 44 ret = phytec_eeprom_data_init(data, bus_num, addr); 45 if (ret) 46 pr_err("%s: EEPROM data init failed\n", __func__); 47 48 return ret; 49} 50 51int phytec_eeprom_data_init(struct phytec_eeprom_data *data, 52 int bus_num, int addr) 53{ 54 int ret, i; 55 unsigned int crc; 56 u8 *ptr; 57 const unsigned int payload_size = sizeof(struct phytec_eeprom_payload); 58 59 if (!data) 60 data = &eeprom_data; 61 62#if CONFIG_IS_ENABLED(DM_I2C) 63 struct udevice *dev; 64 65 ret = i2c_get_chip_for_busnum(bus_num, addr, 2, &dev); 66 if (ret) { 67 pr_err("%s: i2c EEPROM not found: %i.\n", __func__, ret); 68 goto err; 69 } 70 71 ret = dm_i2c_read(dev, 0, (uint8_t *)data, payload_size); 72 if (ret) { 73 pr_err("%s: Unable to read EEPROM data: %i\n", __func__, ret); 74 goto err; 75 } 76#else 77 i2c_set_bus_num(bus_num); 78 ret = i2c_read(addr, 0, 2, (uint8_t *)data, 79 sizeof(struct phytec_eeprom_data)); 80#endif 81 82 if (data->payload.api_rev == 0xff) { 83 pr_err("%s: EEPROM is not flashed. Prototype?\n", __func__); 84 ret = -EINVAL; 85 goto err; 86 } 87 88 ptr = (u8 *)data; 89 for (i = 0; i < payload_size; ++i) 90 if (ptr[i] != 0x0) 91 break; 92 93 if (i == payload_size) { 94 pr_err("%s: EEPROM data is all zero. Erased?\n", __func__); 95 ret = -EINVAL; 96 goto err; 97 } 98 99 /* We are done here for early revisions */ 100 if (data->payload.api_rev <= PHYTEC_API_REV1) { 101 data->valid = true; 102 return 0; 103 } 104 105 crc = crc8(0, (const unsigned char *)&data->payload, payload_size); 106 debug("%s: crc: %x\n", __func__, crc); 107 108 if (crc) { 109 pr_err("%s: CRC mismatch. EEPROM data is not usable.\n", 110 __func__); 111 ret = -EINVAL; 112 goto err; 113 } 114 115 data->valid = true; 116 return 0; 117err: 118 data->valid = false; 119 return ret; 120} 121 122void __maybe_unused phytec_print_som_info(struct phytec_eeprom_data *data) 123{ 124 struct phytec_api2_data *api2; 125 char pcb_sub_rev; 126 unsigned int ksp_no, sub_som_type1, sub_som_type2; 127 128 if (!data) 129 data = &eeprom_data; 130 131 if (!data->valid || data->payload.api_rev < PHYTEC_API_REV2) 132 return; 133 134 api2 = &data->payload.data.data_api2; 135 136 /* Calculate PCB subrevision */ 137 pcb_sub_rev = api2->pcb_sub_opt_rev & 0x0f; 138 pcb_sub_rev = pcb_sub_rev ? ((pcb_sub_rev - 1) + 'a') : ' '; 139 140 /* print standard product string */ 141 if (api2->som_type <= 1) { 142 printf("SoM: %s-%03u-%s.%s PCB rev: %u%c\n", 143 phytec_som_type_str[api2->som_type], api2->som_no, 144 api2->opt, api2->bom_rev, api2->pcb_rev, pcb_sub_rev); 145 return; 146 } 147 /* print KSP/KSM string */ 148 if (api2->som_type <= 3) { 149 ksp_no = (api2->ksp_no << 8) | api2->som_no; 150 printf("SoM: %s-%u ", 151 phytec_som_type_str[api2->som_type], ksp_no); 152 /* print standard product based KSP/KSM strings */ 153 } else { 154 switch (api2->som_type) { 155 case 4: 156 sub_som_type1 = 0; 157 sub_som_type2 = 3; 158 break; 159 case 5: 160 sub_som_type1 = 0; 161 sub_som_type2 = 2; 162 break; 163 case 6: 164 sub_som_type1 = 1; 165 sub_som_type2 = 3; 166 break; 167 case 7: 168 sub_som_type1 = 1; 169 sub_som_type2 = 2; 170 break; 171 default: 172 pr_err("%s: Invalid SoM type: %i", __func__, api2->som_type); 173 return; 174 }; 175 176 printf("SoM: %s-%03u-%s-%03u ", 177 phytec_som_type_str[sub_som_type1], 178 api2->som_no, phytec_som_type_str[sub_som_type2], 179 api2->ksp_no); 180 } 181 182 printf("Option: %s BOM rev: %s PCB rev: %u%c\n", api2->opt, 183 api2->bom_rev, api2->pcb_rev, pcb_sub_rev); 184} 185 186char * __maybe_unused phytec_get_opt(struct phytec_eeprom_data *data) 187{ 188 char *opt; 189 190 if (!data) 191 data = &eeprom_data; 192 193 if (!data->valid) 194 return NULL; 195 196 if (data->payload.api_rev < PHYTEC_API_REV2) 197 opt = data->payload.data.data_api0.opt; 198 else 199 opt = data->payload.data.data_api2.opt; 200 201 return opt; 202} 203 204u8 __maybe_unused phytec_get_rev(struct phytec_eeprom_data *data) 205{ 206 struct phytec_api2_data *api2; 207 208 if (!data) 209 data = &eeprom_data; 210 211 if (!data->valid || data->payload.api_rev < PHYTEC_API_REV2) 212 return PHYTEC_EEPROM_INVAL; 213 214 api2 = &data->payload.data.data_api2; 215 216 return api2->pcb_rev; 217} 218 219u8 __maybe_unused phytec_get_som_type(struct phytec_eeprom_data *data) 220{ 221 if (!data) 222 data = &eeprom_data; 223 224 if (!data->valid || data->payload.api_rev < PHYTEC_API_REV2) 225 return PHYTEC_EEPROM_INVAL; 226 227 return data->payload.data.data_api2.som_type; 228} 229 230#if IS_ENABLED(CONFIG_CMD_EXTENSION) 231struct extension *phytec_add_extension(const char *name, const char *overlay, 232 const char *other) 233{ 234 struct extension *extension; 235 236 if (strlen(overlay) > sizeof(extension->overlay)) { 237 pr_err("Overlay name %s is longer than %lu.\n", overlay, 238 sizeof(extension->overlay)); 239 return NULL; 240 } 241 242 extension = calloc(1, sizeof(struct extension)); 243 snprintf(extension->name, sizeof(extension->name), name); 244 snprintf(extension->overlay, sizeof(extension->overlay), overlay); 245 snprintf(extension->other, sizeof(extension->other), other); 246 snprintf(extension->owner, sizeof(extension->owner), "PHYTEC"); 247 248 return extension; 249} 250#endif /* IS_ENABLED(CONFIG_CMD_EXTENSION) */ 251 252#else 253 254inline int phytec_eeprom_data_setup(struct phytec_eeprom_data *data, 255 int bus_num, int addr) 256{ 257 return PHYTEC_EEPROM_INVAL; 258} 259 260inline int phytec_eeprom_data_setup_fallback(struct phytec_eeprom_data *data, 261 int bus_num, int addr, 262 int addr_fallback) 263{ 264 return PHYTEC_EEPROM_INVAL; 265} 266 267inline int phytec_eeprom_data_init(struct phytec_eeprom_data *data, 268 int bus_num, int addr) 269{ 270 return PHYTEC_EEPROM_INVAL; 271} 272 273inline void __maybe_unused phytec_print_som_info(struct phytec_eeprom_data *data) 274{ 275} 276 277inline char *__maybe_unused phytec_get_opt(struct phytec_eeprom_data *data) 278{ 279 return NULL; 280} 281 282u8 __maybe_unused phytec_get_rev(struct phytec_eeprom_data *data) 283{ 284 return PHYTEC_EEPROM_INVAL; 285} 286 287u8 __maybe_unused phytec_get_som_type(struct phytec_eeprom_data *data) 288{ 289 return PHYTEC_EEPROM_INVAL; 290} 291 292#if IS_ENABLED(CONFIG_CMD_EXTENSION) 293inline struct extension *phytec_add_extension(const char *name, 294 const char *overlay, 295 const char *other) 296{ 297 return NULL; 298} 299#endif /* IS_ENABLED(CONFIG_CMD_EXTENSION) */ 300 301#endif /* IS_ENABLED(CONFIG_PHYTEC_SOM_DETECTION) */ 302