xref: /openbsd-src/sys/arch/i386/pci/ichpcib.c (revision 24ee467d1e5e812429867003c29e5339cd18bad7)
1*24ee467dScheloha /*	$OpenBSD: ichpcib.c,v 1.33 2023/02/04 19:19:36 cheloha Exp $	*/
2bb3d9585Sgrange /*
3bb3d9585Sgrange  * Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org>
4bb3d9585Sgrange  *
5bb3d9585Sgrange  * Permission to use, copy, modify, and distribute this software for any
6bb3d9585Sgrange  * purpose with or without fee is hereby granted, provided that the above
7bb3d9585Sgrange  * copyright notice and this permission notice appear in all copies.
8bb3d9585Sgrange  *
9bb3d9585Sgrange  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10bb3d9585Sgrange  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11bb3d9585Sgrange  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12bb3d9585Sgrange  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13bb3d9585Sgrange  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14bb3d9585Sgrange  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15bb3d9585Sgrange  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16bb3d9585Sgrange  */
17bb3d9585Sgrange 
18bb3d9585Sgrange /*
19bb3d9585Sgrange  * Special driver for the Intel ICHx/ICHx-M LPC bridges that attaches
20bb3d9585Sgrange  * instead of pcib(4). In addition to the core pcib(4) functionality this
21e809eab5Sgrange  * driver provides support for the Intel SpeedStep technology and
22e809eab5Sgrange  * power management timer.
23bb3d9585Sgrange  */
24bb3d9585Sgrange 
25bb3d9585Sgrange #include <sys/param.h>
26bb3d9585Sgrange #include <sys/systm.h>
27bb3d9585Sgrange #include <sys/device.h>
28bb3d9585Sgrange #include <sys/sysctl.h>
29e809eab5Sgrange #include <sys/timetc.h>
30bb3d9585Sgrange 
31bb3d9585Sgrange #include <machine/bus.h>
32bb3d9585Sgrange 
33bb3d9585Sgrange #include <dev/pci/pcireg.h>
34bb3d9585Sgrange #include <dev/pci/pcivar.h>
35ac975380Sgrange #include <dev/pci/pcidevs.h>
36bb3d9585Sgrange 
37bb3d9585Sgrange #include <dev/pci/ichreg.h>
38bb3d9585Sgrange 
39bb3d9585Sgrange struct ichpcib_softc {
40bb3d9585Sgrange 	struct device sc_dev;
41bb3d9585Sgrange 
42bb3d9585Sgrange 	bus_space_tag_t sc_pm_iot;
43bb3d9585Sgrange 	bus_space_handle_t sc_pm_ioh;
44bb3d9585Sgrange };
45bb3d9585Sgrange 
46bb3d9585Sgrange int	ichpcib_match(struct device *, void *, void *);
47bb3d9585Sgrange void	ichpcib_attach(struct device *, struct device *, void *);
48bb3d9585Sgrange 
49bb3d9585Sgrange int	ichss_present(struct pci_attach_args *);
5096773847Sdim void	ichss_setperf(int);
51bb3d9585Sgrange 
52bb3d9585Sgrange /* arch/i386/pci/pcib.c */
53bb3d9585Sgrange void    pcibattach(struct device *, struct device *, void *);
54bb3d9585Sgrange 
55e809eab5Sgrange u_int	ichpcib_get_timecount(struct timecounter *tc);
56e809eab5Sgrange 
57e809eab5Sgrange struct timecounter ichpcib_timecounter = {
588611d3cdScheloha 	.tc_get_timecount = ichpcib_get_timecount,
598611d3cdScheloha 	.tc_counter_mask = 0xffffff,
608611d3cdScheloha 	.tc_frequency = 3579545,
618611d3cdScheloha 	.tc_name = "ICHPM",
628611d3cdScheloha 	.tc_quality = 1000,
638611d3cdScheloha 	.tc_priv = NULL,
648611d3cdScheloha 	.tc_user = 0,
65e809eab5Sgrange };
66e809eab5Sgrange 
677769e6a4Smpi const struct cfattach ichpcib_ca = {
68c06fda6dSderaadt 	sizeof(struct ichpcib_softc), ichpcib_match, ichpcib_attach
69bb3d9585Sgrange };
70bb3d9585Sgrange 
71bb3d9585Sgrange struct cfdriver ichpcib_cd = {
72bb3d9585Sgrange 	NULL, "ichpcib", DV_DULL
73bb3d9585Sgrange };
74bb3d9585Sgrange 
75bb3d9585Sgrange #ifndef SMALL_KERNEL
76591e0f53Sdim static const char p4hint[] = "Mobile Intel(R) Pentium(R) 4";
77fe42e991Sgwk struct ichpcib_softc *ichss_sc;
78bb3d9585Sgrange extern int setperf_prio;
79bb3d9585Sgrange #endif	/* !SMALL_KERNEL */
80bb3d9585Sgrange 
81ac975380Sgrange const struct pci_matchid ichpcib_devices[] = {
824c1f80d3Sbrad 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_6300ESB_LPC },
83c039bb67Sbrad 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_6321ESB_LPC },
84ac975380Sgrange 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801AA_LPC },
85ac975380Sgrange 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801AB_LPC },
86ac975380Sgrange 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801BA_LPC },
87ac975380Sgrange 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801BAM_LPC },
88ac975380Sgrange 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801CA_LPC },
89ac975380Sgrange 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801CAM_LPC },
90ac975380Sgrange 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801DB_LPC },
91ac975380Sgrange 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801DBM_LPC },
924c1f80d3Sbrad 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801E_LPC },
93afeb966fSgrange 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801EB_LPC },
94e0cd2d8eSjsg 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801FB_LPC },
954c1f80d3Sbrad 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801FBM_LPC },
964c1f80d3Sbrad 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GB_LPC },
974c1f80d3Sbrad 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GBM_LPC },
984c1f80d3Sbrad 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GH_LPC },
995080bc1bSbrad 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GHM_LPC },
100e9e05614Sjsg 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801H_LPC },
10109e7fbc8Sbrad 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801HBM_LPC },
10209e7fbc8Sbrad 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IB_LPC },
10309e7fbc8Sbrad 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IH_LPC },
10409e7fbc8Sbrad 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IO_LPC },
10509e7fbc8Sbrad 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IR_LPC }
106ac975380Sgrange };
107ac975380Sgrange 
108bb3d9585Sgrange int
ichpcib_match(struct device * parent,void * match,void * aux)109bb3d9585Sgrange ichpcib_match(struct device *parent, void *match, void *aux)
110bb3d9585Sgrange {
111ac975380Sgrange 	if (pci_matchbyid((struct pci_attach_args *)aux, ichpcib_devices,
112ac975380Sgrange 	    sizeof(ichpcib_devices) / sizeof(ichpcib_devices[0])))
113bb3d9585Sgrange 		return (2);	/* supersede pcib(4) */
114bb3d9585Sgrange 	return (0);
115bb3d9585Sgrange }
116bb3d9585Sgrange 
117bb3d9585Sgrange void
ichpcib_attach(struct device * parent,struct device * self,void * aux)118bb3d9585Sgrange ichpcib_attach(struct device *parent, struct device *self, void *aux)
119bb3d9585Sgrange {
120bb3d9585Sgrange 	struct ichpcib_softc *sc = (struct ichpcib_softc *)self;
121bb3d9585Sgrange 	struct pci_attach_args *pa = aux;
122ac975380Sgrange 	pcireg_t cntl, pmbase;
123ac975380Sgrange 
124ac975380Sgrange 	/* Check if power management I/O space is enabled */
125ac975380Sgrange 	cntl = pci_conf_read(pa->pa_pc, pa->pa_tag, ICH_ACPI_CNTL);
126ac975380Sgrange 	if ((cntl & ICH_ACPI_CNTL_ACPI_EN) == 0) {
127ac975380Sgrange 		printf(": PM disabled");
128ac975380Sgrange 		goto corepcib;
129ac975380Sgrange 	}
130bb3d9585Sgrange 
131bb3d9585Sgrange 	/* Map power management I/O space */
132bb3d9585Sgrange 	sc->sc_pm_iot = pa->pa_iot;
133bb3d9585Sgrange 	pmbase = pci_conf_read(pa->pa_pc, pa->pa_tag, ICH_PMBASE);
134bb3d9585Sgrange 	if (bus_space_map(sc->sc_pm_iot, PCI_MAPREG_IO_ADDR(pmbase),
1357c6d77ceSderaadt 	    ICH_PMSIZE, 0, &sc->sc_pm_ioh) != 0)
136bb3d9585Sgrange 		goto corepcib;
137bb3d9585Sgrange 
138ac975380Sgrange 	/* Register new timecounter */
139e809eab5Sgrange 	ichpcib_timecounter.tc_priv = sc;
140e809eab5Sgrange 	tc_init(&ichpcib_timecounter);
141ac975380Sgrange 
142ac975380Sgrange 	printf(": %s-bit timer at %lluHz",
143ac975380Sgrange 	    (ichpcib_timecounter.tc_counter_mask == 0xffffffff ? "32" : "24"),
144ac975380Sgrange 	    (unsigned long long)ichpcib_timecounter.tc_frequency);
145e809eab5Sgrange 
146e809eab5Sgrange #ifndef SMALL_KERNEL
147bb3d9585Sgrange 	/* Check for SpeedStep */
148bb3d9585Sgrange 	if (ichss_present(pa)) {
149bb3d9585Sgrange 		printf(": SpeedStep");
150bb3d9585Sgrange 
151bb3d9585Sgrange 		/* Enable SpeedStep */
152bb3d9585Sgrange 		pci_conf_write(pa->pa_pc, pa->pa_tag, ICH_GEN_PMCON1,
153bb3d9585Sgrange 		    pci_conf_read(pa->pa_pc, pa->pa_tag, ICH_GEN_PMCON1) |
154bb3d9585Sgrange 			ICH_GEN_PMCON1_SS_EN);
155bb3d9585Sgrange 
156bb3d9585Sgrange 		/* Hook into hw.setperf sysctl */
157fe42e991Sgwk 		ichss_sc = sc;
158bb3d9585Sgrange 		cpu_setperf = ichss_setperf;
159bb3d9585Sgrange 		setperf_prio = 2;
160bb3d9585Sgrange 	}
161e809eab5Sgrange #endif /* !SMALL_KERNEL */
162bb3d9585Sgrange 
163bb3d9585Sgrange corepcib:
164bb3d9585Sgrange 	/* Provide core pcib(4) functionality */
165bb3d9585Sgrange 	pcibattach(parent, self, aux);
166bb3d9585Sgrange }
167bb3d9585Sgrange 
168bb3d9585Sgrange #ifndef SMALL_KERNEL
169bb3d9585Sgrange int
ichss_present(struct pci_attach_args * pa)170bb3d9585Sgrange ichss_present(struct pci_attach_args *pa)
171bb3d9585Sgrange {
172bb3d9585Sgrange 	pcitag_t br_tag;
173bb3d9585Sgrange 	pcireg_t br_id, br_class;
174591e0f53Sdim 	struct cpu_info *ci;
17555a41d06Sgwk 	int family, model, stepping, brandid, ret;
176bb3d9585Sgrange 
17755a41d06Sgwk 	ret = 0;
178bb3d9585Sgrange 	if (setperf_prio > 2)
17955a41d06Sgwk 		return (ret);
180bb3d9585Sgrange 
181591e0f53Sdim 	ci = curcpu();
182591e0f53Sdim 	family = (ci->ci_signature >> 8) & 15;
183591e0f53Sdim 	model = (ci->ci_signature >> 4) & 15;
184591e0f53Sdim 	stepping = ci->ci_signature & 15;
185591e0f53Sdim 	brandid = cpu_miscinfo & 0xff; /* XXX should put this in ci */
186591e0f53Sdim 
187591e0f53Sdim 	/*
18855a41d06Sgwk 	 * This form of SpeedStep works only with certain Intel processors.
18955a41d06Sgwk 	 * However, other processors can be coupled with these ICH southbridges
19055a41d06Sgwk 	 * causing false positives. This heuristic comes partly from the
19155a41d06Sgwk 	 * Linux speedstep-ich driver.
192591e0f53Sdim 	 */
193bb3d9585Sgrange 	if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801DBM_LPC ||
19455a41d06Sgwk 	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801CAM_LPC ||
19555a41d06Sgwk 	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801BAM_LPC) {
19655a41d06Sgwk 		if (family == 15 && model == 2) {
19755a41d06Sgwk 			switch(stepping) {
19855a41d06Sgwk 			case 4:
19955a41d06Sgwk 				if (brandid == 14 || brandid == 15)
20055a41d06Sgwk 					ret = 1;
20155a41d06Sgwk 				break;
20255a41d06Sgwk 			case 7:
20355a41d06Sgwk 				if (brandid == 14)
20455a41d06Sgwk 					ret = 1;
20555a41d06Sgwk 				break;
20655a41d06Sgwk 			case 9:
20755a41d06Sgwk 				if (brandid == 14 && strncasecmp(cpu_model,
20855a41d06Sgwk 				    p4hint, sizeof(p4hint)-1) == 0) {
20955a41d06Sgwk 					ret = 1;
21055a41d06Sgwk 				}
21155a41d06Sgwk 				break;
21255a41d06Sgwk 			}
21355a41d06Sgwk 		} else if (family == 6 && model == 11) {
21455a41d06Sgwk 			if (stepping == 1)
21555a41d06Sgwk 				ret = 1;
21655a41d06Sgwk 		}
21755a41d06Sgwk 
218bb3d9585Sgrange 		/*
219bb3d9585Sgrange 		 * Old revisions of the 82815 hostbridge found on
220bb3d9585Sgrange 		 * Dell Inspirons 8000 and 8100 don't support
221bb3d9585Sgrange 		 * SpeedStep.
222bb3d9585Sgrange 		 */
22355a41d06Sgwk 		if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801BAM_LPC) {
224bb3d9585Sgrange 			/*
225bb3d9585Sgrange 			 * XXX: dev 0 func 0 is not always a hostbridge,
226bb3d9585Sgrange 			 * should be converted to use pchb(4) hook.
227bb3d9585Sgrange 			 */
228bb3d9585Sgrange 			br_tag = pci_make_tag(pa->pa_pc, pa->pa_bus, 0, 0);
229bb3d9585Sgrange 			br_id = pci_conf_read(pa->pa_pc, br_tag, PCI_ID_REG);
230bb3d9585Sgrange 			br_class = pci_conf_read(pa->pa_pc, br_tag, PCI_CLASS_REG);
231bb3d9585Sgrange 
232c839f1b1Skettenis 			if (PCI_PRODUCT(br_id) == PCI_PRODUCT_INTEL_82815_HB &&
23355a41d06Sgwk 			    PCI_REVISION(br_class) < 5) {
23455a41d06Sgwk 				ret = 0;
23555a41d06Sgwk 			}
23655a41d06Sgwk 		}
237bb3d9585Sgrange 	}
238bb3d9585Sgrange 
23955a41d06Sgwk 	return (ret);
240bb3d9585Sgrange }
241bb3d9585Sgrange 
24296773847Sdim void
ichss_setperf(int level)243bb3d9585Sgrange ichss_setperf(int level)
244bb3d9585Sgrange {
245fe42e991Sgwk 	struct ichpcib_softc *sc = ichss_sc;
246bb3d9585Sgrange 	u_int8_t state, ostate, cntl;
247bb3d9585Sgrange 	int s;
248bb3d9585Sgrange 
249bb3d9585Sgrange #ifdef DIAGNOSTIC
250bb3d9585Sgrange 	if (sc == NULL) {
251fe42e991Sgwk 		printf("%s: no ichss_sc", __func__);
25296773847Sdim 		return;
253bb3d9585Sgrange 	}
254bb3d9585Sgrange #endif
255bb3d9585Sgrange 
256bb3d9585Sgrange 	s = splhigh();
257bb3d9585Sgrange 	state = bus_space_read_1(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_SS_CNTL);
258bb3d9585Sgrange 	ostate = state;
259bb3d9585Sgrange 
260bb3d9585Sgrange 	/* Only two states are available */
261bb3d9585Sgrange 	if (level <= 50)
262bb3d9585Sgrange 		state |= ICH_PM_SS_STATE_LOW;
263bb3d9585Sgrange 	else
264bb3d9585Sgrange 		state &= ~ICH_PM_SS_STATE_LOW;
265bb3d9585Sgrange 
266bb3d9585Sgrange 	/*
267bb3d9585Sgrange 	 * An Intel SpeedStep technology transition _always_ occur on
268bb3d9585Sgrange 	 * writes to the ICH_PM_SS_CNTL register, even if the value
269bb3d9585Sgrange 	 * written is the same as the previous value. So do the write
270bb3d9585Sgrange 	 * only if the state has changed.
271bb3d9585Sgrange 	 */
272bb3d9585Sgrange 	if (state != ostate) {
273bb3d9585Sgrange 		/* Disable bus mastering arbitration */
274bb3d9585Sgrange 		cntl = bus_space_read_1(sc->sc_pm_iot, sc->sc_pm_ioh,
275bb3d9585Sgrange 		    ICH_PM_CNTL);
276bb3d9585Sgrange 		bus_space_write_1(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_CNTL,
277bb3d9585Sgrange 		    cntl | ICH_PM_ARB_DIS);
278bb3d9585Sgrange 
279bb3d9585Sgrange 		/* Do the transition */
280bb3d9585Sgrange 		bus_space_write_1(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_SS_CNTL,
281bb3d9585Sgrange 		    state);
282bb3d9585Sgrange 
283bb3d9585Sgrange 		/* Restore bus mastering arbitration state */
284bb3d9585Sgrange 		bus_space_write_1(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_CNTL,
285bb3d9585Sgrange 		    cntl);
286163e6d2eSgrange 
287163e6d2eSgrange 		if (update_cpuspeed != NULL)
288163e6d2eSgrange 			update_cpuspeed();
289bb3d9585Sgrange 	}
290bb3d9585Sgrange 	splx(s);
291bb3d9585Sgrange }
292bb3d9585Sgrange #endif	/* !SMALL_KERNEL */
293e809eab5Sgrange 
294e809eab5Sgrange u_int
ichpcib_get_timecount(struct timecounter * tc)295e809eab5Sgrange ichpcib_get_timecount(struct timecounter *tc)
296e809eab5Sgrange {
297e809eab5Sgrange 	struct ichpcib_softc *sc = tc->tc_priv;
298e809eab5Sgrange 	u_int u1, u2, u3;
299e809eab5Sgrange 
300e809eab5Sgrange 	u2 = bus_space_read_4(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_TMR);
301e809eab5Sgrange 	u3 = bus_space_read_4(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_TMR);
302e809eab5Sgrange 	do {
303e809eab5Sgrange 		u1 = u2;
304e809eab5Sgrange 		u2 = u3;
305e809eab5Sgrange 		u3 = bus_space_read_4(sc->sc_pm_iot, sc->sc_pm_ioh,
306e809eab5Sgrange 		    ICH_PM_TMR);
307e809eab5Sgrange 	} while (u1 > u2 || u2 > u3);
308e809eab5Sgrange 
309e809eab5Sgrange 	return (u2);
310e809eab5Sgrange }
311