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