[U-Boot] [PATCH v3 3/7] MSCC: add support for Ocelot SoCs

Gregory CLEMENT gregory.clement at bootlin.com
Thu Dec 13 14:05:21 UTC 2018


Hi Daniel,
 
 On lun., déc. 10 2018, Daniel Schwierzeck <daniel.schwierzeck at gmail.com> wrote:

>> diff --git a/arch/mips/mach-mscc/include/ioremap.h b/arch/mips/mach-mscc/include/ioremap.h
>> new file mode 100644
>> index 0000000000..8ea5c65ce3
>> --- /dev/null
>> +++ b/arch/mips/mach-mscc/include/ioremap.h
>> @@ -0,0 +1,51 @@
>> +/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
>
> this line should start with a //. There are more files in this patch
> which need to be fixed.

Actually, according to the documentation (Licenses/README):
  The SPDX license identifier is added in form of a comment.  The comment
   style depends on the file type::

      C source:	// SPDX-License-Identifier: <SPDX License Expression>
      C header:	/* SPDX-License-Identifier: <SPDX License Expression> */

So for a C header file, /* comment */ is correct.

>
>> +/*
>> + * Copyright (c) 2018 Microsemi Corporation
>> + */
>> +
>> +#ifndef __ASM_MACH_MSCC_IOREMAP_H
>> +#define __ASM_MACH_MSCC_IOREMAP_H
>> +
>> +#include <linux/types.h>
>> +#include <mach/common.h>
>> +
>> +/*
>> + * Allow physical addresses to be fixed up to help peripherals located
>> + * outside the low 32-bit range -- generic pass-through version.
>> + */
>> +static inline phys_addr_t fixup_bigphys_addr(phys_addr_t phys_addr,
>> +					     phys_addr_t size)
>> +{
>> +	return phys_addr;
>> +}
>> +
>> +static inline int is_vcoreiii_internal_registers(phys_addr_t offset)
>> +{
>> +#if defined(CONFIG_ARCH_MSCC)
>
> this define is superfluous because this directory is only added to the
> include paths when CONFIG_ARCH_MSCC is selected

OK

>
>> +	if ((offset >= MSCC_IO_ORIGIN1_OFFSET &&
>> +	     offset < (MSCC_IO_ORIGIN1_OFFSET + MSCC_IO_ORIGIN1_SIZE)) ||
>> +	    (offset >= MSCC_IO_ORIGIN2_OFFSET &&
>> +	     offset < (MSCC_IO_ORIGIN2_OFFSET + MSCC_IO_ORIGIN2_SIZE)))
>> +		return 1;
>> +#endif
>> +
>> +	return 0;
>> +}

[...]

>> +/*
>> + * DDR memory sanity checking failed, tally and do hard reset
>> + *
>> + * NB: Assumes inlining as no stack is available!
>> + */
>> +static inline void hal_vcoreiii_ddr_failed(void)
>> +{
>> +	register u32 reset;
>> +
>> +	writel(readl(BASE_CFG + ICPU_GPR(6)) + 1, BASE_CFG + ICPU_GPR(6));
>> +
>> +	clrbits_le32(BASE_DEVCPU_GCB + PERF_GPIO_OE, BIT(19));
>> +
>> +	/* Jump to reset - does not return */
>> +	reset = KSEG0ADDR(_machine_restart);
>> +	/* Reset while running from cache */
>> +	icache_lock((void *)reset, 128);
>> +	asm volatile ("jr %0"::"r" (reset));
>
> could you briefly describe the reason for this in a comment? It's not
> clear why this code is necessary without knowing the SoC. AFAIU from
> your last mail the boot SPI flash is mapped to KUSEG and you need to
> establish a TLB mapping in lowlevel_init() to be able to move to
> KSEG0.

The reboot workaround in _machine_restart() will change the SPI NOR
into SW bitbang.

This will render the CPU unable to execute directly from the NOR, which
is why the reset instructions are prefetched into the I-cache.

When failing the DDR initialization we are executing from NOR.

The last instruction in _machine_restart() will reset the MIPS CPU
(and the cache), and the CPU will start executing from the reset vactor.

I will add this explanation as comment.

>
>> +
>> +	panic("DDR init failed\n");
>> +}
>> +
>> +/*
>> + * DDR memory sanity checking done, possibly enable ECC.
>> + *
>> + * NB: Assumes inlining as no stack is available!
>> + */
>> +static inline void hal_vcoreiii_ddr_verified(void)
>> +{
>> +#ifdef MIPS_VCOREIII_MEMORY_ECC
>> +	/* Finally, enable ECC */
>> +	register u32 val = readl(BASE_CFG + ICPU_MEMCTRL_CFG);
>> +
>> +	val |= ICPU_MEMCTRL_CFG_DDR_ECC_ERR_ENA;
>> +	val &= ~ICPU_MEMCTRL_CFG_BURST_SIZE;
>> +
>> +	writel(val, BASE_CFG + ICPU_MEMCTRL_CFG);
>> +#endif
>> +
>> +	/* Reset Status register - sticky bits */
>> +	writel(readl(BASE_CFG + ICPU_MEMCTRL_STAT), BASE_CFG + ICPU_MEMCTRL_STAT);
>> +}
>> +
>> +/* NB: Assumes inlining as no stack is available! */
>> +static inline int look_for(u32 bytelane)
>> +{
>> +	register u32 i;
>> +
>> +	/* Reset FIFO in case any previous access failed */
>> +	for (i = 0; i < sizeof(training_data); i++) {
>> +		register u32 byte;
>> +
>> +		memphy_soft_reset();
>> +		/* Reset sticky bits */
>> +		writel(readl(BASE_CFG + ICPU_MEMCTRL_STAT),
>> +		       BASE_CFG + ICPU_MEMCTRL_STAT);
>> +		/* Read data */
>> +		byte = ((volatile u8 *)MSCC_DDR_TO)[bytelane + (i * 4)];
>
> __raw_readl()?

I had tried it before but without luck, but after trying harder this
time I managed to use read(b|l)/write(b|l) everywhere and get ride of
the volatile variable.

>
>> +		/*
>> +		 * Prevent the compiler reordering the instruction so
>> +		 * the read of RAM happens after the check of the
>> +		 * errors.
>> +		 */
>> +		asm volatile("" : : : "memory");
>
> this is available as barrier(). But according to context you could use
> rmb(). Anyway with the current volatile pointer or the suggested
> __raw_readl() the compiler shouldn't reorder at all

I had a close look on the code generating the __raw_readl and there is
nothing there to guaranty the ordering. Actually in our case (32 bits)
__read_readl is just:
static inline u32 __raw_readl(const volatile void __iomem *mem)
{
	u32 __val;

	__val = *mem;
	return __val;
}

initial code is here:
https://elixir.bootlin.com/u-boot/v2018.11-rc3/source/arch/mips/include/asm/io.h#L265
but __swizzle_addr_l() did nothing
https://elixir.bootlin.com/u-boot/v2018.11-rc3/source/arch/mips/include/asm/mach-generic/mangle-port.h#L10
same for __raw_ioswabl():
https://elixir.bootlin.com/u-boot/v2018.11-rc3/source/arch/mips/include/asm/io.h#L35

So the code is the same that we have written. I agree it is cleaner
to use __raw_readl but it doesn't add anything about the ordering.

It is the same for the use of the volatile, it ensures that the compiler
will always produce a operation to read the data in memory, but it is
not about ordering.

As you suggested I will use rmb();

>> +static inline int hal_vcoreiii_train_bytelane(u32 bytelane)
>> +{
>> +	register int res;
>> +	register u32 dqs_s;
>> +
>> +	set_dly(bytelane, 0);	// Start training at DQS=0
>
> no C++ style comments

OK

>
>> +	while ((res = look_for(bytelane)) == DDR_TRAIN_CONTINUE)
>> +		;
>> +	if (res != DDR_TRAIN_OK)
>> +		return res;
>> +
>> +	dqs_s = readl(BASE_CFG + ICPU_MEMCTRL_DQS_DLY(bytelane));
>> +	while ((res = look_past(bytelane)) == DDR_TRAIN_CONTINUE)
>> +		;
>> +	if (res != DDR_TRAIN_OK)
>> +		return res;
>> +	/* Reset FIFO - for good measure */
>> +	memphy_soft_reset();
>> +	/* Adjust to center [dqs_s;cur] */
>> +	center_dly(bytelane, dqs_s);
>> +	return DDR_TRAIN_OK;
>> +}
>> +
>> +/* This algorithm is converted from the TCL training algorithm used
>> + * during silicon simulation.
>> + * NB: Assumes inlining as no stack is available!
>> + */
>> +static inline int hal_vcoreiii_init_dqs(void)
>> +{
>> +#define MAX_DQS 32
>> +	register u32 i, j;
>> +
>> +	for (i = 0; i < MAX_DQS; i++) {
>> +		set_dly(0, i);	// Byte-lane 0
>
> no C++ style comments

OK

>
>> +		for (j = 0; j < MAX_DQS; j++) {
>> +			register u32 __attribute__ ((unused)) byte;
>
> why unused? If you really need it, you could use __maybe_unused

Because the purpose of this variable is just to access the memory, we
don't do nothing of the value read, and gcc complain about it. But as
you suggest I will use __maybe_unused.

>
>> +			set_dly(1, j);	// Byte-lane 1
>> +			/* Reset FIFO in case any previous access failed */
>> +			memphy_soft_reset();
>> +			writel(readl(BASE_CFG + ICPU_MEMCTRL_STAT),
>> +			       BASE_CFG + ICPU_MEMCTRL_STAT);
>> +			byte = ((volatile u8 *)MSCC_DDR_TO)[0];
>> +			byte = ((volatile u8 *)MSCC_DDR_TO)[1];
>> +			if (!(readl(BASE_CFG + ICPU_MEMCTRL_STAT) &
>> +			    (ICPU_MEMCTRL_STAT_RDATA_MASKED |
>> +			     ICPU_MEMCTRL_STAT_RDATA_DUMMY)))
>> +				return 0;
>> +		}
>> +	}
>> +	return -1;
>> +}
>> +
>> +static inline int dram_check(void)
>> +{
>> +#define DDR ((volatile u32 *) MSCC_DDR_TO)
>> +	register u32 i;
>> +
>> +	for (i = 0; i < 8; i++) {
>> +		DDR[i] = ~i;
>> +		if (DDR[i] != ~i)
>
> __raw_readl(), __raw_writel() and drop the explicit volatile?

Yes, as explain above, it s done now.

>> +
>> +/*
>> + * Target offset base(s)
>> + */
>> +#define MSCC_IO_ORIGIN1_OFFSET 0x70000000
>> +#define MSCC_IO_ORIGIN1_SIZE   0x00200000
>> +#define MSCC_IO_ORIGIN2_OFFSET 0x71000000
>> +#define MSCC_IO_ORIGIN2_SIZE   0x01000000
>> +#define BASE_CFG        ((void __iomem *)0x70000000)
>> +#define BASE_DEVCPU_GCB ((void __iomem *)0x71070000)
>
> Would it be possible on that SoC to define those register offsets as
> simple physical address and create the mapping when needed?
> For example:
>
> void foo()
> {
>     void __iomem *base_cfg = ioremap(BASE_CFG, ...);
>     writel(base_cfg + XXX, 0);
> }

Actually creating the mapping is just casting the physical address in an
(void __iomem *), see our plat_ioremap.

Calling ioremap in every function will just grow them with little
benefit.

If you really want it, what I could is sharing void __iomem *base_cfg
and void __iomem *base_devcpu_gcb at platform level, and initialize them
only once very early during the boot.


>> +LEAF(lowlevel_init)
>> +	/*
>> +	 * As we have no stack yet, we can assume the restricted
>> +	 * luxury of the sX-registers without saving them
>> +	 */
>> +	move	s0,ra
>> +
>> +	jal	vcoreiii_tlb_init
>> +	nop
>
> we use the same style as Linux MIPS where instructions in the delay slot
> should be indented by an extra space.

OK

Thanks,

Gregory

-- 
Gregory Clement, Bootlin
Embedded Linux and Kernel engineering
http://bootlin.com


More information about the U-Boot mailing list