u-boot/drivers/clk/exynos/clk.c
Sam Protsenko f77780b0ee clk: exynos: Fix incorrect clock lookup for non-top CMUs
Samsung clock drivers usually define the clock indices that are unique
per one CMU, but are not unique across all CMUs. That is, clock indices
start from 1 for each CMU, as provided in CMU bindings header. The way
the clock lookup via clk_get_by_index() works at the moment is by using
clk_of_xlate_default(), which returns globally non-unique clock ids for
for clocks registered with Samsung CCF API, which leads to incorrect
clocks being obtained. One way to fix that would be to make all clock
ids defined in the bindings header unique, but it'd make it incompatible
with Linux kernel bindings header. A better way to solve this issue is
to calculate the global clock id and use it when registering a clock
with clk_dm() and when obtaining it, in a custom .of_xlate function.

This patch adds an API for such mapping calculation, introducing the
necessary modifications to CMU registering functions in Samsung CCF.
Exynos850 clock driver (the only driver that uses Samsung CCF at the
moment) is modified accordingly, as it uses the changed API. So the
clock lookup with clk-exynos850.c driver is also fixed here.

The global clock id is calculated from CMU id and local clock id in
SAMSUNG_TO_CLK_ID() macro like this:

    clk_id_global = cmu_id * 256 + clk_id_local

leaving a range of up to 256 clocks for each CMU. Then this mapping
macro is used in clk_dm() to register clocks using their global ids, and
in .of_xlate() to lookup the clock by its local id correctly. Because
.of_xlate() operation has a separate function for each CMU, it "knows"
the correct way of finding the correct clk_id_global by provided
clk_id_local.

Fixes: ff3e8b8c6c ("clk: exynos: Add Samsung clock framework")
Fixes: a36cc5e3ef ("clk: exynos: Add Exynos850 clock driver")
Signed-off-by: Sam Protsenko <semen.protsenko@linaro.org>
Signed-off-by: Minkyu Kang <mk7.kang@samsung.com>
2024-03-26 18:56:55 +09:00

131 lines
3.5 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2023 Linaro Ltd.
* Sam Protsenko <semen.protsenko@linaro.org>
*
* This file includes utility functions to register clocks to common
* clock framework for Samsung platforms.
*/
#include <dm.h>
#include "clk.h"
static void samsung_clk_register_mux(void __iomem *base, unsigned int cmu_id,
const struct samsung_mux_clock *clk_list,
unsigned int nr_clk)
{
unsigned int cnt;
for (cnt = 0; cnt < nr_clk; cnt++) {
struct clk *clk;
const struct samsung_mux_clock *m;
unsigned long clk_id;
m = &clk_list[cnt];
clk = clk_register_mux(NULL, m->name, m->parent_names,
m->num_parents, m->flags, base + m->offset, m->shift,
m->width, m->mux_flags);
clk_id = SAMSUNG_TO_CLK_ID(cmu_id, m->id);
clk_dm(clk_id, clk);
}
}
static void samsung_clk_register_div(void __iomem *base, unsigned int cmu_id,
const struct samsung_div_clock *clk_list,
unsigned int nr_clk)
{
unsigned int cnt;
for (cnt = 0; cnt < nr_clk; cnt++) {
struct clk *clk;
const struct samsung_div_clock *d;
unsigned long clk_id;
d = &clk_list[cnt];
clk = clk_register_divider(NULL, d->name, d->parent_name,
d->flags, base + d->offset, d->shift,
d->width, d->div_flags);
clk_id = SAMSUNG_TO_CLK_ID(cmu_id, d->id);
clk_dm(clk_id, clk);
}
}
static void samsung_clk_register_gate(void __iomem *base, unsigned int cmu_id,
const struct samsung_gate_clock *clk_list,
unsigned int nr_clk)
{
unsigned int cnt;
for (cnt = 0; cnt < nr_clk; cnt++) {
struct clk *clk;
const struct samsung_gate_clock *g;
unsigned long clk_id;
g = &clk_list[cnt];
clk = clk_register_gate(NULL, g->name, g->parent_name,
g->flags, base + g->offset, g->bit_idx,
g->gate_flags, NULL);
clk_id = SAMSUNG_TO_CLK_ID(cmu_id, g->id);
clk_dm(clk_id, clk);
}
}
typedef void (*samsung_clk_register_fn)(void __iomem *base, unsigned int cmu_id,
const void *clk_list,
unsigned int nr_clk);
static const samsung_clk_register_fn samsung_clk_register_fns[] = {
[S_CLK_MUX] = (samsung_clk_register_fn)samsung_clk_register_mux,
[S_CLK_DIV] = (samsung_clk_register_fn)samsung_clk_register_div,
[S_CLK_GATE] = (samsung_clk_register_fn)samsung_clk_register_gate,
[S_CLK_PLL] = (samsung_clk_register_fn)samsung_clk_register_pll,
};
/**
* samsung_cmu_register_clocks() - Register provided clock groups
* @base: Base address of CMU registers
* @cmu_id: CMU index number
* @clk_groups: list of clock groups
* @nr_groups: count of clock groups in @clk_groups
*
* Having the array of clock groups @clk_groups makes it possible to keep a
* correct clocks registration order.
*/
static void samsung_cmu_register_clocks(void __iomem *base, unsigned int cmu_id,
const struct samsung_clk_group *clk_groups,
unsigned int nr_groups)
{
unsigned int i;
for (i = 0; i < nr_groups; i++) {
const struct samsung_clk_group *g = &clk_groups[i];
samsung_clk_register_fns[g->type](base, cmu_id,
g->clk_list, g->nr_clk);
}
}
/**
* samsung_cmu_register_one - Register all CMU clocks
* @dev: CMU device
* @cmu_id: CMU index number
* @clk_groups: list of CMU clock groups
* @nr_groups: count of CMU clock groups in @clk_groups
*
* Return: 0 on success or negative value on error.
*/
int samsung_cmu_register_one(struct udevice *dev, unsigned int cmu_id,
const struct samsung_clk_group *clk_groups,
unsigned int nr_groups)
{
void __iomem *base;
base = dev_read_addr_ptr(dev);
if (!base)
return -EINVAL;
samsung_cmu_register_clocks(base, cmu_id, clk_groups, nr_groups);
return 0;
}