18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Authors:
68c2ecf20Sopenharmony_ci *   Serge Semin <Sergey.Semin@baikalelectronics.ru>
78c2ecf20Sopenharmony_ci *   Dmitry Dunaev <dmitry.dunaev@baikalelectronics.ru>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Baikal-T1 CCU Dividers interface driver
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "bt1-ccu-div: " fmt
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/printk.h>
168c2ecf20Sopenharmony_ci#include <linux/bits.h>
178c2ecf20Sopenharmony_ci#include <linux/bitfield.h>
188c2ecf20Sopenharmony_ci#include <linux/slab.h>
198c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
208c2ecf20Sopenharmony_ci#include <linux/of.h>
218c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
228c2ecf20Sopenharmony_ci#include <linux/regmap.h>
238c2ecf20Sopenharmony_ci#include <linux/delay.h>
248c2ecf20Sopenharmony_ci#include <linux/time64.h>
258c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include "ccu-div.h"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define CCU_DIV_CTL			0x00
308c2ecf20Sopenharmony_ci#define CCU_DIV_CTL_EN			BIT(0)
318c2ecf20Sopenharmony_ci#define CCU_DIV_CTL_RST			BIT(1)
328c2ecf20Sopenharmony_ci#define CCU_DIV_CTL_SET_CLKDIV		BIT(2)
338c2ecf20Sopenharmony_ci#define CCU_DIV_CTL_CLKDIV_FLD		4
348c2ecf20Sopenharmony_ci#define CCU_DIV_CTL_CLKDIV_MASK(_width) \
358c2ecf20Sopenharmony_ci	GENMASK((_width) + CCU_DIV_CTL_CLKDIV_FLD - 1, CCU_DIV_CTL_CLKDIV_FLD)
368c2ecf20Sopenharmony_ci#define CCU_DIV_CTL_LOCK_SHIFTED	BIT(27)
378c2ecf20Sopenharmony_ci#define CCU_DIV_CTL_GATE_REF_BUF	BIT(28)
388c2ecf20Sopenharmony_ci#define CCU_DIV_CTL_LOCK_NORMAL		BIT(31)
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define CCU_DIV_RST_DELAY_US		1
418c2ecf20Sopenharmony_ci#define CCU_DIV_LOCK_CHECK_RETRIES	50
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#define CCU_DIV_CLKDIV_MIN		0
448c2ecf20Sopenharmony_ci#define CCU_DIV_CLKDIV_MAX(_mask) \
458c2ecf20Sopenharmony_ci	((_mask) >> CCU_DIV_CTL_CLKDIV_FLD)
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/*
488c2ecf20Sopenharmony_ci * Use the next two methods until there are generic field setter and
498c2ecf20Sopenharmony_ci * getter available with non-constant mask support.
508c2ecf20Sopenharmony_ci */
518c2ecf20Sopenharmony_cistatic inline u32 ccu_div_get(u32 mask, u32 val)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	return (val & mask) >> CCU_DIV_CTL_CLKDIV_FLD;
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic inline u32 ccu_div_prep(u32 mask, u32 val)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	return (val << CCU_DIV_CTL_CLKDIV_FLD) & mask;
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic inline unsigned long ccu_div_lock_delay_ns(unsigned long ref_clk,
628c2ecf20Sopenharmony_ci						  unsigned long div)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	u64 ns = 4ULL * (div ?: 1) * NSEC_PER_SEC;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	do_div(ns, ref_clk);
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	return ns;
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic inline unsigned long ccu_div_calc_freq(unsigned long ref_clk,
728c2ecf20Sopenharmony_ci					      unsigned long div)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	return ref_clk / (div ?: 1);
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic int ccu_div_var_update_clkdiv(struct ccu_div *div,
788c2ecf20Sopenharmony_ci				     unsigned long parent_rate,
798c2ecf20Sopenharmony_ci				     unsigned long divider)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	unsigned long nd;
828c2ecf20Sopenharmony_ci	u32 val = 0;
838c2ecf20Sopenharmony_ci	u32 lock;
848c2ecf20Sopenharmony_ci	int count;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	nd = ccu_div_lock_delay_ns(parent_rate, divider);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	if (div->features & CCU_DIV_LOCK_SHIFTED)
898c2ecf20Sopenharmony_ci		lock = CCU_DIV_CTL_LOCK_SHIFTED;
908c2ecf20Sopenharmony_ci	else
918c2ecf20Sopenharmony_ci		lock = CCU_DIV_CTL_LOCK_NORMAL;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	regmap_update_bits(div->sys_regs, div->reg_ctl,
948c2ecf20Sopenharmony_ci			   CCU_DIV_CTL_SET_CLKDIV, CCU_DIV_CTL_SET_CLKDIV);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	/*
978c2ecf20Sopenharmony_ci	 * Until there is nsec-version of readl_poll_timeout() is available
988c2ecf20Sopenharmony_ci	 * we have to implement the next polling loop.
998c2ecf20Sopenharmony_ci	 */
1008c2ecf20Sopenharmony_ci	count = CCU_DIV_LOCK_CHECK_RETRIES;
1018c2ecf20Sopenharmony_ci	do {
1028c2ecf20Sopenharmony_ci		ndelay(nd);
1038c2ecf20Sopenharmony_ci		regmap_read(div->sys_regs, div->reg_ctl, &val);
1048c2ecf20Sopenharmony_ci		if (val & lock)
1058c2ecf20Sopenharmony_ci			return 0;
1068c2ecf20Sopenharmony_ci	} while (--count);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	return -ETIMEDOUT;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic int ccu_div_var_enable(struct clk_hw *hw)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	struct clk_hw *parent_hw = clk_hw_get_parent(hw);
1148c2ecf20Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
1158c2ecf20Sopenharmony_ci	unsigned long flags;
1168c2ecf20Sopenharmony_ci	u32 val = 0;
1178c2ecf20Sopenharmony_ci	int ret;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	if (!parent_hw) {
1208c2ecf20Sopenharmony_ci		pr_err("Can't enable '%s' with no parent", clk_hw_get_name(hw));
1218c2ecf20Sopenharmony_ci		return -EINVAL;
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	regmap_read(div->sys_regs, div->reg_ctl, &val);
1258c2ecf20Sopenharmony_ci	if (val & CCU_DIV_CTL_EN)
1268c2ecf20Sopenharmony_ci		return 0;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	spin_lock_irqsave(&div->lock, flags);
1298c2ecf20Sopenharmony_ci	ret = ccu_div_var_update_clkdiv(div, clk_hw_get_rate(parent_hw),
1308c2ecf20Sopenharmony_ci					ccu_div_get(div->mask, val));
1318c2ecf20Sopenharmony_ci	if (!ret)
1328c2ecf20Sopenharmony_ci		regmap_update_bits(div->sys_regs, div->reg_ctl,
1338c2ecf20Sopenharmony_ci				   CCU_DIV_CTL_EN, CCU_DIV_CTL_EN);
1348c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&div->lock, flags);
1358c2ecf20Sopenharmony_ci	if (ret)
1368c2ecf20Sopenharmony_ci		pr_err("Divider '%s' lock timed out\n", clk_hw_get_name(hw));
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	return ret;
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic int ccu_div_gate_enable(struct clk_hw *hw)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
1448c2ecf20Sopenharmony_ci	unsigned long flags;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	spin_lock_irqsave(&div->lock, flags);
1478c2ecf20Sopenharmony_ci	regmap_update_bits(div->sys_regs, div->reg_ctl,
1488c2ecf20Sopenharmony_ci			   CCU_DIV_CTL_EN, CCU_DIV_CTL_EN);
1498c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&div->lock, flags);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	return 0;
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic void ccu_div_gate_disable(struct clk_hw *hw)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
1578c2ecf20Sopenharmony_ci	unsigned long flags;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	spin_lock_irqsave(&div->lock, flags);
1608c2ecf20Sopenharmony_ci	regmap_update_bits(div->sys_regs, div->reg_ctl, CCU_DIV_CTL_EN, 0);
1618c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&div->lock, flags);
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cistatic int ccu_div_gate_is_enabled(struct clk_hw *hw)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
1678c2ecf20Sopenharmony_ci	u32 val = 0;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	regmap_read(div->sys_regs, div->reg_ctl, &val);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	return !!(val & CCU_DIV_CTL_EN);
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistatic int ccu_div_buf_enable(struct clk_hw *hw)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
1778c2ecf20Sopenharmony_ci	unsigned long flags;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	spin_lock_irqsave(&div->lock, flags);
1808c2ecf20Sopenharmony_ci	regmap_update_bits(div->sys_regs, div->reg_ctl,
1818c2ecf20Sopenharmony_ci			   CCU_DIV_CTL_GATE_REF_BUF, 0);
1828c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&div->lock, flags);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	return 0;
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistatic void ccu_div_buf_disable(struct clk_hw *hw)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
1908c2ecf20Sopenharmony_ci	unsigned long flags;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	spin_lock_irqsave(&div->lock, flags);
1938c2ecf20Sopenharmony_ci	regmap_update_bits(div->sys_regs, div->reg_ctl,
1948c2ecf20Sopenharmony_ci			   CCU_DIV_CTL_GATE_REF_BUF, CCU_DIV_CTL_GATE_REF_BUF);
1958c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&div->lock, flags);
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic int ccu_div_buf_is_enabled(struct clk_hw *hw)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
2018c2ecf20Sopenharmony_ci	u32 val = 0;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	regmap_read(div->sys_regs, div->reg_ctl, &val);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	return !(val & CCU_DIV_CTL_GATE_REF_BUF);
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic unsigned long ccu_div_var_recalc_rate(struct clk_hw *hw,
2098c2ecf20Sopenharmony_ci					     unsigned long parent_rate)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
2128c2ecf20Sopenharmony_ci	unsigned long divider;
2138c2ecf20Sopenharmony_ci	u32 val = 0;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	regmap_read(div->sys_regs, div->reg_ctl, &val);
2168c2ecf20Sopenharmony_ci	divider = ccu_div_get(div->mask, val);
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	return ccu_div_calc_freq(parent_rate, divider);
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_cistatic inline unsigned long ccu_div_var_calc_divider(unsigned long rate,
2228c2ecf20Sopenharmony_ci						     unsigned long parent_rate,
2238c2ecf20Sopenharmony_ci						     unsigned int mask)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	unsigned long divider;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	divider = parent_rate / rate;
2288c2ecf20Sopenharmony_ci	return clamp_t(unsigned long, divider, CCU_DIV_CLKDIV_MIN,
2298c2ecf20Sopenharmony_ci		       CCU_DIV_CLKDIV_MAX(mask));
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistatic long ccu_div_var_round_rate(struct clk_hw *hw, unsigned long rate,
2338c2ecf20Sopenharmony_ci				   unsigned long *parent_rate)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
2368c2ecf20Sopenharmony_ci	unsigned long divider;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	divider = ccu_div_var_calc_divider(rate, *parent_rate, div->mask);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	return ccu_div_calc_freq(*parent_rate, divider);
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci/*
2448c2ecf20Sopenharmony_ci * This method is used for the clock divider blocks, which support the
2458c2ecf20Sopenharmony_ci * on-the-fly rate change. So due to lacking the EN bit functionality
2468c2ecf20Sopenharmony_ci * they can't be gated before the rate adjustment.
2478c2ecf20Sopenharmony_ci */
2488c2ecf20Sopenharmony_cistatic int ccu_div_var_set_rate_slow(struct clk_hw *hw, unsigned long rate,
2498c2ecf20Sopenharmony_ci				     unsigned long parent_rate)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
2528c2ecf20Sopenharmony_ci	unsigned long flags, divider;
2538c2ecf20Sopenharmony_ci	u32 val;
2548c2ecf20Sopenharmony_ci	int ret;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	divider = ccu_div_var_calc_divider(rate, parent_rate, div->mask);
2578c2ecf20Sopenharmony_ci	if (divider == 1 && div->features & CCU_DIV_SKIP_ONE) {
2588c2ecf20Sopenharmony_ci		divider = 0;
2598c2ecf20Sopenharmony_ci	} else if (div->features & CCU_DIV_SKIP_ONE_TO_THREE) {
2608c2ecf20Sopenharmony_ci		if (divider == 1 || divider == 2)
2618c2ecf20Sopenharmony_ci			divider = 0;
2628c2ecf20Sopenharmony_ci		else if (divider == 3)
2638c2ecf20Sopenharmony_ci			divider = 4;
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	val = ccu_div_prep(div->mask, divider);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	spin_lock_irqsave(&div->lock, flags);
2698c2ecf20Sopenharmony_ci	regmap_update_bits(div->sys_regs, div->reg_ctl, div->mask, val);
2708c2ecf20Sopenharmony_ci	ret = ccu_div_var_update_clkdiv(div, parent_rate, divider);
2718c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&div->lock, flags);
2728c2ecf20Sopenharmony_ci	if (ret)
2738c2ecf20Sopenharmony_ci		pr_err("Divider '%s' lock timed out\n", clk_hw_get_name(hw));
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	return ret;
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci/*
2798c2ecf20Sopenharmony_ci * This method is used for the clock divider blocks, which don't support
2808c2ecf20Sopenharmony_ci * the on-the-fly rate change.
2818c2ecf20Sopenharmony_ci */
2828c2ecf20Sopenharmony_cistatic int ccu_div_var_set_rate_fast(struct clk_hw *hw, unsigned long rate,
2838c2ecf20Sopenharmony_ci				     unsigned long parent_rate)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
2868c2ecf20Sopenharmony_ci	unsigned long flags, divider;
2878c2ecf20Sopenharmony_ci	u32 val;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	divider = ccu_div_var_calc_divider(rate, parent_rate, div->mask);
2908c2ecf20Sopenharmony_ci	val = ccu_div_prep(div->mask, divider);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	/*
2938c2ecf20Sopenharmony_ci	 * Also disable the clock divider block if it was enabled by default
2948c2ecf20Sopenharmony_ci	 * or by the bootloader.
2958c2ecf20Sopenharmony_ci	 */
2968c2ecf20Sopenharmony_ci	spin_lock_irqsave(&div->lock, flags);
2978c2ecf20Sopenharmony_ci	regmap_update_bits(div->sys_regs, div->reg_ctl,
2988c2ecf20Sopenharmony_ci			   div->mask | CCU_DIV_CTL_EN, val);
2998c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&div->lock, flags);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	return 0;
3028c2ecf20Sopenharmony_ci}
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_cistatic unsigned long ccu_div_fixed_recalc_rate(struct clk_hw *hw,
3058c2ecf20Sopenharmony_ci					       unsigned long parent_rate)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	return ccu_div_calc_freq(parent_rate, div->divider);
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic long ccu_div_fixed_round_rate(struct clk_hw *hw, unsigned long rate,
3138c2ecf20Sopenharmony_ci				     unsigned long *parent_rate)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	return ccu_div_calc_freq(*parent_rate, div->divider);
3188c2ecf20Sopenharmony_ci}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_cistatic int ccu_div_fixed_set_rate(struct clk_hw *hw, unsigned long rate,
3218c2ecf20Sopenharmony_ci				  unsigned long parent_rate)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	return 0;
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ciint ccu_div_reset_domain(struct ccu_div *div)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	unsigned long flags;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	if (!div || !(div->features & CCU_DIV_RESET_DOMAIN))
3318c2ecf20Sopenharmony_ci		return -EINVAL;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	spin_lock_irqsave(&div->lock, flags);
3348c2ecf20Sopenharmony_ci	regmap_update_bits(div->sys_regs, div->reg_ctl,
3358c2ecf20Sopenharmony_ci			   CCU_DIV_CTL_RST, CCU_DIV_CTL_RST);
3368c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&div->lock, flags);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	/* The next delay must be enough to cover all the resets. */
3398c2ecf20Sopenharmony_ci	udelay(CCU_DIV_RST_DELAY_US);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	return 0;
3428c2ecf20Sopenharmony_ci}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cistruct ccu_div_dbgfs_bit {
3478c2ecf20Sopenharmony_ci	struct ccu_div *div;
3488c2ecf20Sopenharmony_ci	const char *name;
3498c2ecf20Sopenharmony_ci	u32 mask;
3508c2ecf20Sopenharmony_ci};
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci#define CCU_DIV_DBGFS_BIT_ATTR(_name, _mask) {	\
3538c2ecf20Sopenharmony_ci		.name = _name,			\
3548c2ecf20Sopenharmony_ci		.mask = _mask			\
3558c2ecf20Sopenharmony_ci	}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cistatic const struct ccu_div_dbgfs_bit ccu_div_bits[] = {
3588c2ecf20Sopenharmony_ci	CCU_DIV_DBGFS_BIT_ATTR("div_en", CCU_DIV_CTL_EN),
3598c2ecf20Sopenharmony_ci	CCU_DIV_DBGFS_BIT_ATTR("div_rst", CCU_DIV_CTL_RST),
3608c2ecf20Sopenharmony_ci	CCU_DIV_DBGFS_BIT_ATTR("div_bypass", CCU_DIV_CTL_SET_CLKDIV),
3618c2ecf20Sopenharmony_ci	CCU_DIV_DBGFS_BIT_ATTR("div_buf", CCU_DIV_CTL_GATE_REF_BUF),
3628c2ecf20Sopenharmony_ci	CCU_DIV_DBGFS_BIT_ATTR("div_lock", CCU_DIV_CTL_LOCK_NORMAL)
3638c2ecf20Sopenharmony_ci};
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci#define CCU_DIV_DBGFS_BIT_NUM	ARRAY_SIZE(ccu_div_bits)
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci/*
3688c2ecf20Sopenharmony_ci * It can be dangerous to change the Divider settings behind clock framework
3698c2ecf20Sopenharmony_ci * back, therefore we don't provide any kernel config based compile time option
3708c2ecf20Sopenharmony_ci * for this feature to enable.
3718c2ecf20Sopenharmony_ci */
3728c2ecf20Sopenharmony_ci#undef CCU_DIV_ALLOW_WRITE_DEBUGFS
3738c2ecf20Sopenharmony_ci#ifdef CCU_DIV_ALLOW_WRITE_DEBUGFS
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_cistatic int ccu_div_dbgfs_bit_set(void *priv, u64 val)
3768c2ecf20Sopenharmony_ci{
3778c2ecf20Sopenharmony_ci	const struct ccu_div_dbgfs_bit *bit = priv;
3788c2ecf20Sopenharmony_ci	struct ccu_div *div = bit->div;
3798c2ecf20Sopenharmony_ci	unsigned long flags;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	spin_lock_irqsave(&div->lock, flags);
3828c2ecf20Sopenharmony_ci	regmap_update_bits(div->sys_regs, div->reg_ctl,
3838c2ecf20Sopenharmony_ci			   bit->mask, val ? bit->mask : 0);
3848c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&div->lock, flags);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	return 0;
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_cistatic int ccu_div_dbgfs_var_clkdiv_set(void *priv, u64 val)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	struct ccu_div *div = priv;
3928c2ecf20Sopenharmony_ci	unsigned long flags;
3938c2ecf20Sopenharmony_ci	u32 data;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	val = clamp_t(u64, val, CCU_DIV_CLKDIV_MIN,
3968c2ecf20Sopenharmony_ci		      CCU_DIV_CLKDIV_MAX(div->mask));
3978c2ecf20Sopenharmony_ci	data = ccu_div_prep(div->mask, val);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	spin_lock_irqsave(&div->lock, flags);
4008c2ecf20Sopenharmony_ci	regmap_update_bits(div->sys_regs, div->reg_ctl, div->mask, data);
4018c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&div->lock, flags);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	return 0;
4048c2ecf20Sopenharmony_ci}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci#define ccu_div_dbgfs_mode		0644
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci#else /* !CCU_DIV_ALLOW_WRITE_DEBUGFS */
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci#define ccu_div_dbgfs_bit_set		NULL
4118c2ecf20Sopenharmony_ci#define ccu_div_dbgfs_var_clkdiv_set	NULL
4128c2ecf20Sopenharmony_ci#define ccu_div_dbgfs_mode		0444
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci#endif /* !CCU_DIV_ALLOW_WRITE_DEBUGFS */
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_cistatic int ccu_div_dbgfs_bit_get(void *priv, u64 *val)
4178c2ecf20Sopenharmony_ci{
4188c2ecf20Sopenharmony_ci	const struct ccu_div_dbgfs_bit *bit = priv;
4198c2ecf20Sopenharmony_ci	struct ccu_div *div = bit->div;
4208c2ecf20Sopenharmony_ci	u32 data = 0;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	regmap_read(div->sys_regs, div->reg_ctl, &data);
4238c2ecf20Sopenharmony_ci	*val = !!(data & bit->mask);
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	return 0;
4268c2ecf20Sopenharmony_ci}
4278c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(ccu_div_dbgfs_bit_fops,
4288c2ecf20Sopenharmony_ci	ccu_div_dbgfs_bit_get, ccu_div_dbgfs_bit_set, "%llu\n");
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_cistatic int ccu_div_dbgfs_var_clkdiv_get(void *priv, u64 *val)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci	struct ccu_div *div = priv;
4338c2ecf20Sopenharmony_ci	u32 data = 0;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	regmap_read(div->sys_regs, div->reg_ctl, &data);
4368c2ecf20Sopenharmony_ci	*val = ccu_div_get(div->mask, data);
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	return 0;
4398c2ecf20Sopenharmony_ci}
4408c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(ccu_div_dbgfs_var_clkdiv_fops,
4418c2ecf20Sopenharmony_ci	ccu_div_dbgfs_var_clkdiv_get, ccu_div_dbgfs_var_clkdiv_set, "%llu\n");
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_cistatic int ccu_div_dbgfs_fixed_clkdiv_get(void *priv, u64 *val)
4448c2ecf20Sopenharmony_ci{
4458c2ecf20Sopenharmony_ci	struct ccu_div *div = priv;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	*val = div->divider;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	return 0;
4508c2ecf20Sopenharmony_ci}
4518c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(ccu_div_dbgfs_fixed_clkdiv_fops,
4528c2ecf20Sopenharmony_ci	ccu_div_dbgfs_fixed_clkdiv_get, NULL, "%llu\n");
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_cistatic void ccu_div_var_debug_init(struct clk_hw *hw, struct dentry *dentry)
4558c2ecf20Sopenharmony_ci{
4568c2ecf20Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
4578c2ecf20Sopenharmony_ci	struct ccu_div_dbgfs_bit *bits;
4588c2ecf20Sopenharmony_ci	int didx, bidx, num = 2;
4598c2ecf20Sopenharmony_ci	const char *name;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	num += !!(div->flags & CLK_SET_RATE_GATE) +
4628c2ecf20Sopenharmony_ci		!!(div->features & CCU_DIV_RESET_DOMAIN);
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	bits = kcalloc(num, sizeof(*bits), GFP_KERNEL);
4658c2ecf20Sopenharmony_ci	if (!bits)
4668c2ecf20Sopenharmony_ci		return;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	for (didx = 0, bidx = 0; bidx < CCU_DIV_DBGFS_BIT_NUM; ++bidx) {
4698c2ecf20Sopenharmony_ci		name = ccu_div_bits[bidx].name;
4708c2ecf20Sopenharmony_ci		if (!(div->flags & CLK_SET_RATE_GATE) &&
4718c2ecf20Sopenharmony_ci		    !strcmp("div_en", name)) {
4728c2ecf20Sopenharmony_ci			continue;
4738c2ecf20Sopenharmony_ci		}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci		if (!(div->features & CCU_DIV_RESET_DOMAIN) &&
4768c2ecf20Sopenharmony_ci		    !strcmp("div_rst", name)) {
4778c2ecf20Sopenharmony_ci			continue;
4788c2ecf20Sopenharmony_ci		}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci		if (!strcmp("div_buf", name))
4818c2ecf20Sopenharmony_ci			continue;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci		bits[didx] = ccu_div_bits[bidx];
4848c2ecf20Sopenharmony_ci		bits[didx].div = div;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci		if (div->features & CCU_DIV_LOCK_SHIFTED &&
4878c2ecf20Sopenharmony_ci		    !strcmp("div_lock", name)) {
4888c2ecf20Sopenharmony_ci			bits[didx].mask = CCU_DIV_CTL_LOCK_SHIFTED;
4898c2ecf20Sopenharmony_ci		}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci		debugfs_create_file_unsafe(bits[didx].name, ccu_div_dbgfs_mode,
4928c2ecf20Sopenharmony_ci					   dentry, &bits[didx],
4938c2ecf20Sopenharmony_ci					   &ccu_div_dbgfs_bit_fops);
4948c2ecf20Sopenharmony_ci		++didx;
4958c2ecf20Sopenharmony_ci	}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	debugfs_create_file_unsafe("div_clkdiv", ccu_div_dbgfs_mode, dentry,
4988c2ecf20Sopenharmony_ci				   div, &ccu_div_dbgfs_var_clkdiv_fops);
4998c2ecf20Sopenharmony_ci}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_cistatic void ccu_div_gate_debug_init(struct clk_hw *hw, struct dentry *dentry)
5028c2ecf20Sopenharmony_ci{
5038c2ecf20Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
5048c2ecf20Sopenharmony_ci	struct ccu_div_dbgfs_bit *bit;
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	bit = kmalloc(sizeof(*bit), GFP_KERNEL);
5078c2ecf20Sopenharmony_ci	if (!bit)
5088c2ecf20Sopenharmony_ci		return;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	*bit = ccu_div_bits[0];
5118c2ecf20Sopenharmony_ci	bit->div = div;
5128c2ecf20Sopenharmony_ci	debugfs_create_file_unsafe(bit->name, ccu_div_dbgfs_mode, dentry, bit,
5138c2ecf20Sopenharmony_ci				   &ccu_div_dbgfs_bit_fops);
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	debugfs_create_file_unsafe("div_clkdiv", 0400, dentry, div,
5168c2ecf20Sopenharmony_ci				   &ccu_div_dbgfs_fixed_clkdiv_fops);
5178c2ecf20Sopenharmony_ci}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_cistatic void ccu_div_buf_debug_init(struct clk_hw *hw, struct dentry *dentry)
5208c2ecf20Sopenharmony_ci{
5218c2ecf20Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
5228c2ecf20Sopenharmony_ci	struct ccu_div_dbgfs_bit *bit;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	bit = kmalloc(sizeof(*bit), GFP_KERNEL);
5258c2ecf20Sopenharmony_ci	if (!bit)
5268c2ecf20Sopenharmony_ci		return;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	*bit = ccu_div_bits[3];
5298c2ecf20Sopenharmony_ci	bit->div = div;
5308c2ecf20Sopenharmony_ci	debugfs_create_file_unsafe(bit->name, ccu_div_dbgfs_mode, dentry, bit,
5318c2ecf20Sopenharmony_ci				   &ccu_div_dbgfs_bit_fops);
5328c2ecf20Sopenharmony_ci}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_cistatic void ccu_div_fixed_debug_init(struct clk_hw *hw, struct dentry *dentry)
5358c2ecf20Sopenharmony_ci{
5368c2ecf20Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	debugfs_create_file_unsafe("div_clkdiv", 0400, dentry, div,
5398c2ecf20Sopenharmony_ci				   &ccu_div_dbgfs_fixed_clkdiv_fops);
5408c2ecf20Sopenharmony_ci}
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci#else /* !CONFIG_DEBUG_FS */
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci#define ccu_div_var_debug_init NULL
5458c2ecf20Sopenharmony_ci#define ccu_div_gate_debug_init NULL
5468c2ecf20Sopenharmony_ci#define ccu_div_buf_debug_init NULL
5478c2ecf20Sopenharmony_ci#define ccu_div_fixed_debug_init NULL
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci#endif /* !CONFIG_DEBUG_FS */
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_cistatic const struct clk_ops ccu_div_var_gate_to_set_ops = {
5528c2ecf20Sopenharmony_ci	.enable = ccu_div_var_enable,
5538c2ecf20Sopenharmony_ci	.disable = ccu_div_gate_disable,
5548c2ecf20Sopenharmony_ci	.is_enabled = ccu_div_gate_is_enabled,
5558c2ecf20Sopenharmony_ci	.recalc_rate = ccu_div_var_recalc_rate,
5568c2ecf20Sopenharmony_ci	.round_rate = ccu_div_var_round_rate,
5578c2ecf20Sopenharmony_ci	.set_rate = ccu_div_var_set_rate_fast,
5588c2ecf20Sopenharmony_ci	.debug_init = ccu_div_var_debug_init
5598c2ecf20Sopenharmony_ci};
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_cistatic const struct clk_ops ccu_div_var_nogate_ops = {
5628c2ecf20Sopenharmony_ci	.recalc_rate = ccu_div_var_recalc_rate,
5638c2ecf20Sopenharmony_ci	.round_rate = ccu_div_var_round_rate,
5648c2ecf20Sopenharmony_ci	.set_rate = ccu_div_var_set_rate_slow,
5658c2ecf20Sopenharmony_ci	.debug_init = ccu_div_var_debug_init
5668c2ecf20Sopenharmony_ci};
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_cistatic const struct clk_ops ccu_div_gate_ops = {
5698c2ecf20Sopenharmony_ci	.enable = ccu_div_gate_enable,
5708c2ecf20Sopenharmony_ci	.disable = ccu_div_gate_disable,
5718c2ecf20Sopenharmony_ci	.is_enabled = ccu_div_gate_is_enabled,
5728c2ecf20Sopenharmony_ci	.recalc_rate = ccu_div_fixed_recalc_rate,
5738c2ecf20Sopenharmony_ci	.round_rate = ccu_div_fixed_round_rate,
5748c2ecf20Sopenharmony_ci	.set_rate = ccu_div_fixed_set_rate,
5758c2ecf20Sopenharmony_ci	.debug_init = ccu_div_gate_debug_init
5768c2ecf20Sopenharmony_ci};
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_cistatic const struct clk_ops ccu_div_buf_ops = {
5798c2ecf20Sopenharmony_ci	.enable = ccu_div_buf_enable,
5808c2ecf20Sopenharmony_ci	.disable = ccu_div_buf_disable,
5818c2ecf20Sopenharmony_ci	.is_enabled = ccu_div_buf_is_enabled,
5828c2ecf20Sopenharmony_ci	.debug_init = ccu_div_buf_debug_init
5838c2ecf20Sopenharmony_ci};
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_cistatic const struct clk_ops ccu_div_fixed_ops = {
5868c2ecf20Sopenharmony_ci	.recalc_rate = ccu_div_fixed_recalc_rate,
5878c2ecf20Sopenharmony_ci	.round_rate = ccu_div_fixed_round_rate,
5888c2ecf20Sopenharmony_ci	.set_rate = ccu_div_fixed_set_rate,
5898c2ecf20Sopenharmony_ci	.debug_init = ccu_div_fixed_debug_init
5908c2ecf20Sopenharmony_ci};
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_cistruct ccu_div *ccu_div_hw_register(const struct ccu_div_init_data *div_init)
5938c2ecf20Sopenharmony_ci{
5948c2ecf20Sopenharmony_ci	struct clk_parent_data parent_data = { };
5958c2ecf20Sopenharmony_ci	struct clk_init_data hw_init = { };
5968c2ecf20Sopenharmony_ci	struct ccu_div *div;
5978c2ecf20Sopenharmony_ci	int ret;
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	if (!div_init)
6008c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	div = kzalloc(sizeof(*div), GFP_KERNEL);
6038c2ecf20Sopenharmony_ci	if (!div)
6048c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	/*
6078c2ecf20Sopenharmony_ci	 * Note since Baikal-T1 System Controller registers are MMIO-backed
6088c2ecf20Sopenharmony_ci	 * we won't check the regmap IO operations return status, because it
6098c2ecf20Sopenharmony_ci	 * must be zero anyway.
6108c2ecf20Sopenharmony_ci	 */
6118c2ecf20Sopenharmony_ci	div->hw.init = &hw_init;
6128c2ecf20Sopenharmony_ci	div->id = div_init->id;
6138c2ecf20Sopenharmony_ci	div->reg_ctl = div_init->base + CCU_DIV_CTL;
6148c2ecf20Sopenharmony_ci	div->sys_regs = div_init->sys_regs;
6158c2ecf20Sopenharmony_ci	div->flags = div_init->flags;
6168c2ecf20Sopenharmony_ci	div->features = div_init->features;
6178c2ecf20Sopenharmony_ci	spin_lock_init(&div->lock);
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	hw_init.name = div_init->name;
6208c2ecf20Sopenharmony_ci	hw_init.flags = div_init->flags;
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	if (div_init->type == CCU_DIV_VAR) {
6238c2ecf20Sopenharmony_ci		if (hw_init.flags & CLK_SET_RATE_GATE)
6248c2ecf20Sopenharmony_ci			hw_init.ops = &ccu_div_var_gate_to_set_ops;
6258c2ecf20Sopenharmony_ci		else
6268c2ecf20Sopenharmony_ci			hw_init.ops = &ccu_div_var_nogate_ops;
6278c2ecf20Sopenharmony_ci		div->mask = CCU_DIV_CTL_CLKDIV_MASK(div_init->width);
6288c2ecf20Sopenharmony_ci	} else if (div_init->type == CCU_DIV_GATE) {
6298c2ecf20Sopenharmony_ci		hw_init.ops = &ccu_div_gate_ops;
6308c2ecf20Sopenharmony_ci		div->divider = div_init->divider;
6318c2ecf20Sopenharmony_ci	} else if (div_init->type == CCU_DIV_BUF) {
6328c2ecf20Sopenharmony_ci		hw_init.ops = &ccu_div_buf_ops;
6338c2ecf20Sopenharmony_ci	} else if (div_init->type == CCU_DIV_FIXED) {
6348c2ecf20Sopenharmony_ci		hw_init.ops = &ccu_div_fixed_ops;
6358c2ecf20Sopenharmony_ci		div->divider = div_init->divider;
6368c2ecf20Sopenharmony_ci	} else {
6378c2ecf20Sopenharmony_ci		ret = -EINVAL;
6388c2ecf20Sopenharmony_ci		goto err_free_div;
6398c2ecf20Sopenharmony_ci	}
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	if (!div_init->parent_name) {
6428c2ecf20Sopenharmony_ci		ret = -EINVAL;
6438c2ecf20Sopenharmony_ci		goto err_free_div;
6448c2ecf20Sopenharmony_ci	}
6458c2ecf20Sopenharmony_ci	parent_data.fw_name = div_init->parent_name;
6468c2ecf20Sopenharmony_ci	parent_data.name = div_init->parent_name;
6478c2ecf20Sopenharmony_ci	hw_init.parent_data = &parent_data;
6488c2ecf20Sopenharmony_ci	hw_init.num_parents = 1;
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	ret = of_clk_hw_register(div_init->np, &div->hw);
6518c2ecf20Sopenharmony_ci	if (ret)
6528c2ecf20Sopenharmony_ci		goto err_free_div;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	return div;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_cierr_free_div:
6578c2ecf20Sopenharmony_ci	kfree(div);
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	return ERR_PTR(ret);
6608c2ecf20Sopenharmony_ci}
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_civoid ccu_div_hw_unregister(struct ccu_div *div)
6638c2ecf20Sopenharmony_ci{
6648c2ecf20Sopenharmony_ci	clk_hw_unregister(&div->hw);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	kfree(div);
6678c2ecf20Sopenharmony_ci}
668