11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 23b9334acSPawel Moll /* 33b9334acSPawel Moll * 43b9334acSPawel Moll * Copyright (C) 2014 ARM Limited 53b9334acSPawel Moll */ 63b9334acSPawel Moll 73b9334acSPawel Moll #include <linux/err.h> 83b9334acSPawel Moll #include <linux/init.h> 9d06cfe3fSRob Herring #include <linux/io.h> 103b9334acSPawel Moll #include <linux/of.h> 11d06cfe3fSRob Herring #include <linux/platform_device.h> 123b9334acSPawel Moll #include <linux/of_device.h> 13d06cfe3fSRob Herring #include <linux/sched/signal.h> 14d06cfe3fSRob Herring #include <linux/slab.h> 153b9334acSPawel Moll #include <linux/vexpress.h> 163b9334acSPawel Moll 17d06cfe3fSRob Herring #define SYS_CFGDATA 0x0 18d06cfe3fSRob Herring 19d06cfe3fSRob Herring #define SYS_CFGCTRL 0x4 20d06cfe3fSRob Herring #define SYS_CFGCTRL_START (1 << 31) 21d06cfe3fSRob Herring #define SYS_CFGCTRL_WRITE (1 << 30) 22d06cfe3fSRob Herring #define SYS_CFGCTRL_DCC(n) (((n) & 0xf) << 26) 23d06cfe3fSRob Herring #define SYS_CFGCTRL_FUNC(n) (((n) & 0x3f) << 20) 24d06cfe3fSRob Herring #define SYS_CFGCTRL_SITE(n) (((n) & 0x3) << 16) 25d06cfe3fSRob Herring #define SYS_CFGCTRL_POSITION(n) (((n) & 0xf) << 12) 26d06cfe3fSRob Herring #define SYS_CFGCTRL_DEVICE(n) (((n) & 0xfff) << 0) 27d06cfe3fSRob Herring 28d06cfe3fSRob Herring #define SYS_CFGSTAT 0x8 29d06cfe3fSRob Herring #define SYS_CFGSTAT_ERR (1 << 1) 30d06cfe3fSRob Herring #define SYS_CFGSTAT_COMPLETE (1 << 0) 31d06cfe3fSRob Herring 32d06cfe3fSRob Herring 33d06cfe3fSRob Herring struct vexpress_syscfg { 34d06cfe3fSRob Herring struct device *dev; 35d06cfe3fSRob Herring void __iomem *base; 36d06cfe3fSRob Herring struct list_head funcs; 37d06cfe3fSRob Herring }; 38d06cfe3fSRob Herring 39d06cfe3fSRob Herring struct vexpress_syscfg_func { 40d06cfe3fSRob Herring struct list_head list; 41d06cfe3fSRob Herring struct vexpress_syscfg *syscfg; 42d06cfe3fSRob Herring struct regmap *regmap; 43d06cfe3fSRob Herring int num_templates; 44d06cfe3fSRob Herring u32 template[]; /* Keep it last! */ 45d06cfe3fSRob Herring }; 46d06cfe3fSRob Herring 47d06cfe3fSRob Herring struct vexpress_config_bridge_ops { 48d06cfe3fSRob Herring struct regmap * (*regmap_init)(struct device *dev, void *context); 49d06cfe3fSRob Herring void (*regmap_exit)(struct regmap *regmap, void *context); 50d06cfe3fSRob Herring }; 513b9334acSPawel Moll 523b9334acSPawel Moll struct vexpress_config_bridge { 533b9334acSPawel Moll struct vexpress_config_bridge_ops *ops; 543b9334acSPawel Moll void *context; 553b9334acSPawel Moll }; 563b9334acSPawel Moll 573b9334acSPawel Moll 583b9334acSPawel Moll static DEFINE_MUTEX(vexpress_config_mutex); 593b9334acSPawel Moll static struct class *vexpress_config_class; 603b9334acSPawel Moll static u32 vexpress_config_site_master = VEXPRESS_SITE_MASTER; 613b9334acSPawel Moll 623b9334acSPawel Moll 633b9334acSPawel Moll void vexpress_config_set_master(u32 site) 643b9334acSPawel Moll { 653b9334acSPawel Moll vexpress_config_site_master = site; 663b9334acSPawel Moll } 673b9334acSPawel Moll 68d06cfe3fSRob Herring static void vexpress_config_lock(void *arg) 693b9334acSPawel Moll { 703b9334acSPawel Moll mutex_lock(&vexpress_config_mutex); 713b9334acSPawel Moll } 723b9334acSPawel Moll 73d06cfe3fSRob Herring static void vexpress_config_unlock(void *arg) 743b9334acSPawel Moll { 753b9334acSPawel Moll mutex_unlock(&vexpress_config_mutex); 763b9334acSPawel Moll } 773b9334acSPawel Moll 783b9334acSPawel Moll 793b9334acSPawel Moll static void vexpress_config_find_prop(struct device_node *node, 803b9334acSPawel Moll const char *name, u32 *val) 813b9334acSPawel Moll { 823b9334acSPawel Moll /* Default value */ 833b9334acSPawel Moll *val = 0; 843b9334acSPawel Moll 853b9334acSPawel Moll of_node_get(node); 863b9334acSPawel Moll while (node) { 873b9334acSPawel Moll if (of_property_read_u32(node, name, val) == 0) { 883b9334acSPawel Moll of_node_put(node); 893b9334acSPawel Moll return; 903b9334acSPawel Moll } 913b9334acSPawel Moll node = of_get_next_parent(node); 923b9334acSPawel Moll } 933b9334acSPawel Moll } 943b9334acSPawel Moll 95d06cfe3fSRob Herring static int vexpress_config_get_topo(struct device_node *node, u32 *site, 963b9334acSPawel Moll u32 *position, u32 *dcc) 973b9334acSPawel Moll { 983b9334acSPawel Moll vexpress_config_find_prop(node, "arm,vexpress,site", site); 993b9334acSPawel Moll if (*site == VEXPRESS_SITE_MASTER) 1003b9334acSPawel Moll *site = vexpress_config_site_master; 1013b9334acSPawel Moll if (WARN_ON(vexpress_config_site_master == VEXPRESS_SITE_MASTER)) 1023b9334acSPawel Moll return -EINVAL; 1033b9334acSPawel Moll vexpress_config_find_prop(node, "arm,vexpress,position", position); 1043b9334acSPawel Moll vexpress_config_find_prop(node, "arm,vexpress,dcc", dcc); 1053b9334acSPawel Moll 1063b9334acSPawel Moll return 0; 1073b9334acSPawel Moll } 1083b9334acSPawel Moll 1093b9334acSPawel Moll 1103b9334acSPawel Moll static void vexpress_config_devres_release(struct device *dev, void *res) 1113b9334acSPawel Moll { 1123b9334acSPawel Moll struct vexpress_config_bridge *bridge = dev_get_drvdata(dev->parent); 1133b9334acSPawel Moll struct regmap *regmap = res; 1143b9334acSPawel Moll 1153b9334acSPawel Moll bridge->ops->regmap_exit(regmap, bridge->context); 1163b9334acSPawel Moll } 1173b9334acSPawel Moll 1183b9334acSPawel Moll struct regmap *devm_regmap_init_vexpress_config(struct device *dev) 1193b9334acSPawel Moll { 1203b9334acSPawel Moll struct vexpress_config_bridge *bridge; 1213b9334acSPawel Moll struct regmap *regmap; 1223b9334acSPawel Moll struct regmap **res; 1233b9334acSPawel Moll 1243b9334acSPawel Moll if (WARN_ON(dev->parent->class != vexpress_config_class)) 1253b9334acSPawel Moll return ERR_PTR(-ENODEV); 1263b9334acSPawel Moll 1273b9334acSPawel Moll bridge = dev_get_drvdata(dev->parent); 1283b9334acSPawel Moll if (WARN_ON(!bridge)) 1293b9334acSPawel Moll return ERR_PTR(-EINVAL); 1303b9334acSPawel Moll 1313b9334acSPawel Moll res = devres_alloc(vexpress_config_devres_release, sizeof(*res), 1323b9334acSPawel Moll GFP_KERNEL); 1333b9334acSPawel Moll if (!res) 1343b9334acSPawel Moll return ERR_PTR(-ENOMEM); 1353b9334acSPawel Moll 136bbb4d872SNicolas Boichat regmap = (bridge->ops->regmap_init)(dev, bridge->context); 1373b9334acSPawel Moll if (IS_ERR(regmap)) { 1383b9334acSPawel Moll devres_free(res); 1393b9334acSPawel Moll return regmap; 1403b9334acSPawel Moll } 1413b9334acSPawel Moll 1423b9334acSPawel Moll *res = regmap; 1433b9334acSPawel Moll devres_add(dev, res); 1443b9334acSPawel Moll 1453b9334acSPawel Moll return regmap; 1463b9334acSPawel Moll } 147b33cdd28SArnd Bergmann EXPORT_SYMBOL_GPL(devm_regmap_init_vexpress_config); 1483b9334acSPawel Moll 149d06cfe3fSRob Herring static struct device *vexpress_config_bridge_register(struct device *parent, 1503b9334acSPawel Moll struct vexpress_config_bridge_ops *ops, void *context) 1513b9334acSPawel Moll { 1523b9334acSPawel Moll struct device *dev; 1533b9334acSPawel Moll struct vexpress_config_bridge *bridge; 1543b9334acSPawel Moll 1553b9334acSPawel Moll if (!vexpress_config_class) { 1563b9334acSPawel Moll vexpress_config_class = class_create(THIS_MODULE, 1573b9334acSPawel Moll "vexpress-config"); 1583b9334acSPawel Moll if (IS_ERR(vexpress_config_class)) 1593b9334acSPawel Moll return (void *)vexpress_config_class; 1603b9334acSPawel Moll } 1613b9334acSPawel Moll 1623b9334acSPawel Moll dev = device_create(vexpress_config_class, parent, 0, 1633b9334acSPawel Moll NULL, "%s.bridge", dev_name(parent)); 1643b9334acSPawel Moll 1653b9334acSPawel Moll if (IS_ERR(dev)) 1663b9334acSPawel Moll return dev; 1673b9334acSPawel Moll 1683b9334acSPawel Moll bridge = devm_kmalloc(dev, sizeof(*bridge), GFP_KERNEL); 1693b9334acSPawel Moll if (!bridge) { 1703b9334acSPawel Moll put_device(dev); 1713b9334acSPawel Moll device_unregister(dev); 1723b9334acSPawel Moll return ERR_PTR(-ENOMEM); 1733b9334acSPawel Moll } 1743b9334acSPawel Moll bridge->ops = ops; 1753b9334acSPawel Moll bridge->context = context; 1763b9334acSPawel Moll 1773b9334acSPawel Moll dev_set_drvdata(dev, bridge); 1783b9334acSPawel Moll 1793b9334acSPawel Moll dev_dbg(parent, "Registered bridge '%s', parent node %p\n", 1803b9334acSPawel Moll dev_name(dev), parent->of_node); 1813b9334acSPawel Moll 1823b9334acSPawel Moll return dev; 1833b9334acSPawel Moll } 1843b9334acSPawel Moll 1853b9334acSPawel Moll 1863b9334acSPawel Moll static int vexpress_config_node_match(struct device *dev, const void *data) 1873b9334acSPawel Moll { 1883b9334acSPawel Moll const struct device_node *node = data; 1893b9334acSPawel Moll 1903b9334acSPawel Moll dev_dbg(dev, "Parent node %p, looking for %p\n", 1913b9334acSPawel Moll dev->parent->of_node, node); 1923b9334acSPawel Moll 1933b9334acSPawel Moll return dev->parent->of_node == node; 1943b9334acSPawel Moll } 1953b9334acSPawel Moll 1963b9334acSPawel Moll static int vexpress_config_populate(struct device_node *node) 1973b9334acSPawel Moll { 1983b9334acSPawel Moll struct device_node *bridge; 1993b9334acSPawel Moll struct device *parent; 200c090959bSJohan Hovold int ret; 2013b9334acSPawel Moll 2023b9334acSPawel Moll bridge = of_parse_phandle(node, "arm,vexpress,config-bridge", 0); 2033b9334acSPawel Moll if (!bridge) 2043b9334acSPawel Moll return -EINVAL; 2053b9334acSPawel Moll 2063b9334acSPawel Moll parent = class_find_device(vexpress_config_class, NULL, bridge, 2073b9334acSPawel Moll vexpress_config_node_match); 208557e37c0SPeter Chen of_node_put(bridge); 2093b9334acSPawel Moll if (WARN_ON(!parent)) 2103b9334acSPawel Moll return -ENODEV; 2113b9334acSPawel Moll 212c090959bSJohan Hovold ret = of_platform_populate(node, NULL, NULL, parent); 213c090959bSJohan Hovold 214c090959bSJohan Hovold put_device(parent); 215c090959bSJohan Hovold 216c090959bSJohan Hovold return ret; 2173b9334acSPawel Moll } 2183b9334acSPawel Moll 2193b9334acSPawel Moll static int __init vexpress_config_init(void) 2203b9334acSPawel Moll { 2213b9334acSPawel Moll int err = 0; 2223b9334acSPawel Moll struct device_node *node; 2233b9334acSPawel Moll 2243b9334acSPawel Moll /* Need the config devices early, before the "normal" devices... */ 2253b9334acSPawel Moll for_each_compatible_node(node, NULL, "arm,vexpress,config-bus") { 2263b9334acSPawel Moll err = vexpress_config_populate(node); 227d99875eeSAmitoj Kaur Chawla if (err) { 228d99875eeSAmitoj Kaur Chawla of_node_put(node); 2293b9334acSPawel Moll break; 2303b9334acSPawel Moll } 231d99875eeSAmitoj Kaur Chawla } 2323b9334acSPawel Moll 2333b9334acSPawel Moll return err; 2343b9334acSPawel Moll } 2353b9334acSPawel Moll postcore_initcall(vexpress_config_init); 2363b9334acSPawel Moll 237d06cfe3fSRob Herring static int vexpress_syscfg_exec(struct vexpress_syscfg_func *func, 238d06cfe3fSRob Herring int index, bool write, u32 *data) 239d06cfe3fSRob Herring { 240d06cfe3fSRob Herring struct vexpress_syscfg *syscfg = func->syscfg; 241d06cfe3fSRob Herring u32 command, status; 242d06cfe3fSRob Herring int tries; 243d06cfe3fSRob Herring long timeout; 244d06cfe3fSRob Herring 245d06cfe3fSRob Herring if (WARN_ON(index >= func->num_templates)) 246d06cfe3fSRob Herring return -EINVAL; 247d06cfe3fSRob Herring 248d06cfe3fSRob Herring command = readl(syscfg->base + SYS_CFGCTRL); 249d06cfe3fSRob Herring if (WARN_ON(command & SYS_CFGCTRL_START)) 250d06cfe3fSRob Herring return -EBUSY; 251d06cfe3fSRob Herring 252d06cfe3fSRob Herring command = func->template[index]; 253d06cfe3fSRob Herring command |= SYS_CFGCTRL_START; 254d06cfe3fSRob Herring command |= write ? SYS_CFGCTRL_WRITE : 0; 255d06cfe3fSRob Herring 256d06cfe3fSRob Herring /* Use a canary for reads */ 257d06cfe3fSRob Herring if (!write) 258d06cfe3fSRob Herring *data = 0xdeadbeef; 259d06cfe3fSRob Herring 260d06cfe3fSRob Herring dev_dbg(syscfg->dev, "func %p, command %x, data %x\n", 261d06cfe3fSRob Herring func, command, *data); 262d06cfe3fSRob Herring writel(*data, syscfg->base + SYS_CFGDATA); 263d06cfe3fSRob Herring writel(0, syscfg->base + SYS_CFGSTAT); 264d06cfe3fSRob Herring writel(command, syscfg->base + SYS_CFGCTRL); 265d06cfe3fSRob Herring mb(); 266d06cfe3fSRob Herring 267d06cfe3fSRob Herring /* The operation can take ages... Go to sleep, 100us initially */ 268d06cfe3fSRob Herring tries = 100; 269d06cfe3fSRob Herring timeout = 100; 270d06cfe3fSRob Herring do { 271d06cfe3fSRob Herring if (!irqs_disabled()) { 272d06cfe3fSRob Herring set_current_state(TASK_INTERRUPTIBLE); 273d06cfe3fSRob Herring schedule_timeout(usecs_to_jiffies(timeout)); 274d06cfe3fSRob Herring if (signal_pending(current)) 275d06cfe3fSRob Herring return -EINTR; 276d06cfe3fSRob Herring } else { 277d06cfe3fSRob Herring udelay(timeout); 278d06cfe3fSRob Herring } 279d06cfe3fSRob Herring 280d06cfe3fSRob Herring status = readl(syscfg->base + SYS_CFGSTAT); 281d06cfe3fSRob Herring if (status & SYS_CFGSTAT_ERR) 282d06cfe3fSRob Herring return -EFAULT; 283d06cfe3fSRob Herring 284d06cfe3fSRob Herring if (timeout > 20) 285d06cfe3fSRob Herring timeout -= 20; 286d06cfe3fSRob Herring } while (--tries && !(status & SYS_CFGSTAT_COMPLETE)); 287d06cfe3fSRob Herring if (WARN_ON_ONCE(!tries)) 288d06cfe3fSRob Herring return -ETIMEDOUT; 289d06cfe3fSRob Herring 290d06cfe3fSRob Herring if (!write) { 291d06cfe3fSRob Herring *data = readl(syscfg->base + SYS_CFGDATA); 292d06cfe3fSRob Herring dev_dbg(syscfg->dev, "func %p, read data %x\n", func, *data); 293d06cfe3fSRob Herring } 294d06cfe3fSRob Herring 295d06cfe3fSRob Herring return 0; 296d06cfe3fSRob Herring } 297d06cfe3fSRob Herring 298d06cfe3fSRob Herring static int vexpress_syscfg_read(void *context, unsigned int index, 299d06cfe3fSRob Herring unsigned int *val) 300d06cfe3fSRob Herring { 301d06cfe3fSRob Herring struct vexpress_syscfg_func *func = context; 302d06cfe3fSRob Herring 303d06cfe3fSRob Herring return vexpress_syscfg_exec(func, index, false, val); 304d06cfe3fSRob Herring } 305d06cfe3fSRob Herring 306d06cfe3fSRob Herring static int vexpress_syscfg_write(void *context, unsigned int index, 307d06cfe3fSRob Herring unsigned int val) 308d06cfe3fSRob Herring { 309d06cfe3fSRob Herring struct vexpress_syscfg_func *func = context; 310d06cfe3fSRob Herring 311d06cfe3fSRob Herring return vexpress_syscfg_exec(func, index, true, &val); 312d06cfe3fSRob Herring } 313d06cfe3fSRob Herring 314d06cfe3fSRob Herring static struct regmap_config vexpress_syscfg_regmap_config = { 315d06cfe3fSRob Herring .lock = vexpress_config_lock, 316d06cfe3fSRob Herring .unlock = vexpress_config_unlock, 317d06cfe3fSRob Herring .reg_bits = 32, 318d06cfe3fSRob Herring .val_bits = 32, 319d06cfe3fSRob Herring .reg_read = vexpress_syscfg_read, 320d06cfe3fSRob Herring .reg_write = vexpress_syscfg_write, 321d06cfe3fSRob Herring .reg_format_endian = REGMAP_ENDIAN_LITTLE, 322d06cfe3fSRob Herring .val_format_endian = REGMAP_ENDIAN_LITTLE, 323d06cfe3fSRob Herring }; 324d06cfe3fSRob Herring 325d06cfe3fSRob Herring 326d06cfe3fSRob Herring static struct regmap *vexpress_syscfg_regmap_init(struct device *dev, 327d06cfe3fSRob Herring void *context) 328d06cfe3fSRob Herring { 329d06cfe3fSRob Herring int err; 330d06cfe3fSRob Herring struct vexpress_syscfg *syscfg = context; 331d06cfe3fSRob Herring struct vexpress_syscfg_func *func; 332d06cfe3fSRob Herring struct property *prop; 333d06cfe3fSRob Herring const __be32 *val = NULL; 334d06cfe3fSRob Herring __be32 energy_quirk[4]; 335d06cfe3fSRob Herring int num; 336d06cfe3fSRob Herring u32 site, position, dcc; 337d06cfe3fSRob Herring int i; 338d06cfe3fSRob Herring 339d06cfe3fSRob Herring err = vexpress_config_get_topo(dev->of_node, &site, 340d06cfe3fSRob Herring &position, &dcc); 341d06cfe3fSRob Herring if (err) 342d06cfe3fSRob Herring return ERR_PTR(err); 343d06cfe3fSRob Herring 344d06cfe3fSRob Herring prop = of_find_property(dev->of_node, 345d06cfe3fSRob Herring "arm,vexpress-sysreg,func", NULL); 346d06cfe3fSRob Herring if (!prop) 347d06cfe3fSRob Herring return ERR_PTR(-EINVAL); 348d06cfe3fSRob Herring 349d06cfe3fSRob Herring num = prop->length / sizeof(u32) / 2; 350d06cfe3fSRob Herring val = prop->value; 351d06cfe3fSRob Herring 352d06cfe3fSRob Herring /* 353d06cfe3fSRob Herring * "arm,vexpress-energy" function used to be described 354d06cfe3fSRob Herring * by its first device only, now it requires both 355d06cfe3fSRob Herring */ 356d06cfe3fSRob Herring if (num == 1 && of_device_is_compatible(dev->of_node, 357d06cfe3fSRob Herring "arm,vexpress-energy")) { 358d06cfe3fSRob Herring num = 2; 359d06cfe3fSRob Herring energy_quirk[0] = *val; 360d06cfe3fSRob Herring energy_quirk[2] = *val++; 361d06cfe3fSRob Herring energy_quirk[1] = *val; 362d06cfe3fSRob Herring energy_quirk[3] = cpu_to_be32(be32_to_cpup(val) + 1); 363d06cfe3fSRob Herring val = energy_quirk; 364d06cfe3fSRob Herring } 365d06cfe3fSRob Herring 366d06cfe3fSRob Herring func = kzalloc(struct_size(func, template, num), GFP_KERNEL); 367d06cfe3fSRob Herring if (!func) 368d06cfe3fSRob Herring return ERR_PTR(-ENOMEM); 369d06cfe3fSRob Herring 370d06cfe3fSRob Herring func->syscfg = syscfg; 371d06cfe3fSRob Herring func->num_templates = num; 372d06cfe3fSRob Herring 373d06cfe3fSRob Herring for (i = 0; i < num; i++) { 374d06cfe3fSRob Herring u32 function, device; 375d06cfe3fSRob Herring 376d06cfe3fSRob Herring function = be32_to_cpup(val++); 377d06cfe3fSRob Herring device = be32_to_cpup(val++); 378d06cfe3fSRob Herring 379d06cfe3fSRob Herring dev_dbg(dev, "func %p: %u/%u/%u/%u/%u\n", 380d06cfe3fSRob Herring func, site, position, dcc, 381d06cfe3fSRob Herring function, device); 382d06cfe3fSRob Herring 383d06cfe3fSRob Herring func->template[i] = SYS_CFGCTRL_DCC(dcc); 384d06cfe3fSRob Herring func->template[i] |= SYS_CFGCTRL_SITE(site); 385d06cfe3fSRob Herring func->template[i] |= SYS_CFGCTRL_POSITION(position); 386d06cfe3fSRob Herring func->template[i] |= SYS_CFGCTRL_FUNC(function); 387d06cfe3fSRob Herring func->template[i] |= SYS_CFGCTRL_DEVICE(device); 388d06cfe3fSRob Herring } 389d06cfe3fSRob Herring 390d06cfe3fSRob Herring vexpress_syscfg_regmap_config.max_register = num - 1; 391d06cfe3fSRob Herring 392d06cfe3fSRob Herring func->regmap = regmap_init(dev, NULL, func, 393d06cfe3fSRob Herring &vexpress_syscfg_regmap_config); 394d06cfe3fSRob Herring 395d06cfe3fSRob Herring if (IS_ERR(func->regmap)) { 396d06cfe3fSRob Herring void *err = func->regmap; 397d06cfe3fSRob Herring 398d06cfe3fSRob Herring kfree(func); 399d06cfe3fSRob Herring return err; 400d06cfe3fSRob Herring } 401d06cfe3fSRob Herring 402d06cfe3fSRob Herring list_add(&func->list, &syscfg->funcs); 403d06cfe3fSRob Herring 404d06cfe3fSRob Herring return func->regmap; 405d06cfe3fSRob Herring } 406d06cfe3fSRob Herring 407d06cfe3fSRob Herring static void vexpress_syscfg_regmap_exit(struct regmap *regmap, void *context) 408d06cfe3fSRob Herring { 409d06cfe3fSRob Herring struct vexpress_syscfg *syscfg = context; 410d06cfe3fSRob Herring struct vexpress_syscfg_func *func, *tmp; 411d06cfe3fSRob Herring 412d06cfe3fSRob Herring regmap_exit(regmap); 413d06cfe3fSRob Herring 414d06cfe3fSRob Herring list_for_each_entry_safe(func, tmp, &syscfg->funcs, list) { 415d06cfe3fSRob Herring if (func->regmap == regmap) { 416d06cfe3fSRob Herring list_del(&syscfg->funcs); 417d06cfe3fSRob Herring kfree(func); 418d06cfe3fSRob Herring break; 419d06cfe3fSRob Herring } 420d06cfe3fSRob Herring } 421d06cfe3fSRob Herring } 422d06cfe3fSRob Herring 423d06cfe3fSRob Herring static struct vexpress_config_bridge_ops vexpress_syscfg_bridge_ops = { 424d06cfe3fSRob Herring .regmap_init = vexpress_syscfg_regmap_init, 425d06cfe3fSRob Herring .regmap_exit = vexpress_syscfg_regmap_exit, 426d06cfe3fSRob Herring }; 427d06cfe3fSRob Herring 428d06cfe3fSRob Herring 429d06cfe3fSRob Herring static int vexpress_syscfg_probe(struct platform_device *pdev) 430d06cfe3fSRob Herring { 431d06cfe3fSRob Herring struct vexpress_syscfg *syscfg; 432d06cfe3fSRob Herring struct resource *res; 433d06cfe3fSRob Herring struct device *bridge; 434d06cfe3fSRob Herring 435d06cfe3fSRob Herring syscfg = devm_kzalloc(&pdev->dev, sizeof(*syscfg), GFP_KERNEL); 436d06cfe3fSRob Herring if (!syscfg) 437d06cfe3fSRob Herring return -ENOMEM; 438d06cfe3fSRob Herring syscfg->dev = &pdev->dev; 439d06cfe3fSRob Herring INIT_LIST_HEAD(&syscfg->funcs); 440d06cfe3fSRob Herring 441d06cfe3fSRob Herring res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 442d06cfe3fSRob Herring syscfg->base = devm_ioremap_resource(&pdev->dev, res); 443d06cfe3fSRob Herring if (IS_ERR(syscfg->base)) 444d06cfe3fSRob Herring return PTR_ERR(syscfg->base); 445d06cfe3fSRob Herring 446d06cfe3fSRob Herring /* Must use dev.parent (MFD), as that's where DT phandle points at... */ 447d06cfe3fSRob Herring bridge = vexpress_config_bridge_register(pdev->dev.parent, 448d06cfe3fSRob Herring &vexpress_syscfg_bridge_ops, syscfg); 449d06cfe3fSRob Herring 450d06cfe3fSRob Herring return PTR_ERR_OR_ZERO(bridge); 451d06cfe3fSRob Herring } 452d06cfe3fSRob Herring 453d06cfe3fSRob Herring static const struct platform_device_id vexpress_syscfg_id_table[] = { 454d06cfe3fSRob Herring { "vexpress-syscfg", }, 455d06cfe3fSRob Herring {}, 456d06cfe3fSRob Herring }; 457d06cfe3fSRob Herring 458d06cfe3fSRob Herring static struct platform_driver vexpress_syscfg_driver = { 459d06cfe3fSRob Herring .driver.name = "vexpress-syscfg", 460d06cfe3fSRob Herring .id_table = vexpress_syscfg_id_table, 461d06cfe3fSRob Herring .probe = vexpress_syscfg_probe, 462d06cfe3fSRob Herring }; 463d06cfe3fSRob Herring 464d06cfe3fSRob Herring static int __init vexpress_syscfg_init(void) 465d06cfe3fSRob Herring { 466d06cfe3fSRob Herring return platform_driver_register(&vexpress_syscfg_driver); 467d06cfe3fSRob Herring } 468d06cfe3fSRob Herring core_initcall(vexpress_syscfg_init); 469