xref: /freebsd/sys/riscv/sifive/sifive_prci.c (revision 5956d97f4b3204318ceb6aa9c77bd0bc6ea87a41)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2019 Axiado Corporation
5  * All rights reserved.
6  * Copyright (c) 2021 Jessica Clarke <jrtc27@FreeBSD.org>
7  *
8  * This software was developed in part by Kristof Provost under contract for
9  * Axiado Corporation.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35 
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/bus.h>
39 #include <sys/kernel.h>
40 #include <sys/lock.h>
41 #include <sys/module.h>
42 #include <sys/mutex.h>
43 #include <sys/rman.h>
44 
45 #include <machine/bus.h>
46 #include <machine/cpu.h>
47 
48 #include <dev/extres/clk/clk.h>
49 #include <dev/extres/clk/clk_fixed.h>
50 #include <dev/extres/clk/clk_gate.h>
51 
52 #include <dev/ofw/ofw_bus.h>
53 #include <dev/ofw/ofw_bus_subr.h>
54 #include <dev/ofw/openfirm.h>
55 
56 #include "clkdev_if.h"
57 #include "hwreset_if.h"
58 
59 static struct resource_spec prci_spec[] = {
60 	{ SYS_RES_MEMORY, 0, RF_ACTIVE },
61 	RESOURCE_SPEC_END
62 };
63 
64 struct prci_softc {
65 	device_t		dev;
66 
67 	struct mtx		mtx;
68 
69 	struct clkdom		*clkdom;
70 	struct resource		*res;
71 	bus_space_tag_t		bst;
72 	bus_space_handle_t	bsh;
73 
74 	int			nresets;
75 };
76 
77 struct prci_clk_pll_sc {
78 	struct prci_softc	*parent_sc;
79 	uint32_t		reg;
80 };
81 
82 struct prci_clk_div_sc {
83 	struct prci_softc	*parent_sc;
84 	uint32_t		reg;
85 	uint32_t		bias;
86 };
87 
88 #define	PRCI_LOCK(sc)			mtx_lock(&(sc)->mtx)
89 #define	PRCI_UNLOCK(sc)			mtx_unlock(&(sc)->mtx)
90 #define	PRCI_ASSERT_LOCKED(sc)		mtx_assert(&(sc)->mtx, MA_OWNED);
91 #define	PRCI_ASSERT_UNLOCKED(sc)	mtx_assert(&(sc)->mtx, MA_NOTOWNED);
92 
93 #define	PRCI_PLL_DIVR_MASK		0x3f
94 #define	PRCI_PLL_DIVR_SHIFT		0
95 #define	PRCI_PLL_DIVF_MASK		0x7fc0
96 #define	PRCI_PLL_DIVF_SHIFT		6
97 #define	PRCI_PLL_DIVQ_MASK		0x38000
98 #define	PRCI_PLL_DIVQ_SHIFT		15
99 
100 /* Called devicesresetreg on the FU540 */
101 #define	PRCI_DEVICES_RESET_N		0x28
102 
103 #define	PRCI_READ(_sc, _reg)		\
104     bus_space_read_4((_sc)->bst, (_sc)->bsh, (_reg))
105 #define	PRCI_WRITE(_sc, _reg, _val)	\
106     bus_space_write_4((_sc)->bst, (_sc)->bsh, (_reg), (_val))
107 
108 struct prci_pll_def {
109 	uint32_t	id;
110 	const char	*name;
111 	uint32_t	reg;
112 };
113 
114 #define PLL(_id, _name, _base)					\
115 {								\
116 	.id = (_id),						\
117 	.name = (_name),					\
118 	.reg = (_base),						\
119 }
120 
121 #define PLL_END	PLL(0, NULL, 0)
122 
123 struct prci_div_def {
124 	uint32_t	id;
125 	const char	*name;
126 	const char	*parent_name;
127 	uint32_t	reg;
128 	uint32_t	bias;
129 };
130 
131 #define DIV(_id, _name, _parent_name, _base, _bias)		\
132 {								\
133 	.id = (_id),						\
134 	.name = (_name),					\
135 	.parent_name = (_parent_name),				\
136 	.reg = (_base),						\
137 	.bias = (_bias),					\
138 }
139 
140 #define DIV_END	DIV(0, NULL, NULL, 0, 0)
141 
142 struct prci_gate_def {
143 	uint32_t	id;
144 	const char	*name;
145 	const char	*parent_name;
146 	uint32_t	reg;
147 };
148 
149 #define GATE(_id, _name, _parent_name, _base)			\
150 {								\
151 	.id = (_id),						\
152 	.name = (_name),					\
153 	.parent_name = (_parent_name),				\
154 	.reg = (_base),						\
155 }
156 
157 #define GATE_END	GATE(0, NULL, NULL, 0)
158 
159 struct prci_config {
160 	struct prci_pll_def	*pll_clks;
161 	struct prci_div_def	*div_clks;
162 	struct prci_gate_def	*gate_clks;
163 	struct clk_fixed_def	*tlclk_def;
164 	int			nresets;
165 };
166 
167 /* FU540 clock numbers */
168 #define	FU540_PRCI_CORECLK		0
169 #define	FU540_PRCI_DDRCLK		1
170 #define	FU540_PRCI_GEMGXLCLK		2
171 #define	FU540_PRCI_TLCLK		3
172 
173 /* FU540 registers */
174 #define	FU540_PRCI_COREPLL_CFG0		0x4
175 #define	FU540_PRCI_DDRPLL_CFG0		0xC
176 #define	FU540_PRCI_GEMGXLPLL_CFG0	0x1C
177 
178 /* FU540 PLL clocks */
179 static struct prci_pll_def fu540_pll_clks[] = {
180 	PLL(FU540_PRCI_CORECLK, "coreclk", FU540_PRCI_COREPLL_CFG0),
181 	PLL(FU540_PRCI_DDRCLK, "ddrclk", FU540_PRCI_DDRPLL_CFG0),
182 	PLL(FU540_PRCI_GEMGXLCLK, "gemgxlclk", FU540_PRCI_GEMGXLPLL_CFG0),
183 	PLL_END
184 };
185 
186 /* FU540 fixed divisor clock TLCLK. */
187 static struct clk_fixed_def fu540_tlclk_def = {
188 	.clkdef.id = FU540_PRCI_TLCLK,
189 	.clkdef.name = "tlclk",
190 	.clkdef.parent_names = (const char *[]){"coreclk"},
191 	.clkdef.parent_cnt = 1,
192 	.clkdef.flags = CLK_NODE_STATIC_STRINGS,
193 	.mult = 1,
194 	.div = 2,
195 };
196 
197 /* FU540 config */
198 struct prci_config fu540_prci_config = {
199 	.pll_clks = fu540_pll_clks,
200 	.tlclk_def = &fu540_tlclk_def,
201 	.nresets = 6,
202 };
203 
204 /* FU740 clock numbers */
205 #define	FU740_PRCI_CORECLK		0
206 #define	FU740_PRCI_DDRCLK		1
207 #define	FU740_PRCI_GEMGXLCLK		2
208 #define	FU740_PRCI_DVFSCORECLK		3
209 #define	FU740_PRCI_HFPCLK		4
210 #define	FU740_PRCI_CLTXCLK		5
211 #define	FU740_PRCI_TLCLK		6
212 #define	FU740_PRCI_PCLK			7
213 #define	FU740_PRCI_PCIEAUXCLK		8
214 
215 /* FU740 registers */
216 #define	FU740_PRCI_COREPLL_CFG0		0x4
217 #define	FU740_PRCI_DDRPLL_CFG0		0xC
218 #define	FU740_PRCI_PCIEAUX_GATE		0x14
219 #define	FU740_PRCI_GEMGXLPLL_CFG0	0x1C
220 #define	FU740_PRCI_DVFSCOREPLL_CFG0	0x38
221 #define	FU740_PRCI_HFPCLKPLL_CFG0	0x50
222 #define	FU740_PRCI_CLTXPLL_CFG0		0x30
223 #define	FU740_PRCI_HFPCLK_DIV		0x5C
224 
225 /* FU740 PLL clocks */
226 static struct prci_pll_def fu740_pll_clks[] = {
227 	PLL(FU740_PRCI_CORECLK, "coreclk", FU740_PRCI_COREPLL_CFG0),
228 	PLL(FU740_PRCI_DDRCLK, "ddrclk", FU740_PRCI_DDRPLL_CFG0),
229 	PLL(FU740_PRCI_GEMGXLCLK, "gemgxlclk", FU740_PRCI_GEMGXLPLL_CFG0),
230 	PLL(FU740_PRCI_DVFSCORECLK, "dvfscoreclk", FU740_PRCI_DVFSCOREPLL_CFG0),
231 	PLL(FU740_PRCI_HFPCLK, "hfpclk", FU740_PRCI_HFPCLKPLL_CFG0),
232 	PLL(FU740_PRCI_CLTXCLK, "cltxclk", FU740_PRCI_CLTXPLL_CFG0),
233 	PLL_END
234 };
235 
236 /* FU740 divisor clocks */
237 static struct prci_div_def fu740_div_clks[] = {
238 	DIV(FU740_PRCI_PCLK, "pclk", "hfpclk", FU740_PRCI_HFPCLK_DIV, 2),
239 	DIV_END
240 };
241 
242 /* FU740 gated clocks */
243 static struct prci_gate_def fu740_gate_clks[] = {
244 	GATE(FU740_PRCI_PCIEAUXCLK, "pcieauxclk", "hfclk", FU740_PRCI_PCIEAUX_GATE),
245 	GATE_END
246 };
247 
248 /* FU740 fixed divisor clock TLCLK. */
249 static struct clk_fixed_def fu740_tlclk_def = {
250 	.clkdef.id = FU740_PRCI_TLCLK,
251 	.clkdef.name = "tlclk",
252 	.clkdef.parent_names = (const char *[]){"coreclk"},
253 	.clkdef.parent_cnt = 1,
254 	.clkdef.flags = CLK_NODE_STATIC_STRINGS,
255 	.mult = 1,
256 	.div = 2,
257 };
258 
259 /* FU740 config */
260 struct prci_config fu740_prci_config = {
261 	.pll_clks = fu740_pll_clks,
262 	.div_clks = fu740_div_clks,
263 	.gate_clks = fu740_gate_clks,
264 	.tlclk_def = &fu740_tlclk_def,
265 	.nresets = 7,
266 };
267 
268 static struct ofw_compat_data compat_data[] = {
269 	{ "sifive,aloeprci0",		(uintptr_t)&fu540_prci_config },
270 	{ "sifive,ux00prci0",		(uintptr_t)&fu540_prci_config },
271 	{ "sifive,fu540-c000-prci",	(uintptr_t)&fu540_prci_config },
272 	{ "sifive,fu740-c000-prci",	(uintptr_t)&fu740_prci_config },
273 	{ NULL,				0 },
274 };
275 
276 static int
277 prci_clk_pll_init(struct clknode *clk, device_t dev)
278 {
279 
280 	clknode_init_parent_idx(clk, 0);
281 
282 	return (0);
283 }
284 
285 static int
286 prci_clk_pll_recalc(struct clknode *clk, uint64_t *freq)
287 {
288 	struct prci_clk_pll_sc *sc;
289 	struct clknode *parent_clk;
290 	uint32_t val;
291 	uint64_t refclk, divf, divq, divr;
292 	int err;
293 
294 	KASSERT(freq != NULL, ("freq cannot be NULL"));
295 
296 	sc = clknode_get_softc(clk);
297 
298 	PRCI_LOCK(sc->parent_sc);
299 
300 	/* Get refclock frequency. */
301 	parent_clk = clknode_get_parent(clk);
302 	err = clknode_get_freq(parent_clk, &refclk);
303 	if (err) {
304 		device_printf(sc->parent_sc->dev,
305 		    "Failed to get refclk frequency\n");
306 		PRCI_UNLOCK(sc->parent_sc);
307 		return (err);
308 	}
309 
310 	/* Calculate the PLL output */
311 	val = PRCI_READ(sc->parent_sc, sc->reg);
312 
313 	divf = (val & PRCI_PLL_DIVF_MASK) >> PRCI_PLL_DIVF_SHIFT;
314 	divq = (val & PRCI_PLL_DIVQ_MASK) >> PRCI_PLL_DIVQ_SHIFT;
315 	divr = (val & PRCI_PLL_DIVR_MASK) >> PRCI_PLL_DIVR_SHIFT;
316 
317 	*freq = refclk / (divr + 1) * (2 * (divf + 1)) / (1 << divq);
318 
319 	PRCI_UNLOCK(sc->parent_sc);
320 
321 	return (0);
322 }
323 
324 static clknode_method_t prci_clk_pll_clknode_methods[] = {
325 	CLKNODEMETHOD(clknode_init,		prci_clk_pll_init),
326 	CLKNODEMETHOD(clknode_recalc_freq,	prci_clk_pll_recalc),
327 	CLKNODEMETHOD_END
328 };
329 
330 DEFINE_CLASS_1(prci_clk_pll_clknode, prci_clk_pll_clknode_class,
331     prci_clk_pll_clknode_methods, sizeof(struct prci_clk_pll_sc),
332     clknode_class);
333 
334 static int
335 prci_clk_div_init(struct clknode *clk, device_t dev)
336 {
337 
338 	clknode_init_parent_idx(clk, 0);
339 
340 	return (0);
341 }
342 
343 static int
344 prci_clk_div_recalc(struct clknode *clk, uint64_t *freq)
345 {
346 	struct prci_clk_div_sc *sc;
347 	struct clknode *parent_clk;
348 	uint32_t div;
349 	uint64_t refclk;
350 	int err;
351 
352 	KASSERT(freq != NULL, ("freq cannot be NULL"));
353 
354 	sc = clknode_get_softc(clk);
355 
356 	PRCI_LOCK(sc->parent_sc);
357 
358 	/* Get refclock frequency. */
359 	parent_clk = clknode_get_parent(clk);
360 	err = clknode_get_freq(parent_clk, &refclk);
361 	if (err) {
362 		device_printf(sc->parent_sc->dev,
363 		    "Failed to get refclk frequency\n");
364 		PRCI_UNLOCK(sc->parent_sc);
365 		return (err);
366 	}
367 
368 	/* Calculate the divisor output */
369 	div = PRCI_READ(sc->parent_sc, sc->reg);
370 
371 	*freq = refclk / (div + sc->bias);
372 
373 	PRCI_UNLOCK(sc->parent_sc);
374 
375 	return (0);
376 }
377 
378 static clknode_method_t prci_clk_div_clknode_methods[] = {
379 	CLKNODEMETHOD(clknode_init,		prci_clk_div_init),
380 	CLKNODEMETHOD(clknode_recalc_freq,	prci_clk_div_recalc),
381 	CLKNODEMETHOD_END
382 };
383 
384 DEFINE_CLASS_1(prci_clk_div_clknode, prci_clk_div_clknode_class,
385     prci_clk_div_clknode_methods, sizeof(struct prci_clk_div_sc),
386     clknode_class);
387 
388 static int
389 prci_probe(device_t dev)
390 {
391 
392 	if (!ofw_bus_status_okay(dev))
393 		return (ENXIO);
394 
395 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
396 		return (ENXIO);
397 
398 	device_set_desc(dev, "SiFive Power Reset Clocking Interrupt");
399 
400 	return (BUS_PROBE_DEFAULT);
401 }
402 
403 static void
404 prci_pll_register(struct prci_softc *parent_sc, struct clknode_init_def *clkdef,
405 	uint32_t reg)
406 {
407 	struct clknode *clk;
408 	struct prci_clk_pll_sc *sc;
409 
410 	clk = clknode_create(parent_sc->clkdom, &prci_clk_pll_clknode_class,
411 	    clkdef);
412 	if (clk == NULL)
413 		panic("Failed to create clknode");
414 
415 	sc = clknode_get_softc(clk);
416 	sc->parent_sc = parent_sc;
417 	sc->reg = reg;
418 
419 	clknode_register(parent_sc->clkdom, clk);
420 }
421 
422 static void
423 prci_div_register(struct prci_softc *parent_sc, struct clknode_init_def *clkdef,
424 	uint32_t reg, uint32_t bias)
425 {
426 	struct clknode *clk;
427 	struct prci_clk_div_sc *sc;
428 
429 	clk = clknode_create(parent_sc->clkdom, &prci_clk_div_clknode_class,
430 	    clkdef);
431 	if (clk == NULL)
432 		panic("Failed to create clknode");
433 
434 	sc = clknode_get_softc(clk);
435 	sc->parent_sc = parent_sc;
436 	sc->reg = reg;
437 	sc->bias = bias;
438 
439 	clknode_register(parent_sc->clkdom, clk);
440 }
441 
442 static int
443 prci_attach(device_t dev)
444 {
445 	struct clknode_init_def clkdef, clkdef_div;
446 	struct clk_gate_def clkdef_gate;
447 	struct prci_softc *sc;
448 	clk_t clk_parent;
449 	phandle_t node;
450 	int i, ncells, error;
451 	struct prci_config *cfg;
452 	struct prci_pll_def *pll_clk;
453 	struct prci_div_def *div_clk;
454 	struct prci_gate_def *gate_clk;
455 
456 	sc = device_get_softc(dev);
457 	sc->dev = dev;
458 
459 	cfg = (struct prci_config *)ofw_bus_search_compatible(dev,
460 	    compat_data)->ocd_data;
461 
462 	mtx_init(&sc->mtx, device_get_nameunit(sc->dev), NULL, MTX_DEF);
463 
464 	error = bus_alloc_resources(dev, prci_spec, &sc->res);
465 	if (error) {
466 		device_printf(dev, "Couldn't allocate resources\n");
467 		goto fail;
468 	}
469 	sc->bst = rman_get_bustag(sc->res);
470 	sc->bsh = rman_get_bushandle(sc->res);
471 
472 	node = ofw_bus_get_node(dev);
473 	error = ofw_bus_parse_xref_list_get_length(node, "clocks",
474 	    "#clock-cells", &ncells);
475 	if (error != 0 || ncells < 1) {
476 		device_printf(dev, "couldn't find parent clock\n");
477 		goto fail;
478 	}
479 
480 	bzero(&clkdef, sizeof(clkdef));
481 	clkdef.parent_names = mallocarray(ncells, sizeof(char *), M_OFWPROP,
482 	    M_WAITOK);
483 	for (i = 0; i < ncells; i++) {
484 		error = clk_get_by_ofw_index(dev, 0, i, &clk_parent);
485 		if (error != 0) {
486 			device_printf(dev, "cannot get clock %d\n", error);
487 			goto fail1;
488 		}
489 		clkdef.parent_names[i] = clk_get_name(clk_parent);
490 		if (bootverbose)
491 			device_printf(dev, "clk parent: %s\n",
492 			    clkdef.parent_names[i]);
493 		clk_release(clk_parent);
494 	}
495 	clkdef.parent_cnt = ncells;
496 
497 	sc->clkdom = clkdom_create(dev);
498 	if (sc->clkdom == NULL) {
499 		device_printf(dev, "Couldn't create clock domain\n");
500 		goto fail;
501 	}
502 
503 	/* We can't free a clkdom, so from now on we cannot fail. */
504 	for (pll_clk = cfg->pll_clks; pll_clk->name; pll_clk++) {
505 		clkdef.id = pll_clk->id;
506 		clkdef.name = pll_clk->name;
507 		prci_pll_register(sc, &clkdef, pll_clk->reg);
508 	}
509 
510 	if (cfg->div_clks != NULL) {
511 		bzero(&clkdef_div, sizeof(clkdef_div));
512 		for (div_clk = cfg->div_clks; div_clk->name; div_clk++) {
513 			clkdef_div.id = div_clk->id;
514 			clkdef_div.name = div_clk->name;
515 			clkdef_div.parent_names = &div_clk->parent_name;
516 			clkdef_div.parent_cnt = 1;
517 			prci_div_register(sc, &clkdef_div, div_clk->reg,
518 			    div_clk->bias);
519 		}
520 	}
521 
522 	if (cfg->gate_clks != NULL) {
523 		bzero(&clkdef_gate, sizeof(clkdef_gate));
524 		for (gate_clk = cfg->gate_clks; gate_clk->name; gate_clk++) {
525 			clkdef_gate.clkdef.id = gate_clk->id;
526 			clkdef_gate.clkdef.name = gate_clk->name;
527 			clkdef_gate.clkdef.parent_names = &gate_clk->parent_name;
528 			clkdef_gate.clkdef.parent_cnt = 1;
529 			clkdef_gate.offset = gate_clk->reg;
530 			clkdef_gate.shift = 0;
531 			clkdef_gate.mask = 1;
532 			clkdef_gate.on_value = 1;
533 			clkdef_gate.off_value = 0;
534 			error = clknode_gate_register(sc->clkdom,
535 			    &clkdef_gate);
536 			if (error != 0) {
537 				device_printf(dev,
538 				    "Couldn't create gated clock %s: %d\n",
539 				    gate_clk->name, error);
540 				goto fail;
541 			}
542 		}
543 	}
544 
545 	/*
546 	 * Register the fixed clock "tlclk".
547 	 *
548 	 * If an older device tree is being used, tlclk may appear as its own
549 	 * entity in the device tree, under soc/tlclk. If this is the case it
550 	 * will be registered automatically by the fixed_clk driver, and the
551 	 * version we register here will be an unreferenced duplicate.
552 	 */
553 	clknode_fixed_register(sc->clkdom, cfg->tlclk_def);
554 
555 	error = clkdom_finit(sc->clkdom);
556 	if (error)
557 		panic("Couldn't finalise clock domain");
558 
559 	sc->nresets = cfg->nresets;
560 
561 	return (0);
562 
563 fail1:
564 	free(clkdef.parent_names, M_OFWPROP);
565 
566 fail:
567 	bus_release_resources(dev, prci_spec, &sc->res);
568 	mtx_destroy(&sc->mtx);
569 	return (error);
570 }
571 
572 static int
573 prci_write_4(device_t dev, bus_addr_t addr, uint32_t val)
574 {
575 	struct prci_softc *sc;
576 
577 	sc = device_get_softc(dev);
578 
579 	PRCI_WRITE(sc, addr, val);
580 
581 	return (0);
582 }
583 
584 static int
585 prci_read_4(device_t dev, bus_addr_t addr, uint32_t *val)
586 {
587 	struct prci_softc *sc;
588 
589 	sc = device_get_softc(dev);
590 
591 	*val = PRCI_READ(sc, addr);
592 
593 	return (0);
594 }
595 
596 static int
597 prci_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set)
598 {
599 	struct prci_softc *sc;
600 	uint32_t reg;
601 
602 	sc = device_get_softc(dev);
603 
604 	reg = PRCI_READ(sc, addr);
605 	reg &= ~clr;
606 	reg |= set;
607 	PRCI_WRITE(sc, addr, reg);
608 
609 	return (0);
610 }
611 
612 static void
613 prci_device_lock(device_t dev)
614 {
615 	struct prci_softc *sc;
616 
617 	sc = device_get_softc(dev);
618 	PRCI_LOCK(sc);
619 }
620 
621 static void
622 prci_device_unlock(device_t dev)
623 {
624 	struct prci_softc *sc;
625 
626 	sc = device_get_softc(dev);
627 	PRCI_UNLOCK(sc);
628 }
629 
630 static int
631 prci_reset_assert(device_t dev, intptr_t id, bool reset)
632 {
633 	struct prci_softc *sc;
634 	uint32_t reg;
635 
636 	sc = device_get_softc(dev);
637 
638 	if (id >= sc->nresets)
639 		return (ENXIO);
640 
641 	PRCI_LOCK(sc);
642 	reg = PRCI_READ(sc, PRCI_DEVICES_RESET_N);
643 	if (reset)
644 		reg &= ~(1u << id);
645 	else
646 		reg |= (1u << id);
647 	PRCI_WRITE(sc, PRCI_DEVICES_RESET_N, reg);
648 	PRCI_UNLOCK(sc);
649 
650 	return (0);
651 }
652 
653 static int
654 prci_reset_is_asserted(device_t dev, intptr_t id, bool *reset)
655 {
656 	struct prci_softc *sc;
657 	uint32_t reg;
658 
659 	sc = device_get_softc(dev);
660 
661 	if (id >= sc->nresets)
662 		return (ENXIO);
663 
664 	PRCI_LOCK(sc);
665 	reg = PRCI_READ(sc, PRCI_DEVICES_RESET_N);
666 	*reset = (reg & (1u << id)) == 0;
667 	PRCI_UNLOCK(sc);
668 
669 	return (0);
670 }
671 
672 static device_method_t prci_methods[] = {
673 	DEVMETHOD(device_probe,		prci_probe),
674 	DEVMETHOD(device_attach,	prci_attach),
675 
676 	/* clkdev interface */
677 	DEVMETHOD(clkdev_write_4,	prci_write_4),
678 	DEVMETHOD(clkdev_read_4,	prci_read_4),
679 	DEVMETHOD(clkdev_modify_4,	prci_modify_4),
680 	DEVMETHOD(clkdev_device_lock,	prci_device_lock),
681 	DEVMETHOD(clkdev_device_unlock,	prci_device_unlock),
682 
683 	/* Reset interface */
684 	DEVMETHOD(hwreset_assert,	prci_reset_assert),
685 	DEVMETHOD(hwreset_is_asserted,	prci_reset_is_asserted),
686 
687 	DEVMETHOD_END
688 };
689 
690 static driver_t prci_driver = {
691 	"sifive_prci",
692 	prci_methods,
693 	sizeof(struct prci_softc)
694 };
695 
696 /*
697  * hfclk and rtcclk appear later in the device tree than prci, so we must
698  * attach late.
699  */
700 EARLY_DRIVER_MODULE(sifive_prci, simplebus, prci_driver, 0, 0,
701     BUS_PASS_BUS + BUS_PASS_ORDER_LATE);
702