Files
linux/drivers/clk/mediatek/clk-pll.c
Linus Torvalds bf4afc53b7 Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
This was done entirely with mindless brute force, using

    git grep -l '\<k[vmz]*alloc_objs*(.*, GFP_KERNEL)' |
        xargs sed -i 's/\(alloc_objs*(.*\), GFP_KERNEL)/\1)/'

to convert the new alloc_obj() users that had a simple GFP_KERNEL
argument to just drop that argument.

Note that due to the extreme simplicity of the scripting, any slightly
more complex cases spread over multiple lines would not be triggered:
they definitely exist, but this covers the vast bulk of the cases, and
the resulting diff is also then easier to check automatically.

For the same reason the 'flex' versions will be done as a separate
conversion.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2026-02-21 17:09:51 -08:00

504 lines
11 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2014 MediaTek Inc.
* Author: James Liao <jamesjj.liao@mediatek.com>
*/
#include <linux/clk-provider.h>
#include <linux/container_of.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include "clk-pll.h"
#define MHZ (1000 * 1000)
#define REG_CON0 0
#define REG_CON1 4
#define CON0_BASE_EN BIT(0)
#define CON0_PWR_ON BIT(0)
#define CON0_ISO_EN BIT(1)
#define PCW_CHG_BIT 31
#define AUDPLL_TUNER_EN BIT(31)
/* default 7 bits integer, can be overridden with pcwibits. */
#define INTEGER_BITS 7
int mtk_pll_is_prepared(struct clk_hw *hw)
{
struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
return (readl(pll->en_addr) & BIT(pll->data->pll_en_bit)) != 0;
}
static int mtk_pll_fenc_is_prepared(struct clk_hw *hw)
{
struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
return !!(readl(pll->fenc_addr) & BIT(pll->data->fenc_sta_bit));
}
static unsigned long __mtk_pll_recalc_rate(struct mtk_clk_pll *pll, u32 fin,
u32 pcw, int postdiv)
{
int pcwbits = pll->data->pcwbits;
int pcwfbits = 0;
int ibits;
u64 vco;
u8 c = 0;
/* The fractional part of the PLL divider. */
ibits = pll->data->pcwibits ? pll->data->pcwibits : INTEGER_BITS;
if (pcwbits > ibits)
pcwfbits = pcwbits - ibits;
vco = (u64)fin * pcw;
if (pcwfbits && (vco & GENMASK(pcwfbits - 1, 0)))
c = 1;
vco >>= pcwfbits;
if (c)
vco++;
return ((unsigned long)vco + postdiv - 1) / postdiv;
}
static void __mtk_pll_tuner_enable(struct mtk_clk_pll *pll)
{
u32 r;
if (pll->tuner_en_addr) {
r = readl(pll->tuner_en_addr) | BIT(pll->data->tuner_en_bit);
writel(r, pll->tuner_en_addr);
} else if (pll->tuner_addr) {
r = readl(pll->tuner_addr) | AUDPLL_TUNER_EN;
writel(r, pll->tuner_addr);
}
}
static void __mtk_pll_tuner_disable(struct mtk_clk_pll *pll)
{
u32 r;
if (pll->tuner_en_addr) {
r = readl(pll->tuner_en_addr) & ~BIT(pll->data->tuner_en_bit);
writel(r, pll->tuner_en_addr);
} else if (pll->tuner_addr) {
r = readl(pll->tuner_addr) & ~AUDPLL_TUNER_EN;
writel(r, pll->tuner_addr);
}
}
static void mtk_pll_set_rate_regs(struct mtk_clk_pll *pll, u32 pcw,
int postdiv)
{
u32 chg, val;
/* disable tuner */
__mtk_pll_tuner_disable(pll);
/* set postdiv */
val = readl(pll->pd_addr);
val &= ~(POSTDIV_MASK << pll->data->pd_shift);
val |= (ffs(postdiv) - 1) << pll->data->pd_shift;
/* postdiv and pcw need to set at the same time if on same register */
if (pll->pd_addr != pll->pcw_addr) {
writel(val, pll->pd_addr);
val = readl(pll->pcw_addr);
}
/* set pcw */
val &= ~GENMASK(pll->data->pcw_shift + pll->data->pcwbits - 1,
pll->data->pcw_shift);
val |= pcw << pll->data->pcw_shift;
writel(val, pll->pcw_addr);
chg = readl(pll->pcw_chg_addr) |
BIT(pll->data->pcw_chg_bit ? : PCW_CHG_BIT);
writel(chg, pll->pcw_chg_addr);
if (pll->tuner_addr)
writel(val + 1, pll->tuner_addr);
/* restore tuner_en */
__mtk_pll_tuner_enable(pll);
udelay(20);
}
/*
* mtk_pll_calc_values - calculate good values for a given input frequency.
* @pll: The pll
* @pcw: The pcw value (output)
* @postdiv: The post divider (output)
* @freq: The desired target frequency
* @fin: The input frequency
*
*/
void mtk_pll_calc_values(struct mtk_clk_pll *pll, u32 *pcw, u32 *postdiv,
u32 freq, u32 fin)
{
unsigned long fmin = pll->data->fmin ? pll->data->fmin : (1000 * MHZ);
const struct mtk_pll_div_table *div_table = pll->data->div_table;
u64 _pcw;
int ibits;
u32 val;
if (freq > pll->data->fmax)
freq = pll->data->fmax;
if (div_table) {
if (freq > div_table[0].freq)
freq = div_table[0].freq;
for (val = 0; div_table[val + 1].freq != 0; val++) {
if (freq > div_table[val + 1].freq)
break;
}
*postdiv = 1 << val;
} else {
for (val = 0; val < 5; val++) {
*postdiv = 1 << val;
if ((u64)freq * *postdiv >= fmin)
break;
}
}
/* _pcw = freq * postdiv / fin * 2^pcwfbits */
ibits = pll->data->pcwibits ? pll->data->pcwibits : INTEGER_BITS;
_pcw = ((u64)freq << val) << (pll->data->pcwbits - ibits);
do_div(_pcw, fin);
*pcw = (u32)_pcw;
}
int mtk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
u32 pcw = 0;
u32 postdiv;
mtk_pll_calc_values(pll, &pcw, &postdiv, rate, parent_rate);
mtk_pll_set_rate_regs(pll, pcw, postdiv);
return 0;
}
unsigned long mtk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
u32 postdiv;
u32 pcw;
postdiv = (readl(pll->pd_addr) >> pll->data->pd_shift) & POSTDIV_MASK;
postdiv = 1 << postdiv;
pcw = readl(pll->pcw_addr) >> pll->data->pcw_shift;
pcw &= GENMASK(pll->data->pcwbits - 1, 0);
return __mtk_pll_recalc_rate(pll, parent_rate, pcw, postdiv);
}
int mtk_pll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
{
struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
u32 pcw = 0;
int postdiv;
mtk_pll_calc_values(pll, &pcw, &postdiv, req->rate,
req->best_parent_rate);
req->rate = __mtk_pll_recalc_rate(pll, req->best_parent_rate, pcw,
postdiv);
return 0;
}
int mtk_pll_prepare(struct clk_hw *hw)
{
struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
u32 r;
r = readl(pll->pwr_addr) | CON0_PWR_ON;
writel(r, pll->pwr_addr);
udelay(1);
r = readl(pll->pwr_addr) & ~CON0_ISO_EN;
writel(r, pll->pwr_addr);
udelay(1);
r = readl(pll->en_addr) | BIT(pll->data->pll_en_bit);
writel(r, pll->en_addr);
if (pll->data->en_mask) {
r = readl(pll->base_addr + REG_CON0) | pll->data->en_mask;
writel(r, pll->base_addr + REG_CON0);
}
__mtk_pll_tuner_enable(pll);
udelay(20);
if (pll->data->flags & HAVE_RST_BAR) {
r = readl(pll->base_addr + REG_CON0);
r |= pll->data->rst_bar_mask;
writel(r, pll->base_addr + REG_CON0);
}
return 0;
}
void mtk_pll_unprepare(struct clk_hw *hw)
{
struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
u32 r;
if (pll->data->flags & HAVE_RST_BAR) {
r = readl(pll->base_addr + REG_CON0);
r &= ~pll->data->rst_bar_mask;
writel(r, pll->base_addr + REG_CON0);
}
__mtk_pll_tuner_disable(pll);
if (pll->data->en_mask) {
r = readl(pll->base_addr + REG_CON0) & ~pll->data->en_mask;
writel(r, pll->base_addr + REG_CON0);
}
r = readl(pll->en_addr) & ~BIT(pll->data->pll_en_bit);
writel(r, pll->en_addr);
r = readl(pll->pwr_addr) | CON0_ISO_EN;
writel(r, pll->pwr_addr);
r = readl(pll->pwr_addr) & ~CON0_PWR_ON;
writel(r, pll->pwr_addr);
}
static int mtk_pll_prepare_setclr(struct clk_hw *hw)
{
struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
writel(BIT(pll->data->pll_en_bit), pll->en_set_addr);
/* Wait 20us after enable for the PLL to stabilize */
udelay(20);
return 0;
}
static void mtk_pll_unprepare_setclr(struct clk_hw *hw)
{
struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
writel(BIT(pll->data->pll_en_bit), pll->en_clr_addr);
}
const struct clk_ops mtk_pll_ops = {
.is_prepared = mtk_pll_is_prepared,
.prepare = mtk_pll_prepare,
.unprepare = mtk_pll_unprepare,
.recalc_rate = mtk_pll_recalc_rate,
.determine_rate = mtk_pll_determine_rate,
.set_rate = mtk_pll_set_rate,
};
const struct clk_ops mtk_pll_fenc_clr_set_ops = {
.is_prepared = mtk_pll_fenc_is_prepared,
.prepare = mtk_pll_prepare_setclr,
.unprepare = mtk_pll_unprepare_setclr,
.recalc_rate = mtk_pll_recalc_rate,
.determine_rate = mtk_pll_determine_rate,
.set_rate = mtk_pll_set_rate,
};
EXPORT_SYMBOL_GPL(mtk_pll_fenc_clr_set_ops);
struct clk_hw *mtk_clk_register_pll_ops(struct mtk_clk_pll *pll,
const struct mtk_pll_data *data,
void __iomem *base,
const struct clk_ops *pll_ops)
{
struct clk_init_data init = {};
int ret;
const char *parent_name = "clk26m";
pll->base_addr = base + data->reg;
pll->pwr_addr = base + data->pwr_reg;
pll->pd_addr = base + data->pd_reg;
pll->pcw_addr = base + data->pcw_reg;
if (data->pcw_chg_reg)
pll->pcw_chg_addr = base + data->pcw_chg_reg;
else
pll->pcw_chg_addr = pll->base_addr + REG_CON1;
if (data->tuner_reg)
pll->tuner_addr = base + data->tuner_reg;
if (data->tuner_en_reg || data->tuner_en_bit)
pll->tuner_en_addr = base + data->tuner_en_reg;
if (data->en_reg)
pll->en_addr = base + data->en_reg;
else
pll->en_addr = pll->base_addr + REG_CON0;
if (data->en_set_reg)
pll->en_set_addr = base + data->en_set_reg;
if (data->en_clr_reg)
pll->en_clr_addr = base + data->en_clr_reg;
pll->hw.init = &init;
pll->data = data;
pll->fenc_addr = base + data->fenc_sta_ofs;
init.name = data->name;
init.flags = (data->flags & PLL_AO) ? CLK_IS_CRITICAL : 0;
if (data->flags & PLL_PARENT_EN)
init.flags |= CLK_OPS_PARENT_ENABLE;
init.ops = pll_ops;
if (data->parent_name)
init.parent_names = &data->parent_name;
else
init.parent_names = &parent_name;
init.num_parents = 1;
ret = clk_hw_register(pll->dev, &pll->hw);
if (ret)
return ERR_PTR(ret);
return &pll->hw;
}
struct clk_hw *mtk_clk_register_pll(struct device *dev,
const struct mtk_pll_data *data,
void __iomem *base)
{
struct mtk_clk_pll *pll;
struct clk_hw *hw;
const struct clk_ops *pll_ops = data->ops ? data->ops : &mtk_pll_ops;
pll = kzalloc_obj(*pll);
if (!pll)
return ERR_PTR(-ENOMEM);
pll->dev = dev;
hw = mtk_clk_register_pll_ops(pll, data, base, pll_ops);
if (IS_ERR(hw))
kfree(pll);
return hw;
}
void mtk_clk_unregister_pll(struct clk_hw *hw)
{
struct mtk_clk_pll *pll;
if (!hw)
return;
pll = to_mtk_clk_pll(hw);
clk_hw_unregister(hw);
kfree(pll);
}
int mtk_clk_register_plls(struct device *dev,
const struct mtk_pll_data *plls, int num_plls,
struct clk_hw_onecell_data *clk_data)
{
void __iomem *base;
int i;
struct clk_hw *hw;
base = of_iomap(dev->of_node, 0);
if (!base) {
pr_err("%s(): ioremap failed\n", __func__);
return -EINVAL;
}
for (i = 0; i < num_plls; i++) {
const struct mtk_pll_data *pll = &plls[i];
if (!IS_ERR_OR_NULL(clk_data->hws[pll->id])) {
pr_warn("%pOF: Trying to register duplicate clock ID: %d\n",
dev->of_node, pll->id);
continue;
}
hw = mtk_clk_register_pll(dev, pll, base);
if (IS_ERR(hw)) {
pr_err("Failed to register clk %s: %pe\n", pll->name,
hw);
goto err;
}
clk_data->hws[pll->id] = hw;
}
return 0;
err:
while (--i >= 0) {
const struct mtk_pll_data *pll = &plls[i];
mtk_clk_unregister_pll(clk_data->hws[pll->id]);
clk_data->hws[pll->id] = ERR_PTR(-ENOENT);
}
iounmap(base);
return PTR_ERR(hw);
}
EXPORT_SYMBOL_GPL(mtk_clk_register_plls);
__iomem void *mtk_clk_pll_get_base(struct clk_hw *hw,
const struct mtk_pll_data *data)
{
struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
return pll->base_addr - data->reg;
}
void mtk_clk_unregister_plls(const struct mtk_pll_data *plls, int num_plls,
struct clk_hw_onecell_data *clk_data)
{
__iomem void *base = NULL;
int i;
if (!clk_data)
return;
for (i = num_plls; i > 0; i--) {
const struct mtk_pll_data *pll = &plls[i - 1];
if (IS_ERR_OR_NULL(clk_data->hws[pll->id]))
continue;
/*
* This is quite ugly but unfortunately the clks don't have
* any device tied to them, so there's no place to store the
* pointer to the I/O region base address. We have to fetch
* it from one of the registered clks.
*/
base = mtk_clk_pll_get_base(clk_data->hws[pll->id], pll);
mtk_clk_unregister_pll(clk_data->hws[pll->id]);
clk_data->hws[pll->id] = ERR_PTR(-ENOENT);
}
iounmap(base);
}
EXPORT_SYMBOL_GPL(mtk_clk_unregister_plls);
MODULE_LICENSE("GPL");