1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (c) 2018 MediaTek Inc.
4 * Author: Owen Chen <owen.chen@mediatek.com>
8 #include <linux/of_address.h>
9 #include <linux/slab.h>
10 #include <linux/mfd/syscon.h>
15 static inline struct mtk_clk_mux
*to_mtk_clk_mux(struct clk_hw
*hw
)
17 return container_of(hw
, struct mtk_clk_mux
, hw
);
20 static int mtk_clk_mux_enable_setclr(struct clk_hw
*hw
)
22 struct mtk_clk_mux
*mux
= to_mtk_clk_mux(hw
);
23 unsigned long flags
= 0;
26 spin_lock_irqsave(mux
->lock
, flags
);
30 regmap_write(mux
->regmap
, mux
->data
->clr_ofs
,
31 BIT(mux
->data
->gate_shift
));
34 * If the parent has been changed when the clock was disabled, it will
35 * not be effective yet. Set the update bit to ensure the mux gets
38 if (mux
->reparent
&& mux
->data
->upd_shift
>= 0) {
39 regmap_write(mux
->regmap
, mux
->data
->upd_ofs
,
40 BIT(mux
->data
->upd_shift
));
41 mux
->reparent
= false;
45 spin_unlock_irqrestore(mux
->lock
, flags
);
52 static void mtk_clk_mux_disable_setclr(struct clk_hw
*hw
)
54 struct mtk_clk_mux
*mux
= to_mtk_clk_mux(hw
);
56 regmap_write(mux
->regmap
, mux
->data
->set_ofs
,
57 BIT(mux
->data
->gate_shift
));
60 static int mtk_clk_mux_is_enabled(struct clk_hw
*hw
)
62 struct mtk_clk_mux
*mux
= to_mtk_clk_mux(hw
);
65 regmap_read(mux
->regmap
, mux
->data
->mux_ofs
, &val
);
67 return (val
& BIT(mux
->data
->gate_shift
)) == 0;
70 static u8
mtk_clk_mux_get_parent(struct clk_hw
*hw
)
72 struct mtk_clk_mux
*mux
= to_mtk_clk_mux(hw
);
73 u32 mask
= GENMASK(mux
->data
->mux_width
- 1, 0);
76 regmap_read(mux
->regmap
, mux
->data
->mux_ofs
, &val
);
77 val
= (val
>> mux
->data
->mux_shift
) & mask
;
82 static int mtk_clk_mux_set_parent_setclr_lock(struct clk_hw
*hw
, u8 index
)
84 struct mtk_clk_mux
*mux
= to_mtk_clk_mux(hw
);
85 u32 mask
= GENMASK(mux
->data
->mux_width
- 1, 0);
87 unsigned long flags
= 0;
90 spin_lock_irqsave(mux
->lock
, flags
);
94 regmap_read(mux
->regmap
, mux
->data
->mux_ofs
, &orig
);
95 val
= (orig
& ~(mask
<< mux
->data
->mux_shift
))
96 | (index
<< mux
->data
->mux_shift
);
99 regmap_write(mux
->regmap
, mux
->data
->clr_ofs
,
100 mask
<< mux
->data
->mux_shift
);
101 regmap_write(mux
->regmap
, mux
->data
->set_ofs
,
102 index
<< mux
->data
->mux_shift
);
104 if (mux
->data
->upd_shift
>= 0) {
105 regmap_write(mux
->regmap
, mux
->data
->upd_ofs
,
106 BIT(mux
->data
->upd_shift
));
107 mux
->reparent
= true;
112 spin_unlock_irqrestore(mux
->lock
, flags
);
114 __release(mux
->lock
);
119 const struct clk_ops mtk_mux_clr_set_upd_ops
= {
120 .get_parent
= mtk_clk_mux_get_parent
,
121 .set_parent
= mtk_clk_mux_set_parent_setclr_lock
,
124 const struct clk_ops mtk_mux_gate_clr_set_upd_ops
= {
125 .enable
= mtk_clk_mux_enable_setclr
,
126 .disable
= mtk_clk_mux_disable_setclr
,
127 .is_enabled
= mtk_clk_mux_is_enabled
,
128 .get_parent
= mtk_clk_mux_get_parent
,
129 .set_parent
= mtk_clk_mux_set_parent_setclr_lock
,
132 static struct clk
*mtk_clk_register_mux(const struct mtk_mux
*mux
,
133 struct regmap
*regmap
,
136 struct mtk_clk_mux
*clk_mux
;
137 struct clk_init_data init
= {};
140 clk_mux
= kzalloc(sizeof(*clk_mux
), GFP_KERNEL
);
142 return ERR_PTR(-ENOMEM
);
144 init
.name
= mux
->name
;
145 init
.flags
= mux
->flags
| CLK_SET_RATE_PARENT
;
146 init
.parent_names
= mux
->parent_names
;
147 init
.num_parents
= mux
->num_parents
;
150 clk_mux
->regmap
= regmap
;
152 clk_mux
->lock
= lock
;
153 clk_mux
->hw
.init
= &init
;
155 clk
= clk_register(NULL
, &clk_mux
->hw
);
164 int mtk_clk_register_muxes(const struct mtk_mux
*muxes
,
165 int num
, struct device_node
*node
,
167 struct clk_onecell_data
*clk_data
)
169 struct regmap
*regmap
;
173 regmap
= device_node_to_regmap(node
);
174 if (IS_ERR(regmap
)) {
175 pr_err("Cannot find regmap for %pOF: %ld\n", node
,
177 return PTR_ERR(regmap
);
180 for (i
= 0; i
< num
; i
++) {
181 const struct mtk_mux
*mux
= &muxes
[i
];
183 if (IS_ERR_OR_NULL(clk_data
->clks
[mux
->id
])) {
184 clk
= mtk_clk_register_mux(mux
, regmap
, lock
);
187 pr_err("Failed to register clk %s: %ld\n",
188 mux
->name
, PTR_ERR(clk
));
192 clk_data
->clks
[mux
->id
] = clk
;