xref: /openbsd-src/sys/arch/i386/pci/ichpcib.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: ichpcib.c,v 1.23 2008/01/04 00:23:25 kettenis 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 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IB_LPC },
111 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IH_LPC },
112 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IO_LPC },
113 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IR_LPC }
114 };
115 
116 int
117 ichpcib_match(struct device *parent, void *match, void *aux)
118 {
119 	if (pci_matchbyid((struct pci_attach_args *)aux, ichpcib_devices,
120 	    sizeof(ichpcib_devices) / sizeof(ichpcib_devices[0])))
121 		return (2);	/* supersede pcib(4) */
122 	return (0);
123 }
124 
125 void
126 ichpcib_attach(struct device *parent, struct device *self, void *aux)
127 {
128 	struct ichpcib_softc *sc = (struct ichpcib_softc *)self;
129 	struct pci_attach_args *pa = aux;
130 	pcireg_t cntl, pmbase;
131 
132 	/* Check if power management I/O space is enabled */
133 	cntl = pci_conf_read(pa->pa_pc, pa->pa_tag, ICH_ACPI_CNTL);
134 	if ((cntl & ICH_ACPI_CNTL_ACPI_EN) == 0) {
135 		printf(": PM disabled");
136 		goto corepcib;
137 	}
138 
139 	/* Map power management I/O space */
140 	sc->sc_pm_iot = pa->pa_iot;
141 	pmbase = pci_conf_read(pa->pa_pc, pa->pa_tag, ICH_PMBASE);
142 	if (bus_space_map(sc->sc_pm_iot, PCI_MAPREG_IO_ADDR(pmbase),
143 	    ICH_PMSIZE, 0, &sc->sc_pm_ioh) != 0)
144 		goto corepcib;
145 
146 #ifdef __HAVE_TIMECOUNTER
147 	/* Register new timecounter */
148 	ichpcib_timecounter.tc_priv = sc;
149 	tc_init(&ichpcib_timecounter);
150 
151 	printf(": %s-bit timer at %lluHz",
152 	    (ichpcib_timecounter.tc_counter_mask == 0xffffffff ? "32" : "24"),
153 	    (unsigned long long)ichpcib_timecounter.tc_frequency);
154 #endif	/* __HAVE_TIMECOUNTER */
155 
156 #ifndef SMALL_KERNEL
157 	/* Check for SpeedStep */
158 	if (ichss_present(pa)) {
159 		printf(": SpeedStep");
160 
161 		/* Enable SpeedStep */
162 		pci_conf_write(pa->pa_pc, pa->pa_tag, ICH_GEN_PMCON1,
163 		    pci_conf_read(pa->pa_pc, pa->pa_tag, ICH_GEN_PMCON1) |
164 			ICH_GEN_PMCON1_SS_EN);
165 
166 		/* Hook into hw.setperf sysctl */
167 		ichss_sc = sc;
168 		cpu_setperf = ichss_setperf;
169 		setperf_prio = 2;
170 	}
171 #endif /* !SMALL_KERNEL */
172 
173 corepcib:
174 	/* Provide core pcib(4) functionality */
175 	pcibattach(parent, self, aux);
176 }
177 
178 #ifndef SMALL_KERNEL
179 int
180 ichss_present(struct pci_attach_args *pa)
181 {
182 	pcitag_t br_tag;
183 	pcireg_t br_id, br_class;
184 	struct cpu_info *ci;
185 	int family, model, stepping, brandid, ret;
186 
187 	ret = 0;
188 	if (setperf_prio > 2)
189 		return (ret);
190 
191 	ci = curcpu();
192 	family = (ci->ci_signature >> 8) & 15;
193 	model = (ci->ci_signature >> 4) & 15;
194 	stepping = ci->ci_signature & 15;
195 	brandid = cpu_miscinfo & 0xff; /* XXX should put this in ci */
196 
197 	/*
198 	 * This form of SpeedStep works only with certain Intel processors.
199 	 * However, other processors can be coupled with these ICH southbridges
200 	 * causing false positives. This heuristic comes partly from the
201 	 * Linux speedstep-ich driver.
202 	 */
203 	if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801DBM_LPC ||
204 	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801CAM_LPC ||
205 	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801BAM_LPC) {
206 		if (family == 15 && model == 2) {
207 			switch(stepping) {
208 			case 4:
209 				if (brandid == 14 || brandid == 15)
210 					ret = 1;
211 				break;
212 			case 7:
213 				if (brandid == 14)
214 					ret = 1;
215 				break;
216 			case 9:
217 				if (brandid == 14 && strncasecmp(cpu_model,
218 				    p4hint, sizeof(p4hint)-1) == 0) {
219 					ret = 1;
220 				}
221 				break;
222 			}
223 		} else if (family == 6 && model == 11) {
224 			if (stepping == 1)
225 				ret = 1;
226 		}
227 
228 		/*
229 		 * Old revisions of the 82815 hostbridge found on
230 		 * Dell Inspirons 8000 and 8100 don't support
231 		 * SpeedStep.
232 		 */
233 		if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801BAM_LPC) {
234 			/*
235 			 * XXX: dev 0 func 0 is not always a hostbridge,
236 			 * should be converted to use pchb(4) hook.
237 			 */
238 			br_tag = pci_make_tag(pa->pa_pc, pa->pa_bus, 0, 0);
239 			br_id = pci_conf_read(pa->pa_pc, br_tag, PCI_ID_REG);
240 			br_class = pci_conf_read(pa->pa_pc, br_tag, PCI_CLASS_REG);
241 
242 			if (PCI_PRODUCT(br_id) == PCI_PRODUCT_INTEL_82815_HB &&
243 			    PCI_REVISION(br_class) < 5) {
244 				ret = 0;
245 			}
246 		}
247 	}
248 
249 	return (ret);
250 }
251 
252 void
253 ichss_setperf(int level)
254 {
255 	struct ichpcib_softc *sc = ichss_sc;
256 	u_int8_t state, ostate, cntl;
257 	int s;
258 
259 #ifdef DIAGNOSTIC
260 	if (sc == NULL) {
261 		printf("%s: no ichss_sc", __func__);
262 		return;
263 	}
264 #endif
265 
266 	s = splhigh();
267 	state = bus_space_read_1(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_SS_CNTL);
268 	ostate = state;
269 
270 	/* Only two states are available */
271 	if (level <= 50)
272 		state |= ICH_PM_SS_STATE_LOW;
273 	else
274 		state &= ~ICH_PM_SS_STATE_LOW;
275 
276 	/*
277 	 * An Intel SpeedStep technology transition _always_ occur on
278 	 * writes to the ICH_PM_SS_CNTL register, even if the value
279 	 * written is the same as the previous value. So do the write
280 	 * only if the state has changed.
281 	 */
282 	if (state != ostate) {
283 		/* Disable bus mastering arbitration */
284 		cntl = bus_space_read_1(sc->sc_pm_iot, sc->sc_pm_ioh,
285 		    ICH_PM_CNTL);
286 		bus_space_write_1(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_CNTL,
287 		    cntl | ICH_PM_ARB_DIS);
288 
289 		/* Do the transition */
290 		bus_space_write_1(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_SS_CNTL,
291 		    state);
292 
293 		/* Restore bus mastering arbitration state */
294 		bus_space_write_1(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_CNTL,
295 		    cntl);
296 
297 		if (update_cpuspeed != NULL)
298 			update_cpuspeed();
299 	}
300 	splx(s);
301 }
302 #endif	/* !SMALL_KERNEL */
303 
304 #ifdef __HAVE_TIMECOUNTER
305 u_int
306 ichpcib_get_timecount(struct timecounter *tc)
307 {
308 	struct ichpcib_softc *sc = tc->tc_priv;
309 	u_int u1, u2, u3;
310 
311 	u2 = bus_space_read_4(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_TMR);
312 	u3 = bus_space_read_4(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_TMR);
313 	do {
314 		u1 = u2;
315 		u2 = u3;
316 		u3 = bus_space_read_4(sc->sc_pm_iot, sc->sc_pm_ioh,
317 		    ICH_PM_TMR);
318 	} while (u1 > u2 || u2 > u3);
319 
320 	return (u2);
321 }
322 #endif	/* __HAVE_TIMECOUNTER */
323