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 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/bus.h> 35 #include <sys/cpu.h> 36 #include <sys/kernel.h> 37 #include <sys/module.h> 38 39 #include <dev/extres/clk/clk.h> 40 #include <dev/fdt/simplebus.h> 41 #include <dev/fdt/fdt_common.h> 42 #include <dev/ofw/ofw_bus_subr.h> 43 44 #include "scmi.h" 45 #include "scmi_protocols.h" 46 #include "scmi_clk.h" 47 48 struct scmi_clk_softc { 49 device_t dev; 50 device_t scmi; 51 struct clkdom *clkdom; 52 }; 53 54 struct scmi_clknode_softc { 55 device_t dev; 56 int clock_id; 57 }; 58 59 static int __unused 60 scmi_clk_get_rate(struct scmi_clk_softc *sc, int clk_id, uint64_t *rate) 61 { 62 struct scmi_clk_rate_get_out out; 63 struct scmi_clk_rate_get_in in; 64 struct scmi_req req; 65 int error; 66 67 req.protocol_id = SCMI_PROTOCOL_ID_CLOCK; 68 req.message_id = SCMI_CLOCK_RATE_GET; 69 req.in_buf = ∈ 70 req.in_size = sizeof(struct scmi_clk_rate_get_in); 71 req.out_buf = &out; 72 req.out_size = sizeof(struct scmi_clk_rate_get_out); 73 74 in.clock_id = clk_id; 75 76 error = scmi_request(sc->scmi, &req); 77 if (error != 0) 78 return (error); 79 80 if (out.status != 0) 81 return (ENXIO); 82 83 *rate = out.rate_lsb | ((uint64_t)out.rate_msb << 32); 84 85 return (0); 86 } 87 88 static int 89 scmi_clk_set_rate(struct scmi_clk_softc *sc, int clk_id, uint64_t rate) 90 { 91 struct scmi_clk_rate_set_out out; 92 struct scmi_clk_rate_set_in in; 93 struct scmi_req req; 94 int error; 95 96 req.protocol_id = SCMI_PROTOCOL_ID_CLOCK; 97 req.message_id = SCMI_CLOCK_RATE_SET; 98 req.in_buf = ∈ 99 req.in_size = sizeof(struct scmi_clk_rate_set_in); 100 req.out_buf = &out; 101 req.out_size = sizeof(struct scmi_clk_rate_set_out); 102 103 in.clock_id = clk_id; 104 in.flags = SCMI_CLK_RATE_ROUND_CLOSEST; 105 in.rate_lsb = (uint32_t)rate; 106 in.rate_msb = (uint32_t)(rate >> 32); 107 108 error = scmi_request(sc->scmi, &req); 109 if (error != 0) 110 return (error); 111 112 if (out.status != 0) 113 return (ENXIO); 114 115 return (0); 116 } 117 118 static int __unused 119 scmi_clk_gate(struct scmi_clk_softc *sc, int clk_id, int enable) 120 { 121 struct scmi_clk_state_out out; 122 struct scmi_clk_state_in in; 123 struct scmi_req req; 124 int error; 125 126 req.protocol_id = SCMI_PROTOCOL_ID_CLOCK; 127 req.message_id = SCMI_CLOCK_CONFIG_SET; 128 req.in_buf = ∈ 129 req.in_size = sizeof(struct scmi_clk_state_in); 130 req.out_buf = &out; 131 req.out_size = sizeof(struct scmi_clk_state_out); 132 133 in.clock_id = clk_id; 134 in.attributes = enable; 135 136 error = scmi_request(sc->scmi, &req); 137 if (error != 0) 138 return (error); 139 140 if (out.status != 0) 141 return (ENXIO); 142 143 return (0); 144 } 145 146 static int 147 scmi_clknode_init(struct clknode *clk, device_t dev) 148 { 149 150 clknode_init_parent_idx(clk, 0); 151 152 return (0); 153 } 154 155 static int 156 scmi_clknode_recalc_freq(struct clknode *clk, uint64_t *freq) 157 { 158 159 return (0); 160 } 161 162 static int 163 scmi_clknode_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, 164 int flags, int *stop) 165 { 166 struct scmi_clknode_softc *clk_sc; 167 struct scmi_clk_softc *sc; 168 169 clk_sc = clknode_get_softc(clk); 170 sc = device_get_softc(clk_sc->dev); 171 172 dprintf("%s: %ld\n", __func__, *fout); 173 174 scmi_clk_set_rate(sc, clk_sc->clock_id, *fout); 175 176 *stop = 1; 177 178 return (0); 179 } 180 181 static clknode_method_t scmi_clknode_methods[] = { 182 /* Device interface */ 183 CLKNODEMETHOD(clknode_init, scmi_clknode_init), 184 CLKNODEMETHOD(clknode_recalc_freq, scmi_clknode_recalc_freq), 185 CLKNODEMETHOD(clknode_set_freq, scmi_clknode_set_freq), 186 CLKNODEMETHOD_END 187 }; 188 189 DEFINE_CLASS_1(scmi_clknode, scmi_clknode_class, scmi_clknode_methods, 190 sizeof(struct scmi_clknode_softc), clknode_class); 191 192 static int 193 scmi_clk_add_node(struct scmi_clk_softc *sc, int index, char *clock_name) 194 { 195 struct scmi_clknode_softc *clk_sc; 196 struct clknode_init_def def; 197 struct clknode *clk; 198 199 memset(&def, 0, sizeof(def)); 200 def.id = index; 201 def.name = clock_name; 202 def.parent_names = NULL; 203 def.parent_cnt = 0; 204 205 clk = clknode_create(sc->clkdom, &scmi_clknode_class, &def); 206 if (clk == NULL) { 207 device_printf(sc->dev, "Cannot create clknode.\n"); 208 return (ENXIO); 209 } 210 211 clk_sc = clknode_get_softc(clk); 212 clk_sc->dev = sc->dev; 213 clk_sc->clock_id = index; 214 215 if (clknode_register(sc->clkdom, clk) == NULL) { 216 device_printf(sc->dev, "Could not register clock '%s'.\n", 217 def.name); 218 return (ENXIO); 219 } 220 221 device_printf(sc->dev, "Clock '%s' registered.\n", def.name); 222 223 return (0); 224 } 225 226 static int 227 scmi_clk_get_name(struct scmi_clk_softc *sc, int index, char **result) 228 { 229 struct scmi_clk_name_get_out out; 230 struct scmi_clk_name_get_in in; 231 struct scmi_req req; 232 char *clock_name; 233 int error; 234 235 req.protocol_id = SCMI_PROTOCOL_ID_CLOCK; 236 req.message_id = SCMI_CLOCK_NAME_GET; 237 req.in_buf = ∈ 238 req.in_size = sizeof(struct scmi_clk_name_get_in); 239 req.out_buf = &out; 240 req.out_size = sizeof(struct scmi_clk_name_get_out); 241 242 in.clock_id = index; 243 244 error = scmi_request(sc->scmi, &req); 245 if (error != 0) 246 return (error); 247 248 if (out.status != 0) 249 return (ENXIO); 250 251 clock_name = malloc(sizeof(out.name), M_DEVBUF, M_WAITOK); 252 strncpy(clock_name, out.name, sizeof(out.name)); 253 254 *result = clock_name; 255 256 return (0); 257 } 258 259 static int 260 scmi_clk_attrs(struct scmi_clk_softc *sc, int index) 261 { 262 struct scmi_clk_attrs_out out; 263 struct scmi_clk_attrs_in in; 264 struct scmi_req req; 265 int error; 266 char *clock_name; 267 268 req.protocol_id = SCMI_PROTOCOL_ID_CLOCK; 269 req.message_id = SCMI_CLOCK_ATTRIBUTES; 270 req.in_buf = ∈ 271 req.in_size = sizeof(struct scmi_clk_attrs_in); 272 req.out_buf = &out; 273 req.out_size = sizeof(struct scmi_clk_attrs_out); 274 275 in.clock_id = index; 276 277 error = scmi_request(sc->scmi, &req); 278 if (error != 0) 279 return (error); 280 281 if (out.status != 0) 282 return (ENXIO); 283 284 if (out.attributes & CLK_ATTRS_EXT_CLK_NAME) { 285 error = scmi_clk_get_name(sc, index, &clock_name); 286 if (error) 287 return (error); 288 } else { 289 clock_name = malloc(sizeof(out.clock_name), M_DEVBUF, M_WAITOK); 290 strncpy(clock_name, out.clock_name, sizeof(out.clock_name)); 291 } 292 293 error = scmi_clk_add_node(sc, index, clock_name); 294 295 return (error); 296 } 297 298 static int 299 scmi_clk_discover(struct scmi_clk_softc *sc) 300 { 301 struct scmi_clk_protocol_attrs_out out; 302 struct scmi_req req; 303 int nclocks; 304 int failing; 305 int error; 306 int i; 307 308 req.protocol_id = SCMI_PROTOCOL_ID_CLOCK; 309 req.message_id = SCMI_PROTOCOL_ATTRIBUTES; 310 req.in_buf = NULL; 311 req.in_size = 0; 312 req.out_buf = &out; 313 req.out_size = sizeof(struct scmi_clk_protocol_attrs_out); 314 315 error = scmi_request(sc->scmi, &req); 316 if (error != 0) 317 return (error); 318 319 if (out.status != 0) 320 return (ENXIO); 321 322 nclocks = (out.attributes & CLK_ATTRS_NCLOCKS_M) >> 323 CLK_ATTRS_NCLOCKS_S; 324 325 device_printf(sc->dev, "Found %d clocks.\n", nclocks); 326 327 failing = 0; 328 329 for (i = 0; i < nclocks; i++) { 330 error = scmi_clk_attrs(sc, i); 331 if (error) { 332 device_printf(sc->dev, 333 "Could not process clock index %d.\n", i); 334 failing++; 335 } 336 } 337 338 if (failing == nclocks) 339 return (ENXIO); 340 341 return (0); 342 } 343 344 static int 345 scmi_clk_init(struct scmi_clk_softc *sc) 346 { 347 int error; 348 349 /* Create clock domain */ 350 sc->clkdom = clkdom_create(sc->dev); 351 if (sc->clkdom == NULL) 352 return (ENXIO); 353 354 error = scmi_clk_discover(sc); 355 if (error) { 356 device_printf(sc->dev, "Could not discover clocks.\n"); 357 return (ENXIO); 358 } 359 360 error = clkdom_finit(sc->clkdom); 361 if (error) { 362 device_printf(sc->dev, "Failed to init clock domain.\n"); 363 return (ENXIO); 364 } 365 366 return (0); 367 } 368 369 static int 370 scmi_clk_probe(device_t dev) 371 { 372 phandle_t node; 373 uint32_t reg; 374 int error; 375 376 node = ofw_bus_get_node(dev); 377 378 error = OF_getencprop(node, "reg", ®, sizeof(uint32_t)); 379 if (error < 0) 380 return (ENXIO); 381 382 if (reg != SCMI_PROTOCOL_ID_CLOCK) 383 return (ENXIO); 384 385 device_set_desc(dev, "SCMI Clock Management Unit"); 386 387 return (BUS_PROBE_DEFAULT); 388 } 389 390 static int 391 scmi_clk_attach(device_t dev) 392 { 393 struct scmi_clk_softc *sc; 394 phandle_t node; 395 396 sc = device_get_softc(dev); 397 sc->dev = dev; 398 sc->scmi = device_get_parent(dev); 399 400 node = ofw_bus_get_node(sc->dev); 401 402 OF_device_register_xref(OF_xref_from_node(node), sc->dev); 403 404 scmi_clk_init(sc); 405 406 return (0); 407 } 408 409 static int 410 scmi_clk_detach(device_t dev) 411 { 412 413 return (0); 414 } 415 416 static device_method_t scmi_clk_methods[] = { 417 /* Device interface */ 418 DEVMETHOD(device_probe, scmi_clk_probe), 419 DEVMETHOD(device_attach, scmi_clk_attach), 420 DEVMETHOD(device_detach, scmi_clk_detach), 421 DEVMETHOD_END 422 }; 423 424 static driver_t scmi_clk_driver = { 425 "scmi_clk", 426 scmi_clk_methods, 427 sizeof(struct scmi_clk_softc), 428 }; 429 430 EARLY_DRIVER_MODULE(scmi_clk, scmi, scmi_clk_driver, 0, 0, 431 BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); 432 MODULE_VERSION(scmi_clk, 1); 433