45fe3e5f0bdf800c1225a5fac2bea92c81c8cda6
[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         /* Find free buffer in default_environment[] for new variables */
103         while (*ptr != '\0' && *(ptr+1) != '\0') ptr++;
104         ptr += 2;
105
106         /*
107          * Ensure that 'env default -a' does not erase permanent MAC addresses
108          * stored in env variables: $ethaddr, $eth1addr, $eth2addr and $eth3addr
109          */
110
111         mac = env_get("ethaddr");
112         if (mac && strlen(mac) <= 17)
113                 ptr += sprintf(ptr, "ethaddr=%s", mac) + 1;
114
115         for (i = 1; i <= 3; i++) {
116                 sprintf(eth, "eth%daddr", i);
117                 mac = env_get(eth);
118                 if (mac && strlen(mac) <= 17)
119                         ptr += sprintf(ptr, "%s=%s", eth, mac) + 1;
120         }
121
122         /* If the memory controller has been configured for DDR4, we're running on v7 */
123         ddr4 = ((readl(A3700_CH0_MC_CTRL2_REG) >> A3700_MC_CTRL2_SDRAM_TYPE_OFFS)
124                 & A3700_MC_CTRL2_SDRAM_TYPE_MASK) == A3700_MC_CTRL2_SDRAM_TYPE_DDR4;
125
126         /* eMMC is mmc dev num 1 */
127         mmc_dev = find_mmc_device(1);
128         emmc = (mmc_dev && mmc_get_op_cond(mmc_dev, true) == 0);
129
130         /* if eMMC is not present then remove it from DM */
131         if (!emmc && mmc_dev) {
132                 dev = mmc_dev->dev;
133                 device_remove(dev, DM_REMOVE_NORMAL);
134                 device_unbind(dev);
135                 if (of_live_active())
136                         ofnode_set_enabled(dev_ofnode(dev), false);
137         }
138
139         /* Ensure that 'env default -a' set correct value to $fdtfile */
140         if (ddr4 && emmc)
141                 strcpy(ptr, "fdtfile=marvell/armada-3720-espressobin-v7-emmc.dtb");
142         else if (ddr4)
143                 strcpy(ptr, "fdtfile=marvell/armada-3720-espressobin-v7.dtb");
144         else if (emmc)
145                 strcpy(ptr, "fdtfile=marvell/armada-3720-espressobin-emmc.dtb");
146         else
147                 strcpy(ptr, "fdtfile=marvell/armada-3720-espressobin.dtb");
148
149         return 0;
150 }
151 #endif
152
153 /* Board specific AHCI / SATA enable code */
154 int board_ahci_enable(void)
155 {
156         struct udevice *dev;
157         int ret;
158         u8 buf[8];
159
160         /* Only DB requres this configuration */
161         if (!of_machine_is_compatible("marvell,armada-3720-db"))
162                 return 0;
163
164         /* Configure IO exander PCA9555: 7bit address 0x22 */
165         ret = i2c_get_chip_for_busnum(0, I2C_IO_EXP_ADDR, 1, &dev);
166         if (ret) {
167                 printf("Cannot find PCA9555: %d\n", ret);
168                 return 0;
169         }
170
171         ret = dm_i2c_read(dev, I2C_IO_CFG_REG_0, buf, 1);
172         if (ret) {
173                 printf("Failed to read IO expander value via I2C\n");
174                 return -EIO;
175         }
176
177         /*
178          * Enable SATA power via IO expander connected via I2C by setting
179          * the corresponding bit to output mode to enable power for SATA
180          */
181         buf[0] &= ~(1 << I2C_IO_REG_0_SATA_OFF);
182         ret = dm_i2c_write(dev, I2C_IO_CFG_REG_0, buf, 1);
183         if (ret) {
184                 printf("Failed to set IO expander via I2C\n");
185                 return -EIO;
186         }
187
188         return 0;
189 }
190
191 /* Board specific xHCI enable code */
192 int board_xhci_enable(fdt_addr_t base)
193 {
194         struct udevice *dev;
195         int ret;
196         u8 buf[8];
197
198         /* Only DB requres this configuration */
199         if (!of_machine_is_compatible("marvell,armada-3720-db"))
200                 return 0;
201
202         /* Configure IO exander PCA9555: 7bit address 0x22 */
203         ret = i2c_get_chip_for_busnum(0, I2C_IO_EXP_ADDR, 1, &dev);
204         if (ret) {
205                 printf("Cannot find PCA9555: %d\n", ret);
206                 return 0;
207         }
208
209         printf("Enable USB VBUS\n");
210
211         /*
212          * Read configuration (direction) and set VBUS pin as output
213          * (reset pin = output)
214          */
215         ret = dm_i2c_read(dev, I2C_IO_CFG_REG_0, buf, 1);
216         if (ret) {
217                 printf("Failed to read IO expander value via I2C\n");
218                 return -EIO;
219         }
220         buf[0] &= ~(1 << I2C_IO_REG_0_USB_H_OFF);
221         ret = dm_i2c_write(dev, I2C_IO_CFG_REG_0, buf, 1);
222         if (ret) {
223                 printf("Failed to set IO expander via I2C\n");
224                 return -EIO;
225         }
226
227         /* Read VBUS output value and disable it */
228         ret = dm_i2c_read(dev, I2C_IO_DATA_OUT_REG_0, buf, 1);
229         if (ret) {
230                 printf("Failed to read IO expander value via I2C\n");
231                 return -EIO;
232         }
233         buf[0] &= ~(1 << I2C_IO_REG_0_USB_H_OFF);
234         ret = dm_i2c_write(dev, I2C_IO_DATA_OUT_REG_0, buf, 1);
235         if (ret) {
236                 printf("Failed to set IO expander via I2C\n");
237                 return -EIO;
238         }
239
240         /*
241          * Required delay for configuration to settle - must wait for
242          * power on port is disabled in case VBUS signal was high,
243          * required 3 seconds delay to let VBUS signal fully settle down
244          */
245         mdelay(3000);
246
247         /* Enable VBUS power: Set output value of VBUS pin as enabled */
248         buf[0] |= (1 << I2C_IO_REG_0_USB_H_OFF);
249         ret = dm_i2c_write(dev, I2C_IO_DATA_OUT_REG_0, buf, 1);
250         if (ret) {
251                 printf("Failed to set IO expander via I2C\n");
252                 return -EIO;
253         }
254
255         mdelay(500); /* required delay to let output value settle */
256
257         return 0;
258 }
259
260 #ifdef CONFIG_LAST_STAGE_INIT
261 /* Helper function for accessing switch devices in multi-chip connection mode */
262 static int mii_multi_chip_mode_write(struct udevice *bus, int dev_smi_addr,
263                                      int smi_addr, int reg, u16 value)
264 {
265         u16 smi_cmd = 0;
266
267         if (dm_mdio_write(bus, dev_smi_addr, MDIO_DEVAD_NONE,
268                           MVEBU_SW_SMI_DATA_REG, value) != 0) {
269                 printf("Error writing to the PHY addr=%02x reg=%02x\n",
270                        smi_addr, reg);
271                 return -EFAULT;
272         }
273
274         smi_cmd = (1 << SW_SMI_CMD_SMI_BUSY_OFF) |
275                   (1 << SW_SMI_CMD_SMI_MODE_OFF) |
276                   (1 << SW_SMI_CMD_SMI_OP_OFF) |
277                   (smi_addr << SW_SMI_CMD_DEV_ADDR_OFF) |
278                   (reg << SW_SMI_CMD_REG_ADDR_OFF);
279         if (dm_mdio_write(bus, dev_smi_addr, MDIO_DEVAD_NONE,
280                           MVEBU_SW_SMI_CMD_REG, smi_cmd) != 0) {
281                 printf("Error writing to the PHY addr=%02x reg=%02x\n",
282                        smi_addr, reg);
283                 return -EFAULT;
284         }
285
286         return 0;
287 }
288
289 /* Bring-up board-specific network stuff */
290 int last_stage_init(void)
291 {
292         struct udevice *bus;
293         ofnode node;
294
295         if (!of_machine_is_compatible("globalscale,espressobin"))
296                 return 0;
297
298         node = ofnode_by_compatible(ofnode_null(), "marvell,orion-mdio");
299         if (!ofnode_valid(node) ||
300             uclass_get_device_by_ofnode(UCLASS_MDIO, node, &bus) ||
301             device_probe(bus)) {
302                 printf("Cannot find MDIO bus\n");
303                 return 0;
304         }
305
306         /*
307          * FIXME: remove this code once Topaz driver gets available
308          * A3720 Community Board Only
309          * Configure Topaz switch (88E6341)
310          * Restrict output to ports 1,2,3 only from port 0 (CPU)
311          * Set port 0,1,2,3 to forwarding Mode (through Switch Port registers)
312          */
313         mii_multi_chip_mode_write(bus, 1, MVEBU_PORT_CTRL_SMI_ADDR(1),
314                                   MVEBU_SW_PORT_BASE_VLAN, BIT(0));
315         mii_multi_chip_mode_write(bus, 1, MVEBU_PORT_CTRL_SMI_ADDR(2),
316                                   MVEBU_SW_PORT_BASE_VLAN, BIT(0));
317         mii_multi_chip_mode_write(bus, 1, MVEBU_PORT_CTRL_SMI_ADDR(3),
318                                   MVEBU_SW_PORT_BASE_VLAN, BIT(0));
319
320         mii_multi_chip_mode_write(bus, 1, MVEBU_PORT_CTRL_SMI_ADDR(0),
321                                   MVEBU_SW_PORT_CTRL_REG, 0x7f);
322         mii_multi_chip_mode_write(bus, 1, MVEBU_PORT_CTRL_SMI_ADDR(1),
323                                   MVEBU_SW_PORT_CTRL_REG, 0x7f);
324         mii_multi_chip_mode_write(bus, 1, MVEBU_PORT_CTRL_SMI_ADDR(2),
325                                   MVEBU_SW_PORT_CTRL_REG, 0x7f);
326         mii_multi_chip_mode_write(bus, 1, MVEBU_PORT_CTRL_SMI_ADDR(3),
327                                   MVEBU_SW_PORT_CTRL_REG, 0x7f);
328
329         /* RGMII Delay on Port 0 (CPU port), force link to 1000Mbps */
330         mii_multi_chip_mode_write(bus, 1, MVEBU_PORT_CTRL_SMI_ADDR(0),
331                                   MVEBU_SW_LINK_CTRL_REG, 0xe002);
332
333         /* Power up PHY 1, 2, 3 (through Global 2 registers) */
334         mii_multi_chip_mode_write(bus, 1, MVEBU_SW_G2_SMI_ADDR,
335                                   MVEBU_G2_SMI_PHY_DATA_REG, 0x1140);
336         mii_multi_chip_mode_write(bus, 1, MVEBU_SW_G2_SMI_ADDR,
337                                   MVEBU_G2_SMI_PHY_CMD_REG, 0x9620);
338         mii_multi_chip_mode_write(bus, 1, MVEBU_SW_G2_SMI_ADDR,
339                                   MVEBU_G2_SMI_PHY_CMD_REG, 0x9640);
340         mii_multi_chip_mode_write(bus, 1, MVEBU_SW_G2_SMI_ADDR,
341                                   MVEBU_G2_SMI_PHY_CMD_REG, 0x9660);
342
343         return 0;
344 }
345 #endif
346
347 #ifdef CONFIG_OF_BOARD_SETUP
348 int ft_board_setup(void *blob, struct bd_info *bd)
349 {
350 #ifdef CONFIG_ENV_IS_IN_SPI_FLASH
351         int ret;
352         int spi_off;
353         int parts_off;
354         int part_off;
355
356         /* Fill SPI MTD partitions for Linux kernel on Espressobin */
357         if (!of_machine_is_compatible("globalscale,espressobin"))
358                 return 0;
359
360         spi_off = fdt_node_offset_by_compatible(blob, -1, "jedec,spi-nor");
361         if (spi_off < 0)
362                 return 0;
363
364         /* Do not touch partitions if they are already defined */
365         if (fdt_subnode_offset(blob, spi_off, "partitions") >= 0)
366                 return 0;
367
368         parts_off = fdt_add_subnode(blob, spi_off, "partitions");
369         if (parts_off < 0) {
370                 printf("Can't add partitions node: %s\n", fdt_strerror(parts_off));
371                 return 0;
372         }
373
374         ret = fdt_setprop_string(blob, parts_off, "compatible", "fixed-partitions");
375         if (ret < 0) {
376                 printf("Can't set compatible property: %s\n", fdt_strerror(ret));
377                 return 0;
378         }
379
380         ret = fdt_setprop_u32(blob, parts_off, "#address-cells", 1);
381         if (ret < 0) {
382                 printf("Can't set #address-cells property: %s\n", fdt_strerror(ret));
383                 return 0;
384         }
385
386         ret = fdt_setprop_u32(blob, parts_off, "#size-cells", 1);
387         if (ret < 0) {
388                 printf("Can't set #size-cells property: %s\n", fdt_strerror(ret));
389                 return 0;
390         }
391
392         /* Add u-boot-env partition */
393
394         part_off = fdt_add_subnode(blob, parts_off, "partition@u-boot-env");
395         if (part_off < 0) {
396                 printf("Can't add partition@u-boot-env node: %s\n", fdt_strerror(part_off));
397                 return 0;
398         }
399
400         ret = fdt_setprop_u32(blob, part_off, "reg", CONFIG_ENV_OFFSET);
401         if (ret < 0) {
402                 printf("Can't set partition@u-boot-env reg property: %s\n", fdt_strerror(ret));
403                 return 0;
404         }
405
406         ret = fdt_appendprop_u32(blob, part_off, "reg", CONFIG_ENV_SIZE);
407         if (ret < 0) {
408                 printf("Can't set partition@u-boot-env reg property: %s\n", fdt_strerror(ret));
409                 return 0;
410         }
411
412         ret = fdt_setprop_string(blob, part_off, "label", "u-boot-env");
413         if (ret < 0) {
414                 printf("Can't set partition@u-boot-env label property: %s\n", fdt_strerror(ret));
415                 return 0;
416         }
417
418         /* Add firmware partition */
419
420         part_off = fdt_add_subnode(blob, parts_off, "partition@firmware");
421         if (part_off < 0) {
422                 printf("Can't add partition@firmware node: %s\n", fdt_strerror(part_off));
423                 return 0;
424         }
425
426         ret = fdt_setprop_u32(blob, part_off, "reg", 0);
427         if (ret < 0) {
428                 printf("Can't set partition@firmware reg property: %s\n", fdt_strerror(ret));
429                 return 0;
430         }
431
432         ret = fdt_appendprop_u32(blob, part_off, "reg", CONFIG_ENV_OFFSET);
433         if (ret < 0) {
434                 printf("Can't set partition@firmware reg property: %s\n", fdt_strerror(ret));
435                 return 0;
436         }
437
438         ret = fdt_setprop_string(blob, part_off, "label", "firmware");
439         if (ret < 0) {
440                 printf("Can't set partition@firmware label property: %s\n", fdt_strerror(ret));
441                 return 0;
442         }
443
444 #endif
445         return 0;
446 }
447 #endif