1 /*
2 *
3 * This file is subject to the terms and conditions of the GNU General Public
4 * License. See the file "COPYING" in the main directory of this archive
5 * for more details.
6 *
7 * Copyright (C) 2001-2003 Silicon Graphics, Inc. All rights reserved.
8 */
9
10 #include <linux/types.h>
11 #include <linux/slab.h>
12 #include <linux/module.h>
13 #include <linux/byteorder/swab.h>
14 #include <asm/sn/sgi.h>
15 #include <asm/sn/sn_cpuid.h>
16 #include <asm/sn/addrs.h>
17 #include <asm/sn/arch.h>
18 #include <asm/sn/iograph.h>
19 #include <asm/sn/invent.h>
20 #include <asm/sn/hcl.h>
21 #include <asm/sn/labelcl.h>
22 #include <asm/sn/xtalk/xwidget.h>
23 #include <asm/sn/pci/bridge.h>
24 #include <asm/sn/pci/pciio.h>
25 #include <asm/sn/pci/pcibr.h>
26 #include <asm/sn/pci/pcibr_private.h>
27 #include <asm/sn/pci/pci_defs.h>
28 #include <asm/sn/prio.h>
29 #include <asm/sn/xtalk/xbow.h>
30 #include <asm/sn/ioc3.h>
31 #include <asm/sn/io.h>
32 #include <asm/sn/sn_private.h>
33
34 extern pcibr_info_t pcibr_info_get(vertex_hdl_t);
35
36 uint64_t pcibr_config_get(vertex_hdl_t, unsigned, unsigned);
37 uint64_t do_pcibr_config_get(cfg_p, unsigned, unsigned);
38 void pcibr_config_set(vertex_hdl_t, unsigned, unsigned, uint64_t);
39 void do_pcibr_config_set(cfg_p, unsigned, unsigned, uint64_t);
40
41 /*
42 * on sn-ia we need to twiddle the the addresses going out
43 * the pci bus because we use the unswizzled synergy space
44 * (the alternative is to use the swizzled synergy space
45 * and byte swap the data)
46 */
47 #define CB(b,r) (((volatile uint8_t *) b)[((r)^4)])
48 #define CS(b,r) (((volatile uint16_t *) b)[((r^4)/2)])
49 #define CW(b,r) (((volatile uint32_t *) b)[((r^4)/4)])
50
51 #define CBP(b,r) (((volatile uint8_t *) b)[(r)])
52 #define CSP(b,r) (((volatile uint16_t *) b)[((r)/2)])
53 #define CWP(b,r) (((volatile uint32_t *) b)[(r)/4])
54
55 #define SCB(b,r) (((volatile uint8_t *) b)[((r)^3)])
56 #define SCS(b,r) (((volatile uint16_t *) b)[((r^2)/2)])
57 #define SCW(b,r) (((volatile uint32_t *) b)[((r)/4)])
58
59 /*
60 * Return a config space address for given slot / func / offset. Note the
61 * returned pointer is a 32bit word (ie. cfg_p) aligned pointer pointing to
62 * the 32bit word that contains the "offset" byte.
63 */
64 cfg_p
pcibr_func_config_addr(bridge_t * bridge,pciio_bus_t bus,pciio_slot_t slot,pciio_function_t func,int offset)65 pcibr_func_config_addr(bridge_t *bridge, pciio_bus_t bus, pciio_slot_t slot,
66 pciio_function_t func, int offset)
67 {
68 /*
69 * Type 1 config space
70 */
71 if (bus > 0) {
72 bridge->b_pci_cfg = ((bus << 16) | (slot << 11));
73 return &bridge->b_type1_cfg.f[func].l[(offset)];
74 }
75
76 /*
77 * Type 0 config space
78 */
79 slot++;
80 return &bridge->b_type0_cfg_dev[slot].f[func].l[offset];
81 }
82
83 /*
84 * Return config space address for given slot / offset. Note the returned
85 * pointer is a 32bit word (ie. cfg_p) aligned pointer pointing to the
86 * 32bit word that contains the "offset" byte.
87 */
88 cfg_p
pcibr_slot_config_addr(bridge_t * bridge,pciio_slot_t slot,int offset)89 pcibr_slot_config_addr(bridge_t *bridge, pciio_slot_t slot, int offset)
90 {
91 return pcibr_func_config_addr(bridge, 0, slot, 0, offset);
92 }
93
94 /*
95 * Return config space data for given slot / offset
96 */
97 unsigned
pcibr_slot_config_get(bridge_t * bridge,pciio_slot_t slot,int offset)98 pcibr_slot_config_get(bridge_t *bridge, pciio_slot_t slot, int offset)
99 {
100 cfg_p cfg_base;
101
102 cfg_base = pcibr_slot_config_addr(bridge, slot, 0);
103 return (do_pcibr_config_get(cfg_base, offset, sizeof(unsigned)));
104 }
105
106 /*
107 * Return config space data for given slot / func / offset
108 */
109 unsigned
pcibr_func_config_get(bridge_t * bridge,pciio_slot_t slot,pciio_function_t func,int offset)110 pcibr_func_config_get(bridge_t *bridge, pciio_slot_t slot,
111 pciio_function_t func, int offset)
112 {
113 cfg_p cfg_base;
114
115 cfg_base = pcibr_func_config_addr(bridge, 0, slot, func, 0);
116 return (do_pcibr_config_get(cfg_base, offset, sizeof(unsigned)));
117 }
118
119 /*
120 * Set config space data for given slot / offset
121 */
122 void
pcibr_slot_config_set(bridge_t * bridge,pciio_slot_t slot,int offset,unsigned val)123 pcibr_slot_config_set(bridge_t *bridge, pciio_slot_t slot,
124 int offset, unsigned val)
125 {
126 cfg_p cfg_base;
127
128 cfg_base = pcibr_slot_config_addr(bridge, slot, 0);
129 do_pcibr_config_set(cfg_base, offset, sizeof(unsigned), val);
130 }
131
132 /*
133 * Set config space data for given slot / func / offset
134 */
135 void
pcibr_func_config_set(bridge_t * bridge,pciio_slot_t slot,pciio_function_t func,int offset,unsigned val)136 pcibr_func_config_set(bridge_t *bridge, pciio_slot_t slot,
137 pciio_function_t func, int offset, unsigned val)
138 {
139 cfg_p cfg_base;
140
141 cfg_base = pcibr_func_config_addr(bridge, 0, slot, func, 0);
142 do_pcibr_config_set(cfg_base, offset, sizeof(unsigned), val);
143 }
144
145 int pcibr_config_debug = 0;
146
147 cfg_p
pcibr_config_addr(vertex_hdl_t conn,unsigned reg)148 pcibr_config_addr(vertex_hdl_t conn,
149 unsigned reg)
150 {
151 pcibr_info_t pcibr_info;
152 pciio_bus_t pciio_bus;
153 pciio_slot_t pciio_slot;
154 pciio_function_t pciio_func;
155 pcibr_soft_t pcibr_soft;
156 bridge_t *bridge;
157 cfg_p cfgbase = (cfg_p)0;
158 pciio_info_t pciio_info;
159
160 pciio_info = pciio_info_get(conn);
161 pcibr_info = pcibr_info_get(conn);
162
163 /*
164 * Determine the PCI bus/slot/func to generate a config address for.
165 */
166
167 if (pciio_info_type1_get(pciio_info)) {
168 /*
169 * Conn is a vhdl which uses TYPE 1 addressing explicitly passed
170 * in reg.
171 */
172 pciio_bus = PCI_TYPE1_BUS(reg);
173 pciio_slot = PCI_TYPE1_SLOT(reg);
174 pciio_func = PCI_TYPE1_FUNC(reg);
175
176 ASSERT(pciio_bus != 0);
177 } else {
178 /*
179 * Conn is directly connected to the host bus. PCI bus number is
180 * hardcoded to 0 (even though it may have a logical bus number != 0)
181 * and slot/function are derived from the pcibr_info_t associated
182 * with the device.
183 */
184 pciio_bus = 0;
185
186 pciio_slot = PCIBR_INFO_SLOT_GET_INT(pcibr_info);
187 if (pciio_slot == PCIIO_SLOT_NONE)
188 pciio_slot = PCI_TYPE1_SLOT(reg);
189
190 pciio_func = pcibr_info->f_func;
191 if (pciio_func == PCIIO_FUNC_NONE)
192 pciio_func = PCI_TYPE1_FUNC(reg);
193 }
194
195 pcibr_soft = (pcibr_soft_t) pcibr_info->f_mfast;
196
197 bridge = pcibr_soft->bs_base;
198
199 cfgbase = pcibr_func_config_addr(bridge,
200 pciio_bus, pciio_slot, pciio_func, 0);
201
202 return cfgbase;
203 }
204
205 uint64_t
pcibr_config_get(vertex_hdl_t conn,unsigned reg,unsigned size)206 pcibr_config_get(vertex_hdl_t conn,
207 unsigned reg,
208 unsigned size)
209 {
210 return do_pcibr_config_get(pcibr_config_addr(conn, reg),
211 PCI_TYPE1_REG(reg), size);
212 }
213
214 uint64_t
do_pcibr_config_get(cfg_p cfgbase,unsigned reg,unsigned size)215 do_pcibr_config_get(cfg_p cfgbase,
216 unsigned reg,
217 unsigned size)
218 {
219 unsigned value;
220
221 value = CWP(cfgbase, reg);
222 if (reg & 3)
223 value >>= 8 * (reg & 3);
224 if (size < 4)
225 value &= (1 << (8 * size)) - 1;
226 return value;
227 }
228
229 void
pcibr_config_set(vertex_hdl_t conn,unsigned reg,unsigned size,uint64_t value)230 pcibr_config_set(vertex_hdl_t conn,
231 unsigned reg,
232 unsigned size,
233 uint64_t value)
234 {
235 do_pcibr_config_set(pcibr_config_addr(conn, reg),
236 PCI_TYPE1_REG(reg), size, value);
237 }
238
239 void
do_pcibr_config_set(cfg_p cfgbase,unsigned reg,unsigned size,uint64_t value)240 do_pcibr_config_set(cfg_p cfgbase,
241 unsigned reg,
242 unsigned size,
243 uint64_t value)
244 {
245 switch (size) {
246 case 1:
247 CBP(cfgbase, reg) = value;
248 break;
249 case 2:
250 if (reg & 1) {
251 CBP(cfgbase, reg) = value;
252 CBP(cfgbase, reg + 1) = value >> 8;
253 } else
254 CSP(cfgbase, reg) = value;
255 break;
256 case 3:
257 if (reg & 1) {
258 CBP(cfgbase, reg) = value;
259 CSP(cfgbase, (reg + 1)) = value >> 8;
260 } else {
261 CSP(cfgbase, reg) = value;
262 CBP(cfgbase, reg + 2) = value >> 16;
263 }
264 break;
265 case 4:
266 CWP(cfgbase, reg) = value;
267 break;
268 }
269 }
270