Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/powerpc/platforms/cell/celleb_scc_epci.c
10818 views
1
/*
2
* Support for SCC external PCI
3
*
4
* (C) Copyright 2004-2007 TOSHIBA CORPORATION
5
*
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
10
*
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
15
*
16
* You should have received a copy of the GNU General Public License along
17
* with this program; if not, write to the Free Software Foundation, Inc.,
18
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
*/
20
21
#undef DEBUG
22
23
#include <linux/kernel.h>
24
#include <linux/threads.h>
25
#include <linux/pci.h>
26
#include <linux/init.h>
27
#include <linux/pci_regs.h>
28
#include <linux/bootmem.h>
29
30
#include <asm/io.h>
31
#include <asm/irq.h>
32
#include <asm/prom.h>
33
#include <asm/pci-bridge.h>
34
#include <asm/ppc-pci.h>
35
36
#include "celleb_scc.h"
37
#include "celleb_pci.h"
38
39
#define MAX_PCI_DEVICES 32
40
#define MAX_PCI_FUNCTIONS 8
41
42
#define iob() __asm__ __volatile__("eieio; sync":::"memory")
43
44
static inline PCI_IO_ADDR celleb_epci_get_epci_base(
45
struct pci_controller *hose)
46
{
47
/*
48
* Note:
49
* Celleb epci uses cfg_addr as a base address for
50
* epci control registers.
51
*/
52
53
return hose->cfg_addr;
54
}
55
56
static inline PCI_IO_ADDR celleb_epci_get_epci_cfg(
57
struct pci_controller *hose)
58
{
59
/*
60
* Note:
61
* Celleb epci uses cfg_data as a base address for
62
* configuration area for epci devices.
63
*/
64
65
return hose->cfg_data;
66
}
67
68
static inline void clear_and_disable_master_abort_interrupt(
69
struct pci_controller *hose)
70
{
71
PCI_IO_ADDR epci_base;
72
PCI_IO_ADDR reg;
73
epci_base = celleb_epci_get_epci_base(hose);
74
reg = epci_base + PCI_COMMAND;
75
out_be32(reg, in_be32(reg) | (PCI_STATUS_REC_MASTER_ABORT << 16));
76
}
77
78
static int celleb_epci_check_abort(struct pci_controller *hose,
79
PCI_IO_ADDR addr)
80
{
81
PCI_IO_ADDR reg;
82
PCI_IO_ADDR epci_base;
83
u32 val;
84
85
iob();
86
epci_base = celleb_epci_get_epci_base(hose);
87
88
reg = epci_base + PCI_COMMAND;
89
val = in_be32(reg);
90
91
if (val & (PCI_STATUS_REC_MASTER_ABORT << 16)) {
92
out_be32(reg,
93
(val & 0xffff) | (PCI_STATUS_REC_MASTER_ABORT << 16));
94
95
/* clear PCI Controller error, FRE, PMFE */
96
reg = epci_base + SCC_EPCI_STATUS;
97
out_be32(reg, SCC_EPCI_INT_PAI);
98
99
reg = epci_base + SCC_EPCI_VCSR;
100
val = in_be32(reg) & 0xffff;
101
val |= SCC_EPCI_VCSR_FRE;
102
out_be32(reg, val);
103
104
reg = epci_base + SCC_EPCI_VISTAT;
105
out_be32(reg, SCC_EPCI_VISTAT_PMFE);
106
return PCIBIOS_DEVICE_NOT_FOUND;
107
}
108
109
return PCIBIOS_SUCCESSFUL;
110
}
111
112
static PCI_IO_ADDR celleb_epci_make_config_addr(struct pci_bus *bus,
113
struct pci_controller *hose, unsigned int devfn, int where)
114
{
115
PCI_IO_ADDR addr;
116
117
if (bus != hose->bus)
118
addr = celleb_epci_get_epci_cfg(hose) +
119
(((bus->number & 0xff) << 16)
120
| ((devfn & 0xff) << 8)
121
| (where & 0xff)
122
| 0x01000000);
123
else
124
addr = celleb_epci_get_epci_cfg(hose) +
125
(((devfn & 0xff) << 8) | (where & 0xff));
126
127
pr_debug("EPCI: config_addr = 0x%p\n", addr);
128
129
return addr;
130
}
131
132
static int celleb_epci_read_config(struct pci_bus *bus,
133
unsigned int devfn, int where, int size, u32 *val)
134
{
135
PCI_IO_ADDR epci_base;
136
PCI_IO_ADDR addr;
137
struct pci_controller *hose = pci_bus_to_host(bus);
138
139
/* allignment check */
140
BUG_ON(where % size);
141
142
if (!celleb_epci_get_epci_cfg(hose))
143
return PCIBIOS_DEVICE_NOT_FOUND;
144
145
if (bus->number == hose->first_busno && devfn == 0) {
146
/* EPCI controller self */
147
148
epci_base = celleb_epci_get_epci_base(hose);
149
addr = epci_base + where;
150
151
switch (size) {
152
case 1:
153
*val = in_8(addr);
154
break;
155
case 2:
156
*val = in_be16(addr);
157
break;
158
case 4:
159
*val = in_be32(addr);
160
break;
161
default:
162
return PCIBIOS_DEVICE_NOT_FOUND;
163
}
164
165
} else {
166
167
clear_and_disable_master_abort_interrupt(hose);
168
addr = celleb_epci_make_config_addr(bus, hose, devfn, where);
169
170
switch (size) {
171
case 1:
172
*val = in_8(addr);
173
break;
174
case 2:
175
*val = in_le16(addr);
176
break;
177
case 4:
178
*val = in_le32(addr);
179
break;
180
default:
181
return PCIBIOS_DEVICE_NOT_FOUND;
182
}
183
}
184
185
pr_debug("EPCI: "
186
"addr=0x%p, devfn=0x%x, where=0x%x, size=0x%x, val=0x%x\n",
187
addr, devfn, where, size, *val);
188
189
return celleb_epci_check_abort(hose, NULL);
190
}
191
192
static int celleb_epci_write_config(struct pci_bus *bus,
193
unsigned int devfn, int where, int size, u32 val)
194
{
195
PCI_IO_ADDR epci_base;
196
PCI_IO_ADDR addr;
197
struct pci_controller *hose = pci_bus_to_host(bus);
198
199
/* allignment check */
200
BUG_ON(where % size);
201
202
if (!celleb_epci_get_epci_cfg(hose))
203
return PCIBIOS_DEVICE_NOT_FOUND;
204
205
if (bus->number == hose->first_busno && devfn == 0) {
206
/* EPCI controller self */
207
208
epci_base = celleb_epci_get_epci_base(hose);
209
addr = epci_base + where;
210
211
switch (size) {
212
case 1:
213
out_8(addr, val);
214
break;
215
case 2:
216
out_be16(addr, val);
217
break;
218
case 4:
219
out_be32(addr, val);
220
break;
221
default:
222
return PCIBIOS_DEVICE_NOT_FOUND;
223
}
224
225
} else {
226
227
clear_and_disable_master_abort_interrupt(hose);
228
addr = celleb_epci_make_config_addr(bus, hose, devfn, where);
229
230
switch (size) {
231
case 1:
232
out_8(addr, val);
233
break;
234
case 2:
235
out_le16(addr, val);
236
break;
237
case 4:
238
out_le32(addr, val);
239
break;
240
default:
241
return PCIBIOS_DEVICE_NOT_FOUND;
242
}
243
}
244
245
return celleb_epci_check_abort(hose, addr);
246
}
247
248
struct pci_ops celleb_epci_ops = {
249
.read = celleb_epci_read_config,
250
.write = celleb_epci_write_config,
251
};
252
253
/* to be moved in FW */
254
static int __init celleb_epci_init(struct pci_controller *hose)
255
{
256
u32 val;
257
PCI_IO_ADDR reg;
258
PCI_IO_ADDR epci_base;
259
int hwres = 0;
260
261
epci_base = celleb_epci_get_epci_base(hose);
262
263
/* PCI core reset(Internal bus and PCI clock) */
264
reg = epci_base + SCC_EPCI_CKCTRL;
265
val = in_be32(reg);
266
if (val == 0x00030101)
267
hwres = 1;
268
else {
269
val &= ~(SCC_EPCI_CKCTRL_CRST0 | SCC_EPCI_CKCTRL_CRST1);
270
out_be32(reg, val);
271
272
/* set PCI core clock */
273
val = in_be32(reg);
274
val |= (SCC_EPCI_CKCTRL_OCLKEN | SCC_EPCI_CKCTRL_LCLKEN);
275
out_be32(reg, val);
276
277
/* release PCI core reset (internal bus) */
278
val = in_be32(reg);
279
val |= SCC_EPCI_CKCTRL_CRST0;
280
out_be32(reg, val);
281
282
/* set PCI clock select */
283
reg = epci_base + SCC_EPCI_CLKRST;
284
val = in_be32(reg);
285
val &= ~SCC_EPCI_CLKRST_CKS_MASK;
286
val |= SCC_EPCI_CLKRST_CKS_2;
287
out_be32(reg, val);
288
289
/* set arbiter */
290
reg = epci_base + SCC_EPCI_ABTSET;
291
out_be32(reg, 0x0f1f001f); /* temporary value */
292
293
/* buffer on */
294
reg = epci_base + SCC_EPCI_CLKRST;
295
val = in_be32(reg);
296
val |= SCC_EPCI_CLKRST_BC;
297
out_be32(reg, val);
298
299
/* PCI clock enable */
300
val = in_be32(reg);
301
val |= SCC_EPCI_CLKRST_PCKEN;
302
out_be32(reg, val);
303
304
/* release PCI core reset (all) */
305
reg = epci_base + SCC_EPCI_CKCTRL;
306
val = in_be32(reg);
307
val |= (SCC_EPCI_CKCTRL_CRST0 | SCC_EPCI_CKCTRL_CRST1);
308
out_be32(reg, val);
309
310
/* set base translation registers. (already set by Beat) */
311
312
/* set base address masks. (already set by Beat) */
313
}
314
315
/* release interrupt masks and clear all interrupts */
316
reg = epci_base + SCC_EPCI_INTSET;
317
out_be32(reg, 0x013f011f); /* all interrupts enable */
318
reg = epci_base + SCC_EPCI_VIENAB;
319
val = SCC_EPCI_VIENAB_PMPEE | SCC_EPCI_VIENAB_PMFEE;
320
out_be32(reg, val);
321
reg = epci_base + SCC_EPCI_STATUS;
322
out_be32(reg, 0xffffffff);
323
reg = epci_base + SCC_EPCI_VISTAT;
324
out_be32(reg, 0xffffffff);
325
326
/* disable PCI->IB address translation */
327
reg = epci_base + SCC_EPCI_VCSR;
328
val = in_be32(reg);
329
val &= ~(SCC_EPCI_VCSR_DR | SCC_EPCI_VCSR_AT);
330
out_be32(reg, val);
331
332
/* set base addresses. (no need to set?) */
333
334
/* memory space, bus master enable */
335
reg = epci_base + PCI_COMMAND;
336
val = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
337
out_be32(reg, val);
338
339
/* endian mode setup */
340
reg = epci_base + SCC_EPCI_ECMODE;
341
val = 0x00550155;
342
out_be32(reg, val);
343
344
/* set control option */
345
reg = epci_base + SCC_EPCI_CNTOPT;
346
val = in_be32(reg);
347
val |= SCC_EPCI_CNTOPT_O2PMB;
348
out_be32(reg, val);
349
350
/* XXX: temporay: set registers for address conversion setup */
351
reg = epci_base + SCC_EPCI_CNF10_REG;
352
out_be32(reg, 0x80000008);
353
reg = epci_base + SCC_EPCI_CNF14_REG;
354
out_be32(reg, 0x40000008);
355
356
reg = epci_base + SCC_EPCI_BAM0;
357
out_be32(reg, 0x80000000);
358
reg = epci_base + SCC_EPCI_BAM1;
359
out_be32(reg, 0xe0000000);
360
361
reg = epci_base + SCC_EPCI_PVBAT;
362
out_be32(reg, 0x80000000);
363
364
if (!hwres) {
365
/* release external PCI reset */
366
reg = epci_base + SCC_EPCI_CLKRST;
367
val = in_be32(reg);
368
val |= SCC_EPCI_CLKRST_PCIRST;
369
out_be32(reg, val);
370
}
371
372
return 0;
373
}
374
375
static int __init celleb_setup_epci(struct device_node *node,
376
struct pci_controller *hose)
377
{
378
struct resource r;
379
380
pr_debug("PCI: celleb_setup_epci()\n");
381
382
/*
383
* Note:
384
* Celleb epci uses cfg_addr and cfg_data member of
385
* pci_controller structure in irregular way.
386
*
387
* cfg_addr is used to map for control registers of
388
* celleb epci.
389
*
390
* cfg_data is used for configuration area of devices
391
* on Celleb epci buses.
392
*/
393
394
if (of_address_to_resource(node, 0, &r))
395
goto error;
396
hose->cfg_addr = ioremap(r.start, (r.end - r.start + 1));
397
if (!hose->cfg_addr)
398
goto error;
399
pr_debug("EPCI: cfg_addr map 0x%016llx->0x%016lx + 0x%016llx\n",
400
r.start, (unsigned long)hose->cfg_addr, (r.end - r.start + 1));
401
402
if (of_address_to_resource(node, 2, &r))
403
goto error;
404
hose->cfg_data = ioremap(r.start, (r.end - r.start + 1));
405
if (!hose->cfg_data)
406
goto error;
407
pr_debug("EPCI: cfg_data map 0x%016llx->0x%016lx + 0x%016llx\n",
408
r.start, (unsigned long)hose->cfg_data, (r.end - r.start + 1));
409
410
hose->ops = &celleb_epci_ops;
411
celleb_epci_init(hose);
412
413
return 0;
414
415
error:
416
if (hose->cfg_addr)
417
iounmap(hose->cfg_addr);
418
419
if (hose->cfg_data)
420
iounmap(hose->cfg_data);
421
return 1;
422
}
423
424
struct celleb_phb_spec celleb_epci_spec __initdata = {
425
.setup = celleb_setup_epci,
426
.ops = &spiderpci_ops,
427
.iowa_init = &spiderpci_iowa_init,
428
.iowa_data = (void *)0,
429
};
430
431