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; 187e76f5e73SSepherosa Ziehau cpumask_t mask, found_mask = 0; 188e76f5e73SSepherosa Ziehau int error = 0; 189e76f5e73SSepherosa Ziehau 190e76f5e73SSepherosa Ziehau mask = get_cpumask_from_level(sc->sc_cpuid, CORE_LEVEL); 191e76f5e73SSepherosa Ziehau if (mask == 0) 192e76f5e73SSepherosa Ziehau mask = CPUMASK(sc->sc_cpuid); 193e76f5e73SSepherosa Ziehau 194e76f5e73SSepherosa Ziehau lwkt_serialize_enter(&clockmod_dom_slize); 195e76f5e73SSepherosa Ziehau 196e76f5e73SSepherosa Ziehau dom = clockmod_dom_find(mask); 197e76f5e73SSepherosa Ziehau if (dom == NULL) { 198e76f5e73SSepherosa Ziehau dom = clockmod_dom_create(mask); 199e76f5e73SSepherosa Ziehau if (dom == NULL) { 200e76f5e73SSepherosa Ziehau error = ENOMEM; 201e76f5e73SSepherosa Ziehau goto back; 202e76f5e73SSepherosa Ziehau } 203e76f5e73SSepherosa Ziehau } 204e76f5e73SSepherosa Ziehau 205e76f5e73SSepherosa Ziehau sc->sc_dom = dom; 206e76f5e73SSepherosa Ziehau TAILQ_INSERT_TAIL(&dom->dom_list, sc, sc_link); 207e76f5e73SSepherosa Ziehau 208e76f5e73SSepherosa Ziehau TAILQ_FOREACH(sc1, &dom->dom_list, sc_link) 209e76f5e73SSepherosa Ziehau found_mask |= CPUMASK(sc1->sc_cpuid); 210e76f5e73SSepherosa Ziehau 211e76f5e73SSepherosa Ziehau if (found_mask == dom->dom_cpumask) { 212e76f5e73SSepherosa Ziehau /* All cpus in this domain is found */ 213e76f5e73SSepherosa Ziehau dom->dom_flags |= CLOCKMOD_DOM_FLAG_ACTIVE; 214e76f5e73SSepherosa Ziehau } 215e76f5e73SSepherosa Ziehau back: 216e76f5e73SSepherosa Ziehau lwkt_serialize_exit(&clockmod_dom_slize); 217e76f5e73SSepherosa Ziehau return error; 218e76f5e73SSepherosa Ziehau } 219e76f5e73SSepherosa Ziehau 220e76f5e73SSepherosa Ziehau static void 221e76f5e73SSepherosa Ziehau clockmod_dom_detach(struct clockmod_softc *sc) 222e76f5e73SSepherosa Ziehau { 223e76f5e73SSepherosa Ziehau struct clockmod_dom *dom; 224e76f5e73SSepherosa Ziehau 225e76f5e73SSepherosa Ziehau lwkt_serialize_enter(&clockmod_dom_slize); 226e76f5e73SSepherosa Ziehau 227e76f5e73SSepherosa Ziehau dom = sc->sc_dom; 228e76f5e73SSepherosa Ziehau sc->sc_dom = NULL; 229e76f5e73SSepherosa Ziehau 230e76f5e73SSepherosa Ziehau if (dom->dom_flags & CLOCKMOD_DOM_FLAG_ACTIVE) { 231e76f5e73SSepherosa Ziehau struct clockmod_softc *sc1; 232e76f5e73SSepherosa Ziehau 233e76f5e73SSepherosa Ziehau /* Raise to 100% */ 234e76f5e73SSepherosa Ziehau TAILQ_FOREACH(sc1, &dom->dom_list, sc_link) 235e76f5e73SSepherosa Ziehau clockmod_select(sc1, &clockmod_dom_controls[0]); 236e76f5e73SSepherosa Ziehau } 237e76f5e73SSepherosa Ziehau 238e76f5e73SSepherosa Ziehau /* One cpu is leaving; domain is no longer active */ 239e76f5e73SSepherosa Ziehau dom->dom_flags &= ~CLOCKMOD_DOM_FLAG_ACTIVE; 240e76f5e73SSepherosa Ziehau 241e76f5e73SSepherosa Ziehau TAILQ_REMOVE(&dom->dom_list, sc, sc_link); 242e76f5e73SSepherosa Ziehau if (TAILQ_EMPTY(&dom->dom_list)) 243e76f5e73SSepherosa Ziehau clockmod_dom_destroy(dom); 244e76f5e73SSepherosa Ziehau 245e76f5e73SSepherosa Ziehau lwkt_serialize_exit(&clockmod_dom_slize); 246e76f5e73SSepherosa Ziehau } 247e76f5e73SSepherosa Ziehau 248e76f5e73SSepherosa Ziehau static struct clockmod_dom * 249e76f5e73SSepherosa Ziehau clockmod_dom_find(cpumask_t mask) 250e76f5e73SSepherosa Ziehau { 251e76f5e73SSepherosa Ziehau struct clockmod_dom *dom; 252e76f5e73SSepherosa Ziehau 253e76f5e73SSepherosa Ziehau TAILQ_FOREACH(dom, &clockmod_dom_list, dom_link) { 254e76f5e73SSepherosa Ziehau if (dom->dom_cpumask == mask) 255e76f5e73SSepherosa Ziehau return dom; 256e76f5e73SSepherosa Ziehau } 257e76f5e73SSepherosa Ziehau return NULL; 258e76f5e73SSepherosa Ziehau } 259e76f5e73SSepherosa Ziehau 260e76f5e73SSepherosa Ziehau static struct clockmod_dom * 261e76f5e73SSepherosa Ziehau clockmod_dom_create(cpumask_t mask) 262e76f5e73SSepherosa Ziehau { 263e76f5e73SSepherosa Ziehau struct clockmod_dom *dom; 264e76f5e73SSepherosa Ziehau int id; 265e76f5e73SSepherosa Ziehau 266e76f5e73SSepherosa Ziehau id = clockmod_dom_id++; 267e76f5e73SSepherosa Ziehau dom = kmalloc(sizeof(*dom), M_DEVBUF, M_WAITOK | M_ZERO); 268e76f5e73SSepherosa Ziehau 269e76f5e73SSepherosa Ziehau TAILQ_INIT(&dom->dom_list); 270e76f5e73SSepherosa Ziehau dom->dom_cpumask = mask; 271e76f5e73SSepherosa Ziehau ksnprintf(dom->dom_name, sizeof(dom->dom_name), "clockmod_dom%d", id); 272e76f5e73SSepherosa Ziehau 273e76f5e73SSepherosa Ziehau sysctl_ctx_init(&dom->dom_sysctl_ctx); 274e76f5e73SSepherosa Ziehau dom->dom_sysctl_tree = SYSCTL_ADD_NODE(&dom->dom_sysctl_ctx, 275e76f5e73SSepherosa Ziehau SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, dom->dom_name, 276e76f5e73SSepherosa Ziehau CTLFLAG_RD, 0, ""); 277e76f5e73SSepherosa Ziehau if (dom->dom_sysctl_tree == NULL) { 278e76f5e73SSepherosa Ziehau kprintf("%s: can't add sysctl node\n", dom->dom_name); 279e76f5e73SSepherosa Ziehau kfree(dom, M_DEVBUF); 280e76f5e73SSepherosa Ziehau return NULL; 281e76f5e73SSepherosa Ziehau } 282e76f5e73SSepherosa Ziehau 283e76f5e73SSepherosa Ziehau SYSCTL_ADD_PROC(&dom->dom_sysctl_ctx, 284e76f5e73SSepherosa Ziehau SYSCTL_CHILDREN(dom->dom_sysctl_tree), 285e76f5e73SSepherosa Ziehau OID_AUTO, "members", CTLTYPE_STRING | CTLFLAG_RD, 286e76f5e73SSepherosa Ziehau dom, 0, clockmod_dom_sysctl_members, "A", "member cpus"); 287e76f5e73SSepherosa Ziehau 288e76f5e73SSepherosa Ziehau SYSCTL_ADD_PROC(&dom->dom_sysctl_ctx, 289e76f5e73SSepherosa Ziehau SYSCTL_CHILDREN(dom->dom_sysctl_tree), 290e76f5e73SSepherosa Ziehau OID_AUTO, "available", CTLTYPE_STRING | CTLFLAG_RD, 291e76f5e73SSepherosa Ziehau dom, 0, clockmod_dom_sysctl_available, "A", 292e76f5e73SSepherosa Ziehau "available duty percent"); 293e76f5e73SSepherosa Ziehau 294e76f5e73SSepherosa Ziehau SYSCTL_ADD_PROC(&dom->dom_sysctl_ctx, 295e76f5e73SSepherosa Ziehau SYSCTL_CHILDREN(dom->dom_sysctl_tree), 296e76f5e73SSepherosa Ziehau OID_AUTO, "select", CTLTYPE_STRING | CTLFLAG_RW, 297e76f5e73SSepherosa Ziehau dom, 0, clockmod_dom_sysctl_select, "A", "select duty"); 298e76f5e73SSepherosa Ziehau 299e76f5e73SSepherosa Ziehau TAILQ_INSERT_TAIL(&clockmod_dom_list, dom, dom_link); 300e76f5e73SSepherosa Ziehau 301e76f5e73SSepherosa Ziehau if (clockmod_dom_controls == NULL) { 302e76f5e73SSepherosa Ziehau int nctrl, step, i, shift, cnt; 303e76f5e73SSepherosa Ziehau 304e76f5e73SSepherosa Ziehau #ifdef __x86_64__ 305e76f5e73SSepherosa Ziehau if (cpu_thermal_feature & CPUID_THERMAL_ECMD) 306e76f5e73SSepherosa Ziehau shift = 0; 307e76f5e73SSepherosa Ziehau else 308e76f5e73SSepherosa Ziehau #endif 309e76f5e73SSepherosa Ziehau shift = 1; 310e76f5e73SSepherosa Ziehau 311e76f5e73SSepherosa Ziehau nctrl = 8 << (1 - shift); 312e76f5e73SSepherosa Ziehau step = 10000 / nctrl; 313e76f5e73SSepherosa Ziehau 314e76f5e73SSepherosa Ziehau clockmod_dom_controls = 315e76f5e73SSepherosa Ziehau kmalloc(sizeof(struct clockmod_dom_ctrl) * nctrl, M_DEVBUF, 316e76f5e73SSepherosa Ziehau M_WAITOK | M_ZERO); 317e76f5e73SSepherosa Ziehau 318e76f5e73SSepherosa Ziehau if (bootverbose) 319e76f5e73SSepherosa Ziehau kprintf("clock modulation:\n"); 320e76f5e73SSepherosa Ziehau 321e76f5e73SSepherosa Ziehau cnt = 0; 322e76f5e73SSepherosa Ziehau for (i = 0; i < nctrl; ++i) { 323e76f5e73SSepherosa Ziehau struct clockmod_dom_ctrl *ctrl = 324e76f5e73SSepherosa Ziehau &clockmod_dom_controls[cnt]; 325e76f5e73SSepherosa Ziehau int duty; 326e76f5e73SSepherosa Ziehau 327e76f5e73SSepherosa Ziehau duty = 10000 - (i * step); 328e76f5e73SSepherosa Ziehau if (clockmod_errata_duty(duty)) 329e76f5e73SSepherosa Ziehau continue; 330e76f5e73SSepherosa Ziehau ++cnt; 331e76f5e73SSepherosa Ziehau 332e76f5e73SSepherosa Ziehau ksnprintf(ctrl->ctl_name, sizeof(ctrl->ctl_name), 333e76f5e73SSepherosa Ziehau "%d.%02d%%", duty / 100, duty % 100); 334e76f5e73SSepherosa Ziehau ctrl->ctl_value = (((nctrl - i) << shift) & 0xf); 335e76f5e73SSepherosa Ziehau if (i != 0) 336e76f5e73SSepherosa Ziehau ctrl->ctl_value |= 1 << 4; 337e76f5e73SSepherosa Ziehau 338e76f5e73SSepherosa Ziehau if (bootverbose) { 339e76f5e73SSepherosa Ziehau kprintf(" 0x%04jx %s\n", 340e76f5e73SSepherosa Ziehau (uintmax_t)ctrl->ctl_value, 341e76f5e73SSepherosa Ziehau ctrl->ctl_name); 342e76f5e73SSepherosa Ziehau } 343e76f5e73SSepherosa Ziehau } 344e76f5e73SSepherosa Ziehau clockmod_dom_nctrl = cnt; 345e76f5e73SSepherosa Ziehau } 346e76f5e73SSepherosa Ziehau return dom; 347e76f5e73SSepherosa Ziehau } 348e76f5e73SSepherosa Ziehau 349e76f5e73SSepherosa Ziehau static void 350e76f5e73SSepherosa Ziehau clockmod_dom_destroy(struct clockmod_dom *dom) 351e76f5e73SSepherosa Ziehau { 352e76f5e73SSepherosa Ziehau KASSERT(TAILQ_EMPTY(&dom->dom_list), 353e76f5e73SSepherosa Ziehau ("%s: still has member cpus", dom->dom_name)); 354e76f5e73SSepherosa Ziehau TAILQ_REMOVE(&clockmod_dom_list, dom, dom_link); 355e76f5e73SSepherosa Ziehau 356e76f5e73SSepherosa Ziehau sysctl_ctx_free(&dom->dom_sysctl_ctx); 357e76f5e73SSepherosa Ziehau kfree(dom, M_DEVBUF); 358e76f5e73SSepherosa Ziehau 359e76f5e73SSepherosa Ziehau if (TAILQ_EMPTY(&clockmod_dom_list)) { 360e76f5e73SSepherosa Ziehau clockmod_dom_nctrl = 0; 361e76f5e73SSepherosa Ziehau kfree(clockmod_dom_controls, M_DEVBUF); 362e76f5e73SSepherosa Ziehau clockmod_dom_controls = NULL; 363e76f5e73SSepherosa Ziehau } 364e76f5e73SSepherosa Ziehau } 365e76f5e73SSepherosa Ziehau 366e76f5e73SSepherosa Ziehau static int 367e76f5e73SSepherosa Ziehau clockmod_dom_sysctl_members(SYSCTL_HANDLER_ARGS) 368e76f5e73SSepherosa Ziehau { 369e76f5e73SSepherosa Ziehau struct clockmod_dom *dom = arg1; 370e76f5e73SSepherosa Ziehau struct clockmod_softc *sc; 371e76f5e73SSepherosa Ziehau int loop, error; 372e76f5e73SSepherosa Ziehau 373e76f5e73SSepherosa Ziehau lwkt_serialize_enter(&clockmod_dom_slize); 374e76f5e73SSepherosa Ziehau 375e76f5e73SSepherosa Ziehau loop = error = 0; 376e76f5e73SSepherosa Ziehau TAILQ_FOREACH(sc, &dom->dom_list, sc_link) { 377e76f5e73SSepherosa Ziehau char buf[16]; 378e76f5e73SSepherosa Ziehau 379e76f5e73SSepherosa Ziehau if (error == 0 && loop) 380e76f5e73SSepherosa Ziehau error = SYSCTL_OUT(req, " ", 1); 381e76f5e73SSepherosa Ziehau if (error == 0) { 382e76f5e73SSepherosa Ziehau ksnprintf(buf, sizeof(buf), "cpu%d", sc->sc_cpuid); 383e76f5e73SSepherosa Ziehau error = SYSCTL_OUT(req, buf, strlen(buf)); 384e76f5e73SSepherosa Ziehau } 385e76f5e73SSepherosa Ziehau ++loop; 386e76f5e73SSepherosa Ziehau } 387e76f5e73SSepherosa Ziehau 388e76f5e73SSepherosa Ziehau lwkt_serialize_exit(&clockmod_dom_slize); 389e76f5e73SSepherosa Ziehau return error; 390e76f5e73SSepherosa Ziehau } 391e76f5e73SSepherosa Ziehau 392e76f5e73SSepherosa Ziehau static int 393e76f5e73SSepherosa Ziehau clockmod_dom_sysctl_available(SYSCTL_HANDLER_ARGS) 394e76f5e73SSepherosa Ziehau { 395e76f5e73SSepherosa Ziehau struct clockmod_dom *dom = arg1; 396e76f5e73SSepherosa Ziehau int loop, error, i; 397e76f5e73SSepherosa Ziehau 398e76f5e73SSepherosa Ziehau lwkt_serialize_enter(&clockmod_dom_slize); 399e76f5e73SSepherosa Ziehau 400e76f5e73SSepherosa Ziehau if ((dom->dom_flags & CLOCKMOD_DOM_FLAG_ACTIVE) == 0) { 401e76f5e73SSepherosa Ziehau error = SYSCTL_OUT(req, " ", 1); 402e76f5e73SSepherosa Ziehau goto done; 403e76f5e73SSepherosa Ziehau } 404e76f5e73SSepherosa Ziehau 405e76f5e73SSepherosa Ziehau loop = error = 0; 406e76f5e73SSepherosa Ziehau for (i = 0; i < clockmod_dom_nctrl; ++i) { 407e76f5e73SSepherosa Ziehau if (error == 0 && loop) 408e76f5e73SSepherosa Ziehau error = SYSCTL_OUT(req, " ", 1); 409e76f5e73SSepherosa Ziehau if (error == 0) { 410e76f5e73SSepherosa Ziehau error = SYSCTL_OUT(req, 411e76f5e73SSepherosa Ziehau clockmod_dom_controls[i].ctl_name, 412e76f5e73SSepherosa Ziehau strlen(clockmod_dom_controls[i].ctl_name)); 413e76f5e73SSepherosa Ziehau } 414e76f5e73SSepherosa Ziehau ++loop; 415e76f5e73SSepherosa Ziehau } 416e76f5e73SSepherosa Ziehau done: 417e76f5e73SSepherosa Ziehau lwkt_serialize_exit(&clockmod_dom_slize); 418e76f5e73SSepherosa Ziehau return error; 419e76f5e73SSepherosa Ziehau } 420e76f5e73SSepherosa Ziehau 421e76f5e73SSepherosa Ziehau static int 422e76f5e73SSepherosa Ziehau clockmod_dom_sysctl_select(SYSCTL_HANDLER_ARGS) 423e76f5e73SSepherosa Ziehau { 424e76f5e73SSepherosa Ziehau struct clockmod_dom *dom = arg1; 425e76f5e73SSepherosa Ziehau struct clockmod_softc *sc; 426e76f5e73SSepherosa Ziehau const struct clockmod_dom_ctrl *ctrl = NULL; 427e76f5e73SSepherosa Ziehau char duty[16]; 428e76f5e73SSepherosa Ziehau int error, i; 429e76f5e73SSepherosa Ziehau 430e76f5e73SSepherosa Ziehau lwkt_serialize_enter(&clockmod_dom_slize); 431e76f5e73SSepherosa Ziehau KKASSERT(dom->dom_select >= 0 && dom->dom_select < clockmod_dom_nctrl); 432e76f5e73SSepherosa Ziehau ksnprintf(duty, sizeof(duty), "%s", 433e76f5e73SSepherosa Ziehau clockmod_dom_controls[dom->dom_select].ctl_name); 434e76f5e73SSepherosa Ziehau lwkt_serialize_exit(&clockmod_dom_slize); 435e76f5e73SSepherosa Ziehau 436e76f5e73SSepherosa Ziehau error = sysctl_handle_string(oidp, duty, sizeof(duty), req); 437e76f5e73SSepherosa Ziehau if (error != 0 || req->newptr == NULL) 438e76f5e73SSepherosa Ziehau return error; 439e76f5e73SSepherosa Ziehau 440e76f5e73SSepherosa Ziehau lwkt_serialize_enter(&clockmod_dom_slize); 441e76f5e73SSepherosa Ziehau 442e76f5e73SSepherosa Ziehau if ((dom->dom_flags & CLOCKMOD_DOM_FLAG_ACTIVE) == 0) { 443e76f5e73SSepherosa Ziehau error = EOPNOTSUPP; 444e76f5e73SSepherosa Ziehau goto back; 445e76f5e73SSepherosa Ziehau } 446e76f5e73SSepherosa Ziehau 447e76f5e73SSepherosa Ziehau for (i = 0; i < clockmod_dom_nctrl; ++i) { 448e76f5e73SSepherosa Ziehau ctrl = &clockmod_dom_controls[i]; 449e76f5e73SSepherosa Ziehau if (strcmp(duty, ctrl->ctl_name) == 0) 450e76f5e73SSepherosa Ziehau break; 451e76f5e73SSepherosa Ziehau } 452e76f5e73SSepherosa Ziehau if (i == clockmod_dom_nctrl) { 453e76f5e73SSepherosa Ziehau error = EINVAL; 454e76f5e73SSepherosa Ziehau goto back; 455e76f5e73SSepherosa Ziehau } 456e76f5e73SSepherosa Ziehau dom->dom_select = i; 457e76f5e73SSepherosa Ziehau 458e76f5e73SSepherosa Ziehau TAILQ_FOREACH(sc, &dom->dom_list, sc_link) 459e76f5e73SSepherosa Ziehau clockmod_select(sc, ctrl); 460e76f5e73SSepherosa Ziehau back: 461e76f5e73SSepherosa Ziehau lwkt_serialize_exit(&clockmod_dom_slize); 462e76f5e73SSepherosa Ziehau return error; 463e76f5e73SSepherosa Ziehau } 464e76f5e73SSepherosa Ziehau 465e76f5e73SSepherosa Ziehau static void 466e76f5e73SSepherosa Ziehau clockmod_select_handler(netmsg_t msg) 467e76f5e73SSepherosa Ziehau { 468e76f5e73SSepherosa Ziehau struct netmsg_clockmod *cmsg = (struct netmsg_clockmod *)msg; 469e76f5e73SSepherosa Ziehau 470e76f5e73SSepherosa Ziehau #if 0 471e76f5e73SSepherosa Ziehau if (bootverbose) { 472e76f5e73SSepherosa Ziehau kprintf("cpu%d: clockmod 0x%04jx\n", mycpuid, 473e76f5e73SSepherosa Ziehau (uintmax_t)cmsg->ctl_value); 474e76f5e73SSepherosa Ziehau } 475e76f5e73SSepherosa Ziehau #endif 476e76f5e73SSepherosa Ziehau 477e76f5e73SSepherosa Ziehau wrmsr(MSR_THERM_CONTROL, cmsg->ctl_value); 478e76f5e73SSepherosa Ziehau lwkt_replymsg(&cmsg->base.lmsg, 0); 479e76f5e73SSepherosa Ziehau } 480e76f5e73SSepherosa Ziehau 481e76f5e73SSepherosa Ziehau static int 482e76f5e73SSepherosa Ziehau clockmod_select(const struct clockmod_softc *sc, 483e76f5e73SSepherosa Ziehau const struct clockmod_dom_ctrl *ctrl) 484e76f5e73SSepherosa Ziehau { 485e76f5e73SSepherosa Ziehau struct netmsg_clockmod msg; 486e76f5e73SSepherosa Ziehau 487e76f5e73SSepherosa Ziehau netmsg_init(&msg.base, NULL, &curthread->td_msgport, MSGF_PRIORITY, 488e76f5e73SSepherosa Ziehau clockmod_select_handler); 489e76f5e73SSepherosa Ziehau msg.ctl_value = ctrl->ctl_value; 490e76f5e73SSepherosa Ziehau return lwkt_domsg(netisr_cpuport(sc->sc_cpuid), &msg.base.lmsg, 0); 491e76f5e73SSepherosa Ziehau } 492e76f5e73SSepherosa Ziehau 493e76f5e73SSepherosa Ziehau static boolean_t 494e76f5e73SSepherosa Ziehau clockmod_errata_duty(int duty) 495e76f5e73SSepherosa Ziehau { 496e76f5e73SSepherosa Ziehau uint32_t model, stepping; 497e76f5e73SSepherosa Ziehau 498e76f5e73SSepherosa Ziehau /* 499e76f5e73SSepherosa Ziehau * This is obtained from the original p4tcc code. 500e76f5e73SSepherosa Ziehau * 501e76f5e73SSepherosa Ziehau * The original errata checking code in p4tcc is obviously wrong. 502e76f5e73SSepherosa Ziehau * However, I am no longer being able to find the errata mentioned 503e76f5e73SSepherosa Ziehau * in the code. The guess is that the errata only affects family 504e76f5e73SSepherosa Ziehau * 0x0f CPUs, since: 505e76f5e73SSepherosa Ziehau * - The errata applies to only to model 0x00, 0x01 and 0x02 in 506e76f5e73SSepherosa Ziehau * the original p4tcc code. 507e76f5e73SSepherosa Ziehau * - Software controlled clock modulation has been supported since 508e76f5e73SSepherosa Ziehau * 0f_00 and the model of the oldest family 0x06 CPUs supporting 509e76f5e73SSepherosa Ziehau * this feature is 0x09. 510e76f5e73SSepherosa Ziehau */ 511e76f5e73SSepherosa Ziehau if (CPUID_TO_FAMILY(cpu_id) != 0xf) 512e76f5e73SSepherosa Ziehau return FALSE; 513e76f5e73SSepherosa Ziehau 514e76f5e73SSepherosa Ziehau model = CPUID_TO_MODEL(cpu_id); 515e76f5e73SSepherosa Ziehau stepping = cpu_id & 0xf; 516e76f5e73SSepherosa Ziehau 517*a1efa688SSepherosa Ziehau if (model == 0x6) { 518*a1efa688SSepherosa Ziehau switch (stepping) { 519*a1efa688SSepherosa Ziehau case 0x2: 520*a1efa688SSepherosa Ziehau case 0x4: 521*a1efa688SSepherosa Ziehau case 0x5: 522*a1efa688SSepherosa Ziehau /* Hang w/ 12.50% and 25.00% */ 523*a1efa688SSepherosa Ziehau if (duty == 1250 || duty == 2500) 524*a1efa688SSepherosa Ziehau return TRUE; 525*a1efa688SSepherosa Ziehau break; 526*a1efa688SSepherosa Ziehau } 527*a1efa688SSepherosa Ziehau } else if (model == 0x2) { 528e76f5e73SSepherosa Ziehau switch (stepping) { 529e76f5e73SSepherosa Ziehau case 0x2: 530e76f5e73SSepherosa Ziehau case 0x4: 531e76f5e73SSepherosa Ziehau case 0x5: 532e76f5e73SSepherosa Ziehau case 0x7: 533e76f5e73SSepherosa Ziehau case 0x9: 534e76f5e73SSepherosa Ziehau /* Hang w/ 12.50% */ 535e76f5e73SSepherosa Ziehau if (duty == 1250) 536e76f5e73SSepherosa Ziehau return TRUE; 537e76f5e73SSepherosa Ziehau break; 538e76f5e73SSepherosa Ziehau } 539e76f5e73SSepherosa Ziehau } else if (model == 0x1) { 540e76f5e73SSepherosa Ziehau switch (stepping) { 541e76f5e73SSepherosa Ziehau case 0x2: 542e76f5e73SSepherosa Ziehau case 0x3: 543e76f5e73SSepherosa Ziehau /* Hang w/ 12.50% and 25.00% */ 544e76f5e73SSepherosa Ziehau if (duty == 1250 || duty == 2500) 545e76f5e73SSepherosa Ziehau return TRUE; 546e76f5e73SSepherosa Ziehau break; 547e76f5e73SSepherosa Ziehau } 548e76f5e73SSepherosa Ziehau } else if (model == 0x0) { 549e76f5e73SSepherosa Ziehau switch (stepping) { 550e76f5e73SSepherosa Ziehau case 0x7: 551e76f5e73SSepherosa Ziehau case 0xa: 552e76f5e73SSepherosa Ziehau /* Hang w/ 12.50% and 25.00% */ 553e76f5e73SSepherosa Ziehau if (duty == 1250 || duty == 2500) 554e76f5e73SSepherosa Ziehau return TRUE; 555e76f5e73SSepherosa Ziehau break; 556e76f5e73SSepherosa Ziehau } 557e76f5e73SSepherosa Ziehau } 558e76f5e73SSepherosa Ziehau return FALSE; 559e76f5e73SSepherosa Ziehau } 560