1 /* $OpenBSD: zqclock.c,v 1.1 2021/04/30 13:25:24 visa Exp $ */ 2 3 /* 4 * Copyright (c) 2021 Visa Hankala 5 * 6 * Permission to use, copy, modify, and/or distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* 20 * Driver for Xilinx Zynq-7000 clock controller. 21 */ 22 23 #include <sys/param.h> 24 #include <sys/systm.h> 25 #include <sys/device.h> 26 #include <sys/mutex.h> 27 28 #include <machine/bus.h> 29 #include <machine/fdt.h> 30 31 #include <dev/ofw/fdt.h> 32 #include <dev/ofw/openfirm.h> 33 #include <dev/ofw/ofw_clock.h> 34 #include <dev/ofw/ofw_misc.h> 35 36 #include <armv7/xilinx/slcreg.h> 37 38 #define CLK_ARM_PLL 0 39 #define CLK_DDR_PLL 1 40 #define CLK_IO_PLL 2 41 #define CLK_CPU_6OR4X 3 42 #define CLK_CPU_3OR2X 4 43 #define CLK_CPU_2X 5 44 #define CLK_CPU_1X 6 45 #define CLK_DDR_2X 7 46 #define CLK_DDR_3X 8 47 #define CLK_DCI 9 48 #define CLK_LQSPI 10 49 #define CLK_SMC 11 50 #define CLK_PCAP 12 51 #define CLK_GEM0 13 52 #define CLK_GEM1 14 53 #define CLK_FCLK0 15 54 #define CLK_FCLK1 16 55 #define CLK_FCLK2 17 56 #define CLK_FCLK3 18 57 #define CLK_CAN0 19 58 #define CLK_CAN1 20 59 #define CLK_SDIO0 21 60 #define CLK_SDIO1 22 61 #define CLK_UART0 23 62 #define CLK_UART1 24 63 #define CLK_SPI0 25 64 #define CLK_SPI1 26 65 #define CLK_DMA 27 66 67 struct zqclock_softc { 68 struct device sc_dev; 69 struct regmap *sc_rm; 70 71 struct clock_device sc_cd; 72 uint32_t sc_psclk_freq; /* in Hz */ 73 }; 74 75 int zqclock_match(struct device *, void *, void *); 76 void zqclock_attach(struct device *, struct device *, void *); 77 78 void zqclock_enable(void *, uint32_t *, int); 79 uint32_t zqclock_get_frequency(void *, uint32_t *); 80 int zqclock_set_frequency(void *, uint32_t *, uint32_t); 81 82 const struct cfattach zqclock_ca = { 83 sizeof(struct zqclock_softc), zqclock_match, zqclock_attach 84 }; 85 86 struct cfdriver zqclock_cd = { 87 NULL, "zqclock", DV_DULL 88 }; 89 90 struct zqclock_clock { 91 uint16_t clk_ctl_reg; 92 uint8_t clk_has_div1; 93 uint8_t clk_index; 94 }; 95 96 const struct zqclock_clock zqclock_clocks[] = { 97 [CLK_GEM0] = { SLCR_GEM0_CLK_CTRL, 1, 0 }, 98 [CLK_GEM1] = { SLCR_GEM1_CLK_CTRL, 1, 0 }, 99 [CLK_SDIO0] = { SLCR_SDIO_CLK_CTRL, 0, 0 }, 100 [CLK_SDIO1] = { SLCR_SDIO_CLK_CTRL, 0, 1 }, 101 [CLK_UART0] = { SLCR_UART_CLK_CTRL, 0, 0 }, 102 [CLK_UART1] = { SLCR_UART_CLK_CTRL, 0, 1 }, 103 }; 104 105 int 106 zqclock_match(struct device *parent, void *match, void *aux) 107 { 108 struct fdt_attach_args *faa = aux; 109 110 return OF_is_compatible(faa->fa_node, "xlnx,ps7-clkc"); 111 } 112 113 void 114 zqclock_attach(struct device *parent, struct device *self, void *aux) 115 { 116 struct fdt_attach_args *faa = aux; 117 struct zqclock_softc *sc = (struct zqclock_softc *)self; 118 119 sc->sc_rm = regmap_bynode(OF_parent(faa->fa_node)); 120 if (sc->sc_rm == NULL) { 121 printf(": can't get regmap\n"); 122 return; 123 } 124 125 sc->sc_psclk_freq = OF_getpropint(faa->fa_node, "ps-clk-frequency", 126 33333333); 127 128 printf(": %u MHz PS clock\n", (sc->sc_psclk_freq + 500000) / 1000000); 129 130 sc->sc_cd.cd_node = faa->fa_node; 131 sc->sc_cd.cd_cookie = sc; 132 sc->sc_cd.cd_enable = zqclock_enable; 133 sc->sc_cd.cd_get_frequency = zqclock_get_frequency; 134 sc->sc_cd.cd_set_frequency = zqclock_set_frequency; 135 clock_register(&sc->sc_cd); 136 } 137 138 const struct zqclock_clock * 139 zqclock_get_clock(uint32_t idx) 140 { 141 const struct zqclock_clock *clock; 142 143 if (idx >= nitems(zqclock_clocks)) 144 return NULL; 145 146 clock = &zqclock_clocks[idx]; 147 if (clock->clk_ctl_reg == 0) 148 return NULL; 149 150 return clock; 151 } 152 153 uint32_t 154 zqclock_get_pll_frequency(struct zqclock_softc *sc, uint32_t clk_ctrl) 155 { 156 uint32_t reg, val; 157 158 switch (clk_ctrl & SLCR_CLK_CTRL_SRCSEL_MASK) { 159 case SLCR_CLK_CTRL_SRCSEL_ARM: 160 reg = SLCR_ARM_PLL_CTRL; 161 break; 162 case SLCR_CLK_CTRL_SRCSEL_DDR: 163 reg = SLCR_DDR_PLL_CTRL; 164 break; 165 default: 166 reg = SLCR_IO_PLL_CTRL; 167 break; 168 } 169 170 val = zynq_slcr_read(sc->sc_rm, reg); 171 return sc->sc_psclk_freq * ((val >> SLCR_PLL_CTRL_FDIV_SHIFT) & 172 SLCR_PLL_CTRL_FDIV_MASK); 173 } 174 175 uint32_t 176 zqclock_get_frequency(void *cookie, uint32_t *cells) 177 { 178 const struct zqclock_clock *clock; 179 struct zqclock_softc *sc = cookie; 180 uint32_t idx = cells[0]; 181 uint32_t ctl, div, freq; 182 183 clock = zqclock_get_clock(idx); 184 if (clock == NULL) 185 return 0; 186 187 mtx_enter(&zynq_slcr_lock); 188 189 ctl = zynq_slcr_read(sc->sc_rm, clock->clk_ctl_reg); 190 191 div = SLCR_CLK_CTRL_DIVISOR(ctl); 192 if (clock->clk_has_div1) 193 div *= SLCR_CLK_CTRL_DIVISOR1(ctl); 194 195 freq = zqclock_get_pll_frequency(sc, ctl); 196 freq = (freq + div / 2) / div; 197 198 mtx_leave(&zynq_slcr_lock); 199 200 return freq; 201 } 202 203 int 204 zqclock_set_frequency(void *cookie, uint32_t *cells, uint32_t freq) 205 { 206 static const uint32_t srcsels[] = { 207 SLCR_CLK_CTRL_SRCSEL_IO, 208 SLCR_CLK_CTRL_SRCSEL_ARM, 209 SLCR_CLK_CTRL_SRCSEL_DDR, 210 }; 211 const struct zqclock_clock *clock; 212 struct zqclock_softc *sc = cookie; 213 uint32_t idx = cells[0]; 214 uint32_t best_delta = ~0U; 215 uint32_t best_div1 = 0; 216 uint32_t best_si = 0; 217 uint32_t best_pllf = 0; 218 uint32_t ctl, div, div1, maxdiv1, si; 219 int error = 0; 220 221 clock = zqclock_get_clock(idx); 222 if (clock == NULL) 223 return EINVAL; 224 225 if (freq == 0) 226 return EINVAL; 227 228 mtx_enter(&zynq_slcr_lock); 229 230 maxdiv1 = 1; 231 if (clock->clk_has_div1) 232 maxdiv1 = SLCR_DIV_MASK; 233 234 /* Find PLL and divisors that give best frequency. */ 235 for (si = 0; si < nitems(srcsels); si++) { 236 uint32_t delta, f, pllf; 237 238 pllf = zqclock_get_pll_frequency(sc, srcsels[si]); 239 if (freq > pllf) 240 continue; 241 242 for (div1 = 1; div1 <= maxdiv1; div1++) { 243 div = (pllf + (freq * div1 / 2)) / (freq * div1); 244 if (div > SLCR_DIV_MASK) 245 continue; 246 if (div == 0) 247 break; 248 249 f = (pllf + (div * div1 / 2)) / (div * div1); 250 delta = abs(f - freq); 251 if (best_div1 == 0 || delta < best_delta) { 252 best_delta = delta; 253 best_div1 = div1; 254 best_pllf = pllf; 255 best_si = si; 256 257 if (delta == 0) 258 goto found; 259 } 260 } 261 } 262 263 if (best_div1 == 0) { 264 error = EINVAL; 265 goto out; 266 } 267 268 found: 269 div1 = best_div1; 270 div = (best_pllf + (freq * div1 / 2)) / (freq * div1); 271 272 KASSERT(div > 0 && div <= SLCR_DIV_MASK); 273 KASSERT(div1 > 0 && div1 <= SLCR_DIV_MASK); 274 275 ctl = zynq_slcr_read(sc->sc_rm, clock->clk_ctl_reg); 276 277 ctl &= ~SLCR_CLK_CTRL_SRCSEL_MASK; 278 ctl |= srcsels[best_si]; 279 ctl &= ~(SLCR_DIV_MASK << SLCR_CLK_CTRL_DIVISOR_SHIFT); 280 ctl |= (div & SLCR_DIV_MASK) << SLCR_CLK_CTRL_DIVISOR_SHIFT; 281 if (clock->clk_has_div1) { 282 ctl &= ~(SLCR_DIV_MASK << SLCR_CLK_CTRL_DIVISOR1_SHIFT); 283 ctl |= (div1 & SLCR_DIV_MASK) << SLCR_CLK_CTRL_DIVISOR1_SHIFT; 284 } 285 286 zynq_slcr_write(sc->sc_rm, clock->clk_ctl_reg, ctl); 287 288 out: 289 mtx_leave(&zynq_slcr_lock); 290 291 return error; 292 } 293 294 void 295 zqclock_enable(void *cookie, uint32_t *cells, int on) 296 { 297 const struct zqclock_clock *clock; 298 struct zqclock_softc *sc = cookie; 299 uint32_t idx = cells[0]; 300 uint32_t ctl; 301 302 clock = zqclock_get_clock(idx); 303 if (clock == NULL) 304 return; 305 306 mtx_enter(&zynq_slcr_lock); 307 308 ctl = zynq_slcr_read(sc->sc_rm, clock->clk_ctl_reg); 309 if (on) 310 ctl |= SLCR_CLK_CTRL_CLKACT(clock->clk_index); 311 else 312 ctl &= ~SLCR_CLK_CTRL_CLKACT(clock->clk_index); 313 zynq_slcr_write(sc->sc_rm, clock->clk_ctl_reg, ctl); 314 315 mtx_leave(&zynq_slcr_lock); 316 } 317