[PATCH] mfd: syscon: Decouple syscon interface from syscon devices
Pankaj Dubey
pankaj.dubey at samsung.com
Sun Aug 31 21:28:55 PDT 2014
Sorry I forgot to add maintainer into CC.
+Lee Jones
Any comments on this patch.
As a lot of Exynos PMU patch sets are dependent on this patch.
Thanks,
Pankaj Dubey
> -----Original Message-----
> From: Pankaj Dubey [mailto:pankaj.dubey at samsung.com]
> Sent: Friday, August 22, 2014 1:40 PM
> To: linux-arm-kernel at lists.infradead.org;
linux-samsung-soc at vger.kernel.org; linux-
> kernel at vger.kernel.org
> Cc: kgene.kim at samsung.com; linux at arm.linux.org.uk; arnd at arndb.de;
> vikas.sajjan at samsung.com; joshi at samsung.com; naushad at samsung.com;
> thomas.ab at samsung.com; chow.kim at samsung.com; Tomasz Figa; Pankaj Dubey
> Subject: [PATCH] mfd: syscon: Decouple syscon interface from syscon
devices
>
> From: Tomasz Figa <t.figa at samsung.com>
>
> Currently a syscon entity can be only registered directly through a
platform device
> that binds to a dedicated driver. However in certain use cases it is
desirable to make a
> device used with another driver a syscon interface provider. For example,
certain
> SoCs (e.g. Exynos) contain system controller blocks which perform various
functions
> such as power domain control, CPU power management, low power mode
control,
> but in addition contain certain IP integration glue, such as various
signal masks,
> coprocessor power control, etc. In such case, there is a need to have a
dedicated
> driver for such system controller but also share registers with other
drivers. The latter
> is where the syscon interface is helpful.
>
> This patch decouples syscon object from syscon driver, so that it can be
registered
> from any driver in addition to the original "syscon" platform driver.
>
> Signed-off-by: Tomasz Figa <t.figa at samsung.com>
> Signed-off-by: Pankaj Dubey <pankaj.dubey at samsung.com>
> ---
>
> RFC patch [1] was posted by Tomasz Figa. This patch addresses some of
comments
> given by Arnd to RFC patch, and further decouples syscon from device
model. It also
> gives flexibility of registering with syscon at early stage using
device_node object.
>
> [1]: https://lkml.org/lkml/2014/6/17/331
>
> drivers/mfd/syscon.c | 112 ++++++++++++++++++++++++++++----
> ------------
> include/linux/mfd/syscon.h | 14 ++++++
> 2 files changed, 86 insertions(+), 40 deletions(-)
>
> diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c index
ca15878..a91db30
> 100644
> --- a/drivers/mfd/syscon.c
> +++ b/drivers/mfd/syscon.c
> @@ -14,6 +14,7 @@
>
> #include <linux/err.h>
> #include <linux/io.h>
> +#include <linux/list.h>
> #include <linux/module.h>
> #include <linux/of.h>
> #include <linux/of_address.h>
> @@ -22,33 +23,32 @@
> #include <linux/platform_device.h>
> #include <linux/regmap.h>
> #include <linux/mfd/syscon.h>
> +#include <linux/slab.h>
>
> -static struct platform_driver syscon_driver;
> +static DEFINE_SPINLOCK(syscon_list_slock);
> +static LIST_HEAD(syscon_list);
>
> struct syscon {
> + struct device_node *np;
> struct regmap *regmap;
> + struct list_head list;
> };
>
> -static int syscon_match_node(struct device *dev, void *data) -{
> - struct device_node *dn = data;
> -
> - return (dev->of_node == dn) ? 1 : 0;
> -}
> -
> struct regmap *syscon_node_to_regmap(struct device_node *np) {
> - struct syscon *syscon;
> - struct device *dev;
> + struct syscon *entry, *syscon = NULL;
>
> - dev = driver_find_device(&syscon_driver.driver, NULL, np,
> - syscon_match_node);
> - if (!dev)
> - return ERR_PTR(-EPROBE_DEFER);
> + spin_lock(&syscon_list_slock);
>
> - syscon = dev_get_drvdata(dev);
> + list_for_each_entry(entry, &syscon_list, list)
> + if (entry->np == np) {
> + syscon = entry;
> + break;
> + }
>
> - return syscon->regmap;
> + spin_unlock(&syscon_list_slock);
> +
> + return syscon ? syscon->regmap : ERR_PTR(-EPROBE_DEFER);
> }
> EXPORT_SYMBOL_GPL(syscon_node_to_regmap);
>
> @@ -68,24 +68,22 @@ struct regmap
> *syscon_regmap_lookup_by_compatible(const char *s) }
> EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_compatible);
>
> -static int syscon_match_pdevname(struct device *dev, void *data) -{
> - return !strcmp(dev_name(dev), (const char *)data);
> -}
> -
> struct regmap *syscon_regmap_lookup_by_pdevname(const char *s) {
> - struct device *dev;
> - struct syscon *syscon;
> -
> - dev = driver_find_device(&syscon_driver.driver, NULL, (void *)s,
> - syscon_match_pdevname);
> - if (!dev)
> - return ERR_PTR(-EPROBE_DEFER);
> -
> - syscon = dev_get_drvdata(dev);
> + struct syscon *entry, *syscon = NULL;
> + struct platform_device *pdev = NULL;
> +
> + spin_lock(&syscon_list_slock);
> + list_for_each_entry(entry, &syscon_list, list) {
> + pdev = of_find_device_by_node(entry->np);
> + if (pdev && !strcmp(dev_name(&pdev->dev), s)) {
> + syscon = entry;
> + break;
> + }
> + }
> + spin_unlock(&syscon_list_slock);
>
> - return syscon->regmap;
> + return syscon ? syscon->regmap : ERR_PTR(-EPROBE_DEFER);
> }
> EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_pdevname);
>
> @@ -121,17 +119,49 @@ static struct regmap_config syscon_regmap_config = {
> .reg_stride = 4,
> };
>
> +void of_syscon_unregister(struct device_node *np) {
> + struct syscon *entry;
> +
> + spin_lock(&syscon_list_slock);
> +
> + list_for_each_entry(entry, &syscon_list, list)
> + if (entry->np == np) {
> + list_del(&entry->list);
> + break;
> + }
> +
> + spin_unlock(&syscon_list_slock);
> +}
> +EXPORT_SYMBOL_GPL(of_syscon_unregister);
> +
> +int of_syscon_register(struct device_node *np, struct regmap *regmap) {
> + struct syscon *syscon;
> +
> + syscon = kzalloc(sizeof(*syscon), GFP_KERNEL);
> + if (!syscon)
> + return -ENOMEM;
> +
> + syscon->regmap = regmap;
> + syscon->np = np;
> +
> + spin_lock(&syscon_list_slock);
> + list_add_tail(&syscon->list, &syscon_list);
> + spin_unlock(&syscon_list_slock);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(of_syscon_register);
> +
> static int syscon_probe(struct platform_device *pdev) {
> struct device *dev = &pdev->dev;
> struct syscon_platform_data *pdata = dev_get_platdata(dev);
> - struct syscon *syscon;
> + struct regmap *regmap;
> struct resource *res;
> void __iomem *base;
> -
> - syscon = devm_kzalloc(dev, sizeof(*syscon), GFP_KERNEL);
> - if (!syscon)
> - return -ENOMEM;
> + int ret;
>
> res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> if (!res)
> @@ -144,14 +174,16 @@ static int syscon_probe(struct platform_device
*pdev)
> syscon_regmap_config.max_register = res->end - res->start - 3;
> if (pdata)
> syscon_regmap_config.name = pdata->label;
> - syscon->regmap = devm_regmap_init_mmio(dev, base,
> + regmap = devm_regmap_init_mmio(dev, base,
> &syscon_regmap_config);
> - if (IS_ERR(syscon->regmap)) {
> + if (IS_ERR(regmap)) {
> dev_err(dev, "regmap init failed\n");
> - return PTR_ERR(syscon->regmap);
> + return PTR_ERR(regmap);
> }
>
> - platform_set_drvdata(pdev, syscon);
> + ret = of_syscon_register(dev->of_node, regmap);
> + if (ret)
> + return ret;
>
> dev_dbg(dev, "regmap %pR registered\n", res);
>
> diff --git a/include/linux/mfd/syscon.h b/include/linux/mfd/syscon.h index
> 75e543b..dc2807b 100644
> --- a/include/linux/mfd/syscon.h
> +++ b/include/linux/mfd/syscon.h
> @@ -18,8 +18,12 @@
> #include <linux/err.h>
>
> struct device_node;
> +struct regmap;
>
> #ifdef CONFIG_MFD_SYSCON
> +extern int of_syscon_register(struct device_node *np, struct regmap
> +*regmap); extern void of_syscon_unregister(struct device_node *np);
> +
> extern struct regmap *syscon_node_to_regmap(struct device_node *np);
extern
> struct regmap *syscon_regmap_lookup_by_compatible(const char *s); extern
struct
> regmap *syscon_regmap_lookup_by_pdevname(const char *s); @@ -27,6 +31,16
> @@ extern struct regmap *syscon_regmap_lookup_by_phandle(
> struct device_node *np,
> const char *property);
> #else
> +static inline int of_syscon_register(struct device_node *np,
> + struct regmap *regmap)
> +{
> + return -ENOSYS;
> +}
> +
> +static inline void of_syscon_unregister(struct device_node *np) { }
> +
> static inline struct regmap *syscon_node_to_regmap(struct device_node
*np) {
> return ERR_PTR(-ENOSYS);
> --
> 1.7.9.5
More information about the linux-arm-kernel
mailing list