1/* $OpenBSD: sfclock.c,v 1.2 2022/04/06 18:59:27 naddy 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/intr.h> 23#include <machine/bus.h> 24#include <machine/fdt.h> 25 26#include <dev/ofw/openfirm.h> 27#include <dev/ofw/ofw_clock.h> 28#include <dev/ofw/ofw_misc.h> 29#include <dev/ofw/fdt.h> 30 31/* Clock IDs */ 32#define FU740_CLK_COREPLL 0 33#define FU740_CLK_DDRPLL 1 34#define FU740_CLK_GEMGXLPLL 2 35#define FU740_CLK_DVFSCOREPLL 3 36#define FU740_CLK_HFPCLKPLL 4 37#define FU740_CLK_CLTXPLL 5 38#define FU740_CLK_TLCLK 6 39#define FU740_CLK_PCLK 7 40#define FU740_CLK_PCIE_AUX 8 41 42/* Registers */ 43#define CORE_PLLCFG 0x04 44#define GEMGXL_PLLCFG 0x1c 45#define HFPCLK_PLLCFG 0x50 46#define HFPCLK_PLLOUTDIV 0x54 47#define HFPCLKPLLSEL 0x58 48#define HFPCLKPLLSEL_HFCLK (1 << 0) 49#define HFPCLK_DIV 0x5c 50 51#define PLLCFG_PLLR(x) (((x) >> 0) & 0x3f) 52#define PLLCFG_PLLF(x) (((x) >> 6) & 0x1ff) 53#define PLLCFG_PLLQ(x) (((x) >> 15) & 0x7) 54 55#define HREAD4(sc, reg) \ 56 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) 57#define HWRITE4(sc, reg, val) \ 58 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 59 60struct sfclock_softc { 61 struct device sc_dev; 62 bus_space_tag_t sc_iot; 63 bus_space_handle_t sc_ioh; 64 int sc_node; 65 66 struct clock_device sc_cd; 67}; 68 69int sfclock_match(struct device *, void *, void *); 70void sfclock_attach(struct device *, struct device *, void *); 71 72const struct cfattach sfclock_ca = { 73 sizeof (struct sfclock_softc), sfclock_match, sfclock_attach 74}; 75 76struct cfdriver sfclock_cd = { 77 NULL, "sfclock", DV_DULL 78}; 79 80uint32_t sfclock_get_frequency(void *, uint32_t *); 81int sfclock_set_frequency(void *, uint32_t *, uint32_t); 82void sfclock_enable(void *, uint32_t *, int); 83 84int 85sfclock_match(struct device *parent, void *match, void *aux) 86{ 87 struct fdt_attach_args *faa = aux; 88 89 return OF_is_compatible(faa->fa_node, "sifive,fu740-c000-prci"); 90} 91 92void 93sfclock_attach(struct device *parent, struct device *self, void *aux) 94{ 95 struct sfclock_softc *sc = (struct sfclock_softc *)self; 96 struct fdt_attach_args *faa = aux; 97 98 if (faa->fa_nreg < 1) { 99 printf(": no registers\n"); 100 return; 101 } 102 103 sc->sc_iot = faa->fa_iot; 104 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 105 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 106 printf(": can't map registers\n"); 107 return; 108 } 109 110 sc->sc_node = faa->fa_node; 111 112 printf("\n"); 113 114 sc->sc_cd.cd_node = faa->fa_node; 115 sc->sc_cd.cd_cookie = sc; 116 sc->sc_cd.cd_get_frequency = sfclock_get_frequency; 117 sc->sc_cd.cd_set_frequency = sfclock_set_frequency; 118 sc->sc_cd.cd_enable = sfclock_enable; 119 clock_register(&sc->sc_cd); 120} 121 122uint32_t 123sfclock_getpll_frequency(struct sfclock_softc *sc, bus_size_t off) 124{ 125 uint64_t parent_freq = clock_get_frequency_idx(sc->sc_node, 0); 126 uint32_t pllr, pllf, pllq; 127 uint32_t reg; 128 129 reg = HREAD4(sc, off); 130 pllr = PLLCFG_PLLR(reg); 131 pllf = PLLCFG_PLLF(reg); 132 pllq = PLLCFG_PLLQ(reg); 133 return ((parent_freq * 2 * (pllf + 1)) / (pllr + 1)) >> pllq; 134} 135 136uint32_t 137sfclock_get_frequency(void *cookie, uint32_t *cells) 138{ 139 struct sfclock_softc *sc = cookie; 140 uint32_t idx = cells[0]; 141 uint32_t reg, div; 142 143 switch (idx) { 144 case FU740_CLK_COREPLL: 145 return sfclock_getpll_frequency(sc, CORE_PLLCFG); 146 case FU740_CLK_GEMGXLPLL: 147 return sfclock_getpll_frequency(sc, GEMGXL_PLLCFG); 148 case FU740_CLK_HFPCLKPLL: 149 reg = HREAD4(sc, HFPCLKPLLSEL); 150 if (reg & HFPCLKPLLSEL_HFCLK) 151 return clock_get_frequency_idx(sc->sc_node, 0); 152 return sfclock_getpll_frequency(sc, HFPCLK_PLLCFG); 153 case FU740_CLK_PCLK: 154 div = HREAD4(sc, HFPCLK_DIV) + 2; 155 idx = FU740_CLK_HFPCLKPLL; 156 return sfclock_get_frequency(sc, &idx) / div; 157 } 158 159 printf("%s: 0x%08x\n", __func__, idx); 160 return 0; 161} 162 163int 164sfclock_set_frequency(void *cookie, uint32_t *cells, uint32_t freq) 165{ 166 uint32_t idx = cells[0]; 167 168 printf("%s: 0x%08x\n", __func__, idx); 169 return -1; 170} 171 172void 173sfclock_enable(void *cookie, uint32_t *cells, int on) 174{ 175 uint32_t idx = cells[0]; 176 177 switch (idx) { 178 case FU740_CLK_PCLK: 179 return; 180 } 181 182 printf("%s: 0x%08x\n", __func__, idx); 183} 184