root/drivers/mfd/syscon.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. of_syscon_register
  2. device_node_get_regmap
  3. device_node_to_regmap
  4. syscon_node_to_regmap
  5. syscon_regmap_lookup_by_compatible
  6. syscon_regmap_lookup_by_phandle
  7. syscon_probe
  8. syscon_init

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * System Control Driver
   4  *
   5  * Copyright (C) 2012 Freescale Semiconductor, Inc.
   6  * Copyright (C) 2012 Linaro Ltd.
   7  *
   8  * Author: Dong Aisheng <dong.aisheng@linaro.org>
   9  */
  10 
  11 #include <linux/clk.h>
  12 #include <linux/err.h>
  13 #include <linux/hwspinlock.h>
  14 #include <linux/io.h>
  15 #include <linux/init.h>
  16 #include <linux/list.h>
  17 #include <linux/of.h>
  18 #include <linux/of_address.h>
  19 #include <linux/of_platform.h>
  20 #include <linux/platform_data/syscon.h>
  21 #include <linux/platform_device.h>
  22 #include <linux/regmap.h>
  23 #include <linux/mfd/syscon.h>
  24 #include <linux/slab.h>
  25 
  26 static struct platform_driver syscon_driver;
  27 
  28 static DEFINE_SPINLOCK(syscon_list_slock);
  29 static LIST_HEAD(syscon_list);
  30 
  31 struct syscon {
  32         struct device_node *np;
  33         struct regmap *regmap;
  34         struct list_head list;
  35 };
  36 
  37 static const struct regmap_config syscon_regmap_config = {
  38         .reg_bits = 32,
  39         .val_bits = 32,
  40         .reg_stride = 4,
  41 };
  42 
  43 static struct syscon *of_syscon_register(struct device_node *np, bool check_clk)
  44 {
  45         struct clk *clk;
  46         struct syscon *syscon;
  47         struct regmap *regmap;
  48         void __iomem *base;
  49         u32 reg_io_width;
  50         int ret;
  51         struct regmap_config syscon_config = syscon_regmap_config;
  52         struct resource res;
  53 
  54         syscon = kzalloc(sizeof(*syscon), GFP_KERNEL);
  55         if (!syscon)
  56                 return ERR_PTR(-ENOMEM);
  57 
  58         if (of_address_to_resource(np, 0, &res)) {
  59                 ret = -ENOMEM;
  60                 goto err_map;
  61         }
  62 
  63         base = ioremap(res.start, resource_size(&res));
  64         if (!base) {
  65                 ret = -ENOMEM;
  66                 goto err_map;
  67         }
  68 
  69         /* Parse the device's DT node for an endianness specification */
  70         if (of_property_read_bool(np, "big-endian"))
  71                 syscon_config.val_format_endian = REGMAP_ENDIAN_BIG;
  72         else if (of_property_read_bool(np, "little-endian"))
  73                 syscon_config.val_format_endian = REGMAP_ENDIAN_LITTLE;
  74         else if (of_property_read_bool(np, "native-endian"))
  75                 syscon_config.val_format_endian = REGMAP_ENDIAN_NATIVE;
  76 
  77         /*
  78          * search for reg-io-width property in DT. If it is not provided,
  79          * default to 4 bytes. regmap_init_mmio will return an error if values
  80          * are invalid so there is no need to check them here.
  81          */
  82         ret = of_property_read_u32(np, "reg-io-width", &reg_io_width);
  83         if (ret)
  84                 reg_io_width = 4;
  85 
  86         ret = of_hwspin_lock_get_id(np, 0);
  87         if (ret > 0 || (IS_ENABLED(CONFIG_HWSPINLOCK) && ret == 0)) {
  88                 syscon_config.use_hwlock = true;
  89                 syscon_config.hwlock_id = ret;
  90                 syscon_config.hwlock_mode = HWLOCK_IRQSTATE;
  91         } else if (ret < 0) {
  92                 switch (ret) {
  93                 case -ENOENT:
  94                         /* Ignore missing hwlock, it's optional. */
  95                         break;
  96                 default:
  97                         pr_err("Failed to retrieve valid hwlock: %d\n", ret);
  98                         /* fall-through */
  99                 case -EPROBE_DEFER:
 100                         goto err_regmap;
 101                 }
 102         }
 103 
 104         syscon_config.name = of_node_full_name(np);
 105         syscon_config.reg_stride = reg_io_width;
 106         syscon_config.val_bits = reg_io_width * 8;
 107         syscon_config.max_register = resource_size(&res) - reg_io_width;
 108         syscon_config.name = of_node_full_name(np);
 109 
 110         regmap = regmap_init_mmio(NULL, base, &syscon_config);
 111         if (IS_ERR(regmap)) {
 112                 pr_err("regmap init failed\n");
 113                 ret = PTR_ERR(regmap);
 114                 goto err_regmap;
 115         }
 116 
 117         if (check_clk) {
 118                 clk = of_clk_get(np, 0);
 119                 if (IS_ERR(clk)) {
 120                         ret = PTR_ERR(clk);
 121                         /* clock is optional */
 122                         if (ret != -ENOENT)
 123                                 goto err_clk;
 124                 } else {
 125                         ret = regmap_mmio_attach_clk(regmap, clk);
 126                         if (ret)
 127                                 goto err_attach;
 128                 }
 129         }
 130 
 131         syscon->regmap = regmap;
 132         syscon->np = np;
 133 
 134         spin_lock(&syscon_list_slock);
 135         list_add_tail(&syscon->list, &syscon_list);
 136         spin_unlock(&syscon_list_slock);
 137 
 138         return syscon;
 139 
 140 err_attach:
 141         if (!IS_ERR(clk))
 142                 clk_put(clk);
 143 err_clk:
 144         regmap_exit(regmap);
 145 err_regmap:
 146         iounmap(base);
 147 err_map:
 148         kfree(syscon);
 149         return ERR_PTR(ret);
 150 }
 151 
 152 static struct regmap *device_node_get_regmap(struct device_node *np,
 153                                              bool check_clk)
 154 {
 155         struct syscon *entry, *syscon = NULL;
 156 
 157         spin_lock(&syscon_list_slock);
 158 
 159         list_for_each_entry(entry, &syscon_list, list)
 160                 if (entry->np == np) {
 161                         syscon = entry;
 162                         break;
 163                 }
 164 
 165         spin_unlock(&syscon_list_slock);
 166 
 167         if (!syscon)
 168                 syscon = of_syscon_register(np, check_clk);
 169 
 170         if (IS_ERR(syscon))
 171                 return ERR_CAST(syscon);
 172 
 173         return syscon->regmap;
 174 }
 175 
 176 struct regmap *device_node_to_regmap(struct device_node *np)
 177 {
 178         return device_node_get_regmap(np, false);
 179 }
 180 EXPORT_SYMBOL_GPL(device_node_to_regmap);
 181 
 182 struct regmap *syscon_node_to_regmap(struct device_node *np)
 183 {
 184         if (!of_device_is_compatible(np, "syscon"))
 185                 return ERR_PTR(-EINVAL);
 186 
 187         return device_node_get_regmap(np, true);
 188 }
 189 EXPORT_SYMBOL_GPL(syscon_node_to_regmap);
 190 
 191 struct regmap *syscon_regmap_lookup_by_compatible(const char *s)
 192 {
 193         struct device_node *syscon_np;
 194         struct regmap *regmap;
 195 
 196         syscon_np = of_find_compatible_node(NULL, NULL, s);
 197         if (!syscon_np)
 198                 return ERR_PTR(-ENODEV);
 199 
 200         regmap = syscon_node_to_regmap(syscon_np);
 201         of_node_put(syscon_np);
 202 
 203         return regmap;
 204 }
 205 EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_compatible);
 206 
 207 struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np,
 208                                         const char *property)
 209 {
 210         struct device_node *syscon_np;
 211         struct regmap *regmap;
 212 
 213         if (property)
 214                 syscon_np = of_parse_phandle(np, property, 0);
 215         else
 216                 syscon_np = np;
 217 
 218         if (!syscon_np)
 219                 return ERR_PTR(-ENODEV);
 220 
 221         regmap = syscon_node_to_regmap(syscon_np);
 222         of_node_put(syscon_np);
 223 
 224         return regmap;
 225 }
 226 EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle);
 227 
 228 static int syscon_probe(struct platform_device *pdev)
 229 {
 230         struct device *dev = &pdev->dev;
 231         struct syscon_platform_data *pdata = dev_get_platdata(dev);
 232         struct syscon *syscon;
 233         struct regmap_config syscon_config = syscon_regmap_config;
 234         struct resource *res;
 235         void __iomem *base;
 236 
 237         syscon = devm_kzalloc(dev, sizeof(*syscon), GFP_KERNEL);
 238         if (!syscon)
 239                 return -ENOMEM;
 240 
 241         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 242         if (!res)
 243                 return -ENOENT;
 244 
 245         base = devm_ioremap(dev, res->start, resource_size(res));
 246         if (!base)
 247                 return -ENOMEM;
 248 
 249         syscon_config.max_register = res->end - res->start - 3;
 250         if (pdata)
 251                 syscon_config.name = pdata->label;
 252         syscon->regmap = devm_regmap_init_mmio(dev, base, &syscon_config);
 253         if (IS_ERR(syscon->regmap)) {
 254                 dev_err(dev, "regmap init failed\n");
 255                 return PTR_ERR(syscon->regmap);
 256         }
 257 
 258         platform_set_drvdata(pdev, syscon);
 259 
 260         dev_dbg(dev, "regmap %pR registered\n", res);
 261 
 262         return 0;
 263 }
 264 
 265 static const struct platform_device_id syscon_ids[] = {
 266         { "syscon", },
 267         { }
 268 };
 269 
 270 static struct platform_driver syscon_driver = {
 271         .driver = {
 272                 .name = "syscon",
 273         },
 274         .probe          = syscon_probe,
 275         .id_table       = syscon_ids,
 276 };
 277 
 278 static int __init syscon_init(void)
 279 {
 280         return platform_driver_register(&syscon_driver);
 281 }
 282 postcore_initcall(syscon_init);

/* [<][>][^][v][top][bottom][index][help] */