ichss.c revision 142625
1141241Snjl/*-
2141241Snjl * Copyright (c) 2004-2005 Nate Lawson (SDG)
3141241Snjl * All rights reserved.
4141241Snjl *
5141241Snjl * Redistribution and use in source and binary forms, with or without
6141241Snjl * modification, are permitted provided that the following conditions
7141241Snjl * are met:
8141241Snjl * 1. Redistributions of source code must retain the above copyright
9141241Snjl *    notice, this list of conditions and the following disclaimer.
10141241Snjl * 2. Redistributions in binary form must reproduce the above copyright
11141241Snjl *    notice, this list of conditions and the following disclaimer in the
12141241Snjl *    documentation and/or other materials provided with the distribution.
13141241Snjl *
14141241Snjl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15141241Snjl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16141241Snjl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17141241Snjl * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18141241Snjl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19141241Snjl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20141241Snjl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21141241Snjl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22141241Snjl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23141241Snjl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24141241Snjl * SUCH DAMAGE.
25141241Snjl */
26141241Snjl
27141241Snjl#include <sys/cdefs.h>
28141241Snjl__FBSDID("$FreeBSD: head/sys/dev/cpufreq/ichss.c 142625 2005-02-27 02:43:02Z njl $");
29141241Snjl
30141241Snjl#include <sys/param.h>
31141241Snjl#include <sys/bus.h>
32141241Snjl#include <sys/cpu.h>
33141241Snjl#include <sys/kernel.h>
34141241Snjl#include <sys/malloc.h>
35141241Snjl#include <sys/module.h>
36141241Snjl#include <sys/pcpu.h>
37141241Snjl#include <sys/sysctl.h>
38141241Snjl#include <sys/systm.h>
39141241Snjl
40141241Snjl#include <dev/pci/pcivar.h>
41141293Simp#include <machine/bus.h>
42141241Snjl#include <machine/clock.h>
43141241Snjl#include <machine/resource.h>
44141241Snjl#include <sys/rman.h>
45141241Snjl
46141241Snjl#include "cpufreq_if.h"
47141241Snjl
48141241Snjl/*
49141241Snjl * The SpeedStep ICH feature is a chipset-initiated voltage and frequency
50141241Snjl * transition available on the ICH2M, 3M, and 4M.  It is different from
51141241Snjl * the newer Pentium-M SpeedStep feature.  It offers only two levels of
52141241Snjl * frequency/voltage.  Often, the BIOS will select one of the levels via
53141241Snjl * SMM code during the power-on process (i.e., choose a lower level if the
54141241Snjl * system is off AC power.)
55141241Snjl */
56141241Snjl
57141241Snjlstruct ichss_softc {
58141241Snjl	device_t	 dev;
59141241Snjl	int		 bm_rid;	/* Bus-mastering control (PM2REG). */
60141241Snjl	struct resource	*bm_reg;
61141241Snjl	int		 ctrl_rid;	/* Control/status register. */
62141241Snjl	struct resource	*ctrl_reg;
63141241Snjl	struct cf_setting sets[2];	/* Only two settings. */
64141241Snjl};
65141241Snjl
66141241Snjl/* Supported PCI IDs. */
67141241Snjl#define PCI_VENDOR_INTEL	0x8086
68141241Snjl#define PCI_DEV_82801BA		0x244c /* ICH2M */
69141241Snjl#define PCI_DEV_82801CA		0x248c /* ICH3M */
70141241Snjl#define PCI_DEV_82801DB		0x24cc /* ICH4M */
71141241Snjl#define PCI_DEV_82815BA		0x1130 /* Unsupported/buggy part */
72141241Snjl
73141241Snjl/* PCI config registers for finding PMBASE and enabling SpeedStep. */
74141241Snjl#define ICHSS_PMBASE_OFFSET	0x40
75141241Snjl#define ICHSS_PMCFG_OFFSET	0xa0
76141241Snjl
77141241Snjl/* Values and masks. */
78141241Snjl#define ICHSS_ENABLE		(1<<3)	/* Enable SpeedStep control. */
79141241Snjl#define ICHSS_IO_REG		0x1	/* Access register via I/O space. */
80141241Snjl#define ICHSS_PMBASE_MASK	0xff80	/* PMBASE address bits. */
81141241Snjl#define ICHSS_CTRL_BIT		0x1	/* 0 is high speed, 1 is low. */
82141241Snjl#define ICHSS_BM_DISABLE	0x1
83141241Snjl
84141241Snjl/* Offsets from PMBASE for various registers. */
85141241Snjl#define ICHSS_BM_OFFSET		0x20
86141241Snjl#define ICHSS_CTRL_OFFSET	0x50
87141241Snjl
88141241Snjl#define ICH_GET_REG(reg) 				\
89141241Snjl	(bus_space_read_1(rman_get_bustag((reg)), 	\
90141241Snjl	    rman_get_bushandle((reg)), 0))
91141241Snjl#define ICH_SET_REG(reg, val)				\
92141241Snjl	(bus_space_write_1(rman_get_bustag((reg)), 	\
93141241Snjl	    rman_get_bushandle((reg)), 0, (val)))
94141241Snjl
95141241Snjlstatic int	ichss_pci_probe(device_t dev);
96141241Snjlstatic int	ichss_probe(device_t dev);
97141241Snjlstatic int	ichss_attach(device_t dev);
98141241Snjlstatic int	ichss_detach(device_t dev);
99141241Snjlstatic int	ichss_settings(device_t dev, struct cf_setting *sets,
100142032Snjl		    int *count);
101141241Snjlstatic int	ichss_set(device_t dev, const struct cf_setting *set);
102141241Snjlstatic int	ichss_get(device_t dev, struct cf_setting *set);
103142032Snjlstatic int	ichss_type(device_t dev, int *type);
104141241Snjl
105141241Snjlstatic device_method_t ichss_methods[] = {
106141241Snjl	/* Device interface */
107141241Snjl	DEVMETHOD(device_probe,		ichss_probe),
108141241Snjl	DEVMETHOD(device_attach,	ichss_attach),
109141241Snjl	DEVMETHOD(device_detach,	ichss_detach),
110141241Snjl
111141241Snjl	/* cpufreq interface */
112141241Snjl	DEVMETHOD(cpufreq_drv_set,	ichss_set),
113141241Snjl	DEVMETHOD(cpufreq_drv_get,	ichss_get),
114142032Snjl	DEVMETHOD(cpufreq_drv_type,	ichss_type),
115141241Snjl	DEVMETHOD(cpufreq_drv_settings,	ichss_settings),
116141241Snjl	{0, 0}
117141241Snjl};
118141241Snjlstatic driver_t ichss_driver = {
119141241Snjl	"ichss", ichss_methods, sizeof(struct ichss_softc)
120141241Snjl};
121141241Snjlstatic devclass_t ichss_devclass;
122141241SnjlDRIVER_MODULE(ichss, cpu, ichss_driver, ichss_devclass, 0, 0);
123141241Snjl
124141241Snjlstatic device_method_t ichss_pci_methods[] = {
125141241Snjl	DEVMETHOD(device_probe,		ichss_pci_probe),
126141241Snjl	{0, 0}
127141241Snjl};
128141241Snjlstatic driver_t ichss_pci_driver = {
129141241Snjl	"ichss_pci", ichss_pci_methods, 0
130141241Snjl};
131141241Snjlstatic devclass_t ichss_pci_devclass;
132141241SnjlDRIVER_MODULE(ichss_pci, pci, ichss_pci_driver, ichss_pci_devclass, 0, 0);
133141241Snjl
134141241Snjl#if 0
135141241Snjl#define DPRINT(x...)	printf(x)
136141241Snjl#else
137141241Snjl#define DPRINT(x...)
138141241Snjl#endif
139141241Snjl
140141241Snjl/*
141141241Snjl * We detect the chipset by looking for its LPC bus ID during the PCI
142141241Snjl * scan and reading its config registers during the probe.  However,
143141241Snjl * we add the ichss child under the cpu device since even though the
144141241Snjl * chipset provides the control, it really affects the cpu only.
145141241Snjl *
146141241Snjl * XXX This approach does not work if the module is loaded after boot.
147141241Snjl */
148141241Snjlstatic int
149141241Snjlichss_pci_probe(device_t dev)
150141241Snjl{
151141241Snjl	device_t child, parent;
152141241Snjl	uint32_t pmbase;
153141241Snjl	uint16_t ss_en;
154141241Snjl
155141241Snjl	/*
156141241Snjl	 * TODO: add a quirk to disable if we see the 82815_MC along
157141241Snjl	 * with the 82801BA and revision < 5.
158141241Snjl	 */
159141241Snjl	if (pci_get_vendor(dev) != PCI_VENDOR_INTEL ||
160141241Snjl	    (pci_get_device(dev) != PCI_DEV_82801BA &&
161141241Snjl	    pci_get_device(dev) != PCI_DEV_82801CA &&
162141241Snjl	    pci_get_device(dev) != PCI_DEV_82801DB))
163141241Snjl		return (ENXIO);
164141241Snjl
165141241Snjl	/* Only one CPU is supported for this hardware. */
166141241Snjl	if (devclass_get_device(ichss_devclass, 0))
167141241Snjl		return (ENXIO);
168141241Snjl
169142625Snjl	/*
170142625Snjl	 * Add a child under the CPU parent.  It appears that ICH SpeedStep
171142625Snjl	 * only requires a single CPU to set the value (since the chipset
172142625Snjl	 * is shared by all CPUs.)  Thus, we only add a child to cpu 0.
173142625Snjl	 */
174141241Snjl	parent = devclass_get_device(devclass_find("cpu"), 0);
175141241Snjl	KASSERT(parent != NULL, ("cpu parent is NULL"));
176141241Snjl	child = BUS_ADD_CHILD(parent, 0, "ichss", 0);
177141241Snjl	if (child == NULL) {
178141241Snjl		device_printf(parent, "add SpeedStep child failed\n");
179141241Snjl		return (ENXIO);
180141241Snjl	}
181141241Snjl
182141241Snjl	/* Find the PMBASE register from our PCI config header. */
183141241Snjl	pmbase = pci_read_config(dev, ICHSS_PMBASE_OFFSET, sizeof(pmbase));
184141241Snjl	if ((pmbase & ICHSS_IO_REG) == 0) {
185141241Snjl		printf("ichss: invalid PMBASE memory type\n");
186141241Snjl		return (ENXIO);
187141241Snjl	}
188141241Snjl	pmbase &= ICHSS_PMBASE_MASK;
189141241Snjl	if (pmbase == 0) {
190141241Snjl		printf("ichss: invalid zero PMBASE address\n");
191141241Snjl		return (ENXIO);
192141241Snjl	}
193141241Snjl	DPRINT("ichss: PMBASE is %#x\n", pmbase);
194141241Snjl
195141241Snjl	/* Add the bus master arbitration and control registers. */
196141241Snjl	bus_set_resource(child, SYS_RES_IOPORT, 0, pmbase + ICHSS_BM_OFFSET,
197141241Snjl	    1);
198141241Snjl	bus_set_resource(child, SYS_RES_IOPORT, 1, pmbase + ICHSS_CTRL_OFFSET,
199141241Snjl	    1);
200141241Snjl
201141241Snjl	/* Activate SpeedStep control if not already enabled. */
202141241Snjl	ss_en = pci_read_config(dev, ICHSS_PMCFG_OFFSET, sizeof(ss_en));
203141241Snjl	if ((ss_en & ICHSS_ENABLE) == 0) {
204141241Snjl		printf("ichss: enabling SpeedStep support\n");
205141241Snjl		pci_write_config(dev, ICHSS_PMCFG_OFFSET,
206141241Snjl		    ss_en | ICHSS_ENABLE, sizeof(ss_en));
207141241Snjl	}
208141241Snjl
209141241Snjl	/* Attach the new CPU child now. */
210141241Snjl	device_probe_and_attach(child);
211141241Snjl
212141241Snjl	return (ENXIO);
213141241Snjl}
214141241Snjl
215141241Snjlstatic int
216141241Snjlichss_probe(device_t dev)
217141241Snjl{
218142156Snjl	device_t est_dev, perf_dev;
219142032Snjl	int error, type;
220141241Snjl
221142203Snjl	if (resource_disabled("ichss", 0))
222142203Snjl		return (ENXIO);
223142203Snjl
224142002Snjl	/*
225142002Snjl	 * If the ACPI perf driver has attached and is not just offering
226142156Snjl	 * info, let it manage things.  Also, if Enhanced SpeedStep is
227142156Snjl	 * available, don't attach.
228142002Snjl	 */
229142002Snjl	perf_dev = device_find_child(device_get_parent(dev), "acpi_perf", -1);
230142002Snjl	if (perf_dev && device_is_attached(perf_dev)) {
231142032Snjl		error = CPUFREQ_DRV_TYPE(perf_dev, &type);
232142032Snjl		if (error == 0 && (type & CPUFREQ_FLAG_INFO_ONLY) == 0)
233142002Snjl			return (ENXIO);
234142002Snjl	}
235142156Snjl	est_dev = device_find_child(device_get_parent(dev), "est", -1);
236142156Snjl	if (est_dev && device_is_attached(est_dev))
237142156Snjl		return (ENXIO);
238141241Snjl
239141241Snjl	device_set_desc(dev, "SpeedStep ICH");
240141241Snjl	return (-1000);
241141241Snjl}
242141241Snjl
243141241Snjlstatic int
244141241Snjlichss_attach(device_t dev)
245141241Snjl{
246141241Snjl	struct ichss_softc *sc;
247141241Snjl
248141241Snjl	sc = device_get_softc(dev);
249141241Snjl	sc->dev = dev;
250141241Snjl
251141241Snjl	sc->bm_rid = 0;
252141241Snjl	sc->bm_reg = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &sc->bm_rid,
253141241Snjl	    RF_ACTIVE);
254141241Snjl	if (sc->bm_reg == NULL) {
255141241Snjl		device_printf(dev, "failed to alloc BM arb register\n");
256141241Snjl		return (ENXIO);
257141241Snjl	}
258141241Snjl	sc->ctrl_rid = 1;
259141241Snjl	sc->ctrl_reg = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
260141241Snjl	    &sc->ctrl_rid, RF_ACTIVE);
261141241Snjl	if (sc->ctrl_reg == NULL) {
262141241Snjl		device_printf(dev, "failed to alloc control register\n");
263141241Snjl		bus_release_resource(dev, SYS_RES_IOPORT, sc->bm_rid,
264141241Snjl		    sc->bm_reg);
265141241Snjl		return (ENXIO);
266141241Snjl	}
267141241Snjl
268141241Snjl	/* Setup some defaults for our exported settings. */
269141241Snjl	sc->sets[0].freq = CPUFREQ_VAL_UNKNOWN;
270141241Snjl	sc->sets[0].volts = CPUFREQ_VAL_UNKNOWN;
271141241Snjl	sc->sets[0].power = CPUFREQ_VAL_UNKNOWN;
272141241Snjl	sc->sets[0].lat = 1000;
273141241Snjl	sc->sets[0].dev = dev;
274141241Snjl	sc->sets[1] = sc->sets[0];
275141241Snjl	cpufreq_register(dev);
276141241Snjl
277141241Snjl	return (0);
278141241Snjl}
279141241Snjl
280141241Snjlstatic int
281141241Snjlichss_detach(device_t dev)
282141241Snjl{
283141241Snjl	/* TODO: teardown BM and CTRL registers. */
284141241Snjl	return (ENXIO);
285141241Snjl}
286141241Snjl
287141241Snjlstatic int
288142032Snjlichss_settings(device_t dev, struct cf_setting *sets, int *count)
289141241Snjl{
290141241Snjl	struct ichss_softc *sc;
291141241Snjl	struct cf_setting set;
292141241Snjl	int first, i;
293141241Snjl
294141241Snjl	if (sets == NULL || count == NULL)
295141241Snjl		return (EINVAL);
296141241Snjl	if (*count < 2) {
297141241Snjl		*count = 2;
298141241Snjl		return (E2BIG);
299141241Snjl	}
300141241Snjl	sc = device_get_softc(dev);
301141241Snjl
302141241Snjl	/*
303141241Snjl	 * Estimate frequencies for both levels, temporarily switching to
304141241Snjl	 * the other one if we haven't calibrated it yet.
305141241Snjl	 */
306141241Snjl	ichss_get(dev, &set);
307141241Snjl	for (i = 0; i < 2; i++) {
308141241Snjl		if (sc->sets[i].freq == CPUFREQ_VAL_UNKNOWN) {
309141241Snjl			first = (i == 0) ? 1 : 0;
310141241Snjl			ichss_set(dev, &sc->sets[i]);
311141241Snjl			ichss_set(dev, &sc->sets[first]);
312141241Snjl		}
313141241Snjl	}
314141241Snjl
315141241Snjl	bcopy(sc->sets, sets, sizeof(sc->sets));
316141241Snjl	*count = 2;
317141241Snjl
318141241Snjl	return (0);
319141241Snjl}
320141241Snjl
321141241Snjlstatic int
322141241Snjlichss_set(device_t dev, const struct cf_setting *set)
323141241Snjl{
324141241Snjl	struct ichss_softc *sc;
325141241Snjl	uint8_t bmval, new_val, old_val, req_val;
326141241Snjl	uint64_t rate;
327141363Snjl	register_t regs;
328141241Snjl
329141241Snjl	/* Look up appropriate bit value based on frequency. */
330141241Snjl	sc = device_get_softc(dev);
331141241Snjl	if (CPUFREQ_CMP(set->freq, sc->sets[0].freq))
332141241Snjl		req_val = 0;
333141241Snjl	else if (CPUFREQ_CMP(set->freq, sc->sets[1].freq))
334141241Snjl		req_val = ICHSS_CTRL_BIT;
335141241Snjl	else
336141241Snjl		return (EINVAL);
337141241Snjl	DPRINT("ichss: requested setting %d\n", req_val);
338141241Snjl
339141241Snjl	/* Disable interrupts and get the other register contents. */
340141363Snjl	regs = intr_disable();
341141241Snjl	old_val = ICH_GET_REG(sc->ctrl_reg) & ~ICHSS_CTRL_BIT;
342141241Snjl
343141241Snjl	/*
344141241Snjl	 * Disable bus master arbitration, write the new value to the control
345141241Snjl	 * register, and then re-enable bus master arbitration.
346141241Snjl	 */
347141241Snjl	bmval = ICH_GET_REG(sc->bm_reg) | ICHSS_BM_DISABLE;
348141241Snjl	ICH_SET_REG(sc->bm_reg, bmval);
349141241Snjl	ICH_SET_REG(sc->ctrl_reg, old_val | req_val);
350141241Snjl	ICH_SET_REG(sc->bm_reg, bmval & ~ICHSS_BM_DISABLE);
351141241Snjl
352141241Snjl	/* Get the new value and re-enable interrupts. */
353141241Snjl	new_val = ICH_GET_REG(sc->ctrl_reg);
354141363Snjl	intr_restore(regs);
355141241Snjl
356141241Snjl	/* Check if the desired state was indeed selected. */
357141241Snjl	if (req_val != (new_val & ICHSS_CTRL_BIT)) {
358141241Snjl	    device_printf(sc->dev, "transition to %d failed\n", req_val);
359141241Snjl	    return (ENXIO);
360141241Snjl	}
361141241Snjl
362141241Snjl	/* Re-initialize our cycle counter if we don't know this new state. */
363141241Snjl	if (sc->sets[req_val].freq == CPUFREQ_VAL_UNKNOWN) {
364141241Snjl		cpu_est_clockrate(0, &rate);
365141241Snjl		sc->sets[req_val].freq = rate / 1000000;
366141241Snjl		DPRINT("ichss: set calibrated new rate of %d\n",
367141241Snjl		    sc->sets[req_val].freq);
368141241Snjl	}
369141241Snjl
370141241Snjl	return (0);
371141241Snjl}
372141241Snjl
373141241Snjlstatic int
374141241Snjlichss_get(device_t dev, struct cf_setting *set)
375141241Snjl{
376141241Snjl	struct ichss_softc *sc;
377141241Snjl	uint64_t rate;
378141241Snjl	uint8_t state;
379141241Snjl
380141241Snjl	sc = device_get_softc(dev);
381141241Snjl	state = ICH_GET_REG(sc->ctrl_reg) & ICHSS_CTRL_BIT;
382141241Snjl
383141241Snjl	/* If we haven't changed settings yet, estimate the current value. */
384141241Snjl	if (sc->sets[state].freq == CPUFREQ_VAL_UNKNOWN) {
385141241Snjl		cpu_est_clockrate(0, &rate);
386141241Snjl		sc->sets[state].freq = rate / 1000000;
387141241Snjl		DPRINT("ichss: get calibrated new rate of %d\n",
388141241Snjl		    sc->sets[state].freq);
389141241Snjl	}
390141241Snjl	*set = sc->sets[state];
391141241Snjl
392141241Snjl	return (0);
393141241Snjl}
394142032Snjl
395142032Snjlstatic int
396142032Snjlichss_type(device_t dev, int *type)
397142032Snjl{
398142032Snjl
399142032Snjl	if (type == NULL)
400142032Snjl		return (EINVAL);
401142032Snjl
402142032Snjl	*type = CPUFREQ_TYPE_ABSOLUTE;
403142032Snjl	return (0);
404142032Snjl}
405