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 > --- > > 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 > #include > #include > +#include > #include > > 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 -- Michal Simek, Ing. (M.Eng), OpenPGP -> KeyID: FE3D1F91 w: www.monstr.eu p: +42-0-721842854 Maintainer of Linux kernel - Microblaze cpu - http://www.monstr.eu/fdt/ Maintainer of Linux kernel - Xilinx Zynq ARM architecture Microblaze U-BOOT custodian and responsible for u-boot arm zynq platform