Coverage Report

Created: 2026-03-11 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/u-boot/drivers/core/syscon-uclass.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0+
2
/*
3
 * Copyright (C) 2015 Google, Inc
4
 * Written by Simon Glass <sjg@chromium.org>
5
 */
6
7
#define LOG_CATEGORY UCLASS_SYSCON
8
9
#include <log.h>
10
#include <syscon.h>
11
#include <dm.h>
12
#include <errno.h>
13
#include <regmap.h>
14
#include <dm/device-internal.h>
15
#include <dm/device_compat.h>
16
#include <dm/lists.h>
17
#include <dm/root.h>
18
#include <linux/err.h>
19
20
/*
21
 * Caution:
22
 * This API requires the given device has already been bound to the syscon
23
 * driver. For example,
24
 *
25
 *    compatible = "syscon", "simple-mfd";
26
 *
27
 * works, but
28
 *
29
 *    compatible = "simple-mfd", "syscon";
30
 *
31
 * does not. The behavior is different from Linux.
32
 */
33
struct regmap *syscon_get_regmap(struct udevice *dev)
34
0
{
35
0
  struct syscon_uc_info *priv;
36
37
0
  if (device_get_uclass_id(dev) != UCLASS_SYSCON)
38
0
    return ERR_PTR(-ENOEXEC);
39
0
  priv = dev_get_uclass_priv(dev);
40
0
  return priv->regmap;
41
0
}
42
43
static int syscon_pre_probe(struct udevice *dev)
44
0
{
45
0
  struct syscon_uc_info *priv = dev_get_uclass_priv(dev);
46
47
  /* Special case for PCI devices, which don't have a regmap */
48
0
  if (device_get_uclass_id(dev->parent) == UCLASS_PCI)
49
0
    return 0;
50
51
#if CONFIG_IS_ENABLED(OF_PLATDATA)
52
  /*
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
55
   * 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.
59
   */
60
  struct syscon_base_plat {
61
    phys_addr_t reg[2];
62
  };
63
64
  struct syscon_base_plat *plat = dev_get_plat(dev);
65
66
  /*
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);
75
#else
76
0
  return regmap_init_mem(dev_ofnode(dev), &priv->regmap);
77
0
#endif
78
0
}
79
80
static int syscon_probe_by_ofnode(ofnode node, struct udevice **devp)
81
0
{
82
0
  struct udevice *dev, *parent;
83
0
  int ret;
84
85
  /* found node with "syscon" compatible, not bounded to SYSCON UCLASS */
86
0
  if (!ofnode_device_is_compatible(node, "syscon")) {
87
0
    log_debug("invalid compatible for syscon device\n");
88
0
    return -EINVAL;
89
0
  }
90
91
  /* bound to driver with same ofnode or to root if not found */
92
0
  if (device_find_global_by_ofnode(node, &parent))
93
0
    parent = dm_root();
94
95
  /* force bound to syscon class */
96
0
  ret = device_bind_driver_to_node(parent, "syscon",
97
0
           ofnode_get_name(node),
98
0
           node, &dev);
99
0
  if (ret) {
100
0
    dev_dbg(dev, "unable to bound syscon device\n");
101
0
    return ret;
102
0
  }
103
0
  ret = device_probe(dev);
104
0
  if (ret) {
105
0
    dev_dbg(dev, "unable to probe syscon device\n");
106
0
    return ret;
107
0
  }
108
109
0
  *devp = dev;
110
0
  return 0;
111
0
}
112
113
struct regmap *syscon_regmap_lookup_by_phandle(struct udevice *dev,
114
                 const char *name)
115
0
{
116
0
  struct udevice *syscon;
117
0
  struct regmap *r;
118
0
  u32 phandle;
119
0
  ofnode node;
120
0
  int err;
121
122
0
  err = uclass_get_device_by_phandle(UCLASS_SYSCON, dev,
123
0
             name, &syscon);
124
0
  if (err) {
125
    /* found node with "syscon" compatible, not bounded to SYSCON */
126
0
    err = ofnode_read_u32(dev_ofnode(dev), name, &phandle);
127
0
    if (err)
128
0
      return ERR_PTR(err);
129
130
0
    node = ofnode_get_by_phandle(phandle);
131
0
    if (!ofnode_valid(node)) {
132
0
      dev_dbg(dev, "unable to find syscon device\n");
133
0
      return ERR_PTR(-EINVAL);
134
0
    }
135
0
    err = syscon_probe_by_ofnode(node, &syscon);
136
0
    if (err)
137
0
      return ERR_PTR(-ENODEV);
138
0
  }
139
140
0
  r = syscon_get_regmap(syscon);
141
0
  if (!r) {
142
0
    dev_dbg(dev, "unable to find regmap\n");
143
0
    return ERR_PTR(-ENODEV);
144
0
  }
145
146
0
  return r;
147
0
}
148
149
int syscon_get_by_driver_data(ulong driver_data, struct udevice **devp)
150
0
{
151
0
  int ret;
152
153
0
  *devp = NULL;
154
155
0
  ret = uclass_first_device_drvdata(UCLASS_SYSCON, driver_data, devp);
156
0
  if (ret)
157
0
    return ret;
158
159
0
  return 0;
160
0
}
161
162
struct regmap *syscon_get_regmap_by_driver_data(ulong driver_data)
163
0
{
164
0
  struct syscon_uc_info *priv;
165
0
  struct udevice *dev;
166
0
  int ret;
167
168
0
  ret = syscon_get_by_driver_data(driver_data, &dev);
169
0
  if (ret)
170
0
    return ERR_PTR(ret);
171
0
  priv = dev_get_uclass_priv(dev);
172
173
0
  return priv->regmap;
174
0
}
175
176
void *syscon_get_first_range(ulong driver_data)
177
0
{
178
0
  struct regmap *map;
179
180
0
  map = syscon_get_regmap_by_driver_data(driver_data);
181
0
  if (IS_ERR(map))
182
0
    return map;
183
0
  return regmap_get_range(map, 0);
184
0
}
185
186
UCLASS_DRIVER(syscon) = {
187
  .id   = UCLASS_SYSCON,
188
  .name   = "syscon",
189
  .per_device_auto  = sizeof(struct syscon_uc_info),
190
  .pre_probe = syscon_pre_probe,
191
};
192
193
static const struct udevice_id generic_syscon_ids[] = {
194
  { .compatible = "syscon" },
195
  { }
196
};
197
198
U_BOOT_DRIVER(generic_syscon) = {
199
  .name = "syscon",
200
  .id = UCLASS_SYSCON,
201
#if CONFIG_IS_ENABLED(OF_REAL)
202
  .bind           = dm_scan_fdt_dev,
203
#endif
204
  .of_match = generic_syscon_ids,
205
};
206
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
 */
212
struct regmap *syscon_node_to_regmap(ofnode node)
213
0
{
214
0
  struct udevice *dev;
215
0
  struct regmap *r;
216
217
0
  if (uclass_get_device_by_ofnode(UCLASS_SYSCON, node, &dev))
218
0
    if (syscon_probe_by_ofnode(node, &dev))
219
0
      return ERR_PTR(-ENODEV);
220
221
0
  r = syscon_get_regmap(dev);
222
0
  if (!r) {
223
0
    dev_dbg(dev, "unable to find regmap\n");
224
0
    return ERR_PTR(-ENODEV);
225
0
  }
226
227
0
  return r;
228
0
}