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

q6dsp-lpass-clocks.c (4316B)


      1// SPDX-License-Identifier: GPL-2.0
      2// Copyright (c) 2020, Linaro Limited
      3
      4#include <linux/err.h>
      5#include <linux/init.h>
      6#include <linux/clk-provider.h>
      7#include <linux/module.h>
      8#include <linux/device.h>
      9#include <linux/platform_device.h>
     10#include <linux/of.h>
     11#include <linux/of_device.h>
     12#include <linux/slab.h>
     13#include <dt-bindings/sound/qcom,q6dsp-lpass-ports.h>
     14#include "q6dsp-lpass-clocks.h"
     15
     16#define Q6DSP_MAX_CLK_ID			104
     17#define Q6DSP_LPASS_CLK_ROOT_DEFAULT		0
     18
     19
     20struct q6dsp_clk {
     21	struct device *dev;
     22	int q6dsp_clk_id;
     23	int attributes;
     24	int rate;
     25	uint32_t handle;
     26	struct clk_hw hw;
     27};
     28
     29#define to_q6dsp_clk(_hw) container_of(_hw, struct q6dsp_clk, hw)
     30
     31struct q6dsp_cc {
     32	struct device *dev;
     33	struct q6dsp_clk *clks[Q6DSP_MAX_CLK_ID];
     34	const struct q6dsp_clk_desc *desc;
     35};
     36
     37static int clk_q6dsp_prepare(struct clk_hw *hw)
     38{
     39	struct q6dsp_clk *clk = to_q6dsp_clk(hw);
     40	struct q6dsp_cc *cc = dev_get_drvdata(clk->dev);
     41
     42	return cc->desc->lpass_set_clk(clk->dev, clk->q6dsp_clk_id, clk->attributes,
     43				     Q6DSP_LPASS_CLK_ROOT_DEFAULT, clk->rate);
     44}
     45
     46static void clk_q6dsp_unprepare(struct clk_hw *hw)
     47{
     48	struct q6dsp_clk *clk = to_q6dsp_clk(hw);
     49	struct q6dsp_cc *cc = dev_get_drvdata(clk->dev);
     50
     51	cc->desc->lpass_set_clk(clk->dev, clk->q6dsp_clk_id, clk->attributes,
     52			      Q6DSP_LPASS_CLK_ROOT_DEFAULT, 0);
     53}
     54
     55static int clk_q6dsp_set_rate(struct clk_hw *hw, unsigned long rate,
     56			      unsigned long parent_rate)
     57{
     58	struct q6dsp_clk *clk = to_q6dsp_clk(hw);
     59
     60	clk->rate = rate;
     61
     62	return 0;
     63}
     64
     65static unsigned long clk_q6dsp_recalc_rate(struct clk_hw *hw,
     66					   unsigned long parent_rate)
     67{
     68	struct q6dsp_clk *clk = to_q6dsp_clk(hw);
     69
     70	return clk->rate;
     71}
     72
     73static long clk_q6dsp_round_rate(struct clk_hw *hw, unsigned long rate,
     74				 unsigned long *parent_rate)
     75{
     76	return rate;
     77}
     78
     79static const struct clk_ops clk_q6dsp_ops = {
     80	.prepare	= clk_q6dsp_prepare,
     81	.unprepare	= clk_q6dsp_unprepare,
     82	.set_rate	= clk_q6dsp_set_rate,
     83	.round_rate	= clk_q6dsp_round_rate,
     84	.recalc_rate	= clk_q6dsp_recalc_rate,
     85};
     86
     87static int clk_vote_q6dsp_block(struct clk_hw *hw)
     88{
     89	struct q6dsp_clk *clk = to_q6dsp_clk(hw);
     90	struct q6dsp_cc *cc = dev_get_drvdata(clk->dev);
     91
     92	return cc->desc->lpass_vote_clk(clk->dev, clk->q6dsp_clk_id,
     93				  clk_hw_get_name(&clk->hw), &clk->handle);
     94}
     95
     96static void clk_unvote_q6dsp_block(struct clk_hw *hw)
     97{
     98	struct q6dsp_clk *clk = to_q6dsp_clk(hw);
     99	struct q6dsp_cc *cc = dev_get_drvdata(clk->dev);
    100
    101	cc->desc->lpass_unvote_clk(clk->dev, clk->q6dsp_clk_id, clk->handle);
    102}
    103
    104static const struct clk_ops clk_vote_q6dsp_ops = {
    105	.prepare	= clk_vote_q6dsp_block,
    106	.unprepare	= clk_unvote_q6dsp_block,
    107};
    108
    109
    110static struct clk_hw *q6dsp_of_clk_hw_get(struct of_phandle_args *clkspec,
    111					  void *data)
    112{
    113	struct q6dsp_cc *cc = data;
    114	unsigned int idx = clkspec->args[0];
    115	unsigned int attr = clkspec->args[1];
    116
    117	if (idx >= Q6DSP_MAX_CLK_ID || attr > LPASS_CLK_ATTRIBUTE_COUPLE_DIVISOR) {
    118		dev_err(cc->dev, "Invalid clk specifier (%d, %d)\n", idx, attr);
    119		return ERR_PTR(-EINVAL);
    120	}
    121
    122	if (cc->clks[idx]) {
    123		cc->clks[idx]->attributes = attr;
    124		return &cc->clks[idx]->hw;
    125	}
    126
    127	return ERR_PTR(-ENOENT);
    128}
    129
    130int q6dsp_clock_dev_probe(struct platform_device *pdev)
    131{
    132	struct q6dsp_cc *cc;
    133	struct device *dev = &pdev->dev;
    134	const struct q6dsp_clk_init *q6dsp_clks;
    135	const struct q6dsp_clk_desc *desc;
    136	int i, ret;
    137
    138	cc = devm_kzalloc(dev, sizeof(*cc), GFP_KERNEL);
    139	if (!cc)
    140		return -ENOMEM;
    141
    142	desc = of_device_get_match_data(&pdev->dev);
    143	if (!desc)
    144		return -EINVAL;
    145
    146	cc->desc = desc;
    147	cc->dev = dev;
    148	q6dsp_clks = desc->clks;
    149
    150	for (i = 0; i < desc->num_clks; i++) {
    151		unsigned int id = q6dsp_clks[i].clk_id;
    152		struct clk_init_data init = {
    153			.name =  q6dsp_clks[i].name,
    154		};
    155		struct q6dsp_clk *clk;
    156
    157		clk = devm_kzalloc(dev, sizeof(*clk), GFP_KERNEL);
    158		if (!clk)
    159			return -ENOMEM;
    160
    161		clk->dev = dev;
    162		clk->q6dsp_clk_id = q6dsp_clks[i].q6dsp_clk_id;
    163		clk->rate = q6dsp_clks[i].rate;
    164		clk->hw.init = &init;
    165
    166		if (clk->rate)
    167			init.ops = &clk_q6dsp_ops;
    168		else
    169			init.ops = &clk_vote_q6dsp_ops;
    170
    171		cc->clks[id] = clk;
    172
    173		ret = devm_clk_hw_register(dev, &clk->hw);
    174		if (ret)
    175			return ret;
    176	}
    177
    178	ret = devm_of_clk_add_hw_provider(dev, q6dsp_of_clk_hw_get, cc);
    179	if (ret)
    180		return ret;
    181
    182	dev_set_drvdata(dev, cc);
    183
    184	return 0;
    185}
    186EXPORT_SYMBOL_GPL(q6dsp_clock_dev_probe);