blob: 410b4cd998eac4ed9948b89c527b0dec12d32207 [file] [log] [blame]
Michal Simek9755e3d2019-01-21 15:25:02 +01001// SPDX-License-Identifier: GPL-2.0+
2/*
Michal Simekd61728c2020-08-03 13:01:45 +02003 * (C) Copyright 2014 - 2020 Xilinx, Inc.
Michal Simek9755e3d2019-01-21 15:25:02 +01004 * Michal Simek <michal.simek@xilinx.com>
5 */
6
7#include <common.h>
Simon Glass09140112020-05-10 11:40:03 -06008#include <env.h>
Simon Glassf7ae49f2020-05-10 11:40:05 -06009#include <log.h>
Michal Simekfc274a52019-12-19 17:45:15 +010010#include <asm/sections.h>
Michal Simek9755e3d2019-01-21 15:25:02 +010011#include <dm/uclass.h>
12#include <i2c.h>
Michal Simeka29511e2020-04-08 10:51:36 +020013#include <linux/sizes.h>
Michal Simekd61728c2020-08-03 13:01:45 +020014#include <malloc.h>
Michal Simek80fdef12020-03-31 12:39:37 +020015#include "board.h"
Michal Simekd61728c2020-08-03 13:01:45 +020016#include <dm.h>
17#include <i2c_eeprom.h>
18#include <net.h>
Michal Simek9755e3d2019-01-21 15:25:02 +010019
Michal Simek9fea3b12020-08-03 12:59:28 +020020#if defined(CONFIG_ZYNQ_GEM_I2C_MAC_OFFSET)
Michal Simek829e8c72019-01-21 16:29:07 +010021int zynq_board_read_rom_ethaddr(unsigned char *ethaddr)
22{
23 int ret = -EINVAL;
Michal Simek829e8c72019-01-21 16:29:07 +010024 struct udevice *dev;
25 ofnode eeprom;
26
27 eeprom = ofnode_get_chosen_node("xlnx,eeprom");
28 if (!ofnode_valid(eeprom))
29 return -ENODEV;
30
31 debug("%s: Path to EEPROM %s\n", __func__,
Simon Glass14ca9f72020-01-27 08:49:43 -070032 ofnode_read_chosen_string("xlnx,eeprom"));
Michal Simek829e8c72019-01-21 16:29:07 +010033
34 ret = uclass_get_device_by_ofnode(UCLASS_I2C_EEPROM, eeprom, &dev);
35 if (ret)
36 return ret;
37
38 ret = dm_i2c_read(dev, CONFIG_ZYNQ_GEM_I2C_MAC_OFFSET, ethaddr, 6);
39 if (ret)
40 debug("%s: I2C EEPROM MAC address read failed\n", __func__);
41 else
42 debug("%s: I2C EEPROM MAC %pM\n", __func__, ethaddr);
Michal Simek829e8c72019-01-21 16:29:07 +010043
44 return ret;
45}
Michal Simek9fea3b12020-08-03 12:59:28 +020046#endif
Ibai Erkiagafec657b2019-10-02 15:57:36 +010047
Michal Simekd61728c2020-08-03 13:01:45 +020048#define EEPROM_HEADER_MAGIC 0xdaaddeed
49#define EEPROM_HDR_MANUFACTURER_LEN 16
50#define EEPROM_HDR_NAME_LEN 16
51#define EEPROM_HDR_REV_LEN 8
52#define EEPROM_HDR_SERIAL_LEN 20
53#define EEPROM_HDR_NO_OF_MAC_ADDR 4
54#define EEPROM_HDR_ETH_ALEN ETH_ALEN
55
56struct xilinx_board_description {
57 u32 header;
58 char manufacturer[EEPROM_HDR_MANUFACTURER_LEN + 1];
59 char name[EEPROM_HDR_NAME_LEN + 1];
60 char revision[EEPROM_HDR_REV_LEN + 1];
61 char serial[EEPROM_HDR_SERIAL_LEN + 1];
62 u8 mac_addr[EEPROM_HDR_NO_OF_MAC_ADDR][EEPROM_HDR_ETH_ALEN + 1];
63};
64
Michal Simeka03b5942020-08-03 12:57:05 +020065static int highest_id = -1;
66static struct xilinx_board_description **board_info;
Michal Simekd61728c2020-08-03 13:01:45 +020067
68#define XILINX_I2C_DETECTION_BITS 8
69
70/* Variable which stores pointer to array which stores eeprom content */
71struct xilinx_legacy_format {
72 char board_sn[18]; /* 0x0 */
73 char unused0[14]; /* 0x12 */
74 char eth_mac[6]; /* 0x20 */
75 char unused1[170]; /* 0x26 */
76 char board_name[11]; /* 0xd0 */
77 char unused2[5]; /* 0xdc */
78 char board_revision[3]; /* 0xe0 */
79 char unused3[29]; /* 0xe3 */
80};
81
82static void xilinx_eeprom_legacy_cleanup(char *eeprom, int size)
83{
84 int i;
85 char byte;
86
87 for (i = 0; i < size; i++) {
88 byte = eeprom[i];
89
90 /* Remove all ffs and spaces */
91 if (byte == 0xff || byte == ' ')
92 eeprom[i] = 0;
93
94 /* Convert strings to lower case */
95 if (byte >= 'A' && byte <= 'Z')
96 eeprom[i] = byte + 'a' - 'A';
97 }
98}
99
100static int xilinx_read_eeprom_legacy(struct udevice *dev, char *name,
101 struct xilinx_board_description *desc)
102{
103 int ret, size;
104 struct xilinx_legacy_format *eeprom_content;
105 bool eth_valid = false;
106
107 size = sizeof(*eeprom_content);
108
109 eeprom_content = calloc(1, size);
110 if (!eeprom_content)
111 return -ENOMEM;
112
113 debug("%s: I2C EEPROM read pass data at %p\n", __func__,
114 eeprom_content);
115
116 ret = dm_i2c_read(dev, 0, (uchar *)eeprom_content, size);
117 if (ret) {
118 debug("%s: I2C EEPROM read failed\n", __func__);
119 free(eeprom_content);
120 return ret;
121 }
122
123 xilinx_eeprom_legacy_cleanup((char *)eeprom_content, size);
124
125 printf("Xilinx I2C Legacy format at %s:\n", name);
126 printf(" Board name:\t%s\n", eeprom_content->board_name);
127 printf(" Board rev:\t%s\n", eeprom_content->board_revision);
128 printf(" Board SN:\t%s\n", eeprom_content->board_sn);
129
130 eth_valid = is_valid_ethaddr((const u8 *)eeprom_content->eth_mac);
131 if (eth_valid)
132 printf(" Ethernet mac:\t%pM\n", eeprom_content->eth_mac);
133
134 /* Terminating \0 chars ensure end of string */
135 strcpy(desc->name, eeprom_content->board_name);
136 strcpy(desc->revision, eeprom_content->board_revision);
137 strcpy(desc->serial, eeprom_content->board_sn);
138 if (eth_valid)
139 memcpy(desc->mac_addr[0], eeprom_content->eth_mac, ETH_ALEN);
140
141 desc->header = EEPROM_HEADER_MAGIC;
142
143 free(eeprom_content);
144
145 return ret;
146}
147
148static bool xilinx_detect_legacy(u8 *buffer)
149{
150 int i;
151 char c;
152
153 for (i = 0; i < XILINX_I2C_DETECTION_BITS; i++) {
154 c = buffer[i];
155
156 if (c < '0' || c > '9')
157 return false;
158 }
159
160 return true;
161}
162
163static int xilinx_read_eeprom_single(char *name,
164 struct xilinx_board_description *desc)
165{
166 int ret;
167 struct udevice *dev;
168 ofnode eeprom;
169 u8 buffer[XILINX_I2C_DETECTION_BITS];
170
171 eeprom = ofnode_get_aliases_node(name);
172 if (!ofnode_valid(eeprom))
173 return -ENODEV;
174
175 ret = uclass_get_device_by_ofnode(UCLASS_I2C_EEPROM, eeprom, &dev);
176 if (ret)
177 return ret;
178
179 ret = dm_i2c_read(dev, 0, buffer, sizeof(buffer));
180 if (ret) {
181 debug("%s: I2C EEPROM read failed\n", __func__);
182 return ret;
183 }
184
185 debug("%s: i2c memory detected: %s\n", __func__, name);
186
187 if (xilinx_detect_legacy(buffer))
188 return xilinx_read_eeprom_legacy(dev, name, desc);
189
190 return -ENODEV;
191}
192
193__maybe_unused int xilinx_read_eeprom(void)
194{
Michal Simeka03b5942020-08-03 12:57:05 +0200195 int id, ret;
Michal Simekd61728c2020-08-03 13:01:45 +0200196 char name_buf[8]; /* 8 bytes should be enough for nvmem+number */
Michal Simeka03b5942020-08-03 12:57:05 +0200197 struct xilinx_board_description *desc;
Michal Simekd61728c2020-08-03 13:01:45 +0200198
199 highest_id = dev_read_alias_highest_id("nvmem");
200 /* No nvmem aliases present */
201 if (highest_id < 0)
202 return -EINVAL;
203
Michal Simeka03b5942020-08-03 12:57:05 +0200204 board_info = calloc(1, sizeof(desc) * highest_id);
Michal Simekd61728c2020-08-03 13:01:45 +0200205 if (!board_info)
206 return -ENOMEM;
207
208 debug("%s: Highest ID %d, board_info %p\n", __func__,
209 highest_id, board_info);
210
211 for (id = 0; id <= highest_id; id++) {
212 snprintf(name_buf, sizeof(name_buf), "nvmem%d", id);
213
Michal Simeka03b5942020-08-03 12:57:05 +0200214 /* Alloc structure */
215 desc = board_info[id];
216 if (!desc) {
217 desc = calloc(1, sizeof(*desc));
218 if (!desc)
219 return -ENOMEM;
220
221 board_info[id] = desc;
222 }
223
Michal Simekd61728c2020-08-03 13:01:45 +0200224 /* Ignoring return value for supporting multiple chips */
Michal Simeka03b5942020-08-03 12:57:05 +0200225 ret = xilinx_read_eeprom_single(name_buf, desc);
226 if (ret) {
227 free(desc);
228 board_info[id] = NULL;
229 }
Michal Simekd61728c2020-08-03 13:01:45 +0200230 }
231
Michal Simeka03b5942020-08-03 12:57:05 +0200232 /*
233 * Consider to clean board_info structure when board/cards are not
234 * detected.
235 */
Michal Simekd61728c2020-08-03 13:01:45 +0200236
237 return 0;
238}
239
Michal Simekfc274a52019-12-19 17:45:15 +0100240#if defined(CONFIG_OF_BOARD) || defined(CONFIG_OF_SEPARATE)
Ibai Erkiagafec657b2019-10-02 15:57:36 +0100241void *board_fdt_blob_setup(void)
242{
Michal Simeke2572b52020-09-04 16:21:47 +0200243 void *fdt_blob;
Michal Simek453bb772020-03-19 10:23:56 +0100244
245#if !defined(CONFIG_VERSAL_NO_DDR) && !defined(CONFIG_ZYNQMP_NO_DDR)
246 fdt_blob = (void *)CONFIG_XILINX_OF_BOARD_DTB_ADDR;
Ibai Erkiagafec657b2019-10-02 15:57:36 +0100247
Michal Simekfc274a52019-12-19 17:45:15 +0100248 if (fdt_magic(fdt_blob) == FDT_MAGIC)
249 return fdt_blob;
Ibai Erkiagafec657b2019-10-02 15:57:36 +0100250
Michal Simekfc274a52019-12-19 17:45:15 +0100251 debug("DTB is not passed via %p\n", fdt_blob);
Michal Simek453bb772020-03-19 10:23:56 +0100252#endif
Michal Simekfc274a52019-12-19 17:45:15 +0100253
254#ifdef CONFIG_SPL_BUILD
255 /* FDT is at end of BSS unless it is in a different memory region */
256 if (IS_ENABLED(CONFIG_SPL_SEPARATE_BSS))
257 fdt_blob = (ulong *)&_image_binary_end;
258 else
259 fdt_blob = (ulong *)&__bss_end;
260#else
261 /* FDT is at end of image */
262 fdt_blob = (ulong *)&_end;
263#endif
264
265 if (fdt_magic(fdt_blob) == FDT_MAGIC)
266 return fdt_blob;
267
268 debug("DTB is also not passed via %p\n", fdt_blob);
269
270 return NULL;
Ibai Erkiagafec657b2019-10-02 15:57:36 +0100271}
272#endif
Michal Simek80fdef12020-03-31 12:39:37 +0200273
Michal Simek7c553ac2020-10-22 11:14:20 +0200274#if defined(CONFIG_BOARD_LATE_INIT)
Michal Simeka03b5942020-08-03 12:57:05 +0200275static int env_set_by_index(const char *name, int index, char *data)
276{
277 char var[32];
278
279 if (!index)
280 sprintf(var, "board_%s", name);
281 else
282 sprintf(var, "card%d_%s", index, name);
283
284 return env_set(var, data);
285}
286
Michal Simek80fdef12020-03-31 12:39:37 +0200287int board_late_init_xilinx(void)
288{
Michal Simekca0f6162020-08-12 12:16:49 +0200289 u32 ret = 0;
Michal Simeka03b5942020-08-03 12:57:05 +0200290 int i, id, macid = 0;
291 struct xilinx_board_description *desc;
Michal Simek2570cc62020-08-12 12:17:53 +0200292 phys_size_t bootm_size = gd->ram_size;
293
294 if (CONFIG_IS_ENABLED(ARCH_ZYNQ))
295 bootm_size = min(bootm_size, (phys_size_t)(SZ_512M + SZ_256M));
Michal Simek80fdef12020-03-31 12:39:37 +0200296
Michal Simekca0f6162020-08-12 12:16:49 +0200297 ret |= env_set_hex("script_offset_f", CONFIG_BOOT_SCRIPT_OFFSET);
298
299 ret |= env_set_addr("bootm_low", (void *)gd->ram_base);
Michal Simek2570cc62020-08-12 12:17:53 +0200300 ret |= env_set_addr("bootm_size", (void *)bootm_size);
Michal Simekca0f6162020-08-12 12:16:49 +0200301
Michal Simeka03b5942020-08-03 12:57:05 +0200302 for (id = 0; id <= highest_id; id++) {
303 desc = board_info[id];
304 if (desc && desc->header == EEPROM_HEADER_MAGIC) {
305 if (desc->manufacturer[0])
306 ret |= env_set_by_index("manufacturer", id,
307 desc->manufacturer);
308 if (desc->name[0])
309 ret |= env_set_by_index("name", id,
310 desc->name);
311 if (desc->revision[0])
312 ret |= env_set_by_index("rev", id,
313 desc->revision);
314 if (desc->serial[0])
315 ret |= env_set_by_index("serial", id,
316 desc->serial);
Michal Simekd61728c2020-08-03 13:01:45 +0200317
318 if (!CONFIG_IS_ENABLED(NET))
319 continue;
320
Michal Simeka03b5942020-08-03 12:57:05 +0200321 for (i = 0; i < EEPROM_HDR_NO_OF_MAC_ADDR; i++) {
322 if (!desc->mac_addr[i])
323 continue;
324
325 if (is_valid_ethaddr((const u8 *)desc->mac_addr[i]))
326 ret |= eth_env_set_enetaddr_by_index("eth",
327 macid++, desc->mac_addr[i]);
328 }
Michal Simekd61728c2020-08-03 13:01:45 +0200329 }
330 }
331
Michal Simekca0f6162020-08-12 12:16:49 +0200332 if (ret)
333 printf("%s: Saving run time variables FAILED\n", __func__);
Michal Simekc8da6512020-07-09 15:57:56 +0200334
Michal Simek80fdef12020-03-31 12:39:37 +0200335 return 0;
336}
Michal Simek7c553ac2020-10-22 11:14:20 +0200337#endif