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