Skip to content

Commit ee394f6

Browse files
oleremabelvesa
authored andcommitted
clk: imx: add clk-gpr-mux driver
Almost(?) every i.MX variant has clk mux for ethernet (rgmii/rmii) reference clock located in the GPR1 register. So far this clk is configured in different ways: - mach-imx6q is doing mux configuration based on ptp vs enet_ref clk comparison. - mach-imx7d is setting mux to PAD for all boards - mach-imx6ul is setting mux to internal clock for all boards. Since we have imx7d and imx6ul board variants which do not work with configurations forced by kernel mach code, we need to implement this clk mux properly as part of the clk framework. Which is done by this patch. Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de> Reviewed-by: Abel Vesa <abel.vesa@linaro.org> Signed-off-by: Abel Vesa <abel.vesa@linaro.org> Link: https://lore.kernel.org/r/20230131084642.709385-2-o.rempel@pengutronix.de
1 parent 8864eac commit ee394f6

3 files changed

Lines changed: 125 additions & 0 deletions

File tree

drivers/clk/imx/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ mxc-clk-objs += clk-pllv3.o
2222
mxc-clk-objs += clk-pllv4.o
2323
mxc-clk-objs += clk-pll14xx.o
2424
mxc-clk-objs += clk-sscg-pll.o
25+
mxc-clk-objs += clk-gpr-mux.o
2526
obj-$(CONFIG_MXC_CLK) += mxc-clk.o
2627

2728
obj-$(CONFIG_CLK_IMX8MM) += clk-imx8mm.o

drivers/clk/imx/clk-gpr-mux.c

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
*/
4+
5+
#define pr_fmt(fmt) "imx:clk-gpr-mux: " fmt
6+
7+
#include <linux/module.h>
8+
9+
#include <linux/clk-provider.h>
10+
#include <linux/errno.h>
11+
#include <linux/export.h>
12+
#include <linux/io.h>
13+
#include <linux/slab.h>
14+
#include <linux/regmap.h>
15+
#include <linux/mfd/syscon.h>
16+
17+
#include "clk.h"
18+
19+
struct imx_clk_gpr {
20+
struct clk_hw hw;
21+
struct regmap *regmap;
22+
u32 mask;
23+
u32 reg;
24+
const u32 *mux_table;
25+
};
26+
27+
static struct imx_clk_gpr *to_imx_clk_gpr(struct clk_hw *hw)
28+
{
29+
return container_of(hw, struct imx_clk_gpr, hw);
30+
}
31+
32+
static u8 imx_clk_gpr_mux_get_parent(struct clk_hw *hw)
33+
{
34+
struct imx_clk_gpr *priv = to_imx_clk_gpr(hw);
35+
unsigned int val;
36+
int ret;
37+
38+
ret = regmap_read(priv->regmap, priv->reg, &val);
39+
if (ret)
40+
goto get_parent_err;
41+
42+
val &= priv->mask;
43+
44+
ret = clk_mux_val_to_index(hw, priv->mux_table, 0, val);
45+
if (ret < 0)
46+
goto get_parent_err;
47+
48+
return ret;
49+
50+
get_parent_err:
51+
pr_err("failed to get parent (%pe)\n", ERR_PTR(ret));
52+
53+
/* return some realistic non negative value. Potentially we could
54+
* give index to some dummy error parent.
55+
*/
56+
return 0;
57+
}
58+
59+
static int imx_clk_gpr_mux_set_parent(struct clk_hw *hw, u8 index)
60+
{
61+
struct imx_clk_gpr *priv = to_imx_clk_gpr(hw);
62+
unsigned int val = clk_mux_index_to_val(priv->mux_table, 0, index);
63+
64+
return regmap_update_bits(priv->regmap, priv->reg, priv->mask, val);
65+
}
66+
67+
static int imx_clk_gpr_mux_determine_rate(struct clk_hw *hw,
68+
struct clk_rate_request *req)
69+
{
70+
return clk_mux_determine_rate_flags(hw, req, 0);
71+
}
72+
73+
const struct clk_ops imx_clk_gpr_mux_ops = {
74+
.get_parent = imx_clk_gpr_mux_get_parent,
75+
.set_parent = imx_clk_gpr_mux_set_parent,
76+
.determine_rate = imx_clk_gpr_mux_determine_rate,
77+
};
78+
79+
struct clk_hw *imx_clk_gpr_mux(const char *name, const char *compatible,
80+
u32 reg, const char **parent_names,
81+
u8 num_parents, const u32 *mux_table, u32 mask)
82+
{
83+
struct clk_init_data init = { };
84+
struct imx_clk_gpr *priv;
85+
struct regmap *regmap;
86+
struct clk_hw *hw;
87+
int ret;
88+
89+
regmap = syscon_regmap_lookup_by_compatible(compatible);
90+
if (IS_ERR(regmap)) {
91+
pr_err("failed to find %s regmap\n", compatible);
92+
return ERR_CAST(regmap);
93+
}
94+
95+
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
96+
if (!priv)
97+
return ERR_PTR(-ENOMEM);
98+
99+
init.name = name;
100+
init.ops = &imx_clk_gpr_mux_ops;
101+
init.parent_names = parent_names;
102+
init.num_parents = num_parents;
103+
init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
104+
105+
priv->hw.init = &init;
106+
priv->regmap = regmap;
107+
priv->mux_table = mux_table;
108+
priv->reg = reg;
109+
priv->mask = mask;
110+
111+
hw = &priv->hw;
112+
ret = clk_hw_register(NULL, &priv->hw);
113+
if (ret) {
114+
kfree(priv);
115+
hw = ERR_PTR(ret);
116+
}
117+
118+
return hw;
119+
}

drivers/clk/imx/clk.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,4 +458,9 @@ struct clk_hw *imx_clk_hw_divider_gate(const char *name, const char *parent_name
458458
unsigned long flags, void __iomem *reg, u8 shift, u8 width,
459459
u8 clk_divider_flags, const struct clk_div_table *table,
460460
spinlock_t *lock);
461+
462+
struct clk_hw *imx_clk_gpr_mux(const char *name, const char *compatible,
463+
u32 reg, const char **parent_names,
464+
u8 num_parents, const u32 *mux_table, u32 mask);
465+
461466
#endif

0 commit comments

Comments
 (0)