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$");
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/resource.h>
43141241Snjl#include <sys/rman.h>
44141241Snjl
45141241Snjl#include "cpufreq_if.h"
46141241Snjl
47141241Snjl/*
48141241Snjl * The SpeedStep ICH feature is a chipset-initiated voltage and frequency
49141241Snjl * transition available on the ICH2M, 3M, and 4M.  It is different from
50141241Snjl * the newer Pentium-M SpeedStep feature.  It offers only two levels of
51141241Snjl * frequency/voltage.  Often, the BIOS will select one of the levels via
52141241Snjl * SMM code during the power-on process (i.e., choose a lower level if the
53141241Snjl * system is off AC power.)
54141241Snjl */
55141241Snjl
56141241Snjlstruct ichss_softc {
57141241Snjl	device_t	 dev;
58141241Snjl	int		 bm_rid;	/* Bus-mastering control (PM2REG). */
59141241Snjl	struct resource	*bm_reg;
60141241Snjl	int		 ctrl_rid;	/* Control/status register. */
61141241Snjl	struct resource	*ctrl_reg;
62141241Snjl	struct cf_setting sets[2];	/* Only two settings. */
63141241Snjl};
64141241Snjl
65141241Snjl/* Supported PCI IDs. */
66141241Snjl#define PCI_VENDOR_INTEL	0x8086
67141241Snjl#define PCI_DEV_82801BA		0x244c /* ICH2M */
68141241Snjl#define PCI_DEV_82801CA		0x248c /* ICH3M */
69141241Snjl#define PCI_DEV_82801DB		0x24cc /* ICH4M */
70247332Sjhb#define PCI_DEV_82815_MC	0x1130 /* Unsupported/buggy part */
71141241Snjl
72141241Snjl/* PCI config registers for finding PMBASE and enabling SpeedStep. */
73141241Snjl#define ICHSS_PMBASE_OFFSET	0x40
74141241Snjl#define ICHSS_PMCFG_OFFSET	0xa0
75141241Snjl
76141241Snjl/* Values and masks. */
77141241Snjl#define ICHSS_ENABLE		(1<<3)	/* Enable SpeedStep control. */
78141241Snjl#define ICHSS_IO_REG		0x1	/* Access register via I/O space. */
79141241Snjl#define ICHSS_PMBASE_MASK	0xff80	/* PMBASE address bits. */
80141241Snjl#define ICHSS_CTRL_BIT		0x1	/* 0 is high speed, 1 is low. */
81141241Snjl#define ICHSS_BM_DISABLE	0x1
82141241Snjl
83141241Snjl/* Offsets from PMBASE for various registers. */
84141241Snjl#define ICHSS_BM_OFFSET		0x20
85141241Snjl#define ICHSS_CTRL_OFFSET	0x50
86141241Snjl
87141241Snjl#define ICH_GET_REG(reg) 				\
88141241Snjl	(bus_space_read_1(rman_get_bustag((reg)), 	\
89141241Snjl	    rman_get_bushandle((reg)), 0))
90141241Snjl#define ICH_SET_REG(reg, val)				\
91141241Snjl	(bus_space_write_1(rman_get_bustag((reg)), 	\
92141241Snjl	    rman_get_bushandle((reg)), 0, (val)))
93141241Snjl
94177041Sjhbstatic void	ichss_identify(driver_t *driver, device_t parent);
95141241Snjlstatic int	ichss_probe(device_t dev);
96141241Snjlstatic int	ichss_attach(device_t dev);
97141241Snjlstatic int	ichss_detach(device_t dev);
98141241Snjlstatic int	ichss_settings(device_t dev, struct cf_setting *sets,
99142032Snjl		    int *count);
100141241Snjlstatic int	ichss_set(device_t dev, const struct cf_setting *set);
101141241Snjlstatic int	ichss_get(device_t dev, struct cf_setting *set);
102142032Snjlstatic int	ichss_type(device_t dev, int *type);
103141241Snjl
104141241Snjlstatic device_method_t ichss_methods[] = {
105141241Snjl	/* Device interface */
106177041Sjhb	DEVMETHOD(device_identify,	ichss_identify),
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),
116246128Ssbz	DEVMETHOD_END
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
124177041Sjhbstatic device_t ich_device;
125141241Snjl
126141241Snjl#if 0
127141241Snjl#define DPRINT(x...)	printf(x)
128141241Snjl#else
129141241Snjl#define DPRINT(x...)
130141241Snjl#endif
131141241Snjl
132177041Sjhbstatic void
133177041Sjhbichss_identify(driver_t *driver, device_t parent)
134141241Snjl{
135177041Sjhb	device_t child;
136141241Snjl	uint32_t pmbase;
137141241Snjl
138177041Sjhb	if (resource_disabled("ichss", 0))
139177041Sjhb		return;
140177041Sjhb
141141241Snjl	/*
142177041Sjhb	 * It appears that ICH SpeedStep only requires a single CPU to
143177041Sjhb	 * set the value (since the chipset is shared by all CPUs.)
144177041Sjhb	 * Thus, we only add a child to cpu 0.
145141241Snjl	 */
146177041Sjhb	if (device_get_unit(parent) != 0)
147177041Sjhb		return;
148141241Snjl
149177041Sjhb	/* Avoid duplicates. */
150177041Sjhb	if (device_find_child(parent, "ichss", -1))
151177041Sjhb		return;
152141241Snjl
153142625Snjl	/*
154177041Sjhb	 * ICH2/3/4-M I/O Controller Hub is at bus 0, slot 1F, function 0.
155177041Sjhb	 * E.g. see Section 6.1 "PCI Devices and Functions" and table 6.1 of
156177041Sjhb	 * Intel(r) 82801BA I/O Controller Hub 2 (ICH2) and Intel(r) 82801BAM
157177041Sjhb	 * I/O Controller Hub 2 Mobile (ICH2-M).
158142625Snjl	 */
159177041Sjhb	ich_device = pci_find_bsf(0, 0x1f, 0);
160177041Sjhb	if (ich_device == NULL ||
161177041Sjhb	    pci_get_vendor(ich_device) != PCI_VENDOR_INTEL ||
162177041Sjhb	    (pci_get_device(ich_device) != PCI_DEV_82801BA &&
163177041Sjhb	    pci_get_device(ich_device) != PCI_DEV_82801CA &&
164177041Sjhb	    pci_get_device(ich_device) != PCI_DEV_82801DB))
165177041Sjhb		return;
166141241Snjl
167247332Sjhb	/*
168247332Sjhb	 * Certain systems with ICH2 and an Intel 82815_MC host bridge
169247332Sjhb	 * where the host bridge's revision is < 5 lockup if SpeedStep
170247332Sjhb	 * is used.
171247332Sjhb	 */
172247332Sjhb	if (pci_get_device(ich_device) == PCI_DEV_82801BA) {
173247332Sjhb		device_t hostb;
174247332Sjhb
175247332Sjhb		hostb = pci_find_bsf(0, 0, 0);
176247332Sjhb		if (hostb != NULL &&
177247332Sjhb		    pci_get_vendor(hostb) == PCI_VENDOR_INTEL &&
178247332Sjhb		    pci_get_device(hostb) == PCI_DEV_82815_MC &&
179247332Sjhb		    pci_get_revid(hostb) < 5)
180247332Sjhb			return;
181247332Sjhb	}
182247332Sjhb
183141241Snjl	/* Find the PMBASE register from our PCI config header. */
184177041Sjhb	pmbase = pci_read_config(ich_device, ICHSS_PMBASE_OFFSET,
185177041Sjhb	    sizeof(pmbase));
186141241Snjl	if ((pmbase & ICHSS_IO_REG) == 0) {
187141241Snjl		printf("ichss: invalid PMBASE memory type\n");
188177041Sjhb		return;
189141241Snjl	}
190141241Snjl	pmbase &= ICHSS_PMBASE_MASK;
191141241Snjl	if (pmbase == 0) {
192141241Snjl		printf("ichss: invalid zero PMBASE address\n");
193177041Sjhb		return;
194141241Snjl	}
195141241Snjl	DPRINT("ichss: PMBASE is %#x\n", pmbase);
196141241Snjl
197181691Sjhb	child = BUS_ADD_CHILD(parent, 20, "ichss", 0);
198177041Sjhb	if (child == NULL) {
199177041Sjhb		device_printf(parent, "add SpeedStep child failed\n");
200177041Sjhb		return;
201177041Sjhb	}
202177041Sjhb
203141241Snjl	/* Add the bus master arbitration and control registers. */
204141241Snjl	bus_set_resource(child, SYS_RES_IOPORT, 0, pmbase + ICHSS_BM_OFFSET,
205141241Snjl	    1);
206141241Snjl	bus_set_resource(child, SYS_RES_IOPORT, 1, pmbase + ICHSS_CTRL_OFFSET,
207141241Snjl	    1);
208141241Snjl}
209141241Snjl
210141241Snjlstatic int
211141241Snjlichss_probe(device_t dev)
212141241Snjl{
213142156Snjl	device_t est_dev, perf_dev;
214142032Snjl	int error, type;
215141241Snjl
216142002Snjl	/*
217142002Snjl	 * If the ACPI perf driver has attached and is not just offering
218142156Snjl	 * info, let it manage things.  Also, if Enhanced SpeedStep is
219142156Snjl	 * available, don't attach.
220142002Snjl	 */
221142002Snjl	perf_dev = device_find_child(device_get_parent(dev), "acpi_perf", -1);
222142002Snjl	if (perf_dev && device_is_attached(perf_dev)) {
223142032Snjl		error = CPUFREQ_DRV_TYPE(perf_dev, &type);
224142032Snjl		if (error == 0 && (type & CPUFREQ_FLAG_INFO_ONLY) == 0)
225142002Snjl			return (ENXIO);
226142002Snjl	}
227142156Snjl	est_dev = device_find_child(device_get_parent(dev), "est", -1);
228142156Snjl	if (est_dev && device_is_attached(est_dev))
229142156Snjl		return (ENXIO);
230141241Snjl
231141241Snjl	device_set_desc(dev, "SpeedStep ICH");
232141241Snjl	return (-1000);
233141241Snjl}
234141241Snjl
235141241Snjlstatic int
236141241Snjlichss_attach(device_t dev)
237141241Snjl{
238141241Snjl	struct ichss_softc *sc;
239177041Sjhb	uint16_t ss_en;
240141241Snjl
241141241Snjl	sc = device_get_softc(dev);
242141241Snjl	sc->dev = dev;
243141241Snjl
244141241Snjl	sc->bm_rid = 0;
245141241Snjl	sc->bm_reg = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &sc->bm_rid,
246141241Snjl	    RF_ACTIVE);
247141241Snjl	if (sc->bm_reg == NULL) {
248141241Snjl		device_printf(dev, "failed to alloc BM arb register\n");
249141241Snjl		return (ENXIO);
250141241Snjl	}
251141241Snjl	sc->ctrl_rid = 1;
252141241Snjl	sc->ctrl_reg = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
253141241Snjl	    &sc->ctrl_rid, RF_ACTIVE);
254141241Snjl	if (sc->ctrl_reg == NULL) {
255141241Snjl		device_printf(dev, "failed to alloc control register\n");
256141241Snjl		bus_release_resource(dev, SYS_RES_IOPORT, sc->bm_rid,
257141241Snjl		    sc->bm_reg);
258141241Snjl		return (ENXIO);
259141241Snjl	}
260141241Snjl
261177041Sjhb	/* Activate SpeedStep control if not already enabled. */
262177041Sjhb	ss_en = pci_read_config(ich_device, ICHSS_PMCFG_OFFSET, sizeof(ss_en));
263177041Sjhb	if ((ss_en & ICHSS_ENABLE) == 0) {
264177041Sjhb		device_printf(dev, "enabling SpeedStep support\n");
265177041Sjhb		pci_write_config(ich_device, ICHSS_PMCFG_OFFSET,
266177041Sjhb		    ss_en | ICHSS_ENABLE, sizeof(ss_en));
267177041Sjhb	}
268177041Sjhb
269141241Snjl	/* Setup some defaults for our exported settings. */
270141241Snjl	sc->sets[0].freq = CPUFREQ_VAL_UNKNOWN;
271141241Snjl	sc->sets[0].volts = CPUFREQ_VAL_UNKNOWN;
272141241Snjl	sc->sets[0].power = CPUFREQ_VAL_UNKNOWN;
273141241Snjl	sc->sets[0].lat = 1000;
274141241Snjl	sc->sets[0].dev = dev;
275141241Snjl	sc->sets[1] = sc->sets[0];
276141241Snjl	cpufreq_register(dev);
277141241Snjl
278141241Snjl	return (0);
279141241Snjl}
280141241Snjl
281141241Snjlstatic int
282141241Snjlichss_detach(device_t dev)
283141241Snjl{
284141241Snjl	/* TODO: teardown BM and CTRL registers. */
285141241Snjl	return (ENXIO);
286141241Snjl}
287141241Snjl
288141241Snjlstatic int
289142032Snjlichss_settings(device_t dev, struct cf_setting *sets, int *count)
290141241Snjl{
291141241Snjl	struct ichss_softc *sc;
292141241Snjl	struct cf_setting set;
293141241Snjl	int first, i;
294141241Snjl
295141241Snjl	if (sets == NULL || count == NULL)
296141241Snjl		return (EINVAL);
297141241Snjl	if (*count < 2) {
298141241Snjl		*count = 2;
299141241Snjl		return (E2BIG);
300141241Snjl	}
301141241Snjl	sc = device_get_softc(dev);
302141241Snjl
303141241Snjl	/*
304141241Snjl	 * Estimate frequencies for both levels, temporarily switching to
305141241Snjl	 * the other one if we haven't calibrated it yet.
306141241Snjl	 */
307141241Snjl	ichss_get(dev, &set);
308141241Snjl	for (i = 0; i < 2; i++) {
309141241Snjl		if (sc->sets[i].freq == CPUFREQ_VAL_UNKNOWN) {
310141241Snjl			first = (i == 0) ? 1 : 0;
311141241Snjl			ichss_set(dev, &sc->sets[i]);
312141241Snjl			ichss_set(dev, &sc->sets[first]);
313141241Snjl		}
314141241Snjl	}
315141241Snjl
316141241Snjl	bcopy(sc->sets, sets, sizeof(sc->sets));
317141241Snjl	*count = 2;
318141241Snjl
319141241Snjl	return (0);
320141241Snjl}
321141241Snjl
322141241Snjlstatic int
323141241Snjlichss_set(device_t dev, const struct cf_setting *set)
324141241Snjl{
325141241Snjl	struct ichss_softc *sc;
326141241Snjl	uint8_t bmval, new_val, old_val, req_val;
327141241Snjl	uint64_t rate;
328141363Snjl	register_t regs;
329141241Snjl
330141241Snjl	/* Look up appropriate bit value based on frequency. */
331141241Snjl	sc = device_get_softc(dev);
332141241Snjl	if (CPUFREQ_CMP(set->freq, sc->sets[0].freq))
333141241Snjl		req_val = 0;
334141241Snjl	else if (CPUFREQ_CMP(set->freq, sc->sets[1].freq))
335141241Snjl		req_val = ICHSS_CTRL_BIT;
336141241Snjl	else
337141241Snjl		return (EINVAL);
338141241Snjl	DPRINT("ichss: requested setting %d\n", req_val);
339141241Snjl
340141241Snjl	/* Disable interrupts and get the other register contents. */
341141363Snjl	regs = intr_disable();
342141241Snjl	old_val = ICH_GET_REG(sc->ctrl_reg) & ~ICHSS_CTRL_BIT;
343141241Snjl
344141241Snjl	/*
345141241Snjl	 * Disable bus master arbitration, write the new value to the control
346141241Snjl	 * register, and then re-enable bus master arbitration.
347141241Snjl	 */
348141241Snjl	bmval = ICH_GET_REG(sc->bm_reg) | ICHSS_BM_DISABLE;
349141241Snjl	ICH_SET_REG(sc->bm_reg, bmval);
350141241Snjl	ICH_SET_REG(sc->ctrl_reg, old_val | req_val);
351141241Snjl	ICH_SET_REG(sc->bm_reg, bmval & ~ICHSS_BM_DISABLE);
352141241Snjl
353141241Snjl	/* Get the new value and re-enable interrupts. */
354141241Snjl	new_val = ICH_GET_REG(sc->ctrl_reg);
355141363Snjl	intr_restore(regs);
356141241Snjl
357141241Snjl	/* Check if the desired state was indeed selected. */
358141241Snjl	if (req_val != (new_val & ICHSS_CTRL_BIT)) {
359141241Snjl	    device_printf(sc->dev, "transition to %d failed\n", req_val);
360141241Snjl	    return (ENXIO);
361141241Snjl	}
362141241Snjl
363141241Snjl	/* Re-initialize our cycle counter if we don't know this new state. */
364141241Snjl	if (sc->sets[req_val].freq == CPUFREQ_VAL_UNKNOWN) {
365141241Snjl		cpu_est_clockrate(0, &rate);
366141241Snjl		sc->sets[req_val].freq = rate / 1000000;
367141241Snjl		DPRINT("ichss: set calibrated new rate of %d\n",
368141241Snjl		    sc->sets[req_val].freq);
369141241Snjl	}
370141241Snjl
371141241Snjl	return (0);
372141241Snjl}
373141241Snjl
374141241Snjlstatic int
375141241Snjlichss_get(device_t dev, struct cf_setting *set)
376141241Snjl{
377141241Snjl	struct ichss_softc *sc;
378141241Snjl	uint64_t rate;
379141241Snjl	uint8_t state;
380141241Snjl
381141241Snjl	sc = device_get_softc(dev);
382141241Snjl	state = ICH_GET_REG(sc->ctrl_reg) & ICHSS_CTRL_BIT;
383141241Snjl
384141241Snjl	/* If we haven't changed settings yet, estimate the current value. */
385141241Snjl	if (sc->sets[state].freq == CPUFREQ_VAL_UNKNOWN) {
386141241Snjl		cpu_est_clockrate(0, &rate);
387141241Snjl		sc->sets[state].freq = rate / 1000000;
388141241Snjl		DPRINT("ichss: get calibrated new rate of %d\n",
389141241Snjl		    sc->sets[state].freq);
390141241Snjl	}
391141241Snjl	*set = sc->sets[state];
392141241Snjl
393141241Snjl	return (0);
394141241Snjl}
395142032Snjl
396142032Snjlstatic int
397142032Snjlichss_type(device_t dev, int *type)
398142032Snjl{
399142032Snjl
400142032Snjl	if (type == NULL)
401142032Snjl		return (EINVAL);
402142032Snjl
403142032Snjl	*type = CPUFREQ_TYPE_ABSOLUTE;
404142032Snjl	return (0);
405142032Snjl}
406