cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

sparc-us2e-cpufreq.c (8975B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/* us2e_cpufreq.c: UltraSPARC-IIe cpu frequency support
      3 *
      4 * Copyright (C) 2003 David S. Miller (davem@redhat.com)
      5 *
      6 * Many thanks to Dominik Brodowski for fixing up the cpufreq
      7 * infrastructure in order to make this driver easier to implement.
      8 */
      9
     10#include <linux/kernel.h>
     11#include <linux/module.h>
     12#include <linux/sched.h>
     13#include <linux/smp.h>
     14#include <linux/cpufreq.h>
     15#include <linux/threads.h>
     16#include <linux/slab.h>
     17#include <linux/delay.h>
     18#include <linux/init.h>
     19
     20#include <asm/asi.h>
     21#include <asm/timer.h>
     22
     23static struct cpufreq_driver *cpufreq_us2e_driver;
     24
     25struct us2e_freq_percpu_info {
     26	struct cpufreq_frequency_table table[6];
     27};
     28
     29/* Indexed by cpu number. */
     30static struct us2e_freq_percpu_info *us2e_freq_table;
     31
     32#define HBIRD_MEM_CNTL0_ADDR	0x1fe0000f010UL
     33#define HBIRD_ESTAR_MODE_ADDR	0x1fe0000f080UL
     34
     35/* UltraSPARC-IIe has five dividers: 1, 2, 4, 6, and 8.  These are controlled
     36 * in the ESTAR mode control register.
     37 */
     38#define ESTAR_MODE_DIV_1	0x0000000000000000UL
     39#define ESTAR_MODE_DIV_2	0x0000000000000001UL
     40#define ESTAR_MODE_DIV_4	0x0000000000000003UL
     41#define ESTAR_MODE_DIV_6	0x0000000000000002UL
     42#define ESTAR_MODE_DIV_8	0x0000000000000004UL
     43#define ESTAR_MODE_DIV_MASK	0x0000000000000007UL
     44
     45#define MCTRL0_SREFRESH_ENAB	0x0000000000010000UL
     46#define MCTRL0_REFR_COUNT_MASK	0x0000000000007f00UL
     47#define MCTRL0_REFR_COUNT_SHIFT	8
     48#define MCTRL0_REFR_INTERVAL	7800
     49#define MCTRL0_REFR_CLKS_P_CNT	64
     50
     51static unsigned long read_hbreg(unsigned long addr)
     52{
     53	unsigned long ret;
     54
     55	__asm__ __volatile__("ldxa	[%1] %2, %0"
     56			     : "=&r" (ret)
     57			     : "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E));
     58	return ret;
     59}
     60
     61static void write_hbreg(unsigned long addr, unsigned long val)
     62{
     63	__asm__ __volatile__("stxa	%0, [%1] %2\n\t"
     64			     "membar	#Sync"
     65			     : /* no outputs */
     66			     : "r" (val), "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E)
     67			     : "memory");
     68	if (addr == HBIRD_ESTAR_MODE_ADDR) {
     69		/* Need to wait 16 clock cycles for the PLL to lock.  */
     70		udelay(1);
     71	}
     72}
     73
     74static void self_refresh_ctl(int enable)
     75{
     76	unsigned long mctrl = read_hbreg(HBIRD_MEM_CNTL0_ADDR);
     77
     78	if (enable)
     79		mctrl |= MCTRL0_SREFRESH_ENAB;
     80	else
     81		mctrl &= ~MCTRL0_SREFRESH_ENAB;
     82	write_hbreg(HBIRD_MEM_CNTL0_ADDR, mctrl);
     83	(void) read_hbreg(HBIRD_MEM_CNTL0_ADDR);
     84}
     85
     86static void frob_mem_refresh(int cpu_slowing_down,
     87			     unsigned long clock_tick,
     88			     unsigned long old_divisor, unsigned long divisor)
     89{
     90	unsigned long old_refr_count, refr_count, mctrl;
     91
     92	refr_count  = (clock_tick * MCTRL0_REFR_INTERVAL);
     93	refr_count /= (MCTRL0_REFR_CLKS_P_CNT * divisor * 1000000000UL);
     94
     95	mctrl = read_hbreg(HBIRD_MEM_CNTL0_ADDR);
     96	old_refr_count = (mctrl & MCTRL0_REFR_COUNT_MASK)
     97		>> MCTRL0_REFR_COUNT_SHIFT;
     98
     99	mctrl &= ~MCTRL0_REFR_COUNT_MASK;
    100	mctrl |= refr_count << MCTRL0_REFR_COUNT_SHIFT;
    101	write_hbreg(HBIRD_MEM_CNTL0_ADDR, mctrl);
    102	mctrl = read_hbreg(HBIRD_MEM_CNTL0_ADDR);
    103
    104	if (cpu_slowing_down && !(mctrl & MCTRL0_SREFRESH_ENAB)) {
    105		unsigned long usecs;
    106
    107		/* We have to wait for both refresh counts (old
    108		 * and new) to go to zero.
    109		 */
    110		usecs = (MCTRL0_REFR_CLKS_P_CNT *
    111			 (refr_count + old_refr_count) *
    112			 1000000UL *
    113			 old_divisor) / clock_tick;
    114		udelay(usecs + 1UL);
    115	}
    116}
    117
    118static void us2e_transition(unsigned long estar, unsigned long new_bits,
    119			    unsigned long clock_tick,
    120			    unsigned long old_divisor, unsigned long divisor)
    121{
    122	estar &= ~ESTAR_MODE_DIV_MASK;
    123
    124	/* This is based upon the state transition diagram in the IIe manual.  */
    125	if (old_divisor == 2 && divisor == 1) {
    126		self_refresh_ctl(0);
    127		write_hbreg(HBIRD_ESTAR_MODE_ADDR, estar | new_bits);
    128		frob_mem_refresh(0, clock_tick, old_divisor, divisor);
    129	} else if (old_divisor == 1 && divisor == 2) {
    130		frob_mem_refresh(1, clock_tick, old_divisor, divisor);
    131		write_hbreg(HBIRD_ESTAR_MODE_ADDR, estar | new_bits);
    132		self_refresh_ctl(1);
    133	} else if (old_divisor == 1 && divisor > 2) {
    134		us2e_transition(estar, ESTAR_MODE_DIV_2, clock_tick,
    135				1, 2);
    136		us2e_transition(estar, new_bits, clock_tick,
    137				2, divisor);
    138	} else if (old_divisor > 2 && divisor == 1) {
    139		us2e_transition(estar, ESTAR_MODE_DIV_2, clock_tick,
    140				old_divisor, 2);
    141		us2e_transition(estar, new_bits, clock_tick,
    142				2, divisor);
    143	} else if (old_divisor < divisor) {
    144		frob_mem_refresh(0, clock_tick, old_divisor, divisor);
    145		write_hbreg(HBIRD_ESTAR_MODE_ADDR, estar | new_bits);
    146	} else if (old_divisor > divisor) {
    147		write_hbreg(HBIRD_ESTAR_MODE_ADDR, estar | new_bits);
    148		frob_mem_refresh(1, clock_tick, old_divisor, divisor);
    149	} else {
    150		BUG();
    151	}
    152}
    153
    154static unsigned long index_to_estar_mode(unsigned int index)
    155{
    156	switch (index) {
    157	case 0:
    158		return ESTAR_MODE_DIV_1;
    159
    160	case 1:
    161		return ESTAR_MODE_DIV_2;
    162
    163	case 2:
    164		return ESTAR_MODE_DIV_4;
    165
    166	case 3:
    167		return ESTAR_MODE_DIV_6;
    168
    169	case 4:
    170		return ESTAR_MODE_DIV_8;
    171
    172	default:
    173		BUG();
    174	}
    175}
    176
    177static unsigned long index_to_divisor(unsigned int index)
    178{
    179	switch (index) {
    180	case 0:
    181		return 1;
    182
    183	case 1:
    184		return 2;
    185
    186	case 2:
    187		return 4;
    188
    189	case 3:
    190		return 6;
    191
    192	case 4:
    193		return 8;
    194
    195	default:
    196		BUG();
    197	}
    198}
    199
    200static unsigned long estar_to_divisor(unsigned long estar)
    201{
    202	unsigned long ret;
    203
    204	switch (estar & ESTAR_MODE_DIV_MASK) {
    205	case ESTAR_MODE_DIV_1:
    206		ret = 1;
    207		break;
    208	case ESTAR_MODE_DIV_2:
    209		ret = 2;
    210		break;
    211	case ESTAR_MODE_DIV_4:
    212		ret = 4;
    213		break;
    214	case ESTAR_MODE_DIV_6:
    215		ret = 6;
    216		break;
    217	case ESTAR_MODE_DIV_8:
    218		ret = 8;
    219		break;
    220	default:
    221		BUG();
    222	}
    223
    224	return ret;
    225}
    226
    227static void __us2e_freq_get(void *arg)
    228{
    229	unsigned long *estar = arg;
    230
    231	*estar = read_hbreg(HBIRD_ESTAR_MODE_ADDR);
    232}
    233
    234static unsigned int us2e_freq_get(unsigned int cpu)
    235{
    236	unsigned long clock_tick, estar;
    237
    238	clock_tick = sparc64_get_clock_tick(cpu) / 1000;
    239	if (smp_call_function_single(cpu, __us2e_freq_get, &estar, 1))
    240		return 0;
    241
    242	return clock_tick / estar_to_divisor(estar);
    243}
    244
    245static void __us2e_freq_target(void *arg)
    246{
    247	unsigned int cpu = smp_processor_id();
    248	unsigned int *index = arg;
    249	unsigned long new_bits, new_freq;
    250	unsigned long clock_tick, divisor, old_divisor, estar;
    251
    252	new_freq = clock_tick = sparc64_get_clock_tick(cpu) / 1000;
    253	new_bits = index_to_estar_mode(*index);
    254	divisor = index_to_divisor(*index);
    255	new_freq /= divisor;
    256
    257	estar = read_hbreg(HBIRD_ESTAR_MODE_ADDR);
    258
    259	old_divisor = estar_to_divisor(estar);
    260
    261	if (old_divisor != divisor) {
    262		us2e_transition(estar, new_bits, clock_tick * 1000,
    263				old_divisor, divisor);
    264	}
    265}
    266
    267static int us2e_freq_target(struct cpufreq_policy *policy, unsigned int index)
    268{
    269	unsigned int cpu = policy->cpu;
    270
    271	return smp_call_function_single(cpu, __us2e_freq_target, &index, 1);
    272}
    273
    274static int __init us2e_freq_cpu_init(struct cpufreq_policy *policy)
    275{
    276	unsigned int cpu = policy->cpu;
    277	unsigned long clock_tick = sparc64_get_clock_tick(cpu) / 1000;
    278	struct cpufreq_frequency_table *table =
    279		&us2e_freq_table[cpu].table[0];
    280
    281	table[0].driver_data = 0;
    282	table[0].frequency = clock_tick / 1;
    283	table[1].driver_data = 1;
    284	table[1].frequency = clock_tick / 2;
    285	table[2].driver_data = 2;
    286	table[2].frequency = clock_tick / 4;
    287	table[2].driver_data = 3;
    288	table[2].frequency = clock_tick / 6;
    289	table[2].driver_data = 4;
    290	table[2].frequency = clock_tick / 8;
    291	table[2].driver_data = 5;
    292	table[3].frequency = CPUFREQ_TABLE_END;
    293
    294	policy->cpuinfo.transition_latency = 0;
    295	policy->cur = clock_tick;
    296	policy->freq_table = table;
    297
    298	return 0;
    299}
    300
    301static int us2e_freq_cpu_exit(struct cpufreq_policy *policy)
    302{
    303	if (cpufreq_us2e_driver)
    304		us2e_freq_target(policy, 0);
    305
    306	return 0;
    307}
    308
    309static int __init us2e_freq_init(void)
    310{
    311	unsigned long manuf, impl, ver;
    312	int ret;
    313
    314	if (tlb_type != spitfire)
    315		return -ENODEV;
    316
    317	__asm__("rdpr %%ver, %0" : "=r" (ver));
    318	manuf = ((ver >> 48) & 0xffff);
    319	impl  = ((ver >> 32) & 0xffff);
    320
    321	if (manuf == 0x17 && impl == 0x13) {
    322		struct cpufreq_driver *driver;
    323
    324		ret = -ENOMEM;
    325		driver = kzalloc(sizeof(*driver), GFP_KERNEL);
    326		if (!driver)
    327			goto err_out;
    328
    329		us2e_freq_table = kzalloc((NR_CPUS * sizeof(*us2e_freq_table)),
    330			GFP_KERNEL);
    331		if (!us2e_freq_table)
    332			goto err_out;
    333
    334		driver->init = us2e_freq_cpu_init;
    335		driver->verify = cpufreq_generic_frequency_table_verify;
    336		driver->target_index = us2e_freq_target;
    337		driver->get = us2e_freq_get;
    338		driver->exit = us2e_freq_cpu_exit;
    339		strcpy(driver->name, "UltraSPARC-IIe");
    340
    341		cpufreq_us2e_driver = driver;
    342		ret = cpufreq_register_driver(driver);
    343		if (ret)
    344			goto err_out;
    345
    346		return 0;
    347
    348err_out:
    349		if (driver) {
    350			kfree(driver);
    351			cpufreq_us2e_driver = NULL;
    352		}
    353		kfree(us2e_freq_table);
    354		us2e_freq_table = NULL;
    355		return ret;
    356	}
    357
    358	return -ENODEV;
    359}
    360
    361static void __exit us2e_freq_exit(void)
    362{
    363	if (cpufreq_us2e_driver) {
    364		cpufreq_unregister_driver(cpufreq_us2e_driver);
    365		kfree(cpufreq_us2e_driver);
    366		cpufreq_us2e_driver = NULL;
    367		kfree(us2e_freq_table);
    368		us2e_freq_table = NULL;
    369	}
    370}
    371
    372MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
    373MODULE_DESCRIPTION("cpufreq driver for UltraSPARC-IIe");
    374MODULE_LICENSE("GPL");
    375
    376module_init(us2e_freq_init);
    377module_exit(us2e_freq_exit);