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