linux-stable/drivers/clk/mediatek/clk-gate.c
Chen-Yu Tsai 6f691a5862 clk: mediatek: Switch to clk_hw provider APIs
As part of the effort to improve the MediaTek clk drivers, the next step
is to switch from the old 'struct clk' clk prodivder APIs to the new
'struct clk_hw' ones.

In a previous patch, 'struct clk_onecell_data' was replaced with
'struct clk_hw_onecell_data', with (struct clk_hw *)->clk and
__clk_get_hw() bridging the new data structures and old code.

Now switch from the old 'clk_(un)?register*()' APIs to the new
'clk_hw_(un)?register*()' ones. This is done with the coccinelle script
below.

Unfortunately this also leaves clk-mt8173.c with a compile error that
would need a coccinelle script longer than the actual diff to fix. This
last part is fixed up by hand.

    // Fix prototypes
    @@
    identifier F =~ "^mtk_clk_register_";
    @@
    - struct clk *
    + struct clk_hw *
      F(...);

    // Fix calls to mtk_clk_register_<singular>
    @ reg @
    identifier F =~ "^mtk_clk_register_";
    identifier FS =~ "^mtk_clk_register_[a-z_]*s";
    identifier I;
    expression clk_data;
    expression E;
    @@
      FS(...) {
	    ...
    -	struct clk *I;
    +	struct clk_hw *hw;
	    ...
	    for (...;...;...) {
		    ...
    (
    -		I
    +		hw
		    =
    -		clk_register_fixed_rate(
    +		clk_hw_register_fixed_rate(
					    ...
		    );
    |
    -		I
    +		hw
		    =
    -		clk_register_fixed_factor(
    +		clk_hw_register_fixed_factor(
					    ...
		    );
    |
    -		I
    +		hw
		    =
    -		clk_register_divider(
    +		clk_hw_register_divider(
					    ...
		    );
    |
    -		I
    +		hw
		    =
		    F(...);
    )
		    ...
		    if (
    -		    IS_ERR(I)
    +		    IS_ERR(hw)
		       ) {
			    pr_err(...,
    -			       I
    +			       hw
			    ,...);
			    ...
		    }

    -		clk_data->hws[E] = __clk_get_hw(I);
    +		clk_data->hws[E] = hw;
	    }
	    ...
      }

    @ depends on reg @
    identifier reg.I;
    @@
      return PTR_ERR(
    - I
    + hw
      );

    // Fix mtk_clk_register_composite to return clk_hw instead of clk
    @@
    identifier I, R;
    expression E;
    @@
    - struct clk *
    + struct clk_hw *
      mtk_clk_register_composite(...) {
	    ...
    -	struct clk *I;
    +	struct clk_hw *hw;
	    ...
    -	I = clk_register_composite(
    +	hw = clk_hw_register_composite(
		    ...);
	    if (IS_ERR(
    -		   I
    +		   hw
		       )) {
		    ...
		    R = PTR_ERR(
    -			      I
    +			      hw
				  );
		    ...
	    }

	    return
    -		I
    +		hw
	    ;
	    ...
      }

    // Fix other mtk_clk_register_<singular> to return clk_hw instead of clk
    @@
    identifier F =~ "^mtk_clk_register_";
    identifier I, D, C;
    expression E;
    @@
    - struct clk *
    + struct clk_hw *
      F(...) {
	    ...
    -	struct clk *I;
    +	int ret;
	    ...
    -	I = clk_register(D, E);
    +	ret = clk_hw_register(D, E);
	    ...
    (
    -	if (IS_ERR(I))
    +	if (ret) {
		    kfree(C);
    +		return ERR_PTR(ret);
    +	}
    |
    -	if (IS_ERR(I))
    +	if (ret)
	    {
		    kfree(C);
    -		return I;
    +		return ERR_PTR(ret);
	    }
    )

    -	return I;
    +	return E;
      }

    // Fix mtk_clk_unregister_<singular> to take clk_hw instead of clk
    @@
    identifier F =~ "^mtk_clk_unregister_";
    identifier I, I2;
    @@
      static void F(
    - 	struct clk *I
    +	struct clk_hw *I2
      )
      {
	    ...
    -	struct clk_hw *I2;
	    ...
    -	I2 = __clk_get_hw(I);
	    ...
    (
    -	clk_unregister(I);
    +	clk_hw_unregister(I2);
    |
    -	clk_unregister_composite(I);
    +	clk_hw_unregister_composite(I2);
    )
	    ...
      }

    // Fix calls to mtk_clk_unregister_*()
    @@
    identifier F =~ "^mtk_clk_unregister_";
    expression I;
    expression E;
    @@
    - F(I->hws[E]->clk);
    + F(I->hws[E]);

Signed-off-by: Chen-Yu Tsai <wenst@chromium.org>
Reviewed-by: Miles Chen <miles.chen@mediatek.com>
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Tested-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Tested-by: Miles Chen <miles.chen@mediatek.com>
Link: https://lore.kernel.org/r/20220519071610.423372-5-wenst@chromium.org
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
2022-05-19 16:57:57 -07:00

293 lines
6 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/mfd/syscon.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/types.h>
#include "clk-gate.h"
struct mtk_clk_gate {
struct clk_hw hw;
struct regmap *regmap;
int set_ofs;
int clr_ofs;
int sta_ofs;
u8 bit;
};
static inline struct mtk_clk_gate *to_mtk_clk_gate(struct clk_hw *hw)
{
return container_of(hw, struct mtk_clk_gate, hw);
}
static u32 mtk_get_clockgating(struct clk_hw *hw)
{
struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
u32 val;
regmap_read(cg->regmap, cg->sta_ofs, &val);
return val & BIT(cg->bit);
}
static int mtk_cg_bit_is_cleared(struct clk_hw *hw)
{
return mtk_get_clockgating(hw) == 0;
}
static int mtk_cg_bit_is_set(struct clk_hw *hw)
{
return mtk_get_clockgating(hw) != 0;
}
static void mtk_cg_set_bit(struct clk_hw *hw)
{
struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
regmap_write(cg->regmap, cg->set_ofs, BIT(cg->bit));
}
static void mtk_cg_clr_bit(struct clk_hw *hw)
{
struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
regmap_write(cg->regmap, cg->clr_ofs, BIT(cg->bit));
}
static void mtk_cg_set_bit_no_setclr(struct clk_hw *hw)
{
struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
regmap_set_bits(cg->regmap, cg->sta_ofs, BIT(cg->bit));
}
static void mtk_cg_clr_bit_no_setclr(struct clk_hw *hw)
{
struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
regmap_clear_bits(cg->regmap, cg->sta_ofs, BIT(cg->bit));
}
static int mtk_cg_enable(struct clk_hw *hw)
{
mtk_cg_clr_bit(hw);
return 0;
}
static void mtk_cg_disable(struct clk_hw *hw)
{
mtk_cg_set_bit(hw);
}
static int mtk_cg_enable_inv(struct clk_hw *hw)
{
mtk_cg_set_bit(hw);
return 0;
}
static void mtk_cg_disable_inv(struct clk_hw *hw)
{
mtk_cg_clr_bit(hw);
}
static int mtk_cg_enable_no_setclr(struct clk_hw *hw)
{
mtk_cg_clr_bit_no_setclr(hw);
return 0;
}
static void mtk_cg_disable_no_setclr(struct clk_hw *hw)
{
mtk_cg_set_bit_no_setclr(hw);
}
static int mtk_cg_enable_inv_no_setclr(struct clk_hw *hw)
{
mtk_cg_set_bit_no_setclr(hw);
return 0;
}
static void mtk_cg_disable_inv_no_setclr(struct clk_hw *hw)
{
mtk_cg_clr_bit_no_setclr(hw);
}
const struct clk_ops mtk_clk_gate_ops_setclr = {
.is_enabled = mtk_cg_bit_is_cleared,
.enable = mtk_cg_enable,
.disable = mtk_cg_disable,
};
EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_setclr);
const struct clk_ops mtk_clk_gate_ops_setclr_inv = {
.is_enabled = mtk_cg_bit_is_set,
.enable = mtk_cg_enable_inv,
.disable = mtk_cg_disable_inv,
};
EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_setclr_inv);
const struct clk_ops mtk_clk_gate_ops_no_setclr = {
.is_enabled = mtk_cg_bit_is_cleared,
.enable = mtk_cg_enable_no_setclr,
.disable = mtk_cg_disable_no_setclr,
};
EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_no_setclr);
const struct clk_ops mtk_clk_gate_ops_no_setclr_inv = {
.is_enabled = mtk_cg_bit_is_set,
.enable = mtk_cg_enable_inv_no_setclr,
.disable = mtk_cg_disable_inv_no_setclr,
};
EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_no_setclr_inv);
static struct clk_hw *mtk_clk_register_gate(const char *name,
const char *parent_name,
struct regmap *regmap, int set_ofs,
int clr_ofs, int sta_ofs, u8 bit,
const struct clk_ops *ops,
unsigned long flags, struct device *dev)
{
struct mtk_clk_gate *cg;
int ret;
struct clk_init_data init = {};
cg = kzalloc(sizeof(*cg), GFP_KERNEL);
if (!cg)
return ERR_PTR(-ENOMEM);
init.name = name;
init.flags = flags | CLK_SET_RATE_PARENT;
init.parent_names = parent_name ? &parent_name : NULL;
init.num_parents = parent_name ? 1 : 0;
init.ops = ops;
cg->regmap = regmap;
cg->set_ofs = set_ofs;
cg->clr_ofs = clr_ofs;
cg->sta_ofs = sta_ofs;
cg->bit = bit;
cg->hw.init = &init;
ret = clk_hw_register(dev, &cg->hw);
if (ret) {
kfree(cg);
return ERR_PTR(ret);
}
return &cg->hw;
}
static void mtk_clk_unregister_gate(struct clk_hw *hw)
{
struct mtk_clk_gate *cg;
if (!hw)
return;
cg = to_mtk_clk_gate(hw);
clk_hw_unregister(hw);
kfree(cg);
}
int mtk_clk_register_gates_with_dev(struct device_node *node,
const struct mtk_gate *clks, int num,
struct clk_hw_onecell_data *clk_data,
struct device *dev)
{
int i;
struct clk_hw *hw;
struct regmap *regmap;
if (!clk_data)
return -ENOMEM;
regmap = device_node_to_regmap(node);
if (IS_ERR(regmap)) {
pr_err("Cannot find regmap for %pOF: %pe\n", node, regmap);
return PTR_ERR(regmap);
}
for (i = 0; i < num; i++) {
const struct mtk_gate *gate = &clks[i];
if (!IS_ERR_OR_NULL(clk_data->hws[gate->id])) {
pr_warn("%pOF: Trying to register duplicate clock ID: %d\n",
node, gate->id);
continue;
}
hw = mtk_clk_register_gate(gate->name, gate->parent_name,
regmap,
gate->regs->set_ofs,
gate->regs->clr_ofs,
gate->regs->sta_ofs,
gate->shift, gate->ops,
gate->flags, dev);
if (IS_ERR(hw)) {
pr_err("Failed to register clk %s: %pe\n", gate->name,
hw);
goto err;
}
clk_data->hws[gate->id] = hw;
}
return 0;
err:
while (--i >= 0) {
const struct mtk_gate *gate = &clks[i];
if (IS_ERR_OR_NULL(clk_data->hws[gate->id]))
continue;
mtk_clk_unregister_gate(clk_data->hws[gate->id]);
clk_data->hws[gate->id] = ERR_PTR(-ENOENT);
}
return PTR_ERR(hw);
}
int mtk_clk_register_gates(struct device_node *node,
const struct mtk_gate *clks, int num,
struct clk_hw_onecell_data *clk_data)
{
return mtk_clk_register_gates_with_dev(node, clks, num, clk_data, NULL);
}
EXPORT_SYMBOL_GPL(mtk_clk_register_gates);
void mtk_clk_unregister_gates(const struct mtk_gate *clks, int num,
struct clk_hw_onecell_data *clk_data)
{
int i;
if (!clk_data)
return;
for (i = num; i > 0; i--) {
const struct mtk_gate *gate = &clks[i - 1];
if (IS_ERR_OR_NULL(clk_data->hws[gate->id]))
continue;
mtk_clk_unregister_gate(clk_data->hws[gate->id]);
clk_data->hws[gate->id] = ERR_PTR(-ENOENT);
}
}
EXPORT_SYMBOL_GPL(mtk_clk_unregister_gates);
MODULE_LICENSE("GPL");