1 /* $NetBSD: fu540_prci.c,v 1.1 2022/11/25 12:35:44 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2022 Jared McNeill <jmcneill (at) 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: fu540_prci.c,v 1.1 2022/11/25 12:35:44 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 45 #define COREPLLCFG0 0x04 46 #define DDRPLLCFG0 0x0c 47 #define DDRPLLCFG1 0x10 48 #define GEMGXLPLLCFG0 0x1c 49 #define GEMGXLPLLCFG1 0x20 50 #define PLL0_DIVQ __BITS(17,15) 51 #define PLL0_DIVF __BITS(14,6) 52 #define PLL0_DIVR __BITS(5,0) 53 #define PLL1_CKE __BIT(24) 54 #define CORECLKSEL 0x24 55 56 enum fu540_clkid { 57 clkid_corepll, 58 clkid_ddrpll, 59 clkid_gemgxlpll, 60 clkid_tlclk, 61 num_clkid 62 }; 63 CTASSERT(num_clkid == 4); 64 65 static int fu540_prci_match(device_t, cfdata_t, void *); 66 static void fu540_prci_attach(device_t, device_t, void *); 67 68 static u_int fu540_prci_clk_get_rate(void *, struct clk *); 69 70 static const struct device_compatible_entry compat_data[] = { 71 { .compat = "sifive,fu540-c000-prci" }, 72 DEVICE_COMPAT_EOL 73 }; 74 75 struct fu540_prci_softc { 76 device_t sc_dev; 77 bus_space_tag_t sc_bst; 78 bus_space_handle_t sc_bsh; 79 struct clk_domain sc_clkdom; 80 struct clk sc_clk[num_clkid]; 81 82 u_int sc_hfclk; 83 u_int sc_rtcclk; 84 }; 85 86 #define RD4(sc, reg) \ 87 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) 88 #define WR4(sc, reg, val) \ 89 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 90 91 CFATTACH_DECL_NEW(fu540_prci, sizeof(struct fu540_prci_softc), 92 fu540_prci_match, fu540_prci_attach, NULL, NULL); 93 94 static struct clk * 95 fu540_prci_clk_get(void *priv, const char *name) 96 { 97 struct fu540_prci_softc * const sc = priv; 98 u_int clkid; 99 100 for (clkid = 0; clkid < num_clkid; clkid++) { 101 if (strcmp(name, sc->sc_clk[clkid].name) == 0) { 102 return &sc->sc_clk[clkid]; 103 } 104 } 105 106 return NULL; 107 } 108 109 static void 110 fu540_prci_clk_put(void *priv, struct clk *clk) 111 { 112 } 113 114 static u_int 115 fu540_prci_clk_get_rate_pll(struct fu540_prci_softc *sc, u_int reg) 116 { 117 uint32_t val; 118 u_int rate, divr, divf, divq; 119 120 val = RD4(sc, reg); 121 divr = __SHIFTOUT(val, PLL0_DIVR) + 1; 122 divf = __SHIFTOUT(val, PLL0_DIVF) + 1; 123 divq = __SHIFTOUT(val, PLL0_DIVQ); 124 rate = sc->sc_hfclk * divr * divf; 125 rate <<= divq; 126 127 return rate; 128 } 129 130 static u_int 131 fu540_prci_clk_get_rate(void *priv, struct clk *clk) 132 { 133 struct fu540_prci_softc * const sc = priv; 134 u_int rate; 135 136 if (clk == &sc->sc_clk[clkid_corepll] || 137 clk == &sc->sc_clk[clkid_tlclk]) { 138 rate = fu540_prci_clk_get_rate_pll(sc, COREPLLCFG0); 139 if (clk == &sc->sc_clk[clkid_tlclk]) { 140 rate /= 2; 141 } 142 return rate; 143 } else if (clk == &sc->sc_clk[clkid_ddrpll]) { 144 return fu540_prci_clk_get_rate_pll(sc, DDRPLLCFG0); 145 } else if (clk == &sc->sc_clk[clkid_gemgxlpll]) { 146 return fu540_prci_clk_get_rate_pll(sc, GEMGXLPLLCFG0); 147 } else { 148 /* Not implemented. */ 149 return 0; 150 } 151 } 152 153 static int 154 fu540_prci_clk_enable(void *priv, struct clk *clk) 155 { 156 struct fu540_prci_softc * const sc = priv; 157 uint32_t val; 158 159 if (clk == &sc->sc_clk[clkid_corepll] || 160 clk == &sc->sc_clk[clkid_tlclk]) { 161 /* Always enabled. */ 162 return 0; 163 } else if (clk == &sc->sc_clk[clkid_ddrpll]) { 164 val = RD4(sc, DDRPLLCFG1); 165 WR4(sc, DDRPLLCFG1, val | PLL1_CKE); 166 return 0; 167 } else if (clk == &sc->sc_clk[clkid_gemgxlpll]) { 168 val = RD4(sc, GEMGXLPLLCFG1); 169 WR4(sc, GEMGXLPLLCFG1, val | PLL1_CKE); 170 return 0; 171 } else { 172 /* Not implemented. */ 173 return ENXIO; 174 } 175 } 176 177 static int 178 fu540_prci_clk_disable(void *priv, struct clk *clk) 179 { 180 return ENXIO; 181 } 182 183 static const struct clk_funcs fu540_prci_clk_funcs = { 184 .get = fu540_prci_clk_get, 185 .put = fu540_prci_clk_put, 186 .get_rate = fu540_prci_clk_get_rate, 187 .enable = fu540_prci_clk_enable, 188 .disable = fu540_prci_clk_disable, 189 }; 190 191 static struct clk * 192 fu540_prci_fdt_decode(device_t dev, int cc_phandle, const void *data, size_t len) 193 { 194 struct fu540_prci_softc * const sc = device_private(dev); 195 u_int clkid; 196 197 if (len != 4) { 198 return NULL; 199 } 200 201 clkid = be32dec(data); 202 if (clkid >= num_clkid) { 203 return NULL; 204 } 205 206 return &sc->sc_clk[clkid]; 207 } 208 209 static const struct fdtbus_clock_controller_func fu540_prci_fdt_funcs = { 210 .decode = fu540_prci_fdt_decode 211 }; 212 213 static int 214 fu540_prci_match(device_t parent, cfdata_t cf, void *aux) 215 { 216 struct fdt_attach_args * const faa = aux; 217 218 return of_compatible_match(faa->faa_phandle, compat_data); 219 } 220 221 static void 222 fu540_prci_attach(device_t parent, device_t self, void *aux) 223 { 224 struct fu540_prci_softc * const sc = device_private(self); 225 struct fdt_attach_args * const faa = aux; 226 const int phandle = faa->faa_phandle; 227 const char *clkname; 228 struct clk *clk; 229 bus_addr_t addr; 230 bus_size_t size; 231 u_int clkid; 232 233 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 234 aprint_error(": couldn't get registers\n"); 235 return; 236 } 237 238 sc->sc_dev = self; 239 sc->sc_bst = faa->faa_bst; 240 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { 241 aprint_error(": couldn't map registers\n"); 242 return; 243 } 244 245 clk = fdtbus_clock_get(phandle, "hfclk"); 246 if (clk == NULL) { 247 aprint_error(": couldn't get hfclk\n"); 248 return; 249 } 250 sc->sc_hfclk = clk_get_rate(clk); 251 252 clk = fdtbus_clock_get(phandle, "rtcclk"); 253 if (clk == NULL) { 254 aprint_error(": couldn't get rtcclk\n"); 255 return; 256 } 257 sc->sc_rtcclk = clk_get_rate(clk); 258 259 sc->sc_clkdom.name = device_xname(self); 260 sc->sc_clkdom.funcs = &fu540_prci_clk_funcs; 261 sc->sc_clkdom.priv = sc; 262 for (clkid = 0; clkid < num_clkid; clkid++) { 263 clkname = fdtbus_get_string_index(phandle, 264 "clock-output-names", clkid); 265 sc->sc_clk[clkid].domain = &sc->sc_clkdom; 266 if (clkname != NULL) { 267 sc->sc_clk[clkid].name = kmem_asprintf("%s", clkname); 268 } 269 clk_attach(&sc->sc_clk[clkid]); 270 } 271 272 aprint_naive("\n"); 273 aprint_normal(": FU540 PRCI (HF %u Hz, RTC %u Hz)\n", 274 sc->sc_hfclk, sc->sc_rtcclk); 275 276 for (clkid = 0; clkid < num_clkid; clkid++) { 277 aprint_debug_dev(self, "clkid %u [%s]: %u Hz\n", clkid, 278 sc->sc_clk[clkid].name ? sc->sc_clk[clkid].name : "<none>", 279 clk_get_rate(&sc->sc_clk[clkid])); 280 } 281 282 fdtbus_register_clock_controller(self, phandle, &fu540_prci_fdt_funcs); 283 } 284