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 } 370