cancel
Showing results for 
Search instead for 
Did you mean: 

Cannot assign USART1 as stdout-path for U-Boot (Mickledore)

varuslan
Associate II

Custom board based on stm32mp151a. Migrating from kirkstone to mickledore and from SP_MIN to OPTEE. usart1 assigned for debuging, uart4 and uart7 for modbus communication.

I cannot boot uboot with usart1 assigned as stdout-path. It boots with uart4 and uart7, but not usart1. I thought it was some error with security context, but linux can use usart1 as stdout-path. Uboot(uart4) + Linux(usart1) works, but Uboot(usart1) + Linux(usart1) doesn't.

I tried to debug with early trace and lot's of printf. Uboot calls serial_init() (board_f.c) and ends up in device_probe() (device.c) and it never leaves device_probe(). The last debug message was from optee for rcc-reset and then it hangs indefinitely. I couldn't catch it in uboot so I assume it hangs in optee.

1 ACCEPTED SOLUTION

Accepted Solutions
HLee.21
Associate III

😉  had the same issue today, non-secure-world is not allowed to change that rcc reset register.

Please see my post here:

Issue with USART1 Reset in U-Boot on STM32MP15 with TZEN

and just have a look at TRM 10.7.107: "If TZEN = '1', this register can only be modified in secure mode."

View solution in original post

8 REPLIES 8
varuslan
Associate II

Optee and tf-a

varuslan
Associate II

I configured TF-A to UART4, Optee to UART4, Uboot to USART1 and added printf messages to uboot source code.

/* serial-uclass.c */

#if CONFIG_IS_ENABLED(SERIAL_PRESENT)
static int serial_check_stdout(const void *blob, struct udevice **devp)
{
	int node = -1;
	const char *str, *p, *name;
	int namelen;

	/* Check for a chosen console */
	str = fdtdec_get_chosen_prop(blob, "stdout-path");
	if (str) {
		p = strchr(str, ':');
		namelen = p ? p - str : strlen(str);
		node = fdt_path_offset_namelen(blob, str, namelen);

		if (node < 0) {
			/*
			 * Deal with things like
			 *	stdout-path = "serial0:115200n8";
			 *
			 * We need to look up the alias and then follow it to
			 * the correct node.
			 */
			name = fdt_get_alias_namelen(blob, str, namelen);
			if (name)
				node = fdt_path_offset(blob, name);
		}
	}

	if (node < 0)
		node = fdt_path_offset(blob, "console");
	if (!uclass_get_device_by_of_offset(UCLASS_SERIAL, node, devp))
		return 0;

	/*
	 * If the console is not marked to be bound before relocation, bind it
	 * anyway.
	 */
	if (node > 0 && !lists_bind_fdt(gd->dm_root, offset_to_ofnode(node),
					devp, NULL, false)) {
		if (device_get_uclass_id(*devp) == UCLASS_SERIAL) {
			printf("\nserial_check_stdout: probing %s\n", (*devp)->name);
			int result = !device_probe(*devp);
			printf("\nserial_check_stdout: finished probing %s\n", (*devp)->name);

			if (result)
				return 0;
		}
	}

	return -ENODEV;
}
/* device.c */

int device_probe(struct udevice *dev)
{
	const struct driver *drv;
	int ret;

	if (!dev)
		return -EINVAL;

	if (dev_get_flags(dev) & DM_FLAG_ACTIVATED)
		return 0;

	ret = device_notify(dev, EVT_DM_PRE_PROBE);
	if (ret)
		return ret;

	drv = dev->driver;
	assert(drv);

	printf("\ndevice_probe: probing %s\n", dev->name);

	ret = device_of_to_plat(dev);
	if (ret)
		goto fail;

	/* Ensure all parents are probed */
	if (dev->parent) {
		printf("\ndevice_probe: about to probe %s\n", dev->parent->name);
		ret = device_probe(dev->parent);
		printf("\ndevice_probe: probed %s, exit code %i\n", dev->parent->name, ret);
		if (ret)
			goto fail;

		/*
		 * The device might have already been probed during
		 * the call to device_probe() on its parent device
		 * (e.g. PCI bridge devices). Test the flags again
		 * so that we don't mess up the device.
		 */
		if (dev_get_flags(dev) & DM_FLAG_ACTIVATED)
			return 0;
	}

	dev_or_flags(dev, DM_FLAG_ACTIVATED);

	if (CONFIG_IS_ENABLED(POWER_DOMAIN) && dev->parent &&
	    (device_get_uclass_id(dev) != UCLASS_POWER_DOMAIN) &&
	    !(drv->flags & DM_FLAG_DEFAULT_PD_CTRL_OFF)) {
		ret = dev_power_domain_on(dev);
		if (ret)
			goto fail;
	}

	/*
	 * Process pinctrl for everything except the root device, and
	 * continue regardless of the result of pinctrl. Don't process pinctrl
	 * settings for pinctrl devices since the device may not yet be
	 * probed.
	 *
	 * This call can produce some non-intuitive results. For example, on an
	 * x86 device where dev is the main PCI bus, the pinctrl device may be
	 * child or grandchild of that bus, meaning that the child will be
	 * probed here. If the child happens to be the P2SB and the pinctrl
	 * device is a child of that, then both the pinctrl and P2SB will be
	 * probed by this call. This works because the DM_FLAG_ACTIVATED flag
	 * is set just above. However, the PCI bus' probe() method and
	 * associated uclass methods have not yet been called.
	 */
	if (dev->parent && device_get_uclass_id(dev) != UCLASS_PINCTRL) {
		ret = pinctrl_select_state(dev, "default");
		if (ret && ret != -ENOSYS)
			log_debug("Device '%s' failed to configure default pinctrl: %d (%s)\n",
				  dev->name, ret, errno_str(ret));
	}

	if (CONFIG_IS_ENABLED(IOMMU) && dev->parent &&
	    (device_get_uclass_id(dev) != UCLASS_IOMMU)) {
		ret = dev_iommu_enable(dev);
		if (ret)
			goto fail;
	}

	ret = device_get_dma_constraints(dev);
	if (ret)
		goto fail;

	ret = uclass_pre_probe_device(dev);
	if (ret)
		goto fail;

	if (dev->parent && dev->parent->driver->child_pre_probe) {
		ret = dev->parent->driver->child_pre_probe(dev);
		if (ret)
			goto fail;
	}

	/* Only handle devices that have a valid ofnode */
	if (dev_has_ofnode(dev)) {
		/*
		 * Process 'assigned-{clocks/clock-parents/clock-rates}'
		 * properties
		 */
		ret = clk_set_defaults(dev, CLK_DEFAULTS_PRE);
		if (ret)
			goto fail;
	}

	if (drv->probe) {
		ret = drv->probe(dev);
		if (ret)
			goto fail;
	}

	ret = uclass_post_probe_device(dev);
	if (ret)
		goto fail_uclass;

	if (dev->parent && device_get_uclass_id(dev) == UCLASS_PINCTRL) {
		ret = pinctrl_select_state(dev, "default");
		if (ret && ret != -ENOSYS)
			log_debug("Device '%s' failed to configure default pinctrl: %d (%s)\n",
				  dev->name, ret, errno_str(ret));
	}

	ret = device_notify(dev, EVT_DM_POST_PROBE);
	if (ret)
		return ret;

	printf("\ndevice_probe: probing %s finished with 0 code\n", dev->name);

	return 0;
fail_uclass:
	if (device_remove(dev, DM_REMOVE_NORMAL)) {
		dm_warn("%s: Device '%s' failed to remove on error path\n",
			__func__, dev->name);
	}
fail:
	dev_bic_flags(dev, DM_FLAG_ACTIVATED);

	device_free(dev);

	printf("\ndevice_probe: reached fail while probing %s\n", dev->name);

	return ret;
}

 

/* log.h */

//#ifdef LOG_DEBUG
#define _LOG_DEBUG	LOGL_FORCE_DEBUG
#ifndef DEBUG
#define DEBUG
#endif
//#else
//#define _LOG_DEBUG	0
//#endif

 

/* stm32mp15_defconfig */

CONFIG_LOG=y
CONFIG_LOG_CONSOLE=y
CONFIG_LOG_MAX_LEVEL=7
CONFIG_DEBUG_UART_BOARD_INIT=y
CONFIG_DEBUG_UART=y
CONFIG_DEBUG_UART_STM32=y
CONFIG_DEBUG_UART_CLOCK=64000000
CONFIG_DEBUG_UART_SKIP_INIT=y
CONFIG_TRACE_EARLY=y
CONFIG_CMD_TRACE=y

 

For log_uboot_uart4.txt I removed modifications in log.h because there was too much verbosity.

varuslan
Associate II

Log from linux with uboot assigned to uart4 and linux to usart1 

HLee.21
Associate III

😉  had the same issue today, non-secure-world is not allowed to change that rcc reset register.

Please see my post here:

Issue with USART1 Reset in U-Boot on STM32MP15 with TZEN

and just have a look at TRM 10.7.107: "If TZEN = '1', this register can only be modified in secure mode."

varuslan
Associate II

Yeah, I already tried scmi reset. 

&usart1 {
	clocks = <&scmi_clk CK_SCMI_USART1>;
	resets = <&scmi_reset RST_SCMI_USART1>;
};

But unfortunately that didn't wok in my case. Tried it again just now and still nothing.

I decompiled dtb just to be sure and it looks ok to me. 

serial@5c000000 {
	compatible = "st,stm32h7-uart";
	reg = <0x5c000000 0x400>;
	interrupts-extended = <0x09 0x1a 0x04>;
	clocks = <0x03 0x14>;
	wakeup-source;
	power-domains = <0x0a>;
	feature-domains = <0x1c 0x03>;
	status = "okay";
	pinctrl-names = "default\0idle\0sleep";
	pinctrl-0 = <0x45>;
	pinctrl-1 = <0x46>;
	pinctrl-2 = <0x47>;
	resets = <0x10 0x03>;
	phandle = <0xb5>;
};

 

Oh, never mind. I messed too much with my defconfig, so I had to fallback to default one. And now it works! Thanks!

varuslan
Associate II

Checked again. Previously I added resets to *-scmi.dtsi and that didn't work, but adding it to *-u-boot.dtsi works, because resets are being overridden in stm32mp15-scmi-u-boot.dtsi.

HLee.21
Associate III

😁let's see what we stumble upon next together, so funny that you also tried *-scmi.dtsi first...