[PATCH v2 5/9] ram: octeon: Add MIPS Octeon3 DDR4 support (part 1/3)
Stefan Roese
sr at denx.de
Thu Aug 20 11:46:41 CEST 2020
Hi Daniel,
On 19.08.20 16:30, Daniel Schwierzeck wrote:
> Am Montag, den 17.08.2020, 14:12 +0200 schrieb Stefan Roese:
>> From: Aaron Williams <awilliams at marvell.com>
>>
>> This Octeon 3 DDR driver is ported from the 2013 Cavium / Marvell U-Boot
>> repository. It currently supports DDR4 on Octeon 3. It can be later
>> extended to support also DDR3 and Octeon 2 platforms.
>>
>> Part 1 adds the base U-Boot RAM driver, which will be instantiated by
>> the DT based probing.
>>
>> Signed-off-by: Aaron Williams <awilliams at marvell.com>
>> Signed-off-by: Stefan Roese <sr at denx.de>
>>
>> ---
>>
>> Changes in v2:
>> - Don't re-init after relocation
>> - Some unsupported Octeon families removed (only Octeon 2 & 3 supported
>> in general)
>>
>> drivers/ram/octeon/octeon_ddr.c | 2730 +++++++++++++++++++++++++++++++
>> 1 file changed, 2730 insertions(+)
>> create mode 100644 drivers/ram/octeon/octeon_ddr.c
>
> some general notes:
> - there are still some C++ style comments
Yes. Please see my comment from "[PATCH v2 4/9] mips: octeon: Add
octeon_ddr.h header" on this.
> - there are a lot of non-static functions, are them really used in
> other source files and why?
This separation into multiple files is historical. One reason for this
split is, that the file "octeon3_lmc.c" will not be not used by all
Octeon versions. It will be replaced (Makefile switch) by a different
driver file for other Octeon SoC's - once we get to supporting these.
> Of course you can split the code into
> multiple files but this file is the driver entrypoint and shouldn't
> export anything
I'm not sure, if I understand this logic fully. You want me to split
this file again, so that it only includes the (DM) driver entrypoint?
What's the reasoning for this?
>>
>> diff --git a/drivers/ram/octeon/octeon_ddr.c b/drivers/ram/octeon/octeon_ddr.c
>> new file mode 100644
>> index 0000000000..0b347b61fd
>> --- /dev/null
>> +++ b/drivers/ram/octeon/octeon_ddr.c
>> @@ -0,0 +1,2730 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2020 Marvell International Ltd.
>> + *
>> + * https://spdx.org/licenses
>
> this line is superfluous
Removed.
>> + */
>> +
>> +#include <common.h>
>
> try to avoid common.h in new code
Yes, sorry. I should have remembered this. But these changes started
before "common.h" got deprecated. I'll check all DDR files for this.
>> +#include <command.h>
>> +#include <dm.h>
>> +#include <hang.h>
>> +#include <i2c.h>
>> +#include <ram.h>
>> +#include <time.h>
>> +
>> +#include <asm/sections.h>
>> +#include <linux/io.h>
>> +
>> +#include <mach/octeon_ddr.h>
>
> do Octeon ARM and MIPS share the same DDR3/4 memory controller? If yes,
> would this driver support both archs and would ARM provide an own
> version of octeon_ddr.h?
No. The DDR3/4 code is *only* used by the "older" MIPS Octeon II/III.
The ARM Octeon TX variants have the DDR init code integrated in the
other binaries, like TF-A. So no code for newer SoC will even be
integrated into this code base.
<snip>
>> +static int octeon_ddr_probe(struct udevice *dev)
>> +{
>> + struct ddr_priv *priv = dev_get_priv(dev);
>> + struct ofnode_phandle_args l2c_node;
>> + struct ddr_conf *ddr_conf_ptr;
>> + u32 ddr_conf_valid_mask = 0;
>> + u32 measured_ddr_hertz = 0;
>> + int conf_table_count;
>> + int def_ddr_freq;
>> + u32 mem_mbytes = 0;
>> + u32 ddr_hertz;
>> + u32 ddr_ref_hertz;
>> + int alt_refclk;
>> + const char *eptr;
>> + fdt_addr_t addr;
>> + u64 *ptr;
>> + u64 val;
>> + int ret;
>> + int i;
>> +
>> + /* Don't try to re-init the DDR controller after relocation */
>> + if (gd->flags & GD_FLG_RELOC)
>> + return 0;
>> +
>> + /*
>> + * Dummy read all local variables into cache, so that they are
>> + * locked in cache when the DDR code runs with flushes etc enabled
>> + */
>> + ptr = (u64 *)_end;
>> + for (i = 0; i < (0x100000 / sizeof(u64)); i++)
>> + val = readq(ptr++);
>> +
>> + /*
>> + * The base addresses of LMC and L2C are read from the DT. This
>> + * makes it possible to use the DDR init code without the need
>> + * of the "node" variable, describing on which node to access. The
>> + * node number is already included implicitly in the base addresses
>> + * read from the DT this way.
>> + */
>> +
>> + /* Get LMC base address */
>> + priv->lmc_base = dev_remap_addr(dev);
>> + debug("%s: lmc_base=%p\n", __func__, priv->lmc_base);
>> +
>> + /* Get L2C base address */
>> + ret = dev_read_phandle_with_args(dev, "l2c-handle", NULL, 0, 0,
>> + &l2c_node);
>> + if (ret) {
>> + printf("Can't access L2C node!\n");
>> + return -ENODEV;
>> + }
>> +
>> + addr = ofnode_get_addr(l2c_node.node);
>> + if (addr == FDT_ADDR_T_NONE) {
>> + printf("Can't access L2C node!\n");
>> + return -ENODEV;
>> + }
>> +
>> + priv->l2c_base = map_physmem(addr, 0, MAP_NOCACHE);
>> + debug("%s: l2c_base=%p\n", __func__, priv->l2c_base);
>
> why not dev_remap_addr()?
I'm not sure, how I can access the "udev" necessary for this for the
L2C DT node:
l2c: l2c at 1180080000000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "cavium,octeon-7xxx-l2c";
reg = <0x11800 0x80000000 0x0 0x01000000>;
u-boot,dm-pre-reloc;
};
lmc: lmc at 1180088000000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "cavium,octeon-7xxx-ddr4";
reg = <0x11800 0x88000000 0x0 0x02000000>; // 2 IFs
u-boot,dm-pre-reloc;
l2c-handle = <&l2c>;
};
Can I read the mapped L2C controller address more elegant?
>> +
>> + ddr_conf_ptr = octeon_ddr_conf_table_get(&conf_table_count,
>> + &def_ddr_freq);
>> + if (!ddr_conf_ptr) {
>> + printf("ERROR: unable to determine DDR configuration\n");
>> + return -ENODEV;
>> + }
>> +
>> + for (i = 0; i < conf_table_count; i++) {
>> + if (ddr_conf_ptr[i].dimm_config_table[0].spd_addrs[0] ||
>> + ddr_conf_ptr[i].dimm_config_table[0].spd_ptrs[0])
>> + ddr_conf_valid_mask |= 1 << i;
>> + }
>> +
>> + /*
>> + * Check for special case of mismarked 3005 samples,
>> + * and adjust cpuid
>> + */
>> + alt_refclk = 0;
>> + ddr_hertz = def_ddr_freq * 1000000;
>> +
>> + eptr = env_get("ddr_clock_hertz");
>> + if (eptr) {
>> + ddr_hertz = simple_strtoul(eptr, NULL, 0);
>> + gd->mem_clk = divide_nint(ddr_hertz, 1000000);
>> + printf("Parameter found in environment. ddr_clock_hertz = %d\n",
>> + ddr_hertz);
>> + }
>
> who will set this in the environment and when? Shouldn't this be
> configurable via device-tree?
As mentioned before, I've ported all this from the original 2013 Octeon
U-Boot version with no functional change (intended). These env variables
are a tunining method (for testing purpose most likely), which I didn't
want to remove. So that the mainline DDR code does not lack any
functionality (features, tuning, test methods) that the original code
had.
I hope you understand this reasoning.
Thanks,
Stefan
>> +
>> + ddr_ref_hertz = octeon3_refclock(alt_refclk,
>> + ddr_hertz,
>> + &ddr_conf_ptr[0].dimm_config_table[0]);
>> +
>> + debug("Initializing DDR, clock = %uhz, reference = %uhz\n",
>> + ddr_hertz, ddr_ref_hertz);
>> +
>> + mem_mbytes = octeon_ddr_initialize(priv, gd->cpu_clk,
>> + ddr_hertz, ddr_ref_hertz,
>> + ddr_conf_valid_mask,
>> + ddr_conf_ptr, &measured_ddr_hertz);
>> + debug("Mem size in MBYTES: %u\n", mem_mbytes);
>> +
>> + gd->mem_clk = divide_nint(measured_ddr_hertz, 1000000);
>> +
>> + debug("Measured DDR clock %d Hz\n", measured_ddr_hertz);
>> +
>> + if (measured_ddr_hertz != 0) {
>> + if (!gd->mem_clk) {
>> + /*
>> + * If ddr_clock not set, use measured clock
>> + * and don't warn
>> + */
>> + gd->mem_clk = divide_nint(measured_ddr_hertz, 1000000);
>> + } else if ((measured_ddr_hertz > ddr_hertz + 3000000) ||
>> + (measured_ddr_hertz < ddr_hertz - 3000000)) {
>> + printf("\nWARNING:\n");
>> + printf("WARNING: Measured DDR clock mismatch! expected: %lld MHz, measured: %lldMHz, cpu clock: %lu MHz\n",
>> + divide_nint(ddr_hertz, 1000000),
>> + divide_nint(measured_ddr_hertz, 1000000),
>> + gd->cpu_clk);
>> + printf("WARNING:\n\n");
>> + gd->mem_clk = divide_nint(measured_ddr_hertz, 1000000);
>> + }
>> + }
>> +
>> + if (!mem_mbytes)
>> + return -ENODEV;
>> +
>> + priv->info.base = CONFIG_SYS_SDRAM_BASE;
>> + priv->info.size = MB(mem_mbytes);
>> +
>> + /*
>> + * For 6XXX generate a proper error when reading/writing
>> + * non-existent memory locations.
>> + */
>> + cvmx_l2c_set_big_size(priv, mem_mbytes, 0);
>> +
>> + debug("Ram size %uMiB\n", mem_mbytes);
>> +
>> + return 0;
>> +}
>> +
>> +static int octeon_get_info(struct udevice *dev, struct ram_info *info)
>> +{
>> + struct ddr_priv *priv = dev_get_priv(dev);
>> +
>> + *info = priv->info;
>> +
>> + return 0;
>> +}
>> +
>> +static struct ram_ops octeon_ops = {
>> + .get_info = octeon_get_info,
>> +};
>> +
>> +static const struct udevice_id octeon_ids[] = {
>> + {.compatible = "cavium,octeon-7xxx-ddr4" },
>> + { }
>> +};
>> +
>> +U_BOOT_DRIVER(octeon_ddr) = {
>> + .name = "octeon_ddr",
>> + .id = UCLASS_RAM,
>> + .of_match = octeon_ids,
>> + .ops = &octeon_ops,
>> + .probe = octeon_ddr_probe,
>> + .platdata_auto_alloc_size = sizeof(struct ddr_priv),
>> +};
Viele Grüße,
Stefan
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-51 Fax: (+49)-8142-66989-80 Email: sr at denx.de
More information about the U-Boot
mailing list