1270957Sian/*-
2270957Sian * Copyright (c) 2014 Ian Lepore <ian@freebsd.org>
3270957Sian * All rights reserved.
4270957Sian *
5270957Sian * Redistribution and use in source and binary forms, with or without
6270957Sian * modification, are permitted provided that the following conditions
7270957Sian * are met:
8270957Sian * 1. Redistributions of source code must retain the above copyright
9270957Sian *    notice, this list of conditions and the following disclaimer.
10270957Sian * 2. Redistributions in binary form must reproduce the above copyright
11270957Sian *    notice, this list of conditions and the following disclaimer in the
12270957Sian *    documentation and/or other materials provided with the distribution.
13270957Sian *
14270957Sian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15270957Sian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16270957Sian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17270957Sian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18270957Sian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19270957Sian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20270957Sian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21270957Sian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22270957Sian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23270957Sian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24270957Sian * SUCH DAMAGE.
25270957Sian *
26270957Sian * $FreeBSD$
27270957Sian */
28270957Sian
29270957Sian#include <sys/cdefs.h>
30270957Sian#include <sys/param.h>
31270957Sian#include <sys/kernel.h>
32270957Sian#include <sys/lock.h>
33270957Sian#include <sys/mutex.h>
34270957Sian#include <sys/queue.h>
35270959Sian#include <sys/systm.h>
36270957Sian
37270957Sian#include <dev/ofw/ofw_bus.h>
38270957Sian#include <dev/ofw/ofw_bus_subr.h>
39270957Sian
40270957Sian#include "fdt_clock_if.h"
41270957Sian#include <dev/fdt/fdt_clock.h>
42270957Sian
43270957Sian/*
44270957Sian * Loop through all the tuples in the clocks= property for a device, enabling or
45270957Sian * disabling each clock.
46270957Sian *
47270957Sian * Be liberal about errors for now: warn about a failure to enable but keep
48270957Sian * trying with any other clocks in the list.  Return ENXIO if any errors were
49270957Sian * found, and let the caller decide whether the problem is fatal.
50270957Sian */
51270957Sianstatic int
52270957Sianenable_disable_all(device_t consumer, boolean_t enable)
53270957Sian{
54270957Sian	phandle_t cnode;
55270957Sian	device_t clockdev;
56270957Sian	int clocknum, err, i, ncells;
57270957Sian	uint32_t *clks;
58270957Sian	boolean_t anyerrors;
59270957Sian
60270957Sian	cnode = ofw_bus_get_node(consumer);
61270957Sian	ncells = OF_getencprop_alloc(cnode, "clocks", sizeof(*clks),
62270957Sian	    (void **)&clks);
63270957Sian	if (enable && ncells < 2) {
64270957Sian		device_printf(consumer, "Warning: No clocks specified in fdt "
65270957Sian		    "data; device may not function.");
66270957Sian		return (ENXIO);
67270957Sian	}
68270957Sian	anyerrors = false;
69270957Sian	for (i = 0; i < ncells; i += 2) {
70270957Sian		clockdev = OF_device_from_xref(clks[i]);
71270957Sian		clocknum = clks[i + 1];
72270957Sian		if (clockdev == NULL) {
73270957Sian			if (enable)
74270957Sian				device_printf(consumer, "Warning: can not find "
75270957Sian				    "driver for clock number %u; device may not "
76270957Sian				    "function\n", clocknum);
77270957Sian			anyerrors = true;
78270957Sian			continue;
79270957Sian		}
80270957Sian		if (enable)
81270957Sian			err = FDT_CLOCK_ENABLE(clockdev, clocknum);
82270957Sian		else
83270957Sian			err = FDT_CLOCK_DISABLE(clockdev, clocknum);
84270957Sian		if (err != 0) {
85270957Sian			if (enable)
86270957Sian				device_printf(consumer, "Warning: failed to "
87270957Sian				    "enable clock number %u; device may not "
88270957Sian				    "function\n", clocknum);
89270957Sian			anyerrors = true;
90270957Sian		}
91270957Sian	}
92299477Sgonzo	OF_prop_free(clks);
93270957Sian	return (anyerrors ? ENXIO : 0);
94270957Sian}
95270957Sian
96270957Sianint
97270957Sianfdt_clock_get_info(device_t consumer, int n, struct fdt_clock_info *info)
98270957Sian{
99270957Sian	phandle_t cnode;
100270957Sian	device_t clockdev;
101270957Sian	int clocknum, err, ncells;
102270957Sian	uint32_t *clks;
103270957Sian
104270957Sian	cnode = ofw_bus_get_node(consumer);
105270957Sian	ncells = OF_getencprop_alloc(cnode, "clocks", sizeof(*clks),
106270957Sian	    (void **)&clks);
107270957Sian	if (ncells <= 0)
108270957Sian		return (ENXIO);
109270957Sian	n *= 2;
110270957Sian	if (ncells <= n)
111270957Sian		err = ENXIO;
112270957Sian	else {
113270957Sian		clockdev = OF_device_from_xref(clks[n]);
114270957Sian		if (clockdev == NULL)
115270957Sian			err = ENXIO;
116270957Sian		else  {
117270957Sian			/*
118270957Sian			 * Make struct contents minimally valid, then call
119270957Sian			 * provider to fill in what it knows (provider can
120270957Sian			 * override anything it wants to).
121270957Sian			 */
122270957Sian			clocknum = clks[n + 1];
123270959Sian			bzero(info, sizeof(*info));
124270957Sian			info->provider = clockdev;
125270957Sian			info->index = clocknum;
126270957Sian			info->name = "";
127270957Sian			err = FDT_CLOCK_GET_INFO(clockdev, clocknum, info);
128270957Sian		}
129270957Sian	}
130299477Sgonzo	OF_prop_free(clks);
131270957Sian	return (err);
132270957Sian}
133270957Sian
134270957Sianint
135270957Sianfdt_clock_enable_all(device_t consumer)
136270957Sian{
137270957Sian
138270957Sian	return (enable_disable_all(consumer, true));
139270957Sian}
140270957Sian
141270957Sianint
142270957Sianfdt_clock_disable_all(device_t consumer)
143270957Sian{
144270957Sian
145270957Sian	return (enable_disable_all(consumer, false));
146270957Sian}
147270957Sian
148270957Sianvoid
149270957Sianfdt_clock_register_provider(device_t provider)
150270957Sian{
151270957Sian
152277655Sian	OF_device_register_xref(
153277989Sian	    OF_xref_from_node(ofw_bus_get_node(provider)), provider);
154270957Sian}
155270957Sian
156270957Sianvoid
157270957Sianfdt_clock_unregister_provider(device_t provider)
158270957Sian{
159270957Sian
160270959Sian	OF_device_register_xref(OF_xref_from_device(provider), NULL);
161270957Sian}
162270957Sian
163