1 /* $NetBSD: rk_cru_pll.c,v 1.3 2018/06/30 17:54:07 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2018 Jared McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: rk_cru_pll.c,v 1.3 2018/06/30 17:54:07 jmcneill Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/bus.h> 34 35 #include <dev/clk/clk_backend.h> 36 37 #include <arm/rockchip/rk_cru.h> 38 39 #define PLL_CON0 0x00 40 #define PLL_BYPASS __BIT(15) 41 #define PLL_POSTDIV1 __BITS(14,12) 42 #define PLL_FBDIV __BITS(11,0) 43 44 #define PLL_CON1 0x04 45 #define PLL_PDSEL __BIT(15) 46 #define PLL_PD1 __BIT(14) 47 #define PLL_PD0 __BIT(13) 48 #define PLL_DSMPD __BIT(12) 49 #define PLL_LOCK __BIT(10) 50 #define PLL_POSTDIV2 __BITS(8,6) 51 #define PLL_REFDIV __BITS(5,0) 52 53 #define PLL_CON2 0x08 54 #define PLL_FOUT4PHASEPD __BIT(27) 55 #define PLL_FOUTVCOPD __BIT(26) 56 #define PLL_FOUTPOSTDIVPD __BIT(25) 57 #define PLL_DACPD __BIT(24) 58 #define PLL_FRACDIV __BITS(23,0) 59 60 #define PLL_WRITE_MASK 0xffff0000 /* for CON0 and CON1 */ 61 62 #define GRF_SOC_STATUS0 0x0480 63 64 u_int 65 rk_cru_pll_get_rate(struct rk_cru_softc *sc, 66 struct rk_cru_clk *clk) 67 { 68 struct rk_cru_pll *pll = &clk->u.pll; 69 struct clk *clkp, *clkp_parent; 70 u_int foutvco, foutpostdiv; 71 72 KASSERT(clk->type == RK_CRU_PLL); 73 74 clkp = &clk->base; 75 clkp_parent = clk_get_parent(clkp); 76 if (clkp_parent == NULL) 77 return 0; 78 79 const u_int fref = clk_get_rate(clkp_parent); 80 if (fref == 0) 81 return 0; 82 83 const uint32_t con0 = CRU_READ(sc, pll->con_base + PLL_CON0); 84 const uint32_t con1 = CRU_READ(sc, pll->con_base + PLL_CON1); 85 const uint32_t con2 = CRU_READ(sc, pll->con_base + PLL_CON2); 86 87 const u_int postdiv1 = __SHIFTOUT(con0, PLL_POSTDIV1); 88 const u_int fbdiv = __SHIFTOUT(con0, PLL_FBDIV); 89 const u_int dsmpd = __SHIFTOUT(con1, PLL_DSMPD); 90 const u_int refdiv = __SHIFTOUT(con1, PLL_REFDIV); 91 const u_int postdiv2 = __SHIFTOUT(con1, PLL_POSTDIV2); 92 const u_int fracdiv = __SHIFTOUT(con2, PLL_FRACDIV); 93 94 if (dsmpd == 1) { 95 /* integer mode */ 96 foutvco = fref / refdiv * fbdiv; 97 } else { 98 /* fractional mode */ 99 foutvco = fref / refdiv * fbdiv + ((fref * fracdiv) >> 24); 100 } 101 foutpostdiv = foutvco / postdiv1 / postdiv2; 102 103 return foutpostdiv; 104 } 105 106 int 107 rk_cru_pll_set_rate(struct rk_cru_softc *sc, 108 struct rk_cru_clk *clk, u_int rate) 109 { 110 struct rk_cru_pll *pll = &clk->u.pll; 111 const struct rk_cru_pll_rate *pll_rate = NULL; 112 uint32_t val; 113 int retry; 114 115 KASSERT(clk->type == RK_CRU_PLL); 116 117 if (pll->rates == NULL || rate == 0 || !HAS_GRF(sc)) 118 return EIO; 119 120 for (int i = 0; i < pll->nrates; i++) 121 if (pll->rates[i].rate == rate) { 122 pll_rate = &pll->rates[i]; 123 break; 124 } 125 if (pll_rate == NULL) 126 return EINVAL; 127 128 CRU_WRITE(sc, pll->con_base + PLL_CON0, 129 __SHIFTIN(pll_rate->postdiv1, PLL_POSTDIV1) | 130 __SHIFTIN(pll_rate->fbdiv, PLL_FBDIV) | 131 PLL_WRITE_MASK); 132 133 CRU_WRITE(sc, pll->con_base + PLL_CON1, 134 __SHIFTIN(pll_rate->dsmpd, PLL_DSMPD) | 135 __SHIFTIN(pll_rate->postdiv2, PLL_POSTDIV2) | 136 __SHIFTIN(pll_rate->refdiv, PLL_REFDIV) | 137 PLL_WRITE_MASK); 138 139 val = CRU_READ(sc, pll->con_base + PLL_CON2); 140 val &= ~PLL_FRACDIV; 141 val |= __SHIFTIN(pll_rate->fracdiv, PLL_FRACDIV); 142 CRU_WRITE(sc, pll->con_base + PLL_CON2, val); 143 144 /* Set PLL work mode to normal */ 145 const uint32_t write_mask = pll->mode_mask << 16; 146 const uint32_t write_val = pll->mode_mask; 147 CRU_WRITE(sc, pll->mode_reg, write_mask | write_val); 148 149 syscon_lock(sc->sc_grf); 150 for (retry = 1000; retry > 0; retry--) { 151 if (syscon_read_4(sc->sc_grf, GRF_SOC_STATUS0) & pll->lock_mask) 152 break; 153 delay(1); 154 } 155 syscon_unlock(sc->sc_grf); 156 157 if (retry == 0) 158 device_printf(sc->sc_dev, "WARNING: %s failed to lock\n", 159 clk->base.name); 160 161 return 0; 162 } 163 164 const char * 165 rk_cru_pll_get_parent(struct rk_cru_softc *sc, 166 struct rk_cru_clk *clk) 167 { 168 struct rk_cru_pll *pll = &clk->u.pll; 169 170 KASSERT(clk->type == RK_CRU_PLL); 171 172 return pll->parent; 173 } 174