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