162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2016 Freescale Semiconductor, Inc.
462306a36Sopenharmony_ci * Copyright 2017~2018 NXP
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Author: Dong Aisheng <aisheng.dong@nxp.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/clk-provider.h>
1162306a36Sopenharmony_ci#include <linux/err.h>
1262306a36Sopenharmony_ci#include <linux/io.h>
1362306a36Sopenharmony_ci#include <linux/iopoll.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "clk.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/**
1962306a36Sopenharmony_ci * struct clk_pfdv2 - IMX PFD clock
2062306a36Sopenharmony_ci * @hw:		clock source
2162306a36Sopenharmony_ci * @reg:	PFD register address
2262306a36Sopenharmony_ci * @gate_bit:	Gate bit offset
2362306a36Sopenharmony_ci * @vld_bit:	Valid bit offset
2462306a36Sopenharmony_ci * @frac_off:	PLL Fractional Divider offset
2562306a36Sopenharmony_ci */
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistruct clk_pfdv2 {
2862306a36Sopenharmony_ci	struct clk_hw	hw;
2962306a36Sopenharmony_ci	void __iomem	*reg;
3062306a36Sopenharmony_ci	u8		gate_bit;
3162306a36Sopenharmony_ci	u8		vld_bit;
3262306a36Sopenharmony_ci	u8		frac_off;
3362306a36Sopenharmony_ci};
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define to_clk_pfdv2(_hw) container_of(_hw, struct clk_pfdv2, hw)
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define CLK_PFDV2_FRAC_MASK 0x3f
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define LOCK_TIMEOUT_US		USEC_PER_MSEC
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic DEFINE_SPINLOCK(pfd_lock);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic int clk_pfdv2_wait(struct clk_pfdv2 *pfd)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	u32 val;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	return readl_poll_timeout(pfd->reg, val, val & (1 << pfd->vld_bit),
4862306a36Sopenharmony_ci				  0, LOCK_TIMEOUT_US);
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic int clk_pfdv2_enable(struct clk_hw *hw)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	struct clk_pfdv2 *pfd = to_clk_pfdv2(hw);
5462306a36Sopenharmony_ci	unsigned long flags;
5562306a36Sopenharmony_ci	u32 val;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	spin_lock_irqsave(&pfd_lock, flags);
5862306a36Sopenharmony_ci	val = readl_relaxed(pfd->reg);
5962306a36Sopenharmony_ci	val &= ~(1 << pfd->gate_bit);
6062306a36Sopenharmony_ci	writel_relaxed(val, pfd->reg);
6162306a36Sopenharmony_ci	spin_unlock_irqrestore(&pfd_lock, flags);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	return clk_pfdv2_wait(pfd);
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic void clk_pfdv2_disable(struct clk_hw *hw)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	struct clk_pfdv2 *pfd = to_clk_pfdv2(hw);
6962306a36Sopenharmony_ci	unsigned long flags;
7062306a36Sopenharmony_ci	u32 val;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	spin_lock_irqsave(&pfd_lock, flags);
7362306a36Sopenharmony_ci	val = readl_relaxed(pfd->reg);
7462306a36Sopenharmony_ci	val |= (1 << pfd->gate_bit);
7562306a36Sopenharmony_ci	writel_relaxed(val, pfd->reg);
7662306a36Sopenharmony_ci	spin_unlock_irqrestore(&pfd_lock, flags);
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic unsigned long clk_pfdv2_recalc_rate(struct clk_hw *hw,
8062306a36Sopenharmony_ci					   unsigned long parent_rate)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	struct clk_pfdv2 *pfd = to_clk_pfdv2(hw);
8362306a36Sopenharmony_ci	u64 tmp = parent_rate;
8462306a36Sopenharmony_ci	u8 frac;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	frac = (readl_relaxed(pfd->reg) >> pfd->frac_off)
8762306a36Sopenharmony_ci		& CLK_PFDV2_FRAC_MASK;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	if (!frac) {
9062306a36Sopenharmony_ci		pr_debug("clk_pfdv2: %s invalid pfd frac value 0\n",
9162306a36Sopenharmony_ci			 clk_hw_get_name(hw));
9262306a36Sopenharmony_ci		return 0;
9362306a36Sopenharmony_ci	}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	tmp *= 18;
9662306a36Sopenharmony_ci	do_div(tmp, frac);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	return tmp;
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic int clk_pfdv2_determine_rate(struct clk_hw *hw,
10262306a36Sopenharmony_ci				    struct clk_rate_request *req)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	unsigned long parent_rates[] = {
10562306a36Sopenharmony_ci					480000000,
10662306a36Sopenharmony_ci					528000000,
10762306a36Sopenharmony_ci					req->best_parent_rate
10862306a36Sopenharmony_ci				       };
10962306a36Sopenharmony_ci	unsigned long best_rate = -1UL, rate = req->rate;
11062306a36Sopenharmony_ci	unsigned long best_parent_rate = req->best_parent_rate;
11162306a36Sopenharmony_ci	u64 tmp;
11262306a36Sopenharmony_ci	u8 frac;
11362306a36Sopenharmony_ci	int i;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(parent_rates); i++) {
11662306a36Sopenharmony_ci		tmp = parent_rates[i];
11762306a36Sopenharmony_ci		tmp = tmp * 18 + rate / 2;
11862306a36Sopenharmony_ci		do_div(tmp, rate);
11962306a36Sopenharmony_ci		frac = tmp;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci		if (frac < 12)
12262306a36Sopenharmony_ci			frac = 12;
12362306a36Sopenharmony_ci		else if (frac > 35)
12462306a36Sopenharmony_ci			frac = 35;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci		tmp = parent_rates[i];
12762306a36Sopenharmony_ci		tmp *= 18;
12862306a36Sopenharmony_ci		do_div(tmp, frac);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci		if (abs(tmp - req->rate) < abs(best_rate - req->rate)) {
13162306a36Sopenharmony_ci			best_rate = tmp;
13262306a36Sopenharmony_ci			best_parent_rate = parent_rates[i];
13362306a36Sopenharmony_ci		}
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	req->best_parent_rate = best_parent_rate;
13762306a36Sopenharmony_ci	req->rate = best_rate;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	return 0;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic int clk_pfdv2_is_enabled(struct clk_hw *hw)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	struct clk_pfdv2 *pfd = to_clk_pfdv2(hw);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	if (readl_relaxed(pfd->reg) & (1 << pfd->gate_bit))
14762306a36Sopenharmony_ci		return 0;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	return 1;
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic int clk_pfdv2_set_rate(struct clk_hw *hw, unsigned long rate,
15362306a36Sopenharmony_ci			      unsigned long parent_rate)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	struct clk_pfdv2 *pfd = to_clk_pfdv2(hw);
15662306a36Sopenharmony_ci	unsigned long flags;
15762306a36Sopenharmony_ci	u64 tmp = parent_rate;
15862306a36Sopenharmony_ci	u32 val;
15962306a36Sopenharmony_ci	u8 frac;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	if (!rate)
16262306a36Sopenharmony_ci		return -EINVAL;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	/*
16562306a36Sopenharmony_ci	 * PFD can NOT change rate without gating.
16662306a36Sopenharmony_ci	 * as the PFDs may enabled in HW by default but no
16762306a36Sopenharmony_ci	 * consumer used it, the enable count is '0', so the
16862306a36Sopenharmony_ci	 * 'SET_RATE_GATE' can NOT help on blocking the set_rate
16962306a36Sopenharmony_ci	 * ops especially for 'assigned-clock-xxx'. In order
17062306a36Sopenharmony_ci	 * to simplify the case, just disable the PFD if it is
17162306a36Sopenharmony_ci	 * enabled in HW but not in SW.
17262306a36Sopenharmony_ci	 */
17362306a36Sopenharmony_ci	if (clk_pfdv2_is_enabled(hw))
17462306a36Sopenharmony_ci		clk_pfdv2_disable(hw);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	tmp = tmp * 18 + rate / 2;
17762306a36Sopenharmony_ci	do_div(tmp, rate);
17862306a36Sopenharmony_ci	frac = tmp;
17962306a36Sopenharmony_ci	if (frac < 12)
18062306a36Sopenharmony_ci		frac = 12;
18162306a36Sopenharmony_ci	else if (frac > 35)
18262306a36Sopenharmony_ci		frac = 35;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	spin_lock_irqsave(&pfd_lock, flags);
18562306a36Sopenharmony_ci	val = readl_relaxed(pfd->reg);
18662306a36Sopenharmony_ci	val &= ~(CLK_PFDV2_FRAC_MASK << pfd->frac_off);
18762306a36Sopenharmony_ci	val |= frac << pfd->frac_off;
18862306a36Sopenharmony_ci	writel_relaxed(val, pfd->reg);
18962306a36Sopenharmony_ci	spin_unlock_irqrestore(&pfd_lock, flags);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	return 0;
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic const struct clk_ops clk_pfdv2_ops = {
19562306a36Sopenharmony_ci	.enable		= clk_pfdv2_enable,
19662306a36Sopenharmony_ci	.disable	= clk_pfdv2_disable,
19762306a36Sopenharmony_ci	.recalc_rate	= clk_pfdv2_recalc_rate,
19862306a36Sopenharmony_ci	.determine_rate	= clk_pfdv2_determine_rate,
19962306a36Sopenharmony_ci	.set_rate	= clk_pfdv2_set_rate,
20062306a36Sopenharmony_ci	.is_enabled     = clk_pfdv2_is_enabled,
20162306a36Sopenharmony_ci};
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistruct clk_hw *imx_clk_hw_pfdv2(enum imx_pfdv2_type type, const char *name,
20462306a36Sopenharmony_ci			     const char *parent_name, void __iomem *reg, u8 idx)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	struct clk_init_data init;
20762306a36Sopenharmony_ci	struct clk_pfdv2 *pfd;
20862306a36Sopenharmony_ci	struct clk_hw *hw;
20962306a36Sopenharmony_ci	int ret;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	WARN_ON(idx > 3);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	pfd = kzalloc(sizeof(*pfd), GFP_KERNEL);
21462306a36Sopenharmony_ci	if (!pfd)
21562306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	pfd->reg = reg;
21862306a36Sopenharmony_ci	pfd->gate_bit = (idx + 1) * 8 - 1;
21962306a36Sopenharmony_ci	pfd->vld_bit = pfd->gate_bit - 1;
22062306a36Sopenharmony_ci	pfd->frac_off = idx * 8;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	init.name = name;
22362306a36Sopenharmony_ci	init.ops = &clk_pfdv2_ops;
22462306a36Sopenharmony_ci	init.parent_names = &parent_name;
22562306a36Sopenharmony_ci	init.num_parents = 1;
22662306a36Sopenharmony_ci	if (type == IMX_PFDV2_IMX7ULP)
22762306a36Sopenharmony_ci		init.flags = CLK_SET_RATE_GATE | CLK_SET_RATE_PARENT;
22862306a36Sopenharmony_ci	else
22962306a36Sopenharmony_ci		init.flags = CLK_SET_RATE_GATE;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	pfd->hw.init = &init;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	hw = &pfd->hw;
23462306a36Sopenharmony_ci	ret = clk_hw_register(NULL, hw);
23562306a36Sopenharmony_ci	if (ret) {
23662306a36Sopenharmony_ci		kfree(pfd);
23762306a36Sopenharmony_ci		hw = ERR_PTR(ret);
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	return hw;
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(imx_clk_hw_pfdv2);
243