ofw_clock.c revision 1.4
1/* $OpenBSD: ofw_clock.c,v 1.4 2016/08/22 19:28:27 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 29LIST_HEAD(, clock_device) clock_devices = 30 LIST_HEAD_INITIALIZER(clock_devices); 31 32void 33clock_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 43uint32_t 44clock_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 77int 78clock_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 94void 95clock_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 109uint32_t * 110clock_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 123int 124clock_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 155uint32_t 156clock_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 184uint32_t 185clock_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 196int 197clock_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 0; 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 225int 226clock_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 0; 233 234 return clock_set_frequency_idx(node, idx, freq); 235} 236 237void 238clock_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 264void 265clock_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 276void 277clock_enable_idx(int node, int idx) 278{ 279 clock_do_enable_idx(node, idx, 1); 280} 281 282void 283clock_enable(int node, const char *name) 284{ 285 clock_do_enable(node, name, 1); 286} 287 288void 289clock_disable_idx(int node, int idx) 290{ 291 clock_do_enable_idx(node, idx, 0); 292} 293 294void 295clock_disable(int node, const char *name) 296{ 297 clock_do_enable(node, name, 0); 298} 299 300/* 301 * Reset functionality. 302 */ 303 304LIST_HEAD(, reset_device) reset_devices = 305 LIST_HEAD_INITIALIZER(reset_devices); 306 307void 308reset_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 318void 319reset_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 rd->rd_reset(rd->rd_cookie, &cells[1], assert); 330} 331 332uint32_t * 333reset_next_reset(uint32_t *cells) 334{ 335 uint32_t phandle = cells[0]; 336 int node, ncells; 337 338 node = OF_getnodebyphandle(phandle); 339 if (node == 0) 340 return NULL; 341 342 ncells = OF_getpropint(node, "#reset-cells", 0); 343 return cells + ncells + 1; 344} 345 346int 347reset_index(int node, const char *reset) 348{ 349 char *names; 350 char *name; 351 char *end; 352 int idx = 0; 353 int len; 354 355 if (reset == NULL) 356 return 0; 357 358 len = OF_getproplen(node, "reset-names"); 359 if (len <= 0) 360 return -1; 361 362 names = malloc(len, M_TEMP, M_WAITOK); 363 OF_getprop(node, "reset-names", names, len); 364 end = names + len; 365 name = names; 366 while (name < end) { 367 if (strcmp(name, reset) == 0) { 368 free(names, M_TEMP, len); 369 return idx; 370 } 371 name += strlen(name) + 1; 372 idx++; 373 } 374 free(names, M_TEMP, len); 375 return -1; 376} 377 378void 379reset_do_assert_idx(int node, int idx, int assert) 380{ 381 uint32_t *resets; 382 uint32_t *reset; 383 int len; 384 385 len = OF_getproplen(node, "resets"); 386 if (len <= 0) 387 return; 388 389 resets = malloc(len, M_TEMP, M_WAITOK); 390 OF_getpropintarray(node, "resets", resets, len); 391 392 reset = resets; 393 while (reset && resets < resets + (len / sizeof(uint32_t))) { 394 if (idx <= 0) 395 reset_assert_cells(reset, assert); 396 if (idx == 0) 397 break; 398 reset = reset_next_reset(reset); 399 idx--; 400 } 401 402 free(resets, M_TEMP, len); 403} 404 405void 406reset_do_assert(int node, const char *name, int assert) 407{ 408 int idx; 409 410 idx = reset_index(node, name); 411 if (idx == -1) 412 return; 413 414 reset_do_assert_idx(node, idx, assert); 415} 416 417void 418reset_assert(int node, const char *name) 419{ 420 reset_do_assert(node, name, 1); 421} 422 423void 424reset_deassert(int node, const char *name) 425{ 426 reset_do_assert(node, name, 0); 427} 428