Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/arm/mach-ixp2000/pci.c
10817 views
1
/*
2
* arch/arm/mach-ixp2000/pci.c
3
*
4
* PCI routines for IXDP2400/IXDP2800 boards
5
*
6
* Original Author: Naeem Afzal <[email protected]>
7
* Maintained by: Deepak Saxena <[email protected]>
8
*
9
* Copyright 2002 Intel Corp.
10
* Copyright (C) 2003-2004 MontaVista Software, Inc.
11
*
12
* This program is free software; you can redistribute it and/or modify it
13
* under the terms of the GNU General Public License as published by the
14
* Free Software Foundation; either version 2 of the License, or (at your
15
* option) any later version.
16
*/
17
18
#include <linux/sched.h>
19
#include <linux/kernel.h>
20
#include <linux/pci.h>
21
#include <linux/interrupt.h>
22
#include <linux/mm.h>
23
#include <linux/init.h>
24
#include <linux/ioport.h>
25
#include <linux/delay.h>
26
#include <linux/io.h>
27
28
#include <asm/irq.h>
29
#include <asm/system.h>
30
#include <mach/hardware.h>
31
32
#include <asm/mach/pci.h>
33
34
static volatile int pci_master_aborts = 0;
35
36
static int clear_master_aborts(void);
37
38
u32 *
39
ixp2000_pci_config_addr(unsigned int bus_nr, unsigned int devfn, int where)
40
{
41
u32 *paddress;
42
43
if (PCI_SLOT(devfn) > 7)
44
return 0;
45
46
/* Must be dword aligned */
47
where &= ~3;
48
49
/*
50
* For top bus, generate type 0, else type 1
51
*/
52
if (!bus_nr) {
53
/* only bits[23:16] are used for IDSEL */
54
paddress = (u32 *) (IXP2000_PCI_CFG0_VIRT_BASE
55
| (1 << (PCI_SLOT(devfn) + 16))
56
| (PCI_FUNC(devfn) << 8) | where);
57
} else {
58
paddress = (u32 *) (IXP2000_PCI_CFG1_VIRT_BASE
59
| (bus_nr << 16)
60
| (PCI_SLOT(devfn) << 11)
61
| (PCI_FUNC(devfn) << 8) | where);
62
}
63
64
return paddress;
65
}
66
67
/*
68
* Mask table, bits to mask for quantity of size 1, 2 or 4 bytes.
69
* 0 and 3 are not valid indexes...
70
*/
71
static u32 bytemask[] = {
72
/*0*/ 0,
73
/*1*/ 0xff,
74
/*2*/ 0xffff,
75
/*3*/ 0,
76
/*4*/ 0xffffffff,
77
};
78
79
80
int ixp2000_pci_read_config(struct pci_bus *bus, unsigned int devfn, int where,
81
int size, u32 *value)
82
{
83
u32 n;
84
u32 *addr;
85
86
n = where % 4;
87
88
addr = ixp2000_pci_config_addr(bus->number, devfn, where);
89
if (!addr)
90
return PCIBIOS_DEVICE_NOT_FOUND;
91
92
pci_master_aborts = 0;
93
*value = (*addr >> (8*n)) & bytemask[size];
94
if (pci_master_aborts) {
95
pci_master_aborts = 0;
96
*value = 0xffffffff;
97
return PCIBIOS_DEVICE_NOT_FOUND;
98
}
99
100
return PCIBIOS_SUCCESSFUL;
101
}
102
103
/*
104
* We don't do error checks by calling clear_master_aborts() b/c the
105
* assumption is that the caller did a read first to make sure a device
106
* exists.
107
*/
108
int ixp2000_pci_write_config(struct pci_bus *bus, unsigned int devfn, int where,
109
int size, u32 value)
110
{
111
u32 mask;
112
u32 *addr;
113
u32 temp;
114
115
mask = ~(bytemask[size] << ((where % 0x4) * 8));
116
addr = ixp2000_pci_config_addr(bus->number, devfn, where);
117
if (!addr)
118
return PCIBIOS_DEVICE_NOT_FOUND;
119
temp = (u32) (value) << ((where % 0x4) * 8);
120
*addr = (*addr & mask) | temp;
121
122
clear_master_aborts();
123
124
return PCIBIOS_SUCCESSFUL;
125
}
126
127
128
static struct pci_ops ixp2000_pci_ops = {
129
.read = ixp2000_pci_read_config,
130
.write = ixp2000_pci_write_config
131
};
132
133
struct pci_bus *ixp2000_pci_scan_bus(int nr, struct pci_sys_data *sysdata)
134
{
135
return pci_scan_bus(sysdata->busnr, &ixp2000_pci_ops, sysdata);
136
}
137
138
139
int ixp2000_pci_abort_handler(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
140
{
141
142
volatile u32 temp;
143
unsigned long flags;
144
145
pci_master_aborts = 1;
146
147
local_irq_save(flags);
148
temp = *(IXP2000_PCI_CONTROL);
149
if (temp & ((1 << 8) | (1 << 5))) {
150
ixp2000_reg_wrb(IXP2000_PCI_CONTROL, temp);
151
}
152
153
temp = *(IXP2000_PCI_CMDSTAT);
154
if (temp & (1 << 29)) {
155
while (temp & (1 << 29)) {
156
ixp2000_reg_write(IXP2000_PCI_CMDSTAT, temp);
157
temp = *(IXP2000_PCI_CMDSTAT);
158
}
159
}
160
local_irq_restore(flags);
161
162
/*
163
* If it was an imprecise abort, then we need to correct the
164
* return address to be _after_ the instruction.
165
*/
166
if (fsr & (1 << 10))
167
regs->ARM_pc += 4;
168
169
return 0;
170
}
171
172
int
173
clear_master_aborts(void)
174
{
175
volatile u32 temp;
176
unsigned long flags;
177
178
local_irq_save(flags);
179
temp = *(IXP2000_PCI_CONTROL);
180
if (temp & ((1 << 8) | (1 << 5))) {
181
ixp2000_reg_wrb(IXP2000_PCI_CONTROL, temp);
182
}
183
184
temp = *(IXP2000_PCI_CMDSTAT);
185
if (temp & (1 << 29)) {
186
while (temp & (1 << 29)) {
187
ixp2000_reg_write(IXP2000_PCI_CMDSTAT, temp);
188
temp = *(IXP2000_PCI_CMDSTAT);
189
}
190
}
191
local_irq_restore(flags);
192
193
return 0;
194
}
195
196
void __init
197
ixp2000_pci_preinit(void)
198
{
199
#ifndef CONFIG_IXP2000_SUPPORT_BROKEN_PCI_IO
200
/*
201
* Configure the PCI unit to properly byteswap I/O transactions,
202
* and verify that it worked.
203
*/
204
ixp2000_reg_write(IXP2000_PCI_CONTROL,
205
(*IXP2000_PCI_CONTROL | PCI_CONTROL_IEE));
206
207
if ((*IXP2000_PCI_CONTROL & PCI_CONTROL_IEE) == 0)
208
panic("IXP2000: PCI I/O is broken on this ixp model, and "
209
"the needed workaround has not been configured in");
210
#endif
211
212
hook_fault_code(16+6, ixp2000_pci_abort_handler, SIGBUS, 0,
213
"PCI config cycle to non-existent device");
214
}
215
216
217
/*
218
* IXP2000 systems often have large resource requirements, so we just
219
* use our own resource space.
220
*/
221
static struct resource ixp2000_pci_mem_space = {
222
.start = 0xe0000000,
223
.end = 0xffffffff,
224
.flags = IORESOURCE_MEM,
225
.name = "PCI Mem Space"
226
};
227
228
static struct resource ixp2000_pci_io_space = {
229
.start = 0x00010000,
230
.end = 0x0001ffff,
231
.flags = IORESOURCE_IO,
232
.name = "PCI I/O Space"
233
};
234
235
int ixp2000_pci_setup(int nr, struct pci_sys_data *sys)
236
{
237
if (nr >= 1)
238
return 0;
239
240
sys->resource[0] = &ixp2000_pci_io_space;
241
sys->resource[1] = &ixp2000_pci_mem_space;
242
sys->resource[2] = NULL;
243
244
return 1;
245
}
246
247
248