root/drivers/misc/vexpress-syscfg.c

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

DEFINITIONS

This source file includes following definitions.
  1. vexpress_syscfg_exec
  2. vexpress_syscfg_read
  3. vexpress_syscfg_write
  4. vexpress_syscfg_regmap_init
  5. vexpress_syscfg_regmap_exit
  6. vexpress_syscfg_probe
  7. vexpress_syscfg_init

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  *
   4  * Copyright (C) 2014 ARM Limited
   5  */
   6 
   7 #include <linux/delay.h>
   8 #include <linux/err.h>
   9 #include <linux/io.h>
  10 #include <linux/of.h>
  11 #include <linux/platform_device.h>
  12 #include <linux/sched/signal.h>
  13 #include <linux/slab.h>
  14 #include <linux/syscore_ops.h>
  15 #include <linux/vexpress.h>
  16 
  17 
  18 #define SYS_CFGDATA             0x0
  19 
  20 #define SYS_CFGCTRL             0x4
  21 #define SYS_CFGCTRL_START       (1 << 31)
  22 #define SYS_CFGCTRL_WRITE       (1 << 30)
  23 #define SYS_CFGCTRL_DCC(n)      (((n) & 0xf) << 26)
  24 #define SYS_CFGCTRL_FUNC(n)     (((n) & 0x3f) << 20)
  25 #define SYS_CFGCTRL_SITE(n)     (((n) & 0x3) << 16)
  26 #define SYS_CFGCTRL_POSITION(n) (((n) & 0xf) << 12)
  27 #define SYS_CFGCTRL_DEVICE(n)   (((n) & 0xfff) << 0)
  28 
  29 #define SYS_CFGSTAT             0x8
  30 #define SYS_CFGSTAT_ERR         (1 << 1)
  31 #define SYS_CFGSTAT_COMPLETE    (1 << 0)
  32 
  33 
  34 struct vexpress_syscfg {
  35         struct device *dev;
  36         void __iomem *base;
  37         struct list_head funcs;
  38 };
  39 
  40 struct vexpress_syscfg_func {
  41         struct list_head list;
  42         struct vexpress_syscfg *syscfg;
  43         struct regmap *regmap;
  44         int num_templates;
  45         u32 template[0]; /* Keep it last! */
  46 };
  47 
  48 
  49 static int vexpress_syscfg_exec(struct vexpress_syscfg_func *func,
  50                 int index, bool write, u32 *data)
  51 {
  52         struct vexpress_syscfg *syscfg = func->syscfg;
  53         u32 command, status;
  54         int tries;
  55         long timeout;
  56 
  57         if (WARN_ON(index >= func->num_templates))
  58                 return -EINVAL;
  59 
  60         command = readl(syscfg->base + SYS_CFGCTRL);
  61         if (WARN_ON(command & SYS_CFGCTRL_START))
  62                 return -EBUSY;
  63 
  64         command = func->template[index];
  65         command |= SYS_CFGCTRL_START;
  66         command |= write ? SYS_CFGCTRL_WRITE : 0;
  67 
  68         /* Use a canary for reads */
  69         if (!write)
  70                 *data = 0xdeadbeef;
  71 
  72         dev_dbg(syscfg->dev, "func %p, command %x, data %x\n",
  73                         func, command, *data);
  74         writel(*data, syscfg->base + SYS_CFGDATA);
  75         writel(0, syscfg->base + SYS_CFGSTAT);
  76         writel(command, syscfg->base + SYS_CFGCTRL);
  77         mb();
  78 
  79         /* The operation can take ages... Go to sleep, 100us initially */
  80         tries = 100;
  81         timeout = 100;
  82         do {
  83                 if (!irqs_disabled()) {
  84                         set_current_state(TASK_INTERRUPTIBLE);
  85                         schedule_timeout(usecs_to_jiffies(timeout));
  86                         if (signal_pending(current))
  87                                 return -EINTR;
  88                 } else {
  89                         udelay(timeout);
  90                 }
  91 
  92                 status = readl(syscfg->base + SYS_CFGSTAT);
  93                 if (status & SYS_CFGSTAT_ERR)
  94                         return -EFAULT;
  95 
  96                 if (timeout > 20)
  97                         timeout -= 20;
  98         } while (--tries && !(status & SYS_CFGSTAT_COMPLETE));
  99         if (WARN_ON_ONCE(!tries))
 100                 return -ETIMEDOUT;
 101 
 102         if (!write) {
 103                 *data = readl(syscfg->base + SYS_CFGDATA);
 104                 dev_dbg(syscfg->dev, "func %p, read data %x\n", func, *data);
 105         }
 106 
 107         return 0;
 108 }
 109 
 110 static int vexpress_syscfg_read(void *context, unsigned int index,
 111                 unsigned int *val)
 112 {
 113         struct vexpress_syscfg_func *func = context;
 114 
 115         return vexpress_syscfg_exec(func, index, false, val);
 116 }
 117 
 118 static int vexpress_syscfg_write(void *context, unsigned int index,
 119                 unsigned int val)
 120 {
 121         struct vexpress_syscfg_func *func = context;
 122 
 123         return vexpress_syscfg_exec(func, index, true, &val);
 124 }
 125 
 126 static struct regmap_config vexpress_syscfg_regmap_config = {
 127         .lock = vexpress_config_lock,
 128         .unlock = vexpress_config_unlock,
 129         .reg_bits = 32,
 130         .val_bits = 32,
 131         .reg_read = vexpress_syscfg_read,
 132         .reg_write = vexpress_syscfg_write,
 133         .reg_format_endian = REGMAP_ENDIAN_LITTLE,
 134         .val_format_endian = REGMAP_ENDIAN_LITTLE,
 135 };
 136 
 137 
 138 static struct regmap *vexpress_syscfg_regmap_init(struct device *dev,
 139                 void *context)
 140 {
 141         int err;
 142         struct vexpress_syscfg *syscfg = context;
 143         struct vexpress_syscfg_func *func;
 144         struct property *prop;
 145         const __be32 *val = NULL;
 146         __be32 energy_quirk[4];
 147         int num;
 148         u32 site, position, dcc;
 149         int i;
 150 
 151         err = vexpress_config_get_topo(dev->of_node, &site,
 152                                 &position, &dcc);
 153         if (err)
 154                 return ERR_PTR(err);
 155 
 156         prop = of_find_property(dev->of_node,
 157                         "arm,vexpress-sysreg,func", NULL);
 158         if (!prop)
 159                 return ERR_PTR(-EINVAL);
 160 
 161         num = prop->length / sizeof(u32) / 2;
 162         val = prop->value;
 163 
 164         /*
 165          * "arm,vexpress-energy" function used to be described
 166          * by its first device only, now it requires both
 167          */
 168         if (num == 1 && of_device_is_compatible(dev->of_node,
 169                         "arm,vexpress-energy")) {
 170                 num = 2;
 171                 energy_quirk[0] = *val;
 172                 energy_quirk[2] = *val++;
 173                 energy_quirk[1] = *val;
 174                 energy_quirk[3] = cpu_to_be32(be32_to_cpup(val) + 1);
 175                 val = energy_quirk;
 176         }
 177 
 178         func = kzalloc(struct_size(func, template, num), GFP_KERNEL);
 179         if (!func)
 180                 return ERR_PTR(-ENOMEM);
 181 
 182         func->syscfg = syscfg;
 183         func->num_templates = num;
 184 
 185         for (i = 0; i < num; i++) {
 186                 u32 function, device;
 187 
 188                 function = be32_to_cpup(val++);
 189                 device = be32_to_cpup(val++);
 190 
 191                 dev_dbg(dev, "func %p: %u/%u/%u/%u/%u\n",
 192                                 func, site, position, dcc,
 193                                 function, device);
 194 
 195                 func->template[i] = SYS_CFGCTRL_DCC(dcc);
 196                 func->template[i] |= SYS_CFGCTRL_SITE(site);
 197                 func->template[i] |= SYS_CFGCTRL_POSITION(position);
 198                 func->template[i] |= SYS_CFGCTRL_FUNC(function);
 199                 func->template[i] |= SYS_CFGCTRL_DEVICE(device);
 200         }
 201 
 202         vexpress_syscfg_regmap_config.max_register = num - 1;
 203 
 204         func->regmap = regmap_init(dev, NULL, func,
 205                         &vexpress_syscfg_regmap_config);
 206 
 207         if (IS_ERR(func->regmap)) {
 208                 void *err = func->regmap;
 209 
 210                 kfree(func);
 211                 return err;
 212         }
 213 
 214         list_add(&func->list, &syscfg->funcs);
 215 
 216         return func->regmap;
 217 }
 218 
 219 static void vexpress_syscfg_regmap_exit(struct regmap *regmap, void *context)
 220 {
 221         struct vexpress_syscfg *syscfg = context;
 222         struct vexpress_syscfg_func *func, *tmp;
 223 
 224         regmap_exit(regmap);
 225 
 226         list_for_each_entry_safe(func, tmp, &syscfg->funcs, list) {
 227                 if (func->regmap == regmap) {
 228                         list_del(&syscfg->funcs);
 229                         kfree(func);
 230                         break;
 231                 }
 232         }
 233 }
 234 
 235 static struct vexpress_config_bridge_ops vexpress_syscfg_bridge_ops = {
 236         .regmap_init = vexpress_syscfg_regmap_init,
 237         .regmap_exit = vexpress_syscfg_regmap_exit,
 238 };
 239 
 240 
 241 static int vexpress_syscfg_probe(struct platform_device *pdev)
 242 {
 243         struct vexpress_syscfg *syscfg;
 244         struct resource *res;
 245         struct device *bridge;
 246 
 247         syscfg = devm_kzalloc(&pdev->dev, sizeof(*syscfg), GFP_KERNEL);
 248         if (!syscfg)
 249                 return -ENOMEM;
 250         syscfg->dev = &pdev->dev;
 251         INIT_LIST_HEAD(&syscfg->funcs);
 252 
 253         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 254         syscfg->base = devm_ioremap_resource(&pdev->dev, res);
 255         if (IS_ERR(syscfg->base))
 256                 return PTR_ERR(syscfg->base);
 257 
 258         /* Must use dev.parent (MFD), as that's where DT phandle points at... */
 259         bridge = vexpress_config_bridge_register(pdev->dev.parent,
 260                         &vexpress_syscfg_bridge_ops, syscfg);
 261 
 262         return PTR_ERR_OR_ZERO(bridge);
 263 }
 264 
 265 static const struct platform_device_id vexpress_syscfg_id_table[] = {
 266         { "vexpress-syscfg", },
 267         {},
 268 };
 269 
 270 static struct platform_driver vexpress_syscfg_driver = {
 271         .driver.name = "vexpress-syscfg",
 272         .id_table = vexpress_syscfg_id_table,
 273         .probe = vexpress_syscfg_probe,
 274 };
 275 
 276 static int __init vexpress_syscfg_init(void)
 277 {
 278         return platform_driver_register(&vexpress_syscfg_driver);
 279 }
 280 core_initcall(vexpress_syscfg_init);

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