• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500-V1.0.1.40_1.0.68/src/linux/linux-2.6/arch/powerpc/platforms/celleb/
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/machdep.h>
34#include <asm/pci-bridge.h>
35#include <asm/ppc-pci.h>
36
37#include "scc.h"
38#include "pci.h"
39#include "interrupt.h"
40
41#define MAX_PCI_DEVICES   32
42#define MAX_PCI_FUNCTIONS  8
43
44#define iob()  __asm__ __volatile__("eieio; sync":::"memory")
45
46static inline volatile void __iomem *celleb_epci_get_epci_base(
47					struct pci_controller *hose)
48{
49	/*
50	 * Note:
51	 * Celleb epci uses cfg_addr as a base address for
52	 * epci control registers.
53	 */
54
55	return hose->cfg_addr;
56}
57
58static inline volatile void __iomem *celleb_epci_get_epci_cfg(
59					struct pci_controller *hose)
60{
61	/*
62	 * Note:
63	 * Celleb epci uses cfg_data as a base address for
64	 * configuration area for epci devices.
65	 */
66
67	return hose->cfg_data;
68}
69
70
71static inline void clear_and_disable_master_abort_interrupt(
72					struct pci_controller *hose)
73{
74	volatile void __iomem *epci_base, *reg;
75	epci_base = celleb_epci_get_epci_base(hose);
76	reg = epci_base + PCI_COMMAND;
77	out_be32(reg, in_be32(reg) | (PCI_STATUS_REC_MASTER_ABORT << 16));
78}
79
80static int celleb_epci_check_abort(struct pci_controller *hose,
81				   volatile void __iomem *addr)
82{
83	volatile void __iomem *reg, *epci_base;
84	u32 val;
85
86	iob();
87	epci_base = celleb_epci_get_epci_base(hose);
88
89	reg = epci_base + PCI_COMMAND;
90	val = in_be32(reg);
91
92	if (val & (PCI_STATUS_REC_MASTER_ABORT << 16)) {
93		out_be32(reg,
94			 (val & 0xffff) | (PCI_STATUS_REC_MASTER_ABORT << 16));
95
96		/* clear PCI Controller error, FRE, PMFE */
97		reg = epci_base + SCC_EPCI_STATUS;
98		out_be32(reg, SCC_EPCI_INT_PAI);
99
100		reg = epci_base + SCC_EPCI_VCSR;
101		val = in_be32(reg) & 0xffff;
102		val |= SCC_EPCI_VCSR_FRE;
103		out_be32(reg, val);
104
105		reg = epci_base + SCC_EPCI_VISTAT;
106		out_be32(reg, SCC_EPCI_VISTAT_PMFE);
107		return PCIBIOS_DEVICE_NOT_FOUND;
108	}
109
110	return PCIBIOS_SUCCESSFUL;
111}
112
113static volatile void __iomem *celleb_epci_make_config_addr(
114					struct pci_bus *bus,
115					struct pci_controller *hose,
116					unsigned int devfn, int where)
117{
118	volatile void __iomem *addr;
119
120	if (bus != hose->bus)
121		addr = celleb_epci_get_epci_cfg(hose) +
122		       (((bus->number & 0xff) << 16)
123		        | ((devfn & 0xff) << 8)
124		        | (where & 0xff)
125		        | 0x01000000);
126	else
127		addr = celleb_epci_get_epci_cfg(hose) +
128		       (((devfn & 0xff) << 8) | (where & 0xff));
129
130	pr_debug("EPCI: config_addr = 0x%p\n", addr);
131
132	return addr;
133}
134
135static int celleb_epci_read_config(struct pci_bus *bus,
136			unsigned int devfn, int where, int size, u32 * val)
137{
138	volatile void __iomem *epci_base, *addr;
139	struct device_node *node;
140	struct pci_controller *hose;
141
142	/* allignment check */
143	BUG_ON(where % size);
144
145	node = (struct device_node *)bus->sysdata;
146	hose = pci_find_hose_for_OF_device(node);
147
148	if (!celleb_epci_get_epci_cfg(hose))
149		return PCIBIOS_DEVICE_NOT_FOUND;
150
151	if (bus->number == hose->first_busno && devfn == 0) {
152		/* EPCI controller self */
153
154		epci_base = celleb_epci_get_epci_base(hose);
155		addr = epci_base + where;
156
157		switch (size) {
158		case 1:
159			*val = in_8(addr);
160			break;
161		case 2:
162			*val = in_be16(addr);
163			break;
164		case 4:
165			*val = in_be32(addr);
166			break;
167		default:
168			return PCIBIOS_DEVICE_NOT_FOUND;
169		}
170
171	} else {
172
173		clear_and_disable_master_abort_interrupt(hose);
174		addr = celleb_epci_make_config_addr(bus, hose, devfn, where);
175
176		switch (size) {
177		case 1:
178			*val = in_8(addr);
179			break;
180		case 2:
181			*val = in_le16(addr);
182			break;
183		case 4:
184			*val = in_le32(addr);
185			break;
186		default:
187			return PCIBIOS_DEVICE_NOT_FOUND;
188		}
189	}
190
191	pr_debug("EPCI: "
192		 "addr=0x%p, devfn=0x%x, where=0x%x, size=0x%x, val=0x%x\n",
193		 addr, devfn, where, size, *val);
194
195	return celleb_epci_check_abort(hose, NULL);
196}
197
198static int celleb_epci_write_config(struct pci_bus *bus,
199			unsigned int devfn, int where, int size, u32 val)
200{
201	volatile void __iomem *epci_base, *addr;
202	struct device_node *node;
203	struct pci_controller *hose;
204
205	/* allignment check */
206	BUG_ON(where % size);
207
208	node = (struct device_node *)bus->sysdata;
209	hose = pci_find_hose_for_OF_device(node);
210
211
212	if (!celleb_epci_get_epci_cfg(hose))
213		return PCIBIOS_DEVICE_NOT_FOUND;
214
215	if (bus->number == hose->first_busno && devfn == 0) {
216		/* EPCI controller self */
217
218		epci_base = celleb_epci_get_epci_base(hose);
219		addr = epci_base + where;
220
221		switch (size) {
222		case 1:
223			out_8(addr, val);
224			break;
225		case 2:
226			out_be16(addr, val);
227			break;
228		case 4:
229			out_be32(addr, val);
230			break;
231		default:
232			return PCIBIOS_DEVICE_NOT_FOUND;
233		}
234
235	} else {
236
237		clear_and_disable_master_abort_interrupt(hose);
238		addr = celleb_epci_make_config_addr(bus, hose, devfn, where);
239
240		switch (size) {
241		case 1:
242			out_8(addr, val);
243			break;
244		case 2:
245			out_le16(addr, val);
246			break;
247		case 4:
248			out_le32(addr, val);
249			break;
250		default:
251			return PCIBIOS_DEVICE_NOT_FOUND;
252		}
253	}
254
255	return celleb_epci_check_abort(hose, addr);
256}
257
258struct pci_ops celleb_epci_ops = {
259	celleb_epci_read_config,
260	celleb_epci_write_config,
261};
262
263/* to be moved in FW */
264static int __devinit celleb_epci_init(struct pci_controller *hose)
265{
266	u32 val;
267	volatile void __iomem *reg, *epci_base;
268	int hwres = 0;
269
270	epci_base = celleb_epci_get_epci_base(hose);
271
272	/* PCI core reset(Internal bus and PCI clock) */
273	reg = epci_base + SCC_EPCI_CKCTRL;
274	val = in_be32(reg);
275	if (val == 0x00030101)
276		hwres = 1;
277	else {
278		val &= ~(SCC_EPCI_CKCTRL_CRST0 | SCC_EPCI_CKCTRL_CRST1);
279		out_be32(reg, val);
280
281		/* set PCI core clock */
282		val = in_be32(reg);
283		val |= (SCC_EPCI_CKCTRL_OCLKEN | SCC_EPCI_CKCTRL_LCLKEN);
284		out_be32(reg, val);
285
286		/* release PCI core reset (internal bus) */
287		val = in_be32(reg);
288		val |= SCC_EPCI_CKCTRL_CRST0;
289		out_be32(reg, val);
290
291		/* set PCI clock select */
292		reg = epci_base + SCC_EPCI_CLKRST;
293		val = in_be32(reg);
294		val &= ~SCC_EPCI_CLKRST_CKS_MASK;
295		val |= SCC_EPCI_CLKRST_CKS_2;
296		out_be32(reg, val);
297
298		/* set arbiter */
299		reg = epci_base + SCC_EPCI_ABTSET;
300		out_be32(reg, 0x0f1f001f);	/* temporary value */
301
302		/* buffer on */
303		reg = epci_base + SCC_EPCI_CLKRST;
304		val = in_be32(reg);
305		val |= SCC_EPCI_CLKRST_BC;
306		out_be32(reg, val);
307
308		/* PCI clock enable */
309		val = in_be32(reg);
310		val |= SCC_EPCI_CLKRST_PCKEN;
311		out_be32(reg, val);
312
313		/* release PCI core reset (all) */
314		reg = epci_base + SCC_EPCI_CKCTRL;
315		val = in_be32(reg);
316		val |= (SCC_EPCI_CKCTRL_CRST0 | SCC_EPCI_CKCTRL_CRST1);
317		out_be32(reg, val);
318
319		/* set base translation registers. (already set by Beat) */
320
321		/* set base address masks. (already set by Beat) */
322	}
323
324	/* release interrupt masks and clear all interrupts */
325	reg = epci_base + SCC_EPCI_INTSET;
326	out_be32(reg, 0x013f011f);	/* all interrupts enable */
327	reg = epci_base + SCC_EPCI_VIENAB;
328	val = SCC_EPCI_VIENAB_PMPEE | SCC_EPCI_VIENAB_PMFEE;
329	out_be32(reg, val);
330	reg = epci_base + SCC_EPCI_STATUS;
331	out_be32(reg, 0xffffffff);
332	reg = epci_base + SCC_EPCI_VISTAT;
333	out_be32(reg, 0xffffffff);
334
335	/* disable PCI->IB address translation */
336	reg = epci_base + SCC_EPCI_VCSR;
337	val = in_be32(reg);
338	val &= ~(SCC_EPCI_VCSR_DR | SCC_EPCI_VCSR_AT);
339	out_be32(reg, val);
340
341	/* set base addresses. (no need to set?) */
342
343	/* memory space, bus master enable */
344	reg = epci_base + PCI_COMMAND;
345	val = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
346	out_be32(reg, val);
347
348	/* endian mode setup */
349	reg = epci_base + SCC_EPCI_ECMODE;
350	val = 0x00550155;
351	out_be32(reg, val);
352
353	/* set control option */
354	reg = epci_base + SCC_EPCI_CNTOPT;
355	val = in_be32(reg);
356	val |= SCC_EPCI_CNTOPT_O2PMB;
357	out_be32(reg, val);
358
359	reg = epci_base + SCC_EPCI_CNF10_REG;
360	out_be32(reg, 0x80000008);
361	reg = epci_base + SCC_EPCI_CNF14_REG;
362	out_be32(reg, 0x40000008);
363
364	reg = epci_base + SCC_EPCI_BAM0;
365	out_be32(reg, 0x80000000);
366	reg = epci_base + SCC_EPCI_BAM1;
367	out_be32(reg, 0xe0000000);
368
369	reg = epci_base + SCC_EPCI_PVBAT;
370	out_be32(reg, 0x80000000);
371
372	if (!hwres) {
373		/* release external PCI reset */
374		reg = epci_base + SCC_EPCI_CLKRST;
375		val = in_be32(reg);
376		val |= SCC_EPCI_CLKRST_PCIRST;
377		out_be32(reg, val);
378	}
379
380	return 0;
381}
382
383int __devinit celleb_setup_epci(struct device_node *node,
384				struct pci_controller *hose)
385{
386	struct resource r;
387
388	pr_debug("PCI: celleb_setup_epci()\n");
389
390	/*
391	 * Note:
392	 * Celleb epci uses cfg_addr and cfg_data member of
393	 * pci_controller structure in irregular way.
394	 *
395	 * cfg_addr is used to map for control registers of
396	 * celleb epci.
397	 *
398	 * cfg_data is used for configuration area of devices
399	 * on Celleb epci buses.
400	 */
401
402	if (of_address_to_resource(node, 0, &r))
403		goto error;
404	hose->cfg_addr = ioremap(r.start, (r.end - r.start + 1));
405	if (!hose->cfg_addr)
406		goto error;
407	pr_debug("EPCI: cfg_addr map 0x%016lx->0x%016lx + 0x%016lx\n",
408		 r.start, (unsigned long)hose->cfg_addr,
409		(r.end - r.start + 1));
410
411	if (of_address_to_resource(node, 2, &r))
412		goto error;
413	hose->cfg_data = ioremap(r.start, (r.end - r.start + 1));
414	if (!hose->cfg_data)
415		goto error;
416	pr_debug("EPCI: cfg_data map 0x%016lx->0x%016lx + 0x%016lx\n",
417		 r.start, (unsigned long)hose->cfg_data,
418		(r.end - r.start + 1));
419
420	celleb_epci_init(hose);
421
422	return 0;
423
424error:
425	return 1;
426}
427