xref: /freebsd/sys/dev/fdt/fdt_clock.c (revision 6b6d6c4437cbc8d580710bd030d3f7055105a38a)
1*6b6d6c44SIan Lepore /*-
2*6b6d6c44SIan Lepore  * Copyright (c) 2014 Ian Lepore <ian@freebsd.org>
3*6b6d6c44SIan Lepore  * All rights reserved.
4*6b6d6c44SIan Lepore  *
5*6b6d6c44SIan Lepore  * Redistribution and use in source and binary forms, with or without
6*6b6d6c44SIan Lepore  * modification, are permitted provided that the following conditions
7*6b6d6c44SIan Lepore  * are met:
8*6b6d6c44SIan Lepore  * 1. Redistributions of source code must retain the above copyright
9*6b6d6c44SIan Lepore  *    notice, this list of conditions and the following disclaimer.
10*6b6d6c44SIan Lepore  * 2. Redistributions in binary form must reproduce the above copyright
11*6b6d6c44SIan Lepore  *    notice, this list of conditions and the following disclaimer in the
12*6b6d6c44SIan Lepore  *    documentation and/or other materials provided with the distribution.
13*6b6d6c44SIan Lepore  *
14*6b6d6c44SIan Lepore  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15*6b6d6c44SIan Lepore  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16*6b6d6c44SIan Lepore  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17*6b6d6c44SIan Lepore  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18*6b6d6c44SIan Lepore  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19*6b6d6c44SIan Lepore  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20*6b6d6c44SIan Lepore  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21*6b6d6c44SIan Lepore  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22*6b6d6c44SIan Lepore  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23*6b6d6c44SIan Lepore  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24*6b6d6c44SIan Lepore  * SUCH DAMAGE.
25*6b6d6c44SIan Lepore  *
26*6b6d6c44SIan Lepore  * $FreeBSD$
27*6b6d6c44SIan Lepore  */
28*6b6d6c44SIan Lepore 
29*6b6d6c44SIan Lepore #include <sys/cdefs.h>
30*6b6d6c44SIan Lepore #include <sys/param.h>
31*6b6d6c44SIan Lepore #include <sys/kernel.h>
32*6b6d6c44SIan Lepore #include <sys/lock.h>
33*6b6d6c44SIan Lepore #include <sys/mutex.h>
34*6b6d6c44SIan Lepore #include <sys/queue.h>
35*6b6d6c44SIan Lepore 
36*6b6d6c44SIan Lepore #include <dev/ofw/ofw_bus.h>
37*6b6d6c44SIan Lepore #include <dev/ofw/ofw_bus_subr.h>
38*6b6d6c44SIan Lepore 
39*6b6d6c44SIan Lepore #include "fdt_clock_if.h"
40*6b6d6c44SIan Lepore #include <dev/fdt/fdt_clock.h>
41*6b6d6c44SIan Lepore 
42*6b6d6c44SIan Lepore /*
43*6b6d6c44SIan Lepore  * Loop through all the tuples in the clocks= property for a device, enabling or
44*6b6d6c44SIan Lepore  * disabling each clock.
45*6b6d6c44SIan Lepore  *
46*6b6d6c44SIan Lepore  * Be liberal about errors for now: warn about a failure to enable but keep
47*6b6d6c44SIan Lepore  * trying with any other clocks in the list.  Return ENXIO if any errors were
48*6b6d6c44SIan Lepore  * found, and let the caller decide whether the problem is fatal.
49*6b6d6c44SIan Lepore  */
50*6b6d6c44SIan Lepore static int
51*6b6d6c44SIan Lepore enable_disable_all(device_t consumer, boolean_t enable)
52*6b6d6c44SIan Lepore {
53*6b6d6c44SIan Lepore 	phandle_t cnode;
54*6b6d6c44SIan Lepore 	device_t clockdev;
55*6b6d6c44SIan Lepore 	int clocknum, err, i, ncells;
56*6b6d6c44SIan Lepore 	uint32_t *clks;
57*6b6d6c44SIan Lepore 	boolean_t anyerrors;
58*6b6d6c44SIan Lepore 
59*6b6d6c44SIan Lepore 	cnode = ofw_bus_get_node(consumer);
60*6b6d6c44SIan Lepore 	ncells = OF_getencprop_alloc(cnode, "clocks", sizeof(*clks),
61*6b6d6c44SIan Lepore 	    (void **)&clks);
62*6b6d6c44SIan Lepore 	if (enable && ncells < 2) {
63*6b6d6c44SIan Lepore 		device_printf(consumer, "Warning: No clocks specified in fdt "
64*6b6d6c44SIan Lepore 		    "data; device may not function.");
65*6b6d6c44SIan Lepore 		return (ENXIO);
66*6b6d6c44SIan Lepore 	}
67*6b6d6c44SIan Lepore 	anyerrors = false;
68*6b6d6c44SIan Lepore 	for (i = 0; i < ncells; i += 2) {
69*6b6d6c44SIan Lepore 		clockdev = OF_device_from_xref(clks[i]);
70*6b6d6c44SIan Lepore 		clocknum = clks[i + 1];
71*6b6d6c44SIan Lepore 		if (clockdev == NULL) {
72*6b6d6c44SIan Lepore 			if (enable)
73*6b6d6c44SIan Lepore 				device_printf(consumer, "Warning: can not find "
74*6b6d6c44SIan Lepore 				    "driver for clock number %u; device may not "
75*6b6d6c44SIan Lepore 				    "function\n", clocknum);
76*6b6d6c44SIan Lepore 			anyerrors = true;
77*6b6d6c44SIan Lepore 			continue;
78*6b6d6c44SIan Lepore 		}
79*6b6d6c44SIan Lepore 		if (enable)
80*6b6d6c44SIan Lepore 			err = FDT_CLOCK_ENABLE(clockdev, clocknum);
81*6b6d6c44SIan Lepore 		else
82*6b6d6c44SIan Lepore 			err = FDT_CLOCK_DISABLE(clockdev, clocknum);
83*6b6d6c44SIan Lepore 		if (err != 0) {
84*6b6d6c44SIan Lepore 			if (enable)
85*6b6d6c44SIan Lepore 				device_printf(consumer, "Warning: failed to "
86*6b6d6c44SIan Lepore 				    "enable clock number %u; device may not "
87*6b6d6c44SIan Lepore 				    "function\n", clocknum);
88*6b6d6c44SIan Lepore 			anyerrors = true;
89*6b6d6c44SIan Lepore 		}
90*6b6d6c44SIan Lepore 	}
91*6b6d6c44SIan Lepore 	free(clks, M_OFWPROP);
92*6b6d6c44SIan Lepore 	return (anyerrors ? ENXIO : 0);
93*6b6d6c44SIan Lepore }
94*6b6d6c44SIan Lepore 
95*6b6d6c44SIan Lepore int
96*6b6d6c44SIan Lepore fdt_clock_get_info(device_t consumer, int n, struct fdt_clock_info *info)
97*6b6d6c44SIan Lepore {
98*6b6d6c44SIan Lepore 	phandle_t cnode;
99*6b6d6c44SIan Lepore 	device_t clockdev;
100*6b6d6c44SIan Lepore 	int clocknum, err, ncells;
101*6b6d6c44SIan Lepore 	uint32_t *clks;
102*6b6d6c44SIan Lepore 
103*6b6d6c44SIan Lepore 	cnode = ofw_bus_get_node(consumer);
104*6b6d6c44SIan Lepore 	ncells = OF_getencprop_alloc(cnode, "clocks", sizeof(*clks),
105*6b6d6c44SIan Lepore 	    (void **)&clks);
106*6b6d6c44SIan Lepore 	if (ncells <= 0)
107*6b6d6c44SIan Lepore 		return (ENXIO);
108*6b6d6c44SIan Lepore 	n *= 2;
109*6b6d6c44SIan Lepore 	if (ncells <= n)
110*6b6d6c44SIan Lepore 		err = ENXIO;
111*6b6d6c44SIan Lepore 	else {
112*6b6d6c44SIan Lepore 		clockdev = OF_device_from_xref(clks[n]);
113*6b6d6c44SIan Lepore 		if (clockdev == NULL)
114*6b6d6c44SIan Lepore 			err = ENXIO;
115*6b6d6c44SIan Lepore 		else  {
116*6b6d6c44SIan Lepore 			/*
117*6b6d6c44SIan Lepore 			 * Make struct contents minimally valid, then call
118*6b6d6c44SIan Lepore 			 * provider to fill in what it knows (provider can
119*6b6d6c44SIan Lepore 			 * override anything it wants to).
120*6b6d6c44SIan Lepore 			 */
121*6b6d6c44SIan Lepore 			clocknum = clks[n + 1];
122*6b6d6c44SIan Lepore 			memset(info, 0, sizeof(*info));
123*6b6d6c44SIan Lepore 			info->provider = clockdev;
124*6b6d6c44SIan Lepore 			info->index = clocknum;
125*6b6d6c44SIan Lepore 			info->name = "";
126*6b6d6c44SIan Lepore 			err = FDT_CLOCK_GET_INFO(clockdev, clocknum, info);
127*6b6d6c44SIan Lepore 		}
128*6b6d6c44SIan Lepore 	}
129*6b6d6c44SIan Lepore 	free(clks, M_OFWPROP);
130*6b6d6c44SIan Lepore 	return (err);
131*6b6d6c44SIan Lepore }
132*6b6d6c44SIan Lepore 
133*6b6d6c44SIan Lepore int
134*6b6d6c44SIan Lepore fdt_clock_enable_all(device_t consumer)
135*6b6d6c44SIan Lepore {
136*6b6d6c44SIan Lepore 
137*6b6d6c44SIan Lepore 	return (enable_disable_all(consumer, true));
138*6b6d6c44SIan Lepore }
139*6b6d6c44SIan Lepore 
140*6b6d6c44SIan Lepore int
141*6b6d6c44SIan Lepore fdt_clock_disable_all(device_t consumer)
142*6b6d6c44SIan Lepore {
143*6b6d6c44SIan Lepore 
144*6b6d6c44SIan Lepore 	return (enable_disable_all(consumer, false));
145*6b6d6c44SIan Lepore }
146*6b6d6c44SIan Lepore 
147*6b6d6c44SIan Lepore void
148*6b6d6c44SIan Lepore fdt_clock_register_provider(device_t provider)
149*6b6d6c44SIan Lepore {
150*6b6d6c44SIan Lepore 
151*6b6d6c44SIan Lepore 	OF_device_register_xref(OF_xref_from_node(provider), provider);
152*6b6d6c44SIan Lepore }
153*6b6d6c44SIan Lepore 
154*6b6d6c44SIan Lepore void
155*6b6d6c44SIan Lepore fdt_clock_unregister_provider(device_t provider)
156*6b6d6c44SIan Lepore {
157*6b6d6c44SIan Lepore 
158*6b6d6c44SIan Lepore 	OF_device_register_xref(OF_xref_from_node(provider), NULL);
159*6b6d6c44SIan Lepore }
160*6b6d6c44SIan Lepore 
161