root/arch/i386/pci/ichpcib.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ichpcib_match
  2. ichpcib_attach
  3. ichss_present
  4. ichss_setperf
  5. ichpcib_get_timecount

    1 /*      $OpenBSD: ichpcib.c,v 1.19 2007/06/02 18:39:57 jsg Exp $        */
    2 /*
    3  * Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org>
    4  *
    5  * Permission to use, copy, modify, and distribute this software for any
    6  * purpose with or without fee is hereby granted, provided that the above
    7  * copyright notice and this permission notice appear in all copies.
    8  *
    9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   16  */
   17 
   18 /*
   19  * Special driver for the Intel ICHx/ICHx-M LPC bridges that attaches
   20  * instead of pcib(4). In addition to the core pcib(4) functionality this
   21  * driver provides support for the Intel SpeedStep technology and
   22  * power management timer.
   23  */
   24 
   25 #include <sys/param.h>
   26 #include <sys/systm.h>
   27 #include <sys/device.h>
   28 #include <sys/sysctl.h>
   29 #ifdef __HAVE_TIMECOUNTER
   30 #include <sys/timetc.h>
   31 #endif
   32 
   33 #include <machine/bus.h>
   34 
   35 #include <dev/pci/pcireg.h>
   36 #include <dev/pci/pcivar.h>
   37 #include <dev/pci/pcidevs.h>
   38 
   39 #include <dev/pci/ichreg.h>
   40 
   41 #include <machine/cpu.h>
   42 #include <machine/cpufunc.h>
   43 
   44 struct ichpcib_softc {
   45         struct device sc_dev;
   46 
   47         bus_space_tag_t sc_pm_iot;
   48         bus_space_handle_t sc_pm_ioh;
   49 };
   50 
   51 int     ichpcib_match(struct device *, void *, void *);
   52 void    ichpcib_attach(struct device *, struct device *, void *);
   53 
   54 int     ichss_present(struct pci_attach_args *);
   55 void    ichss_setperf(int);
   56 
   57 /* arch/i386/pci/pcib.c */
   58 void    pcibattach(struct device *, struct device *, void *);
   59 
   60 #ifdef __HAVE_TIMECOUNTER
   61 u_int   ichpcib_get_timecount(struct timecounter *tc);
   62 
   63 struct timecounter ichpcib_timecounter = {
   64         ichpcib_get_timecount,  /* get_timecount */
   65         0,                      /* no poll_pps */
   66         0xffffff,               /* counter_mask */
   67         3579545,                /* frequency */
   68         "ICHPM",                /* name */
   69         1000                    /* quality */
   70 };
   71 #endif  /* __HAVE_TIMECOUNTER */
   72 
   73 struct cfattach ichpcib_ca = {
   74         sizeof(struct ichpcib_softc),
   75         ichpcib_match,
   76         ichpcib_attach
   77 };
   78 
   79 struct cfdriver ichpcib_cd = {
   80         NULL, "ichpcib", DV_DULL
   81 };
   82 
   83 #ifndef SMALL_KERNEL
   84 static const char p4hint[] = "Mobile Intel(R) Pentium(R) 4";
   85 struct ichpcib_softc *ichss_sc;
   86 extern int setperf_prio;
   87 #endif  /* !SMALL_KERNEL */
   88 
   89 const struct pci_matchid ichpcib_devices[] = {
   90         { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_6300ESB_LPC },
   91         { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_6321ESB_LPC },
   92         { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801AA_LPC },
   93         { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801AB_LPC },
   94         { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801BA_LPC },
   95         { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801BAM_LPC },
   96         { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801CA_LPC },
   97         { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801CAM_LPC },
   98         { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801DB_LPC },
   99         { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801DBM_LPC },
  100         { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801E_LPC },
  101         { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801EB_LPC },
  102         { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801FB_LPC },
  103         { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801FBM_LPC },
  104         { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GB_LPC },
  105         { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GBM_LPC },
  106         { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GH_LPC },
  107         { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GHM_LPC },
  108         { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801H_LPC },
  109         { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801HBM_LPC }
  110 };
  111 
  112 int
  113 ichpcib_match(struct device *parent, void *match, void *aux)
  114 {
  115         if (pci_matchbyid((struct pci_attach_args *)aux, ichpcib_devices,
  116             sizeof(ichpcib_devices) / sizeof(ichpcib_devices[0])))
  117                 return (2);     /* supersede pcib(4) */
  118         return (0);
  119 }
  120 
  121 void
  122 ichpcib_attach(struct device *parent, struct device *self, void *aux)
  123 {
  124         struct ichpcib_softc *sc = (struct ichpcib_softc *)self;
  125         struct pci_attach_args *pa = aux;
  126         pcireg_t cntl, pmbase;
  127 
  128         /* Check if power management I/O space is enabled */
  129         cntl = pci_conf_read(pa->pa_pc, pa->pa_tag, ICH_ACPI_CNTL);
  130         if ((cntl & ICH_ACPI_CNTL_ACPI_EN) == 0) {
  131                 printf(": PM disabled");
  132                 goto corepcib;
  133         }
  134 
  135         /* Map power management I/O space */
  136         sc->sc_pm_iot = pa->pa_iot;
  137         pmbase = pci_conf_read(pa->pa_pc, pa->pa_tag, ICH_PMBASE);
  138         if (bus_space_map(sc->sc_pm_iot, PCI_MAPREG_IO_ADDR(pmbase),
  139             ICH_PMSIZE, 0, &sc->sc_pm_ioh) != 0)
  140                 goto corepcib;
  141 
  142 #ifdef __HAVE_TIMECOUNTER
  143         /* Register new timecounter */
  144         ichpcib_timecounter.tc_priv = sc;
  145         tc_init(&ichpcib_timecounter);
  146 
  147         printf(": %s-bit timer at %lluHz",
  148             (ichpcib_timecounter.tc_counter_mask == 0xffffffff ? "32" : "24"),
  149             (unsigned long long)ichpcib_timecounter.tc_frequency);
  150 #endif  /* __HAVE_TIMECOUNTER */
  151 
  152 #ifndef SMALL_KERNEL
  153         /* Check for SpeedStep */
  154         if (ichss_present(pa)) {
  155                 printf(": SpeedStep");
  156 
  157                 /* Enable SpeedStep */
  158                 pci_conf_write(pa->pa_pc, pa->pa_tag, ICH_GEN_PMCON1,
  159                     pci_conf_read(pa->pa_pc, pa->pa_tag, ICH_GEN_PMCON1) |
  160                         ICH_GEN_PMCON1_SS_EN);
  161 
  162                 /* Hook into hw.setperf sysctl */
  163                 ichss_sc = sc;
  164                 cpu_setperf = ichss_setperf;
  165                 setperf_prio = 2;
  166         }
  167 #endif /* !SMALL_KERNEL */
  168 
  169 corepcib:
  170         /* Provide core pcib(4) functionality */
  171         pcibattach(parent, self, aux);
  172 }
  173 
  174 #ifndef SMALL_KERNEL
  175 int
  176 ichss_present(struct pci_attach_args *pa)
  177 {
  178         pcitag_t br_tag;
  179         pcireg_t br_id, br_class;
  180         struct cpu_info *ci;
  181         int family, model, stepping, brandid;
  182 
  183         if (setperf_prio > 2)
  184                 return (0);
  185 
  186         ci = curcpu();
  187         family = (ci->ci_signature >> 8) & 15;
  188         model = (ci->ci_signature >> 4) & 15;
  189         stepping = ci->ci_signature & 15;
  190         brandid = cpu_miscinfo & 0xff; /* XXX should put this in ci */
  191 
  192         /*
  193          * This form of SpeedStep works only on Intel Mobile Pentium 4.
  194          * Intel Celeron processors don't support it.  However, they
  195          * can be coupled with ICH southbridges that do, causing false
  196          * positives.  So we ensure that we are running on Intel Mobile
  197          * Pentium 4.
  198          * This heuristic comes from the Linux speedstep-ich driver.
  199          */
  200         if (!(family == 15 && model == 2 &&
  201             ((stepping == 4 && (brandid == 14 || brandid == 15)) ||
  202             (stepping == 7 && brandid == 14) ||
  203             (stepping == 9 && (brandid == 14 || strncasecmp(cpu_model, p4hint,
  204             sizeof(p4hint) - 1) == 0)))))
  205                 return (0);
  206 
  207         if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801DBM_LPC ||
  208             PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801CAM_LPC)
  209                 return (1);
  210         if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801BAM_LPC) {
  211                 /*
  212                  * Old revisions of the 82815 hostbridge found on
  213                  * Dell Inspirons 8000 and 8100 don't support
  214                  * SpeedStep.
  215                  */
  216                 /*
  217                  * XXX: dev 0 func 0 is not always a hostbridge,
  218                  * should be converted to use pchb(4) hook.
  219                  */
  220                 br_tag = pci_make_tag(pa->pa_pc, pa->pa_bus, 0, 0);
  221                 br_id = pci_conf_read(pa->pa_pc, br_tag, PCI_ID_REG);
  222                 br_class = pci_conf_read(pa->pa_pc, br_tag, PCI_CLASS_REG);
  223 
  224                 if (PCI_PRODUCT(br_id) == PCI_PRODUCT_INTEL_82815_FULL_HUB &&
  225                     PCI_REVISION(br_class) < 5)
  226                         return (0);
  227                 return (1);
  228         }
  229 
  230         return (0);
  231 }
  232 
  233 void
  234 ichss_setperf(int level)
  235 {
  236         struct ichpcib_softc *sc = ichss_sc;
  237         u_int8_t state, ostate, cntl;
  238         int s;
  239 
  240 #ifdef DIAGNOSTIC
  241         if (sc == NULL) {
  242                 printf("%s: no ichss_sc", __func__);
  243                 return;
  244         }
  245 #endif
  246 
  247         s = splhigh();
  248         state = bus_space_read_1(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_SS_CNTL);
  249         ostate = state;
  250 
  251         /* Only two states are available */
  252         if (level <= 50)
  253                 state |= ICH_PM_SS_STATE_LOW;
  254         else
  255                 state &= ~ICH_PM_SS_STATE_LOW;
  256 
  257         /*
  258          * An Intel SpeedStep technology transition _always_ occur on
  259          * writes to the ICH_PM_SS_CNTL register, even if the value
  260          * written is the same as the previous value. So do the write
  261          * only if the state has changed.
  262          */
  263         if (state != ostate) {
  264                 /* Disable bus mastering arbitration */
  265                 cntl = bus_space_read_1(sc->sc_pm_iot, sc->sc_pm_ioh,
  266                     ICH_PM_CNTL);
  267                 bus_space_write_1(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_CNTL,
  268                     cntl | ICH_PM_ARB_DIS);
  269 
  270                 /* Do the transition */
  271                 bus_space_write_1(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_SS_CNTL,
  272                     state);
  273 
  274                 /* Restore bus mastering arbitration state */
  275                 bus_space_write_1(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_CNTL,
  276                     cntl);
  277 
  278 #ifdef I686_CPU
  279                 if (update_cpuspeed != NULL)
  280                         update_cpuspeed();
  281 #endif
  282         }
  283         splx(s);
  284 }
  285 #endif  /* !SMALL_KERNEL */
  286 
  287 #ifdef __HAVE_TIMECOUNTER
  288 u_int
  289 ichpcib_get_timecount(struct timecounter *tc)
  290 {
  291         struct ichpcib_softc *sc = tc->tc_priv;
  292         u_int u1, u2, u3;
  293 
  294         u2 = bus_space_read_4(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_TMR);
  295         u3 = bus_space_read_4(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_TMR);
  296         do {
  297                 u1 = u2;
  298                 u2 = u3;
  299                 u3 = bus_space_read_4(sc->sc_pm_iot, sc->sc_pm_ioh,
  300                     ICH_PM_TMR);
  301         } while (u1 > u2 || u2 > u3);
  302 
  303         return (u2);
  304 }
  305 #endif  /* __HAVE_TIMECOUNTER */

/* [<][>][^][v][top][bottom][index][help] */