xref: /openbmc/u-boot/drivers/core/syscon-uclass.c (revision c507d306)
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