xref: /dflybsd-src/sys/dev/powermng/clockmod/clockmod.c (revision 805c8e8e4093ceca2e27510ad3a66d4de8060a55)
1e76f5e73SSepherosa Ziehau /*
2e76f5e73SSepherosa Ziehau  * Copyright (c) 2014 The DragonFly Project.  All rights reserved.
3e76f5e73SSepherosa Ziehau  *
4e76f5e73SSepherosa Ziehau  * This code is derived from software contributed to The DragonFly Project
5e76f5e73SSepherosa Ziehau  * by Sepherosa Ziehau <sepherosa@gmail.com>
6e76f5e73SSepherosa Ziehau  *
7e76f5e73SSepherosa Ziehau  * Redistribution and use in source and binary forms, with or without
8e76f5e73SSepherosa Ziehau  * modification, are permitted provided that the following conditions
9e76f5e73SSepherosa Ziehau  * are met:
10e76f5e73SSepherosa Ziehau  *
11e76f5e73SSepherosa Ziehau  * 1. Redistributions of source code must retain the above copyright
12e76f5e73SSepherosa Ziehau  *    notice, this list of conditions and the following disclaimer.
13e76f5e73SSepherosa Ziehau  * 2. Redistributions in binary form must reproduce the above copyright
14e76f5e73SSepherosa Ziehau  *    notice, this list of conditions and the following disclaimer in
15e76f5e73SSepherosa Ziehau  *    the documentation and/or other materials provided with the
16e76f5e73SSepherosa Ziehau  *    distribution.
17e76f5e73SSepherosa Ziehau  * 3. Neither the name of The DragonFly Project nor the names of its
18e76f5e73SSepherosa Ziehau  *    contributors may be used to endorse or promote products derived
19e76f5e73SSepherosa Ziehau  *    from this software without specific, prior written permission.
20e76f5e73SSepherosa Ziehau  *
21e76f5e73SSepherosa Ziehau  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22e76f5e73SSepherosa Ziehau  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23e76f5e73SSepherosa Ziehau  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24e76f5e73SSepherosa Ziehau  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25e76f5e73SSepherosa Ziehau  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26e76f5e73SSepherosa Ziehau  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27e76f5e73SSepherosa Ziehau  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28e76f5e73SSepherosa Ziehau  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29e76f5e73SSepherosa Ziehau  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30e76f5e73SSepherosa Ziehau  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31e76f5e73SSepherosa Ziehau  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32e76f5e73SSepherosa Ziehau  * SUCH DAMAGE.
33e76f5e73SSepherosa Ziehau  */
34e76f5e73SSepherosa Ziehau 
35e76f5e73SSepherosa Ziehau #include <sys/param.h>
36e76f5e73SSepherosa Ziehau #include <sys/conf.h>
37e76f5e73SSepherosa Ziehau #include <sys/kernel.h>
38e76f5e73SSepherosa Ziehau #include <sys/bus.h>
39e76f5e73SSepherosa Ziehau #include <sys/cpu_topology.h>
400e4ac8cfSSepherosa Ziehau #include <sys/cpuhelper.h>
41*805c8e8eSzrj #include <sys/malloc.h>
42e76f5e73SSepherosa Ziehau #include <sys/module.h>
43e76f5e73SSepherosa Ziehau #include <sys/queue.h>
44e76f5e73SSepherosa Ziehau #include <sys/serialize.h>
45e76f5e73SSepherosa Ziehau #include <sys/sysctl.h>
46e76f5e73SSepherosa Ziehau #include <sys/systm.h>
47e76f5e73SSepherosa Ziehau 
48e76f5e73SSepherosa Ziehau #include <machine/specialreg.h>
49e76f5e73SSepherosa Ziehau #include <machine/cpufunc.h>
50e76f5e73SSepherosa Ziehau #include <machine/cputypes.h>
51e76f5e73SSepherosa Ziehau #include <machine/md_var.h>
52e76f5e73SSepherosa Ziehau 
53e76f5e73SSepherosa Ziehau struct clockmod_dom;
54e76f5e73SSepherosa Ziehau 
55e76f5e73SSepherosa Ziehau struct clockmod_softc {
56e76f5e73SSepherosa Ziehau 	TAILQ_ENTRY(clockmod_softc) sc_link;
57e76f5e73SSepherosa Ziehau 	struct clockmod_dom	*sc_dom;
58e76f5e73SSepherosa Ziehau 	int			sc_cpuid;
59e76f5e73SSepherosa Ziehau };
60e76f5e73SSepherosa Ziehau 
61e76f5e73SSepherosa Ziehau struct clockmod_dom {
62e76f5e73SSepherosa Ziehau 	TAILQ_ENTRY(clockmod_dom) dom_link;
63e76f5e73SSepherosa Ziehau 	TAILQ_HEAD(, clockmod_softc) dom_list;
64e76f5e73SSepherosa Ziehau 	struct sysctl_ctx_list	dom_sysctl_ctx;
65e76f5e73SSepherosa Ziehau 	struct sysctl_oid	*dom_sysctl_tree;
66e76f5e73SSepherosa Ziehau 	cpumask_t		dom_cpumask;
67e76f5e73SSepherosa Ziehau 	char			dom_name[16];
68e76f5e73SSepherosa Ziehau 	int			dom_select;
69e76f5e73SSepherosa Ziehau 	uint32_t		dom_flags;
70e76f5e73SSepherosa Ziehau };
71e76f5e73SSepherosa Ziehau 
72e76f5e73SSepherosa Ziehau #define CLOCKMOD_DOM_FLAG_ACTIVE	0x1
73e76f5e73SSepherosa Ziehau 
74e76f5e73SSepherosa Ziehau struct clockmod_dom_ctrl {
75e76f5e73SSepherosa Ziehau 	char			ctl_name[8];
76e76f5e73SSepherosa Ziehau 	uint64_t		ctl_value;
77e76f5e73SSepherosa Ziehau };
78e76f5e73SSepherosa Ziehau 
79e76f5e73SSepherosa Ziehau static int	clockmod_dom_attach(struct clockmod_softc *);
80e76f5e73SSepherosa Ziehau static void	clockmod_dom_detach(struct clockmod_softc *);
81e76f5e73SSepherosa Ziehau static struct clockmod_dom *clockmod_dom_find(cpumask_t);
82e76f5e73SSepherosa Ziehau static struct clockmod_dom *clockmod_dom_create(cpumask_t);
83e76f5e73SSepherosa Ziehau static void	clockmod_dom_destroy(struct clockmod_dom *);
84e76f5e73SSepherosa Ziehau 
85e76f5e73SSepherosa Ziehau static int	clockmod_dom_sysctl_select(SYSCTL_HANDLER_ARGS);
86e76f5e73SSepherosa Ziehau static int	clockmod_dom_sysctl_members(SYSCTL_HANDLER_ARGS);
87e76f5e73SSepherosa Ziehau static int	clockmod_dom_sysctl_available(SYSCTL_HANDLER_ARGS);
88e76f5e73SSepherosa Ziehau 
89e76f5e73SSepherosa Ziehau static void	clockmod_identify(driver_t *, device_t);
90e76f5e73SSepherosa Ziehau static int	clockmod_probe(device_t);
91e76f5e73SSepherosa Ziehau static int	clockmod_attach(device_t);
92e76f5e73SSepherosa Ziehau static int	clockmod_detach(device_t);
93e76f5e73SSepherosa Ziehau 
940e4ac8cfSSepherosa Ziehau static void	clockmod_select_handler(struct cpuhelper_msg *);
95e76f5e73SSepherosa Ziehau static int	clockmod_select(const struct clockmod_softc *,
96e76f5e73SSepherosa Ziehau 		    const struct clockmod_dom_ctrl *);
97e76f5e73SSepherosa Ziehau 
98e76f5e73SSepherosa Ziehau static boolean_t clockmod_errata_duty(int);
99e76f5e73SSepherosa Ziehau 
100e76f5e73SSepherosa Ziehau static struct lwkt_serialize clockmod_dom_slize = LWKT_SERIALIZE_INITIALIZER;
101e76f5e73SSepherosa Ziehau static int	clockmod_dom_id;
102e76f5e73SSepherosa Ziehau static TAILQ_HEAD(, clockmod_dom) clockmod_dom_list =
103e76f5e73SSepherosa Ziehau     TAILQ_HEAD_INITIALIZER(clockmod_dom_list);
104e76f5e73SSepherosa Ziehau static int	clockmod_dom_nctrl;
105e76f5e73SSepherosa Ziehau static struct clockmod_dom_ctrl *clockmod_dom_controls;
106e76f5e73SSepherosa Ziehau 
107e76f5e73SSepherosa Ziehau static device_method_t clockmod_methods[] = {
108e76f5e73SSepherosa Ziehau 	/* Device interface */
109e76f5e73SSepherosa Ziehau 	DEVMETHOD(device_identify,	clockmod_identify),
110e76f5e73SSepherosa Ziehau 	DEVMETHOD(device_probe,		clockmod_probe),
111e76f5e73SSepherosa Ziehau 	DEVMETHOD(device_attach,	clockmod_attach),
112e76f5e73SSepherosa Ziehau 	DEVMETHOD(device_detach,	clockmod_detach),
113e76f5e73SSepherosa Ziehau 
114e76f5e73SSepherosa Ziehau 	DEVMETHOD_END
115e76f5e73SSepherosa Ziehau };
116e76f5e73SSepherosa Ziehau 
117e76f5e73SSepherosa Ziehau static driver_t clockmod_driver = {
118e76f5e73SSepherosa Ziehau 	"clockmod",
119e76f5e73SSepherosa Ziehau 	clockmod_methods,
120e76f5e73SSepherosa Ziehau 	sizeof(struct clockmod_softc),
121e76f5e73SSepherosa Ziehau };
122e76f5e73SSepherosa Ziehau 
123e76f5e73SSepherosa Ziehau static devclass_t clockmod_devclass;
124e76f5e73SSepherosa Ziehau DRIVER_MODULE(clockmod, cpu, clockmod_driver, clockmod_devclass, NULL, NULL);
125e76f5e73SSepherosa Ziehau 
126e76f5e73SSepherosa Ziehau static void
clockmod_identify(driver_t * driver,device_t parent)127e76f5e73SSepherosa Ziehau clockmod_identify(driver_t *driver, device_t parent)
128e76f5e73SSepherosa Ziehau {
129e76f5e73SSepherosa Ziehau 	device_t child;
130e76f5e73SSepherosa Ziehau 
131e76f5e73SSepherosa Ziehau 	if (device_find_child(parent, "clockmod", -1) != NULL)
132e76f5e73SSepherosa Ziehau 		return;
133e76f5e73SSepherosa Ziehau 
134e76f5e73SSepherosa Ziehau 	if (cpu_vendor_id != CPU_VENDOR_INTEL)
135e76f5e73SSepherosa Ziehau 		return;
136e76f5e73SSepherosa Ziehau 
137e76f5e73SSepherosa Ziehau 	if ((cpu_feature & (CPUID_ACPI | CPUID_TM)) != (CPUID_ACPI | CPUID_TM))
138e76f5e73SSepherosa Ziehau 		return;
139e76f5e73SSepherosa Ziehau 
140e76f5e73SSepherosa Ziehau 	child = device_add_child(parent, "clockmod", device_get_unit(parent));
141e76f5e73SSepherosa Ziehau 	if (child == NULL)
142e76f5e73SSepherosa Ziehau 		device_printf(parent, "add clockmod failed\n");
143e76f5e73SSepherosa Ziehau }
144e76f5e73SSepherosa Ziehau 
145e76f5e73SSepherosa Ziehau static int
clockmod_probe(device_t dev)146e76f5e73SSepherosa Ziehau clockmod_probe(device_t dev)
147e76f5e73SSepherosa Ziehau {
148e76f5e73SSepherosa Ziehau 	device_set_desc(dev, "CPU clock modulation");
149e76f5e73SSepherosa Ziehau 	return 0;
150e76f5e73SSepherosa Ziehau }
151e76f5e73SSepherosa Ziehau 
152e76f5e73SSepherosa Ziehau static int
clockmod_attach(device_t dev)153e76f5e73SSepherosa Ziehau clockmod_attach(device_t dev)
154e76f5e73SSepherosa Ziehau {
155e76f5e73SSepherosa Ziehau 	struct clockmod_softc *sc = device_get_softc(dev);
156e76f5e73SSepherosa Ziehau 	int error;
157e76f5e73SSepherosa Ziehau 
158e76f5e73SSepherosa Ziehau 	sc->sc_cpuid = device_get_unit(dev);
159e76f5e73SSepherosa Ziehau 
160e76f5e73SSepherosa Ziehau 	error = clockmod_dom_attach(sc);
161e76f5e73SSepherosa Ziehau 	if (error) {
162e76f5e73SSepherosa Ziehau 		device_printf(dev, "domain attach failed\n");
163e76f5e73SSepherosa Ziehau 		return error;
164e76f5e73SSepherosa Ziehau 	}
165e76f5e73SSepherosa Ziehau 
166e76f5e73SSepherosa Ziehau 	return 0;
167e76f5e73SSepherosa Ziehau }
168e76f5e73SSepherosa Ziehau 
169e76f5e73SSepherosa Ziehau static int
clockmod_detach(device_t dev)170e76f5e73SSepherosa Ziehau clockmod_detach(device_t dev)
171e76f5e73SSepherosa Ziehau {
172e76f5e73SSepherosa Ziehau 	clockmod_dom_detach(device_get_softc(dev));
173e76f5e73SSepherosa Ziehau 	return 0;
174e76f5e73SSepherosa Ziehau }
175e76f5e73SSepherosa Ziehau 
176e76f5e73SSepherosa Ziehau static int
clockmod_dom_attach(struct clockmod_softc * sc)177e76f5e73SSepherosa Ziehau clockmod_dom_attach(struct clockmod_softc *sc)
178e76f5e73SSepherosa Ziehau {
179e76f5e73SSepherosa Ziehau 	struct clockmod_softc *sc1;
180e76f5e73SSepherosa Ziehau 	struct clockmod_dom *dom;
181c07315c4SMatthew Dillon 	cpumask_t mask, found_mask;
182e76f5e73SSepherosa Ziehau 	int error = 0;
183e76f5e73SSepherosa Ziehau 
184c07315c4SMatthew Dillon 	CPUMASK_ASSZERO(found_mask);
185c07315c4SMatthew Dillon 
186e76f5e73SSepherosa Ziehau 	mask = get_cpumask_from_level(sc->sc_cpuid, CORE_LEVEL);
187c07315c4SMatthew Dillon 	if (CPUMASK_TESTZERO(mask))
188c07315c4SMatthew Dillon 		CPUMASK_ASSBIT(mask, sc->sc_cpuid);
189e76f5e73SSepherosa Ziehau 
190e76f5e73SSepherosa Ziehau 	lwkt_serialize_enter(&clockmod_dom_slize);
191e76f5e73SSepherosa Ziehau 
192e76f5e73SSepherosa Ziehau 	dom = clockmod_dom_find(mask);
193e76f5e73SSepherosa Ziehau 	if (dom == NULL) {
194e76f5e73SSepherosa Ziehau 		dom = clockmod_dom_create(mask);
195e76f5e73SSepherosa Ziehau 		if (dom == NULL) {
196e76f5e73SSepherosa Ziehau 			error = ENOMEM;
197e76f5e73SSepherosa Ziehau 			goto back;
198e76f5e73SSepherosa Ziehau 		}
199e76f5e73SSepherosa Ziehau 	}
200e76f5e73SSepherosa Ziehau 
201e76f5e73SSepherosa Ziehau 	sc->sc_dom = dom;
202e76f5e73SSepherosa Ziehau 	TAILQ_INSERT_TAIL(&dom->dom_list, sc, sc_link);
203e76f5e73SSepherosa Ziehau 
204e76f5e73SSepherosa Ziehau 	TAILQ_FOREACH(sc1, &dom->dom_list, sc_link)
205c07315c4SMatthew Dillon 		CPUMASK_ORBIT(found_mask, sc1->sc_cpuid);
206e76f5e73SSepherosa Ziehau 
207c07315c4SMatthew Dillon 	if (CPUMASK_CMPMASKEQ(found_mask, dom->dom_cpumask)) {
208e76f5e73SSepherosa Ziehau 		/* All cpus in this domain is found */
209e76f5e73SSepherosa Ziehau 		dom->dom_flags |= CLOCKMOD_DOM_FLAG_ACTIVE;
210e76f5e73SSepherosa Ziehau 	}
211e76f5e73SSepherosa Ziehau back:
212e76f5e73SSepherosa Ziehau 	lwkt_serialize_exit(&clockmod_dom_slize);
213e76f5e73SSepherosa Ziehau 	return error;
214e76f5e73SSepherosa Ziehau }
215e76f5e73SSepherosa Ziehau 
216e76f5e73SSepherosa Ziehau static void
clockmod_dom_detach(struct clockmod_softc * sc)217e76f5e73SSepherosa Ziehau clockmod_dom_detach(struct clockmod_softc *sc)
218e76f5e73SSepherosa Ziehau {
219e76f5e73SSepherosa Ziehau 	struct clockmod_dom *dom;
220e76f5e73SSepherosa Ziehau 
221e76f5e73SSepherosa Ziehau 	lwkt_serialize_enter(&clockmod_dom_slize);
222e76f5e73SSepherosa Ziehau 
223e76f5e73SSepherosa Ziehau 	dom = sc->sc_dom;
224e76f5e73SSepherosa Ziehau 	sc->sc_dom = NULL;
225e76f5e73SSepherosa Ziehau 
226e76f5e73SSepherosa Ziehau 	if (dom->dom_flags & CLOCKMOD_DOM_FLAG_ACTIVE) {
227e76f5e73SSepherosa Ziehau 		struct clockmod_softc *sc1;
228e76f5e73SSepherosa Ziehau 
229e76f5e73SSepherosa Ziehau 		/* Raise to 100% */
230e76f5e73SSepherosa Ziehau 		TAILQ_FOREACH(sc1, &dom->dom_list, sc_link)
231e76f5e73SSepherosa Ziehau 			clockmod_select(sc1, &clockmod_dom_controls[0]);
232e76f5e73SSepherosa Ziehau 	}
233e76f5e73SSepherosa Ziehau 
234e76f5e73SSepherosa Ziehau 	/* One cpu is leaving; domain is no longer active */
235e76f5e73SSepherosa Ziehau 	dom->dom_flags &= ~CLOCKMOD_DOM_FLAG_ACTIVE;
236e76f5e73SSepherosa Ziehau 
237e76f5e73SSepherosa Ziehau 	TAILQ_REMOVE(&dom->dom_list, sc, sc_link);
238e76f5e73SSepherosa Ziehau 	if (TAILQ_EMPTY(&dom->dom_list))
239e76f5e73SSepherosa Ziehau 		clockmod_dom_destroy(dom);
240e76f5e73SSepherosa Ziehau 
241e76f5e73SSepherosa Ziehau 	lwkt_serialize_exit(&clockmod_dom_slize);
242e76f5e73SSepherosa Ziehau }
243e76f5e73SSepherosa Ziehau 
244e76f5e73SSepherosa Ziehau static struct clockmod_dom *
clockmod_dom_find(cpumask_t mask)245e76f5e73SSepherosa Ziehau clockmod_dom_find(cpumask_t mask)
246e76f5e73SSepherosa Ziehau {
247e76f5e73SSepherosa Ziehau 	struct clockmod_dom *dom;
248e76f5e73SSepherosa Ziehau 
249e76f5e73SSepherosa Ziehau 	TAILQ_FOREACH(dom, &clockmod_dom_list, dom_link) {
250c07315c4SMatthew Dillon 		if (CPUMASK_CMPMASKEQ(dom->dom_cpumask, mask))
251e76f5e73SSepherosa Ziehau 			return dom;
252e76f5e73SSepherosa Ziehau 	}
253e76f5e73SSepherosa Ziehau 	return NULL;
254e76f5e73SSepherosa Ziehau }
255e76f5e73SSepherosa Ziehau 
256e76f5e73SSepherosa Ziehau static struct clockmod_dom *
clockmod_dom_create(cpumask_t mask)257e76f5e73SSepherosa Ziehau clockmod_dom_create(cpumask_t mask)
258e76f5e73SSepherosa Ziehau {
259e76f5e73SSepherosa Ziehau 	struct clockmod_dom *dom;
260e76f5e73SSepherosa Ziehau 	int id;
261e76f5e73SSepherosa Ziehau 
262e76f5e73SSepherosa Ziehau 	id = clockmod_dom_id++;
263e76f5e73SSepherosa Ziehau 	dom = kmalloc(sizeof(*dom), M_DEVBUF, M_WAITOK | M_ZERO);
264e76f5e73SSepherosa Ziehau 
265e76f5e73SSepherosa Ziehau 	TAILQ_INIT(&dom->dom_list);
266e76f5e73SSepherosa Ziehau 	dom->dom_cpumask = mask;
267e76f5e73SSepherosa Ziehau 	ksnprintf(dom->dom_name, sizeof(dom->dom_name), "clockmod_dom%d", id);
268e76f5e73SSepherosa Ziehau 
269e76f5e73SSepherosa Ziehau 	sysctl_ctx_init(&dom->dom_sysctl_ctx);
270e76f5e73SSepherosa Ziehau 	dom->dom_sysctl_tree = SYSCTL_ADD_NODE(&dom->dom_sysctl_ctx,
271e76f5e73SSepherosa Ziehau 	    SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, dom->dom_name,
272e76f5e73SSepherosa Ziehau 	    CTLFLAG_RD, 0, "");
273e76f5e73SSepherosa Ziehau 	if (dom->dom_sysctl_tree == NULL) {
274e76f5e73SSepherosa Ziehau 		kprintf("%s: can't add sysctl node\n", dom->dom_name);
275e76f5e73SSepherosa Ziehau 		kfree(dom, M_DEVBUF);
276e76f5e73SSepherosa Ziehau 		return NULL;
277e76f5e73SSepherosa Ziehau 	}
278e76f5e73SSepherosa Ziehau 
279e76f5e73SSepherosa Ziehau 	SYSCTL_ADD_PROC(&dom->dom_sysctl_ctx,
280e76f5e73SSepherosa Ziehau 	    SYSCTL_CHILDREN(dom->dom_sysctl_tree),
281e76f5e73SSepherosa Ziehau 	    OID_AUTO, "members", CTLTYPE_STRING | CTLFLAG_RD,
282e76f5e73SSepherosa Ziehau 	    dom, 0, clockmod_dom_sysctl_members, "A", "member cpus");
283e76f5e73SSepherosa Ziehau 
284e76f5e73SSepherosa Ziehau 	SYSCTL_ADD_PROC(&dom->dom_sysctl_ctx,
285e76f5e73SSepherosa Ziehau 	    SYSCTL_CHILDREN(dom->dom_sysctl_tree),
286e76f5e73SSepherosa Ziehau 	    OID_AUTO, "available", CTLTYPE_STRING | CTLFLAG_RD,
287e76f5e73SSepherosa Ziehau 	    dom, 0, clockmod_dom_sysctl_available, "A",
288e76f5e73SSepherosa Ziehau 	    "available duty percent");
289e76f5e73SSepherosa Ziehau 
290e76f5e73SSepherosa Ziehau 	SYSCTL_ADD_PROC(&dom->dom_sysctl_ctx,
291e76f5e73SSepherosa Ziehau 	    SYSCTL_CHILDREN(dom->dom_sysctl_tree),
292e76f5e73SSepherosa Ziehau 	    OID_AUTO, "select", CTLTYPE_STRING | CTLFLAG_RW,
293e76f5e73SSepherosa Ziehau 	    dom, 0, clockmod_dom_sysctl_select, "A", "select duty");
294e76f5e73SSepherosa Ziehau 
295e76f5e73SSepherosa Ziehau 	TAILQ_INSERT_TAIL(&clockmod_dom_list, dom, dom_link);
296e76f5e73SSepherosa Ziehau 
297e76f5e73SSepherosa Ziehau 	if (clockmod_dom_controls == NULL) {
298e76f5e73SSepherosa Ziehau 		int nctrl, step, i, shift, cnt;
299e76f5e73SSepherosa Ziehau 
300e76f5e73SSepherosa Ziehau #ifdef __x86_64__
301e76f5e73SSepherosa Ziehau 		if (cpu_thermal_feature & CPUID_THERMAL_ECMD)
302e76f5e73SSepherosa Ziehau 			shift = 0;
303e76f5e73SSepherosa Ziehau 		else
304e76f5e73SSepherosa Ziehau #endif
305e76f5e73SSepherosa Ziehau 			shift = 1;
306e76f5e73SSepherosa Ziehau 
307e76f5e73SSepherosa Ziehau 		nctrl = 8 << (1 - shift);
308e76f5e73SSepherosa Ziehau 		step = 10000 / nctrl;
309e76f5e73SSepherosa Ziehau 
310e76f5e73SSepherosa Ziehau 		clockmod_dom_controls =
311e76f5e73SSepherosa Ziehau 		    kmalloc(sizeof(struct clockmod_dom_ctrl) * nctrl, M_DEVBUF,
312e76f5e73SSepherosa Ziehau 		    M_WAITOK | M_ZERO);
313e76f5e73SSepherosa Ziehau 
314e76f5e73SSepherosa Ziehau 		if (bootverbose)
315e76f5e73SSepherosa Ziehau 			kprintf("clock modulation:\n");
316e76f5e73SSepherosa Ziehau 
317e76f5e73SSepherosa Ziehau 		cnt = 0;
318e76f5e73SSepherosa Ziehau 		for (i = 0; i < nctrl; ++i) {
319e76f5e73SSepherosa Ziehau 			struct clockmod_dom_ctrl *ctrl =
320e76f5e73SSepherosa Ziehau 			    &clockmod_dom_controls[cnt];
321e76f5e73SSepherosa Ziehau 			int duty;
322e76f5e73SSepherosa Ziehau 
323e76f5e73SSepherosa Ziehau 			duty = 10000 - (i * step);
324e76f5e73SSepherosa Ziehau 			if (clockmod_errata_duty(duty))
325e76f5e73SSepherosa Ziehau 				continue;
326e76f5e73SSepherosa Ziehau 			++cnt;
327e76f5e73SSepherosa Ziehau 
328e76f5e73SSepherosa Ziehau 			ksnprintf(ctrl->ctl_name, sizeof(ctrl->ctl_name),
329e76f5e73SSepherosa Ziehau 			    "%d.%02d%%", duty / 100, duty % 100);
330e76f5e73SSepherosa Ziehau 			ctrl->ctl_value = (((nctrl - i) << shift) & 0xf);
331e76f5e73SSepherosa Ziehau 			if (i != 0)
332e76f5e73SSepherosa Ziehau 				ctrl->ctl_value |= 1 << 4;
333e76f5e73SSepherosa Ziehau 
334e76f5e73SSepherosa Ziehau 			if (bootverbose) {
335e76f5e73SSepherosa Ziehau 				kprintf("  0x%04jx %s\n",
336e76f5e73SSepherosa Ziehau 				    (uintmax_t)ctrl->ctl_value,
337e76f5e73SSepherosa Ziehau 				    ctrl->ctl_name);
338e76f5e73SSepherosa Ziehau 			}
339e76f5e73SSepherosa Ziehau 		}
340e76f5e73SSepherosa Ziehau 		clockmod_dom_nctrl = cnt;
341e76f5e73SSepherosa Ziehau 	}
342e76f5e73SSepherosa Ziehau 	return dom;
343e76f5e73SSepherosa Ziehau }
344e76f5e73SSepherosa Ziehau 
345e76f5e73SSepherosa Ziehau static void
clockmod_dom_destroy(struct clockmod_dom * dom)346e76f5e73SSepherosa Ziehau clockmod_dom_destroy(struct clockmod_dom *dom)
347e76f5e73SSepherosa Ziehau {
348e76f5e73SSepherosa Ziehau 	KASSERT(TAILQ_EMPTY(&dom->dom_list),
349e76f5e73SSepherosa Ziehau 	    ("%s: still has member cpus", dom->dom_name));
350e76f5e73SSepherosa Ziehau 	TAILQ_REMOVE(&clockmod_dom_list, dom, dom_link);
351e76f5e73SSepherosa Ziehau 
352e76f5e73SSepherosa Ziehau 	sysctl_ctx_free(&dom->dom_sysctl_ctx);
353e76f5e73SSepherosa Ziehau 	kfree(dom, M_DEVBUF);
354e76f5e73SSepherosa Ziehau 
355e76f5e73SSepherosa Ziehau 	if (TAILQ_EMPTY(&clockmod_dom_list)) {
356e76f5e73SSepherosa Ziehau 		clockmod_dom_nctrl = 0;
357e76f5e73SSepherosa Ziehau 		kfree(clockmod_dom_controls, M_DEVBUF);
358e76f5e73SSepherosa Ziehau 		clockmod_dom_controls = NULL;
359e76f5e73SSepherosa Ziehau 	}
360e76f5e73SSepherosa Ziehau }
361e76f5e73SSepherosa Ziehau 
362e76f5e73SSepherosa Ziehau static int
clockmod_dom_sysctl_members(SYSCTL_HANDLER_ARGS)363e76f5e73SSepherosa Ziehau clockmod_dom_sysctl_members(SYSCTL_HANDLER_ARGS)
364e76f5e73SSepherosa Ziehau {
365e76f5e73SSepherosa Ziehau 	struct clockmod_dom *dom = arg1;
366e76f5e73SSepherosa Ziehau 	struct clockmod_softc *sc;
367e76f5e73SSepherosa Ziehau 	int loop, error;
368e76f5e73SSepherosa Ziehau 
369e76f5e73SSepherosa Ziehau 	lwkt_serialize_enter(&clockmod_dom_slize);
370e76f5e73SSepherosa Ziehau 
371e76f5e73SSepherosa Ziehau 	loop = error = 0;
372e76f5e73SSepherosa Ziehau 	TAILQ_FOREACH(sc, &dom->dom_list, sc_link) {
373e76f5e73SSepherosa Ziehau 		char buf[16];
374e76f5e73SSepherosa Ziehau 
375e76f5e73SSepherosa Ziehau 		if (error == 0 && loop)
376e76f5e73SSepherosa Ziehau 			error = SYSCTL_OUT(req, " ", 1);
377e76f5e73SSepherosa Ziehau 		if (error == 0) {
378e76f5e73SSepherosa Ziehau 			ksnprintf(buf, sizeof(buf), "cpu%d", sc->sc_cpuid);
379e76f5e73SSepherosa Ziehau 			error = SYSCTL_OUT(req, buf, strlen(buf));
380e76f5e73SSepherosa Ziehau 		}
381e76f5e73SSepherosa Ziehau 		++loop;
382e76f5e73SSepherosa Ziehau 	}
383e76f5e73SSepherosa Ziehau 
384e76f5e73SSepherosa Ziehau 	lwkt_serialize_exit(&clockmod_dom_slize);
385e76f5e73SSepherosa Ziehau 	return error;
386e76f5e73SSepherosa Ziehau }
387e76f5e73SSepherosa Ziehau 
388e76f5e73SSepherosa Ziehau static int
clockmod_dom_sysctl_available(SYSCTL_HANDLER_ARGS)389e76f5e73SSepherosa Ziehau clockmod_dom_sysctl_available(SYSCTL_HANDLER_ARGS)
390e76f5e73SSepherosa Ziehau {
391e76f5e73SSepherosa Ziehau 	struct clockmod_dom *dom = arg1;
392e76f5e73SSepherosa Ziehau 	int loop, error, i;
393e76f5e73SSepherosa Ziehau 
394e76f5e73SSepherosa Ziehau 	lwkt_serialize_enter(&clockmod_dom_slize);
395e76f5e73SSepherosa Ziehau 
396e76f5e73SSepherosa Ziehau 	if ((dom->dom_flags & CLOCKMOD_DOM_FLAG_ACTIVE) == 0) {
397e76f5e73SSepherosa Ziehau 		error = SYSCTL_OUT(req, " ", 1);
398e76f5e73SSepherosa Ziehau 		goto done;
399e76f5e73SSepherosa Ziehau 	}
400e76f5e73SSepherosa Ziehau 
401e76f5e73SSepherosa Ziehau 	loop = error = 0;
402e76f5e73SSepherosa Ziehau 	for (i = 0; i < clockmod_dom_nctrl; ++i) {
403e76f5e73SSepherosa Ziehau 		if (error == 0 && loop)
404e76f5e73SSepherosa Ziehau 			error = SYSCTL_OUT(req, " ", 1);
405e76f5e73SSepherosa Ziehau 		if (error == 0) {
406e76f5e73SSepherosa Ziehau 			error = SYSCTL_OUT(req,
407e76f5e73SSepherosa Ziehau 			    clockmod_dom_controls[i].ctl_name,
408e76f5e73SSepherosa Ziehau 			    strlen(clockmod_dom_controls[i].ctl_name));
409e76f5e73SSepherosa Ziehau 		}
410e76f5e73SSepherosa Ziehau 		++loop;
411e76f5e73SSepherosa Ziehau 	}
412e76f5e73SSepherosa Ziehau done:
413e76f5e73SSepherosa Ziehau 	lwkt_serialize_exit(&clockmod_dom_slize);
414e76f5e73SSepherosa Ziehau 	return error;
415e76f5e73SSepherosa Ziehau }
416e76f5e73SSepherosa Ziehau 
417e76f5e73SSepherosa Ziehau static int
clockmod_dom_sysctl_select(SYSCTL_HANDLER_ARGS)418e76f5e73SSepherosa Ziehau clockmod_dom_sysctl_select(SYSCTL_HANDLER_ARGS)
419e76f5e73SSepherosa Ziehau {
420e76f5e73SSepherosa Ziehau 	struct clockmod_dom *dom = arg1;
421e76f5e73SSepherosa Ziehau 	struct clockmod_softc *sc;
422e76f5e73SSepherosa Ziehau 	const struct clockmod_dom_ctrl *ctrl = NULL;
423e76f5e73SSepherosa Ziehau 	char duty[16];
424e76f5e73SSepherosa Ziehau 	int error, i;
425e76f5e73SSepherosa Ziehau 
426e76f5e73SSepherosa Ziehau 	lwkt_serialize_enter(&clockmod_dom_slize);
427e76f5e73SSepherosa Ziehau 	KKASSERT(dom->dom_select >= 0 && dom->dom_select < clockmod_dom_nctrl);
428e76f5e73SSepherosa Ziehau 	ksnprintf(duty, sizeof(duty), "%s",
429e76f5e73SSepherosa Ziehau 	    clockmod_dom_controls[dom->dom_select].ctl_name);
430e76f5e73SSepherosa Ziehau 	lwkt_serialize_exit(&clockmod_dom_slize);
431e76f5e73SSepherosa Ziehau 
432e76f5e73SSepherosa Ziehau 	error = sysctl_handle_string(oidp, duty, sizeof(duty), req);
433e76f5e73SSepherosa Ziehau 	if (error != 0 || req->newptr == NULL)
434e76f5e73SSepherosa Ziehau 		return error;
435e76f5e73SSepherosa Ziehau 
436e76f5e73SSepherosa Ziehau 	lwkt_serialize_enter(&clockmod_dom_slize);
437e76f5e73SSepherosa Ziehau 
438e76f5e73SSepherosa Ziehau 	if ((dom->dom_flags & CLOCKMOD_DOM_FLAG_ACTIVE) == 0) {
439e76f5e73SSepherosa Ziehau 		error = EOPNOTSUPP;
440e76f5e73SSepherosa Ziehau 		goto back;
441e76f5e73SSepherosa Ziehau 	}
442e76f5e73SSepherosa Ziehau 
443e76f5e73SSepherosa Ziehau 	for (i = 0; i < clockmod_dom_nctrl; ++i) {
444e76f5e73SSepherosa Ziehau 		ctrl = &clockmod_dom_controls[i];
445e76f5e73SSepherosa Ziehau 		if (strcmp(duty, ctrl->ctl_name) == 0)
446e76f5e73SSepherosa Ziehau 			break;
447e76f5e73SSepherosa Ziehau 	}
448e76f5e73SSepherosa Ziehau 	if (i == clockmod_dom_nctrl) {
449e76f5e73SSepherosa Ziehau 		error = EINVAL;
450e76f5e73SSepherosa Ziehau 		goto back;
451e76f5e73SSepherosa Ziehau 	}
452e76f5e73SSepherosa Ziehau 	dom->dom_select = i;
453e76f5e73SSepherosa Ziehau 
454e76f5e73SSepherosa Ziehau 	TAILQ_FOREACH(sc, &dom->dom_list, sc_link)
455e76f5e73SSepherosa Ziehau 		clockmod_select(sc, ctrl);
456e76f5e73SSepherosa Ziehau back:
457e76f5e73SSepherosa Ziehau 	lwkt_serialize_exit(&clockmod_dom_slize);
458e76f5e73SSepherosa Ziehau 	return error;
459e76f5e73SSepherosa Ziehau }
460e76f5e73SSepherosa Ziehau 
461e76f5e73SSepherosa Ziehau static void
clockmod_select_handler(struct cpuhelper_msg * msg)4620e4ac8cfSSepherosa Ziehau clockmod_select_handler(struct cpuhelper_msg *msg)
463e76f5e73SSepherosa Ziehau {
4640e4ac8cfSSepherosa Ziehau 	uint64_t ctl_value = *((const uint64_t *)msg->ch_cbarg);
465e76f5e73SSepherosa Ziehau 
466e76f5e73SSepherosa Ziehau #if 0
467e76f5e73SSepherosa Ziehau 	if (bootverbose) {
468e76f5e73SSepherosa Ziehau 		kprintf("cpu%d: clockmod 0x%04jx\n", mycpuid,
4690e4ac8cfSSepherosa Ziehau 		    (uintmax_t)ctl_value);
470e76f5e73SSepherosa Ziehau 	}
471e76f5e73SSepherosa Ziehau #endif
4720e4ac8cfSSepherosa Ziehau 	wrmsr(MSR_THERM_CONTROL, ctl_value);
4730e4ac8cfSSepherosa Ziehau 	cpuhelper_replymsg(msg, 0);
474e76f5e73SSepherosa Ziehau }
475e76f5e73SSepherosa Ziehau 
476e76f5e73SSepherosa Ziehau static int
clockmod_select(const struct clockmod_softc * sc,const struct clockmod_dom_ctrl * ctrl)477e76f5e73SSepherosa Ziehau clockmod_select(const struct clockmod_softc *sc,
478e76f5e73SSepherosa Ziehau     const struct clockmod_dom_ctrl *ctrl)
479e76f5e73SSepherosa Ziehau {
4800e4ac8cfSSepherosa Ziehau 	struct cpuhelper_msg msg;
481e76f5e73SSepherosa Ziehau 
4820e4ac8cfSSepherosa Ziehau 	cpuhelper_initmsg(&msg, &curthread->td_msgport,
4830e4ac8cfSSepherosa Ziehau 	    clockmod_select_handler, __DECONST(void *, &ctrl->ctl_value),
4840e4ac8cfSSepherosa Ziehau 	    MSGF_PRIORITY);
4850e4ac8cfSSepherosa Ziehau 	return (cpuhelper_domsg(&msg, sc->sc_cpuid));
486e76f5e73SSepherosa Ziehau }
487e76f5e73SSepherosa Ziehau 
488e76f5e73SSepherosa Ziehau static boolean_t
clockmod_errata_duty(int duty)489e76f5e73SSepherosa Ziehau clockmod_errata_duty(int duty)
490e76f5e73SSepherosa Ziehau {
491e76f5e73SSepherosa Ziehau 	uint32_t model, stepping;
492e76f5e73SSepherosa Ziehau 
493e76f5e73SSepherosa Ziehau 	/*
494e76f5e73SSepherosa Ziehau 	 * This is obtained from the original p4tcc code.
495e76f5e73SSepherosa Ziehau 	 *
496e76f5e73SSepherosa Ziehau 	 * The original errata checking code in p4tcc is obviously wrong.
497e76f5e73SSepherosa Ziehau 	 * However, I am no longer being able to find the errata mentioned
498e76f5e73SSepherosa Ziehau 	 * in the code.  The guess is that the errata only affects family
499e76f5e73SSepherosa Ziehau 	 * 0x0f CPUs, since:
500e76f5e73SSepherosa Ziehau 	 * - The errata applies to only to model 0x00, 0x01 and 0x02 in
501e76f5e73SSepherosa Ziehau 	 *   the original p4tcc code.
502e76f5e73SSepherosa Ziehau 	 * - Software controlled clock modulation has been supported since
503e76f5e73SSepherosa Ziehau 	 *   0f_00 and the model of the oldest family 0x06 CPUs supporting
504e76f5e73SSepherosa Ziehau 	 *   this feature is 0x09.
505e76f5e73SSepherosa Ziehau 	 */
506e76f5e73SSepherosa Ziehau 	if (CPUID_TO_FAMILY(cpu_id) != 0xf)
507e76f5e73SSepherosa Ziehau 		return FALSE;
508e76f5e73SSepherosa Ziehau 
509e76f5e73SSepherosa Ziehau 	model = CPUID_TO_MODEL(cpu_id);
510e76f5e73SSepherosa Ziehau 	stepping = cpu_id & 0xf;
511e76f5e73SSepherosa Ziehau 
512a1efa688SSepherosa Ziehau 	if (model == 0x6) {
513a1efa688SSepherosa Ziehau 		switch (stepping) {
514a1efa688SSepherosa Ziehau 		case 0x2:
515a1efa688SSepherosa Ziehau 		case 0x4:
516a1efa688SSepherosa Ziehau 		case 0x5:
517a1efa688SSepherosa Ziehau 			/* Hang w/ 12.50% and 25.00% */
518a1efa688SSepherosa Ziehau 			if (duty == 1250 || duty == 2500)
519a1efa688SSepherosa Ziehau 				return TRUE;
520a1efa688SSepherosa Ziehau 			break;
521a1efa688SSepherosa Ziehau 		}
522a1efa688SSepherosa Ziehau 	} else if (model == 0x2) {
523e76f5e73SSepherosa Ziehau 		switch (stepping) {
524e76f5e73SSepherosa Ziehau 		case 0x2:
525e76f5e73SSepherosa Ziehau 		case 0x4:
526e76f5e73SSepherosa Ziehau 		case 0x5:
527e76f5e73SSepherosa Ziehau 		case 0x7:
528e76f5e73SSepherosa Ziehau 		case 0x9:
529e76f5e73SSepherosa Ziehau 			/* Hang w/ 12.50% */
530e76f5e73SSepherosa Ziehau 			if (duty == 1250)
531e76f5e73SSepherosa Ziehau 				return TRUE;
532e76f5e73SSepherosa Ziehau 			break;
533e76f5e73SSepherosa Ziehau 		}
534e76f5e73SSepherosa Ziehau 	} else if (model == 0x1) {
535e76f5e73SSepherosa Ziehau 		switch (stepping) {
536e76f5e73SSepherosa Ziehau 		case 0x2:
537e76f5e73SSepherosa Ziehau 		case 0x3:
538e76f5e73SSepherosa Ziehau 			/* Hang w/ 12.50% and 25.00% */
539e76f5e73SSepherosa Ziehau 			if (duty == 1250 || duty == 2500)
540e76f5e73SSepherosa Ziehau 				return TRUE;
541e76f5e73SSepherosa Ziehau 			break;
542e76f5e73SSepherosa Ziehau 		}
543e76f5e73SSepherosa Ziehau 	} else if (model == 0x0) {
544e76f5e73SSepherosa Ziehau 		switch (stepping) {
545e76f5e73SSepherosa Ziehau 		case 0x7:
546e76f5e73SSepherosa Ziehau 		case 0xa:
547e76f5e73SSepherosa Ziehau 			/* Hang w/ 12.50% and 25.00% */
548e76f5e73SSepherosa Ziehau 			if (duty == 1250 || duty == 2500)
549e76f5e73SSepherosa Ziehau 				return TRUE;
550e76f5e73SSepherosa Ziehau 			break;
551e76f5e73SSepherosa Ziehau 		}
552e76f5e73SSepherosa Ziehau 	}
553e76f5e73SSepherosa Ziehau 	return FALSE;
554e76f5e73SSepherosa Ziehau }
555