1257064Sloos/*-
2257064Sloos * Copyright (c) 2009, Nathan Whitehorn <nwhitehorn@FreeBSD.org>
3257064Sloos * Copyright (c) 2013 The FreeBSD Foundation
4257064Sloos * All rights reserved.
5257064Sloos *
6257064Sloos * Portions of this software were developed by Oleksandr Rybalko
7257064Sloos * under sponsorship from the FreeBSD Foundation.
8257064Sloos *
9257064Sloos * Redistribution and use in source and binary forms, with or without
10257064Sloos * modification, are permitted provided that the following conditions
11257064Sloos * are met:
12257064Sloos * 1. Redistributions of source code must retain the above copyright
13257064Sloos *    notice unmodified, this list of conditions, and the following
14257064Sloos *    disclaimer.
15257064Sloos * 2. Redistributions in binary form must reproduce the above copyright
16257064Sloos *    notice, this list of conditions and the following disclaimer in the
17257064Sloos *    documentation and/or other materials provided with the distribution.
18257064Sloos *
19257064Sloos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20257064Sloos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21257064Sloos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22257064Sloos * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23257064Sloos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24257064Sloos * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25257064Sloos * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26257064Sloos * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27257064Sloos * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28257064Sloos * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29257064Sloos */
30257064Sloos
31257064Sloos#include <sys/cdefs.h>
32257064Sloos__FBSDID("$FreeBSD: stable/11/sys/dev/spibus/ofw_spibus.c 332942 2018-04-24 17:00:08Z ian $");
33257064Sloos
34257064Sloos#include <sys/param.h>
35257064Sloos#include <sys/bus.h>
36257064Sloos#include <sys/kernel.h>
37257064Sloos#include <sys/libkern.h>
38257064Sloos#include <sys/lock.h>
39257064Sloos#include <sys/module.h>
40257064Sloos#include <sys/mutex.h>
41257064Sloos
42257064Sloos#include <dev/fdt/fdt_common.h>
43257064Sloos#include <dev/spibus/spi.h>
44257064Sloos#include <dev/spibus/spibusvar.h>
45257064Sloos#include <dev/ofw/ofw_bus.h>
46257064Sloos#include <dev/ofw/ofw_bus_subr.h>
47257064Sloos#include <dev/ofw/openfirm.h>
48257064Sloos
49257064Sloos#include "spibus_if.h"
50257064Sloos
51257064Sloosstruct ofw_spibus_devinfo {
52257064Sloos	struct spibus_ivar	opd_dinfo;
53257064Sloos	struct ofw_bus_devinfo	opd_obdinfo;
54257064Sloos};
55257064Sloos
56257064Sloos/* Methods */
57257064Sloosstatic device_probe_t ofw_spibus_probe;
58257064Sloosstatic device_attach_t ofw_spibus_attach;
59257064Sloosstatic device_t ofw_spibus_add_child(device_t dev, u_int order,
60257064Sloos    const char *name, int unit);
61257064Sloosstatic const struct ofw_bus_devinfo *ofw_spibus_get_devinfo(device_t bus,
62257064Sloos    device_t dev);
63257064Sloos
64257064Sloosstatic int
65257064Sloosofw_spibus_probe(device_t dev)
66257064Sloos{
67257064Sloos
68257064Sloos	if (ofw_bus_get_node(dev) == -1)
69257064Sloos		return (ENXIO);
70257064Sloos	device_set_desc(dev, "OFW SPI bus");
71257064Sloos
72332942Sian	return (BUS_PROBE_DEFAULT);
73257064Sloos}
74257064Sloos
75257064Sloosstatic int
76257064Sloosofw_spibus_attach(device_t dev)
77257064Sloos{
78257064Sloos	struct spibus_softc *sc = device_get_softc(dev);
79257064Sloos	struct ofw_spibus_devinfo *dinfo;
80257064Sloos	phandle_t child;
81300712Sadrian	pcell_t clock, paddr;
82257064Sloos	device_t childdev;
83257064Sloos
84257064Sloos	sc->dev = dev;
85257064Sloos
86257064Sloos	bus_generic_probe(dev);
87257064Sloos	bus_enumerate_hinted_children(dev);
88257064Sloos
89257064Sloos	/*
90257064Sloos	 * Attach those children represented in the device tree.
91257064Sloos	 */
92257064Sloos	for (child = OF_child(ofw_bus_get_node(dev)); child != 0;
93257064Sloos	    child = OF_peer(child)) {
94257064Sloos		/*
95257064Sloos		 * Try to get the CS number first from the spi-chipselect
96257064Sloos		 * property, then try the reg property.
97257064Sloos		 */
98257064Sloos		if (OF_getencprop(child, "spi-chipselect", &paddr,
99257064Sloos		    sizeof(paddr)) == -1) {
100257064Sloos			if (OF_getencprop(child, "reg", &paddr,
101257064Sloos			    sizeof(paddr)) == -1)
102257064Sloos				continue;
103257064Sloos		}
104257064Sloos
105257064Sloos		/*
106300712Sadrian		 * Get the maximum clock frequency for device, zero means
107300712Sadrian		 * use the default bus speed.
108332942Sian		 *
109332942Sian		 * XXX Note that the current (2018-04-07) dts bindings say that
110332942Sian		 * spi-max-frequency is a required property (but says nothing of
111332942Sian		 * how to interpret a value of zero).
112300712Sadrian		 */
113300712Sadrian		if (OF_getencprop(child, "spi-max-frequency", &clock,
114300712Sadrian		    sizeof(clock)) == -1)
115300712Sadrian			clock = 0;
116300712Sadrian
117300712Sadrian		/*
118257064Sloos		 * Now set up the SPI and OFW bus layer devinfo and add it
119257064Sloos		 * to the bus.
120257064Sloos		 */
121257064Sloos		dinfo = malloc(sizeof(struct ofw_spibus_devinfo), M_DEVBUF,
122257064Sloos		    M_NOWAIT | M_ZERO);
123257064Sloos		if (dinfo == NULL)
124257064Sloos			continue;
125257064Sloos		dinfo->opd_dinfo.cs = paddr;
126300712Sadrian		dinfo->opd_dinfo.clock = clock;
127257064Sloos		if (ofw_bus_gen_setup_devinfo(&dinfo->opd_obdinfo, child) !=
128257064Sloos		    0) {
129257064Sloos			free(dinfo, M_DEVBUF);
130257064Sloos			continue;
131257064Sloos		}
132257064Sloos		childdev = device_add_child(dev, NULL, -1);
133257064Sloos		device_set_ivars(childdev, dinfo);
134257064Sloos	}
135257064Sloos
136257064Sloos	return (bus_generic_attach(dev));
137257064Sloos}
138257064Sloos
139257064Sloosstatic device_t
140257064Sloosofw_spibus_add_child(device_t dev, u_int order, const char *name, int unit)
141257064Sloos{
142257064Sloos	device_t child;
143257064Sloos	struct ofw_spibus_devinfo *devi;
144257064Sloos
145257064Sloos	child = device_add_child_ordered(dev, order, name, unit);
146257064Sloos	if (child == NULL)
147257064Sloos		return (child);
148257064Sloos	devi = malloc(sizeof(struct ofw_spibus_devinfo), M_DEVBUF,
149257064Sloos	    M_NOWAIT | M_ZERO);
150257064Sloos	if (devi == NULL) {
151257064Sloos		device_delete_child(dev, child);
152257064Sloos		return (0);
153257064Sloos	}
154257064Sloos
155257064Sloos	/*
156257064Sloos	 * NULL all the OFW-related parts of the ivars for non-OFW
157257064Sloos	 * children.
158257064Sloos	 */
159257064Sloos	devi->opd_obdinfo.obd_node = -1;
160257064Sloos	devi->opd_obdinfo.obd_name = NULL;
161257064Sloos	devi->opd_obdinfo.obd_compat = NULL;
162257064Sloos	devi->opd_obdinfo.obd_type = NULL;
163257064Sloos	devi->opd_obdinfo.obd_model = NULL;
164257064Sloos
165257064Sloos	device_set_ivars(child, devi);
166257064Sloos
167257064Sloos	return (child);
168257064Sloos}
169257064Sloos
170257064Sloosstatic const struct ofw_bus_devinfo *
171257064Sloosofw_spibus_get_devinfo(device_t bus, device_t dev)
172257064Sloos{
173257064Sloos	struct ofw_spibus_devinfo *dinfo;
174257064Sloos
175257064Sloos	dinfo = device_get_ivars(dev);
176257064Sloos	return (&dinfo->opd_obdinfo);
177257064Sloos}
178257064Sloos
179257064Sloosstatic device_method_t ofw_spibus_methods[] = {
180257064Sloos	/* Device interface */
181257064Sloos	DEVMETHOD(device_probe,		ofw_spibus_probe),
182257064Sloos	DEVMETHOD(device_attach,	ofw_spibus_attach),
183257064Sloos
184257064Sloos	/* Bus interface */
185257064Sloos	DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str),
186257064Sloos	DEVMETHOD(bus_add_child,	ofw_spibus_add_child),
187257064Sloos
188257064Sloos	/* ofw_bus interface */
189257064Sloos	DEVMETHOD(ofw_bus_get_devinfo,	ofw_spibus_get_devinfo),
190257064Sloos	DEVMETHOD(ofw_bus_get_compat,	ofw_bus_gen_get_compat),
191257064Sloos	DEVMETHOD(ofw_bus_get_model,	ofw_bus_gen_get_model),
192257064Sloos	DEVMETHOD(ofw_bus_get_name,	ofw_bus_gen_get_name),
193257064Sloos	DEVMETHOD(ofw_bus_get_node,	ofw_bus_gen_get_node),
194257064Sloos	DEVMETHOD(ofw_bus_get_type,	ofw_bus_gen_get_type),
195257064Sloos
196257064Sloos	DEVMETHOD_END
197257064Sloos};
198257064Sloos
199331722Seadlerstatic devclass_t ofwspibus_devclass;
200257064Sloos
201257064SloosDEFINE_CLASS_1(spibus, ofw_spibus_driver, ofw_spibus_methods,
202257064Sloos    sizeof(struct spibus_softc), spibus_driver);
203331722SeadlerDRIVER_MODULE(ofw_spibus, spi, ofw_spibus_driver, ofwspibus_devclass, 0, 0);
204257064SloosMODULE_VERSION(ofw_spibus, 1);
205257064SloosMODULE_DEPEND(ofw_spibus, spibus, 1, 1, 1);
206