diff mbox

[RFC,2/3] mfd: syscon: Support early initialization

Message ID 1785585d090175da81b561b17eeef95d991ff0de.1392045742.git.michal.simek@xilinx.com (mailing list archive)
State New, archived
Headers show

Commit Message

Michal Simek Feb. 10, 2014, 3:22 p.m. UTC
Some platforms need to get system controller
ready as soon as possible.
The patch provides early_syscon_initialization
which create early mapping for all syscon compatible
devices in early_syscon_probe.
Regmap is get via syscon_early_regmap_lookup_by_phandle()

Regular device probes attach device to regmap
via regmap_attach_dev().

For early syscon initialization is necessary to extend
struct syscon and provide remove function
which unmap all early init structures.

Signed-off-by: Michal Simek <michal.simek@xilinx.com>
---

 drivers/mfd/syscon.c       | 126 +++++++++++++++++++++++++++++++++++++++------
 include/linux/mfd/syscon.h |  11 ++++
 2 files changed, 120 insertions(+), 17 deletions(-)

--
1.8.2.3

Comments

Michal Simek Feb. 10, 2014, 3:42 p.m. UTC | #1
On 02/10/2014 04:22 PM, Michal Simek wrote:
> Some platforms need to get system controller
> ready as soon as possible.
> The patch provides early_syscon_initialization
> which create early mapping for all syscon compatible
> devices in early_syscon_probe.
> Regmap is get via syscon_early_regmap_lookup_by_phandle()
> 
> Regular device probes attach device to regmap
> via regmap_attach_dev().
> 
> For early syscon initialization is necessary to extend
> struct syscon and provide remove function
> which unmap all early init structures.
> 
> Signed-off-by: Michal Simek <michal.simek@xilinx.com>
> ---
> 
>  drivers/mfd/syscon.c       | 126 +++++++++++++++++++++++++++++++++++++++------
>  include/linux/mfd/syscon.h |  11 ++++
>  2 files changed, 120 insertions(+), 17 deletions(-)
> 
> diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c
> index 71841f9..5935f02 100644
> --- a/drivers/mfd/syscon.c
> +++ b/drivers/mfd/syscon.c
> @@ -20,12 +20,15 @@
>  #include <linux/of_platform.h>
>  #include <linux/platform_device.h>
>  #include <linux/regmap.h>
> +#include <linux/slab.h>
>  #include <linux/mfd/syscon.h>
> 
>  static struct platform_driver syscon_driver;
> 
>  struct syscon {
> +	void __iomem *base;
>  	struct regmap *regmap;
> +	struct resource res;
>  };
> 
>  static int syscon_match_node(struct device *dev, void *data)
> @@ -95,6 +98,24 @@ struct regmap *syscon_regmap_lookup_by_pdevname(const char *s)
>  }
>  EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_pdevname);
> 
> +struct regmap *syscon_early_regmap_lookup_by_phandle(struct device_node *np,
> +						     const char *property)
> +{
> +	struct device_node *syscon_np;
> +	struct syscon *syscon;
> +
> +	syscon_np = of_parse_phandle(np, property, 0);
> +	if (!syscon_np)
> +		return ERR_PTR(-ENODEV);
> +
> +	syscon = syscon_np->data;
> +
> +	of_node_put(syscon_np);
> +
> +	return syscon->regmap;
> +}
> +EXPORT_SYMBOL_GPL(syscon_early_regmap_lookup_by_phandle);
> +
>  struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np,
>  					const char *property)
>  {
> @@ -128,40 +149,110 @@ static int syscon_probe(struct platform_device *pdev)
>  	struct device *dev = &pdev->dev;
>  	struct syscon *syscon;
>  	struct resource *res;
> -	void __iomem *base;
> 
> -	syscon = devm_kzalloc(dev, sizeof(*syscon), GFP_KERNEL);
> +	/* Early syscon init */
> +	if (pdev->dev.of_node && pdev->dev.of_node->data) {
> +		syscon = pdev->dev.of_node->data;
> +		res = &syscon->res;
> +		regmap_attach_dev(dev, syscon->regmap, &syscon_regmap_config);
> +	} else {
> +
> +		syscon = devm_kzalloc(dev, sizeof(*syscon), GFP_KERNEL);
> +		if (!syscon)
> +			return -ENOMEM;
> +
> +		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +		if (!res)
> +			return -ENOENT;
> +
> +		syscon->base = devm_ioremap(dev, res->start,
> +					    resource_size(res));
> +		if (!syscon->base)
> +			return -ENOMEM;
> +
> +		syscon_regmap_config.max_register = res->end - res->start - 3;
> +		syscon->regmap = devm_regmap_init_mmio(dev, syscon->base,
> +						&syscon_regmap_config);
> +		if (IS_ERR(syscon->regmap)) {
> +			dev_err(dev, "regmap init failed\n");
> +			return PTR_ERR(syscon->regmap);
> +		}
> +	}
> +
> +	platform_set_drvdata(pdev, syscon);
> +
> +	dev_info(dev, "regmap %pR registered\n", res);
> +
> +	return 0;
> +}
> +
> +static const struct platform_device_id syscon_ids[] = {
> +	{ "syscon", },
> +	{ }
> +};
> +
> +static int syscon_remove(struct platform_device *pdev)
> +{
> +	struct syscon *syscon = platform_get_drvdata(pdev);
> +
> +	if (pdev->dev.of_node && pdev->dev.of_node->data) {
> +		iounmap(syscon->base);
> +		kfree(syscon);
> +	}
> +
> +	return 0;
> +}
> +
> +static int early_syscon_probe(struct device_node *np)
> +{
> +	struct syscon *syscon;
> +
> +	syscon = kzalloc(sizeof(*syscon), GFP_KERNEL);
>  	if (!syscon)
>  		return -ENOMEM;
> 
> -	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> -	if (!res)
> -		return -ENOENT;
> +	if (of_address_to_resource(np, 0, &syscon->res))
> +		return -EINVAL;
> 
> -	base = devm_ioremap(dev, res->start, resource_size(res));
> -	if (!base)
> -		return -ENOMEM;
> +	syscon->base = ioremap(syscon->res.start, resource_size(&syscon->res));
> +	if (!syscon->base) {
> +		pr_err("%s: Unable to map I/O memory\n", __func__);
> +		return PTR_ERR(syscon->base);
> +	}
> 
> -	syscon_regmap_config.max_register = res->end - res->start - 3;
> -	syscon->regmap = devm_regmap_init_mmio(dev, base,
> -					&syscon_regmap_config);
> +	syscon_regmap_config.max_register = syscon->res.end -
> +					    syscon->res.start - 3;
> +	syscon->regmap = regmap_init_mmio(NULL, syscon->base,
> +					  &syscon_regmap_config);
>  	if (IS_ERR(syscon->regmap)) {
> -		dev_err(dev, "regmap init failed\n");
> +		pr_err("regmap init failed\n");
>  		return PTR_ERR(syscon->regmap);
>  	}
> 
> -	platform_set_drvdata(pdev, syscon);
> +	np->data = syscon;
> 
> -	dev_info(dev, "regmap %pR registered\n", res);
> +	of_node_put(np);
> +
> +	pr_info("%s: regmap %pR registered\n", np->full_name, &syscon->res);
> 
>  	return 0;
>  }
> 
> -static const struct platform_device_id syscon_ids[] = {
> -	{ "syscon", },
> -	{ }
> +static struct of_device_id of_syscon_ids[] = {
> +	{ .compatible = "syscon" },
> +	{},
>  };
> 
> +void __init early_syscon_init(void)
> +{
> +	struct device_node *np;
> +
> +	for_each_matching_node_and_match(np, of_syscon_ids, NULL) {
> +		if (!early_syscon_probe(np))

just
if (early_syscon_probe(np))
	BUG();
here.

Thanks,
Michal
Lee Jones Feb. 12, 2014, 9:54 a.m. UTC | #2
On Mon, 10 Feb 2014, Michal Simek wrote:

> Some platforms need to get system controller
> ready as soon as possible.
> The patch provides early_syscon_initialization
> which create early mapping for all syscon compatible
> devices in early_syscon_probe.
> Regmap is get via syscon_early_regmap_lookup_by_phandle()
> 
> Regular device probes attach device to regmap
> via regmap_attach_dev().
> 
> For early syscon initialization is necessary to extend
> struct syscon and provide remove function
> which unmap all early init structures.
> 
> Signed-off-by: Michal Simek <michal.simek@xilinx.com>
> ---
> 
>  drivers/mfd/syscon.c       | 126 +++++++++++++++++++++++++++++++++++++++------
>  include/linux/mfd/syscon.h |  11 ++++
>  2 files changed, 120 insertions(+), 17 deletions(-)
> 
> diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c

<snip>

>  struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np,
>  					const char *property)
>  {
> @@ -128,40 +149,110 @@ static int syscon_probe(struct platform_device *pdev)
>  	struct device *dev = &pdev->dev;
>  	struct syscon *syscon;
>  	struct resource *res;
> -	void __iomem *base;
> 
> -	syscon = devm_kzalloc(dev, sizeof(*syscon), GFP_KERNEL);
> +	/* Early syscon init */
> +	if (pdev->dev.of_node && pdev->dev.of_node->data) {
> +		syscon = pdev->dev.of_node->data;
> +		res = &syscon->res;
> +		regmap_attach_dev(dev, syscon->regmap, &syscon_regmap_config);

Instead of duplicating all of the init code with early and late
versions of each, is there any reason why we can't always do the early
stuff early and then connect it to the dev infrastructure when it
becomes available?

> +	} else {
> +
> +		syscon = devm_kzalloc(dev, sizeof(*syscon), GFP_KERNEL);
> +		if (!syscon)
> +			return -ENOMEM;
> +
> +		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +		if (!res)
> +			return -ENOENT;
> +
> +		syscon->base = devm_ioremap(dev, res->start,
> +					    resource_size(res));
> +		if (!syscon->base)
> +			return -ENOMEM;
> +
> +		syscon_regmap_config.max_register = res->end - res->start - 3;
> +		syscon->regmap = devm_regmap_init_mmio(dev, syscon->base,
> +						&syscon_regmap_config);
> +		if (IS_ERR(syscon->regmap)) {
> +			dev_err(dev, "regmap init failed\n");
> +			return PTR_ERR(syscon->regmap);
> +		}
> +	}
> +	platform_set_drvdata(pdev, syscon);
> +
> +	dev_info(dev, "regmap %pR registered\n", res);
> +
> +	return 0;
> +}
> +
> +static const struct platform_device_id syscon_ids[] = {
> +	{ "syscon", },
> +	{ }
> +};
> +
> +static int syscon_remove(struct platform_device *pdev)
> +{
> +	struct syscon *syscon = platform_get_drvdata(pdev);
> +
> +	if (pdev->dev.of_node && pdev->dev.of_node->data) {
> +		iounmap(syscon->base);
> +		kfree(syscon);
> +	}
> +
> +	return 0;
> +}
> +
> +static int early_syscon_probe(struct device_node *np)
> +{
> +	struct syscon *syscon;
> +
> +	syscon = kzalloc(sizeof(*syscon), GFP_KERNEL);
>  	if (!syscon)
>  		return -ENOMEM;
> 
> -	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> -	if (!res)
> -		return -ENOENT;
> +	if (of_address_to_resource(np, 0, &syscon->res))
> +		return -EINVAL;
> 
> -	base = devm_ioremap(dev, res->start, resource_size(res));
> -	if (!base)
> -		return -ENOMEM;
> +	syscon->base = ioremap(syscon->res.start, resource_size(&syscon->res));
> +	if (!syscon->base) {
> +		pr_err("%s: Unable to map I/O memory\n", __func__);
> +		return PTR_ERR(syscon->base);
> +	}
> 
> -	syscon_regmap_config.max_register = res->end - res->start - 3;
> -	syscon->regmap = devm_regmap_init_mmio(dev, base,
> -					&syscon_regmap_config);
> +	syscon_regmap_config.max_register = syscon->res.end -
> +					    syscon->res.start - 3;
> +	syscon->regmap = regmap_init_mmio(NULL, syscon->base,
> +					  &syscon_regmap_config);
>  	if (IS_ERR(syscon->regmap)) {
> -		dev_err(dev, "regmap init failed\n");
> +		pr_err("regmap init failed\n");
>  		return PTR_ERR(syscon->regmap);
>  	}
> 
> -	platform_set_drvdata(pdev, syscon);
> +	np->data = syscon;
> 
> -	dev_info(dev, "regmap %pR registered\n", res);
> +	of_node_put(np);
> +
> +	pr_info("%s: regmap %pR registered\n", np->full_name, &syscon->res);
> 
>  	return 0;
>  }
> 
> -static const struct platform_device_id syscon_ids[] = {
> -	{ "syscon", },
> -	{ }
> +static struct of_device_id of_syscon_ids[] = {
> +	{ .compatible = "syscon" },
> +	{},
>  };
> 
> +void __init early_syscon_init(void)
> +{
> +	struct device_node *np;
> +
> +	for_each_matching_node_and_match(np, of_syscon_ids, NULL) {
> +		if (!early_syscon_probe(np))
> +			BUG();
> +	}
> +}
> +
>  static struct platform_driver syscon_driver = {
>  	.driver = {
>  		.name = "syscon",
> @@ -169,6 +260,7 @@ static struct platform_driver syscon_driver = {
>  		.of_match_table = of_syscon_match,
>  	},
>  	.probe		= syscon_probe,
> +	.remove		= syscon_remove,
>  	.id_table	= syscon_ids,
>  };
> 
> diff --git a/include/linux/mfd/syscon.h b/include/linux/mfd/syscon.h
> index 8789fa3..465c092 100644
> --- a/include/linux/mfd/syscon.h
> +++ b/include/linux/mfd/syscon.h
> @@ -24,6 +24,10 @@ extern struct regmap *syscon_regmap_lookup_by_pdevname(const char *s);
>  extern struct regmap *syscon_regmap_lookup_by_phandle(
>  					struct device_node *np,
>  					const char *property);
> +extern struct regmap *syscon_early_regmap_lookup_by_phandle(
> +					struct device_node *np,
> +					const char *property);
> +extern void early_syscon_init(void);
>  #else
>  static inline struct regmap *syscon_node_to_regmap(struct device_node *np)
>  {
> @@ -46,6 +50,13 @@ static inline struct regmap *syscon_regmap_lookup_by_phandle(
>  {
>  	return ERR_PTR(-ENOSYS);
>  }
> +
> +static struct regmap *syscon_early_regmap_lookup_by_phandle(
> +					struct device_node *np,
> +					const char *property)
> +{
> +	return ERR_PTR(-ENOSYS);
> +}
>  #endif
> 
>  #endif /* __LINUX_MFD_SYSCON_H__ */
Michal Simek Feb. 12, 2014, 10:51 a.m. UTC | #3
On 02/12/2014 10:54 AM, Lee Jones wrote:
> On Mon, 10 Feb 2014, Michal Simek wrote:
> 
>> Some platforms need to get system controller
>> ready as soon as possible.
>> The patch provides early_syscon_initialization
>> which create early mapping for all syscon compatible
>> devices in early_syscon_probe.
>> Regmap is get via syscon_early_regmap_lookup_by_phandle()
>>
>> Regular device probes attach device to regmap
>> via regmap_attach_dev().
>>
>> For early syscon initialization is necessary to extend
>> struct syscon and provide remove function
>> which unmap all early init structures.
>>
>> Signed-off-by: Michal Simek <michal.simek@xilinx.com>
>> ---
>>
>>  drivers/mfd/syscon.c       | 126 +++++++++++++++++++++++++++++++++++++++------
>>  include/linux/mfd/syscon.h |  11 ++++
>>  2 files changed, 120 insertions(+), 17 deletions(-)
>>
>> diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c
> 
> <snip>
> 
>>  struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np,
>>  					const char *property)
>>  {
>> @@ -128,40 +149,110 @@ static int syscon_probe(struct platform_device *pdev)
>>  	struct device *dev = &pdev->dev;
>>  	struct syscon *syscon;
>>  	struct resource *res;
>> -	void __iomem *base;
>>
>> -	syscon = devm_kzalloc(dev, sizeof(*syscon), GFP_KERNEL);
>> +	/* Early syscon init */
>> +	if (pdev->dev.of_node && pdev->dev.of_node->data) {
>> +		syscon = pdev->dev.of_node->data;
>> +		res = &syscon->res;
>> +		regmap_attach_dev(dev, syscon->regmap, &syscon_regmap_config);
> 
> Instead of duplicating all of the init code with early and late
> versions of each, is there any reason why we can't always do the early
> stuff early and then connect it to the dev infrastructure when it
> becomes available?

I am waiting for Arnd and Mark opinions regarding this.
That's why it was just RFC.
There the next question is if this should still stay
in mfd or moved to base.

I wanted to keep this to be driver compatible
that's why I have done it in this way.

We will see.

Thanks,
Michal
diff mbox

Patch

diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c
index 71841f9..5935f02 100644
--- a/drivers/mfd/syscon.c
+++ b/drivers/mfd/syscon.c
@@ -20,12 +20,15 @@ 
 #include <linux/of_platform.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
+#include <linux/slab.h>
 #include <linux/mfd/syscon.h>

 static struct platform_driver syscon_driver;

 struct syscon {
+	void __iomem *base;
 	struct regmap *regmap;
+	struct resource res;
 };

 static int syscon_match_node(struct device *dev, void *data)
@@ -95,6 +98,24 @@  struct regmap *syscon_regmap_lookup_by_pdevname(const char *s)
 }
 EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_pdevname);

+struct regmap *syscon_early_regmap_lookup_by_phandle(struct device_node *np,
+						     const char *property)
+{
+	struct device_node *syscon_np;
+	struct syscon *syscon;
+
+	syscon_np = of_parse_phandle(np, property, 0);
+	if (!syscon_np)
+		return ERR_PTR(-ENODEV);
+
+	syscon = syscon_np->data;
+
+	of_node_put(syscon_np);
+
+	return syscon->regmap;
+}
+EXPORT_SYMBOL_GPL(syscon_early_regmap_lookup_by_phandle);
+
 struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np,
 					const char *property)
 {
@@ -128,40 +149,110 @@  static int syscon_probe(struct platform_device *pdev)
 	struct device *dev = &pdev->dev;
 	struct syscon *syscon;
 	struct resource *res;
-	void __iomem *base;

-	syscon = devm_kzalloc(dev, sizeof(*syscon), GFP_KERNEL);
+	/* Early syscon init */
+	if (pdev->dev.of_node && pdev->dev.of_node->data) {
+		syscon = pdev->dev.of_node->data;
+		res = &syscon->res;
+		regmap_attach_dev(dev, syscon->regmap, &syscon_regmap_config);
+	} else {
+
+		syscon = devm_kzalloc(dev, sizeof(*syscon), GFP_KERNEL);
+		if (!syscon)
+			return -ENOMEM;
+
+		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+		if (!res)
+			return -ENOENT;
+
+		syscon->base = devm_ioremap(dev, res->start,
+					    resource_size(res));
+		if (!syscon->base)
+			return -ENOMEM;
+
+		syscon_regmap_config.max_register = res->end - res->start - 3;
+		syscon->regmap = devm_regmap_init_mmio(dev, syscon->base,
+						&syscon_regmap_config);
+		if (IS_ERR(syscon->regmap)) {
+			dev_err(dev, "regmap init failed\n");
+			return PTR_ERR(syscon->regmap);
+		}
+	}
+
+	platform_set_drvdata(pdev, syscon);
+
+	dev_info(dev, "regmap %pR registered\n", res);
+
+	return 0;
+}
+
+static const struct platform_device_id syscon_ids[] = {
+	{ "syscon", },
+	{ }
+};
+
+static int syscon_remove(struct platform_device *pdev)
+{
+	struct syscon *syscon = platform_get_drvdata(pdev);
+
+	if (pdev->dev.of_node && pdev->dev.of_node->data) {
+		iounmap(syscon->base);
+		kfree(syscon);
+	}
+
+	return 0;
+}
+
+static int early_syscon_probe(struct device_node *np)
+{
+	struct syscon *syscon;
+
+	syscon = kzalloc(sizeof(*syscon), GFP_KERNEL);
 	if (!syscon)
 		return -ENOMEM;

-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!res)
-		return -ENOENT;
+	if (of_address_to_resource(np, 0, &syscon->res))
+		return -EINVAL;

-	base = devm_ioremap(dev, res->start, resource_size(res));
-	if (!base)
-		return -ENOMEM;
+	syscon->base = ioremap(syscon->res.start, resource_size(&syscon->res));
+	if (!syscon->base) {
+		pr_err("%s: Unable to map I/O memory\n", __func__);
+		return PTR_ERR(syscon->base);
+	}

-	syscon_regmap_config.max_register = res->end - res->start - 3;
-	syscon->regmap = devm_regmap_init_mmio(dev, base,
-					&syscon_regmap_config);
+	syscon_regmap_config.max_register = syscon->res.end -
+					    syscon->res.start - 3;
+	syscon->regmap = regmap_init_mmio(NULL, syscon->base,
+					  &syscon_regmap_config);
 	if (IS_ERR(syscon->regmap)) {
-		dev_err(dev, "regmap init failed\n");
+		pr_err("regmap init failed\n");
 		return PTR_ERR(syscon->regmap);
 	}

-	platform_set_drvdata(pdev, syscon);
+	np->data = syscon;

-	dev_info(dev, "regmap %pR registered\n", res);
+	of_node_put(np);
+
+	pr_info("%s: regmap %pR registered\n", np->full_name, &syscon->res);

 	return 0;
 }

-static const struct platform_device_id syscon_ids[] = {
-	{ "syscon", },
-	{ }
+static struct of_device_id of_syscon_ids[] = {
+	{ .compatible = "syscon" },
+	{},
 };

+void __init early_syscon_init(void)
+{
+	struct device_node *np;
+
+	for_each_matching_node_and_match(np, of_syscon_ids, NULL) {
+		if (!early_syscon_probe(np))
+			BUG();
+	}
+}
+
 static struct platform_driver syscon_driver = {
 	.driver = {
 		.name = "syscon",
@@ -169,6 +260,7 @@  static struct platform_driver syscon_driver = {
 		.of_match_table = of_syscon_match,
 	},
 	.probe		= syscon_probe,
+	.remove		= syscon_remove,
 	.id_table	= syscon_ids,
 };

diff --git a/include/linux/mfd/syscon.h b/include/linux/mfd/syscon.h
index 8789fa3..465c092 100644
--- a/include/linux/mfd/syscon.h
+++ b/include/linux/mfd/syscon.h
@@ -24,6 +24,10 @@  extern struct regmap *syscon_regmap_lookup_by_pdevname(const char *s);
 extern struct regmap *syscon_regmap_lookup_by_phandle(
 					struct device_node *np,
 					const char *property);
+extern struct regmap *syscon_early_regmap_lookup_by_phandle(
+					struct device_node *np,
+					const char *property);
+extern void early_syscon_init(void);
 #else
 static inline struct regmap *syscon_node_to_regmap(struct device_node *np)
 {
@@ -46,6 +50,13 @@  static inline struct regmap *syscon_regmap_lookup_by_phandle(
 {
 	return ERR_PTR(-ENOSYS);
 }
+
+static struct regmap *syscon_early_regmap_lookup_by_phandle(
+					struct device_node *np,
+					const char *property)
+{
+	return ERR_PTR(-ENOSYS);
+}
 #endif

 #endif /* __LINUX_MFD_SYSCON_H__ */