1 /* $OpenBSD: sfclock.c,v 1.3 2024/10/17 01:57:18 jsg Exp $ */ 2 /* 3 * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/systm.h> 20 #include <sys/device.h> 21 22 #include <machine/bus.h> 23 #include <machine/fdt.h> 24 25 #include <dev/ofw/openfirm.h> 26 #include <dev/ofw/ofw_clock.h> 27 #include <dev/ofw/fdt.h> 28 29 /* Clock IDs */ 30 #define FU740_CLK_COREPLL 0 31 #define FU740_CLK_DDRPLL 1 32 #define FU740_CLK_GEMGXLPLL 2 33 #define FU740_CLK_DVFSCOREPLL 3 34 #define FU740_CLK_HFPCLKPLL 4 35 #define FU740_CLK_CLTXPLL 5 36 #define FU740_CLK_TLCLK 6 37 #define FU740_CLK_PCLK 7 38 #define FU740_CLK_PCIE_AUX 8 39 40 /* Registers */ 41 #define CORE_PLLCFG 0x04 42 #define GEMGXL_PLLCFG 0x1c 43 #define HFPCLK_PLLCFG 0x50 44 #define HFPCLK_PLLOUTDIV 0x54 45 #define HFPCLKPLLSEL 0x58 46 #define HFPCLKPLLSEL_HFCLK (1 << 0) 47 #define HFPCLK_DIV 0x5c 48 49 #define PLLCFG_PLLR(x) (((x) >> 0) & 0x3f) 50 #define PLLCFG_PLLF(x) (((x) >> 6) & 0x1ff) 51 #define PLLCFG_PLLQ(x) (((x) >> 15) & 0x7) 52 53 #define HREAD4(sc, reg) \ 54 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) 55 #define HWRITE4(sc, reg, val) \ 56 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 57 58 struct sfclock_softc { 59 struct device sc_dev; 60 bus_space_tag_t sc_iot; 61 bus_space_handle_t sc_ioh; 62 int sc_node; 63 64 struct clock_device sc_cd; 65 }; 66 67 int sfclock_match(struct device *, void *, void *); 68 void sfclock_attach(struct device *, struct device *, void *); 69 70 const struct cfattach sfclock_ca = { 71 sizeof (struct sfclock_softc), sfclock_match, sfclock_attach 72 }; 73 74 struct cfdriver sfclock_cd = { 75 NULL, "sfclock", DV_DULL 76 }; 77 78 uint32_t sfclock_get_frequency(void *, uint32_t *); 79 int sfclock_set_frequency(void *, uint32_t *, uint32_t); 80 void sfclock_enable(void *, uint32_t *, int); 81 82 int 83 sfclock_match(struct device *parent, void *match, void *aux) 84 { 85 struct fdt_attach_args *faa = aux; 86 87 return OF_is_compatible(faa->fa_node, "sifive,fu740-c000-prci"); 88 } 89 90 void 91 sfclock_attach(struct device *parent, struct device *self, void *aux) 92 { 93 struct sfclock_softc *sc = (struct sfclock_softc *)self; 94 struct fdt_attach_args *faa = aux; 95 96 if (faa->fa_nreg < 1) { 97 printf(": no registers\n"); 98 return; 99 } 100 101 sc->sc_iot = faa->fa_iot; 102 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 103 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 104 printf(": can't map registers\n"); 105 return; 106 } 107 108 sc->sc_node = faa->fa_node; 109 110 printf("\n"); 111 112 sc->sc_cd.cd_node = faa->fa_node; 113 sc->sc_cd.cd_cookie = sc; 114 sc->sc_cd.cd_get_frequency = sfclock_get_frequency; 115 sc->sc_cd.cd_set_frequency = sfclock_set_frequency; 116 sc->sc_cd.cd_enable = sfclock_enable; 117 clock_register(&sc->sc_cd); 118 } 119 120 uint32_t 121 sfclock_getpll_frequency(struct sfclock_softc *sc, bus_size_t off) 122 { 123 uint64_t parent_freq = clock_get_frequency_idx(sc->sc_node, 0); 124 uint32_t pllr, pllf, pllq; 125 uint32_t reg; 126 127 reg = HREAD4(sc, off); 128 pllr = PLLCFG_PLLR(reg); 129 pllf = PLLCFG_PLLF(reg); 130 pllq = PLLCFG_PLLQ(reg); 131 return ((parent_freq * 2 * (pllf + 1)) / (pllr + 1)) >> pllq; 132 } 133 134 uint32_t 135 sfclock_get_frequency(void *cookie, uint32_t *cells) 136 { 137 struct sfclock_softc *sc = cookie; 138 uint32_t idx = cells[0]; 139 uint32_t reg, div; 140 141 switch (idx) { 142 case FU740_CLK_COREPLL: 143 return sfclock_getpll_frequency(sc, CORE_PLLCFG); 144 case FU740_CLK_GEMGXLPLL: 145 return sfclock_getpll_frequency(sc, GEMGXL_PLLCFG); 146 case FU740_CLK_HFPCLKPLL: 147 reg = HREAD4(sc, HFPCLKPLLSEL); 148 if (reg & HFPCLKPLLSEL_HFCLK) 149 return clock_get_frequency_idx(sc->sc_node, 0); 150 return sfclock_getpll_frequency(sc, HFPCLK_PLLCFG); 151 case FU740_CLK_PCLK: 152 div = HREAD4(sc, HFPCLK_DIV) + 2; 153 idx = FU740_CLK_HFPCLKPLL; 154 return sfclock_get_frequency(sc, &idx) / div; 155 } 156 157 printf("%s: 0x%08x\n", __func__, idx); 158 return 0; 159 } 160 161 int 162 sfclock_set_frequency(void *cookie, uint32_t *cells, uint32_t freq) 163 { 164 uint32_t idx = cells[0]; 165 166 printf("%s: 0x%08x\n", __func__, idx); 167 return -1; 168 } 169 170 void 171 sfclock_enable(void *cookie, uint32_t *cells, int on) 172 { 173 uint32_t idx = cells[0]; 174 175 switch (idx) { 176 case FU740_CLK_PCLK: 177 return; 178 } 179 180 printf("%s: 0x%08x\n", __func__, idx); 181 } 182