1/*
2 * Support for indirect PCI bridges.
3 *
4 * Copyright (C) 1998 Gabriel Paubert.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 * "Temporary" MPC8548 Errata file -
12 * The standard indirect_pci code should work with future silicon versions.
13 */
14
15#include <linux/kernel.h>
16#include <linux/pci.h>
17#include <linux/delay.h>
18#include <linux/string.h>
19#include <linux/init.h>
20#include <linux/bootmem.h>
21
22#include <asm/io.h>
23#include <asm/prom.h>
24#include <asm/pci-bridge.h>
25#include <asm/machdep.h>
26
27#define PCI_CFG_OUT out_be32
28
29/* ERRATA PCI-Ex 14 PCIE Controller timeout */
30#define PCIE_FIX		out_be32(hose->cfg_addr+0x4, 0x0400ffff)
31
32
33static int
34indirect_read_config_pcie(struct pci_bus *bus, unsigned int devfn, int offset,
35		     int len, u32 *val)
36{
37	struct pci_controller *hose = bus->sysdata;
38	volatile void __iomem *cfg_data;
39	u32 temp;
40
41	if (ppc_md.pci_exclude_device)
42		if (ppc_md.pci_exclude_device(bus->number, devfn))
43			return PCIBIOS_DEVICE_NOT_FOUND;
44
45	/* Possible artifact of CDCpp50937 needs further investigation */
46	if (devfn != 0x0 && bus->number == 0xff)
47		return PCIBIOS_DEVICE_NOT_FOUND;
48
49	PCIE_FIX;
50	if (bus->number == 0xff) {
51		PCI_CFG_OUT(hose->cfg_addr,
52			    (0x80000000 | ((offset & 0xf00) << 16) |
53			     ((bus->number - hose->bus_offset) << 16)
54			     | (devfn << 8) | ((offset & 0xfc) )));
55	} else {
56		PCI_CFG_OUT(hose->cfg_addr,
57			    (0x80000001 | ((offset & 0xf00) << 16) |
58			     ((bus->number - hose->bus_offset) << 16)
59			     | (devfn << 8) | ((offset & 0xfc) )));
60	}
61
62	/*
63	 * Note: the caller has already checked that offset is
64	 * suitably aligned and that len is 1, 2 or 4.
65	 */
66	/* ERRATA PCI-Ex 12 - Configuration Address/Data Alignment */
67	cfg_data = hose->cfg_data;
68	PCIE_FIX;
69	temp = in_le32(cfg_data);
70	switch (len) {
71	case 1:
72		*val = (temp >> (((offset & 3))*8)) & 0xff;
73		break;
74	case 2:
75		*val = (temp >> (((offset & 3))*8)) & 0xffff;
76		break;
77	default:
78		*val = temp;
79		break;
80	}
81	return PCIBIOS_SUCCESSFUL;
82}
83
84static int
85indirect_write_config_pcie(struct pci_bus *bus, unsigned int devfn, int offset,
86		      int len, u32 val)
87{
88	struct pci_controller *hose = bus->sysdata;
89	volatile void __iomem *cfg_data;
90	u32 temp;
91
92	if (ppc_md.pci_exclude_device)
93		if (ppc_md.pci_exclude_device(bus->number, devfn))
94			return PCIBIOS_DEVICE_NOT_FOUND;
95
96	/* Possible artifact of CDCpp50937 needs further investigation */
97	if (devfn != 0x0 && bus->number == 0xff)
98		return PCIBIOS_DEVICE_NOT_FOUND;
99
100	PCIE_FIX;
101	if (bus->number == 0xff) {
102		PCI_CFG_OUT(hose->cfg_addr,
103			    (0x80000000 | ((offset & 0xf00) << 16) |
104			     ((bus->number - hose->bus_offset) << 16)
105			     | (devfn << 8) | ((offset & 0xfc) )));
106	} else {
107		PCI_CFG_OUT(hose->cfg_addr,
108			    (0x80000001 | ((offset & 0xf00) << 16) |
109			     ((bus->number - hose->bus_offset) << 16)
110			     | (devfn << 8) | ((offset & 0xfc) )));
111        }
112
113	/*
114	 * Note: the caller has already checked that offset is
115	 * suitably aligned and that len is 1, 2 or 4.
116	 */
117	/* ERRATA PCI-Ex 12 - Configuration Address/Data Alignment */
118	cfg_data = hose->cfg_data;
119	switch (len) {
120	case 1:
121		PCIE_FIX;
122		temp = in_le32(cfg_data);
123		temp = (temp & ~(0xff << ((offset & 3) * 8))) |
124			(val << ((offset & 3) * 8));
125		PCIE_FIX;
126		out_le32(cfg_data, temp);
127		break;
128	case 2:
129		PCIE_FIX;
130		temp = in_le32(cfg_data);
131		temp = (temp & ~(0xffff << ((offset & 3) * 8)));
132		temp |= (val << ((offset & 3) * 8)) ;
133		PCIE_FIX;
134		out_le32(cfg_data, temp);
135		break;
136	default:
137		PCIE_FIX;
138		out_le32(cfg_data, val);
139		break;
140	}
141	PCIE_FIX;
142	return PCIBIOS_SUCCESSFUL;
143}
144
145static struct pci_ops indirect_pcie_ops = {
146	indirect_read_config_pcie,
147	indirect_write_config_pcie
148};
149
150void __init
151setup_indirect_pcie_nomap(struct pci_controller* hose, void __iomem * cfg_addr,
152	void __iomem * cfg_data)
153{
154	hose->cfg_addr = cfg_addr;
155	hose->cfg_data = cfg_data;
156	hose->ops = &indirect_pcie_ops;
157}
158
159void __init
160setup_indirect_pcie(struct pci_controller* hose, u32 cfg_addr, u32 cfg_data)
161{
162	unsigned long base = cfg_addr & PAGE_MASK;
163	void __iomem *mbase, *addr, *data;
164
165	mbase = ioremap(base, PAGE_SIZE);
166	addr = mbase + (cfg_addr & ~PAGE_MASK);
167	if ((cfg_data & PAGE_MASK) != base)
168		mbase = ioremap(cfg_data & PAGE_MASK, PAGE_SIZE);
169	data = mbase + (cfg_data & ~PAGE_MASK);
170	setup_indirect_pcie_nomap(hose, addr, data);
171}
172