Prepare v2023.10
[platform/kernel/u-boot.git] / board / Marvell / mvebu_armada-37xx / board.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2016 Stefan Roese <sr@denx.de>
4  */
5
6 #include <common.h>
7 #include <dm.h>
8 #include <dm/device-internal.h>
9 #include <env.h>
10 #include <env_internal.h>
11 #include <i2c.h>
12 #include <init.h>
13 #include <mmc.h>
14 #include <miiphy.h>
15 #include <phy.h>
16 #include <asm/global_data.h>
17 #include <asm/io.h>
18 #include <asm/arch/cpu.h>
19 #include <asm/arch/soc.h>
20 #include <linux/delay.h>
21
22 DECLARE_GLOBAL_DATA_PTR;
23
24 /* IO expander I2C device */
25 #define I2C_IO_EXP_ADDR         0x22
26 #define I2C_IO_CFG_REG_0        0x6
27 #define I2C_IO_DATA_OUT_REG_0   0x2
28 #define I2C_IO_REG_0_SATA_OFF   2
29 #define I2C_IO_REG_0_USB_H_OFF  1
30
31 /* The pin control values are the same for DB and Espressobin */
32 #define PINCTRL_NB_REG_VALUE    0x000173fa
33 #define PINCTRL_SB_REG_VALUE    0x00007a23
34
35 /* Ethernet switch registers */
36 /* SMI addresses for multi-chip mode */
37 #define MVEBU_PORT_CTRL_SMI_ADDR(p)     (16 + (p))
38 #define MVEBU_SW_G2_SMI_ADDR            (28)
39
40 /* Multi-chip mode */
41 #define MVEBU_SW_SMI_DATA_REG           (1)
42 #define MVEBU_SW_SMI_CMD_REG            (0)
43  #define SW_SMI_CMD_REG_ADDR_OFF        0
44  #define SW_SMI_CMD_DEV_ADDR_OFF        5
45  #define SW_SMI_CMD_SMI_OP_OFF          10
46  #define SW_SMI_CMD_SMI_MODE_OFF        12
47  #define SW_SMI_CMD_SMI_BUSY_OFF        15
48
49 /* Single-chip mode */
50 /* Switch Port Registers */
51 #define MVEBU_SW_LINK_CTRL_REG          (1)
52 #define MVEBU_SW_PORT_CTRL_REG          (4)
53 #define MVEBU_SW_PORT_BASE_VLAN         (6)
54
55 /* Global 2 Registers */
56 #define MVEBU_G2_SMI_PHY_CMD_REG        (24)
57 #define MVEBU_G2_SMI_PHY_DATA_REG       (25)
58
59 /*
60  * Memory Controller Registers
61  *
62  * Assembled based on public information:
63  * https://gitlab.nic.cz/turris/mox-boot-builder/-/blob/v2020.11.26/wtmi/main.c#L332-336
64  * https://github.com/MarvellEmbeddedProcessors/mv-ddr-marvell/blob/mv_ddr-armada-18.12/drivers/mv_ddr_mc6.h#L309-L332
65  *
66  * And checked against the written register values for the various topologies:
67  * https://github.com/MarvellEmbeddedProcessors/mv-ddr-marvell/blob/master/a3700/mv_ddr_tim.h
68  */
69 #define A3700_CH0_MC_CTRL2_REG          MVEBU_REGISTER(0x002c4)
70 #define A3700_MC_CTRL2_SDRAM_TYPE_MASK  0xf
71 #define A3700_MC_CTRL2_SDRAM_TYPE_OFFS  4
72 #define A3700_MC_CTRL2_SDRAM_TYPE_DDR3  2
73 #define A3700_MC_CTRL2_SDRAM_TYPE_DDR4  3
74
75 int board_early_init_f(void)
76 {
77         return 0;
78 }
79
80 int board_init(void)
81 {
82         /* adress of boot parameters */
83         gd->bd->bi_boot_params = CFG_SYS_SDRAM_BASE + 0x100;
84
85         return 0;
86 }
87
88 #ifdef CONFIG_BOARD_LATE_INIT
89 int board_late_init(void)
90 {
91         char *ptr = &default_environment[0];
92         struct udevice *dev;
93         struct mmc *mmc_dev;
94         bool ddr4, emmc;
95         const char *mac;
96         char eth[10];
97         int i;
98
99         if (!of_machine_is_compatible("globalscale,espressobin"))
100                 return 0;
101
102         /*
103          * Find free space for new variables in default_environment[] array.
104          * Free space is after the last variable, each variable is termined
105          * by nul byte and after the last variable is additional nul byte.
106          * Move ptr to the position where new variable can be filled.
107          */
108         while (*ptr != '\0') {
109                 do { ptr++; } while (*ptr != '\0');
110                 ptr++;
111         }
112
113         /*
114          * Ensure that 'env default -a' does not erase permanent MAC addresses
115          * stored in env variables: $ethaddr, $eth1addr, $eth2addr and $eth3addr
116          */
117
118         mac = env_get("ethaddr");
119         if (mac && strlen(mac) <= 17)
120                 ptr += sprintf(ptr, "ethaddr=%s", mac) + 1;
121
122         for (i = 1; i <= 3; i++) {
123                 sprintf(eth, "eth%daddr", i);
124                 mac = env_get(eth);
125                 if (mac && strlen(mac) <= 17)
126                         ptr += sprintf(ptr, "%s=%s", eth, mac) + 1;
127         }
128
129         /* If the memory controller has been configured for DDR4, we're running on v7 */
130         ddr4 = ((readl(A3700_CH0_MC_CTRL2_REG) >> A3700_MC_CTRL2_SDRAM_TYPE_OFFS)
131                 & A3700_MC_CTRL2_SDRAM_TYPE_MASK) == A3700_MC_CTRL2_SDRAM_TYPE_DDR4;
132
133         /* eMMC is mmc dev num 1 */
134         mmc_dev = find_mmc_device(1);
135         emmc = (mmc_dev && mmc_get_op_cond(mmc_dev, true) == 0);
136
137         /* if eMMC is not present then remove it from DM */
138         if (!emmc && mmc_dev) {
139                 dev = mmc_dev->dev;
140                 device_remove(dev, DM_REMOVE_NORMAL);
141                 device_unbind(dev);
142                 if (of_live_active())
143                         ofnode_set_enabled(dev_ofnode(dev), false);
144         }
145
146         /* Ensure that 'env default -a' set correct value to $fdtfile */
147         if (ddr4 && emmc)
148                 strcpy(ptr, "fdtfile=marvell/armada-3720-espressobin-v7-emmc.dtb");
149         else if (ddr4)
150                 strcpy(ptr, "fdtfile=marvell/armada-3720-espressobin-v7.dtb");
151         else if (emmc)
152                 strcpy(ptr, "fdtfile=marvell/armada-3720-espressobin-emmc.dtb");
153         else
154                 strcpy(ptr, "fdtfile=marvell/armada-3720-espressobin.dtb");
155         ptr += strlen(ptr) + 1;
156
157         /*
158          * After the last variable (which is nul term string) append another nul
159          * byte which terminates the list. So everything after ptr is ignored.
160          */
161         *ptr = '\0';
162
163         return 0;
164 }
165 #endif
166
167 /* Board specific AHCI / SATA enable code */
168 int board_ahci_enable(void)
169 {
170         struct udevice *dev;
171         int ret;
172         u8 buf[8];
173
174         /* Only DB requres this configuration */
175         if (!of_machine_is_compatible("marvell,armada-3720-db"))
176                 return 0;
177
178         /* Configure IO exander PCA9555: 7bit address 0x22 */
179         ret = i2c_get_chip_for_busnum(0, I2C_IO_EXP_ADDR, 1, &dev);
180         if (ret) {
181                 printf("Cannot find PCA9555: %d\n", ret);
182                 return 0;
183         }
184
185         ret = dm_i2c_read(dev, I2C_IO_CFG_REG_0, buf, 1);
186         if (ret) {
187                 printf("Failed to read IO expander value via I2C\n");
188                 return -EIO;
189         }
190
191         /*
192          * Enable SATA power via IO expander connected via I2C by setting
193          * the corresponding bit to output mode to enable power for SATA
194          */
195         buf[0] &= ~(1 << I2C_IO_REG_0_SATA_OFF);
196         ret = dm_i2c_write(dev, I2C_IO_CFG_REG_0, buf, 1);
197         if (ret) {
198                 printf("Failed to set IO expander via I2C\n");
199                 return -EIO;
200         }
201
202         return 0;
203 }
204
205 /* Board specific xHCI enable code */
206 int board_xhci_enable(fdt_addr_t base)
207 {
208         struct udevice *dev;
209         int ret;
210         u8 buf[8];
211
212         /* Only DB requres this configuration */
213         if (!of_machine_is_compatible("marvell,armada-3720-db"))
214                 return 0;
215
216         /* Configure IO exander PCA9555: 7bit address 0x22 */
217         ret = i2c_get_chip_for_busnum(0, I2C_IO_EXP_ADDR, 1, &dev);
218         if (ret) {
219                 printf("Cannot find PCA9555: %d\n", ret);
220                 return 0;
221         }
222
223         printf("Enable USB VBUS\n");
224
225         /*
226          * Read configuration (direction) and set VBUS pin as output
227          * (reset pin = output)
228          */
229         ret = dm_i2c_read(dev, I2C_IO_CFG_REG_0, buf, 1);
230         if (ret) {
231                 printf("Failed to read IO expander value via I2C\n");
232                 return -EIO;
233         }
234         buf[0] &= ~(1 << I2C_IO_REG_0_USB_H_OFF);
235         ret = dm_i2c_write(dev, I2C_IO_CFG_REG_0, buf, 1);
236         if (ret) {
237                 printf("Failed to set IO expander via I2C\n");
238                 return -EIO;
239         }
240
241         /* Read VBUS output value and disable it */
242         ret = dm_i2c_read(dev, I2C_IO_DATA_OUT_REG_0, buf, 1);
243         if (ret) {
244                 printf("Failed to read IO expander value via I2C\n");
245                 return -EIO;
246         }
247         buf[0] &= ~(1 << I2C_IO_REG_0_USB_H_OFF);
248         ret = dm_i2c_write(dev, I2C_IO_DATA_OUT_REG_0, buf, 1);
249         if (ret) {
250                 printf("Failed to set IO expander via I2C\n");
251                 return -EIO;
252         }
253
254         /*
255          * Required delay for configuration to settle - must wait for
256          * power on port is disabled in case VBUS signal was high,
257          * required 3 seconds delay to let VBUS signal fully settle down
258          */
259         mdelay(3000);
260
261         /* Enable VBUS power: Set output value of VBUS pin as enabled */
262         buf[0] |= (1 << I2C_IO_REG_0_USB_H_OFF);
263         ret = dm_i2c_write(dev, I2C_IO_DATA_OUT_REG_0, buf, 1);
264         if (ret) {
265                 printf("Failed to set IO expander via I2C\n");
266                 return -EIO;
267         }
268
269         mdelay(500); /* required delay to let output value settle */
270
271         return 0;
272 }
273
274 #ifdef CONFIG_LAST_STAGE_INIT
275 /* Helper function for accessing switch devices in multi-chip connection mode */
276 static int mii_multi_chip_mode_write(struct udevice *bus, int dev_smi_addr,
277                                      int smi_addr, int reg, u16 value)
278 {
279         u16 smi_cmd = 0;
280
281         if (dm_mdio_write(bus, dev_smi_addr, MDIO_DEVAD_NONE,
282                           MVEBU_SW_SMI_DATA_REG, value) != 0) {
283                 printf("Error writing to the PHY addr=%02x reg=%02x\n",
284                        smi_addr, reg);
285                 return -EFAULT;
286         }
287
288         smi_cmd = (1 << SW_SMI_CMD_SMI_BUSY_OFF) |
289                   (1 << SW_SMI_CMD_SMI_MODE_OFF) |
290                   (1 << SW_SMI_CMD_SMI_OP_OFF) |
291                   (smi_addr << SW_SMI_CMD_DEV_ADDR_OFF) |
292                   (reg << SW_SMI_CMD_REG_ADDR_OFF);
293         if (dm_mdio_write(bus, dev_smi_addr, MDIO_DEVAD_NONE,
294                           MVEBU_SW_SMI_CMD_REG, smi_cmd) != 0) {
295                 printf("Error writing to the PHY addr=%02x reg=%02x\n",
296                        smi_addr, reg);
297                 return -EFAULT;
298         }
299
300         return 0;
301 }
302
303 /* Bring-up board-specific network stuff */
304 int last_stage_init(void)
305 {
306         struct udevice *bus;
307         ofnode node;
308
309         if (!of_machine_is_compatible("globalscale,espressobin"))
310                 return 0;
311
312         node = ofnode_by_compatible(ofnode_null(), "marvell,orion-mdio");
313         if (!ofnode_valid(node) ||
314             uclass_get_device_by_ofnode(UCLASS_MDIO, node, &bus) ||
315             device_probe(bus)) {
316                 printf("Cannot find MDIO bus\n");
317                 return 0;
318         }
319
320         /*
321          * FIXME: remove this code once Topaz driver gets available
322          * A3720 Community Board Only
323          * Configure Topaz switch (88E6341)
324          * Restrict output to ports 1,2,3 only from port 0 (CPU)
325          * Set port 0,1,2,3 to forwarding Mode (through Switch Port registers)
326          */
327         mii_multi_chip_mode_write(bus, 1, MVEBU_PORT_CTRL_SMI_ADDR(1),
328                                   MVEBU_SW_PORT_BASE_VLAN, BIT(0));
329         mii_multi_chip_mode_write(bus, 1, MVEBU_PORT_CTRL_SMI_ADDR(2),
330                                   MVEBU_SW_PORT_BASE_VLAN, BIT(0));
331         mii_multi_chip_mode_write(bus, 1, MVEBU_PORT_CTRL_SMI_ADDR(3),
332                                   MVEBU_SW_PORT_BASE_VLAN, BIT(0));
333
334         mii_multi_chip_mode_write(bus, 1, MVEBU_PORT_CTRL_SMI_ADDR(0),
335                                   MVEBU_SW_PORT_CTRL_REG, 0x7f);
336         mii_multi_chip_mode_write(bus, 1, MVEBU_PORT_CTRL_SMI_ADDR(1),
337                                   MVEBU_SW_PORT_CTRL_REG, 0x7f);
338         mii_multi_chip_mode_write(bus, 1, MVEBU_PORT_CTRL_SMI_ADDR(2),
339                                   MVEBU_SW_PORT_CTRL_REG, 0x7f);
340         mii_multi_chip_mode_write(bus, 1, MVEBU_PORT_CTRL_SMI_ADDR(3),
341                                   MVEBU_SW_PORT_CTRL_REG, 0x7f);
342
343         /* RGMII Delay on Port 0 (CPU port), force link to 1000Mbps */
344         mii_multi_chip_mode_write(bus, 1, MVEBU_PORT_CTRL_SMI_ADDR(0),
345                                   MVEBU_SW_LINK_CTRL_REG, 0xe002);
346
347         /* Power up PHY 1, 2, 3 (through Global 2 registers) */
348         mii_multi_chip_mode_write(bus, 1, MVEBU_SW_G2_SMI_ADDR,
349                                   MVEBU_G2_SMI_PHY_DATA_REG, 0x1140);
350         mii_multi_chip_mode_write(bus, 1, MVEBU_SW_G2_SMI_ADDR,
351                                   MVEBU_G2_SMI_PHY_CMD_REG, 0x9620);
352         mii_multi_chip_mode_write(bus, 1, MVEBU_SW_G2_SMI_ADDR,
353                                   MVEBU_G2_SMI_PHY_CMD_REG, 0x9640);
354         mii_multi_chip_mode_write(bus, 1, MVEBU_SW_G2_SMI_ADDR,
355                                   MVEBU_G2_SMI_PHY_CMD_REG, 0x9660);
356
357         return 0;
358 }
359 #endif
360
361 #ifdef CONFIG_OF_BOARD_SETUP
362 int ft_board_setup(void *blob, struct bd_info *bd)
363 {
364 #ifdef CONFIG_ENV_IS_IN_SPI_FLASH
365         int ret;
366         int spi_off;
367         int parts_off;
368         int part_off;
369
370         /* Fill SPI MTD partitions for Linux kernel on Espressobin */
371         if (!of_machine_is_compatible("globalscale,espressobin"))
372                 return 0;
373
374         spi_off = fdt_node_offset_by_compatible(blob, -1, "jedec,spi-nor");
375         if (spi_off < 0)
376                 return 0;
377
378         /* Do not touch partitions if they are already defined */
379         if (fdt_subnode_offset(blob, spi_off, "partitions") >= 0)
380                 return 0;
381
382         parts_off = fdt_add_subnode(blob, spi_off, "partitions");
383         if (parts_off < 0) {
384                 printf("Can't add partitions node: %s\n", fdt_strerror(parts_off));
385                 return 0;
386         }
387
388         ret = fdt_setprop_string(blob, parts_off, "compatible", "fixed-partitions");
389         if (ret < 0) {
390                 printf("Can't set compatible property: %s\n", fdt_strerror(ret));
391                 return 0;
392         }
393
394         ret = fdt_setprop_u32(blob, parts_off, "#address-cells", 1);
395         if (ret < 0) {
396                 printf("Can't set #address-cells property: %s\n", fdt_strerror(ret));
397                 return 0;
398         }
399
400         ret = fdt_setprop_u32(blob, parts_off, "#size-cells", 1);
401         if (ret < 0) {
402                 printf("Can't set #size-cells property: %s\n", fdt_strerror(ret));
403                 return 0;
404         }
405
406         /* Add u-boot-env partition */
407
408         part_off = fdt_add_subnode(blob, parts_off, "partition@u-boot-env");
409         if (part_off < 0) {
410                 printf("Can't add partition@u-boot-env node: %s\n", fdt_strerror(part_off));
411                 return 0;
412         }
413
414         ret = fdt_setprop_u32(blob, part_off, "reg", CONFIG_ENV_OFFSET);
415         if (ret < 0) {
416                 printf("Can't set partition@u-boot-env reg property: %s\n", fdt_strerror(ret));
417                 return 0;
418         }
419
420         ret = fdt_appendprop_u32(blob, part_off, "reg", CONFIG_ENV_SIZE);
421         if (ret < 0) {
422                 printf("Can't set partition@u-boot-env reg property: %s\n", fdt_strerror(ret));
423                 return 0;
424         }
425
426         ret = fdt_setprop_string(blob, part_off, "label", "u-boot-env");
427         if (ret < 0) {
428                 printf("Can't set partition@u-boot-env label property: %s\n", fdt_strerror(ret));
429                 return 0;
430         }
431
432         /* Add firmware partition */
433
434         part_off = fdt_add_subnode(blob, parts_off, "partition@firmware");
435         if (part_off < 0) {
436                 printf("Can't add partition@firmware node: %s\n", fdt_strerror(part_off));
437                 return 0;
438         }
439
440         ret = fdt_setprop_u32(blob, part_off, "reg", 0);
441         if (ret < 0) {
442                 printf("Can't set partition@firmware reg property: %s\n", fdt_strerror(ret));
443                 return 0;
444         }
445
446         ret = fdt_appendprop_u32(blob, part_off, "reg", CONFIG_ENV_OFFSET);
447         if (ret < 0) {
448                 printf("Can't set partition@firmware reg property: %s\n", fdt_strerror(ret));
449                 return 0;
450         }
451
452         ret = fdt_setprop_string(blob, part_off, "label", "firmware");
453         if (ret < 0) {
454                 printf("Can't set partition@firmware label property: %s\n", fdt_strerror(ret));
455                 return 0;
456         }
457
458 #endif
459         return 0;
460 }
461 #endif