1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2021 Brandon Bergren <bdragon@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
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/kernel.h>
33#include <sys/module.h>
34#include <sys/bus.h>
35
36#include <dev/ofw/ofw_bus.h>
37#include <dev/ofw/openfirm.h>
38
39#include <powerpc/powermac/macgpiovar.h>
40#include <powerpc/powermac/platform_powermac.h>
41
42static int	tbgpio_probe(device_t);
43static int	tbgpio_attach(device_t);
44static void	tbgpio_freeze_timebase(device_t, bool);
45
46static device_method_t	tbgpio_methods[] = {
47	/* Device interface */
48	DEVMETHOD(device_probe,		tbgpio_probe),
49	DEVMETHOD(device_attach,	tbgpio_attach),
50	DEVMETHOD_END
51};
52
53struct tbgpio_softc {
54	uint32_t	sc_value;
55	uint32_t	sc_mask;
56};
57
58static driver_t tbgpio_driver = {
59	"tbgpio",
60	tbgpio_methods,
61	sizeof(struct tbgpio_softc)
62};
63
64EARLY_DRIVER_MODULE(tbgpio, macgpio, tbgpio_driver, 0, 0, BUS_PASS_CPU);
65
66static int
67tbgpio_probe(device_t dev)
68{
69	phandle_t node;
70	const char *name;
71	pcell_t pfunc[32];
72	int res;
73
74	name = ofw_bus_get_name(dev);
75	node = ofw_bus_get_node(dev);
76
77	if (strcmp(name, "timebase-enable") != 0)
78		return (ENXIO);
79
80	res = OF_getencprop(node, "platform-do-cpu-timebase", pfunc,
81	    sizeof(pfunc));
82	if (res == -1)
83		return (ENXIO);
84
85	/*
86	 * If this doesn't look like a simple gpio_write pfunc,
87	 * complain about it so we can collect the pfunc.
88	 */
89	if (res != 20 || pfunc[2] != 0x01) {
90		printf("\nUnknown platform function detected!\n");
91		printf("Please send a PR including the following data:\n");
92		printf("===================\n");
93		printf("Func: platform-do-cpu-timebase\n");
94		hexdump(pfunc, res, NULL, HD_OMIT_CHARS);
95		printf("===================\n");
96		return (ENXIO);
97	}
98
99	device_set_desc(dev, "CPU Timebase Control");
100	return (BUS_PROBE_SPECIFIC);
101}
102
103static int
104tbgpio_attach(device_t dev)
105{
106	phandle_t node;
107	struct tbgpio_softc *sc;
108
109	/*
110	 * Structure of pfunc:
111	 * pfunc[0]: phandle to /cpus
112	 * pfunc[1]: flags
113	 * pfunc[2]: 0x1 == CMD_WRITE_GPIO
114	 * pfunc[3]: value
115	 * pfunc[4]: mask
116	 */
117	pcell_t pfunc[5];
118
119	sc = device_get_softc(dev);
120	node = ofw_bus_get_node(dev);
121
122	OF_getencprop(node, "platform-do-cpu-timebase", pfunc, sizeof(pfunc));
123
124	sc->sc_value = pfunc[3];
125	sc->sc_mask = pfunc[4];
126
127	powermac_register_timebase(dev, tbgpio_freeze_timebase);
128	return (0);
129}
130
131static void
132tbgpio_freeze_timebase(device_t dev, bool freeze)
133{
134	struct tbgpio_softc *sc;
135	uint32_t val;
136
137	sc = device_get_softc(dev);
138
139	val = sc->sc_value;
140	if (freeze)
141		val = ~val;
142	val &= sc->sc_mask;
143
144	macgpio_write(dev, val);
145}
146