]> git.karo-electronics.de Git - mv-sheeva.git/blob - drivers/xen/xen-pciback/conf_space_header.c
3ae7da137f7e61358c0753da103a62979cc29cd9
[mv-sheeva.git] / drivers / xen / xen-pciback / conf_space_header.c
1 /*
2  * PCI Backend - Handles the virtual fields in the configuration space headers.
3  *
4  * Author: Ryan Wilson <hap9@epoch.ncsc.mil>
5  */
6
7 #include <linux/kernel.h>
8 #include <linux/pci.h>
9 #include "pciback.h"
10 #include "conf_space.h"
11
12 struct pci_bar_info {
13         u32 val;
14         u32 len_val;
15         int which;
16 };
17
18 #define is_enable_cmd(value) ((value)&(PCI_COMMAND_MEMORY|PCI_COMMAND_IO))
19 #define is_master_cmd(value) ((value)&PCI_COMMAND_MASTER)
20
21 static int command_write(struct pci_dev *dev, int offset, u16 value, void *data)
22 {
23         int err;
24
25         if (!pci_is_enabled(dev) && is_enable_cmd(value)) {
26                 if (unlikely(verbose_request))
27                         printk(KERN_DEBUG "pciback: %s: enable\n",
28                                pci_name(dev));
29                 err = pci_enable_device(dev);
30                 if (err)
31                         return err;
32         } else if (pci_is_enabled(dev) && !is_enable_cmd(value)) {
33                 if (unlikely(verbose_request))
34                         printk(KERN_DEBUG "pciback: %s: disable\n",
35                                pci_name(dev));
36                 pci_disable_device(dev);
37         }
38
39         if (!dev->is_busmaster && is_master_cmd(value)) {
40                 if (unlikely(verbose_request))
41                         printk(KERN_DEBUG "pciback: %s: set bus master\n",
42                                pci_name(dev));
43                 pci_set_master(dev);
44         }
45
46         if (value & PCI_COMMAND_INVALIDATE) {
47                 if (unlikely(verbose_request))
48                         printk(KERN_DEBUG
49                                "pciback: %s: enable memory-write-invalidate\n",
50                                pci_name(dev));
51                 err = pci_set_mwi(dev);
52                 if (err) {
53                         printk(KERN_WARNING
54                                "pciback: %s: cannot enable "
55                                "memory-write-invalidate (%d)\n",
56                                pci_name(dev), err);
57                         value &= ~PCI_COMMAND_INVALIDATE;
58                 }
59         }
60
61         return pci_write_config_word(dev, offset, value);
62 }
63
64 static int rom_write(struct pci_dev *dev, int offset, u32 value, void *data)
65 {
66         struct pci_bar_info *bar = data;
67
68         if (unlikely(!bar)) {
69                 printk(KERN_WARNING "pciback: driver data not found for %s\n",
70                        pci_name(dev));
71                 return XEN_PCI_ERR_op_failed;
72         }
73
74         /* A write to obtain the length must happen as a 32-bit write.
75          * This does not (yet) support writing individual bytes
76          */
77         if (value == ~PCI_ROM_ADDRESS_ENABLE)
78                 bar->which = 1;
79         else {
80                 u32 tmpval;
81                 pci_read_config_dword(dev, offset, &tmpval);
82                 if (tmpval != bar->val && value == bar->val) {
83                         /* Allow restoration of bar value. */
84                         pci_write_config_dword(dev, offset, bar->val);
85                 }
86                 bar->which = 0;
87         }
88
89         /* Do we need to support enabling/disabling the rom address here? */
90
91         return 0;
92 }
93
94 /* For the BARs, only allow writes which write ~0 or
95  * the correct resource information
96  * (Needed for when the driver probes the resource usage)
97  */
98 static int bar_write(struct pci_dev *dev, int offset, u32 value, void *data)
99 {
100         struct pci_bar_info *bar = data;
101
102         if (unlikely(!bar)) {
103                 printk(KERN_WARNING "pciback: driver data not found for %s\n",
104                        pci_name(dev));
105                 return XEN_PCI_ERR_op_failed;
106         }
107
108         /* A write to obtain the length must happen as a 32-bit write.
109          * This does not (yet) support writing individual bytes
110          */
111         if (value == ~0)
112                 bar->which = 1;
113         else {
114                 u32 tmpval;
115                 pci_read_config_dword(dev, offset, &tmpval);
116                 if (tmpval != bar->val && value == bar->val) {
117                         /* Allow restoration of bar value. */
118                         pci_write_config_dword(dev, offset, bar->val);
119                 }
120                 bar->which = 0;
121         }
122
123         return 0;
124 }
125
126 static int bar_read(struct pci_dev *dev, int offset, u32 * value, void *data)
127 {
128         struct pci_bar_info *bar = data;
129
130         if (unlikely(!bar)) {
131                 printk(KERN_WARNING "pciback: driver data not found for %s\n",
132                        pci_name(dev));
133                 return XEN_PCI_ERR_op_failed;
134         }
135
136         *value = bar->which ? bar->len_val : bar->val;
137
138         return 0;
139 }
140
141 static inline void read_dev_bar(struct pci_dev *dev,
142                                 struct pci_bar_info *bar_info, int offset,
143                                 u32 len_mask)
144 {
145         pci_read_config_dword(dev, offset, &bar_info->val);
146         pci_write_config_dword(dev, offset, len_mask);
147         pci_read_config_dword(dev, offset, &bar_info->len_val);
148         pci_write_config_dword(dev, offset, bar_info->val);
149 }
150
151 static void *bar_init(struct pci_dev *dev, int offset)
152 {
153         struct pci_bar_info *bar = kmalloc(sizeof(*bar), GFP_KERNEL);
154
155         if (!bar)
156                 return ERR_PTR(-ENOMEM);
157
158         read_dev_bar(dev, bar, offset, ~0);
159         bar->which = 0;
160
161         return bar;
162 }
163
164 static void *rom_init(struct pci_dev *dev, int offset)
165 {
166         struct pci_bar_info *bar = kmalloc(sizeof(*bar), GFP_KERNEL);
167
168         if (!bar)
169                 return ERR_PTR(-ENOMEM);
170
171         read_dev_bar(dev, bar, offset, ~PCI_ROM_ADDRESS_ENABLE);
172         bar->which = 0;
173
174         return bar;
175 }
176
177 static void bar_reset(struct pci_dev *dev, int offset, void *data)
178 {
179         struct pci_bar_info *bar = data;
180
181         bar->which = 0;
182 }
183
184 static void bar_release(struct pci_dev *dev, int offset, void *data)
185 {
186         kfree(data);
187 }
188
189 static int interrupt_read(struct pci_dev *dev, int offset, u8 * value,
190                           void *data)
191 {
192         *value = (u8) dev->irq;
193
194         return 0;
195 }
196
197 static int bist_write(struct pci_dev *dev, int offset, u8 value, void *data)
198 {
199         u8 cur_value;
200         int err;
201
202         err = pci_read_config_byte(dev, offset, &cur_value);
203         if (err)
204                 goto out;
205
206         if ((cur_value & ~PCI_BIST_START) == (value & ~PCI_BIST_START)
207             || value == PCI_BIST_START)
208                 err = pci_write_config_byte(dev, offset, value);
209
210 out:
211         return err;
212 }
213
214 static const struct config_field header_common[] = {
215         {
216          .offset    = PCI_COMMAND,
217          .size      = 2,
218          .u.w.read  = pciback_read_config_word,
219          .u.w.write = command_write,
220         },
221         {
222          .offset    = PCI_INTERRUPT_LINE,
223          .size      = 1,
224          .u.b.read  = interrupt_read,
225         },
226         {
227          .offset    = PCI_INTERRUPT_PIN,
228          .size      = 1,
229          .u.b.read  = pciback_read_config_byte,
230         },
231         {
232          /* Any side effects of letting driver domain control cache line? */
233          .offset    = PCI_CACHE_LINE_SIZE,
234          .size      = 1,
235          .u.b.read  = pciback_read_config_byte,
236          .u.b.write = pciback_write_config_byte,
237         },
238         {
239          .offset    = PCI_LATENCY_TIMER,
240          .size      = 1,
241          .u.b.read  = pciback_read_config_byte,
242         },
243         {
244          .offset    = PCI_BIST,
245          .size      = 1,
246          .u.b.read  = pciback_read_config_byte,
247          .u.b.write = bist_write,
248         },
249         {}
250 };
251
252 #define CFG_FIELD_BAR(reg_offset)                       \
253         {                                               \
254          .offset     = reg_offset,                      \
255          .size       = 4,                               \
256          .init       = bar_init,                        \
257          .reset      = bar_reset,                       \
258          .release    = bar_release,                     \
259          .u.dw.read  = bar_read,                        \
260          .u.dw.write = bar_write,                       \
261          }
262
263 #define CFG_FIELD_ROM(reg_offset)                       \
264         {                                               \
265          .offset     = reg_offset,                      \
266          .size       = 4,                               \
267          .init       = rom_init,                        \
268          .reset      = bar_reset,                       \
269          .release    = bar_release,                     \
270          .u.dw.read  = bar_read,                        \
271          .u.dw.write = rom_write,                       \
272          }
273
274 static const struct config_field header_0[] = {
275         CFG_FIELD_BAR(PCI_BASE_ADDRESS_0),
276         CFG_FIELD_BAR(PCI_BASE_ADDRESS_1),
277         CFG_FIELD_BAR(PCI_BASE_ADDRESS_2),
278         CFG_FIELD_BAR(PCI_BASE_ADDRESS_3),
279         CFG_FIELD_BAR(PCI_BASE_ADDRESS_4),
280         CFG_FIELD_BAR(PCI_BASE_ADDRESS_5),
281         CFG_FIELD_ROM(PCI_ROM_ADDRESS),
282         {}
283 };
284
285 static const struct config_field header_1[] = {
286         CFG_FIELD_BAR(PCI_BASE_ADDRESS_0),
287         CFG_FIELD_BAR(PCI_BASE_ADDRESS_1),
288         CFG_FIELD_ROM(PCI_ROM_ADDRESS1),
289         {}
290 };
291
292 int pciback_config_header_add_fields(struct pci_dev *dev)
293 {
294         int err;
295
296         err = pciback_config_add_fields(dev, header_common);
297         if (err)
298                 goto out;
299
300         switch (dev->hdr_type) {
301         case PCI_HEADER_TYPE_NORMAL:
302                 err = pciback_config_add_fields(dev, header_0);
303                 break;
304
305         case PCI_HEADER_TYPE_BRIDGE:
306                 err = pciback_config_add_fields(dev, header_1);
307                 break;
308
309         default:
310                 err = -EINVAL;
311                 printk(KERN_ERR "pciback: %s: Unsupported header type %d!\n",
312                        pci_name(dev), dev->hdr_type);
313                 break;
314         }
315
316 out:
317         return err;
318 }