1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Copyright 2021 Collabora Ltd. 4 * Copyright 2018-2020 Variscite Ltd. 5 * Copyright 2023 DimOnOff Inc. 6 */ 7 8#include <common.h> 9#include <dm.h> 10#include <env.h> 11#include <fdtdec.h> 12#include <fdt_support.h> 13#include <i2c_eeprom.h> 14#include <malloc.h> 15#include <asm/global_data.h> 16#include <dt-bindings/gpio/gpio.h> 17#include <linux/libfdt.h> 18 19DECLARE_GLOBAL_DATA_PTR; 20 21/* Optional SOM features flags. */ 22#define VAR_EEPROM_F_WIFI BIT(0) 23#define VAR_EEPROM_F_ETH BIT(1) /* Ethernet PHY on SOM. */ 24#define VAR_EEPROM_F_AUDIO BIT(2) 25#define VAR_EEPROM_F_MX8M_LVDS BIT(3) /* i.MX8MM, i.MX8MN, i.MX8MQ only */ 26#define VAR_EEPROM_F_MX8Q_SOC_ID BIT(3) /* 0 = i.MX8QM, 1 = i.MX8QP */ 27#define VAR_EEPROM_F_NAND BIT(4) 28 29#define VAR_IMX8_EEPROM_MAGIC 0x384D /* "8M" */ 30 31/* Number of DRAM adjustment tables. */ 32#define DRAM_TABLES_NUM 7 33 34struct var_imx8_eeprom_info { 35 u16 magic; 36 u8 partnumber[3]; /* Part number */ 37 u8 assembly[10]; /* Assembly number */ 38 u8 date[9]; /* Build date */ 39 u8 mac[6]; /* MAC address */ 40 u8 somrev; 41 u8 eeprom_version; 42 u8 features; /* SOM features */ 43 u8 dramsize; /* DRAM size */ 44 u8 off[DRAM_TABLES_NUM + 1]; /* DRAM table offsets */ 45 u8 partnumber2[5]; /* Part number 2 */ 46} __packed; 47 48int board_init(void) 49{ 50 return 0; 51} 52 53int board_mmc_get_env_dev(int devno) 54{ 55 return devno; 56} 57 58#if !defined(CONFIG_SPL_BUILD) 59 60#if defined(CONFIG_DISPLAY_BOARDINFO) 61 62static void display_som_infos(struct var_imx8_eeprom_info *info) 63{ 64 char partnumber[sizeof(info->partnumber) + 65 sizeof(info->partnumber2) + 1]; 66 char assembly[sizeof(info->assembly) + 1]; 67 char date[sizeof(info->date) + 1]; 68 69 /* Read first part of P/N. */ 70 memcpy(partnumber, info->partnumber, sizeof(info->partnumber)); 71 72 /* Read second part of P/N. */ 73 if (info->eeprom_version >= 3) 74 memcpy(partnumber + sizeof(info->partnumber), info->partnumber2, 75 sizeof(info->partnumber2)); 76 77 memcpy(assembly, info->assembly, sizeof(info->assembly)); 78 memcpy(date, info->date, sizeof(info->date)); 79 80 /* Make sure strings are null terminated. */ 81 partnumber[sizeof(partnumber) - 1] = '\0'; 82 assembly[sizeof(assembly) - 1] = '\0'; 83 date[sizeof(date) - 1] = '\0'; 84 85 printf("SOM board: P/N: %s, Assy: %s, Date: %s\n" 86 " Wifi: %s, EthPhy: %s, Rev: %d\n", 87 partnumber, assembly, date, 88 info->features & VAR_EEPROM_F_WIFI ? "yes" : "no", 89 info->features & VAR_EEPROM_F_ETH ? "yes" : "no", 90 info->somrev); 91} 92 93static int var_read_som_eeprom(struct var_imx8_eeprom_info *info) 94{ 95 const char *path = "eeprom-som"; 96 struct udevice *dev; 97 int ret, off; 98 99 off = fdt_path_offset(gd->fdt_blob, path); 100 if (off < 0) { 101 pr_err("%s: fdt_path_offset() failed: %d\n", __func__, off); 102 return off; 103 } 104 105 ret = uclass_get_device_by_of_offset(UCLASS_I2C_EEPROM, off, &dev); 106 if (ret) { 107 pr_err("%s: uclass_get_device_by_of_offset() failed: %d\n", 108 __func__, ret); 109 return ret; 110 } 111 112 ret = i2c_eeprom_read(dev, 0, (uint8_t *)info, 113 sizeof(struct var_imx8_eeprom_info)); 114 if (ret) { 115 pr_err("%s: i2c_eeprom_read() failed: %d\n", __func__, ret); 116 return ret; 117 } 118 119 if (htons(info->magic) != VAR_IMX8_EEPROM_MAGIC) { 120 /* Do not fail if the content is invalid */ 121 pr_err("Board: Invalid board info magic: 0x%08x, expected 0x%08x\n", 122 htons(info->magic), VAR_IMX8_EEPROM_MAGIC); 123 } 124 125 return 0; 126} 127 128int checkboard(void) 129{ 130 int rc; 131 struct var_imx8_eeprom_info *info; 132 133 info = malloc(sizeof(struct var_imx8_eeprom_info)); 134 if (!info) 135 return -ENOMEM; 136 137 rc = var_read_som_eeprom(info); 138 if (rc) 139 return rc; 140 141 display_som_infos(info); 142 143#if defined(CONFIG_BOARD_TYPES) 144 gd->board_type = info->features; 145#endif /* CONFIG_BOARD_TYPES */ 146 147 return 0; 148} 149 150#endif /* CONFIG_DISPLAY_BOARDINFO */ 151 152static int insert_gpios_prop(void *blob, int node, const char *prop, 153 unsigned int phandle, u32 gpio, u32 flags) 154{ 155 fdt32_t val[3] = { cpu_to_fdt32(phandle), cpu_to_fdt32(gpio), 156 cpu_to_fdt32(flags) }; 157 return fdt_setprop(blob, node, prop, &val, sizeof(val)); 158} 159 160static int configure_phy_reset_gpios(void *blob) 161{ 162 int node; 163 int phynode; 164 int ret; 165 u32 handle; 166 u32 gpio; 167 u32 flags; 168 char path[1024]; 169 const char *eth_alias = "ethernet0"; 170 171 snprintf(path, sizeof(path), "%s/mdio/ethernet-phy@4", 172 fdt_get_alias(blob, eth_alias)); 173 174 phynode = fdt_path_offset(blob, path); 175 if (phynode < 0) { 176 pr_err("%s(): unable to locate PHY node: %s\n", __func__, path); 177 return 0; 178 } 179 180 if (gd_board_type() & VAR_EEPROM_F_ETH) { 181 snprintf(path, sizeof(path), "%s", 182 fdt_get_alias(blob, "gpio0")); /* Alias to gpio1 */ 183 gpio = 9; 184 flags = GPIO_ACTIVE_LOW; 185 } else { 186 snprintf(path, sizeof(path), "%s/gpio@20", 187 fdt_get_alias(blob, "i2c1")); /* Alias to i2c2 */ 188 gpio = 5; 189 flags = GPIO_ACTIVE_HIGH; 190 } 191 192 node = fdt_path_offset(blob, path); 193 if (node < 0) { 194 pr_err("%s(): unable to locate GPIO node: %s\n", __func__, 195 path); 196 return 0; 197 } 198 199 handle = fdt_get_phandle(blob, node); 200 if (handle < 0) { 201 pr_err("%s(): unable to locate GPIO controller handle: %s\n", 202 __func__, path); 203 } 204 205 ret = insert_gpios_prop(blob, phynode, "reset-gpios", 206 handle, gpio, flags); 207 if (ret < 0) { 208 pr_err("%s(): failed to set reset-gpios property\n", __func__); 209 return ret; 210 } 211 212 return 0; 213} 214 215#if defined(CONFIG_OF_BOARD_FIXUP) 216int board_fix_fdt(void *blob) 217{ 218 /* Fix U-Boot device tree: */ 219 return configure_phy_reset_gpios(blob); 220} 221#endif /* CONFIG_OF_BOARD_FIXUP */ 222 223#if defined(CONFIG_OF_BOARD_SETUP) 224int ft_board_setup(void *blob, struct bd_info *bd) 225{ 226 /* Fix kernel device tree: */ 227 return configure_phy_reset_gpios(blob); 228} 229#endif /* CONFIG_OF_BOARD_SETUP */ 230 231#endif /* CONFIG_SPL_BUILD */ 232