1/* $NetBSD: zynq7000_clkc.c,v 1.5 2022/11/11 20:29:47 jmcneill Exp $ */ 2 3/*- 4 * Copyright (c) 2022 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30 31__KERNEL_RCSID(0, "$NetBSD: zynq7000_clkc.c,v 1.5 2022/11/11 20:29:47 jmcneill Exp $"); 32 33#include <sys/param.h> 34#include <sys/bus.h> 35#include <sys/device.h> 36#include <sys/intr.h> 37#include <sys/systm.h> 38#include <sys/time.h> 39#include <sys/kmem.h> 40 41#include <dev/clk/clk_backend.h> 42 43#include <dev/fdt/fdtvar.h> 44#include <dev/fdt/syscon.h> 45 46#define ARM_PLL_CTRL 0x100 47#define DDR_PLL_CTRL 0x104 48#define IO_PLL_CTRL 0x108 49#define PLL_FDIV __BITS(18,12) 50#define ARM_CLK_CTRL 0x120 51#define ARM_CLK_CTRL_CPU_1XCLKACT __BIT(27) 52#define ARM_CLK_CTRL_CPU_2XCLKACT __BIT(26) 53#define ARM_CLK_CTRL_CPU_3OR2XCLKACT __BIT(25) 54#define ARM_CLK_CTRL_CPU_6OR4XCLKACT __BIT(24) 55#define ARM_CLK_CTRL_DIVISOR __BITS(13,8) 56#define APER_CLK_CTRL 0x12c 57#define UART1_CPU_1XCLKACT __BIT(21) 58#define UART0_CPU_1XCLKACT __BIT(20) 59#define I2C1_CPU_1XCLKACT __BIT(19) 60#define I2C0_CPU_1XCLKACT __BIT(18) 61#define SDI1_CPU_1XCLKACT __BIT(11) 62#define SDI0_CPU_1XCLKACT __BIT(10) 63#define SDIO_CLK_CTRL 0x150 64#define UART_CLK_CTRL 0x154 65#define PCAP_CLK_CTRL 0x168 66#define CLK_CTRL_DIVISOR __BITS(13,8) 67#define CLK_CTRL_SRCSEL __BITS(5,4) 68#define CLK_CTRL_CLKACT1 __BIT(1) 69#define CLK_CTRL_CLKACT0 __BIT(0) 70#define CLK_621_TRUE 0x1C4 71#define CLK_621_TRUE_EN __BIT(0) 72 73enum xynq7000_clkid { 74 clkid_armpll, 75 clkid_ddrpll, 76 clkid_iopll, 77 clkid_cpu_6or4x, 78 clkid_cpu_3or2x, 79 clkid_cpu_2x, 80 clkid_cpu_1x, 81 clkid_ddr2x, 82 clkid_ddr3x, 83 clkid_dci, 84 clkid_lqspi, 85 clkid_smc, 86 clkid_pcap, 87 clkid_gem0, 88 clkid_gem1, 89 clkid_fclk0, 90 clkid_fclk1, 91 clkid_fclk2, 92 clkid_fclk3, 93 clkid_can0, 94 clkid_can1, 95 clkid_sdio0, 96 clkid_sdio1, 97 clkid_uart0, 98 clkid_uart1, 99 clkid_spi0, 100 clkid_spi1, 101 clkid_dma, 102 clkid_usb0_aper, 103 clkid_usb1_aper, 104 clkid_gem0_aper, 105 clkid_gem1_aper, 106 clkid_sdio0_aper, 107 clkid_sdio1_aper, 108 clkid_spi0_aper, 109 clkid_spi1_aper, 110 clkid_can0_aper, 111 clkid_can1_aper, 112 clkid_i2c0_aper, 113 clkid_i2c1_aper, 114 clkid_uart0_aper, 115 clkid_uart1_aper, 116 clkid_gpio_aper, 117 clkid_lqspi_aper, 118 clkid_smc_aper, 119 clkid_swdt, 120 clkid_dbg_trc, 121 clkid_dbg_apb, 122 num_clkid 123}; 124CTASSERT(clkid_dbg_apb == 47); 125 126static int zynq7000_clkc_match(device_t, cfdata_t, void *); 127static void zynq7000_clkc_attach(device_t, device_t, void *); 128 129static u_int zynq7000_clkc_clk_get_rate(void *, struct clk *); 130 131static const struct device_compatible_entry compat_data[] = { 132 { .compat = "xlnx,ps7-clkc" }, 133 DEVICE_COMPAT_EOL 134}; 135 136struct zynq7000_clkc_softc { 137 device_t sc_dev; 138 struct clk_domain sc_clkdom; 139 struct clk sc_clk[num_clkid]; 140 141 u_int sc_ps_clk_frequency; 142 struct syscon *sc_syscon; 143}; 144 145CFATTACH_DECL_NEW(zynq7000_clkc, sizeof(struct zynq7000_clkc_softc), 146 zynq7000_clkc_match, zynq7000_clkc_attach, NULL, NULL); 147 148static struct clk * 149zynq7000_clkc_clk_get(void *priv, const char *name) 150{ 151 struct zynq7000_clkc_softc * const sc = priv; 152 u_int clkid; 153 154 for (clkid = 0; clkid < num_clkid; clkid++) { 155 if (strcmp(name, sc->sc_clk[clkid].name) == 0) { 156 return &sc->sc_clk[clkid]; 157 } 158 } 159 160 return NULL; 161} 162 163static void 164zynq7000_clkc_clk_put(void *priv, struct clk *clk) 165{ 166} 167 168static u_int 169zynq7000_clkc_get_rate_pll(struct zynq7000_clkc_softc *sc, 170 bus_addr_t reg) 171{ 172 uint32_t val; 173 174 syscon_lock(sc->sc_syscon); 175 val = syscon_read_4(sc->sc_syscon, reg); 176 syscon_unlock(sc->sc_syscon); 177 178 return sc->sc_ps_clk_frequency * __SHIFTOUT(val, PLL_FDIV); 179} 180 181static u_int 182zynq7000_clkc_get_rate_iop(struct zynq7000_clkc_softc *sc, 183 bus_addr_t reg) 184{ 185 uint32_t val; 186 u_int prate, sel; 187 188 syscon_lock(sc->sc_syscon); 189 val = syscon_read_4(sc->sc_syscon, reg); 190 syscon_unlock(sc->sc_syscon); 191 192 sel = __SHIFTOUT(val, CLK_CTRL_SRCSEL); 193 if (sel == 2) { 194 prate = zynq7000_clkc_clk_get_rate(sc, 195 &sc->sc_clk[clkid_armpll]); 196 } else if (sel == 3) { 197 prate = zynq7000_clkc_clk_get_rate(sc, 198 &sc->sc_clk[clkid_ddrpll]); 199 } else { 200 prate = zynq7000_clkc_clk_get_rate(sc, 201 &sc->sc_clk[clkid_iopll]); 202 } 203 204 return prate / __SHIFTOUT(val, CLK_CTRL_DIVISOR); 205} 206 207static u_int 208zynq7000_clkc_clk_get_rate(void *priv, struct clk *clk) 209{ 210 struct zynq7000_clkc_softc * const sc = priv; 211 uint32_t val; 212 u_int prate; 213 214 if (clk == &sc->sc_clk[clkid_armpll]) { 215 return zynq7000_clkc_get_rate_pll(sc, ARM_PLL_CTRL); 216 } else if (clk == &sc->sc_clk[clkid_iopll]) { 217 return zynq7000_clkc_get_rate_pll(sc, IO_PLL_CTRL); 218 } else if (clk == &sc->sc_clk[clkid_cpu_6or4x]) { 219 prate = zynq7000_clkc_clk_get_rate(sc, 220 &sc->sc_clk[clkid_cpu_1x]); 221 syscon_lock(sc->sc_syscon); 222 val = syscon_read_4(sc->sc_syscon, CLK_621_TRUE); 223 syscon_unlock(sc->sc_syscon); 224 return (val & CLK_621_TRUE_EN) != 0 ? 225 prate * 6 : prate * 4; 226 } else if (clk == &sc->sc_clk[clkid_cpu_3or2x]) { 227 prate = zynq7000_clkc_clk_get_rate(sc, 228 &sc->sc_clk[clkid_cpu_1x]); 229 syscon_lock(sc->sc_syscon); 230 val = syscon_read_4(sc->sc_syscon, CLK_621_TRUE); 231 syscon_unlock(sc->sc_syscon); 232 return (val & CLK_621_TRUE_EN) != 0 ? 233 prate * 3 : prate * 2; 234 } else if (clk == &sc->sc_clk[clkid_cpu_2x]) { 235 prate = zynq7000_clkc_clk_get_rate(sc, 236 &sc->sc_clk[clkid_cpu_1x]); 237 return prate * 2; 238 } else if (clk == &sc->sc_clk[clkid_cpu_1x]) { 239 prate = zynq7000_clkc_clk_get_rate(sc, 240 &sc->sc_clk[clkid_armpll]); 241 syscon_lock(sc->sc_syscon); 242 val = syscon_read_4(sc->sc_syscon, ARM_CLK_CTRL); 243 syscon_unlock(sc->sc_syscon); 244 return prate / __SHIFTOUT(val, ARM_CLK_CTRL_DIVISOR) / 6; 245 } else if (clk == &sc->sc_clk[clkid_sdio0] || 246 clk == &sc->sc_clk[clkid_sdio1]) { 247 return zynq7000_clkc_get_rate_iop(sc, SDIO_CLK_CTRL); 248 } else if (clk == &sc->sc_clk[clkid_uart0] || 249 clk == &sc->sc_clk[clkid_uart1]) { 250 return zynq7000_clkc_get_rate_iop(sc, UART_CLK_CTRL); 251 } else if (clk == &sc->sc_clk[clkid_pcap]) { 252 return zynq7000_clkc_get_rate_iop(sc, PCAP_CLK_CTRL); 253 } else if (clk == &sc->sc_clk[clkid_uart0_aper] || 254 clk == &sc->sc_clk[clkid_uart1_aper] || 255 clk == &sc->sc_clk[clkid_i2c0_aper] || 256 clk == &sc->sc_clk[clkid_i2c1_aper]) { 257 return zynq7000_clkc_clk_get_rate(sc, 258 &sc->sc_clk[clkid_cpu_1x]); 259 } else { 260 /* Not implemented. */ 261 return 0; 262 } 263} 264 265static int 266zynq7000_clkc_clk_enable(void *priv, struct clk *clk) 267{ 268 struct zynq7000_clkc_softc * const sc = priv; 269 uint32_t val, mask; 270 bus_addr_t reg; 271 272 if (clk == &sc->sc_clk[clkid_cpu_1x]) { 273 reg = ARM_CLK_CTRL; 274 mask = ARM_CLK_CTRL_CPU_1XCLKACT; 275 } else if (clk == &sc->sc_clk[clkid_cpu_2x]) { 276 reg = ARM_CLK_CTRL; 277 mask = ARM_CLK_CTRL_CPU_2XCLKACT; 278 } else if (clk == &sc->sc_clk[clkid_cpu_3or2x]) { 279 reg = ARM_CLK_CTRL; 280 mask = ARM_CLK_CTRL_CPU_3OR2XCLKACT; 281 } else if (clk == &sc->sc_clk[clkid_cpu_6or4x]) { 282 reg = ARM_CLK_CTRL; 283 mask = ARM_CLK_CTRL_CPU_6OR4XCLKACT; 284 } else if (clk == &sc->sc_clk[clkid_sdio0]) { 285 reg = SDIO_CLK_CTRL; 286 mask = CLK_CTRL_CLKACT0; 287 } else if (clk == &sc->sc_clk[clkid_sdio1]) { 288 reg = SDIO_CLK_CTRL; 289 mask = CLK_CTRL_CLKACT1; 290 } else if (clk == &sc->sc_clk[clkid_uart0]) { 291 reg = UART_CLK_CTRL; 292 mask = CLK_CTRL_CLKACT0; 293 } else if (clk == &sc->sc_clk[clkid_uart1]) { 294 reg = UART_CLK_CTRL; 295 mask = CLK_CTRL_CLKACT1; 296 } else if (clk == &sc->sc_clk[clkid_pcap]) { 297 reg = PCAP_CLK_CTRL; 298 mask = CLK_CTRL_CLKACT0; 299 } else if (clk == &sc->sc_clk[clkid_sdio0_aper]) { 300 reg = APER_CLK_CTRL; 301 mask = SDI0_CPU_1XCLKACT; 302 } else if (clk == &sc->sc_clk[clkid_sdio1_aper]) { 303 reg = APER_CLK_CTRL; 304 mask = SDI1_CPU_1XCLKACT; 305 } else if (clk == &sc->sc_clk[clkid_uart0_aper]) { 306 reg = APER_CLK_CTRL; 307 mask = UART0_CPU_1XCLKACT; 308 } else if (clk == &sc->sc_clk[clkid_uart1_aper]) { 309 reg = APER_CLK_CTRL; 310 mask = UART1_CPU_1XCLKACT; 311 } else if (clk == &sc->sc_clk[clkid_i2c0_aper]) { 312 reg = APER_CLK_CTRL; 313 mask = I2C0_CPU_1XCLKACT; 314 } else if (clk == &sc->sc_clk[clkid_i2c1_aper]) { 315 reg = APER_CLK_CTRL; 316 mask = I2C1_CPU_1XCLKACT; 317 } else { 318 return ENXIO; 319 } 320 321 syscon_lock(sc->sc_syscon); 322 val = syscon_read_4(sc->sc_syscon, reg); 323 syscon_write_4(sc->sc_syscon, reg, val | mask); 324 syscon_unlock(sc->sc_syscon); 325 326 return 0; 327} 328 329static int 330zynq7000_clkc_clk_disable(void *priv, struct clk *clk) 331{ 332 return ENXIO; 333} 334 335static const struct clk_funcs zynq7000_clkc_clk_funcs = { 336 .get = zynq7000_clkc_clk_get, 337 .put = zynq7000_clkc_clk_put, 338 .get_rate = zynq7000_clkc_clk_get_rate, 339 .enable = zynq7000_clkc_clk_enable, 340 .disable = zynq7000_clkc_clk_disable, 341}; 342 343static struct clk * 344zynq7000_clkc_fdt_decode(device_t dev, int cc_phandle, const void *data, size_t len) 345{ 346 struct zynq7000_clkc_softc * const sc = device_private(dev); 347 u_int clkid; 348 349 if (len != 4) { 350 return NULL; 351 } 352 353 clkid = be32dec(data); 354 if (clkid >= num_clkid) { 355 return NULL; 356 } 357 358 return &sc->sc_clk[clkid]; 359} 360 361static const struct fdtbus_clock_controller_func zynq7000_clkc_fdt_funcs = { 362 .decode = zynq7000_clkc_fdt_decode 363}; 364 365static int 366zynq7000_clkc_match(device_t parent, cfdata_t cf, void *aux) 367{ 368 struct fdt_attach_args * const faa = aux; 369 370 return of_compatible_match(faa->faa_phandle, compat_data); 371} 372 373static void 374zynq7000_clkc_attach(device_t parent, device_t self, void *aux) 375{ 376 struct zynq7000_clkc_softc * const sc = device_private(self); 377 struct fdt_attach_args * const faa = aux; 378 const int phandle = faa->faa_phandle; 379 const char *clkname; 380 bus_addr_t addr; 381 bus_size_t size; 382 u_int clkid; 383 int error; 384 385 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 386 aprint_error(": couldn't get registers\n"); 387 return; 388 } 389 390 sc->sc_dev = self; 391 sc->sc_syscon = fdtbus_syscon_lookup(OF_parent(phandle)); 392 if (sc->sc_syscon == NULL) { 393 aprint_error(": couldn't get syscon registers\n"); 394 return; 395 } 396 397 error = of_getprop_uint32(phandle, "ps-clk-frequency", 398 &sc->sc_ps_clk_frequency); 399 if (error != 0) { 400 aprint_error(": couldn't get ps-clk-frequency\n"); 401 return; 402 } 403 404 sc->sc_clkdom.name = device_xname(self); 405 sc->sc_clkdom.funcs = &zynq7000_clkc_clk_funcs; 406 sc->sc_clkdom.priv = sc; 407 for (clkid = 0; clkid < num_clkid; clkid++) { 408 clkname = fdtbus_get_string_index(phandle, 409 "clock-output-names", clkid); 410 sc->sc_clk[clkid].domain = &sc->sc_clkdom; 411 if (clkname != NULL) { 412 sc->sc_clk[clkid].name = kmem_asprintf("%s", clkname); 413 } 414 clk_attach(&sc->sc_clk[clkid]); 415 } 416 417 aprint_naive("\n"); 418 aprint_normal(": Zynq-7000 PS clock subsystem (PS_CLK %u Hz)\n", 419 sc->sc_ps_clk_frequency); 420 421 for (clkid = 0; clkid < num_clkid; clkid++) { 422 aprint_debug_dev(self, "clkid %u [%s]: %u Hz\n", clkid, 423 sc->sc_clk[clkid].name ? sc->sc_clk[clkid].name : "<none>", 424 clk_get_rate(&sc->sc_clk[clkid])); 425 } 426 427 fdtbus_register_clock_controller(self, phandle, &zynq7000_clkc_fdt_funcs); 428} 429