Message ID | 20240430083730.134918-2-herve.codina@bootlin.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Add support for the LAN966x PCI device using a DT overlay | expand |
On Tue, Apr 30, 2024 at 10:37:10AM +0200, Herve Codina wrote: > From: Clément Léger <clement.leger@bootlin.com> > > Syscon releasing is not supported. > Without release function, unbinding a driver that uses syscon whether > explicitly or due to a module removal left the used syscon in a in-use > state. > > For instance a syscon_node_to_regmap() call from a consumer retrieve a > syscon regmap instance. Internally, syscon_node_to_regmap() can create > syscon instance and add it to the existing syscon list. No API is > available to release this syscon instance, remove it from the list and > free it when it is not used anymore. > > Introduce reference counting in syscon in order to keep track of syscon > usage using syscon_{get,put}() and add a device managed version of > syscon_regmap_lookup_by_phandle(), to automatically release the syscon > instance on the consumer removal. > > Signed-off-by: Clément Léger <clement.leger@bootlin.com> > Signed-off-by: Herve Codina <herve.codina@bootlin.com> ... > diff --git a/include/linux/mfd/syscon.h b/include/linux/mfd/syscon.h > index c315903f6dab..164b9bcb49c3 100644 > --- a/include/linux/mfd/syscon.h > +++ b/include/linux/mfd/syscon.h > @@ -15,6 +15,7 @@ > #include <linux/errno.h> > > struct device_node; > +struct device; > > #ifdef CONFIG_MFD_SYSCON > struct regmap *device_node_to_regmap(struct device_node *np); > @@ -28,6 +29,11 @@ struct regmap *syscon_regmap_lookup_by_phandle_args(struct device_node *np, > unsigned int *out_args); > struct regmap *syscon_regmap_lookup_by_phandle_optional(struct device_node *np, > const char *property); > +void syscon_put_regmap(struct regmap *regmap); > + > +struct regmap *devm_syscon_regmap_lookup_by_phandle(struct device *dev, > + struct device_node *np, > + const char *property); > #else > static inline struct regmap *device_node_to_regmap(struct device_node *np) > { > @@ -67,6 +73,18 @@ static inline struct regmap *syscon_regmap_lookup_by_phandle_optional( > return NULL; > } > > +static intline void syscon_put_regmap(struct regmap *regmap) intline -> inline > +{ > +} ...
Hi Herve, kernel test robot noticed the following build errors: [auto build test ERROR on tip/irq/core] [also build test ERROR on lee-mfd/for-mfd-next pza/reset/next linus/master v6.9-rc6 next-20240430] [cannot apply to lee-mfd/for-mfd-fixes pza/imx-drm/next] [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/Herve-Codina/mfd-syscon-Add-reference-counting-and-device-managed-support/20240430-164912 base: tip/irq/core patch link: https://lore.kernel.org/r/20240430083730.134918-2-herve.codina%40bootlin.com patch subject: [PATCH 01/17] mfd: syscon: Add reference counting and device managed support config: i386-buildonly-randconfig-005-20240501 (https://download.01.org/0day-ci/archive/20240501/202405010521.dW27hHxi-lkp@intel.com/config) compiler: gcc-13 (Ubuntu 13.2.0-4ubuntu3) 13.2.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240501/202405010521.dW27hHxi-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/202405010521.dW27hHxi-lkp@intel.com/ All error/warnings (new ones prefixed by >>): In file included from drivers/misc/sram.c:18: >> include/linux/mfd/syscon.h:76:15: error: expected ';' before 'void' 76 | static intline void syscon_put_regmap(struct regmap *regmap) | ^~~~~ | ; >> include/linux/mfd/syscon.h:76:21: warning: no previous prototype for 'syscon_put_regmap' [-Wmissing-prototypes] 76 | static intline void syscon_put_regmap(struct regmap *regmap) | ^~~~~~~~~~~~~~~~~ vim +76 include/linux/mfd/syscon.h 75 > 76 static intline void syscon_put_regmap(struct regmap *regmap) 77 { 78 } 79
Hi Herve, kernel test robot noticed the following build errors: [auto build test ERROR on tip/irq/core] [also build test ERROR on lee-mfd/for-mfd-next pza/reset/next linus/master v6.9-rc6 next-20240430] [cannot apply to lee-mfd/for-mfd-fixes pza/imx-drm/next] [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/Herve-Codina/mfd-syscon-Add-reference-counting-and-device-managed-support/20240430-164912 base: tip/irq/core patch link: https://lore.kernel.org/r/20240430083730.134918-2-herve.codina%40bootlin.com patch subject: [PATCH 01/17] mfd: syscon: Add reference counting and device managed support config: x86_64-rhel-8.3 (https://download.01.org/0day-ci/archive/20240501/202405010513.GHoo4Vwg-lkp@intel.com/config) compiler: gcc-13 (Ubuntu 13.2.0-4ubuntu3) 13.2.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240501/202405010513.GHoo4Vwg-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/202405010513.GHoo4Vwg-lkp@intel.com/ All errors (new ones prefixed by >>): In file included from drivers/net/can/c_can/c_can_platform.c:36: >> include/linux/mfd/syscon.h:76:15: error: expected ';' before 'void' 76 | static intline void syscon_put_regmap(struct regmap *regmap) | ^~~~~ | ; include/linux/mfd/syscon.h:76:21: warning: no previous prototype for 'syscon_put_regmap' [-Wmissing-prototypes] 76 | static intline void syscon_put_regmap(struct regmap *regmap) | ^~~~~~~~~~~~~~~~~ vim +76 include/linux/mfd/syscon.h 75 > 76 static intline void syscon_put_regmap(struct regmap *regmap) 77 { 78 } 79
Hi Simon, On Tue, 30 Apr 2024 21:34:43 +0100 Simon Horman <horms@kernel.org> wrote: ... > > +static intline void syscon_put_regmap(struct regmap *regmap) > > intline -> inline Sure, this will be fixed in the next iteration. Best regards, Hervé
diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c index 7d0e91164cba..86898831b842 100644 --- a/drivers/mfd/syscon.c +++ b/drivers/mfd/syscon.c @@ -34,6 +34,7 @@ struct syscon { struct regmap *regmap; struct reset_control *reset; struct list_head list; + struct kref refcount; }; static const struct regmap_config syscon_regmap_config = { @@ -147,6 +148,8 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_res) syscon->regmap = regmap; syscon->np = np; + of_node_get(syscon->np); + kref_init(&syscon->refcount); spin_lock(&syscon_list_slock); list_add_tail(&syscon->list, &syscon_list); @@ -168,7 +171,30 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_res) return ERR_PTR(ret); } -static struct regmap *device_node_get_regmap(struct device_node *np, +static void syscon_free(struct kref *kref) +{ + struct syscon *syscon = container_of(kref, struct syscon, refcount); + + spin_lock(&syscon_list_slock); + list_del(&syscon->list); + spin_unlock(&syscon_list_slock); + + regmap_exit(syscon->regmap); + of_node_put(syscon->np); + kfree(syscon); +} + +static void syscon_get(struct syscon *syscon) +{ + kref_get(&syscon->refcount); +} + +static void syscon_put(struct syscon *syscon) +{ + kref_put(&syscon->refcount, syscon_free); +} + +static struct syscon *device_node_get_syscon(struct device_node *np, bool check_res) { struct syscon *entry, *syscon = NULL; @@ -183,9 +209,23 @@ static struct regmap *device_node_get_regmap(struct device_node *np, spin_unlock(&syscon_list_slock); - if (!syscon) + if (!syscon) { syscon = of_syscon_register(np, check_res); + if (IS_ERR(syscon)) + return ERR_CAST(syscon); + } else { + syscon_get(syscon); + } + + return syscon; +} +static struct regmap *device_node_get_regmap(struct device_node *np, + bool check_res) +{ + struct syscon *syscon; + + syscon = device_node_get_syscon(np, check_res); if (IS_ERR(syscon)) return ERR_CAST(syscon); @@ -198,12 +238,23 @@ struct regmap *device_node_to_regmap(struct device_node *np) } EXPORT_SYMBOL_GPL(device_node_to_regmap); -struct regmap *syscon_node_to_regmap(struct device_node *np) +static struct syscon *syscon_node_to_syscon(struct device_node *np) { if (!of_device_is_compatible(np, "syscon")) return ERR_PTR(-EINVAL); - return device_node_get_regmap(np, true); + return device_node_get_syscon(np, true); +} + +struct regmap *syscon_node_to_regmap(struct device_node *np) +{ + struct syscon *syscon; + + syscon = syscon_node_to_syscon(np); + if (IS_ERR(syscon)) + return ERR_CAST(syscon); + + return syscon->regmap; } EXPORT_SYMBOL_GPL(syscon_node_to_regmap); @@ -223,11 +274,11 @@ struct regmap *syscon_regmap_lookup_by_compatible(const char *s) } EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_compatible); -struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np, - const char *property) +static struct syscon *syscon_lookup_by_phandle(struct device_node *np, + const char *property) { struct device_node *syscon_np; - struct regmap *regmap; + struct syscon *syscon; if (property) syscon_np = of_parse_phandle(np, property, 0); @@ -237,12 +288,24 @@ struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np, if (!syscon_np) return ERR_PTR(-ENODEV); - regmap = syscon_node_to_regmap(syscon_np); + syscon = syscon_node_to_syscon(syscon_np); if (property) of_node_put(syscon_np); - return regmap; + return syscon; +} + +struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np, + const char *property) +{ + struct syscon *syscon; + + syscon = syscon_lookup_by_phandle(np, property); + if (IS_ERR(syscon)) + return ERR_CAST(syscon); + + return syscon->regmap; } EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle); @@ -293,6 +356,70 @@ struct regmap *syscon_regmap_lookup_by_phandle_optional(struct device_node *np, } EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle_optional); +static struct syscon *syscon_from_regmap(struct regmap *regmap) +{ + struct syscon *entry, *syscon = NULL; + + spin_lock(&syscon_list_slock); + + list_for_each_entry(entry, &syscon_list, list) + if (entry->regmap == regmap) { + syscon = entry; + break; + } + + spin_unlock(&syscon_list_slock); + + return syscon; +} + +void syscon_put_regmap(struct regmap *regmap) +{ + struct syscon *syscon; + + syscon = syscon_from_regmap(regmap); + if (!syscon) + return; + + syscon_put(syscon); +} +EXPORT_SYMBOL_GPL(syscon_put_regmap); + +static void devm_syscon_release(struct device *dev, void *res) +{ + syscon_put(*(struct syscon **)res); +} + +static struct regmap *__devm_syscon_get(struct device *dev, + struct syscon *syscon) +{ + struct syscon **ptr; + + if (IS_ERR(syscon)) + return ERR_CAST(syscon); + + ptr = devres_alloc(devm_syscon_release, sizeof(struct syscon *), GFP_KERNEL); + if (!ptr) { + syscon_put(syscon); + return ERR_PTR(-ENOMEM); + } + + *ptr = syscon; + devres_add(dev, ptr); + + return syscon->regmap; +} + +struct regmap *devm_syscon_regmap_lookup_by_phandle(struct device *dev, + struct device_node *np, + const char *property) +{ + struct syscon *syscon = syscon_lookup_by_phandle(np, property); + + return __devm_syscon_get(dev, syscon); +} +EXPORT_SYMBOL_GPL(devm_syscon_regmap_lookup_by_phandle); + static int syscon_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; diff --git a/include/linux/mfd/syscon.h b/include/linux/mfd/syscon.h index c315903f6dab..164b9bcb49c3 100644 --- a/include/linux/mfd/syscon.h +++ b/include/linux/mfd/syscon.h @@ -15,6 +15,7 @@ #include <linux/errno.h> struct device_node; +struct device; #ifdef CONFIG_MFD_SYSCON struct regmap *device_node_to_regmap(struct device_node *np); @@ -28,6 +29,11 @@ struct regmap *syscon_regmap_lookup_by_phandle_args(struct device_node *np, unsigned int *out_args); struct regmap *syscon_regmap_lookup_by_phandle_optional(struct device_node *np, const char *property); +void syscon_put_regmap(struct regmap *regmap); + +struct regmap *devm_syscon_regmap_lookup_by_phandle(struct device *dev, + struct device_node *np, + const char *property); #else static inline struct regmap *device_node_to_regmap(struct device_node *np) { @@ -67,6 +73,18 @@ static inline struct regmap *syscon_regmap_lookup_by_phandle_optional( return NULL; } +static intline void syscon_put_regmap(struct regmap *regmap) +{ +} + +static inline +struct regmap *devm_syscon_regmap_lookup_by_phandle(struct device *dev, + struct device_node *np, + const char *property) +{ + return NULL; +} + #endif #endif /* __LINUX_MFD_SYSCON_H__ */