1/*	$NetBSD: fu540_ccache.c,v 1.2 2024/01/14 07:13:15 skrll Exp $	*/
2
3/*-
4 * Copyright (c) 2023 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Nick Hudson
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLinIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33__KERNEL_RCSID(0, "$NetBSD: fu540_ccache.c,v 1.2 2024/01/14 07:13:15 skrll Exp $");
34
35#include <sys/param.h>
36
37#include <sys/bus.h>
38
39#include <dev/fdt/fdtvar.h>
40
41#include <machine/cpufunc.h>
42
43#define CCACHE_CONFIG			0x0000
44#define  CCACHE_CONFIG_BANKS_MASK		__BITS( 7,  0)
45#define  CCACHE_CONFIG_WAYS_MASK		__BITS(15,  8)
46#define  CCACHE_CONFIG_LGSETS_MASK		__BITS(23, 16)
47#define  CCACHE_CONFIG_LGBLOCKBYTES_MASK	__BITS(31, 24)
48#define CCACHE_WAYENABLE		0x0008
49
50#define CCACHE_ECCINJECTERROR		0x0040
51
52#define CCACHE_DIRECCFIX_LOW		0x0100
53#define CCACHE_DIRECCFIX_HIGH		0x0104
54#define CCACHE_DIRECCFIX_COUNT 		0x0108
55
56#define CCACHE_DIRECCFAIL_LOW		0x0120
57#define CCACHE_DIRECCFAIL_HIGH	 	0x0124
58#define CCACHE_DIRECCFAIL_COUNT 	0x0128
59
60#define CCACHE_DATECCFIX_LOW		0x0140
61#define CCACHE_DATECCFIX_HIGH		0x0144
62#define CCACHE_DATECCFIX_COUNT		0x0148
63
64#define CCACHE_DATECCFAIL_LOW		0x0160
65#define CCACHE_DATECCFAIL_HIGH		0x0164
66#define CCACHE_DATECCFAIL_COUNT		0x0168
67
68#define CCACHE_FLUSH64			0x0200
69#define CCACHE_FLUSH32			0x0250
70
71#define CCACHE_MAX_ECCINTR		4
72
73#define CCACHE_FLUSH64_LINE_LEN		64
74
75static const struct device_compatible_entry compat_data[] = {
76	{ .compat = "sifive,fu540-c000-ccache" },
77	{ .compat = "sifive,fu740-c000-ccache" },
78	{ .compat = "starfive,jh7100-ccache" },
79	{ .compat = "starfive,jh7110-ccache" },
80	{ .compat = "starfive,ccache0" },
81	DEVICE_COMPAT_EOL
82};
83
84struct fu540_ccache_softc {
85	device_t		 sc_dev;
86	bus_space_tag_t		 sc_bst;
87	bus_space_handle_t	 sc_bsh;
88
89	uint32_t		 sc_line_size;
90	uint32_t		 sc_size;
91	uint32_t		 sc_sets;
92	uint32_t		 sc_level;
93};
94
95
96static struct fu540_ccache_softc *fu540_ccache_sc;
97
98#define RD4(sc, reg) \
99	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
100#define	WR4(sc, reg, val) \
101	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
102#define	WR8(sc, reg, val)						\
103	bus_space_write_8((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
104
105
106static void
107fu540_ccache_cache_wbinv_range(vaddr_t va, paddr_t pa, psize_t len)
108{
109	struct fu540_ccache_softc * const sc = fu540_ccache_sc;
110
111	KASSERT(powerof2(sc->sc_line_size));
112	KASSERT(len != 0);
113
114	const paddr_t spa = rounddown2(pa, sc->sc_line_size);
115	const paddr_t epa = roundup2(pa + len, sc->sc_line_size);
116
117	asm volatile ("fence iorw,iorw" ::: "memory");
118
119	for (paddr_t fpa = spa; fpa < epa; fpa += sc->sc_line_size) {
120#ifdef _LP64
121		WR8(sc, CCACHE_FLUSH64, fpa);
122#else
123		WR4(sc, CCACHE_FLUSH32, fpa >> 4);
124#endif
125		asm volatile ("fence iorw,iorw" ::: "memory");
126	}
127}
128
129
130static int
131fu540_ccache_match(device_t parent, cfdata_t cf, void *aux)
132{
133	struct fdt_attach_args * const faa = aux;
134
135	return of_compatible_match(faa->faa_phandle, compat_data);
136}
137
138static void
139fu540_ccache_attach(device_t parent, device_t self, void *aux)
140{
141	struct fu540_ccache_softc * const sc = device_private(self);
142	const struct fdt_attach_args * const faa = aux;
143	const int phandle = faa->faa_phandle;
144	bus_addr_t addr;
145	bus_size_t size;
146
147	int error;
148	error = fdtbus_get_reg(phandle, 0, &addr, &size);
149	if (error) {
150		aprint_error(": couldn't get registers\n");
151		return;
152	}
153
154	sc->sc_dev = self;
155	sc->sc_bst = faa->faa_bst;
156
157	error = bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh);
158	if (error) {
159		aprint_error(": couldn't map registers\n");
160		return;
161	}
162
163	int ret;
164	ret = of_getprop_uint32(phandle, "cache-block-size",
165	    &sc->sc_line_size);
166	if (ret < 0) {
167		aprint_error(": can't get cache-block-size\n");
168		return;
169	}
170
171	ret = of_getprop_uint32(phandle, "cache-level",
172	    &sc->sc_level);
173	if (ret < 0) {
174		aprint_error(": can't get cache-level\n");
175		return;
176	}
177
178	ret = of_getprop_uint32(phandle, "cache-sets",
179	    &sc->sc_sets);
180	if (ret < 0) {
181		aprint_error(": can't get cache-sets\n");
182		return;
183	}
184	ret = of_getprop_uint32(phandle, "cache-size",
185	    &sc->sc_size);
186	if (ret < 0) {
187		aprint_error(": can't get cache-size\n");
188		return;
189	}
190
191	if (!of_hasprop(phandle, "cache-unified")) {
192		aprint_error(": can't get cache-unified\n");
193		return;
194	}
195
196	uint32_t ways = sc->sc_size / (sc->sc_sets * sc->sc_line_size);
197
198	aprint_naive("\n");
199	aprint_normal(": L%u cache controller. %u KiB/%uB %u-way (%u set).\n",
200	    sc->sc_level, sc->sc_size / 1024, sc->sc_line_size, ways,
201	    sc->sc_sets);
202
203	uint32_t l2config = RD4(sc, CCACHE_CONFIG);
204
205	aprint_debug_dev(self,   "l2config        %#10" PRIx32 "\n",
206	    l2config);
207	aprint_verbose_dev(self, "No. of banks          %4" __PRIuBIT "\n",
208	    __SHIFTOUT(l2config, CCACHE_CONFIG_BANKS_MASK));
209	aprint_verbose_dev(self, "No. of ways per bank  %4" __PRIuBIT "\n",
210	    __SHIFTOUT(l2config, CCACHE_CONFIG_WAYS_MASK));
211	aprint_verbose_dev(self, "Sets per bank         %4lu\n",
212	    1UL << __SHIFTOUT(l2config, CCACHE_CONFIG_LGSETS_MASK));
213	aprint_verbose_dev(self, "Bytes per cache block %4lu\n",
214	    1UL << __SHIFTOUT(l2config, CCACHE_CONFIG_LGBLOCKBYTES_MASK));
215
216	uint32_t l2wayenable = RD4(sc, CCACHE_WAYENABLE);
217
218	aprint_verbose_dev(self, "Largest way enabled   %4" PRIu32 "\n",
219	    l2wayenable);
220
221	fu540_ccache_sc = sc;
222
223	cpu_sdcache_wbinv_range = fu540_ccache_cache_wbinv_range;
224	cpu_sdcache_inv_range = fu540_ccache_cache_wbinv_range;
225	cpu_sdcache_wb_range = fu540_ccache_cache_wbinv_range;
226}
227
228CFATTACH_DECL_NEW(fu540_ccache, sizeof(struct fu540_ccache_softc),
229	fu540_ccache_match, fu540_ccache_attach, NULL, NULL);
230