blob: b7588fa4eec944efed3804c3f1f4e2b52dc812d1 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Marek Behún09e16b82017-06-09 19:28:45 +02002/*
Marek Behúnd63726e2022-06-01 17:17:06 +02003 * Copyright (C) 2017 Marek Behún <kabel@kernel.org>
Marek Behún09e16b82017-06-09 19:28:45 +02004 * Copyright (C) 2016 Tomas Hlavacek <tomas.hlavacek@nic.cz>
5 *
6 * Derived from the code for
7 * Marvell/db-88f6820-gp by Stefan Roese <sr@denx.de>
Marek Behún09e16b82017-06-09 19:28:45 +02008 */
9
Tom Rinidec7ea02024-05-20 13:35:03 -060010#include <config.h>
Simon Glass07dc93c2019-08-01 09:46:47 -060011#include <env.h>
Marek Behún09e16b82017-06-09 19:28:45 +020012#include <i2c.h>
Simon Glassa7b51302019-11-14 12:57:46 -070013#include <init.h>
Simon Glass0f2af882020-05-10 11:40:05 -060014#include <log.h>
Marek Behún09e16b82017-06-09 19:28:45 +020015#include <miiphy.h>
Marek Behún91ef59c2021-07-15 19:21:02 +020016#include <mtd.h>
Simon Glass3ba929a2020-10-30 21:38:53 -060017#include <asm/global_data.h>
Marek Behún09e16b82017-06-09 19:28:45 +020018#include <asm/io.h>
19#include <asm/arch/cpu.h>
20#include <asm/arch/soc.h>
Marek Behúnc2d19d02024-04-04 09:50:54 +020021#include <asm/unaligned.h>
Marek Behún09e16b82017-06-09 19:28:45 +020022#include <dm/uclass.h>
Pali Rohár1e0a9752022-07-29 13:29:07 +020023#include <dt-bindings/gpio/gpio.h>
Marek Behún09e16b82017-06-09 19:28:45 +020024#include <fdt_support.h>
Pali Roháre16cc982022-08-10 11:00:25 +020025#include <hexdump.h>
Marek Behún6fa120d2024-08-29 10:08:48 +020026#include <i2c_eeprom.h>
Marek Behún09e16b82017-06-09 19:28:45 +020027#include <time.h>
Marek Behúnbb42a5b2024-04-04 09:50:51 +020028#include <turris-omnia-mcu-interface.h>
Simon Glass4dcacfc2020-05-10 11:40:13 -060029#include <linux/bitops.h>
Marek Behúnc2d19d02024-04-04 09:50:54 +020030#include <linux/bitrev.h>
Pali Rohár1e0a9752022-07-29 13:29:07 +020031#include <linux/delay.h>
Simon Glass48b6c6b2019-11-14 12:57:16 -070032#include <u-boot/crc.h>
Marek Behún09e16b82017-06-09 19:28:45 +020033
Chris Packham1a07d212018-05-10 13:28:29 +120034#include "../drivers/ddr/marvell/a38x/ddr3_init.h"
Marek Behún09e16b82017-06-09 19:28:45 +020035#include <../serdes/a38x/high_speed_env_spec.h>
Pali Rohár0387f7f2022-04-08 16:30:12 +020036#include "../turris_atsha_otp.h"
Marek Behúnc2d19d02024-04-04 09:50:54 +020037#include "../turris_common.h"
Marek Behún09e16b82017-06-09 19:28:45 +020038
39DECLARE_GLOBAL_DATA_PTR;
40
Marek Behúnba53b6b2019-05-02 16:53:30 +020041#define OMNIA_I2C_BUS_NAME "i2c@11000->i2cmux@70->i2c@0"
42
43#define OMNIA_I2C_MCU_CHIP_ADDR 0x2a
44#define OMNIA_I2C_MCU_CHIP_LEN 1
45
46#define OMNIA_I2C_EEPROM_CHIP_ADDR 0x54
47#define OMNIA_I2C_EEPROM_CHIP_LEN 2
Marek Behún09e16b82017-06-09 19:28:45 +020048#define OMNIA_I2C_EEPROM_MAGIC 0x0341a034
49
Marek Behún70622b32024-08-29 10:08:49 +020050#define OMNIA_RESET_TO_LOWER_DDR_SPEED 9
51#define OMNIA_LOWER_DDR_SPEED "1333H"
52
Pali Rohár30e398d2022-04-29 13:53:25 +020053#define A385_SYS_RSTOUT_MASK MVEBU_REGISTER(0x18260)
54#define A385_SYS_RSTOUT_MASK_WD BIT(10)
Pali Rohár7fcda0c2021-11-09 17:14:02 +010055
56#define A385_WDT_GLOBAL_CTRL MVEBU_REGISTER(0x20300)
57#define A385_WDT_GLOBAL_RATIO_MASK GENMASK(18, 16)
58#define A385_WDT_GLOBAL_RATIO_SHIFT 16
59#define A385_WDT_GLOBAL_25MHZ BIT(10)
60#define A385_WDT_GLOBAL_ENABLE BIT(8)
61
62#define A385_WDT_GLOBAL_STATUS MVEBU_REGISTER(0x20304)
63#define A385_WDT_GLOBAL_EXPIRED BIT(31)
64
65#define A385_WDT_DURATION MVEBU_REGISTER(0x20334)
66
67#define A385_WD_RSTOUT_UNMASK MVEBU_REGISTER(0x20704)
68#define A385_WD_RSTOUT_UNMASK_GLOBAL BIT(8)
69
Marek Behún09e16b82017-06-09 19:28:45 +020070/*
71 * Those values and defines are taken from the Marvell U-Boot version
72 * "u-boot-2013.01-2014_T3.0"
73 */
74#define OMNIA_GPP_OUT_ENA_LOW \
75 (~(BIT(1) | BIT(4) | BIT(6) | BIT(7) | BIT(8) | BIT(9) | \
76 BIT(10) | BIT(11) | BIT(19) | BIT(22) | BIT(23) | BIT(25) | \
77 BIT(26) | BIT(27) | BIT(29) | BIT(30) | BIT(31)))
78#define OMNIA_GPP_OUT_ENA_MID \
79 (~(BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(15) | \
80 BIT(16) | BIT(17) | BIT(18)))
81
82#define OMNIA_GPP_OUT_VAL_LOW 0x0
83#define OMNIA_GPP_OUT_VAL_MID 0x0
84#define OMNIA_GPP_POL_LOW 0x0
85#define OMNIA_GPP_POL_MID 0x0
86
Pali Rohár3c4dd9812022-03-02 12:47:54 +010087static struct serdes_map board_serdes_map[] = {
Marek Behún09e16b82017-06-09 19:28:45 +020088 {PEX0, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0},
89 {USB3_HOST0, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, 0},
90 {PEX1, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0},
91 {USB3_HOST1, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, 0},
92 {PEX2, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0},
93 {SGMII2, SERDES_SPEED_1_25_GBPS, SERDES_DEFAULT_MODE, 0, 0}
94};
95
Marek Behúnba53b6b2019-05-02 16:53:30 +020096static struct udevice *omnia_get_i2c_chip(const char *name, uint addr,
97 uint offset_len)
Marek Behún09e16b82017-06-09 19:28:45 +020098{
99 struct udevice *bus, *dev;
Marek Behúnba53b6b2019-05-02 16:53:30 +0200100 int ret;
Marek Behún09e16b82017-06-09 19:28:45 +0200101
Marek Behúnba53b6b2019-05-02 16:53:30 +0200102 ret = uclass_get_device_by_name(UCLASS_I2C, OMNIA_I2C_BUS_NAME, &bus);
103 if (ret) {
104 printf("Cannot get I2C bus %s: uclass_get_device_by_name failed: %i\n",
105 OMNIA_I2C_BUS_NAME, ret);
106 return NULL;
Marek Behún09e16b82017-06-09 19:28:45 +0200107 }
108
Marek Behúnba53b6b2019-05-02 16:53:30 +0200109 ret = i2c_get_chip(bus, addr, offset_len, &dev);
Marek Behún09e16b82017-06-09 19:28:45 +0200110 if (ret) {
Marek Behúnba53b6b2019-05-02 16:53:30 +0200111 printf("Cannot get %s I2C chip: i2c_get_chip failed: %i\n",
112 name, ret);
113 return NULL;
Marek Behún09e16b82017-06-09 19:28:45 +0200114 }
115
Marek Behúnba53b6b2019-05-02 16:53:30 +0200116 return dev;
117}
Marek Behúnd0b374d2017-08-04 15:28:25 +0200118
Marek Behúnba53b6b2019-05-02 16:53:30 +0200119static int omnia_mcu_read(u8 cmd, void *buf, int len)
120{
121 struct udevice *chip;
122
123 chip = omnia_get_i2c_chip("MCU", OMNIA_I2C_MCU_CHIP_ADDR,
124 OMNIA_I2C_MCU_CHIP_LEN);
125 if (!chip)
126 return -ENODEV;
127
128 return dm_i2c_read(chip, cmd, buf, len);
129}
130
Marek Behúnba53b6b2019-05-02 16:53:30 +0200131static int omnia_mcu_write(u8 cmd, const void *buf, int len)
132{
133 struct udevice *chip;
134
135 chip = omnia_get_i2c_chip("MCU", OMNIA_I2C_MCU_CHIP_ADDR,
136 OMNIA_I2C_MCU_CHIP_LEN);
137 if (!chip)
138 return -ENODEV;
139
140 return dm_i2c_write(chip, cmd, buf, len);
141}
142
Marek Behún8b52b8c2024-04-04 09:50:53 +0200143static int omnia_mcu_get_sts_and_features(u16 *psts, u32 *pfeatures)
144{
145 u16 sts, feat16;
146 int ret;
147
148 ret = omnia_mcu_read(CMD_GET_STATUS_WORD, &sts, sizeof(sts));
149 if (ret)
150 return ret;
151
152 if (psts)
153 *psts = sts;
154
155 if (!pfeatures)
156 return 0;
157
158 if (sts & STS_FEATURES_SUPPORTED) {
159 /* try read 32-bit features */
160 ret = omnia_mcu_read(CMD_GET_FEATURES, pfeatures,
161 sizeof(*pfeatures));
162 if (ret) {
163 /* try read 16-bit features */
164 ret = omnia_mcu_read(CMD_GET_FEATURES, &feat16,
165 sizeof(&feat16));
166 if (ret)
167 return ret;
168
169 *pfeatures = feat16;
170 } else {
171 if (*pfeatures & FEAT_FROM_BIT_16_INVALID)
172 *pfeatures &= GENMASK(15, 0);
173 }
174 } else {
175 *pfeatures = 0;
176 }
177
178 return 0;
179}
180
181static int omnia_mcu_get_sts(u16 *sts)
182{
183 return omnia_mcu_get_sts_and_features(sts, NULL);
184}
185
186static bool omnia_mcu_has_feature(u32 feature)
187{
188 u32 features;
189
190 if (omnia_mcu_get_sts_and_features(NULL, &features))
191 return false;
192
193 return feature & features;
194}
195
Marek Behúnc2d19d02024-04-04 09:50:54 +0200196static u32 omnia_mcu_crc32(const void *p, size_t len)
197{
198 u32 val, crc = 0;
199
200 compiletime_assert(!(len % 4), "length has to be a multiple of 4");
201
202 while (len) {
203 val = bitrev32(get_unaligned_le32(p));
204 crc = crc32(crc, (void *)&val, 4);
205 p += 4;
206 len -= 4;
207 }
208
209 return ~bitrev32(crc);
210}
211
Marek Behún70622b32024-08-29 10:08:49 +0200212static int omnia_mcu_get_reset(void)
213{
214 u8 reset_status;
215 int ret;
216
217 ret = omnia_mcu_read(CMD_GET_RESET, &reset_status, 1);
218 if (ret) {
219 printf("omnia_mcu_read failed: %i, reset status unknown!\n", ret);
220 return ret;
221 }
222
223 return reset_status;
224}
225
Marek Behúnc2d19d02024-04-04 09:50:54 +0200226/* Can only be called after relocation, since it needs cleared BSS */
227static int omnia_mcu_board_info(char *serial, u8 *mac, char *version)
228{
229 static u8 reply[17];
230 static bool cached;
231
232 if (!cached) {
233 u8 csum;
234 int ret;
235
236 ret = omnia_mcu_read(CMD_BOARD_INFO_GET, reply, sizeof(reply));
237 if (ret)
238 return ret;
239
240 if (reply[0] != 16)
241 return -EBADMSG;
242
243 csum = reply[16];
244 reply[16] = 0;
245
246 if ((omnia_mcu_crc32(&reply[1], 16) & 0xff) != csum)
247 return -EBADMSG;
248
249 cached = true;
250 }
251
252 if (serial) {
253 const char *serial_env;
254
255 serial_env = env_get("serial#");
256 if (serial_env && strlen(serial_env) == 16) {
257 strcpy(serial, serial_env);
258 } else {
259 sprintf(serial, "%016llX",
260 get_unaligned_le64(&reply[1]));
261 env_set("serial#", serial);
262 }
263 }
264
265 if (mac)
266 memcpy(mac, &reply[9], ETH_ALEN);
267
268 if (version)
269 sprintf(version, "%u", reply[15]);
270
271 return 0;
272}
273
Marek Behún087b2352024-04-04 09:50:55 +0200274static int omnia_mcu_get_board_public_key(char pub_key[static 67])
275{
276 u8 reply[34];
277 int ret;
278
279 ret = omnia_mcu_read(CMD_CRYPTO_GET_PUBLIC_KEY, reply, sizeof(reply));
280 if (ret)
281 return ret;
282
283 if (reply[0] != 33)
284 return -EBADMSG;
285
286 bin2hex(pub_key, &reply[1], 33);
287 pub_key[66] = '\0';
288
289 return 0;
290}
291
Pali Rohár7fcda0c2021-11-09 17:14:02 +0100292static void enable_a385_watchdog(unsigned int timeout_minutes)
293{
294 struct sar_freq_modes sar_freq;
295 u32 watchdog_freq;
296
297 printf("Enabling A385 watchdog with %u minutes timeout...\n",
298 timeout_minutes);
299
300 /*
301 * Use NBCLK clock (a.k.a. L2 clock) as watchdog input clock with
302 * its maximal ratio 7 instead of default fixed 25 MHz clock.
303 * It allows to set watchdog duration up to the 22 minutes.
304 */
305 clrsetbits_32(A385_WDT_GLOBAL_CTRL,
306 A385_WDT_GLOBAL_25MHZ | A385_WDT_GLOBAL_RATIO_MASK,
307 7 << A385_WDT_GLOBAL_RATIO_SHIFT);
308
309 /*
310 * Calculate watchdog clock frequency. It is defined by formula:
311 * freq = NBCLK / 2 / (2 ^ ratio)
312 * We set ratio to the maximal possible value 7.
313 */
314 get_sar_freq(&sar_freq);
315 watchdog_freq = sar_freq.nb_clk * 1000000 / 2 / (1 << 7);
316
317 /* Set watchdog duration */
318 writel(timeout_minutes * 60 * watchdog_freq, A385_WDT_DURATION);
319
320 /* Clear the watchdog expiration bit */
321 clrbits_32(A385_WDT_GLOBAL_STATUS, A385_WDT_GLOBAL_EXPIRED);
322
323 /* Enable watchdog timer */
324 setbits_32(A385_WDT_GLOBAL_CTRL, A385_WDT_GLOBAL_ENABLE);
325
326 /* Enable reset on watchdog */
327 setbits_32(A385_WD_RSTOUT_UNMASK, A385_WD_RSTOUT_UNMASK_GLOBAL);
328
329 /* Unmask reset for watchdog */
Pali Rohár30e398d2022-04-29 13:53:25 +0200330 clrbits_32(A385_SYS_RSTOUT_MASK, A385_SYS_RSTOUT_MASK_WD);
Pali Rohár7fcda0c2021-11-09 17:14:02 +0100331}
332
Marek Behúnba53b6b2019-05-02 16:53:30 +0200333static bool disable_mcu_watchdog(void)
334{
335 int ret;
336
337 puts("Disabling MCU watchdog... ");
338
Marek Behúnbb42a5b2024-04-04 09:50:51 +0200339 ret = omnia_mcu_write(CMD_SET_WATCHDOG_STATE, "\x00", 1);
Marek Behúnba53b6b2019-05-02 16:53:30 +0200340 if (ret) {
341 printf("omnia_mcu_write failed: %i\n", ret);
Marek Behún09e16b82017-06-09 19:28:45 +0200342 return false;
343 }
344
Marek Behúnba53b6b2019-05-02 16:53:30 +0200345 puts("disabled\n");
346
347 return true;
348}
Marek Behúnba53b6b2019-05-02 16:53:30 +0200349
Pali Rohárf8f305b2022-03-02 12:47:55 +0100350static bool omnia_detect_sata(const char *msata_slot)
Marek Behúnba53b6b2019-05-02 16:53:30 +0200351{
352 int ret;
Marek Behún8b52b8c2024-04-04 09:50:53 +0200353 u16 sts;
Marek Behúnba53b6b2019-05-02 16:53:30 +0200354
355 puts("MiniPCIe/mSATA card detection... ");
356
Pali Rohárf8f305b2022-03-02 12:47:55 +0100357 if (msata_slot) {
358 if (strcmp(msata_slot, "pcie") == 0) {
359 puts("forced to MiniPCIe via env\n");
360 return false;
361 } else if (strcmp(msata_slot, "sata") == 0) {
362 puts("forced to mSATA via env\n");
363 return true;
364 } else if (strcmp(msata_slot, "auto") != 0) {
365 printf("unsupported env value '%s', fallback to... ", msata_slot);
366 }
367 }
368
Marek Behún8b52b8c2024-04-04 09:50:53 +0200369 ret = omnia_mcu_get_sts(&sts);
Marek Behúnba53b6b2019-05-02 16:53:30 +0200370 if (ret) {
371 printf("omnia_mcu_read failed: %i, defaulting to MiniPCIe card\n",
372 ret);
Marek Behún09e16b82017-06-09 19:28:45 +0200373 return false;
374 }
375
Marek Behún8b52b8c2024-04-04 09:50:53 +0200376 if (!(sts & STS_CARD_DET)) {
Marek Behúnba53b6b2019-05-02 16:53:30 +0200377 puts("none\n");
Marek Behún09e16b82017-06-09 19:28:45 +0200378 return false;
379 }
Marek Behúnba53b6b2019-05-02 16:53:30 +0200380
Marek Behún8b52b8c2024-04-04 09:50:53 +0200381 if (sts & STS_MSATA_IND)
Marek Behúnba53b6b2019-05-02 16:53:30 +0200382 puts("mSATA\n");
383 else
384 puts("MiniPCIe\n");
385
Marek Behún8b52b8c2024-04-04 09:50:53 +0200386 return sts & STS_MSATA_IND;
Marek Behún09e16b82017-06-09 19:28:45 +0200387}
388
Pali Rohár93a89c52022-03-02 12:47:58 +0100389static bool omnia_detect_wwan_usb3(const char *wwan_slot)
390{
391 puts("WWAN slot configuration... ");
392
393 if (wwan_slot && strcmp(wwan_slot, "usb3") == 0) {
394 puts("USB3.0\n");
395 return true;
396 }
397
398 if (wwan_slot && strcmp(wwan_slot, "pcie") != 0)
399 printf("unsupported env value '%s', fallback to... ", wwan_slot);
400
401 puts("PCIe+USB2.0\n");
402 return false;
403}
404
Marek Behún09e16b82017-06-09 19:28:45 +0200405int hws_board_topology_load(struct serdes_map **serdes_map_array, u8 *count)
406{
Pali Rohárf8f305b2022-03-02 12:47:55 +0100407#ifdef CONFIG_SPL_ENV_SUPPORT
408 /* Do not use env_load() as malloc() pool is too small at this stage */
409 bool has_env = (env_init() == 0);
410#endif
411 const char *env_value = NULL;
412
413#ifdef CONFIG_SPL_ENV_SUPPORT
414 /* beware that env_get() returns static allocated memory */
415 env_value = has_env ? env_get("omnia_msata_slot") : NULL;
416#endif
417
418 if (omnia_detect_sata(env_value)) {
Pali Rohár3c4dd9812022-03-02 12:47:54 +0100419 /* Change SerDes for first mPCIe port (mSATA) from PCIe to SATA */
420 board_serdes_map[0].serdes_type = SATA0;
421 board_serdes_map[0].serdes_speed = SERDES_SPEED_6_GBPS;
422 board_serdes_map[0].serdes_mode = SERDES_DEFAULT_MODE;
Marek Behún09e16b82017-06-09 19:28:45 +0200423 }
424
Pali Rohár93a89c52022-03-02 12:47:58 +0100425#ifdef CONFIG_SPL_ENV_SUPPORT
426 /* beware that env_get() returns static allocated memory */
427 env_value = has_env ? env_get("omnia_wwan_slot") : NULL;
428#endif
429
430 if (omnia_detect_wwan_usb3(env_value)) {
431 /* Disable SerDes for USB 3.0 pins on the front USB-A port */
432 board_serdes_map[1].serdes_type = DEFAULT_SERDES;
433 /* Change SerDes for third mPCIe port (WWAN) from PCIe to USB 3.0 */
434 board_serdes_map[4].serdes_type = USB3_HOST0;
435 board_serdes_map[4].serdes_speed = SERDES_SPEED_5_GBPS;
436 board_serdes_map[4].serdes_mode = SERDES_DEFAULT_MODE;
437 }
438
Pali Rohár3c4dd9812022-03-02 12:47:54 +0100439 *serdes_map_array = board_serdes_map;
440 *count = ARRAY_SIZE(board_serdes_map);
441
Marek Behún09e16b82017-06-09 19:28:45 +0200442 return 0;
443}
444
445struct omnia_eeprom {
446 u32 magic;
447 u32 ramsize;
448 char region[4];
449 u32 crc;
Marek Behúnc7548162024-06-18 17:34:33 +0200450
451 /* second part (only considered if crc2 is not all-ones) */
Marek Behún9535f792024-06-18 17:34:34 +0200452 char ddr_speed[5];
Marek Behúnd1e68442024-06-18 17:34:39 +0200453 u8 old_ddr_training;
454 u8 reserved[38];
Marek Behúnc7548162024-06-18 17:34:33 +0200455 u32 crc2;
Marek Behún09e16b82017-06-09 19:28:45 +0200456};
457
Marek Behúnc7548162024-06-18 17:34:33 +0200458static bool is_omnia_eeprom_second_part_valid(const struct omnia_eeprom *oep)
459{
460 return oep->crc2 != 0xffffffff;
461}
462
463static void make_omnia_eeprom_second_part_invalid(struct omnia_eeprom *oep)
464{
465 oep->crc2 = 0xffffffff;
466}
467
468static bool check_eeprom_crc(const void *buf, size_t size, u32 expected,
469 const char *name)
470{
471 u32 crc;
472
473 crc = crc32(0, buf, size);
474 if (crc != expected) {
475 printf("bad %s EEPROM CRC (stored %08x, computed %08x)\n",
476 name, expected, crc);
477 return false;
478 }
479
480 return true;
481}
482
Marek Behún70622b32024-08-29 10:08:49 +0200483static struct udevice *omnia_get_eeprom(void)
484{
485 return omnia_get_i2c_chip("EEPROM", OMNIA_I2C_EEPROM_CHIP_ADDR,
486 OMNIA_I2C_EEPROM_CHIP_LEN);
487}
488
Marek Behún09e16b82017-06-09 19:28:45 +0200489static bool omnia_read_eeprom(struct omnia_eeprom *oep)
490{
Marek Behún70622b32024-08-29 10:08:49 +0200491 struct udevice *eeprom = omnia_get_eeprom();
Marek Behúnba53b6b2019-05-02 16:53:30 +0200492 int ret;
Marek Behún09e16b82017-06-09 19:28:45 +0200493
Marek Behúne5792ca2024-08-29 10:08:47 +0200494 if (!eeprom)
Marek Behún09e16b82017-06-09 19:28:45 +0200495 return false;
Marek Behún09e16b82017-06-09 19:28:45 +0200496
Simon Glass49c24a82024-09-29 19:49:47 -0600497 if (IS_ENABLED(CONFIG_XPL_BUILD))
Marek Behún6fa120d2024-08-29 10:08:48 +0200498 ret = dm_i2c_read(eeprom, 0, (void *)oep, sizeof(*oep));
499 else
500 ret = i2c_eeprom_read(eeprom, 0, (void *)oep, sizeof(*oep));
501
Marek Behún09e16b82017-06-09 19:28:45 +0200502 if (ret) {
Marek Behún6fa120d2024-08-29 10:08:48 +0200503 printf("cannot read EEPROM: %d\n", ret);
Marek Behún09e16b82017-06-09 19:28:45 +0200504 return false;
505 }
506
Marek Behúnba53b6b2019-05-02 16:53:30 +0200507 if (oep->magic != OMNIA_I2C_EEPROM_MAGIC) {
508 printf("bad EEPROM magic number (%08x, should be %08x)\n",
509 oep->magic, OMNIA_I2C_EEPROM_MAGIC);
510 return false;
Marek Behún09e16b82017-06-09 19:28:45 +0200511 }
512
Marek Behúnc7548162024-06-18 17:34:33 +0200513 if (!check_eeprom_crc(oep, offsetof(struct omnia_eeprom, crc), oep->crc,
514 "first"))
Marek Behún09e16b82017-06-09 19:28:45 +0200515 return false;
Marek Behúnc7548162024-06-18 17:34:33 +0200516
517 if (is_omnia_eeprom_second_part_valid(oep) &&
518 !check_eeprom_crc(oep, offsetof(struct omnia_eeprom, crc2),
519 oep->crc2, "second"))
520 make_omnia_eeprom_second_part_invalid(oep);
Marek Behún09e16b82017-06-09 19:28:45 +0200521
522 return true;
523}
524
Marek Behún70622b32024-08-29 10:08:49 +0200525static void omnia_eeprom_set_lower_ddr_speed(void)
526{
527 struct udevice *eeprom = omnia_get_eeprom();
528 struct omnia_eeprom oep;
529 int ret;
530
531 if (!eeprom || !omnia_read_eeprom(&oep))
532 return;
533
534 puts("Setting DDR speed to " OMNIA_LOWER_DDR_SPEED " in EEPROM as requested by reset button... ");
535
536 /* check if already set */
537 if (!strncmp(oep.ddr_speed, OMNIA_LOWER_DDR_SPEED, sizeof(oep.ddr_speed)) &&
538 (oep.old_ddr_training == 0 || oep.old_ddr_training == 0xff)) {
539 puts("was already set\n");
540 return;
541 }
542
543 strncpy(oep.ddr_speed, OMNIA_LOWER_DDR_SPEED, sizeof(oep.ddr_speed));
544 oep.old_ddr_training = 0xff;
545 oep.crc2 = crc32(0, (const void *)&oep, offsetof(struct omnia_eeprom, crc2));
546
547 ret = i2c_eeprom_write(eeprom, 0, (const void *)&oep, sizeof(oep));
548 if (ret)
549 printf("cannot write EEPROM: %d\n", ret);
550 else
551 puts("done\n");
552}
553
Marek Behúnd1e68442024-06-18 17:34:39 +0200554int omnia_get_ram_size_gb(void)
Marek Behún77652c72019-05-02 16:53:33 +0200555{
556 static int ram_size;
557 struct omnia_eeprom oep;
558
559 if (!ram_size) {
560 /* Get the board config from EEPROM */
561 if (omnia_read_eeprom(&oep)) {
562 debug("Memory config in EEPROM: 0x%02x\n", oep.ramsize);
563
564 if (oep.ramsize == 0x2)
565 ram_size = 2;
566 else
567 ram_size = 1;
568 } else {
569 /* Hardcoded fallback */
570 puts("Memory config from EEPROM read failed!\n");
571 puts("Falling back to default 1 GiB!\n");
572 ram_size = 1;
573 }
574 }
575
576 return ram_size;
577}
578
Marek Behúnd1e68442024-06-18 17:34:39 +0200579bool board_use_old_ddr3_training(void)
580{
581 struct omnia_eeprom oep;
582
Marek Behún70622b32024-08-29 10:08:49 +0200583 /*
584 * If lower DDR speed is requested by reset button, we can't use old DDR
585 * training algorithm.
586 */
587 if (omnia_mcu_get_reset() == OMNIA_RESET_TO_LOWER_DDR_SPEED)
588 return false;
589
Marek Behúnd1e68442024-06-18 17:34:39 +0200590 if (!omnia_read_eeprom(&oep))
591 return false;
592
593 if (!is_omnia_eeprom_second_part_valid(&oep))
594 return false;
595
596 return oep.old_ddr_training == 1;
597}
598
Marek Behún9535f792024-06-18 17:34:34 +0200599static const char *omnia_get_ddr_speed(void)
600{
601 struct omnia_eeprom oep;
602 static char speed[sizeof(oep.ddr_speed) + 1];
603
604 if (!omnia_read_eeprom(&oep))
605 return NULL;
606
607 if (!is_omnia_eeprom_second_part_valid(&oep))
608 return NULL;
609
610 if (!oep.ddr_speed[0] || oep.ddr_speed[0] == 0xff)
611 return NULL;
612
613 memcpy(&speed, &oep.ddr_speed, sizeof(oep.ddr_speed));
614 speed[sizeof(speed) - 1] = '\0';
615
616 return speed;
617}
618
Pali Rohár4798ba92022-07-29 13:29:06 +0200619static const char * const omnia_get_mcu_type(void)
620{
Marek Behúnbb42a5b2024-04-04 09:50:51 +0200621 static char result[] = "xxxxxxx (with peripheral resets)";
Marek Behún8b52b8c2024-04-04 09:50:53 +0200622 u16 sts;
Pali Rohár4798ba92022-07-29 13:29:06 +0200623 int ret;
624
Marek Behún8b52b8c2024-04-04 09:50:53 +0200625 ret = omnia_mcu_get_sts(&sts);
Pali Rohár4798ba92022-07-29 13:29:06 +0200626 if (ret)
627 return "unknown";
628
Marek Behún8b52b8c2024-04-04 09:50:53 +0200629 switch (sts & STS_MCU_TYPE_MASK) {
Marek Behúnbb42a5b2024-04-04 09:50:51 +0200630 case STS_MCU_TYPE_STM32:
631 strcpy(result, "STM32");
632 break;
633 case STS_MCU_TYPE_GD32:
634 strcpy(result, "GD32");
635 break;
636 case STS_MCU_TYPE_MKL:
637 strcpy(result, "MKL");
638 break;
639 default:
640 strcpy(result, "unknown");
641 break;
642 }
643
Marek Behún8b52b8c2024-04-04 09:50:53 +0200644 if (omnia_mcu_has_feature(FEAT_PERIPH_MCU))
645 strcat(result, " (with peripheral resets)");
Pali Rohár4798ba92022-07-29 13:29:06 +0200646
Marek Behúnbb42a5b2024-04-04 09:50:51 +0200647 return result;
Pali Rohár4798ba92022-07-29 13:29:06 +0200648}
649
Pali Roháre16cc982022-08-10 11:00:25 +0200650static const char * const omnia_get_mcu_version(void)
651{
652 static char version[82];
653 u8 version_app[20];
654 u8 version_boot[20];
655 int ret;
656
657 ret = omnia_mcu_read(CMD_GET_FW_VERSION_APP, &version_app, sizeof(version_app));
658 if (ret)
659 return "unknown";
660
661 ret = omnia_mcu_read(CMD_GET_FW_VERSION_BOOT, &version_boot, sizeof(version_boot));
662 if (ret)
663 return "unknown";
664
665 /*
666 * If git commits of MCU bootloader and MCU application are same then
667 * show version only once. If they are different then show both commits.
668 */
669 if (!memcmp(version_app, version_boot, 20)) {
670 bin2hex(version, version_app, 20);
671 version[40] = '\0';
672 } else {
673 bin2hex(version, version_boot, 20);
674 version[40] = '/';
675 bin2hex(version + 41, version_app, 20);
676 version[81] = '\0';
677 }
678
679 return version;
680}
681
Marek Behún09e16b82017-06-09 19:28:45 +0200682/*
683 * Define the DDR layout / topology here in the board file. This will
684 * be used by the DDR3 init code in the SPL U-Boot version to configure
685 * the DDR3 controller.
686 */
Chris Packham1a07d212018-05-10 13:28:29 +1200687static struct mv_ddr_topology_map board_topology_map_1g = {
688 DEBUG_LEVEL_ERROR,
Marek Behún09e16b82017-06-09 19:28:45 +0200689 0x1, /* active interfaces */
690 /* cs_mask, mirror, dqs_swap, ck_swap X PUPs */
691 { { { {0x1, 0, 0, 0},
692 {0x1, 0, 0, 0},
693 {0x1, 0, 0, 0},
694 {0x1, 0, 0, 0},
695 {0x1, 0, 0, 0} },
696 SPEED_BIN_DDR_1600K, /* speed_bin */
Chris Packham1a07d212018-05-10 13:28:29 +1200697 MV_DDR_DEV_WIDTH_16BIT, /* memory_width */
698 MV_DDR_DIE_CAP_4GBIT, /* mem_size */
Chris Packham4bf81db2018-12-03 14:26:49 +1300699 MV_DDR_FREQ_800, /* frequency */
Chris Packhamdd092bd2017-11-29 10:38:34 +1300700 0, 0, /* cas_wl cas_l */
Chris Packham3a09e132018-05-10 13:28:30 +1200701 MV_DDR_TEMP_NORMAL, /* temperature */
702 MV_DDR_TIM_2T} }, /* timing */
Chris Packham1a07d212018-05-10 13:28:29 +1200703 BUS_MASK_32BIT, /* Busses mask */
704 MV_DDR_CFG_DEFAULT, /* ddr configuration data source */
Moti Buskila498475e2021-02-19 17:11:19 +0100705 NOT_COMBINED, /* ddr twin-die combined */
Chris Packham1a07d212018-05-10 13:28:29 +1200706 { {0} }, /* raw spd data */
707 {0} /* timing parameters */
Marek Behún09e16b82017-06-09 19:28:45 +0200708};
709
Chris Packham1a07d212018-05-10 13:28:29 +1200710static struct mv_ddr_topology_map board_topology_map_2g = {
711 DEBUG_LEVEL_ERROR,
Marek Behún09e16b82017-06-09 19:28:45 +0200712 0x1, /* active interfaces */
713 /* cs_mask, mirror, dqs_swap, ck_swap X PUPs */
714 { { { {0x1, 0, 0, 0},
715 {0x1, 0, 0, 0},
716 {0x1, 0, 0, 0},
717 {0x1, 0, 0, 0},
718 {0x1, 0, 0, 0} },
719 SPEED_BIN_DDR_1600K, /* speed_bin */
Chris Packham1a07d212018-05-10 13:28:29 +1200720 MV_DDR_DEV_WIDTH_16BIT, /* memory_width */
721 MV_DDR_DIE_CAP_8GBIT, /* mem_size */
Chris Packham4bf81db2018-12-03 14:26:49 +1300722 MV_DDR_FREQ_800, /* frequency */
Chris Packhamdd092bd2017-11-29 10:38:34 +1300723 0, 0, /* cas_wl cas_l */
Chris Packham3a09e132018-05-10 13:28:30 +1200724 MV_DDR_TEMP_NORMAL, /* temperature */
725 MV_DDR_TIM_2T} }, /* timing */
Chris Packham1a07d212018-05-10 13:28:29 +1200726 BUS_MASK_32BIT, /* Busses mask */
727 MV_DDR_CFG_DEFAULT, /* ddr configuration data source */
Moti Buskila498475e2021-02-19 17:11:19 +0100728 NOT_COMBINED, /* ddr twin-die combined */
Chris Packham1a07d212018-05-10 13:28:29 +1200729 { {0} }, /* raw spd data */
730 {0} /* timing parameters */
Marek Behún09e16b82017-06-09 19:28:45 +0200731};
732
Marek Behún9535f792024-06-18 17:34:34 +0200733static const struct omnia_ddr_speed {
734 char name[5];
735 u8 speed_bin;
736 u8 freq;
737} omnia_ddr_speeds[] = {
738 { "1066F", SPEED_BIN_DDR_1066F, MV_DDR_FREQ_533 },
739 { "1333H", SPEED_BIN_DDR_1333H, MV_DDR_FREQ_667 },
740 { "1600K", SPEED_BIN_DDR_1600K, MV_DDR_FREQ_800 },
741};
742
743static const struct omnia_ddr_speed *find_ddr_speed_setting(const char *name)
744{
745 for (int i = 0; i < ARRAY_SIZE(omnia_ddr_speeds); ++i)
746 if (!strncmp(name, omnia_ddr_speeds[i].name, 5))
747 return &omnia_ddr_speeds[i];
748
749 return NULL;
750}
751
752bool omnia_valid_ddr_speed(const char *name)
753{
754 return find_ddr_speed_setting(name) != NULL;
755}
756
757void omnia_print_ddr_speeds(void)
758{
759 for (int i = 0; i < ARRAY_SIZE(omnia_ddr_speeds); ++i)
760 printf("%.5s%s", omnia_ddr_speeds[i].name,
761 i == ARRAY_SIZE(omnia_ddr_speeds) - 1 ? "\n" : ", ");
762}
763
764static void fixup_speed_in_ddr_topology(struct mv_ddr_topology_map *topology)
765{
766 typeof(topology->interface_params[0]) *params;
767 const struct omnia_ddr_speed *setting;
768 const char *speed;
769 static bool done;
Marek Behún70622b32024-08-29 10:08:49 +0200770 int reset_status;
Marek Behún9535f792024-06-18 17:34:34 +0200771
772 if (done)
773 return;
774
775 done = true;
776
Marek Behún70622b32024-08-29 10:08:49 +0200777 reset_status = omnia_mcu_get_reset();
778 if (reset_status == OMNIA_RESET_TO_LOWER_DDR_SPEED)
779 speed = OMNIA_LOWER_DDR_SPEED;
780 else
781 speed = omnia_get_ddr_speed();
782
Marek Behún9535f792024-06-18 17:34:34 +0200783 if (!speed)
784 return;
785
786 setting = find_ddr_speed_setting(speed);
787 if (!setting) {
788 printf("Unsupported value %s for DDR3 speed in EEPROM!\n",
789 speed);
790 return;
791 }
792
793 params = &topology->interface_params[0];
794
795 /* don't inform if we are not changing the speed from the default one */
796 if (params->speed_bin_index == setting->speed_bin)
797 return;
798
Marek Behún70622b32024-08-29 10:08:49 +0200799 if (reset_status == OMNIA_RESET_TO_LOWER_DDR_SPEED)
800 printf("Fixing up DDR3 speed to %s as requested by reset button\n", speed);
801 else
802 printf("Fixing up DDR3 speed (EEPROM defines %s)\n", speed);
Marek Behún9535f792024-06-18 17:34:34 +0200803
804 params->speed_bin_index = setting->speed_bin;
805 params->memory_freq = setting->freq;
806}
807
Chris Packham1a07d212018-05-10 13:28:29 +1200808struct mv_ddr_topology_map *mv_ddr_topology_map_get(void)
Marek Behún09e16b82017-06-09 19:28:45 +0200809{
Marek Behún9535f792024-06-18 17:34:34 +0200810 struct mv_ddr_topology_map *topology;
811
Marek Behún77652c72019-05-02 16:53:33 +0200812 if (omnia_get_ram_size_gb() == 2)
Marek Behún9535f792024-06-18 17:34:34 +0200813 topology = &board_topology_map_2g;
Marek Behún77652c72019-05-02 16:53:33 +0200814 else
Marek Behún9535f792024-06-18 17:34:34 +0200815 topology = &board_topology_map_1g;
816
817 fixup_speed_in_ddr_topology(topology);
818
819 return topology;
Marek Behún09e16b82017-06-09 19:28:45 +0200820}
821
Marek Behún09e16b82017-06-09 19:28:45 +0200822static int set_regdomain(void)
823{
824 struct omnia_eeprom oep;
825 char rd[3] = {' ', ' ', 0};
826
827 if (omnia_read_eeprom(&oep))
828 memcpy(rd, &oep.region, 2);
829 else
830 puts("EEPROM regdomain read failed.\n");
831
832 printf("Regdomain set to %s\n", rd);
Simon Glass6a38e412017-08-03 12:22:09 -0600833 return env_set("regdomain", rd);
Marek Behún09e16b82017-06-09 19:28:45 +0200834}
Marek Behún0f2e66a2019-05-02 16:53:37 +0200835
Marek Behún0f2e66a2019-05-02 16:53:37 +0200836static void handle_reset_button(void)
837{
Pali Rohár905c3bf2021-06-14 16:45:58 +0200838 const char * const vars[1] = { "bootcmd_rescue", };
Marek Behún70622b32024-08-29 10:08:49 +0200839 int reset_status;
Marek Behún0f2e66a2019-05-02 16:53:37 +0200840
Pali Rohár905c3bf2021-06-14 16:45:58 +0200841 /*
842 * Ensure that bootcmd_rescue has always stock value, so that running
843 * run bootcmd_rescue
844 * always works correctly.
845 */
846 env_set_default_vars(1, (char * const *)vars, 0);
847
Marek Behún70622b32024-08-29 10:08:49 +0200848 reset_status = omnia_mcu_get_reset();
849 if (reset_status < 0)
Marek Behún0f2e66a2019-05-02 16:53:37 +0200850 return;
Marek Behún70622b32024-08-29 10:08:49 +0200851
852 if (reset_status == OMNIA_RESET_TO_LOWER_DDR_SPEED)
853 return omnia_eeprom_set_lower_ddr_speed();
Marek Behún0f2e66a2019-05-02 16:53:37 +0200854
855 env_set_ulong("omnia_reset", reset_status);
856
857 if (reset_status) {
Pali Rohár8adec652022-08-27 20:49:20 +0200858 const char * const vars[3] = {
Marek Behún09f8de22021-05-28 10:00:49 +0200859 "bootcmd",
Pali Rohár8adec652022-08-27 20:49:20 +0200860 "bootdelay",
Marek Behún09f8de22021-05-28 10:00:49 +0200861 "distro_bootcmd",
862 };
863
864 /*
865 * Set the above envs to their default values, in case the user
866 * managed to break them.
867 */
Pali Rohár8adec652022-08-27 20:49:20 +0200868 env_set_default_vars(3, (char * const *)vars, 0);
Marek Behún09f8de22021-05-28 10:00:49 +0200869
870 /* Ensure bootcmd_rescue is used by distroboot */
871 env_set("boot_targets", "rescue");
872
Pali Rohár4f9e6fb2022-04-06 11:39:32 +0200873 printf("RESET button was pressed, overwriting boot_targets!\n");
Marek Behún09f8de22021-05-28 10:00:49 +0200874 } else {
875 /*
876 * In case the user somehow managed to save environment with
877 * boot_targets=rescue, reset boot_targets to default value.
878 * This could happen in subsequent commands if bootcmd_rescue
879 * failed.
880 */
881 if (!strcmp(env_get("boot_targets"), "rescue")) {
882 const char * const vars[1] = {
883 "boot_targets",
884 };
885
886 env_set_default_vars(1, (char * const *)vars, 0);
887 }
Marek Behún0f2e66a2019-05-02 16:53:37 +0200888 }
889}
Marek Behún09e16b82017-06-09 19:28:45 +0200890
Pali Rohár1e0a9752022-07-29 13:29:07 +0200891static void initialize_switch(void)
892{
893 u32 val, val04, val08, val10, val14;
894 u16 ctrl[2];
895 int err;
896
897 printf("Initializing LAN eth switch... ");
898
899 /* Change RGMII pins to GPIO mode */
900
901 val = val04 = readl(MVEBU_MPP_BASE + 0x04);
902 val &= ~GENMASK(19, 16); /* MPP[12] := GPIO */
903 val &= ~GENMASK(23, 20); /* MPP[13] := GPIO */
904 val &= ~GENMASK(27, 24); /* MPP[14] := GPIO */
905 val &= ~GENMASK(31, 28); /* MPP[15] := GPIO */
906 writel(val, MVEBU_MPP_BASE + 0x04);
907
908 val = val08 = readl(MVEBU_MPP_BASE + 0x08);
909 val &= ~GENMASK(3, 0); /* MPP[16] := GPIO */
910 val &= ~GENMASK(23, 20); /* MPP[21] := GPIO */
911 writel(val, MVEBU_MPP_BASE + 0x08);
912
913 val = val10 = readl(MVEBU_MPP_BASE + 0x10);
914 val &= ~GENMASK(27, 24); /* MPP[38] := GPIO */
915 val &= ~GENMASK(31, 28); /* MPP[39] := GPIO */
916 writel(val, MVEBU_MPP_BASE + 0x10);
917
918 val = val14 = readl(MVEBU_MPP_BASE + 0x14);
919 val &= ~GENMASK(3, 0); /* MPP[40] := GPIO */
920 val &= ~GENMASK(7, 4); /* MPP[41] := GPIO */
921 writel(val, MVEBU_MPP_BASE + 0x14);
922
923 /* Set initial values for switch reset strapping pins */
924
925 val = readl(MVEBU_GPIO0_BASE + 0x00);
926 val |= BIT(12); /* GPIO[12] := 1 */
927 val |= BIT(13); /* GPIO[13] := 1 */
928 val |= BIT(14); /* GPIO[14] := 1 */
929 val |= BIT(15); /* GPIO[15] := 1 */
930 val &= ~BIT(16); /* GPIO[16] := 0 */
931 val |= BIT(21); /* GPIO[21] := 1 */
932 writel(val, MVEBU_GPIO0_BASE + 0x00);
933
934 val = readl(MVEBU_GPIO1_BASE + 0x00);
935 val |= BIT(6); /* GPIO[38] := 1 */
936 val |= BIT(7); /* GPIO[39] := 1 */
937 val |= BIT(8); /* GPIO[40] := 1 */
938 val &= ~BIT(9); /* GPIO[41] := 0 */
939 writel(val, MVEBU_GPIO1_BASE + 0x00);
940
941 val = readl(MVEBU_GPIO0_BASE + 0x04);
942 val &= ~BIT(12); /* GPIO[12] := Out Enable */
943 val &= ~BIT(13); /* GPIO[13] := Out Enable */
944 val &= ~BIT(14); /* GPIO[14] := Out Enable */
945 val &= ~BIT(15); /* GPIO[15] := Out Enable */
946 val &= ~BIT(16); /* GPIO[16] := Out Enable */
947 val &= ~BIT(21); /* GPIO[21] := Out Enable */
948 writel(val, MVEBU_GPIO0_BASE + 0x04);
949
950 val = readl(MVEBU_GPIO1_BASE + 0x04);
951 val &= ~BIT(6); /* GPIO[38] := Out Enable */
952 val &= ~BIT(7); /* GPIO[39] := Out Enable */
953 val &= ~BIT(8); /* GPIO[40] := Out Enable */
954 val &= ~BIT(9); /* GPIO[41] := Out Enable */
955 writel(val, MVEBU_GPIO1_BASE + 0x04);
956
957 /* Release switch reset */
958
959 ctrl[0] = EXT_CTL_nRES_LAN;
960 ctrl[1] = EXT_CTL_nRES_LAN;
961 err = omnia_mcu_write(CMD_EXT_CONTROL, ctrl, sizeof(ctrl));
962
Marek Behún59aa4652022-09-13 18:10:28 +0200963 mdelay(50);
Pali Rohár1e0a9752022-07-29 13:29:07 +0200964
965 /* Change RGMII pins back to RGMII mode */
966
967 writel(val04, MVEBU_MPP_BASE + 0x04);
968 writel(val08, MVEBU_MPP_BASE + 0x08);
969 writel(val10, MVEBU_MPP_BASE + 0x10);
970 writel(val14, MVEBU_MPP_BASE + 0x14);
971
972 puts(err ? "failed\n" : "done\n");
973}
974
Marek Behún09e16b82017-06-09 19:28:45 +0200975int board_early_init_f(void)
976{
Marek Behún09e16b82017-06-09 19:28:45 +0200977 /* Configure MPP */
978 writel(0x11111111, MVEBU_MPP_BASE + 0x00);
979 writel(0x11111111, MVEBU_MPP_BASE + 0x04);
980 writel(0x11244011, MVEBU_MPP_BASE + 0x08);
981 writel(0x22222111, MVEBU_MPP_BASE + 0x0c);
982 writel(0x22200002, MVEBU_MPP_BASE + 0x10);
983 writel(0x30042022, MVEBU_MPP_BASE + 0x14);
984 writel(0x55550555, MVEBU_MPP_BASE + 0x18);
985 writel(0x00005550, MVEBU_MPP_BASE + 0x1c);
986
987 /* Set GPP Out value */
988 writel(OMNIA_GPP_OUT_VAL_LOW, MVEBU_GPIO0_BASE + 0x00);
989 writel(OMNIA_GPP_OUT_VAL_MID, MVEBU_GPIO1_BASE + 0x00);
990
991 /* Set GPP Polarity */
992 writel(OMNIA_GPP_POL_LOW, MVEBU_GPIO0_BASE + 0x0c);
993 writel(OMNIA_GPP_POL_MID, MVEBU_GPIO1_BASE + 0x0c);
994
995 /* Set GPP Out Enable */
996 writel(OMNIA_GPP_OUT_ENA_LOW, MVEBU_GPIO0_BASE + 0x04);
997 writel(OMNIA_GPP_OUT_ENA_MID, MVEBU_GPIO1_BASE + 0x04);
998
Marek Behún09e16b82017-06-09 19:28:45 +0200999 return 0;
1000}
1001
Marek Behúnf3556162021-08-16 15:19:39 +02001002void spl_board_init(void)
1003{
1004 /*
1005 * If booting from UART, disable MCU watchdog in SPL, since uploading
Pali Rohár7fcda0c2021-11-09 17:14:02 +01001006 * U-Boot proper can take too much time and trigger it. Instead enable
1007 * A385 watchdog with very high timeout (10 minutes) to prevent hangup.
Marek Behúnf3556162021-08-16 15:19:39 +02001008 */
Pali Rohár7fcda0c2021-11-09 17:14:02 +01001009 if (get_boot_device() == BOOT_DEVICE_UART) {
1010 enable_a385_watchdog(10);
Marek Behúnf3556162021-08-16 15:19:39 +02001011 disable_mcu_watchdog();
Pali Rohár7fcda0c2021-11-09 17:14:02 +01001012 }
Pali Rohár1e0a9752022-07-29 13:29:07 +02001013
1014 /*
1015 * When MCU controls peripheral resets then release LAN eth switch from
1016 * the reset and initialize it. When MCU does not control peripheral
1017 * resets then LAN eth switch is initialized automatically by bootstrap
1018 * pins when A385 is released from the reset.
1019 */
Marek Behún8b52b8c2024-04-04 09:50:53 +02001020 if (omnia_mcu_has_feature(FEAT_PERIPH_MCU))
1021 initialize_switch();
Marek Behúnf3556162021-08-16 15:19:39 +02001022}
1023
Pali Rohárcbda3e22022-01-10 11:47:18 +01001024#if IS_ENABLED(CONFIG_OF_BOARD_FIXUP) || IS_ENABLED(CONFIG_OF_BOARD_SETUP)
1025
Pali Rohár7cd41732022-03-02 12:47:56 +01001026static void disable_sata_node(void *blob)
Pali Rohárcbda3e22022-01-10 11:47:18 +01001027{
Pali Rohárcbda3e22022-01-10 11:47:18 +01001028 int node;
1029
Pali Rohár7cd41732022-03-02 12:47:56 +01001030 fdt_for_each_node_by_compatible(node, blob, -1, "marvell,armada-380-ahci") {
1031 if (!fdtdec_get_is_enabled(blob, node))
1032 continue;
1033
1034 if (fdt_status_disabled(blob, node) < 0)
1035 printf("Cannot disable SATA DT node!\n");
1036 else
1037 debug("Disabled SATA DT node\n");
1038
Pali Roháre9105262022-03-02 12:47:57 +01001039 return;
Pali Rohár7cd41732022-03-02 12:47:56 +01001040 }
Pali Roháre9105262022-03-02 12:47:57 +01001041
1042 printf("Cannot find SATA DT node!\n");
Pali Rohár7cd41732022-03-02 12:47:56 +01001043}
1044
1045static void disable_pcie_node(void *blob, int port)
1046{
1047 int node;
1048
1049 fdt_for_each_node_by_compatible(node, blob, -1, "marvell,armada-370-pcie") {
1050 int port_node;
1051
1052 if (!fdtdec_get_is_enabled(blob, node))
1053 continue;
1054
1055 fdt_for_each_subnode (port_node, blob, node) {
1056 if (!fdtdec_get_is_enabled(blob, port_node))
1057 continue;
1058
1059 if (fdtdec_get_int(blob, port_node, "marvell,pcie-port", -1) != port)
1060 continue;
1061
1062 if (fdt_status_disabled(blob, port_node) < 0)
1063 printf("Cannot disable PCIe port %d DT node!\n", port);
1064 else
1065 debug("Disabled PCIe port %d DT node\n", port);
1066
1067 return;
1068 }
1069 }
Pali Roháre9105262022-03-02 12:47:57 +01001070
1071 printf("Cannot find PCIe port %d DT node!\n", port);
Pali Rohár7cd41732022-03-02 12:47:56 +01001072}
1073
1074static void fixup_msata_port_nodes(void *blob)
1075{
1076 bool mode_sata;
1077
Pali Rohárcbda3e22022-01-10 11:47:18 +01001078 /*
1079 * Determine if SerDes 0 is configured to SATA mode.
1080 * We do this instead of calling omnia_detect_sata() to avoid another
1081 * call to the MCU. By this time the common PHYs are initialized (it is
1082 * done in SPL), so we can read this common PHY register.
1083 */
1084 mode_sata = (readl(MVEBU_REGISTER(0x183fc)) & GENMASK(3, 0)) == 2;
1085
1086 /*
1087 * We're either adding status = "disabled" property, or changing
1088 * status = "okay" to status = "disabled". In both cases we'll need more
1089 * space. Increase the size a little.
1090 */
1091 if (fdt_increase_size(blob, 32) < 0) {
1092 printf("Cannot increase FDT size!\n");
1093 return;
1094 }
1095
Pali Rohárcbda3e22022-01-10 11:47:18 +01001096 if (!mode_sata) {
Pali Rohár7cd41732022-03-02 12:47:56 +01001097 /* If mSATA card is not present, disable SATA DT node */
1098 disable_sata_node(blob);
1099 } else {
1100 /* Otherwise disable PCIe port 0 DT node (MiniPCIe / mSATA port) */
1101 disable_pcie_node(blob, 0);
Pali Rohárcbda3e22022-01-10 11:47:18 +01001102 }
Pali Rohár93a89c52022-03-02 12:47:58 +01001103}
1104
1105static void fixup_wwan_port_nodes(void *blob)
1106{
1107 bool mode_usb3;
1108
1109 /* Determine if SerDes 4 is configured to USB3 mode */
1110 mode_usb3 = ((readl(MVEBU_REGISTER(0x183fc)) & GENMASK(19, 16)) >> 16) == 4;
1111
1112 /* If SerDes 4 is not configured to USB3 mode then nothing is needed to fixup */
1113 if (!mode_usb3)
1114 return;
1115
1116 /*
1117 * We're either adding status = "disabled" property, or changing
1118 * status = "okay" to status = "disabled". In both cases we'll need more
1119 * space. Increase the size a little.
1120 */
1121 if (fdt_increase_size(blob, 32) < 0) {
1122 printf("Cannot increase FDT size!\n");
1123 return;
1124 }
1125
1126 /* Disable PCIe port 2 DT node (WWAN) */
1127 disable_pcie_node(blob, 2);
Pali Rohárcbda3e22022-01-10 11:47:18 +01001128}
1129
Pali Rohár1e0a9752022-07-29 13:29:07 +02001130static int insert_mcu_gpio_prop(void *blob, int node, const char *prop,
1131 unsigned int phandle, u32 bank, u32 gpio,
1132 u32 flags)
1133{
1134 fdt32_t val[4] = { cpu_to_fdt32(phandle), cpu_to_fdt32(bank),
1135 cpu_to_fdt32(gpio), cpu_to_fdt32(flags) };
1136 return fdt_setprop(blob, node, prop, &val, sizeof(val));
1137}
1138
1139static int fixup_mcu_gpio_in_pcie_nodes(void *blob)
1140{
1141 unsigned int mcu_phandle;
1142 int port, gpio;
1143 int pcie_node;
1144 int port_node;
1145 int ret;
1146
1147 ret = fdt_increase_size(blob, 128);
1148 if (ret < 0) {
1149 printf("Cannot increase FDT size!\n");
1150 return ret;
1151 }
1152
1153 mcu_phandle = fdt_create_phandle_by_compatible(blob, "cznic,turris-omnia-mcu");
1154 if (!mcu_phandle)
1155 return -FDT_ERR_NOPHANDLES;
1156
1157 fdt_for_each_node_by_compatible(pcie_node, blob, -1, "marvell,armada-370-pcie") {
1158 if (!fdtdec_get_is_enabled(blob, pcie_node))
1159 continue;
1160
1161 fdt_for_each_subnode(port_node, blob, pcie_node) {
1162 if (!fdtdec_get_is_enabled(blob, port_node))
1163 continue;
1164
1165 port = fdtdec_get_int(blob, port_node, "marvell,pcie-port", -1);
1166
1167 if (port == 0)
1168 gpio = ilog2(EXT_CTL_nPERST0);
1169 else if (port == 1)
1170 gpio = ilog2(EXT_CTL_nPERST1);
1171 else if (port == 2)
1172 gpio = ilog2(EXT_CTL_nPERST2);
1173 else
1174 continue;
1175
1176 /* insert: reset-gpios = <&mcu 2 gpio GPIO_ACTIVE_LOW>; */
1177 ret = insert_mcu_gpio_prop(blob, port_node, "reset-gpios",
1178 mcu_phandle, 2, gpio, GPIO_ACTIVE_LOW);
1179 if (ret < 0)
1180 return ret;
1181 }
1182 }
1183
1184 return 0;
1185}
1186
Marek Behún85e223e2024-06-18 17:34:30 +02001187static int get_phy_wan_node_offset(const void *blob)
1188{
1189 u32 phy_wan_phandle;
1190
1191 phy_wan_phandle = fdt_getprop_u32_default(blob, "ethernet2", "phy-handle", 0);
1192 if (!phy_wan_phandle)
1193 return -FDT_ERR_NOTFOUND;
1194
1195 return fdt_node_offset_by_phandle(blob, phy_wan_phandle);
1196}
1197
1198static int fixup_mcu_gpio_in_phy_wan_node(void *blob)
Pali Rohár1e0a9752022-07-29 13:29:07 +02001199{
1200 unsigned int mcu_phandle;
Marek Behún85e223e2024-06-18 17:34:30 +02001201 int phy_wan_node, ret;
Pali Rohár1e0a9752022-07-29 13:29:07 +02001202
1203 ret = fdt_increase_size(blob, 64);
1204 if (ret < 0) {
1205 printf("Cannot increase FDT size!\n");
1206 return ret;
1207 }
1208
Marek Behún85e223e2024-06-18 17:34:30 +02001209 phy_wan_node = get_phy_wan_node_offset(blob);
1210 if (phy_wan_node < 0)
1211 return phy_wan_node;
Pali Rohár1e0a9752022-07-29 13:29:07 +02001212
1213 mcu_phandle = fdt_create_phandle_by_compatible(blob, "cznic,turris-omnia-mcu");
1214 if (!mcu_phandle)
1215 return -FDT_ERR_NOPHANDLES;
1216
Marek Behún85e223e2024-06-18 17:34:30 +02001217 /* insert: reset-gpios = <&mcu 2 gpio GPIO_ACTIVE_LOW>; */
1218 return insert_mcu_gpio_prop(blob, phy_wan_node, "reset-gpios",
1219 mcu_phandle, 2, ilog2(EXT_CTL_nRES_PHY), GPIO_ACTIVE_LOW);
Pali Rohár1e0a9752022-07-29 13:29:07 +02001220}
1221
Marek Behún4cf5a032024-04-04 09:50:56 +02001222static void fixup_atsha_node(void *blob)
1223{
1224 int node;
1225
1226 if (!omnia_mcu_has_feature(FEAT_CRYPTO))
1227 return;
1228
1229 node = fdt_node_offset_by_compatible(blob, -1, "atmel,atsha204a");
1230 if (node < 0) {
1231 printf("Cannot find ATSHA204A node!\n");
1232 return;
1233 }
1234
1235 if (fdt_status_disabled(blob, node) < 0)
1236 printf("Cannot disable ATSHA204A node!\n");
1237 else
1238 debug("Disabled ATSHA204A node\n");
1239}
1240
Pali Rohárcbda3e22022-01-10 11:47:18 +01001241#endif
1242
1243#if IS_ENABLED(CONFIG_OF_BOARD_FIXUP)
1244int board_fix_fdt(void *blob)
1245{
Marek Behún8b52b8c2024-04-04 09:50:53 +02001246 if (omnia_mcu_has_feature(FEAT_PERIPH_MCU)) {
1247 fixup_mcu_gpio_in_pcie_nodes(blob);
Marek Behún85e223e2024-06-18 17:34:30 +02001248 fixup_mcu_gpio_in_phy_wan_node(blob);
Pali Rohár1e0a9752022-07-29 13:29:07 +02001249 }
1250
Pali Rohár7cd41732022-03-02 12:47:56 +01001251 fixup_msata_port_nodes(blob);
Pali Rohár93a89c52022-03-02 12:47:58 +01001252 fixup_wwan_port_nodes(blob);
Pali Rohárcbda3e22022-01-10 11:47:18 +01001253
Marek Behún4cf5a032024-04-04 09:50:56 +02001254 fixup_atsha_node(blob);
1255
Pali Rohárcbda3e22022-01-10 11:47:18 +01001256 return 0;
1257}
1258#endif
1259
Marek Behún09e16b82017-06-09 19:28:45 +02001260int board_init(void)
1261{
Marek Behún4dfc57e2019-05-02 16:53:31 +02001262 /* address of boot parameters */
Marek Behún09e16b82017-06-09 19:28:45 +02001263 gd->bd->bi_boot_params = mvebu_sdram_bar(0) + 0x100;
1264
Marek Behún88dc0242021-08-16 15:19:40 +02001265 return 0;
1266}
1267
1268int board_late_init(void)
1269{
Marek Behúnf3556162021-08-16 15:19:39 +02001270 /*
1271 * If not booting from UART, MCU watchdog was not disabled in SPL,
1272 * disable it now.
1273 */
1274 if (get_boot_device() != BOOT_DEVICE_UART)
1275 disable_mcu_watchdog();
Marek Behún09e16b82017-06-09 19:28:45 +02001276
Marek Behún09e16b82017-06-09 19:28:45 +02001277 set_regdomain();
Marek Behún0f2e66a2019-05-02 16:53:37 +02001278 handle_reset_button();
Marek Behúndb1e5c62019-05-24 14:57:53 +02001279 pci_init();
Marek Behún09e16b82017-06-09 19:28:45 +02001280
1281 return 0;
1282}
1283
Simon Glass629d9b62023-11-12 19:58:23 -07001284int checkboard(void)
Marek Behún09e16b82017-06-09 19:28:45 +02001285{
Marek Behún087b2352024-04-04 09:50:55 +02001286 char serial[17], version[4], pub_key[67];
Marek Behúnc2d19d02024-04-04 09:50:54 +02001287 bool has_version;
Pali Rohár0387f7f2022-04-08 16:30:12 +02001288 int err;
Marek Behún09e16b82017-06-09 19:28:45 +02001289
Pali Rohár4798ba92022-07-29 13:29:06 +02001290 printf(" MCU type: %s\n", omnia_get_mcu_type());
Pali Roháre16cc982022-08-10 11:00:25 +02001291 printf(" MCU version: %s\n", omnia_get_mcu_version());
Marek Behúnc4ba72a2019-05-02 16:53:34 +02001292 printf(" RAM size: %i MiB\n", omnia_get_ram_size_gb() * 1024);
Marek Behúnc2d19d02024-04-04 09:50:54 +02001293
1294 if (omnia_mcu_has_feature(FEAT_BOARD_INFO)) {
1295 err = omnia_mcu_board_info(serial, NULL, version);
1296 has_version = !err;
1297 } else {
1298 err = turris_atsha_otp_get_serial_number(serial);
1299 has_version = false;
1300 }
1301
1302 printf(" Board version: %s\n", has_version ? version : "unknown");
Pali Rohár38ecdab2022-08-27 20:06:30 +02001303 printf(" Serial Number: %s\n", !err ? serial : "unknown");
Marek Behún09e16b82017-06-09 19:28:45 +02001304
Marek Behún087b2352024-04-04 09:50:55 +02001305 if (omnia_mcu_has_feature(FEAT_CRYPTO)) {
1306 err = omnia_mcu_get_board_public_key(pub_key);
1307 printf(" ECDSA Public Key: %s\n", !err ? pub_key : "unknown");
1308 }
1309
Marek Behún09e16b82017-06-09 19:28:45 +02001310 return 0;
1311}
1312
Marek Behún09e16b82017-06-09 19:28:45 +02001313int misc_init_r(void)
1314{
Marek Behúnc2d19d02024-04-04 09:50:54 +02001315 if (omnia_mcu_has_feature(FEAT_BOARD_INFO)) {
1316 char serial[17];
1317 u8 first_mac[6];
1318
1319 if (!omnia_mcu_board_info(serial, first_mac, NULL))
1320 turris_init_mac_addresses(1, first_mac);
1321 } else {
1322 turris_atsha_otp_init_mac_addresses(1);
1323 turris_atsha_otp_init_serial_number();
1324 }
1325
Marek Behún09e16b82017-06-09 19:28:45 +02001326 return 0;
1327}
1328
Marek Behún91ef59c2021-07-15 19:21:02 +02001329#if defined(CONFIG_OF_BOARD_SETUP)
1330/*
1331 * I plan to generalize this function and move it to common/fdt_support.c.
1332 * This will require some more work on multiple boards, though, so for now leave
1333 * it here.
1334 */
1335static bool fixup_mtd_partitions(void *blob, int offset, struct mtd_info *mtd)
1336{
1337 struct mtd_info *slave;
1338 int parts;
1339
1340 parts = fdt_subnode_offset(blob, offset, "partitions");
Pali Roháre2b1ba02022-08-01 12:02:19 +02001341 if (parts >= 0) {
1342 if (fdt_del_node(blob, parts) < 0)
1343 return false;
1344 }
Marek Behún91ef59c2021-07-15 19:21:02 +02001345
Pali Rohárd35b6f22022-08-01 12:02:20 +02001346 if (fdt_increase_size(blob, 512) < 0)
1347 return false;
1348
Marek Behún91ef59c2021-07-15 19:21:02 +02001349 parts = fdt_add_subnode(blob, offset, "partitions");
1350 if (parts < 0)
1351 return false;
1352
1353 if (fdt_setprop_u32(blob, parts, "#address-cells", 1) < 0)
1354 return false;
1355
1356 if (fdt_setprop_u32(blob, parts, "#size-cells", 1) < 0)
1357 return false;
1358
1359 if (fdt_setprop_string(blob, parts, "compatible",
1360 "fixed-partitions") < 0)
1361 return false;
1362
1363 mtd_probe_devices();
1364
Pali Rohárd8210ef2021-10-21 17:55:48 +02001365 list_for_each_entry_reverse(slave, &mtd->partitions, node) {
Marek Behún91ef59c2021-07-15 19:21:02 +02001366 char name[32];
1367 int part;
1368
1369 snprintf(name, sizeof(name), "partition@%llx", slave->offset);
1370 part = fdt_add_subnode(blob, parts, name);
1371 if (part < 0)
1372 return false;
1373
1374 if (fdt_setprop_u32(blob, part, "reg", slave->offset) < 0)
1375 return false;
1376
1377 if (fdt_appendprop_u32(blob, part, "reg", slave->size) < 0)
1378 return false;
1379
1380 if (fdt_setprop_string(blob, part, "label", slave->name) < 0)
1381 return false;
1382
1383 if (!(slave->flags & MTD_WRITEABLE))
1384 if (fdt_setprop_empty(blob, part, "read-only") < 0)
1385 return false;
1386
1387 if (slave->flags & MTD_POWERUP_LOCK)
1388 if (fdt_setprop_empty(blob, part, "lock") < 0)
1389 return false;
1390 }
1391
1392 return true;
1393}
1394
Pali Rohárcbda3e22022-01-10 11:47:18 +01001395static void fixup_spi_nor_partitions(void *blob)
Marek Behún91ef59c2021-07-15 19:21:02 +02001396{
Pali Rohár3215c032022-08-01 23:58:42 +02001397 struct mtd_info *mtd = NULL;
1398 char mtd_path[64];
Marek Behún91ef59c2021-07-15 19:21:02 +02001399 int node;
1400
Pali Rohár3215c032022-08-01 23:58:42 +02001401 node = fdt_node_offset_by_compatible(gd->fdt_blob, -1, "jedec,spi-nor");
1402 if (node < 0)
1403 goto fail;
1404
1405 if (fdt_get_path(gd->fdt_blob, node, mtd_path, sizeof(mtd_path)) < 0)
1406 goto fail;
1407
1408 mtd = get_mtd_device_nm(mtd_path);
Marek Behún91ef59c2021-07-15 19:21:02 +02001409 if (IS_ERR_OR_NULL(mtd))
1410 goto fail;
1411
Pali Rohár3215c032022-08-01 23:58:42 +02001412 node = fdt_node_offset_by_compatible(blob, -1, "jedec,spi-nor");
Marek Behún91ef59c2021-07-15 19:21:02 +02001413 if (node < 0)
1414 goto fail;
1415
1416 if (!fixup_mtd_partitions(blob, node, mtd))
1417 goto fail;
1418
Marek Behún36feac92021-09-25 02:49:18 +02001419 put_mtd_device(mtd);
Pali Rohárcbda3e22022-01-10 11:47:18 +01001420 return;
Marek Behún91ef59c2021-07-15 19:21:02 +02001421
1422fail:
1423 printf("Failed fixing SPI NOR partitions!\n");
Marek Behún36feac92021-09-25 02:49:18 +02001424 if (!IS_ERR_OR_NULL(mtd))
1425 put_mtd_device(mtd);
Pali Rohárcbda3e22022-01-10 11:47:18 +01001426}
1427
1428int ft_board_setup(void *blob, struct bd_info *bd)
1429{
Pali Rohár1e0a9752022-07-29 13:29:07 +02001430 int node;
1431
1432 /*
Marek Behún85e223e2024-06-18 17:34:30 +02001433 * U-Boot's FDT blob contains reset-gpios in ethernet2 PHY node when MCU
1434 * controls all peripherals resets.
Pali Rohár1e0a9752022-07-29 13:29:07 +02001435 * Fixup MCU GPIO nodes in PCIe and eth wan nodes in this case.
1436 */
Marek Behún85e223e2024-06-18 17:34:30 +02001437 node = get_phy_wan_node_offset(gd->fdt_blob);
1438 if (node >= 0 && fdt_getprop(gd->fdt_blob, node, "reset-gpios", NULL)) {
Pali Rohár1e0a9752022-07-29 13:29:07 +02001439 fixup_mcu_gpio_in_pcie_nodes(blob);
Marek Behún85e223e2024-06-18 17:34:30 +02001440 fixup_mcu_gpio_in_phy_wan_node(blob);
Pali Rohár1e0a9752022-07-29 13:29:07 +02001441 }
1442
Pali Rohárcbda3e22022-01-10 11:47:18 +01001443 fixup_spi_nor_partitions(blob);
Pali Rohár7cd41732022-03-02 12:47:56 +01001444 fixup_msata_port_nodes(blob);
Pali Rohár93a89c52022-03-02 12:47:58 +01001445 fixup_wwan_port_nodes(blob);
Pali Rohárcbda3e22022-01-10 11:47:18 +01001446
Marek Behún4cf5a032024-04-04 09:50:56 +02001447 fixup_atsha_node(blob);
1448
Marek Behún91ef59c2021-07-15 19:21:02 +02001449 return 0;
1450}
1451#endif