Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

base: soc: Allow early registration of a single SoC device

Commit 1da1b3628df34a2a ("base: soc: Early register bus when needed")
added support for early registration of SoC devices from a
core_initcall(). However, some drivers need to check the SoC revision
from an early_initcall(), which is even earlier.

A specific example is the Renesas R-Car SYSC driver, which manages PM
Domains and thus needs to be initialized from an early_initcall.
Preproduction versions of the R-Car H3 SoC have an additional power
area, which no longer exists on H3 ES2.0, so the R-Car SYSC driver needs
to check the exact SoC revision before instantiating a PM Domain for
that power area.

While registering the SoC bus and device, and using soc_device_match(),
from an early_initcall() do work, the "soc" directory and the "soc0"
file end up wrongly in the sysfs root, as the "bus" resp. "devices"
directories haven't been created yet.

To fix this, allow to register a single SoC device early on.
As long as the SoC bus isn't registered, soc_device_match() just
matches against this early device.
When the SoC bus is registered later, the early device is registered for
real.

Note that soc_device_register() returns NULL (no error, but also not a
valid pointer) when registering an early device. Hence platform devices
cannot be instantiated as children of the "soc0" node representing an
early SoC device. This should not be an issue, as that practice has
been deprecated for new platforms.

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Acked-by: Arnd Bergmann <arnd@arndb.de>

+42 -28
+42 -28
drivers/base/soc.c
··· 109 109 kfree(soc_dev); 110 110 } 111 111 112 + static struct soc_device_attribute *early_soc_dev_attr; 113 + 112 114 struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr) 113 115 { 114 116 struct soc_device *soc_dev; 115 117 int ret; 116 118 117 119 if (!soc_bus_type.p) { 118 - ret = bus_register(&soc_bus_type); 119 - if (ret) 120 - goto out1; 120 + if (early_soc_dev_attr) 121 + return ERR_PTR(-EBUSY); 122 + early_soc_dev_attr = soc_dev_attr; 123 + return NULL; 121 124 } 122 125 123 126 soc_dev = kzalloc(sizeof(*soc_dev), GFP_KERNEL); ··· 162 159 ida_simple_remove(&soc_ida, soc_dev->soc_dev_num); 163 160 164 161 device_unregister(&soc_dev->dev); 162 + early_soc_dev_attr = NULL; 165 163 } 166 164 167 165 static int __init soc_bus_register(void) 168 166 { 169 - if (soc_bus_type.p) 170 - return 0; 167 + int ret; 171 168 172 - return bus_register(&soc_bus_type); 169 + ret = bus_register(&soc_bus_type); 170 + if (ret) 171 + return ret; 172 + 173 + if (early_soc_dev_attr) 174 + return PTR_ERR(soc_device_register(early_soc_dev_attr)); 175 + 176 + return 0; 173 177 } 174 178 core_initcall(soc_bus_register); 179 + 180 + static int soc_device_match_attr(const struct soc_device_attribute *attr, 181 + const struct soc_device_attribute *match) 182 + { 183 + if (match->machine && 184 + (!attr->machine || !glob_match(match->machine, attr->machine))) 185 + return 0; 186 + 187 + if (match->family && 188 + (!attr->family || !glob_match(match->family, attr->family))) 189 + return 0; 190 + 191 + if (match->revision && 192 + (!attr->revision || !glob_match(match->revision, attr->revision))) 193 + return 0; 194 + 195 + if (match->soc_id && 196 + (!attr->soc_id || !glob_match(match->soc_id, attr->soc_id))) 197 + return 0; 198 + 199 + return 1; 200 + } 175 201 176 202 static int soc_device_match_one(struct device *dev, void *arg) 177 203 { 178 204 struct soc_device *soc_dev = container_of(dev, struct soc_device, dev); 179 - const struct soc_device_attribute *match = arg; 180 205 181 - if (match->machine && 182 - (!soc_dev->attr->machine || 183 - !glob_match(match->machine, soc_dev->attr->machine))) 184 - return 0; 185 - 186 - if (match->family && 187 - (!soc_dev->attr->family || 188 - !glob_match(match->family, soc_dev->attr->family))) 189 - return 0; 190 - 191 - if (match->revision && 192 - (!soc_dev->attr->revision || 193 - !glob_match(match->revision, soc_dev->attr->revision))) 194 - return 0; 195 - 196 - if (match->soc_id && 197 - (!soc_dev->attr->soc_id || 198 - !glob_match(match->soc_id, soc_dev->attr->soc_id))) 199 - return 0; 200 - 201 - return 1; 206 + return soc_device_match_attr(soc_dev->attr, arg); 202 207 } 203 208 204 209 /* ··· 241 230 break; 242 231 ret = bus_for_each_dev(&soc_bus_type, NULL, (void *)matches, 243 232 soc_device_match_one); 233 + if (ret < 0 && early_soc_dev_attr) 234 + ret = soc_device_match_attr(early_soc_dev_attr, 235 + matches); 244 236 if (ret < 0) 245 237 return NULL; 246 238 if (!ret)