// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause /* * Copyright (C) 2018, STMicroelectronics - All Rights Reserved */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* SYSCFG registers */ #define SYSCFG_BOOTR 0x00 #define SYSCFG_PMCSETR 0x04 #define SYSCFG_IOCTRLSETR 0x18 #define SYSCFG_ICNR 0x1C #define SYSCFG_CMPCR 0x20 #define SYSCFG_CMPENSETR 0x24 #define SYSCFG_PMCCLRR 0x44 #define SYSCFG_BOOTR_BOOT_MASK GENMASK(2, 0) #define SYSCFG_BOOTR_BOOTPD_SHIFT 4 #define SYSCFG_IOCTRLSETR_HSLVEN_TRACE BIT(0) #define SYSCFG_IOCTRLSETR_HSLVEN_QUADSPI BIT(1) #define SYSCFG_IOCTRLSETR_HSLVEN_ETH BIT(2) #define SYSCFG_IOCTRLSETR_HSLVEN_SDMMC BIT(3) #define SYSCFG_IOCTRLSETR_HSLVEN_SPI BIT(4) #define SYSCFG_CMPCR_SW_CTRL BIT(1) #define SYSCFG_CMPCR_READY BIT(8) #define SYSCFG_CMPENSETR_MPU_EN BIT(0) #define SYSCFG_PMCSETR_ETH_CLK_SEL BIT(16) #define SYSCFG_PMCSETR_ETH_REF_CLK_SEL BIT(17) #define SYSCFG_PMCSETR_ETH_SELMII BIT(20) #define SYSCFG_PMCSETR_ETH_SEL_MASK GENMASK(23, 21) #define SYSCFG_PMCSETR_ETH_SEL_GMII_MII (0 << 21) #define SYSCFG_PMCSETR_ETH_SEL_RGMII (1 << 21) #define SYSCFG_PMCSETR_ETH_SEL_RMII (4 << 21) /* * Get a global data pointer */ DECLARE_GLOBAL_DATA_PTR; #define STM32MP_GUSBCFG 0x40002407 #define STM32MP_GGPIO 0x38 #define STM32MP_GGPIO_VBUS_SENSING BIT(21) int checkboard(void) { int ret; char *mode; u32 otp; struct udevice *dev; const char *fdt_compat; int fdt_compat_len; if (IS_ENABLED(CONFIG_STM32MP1_TRUSTED)) mode = "trusted"; else mode = "basic"; printf("Board: stm32mp1 in %s mode", mode); fdt_compat = fdt_getprop(gd->fdt_blob, 0, "compatible", &fdt_compat_len); if (fdt_compat && fdt_compat_len) printf(" (%s)", fdt_compat); puts("\n"); ret = uclass_get_device_by_driver(UCLASS_MISC, DM_GET_DRIVER(stm32mp_bsec), &dev); if (!ret) ret = misc_read(dev, STM32_BSEC_SHADOW(BSEC_OTP_BOARD), &otp, sizeof(otp)); if (!ret && otp) { printf("Board: MB%04x Var%d Rev.%c-%02d\n", otp >> 16, (otp >> 12) & 0xF, ((otp >> 8) & 0xF) - 1 + 'A', otp & 0xF); } return 0; } static void board_key_check(void) { #if defined(CONFIG_FASTBOOT) || defined(CONFIG_CMD_STM32PROG) ofnode node; struct gpio_desc gpio; enum forced_boot_mode boot_mode = BOOT_NORMAL; node = ofnode_path("/config"); if (!ofnode_valid(node)) { debug("%s: no /config node?\n", __func__); return; } #ifdef CONFIG_FASTBOOT if (gpio_request_by_name_nodev(node, "st,fastboot-gpios", 0, &gpio, GPIOD_IS_IN)) { debug("%s: could not find a /config/st,fastboot-gpios\n", __func__); } else { if (dm_gpio_get_value(&gpio)) { puts("Fastboot key pressed, "); boot_mode = BOOT_FASTBOOT; } dm_gpio_free(NULL, &gpio); } #endif #ifdef CONFIG_CMD_STM32PROG if (gpio_request_by_name_nodev(node, "st,stm32prog-gpios", 0, &gpio, GPIOD_IS_IN)) { debug("%s: could not find a /config/st,stm32prog-gpios\n", __func__); } else { if (dm_gpio_get_value(&gpio)) { puts("STM32Programmer key pressed, "); boot_mode = BOOT_STM32PROG; } dm_gpio_free(NULL, &gpio); } #endif if (boot_mode != BOOT_NORMAL) { puts("entering download mode...\n"); clrsetbits_le32(TAMP_BOOT_CONTEXT, TAMP_BOOT_FORCED_MASK, boot_mode); } #endif } static struct dwc2_plat_otg_data stm32mp_otg_data = { .usb_gusbcfg = STM32MP_GUSBCFG, }; static struct reset_ctl usbotg_reset; int board_usb_init(int index, enum usb_init_type init) { struct fdtdec_phandle_args args; struct udevice *dev; const void *blob = gd->fdt_blob; struct clk clk; struct phy phy; int node; int phy_provider; int ret; /* find the usb otg node */ node = fdt_node_offset_by_compatible(blob, -1, "snps,dwc2"); if (node < 0) { debug("Not found usb_otg device\n"); return -ENODEV; } if (!fdtdec_get_is_enabled(blob, node)) { debug("stm32 usbotg is disabled in the device tree\n"); return -ENODEV; } /* Enable clock */ ret = fdtdec_parse_phandle_with_args(blob, node, "clocks", "#clock-cells", 0, 0, &args); if (ret) { debug("usbotg has no clocks defined in the device tree\n"); return ret; } ret = uclass_get_device_by_of_offset(UCLASS_CLK, args.node, &dev); if (ret) return ret; if (args.args_count != 1) { debug("Can't find clock ID in the device tree\n"); return -ENODATA; } clk.dev = dev; clk.id = args.args[0]; ret = clk_enable(&clk); if (ret) { debug("Failed to enable usbotg clock\n"); return ret; } /* Reset */ ret = fdtdec_parse_phandle_with_args(blob, node, "resets", "#reset-cells", 0, 0, &args); if (ret) { debug("usbotg has no resets defined in the device tree\n"); goto clk_err; } ret = uclass_get_device_by_of_offset(UCLASS_RESET, args.node, &dev); if (ret || args.args_count != 1) goto clk_err; usbotg_reset.dev = dev; usbotg_reset.id = args.args[0]; reset_assert(&usbotg_reset); udelay(2); reset_deassert(&usbotg_reset); /* Get USB PHY */ ret = fdtdec_parse_phandle_with_args(blob, node, "phys", "#phy-cells", 0, 0, &args); if (!ret) { phy_provider = fdt_parent_offset(blob, args.node); ret = uclass_get_device_by_of_offset(UCLASS_PHY, phy_provider, &dev); if (ret) goto clk_err; phy.dev = dev; phy.id = fdtdec_get_uint(blob, args.node, "reg", -1); ret = generic_phy_power_on(&phy); if (ret) { debug("unable to power on the phy\n"); goto clk_err; } ret = generic_phy_init(&phy); if (ret) { debug("failed to init usb phy\n"); goto phy_power_err; } } /* Parse and store data needed for gadget */ stm32mp_otg_data.regs_otg = fdtdec_get_addr(blob, node, "reg"); if (stm32mp_otg_data.regs_otg == FDT_ADDR_T_NONE) { debug("usbotg: can't get base address\n"); ret = -ENODATA; goto phy_init_err; } stm32mp_otg_data.rx_fifo_sz = fdtdec_get_int(blob, node, "g-rx-fifo-size", 0); stm32mp_otg_data.np_tx_fifo_sz = fdtdec_get_int(blob, node, "g-np-tx-fifo-size", 0); stm32mp_otg_data.tx_fifo_sz = fdtdec_get_int(blob, node, "g-tx-fifo-size", 0); /* Enable voltage level detector */ if (!(fdtdec_parse_phandle_with_args(blob, node, "usb33d-supply", NULL, 0, 0, &args))) { if (!uclass_get_device_by_of_offset(UCLASS_REGULATOR, args.node, &dev)) { ret = regulator_set_enable(dev, true); if (ret) { debug("Failed to enable usb33d\n"); goto phy_init_err; } } } /* Enable vbus sensing */ setbits_le32(stm32mp_otg_data.regs_otg + STM32MP_GGPIO, STM32MP_GGPIO_VBUS_SENSING); return dwc2_udc_probe(&stm32mp_otg_data); phy_init_err: generic_phy_exit(&phy); phy_power_err: generic_phy_power_off(&phy); clk_err: clk_disable(&clk); return ret; } int board_usb_cleanup(int index, enum usb_init_type init) { /* Reset usbotg */ reset_assert(&usbotg_reset); udelay(2); reset_deassert(&usbotg_reset); return 0; } static void sysconf_init(void) { #ifndef CONFIG_STM32MP1_TRUSTED u8 *syscfg; #ifdef CONFIG_DM_REGULATOR struct udevice *pwr_dev; struct udevice *pwr_reg; struct udevice *dev; int ret; u32 otp = 0; #endif u32 bootr; syscfg = (u8 *)syscon_get_first_range(STM32MP_SYSCON_SYSCFG); /* interconnect update : select master using the port 1 */ /* LTDC = AXI_M9 */ /* GPU = AXI_M8 */ /* today information is hardcoded in U-Boot */ writel(BIT(9), syscfg + SYSCFG_ICNR); /* disable Pull-Down for boot pin connected to VDD */ bootr = readl(syscfg + SYSCFG_BOOTR); bootr &= ~(SYSCFG_BOOTR_BOOT_MASK << SYSCFG_BOOTR_BOOTPD_SHIFT); bootr |= (bootr & SYSCFG_BOOTR_BOOT_MASK) << SYSCFG_BOOTR_BOOTPD_SHIFT; writel(bootr, syscfg + SYSCFG_BOOTR); #ifdef CONFIG_DM_REGULATOR /* High Speed Low Voltage Pad mode Enable for SPI, SDMMC, ETH, QSPI * and TRACE. Needed above ~50MHz and conditioned by AFMUX selection. * The customer will have to disable this for low frequencies * or if AFMUX is selected but the function not used, typically for * TRACE. Otherwise, impact on power consumption. * * WARNING: * enabling High Speed mode while VDD>2.7V * with the OTP product_below_2v5 (OTP 18, BIT 13) * erroneously set to 1 can damage the IC! * => U-Boot set the register only if VDD < 2.7V (in DT) * but this value need to be consistent with board design */ ret = syscon_get_by_driver_data(STM32MP_SYSCON_PWR, &pwr_dev); if (!ret) { ret = uclass_get_device_by_driver(UCLASS_MISC, DM_GET_DRIVER(stm32mp_bsec), &dev); if (ret) { pr_err("Can't find stm32mp_bsec driver\n"); return; } ret = misc_read(dev, STM32_BSEC_SHADOW(18), &otp, 4); if (!ret) otp = otp & BIT(13); /* get VDD = pwr-supply */ ret = device_get_supply_regulator(pwr_dev, "pwr-supply", &pwr_reg); /* check if VDD is Low Voltage */ if (!ret) { if (regulator_get_value(pwr_reg) < 2700000) { writel(SYSCFG_IOCTRLSETR_HSLVEN_TRACE | SYSCFG_IOCTRLSETR_HSLVEN_QUADSPI | SYSCFG_IOCTRLSETR_HSLVEN_ETH | SYSCFG_IOCTRLSETR_HSLVEN_SDMMC | SYSCFG_IOCTRLSETR_HSLVEN_SPI, syscfg + SYSCFG_IOCTRLSETR); if (!otp) pr_err("product_below_2v5=0: HSLVEN protected by HW\n"); } else { if (otp) pr_err("product_below_2v5=1: HSLVEN update is destructive, no update as VDD>2.7V\n"); } } else { debug("VDD unknown"); } } #endif /* activate automatic I/O compensation * warning: need to ensure CSI enabled and ready in clock driver */ writel(SYSCFG_CMPENSETR_MPU_EN, syscfg + SYSCFG_CMPENSETR); while (!(readl(syscfg + SYSCFG_CMPCR) & SYSCFG_CMPCR_READY)) ; clrbits_le32(syscfg + SYSCFG_CMPCR, SYSCFG_CMPCR_SW_CTRL); #endif } /* board dependent setup after realloc */ int board_init(void) { /* address of boot parameters */ gd->bd->bi_boot_params = STM32_DDR_BASE + 0x100; board_key_check(); sysconf_init(); if (IS_ENABLED(CONFIG_LED)) led_default_state(); return 0; } int board_late_init(void) { #ifdef CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG const void *fdt_compat; int fdt_compat_len; fdt_compat = fdt_getprop(gd->fdt_blob, 0, "compatible", &fdt_compat_len); if (fdt_compat && fdt_compat_len) { if (strncmp(fdt_compat, "st,", 3) != 0) env_set("board_name", fdt_compat); else env_set("board_name", fdt_compat + 3); } #endif return 0; }