1 /* $OpenBSD: ofw_clock.c,v 1.7 2016/08/27 16:50:40 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 void 95 clock_enable_cells(uint32_t *cells, int on) 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_enable) 106 cd->cd_enable(cd->cd_cookie, &cells[1], on); 107 } 108 109 uint32_t * 110 clock_next_clock(uint32_t *cells) 111 { 112 uint32_t phandle = cells[0]; 113 int node, ncells; 114 115 node = OF_getnodebyphandle(phandle); 116 if (node == 0) 117 return NULL; 118 119 ncells = OF_getpropint(node, "#clock-cells", 0); 120 return cells + ncells + 1; 121 } 122 123 int 124 clock_index(int node, const char *clock) 125 { 126 char *names; 127 char *name; 128 char *end; 129 int idx = 0; 130 int len; 131 132 if (clock == NULL) 133 return 0; 134 135 len = OF_getproplen(node, "clock-names"); 136 if (len <= 0) 137 return -1; 138 139 names = malloc(len, M_TEMP, M_WAITOK); 140 OF_getprop(node, "clock-names", names, len); 141 end = names + len; 142 name = names; 143 while (name < end) { 144 if (strcmp(name, clock) == 0) { 145 free(names, M_TEMP, len); 146 return idx; 147 } 148 name += strlen(name) + 1; 149 idx++; 150 } 151 free(names, M_TEMP, len); 152 return -1; 153 } 154 155 uint32_t 156 clock_get_frequency_idx(int node, int idx) 157 { 158 uint32_t *clocks; 159 uint32_t *clock; 160 uint32_t freq = 0; 161 int len; 162 163 len = OF_getproplen(node, "clocks"); 164 if (len <= 0) 165 return 0; 166 167 clocks = malloc(len, M_TEMP, M_WAITOK); 168 OF_getpropintarray(node, "clocks", clocks, len); 169 170 clock = clocks; 171 while (clock && clock < clocks + (len / sizeof(uint32_t))) { 172 if (idx == 0) { 173 freq = clock_get_frequency_cells(clock); 174 break; 175 } 176 clock = clock_next_clock(clock); 177 idx--; 178 } 179 180 free(clocks, M_TEMP, len); 181 return freq; 182 } 183 184 uint32_t 185 clock_get_frequency(int node, const char *name) 186 { 187 int idx; 188 189 idx = clock_index(node, name); 190 if (idx == -1) 191 return 0; 192 193 return clock_get_frequency_idx(node, idx); 194 } 195 196 int 197 clock_set_frequency_idx(int node, int idx, uint32_t freq) 198 { 199 uint32_t *clocks; 200 uint32_t *clock; 201 int rv = -1; 202 int len; 203 204 len = OF_getproplen(node, "clocks"); 205 if (len <= 0) 206 return -1; 207 208 clocks = malloc(len, M_TEMP, M_WAITOK); 209 OF_getpropintarray(node, "clocks", clocks, len); 210 211 clock = clocks; 212 while (clock && clock < clocks + (len / sizeof(uint32_t))) { 213 if (idx == 0) { 214 rv = clock_set_frequency_cells(clock, freq); 215 break; 216 } 217 clock = clock_next_clock(clock); 218 idx--; 219 } 220 221 free(clocks, M_TEMP, len); 222 return rv; 223 } 224 225 int 226 clock_set_frequency(int node, const char *name, uint32_t freq) 227 { 228 int idx; 229 230 idx = clock_index(node, name); 231 if (idx == -1) 232 return -1; 233 234 return clock_set_frequency_idx(node, idx, freq); 235 } 236 237 void 238 clock_do_enable_idx(int node, int idx, int on) 239 { 240 uint32_t *clocks; 241 uint32_t *clock; 242 int len; 243 244 len = OF_getproplen(node, "clocks"); 245 if (len <= 0) 246 return; 247 248 clocks = malloc(len, M_TEMP, M_WAITOK); 249 OF_getpropintarray(node, "clocks", clocks, len); 250 251 clock = clocks; 252 while (clock && clock < clocks + (len / sizeof(uint32_t))) { 253 if (idx <= 0) 254 clock_enable_cells(clock, on); 255 if (idx == 0) 256 break; 257 clock = clock_next_clock(clock); 258 idx--; 259 } 260 261 free(clocks, M_TEMP, len); 262 } 263 264 void 265 clock_do_enable(int node, const char *name, int on) 266 { 267 int idx; 268 269 idx = clock_index(node, name); 270 if (idx == -1) 271 return; 272 273 clock_do_enable_idx(node, idx, on); 274 } 275 276 void 277 clock_enable_idx(int node, int idx) 278 { 279 clock_do_enable_idx(node, idx, 1); 280 } 281 282 void 283 clock_enable(int node, const char *name) 284 { 285 clock_do_enable(node, name, 1); 286 } 287 288 void 289 clock_disable_idx(int node, int idx) 290 { 291 clock_do_enable_idx(node, idx, 0); 292 } 293 294 void 295 clock_disable(int node, const char *name) 296 { 297 clock_do_enable(node, name, 0); 298 } 299 300 /* 301 * Reset functionality. 302 */ 303 304 LIST_HEAD(, reset_device) reset_devices = 305 LIST_HEAD_INITIALIZER(reset_devices); 306 307 void 308 reset_register(struct reset_device *rd) 309 { 310 rd->rd_cells = OF_getpropint(rd->rd_node, "#reset-cells", 0); 311 rd->rd_phandle = OF_getpropint(rd->rd_node, "phandle", 0); 312 if (rd->rd_phandle == 0) 313 return; 314 315 LIST_INSERT_HEAD(&reset_devices, rd, rd_list); 316 } 317 318 void 319 reset_assert_cells(uint32_t *cells, int assert) 320 { 321 struct reset_device *rd; 322 uint32_t phandle = cells[0]; 323 324 LIST_FOREACH(rd, &reset_devices, rd_list) { 325 if (rd->rd_phandle == phandle) 326 break; 327 } 328 329 if (rd && rd->rd_reset) 330 rd->rd_reset(rd->rd_cookie, &cells[1], assert); 331 } 332 333 uint32_t * 334 reset_next_reset(uint32_t *cells) 335 { 336 uint32_t phandle = cells[0]; 337 int node, ncells; 338 339 node = OF_getnodebyphandle(phandle); 340 if (node == 0) 341 return NULL; 342 343 ncells = OF_getpropint(node, "#reset-cells", 0); 344 return cells + ncells + 1; 345 } 346 347 int 348 reset_index(int node, const char *reset) 349 { 350 char *names; 351 char *name; 352 char *end; 353 int idx = 0; 354 int len; 355 356 if (reset == NULL) 357 return 0; 358 359 len = OF_getproplen(node, "reset-names"); 360 if (len <= 0) 361 return -1; 362 363 names = malloc(len, M_TEMP, M_WAITOK); 364 OF_getprop(node, "reset-names", names, len); 365 end = names + len; 366 name = names; 367 while (name < end) { 368 if (strcmp(name, reset) == 0) { 369 free(names, M_TEMP, len); 370 return idx; 371 } 372 name += strlen(name) + 1; 373 idx++; 374 } 375 free(names, M_TEMP, len); 376 return -1; 377 } 378 379 void 380 reset_do_assert_idx(int node, int idx, int assert) 381 { 382 uint32_t *resets; 383 uint32_t *reset; 384 int len; 385 386 len = OF_getproplen(node, "resets"); 387 if (len <= 0) 388 return; 389 390 resets = malloc(len, M_TEMP, M_WAITOK); 391 OF_getpropintarray(node, "resets", resets, len); 392 393 reset = resets; 394 while (reset && reset < resets + (len / sizeof(uint32_t))) { 395 if (idx <= 0) 396 reset_assert_cells(reset, assert); 397 if (idx == 0) 398 break; 399 reset = reset_next_reset(reset); 400 idx--; 401 } 402 403 free(resets, M_TEMP, len); 404 } 405 406 void 407 reset_do_assert(int node, const char *name, int assert) 408 { 409 int idx; 410 411 idx = reset_index(node, name); 412 if (idx == -1) 413 return; 414 415 reset_do_assert_idx(node, idx, assert); 416 } 417 418 void 419 reset_assert_idx(int node, int idx) 420 { 421 reset_do_assert_idx(node, idx, 1); 422 } 423 424 void 425 reset_assert(int node, const char *name) 426 { 427 reset_do_assert(node, name, 1); 428 } 429 430 void 431 reset_deassert_idx(int node, int idx) 432 { 433 reset_do_assert_idx(node, idx, 0); 434 } 435 436 void 437 reset_deassert(int node, const char *name) 438 { 439 reset_do_assert(node, name, 0); 440 } 441