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