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

armada-37xx-tbg.c (3739B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Marvell Armada 37xx SoC Time Base Generator clocks
      4 *
      5 * Copyright (C) 2016 Marvell
      6 *
      7 * Gregory CLEMENT <gregory.clement@free-electrons.com>
      8 */
      9
     10#include <linux/clk-provider.h>
     11#include <linux/clk.h>
     12#include <linux/io.h>
     13#include <linux/of.h>
     14#include <linux/of_address.h>
     15#include <linux/platform_device.h>
     16#include <linux/slab.h>
     17
     18#define NUM_TBG	    4
     19
     20#define TBG_CTRL0		0x4
     21#define TBG_CTRL1		0x8
     22#define TBG_CTRL7		0x20
     23#define TBG_CTRL8		0x30
     24
     25#define TBG_DIV_MASK		0x1FF
     26
     27#define TBG_A_REFDIV		0
     28#define TBG_B_REFDIV		16
     29
     30#define TBG_A_FBDIV		2
     31#define TBG_B_FBDIV		18
     32
     33#define TBG_A_VCODIV_SE		0
     34#define TBG_B_VCODIV_SE		16
     35
     36#define TBG_A_VCODIV_DIFF	1
     37#define TBG_B_VCODIV_DIFF	17
     38
     39struct tbg_def {
     40	char *name;
     41	u32 refdiv_offset;
     42	u32 fbdiv_offset;
     43	u32 vcodiv_reg;
     44	u32 vcodiv_offset;
     45};
     46
     47static const struct tbg_def tbg[NUM_TBG] = {
     48	{"TBG-A-P", TBG_A_REFDIV, TBG_A_FBDIV, TBG_CTRL8, TBG_A_VCODIV_DIFF},
     49	{"TBG-B-P", TBG_B_REFDIV, TBG_B_FBDIV, TBG_CTRL8, TBG_B_VCODIV_DIFF},
     50	{"TBG-A-S", TBG_A_REFDIV, TBG_A_FBDIV, TBG_CTRL1, TBG_A_VCODIV_SE},
     51	{"TBG-B-S", TBG_B_REFDIV, TBG_B_FBDIV, TBG_CTRL1, TBG_B_VCODIV_SE},
     52};
     53
     54static unsigned int tbg_get_mult(void __iomem *reg, const struct tbg_def *ptbg)
     55{
     56	u32 val;
     57
     58	val = readl(reg + TBG_CTRL0);
     59
     60	return ((val >> ptbg->fbdiv_offset) & TBG_DIV_MASK) << 2;
     61}
     62
     63static unsigned int tbg_get_div(void __iomem *reg, const struct tbg_def *ptbg)
     64{
     65	u32 val;
     66	unsigned int div;
     67
     68	val = readl(reg + TBG_CTRL7);
     69
     70	div = (val >> ptbg->refdiv_offset) & TBG_DIV_MASK;
     71	if (div == 0)
     72		div = 1;
     73	val = readl(reg + ptbg->vcodiv_reg);
     74
     75	div *= 1 << ((val >>  ptbg->vcodiv_offset) & TBG_DIV_MASK);
     76
     77	return div;
     78}
     79
     80
     81static int armada_3700_tbg_clock_probe(struct platform_device *pdev)
     82{
     83	struct device_node *np = pdev->dev.of_node;
     84	struct clk_hw_onecell_data *hw_tbg_data;
     85	struct device *dev = &pdev->dev;
     86	const char *parent_name;
     87	struct resource *res;
     88	struct clk *parent;
     89	void __iomem *reg;
     90	int i, ret;
     91
     92	hw_tbg_data = devm_kzalloc(&pdev->dev,
     93				   struct_size(hw_tbg_data, hws, NUM_TBG),
     94				   GFP_KERNEL);
     95	if (!hw_tbg_data)
     96		return -ENOMEM;
     97	hw_tbg_data->num = NUM_TBG;
     98	platform_set_drvdata(pdev, hw_tbg_data);
     99
    100	parent = clk_get(dev, NULL);
    101	if (IS_ERR(parent)) {
    102		dev_err(dev, "Could get the clock parent\n");
    103		return -EINVAL;
    104	}
    105	parent_name = __clk_get_name(parent);
    106	clk_put(parent);
    107
    108	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    109	reg = devm_ioremap_resource(dev, res);
    110	if (IS_ERR(reg))
    111		return PTR_ERR(reg);
    112
    113	for (i = 0; i < NUM_TBG; i++) {
    114		const char *name;
    115		unsigned int mult, div;
    116
    117		name = tbg[i].name;
    118		mult = tbg_get_mult(reg, &tbg[i]);
    119		div = tbg_get_div(reg, &tbg[i]);
    120		hw_tbg_data->hws[i] = clk_hw_register_fixed_factor(NULL, name,
    121						parent_name, 0, mult, div);
    122		if (IS_ERR(hw_tbg_data->hws[i]))
    123			dev_err(dev, "Can't register TBG clock %s\n", name);
    124	}
    125
    126	ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, hw_tbg_data);
    127
    128	return ret;
    129}
    130
    131static int armada_3700_tbg_clock_remove(struct platform_device *pdev)
    132{
    133	int i;
    134	struct clk_hw_onecell_data *hw_tbg_data = platform_get_drvdata(pdev);
    135
    136	of_clk_del_provider(pdev->dev.of_node);
    137	for (i = 0; i < hw_tbg_data->num; i++)
    138		clk_hw_unregister_fixed_factor(hw_tbg_data->hws[i]);
    139
    140	return 0;
    141}
    142
    143static const struct of_device_id armada_3700_tbg_clock_of_match[] = {
    144	{ .compatible = "marvell,armada-3700-tbg-clock", },
    145	{ }
    146};
    147
    148static struct platform_driver armada_3700_tbg_clock_driver = {
    149	.probe = armada_3700_tbg_clock_probe,
    150	.remove = armada_3700_tbg_clock_remove,
    151	.driver		= {
    152		.name	= "marvell-armada-3700-tbg-clock",
    153		.of_match_table = armada_3700_tbg_clock_of_match,
    154	},
    155};
    156
    157builtin_platform_driver(armada_3700_tbg_clock_driver);