1 /* $OpenBSD: ofw_clock.c,v 1.11 2018/08/05 21:05:17 kettenis Exp $ */ 2 /* 3 * Copyright (c) 2016 Mark Kettenis 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/types.h> 19 #include <sys/systm.h> 20 #include <sys/malloc.h> 21 22 #include <dev/ofw/openfirm.h> 23 #include <dev/ofw/ofw_clock.h> 24 25 /* 26 * Clock functionality. 27 */ 28 29 LIST_HEAD(, clock_device) clock_devices = 30 LIST_HEAD_INITIALIZER(clock_devices); 31 32 void 33 clock_register(struct clock_device *cd) 34 { 35 cd->cd_cells = OF_getpropint(cd->cd_node, "#clock-cells", 0); 36 cd->cd_phandle = OF_getpropint(cd->cd_node, "phandle", 0); 37 if (cd->cd_phandle == 0) 38 return; 39 40 LIST_INSERT_HEAD(&clock_devices, cd, cd_list); 41 } 42 43 uint32_t 44 clock_get_frequency_cells(uint32_t *cells) 45 { 46 struct clock_device *cd; 47 uint32_t phandle = cells[0]; 48 int node; 49 50 LIST_FOREACH(cd, &clock_devices, cd_list) { 51 if (cd->cd_phandle == phandle) 52 break; 53 } 54 55 if (cd && cd->cd_get_frequency) 56 return cd->cd_get_frequency(cd->cd_cookie, &cells[1]); 57 58 node = OF_getnodebyphandle(phandle); 59 if (node == 0) 60 return 0; 61 62 if (OF_is_compatible(node, "fixed-clock")) 63 return OF_getpropint(node, "clock-frequency", 0); 64 65 if (OF_is_compatible(node, "fixed-factor-clock")) { 66 uint32_t mult, div, freq; 67 68 mult = OF_getpropint(node, "clock-mult", 1); 69 div = OF_getpropint(node, "clock-div", 1); 70 freq = clock_get_frequency(node, NULL); 71 return (freq * mult) / div; 72 } 73 74 return 0; 75 } 76 77 int 78 clock_set_frequency_cells(uint32_t *cells, uint32_t freq) 79 { 80 struct clock_device *cd; 81 uint32_t phandle = cells[0]; 82 83 LIST_FOREACH(cd, &clock_devices, cd_list) { 84 if (cd->cd_phandle == phandle) 85 break; 86 } 87 88 if (cd && cd->cd_set_frequency) 89 return cd->cd_set_frequency(cd->cd_cookie, &cells[1], freq); 90 91 return -1; 92 } 93 94 int 95 clock_set_parent_cells(uint32_t *cells, uint32_t *pcells) 96 { 97 struct clock_device *cd; 98 uint32_t phandle = cells[0]; 99 100 LIST_FOREACH(cd, &clock_devices, cd_list) { 101 if (cd->cd_phandle == phandle) 102 break; 103 } 104 105 if (cd && cd->cd_set_parent) 106 return cd->cd_set_parent(cd->cd_cookie, &cells[1], pcells); 107 108 return -1; 109 } 110 111 void 112 clock_enable_cells(uint32_t *cells, int on) 113 { 114 struct clock_device *cd; 115 uint32_t phandle = cells[0]; 116 117 LIST_FOREACH(cd, &clock_devices, cd_list) { 118 if (cd->cd_phandle == phandle) 119 break; 120 } 121 122 if (cd && cd->cd_enable) 123 cd->cd_enable(cd->cd_cookie, &cells[1], on); 124 } 125 126 uint32_t * 127 clock_next_clock(uint32_t *cells) 128 { 129 uint32_t phandle = cells[0]; 130 int node, ncells; 131 132 node = OF_getnodebyphandle(phandle); 133 if (node == 0) 134 return NULL; 135 136 ncells = OF_getpropint(node, "#clock-cells", 0); 137 return cells + ncells + 1; 138 } 139 140 uint32_t 141 clock_get_frequency_idx(int node, int idx) 142 { 143 uint32_t *clocks; 144 uint32_t *clock; 145 uint32_t freq = 0; 146 int len; 147 148 len = OF_getproplen(node, "clocks"); 149 if (len <= 0) 150 return 0; 151 152 clocks = malloc(len, M_TEMP, M_WAITOK); 153 OF_getpropintarray(node, "clocks", clocks, len); 154 155 clock = clocks; 156 while (clock && clock < clocks + (len / sizeof(uint32_t))) { 157 if (idx == 0) { 158 freq = clock_get_frequency_cells(clock); 159 break; 160 } 161 clock = clock_next_clock(clock); 162 idx--; 163 } 164 165 free(clocks, M_TEMP, len); 166 return freq; 167 } 168 169 uint32_t 170 clock_get_frequency(int node, const char *name) 171 { 172 int idx; 173 174 idx = OF_getindex(node, name, "clock-names"); 175 if (idx == -1) 176 return 0; 177 178 return clock_get_frequency_idx(node, idx); 179 } 180 181 int 182 clock_set_frequency_idx(int node, int idx, uint32_t freq) 183 { 184 uint32_t *clocks; 185 uint32_t *clock; 186 int rv = -1; 187 int len; 188 189 len = OF_getproplen(node, "clocks"); 190 if (len <= 0) 191 return -1; 192 193 clocks = malloc(len, M_TEMP, M_WAITOK); 194 OF_getpropintarray(node, "clocks", clocks, len); 195 196 clock = clocks; 197 while (clock && clock < clocks + (len / sizeof(uint32_t))) { 198 if (idx == 0) { 199 rv = clock_set_frequency_cells(clock, freq); 200 break; 201 } 202 clock = clock_next_clock(clock); 203 idx--; 204 } 205 206 free(clocks, M_TEMP, len); 207 return rv; 208 } 209 210 int 211 clock_set_frequency(int node, const char *name, uint32_t freq) 212 { 213 int idx; 214 215 idx = OF_getindex(node, name, "clock-names"); 216 if (idx == -1) 217 return -1; 218 219 return clock_set_frequency_idx(node, idx, freq); 220 } 221 222 void 223 clock_do_enable_idx(int node, int idx, int on) 224 { 225 uint32_t *clocks; 226 uint32_t *clock; 227 int len; 228 229 len = OF_getproplen(node, "clocks"); 230 if (len <= 0) 231 return; 232 233 clocks = malloc(len, M_TEMP, M_WAITOK); 234 OF_getpropintarray(node, "clocks", clocks, len); 235 236 clock = clocks; 237 while (clock && clock < clocks + (len / sizeof(uint32_t))) { 238 if (idx <= 0) 239 clock_enable_cells(clock, on); 240 if (idx == 0) 241 break; 242 clock = clock_next_clock(clock); 243 idx--; 244 } 245 246 free(clocks, M_TEMP, len); 247 } 248 249 void 250 clock_do_enable(int node, const char *name, int on) 251 { 252 int idx; 253 254 idx = OF_getindex(node, name, "clock-names"); 255 if (idx == -1) 256 return; 257 258 clock_do_enable_idx(node, idx, on); 259 } 260 261 void 262 clock_enable_idx(int node, int idx) 263 { 264 clock_do_enable_idx(node, idx, 1); 265 } 266 267 void 268 clock_enable(int node, const char *name) 269 { 270 clock_do_enable(node, name, 1); 271 } 272 273 void 274 clock_disable_idx(int node, int idx) 275 { 276 clock_do_enable_idx(node, idx, 0); 277 } 278 279 void 280 clock_disable(int node, const char *name) 281 { 282 clock_do_enable(node, name, 0); 283 } 284 285 void 286 clock_set_assigned(int node) 287 { 288 uint32_t *clocks, *parents, *rates; 289 uint32_t *clock, *parent, *rate; 290 int clen, plen, rlen; 291 292 clen = OF_getproplen(node, "assigned-clocks"); 293 plen = OF_getproplen(node, "assigned-clock-parents"); 294 rlen = OF_getproplen(node, "assigned-clock-rates"); 295 296 if (clen <= 0 || (plen <= 0 && rlen <= 0)) 297 return; 298 299 clock = clocks = malloc(clen, M_TEMP, M_WAITOK); 300 OF_getpropintarray(node, "assigned-clocks", clocks, clen); 301 302 parent = parents = NULL; 303 if (plen > 0) { 304 parent = parents = malloc(plen, M_TEMP, M_WAITOK); 305 OF_getpropintarray(node, "assigned-clock-parents", parents, plen); 306 } 307 rate = rates = NULL; 308 if (rlen > 0) { 309 rate = rates = malloc(rlen, M_TEMP, M_WAITOK); 310 OF_getpropintarray(node, "assigned-clock-rates", rates, rlen); 311 } 312 313 while (clock && clock < clocks + (clen / sizeof(uint32_t))) { 314 if (parent && parent < parents + (plen / sizeof(uint32_t))) 315 if (*parent != 0) 316 clock_set_parent_cells(clock, parent); 317 318 if (rate && rate < rates + (rlen / sizeof(uint32_t))) 319 if (*rate != 0) 320 clock_set_frequency_cells(clock, *rate); 321 322 clock = clock_next_clock(clock); 323 if (parent) 324 parent = clock_next_clock(parent); 325 if (rate) 326 rate++; 327 } 328 329 free(clocks, M_TEMP, clen); 330 free(parents, M_TEMP, plen); 331 free(rates, M_TEMP, rlen); 332 } 333 334 /* 335 * Reset functionality. 336 */ 337 338 LIST_HEAD(, reset_device) reset_devices = 339 LIST_HEAD_INITIALIZER(reset_devices); 340 341 void 342 reset_register(struct reset_device *rd) 343 { 344 rd->rd_cells = OF_getpropint(rd->rd_node, "#reset-cells", 0); 345 rd->rd_phandle = OF_getpropint(rd->rd_node, "phandle", 0); 346 if (rd->rd_phandle == 0) 347 return; 348 349 LIST_INSERT_HEAD(&reset_devices, rd, rd_list); 350 } 351 352 void 353 reset_assert_cells(uint32_t *cells, int assert) 354 { 355 struct reset_device *rd; 356 uint32_t phandle = cells[0]; 357 358 LIST_FOREACH(rd, &reset_devices, rd_list) { 359 if (rd->rd_phandle == phandle) 360 break; 361 } 362 363 if (rd && rd->rd_reset) 364 rd->rd_reset(rd->rd_cookie, &cells[1], assert); 365 } 366 367 uint32_t * 368 reset_next_reset(uint32_t *cells) 369 { 370 uint32_t phandle = cells[0]; 371 int node, ncells; 372 373 node = OF_getnodebyphandle(phandle); 374 if (node == 0) 375 return NULL; 376 377 ncells = OF_getpropint(node, "#reset-cells", 0); 378 return cells + ncells + 1; 379 } 380 381 void 382 reset_do_assert_idx(int node, int idx, int assert) 383 { 384 uint32_t *resets; 385 uint32_t *reset; 386 int len; 387 388 len = OF_getproplen(node, "resets"); 389 if (len <= 0) 390 return; 391 392 resets = malloc(len, M_TEMP, M_WAITOK); 393 OF_getpropintarray(node, "resets", resets, len); 394 395 reset = resets; 396 while (reset && reset < resets + (len / sizeof(uint32_t))) { 397 if (idx <= 0) 398 reset_assert_cells(reset, assert); 399 if (idx == 0) 400 break; 401 reset = reset_next_reset(reset); 402 idx--; 403 } 404 405 free(resets, M_TEMP, len); 406 } 407 408 void 409 reset_do_assert(int node, const char *name, int assert) 410 { 411 int idx; 412 413 idx = OF_getindex(node, name, "reset-names"); 414 if (idx == -1) 415 return; 416 417 reset_do_assert_idx(node, idx, assert); 418 } 419 420 void 421 reset_assert_idx(int node, int idx) 422 { 423 reset_do_assert_idx(node, idx, 1); 424 } 425 426 void 427 reset_assert(int node, const char *name) 428 { 429 reset_do_assert(node, name, 1); 430 } 431 432 void 433 reset_deassert_idx(int node, int idx) 434 { 435 reset_do_assert_idx(node, idx, 0); 436 } 437 438 void 439 reset_deassert(int node, const char *name) 440 { 441 reset_do_assert(node, name, 0); 442 } 443