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