brintos

brintos / linux-shallow public Read only

0
0
Text · 5.5 KiB · 4ce5eb3 Raw
229 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *	elanfreq:	cpufreq driver for the AMD ELAN family4 *5 *	(c) Copyright 2002 Robert Schwebel <r.schwebel@pengutronix.de>6 *7 *	Parts of this code are (c) Sven Geggus <sven@geggus.net>8 *9 *      All Rights Reserved.10 *11 *	2002-02-13: - initial revision for 2.4.18-pre9 by Robert Schwebel12 */13 14#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt15 16#include <linux/kernel.h>17#include <linux/module.h>18#include <linux/init.h>19 20#include <linux/delay.h>21#include <linux/cpufreq.h>22 23#include <asm/cpu_device_id.h>24#include <asm/msr.h>25#include <linux/timex.h>26#include <linux/io.h>27 28#define REG_CSCIR 0x22		/* Chip Setup and Control Index Register    */29#define REG_CSCDR 0x23		/* Chip Setup and Control Data  Register    */30 31/* Module parameter */32static int max_freq;33 34struct s_elan_multiplier {35	int clock;		/* frequency in kHz                         */36	int val40h;		/* PMU Force Mode register                  */37	int val80h;		/* CPU Clock Speed Register                 */38};39 40/*41 * It is important that the frequencies42 * are listed in ascending order here!43 */44static struct s_elan_multiplier elan_multiplier[] = {45	{1000,	0x02,	0x18},46	{2000,	0x02,	0x10},47	{4000,	0x02,	0x08},48	{8000,	0x00,	0x00},49	{16000,	0x00,	0x02},50	{33000,	0x00,	0x04},51	{66000,	0x01,	0x04},52	{99000,	0x01,	0x05}53};54 55static struct cpufreq_frequency_table elanfreq_table[] = {56	{0, 0,	1000},57	{0, 1,	2000},58	{0, 2,	4000},59	{0, 3,	8000},60	{0, 4,	16000},61	{0, 5,	33000},62	{0, 6,	66000},63	{0, 7,	99000},64	{0, 0,	CPUFREQ_TABLE_END},65};66 67 68/**69 *	elanfreq_get_cpu_frequency: determine current cpu speed70 *71 *	Finds out at which frequency the CPU of the Elan SOC runs72 *	at the moment. Frequencies from 1 to 33 MHz are generated73 *	the normal way, 66 and 99 MHz are called "Hyperspeed Mode"74 *	and have the rest of the chip running with 33 MHz.75 */76 77static unsigned int elanfreq_get_cpu_frequency(unsigned int cpu)78{79	u8 clockspeed_reg;    /* Clock Speed Register */80 81	local_irq_disable();82	outb_p(0x80, REG_CSCIR);83	clockspeed_reg = inb_p(REG_CSCDR);84	local_irq_enable();85 86	if ((clockspeed_reg & 0xE0) == 0xE0)87		return 0;88 89	/* Are we in CPU clock multiplied mode (66/99 MHz)? */90	if ((clockspeed_reg & 0xE0) == 0xC0) {91		if ((clockspeed_reg & 0x01) == 0)92			return 66000;93		else94			return 99000;95	}96 97	/* 33 MHz is not 32 MHz... */98	if ((clockspeed_reg & 0xE0) == 0xA0)99		return 33000;100 101	return (1<<((clockspeed_reg & 0xE0) >> 5)) * 1000;102}103 104 105static int elanfreq_target(struct cpufreq_policy *policy,106			    unsigned int state)107{108	/*109	 * Access to the Elan's internal registers is indexed via110	 * 0x22: Chip Setup & Control Register Index Register (CSCI)111	 * 0x23: Chip Setup & Control Register Data  Register (CSCD)112	 *113	 */114 115	/*116	 * 0x40 is the Power Management Unit's Force Mode Register.117	 * Bit 6 enables Hyperspeed Mode (66/100 MHz core frequency)118	 */119 120	local_irq_disable();121	outb_p(0x40, REG_CSCIR);		/* Disable hyperspeed mode */122	outb_p(0x00, REG_CSCDR);123	local_irq_enable();		/* wait till internal pipelines and */124	udelay(1000);			/* buffers have cleaned up          */125 126	local_irq_disable();127 128	/* now, set the CPU clock speed register (0x80) */129	outb_p(0x80, REG_CSCIR);130	outb_p(elan_multiplier[state].val80h, REG_CSCDR);131 132	/* now, the hyperspeed bit in PMU Force Mode Register (0x40) */133	outb_p(0x40, REG_CSCIR);134	outb_p(elan_multiplier[state].val40h, REG_CSCDR);135	udelay(10000);136	local_irq_enable();137 138	return 0;139}140/*141 *	Module init and exit code142 */143 144static int elanfreq_cpu_init(struct cpufreq_policy *policy)145{146	struct cpuinfo_x86 *c = &cpu_data(0);147	struct cpufreq_frequency_table *pos;148 149	/* capability check */150	if ((c->x86_vendor != X86_VENDOR_AMD) ||151	    (c->x86 != 4) || (c->x86_model != 10))152		return -ENODEV;153 154	/* max freq */155	if (!max_freq)156		max_freq = elanfreq_get_cpu_frequency(0);157 158	/* table init */159	cpufreq_for_each_entry(pos, elanfreq_table)160		if (pos->frequency > max_freq)161			pos->frequency = CPUFREQ_ENTRY_INVALID;162 163	policy->freq_table = elanfreq_table;164	return 0;165}166 167 168#ifndef MODULE169/**170 * elanfreq_setup - elanfreq command line parameter parsing171 *172 * elanfreq command line parameter.  Use:173 *  elanfreq=66000174 * to set the maximum CPU frequency to 66 MHz. Note that in175 * case you do not give this boot parameter, the maximum176 * frequency will fall back to _current_ CPU frequency which177 * might be lower. If you build this as a module, use the178 * max_freq module parameter instead.179 */180static int __init elanfreq_setup(char *str)181{182	max_freq = simple_strtoul(str, &str, 0);183	pr_warn("You're using the deprecated elanfreq command line option. Use elanfreq.max_freq instead, please!\n");184	return 1;185}186__setup("elanfreq=", elanfreq_setup);187#endif188 189 190static struct cpufreq_driver elanfreq_driver = {191	.get		= elanfreq_get_cpu_frequency,192	.flags		= CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING,193	.verify		= cpufreq_generic_frequency_table_verify,194	.target_index	= elanfreq_target,195	.init		= elanfreq_cpu_init,196	.name		= "elanfreq",197	.attr		= cpufreq_generic_attr,198};199 200static const struct x86_cpu_id elan_id[] = {201	X86_MATCH_VENDOR_FAM_MODEL(AMD, 4, 10, NULL),202	{}203};204MODULE_DEVICE_TABLE(x86cpu, elan_id);205 206static int __init elanfreq_init(void)207{208	if (!x86_match_cpu(elan_id))209		return -ENODEV;210	return cpufreq_register_driver(&elanfreq_driver);211}212 213 214static void __exit elanfreq_exit(void)215{216	cpufreq_unregister_driver(&elanfreq_driver);217}218 219 220module_param(max_freq, int, 0444);221 222MODULE_LICENSE("GPL");223MODULE_AUTHOR("Robert Schwebel <r.schwebel@pengutronix.de>, "224		"Sven Geggus <sven@geggus.net>");225MODULE_DESCRIPTION("cpufreq driver for AMD's Elan CPUs");226 227module_init(elanfreq_init);228module_exit(elanfreq_exit);229