xref: /freebsd/sys/powerpc/cpufreq/dfs.c (revision 9eb9db93da05b01b5bce45fa9ed0ee234aece429)
19eb9db93SNathan Whitehorn /*-
29eb9db93SNathan Whitehorn  * Copyright (c) 2009 Nathan Whitehorn
39eb9db93SNathan Whitehorn  * All rights reserved.
49eb9db93SNathan Whitehorn  *
59eb9db93SNathan Whitehorn  * Redistribution and use in source and binary forms, with or without
69eb9db93SNathan Whitehorn  * modification, are permitted provided that the following conditions
79eb9db93SNathan Whitehorn  * are met:
89eb9db93SNathan Whitehorn  * 1. Redistributions of source code must retain the above copyright
99eb9db93SNathan Whitehorn  *    notice, this list of conditions and the following disclaimer.
109eb9db93SNathan Whitehorn  * 2. Redistributions in binary form must reproduce the above copyright
119eb9db93SNathan Whitehorn  *    notice, this list of conditions and the following disclaimer in the
129eb9db93SNathan Whitehorn  *    documentation and/or other materials provided with the distribution.
139eb9db93SNathan Whitehorn  *
149eb9db93SNathan Whitehorn  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
159eb9db93SNathan Whitehorn  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
169eb9db93SNathan Whitehorn  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
179eb9db93SNathan Whitehorn  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
189eb9db93SNathan Whitehorn  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
199eb9db93SNathan Whitehorn  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
209eb9db93SNathan Whitehorn  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
219eb9db93SNathan Whitehorn  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
229eb9db93SNathan Whitehorn  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
239eb9db93SNathan Whitehorn  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
249eb9db93SNathan Whitehorn  * SUCH DAMAGE.
259eb9db93SNathan Whitehorn  */
269eb9db93SNathan Whitehorn 
279eb9db93SNathan Whitehorn #include <sys/cdefs.h>
289eb9db93SNathan Whitehorn __FBSDID("$FreeBSD$");
299eb9db93SNathan Whitehorn 
309eb9db93SNathan Whitehorn #include <sys/param.h>
319eb9db93SNathan Whitehorn #include <sys/systm.h>
329eb9db93SNathan Whitehorn #include <sys/bus.h>
339eb9db93SNathan Whitehorn #include <sys/cpu.h>
349eb9db93SNathan Whitehorn #include <sys/kernel.h>
359eb9db93SNathan Whitehorn #include <sys/module.h>
369eb9db93SNathan Whitehorn 
379eb9db93SNathan Whitehorn #include "cpufreq_if.h"
389eb9db93SNathan Whitehorn 
399eb9db93SNathan Whitehorn struct dfs_softc {
409eb9db93SNathan Whitehorn 	device_t dev;
419eb9db93SNathan Whitehorn 	int	 dfs4;
429eb9db93SNathan Whitehorn };
439eb9db93SNathan Whitehorn 
449eb9db93SNathan Whitehorn static void	dfs_identify(driver_t *driver, device_t parent);
459eb9db93SNathan Whitehorn static int	dfs_probe(device_t dev);
469eb9db93SNathan Whitehorn static int	dfs_attach(device_t dev);
479eb9db93SNathan Whitehorn static int	dfs_settings(device_t dev, struct cf_setting *sets, int *count);
489eb9db93SNathan Whitehorn static int	dfs_set(device_t dev, const struct cf_setting *set);
499eb9db93SNathan Whitehorn static int	dfs_get(device_t dev, struct cf_setting *set);
509eb9db93SNathan Whitehorn static int	dfs_type(device_t dev, int *type);
519eb9db93SNathan Whitehorn 
529eb9db93SNathan Whitehorn static device_method_t dfs_methods[] = {
539eb9db93SNathan Whitehorn 	/* Device interface */
549eb9db93SNathan Whitehorn 	DEVMETHOD(device_identify,	dfs_identify),
559eb9db93SNathan Whitehorn 	DEVMETHOD(device_probe,		dfs_probe),
569eb9db93SNathan Whitehorn 	DEVMETHOD(device_attach,	dfs_attach),
579eb9db93SNathan Whitehorn 
589eb9db93SNathan Whitehorn 	/* cpufreq interface */
599eb9db93SNathan Whitehorn 	DEVMETHOD(cpufreq_drv_set,	dfs_set),
609eb9db93SNathan Whitehorn 	DEVMETHOD(cpufreq_drv_get,	dfs_get),
619eb9db93SNathan Whitehorn 	DEVMETHOD(cpufreq_drv_type,	dfs_type),
629eb9db93SNathan Whitehorn 	DEVMETHOD(cpufreq_drv_settings,	dfs_settings),
639eb9db93SNathan Whitehorn 
649eb9db93SNathan Whitehorn 	{0, 0}
659eb9db93SNathan Whitehorn };
669eb9db93SNathan Whitehorn 
679eb9db93SNathan Whitehorn static driver_t dfs_driver = {
689eb9db93SNathan Whitehorn 	"dfs",
699eb9db93SNathan Whitehorn 	dfs_methods,
709eb9db93SNathan Whitehorn 	sizeof(struct dfs_softc)
719eb9db93SNathan Whitehorn };
729eb9db93SNathan Whitehorn 
739eb9db93SNathan Whitehorn static devclass_t dfs_devclass;
749eb9db93SNathan Whitehorn DRIVER_MODULE(dfs, cpu, dfs_driver, dfs_devclass, 0, 0);
759eb9db93SNathan Whitehorn 
769eb9db93SNathan Whitehorn /*
779eb9db93SNathan Whitehorn  * Bits of the HID1 register to enable DFS. See page 2-24 of "MPC7450
789eb9db93SNathan Whitehorn  * RISC Microprocessor Family Reference Manual", rev. 5.
799eb9db93SNathan Whitehorn  */
809eb9db93SNathan Whitehorn 
819eb9db93SNathan Whitehorn #define	HID1_DFS2	(1UL << 22)
829eb9db93SNathan Whitehorn #define	HID1_DFS4	(1UL << 23)
839eb9db93SNathan Whitehorn 
849eb9db93SNathan Whitehorn static void
859eb9db93SNathan Whitehorn dfs_identify(driver_t *driver, device_t parent)
869eb9db93SNathan Whitehorn {
879eb9db93SNathan Whitehorn 	uint16_t vers;
889eb9db93SNathan Whitehorn 	vers = mfpvr() >> 16;
899eb9db93SNathan Whitehorn 
909eb9db93SNathan Whitehorn 	/* Check for an MPC 7447A or 7448 CPU */
919eb9db93SNathan Whitehorn 	switch (vers) {
929eb9db93SNathan Whitehorn 		case MPC7447A:
939eb9db93SNathan Whitehorn 		case MPC7448:
949eb9db93SNathan Whitehorn 			break;
959eb9db93SNathan Whitehorn 		default:
969eb9db93SNathan Whitehorn 			return;
979eb9db93SNathan Whitehorn 	}
989eb9db93SNathan Whitehorn 
999eb9db93SNathan Whitehorn 	/* Make sure we're not being doubly invoked. */
1009eb9db93SNathan Whitehorn 	if (device_find_child(parent, "dfs", -1) != NULL)
1019eb9db93SNathan Whitehorn 		return;
1029eb9db93SNathan Whitehorn 
1039eb9db93SNathan Whitehorn 	/*
1049eb9db93SNathan Whitehorn 	 * We attach a child for every CPU since settings need to
1059eb9db93SNathan Whitehorn 	 * be performed on every CPU in the SMP case.
1069eb9db93SNathan Whitehorn 	 */
1079eb9db93SNathan Whitehorn 	if (BUS_ADD_CHILD(parent, 10, "dfs", -1) == NULL)
1089eb9db93SNathan Whitehorn 		device_printf(parent, "add dfs child failed\n");
1099eb9db93SNathan Whitehorn }
1109eb9db93SNathan Whitehorn 
1119eb9db93SNathan Whitehorn static int
1129eb9db93SNathan Whitehorn dfs_probe(device_t dev)
1139eb9db93SNathan Whitehorn {
1149eb9db93SNathan Whitehorn 	if (resource_disabled("dfs", 0))
1159eb9db93SNathan Whitehorn 		return (ENXIO);
1169eb9db93SNathan Whitehorn 
1179eb9db93SNathan Whitehorn 	device_set_desc(dev, "Dynamic Frequency Switching");
1189eb9db93SNathan Whitehorn 	return (0);
1199eb9db93SNathan Whitehorn }
1209eb9db93SNathan Whitehorn 
1219eb9db93SNathan Whitehorn static int
1229eb9db93SNathan Whitehorn dfs_attach(device_t dev)
1239eb9db93SNathan Whitehorn {
1249eb9db93SNathan Whitehorn 	struct dfs_softc *sc;
1259eb9db93SNathan Whitehorn 	uint16_t vers;
1269eb9db93SNathan Whitehorn 
1279eb9db93SNathan Whitehorn 	sc = device_get_softc(dev);
1289eb9db93SNathan Whitehorn 	sc->dev = dev;
1299eb9db93SNathan Whitehorn 	sc->dfs4 = 0;
1309eb9db93SNathan Whitehorn 	vers = mfpvr() >> 16;
1319eb9db93SNathan Whitehorn 
1329eb9db93SNathan Whitehorn 	/* The 7448 supports divide-by-four as well */
1339eb9db93SNathan Whitehorn 	if (vers == MPC7448)
1349eb9db93SNathan Whitehorn 		sc->dfs4 = 1;
1359eb9db93SNathan Whitehorn 
1369eb9db93SNathan Whitehorn 	cpufreq_register(dev);
1379eb9db93SNathan Whitehorn 	return (0);
1389eb9db93SNathan Whitehorn }
1399eb9db93SNathan Whitehorn 
1409eb9db93SNathan Whitehorn static int
1419eb9db93SNathan Whitehorn dfs_settings(device_t dev, struct cf_setting *sets, int *count)
1429eb9db93SNathan Whitehorn {
1439eb9db93SNathan Whitehorn 	struct dfs_softc *sc;
1449eb9db93SNathan Whitehorn 	int states;
1459eb9db93SNathan Whitehorn 
1469eb9db93SNathan Whitehorn 	sc = device_get_softc(dev);
1479eb9db93SNathan Whitehorn 	states = sc->dfs4 ? 3 : 2;
1489eb9db93SNathan Whitehorn 	if (sets == NULL || count == NULL)
1499eb9db93SNathan Whitehorn 		return (EINVAL);
1509eb9db93SNathan Whitehorn 	if (*count < states)
1519eb9db93SNathan Whitehorn 		return (E2BIG);
1529eb9db93SNathan Whitehorn 
1539eb9db93SNathan Whitehorn 	/* Return a list of valid settings for this driver. */
1549eb9db93SNathan Whitehorn 	memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * states);
1559eb9db93SNathan Whitehorn 
1569eb9db93SNathan Whitehorn 	sets[0].freq = 10000; sets[0].dev = dev;
1579eb9db93SNathan Whitehorn 	sets[1].freq = 5000; sets[1].dev = dev;
1589eb9db93SNathan Whitehorn 	if (sc->dfs4)
1599eb9db93SNathan Whitehorn 		sets[2].freq = 2500; sets[2].dev = dev;
1609eb9db93SNathan Whitehorn 	*count = states;
1619eb9db93SNathan Whitehorn 
1629eb9db93SNathan Whitehorn 	return (0);
1639eb9db93SNathan Whitehorn }
1649eb9db93SNathan Whitehorn 
1659eb9db93SNathan Whitehorn static int
1669eb9db93SNathan Whitehorn dfs_set(device_t dev, const struct cf_setting *set)
1679eb9db93SNathan Whitehorn {
1689eb9db93SNathan Whitehorn 	struct dfs_softc *sc;
1699eb9db93SNathan Whitehorn 	register_t hid1;
1709eb9db93SNathan Whitehorn 
1719eb9db93SNathan Whitehorn 	if (set == NULL)
1729eb9db93SNathan Whitehorn 		return (EINVAL);
1739eb9db93SNathan Whitehorn 	sc = device_get_softc(dev);
1749eb9db93SNathan Whitehorn 
1759eb9db93SNathan Whitehorn 	hid1 = mfspr(SPR_HID1);
1769eb9db93SNathan Whitehorn 	hid1 &= ~(HID1_DFS2 | HID1_DFS4);
1779eb9db93SNathan Whitehorn 
1789eb9db93SNathan Whitehorn 	if (set->freq == 5000)
1799eb9db93SNathan Whitehorn 		hid1 |= HID1_DFS2;
1809eb9db93SNathan Whitehorn 	else if (set->freq == 2500)
1819eb9db93SNathan Whitehorn 		hid1 |= HID1_DFS4;
1829eb9db93SNathan Whitehorn 
1839eb9db93SNathan Whitehorn 	/*
1849eb9db93SNathan Whitehorn 	 * Now set the HID1 register with new values. Calling sequence
1859eb9db93SNathan Whitehorn 	 * taken from page 2-26 of the MPC7450 family CPU manual.
1869eb9db93SNathan Whitehorn 	 */
1879eb9db93SNathan Whitehorn 
1889eb9db93SNathan Whitehorn 	powerpc_sync();
1899eb9db93SNathan Whitehorn 	mtspr(SPR_HID1, hid1);
1909eb9db93SNathan Whitehorn 	powerpc_sync(); isync();
1919eb9db93SNathan Whitehorn 
1929eb9db93SNathan Whitehorn 	return (0);
1939eb9db93SNathan Whitehorn }
1949eb9db93SNathan Whitehorn 
1959eb9db93SNathan Whitehorn static int
1969eb9db93SNathan Whitehorn dfs_get(device_t dev, struct cf_setting *set)
1979eb9db93SNathan Whitehorn {
1989eb9db93SNathan Whitehorn 	struct dfs_softc *sc;
1999eb9db93SNathan Whitehorn 	register_t hid1;
2009eb9db93SNathan Whitehorn 
2019eb9db93SNathan Whitehorn 	if (set == NULL)
2029eb9db93SNathan Whitehorn 		return (EINVAL);
2039eb9db93SNathan Whitehorn 	sc = device_get_softc(dev);
2049eb9db93SNathan Whitehorn 
2059eb9db93SNathan Whitehorn 	memset(set, CPUFREQ_VAL_UNKNOWN, sizeof(*set));
2069eb9db93SNathan Whitehorn 
2079eb9db93SNathan Whitehorn 	hid1 = mfspr(SPR_HID1);
2089eb9db93SNathan Whitehorn 
2099eb9db93SNathan Whitehorn 	set->freq = 10000;
2109eb9db93SNathan Whitehorn 	if (hid1 & HID1_DFS2)
2119eb9db93SNathan Whitehorn 		set->freq = 5000;
2129eb9db93SNathan Whitehorn 	else if (sc->dfs4 && (hid1 & HID1_DFS4))
2139eb9db93SNathan Whitehorn 		set->freq = 2500;
2149eb9db93SNathan Whitehorn 
2159eb9db93SNathan Whitehorn 	set->dev = dev;
2169eb9db93SNathan Whitehorn 
2179eb9db93SNathan Whitehorn 	return (0);
2189eb9db93SNathan Whitehorn }
2199eb9db93SNathan Whitehorn 
2209eb9db93SNathan Whitehorn static int
2219eb9db93SNathan Whitehorn dfs_type(device_t dev, int *type)
2229eb9db93SNathan Whitehorn {
2239eb9db93SNathan Whitehorn 
2249eb9db93SNathan Whitehorn 	if (type == NULL)
2259eb9db93SNathan Whitehorn 		return (EINVAL);
2269eb9db93SNathan Whitehorn 
2279eb9db93SNathan Whitehorn 	*type = CPUFREQ_TYPE_RELATIVE;
2289eb9db93SNathan Whitehorn 	return (0);
2299eb9db93SNathan Whitehorn }
2309eb9db93SNathan Whitehorn 
231