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

s3c2416-cpufreq.c (12446B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * S3C2416/2450 CPUfreq Support
      4 *
      5 * Copyright 2011 Heiko Stuebner <heiko@sntech.de>
      6 *
      7 * based on s3c64xx_cpufreq.c
      8 *
      9 * Copyright 2009 Wolfson Microelectronics plc
     10 */
     11
     12#include <linux/kernel.h>
     13#include <linux/types.h>
     14#include <linux/init.h>
     15#include <linux/cpufreq.h>
     16#include <linux/clk.h>
     17#include <linux/err.h>
     18#include <linux/regulator/consumer.h>
     19#include <linux/reboot.h>
     20#include <linux/module.h>
     21
     22static DEFINE_MUTEX(cpufreq_lock);
     23
     24struct s3c2416_data {
     25	struct clk *armdiv;
     26	struct clk *armclk;
     27	struct clk *hclk;
     28
     29	unsigned long regulator_latency;
     30#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
     31	struct regulator *vddarm;
     32#endif
     33
     34	struct cpufreq_frequency_table *freq_table;
     35
     36	bool is_dvs;
     37	bool disable_dvs;
     38};
     39
     40static struct s3c2416_data s3c2416_cpufreq;
     41
     42struct s3c2416_dvfs {
     43	unsigned int vddarm_min;
     44	unsigned int vddarm_max;
     45};
     46
     47/* pseudo-frequency for dvs mode */
     48#define FREQ_DVS	132333
     49
     50/* frequency to sleep and reboot in
     51 * it's essential to leave dvs, as some boards do not reconfigure the
     52 * regulator on reboot
     53 */
     54#define FREQ_SLEEP	133333
     55
     56/* Sources for the ARMCLK */
     57#define SOURCE_HCLK	0
     58#define SOURCE_ARMDIV	1
     59
     60#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
     61/* S3C2416 only supports changing the voltage in the dvs-mode.
     62 * Voltages down to 1.0V seem to work, so we take what the regulator
     63 * can get us.
     64 */
     65static struct s3c2416_dvfs s3c2416_dvfs_table[] = {
     66	[SOURCE_HCLK] = {  950000, 1250000 },
     67	[SOURCE_ARMDIV] = { 1250000, 1350000 },
     68};
     69#endif
     70
     71static struct cpufreq_frequency_table s3c2416_freq_table[] = {
     72	{ 0, SOURCE_HCLK, FREQ_DVS },
     73	{ 0, SOURCE_ARMDIV, 133333 },
     74	{ 0, SOURCE_ARMDIV, 266666 },
     75	{ 0, SOURCE_ARMDIV, 400000 },
     76	{ 0, 0, CPUFREQ_TABLE_END },
     77};
     78
     79static struct cpufreq_frequency_table s3c2450_freq_table[] = {
     80	{ 0, SOURCE_HCLK, FREQ_DVS },
     81	{ 0, SOURCE_ARMDIV, 133500 },
     82	{ 0, SOURCE_ARMDIV, 267000 },
     83	{ 0, SOURCE_ARMDIV, 534000 },
     84	{ 0, 0, CPUFREQ_TABLE_END },
     85};
     86
     87static unsigned int s3c2416_cpufreq_get_speed(unsigned int cpu)
     88{
     89	struct s3c2416_data *s3c_freq = &s3c2416_cpufreq;
     90
     91	if (cpu != 0)
     92		return 0;
     93
     94	/* return our pseudo-frequency when in dvs mode */
     95	if (s3c_freq->is_dvs)
     96		return FREQ_DVS;
     97
     98	return clk_get_rate(s3c_freq->armclk) / 1000;
     99}
    100
    101static int s3c2416_cpufreq_set_armdiv(struct s3c2416_data *s3c_freq,
    102				      unsigned int freq)
    103{
    104	int ret;
    105
    106	if (clk_get_rate(s3c_freq->armdiv) / 1000 != freq) {
    107		ret = clk_set_rate(s3c_freq->armdiv, freq * 1000);
    108		if (ret < 0) {
    109			pr_err("cpufreq: Failed to set armdiv rate %dkHz: %d\n",
    110			       freq, ret);
    111			return ret;
    112		}
    113	}
    114
    115	return 0;
    116}
    117
    118static int s3c2416_cpufreq_enter_dvs(struct s3c2416_data *s3c_freq, int idx)
    119{
    120#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
    121	struct s3c2416_dvfs *dvfs;
    122#endif
    123	int ret;
    124
    125	if (s3c_freq->is_dvs) {
    126		pr_debug("cpufreq: already in dvs mode, nothing to do\n");
    127		return 0;
    128	}
    129
    130	pr_debug("cpufreq: switching armclk to hclk (%lukHz)\n",
    131		 clk_get_rate(s3c_freq->hclk) / 1000);
    132	ret = clk_set_parent(s3c_freq->armclk, s3c_freq->hclk);
    133	if (ret < 0) {
    134		pr_err("cpufreq: Failed to switch armclk to hclk: %d\n", ret);
    135		return ret;
    136	}
    137
    138#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
    139	/* changing the core voltage is only allowed when in dvs mode */
    140	if (s3c_freq->vddarm) {
    141		dvfs = &s3c2416_dvfs_table[idx];
    142
    143		pr_debug("cpufreq: setting regulator to %d-%d\n",
    144			 dvfs->vddarm_min, dvfs->vddarm_max);
    145		ret = regulator_set_voltage(s3c_freq->vddarm,
    146					    dvfs->vddarm_min,
    147					    dvfs->vddarm_max);
    148
    149		/* when lowering the voltage failed, there is nothing to do */
    150		if (ret != 0)
    151			pr_err("cpufreq: Failed to set VDDARM: %d\n", ret);
    152	}
    153#endif
    154
    155	s3c_freq->is_dvs = 1;
    156
    157	return 0;
    158}
    159
    160static int s3c2416_cpufreq_leave_dvs(struct s3c2416_data *s3c_freq, int idx)
    161{
    162#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
    163	struct s3c2416_dvfs *dvfs;
    164#endif
    165	int ret;
    166
    167	if (!s3c_freq->is_dvs) {
    168		pr_debug("cpufreq: not in dvs mode, so can't leave\n");
    169		return 0;
    170	}
    171
    172#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
    173	if (s3c_freq->vddarm) {
    174		dvfs = &s3c2416_dvfs_table[idx];
    175
    176		pr_debug("cpufreq: setting regulator to %d-%d\n",
    177			 dvfs->vddarm_min, dvfs->vddarm_max);
    178		ret = regulator_set_voltage(s3c_freq->vddarm,
    179					    dvfs->vddarm_min,
    180					    dvfs->vddarm_max);
    181		if (ret != 0) {
    182			pr_err("cpufreq: Failed to set VDDARM: %d\n", ret);
    183			return ret;
    184		}
    185	}
    186#endif
    187
    188	/* force armdiv to hclk frequency for transition from dvs*/
    189	if (clk_get_rate(s3c_freq->armdiv) > clk_get_rate(s3c_freq->hclk)) {
    190		pr_debug("cpufreq: force armdiv to hclk frequency (%lukHz)\n",
    191			 clk_get_rate(s3c_freq->hclk) / 1000);
    192		ret = s3c2416_cpufreq_set_armdiv(s3c_freq,
    193					clk_get_rate(s3c_freq->hclk) / 1000);
    194		if (ret < 0) {
    195			pr_err("cpufreq: Failed to set the armdiv to %lukHz: %d\n",
    196			       clk_get_rate(s3c_freq->hclk) / 1000, ret);
    197			return ret;
    198		}
    199	}
    200
    201	pr_debug("cpufreq: switching armclk parent to armdiv (%lukHz)\n",
    202			clk_get_rate(s3c_freq->armdiv) / 1000);
    203
    204	ret = clk_set_parent(s3c_freq->armclk, s3c_freq->armdiv);
    205	if (ret < 0) {
    206		pr_err("cpufreq: Failed to switch armclk clock parent to armdiv: %d\n",
    207		       ret);
    208		return ret;
    209	}
    210
    211	s3c_freq->is_dvs = 0;
    212
    213	return 0;
    214}
    215
    216static int s3c2416_cpufreq_set_target(struct cpufreq_policy *policy,
    217				      unsigned int index)
    218{
    219	struct s3c2416_data *s3c_freq = &s3c2416_cpufreq;
    220	unsigned int new_freq;
    221	int idx, ret, to_dvs = 0;
    222
    223	mutex_lock(&cpufreq_lock);
    224
    225	idx = s3c_freq->freq_table[index].driver_data;
    226
    227	if (idx == SOURCE_HCLK)
    228		to_dvs = 1;
    229
    230	/* switching to dvs when it's not allowed */
    231	if (to_dvs && s3c_freq->disable_dvs) {
    232		pr_debug("cpufreq: entering dvs mode not allowed\n");
    233		ret = -EINVAL;
    234		goto out;
    235	}
    236
    237	/* When leavin dvs mode, always switch the armdiv to the hclk rate
    238	 * The S3C2416 has stability issues when switching directly to
    239	 * higher frequencies.
    240	 */
    241	new_freq = (s3c_freq->is_dvs && !to_dvs)
    242				? clk_get_rate(s3c_freq->hclk) / 1000
    243				: s3c_freq->freq_table[index].frequency;
    244
    245	if (to_dvs) {
    246		pr_debug("cpufreq: enter dvs\n");
    247		ret = s3c2416_cpufreq_enter_dvs(s3c_freq, idx);
    248	} else if (s3c_freq->is_dvs) {
    249		pr_debug("cpufreq: leave dvs\n");
    250		ret = s3c2416_cpufreq_leave_dvs(s3c_freq, idx);
    251	} else {
    252		pr_debug("cpufreq: change armdiv to %dkHz\n", new_freq);
    253		ret = s3c2416_cpufreq_set_armdiv(s3c_freq, new_freq);
    254	}
    255
    256out:
    257	mutex_unlock(&cpufreq_lock);
    258
    259	return ret;
    260}
    261
    262#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
    263static void s3c2416_cpufreq_cfg_regulator(struct s3c2416_data *s3c_freq)
    264{
    265	int count, v, i, found;
    266	struct cpufreq_frequency_table *pos;
    267	struct s3c2416_dvfs *dvfs;
    268
    269	count = regulator_count_voltages(s3c_freq->vddarm);
    270	if (count < 0) {
    271		pr_err("cpufreq: Unable to check supported voltages\n");
    272		return;
    273	}
    274
    275	if (!count)
    276		goto out;
    277
    278	cpufreq_for_each_valid_entry(pos, s3c_freq->freq_table) {
    279		dvfs = &s3c2416_dvfs_table[pos->driver_data];
    280		found = 0;
    281
    282		/* Check only the min-voltage, more is always ok on S3C2416 */
    283		for (i = 0; i < count; i++) {
    284			v = regulator_list_voltage(s3c_freq->vddarm, i);
    285			if (v >= dvfs->vddarm_min)
    286				found = 1;
    287		}
    288
    289		if (!found) {
    290			pr_debug("cpufreq: %dkHz unsupported by regulator\n",
    291				 pos->frequency);
    292			pos->frequency = CPUFREQ_ENTRY_INVALID;
    293		}
    294	}
    295
    296out:
    297	/* Guessed */
    298	s3c_freq->regulator_latency = 1 * 1000 * 1000;
    299}
    300#endif
    301
    302static int s3c2416_cpufreq_reboot_notifier_evt(struct notifier_block *this,
    303					       unsigned long event, void *ptr)
    304{
    305	struct s3c2416_data *s3c_freq = &s3c2416_cpufreq;
    306	int ret;
    307	struct cpufreq_policy *policy;
    308
    309	mutex_lock(&cpufreq_lock);
    310
    311	/* disable further changes */
    312	s3c_freq->disable_dvs = 1;
    313
    314	mutex_unlock(&cpufreq_lock);
    315
    316	/* some boards don't reconfigure the regulator on reboot, which
    317	 * could lead to undervolting the cpu when the clock is reset.
    318	 * Therefore we always leave the DVS mode on reboot.
    319	 */
    320	if (s3c_freq->is_dvs) {
    321		pr_debug("cpufreq: leave dvs on reboot\n");
    322
    323		policy = cpufreq_cpu_get(0);
    324		if (!policy) {
    325			pr_debug("cpufreq: get no policy for cpu0\n");
    326			return NOTIFY_BAD;
    327		}
    328
    329		ret = cpufreq_driver_target(policy, FREQ_SLEEP, 0);
    330		cpufreq_cpu_put(policy);
    331
    332		if (ret < 0)
    333			return NOTIFY_BAD;
    334	}
    335
    336	return NOTIFY_DONE;
    337}
    338
    339static struct notifier_block s3c2416_cpufreq_reboot_notifier = {
    340	.notifier_call = s3c2416_cpufreq_reboot_notifier_evt,
    341};
    342
    343static int s3c2416_cpufreq_driver_init(struct cpufreq_policy *policy)
    344{
    345	struct s3c2416_data *s3c_freq = &s3c2416_cpufreq;
    346	struct cpufreq_frequency_table *pos;
    347	struct clk *msysclk;
    348	unsigned long rate;
    349	int ret;
    350
    351	if (policy->cpu != 0)
    352		return -EINVAL;
    353
    354	msysclk = clk_get(NULL, "msysclk");
    355	if (IS_ERR(msysclk)) {
    356		ret = PTR_ERR(msysclk);
    357		pr_err("cpufreq: Unable to obtain msysclk: %d\n", ret);
    358		return ret;
    359	}
    360
    361	/*
    362	 * S3C2416 and S3C2450 share the same processor-ID and also provide no
    363	 * other means to distinguish them other than through the rate of
    364	 * msysclk. On S3C2416 msysclk runs at 800MHz and on S3C2450 at 533MHz.
    365	 */
    366	rate = clk_get_rate(msysclk);
    367	if (rate == 800 * 1000 * 1000) {
    368		pr_info("cpufreq: msysclk running at %lukHz, using S3C2416 frequency table\n",
    369			rate / 1000);
    370		s3c_freq->freq_table = s3c2416_freq_table;
    371		policy->cpuinfo.max_freq = 400000;
    372	} else if (rate / 1000 == 534000) {
    373		pr_info("cpufreq: msysclk running at %lukHz, using S3C2450 frequency table\n",
    374			rate / 1000);
    375		s3c_freq->freq_table = s3c2450_freq_table;
    376		policy->cpuinfo.max_freq = 534000;
    377	}
    378
    379	/* not needed anymore */
    380	clk_put(msysclk);
    381
    382	if (s3c_freq->freq_table == NULL) {
    383		pr_err("cpufreq: No frequency information for this CPU, msysclk at %lukHz\n",
    384		       rate / 1000);
    385		return -ENODEV;
    386	}
    387
    388	s3c_freq->is_dvs = 0;
    389
    390	s3c_freq->armdiv = clk_get(NULL, "armdiv");
    391	if (IS_ERR(s3c_freq->armdiv)) {
    392		ret = PTR_ERR(s3c_freq->armdiv);
    393		pr_err("cpufreq: Unable to obtain ARMDIV: %d\n", ret);
    394		return ret;
    395	}
    396
    397	s3c_freq->hclk = clk_get(NULL, "hclk");
    398	if (IS_ERR(s3c_freq->hclk)) {
    399		ret = PTR_ERR(s3c_freq->hclk);
    400		pr_err("cpufreq: Unable to obtain HCLK: %d\n", ret);
    401		goto err_hclk;
    402	}
    403
    404	/* chech hclk rate, we only support the common 133MHz for now
    405	 * hclk could also run at 66MHz, but this not often used
    406	 */
    407	rate = clk_get_rate(s3c_freq->hclk);
    408	if (rate < 133 * 1000 * 1000) {
    409		pr_err("cpufreq: HCLK not at 133MHz\n");
    410		ret = -EINVAL;
    411		goto err_armclk;
    412	}
    413
    414	s3c_freq->armclk = clk_get(NULL, "armclk");
    415	if (IS_ERR(s3c_freq->armclk)) {
    416		ret = PTR_ERR(s3c_freq->armclk);
    417		pr_err("cpufreq: Unable to obtain ARMCLK: %d\n", ret);
    418		goto err_armclk;
    419	}
    420
    421#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
    422	s3c_freq->vddarm = regulator_get(NULL, "vddarm");
    423	if (IS_ERR(s3c_freq->vddarm)) {
    424		ret = PTR_ERR(s3c_freq->vddarm);
    425		pr_err("cpufreq: Failed to obtain VDDARM: %d\n", ret);
    426		goto err_vddarm;
    427	}
    428
    429	s3c2416_cpufreq_cfg_regulator(s3c_freq);
    430#else
    431	s3c_freq->regulator_latency = 0;
    432#endif
    433
    434	cpufreq_for_each_entry(pos, s3c_freq->freq_table) {
    435		/* special handling for dvs mode */
    436		if (pos->driver_data == 0) {
    437			if (!s3c_freq->hclk) {
    438				pr_debug("cpufreq: %dkHz unsupported as it would need unavailable dvs mode\n",
    439					 pos->frequency);
    440				pos->frequency = CPUFREQ_ENTRY_INVALID;
    441			} else {
    442				continue;
    443			}
    444		}
    445
    446		/* Check for frequencies we can generate */
    447		rate = clk_round_rate(s3c_freq->armdiv,
    448				      pos->frequency * 1000);
    449		rate /= 1000;
    450		if (rate != pos->frequency) {
    451			pr_debug("cpufreq: %dkHz unsupported by clock (clk_round_rate return %lu)\n",
    452				pos->frequency, rate);
    453			pos->frequency = CPUFREQ_ENTRY_INVALID;
    454		}
    455	}
    456
    457	/* Datasheet says PLL stabalisation time must be at least 300us,
    458	 * so but add some fudge. (reference in LOCKCON0 register description)
    459	 */
    460	cpufreq_generic_init(policy, s3c_freq->freq_table,
    461			(500 * 1000) + s3c_freq->regulator_latency);
    462	register_reboot_notifier(&s3c2416_cpufreq_reboot_notifier);
    463
    464	return 0;
    465
    466#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
    467err_vddarm:
    468	clk_put(s3c_freq->armclk);
    469#endif
    470err_armclk:
    471	clk_put(s3c_freq->hclk);
    472err_hclk:
    473	clk_put(s3c_freq->armdiv);
    474
    475	return ret;
    476}
    477
    478static struct cpufreq_driver s3c2416_cpufreq_driver = {
    479	.flags		= CPUFREQ_NEED_INITIAL_FREQ_CHECK,
    480	.verify		= cpufreq_generic_frequency_table_verify,
    481	.target_index	= s3c2416_cpufreq_set_target,
    482	.get		= s3c2416_cpufreq_get_speed,
    483	.init		= s3c2416_cpufreq_driver_init,
    484	.name		= "s3c2416",
    485	.attr		= cpufreq_generic_attr,
    486};
    487
    488static int __init s3c2416_cpufreq_init(void)
    489{
    490	return cpufreq_register_driver(&s3c2416_cpufreq_driver);
    491}
    492module_init(s3c2416_cpufreq_init);