forked from Minki/linux
293d2e97b3
In the current parent mapping code, we can get duplicate or inconsistent indexes, which leads to discrepancy between the number of elements in the array and the number of parents. Until now, this was solved with some reordering but this is not always possible. This patch introduces index tables that are used to define the relations between the PLL source and the hardware mux configuration value. To accomplish this, here we do the following: - Define a parent_map struct to map the relations between PLL source index and register configuration value. - Add a qcom_find_src_index() function for finding the index of a clock matching the specific PLL configuration. - Update the {set,get}_parent RCG functions use the newly introduced parent_map struct. - Convert all existing drivers to the new parent_map tables. Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org> Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
147 lines
3.5 KiB
C
147 lines
3.5 KiB
C
/*
|
|
* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
|
|
*
|
|
* This software is licensed under the terms of the GNU General Public
|
|
* License version 2, as published by the Free Software Foundation, and
|
|
* may be copied, distributed, and modified under those terms.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#include <linux/export.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/clk-provider.h>
|
|
#include <linux/reset-controller.h>
|
|
|
|
#include "common.h"
|
|
#include "clk-rcg.h"
|
|
#include "clk-regmap.h"
|
|
#include "reset.h"
|
|
|
|
struct qcom_cc {
|
|
struct qcom_reset_controller reset;
|
|
struct clk_onecell_data data;
|
|
struct clk *clks[];
|
|
};
|
|
|
|
const
|
|
struct freq_tbl *qcom_find_freq(const struct freq_tbl *f, unsigned long rate)
|
|
{
|
|
if (!f)
|
|
return NULL;
|
|
|
|
for (; f->freq; f++)
|
|
if (rate <= f->freq)
|
|
return f;
|
|
|
|
/* Default to our fastest rate */
|
|
return f - 1;
|
|
}
|
|
EXPORT_SYMBOL_GPL(qcom_find_freq);
|
|
|
|
int qcom_find_src_index(struct clk_hw *hw, const struct parent_map *map, u8 src)
|
|
{
|
|
int i, num_parents = __clk_get_num_parents(hw->clk);
|
|
|
|
for (i = 0; i < num_parents; i++)
|
|
if (src == map[i].src)
|
|
return i;
|
|
|
|
return -ENOENT;
|
|
}
|
|
EXPORT_SYMBOL_GPL(qcom_find_src_index);
|
|
|
|
struct regmap *
|
|
qcom_cc_map(struct platform_device *pdev, const struct qcom_cc_desc *desc)
|
|
{
|
|
void __iomem *base;
|
|
struct resource *res;
|
|
struct device *dev = &pdev->dev;
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
base = devm_ioremap_resource(dev, res);
|
|
if (IS_ERR(base))
|
|
return ERR_CAST(base);
|
|
|
|
return devm_regmap_init_mmio(dev, base, desc->config);
|
|
}
|
|
EXPORT_SYMBOL_GPL(qcom_cc_map);
|
|
|
|
int qcom_cc_really_probe(struct platform_device *pdev,
|
|
const struct qcom_cc_desc *desc, struct regmap *regmap)
|
|
{
|
|
int i, ret;
|
|
struct device *dev = &pdev->dev;
|
|
struct clk *clk;
|
|
struct clk_onecell_data *data;
|
|
struct clk **clks;
|
|
struct qcom_reset_controller *reset;
|
|
struct qcom_cc *cc;
|
|
size_t num_clks = desc->num_clks;
|
|
struct clk_regmap **rclks = desc->clks;
|
|
|
|
cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks,
|
|
GFP_KERNEL);
|
|
if (!cc)
|
|
return -ENOMEM;
|
|
|
|
clks = cc->clks;
|
|
data = &cc->data;
|
|
data->clks = clks;
|
|
data->clk_num = num_clks;
|
|
|
|
for (i = 0; i < num_clks; i++) {
|
|
if (!rclks[i]) {
|
|
clks[i] = ERR_PTR(-ENOENT);
|
|
continue;
|
|
}
|
|
clk = devm_clk_register_regmap(dev, rclks[i]);
|
|
if (IS_ERR(clk))
|
|
return PTR_ERR(clk);
|
|
clks[i] = clk;
|
|
}
|
|
|
|
ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data);
|
|
if (ret)
|
|
return ret;
|
|
|
|
reset = &cc->reset;
|
|
reset->rcdev.of_node = dev->of_node;
|
|
reset->rcdev.ops = &qcom_reset_ops;
|
|
reset->rcdev.owner = dev->driver->owner;
|
|
reset->rcdev.nr_resets = desc->num_resets;
|
|
reset->regmap = regmap;
|
|
reset->reset_map = desc->resets;
|
|
platform_set_drvdata(pdev, &reset->rcdev);
|
|
|
|
ret = reset_controller_register(&reset->rcdev);
|
|
if (ret)
|
|
of_clk_del_provider(dev->of_node);
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(qcom_cc_really_probe);
|
|
|
|
int qcom_cc_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc)
|
|
{
|
|
struct regmap *regmap;
|
|
|
|
regmap = qcom_cc_map(pdev, desc);
|
|
if (IS_ERR(regmap))
|
|
return PTR_ERR(regmap);
|
|
|
|
return qcom_cc_really_probe(pdev, desc, regmap);
|
|
}
|
|
EXPORT_SYMBOL_GPL(qcom_cc_probe);
|
|
|
|
void qcom_cc_remove(struct platform_device *pdev)
|
|
{
|
|
of_clk_del_provider(pdev->dev.of_node);
|
|
reset_controller_unregister(platform_get_drvdata(pdev));
|
|
}
|
|
EXPORT_SYMBOL_GPL(qcom_cc_remove);
|