Home | History | Annotate | Line # | Download | only in fdt
fdt_clock.c revision 1.8.4.2
      1  1.8.4.2    martin /* $NetBSD: fdt_clock.c,v 1.8.4.2 2019/11/27 13:46:45 martin Exp $ */
      2      1.1  jmcneill 
      3      1.1  jmcneill /*-
      4      1.1  jmcneill  * Copyright (c) 2015 Jared D. McNeill <jmcneill (at) invisible.ca>
      5      1.1  jmcneill  * All rights reserved.
      6      1.1  jmcneill  *
      7      1.1  jmcneill  * Redistribution and use in source and binary forms, with or without
      8      1.1  jmcneill  * modification, are permitted provided that the following conditions
      9      1.1  jmcneill  * are met:
     10      1.1  jmcneill  * 1. Redistributions of source code must retain the above copyright
     11      1.1  jmcneill  *    notice, this list of conditions and the following disclaimer.
     12      1.1  jmcneill  * 2. Redistributions in binary form must reproduce the above copyright
     13      1.1  jmcneill  *    notice, this list of conditions and the following disclaimer in the
     14      1.1  jmcneill  *    documentation and/or other materials provided with the distribution.
     15      1.1  jmcneill  *
     16      1.1  jmcneill  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17      1.1  jmcneill  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     18      1.1  jmcneill  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     19      1.1  jmcneill  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     20      1.1  jmcneill  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     21      1.1  jmcneill  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     22      1.1  jmcneill  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     23      1.1  jmcneill  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     24      1.1  jmcneill  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25      1.1  jmcneill  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26      1.1  jmcneill  * SUCH DAMAGE.
     27      1.1  jmcneill  */
     28      1.1  jmcneill 
     29      1.1  jmcneill #include <sys/cdefs.h>
     30  1.8.4.2    martin __KERNEL_RCSID(0, "$NetBSD: fdt_clock.c,v 1.8.4.2 2019/11/27 13:46:45 martin Exp $");
     31      1.1  jmcneill 
     32      1.1  jmcneill #include <sys/param.h>
     33      1.1  jmcneill #include <sys/bus.h>
     34      1.1  jmcneill #include <sys/kmem.h>
     35      1.5  jmcneill #include <sys/queue.h>
     36      1.1  jmcneill 
     37      1.1  jmcneill #include <libfdt.h>
     38      1.1  jmcneill #include <dev/fdt/fdtvar.h>
     39      1.1  jmcneill 
     40      1.3  jmcneill #include <dev/clk/clk_backend.h>
     41      1.3  jmcneill 
     42      1.1  jmcneill struct fdtbus_clock_controller {
     43      1.1  jmcneill 	device_t cc_dev;
     44      1.1  jmcneill 	int cc_phandle;
     45      1.1  jmcneill 	const struct fdtbus_clock_controller_func *cc_funcs;
     46      1.1  jmcneill 
     47      1.5  jmcneill 	LIST_ENTRY(fdtbus_clock_controller) cc_next;
     48      1.1  jmcneill };
     49      1.1  jmcneill 
     50      1.5  jmcneill static LIST_HEAD(, fdtbus_clock_controller) fdtbus_clock_controllers =
     51      1.5  jmcneill     LIST_HEAD_INITIALIZER(fdtbus_clock_controller);
     52      1.1  jmcneill 
     53      1.1  jmcneill int
     54      1.1  jmcneill fdtbus_register_clock_controller(device_t dev, int phandle,
     55      1.1  jmcneill     const struct fdtbus_clock_controller_func *funcs)
     56      1.1  jmcneill {
     57      1.1  jmcneill 	struct fdtbus_clock_controller *cc;
     58      1.1  jmcneill 
     59      1.1  jmcneill 	cc = kmem_alloc(sizeof(*cc), KM_SLEEP);
     60      1.1  jmcneill 	cc->cc_dev = dev;
     61      1.1  jmcneill 	cc->cc_phandle = phandle;
     62      1.1  jmcneill 	cc->cc_funcs = funcs;
     63      1.1  jmcneill 
     64      1.5  jmcneill 	LIST_INSERT_HEAD(&fdtbus_clock_controllers, cc, cc_next);
     65      1.1  jmcneill 
     66      1.3  jmcneill 	fdtbus_clock_assign(phandle);
     67      1.3  jmcneill 
     68      1.1  jmcneill 	return 0;
     69      1.1  jmcneill }
     70      1.1  jmcneill 
     71      1.1  jmcneill static struct fdtbus_clock_controller *
     72      1.1  jmcneill fdtbus_get_clock_controller(int phandle)
     73      1.1  jmcneill {
     74      1.1  jmcneill 	struct fdtbus_clock_controller *cc;
     75      1.1  jmcneill 
     76      1.5  jmcneill 	LIST_FOREACH(cc, &fdtbus_clock_controllers, cc_next) {
     77      1.5  jmcneill 		if (cc->cc_phandle == phandle)
     78      1.1  jmcneill 			return cc;
     79      1.1  jmcneill 	}
     80      1.1  jmcneill 
     81      1.1  jmcneill 	return NULL;
     82      1.1  jmcneill }
     83      1.1  jmcneill 
     84      1.3  jmcneill static struct clk *
     85      1.3  jmcneill fdtbus_clock_get_index_prop(int phandle, u_int index, const char *prop)
     86      1.1  jmcneill {
     87      1.1  jmcneill 	struct fdtbus_clock_controller *cc;
     88      1.1  jmcneill 	struct clk *clk = NULL;
     89      1.3  jmcneill 	const u_int *p;
     90      1.1  jmcneill 	u_int n, clock_cells;
     91      1.1  jmcneill 	int len, resid;
     92      1.1  jmcneill 
     93      1.3  jmcneill 	p = fdtbus_get_prop(phandle, prop, &len);
     94      1.3  jmcneill 	if (p == NULL)
     95      1.1  jmcneill 		return NULL;
     96      1.1  jmcneill 
     97      1.1  jmcneill 	for (n = 0, resid = len; resid > 0; n++) {
     98      1.1  jmcneill 		const int cc_phandle =
     99      1.1  jmcneill 		    fdtbus_get_phandle_from_native(be32toh(p[0]));
    100      1.1  jmcneill 		if (of_getprop_uint32(cc_phandle, "#clock-cells", &clock_cells))
    101      1.1  jmcneill 			break;
    102      1.1  jmcneill 		if (n == index) {
    103      1.1  jmcneill 			cc = fdtbus_get_clock_controller(cc_phandle);
    104      1.1  jmcneill 			if (cc == NULL)
    105      1.3  jmcneill 				break;
    106      1.6   aymeric 			clk = cc->cc_funcs->decode(cc->cc_dev, cc_phandle,
    107      1.1  jmcneill 			    clock_cells > 0 ? &p[1] : NULL, clock_cells * 4);
    108      1.1  jmcneill 			break;
    109      1.1  jmcneill 		}
    110      1.1  jmcneill 		resid -= (clock_cells + 1) * 4;
    111      1.1  jmcneill 		p += clock_cells + 1;
    112      1.1  jmcneill 	}
    113      1.1  jmcneill 
    114      1.1  jmcneill 	return clk;
    115      1.1  jmcneill }
    116      1.1  jmcneill 
    117      1.1  jmcneill struct clk *
    118      1.3  jmcneill fdtbus_clock_get_index(int phandle, u_int index)
    119      1.3  jmcneill {
    120      1.3  jmcneill 	return fdtbus_clock_get_index_prop(phandle, index, "clocks");
    121      1.3  jmcneill }
    122      1.3  jmcneill 
    123      1.3  jmcneill static struct clk *
    124      1.3  jmcneill fdtbus_clock_get_prop(int phandle, const char *clkname, const char *prop)
    125      1.1  jmcneill {
    126      1.1  jmcneill 	u_int index;
    127      1.8  jakllsch 	int err;
    128      1.1  jmcneill 
    129      1.8  jakllsch 	err = fdtbus_get_index(phandle, prop, clkname, &index);
    130      1.8  jakllsch 	if (err != 0)
    131      1.1  jmcneill 		return NULL;
    132      1.1  jmcneill 
    133      1.8  jakllsch 	return fdtbus_clock_get_index(phandle, index);
    134      1.3  jmcneill }
    135      1.1  jmcneill 
    136  1.8.4.2    martin u_int
    137  1.8.4.2    martin fdtbus_clock_count(int phandle, const char *prop)
    138      1.3  jmcneill {
    139      1.3  jmcneill 	u_int n, clock_cells;
    140      1.3  jmcneill 	int len, resid;
    141      1.3  jmcneill 
    142      1.3  jmcneill 	const u_int *p = fdtbus_get_prop(phandle, prop, &len);
    143      1.3  jmcneill 	if (p == NULL)
    144      1.3  jmcneill 		return 0;
    145      1.3  jmcneill 
    146      1.3  jmcneill 	for (n = 0, resid = len; resid > 0; n++) {
    147      1.3  jmcneill 		const int cc_phandle =
    148      1.3  jmcneill 		    fdtbus_get_phandle_from_native(be32toh(p[0]));
    149      1.3  jmcneill 		if (of_getprop_uint32(cc_phandle, "#clock-cells", &clock_cells))
    150      1.3  jmcneill 			break;
    151      1.3  jmcneill 		resid -= (clock_cells + 1) * 4;
    152      1.3  jmcneill 		p += clock_cells + 1;
    153      1.3  jmcneill 	}
    154      1.3  jmcneill 
    155      1.3  jmcneill 	return n;
    156      1.3  jmcneill }
    157      1.3  jmcneill 
    158      1.3  jmcneill struct clk *
    159      1.3  jmcneill fdtbus_clock_get(int phandle, const char *clkname)
    160      1.3  jmcneill {
    161      1.3  jmcneill 	return fdtbus_clock_get_prop(phandle, clkname, "clock-names");
    162      1.1  jmcneill }
    163      1.2  jmcneill 
    164  1.8.4.1    martin int
    165  1.8.4.1    martin fdtbus_clock_enable(int phandle, const char *clkname, bool required)
    166  1.8.4.1    martin {
    167  1.8.4.1    martin 	struct clk *clk;
    168  1.8.4.1    martin 
    169  1.8.4.1    martin 	clk = fdtbus_clock_get(phandle, clkname);
    170  1.8.4.1    martin 	if (clk == NULL)
    171  1.8.4.1    martin 		return required ? ENOENT : 0;
    172  1.8.4.1    martin 
    173  1.8.4.1    martin 	return clk_enable(clk);
    174  1.8.4.1    martin }
    175  1.8.4.1    martin 
    176  1.8.4.1    martin int
    177  1.8.4.1    martin fdtbus_clock_enable_index(int phandle, u_int index, bool required)
    178  1.8.4.1    martin {
    179  1.8.4.1    martin 	struct clk *clk;
    180  1.8.4.1    martin 
    181  1.8.4.1    martin 	clk = fdtbus_clock_get_index(phandle, index);
    182  1.8.4.1    martin 	if (clk == NULL)
    183  1.8.4.1    martin 		return required ? ENOENT : 0;
    184  1.8.4.1    martin 
    185  1.8.4.1    martin 	return clk_enable(clk);
    186  1.8.4.1    martin }
    187  1.8.4.1    martin 
    188      1.2  jmcneill /*
    189      1.2  jmcneill  * Search the DT for a clock by "clock-output-names" property.
    190      1.2  jmcneill  *
    191      1.2  jmcneill  * This should only be used by clk backends. Not for use by ordinary
    192      1.2  jmcneill  * clock consumers!
    193      1.2  jmcneill  */
    194      1.2  jmcneill struct clk *
    195      1.2  jmcneill fdtbus_clock_byname(const char *clkname)
    196      1.2  jmcneill {
    197      1.2  jmcneill 	struct fdtbus_clock_controller *cc;
    198      1.8  jakllsch 	u_int index, clock_cells;
    199      1.8  jakllsch 	int err;
    200      1.2  jmcneill 
    201      1.5  jmcneill 	LIST_FOREACH(cc, &fdtbus_clock_controllers, cc_next) {
    202      1.8  jakllsch 		err = fdtbus_get_index(cc->cc_phandle, "clock-output-names", clkname, &index);
    203      1.8  jakllsch 		if (err != 0)
    204      1.2  jmcneill 			continue;
    205      1.8  jakllsch 		if (of_getprop_uint32(cc->cc_phandle, "#clock-cells", &clock_cells))
    206      1.8  jakllsch 			continue;
    207      1.8  jakllsch 		const u_int index_raw = htobe32(index);
    208      1.8  jakllsch 		return cc->cc_funcs->decode(cc->cc_dev,
    209      1.8  jakllsch 		    cc->cc_phandle,
    210      1.8  jakllsch 		    clock_cells > 0 ? &index_raw : NULL,
    211      1.8  jakllsch 		    clock_cells > 0 ? 4 : 0);
    212      1.2  jmcneill 	}
    213      1.2  jmcneill 
    214      1.2  jmcneill 	return NULL;
    215      1.2  jmcneill }
    216      1.3  jmcneill 
    217      1.3  jmcneill /*
    218      1.3  jmcneill  * Apply assigned clock parents and rates.
    219      1.3  jmcneill  *
    220      1.3  jmcneill  * This is automatically called by fdtbus_register_clock_controller, so clock
    221      1.3  jmcneill  * drivers likely don't need to call this directly.
    222      1.3  jmcneill  */
    223      1.3  jmcneill void
    224      1.3  jmcneill fdtbus_clock_assign(int phandle)
    225      1.3  jmcneill {
    226      1.3  jmcneill 	u_int index, rates_len;
    227      1.3  jmcneill 	struct clk *clk, *clk_parent;
    228      1.3  jmcneill 	int error;
    229      1.3  jmcneill 
    230      1.3  jmcneill 	const u_int *rates = fdtbus_get_prop(phandle, "assigned-clock-rates", &rates_len);
    231      1.3  jmcneill 	if (rates == NULL)
    232      1.3  jmcneill 		rates_len = 0;
    233      1.3  jmcneill 
    234  1.8.4.2    martin 	const u_int nclocks = fdtbus_clock_count(phandle, "assigned-clocks");
    235  1.8.4.2    martin 	const u_int nparents = fdtbus_clock_count(phandle, "assigned-clock-parents");
    236      1.4  jmcneill 	const u_int nrates = rates_len / sizeof(*rates);
    237      1.3  jmcneill 
    238      1.3  jmcneill 	for (index = 0; index < nclocks; index++) {
    239      1.3  jmcneill 		clk = fdtbus_clock_get_index_prop(phandle, index, "assigned-clocks");
    240      1.3  jmcneill 		if (clk == NULL) {
    241      1.3  jmcneill 			aprint_debug("clk: assigned clock (%u) not found, skipping...\n", index);
    242      1.3  jmcneill 			continue;
    243      1.3  jmcneill 		}
    244      1.3  jmcneill 
    245      1.4  jmcneill 		if (index < nparents) {
    246      1.4  jmcneill 			clk_parent = fdtbus_clock_get_index_prop(phandle, index, "assigned-clock-parents");
    247      1.4  jmcneill 			if (clk_parent != NULL) {
    248      1.4  jmcneill 				error = clk_set_parent(clk, clk_parent);
    249      1.4  jmcneill 				if (error != 0) {
    250      1.4  jmcneill 					aprint_error("clk: failed to set %s parent to %s, error %d\n",
    251      1.4  jmcneill 					    clk->name, clk_parent->name, error);
    252      1.4  jmcneill 				}
    253      1.4  jmcneill 			} else {
    254      1.4  jmcneill 				aprint_debug("clk: failed to set %s parent (not found)\n", clk->name);
    255      1.3  jmcneill 			}
    256      1.3  jmcneill 		}
    257      1.3  jmcneill 
    258      1.4  jmcneill 		if (index < nrates) {
    259      1.4  jmcneill 			const u_int rate = be32toh(rates[index]);
    260      1.3  jmcneill 			if (rate != 0) {
    261      1.3  jmcneill 				error = clk_set_rate(clk, rate);
    262      1.3  jmcneill 				if (error != 0)
    263      1.3  jmcneill 					aprint_error("clk: failed to set %s rate to %u Hz, error %d\n",
    264      1.3  jmcneill 					    clk->name, rate, error);
    265      1.3  jmcneill 			}
    266      1.3  jmcneill 		}
    267      1.3  jmcneill 	}
    268      1.3  jmcneill }
    269