1 /* 2 * (C) Copyright 2016 Rockchip Electronics Co., Ltd 3 * Author: Andy Yan <andy.yan@rock-chips.com> 4 * SPDX-License-Identifier: GPL-2.0 5 */ 6 7 #include <common.h> 8 #include <clk-uclass.h> 9 #include <dm.h> 10 #include <errno.h> 11 #include <syscon.h> 12 #include <asm/io.h> 13 #include <asm/arch/clock.h> 14 #include <asm/arch/cru_rv1108.h> 15 #include <asm/arch/hardware.h> 16 #include <dm/lists.h> 17 #include <dt-bindings/clock/rv1108-cru.h> 18 19 DECLARE_GLOBAL_DATA_PTR; 20 21 enum { 22 VCO_MAX_HZ = 2400U * 1000000, 23 VCO_MIN_HZ = 600 * 1000000, 24 OUTPUT_MAX_HZ = 2400U * 1000000, 25 OUTPUT_MIN_HZ = 24 * 1000000, 26 }; 27 28 #define RATE_TO_DIV(input_rate, output_rate) \ 29 ((input_rate) / (output_rate) - 1); 30 31 #define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1)) 32 33 #define PLL_DIVISORS(hz, _refdiv, _postdiv1, _postdiv2) {\ 34 .refdiv = _refdiv,\ 35 .fbdiv = (u32)((u64)hz * _refdiv * _postdiv1 * _postdiv2 / OSC_HZ),\ 36 .postdiv1 = _postdiv1, .postdiv2 = _postdiv2};\ 37 _Static_assert(((u64)hz * _refdiv * _postdiv1 * _postdiv2 / OSC_HZ) *\ 38 OSC_HZ / (_refdiv * _postdiv1 * _postdiv2) == hz,\ 39 #hz "Hz cannot be hit with PLL "\ 40 "divisors on line " __stringify(__LINE__)); 41 42 /* use interge mode*/ 43 static inline int rv1108_pll_id(enum rk_clk_id clk_id) 44 { 45 int id = 0; 46 47 switch (clk_id) { 48 case CLK_ARM: 49 case CLK_DDR: 50 id = clk_id - 1; 51 break; 52 case CLK_GENERAL: 53 id = 2; 54 break; 55 default: 56 printf("invalid pll id:%d\n", clk_id); 57 id = -1; 58 break; 59 } 60 61 return id; 62 } 63 64 static uint32_t rkclk_pll_get_rate(struct rv1108_cru *cru, 65 enum rk_clk_id clk_id) 66 { 67 uint32_t refdiv, fbdiv, postdiv1, postdiv2; 68 uint32_t con0, con1, con3; 69 int pll_id = rv1108_pll_id(clk_id); 70 struct rv1108_pll *pll = &cru->pll[pll_id]; 71 uint32_t freq; 72 73 con3 = readl(&pll->con3); 74 75 if (con3 & WORK_MODE_MASK) { 76 con0 = readl(&pll->con0); 77 con1 = readl(&pll->con1); 78 fbdiv = (con0 >> FBDIV_SHIFT) & FBDIV_MASK; 79 postdiv1 = (con1 & POSTDIV1_MASK) >> POSTDIV1_SHIFT; 80 postdiv2 = (con1 & POSTDIV2_MASK) >> POSTDIV2_SHIFT; 81 refdiv = (con1 & REFDIV_MASK) >> REFDIV_SHIFT; 82 freq = (24 * fbdiv / (refdiv * postdiv1 * postdiv2)) * 1000000; 83 } else { 84 freq = OSC_HZ; 85 } 86 87 return freq; 88 } 89 90 static int rv1108_mac_set_clk(struct rv1108_cru *cru, ulong rate) 91 { 92 uint32_t con = readl(&cru->clksel_con[24]); 93 ulong pll_rate; 94 uint8_t div; 95 96 if ((con >> MAC_PLL_SEL_SHIFT) & MAC_PLL_SEL_GPLL) 97 pll_rate = rkclk_pll_get_rate(cru, CLK_GENERAL); 98 else 99 pll_rate = rkclk_pll_get_rate(cru, CLK_ARM); 100 101 /*default set 50MHZ for gmac*/ 102 if (!rate) 103 rate = 50000000; 104 105 div = DIV_ROUND_UP(pll_rate, rate) - 1; 106 if (div <= 0x1f) 107 rk_clrsetreg(&cru->clksel_con[24], MAC_CLK_DIV_MASK, 108 div << MAC_CLK_DIV_SHIFT); 109 else 110 debug("Unsupported div for gmac:%d\n", div); 111 112 return DIV_TO_RATE(pll_rate, div); 113 } 114 115 static int rv1108_sfc_set_clk(struct rv1108_cru *cru, uint rate) 116 { 117 u32 con = readl(&cru->clksel_con[27]); 118 u32 pll_rate; 119 u32 div; 120 121 if ((con >> SFC_PLL_SEL_SHIFT) && SFC_PLL_SEL_GPLL) 122 pll_rate = rkclk_pll_get_rate(cru, CLK_GENERAL); 123 else 124 pll_rate = rkclk_pll_get_rate(cru, CLK_DDR); 125 126 div = DIV_ROUND_UP(pll_rate, rate) - 1; 127 if (div <= 0x3f) 128 rk_clrsetreg(&cru->clksel_con[27], SFC_CLK_DIV_MASK, 129 div << SFC_CLK_DIV_SHIFT); 130 else 131 debug("Unsupported sfc clk rate:%d\n", rate); 132 133 return DIV_TO_RATE(pll_rate, div); 134 } 135 136 static ulong rv1108_clk_get_rate(struct clk *clk) 137 { 138 struct rv1108_clk_priv *priv = dev_get_priv(clk->dev); 139 140 switch (clk->id) { 141 case 0 ... 63: 142 return rkclk_pll_get_rate(priv->cru, clk->id); 143 default: 144 return -ENOENT; 145 } 146 } 147 148 static ulong rv1108_clk_set_rate(struct clk *clk, ulong rate) 149 { 150 struct rv1108_clk_priv *priv = dev_get_priv(clk->dev); 151 ulong new_rate; 152 153 switch (clk->id) { 154 case SCLK_MAC: 155 new_rate = rv1108_mac_set_clk(priv->cru, rate); 156 break; 157 case SCLK_SFC: 158 new_rate = rv1108_sfc_set_clk(priv->cru, rate); 159 break; 160 default: 161 return -ENOENT; 162 } 163 164 return new_rate; 165 } 166 167 static const struct clk_ops rv1108_clk_ops = { 168 .get_rate = rv1108_clk_get_rate, 169 .set_rate = rv1108_clk_set_rate, 170 }; 171 172 static void rkclk_init(struct rv1108_cru *cru) 173 { 174 unsigned int apll = rkclk_pll_get_rate(cru, CLK_ARM); 175 unsigned int dpll = rkclk_pll_get_rate(cru, CLK_DDR); 176 unsigned int gpll = rkclk_pll_get_rate(cru, CLK_GENERAL); 177 178 rk_clrsetreg(&cru->clksel_con[0], CORE_CLK_DIV_MASK, 179 0 << MAC_CLK_DIV_SHIFT); 180 181 printf("APLL: %d DPLL:%d GPLL:%d\n", apll, dpll, gpll); 182 } 183 184 static int rv1108_clk_probe(struct udevice *dev) 185 { 186 struct rv1108_clk_priv *priv = dev_get_priv(dev); 187 188 priv->cru = (struct rv1108_cru *)devfdt_get_addr(dev); 189 190 rkclk_init(priv->cru); 191 192 return 0; 193 } 194 195 static int rv1108_clk_bind(struct udevice *dev) 196 { 197 int ret; 198 199 /* The reset driver does not have a device node, so bind it here */ 200 ret = device_bind_driver(gd->dm_root, "rv1108_sysreset", "reset", &dev); 201 if (ret) 202 error("No Rv1108 reset driver: ret=%d\n", ret); 203 204 return 0; 205 } 206 207 static const struct udevice_id rv1108_clk_ids[] = { 208 { .compatible = "rockchip,rv1108-cru" }, 209 { } 210 }; 211 212 U_BOOT_DRIVER(clk_rv1108) = { 213 .name = "clk_rv1108", 214 .id = UCLASS_CLK, 215 .of_match = rv1108_clk_ids, 216 .priv_auto_alloc_size = sizeof(struct rv1108_clk_priv), 217 .ops = &rv1108_clk_ops, 218 .bind = rv1108_clk_bind, 219 .probe = rv1108_clk_probe, 220 }; 221