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