1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (C) 2016 Freescale Semiconductor, Inc.
4 * Copyright 2017~2018 NXP
6 * Author: Dong Aisheng <aisheng.dong@nxp.com>
10 #include <linux/clk-provider.h>
11 #include <linux/err.h>
13 #include <linux/iopoll.h>
14 #include <linux/slab.h>
19 * struct clk_pfdv2 - IMX PFD clock
20 * @clk_hw: clock source
21 * @reg: PFD register address
22 * @gate_bit: Gate bit offset
23 * @vld_bit: Valid bit offset
24 * @frac_off: PLL Fractional Divider offset
35 #define to_clk_pfdv2(_hw) container_of(_hw, struct clk_pfdv2, hw)
37 #define CLK_PFDV2_FRAC_MASK 0x3f
39 #define LOCK_TIMEOUT_US USEC_PER_MSEC
41 static DEFINE_SPINLOCK(pfd_lock
);
43 static int clk_pfdv2_wait(struct clk_pfdv2
*pfd
)
47 return readl_poll_timeout(pfd
->reg
, val
, val
& (1 << pfd
->vld_bit
),
51 static int clk_pfdv2_enable(struct clk_hw
*hw
)
53 struct clk_pfdv2
*pfd
= to_clk_pfdv2(hw
);
57 spin_lock_irqsave(&pfd_lock
, flags
);
58 val
= readl_relaxed(pfd
->reg
);
59 val
&= ~(1 << pfd
->gate_bit
);
60 writel_relaxed(val
, pfd
->reg
);
61 spin_unlock_irqrestore(&pfd_lock
, flags
);
63 return clk_pfdv2_wait(pfd
);
66 static void clk_pfdv2_disable(struct clk_hw
*hw
)
68 struct clk_pfdv2
*pfd
= to_clk_pfdv2(hw
);
72 spin_lock_irqsave(&pfd_lock
, flags
);
73 val
= readl_relaxed(pfd
->reg
);
74 val
|= (1 << pfd
->gate_bit
);
75 writel_relaxed(val
, pfd
->reg
);
76 spin_unlock_irqrestore(&pfd_lock
, flags
);
79 static unsigned long clk_pfdv2_recalc_rate(struct clk_hw
*hw
,
80 unsigned long parent_rate
)
82 struct clk_pfdv2
*pfd
= to_clk_pfdv2(hw
);
83 u64 tmp
= parent_rate
;
86 frac
= (readl_relaxed(pfd
->reg
) >> pfd
->frac_off
)
87 & CLK_PFDV2_FRAC_MASK
;
90 pr_debug("clk_pfdv2: %s invalid pfd frac value 0\n",
101 static long clk_pfdv2_round_rate(struct clk_hw
*hw
, unsigned long rate
,
102 unsigned long *prate
)
107 tmp
= tmp
* 18 + rate
/ 2;
123 static int clk_pfdv2_is_enabled(struct clk_hw
*hw
)
125 struct clk_pfdv2
*pfd
= to_clk_pfdv2(hw
);
127 if (readl_relaxed(pfd
->reg
) & (1 << pfd
->gate_bit
))
133 static int clk_pfdv2_set_rate(struct clk_hw
*hw
, unsigned long rate
,
134 unsigned long parent_rate
)
136 struct clk_pfdv2
*pfd
= to_clk_pfdv2(hw
);
138 u64 tmp
= parent_rate
;
142 tmp
= tmp
* 18 + rate
/ 2;
150 spin_lock_irqsave(&pfd_lock
, flags
);
151 val
= readl_relaxed(pfd
->reg
);
152 val
&= ~(CLK_PFDV2_FRAC_MASK
<< pfd
->frac_off
);
153 val
|= frac
<< pfd
->frac_off
;
154 writel_relaxed(val
, pfd
->reg
);
155 spin_unlock_irqrestore(&pfd_lock
, flags
);
160 static const struct clk_ops clk_pfdv2_ops
= {
161 .enable
= clk_pfdv2_enable
,
162 .disable
= clk_pfdv2_disable
,
163 .recalc_rate
= clk_pfdv2_recalc_rate
,
164 .round_rate
= clk_pfdv2_round_rate
,
165 .set_rate
= clk_pfdv2_set_rate
,
166 .is_enabled
= clk_pfdv2_is_enabled
,
169 struct clk_hw
*imx_clk_pfdv2(const char *name
, const char *parent_name
,
170 void __iomem
*reg
, u8 idx
)
172 struct clk_init_data init
;
173 struct clk_pfdv2
*pfd
;
179 pfd
= kzalloc(sizeof(*pfd
), GFP_KERNEL
);
181 return ERR_PTR(-ENOMEM
);
184 pfd
->gate_bit
= (idx
+ 1) * 8 - 1;
185 pfd
->vld_bit
= pfd
->gate_bit
- 1;
186 pfd
->frac_off
= idx
* 8;
189 init
.ops
= &clk_pfdv2_ops
;
190 init
.parent_names
= &parent_name
;
191 init
.num_parents
= 1;
192 init
.flags
= CLK_SET_RATE_GATE
;
194 pfd
->hw
.init
= &init
;
197 ret
= clk_hw_register(NULL
, hw
);