1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2022 Ruslan Bukin <br@bsdpad.com> 5 * 6 * This work was supported by Innovate UK project 105694, "Digital Security 7 * by Design (DSbD) Technology Platform Prototype". 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 #include <sys/cdefs.h> 32 __FBSDID("$FreeBSD$"); 33 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/bus.h> 37 #include <sys/cpu.h> 38 #include <sys/kernel.h> 39 #include <sys/module.h> 40 41 #include <dev/extres/clk/clk.h> 42 #include <dev/fdt/simplebus.h> 43 #include <dev/fdt/fdt_common.h> 44 #include <dev/ofw/ofw_bus_subr.h> 45 46 #include "scmi.h" 47 #include "scmi_protocols.h" 48 #include "scmi_clk.h" 49 50 struct scmi_clk_softc { 51 device_t dev; 52 device_t scmi; 53 struct clkdom *clkdom; 54 }; 55 56 struct scmi_clknode_softc { 57 device_t dev; 58 int clock_id; 59 }; 60 61 static int __unused 62 scmi_clk_get_rate(struct scmi_clk_softc *sc, int clk_id, uint64_t *rate) 63 { 64 struct scmi_clk_rate_get_out out; 65 struct scmi_clk_rate_get_in in; 66 struct scmi_req req; 67 int error; 68 69 req.protocol_id = SCMI_PROTOCOL_ID_CLOCK; 70 req.message_id = SCMI_CLOCK_RATE_GET; 71 req.in_buf = ∈ 72 req.in_size = sizeof(struct scmi_clk_rate_get_in); 73 req.out_buf = &out; 74 req.out_size = sizeof(struct scmi_clk_rate_get_out); 75 76 in.clock_id = clk_id; 77 78 error = scmi_request(sc->scmi, &req); 79 if (error != 0) 80 return (error); 81 82 if (out.status != 0) 83 return (ENXIO); 84 85 *rate = out.rate_lsb | ((uint64_t)out.rate_msb << 32); 86 87 return (0); 88 } 89 90 static int 91 scmi_clk_set_rate(struct scmi_clk_softc *sc, int clk_id, uint64_t rate) 92 { 93 struct scmi_clk_rate_set_out out; 94 struct scmi_clk_rate_set_in in; 95 struct scmi_req req; 96 int error; 97 98 req.protocol_id = SCMI_PROTOCOL_ID_CLOCK; 99 req.message_id = SCMI_CLOCK_RATE_SET; 100 req.in_buf = ∈ 101 req.in_size = sizeof(struct scmi_clk_rate_set_in); 102 req.out_buf = &out; 103 req.out_size = sizeof(struct scmi_clk_rate_set_out); 104 105 in.clock_id = clk_id; 106 in.flags = SCMI_CLK_RATE_ROUND_CLOSEST; 107 in.rate_lsb = (uint32_t)rate; 108 in.rate_msb = (uint32_t)(rate >> 32); 109 110 error = scmi_request(sc->scmi, &req); 111 if (error != 0) 112 return (error); 113 114 if (out.status != 0) 115 return (ENXIO); 116 117 return (0); 118 } 119 120 static int __unused 121 scmi_clk_gate(struct scmi_clk_softc *sc, int clk_id, int enable) 122 { 123 struct scmi_clk_state_out out; 124 struct scmi_clk_state_in in; 125 struct scmi_req req; 126 int error; 127 128 req.protocol_id = SCMI_PROTOCOL_ID_CLOCK; 129 req.message_id = SCMI_CLOCK_CONFIG_SET; 130 req.in_buf = ∈ 131 req.in_size = sizeof(struct scmi_clk_state_in); 132 req.out_buf = &out; 133 req.out_size = sizeof(struct scmi_clk_state_out); 134 135 in.clock_id = clk_id; 136 in.attributes = enable; 137 138 error = scmi_request(sc->scmi, &req); 139 if (error != 0) 140 return (error); 141 142 if (out.status != 0) 143 return (ENXIO); 144 145 return (0); 146 } 147 148 static int 149 scmi_clknode_init(struct clknode *clk, device_t dev) 150 { 151 152 clknode_init_parent_idx(clk, 0); 153 154 return (0); 155 } 156 157 static int 158 scmi_clknode_recalc_freq(struct clknode *clk, uint64_t *freq) 159 { 160 161 return (0); 162 } 163 164 static int 165 scmi_clknode_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, 166 int flags, int *stop) 167 { 168 struct scmi_clknode_softc *clk_sc; 169 struct scmi_clk_softc *sc; 170 171 clk_sc = clknode_get_softc(clk); 172 sc = device_get_softc(clk_sc->dev); 173 174 dprintf("%s: %ld\n", __func__, *fout); 175 176 scmi_clk_set_rate(sc, clk_sc->clock_id, *fout); 177 178 *stop = 1; 179 180 return (0); 181 } 182 183 static clknode_method_t scmi_clknode_methods[] = { 184 /* Device interface */ 185 CLKNODEMETHOD(clknode_init, scmi_clknode_init), 186 CLKNODEMETHOD(clknode_recalc_freq, scmi_clknode_recalc_freq), 187 CLKNODEMETHOD(clknode_set_freq, scmi_clknode_set_freq), 188 CLKNODEMETHOD_END 189 }; 190 191 DEFINE_CLASS_1(scmi_clknode, scmi_clknode_class, scmi_clknode_methods, 192 sizeof(struct scmi_clknode_softc), clknode_class); 193 194 static int 195 scmi_clk_add_node(struct scmi_clk_softc *sc, int index, char *clock_name) 196 { 197 struct scmi_clknode_softc *clk_sc; 198 struct clknode_init_def def; 199 struct clknode *clk; 200 201 memset(&def, 0, sizeof(def)); 202 def.id = index; 203 def.name = clock_name; 204 def.parent_names = NULL; 205 def.parent_cnt = 0; 206 207 clk = clknode_create(sc->clkdom, &scmi_clknode_class, &def); 208 if (clk == NULL) { 209 device_printf(sc->dev, "Cannot create clknode.\n"); 210 return (ENXIO); 211 } 212 213 clk_sc = clknode_get_softc(clk); 214 clk_sc->dev = sc->dev; 215 clk_sc->clock_id = index; 216 217 if (clknode_register(sc->clkdom, clk) == NULL) { 218 device_printf(sc->dev, "Could not register clock '%s'.\n", 219 def.name); 220 return (ENXIO); 221 } 222 223 device_printf(sc->dev, "Clock '%s' registered.\n", def.name); 224 225 return (0); 226 } 227 228 static int 229 scmi_clk_get_name(struct scmi_clk_softc *sc, int index, char **result) 230 { 231 struct scmi_clk_name_get_out out; 232 struct scmi_clk_name_get_in in; 233 struct scmi_req req; 234 char *clock_name; 235 int error; 236 237 req.protocol_id = SCMI_PROTOCOL_ID_CLOCK; 238 req.message_id = SCMI_CLOCK_NAME_GET; 239 req.in_buf = ∈ 240 req.in_size = sizeof(struct scmi_clk_name_get_in); 241 req.out_buf = &out; 242 req.out_size = sizeof(struct scmi_clk_name_get_out); 243 244 in.clock_id = index; 245 246 error = scmi_request(sc->scmi, &req); 247 if (error != 0) 248 return (error); 249 250 if (out.status != 0) 251 return (ENXIO); 252 253 clock_name = malloc(sizeof(out.name), M_DEVBUF, M_WAITOK); 254 strncpy(clock_name, out.name, sizeof(out.name)); 255 256 *result = clock_name; 257 258 return (0); 259 } 260 261 static int 262 scmi_clk_attrs(struct scmi_clk_softc *sc, int index) 263 { 264 struct scmi_clk_attrs_out out; 265 struct scmi_clk_attrs_in in; 266 struct scmi_req req; 267 int error; 268 char *clock_name; 269 270 req.protocol_id = SCMI_PROTOCOL_ID_CLOCK; 271 req.message_id = SCMI_CLOCK_ATTRIBUTES; 272 req.in_buf = ∈ 273 req.in_size = sizeof(struct scmi_clk_attrs_in); 274 req.out_buf = &out; 275 req.out_size = sizeof(struct scmi_clk_attrs_out); 276 277 in.clock_id = index; 278 279 error = scmi_request(sc->scmi, &req); 280 if (error != 0) 281 return (error); 282 283 if (out.status != 0) 284 return (ENXIO); 285 286 if (out.attributes & CLK_ATTRS_EXT_CLK_NAME) { 287 error = scmi_clk_get_name(sc, index, &clock_name); 288 if (error) 289 return (error); 290 } else { 291 clock_name = malloc(sizeof(out.clock_name), M_DEVBUF, M_WAITOK); 292 strncpy(clock_name, out.clock_name, sizeof(out.clock_name)); 293 } 294 295 error = scmi_clk_add_node(sc, index, clock_name); 296 297 return (error); 298 } 299 300 static int 301 scmi_clk_discover(struct scmi_clk_softc *sc) 302 { 303 struct scmi_clk_protocol_attrs_out out; 304 struct scmi_req req; 305 int nclocks; 306 int failing; 307 int error; 308 int i; 309 310 req.protocol_id = SCMI_PROTOCOL_ID_CLOCK; 311 req.message_id = SCMI_PROTOCOL_ATTRIBUTES; 312 req.in_buf = NULL; 313 req.in_size = 0; 314 req.out_buf = &out; 315 req.out_size = sizeof(struct scmi_clk_protocol_attrs_out); 316 317 error = scmi_request(sc->scmi, &req); 318 if (error != 0) 319 return (error); 320 321 if (out.status != 0) 322 return (ENXIO); 323 324 nclocks = (out.attributes & CLK_ATTRS_NCLOCKS_M) >> 325 CLK_ATTRS_NCLOCKS_S; 326 327 device_printf(sc->dev, "Found %d clocks.\n", nclocks); 328 329 failing = 0; 330 331 for (i = 0; i < nclocks; i++) { 332 error = scmi_clk_attrs(sc, i); 333 if (error) { 334 device_printf(sc->dev, 335 "Could not process clock index %d.\n", i); 336 failing++; 337 } 338 } 339 340 if (failing == nclocks) 341 return (ENXIO); 342 343 return (0); 344 } 345 346 static int 347 scmi_clk_init(struct scmi_clk_softc *sc) 348 { 349 int error; 350 351 /* Create clock domain */ 352 sc->clkdom = clkdom_create(sc->dev); 353 if (sc->clkdom == NULL) 354 return (ENXIO); 355 356 error = scmi_clk_discover(sc); 357 if (error) { 358 device_printf(sc->dev, "Could not discover clocks.\n"); 359 return (ENXIO); 360 } 361 362 error = clkdom_finit(sc->clkdom); 363 if (error) { 364 device_printf(sc->dev, "Failed to init clock domain.\n"); 365 return (ENXIO); 366 } 367 368 return (0); 369 } 370 371 static int 372 scmi_clk_probe(device_t dev) 373 { 374 phandle_t node; 375 uint32_t reg; 376 int error; 377 378 node = ofw_bus_get_node(dev); 379 380 error = OF_getencprop(node, "reg", ®, sizeof(uint32_t)); 381 if (error < 0) 382 return (ENXIO); 383 384 if (reg != SCMI_PROTOCOL_ID_CLOCK) 385 return (ENXIO); 386 387 device_set_desc(dev, "SCMI Clock Management Unit"); 388 389 return (BUS_PROBE_DEFAULT); 390 } 391 392 static int 393 scmi_clk_attach(device_t dev) 394 { 395 struct scmi_clk_softc *sc; 396 phandle_t node; 397 398 sc = device_get_softc(dev); 399 sc->dev = dev; 400 sc->scmi = device_get_parent(dev); 401 402 node = ofw_bus_get_node(sc->dev); 403 404 OF_device_register_xref(OF_xref_from_node(node), sc->dev); 405 406 scmi_clk_init(sc); 407 408 return (0); 409 } 410 411 static int 412 scmi_clk_detach(device_t dev) 413 { 414 415 return (0); 416 } 417 418 static device_method_t scmi_clk_methods[] = { 419 /* Device interface */ 420 DEVMETHOD(device_probe, scmi_clk_probe), 421 DEVMETHOD(device_attach, scmi_clk_attach), 422 DEVMETHOD(device_detach, scmi_clk_detach), 423 DEVMETHOD_END 424 }; 425 426 static driver_t scmi_clk_driver = { 427 "scmi_clk", 428 scmi_clk_methods, 429 sizeof(struct scmi_clk_softc), 430 }; 431 432 EARLY_DRIVER_MODULE(scmi_clk, scmi, scmi_clk_driver, 0, 0, 433 BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); 434 MODULE_VERSION(scmi_clk, 1); 435