ibm4xx_460ex_l2.c revision 1.1 1 /* $NetBSD: ibm4xx_460ex_l2.c,v 1.1 2026/06/19 18:55:23 rkujawa Exp $ */
2
3 /*
4 * Copyright (c) 2026 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Radoslaw Kujawa.
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 IMPLIED 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 /*
33 * AMCC PPC460EX on-chip 256KB L2 cache support
34 */
35
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: ibm4xx_460ex_l2.c,v 1.1 2026/06/19 18:55:23 rkujawa Exp $");
38
39 #include "opt_ppc4xx.h"
40
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/intr.h>
44
45 #define _POWERPC_BUS_DMA_PRIVATE
46 #include <sys/bus.h>
47
48 #include <powerpc/ibm4xx/cpu.h>
49 #include <powerpc/ibm4xx/amcc460ex.h>
50 #include <powerpc/ibm4xx/ibm4xx_460ex_l2.h>
51
52 #define L2_LINE_SIZE 32
53 #define L2_WAYS 4
54 #define L2_SIZE (256 * 1024)
55 #define L2_INDEX_SPAN (L2_SIZE / L2_WAYS) /* 64KB */
56
57 bool ibm4xx_460ex_l2_enabled = false;
58 uint32_t ibm4xx_460ex_l2_cfg;
59
60 /*
61 * Build the L2C0_ADDR val for an invalidate command
62 */
63 static inline uint32_t
64 ibm4xx_460ex_l2_addr(bus_addr_t pa)
65 {
66 return (uint32_t)pa & 0xfffffff0;
67 }
68
69 /*
70 * Invalidate the L2 lines backing the (offset, offset+len) window
71 */
72 static void
73 ibm4xx_460ex_l2_invalidate(bus_dma_tag_t t, bus_dmamap_t map,
74 bus_addr_t offset, bus_size_t len)
75 {
76 const bus_dma_segment_t *ds = map->dm_segs;
77 int s;
78
79 if (len == 0)
80 return;
81
82 s = splhigh();
83
84 if (len >= L2_INDEX_SPAN) {
85 /* Window sweeps every index: one whole-cache invalidate. */
86 mtdcr(DCR_L2C0_ADDR, 0);
87 mtdcr(DCR_L2C0_CMD, L2C_CMD_HCC);
88 while ((mfdcr(DCR_L2C0_SR) & L2C_SR_CC) == 0)
89 ;
90 __asm volatile ("msync" ::: "memory");
91 splx(s);
92 return;
93 }
94
95 /* Skip leading amount. */
96 while (offset >= ds->ds_len) {
97 offset -= ds->ds_len;
98 ds++;
99 }
100 for (; len > 0; ds++, offset = 0) {
101 bus_size_t seglen = ds->ds_len - offset;
102 bus_addr_t addr = BUS_MEM_TO_PHYS(t, ds->ds_addr) + offset;
103 bus_addr_t lineoff, epa;
104
105 if (seglen > len)
106 seglen = len;
107 len -= seglen;
108 KASSERT(ds < &map->dm_segs[map->dm_nsegs]);
109
110 /* Realign to cache-line boundaries. */
111 lineoff = addr & (L2_LINE_SIZE - 1);
112 seglen += lineoff;
113 addr -= lineoff;
114
115 for (epa = addr + seglen; addr < epa; addr += L2_LINE_SIZE) {
116 mtdcr(DCR_L2C0_ADDR, ibm4xx_460ex_l2_addr(addr));
117 mtdcr(DCR_L2C0_CMD, L2C_CMD_INV);
118 while ((mfdcr(DCR_L2C0_SR) & L2C_SR_CC) == 0)
119 ;
120 }
121 }
122 __asm volatile ("msync" ::: "memory");
123 splx(s);
124 }
125
126 /*
127 * Private bus_dma sync for the USB controllers.
128 */
129 static void
130 ibm4xx_460ex_l2_dmamap_sync(bus_dma_tag_t t, bus_dmamap_t map,
131 bus_addr_t offset, bus_size_t len, int ops)
132 {
133 if (ibm4xx_460ex_l2_enabled && (ops & BUS_DMASYNC_POSTREAD) != 0)
134 ibm4xx_460ex_l2_invalidate(t, map, offset, len);
135 _bus_dmamap_sync(t, map, offset, len, ops);
136 }
137
138 /*
139 * A clone of ibm4xx_default_bus_dma_tag sync overriden
140 */
141 struct powerpc_bus_dma_tag ibm4xx_460ex_l2_bus_dma_tag = {
142 0, 0,
143 _bus_dmamap_create,
144 _bus_dmamap_destroy,
145 _bus_dmamap_load,
146 _bus_dmamap_load_mbuf,
147 _bus_dmamap_load_uio,
148 _bus_dmamap_load_raw,
149 _bus_dmamap_unload,
150 ibm4xx_460ex_l2_dmamap_sync,
151 _bus_dmamem_alloc,
152 _bus_dmamem_free,
153 _bus_dmamem_map,
154 _bus_dmamem_unmap,
155 _bus_dmamem_mmap,
156 _bus_dma_phys_to_bus_mem_generic,
157 _bus_dma_bus_mem_to_phys_generic,
158 };
159
160 bus_dma_tag_t
161 ibm4xx_460ex_l2_dmatag(void)
162 {
163 return &ibm4xx_460ex_l2_bus_dma_tag;
164 }
165
166 void
167 ibm4xx_460ex_l2cache_enable(u_int memsize)
168 {
169 uint32_t snpsz;
170 u_int code;
171
172 /* Hand the SRAM0 data arrays back from the SRAM controller. */
173 mtdcr(DCR_SRAM0_SB0CR, 0);
174 mtdcr(DCR_SRAM0_SB1CR, 0);
175 mtdcr(DCR_SRAM0_SB2CR, 0);
176 mtdcr(DCR_SRAM0_SB3CR, 0);
177
178 /* RDBW is required; switch the array into L2 mode. */
179 mtdcr(DCR_L2C0_CFG, L2C_CFG_RDBW | L2C_CFG_L2M | L2C_CFG_SS_256KB);
180
181 /* Reset the tag array with the hardware clear command. */
182 mtdcr(DCR_L2C0_ADDR, 0);
183 mtdcr(DCR_L2C0_CMD, L2C_CMD_HCC);
184 while ((mfdcr(DCR_L2C0_SR) & L2C_SR_CC) == 0)
185 ;
186 /* Clear any latched cache- and tag-parity errors. */
187 mtdcr(DCR_L2C0_CMD, L2C_CMD_CCP);
188 while ((mfdcr(DCR_L2C0_SR) & L2C_SR_CC) == 0)
189 ;
190 mtdcr(DCR_L2C0_CMD, L2C_CMD_CTE);
191 while ((mfdcr(DCR_L2C0_SR) & L2C_SR_CC) == 0)
192 ;
193 __asm volatile ("msync" ::: "memory");
194
195 /*
196 * Program snoop region 0 to cover all of DRAM on the LL segment!
197 */
198 snpsz = 0x100000; /* 1MB, the smallest snoop region */
199 code = 0;
200 while (snpsz < memsize) {
201 snpsz <<= 1;
202 code++;
203 }
204 mtdcr(DCR_L2C0_SNP0, (code << L2C_SNP_SSR_SHIFT) | L2C_SNP_ESR);
205 mtdcr(DCR_L2C0_SNP1, 0);
206
207 /* Enable instruction- and data-side L2 caching. */
208 mtdcr(DCR_L2C0_CFG, L2C_CFG_RDBW | L2C_CFG_L2M | L2C_CFG_SS_256KB |
209 L2C_CFG_FRAN | L2C_CFG_SNPCI | L2C_CFG_ICU | L2C_CFG_DCU);
210 __asm volatile ("msync" ::: "memory");
211
212 /* Read back so the caller can confirm what actually stuck. */
213 ibm4xx_460ex_l2_cfg = mfdcr(DCR_L2C0_CFG);
214 if (ibm4xx_460ex_l2_cfg & L2C_CFG_L2M)
215 ibm4xx_460ex_l2_enabled = true;
216 }
217
218