1*e3ee5e84Smpi /* $OpenBSD: arml2cc.c,v 1.8 2022/03/12 14:40:41 mpi Exp $ */
2d1433e4aSpatrick /*
3d1433e4aSpatrick * Copyright (c) 2013 Patrick Wildt <patrick@blueri.se>
4d1433e4aSpatrick *
5d1433e4aSpatrick * Permission to use, copy, modify, and distribute this software for any
6d1433e4aSpatrick * purpose with or without fee is hereby granted, provided that the above
7d1433e4aSpatrick * copyright notice and this permission notice appear in all copies.
8d1433e4aSpatrick *
9d1433e4aSpatrick * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10d1433e4aSpatrick * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11d1433e4aSpatrick * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12d1433e4aSpatrick * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13d1433e4aSpatrick * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14d1433e4aSpatrick * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15d1433e4aSpatrick * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16d1433e4aSpatrick */
17d1433e4aSpatrick
18d1433e4aSpatrick #include <sys/param.h>
19d1433e4aSpatrick #include <sys/systm.h>
20d1433e4aSpatrick #include <sys/device.h>
21d1433e4aSpatrick #include <machine/intr.h>
22d1433e4aSpatrick #include <machine/bus.h>
23170422a0Spatrick #include <arm/cpufunc.h>
24d1433e4aSpatrick #include <arm/cortex/cortex.h>
25d1433e4aSpatrick #include <arm/cortex/smc.h>
26d1433e4aSpatrick
27d1433e4aSpatrick #define PL310_ERRATA_727915
28d1433e4aSpatrick
29d1433e4aSpatrick /* offset from periphbase */
30d1433e4aSpatrick #define L2C_ADDR 0x2000
31d1433e4aSpatrick #define L2C_SIZE 0x1000
32d1433e4aSpatrick
33d1433e4aSpatrick /* registers */
34d1433e4aSpatrick #define L2C_CACHE_ID 0x000
35d1433e4aSpatrick #define L2C_CACHE_TYPE 0x004
36d1433e4aSpatrick #define L2C_CTL 0x100
37d1433e4aSpatrick #define L2C_AUXCTL 0x104
38d1433e4aSpatrick #define L2C_TAG_RAM_CTL 0x108
39d1433e4aSpatrick #define L2C_DATA_RAM_CTL 0x10c
40d1433e4aSpatrick #define L2C_EVC_CTR_CTL 0x200
41d1433e4aSpatrick #define L2C_EVC_CTR0_CTL 0x204
42d1433e4aSpatrick #define L2C_EVC_CTR1_CTL 0x208
43d1433e4aSpatrick #define L2C_EVC_CTR0_VAL 0x20c
44d1433e4aSpatrick #define L2C_EVC_CTR1_VAL 0x210
45d1433e4aSpatrick #define L2C_INT_MASK 0x214
46d1433e4aSpatrick #define L2C_INT_MASK_STS 0x218
47d1433e4aSpatrick #define L2C_INT_RAW_STS 0x21c
48d1433e4aSpatrick #define L2C_INT_CLR 0x220
49d1433e4aSpatrick #define L2C_CACHE_SYNC 0x730
50d1433e4aSpatrick #define L2C_INV_PA 0x770
51d1433e4aSpatrick #define L2C_INV_WAY 0x77c
52d1433e4aSpatrick #define L2C_CLEAN_PA 0x7b0
53d1433e4aSpatrick #define L2C_CLEAN_INDEX 0x7b8
54d1433e4aSpatrick #define L2C_CLEAN_WAY 0x7bc
55d1433e4aSpatrick #define L2C_CLEAN_INV_PA 0x7f0
56d1433e4aSpatrick #define L2C_CLEAN_INV_INDEX 0x7f8
57d1433e4aSpatrick #define L2C_CLEAN_INV_WAY 0x7fc
58d1433e4aSpatrick #define L2C_D_LOCKDOWN0 0x900
59d1433e4aSpatrick #define L2C_I_LOCKDOWN0 0x904
60d1433e4aSpatrick #define L2C_D_LOCKDOWN1 0x908
61d1433e4aSpatrick #define L2C_I_LOCKDOWN1 0x90c
62d1433e4aSpatrick #define L2C_D_LOCKDOWN2 0x910
63d1433e4aSpatrick #define L2C_I_LOCKDOWN2 0x914
64d1433e4aSpatrick #define L2C_D_LOCKDOWN3 0x918
65d1433e4aSpatrick #define L2C_I_LOCKDOWN3 0x91c
66d1433e4aSpatrick #define L2C_D_LOCKDOWN4 0x920
67d1433e4aSpatrick #define L2C_I_LOCKDOWN4 0x924
68d1433e4aSpatrick #define L2C_D_LOCKDOWN5 0x928
69d1433e4aSpatrick #define L2C_I_LOCKDOWN5 0x92c
70d1433e4aSpatrick #define L2C_D_LOCKDOWN6 0x930
71d1433e4aSpatrick #define L2C_I_LOCKDOWN6 0x934
72d1433e4aSpatrick #define L2C_D_LOCKDOWN7 0x938
73d1433e4aSpatrick #define L2C_I_LOCKDOWN7 0x93c
74d1433e4aSpatrick #define L2C_LOCKDOWN_LINE_EN 0x950
75d1433e4aSpatrick #define L2C_UNLOCK_WAY 0x954
76d1433e4aSpatrick #define L2C_ADDR_FILTER_START 0xc00
77d1433e4aSpatrick #define L2C_ADDR_FILTER_END 0xc04
78d1433e4aSpatrick #define L2C_DEBUG_CTL 0xf40
79d1433e4aSpatrick #define L2C_PREFETCH_CTL 0xf60
80d1433e4aSpatrick #define L2C_POWER_CTL 0xf80
81d1433e4aSpatrick
82d1433e4aSpatrick #define L2C_CACHE_ID_RELEASE_MASK 0x3f
83d1433e4aSpatrick #define L2C_CACHE_TYPE_LINESIZE 0x3
84d1433e4aSpatrick #define L2C_AUXCTL_ASSOC_SHIFT 16
85d1433e4aSpatrick #define L2C_AUXCTL_ASSOC_MASK 0x1
86d1433e4aSpatrick
87d1433e4aSpatrick #define roundup2(size, unit) (((size) + (unit) - 1) & ~((unit) - 1))
88d1433e4aSpatrick
89d1433e4aSpatrick struct arml2cc_softc {
90d1433e4aSpatrick struct device sc_dev;
91d1433e4aSpatrick bus_space_tag_t sc_iot;
92d1433e4aSpatrick bus_space_handle_t sc_ioh;
93d1433e4aSpatrick uint32_t sc_enabled;
94d1433e4aSpatrick uint32_t sc_waymask;
95d1433e4aSpatrick uint32_t sc_dcache_line_size;
96d1433e4aSpatrick };
97d1433e4aSpatrick
98d1433e4aSpatrick struct arml2cc_softc *arml2cc_sc;
99d1433e4aSpatrick
100d1433e4aSpatrick int arml2cc_match(struct device *, void *, void *);
101d1433e4aSpatrick void arml2cc_attach(struct device *parent, struct device *self, void *args);
102d1433e4aSpatrick void arml2cc_enable(struct arml2cc_softc *);
103d1433e4aSpatrick void arml2cc_disable(struct arml2cc_softc *);
104d1433e4aSpatrick void arml2cc_sdcache_wbinv_all(void);
105d1433e4aSpatrick void arml2cc_sdcache_wbinv_range(vaddr_t, paddr_t, psize_t);
106d1433e4aSpatrick void arml2cc_sdcache_inv_range(vaddr_t, paddr_t, psize_t);
107d1433e4aSpatrick void arml2cc_sdcache_wb_range(vaddr_t, paddr_t, psize_t);
108d1433e4aSpatrick void arml2cc_cache_range_op(paddr_t, psize_t, bus_size_t);
109d1433e4aSpatrick void arml2cc_cache_way_op(struct arml2cc_softc *, bus_size_t, uint32_t);
110d1433e4aSpatrick void arml2cc_cache_op(struct arml2cc_softc *, bus_size_t, uint32_t);
111d1433e4aSpatrick void arml2cc_cache_sync(struct arml2cc_softc *);
112c4f21f07Sjsg void arml2cc_sdcache_drain_writebuf(void);
113d1433e4aSpatrick
114*e3ee5e84Smpi const struct cfattach armliicc_ca = {
115d1433e4aSpatrick sizeof (struct arml2cc_softc), arml2cc_match, arml2cc_attach
116d1433e4aSpatrick };
117d1433e4aSpatrick
118d1433e4aSpatrick struct cfdriver armliicc_cd = {
119d1433e4aSpatrick NULL, "armliicc", DV_DULL
120d1433e4aSpatrick };
121d1433e4aSpatrick
122d1433e4aSpatrick int
arml2cc_match(struct device * parent,void * cfdata,void * aux)123d1433e4aSpatrick arml2cc_match(struct device *parent, void *cfdata, void *aux)
124d1433e4aSpatrick {
125c69d9998Sjsg if ((cpufunc_id() & CPU_ID_CORTEX_A9_MASK) == CPU_ID_CORTEX_A9)
126d1433e4aSpatrick return (1);
127c69d9998Sjsg
128c69d9998Sjsg return (0);
129d1433e4aSpatrick }
130d1433e4aSpatrick
131d1433e4aSpatrick void
arml2cc_attach(struct device * parent,struct device * self,void * args)132d1433e4aSpatrick arml2cc_attach(struct device *parent, struct device *self, void *args)
133d1433e4aSpatrick {
134d1433e4aSpatrick struct cortex_attach_args *ia = args;
135d1433e4aSpatrick struct arml2cc_softc *sc = (struct arml2cc_softc *) self;
136d1433e4aSpatrick
137d1433e4aSpatrick sc->sc_iot = ia->ca_iot;
138d1433e4aSpatrick if (bus_space_map(sc->sc_iot, ia->ca_periphbase + L2C_ADDR,
139d1433e4aSpatrick L2C_SIZE, 0, &sc->sc_ioh))
140d1433e4aSpatrick panic("arml2cc_attach: bus_space_map failed!");
141d1433e4aSpatrick
142d1433e4aSpatrick printf(": rtl %d", bus_space_read_4(sc->sc_iot, sc->sc_ioh,
143d1433e4aSpatrick L2C_CACHE_ID) & 0x3f);
144d1433e4aSpatrick
145d1433e4aSpatrick arml2cc_sc = sc;
146d1433e4aSpatrick
147d1433e4aSpatrick if (bus_space_read_4(sc->sc_iot, sc->sc_ioh, L2C_CTL))
1484123b6a7Sderaadt panic("L2 Cache controller was already enabled");
149d1433e4aSpatrick
150d1433e4aSpatrick sc->sc_dcache_line_size = 32 << (bus_space_read_4(sc->sc_iot, sc->sc_ioh, L2C_CACHE_TYPE) & L2C_CACHE_TYPE_LINESIZE);
151d1433e4aSpatrick sc->sc_waymask = (8 << ((bus_space_read_4(sc->sc_iot, sc->sc_ioh, L2C_AUXCTL)
152d1433e4aSpatrick >> L2C_AUXCTL_ASSOC_SHIFT) & L2C_AUXCTL_ASSOC_MASK)) - 1;
153d1433e4aSpatrick printf(" waymask: 0x%08x\n", sc->sc_waymask);
154d1433e4aSpatrick
155d1433e4aSpatrick arml2cc_enable(sc);
156d1433e4aSpatrick sc->sc_enabled = 1;
157d1433e4aSpatrick
158d1433e4aSpatrick arml2cc_sdcache_wbinv_all();
159d1433e4aSpatrick
160d1433e4aSpatrick cpufuncs.cf_sdcache_wbinv_all = arml2cc_sdcache_wbinv_all;
161d1433e4aSpatrick cpufuncs.cf_sdcache_wbinv_range = arml2cc_sdcache_wbinv_range;
162d1433e4aSpatrick cpufuncs.cf_sdcache_inv_range = arml2cc_sdcache_inv_range;
163d1433e4aSpatrick cpufuncs.cf_sdcache_wb_range = arml2cc_sdcache_wb_range;
164c4f21f07Sjsg cpufuncs.cf_sdcache_drain_writebuf = arml2cc_sdcache_drain_writebuf;
165d1433e4aSpatrick }
166d1433e4aSpatrick
167d1433e4aSpatrick void
arml2cc_enable(struct arml2cc_softc * sc)168d1433e4aSpatrick arml2cc_enable(struct arml2cc_softc *sc)
169d1433e4aSpatrick {
170d1433e4aSpatrick int s;
171d1433e4aSpatrick s = splhigh();
172d1433e4aSpatrick
173d1433e4aSpatrick platform_smc_write(sc->sc_iot, sc->sc_ioh, L2C_CTL, SMC_L2_CTL,
174d1433e4aSpatrick 1);
175d1433e4aSpatrick
176d1433e4aSpatrick arml2cc_cache_way_op(sc, L2C_INV_WAY, sc->sc_waymask);
177d1433e4aSpatrick arml2cc_cache_sync(sc);
178d1433e4aSpatrick
179d1433e4aSpatrick splx(s);
180d1433e4aSpatrick }
181d1433e4aSpatrick
182d1433e4aSpatrick void
arml2cc_disable(struct arml2cc_softc * sc)183d1433e4aSpatrick arml2cc_disable(struct arml2cc_softc *sc)
184d1433e4aSpatrick {
185d1433e4aSpatrick int s;
186d1433e4aSpatrick s = splhigh();
187d1433e4aSpatrick
188d1433e4aSpatrick arml2cc_cache_way_op(sc, L2C_CLEAN_INV_WAY, sc->sc_waymask);
189d1433e4aSpatrick arml2cc_cache_sync(sc);
190d1433e4aSpatrick
191d1433e4aSpatrick platform_smc_write(sc->sc_iot, sc->sc_ioh, L2C_CTL, SMC_L2_CTL, 0);
192d1433e4aSpatrick
193d1433e4aSpatrick splx(s);
194d1433e4aSpatrick }
195d1433e4aSpatrick
196d1433e4aSpatrick void
arml2cc_cache_op(struct arml2cc_softc * sc,bus_size_t off,uint32_t val)197d1433e4aSpatrick arml2cc_cache_op(struct arml2cc_softc *sc, bus_size_t off, uint32_t val)
198d1433e4aSpatrick {
199d1433e4aSpatrick bus_space_write_4(sc->sc_iot, sc->sc_ioh, off, val);
200d1433e4aSpatrick while (bus_space_read_4(sc->sc_iot, sc->sc_ioh, off) & 1) {
201d1433e4aSpatrick /* spin */
202d1433e4aSpatrick }
203d1433e4aSpatrick }
204d1433e4aSpatrick
205d1433e4aSpatrick void
arml2cc_cache_way_op(struct arml2cc_softc * sc,bus_size_t off,uint32_t way_mask)206d1433e4aSpatrick arml2cc_cache_way_op(struct arml2cc_softc *sc, bus_size_t off, uint32_t way_mask)
207d1433e4aSpatrick {
208d1433e4aSpatrick bus_space_write_4(sc->sc_iot, sc->sc_ioh, off, way_mask);
209d1433e4aSpatrick while (bus_space_read_4(sc->sc_iot, sc->sc_ioh, off) & way_mask) {
210d1433e4aSpatrick /* spin */
211d1433e4aSpatrick }
212d1433e4aSpatrick }
213d1433e4aSpatrick
214d1433e4aSpatrick void
arml2cc_cache_sync(struct arml2cc_softc * sc)215d1433e4aSpatrick arml2cc_cache_sync(struct arml2cc_softc *sc)
216d1433e4aSpatrick {
217d1433e4aSpatrick /* ARM Errata 753970 */
218d1433e4aSpatrick bus_space_write_4(sc->sc_iot, sc->sc_ioh, 0x740, 0xffffffff);
219d1433e4aSpatrick }
220d1433e4aSpatrick
221d1433e4aSpatrick void
arml2cc_sdcache_drain_writebuf(void)222c4f21f07Sjsg arml2cc_sdcache_drain_writebuf(void)
223c4f21f07Sjsg {
224c4f21f07Sjsg struct arml2cc_softc * const sc = arml2cc_sc;
225c4f21f07Sjsg if (sc == NULL || !sc->sc_enabled)
226c4f21f07Sjsg return;
227c4f21f07Sjsg
228c4f21f07Sjsg arml2cc_cache_sync(sc);
229c4f21f07Sjsg }
230c4f21f07Sjsg
231c4f21f07Sjsg void
arml2cc_cache_range_op(paddr_t pa,psize_t len,bus_size_t cache_op)232d1433e4aSpatrick arml2cc_cache_range_op(paddr_t pa, psize_t len, bus_size_t cache_op)
233d1433e4aSpatrick {
234d1433e4aSpatrick struct arml2cc_softc * const sc = arml2cc_sc;
235d1433e4aSpatrick size_t line_size = sc->sc_dcache_line_size;
236d1433e4aSpatrick size_t line_mask = line_size - 1;
237d1433e4aSpatrick paddr_t endpa;
238d1433e4aSpatrick
239d1433e4aSpatrick endpa = pa + len;
240d1433e4aSpatrick pa = pa & ~line_mask;
241d1433e4aSpatrick
242d1433e4aSpatrick // printf("l2inv op %x %08x %08x incr %d %d\n", cache_op, pa, endpa, line_size, len);
243d1433e4aSpatrick while (endpa > pa) {
244d1433e4aSpatrick arml2cc_cache_op(sc, cache_op, pa);
245d1433e4aSpatrick pa += line_size;
246d1433e4aSpatrick }
247d1433e4aSpatrick }
248d1433e4aSpatrick
249d1433e4aSpatrick void
arml2cc_sdcache_wbinv_all(void)250d1433e4aSpatrick arml2cc_sdcache_wbinv_all(void)
251d1433e4aSpatrick {
252d1433e4aSpatrick struct arml2cc_softc *sc = arml2cc_sc;
253d1433e4aSpatrick if (sc == NULL || !sc->sc_enabled)
254d1433e4aSpatrick return;
255d1433e4aSpatrick
256d1433e4aSpatrick #ifdef PL310_ERRATA_727915
257d1433e4aSpatrick platform_smc_write(sc->sc_iot, sc->sc_ioh, L2C_DEBUG_CTL, SMC_L2_DBG, 3);
258d1433e4aSpatrick #endif
259d1433e4aSpatrick
260d1433e4aSpatrick bus_space_write_4(sc->sc_iot, sc->sc_ioh, L2C_CLEAN_INV_WAY, sc->sc_waymask);
261d1433e4aSpatrick while(bus_space_read_4(sc->sc_iot, sc->sc_ioh, L2C_CLEAN_INV_WAY) & sc->sc_waymask);
262d1433e4aSpatrick
263d1433e4aSpatrick #ifdef PL310_ERRATA_727915
264d1433e4aSpatrick platform_smc_write(sc->sc_iot, sc->sc_ioh, L2C_DEBUG_CTL, SMC_L2_DBG, 0);
265d1433e4aSpatrick #endif
266d1433e4aSpatrick
267d1433e4aSpatrick arml2cc_cache_sync(sc);
268d1433e4aSpatrick }
269d1433e4aSpatrick void
arml2cc_sdcache_wbinv_range(vaddr_t va,paddr_t pa,psize_t len)270d1433e4aSpatrick arml2cc_sdcache_wbinv_range(vaddr_t va, paddr_t pa, psize_t len)
271d1433e4aSpatrick {
272d1433e4aSpatrick struct arml2cc_softc *sc = arml2cc_sc;
273d1433e4aSpatrick if (sc == NULL || !sc->sc_enabled)
274d1433e4aSpatrick return;
275d1433e4aSpatrick
276d1433e4aSpatrick #ifdef PL310_ERRATA_727915
277d1433e4aSpatrick platform_smc_write(sc->sc_iot, sc->sc_ioh, L2C_DEBUG_CTL, SMC_L2_DBG, 3);
278d1433e4aSpatrick #endif
279d1433e4aSpatrick
280d1433e4aSpatrick arml2cc_cache_range_op(pa, len, L2C_CLEAN_INV_PA);
281d1433e4aSpatrick arml2cc_cache_sync(sc);
282d1433e4aSpatrick
283d1433e4aSpatrick #ifdef PL310_ERRATA_727915
284d1433e4aSpatrick platform_smc_write(sc->sc_iot, sc->sc_ioh, L2C_DEBUG_CTL, SMC_L2_DBG, 0);
285d1433e4aSpatrick #endif
286d1433e4aSpatrick }
287d1433e4aSpatrick
288d1433e4aSpatrick void
arml2cc_sdcache_inv_range(vaddr_t va,paddr_t pa,psize_t len)289d1433e4aSpatrick arml2cc_sdcache_inv_range(vaddr_t va, paddr_t pa, psize_t len)
290d1433e4aSpatrick {
291d1433e4aSpatrick struct arml2cc_softc *sc = arml2cc_sc;
292d1433e4aSpatrick if (sc == NULL || !sc->sc_enabled)
293d1433e4aSpatrick return;
294d1433e4aSpatrick arml2cc_cache_range_op(pa, len, L2C_INV_PA);
295d1433e4aSpatrick arml2cc_cache_sync(sc);
296d1433e4aSpatrick }
297d1433e4aSpatrick
298d1433e4aSpatrick void
arml2cc_sdcache_wb_range(vaddr_t va,paddr_t pa,psize_t len)299d1433e4aSpatrick arml2cc_sdcache_wb_range(vaddr_t va, paddr_t pa, psize_t len)
300d1433e4aSpatrick {
301d1433e4aSpatrick struct arml2cc_softc *sc = arml2cc_sc;
302d1433e4aSpatrick if (sc == NULL || !sc->sc_enabled)
303d1433e4aSpatrick return;
304d1433e4aSpatrick arml2cc_cache_range_op(pa, len, L2C_CLEAN_PA);
305d1433e4aSpatrick arml2cc_cache_sync(sc);
306d1433e4aSpatrick }
307