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
42 static int tbgpio_probe(device_t);
43 static int tbgpio_attach(device_t);
44 static void tbgpio_freeze_timebase(device_t, bool);
45
46 static 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
53 struct tbgpio_softc {
54 uint32_t sc_value;
55 uint32_t sc_mask;
56 };
57
58 static driver_t tbgpio_driver = {
59 "tbgpio",
60 tbgpio_methods,
61 sizeof(struct tbgpio_softc)
62 };
63
64 EARLY_DRIVER_MODULE(tbgpio, macgpio, tbgpio_driver, 0, 0, BUS_PASS_CPU);
65
66 static int
tbgpio_probe(device_t dev)67 tbgpio_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
103 static int
tbgpio_attach(device_t dev)104 tbgpio_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
131 static void
tbgpio_freeze_timebase(device_t dev,bool freeze)132 tbgpio_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