xref: /freebsd/sys/dev/pwm/ofw_pwmbus.c (revision edf8578117e8844e02c0121147f45e4609b30680)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2019 Ian Lepore <ian@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 #include <sys/param.h>
30 #include <sys/bus.h>
31 #include <sys/kernel.h>
32 #include <sys/libkern.h>
33 #include <sys/lock.h>
34 #include <sys/module.h>
35 
36 #include <dev/fdt/fdt_common.h>
37 #include <dev/ofw/ofw_bus.h>
38 #include <dev/ofw/ofw_bus_subr.h>
39 #include <dev/ofw/openfirm.h>
40 #include <dev/pwm/pwmbus.h>
41 
42 #include "pwmbus_if.h"
43 
44 struct ofw_pwmbus_ivars {
45 	struct pwmbus_ivars	base;
46 	struct ofw_bus_devinfo	devinfo;
47 };
48 
49 struct ofw_pwmbus_softc {
50 	struct pwmbus_softc	base;
51 };
52 
53 /*
54  * bus_if methods...
55  */
56 
57 static device_t
58 ofw_pwmbus_add_child(device_t dev, u_int order, const char *name, int unit)
59 {
60 	device_t child;
61 	struct ofw_pwmbus_ivars *ivars;
62 
63 	if ((ivars = malloc(sizeof(struct ofw_pwmbus_ivars), M_DEVBUF,
64 	    M_NOWAIT | M_ZERO)) == NULL) {
65 		return (NULL);
66 	}
67 
68 	if ((child = device_add_child_ordered(dev, order, name, unit)) == NULL) {
69 		free(ivars, M_DEVBUF);
70 		return (NULL);
71 	}
72 
73 	ivars->devinfo.obd_node = -1;
74 	device_set_ivars(child, ivars);
75 
76 	return (child);
77 }
78 
79 static void
80 ofw_pwmbus_child_deleted(device_t dev, device_t child)
81 {
82 	struct ofw_pwmbus_ivars *ivars;
83 
84 	ivars = device_get_ivars(child);
85 	if (ivars != NULL) {
86 		ofw_bus_gen_destroy_devinfo(&ivars->devinfo);
87 		free(ivars, M_DEVBUF);
88 	}
89 }
90 
91 static const struct ofw_bus_devinfo *
92 ofw_pwmbus_get_devinfo(device_t bus, device_t dev)
93 {
94 	struct ofw_pwmbus_ivars *ivars;
95 
96 	ivars = device_get_ivars(dev);
97 	return (&ivars->devinfo);
98 }
99 
100 /*
101  * device_if methods...
102  */
103 
104 static int
105 ofw_pwmbus_probe(device_t dev)
106 {
107 
108 	if (ofw_bus_get_node(dev) == -1) {
109 		return (ENXIO);
110 	}
111 	device_set_desc(dev, "OFW PWM bus");
112 
113 	return (BUS_PROBE_DEFAULT);
114 }
115 
116 static int
117 ofw_pwmbus_attach(device_t dev)
118 {
119 	struct ofw_pwmbus_softc *sc;
120 	struct ofw_pwmbus_ivars *ivars;
121 	phandle_t node;
122 	device_t child, parent;
123 	pcell_t  chan;
124 	bool any_children;
125 
126 	sc = device_get_softc(dev);
127 	sc->base.dev = dev;
128 	parent = device_get_parent(dev);
129 
130 	if (PWMBUS_CHANNEL_COUNT(parent, &sc->base.nchannels) != 0 ||
131 	    sc->base.nchannels == 0) {
132 		device_printf(dev, "No channels on parent %s\n",
133 		    device_get_nameunit(parent));
134 		return (ENXIO);
135 	}
136 
137 	/*
138 	 * Attach the children found in the fdt node of the hardware controller.
139 	 * Hardware controllers must implement the ofw_bus_get_node method so
140 	 * that our call to ofw_bus_get_node() gets back the controller's node.
141 	 */
142 	any_children = false;
143 	node = ofw_bus_get_node(dev);
144 	for (node = OF_child(node); node != 0; node = OF_peer(node)) {
145 		/*
146 		 * The child has to have a reg property; its value is the
147 		 * channel number so range-check it.
148 		 */
149 		if (OF_getencprop(node, "reg", &chan, sizeof(chan)) == -1)
150 			continue;
151 		if (chan >= sc->base.nchannels)
152 			continue;
153 
154 		if ((child = ofw_pwmbus_add_child(dev, 0, NULL, -1)) == NULL)
155 			continue;
156 
157 		ivars = device_get_ivars(child);
158 		ivars->base.pi_channel = chan;
159 
160 		/* Set up the standard ofw devinfo. */
161 		if (ofw_bus_gen_setup_devinfo(&ivars->devinfo, node) != 0) {
162 			device_delete_child(dev, child);
163 			continue;
164 		}
165 		any_children = true;
166 	}
167 
168 	/*
169 	 * If we didn't find any children in the fdt data, add a pwmc(4) child
170 	 * for each channel, like the base pwmbus does.  The idea is that if
171 	 * there is any fdt data, then we do exactly what it says and nothing
172 	 * more, otherwise we just provide generic userland access to all the
173 	 * pwm channels that exist like the base pwmbus's attach code does.
174 	 */
175 	if (!any_children) {
176 		for (chan = 0; chan < sc->base.nchannels; ++chan) {
177 			child = ofw_pwmbus_add_child(dev, 0, "pwmc", -1);
178 			if (child == NULL) {
179 				device_printf(dev, "failed to add pwmc child "
180 				    " device for channel %u\n", chan);
181 				continue;
182 			}
183 			ivars = device_get_ivars(child);
184 			ivars->base.pi_channel = chan;
185 		}
186 	}
187 	bus_enumerate_hinted_children(dev);
188 	bus_generic_probe(dev);
189 
190 	return (bus_generic_attach(dev));
191 }
192 
193 static device_method_t ofw_pwmbus_methods[] = {
194 	/* Device interface */
195 	DEVMETHOD(device_probe,           ofw_pwmbus_probe),
196 	DEVMETHOD(device_attach,          ofw_pwmbus_attach),
197 
198 	/* Bus interface */
199 	DEVMETHOD(bus_child_pnpinfo,	  ofw_bus_gen_child_pnpinfo),
200 	DEVMETHOD(bus_add_child,          ofw_pwmbus_add_child),
201 	DEVMETHOD(bus_child_deleted,      ofw_pwmbus_child_deleted),
202 
203 	/* ofw_bus interface */
204 	DEVMETHOD(ofw_bus_get_devinfo,    ofw_pwmbus_get_devinfo),
205 	DEVMETHOD(ofw_bus_get_compat,     ofw_bus_gen_get_compat),
206 	DEVMETHOD(ofw_bus_get_model,      ofw_bus_gen_get_model),
207 	DEVMETHOD(ofw_bus_get_name,       ofw_bus_gen_get_name),
208 	DEVMETHOD(ofw_bus_get_node,       ofw_bus_gen_get_node),
209 	DEVMETHOD(ofw_bus_get_type,       ofw_bus_gen_get_type),
210 
211 	DEVMETHOD_END
212 };
213 
214 DEFINE_CLASS_1(pwmbus, ofw_pwmbus_driver, ofw_pwmbus_methods,
215     sizeof(struct pwmbus_softc), pwmbus_driver);
216 EARLY_DRIVER_MODULE(ofw_pwmbus, pwm, ofw_pwmbus_driver, 0, 0,
217     BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
218 MODULE_VERSION(ofw_pwmbus, 1);
219 MODULE_DEPEND(ofw_pwmbus, pwmbus, 1, 1, 1);
220