diff options
Diffstat (limited to 'drivers/pci/pci-sysfs.c')
-rw-r--r-- | drivers/pci/pci-sysfs.c | 241 |
1 files changed, 176 insertions, 65 deletions
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 77baff022f71..110022d78689 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c | |||
@@ -423,7 +423,7 @@ pci_write_vpd(struct kobject *kobj, struct bin_attribute *bin_attr, | |||
423 | * Reads 1, 2, or 4 bytes from legacy I/O port space using an arch specific | 423 | * Reads 1, 2, or 4 bytes from legacy I/O port space using an arch specific |
424 | * callback routine (pci_legacy_read). | 424 | * callback routine (pci_legacy_read). |
425 | */ | 425 | */ |
426 | ssize_t | 426 | static ssize_t |
427 | pci_read_legacy_io(struct kobject *kobj, struct bin_attribute *bin_attr, | 427 | pci_read_legacy_io(struct kobject *kobj, struct bin_attribute *bin_attr, |
428 | char *buf, loff_t off, size_t count) | 428 | char *buf, loff_t off, size_t count) |
429 | { | 429 | { |
@@ -448,7 +448,7 @@ pci_read_legacy_io(struct kobject *kobj, struct bin_attribute *bin_attr, | |||
448 | * Writes 1, 2, or 4 bytes from legacy I/O port space using an arch specific | 448 | * Writes 1, 2, or 4 bytes from legacy I/O port space using an arch specific |
449 | * callback routine (pci_legacy_write). | 449 | * callback routine (pci_legacy_write). |
450 | */ | 450 | */ |
451 | ssize_t | 451 | static ssize_t |
452 | pci_write_legacy_io(struct kobject *kobj, struct bin_attribute *bin_attr, | 452 | pci_write_legacy_io(struct kobject *kobj, struct bin_attribute *bin_attr, |
453 | char *buf, loff_t off, size_t count) | 453 | char *buf, loff_t off, size_t count) |
454 | { | 454 | { |
@@ -468,11 +468,11 @@ pci_write_legacy_io(struct kobject *kobj, struct bin_attribute *bin_attr, | |||
468 | * @attr: struct bin_attribute for this file | 468 | * @attr: struct bin_attribute for this file |
469 | * @vma: struct vm_area_struct passed to mmap | 469 | * @vma: struct vm_area_struct passed to mmap |
470 | * | 470 | * |
471 | * Uses an arch specific callback, pci_mmap_legacy_page_range, to mmap | 471 | * Uses an arch specific callback, pci_mmap_legacy_mem_page_range, to mmap |
472 | * legacy memory space (first meg of bus space) into application virtual | 472 | * legacy memory space (first meg of bus space) into application virtual |
473 | * memory space. | 473 | * memory space. |
474 | */ | 474 | */ |
475 | int | 475 | static int |
476 | pci_mmap_legacy_mem(struct kobject *kobj, struct bin_attribute *attr, | 476 | pci_mmap_legacy_mem(struct kobject *kobj, struct bin_attribute *attr, |
477 | struct vm_area_struct *vma) | 477 | struct vm_area_struct *vma) |
478 | { | 478 | { |
@@ -480,7 +480,90 @@ pci_mmap_legacy_mem(struct kobject *kobj, struct bin_attribute *attr, | |||
480 | struct device, | 480 | struct device, |
481 | kobj)); | 481 | kobj)); |
482 | 482 | ||
483 | return pci_mmap_legacy_page_range(bus, vma); | 483 | return pci_mmap_legacy_page_range(bus, vma, pci_mmap_mem); |
484 | } | ||
485 | |||
486 | /** | ||
487 | * pci_mmap_legacy_io - map legacy PCI IO into user memory space | ||
488 | * @kobj: kobject corresponding to device to be mapped | ||
489 | * @attr: struct bin_attribute for this file | ||
490 | * @vma: struct vm_area_struct passed to mmap | ||
491 | * | ||
492 | * Uses an arch specific callback, pci_mmap_legacy_io_page_range, to mmap | ||
493 | * legacy IO space (first meg of bus space) into application virtual | ||
494 | * memory space. Returns -ENOSYS if the operation isn't supported | ||
495 | */ | ||
496 | static int | ||
497 | pci_mmap_legacy_io(struct kobject *kobj, struct bin_attribute *attr, | ||
498 | struct vm_area_struct *vma) | ||
499 | { | ||
500 | struct pci_bus *bus = to_pci_bus(container_of(kobj, | ||
501 | struct device, | ||
502 | kobj)); | ||
503 | |||
504 | return pci_mmap_legacy_page_range(bus, vma, pci_mmap_io); | ||
505 | } | ||
506 | |||
507 | /** | ||
508 | * pci_create_legacy_files - create legacy I/O port and memory files | ||
509 | * @b: bus to create files under | ||
510 | * | ||
511 | * Some platforms allow access to legacy I/O port and ISA memory space on | ||
512 | * a per-bus basis. This routine creates the files and ties them into | ||
513 | * their associated read, write and mmap files from pci-sysfs.c | ||
514 | * | ||
515 | * On error unwind, but don't propogate the error to the caller | ||
516 | * as it is ok to set up the PCI bus without these files. | ||
517 | */ | ||
518 | void pci_create_legacy_files(struct pci_bus *b) | ||
519 | { | ||
520 | int error; | ||
521 | |||
522 | b->legacy_io = kzalloc(sizeof(struct bin_attribute) * 2, | ||
523 | GFP_ATOMIC); | ||
524 | if (!b->legacy_io) | ||
525 | goto kzalloc_err; | ||
526 | |||
527 | b->legacy_io->attr.name = "legacy_io"; | ||
528 | b->legacy_io->size = 0xffff; | ||
529 | b->legacy_io->attr.mode = S_IRUSR | S_IWUSR; | ||
530 | b->legacy_io->read = pci_read_legacy_io; | ||
531 | b->legacy_io->write = pci_write_legacy_io; | ||
532 | b->legacy_io->mmap = pci_mmap_legacy_io; | ||
533 | error = device_create_bin_file(&b->dev, b->legacy_io); | ||
534 | if (error) | ||
535 | goto legacy_io_err; | ||
536 | |||
537 | /* Allocated above after the legacy_io struct */ | ||
538 | b->legacy_mem = b->legacy_io + 1; | ||
539 | b->legacy_mem->attr.name = "legacy_mem"; | ||
540 | b->legacy_mem->size = 1024*1024; | ||
541 | b->legacy_mem->attr.mode = S_IRUSR | S_IWUSR; | ||
542 | b->legacy_mem->mmap = pci_mmap_legacy_mem; | ||
543 | error = device_create_bin_file(&b->dev, b->legacy_mem); | ||
544 | if (error) | ||
545 | goto legacy_mem_err; | ||
546 | |||
547 | return; | ||
548 | |||
549 | legacy_mem_err: | ||
550 | device_remove_bin_file(&b->dev, b->legacy_io); | ||
551 | legacy_io_err: | ||
552 | kfree(b->legacy_io); | ||
553 | b->legacy_io = NULL; | ||
554 | kzalloc_err: | ||
555 | printk(KERN_WARNING "pci: warning: could not create legacy I/O port " | ||
556 | "and ISA memory resources to sysfs\n"); | ||
557 | return; | ||
558 | } | ||
559 | |||
560 | void pci_remove_legacy_files(struct pci_bus *b) | ||
561 | { | ||
562 | if (b->legacy_io) { | ||
563 | device_remove_bin_file(&b->dev, b->legacy_io); | ||
564 | device_remove_bin_file(&b->dev, b->legacy_mem); | ||
565 | kfree(b->legacy_io); /* both are allocated here */ | ||
566 | } | ||
484 | } | 567 | } |
485 | #endif /* HAVE_PCI_LEGACY */ | 568 | #endif /* HAVE_PCI_LEGACY */ |
486 | 569 | ||
@@ -715,7 +798,7 @@ static struct bin_attribute pci_config_attr = { | |||
715 | .name = "config", | 798 | .name = "config", |
716 | .mode = S_IRUGO | S_IWUSR, | 799 | .mode = S_IRUGO | S_IWUSR, |
717 | }, | 800 | }, |
718 | .size = 256, | 801 | .size = PCI_CFG_SPACE_SIZE, |
719 | .read = pci_read_config, | 802 | .read = pci_read_config, |
720 | .write = pci_write_config, | 803 | .write = pci_write_config, |
721 | }; | 804 | }; |
@@ -725,7 +808,7 @@ static struct bin_attribute pcie_config_attr = { | |||
725 | .name = "config", | 808 | .name = "config", |
726 | .mode = S_IRUGO | S_IWUSR, | 809 | .mode = S_IRUGO | S_IWUSR, |
727 | }, | 810 | }, |
728 | .size = 4096, | 811 | .size = PCI_CFG_SPACE_EXP_SIZE, |
729 | .read = pci_read_config, | 812 | .read = pci_read_config, |
730 | .write = pci_write_config, | 813 | .write = pci_write_config, |
731 | }; | 814 | }; |
@@ -735,86 +818,103 @@ int __attribute__ ((weak)) pcibios_add_platform_entries(struct pci_dev *dev) | |||
735 | return 0; | 818 | return 0; |
736 | } | 819 | } |
737 | 820 | ||
821 | static int pci_create_capabilities_sysfs(struct pci_dev *dev) | ||
822 | { | ||
823 | int retval; | ||
824 | struct bin_attribute *attr; | ||
825 | |||
826 | /* If the device has VPD, try to expose it in sysfs. */ | ||
827 | if (dev->vpd) { | ||
828 | attr = kzalloc(sizeof(*attr), GFP_ATOMIC); | ||
829 | if (!attr) | ||
830 | return -ENOMEM; | ||
831 | |||
832 | attr->size = dev->vpd->len; | ||
833 | attr->attr.name = "vpd"; | ||
834 | attr->attr.mode = S_IRUSR | S_IWUSR; | ||
835 | attr->read = pci_read_vpd; | ||
836 | attr->write = pci_write_vpd; | ||
837 | retval = sysfs_create_bin_file(&dev->dev.kobj, attr); | ||
838 | if (retval) { | ||
839 | kfree(dev->vpd->attr); | ||
840 | return retval; | ||
841 | } | ||
842 | dev->vpd->attr = attr; | ||
843 | } | ||
844 | |||
845 | /* Active State Power Management */ | ||
846 | pcie_aspm_create_sysfs_dev_files(dev); | ||
847 | |||
848 | return 0; | ||
849 | } | ||
850 | |||
738 | int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev) | 851 | int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev) |
739 | { | 852 | { |
740 | struct bin_attribute *attr = NULL; | ||
741 | int retval; | 853 | int retval; |
854 | int rom_size = 0; | ||
855 | struct bin_attribute *attr; | ||
742 | 856 | ||
743 | if (!sysfs_initialized) | 857 | if (!sysfs_initialized) |
744 | return -EACCES; | 858 | return -EACCES; |
745 | 859 | ||
746 | if (pdev->cfg_size < 4096) | 860 | if (pdev->cfg_size < PCI_CFG_SPACE_EXP_SIZE) |
747 | retval = sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr); | 861 | retval = sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr); |
748 | else | 862 | else |
749 | retval = sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr); | 863 | retval = sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr); |
750 | if (retval) | 864 | if (retval) |
751 | goto err; | 865 | goto err; |
752 | 866 | ||
753 | /* If the device has VPD, try to expose it in sysfs. */ | ||
754 | if (pdev->vpd) { | ||
755 | attr = kzalloc(sizeof(*attr), GFP_ATOMIC); | ||
756 | if (attr) { | ||
757 | pdev->vpd->attr = attr; | ||
758 | attr->size = pdev->vpd->len; | ||
759 | attr->attr.name = "vpd"; | ||
760 | attr->attr.mode = S_IRUSR | S_IWUSR; | ||
761 | attr->read = pci_read_vpd; | ||
762 | attr->write = pci_write_vpd; | ||
763 | retval = sysfs_create_bin_file(&pdev->dev.kobj, attr); | ||
764 | if (retval) | ||
765 | goto err_vpd; | ||
766 | } else { | ||
767 | retval = -ENOMEM; | ||
768 | goto err_config_file; | ||
769 | } | ||
770 | } | ||
771 | |||
772 | retval = pci_create_resource_files(pdev); | 867 | retval = pci_create_resource_files(pdev); |
773 | if (retval) | 868 | if (retval) |
774 | goto err_vpd_file; | 869 | goto err_config_file; |
870 | |||
871 | if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) | ||
872 | rom_size = pci_resource_len(pdev, PCI_ROM_RESOURCE); | ||
873 | else if (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW) | ||
874 | rom_size = 0x20000; | ||
775 | 875 | ||
776 | /* If the device has a ROM, try to expose it in sysfs. */ | 876 | /* If the device has a ROM, try to expose it in sysfs. */ |
777 | if (pci_resource_len(pdev, PCI_ROM_RESOURCE) || | 877 | if (rom_size) { |
778 | (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW)) { | ||
779 | attr = kzalloc(sizeof(*attr), GFP_ATOMIC); | 878 | attr = kzalloc(sizeof(*attr), GFP_ATOMIC); |
780 | if (attr) { | 879 | if (!attr) { |
781 | pdev->rom_attr = attr; | ||
782 | attr->size = pci_resource_len(pdev, PCI_ROM_RESOURCE); | ||
783 | attr->attr.name = "rom"; | ||
784 | attr->attr.mode = S_IRUSR; | ||
785 | attr->read = pci_read_rom; | ||
786 | attr->write = pci_write_rom; | ||
787 | retval = sysfs_create_bin_file(&pdev->dev.kobj, attr); | ||
788 | if (retval) | ||
789 | goto err_rom; | ||
790 | } else { | ||
791 | retval = -ENOMEM; | 880 | retval = -ENOMEM; |
792 | goto err_resource_files; | 881 | goto err_resource_files; |
793 | } | 882 | } |
883 | attr->size = rom_size; | ||
884 | attr->attr.name = "rom"; | ||
885 | attr->attr.mode = S_IRUSR; | ||
886 | attr->read = pci_read_rom; | ||
887 | attr->write = pci_write_rom; | ||
888 | retval = sysfs_create_bin_file(&pdev->dev.kobj, attr); | ||
889 | if (retval) { | ||
890 | kfree(attr); | ||
891 | goto err_resource_files; | ||
892 | } | ||
893 | pdev->rom_attr = attr; | ||
794 | } | 894 | } |
895 | |||
795 | /* add platform-specific attributes */ | 896 | /* add platform-specific attributes */ |
796 | if (pcibios_add_platform_entries(pdev)) | 897 | retval = pcibios_add_platform_entries(pdev); |
898 | if (retval) | ||
797 | goto err_rom_file; | 899 | goto err_rom_file; |
798 | 900 | ||
799 | pcie_aspm_create_sysfs_dev_files(pdev); | 901 | /* add sysfs entries for various capabilities */ |
902 | retval = pci_create_capabilities_sysfs(pdev); | ||
903 | if (retval) | ||
904 | goto err_rom_file; | ||
800 | 905 | ||
801 | return 0; | 906 | return 0; |
802 | 907 | ||
803 | err_rom_file: | 908 | err_rom_file: |
804 | if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) | 909 | if (rom_size) { |
805 | sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr); | 910 | sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr); |
806 | err_rom: | 911 | kfree(pdev->rom_attr); |
807 | kfree(pdev->rom_attr); | 912 | pdev->rom_attr = NULL; |
913 | } | ||
808 | err_resource_files: | 914 | err_resource_files: |
809 | pci_remove_resource_files(pdev); | 915 | pci_remove_resource_files(pdev); |
810 | err_vpd_file: | ||
811 | if (pdev->vpd) { | ||
812 | sysfs_remove_bin_file(&pdev->dev.kobj, pdev->vpd->attr); | ||
813 | err_vpd: | ||
814 | kfree(pdev->vpd->attr); | ||
815 | } | ||
816 | err_config_file: | 916 | err_config_file: |
817 | if (pdev->cfg_size < 4096) | 917 | if (pdev->cfg_size < PCI_CFG_SPACE_EXP_SIZE) |
818 | sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr); | 918 | sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr); |
819 | else | 919 | else |
820 | sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr); | 920 | sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr); |
@@ -822,6 +922,16 @@ err: | |||
822 | return retval; | 922 | return retval; |
823 | } | 923 | } |
824 | 924 | ||
925 | static void pci_remove_capabilities_sysfs(struct pci_dev *dev) | ||
926 | { | ||
927 | if (dev->vpd && dev->vpd->attr) { | ||
928 | sysfs_remove_bin_file(&dev->dev.kobj, dev->vpd->attr); | ||
929 | kfree(dev->vpd->attr); | ||
930 | } | ||
931 | |||
932 | pcie_aspm_remove_sysfs_dev_files(dev); | ||
933 | } | ||
934 | |||
825 | /** | 935 | /** |
826 | * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files | 936 | * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files |
827 | * @pdev: device whose entries we should free | 937 | * @pdev: device whose entries we should free |
@@ -830,27 +940,28 @@ err: | |||
830 | */ | 940 | */ |
831 | void pci_remove_sysfs_dev_files(struct pci_dev *pdev) | 941 | void pci_remove_sysfs_dev_files(struct pci_dev *pdev) |
832 | { | 942 | { |
943 | int rom_size = 0; | ||
944 | |||
833 | if (!sysfs_initialized) | 945 | if (!sysfs_initialized) |
834 | return; | 946 | return; |
835 | 947 | ||
836 | pcie_aspm_remove_sysfs_dev_files(pdev); | 948 | pci_remove_capabilities_sysfs(pdev); |
837 | 949 | ||
838 | if (pdev->vpd) { | 950 | if (pdev->cfg_size < PCI_CFG_SPACE_EXP_SIZE) |
839 | sysfs_remove_bin_file(&pdev->dev.kobj, pdev->vpd->attr); | ||
840 | kfree(pdev->vpd->attr); | ||
841 | } | ||
842 | if (pdev->cfg_size < 4096) | ||
843 | sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr); | 951 | sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr); |
844 | else | 952 | else |
845 | sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr); | 953 | sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr); |
846 | 954 | ||
847 | pci_remove_resource_files(pdev); | 955 | pci_remove_resource_files(pdev); |
848 | 956 | ||
849 | if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) { | 957 | if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) |
850 | if (pdev->rom_attr) { | 958 | rom_size = pci_resource_len(pdev, PCI_ROM_RESOURCE); |
851 | sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr); | 959 | else if (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW) |
852 | kfree(pdev->rom_attr); | 960 | rom_size = 0x20000; |
853 | } | 961 | |
962 | if (rom_size && pdev->rom_attr) { | ||
963 | sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr); | ||
964 | kfree(pdev->rom_attr); | ||
854 | } | 965 | } |
855 | } | 966 | } |
856 | 967 | ||