• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Clk driver for NXP LPC18xx/43xx Configuration Registers (CREG)
3  *
4  * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
5  *
6  * This file is licensed under the terms of the GNU General Public
7  * License version 2. This program is licensed "as is" without any
8  * warranty of any kind, whether express or implied.
9  */
10 
11 #include <linux/clk-provider.h>
12 #include <linux/delay.h>
13 #include <linux/kernel.h>
14 #include <linux/mfd/syscon.h>
15 #include <linux/of.h>
16 #include <linux/platform_device.h>
17 #include <linux/regmap.h>
18 
19 #define LPC18XX_CREG_CREG0			0x004
20 #define  LPC18XX_CREG_CREG0_EN1KHZ		BIT(0)
21 #define  LPC18XX_CREG_CREG0_EN32KHZ		BIT(1)
22 #define  LPC18XX_CREG_CREG0_RESET32KHZ		BIT(2)
23 #define  LPC18XX_CREG_CREG0_PD32KHZ		BIT(3)
24 
25 #define to_clk_creg(_hw) container_of(_hw, struct clk_creg_data, hw)
26 
27 enum {
28 	CREG_CLK_1KHZ,
29 	CREG_CLK_32KHZ,
30 	CREG_CLK_MAX,
31 };
32 
33 struct clk_creg_data {
34 	struct clk_hw hw;
35 	const char *name;
36 	struct regmap *reg;
37 	unsigned int en_mask;
38 	const struct clk_ops *ops;
39 };
40 
41 #define CREG_CLK(_name, _emask, _ops)		\
42 {						\
43 	.name = _name,				\
44 	.en_mask = LPC18XX_CREG_CREG0_##_emask,	\
45 	.ops = &_ops,				\
46 }
47 
clk_creg_32k_prepare(struct clk_hw * hw)48 static int clk_creg_32k_prepare(struct clk_hw *hw)
49 {
50 	struct clk_creg_data *creg = to_clk_creg(hw);
51 	int ret;
52 
53 	ret = regmap_update_bits(creg->reg, LPC18XX_CREG_CREG0,
54 				 LPC18XX_CREG_CREG0_PD32KHZ |
55 				 LPC18XX_CREG_CREG0_RESET32KHZ, 0);
56 
57 	/*
58 	 * Powering up the 32k oscillator takes a long while
59 	 * and sadly there aren't any status bit to poll.
60 	 */
61 	msleep(2500);
62 
63 	return ret;
64 }
65 
clk_creg_32k_unprepare(struct clk_hw * hw)66 static void clk_creg_32k_unprepare(struct clk_hw *hw)
67 {
68 	struct clk_creg_data *creg = to_clk_creg(hw);
69 
70 	regmap_update_bits(creg->reg, LPC18XX_CREG_CREG0,
71 			   LPC18XX_CREG_CREG0_PD32KHZ,
72 			   LPC18XX_CREG_CREG0_PD32KHZ);
73 }
74 
clk_creg_32k_is_prepared(struct clk_hw * hw)75 static int clk_creg_32k_is_prepared(struct clk_hw *hw)
76 {
77 	struct clk_creg_data *creg = to_clk_creg(hw);
78 	u32 reg;
79 
80 	regmap_read(creg->reg, LPC18XX_CREG_CREG0, &reg);
81 
82 	return !(reg & LPC18XX_CREG_CREG0_PD32KHZ) &&
83 	       !(reg & LPC18XX_CREG_CREG0_RESET32KHZ);
84 }
85 
clk_creg_1k_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)86 static unsigned long clk_creg_1k_recalc_rate(struct clk_hw *hw,
87 					     unsigned long parent_rate)
88 {
89 	return parent_rate / 32;
90 }
91 
clk_creg_enable(struct clk_hw * hw)92 static int clk_creg_enable(struct clk_hw *hw)
93 {
94 	struct clk_creg_data *creg = to_clk_creg(hw);
95 
96 	return regmap_update_bits(creg->reg, LPC18XX_CREG_CREG0,
97 				  creg->en_mask, creg->en_mask);
98 }
99 
clk_creg_disable(struct clk_hw * hw)100 static void clk_creg_disable(struct clk_hw *hw)
101 {
102 	struct clk_creg_data *creg = to_clk_creg(hw);
103 
104 	regmap_update_bits(creg->reg, LPC18XX_CREG_CREG0,
105 			   creg->en_mask, 0);
106 }
107 
clk_creg_is_enabled(struct clk_hw * hw)108 static int clk_creg_is_enabled(struct clk_hw *hw)
109 {
110 	struct clk_creg_data *creg = to_clk_creg(hw);
111 	u32 reg;
112 
113 	regmap_read(creg->reg, LPC18XX_CREG_CREG0, &reg);
114 
115 	return !!(reg & creg->en_mask);
116 }
117 
118 static const struct clk_ops clk_creg_32k = {
119 	.enable		= clk_creg_enable,
120 	.disable	= clk_creg_disable,
121 	.is_enabled	= clk_creg_is_enabled,
122 	.prepare	= clk_creg_32k_prepare,
123 	.unprepare	= clk_creg_32k_unprepare,
124 	.is_prepared	= clk_creg_32k_is_prepared,
125 };
126 
127 static const struct clk_ops clk_creg_1k = {
128 	.enable		= clk_creg_enable,
129 	.disable	= clk_creg_disable,
130 	.is_enabled	= clk_creg_is_enabled,
131 	.recalc_rate	= clk_creg_1k_recalc_rate,
132 };
133 
134 static struct clk_creg_data clk_creg_clocks[] = {
135 	[CREG_CLK_1KHZ]  = CREG_CLK("1khz_clk",  EN1KHZ,  clk_creg_1k),
136 	[CREG_CLK_32KHZ] = CREG_CLK("32khz_clk", EN32KHZ, clk_creg_32k),
137 };
138 
clk_register_creg_clk(struct device * dev,struct clk_creg_data * creg_clk,const char ** parent_name,struct regmap * syscon)139 static struct clk *clk_register_creg_clk(struct device *dev,
140 					 struct clk_creg_data *creg_clk,
141 					 const char **parent_name,
142 					 struct regmap *syscon)
143 {
144 	struct clk_init_data init;
145 
146 	init.ops = creg_clk->ops;
147 	init.name = creg_clk->name;
148 	init.parent_names = parent_name;
149 	init.num_parents = 1;
150 	init.flags = 0;
151 
152 	creg_clk->reg = syscon;
153 	creg_clk->hw.init = &init;
154 
155 	if (dev)
156 		return devm_clk_register(dev, &creg_clk->hw);
157 
158 	return clk_register(NULL, &creg_clk->hw);
159 }
160 
161 static struct clk *clk_creg_early[CREG_CLK_MAX];
162 static struct clk_onecell_data clk_creg_early_data = {
163 	.clks = clk_creg_early,
164 	.clk_num = CREG_CLK_MAX,
165 };
166 
lpc18xx_creg_clk_init(struct device_node * np)167 static void __init lpc18xx_creg_clk_init(struct device_node *np)
168 {
169 	const char *clk_32khz_parent;
170 	struct regmap *syscon;
171 
172 	syscon = syscon_node_to_regmap(np->parent);
173 	if (IS_ERR(syscon)) {
174 		pr_err("%s: syscon lookup failed\n", __func__);
175 		return;
176 	}
177 
178 	clk_32khz_parent = of_clk_get_parent_name(np, 0);
179 
180 	clk_creg_early[CREG_CLK_32KHZ] =
181 		clk_register_creg_clk(NULL, &clk_creg_clocks[CREG_CLK_32KHZ],
182 				      &clk_32khz_parent, syscon);
183 	clk_creg_early[CREG_CLK_1KHZ] = ERR_PTR(-EPROBE_DEFER);
184 
185 	of_clk_add_provider(np, of_clk_src_onecell_get, &clk_creg_early_data);
186 }
187 CLK_OF_DECLARE_DRIVER(lpc18xx_creg_clk, "nxp,lpc1850-creg-clk",
188 		      lpc18xx_creg_clk_init);
189 
190 static struct clk *clk_creg[CREG_CLK_MAX];
191 static struct clk_onecell_data clk_creg_data = {
192 	.clks = clk_creg,
193 	.clk_num = CREG_CLK_MAX,
194 };
195 
lpc18xx_creg_clk_probe(struct platform_device * pdev)196 static int lpc18xx_creg_clk_probe(struct platform_device *pdev)
197 {
198 	struct device_node *np = pdev->dev.of_node;
199 	struct regmap *syscon;
200 
201 	syscon = syscon_node_to_regmap(np->parent);
202 	if (IS_ERR(syscon)) {
203 		dev_err(&pdev->dev, "syscon lookup failed\n");
204 		return PTR_ERR(syscon);
205 	}
206 
207 	clk_creg[CREG_CLK_32KHZ] = clk_creg_early[CREG_CLK_32KHZ];
208 	clk_creg[CREG_CLK_1KHZ] =
209 		clk_register_creg_clk(NULL, &clk_creg_clocks[CREG_CLK_1KHZ],
210 				      &clk_creg_clocks[CREG_CLK_32KHZ].name,
211 				      syscon);
212 
213 	return of_clk_add_provider(np, of_clk_src_onecell_get, &clk_creg_data);
214 }
215 
216 static const struct of_device_id lpc18xx_creg_clk_of_match[] = {
217 	{ .compatible = "nxp,lpc1850-creg-clk" },
218 	{},
219 };
220 
221 static struct platform_driver lpc18xx_creg_clk_driver = {
222 	.probe = lpc18xx_creg_clk_probe,
223 	.driver = {
224 		.name = "lpc18xx-creg-clk",
225 		.of_match_table = lpc18xx_creg_clk_of_match,
226 	},
227 };
228 builtin_platform_driver(lpc18xx_creg_clk_driver);
229