1/*-
2 * Copyright (c) 2015 Nathan Whitehorn
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/module.h>
33#include <sys/bus.h>
34#include <sys/conf.h>
35#include <sys/clock.h>
36#include <sys/cpu.h>
37#include <sys/kernel.h>
38#include <sys/reboot.h>
39#include <sys/sysctl.h>
40#include <sys/endian.h>
41
42#include <vm/vm.h>
43#include <vm/pmap.h>
44
45#include <dev/ofw/ofw_bus.h>
46#include <dev/ofw/ofw_bus_subr.h>
47#include <dev/ofw/openfirm.h>
48
49#include "clock_if.h"
50#include "opal.h"
51
52static int	opaldev_probe(device_t);
53static int	opaldev_attach(device_t);
54/* clock interface */
55static int	opal_gettime(device_t dev, struct timespec *ts);
56static int	opal_settime(device_t dev, struct timespec *ts);
57/* ofw bus interface */
58static const struct ofw_bus_devinfo *opaldev_get_devinfo(device_t dev,
59    device_t child);
60
61static void	opal_shutdown(void *arg, int howto);
62static void	opal_intr(void *);
63
64static device_method_t  opaldev_methods[] = {
65	/* Device interface */
66	DEVMETHOD(device_probe,		opaldev_probe),
67	DEVMETHOD(device_attach,	opaldev_attach),
68
69	/* clock interface */
70	DEVMETHOD(clock_gettime,	opal_gettime),
71	DEVMETHOD(clock_settime,	opal_settime),
72
73        /* ofw_bus interface */
74	DEVMETHOD(ofw_bus_get_devinfo,	opaldev_get_devinfo),
75	DEVMETHOD(ofw_bus_get_compat,	ofw_bus_gen_get_compat),
76	DEVMETHOD(ofw_bus_get_model,	ofw_bus_gen_get_model),
77	DEVMETHOD(ofw_bus_get_name,	ofw_bus_gen_get_name),
78	DEVMETHOD(ofw_bus_get_node,	ofw_bus_gen_get_node),
79	DEVMETHOD(ofw_bus_get_type,	ofw_bus_gen_get_type),
80
81	DEVMETHOD_END
82};
83
84static driver_t opaldev_driver = {
85	"opal",
86	opaldev_methods,
87	0
88};
89
90static devclass_t opaldev_devclass;
91
92DRIVER_MODULE(opaldev, ofwbus, opaldev_driver, opaldev_devclass, 0, 0);
93
94static int
95opaldev_probe(device_t dev)
96{
97	phandle_t iparent;
98	pcell_t *irqs;
99	int i, n_irqs;
100
101	if (!ofw_bus_is_compatible(dev, "ibm,opal-v3"))
102		return (ENXIO);
103	if (opal_check() != 0)
104		return (ENXIO);
105
106	device_set_desc(dev, "OPAL Abstraction Firmware");
107
108	/* Manually add IRQs before attaching */
109	if (OF_hasprop(ofw_bus_get_node(dev), "opal-interrupts")) {
110		iparent = OF_finddevice("/interrupt-controller@0");
111		iparent = OF_xref_from_node(iparent);
112
113		n_irqs = OF_getproplen(ofw_bus_get_node(dev),
114                    "opal-interrupts") / sizeof(*irqs);
115		irqs = malloc(n_irqs * sizeof(*irqs), M_DEVBUF, M_WAITOK);
116		OF_getencprop(ofw_bus_get_node(dev), "opal-interrupts", irqs,
117		    n_irqs * sizeof(*irqs));
118		for (i = 0; i < n_irqs; i++)
119			bus_set_resource(dev, SYS_RES_IRQ, i,
120			    ofw_bus_map_intr(dev, iparent, 1, &irqs[i]), 1);
121		free(irqs, M_DEVBUF);
122	}
123
124
125	return (BUS_PROBE_SPECIFIC);
126}
127
128static int
129opaldev_attach(device_t dev)
130{
131	phandle_t child;
132	device_t cdev;
133	uint64_t junk;
134	int i, rv;
135	struct ofw_bus_devinfo *dinfo;
136	struct resource *irq;
137
138	/* Test for RTC support and register clock if it works */
139	rv = opal_call(OPAL_RTC_READ, vtophys(&junk), vtophys(&junk));
140	do {
141		rv = opal_call(OPAL_RTC_READ, vtophys(&junk), vtophys(&junk));
142		if (rv == OPAL_BUSY_EVENT)
143			rv = opal_call(OPAL_POLL_EVENTS, 0);
144	} while (rv == OPAL_BUSY_EVENT);
145
146	if (rv == OPAL_SUCCESS)
147		clock_register(dev, 2000);
148
149	EVENTHANDLER_REGISTER(shutdown_final, opal_shutdown, NULL,
150	    SHUTDOWN_PRI_LAST);
151
152	/* Bind to interrupts */
153	for (i = 0; (irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &i,
154	    RF_ACTIVE)) != NULL; i++)
155		bus_setup_intr(dev, irq, INTR_TYPE_TTY | INTR_MPSAFE |
156		    INTR_ENTROPY, NULL, opal_intr, (void *)rman_get_start(irq),
157		    NULL);
158
159	for (child = OF_child(ofw_bus_get_node(dev)); child != 0;
160	    child = OF_peer(child)) {
161		dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_WAITOK | M_ZERO);
162		if (ofw_bus_gen_setup_devinfo(dinfo, child) != 0) {
163			free(dinfo, M_DEVBUF);
164			continue;
165		}
166		cdev = device_add_child(dev, NULL, -1);
167		if (cdev == NULL) {
168			device_printf(dev, "<%s>: device_add_child failed\n",
169			    dinfo->obd_name);
170			ofw_bus_gen_destroy_devinfo(dinfo);
171			free(dinfo, M_DEVBUF);
172			continue;
173		}
174		device_set_ivars(cdev, dinfo);
175	}
176
177	return (bus_generic_attach(dev));
178}
179
180static int
181bcd2bin32(int bcd)
182{
183	int out = 0;
184
185	out += bcd2bin(bcd & 0xff);
186	out += 100*bcd2bin((bcd & 0x0000ff00) >> 8);
187	out += 10000*bcd2bin((bcd & 0x00ff0000) >> 16);
188	out += 1000000*bcd2bin((bcd & 0xffff0000) >> 24);
189
190	return (out);
191}
192
193static int
194bin2bcd32(int bin)
195{
196	int out = 0;
197	int tmp;
198
199	tmp = bin % 100;
200	out += bin2bcd(tmp) * 1;
201	bin = bin / 100;
202
203	tmp = bin % 100;
204	out += bin2bcd(tmp) * 100;
205	bin = bin / 100;
206
207	tmp = bin % 100;
208	out += bin2bcd(tmp) * 10000;
209
210	return (out);
211}
212
213static int
214opal_gettime(device_t dev, struct timespec *ts)
215{
216	int rv;
217	struct clocktime ct;
218	uint32_t ymd;
219	uint64_t hmsm;
220
221	rv = opal_call(OPAL_RTC_READ, vtophys(&ymd), vtophys(&hmsm));
222	while (rv == OPAL_BUSY_EVENT)  {
223		opal_call(OPAL_POLL_EVENTS, 0);
224		pause("opalrtc", 1);
225		rv = opal_call(OPAL_RTC_READ, vtophys(&ymd), vtophys(&hmsm));
226	}
227
228	if (rv != OPAL_SUCCESS)
229		return (ENXIO);
230
231	hmsm = be64toh(hmsm);
232	ymd = be32toh(ymd);
233
234	ct.nsec	= bcd2bin32((hmsm & 0x000000ffffff0000) >> 16) * 1000;
235	ct.sec	= bcd2bin((hmsm & 0x0000ff0000000000) >> 40);
236	ct.min	= bcd2bin((hmsm & 0x00ff000000000000) >> 48);
237	ct.hour	= bcd2bin((hmsm & 0xff00000000000000) >> 56);
238
239	ct.day	= bcd2bin((ymd & 0x000000ff) >> 0);
240	ct.mon	= bcd2bin((ymd & 0x0000ff00) >> 8);
241	ct.year	= bcd2bin32((ymd & 0xffff0000) >> 16);
242
243	return (clock_ct_to_ts(&ct, ts));
244}
245
246static int
247opal_settime(device_t dev, struct timespec *ts)
248{
249	int rv;
250	struct clocktime ct;
251	uint32_t ymd = 0;
252	uint64_t hmsm = 0;
253
254	clock_ts_to_ct(ts, &ct);
255
256	ymd |= (uint32_t)bin2bcd(ct.day);
257	ymd |= ((uint32_t)bin2bcd(ct.mon) << 8);
258	ymd |= ((uint32_t)bin2bcd32(ct.year) << 16);
259
260	hmsm |= ((uint64_t)bin2bcd32(ct.nsec/1000) << 16);
261	hmsm |= ((uint64_t)bin2bcd(ct.sec) << 40);
262	hmsm |= ((uint64_t)bin2bcd(ct.min) << 48);
263	hmsm |= ((uint64_t)bin2bcd(ct.hour) << 56);
264
265	hmsm = htobe64(hmsm);
266	ymd = htobe32(ymd);
267
268	do {
269		rv = opal_call(OPAL_RTC_WRITE, vtophys(&ymd), vtophys(&hmsm));
270		if (rv == OPAL_BUSY_EVENT) {
271			rv = opal_call(OPAL_POLL_EVENTS, 0);
272			pause("opalrtc", 1);
273		}
274	} while (rv == OPAL_BUSY_EVENT);
275
276	if (rv != OPAL_SUCCESS)
277		return (ENXIO);
278
279	return (0);
280}
281
282static const struct ofw_bus_devinfo *
283opaldev_get_devinfo(device_t dev, device_t child)
284{
285	return (device_get_ivars(child));
286}
287
288static void
289opal_shutdown(void *arg, int howto)
290{
291
292	if (howto & RB_HALT)
293		opal_call(OPAL_CEC_POWER_DOWN, 0 /* Normal power off */);
294	else
295		opal_call(OPAL_CEC_REBOOT);
296
297	opal_call(OPAL_RETURN_CPU);
298}
299
300static void
301opal_intr(void *xintr)
302{
303	uint64_t events = 0;
304
305	opal_call(OPAL_HANDLE_INTERRUPT, (uint32_t)(uint64_t)xintr,
306	    vtophys(&events));
307	/* XXX: do something useful with this information */
308
309}
310
311