183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+ 257251285SSimon Glass /* 357251285SSimon Glass * Copyright (C) 2015 Google, Inc 457251285SSimon Glass * Written by Simon Glass <sjg@chromium.org> 557251285SSimon Glass */ 657251285SSimon Glass 757251285SSimon Glass #include <common.h> 857251285SSimon Glass #include <syscon.h> 957251285SSimon Glass #include <dm.h> 1057251285SSimon Glass #include <errno.h> 1157251285SSimon Glass #include <regmap.h> 1257251285SSimon Glass #include <dm/device-internal.h> 1357251285SSimon Glass #include <dm/lists.h> 1457251285SSimon Glass #include <dm/root.h> 1557251285SSimon Glass #include <linux/err.h> 1657251285SSimon Glass 17e151a1c2SMasahiro Yamada /* 18e151a1c2SMasahiro Yamada * Caution: 19e151a1c2SMasahiro Yamada * This API requires the given device has alerady been bound to syscon driver. 20e151a1c2SMasahiro Yamada * For example, 21e151a1c2SMasahiro Yamada * compatible = "syscon", "simple-mfd"; 22e151a1c2SMasahiro Yamada * works, but 23e151a1c2SMasahiro Yamada * compatible = "simple-mfd", "syscon"; 24e151a1c2SMasahiro Yamada * does not. The behavior is different from Linux. 25e151a1c2SMasahiro Yamada */ 2657251285SSimon Glass struct regmap *syscon_get_regmap(struct udevice *dev) 2757251285SSimon Glass { 289f4629beSSimon Glass struct syscon_uc_info *priv; 2957251285SSimon Glass 309f4629beSSimon Glass if (device_get_uclass_id(dev) != UCLASS_SYSCON) 319f4629beSSimon Glass return ERR_PTR(-ENOEXEC); 329f4629beSSimon Glass priv = dev_get_uclass_priv(dev); 3357251285SSimon Glass return priv->regmap; 3457251285SSimon Glass } 3557251285SSimon Glass 3657251285SSimon Glass static int syscon_pre_probe(struct udevice *dev) 3757251285SSimon Glass { 3857251285SSimon Glass struct syscon_uc_info *priv = dev_get_uclass_priv(dev); 3957251285SSimon Glass 40*529f57d9SSimon Glass /* Special case for PCI devices, which don't have a regmap */ 41*529f57d9SSimon Glass if (device_get_uclass_id(dev->parent) == UCLASS_PCI) 42*529f57d9SSimon Glass return 0; 43*529f57d9SSimon Glass 4404ecf36bSSimon Glass /* 4504ecf36bSSimon Glass * With OF_PLATDATA we really have no way of knowing the format of 4604ecf36bSSimon Glass * the device-specific platform data. So we assume that it starts with 4704ecf36bSSimon Glass * a 'reg' member, and this holds a single address and size. Drivers 4804ecf36bSSimon Glass * using OF_PLATDATA will need to ensure that this is true. 4904ecf36bSSimon Glass */ 5004ecf36bSSimon Glass #if CONFIG_IS_ENABLED(OF_PLATDATA) 5104ecf36bSSimon Glass struct syscon_base_platdata *plat = dev_get_platdata(dev); 5204ecf36bSSimon Glass 5304ecf36bSSimon Glass return regmap_init_mem_platdata(dev, plat->reg, ARRAY_SIZE(plat->reg), 5404ecf36bSSimon Glass &priv->regmap); 5504ecf36bSSimon Glass #else 56d3581236SMasahiro Yamada return regmap_init_mem(dev_ofnode(dev), &priv->regmap); 5704ecf36bSSimon Glass #endif 5857251285SSimon Glass } 5957251285SSimon Glass 606c3af1f2SJean-Jacques Hiblot struct regmap *syscon_regmap_lookup_by_phandle(struct udevice *dev, 616c3af1f2SJean-Jacques Hiblot const char *name) 626c3af1f2SJean-Jacques Hiblot { 636c3af1f2SJean-Jacques Hiblot struct udevice *syscon; 646c3af1f2SJean-Jacques Hiblot struct regmap *r; 656c3af1f2SJean-Jacques Hiblot int err; 666c3af1f2SJean-Jacques Hiblot 676c3af1f2SJean-Jacques Hiblot err = uclass_get_device_by_phandle(UCLASS_SYSCON, dev, 686c3af1f2SJean-Jacques Hiblot name, &syscon); 696c3af1f2SJean-Jacques Hiblot if (err) { 706c3af1f2SJean-Jacques Hiblot dev_dbg(dev, "unable to find syscon device\n"); 716c3af1f2SJean-Jacques Hiblot return ERR_PTR(err); 726c3af1f2SJean-Jacques Hiblot } 736c3af1f2SJean-Jacques Hiblot 746c3af1f2SJean-Jacques Hiblot r = syscon_get_regmap(syscon); 756c3af1f2SJean-Jacques Hiblot if (!r) { 766c3af1f2SJean-Jacques Hiblot dev_dbg(dev, "unable to find regmap\n"); 776c3af1f2SJean-Jacques Hiblot return ERR_PTR(-ENODEV); 786c3af1f2SJean-Jacques Hiblot } 796c3af1f2SJean-Jacques Hiblot 806c3af1f2SJean-Jacques Hiblot return r; 816c3af1f2SJean-Jacques Hiblot } 826c3af1f2SJean-Jacques Hiblot 83ac94b7bcSSimon Glass int syscon_get_by_driver_data(ulong driver_data, struct udevice **devp) 8457251285SSimon Glass { 8557251285SSimon Glass struct udevice *dev; 8657251285SSimon Glass struct uclass *uc; 8757251285SSimon Glass int ret; 8857251285SSimon Glass 89532f2435SSimon Glass *devp = NULL; 9057251285SSimon Glass ret = uclass_get(UCLASS_SYSCON, &uc); 9157251285SSimon Glass if (ret) 92ac94b7bcSSimon Glass return ret; 9357251285SSimon Glass uclass_foreach_dev(dev, uc) { 9457251285SSimon Glass if (dev->driver_data == driver_data) { 95ac94b7bcSSimon Glass *devp = dev; 96ac94b7bcSSimon Glass return device_probe(dev); 97ac94b7bcSSimon Glass } 98ac94b7bcSSimon Glass } 99ac94b7bcSSimon Glass 100ac94b7bcSSimon Glass return -ENODEV; 101ac94b7bcSSimon Glass } 102ac94b7bcSSimon Glass 103ac94b7bcSSimon Glass struct regmap *syscon_get_regmap_by_driver_data(ulong driver_data) 104ac94b7bcSSimon Glass { 10557251285SSimon Glass struct syscon_uc_info *priv; 106ac94b7bcSSimon Glass struct udevice *dev; 10757251285SSimon Glass int ret; 10857251285SSimon Glass 109ac94b7bcSSimon Glass ret = syscon_get_by_driver_data(driver_data, &dev); 11057251285SSimon Glass if (ret) 11157251285SSimon Glass return ERR_PTR(ret); 11257251285SSimon Glass priv = dev_get_uclass_priv(dev); 11357251285SSimon Glass 11457251285SSimon Glass return priv->regmap; 11557251285SSimon Glass } 11657251285SSimon Glass 11757251285SSimon Glass void *syscon_get_first_range(ulong driver_data) 11857251285SSimon Glass { 11957251285SSimon Glass struct regmap *map; 12057251285SSimon Glass 12157251285SSimon Glass map = syscon_get_regmap_by_driver_data(driver_data); 12257251285SSimon Glass if (IS_ERR(map)) 12357251285SSimon Glass return map; 12457251285SSimon Glass return regmap_get_range(map, 0); 12557251285SSimon Glass } 12657251285SSimon Glass 12757251285SSimon Glass UCLASS_DRIVER(syscon) = { 12857251285SSimon Glass .id = UCLASS_SYSCON, 12957251285SSimon Glass .name = "syscon", 13057251285SSimon Glass .per_device_auto_alloc_size = sizeof(struct syscon_uc_info), 13157251285SSimon Glass .pre_probe = syscon_pre_probe, 13257251285SSimon Glass }; 1338291bc87SPaul Burton 1348291bc87SPaul Burton static const struct udevice_id generic_syscon_ids[] = { 1358291bc87SPaul Burton { .compatible = "syscon" }, 1368291bc87SPaul Burton { } 1378291bc87SPaul Burton }; 1388291bc87SPaul Burton 1398291bc87SPaul Burton U_BOOT_DRIVER(generic_syscon) = { 1408291bc87SPaul Burton .name = "syscon", 1418291bc87SPaul Burton .id = UCLASS_SYSCON, 142745fb9c2SSimon Glass #if !CONFIG_IS_ENABLED(OF_PLATDATA) 143745fb9c2SSimon Glass .bind = dm_scan_fdt_dev, 144745fb9c2SSimon Glass #endif 1458291bc87SPaul Burton .of_match = generic_syscon_ids, 1468291bc87SPaul Burton }; 147e151a1c2SMasahiro Yamada 148e151a1c2SMasahiro Yamada /* 149e151a1c2SMasahiro Yamada * Linux-compatible syscon-to-regmap 150e151a1c2SMasahiro Yamada * The syscon node can be bound to another driver, but still works 151e151a1c2SMasahiro Yamada * as a syscon provider. 152e151a1c2SMasahiro Yamada */ 153de5bab9cSPatrick Delaunay struct regmap *syscon_node_to_regmap(ofnode node) 154e151a1c2SMasahiro Yamada { 155de5bab9cSPatrick Delaunay struct udevice *dev, *parent; 156e151a1c2SMasahiro Yamada int ret; 157e151a1c2SMasahiro Yamada 158de5bab9cSPatrick Delaunay if (!uclass_get_device_by_ofnode(UCLASS_SYSCON, node, &dev)) 159de5bab9cSPatrick Delaunay return syscon_get_regmap(dev); 160de5bab9cSPatrick Delaunay 161e151a1c2SMasahiro Yamada if (!ofnode_device_is_compatible(node, "syscon")) 162e151a1c2SMasahiro Yamada return ERR_PTR(-EINVAL); 163e151a1c2SMasahiro Yamada 164de5bab9cSPatrick Delaunay /* bound to driver with same ofnode or to root if not found */ 165de5bab9cSPatrick Delaunay if (device_find_global_by_ofnode(node, &parent)) 166de5bab9cSPatrick Delaunay parent = dm_root(); 167e151a1c2SMasahiro Yamada 168de5bab9cSPatrick Delaunay /* force bound to syscon class */ 169de5bab9cSPatrick Delaunay ret = device_bind_driver_to_node(parent, "syscon", 170de5bab9cSPatrick Delaunay ofnode_get_name(node), 171de5bab9cSPatrick Delaunay node, &dev); 172de5bab9cSPatrick Delaunay if (ret) 173e151a1c2SMasahiro Yamada return ERR_PTR(ret); 174e151a1c2SMasahiro Yamada 175de5bab9cSPatrick Delaunay ret = device_probe(dev); 176de5bab9cSPatrick Delaunay if (ret) 177de5bab9cSPatrick Delaunay return ERR_PTR(ret); 178e151a1c2SMasahiro Yamada 179de5bab9cSPatrick Delaunay return syscon_get_regmap(dev); 180e151a1c2SMasahiro Yamada } 181