xref: /dflybsd-src/sys/dev/powermng/clockmod/clockmod.c (revision c07315c47f12ac7242a5409623dcfbb82f8168ab)
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>
40e76f5e73SSepherosa Ziehau #include <sys/module.h>
41e76f5e73SSepherosa Ziehau #include <sys/queue.h>
42e76f5e73SSepherosa Ziehau #include <sys/serialize.h>
43e76f5e73SSepherosa Ziehau #include <sys/sysctl.h>
44e76f5e73SSepherosa Ziehau #include <sys/systm.h>
45e76f5e73SSepherosa Ziehau 
46e76f5e73SSepherosa Ziehau #include <net/netmsg2.h>
47e76f5e73SSepherosa Ziehau #include <net/netisr2.h>
48e76f5e73SSepherosa Ziehau 
49e76f5e73SSepherosa Ziehau #include <machine/specialreg.h>
50e76f5e73SSepherosa Ziehau #include <machine/cpufunc.h>
51e76f5e73SSepherosa Ziehau #include <machine/cputypes.h>
52e76f5e73SSepherosa Ziehau #include <machine/md_var.h>
53e76f5e73SSepherosa Ziehau 
54e76f5e73SSepherosa Ziehau struct clockmod_dom;
55e76f5e73SSepherosa Ziehau 
56e76f5e73SSepherosa Ziehau struct netmsg_clockmod {
57e76f5e73SSepherosa Ziehau 	struct netmsg_base	base;
58e76f5e73SSepherosa Ziehau 	uint64_t		ctl_value;
59e76f5e73SSepherosa Ziehau };
60e76f5e73SSepherosa Ziehau 
61e76f5e73SSepherosa Ziehau struct clockmod_softc {
62e76f5e73SSepherosa Ziehau 	TAILQ_ENTRY(clockmod_softc) sc_link;
63e76f5e73SSepherosa Ziehau 	struct clockmod_dom	*sc_dom;
64e76f5e73SSepherosa Ziehau 	int			sc_cpuid;
65e76f5e73SSepherosa Ziehau };
66e76f5e73SSepherosa Ziehau 
67e76f5e73SSepherosa Ziehau struct clockmod_dom {
68e76f5e73SSepherosa Ziehau 	TAILQ_ENTRY(clockmod_dom) dom_link;
69e76f5e73SSepherosa Ziehau 	TAILQ_HEAD(, clockmod_softc) dom_list;
70e76f5e73SSepherosa Ziehau 	struct sysctl_ctx_list	dom_sysctl_ctx;
71e76f5e73SSepherosa Ziehau 	struct sysctl_oid	*dom_sysctl_tree;
72e76f5e73SSepherosa Ziehau 	cpumask_t		dom_cpumask;
73e76f5e73SSepherosa Ziehau 	char			dom_name[16];
74e76f5e73SSepherosa Ziehau 	int			dom_select;
75e76f5e73SSepherosa Ziehau 	uint32_t		dom_flags;
76e76f5e73SSepherosa Ziehau };
77e76f5e73SSepherosa Ziehau 
78e76f5e73SSepherosa Ziehau #define CLOCKMOD_DOM_FLAG_ACTIVE	0x1
79e76f5e73SSepherosa Ziehau 
80e76f5e73SSepherosa Ziehau struct clockmod_dom_ctrl {
81e76f5e73SSepherosa Ziehau 	char			ctl_name[8];
82e76f5e73SSepherosa Ziehau 	uint64_t		ctl_value;
83e76f5e73SSepherosa Ziehau };
84e76f5e73SSepherosa Ziehau 
85e76f5e73SSepherosa Ziehau static int	clockmod_dom_attach(struct clockmod_softc *);
86e76f5e73SSepherosa Ziehau static void	clockmod_dom_detach(struct clockmod_softc *);
87e76f5e73SSepherosa Ziehau static struct clockmod_dom *clockmod_dom_find(cpumask_t);
88e76f5e73SSepherosa Ziehau static struct clockmod_dom *clockmod_dom_create(cpumask_t);
89e76f5e73SSepherosa Ziehau static void	clockmod_dom_destroy(struct clockmod_dom *);
90e76f5e73SSepherosa Ziehau 
91e76f5e73SSepherosa Ziehau static int	clockmod_dom_sysctl_select(SYSCTL_HANDLER_ARGS);
92e76f5e73SSepherosa Ziehau static int	clockmod_dom_sysctl_members(SYSCTL_HANDLER_ARGS);
93e76f5e73SSepherosa Ziehau static int	clockmod_dom_sysctl_available(SYSCTL_HANDLER_ARGS);
94e76f5e73SSepherosa Ziehau 
95e76f5e73SSepherosa Ziehau static void	clockmod_identify(driver_t *, device_t);
96e76f5e73SSepherosa Ziehau static int	clockmod_probe(device_t);
97e76f5e73SSepherosa Ziehau static int	clockmod_attach(device_t);
98e76f5e73SSepherosa Ziehau static int	clockmod_detach(device_t);
99e76f5e73SSepherosa Ziehau 
100e76f5e73SSepherosa Ziehau static void	clockmod_select_handler(netmsg_t);
101e76f5e73SSepherosa Ziehau static int	clockmod_select(const struct clockmod_softc *,
102e76f5e73SSepherosa Ziehau 		    const struct clockmod_dom_ctrl *);
103e76f5e73SSepherosa Ziehau 
104e76f5e73SSepherosa Ziehau static boolean_t clockmod_errata_duty(int);
105e76f5e73SSepherosa Ziehau 
106e76f5e73SSepherosa Ziehau static struct lwkt_serialize clockmod_dom_slize = LWKT_SERIALIZE_INITIALIZER;
107e76f5e73SSepherosa Ziehau static int	clockmod_dom_id;
108e76f5e73SSepherosa Ziehau static TAILQ_HEAD(, clockmod_dom) clockmod_dom_list =
109e76f5e73SSepherosa Ziehau     TAILQ_HEAD_INITIALIZER(clockmod_dom_list);
110e76f5e73SSepherosa Ziehau static int	clockmod_dom_nctrl;
111e76f5e73SSepherosa Ziehau static struct clockmod_dom_ctrl *clockmod_dom_controls;
112e76f5e73SSepherosa Ziehau 
113e76f5e73SSepherosa Ziehau static device_method_t clockmod_methods[] = {
114e76f5e73SSepherosa Ziehau 	/* Device interface */
115e76f5e73SSepherosa Ziehau 	DEVMETHOD(device_identify,	clockmod_identify),
116e76f5e73SSepherosa Ziehau 	DEVMETHOD(device_probe,		clockmod_probe),
117e76f5e73SSepherosa Ziehau 	DEVMETHOD(device_attach,	clockmod_attach),
118e76f5e73SSepherosa Ziehau 	DEVMETHOD(device_detach,	clockmod_detach),
119e76f5e73SSepherosa Ziehau 
120e76f5e73SSepherosa Ziehau 	DEVMETHOD_END
121e76f5e73SSepherosa Ziehau };
122e76f5e73SSepherosa Ziehau 
123e76f5e73SSepherosa Ziehau static driver_t clockmod_driver = {
124e76f5e73SSepherosa Ziehau 	"clockmod",
125e76f5e73SSepherosa Ziehau 	clockmod_methods,
126e76f5e73SSepherosa Ziehau 	sizeof(struct clockmod_softc),
127e76f5e73SSepherosa Ziehau };
128e76f5e73SSepherosa Ziehau 
129e76f5e73SSepherosa Ziehau static devclass_t clockmod_devclass;
130e76f5e73SSepherosa Ziehau DRIVER_MODULE(clockmod, cpu, clockmod_driver, clockmod_devclass, NULL, NULL);
131e76f5e73SSepherosa Ziehau 
132e76f5e73SSepherosa Ziehau static void
133e76f5e73SSepherosa Ziehau clockmod_identify(driver_t *driver, device_t parent)
134e76f5e73SSepherosa Ziehau {
135e76f5e73SSepherosa Ziehau 	device_t child;
136e76f5e73SSepherosa Ziehau 
137e76f5e73SSepherosa Ziehau 	if (device_find_child(parent, "clockmod", -1) != NULL)
138e76f5e73SSepherosa Ziehau 		return;
139e76f5e73SSepherosa Ziehau 
140e76f5e73SSepherosa Ziehau 	if (cpu_vendor_id != CPU_VENDOR_INTEL)
141e76f5e73SSepherosa Ziehau 		return;
142e76f5e73SSepherosa Ziehau 
143e76f5e73SSepherosa Ziehau 	if ((cpu_feature & (CPUID_ACPI | CPUID_TM)) != (CPUID_ACPI | CPUID_TM))
144e76f5e73SSepherosa Ziehau 		return;
145e76f5e73SSepherosa Ziehau 
146e76f5e73SSepherosa Ziehau 	child = device_add_child(parent, "clockmod", device_get_unit(parent));
147e76f5e73SSepherosa Ziehau 	if (child == NULL)
148e76f5e73SSepherosa Ziehau 		device_printf(parent, "add clockmod failed\n");
149e76f5e73SSepherosa Ziehau }
150e76f5e73SSepherosa Ziehau 
151e76f5e73SSepherosa Ziehau static int
152e76f5e73SSepherosa Ziehau clockmod_probe(device_t dev)
153e76f5e73SSepherosa Ziehau {
154e76f5e73SSepherosa Ziehau 	device_set_desc(dev, "CPU clock modulation");
155e76f5e73SSepherosa Ziehau 	return 0;
156e76f5e73SSepherosa Ziehau }
157e76f5e73SSepherosa Ziehau 
158e76f5e73SSepherosa Ziehau static int
159e76f5e73SSepherosa Ziehau clockmod_attach(device_t dev)
160e76f5e73SSepherosa Ziehau {
161e76f5e73SSepherosa Ziehau 	struct clockmod_softc *sc = device_get_softc(dev);
162e76f5e73SSepherosa Ziehau 	int error;
163e76f5e73SSepherosa Ziehau 
164e76f5e73SSepherosa Ziehau 	sc->sc_cpuid = device_get_unit(dev);
165e76f5e73SSepherosa Ziehau 
166e76f5e73SSepherosa Ziehau 	error = clockmod_dom_attach(sc);
167e76f5e73SSepherosa Ziehau 	if (error) {
168e76f5e73SSepherosa Ziehau 		device_printf(dev, "domain attach failed\n");
169e76f5e73SSepherosa Ziehau 		return error;
170e76f5e73SSepherosa Ziehau 	}
171e76f5e73SSepherosa Ziehau 
172e76f5e73SSepherosa Ziehau 	return 0;
173e76f5e73SSepherosa Ziehau }
174e76f5e73SSepherosa Ziehau 
175e76f5e73SSepherosa Ziehau static int
176e76f5e73SSepherosa Ziehau clockmod_detach(device_t dev)
177e76f5e73SSepherosa Ziehau {
178e76f5e73SSepherosa Ziehau 	clockmod_dom_detach(device_get_softc(dev));
179e76f5e73SSepherosa Ziehau 	return 0;
180e76f5e73SSepherosa Ziehau }
181e76f5e73SSepherosa Ziehau 
182e76f5e73SSepherosa Ziehau static int
183e76f5e73SSepherosa Ziehau clockmod_dom_attach(struct clockmod_softc *sc)
184e76f5e73SSepherosa Ziehau {
185e76f5e73SSepherosa Ziehau 	struct clockmod_softc *sc1;
186e76f5e73SSepherosa Ziehau 	struct clockmod_dom *dom;
187*c07315c4SMatthew Dillon 	cpumask_t mask, found_mask;
188e76f5e73SSepherosa Ziehau 	int error = 0;
189e76f5e73SSepherosa Ziehau 
190*c07315c4SMatthew Dillon 	CPUMASK_ASSZERO(found_mask);
191*c07315c4SMatthew Dillon 
192e76f5e73SSepherosa Ziehau 	mask = get_cpumask_from_level(sc->sc_cpuid, CORE_LEVEL);
193*c07315c4SMatthew Dillon 	if (CPUMASK_TESTZERO(mask))
194*c07315c4SMatthew Dillon 		CPUMASK_ASSBIT(mask, sc->sc_cpuid);
195e76f5e73SSepherosa Ziehau 
196e76f5e73SSepherosa Ziehau 	lwkt_serialize_enter(&clockmod_dom_slize);
197e76f5e73SSepherosa Ziehau 
198e76f5e73SSepherosa Ziehau 	dom = clockmod_dom_find(mask);
199e76f5e73SSepherosa Ziehau 	if (dom == NULL) {
200e76f5e73SSepherosa Ziehau 		dom = clockmod_dom_create(mask);
201e76f5e73SSepherosa Ziehau 		if (dom == NULL) {
202e76f5e73SSepherosa Ziehau 			error = ENOMEM;
203e76f5e73SSepherosa Ziehau 			goto back;
204e76f5e73SSepherosa Ziehau 		}
205e76f5e73SSepherosa Ziehau 	}
206e76f5e73SSepherosa Ziehau 
207e76f5e73SSepherosa Ziehau 	sc->sc_dom = dom;
208e76f5e73SSepherosa Ziehau 	TAILQ_INSERT_TAIL(&dom->dom_list, sc, sc_link);
209e76f5e73SSepherosa Ziehau 
210e76f5e73SSepherosa Ziehau 	TAILQ_FOREACH(sc1, &dom->dom_list, sc_link)
211*c07315c4SMatthew Dillon 		CPUMASK_ORBIT(found_mask, sc1->sc_cpuid);
212e76f5e73SSepherosa Ziehau 
213*c07315c4SMatthew Dillon 	if (CPUMASK_CMPMASKEQ(found_mask, dom->dom_cpumask)) {
214e76f5e73SSepherosa Ziehau 		/* All cpus in this domain is found */
215e76f5e73SSepherosa Ziehau 		dom->dom_flags |= CLOCKMOD_DOM_FLAG_ACTIVE;
216e76f5e73SSepherosa Ziehau 	}
217e76f5e73SSepherosa Ziehau back:
218e76f5e73SSepherosa Ziehau 	lwkt_serialize_exit(&clockmod_dom_slize);
219e76f5e73SSepherosa Ziehau 	return error;
220e76f5e73SSepherosa Ziehau }
221e76f5e73SSepherosa Ziehau 
222e76f5e73SSepherosa Ziehau static void
223e76f5e73SSepherosa Ziehau clockmod_dom_detach(struct clockmod_softc *sc)
224e76f5e73SSepherosa Ziehau {
225e76f5e73SSepherosa Ziehau 	struct clockmod_dom *dom;
226e76f5e73SSepherosa Ziehau 
227e76f5e73SSepherosa Ziehau 	lwkt_serialize_enter(&clockmod_dom_slize);
228e76f5e73SSepherosa Ziehau 
229e76f5e73SSepherosa Ziehau 	dom = sc->sc_dom;
230e76f5e73SSepherosa Ziehau 	sc->sc_dom = NULL;
231e76f5e73SSepherosa Ziehau 
232e76f5e73SSepherosa Ziehau 	if (dom->dom_flags & CLOCKMOD_DOM_FLAG_ACTIVE) {
233e76f5e73SSepherosa Ziehau 		struct clockmod_softc *sc1;
234e76f5e73SSepherosa Ziehau 
235e76f5e73SSepherosa Ziehau 		/* Raise to 100% */
236e76f5e73SSepherosa Ziehau 		TAILQ_FOREACH(sc1, &dom->dom_list, sc_link)
237e76f5e73SSepherosa Ziehau 			clockmod_select(sc1, &clockmod_dom_controls[0]);
238e76f5e73SSepherosa Ziehau 	}
239e76f5e73SSepherosa Ziehau 
240e76f5e73SSepherosa Ziehau 	/* One cpu is leaving; domain is no longer active */
241e76f5e73SSepherosa Ziehau 	dom->dom_flags &= ~CLOCKMOD_DOM_FLAG_ACTIVE;
242e76f5e73SSepherosa Ziehau 
243e76f5e73SSepherosa Ziehau 	TAILQ_REMOVE(&dom->dom_list, sc, sc_link);
244e76f5e73SSepherosa Ziehau 	if (TAILQ_EMPTY(&dom->dom_list))
245e76f5e73SSepherosa Ziehau 		clockmod_dom_destroy(dom);
246e76f5e73SSepherosa Ziehau 
247e76f5e73SSepherosa Ziehau 	lwkt_serialize_exit(&clockmod_dom_slize);
248e76f5e73SSepherosa Ziehau }
249e76f5e73SSepherosa Ziehau 
250e76f5e73SSepherosa Ziehau static struct clockmod_dom *
251e76f5e73SSepherosa Ziehau clockmod_dom_find(cpumask_t mask)
252e76f5e73SSepherosa Ziehau {
253e76f5e73SSepherosa Ziehau 	struct clockmod_dom *dom;
254e76f5e73SSepherosa Ziehau 
255e76f5e73SSepherosa Ziehau 	TAILQ_FOREACH(dom, &clockmod_dom_list, dom_link) {
256*c07315c4SMatthew Dillon 		if (CPUMASK_CMPMASKEQ(dom->dom_cpumask, mask))
257e76f5e73SSepherosa Ziehau 			return dom;
258e76f5e73SSepherosa Ziehau 	}
259e76f5e73SSepherosa Ziehau 	return NULL;
260e76f5e73SSepherosa Ziehau }
261e76f5e73SSepherosa Ziehau 
262e76f5e73SSepherosa Ziehau static struct clockmod_dom *
263e76f5e73SSepherosa Ziehau clockmod_dom_create(cpumask_t mask)
264e76f5e73SSepherosa Ziehau {
265e76f5e73SSepherosa Ziehau 	struct clockmod_dom *dom;
266e76f5e73SSepherosa Ziehau 	int id;
267e76f5e73SSepherosa Ziehau 
268e76f5e73SSepherosa Ziehau 	id = clockmod_dom_id++;
269e76f5e73SSepherosa Ziehau 	dom = kmalloc(sizeof(*dom), M_DEVBUF, M_WAITOK | M_ZERO);
270e76f5e73SSepherosa Ziehau 
271e76f5e73SSepherosa Ziehau 	TAILQ_INIT(&dom->dom_list);
272e76f5e73SSepherosa Ziehau 	dom->dom_cpumask = mask;
273e76f5e73SSepherosa Ziehau 	ksnprintf(dom->dom_name, sizeof(dom->dom_name), "clockmod_dom%d", id);
274e76f5e73SSepherosa Ziehau 
275e76f5e73SSepherosa Ziehau 	sysctl_ctx_init(&dom->dom_sysctl_ctx);
276e76f5e73SSepherosa Ziehau 	dom->dom_sysctl_tree = SYSCTL_ADD_NODE(&dom->dom_sysctl_ctx,
277e76f5e73SSepherosa Ziehau 	    SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, dom->dom_name,
278e76f5e73SSepherosa Ziehau 	    CTLFLAG_RD, 0, "");
279e76f5e73SSepherosa Ziehau 	if (dom->dom_sysctl_tree == NULL) {
280e76f5e73SSepherosa Ziehau 		kprintf("%s: can't add sysctl node\n", dom->dom_name);
281e76f5e73SSepherosa Ziehau 		kfree(dom, M_DEVBUF);
282e76f5e73SSepherosa Ziehau 		return NULL;
283e76f5e73SSepherosa Ziehau 	}
284e76f5e73SSepherosa Ziehau 
285e76f5e73SSepherosa Ziehau 	SYSCTL_ADD_PROC(&dom->dom_sysctl_ctx,
286e76f5e73SSepherosa Ziehau 	    SYSCTL_CHILDREN(dom->dom_sysctl_tree),
287e76f5e73SSepherosa Ziehau 	    OID_AUTO, "members", CTLTYPE_STRING | CTLFLAG_RD,
288e76f5e73SSepherosa Ziehau 	    dom, 0, clockmod_dom_sysctl_members, "A", "member cpus");
289e76f5e73SSepherosa Ziehau 
290e76f5e73SSepherosa Ziehau 	SYSCTL_ADD_PROC(&dom->dom_sysctl_ctx,
291e76f5e73SSepherosa Ziehau 	    SYSCTL_CHILDREN(dom->dom_sysctl_tree),
292e76f5e73SSepherosa Ziehau 	    OID_AUTO, "available", CTLTYPE_STRING | CTLFLAG_RD,
293e76f5e73SSepherosa Ziehau 	    dom, 0, clockmod_dom_sysctl_available, "A",
294e76f5e73SSepherosa Ziehau 	    "available duty percent");
295e76f5e73SSepherosa Ziehau 
296e76f5e73SSepherosa Ziehau 	SYSCTL_ADD_PROC(&dom->dom_sysctl_ctx,
297e76f5e73SSepherosa Ziehau 	    SYSCTL_CHILDREN(dom->dom_sysctl_tree),
298e76f5e73SSepherosa Ziehau 	    OID_AUTO, "select", CTLTYPE_STRING | CTLFLAG_RW,
299e76f5e73SSepherosa Ziehau 	    dom, 0, clockmod_dom_sysctl_select, "A", "select duty");
300e76f5e73SSepherosa Ziehau 
301e76f5e73SSepherosa Ziehau 	TAILQ_INSERT_TAIL(&clockmod_dom_list, dom, dom_link);
302e76f5e73SSepherosa Ziehau 
303e76f5e73SSepherosa Ziehau 	if (clockmod_dom_controls == NULL) {
304e76f5e73SSepherosa Ziehau 		int nctrl, step, i, shift, cnt;
305e76f5e73SSepherosa Ziehau 
306e76f5e73SSepherosa Ziehau #ifdef __x86_64__
307e76f5e73SSepherosa Ziehau 		if (cpu_thermal_feature & CPUID_THERMAL_ECMD)
308e76f5e73SSepherosa Ziehau 			shift = 0;
309e76f5e73SSepherosa Ziehau 		else
310e76f5e73SSepherosa Ziehau #endif
311e76f5e73SSepherosa Ziehau 			shift = 1;
312e76f5e73SSepherosa Ziehau 
313e76f5e73SSepherosa Ziehau 		nctrl = 8 << (1 - shift);
314e76f5e73SSepherosa Ziehau 		step = 10000 / nctrl;
315e76f5e73SSepherosa Ziehau 
316e76f5e73SSepherosa Ziehau 		clockmod_dom_controls =
317e76f5e73SSepherosa Ziehau 		    kmalloc(sizeof(struct clockmod_dom_ctrl) * nctrl, M_DEVBUF,
318e76f5e73SSepherosa Ziehau 		    M_WAITOK | M_ZERO);
319e76f5e73SSepherosa Ziehau 
320e76f5e73SSepherosa Ziehau 		if (bootverbose)
321e76f5e73SSepherosa Ziehau 			kprintf("clock modulation:\n");
322e76f5e73SSepherosa Ziehau 
323e76f5e73SSepherosa Ziehau 		cnt = 0;
324e76f5e73SSepherosa Ziehau 		for (i = 0; i < nctrl; ++i) {
325e76f5e73SSepherosa Ziehau 			struct clockmod_dom_ctrl *ctrl =
326e76f5e73SSepherosa Ziehau 			    &clockmod_dom_controls[cnt];
327e76f5e73SSepherosa Ziehau 			int duty;
328e76f5e73SSepherosa Ziehau 
329e76f5e73SSepherosa Ziehau 			duty = 10000 - (i * step);
330e76f5e73SSepherosa Ziehau 			if (clockmod_errata_duty(duty))
331e76f5e73SSepherosa Ziehau 				continue;
332e76f5e73SSepherosa Ziehau 			++cnt;
333e76f5e73SSepherosa Ziehau 
334e76f5e73SSepherosa Ziehau 			ksnprintf(ctrl->ctl_name, sizeof(ctrl->ctl_name),
335e76f5e73SSepherosa Ziehau 			    "%d.%02d%%", duty / 100, duty % 100);
336e76f5e73SSepherosa Ziehau 			ctrl->ctl_value = (((nctrl - i) << shift) & 0xf);
337e76f5e73SSepherosa Ziehau 			if (i != 0)
338e76f5e73SSepherosa Ziehau 				ctrl->ctl_value |= 1 << 4;
339e76f5e73SSepherosa Ziehau 
340e76f5e73SSepherosa Ziehau 			if (bootverbose) {
341e76f5e73SSepherosa Ziehau 				kprintf("  0x%04jx %s\n",
342e76f5e73SSepherosa Ziehau 				    (uintmax_t)ctrl->ctl_value,
343e76f5e73SSepherosa Ziehau 				    ctrl->ctl_name);
344e76f5e73SSepherosa Ziehau 			}
345e76f5e73SSepherosa Ziehau 		}
346e76f5e73SSepherosa Ziehau 		clockmod_dom_nctrl = cnt;
347e76f5e73SSepherosa Ziehau 	}
348e76f5e73SSepherosa Ziehau 	return dom;
349e76f5e73SSepherosa Ziehau }
350e76f5e73SSepherosa Ziehau 
351e76f5e73SSepherosa Ziehau static void
352e76f5e73SSepherosa Ziehau clockmod_dom_destroy(struct clockmod_dom *dom)
353e76f5e73SSepherosa Ziehau {
354e76f5e73SSepherosa Ziehau 	KASSERT(TAILQ_EMPTY(&dom->dom_list),
355e76f5e73SSepherosa Ziehau 	    ("%s: still has member cpus", dom->dom_name));
356e76f5e73SSepherosa Ziehau 	TAILQ_REMOVE(&clockmod_dom_list, dom, dom_link);
357e76f5e73SSepherosa Ziehau 
358e76f5e73SSepherosa Ziehau 	sysctl_ctx_free(&dom->dom_sysctl_ctx);
359e76f5e73SSepherosa Ziehau 	kfree(dom, M_DEVBUF);
360e76f5e73SSepherosa Ziehau 
361e76f5e73SSepherosa Ziehau 	if (TAILQ_EMPTY(&clockmod_dom_list)) {
362e76f5e73SSepherosa Ziehau 		clockmod_dom_nctrl = 0;
363e76f5e73SSepherosa Ziehau 		kfree(clockmod_dom_controls, M_DEVBUF);
364e76f5e73SSepherosa Ziehau 		clockmod_dom_controls = NULL;
365e76f5e73SSepherosa Ziehau 	}
366e76f5e73SSepherosa Ziehau }
367e76f5e73SSepherosa Ziehau 
368e76f5e73SSepherosa Ziehau static int
369e76f5e73SSepherosa Ziehau clockmod_dom_sysctl_members(SYSCTL_HANDLER_ARGS)
370e76f5e73SSepherosa Ziehau {
371e76f5e73SSepherosa Ziehau 	struct clockmod_dom *dom = arg1;
372e76f5e73SSepherosa Ziehau 	struct clockmod_softc *sc;
373e76f5e73SSepherosa Ziehau 	int loop, error;
374e76f5e73SSepherosa Ziehau 
375e76f5e73SSepherosa Ziehau 	lwkt_serialize_enter(&clockmod_dom_slize);
376e76f5e73SSepherosa Ziehau 
377e76f5e73SSepherosa Ziehau 	loop = error = 0;
378e76f5e73SSepherosa Ziehau 	TAILQ_FOREACH(sc, &dom->dom_list, sc_link) {
379e76f5e73SSepherosa Ziehau 		char buf[16];
380e76f5e73SSepherosa Ziehau 
381e76f5e73SSepherosa Ziehau 		if (error == 0 && loop)
382e76f5e73SSepherosa Ziehau 			error = SYSCTL_OUT(req, " ", 1);
383e76f5e73SSepherosa Ziehau 		if (error == 0) {
384e76f5e73SSepherosa Ziehau 			ksnprintf(buf, sizeof(buf), "cpu%d", sc->sc_cpuid);
385e76f5e73SSepherosa Ziehau 			error = SYSCTL_OUT(req, buf, strlen(buf));
386e76f5e73SSepherosa Ziehau 		}
387e76f5e73SSepherosa Ziehau 		++loop;
388e76f5e73SSepherosa Ziehau 	}
389e76f5e73SSepherosa Ziehau 
390e76f5e73SSepherosa Ziehau 	lwkt_serialize_exit(&clockmod_dom_slize);
391e76f5e73SSepherosa Ziehau 	return error;
392e76f5e73SSepherosa Ziehau }
393e76f5e73SSepherosa Ziehau 
394e76f5e73SSepherosa Ziehau static int
395e76f5e73SSepherosa Ziehau clockmod_dom_sysctl_available(SYSCTL_HANDLER_ARGS)
396e76f5e73SSepherosa Ziehau {
397e76f5e73SSepherosa Ziehau 	struct clockmod_dom *dom = arg1;
398e76f5e73SSepherosa Ziehau 	int loop, error, i;
399e76f5e73SSepherosa Ziehau 
400e76f5e73SSepherosa Ziehau 	lwkt_serialize_enter(&clockmod_dom_slize);
401e76f5e73SSepherosa Ziehau 
402e76f5e73SSepherosa Ziehau 	if ((dom->dom_flags & CLOCKMOD_DOM_FLAG_ACTIVE) == 0) {
403e76f5e73SSepherosa Ziehau 		error = SYSCTL_OUT(req, " ", 1);
404e76f5e73SSepherosa Ziehau 		goto done;
405e76f5e73SSepherosa Ziehau 	}
406e76f5e73SSepherosa Ziehau 
407e76f5e73SSepherosa Ziehau 	loop = error = 0;
408e76f5e73SSepherosa Ziehau 	for (i = 0; i < clockmod_dom_nctrl; ++i) {
409e76f5e73SSepherosa Ziehau 		if (error == 0 && loop)
410e76f5e73SSepherosa Ziehau 			error = SYSCTL_OUT(req, " ", 1);
411e76f5e73SSepherosa Ziehau 		if (error == 0) {
412e76f5e73SSepherosa Ziehau 			error = SYSCTL_OUT(req,
413e76f5e73SSepherosa Ziehau 			    clockmod_dom_controls[i].ctl_name,
414e76f5e73SSepherosa Ziehau 			    strlen(clockmod_dom_controls[i].ctl_name));
415e76f5e73SSepherosa Ziehau 		}
416e76f5e73SSepherosa Ziehau 		++loop;
417e76f5e73SSepherosa Ziehau 	}
418e76f5e73SSepherosa Ziehau done:
419e76f5e73SSepherosa Ziehau 	lwkt_serialize_exit(&clockmod_dom_slize);
420e76f5e73SSepherosa Ziehau 	return error;
421e76f5e73SSepherosa Ziehau }
422e76f5e73SSepherosa Ziehau 
423e76f5e73SSepherosa Ziehau static int
424e76f5e73SSepherosa Ziehau clockmod_dom_sysctl_select(SYSCTL_HANDLER_ARGS)
425e76f5e73SSepherosa Ziehau {
426e76f5e73SSepherosa Ziehau 	struct clockmod_dom *dom = arg1;
427e76f5e73SSepherosa Ziehau 	struct clockmod_softc *sc;
428e76f5e73SSepherosa Ziehau 	const struct clockmod_dom_ctrl *ctrl = NULL;
429e76f5e73SSepherosa Ziehau 	char duty[16];
430e76f5e73SSepherosa Ziehau 	int error, i;
431e76f5e73SSepherosa Ziehau 
432e76f5e73SSepherosa Ziehau 	lwkt_serialize_enter(&clockmod_dom_slize);
433e76f5e73SSepherosa Ziehau 	KKASSERT(dom->dom_select >= 0 && dom->dom_select < clockmod_dom_nctrl);
434e76f5e73SSepherosa Ziehau 	ksnprintf(duty, sizeof(duty), "%s",
435e76f5e73SSepherosa Ziehau 	    clockmod_dom_controls[dom->dom_select].ctl_name);
436e76f5e73SSepherosa Ziehau 	lwkt_serialize_exit(&clockmod_dom_slize);
437e76f5e73SSepherosa Ziehau 
438e76f5e73SSepherosa Ziehau 	error = sysctl_handle_string(oidp, duty, sizeof(duty), req);
439e76f5e73SSepherosa Ziehau 	if (error != 0 || req->newptr == NULL)
440e76f5e73SSepherosa Ziehau 		return error;
441e76f5e73SSepherosa Ziehau 
442e76f5e73SSepherosa Ziehau 	lwkt_serialize_enter(&clockmod_dom_slize);
443e76f5e73SSepherosa Ziehau 
444e76f5e73SSepherosa Ziehau 	if ((dom->dom_flags & CLOCKMOD_DOM_FLAG_ACTIVE) == 0) {
445e76f5e73SSepherosa Ziehau 		error = EOPNOTSUPP;
446e76f5e73SSepherosa Ziehau 		goto back;
447e76f5e73SSepherosa Ziehau 	}
448e76f5e73SSepherosa Ziehau 
449e76f5e73SSepherosa Ziehau 	for (i = 0; i < clockmod_dom_nctrl; ++i) {
450e76f5e73SSepherosa Ziehau 		ctrl = &clockmod_dom_controls[i];
451e76f5e73SSepherosa Ziehau 		if (strcmp(duty, ctrl->ctl_name) == 0)
452e76f5e73SSepherosa Ziehau 			break;
453e76f5e73SSepherosa Ziehau 	}
454e76f5e73SSepherosa Ziehau 	if (i == clockmod_dom_nctrl) {
455e76f5e73SSepherosa Ziehau 		error = EINVAL;
456e76f5e73SSepherosa Ziehau 		goto back;
457e76f5e73SSepherosa Ziehau 	}
458e76f5e73SSepherosa Ziehau 	dom->dom_select = i;
459e76f5e73SSepherosa Ziehau 
460e76f5e73SSepherosa Ziehau 	TAILQ_FOREACH(sc, &dom->dom_list, sc_link)
461e76f5e73SSepherosa Ziehau 		clockmod_select(sc, ctrl);
462e76f5e73SSepherosa Ziehau back:
463e76f5e73SSepherosa Ziehau 	lwkt_serialize_exit(&clockmod_dom_slize);
464e76f5e73SSepherosa Ziehau 	return error;
465e76f5e73SSepherosa Ziehau }
466e76f5e73SSepherosa Ziehau 
467e76f5e73SSepherosa Ziehau static void
468e76f5e73SSepherosa Ziehau clockmod_select_handler(netmsg_t msg)
469e76f5e73SSepherosa Ziehau {
470e76f5e73SSepherosa Ziehau 	struct netmsg_clockmod *cmsg = (struct netmsg_clockmod *)msg;
471e76f5e73SSepherosa Ziehau 
472e76f5e73SSepherosa Ziehau #if 0
473e76f5e73SSepherosa Ziehau 	if (bootverbose) {
474e76f5e73SSepherosa Ziehau 		kprintf("cpu%d: clockmod 0x%04jx\n", mycpuid,
475e76f5e73SSepherosa Ziehau 		    (uintmax_t)cmsg->ctl_value);
476e76f5e73SSepherosa Ziehau 	}
477e76f5e73SSepherosa Ziehau #endif
478e76f5e73SSepherosa Ziehau 
479e76f5e73SSepherosa Ziehau 	wrmsr(MSR_THERM_CONTROL, cmsg->ctl_value);
480e76f5e73SSepherosa Ziehau 	lwkt_replymsg(&cmsg->base.lmsg, 0);
481e76f5e73SSepherosa Ziehau }
482e76f5e73SSepherosa Ziehau 
483e76f5e73SSepherosa Ziehau static int
484e76f5e73SSepherosa Ziehau clockmod_select(const struct clockmod_softc *sc,
485e76f5e73SSepherosa Ziehau     const struct clockmod_dom_ctrl *ctrl)
486e76f5e73SSepherosa Ziehau {
487e76f5e73SSepherosa Ziehau 	struct netmsg_clockmod msg;
488e76f5e73SSepherosa Ziehau 
489e76f5e73SSepherosa Ziehau 	netmsg_init(&msg.base, NULL, &curthread->td_msgport, MSGF_PRIORITY,
490e76f5e73SSepherosa Ziehau 	    clockmod_select_handler);
491e76f5e73SSepherosa Ziehau 	msg.ctl_value = ctrl->ctl_value;
492e76f5e73SSepherosa Ziehau 	return lwkt_domsg(netisr_cpuport(sc->sc_cpuid), &msg.base.lmsg, 0);
493e76f5e73SSepherosa Ziehau }
494e76f5e73SSepherosa Ziehau 
495e76f5e73SSepherosa Ziehau static boolean_t
496e76f5e73SSepherosa Ziehau clockmod_errata_duty(int duty)
497e76f5e73SSepherosa Ziehau {
498e76f5e73SSepherosa Ziehau 	uint32_t model, stepping;
499e76f5e73SSepherosa Ziehau 
500e76f5e73SSepherosa Ziehau 	/*
501e76f5e73SSepherosa Ziehau 	 * This is obtained from the original p4tcc code.
502e76f5e73SSepherosa Ziehau 	 *
503e76f5e73SSepherosa Ziehau 	 * The original errata checking code in p4tcc is obviously wrong.
504e76f5e73SSepherosa Ziehau 	 * However, I am no longer being able to find the errata mentioned
505e76f5e73SSepherosa Ziehau 	 * in the code.  The guess is that the errata only affects family
506e76f5e73SSepherosa Ziehau 	 * 0x0f CPUs, since:
507e76f5e73SSepherosa Ziehau 	 * - The errata applies to only to model 0x00, 0x01 and 0x02 in
508e76f5e73SSepherosa Ziehau 	 *   the original p4tcc code.
509e76f5e73SSepherosa Ziehau 	 * - Software controlled clock modulation has been supported since
510e76f5e73SSepherosa Ziehau 	 *   0f_00 and the model of the oldest family 0x06 CPUs supporting
511e76f5e73SSepherosa Ziehau 	 *   this feature is 0x09.
512e76f5e73SSepherosa Ziehau 	 */
513e76f5e73SSepherosa Ziehau 	if (CPUID_TO_FAMILY(cpu_id) != 0xf)
514e76f5e73SSepherosa Ziehau 		return FALSE;
515e76f5e73SSepherosa Ziehau 
516e76f5e73SSepherosa Ziehau 	model = CPUID_TO_MODEL(cpu_id);
517e76f5e73SSepherosa Ziehau 	stepping = cpu_id & 0xf;
518e76f5e73SSepherosa Ziehau 
519a1efa688SSepherosa Ziehau 	if (model == 0x6) {
520a1efa688SSepherosa Ziehau 		switch (stepping) {
521a1efa688SSepherosa Ziehau 		case 0x2:
522a1efa688SSepherosa Ziehau 		case 0x4:
523a1efa688SSepherosa Ziehau 		case 0x5:
524a1efa688SSepherosa Ziehau 			/* Hang w/ 12.50% and 25.00% */
525a1efa688SSepherosa Ziehau 			if (duty == 1250 || duty == 2500)
526a1efa688SSepherosa Ziehau 				return TRUE;
527a1efa688SSepherosa Ziehau 			break;
528a1efa688SSepherosa Ziehau 		}
529a1efa688SSepherosa Ziehau 	} else if (model == 0x2) {
530e76f5e73SSepherosa Ziehau 		switch (stepping) {
531e76f5e73SSepherosa Ziehau 		case 0x2:
532e76f5e73SSepherosa Ziehau 		case 0x4:
533e76f5e73SSepherosa Ziehau 		case 0x5:
534e76f5e73SSepherosa Ziehau 		case 0x7:
535e76f5e73SSepherosa Ziehau 		case 0x9:
536e76f5e73SSepherosa Ziehau 			/* Hang w/ 12.50% */
537e76f5e73SSepherosa Ziehau 			if (duty == 1250)
538e76f5e73SSepherosa Ziehau 				return TRUE;
539e76f5e73SSepherosa Ziehau 			break;
540e76f5e73SSepherosa Ziehau 		}
541e76f5e73SSepherosa Ziehau 	} else if (model == 0x1) {
542e76f5e73SSepherosa Ziehau 		switch (stepping) {
543e76f5e73SSepherosa Ziehau 		case 0x2:
544e76f5e73SSepherosa Ziehau 		case 0x3:
545e76f5e73SSepherosa Ziehau 			/* Hang w/ 12.50% and 25.00% */
546e76f5e73SSepherosa Ziehau 			if (duty == 1250 || duty == 2500)
547e76f5e73SSepherosa Ziehau 				return TRUE;
548e76f5e73SSepherosa Ziehau 			break;
549e76f5e73SSepherosa Ziehau 		}
550e76f5e73SSepherosa Ziehau 	} else if (model == 0x0) {
551e76f5e73SSepherosa Ziehau 		switch (stepping) {
552e76f5e73SSepherosa Ziehau 		case 0x7:
553e76f5e73SSepherosa Ziehau 		case 0xa:
554e76f5e73SSepherosa Ziehau 			/* Hang w/ 12.50% and 25.00% */
555e76f5e73SSepherosa Ziehau 			if (duty == 1250 || duty == 2500)
556e76f5e73SSepherosa Ziehau 				return TRUE;
557e76f5e73SSepherosa Ziehau 			break;
558e76f5e73SSepherosa Ziehau 		}
559e76f5e73SSepherosa Ziehau 	}
560e76f5e73SSepherosa Ziehau 	return FALSE;
561e76f5e73SSepherosa Ziehau }
562