blob: f0e69d7216b3f0f0eeaf1726d1b37b1577335473 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Simon Glass6a84aaf2015-06-23 15:38:43 -06002/*
3 * Copyright (C) 2015 Google, Inc
4 * Written by Simon Glass <sjg@chromium.org>
Simon Glass6a84aaf2015-06-23 15:38:43 -06005 */
6
Sean Anderson02a8f6d2020-10-04 21:39:43 -04007#define LOG_CATEGORY UCLASS_SYSCON
8
Simon Glass0f2af882020-05-10 11:40:05 -06009#include <log.h>
Simon Glass6a84aaf2015-06-23 15:38:43 -060010#include <syscon.h>
11#include <dm.h>
12#include <errno.h>
13#include <regmap.h>
14#include <dm/device-internal.h>
Simon Glass9bc15642020-02-03 07:36:16 -070015#include <dm/device_compat.h>
Simon Glass6a84aaf2015-06-23 15:38:43 -060016#include <dm/lists.h>
17#include <dm/root.h>
18#include <linux/err.h>
19
Masahiro Yamada60f9a1e2018-04-19 12:14:04 +090020/*
21 * Caution:
Heinrich Schuchardt43b05c02020-08-22 07:16:26 +020022 * This API requires the given device has already been bound to the syscon
23 * driver. For example,
24 *
Masahiro Yamada60f9a1e2018-04-19 12:14:04 +090025 * compatible = "syscon", "simple-mfd";
Heinrich Schuchardt43b05c02020-08-22 07:16:26 +020026 *
Masahiro Yamada60f9a1e2018-04-19 12:14:04 +090027 * works, but
Heinrich Schuchardt43b05c02020-08-22 07:16:26 +020028 *
Masahiro Yamada60f9a1e2018-04-19 12:14:04 +090029 * compatible = "simple-mfd", "syscon";
Heinrich Schuchardt43b05c02020-08-22 07:16:26 +020030 *
31 * does not. The behavior is different from Linux.
Masahiro Yamada60f9a1e2018-04-19 12:14:04 +090032 */
Simon Glass6a84aaf2015-06-23 15:38:43 -060033struct regmap *syscon_get_regmap(struct udevice *dev)
34{
Simon Glassead64972015-07-06 12:54:38 -060035 struct syscon_uc_info *priv;
Simon Glass6a84aaf2015-06-23 15:38:43 -060036
Simon Glassead64972015-07-06 12:54:38 -060037 if (device_get_uclass_id(dev) != UCLASS_SYSCON)
38 return ERR_PTR(-ENOEXEC);
39 priv = dev_get_uclass_priv(dev);
Simon Glass6a84aaf2015-06-23 15:38:43 -060040 return priv->regmap;
41}
42
43static int syscon_pre_probe(struct udevice *dev)
44{
45 struct syscon_uc_info *priv = dev_get_uclass_priv(dev);
46
Simon Glass7e75aba2019-02-16 20:24:38 -070047 /* Special case for PCI devices, which don't have a regmap */
48 if (device_get_uclass_id(dev->parent) == UCLASS_PCI)
49 return 0;
50
Johan Jonker2e304a22023-03-13 01:30:46 +010051#if CONFIG_IS_ENABLED(OF_PLATDATA)
Simon Glass73dd0692016-07-04 11:58:00 -060052 /*
53 * With OF_PLATDATA we really have no way of knowing the format of
54 * the device-specific platform data. So we assume that it starts with
Johan Jonker2e304a22023-03-13 01:30:46 +010055 * a 'reg' member that holds a single address and size. Drivers
56 * using OF_PLATDATA will need to ensure that this is true. In case of
57 * odd reg structures other then the syscon_base_plat structure
58 * below the regmap must be defined in the individual syscon driver.
Simon Glass73dd0692016-07-04 11:58:00 -060059 */
Johan Jonker2e304a22023-03-13 01:30:46 +010060 struct syscon_base_plat {
61 phys_addr_t reg[2];
62 };
63
Simon Glassb75b15b2020-12-03 16:55:23 -070064 struct syscon_base_plat *plat = dev_get_plat(dev);
Simon Glass73dd0692016-07-04 11:58:00 -060065
Johan Jonker2e304a22023-03-13 01:30:46 +010066 /*
67 * Return if the regmap is already defined in the individual
68 * syscon driver.
69 */
70 if (priv->regmap)
71 return 0;
72
73 return regmap_init_mem_plat(dev, plat->reg, sizeof(plat->reg[0]),
74 ARRAY_SIZE(plat->reg) / 2, &priv->regmap);
Simon Glass73dd0692016-07-04 11:58:00 -060075#else
Masahiro Yamadae4873e32018-04-19 12:14:03 +090076 return regmap_init_mem(dev_ofnode(dev), &priv->regmap);
Simon Glass73dd0692016-07-04 11:58:00 -060077#endif
Simon Glass6a84aaf2015-06-23 15:38:43 -060078}
79
Patrick Delaunayee010432019-03-07 09:57:13 +010080static int syscon_probe_by_ofnode(ofnode node, struct udevice **devp)
81{
82 struct udevice *dev, *parent;
83 int ret;
84
85 /* found node with "syscon" compatible, not bounded to SYSCON UCLASS */
86 if (!ofnode_device_is_compatible(node, "syscon")) {
Sean Anderson8d06ce42020-09-15 10:44:37 -040087 log_debug("invalid compatible for syscon device\n");
Patrick Delaunayee010432019-03-07 09:57:13 +010088 return -EINVAL;
89 }
90
91 /* bound to driver with same ofnode or to root if not found */
92 if (device_find_global_by_ofnode(node, &parent))
93 parent = dm_root();
94
95 /* force bound to syscon class */
96 ret = device_bind_driver_to_node(parent, "syscon",
97 ofnode_get_name(node),
98 node, &dev);
99 if (ret) {
100 dev_dbg(dev, "unable to bound syscon device\n");
101 return ret;
102 }
103 ret = device_probe(dev);
104 if (ret) {
105 dev_dbg(dev, "unable to probe syscon device\n");
106 return ret;
107 }
108
109 *devp = dev;
110 return 0;
111}
112
Jean-Jacques Hiblotdc44ea42018-11-29 10:57:37 +0100113struct regmap *syscon_regmap_lookup_by_phandle(struct udevice *dev,
114 const char *name)
115{
116 struct udevice *syscon;
117 struct regmap *r;
Patrick Delaunayee010432019-03-07 09:57:13 +0100118 u32 phandle;
119 ofnode node;
Jean-Jacques Hiblotdc44ea42018-11-29 10:57:37 +0100120 int err;
121
122 err = uclass_get_device_by_phandle(UCLASS_SYSCON, dev,
123 name, &syscon);
124 if (err) {
Patrick Delaunayee010432019-03-07 09:57:13 +0100125 /* found node with "syscon" compatible, not bounded to SYSCON */
126 err = ofnode_read_u32(dev_ofnode(dev), name, &phandle);
127 if (err)
128 return ERR_PTR(err);
129
130 node = ofnode_get_by_phandle(phandle);
131 if (!ofnode_valid(node)) {
132 dev_dbg(dev, "unable to find syscon device\n");
133 return ERR_PTR(-EINVAL);
134 }
135 err = syscon_probe_by_ofnode(node, &syscon);
136 if (err)
137 return ERR_PTR(-ENODEV);
Jean-Jacques Hiblotdc44ea42018-11-29 10:57:37 +0100138 }
139
140 r = syscon_get_regmap(syscon);
141 if (!r) {
142 dev_dbg(dev, "unable to find regmap\n");
143 return ERR_PTR(-ENODEV);
144 }
145
146 return r;
147}
148
Simon Glass6d5579c2016-01-17 16:11:08 -0700149int syscon_get_by_driver_data(ulong driver_data, struct udevice **devp)
Simon Glass6a84aaf2015-06-23 15:38:43 -0600150{
Simon Glass6a84aaf2015-06-23 15:38:43 -0600151 int ret;
152
Simon Glass36ab8e32016-03-11 22:06:49 -0700153 *devp = NULL;
Simon Glasse5f50c92020-02-06 09:54:51 -0700154
155 ret = uclass_first_device_drvdata(UCLASS_SYSCON, driver_data, devp);
Simon Glass6a84aaf2015-06-23 15:38:43 -0600156 if (ret)
Simon Glass72f0d992020-09-27 18:46:17 -0600157 return ret;
Simon Glass6a84aaf2015-06-23 15:38:43 -0600158
Simon Glasse5f50c92020-02-06 09:54:51 -0700159 return 0;
Simon Glass6d5579c2016-01-17 16:11:08 -0700160}
161
162struct regmap *syscon_get_regmap_by_driver_data(ulong driver_data)
163{
164 struct syscon_uc_info *priv;
165 struct udevice *dev;
166 int ret;
167
168 ret = syscon_get_by_driver_data(driver_data, &dev);
169 if (ret)
170 return ERR_PTR(ret);
171 priv = dev_get_uclass_priv(dev);
172
173 return priv->regmap;
Simon Glass6a84aaf2015-06-23 15:38:43 -0600174}
175
176void *syscon_get_first_range(ulong driver_data)
177{
178 struct regmap *map;
179
180 map = syscon_get_regmap_by_driver_data(driver_data);
181 if (IS_ERR(map))
182 return map;
183 return regmap_get_range(map, 0);
184}
185
186UCLASS_DRIVER(syscon) = {
187 .id = UCLASS_SYSCON,
188 .name = "syscon",
Simon Glass8a2b47f2020-12-03 16:55:17 -0700189 .per_device_auto = sizeof(struct syscon_uc_info),
Simon Glass6a84aaf2015-06-23 15:38:43 -0600190 .pre_probe = syscon_pre_probe,
191};
Paul Burton8c946a72016-09-08 07:47:37 +0100192
193static const struct udevice_id generic_syscon_ids[] = {
194 { .compatible = "syscon" },
195 { }
196};
197
198U_BOOT_DRIVER(generic_syscon) = {
199 .name = "syscon",
200 .id = UCLASS_SYSCON,
Simon Glass92882652021-08-07 07:24:04 -0600201#if CONFIG_IS_ENABLED(OF_REAL)
Simon Glass8061bc62017-07-29 11:34:52 -0600202 .bind = dm_scan_fdt_dev,
203#endif
Paul Burton8c946a72016-09-08 07:47:37 +0100204 .of_match = generic_syscon_ids,
205};
Masahiro Yamada60f9a1e2018-04-19 12:14:04 +0900206
207/*
208 * Linux-compatible syscon-to-regmap
209 * The syscon node can be bound to another driver, but still works
210 * as a syscon provider.
211 */
Patrick Delaunay8a5a8a72018-10-31 16:49:03 +0100212struct regmap *syscon_node_to_regmap(ofnode node)
Masahiro Yamada60f9a1e2018-04-19 12:14:04 +0900213{
Patrick Delaunayee010432019-03-07 09:57:13 +0100214 struct udevice *dev;
215 struct regmap *r;
Masahiro Yamada60f9a1e2018-04-19 12:14:04 +0900216
Patrick Delaunayee010432019-03-07 09:57:13 +0100217 if (uclass_get_device_by_ofnode(UCLASS_SYSCON, node, &dev))
218 if (syscon_probe_by_ofnode(node, &dev))
219 return ERR_PTR(-ENODEV);
Masahiro Yamada60f9a1e2018-04-19 12:14:04 +0900220
Patrick Delaunayee010432019-03-07 09:57:13 +0100221 r = syscon_get_regmap(dev);
222 if (!r) {
223 dev_dbg(dev, "unable to find regmap\n");
224 return ERR_PTR(-ENODEV);
225 }
Masahiro Yamada60f9a1e2018-04-19 12:14:04 +0900226
Patrick Delaunayee010432019-03-07 09:57:13 +0100227 return r;
Masahiro Yamada60f9a1e2018-04-19 12:14:04 +0900228}