The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/cpufreq/ichss.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2004-2005 Nate Lawson (SDG)
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   24  * SUCH DAMAGE.
   25  */
   26 
   27 #include <sys/cdefs.h>
   28 __FBSDID("$FreeBSD$");
   29 
   30 #include <sys/param.h>
   31 #include <sys/bus.h>
   32 #include <sys/cpu.h>
   33 #include <sys/kernel.h>
   34 #include <sys/malloc.h>
   35 #include <sys/module.h>
   36 #include <sys/pcpu.h>
   37 #include <sys/sysctl.h>
   38 #include <sys/systm.h>
   39 
   40 #include <dev/pci/pcivar.h>
   41 #include <machine/bus.h>
   42 #include <machine/resource.h>
   43 #include <sys/rman.h>
   44 
   45 #include "cpufreq_if.h"
   46 
   47 /*
   48  * The SpeedStep ICH feature is a chipset-initiated voltage and frequency
   49  * transition available on the ICH2M, 3M, and 4M.  It is different from
   50  * the newer Pentium-M SpeedStep feature.  It offers only two levels of
   51  * frequency/voltage.  Often, the BIOS will select one of the levels via
   52  * SMM code during the power-on process (i.e., choose a lower level if the
   53  * system is off AC power.)
   54  */
   55 
   56 struct ichss_softc {
   57         device_t         dev;
   58         int              bm_rid;        /* Bus-mastering control (PM2REG). */
   59         struct resource *bm_reg;
   60         int              ctrl_rid;      /* Control/status register. */
   61         struct resource *ctrl_reg;
   62         struct cf_setting sets[2];      /* Only two settings. */
   63 };
   64 
   65 /* Supported PCI IDs. */
   66 #define PCI_VENDOR_INTEL        0x8086
   67 #define PCI_DEV_82801BA         0x244c /* ICH2M */
   68 #define PCI_DEV_82801CA         0x248c /* ICH3M */
   69 #define PCI_DEV_82801DB         0x24cc /* ICH4M */
   70 #define PCI_DEV_82815BA         0x1130 /* Unsupported/buggy part */
   71 
   72 /* PCI config registers for finding PMBASE and enabling SpeedStep. */
   73 #define ICHSS_PMBASE_OFFSET     0x40
   74 #define ICHSS_PMCFG_OFFSET      0xa0
   75 
   76 /* Values and masks. */
   77 #define ICHSS_ENABLE            (1<<3)  /* Enable SpeedStep control. */
   78 #define ICHSS_IO_REG            0x1     /* Access register via I/O space. */
   79 #define ICHSS_PMBASE_MASK       0xff80  /* PMBASE address bits. */
   80 #define ICHSS_CTRL_BIT          0x1     /* 0 is high speed, 1 is low. */
   81 #define ICHSS_BM_DISABLE        0x1
   82 
   83 /* Offsets from PMBASE for various registers. */
   84 #define ICHSS_BM_OFFSET         0x20
   85 #define ICHSS_CTRL_OFFSET       0x50
   86 
   87 #define ICH_GET_REG(reg)                                \
   88         (bus_space_read_1(rman_get_bustag((reg)),       \
   89             rman_get_bushandle((reg)), 0))
   90 #define ICH_SET_REG(reg, val)                           \
   91         (bus_space_write_1(rman_get_bustag((reg)),      \
   92             rman_get_bushandle((reg)), 0, (val)))
   93 
   94 static int      ichss_pci_probe(device_t dev);
   95 static int      ichss_probe(device_t dev);
   96 static int      ichss_attach(device_t dev);
   97 static int      ichss_detach(device_t dev);
   98 static int      ichss_settings(device_t dev, struct cf_setting *sets,
   99                     int *count);
  100 static int      ichss_set(device_t dev, const struct cf_setting *set);
  101 static int      ichss_get(device_t dev, struct cf_setting *set);
  102 static int      ichss_type(device_t dev, int *type);
  103 
  104 static device_method_t ichss_methods[] = {
  105         /* Device interface */
  106         DEVMETHOD(device_probe,         ichss_probe),
  107         DEVMETHOD(device_attach,        ichss_attach),
  108         DEVMETHOD(device_detach,        ichss_detach),
  109 
  110         /* cpufreq interface */
  111         DEVMETHOD(cpufreq_drv_set,      ichss_set),
  112         DEVMETHOD(cpufreq_drv_get,      ichss_get),
  113         DEVMETHOD(cpufreq_drv_type,     ichss_type),
  114         DEVMETHOD(cpufreq_drv_settings, ichss_settings),
  115         {0, 0}
  116 };
  117 static driver_t ichss_driver = {
  118         "ichss", ichss_methods, sizeof(struct ichss_softc)
  119 };
  120 static devclass_t ichss_devclass;
  121 DRIVER_MODULE(ichss, cpu, ichss_driver, ichss_devclass, 0, 0);
  122 
  123 static device_method_t ichss_pci_methods[] = {
  124         DEVMETHOD(device_probe,         ichss_pci_probe),
  125         {0, 0}
  126 };
  127 static driver_t ichss_pci_driver = {
  128         "ichss_pci", ichss_pci_methods, 0
  129 };
  130 static devclass_t ichss_pci_devclass;
  131 DRIVER_MODULE(ichss_pci, pci, ichss_pci_driver, ichss_pci_devclass, 0, 0);
  132 
  133 #if 0
  134 #define DPRINT(x...)    printf(x)
  135 #else
  136 #define DPRINT(x...)
  137 #endif
  138 
  139 /*
  140  * We detect the chipset by looking for its LPC bus ID during the PCI
  141  * scan and reading its config registers during the probe.  However,
  142  * we add the ichss child under the cpu device since even though the
  143  * chipset provides the control, it really affects the cpu only.
  144  *
  145  * XXX This approach does not work if the module is loaded after boot.
  146  */
  147 static int
  148 ichss_pci_probe(device_t dev)
  149 {
  150         device_t child, parent;
  151         uint32_t pmbase;
  152 
  153         /*
  154          * TODO: add a quirk to disable if we see the 82815_MC along
  155          * with the 82801BA and revision < 5.
  156          */
  157         if (pci_get_vendor(dev) != PCI_VENDOR_INTEL ||
  158             (pci_get_device(dev) != PCI_DEV_82801BA &&
  159             pci_get_device(dev) != PCI_DEV_82801CA &&
  160             pci_get_device(dev) != PCI_DEV_82801DB))
  161                 return (ENXIO);
  162 
  163         /* Only one CPU is supported for this hardware. */
  164         if (devclass_get_device(ichss_devclass, 0))
  165                 return (ENXIO);
  166 
  167         /*
  168          * Add a child under the CPU parent.  It appears that ICH SpeedStep
  169          * only requires a single CPU to set the value (since the chipset
  170          * is shared by all CPUs.)  Thus, we only add a child to cpu 0.
  171          */
  172         parent = devclass_get_device(devclass_find("cpu"), 0);
  173         KASSERT(parent != NULL, ("cpu parent is NULL"));
  174         child = BUS_ADD_CHILD(parent, 0, "ichss", 0);
  175         if (child == NULL) {
  176                 device_printf(parent, "add SpeedStep child failed\n");
  177                 return (ENXIO);
  178         }
  179 
  180         /* Find the PMBASE register from our PCI config header. */
  181         pmbase = pci_read_config(dev, ICHSS_PMBASE_OFFSET, sizeof(pmbase));
  182         if ((pmbase & ICHSS_IO_REG) == 0) {
  183                 printf("ichss: invalid PMBASE memory type\n");
  184                 return (ENXIO);
  185         }
  186         pmbase &= ICHSS_PMBASE_MASK;
  187         if (pmbase == 0) {
  188                 printf("ichss: invalid zero PMBASE address\n");
  189                 return (ENXIO);
  190         }
  191         DPRINT("ichss: PMBASE is %#x\n", pmbase);
  192 
  193         /* Add the bus master arbitration and control registers. */
  194         bus_set_resource(child, SYS_RES_IOPORT, 0, pmbase + ICHSS_BM_OFFSET,
  195             1);
  196         bus_set_resource(child, SYS_RES_IOPORT, 1, pmbase + ICHSS_CTRL_OFFSET,
  197             1);
  198 
  199         /* Attach the new CPU child now. */
  200         device_probe_and_attach(child);
  201 
  202         return (ENXIO);
  203 }
  204 
  205 static int
  206 ichss_probe(device_t dev)
  207 {
  208         device_t est_dev, perf_dev;
  209         int error, type;
  210         uint16_t ss_en;
  211 
  212         if (resource_disabled("ichss", 0))
  213                 return (ENXIO);
  214 
  215         /*
  216          * If the ACPI perf driver has attached and is not just offering
  217          * info, let it manage things.  Also, if Enhanced SpeedStep is
  218          * available, don't attach.
  219          */
  220         perf_dev = device_find_child(device_get_parent(dev), "acpi_perf", -1);
  221         if (perf_dev && device_is_attached(perf_dev)) {
  222                 error = CPUFREQ_DRV_TYPE(perf_dev, &type);
  223                 if (error == 0 && (type & CPUFREQ_FLAG_INFO_ONLY) == 0)
  224                         return (ENXIO);
  225         }
  226         est_dev = device_find_child(device_get_parent(dev), "est", -1);
  227         if (est_dev && device_is_attached(est_dev))
  228                 return (ENXIO);
  229 
  230         /* Activate SpeedStep control if not already enabled. */
  231         ss_en = pci_read_config(dev, ICHSS_PMCFG_OFFSET, sizeof(ss_en));
  232         if ((ss_en & ICHSS_ENABLE) == 0) {
  233                 printf("ichss: enabling SpeedStep support\n");
  234                 pci_write_config(dev, ICHSS_PMCFG_OFFSET,
  235                     ss_en | ICHSS_ENABLE, sizeof(ss_en));
  236         }
  237 
  238         device_set_desc(dev, "SpeedStep ICH");
  239         return (-1000);
  240 }
  241 
  242 static int
  243 ichss_attach(device_t dev)
  244 {
  245         struct ichss_softc *sc;
  246 
  247         sc = device_get_softc(dev);
  248         sc->dev = dev;
  249 
  250         sc->bm_rid = 0;
  251         sc->bm_reg = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &sc->bm_rid,
  252             RF_ACTIVE);
  253         if (sc->bm_reg == NULL) {
  254                 device_printf(dev, "failed to alloc BM arb register\n");
  255                 return (ENXIO);
  256         }
  257         sc->ctrl_rid = 1;
  258         sc->ctrl_reg = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
  259             &sc->ctrl_rid, RF_ACTIVE);
  260         if (sc->ctrl_reg == NULL) {
  261                 device_printf(dev, "failed to alloc control register\n");
  262                 bus_release_resource(dev, SYS_RES_IOPORT, sc->bm_rid,
  263                     sc->bm_reg);
  264                 return (ENXIO);
  265         }
  266 
  267         /* Setup some defaults for our exported settings. */
  268         sc->sets[0].freq = CPUFREQ_VAL_UNKNOWN;
  269         sc->sets[0].volts = CPUFREQ_VAL_UNKNOWN;
  270         sc->sets[0].power = CPUFREQ_VAL_UNKNOWN;
  271         sc->sets[0].lat = 1000;
  272         sc->sets[0].dev = dev;
  273         sc->sets[1] = sc->sets[0];
  274         cpufreq_register(dev);
  275 
  276         return (0);
  277 }
  278 
  279 static int
  280 ichss_detach(device_t dev)
  281 {
  282         /* TODO: teardown BM and CTRL registers. */
  283         return (ENXIO);
  284 }
  285 
  286 static int
  287 ichss_settings(device_t dev, struct cf_setting *sets, int *count)
  288 {
  289         struct ichss_softc *sc;
  290         struct cf_setting set;
  291         int first, i;
  292 
  293         if (sets == NULL || count == NULL)
  294                 return (EINVAL);
  295         if (*count < 2) {
  296                 *count = 2;
  297                 return (E2BIG);
  298         }
  299         sc = device_get_softc(dev);
  300 
  301         /*
  302          * Estimate frequencies for both levels, temporarily switching to
  303          * the other one if we haven't calibrated it yet.
  304          */
  305         ichss_get(dev, &set);
  306         for (i = 0; i < 2; i++) {
  307                 if (sc->sets[i].freq == CPUFREQ_VAL_UNKNOWN) {
  308                         first = (i == 0) ? 1 : 0;
  309                         ichss_set(dev, &sc->sets[i]);
  310                         ichss_set(dev, &sc->sets[first]);
  311                 }
  312         }
  313 
  314         bcopy(sc->sets, sets, sizeof(sc->sets));
  315         *count = 2;
  316 
  317         return (0);
  318 }
  319 
  320 static int
  321 ichss_set(device_t dev, const struct cf_setting *set)
  322 {
  323         struct ichss_softc *sc;
  324         uint8_t bmval, new_val, old_val, req_val;
  325         uint64_t rate;
  326         register_t regs;
  327 
  328         /* Look up appropriate bit value based on frequency. */
  329         sc = device_get_softc(dev);
  330         if (CPUFREQ_CMP(set->freq, sc->sets[0].freq))
  331                 req_val = 0;
  332         else if (CPUFREQ_CMP(set->freq, sc->sets[1].freq))
  333                 req_val = ICHSS_CTRL_BIT;
  334         else
  335                 return (EINVAL);
  336         DPRINT("ichss: requested setting %d\n", req_val);
  337 
  338         /* Disable interrupts and get the other register contents. */
  339         regs = intr_disable();
  340         old_val = ICH_GET_REG(sc->ctrl_reg) & ~ICHSS_CTRL_BIT;
  341 
  342         /*
  343          * Disable bus master arbitration, write the new value to the control
  344          * register, and then re-enable bus master arbitration.
  345          */
  346         bmval = ICH_GET_REG(sc->bm_reg) | ICHSS_BM_DISABLE;
  347         ICH_SET_REG(sc->bm_reg, bmval);
  348         ICH_SET_REG(sc->ctrl_reg, old_val | req_val);
  349         ICH_SET_REG(sc->bm_reg, bmval & ~ICHSS_BM_DISABLE);
  350 
  351         /* Get the new value and re-enable interrupts. */
  352         new_val = ICH_GET_REG(sc->ctrl_reg);
  353         intr_restore(regs);
  354 
  355         /* Check if the desired state was indeed selected. */
  356         if (req_val != (new_val & ICHSS_CTRL_BIT)) {
  357             device_printf(sc->dev, "transition to %d failed\n", req_val);
  358             return (ENXIO);
  359         }
  360 
  361         /* Re-initialize our cycle counter if we don't know this new state. */
  362         if (sc->sets[req_val].freq == CPUFREQ_VAL_UNKNOWN) {
  363                 cpu_est_clockrate(0, &rate);
  364                 sc->sets[req_val].freq = rate / 1000000;
  365                 DPRINT("ichss: set calibrated new rate of %d\n",
  366                     sc->sets[req_val].freq);
  367         }
  368 
  369         return (0);
  370 }
  371 
  372 static int
  373 ichss_get(device_t dev, struct cf_setting *set)
  374 {
  375         struct ichss_softc *sc;
  376         uint64_t rate;
  377         uint8_t state;
  378 
  379         sc = device_get_softc(dev);
  380         state = ICH_GET_REG(sc->ctrl_reg) & ICHSS_CTRL_BIT;
  381 
  382         /* If we haven't changed settings yet, estimate the current value. */
  383         if (sc->sets[state].freq == CPUFREQ_VAL_UNKNOWN) {
  384                 cpu_est_clockrate(0, &rate);
  385                 sc->sets[state].freq = rate / 1000000;
  386                 DPRINT("ichss: get calibrated new rate of %d\n",
  387                     sc->sets[state].freq);
  388         }
  389         *set = sc->sets[state];
  390 
  391         return (0);
  392 }
  393 
  394 static int
  395 ichss_type(device_t dev, int *type)
  396 {
  397 
  398         if (type == NULL)
  399                 return (EINVAL);
  400 
  401         *type = CPUFREQ_TYPE_ABSOLUTE;
  402         return (0);
  403 }

Cache object: 763568a64049d15e61c3a323879c24b5


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.