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$");
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
72257064Sloos	return (0);
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;
81257064Sloos	pcell_t 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		/*
106257064Sloos		 * Now set up the SPI and OFW bus layer devinfo and add it
107257064Sloos		 * to the bus.
108257064Sloos		 */
109257064Sloos		dinfo = malloc(sizeof(struct ofw_spibus_devinfo), M_DEVBUF,
110257064Sloos		    M_NOWAIT | M_ZERO);
111257064Sloos		if (dinfo == NULL)
112257064Sloos			continue;
113257064Sloos		dinfo->opd_dinfo.cs = paddr;
114257064Sloos		if (ofw_bus_gen_setup_devinfo(&dinfo->opd_obdinfo, child) !=
115257064Sloos		    0) {
116257064Sloos			free(dinfo, M_DEVBUF);
117257064Sloos			continue;
118257064Sloos		}
119257064Sloos		childdev = device_add_child(dev, NULL, -1);
120257064Sloos		device_set_ivars(childdev, dinfo);
121257064Sloos	}
122257064Sloos
123257064Sloos	return (bus_generic_attach(dev));
124257064Sloos}
125257064Sloos
126257064Sloosstatic device_t
127257064Sloosofw_spibus_add_child(device_t dev, u_int order, const char *name, int unit)
128257064Sloos{
129257064Sloos	device_t child;
130257064Sloos	struct ofw_spibus_devinfo *devi;
131257064Sloos
132257064Sloos	child = device_add_child_ordered(dev, order, name, unit);
133257064Sloos	if (child == NULL)
134257064Sloos		return (child);
135257064Sloos	devi = malloc(sizeof(struct ofw_spibus_devinfo), M_DEVBUF,
136257064Sloos	    M_NOWAIT | M_ZERO);
137257064Sloos	if (devi == NULL) {
138257064Sloos		device_delete_child(dev, child);
139257064Sloos		return (0);
140257064Sloos	}
141257064Sloos
142257064Sloos	/*
143257064Sloos	 * NULL all the OFW-related parts of the ivars for non-OFW
144257064Sloos	 * children.
145257064Sloos	 */
146257064Sloos	devi->opd_obdinfo.obd_node = -1;
147257064Sloos	devi->opd_obdinfo.obd_name = NULL;
148257064Sloos	devi->opd_obdinfo.obd_compat = NULL;
149257064Sloos	devi->opd_obdinfo.obd_type = NULL;
150257064Sloos	devi->opd_obdinfo.obd_model = NULL;
151257064Sloos
152257064Sloos	device_set_ivars(child, devi);
153257064Sloos
154257064Sloos	return (child);
155257064Sloos}
156257064Sloos
157257064Sloosstatic const struct ofw_bus_devinfo *
158257064Sloosofw_spibus_get_devinfo(device_t bus, device_t dev)
159257064Sloos{
160257064Sloos	struct ofw_spibus_devinfo *dinfo;
161257064Sloos
162257064Sloos	dinfo = device_get_ivars(dev);
163257064Sloos	return (&dinfo->opd_obdinfo);
164257064Sloos}
165257064Sloos
166257064Sloosstatic device_method_t ofw_spibus_methods[] = {
167257064Sloos	/* Device interface */
168257064Sloos	DEVMETHOD(device_probe,		ofw_spibus_probe),
169257064Sloos	DEVMETHOD(device_attach,	ofw_spibus_attach),
170257064Sloos
171257064Sloos	/* Bus interface */
172257064Sloos	DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str),
173257064Sloos	DEVMETHOD(bus_add_child,	ofw_spibus_add_child),
174257064Sloos
175257064Sloos	/* ofw_bus interface */
176257064Sloos	DEVMETHOD(ofw_bus_get_devinfo,	ofw_spibus_get_devinfo),
177257064Sloos	DEVMETHOD(ofw_bus_get_compat,	ofw_bus_gen_get_compat),
178257064Sloos	DEVMETHOD(ofw_bus_get_model,	ofw_bus_gen_get_model),
179257064Sloos	DEVMETHOD(ofw_bus_get_name,	ofw_bus_gen_get_name),
180257064Sloos	DEVMETHOD(ofw_bus_get_node,	ofw_bus_gen_get_node),
181257064Sloos	DEVMETHOD(ofw_bus_get_type,	ofw_bus_gen_get_type),
182257064Sloos
183257064Sloos	DEVMETHOD_END
184257064Sloos};
185257064Sloos
186257064Sloosstatic devclass_t ofwspibus_devclass;
187257064Sloos
188257064SloosDEFINE_CLASS_1(spibus, ofw_spibus_driver, ofw_spibus_methods,
189257064Sloos    sizeof(struct spibus_softc), spibus_driver);
190257064SloosDRIVER_MODULE(ofw_spibus, spi, ofw_spibus_driver, ofwspibus_devclass, 0, 0);
191257064SloosMODULE_VERSION(ofw_spibus, 1);
192257064SloosMODULE_DEPEND(ofw_spibus, spibus, 1, 1, 1);
193