77
88#include <dt-bindings/clock/thead,th1520-clk-ap.h>
99#include <linux/bitfield.h>
10+ #include <linux/clk.h>
1011#include <linux/clk-provider.h>
1112#include <linux/delay.h>
1213#include <linux/device.h>
14+ #include <linux/minmax.h>
1315#include <linux/module.h>
1416#include <linux/platform_device.h>
1517#include <linux/regmap.h>
3436#define TH1520_PLL_LOCK_TIMEOUT_US 44
3537#define TH1520_PLL_STABLE_DELAY_US 30
3638
39+ /* c910_bus_clk must be kept below 750MHz for stability */
40+ #define TH1520_C910_BUS_MAX_RATE (750 * 1000 * 1000)
41+
3742struct ccu_internal {
3843 u8 shift ;
3944 u8 width ;
@@ -472,6 +477,72 @@ static const struct clk_ops clk_pll_ops = {
472477 .set_rate = ccu_pll_set_rate ,
473478};
474479
480+ /*
481+ * c910_clk could be reparented glitchlessly for DVFS. There are two parents,
482+ * - c910_i0_clk, derived from cpu_pll0_clk or osc_24m.
483+ * - cpu_pll1_clk, which provides the exact same set of rates as cpu_pll0_clk.
484+ *
485+ * During rate setting, always forward the request to the unused parent, and
486+ * then switch c910_clk to it to avoid glitch.
487+ */
488+ static u8 c910_clk_get_parent (struct clk_hw * hw )
489+ {
490+ return clk_mux_ops .get_parent (hw );
491+ }
492+
493+ static int c910_clk_set_parent (struct clk_hw * hw , u8 index )
494+ {
495+ return clk_mux_ops .set_parent (hw , index );
496+ }
497+
498+ static unsigned long c910_clk_recalc_rate (struct clk_hw * hw ,
499+ unsigned long parent_rate )
500+ {
501+ return parent_rate ;
502+ }
503+
504+ static int c910_clk_determine_rate (struct clk_hw * hw ,
505+ struct clk_rate_request * req )
506+ {
507+ u8 alt_parent_index = !c910_clk_get_parent (hw );
508+ struct clk_hw * alt_parent ;
509+
510+ alt_parent = clk_hw_get_parent_by_index (hw , alt_parent_index );
511+
512+ req -> rate = clk_hw_round_rate (alt_parent , req -> rate );
513+ req -> best_parent_hw = alt_parent ;
514+ req -> best_parent_rate = req -> rate ;
515+
516+ return 0 ;
517+ }
518+
519+ static int c910_clk_set_rate (struct clk_hw * hw , unsigned long rate ,
520+ unsigned long parent_rate )
521+ {
522+ return - EOPNOTSUPP ;
523+ }
524+
525+ static int c910_clk_set_rate_and_parent (struct clk_hw * hw , unsigned long rate ,
526+ unsigned long parent_rate , u8 index )
527+ {
528+ struct clk_hw * parent = clk_hw_get_parent_by_index (hw , index );
529+
530+ clk_set_rate (parent -> clk , parent_rate );
531+
532+ c910_clk_set_parent (hw , index );
533+
534+ return 0 ;
535+ }
536+
537+ static const struct clk_ops c910_clk_ops = {
538+ .get_parent = c910_clk_get_parent ,
539+ .set_parent = c910_clk_set_parent ,
540+ .recalc_rate = c910_clk_recalc_rate ,
541+ .determine_rate = c910_clk_determine_rate ,
542+ .set_rate = c910_clk_set_rate ,
543+ .set_rate_and_parent = c910_clk_set_rate_and_parent ,
544+ };
545+
475546static const struct clk_parent_data osc_24m_clk [] = {
476547 { .index = 0 }
477548};
@@ -672,7 +743,8 @@ static const struct clk_parent_data c910_i0_parents[] = {
672743static struct ccu_mux c910_i0_clk = {
673744 .clkid = CLK_C910_I0 ,
674745 .reg = 0x100 ,
675- .mux = TH_CCU_MUX ("c910-i0" , c910_i0_parents , 1 , 1 ),
746+ .mux = TH_CCU_MUX_FLAGS ("c910-i0" , c910_i0_parents , 1 , 1 ,
747+ CLK_SET_RATE_PARENT , CLK_MUX_ROUND_CLOSEST ),
676748};
677749
678750static const struct clk_parent_data c910_parents [] = {
@@ -683,7 +755,14 @@ static const struct clk_parent_data c910_parents[] = {
683755static struct ccu_mux c910_clk = {
684756 .clkid = CLK_C910 ,
685757 .reg = 0x100 ,
686- .mux = TH_CCU_MUX ("c910" , c910_parents , 0 , 1 ),
758+ .mux = {
759+ .mask = BIT (0 ),
760+ .shift = 0 ,
761+ .hw .init = CLK_HW_INIT_PARENTS_DATA ("c910" ,
762+ c910_parents ,
763+ & c910_clk_ops ,
764+ CLK_SET_RATE_PARENT ),
765+ },
687766};
688767
689768static struct ccu_div c910_bus_clk = {
@@ -1372,11 +1451,69 @@ static const struct th1520_plat_data th1520_vo_platdata = {
13721451 .nr_gate_clks = ARRAY_SIZE (th1520_vo_gate_clks ),
13731452};
13741453
1454+ /*
1455+ * Maintain clock rate of c910_bus_clk below TH1520_C910_BUS_MAX_RATE (750MHz)
1456+ * when its parent, c910_clk, changes the rate.
1457+ *
1458+ * Additionally, TRM is unclear about c910_bus_clk behavior when the divisor is
1459+ * set below 2, thus we should ensure the new divisor stays in (2, MAXDIVISOR).
1460+ */
1461+ static unsigned long c910_bus_clk_divisor (struct ccu_div * cd ,
1462+ unsigned long parent_rate )
1463+ {
1464+ return clamp (DIV_ROUND_UP (parent_rate , TH1520_C910_BUS_MAX_RATE ),
1465+ 2U , 1U << cd -> div .width );
1466+ }
1467+
1468+ static int c910_clk_notifier_cb (struct notifier_block * nb ,
1469+ unsigned long action , void * data )
1470+ {
1471+ struct clk_notifier_data * cnd = data ;
1472+ unsigned long new_divisor , ref_rate ;
1473+
1474+ if (action != PRE_RATE_CHANGE && action != POST_RATE_CHANGE )
1475+ return NOTIFY_DONE ;
1476+
1477+ new_divisor = c910_bus_clk_divisor (& c910_bus_clk , cnd -> new_rate );
1478+
1479+ if (cnd -> new_rate > cnd -> old_rate ) {
1480+ /*
1481+ * Scaling up. Adjust c910_bus_clk divisor
1482+ * - before c910_clk rate change to ensure the constraints
1483+ * aren't broken after scaling to higher rates,
1484+ * - after c910_clk rate change to keep c910_bus_clk as high as
1485+ * possible
1486+ */
1487+ ref_rate = action == PRE_RATE_CHANGE ?
1488+ cnd -> old_rate : cnd -> new_rate ;
1489+ clk_set_rate (c910_bus_clk .common .hw .clk ,
1490+ ref_rate / new_divisor );
1491+ } else if (cnd -> new_rate < cnd -> old_rate &&
1492+ action == POST_RATE_CHANGE ) {
1493+ /*
1494+ * Scaling down. Adjust c910_bus_clk divisor only after
1495+ * c910_clk rate change to keep c910_bus_clk as high as
1496+ * possible, Scaling down never breaks the constraints.
1497+ */
1498+ clk_set_rate (c910_bus_clk .common .hw .clk ,
1499+ cnd -> new_rate / new_divisor );
1500+ } else {
1501+ return NOTIFY_DONE ;
1502+ }
1503+
1504+ return NOTIFY_OK ;
1505+ }
1506+
1507+ static struct notifier_block c910_clk_notifier = {
1508+ .notifier_call = c910_clk_notifier_cb ,
1509+ };
1510+
13751511static int th1520_clk_probe (struct platform_device * pdev )
13761512{
13771513 const struct th1520_plat_data * plat_data ;
13781514 struct device * dev = & pdev -> dev ;
13791515 struct clk_hw_onecell_data * priv ;
1516+ struct clk * notifier_clk ;
13801517
13811518 struct regmap * map ;
13821519 void __iomem * base ;
@@ -1463,6 +1600,13 @@ static int th1520_clk_probe(struct platform_device *pdev)
14631600 ret = devm_clk_hw_register (dev , & emmc_sdio_ref_clk .hw );
14641601 if (ret )
14651602 return ret ;
1603+
1604+ notifier_clk = devm_clk_hw_get_clk (dev , & c910_clk .mux .hw ,
1605+ "dvfs" );
1606+ ret = devm_clk_notifier_register (dev , notifier_clk ,
1607+ & c910_clk_notifier );
1608+ if (ret )
1609+ return ret ;
14661610 }
14671611
14681612 ret = devm_of_clk_add_hw_provider (dev , of_clk_hw_onecell_get , priv );
0 commit comments