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