diff options
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/pci.c | 127 |
1 files changed, 126 insertions, 1 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 84c757ba0664..8b44cff2c176 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c | |||
@@ -744,6 +744,104 @@ int pci_enable_device(struct pci_dev *dev) | |||
744 | return pci_enable_device_bars(dev, (1 << PCI_NUM_RESOURCES) - 1); | 744 | return pci_enable_device_bars(dev, (1 << PCI_NUM_RESOURCES) - 1); |
745 | } | 745 | } |
746 | 746 | ||
747 | /* | ||
748 | * Managed PCI resources. This manages device on/off, intx/msi/msix | ||
749 | * on/off and BAR regions. pci_dev itself records msi/msix status, so | ||
750 | * there's no need to track it separately. pci_devres is initialized | ||
751 | * when a device is enabled using managed PCI device enable interface. | ||
752 | */ | ||
753 | struct pci_devres { | ||
754 | unsigned int disable:1; | ||
755 | unsigned int orig_intx:1; | ||
756 | unsigned int restore_intx:1; | ||
757 | u32 region_mask; | ||
758 | }; | ||
759 | |||
760 | static void pcim_release(struct device *gendev, void *res) | ||
761 | { | ||
762 | struct pci_dev *dev = container_of(gendev, struct pci_dev, dev); | ||
763 | struct pci_devres *this = res; | ||
764 | int i; | ||
765 | |||
766 | if (dev->msi_enabled) | ||
767 | pci_disable_msi(dev); | ||
768 | if (dev->msix_enabled) | ||
769 | pci_disable_msix(dev); | ||
770 | |||
771 | for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) | ||
772 | if (this->region_mask & (1 << i)) | ||
773 | pci_release_region(dev, i); | ||
774 | |||
775 | if (this->restore_intx) | ||
776 | pci_intx(dev, this->orig_intx); | ||
777 | |||
778 | if (this->disable) | ||
779 | pci_disable_device(dev); | ||
780 | } | ||
781 | |||
782 | static struct pci_devres * get_pci_dr(struct pci_dev *pdev) | ||
783 | { | ||
784 | struct pci_devres *dr, *new_dr; | ||
785 | |||
786 | dr = devres_find(&pdev->dev, pcim_release, NULL, NULL); | ||
787 | if (dr) | ||
788 | return dr; | ||
789 | |||
790 | new_dr = devres_alloc(pcim_release, sizeof(*new_dr), GFP_KERNEL); | ||
791 | if (!new_dr) | ||
792 | return NULL; | ||
793 | return devres_get(&pdev->dev, new_dr, NULL, NULL); | ||
794 | } | ||
795 | |||
796 | static struct pci_devres * find_pci_dr(struct pci_dev *pdev) | ||
797 | { | ||
798 | if (pci_is_managed(pdev)) | ||
799 | return devres_find(&pdev->dev, pcim_release, NULL, NULL); | ||
800 | return NULL; | ||
801 | } | ||
802 | |||
803 | /** | ||
804 | * pcim_enable_device - Managed pci_enable_device() | ||
805 | * @pdev: PCI device to be initialized | ||
806 | * | ||
807 | * Managed pci_enable_device(). | ||
808 | */ | ||
809 | int pcim_enable_device(struct pci_dev *pdev) | ||
810 | { | ||
811 | struct pci_devres *dr; | ||
812 | int rc; | ||
813 | |||
814 | dr = get_pci_dr(pdev); | ||
815 | if (unlikely(!dr)) | ||
816 | return -ENOMEM; | ||
817 | WARN_ON(!!dr->disable); | ||
818 | |||
819 | rc = pci_enable_device(pdev); | ||
820 | if (!rc) { | ||
821 | pdev->is_managed = 1; | ||
822 | dr->disable = 1; | ||
823 | } | ||
824 | return rc; | ||
825 | } | ||
826 | |||
827 | /** | ||
828 | * pcim_pin_device - Pin managed PCI device | ||
829 | * @pdev: PCI device to pin | ||
830 | * | ||
831 | * Pin managed PCI device @pdev. Pinned device won't be disabled on | ||
832 | * driver detach. @pdev must have been enabled with | ||
833 | * pcim_enable_device(). | ||
834 | */ | ||
835 | void pcim_pin_device(struct pci_dev *pdev) | ||
836 | { | ||
837 | struct pci_devres *dr; | ||
838 | |||
839 | dr = find_pci_dr(pdev); | ||
840 | WARN_ON(!dr || !dr->disable); | ||
841 | if (dr) | ||
842 | dr->disable = 0; | ||
843 | } | ||
844 | |||
747 | /** | 845 | /** |
748 | * pcibios_disable_device - disable arch specific PCI resources for device dev | 846 | * pcibios_disable_device - disable arch specific PCI resources for device dev |
749 | * @dev: the PCI device to disable | 847 | * @dev: the PCI device to disable |
@@ -767,8 +865,13 @@ void __attribute__ ((weak)) pcibios_disable_device (struct pci_dev *dev) {} | |||
767 | void | 865 | void |
768 | pci_disable_device(struct pci_dev *dev) | 866 | pci_disable_device(struct pci_dev *dev) |
769 | { | 867 | { |
868 | struct pci_devres *dr; | ||
770 | u16 pci_command; | 869 | u16 pci_command; |
771 | 870 | ||
871 | dr = find_pci_dr(dev); | ||
872 | if (dr) | ||
873 | dr->disable = 0; | ||
874 | |||
772 | if (atomic_sub_return(1, &dev->enable_cnt) != 0) | 875 | if (atomic_sub_return(1, &dev->enable_cnt) != 0) |
773 | return; | 876 | return; |
774 | 877 | ||
@@ -867,6 +970,8 @@ pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge) | |||
867 | */ | 970 | */ |
868 | void pci_release_region(struct pci_dev *pdev, int bar) | 971 | void pci_release_region(struct pci_dev *pdev, int bar) |
869 | { | 972 | { |
973 | struct pci_devres *dr; | ||
974 | |||
870 | if (pci_resource_len(pdev, bar) == 0) | 975 | if (pci_resource_len(pdev, bar) == 0) |
871 | return; | 976 | return; |
872 | if (pci_resource_flags(pdev, bar) & IORESOURCE_IO) | 977 | if (pci_resource_flags(pdev, bar) & IORESOURCE_IO) |
@@ -875,6 +980,10 @@ void pci_release_region(struct pci_dev *pdev, int bar) | |||
875 | else if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) | 980 | else if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) |
876 | release_mem_region(pci_resource_start(pdev, bar), | 981 | release_mem_region(pci_resource_start(pdev, bar), |
877 | pci_resource_len(pdev, bar)); | 982 | pci_resource_len(pdev, bar)); |
983 | |||
984 | dr = find_pci_dr(pdev); | ||
985 | if (dr) | ||
986 | dr->region_mask &= ~(1 << bar); | ||
878 | } | 987 | } |
879 | 988 | ||
880 | /** | 989 | /** |
@@ -893,6 +1002,8 @@ void pci_release_region(struct pci_dev *pdev, int bar) | |||
893 | */ | 1002 | */ |
894 | int pci_request_region(struct pci_dev *pdev, int bar, const char *res_name) | 1003 | int pci_request_region(struct pci_dev *pdev, int bar, const char *res_name) |
895 | { | 1004 | { |
1005 | struct pci_devres *dr; | ||
1006 | |||
896 | if (pci_resource_len(pdev, bar) == 0) | 1007 | if (pci_resource_len(pdev, bar) == 0) |
897 | return 0; | 1008 | return 0; |
898 | 1009 | ||
@@ -906,7 +1017,11 @@ int pci_request_region(struct pci_dev *pdev, int bar, const char *res_name) | |||
906 | pci_resource_len(pdev, bar), res_name)) | 1017 | pci_resource_len(pdev, bar), res_name)) |
907 | goto err_out; | 1018 | goto err_out; |
908 | } | 1019 | } |
909 | 1020 | ||
1021 | dr = find_pci_dr(pdev); | ||
1022 | if (dr) | ||
1023 | dr->region_mask |= 1 << bar; | ||
1024 | |||
910 | return 0; | 1025 | return 0; |
911 | 1026 | ||
912 | err_out: | 1027 | err_out: |
@@ -1144,7 +1259,15 @@ pci_intx(struct pci_dev *pdev, int enable) | |||
1144 | } | 1259 | } |
1145 | 1260 | ||
1146 | if (new != pci_command) { | 1261 | if (new != pci_command) { |
1262 | struct pci_devres *dr; | ||
1263 | |||
1147 | pci_write_config_word(pdev, PCI_COMMAND, new); | 1264 | pci_write_config_word(pdev, PCI_COMMAND, new); |
1265 | |||
1266 | dr = find_pci_dr(pdev); | ||
1267 | if (dr && !dr->restore_intx) { | ||
1268 | dr->restore_intx = 1; | ||
1269 | dr->orig_intx = !enable; | ||
1270 | } | ||
1148 | } | 1271 | } |
1149 | } | 1272 | } |
1150 | 1273 | ||
@@ -1226,6 +1349,8 @@ device_initcall(pci_init); | |||
1226 | EXPORT_SYMBOL_GPL(pci_restore_bars); | 1349 | EXPORT_SYMBOL_GPL(pci_restore_bars); |
1227 | EXPORT_SYMBOL(pci_enable_device_bars); | 1350 | EXPORT_SYMBOL(pci_enable_device_bars); |
1228 | EXPORT_SYMBOL(pci_enable_device); | 1351 | EXPORT_SYMBOL(pci_enable_device); |
1352 | EXPORT_SYMBOL(pcim_enable_device); | ||
1353 | EXPORT_SYMBOL(pcim_pin_device); | ||
1229 | EXPORT_SYMBOL(pci_disable_device); | 1354 | EXPORT_SYMBOL(pci_disable_device); |
1230 | EXPORT_SYMBOL(pci_find_capability); | 1355 | EXPORT_SYMBOL(pci_find_capability); |
1231 | EXPORT_SYMBOL(pci_bus_find_capability); | 1356 | EXPORT_SYMBOL(pci_bus_find_capability); |