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