blob: bc5b4e22fa5d16c74ae2006f6dd0abc9addb60cd [file] [log] [blame]
Paul Mackerrasdaec9622005-10-10 22:25:26 +10001/*
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
12#include <linux/kernel.h>
13#include <linux/pci.h>
14#include <linux/delay.h>
15#include <linux/string.h>
16#include <linux/init.h>
17
18#include <asm/io.h>
19#include <asm/prom.h>
20#include <asm/pci-bridge.h>
21#include <asm/machdep.h>
22
23#ifdef CONFIG_PPC_INDIRECT_PCI_BE
24#define PCI_CFG_OUT out_be32
25#else
26#define PCI_CFG_OUT out_le32
27#endif
28
29static int
30indirect_read_config(struct pci_bus *bus, unsigned int devfn, int offset,
31 int len, u32 *val)
32{
33 struct pci_controller *hose = bus->sysdata;
34 volatile void __iomem *cfg_data;
35 u8 cfg_type = 0;
Kumar Galaab0f9ad2007-06-25 15:19:48 -050036 u32 bus_no, reg;
Paul Mackerrasdaec9622005-10-10 22:25:26 +100037
Kumar Gala62c66c82007-07-11 13:22:41 -050038 if (hose->indirect_type & PPC_INDIRECT_TYPE_NO_PCIE_LINK) {
39 if (bus->number != hose->first_busno)
40 return PCIBIOS_DEVICE_NOT_FOUND;
41 if (devfn != 0)
42 return PCIBIOS_DEVICE_NOT_FOUND;
43 }
44
Paul Mackerrasdaec9622005-10-10 22:25:26 +100045 if (ppc_md.pci_exclude_device)
Kumar Gala7d52c7b2007-06-22 00:23:57 -050046 if (ppc_md.pci_exclude_device(hose, bus->number, devfn))
Paul Mackerrasdaec9622005-10-10 22:25:26 +100047 return PCIBIOS_DEVICE_NOT_FOUND;
Kumar Gala62c66c82007-07-11 13:22:41 -050048
Kumar Galaab0f9ad2007-06-25 15:19:48 -050049 if (hose->indirect_type & PPC_INDIRECT_TYPE_SET_CFG_TYPE)
Paul Mackerrasdaec9622005-10-10 22:25:26 +100050 if (bus->number != hose->first_busno)
51 cfg_type = 1;
52
Kumar Gala5ab65ec2007-06-25 13:09:42 -050053 bus_no = (bus->number == hose->first_busno) ?
Kumar Gala0a3786c2007-06-25 13:32:48 -050054 hose->self_busno : bus->number;
Kumar Gala5ab65ec2007-06-25 13:09:42 -050055
Kumar Galaab0f9ad2007-06-25 15:19:48 -050056 if (hose->indirect_type & PPC_INDIRECT_TYPE_EXT_REG)
57 reg = ((offset & 0xf00) << 16) | (offset & 0xfc);
58 else
59 reg = offset & 0xfc;
60
Kumar Gala7d52c7b2007-06-22 00:23:57 -050061 PCI_CFG_OUT(hose->cfg_addr,
Kumar Gala5ab65ec2007-06-25 13:09:42 -050062 (0x80000000 | (bus_no << 16)
Kumar Galaab0f9ad2007-06-25 15:19:48 -050063 | (devfn << 8) | reg | cfg_type));
Paul Mackerrasdaec9622005-10-10 22:25:26 +100064
65 /*
66 * Note: the caller has already checked that offset is
67 * suitably aligned and that len is 1, 2 or 4.
68 */
69 cfg_data = hose->cfg_data + (offset & 3);
70 switch (len) {
71 case 1:
72 *val = in_8(cfg_data);
73 break;
74 case 2:
75 *val = in_le16(cfg_data);
76 break;
77 default:
78 *val = in_le32(cfg_data);
79 break;
80 }
81 return PCIBIOS_SUCCESSFUL;
82}
83
84static int
85indirect_write_config(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 u8 cfg_type = 0;
Kumar Galaab0f9ad2007-06-25 15:19:48 -050091 u32 bus_no, reg;
Paul Mackerrasdaec9622005-10-10 22:25:26 +100092
Kumar Gala62c66c82007-07-11 13:22:41 -050093 if (hose->indirect_type & PPC_INDIRECT_TYPE_NO_PCIE_LINK) {
94 if (bus->number != hose->first_busno)
95 return PCIBIOS_DEVICE_NOT_FOUND;
96 if (devfn != 0)
97 return PCIBIOS_DEVICE_NOT_FOUND;
98 }
99
Paul Mackerrasdaec9622005-10-10 22:25:26 +1000100 if (ppc_md.pci_exclude_device)
Kumar Gala7d52c7b2007-06-22 00:23:57 -0500101 if (ppc_md.pci_exclude_device(hose, bus->number, devfn))
Paul Mackerrasdaec9622005-10-10 22:25:26 +1000102 return PCIBIOS_DEVICE_NOT_FOUND;
103
Kumar Galaab0f9ad2007-06-25 15:19:48 -0500104 if (hose->indirect_type & PPC_INDIRECT_TYPE_SET_CFG_TYPE)
Paul Mackerrasdaec9622005-10-10 22:25:26 +1000105 if (bus->number != hose->first_busno)
106 cfg_type = 1;
107
Kumar Gala5ab65ec2007-06-25 13:09:42 -0500108 bus_no = (bus->number == hose->first_busno) ?
Kumar Gala0a3786c2007-06-25 13:32:48 -0500109 hose->self_busno : bus->number;
Kumar Gala5ab65ec2007-06-25 13:09:42 -0500110
Kumar Galaab0f9ad2007-06-25 15:19:48 -0500111 if (hose->indirect_type & PPC_INDIRECT_TYPE_EXT_REG)
112 reg = ((offset & 0xf00) << 16) | (offset & 0xfc);
113 else
114 reg = offset & 0xfc;
115
Kumar Gala7d52c7b2007-06-22 00:23:57 -0500116 PCI_CFG_OUT(hose->cfg_addr,
Kumar Gala5ab65ec2007-06-25 13:09:42 -0500117 (0x80000000 | (bus_no << 16)
Kumar Galaab0f9ad2007-06-25 15:19:48 -0500118 | (devfn << 8) | reg | cfg_type));
Paul Mackerrasdaec9622005-10-10 22:25:26 +1000119
Kumar Gala476f5772007-06-26 12:12:55 -0500120 /* surpress setting of PCI_PRIMARY_BUS */
121 if (hose->indirect_type & PPC_INDIRECT_TYPE_SURPRESS_PRIMARY_BUS)
122 if ((offset == PCI_PRIMARY_BUS) &&
123 (bus->number == hose->first_busno))
124 val &= 0xffffff00;
125
Paul Mackerrasdaec9622005-10-10 22:25:26 +1000126 /*
127 * Note: the caller has already checked that offset is
128 * suitably aligned and that len is 1, 2 or 4.
129 */
130 cfg_data = hose->cfg_data + (offset & 3);
131 switch (len) {
132 case 1:
133 out_8(cfg_data, val);
134 break;
135 case 2:
136 out_le16(cfg_data, val);
137 break;
138 default:
139 out_le32(cfg_data, val);
140 break;
141 }
142 return PCIBIOS_SUCCESSFUL;
143}
144
145static struct pci_ops indirect_pci_ops =
146{
147 indirect_read_config,
148 indirect_write_config
149};
150
151void __init
152setup_indirect_pci_nomap(struct pci_controller* hose, void __iomem * cfg_addr,
153 void __iomem * cfg_data)
154{
155 hose->cfg_addr = cfg_addr;
156 hose->cfg_data = cfg_data;
157 hose->ops = &indirect_pci_ops;
158}
159
160void __init
161setup_indirect_pci(struct pci_controller* hose, u32 cfg_addr, u32 cfg_data)
162{
163 unsigned long base = cfg_addr & PAGE_MASK;
164 void __iomem *mbase, *addr, *data;
165
166 mbase = ioremap(base, PAGE_SIZE);
167 addr = mbase + (cfg_addr & ~PAGE_MASK);
168 if ((cfg_data & PAGE_MASK) != base)
169 mbase = ioremap(cfg_data & PAGE_MASK, PAGE_SIZE);
170 data = mbase + (cfg_data & ~PAGE_MASK);
171 setup_indirect_pci_nomap(hose, addr, data);
172}