1271546Sian/*-
2271546Sian * Copyright (c) 2014 Ian Lepore <ian@freebsd.org>
3271546Sian * All rights reserved.
4271546Sian *
5271546Sian * Redistribution and use in source and binary forms, with or without
6271546Sian * modification, are permitted provided that the following conditions
7271546Sian * are met:
8271546Sian * 1. Redistributions of source code must retain the above copyright
9271546Sian *    notice, this list of conditions and the following disclaimer.
10271546Sian * 2. Redistributions in binary form must reproduce the above copyright
11271546Sian *    notice, this list of conditions and the following disclaimer in the
12271546Sian *    documentation and/or other materials provided with the distribution.
13271546Sian *
14271546Sian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15271546Sian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16271546Sian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17271546Sian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18271546Sian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19271546Sian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20271546Sian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21271546Sian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22271546Sian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23271546Sian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24271546Sian * SUCH DAMAGE.
25271546Sian *
26271546Sian * $FreeBSD$
27271546Sian */
28271546Sian
29271546Sian#include <sys/cdefs.h>
30271546Sian#include <sys/param.h>
31271546Sian
32271546Sian#include <dev/ofw/ofw_bus.h>
33271546Sian#include <dev/ofw/ofw_bus_subr.h>
34271546Sian
35271546Sian#include "fdt_pinctrl_if.h"
36271546Sian
37271546Sian#include <dev/fdt/fdt_common.h>
38271546Sian#include <dev/fdt/fdt_pinctrl.h>
39271546Sian
40271546Sianint
41271546Sianfdt_pinctrl_configure(device_t client, u_int index)
42271546Sian{
43271546Sian	device_t pinctrl;
44271546Sian	phandle_t *configs;
45271546Sian	int i, nconfigs;
46271546Sian	char name[16];
47271546Sian
48271546Sian	snprintf(name, sizeof(name), "pinctrl-%u", index);
49283485Sian	nconfigs = OF_getencprop_alloc(ofw_bus_get_node(client), name,
50271546Sian	    sizeof(*configs), (void **)&configs);
51271546Sian	if (nconfigs < 0)
52271546Sian		return (ENOENT);
53271546Sian	if (nconfigs == 0)
54271546Sian		return (0); /* Empty property is documented as valid. */
55271546Sian	for (i = 0; i < nconfigs; i++) {
56271546Sian		if ((pinctrl = OF_device_from_xref(configs[i])) != NULL)
57271546Sian			FDT_PINCTRL_CONFIGURE(pinctrl, configs[i]);
58271546Sian	}
59271546Sian	free(configs, M_OFWPROP);
60271546Sian	return (0);
61271546Sian}
62271546Sian
63271546Sianint
64271546Sianfdt_pinctrl_configure_by_name(device_t client, const char * name)
65271546Sian{
66271546Sian	char * names;
67271546Sian	int i, offset, nameslen;
68271546Sian
69271546Sian	nameslen = OF_getprop_alloc(ofw_bus_get_node(client), "pinctrl-names",
70271546Sian	    sizeof(*names), (void **)&names);
71271546Sian	if (nameslen <= 0)
72271546Sian		return (ENOENT);
73271546Sian	for (i = 0, offset = 0; offset < nameslen; i++) {
74271546Sian		if (strcmp(name, &names[offset]) == 0)
75271546Sian			break;
76271546Sian		offset += strlen(&names[offset]) + 1;
77271546Sian	}
78271546Sian	free(names, M_OFWPROP);
79271546Sian	if (offset < nameslen)
80271546Sian		return (fdt_pinctrl_configure(client, i));
81271546Sian	else
82271546Sian		return (ENOENT);
83271546Sian}
84271546Sian
85271546Sianstatic int
86271546Sianpinctrl_register_children(device_t pinctrl, phandle_t parent,
87271546Sian    const char *pinprop)
88271546Sian{
89271546Sian	phandle_t node;
90271546Sian
91271546Sian	/*
92271546Sian	 * Recursively descend from parent, looking for nodes that have the
93271546Sian	 * given property, and associate the pinctrl device_t with each one.
94271546Sian	 */
95271546Sian	for (node = OF_child(parent); node != 0; node = OF_peer(node)) {
96271546Sian		pinctrl_register_children(pinctrl, node, pinprop);
97271546Sian		if (pinprop == NULL || OF_hasprop(node, pinprop)) {
98271546Sian			OF_device_register_xref(OF_xref_from_node(node),
99271546Sian			    pinctrl);
100271546Sian		}
101271546Sian	}
102271546Sian	return (0);
103271546Sian}
104271546Sian
105271546Sianint
106271546Sianfdt_pinctrl_register(device_t pinctrl, const char *pinprop)
107271546Sian{
108271546Sian	phandle_t node;
109271546Sian
110271546Sian	node = ofw_bus_get_node(pinctrl);
111271546Sian	OF_device_register_xref(OF_xref_from_node(node), pinctrl);
112271546Sian	return (pinctrl_register_children(pinctrl, node, pinprop));
113271546Sian}
114271546Sian
115271546Sianstatic int
116271546Sianpinctrl_configure_children(device_t pinctrl, phandle_t parent)
117271546Sian{
118271546Sian	phandle_t node, *configs;
119271546Sian	int i, nconfigs;
120271546Sian
121271546Sian	for (node = OF_child(parent); node != 0; node = OF_peer(node)) {
122271546Sian		if (!fdt_is_enabled(node))
123271546Sian			continue;
124271546Sian		pinctrl_configure_children(pinctrl, node);
125283485Sian		nconfigs = OF_getencprop_alloc(node, "pinctrl-0",
126271546Sian		    sizeof(*configs), (void **)&configs);
127276275Sian		if (nconfigs <= 0)
128276275Sian			continue;
129276275Sian		if (bootverbose) {
130283485Sian			char name[32];
131271546Sian			OF_getprop(node, "name", &name, sizeof(name));
132276275Sian			printf("Processing %d pin-config node(s) in pinctrl-0 for %s\n",
133276275Sian			    nconfigs, name);
134271546Sian		}
135271546Sian		for (i = 0; i < nconfigs; i++) {
136271546Sian			if (OF_device_from_xref(configs[i]) == pinctrl)
137271546Sian				FDT_PINCTRL_CONFIGURE(pinctrl, configs[i]);
138271546Sian		}
139271546Sian		free(configs, M_OFWPROP);
140271546Sian	}
141271546Sian	return (0);
142271546Sian}
143271546Sian
144271546Sianint
145271546Sianfdt_pinctrl_configure_tree(device_t pinctrl)
146271546Sian{
147271546Sian
148271546Sian	return (pinctrl_configure_children(pinctrl, OF_peer(0)));
149271546Sian}
150271546Sian
151