1*47f97eb3Sjmcneill /* $NetBSD: fdt_clock.c,v 1.10 2019/11/09 23:28:26 jmcneill Exp $ */
2fda513fdSjmcneill
3fda513fdSjmcneill /*-
4fda513fdSjmcneill * Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca>
5fda513fdSjmcneill * All rights reserved.
6fda513fdSjmcneill *
7fda513fdSjmcneill * Redistribution and use in source and binary forms, with or without
8fda513fdSjmcneill * modification, are permitted provided that the following conditions
9fda513fdSjmcneill * are met:
10fda513fdSjmcneill * 1. Redistributions of source code must retain the above copyright
11fda513fdSjmcneill * notice, this list of conditions and the following disclaimer.
12fda513fdSjmcneill * 2. Redistributions in binary form must reproduce the above copyright
13fda513fdSjmcneill * notice, this list of conditions and the following disclaimer in the
14fda513fdSjmcneill * documentation and/or other materials provided with the distribution.
15fda513fdSjmcneill *
16fda513fdSjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17fda513fdSjmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18fda513fdSjmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19fda513fdSjmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20fda513fdSjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21fda513fdSjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22fda513fdSjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23fda513fdSjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24fda513fdSjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25fda513fdSjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26fda513fdSjmcneill * SUCH DAMAGE.
27fda513fdSjmcneill */
28fda513fdSjmcneill
29fda513fdSjmcneill #include <sys/cdefs.h>
30*47f97eb3Sjmcneill __KERNEL_RCSID(0, "$NetBSD: fdt_clock.c,v 1.10 2019/11/09 23:28:26 jmcneill Exp $");
31fda513fdSjmcneill
32fda513fdSjmcneill #include <sys/param.h>
33fda513fdSjmcneill #include <sys/bus.h>
34fda513fdSjmcneill #include <sys/kmem.h>
35233c5d85Sjmcneill #include <sys/queue.h>
36fda513fdSjmcneill
37fda513fdSjmcneill #include <libfdt.h>
38fda513fdSjmcneill #include <dev/fdt/fdtvar.h>
39fda513fdSjmcneill
408baf3a21Sjmcneill #include <dev/clk/clk_backend.h>
418baf3a21Sjmcneill
42fda513fdSjmcneill struct fdtbus_clock_controller {
43fda513fdSjmcneill device_t cc_dev;
44fda513fdSjmcneill int cc_phandle;
45fda513fdSjmcneill const struct fdtbus_clock_controller_func *cc_funcs;
46fda513fdSjmcneill
47233c5d85Sjmcneill LIST_ENTRY(fdtbus_clock_controller) cc_next;
48fda513fdSjmcneill };
49fda513fdSjmcneill
50233c5d85Sjmcneill static LIST_HEAD(, fdtbus_clock_controller) fdtbus_clock_controllers =
51233c5d85Sjmcneill LIST_HEAD_INITIALIZER(fdtbus_clock_controller);
52fda513fdSjmcneill
53fda513fdSjmcneill int
fdtbus_register_clock_controller(device_t dev,int phandle,const struct fdtbus_clock_controller_func * funcs)54fda513fdSjmcneill fdtbus_register_clock_controller(device_t dev, int phandle,
55fda513fdSjmcneill const struct fdtbus_clock_controller_func *funcs)
56fda513fdSjmcneill {
57fda513fdSjmcneill struct fdtbus_clock_controller *cc;
58fda513fdSjmcneill
59fda513fdSjmcneill cc = kmem_alloc(sizeof(*cc), KM_SLEEP);
60fda513fdSjmcneill cc->cc_dev = dev;
61fda513fdSjmcneill cc->cc_phandle = phandle;
62fda513fdSjmcneill cc->cc_funcs = funcs;
63fda513fdSjmcneill
64233c5d85Sjmcneill LIST_INSERT_HEAD(&fdtbus_clock_controllers, cc, cc_next);
65fda513fdSjmcneill
668baf3a21Sjmcneill fdtbus_clock_assign(phandle);
678baf3a21Sjmcneill
68fda513fdSjmcneill return 0;
69fda513fdSjmcneill }
70fda513fdSjmcneill
71fda513fdSjmcneill static struct fdtbus_clock_controller *
fdtbus_get_clock_controller(int phandle)72fda513fdSjmcneill fdtbus_get_clock_controller(int phandle)
73fda513fdSjmcneill {
74fda513fdSjmcneill struct fdtbus_clock_controller *cc;
75fda513fdSjmcneill
76233c5d85Sjmcneill LIST_FOREACH(cc, &fdtbus_clock_controllers, cc_next) {
77233c5d85Sjmcneill if (cc->cc_phandle == phandle)
78fda513fdSjmcneill return cc;
79fda513fdSjmcneill }
80fda513fdSjmcneill
81fda513fdSjmcneill return NULL;
82fda513fdSjmcneill }
83fda513fdSjmcneill
848baf3a21Sjmcneill static struct clk *
fdtbus_clock_get_index_prop(int phandle,u_int index,const char * prop)858baf3a21Sjmcneill fdtbus_clock_get_index_prop(int phandle, u_int index, const char *prop)
86fda513fdSjmcneill {
87fda513fdSjmcneill struct fdtbus_clock_controller *cc;
88fda513fdSjmcneill struct clk *clk = NULL;
898baf3a21Sjmcneill const u_int *p;
90fda513fdSjmcneill u_int n, clock_cells;
91fda513fdSjmcneill int len, resid;
92fda513fdSjmcneill
938baf3a21Sjmcneill p = fdtbus_get_prop(phandle, prop, &len);
948baf3a21Sjmcneill if (p == NULL)
95fda513fdSjmcneill return NULL;
96fda513fdSjmcneill
97fda513fdSjmcneill for (n = 0, resid = len; resid > 0; n++) {
98fda513fdSjmcneill const int cc_phandle =
99fda513fdSjmcneill fdtbus_get_phandle_from_native(be32toh(p[0]));
100fda513fdSjmcneill if (of_getprop_uint32(cc_phandle, "#clock-cells", &clock_cells))
101fda513fdSjmcneill break;
102fda513fdSjmcneill if (n == index) {
103fda513fdSjmcneill cc = fdtbus_get_clock_controller(cc_phandle);
104fda513fdSjmcneill if (cc == NULL)
1058baf3a21Sjmcneill break;
10651b425efSaymeric clk = cc->cc_funcs->decode(cc->cc_dev, cc_phandle,
107fda513fdSjmcneill clock_cells > 0 ? &p[1] : NULL, clock_cells * 4);
108fda513fdSjmcneill break;
109fda513fdSjmcneill }
110fda513fdSjmcneill resid -= (clock_cells + 1) * 4;
111fda513fdSjmcneill p += clock_cells + 1;
112fda513fdSjmcneill }
113fda513fdSjmcneill
114fda513fdSjmcneill return clk;
115fda513fdSjmcneill }
116fda513fdSjmcneill
117fda513fdSjmcneill struct clk *
fdtbus_clock_get_index(int phandle,u_int index)1188baf3a21Sjmcneill fdtbus_clock_get_index(int phandle, u_int index)
1198baf3a21Sjmcneill {
1208baf3a21Sjmcneill return fdtbus_clock_get_index_prop(phandle, index, "clocks");
1218baf3a21Sjmcneill }
1228baf3a21Sjmcneill
1238baf3a21Sjmcneill static struct clk *
fdtbus_clock_get_prop(int phandle,const char * clkname,const char * prop)1248baf3a21Sjmcneill fdtbus_clock_get_prop(int phandle, const char *clkname, const char *prop)
125fda513fdSjmcneill {
126fda513fdSjmcneill u_int index;
12721aceb10Sjakllsch int err;
128fda513fdSjmcneill
12921aceb10Sjakllsch err = fdtbus_get_index(phandle, prop, clkname, &index);
13021aceb10Sjakllsch if (err != 0)
131fda513fdSjmcneill return NULL;
132fda513fdSjmcneill
13321aceb10Sjakllsch return fdtbus_clock_get_index(phandle, index);
134fda513fdSjmcneill }
1359a2e3e05Sjmcneill
1368fc8ad9aSjmcneill u_int
fdtbus_clock_count(int phandle,const char * prop)1378fc8ad9aSjmcneill fdtbus_clock_count(int phandle, const char *prop)
1388baf3a21Sjmcneill {
1398baf3a21Sjmcneill u_int n, clock_cells;
1408baf3a21Sjmcneill int len, resid;
1418baf3a21Sjmcneill
1428baf3a21Sjmcneill const u_int *p = fdtbus_get_prop(phandle, prop, &len);
1438baf3a21Sjmcneill if (p == NULL)
1448baf3a21Sjmcneill return 0;
1458baf3a21Sjmcneill
1468baf3a21Sjmcneill for (n = 0, resid = len; resid > 0; n++) {
1478baf3a21Sjmcneill const int cc_phandle =
1488baf3a21Sjmcneill fdtbus_get_phandle_from_native(be32toh(p[0]));
1498baf3a21Sjmcneill if (of_getprop_uint32(cc_phandle, "#clock-cells", &clock_cells))
1508baf3a21Sjmcneill break;
1518baf3a21Sjmcneill resid -= (clock_cells + 1) * 4;
1528baf3a21Sjmcneill p += clock_cells + 1;
1538baf3a21Sjmcneill }
1548baf3a21Sjmcneill
1558baf3a21Sjmcneill return n;
1568baf3a21Sjmcneill }
1578baf3a21Sjmcneill
1588baf3a21Sjmcneill struct clk *
fdtbus_clock_get(int phandle,const char * clkname)1598baf3a21Sjmcneill fdtbus_clock_get(int phandle, const char *clkname)
1608baf3a21Sjmcneill {
1618baf3a21Sjmcneill return fdtbus_clock_get_prop(phandle, clkname, "clock-names");
1628baf3a21Sjmcneill }
1638baf3a21Sjmcneill
164*47f97eb3Sjmcneill int
fdtbus_clock_enable(int phandle,const char * clkname,bool required)165*47f97eb3Sjmcneill fdtbus_clock_enable(int phandle, const char *clkname, bool required)
166*47f97eb3Sjmcneill {
167*47f97eb3Sjmcneill struct clk *clk;
168*47f97eb3Sjmcneill
169*47f97eb3Sjmcneill clk = fdtbus_clock_get(phandle, clkname);
170*47f97eb3Sjmcneill if (clk == NULL)
171*47f97eb3Sjmcneill return required ? ENOENT : 0;
172*47f97eb3Sjmcneill
173*47f97eb3Sjmcneill return clk_enable(clk);
174*47f97eb3Sjmcneill }
175*47f97eb3Sjmcneill
176*47f97eb3Sjmcneill int
fdtbus_clock_enable_index(int phandle,u_int index,bool required)177*47f97eb3Sjmcneill fdtbus_clock_enable_index(int phandle, u_int index, bool required)
178*47f97eb3Sjmcneill {
179*47f97eb3Sjmcneill struct clk *clk;
180*47f97eb3Sjmcneill
181*47f97eb3Sjmcneill clk = fdtbus_clock_get_index(phandle, index);
182*47f97eb3Sjmcneill if (clk == NULL)
183*47f97eb3Sjmcneill return required ? ENOENT : 0;
184*47f97eb3Sjmcneill
185*47f97eb3Sjmcneill return clk_enable(clk);
186*47f97eb3Sjmcneill }
187*47f97eb3Sjmcneill
1889a2e3e05Sjmcneill /*
1899a2e3e05Sjmcneill * Search the DT for a clock by "clock-output-names" property.
1909a2e3e05Sjmcneill *
1919a2e3e05Sjmcneill * This should only be used by clk backends. Not for use by ordinary
1929a2e3e05Sjmcneill * clock consumers!
1939a2e3e05Sjmcneill */
1949a2e3e05Sjmcneill struct clk *
fdtbus_clock_byname(const char * clkname)1959a2e3e05Sjmcneill fdtbus_clock_byname(const char *clkname)
1969a2e3e05Sjmcneill {
1979a2e3e05Sjmcneill struct fdtbus_clock_controller *cc;
19821aceb10Sjakllsch u_int index, clock_cells;
19921aceb10Sjakllsch int err;
2009a2e3e05Sjmcneill
201233c5d85Sjmcneill LIST_FOREACH(cc, &fdtbus_clock_controllers, cc_next) {
20221aceb10Sjakllsch err = fdtbus_get_index(cc->cc_phandle, "clock-output-names", clkname, &index);
20321aceb10Sjakllsch if (err != 0)
2049a2e3e05Sjmcneill continue;
2059a2e3e05Sjmcneill if (of_getprop_uint32(cc->cc_phandle, "#clock-cells", &clock_cells))
20621aceb10Sjakllsch continue;
2079a2e3e05Sjmcneill const u_int index_raw = htobe32(index);
2089a2e3e05Sjmcneill return cc->cc_funcs->decode(cc->cc_dev,
20951b425efSaymeric cc->cc_phandle,
2109a2e3e05Sjmcneill clock_cells > 0 ? &index_raw : NULL,
2119a2e3e05Sjmcneill clock_cells > 0 ? 4 : 0);
2129a2e3e05Sjmcneill }
2139a2e3e05Sjmcneill
2149a2e3e05Sjmcneill return NULL;
2159a2e3e05Sjmcneill }
2168baf3a21Sjmcneill
2178baf3a21Sjmcneill /*
2188baf3a21Sjmcneill * Apply assigned clock parents and rates.
2198baf3a21Sjmcneill *
2208baf3a21Sjmcneill * This is automatically called by fdtbus_register_clock_controller, so clock
2218baf3a21Sjmcneill * drivers likely don't need to call this directly.
2228baf3a21Sjmcneill */
2238baf3a21Sjmcneill void
fdtbus_clock_assign(int phandle)2248baf3a21Sjmcneill fdtbus_clock_assign(int phandle)
2258baf3a21Sjmcneill {
2268baf3a21Sjmcneill u_int index, rates_len;
2278baf3a21Sjmcneill struct clk *clk, *clk_parent;
2288baf3a21Sjmcneill int error;
2298baf3a21Sjmcneill
2308baf3a21Sjmcneill const u_int *rates = fdtbus_get_prop(phandle, "assigned-clock-rates", &rates_len);
2318baf3a21Sjmcneill if (rates == NULL)
2328baf3a21Sjmcneill rates_len = 0;
2338baf3a21Sjmcneill
2348fc8ad9aSjmcneill const u_int nclocks = fdtbus_clock_count(phandle, "assigned-clocks");
2358fc8ad9aSjmcneill const u_int nparents = fdtbus_clock_count(phandle, "assigned-clock-parents");
2368be04e5fSjmcneill const u_int nrates = rates_len / sizeof(*rates);
2378baf3a21Sjmcneill
2388baf3a21Sjmcneill for (index = 0; index < nclocks; index++) {
2398baf3a21Sjmcneill clk = fdtbus_clock_get_index_prop(phandle, index, "assigned-clocks");
2408baf3a21Sjmcneill if (clk == NULL) {
2418baf3a21Sjmcneill aprint_debug("clk: assigned clock (%u) not found, skipping...\n", index);
2428baf3a21Sjmcneill continue;
2438baf3a21Sjmcneill }
2448baf3a21Sjmcneill
2458be04e5fSjmcneill if (index < nparents) {
2468baf3a21Sjmcneill clk_parent = fdtbus_clock_get_index_prop(phandle, index, "assigned-clock-parents");
2478baf3a21Sjmcneill if (clk_parent != NULL) {
2488baf3a21Sjmcneill error = clk_set_parent(clk, clk_parent);
2498baf3a21Sjmcneill if (error != 0) {
2508baf3a21Sjmcneill aprint_error("clk: failed to set %s parent to %s, error %d\n",
2518baf3a21Sjmcneill clk->name, clk_parent->name, error);
2528baf3a21Sjmcneill }
2538be04e5fSjmcneill } else {
2548be04e5fSjmcneill aprint_debug("clk: failed to set %s parent (not found)\n", clk->name);
2558be04e5fSjmcneill }
2568baf3a21Sjmcneill }
2578baf3a21Sjmcneill
2588be04e5fSjmcneill if (index < nrates) {
2598be04e5fSjmcneill const u_int rate = be32toh(rates[index]);
2608baf3a21Sjmcneill if (rate != 0) {
2618baf3a21Sjmcneill error = clk_set_rate(clk, rate);
2628baf3a21Sjmcneill if (error != 0)
2638baf3a21Sjmcneill aprint_error("clk: failed to set %s rate to %u Hz, error %d\n",
2648baf3a21Sjmcneill clk->name, rate, error);
2658baf3a21Sjmcneill }
2668baf3a21Sjmcneill }
2678baf3a21Sjmcneill }
2688baf3a21Sjmcneill }
269