1152683Smarius/*-
2186128Snwhitehorn * Copyright (c) 2001 - 2003 by Thomas Moestl <tmm@FreeBSD.org>.
3152683Smarius * Copyright (c) 2005 Marius Strobl <marius@FreeBSD.org>
4152683Smarius * All rights reserved.
5152683Smarius *
6152683Smarius * Redistribution and use in source and binary forms, with or without
7152683Smarius * modification, are permitted provided that the following conditions
8152683Smarius * are met:
9152683Smarius * 1. Redistributions of source code must retain the above copyright
10152683Smarius *    notice, this list of conditions, and the following disclaimer,
11152683Smarius *    without modification, immediately at the beginning of the file.
12152683Smarius * 2. Redistributions in binary form must reproduce the above copyright
13152683Smarius *    notice, this list of conditions and the following disclaimer in
14152683Smarius *    the documentation and/or other materials provided with the
15152683Smarius *    distribution.
16152683Smarius *
17152683Smarius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18152683Smarius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19152683Smarius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20152683Smarius * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
21152683Smarius * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22152683Smarius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23152683Smarius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24152683Smarius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25152683Smarius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26152683Smarius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27152683Smarius * SUCH DAMAGE.
28152683Smarius */
29152683Smarius
30152683Smarius#include <sys/cdefs.h>
31152683Smarius__FBSDID("$FreeBSD$");
32152683Smarius
33208614Sraj#include "opt_platform.h"
34152683Smarius#include <sys/param.h>
35152683Smarius#include <sys/systm.h>
36152683Smarius#include <sys/bus.h>
37152683Smarius#include <sys/errno.h>
38186128Snwhitehorn#include <sys/libkern.h>
39152683Smarius
40186128Snwhitehorn#include <dev/ofw/ofw_bus.h>
41152683Smarius#include <dev/ofw/ofw_bus_subr.h>
42152683Smarius#include <dev/ofw/openfirm.h>
43152683Smarius
44152683Smarius#include "ofw_bus_if.h"
45152683Smarius
46152683Smariusint
47152683Smariusofw_bus_gen_setup_devinfo(struct ofw_bus_devinfo *obd, phandle_t node)
48152683Smarius{
49152683Smarius
50152683Smarius	if (obd == NULL)
51152683Smarius		return (ENOMEM);
52152683Smarius	/* The 'name' property is considered mandatory. */
53152683Smarius	if ((OF_getprop_alloc(node, "name", 1, (void **)&obd->obd_name)) == -1)
54152683Smarius		return (EINVAL);
55152683Smarius	OF_getprop_alloc(node, "compatible", 1, (void **)&obd->obd_compat);
56152683Smarius	OF_getprop_alloc(node, "device_type", 1, (void **)&obd->obd_type);
57152683Smarius	OF_getprop_alloc(node, "model", 1, (void **)&obd->obd_model);
58266128Sian	OF_getprop_alloc(node, "status", 1, (void **)&obd->obd_status);
59152683Smarius	obd->obd_node = node;
60152683Smarius	return (0);
61152683Smarius}
62152683Smarius
63152683Smariusvoid
64152683Smariusofw_bus_gen_destroy_devinfo(struct ofw_bus_devinfo *obd)
65152683Smarius{
66152683Smarius
67152683Smarius	if (obd == NULL)
68152683Smarius		return;
69152683Smarius	if (obd->obd_compat != NULL)
70152683Smarius		free(obd->obd_compat, M_OFWPROP);
71152683Smarius	if (obd->obd_model != NULL)
72152683Smarius		free(obd->obd_model, M_OFWPROP);
73152683Smarius	if (obd->obd_name != NULL)
74152683Smarius		free(obd->obd_name, M_OFWPROP);
75152683Smarius	if (obd->obd_type != NULL)
76152683Smarius		free(obd->obd_type, M_OFWPROP);
77266128Sian	if (obd->obd_status != NULL)
78266128Sian		free(obd->obd_status, M_OFWPROP);
79152683Smarius}
80152683Smarius
81194138Smariusint
82186128Snwhitehornofw_bus_gen_child_pnpinfo_str(device_t cbdev, device_t child, char *buf,
83186128Snwhitehorn    size_t buflen)
84186128Snwhitehorn{
85194138Smarius
86186128Snwhitehorn	if (ofw_bus_get_name(child) != NULL) {
87186128Snwhitehorn		strlcat(buf, "name=", buflen);
88186128Snwhitehorn		strlcat(buf, ofw_bus_get_name(child), buflen);
89186128Snwhitehorn	}
90152683Smarius
91186128Snwhitehorn	if (ofw_bus_get_compat(child) != NULL) {
92186128Snwhitehorn		strlcat(buf, " compat=", buflen);
93186128Snwhitehorn		strlcat(buf, ofw_bus_get_compat(child), buflen);
94186128Snwhitehorn	}
95186128Snwhitehorn	return (0);
96186128Snwhitehorn};
97186128Snwhitehorn
98152683Smariusconst char *
99152683Smariusofw_bus_gen_get_compat(device_t bus, device_t dev)
100152683Smarius{
101152683Smarius	const struct ofw_bus_devinfo *obd;
102194138Smarius
103194138Smarius	obd = OFW_BUS_GET_DEVINFO(bus, dev);
104152683Smarius	if (obd == NULL)
105152683Smarius		return (NULL);
106152683Smarius	return (obd->obd_compat);
107152683Smarius}
108194138Smarius
109152683Smariusconst char *
110152683Smariusofw_bus_gen_get_model(device_t bus, device_t dev)
111152683Smarius{
112152683Smarius	const struct ofw_bus_devinfo *obd;
113152683Smarius
114194138Smarius	obd = OFW_BUS_GET_DEVINFO(bus, dev);
115152683Smarius	if (obd == NULL)
116152683Smarius		return (NULL);
117152683Smarius	return (obd->obd_model);
118152683Smarius}
119152683Smarius
120152683Smariusconst char *
121152683Smariusofw_bus_gen_get_name(device_t bus, device_t dev)
122152683Smarius{
123152683Smarius	const struct ofw_bus_devinfo *obd;
124152683Smarius
125194138Smarius	obd = OFW_BUS_GET_DEVINFO(bus, dev);
126152683Smarius	if (obd == NULL)
127152683Smarius		return (NULL);
128152683Smarius	return (obd->obd_name);
129152683Smarius}
130152683Smarius
131152683Smariusphandle_t
132152683Smariusofw_bus_gen_get_node(device_t bus, device_t dev)
133152683Smarius{
134152683Smarius	const struct ofw_bus_devinfo *obd;
135152683Smarius
136194138Smarius	obd = OFW_BUS_GET_DEVINFO(bus, dev);
137152683Smarius	if (obd == NULL)
138152683Smarius		return (0);
139152683Smarius	return (obd->obd_node);
140152683Smarius}
141152683Smarius
142152683Smariusconst char *
143152683Smariusofw_bus_gen_get_type(device_t bus, device_t dev)
144152683Smarius{
145152683Smarius	const struct ofw_bus_devinfo *obd;
146152683Smarius
147194138Smarius	obd = OFW_BUS_GET_DEVINFO(bus, dev);
148152683Smarius	if (obd == NULL)
149152683Smarius		return (NULL);
150152683Smarius	return (obd->obd_type);
151152683Smarius}
152186128Snwhitehorn
153266128Sianconst char *
154266128Sianofw_bus_get_status(device_t dev)
155266128Sian{
156266128Sian	const struct ofw_bus_devinfo *obd;
157266128Sian
158266128Sian	obd = OFW_BUS_GET_DEVINFO(device_get_parent(dev), dev);
159266128Sian	if (obd == NULL)
160266128Sian		return (NULL);
161266128Sian
162266128Sian	return (obd->obd_status);
163266128Sian}
164266128Sian
165208614Srajint
166266128Sianofw_bus_status_okay(device_t dev)
167266128Sian{
168266128Sian	const char *status;
169266128Sian
170266128Sian	status = ofw_bus_get_status(dev);
171266128Sian	if (status == NULL || strcmp(status, "okay") == 0)
172266128Sian		return (1);
173266128Sian
174266128Sian	return (0);
175266128Sian}
176266128Sian
177266128Sianint
178208614Srajofw_bus_is_compatible(device_t dev, const char *onecompat)
179208614Sraj{
180208614Sraj	phandle_t node;
181208614Sraj	const char *compat;
182208614Sraj	int len, onelen, l;
183208614Sraj
184208614Sraj	if ((compat = ofw_bus_get_compat(dev)) == NULL)
185208614Sraj		return (0);
186208614Sraj
187233018Snwhitehorn	if ((node = ofw_bus_get_node(dev)) == -1)
188208614Sraj		return (0);
189208614Sraj
190208614Sraj	/* Get total 'compatible' prop len */
191208614Sraj	if ((len = OF_getproplen(node, "compatible")) <= 0)
192208614Sraj		return (0);
193208614Sraj
194208614Sraj	onelen = strlen(onecompat);
195208614Sraj
196208614Sraj	while (len > 0) {
197239366Shrs		if (strlen(compat) == onelen &&
198239366Shrs		    strncasecmp(compat, onecompat, onelen) == 0)
199208614Sraj			/* Found it. */
200208614Sraj			return (1);
201208614Sraj
202208614Sraj		/* Slide to the next sub-string. */
203208614Sraj		l = strlen(compat) + 1;
204208614Sraj		compat += l;
205208614Sraj		len -= l;
206208614Sraj	}
207208614Sraj	return (0);
208208614Sraj}
209208614Sraj
210208614Srajint
211208614Srajofw_bus_is_compatible_strict(device_t dev, const char *compatible)
212208614Sraj{
213208614Sraj	const char *compat;
214239366Shrs	size_t len;
215208614Sraj
216208614Sraj	if ((compat = ofw_bus_get_compat(dev)) == NULL)
217208614Sraj		return (0);
218208614Sraj
219239366Shrs	len = strlen(compatible);
220239366Shrs	if (strlen(compat) == len &&
221239366Shrs	    strncasecmp(compat, compatible, len) == 0)
222208614Sraj		return (1);
223208614Sraj
224208614Sraj	return (0);
225208614Sraj}
226208614Sraj
227259316Sianconst struct ofw_compat_data *
228259316Sianofw_bus_search_compatible(device_t dev, const struct ofw_compat_data *compat)
229259316Sian{
230259316Sian
231259316Sian	if (compat == NULL)
232259316Sian		return NULL;
233259316Sian
234259316Sian	for (; compat->ocd_str != NULL; ++compat) {
235259316Sian		if (ofw_bus_is_compatible(dev, compat->ocd_str))
236259316Sian			break;
237259316Sian	}
238259316Sian
239259316Sian	return (compat);
240259316Sian}
241259316Sian
242239366Shrsint
243239366Shrsofw_bus_has_prop(device_t dev, const char *propname)
244239366Shrs{
245239366Shrs	phandle_t node;
246239366Shrs
247239366Shrs	if ((node = ofw_bus_get_node(dev)) == -1)
248239366Shrs		return (0);
249239366Shrs
250239366Shrs	return (OF_hasprop(node, propname));
251239366Shrs}
252239366Shrs
253186128Snwhitehornvoid
254186128Snwhitehornofw_bus_setup_iinfo(phandle_t node, struct ofw_bus_iinfo *ii, int intrsz)
255186128Snwhitehorn{
256186128Snwhitehorn	pcell_t addrc;
257186128Snwhitehorn	int msksz;
258186128Snwhitehorn
259265967Sian	if (OF_getencprop(node, "#address-cells", &addrc, sizeof(addrc)) == -1)
260186128Snwhitehorn		addrc = 2;
261186128Snwhitehorn	ii->opi_addrc = addrc * sizeof(pcell_t);
262186128Snwhitehorn
263265967Sian	ii->opi_imapsz = OF_getencprop_alloc(node, "interrupt-map", 1,
264186128Snwhitehorn	    (void **)&ii->opi_imap);
265186128Snwhitehorn	if (ii->opi_imapsz > 0) {
266265967Sian		msksz = OF_getencprop_alloc(node, "interrupt-map-mask", 1,
267186128Snwhitehorn		    (void **)&ii->opi_imapmsk);
268186128Snwhitehorn		/*
269194138Smarius		 * Failure to get the mask is ignored; a full mask is used
270194138Smarius		 * then.  We barf on bad mask sizes, however.
271186128Snwhitehorn		 */
272194138Smarius		if (msksz != -1 && msksz != ii->opi_addrc + intrsz)
273186128Snwhitehorn			panic("ofw_bus_setup_iinfo: bad interrupt-map-mask "
274186128Snwhitehorn			    "property!");
275186128Snwhitehorn	}
276186128Snwhitehorn}
277186128Snwhitehorn
278186128Snwhitehornint
279186128Snwhitehornofw_bus_lookup_imap(phandle_t node, struct ofw_bus_iinfo *ii, void *reg,
280186128Snwhitehorn    int regsz, void *pintr, int pintrsz, void *mintr, int mintrsz,
281266020Sian    phandle_t *iparent)
282186128Snwhitehorn{
283266020Sian	uint8_t maskbuf[regsz + pintrsz];
284186128Snwhitehorn	int rv;
285186128Snwhitehorn
286186128Snwhitehorn	if (ii->opi_imapsz <= 0)
287186128Snwhitehorn		return (0);
288186128Snwhitehorn	KASSERT(regsz >= ii->opi_addrc,
289186128Snwhitehorn	    ("ofw_bus_lookup_imap: register size too small: %d < %d",
290186128Snwhitehorn		regsz, ii->opi_addrc));
291265954Sian	if (node != -1) {
292265967Sian		rv = OF_getencprop(node, "reg", reg, regsz);
293265954Sian		if (rv < regsz)
294265954Sian			panic("ofw_bus_lookup_imap: cannot get reg property");
295265954Sian	}
296186128Snwhitehorn	return (ofw_bus_search_intrmap(pintr, pintrsz, reg, ii->opi_addrc,
297186128Snwhitehorn	    ii->opi_imap, ii->opi_imapsz, ii->opi_imapmsk, maskbuf, mintr,
298209298Snwhitehorn	    mintrsz, iparent));
299186128Snwhitehorn}
300186128Snwhitehorn
301186128Snwhitehorn/*
302186128Snwhitehorn * Map an interrupt using the firmware reg, interrupt-map and
303186128Snwhitehorn * interrupt-map-mask properties.
304186128Snwhitehorn * The interrupt property to be mapped must be of size intrsz, and pointed to
305194138Smarius * by intr.  The regs property of the node for which the mapping is done must
306186128Snwhitehorn * be passed as regs. This property is an array of register specifications;
307186128Snwhitehorn * the size of the address part of such a specification must be passed as
308194138Smarius * physsz.  Only the first element of the property is used.
309186128Snwhitehorn * imap and imapsz hold the interrupt mask and it's size.
310186128Snwhitehorn * imapmsk is a pointer to the interrupt-map-mask property, which must have
311186128Snwhitehorn * a size of physsz + intrsz; it may be NULL, in which case a full mask is
312186128Snwhitehorn * assumed.
313186128Snwhitehorn * maskbuf must point to a buffer of length physsz + intrsz.
314186128Snwhitehorn * The interrupt is returned in result, which must point to a buffer of length
315186128Snwhitehorn * rintrsz (which gives the expected size of the mapped interrupt).
316266020Sian * Returns number of cells in the interrupt if a mapping was found, 0 otherwise.
317186128Snwhitehorn */
318186128Snwhitehornint
319186128Snwhitehornofw_bus_search_intrmap(void *intr, int intrsz, void *regs, int physsz,
320186128Snwhitehorn    void *imap, int imapsz, void *imapmsk, void *maskbuf, void *result,
321209298Snwhitehorn    int rintrsz, phandle_t *iparent)
322186128Snwhitehorn{
323186128Snwhitehorn	phandle_t parent;
324194138Smarius	uint8_t *ref = maskbuf;
325194138Smarius	uint8_t *uiintr = intr;
326194138Smarius	uint8_t *uiregs = regs;
327194138Smarius	uint8_t *uiimapmsk = imapmsk;
328194138Smarius	uint8_t *mptr;
329186128Snwhitehorn	pcell_t pintrsz;
330186128Snwhitehorn	int i, rsz, tsz;
331186128Snwhitehorn
332186128Snwhitehorn	rsz = -1;
333186128Snwhitehorn	if (imapmsk != NULL) {
334186128Snwhitehorn		for (i = 0; i < physsz; i++)
335186128Snwhitehorn			ref[i] = uiregs[i] & uiimapmsk[i];
336186128Snwhitehorn		for (i = 0; i < intrsz; i++)
337186128Snwhitehorn			ref[physsz + i] = uiintr[i] & uiimapmsk[physsz + i];
338186128Snwhitehorn	} else {
339186128Snwhitehorn		bcopy(regs, ref, physsz);
340186128Snwhitehorn		bcopy(intr, ref + physsz, intrsz);
341186128Snwhitehorn	}
342186128Snwhitehorn
343186128Snwhitehorn	mptr = imap;
344186128Snwhitehorn	i = imapsz;
345186128Snwhitehorn	while (i > 0) {
346186128Snwhitehorn		bcopy(mptr + physsz + intrsz, &parent, sizeof(parent));
347265967Sian		if (OF_searchencprop(OF_xref_phandle(parent),
348265967Sian		    "#interrupt-cells", &pintrsz, sizeof(pintrsz)) == -1)
349186128Snwhitehorn			pintrsz = 1;	/* default */
350186128Snwhitehorn		pintrsz *= sizeof(pcell_t);
351186728Snwhitehorn
352194138Smarius		/* Compute the map stride size. */
353186728Snwhitehorn		tsz = physsz + intrsz + sizeof(phandle_t) + pintrsz;
354186728Snwhitehorn		KASSERT(i >= tsz, ("ofw_bus_search_intrmap: truncated map"));
355194138Smarius
356186128Snwhitehorn		if (bcmp(ref, mptr, physsz + intrsz) == 0) {
357186128Snwhitehorn			bcopy(mptr + physsz + intrsz + sizeof(parent),
358266020Sian			    result, MIN(rintrsz, pintrsz));
359209298Snwhitehorn
360209298Snwhitehorn			if (iparent != NULL)
361209298Snwhitehorn				*iparent = parent;
362266020Sian			return (pintrsz/sizeof(pcell_t));
363186128Snwhitehorn		}
364186128Snwhitehorn		mptr += tsz;
365186128Snwhitehorn		i -= tsz;
366186128Snwhitehorn	}
367186128Snwhitehorn	return (0);
368186128Snwhitehorn}
369265954Sian
370