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 */
syscon_get_regmap(struct udevice * dev)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
syscon_pre_probe(struct udevice * dev)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
syscon_regmap_lookup_by_phandle(struct udevice * dev,const char * name)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
syscon_get_by_driver_data(ulong driver_data,struct udevice ** devp)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
syscon_get_regmap_by_driver_data(ulong driver_data)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
syscon_get_first_range(ulong driver_data)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 */
syscon_node_to_regmap(ofnode node)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