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