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