Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/mips/sibyte/sb1250/bus_watcher.c
15118 views
1
/*
2
* Copyright (C) 2002,2003 Broadcom Corporation
3
*
4
* This program is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU General Public License
6
* as published by the Free Software Foundation; either version 2
7
* of the License, or (at your option) any later version.
8
*
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more details.
13
*
14
* You should have received a copy of the GNU General Public License
15
* along with this program; if not, write to the Free Software
16
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
*/
18
19
/*
20
* The Bus Watcher monitors internal bus transactions and maintains
21
* counts of transactions with error status, logging details and
22
* causing one of several interrupts. This driver provides a handler
23
* for those interrupts which aggregates the counts (to avoid
24
* saturating the 8-bit counters) and provides a presence in
25
* /proc/bus_watcher if PROC_FS is on.
26
*/
27
28
#include <linux/init.h>
29
#include <linux/kernel.h>
30
#include <linux/interrupt.h>
31
#include <linux/sched.h>
32
#include <linux/proc_fs.h>
33
#include <asm/system.h>
34
#include <asm/io.h>
35
36
#include <asm/sibyte/sb1250.h>
37
#include <asm/sibyte/sb1250_regs.h>
38
#include <asm/sibyte/sb1250_int.h>
39
#include <asm/sibyte/sb1250_scd.h>
40
41
42
struct bw_stats_struct {
43
uint64_t status;
44
uint32_t l2_err;
45
uint32_t memio_err;
46
int status_printed;
47
unsigned long l2_cor_d;
48
unsigned long l2_bad_d;
49
unsigned long l2_cor_t;
50
unsigned long l2_bad_t;
51
unsigned long mem_cor_d;
52
unsigned long mem_bad_d;
53
unsigned long bus_error;
54
} bw_stats;
55
56
57
static void print_summary(uint32_t status, uint32_t l2_err,
58
uint32_t memio_err)
59
{
60
printk("Bus watcher error counters: %08x %08x\n", l2_err, memio_err);
61
printk("\nLast recorded signature:\n");
62
printk("Request %02x from %d, answered by %d with Dcode %d\n",
63
(unsigned int)(G_SCD_BERR_TID(status) & 0x3f),
64
(int)(G_SCD_BERR_TID(status) >> 6),
65
(int)G_SCD_BERR_RID(status),
66
(int)G_SCD_BERR_DCODE(status));
67
}
68
69
/*
70
* check_bus_watcher is exported for use in situations where we want
71
* to see the most recent status of the bus watcher, which might have
72
* already been destructively read out of the registers.
73
*
74
* notes: this is currently used by the cache error handler
75
* should provide locking against the interrupt handler
76
*/
77
void check_bus_watcher(void)
78
{
79
u32 status, l2_err, memio_err;
80
81
#ifdef CONFIG_SB1_PASS_1_WORKAROUNDS
82
/* Destructive read, clears register and interrupt */
83
status = csr_in32(IOADDR(A_SCD_BUS_ERR_STATUS));
84
#else
85
/* Use non-destructive register */
86
status = csr_in32(IOADDR(A_SCD_BUS_ERR_STATUS_DEBUG));
87
#endif
88
if (!(status & 0x7fffffff)) {
89
printk("Using last values reaped by bus watcher driver\n");
90
status = bw_stats.status;
91
l2_err = bw_stats.l2_err;
92
memio_err = bw_stats.memio_err;
93
} else {
94
l2_err = csr_in32(IOADDR(A_BUS_L2_ERRORS));
95
memio_err = csr_in32(IOADDR(A_BUS_MEM_IO_ERRORS));
96
}
97
if (status & ~(1UL << 31))
98
print_summary(status, l2_err, memio_err);
99
else
100
printk("Bus watcher indicates no error\n");
101
}
102
103
static int bw_print_buffer(char *page, struct bw_stats_struct *stats)
104
{
105
int len;
106
107
len = sprintf(page, "SiByte Bus Watcher statistics\n");
108
len += sprintf(page+len, "-----------------------------\n");
109
len += sprintf(page+len, "L2-d-cor %8ld\nL2-d-bad %8ld\n",
110
stats->l2_cor_d, stats->l2_bad_d);
111
len += sprintf(page+len, "L2-t-cor %8ld\nL2-t-bad %8ld\n",
112
stats->l2_cor_t, stats->l2_bad_t);
113
len += sprintf(page+len, "MC-d-cor %8ld\nMC-d-bad %8ld\n",
114
stats->mem_cor_d, stats->mem_bad_d);
115
len += sprintf(page+len, "IO-err %8ld\n", stats->bus_error);
116
len += sprintf(page+len, "\nLast recorded signature:\n");
117
len += sprintf(page+len, "Request %02x from %d, answered by %d with Dcode %d\n",
118
(unsigned int)(G_SCD_BERR_TID(stats->status) & 0x3f),
119
(int)(G_SCD_BERR_TID(stats->status) >> 6),
120
(int)G_SCD_BERR_RID(stats->status),
121
(int)G_SCD_BERR_DCODE(stats->status));
122
/* XXXKW indicate multiple errors between printings, or stats
123
collection (or both)? */
124
if (stats->status & M_SCD_BERR_MULTERRS)
125
len += sprintf(page+len, "Multiple errors observed since last check.\n");
126
if (stats->status_printed) {
127
len += sprintf(page+len, "(no change since last printing)\n");
128
} else {
129
stats->status_printed = 1;
130
}
131
132
return len;
133
}
134
135
#ifdef CONFIG_PROC_FS
136
137
/* For simplicity, I want to assume a single read is required each
138
time */
139
static int bw_read_proc(char *page, char **start, off_t off,
140
int count, int *eof, void *data)
141
{
142
int len;
143
144
if (off == 0) {
145
len = bw_print_buffer(page, data);
146
*start = page;
147
} else {
148
len = 0;
149
*eof = 1;
150
}
151
return len;
152
}
153
154
static void create_proc_decoder(struct bw_stats_struct *stats)
155
{
156
struct proc_dir_entry *ent;
157
158
ent = create_proc_read_entry("bus_watcher", S_IWUSR | S_IRUGO, NULL,
159
bw_read_proc, stats);
160
if (!ent) {
161
printk(KERN_INFO "Unable to initialize bus_watcher /proc entry\n");
162
return;
163
}
164
}
165
166
#endif /* CONFIG_PROC_FS */
167
168
/*
169
* sibyte_bw_int - handle bus watcher interrupts and accumulate counts
170
*
171
* notes: possible re-entry due to multiple sources
172
* should check/indicate saturation
173
*/
174
static irqreturn_t sibyte_bw_int(int irq, void *data)
175
{
176
struct bw_stats_struct *stats = data;
177
unsigned long cntr;
178
#ifdef CONFIG_SIBYTE_BW_TRACE
179
int i;
180
#endif
181
#ifndef CONFIG_PROC_FS
182
char bw_buf[1024];
183
#endif
184
185
#ifdef CONFIG_SIBYTE_BW_TRACE
186
csr_out32(M_SCD_TRACE_CFG_FREEZE, IOADDR(A_SCD_TRACE_CFG));
187
csr_out32(M_SCD_TRACE_CFG_START_READ, IOADDR(A_SCD_TRACE_CFG));
188
189
for (i=0; i<256*6; i++)
190
printk("%016llx\n",
191
(long long)__raw_readq(IOADDR(A_SCD_TRACE_READ)));
192
193
csr_out32(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG));
194
csr_out32(M_SCD_TRACE_CFG_START, IOADDR(A_SCD_TRACE_CFG));
195
#endif
196
197
/* Destructive read, clears register and interrupt */
198
stats->status = csr_in32(IOADDR(A_SCD_BUS_ERR_STATUS));
199
stats->status_printed = 0;
200
201
stats->l2_err = cntr = csr_in32(IOADDR(A_BUS_L2_ERRORS));
202
stats->l2_cor_d += G_SCD_L2ECC_CORR_D(cntr);
203
stats->l2_bad_d += G_SCD_L2ECC_BAD_D(cntr);
204
stats->l2_cor_t += G_SCD_L2ECC_CORR_T(cntr);
205
stats->l2_bad_t += G_SCD_L2ECC_BAD_T(cntr);
206
csr_out32(0, IOADDR(A_BUS_L2_ERRORS));
207
208
stats->memio_err = cntr = csr_in32(IOADDR(A_BUS_MEM_IO_ERRORS));
209
stats->mem_cor_d += G_SCD_MEM_ECC_CORR(cntr);
210
stats->mem_bad_d += G_SCD_MEM_ECC_BAD(cntr);
211
stats->bus_error += G_SCD_MEM_BUSERR(cntr);
212
csr_out32(0, IOADDR(A_BUS_MEM_IO_ERRORS));
213
214
#ifndef CONFIG_PROC_FS
215
bw_print_buffer(bw_buf, stats);
216
printk(bw_buf);
217
#endif
218
219
return IRQ_HANDLED;
220
}
221
222
int __init sibyte_bus_watcher(void)
223
{
224
memset(&bw_stats, 0, sizeof(struct bw_stats_struct));
225
bw_stats.status_printed = 1;
226
227
if (request_irq(K_INT_BAD_ECC, sibyte_bw_int, 0, "Bus watcher", &bw_stats)) {
228
printk("Failed to register bus watcher BAD_ECC irq\n");
229
return -1;
230
}
231
if (request_irq(K_INT_COR_ECC, sibyte_bw_int, 0, "Bus watcher", &bw_stats)) {
232
free_irq(K_INT_BAD_ECC, &bw_stats);
233
printk("Failed to register bus watcher COR_ECC irq\n");
234
return -1;
235
}
236
if (request_irq(K_INT_IO_BUS, sibyte_bw_int, 0, "Bus watcher", &bw_stats)) {
237
free_irq(K_INT_BAD_ECC, &bw_stats);
238
free_irq(K_INT_COR_ECC, &bw_stats);
239
printk("Failed to register bus watcher IO_BUS irq\n");
240
return -1;
241
}
242
243
#ifdef CONFIG_PROC_FS
244
create_proc_decoder(&bw_stats);
245
#endif
246
247
#ifdef CONFIG_SIBYTE_BW_TRACE
248
csr_out32((M_SCD_TRSEQ_ASAMPLE | M_SCD_TRSEQ_DSAMPLE |
249
K_SCD_TRSEQ_TRIGGER_ALL),
250
IOADDR(A_SCD_TRACE_SEQUENCE_0));
251
csr_out32(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG));
252
csr_out32(M_SCD_TRACE_CFG_START, IOADDR(A_SCD_TRACE_CFG));
253
#endif
254
255
return 0;
256
}
257
258
__initcall(sibyte_bus_watcher);
259
260