1/* $NetBSD: acpi_cpu_md.c,v 1.1 2020/12/07 10:57:41 jmcneill Exp $ */
2
3/*-
4 * Copyright (c) 2020 Jared McNeill <jmcneill@invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__KERNEL_RCSID(0, "$NetBSD: acpi_cpu_md.c,v 1.1 2020/12/07 10:57:41 jmcneill Exp $");
31
32#include <sys/param.h>
33#include <sys/bus.h>
34#include <sys/cpu.h>
35#include <sys/cpufreq.h>
36#include <sys/device.h>
37#include <sys/sysctl.h>
38
39#include <dev/acpi/acpica.h>
40#include <dev/acpi/acpivar.h>
41#include <dev/acpi/acpi_cpu.h>
42#include <dev/acpi/acpi_util.h>
43
44static struct sysctllog *acpicpu_log = NULL;
45
46/*
47 * acpicpu_md_match --
48 *
49 * 	Match against an ACPI processor device node (either a device
50 * 	with HID "ACPI0007" or a processor node) and return a pointer
51 * 	to the corresponding CPU device's 'cpu_info' struct.
52 *
53 */
54struct cpu_info *
55acpicpu_md_match(device_t parent, cfdata_t cf, void *aux)
56{
57	struct acpi_attach_args * const aa = aux;
58
59	return acpi_match_cpu_handle(aa->aa_node->ad_handle);
60}
61
62/*
63 * acpicpu_md_attach --
64 *
65 * 	Return a pointer to the CPU device's 'cpu_info' struct
66 * 	corresponding with this device.
67 *
68 */
69struct cpu_info *
70acpicpu_md_attach(device_t parent, device_t self, void *aux)
71{
72	struct acpi_attach_args * const aa = aux;
73
74	return acpi_match_cpu_handle(aa->aa_node->ad_handle);
75}
76
77/*
78 * acpicpu_md_flags --
79 *
80 * 	Return a bitmask of ACPICPU_FLAG_* platform specific quirks.
81 *
82 */
83uint32_t
84acpicpu_md_flags(void)
85{
86	return 0;
87}
88
89/*
90 * acpicpu_md_cstate_start --
91 *
92 * 	Not implemented.
93 *
94 */
95int
96acpicpu_md_cstate_start(struct acpicpu_softc *sc)
97{
98	return EINVAL;
99}
100
101/*
102 * acpicpu_md_cstate_stop --
103 *
104 * 	Not implemented.
105 *
106 */
107int
108acpicpu_md_cstate_stop(void)
109{
110	return EALREADY;
111}
112
113/*
114 * acpicpu_md_cstate_enter --
115 *
116 * 	Not implemented.
117 *
118 */
119void
120acpicpu_md_cstate_enter(int method, int state)
121{
122}
123
124/*
125 * acpicpu_md_pstate_init --
126 *
127 * 	MD initialization for P-state support. Nothing to do here.
128 *
129 */
130int
131acpicpu_md_pstate_init(struct acpicpu_softc *sc)
132{
133	return 0;
134}
135
136/*
137 * acpicpu_md_pstate_sysctl_current --
138 *
139 * 	sysctl(9) callback for retrieving the current CPU frequency.
140 *
141 */
142static int
143acpicpu_md_pstate_sysctl_current(SYSCTLFN_ARGS)
144{
145	struct sysctlnode node;
146	uint32_t freq;
147	int error;
148
149	freq = cpufreq_get(curcpu());
150	if (freq == 0)
151		return ENXIO;
152
153	node = *rnode;
154	node.sysctl_data = &freq;
155
156	error = sysctl_lookup(SYSCTLFN_CALL(&node));
157	if (error || newp == NULL)
158		return error;
159
160	return 0;
161}
162
163/*
164 * acpicpu_md_pstate_sysctl_target --
165 *
166 * 	sysctl(9) callback for setting the target CPU frequency.
167 *
168 */
169static int
170acpicpu_md_pstate_sysctl_target(SYSCTLFN_ARGS)
171{
172	struct sysctlnode node;
173	uint32_t freq;
174	int error;
175
176	freq = cpufreq_get(curcpu());
177	if (freq == 0)
178		return ENXIO;
179
180	node = *rnode;
181	node.sysctl_data = &freq;
182
183	error = sysctl_lookup(SYSCTLFN_CALL(&node));
184	if (error || newp == NULL)
185		return error;
186
187	cpufreq_set_all(freq);
188
189	return 0;
190}
191
192/*
193 * acpicpu_md_pstate_sysctl_available --
194 *
195 * 	sysctl(9) callback for returning a list of supported CPU frequencies.
196 *
197 */
198static int
199acpicpu_md_pstate_sysctl_available(SYSCTLFN_ARGS)
200{
201	struct acpicpu_softc * const sc = rnode->sysctl_data;
202	struct sysctlnode node;
203	char buf[1024];
204	size_t len;
205	uint32_t i;
206	int error;
207
208	memset(buf, 0, sizeof(buf));
209
210	mutex_enter(&sc->sc_mtx);
211	for (len = 0, i = sc->sc_pstate_max; i < sc->sc_pstate_count; i++) {
212		if (sc->sc_pstate[i].ps_freq == 0)
213			continue;
214		if (len >= sizeof(buf))
215			break;
216		len += snprintf(buf + len, sizeof(buf) - len, "%u%s",
217		    sc->sc_pstate[i].ps_freq,
218		    i < (sc->sc_pstate_count - 1) ? " " : "");
219	}
220	mutex_exit(&sc->sc_mtx);
221
222	node = *rnode;
223	node.sysctl_data = buf;
224
225	error = sysctl_lookup(SYSCTLFN_CALL(&node));
226	if (error || newp == NULL)
227		return error;
228
229	return 0;
230}
231
232/*
233 * acpicpu_md_pstate_start --
234 *
235 * 	MD startup for P-state support. Create sysctls here.
236 *
237 */
238int
239acpicpu_md_pstate_start(struct acpicpu_softc *sc)
240{
241	const struct sysctlnode *mnode, *cnode, *fnode, *node;
242	int error;
243
244	error = sysctl_createv(&acpicpu_log, 0, NULL, &mnode,
245	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
246	    NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL);
247	if (error) {
248		goto teardown;
249	}
250
251	error = sysctl_createv(&acpicpu_log, 0, &mnode, &cnode,
252	    0, CTLTYPE_NODE, "cpu", NULL,
253	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
254	if (error) {
255		goto teardown;
256	}
257
258	error = sysctl_createv(&acpicpu_log, 0, &cnode, &fnode,
259	    0, CTLTYPE_NODE, "frequency", NULL,
260	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
261	if (error) {
262		goto teardown;
263	}
264
265	error = sysctl_createv(&acpicpu_log, 0, &fnode, &node,
266	    CTLFLAG_READWRITE, CTLTYPE_INT, "target", NULL,
267	    acpicpu_md_pstate_sysctl_target, 0, NULL, 0, CTL_CREATE, CTL_EOL);
268	if (error) {
269		goto teardown;
270	}
271
272	error = sysctl_createv(&acpicpu_log, 0, &fnode, &node,
273	    CTLFLAG_READONLY, CTLTYPE_INT, "current", NULL,
274	    acpicpu_md_pstate_sysctl_current, 0, NULL, 0, CTL_CREATE, CTL_EOL);
275	if (error) {
276		goto teardown;
277	}
278
279	error = sysctl_createv(&acpicpu_log, 0, &fnode, &node,
280	    CTLFLAG_READONLY, CTLTYPE_STRING, "available", NULL,
281	    acpicpu_md_pstate_sysctl_available, 0, (void *)sc, 0, CTL_CREATE,
282	    CTL_EOL);
283	if (error) {
284		goto teardown;
285	}
286
287	return 0;
288
289teardown:
290	if (acpicpu_log != NULL) {
291		sysctl_teardown(&acpicpu_log);
292		acpicpu_log = NULL;
293	}
294
295	return error;
296}
297
298/*
299 * acpicpu_md_pstate_stop --
300 *
301 * 	MD shutdown for P-state support. Destroy sysctls here.
302 *
303 */
304int
305acpicpu_md_pstate_stop(void)
306{
307	if (acpicpu_log != NULL) {
308		sysctl_teardown(&acpicpu_log);
309		acpicpu_log = NULL;
310	}
311
312	return 0;
313}
314
315/*
316 * acpicpu_md_pstate_get --
317 *
318 * 	Fixed hardware access method for getting current processor P-state.
319 * 	Not implemented.
320 *
321 */
322int
323acpicpu_md_pstate_get(struct acpicpu_softc *sc, uint32_t *freq)
324{
325	return EINVAL;
326}
327
328/*
329 * acpicpu_md_pstate_set --
330 *
331 * 	Fixed hardware access method for setting current processor P-state.
332 * 	Not implemented.
333 *
334 */
335int
336acpicpu_md_pstate_set(struct acpicpu_pstate *ps)
337{
338	return EINVAL;
339}
340
341/*
342 * acpicpu_md_tstate_get --
343 *
344 * 	Fixed hardware access method for getting current processor T-state.
345 * 	Not implemented.
346 *
347 */
348int
349acpicpu_md_tstate_get(struct acpicpu_softc *sc, uint32_t *percent)
350{
351	return EINVAL;
352}
353
354/*
355 * acpicpu_md_tstate_set --
356 *
357 * 	Fixed hardware access method for setting current processor T-state.
358 * 	Not implemented.
359 *
360 */
361int
362acpicpu_md_tstate_set(struct acpicpu_tstate *ts)
363{
364	return EINVAL;
365}
366