44 *
55 * Copyright (C) 2020-2022 Microchip Technology Inc. All rights reserved.
66 */
7+ #include <linux/cleanup.h>
78#include <linux/clk-provider.h>
89#include <linux/io.h>
10+ #include <linux/mfd/syscon.h>
911#include <linux/module.h>
1012#include <linux/platform_device.h>
13+ #include <linux/regmap.h>
1114#include <dt-bindings/clock/microchip,mpfs-clock.h>
1215#include <soc/microchip/mpfs.h>
1316
3033#define MSSPLL_POSTDIV_WIDTH 0x07u
3134#define MSSPLL_FIXED_DIV 4u
3235
36+ static const struct regmap_config mpfs_clk_regmap_config = {
37+ .reg_bits = 32 ,
38+ .reg_stride = 4 ,
39+ .val_bits = 32 ,
40+ .val_format_endian = REGMAP_ENDIAN_LITTLE ,
41+ .max_register = REG_SUBBLK_CLOCK_CR ,
42+ };
43+
3344/*
3445 * This clock ID is defined here, rather than the binding headers, as it is an
3546 * internal clock only, and therefore has no consumers in other peripheral
3950
4051struct mpfs_clock_data {
4152 struct device * dev ;
53+ struct regmap * regmap ;
4254 void __iomem * base ;
4355 void __iomem * msspll_base ;
4456 struct clk_hw_onecell_data hw_data ;
@@ -67,21 +79,39 @@ struct mpfs_msspll_out_hw_clock {
6779
6880#define to_mpfs_msspll_out_clk (_hw ) container_of(_hw, struct mpfs_msspll_out_hw_clock, hw)
6981
82+ struct mpfs_cfg_clock {
83+ struct regmap * map ;
84+ const struct clk_div_table * table ;
85+ u8 map_offset ;
86+ u8 shift ;
87+ u8 width ;
88+ u8 flags ;
89+ };
90+
7091struct mpfs_cfg_hw_clock {
71- struct clk_divider cfg ;
72- struct clk_init_data init ;
92+ struct clk_hw hw ;
93+ struct mpfs_cfg_clock cfg ;
7394 unsigned int id ;
74- u32 reg_offset ;
95+ };
96+
97+ #define to_mpfs_cfg_clk (_hw ) container_of(_hw, struct mpfs_cfg_hw_clock, hw)
98+
99+ struct mpfs_periph_clock {
100+ struct regmap * map ;
101+ u8 map_offset ;
102+ u8 shift ;
75103};
76104
77105struct mpfs_periph_hw_clock {
78- struct clk_gate periph ;
106+ struct clk_hw hw ;
107+ struct mpfs_periph_clock periph ;
79108 unsigned int id ;
80109};
81110
111+ #define to_mpfs_periph_clk (_hw ) container_of(_hw, struct mpfs_periph_hw_clock, hw)
112+
82113/*
83- * mpfs_clk_lock prevents anything else from writing to the
84- * mpfs clk block while a software locked register is being written.
114+ * Protects MSSPLL outputs, since there's two to a register
85115 */
86116static DEFINE_SPINLOCK (mpfs_clk_lock );
87117
@@ -219,16 +249,61 @@ static int mpfs_clk_register_msspll_outs(struct device *dev,
219249/*
220250 * "CFG" clocks
221251 */
252+ static unsigned long mpfs_cfg_clk_recalc_rate (struct clk_hw * hw , unsigned long prate )
253+ {
254+ struct mpfs_cfg_hw_clock * cfg_hw = to_mpfs_cfg_clk (hw );
255+ struct mpfs_cfg_clock * cfg = & cfg_hw -> cfg ;
256+ u32 val ;
222257
223- #define CLK_CFG (_id , _name , _parent , _shift , _width , _table , _flags , _offset ) { \
224- .id = _id, \
225- .cfg.shift = _shift, \
226- .cfg.width = _width, \
227- .cfg.table = _table, \
228- .reg_offset = _offset, \
229- .cfg.flags = _flags, \
230- .cfg.hw.init = CLK_HW_INIT(_name, _parent, &clk_divider_ops, 0), \
231- .cfg.lock = &mpfs_clk_lock, \
258+ regmap_read (cfg -> map , cfg -> map_offset , & val );
259+ val >>= cfg -> shift ;
260+ val &= clk_div_mask (cfg -> width );
261+
262+ return divider_recalc_rate (hw , prate , val , cfg -> table , cfg -> flags , cfg -> width );
263+ }
264+
265+ static int mpfs_cfg_clk_determine_rate (struct clk_hw * hw , struct clk_rate_request * req )
266+ {
267+ struct mpfs_cfg_hw_clock * cfg_hw = to_mpfs_cfg_clk (hw );
268+ struct mpfs_cfg_clock * cfg = & cfg_hw -> cfg ;
269+
270+ return divider_determine_rate (hw , req , cfg -> table , cfg -> width , 0 );
271+ }
272+
273+ static int mpfs_cfg_clk_set_rate (struct clk_hw * hw , unsigned long rate , unsigned long prate )
274+ {
275+ struct mpfs_cfg_hw_clock * cfg_hw = to_mpfs_cfg_clk (hw );
276+ struct mpfs_cfg_clock * cfg = & cfg_hw -> cfg ;
277+ int divider_setting ;
278+ u32 val ;
279+ u32 mask ;
280+
281+ divider_setting = divider_get_val (rate , prate , cfg -> table , cfg -> width , 0 );
282+
283+ if (divider_setting < 0 )
284+ return divider_setting ;
285+
286+ mask = clk_div_mask (cfg -> width ) << cfg -> shift ;
287+ val = divider_setting << cfg -> shift ;
288+ regmap_update_bits (cfg -> map , cfg -> map_offset , val , mask );
289+
290+ return 0 ;
291+ }
292+
293+ static const struct clk_ops mpfs_clk_cfg_ops = {
294+ .recalc_rate = mpfs_cfg_clk_recalc_rate ,
295+ .determine_rate = mpfs_cfg_clk_determine_rate ,
296+ .set_rate = mpfs_cfg_clk_set_rate ,
297+ };
298+
299+ #define CLK_CFG (_id , _name , _parent , _shift , _width , _table , _flags , _offset ) { \
300+ .id = _id, \
301+ .cfg.shift = _shift, \
302+ .cfg.width = _width, \
303+ .cfg.table = _table, \
304+ .cfg.map_offset = _offset, \
305+ .cfg.flags = _flags, \
306+ .hw.init = CLK_HW_INIT(_name, _parent, &mpfs_clk_cfg_ops, 0), \
232307}
233308
234309#define CLK_CPU_OFFSET 0u
@@ -248,10 +323,10 @@ static struct mpfs_cfg_hw_clock mpfs_cfg_clks[] = {
248323 .cfg .shift = 0 ,
249324 .cfg .width = 12 ,
250325 .cfg .table = mpfs_div_rtcref_table ,
251- .reg_offset = REG_RTC_CLOCK_CR ,
326+ .cfg . map_offset = REG_RTC_CLOCK_CR ,
252327 .cfg .flags = CLK_DIVIDER_ONE_BASED ,
253- .cfg . hw .init =
254- CLK_HW_INIT_PARENTS_DATA ("clk_rtcref" , mpfs_ext_ref , & clk_divider_ops , 0 ),
328+ .hw .init =
329+ CLK_HW_INIT_PARENTS_DATA ("clk_rtcref" , mpfs_ext_ref , & mpfs_clk_cfg_ops , 0 ),
255330 }
256331};
257332
@@ -264,14 +339,14 @@ static int mpfs_clk_register_cfgs(struct device *dev, struct mpfs_cfg_hw_clock *
264339 for (i = 0 ; i < num_clks ; i ++ ) {
265340 struct mpfs_cfg_hw_clock * cfg_hw = & cfg_hws [i ];
266341
267- cfg_hw -> cfg .reg = data -> base + cfg_hw -> reg_offset ;
268- ret = devm_clk_hw_register (dev , & cfg_hw -> cfg . hw );
342+ cfg_hw -> cfg .map = data -> regmap ;
343+ ret = devm_clk_hw_register (dev , & cfg_hw -> hw );
269344 if (ret )
270345 return dev_err_probe (dev , ret , "failed to register clock id: %d\n" ,
271346 cfg_hw -> id );
272347
273348 id = cfg_hw -> id ;
274- data -> hw_data .hws [id ] = & cfg_hw -> cfg . hw ;
349+ data -> hw_data .hws [id ] = & cfg_hw -> hw ;
275350 }
276351
277352 return 0 ;
@@ -281,15 +356,50 @@ static int mpfs_clk_register_cfgs(struct device *dev, struct mpfs_cfg_hw_clock *
281356 * peripheral clocks - devices connected to axi or ahb buses.
282357 */
283358
284- #define CLK_PERIPH (_id , _name , _parent , _shift , _flags ) { \
285- .id = _id, \
286- .periph.bit_idx = _shift, \
287- .periph.hw.init = CLK_HW_INIT_HW(_name, _parent, &clk_gate_ops, \
288- _flags), \
289- .periph.lock = &mpfs_clk_lock, \
359+ static int mpfs_periph_clk_enable (struct clk_hw * hw )
360+ {
361+ struct mpfs_periph_hw_clock * periph_hw = to_mpfs_periph_clk (hw );
362+ struct mpfs_periph_clock * periph = & periph_hw -> periph ;
363+
364+ regmap_update_bits (periph -> map , periph -> map_offset ,
365+ BIT (periph -> shift ), BIT (periph -> shift ));
366+
367+ return 0 ;
290368}
291369
292- #define PARENT_CLK (PARENT ) (&mpfs_cfg_clks[CLK_##PARENT##_OFFSET].cfg.hw)
370+ static void mpfs_periph_clk_disable (struct clk_hw * hw )
371+ {
372+ struct mpfs_periph_hw_clock * periph_hw = to_mpfs_periph_clk (hw );
373+ struct mpfs_periph_clock * periph = & periph_hw -> periph ;
374+
375+ regmap_update_bits (periph -> map , periph -> map_offset , BIT (periph -> shift ), 0 );
376+ }
377+
378+ static int mpfs_periph_clk_is_enabled (struct clk_hw * hw )
379+ {
380+ struct mpfs_periph_hw_clock * periph_hw = to_mpfs_periph_clk (hw );
381+ struct mpfs_periph_clock * periph = & periph_hw -> periph ;
382+ u32 val ;
383+
384+ regmap_read (periph -> map , periph -> map_offset , & val );
385+
386+ return !!(val & BIT (periph -> shift ));
387+ }
388+
389+ static const struct clk_ops mpfs_periph_clk_ops = {
390+ .enable = mpfs_periph_clk_enable ,
391+ .disable = mpfs_periph_clk_disable ,
392+ .is_enabled = mpfs_periph_clk_is_enabled ,
393+ };
394+
395+ #define CLK_PERIPH (_id , _name , _parent , _shift , _flags ) { \
396+ .id = _id, \
397+ .periph.map_offset = REG_SUBBLK_CLOCK_CR, \
398+ .periph.shift = _shift, \
399+ .hw.init = CLK_HW_INIT_HW(_name, _parent, &mpfs_periph_clk_ops, _flags), \
400+ }
401+
402+ #define PARENT_CLK (PARENT ) (&mpfs_cfg_clks[CLK_##PARENT##_OFFSET].hw)
293403
294404/*
295405 * Critical clocks:
@@ -346,19 +456,55 @@ static int mpfs_clk_register_periphs(struct device *dev, struct mpfs_periph_hw_c
346456 for (i = 0 ; i < num_clks ; i ++ ) {
347457 struct mpfs_periph_hw_clock * periph_hw = & periph_hws [i ];
348458
349- periph_hw -> periph .reg = data -> base + REG_SUBBLK_CLOCK_CR ;
350- ret = devm_clk_hw_register (dev , & periph_hw -> periph . hw );
459+ periph_hw -> periph .map = data -> regmap ;
460+ ret = devm_clk_hw_register (dev , & periph_hw -> hw );
351461 if (ret )
352462 return dev_err_probe (dev , ret , "failed to register clock id: %d\n" ,
353463 periph_hw -> id );
354464
355465 id = periph_hws [i ].id ;
356- data -> hw_data .hws [id ] = & periph_hw -> periph . hw ;
466+ data -> hw_data .hws [id ] = & periph_hw -> hw ;
357467 }
358468
359469 return 0 ;
360470}
361471
472+ static inline int mpfs_clk_syscon_probe (struct mpfs_clock_data * clk_data ,
473+ struct platform_device * pdev )
474+ {
475+ clk_data -> regmap = syscon_regmap_lookup_by_compatible ("microchip,mpfs-mss-top-sysreg" );
476+ if (IS_ERR (clk_data -> regmap ))
477+ return PTR_ERR (clk_data -> regmap );
478+
479+ clk_data -> msspll_base = devm_platform_ioremap_resource (pdev , 0 );
480+ if (IS_ERR (clk_data -> msspll_base ))
481+ return PTR_ERR (clk_data -> msspll_base );
482+
483+ return 0 ;
484+ }
485+
486+ static inline int mpfs_clk_old_format_probe (struct mpfs_clock_data * clk_data ,
487+ struct platform_device * pdev )
488+ {
489+ struct device * dev = & pdev -> dev ;
490+
491+ dev_warn (& pdev -> dev , "falling back to old devicetree format" );
492+
493+ clk_data -> base = devm_platform_ioremap_resource (pdev , 0 );
494+ if (IS_ERR (clk_data -> base ))
495+ return PTR_ERR (clk_data -> base );
496+
497+ clk_data -> msspll_base = devm_platform_ioremap_resource (pdev , 1 );
498+ if (IS_ERR (clk_data -> msspll_base ))
499+ return PTR_ERR (clk_data -> msspll_base );
500+
501+ clk_data -> regmap = devm_regmap_init_mmio (dev , clk_data -> base , & mpfs_clk_regmap_config );
502+ if (IS_ERR (clk_data -> regmap ))
503+ return PTR_ERR (clk_data -> regmap );
504+
505+ return mpfs_reset_controller_register (dev , clk_data -> base + REG_SUBBLK_RESET_CR );
506+ }
507+
362508static int mpfs_clk_probe (struct platform_device * pdev )
363509{
364510 struct device * dev = & pdev -> dev ;
@@ -374,13 +520,12 @@ static int mpfs_clk_probe(struct platform_device *pdev)
374520 if (!clk_data )
375521 return - ENOMEM ;
376522
377- clk_data -> base = devm_platform_ioremap_resource (pdev , 0 );
378- if (IS_ERR (clk_data -> base ))
379- return PTR_ERR (clk_data -> base );
380-
381- clk_data -> msspll_base = devm_platform_ioremap_resource (pdev , 1 );
382- if (IS_ERR (clk_data -> msspll_base ))
383- return PTR_ERR (clk_data -> msspll_base );
523+ ret = mpfs_clk_syscon_probe (clk_data , pdev );
524+ if (ret ) {
525+ ret = mpfs_clk_old_format_probe (clk_data , pdev );
526+ if (ret )
527+ return ret ;
528+ }
384529
385530 clk_data -> hw_data .num = num_clks ;
386531 clk_data -> dev = dev ;
@@ -406,11 +551,7 @@ static int mpfs_clk_probe(struct platform_device *pdev)
406551 if (ret )
407552 return ret ;
408553
409- ret = devm_of_clk_add_hw_provider (dev , of_clk_hw_onecell_get , & clk_data -> hw_data );
410- if (ret )
411- return ret ;
412-
413- return mpfs_reset_controller_register (dev , clk_data -> base + REG_SUBBLK_RESET_CR );
554+ return devm_of_clk_add_hw_provider (dev , of_clk_hw_onecell_get , & clk_data -> hw_data );
414555}
415556
416557static const struct of_device_id mpfs_clk_of_match_table [] = {
0 commit comments