From: Simon Glass <simon.glass@canonical.com> Fix several issues preventing ext4_fill_super() from completing: - Fix shrinker_alloc() to return a dummy shrinker instead of NULL - Fix getblk_unmovable() to use sb_getblk() instead of returning NULL - Fix mb_cache_create() to allocate a cache structure - Implement d_make_root() to create root dentry for mount - Add ext4_init_system_zone() call for block validity checking - Add global task_struct for journal_info consistency - Store super_block pointer after successful mount The filesystem now mounts successfully in read-only mode. Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- .pickman-history | 121 +++ arch/arm/dts/k3-am62a-ddr.dtsi | 7 +- arch/arm/dts/k3-j721s2-ddr.dtsi | 12 +- arch/arm/dts/k3-j784s4-ddr.dtsi | 24 +- arch/arm/mach-k3/Kconfig | 3 + arch/arm/mach-k3/Makefile | 2 +- arch/arm/mach-k3/am62px/am62p5_fdt.c | 83 ++ arch/arm/mach-k3/am62px/am62p5_init.c | 62 ++ .../arm/mach-k3/include/mach/am62p_hardware.h | 65 ++ arch/arm/mach-k3/include/mach/k3-ddr.h | 15 + arch/arm/mach-k3/k3-ddr.c | 72 ++ board/ti/am62ax/evm.c | 17 +- board/ti/am62px/evm.c | 17 +- board/ti/am62x/evm.c | 63 +- board/ti/am64x/evm.c | 73 +- board/ti/am65x/evm.c | 29 +- board/ti/common/Makefile | 1 + board/ti/common/k3-ddr.c | 33 + board/ti/common/k3-ddr.h | 12 + board/ti/j721e/evm.c | 29 +- board/ti/j721s2/evm.c | 35 +- board/ti/j722s/evm.c | 17 +- board/ti/j784s4/evm.c | 17 +- configs/am62ax_evm_a53_defconfig | 1 - configs/am62px_evm_a53_defconfig | 1 - configs/am62x_evm_a53_defconfig | 1 - configs/am62x_evm_r5_defconfig | 1 - configs/am64x_evm_a53_defconfig | 1 - configs/am64x_evm_r5_defconfig | 1 - configs/am65x_evm_a53_defconfig | 1 - configs/am65x_evm_r5_defconfig | 1 - configs/am65x_evm_r5_usbdfu_defconfig | 1 - configs/am65x_evm_r5_usbmsc_defconfig | 1 - configs/j7200_evm_a72_defconfig | 1 - configs/j721e_evm_a72_defconfig | 1 - configs/j721s2_evm_a72_defconfig | 1 - configs/j722s_evm_a53_defconfig | 1 - configs/j784s4_evm_a72_defconfig | 1 - drivers/ram/Kconfig | 10 + drivers/ram/k3-ddrss/k3-ddrss.c | 219 +++++- fs/ext4l/ext4_uboot.h | 21 +- fs/ext4l/interface.c | 24 +- fs/ext4l/stub.c | 23 +- fs/ext4l/support.c | 7 + include/linux/types.h | 22 + tools/buildman/func_test.py | 1 + tools/buildman/main.py | 4 +- tools/buildman/test_boards.py | 739 ++++++++++++++++++ tools/pickman/README.rst | 70 +- tools/pickman/agent.py | 69 +- tools/pickman/control.py | 73 ++ tools/pickman/ftest.py | 82 ++ 52 files changed, 1854 insertions(+), 334 deletions(-) create mode 100644 arch/arm/mach-k3/include/mach/k3-ddr.h create mode 100644 arch/arm/mach-k3/k3-ddr.c create mode 100644 board/ti/common/k3-ddr.c create mode 100644 board/ti/common/k3-ddr.h create mode 100644 tools/buildman/test_boards.py diff --git a/.pickman-history b/.pickman-history index 8c7190b8748..52d972c323d 100644 --- a/.pickman-history +++ b/.pickman-history @@ -1281,3 +1281,124 @@ The build completed successfully with no warnings or errors. --- +## 2025-12-21: us/next + +Branch: cherry-8ff96fb6d01 + +Commits: +- 8ff96fb6d01 arm: dts: k3-*-ddr: Add ss_cfg reg entry +- fed3b1eb9bf ram: k3-ddrss: Use the DDR controller BIST engine for ECC priming +- 22ce56a3ebd ram: k3-ddrss: Add k3_ddrss_ddr_bank_base_size_calc() to solve 'calculations restricted to 32 bits' issue +- 39d66893ef2 ram: k3-ddrss: Setup ECC region start and range +- 42957c3a8ba ram: k3-ddrss: Enable ECC interrupts +- 98dd3c126e3 drivers: ram: Kconfig: Add CONFIG_K3_INLINE_ECC +- 7a84969dcb8 ram: k3-ddrss: Remove 'ti,ecc-enable' support +- 01fa91bd5bb arm: mach-k3: Set NR_DRAM_BANKS to 2 +- bc07851897b board: ti: Pull redundant DDR functions to a common location and Fixup DDR size when ECC is enabled +- 08733bf3138 Merge patch series "Inline ECC Series" + +### Conversation log +The repository is clean. Now let me create and checkout the new branch based on ci/master: + +Now let me cherry-pick each commit in order. I'll start with the first one: + +Now for the merge commit - I need to use `-m 1 --allow-empty`: + +The merge commit is empty (which is expected since all changes were already applied). I need to commit it with --allow-empty to preserve the merge commit message: + +All 10 commits have been cherry-picked. Now let me verify with `git log --oneline -n 12`: + +All 10 commits are present. Now let me run the build verification: + +Excellent! The build completed successfully. Let me provide a final summary: + +## Cherry-pick Summary + +### Status: ✅ SUCCESS + +**Branch created:** `cherry-8ff96fb6d01` (based on `ci/master`) + +### Commits cherry-picked (10 total): + +| # | Original Hash | New Hash | Subject | +|---|---------------|----------|---------| +| 1 | 8ff96fb6d01 | 85552b44dad | arm: dts: k3-*-ddr: Add ss_cfg reg entry | +| 2 | fed3b1eb9bf | dd66f11e71a | ram: k3-ddrss: Use the DDR controller BIST engine for ECC priming | +| 3 | 22ce56a3ebd | 1717d982d29 | ram: k3-ddrss: Add k3_ddrss_ddr_bank_base_size_calc() to solve 'calculations restricted to 32 bits' issue | +| 4 | 39d66893ef2 | 3242b505bc9 | ram: k3-ddrss: Setup ECC region start and range | +| 5 | 42957c3a8ba | 781e6e0c5da | ram: k3-ddrss: Enable ECC interrupts | +| 6 | 98dd3c126e3 | a4fbb6d1d98 | drivers: ram: Kconfig: Add CONFIG_K3_INLINE_ECC | +| 7 | 7a84969dcb8 | dc4ba985ad9 | ram: k3-ddrss: Remove 'ti,ecc-enable' support | +| 8 | 01fa91bd5bb | 3ad4b71887d | arm: mach-k3: Set NR_DRAM_BANKS to 2 | +| 9 | bc07851897b | de285bc3a63 | board: ti: Pull redundant DDR functions to a common location and Fixup DDR size when ECC is enabled | +| 10 | 08733bf3138 | a95f049572f | Merge patch series "Inline ECC Series" (empty merge) | + +### Build Result: ✅ OK +- Board: sandbox +- Duration: 4 seconds +- No warnings or errors + +### Fix-ups Made: None +All commits applied cleanly with auto-merge. + +The branch `cherry-8ff96fb6d01` is ready for pushing. + +--- + +## 2025-12-21: us/next + +Branch: cherry-e44098d1aa5 + +Commits: +- e44098d1aa5 arm: mach-k3: am62p: Fixup CPU core, CAN-FD and Video-codec nodes in fdt +- a226a6422c9 arm: mach-k3: am62p: Fixup thermal zone critical points +- 8d05cbef73a arm: mach-k3: am62p: Fixup a53 max cpu frequency by speed-grade +- 969f43c563b arm: mach-k3: am62p: Set a53 cpu freq based on speed-grade +- e26a9ac4c69 Merge patch series "Add fdt-fixups for AM62P variants" + +### Conversation log +The repository is clean. Now let me create and checkout a new branch based on ci/master: + +Now let me cherry-pick the first commit: + +First commit successful. Now cherry-pick the second commit: + +Second commit successful. Now cherry-pick the third commit: + +Third commit successful. Now cherry-pick the fourth commit: + +Fourth commit successful. Now cherry-pick the fifth commit (the merge commit - needs -m 1 --allow-empty): + +The merge commit is empty (as expected since we already cherry-picked all the individual commits). Let me commit it with --allow-empty to preserve the merge commit message: + +All 5 commits have been cherry-picked. Let me verify with git log: + +All 5 commits are present. Now let me run the build verification: + +The build completed successfully. Let me provide the final summary: + +## Cherry-pick Summary + +### Commits Cherry-picked (5 total): +| Commit | Subject | +|--------|---------| +| 5f40cb915d8 | arm: mach-k3: am62p: Fixup CPU core, CAN-FD and Video-codec nodes in fdt | +| fec54c227bf | arm: mach-k3: am62p: Fixup thermal zone critical points | +| 02156be5da9 | arm: mach-k3: am62p: Fixup a53 max cpu frequency by speed-grade | +| f1ec3f59e22 | arm: mach-k3: am62p: Set a53 cpu freq based on speed-grade | +| 42106518f28 | Merge patch series "Add fdt-fixups for AM62P variants" | + +### Build Result: ✅ OK +- Build completed successfully with no warnings or errors +- Duration: 4 seconds +- Board: sandbox + +### Fix-ups Made: None +- All commits applied cleanly without conflicts + +### Branch Status: +- Branch `cherry-e44098d1aa5` is ready for pushing +- Currently ahead of `ci/master` by 5 commits + +--- + diff --git a/arch/arm/dts/k3-am62a-ddr.dtsi b/arch/arm/dts/k3-am62a-ddr.dtsi index 8629ea45b84..42e41f78505 100644 --- a/arch/arm/dts/k3-am62a-ddr.dtsi +++ b/arch/arm/dts/k3-am62a-ddr.dtsi @@ -4,11 +4,12 @@ */ / { - memorycontroller: memory-controller@f308000 { + memorycontroller: memory-controller@f300000 { compatible = "ti,am62a-ddrss"; reg = <0x00 0x0f308000 0x00 0x4000>, - <0x00 0x43014000 0x00 0x100>; - reg-names = "cfg", "ctrl_mmr_lp4"; + <0x00 0x43014000 0x00 0x100>, + <0x00 0x0f300000 0x00 0x200>; + reg-names = "cfg", "ctrl_mmr_lp4", "ss_cfg"; ti,ddr-freq1 = <DDRSS_PLL_FREQUENCY_1>; ti,ddr-freq2 = <DDRSS_PLL_FREQUENCY_2>; ti,ddr-fhs-cnt = <DDRSS_PLL_FHS_CNT>; diff --git a/arch/arm/dts/k3-j721s2-ddr.dtsi b/arch/arm/dts/k3-j721s2-ddr.dtsi index 345e2b84f9e..9764085163c 100644 --- a/arch/arm/dts/k3-j721s2-ddr.dtsi +++ b/arch/arm/dts/k3-j721s2-ddr.dtsi @@ -5,6 +5,8 @@ &main_navss { ranges = <0x00 0x00114000 0x00 0x00114000 0x00 0x00000100>, // ctrl_mmr_lpr + <0x00 0x02980000 0x00 0x02980000 0x00 0x00000200>, // ss cfg 0 + <0x00 0x029a0000 0x00 0x029a0000 0x00 0x00000200>, // ss cfg 1 <0x00 0x02990000 0x00 0x02990000 0x00 0x00004000>, // ddr0 cfg <0x00 0x029b0000 0x00 0x029b0000 0x00 0x00004000>, // ddr1 cfg <0x00 0x30000000 0x00 0x30000000 0x00 0x0c400000>; @@ -24,8 +26,9 @@ memorycontroller0: memorycontroller@2990000 { compatible = "ti,j721s2-ddrss"; reg = <0x0 0x02990000 0x0 0x4000>, - <0x0 0x0114000 0x0 0x100>; - reg-names = "cfg", "ctrl_mmr_lp4"; + <0x0 0x0114000 0x0 0x100>, + <0x0 0x02980000 0x0 0x200>; + reg-names = "cfg", "ctrl_mmr_lp4", "ss_cfg"; power-domains = <&k3_pds 138 TI_SCI_PD_SHARED>, <&k3_pds 96 TI_SCI_PD_SHARED>; clocks = <&k3_clks 138 0>, <&k3_clks 43 2>; @@ -2232,8 +2235,9 @@ memorycontroller1: memorycontroller@29b0000 { compatible = "ti,j721s2-ddrss"; reg = <0x0 0x029b0000 0x0 0x4000>, - <0x0 0x0114000 0x0 0x100>; - reg-names = "cfg", "ctrl_mmr_lp4"; + <0x0 0x0114000 0x0 0x100>, + <0x0 0x029a0000 0x0 0x200>; + reg-names = "cfg", "ctrl_mmr_lp4", "ss_cfg"; power-domains = <&k3_pds 139 TI_SCI_PD_SHARED>, <&k3_pds 97 TI_SCI_PD_SHARED>; clocks = <&k3_clks 139 0>, <&k3_clks 43 2>; diff --git a/arch/arm/dts/k3-j784s4-ddr.dtsi b/arch/arm/dts/k3-j784s4-ddr.dtsi index 1c3242b0870..fc74c539331 100644 --- a/arch/arm/dts/k3-j784s4-ddr.dtsi +++ b/arch/arm/dts/k3-j784s4-ddr.dtsi @@ -9,6 +9,10 @@ <0x00 0x029b0000 0x00 0x029b0000 0x00 0x00004000>, // ddr1 cfg <0x00 0x029d0000 0x00 0x029d0000 0x00 0x00004000>, // ddr2 cfg <0x00 0x029f0000 0x00 0x029f0000 0x00 0x00004000>, // ddr3 cfg + <0x00 0x02980000 0x00 0x02980000 0x00 0x00000200>, // ss cfg 0 + <0x00 0x029a0000 0x00 0x029a0000 0x00 0x00000200>, // ss cfg 1 + <0x00 0x029c0000 0x00 0x029c0000 0x00 0x00000200>, // ss cfg 2 + <0x00 0x029e0000 0x00 0x029e0000 0x00 0x00000200>, // ss cfg 3 <0x00 0x30000000 0x00 0x30000000 0x00 0x0c400000>; msmc0: msmc { @@ -26,8 +30,9 @@ memorycontroller0: memorycontroller@2990000 { compatible = "ti,j721s2-ddrss"; reg = <0x0 0x02990000 0x0 0x4000>, - <0x0 0x0114000 0x0 0x100>; - reg-names = "cfg", "ctrl_mmr_lp4"; + <0x0 0x0114000 0x0 0x100>, + <0x0 0x02980000 0x0 0x200>; + reg-names = "cfg", "ctrl_mmr_lp4", "ss_cfg"; power-domains = <&k3_pds 191 TI_SCI_PD_SHARED>, <&k3_pds 131 TI_SCI_PD_SHARED>; clocks = <&k3_clks 191 1>, <&k3_clks 78 2>; @@ -2234,8 +2239,9 @@ memorycontroller1: memorycontroller@29b0000 { compatible = "ti,j721s2-ddrss"; reg = <0x0 0x029b0000 0x0 0x4000>, - <0x0 0x0114000 0x0 0x100>; - reg-names = "cfg", "ctrl_mmr_lp4"; + <0x0 0x0114000 0x0 0x100>, + <0x0 0x029a0000 0x0 0x200>; + reg-names = "cfg", "ctrl_mmr_lp4", "ss_cfg"; power-domains = <&k3_pds 192 TI_SCI_PD_SHARED>, <&k3_pds 132 TI_SCI_PD_SHARED>; clocks = <&k3_clks 192 1>, <&k3_clks 78 2>; @@ -4442,8 +4448,9 @@ memorycontroller2: memorycontroller@29d0000 { compatible = "ti,j721s2-ddrss"; reg = <0x0 0x029d0000 0x0 0x4000>, - <0x0 0x0114000 0x0 0x100>; - reg-names = "cfg", "ctrl_mmr_lp4"; + <0x0 0x0114000 0x0 0x100>, + <0x0 0x029c0000 0x0 0x200>; + reg-names = "cfg", "ctrl_mmr_lp4", "ss_cfg"; power-domains = <&k3_pds 193 TI_SCI_PD_SHARED>, <&k3_pds 133 TI_SCI_PD_SHARED>; clocks = <&k3_clks 193 1>, <&k3_clks 78 2>; @@ -6650,8 +6657,9 @@ memorycontroller3: memorycontroller@29f0000 { compatible = "ti,j721s2-ddrss"; reg = <0x0 0x029f0000 0x0 0x4000>, - <0x0 0x0114000 0x0 0x100>; - reg-names = "cfg", "ctrl_mmr_lp4"; + <0x0 0x0114000 0x0 0x100>, + <0x0 0x29e0000 0x0 0x200>; + reg-names = "cfg", "ctrl_mmr_lp4", "ss_cfg"; power-domains = <&k3_pds 194 TI_SCI_PD_SHARED>, <&k3_pds 139 TI_SCI_PD_SHARED>; clocks = <&k3_clks 194 1>, <&k3_clks 78 2>; diff --git a/arch/arm/mach-k3/Kconfig b/arch/arm/mach-k3/Kconfig index f3f42b39213..a3ac490f677 100644 --- a/arch/arm/mach-k3/Kconfig +++ b/arch/arm/mach-k3/Kconfig @@ -156,6 +156,9 @@ config K3_X509_SWRV help SWRV for X509 certificate used for boot images +config NR_DRAM_BANKS + default 2 + if CPU_V7R source "arch/arm/mach-k3/r5/Kconfig" endif diff --git a/arch/arm/mach-k3/Makefile b/arch/arm/mach-k3/Makefile index 8c4f6786a5b..5ce7fc62d80 100644 --- a/arch/arm/mach-k3/Makefile +++ b/arch/arm/mach-k3/Makefile @@ -6,7 +6,7 @@ obj-$(CONFIG_ARM64) += arm64/ obj-$(CONFIG_CPU_V7R) += r5/ obj-$(CONFIG_OF_LIBFDT) += common_fdt.o -obj-y += common.o security.o +obj-y += common.o security.o k3-ddr.o obj-$(CONFIG_SOC_K3_AM62A7) += am62ax/ obj-$(CONFIG_SOC_K3_AM62P5) += am62px/ obj-$(CONFIG_SOC_K3_AM625) += am62x/ diff --git a/arch/arm/mach-k3/am62px/am62p5_fdt.c b/arch/arm/mach-k3/am62px/am62p5_fdt.c index 29c832d28ac..2c40fa5a594 100644 --- a/arch/arm/mach-k3/am62px/am62p5_fdt.c +++ b/arch/arm/mach-k3/am62px/am62p5_fdt.c @@ -7,8 +7,91 @@ #include "../common_fdt.h" #include <fdt_support.h> +static void fdt_fixup_cores_wdt_nodes_am62p(void *blob, int core_nr) +{ + char node_path[32]; + + if (core_nr < 1) + return; + + for (; core_nr < 4; core_nr++) { + snprintf(node_path, sizeof(node_path), "/cpus/cpu@%d", core_nr); + fdt_del_node_path(blob, node_path); + snprintf(node_path, sizeof(node_path), "/cpus/cpu-map/cluster0/core%d", core_nr); + fdt_del_node_path(blob, node_path); + snprintf(node_path, sizeof(node_path), "/bus@f0000/watchdog@e0%d0000", core_nr); + fdt_del_node_path(blob, node_path); + } +} + +static void fdt_fixup_video_codec_nodes_am62p(void *blob, bool has_video_codec) +{ + if (!has_video_codec) + fdt_del_node_path(blob, "/bus@f0000/video-codec@30210000"); +} + +static void fdt_fixup_canfd_nodes_am62p(void *blob, bool has_canfd) +{ + if (!has_canfd) { + fdt_del_node_path(blob, "/bus@f0000/can@20701000"); + fdt_del_node_path(blob, "/bus@f0000/can@20711000"); + } +} + +static int fdt_fixup_trips_node(void *blob, int zoneoffset, int maxc) +{ + int node, trip; + + node = fdt_subnode_offset(blob, zoneoffset, "trips"); + if (node < 0) + return -1; + + fdt_for_each_subnode(trip, blob, node) { + const char *type = fdt_getprop(blob, trip, "type", NULL); + + if (!type || (strncmp(type, "critical", 8) != 0)) + continue; + + if (fdt_setprop_u32(blob, trip, "temperature", 1000 * maxc) < 0) + return -1; + } + + return 0; +} + +static void fdt_fixup_thermal_zone_nodes_am62p(void *blob, int maxc) +{ + int node, zone; + + node = fdt_path_offset(blob, "/thermal-zones"); + if (node < 0) + return; + + fdt_for_each_subnode(zone, blob, node) { + if (fdt_fixup_trips_node(blob, zone, maxc) < 0) + printf("Failed to set temperature in %s critical trips\n", + fdt_get_name(blob, zone, NULL)); + } +} + +static void fdt_fixup_cpu_freq_nodes_am62p(void *blob, int max_freq) +{ + if (max_freq >= 1250000000) + return; + + if (max_freq <= 1000000000) { + fdt_del_node_path(blob, "/opp-table/opp-1250000000"); + fdt_del_node_path(blob, "/opp-table/opp-1400000000"); + } +} + int ft_system_setup(void *blob, struct bd_info *bd) { + fdt_fixup_cores_wdt_nodes_am62p(blob, k3_get_core_nr()); + fdt_fixup_video_codec_nodes_am62p(blob, k3_has_video_codec()); + fdt_fixup_canfd_nodes_am62p(blob, k3_has_canfd()); + fdt_fixup_thermal_zone_nodes_am62p(blob, k3_get_max_temp()); + fdt_fixup_cpu_freq_nodes_am62p(blob, k3_get_a53_max_frequency()); fdt_fixup_reserved(blob, "tfa", CONFIG_K3_ATF_LOAD_ADDR, 0x80000); fdt_fixup_reserved(blob, "optee", CONFIG_K3_OPTEE_LOAD_ADDR, 0x1800000); diff --git a/arch/arm/mach-k3/am62px/am62p5_init.c b/arch/arm/mach-k3/am62px/am62p5_init.c index 34ed01cd78c..74631a4ce93 100644 --- a/arch/arm/mach-k3/am62px/am62p5_init.c +++ b/arch/arm/mach-k3/am62px/am62p5_init.c @@ -11,10 +11,14 @@ #include <dm.h> #include <dm/uclass-internal.h> #include <dm/pinctrl.h> +#include <dm/ofnode.h> #include "../sysfw-loader.h" #include "../common.h" +/* TISCI DEV ID for A53 Clock */ +#define AM62PX_DEV_A53SS0_CORE_0_DEV_ID 135 + struct fwl_data cbass_main_fwls[] = { { "FSS_DAT_REG3", 7, 8 }, }; @@ -67,6 +71,62 @@ static void ctrl_mmr_unlock(void) mmr_unlock(PADCFG_MMR1_BASE, 1); } +#if CONFIG_IS_ENABLED(OF_CONTROL) +static int get_a53_cpu_clock_index(ofnode node) +{ + int count, i; + struct ofnode_phandle_args *args; + ofnode clknode; + + clknode = ofnode_path("/bus@f0000/system-controller@44043000/clock-controller"); + if (!ofnode_valid(clknode)) + return -1; + + count = ofnode_count_phandle_with_args(node, "assigned-clocks", "#clock-cells", 0); + + for (i = 0; i < count; i++) { + if (!ofnode_parse_phandle_with_args(node, "assigned-clocks", + "#clock-cells", 0, i, args)) { + if (ofnode_equal(clknode, args->node) && + args->args[0] == AM62PX_DEV_A53SS0_CORE_0_DEV_ID) + return i; + } + } + + return -1; +} + +static void fixup_a53_cpu_freq_by_speed_grade(void) +{ + int index, size; + u32 *rates; + ofnode node; + + node = ofnode_path("/a53@0"); + if (!ofnode_valid(node)) + return; + + rates = fdt_getprop_w(ofnode_to_fdt(node), ofnode_to_offset(node), + "assigned-clock-rates", &size); + + index = get_a53_cpu_clock_index(node); + + if (!rates || index < 0 || index >= (size / sizeof(u32))) { + printf("Wrong A53 assigned-clocks configuration\n"); + return; + } + + rates[index] = cpu_to_fdt32(k3_get_a53_max_frequency()); + + printf("Changed A53 CPU frequency to %dHz (%c grade) in DT\n", + k3_get_a53_max_frequency(), k3_get_speed_grade()); +} +#else +static void fixup_a53_cpu_freq_by_speed_grade(void) +{ +} +#endif + void board_init_f(ulong dummy) { struct udevice *dev; @@ -160,6 +220,8 @@ void board_init_f(ulong dummy) spl_enable_cache(); debug("am62px_init: %s done\n", __func__); + + fixup_a53_cpu_freq_by_speed_grade(); } u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device) diff --git a/arch/arm/mach-k3/include/mach/am62p_hardware.h b/arch/arm/mach-k3/include/mach/am62p_hardware.h index 923466c41f4..95af5c5c547 100644 --- a/arch/arm/mach-k3/include/mach/am62p_hardware.h +++ b/arch/arm/mach-k3/include/mach/am62p_hardware.h @@ -19,6 +19,22 @@ #define MCU_CTRL_MMR0_BASE 0x04500000 #define WKUP_CTRL_MMR0_BASE 0x43000000 +#define CTRLMMR_WKUP_JTAG_DEVICE_ID (WKUP_CTRL_MMR0_BASE + 0x18) +#define JTAG_DEV_CORE_NR_MASK GENMASK(19, 18) +#define JTAG_DEV_CORE_NR_SHIFT 18 +#define JTAG_DEV_CANFD_MASK BIT(15) +#define JTAG_DEV_CANFD_SHIFT 15 +#define JTAG_DEV_VIDEO_CODEC_MASK BIT(14) +#define JTAG_DEV_VIDEO_CODEC_SHIFT 14 +#define JTAG_DEV_SPEED_MASK GENMASK(10, 6) +#define JTAG_DEV_SPEED_SHIFT 6 +#define JTAG_DEV_TEMP_MASK GENMASK(5, 3) +#define JTAG_DEV_TEMP_SHIFT 3 + +#define JTAG_DEV_TEMP_AUTOMOTIVE 0x5 +#define JTAG_DEV_TEMP_EXTENDED_VALUE 105 +#define JTAG_DEV_TEMP_AUTOMOTIVE_VALUE 125 + #define CTRLMMR_MAIN_DEVSTAT (WKUP_CTRL_MMR0_BASE + 0x30) #define MAIN_DEVSTAT_PRIMARY_BOOTMODE_MASK GENMASK(6, 3) #define MAIN_DEVSTAT_PRIMARY_BOOTMODE_SHIFT 3 @@ -72,6 +88,55 @@ #define TI_SRAM_SCRATCH_BOARD_EEPROM_START 0x43c30000 +static inline int k3_get_core_nr(void) +{ + u32 dev_id = readl(CTRLMMR_WKUP_JTAG_DEVICE_ID); + + return ((dev_id & JTAG_DEV_CORE_NR_MASK) >> JTAG_DEV_CORE_NR_SHIFT) + 1; +} + +static inline int k3_has_video_codec(void) +{ + u32 dev_id = readl(CTRLMMR_WKUP_JTAG_DEVICE_ID); + + return !((dev_id & JTAG_DEV_VIDEO_CODEC_MASK) >> JTAG_DEV_VIDEO_CODEC_SHIFT); +} + +static inline int k3_has_canfd(void) +{ + u32 dev_id = readl(CTRLMMR_WKUP_JTAG_DEVICE_ID); + + return (dev_id & JTAG_DEV_CANFD_MASK) >> JTAG_DEV_CANFD_SHIFT; +} + +static inline int k3_get_max_temp(void) +{ + u32 dev_id = readl(CTRLMMR_WKUP_JTAG_DEVICE_ID); + u32 dev_temp = (dev_id & JTAG_DEV_TEMP_MASK) >> JTAG_DEV_TEMP_SHIFT; + + if (dev_temp == JTAG_DEV_TEMP_AUTOMOTIVE) + return JTAG_DEV_TEMP_AUTOMOTIVE_VALUE; + else + return JTAG_DEV_TEMP_EXTENDED_VALUE; +} + +static inline char k3_get_speed_grade(void) +{ + u32 dev_id = readl(CTRLMMR_WKUP_JTAG_DEVICE_ID); + u32 speed_grade = (dev_id & JTAG_DEV_SPEED_MASK) >> + JTAG_DEV_SPEED_SHIFT; + + return 'A' - 1 + speed_grade; +} + +static inline int k3_get_a53_max_frequency(void) +{ + if (k3_get_speed_grade() == 'O') + return 1000000000; + else + return 1250000000; +} + #if defined(CONFIG_SYS_K3_SPL_ATF) && !defined(__ASSEMBLY__) static const u32 put_device_ids[] = {}; diff --git a/arch/arm/mach-k3/include/mach/k3-ddr.h b/arch/arm/mach-k3/include/mach/k3-ddr.h new file mode 100644 index 00000000000..95496e1c59d --- /dev/null +++ b/arch/arm/mach-k3/include/mach/k3-ddr.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2024, Texas Instruments Incorporated - https://www.ti.com/ + */ + +#ifndef _K3_DDR_H_ +#define _K3_DDR_H_ + +int dram_init(void); +int dram_init_banksize(void); + +void fixup_ddr_driver_for_ecc(struct spl_image_info *spl_image); +void fixup_memory_node(struct spl_image_info *spl_image); + +#endif /* _K3_DDR_H_ */ diff --git a/arch/arm/mach-k3/k3-ddr.c b/arch/arm/mach-k3/k3-ddr.c new file mode 100644 index 00000000000..6e3e60cdc86 --- /dev/null +++ b/arch/arm/mach-k3/k3-ddr.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2024, Texas Instruments Incorporated - https://www.ti.com/ + */ + +#include <fdt_support.h> +#include <dm/uclass.h> +#include <k3-ddrss.h> +#include <spl.h> + +#include <asm/arch/k3-ddr.h> + +__weak int dram_init(void) +{ + return 0; +} + +__weak int dram_init_banksize(void) +{ + return 0; +} + +#if defined(CONFIG_SPL_BUILD) +void fixup_ddr_driver_for_ecc(struct spl_image_info *spl_image) +{ + struct udevice *dev; + int ret, ctr = 1; + + dram_init_banksize(); + + ret = uclass_get_device(UCLASS_RAM, 0, &dev); + if (ret) + panic("Cannnot get RAM device for ddr size fixup: %d\n", ret); + + ret = k3_ddrss_ddr_fdt_fixup(dev, spl_image->fdt_addr, gd->bd); + if (ret) + printf("Error fixing up ddr node for ECC use! %d\n", ret); + + ret = uclass_next_device_err(&dev); + + while (ret && ret != -ENODEV) { + ret = k3_ddrss_ddr_fdt_fixup(dev, spl_image->fdt_addr, gd->bd); + if (ret) + printf("Error fixing up ddr node %d for ECC use! %d\n", ctr, ret); + + ret = uclass_next_device_err(&dev); + ctr++; + } +} + +void fixup_memory_node(struct spl_image_info *spl_image) +{ + u64 start[CONFIG_NR_DRAM_BANKS]; + u64 size[CONFIG_NR_DRAM_BANKS]; + int bank; + int ret; + + dram_init(); + dram_init_banksize(); + + for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) { + start[bank] = gd->bd->bi_dram[bank].start; + size[bank] = gd->bd->bi_dram[bank].size; + } + + ret = fdt_fixup_memory_banks(spl_image->fdt_addr, start, size, + CONFIG_NR_DRAM_BANKS); + + if (ret) + printf("Error fixing up memory node! %d\n", ret); +} +#endif diff --git a/board/ti/am62ax/evm.c b/board/ti/am62ax/evm.c index 62d3664936e..3351544c5b3 100644 --- a/board/ti/am62ax/evm.c +++ b/board/ti/am62ax/evm.c @@ -12,6 +12,7 @@ #include <env.h> #include <fdt_support.h> #include <spl.h> +#include <asm/arch/k3-ddr.h> #include "../common/fdt_ops.h" @@ -20,15 +21,17 @@ int board_init(void) return 0; } -int dram_init(void) +#if defined(CONFIG_XPL_BUILD) +void spl_perform_fixups(struct spl_image_info *spl_image) { - return fdtdec_setup_mem_size_base(); -} - -int dram_init_banksize(void) -{ - return fdtdec_setup_memory_banksize(); + if (IS_ENABLED(CONFIG_K3_DDRSS)) { + if (IS_ENABLED(CONFIG_K3_INLINE_ECC)) + fixup_ddr_driver_for_ecc(spl_image); + } else { + fixup_memory_node(spl_image); + } } +#endif #ifdef CONFIG_BOARD_LATE_INIT int board_late_init(void) diff --git a/board/ti/am62px/evm.c b/board/ti/am62px/evm.c index 7362fa4520a..75359fa1614 100644 --- a/board/ti/am62px/evm.c +++ b/board/ti/am62px/evm.c @@ -13,6 +13,7 @@ #include <env.h> #include <fdt_support.h> #include <spl.h> +#include <asm/arch/k3-ddr.h> #include "../common/fdt_ops.h" struct efi_fw_image fw_images[] = { @@ -53,15 +54,17 @@ int board_init(void) return 0; } -int dram_init(void) +#if defined(CONFIG_XPL_BUILD) +void spl_perform_fixups(struct spl_image_info *spl_image) { - return fdtdec_setup_mem_size_base(); -} - -int dram_init_banksize(void) -{ - return fdtdec_setup_memory_banksize(); + if (IS_ENABLED(CONFIG_K3_DDRSS)) { + if (IS_ENABLED(CONFIG_K3_INLINE_ECC)) + fixup_ddr_driver_for_ecc(spl_image); + } else { + fixup_memory_node(spl_image); + } } +#endif #if IS_ENABLED(CONFIG_BOARD_LATE_INIT) int board_late_init(void) diff --git a/board/ti/am62x/evm.c b/board/ti/am62x/evm.c index 9075df01cac..279ceba9554 100644 --- a/board/ti/am62x/evm.c +++ b/board/ti/am62x/evm.c @@ -20,6 +20,7 @@ #include <asm/io.h> #include <asm/arch/hardware.h> #include <dm/uclass.h> +#include <asm/arch/k3-ddr.h> #include "../common/fdt_ops.h" @@ -86,11 +87,6 @@ int board_init(void) return 0; } -int dram_init(void) -{ - return fdtdec_setup_mem_size_base(); -} - #ifdef CONFIG_BOARD_LATE_INIT int board_late_init(void) { @@ -99,13 +95,7 @@ int board_late_init(void) } #endif -int dram_init_banksize(void) -{ - return fdtdec_setup_memory_banksize(); -} - #if defined(CONFIG_XPL_BUILD) - void spl_board_init(void) { enable_caches(); @@ -114,53 +104,14 @@ void spl_board_init(void) } -#if defined(CONFIG_K3_AM64_DDRSS) -static void fixup_ddr_driver_for_ecc(struct spl_image_info *spl_image) -{ - struct udevice *dev; - int ret; - - dram_init_banksize(); - - ret = uclass_get_device(UCLASS_RAM, 0, &dev); - if (ret) - panic("Cannot get RAM device for ddr size fixup: %d\n", ret); - - ret = k3_ddrss_ddr_fdt_fixup(dev, spl_image->fdt_addr, gd->bd); - if (ret) - printf("Error fixing up ddr node for ECC use! %d\n", ret); -} -#else -static void fixup_memory_node(struct spl_image_info *spl_image) -{ - u64 start[CONFIG_NR_DRAM_BANKS]; - u64 size[CONFIG_NR_DRAM_BANKS]; - int bank; - int ret; - - dram_init(); - dram_init_banksize(); - - for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) { - start[bank] = gd->bd->bi_dram[bank].start; - size[bank] = gd->bd->bi_dram[bank].size; - } - - /* dram_init functions use SPL fdt, and we must fixup u-boot fdt */ - ret = fdt_fixup_memory_banks(spl_image->fdt_addr, start, size, - CONFIG_NR_DRAM_BANKS); - if (ret) - printf("Error fixing up memory node! %d\n", ret); -} -#endif - void spl_perform_fixups(struct spl_image_info *spl_image) { -#if defined(CONFIG_K3_AM64_DDRSS) - fixup_ddr_driver_for_ecc(spl_image); -#else - fixup_memory_node(spl_image); -#endif + if (IS_ENABLED(CONFIG_K3_DDRSS)) { + if (IS_ENABLED(CONFIG_K3_INLINE_ECC)) + fixup_ddr_driver_for_ecc(spl_image); + } else { + fixup_memory_node(spl_image); + } } #endif diff --git a/board/ti/am64x/evm.c b/board/ti/am64x/evm.c index 00b8317d6bf..6f7e1f82866 100644 --- a/board/ti/am64x/evm.c +++ b/board/ti/am64x/evm.c @@ -15,6 +15,7 @@ #include <fdt_support.h> #include <asm/arch/hardware.h> #include <env.h> +#include <asm/arch/k3-ddr.h> #include "../common/board_detect.h" #include "../common/fdt_ops.h" @@ -66,28 +67,6 @@ int board_init(void) return 0; } -int dram_init(void) -{ - s32 ret; - - ret = fdtdec_setup_mem_size_base(); - if (ret) - printf("Error setting up mem size and base. %d\n", ret); - - return ret; -} - -int dram_init_banksize(void) -{ - s32 ret; - - ret = fdtdec_setup_memory_banksize(); - if (ret) - printf("Error setting up memory banksize. %d\n", ret); - - return ret; -} - #if defined(CONFIG_SPL_LOAD_FIT) int board_fit_config_name_match(const char *name) { @@ -132,52 +111,14 @@ static int fixup_usb_boot(const void *fdt_blob) } #endif -#if defined(CONFIG_K3_AM64_DDRSS) -static void fixup_ddr_driver_for_ecc(struct spl_image_info *spl_image) -{ - struct udevice *dev; - int ret; - - dram_init_banksize(); - - ret = uclass_get_device(UCLASS_RAM, 0, &dev); - if (ret) - panic("Cannot get RAM device for ddr size fixup: %d\n", ret); - - ret = k3_ddrss_ddr_fdt_fixup(dev, spl_image->fdt_addr, gd->bd); - if (ret) - printf("Error fixing up ddr node for ECC use! %d\n", ret); -} -#else -static void fixup_memory_node(struct spl_image_info *spl_image) -{ - u64 start[CONFIG_NR_DRAM_BANKS]; - u64 size[CONFIG_NR_DRAM_BANKS]; - int bank; - int ret; - - dram_init(); - dram_init_banksize(); - - for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) { - start[bank] = gd->bd->bi_dram[bank].start; - size[bank] = gd->bd->bi_dram[bank].size; - } - - /* dram_init functions use SPL fdt, and we must fixup u-boot fdt */ - ret = fdt_fixup_memory_banks(spl_image->fdt_addr, start, size, CONFIG_NR_DRAM_BANKS); - if (ret) - printf("Error fixing up memory node! %d\n", ret); -} -#endif - void spl_perform_fixups(struct spl_image_info *spl_image) { -#if defined(CONFIG_K3_AM64_DDRSS) - fixup_ddr_driver_for_ecc(spl_image); -#else - fixup_memory_node(spl_image); -#endif + if (IS_ENABLED(CONFIG_K3_DDRSS)) { + if (IS_ENABLED(CONFIG_K3_INLINE_ECC)) + fixup_ddr_driver_for_ecc(spl_image); + } else { + fixup_memory_node(spl_image); + } #if CONFIG_IS_ENABLED(USB_STORAGE) fixup_usb_boot(spl_image->fdt_addr); diff --git a/board/ti/am65x/evm.c b/board/ti/am65x/evm.c index 07073a5940b..6658794a137 100644 --- a/board/ti/am65x/evm.c +++ b/board/ti/am65x/evm.c @@ -20,6 +20,7 @@ #include <env.h> #include <spl.h> #include <linux/printk.h> +#include <asm/arch/k3-ddr.h> #include "../common/board_detect.h" #include "../common/fdt_ops.h" @@ -49,17 +50,6 @@ int board_init(void) return 0; } -int dram_init(void) -{ -#ifdef CONFIG_PHYS_64BIT - gd->ram_size = 0x100000000; -#else - gd->ram_size = 0x80000000; -#endif - - return 0; -} - phys_addr_t board_get_usable_ram_top(phys_size_t total_size) { #ifdef CONFIG_PHYS_64BIT @@ -71,23 +61,6 @@ phys_addr_t board_get_usable_ram_top(phys_size_t total_size) return gd->ram_top; } -int dram_init_banksize(void) -{ - /* Bank 0 declares the memory available in the DDR low region */ - gd->bd->bi_dram[0].start = 0x80000000; - gd->bd->bi_dram[0].size = 0x80000000; - gd->ram_size = 0x80000000; - -#ifdef CONFIG_PHYS_64BIT - /* Bank 1 declares the memory available in the DDR high region */ - gd->bd->bi_dram[1].start = 0x880000000; - gd->bd->bi_dram[1].size = 0x80000000; - gd->ram_size = 0x100000000; -#endif - - return 0; -} - #ifdef CONFIG_SPL_LOAD_FIT int board_fit_config_name_match(const char *name) { diff --git a/board/ti/common/Makefile b/board/ti/common/Makefile index 5ac361ba7fc..caf6b9fa8c1 100644 --- a/board/ti/common/Makefile +++ b/board/ti/common/Makefile @@ -4,3 +4,4 @@ obj-${CONFIG_TI_I2C_BOARD_DETECT} += board_detect.o obj-${CONFIG_CMD_EXTENSION} += cape_detect.o obj-${CONFIG_OF_LIBFDT} += fdt_ops.o +obj-${CONFIG_ARCH_K3} += k3-ddr.o diff --git a/board/ti/common/k3-ddr.c b/board/ti/common/k3-ddr.c new file mode 100644 index 00000000000..a8425da8de5 --- /dev/null +++ b/board/ti/common/k3-ddr.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2024, Texas Instruments Incorporated - https://www.ti.com/ + */ + +#include <fdt_support.h> +#include <dm/uclass.h> +#include <k3-ddrss.h> +#include <spl.h> + +#include "k3-ddr.h" + +int dram_init(void) +{ + s32 ret; + + ret = fdtdec_setup_mem_size_base_lowest(); + if (ret) + printf("Error setting up mem size and base. %d\n", ret); + + return ret; +} + +int dram_init_banksize(void) +{ + s32 ret; + + ret = fdtdec_setup_memory_banksize(); + if (ret) + printf("Error setting up memory banksize. %d\n", ret); + + return ret; +} diff --git a/board/ti/common/k3-ddr.h b/board/ti/common/k3-ddr.h new file mode 100644 index 00000000000..737d45e2e50 --- /dev/null +++ b/board/ti/common/k3-ddr.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2024, Texas Instruments Incorporated - https://www.ti.com/ + */ + +#ifndef __K3_DDR_INIT_H +#define __K3_DDR_INIT_H + +int dram_init(void); +int dram_init_banksize(void); + +#endif /* __K3_DDR_INIT_H */ diff --git a/board/ti/j721e/evm.c b/board/ti/j721e/evm.c index 6221be9dcff..4b5b9dded7b 100644 --- a/board/ti/j721e/evm.c +++ b/board/ti/j721e/evm.c @@ -15,6 +15,7 @@ #include <asm/gpio.h> #include <spl.h> #include <dm.h> +#include <asm/arch/k3-ddr.h> #include "../common/board_detect.h" #include "../common/fdt_ops.h" @@ -77,17 +78,6 @@ int board_init(void) return 0; } -int dram_init(void) -{ -#ifdef CONFIG_PHYS_64BIT - gd->ram_size = 0x100000000; -#else - gd->ram_size = 0x80000000; -#endif - - return 0; -} - phys_addr_t board_get_usable_ram_top(phys_size_t total_size) { #ifdef CONFIG_PHYS_64BIT @@ -99,23 +89,6 @@ phys_addr_t board_get_usable_ram_top(phys_size_t total_size) return gd->ram_top; } -int dram_init_banksize(void) -{ - /* Bank 0 declares the memory available in the DDR low region */ - gd->bd->bi_dram[0].start = 0x80000000; - gd->bd->bi_dram[0].size = 0x80000000; - gd->ram_size = 0x80000000; - -#ifdef CONFIG_PHYS_64BIT - /* Bank 1 declares the memory available in the DDR high region */ - gd->bd->bi_dram[1].start = 0x880000000; - gd->bd->bi_dram[1].size = 0x80000000; - gd->ram_size = 0x100000000; -#endif - - return 0; -} - #ifdef CONFIG_SPL_LOAD_FIT int board_fit_config_name_match(const char *name) { diff --git a/board/ti/j721s2/evm.c b/board/ti/j721s2/evm.c index 2cfeb3bec6c..1b5ad19df7f 100644 --- a/board/ti/j721s2/evm.c +++ b/board/ti/j721s2/evm.c @@ -21,6 +21,7 @@ #include <dm.h> #include <dm/uclass-internal.h> #include <dm/root.h> +#include <asm/arch/k3-ddr.h> #include "../common/board_detect.h" #include "../common/fdt_ops.h" @@ -32,17 +33,6 @@ int board_init(void) return 0; } -int dram_init(void) -{ -#ifdef CONFIG_PHYS_64BIT - gd->ram_size = 0x100000000; -#else - gd->ram_size = 0x80000000; -#endif - - return 0; -} - phys_addr_t board_get_usable_ram_top(phys_size_t total_size) { #ifdef CONFIG_PHYS_64BIT @@ -54,22 +44,17 @@ phys_addr_t board_get_usable_ram_top(phys_size_t total_size) return gd->ram_top; } -int dram_init_banksize(void) +#if defined(CONFIG_XPL_BUILD) +void spl_perform_fixups(struct spl_image_info *spl_image) { - /* Bank 0 declares the memory available in the DDR low region */ - gd->bd->bi_dram[0].start = 0x80000000; - gd->bd->bi_dram[0].size = 0x7fffffff; - gd->ram_size = 0x80000000; - -#ifdef CONFIG_PHYS_64BIT - /* Bank 1 declares the memory available in the DDR high region */ - gd->bd->bi_dram[1].start = 0x880000000; - gd->bd->bi_dram[1].size = 0x37fffffff; - gd->ram_size = 0x400000000; -#endif - - return 0; + if (IS_ENABLED(CONFIG_K3_DDRSS)) { + if (IS_ENABLED(CONFIG_K3_INLINE_ECC)) + fixup_ddr_driver_for_ecc(spl_image); + } else { + fixup_memory_node(spl_image); + } } +#endif #ifdef CONFIG_TI_I2C_BOARD_DETECT /* diff --git a/board/ti/j722s/evm.c b/board/ti/j722s/evm.c index 29e06a5442f..f085ecfd37e 100644 --- a/board/ti/j722s/evm.c +++ b/board/ti/j722s/evm.c @@ -12,6 +12,7 @@ #include <env.h> #include <fdt_support.h> #include <spl.h> +#include <asm/arch/k3-ddr.h> #include "../common/fdt_ops.h" int board_init(void) @@ -19,15 +20,17 @@ int board_init(void) return 0; } -int dram_init(void) +#if defined(CONFIG_XPL_BUILD) +void spl_perform_fixups(struct spl_image_info *spl_image) { - return fdtdec_setup_mem_size_base(); -} - -int dram_init_banksize(void) -{ - return fdtdec_setup_memory_banksize(); + if (IS_ENABLED(CONFIG_K3_DDRSS)) { + if (IS_ENABLED(CONFIG_K3_INLINE_ECC)) + fixup_ddr_driver_for_ecc(spl_image); + } else { + fixup_memory_node(spl_image); + } } +#endif #if IS_ENABLED(CONFIG_BOARD_LATE_INIT) int board_late_init(void) diff --git a/board/ti/j784s4/evm.c b/board/ti/j784s4/evm.c index 548dbd5925d..d317f3eccbb 100644 --- a/board/ti/j784s4/evm.c +++ b/board/ti/j784s4/evm.c @@ -10,6 +10,7 @@ #include <efi_loader.h> #include <init.h> #include <spl.h> +#include <asm/arch/k3-ddr.h> #include "../common/fdt_ops.h" DECLARE_GLOBAL_DATA_PTR; @@ -52,15 +53,17 @@ int board_init(void) return 0; } -int dram_init(void) +#if defined(CONFIG_XPL_BUILD) +void spl_perform_fixups(struct spl_image_info *spl_image) { - return fdtdec_setup_mem_size_base(); -} - -int dram_init_banksize(void) -{ - return fdtdec_setup_memory_banksize(); + if (IS_ENABLED(CONFIG_K3_DDRSS)) { + if (IS_ENABLED(CONFIG_K3_INLINE_ECC)) + fixup_ddr_driver_for_ecc(spl_image); + } else { + fixup_memory_node(spl_image); + } } +#endif #ifdef CONFIG_BOARD_LATE_INIT int board_late_init(void) diff --git a/configs/am62ax_evm_a53_defconfig b/configs/am62ax_evm_a53_defconfig index 7d63340c903..107d6c6becf 100644 --- a/configs/am62ax_evm_a53_defconfig +++ b/configs/am62ax_evm_a53_defconfig @@ -3,7 +3,6 @@ CONFIG_ARCH_K3=y CONFIG_SYS_MALLOC_F_LEN=0x8000 CONFIG_SPL_LIBCOMMON_SUPPORT=y CONFIG_SPL_LIBGENERIC_SUPPORT=y -CONFIG_NR_DRAM_BANKS=2 CONFIG_SOC_K3_AM62A7=y CONFIG_TARGET_AM62A7_A53_EVM=y CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y diff --git a/configs/am62px_evm_a53_defconfig b/configs/am62px_evm_a53_defconfig index 298fb9684ea..721dfa67d34 100644 --- a/configs/am62px_evm_a53_defconfig +++ b/configs/am62px_evm_a53_defconfig @@ -4,7 +4,6 @@ CONFIG_SYS_MALLOC_F_LEN=0x8000 CONFIG_SPL_GPIO=y CONFIG_SPL_LIBCOMMON_SUPPORT=y CONFIG_SPL_LIBGENERIC_SUPPORT=y -CONFIG_NR_DRAM_BANKS=2 CONFIG_SOC_K3_AM62P5=y CONFIG_TARGET_AM62P5_A53_EVM=y CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y diff --git a/configs/am62x_evm_a53_defconfig b/configs/am62x_evm_a53_defconfig index abc061f1dbc..1a362d44d1b 100644 --- a/configs/am62x_evm_a53_defconfig +++ b/configs/am62x_evm_a53_defconfig @@ -4,7 +4,6 @@ CONFIG_SYS_MALLOC_LEN=0x2000000 CONFIG_SYS_MALLOC_F_LEN=0x8000 CONFIG_SPL_LIBCOMMON_SUPPORT=y CONFIG_SPL_LIBGENERIC_SUPPORT=y -CONFIG_NR_DRAM_BANKS=2 CONFIG_SOC_K3_AM625=y CONFIG_TARGET_AM625_A53_EVM=y CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y diff --git a/configs/am62x_evm_r5_defconfig b/configs/am62x_evm_r5_defconfig index fcc5eb02867..b47f2f2d1f3 100644 --- a/configs/am62x_evm_r5_defconfig +++ b/configs/am62x_evm_r5_defconfig @@ -4,7 +4,6 @@ CONFIG_SYS_MALLOC_LEN=0x08000000 CONFIG_SYS_MALLOC_F_LEN=0x9000 CONFIG_SPL_LIBCOMMON_SUPPORT=y CONFIG_SPL_LIBGENERIC_SUPPORT=y -CONFIG_NR_DRAM_BANKS=2 CONFIG_SOC_K3_AM625=y CONFIG_TARGET_AM625_R5_EVM=y CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y diff --git a/configs/am64x_evm_a53_defconfig b/configs/am64x_evm_a53_defconfig index 80027a4d94b..feb021ff281 100644 --- a/configs/am64x_evm_a53_defconfig +++ b/configs/am64x_evm_a53_defconfig @@ -6,7 +6,6 @@ CONFIG_SYS_MALLOC_F_LEN=0x8000 CONFIG_SPL_GPIO=y CONFIG_SPL_LIBCOMMON_SUPPORT=y CONFIG_SPL_LIBGENERIC_SUPPORT=y -CONFIG_NR_DRAM_BANKS=2 CONFIG_SOC_K3_AM642=y CONFIG_K3_ATF_LOAD_ADDR=0x701c0000 CONFIG_TARGET_AM642_A53_EVM=y diff --git a/configs/am64x_evm_r5_defconfig b/configs/am64x_evm_r5_defconfig index c4e498a6ae7..c7fc9d124c7 100644 --- a/configs/am64x_evm_r5_defconfig +++ b/configs/am64x_evm_r5_defconfig @@ -5,7 +5,6 @@ CONFIG_SYS_MALLOC_F_LEN=0x80000 CONFIG_SPL_GPIO=y CONFIG_SPL_LIBCOMMON_SUPPORT=y CONFIG_SPL_LIBGENERIC_SUPPORT=y -CONFIG_NR_DRAM_BANKS=2 CONFIG_SOC_K3_AM642=y CONFIG_TARGET_AM642_R5_EVM=y CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y diff --git a/configs/am65x_evm_a53_defconfig b/configs/am65x_evm_a53_defconfig index cf7a2114883..b30907a4f25 100644 --- a/configs/am65x_evm_a53_defconfig +++ b/configs/am65x_evm_a53_defconfig @@ -5,7 +5,6 @@ CONFIG_SYS_MALLOC_LEN=0x2000000 CONFIG_SYS_MALLOC_F_LEN=0x8000 CONFIG_SPL_LIBCOMMON_SUPPORT=y CONFIG_SPL_LIBGENERIC_SUPPORT=y -CONFIG_NR_DRAM_BANKS=2 CONFIG_SOC_K3_AM654=y CONFIG_TARGET_AM654_A53_EVM=y CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y diff --git a/configs/am65x_evm_r5_defconfig b/configs/am65x_evm_r5_defconfig index 083522ce9b4..4bd5e8f63ff 100644 --- a/configs/am65x_evm_r5_defconfig +++ b/configs/am65x_evm_r5_defconfig @@ -5,7 +5,6 @@ CONFIG_SYS_MALLOC_F_LEN=0x55000 CONFIG_SPL_GPIO=y CONFIG_SPL_LIBCOMMON_SUPPORT=y CONFIG_SPL_LIBGENERIC_SUPPORT=y -CONFIG_NR_DRAM_BANKS=2 CONFIG_SOC_K3_AM654=y CONFIG_K3_EARLY_CONS=y CONFIG_TARGET_AM654_R5_EVM=y diff --git a/configs/am65x_evm_r5_usbdfu_defconfig b/configs/am65x_evm_r5_usbdfu_defconfig index e60e0d6588d..ae57007d898 100644 --- a/configs/am65x_evm_r5_usbdfu_defconfig +++ b/configs/am65x_evm_r5_usbdfu_defconfig @@ -5,7 +5,6 @@ CONFIG_SYS_MALLOC_F_LEN=0x55000 CONFIG_SPL_GPIO=y CONFIG_SPL_LIBCOMMON_SUPPORT=y CONFIG_SPL_LIBGENERIC_SUPPORT=y -CONFIG_NR_DRAM_BANKS=2 CONFIG_SOC_K3_AM654=y CONFIG_K3_EARLY_CONS=y CONFIG_TARGET_AM654_R5_EVM=y diff --git a/configs/am65x_evm_r5_usbmsc_defconfig b/configs/am65x_evm_r5_usbmsc_defconfig index ecd48c45ea1..3aa17eb77f0 100644 --- a/configs/am65x_evm_r5_usbmsc_defconfig +++ b/configs/am65x_evm_r5_usbmsc_defconfig @@ -5,7 +5,6 @@ CONFIG_SYS_MALLOC_F_LEN=0x55000 CONFIG_SPL_GPIO=y CONFIG_SPL_LIBCOMMON_SUPPORT=y CONFIG_SPL_LIBGENERIC_SUPPORT=y -CONFIG_NR_DRAM_BANKS=2 CONFIG_SOC_K3_AM654=y CONFIG_K3_EARLY_CONS=y CONFIG_TARGET_AM654_R5_EVM=y diff --git a/configs/j7200_evm_a72_defconfig b/configs/j7200_evm_a72_defconfig index eb6203f2b4c..7e598eeadbe 100644 --- a/configs/j7200_evm_a72_defconfig +++ b/configs/j7200_evm_a72_defconfig @@ -5,7 +5,6 @@ CONFIG_SYS_MALLOC_F_LEN=0x8000 CONFIG_SPL_GPIO=y CONFIG_SPL_LIBCOMMON_SUPPORT=y CONFIG_SPL_LIBGENERIC_SUPPORT=y -CONFIG_NR_DRAM_BANKS=2 CONFIG_SOC_K3_J721E=y CONFIG_TARGET_J7200_A72_EVM=y CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y diff --git a/configs/j721e_evm_a72_defconfig b/configs/j721e_evm_a72_defconfig index 5833e22a004..8ca2afeed04 100644 --- a/configs/j721e_evm_a72_defconfig +++ b/configs/j721e_evm_a72_defconfig @@ -5,7 +5,6 @@ CONFIG_SYS_MALLOC_F_LEN=0x8000 CONFIG_SPL_GPIO=y CONFIG_SPL_LIBCOMMON_SUPPORT=y CONFIG_SPL_LIBGENERIC_SUPPORT=y -CONFIG_NR_DRAM_BANKS=2 CONFIG_SOC_K3_J721E=y CONFIG_TARGET_J721E_A72_EVM=y CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y diff --git a/configs/j721s2_evm_a72_defconfig b/configs/j721s2_evm_a72_defconfig index 2a0ccfb581e..640aa00a024 100644 --- a/configs/j721s2_evm_a72_defconfig +++ b/configs/j721s2_evm_a72_defconfig @@ -5,7 +5,6 @@ CONFIG_SYS_MALLOC_F_LEN=0x8000 CONFIG_SPL_GPIO=y CONFIG_SPL_LIBCOMMON_SUPPORT=y CONFIG_SPL_LIBGENERIC_SUPPORT=y -CONFIG_NR_DRAM_BANKS=2 CONFIG_SOC_K3_J721S2=y CONFIG_TARGET_J721S2_A72_EVM=y CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y diff --git a/configs/j722s_evm_a53_defconfig b/configs/j722s_evm_a53_defconfig index 35329fb336b..17595e3c451 100644 --- a/configs/j722s_evm_a53_defconfig +++ b/configs/j722s_evm_a53_defconfig @@ -4,7 +4,6 @@ CONFIG_SYS_MALLOC_F_LEN=0x8000 CONFIG_SPL_GPIO=y CONFIG_SPL_LIBCOMMON_SUPPORT=y CONFIG_SPL_LIBGENERIC_SUPPORT=y -CONFIG_NR_DRAM_BANKS=2 CONFIG_SOC_K3_J722S=y CONFIG_TARGET_J722S_A53_EVM=y CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y diff --git a/configs/j784s4_evm_a72_defconfig b/configs/j784s4_evm_a72_defconfig index 88a690fbf96..5b344cc4518 100644 --- a/configs/j784s4_evm_a72_defconfig +++ b/configs/j784s4_evm_a72_defconfig @@ -5,7 +5,6 @@ CONFIG_SYS_MALLOC_F_LEN=0x8000 CONFIG_SPL_GPIO=y CONFIG_SPL_LIBCOMMON_SUPPORT=y CONFIG_SPL_LIBGENERIC_SUPPORT=y -CONFIG_NR_DRAM_BANKS=2 CONFIG_SOC_K3_J784S4=y CONFIG_TARGET_J784S4_A72_EVM=y CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y diff --git a/drivers/ram/Kconfig b/drivers/ram/Kconfig index f7e357f24da..533d0c629a6 100644 --- a/drivers/ram/Kconfig +++ b/drivers/ram/Kconfig @@ -116,6 +116,16 @@ config IMXRT_SDRAM to support external memories like sdram, psram & nand. This driver is for the sdram memory interface with the SEMC. +config K3_INLINE_ECC + bool "Enable TI Inline ECC support" + depends on K3_DDRSS + help + Enable Inline ECC support on K3 platforms. 1/9th of the SDRAM space + is used for ECC storage and the rest 8/9th is available for system + use. Enabling ECC increases boot time as the ECC protected regions + need to be primed with a predefined value prior to enabling ECC + check. + source "drivers/ram/aspeed/Kconfig" source "drivers/ram/cadence/Kconfig" source "drivers/ram/octeon/Kconfig" diff --git a/drivers/ram/k3-ddrss/k3-ddrss.c b/drivers/ram/k3-ddrss/k3-ddrss.c index 525b6d5b79f..80f81a34b35 100644 --- a/drivers/ram/k3-ddrss/k3-ddrss.c +++ b/drivers/ram/k3-ddrss/k3-ddrss.c @@ -6,6 +6,7 @@ */ #include <config.h> +#include <time.h> #include <clk.h> #include <div64.h> #include <dm.h> @@ -44,6 +45,11 @@ #define DDRSS_ECC_R2_STR_ADDR_REG 0x0140 #define DDRSS_ECC_R2_END_ADDR_REG 0x0144 #define DDRSS_ECC_1B_ERR_CNT_REG 0x0150 +#define DDRSS_V2A_INT_SET_REG 0x00a8 + +#define DDRSS_V2A_INT_SET_REG_ECC1BERR_EN BIT(3) +#define DDRSS_V2A_INT_SET_REG_ECC2BERR_EN BIT(4) +#define DDRSS_V2A_INT_SET_REG_ECCM1BERR_EN BIT(5) #define SINGLE_DDR_SUBSYSTEM 0x1 #define MULTI_DDR_SUBSYSTEM 0x2 @@ -120,8 +126,8 @@ struct k3_msmc { #define K3_DDRSS_MAX_ECC_REGIONS 3 struct k3_ddrss_ecc_region { - u32 start; - u32 range; + u64 start; + u64 range; }; struct k3_ddrss_desc { @@ -145,7 +151,9 @@ struct k3_ddrss_desc { lpddr4_privatedata pd; struct k3_ddrss_ecc_region ecc_regions[K3_DDRSS_MAX_ECC_REGIONS]; u64 ecc_reserved_space; - bool ti_ecc_enabled; + u64 ddr_bank_base[CONFIG_NR_DRAM_BANKS]; + u64 ddr_bank_size[CONFIG_NR_DRAM_BANKS]; + u64 ddr_ram_size; }; struct reginitdata { @@ -404,8 +412,6 @@ static int k3_ddrss_ofdata_to_priv(struct udevice *dev) if (ret) dev_err(dev, "ddr fhs cnt not populated %d\n", ret); - ddrss->ti_ecc_enabled = dev_read_bool(dev, "ti,ecc-enable"); - return ret; } @@ -547,27 +553,174 @@ void k3_lpddr4_start(struct k3_ddrss_desc *ddrss) } } -static void k3_ddrss_set_ecc_range_r0(u32 base, u32 start_address, u32 size) +static void k3_ddrss_set_ecc_range_r0(u32 base, u64 start_address, u64 size) { writel((start_address) >> 16, base + DDRSS_ECC_R0_STR_ADDR_REG); writel((start_address + size - 1) >> 16, base + DDRSS_ECC_R0_END_ADDR_REG); } -static void k3_ddrss_preload_ecc_mem_region(u32 *addr, u32 size, u32 word) +#define BIST_MODE_MEM_INIT 4 +#define BIST_MEM_INIT_TIMEOUT 10000 /* 1msec loops per block = 10s */ +static void k3_lpddr4_bist_init_mem_region(struct k3_ddrss_desc *ddrss, + u64 addr, u64 size, + u32 pattern) { - int i; + lpddr4_obj *driverdt = ddrss->driverdt; + lpddr4_privatedata *pd = &ddrss->pd; + u32 status, offset, regval; + bool int_status; + int i = 0; + + /* Set BIST_START_ADDR_0 [31:0] */ + regval = (u32)(addr & TH_FLD_MASK(LPDDR4__BIST_START_ADDRESS_0__FLD)); + TH_OFFSET_FROM_REG(LPDDR4__BIST_START_ADDRESS_0__REG, CTL_SHIFT, offset); + driverdt->writereg(pd, LPDDR4_CTL_REGS, offset, regval); + + /* Set BIST_START_ADDR_1 [32 or 34:32] */ + regval = (u32)(addr >> TH_FLD_WIDTH(LPDDR4__BIST_START_ADDRESS_0__FLD)); + regval &= TH_FLD_MASK(LPDDR4__BIST_START_ADDRESS_1__FLD); + TH_OFFSET_FROM_REG(LPDDR4__BIST_START_ADDRESS_1__REG, CTL_SHIFT, offset); + driverdt->writereg(pd, LPDDR4_CTL_REGS, offset, regval); + + /* Set ADDR_SPACE = log2(size) */ + regval = (u32)(ilog2(size) << TH_FLD_SHIFT(LPDDR4__ADDR_SPACE__FLD)); + TH_OFFSET_FROM_REG(LPDDR4__ADDR_SPACE__REG, CTL_SHIFT, offset); + driverdt->writereg(pd, LPDDR4_CTL_REGS, offset, regval); + + /* Enable the BIST data check. On 32bit lpddr4 (e.g J7) this shares a + * register with ADDR_SPACE and BIST_GO. + */ + TH_OFFSET_FROM_REG(LPDDR4__BIST_DATA_CHECK__REG, CTL_SHIFT, offset); + driverdt->readreg(pd, LPDDR4_CTL_REGS, offset, ®val); + regval |= TH_FLD_MASK(LPDDR4__BIST_DATA_CHECK__FLD); + driverdt->writereg(pd, LPDDR4_CTL_REGS, offset, regval); + /* Clear the address check bit */ + TH_OFFSET_FROM_REG(LPDDR4__BIST_ADDR_CHECK__REG, CTL_SHIFT, offset); + driverdt->readreg(pd, LPDDR4_CTL_REGS, offset, ®val); + regval &= ~TH_FLD_MASK(LPDDR4__BIST_ADDR_CHECK__FLD); + driverdt->writereg(pd, LPDDR4_CTL_REGS, offset, regval); + + /* Set BIST_TEST_MODE[2:0] to memory initialize (4) */ + regval = BIST_MODE_MEM_INIT; + TH_OFFSET_FROM_REG(LPDDR4__BIST_TEST_MODE__REG, CTL_SHIFT, offset); + driverdt->writereg(pd, LPDDR4_CTL_REGS, offset, regval); + + /* Set BIST_DATA_PATTERN[31:0] */ + TH_OFFSET_FROM_REG(LPDDR4__BIST_DATA_PATTERN_0__REG, CTL_SHIFT, offset); + driverdt->writereg(pd, LPDDR4_CTL_REGS, offset, pattern); + + /* Set BIST_DATA_PATTERN[63:32] */ + TH_OFFSET_FROM_REG(LPDDR4__BIST_DATA_PATTERN_1__REG, CTL_SHIFT, offset); + driverdt->writereg(pd, LPDDR4_CTL_REGS, offset, pattern); + + udelay(1000); + + /* Enable the programmed BIST operation - BIST_GO = 1 */ + TH_OFFSET_FROM_REG(LPDDR4__BIST_GO__REG, CTL_SHIFT, offset); + driverdt->readreg(pd, LPDDR4_CTL_REGS, offset, ®val); + regval |= TH_FLD_MASK(LPDDR4__BIST_GO__FLD); + driverdt->writereg(pd, LPDDR4_CTL_REGS, offset, regval); + + /* Wait for the BIST_DONE interrupt */ + while (i < BIST_MEM_INIT_TIMEOUT) { + status = driverdt->checkctlinterrupt(pd, LPDDR4_INTR_BIST_DONE, + &int_status); + if (!status & int_status) { + /* Clear LPDDR4_INTR_BIST_DONE */ + driverdt->ackctlinterrupt(pd, LPDDR4_INTR_BIST_DONE); + break; + } + udelay(1000); + i++; + } + + /* Before continuing we have to stop BIST - BIST_GO = 0 */ + TH_OFFSET_FROM_REG(LPDDR4__BIST_GO__REG, CTL_SHIFT, offset); + driverdt->writereg(pd, LPDDR4_CTL_REGS, offset, 0); + /* Timeout hit while priming the memory. We can't continue, + * since the memory is not fully initialized and we most + * likely get an uncorrectable error exception while booting. + */ + if (i == BIST_MEM_INIT_TIMEOUT) { + printf("ERROR: Timeout while priming the memory.\n"); + hang(); + } +} + +static void k3_ddrss_lpddr4_preload_full_mem(struct k3_ddrss_desc *ddrss, + u64 total_size, u32 pattern) +{ + u32 done, max_size2; + + /* Get the max size (log2) supported in this config (16/32 lpddr4) + * from the start_addess width - 16bit: 8G, 32bit: 32G + */ + max_size2 = TH_FLD_WIDTH(LPDDR4__BIST_START_ADDRESS_0__FLD) + + TH_FLD_WIDTH(LPDDR4__BIST_START_ADDRESS_1__FLD) + 1; + + /* ECC is enabled in dt but we can't preload the memory if + * the memory configuration is recognized and supported. + */ + if (!total_size || total_size > (1ull << max_size2) || + total_size & (total_size - 1)) { + printf("ECC: the memory configuration is not supported\n"); + hang(); + } printf("ECC is enabled, priming DDR which will take several seconds.\n"); + done = get_timer(0); + k3_lpddr4_bist_init_mem_region(ddrss, 0, total_size, pattern); + printf("ECC: priming DDR completed in %lu msec\n", get_timer(done)); +} + +static void k3_ddrss_ddr_bank_base_size_calc(struct k3_ddrss_desc *ddrss) +{ + int bank, na, ns, len, parent; + const fdt32_t *ptr, *end; + + for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) { + ddrss->ddr_bank_base[bank] = 0; + ddrss->ddr_bank_size[bank] = 0; + } + + ofnode mem = ofnode_null(); + + do { + mem = ofnode_by_prop_value(mem, "device_type", "memory", 7); + } while (!ofnode_is_enabled(mem)); + + const void *fdt = ofnode_to_fdt(mem); + int node = ofnode_to_offset(mem); + const char *property = "reg"; + + parent = fdt_parent_offset(fdt, node); + na = fdt_address_cells(fdt, parent); + ns = fdt_size_cells(fdt, parent); + ptr = fdt_getprop(fdt, node, property, &len); + end = ptr + len / sizeof(*ptr); + + for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) { + if (ptr + na + ns <= end) { + if (CONFIG_IS_ENABLED(OF_TRANSLATE)) + ddrss->ddr_bank_base[bank] = fdt_translate_address(fdt, node, ptr); + else + ddrss->ddr_bank_base[bank] = fdtdec_get_number(ptr, na); + + ddrss->ddr_bank_size[bank] = fdtdec_get_number(&ptr[na], ns); + } - for (i = 0; i < (size / 4); i++) - addr[i] = word; + ptr += na + ns; + } + + for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) + ddrss->ddr_ram_size += ddrss->ddr_bank_size[bank]; } static void k3_ddrss_lpddr4_ecc_calc_reserved_mem(struct k3_ddrss_desc *ddrss) { fdtdec_setup_mem_size_base_lowest(); - ddrss->ecc_reserved_space = gd->ram_size; + ddrss->ecc_reserved_space = ddrss->ddr_ram_size; do_div(ddrss->ecc_reserved_space, 9); /* Round to clean number */ @@ -576,25 +729,29 @@ static void k3_ddrss_lpddr4_ecc_calc_reserved_mem(struct k3_ddrss_desc *ddrss) static void k3_ddrss_lpddr4_ecc_init(struct k3_ddrss_desc *ddrss) { - u32 ecc_region_start = ddrss->ecc_regions[0].start; - u32 ecc_range = ddrss->ecc_regions[0].range; + u64 ecc_region_start = ddrss->ecc_regions[0].start; + u64 ecc_range = ddrss->ecc_regions[0].range; u32 base = (u32)ddrss->ddrss_ss_cfg; u32 val; /* Only Program region 0 which covers full ddr space */ - k3_ddrss_set_ecc_range_r0(base, ecc_region_start - gd->ram_base, ecc_range); + k3_ddrss_set_ecc_range_r0(base, ecc_region_start - ddrss->ddr_bank_base[0], ecc_range); /* Enable ECC, RMW, WR_ALLOC */ writel(DDRSS_ECC_CTRL_REG_ECC_EN | DDRSS_ECC_CTRL_REG_RMW_EN | DDRSS_ECC_CTRL_REG_WR_ALLOC, base + DDRSS_ECC_CTRL_REG); - /* Preload ECC Mem region with 0's */ - k3_ddrss_preload_ecc_mem_region((u32 *)ecc_region_start, ecc_range, - 0x00000000); + /* Preload the full memory with 0's using the BIST engine of + * the LPDDR4 controller. + */ + k3_ddrss_lpddr4_preload_full_mem(ddrss, ddrss->ddr_ram_size, 0); /* Clear Error Count Register */ writel(0x1, base + DDRSS_ECC_1B_ERR_CNT_REG); + writel(DDRSS_V2A_INT_SET_REG_ECC1BERR_EN | DDRSS_V2A_INT_SET_REG_ECC2BERR_EN | + DDRSS_V2A_INT_SET_REG_ECCM1BERR_EN, base + DDRSS_V2A_INT_SET_REG); + /* Enable ECC Check */ val = readl(base + DDRSS_ECC_CTRL_REG); val |= DDRSS_ECC_CTRL_REG_ECC_CK; @@ -635,7 +792,9 @@ static int k3_ddrss_probe(struct udevice *dev) k3_lpddr4_start(ddrss); - if (ddrss->ti_ecc_enabled) { + k3_ddrss_ddr_bank_base_size_calc(ddrss); + + if (IS_ENABLED(CONFIG_K3_INLINE_ECC)) { if (!ddrss->ddrss_ss_cfg) { printf("%s: ss_cfg is required if ecc is enabled but not provided.", __func__); @@ -645,8 +804,8 @@ static int k3_ddrss_probe(struct udevice *dev) k3_ddrss_lpddr4_ecc_calc_reserved_mem(ddrss); /* Always configure one region that covers full DDR space */ - ddrss->ecc_regions[0].start = gd->ram_base; - ddrss->ecc_regions[0].range = gd->ram_size - ddrss->ecc_reserved_space; + ddrss->ecc_regions[0].start = ddrss->ddr_bank_base[0]; + ddrss->ecc_regions[0].range = ddrss->ddr_ram_size - ddrss->ecc_reserved_space; k3_ddrss_lpddr4_ecc_init(ddrss); } @@ -655,30 +814,24 @@ static int k3_ddrss_probe(struct udevice *dev) int k3_ddrss_ddr_fdt_fixup(struct udevice *dev, void *blob, struct bd_info *bd) { - struct k3_ddrss_desc *ddrss = dev_get_priv(dev); - u64 start[CONFIG_NR_DRAM_BANKS]; - u64 size[CONFIG_NR_DRAM_BANKS]; int bank; + struct k3_ddrss_desc *ddrss = dev_get_priv(dev); if (ddrss->ecc_reserved_space == 0) return 0; for (bank = CONFIG_NR_DRAM_BANKS - 1; bank >= 0; bank--) { - if (ddrss->ecc_reserved_space > bd->bi_dram[bank].size) { - ddrss->ecc_reserved_space -= bd->bi_dram[bank].size; - bd->bi_dram[bank].size = 0; + if (ddrss->ecc_reserved_space > ddrss->ddr_bank_size[bank]) { + ddrss->ecc_reserved_space -= ddrss->ddr_bank_size[bank]; + ddrss->ddr_bank_size[bank] = 0; } else { - bd->bi_dram[bank].size -= ddrss->ecc_reserved_space; + ddrss->ddr_bank_size[bank] -= ddrss->ecc_reserved_space; break; } } - for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) { - start[bank] = bd->bi_dram[bank].start; - size[bank] = bd->bi_dram[bank].size; - } - - return fdt_fixup_memory_banks(blob, start, size, CONFIG_NR_DRAM_BANKS); + return fdt_fixup_memory_banks(blob, ddrss->ddr_bank_base, + ddrss->ddr_bank_size, CONFIG_NR_DRAM_BANKS); } static int k3_ddrss_get_info(struct udevice *dev, struct ram_info *info) diff --git a/fs/ext4l/ext4_uboot.h b/fs/ext4l/ext4_uboot.h index 4c4dd2ca559..a4e4e7095d4 100644 --- a/fs/ext4l/ext4_uboot.h +++ b/fs/ext4l/ext4_uboot.h @@ -58,16 +58,14 @@ struct timespec64 { }; /* - * ktime_t, sector_t are in linux/types.h - * atomic_t, atomic64_t are in asm-generic/atomic.h - * MAX_JIFFY_OFFSET is in linux/jiffies.h - * BDEVNAME_SIZE is in linux/blkdev.h + * ktime_t, atomic_t, atomic64_t, sector_t are now in linux/types.h + * MAX_JIFFY_OFFSET is now in linux/jiffies.h + * BDEVNAME_SIZE is now in linux/blkdev.h */ -#include <asm-generic/atomic.h> #include <linux/jiffies.h> #include <linux/blkdev.h> -/* Extra atomic operations not in asm-generic/atomic.h */ +/* Extra atomic operation not in linux/types.h */ #define atomic_dec_if_positive(v) (--(v)->counter) /* SMP stubs - U-Boot is single-threaded */ @@ -1138,7 +1136,10 @@ struct shrinker { static inline struct shrinker *shrinker_alloc(unsigned int flags, const char *fmt, ...) { - return NULL; + /* Return static dummy - U-Boot doesn't need memory reclamation */ + static struct shrinker dummy_shrinker; + + return &dummy_shrinker; } static inline void shrinker_register(struct shrinker *s) @@ -1506,7 +1507,7 @@ static inline char *d_path(const struct path *path, char *buf, int buflen) #define filemap_splice_read(i, p, pi, l, f) ({ (void)(i); (void)(p); (void)(pi); (void)(l); (void)(f); 0L; }) /* Buffer operations - additional */ -#define getblk_unmovable(bd, b, s) ((struct buffer_head *)NULL) +#define getblk_unmovable(bdev, block, size) sb_getblk(bdev->bd_super, block) #define create_empty_buffers(f, s, flags) ({ (void)(f); (void)(s); (void)(flags); (struct buffer_head *)NULL; }) #define bh_offset(bh) (0UL) #define block_invalidate_folio(f, o, l) do { } while (0) @@ -2314,8 +2315,8 @@ struct mb_cache_entry { /* MB cache flags */ #define MBE_REUSABLE_B 0 -#define mb_cache_create(bits) ((struct mb_cache *)NULL) -#define mb_cache_destroy(cache) do { (void)(cache); } while (0) +#define mb_cache_create(bits) kzalloc(sizeof(struct mb_cache), GFP_KERNEL) +#define mb_cache_destroy(cache) do { kfree(cache); } while (0) #define mb_cache_entry_find_first(c, h) ((struct mb_cache_entry *)NULL) #define mb_cache_entry_find_next(c, e) ((struct mb_cache_entry *)NULL) #define mb_cache_entry_delete_or_get(c, k, v) ((struct mb_cache_entry *)NULL) diff --git a/fs/ext4l/interface.c b/fs/ext4l/interface.c index 141afc42c17..7360f44c92b 100644 --- a/fs/ext4l/interface.c +++ b/fs/ext4l/interface.c @@ -28,6 +28,9 @@ static struct blk_desc *ext4l_blk_dev; static struct disk_partition ext4l_partition; static int ext4l_mounted; +/* Global super_block pointer for filesystem operations */ +static struct super_block *ext4l_sb; + /** * ext4l_get_blk_dev() - Get the current block device * Return: Block device descriptor or NULL if not mounted @@ -89,9 +92,7 @@ int ext4l_probe(struct blk_desc *fs_dev_desc, if (!fs_dev_desc) return -EINVAL; - /* Set up block device for buffer I/O */ - ext4l_set_blk_dev(fs_dev_desc, fs_partition); - + /* Initialise CRC32C table for checksum verification */ ext4l_crc32c_init(); /* Initialise journal subsystem if enabled */ @@ -113,6 +114,11 @@ int ext4l_probe(struct blk_desc *fs_dev_desc, if (ret) return ret; + /* Initialise system zone for block validity checking */ + ret = ext4_init_system_zone(); + if (ret) + goto err_exit_es; + /* Allocate super_block */ sb = kzalloc(sizeof(struct super_block), GFP_KERNEL); if (!sb) { @@ -193,11 +199,18 @@ int ext4l_probe(struct blk_desc *fs_dev_desc, if (fs_partition) memcpy(&ext4l_part, fs_partition, sizeof(ext4l_part)); + /* Set block device for buffer I/O */ + ext4l_set_blk_dev(fs_dev_desc, fs_partition); + /* Mount the filesystem */ ret = ext4_fill_super(sb, fc); - if (ret) + if (ret) { + printf("ext4l: ext4_fill_super failed: %d\n", ret); goto err_free_ctx; + } + /* Store super_block for later operations */ + ext4l_sb = sb; return 0; err_free_buf: @@ -219,6 +232,7 @@ err_exit_es: void ext4l_close(void) { - ext4l_clear_blk_dev(); ext4l_dev_desc = NULL; + ext4l_sb = NULL; + ext4l_clear_blk_dev(); } diff --git a/fs/ext4l/stub.c b/fs/ext4l/stub.c index eff54bb540c..2d066be4af3 100644 --- a/fs/ext4l/stub.c +++ b/fs/ext4l/stub.c @@ -561,9 +561,30 @@ void generic_set_sb_d_ops(struct super_block *sb) { } +/** + * d_make_root() - Create a root dentry for an inode + * @inode: Inode to create dentry for + * Return: Allocated dentry or NULL on failure + */ struct dentry *d_make_root(struct inode *inode) { - return NULL; + struct dentry *de; + + if (!inode) + return NULL; + + de = kzalloc(sizeof(struct dentry), GFP_KERNEL); + if (!de) { + iput(inode); + return NULL; + } + + de->d_inode = inode; + de->d_sb = inode->i_sb; + de->d_name.name = "/"; + de->d_name.len = 1; + + return de; } /* percpu init rwsem */ diff --git a/fs/ext4l/support.c b/fs/ext4l/support.c index d1bc32ae8d6..9e93426b565 100644 --- a/fs/ext4l/support.c +++ b/fs/ext4l/support.c @@ -19,6 +19,13 @@ #include "ext4_uboot.h" #include "ext4.h" +/* + * Global task_struct for U-Boot. + * This must be a single global instance shared across all translation units, + * so that journal_info remains consistent. + */ +struct task_struct ext4l_current_task = { .comm = "u-boot", .pid = 1 }; + /* * CRC32C support - uses Castagnoli polynomial 0x82F63B78 * Table is initialised on first mount diff --git a/include/linux/types.h b/include/linux/types.h index 957284d37ab..975040b7980 100644 --- a/include/linux/types.h +++ b/include/linux/types.h @@ -165,6 +165,28 @@ typedef s64 ktime_t; typedef u64 sector_t; typedef u64 blkcnt_t; +/* Atomic types - stubs for single-threaded U-Boot */ +typedef struct { + int counter; +} atomic_t; + +#ifdef CONFIG_64BIT +typedef struct { + s64 counter; +} atomic64_t; +#else +typedef struct { + long counter; +} atomic64_t; +#endif + +#define atomic_read(v) ((v)->counter) +#define atomic_set(v, i) ((v)->counter = (i)) +#define atomic_inc(v) ((v)->counter++) +#define atomic_dec(v) ((v)->counter--) +#define atomic64_read(v) ((v)->counter) +#define atomic64_set(v, i) ((v)->counter = (i)) + #ifdef __linux__ struct ustat { __kernel_daddr_t f_tfree; diff --git a/tools/buildman/func_test.py b/tools/buildman/func_test.py index 6fc08e02fb8..5b112c81aea 100644 --- a/tools/buildman/func_test.py +++ b/tools/buildman/func_test.py @@ -1267,3 +1267,4 @@ something: me boards.ExtendedParser.parse_data('bert', 'name: katie was here') self.assertEqual('bert:1: Invalid name', str(exc.exception)) + diff --git a/tools/buildman/main.py b/tools/buildman/main.py index 77b9bebed27..9483e12e5d0 100755 --- a/tools/buildman/main.py +++ b/tools/buildman/main.py @@ -41,6 +41,7 @@ def run_tests(skip_net_tests, debug, verbose, args): # pylint: disable=C0415 from buildman import func_test from buildman import test + from buildman import test_boards test_name = args.terms and args.terms[0] or None if skip_net_tests: @@ -50,7 +51,8 @@ def run_tests(skip_net_tests, debug, verbose, args): # 'entry' module. result = test_util.run_test_suites( 'buildman', debug, verbose, False, False, args.threads, test_name, [], - [test.TestBuild, func_test.TestFunctional, 'buildman.toolchain']) + [test.TestBuild, func_test.TestFunctional, test_boards.TestBoards, + 'buildman.toolchain']) return (0 if result.wasSuccessful() else 1) diff --git a/tools/buildman/test_boards.py b/tools/buildman/test_boards.py new file mode 100644 index 00000000000..66eb82bc755 --- /dev/null +++ b/tools/buildman/test_boards.py @@ -0,0 +1,739 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2024 Google, Inc +# + +"""Tests for boards.py""" + +import errno +import multiprocessing +import os +from pathlib import Path +import shutil +import tempfile +import time +import unittest +from unittest import mock + +from buildman import board +from buildman import boards +from buildman.boards import Extended +from u_boot_pylib import terminal +from u_boot_pylib import tools + + +BOARDS = [ + ['Active', 'arm', 'armv7', '', 'Tester', 'ARM Board 0', 'board0', ''], + ['Active', 'arm', 'armv7', '', 'Tester', 'ARM Board 1', 'board1', ''], + ['Active', 'powerpc', 'powerpc', '', 'Tester', 'PowerPC board 1', 'board2', ''], + ['Active', 'sandbox', 'sandbox', '', 'Tester', 'Sandbox board', 'board4', ''], +] + + +class TestBoards(unittest.TestCase): + """Test boards.py functionality""" + + def setUp(self): + self._base_dir = tempfile.mkdtemp() + self._output_dir = tempfile.mkdtemp() + self._git_dir = os.path.join(self._base_dir, 'src') + self._buildman_dir = os.path.dirname(os.path.realpath(__file__)) + self._test_dir = os.path.join(self._buildman_dir, 'test') + + # Set up some fake source files + shutil.copytree(self._test_dir, self._git_dir) + + # Avoid sending any output and clear all terminal output + terminal.set_print_test_mode() + terminal.get_print_test_lines() + + self._boards = boards.Boards() + for brd in BOARDS: + self._boards.add_board(board.Board(*brd)) + + def tearDown(self): + shutil.rmtree(self._base_dir) + shutil.rmtree(self._output_dir) + + def test_try_remove(self): + """Test try_remove() function""" + # Test removing a file that doesn't exist - should not raise + boards.try_remove('/nonexistent/path/to/file') + + # Test removing a file that does exist + fname = os.path.join(self._base_dir, 'test_remove') + tools.write_file(fname, b'test') + self.assertTrue(os.path.exists(fname)) + boards.try_remove(fname) + self.assertFalse(os.path.exists(fname)) + + def test_read_boards(self): + """Test Boards.read_boards() with various field counts""" + # Test normal boards.cfg file + boards_cfg = os.path.join(self._base_dir, 'boards.cfg') + content = '''# Comment line +Active arm armv7 - Tester ARM_Board_0 board0 config0 maint@test.com +Active powerpc ppc mpc85xx Tester PPC_Board_1 board2 config2 maint2@test.com + +''' + tools.write_file(boards_cfg, content.encode('utf-8')) + + brds = boards.Boards() + brds.read_boards(boards_cfg) + board_list = brds.get_list() + self.assertEqual(2, len(board_list)) + self.assertEqual('board0', board_list[0].target) + self.assertEqual('arm', board_list[0].arch) + self.assertEqual('', board_list[0].soc) # '-' converted to '' + self.assertEqual('mpc85xx', board_list[1].soc) + + # Test with fewer than 8 fields + boards_cfg = os.path.join(self._base_dir, 'boards_short.cfg') + content = '''Active arm armv7 - Tester Board target config +''' + tools.write_file(boards_cfg, content.encode('utf-8')) + brds = boards.Boards() + brds.read_boards(boards_cfg) + self.assertEqual(1, len(brds.get_list())) + + # Test with more than 8 fields (extra fields ignored) + boards_cfg = os.path.join(self._base_dir, 'boards_extra.cfg') + content = '''Active arm armv7 soc Tester Board target config maint extra +''' + tools.write_file(boards_cfg, content.encode('utf-8')) + brds = boards.Boards() + brds.read_boards(boards_cfg) + self.assertEqual('config', brds.get_list()[0].cfg_name) + + def test_boards_methods(self): + """Test Boards helper methods: get_dict, get_selected_names, find_by_target""" + brds = boards.Boards() + for brd in BOARDS: + brds.add_board(board.Board(*brd)) + + # Test get_dict() + board_dict = brds.get_dict() + self.assertEqual(4, len(board_dict)) + self.assertEqual('arm', board_dict['board0'].arch) + self.assertEqual('sandbox', board_dict['board4'].arch) + + # Test get_selected_names() + brds.select_boards(['arm']) + self.assertEqual(['board0', 'board1'], brds.get_selected_names()) + + # Test select_boards warning for missing board + brds2 = boards.Boards() + for brd in BOARDS: + brds2.add_board(board.Board(*brd)) + result, warnings = brds2.select_boards([], brds=['nonexistent', 'board0']) + self.assertEqual(1, len(warnings)) + self.assertIn('nonexistent', warnings[0]) + + # Test find_by_target() + found = brds.find_by_target('board0') + self.assertEqual('arm', found.arch) + + with terminal.capture() as (stdout, stderr): + with self.assertRaises(ValueError) as exc: + brds.find_by_target('nonexistent') + self.assertIn('nonexistent', str(exc.exception)) + + def test_kconfig_riscv(self): + """Test KconfigScanner riscv architecture detection""" + src = self._git_dir + kc_file = os.path.join(src, 'Kconfig') + orig_kc_data = tools.read_file(kc_file) + + riscv_kconfig = orig_kc_data + b''' + +config RISCV +\tbool + +config ARCH_RV32I +\tbool + +config TARGET_RISCV_BOARD +\tbool "RISC-V Board" +\tselect RISCV +\tdefault n + +if TARGET_RISCV_BOARD +config SYS_ARCH +\tdefault "riscv" + +config SYS_CPU +\tdefault "generic" + +config SYS_VENDOR +\tdefault "RiscVendor" + +config SYS_BOARD +\tdefault "RISC-V Board" + +config SYS_CONFIG_NAME +\tdefault "riscv_config" +endif +''' + tools.write_file(kc_file, riscv_kconfig) + + try: + scanner = boards.KconfigScanner(src) + defconfig = os.path.join(src, 'riscv64_defconfig') + tools.write_file(defconfig, 'CONFIG_TARGET_RISCV_BOARD=y\n', False) + + # Test riscv64 (no RV32I) + res, warnings = scanner.scan(defconfig, False) + self.assertEqual('riscv64', res['arch']) + + # Test riscv32 (with RV32I) + riscv32_kconfig = riscv_kconfig + b''' +config ARCH_RV32I +\tdefault y if TARGET_RISCV_BOARD +''' + tools.write_file(kc_file, riscv32_kconfig) + scanner = boards.KconfigScanner(src) + res, warnings = scanner.scan(defconfig, False) + self.assertEqual('riscv32', res['arch']) + finally: + tools.write_file(kc_file, orig_kc_data) + + def test_maintainers_commented(self): + """Test MaintainersDatabase with commented maintainer lines""" + src = self._git_dir + main = os.path.join(src, 'boards', 'board0', 'MAINTAINERS') + config_dir = os.path.join(src, 'configs') + orig_data = tools.read_file(main, binary=False) + + new_data = '#M: Commented Maintainer <comment@test.com>\n' + orig_data + tools.write_file(main, new_data, binary=False) + + try: + params_list, warnings = self._boards.build_board_list(config_dir, src) + self.assertEqual(2, len(params_list)) + finally: + tools.write_file(main, orig_data, binary=False) + + def test_ensure_board_list_options(self): + """Test ensure_board_list() with force and quiet flags""" + outfile = os.path.join(self._output_dir, 'test-boards-opts.cfg') + brds = boards.Boards() + + # Test force=False, quiet=False (normal generation) + with terminal.capture() as (stdout, stderr): + brds.ensure_board_list(outfile, jobs=1, force=False, quiet=False) + self.assertTrue(os.path.exists(outfile)) + + # Test force=True (regenerate even if current) + with terminal.capture() as (stdout, stderr): + brds.ensure_board_list(outfile, jobs=1, force=True, quiet=False) + self.assertTrue(os.path.exists(outfile)) + + # Test quiet=True (minimal output) + with terminal.capture() as (stdout, stderr): + brds.ensure_board_list(outfile, jobs=1, force=False, quiet=True) + self.assertNotIn('Checking', stdout.getvalue()) + + # Test quiet=True when up to date (no output) + with terminal.capture() as (stdout, stderr): + result = brds.ensure_board_list(outfile, jobs=1, force=False, + quiet=True) + self.assertTrue(result) + self.assertEqual('', stdout.getvalue()) + + def test_output_is_new_old_format(self): + """Test output_is_new() with old format containing Options""" + src = self._git_dir + config_dir = os.path.join(src, 'configs') + boards_cfg = os.path.join(self._base_dir, 'boards_old.cfg') + + content = b'''# +# List of boards +# +# Status, Arch, CPU, SoC, Vendor, Board, Target, Options, Maintainers + +Active arm armv7 - Tester Board board0 options maint +''' + tools.write_file(boards_cfg, content) + self.assertFalse(boards.output_is_new(boards_cfg, config_dir, src)) + + def test_maintainers_status(self): + """Test MaintainersDatabase.get_status() with various statuses""" + database = boards.MaintainersDatabase() + + # Test missing target + self.assertEqual('-', database.get_status('missing')) + self.assertIn("no status info for 'missing'", database.warnings[-1]) + + # Test 'Supported' maps to Active + database.database['test1'] = ('Supported', ['maint@test.com']) + self.assertEqual('Active', database.get_status('test1')) + + # Test 'Orphan' status + database.database['orphan'] = ('Orphan', ['maint@test.com']) + self.assertEqual('Orphan', database.get_status('orphan')) + + # Test unknown status + database.database['test2'] = ('Unknown Status', ['maint@test.com']) + self.assertEqual('-', database.get_status('test2')) + self.assertIn("unknown status for 'test2'", database.warnings[-1]) + + def test_expr_term_str(self): + """Test Expr and Term __str__() methods""" + expr = boards.Expr('arm.*') + self.assertEqual('arm.*', str(expr)) + + term = boards.Term() + term.add_expr('arm') + term.add_expr('cortex') + self.assertEqual('arm&cortex', str(term)) + + def test_kconfig_scanner_warnings(self): + """Test KconfigScanner.scan() TARGET_xxx warnings""" + src = self._git_dir + kc_file = os.path.join(src, 'Kconfig') + orig_kc_data = tools.read_file(kc_file) + + # Test missing TARGET_xxx warning + defconfig = os.path.join(src, 'configs', 'no_target_defconfig') + tools.write_file(defconfig, 'CONFIG_SYS_ARCH="arm"\n', False) + try: + scanner = boards.KconfigScanner(src) + res, warnings = scanner.scan(defconfig, warn_targets=True) + self.assertEqual(1, len(warnings)) + self.assertIn('No TARGET_NO_TARGET enabled', warnings[0]) + finally: + if os.path.exists(defconfig): + os.remove(defconfig) + + # Test duplicate TARGET_xxx warning + extra = b''' +config TARGET_BOARD0_DUP +\tbool "Duplicate target" +\tdefault y if TARGET_BOARD0 +''' + tools.write_file(kc_file, orig_kc_data + extra) + try: + scanner = boards.KconfigScanner(src) + defconfig = os.path.join(src, 'configs', 'board0_defconfig') + res, warnings = scanner.scan(defconfig, warn_targets=True) + self.assertEqual(1, len(warnings)) + self.assertIn('Duplicate TARGET_xxx', warnings[0]) + finally: + tools.write_file(kc_file, orig_kc_data) + + def test_scan_extended(self): + """Test scan_extended() for finding boards matching extended criteria""" + brds = boards.Boards() + + # Test with CONFIG-based selection (value=y) + ext = Extended( + name='test_ext', + desc='Test extended board', + fragments=['test_frag'], + targets=[['CONFIG_ARM', 'y']]) + + with mock.patch('qconfig.find_config') as mock_find, \ + mock.patch.object(tools, 'read_file', return_value='CONFIG_TEST=y'): + mock_find.return_value = {'board0', 'board1'} + result = brds.scan_extended(None, ext) + self.assertEqual({'board0', 'board1'}, result) + mock_find.assert_called_once_with(None, ['CONFIG_ARM']) + + # Test with CONFIG-based selection (value=n) + ext = Extended( + name='test_ext2', + desc='Test extended board 2', + fragments=['test_frag'], + targets=[['CONFIG_DEBUG', 'n']]) + + with mock.patch('qconfig.find_config') as mock_find, \ + mock.patch.object(tools, 'read_file', return_value=''): + mock_find.return_value = {'board2'} + result = brds.scan_extended(None, ext) + self.assertEqual({'board2'}, result) + mock_find.assert_called_once_with(None, ['~CONFIG_DEBUG']) + + # Test with CONFIG-based selection (specific value) + ext = Extended( + name='test_ext3', + desc='Test extended board 3', + fragments=['test_frag'], + targets=[['CONFIG_SYS_SOC', '"k3"']]) + + with mock.patch('qconfig.find_config') as mock_find, \ + mock.patch.object(tools, 'read_file', return_value=''): + mock_find.return_value = {'board4'} + result = brds.scan_extended(None, ext) + self.assertEqual({'board4'}, result) + mock_find.assert_called_once_with(None, ['CONFIG_SYS_SOC="k3"']) + + # Test with regex pattern - intersection of glob and find_config + ext = Extended( + name='test_ext4', + desc='Test extended board 4', + fragments=['test_frag'], + targets=[['regex', 'configs/board*_defconfig']]) + + with mock.patch('qconfig.find_config') as mock_find, \ + mock.patch.object(tools, 'read_file', return_value=''), \ + mock.patch('glob.glob') as mock_glob: + mock_glob.return_value = ['configs/board0_defconfig', + 'configs/board2_defconfig'] + mock_find.return_value = {'board0', 'board1', 'board2'} + result = brds.scan_extended(None, ext) + # Should be intersection: {board0, board2} & {board0, board1, board2} + self.assertEqual({'board0', 'board2'}, result) + + def test_parse_extended(self): + """Test parse_extended() for creating extended board entries""" + brds = boards.Boards() + for brd in BOARDS: + brds.add_board(board.Board(*brd)) + + # Create a .buildman file + buildman_file = os.path.join(self._base_dir, 'test.buildman') + content = '''name: test_acpi +desc: Test ACPI boards +fragment: acpi +targets: + CONFIG_ARM=y +''' + tools.write_file(buildman_file, content, binary=False) + + # Mock scan_extended to return specific boards + with mock.patch.object(brds, 'scan_extended') as mock_scan: + mock_scan.return_value = {'board0', 'board1'} + brds.parse_extended(None, buildman_file) + + # Check that new extended boards were added + board_list = brds.get_list() + # Original 4 boards + 2 extended boards + self.assertEqual(6, len(board_list)) + + # Find the extended boards + ext_boards = [b for b in board_list if ',' in b.target] + self.assertEqual(2, len(ext_boards)) + + # Check the extended board properties + ext_board = next(b for b in ext_boards if 'board0' in b.target) + self.assertEqual('test_acpi,board0', ext_board.target) + self.assertEqual('arm', ext_board.arch) + self.assertEqual('board0', ext_board.orig_target) + self.assertIsNotNone(ext_board.extended) + self.assertEqual('test_acpi', ext_board.extended.name) + + def test_try_remove_other_error(self): + """Test try_remove() re-raises non-ENOENT errors""" + with mock.patch('os.remove') as mock_remove: + # Simulate a permission error (not ENOENT) + err = OSError(errno.EACCES, 'Permission denied') + mock_remove.side_effect = err + with self.assertRaises(OSError) as exc: + boards.try_remove('/some/file') + self.assertEqual(errno.EACCES, exc.exception.errno) + + def test_output_is_new_other_error(self): + """Test output_is_new() re-raises non-ENOENT errors""" + with mock.patch('os.path.getctime') as mock_ctime: + err = OSError(errno.EACCES, 'Permission denied') + mock_ctime.side_effect = err + with self.assertRaises(OSError) as exc: + boards.output_is_new('/some/file', 'configs', '.') + self.assertEqual(errno.EACCES, exc.exception.errno) + + def test_output_is_new_hidden_files(self): + """Test output_is_new() skips hidden defconfig files""" + base = self._base_dir + src = self._git_dir + config_dir = os.path.join(src, 'configs') + + # Create boards.cfg + boards_cfg = os.path.join(base, 'boards_hidden.cfg') + content = b'''# +# List of boards +# Automatically generated by buildman/boards.py: don't edit +# +# Status, Arch, CPU, SoC, Vendor, Board, Target, Config, Maintainers + +Active arm armv7 - Tester Board board0 config0 maint +''' + tools.write_file(boards_cfg, content) + + # Create a hidden defconfig file (should be skipped) + hidden = os.path.join(config_dir, '.hidden_defconfig') + tools.write_file(hidden, b'# hidden') + + try: + # Touch boards.cfg to make it newer + time.sleep(0.02) + Path(boards_cfg).touch() + # Should return True (hidden file skipped) + self.assertTrue(boards.output_is_new(boards_cfg, config_dir, src)) + finally: + os.remove(hidden) + + def test_kconfig_scanner_destructor(self): + """Test KconfigScanner.__del__() cleans up leftover temp file""" + src = self._git_dir + scanner = boards.KconfigScanner(src) + + # Simulate a leftover temp file + tmpfile = os.path.join(self._base_dir, 'leftover.tmp') + tools.write_file(tmpfile, b'temp') + scanner._tmpfile = tmpfile + + # Delete the scanner - should clean up the temp file + del scanner + self.assertFalse(os.path.exists(tmpfile)) + + def test_kconfig_scanner_aarch64(self): + """Test KconfigScanner.scan() aarch64 fix-up""" + src = self._git_dir + kc_file = os.path.join(src, 'Kconfig') + orig_kc_data = tools.read_file(kc_file) + + # Add aarch64 board to Kconfig + aarch64_kconfig = orig_kc_data + b''' + +config TARGET_AARCH64_BOARD +\tbool "AArch64 Board" +\tdefault n + +if TARGET_AARCH64_BOARD +config SYS_ARCH +\tdefault "arm" + +config SYS_CPU +\tdefault "armv8" + +config SYS_VENDOR +\tdefault "Test" + +config SYS_BOARD +\tdefault "AArch64 Board" + +config SYS_CONFIG_NAME +\tdefault "aarch64_config" +endif +''' + tools.write_file(kc_file, aarch64_kconfig) + + try: + scanner = boards.KconfigScanner(src) + defconfig = os.path.join(src, 'aarch64_defconfig') + tools.write_file(defconfig, 'CONFIG_TARGET_AARCH64_BOARD=y\n', False) + res, warnings = scanner.scan(defconfig, False) + # Should be fixed up to aarch64 + self.assertEqual('aarch64', res['arch']) + finally: + tools.write_file(kc_file, orig_kc_data) + if os.path.exists(defconfig): + os.remove(defconfig) + + def test_read_boards_short_line(self): + """Test Boards.read_boards() pads short lines to 8 fields""" + boards_cfg = os.path.join(self._base_dir, 'boards_veryshort.cfg') + # Create a board with only 7 fields (missing maintainers) + content = '''Active arm armv7 soc Tester Board target +''' + tools.write_file(boards_cfg, content.encode('utf-8')) + + brds = boards.Boards() + brds.read_boards(boards_cfg) + board_list = brds.get_list() + self.assertEqual(1, len(board_list)) + # cfg_name should be empty string (padded) + self.assertEqual('', board_list[0].cfg_name) + + def test_ensure_board_list_up_to_date_message(self): + """Test ensure_board_list() shows up-to-date message""" + outfile = os.path.join(self._output_dir, 'test-boards-uptodate.cfg') + brds = boards.Boards() + + # First generate the file + with terminal.capture() as (stdout, stderr): + brds.ensure_board_list(outfile, jobs=1, force=False, quiet=False) + + # Run again - should say "up to date" + with terminal.capture() as (stdout, stderr): + result = brds.ensure_board_list(outfile, jobs=1, force=False, + quiet=False) + self.assertTrue(result) + self.assertIn('up to date', stdout.getvalue()) + + def test_ensure_board_list_warnings(self): + """Test ensure_board_list() prints warnings to stderr""" + outfile = os.path.join(self._output_dir, 'test-boards-warn.cfg') + brds = boards.Boards() + + # Mock build_board_list to return warnings + with mock.patch.object(brds, 'build_board_list') as mock_build: + mock_build.return_value = ([], ['WARNING: test warning']) + with terminal.capture() as (stdout, stderr): + result = brds.ensure_board_list(outfile, jobs=1, force=True, + quiet=False) + self.assertFalse(result) + self.assertIn('WARNING: test warning', stderr.getvalue()) + + def test_parse_all_extended(self): + """Test parse_all_extended() finds and parses .buildman files""" + brds = boards.Boards() + for brd in BOARDS: + brds.add_board(board.Board(*brd)) + + # Mock glob to return a .buildman file and parse_extended + with mock.patch('glob.glob') as mock_glob, \ + mock.patch.object(brds, 'parse_extended') as mock_parse: + mock_glob.return_value = ['configs/test.buildman'] + brds.parse_all_extended(None) + mock_glob.assert_called_once_with('configs/*.buildman') + mock_parse.assert_called_once_with(None, 'configs/test.buildman') + + def test_scan_extended_no_match_warning(self): + """Test scan_extended() warns when no configs match regex""" + brds = boards.Boards() + + ext = Extended( + name='test_ext', + desc='Test extended board', + fragments=['test_frag'], + targets=[['regex', 'nonexistent*_defconfig']]) + + with mock.patch('qconfig.find_config') as mock_find, \ + mock.patch.object(tools, 'read_file', return_value=''), \ + mock.patch('glob.glob') as mock_glob, \ + terminal.capture() as (stdout, stderr): + mock_glob.return_value = [] # No matches + mock_find.return_value = set() + result = brds.scan_extended(None, ext) + self.assertEqual(set(), result) + # Warning should be printed + self.assertIn('Warning', stdout.getvalue()) + + def test_kconfig_scanner_riscv_no_rv32i(self): + """Test KconfigScanner.scan() when ARCH_RV32I symbol doesn't exist""" + src = self._git_dir + kc_file = os.path.join(src, 'Kconfig') + orig_kc_data = tools.read_file(kc_file) + + # Add RISCV board WITHOUT defining ARCH_RV32I symbol + # This will cause syms.get('ARCH_RV32I') to return None, + # and accessing .str_value on None raises AttributeError + riscv_kconfig = orig_kc_data + b''' + +config RISCV +\tbool + +config TARGET_RISCV_TEST +\tbool "RISC-V Test Board" +\tdefault n + +if TARGET_RISCV_TEST +config SYS_ARCH +\tdefault "riscv" + +config SYS_CPU +\tdefault "generic" + +config SYS_VENDOR +\tdefault "Test" + +config SYS_BOARD +\tdefault "RISCV Test" + +config SYS_CONFIG_NAME +\tdefault "riscv_test" +endif +''' + tools.write_file(kc_file, riscv_kconfig) + defconfig = os.path.join(src, 'riscv_test_defconfig') + + try: + # Create defconfig that enables the riscv board + tools.write_file(defconfig, 'CONFIG_TARGET_RISCV_TEST=y\n', False) + + scanner = boards.KconfigScanner(src) + res, warnings = scanner.scan(defconfig, False) + + # Should default to riscv64 when ARCH_RV32I lookup fails + self.assertEqual('riscv64', res['arch']) + finally: + tools.write_file(kc_file, orig_kc_data) + if os.path.exists(defconfig): + os.remove(defconfig) + + def test_scan_defconfigs_for_multiprocess(self): + """Test scan_defconfigs_for_multiprocess() function directly""" + src = self._git_dir + config_dir = os.path.join(src, 'configs') + + # Get a list of defconfigs + defconfigs = [os.path.join(config_dir, 'board0_defconfig')] + + # Create a queue and call the function + queue = multiprocessing.Queue() + boards.Boards.scan_defconfigs_for_multiprocess(src, queue, defconfigs, + False) + + # Get the result from the queue + result = queue.get(timeout=5) + params, warnings = result + self.assertEqual('board0', params['target']) + self.assertEqual('arm', params['arch']) + + def test_scan_defconfigs_hidden_files(self): + """Test scan_defconfigs() skips hidden defconfig files""" + src = self._git_dir + config_dir = os.path.join(src, 'configs') + + # Create a hidden defconfig + hidden = os.path.join(config_dir, '.hidden_defconfig') + tools.write_file(hidden, b'CONFIG_TARGET_BOARD0=y') + + try: + brds = boards.Boards() + params_list, warnings = brds.scan_defconfigs(config_dir, src, 1) + + # Hidden file should not be in results + targets = [p['target'] for p in params_list] + self.assertNotIn('.hidden', targets) + # But regular boards should be there + self.assertIn('board0', targets) + finally: + os.remove(hidden) + + def test_maintainers_n_tag_non_configs_path(self): + """Test MaintainersDatabase N: tag skips non-configs paths""" + src = self._git_dir + + # Create a MAINTAINERS file with N: tag + maintainers_file = os.path.join(src, 'MAINTAINERS_TEST') + maintainers_content = '''BOARD0 +M: Test <test@test.com> +S: Active +N: .* +''' + tools.write_file(maintainers_file, maintainers_content, binary=False) + + # Mock os.walk to return a path that doesn't start with 'configs/' + # when walking the configs directory. This tests line 443. + def mock_walk(path): + # Return paths with 'configs/' prefix (normal) and without (edge case) + yield (os.path.join(src, 'configs'), [], ['board0_defconfig']) + # This path will have 'other/' prefix after srcdir removal + yield (os.path.join(src, 'other'), [], ['fred_defconfig']) + + try: + database = boards.MaintainersDatabase() + with mock.patch('os.walk', mock_walk): + database.parse_file(src, maintainers_file) + + # board0 should be found (path starts with configs/) + # fred should be skipped (path starts with other/, not configs/) + self.assertIn('board0', database.database) + self.assertNotIn('fred', database.database) + finally: + os.remove(maintainers_file) + + +if __name__ == '__main__': + unittest.main() diff --git a/tools/pickman/README.rst b/tools/pickman/README.rst index 8e309af8837..37c3aac038f 100644 --- a/tools/pickman/README.rst +++ b/tools/pickman/README.rst @@ -124,34 +124,66 @@ Pickman will add ``[skipped]`` to the MR title. Skipped MRs: - Don't block the ``step`` or ``poll`` commands from proceeding - Can be unskipped by commenting ``pickman unskip`` -CI Pipelines ------------- +Already-Applied Detection +------------------------- + +Sometimes commits have already been applied to the target branch through a +different path (e.g., directly merged or cherry-picked with different hashes). +Pickman's Claude agent detects this situation automatically. + +**How it works** + +When the first cherry-pick fails with conflicts, the agent checks if the +commits are already present in the target branch by searching for matching +commit subjects:: + + git log --oneline ci/master --grep="<subject>" -1 + +If all commits in the set are found (same subjects, different hashes), the +agent: -Pickman manages CI pipelines to avoid unnecessary runs while ensuring changes -are properly verified. +1. Aborts the cherry-pick +2. Writes a signal file (``.pickman-signal``) with status ``already_applied`` +3. Reports the situation -**Initial MR creation** +**What pickman does** + +When pickman detects the ``already_applied`` signal: + +1. Marks all commits as 'skipped' in the database +2. Updates the source position to advance past these commits +3. Creates an MR with ``[skipped]`` prefix to record the attempt +4. The MR description explains that commits were already applied + +This ensures: + +- There's a record of what was attempted +- The source position advances so the next ``poll`` iteration processes new + commits +- No manual intervention is required to continue + +CI Pipelines +------------ -When creating a new MR (via ``apply -p`` or ``step``), pickman pushes the -branch with ``-o ci.skip``. This skips the push pipeline because GitLab -automatically triggers an MR pipeline when the merge request is created. -Without this, two pipelines would run: one for the push and one for the MR. +Pickman manages CI pipelines to avoid unnecessary duplicate runs. GitLab +automatically triggers an MR pipeline whenever the source branch is updated, +so pickman skips the push pipeline to avoid running two pipelines. -**Review comment handling** +**How it works** -When pushing changes after addressing review comments (via ``review``, -``step``, or ``poll``), pickman does NOT skip the pipeline. A new pipeline -is needed to verify that the changes made in response to review feedback -are correct. +When pushing a branch (for new MRs or updates), pickman uses ``-o ci.skip`` +to skip the push pipeline. GitLab then triggers an MR pipeline when it +detects the branch update on the merge request. This ensures exactly one +pipeline runs for each push. **Summary** -=============================== ================ ========================= +=============================== ================ ============================== Action Pipeline Skipped Reason -=============================== ================ ========================= -Initial branch push for new MR Yes MR creation triggers one -Push after review changes No Need to verify changes -=============================== ================ ========================= +=============================== ================ ============================== +Initial branch push for new MR Yes MR creation triggers pipeline +Push after rebase/review Yes MR update triggers pipeline +=============================== ================ ============================== Usage ----- diff --git a/tools/pickman/agent.py b/tools/pickman/agent.py index f3cec2e43bd..4570312a97c 100644 --- a/tools/pickman/agent.py +++ b/tools/pickman/agent.py @@ -16,6 +16,14 @@ sys.path.insert(0, os.path.join(our_path, '..')) # pylint: disable=wrong-import-position,import-error from u_boot_pylib import tout +# Signal file for agent to communicate status back to pickman +SIGNAL_FILE = '.pickman-signal' + +# Signal status codes +SIGNAL_SUCCESS = 'success' +SIGNAL_ALREADY_APPLIED = 'already_applied' +SIGNAL_CONFLICT = 'conflict' + # Check if claude_agent_sdk is available try: from claude_agent_sdk import query, ClaudeAgentOptions @@ -55,12 +63,20 @@ async def run(commits, source, branch_name, repo_path=None): if repo_path is None: repo_path = os.getcwd() + # Remove any stale signal file from previous runs + signal_path = os.path.join(repo_path, SIGNAL_FILE) + if os.path.exists(signal_path): + os.remove(signal_path) + # Build commit list for the prompt commit_list = '\n'.join( f' - {short_hash}: {subject}' for _, short_hash, subject in commits ) + # Get full hash of last commit for signal file + last_commit_hash = commits[-1][0] + prompt = f"""Cherry-pick the following commits from {source} branch: {commit_list} @@ -94,6 +110,20 @@ Important: - Do not force push or modify history - If cherry-pick fails, run 'git cherry-pick --abort' - NEVER skip merge commits - always use --allow-empty to preserve them + +CRITICAL - Detecting Already-Applied Commits: +If the FIRST cherry-pick fails with conflicts, BEFORE aborting, check if the commits +are already present in ci/master with different hashes. Do this by searching for +commit subjects in ci/master: + git log --oneline ci/master --grep="<subject>" -1 +If ALL commits are already in ci/master (same subjects, different hashes), this means +the series was already applied via a different path. In this case: +1. Abort the cherry-pick: git cherry-pick --abort +2. Delete the branch: git branch -D {branch_name} +3. Write a signal file to indicate this status: + echo "already_applied" > {SIGNAL_FILE} + echo "{last_commit_hash}" >> {SIGNAL_FILE} +4. Report that all {len(commits)} commits are already present in ci/master """ options = ClaudeAgentOptions( @@ -119,6 +149,39 @@ Important: return False, '\n\n'.join(conversation_log) +def read_signal_file(repo_path=None): + """Read and remove the signal file if it exists + + Args: + repo_path (str): path to repository (defaults to current directory) + + Returns: + tuple: (status, last_commit) where status is the signal status code + (e.g., 'already_applied') and last_commit is the commit hash, + or (None, None) if no signal file exists + """ + if repo_path is None: + repo_path = os.getcwd() + + signal_path = os.path.join(repo_path, SIGNAL_FILE) + if not os.path.exists(signal_path): + return None, None + + try: + with open(signal_path, 'r', encoding='utf-8') as fhandle: + lines = fhandle.read().strip().split('\n') + status = lines[0] if lines else None + last_commit = lines[1] if len(lines) > 1 else None + + # Remove the signal file after reading + os.remove(signal_path) + + return status, last_commit + except (IOError, OSError) as exc: + tout.warning(f'Failed to read signal file: {exc}') + return None, None + + def cherry_pick_commits(commits, source, branch_name, repo_path=None): """Synchronous wrapper for running the cherry-pick agent @@ -234,8 +297,8 @@ Steps to follow: 4. Run 'buildman -L --board sandbox -w -o /tmp/pickman' to verify the build 5. Create a local branch with suffix '-v2' (or increment: -v3, -v4, etc.) 6. Force push to the ORIGINAL remote branch to update the MR: - ./tools/pickman/pickman push-branch {branch_name} -r {remote} -f --run-ci - (--run-ci triggers a pipeline to verify the rebased changes) + ./tools/pickman/pickman push-branch {branch_name} -r {remote} -f + (GitLab automatically triggers an MR pipeline when the branch is updated) 7. Report what was done and what reply should be posted to the MR Important: @@ -243,7 +306,7 @@ Important: - If a comment is unclear or cannot be addressed, note this in your report - Local branch: {branch_name}-v2 (or -v3, -v4 etc.) - Remote push: always to '{branch_name}' to update the existing MR -- Always use --run-ci when pushing rebases/updates to trigger verification +- Do NOT update the MR title - it should remain as originally set """ diff --git a/tools/pickman/control.py b/tools/pickman/control.py index c06b8abea17..b14c830f1d4 100644 --- a/tools/pickman/control.py +++ b/tools/pickman/control.py @@ -683,6 +683,72 @@ def prepare_apply(dbs, source, branch): return ApplyInfo(commits, branch_name, original_branch, merge_found), 0 +# pylint: disable=too-many-arguments +def handle_already_applied(dbs, source, commits, branch_name, conv_log, args, + signal_commit): + """Handle the case where commits are already applied to the target branch + + Creates an MR with [skip] prefix to record the attempt and updates the + source position in the database. + + Args: + dbs (Database): Database instance + source (str): Source branch name + commits (list): List of CommitInfo namedtuples + branch_name (str): Branch name that was attempted + conv_log (str): Conversation log from the agent + args (Namespace): Parsed arguments with 'push', 'remote', 'target' + signal_commit (str): Last commit hash from signal file + + Returns: + int: 0 on success, 1 on failure + """ + tout.info('Commits already applied to target branch - creating skip MR') + + # Mark commits as 'skipped' in database + for commit in commits: + dbs.commit_set_status(commit.hash, 'skipped') + dbs.commit() + + # Update source position to the last commit (or signal_commit if provided) + last_hash = signal_commit if signal_commit else commits[-1].hash + dbs.source_set(source, last_hash) + dbs.commit() + tout.info(f"Updated source '{source}' to {last_hash[:12]}") + + # Push and create MR with [skip] prefix if requested + if args.push: + remote = args.remote + target = args.target + + # Create a skip branch from ci/master (no changes) + try: + run_git(['checkout', '-b', branch_name, f'{remote}/{target}']) + except Exception: # pylint: disable=broad-except + # Branch may already exist from failed attempt + try: + run_git(['checkout', branch_name]) + except Exception: # pylint: disable=broad-except + tout.error(f'Could not create/checkout branch {branch_name}') + return 1 + + # Use merge commit subject as title with [skip] prefix + title = f'{SKIPPED_TAG} [pickman] {commits[-1].subject}' + summary = format_history_summary(source, commits, branch_name) + description = (f'{summary}\n\n' + f'**Status:** Commits already applied to {target} ' + f'with different hashes.\n\n' + f'### Conversation log\n{conv_log}') + + mr_url = gitlab_api.push_and_create_mr( + remote, branch_name, target, title, description + ) + if not mr_url: + return 1 + + return 0 + + def execute_apply(dbs, source, commits, branch_name, args): # pylint: disable=too-many-locals """Execute the apply operation: run agent, update database, push MR @@ -709,6 +775,13 @@ def execute_apply(dbs, source, commits, branch_name, args): # pylint: disable=t success, conv_log = agent.cherry_pick_commits(commit_tuples, source, branch_name) + # Check for signal file from agent + signal_status, signal_commit = agent.read_signal_file() + if signal_status == agent.SIGNAL_ALREADY_APPLIED: + ret = handle_already_applied(dbs, source, commits, branch_name, + conv_log, args, signal_commit) + return ret, False, conv_log + # Verify the branch actually exists - agent may have aborted and deleted it if success: try: diff --git a/tools/pickman/ftest.py b/tools/pickman/ftest.py index ce0f61241f7..9d075586c76 100644 --- a/tools/pickman/ftest.py +++ b/tools/pickman/ftest.py @@ -2736,6 +2736,88 @@ class TestExecuteApply(unittest.TestCase): self.assertEqual(commit_rec[6], 'conflict') dbs.close() + def test_execute_apply_already_applied(self): + """Test execute_apply when agent detects commits already applied.""" + with terminal.capture(): + dbs = database.Database(self.db_path) + dbs.start() + dbs.source_set('us/next', 'abc123') + dbs.commit() + + commits = [control.CommitInfo('ggg777', 'ggg777g', 'Test commit', + 'Author'), + control.CommitInfo('hhh888', 'hhh888h', 'Merge commit', + 'Author')] + args = argparse.Namespace(push=False) + + # Agent returns success but leaves signal file + with mock.patch.object(control.agent, 'cherry_pick_commits', + return_value=(True, 'already applied log')): + with mock.patch.object(control.agent, 'read_signal_file', + return_value=(agent.SIGNAL_ALREADY_APPLIED, + 'hhh888')): + ret, success, _ = control.execute_apply( + dbs, 'us/next', commits, 'cherry-branch', args) + + # Should return success (skip MR created), but success=False + self.assertEqual(ret, 0) + self.assertFalse(success) + + # Check commits are marked as skipped + commit_rec = dbs.commit_get('ggg777') + self.assertEqual(commit_rec[6], 'skipped') + commit_rec = dbs.commit_get('hhh888') + self.assertEqual(commit_rec[6], 'skipped') + + # Check source was updated + source_commit = dbs.source_get('us/next') + self.assertEqual(source_commit, 'hhh888') + dbs.close() + + +class TestSignalFile(unittest.TestCase): + """Tests for signal file handling.""" + + def setUp(self): + """Set up test fixtures.""" + self.test_dir = tempfile.mkdtemp() + self.signal_path = os.path.join(self.test_dir, agent.SIGNAL_FILE) + + def tearDown(self): + """Clean up test fixtures.""" + if os.path.exists(self.signal_path): + os.unlink(self.signal_path) + os.rmdir(self.test_dir) + + def test_read_signal_file_not_exists(self): + """Test read_signal_file when file doesn't exist.""" + status, commit = agent.read_signal_file(self.test_dir) + self.assertIsNone(status) + self.assertIsNone(commit) + + def test_read_signal_file_already_applied(self): + """Test read_signal_file with already_applied status.""" + with open(self.signal_path, 'w', encoding='utf-8') as fhandle: + fhandle.write('already_applied\nabc123def456\n') + + status, commit = agent.read_signal_file(self.test_dir) + self.assertEqual(status, 'already_applied') + self.assertEqual(commit, 'abc123def456') + + # File should be removed after reading + self.assertFalse(os.path.exists(self.signal_path)) + + def test_read_signal_file_status_only(self): + """Test read_signal_file with only status line.""" + with open(self.signal_path, 'w', encoding='utf-8') as fhandle: + fhandle.write('conflict\n') + + status, commit = agent.read_signal_file(self.test_dir) + self.assertEqual(status, 'conflict') + self.assertIsNone(commit) + + self.assertFalse(os.path.exists(self.signal_path)) + class TestGetNextCommitsEmptyLine(unittest.TestCase): """Tests for get_next_commits with empty lines.""" -- 2.43.0