xref: /linux/drivers/clk/microchip/clk-mpfs.c (revision 90e0d94d369d342e735a75174439482119b6c393)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * PolarFire SoC MSS/core complex clock control
4  *
5  * Copyright (C) 2020-2022 Microchip Technology Inc. All rights reserved.
6  */
7 #include <linux/auxiliary_bus.h>
8 #include <linux/clk-provider.h>
9 #include <linux/io.h>
10 #include <linux/module.h>
11 #include <linux/platform_device.h>
12 #include <linux/slab.h>
13 #include <dt-bindings/clock/microchip,mpfs-clock.h>
14 #include <soc/microchip/mpfs.h>
15 
16 /* address offset of control registers */
17 #define REG_MSSPLL_REF_CR	0x08u
18 #define REG_MSSPLL_POSTDIV_CR	0x10u
19 #define REG_MSSPLL_SSCG_2_CR	0x2Cu
20 #define REG_CLOCK_CONFIG_CR	0x08u
21 #define REG_RTC_CLOCK_CR	0x0Cu
22 #define REG_SUBBLK_CLOCK_CR	0x84u
23 #define REG_SUBBLK_RESET_CR	0x88u
24 
25 #define MSSPLL_FBDIV_SHIFT	0x00u
26 #define MSSPLL_FBDIV_WIDTH	0x0Cu
27 #define MSSPLL_REFDIV_SHIFT	0x08u
28 #define MSSPLL_REFDIV_WIDTH	0x06u
29 #define MSSPLL_POSTDIV_SHIFT	0x08u
30 #define MSSPLL_POSTDIV_WIDTH	0x07u
31 #define MSSPLL_FIXED_DIV	4u
32 
33 struct mpfs_clock_data {
34 	struct device *dev;
35 	void __iomem *base;
36 	void __iomem *msspll_base;
37 	struct clk_hw_onecell_data hw_data;
38 };
39 
40 struct mpfs_msspll_hw_clock {
41 	void __iomem *base;
42 	unsigned int id;
43 	u32 reg_offset;
44 	u32 shift;
45 	u32 width;
46 	u32 flags;
47 	struct clk_hw hw;
48 	struct clk_init_data init;
49 };
50 
51 #define to_mpfs_msspll_clk(_hw) container_of(_hw, struct mpfs_msspll_hw_clock, hw)
52 
53 struct mpfs_cfg_hw_clock {
54 	struct clk_divider cfg;
55 	struct clk_init_data init;
56 	unsigned int id;
57 	u32 reg_offset;
58 };
59 
60 struct mpfs_periph_hw_clock {
61 	struct clk_gate periph;
62 	unsigned int id;
63 };
64 
65 /*
66  * mpfs_clk_lock prevents anything else from writing to the
67  * mpfs clk block while a software locked register is being written.
68  */
69 static DEFINE_SPINLOCK(mpfs_clk_lock);
70 
71 static const struct clk_parent_data mpfs_ext_ref[] = {
72 	{ .index = 0 },
73 };
74 
75 static const struct clk_div_table mpfs_div_cpu_axi_table[] = {
76 	{ 0, 1 }, { 1, 2 }, { 2, 4 }, { 3, 8 },
77 	{ 0, 0 }
78 };
79 
80 static const struct clk_div_table mpfs_div_ahb_table[] = {
81 	{ 1, 2 }, { 2, 4}, { 3, 8 },
82 	{ 0, 0 }
83 };
84 
85 /*
86  * The only two supported reference clock frequencies for the PolarFire SoC are
87  * 100 and 125 MHz, as the rtc reference is required to be 1 MHz.
88  * It therefore only needs to have divider table entries corresponding to
89  * divide by 100 and 125.
90  */
91 static const struct clk_div_table mpfs_div_rtcref_table[] = {
92 	{ 100, 100 }, { 125, 125 },
93 	{ 0, 0 }
94 };
95 
96 static unsigned long mpfs_clk_msspll_recalc_rate(struct clk_hw *hw, unsigned long prate)
97 {
98 	struct mpfs_msspll_hw_clock *msspll_hw = to_mpfs_msspll_clk(hw);
99 	void __iomem *mult_addr = msspll_hw->base + msspll_hw->reg_offset;
100 	void __iomem *ref_div_addr = msspll_hw->base + REG_MSSPLL_REF_CR;
101 	void __iomem *postdiv_addr = msspll_hw->base + REG_MSSPLL_POSTDIV_CR;
102 	u32 mult, ref_div, postdiv;
103 
104 	mult = readl_relaxed(mult_addr) >> MSSPLL_FBDIV_SHIFT;
105 	mult &= clk_div_mask(MSSPLL_FBDIV_WIDTH);
106 	ref_div = readl_relaxed(ref_div_addr) >> MSSPLL_REFDIV_SHIFT;
107 	ref_div &= clk_div_mask(MSSPLL_REFDIV_WIDTH);
108 	postdiv = readl_relaxed(postdiv_addr) >> MSSPLL_POSTDIV_SHIFT;
109 	postdiv &= clk_div_mask(MSSPLL_POSTDIV_WIDTH);
110 
111 	return prate * mult / (ref_div * MSSPLL_FIXED_DIV * postdiv);
112 }
113 
114 static long mpfs_clk_msspll_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate)
115 {
116 	struct mpfs_msspll_hw_clock *msspll_hw = to_mpfs_msspll_clk(hw);
117 	void __iomem *mult_addr = msspll_hw->base + msspll_hw->reg_offset;
118 	void __iomem *ref_div_addr = msspll_hw->base + REG_MSSPLL_REF_CR;
119 	u32 mult, ref_div;
120 	unsigned long rate_before_ctrl;
121 
122 	mult = readl_relaxed(mult_addr) >> MSSPLL_FBDIV_SHIFT;
123 	mult &= clk_div_mask(MSSPLL_FBDIV_WIDTH);
124 	ref_div = readl_relaxed(ref_div_addr) >> MSSPLL_REFDIV_SHIFT;
125 	ref_div &= clk_div_mask(MSSPLL_REFDIV_WIDTH);
126 
127 	rate_before_ctrl = rate * (ref_div * MSSPLL_FIXED_DIV) / mult;
128 
129 	return divider_round_rate(hw, rate_before_ctrl, prate, NULL, MSSPLL_POSTDIV_WIDTH,
130 				  msspll_hw->flags);
131 }
132 
133 static int mpfs_clk_msspll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long prate)
134 {
135 	struct mpfs_msspll_hw_clock *msspll_hw = to_mpfs_msspll_clk(hw);
136 	void __iomem *mult_addr = msspll_hw->base + msspll_hw->reg_offset;
137 	void __iomem *ref_div_addr = msspll_hw->base + REG_MSSPLL_REF_CR;
138 	void __iomem *postdiv_addr = msspll_hw->base + REG_MSSPLL_POSTDIV_CR;
139 	u32 mult, ref_div, postdiv;
140 	int divider_setting;
141 	unsigned long rate_before_ctrl, flags;
142 
143 	mult = readl_relaxed(mult_addr) >> MSSPLL_FBDIV_SHIFT;
144 	mult &= clk_div_mask(MSSPLL_FBDIV_WIDTH);
145 	ref_div = readl_relaxed(ref_div_addr) >> MSSPLL_REFDIV_SHIFT;
146 	ref_div &= clk_div_mask(MSSPLL_REFDIV_WIDTH);
147 
148 	rate_before_ctrl = rate * (ref_div * MSSPLL_FIXED_DIV) / mult;
149 	divider_setting = divider_get_val(rate_before_ctrl, prate, NULL, MSSPLL_POSTDIV_WIDTH,
150 					  msspll_hw->flags);
151 
152 	if (divider_setting < 0)
153 		return divider_setting;
154 
155 	spin_lock_irqsave(&mpfs_clk_lock, flags);
156 
157 	postdiv = readl_relaxed(postdiv_addr);
158 	postdiv &= ~(clk_div_mask(MSSPLL_POSTDIV_WIDTH) << MSSPLL_POSTDIV_SHIFT);
159 	writel_relaxed(postdiv, postdiv_addr);
160 
161 	spin_unlock_irqrestore(&mpfs_clk_lock, flags);
162 
163 	return 0;
164 }
165 
166 static const struct clk_ops mpfs_clk_msspll_ops = {
167 	.recalc_rate = mpfs_clk_msspll_recalc_rate,
168 	.round_rate = mpfs_clk_msspll_round_rate,
169 	.set_rate = mpfs_clk_msspll_set_rate,
170 };
171 
172 #define CLK_PLL(_id, _name, _parent, _shift, _width, _flags, _offset) {			\
173 	.id = _id,									\
174 	.shift = _shift,								\
175 	.width = _width,								\
176 	.reg_offset = _offset,								\
177 	.flags = _flags,								\
178 	.hw.init = CLK_HW_INIT_PARENTS_DATA(_name, _parent, &mpfs_clk_msspll_ops, 0),	\
179 }
180 
181 static struct mpfs_msspll_hw_clock mpfs_msspll_clks[] = {
182 	CLK_PLL(CLK_MSSPLL, "clk_msspll", mpfs_ext_ref, MSSPLL_FBDIV_SHIFT,
183 		MSSPLL_FBDIV_WIDTH, 0, REG_MSSPLL_SSCG_2_CR),
184 };
185 
186 static int mpfs_clk_register_mssplls(struct device *dev, struct mpfs_msspll_hw_clock *msspll_hws,
187 				     unsigned int num_clks, struct mpfs_clock_data *data)
188 {
189 	unsigned int i;
190 	int ret;
191 
192 	for (i = 0; i < num_clks; i++) {
193 		struct mpfs_msspll_hw_clock *msspll_hw = &msspll_hws[i];
194 
195 		msspll_hw->base = data->msspll_base;
196 		ret = devm_clk_hw_register(dev, &msspll_hw->hw);
197 		if (ret)
198 			return dev_err_probe(dev, ret, "failed to register msspll id: %d\n",
199 					     CLK_MSSPLL);
200 
201 		data->hw_data.hws[msspll_hw->id] = &msspll_hw->hw;
202 	}
203 
204 	return 0;
205 }
206 
207 /*
208  * "CFG" clocks
209  */
210 
211 #define CLK_CFG(_id, _name, _parent, _shift, _width, _table, _flags, _offset) {		\
212 	.id = _id,									\
213 	.cfg.shift = _shift,								\
214 	.cfg.width = _width,								\
215 	.cfg.table = _table,								\
216 	.reg_offset = _offset,								\
217 	.cfg.flags = _flags,								\
218 	.cfg.hw.init = CLK_HW_INIT(_name, _parent, &clk_divider_ops, 0),		\
219 	.cfg.lock = &mpfs_clk_lock,							\
220 }
221 
222 #define CLK_CPU_OFFSET		0u
223 #define CLK_AXI_OFFSET		1u
224 #define CLK_AHB_OFFSET		2u
225 #define CLK_RTCREF_OFFSET	3u
226 
227 static struct mpfs_cfg_hw_clock mpfs_cfg_clks[] = {
228 	CLK_CFG(CLK_CPU, "clk_cpu", "clk_msspll", 0, 2, mpfs_div_cpu_axi_table, 0,
229 		REG_CLOCK_CONFIG_CR),
230 	CLK_CFG(CLK_AXI, "clk_axi", "clk_msspll", 2, 2, mpfs_div_cpu_axi_table, 0,
231 		REG_CLOCK_CONFIG_CR),
232 	CLK_CFG(CLK_AHB, "clk_ahb", "clk_msspll", 4, 2, mpfs_div_ahb_table, 0,
233 		REG_CLOCK_CONFIG_CR),
234 	{
235 		.id = CLK_RTCREF,
236 		.cfg.shift = 0,
237 		.cfg.width = 12,
238 		.cfg.table = mpfs_div_rtcref_table,
239 		.reg_offset = REG_RTC_CLOCK_CR,
240 		.cfg.flags = CLK_DIVIDER_ONE_BASED,
241 		.cfg.hw.init =
242 			CLK_HW_INIT_PARENTS_DATA("clk_rtcref", mpfs_ext_ref, &clk_divider_ops, 0),
243 	}
244 };
245 
246 static int mpfs_clk_register_cfgs(struct device *dev, struct mpfs_cfg_hw_clock *cfg_hws,
247 				  unsigned int num_clks, struct mpfs_clock_data *data)
248 {
249 	unsigned int i, id;
250 	int ret;
251 
252 	for (i = 0; i < num_clks; i++) {
253 		struct mpfs_cfg_hw_clock *cfg_hw = &cfg_hws[i];
254 
255 		cfg_hw->cfg.reg = data->base + cfg_hw->reg_offset;
256 		ret = devm_clk_hw_register(dev, &cfg_hw->cfg.hw);
257 		if (ret)
258 			return dev_err_probe(dev, ret, "failed to register clock id: %d\n",
259 					     cfg_hw->id);
260 
261 		id = cfg_hw->id;
262 		data->hw_data.hws[id] = &cfg_hw->cfg.hw;
263 	}
264 
265 	return 0;
266 }
267 
268 /*
269  * peripheral clocks - devices connected to axi or ahb buses.
270  */
271 
272 #define CLK_PERIPH(_id, _name, _parent, _shift, _flags) {			\
273 	.id = _id,								\
274 	.periph.bit_idx = _shift,						\
275 	.periph.hw.init = CLK_HW_INIT_HW(_name, _parent, &clk_gate_ops,		\
276 				  _flags),					\
277 	.periph.lock = &mpfs_clk_lock,						\
278 }
279 
280 #define PARENT_CLK(PARENT) (&mpfs_cfg_clks[CLK_##PARENT##_OFFSET].cfg.hw)
281 
282 /*
283  * Critical clocks:
284  * - CLK_ENVM: reserved by hart software services (hss) superloop monitor/m mode interrupt
285  *   trap handler
286  * - CLK_MMUART0: reserved by the hss
287  * - CLK_DDRC: provides clock to the ddr subsystem
288  * - CLK_RTC: the onboard RTC's AHB bus clock must be kept running as the rtc will stop
289  *   if the AHB interface clock is disabled
290  * - CLK_FICx: these provide the processor side clocks to the "FIC" (Fabric InterConnect)
291  *   clock domain crossers which provide the interface to the FPGA fabric. Disabling them
292  *   causes the FPGA fabric to go into reset.
293  * - CLK_ATHENA: The athena clock is FIC4, which is reserved for the Athena TeraFire.
294  */
295 
296 static struct mpfs_periph_hw_clock mpfs_periph_clks[] = {
297 	CLK_PERIPH(CLK_ENVM, "clk_periph_envm", PARENT_CLK(AHB), 0, CLK_IS_CRITICAL),
298 	CLK_PERIPH(CLK_MAC0, "clk_periph_mac0", PARENT_CLK(AHB), 1, 0),
299 	CLK_PERIPH(CLK_MAC1, "clk_periph_mac1", PARENT_CLK(AHB), 2, 0),
300 	CLK_PERIPH(CLK_MMC, "clk_periph_mmc", PARENT_CLK(AHB), 3, 0),
301 	CLK_PERIPH(CLK_TIMER, "clk_periph_timer", PARENT_CLK(RTCREF), 4, 0),
302 	CLK_PERIPH(CLK_MMUART0, "clk_periph_mmuart0", PARENT_CLK(AHB), 5, CLK_IS_CRITICAL),
303 	CLK_PERIPH(CLK_MMUART1, "clk_periph_mmuart1", PARENT_CLK(AHB), 6, 0),
304 	CLK_PERIPH(CLK_MMUART2, "clk_periph_mmuart2", PARENT_CLK(AHB), 7, 0),
305 	CLK_PERIPH(CLK_MMUART3, "clk_periph_mmuart3", PARENT_CLK(AHB), 8, 0),
306 	CLK_PERIPH(CLK_MMUART4, "clk_periph_mmuart4", PARENT_CLK(AHB), 9, 0),
307 	CLK_PERIPH(CLK_SPI0, "clk_periph_spi0", PARENT_CLK(AHB), 10, 0),
308 	CLK_PERIPH(CLK_SPI1, "clk_periph_spi1", PARENT_CLK(AHB), 11, 0),
309 	CLK_PERIPH(CLK_I2C0, "clk_periph_i2c0", PARENT_CLK(AHB), 12, 0),
310 	CLK_PERIPH(CLK_I2C1, "clk_periph_i2c1", PARENT_CLK(AHB), 13, 0),
311 	CLK_PERIPH(CLK_CAN0, "clk_periph_can0", PARENT_CLK(AHB), 14, 0),
312 	CLK_PERIPH(CLK_CAN1, "clk_periph_can1", PARENT_CLK(AHB), 15, 0),
313 	CLK_PERIPH(CLK_USB, "clk_periph_usb", PARENT_CLK(AHB), 16, 0),
314 	CLK_PERIPH(CLK_RTC, "clk_periph_rtc", PARENT_CLK(AHB), 18, CLK_IS_CRITICAL),
315 	CLK_PERIPH(CLK_QSPI, "clk_periph_qspi", PARENT_CLK(AHB), 19, 0),
316 	CLK_PERIPH(CLK_GPIO0, "clk_periph_gpio0", PARENT_CLK(AHB), 20, 0),
317 	CLK_PERIPH(CLK_GPIO1, "clk_periph_gpio1", PARENT_CLK(AHB), 21, 0),
318 	CLK_PERIPH(CLK_GPIO2, "clk_periph_gpio2", PARENT_CLK(AHB), 22, 0),
319 	CLK_PERIPH(CLK_DDRC, "clk_periph_ddrc", PARENT_CLK(AHB), 23, CLK_IS_CRITICAL),
320 	CLK_PERIPH(CLK_FIC0, "clk_periph_fic0", PARENT_CLK(AXI), 24, CLK_IS_CRITICAL),
321 	CLK_PERIPH(CLK_FIC1, "clk_periph_fic1", PARENT_CLK(AXI), 25, CLK_IS_CRITICAL),
322 	CLK_PERIPH(CLK_FIC2, "clk_periph_fic2", PARENT_CLK(AXI), 26, CLK_IS_CRITICAL),
323 	CLK_PERIPH(CLK_FIC3, "clk_periph_fic3", PARENT_CLK(AXI), 27, CLK_IS_CRITICAL),
324 	CLK_PERIPH(CLK_ATHENA, "clk_periph_athena", PARENT_CLK(AXI), 28, CLK_IS_CRITICAL),
325 	CLK_PERIPH(CLK_CFM, "clk_periph_cfm", PARENT_CLK(AHB), 29, 0),
326 };
327 
328 static int mpfs_clk_register_periphs(struct device *dev, struct mpfs_periph_hw_clock *periph_hws,
329 				     int num_clks, struct mpfs_clock_data *data)
330 {
331 	unsigned int i, id;
332 	int ret;
333 
334 	for (i = 0; i < num_clks; i++) {
335 		struct mpfs_periph_hw_clock *periph_hw = &periph_hws[i];
336 
337 		periph_hw->periph.reg = data->base + REG_SUBBLK_CLOCK_CR;
338 		ret = devm_clk_hw_register(dev, &periph_hw->periph.hw);
339 		if (ret)
340 			return dev_err_probe(dev, ret, "failed to register clock id: %d\n",
341 					     periph_hw->id);
342 
343 		id = periph_hws[i].id;
344 		data->hw_data.hws[id] = &periph_hw->periph.hw;
345 	}
346 
347 	return 0;
348 }
349 
350 /*
351  * Peripheral clock resets
352  */
353 
354 #if IS_ENABLED(CONFIG_RESET_CONTROLLER)
355 
356 u32 mpfs_reset_read(struct device *dev)
357 {
358 	struct mpfs_clock_data *clock_data = dev_get_drvdata(dev->parent);
359 
360 	return readl_relaxed(clock_data->base + REG_SUBBLK_RESET_CR);
361 }
362 EXPORT_SYMBOL_NS_GPL(mpfs_reset_read, MCHP_CLK_MPFS);
363 
364 void mpfs_reset_write(struct device *dev, u32 val)
365 {
366 	struct mpfs_clock_data *clock_data = dev_get_drvdata(dev->parent);
367 
368 	writel_relaxed(val, clock_data->base + REG_SUBBLK_RESET_CR);
369 }
370 EXPORT_SYMBOL_NS_GPL(mpfs_reset_write, MCHP_CLK_MPFS);
371 
372 static void mpfs_reset_unregister_adev(void *_adev)
373 {
374 	struct auxiliary_device *adev = _adev;
375 
376 	auxiliary_device_delete(adev);
377 }
378 
379 static void mpfs_reset_adev_release(struct device *dev)
380 {
381 	struct auxiliary_device *adev = to_auxiliary_dev(dev);
382 
383 	auxiliary_device_uninit(adev);
384 
385 	kfree(adev);
386 }
387 
388 static struct auxiliary_device *mpfs_reset_adev_alloc(struct mpfs_clock_data *clk_data)
389 {
390 	struct auxiliary_device *adev;
391 	int ret;
392 
393 	adev = kzalloc(sizeof(*adev), GFP_KERNEL);
394 	if (!adev)
395 		return ERR_PTR(-ENOMEM);
396 
397 	adev->name = "reset-mpfs";
398 	adev->dev.parent = clk_data->dev;
399 	adev->dev.release = mpfs_reset_adev_release;
400 	adev->id = 666u;
401 
402 	ret = auxiliary_device_init(adev);
403 	if (ret) {
404 		kfree(adev);
405 		return ERR_PTR(ret);
406 	}
407 
408 	return adev;
409 }
410 
411 static int mpfs_reset_controller_register(struct mpfs_clock_data *clk_data)
412 {
413 	struct auxiliary_device *adev;
414 	int ret;
415 
416 	adev = mpfs_reset_adev_alloc(clk_data);
417 	if (IS_ERR(adev))
418 		return PTR_ERR(adev);
419 
420 	ret = auxiliary_device_add(adev);
421 	if (ret) {
422 		auxiliary_device_uninit(adev);
423 		return ret;
424 	}
425 
426 	return devm_add_action_or_reset(clk_data->dev, mpfs_reset_unregister_adev, adev);
427 }
428 
429 #else /* !CONFIG_RESET_CONTROLLER */
430 
431 static int mpfs_reset_controller_register(struct mpfs_clock_data *clk_data)
432 {
433 	return 0;
434 }
435 
436 #endif /* !CONFIG_RESET_CONTROLLER */
437 
438 static int mpfs_clk_probe(struct platform_device *pdev)
439 {
440 	struct device *dev = &pdev->dev;
441 	struct mpfs_clock_data *clk_data;
442 	unsigned int num_clks;
443 	int ret;
444 
445 	/* CLK_RESERVED is not part of clock arrays, so add 1 */
446 	num_clks = ARRAY_SIZE(mpfs_msspll_clks) + ARRAY_SIZE(mpfs_cfg_clks)
447 		   + ARRAY_SIZE(mpfs_periph_clks) + 1;
448 
449 	clk_data = devm_kzalloc(dev, struct_size(clk_data, hw_data.hws, num_clks), GFP_KERNEL);
450 	if (!clk_data)
451 		return -ENOMEM;
452 
453 	clk_data->base = devm_platform_ioremap_resource(pdev, 0);
454 	if (IS_ERR(clk_data->base))
455 		return PTR_ERR(clk_data->base);
456 
457 	clk_data->msspll_base = devm_platform_ioremap_resource(pdev, 1);
458 	if (IS_ERR(clk_data->msspll_base))
459 		return PTR_ERR(clk_data->msspll_base);
460 
461 	clk_data->hw_data.num = num_clks;
462 	clk_data->dev = dev;
463 	dev_set_drvdata(dev, clk_data);
464 
465 	ret = mpfs_clk_register_mssplls(dev, mpfs_msspll_clks, ARRAY_SIZE(mpfs_msspll_clks),
466 					clk_data);
467 	if (ret)
468 		return ret;
469 
470 	ret = mpfs_clk_register_cfgs(dev, mpfs_cfg_clks, ARRAY_SIZE(mpfs_cfg_clks), clk_data);
471 	if (ret)
472 		return ret;
473 
474 	ret = mpfs_clk_register_periphs(dev, mpfs_periph_clks, ARRAY_SIZE(mpfs_periph_clks),
475 					clk_data);
476 	if (ret)
477 		return ret;
478 
479 	ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, &clk_data->hw_data);
480 	if (ret)
481 		return ret;
482 
483 	return mpfs_reset_controller_register(clk_data);
484 }
485 
486 static const struct of_device_id mpfs_clk_of_match_table[] = {
487 	{ .compatible = "microchip,mpfs-clkcfg", },
488 	{}
489 };
490 MODULE_DEVICE_TABLE(of, mpfs_clk_of_match_table);
491 
492 static struct platform_driver mpfs_clk_driver = {
493 	.probe = mpfs_clk_probe,
494 	.driver	= {
495 		.name = "microchip-mpfs-clkcfg",
496 		.of_match_table = mpfs_clk_of_match_table,
497 	},
498 };
499 
500 static int __init clk_mpfs_init(void)
501 {
502 	return platform_driver_register(&mpfs_clk_driver);
503 }
504 core_initcall(clk_mpfs_init);
505 
506 static void __exit clk_mpfs_exit(void)
507 {
508 	platform_driver_unregister(&mpfs_clk_driver);
509 }
510 module_exit(clk_mpfs_exit);
511 
512 MODULE_DESCRIPTION("Microchip PolarFire SoC Clock Driver");
513 MODULE_AUTHOR("Padmarao Begari <padmarao.begari@microchip.com>");
514 MODULE_AUTHOR("Daire McNamara <daire.mcnamara@microchip.com>");
515 MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>");
516 MODULE_LICENSE("GPL");
517