blob: 12b030842fdbc1623c99997c60e67d534f168112 [file] [log] [blame]
Bernd Schmidtdbdf20d2009-01-07 23:14:38 +08001/*
2 * File: arch/blackfin/kernel/cplb-nompu-c/cplbmgr.c
3 * Based on: arch/blackfin/kernel/cplb-mpu/cplbmgr.c
4 * Author: Michael McTernan <mmcternan@airvana.com>
5 *
6 * Created: 01Nov2008
7 * Description: CPLB miss handler.
8 *
9 * Modified:
10 * Copyright 2008 Airvana Inc.
11 * Copyright 2004-2007 Analog Devices Inc.
12 *
13 * Bugs: Enter bugs at http://blackfin.uclinux.org/
14 *
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 */
25
26#include <linux/kernel.h>
27#include <asm/blackfin.h>
28#include <asm/cplbinit.h>
29#include <asm/cplb.h>
30#include <asm/mmu_context.h>
Robin Getz16aadcb2009-06-09 01:18:41 +000031#include <asm/traps.h>
Bernd Schmidtdbdf20d2009-01-07 23:14:38 +080032
33/*
34 * WARNING
35 *
36 * This file is compiled with certain -ffixed-reg options. We have to
37 * make sure not to call any functions here that could clobber these
38 * registers.
39 */
40
41int nr_dcplb_miss[NR_CPUS], nr_icplb_miss[NR_CPUS];
42int nr_dcplb_supv_miss[NR_CPUS], nr_icplb_supv_miss[NR_CPUS];
43int nr_cplb_flush[NR_CPUS], nr_dcplb_prot[NR_CPUS];
44
45#ifdef CONFIG_EXCPT_IRQ_SYSC_L1
46#define MGR_ATTR __attribute__((l1_text))
47#else
48#define MGR_ATTR
49#endif
50
51/*
52 * We're in an exception handler. The normal cli nop nop workaround
53 * isn't going to do very much, as the only thing that can interrupt
54 * us is an NMI, and the cli isn't going to stop that.
55 */
56#define NOWA_SSYNC __asm__ __volatile__ ("ssync;")
57
58/* Anomaly handlers provide SSYNCs, so avoid extra if anomaly is present */
59#if ANOMALY_05000125
60
61#define bfin_write_DMEM_CONTROL_SSYNC(v) bfin_write_DMEM_CONTROL(v)
62#define bfin_write_IMEM_CONTROL_SSYNC(v) bfin_write_IMEM_CONTROL(v)
63
64#else
65
66#define bfin_write_DMEM_CONTROL_SSYNC(v) \
67 do { NOWA_SSYNC; bfin_write_DMEM_CONTROL(v); NOWA_SSYNC; } while (0)
68#define bfin_write_IMEM_CONTROL_SSYNC(v) \
69 do { NOWA_SSYNC; bfin_write_IMEM_CONTROL(v); NOWA_SSYNC; } while (0)
70
71#endif
72
73static inline void write_dcplb_data(int cpu, int idx, unsigned long data,
74 unsigned long addr)
75{
76 unsigned long ctrl = bfin_read_DMEM_CONTROL();
77 bfin_write_DMEM_CONTROL_SSYNC(ctrl & ~ENDCPLB);
78 bfin_write32(DCPLB_DATA0 + idx * 4, data);
79 bfin_write32(DCPLB_ADDR0 + idx * 4, addr);
80 bfin_write_DMEM_CONTROL_SSYNC(ctrl);
81
82#ifdef CONFIG_CPLB_INFO
83 dcplb_tbl[cpu][idx].addr = addr;
84 dcplb_tbl[cpu][idx].data = data;
85#endif
86}
87
88static inline void write_icplb_data(int cpu, int idx, unsigned long data,
89 unsigned long addr)
90{
91 unsigned long ctrl = bfin_read_IMEM_CONTROL();
92
93 bfin_write_IMEM_CONTROL_SSYNC(ctrl & ~ENICPLB);
94 bfin_write32(ICPLB_DATA0 + idx * 4, data);
95 bfin_write32(ICPLB_ADDR0 + idx * 4, addr);
96 bfin_write_IMEM_CONTROL_SSYNC(ctrl);
97
98#ifdef CONFIG_CPLB_INFO
99 icplb_tbl[cpu][idx].addr = addr;
100 icplb_tbl[cpu][idx].data = data;
101#endif
102}
103
Bernd Schmidtdbdf20d2009-01-07 23:14:38 +0800104/* Counters to implement round-robin replacement. */
105static int icplb_rr_index[NR_CPUS] PDT_ATTR;
106static int dcplb_rr_index[NR_CPUS] PDT_ATTR;
107
108/*
109 * Find an ICPLB entry to be evicted and return its index.
110 */
111static int evict_one_icplb(int cpu)
112{
113 int i = first_switched_icplb + icplb_rr_index[cpu];
114 if (i >= MAX_CPLBS) {
115 i -= MAX_CPLBS - first_switched_icplb;
116 icplb_rr_index[cpu] -= MAX_CPLBS - first_switched_icplb;
117 }
118 icplb_rr_index[cpu]++;
119 return i;
120}
121
122static int evict_one_dcplb(int cpu)
123{
124 int i = first_switched_dcplb + dcplb_rr_index[cpu];
125 if (i >= MAX_CPLBS) {
126 i -= MAX_CPLBS - first_switched_dcplb;
127 dcplb_rr_index[cpu] -= MAX_CPLBS - first_switched_dcplb;
128 }
129 dcplb_rr_index[cpu]++;
130 return i;
131}
132
133MGR_ATTR static int icplb_miss(int cpu)
134{
135 unsigned long addr = bfin_read_ICPLB_FAULT_ADDR();
136 int status = bfin_read_ICPLB_STATUS();
137 int idx;
138 unsigned long i_data, base, addr1, eaddr;
139
140 nr_icplb_miss[cpu]++;
141 if (unlikely(status & FAULT_USERSUPV))
142 nr_icplb_supv_miss[cpu]++;
143
144 base = 0;
Mike Frysingerd04dfc42009-02-04 16:49:45 +0800145 idx = 0;
146 do {
Bernd Schmidtdbdf20d2009-01-07 23:14:38 +0800147 eaddr = icplb_bounds[idx].eaddr;
148 if (addr < eaddr)
149 break;
150 base = eaddr;
Mike Frysingerd04dfc42009-02-04 16:49:45 +0800151 } while (++idx < icplb_nr_bounds);
152
Bernd Schmidtdbdf20d2009-01-07 23:14:38 +0800153 if (unlikely(idx == icplb_nr_bounds))
154 return CPLB_NO_ADDR_MATCH;
155
156 i_data = icplb_bounds[idx].data;
157 if (unlikely(i_data == 0))
158 return CPLB_NO_ADDR_MATCH;
159
160 addr1 = addr & ~(SIZE_4M - 1);
161 addr &= ~(SIZE_1M - 1);
162 i_data |= PAGE_SIZE_1MB;
163 if (addr1 >= base && (addr1 + SIZE_4M) <= eaddr) {
164 /*
165 * This works because
166 * (PAGE_SIZE_4MB & PAGE_SIZE_1MB) == PAGE_SIZE_1MB.
167 */
168 i_data |= PAGE_SIZE_4MB;
169 addr = addr1;
170 }
171
172 /* Pick entry to evict */
173 idx = evict_one_icplb(cpu);
174
175 write_icplb_data(cpu, idx, i_data, addr);
176
177 return CPLB_RELOADED;
178}
179
180MGR_ATTR static int dcplb_miss(int cpu)
181{
182 unsigned long addr = bfin_read_DCPLB_FAULT_ADDR();
183 int status = bfin_read_DCPLB_STATUS();
184 int idx;
185 unsigned long d_data, base, addr1, eaddr;
186
187 nr_dcplb_miss[cpu]++;
188 if (unlikely(status & FAULT_USERSUPV))
189 nr_dcplb_supv_miss[cpu]++;
190
191 base = 0;
Mike Frysingerd04dfc42009-02-04 16:49:45 +0800192 idx = 0;
193 do {
Bernd Schmidtdbdf20d2009-01-07 23:14:38 +0800194 eaddr = dcplb_bounds[idx].eaddr;
195 if (addr < eaddr)
196 break;
197 base = eaddr;
Mike Frysingerd04dfc42009-02-04 16:49:45 +0800198 } while (++idx < dcplb_nr_bounds);
199
Bernd Schmidtdbdf20d2009-01-07 23:14:38 +0800200 if (unlikely(idx == dcplb_nr_bounds))
201 return CPLB_NO_ADDR_MATCH;
202
203 d_data = dcplb_bounds[idx].data;
204 if (unlikely(d_data == 0))
205 return CPLB_NO_ADDR_MATCH;
206
207 addr1 = addr & ~(SIZE_4M - 1);
208 addr &= ~(SIZE_1M - 1);
209 d_data |= PAGE_SIZE_1MB;
210 if (addr1 >= base && (addr1 + SIZE_4M) <= eaddr) {
211 /*
212 * This works because
213 * (PAGE_SIZE_4MB & PAGE_SIZE_1MB) == PAGE_SIZE_1MB.
214 */
215 d_data |= PAGE_SIZE_4MB;
216 addr = addr1;
217 }
218
219 /* Pick entry to evict */
220 idx = evict_one_dcplb(cpu);
221
222 write_dcplb_data(cpu, idx, d_data, addr);
223
224 return CPLB_RELOADED;
225}
226
Bernd Schmidtdbdf20d2009-01-07 23:14:38 +0800227MGR_ATTR int cplb_hdr(int seqstat, struct pt_regs *regs)
228{
229 int cause = seqstat & 0x3f;
230 unsigned int cpu = smp_processor_id();
231 switch (cause) {
Robin Getz16aadcb2009-06-09 01:18:41 +0000232 case VEC_CPLB_I_M:
Bernd Schmidtdbdf20d2009-01-07 23:14:38 +0800233 return icplb_miss(cpu);
Robin Getz16aadcb2009-06-09 01:18:41 +0000234 case VEC_CPLB_M:
Bernd Schmidtdbdf20d2009-01-07 23:14:38 +0800235 return dcplb_miss(cpu);
236 default:
Bernd Schmidtdbdf20d2009-01-07 23:14:38 +0800237 return CPLB_UNKNOWN_ERR;
238 }
239}