140468079SA.s. Dong // SPDX-License-Identifier: GPL-2.0+
240468079SA.s. Dong /*
340468079SA.s. Dong  * Copyright 2018 NXP.
440468079SA.s. Dong  *   Dong Aisheng <aisheng.dong@nxp.com>
540468079SA.s. Dong  */
640468079SA.s. Dong 
740468079SA.s. Dong #include <linux/clk-provider.h>
840468079SA.s. Dong #include <linux/err.h>
940468079SA.s. Dong #include <linux/io.h>
1040468079SA.s. Dong #include <linux/slab.h>
1140468079SA.s. Dong 
1240468079SA.s. Dong #include "clk.h"
1340468079SA.s. Dong 
1440468079SA.s. Dong struct clk_divider_gate {
1540468079SA.s. Dong 	struct clk_divider divider;
1640468079SA.s. Dong 	u32 cached_val;
1740468079SA.s. Dong };
1840468079SA.s. Dong 
to_clk_divider_gate(struct clk_hw * hw)1940468079SA.s. Dong static inline struct clk_divider_gate *to_clk_divider_gate(struct clk_hw *hw)
2040468079SA.s. Dong {
2140468079SA.s. Dong 	struct clk_divider *div = to_clk_divider(hw);
2240468079SA.s. Dong 
2340468079SA.s. Dong 	return container_of(div, struct clk_divider_gate, divider);
2440468079SA.s. Dong }
2540468079SA.s. Dong 
clk_divider_gate_recalc_rate_ro(struct clk_hw * hw,unsigned long parent_rate)2640468079SA.s. Dong static unsigned long clk_divider_gate_recalc_rate_ro(struct clk_hw *hw,
2740468079SA.s. Dong 						     unsigned long parent_rate)
2840468079SA.s. Dong {
2940468079SA.s. Dong 	struct clk_divider *div = to_clk_divider(hw);
3040468079SA.s. Dong 	unsigned int val;
3140468079SA.s. Dong 
325834fd75SJonas Gorski 	val = readl(div->reg) >> div->shift;
3340468079SA.s. Dong 	val &= clk_div_mask(div->width);
3440468079SA.s. Dong 	if (!val)
3540468079SA.s. Dong 		return 0;
3640468079SA.s. Dong 
3740468079SA.s. Dong 	return divider_recalc_rate(hw, parent_rate, val, div->table,
3840468079SA.s. Dong 				   div->flags, div->width);
3940468079SA.s. Dong }
4040468079SA.s. Dong 
clk_divider_gate_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)4140468079SA.s. Dong static unsigned long clk_divider_gate_recalc_rate(struct clk_hw *hw,
4240468079SA.s. Dong 						  unsigned long parent_rate)
4340468079SA.s. Dong {
4440468079SA.s. Dong 	struct clk_divider_gate *div_gate = to_clk_divider_gate(hw);
4540468079SA.s. Dong 	struct clk_divider *div = to_clk_divider(hw);
4628717368SPeng Fan 	unsigned long flags;
4740468079SA.s. Dong 	unsigned int val;
4840468079SA.s. Dong 
4940468079SA.s. Dong 	spin_lock_irqsave(div->lock, flags);
5040468079SA.s. Dong 
5140468079SA.s. Dong 	if (!clk_hw_is_enabled(hw)) {
5240468079SA.s. Dong 		val = div_gate->cached_val;
5340468079SA.s. Dong 	} else {
545834fd75SJonas Gorski 		val = readl(div->reg) >> div->shift;
5540468079SA.s. Dong 		val &= clk_div_mask(div->width);
5640468079SA.s. Dong 	}
5740468079SA.s. Dong 
5840468079SA.s. Dong 	spin_unlock_irqrestore(div->lock, flags);
5940468079SA.s. Dong 
6040468079SA.s. Dong 	if (!val)
6140468079SA.s. Dong 		return 0;
6240468079SA.s. Dong 
6340468079SA.s. Dong 	return divider_recalc_rate(hw, parent_rate, val, div->table,
6440468079SA.s. Dong 				   div->flags, div->width);
6540468079SA.s. Dong }
6640468079SA.s. Dong 
clk_divider_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)67*fb549644SMartin Blumenstingl static int clk_divider_determine_rate(struct clk_hw *hw,
68*fb549644SMartin Blumenstingl 				      struct clk_rate_request *req)
6940468079SA.s. Dong {
70*fb549644SMartin Blumenstingl 	return clk_divider_ops.determine_rate(hw, req);
7140468079SA.s. Dong }
7240468079SA.s. Dong 
clk_divider_gate_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)7340468079SA.s. Dong static int clk_divider_gate_set_rate(struct clk_hw *hw, unsigned long rate,
7440468079SA.s. Dong 				unsigned long parent_rate)
7540468079SA.s. Dong {
7640468079SA.s. Dong 	struct clk_divider_gate *div_gate = to_clk_divider_gate(hw);
7740468079SA.s. Dong 	struct clk_divider *div = to_clk_divider(hw);
7828717368SPeng Fan 	unsigned long flags;
7940468079SA.s. Dong 	int value;
8040468079SA.s. Dong 	u32 val;
8140468079SA.s. Dong 
8240468079SA.s. Dong 	value = divider_get_val(rate, parent_rate, div->table,
8340468079SA.s. Dong 				div->width, div->flags);
8440468079SA.s. Dong 	if (value < 0)
8540468079SA.s. Dong 		return value;
8640468079SA.s. Dong 
8740468079SA.s. Dong 	spin_lock_irqsave(div->lock, flags);
8840468079SA.s. Dong 
8940468079SA.s. Dong 	if (clk_hw_is_enabled(hw)) {
905834fd75SJonas Gorski 		val = readl(div->reg);
9140468079SA.s. Dong 		val &= ~(clk_div_mask(div->width) << div->shift);
9240468079SA.s. Dong 		val |= (u32)value << div->shift;
935834fd75SJonas Gorski 		writel(val, div->reg);
9440468079SA.s. Dong 	} else {
9540468079SA.s. Dong 		div_gate->cached_val = value;
9640468079SA.s. Dong 	}
9740468079SA.s. Dong 
9840468079SA.s. Dong 	spin_unlock_irqrestore(div->lock, flags);
9940468079SA.s. Dong 
10040468079SA.s. Dong 	return 0;
10140468079SA.s. Dong }
10240468079SA.s. Dong 
clk_divider_enable(struct clk_hw * hw)10340468079SA.s. Dong static int clk_divider_enable(struct clk_hw *hw)
10440468079SA.s. Dong {
10540468079SA.s. Dong 	struct clk_divider_gate *div_gate = to_clk_divider_gate(hw);
10640468079SA.s. Dong 	struct clk_divider *div = to_clk_divider(hw);
10728717368SPeng Fan 	unsigned long flags;
10840468079SA.s. Dong 	u32 val;
10940468079SA.s. Dong 
11040468079SA.s. Dong 	if (!div_gate->cached_val) {
11140468079SA.s. Dong 		pr_err("%s: no valid preset rate\n", clk_hw_get_name(hw));
11240468079SA.s. Dong 		return -EINVAL;
11340468079SA.s. Dong 	}
11440468079SA.s. Dong 
11540468079SA.s. Dong 	spin_lock_irqsave(div->lock, flags);
11640468079SA.s. Dong 	/* restore div val */
1175834fd75SJonas Gorski 	val = readl(div->reg);
11840468079SA.s. Dong 	val |= div_gate->cached_val << div->shift;
1195834fd75SJonas Gorski 	writel(val, div->reg);
12040468079SA.s. Dong 
12140468079SA.s. Dong 	spin_unlock_irqrestore(div->lock, flags);
12240468079SA.s. Dong 
12340468079SA.s. Dong 	return 0;
12440468079SA.s. Dong }
12540468079SA.s. Dong 
clk_divider_disable(struct clk_hw * hw)12640468079SA.s. Dong static void clk_divider_disable(struct clk_hw *hw)
12740468079SA.s. Dong {
12840468079SA.s. Dong 	struct clk_divider_gate *div_gate = to_clk_divider_gate(hw);
12940468079SA.s. Dong 	struct clk_divider *div = to_clk_divider(hw);
13028717368SPeng Fan 	unsigned long flags;
13140468079SA.s. Dong 	u32 val;
13240468079SA.s. Dong 
13340468079SA.s. Dong 	spin_lock_irqsave(div->lock, flags);
13440468079SA.s. Dong 
13540468079SA.s. Dong 	/* store the current div val */
1365834fd75SJonas Gorski 	val = readl(div->reg) >> div->shift;
13740468079SA.s. Dong 	val &= clk_div_mask(div->width);
13840468079SA.s. Dong 	div_gate->cached_val = val;
1395834fd75SJonas Gorski 	writel(0, div->reg);
14040468079SA.s. Dong 
14140468079SA.s. Dong 	spin_unlock_irqrestore(div->lock, flags);
14240468079SA.s. Dong }
14340468079SA.s. Dong 
clk_divider_is_enabled(struct clk_hw * hw)14440468079SA.s. Dong static int clk_divider_is_enabled(struct clk_hw *hw)
14540468079SA.s. Dong {
14640468079SA.s. Dong 	struct clk_divider *div = to_clk_divider(hw);
14740468079SA.s. Dong 	u32 val;
14840468079SA.s. Dong 
1495834fd75SJonas Gorski 	val = readl(div->reg) >> div->shift;
15040468079SA.s. Dong 	val &= clk_div_mask(div->width);
15140468079SA.s. Dong 
15240468079SA.s. Dong 	return val ? 1 : 0;
15340468079SA.s. Dong }
15440468079SA.s. Dong 
15540468079SA.s. Dong static const struct clk_ops clk_divider_gate_ro_ops = {
15640468079SA.s. Dong 	.recalc_rate = clk_divider_gate_recalc_rate_ro,
157*fb549644SMartin Blumenstingl 	.determine_rate = clk_divider_determine_rate,
15840468079SA.s. Dong };
15940468079SA.s. Dong 
16040468079SA.s. Dong static const struct clk_ops clk_divider_gate_ops = {
16140468079SA.s. Dong 	.recalc_rate = clk_divider_gate_recalc_rate,
162*fb549644SMartin Blumenstingl 	.determine_rate = clk_divider_determine_rate,
16340468079SA.s. Dong 	.set_rate = clk_divider_gate_set_rate,
16440468079SA.s. Dong 	.enable = clk_divider_enable,
16540468079SA.s. Dong 	.disable = clk_divider_disable,
16640468079SA.s. Dong 	.is_enabled = clk_divider_is_enabled,
16740468079SA.s. Dong };
16840468079SA.s. Dong 
16940468079SA.s. Dong /*
170a546d88cSPeng Fan  * NOTE: In order to reuse the most code from the common divider,
17140468079SA.s. Dong  * we also design our divider following the way that provids an extra
17240468079SA.s. Dong  * clk_divider_flags, however it's fixed to CLK_DIVIDER_ONE_BASED by
17340468079SA.s. Dong  * default as our HW is. Besides that it supports only CLK_DIVIDER_READ_ONLY
17440468079SA.s. Dong  * flag which can be specified by user flexibly.
17540468079SA.s. Dong  */
imx_clk_hw_divider_gate(const char * name,const char * parent_name,unsigned long flags,void __iomem * reg,u8 shift,u8 width,u8 clk_divider_flags,const struct clk_div_table * table,spinlock_t * lock)176ea6a723aSAbel Vesa struct clk_hw *imx_clk_hw_divider_gate(const char *name, const char *parent_name,
17740468079SA.s. Dong 				    unsigned long flags, void __iomem *reg,
17840468079SA.s. Dong 				    u8 shift, u8 width, u8 clk_divider_flags,
17940468079SA.s. Dong 				    const struct clk_div_table *table,
18040468079SA.s. Dong 				    spinlock_t *lock)
18140468079SA.s. Dong {
18240468079SA.s. Dong 	struct clk_init_data init;
18340468079SA.s. Dong 	struct clk_divider_gate *div_gate;
18440468079SA.s. Dong 	struct clk_hw *hw;
18540468079SA.s. Dong 	u32 val;
18640468079SA.s. Dong 	int ret;
18740468079SA.s. Dong 
18840468079SA.s. Dong 	div_gate  = kzalloc(sizeof(*div_gate), GFP_KERNEL);
18940468079SA.s. Dong 	if (!div_gate)
19040468079SA.s. Dong 		return ERR_PTR(-ENOMEM);
19140468079SA.s. Dong 
19240468079SA.s. Dong 	init.name = name;
19340468079SA.s. Dong 	if (clk_divider_flags & CLK_DIVIDER_READ_ONLY)
19440468079SA.s. Dong 		init.ops = &clk_divider_gate_ro_ops;
19540468079SA.s. Dong 	else
19640468079SA.s. Dong 		init.ops = &clk_divider_gate_ops;
19740468079SA.s. Dong 	init.flags = flags;
19840468079SA.s. Dong 	init.parent_names = parent_name ? &parent_name : NULL;
19940468079SA.s. Dong 	init.num_parents = parent_name ? 1 : 0;
20040468079SA.s. Dong 
20140468079SA.s. Dong 	div_gate->divider.reg = reg;
20240468079SA.s. Dong 	div_gate->divider.shift = shift;
20340468079SA.s. Dong 	div_gate->divider.width = width;
20440468079SA.s. Dong 	div_gate->divider.lock = lock;
20540468079SA.s. Dong 	div_gate->divider.table = table;
20640468079SA.s. Dong 	div_gate->divider.hw.init = &init;
20740468079SA.s. Dong 	div_gate->divider.flags = CLK_DIVIDER_ONE_BASED | clk_divider_flags;
20840468079SA.s. Dong 	/* cache gate status */
2095834fd75SJonas Gorski 	val = readl(reg) >> shift;
21040468079SA.s. Dong 	val &= clk_div_mask(width);
21140468079SA.s. Dong 	div_gate->cached_val = val;
21240468079SA.s. Dong 
21340468079SA.s. Dong 	hw = &div_gate->divider.hw;
21440468079SA.s. Dong 	ret = clk_hw_register(NULL, hw);
21540468079SA.s. Dong 	if (ret) {
21640468079SA.s. Dong 		kfree(div_gate);
21740468079SA.s. Dong 		hw = ERR_PTR(ret);
21840468079SA.s. Dong 	}
21940468079SA.s. Dong 
22040468079SA.s. Dong 	return hw;
22140468079SA.s. Dong }
222