// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause /* * Copyright (C) 2019, STMicroelectronics - All Rights Reserved */ #define pr_fmt(fmt) "%s: " fmt, __func__ #include #include #include #include #include #include #include #include #include #define RCC_GCR_HOLD_BOOT 0 #define RCC_GCR_RELEASE_BOOT 1 /** * struct stm32_copro_privdata - power processor private data * @reset_ctl: reset controller handle * @hold_boot_regmap: regmap for remote processor reset hold boot * @hold_boot_offset: offset of the register controlling the hold boot setting * @hold_boot_mask: bitmask of the register for the hold boot field * @rsc_table_addr: resource table address */ struct stm32_copro_privdata { struct reset_ctl reset_ctl; struct regmap *hold_boot_regmap; uint hold_boot_offset; uint hold_boot_mask; ulong rsc_table_addr; }; /** * stm32_copro_probe() - Basic probe * @dev: corresponding STM32 remote processor device * @return 0 if all went ok, else corresponding -ve error */ static int stm32_copro_probe(struct udevice *dev) { struct stm32_copro_privdata *priv; struct regmap *regmap; const fdt32_t *cell; int len, ret; priv = dev_get_priv(dev); regmap = syscon_regmap_lookup_by_phandle(dev, "st,syscfg-holdboot"); if (IS_ERR(regmap)) { dev_err(dev, "unable to find holdboot regmap (%ld)\n", PTR_ERR(regmap)); return PTR_ERR(regmap); } cell = dev_read_prop(dev, "st,syscfg-holdboot", &len); if (len < 3 * sizeof(fdt32_t)) { dev_err(dev, "holdboot offset and mask not available\n"); return -EINVAL; } priv->hold_boot_regmap = regmap; priv->hold_boot_offset = fdtdec_get_number(cell + 1, 1); priv->hold_boot_mask = fdtdec_get_number(cell + 2, 1); ret = reset_get_by_index(dev, 0, &priv->reset_ctl); if (ret) { dev_err(dev, "failed to get reset (%d)\n", ret); return ret; } dev_dbg(dev, "probed\n"); return 0; } /** * stm32_copro_set_hold_boot() - Hold boot bit management * @dev: corresponding STM32 remote processor device * @hold: hold boot value * @return 0 if all went ok, else corresponding -ve error */ static int stm32_copro_set_hold_boot(struct udevice *dev, bool hold) { struct stm32_copro_privdata *priv; uint val; int ret; priv = dev_get_priv(dev); val = hold ? RCC_GCR_HOLD_BOOT : RCC_GCR_RELEASE_BOOT; /* * Note: shall run an SMC call (STM32_SMC_RCC) if platform is secured. * To be updated when the code for this SMC service is available which * is not the case for the time being. */ ret = regmap_update_bits(priv->hold_boot_regmap, priv->hold_boot_offset, priv->hold_boot_mask, val); if (ret) dev_err(dev, "failed to set hold boot\n"); return ret; } /** * stm32_copro_device_to_virt() - Convert device address to virtual address * @dev: corresponding STM32 remote processor device * @da: device address * @size: Size of the memory region @da is pointing to * @return converted virtual address */ static void *stm32_copro_device_to_virt(struct udevice *dev, ulong da, ulong size) { fdt32_t in_addr = cpu_to_be32(da), end_addr; u64 paddr; paddr = dev_translate_dma_address(dev, &in_addr); if (paddr == OF_BAD_ADDR) { dev_err(dev, "Unable to convert address %ld\n", da); return NULL; } end_addr = cpu_to_be32(da + size - 1); if (dev_translate_dma_address(dev, &end_addr) == OF_BAD_ADDR) { dev_err(dev, "Unable to convert address %ld\n", da + size - 1); return NULL; } return phys_to_virt(paddr); } /** * stm32_copro_load() - Loadup the STM32 remote processor * @dev: corresponding STM32 remote processor device * @addr: Address in memory where image is stored * @size: Size in bytes of the image * @return 0 if all went ok, else corresponding -ve error */ static int stm32_copro_load(struct udevice *dev, ulong addr, ulong size) { struct stm32_copro_privdata *priv; ulong rsc_table_size; int ret; priv = dev_get_priv(dev); ret = stm32_copro_set_hold_boot(dev, true); if (ret) return ret; ret = reset_assert(&priv->reset_ctl); if (ret) { dev_err(dev, "Unable to assert reset line (ret=%d)\n", ret); return ret; } if (rproc_elf32_load_rsc_table(dev, addr, size, &priv->rsc_table_addr, &rsc_table_size)) { priv->rsc_table_addr = 0; dev_warn(dev, "No valid resource table for this firmware\n"); } return rproc_elf32_load_image(dev, addr, size); } /** * stm32_copro_start() - Start the STM32 remote processor * @dev: corresponding STM32 remote processor device * @return 0 if all went ok, else corresponding -ve error */ static int stm32_copro_start(struct udevice *dev) { struct stm32_copro_privdata *priv; int ret; priv = dev_get_priv(dev); /* move hold boot from true to false start the copro */ ret = stm32_copro_set_hold_boot(dev, false); if (ret) return ret; /* * Once copro running, reset hold boot flag to avoid copro * rebooting autonomously */ ret = stm32_copro_set_hold_boot(dev, true); writel(ret ? TAMP_COPRO_STATE_OFF : TAMP_COPRO_STATE_CRUN, TAMP_COPRO_STATE); if (!ret) /* Store rsc_address in bkp register */ writel(priv->rsc_table_addr, TAMP_COPRO_RSC_TBL_ADDRESS); return ret; } /** * stm32_copro_reset() - Reset the STM32 remote processor * @dev: corresponding STM32 remote processor device * @return 0 if all went ok, else corresponding -ve error */ static int stm32_copro_reset(struct udevice *dev) { struct stm32_copro_privdata *priv; int ret; priv = dev_get_priv(dev); ret = stm32_copro_set_hold_boot(dev, true); if (ret) return ret; ret = reset_assert(&priv->reset_ctl); if (ret) { dev_err(dev, "Unable to assert reset line (ret=%d)\n", ret); return ret; } writel(TAMP_COPRO_STATE_OFF, TAMP_COPRO_STATE); return 0; } /** * stm32_copro_stop() - Stop the STM32 remote processor * @dev: corresponding STM32 remote processor device * @return 0 if all went ok, else corresponding -ve error */ static int stm32_copro_stop(struct udevice *dev) { return stm32_copro_reset(dev); } /** * stm32_copro_is_running() - Is the STM32 remote processor running * @dev: corresponding STM32 remote processor device * @return 0 if the remote processor is running, 1 otherwise */ static int stm32_copro_is_running(struct udevice *dev) { return (readl(TAMP_COPRO_STATE) == TAMP_COPRO_STATE_OFF); } static const struct dm_rproc_ops stm32_copro_ops = { .load = stm32_copro_load, .start = stm32_copro_start, .stop = stm32_copro_stop, .reset = stm32_copro_reset, .is_running = stm32_copro_is_running, .device_to_virt = stm32_copro_device_to_virt, }; static const struct udevice_id stm32_copro_ids[] = { {.compatible = "st,stm32mp1-m4"}, {} }; U_BOOT_DRIVER(stm32_copro) = { .name = "stm32_m4_proc", .of_match = stm32_copro_ids, .id = UCLASS_REMOTEPROC, .ops = &stm32_copro_ops, .probe = stm32_copro_probe, .priv_auto_alloc_size = sizeof(struct stm32_copro_privdata), };