in qcom/osm-l3.c [223:342]
static int qcom_osm_l3_probe(struct platform_device *pdev)
{
u32 info, src, lval, i, prev_freq = 0, freq;
static unsigned long hw_rate, xo_rate;
struct qcom_osm_l3_icc_provider *qp;
const struct qcom_osm_l3_desc *desc;
struct icc_onecell_data *data;
struct icc_provider *provider;
const struct qcom_osm_l3_node **qnodes;
struct icc_node *node;
size_t num_nodes;
struct clk *clk;
int ret;
clk = clk_get(&pdev->dev, "xo");
if (IS_ERR(clk))
return PTR_ERR(clk);
xo_rate = clk_get_rate(clk);
clk_put(clk);
clk = clk_get(&pdev->dev, "alternate");
if (IS_ERR(clk))
return PTR_ERR(clk);
hw_rate = clk_get_rate(clk) / CLK_HW_DIV;
clk_put(clk);
qp = devm_kzalloc(&pdev->dev, sizeof(*qp), GFP_KERNEL);
if (!qp)
return -ENOMEM;
qp->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(qp->base))
return PTR_ERR(qp->base);
/* HW should be in enabled state to proceed */
if (!(readl_relaxed(qp->base + REG_ENABLE) & 0x1)) {
dev_err(&pdev->dev, "error hardware not enabled\n");
return -ENODEV;
}
desc = device_get_match_data(&pdev->dev);
if (!desc)
return -EINVAL;
qp->reg_perf_state = desc->reg_perf_state;
for (i = 0; i < LUT_MAX_ENTRIES; i++) {
info = readl_relaxed(qp->base + desc->reg_freq_lut +
i * desc->lut_row_size);
src = FIELD_GET(LUT_SRC, info);
lval = FIELD_GET(LUT_L_VAL, info);
if (src)
freq = xo_rate * lval;
else
freq = hw_rate;
/* Two of the same frequencies signify end of table */
if (i > 0 && prev_freq == freq)
break;
dev_dbg(&pdev->dev, "index=%d freq=%d\n", i, freq);
qp->lut_tables[i] = freq;
prev_freq = freq;
}
qp->max_state = i;
qnodes = desc->nodes;
num_nodes = desc->num_nodes;
data = devm_kcalloc(&pdev->dev, num_nodes, sizeof(*node), GFP_KERNEL);
if (!data)
return -ENOMEM;
provider = &qp->provider;
provider->dev = &pdev->dev;
provider->set = qcom_osm_l3_set;
provider->aggregate = icc_std_aggregate;
provider->xlate = of_icc_xlate_onecell;
INIT_LIST_HEAD(&provider->nodes);
provider->data = data;
ret = icc_provider_add(provider);
if (ret) {
dev_err(&pdev->dev, "error adding interconnect provider\n");
return ret;
}
for (i = 0; i < num_nodes; i++) {
size_t j;
node = icc_node_create(qnodes[i]->id);
if (IS_ERR(node)) {
ret = PTR_ERR(node);
goto err;
}
node->name = qnodes[i]->name;
/* Cast away const and add it back in qcom_osm_l3_set() */
node->data = (void *)qnodes[i];
icc_node_add(node, provider);
for (j = 0; j < qnodes[i]->num_links; j++)
icc_link_create(node, qnodes[i]->links[j]);
data->nodes[i] = node;
}
data->num_nodes = num_nodes;
platform_set_drvdata(pdev, qp);
return 0;
err:
icc_nodes_remove(provider);
icc_provider_del(provider);
return ret;
}