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