18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Purna Chandra Mandal,<purna.mandal@microchip.com>
48c2ecf20Sopenharmony_ci * Copyright (C) 2015 Microchip Technology Inc.  All rights reserved.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
78c2ecf20Sopenharmony_ci#include <linux/delay.h>
88c2ecf20Sopenharmony_ci#include <linux/device.h>
98c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
108c2ecf20Sopenharmony_ci#include <linux/io.h>
118c2ecf20Sopenharmony_ci#include <linux/iopoll.h>
128c2ecf20Sopenharmony_ci#include <asm/mach-pic32/pic32.h>
138c2ecf20Sopenharmony_ci#include <asm/traps.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include "clk-core.h"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/* OSCCON Reg fields */
188c2ecf20Sopenharmony_ci#define OSC_CUR_MASK		0x07
198c2ecf20Sopenharmony_ci#define OSC_CUR_SHIFT		12
208c2ecf20Sopenharmony_ci#define OSC_NEW_MASK		0x07
218c2ecf20Sopenharmony_ci#define OSC_NEW_SHIFT		8
228c2ecf20Sopenharmony_ci#define OSC_SWEN		BIT(0)
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/* SPLLCON Reg fields */
258c2ecf20Sopenharmony_ci#define PLL_RANGE_MASK		0x07
268c2ecf20Sopenharmony_ci#define PLL_RANGE_SHIFT		0
278c2ecf20Sopenharmony_ci#define PLL_ICLK_MASK		0x01
288c2ecf20Sopenharmony_ci#define PLL_ICLK_SHIFT		7
298c2ecf20Sopenharmony_ci#define PLL_IDIV_MASK		0x07
308c2ecf20Sopenharmony_ci#define PLL_IDIV_SHIFT		8
318c2ecf20Sopenharmony_ci#define PLL_ODIV_MASK		0x07
328c2ecf20Sopenharmony_ci#define PLL_ODIV_SHIFT		24
338c2ecf20Sopenharmony_ci#define PLL_MULT_MASK		0x7F
348c2ecf20Sopenharmony_ci#define PLL_MULT_SHIFT		16
358c2ecf20Sopenharmony_ci#define PLL_MULT_MAX		128
368c2ecf20Sopenharmony_ci#define PLL_ODIV_MIN		1
378c2ecf20Sopenharmony_ci#define PLL_ODIV_MAX		5
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci/* Peripheral Bus Clock Reg Fields */
408c2ecf20Sopenharmony_ci#define PB_DIV_MASK		0x7f
418c2ecf20Sopenharmony_ci#define PB_DIV_SHIFT		0
428c2ecf20Sopenharmony_ci#define PB_DIV_READY		BIT(11)
438c2ecf20Sopenharmony_ci#define PB_DIV_ENABLE		BIT(15)
448c2ecf20Sopenharmony_ci#define PB_DIV_MAX		128
458c2ecf20Sopenharmony_ci#define PB_DIV_MIN		0
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* Reference Oscillator Control Reg fields */
488c2ecf20Sopenharmony_ci#define REFO_SEL_MASK		0x0f
498c2ecf20Sopenharmony_ci#define REFO_SEL_SHIFT		0
508c2ecf20Sopenharmony_ci#define REFO_ACTIVE		BIT(8)
518c2ecf20Sopenharmony_ci#define REFO_DIVSW_EN		BIT(9)
528c2ecf20Sopenharmony_ci#define REFO_OE			BIT(12)
538c2ecf20Sopenharmony_ci#define REFO_ON			BIT(15)
548c2ecf20Sopenharmony_ci#define REFO_DIV_SHIFT		16
558c2ecf20Sopenharmony_ci#define REFO_DIV_MASK		0x7fff
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci/* Reference Oscillator Trim Register Fields */
588c2ecf20Sopenharmony_ci#define REFO_TRIM_REG		0x10
598c2ecf20Sopenharmony_ci#define REFO_TRIM_MASK		0x1ff
608c2ecf20Sopenharmony_ci#define REFO_TRIM_SHIFT		23
618c2ecf20Sopenharmony_ci#define REFO_TRIM_MAX		511
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci/* Mux Slew Control Register fields */
648c2ecf20Sopenharmony_ci#define SLEW_BUSY		BIT(0)
658c2ecf20Sopenharmony_ci#define SLEW_DOWNEN		BIT(1)
668c2ecf20Sopenharmony_ci#define SLEW_UPEN		BIT(2)
678c2ecf20Sopenharmony_ci#define SLEW_DIV		0x07
688c2ecf20Sopenharmony_ci#define SLEW_DIV_SHIFT		8
698c2ecf20Sopenharmony_ci#define SLEW_SYSDIV		0x0f
708c2ecf20Sopenharmony_ci#define SLEW_SYSDIV_SHIFT	20
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci/* Clock Poll Timeout */
738c2ecf20Sopenharmony_ci#define LOCK_TIMEOUT_US         USEC_PER_MSEC
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci/* SoC specific clock needed during SPLL clock rate switch */
768c2ecf20Sopenharmony_cistatic struct clk_hw *pic32_sclk_hw;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci/* add instruction pipeline delay while CPU clock is in-transition. */
798c2ecf20Sopenharmony_ci#define cpu_nop5()			\
808c2ecf20Sopenharmony_cido {					\
818c2ecf20Sopenharmony_ci	__asm__ __volatile__("nop");	\
828c2ecf20Sopenharmony_ci	__asm__ __volatile__("nop");	\
838c2ecf20Sopenharmony_ci	__asm__ __volatile__("nop");	\
848c2ecf20Sopenharmony_ci	__asm__ __volatile__("nop");	\
858c2ecf20Sopenharmony_ci	__asm__ __volatile__("nop");	\
868c2ecf20Sopenharmony_ci} while (0)
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci/* Perpheral bus clocks */
898c2ecf20Sopenharmony_cistruct pic32_periph_clk {
908c2ecf20Sopenharmony_ci	struct clk_hw hw;
918c2ecf20Sopenharmony_ci	void __iomem *ctrl_reg;
928c2ecf20Sopenharmony_ci	struct pic32_clk_common *core;
938c2ecf20Sopenharmony_ci};
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci#define clkhw_to_pbclk(_hw)	container_of(_hw, struct pic32_periph_clk, hw)
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic int pbclk_is_enabled(struct clk_hw *hw)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	struct pic32_periph_clk *pb = clkhw_to_pbclk(hw);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	return readl(pb->ctrl_reg) & PB_DIV_ENABLE;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic int pbclk_enable(struct clk_hw *hw)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	struct pic32_periph_clk *pb = clkhw_to_pbclk(hw);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	writel(PB_DIV_ENABLE, PIC32_SET(pb->ctrl_reg));
1098c2ecf20Sopenharmony_ci	return 0;
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic void pbclk_disable(struct clk_hw *hw)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	struct pic32_periph_clk *pb = clkhw_to_pbclk(hw);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	writel(PB_DIV_ENABLE, PIC32_CLR(pb->ctrl_reg));
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic unsigned long calc_best_divided_rate(unsigned long rate,
1208c2ecf20Sopenharmony_ci					    unsigned long parent_rate,
1218c2ecf20Sopenharmony_ci					    u32 divider_max,
1228c2ecf20Sopenharmony_ci					    u32 divider_min)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	unsigned long divided_rate, divided_rate_down, best_rate;
1258c2ecf20Sopenharmony_ci	unsigned long div, div_up;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	/* eq. clk_rate = parent_rate / divider.
1288c2ecf20Sopenharmony_ci	 *
1298c2ecf20Sopenharmony_ci	 * Find best divider to produce closest of target divided rate.
1308c2ecf20Sopenharmony_ci	 */
1318c2ecf20Sopenharmony_ci	div = parent_rate / rate;
1328c2ecf20Sopenharmony_ci	div = clamp_val(div, divider_min, divider_max);
1338c2ecf20Sopenharmony_ci	div_up = clamp_val(div + 1, divider_min, divider_max);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	divided_rate = parent_rate / div;
1368c2ecf20Sopenharmony_ci	divided_rate_down = parent_rate / div_up;
1378c2ecf20Sopenharmony_ci	if (abs(rate - divided_rate_down) < abs(rate - divided_rate))
1388c2ecf20Sopenharmony_ci		best_rate = divided_rate_down;
1398c2ecf20Sopenharmony_ci	else
1408c2ecf20Sopenharmony_ci		best_rate = divided_rate;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	return best_rate;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic inline u32 pbclk_read_pbdiv(struct pic32_periph_clk *pb)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	return ((readl(pb->ctrl_reg) >> PB_DIV_SHIFT) & PB_DIV_MASK) + 1;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic unsigned long pbclk_recalc_rate(struct clk_hw *hw,
1518c2ecf20Sopenharmony_ci				       unsigned long parent_rate)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	struct pic32_periph_clk *pb = clkhw_to_pbclk(hw);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	return parent_rate / pbclk_read_pbdiv(pb);
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic long pbclk_round_rate(struct clk_hw *hw, unsigned long rate,
1598c2ecf20Sopenharmony_ci			     unsigned long *parent_rate)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	return calc_best_divided_rate(rate, *parent_rate,
1628c2ecf20Sopenharmony_ci				      PB_DIV_MAX, PB_DIV_MIN);
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic int pbclk_set_rate(struct clk_hw *hw, unsigned long rate,
1668c2ecf20Sopenharmony_ci			  unsigned long parent_rate)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	struct pic32_periph_clk *pb = clkhw_to_pbclk(hw);
1698c2ecf20Sopenharmony_ci	unsigned long flags;
1708c2ecf20Sopenharmony_ci	u32 v, div;
1718c2ecf20Sopenharmony_ci	int err;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	/* check & wait for DIV_READY */
1748c2ecf20Sopenharmony_ci	err = readl_poll_timeout(pb->ctrl_reg, v, v & PB_DIV_READY,
1758c2ecf20Sopenharmony_ci				 1, LOCK_TIMEOUT_US);
1768c2ecf20Sopenharmony_ci	if (err)
1778c2ecf20Sopenharmony_ci		return err;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	/* calculate clkdiv and best rate */
1808c2ecf20Sopenharmony_ci	div = DIV_ROUND_CLOSEST(parent_rate, rate);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	spin_lock_irqsave(&pb->core->reg_lock, flags);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	/* apply new div */
1858c2ecf20Sopenharmony_ci	v = readl(pb->ctrl_reg);
1868c2ecf20Sopenharmony_ci	v &= ~PB_DIV_MASK;
1878c2ecf20Sopenharmony_ci	v |= (div - 1);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	pic32_syskey_unlock();
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	writel(v, pb->ctrl_reg);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&pb->core->reg_lock, flags);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	/* wait again for DIV_READY */
1968c2ecf20Sopenharmony_ci	err = readl_poll_timeout(pb->ctrl_reg, v, v & PB_DIV_READY,
1978c2ecf20Sopenharmony_ci				 1, LOCK_TIMEOUT_US);
1988c2ecf20Sopenharmony_ci	if (err)
1998c2ecf20Sopenharmony_ci		return err;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	/* confirm that new div is applied correctly */
2028c2ecf20Sopenharmony_ci	return (pbclk_read_pbdiv(pb) == div) ? 0 : -EBUSY;
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ciconst struct clk_ops pic32_pbclk_ops = {
2068c2ecf20Sopenharmony_ci	.enable		= pbclk_enable,
2078c2ecf20Sopenharmony_ci	.disable	= pbclk_disable,
2088c2ecf20Sopenharmony_ci	.is_enabled	= pbclk_is_enabled,
2098c2ecf20Sopenharmony_ci	.recalc_rate	= pbclk_recalc_rate,
2108c2ecf20Sopenharmony_ci	.round_rate	= pbclk_round_rate,
2118c2ecf20Sopenharmony_ci	.set_rate	= pbclk_set_rate,
2128c2ecf20Sopenharmony_ci};
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistruct clk *pic32_periph_clk_register(const struct pic32_periph_clk_data *desc,
2158c2ecf20Sopenharmony_ci				      struct pic32_clk_common *core)
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci	struct pic32_periph_clk *pbclk;
2188c2ecf20Sopenharmony_ci	struct clk *clk;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	pbclk = devm_kzalloc(core->dev, sizeof(*pbclk), GFP_KERNEL);
2218c2ecf20Sopenharmony_ci	if (!pbclk)
2228c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	pbclk->hw.init = &desc->init_data;
2258c2ecf20Sopenharmony_ci	pbclk->core = core;
2268c2ecf20Sopenharmony_ci	pbclk->ctrl_reg = desc->ctrl_reg + core->iobase;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	clk = devm_clk_register(core->dev, &pbclk->hw);
2298c2ecf20Sopenharmony_ci	if (IS_ERR(clk)) {
2308c2ecf20Sopenharmony_ci		dev_err(core->dev, "%s: clk_register() failed\n", __func__);
2318c2ecf20Sopenharmony_ci		devm_kfree(core->dev, pbclk);
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	return clk;
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci/* Reference oscillator operations */
2388c2ecf20Sopenharmony_cistruct pic32_ref_osc {
2398c2ecf20Sopenharmony_ci	struct clk_hw hw;
2408c2ecf20Sopenharmony_ci	void __iomem *ctrl_reg;
2418c2ecf20Sopenharmony_ci	const u32 *parent_map;
2428c2ecf20Sopenharmony_ci	struct pic32_clk_common *core;
2438c2ecf20Sopenharmony_ci};
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci#define clkhw_to_refosc(_hw)	container_of(_hw, struct pic32_ref_osc, hw)
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistatic int roclk_is_enabled(struct clk_hw *hw)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	struct pic32_ref_osc *refo = clkhw_to_refosc(hw);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	return readl(refo->ctrl_reg) & REFO_ON;
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_cistatic int roclk_enable(struct clk_hw *hw)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	struct pic32_ref_osc *refo = clkhw_to_refosc(hw);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	writel(REFO_ON | REFO_OE, PIC32_SET(refo->ctrl_reg));
2598c2ecf20Sopenharmony_ci	return 0;
2608c2ecf20Sopenharmony_ci}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_cistatic void roclk_disable(struct clk_hw *hw)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	struct pic32_ref_osc *refo = clkhw_to_refosc(hw);
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	writel(REFO_ON | REFO_OE, PIC32_CLR(refo->ctrl_reg));
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cistatic int roclk_init(struct clk_hw *hw)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	/* initialize clock in disabled state */
2728c2ecf20Sopenharmony_ci	roclk_disable(hw);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	return 0;
2758c2ecf20Sopenharmony_ci}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_cistatic u8 roclk_get_parent(struct clk_hw *hw)
2788c2ecf20Sopenharmony_ci{
2798c2ecf20Sopenharmony_ci	struct pic32_ref_osc *refo = clkhw_to_refosc(hw);
2808c2ecf20Sopenharmony_ci	u32 v, i;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	v = (readl(refo->ctrl_reg) >> REFO_SEL_SHIFT) & REFO_SEL_MASK;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	if (!refo->parent_map)
2858c2ecf20Sopenharmony_ci		return v;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	for (i = 0; i < clk_hw_get_num_parents(hw); i++)
2888c2ecf20Sopenharmony_ci		if (refo->parent_map[i] == v)
2898c2ecf20Sopenharmony_ci			return i;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	return -EINVAL;
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cistatic unsigned long roclk_calc_rate(unsigned long parent_rate,
2958c2ecf20Sopenharmony_ci				     u32 rodiv, u32 rotrim)
2968c2ecf20Sopenharmony_ci{
2978c2ecf20Sopenharmony_ci	u64 rate64;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	/* fout = fin / [2 * {div + (trim / 512)}]
3008c2ecf20Sopenharmony_ci	 *	= fin * 512 / [1024 * div + 2 * trim]
3018c2ecf20Sopenharmony_ci	 *	= fin * 256 / (512 * div + trim)
3028c2ecf20Sopenharmony_ci	 *	= (fin << 8) / ((div << 9) + trim)
3038c2ecf20Sopenharmony_ci	 */
3048c2ecf20Sopenharmony_ci	if (rotrim) {
3058c2ecf20Sopenharmony_ci		rodiv = (rodiv << 9) + rotrim;
3068c2ecf20Sopenharmony_ci		rate64 = parent_rate;
3078c2ecf20Sopenharmony_ci		rate64 <<= 8;
3088c2ecf20Sopenharmony_ci		do_div(rate64, rodiv);
3098c2ecf20Sopenharmony_ci	} else if (rodiv) {
3108c2ecf20Sopenharmony_ci		rate64 = parent_rate / (rodiv << 1);
3118c2ecf20Sopenharmony_ci	} else {
3128c2ecf20Sopenharmony_ci		rate64 = parent_rate;
3138c2ecf20Sopenharmony_ci	}
3148c2ecf20Sopenharmony_ci	return rate64;
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cistatic void roclk_calc_div_trim(unsigned long rate,
3188c2ecf20Sopenharmony_ci				unsigned long parent_rate,
3198c2ecf20Sopenharmony_ci				u32 *rodiv_p, u32 *rotrim_p)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	u32 div, rotrim, rodiv;
3228c2ecf20Sopenharmony_ci	u64 frac;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	/* Find integer approximation of floating-point arithmetic.
3258c2ecf20Sopenharmony_ci	 *      fout = fin / [2 * {rodiv + (rotrim / 512)}] ... (1)
3268c2ecf20Sopenharmony_ci	 * i.e. fout = fin / 2 * DIV
3278c2ecf20Sopenharmony_ci	 *      whereas DIV = rodiv + (rotrim / 512)
3288c2ecf20Sopenharmony_ci	 *
3298c2ecf20Sopenharmony_ci	 * Since kernel does not perform floating-point arithmatic so
3308c2ecf20Sopenharmony_ci	 * (rotrim/512) will be zero. And DIV & rodiv will result same.
3318c2ecf20Sopenharmony_ci	 *
3328c2ecf20Sopenharmony_ci	 * ie. fout = (fin * 256) / [(512 * rodiv) + rotrim]  ... from (1)
3338c2ecf20Sopenharmony_ci	 * ie. rotrim = ((fin * 256) / fout) - (512 * DIV)
3348c2ecf20Sopenharmony_ci	 */
3358c2ecf20Sopenharmony_ci	if (parent_rate <= rate) {
3368c2ecf20Sopenharmony_ci		div = 0;
3378c2ecf20Sopenharmony_ci		frac = 0;
3388c2ecf20Sopenharmony_ci		rodiv = 0;
3398c2ecf20Sopenharmony_ci		rotrim = 0;
3408c2ecf20Sopenharmony_ci	} else {
3418c2ecf20Sopenharmony_ci		div = parent_rate / (rate << 1);
3428c2ecf20Sopenharmony_ci		frac = parent_rate;
3438c2ecf20Sopenharmony_ci		frac <<= 8;
3448c2ecf20Sopenharmony_ci		do_div(frac, rate);
3458c2ecf20Sopenharmony_ci		frac -= (u64)(div << 9);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci		rodiv = (div > REFO_DIV_MASK) ? REFO_DIV_MASK : div;
3488c2ecf20Sopenharmony_ci		rotrim = (frac >= REFO_TRIM_MAX) ? REFO_TRIM_MAX : frac;
3498c2ecf20Sopenharmony_ci	}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	if (rodiv_p)
3528c2ecf20Sopenharmony_ci		*rodiv_p = rodiv;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	if (rotrim_p)
3558c2ecf20Sopenharmony_ci		*rotrim_p = rotrim;
3568c2ecf20Sopenharmony_ci}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_cistatic unsigned long roclk_recalc_rate(struct clk_hw *hw,
3598c2ecf20Sopenharmony_ci				       unsigned long parent_rate)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	struct pic32_ref_osc *refo = clkhw_to_refosc(hw);
3628c2ecf20Sopenharmony_ci	u32 v, rodiv, rotrim;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	/* get rodiv */
3658c2ecf20Sopenharmony_ci	v = readl(refo->ctrl_reg);
3668c2ecf20Sopenharmony_ci	rodiv = (v >> REFO_DIV_SHIFT) & REFO_DIV_MASK;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	/* get trim */
3698c2ecf20Sopenharmony_ci	v = readl(refo->ctrl_reg + REFO_TRIM_REG);
3708c2ecf20Sopenharmony_ci	rotrim = (v >> REFO_TRIM_SHIFT) & REFO_TRIM_MASK;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	return roclk_calc_rate(parent_rate, rodiv, rotrim);
3738c2ecf20Sopenharmony_ci}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_cistatic long roclk_round_rate(struct clk_hw *hw, unsigned long rate,
3768c2ecf20Sopenharmony_ci			     unsigned long *parent_rate)
3778c2ecf20Sopenharmony_ci{
3788c2ecf20Sopenharmony_ci	u32 rotrim, rodiv;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	/* calculate dividers for new rate */
3818c2ecf20Sopenharmony_ci	roclk_calc_div_trim(rate, *parent_rate, &rodiv, &rotrim);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	/* caclulate new rate (rounding) based on new rodiv & rotrim */
3848c2ecf20Sopenharmony_ci	return roclk_calc_rate(*parent_rate, rodiv, rotrim);
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_cistatic int roclk_determine_rate(struct clk_hw *hw,
3888c2ecf20Sopenharmony_ci				struct clk_rate_request *req)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	struct clk_hw *parent_clk, *best_parent_clk = NULL;
3918c2ecf20Sopenharmony_ci	unsigned int i, delta, best_delta = -1;
3928c2ecf20Sopenharmony_ci	unsigned long parent_rate, best_parent_rate = 0;
3938c2ecf20Sopenharmony_ci	unsigned long best = 0, nearest_rate;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	/* find a parent which can generate nearest clkrate >= rate */
3968c2ecf20Sopenharmony_ci	for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
3978c2ecf20Sopenharmony_ci		/* get parent */
3988c2ecf20Sopenharmony_ci		parent_clk = clk_hw_get_parent_by_index(hw, i);
3998c2ecf20Sopenharmony_ci		if (!parent_clk)
4008c2ecf20Sopenharmony_ci			continue;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci		/* skip if parent runs slower than target rate */
4038c2ecf20Sopenharmony_ci		parent_rate = clk_hw_get_rate(parent_clk);
4048c2ecf20Sopenharmony_ci		if (req->rate > parent_rate)
4058c2ecf20Sopenharmony_ci			continue;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci		nearest_rate = roclk_round_rate(hw, req->rate, &parent_rate);
4088c2ecf20Sopenharmony_ci		delta = abs(nearest_rate - req->rate);
4098c2ecf20Sopenharmony_ci		if ((nearest_rate >= req->rate) && (delta < best_delta)) {
4108c2ecf20Sopenharmony_ci			best_parent_clk = parent_clk;
4118c2ecf20Sopenharmony_ci			best_parent_rate = parent_rate;
4128c2ecf20Sopenharmony_ci			best = nearest_rate;
4138c2ecf20Sopenharmony_ci			best_delta = delta;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci			if (delta == 0)
4168c2ecf20Sopenharmony_ci				break;
4178c2ecf20Sopenharmony_ci		}
4188c2ecf20Sopenharmony_ci	}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	/* if no match found, retain old rate */
4218c2ecf20Sopenharmony_ci	if (!best_parent_clk) {
4228c2ecf20Sopenharmony_ci		pr_err("%s:%s, no parent found for rate %lu.\n",
4238c2ecf20Sopenharmony_ci		       __func__, clk_hw_get_name(hw), req->rate);
4248c2ecf20Sopenharmony_ci		return clk_hw_get_rate(hw);
4258c2ecf20Sopenharmony_ci	}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	pr_debug("%s,rate %lu, best_parent(%s, %lu), best %lu, delta %d\n",
4288c2ecf20Sopenharmony_ci		 clk_hw_get_name(hw), req->rate,
4298c2ecf20Sopenharmony_ci		 clk_hw_get_name(best_parent_clk), best_parent_rate,
4308c2ecf20Sopenharmony_ci		 best, best_delta);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	if (req->best_parent_rate)
4338c2ecf20Sopenharmony_ci		req->best_parent_rate = best_parent_rate;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	if (req->best_parent_hw)
4368c2ecf20Sopenharmony_ci		req->best_parent_hw = best_parent_clk;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	return best;
4398c2ecf20Sopenharmony_ci}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_cistatic int roclk_set_parent(struct clk_hw *hw, u8 index)
4428c2ecf20Sopenharmony_ci{
4438c2ecf20Sopenharmony_ci	struct pic32_ref_osc *refo = clkhw_to_refosc(hw);
4448c2ecf20Sopenharmony_ci	unsigned long flags;
4458c2ecf20Sopenharmony_ci	u32 v;
4468c2ecf20Sopenharmony_ci	int err;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	if (refo->parent_map)
4498c2ecf20Sopenharmony_ci		index = refo->parent_map[index];
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	/* wait until ACTIVE bit is zero or timeout */
4528c2ecf20Sopenharmony_ci	err = readl_poll_timeout(refo->ctrl_reg, v, !(v & REFO_ACTIVE),
4538c2ecf20Sopenharmony_ci				 1, LOCK_TIMEOUT_US);
4548c2ecf20Sopenharmony_ci	if (err) {
4558c2ecf20Sopenharmony_ci		pr_err("%s: poll failed, clk active\n", clk_hw_get_name(hw));
4568c2ecf20Sopenharmony_ci		return err;
4578c2ecf20Sopenharmony_ci	}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	spin_lock_irqsave(&refo->core->reg_lock, flags);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	pic32_syskey_unlock();
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	/* calculate & apply new */
4648c2ecf20Sopenharmony_ci	v = readl(refo->ctrl_reg);
4658c2ecf20Sopenharmony_ci	v &= ~(REFO_SEL_MASK << REFO_SEL_SHIFT);
4668c2ecf20Sopenharmony_ci	v |= index << REFO_SEL_SHIFT;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	writel(v, refo->ctrl_reg);
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&refo->core->reg_lock, flags);
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	return 0;
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_cistatic int roclk_set_rate_and_parent(struct clk_hw *hw,
4768c2ecf20Sopenharmony_ci				     unsigned long rate,
4778c2ecf20Sopenharmony_ci				     unsigned long parent_rate,
4788c2ecf20Sopenharmony_ci				     u8 index)
4798c2ecf20Sopenharmony_ci{
4808c2ecf20Sopenharmony_ci	struct pic32_ref_osc *refo = clkhw_to_refosc(hw);
4818c2ecf20Sopenharmony_ci	unsigned long flags;
4828c2ecf20Sopenharmony_ci	u32 trim, rodiv, v;
4838c2ecf20Sopenharmony_ci	int err;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	/* calculate new rodiv & rotrim for new rate */
4868c2ecf20Sopenharmony_ci	roclk_calc_div_trim(rate, parent_rate, &rodiv, &trim);
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	pr_debug("parent_rate = %lu, rate = %lu, div = %d, trim = %d\n",
4898c2ecf20Sopenharmony_ci		 parent_rate, rate, rodiv, trim);
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	/* wait till source change is active */
4928c2ecf20Sopenharmony_ci	err = readl_poll_timeout(refo->ctrl_reg, v,
4938c2ecf20Sopenharmony_ci				 !(v & (REFO_ACTIVE | REFO_DIVSW_EN)),
4948c2ecf20Sopenharmony_ci				 1, LOCK_TIMEOUT_US);
4958c2ecf20Sopenharmony_ci	if (err) {
4968c2ecf20Sopenharmony_ci		pr_err("%s: poll timedout, clock is still active\n", __func__);
4978c2ecf20Sopenharmony_ci		return err;
4988c2ecf20Sopenharmony_ci	}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	spin_lock_irqsave(&refo->core->reg_lock, flags);
5018c2ecf20Sopenharmony_ci	v = readl(refo->ctrl_reg);
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	pic32_syskey_unlock();
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	/* apply parent, if required */
5068c2ecf20Sopenharmony_ci	if (refo->parent_map)
5078c2ecf20Sopenharmony_ci		index = refo->parent_map[index];
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	v &= ~(REFO_SEL_MASK << REFO_SEL_SHIFT);
5108c2ecf20Sopenharmony_ci	v |= index << REFO_SEL_SHIFT;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	/* apply RODIV */
5138c2ecf20Sopenharmony_ci	v &= ~(REFO_DIV_MASK << REFO_DIV_SHIFT);
5148c2ecf20Sopenharmony_ci	v |= rodiv << REFO_DIV_SHIFT;
5158c2ecf20Sopenharmony_ci	writel(v, refo->ctrl_reg);
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	/* apply ROTRIM */
5188c2ecf20Sopenharmony_ci	v = readl(refo->ctrl_reg + REFO_TRIM_REG);
5198c2ecf20Sopenharmony_ci	v &= ~(REFO_TRIM_MASK << REFO_TRIM_SHIFT);
5208c2ecf20Sopenharmony_ci	v |= trim << REFO_TRIM_SHIFT;
5218c2ecf20Sopenharmony_ci	writel(v, refo->ctrl_reg + REFO_TRIM_REG);
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	/* enable & activate divider switching */
5248c2ecf20Sopenharmony_ci	writel(REFO_ON | REFO_DIVSW_EN, PIC32_SET(refo->ctrl_reg));
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	/* wait till divswen is in-progress */
5278c2ecf20Sopenharmony_ci	err = readl_poll_timeout_atomic(refo->ctrl_reg, v, !(v & REFO_DIVSW_EN),
5288c2ecf20Sopenharmony_ci					1, LOCK_TIMEOUT_US);
5298c2ecf20Sopenharmony_ci	/* leave the clk gated as it was */
5308c2ecf20Sopenharmony_ci	writel(REFO_ON, PIC32_CLR(refo->ctrl_reg));
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&refo->core->reg_lock, flags);
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	return err;
5358c2ecf20Sopenharmony_ci}
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_cistatic int roclk_set_rate(struct clk_hw *hw, unsigned long rate,
5388c2ecf20Sopenharmony_ci			  unsigned long parent_rate)
5398c2ecf20Sopenharmony_ci{
5408c2ecf20Sopenharmony_ci	u8 index = roclk_get_parent(hw);
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	return roclk_set_rate_and_parent(hw, rate, parent_rate, index);
5438c2ecf20Sopenharmony_ci}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ciconst struct clk_ops pic32_roclk_ops = {
5468c2ecf20Sopenharmony_ci	.enable			= roclk_enable,
5478c2ecf20Sopenharmony_ci	.disable		= roclk_disable,
5488c2ecf20Sopenharmony_ci	.is_enabled		= roclk_is_enabled,
5498c2ecf20Sopenharmony_ci	.get_parent		= roclk_get_parent,
5508c2ecf20Sopenharmony_ci	.set_parent		= roclk_set_parent,
5518c2ecf20Sopenharmony_ci	.determine_rate		= roclk_determine_rate,
5528c2ecf20Sopenharmony_ci	.recalc_rate		= roclk_recalc_rate,
5538c2ecf20Sopenharmony_ci	.set_rate_and_parent	= roclk_set_rate_and_parent,
5548c2ecf20Sopenharmony_ci	.set_rate		= roclk_set_rate,
5558c2ecf20Sopenharmony_ci	.init			= roclk_init,
5568c2ecf20Sopenharmony_ci};
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_cistruct clk *pic32_refo_clk_register(const struct pic32_ref_osc_data *data,
5598c2ecf20Sopenharmony_ci				    struct pic32_clk_common *core)
5608c2ecf20Sopenharmony_ci{
5618c2ecf20Sopenharmony_ci	struct pic32_ref_osc *refo;
5628c2ecf20Sopenharmony_ci	struct clk *clk;
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	refo = devm_kzalloc(core->dev, sizeof(*refo), GFP_KERNEL);
5658c2ecf20Sopenharmony_ci	if (!refo)
5668c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	refo->core = core;
5698c2ecf20Sopenharmony_ci	refo->hw.init = &data->init_data;
5708c2ecf20Sopenharmony_ci	refo->ctrl_reg = data->ctrl_reg + core->iobase;
5718c2ecf20Sopenharmony_ci	refo->parent_map = data->parent_map;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	clk = devm_clk_register(core->dev, &refo->hw);
5748c2ecf20Sopenharmony_ci	if (IS_ERR(clk))
5758c2ecf20Sopenharmony_ci		dev_err(core->dev, "%s: clk_register() failed\n", __func__);
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	return clk;
5788c2ecf20Sopenharmony_ci}
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_cistruct pic32_sys_pll {
5818c2ecf20Sopenharmony_ci	struct clk_hw hw;
5828c2ecf20Sopenharmony_ci	void __iomem *ctrl_reg;
5838c2ecf20Sopenharmony_ci	void __iomem *status_reg;
5848c2ecf20Sopenharmony_ci	u32 lock_mask;
5858c2ecf20Sopenharmony_ci	u32 idiv; /* PLL iclk divider, treated fixed */
5868c2ecf20Sopenharmony_ci	struct pic32_clk_common *core;
5878c2ecf20Sopenharmony_ci};
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci#define clkhw_to_spll(_hw)	container_of(_hw, struct pic32_sys_pll, hw)
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_cistatic inline u32 spll_odiv_to_divider(u32 odiv)
5928c2ecf20Sopenharmony_ci{
5938c2ecf20Sopenharmony_ci	odiv = clamp_val(odiv, PLL_ODIV_MIN, PLL_ODIV_MAX);
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	return 1 << odiv;
5968c2ecf20Sopenharmony_ci}
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_cistatic unsigned long spll_calc_mult_div(struct pic32_sys_pll *pll,
5998c2ecf20Sopenharmony_ci					unsigned long rate,
6008c2ecf20Sopenharmony_ci					unsigned long parent_rate,
6018c2ecf20Sopenharmony_ci					u32 *mult_p, u32 *odiv_p)
6028c2ecf20Sopenharmony_ci{
6038c2ecf20Sopenharmony_ci	u32 mul, div, best_mul = 1, best_div = 1;
6048c2ecf20Sopenharmony_ci	unsigned long new_rate, best_rate = rate;
6058c2ecf20Sopenharmony_ci	unsigned int best_delta = -1, delta, match_found = 0;
6068c2ecf20Sopenharmony_ci	u64 rate64;
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	parent_rate /= pll->idiv;
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	for (mul = 1; mul <= PLL_MULT_MAX; mul++) {
6118c2ecf20Sopenharmony_ci		for (div = PLL_ODIV_MIN; div <= PLL_ODIV_MAX; div++) {
6128c2ecf20Sopenharmony_ci			rate64 = parent_rate;
6138c2ecf20Sopenharmony_ci			rate64 *= mul;
6148c2ecf20Sopenharmony_ci			do_div(rate64, 1 << div);
6158c2ecf20Sopenharmony_ci			new_rate = rate64;
6168c2ecf20Sopenharmony_ci			delta = abs(rate - new_rate);
6178c2ecf20Sopenharmony_ci			if ((new_rate >= rate) && (delta < best_delta)) {
6188c2ecf20Sopenharmony_ci				best_delta = delta;
6198c2ecf20Sopenharmony_ci				best_rate = new_rate;
6208c2ecf20Sopenharmony_ci				best_mul = mul;
6218c2ecf20Sopenharmony_ci				best_div = div;
6228c2ecf20Sopenharmony_ci				match_found = 1;
6238c2ecf20Sopenharmony_ci			}
6248c2ecf20Sopenharmony_ci		}
6258c2ecf20Sopenharmony_ci	}
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	if (!match_found) {
6288c2ecf20Sopenharmony_ci		pr_warn("spll: no match found\n");
6298c2ecf20Sopenharmony_ci		return 0;
6308c2ecf20Sopenharmony_ci	}
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	pr_debug("rate %lu, par_rate %lu/mult %u, div %u, best_rate %lu\n",
6338c2ecf20Sopenharmony_ci		 rate, parent_rate, best_mul, best_div, best_rate);
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	if (mult_p)
6368c2ecf20Sopenharmony_ci		*mult_p = best_mul - 1;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	if (odiv_p)
6398c2ecf20Sopenharmony_ci		*odiv_p = best_div;
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	return best_rate;
6428c2ecf20Sopenharmony_ci}
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_cistatic unsigned long spll_clk_recalc_rate(struct clk_hw *hw,
6458c2ecf20Sopenharmony_ci					  unsigned long parent_rate)
6468c2ecf20Sopenharmony_ci{
6478c2ecf20Sopenharmony_ci	struct pic32_sys_pll *pll = clkhw_to_spll(hw);
6488c2ecf20Sopenharmony_ci	unsigned long pll_in_rate;
6498c2ecf20Sopenharmony_ci	u32 mult, odiv, div, v;
6508c2ecf20Sopenharmony_ci	u64 rate64;
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	v = readl(pll->ctrl_reg);
6538c2ecf20Sopenharmony_ci	odiv = ((v >> PLL_ODIV_SHIFT) & PLL_ODIV_MASK);
6548c2ecf20Sopenharmony_ci	mult = ((v >> PLL_MULT_SHIFT) & PLL_MULT_MASK) + 1;
6558c2ecf20Sopenharmony_ci	div = spll_odiv_to_divider(odiv);
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	/* pll_in_rate = parent_rate / idiv
6588c2ecf20Sopenharmony_ci	 * pll_out_rate = pll_in_rate * mult / div;
6598c2ecf20Sopenharmony_ci	 */
6608c2ecf20Sopenharmony_ci	pll_in_rate = parent_rate / pll->idiv;
6618c2ecf20Sopenharmony_ci	rate64 = pll_in_rate;
6628c2ecf20Sopenharmony_ci	rate64 *= mult;
6638c2ecf20Sopenharmony_ci	do_div(rate64, div);
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	return rate64;
6668c2ecf20Sopenharmony_ci}
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_cistatic long spll_clk_round_rate(struct clk_hw *hw, unsigned long rate,
6698c2ecf20Sopenharmony_ci				unsigned long *parent_rate)
6708c2ecf20Sopenharmony_ci{
6718c2ecf20Sopenharmony_ci	struct pic32_sys_pll *pll = clkhw_to_spll(hw);
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	return spll_calc_mult_div(pll, rate, *parent_rate, NULL, NULL);
6748c2ecf20Sopenharmony_ci}
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_cistatic int spll_clk_set_rate(struct clk_hw *hw, unsigned long rate,
6778c2ecf20Sopenharmony_ci			     unsigned long parent_rate)
6788c2ecf20Sopenharmony_ci{
6798c2ecf20Sopenharmony_ci	struct pic32_sys_pll *pll = clkhw_to_spll(hw);
6808c2ecf20Sopenharmony_ci	unsigned long ret, flags;
6818c2ecf20Sopenharmony_ci	u32 mult, odiv, v;
6828c2ecf20Sopenharmony_ci	int err;
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	ret = spll_calc_mult_div(pll, rate, parent_rate, &mult, &odiv);
6858c2ecf20Sopenharmony_ci	if (!ret)
6868c2ecf20Sopenharmony_ci		return -EINVAL;
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	/*
6898c2ecf20Sopenharmony_ci	 * We can't change SPLL counters when it is in-active use
6908c2ecf20Sopenharmony_ci	 * by SYSCLK. So check before applying new counters/rate.
6918c2ecf20Sopenharmony_ci	 */
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	/* Is spll_clk active parent of sys_clk ? */
6948c2ecf20Sopenharmony_ci	if (unlikely(clk_hw_get_parent(pic32_sclk_hw) == hw)) {
6958c2ecf20Sopenharmony_ci		pr_err("%s: failed, clk in-use\n", __func__);
6968c2ecf20Sopenharmony_ci		return -EBUSY;
6978c2ecf20Sopenharmony_ci	}
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	spin_lock_irqsave(&pll->core->reg_lock, flags);
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	/* apply new multiplier & divisor */
7028c2ecf20Sopenharmony_ci	v = readl(pll->ctrl_reg);
7038c2ecf20Sopenharmony_ci	v &= ~(PLL_MULT_MASK << PLL_MULT_SHIFT);
7048c2ecf20Sopenharmony_ci	v &= ~(PLL_ODIV_MASK << PLL_ODIV_SHIFT);
7058c2ecf20Sopenharmony_ci	v |= (mult << PLL_MULT_SHIFT) | (odiv << PLL_ODIV_SHIFT);
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	/* sys unlock before write */
7088c2ecf20Sopenharmony_ci	pic32_syskey_unlock();
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	writel(v, pll->ctrl_reg);
7118c2ecf20Sopenharmony_ci	cpu_relax();
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	/* insert few nops (5-stage) to ensure CPU does not hang */
7148c2ecf20Sopenharmony_ci	cpu_nop5();
7158c2ecf20Sopenharmony_ci	cpu_nop5();
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	/* Wait until PLL is locked (maximum 100 usecs). */
7188c2ecf20Sopenharmony_ci	err = readl_poll_timeout_atomic(pll->status_reg, v,
7198c2ecf20Sopenharmony_ci					v & pll->lock_mask, 1, 100);
7208c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&pll->core->reg_lock, flags);
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	return err;
7238c2ecf20Sopenharmony_ci}
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci/* SPLL clock operation */
7268c2ecf20Sopenharmony_ciconst struct clk_ops pic32_spll_ops = {
7278c2ecf20Sopenharmony_ci	.recalc_rate	= spll_clk_recalc_rate,
7288c2ecf20Sopenharmony_ci	.round_rate	= spll_clk_round_rate,
7298c2ecf20Sopenharmony_ci	.set_rate	= spll_clk_set_rate,
7308c2ecf20Sopenharmony_ci};
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_cistruct clk *pic32_spll_clk_register(const struct pic32_sys_pll_data *data,
7338c2ecf20Sopenharmony_ci				    struct pic32_clk_common *core)
7348c2ecf20Sopenharmony_ci{
7358c2ecf20Sopenharmony_ci	struct pic32_sys_pll *spll;
7368c2ecf20Sopenharmony_ci	struct clk *clk;
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	spll = devm_kzalloc(core->dev, sizeof(*spll), GFP_KERNEL);
7398c2ecf20Sopenharmony_ci	if (!spll)
7408c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	spll->core = core;
7438c2ecf20Sopenharmony_ci	spll->hw.init = &data->init_data;
7448c2ecf20Sopenharmony_ci	spll->ctrl_reg = data->ctrl_reg + core->iobase;
7458c2ecf20Sopenharmony_ci	spll->status_reg = data->status_reg + core->iobase;
7468c2ecf20Sopenharmony_ci	spll->lock_mask = data->lock_mask;
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	/* cache PLL idiv; PLL driver uses it as constant.*/
7498c2ecf20Sopenharmony_ci	spll->idiv = (readl(spll->ctrl_reg) >> PLL_IDIV_SHIFT) & PLL_IDIV_MASK;
7508c2ecf20Sopenharmony_ci	spll->idiv += 1;
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	clk = devm_clk_register(core->dev, &spll->hw);
7538c2ecf20Sopenharmony_ci	if (IS_ERR(clk))
7548c2ecf20Sopenharmony_ci		dev_err(core->dev, "sys_pll: clk_register() failed\n");
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	return clk;
7578c2ecf20Sopenharmony_ci}
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci/* System mux clock(aka SCLK) */
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_cistruct pic32_sys_clk {
7628c2ecf20Sopenharmony_ci	struct clk_hw hw;
7638c2ecf20Sopenharmony_ci	void __iomem *mux_reg;
7648c2ecf20Sopenharmony_ci	void __iomem *slew_reg;
7658c2ecf20Sopenharmony_ci	u32 slew_div;
7668c2ecf20Sopenharmony_ci	const u32 *parent_map;
7678c2ecf20Sopenharmony_ci	struct pic32_clk_common *core;
7688c2ecf20Sopenharmony_ci};
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci#define clkhw_to_sys_clk(_hw)	container_of(_hw, struct pic32_sys_clk, hw)
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_cistatic unsigned long sclk_get_rate(struct clk_hw *hw, unsigned long parent_rate)
7738c2ecf20Sopenharmony_ci{
7748c2ecf20Sopenharmony_ci	struct pic32_sys_clk *sclk = clkhw_to_sys_clk(hw);
7758c2ecf20Sopenharmony_ci	u32 div;
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	div = (readl(sclk->slew_reg) >> SLEW_SYSDIV_SHIFT) & SLEW_SYSDIV;
7788c2ecf20Sopenharmony_ci	div += 1; /* sys-div to divider */
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	return parent_rate / div;
7818c2ecf20Sopenharmony_ci}
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_cistatic long sclk_round_rate(struct clk_hw *hw, unsigned long rate,
7848c2ecf20Sopenharmony_ci			    unsigned long *parent_rate)
7858c2ecf20Sopenharmony_ci{
7868c2ecf20Sopenharmony_ci	return calc_best_divided_rate(rate, *parent_rate, SLEW_SYSDIV, 1);
7878c2ecf20Sopenharmony_ci}
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_cistatic int sclk_set_rate(struct clk_hw *hw,
7908c2ecf20Sopenharmony_ci			 unsigned long rate, unsigned long parent_rate)
7918c2ecf20Sopenharmony_ci{
7928c2ecf20Sopenharmony_ci	struct pic32_sys_clk *sclk = clkhw_to_sys_clk(hw);
7938c2ecf20Sopenharmony_ci	unsigned long flags;
7948c2ecf20Sopenharmony_ci	u32 v, div;
7958c2ecf20Sopenharmony_ci	int err;
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	div = parent_rate / rate;
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	spin_lock_irqsave(&sclk->core->reg_lock, flags);
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	/* apply new div */
8028c2ecf20Sopenharmony_ci	v = readl(sclk->slew_reg);
8038c2ecf20Sopenharmony_ci	v &= ~(SLEW_SYSDIV << SLEW_SYSDIV_SHIFT);
8048c2ecf20Sopenharmony_ci	v |= (div - 1) << SLEW_SYSDIV_SHIFT;
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	pic32_syskey_unlock();
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	writel(v, sclk->slew_reg);
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	/* wait until BUSY is cleared */
8118c2ecf20Sopenharmony_ci	err = readl_poll_timeout_atomic(sclk->slew_reg, v,
8128c2ecf20Sopenharmony_ci					!(v & SLEW_BUSY), 1, LOCK_TIMEOUT_US);
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&sclk->core->reg_lock, flags);
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	return err;
8178c2ecf20Sopenharmony_ci}
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_cistatic u8 sclk_get_parent(struct clk_hw *hw)
8208c2ecf20Sopenharmony_ci{
8218c2ecf20Sopenharmony_ci	struct pic32_sys_clk *sclk = clkhw_to_sys_clk(hw);
8228c2ecf20Sopenharmony_ci	u32 i, v;
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	v = (readl(sclk->mux_reg) >> OSC_CUR_SHIFT) & OSC_CUR_MASK;
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	if (!sclk->parent_map)
8278c2ecf20Sopenharmony_ci		return v;
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	for (i = 0; i < clk_hw_get_num_parents(hw); i++)
8308c2ecf20Sopenharmony_ci		if (sclk->parent_map[i] == v)
8318c2ecf20Sopenharmony_ci			return i;
8328c2ecf20Sopenharmony_ci	return -EINVAL;
8338c2ecf20Sopenharmony_ci}
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_cistatic int sclk_set_parent(struct clk_hw *hw, u8 index)
8368c2ecf20Sopenharmony_ci{
8378c2ecf20Sopenharmony_ci	struct pic32_sys_clk *sclk = clkhw_to_sys_clk(hw);
8388c2ecf20Sopenharmony_ci	unsigned long flags;
8398c2ecf20Sopenharmony_ci	u32 nosc, cosc, v;
8408c2ecf20Sopenharmony_ci	int err;
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	spin_lock_irqsave(&sclk->core->reg_lock, flags);
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	/* find new_osc */
8458c2ecf20Sopenharmony_ci	nosc = sclk->parent_map ? sclk->parent_map[index] : index;
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	/* set new parent */
8488c2ecf20Sopenharmony_ci	v = readl(sclk->mux_reg);
8498c2ecf20Sopenharmony_ci	v &= ~(OSC_NEW_MASK << OSC_NEW_SHIFT);
8508c2ecf20Sopenharmony_ci	v |= nosc << OSC_NEW_SHIFT;
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	pic32_syskey_unlock();
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	writel(v, sclk->mux_reg);
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	/* initate switch */
8578c2ecf20Sopenharmony_ci	writel(OSC_SWEN, PIC32_SET(sclk->mux_reg));
8588c2ecf20Sopenharmony_ci	cpu_relax();
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci	/* add nop to flush pipeline (as cpu_clk is in-flux) */
8618c2ecf20Sopenharmony_ci	cpu_nop5();
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	/* wait for SWEN bit to clear */
8648c2ecf20Sopenharmony_ci	err = readl_poll_timeout_atomic(sclk->slew_reg, v,
8658c2ecf20Sopenharmony_ci					!(v & OSC_SWEN), 1, LOCK_TIMEOUT_US);
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&sclk->core->reg_lock, flags);
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	/*
8708c2ecf20Sopenharmony_ci	 * SCLK clock-switching logic might reject a clock switching request
8718c2ecf20Sopenharmony_ci	 * if pre-requisites (like new clk_src not present or unstable) are
8728c2ecf20Sopenharmony_ci	 * not met.
8738c2ecf20Sopenharmony_ci	 * So confirm before claiming success.
8748c2ecf20Sopenharmony_ci	 */
8758c2ecf20Sopenharmony_ci	cosc = (readl(sclk->mux_reg) >> OSC_CUR_SHIFT) & OSC_CUR_MASK;
8768c2ecf20Sopenharmony_ci	if (cosc != nosc) {
8778c2ecf20Sopenharmony_ci		pr_err("%s: err, failed to set_parent() to %d, current %d\n",
8788c2ecf20Sopenharmony_ci		       clk_hw_get_name(hw), nosc, cosc);
8798c2ecf20Sopenharmony_ci		err = -EBUSY;
8808c2ecf20Sopenharmony_ci	}
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	return err;
8838c2ecf20Sopenharmony_ci}
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_cistatic int sclk_init(struct clk_hw *hw)
8868c2ecf20Sopenharmony_ci{
8878c2ecf20Sopenharmony_ci	struct pic32_sys_clk *sclk = clkhw_to_sys_clk(hw);
8888c2ecf20Sopenharmony_ci	unsigned long flags;
8898c2ecf20Sopenharmony_ci	u32 v;
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	/* Maintain reference to this clk, required in spll_clk_set_rate() */
8928c2ecf20Sopenharmony_ci	pic32_sclk_hw = hw;
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	/* apply slew divider on both up and down scaling */
8958c2ecf20Sopenharmony_ci	if (sclk->slew_div) {
8968c2ecf20Sopenharmony_ci		spin_lock_irqsave(&sclk->core->reg_lock, flags);
8978c2ecf20Sopenharmony_ci		v = readl(sclk->slew_reg);
8988c2ecf20Sopenharmony_ci		v &= ~(SLEW_DIV << SLEW_DIV_SHIFT);
8998c2ecf20Sopenharmony_ci		v |= sclk->slew_div << SLEW_DIV_SHIFT;
9008c2ecf20Sopenharmony_ci		v |= SLEW_DOWNEN | SLEW_UPEN;
9018c2ecf20Sopenharmony_ci		writel(v, sclk->slew_reg);
9028c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&sclk->core->reg_lock, flags);
9038c2ecf20Sopenharmony_ci	}
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	return 0;
9068c2ecf20Sopenharmony_ci}
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci/* sclk with post-divider */
9098c2ecf20Sopenharmony_ciconst struct clk_ops pic32_sclk_ops = {
9108c2ecf20Sopenharmony_ci	.get_parent	= sclk_get_parent,
9118c2ecf20Sopenharmony_ci	.set_parent	= sclk_set_parent,
9128c2ecf20Sopenharmony_ci	.round_rate	= sclk_round_rate,
9138c2ecf20Sopenharmony_ci	.set_rate	= sclk_set_rate,
9148c2ecf20Sopenharmony_ci	.recalc_rate	= sclk_get_rate,
9158c2ecf20Sopenharmony_ci	.init		= sclk_init,
9168c2ecf20Sopenharmony_ci	.determine_rate = __clk_mux_determine_rate,
9178c2ecf20Sopenharmony_ci};
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci/* sclk with no slew and no post-divider */
9208c2ecf20Sopenharmony_ciconst struct clk_ops pic32_sclk_no_div_ops = {
9218c2ecf20Sopenharmony_ci	.get_parent	= sclk_get_parent,
9228c2ecf20Sopenharmony_ci	.set_parent	= sclk_set_parent,
9238c2ecf20Sopenharmony_ci	.init		= sclk_init,
9248c2ecf20Sopenharmony_ci	.determine_rate = __clk_mux_determine_rate,
9258c2ecf20Sopenharmony_ci};
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_cistruct clk *pic32_sys_clk_register(const struct pic32_sys_clk_data *data,
9288c2ecf20Sopenharmony_ci				   struct pic32_clk_common *core)
9298c2ecf20Sopenharmony_ci{
9308c2ecf20Sopenharmony_ci	struct pic32_sys_clk *sclk;
9318c2ecf20Sopenharmony_ci	struct clk *clk;
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	sclk = devm_kzalloc(core->dev, sizeof(*sclk), GFP_KERNEL);
9348c2ecf20Sopenharmony_ci	if (!sclk)
9358c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci	sclk->core = core;
9388c2ecf20Sopenharmony_ci	sclk->hw.init = &data->init_data;
9398c2ecf20Sopenharmony_ci	sclk->mux_reg = data->mux_reg + core->iobase;
9408c2ecf20Sopenharmony_ci	sclk->slew_reg = data->slew_reg + core->iobase;
9418c2ecf20Sopenharmony_ci	sclk->slew_div = data->slew_div;
9428c2ecf20Sopenharmony_ci	sclk->parent_map = data->parent_map;
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci	clk = devm_clk_register(core->dev, &sclk->hw);
9458c2ecf20Sopenharmony_ci	if (IS_ERR(clk))
9468c2ecf20Sopenharmony_ci		dev_err(core->dev, "%s: clk register failed\n", __func__);
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci	return clk;
9498c2ecf20Sopenharmony_ci}
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci/* secondary oscillator */
9528c2ecf20Sopenharmony_cistruct pic32_sec_osc {
9538c2ecf20Sopenharmony_ci	struct clk_hw hw;
9548c2ecf20Sopenharmony_ci	void __iomem *enable_reg;
9558c2ecf20Sopenharmony_ci	void __iomem *status_reg;
9568c2ecf20Sopenharmony_ci	u32 enable_mask;
9578c2ecf20Sopenharmony_ci	u32 status_mask;
9588c2ecf20Sopenharmony_ci	unsigned long fixed_rate;
9598c2ecf20Sopenharmony_ci	struct pic32_clk_common *core;
9608c2ecf20Sopenharmony_ci};
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci#define clkhw_to_sosc(_hw)	container_of(_hw, struct pic32_sec_osc, hw)
9638c2ecf20Sopenharmony_cistatic int sosc_clk_enable(struct clk_hw *hw)
9648c2ecf20Sopenharmony_ci{
9658c2ecf20Sopenharmony_ci	struct pic32_sec_osc *sosc = clkhw_to_sosc(hw);
9668c2ecf20Sopenharmony_ci	u32 v;
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	/* enable SOSC */
9698c2ecf20Sopenharmony_ci	pic32_syskey_unlock();
9708c2ecf20Sopenharmony_ci	writel(sosc->enable_mask, PIC32_SET(sosc->enable_reg));
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci	/* wait till warm-up period expires or ready-status is updated */
9738c2ecf20Sopenharmony_ci	return readl_poll_timeout_atomic(sosc->status_reg, v,
9748c2ecf20Sopenharmony_ci					 v & sosc->status_mask, 1, 100);
9758c2ecf20Sopenharmony_ci}
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_cistatic void sosc_clk_disable(struct clk_hw *hw)
9788c2ecf20Sopenharmony_ci{
9798c2ecf20Sopenharmony_ci	struct pic32_sec_osc *sosc = clkhw_to_sosc(hw);
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	pic32_syskey_unlock();
9828c2ecf20Sopenharmony_ci	writel(sosc->enable_mask, PIC32_CLR(sosc->enable_reg));
9838c2ecf20Sopenharmony_ci}
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_cistatic int sosc_clk_is_enabled(struct clk_hw *hw)
9868c2ecf20Sopenharmony_ci{
9878c2ecf20Sopenharmony_ci	struct pic32_sec_osc *sosc = clkhw_to_sosc(hw);
9888c2ecf20Sopenharmony_ci	u32 enabled, ready;
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	/* check enabled and ready status */
9918c2ecf20Sopenharmony_ci	enabled = readl(sosc->enable_reg) & sosc->enable_mask;
9928c2ecf20Sopenharmony_ci	ready = readl(sosc->status_reg) & sosc->status_mask;
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci	return enabled && ready;
9958c2ecf20Sopenharmony_ci}
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_cistatic unsigned long sosc_clk_calc_rate(struct clk_hw *hw,
9988c2ecf20Sopenharmony_ci					unsigned long parent_rate)
9998c2ecf20Sopenharmony_ci{
10008c2ecf20Sopenharmony_ci	return clkhw_to_sosc(hw)->fixed_rate;
10018c2ecf20Sopenharmony_ci}
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ciconst struct clk_ops pic32_sosc_ops = {
10048c2ecf20Sopenharmony_ci	.enable = sosc_clk_enable,
10058c2ecf20Sopenharmony_ci	.disable = sosc_clk_disable,
10068c2ecf20Sopenharmony_ci	.is_enabled = sosc_clk_is_enabled,
10078c2ecf20Sopenharmony_ci	.recalc_rate = sosc_clk_calc_rate,
10088c2ecf20Sopenharmony_ci};
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_cistruct clk *pic32_sosc_clk_register(const struct pic32_sec_osc_data *data,
10118c2ecf20Sopenharmony_ci				    struct pic32_clk_common *core)
10128c2ecf20Sopenharmony_ci{
10138c2ecf20Sopenharmony_ci	struct pic32_sec_osc *sosc;
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ci	sosc = devm_kzalloc(core->dev, sizeof(*sosc), GFP_KERNEL);
10168c2ecf20Sopenharmony_ci	if (!sosc)
10178c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci	sosc->core = core;
10208c2ecf20Sopenharmony_ci	sosc->hw.init = &data->init_data;
10218c2ecf20Sopenharmony_ci	sosc->fixed_rate = data->fixed_rate;
10228c2ecf20Sopenharmony_ci	sosc->enable_mask = data->enable_mask;
10238c2ecf20Sopenharmony_ci	sosc->status_mask = data->status_mask;
10248c2ecf20Sopenharmony_ci	sosc->enable_reg = data->enable_reg + core->iobase;
10258c2ecf20Sopenharmony_ci	sosc->status_reg = data->status_reg + core->iobase;
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci	return devm_clk_register(core->dev, &sosc->hw);
10288c2ecf20Sopenharmony_ci}
1029