u-boot/drivers/clk/imx/clk-composite-8m.c
Tom Rini d678a59d2d Revert "Merge patch series "arm: dts: am62-beagleplay: Fix Beagleplay Ethernet""
When bringing in the series 'arm: dts: am62-beagleplay: Fix Beagleplay
Ethernet"' I failed to notice that b4 noticed it was based on next and
so took that as the base commit and merged that part of next to master.

This reverts commit c8ffd1356d, reversing
changes made to 2ee6f3a5f7.

Reported-by: Jonas Karlman <jonas@kwiboo.se>
Signed-off-by: Tom Rini <trini@konsulko.com>
2024-05-19 08:16:36 -06:00

174 lines
4.2 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2019 NXP
*/
#include <common.h>
#include <log.h>
#include <asm/io.h>
#include <malloc.h>
#include <clk-uclass.h>
#include <dm/device.h>
#include <dm/devres.h>
#include <linux/clk-provider.h>
#include <clk.h>
#include "clk.h"
#include <linux/err.h>
#define UBOOT_DM_CLK_IMX_COMPOSITE "imx_clk_composite"
#define PCG_PREDIV_SHIFT 16
#define PCG_PREDIV_WIDTH 3
#define PCG_PREDIV_MAX 8
#define PCG_DIV_SHIFT 0
#define PCG_DIV_WIDTH 6
#define PCG_DIV_MAX 64
#define PCG_PCS_SHIFT 24
#define PCG_PCS_MASK 0x7
#define PCG_CGC_SHIFT 28
static unsigned long imx8m_clk_composite_divider_recalc_rate(struct clk *clk)
{
struct clk_divider *divider = (struct clk_divider *)to_clk_divider(clk);
struct clk_composite *composite = (struct clk_composite *)clk->data;
ulong parent_rate = clk_get_parent_rate(&composite->clk);
unsigned long prediv_rate;
unsigned int prediv_value;
unsigned int div_value;
debug("%s: name %s prate: %lu reg: %p\n", __func__,
(&composite->clk)->dev->name, parent_rate, divider->reg);
prediv_value = readl(divider->reg) >> divider->shift;
prediv_value &= clk_div_mask(divider->width);
prediv_rate = divider_recalc_rate(clk, parent_rate, prediv_value,
NULL, divider->flags,
divider->width);
div_value = readl(divider->reg) >> PCG_DIV_SHIFT;
div_value &= clk_div_mask(PCG_DIV_WIDTH);
return divider_recalc_rate(clk, prediv_rate, div_value, NULL,
divider->flags, PCG_DIV_WIDTH);
}
static int imx8m_clk_composite_compute_dividers(unsigned long rate,
unsigned long parent_rate,
int *prediv, int *postdiv)
{
int div1, div2;
int error = INT_MAX;
int ret = -EINVAL;
*prediv = 1;
*postdiv = 1;
for (div1 = 1; div1 <= PCG_PREDIV_MAX; div1++) {
for (div2 = 1; div2 <= PCG_DIV_MAX; div2++) {
int new_error = ((parent_rate / div1) / div2) - rate;
if (abs(new_error) < abs(error)) {
*prediv = div1;
*postdiv = div2;
error = new_error;
ret = 0;
}
}
}
return ret;
}
/*
* The clk are bound to a dev, because it is part of composite clk
* use composite clk to get dev
*/
static ulong imx8m_clk_composite_divider_set_rate(struct clk *clk,
unsigned long rate)
{
struct clk_divider *divider = (struct clk_divider *)to_clk_divider(clk);
struct clk_composite *composite = (struct clk_composite *)clk->data;
ulong parent_rate = clk_get_parent_rate(&composite->clk);
int prediv_value;
int div_value;
int ret;
u32 val;
ret = imx8m_clk_composite_compute_dividers(rate, parent_rate,
&prediv_value, &div_value);
if (ret)
return ret;
val = readl(divider->reg);
val &= ~((clk_div_mask(divider->width) << divider->shift) |
(clk_div_mask(PCG_DIV_WIDTH) << PCG_DIV_SHIFT));
val |= (u32)(prediv_value - 1) << divider->shift;
val |= (u32)(div_value - 1) << PCG_DIV_SHIFT;
writel(val, divider->reg);
return clk_get_rate(&composite->clk);
}
static const struct clk_ops imx8m_clk_composite_divider_ops = {
.get_rate = imx8m_clk_composite_divider_recalc_rate,
.set_rate = imx8m_clk_composite_divider_set_rate,
};
struct clk *imx8m_clk_composite_flags(const char *name,
const char * const *parent_names,
int num_parents, void __iomem *reg,
unsigned long flags)
{
struct clk *clk = ERR_PTR(-ENOMEM);
struct clk_divider *div = NULL;
struct clk_gate *gate = NULL;
struct clk_mux *mux = NULL;
mux = kzalloc(sizeof(*mux), GFP_KERNEL);
if (!mux)
goto fail;
mux->reg = reg;
mux->shift = PCG_PCS_SHIFT;
mux->mask = PCG_PCS_MASK;
mux->num_parents = num_parents;
mux->flags = flags;
mux->parent_names = parent_names;
div = kzalloc(sizeof(*div), GFP_KERNEL);
if (!div)
goto fail;
div->reg = reg;
div->shift = PCG_PREDIV_SHIFT;
div->width = PCG_PREDIV_WIDTH;
div->flags = CLK_DIVIDER_ROUND_CLOSEST | flags;
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
if (!gate)
goto fail;
gate->reg = reg;
gate->bit_idx = PCG_CGC_SHIFT;
gate->flags = flags;
clk = clk_register_composite(NULL, name,
parent_names, num_parents,
&mux->clk, &clk_mux_ops, &div->clk,
&imx8m_clk_composite_divider_ops,
&gate->clk, &clk_gate_ops, flags);
if (IS_ERR(clk))
goto fail;
return clk;
fail:
kfree(gate);
kfree(div);
kfree(mux);
return ERR_CAST(clk);
}