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-pmc-atom.c (9029B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Intel Atom platform clocks driver for BayTrail and CherryTrail SoCs
      4 *
      5 * Copyright (C) 2016, Intel Corporation
      6 * Author: Irina Tirdea <irina.tirdea@intel.com>
      7 */
      8
      9#include <linux/clk-provider.h>
     10#include <linux/clkdev.h>
     11#include <linux/err.h>
     12#include <linux/io.h>
     13#include <linux/platform_data/x86/clk-pmc-atom.h>
     14#include <linux/platform_device.h>
     15#include <linux/slab.h>
     16
     17#define PLT_CLK_NAME_BASE	"pmc_plt_clk"
     18
     19#define PMC_CLK_CTL_OFFSET		0x60
     20#define PMC_CLK_CTL_SIZE		4
     21#define PMC_CLK_NUM			6
     22#define PMC_CLK_CTL_GATED_ON_D3		0x0
     23#define PMC_CLK_CTL_FORCE_ON		0x1
     24#define PMC_CLK_CTL_FORCE_OFF		0x2
     25#define PMC_CLK_CTL_RESERVED		0x3
     26#define PMC_MASK_CLK_CTL		GENMASK(1, 0)
     27#define PMC_MASK_CLK_FREQ		BIT(2)
     28#define PMC_CLK_FREQ_XTAL		(0 << 2)	/* 25 MHz */
     29#define PMC_CLK_FREQ_PLL		(1 << 2)	/* 19.2 MHz */
     30
     31struct clk_plt_fixed {
     32	struct clk_hw *clk;
     33	struct clk_lookup *lookup;
     34};
     35
     36struct clk_plt {
     37	struct clk_hw hw;
     38	void __iomem *reg;
     39	struct clk_lookup *lookup;
     40	/* protect access to PMC registers */
     41	spinlock_t lock;
     42};
     43
     44#define to_clk_plt(_hw) container_of(_hw, struct clk_plt, hw)
     45
     46struct clk_plt_data {
     47	struct clk_plt_fixed **parents;
     48	u8 nparents;
     49	struct clk_plt *clks[PMC_CLK_NUM];
     50	struct clk_lookup *mclk_lookup;
     51	struct clk_lookup *ether_clk_lookup;
     52};
     53
     54/* Return an index in parent table */
     55static inline int plt_reg_to_parent(int reg)
     56{
     57	switch (reg & PMC_MASK_CLK_FREQ) {
     58	default:
     59	case PMC_CLK_FREQ_XTAL:
     60		return 0;
     61	case PMC_CLK_FREQ_PLL:
     62		return 1;
     63	}
     64}
     65
     66/* Return clk index of parent */
     67static inline int plt_parent_to_reg(int index)
     68{
     69	switch (index) {
     70	default:
     71	case 0:
     72		return PMC_CLK_FREQ_XTAL;
     73	case 1:
     74		return PMC_CLK_FREQ_PLL;
     75	}
     76}
     77
     78/* Abstract status in simpler enabled/disabled value */
     79static inline int plt_reg_to_enabled(int reg)
     80{
     81	switch (reg & PMC_MASK_CLK_CTL) {
     82	case PMC_CLK_CTL_GATED_ON_D3:
     83	case PMC_CLK_CTL_FORCE_ON:
     84		return 1;	/* enabled */
     85	case PMC_CLK_CTL_FORCE_OFF:
     86	case PMC_CLK_CTL_RESERVED:
     87	default:
     88		return 0;	/* disabled */
     89	}
     90}
     91
     92static void plt_clk_reg_update(struct clk_plt *clk, u32 mask, u32 val)
     93{
     94	u32 tmp;
     95	unsigned long flags;
     96
     97	spin_lock_irqsave(&clk->lock, flags);
     98
     99	tmp = readl(clk->reg);
    100	tmp = (tmp & ~mask) | (val & mask);
    101	writel(tmp, clk->reg);
    102
    103	spin_unlock_irqrestore(&clk->lock, flags);
    104}
    105
    106static int plt_clk_set_parent(struct clk_hw *hw, u8 index)
    107{
    108	struct clk_plt *clk = to_clk_plt(hw);
    109
    110	plt_clk_reg_update(clk, PMC_MASK_CLK_FREQ, plt_parent_to_reg(index));
    111
    112	return 0;
    113}
    114
    115static u8 plt_clk_get_parent(struct clk_hw *hw)
    116{
    117	struct clk_plt *clk = to_clk_plt(hw);
    118	u32 value;
    119
    120	value = readl(clk->reg);
    121
    122	return plt_reg_to_parent(value);
    123}
    124
    125static int plt_clk_enable(struct clk_hw *hw)
    126{
    127	struct clk_plt *clk = to_clk_plt(hw);
    128
    129	plt_clk_reg_update(clk, PMC_MASK_CLK_CTL, PMC_CLK_CTL_FORCE_ON);
    130
    131	return 0;
    132}
    133
    134static void plt_clk_disable(struct clk_hw *hw)
    135{
    136	struct clk_plt *clk = to_clk_plt(hw);
    137
    138	plt_clk_reg_update(clk, PMC_MASK_CLK_CTL, PMC_CLK_CTL_FORCE_OFF);
    139}
    140
    141static int plt_clk_is_enabled(struct clk_hw *hw)
    142{
    143	struct clk_plt *clk = to_clk_plt(hw);
    144	u32 value;
    145
    146	value = readl(clk->reg);
    147
    148	return plt_reg_to_enabled(value);
    149}
    150
    151static const struct clk_ops plt_clk_ops = {
    152	.enable = plt_clk_enable,
    153	.disable = plt_clk_disable,
    154	.is_enabled = plt_clk_is_enabled,
    155	.get_parent = plt_clk_get_parent,
    156	.set_parent = plt_clk_set_parent,
    157	.determine_rate = __clk_mux_determine_rate,
    158};
    159
    160static struct clk_plt *plt_clk_register(struct platform_device *pdev, int id,
    161					const struct pmc_clk_data *pmc_data,
    162					const char **parent_names,
    163					int num_parents)
    164{
    165	struct clk_plt *pclk;
    166	struct clk_init_data init;
    167	int ret;
    168
    169	pclk = devm_kzalloc(&pdev->dev, sizeof(*pclk), GFP_KERNEL);
    170	if (!pclk)
    171		return ERR_PTR(-ENOMEM);
    172
    173	init.name =  kasprintf(GFP_KERNEL, "%s_%d", PLT_CLK_NAME_BASE, id);
    174	init.ops = &plt_clk_ops;
    175	init.flags = 0;
    176	init.parent_names = parent_names;
    177	init.num_parents = num_parents;
    178
    179	pclk->hw.init = &init;
    180	pclk->reg = pmc_data->base + PMC_CLK_CTL_OFFSET + id * PMC_CLK_CTL_SIZE;
    181	spin_lock_init(&pclk->lock);
    182
    183	/*
    184	 * On some systems, the pmc_plt_clocks already enabled by the
    185	 * firmware are being marked as critical to avoid them being
    186	 * gated by the clock framework.
    187	 */
    188	if (pmc_data->critical && plt_clk_is_enabled(&pclk->hw))
    189		init.flags |= CLK_IS_CRITICAL;
    190
    191	ret = devm_clk_hw_register(&pdev->dev, &pclk->hw);
    192	if (ret) {
    193		pclk = ERR_PTR(ret);
    194		goto err_free_init;
    195	}
    196
    197	pclk->lookup = clkdev_hw_create(&pclk->hw, init.name, NULL);
    198	if (!pclk->lookup) {
    199		pclk = ERR_PTR(-ENOMEM);
    200		goto err_free_init;
    201	}
    202
    203err_free_init:
    204	kfree(init.name);
    205	return pclk;
    206}
    207
    208static void plt_clk_unregister(struct clk_plt *pclk)
    209{
    210	clkdev_drop(pclk->lookup);
    211}
    212
    213static struct clk_plt_fixed *plt_clk_register_fixed_rate(struct platform_device *pdev,
    214						 const char *name,
    215						 const char *parent_name,
    216						 unsigned long fixed_rate)
    217{
    218	struct clk_plt_fixed *pclk;
    219
    220	pclk = devm_kzalloc(&pdev->dev, sizeof(*pclk), GFP_KERNEL);
    221	if (!pclk)
    222		return ERR_PTR(-ENOMEM);
    223
    224	pclk->clk = clk_hw_register_fixed_rate(&pdev->dev, name, parent_name,
    225					       0, fixed_rate);
    226	if (IS_ERR(pclk->clk))
    227		return ERR_CAST(pclk->clk);
    228
    229	pclk->lookup = clkdev_hw_create(pclk->clk, name, NULL);
    230	if (!pclk->lookup) {
    231		clk_hw_unregister_fixed_rate(pclk->clk);
    232		return ERR_PTR(-ENOMEM);
    233	}
    234
    235	return pclk;
    236}
    237
    238static void plt_clk_unregister_fixed_rate(struct clk_plt_fixed *pclk)
    239{
    240	clkdev_drop(pclk->lookup);
    241	clk_hw_unregister_fixed_rate(pclk->clk);
    242}
    243
    244static void plt_clk_unregister_fixed_rate_loop(struct clk_plt_data *data,
    245					       unsigned int i)
    246{
    247	while (i--)
    248		plt_clk_unregister_fixed_rate(data->parents[i]);
    249}
    250
    251static void plt_clk_free_parent_names_loop(const char **parent_names,
    252					   unsigned int i)
    253{
    254	while (i--)
    255		kfree_const(parent_names[i]);
    256	kfree(parent_names);
    257}
    258
    259static void plt_clk_unregister_loop(struct clk_plt_data *data,
    260				    unsigned int i)
    261{
    262	while (i--)
    263		plt_clk_unregister(data->clks[i]);
    264}
    265
    266static const char **plt_clk_register_parents(struct platform_device *pdev,
    267					     struct clk_plt_data *data,
    268					     const struct pmc_clk *clks)
    269{
    270	const char **parent_names;
    271	unsigned int i;
    272	int err;
    273	int nparents = 0;
    274
    275	data->nparents = 0;
    276	while (clks[nparents].name)
    277		nparents++;
    278
    279	data->parents = devm_kcalloc(&pdev->dev, nparents,
    280				     sizeof(*data->parents), GFP_KERNEL);
    281	if (!data->parents)
    282		return ERR_PTR(-ENOMEM);
    283
    284	parent_names = kcalloc(nparents, sizeof(*parent_names),
    285			       GFP_KERNEL);
    286	if (!parent_names)
    287		return ERR_PTR(-ENOMEM);
    288
    289	for (i = 0; i < nparents; i++) {
    290		data->parents[i] =
    291			plt_clk_register_fixed_rate(pdev, clks[i].name,
    292						    clks[i].parent_name,
    293						    clks[i].freq);
    294		if (IS_ERR(data->parents[i])) {
    295			err = PTR_ERR(data->parents[i]);
    296			goto err_unreg;
    297		}
    298		parent_names[i] = kstrdup_const(clks[i].name, GFP_KERNEL);
    299	}
    300
    301	data->nparents = nparents;
    302	return parent_names;
    303
    304err_unreg:
    305	plt_clk_unregister_fixed_rate_loop(data, i);
    306	plt_clk_free_parent_names_loop(parent_names, i);
    307	return ERR_PTR(err);
    308}
    309
    310static void plt_clk_unregister_parents(struct clk_plt_data *data)
    311{
    312	plt_clk_unregister_fixed_rate_loop(data, data->nparents);
    313}
    314
    315static int plt_clk_probe(struct platform_device *pdev)
    316{
    317	const struct pmc_clk_data *pmc_data;
    318	const char **parent_names;
    319	struct clk_plt_data *data;
    320	unsigned int i;
    321	int err;
    322
    323	pmc_data = dev_get_platdata(&pdev->dev);
    324	if (!pmc_data || !pmc_data->clks)
    325		return -EINVAL;
    326
    327	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
    328	if (!data)
    329		return -ENOMEM;
    330
    331	parent_names = plt_clk_register_parents(pdev, data, pmc_data->clks);
    332	if (IS_ERR(parent_names))
    333		return PTR_ERR(parent_names);
    334
    335	for (i = 0; i < PMC_CLK_NUM; i++) {
    336		data->clks[i] = plt_clk_register(pdev, i, pmc_data,
    337						 parent_names, data->nparents);
    338		if (IS_ERR(data->clks[i])) {
    339			err = PTR_ERR(data->clks[i]);
    340			goto err_unreg_clk_plt;
    341		}
    342	}
    343	data->mclk_lookup = clkdev_hw_create(&data->clks[3]->hw, "mclk", NULL);
    344	if (!data->mclk_lookup) {
    345		err = -ENOMEM;
    346		goto err_unreg_clk_plt;
    347	}
    348
    349	data->ether_clk_lookup = clkdev_hw_create(&data->clks[4]->hw,
    350						  "ether_clk", NULL);
    351	if (!data->ether_clk_lookup) {
    352		err = -ENOMEM;
    353		goto err_drop_mclk;
    354	}
    355
    356	plt_clk_free_parent_names_loop(parent_names, data->nparents);
    357
    358	platform_set_drvdata(pdev, data);
    359	return 0;
    360
    361err_drop_mclk:
    362	clkdev_drop(data->mclk_lookup);
    363err_unreg_clk_plt:
    364	plt_clk_unregister_loop(data, i);
    365	plt_clk_unregister_parents(data);
    366	plt_clk_free_parent_names_loop(parent_names, data->nparents);
    367	return err;
    368}
    369
    370static int plt_clk_remove(struct platform_device *pdev)
    371{
    372	struct clk_plt_data *data;
    373
    374	data = platform_get_drvdata(pdev);
    375
    376	clkdev_drop(data->ether_clk_lookup);
    377	clkdev_drop(data->mclk_lookup);
    378	plt_clk_unregister_loop(data, PMC_CLK_NUM);
    379	plt_clk_unregister_parents(data);
    380	return 0;
    381}
    382
    383static struct platform_driver plt_clk_driver = {
    384	.driver = {
    385		.name = "clk-pmc-atom",
    386	},
    387	.probe = plt_clk_probe,
    388	.remove = plt_clk_remove,
    389};
    390builtin_platform_driver(plt_clk_driver);