diff options
Diffstat (limited to 'drivers/mfd/syscon.c')
-rw-r--r-- | drivers/mfd/syscon.c | 109 |
1 files changed, 68 insertions, 41 deletions
diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c index f1e6559d71..3c2e1241fd 100644 --- a/drivers/mfd/syscon.c +++ b/drivers/mfd/syscon.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* System Control Driver * * Based on linux driver by: * Copyright (C) 2012 Freescale Semiconductor, Inc. * Copyright (C) 2012 Linaro Ltd. * Author: Dong Aisheng <dong.aisheng@linaro.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <io.h> @@ -20,6 +16,7 @@ #include <of_address.h> #include <linux/err.h> #include <linux/clk.h> +#include <linux/regmap.h> #include <mfd/syscon.h> @@ -40,13 +37,12 @@ static const struct regmap_config syscon_regmap_config = { static struct syscon *of_syscon_register(struct device_node *np, bool check_clk) { - int ret; + struct regmap_config syscon_config = syscon_regmap_config; struct syscon *syscon; + u32 reg_io_width; + int ret; struct resource res; - if (!of_device_is_compatible(np, "syscon")) - return ERR_PTR(-EINVAL); - syscon = xzalloc(sizeof(*syscon)); if (of_address_to_resource(np, 0, &res)) { @@ -55,12 +51,31 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_clk) } syscon->base = IOMEM(res.start); - syscon->np = np; - - list_add_tail(&syscon->list, &syscon_list); - syscon->regmap = of_regmap_init_mmio_clk(np, NULL, syscon->base, - &syscon_regmap_config); + /* Parse the device's DT node for an endianness specification */ + if (of_property_read_bool(np, "big-endian")) + syscon_config.val_format_endian = REGMAP_ENDIAN_BIG; + else if (of_property_read_bool(np, "little-endian")) + syscon_config.val_format_endian = REGMAP_ENDIAN_LITTLE; + else if (of_property_read_bool(np, "native-endian")) + syscon_config.val_format_endian = REGMAP_ENDIAN_NATIVE; + + /* + * search for reg-io-width property in DT. If it is not provided, + * default to 4 bytes. regmap_init_mmio will return an error if values + * are invalid so there is no need to check them here. + */ + ret = of_property_read_u32(np, "reg-io-width", ®_io_width); + if (ret) + reg_io_width = 4; + + syscon_config.name = np->full_name; + syscon_config.reg_stride = reg_io_width; + syscon_config.val_bits = reg_io_width * 8; + syscon_config.max_register = resource_size(&res) - reg_io_width; + + syscon->regmap = regmap_init_mmio_clk(NULL, NULL, syscon->base, + &syscon_config); if (check_clk) { struct clk *clk = of_clk_get(np, 0); @@ -76,6 +91,8 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_clk) } } + list_add_tail(&syscon->list, &syscon_list); + return syscon; err_map: @@ -83,7 +100,7 @@ err_map: return ERR_PTR(ret); } -static struct syscon *node_to_syscon(struct device_node *np) +static struct syscon *node_to_syscon(struct device_node *np, bool check_clk) { struct syscon *entry, *syscon = NULL; @@ -94,7 +111,7 @@ static struct syscon *node_to_syscon(struct device_node *np) } if (!syscon) - syscon = of_syscon_register(np, true); + syscon = of_syscon_register(np, check_clk); if (IS_ERR(syscon)) return ERR_CAST(syscon); @@ -104,27 +121,26 @@ static struct syscon *node_to_syscon(struct device_node *np) static void __iomem *syscon_node_to_base(struct device_node *np) { - struct syscon *syscon = node_to_syscon(np); + struct syscon *syscon; + struct clk *clk; + if (!of_device_is_compatible(np, "syscon")) + return ERR_PTR(-EINVAL); + + syscon = node_to_syscon(np, true); if (IS_ERR(syscon)) return ERR_CAST(syscon); - return syscon->base; -} - -void __iomem *syscon_base_lookup_by_pdevname(const char *s) -{ - struct syscon *syscon; - struct device_d *dev; + /* Returning the direct pointer here side steps the regmap + * and any specified clock wouldn't be enabled on access. + * Deal with this by enabling the clock permanently if any + * syscon_node_to_base users exist. + */ + clk = of_clk_get(np, 0); + if (!IS_ERR(clk)) + clk_enable(clk); - for_each_device(dev) { - if (!strcmp(dev_name(dev), s)) { - syscon = dev->priv; - return syscon->base; - } - } - - return ERR_PTR(-ENODEV); + return syscon->base; } void __iomem *syscon_base_lookup_by_phandle(struct device_node *np, @@ -143,16 +159,31 @@ void __iomem *syscon_base_lookup_by_phandle(struct device_node *np, return syscon_node_to_base(syscon_np); } -struct regmap *syscon_node_to_regmap(struct device_node *np) +static struct regmap *__device_node_to_regmap(struct device_node *np, + bool check_clk) { - struct syscon *syscon = node_to_syscon(np); + struct syscon *syscon; + syscon = node_to_syscon(np, check_clk); if (IS_ERR(syscon)) return ERR_CAST(syscon); return syscon->regmap; } +struct regmap *device_node_to_regmap(struct device_node *np) +{ + return __device_node_to_regmap(np, false); +} + +struct regmap *syscon_node_to_regmap(struct device_node *np) +{ + if (!of_device_is_compatible(np, "syscon")) + return ERR_PTR(-EINVAL); + + return __device_node_to_regmap(np, true); +} + struct regmap *syscon_regmap_lookup_by_compatible(const char *s) { struct device_node *syscon_np; @@ -186,7 +217,7 @@ struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np, return regmap; } -static int syscon_probe(struct device_d *dev) +static int syscon_probe(struct device *dev) { struct syscon *syscon; struct resource *res; @@ -212,17 +243,13 @@ static struct platform_device_id syscon_ids[] = { { } }; -static struct driver_d syscon_driver = { +static struct driver syscon_driver = { .name = "syscon", .probe = syscon_probe, .id_table = syscon_ids, }; -static int __init syscon_init(void) -{ - return platform_driver_register(&syscon_driver); -} -core_initcall(syscon_init); +core_platform_driver(syscon_driver); MODULE_AUTHOR("Dong Aisheng <dong.aisheng@linaro.org>"); MODULE_DESCRIPTION("System Control driver"); |