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