xref: /openbsd-src/sys/arch/i386/pci/ichpcib.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /*	$OpenBSD: ichpcib.c,v 1.25 2010/07/08 20:17:54 deraadt 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/proc.h>
29 #include <sys/sysctl.h>
30 #ifdef __HAVE_TIMECOUNTER
31 #include <sys/timetc.h>
32 #endif
33 
34 #include <machine/bus.h>
35 
36 #include <dev/pci/pcireg.h>
37 #include <dev/pci/pcivar.h>
38 #include <dev/pci/pcidevs.h>
39 
40 #include <dev/pci/ichreg.h>
41 
42 #include <machine/cpu.h>
43 #include <machine/cpufunc.h>
44 
45 struct ichpcib_softc {
46 	struct device sc_dev;
47 
48 	bus_space_tag_t sc_pm_iot;
49 	bus_space_handle_t sc_pm_ioh;
50 };
51 
52 int	ichpcib_match(struct device *, void *, void *);
53 void	ichpcib_attach(struct device *, struct device *, void *);
54 
55 int	ichss_present(struct pci_attach_args *);
56 void	ichss_setperf(int);
57 
58 /* arch/i386/pci/pcib.c */
59 void    pcibattach(struct device *, struct device *, void *);
60 
61 #ifdef __HAVE_TIMECOUNTER
62 u_int	ichpcib_get_timecount(struct timecounter *tc);
63 
64 struct timecounter ichpcib_timecounter = {
65 	ichpcib_get_timecount,	/* get_timecount */
66 	0,			/* no poll_pps */
67 	0xffffff,		/* counter_mask */
68 	3579545,		/* frequency */
69 	"ICHPM",		/* name */
70 	1000			/* quality */
71 };
72 #endif	/* __HAVE_TIMECOUNTER */
73 
74 struct cfattach ichpcib_ca = {
75 	sizeof(struct ichpcib_softc),
76 	ichpcib_match,
77 	ichpcib_attach,
78 	NULL,
79 	config_activate_children
80 };
81 
82 struct cfdriver ichpcib_cd = {
83 	NULL, "ichpcib", DV_DULL
84 };
85 
86 #ifndef SMALL_KERNEL
87 static const char p4hint[] = "Mobile Intel(R) Pentium(R) 4";
88 struct ichpcib_softc *ichss_sc;
89 extern int setperf_prio;
90 #endif	/* !SMALL_KERNEL */
91 
92 const struct pci_matchid ichpcib_devices[] = {
93 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_6300ESB_LPC },
94 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_6321ESB_LPC },
95 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801AA_LPC },
96 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801AB_LPC },
97 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801BA_LPC },
98 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801BAM_LPC },
99 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801CA_LPC },
100 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801CAM_LPC },
101 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801DB_LPC },
102 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801DBM_LPC },
103 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801E_LPC },
104 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801EB_LPC },
105 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801FB_LPC },
106 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801FBM_LPC },
107 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GB_LPC },
108 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GBM_LPC },
109 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GH_LPC },
110 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GHM_LPC },
111 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801H_LPC },
112 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801HBM_LPC },
113 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IB_LPC },
114 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IH_LPC },
115 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IO_LPC },
116 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IR_LPC }
117 };
118 
119 int
120 ichpcib_match(struct device *parent, void *match, void *aux)
121 {
122 	if (pci_matchbyid((struct pci_attach_args *)aux, ichpcib_devices,
123 	    sizeof(ichpcib_devices) / sizeof(ichpcib_devices[0])))
124 		return (2);	/* supersede pcib(4) */
125 	return (0);
126 }
127 
128 void
129 ichpcib_attach(struct device *parent, struct device *self, void *aux)
130 {
131 	struct ichpcib_softc *sc = (struct ichpcib_softc *)self;
132 	struct pci_attach_args *pa = aux;
133 	pcireg_t cntl, pmbase;
134 
135 	/* Check if power management I/O space is enabled */
136 	cntl = pci_conf_read(pa->pa_pc, pa->pa_tag, ICH_ACPI_CNTL);
137 	if ((cntl & ICH_ACPI_CNTL_ACPI_EN) == 0) {
138 		printf(": PM disabled");
139 		goto corepcib;
140 	}
141 
142 	/* Map power management I/O space */
143 	sc->sc_pm_iot = pa->pa_iot;
144 	pmbase = pci_conf_read(pa->pa_pc, pa->pa_tag, ICH_PMBASE);
145 	if (bus_space_map(sc->sc_pm_iot, PCI_MAPREG_IO_ADDR(pmbase),
146 	    ICH_PMSIZE, 0, &sc->sc_pm_ioh) != 0)
147 		goto corepcib;
148 
149 #ifdef __HAVE_TIMECOUNTER
150 	/* Register new timecounter */
151 	ichpcib_timecounter.tc_priv = sc;
152 	tc_init(&ichpcib_timecounter);
153 
154 	printf(": %s-bit timer at %lluHz",
155 	    (ichpcib_timecounter.tc_counter_mask == 0xffffffff ? "32" : "24"),
156 	    (unsigned long long)ichpcib_timecounter.tc_frequency);
157 #endif	/* __HAVE_TIMECOUNTER */
158 
159 #ifndef SMALL_KERNEL
160 	/* Check for SpeedStep */
161 	if (ichss_present(pa)) {
162 		printf(": SpeedStep");
163 
164 		/* Enable SpeedStep */
165 		pci_conf_write(pa->pa_pc, pa->pa_tag, ICH_GEN_PMCON1,
166 		    pci_conf_read(pa->pa_pc, pa->pa_tag, ICH_GEN_PMCON1) |
167 			ICH_GEN_PMCON1_SS_EN);
168 
169 		/* Hook into hw.setperf sysctl */
170 		ichss_sc = sc;
171 		cpu_setperf = ichss_setperf;
172 		setperf_prio = 2;
173 	}
174 #endif /* !SMALL_KERNEL */
175 
176 corepcib:
177 	/* Provide core pcib(4) functionality */
178 	pcibattach(parent, self, aux);
179 }
180 
181 #ifndef SMALL_KERNEL
182 int
183 ichss_present(struct pci_attach_args *pa)
184 {
185 	pcitag_t br_tag;
186 	pcireg_t br_id, br_class;
187 	struct cpu_info *ci;
188 	int family, model, stepping, brandid, ret;
189 
190 	ret = 0;
191 	if (setperf_prio > 2)
192 		return (ret);
193 
194 	ci = curcpu();
195 	family = (ci->ci_signature >> 8) & 15;
196 	model = (ci->ci_signature >> 4) & 15;
197 	stepping = ci->ci_signature & 15;
198 	brandid = cpu_miscinfo & 0xff; /* XXX should put this in ci */
199 
200 	/*
201 	 * This form of SpeedStep works only with certain Intel processors.
202 	 * However, other processors can be coupled with these ICH southbridges
203 	 * causing false positives. This heuristic comes partly from the
204 	 * Linux speedstep-ich driver.
205 	 */
206 	if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801DBM_LPC ||
207 	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801CAM_LPC ||
208 	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801BAM_LPC) {
209 		if (family == 15 && model == 2) {
210 			switch(stepping) {
211 			case 4:
212 				if (brandid == 14 || brandid == 15)
213 					ret = 1;
214 				break;
215 			case 7:
216 				if (brandid == 14)
217 					ret = 1;
218 				break;
219 			case 9:
220 				if (brandid == 14 && strncasecmp(cpu_model,
221 				    p4hint, sizeof(p4hint)-1) == 0) {
222 					ret = 1;
223 				}
224 				break;
225 			}
226 		} else if (family == 6 && model == 11) {
227 			if (stepping == 1)
228 				ret = 1;
229 		}
230 
231 		/*
232 		 * Old revisions of the 82815 hostbridge found on
233 		 * Dell Inspirons 8000 and 8100 don't support
234 		 * SpeedStep.
235 		 */
236 		if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801BAM_LPC) {
237 			/*
238 			 * XXX: dev 0 func 0 is not always a hostbridge,
239 			 * should be converted to use pchb(4) hook.
240 			 */
241 			br_tag = pci_make_tag(pa->pa_pc, pa->pa_bus, 0, 0);
242 			br_id = pci_conf_read(pa->pa_pc, br_tag, PCI_ID_REG);
243 			br_class = pci_conf_read(pa->pa_pc, br_tag, PCI_CLASS_REG);
244 
245 			if (PCI_PRODUCT(br_id) == PCI_PRODUCT_INTEL_82815_HB &&
246 			    PCI_REVISION(br_class) < 5) {
247 				ret = 0;
248 			}
249 		}
250 	}
251 
252 	return (ret);
253 }
254 
255 void
256 ichss_setperf(int level)
257 {
258 	struct ichpcib_softc *sc = ichss_sc;
259 	u_int8_t state, ostate, cntl;
260 	int s;
261 
262 #ifdef DIAGNOSTIC
263 	if (sc == NULL) {
264 		printf("%s: no ichss_sc", __func__);
265 		return;
266 	}
267 #endif
268 
269 	s = splhigh();
270 	state = bus_space_read_1(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_SS_CNTL);
271 	ostate = state;
272 
273 	/* Only two states are available */
274 	if (level <= 50)
275 		state |= ICH_PM_SS_STATE_LOW;
276 	else
277 		state &= ~ICH_PM_SS_STATE_LOW;
278 
279 	/*
280 	 * An Intel SpeedStep technology transition _always_ occur on
281 	 * writes to the ICH_PM_SS_CNTL register, even if the value
282 	 * written is the same as the previous value. So do the write
283 	 * only if the state has changed.
284 	 */
285 	if (state != ostate) {
286 		/* Disable bus mastering arbitration */
287 		cntl = bus_space_read_1(sc->sc_pm_iot, sc->sc_pm_ioh,
288 		    ICH_PM_CNTL);
289 		bus_space_write_1(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_CNTL,
290 		    cntl | ICH_PM_ARB_DIS);
291 
292 		/* Do the transition */
293 		bus_space_write_1(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_SS_CNTL,
294 		    state);
295 
296 		/* Restore bus mastering arbitration state */
297 		bus_space_write_1(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_CNTL,
298 		    cntl);
299 
300 		if (update_cpuspeed != NULL)
301 			update_cpuspeed();
302 	}
303 	splx(s);
304 }
305 #endif	/* !SMALL_KERNEL */
306 
307 #ifdef __HAVE_TIMECOUNTER
308 u_int
309 ichpcib_get_timecount(struct timecounter *tc)
310 {
311 	struct ichpcib_softc *sc = tc->tc_priv;
312 	u_int u1, u2, u3;
313 
314 	u2 = bus_space_read_4(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_TMR);
315 	u3 = bus_space_read_4(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_TMR);
316 	do {
317 		u1 = u2;
318 		u2 = u3;
319 		u3 = bus_space_read_4(sc->sc_pm_iot, sc->sc_pm_ioh,
320 		    ICH_PM_TMR);
321 	} while (u1 > u2 || u2 > u3);
322 
323 	return (u2);
324 }
325 #endif	/* __HAVE_TIMECOUNTER */
326