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 <event.h> 12#include <i2c.h> 13#include <init.h> 14#include <mmc.h> 15#include <miiphy.h> 16#include <phy.h> 17#include <fdt_support.h> 18#include <asm/global_data.h> 19#include <asm/io.h> 20#include <asm/arch/cpu.h> 21#include <asm/arch/soc.h> 22#include <linux/delay.h> 23 24DECLARE_GLOBAL_DATA_PTR; 25 26/* IO expander I2C device */ 27#define I2C_IO_EXP_ADDR 0x22 28#define I2C_IO_CFG_REG_0 0x6 29#define I2C_IO_DATA_OUT_REG_0 0x2 30#define I2C_IO_REG_0_SATA_OFF 2 31#define I2C_IO_REG_0_USB_H_OFF 1 32 33/* The pin control values are the same for DB and Espressobin */ 34#define PINCTRL_NB_REG_VALUE 0x000173fa 35#define PINCTRL_SB_REG_VALUE 0x00007a23 36 37/* Ethernet switch registers */ 38/* SMI addresses for multi-chip mode */ 39#define MVEBU_PORT_CTRL_SMI_ADDR(p) (16 + (p)) 40#define MVEBU_SW_G2_SMI_ADDR (28) 41 42/* Multi-chip mode */ 43#define MVEBU_SW_SMI_DATA_REG (1) 44#define MVEBU_SW_SMI_CMD_REG (0) 45 #define SW_SMI_CMD_REG_ADDR_OFF 0 46 #define SW_SMI_CMD_DEV_ADDR_OFF 5 47 #define SW_SMI_CMD_SMI_OP_OFF 10 48 #define SW_SMI_CMD_SMI_MODE_OFF 12 49 #define SW_SMI_CMD_SMI_BUSY_OFF 15 50 51/* Single-chip mode */ 52/* Switch Port Registers */ 53#define MVEBU_SW_LINK_CTRL_REG (1) 54#define MVEBU_SW_PORT_SWITCH_ID (3) 55#define MVEBU_SW_PORT_CTRL_REG (4) 56#define MVEBU_SW_PORT_BASE_VLAN (6) 57 58/* Global 2 Registers */ 59#define MVEBU_G2_SMI_PHY_CMD_REG (24) 60#define MVEBU_G2_SMI_PHY_DATA_REG (25) 61 62#define SWITCH_88E6361_PRODUCT_NUMBER 0x2610 63 64/* 65 * Memory Controller Registers 66 * 67 * Assembled based on public information: 68 * https://gitlab.nic.cz/turris/mox-boot-builder/-/blob/v2020.11.26/wtmi/main.c#L332-336 69 * https://github.com/MarvellEmbeddedProcessors/mv-ddr-marvell/blob/mv_ddr-armada-18.12/drivers/mv_ddr_mc6.h#L309-L332 70 * 71 * And checked against the written register values for the various topologies: 72 * https://github.com/MarvellEmbeddedProcessors/mv-ddr-marvell/blob/master/a3700/mv_ddr_tim.h 73 */ 74#define A3700_CH0_MC_CTRL2_REG MVEBU_REGISTER(0x002c4) 75#define A3700_MC_CTRL2_SDRAM_TYPE_MASK 0xf 76#define A3700_MC_CTRL2_SDRAM_TYPE_OFFS 4 77#define A3700_MC_CTRL2_SDRAM_TYPE_DDR3 2 78#define A3700_MC_CTRL2_SDRAM_TYPE_DDR4 3 79 80static bool is_edpu_plus(void) 81{ 82 struct udevice *bus; 83 ofnode node; 84 int val; 85 86 if (!CONFIG_IS_ENABLED(DM_MDIO)) 87 return false; 88 89 node = ofnode_by_compatible(ofnode_null(), "marvell,orion-mdio"); 90 if (!ofnode_valid(node) || 91 uclass_get_device_by_ofnode(UCLASS_MDIO, node, &bus) || 92 device_probe(bus)) { 93 printf("Cannot find MDIO bus\n"); 94 return -ENODEV; 95 } 96 97 val = dm_mdio_read(bus, 0x0, MDIO_DEVAD_NONE, MVEBU_SW_PORT_SWITCH_ID); 98 if (val == SWITCH_88E6361_PRODUCT_NUMBER) 99 return true; 100 else 101 return false; 102} 103 104int board_early_init_f(void) 105{ 106 return 0; 107} 108 109int board_init(void) 110{ 111 /* adress of boot parameters */ 112 gd->bd->bi_boot_params = CFG_SYS_SDRAM_BASE + 0x100; 113 114 return 0; 115} 116 117#ifdef CONFIG_BOARD_LATE_INIT 118int board_late_init(void) 119{ 120 char *ptr = &default_environment[0]; 121 struct udevice *dev; 122 struct mmc *mmc_dev; 123 bool ddr4, emmc; 124 const char *mac; 125 char eth[10]; 126 int i; 127 128 if (!of_machine_is_compatible("globalscale,espressobin")) 129 return 0; 130 131 /* 132 * Find free space for new variables in default_environment[] array. 133 * Free space is after the last variable, each variable is termined 134 * by nul byte and after the last variable is additional nul byte. 135 * Move ptr to the position where new variable can be filled. 136 */ 137 while (*ptr != '\0') { 138 do { ptr++; } while (*ptr != '\0'); 139 ptr++; 140 } 141 142 /* 143 * Ensure that 'env default -a' does not erase permanent MAC addresses 144 * stored in env variables: $ethaddr, $eth1addr, $eth2addr and $eth3addr 145 */ 146 147 mac = env_get("ethaddr"); 148 if (mac && strlen(mac) <= 17) 149 ptr += sprintf(ptr, "ethaddr=%s", mac) + 1; 150 151 for (i = 1; i <= 3; i++) { 152 sprintf(eth, "eth%daddr", i); 153 mac = env_get(eth); 154 if (mac && strlen(mac) <= 17) 155 ptr += sprintf(ptr, "%s=%s", eth, mac) + 1; 156 } 157 158 /* If the memory controller has been configured for DDR4, we're running on v7 */ 159 ddr4 = ((readl(A3700_CH0_MC_CTRL2_REG) >> A3700_MC_CTRL2_SDRAM_TYPE_OFFS) 160 & A3700_MC_CTRL2_SDRAM_TYPE_MASK) == A3700_MC_CTRL2_SDRAM_TYPE_DDR4; 161 162 /* eMMC is mmc dev num 1 */ 163 mmc_dev = find_mmc_device(1); 164 emmc = (mmc_dev && mmc_get_op_cond(mmc_dev, true) == 0); 165 166 /* if eMMC is not present then remove it from DM */ 167 if (!emmc && mmc_dev) { 168 dev = mmc_dev->dev; 169 device_remove(dev, DM_REMOVE_NORMAL); 170 device_unbind(dev); 171 if (of_live_active()) 172 ofnode_set_enabled(dev_ofnode(dev), false); 173 } 174 175 /* Ensure that 'env default -a' set correct value to $fdtfile */ 176 if (ddr4 && emmc) 177 strcpy(ptr, "fdtfile=marvell/armada-3720-espressobin-v7-emmc.dtb"); 178 else if (ddr4) 179 strcpy(ptr, "fdtfile=marvell/armada-3720-espressobin-v7.dtb"); 180 else if (emmc) 181 strcpy(ptr, "fdtfile=marvell/armada-3720-espressobin-emmc.dtb"); 182 else 183 strcpy(ptr, "fdtfile=marvell/armada-3720-espressobin.dtb"); 184 ptr += strlen(ptr) + 1; 185 186 /* 187 * After the last variable (which is nul term string) append another nul 188 * byte which terminates the list. So everything after ptr is ignored. 189 */ 190 *ptr = '\0'; 191 192 return 0; 193} 194#endif 195 196/* Board specific AHCI / SATA enable code */ 197int board_ahci_enable(void) 198{ 199 struct udevice *dev; 200 int ret; 201 u8 buf[8]; 202 203 /* Only DB requres this configuration */ 204 if (!of_machine_is_compatible("marvell,armada-3720-db")) 205 return 0; 206 207 /* Configure IO exander PCA9555: 7bit address 0x22 */ 208 ret = i2c_get_chip_for_busnum(0, I2C_IO_EXP_ADDR, 1, &dev); 209 if (ret) { 210 printf("Cannot find PCA9555: %d\n", ret); 211 return 0; 212 } 213 214 ret = dm_i2c_read(dev, I2C_IO_CFG_REG_0, buf, 1); 215 if (ret) { 216 printf("Failed to read IO expander value via I2C\n"); 217 return -EIO; 218 } 219 220 /* 221 * Enable SATA power via IO expander connected via I2C by setting 222 * the corresponding bit to output mode to enable power for SATA 223 */ 224 buf[0] &= ~(1 << I2C_IO_REG_0_SATA_OFF); 225 ret = dm_i2c_write(dev, I2C_IO_CFG_REG_0, buf, 1); 226 if (ret) { 227 printf("Failed to set IO expander via I2C\n"); 228 return -EIO; 229 } 230 231 return 0; 232} 233 234/* Board specific xHCI enable code */ 235int board_xhci_enable(fdt_addr_t base) 236{ 237 struct udevice *dev; 238 int ret; 239 u8 buf[8]; 240 241 /* Only DB requres this configuration */ 242 if (!of_machine_is_compatible("marvell,armada-3720-db")) 243 return 0; 244 245 /* Configure IO exander PCA9555: 7bit address 0x22 */ 246 ret = i2c_get_chip_for_busnum(0, I2C_IO_EXP_ADDR, 1, &dev); 247 if (ret) { 248 printf("Cannot find PCA9555: %d\n", ret); 249 return 0; 250 } 251 252 printf("Enable USB VBUS\n"); 253 254 /* 255 * Read configuration (direction) and set VBUS pin as output 256 * (reset pin = output) 257 */ 258 ret = dm_i2c_read(dev, I2C_IO_CFG_REG_0, buf, 1); 259 if (ret) { 260 printf("Failed to read IO expander value via I2C\n"); 261 return -EIO; 262 } 263 buf[0] &= ~(1 << I2C_IO_REG_0_USB_H_OFF); 264 ret = dm_i2c_write(dev, I2C_IO_CFG_REG_0, buf, 1); 265 if (ret) { 266 printf("Failed to set IO expander via I2C\n"); 267 return -EIO; 268 } 269 270 /* Read VBUS output value and disable it */ 271 ret = dm_i2c_read(dev, I2C_IO_DATA_OUT_REG_0, buf, 1); 272 if (ret) { 273 printf("Failed to read IO expander value via I2C\n"); 274 return -EIO; 275 } 276 buf[0] &= ~(1 << I2C_IO_REG_0_USB_H_OFF); 277 ret = dm_i2c_write(dev, I2C_IO_DATA_OUT_REG_0, buf, 1); 278 if (ret) { 279 printf("Failed to set IO expander via I2C\n"); 280 return -EIO; 281 } 282 283 /* 284 * Required delay for configuration to settle - must wait for 285 * power on port is disabled in case VBUS signal was high, 286 * required 3 seconds delay to let VBUS signal fully settle down 287 */ 288 mdelay(3000); 289 290 /* Enable VBUS power: Set output value of VBUS pin as enabled */ 291 buf[0] |= (1 << I2C_IO_REG_0_USB_H_OFF); 292 ret = dm_i2c_write(dev, I2C_IO_DATA_OUT_REG_0, buf, 1); 293 if (ret) { 294 printf("Failed to set IO expander via I2C\n"); 295 return -EIO; 296 } 297 298 mdelay(500); /* required delay to let output value settle */ 299 300 return 0; 301} 302 303#ifdef CONFIG_LAST_STAGE_INIT 304/* Helper function for accessing switch devices in multi-chip connection mode */ 305static int mii_multi_chip_mode_write(struct udevice *bus, int dev_smi_addr, 306 int smi_addr, int reg, u16 value) 307{ 308 u16 smi_cmd = 0; 309 310 if (dm_mdio_write(bus, dev_smi_addr, MDIO_DEVAD_NONE, 311 MVEBU_SW_SMI_DATA_REG, value) != 0) { 312 printf("Error writing to the PHY addr=%02x reg=%02x\n", 313 smi_addr, reg); 314 return -EFAULT; 315 } 316 317 smi_cmd = (1 << SW_SMI_CMD_SMI_BUSY_OFF) | 318 (1 << SW_SMI_CMD_SMI_MODE_OFF) | 319 (1 << SW_SMI_CMD_SMI_OP_OFF) | 320 (smi_addr << SW_SMI_CMD_DEV_ADDR_OFF) | 321 (reg << SW_SMI_CMD_REG_ADDR_OFF); 322 if (dm_mdio_write(bus, dev_smi_addr, MDIO_DEVAD_NONE, 323 MVEBU_SW_SMI_CMD_REG, smi_cmd) != 0) { 324 printf("Error writing to the PHY addr=%02x reg=%02x\n", 325 smi_addr, reg); 326 return -EFAULT; 327 } 328 329 return 0; 330} 331 332static int espressobin_last_stage_init(void) 333{ 334 struct udevice *bus; 335 ofnode node; 336 337 if (!CONFIG_IS_ENABLED(DM_MDIO)) 338 return 0; 339 340 node = ofnode_by_compatible(ofnode_null(), "marvell,orion-mdio"); 341 if (!ofnode_valid(node) || 342 uclass_get_device_by_ofnode(UCLASS_MDIO, node, &bus) || 343 device_probe(bus)) { 344 printf("Cannot find MDIO bus\n"); 345 return 0; 346 } 347 348 /* 349 * FIXME: remove this code once Topaz driver gets available 350 * A3720 Community Board Only 351 * Configure Topaz switch (88E6341) 352 * Restrict output to ports 1,2,3 only from port 0 (CPU) 353 * Set port 0,1,2,3 to forwarding Mode (through Switch Port registers) 354 */ 355 mii_multi_chip_mode_write(bus, 1, MVEBU_PORT_CTRL_SMI_ADDR(1), 356 MVEBU_SW_PORT_BASE_VLAN, BIT(0)); 357 mii_multi_chip_mode_write(bus, 1, MVEBU_PORT_CTRL_SMI_ADDR(2), 358 MVEBU_SW_PORT_BASE_VLAN, BIT(0)); 359 mii_multi_chip_mode_write(bus, 1, MVEBU_PORT_CTRL_SMI_ADDR(3), 360 MVEBU_SW_PORT_BASE_VLAN, BIT(0)); 361 362 mii_multi_chip_mode_write(bus, 1, MVEBU_PORT_CTRL_SMI_ADDR(0), 363 MVEBU_SW_PORT_CTRL_REG, 0x7f); 364 mii_multi_chip_mode_write(bus, 1, MVEBU_PORT_CTRL_SMI_ADDR(1), 365 MVEBU_SW_PORT_CTRL_REG, 0x7f); 366 mii_multi_chip_mode_write(bus, 1, MVEBU_PORT_CTRL_SMI_ADDR(2), 367 MVEBU_SW_PORT_CTRL_REG, 0x7f); 368 mii_multi_chip_mode_write(bus, 1, MVEBU_PORT_CTRL_SMI_ADDR(3), 369 MVEBU_SW_PORT_CTRL_REG, 0x7f); 370 371 /* RGMII Delay on Port 0 (CPU port), force link to 1000Mbps */ 372 mii_multi_chip_mode_write(bus, 1, MVEBU_PORT_CTRL_SMI_ADDR(0), 373 MVEBU_SW_LINK_CTRL_REG, 0xe002); 374 375 /* Power up PHY 1, 2, 3 (through Global 2 registers) */ 376 mii_multi_chip_mode_write(bus, 1, MVEBU_SW_G2_SMI_ADDR, 377 MVEBU_G2_SMI_PHY_DATA_REG, 0x1140); 378 mii_multi_chip_mode_write(bus, 1, MVEBU_SW_G2_SMI_ADDR, 379 MVEBU_G2_SMI_PHY_CMD_REG, 0x9620); 380 mii_multi_chip_mode_write(bus, 1, MVEBU_SW_G2_SMI_ADDR, 381 MVEBU_G2_SMI_PHY_CMD_REG, 0x9640); 382 mii_multi_chip_mode_write(bus, 1, MVEBU_SW_G2_SMI_ADDR, 383 MVEBU_G2_SMI_PHY_CMD_REG, 0x9660); 384 385 return 0; 386} 387 388static int edpu_plus_last_stage_init(void) 389{ 390 struct udevice *dev; 391 int ret; 392 393 if (is_edpu_plus()) { 394 ret = uclass_get_device_by_name(UCLASS_ETH, 395 "ethernet@40000", 396 &dev); 397 if (!ret) { 398 device_remove(dev, DM_REMOVE_NORMAL); 399 device_unbind(dev); 400 } 401 402 /* Currently no networking support on the eDPU+ board */ 403 ret = uclass_get_device_by_name(UCLASS_ETH, 404 "ethernet@30000", 405 &dev); 406 if (!ret) { 407 device_remove(dev, DM_REMOVE_NORMAL); 408 device_unbind(dev); 409 } 410 } else { 411 ret = uclass_get_device_by_name(UCLASS_ETH, 412 "ethernet@30000", 413 &dev); 414 if (!ret) { 415 device_remove(dev, DM_REMOVE_NORMAL); 416 device_unbind(dev); 417 } 418 } 419 420 return 0; 421} 422 423/* Bring-up board-specific network stuff */ 424static int last_stage_init(void) 425{ 426 427 if (of_machine_is_compatible("globalscale,espressobin")) 428 return espressobin_last_stage_init(); 429 430 if (of_machine_is_compatible("methode,edpu")) 431 return edpu_plus_last_stage_init(); 432 433 return 0; 434} 435EVENT_SPY_SIMPLE(EVT_LAST_STAGE_INIT, last_stage_init); 436#endif 437 438#ifdef CONFIG_OF_BOARD_SETUP 439static int espressobin_fdt_setup(void *blob) 440{ 441 int ret; 442 int spi_off; 443 int parts_off; 444 int part_off; 445 446 /* Fill SPI MTD partitions for Linux kernel on Espressobin */ 447 spi_off = fdt_node_offset_by_compatible(blob, -1, "jedec,spi-nor"); 448 if (spi_off < 0) 449 return 0; 450 451 /* Do not touch partitions if they are already defined */ 452 if (fdt_subnode_offset(blob, spi_off, "partitions") >= 0) 453 return 0; 454 455 parts_off = fdt_add_subnode(blob, spi_off, "partitions"); 456 if (parts_off < 0) { 457 printf("Can't add partitions node: %s\n", fdt_strerror(parts_off)); 458 return 0; 459 } 460 461 ret = fdt_setprop_string(blob, parts_off, "compatible", "fixed-partitions"); 462 if (ret < 0) { 463 printf("Can't set compatible property: %s\n", fdt_strerror(ret)); 464 return 0; 465 } 466 467 ret = fdt_setprop_u32(blob, parts_off, "#address-cells", 1); 468 if (ret < 0) { 469 printf("Can't set #address-cells property: %s\n", fdt_strerror(ret)); 470 return 0; 471 } 472 473 ret = fdt_setprop_u32(blob, parts_off, "#size-cells", 1); 474 if (ret < 0) { 475 printf("Can't set #size-cells property: %s\n", fdt_strerror(ret)); 476 return 0; 477 } 478 479 /* Add u-boot-env partition */ 480 481 part_off = fdt_add_subnode(blob, parts_off, "partition@u-boot-env"); 482 if (part_off < 0) { 483 printf("Can't add partition@u-boot-env node: %s\n", fdt_strerror(part_off)); 484 return 0; 485 } 486 487 ret = fdt_setprop_u32(blob, part_off, "reg", CONFIG_ENV_OFFSET); 488 if (ret < 0) { 489 printf("Can't set partition@u-boot-env reg property: %s\n", fdt_strerror(ret)); 490 return 0; 491 } 492 493 ret = fdt_appendprop_u32(blob, part_off, "reg", CONFIG_ENV_SIZE); 494 if (ret < 0) { 495 printf("Can't set partition@u-boot-env reg property: %s\n", fdt_strerror(ret)); 496 return 0; 497 } 498 499 ret = fdt_setprop_string(blob, part_off, "label", "u-boot-env"); 500 if (ret < 0) { 501 printf("Can't set partition@u-boot-env label property: %s\n", fdt_strerror(ret)); 502 return 0; 503 } 504 505 /* Add firmware partition */ 506 507 part_off = fdt_add_subnode(blob, parts_off, "partition@firmware"); 508 if (part_off < 0) { 509 printf("Can't add partition@firmware node: %s\n", fdt_strerror(part_off)); 510 return 0; 511 } 512 513 ret = fdt_setprop_u32(blob, part_off, "reg", 0); 514 if (ret < 0) { 515 printf("Can't set partition@firmware reg property: %s\n", fdt_strerror(ret)); 516 return 0; 517 } 518 519 ret = fdt_appendprop_u32(blob, part_off, "reg", CONFIG_ENV_OFFSET); 520 if (ret < 0) { 521 printf("Can't set partition@firmware reg property: %s\n", fdt_strerror(ret)); 522 return 0; 523 } 524 525 ret = fdt_setprop_string(blob, part_off, "label", "firmware"); 526 if (ret < 0) { 527 printf("Can't set partition@firmware label property: %s\n", fdt_strerror(ret)); 528 return 0; 529 } 530 531 return 0; 532} 533 534static int edpu_plus_fdt_setup(void *blob) 535{ 536 const char *ports[] = { "downlink", "uplink" }; 537 uint8_t mac[ETH_ALEN]; 538 const char *path; 539 int i, ret; 540 541 if (is_edpu_plus()) { 542 ret = fdt_set_status_by_compatible(blob, 543 "marvell,orion-mdio", 544 FDT_STATUS_OKAY); 545 if (ret) 546 printf("Failed to enable MDIO!\n"); 547 548 ret = fdt_set_status_by_alias(blob, 549 "ethernet1", 550 FDT_STATUS_DISABLED); 551 if (ret) 552 printf("Failed to disable ethernet1!\n"); 553 554 path = fdt_get_alias(blob, "ethernet0"); 555 if (path) 556 do_fixup_by_path_string(blob, path, "phy-mode", "2500base-x"); 557 else 558 printf("Failed to update ethernet0 phy-mode to 2500base-x!\n"); 559 560 ret = fdt_set_status_by_compatible(blob, 561 "marvell,mv88e6190", 562 FDT_STATUS_OKAY); 563 if (ret) 564 printf("Failed to enable MV88E6361!\n"); 565 566 /* 567 * MAC-s for Uplink and Downlink ports are stored under 568 * non standard variable names, so lets manually fixup the 569 * switch port nodes to have the desired MAC-s. 570 */ 571 for (i = 0; i < 2; i++) { 572 if (eth_env_get_enetaddr(ports[i], mac)) { 573 do_fixup_by_prop(blob, 574 "label", 575 ports[i], 576 strlen(ports[i]) + 1, 577 "mac-address", 578 mac, ARP_HLEN, 1); 579 580 do_fixup_by_prop(blob, 581 "label", 582 ports[i], 583 strlen(ports[i]) + 1, 584 "local-mac-address", 585 mac, ARP_HLEN, 1); 586 } 587 } 588 } 589 590 return 0; 591} 592 593int ft_board_setup(void *blob, struct bd_info *bd) 594{ 595#ifdef CONFIG_ENV_IS_IN_SPI_FLASH 596 if (of_machine_is_compatible("globalscale,espressobin")) 597 return espressobin_fdt_setup(blob); 598#endif 599 if (of_machine_is_compatible("methode,edpu")) 600 return edpu_plus_fdt_setup(blob); 601 602 return 0; 603} 604#endif 605