53649c7d0068f19e4b66b7cf8a6012ad5519f7b3
[pandora-kernel.git] / arch / alpha / kernel / pci-sysfs.c
1 /*
2  * arch/alpha/kernel/pci-sysfs.c
3  *
4  * Copyright (C) 2009 Ivan Kokshaysky
5  *
6  * Alpha PCI resource files.
7  *
8  * Loosely based on generic HAVE_PCI_MMAP implementation in
9  * drivers/pci/pci-sysfs.c
10  */
11
12 #include <linux/sched.h>
13 #include <linux/stat.h>
14 #include <linux/slab.h>
15 #include <linux/pci.h>
16
17 static int hose_mmap_page_range(struct pci_controller *hose,
18                                 struct vm_area_struct *vma,
19                                 enum pci_mmap_state mmap_type, int sparse)
20 {
21         unsigned long base;
22
23         if (mmap_type == pci_mmap_mem)
24                 base = sparse ? hose->sparse_mem_base : hose->dense_mem_base;
25         else
26                 base = sparse ? hose->sparse_io_base : hose->dense_io_base;
27
28         vma->vm_pgoff += base >> PAGE_SHIFT;
29         vma->vm_flags |= (VM_IO | VM_RESERVED);
30
31         return io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
32                                   vma->vm_end - vma->vm_start,
33                                   vma->vm_page_prot);
34 }
35
36 static int __pci_mmap_fits(struct pci_dev *pdev, int num,
37                            struct vm_area_struct *vma, int sparse)
38 {
39         unsigned long nr, start, size;
40         int shift = sparse ? 5 : 0;
41
42         nr = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
43         start = vma->vm_pgoff;
44         size = ((pci_resource_len(pdev, num) - 1) >> (PAGE_SHIFT - shift)) + 1;
45
46         if (start < size && size - start >= nr)
47                 return 1;
48         WARN(1, "process \"%s\" tried to map%s 0x%08lx-0x%08lx on %s BAR %d "
49                 "(size 0x%08lx)\n",
50                 current->comm, sparse ? " sparse" : "", start, start + nr,
51                 pci_name(pdev), num, size);
52         return 0;
53 }
54
55 /**
56  * pci_mmap_resource - map a PCI resource into user memory space
57  * @kobj: kobject for mapping
58  * @attr: struct bin_attribute for the file being mapped
59  * @vma: struct vm_area_struct passed into the mmap
60  * @sparse: address space type
61  *
62  * Use the bus mapping routines to map a PCI resource into userspace.
63  */
64 static int pci_mmap_resource(struct kobject *kobj,
65                              struct bin_attribute *attr,
66                              struct vm_area_struct *vma, int sparse)
67 {
68         struct pci_dev *pdev = to_pci_dev(container_of(kobj,
69                                                        struct device, kobj));
70         struct resource *res = attr->private;
71         enum pci_mmap_state mmap_type;
72         struct pci_bus_region bar;
73         int i;
74
75         for (i = 0; i < PCI_ROM_RESOURCE; i++)
76                 if (res == &pdev->resource[i])
77                         break;
78         if (i >= PCI_ROM_RESOURCE)
79                 return -ENODEV;
80
81         if (!__pci_mmap_fits(pdev, i, vma, sparse))
82                 return -EINVAL;
83
84         if (iomem_is_exclusive(res->start))
85                 return -EINVAL;
86
87         pcibios_resource_to_bus(pdev, &bar, res);
88         vma->vm_pgoff += bar.start >> (PAGE_SHIFT - (sparse ? 5 : 0));
89         mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io;
90
91         return hose_mmap_page_range(pdev->sysdata, vma, mmap_type, sparse);
92 }
93
94 static int pci_mmap_resource_sparse(struct file *filp, struct kobject *kobj,
95                                     struct bin_attribute *attr,
96                                     struct vm_area_struct *vma)
97 {
98         return pci_mmap_resource(kobj, attr, vma, 1);
99 }
100
101 static int pci_mmap_resource_dense(struct file *filp, struct kobject *kobj,
102                                    struct bin_attribute *attr,
103                                    struct vm_area_struct *vma)
104 {
105         return pci_mmap_resource(kobj, attr, vma, 0);
106 }
107
108 /**
109  * pci_remove_resource_files - cleanup resource files
110  * @dev: dev to cleanup
111  *
112  * If we created resource files for @dev, remove them from sysfs and
113  * free their resources.
114  */
115 void pci_remove_resource_files(struct pci_dev *pdev)
116 {
117         int i;
118
119         for (i = 0; i < PCI_ROM_RESOURCE; i++) {
120                 struct bin_attribute *res_attr;
121
122                 res_attr = pdev->res_attr[i];
123                 if (res_attr) {
124                         sysfs_remove_bin_file(&pdev->dev.kobj, res_attr);
125                         kfree(res_attr);
126                 }
127
128                 res_attr = pdev->res_attr_wc[i];
129                 if (res_attr) {
130                         sysfs_remove_bin_file(&pdev->dev.kobj, res_attr);
131                         kfree(res_attr);
132                 }
133         }
134 }
135
136 static int sparse_mem_mmap_fits(struct pci_dev *pdev, int num)
137 {
138         struct pci_bus_region bar;
139         struct pci_controller *hose = pdev->sysdata;
140         long dense_offset;
141         unsigned long sparse_size;
142
143         pcibios_resource_to_bus(pdev, &bar, &pdev->resource[num]);
144
145         /* All core logic chips have 4G sparse address space, except
146            CIA which has 16G (see xxx_SPARSE_MEM and xxx_DENSE_MEM
147            definitions in asm/core_xxx.h files). This corresponds
148            to 128M or 512M of the bus space. */
149         dense_offset = (long)(hose->dense_mem_base - hose->sparse_mem_base);
150         sparse_size = dense_offset >= 0x400000000UL ? 0x20000000 : 0x8000000;
151
152         return bar.end < sparse_size;
153 }
154
155 static int pci_create_one_attr(struct pci_dev *pdev, int num, char *name,
156                                char *suffix, struct bin_attribute *res_attr,
157                                unsigned long sparse)
158 {
159         size_t size = pci_resource_len(pdev, num);
160
161         sprintf(name, "resource%d%s", num, suffix);
162         res_attr->mmap = sparse ? pci_mmap_resource_sparse :
163                                   pci_mmap_resource_dense;
164         res_attr->attr.name = name;
165         res_attr->attr.mode = S_IRUSR | S_IWUSR;
166         res_attr->size = sparse ? size << 5 : size;
167         res_attr->private = &pdev->resource[num];
168         return sysfs_create_bin_file(&pdev->dev.kobj, res_attr);
169 }
170
171 static int pci_create_attr(struct pci_dev *pdev, int num)
172 {
173         /* allocate attribute structure, piggyback attribute name */
174         int retval, nlen1, nlen2 = 0, res_count = 1;
175         unsigned long sparse_base, dense_base;
176         struct bin_attribute *attr;
177         struct pci_controller *hose = pdev->sysdata;
178         char *suffix, *attr_name;
179
180         suffix = "";    /* Assume bwx machine, normal resourceN files. */
181         nlen1 = 10;
182
183         if (pdev->resource[num].flags & IORESOURCE_MEM) {
184                 sparse_base = hose->sparse_mem_base;
185                 dense_base = hose->dense_mem_base;
186                 if (sparse_base && !sparse_mem_mmap_fits(pdev, num)) {
187                         sparse_base = 0;
188                         suffix = "_dense";
189                         nlen1 = 16;     /* resourceN_dense */
190                 }
191         } else {
192                 sparse_base = hose->sparse_io_base;
193                 dense_base = hose->dense_io_base;
194         }
195
196         if (sparse_base) {
197                 suffix = "_sparse";
198                 nlen1 = 17;
199                 if (dense_base) {
200                         nlen2 = 16;     /* resourceN_dense */
201                         res_count = 2;
202                 }
203         }
204
205         attr = kzalloc(sizeof(*attr) * res_count + nlen1 + nlen2, GFP_ATOMIC);
206         if (!attr)
207                 return -ENOMEM;
208
209         /* Create bwx, sparse or single dense file */
210         attr_name = (char *)(attr + res_count);
211         pdev->res_attr[num] = attr;
212         retval = pci_create_one_attr(pdev, num, attr_name, suffix, attr,
213                                      sparse_base);
214         if (retval || res_count == 1)
215                 return retval;
216
217         /* Create dense file */
218         attr_name += nlen1;
219         attr++;
220         pdev->res_attr_wc[num] = attr;
221         return pci_create_one_attr(pdev, num, attr_name, "_dense", attr, 0);
222 }
223
224 /**
225  * pci_create_resource_files - create resource files in sysfs for @dev
226  * @dev: dev in question
227  *
228  * Walk the resources in @dev creating files for each resource available.
229  */
230 int pci_create_resource_files(struct pci_dev *pdev)
231 {
232         int i;
233         int retval;
234
235         /* Expose the PCI resources from this device as files */
236         for (i = 0; i < PCI_ROM_RESOURCE; i++) {
237
238                 /* skip empty resources */
239                 if (!pci_resource_len(pdev, i))
240                         continue;
241
242                 retval = pci_create_attr(pdev, i);
243                 if (retval) {
244                         pci_remove_resource_files(pdev);
245                         return retval;
246                 }
247         }
248         return 0;
249 }
250
251 /* Legacy I/O bus mapping stuff. */
252
253 static int __legacy_mmap_fits(struct pci_controller *hose,
254                               struct vm_area_struct *vma,
255                               unsigned long res_size, int sparse)
256 {
257         unsigned long nr, start, size;
258
259         nr = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
260         start = vma->vm_pgoff;
261         size = ((res_size - 1) >> PAGE_SHIFT) + 1;
262
263         if (start < size && size - start >= nr)
264                 return 1;
265         WARN(1, "process \"%s\" tried to map%s 0x%08lx-0x%08lx on hose %d "
266                 "(size 0x%08lx)\n",
267                 current->comm, sparse ? " sparse" : "", start, start + nr,
268                 hose->index, size);
269         return 0;
270 }
271
272 static inline int has_sparse(struct pci_controller *hose,
273                              enum pci_mmap_state mmap_type)
274 {
275         unsigned long base;
276
277         base = (mmap_type == pci_mmap_mem) ? hose->sparse_mem_base :
278                                              hose->sparse_io_base;
279
280         return base != 0;
281 }
282
283 int pci_mmap_legacy_page_range(struct pci_bus *bus, struct vm_area_struct *vma,
284                                enum pci_mmap_state mmap_type)
285 {
286         struct pci_controller *hose = bus->sysdata;
287         int sparse = has_sparse(hose, mmap_type);
288         unsigned long res_size;
289
290         res_size = (mmap_type == pci_mmap_mem) ? bus->legacy_mem->size :
291                                                  bus->legacy_io->size;
292         if (!__legacy_mmap_fits(hose, vma, res_size, sparse))
293                 return -EINVAL;
294
295         return hose_mmap_page_range(hose, vma, mmap_type, sparse);
296 }
297
298 /**
299  * pci_adjust_legacy_attr - adjustment of legacy file attributes
300  * @b: bus to create files under
301  * @mmap_type: I/O port or memory
302  *
303  * Adjust file name and size for sparse mappings.
304  */
305 void pci_adjust_legacy_attr(struct pci_bus *bus, enum pci_mmap_state mmap_type)
306 {
307         struct pci_controller *hose = bus->sysdata;
308
309         if (!has_sparse(hose, mmap_type))
310                 return;
311
312         if (mmap_type == pci_mmap_mem) {
313                 bus->legacy_mem->attr.name = "legacy_mem_sparse";
314                 bus->legacy_mem->size <<= 5;
315         } else {
316                 bus->legacy_io->attr.name = "legacy_io_sparse";
317                 bus->legacy_io->size <<= 5;
318         }
319         return;
320 }
321
322 /* Legacy I/O bus read/write functions */
323 int pci_legacy_read(struct pci_bus *bus, loff_t port, u32 *val, size_t size)
324 {
325         struct pci_controller *hose = bus->sysdata;
326
327         port += hose->io_space->start;
328
329         switch(size) {
330         case 1:
331                 *((u8 *)val) = inb(port);
332                 return 1;
333         case 2:
334                 if (port & 1)
335                         return -EINVAL;
336                 *((u16 *)val) = inw(port);
337                 return 2;
338         case 4:
339                 if (port & 3)
340                         return -EINVAL;
341                 *((u32 *)val) = inl(port);
342                 return 4;
343         }
344         return -EINVAL;
345 }
346
347 int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val, size_t size)
348 {
349         struct pci_controller *hose = bus->sysdata;
350
351         port += hose->io_space->start;
352
353         switch(size) {
354         case 1:
355                 outb(port, val);
356                 return 1;
357         case 2:
358                 if (port & 1)
359                         return -EINVAL;
360                 outw(port, val);
361                 return 2;
362         case 4:
363                 if (port & 3)
364                         return -EINVAL;
365                 outl(port, val);
366                 return 4;
367         }
368         return -EINVAL;
369 }