diff options
author | Mark Kettenis <kettenis@cvs.openbsd.org> | 2016-08-21 21:38:06 +0000 |
---|---|---|
committer | Mark Kettenis <kettenis@cvs.openbsd.org> | 2016-08-21 21:38:06 +0000 |
commit | a0f71046dd0d839a69cbbf1269afa7927c66e7a7 (patch) | |
tree | 10ebe07b9087454b000c4e26500953282d7bf41a /sys/dev/ofw | |
parent | 6c3d9ab86c837a3650ecb62aaee55a9bf5a7b1a6 (diff) |
Add a minimal clock "framework". Build it on armv7.
ok patrick@
Diffstat (limited to 'sys/dev/ofw')
-rw-r--r-- | sys/dev/ofw/ofw_clock.c | 212 | ||||
-rw-r--r-- | sys/dev/ofw/ofw_clock.h | 39 |
2 files changed, 251 insertions, 0 deletions
diff --git a/sys/dev/ofw/ofw_clock.c b/sys/dev/ofw/ofw_clock.c new file mode 100644 index 00000000000..0159d48a84c --- /dev/null +++ b/sys/dev/ofw/ofw_clock.c @@ -0,0 +1,212 @@ +/* $OpenBSD: ofw_clock.c,v 1.1 2016/08/21 21:38:05 kettenis Exp $ */ +/* + * Copyright (c) 2016 Mark Kettenis + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/systm.h> +#include <sys/malloc.h> + +#include <dev/ofw/openfirm.h> +#include <dev/ofw/ofw_clock.h> + +LIST_HEAD(, clock_device) clock_devices = + LIST_HEAD_INITIALIZER(clock_devices); + +void +clock_register(struct clock_device *cd) +{ + cd->cd_cells = OF_getpropint(cd->cd_node, "#clock-cells", 0); + cd->cd_phandle = OF_getpropint(cd->cd_node, "phandle", 0); + if (cd->cd_phandle == 0) + return; + + LIST_INSERT_HEAD(&clock_devices, cd, cd_list); +} + +uint32_t +clock_get_frequency_cells(uint32_t *cells) +{ + struct clock_device *cd; + uint32_t phandle = cells[0]; + int node; + + LIST_FOREACH(cd, &clock_devices, cd_list) { + if (cd->cd_phandle == phandle) + break; + } + + if (cd && cd->cd_get_frequency) + return cd->cd_get_frequency(cd->cd_cookie, &cells[1]); + + node = OF_getnodebyphandle(phandle); + if (node == 0) + return 0; + + if (OF_is_compatible(node, "fixed-clock")) + return OF_getpropint(node, "clock-frequency", 0); + + if (OF_is_compatible(node, "fixed-factor-clock")) { + uint32_t mult, div, freq; + + mult = OF_getpropint(node, "clock-mult", 1); + div = OF_getpropint(node, "clock-div", 1); + freq = clock_get_frequency(node, NULL); + return (freq * mult) / div; + } + + return 0; +} + +void +clock_enable_cells(uint32_t *cells) +{ + struct clock_device *cd; + uint32_t phandle = cells[0]; + + LIST_FOREACH(cd, &clock_devices, cd_list) { + if (cd->cd_phandle == phandle) + break; + } + + if (cd && cd->cd_enable) + cd->cd_enable(cd->cd_cookie, &cells[1], 1); +} + +uint32_t * +clock_next_clock(uint32_t *cells) +{ + uint32_t phandle = cells[0]; + int node, ncells; + + node = OF_getnodebyphandle(phandle); + if (node == 0) + return NULL; + + ncells = OF_getpropint(node, "#clock-cells", 0); + return cells + ncells + 1; +} + +int +clock_index(int node, const char *clock) +{ + char *names; + char *name; + char *end; + int idx = 0; + int len; + + if (clock == NULL) + return 0; + + len = OF_getproplen(node, "clock-names"); + if (len <= 0) + return -1; + + names = malloc(len, M_TEMP, M_WAITOK); + OF_getprop(node, "clock-names", names, len); + end = names + len; + name = names; + while (name < end) { + if (strcmp(name, clock) == 0) { + free(names, M_TEMP, len); + return idx; + } + name += strlen(name) + 1; + idx++; + } + free(names, M_TEMP, len); + return -1; +} + +uint32_t +clock_get_frequency_idx(int node, int idx) +{ + uint32_t *clocks; + uint32_t *clock; + uint32_t freq = 0; + int len; + + len = OF_getproplen(node, "clocks"); + if (len <= 0) + return 0; + + clocks = malloc(len, M_TEMP, M_WAITOK); + OF_getpropintarray(node, "clocks", clocks, len); + + clock = clocks; + while (clock && clock < clocks + (len / sizeof(uint32_t))) { + if (idx == 0) { + freq = clock_get_frequency_cells(clock); + break; + } + clock = clock_next_clock(clock); + idx--; + } + + free(clocks, M_TEMP, len); + return freq; +} + +uint32_t +clock_get_frequency(int node, const char *name) +{ + int idx; + + idx = clock_index(node, name); + if (idx == -1) + return 0; + + return clock_get_frequency_idx(node, idx); +} + +void +clock_enable_idx(int node, int idx) +{ + uint32_t *clocks; + uint32_t *clock; + int len; + + len = OF_getproplen(node, "clocks"); + if (len <= 0) + return; + + clocks = malloc(len, M_TEMP, M_WAITOK); + OF_getpropintarray(node, "clocks", clocks, len); + + clock = clocks; + while (clock && clock < clocks + (len / sizeof(uint32_t))) { + if (idx == 0) { + clock_enable_cells(clock); + break; + } + clock = clock_next_clock(clock); + idx--; + } + + free(clocks, M_TEMP, len); +} + +void +clock_enable(int node, const char *name) +{ + int idx; + + idx = clock_index(node, name); + if (idx == -1) + return; + + clock_enable_idx(node, idx); +} diff --git a/sys/dev/ofw/ofw_clock.h b/sys/dev/ofw/ofw_clock.h new file mode 100644 index 00000000000..eaa4143453c --- /dev/null +++ b/sys/dev/ofw/ofw_clock.h @@ -0,0 +1,39 @@ +/* $OpenBSD: ofw_clock.h,v 1.1 2016/08/21 21:38:05 kettenis Exp $ */ +/* + * Copyright (c) 2016 Mark Kettenis + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _DEV_OFW_CLOCK_H_ +#define _DEV_OFW_CLOCK_H_ + +struct clock_device { + int cd_node; + void *cd_cookie; + uint32_t (*cd_get_frequency)(void *, uint32_t *); + void (*cd_enable)(void *, uint32_t *, int); + + LIST_ENTRY(clock_device) cd_list; + uint32_t cd_phandle; + uint32_t cd_cells; +}; + +void clock_register(struct clock_device *); + +uint32_t clock_get_frequency(int, const char *); +uint32_t clock_get_frequency_idx(int, int); +void clock_enable(int, const char *); +void clock_enable_idx(int, int); + +#endif /* _DEV_OFW_CLOCK_H_ */ |