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

clk-prcmu.c (9920B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * PRCMU clock implementation for ux500 platform.
      4 *
      5 * Copyright (C) 2012 ST-Ericsson SA
      6 * Author: Ulf Hansson <ulf.hansson@linaro.org>
      7 */
      8
      9#include <linux/clk-provider.h>
     10#include <linux/mfd/dbx500-prcmu.h>
     11#include <linux/slab.h>
     12#include <linux/io.h>
     13#include <linux/err.h>
     14#include "clk.h"
     15
     16#define to_clk_prcmu(_hw) container_of(_hw, struct clk_prcmu, hw)
     17#define to_clk_prcmu_clkout(_hw) container_of(_hw, struct clk_prcmu_clkout, hw)
     18
     19struct clk_prcmu {
     20	struct clk_hw hw;
     21	u8 cg_sel;
     22	int opp_requested;
     23};
     24
     25struct clk_prcmu_clkout {
     26	struct clk_hw hw;
     27	u8 clkout_id;
     28	u8 source;
     29	u8 divider;
     30};
     31
     32/* PRCMU clock operations. */
     33
     34static int clk_prcmu_prepare(struct clk_hw *hw)
     35{
     36	struct clk_prcmu *clk = to_clk_prcmu(hw);
     37
     38	return prcmu_request_clock(clk->cg_sel, true);
     39}
     40
     41static void clk_prcmu_unprepare(struct clk_hw *hw)
     42{
     43	struct clk_prcmu *clk = to_clk_prcmu(hw);
     44	if (prcmu_request_clock(clk->cg_sel, false))
     45		pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
     46		       clk_hw_get_name(hw));
     47}
     48
     49static unsigned long clk_prcmu_recalc_rate(struct clk_hw *hw,
     50					   unsigned long parent_rate)
     51{
     52	struct clk_prcmu *clk = to_clk_prcmu(hw);
     53	return prcmu_clock_rate(clk->cg_sel);
     54}
     55
     56static long clk_prcmu_round_rate(struct clk_hw *hw, unsigned long rate,
     57				 unsigned long *parent_rate)
     58{
     59	struct clk_prcmu *clk = to_clk_prcmu(hw);
     60	return prcmu_round_clock_rate(clk->cg_sel, rate);
     61}
     62
     63static int clk_prcmu_set_rate(struct clk_hw *hw, unsigned long rate,
     64			      unsigned long parent_rate)
     65{
     66	struct clk_prcmu *clk = to_clk_prcmu(hw);
     67	return prcmu_set_clock_rate(clk->cg_sel, rate);
     68}
     69
     70static int clk_prcmu_opp_prepare(struct clk_hw *hw)
     71{
     72	int err;
     73	struct clk_prcmu *clk = to_clk_prcmu(hw);
     74
     75	if (!clk->opp_requested) {
     76		err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP,
     77						(char *)clk_hw_get_name(hw),
     78						100);
     79		if (err) {
     80			pr_err("clk_prcmu: %s fail req APE OPP for %s.\n",
     81				__func__, clk_hw_get_name(hw));
     82			return err;
     83		}
     84		clk->opp_requested = 1;
     85	}
     86
     87	err = prcmu_request_clock(clk->cg_sel, true);
     88	if (err) {
     89		prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP,
     90					(char *)clk_hw_get_name(hw));
     91		clk->opp_requested = 0;
     92		return err;
     93	}
     94
     95	return 0;
     96}
     97
     98static void clk_prcmu_opp_unprepare(struct clk_hw *hw)
     99{
    100	struct clk_prcmu *clk = to_clk_prcmu(hw);
    101
    102	if (prcmu_request_clock(clk->cg_sel, false)) {
    103		pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
    104			clk_hw_get_name(hw));
    105		return;
    106	}
    107
    108	if (clk->opp_requested) {
    109		prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP,
    110					(char *)clk_hw_get_name(hw));
    111		clk->opp_requested = 0;
    112	}
    113}
    114
    115static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw)
    116{
    117	int err;
    118	struct clk_prcmu *clk = to_clk_prcmu(hw);
    119
    120	if (!clk->opp_requested) {
    121		err = prcmu_request_ape_opp_100_voltage(true);
    122		if (err) {
    123			pr_err("clk_prcmu: %s fail req APE OPP VOLT for %s.\n",
    124				__func__, clk_hw_get_name(hw));
    125			return err;
    126		}
    127		clk->opp_requested = 1;
    128	}
    129
    130	err = prcmu_request_clock(clk->cg_sel, true);
    131	if (err) {
    132		prcmu_request_ape_opp_100_voltage(false);
    133		clk->opp_requested = 0;
    134		return err;
    135	}
    136
    137	return 0;
    138}
    139
    140static void clk_prcmu_opp_volt_unprepare(struct clk_hw *hw)
    141{
    142	struct clk_prcmu *clk = to_clk_prcmu(hw);
    143
    144	if (prcmu_request_clock(clk->cg_sel, false)) {
    145		pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
    146			clk_hw_get_name(hw));
    147		return;
    148	}
    149
    150	if (clk->opp_requested) {
    151		prcmu_request_ape_opp_100_voltage(false);
    152		clk->opp_requested = 0;
    153	}
    154}
    155
    156static const struct clk_ops clk_prcmu_scalable_ops = {
    157	.prepare = clk_prcmu_prepare,
    158	.unprepare = clk_prcmu_unprepare,
    159	.recalc_rate = clk_prcmu_recalc_rate,
    160	.round_rate = clk_prcmu_round_rate,
    161	.set_rate = clk_prcmu_set_rate,
    162};
    163
    164static const struct clk_ops clk_prcmu_gate_ops = {
    165	.prepare = clk_prcmu_prepare,
    166	.unprepare = clk_prcmu_unprepare,
    167	.recalc_rate = clk_prcmu_recalc_rate,
    168};
    169
    170static const struct clk_ops clk_prcmu_scalable_rate_ops = {
    171	.recalc_rate = clk_prcmu_recalc_rate,
    172	.round_rate = clk_prcmu_round_rate,
    173	.set_rate = clk_prcmu_set_rate,
    174};
    175
    176static const struct clk_ops clk_prcmu_rate_ops = {
    177	.recalc_rate = clk_prcmu_recalc_rate,
    178};
    179
    180static const struct clk_ops clk_prcmu_opp_gate_ops = {
    181	.prepare = clk_prcmu_opp_prepare,
    182	.unprepare = clk_prcmu_opp_unprepare,
    183	.recalc_rate = clk_prcmu_recalc_rate,
    184};
    185
    186static const struct clk_ops clk_prcmu_opp_volt_scalable_ops = {
    187	.prepare = clk_prcmu_opp_volt_prepare,
    188	.unprepare = clk_prcmu_opp_volt_unprepare,
    189	.recalc_rate = clk_prcmu_recalc_rate,
    190	.round_rate = clk_prcmu_round_rate,
    191	.set_rate = clk_prcmu_set_rate,
    192};
    193
    194static struct clk_hw *clk_reg_prcmu(const char *name,
    195				    const char *parent_name,
    196				    u8 cg_sel,
    197				    unsigned long rate,
    198				    unsigned long flags,
    199				    const struct clk_ops *clk_prcmu_ops)
    200{
    201	struct clk_prcmu *clk;
    202	struct clk_init_data clk_prcmu_init;
    203	int ret;
    204
    205	if (!name) {
    206		pr_err("clk_prcmu: %s invalid arguments passed\n", __func__);
    207		return ERR_PTR(-EINVAL);
    208	}
    209
    210	clk = kzalloc(sizeof(*clk), GFP_KERNEL);
    211	if (!clk)
    212		return ERR_PTR(-ENOMEM);
    213
    214	clk->cg_sel = cg_sel;
    215	clk->opp_requested = 0;
    216	/* "rate" can be used for changing the initial frequency */
    217	if (rate)
    218		prcmu_set_clock_rate(cg_sel, rate);
    219
    220	clk_prcmu_init.name = name;
    221	clk_prcmu_init.ops = clk_prcmu_ops;
    222	clk_prcmu_init.flags = flags;
    223	clk_prcmu_init.parent_names = (parent_name ? &parent_name : NULL);
    224	clk_prcmu_init.num_parents = (parent_name ? 1 : 0);
    225	clk->hw.init = &clk_prcmu_init;
    226
    227	ret = clk_hw_register(NULL, &clk->hw);
    228	if (ret)
    229		goto free_clk;
    230
    231	return &clk->hw;
    232
    233free_clk:
    234	kfree(clk);
    235	pr_err("clk_prcmu: %s failed to register clk\n", __func__);
    236	return ERR_PTR(-ENOMEM);
    237}
    238
    239struct clk_hw *clk_reg_prcmu_scalable(const char *name,
    240				      const char *parent_name,
    241				      u8 cg_sel,
    242				      unsigned long rate,
    243				      unsigned long flags)
    244{
    245	return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags,
    246			&clk_prcmu_scalable_ops);
    247}
    248
    249struct clk_hw *clk_reg_prcmu_gate(const char *name,
    250				  const char *parent_name,
    251				  u8 cg_sel,
    252				  unsigned long flags)
    253{
    254	return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags,
    255			&clk_prcmu_gate_ops);
    256}
    257
    258struct clk_hw *clk_reg_prcmu_scalable_rate(const char *name,
    259					   const char *parent_name,
    260					   u8 cg_sel,
    261					   unsigned long rate,
    262					   unsigned long flags)
    263{
    264	return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags,
    265			&clk_prcmu_scalable_rate_ops);
    266}
    267
    268struct clk_hw *clk_reg_prcmu_rate(const char *name,
    269				  const char *parent_name,
    270				  u8 cg_sel,
    271				  unsigned long flags)
    272{
    273	return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags,
    274			&clk_prcmu_rate_ops);
    275}
    276
    277struct clk_hw *clk_reg_prcmu_opp_gate(const char *name,
    278				      const char *parent_name,
    279				      u8 cg_sel,
    280				      unsigned long flags)
    281{
    282	return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags,
    283			&clk_prcmu_opp_gate_ops);
    284}
    285
    286struct clk_hw *clk_reg_prcmu_opp_volt_scalable(const char *name,
    287					       const char *parent_name,
    288					       u8 cg_sel,
    289					       unsigned long rate,
    290					       unsigned long flags)
    291{
    292	return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags,
    293			&clk_prcmu_opp_volt_scalable_ops);
    294}
    295
    296/* The clkout (external) clock is special and need special ops */
    297
    298static int clk_prcmu_clkout_prepare(struct clk_hw *hw)
    299{
    300	struct clk_prcmu_clkout *clk = to_clk_prcmu_clkout(hw);
    301
    302	return prcmu_config_clkout(clk->clkout_id, clk->source, clk->divider);
    303}
    304
    305static void clk_prcmu_clkout_unprepare(struct clk_hw *hw)
    306{
    307	struct clk_prcmu_clkout *clk = to_clk_prcmu_clkout(hw);
    308	int ret;
    309
    310	/* The clkout clock is disabled by dividing by 0 */
    311	ret = prcmu_config_clkout(clk->clkout_id, clk->source, 0);
    312	if (ret)
    313		pr_err("clk_prcmu: %s failed to disable %s\n", __func__,
    314		       clk_hw_get_name(hw));
    315}
    316
    317static unsigned long clk_prcmu_clkout_recalc_rate(struct clk_hw *hw,
    318						  unsigned long parent_rate)
    319{
    320	struct clk_prcmu_clkout *clk = to_clk_prcmu_clkout(hw);
    321
    322	return (parent_rate / clk->divider);
    323}
    324
    325static u8 clk_prcmu_clkout_get_parent(struct clk_hw *hw)
    326{
    327	struct clk_prcmu_clkout *clk = to_clk_prcmu_clkout(hw);
    328
    329	return clk->source;
    330}
    331
    332static int clk_prcmu_clkout_set_parent(struct clk_hw *hw, u8 index)
    333{
    334	struct clk_prcmu_clkout *clk = to_clk_prcmu_clkout(hw);
    335
    336	clk->source = index;
    337	/* Make sure the change reaches the hardware immediately */
    338	if (clk_hw_is_prepared(hw))
    339		return clk_prcmu_clkout_prepare(hw);
    340	return 0;
    341}
    342
    343static const struct clk_ops clk_prcmu_clkout_ops = {
    344	.prepare = clk_prcmu_clkout_prepare,
    345	.unprepare = clk_prcmu_clkout_unprepare,
    346	.recalc_rate = clk_prcmu_clkout_recalc_rate,
    347	.get_parent = clk_prcmu_clkout_get_parent,
    348	.set_parent = clk_prcmu_clkout_set_parent,
    349};
    350
    351struct clk_hw *clk_reg_prcmu_clkout(const char *name,
    352				    const char * const *parent_names,
    353				    int num_parents,
    354				    u8 source, u8 divider)
    355
    356{
    357	struct clk_prcmu_clkout *clk;
    358	struct clk_init_data clk_prcmu_clkout_init;
    359	u8 clkout_id;
    360	int ret;
    361
    362	if (!name) {
    363		pr_err("clk_prcmu_clkout: %s invalid arguments passed\n", __func__);
    364		return ERR_PTR(-EINVAL);
    365	}
    366
    367	if (!strcmp(name, "clkout1"))
    368		clkout_id = 0;
    369	else if (!strcmp(name, "clkout2"))
    370		clkout_id = 1;
    371	else {
    372		pr_err("clk_prcmu_clkout: %s bad clock name\n", __func__);
    373		return ERR_PTR(-EINVAL);
    374	}
    375
    376	clk = kzalloc(sizeof(*clk), GFP_KERNEL);
    377	if (!clk)
    378		return ERR_PTR(-ENOMEM);
    379
    380	clk->clkout_id = clkout_id;
    381	clk->source = source;
    382	clk->divider = divider;
    383
    384	clk_prcmu_clkout_init.name = name;
    385	clk_prcmu_clkout_init.ops = &clk_prcmu_clkout_ops;
    386	clk_prcmu_clkout_init.flags = CLK_GET_RATE_NOCACHE;
    387	clk_prcmu_clkout_init.parent_names = parent_names;
    388	clk_prcmu_clkout_init.num_parents = num_parents;
    389	clk->hw.init = &clk_prcmu_clkout_init;
    390
    391	ret = clk_hw_register(NULL, &clk->hw);
    392	if (ret)
    393		goto free_clkout;
    394
    395	return &clk->hw;
    396free_clkout:
    397	kfree(clk);
    398	pr_err("clk_prcmu_clkout: %s failed to register clk\n", __func__);
    399	return ERR_PTR(-ENOMEM);
    400}