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