import of upstream 2.4.34.4 from kernel.org
[linux-2.4.git] / arch / ia64 / sn / io / sn2 / pcibr / pcibr_config.c
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
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
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
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
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
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
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
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
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
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
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
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 }