// SPDX-License-Identifier: GPL-2.0/* * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. */ #include <linux/export.h>#include <linux/module.h>#include <linux/regmap.h>#include <linux/platform_device.h>#include <linux/clk-provider.h>#include <linux/interconnect-clk.h>#include <linux/pm_runtime.h>#include <linux/reset-controller.h>#include <linux/of.h> #include "common.h"#include "clk-alpha-pll.h"#include "clk-branch.h"#include "clk-rcg.h"#include "clk-regmap.h"#include "reset.h"#include "gdsc.h" struct qcom_cc { struct qcom_reset_controller reset; struct clk_regmap **rclks; size_t num_rclks; struct dev_pm_domain_list *pd_list;}; conststruct freq_tbl *qcom_find_freq(const struct freq_tbl *f, unsigned long rate){ if (!f) return NULL; if (!f->freq) return f; for (; f->freq; f++) if (rate <= f->freq) return f; /* Default to our fastest rate */ return f - 1;}EXPORT_SYMBOL_GPL(qcom_find_freq); const struct freq_multi_tbl *qcom_find_freq_multi(const struct freq_multi_tbl *f, unsigned long rate){ if (!f) return NULL; if (!f->freq) return f; for (; f->freq; f++) if (rate <= f->freq) return f; /* Default to our fastest rate */ return f - 1;}EXPORT_SYMBOL_GPL(qcom_find_freq_multi); const struct freq_tbl *qcom_find_freq_floor(const struct freq_tbl *f, unsigned long rate){ const struct freq_tbl *best = NULL; for ( ; f->freq; f++) { if (rate >= f->freq) best = f; else break; } return best;}EXPORT_SYMBOL_GPL(qcom_find_freq_floor); int qcom_find_src_index(struct clk_hw *hw, const struct parent_map *map, u8 src){ int i, num_parents = clk_hw_get_num_parents(hw); for (i = 0; i < num_parents; i++) if (src == map[i].src) return i; return -ENOENT;}EXPORT_SYMBOL_GPL(qcom_find_src_index); int qcom_find_cfg_index(struct clk_hw *hw, const struct parent_map *map, u8 cfg){ int i, num_parents = clk_hw_get_num_parents(hw); for (i = 0; i < num_parents; i++) if (cfg == map[i].cfg) return i; return -ENOENT;}EXPORT_SYMBOL_GPL(qcom_find_cfg_index); struct regmap *qcom_cc_map(struct platform_device *pdev, const struct qcom_cc_desc *desc){ void __iomem *base; struct device *dev = &pdev->dev; base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return ERR_CAST(base); return devm_regmap_init_mmio(dev, base, desc->config);}EXPORT_SYMBOL_GPL(qcom_cc_map); voidqcom_pll_set_fsm_mode(struct regmap *map, u32 reg, u8 bias_count, u8 lock_count){ u32 val; u32 mask; /* De-assert reset to FSM */ regmap_update_bits(map, reg, PLL_VOTE_FSM_RESET, 0); /* Program bias count and lock count */ val = bias_count << PLL_BIAS_COUNT_SHIFT | lock_count << PLL_LOCK_COUNT_SHIFT; mask = PLL_BIAS_COUNT_MASK << PLL_BIAS_COUNT_SHIFT; mask |= PLL_LOCK_COUNT_MASK << PLL_LOCK_COUNT_SHIFT; regmap_update_bits(map, reg, mask, val); /* Enable PLL FSM voting */ regmap_update_bits(map, reg, PLL_VOTE_FSM_ENA, PLL_VOTE_FSM_ENA);}EXPORT_SYMBOL_GPL(qcom_pll_set_fsm_mode); static void qcom_cc_gdsc_unregister(void *data){ gdsc_unregister(data);} /* * Backwards compatibility with old DTs. Register a pass-through factor 1/1 * clock to translate 'path' clk into 'name' clk and register the 'path' * clk as a fixed rate clock if it isn't present. */static int _qcom_cc_register_board_clk(struct device *dev, const char *path, const char *name, unsigned long rate, bool add_factor){ struct device_node *node = NULL; struct device_node *clocks_node; struct clk_fixed_factor *factor; struct clk_fixed_rate *fixed; struct clk_init_data init_data = { }; int ret; clocks_node = of_find_node_by_path("/clocks"); if (clocks_node) { node = of_get_child_by_name(clocks_node, path); of_node_put(clocks_node); } if (!node) { fixed = devm_kzalloc(dev, sizeof(*fixed), GFP_KERNEL); if (!fixed) return -ENOMEM; fixed->fixed_rate = rate; fixed->hw.init = &init_data; init_data.name = path; init_data.ops = &clk_fixed_rate_ops; ret = devm_clk_hw_register(dev, &fixed->hw); if (ret) return ret; } of_node_put(node); if (add_factor) { factor = devm_kzalloc(dev, sizeof(*factor), GFP_KERNEL); if (!factor) return -ENOMEM; factor->mult = factor->div = 1; factor->hw.init = &init_data; init_data.name = name; init_data.parent_names = &path; init_data.num_parents = 1; init_data.flags = 0; init_data.ops = &clk_fixed_factor_ops; ret = devm_clk_hw_register(dev, &factor->hw); if (ret) return ret; } return 0;} int qcom_cc_register_board_clk(struct device *dev, const char *path, const char *name, unsigned long rate){ bool add_factor = true; /* * TODO: The RPM clock driver currently does not support the xo clock. * When xo is added to the RPM clock driver, we should change this * function to skip registration of xo factor clocks. */ return _qcom_cc_register_board_clk(dev, path, name, rate, add_factor);}EXPORT_SYMBOL_GPL(qcom_cc_register_board_clk); int qcom_cc_register_sleep_clk(struct device *dev){ return _qcom_cc_register_board_clk(dev, "sleep_clk", "sleep_clk_src", 32768, true);}EXPORT_SYMBOL_GPL(qcom_cc_register_sleep_clk); /* Drop 'protected-clocks' from the list of clocks to register */static void qcom_cc_drop_protected(struct device *dev, struct qcom_cc *cc){ struct device_node *np = dev->of_node; u32 i; of_property_for_each_u32(np, "protected-clocks", i) { if (i >= cc->num_rclks) continue; cc->rclks[i] = NULL; }} static struct clk_hw *qcom_cc_clk_hw_get(struct of_phandle_args *clkspec, void *data){ struct qcom_cc *cc = data; unsigned int idx = clkspec->args[0]; if (idx >= cc->num_rclks) { pr_err("%s: invalid index %u\n", __func__, idx); return ERR_PTR(-EINVAL); } return cc->rclks[idx] ? &cc->rclks[idx]->hw : NULL;} static int qcom_cc_icc_register(struct device *dev, const struct qcom_cc_desc *desc){ struct icc_clk_data *icd; struct clk_hw *hws; int i; if (!IS_ENABLED(CONFIG_INTERCONNECT_CLK)) return 0; if (!desc->icc_hws) return 0; icd = devm_kcalloc(dev, desc->num_icc_hws, sizeof(*icd), GFP_KERNEL); if (!icd) return -ENOMEM; for (i = 0; i < desc->num_icc_hws; i++) { icd[i].master_id = desc->icc_hws[i].master_id; icd[i].slave_id = desc->icc_hws[i].slave_id; hws = &desc->clks[desc->icc_hws[i].clk_id]->hw; icd[i].clk = devm_clk_hw_get_clk(dev, hws, "icc"); if (IS_ERR(icd[i].clk)) return dev_err_probe(dev, PTR_ERR(icd[i].clk), "(%d) clock entry is null\n", i); icd[i].name = clk_hw_get_name(hws); } return devm_icc_clk_register(dev, desc->icc_first_node_id, desc->num_icc_hws, icd);} static int qcom_cc_clk_pll_configure(const struct qcom_cc_driver_data *data, struct regmap *regmap){ const struct clk_init_data *init; struct clk_alpha_pll *pll; int i; for (i = 0; i < data->num_alpha_plls; i++) { pll = data->alpha_plls[i]; init = pll->clkr.hw.init; if (!pll->config || !pll->regs) { pr_err("%s: missing pll config or regs\n", init->name); return -EINVAL; } qcom_clk_alpha_pll_configure(pll, regmap); } return 0;} static void qcom_cc_clk_regs_configure(struct device *dev, const struct qcom_cc_driver_data *data, struct regmap *regmap){ int i; for (i = 0; i < data->num_clk_cbcrs; i++) qcom_branch_set_clk_en(regmap, data->clk_cbcrs[i]); if (data->clk_regs_configure) data->clk_regs_configure(dev, regmap);} int qcom_cc_really_probe(struct device *dev, const struct qcom_cc_desc *desc, struct regmap *regmap){ int i, ret; struct qcom_reset_controller *reset; struct qcom_cc *cc; struct gdsc_desc *scd; size_t num_clks = desc->num_clks; struct clk_regmap **rclks = desc->clks; size_t num_clk_hws = desc->num_clk_hws; struct clk_hw **clk_hws = desc->clk_hws; cc = devm_kzalloc(dev, sizeof(*cc), GFP_KERNEL); if (!cc) return -ENOMEM; ret = devm_pm_domain_attach_list(dev, NULL, &cc->pd_list); if (ret < 0 && ret != -EEXIST) return ret; if (desc->use_rpm) { ret = devm_pm_runtime_enable(dev); if (ret) return ret; ret = pm_runtime_resume_and_get(dev); if (ret) return ret; } if (desc->driver_data) { ret = qcom_cc_clk_pll_configure(desc->driver_data, regmap); if (ret) goto put_rpm; qcom_cc_clk_regs_configure(dev, desc->driver_data, regmap); } 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; ret = devm_reset_controller_register(dev, &reset->rcdev); if (ret) goto put_rpm; if (desc->gdscs && desc->num_gdscs) { scd = devm_kzalloc(dev, sizeof(*scd), GFP_KERNEL); if (!scd) { ret = -ENOMEM; goto put_rpm; } scd->dev = dev; scd->scs = desc->gdscs; scd->num = desc->num_gdscs; scd->pd_list = cc->pd_list; ret = gdsc_register(scd, &reset->rcdev, regmap); if (ret) goto put_rpm; ret = devm_add_action_or_reset(dev, qcom_cc_gdsc_unregister, scd); if (ret) goto put_rpm; } if (desc->driver_data && desc->driver_data->dfs_rcgs && desc->driver_data->num_dfs_rcgs) { ret = qcom_cc_register_rcg_dfs(regmap, desc->driver_data->dfs_rcgs, desc->driver_data->num_dfs_rcgs); if (ret) goto put_rpm; } cc->rclks = rclks; cc->num_rclks = num_clks; qcom_cc_drop_protected(dev, cc); for (i = 0; i < num_clk_hws; i++) { ret = devm_clk_hw_register(dev, clk_hws[i]); if (ret) goto put_rpm; } for (i = 0; i < num_clks; i++) { if (!rclks[i]) continue; ret = devm_clk_register_regmap(dev, rclks[i]); if (ret) goto put_rpm; } ret = devm_of_clk_add_hw_provider(dev, qcom_cc_clk_hw_get, cc); if (ret) goto put_rpm; ret = qcom_cc_icc_register(dev, desc); put_rpm: if (desc->use_rpm) pm_runtime_put_sync(dev); 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->dev, desc, regmap);}EXPORT_SYMBOL_GPL(qcom_cc_probe); int qcom_cc_probe_by_index(struct platform_device *pdev, int index, const struct qcom_cc_desc *desc){ struct regmap *regmap; void __iomem *base; base = devm_platform_ioremap_resource(pdev, index); if (IS_ERR(base)) return PTR_ERR(base); regmap = devm_regmap_init_mmio(&pdev->dev, base, desc->config); if (IS_ERR(regmap)) return PTR_ERR(regmap); return qcom_cc_really_probe(&pdev->dev, desc, regmap);}EXPORT_SYMBOL_GPL(qcom_cc_probe_by_index); MODULE_LICENSE("GPL v2");MODULE_DESCRIPTION("QTI Common Clock module");
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
*/
#include <linux/export.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/platform_device.h>
#include <linux/clk-provider.h>
#include <linux/interconnect-clk.h>
#include <linux/pm_runtime.h>
#include <linux/reset-controller.h>
#include <linux/of.h>
#include "common.h"
#include "clk-alpha-pll.h"
#include "clk-branch.h"
#include "clk-rcg.h"
#include "clk-regmap.h"
#include "reset.h"
#include "gdsc.h"
struct qcom_cc {
struct qcom_reset_controller reset;
struct clk_regmap **rclks;
size_t num_rclks;
struct dev_pm_domain_list *pd_list;
};
const
struct freq_tbl *qcom_find_freq(const struct freq_tbl *f, unsigned long rate)
{
if (!f)
return NULL;
if (!f->freq)
return f;
for (; f->freq; f++)
if (rate <= f->freq)
return f;
/* Default to our fastest rate */
return f - 1;
}
EXPORT_SYMBOL_GPL(qcom_find_freq);
const struct freq_multi_tbl *qcom_find_freq_multi(const struct freq_multi_tbl *f,
unsigned long rate)
{
if (!f)
return NULL;
if (!f->freq)
return f;
for (; f->freq; f++)
if (rate <= f->freq)
return f;
/* Default to our fastest rate */
return f - 1;
}
EXPORT_SYMBOL_GPL(qcom_find_freq_multi);
const struct freq_tbl *qcom_find_freq_floor(const struct freq_tbl *f,
unsigned long rate)
{
const struct freq_tbl *best = NULL;
for ( ; f->freq; f++) {
if (rate >= f->freq)
best = f;
else
break;
}
return best;
}
EXPORT_SYMBOL_GPL(qcom_find_freq_floor);
int qcom_find_src_index(struct clk_hw *hw, const struct parent_map *map, u8 src)
{
int i, num_parents = clk_hw_get_num_parents(hw);
for (i = 0; i < num_parents; i++)
if (src == map[i].src)
return i;
return -ENOENT;
}
EXPORT_SYMBOL_GPL(qcom_find_src_index);
int qcom_find_cfg_index(struct clk_hw *hw, const struct parent_map *map, u8 cfg)
{
int i, num_parents = clk_hw_get_num_parents(hw);
for (i = 0; i < num_parents; i++)
if (cfg == map[i].cfg)
return i;
return -ENOENT;
}
EXPORT_SYMBOL_GPL(qcom_find_cfg_index);
struct regmap *
qcom_cc_map(struct platform_device *pdev, const struct qcom_cc_desc *desc)
{
void __iomem *base;
struct device *dev = &pdev->dev;
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return ERR_CAST(base);
return devm_regmap_init_mmio(dev, base, desc->config);
}
EXPORT_SYMBOL_GPL(qcom_cc_map);
void
qcom_pll_set_fsm_mode(struct regmap *map, u32 reg, u8 bias_count, u8 lock_count)
{
u32 val;
u32 mask;
/* De-assert reset to FSM */
regmap_update_bits(map, reg, PLL_VOTE_FSM_RESET, 0);
/* Program bias count and lock count */
val = bias_count << PLL_BIAS_COUNT_SHIFT |
lock_count << PLL_LOCK_COUNT_SHIFT;
mask = PLL_BIAS_COUNT_MASK << PLL_BIAS_COUNT_SHIFT;
mask |= PLL_LOCK_COUNT_MASK << PLL_LOCK_COUNT_SHIFT;
regmap_update_bits(map, reg, mask, val);
/* Enable PLL FSM voting */
regmap_update_bits(map, reg, PLL_VOTE_FSM_ENA, PLL_VOTE_FSM_ENA);
}
EXPORT_SYMBOL_GPL(qcom_pll_set_fsm_mode);
static void qcom_cc_gdsc_unregister(void *data)
{
gdsc_unregister(data);
}
/*
* Backwards compatibility with old DTs. Register a pass-through factor 1/1
* clock to translate 'path' clk into 'name' clk and register the 'path'
* clk as a fixed rate clock if it isn't present.
*/
static int _qcom_cc_register_board_clk(struct device *dev, const char *path,
const char *name, unsigned long rate,
bool add_factor)
{
struct device_node *node = NULL;
struct device_node *clocks_node;
struct clk_fixed_factor *factor;
struct clk_fixed_rate *fixed;
struct clk_init_data init_data = { };
int ret;
clocks_node = of_find_node_by_path("/clocks");
if (clocks_node) {
node = of_get_child_by_name(clocks_node, path);
of_node_put(clocks_node);
}
if (!node) {
fixed = devm_kzalloc(dev, sizeof(*fixed), GFP_KERNEL);
if (!fixed)
return -ENOMEM;
fixed->fixed_rate = rate;
fixed->hw.init = &init_data;
init_data.name = path;
init_data.ops = &clk_fixed_rate_ops;
ret = devm_clk_hw_register(dev, &fixed->hw);
if (ret)
return ret;
}
of_node_put(node);
if (add_factor) {
factor = devm_kzalloc(dev, sizeof(*factor), GFP_KERNEL);
if (!factor)
return -ENOMEM;
factor->mult = factor->div = 1;
factor->hw.init = &init_data;
init_data.name = name;
init_data.parent_names = &path;
init_data.num_parents = 1;
init_data.flags = 0;
init_data.ops = &clk_fixed_factor_ops;
ret = devm_clk_hw_register(dev, &factor->hw);
if (ret)
return ret;
}
return 0;
}
int qcom_cc_register_board_clk(struct device *dev, const char *path,
const char *name, unsigned long rate)
{
bool add_factor = true;
/*
* TODO: The RPM clock driver currently does not support the xo clock.
* When xo is added to the RPM clock driver, we should change this
* function to skip registration of xo factor clocks.
*/
return _qcom_cc_register_board_clk(dev, path, name, rate, add_factor);
}
EXPORT_SYMBOL_GPL(qcom_cc_register_board_clk);
int qcom_cc_register_sleep_clk(struct device *dev)
{
return _qcom_cc_register_board_clk(dev, "sleep_clk", "sleep_clk_src",
32768, true);
}
EXPORT_SYMBOL_GPL(qcom_cc_register_sleep_clk);
/* Drop 'protected-clocks' from the list of clocks to register */
static void qcom_cc_drop_protected(struct device *dev, struct qcom_cc *cc)
{
struct device_node *np = dev->of_node;
u32 i;
of_property_for_each_u32(np, "protected-clocks", i) {
if (i >= cc->num_rclks)
continue;
cc->rclks[i] = NULL;
}
}
static struct clk_hw *qcom_cc_clk_hw_get(struct of_phandle_args *clkspec,
void *data)
{
struct qcom_cc *cc = data;
unsigned int idx = clkspec->args[0];
if (idx >= cc->num_rclks) {
pr_err("%s: invalid index %u\n", __func__, idx);
return ERR_PTR(-EINVAL);
}
return cc->rclks[idx] ? &cc->rclks[idx]->hw : NULL;
}
static int qcom_cc_icc_register(struct device *dev,
const struct qcom_cc_desc *desc)
{
struct icc_clk_data *icd;
struct clk_hw *hws;
int i;
if (!IS_ENABLED(CONFIG_INTERCONNECT_CLK))
return 0;
if (!desc->icc_hws)
return 0;
icd = devm_kcalloc(dev, desc->num_icc_hws, sizeof(*icd), GFP_KERNEL);
if (!icd)
return -ENOMEM;
for (i = 0; i < desc->num_icc_hws; i++) {
icd[i].master_id = desc->icc_hws[i].master_id;
icd[i].slave_id = desc->icc_hws[i].slave_id;
hws = &desc->clks[desc->icc_hws[i].clk_id]->hw;
icd[i].clk = devm_clk_hw_get_clk(dev, hws, "icc");
if (IS_ERR(icd[i].clk))
return dev_err_probe(dev, PTR_ERR(icd[i].clk),
"(%d) clock entry is null\n", i);
icd[i].name = clk_hw_get_name(hws);
}
return devm_icc_clk_register(dev, desc->icc_first_node_id,
desc->num_icc_hws, icd);
}
static int qcom_cc_clk_pll_configure(const struct qcom_cc_driver_data *data,
struct regmap *regmap)
{
const struct clk_init_data *init;
struct clk_alpha_pll *pll;
int i;
for (i = 0; i < data->num_alpha_plls; i++) {
pll = data->alpha_plls[i];
init = pll->clkr.hw.init;
if (!pll->config || !pll->regs) {
pr_err("%s: missing pll config or regs\n", init->name);
return -EINVAL;
}
qcom_clk_alpha_pll_configure(pll, regmap);
}
return 0;
}
static void qcom_cc_clk_regs_configure(struct device *dev, const struct qcom_cc_driver_data *data,
struct regmap *regmap)
{
int i;
for (i = 0; i < data->num_clk_cbcrs; i++)
qcom_branch_set_clk_en(regmap, data->clk_cbcrs[i]);
if (data->clk_regs_configure)
data->clk_regs_configure(dev, regmap);
}
int qcom_cc_really_probe(struct device *dev,
const struct qcom_cc_desc *desc, struct regmap *regmap)
{
int i, ret;
struct qcom_reset_controller *reset;
struct qcom_cc *cc;
struct gdsc_desc *scd;
size_t num_clks = desc->num_clks;
struct clk_regmap **rclks = desc->clks;
size_t num_clk_hws = desc->num_clk_hws;
struct clk_hw **clk_hws = desc->clk_hws;
cc = devm_kzalloc(dev, sizeof(*cc), GFP_KERNEL);
if (!cc)
return -ENOMEM;
ret = devm_pm_domain_attach_list(dev, NULL, &cc->pd_list);
if (ret < 0 && ret != -EEXIST)
return ret;
if (desc->use_rpm) {
ret = devm_pm_runtime_enable(dev);
if (ret)
return ret;
ret = pm_runtime_resume_and_get(dev);
if (ret)
return ret;
}
if (desc->driver_data) {
ret = qcom_cc_clk_pll_configure(desc->driver_data, regmap);
if (ret)
goto put_rpm;
qcom_cc_clk_regs_configure(dev, desc->driver_data, regmap);
}
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;
ret = devm_reset_controller_register(dev, &reset->rcdev);
if (ret)
goto put_rpm;
if (desc->gdscs && desc->num_gdscs) {
scd = devm_kzalloc(dev, sizeof(*scd), GFP_KERNEL);
if (!scd) {
ret = -ENOMEM;
goto put_rpm;
}
scd->dev = dev;
scd->scs = desc->gdscs;
scd->num = desc->num_gdscs;
scd->pd_list = cc->pd_list;
ret = gdsc_register(scd, &reset->rcdev, regmap);
if (ret)
goto put_rpm;
ret = devm_add_action_or_reset(dev, qcom_cc_gdsc_unregister,
scd);
if (ret)
goto put_rpm;
}
if (desc->driver_data &&
desc->driver_data->dfs_rcgs &&
desc->driver_data->num_dfs_rcgs) {
ret = qcom_cc_register_rcg_dfs(regmap,
desc->driver_data->dfs_rcgs,
desc->driver_data->num_dfs_rcgs);
if (ret)
goto put_rpm;
}
cc->rclks = rclks;
cc->num_rclks = num_clks;
qcom_cc_drop_protected(dev, cc);
for (i = 0; i < num_clk_hws; i++) {
ret = devm_clk_hw_register(dev, clk_hws[i]);
if (ret)
goto put_rpm;
}
for (i = 0; i < num_clks; i++) {
if (!rclks[i])
continue;
ret = devm_clk_register_regmap(dev, rclks[i]);
if (ret)
goto put_rpm;
}
ret = devm_of_clk_add_hw_provider(dev, qcom_cc_clk_hw_get, cc);
if (ret)
goto put_rpm;
ret = qcom_cc_icc_register(dev, desc);
put_rpm:
if (desc->use_rpm)
pm_runtime_put_sync(dev);
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->dev, desc, regmap);
}
EXPORT_SYMBOL_GPL(qcom_cc_probe);
int qcom_cc_probe_by_index(struct platform_device *pdev, int index,
const struct qcom_cc_desc *desc)
{
struct regmap *regmap;
void __iomem *base;
base = devm_platform_ioremap_resource(pdev, index);
if (IS_ERR(base))
return PTR_ERR(base);
regmap = devm_regmap_init_mmio(&pdev->dev, base, desc->config);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
return qcom_cc_really_probe(&pdev->dev, desc, regmap);
}
EXPORT_SYMBOL_GPL(qcom_cc_probe_by_index);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("QTI Common Clock module");