Linux Kernel
3.7.1
Main Page
Related Pages
Modules
Namespaces
Data Structures
Files
File List
Globals
All
Data Structures
Namespaces
Files
Functions
Variables
Typedefs
Enumerations
Enumerator
Macros
Groups
Pages
arch
mips
sibyte
sb1250
bus_watcher.c
Go to the documentation of this file.
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/io.h>
34
35
#include <
asm/sibyte/sb1250.h
>
36
#include <
asm/sibyte/sb1250_regs.h
>
37
#include <
asm/sibyte/sb1250_int.h
>
38
#include <
asm/sibyte/sb1250_scd.h
>
39
40
41
struct
bw_stats_struct
{
42
uint64_t
status
;
43
uint32_t
l2_err
;
44
uint32_t
memio_err
;
45
int
status_printed
;
46
unsigned
long
l2_cor_d
;
47
unsigned
long
l2_bad_d
;
48
unsigned
long
l2_cor_t
;
49
unsigned
long
l2_bad_t
;
50
unsigned
long
mem_cor_d
;
51
unsigned
long
mem_bad_d
;
52
unsigned
long
bus_error
;
53
}
bw_stats
;
54
55
56
static
void
print_summary(
uint32_t
status
,
uint32_t
l2_err,
57
uint32_t
memio_err)
58
{
59
printk
(
"Bus watcher error counters: %08x %08x\n"
, l2_err, memio_err);
60
printk
(
"\nLast recorded signature:\n"
);
61
printk
(
"Request %02x from %d, answered by %d with Dcode %d\n"
,
62
(
unsigned
int
)(
G_SCD_BERR_TID
(status) & 0x3f),
63
(
int
)(
G_SCD_BERR_TID
(status) >> 6),
64
(
int
)
G_SCD_BERR_RID
(status),
65
(
int
)
G_SCD_BERR_DCODE
(status));
66
}
67
68
/*
69
* check_bus_watcher is exported for use in situations where we want
70
* to see the most recent status of the bus watcher, which might have
71
* already been destructively read out of the registers.
72
*
73
* notes: this is currently used by the cache error handler
74
* should provide locking against the interrupt handler
75
*/
76
void
check_bus_watcher
(
void
)
77
{
78
u32
status
, l2_err, memio_err;
79
80
#ifdef CONFIG_SB1_PASS_1_WORKAROUNDS
81
/* Destructive read, clears register and interrupt */
82
status =
csr_in32
(
IOADDR
(
A_SCD_BUS_ERR_STATUS
));
83
#else
84
/* Use non-destructive register */
85
status =
csr_in32
(
IOADDR
(A_SCD_BUS_ERR_STATUS_DEBUG));
86
#endif
87
if
(!(status & 0x7fffffff)) {
88
printk
(
"Using last values reaped by bus watcher driver\n"
);
89
status =
bw_stats
.status;
90
l2_err =
bw_stats
.l2_err;
91
memio_err =
bw_stats
.memio_err;
92
}
else
{
93
l2_err =
csr_in32
(
IOADDR
(
A_BUS_L2_ERRORS
));
94
memio_err =
csr_in32
(
IOADDR
(
A_BUS_MEM_IO_ERRORS
));
95
}
96
if
(status & ~(1
UL
<< 31))
97
print_summary(status, l2_err, memio_err);
98
else
99
printk
(
"Bus watcher indicates no error\n"
);
100
}
101
102
static
int
bw_print_buffer(
char
*
page
,
struct
bw_stats_struct
*
stats
)
103
{
104
int
len
;
105
106
len =
sprintf
(page,
"SiByte Bus Watcher statistics\n"
);
107
len +=
sprintf
(page+len,
"-----------------------------\n"
);
108
len +=
sprintf
(page+len,
"L2-d-cor %8ld\nL2-d-bad %8ld\n"
,
109
stats->
l2_cor_d
, stats->
l2_bad_d
);
110
len +=
sprintf
(page+len,
"L2-t-cor %8ld\nL2-t-bad %8ld\n"
,
111
stats->
l2_cor_t
, stats->
l2_bad_t
);
112
len +=
sprintf
(page+len,
"MC-d-cor %8ld\nMC-d-bad %8ld\n"
,
113
stats->
mem_cor_d
, stats->
mem_bad_d
);
114
len +=
sprintf
(page+len,
"IO-err %8ld\n"
, stats->
bus_error
);
115
len +=
sprintf
(page+len,
"\nLast recorded signature:\n"
);
116
len +=
sprintf
(page+len,
"Request %02x from %d, answered by %d with Dcode %d\n"
,
117
(
unsigned
int
)(
G_SCD_BERR_TID
(stats->
status
) & 0x3f),
118
(
int
)(
G_SCD_BERR_TID
(stats->
status
) >> 6),
119
(
int
)
G_SCD_BERR_RID
(stats->
status
),
120
(
int
)
G_SCD_BERR_DCODE
(stats->
status
));
121
/* XXXKW indicate multiple errors between printings, or stats
122
collection (or both)? */
123
if
(stats->
status
&
M_SCD_BERR_MULTERRS
)
124
len +=
sprintf
(page+len,
"Multiple errors observed since last check.\n"
);
125
if
(stats->
status_printed
) {
126
len +=
sprintf
(page+len,
"(no change since last printing)\n"
);
127
}
else
{
128
stats->
status_printed
= 1;
129
}
130
131
return
len;
132
}
133
134
#ifdef CONFIG_PROC_FS
135
136
/* For simplicity, I want to assume a single read is required each
137
time */
138
static
int
bw_read_proc(
char
*page,
char
**
start
,
off_t
off,
139
int
count
,
int
*eof,
void
*
data
)
140
{
141
int
len;
142
143
if
(off == 0) {
144
len = bw_print_buffer(page, data);
145
*start =
page
;
146
}
else
{
147
len = 0;
148
*eof = 1;
149
}
150
return
len;
151
}
152
153
static
void
create_proc_decoder(
struct
bw_stats_struct
*stats)
154
{
155
struct
proc_dir_entry
*
ent
;
156
157
ent = create_proc_read_entry(
"bus_watcher"
,
S_IWUSR
|
S_IRUGO
,
NULL
,
158
bw_read_proc, stats);
159
if
(!ent) {
160
printk
(
KERN_INFO
"Unable to initialize bus_watcher /proc entry\n"
);
161
return
;
162
}
163
}
164
165
#endif
/* CONFIG_PROC_FS */
166
167
/*
168
* sibyte_bw_int - handle bus watcher interrupts and accumulate counts
169
*
170
* notes: possible re-entry due to multiple sources
171
* should check/indicate saturation
172
*/
173
static
irqreturn_t
sibyte_bw_int(
int
irq,
void
*data)
174
{
175
struct
bw_stats_struct
*stats =
data
;
176
unsigned
long
cntr;
177
#ifdef CONFIG_SIBYTE_BW_TRACE
178
int
i
;
179
#endif
180
#ifndef CONFIG_PROC_FS
181
char
bw_buf[1024];
182
#endif
183
184
#ifdef CONFIG_SIBYTE_BW_TRACE
185
csr_out32
(
M_SCD_TRACE_CFG_FREEZE
,
IOADDR
(
A_SCD_TRACE_CFG
));
186
csr_out32
(
M_SCD_TRACE_CFG_START_READ
,
IOADDR
(
A_SCD_TRACE_CFG
));
187
188
for
(i=0; i<256*6; i++)
189
printk
(
"%016llx\n"
,
190
(
long
long
)
__raw_readq
(
IOADDR
(
A_SCD_TRACE_READ
)));
191
192
csr_out32
(
M_SCD_TRACE_CFG_RESET
,
IOADDR
(
A_SCD_TRACE_CFG
));
193
csr_out32
(
M_SCD_TRACE_CFG_START
,
IOADDR
(
A_SCD_TRACE_CFG
));
194
#endif
195
196
/* Destructive read, clears register and interrupt */
197
stats->
status
=
csr_in32
(
IOADDR
(
A_SCD_BUS_ERR_STATUS
));
198
stats->
status_printed
= 0;
199
200
stats->
l2_err
= cntr =
csr_in32
(
IOADDR
(
A_BUS_L2_ERRORS
));
201
stats->
l2_cor_d
+=
G_SCD_L2ECC_CORR_D
(cntr);
202
stats->
l2_bad_d
+=
G_SCD_L2ECC_BAD_D
(cntr);
203
stats->
l2_cor_t
+=
G_SCD_L2ECC_CORR_T
(cntr);
204
stats->
l2_bad_t
+=
G_SCD_L2ECC_BAD_T
(cntr);
205
csr_out32
(0,
IOADDR
(
A_BUS_L2_ERRORS
));
206
207
stats->
memio_err
= cntr =
csr_in32
(
IOADDR
(
A_BUS_MEM_IO_ERRORS
));
208
stats->
mem_cor_d
+=
G_SCD_MEM_ECC_CORR
(cntr);
209
stats->
mem_bad_d
+=
G_SCD_MEM_ECC_BAD
(cntr);
210
stats->
bus_error
+=
G_SCD_MEM_BUSERR
(cntr);
211
csr_out32
(0,
IOADDR
(
A_BUS_MEM_IO_ERRORS
));
212
213
#ifndef CONFIG_PROC_FS
214
bw_print_buffer(bw_buf, stats);
215
printk
(bw_buf);
216
#endif
217
218
return
IRQ_HANDLED
;
219
}
220
221
int
__init
sibyte_bus_watcher
(
void
)
222
{
223
memset
(&
bw_stats
, 0,
sizeof
(
struct
bw_stats_struct
));
224
bw_stats
.status_printed = 1;
225
226
if
(
request_irq
(
K_INT_BAD_ECC
, sibyte_bw_int, 0,
"Bus watcher"
, &
bw_stats
)) {
227
printk
(
"Failed to register bus watcher BAD_ECC irq\n"
);
228
return
-1;
229
}
230
if
(
request_irq
(
K_INT_COR_ECC
, sibyte_bw_int, 0,
"Bus watcher"
, &
bw_stats
)) {
231
free_irq
(
K_INT_BAD_ECC
, &
bw_stats
);
232
printk
(
"Failed to register bus watcher COR_ECC irq\n"
);
233
return
-1;
234
}
235
if
(
request_irq
(
K_INT_IO_BUS
, sibyte_bw_int, 0,
"Bus watcher"
, &
bw_stats
)) {
236
free_irq
(
K_INT_BAD_ECC
, &
bw_stats
);
237
free_irq
(
K_INT_COR_ECC
, &
bw_stats
);
238
printk
(
"Failed to register bus watcher IO_BUS irq\n"
);
239
return
-1;
240
}
241
242
#ifdef CONFIG_PROC_FS
243
create_proc_decoder(&
bw_stats
);
244
#endif
245
246
#ifdef CONFIG_SIBYTE_BW_TRACE
247
csr_out32
((
M_SCD_TRSEQ_ASAMPLE
|
M_SCD_TRSEQ_DSAMPLE
|
248
K_SCD_TRSEQ_TRIGGER_ALL
),
249
IOADDR
(
A_SCD_TRACE_SEQUENCE_0
));
250
csr_out32
(
M_SCD_TRACE_CFG_RESET
,
IOADDR
(
A_SCD_TRACE_CFG
));
251
csr_out32
(
M_SCD_TRACE_CFG_START
,
IOADDR
(
A_SCD_TRACE_CFG
));
252
#endif
253
254
return
0;
255
}
256
257
__initcall
(
sibyte_bus_watcher
);
Generated on Thu Jan 10 2013 13:12:18 for Linux Kernel by
1.8.2