xref: /openbsd-src/sys/arch/riscv64/dev/sfclock.c (revision e309ca491b5bd8801f54e6ae9242b0614938efcc)
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