xref: /netbsd-src/sys/arch/riscv/sifive/fu540_prci.c (revision 644d8f85eb5b192f82c18bd555b19090fa48928d)
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@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 *
fu540_prci_clk_get(void * priv,const char * name)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
fu540_prci_clk_put(void * priv,struct clk * clk)110 fu540_prci_clk_put(void *priv, struct clk *clk)
111 {
112 }
113 
114 static u_int
fu540_prci_clk_get_rate_pll(struct fu540_prci_softc * sc,u_int reg)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
fu540_prci_clk_get_rate(void * priv,struct clk * clk)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
fu540_prci_clk_enable(void * priv,struct clk * clk)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
fu540_prci_clk_disable(void * priv,struct clk * clk)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 *
fu540_prci_fdt_decode(device_t dev,int cc_phandle,const void * data,size_t len)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
fu540_prci_match(device_t parent,cfdata_t cf,void * aux)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
fu540_prci_attach(device_t parent,device_t self,void * aux)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