mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-15 08:14:15 +08:00
340d79a14d
The DT of_device.h and of_platform.h date back to the separate of_platform_bus_type before it was merged into the regular platform bus. As part of that merge prepping Arm DT support 13 years ago, they "temporarily" include each other. They also include platform_device.h and of.h. As a result, there's a pretty much random mix of those include files used throughout the tree. In order to detangle these headers and replace the implicit includes with struct declarations, users need to explicitly include the correct includes. Acked-by: Jernej Skrabec <jernej.skrabec@gmail.com> Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> Acked-by: Charles Keepax <ckeepax@opensource.cirrus.com> Reviewed-by: Claudiu Beznea <claudiu.beznea@tuxon.dev> # for at91 Signed-off-by: Rob Herring <robh@kernel.org> Link: https://lore.kernel.org/r/20231006-dt-asoc-header-cleanups-v3-1-13a4f0f7fee6@kernel.org Signed-off-by: Mark Brown <broonie@kernel.org>
186 lines
4.2 KiB
C
186 lines
4.2 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
// Copyright (c) 2020, Linaro Limited
|
|
|
|
#include <linux/err.h>
|
|
#include <linux/init.h>
|
|
#include <linux/clk-provider.h>
|
|
#include <linux/module.h>
|
|
#include <linux/device.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/of.h>
|
|
#include <linux/slab.h>
|
|
#include <dt-bindings/sound/qcom,q6dsp-lpass-ports.h>
|
|
#include "q6dsp-lpass-clocks.h"
|
|
|
|
#define Q6DSP_MAX_CLK_ID 104
|
|
#define Q6DSP_LPASS_CLK_ROOT_DEFAULT 0
|
|
|
|
|
|
struct q6dsp_clk {
|
|
struct device *dev;
|
|
int q6dsp_clk_id;
|
|
int attributes;
|
|
int rate;
|
|
uint32_t handle;
|
|
struct clk_hw hw;
|
|
};
|
|
|
|
#define to_q6dsp_clk(_hw) container_of(_hw, struct q6dsp_clk, hw)
|
|
|
|
struct q6dsp_cc {
|
|
struct device *dev;
|
|
struct q6dsp_clk *clks[Q6DSP_MAX_CLK_ID];
|
|
const struct q6dsp_clk_desc *desc;
|
|
};
|
|
|
|
static int clk_q6dsp_prepare(struct clk_hw *hw)
|
|
{
|
|
struct q6dsp_clk *clk = to_q6dsp_clk(hw);
|
|
struct q6dsp_cc *cc = dev_get_drvdata(clk->dev);
|
|
|
|
return cc->desc->lpass_set_clk(clk->dev, clk->q6dsp_clk_id, clk->attributes,
|
|
Q6DSP_LPASS_CLK_ROOT_DEFAULT, clk->rate);
|
|
}
|
|
|
|
static void clk_q6dsp_unprepare(struct clk_hw *hw)
|
|
{
|
|
struct q6dsp_clk *clk = to_q6dsp_clk(hw);
|
|
struct q6dsp_cc *cc = dev_get_drvdata(clk->dev);
|
|
|
|
cc->desc->lpass_set_clk(clk->dev, clk->q6dsp_clk_id, clk->attributes,
|
|
Q6DSP_LPASS_CLK_ROOT_DEFAULT, 0);
|
|
}
|
|
|
|
static int clk_q6dsp_set_rate(struct clk_hw *hw, unsigned long rate,
|
|
unsigned long parent_rate)
|
|
{
|
|
struct q6dsp_clk *clk = to_q6dsp_clk(hw);
|
|
|
|
clk->rate = rate;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static unsigned long clk_q6dsp_recalc_rate(struct clk_hw *hw,
|
|
unsigned long parent_rate)
|
|
{
|
|
struct q6dsp_clk *clk = to_q6dsp_clk(hw);
|
|
|
|
return clk->rate;
|
|
}
|
|
|
|
static long clk_q6dsp_round_rate(struct clk_hw *hw, unsigned long rate,
|
|
unsigned long *parent_rate)
|
|
{
|
|
return rate;
|
|
}
|
|
|
|
static const struct clk_ops clk_q6dsp_ops = {
|
|
.prepare = clk_q6dsp_prepare,
|
|
.unprepare = clk_q6dsp_unprepare,
|
|
.set_rate = clk_q6dsp_set_rate,
|
|
.round_rate = clk_q6dsp_round_rate,
|
|
.recalc_rate = clk_q6dsp_recalc_rate,
|
|
};
|
|
|
|
static int clk_vote_q6dsp_block(struct clk_hw *hw)
|
|
{
|
|
struct q6dsp_clk *clk = to_q6dsp_clk(hw);
|
|
struct q6dsp_cc *cc = dev_get_drvdata(clk->dev);
|
|
|
|
return cc->desc->lpass_vote_clk(clk->dev, clk->q6dsp_clk_id,
|
|
clk_hw_get_name(&clk->hw), &clk->handle);
|
|
}
|
|
|
|
static void clk_unvote_q6dsp_block(struct clk_hw *hw)
|
|
{
|
|
struct q6dsp_clk *clk = to_q6dsp_clk(hw);
|
|
struct q6dsp_cc *cc = dev_get_drvdata(clk->dev);
|
|
|
|
cc->desc->lpass_unvote_clk(clk->dev, clk->q6dsp_clk_id, clk->handle);
|
|
}
|
|
|
|
static const struct clk_ops clk_vote_q6dsp_ops = {
|
|
.prepare = clk_vote_q6dsp_block,
|
|
.unprepare = clk_unvote_q6dsp_block,
|
|
};
|
|
|
|
|
|
static struct clk_hw *q6dsp_of_clk_hw_get(struct of_phandle_args *clkspec,
|
|
void *data)
|
|
{
|
|
struct q6dsp_cc *cc = data;
|
|
unsigned int idx = clkspec->args[0];
|
|
unsigned int attr = clkspec->args[1];
|
|
|
|
if (idx >= Q6DSP_MAX_CLK_ID || attr > LPASS_CLK_ATTRIBUTE_COUPLE_DIVISOR) {
|
|
dev_err(cc->dev, "Invalid clk specifier (%d, %d)\n", idx, attr);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
if (cc->clks[idx]) {
|
|
cc->clks[idx]->attributes = attr;
|
|
return &cc->clks[idx]->hw;
|
|
}
|
|
|
|
return ERR_PTR(-ENOENT);
|
|
}
|
|
|
|
int q6dsp_clock_dev_probe(struct platform_device *pdev)
|
|
{
|
|
struct q6dsp_cc *cc;
|
|
struct device *dev = &pdev->dev;
|
|
const struct q6dsp_clk_init *q6dsp_clks;
|
|
const struct q6dsp_clk_desc *desc;
|
|
int i, ret;
|
|
|
|
cc = devm_kzalloc(dev, sizeof(*cc), GFP_KERNEL);
|
|
if (!cc)
|
|
return -ENOMEM;
|
|
|
|
desc = of_device_get_match_data(&pdev->dev);
|
|
if (!desc)
|
|
return -EINVAL;
|
|
|
|
cc->desc = desc;
|
|
cc->dev = dev;
|
|
q6dsp_clks = desc->clks;
|
|
|
|
for (i = 0; i < desc->num_clks; i++) {
|
|
unsigned int id = q6dsp_clks[i].clk_id;
|
|
struct clk_init_data init = {
|
|
.name = q6dsp_clks[i].name,
|
|
};
|
|
struct q6dsp_clk *clk;
|
|
|
|
clk = devm_kzalloc(dev, sizeof(*clk), GFP_KERNEL);
|
|
if (!clk)
|
|
return -ENOMEM;
|
|
|
|
clk->dev = dev;
|
|
clk->q6dsp_clk_id = q6dsp_clks[i].q6dsp_clk_id;
|
|
clk->rate = q6dsp_clks[i].rate;
|
|
clk->hw.init = &init;
|
|
|
|
if (clk->rate)
|
|
init.ops = &clk_q6dsp_ops;
|
|
else
|
|
init.ops = &clk_vote_q6dsp_ops;
|
|
|
|
cc->clks[id] = clk;
|
|
|
|
ret = devm_clk_hw_register(dev, &clk->hw);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
ret = devm_of_clk_add_hw_provider(dev, q6dsp_of_clk_hw_get, cc);
|
|
if (ret)
|
|
return ret;
|
|
|
|
dev_set_drvdata(dev, cc);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(q6dsp_clock_dev_probe);
|