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-highbank.c (7806B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright 2011-2012 Calxeda, Inc.
      4 */
      5
      6#include <linux/kernel.h>
      7#include <linux/slab.h>
      8#include <linux/err.h>
      9#include <linux/clk-provider.h>
     10#include <linux/io.h>
     11#include <linux/of.h>
     12#include <linux/of_address.h>
     13
     14#define HB_PLL_LOCK_500		0x20000000
     15#define HB_PLL_LOCK		0x10000000
     16#define HB_PLL_DIVF_SHIFT	20
     17#define HB_PLL_DIVF_MASK	0x0ff00000
     18#define HB_PLL_DIVQ_SHIFT	16
     19#define HB_PLL_DIVQ_MASK	0x00070000
     20#define HB_PLL_DIVR_SHIFT	8
     21#define HB_PLL_DIVR_MASK	0x00001f00
     22#define HB_PLL_RANGE_SHIFT	4
     23#define HB_PLL_RANGE_MASK	0x00000070
     24#define HB_PLL_BYPASS		0x00000008
     25#define HB_PLL_RESET		0x00000004
     26#define HB_PLL_EXT_BYPASS	0x00000002
     27#define HB_PLL_EXT_ENA		0x00000001
     28
     29#define HB_PLL_VCO_MIN_FREQ	2133000000
     30#define HB_PLL_MAX_FREQ		HB_PLL_VCO_MIN_FREQ
     31#define HB_PLL_MIN_FREQ		(HB_PLL_VCO_MIN_FREQ / 64)
     32
     33#define HB_A9_BCLK_DIV_MASK	0x00000006
     34#define HB_A9_BCLK_DIV_SHIFT	1
     35#define HB_A9_PCLK_DIV		0x00000001
     36
     37struct hb_clk {
     38        struct clk_hw	hw;
     39	void __iomem	*reg;
     40	char *parent_name;
     41};
     42#define to_hb_clk(p) container_of(p, struct hb_clk, hw)
     43
     44static int clk_pll_prepare(struct clk_hw *hwclk)
     45	{
     46	struct hb_clk *hbclk = to_hb_clk(hwclk);
     47	u32 reg;
     48
     49	reg = readl(hbclk->reg);
     50	reg &= ~HB_PLL_RESET;
     51	writel(reg, hbclk->reg);
     52
     53	while ((readl(hbclk->reg) & HB_PLL_LOCK) == 0)
     54		;
     55	while ((readl(hbclk->reg) & HB_PLL_LOCK_500) == 0)
     56		;
     57
     58	return 0;
     59}
     60
     61static void clk_pll_unprepare(struct clk_hw *hwclk)
     62{
     63	struct hb_clk *hbclk = to_hb_clk(hwclk);
     64	u32 reg;
     65
     66	reg = readl(hbclk->reg);
     67	reg |= HB_PLL_RESET;
     68	writel(reg, hbclk->reg);
     69}
     70
     71static int clk_pll_enable(struct clk_hw *hwclk)
     72{
     73	struct hb_clk *hbclk = to_hb_clk(hwclk);
     74	u32 reg;
     75
     76	reg = readl(hbclk->reg);
     77	reg |= HB_PLL_EXT_ENA;
     78	writel(reg, hbclk->reg);
     79
     80	return 0;
     81}
     82
     83static void clk_pll_disable(struct clk_hw *hwclk)
     84{
     85	struct hb_clk *hbclk = to_hb_clk(hwclk);
     86	u32 reg;
     87
     88	reg = readl(hbclk->reg);
     89	reg &= ~HB_PLL_EXT_ENA;
     90	writel(reg, hbclk->reg);
     91}
     92
     93static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk,
     94					 unsigned long parent_rate)
     95{
     96	struct hb_clk *hbclk = to_hb_clk(hwclk);
     97	unsigned long divf, divq, vco_freq, reg;
     98
     99	reg = readl(hbclk->reg);
    100	if (reg & HB_PLL_EXT_BYPASS)
    101		return parent_rate;
    102
    103	divf = (reg & HB_PLL_DIVF_MASK) >> HB_PLL_DIVF_SHIFT;
    104	divq = (reg & HB_PLL_DIVQ_MASK) >> HB_PLL_DIVQ_SHIFT;
    105	vco_freq = parent_rate * (divf + 1);
    106
    107	return vco_freq / (1 << divq);
    108}
    109
    110static void clk_pll_calc(unsigned long rate, unsigned long ref_freq,
    111			u32 *pdivq, u32 *pdivf)
    112{
    113	u32 divq, divf;
    114	unsigned long vco_freq;
    115
    116	if (rate < HB_PLL_MIN_FREQ)
    117		rate = HB_PLL_MIN_FREQ;
    118	if (rate > HB_PLL_MAX_FREQ)
    119		rate = HB_PLL_MAX_FREQ;
    120
    121	for (divq = 1; divq <= 6; divq++) {
    122		if ((rate * (1 << divq)) >= HB_PLL_VCO_MIN_FREQ)
    123			break;
    124	}
    125
    126	vco_freq = rate * (1 << divq);
    127	divf = (vco_freq + (ref_freq / 2)) / ref_freq;
    128	divf--;
    129
    130	*pdivq = divq;
    131	*pdivf = divf;
    132}
    133
    134static long clk_pll_round_rate(struct clk_hw *hwclk, unsigned long rate,
    135			       unsigned long *parent_rate)
    136{
    137	u32 divq, divf;
    138	unsigned long ref_freq = *parent_rate;
    139
    140	clk_pll_calc(rate, ref_freq, &divq, &divf);
    141
    142	return (ref_freq * (divf + 1)) / (1 << divq);
    143}
    144
    145static int clk_pll_set_rate(struct clk_hw *hwclk, unsigned long rate,
    146			    unsigned long parent_rate)
    147{
    148	struct hb_clk *hbclk = to_hb_clk(hwclk);
    149	u32 divq, divf;
    150	u32 reg;
    151
    152	clk_pll_calc(rate, parent_rate, &divq, &divf);
    153
    154	reg = readl(hbclk->reg);
    155	if (divf != ((reg & HB_PLL_DIVF_MASK) >> HB_PLL_DIVF_SHIFT)) {
    156		/* Need to re-lock PLL, so put it into bypass mode */
    157		reg |= HB_PLL_EXT_BYPASS;
    158		writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg);
    159
    160		writel(reg | HB_PLL_RESET, hbclk->reg);
    161		reg &= ~(HB_PLL_DIVF_MASK | HB_PLL_DIVQ_MASK);
    162		reg |= (divf << HB_PLL_DIVF_SHIFT) | (divq << HB_PLL_DIVQ_SHIFT);
    163		writel(reg | HB_PLL_RESET, hbclk->reg);
    164		writel(reg, hbclk->reg);
    165
    166		while ((readl(hbclk->reg) & HB_PLL_LOCK) == 0)
    167			;
    168		while ((readl(hbclk->reg) & HB_PLL_LOCK_500) == 0)
    169			;
    170		reg |= HB_PLL_EXT_ENA;
    171		reg &= ~HB_PLL_EXT_BYPASS;
    172	} else {
    173		writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg);
    174		reg &= ~HB_PLL_DIVQ_MASK;
    175		reg |= divq << HB_PLL_DIVQ_SHIFT;
    176		writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg);
    177	}
    178	writel(reg, hbclk->reg);
    179
    180	return 0;
    181}
    182
    183static const struct clk_ops clk_pll_ops = {
    184	.prepare = clk_pll_prepare,
    185	.unprepare = clk_pll_unprepare,
    186	.enable = clk_pll_enable,
    187	.disable = clk_pll_disable,
    188	.recalc_rate = clk_pll_recalc_rate,
    189	.round_rate = clk_pll_round_rate,
    190	.set_rate = clk_pll_set_rate,
    191};
    192
    193static unsigned long clk_cpu_periphclk_recalc_rate(struct clk_hw *hwclk,
    194						   unsigned long parent_rate)
    195{
    196	struct hb_clk *hbclk = to_hb_clk(hwclk);
    197	u32 div = (readl(hbclk->reg) & HB_A9_PCLK_DIV) ? 8 : 4;
    198	return parent_rate / div;
    199}
    200
    201static const struct clk_ops a9periphclk_ops = {
    202	.recalc_rate = clk_cpu_periphclk_recalc_rate,
    203};
    204
    205static unsigned long clk_cpu_a9bclk_recalc_rate(struct clk_hw *hwclk,
    206						unsigned long parent_rate)
    207{
    208	struct hb_clk *hbclk = to_hb_clk(hwclk);
    209	u32 div = (readl(hbclk->reg) & HB_A9_BCLK_DIV_MASK) >> HB_A9_BCLK_DIV_SHIFT;
    210
    211	return parent_rate / (div + 2);
    212}
    213
    214static const struct clk_ops a9bclk_ops = {
    215	.recalc_rate = clk_cpu_a9bclk_recalc_rate,
    216};
    217
    218static unsigned long clk_periclk_recalc_rate(struct clk_hw *hwclk,
    219					     unsigned long parent_rate)
    220{
    221	struct hb_clk *hbclk = to_hb_clk(hwclk);
    222	u32 div;
    223
    224	div = readl(hbclk->reg) & 0x1f;
    225	div++;
    226	div *= 2;
    227
    228	return parent_rate / div;
    229}
    230
    231static long clk_periclk_round_rate(struct clk_hw *hwclk, unsigned long rate,
    232				   unsigned long *parent_rate)
    233{
    234	u32 div;
    235
    236	div = *parent_rate / rate;
    237	div++;
    238	div &= ~0x1;
    239
    240	return *parent_rate / div;
    241}
    242
    243static int clk_periclk_set_rate(struct clk_hw *hwclk, unsigned long rate,
    244				unsigned long parent_rate)
    245{
    246	struct hb_clk *hbclk = to_hb_clk(hwclk);
    247	u32 div;
    248
    249	div = parent_rate / rate;
    250	if (div & 0x1)
    251		return -EINVAL;
    252
    253	writel(div >> 1, hbclk->reg);
    254	return 0;
    255}
    256
    257static const struct clk_ops periclk_ops = {
    258	.recalc_rate = clk_periclk_recalc_rate,
    259	.round_rate = clk_periclk_round_rate,
    260	.set_rate = clk_periclk_set_rate,
    261};
    262
    263static void __init hb_clk_init(struct device_node *node, const struct clk_ops *ops, unsigned long clkflags)
    264{
    265	u32 reg;
    266	struct hb_clk *hb_clk;
    267	const char *clk_name = node->name;
    268	const char *parent_name;
    269	struct clk_init_data init;
    270	struct device_node *srnp;
    271	int rc;
    272
    273	rc = of_property_read_u32(node, "reg", &reg);
    274	if (WARN_ON(rc))
    275		return;
    276
    277	hb_clk = kzalloc(sizeof(*hb_clk), GFP_KERNEL);
    278	if (WARN_ON(!hb_clk))
    279		return;
    280
    281	/* Map system registers */
    282	srnp = of_find_compatible_node(NULL, NULL, "calxeda,hb-sregs");
    283	hb_clk->reg = of_iomap(srnp, 0);
    284	of_node_put(srnp);
    285	BUG_ON(!hb_clk->reg);
    286	hb_clk->reg += reg;
    287
    288	of_property_read_string(node, "clock-output-names", &clk_name);
    289
    290	init.name = clk_name;
    291	init.ops = ops;
    292	init.flags = clkflags;
    293	parent_name = of_clk_get_parent_name(node, 0);
    294	init.parent_names = &parent_name;
    295	init.num_parents = 1;
    296
    297	hb_clk->hw.init = &init;
    298
    299	rc = clk_hw_register(NULL, &hb_clk->hw);
    300	if (WARN_ON(rc)) {
    301		kfree(hb_clk);
    302		return;
    303	}
    304	of_clk_add_hw_provider(node, of_clk_hw_simple_get, &hb_clk->hw);
    305}
    306
    307static void __init hb_pll_init(struct device_node *node)
    308{
    309	hb_clk_init(node, &clk_pll_ops, 0);
    310}
    311CLK_OF_DECLARE(hb_pll, "calxeda,hb-pll-clock", hb_pll_init);
    312
    313static void __init hb_a9periph_init(struct device_node *node)
    314{
    315	hb_clk_init(node, &a9periphclk_ops, 0);
    316}
    317CLK_OF_DECLARE(hb_a9periph, "calxeda,hb-a9periph-clock", hb_a9periph_init);
    318
    319static void __init hb_a9bus_init(struct device_node *node)
    320{
    321	hb_clk_init(node, &a9bclk_ops, CLK_IS_CRITICAL);
    322}
    323CLK_OF_DECLARE(hb_a9bus, "calxeda,hb-a9bus-clock", hb_a9bus_init);
    324
    325static void __init hb_emmc_init(struct device_node *node)
    326{
    327	hb_clk_init(node, &periclk_ops, 0);
    328}
    329CLK_OF_DECLARE(hb_emmc, "calxeda,hb-emmc-clock", hb_emmc_init);