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

Cache object: 88467d66b7aa8a61457d2ab92b13f4a7


[ 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.