board: stm32mp1: board: add include for dfu
[oweals/u-boot.git] / board / st / stm32mp1 / stm32mp1.c
index b99c6c08a34262e91a11d8cb8d2ddfaa11c0e0e5..1b8ad1ef5315ef655fad9ba67e246612460934be 100644 (file)
@@ -5,26 +5,36 @@
 #include <common.h>
 #include <adc.h>
 #include <bootm.h>
-#include <config.h>
 #include <clk.h>
+#include <config.h>
+#include <dfu.h>
 #include <dm.h>
-#include <environment.h>
+#include <env.h>
+#include <env_internal.h>
 #include <g_dnl.h>
 #include <generic-phy.h>
+#include <hang.h>
 #include <i2c.h>
+#include <init.h>
 #include <led.h>
+#include <malloc.h>
+#include <memalign.h>
 #include <misc.h>
 #include <mtd.h>
 #include <mtd_node.h>
+#include <netdev.h>
 #include <phy.h>
+#include <remoteproc.h>
 #include <reset.h>
 #include <syscon.h>
 #include <usb.h>
+#include <watchdog.h>
 #include <asm/io.h>
 #include <asm/gpio.h>
 #include <asm/arch/stm32.h>
 #include <asm/arch/sys_proto.h>
 #include <jffs2/load_kernel.h>
+#include <linux/err.h>
 #include <power/regulator.h>
 #include <usb/dwc2_udc.h>
 
@@ -101,7 +111,7 @@ int checkboard(void)
        if (!ret)
                ret = misc_read(dev, STM32_BSEC_SHADOW(BSEC_OTP_BOARD),
                                &otp, sizeof(otp));
-       if (!ret && otp) {
+       if (ret > 0 && otp) {
                printf("Board: MB%04x Var%d Rev.%c-%02d\n",
                       otp >> 16,
                       (otp >> 12) & 0xF,
@@ -229,8 +239,26 @@ int g_dnl_board_usb_cable_connected(void)
 
        return dwc2_udc_B_session_valid(dwc2_udc_otg);
 }
+
+#define STM32MP1_G_DNL_DFU_PRODUCT_NUM 0xdf11
+#define STM32MP1_G_DNL_FASTBOOT_PRODUCT_NUM 0x0afb
+
+int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name)
+{
+       if (!strcmp(name, "usb_dnl_dfu"))
+               put_unaligned(STM32MP1_G_DNL_DFU_PRODUCT_NUM, &dev->idProduct);
+       else if (!strcmp(name, "usb_dnl_fastboot"))
+               put_unaligned(STM32MP1_G_DNL_FASTBOOT_PRODUCT_NUM,
+                             &dev->idProduct);
+       else
+               put_unaligned(CONFIG_USB_GADGET_PRODUCT_NUM, &dev->idProduct);
+
+       return 0;
+}
+
 #endif /* CONFIG_USB_GADGET */
 
+#ifdef CONFIG_LED
 static int get_led(struct udevice **dev, char *led_string)
 {
        char *led_name;
@@ -263,12 +291,42 @@ static int setup_led(enum led_state_t cmd)
        ret = led_set_state(dev, cmd);
        return ret;
 }
+#endif
 
+static void __maybe_unused led_error_blink(u32 nb_blink)
+{
+#ifdef CONFIG_LED
+       int ret;
+       struct udevice *led;
+       u32 i;
+#endif
+
+       if (!nb_blink)
+               return;
+
+#ifdef CONFIG_LED
+       ret = get_led(&led, "u-boot,error-led");
+       if (!ret) {
+               /* make u-boot,error-led blinking */
+               /* if U32_MAX and 125ms interval, for 17.02 years */
+               for (i = 0; i < 2 * nb_blink; i++) {
+                       led_set_state(led, LEDST_TOGGLE);
+                       mdelay(125);
+                       WATCHDOG_RESET();
+               }
+       }
+#endif
+
+       /* infinite: the boot process must be stopped */
+       if (nb_blink == U32_MAX)
+               hang();
+}
+
+#ifdef CONFIG_ADC
 static int board_check_usb_power(void)
 {
        struct ofnode_phandle_args adc_args;
        struct udevice *adc;
-       struct udevice *led;
        ofnode node;
        unsigned int raw;
        int max_uV = 0;
@@ -394,23 +452,11 @@ static int board_check_usb_power(void)
                pr_err("****************************************************\n\n");
        }
 
-       ret = get_led(&led, "u-boot,error-led");
-       if (ret) {
-               /* in unattached case, the boot process must be stopped */
-               if (nb_blink == U32_MAX)
-                       hang();
-               return ret;
-       }
-
-       /* make u-boot,error-led blinking */
-       for (i = 0; i < nb_blink * 2; i++) {
-               led_set_state(led, LEDST_TOGGLE);
-               mdelay(125);
-       }
-       led_set_state(led, LEDST_ON);
+       led_error_blink(nb_blink);
 
        return 0;
 }
+#endif /* CONFIG_ADC */
 
 static void sysconf_init(void)
 {
@@ -453,7 +499,9 @@ static void sysconf_init(void)
         *   => 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);
+       ret = uclass_get_device_by_driver(UCLASS_PMIC,
+                                         DM_GET_DRIVER(stm32mp_pwr_pmic),
+                                         &pwr_dev);
        if (!ret) {
                ret = uclass_get_device_by_driver(UCLASS_MISC,
                                                  DM_GET_DRIVER(stm32mp_bsec),
@@ -464,11 +512,11 @@ static void sysconf_init(void)
                }
 
                ret = misc_read(dev, STM32_BSEC_SHADOW(18), &otp, 4);
-               if (!ret)
+               if (ret > 0)
                        otp = otp & BIT(13);
 
-               /* get VDD = pwr-supply */
-               ret = device_get_supply_regulator(pwr_dev, "pwr-supply",
+               /* get VDD = vdd-supply */
+               ret = device_get_supply_regulator(pwr_dev, "vdd-supply",
                                                  &pwr_reg);
 
                /* check if VDD is Low Voltage */
@@ -504,6 +552,73 @@ static void sysconf_init(void)
 #endif
 }
 
+#ifdef CONFIG_DM_REGULATOR
+/* Fix to make I2C1 usable on DK2 for touchscreen usage in kernel */
+static int dk2_i2c1_fix(void)
+{
+       ofnode node;
+       struct gpio_desc hdmi, audio;
+       int ret = 0;
+
+       node = ofnode_path("/soc/i2c@40012000/hdmi-transmitter@39");
+       if (!ofnode_valid(node)) {
+               pr_debug("%s: no hdmi-transmitter@39 ?\n", __func__);
+               return -ENOENT;
+       }
+
+       if (gpio_request_by_name_nodev(node, "reset-gpios", 0,
+                                      &hdmi, GPIOD_IS_OUT)) {
+               pr_debug("%s: could not find reset-gpios\n",
+                        __func__);
+               return -ENOENT;
+       }
+
+       node = ofnode_path("/soc/i2c@40012000/cs42l51@4a");
+       if (!ofnode_valid(node)) {
+               pr_debug("%s: no cs42l51@4a ?\n", __func__);
+               return -ENOENT;
+       }
+
+       if (gpio_request_by_name_nodev(node, "reset-gpios", 0,
+                                      &audio, GPIOD_IS_OUT)) {
+               pr_debug("%s: could not find reset-gpios\n",
+                        __func__);
+               return -ENOENT;
+       }
+
+       /* before power up, insure that HDMI and AUDIO IC is under reset */
+       ret = dm_gpio_set_value(&hdmi, 1);
+       if (ret) {
+               pr_err("%s: can't set_value for hdmi_nrst gpio", __func__);
+               goto error;
+       }
+       ret = dm_gpio_set_value(&audio, 1);
+       if (ret) {
+               pr_err("%s: can't set_value for audio_nrst gpio", __func__);
+               goto error;
+       }
+
+       /* power-up audio IC */
+       regulator_autoset_by_name("v1v8_audio", NULL);
+
+       /* power-up HDMI IC */
+       regulator_autoset_by_name("v1v2_hdmi", NULL);
+       regulator_autoset_by_name("v3v3_hdmi", NULL);
+
+error:
+       return ret;
+}
+
+static bool board_is_dk2(void)
+{
+       if (CONFIG_IS_ENABLED(TARGET_ST_STM32MP15x) &&
+           of_machine_is_compatible("st,stm32mp157c-dk2"))
+               return true;
+
+       return false;
+}
+#endif
+
 /* board dependent setup after realloc */
 int board_init(void)
 {
@@ -522,12 +637,15 @@ int board_init(void)
        board_key_check();
 
 #ifdef CONFIG_DM_REGULATOR
+       if (board_is_dk2())
+               dk2_i2c1_fix();
+
        regulators_enable_boot_on(_DEBUG);
 #endif
 
        sysconf_init();
 
-       if (IS_ENABLED(CONFIG_LED))
+       if (CONFIG_IS_ENABLED(CONFIG_LED))
                led_default_state();
 
        return 0;
@@ -535,9 +653,14 @@ int board_init(void)
 
 int board_late_init(void)
 {
+       char *boot_device;
 #ifdef CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG
        const void *fdt_compat;
        int fdt_compat_len;
+       int ret;
+       u32 otp;
+       struct udevice *dev;
+       char buf[10];
 
        fdt_compat = fdt_getprop(gd->fdt_blob, 0, "compatible",
                                 &fdt_compat_len);
@@ -547,26 +670,58 @@ int board_late_init(void)
                else
                        env_set("board_name", fdt_compat + 3);
        }
+       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) {
+               snprintf(buf, sizeof(buf), "0x%04x", otp >> 16);
+               env_set("board_id", buf);
+
+               snprintf(buf, sizeof(buf), "0x%04x",
+                        ((otp >> 8) & 0xF) - 1 + 0xA);
+               env_set("board_rev", buf);
+       }
 #endif
 
+#ifdef CONFIG_ADC
        /* for DK1/DK2 boards */
        board_check_usb_power();
+#endif /* CONFIG_ADC */
+
+       /* Check the boot-source to disable bootdelay */
+       boot_device = env_get("boot_device");
+       if (!strcmp(boot_device, "serial") || !strcmp(boot_device, "usb"))
+               env_set("bootdelay", "0");
 
        return 0;
 }
 
 void board_quiesce_devices(void)
 {
+#ifdef CONFIG_LED
        setup_led(LEDST_OFF);
+#endif
 }
 
-/* board interface eth init */
-/* this is a weak define that we are overriding */
-int board_interface_eth_init(phy_interface_t interface_type,
-                            bool eth_clk_sel_reg, bool eth_ref_clk_sel_reg)
+/* eth init function : weak called in eqos driver */
+int board_interface_eth_init(struct udevice *dev,
+                            phy_interface_t interface_type)
 {
        u8 *syscfg;
        u32 value;
+       bool eth_clk_sel_reg = false;
+       bool eth_ref_clk_sel_reg = false;
+
+       /* Gigabit Ethernet 125MHz clock selection. */
+       eth_clk_sel_reg = dev_read_bool(dev, "st,eth_clk_sel");
+
+       /* Ethernet 50Mhz RMII clock selection */
+       eth_ref_clk_sel_reg =
+               dev_read_bool(dev, "st,eth_ref_clk_sel");
 
        syscfg = (u8 *)syscon_get_first_range(STM32MP_SYSCON_SYSCFG);
 
@@ -729,8 +884,9 @@ static void board_get_mtdparts(const char *dev,
 
 void board_mtdparts_default(const char **mtdids, const char **mtdparts)
 {
+       struct mtd_info *mtd;
        struct udevice *dev;
-       static char parts[2 * MTDPARTS_LEN + 1];
+       static char parts[3 * MTDPARTS_LEN + 1];
        static char ids[MTDIDS_LEN + 1];
        static bool mtd_initialized;
 
@@ -743,8 +899,24 @@ void board_mtdparts_default(const char **mtdids, const char **mtdparts)
        memset(parts, 0, sizeof(parts));
        memset(ids, 0, sizeof(ids));
 
-       if (!uclass_get_device(UCLASS_MTD, 0, &dev))
+       /* probe all MTD devices */
+       for (uclass_first_device(UCLASS_MTD, &dev);
+            dev;
+            uclass_next_device(&dev)) {
+               pr_debug("mtd device = %s\n", dev->name);
+       }
+
+       mtd = get_mtd_device_nm("nand0");
+       if (!IS_ERR_OR_NULL(mtd)) {
                board_get_mtdparts("nand0", ids, parts);
+               put_mtd_device(mtd);
+       }
+
+       mtd = get_mtd_device_nm("spi-nand0");
+       if (!IS_ERR_OR_NULL(mtd)) {
+               board_get_mtdparts("spi-nand0", ids, parts);
+               put_mtd_device(mtd);
+       }
 
        if (!uclass_get_device(UCLASS_SPI_FLASH, 0, &dev))
                board_get_mtdparts("nor0", ids, parts);
@@ -770,3 +942,166 @@ int ft_board_setup(void *blob, bd_t *bd)
        return 0;
 }
 #endif
+
+#ifdef CONFIG_SET_DFU_ALT_INFO
+#define DFU_ALT_BUF_LEN SZ_1K
+
+static void board_get_alt_info(const char *dev, char *buff)
+{
+       char var_name[32] = "dfu_alt_info_";
+       int ret;
+
+       ALLOC_CACHE_ALIGN_BUFFER(char, tmp_alt, DFU_ALT_BUF_LEN);
+
+       /* name of env variable to read = dfu_alt_info_<dev> */
+       strcat(var_name, dev);
+       ret = env_get_f(var_name, tmp_alt, DFU_ALT_BUF_LEN);
+       if (ret) {
+               if (buff[0] != '\0')
+                       strcat(buff, "&");
+               strncat(buff, tmp_alt, DFU_ALT_BUF_LEN);
+       }
+}
+
+void set_dfu_alt_info(char *interface, char *devstr)
+{
+       struct udevice *dev;
+       struct mtd_info *mtd;
+
+       ALLOC_CACHE_ALIGN_BUFFER(char, buf, DFU_ALT_BUF_LEN);
+
+       if (env_get("dfu_alt_info"))
+               return;
+
+       memset(buf, 0, sizeof(buf));
+
+       /* probe all MTD devices */
+       mtd_probe_devices();
+
+       board_get_alt_info("ram", buf);
+
+       if (!uclass_get_device(UCLASS_MMC, 0, &dev))
+               board_get_alt_info("mmc0", buf);
+
+       if (!uclass_get_device(UCLASS_MMC, 1, &dev))
+               board_get_alt_info("mmc1", buf);
+
+       if (!uclass_get_device(UCLASS_SPI_FLASH, 0, &dev))
+               board_get_alt_info("nor0", buf);
+
+       mtd = get_mtd_device_nm("nand0");
+       if (!IS_ERR_OR_NULL(mtd))
+               board_get_alt_info("nand0", buf);
+
+       mtd = get_mtd_device_nm("spi-nand0");
+       if (!IS_ERR_OR_NULL(mtd))
+               board_get_alt_info("spi-nand0", buf);
+
+#ifdef CONFIG_DFU_VIRT
+       strncat(buf, "&virt 0=OTP", DFU_ALT_BUF_LEN);
+
+       if (IS_ENABLED(CONFIG_PMIC_STPMIC1))
+               strncat(buf, "&virt 1=PMIC", DFU_ALT_BUF_LEN);
+#endif
+
+       env_set("dfu_alt_info", buf);
+       puts("DFU alt info setting: done\n");
+}
+
+#if CONFIG_IS_ENABLED(DFU_VIRT)
+#include <dfu.h>
+#include <power/stpmic1.h>
+
+int dfu_otp_read(u64 offset, u8 *buffer, long *size)
+{
+       struct udevice *dev;
+       int ret;
+
+       ret = uclass_get_device_by_driver(UCLASS_MISC,
+                                         DM_GET_DRIVER(stm32mp_bsec),
+                                         &dev);
+       if (ret)
+               return ret;
+
+       ret = misc_read(dev, offset + STM32_BSEC_OTP_OFFSET, buffer, *size);
+       if (ret >= 0) {
+               *size = ret;
+               ret = 0;
+       }
+
+       return 0;
+}
+
+int dfu_pmic_read(u64 offset, u8 *buffer, long *size)
+{
+       int ret;
+#ifdef CONFIG_PMIC_STPMIC1
+       struct udevice *dev;
+
+       ret = uclass_get_device_by_driver(UCLASS_MISC,
+                                         DM_GET_DRIVER(stpmic1_nvm),
+                                         &dev);
+       if (ret)
+               return ret;
+
+       ret = misc_read(dev, 0xF8 + offset, buffer, *size);
+       if (ret >= 0) {
+               *size = ret;
+               ret = 0;
+       }
+       if (ret == -EACCES) {
+               *size = 0;
+               ret = 0;
+       }
+#else
+       pr_err("PMIC update not supported");
+       ret = -EOPNOTSUPP;
+#endif
+
+       return ret;
+}
+
+int dfu_read_medium_virt(struct dfu_entity *dfu, u64 offset,
+                        void *buf, long *len)
+{
+       switch (dfu->data.virt.dev_num) {
+       case 0x0:
+               return dfu_otp_read(offset, buf, len);
+       case 0x1:
+               return dfu_pmic_read(offset, buf, len);
+       }
+       *len = 0;
+       return 0;
+}
+
+int __weak dfu_get_medium_size_virt(struct dfu_entity *dfu, u64 *size)
+{
+       *size = SZ_1K;
+
+       return 0;
+}
+
+#endif
+
+#endif
+
+static void board_copro_image_process(ulong fw_image, size_t fw_size)
+{
+       int ret, id = 0; /* Copro id fixed to 0 as only one coproc on mp1 */
+
+       if (!rproc_is_initialized())
+               if (rproc_init()) {
+                       printf("Remote Processor %d initialization failed\n",
+                              id);
+                       return;
+               }
+
+       ret = rproc_load(id, fw_image, fw_size);
+       printf("Load Remote Processor %d with data@addr=0x%08lx %u bytes:%s\n",
+              id, fw_image, fw_size, ret ? " Failed!" : " Success!");
+
+       if (!ret)
+               rproc_start(id);
+}
+
+U_BOOT_FIT_LOADABLE_HANDLER(IH_TYPE_COPRO, board_copro_image_process);