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...