ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / arch / ia64 / sn / io / sn2 / pcibr / pcibr_config.c
1 /*
2  * This file is subject to the terms and conditions of the GNU General Public
3  * License.  See the file "COPYING" in the main directory of this archive
4  * for more details.
5  *
6  * Copyright (C) 2001-2003 Silicon Graphics, Inc. All rights reserved.
7  */
8
9 #include <linux/types.h>
10 #include <asm/sn/sgi.h>
11 #include <asm/sn/pci/pciio.h>
12 #include <asm/sn/pci/pcibr.h>
13 #include <asm/sn/pci/pcibr_private.h>
14 #include <asm/sn/pci/pci_defs.h>
15
16 extern pcibr_info_t      pcibr_info_get(vertex_hdl_t);
17
18 uint64_t          pcibr_config_get(vertex_hdl_t, unsigned, unsigned);
19 uint64_t          do_pcibr_config_get(cfg_p, unsigned, unsigned);
20 void              pcibr_config_set(vertex_hdl_t, unsigned, unsigned, uint64_t);
21 void              do_pcibr_config_set(cfg_p, unsigned, unsigned, uint64_t);
22
23 /*
24  * fancy snia bit twiddling....
25  */
26 #define CBP(b,r) (((volatile uint8_t *) b)[(r)])
27 #define CSP(b,r) (((volatile uint16_t *) b)[((r)/2)])
28 #define CWP(b,r) (((volatile uint32_t *) b)[(r)/4])
29
30 /*
31  * Return a config space address for given slot / func / offset.  Note the
32  * returned pointer is a 32bit word (ie. cfg_p) aligned pointer pointing to
33  * the 32bit word that contains the "offset" byte.
34  */
35 cfg_p
36 pcibr_func_config_addr(pcibr_soft_t soft, pciio_bus_t bus, pciio_slot_t slot, 
37                                         pciio_function_t func, int offset)
38 {
39         /*
40          * Type 1 config space
41          */
42         if (bus > 0) {
43                 pcireg_type1_cntr_set(soft, ((bus << 16) | (slot << 11)));
44                 return (pcireg_type1_cfg_addr(soft, func, offset));
45         }
46
47         /*
48          * Type 0 config space
49          */
50         return (pcireg_type0_cfg_addr(soft, slot, func, offset));
51 }
52
53 /*
54  * Return config space address for given slot / offset.  Note the returned
55  * pointer is a 32bit word (ie. cfg_p) aligned pointer pointing to the
56  * 32bit word that contains the "offset" byte.
57  */
58 cfg_p
59 pcibr_slot_config_addr(pcibr_soft_t soft, pciio_slot_t slot, int offset)
60 {
61         return pcibr_func_config_addr(soft, 0, slot, 0, offset);
62 }
63
64 /*
65  * Set config space data for given slot / func / offset
66  */
67 void
68 pcibr_func_config_set(pcibr_soft_t soft, pciio_slot_t slot, 
69                         pciio_function_t func, int offset, unsigned val)
70 {
71         cfg_p  cfg_base;
72
73         cfg_base = pcibr_func_config_addr(soft, 0, slot, func, 0);
74         do_pcibr_config_set(cfg_base, offset, sizeof(unsigned), val);
75 }
76
77 int pcibr_config_debug = 0;
78
79 cfg_p
80 pcibr_config_addr(vertex_hdl_t conn,
81                   unsigned reg)
82 {
83     pcibr_info_t            pcibr_info;
84     pciio_bus_t             pciio_bus;
85     pciio_slot_t            pciio_slot;
86     pciio_function_t        pciio_func;
87     cfg_p                   cfgbase = (cfg_p)0;
88     pciio_info_t            pciio_info;
89
90     pciio_info = pciio_info_get(conn);
91     pcibr_info = pcibr_info_get(conn);
92
93     /*
94      * Determine the PCI bus/slot/func to generate a config address for.
95      */
96
97     if (pciio_info_type1_get(pciio_info)) {
98         /*
99          * Conn is a vhdl which uses TYPE 1 addressing explicitly passed 
100          * in reg.
101          */
102         pciio_bus = PCI_TYPE1_BUS(reg);
103         pciio_slot = PCI_TYPE1_SLOT(reg);
104         pciio_func = PCI_TYPE1_FUNC(reg);
105
106         ASSERT(pciio_bus != 0);
107     } else {
108         /*
109          * Conn is directly connected to the host bus.  PCI bus number is
110          * hardcoded to 0 (even though it may have a logical bus number != 0)
111          * and slot/function are derived from the pcibr_info_t associated
112          * with the device.
113          */
114         pciio_bus = 0;
115
116     pciio_slot = PCIBR_INFO_SLOT_GET_INT(pcibr_info);
117     if (pciio_slot == PCIIO_SLOT_NONE)
118         pciio_slot = PCI_TYPE1_SLOT(reg);
119
120     pciio_func = pcibr_info->f_func;
121     if (pciio_func == PCIIO_FUNC_NONE)
122         pciio_func = PCI_TYPE1_FUNC(reg);
123     }
124
125     cfgbase = pcibr_func_config_addr((pcibr_soft_t) pcibr_info->f_mfast,
126                         pciio_bus, pciio_slot, pciio_func, 0);
127
128     return cfgbase;
129 }
130
131 uint64_t
132 pcibr_config_get(vertex_hdl_t conn,
133                  unsigned reg,
134                  unsigned size)
135 {
136         return do_pcibr_config_get(pcibr_config_addr(conn, reg),
137                                 PCI_TYPE1_REG(reg), size);
138 }
139
140 uint64_t
141 do_pcibr_config_get(cfg_p cfgbase,
142                        unsigned reg,
143                        unsigned size)
144 {
145     unsigned                value;
146
147     value = CWP(cfgbase, reg);
148     if (reg & 3)
149         value >>= 8 * (reg & 3);
150     if (size < 4)
151         value &= (1 << (8 * size)) - 1;
152     return value;
153 }
154
155 void
156 pcibr_config_set(vertex_hdl_t conn,
157                  unsigned reg,
158                  unsigned size,
159                  uint64_t value)
160 {
161         do_pcibr_config_set(pcibr_config_addr(conn, reg),
162                         PCI_TYPE1_REG(reg), size, value);
163 }
164
165 void
166 do_pcibr_config_set(cfg_p cfgbase,
167                     unsigned reg,
168                     unsigned size,
169                     uint64_t value)
170 {
171         switch (size) {
172         case 1:
173                 CBP(cfgbase, reg) = value;
174                 break;
175         case 2:
176                 if (reg & 1) {
177                         CBP(cfgbase, reg) = value;
178                         CBP(cfgbase, reg + 1) = value >> 8;
179                 } else
180                         CSP(cfgbase, reg) = value;
181                 break;
182         case 3:
183                 if (reg & 1) {
184                         CBP(cfgbase, reg) = value;
185                         CSP(cfgbase, (reg + 1)) = value >> 8;
186                 } else {
187                         CSP(cfgbase, reg) = value;
188                         CBP(cfgbase, reg + 2) = value >> 16;
189                 }
190                 break;
191         case 4:
192                 CWP(cfgbase, reg) = value;
193                 break;
194         }
195 }