Message ID | 20250210131826.220318-5-patrice.chotard@foss.st.com (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | Add STM32MP25 SPI NOR support | expand |
Hi, kernel test robot noticed the following build warnings: [auto build test WARNING on broonie-spi/for-next] [also build test WARNING on atorgue-stm32/stm32-next krzk-mem-ctrl/for-next linus/master v6.14-rc2 next-20250210] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/patrice-chotard-foss-st-com/dt-bindings-spi-Add-STM32-OSPI-controller/20250210-212554 base: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git for-next patch link: https://lore.kernel.org/r/20250210131826.220318-5-patrice.chotard%40foss.st.com patch subject: [PATCH v3 4/8] memory: Add STM32 Octo Memory Manager driver config: m68k-allyesconfig (https://download.01.org/0day-ci/archive/20250212/202502120005.pznVoB0E-lkp@intel.com/config) compiler: m68k-linux-gcc (GCC) 14.2.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250212/202502120005.pznVoB0E-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202502120005.pznVoB0E-lkp@intel.com/ All warnings (new ones prefixed by >>): In file included from include/linux/device.h:15, from include/linux/platform_device.h:13, from include/linux/bus/stm32_firewall_device.h:10, from drivers/memory/stm32_omm.c:7: drivers/memory/stm32_omm.c: In function 'stm32_omm_set_amcr': >> drivers/memory/stm32_omm.c:82:38: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 3 has type 'resource_size_t' {aka 'unsigned int'} [-Wformat=] 82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n", | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap' 110 | _p_func(dev, fmt, ##__VA_ARGS__); \ | ^~~ include/linux/dev_printk.h:154:56: note: in expansion of macro 'dev_fmt' 154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__) | ^~~~~~~ drivers/memory/stm32_omm.c:82:25: note: in expansion of macro 'dev_err' 82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n", | ^~~~~~~ drivers/memory/stm32_omm.c:82:45: note: format string is defined here 82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n", | ~~~^ | | | long long unsigned int | %x drivers/memory/stm32_omm.c:82:38: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 4 has type 'resource_size_t' {aka 'unsigned int'} [-Wformat=] 82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n", | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap' 110 | _p_func(dev, fmt, ##__VA_ARGS__); \ | ^~~ include/linux/dev_printk.h:154:56: note: in expansion of macro 'dev_fmt' 154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__) | ^~~~~~~ drivers/memory/stm32_omm.c:82:25: note: in expansion of macro 'dev_err' 82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n", | ^~~~~~~ drivers/memory/stm32_omm.c:82:52: note: format string is defined here 82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n", | ~~~^ | | | long long unsigned int | %x drivers/memory/stm32_omm.c:82:38: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 5 has type 'resource_size_t' {aka 'unsigned int'} [-Wformat=] 82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n", | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap' 110 | _p_func(dev, fmt, ##__VA_ARGS__); \ | ^~~ include/linux/dev_printk.h:154:56: note: in expansion of macro 'dev_fmt' 154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__) | ^~~~~~~ drivers/memory/stm32_omm.c:82:25: note: in expansion of macro 'dev_err' 82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n", | ^~~~~~~ drivers/memory/stm32_omm.c:82:80: note: format string is defined here 82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n", | ~~~^ | | | long long unsigned int | %x drivers/memory/stm32_omm.c:82:38: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 6 has type 'resource_size_t' {aka 'unsigned int'} [-Wformat=] 82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n", | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap' 110 | _p_func(dev, fmt, ##__VA_ARGS__); \ | ^~~ include/linux/dev_printk.h:154:56: note: in expansion of macro 'dev_fmt' 154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__) | ^~~~~~~ drivers/memory/stm32_omm.c:82:25: note: in expansion of macro 'dev_err' 82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n", | ^~~~~~~ drivers/memory/stm32_omm.c:82:87: note: format string is defined here 82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n", | ~~~^ | | | long long unsigned int | %x drivers/memory/stm32_omm.c:96:46: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 3 has type 'resource_size_t' {aka 'unsigned int'} [-Wformat=] 96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n", | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap' 110 | _p_func(dev, fmt, ##__VA_ARGS__); \ | ^~~ include/linux/dev_printk.h:154:56: note: in expansion of macro 'dev_fmt' 154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__) | ^~~~~~~ drivers/memory/stm32_omm.c:96:33: note: in expansion of macro 'dev_err' 96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n", | ^~~~~~~ drivers/memory/stm32_omm.c:96:53: note: format string is defined here 96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n", | ~~~^ | | | long long unsigned int | %x drivers/memory/stm32_omm.c:96:46: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 4 has type 'resource_size_t' {aka 'unsigned int'} [-Wformat=] 96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n", | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap' 110 | _p_func(dev, fmt, ##__VA_ARGS__); \ | ^~~ include/linux/dev_printk.h:154:56: note: in expansion of macro 'dev_fmt' 154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__) | ^~~~~~~ drivers/memory/stm32_omm.c:96:33: note: in expansion of macro 'dev_err' 96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n", vim +82 drivers/memory/stm32_omm.c 44 45 static int stm32_omm_set_amcr(struct device *dev, bool set) 46 { 47 struct stm32_omm *omm = dev_get_drvdata(dev); 48 struct regmap *syscfg_regmap; 49 struct device_node *node; 50 struct resource res, res1; 51 resource_size_t mm_ospi2_size = 0; 52 static const char * const mm_name[] = { "ospi1", "ospi2" }; 53 u32 amcr_base, amcr_mask; 54 int ret, i, idx; 55 unsigned int amcr, read_amcr; 56 57 for (i = 0; i < omm->nb_child; i++) { 58 idx = of_property_match_string(dev->of_node, 59 "memory-region-names", 60 mm_name[i]); 61 if (idx < 0) 62 continue; 63 64 /* res1 only used on second loop iteration */ 65 res1.start = res.start; 66 res1.end = res.end; 67 68 node = of_parse_phandle(dev->of_node, "memory-region", idx); 69 if (!node) 70 continue; 71 72 ret = of_address_to_resource(node, 0, &res); 73 if (ret) { 74 dev_err(dev, "unable to resolve memory region\n"); 75 return ret; 76 } 77 78 /* check that memory region fits inside OMM memory map area */ 79 if (!resource_contains(omm->mm_res, &res)) { 80 dev_err(dev, "%s doesn't fit inside OMM memory map area\n", 81 mm_name[i]); > 82 dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n", 83 res.start, res.end, 84 omm->mm_res->start, omm->mm_res->end); 85 86 return -EFAULT; 87 } 88 89 if (i == 1) { 90 mm_ospi2_size = resource_size(&res); 91 92 /* check that OMM memory region 1 doesn't overlap memory region 2 */ 93 if (resource_overlaps(&res, &res1)) { 94 dev_err(dev, "OMM memory-region %s overlaps memory region %s\n", 95 mm_name[0], mm_name[1]); 96 dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n", 97 res1.start, res1.end, res.start, res.end); 98 99 return -EFAULT; 100 } 101 } 102 } 103 104 syscfg_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "st,syscfg-amcr"); 105 if (IS_ERR(syscfg_regmap)) { 106 dev_err(dev, "Failed to get st,syscfg-amcr property\n"); 107 return PTR_ERR(syscfg_regmap); 108 } 109 110 ret = of_property_read_u32_index(dev->of_node, "st,syscfg-amcr", 1, 111 &amcr_base); 112 if (ret) 113 return ret; 114 115 ret = of_property_read_u32_index(dev->of_node, "st,syscfg-amcr", 2, 116 &amcr_mask); 117 if (ret) 118 return ret; 119 120 amcr = mm_ospi2_size / SZ_64M; 121 122 if (set) 123 regmap_update_bits(syscfg_regmap, amcr_base, amcr_mask, amcr); 124 125 /* read AMCR and check coherency with memory-map areas defined in DT */ 126 regmap_read(syscfg_regmap, amcr_base, &read_amcr); 127 read_amcr = read_amcr >> (ffs(amcr_mask) - 1); 128 129 if (amcr != read_amcr) { 130 dev_err(dev, "AMCR value not coherent with DT memory-map areas\n"); 131 ret = -EINVAL; 132 } 133 134 return ret; 135 } 136
Hi, kernel test robot noticed the following build errors: [auto build test ERROR on broonie-spi/for-next] [also build test ERROR on atorgue-stm32/stm32-next krzk-mem-ctrl/for-next linus/master v6.14-rc2 next-20250210] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/patrice-chotard-foss-st-com/dt-bindings-spi-Add-STM32-OSPI-controller/20250210-212554 base: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git for-next patch link: https://lore.kernel.org/r/20250210131826.220318-5-patrice.chotard%40foss.st.com patch subject: [PATCH v3 4/8] memory: Add STM32 Octo Memory Manager driver config: hexagon-allmodconfig (https://download.01.org/0day-ci/archive/20250212/202502120118.27fjrRqt-lkp@intel.com/config) compiler: clang version 21.0.0git (https://github.com/llvm/llvm-project 6807164500e9920638e2ab0cdb4bf8321d24f8eb) reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250212/202502120118.27fjrRqt-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202502120118.27fjrRqt-lkp@intel.com/ All error/warnings (new ones prefixed by >>): >> drivers/memory/stm32_omm.c:83:5: warning: format specifies type 'unsigned long long' but the argument has type 'resource_size_t' (aka 'unsigned int') [-Wformat] 82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n", | ~~~~ | %x 83 | res.start, res.end, | ^~~~~~~~~ include/linux/dev_printk.h:154:65: note: expanded from macro 'dev_err' 154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__) | ~~~ ^~~~~~~~~~~ include/linux/dev_printk.h:110:23: note: expanded from macro 'dev_printk_index_wrap' 110 | _p_func(dev, fmt, ##__VA_ARGS__); \ | ~~~ ^~~~~~~~~~~ drivers/memory/stm32_omm.c:83:16: warning: format specifies type 'unsigned long long' but the argument has type 'resource_size_t' (aka 'unsigned int') [-Wformat] 82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n", | ~~~~ | %x 83 | res.start, res.end, | ^~~~~~~ include/linux/dev_printk.h:154:65: note: expanded from macro 'dev_err' 154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__) | ~~~ ^~~~~~~~~~~ include/linux/dev_printk.h:110:23: note: expanded from macro 'dev_printk_index_wrap' 110 | _p_func(dev, fmt, ##__VA_ARGS__); \ | ~~~ ^~~~~~~~~~~ drivers/memory/stm32_omm.c:84:5: warning: format specifies type 'unsigned long long' but the argument has type 'resource_size_t' (aka 'unsigned int') [-Wformat] 82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n", | ~~~~ | %x 83 | res.start, res.end, 84 | omm->mm_res->start, omm->mm_res->end); | ^~~~~~~~~~~~~~~~~~ include/linux/dev_printk.h:154:65: note: expanded from macro 'dev_err' 154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__) | ~~~ ^~~~~~~~~~~ include/linux/dev_printk.h:110:23: note: expanded from macro 'dev_printk_index_wrap' 110 | _p_func(dev, fmt, ##__VA_ARGS__); \ | ~~~ ^~~~~~~~~~~ drivers/memory/stm32_omm.c:84:25: warning: format specifies type 'unsigned long long' but the argument has type 'resource_size_t' (aka 'unsigned int') [-Wformat] 82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n", | ~~~~ | %x 83 | res.start, res.end, 84 | omm->mm_res->start, omm->mm_res->end); | ^~~~~~~~~~~~~~~~ include/linux/dev_printk.h:154:65: note: expanded from macro 'dev_err' 154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__) | ~~~ ^~~~~~~~~~~ include/linux/dev_printk.h:110:23: note: expanded from macro 'dev_printk_index_wrap' 110 | _p_func(dev, fmt, ##__VA_ARGS__); \ | ~~~ ^~~~~~~~~~~ drivers/memory/stm32_omm.c:97:6: warning: format specifies type 'unsigned long long' but the argument has type 'resource_size_t' (aka 'unsigned int') [-Wformat] 96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n", | ~~~~ | %x 97 | res1.start, res1.end, res.start, res.end); | ^~~~~~~~~~ include/linux/dev_printk.h:154:65: note: expanded from macro 'dev_err' 154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__) | ~~~ ^~~~~~~~~~~ include/linux/dev_printk.h:110:23: note: expanded from macro 'dev_printk_index_wrap' 110 | _p_func(dev, fmt, ##__VA_ARGS__); \ | ~~~ ^~~~~~~~~~~ drivers/memory/stm32_omm.c:97:18: warning: format specifies type 'unsigned long long' but the argument has type 'resource_size_t' (aka 'unsigned int') [-Wformat] 96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n", | ~~~~ | %x 97 | res1.start, res1.end, res.start, res.end); | ^~~~~~~~ include/linux/dev_printk.h:154:65: note: expanded from macro 'dev_err' 154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__) | ~~~ ^~~~~~~~~~~ include/linux/dev_printk.h:110:23: note: expanded from macro 'dev_printk_index_wrap' 110 | _p_func(dev, fmt, ##__VA_ARGS__); \ | ~~~ ^~~~~~~~~~~ drivers/memory/stm32_omm.c:97:28: warning: format specifies type 'unsigned long long' but the argument has type 'resource_size_t' (aka 'unsigned int') [-Wformat] 96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n", | ~~~~ | %x 97 | res1.start, res1.end, res.start, res.end); | ^~~~~~~~~ include/linux/dev_printk.h:154:65: note: expanded from macro 'dev_err' 154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__) | ~~~ ^~~~~~~~~~~ include/linux/dev_printk.h:110:23: note: expanded from macro 'dev_printk_index_wrap' 110 | _p_func(dev, fmt, ##__VA_ARGS__); \ | ~~~ ^~~~~~~~~~~ drivers/memory/stm32_omm.c:97:39: warning: format specifies type 'unsigned long long' but the argument has type 'resource_size_t' (aka 'unsigned int') [-Wformat] 96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n", | ~~~~ | %x 97 | res1.start, res1.end, res.start, res.end); | ^~~~~~~ include/linux/dev_printk.h:154:65: note: expanded from macro 'dev_err' 154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__) | ~~~ ^~~~~~~~~~~ include/linux/dev_printk.h:110:23: note: expanded from macro 'dev_printk_index_wrap' 110 | _p_func(dev, fmt, ##__VA_ARGS__); \ | ~~~ ^~~~~~~~~~~ >> drivers/memory/stm32_omm.c:224:14: error: call to undeclared function 'FIELD_PREP'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] 224 | req2ack = FIELD_PREP(CR_REQ2ACK_MASK, req2ack); | ^ drivers/memory/stm32_omm.c:239:14: error: call to undeclared function 'FIELD_PREP'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] 239 | omm->cr |= FIELD_PREP(CR_MUXENMODE_MASK, mux); | ^ drivers/memory/stm32_omm.c:246:14: error: call to undeclared function 'FIELD_PREP'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] 246 | omm->cr |= FIELD_PREP(CR_CSSEL_OVR_MASK, cssel_ovr); | ^ 8 warnings and 3 errors generated. vim +/FIELD_PREP +224 drivers/memory/stm32_omm.c 44 45 static int stm32_omm_set_amcr(struct device *dev, bool set) 46 { 47 struct stm32_omm *omm = dev_get_drvdata(dev); 48 struct regmap *syscfg_regmap; 49 struct device_node *node; 50 struct resource res, res1; 51 resource_size_t mm_ospi2_size = 0; 52 static const char * const mm_name[] = { "ospi1", "ospi2" }; 53 u32 amcr_base, amcr_mask; 54 int ret, i, idx; 55 unsigned int amcr, read_amcr; 56 57 for (i = 0; i < omm->nb_child; i++) { 58 idx = of_property_match_string(dev->of_node, 59 "memory-region-names", 60 mm_name[i]); 61 if (idx < 0) 62 continue; 63 64 /* res1 only used on second loop iteration */ 65 res1.start = res.start; 66 res1.end = res.end; 67 68 node = of_parse_phandle(dev->of_node, "memory-region", idx); 69 if (!node) 70 continue; 71 72 ret = of_address_to_resource(node, 0, &res); 73 if (ret) { 74 dev_err(dev, "unable to resolve memory region\n"); 75 return ret; 76 } 77 78 /* check that memory region fits inside OMM memory map area */ 79 if (!resource_contains(omm->mm_res, &res)) { 80 dev_err(dev, "%s doesn't fit inside OMM memory map area\n", 81 mm_name[i]); 82 dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n", > 83 res.start, res.end, 84 omm->mm_res->start, omm->mm_res->end); 85 86 return -EFAULT; 87 } 88 89 if (i == 1) { 90 mm_ospi2_size = resource_size(&res); 91 92 /* check that OMM memory region 1 doesn't overlap memory region 2 */ 93 if (resource_overlaps(&res, &res1)) { 94 dev_err(dev, "OMM memory-region %s overlaps memory region %s\n", 95 mm_name[0], mm_name[1]); 96 dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n", 97 res1.start, res1.end, res.start, res.end); 98 99 return -EFAULT; 100 } 101 } 102 } 103 104 syscfg_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "st,syscfg-amcr"); 105 if (IS_ERR(syscfg_regmap)) { 106 dev_err(dev, "Failed to get st,syscfg-amcr property\n"); 107 return PTR_ERR(syscfg_regmap); 108 } 109 110 ret = of_property_read_u32_index(dev->of_node, "st,syscfg-amcr", 1, 111 &amcr_base); 112 if (ret) 113 return ret; 114 115 ret = of_property_read_u32_index(dev->of_node, "st,syscfg-amcr", 2, 116 &amcr_mask); 117 if (ret) 118 return ret; 119 120 amcr = mm_ospi2_size / SZ_64M; 121 122 if (set) 123 regmap_update_bits(syscfg_regmap, amcr_base, amcr_mask, amcr); 124 125 /* read AMCR and check coherency with memory-map areas defined in DT */ 126 regmap_read(syscfg_regmap, amcr_base, &read_amcr); 127 read_amcr = read_amcr >> (ffs(amcr_mask) - 1); 128 129 if (amcr != read_amcr) { 130 dev_err(dev, "AMCR value not coherent with DT memory-map areas\n"); 131 ret = -EINVAL; 132 } 133 134 return ret; 135 } 136 137 static int stm32_omm_enable_child_clock(struct device *dev, bool enable) 138 { 139 /* As there is only 2 children, remember first child in case of error */ 140 struct clk *first_child_clk = NULL; 141 struct stm32_omm *omm = dev_get_drvdata(dev); 142 u8 i; 143 int ret; 144 145 for (i = 0; i < omm->nb_child; i++) { 146 if (enable) { 147 ret = clk_prepare_enable(omm->child[i].clk); 148 if (ret) { 149 if (first_child_clk) 150 clk_disable_unprepare(first_child_clk); 151 152 dev_err(dev, "Can not enable clock\n"); 153 return ret; 154 } 155 } else { 156 clk_disable_unprepare(omm->child[i].clk); 157 } 158 159 first_child_clk = omm->child[i].clk; 160 } 161 162 return 0; 163 } 164 165 static int stm32_omm_configure(struct device *dev) 166 { 167 struct stm32_omm *omm = dev_get_drvdata(dev); 168 struct reset_control *rstc; 169 unsigned long clk_rate, clk_rate_max = 0; 170 int ret; 171 u8 i; 172 u32 mux = 0; 173 u32 cssel_ovr = 0; 174 u32 req2ack = 0; 175 176 omm->clk = devm_clk_get(dev, NULL); 177 if (IS_ERR(omm->clk)) { 178 dev_err(dev, "Failed to get OMM clock (%ld)\n", 179 PTR_ERR(omm->clk)); 180 181 return PTR_ERR(omm->clk); 182 } 183 184 ret = pm_runtime_resume_and_get(dev); 185 if (ret < 0) 186 return ret; 187 188 /* parse children's clock */ 189 for (i = 0; i < omm->nb_child; i++) { 190 clk_rate = clk_get_rate(omm->child[i].clk); 191 if (!clk_rate) { 192 dev_err(dev, "Invalid clock rate\n"); 193 goto err_clk_disable; 194 } 195 196 if (clk_rate > clk_rate_max) 197 clk_rate_max = clk_rate; 198 } 199 200 rstc = devm_reset_control_get_optional_exclusive(dev, NULL); 201 if (IS_ERR(rstc)) { 202 ret = dev_err_probe(dev, PTR_ERR(rstc), "reset get failed\n"); 203 goto err_clk_disable; 204 } 205 206 reset_control_assert(rstc); 207 udelay(2); 208 reset_control_deassert(rstc); 209 210 omm->cr = readl_relaxed(omm->io_base + OMM_CR); 211 /* optional */ 212 ret = of_property_read_u32(dev->of_node, "st,omm-mux", &mux); 213 if (!ret) { 214 if (mux & CR_MUXEN) { 215 ret = of_property_read_u32(dev->of_node, "st,omm-req2ack-ns", 216 &req2ack); 217 if (!ret && !req2ack) { 218 req2ack = DIV_ROUND_UP(req2ack, NSEC_PER_SEC / clk_rate_max) - 1; 219 220 if (req2ack > 256) 221 req2ack = 256; 222 } 223 > 224 req2ack = FIELD_PREP(CR_REQ2ACK_MASK, req2ack); 225 226 omm->cr &= ~CR_REQ2ACK_MASK; 227 omm->cr |= FIELD_PREP(CR_REQ2ACK_MASK, req2ack); 228 229 /* 230 * If the mux is enabled, the 2 OSPI clocks have to be 231 * always enabled 232 */ 233 ret = stm32_omm_enable_child_clock(dev, true); 234 if (ret) 235 goto err_clk_disable; 236 } 237 238 omm->cr &= ~CR_MUXENMODE_MASK; 239 omm->cr |= FIELD_PREP(CR_MUXENMODE_MASK, mux); 240 } 241 242 /* optional */ 243 ret = of_property_read_u32(dev->of_node, "st,omm-cssel-ovr", &cssel_ovr); 244 if (!ret) { 245 omm->cr &= ~CR_CSSEL_OVR_MASK; 246 omm->cr |= FIELD_PREP(CR_CSSEL_OVR_MASK, cssel_ovr); 247 omm->cr |= CR_CSSEL_OVR_EN; 248 } 249 250 omm->restore_omm = true; 251 writel_relaxed(omm->cr, omm->io_base + OMM_CR); 252 253 ret = stm32_omm_set_amcr(dev, true); 254 255 err_clk_disable: 256 pm_runtime_put_sync_suspend(dev); 257 258 return ret; 259 } 260
Le 10/02/2025 à 14:18, patrice.chotard-rj0Iel/JR4NBDgjK7y7TUQ@public.gmane.org a écrit : > From: Patrice Chotard <patrice.chotard-rj0Iel/JR4NBDgjK7y7TUQ@public.gmane.org> > > Octo Memory Manager driver (OMM) manages: > - the muxing between 2 OSPI busses and 2 output ports. > There are 4 possible muxing configurations: > - direct mode (no multiplexing): OSPI1 output is on port 1 and OSPI2 > output is on port 2 > - OSPI1 and OSPI2 are multiplexed over the same output port 1 > - swapped mode (no multiplexing), OSPI1 output is on port 2, > OSPI2 output is on port 1 > - OSPI1 and OSPI2 are multiplexed over the same output port 2 > - the split of the memory area shared between the 2 OSPI instances. > - chip select selection override. > - the time between 2 transactions in multiplexed mode. > - check firewall access. ... > diff --git a/drivers/memory/stm32_omm.c b/drivers/memory/stm32_omm.c > new file mode 100644 > index 000000000000..af69137bfba2 > --- /dev/null > +++ b/drivers/memory/stm32_omm.c > @@ -0,0 +1,520 @@ > +// SPDX-License-Identifier: GPL Not sure this SPDX-License-Identifier exists. > +/* > + * Copyright (C) STMicroelectronics 2024 - All Rights Reserved ... > + pm_runtime_enable(dev); > + > + /* check if OMM's resource access is granted */ > + ret = stm32_omm_check_access(dev, dev->of_node); > + if (ret < 0 && ret != -EACCES) > + goto err_clk_release; Should we call, here and below, pm_runtime_disable() in the error handling path, as done in the remove function? > + > + if (!ret && child_access_granted == OMM_CHILD_NB) { > + /* Ensure both OSPI instance are disabled before configuring OMM */ > + ret = stm32_omm_disable_child(dev); > + if (ret) > + goto err_clk_release; > + > + ret = stm32_omm_configure(dev); > + if (ret) > + goto err_clk_release; > + } else { > + dev_dbg(dev, "Octo Memory Manager resource's access not granted\n"); > + /* > + * AMCR can't be set, so check if current value is coherent > + * with memory-map areas defined in DT > + */ > + ret = stm32_omm_set_amcr(dev, false); > + if (ret) > + goto err_clk_release; > + } > + > + /* for each child, if resource access is granted and status "okay", probe it */ > + for (i = 0; i < omm->nb_child; i++) { > + if (!child_access[i] || !of_device_is_available(omm->child[i].node)) > + continue; > + > + vdev = of_platform_device_create(omm->child[i].node, NULL, NULL); > + if (!vdev) { > + dev_err(dev, "Failed to create Octo Memory Manager child\n"); > + for (j = i; j > 0; --j) { > + if (omm->child[j].dev) > + of_platform_device_destroy(omm->child[j].dev, NULL); > + } > + > + ret = -EINVAL; > + goto err_clk_release; > + } > + omm->child[i].dev = &vdev->dev; > + } > + > +err_clk_release: > + for (i = 0; i < omm->nb_child; i++) > + clk_put(omm->child[i].clk); > + > + return ret; > +} > + > +static void stm32_omm_remove(struct platform_device *pdev) > +{ > + struct stm32_omm *omm = platform_get_drvdata(pdev); > + int i; > + > + for (i = 0; i < omm->nb_child; i++) > + if (omm->child[i].dev) > + of_platform_device_destroy(omm->child[i].dev, NULL); > + > + if (omm->cr & CR_MUXEN) > + stm32_omm_enable_child_clock(&pdev->dev, false); > + > + pm_runtime_disable(&pdev->dev); Should we have: for (i = 0; i < omm->nb_child; i++) clk_put(omm->child[i].clk); as done in the error handling path of the probe? > +} > + > +static const struct of_device_id stm32_omm_of_match[] = { > + { .compatible = "st,stm32mp25-omm", }, > + {}, Nitpick: Unneeded , after a terminator. > +}; > +MODULE_DEVICE_TABLE(of, stm32_omm_of_match); ... CJ
Hi, kernel test robot noticed the following build errors: [auto build test ERROR on broonie-spi/for-next] [also build test ERROR on atorgue-stm32/stm32-next krzk-mem-ctrl/for-next linus/master v6.14-rc2 next-20250210] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/patrice-chotard-foss-st-com/dt-bindings-spi-Add-STM32-OSPI-controller/20250210-212554 base: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git for-next patch link: https://lore.kernel.org/r/20250210131826.220318-5-patrice.chotard%40foss.st.com patch subject: [PATCH v3 4/8] memory: Add STM32 Octo Memory Manager driver config: m68k-allyesconfig (https://download.01.org/0day-ci/archive/20250212/202502121131.W1HsUg9j-lkp@intel.com/config) compiler: m68k-linux-gcc (GCC) 14.2.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250212/202502121131.W1HsUg9j-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202502121131.W1HsUg9j-lkp@intel.com/ All errors (new ones prefixed by >>): drivers/memory/stm32_omm.c:82:25: note: in expansion of macro 'dev_err' 82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n", | ^~~~~~~ drivers/memory/stm32_omm.c:82:80: note: format string is defined here 82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n", | ~~~^ | | | long long unsigned int | %x drivers/memory/stm32_omm.c:82:38: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 6 has type 'resource_size_t' {aka 'unsigned int'} [-Wformat=] 82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n", | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap' 110 | _p_func(dev, fmt, ##__VA_ARGS__); \ | ^~~ include/linux/dev_printk.h:154:56: note: in expansion of macro 'dev_fmt' 154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__) | ^~~~~~~ drivers/memory/stm32_omm.c:82:25: note: in expansion of macro 'dev_err' 82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n", | ^~~~~~~ drivers/memory/stm32_omm.c:82:87: note: format string is defined here 82 | dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n", | ~~~^ | | | long long unsigned int | %x drivers/memory/stm32_omm.c:96:46: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 3 has type 'resource_size_t' {aka 'unsigned int'} [-Wformat=] 96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n", | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap' 110 | _p_func(dev, fmt, ##__VA_ARGS__); \ | ^~~ include/linux/dev_printk.h:154:56: note: in expansion of macro 'dev_fmt' 154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__) | ^~~~~~~ drivers/memory/stm32_omm.c:96:33: note: in expansion of macro 'dev_err' 96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n", | ^~~~~~~ drivers/memory/stm32_omm.c:96:53: note: format string is defined here 96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n", | ~~~^ | | | long long unsigned int | %x drivers/memory/stm32_omm.c:96:46: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 4 has type 'resource_size_t' {aka 'unsigned int'} [-Wformat=] 96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n", | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap' 110 | _p_func(dev, fmt, ##__VA_ARGS__); \ | ^~~ include/linux/dev_printk.h:154:56: note: in expansion of macro 'dev_fmt' 154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__) | ^~~~~~~ drivers/memory/stm32_omm.c:96:33: note: in expansion of macro 'dev_err' 96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n", | ^~~~~~~ drivers/memory/stm32_omm.c:96:60: note: format string is defined here 96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n", | ~~~^ | | | long long unsigned int | %x drivers/memory/stm32_omm.c:96:46: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 5 has type 'resource_size_t' {aka 'unsigned int'} [-Wformat=] 96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n", | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap' 110 | _p_func(dev, fmt, ##__VA_ARGS__); \ | ^~~ include/linux/dev_printk.h:154:56: note: in expansion of macro 'dev_fmt' 154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__) | ^~~~~~~ drivers/memory/stm32_omm.c:96:33: note: in expansion of macro 'dev_err' 96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n", | ^~~~~~~ drivers/memory/stm32_omm.c:96:78: note: format string is defined here 96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n", | ~~~^ | | | long long unsigned int | %x drivers/memory/stm32_omm.c:96:46: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 6 has type 'resource_size_t' {aka 'unsigned int'} [-Wformat=] 96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n", | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap' 110 | _p_func(dev, fmt, ##__VA_ARGS__); \ | ^~~ include/linux/dev_printk.h:154:56: note: in expansion of macro 'dev_fmt' 154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__) | ^~~~~~~ drivers/memory/stm32_omm.c:96:33: note: in expansion of macro 'dev_err' 96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n", | ^~~~~~~ drivers/memory/stm32_omm.c:96:85: note: format string is defined here 96 | dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n", | ~~~^ | | | long long unsigned int | %x drivers/memory/stm32_omm.c: In function 'stm32_omm_configure': >> drivers/memory/stm32_omm.c:224:35: error: implicit declaration of function 'FIELD_PREP' [-Wimplicit-function-declaration] 224 | req2ack = FIELD_PREP(CR_REQ2ACK_MASK, req2ack); | ^~~~~~~~~~ vim +/FIELD_PREP +224 drivers/memory/stm32_omm.c 164 165 static int stm32_omm_configure(struct device *dev) 166 { 167 struct stm32_omm *omm = dev_get_drvdata(dev); 168 struct reset_control *rstc; 169 unsigned long clk_rate, clk_rate_max = 0; 170 int ret; 171 u8 i; 172 u32 mux = 0; 173 u32 cssel_ovr = 0; 174 u32 req2ack = 0; 175 176 omm->clk = devm_clk_get(dev, NULL); 177 if (IS_ERR(omm->clk)) { 178 dev_err(dev, "Failed to get OMM clock (%ld)\n", 179 PTR_ERR(omm->clk)); 180 181 return PTR_ERR(omm->clk); 182 } 183 184 ret = pm_runtime_resume_and_get(dev); 185 if (ret < 0) 186 return ret; 187 188 /* parse children's clock */ 189 for (i = 0; i < omm->nb_child; i++) { 190 clk_rate = clk_get_rate(omm->child[i].clk); 191 if (!clk_rate) { 192 dev_err(dev, "Invalid clock rate\n"); 193 goto err_clk_disable; 194 } 195 196 if (clk_rate > clk_rate_max) 197 clk_rate_max = clk_rate; 198 } 199 200 rstc = devm_reset_control_get_optional_exclusive(dev, NULL); 201 if (IS_ERR(rstc)) { 202 ret = dev_err_probe(dev, PTR_ERR(rstc), "reset get failed\n"); 203 goto err_clk_disable; 204 } 205 206 reset_control_assert(rstc); 207 udelay(2); 208 reset_control_deassert(rstc); 209 210 omm->cr = readl_relaxed(omm->io_base + OMM_CR); 211 /* optional */ 212 ret = of_property_read_u32(dev->of_node, "st,omm-mux", &mux); 213 if (!ret) { 214 if (mux & CR_MUXEN) { 215 ret = of_property_read_u32(dev->of_node, "st,omm-req2ack-ns", 216 &req2ack); 217 if (!ret && !req2ack) { 218 req2ack = DIV_ROUND_UP(req2ack, NSEC_PER_SEC / clk_rate_max) - 1; 219 220 if (req2ack > 256) 221 req2ack = 256; 222 } 223 > 224 req2ack = FIELD_PREP(CR_REQ2ACK_MASK, req2ack); 225 226 omm->cr &= ~CR_REQ2ACK_MASK; 227 omm->cr |= FIELD_PREP(CR_REQ2ACK_MASK, req2ack); 228 229 /* 230 * If the mux is enabled, the 2 OSPI clocks have to be 231 * always enabled 232 */ 233 ret = stm32_omm_enable_child_clock(dev, true); 234 if (ret) 235 goto err_clk_disable; 236 } 237 238 omm->cr &= ~CR_MUXENMODE_MASK; 239 omm->cr |= FIELD_PREP(CR_MUXENMODE_MASK, mux); 240 } 241 242 /* optional */ 243 ret = of_property_read_u32(dev->of_node, "st,omm-cssel-ovr", &cssel_ovr); 244 if (!ret) { 245 omm->cr &= ~CR_CSSEL_OVR_MASK; 246 omm->cr |= FIELD_PREP(CR_CSSEL_OVR_MASK, cssel_ovr); 247 omm->cr |= CR_CSSEL_OVR_EN; 248 } 249 250 omm->restore_omm = true; 251 writel_relaxed(omm->cr, omm->io_base + OMM_CR); 252 253 ret = stm32_omm_set_amcr(dev, true); 254 255 err_clk_disable: 256 pm_runtime_put_sync_suspend(dev); 257 258 return ret; 259 } 260
Hi Patrice, On Mon, 10 Feb 2025 at 14:21, <patrice.chotard@foss.st.com> wrote: > From: Patrice Chotard <patrice.chotard@foss.st.com> > > Octo Memory Manager driver (OMM) manages: > - the muxing between 2 OSPI busses and 2 output ports. > There are 4 possible muxing configurations: > - direct mode (no multiplexing): OSPI1 output is on port 1 and OSPI2 > output is on port 2 > - OSPI1 and OSPI2 are multiplexed over the same output port 1 > - swapped mode (no multiplexing), OSPI1 output is on port 2, > OSPI2 output is on port 1 > - OSPI1 and OSPI2 are multiplexed over the same output port 2 > - the split of the memory area shared between the 2 OSPI instances. > - chip select selection override. > - the time between 2 transactions in multiplexed mode. > - check firewall access. > > Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com> > Signed-off-by: Christophe Kerello <christophe.kerello@foss.st.com> Thanks for your patch! > --- a/drivers/memory/Kconfig > +++ b/drivers/memory/Kconfig > @@ -225,6 +225,23 @@ config STM32_FMC2_EBI > devices (like SRAM, ethernet adapters, FPGAs, LCD displays, ...) on > SOCs containing the FMC2 External Bus Interface. > > +config STM32_OMM > + tristate "STM32 Octo Memory Manager" > + depends on SPI_STM32_OSPI || TEST_COMPILE COMPILE_TEST > + help > + This driver manages the muxing between the 2 OSPI busses and > + the 2 output ports. There are 4 possible muxing configurations: > + - direct mode (no multiplexing): OSPI1 output is on port 1 and OSPI2 > + output is on port 2 > + - OSPI1 and OSPI2 are multiplexed over the same output port 1 > + - swapped mode (no multiplexing), OSPI1 output is on port 2, > + OSPI2 output is on port 1 > + - OSPI1 and OSPI2 are multiplexed over the same output port 2 > + It also manages : > + - the split of the memory area shared between the 2 OSPI instances. > + - chip select selection override. > + - the time between 2 transactions in multiplexed mode. > + > source "drivers/memory/samsung/Kconfig" > source "drivers/memory/tegra/Kconfig" > --- /dev/null > +++ b/drivers/memory/stm32_omm.c > +static int stm32_omm_set_amcr(struct device *dev, bool set) > +{ > + struct stm32_omm *omm = dev_get_drvdata(dev); > + struct regmap *syscfg_regmap; > + struct device_node *node; > + struct resource res, res1; > + resource_size_t mm_ospi2_size = 0; > + static const char * const mm_name[] = { "ospi1", "ospi2" }; > + u32 amcr_base, amcr_mask; > + int ret, i, idx; unsigned int i > + unsigned int amcr, read_amcr; > + > + for (i = 0; i < omm->nb_child; i++) { > + idx = of_property_match_string(dev->of_node, > + "memory-region-names", > + mm_name[i]); > + if (idx < 0) > + continue; > + > + /* res1 only used on second loop iteration */ > + res1.start = res.start; > + res1.end = res.end; > + > + node = of_parse_phandle(dev->of_node, "memory-region", idx); > + if (!node) > + continue; > + > + ret = of_address_to_resource(node, 0, &res); > + if (ret) { > + dev_err(dev, "unable to resolve memory region\n"); > + return ret; > + } > + > + /* check that memory region fits inside OMM memory map area */ > + if (!resource_contains(omm->mm_res, &res)) { > + dev_err(dev, "%s doesn't fit inside OMM memory map area\n", > + mm_name[i]); > + dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n", > + res.start, res.end, > + omm->mm_res->start, omm->mm_res->end); As reported by the kernel test robot, this fails to build when resource_size_t differs from unsigned long long. However, you can easily print the full resource instead: dev_err(dev, "%pR doesn't fit inside %pR\n", &res, omm->mm_res); https://elixir.bootlin.com/linux/v6.13.2/source/Documentation/core-api/printk-formats.rst#L206 Gr{oetje,eeting}s, Geert
On 2/11/25 19:16, Christophe JAILLET wrote: > Le 10/02/2025 à 14:18, patrice.chotard-rj0Iel/JR4NBDgjK7y7TUQ@public.gmane.org a écrit : >> From: Patrice Chotard <patrice.chotard-rj0Iel/JR4NBDgjK7y7TUQ@public.gmane.org> >> >> Octo Memory Manager driver (OMM) manages: >> - the muxing between 2 OSPI busses and 2 output ports. >> There are 4 possible muxing configurations: >> - direct mode (no multiplexing): OSPI1 output is on port 1 and OSPI2 >> output is on port 2 >> - OSPI1 and OSPI2 are multiplexed over the same output port 1 >> - swapped mode (no multiplexing), OSPI1 output is on port 2, >> OSPI2 output is on port 1 >> - OSPI1 and OSPI2 are multiplexed over the same output port 2 >> - the split of the memory area shared between the 2 OSPI instances. >> - chip select selection override. >> - the time between 2 transactions in multiplexed mode. >> - check firewall access. > > ... > >> diff --git a/drivers/memory/stm32_omm.c b/drivers/memory/stm32_omm.c >> new file mode 100644 >> index 000000000000..af69137bfba2 >> --- /dev/null >> +++ b/drivers/memory/stm32_omm.c >> @@ -0,0 +1,520 @@ >> +// SPDX-License-Identifier: GPL > > Not sure this SPDX-License-Identifier exists. Right, i will fix that. > >> +/* >> + * Copyright (C) STMicroelectronics 2024 - All Rights Reserved > > ... > >> + pm_runtime_enable(dev); >> + >> + /* check if OMM's resource access is granted */ >> + ret = stm32_omm_check_access(dev, dev->of_node); >> + if (ret < 0 && ret != -EACCES) >> + goto err_clk_release; > > Should we call, here and below, pm_runtime_disable() in the error handling path, as done in the remove function? right, i will add it. > >> + >> + if (!ret && child_access_granted == OMM_CHILD_NB) { >> + /* Ensure both OSPI instance are disabled before configuring OMM */ >> + ret = stm32_omm_disable_child(dev); >> + if (ret) >> + goto err_clk_release; >> + >> + ret = stm32_omm_configure(dev); >> + if (ret) >> + goto err_clk_release; >> + } else { >> + dev_dbg(dev, "Octo Memory Manager resource's access not granted\n"); >> + /* >> + * AMCR can't be set, so check if current value is coherent >> + * with memory-map areas defined in DT >> + */ >> + ret = stm32_omm_set_amcr(dev, false); >> + if (ret) >> + goto err_clk_release; >> + } >> + >> + /* for each child, if resource access is granted and status "okay", probe it */ >> + for (i = 0; i < omm->nb_child; i++) { >> + if (!child_access[i] || !of_device_is_available(omm->child[i].node)) >> + continue; >> + >> + vdev = of_platform_device_create(omm->child[i].node, NULL, NULL); >> + if (!vdev) { >> + dev_err(dev, "Failed to create Octo Memory Manager child\n"); >> + for (j = i; j > 0; --j) { >> + if (omm->child[j].dev) >> + of_platform_device_destroy(omm->child[j].dev, NULL); >> + } >> + >> + ret = -EINVAL; >> + goto err_clk_release; >> + } >> + omm->child[i].dev = &vdev->dev; >> + } >> + >> +err_clk_release: >> + for (i = 0; i < omm->nb_child; i++) >> + clk_put(omm->child[i].clk); >> + >> + return ret; >> +} >> + >> +static void stm32_omm_remove(struct platform_device *pdev) >> +{ >> + struct stm32_omm *omm = platform_get_drvdata(pdev); >> + int i; >> + >> + for (i = 0; i < omm->nb_child; i++) >> + if (omm->child[i].dev) >> + of_platform_device_destroy(omm->child[i].dev, NULL); >> + >> + if (omm->cr & CR_MUXEN) >> + stm32_omm_enable_child_clock(&pdev->dev, false); >> + >> + pm_runtime_disable(&pdev->dev); > > Should we have: > for (i = 0; i < omm->nb_child; i++) > clk_put(omm->child[i].clk); > as done in the error handling path of the probe? no need, as child's clock are always freed in stm32_omm_probe() in all cases. > >> +} >> + >> +static const struct of_device_id stm32_omm_of_match[] = { >> + { .compatible = "st,stm32mp25-omm", }, >> + {}, > > Nitpick: Unneeded , after a terminator. ok > >> +}; >> +MODULE_DEVICE_TABLE(of, stm32_omm_of_match); > > ... > > CJ > > >
On 2/12/25 13:51, Geert Uytterhoeven wrote: > Hi Patrice, > > On Mon, 10 Feb 2025 at 14:21, <patrice.chotard@foss.st.com> wrote: >> From: Patrice Chotard <patrice.chotard@foss.st.com> >> >> Octo Memory Manager driver (OMM) manages: >> - the muxing between 2 OSPI busses and 2 output ports. >> There are 4 possible muxing configurations: >> - direct mode (no multiplexing): OSPI1 output is on port 1 and OSPI2 >> output is on port 2 >> - OSPI1 and OSPI2 are multiplexed over the same output port 1 >> - swapped mode (no multiplexing), OSPI1 output is on port 2, >> OSPI2 output is on port 1 >> - OSPI1 and OSPI2 are multiplexed over the same output port 2 >> - the split of the memory area shared between the 2 OSPI instances. >> - chip select selection override. >> - the time between 2 transactions in multiplexed mode. >> - check firewall access. >> >> Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com> >> Signed-off-by: Christophe Kerello <christophe.kerello@foss.st.com> > > Thanks for your patch! > >> --- a/drivers/memory/Kconfig >> +++ b/drivers/memory/Kconfig >> @@ -225,6 +225,23 @@ config STM32_FMC2_EBI >> devices (like SRAM, ethernet adapters, FPGAs, LCD displays, ...) on >> SOCs containing the FMC2 External Bus Interface. >> >> +config STM32_OMM >> + tristate "STM32 Octo Memory Manager" >> + depends on SPI_STM32_OSPI || TEST_COMPILE > > COMPILE_TEST good catch > >> + help >> + This driver manages the muxing between the 2 OSPI busses and >> + the 2 output ports. There are 4 possible muxing configurations: >> + - direct mode (no multiplexing): OSPI1 output is on port 1 and OSPI2 >> + output is on port 2 >> + - OSPI1 and OSPI2 are multiplexed over the same output port 1 >> + - swapped mode (no multiplexing), OSPI1 output is on port 2, >> + OSPI2 output is on port 1 >> + - OSPI1 and OSPI2 are multiplexed over the same output port 2 >> + It also manages : >> + - the split of the memory area shared between the 2 OSPI instances. >> + - chip select selection override. >> + - the time between 2 transactions in multiplexed mode. >> + >> source "drivers/memory/samsung/Kconfig" >> source "drivers/memory/tegra/Kconfig" > >> --- /dev/null >> +++ b/drivers/memory/stm32_omm.c > >> +static int stm32_omm_set_amcr(struct device *dev, bool set) >> +{ >> + struct stm32_omm *omm = dev_get_drvdata(dev); >> + struct regmap *syscfg_regmap; >> + struct device_node *node; >> + struct resource res, res1; >> + resource_size_t mm_ospi2_size = 0; >> + static const char * const mm_name[] = { "ospi1", "ospi2" }; >> + u32 amcr_base, amcr_mask; >> + int ret, i, idx; > > unsigned int i ok > >> + unsigned int amcr, read_amcr; >> + >> + for (i = 0; i < omm->nb_child; i++) { >> + idx = of_property_match_string(dev->of_node, >> + "memory-region-names", >> + mm_name[i]); >> + if (idx < 0) >> + continue; >> + >> + /* res1 only used on second loop iteration */ >> + res1.start = res.start; >> + res1.end = res.end; >> + >> + node = of_parse_phandle(dev->of_node, "memory-region", idx); >> + if (!node) >> + continue; >> + >> + ret = of_address_to_resource(node, 0, &res); >> + if (ret) { >> + dev_err(dev, "unable to resolve memory region\n"); >> + return ret; >> + } >> + >> + /* check that memory region fits inside OMM memory map area */ >> + if (!resource_contains(omm->mm_res, &res)) { >> + dev_err(dev, "%s doesn't fit inside OMM memory map area\n", >> + mm_name[i]); >> + dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n", >> + res.start, res.end, >> + omm->mm_res->start, omm->mm_res->end); > > As reported by the kernel test robot, this fails to build when > resource_size_t differs from unsigned long long. However, you can > easily print the full resource instead: > > dev_err(dev, "%pR doesn't fit inside %pR\n", &res, omm->mm_res); > > https://elixir.bootlin.com/linux/v6.13.2/source/Documentation/core-api/printk-formats.rst#L206 OK, thanks for this tips Patrice > > Gr{oetje,eeting}s, > > Geert >
diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index c82d8d8a16ea..80c44a6d5bdb 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig @@ -225,6 +225,23 @@ config STM32_FMC2_EBI devices (like SRAM, ethernet adapters, FPGAs, LCD displays, ...) on SOCs containing the FMC2 External Bus Interface. +config STM32_OMM + tristate "STM32 Octo Memory Manager" + depends on SPI_STM32_OSPI || TEST_COMPILE + help + This driver manages the muxing between the 2 OSPI busses and + the 2 output ports. There are 4 possible muxing configurations: + - direct mode (no multiplexing): OSPI1 output is on port 1 and OSPI2 + output is on port 2 + - OSPI1 and OSPI2 are multiplexed over the same output port 1 + - swapped mode (no multiplexing), OSPI1 output is on port 2, + OSPI2 output is on port 1 + - OSPI1 and OSPI2 are multiplexed over the same output port 2 + It also manages : + - the split of the memory area shared between the 2 OSPI instances. + - chip select selection override. + - the time between 2 transactions in multiplexed mode. + source "drivers/memory/samsung/Kconfig" source "drivers/memory/tegra/Kconfig" diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile index d2e6ca9abbe0..c1959661bf63 100644 --- a/drivers/memory/Makefile +++ b/drivers/memory/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_DA8XX_DDRCTL) += da8xx-ddrctl.o obj-$(CONFIG_PL353_SMC) += pl353-smc.o obj-$(CONFIG_RENESAS_RPCIF) += renesas-rpc-if.o obj-$(CONFIG_STM32_FMC2_EBI) += stm32-fmc2-ebi.o +obj-$(CONFIG_STM32_OMM) += stm32_omm.o obj-$(CONFIG_SAMSUNG_MC) += samsung/ obj-$(CONFIG_TEGRA_MC) += tegra/ diff --git a/drivers/memory/stm32_omm.c b/drivers/memory/stm32_omm.c new file mode 100644 index 000000000000..af69137bfba2 --- /dev/null +++ b/drivers/memory/stm32_omm.c @@ -0,0 +1,520 @@ +// SPDX-License-Identifier: GPL +/* + * Copyright (C) STMicroelectronics 2024 - All Rights Reserved + * Author(s): Patrice Chotard <patrice.chotard@foss.st.com> for STMicroelectronics. + */ + +#include <linux/bus/stm32_firewall_device.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/mfd/syscon.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/pinctrl/consumer.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/reset.h> + +#define OMM_CR 0 +#define CR_MUXEN BIT(0) +#define CR_MUXENMODE_MASK GENMASK(1, 0) +#define CR_CSSEL_OVR_EN BIT(4) +#define CR_CSSEL_OVR_MASK GENMASK(6, 5) +#define CR_REQ2ACK_MASK GENMASK(23, 16) + +#define OMM_CHILD_NB 2 + +struct ospi_child { + struct device *dev; + struct device_node *node; + struct clk *clk; +}; + +struct stm32_omm { + struct ospi_child child[OMM_CHILD_NB]; + struct resource *mm_res; + struct clk *clk; + void __iomem *io_base; + u32 cr; + u8 nb_child; + bool restore_omm; +}; + +static int stm32_omm_set_amcr(struct device *dev, bool set) +{ + struct stm32_omm *omm = dev_get_drvdata(dev); + struct regmap *syscfg_regmap; + struct device_node *node; + struct resource res, res1; + resource_size_t mm_ospi2_size = 0; + static const char * const mm_name[] = { "ospi1", "ospi2" }; + u32 amcr_base, amcr_mask; + int ret, i, idx; + unsigned int amcr, read_amcr; + + for (i = 0; i < omm->nb_child; i++) { + idx = of_property_match_string(dev->of_node, + "memory-region-names", + mm_name[i]); + if (idx < 0) + continue; + + /* res1 only used on second loop iteration */ + res1.start = res.start; + res1.end = res.end; + + node = of_parse_phandle(dev->of_node, "memory-region", idx); + if (!node) + continue; + + ret = of_address_to_resource(node, 0, &res); + if (ret) { + dev_err(dev, "unable to resolve memory region\n"); + return ret; + } + + /* check that memory region fits inside OMM memory map area */ + if (!resource_contains(omm->mm_res, &res)) { + dev_err(dev, "%s doesn't fit inside OMM memory map area\n", + mm_name[i]); + dev_err(dev, "[0x%llx-0x%llx] doesn't fit inside [0x%llx-0x%llx]\n", + res.start, res.end, + omm->mm_res->start, omm->mm_res->end); + + return -EFAULT; + } + + if (i == 1) { + mm_ospi2_size = resource_size(&res); + + /* check that OMM memory region 1 doesn't overlap memory region 2 */ + if (resource_overlaps(&res, &res1)) { + dev_err(dev, "OMM memory-region %s overlaps memory region %s\n", + mm_name[0], mm_name[1]); + dev_err(dev, "[0x%llx-0x%llx] overlaps [0x%llx-0x%llx]\n", + res1.start, res1.end, res.start, res.end); + + return -EFAULT; + } + } + } + + syscfg_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "st,syscfg-amcr"); + if (IS_ERR(syscfg_regmap)) { + dev_err(dev, "Failed to get st,syscfg-amcr property\n"); + return PTR_ERR(syscfg_regmap); + } + + ret = of_property_read_u32_index(dev->of_node, "st,syscfg-amcr", 1, + &amcr_base); + if (ret) + return ret; + + ret = of_property_read_u32_index(dev->of_node, "st,syscfg-amcr", 2, + &amcr_mask); + if (ret) + return ret; + + amcr = mm_ospi2_size / SZ_64M; + + if (set) + regmap_update_bits(syscfg_regmap, amcr_base, amcr_mask, amcr); + + /* read AMCR and check coherency with memory-map areas defined in DT */ + regmap_read(syscfg_regmap, amcr_base, &read_amcr); + read_amcr = read_amcr >> (ffs(amcr_mask) - 1); + + if (amcr != read_amcr) { + dev_err(dev, "AMCR value not coherent with DT memory-map areas\n"); + ret = -EINVAL; + } + + return ret; +} + +static int stm32_omm_enable_child_clock(struct device *dev, bool enable) +{ + /* As there is only 2 children, remember first child in case of error */ + struct clk *first_child_clk = NULL; + struct stm32_omm *omm = dev_get_drvdata(dev); + u8 i; + int ret; + + for (i = 0; i < omm->nb_child; i++) { + if (enable) { + ret = clk_prepare_enable(omm->child[i].clk); + if (ret) { + if (first_child_clk) + clk_disable_unprepare(first_child_clk); + + dev_err(dev, "Can not enable clock\n"); + return ret; + } + } else { + clk_disable_unprepare(omm->child[i].clk); + } + + first_child_clk = omm->child[i].clk; + } + + return 0; +} + +static int stm32_omm_configure(struct device *dev) +{ + struct stm32_omm *omm = dev_get_drvdata(dev); + struct reset_control *rstc; + unsigned long clk_rate, clk_rate_max = 0; + int ret; + u8 i; + u32 mux = 0; + u32 cssel_ovr = 0; + u32 req2ack = 0; + + omm->clk = devm_clk_get(dev, NULL); + if (IS_ERR(omm->clk)) { + dev_err(dev, "Failed to get OMM clock (%ld)\n", + PTR_ERR(omm->clk)); + + return PTR_ERR(omm->clk); + } + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + /* parse children's clock */ + for (i = 0; i < omm->nb_child; i++) { + clk_rate = clk_get_rate(omm->child[i].clk); + if (!clk_rate) { + dev_err(dev, "Invalid clock rate\n"); + goto err_clk_disable; + } + + if (clk_rate > clk_rate_max) + clk_rate_max = clk_rate; + } + + rstc = devm_reset_control_get_optional_exclusive(dev, NULL); + if (IS_ERR(rstc)) { + ret = dev_err_probe(dev, PTR_ERR(rstc), "reset get failed\n"); + goto err_clk_disable; + } + + reset_control_assert(rstc); + udelay(2); + reset_control_deassert(rstc); + + omm->cr = readl_relaxed(omm->io_base + OMM_CR); + /* optional */ + ret = of_property_read_u32(dev->of_node, "st,omm-mux", &mux); + if (!ret) { + if (mux & CR_MUXEN) { + ret = of_property_read_u32(dev->of_node, "st,omm-req2ack-ns", + &req2ack); + if (!ret && !req2ack) { + req2ack = DIV_ROUND_UP(req2ack, NSEC_PER_SEC / clk_rate_max) - 1; + + if (req2ack > 256) + req2ack = 256; + } + + req2ack = FIELD_PREP(CR_REQ2ACK_MASK, req2ack); + + omm->cr &= ~CR_REQ2ACK_MASK; + omm->cr |= FIELD_PREP(CR_REQ2ACK_MASK, req2ack); + + /* + * If the mux is enabled, the 2 OSPI clocks have to be + * always enabled + */ + ret = stm32_omm_enable_child_clock(dev, true); + if (ret) + goto err_clk_disable; + } + + omm->cr &= ~CR_MUXENMODE_MASK; + omm->cr |= FIELD_PREP(CR_MUXENMODE_MASK, mux); + } + + /* optional */ + ret = of_property_read_u32(dev->of_node, "st,omm-cssel-ovr", &cssel_ovr); + if (!ret) { + omm->cr &= ~CR_CSSEL_OVR_MASK; + omm->cr |= FIELD_PREP(CR_CSSEL_OVR_MASK, cssel_ovr); + omm->cr |= CR_CSSEL_OVR_EN; + } + + omm->restore_omm = true; + writel_relaxed(omm->cr, omm->io_base + OMM_CR); + + ret = stm32_omm_set_amcr(dev, true); + +err_clk_disable: + pm_runtime_put_sync_suspend(dev); + + return ret; +} + +static int stm32_omm_check_access(struct device *dev, struct device_node *np) +{ + struct stm32_firewall firewall; + int ret; + + ret = stm32_firewall_get_firewall(np, &firewall, 1); + if (ret) + return ret; + + return stm32_firewall_grant_access(&firewall); +} + +static int stm32_omm_disable_child(struct device *dev) +{ + struct stm32_omm *omm = dev_get_drvdata(dev); + struct reset_control *reset; + int ret; + u8 i; + + for (i = 0; i < omm->nb_child; i++) { + ret = clk_prepare_enable(omm->child[i].clk); + if (ret) { + dev_err(dev, "Can not enable clock\n"); + return ret; + } + + reset = of_reset_control_get_exclusive(omm->child[i].node, 0); + if (IS_ERR(reset)) { + dev_err(dev, "Can't get child reset\n"); + return PTR_ERR(reset); + }; + + /* reset OSPI to ensure CR_EN bit is set to 0 */ + reset_control_assert(reset); + udelay(2); + reset_control_deassert(reset); + + reset_control_put(reset); + clk_disable_unprepare(omm->child[i].clk); + } + + return 0; +} + +static int stm32_omm_probe(struct platform_device *pdev) +{ + struct platform_device *vdev; + struct device *dev = &pdev->dev; + struct stm32_omm *omm; + struct clk *clk; + int ret; + u8 child_access_granted = 0; + u8 i, j; + bool child_access[OMM_CHILD_NB]; + + omm = devm_kzalloc(dev, sizeof(*omm), GFP_KERNEL); + if (!omm) + return -ENOMEM; + + omm->io_base = devm_platform_ioremap_resource_byname(pdev, "regs"); + if (IS_ERR(omm->io_base)) + return PTR_ERR(omm->io_base); + + omm->mm_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "memory_map"); + if (IS_ERR(omm->mm_res)) + return PTR_ERR(omm->mm_res); + + /* check child's access */ + for_each_child_of_node_scoped(dev->of_node, child) { + if (omm->nb_child >= OMM_CHILD_NB) { + dev_err(dev, "Bad DT, found too much children\n"); + ret = -E2BIG; + goto err_clk_release; + } + + if (!of_device_is_compatible(child, "st,stm32mp25-ospi")) { + ret = -EINVAL; + goto err_clk_release; + } + + ret = stm32_omm_check_access(dev, child); + if (ret < 0 && ret != -EACCES) + goto err_clk_release; + + child_access[omm->nb_child] = false; + if (!ret) { + child_access_granted++; + child_access[omm->nb_child] = true; + } + + omm->child[omm->nb_child].node = child; + + clk = of_clk_get(child, 0); + if (IS_ERR(clk)) { + dev_err(dev, "Can't get child clock\n"); + ret = PTR_ERR(clk); + goto err_clk_release; + }; + + omm->child[omm->nb_child].clk = clk; + omm->nb_child++; + } + + if (omm->nb_child != OMM_CHILD_NB) { + ret = -EINVAL; + goto err_clk_release; + } + + platform_set_drvdata(pdev, omm); + + pm_runtime_enable(dev); + + /* check if OMM's resource access is granted */ + ret = stm32_omm_check_access(dev, dev->of_node); + if (ret < 0 && ret != -EACCES) + goto err_clk_release; + + if (!ret && child_access_granted == OMM_CHILD_NB) { + /* Ensure both OSPI instance are disabled before configuring OMM */ + ret = stm32_omm_disable_child(dev); + if (ret) + goto err_clk_release; + + ret = stm32_omm_configure(dev); + if (ret) + goto err_clk_release; + } else { + dev_dbg(dev, "Octo Memory Manager resource's access not granted\n"); + /* + * AMCR can't be set, so check if current value is coherent + * with memory-map areas defined in DT + */ + ret = stm32_omm_set_amcr(dev, false); + if (ret) + goto err_clk_release; + } + + /* for each child, if resource access is granted and status "okay", probe it */ + for (i = 0; i < omm->nb_child; i++) { + if (!child_access[i] || !of_device_is_available(omm->child[i].node)) + continue; + + vdev = of_platform_device_create(omm->child[i].node, NULL, NULL); + if (!vdev) { + dev_err(dev, "Failed to create Octo Memory Manager child\n"); + for (j = i; j > 0; --j) { + if (omm->child[j].dev) + of_platform_device_destroy(omm->child[j].dev, NULL); + } + + ret = -EINVAL; + goto err_clk_release; + } + omm->child[i].dev = &vdev->dev; + } + +err_clk_release: + for (i = 0; i < omm->nb_child; i++) + clk_put(omm->child[i].clk); + + return ret; +} + +static void stm32_omm_remove(struct platform_device *pdev) +{ + struct stm32_omm *omm = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < omm->nb_child; i++) + if (omm->child[i].dev) + of_platform_device_destroy(omm->child[i].dev, NULL); + + if (omm->cr & CR_MUXEN) + stm32_omm_enable_child_clock(&pdev->dev, false); + + pm_runtime_disable(&pdev->dev); +} + +static const struct of_device_id stm32_omm_of_match[] = { + { .compatible = "st,stm32mp25-omm", }, + {}, +}; +MODULE_DEVICE_TABLE(of, stm32_omm_of_match); + +static int __maybe_unused stm32_omm_runtime_suspend(struct device *dev) +{ + struct stm32_omm *omm = dev_get_drvdata(dev); + + clk_disable_unprepare(omm->clk); + + return 0; +} + +static int __maybe_unused stm32_omm_runtime_resume(struct device *dev) +{ + struct stm32_omm *omm = dev_get_drvdata(dev); + + return clk_prepare_enable(omm->clk); +} + +static int __maybe_unused stm32_omm_suspend(struct device *dev) +{ + struct stm32_omm *omm = dev_get_drvdata(dev); + + if (omm->restore_omm && omm->cr & CR_MUXEN) + stm32_omm_enable_child_clock(dev, false); + + return pinctrl_pm_select_sleep_state(dev); +} + +static int __maybe_unused stm32_omm_resume(struct device *dev) +{ + struct stm32_omm *omm = dev_get_drvdata(dev); + int ret; + + pinctrl_pm_select_default_state(dev); + + if (!omm->restore_omm) + return 0; + + /* Ensure both OSPI instance are disabled before configuring OMM */ + ret = stm32_omm_disable_child(dev); + if (ret) + return ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + writel_relaxed(omm->cr, omm->io_base + OMM_CR); + ret = stm32_omm_set_amcr(dev, true); + pm_runtime_put_sync_suspend(dev); + if (ret) + return ret; + + if (omm->cr & CR_MUXEN) + ret = stm32_omm_enable_child_clock(dev, true); + + return ret; +} + +static const struct dev_pm_ops stm32_omm_pm_ops = { + SET_RUNTIME_PM_OPS(stm32_omm_runtime_suspend, + stm32_omm_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(stm32_omm_suspend, stm32_omm_resume) +}; + +static struct platform_driver stm32_omm_driver = { + .probe = stm32_omm_probe, + .remove = stm32_omm_remove, + .driver = { + .name = "stm32-omm", + .of_match_table = stm32_omm_of_match, + .pm = &stm32_omm_pm_ops, + }, +}; +module_platform_driver(stm32_omm_driver); + +MODULE_DESCRIPTION("STMicroelectronics Octo Memory Manager driver"); +MODULE_LICENSE("GPL");