aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci
diff options
context:
space:
mode:
authorTejun Heo <htejun@gmail.com>2007-01-20 02:00:26 -0500
committerJeff Garzik <jeff@garzik.org>2007-02-09 17:39:36 -0500
commit9ac7849e35f705830f7b016ff272b0ff1f7ff759 (patch)
tree7f17cdff87e154937a15cc2ec8da9b4e6018ce8e /drivers/pci
parent77a527eadb425b60db3f5f0aae6a4c51c38e35e5 (diff)
devres: device resource management
Implement device resource management, in short, devres. A device driver can allocate arbirary size of devres data which is associated with a release function. On driver detach, release function is invoked on the devres data, then, devres data is freed. devreses are typed by associated release functions. Some devreses are better represented by single instance of the type while others need multiple instances sharing the same release function. Both usages are supported. devreses can be grouped using devres group such that a device driver can easily release acquired resources halfway through initialization or selectively release resources (e.g. resources for port 1 out of 4 ports). This patch adds devres core including documentation and the following managed interfaces. * alloc/free : devm_kzalloc(), devm_kzfree() * IO region : devm_request_region(), devm_release_region() * IRQ : devm_request_irq(), devm_free_irq() * DMA : dmam_alloc_coherent(), dmam_free_coherent(), dmam_declare_coherent_memory(), dmam_pool_create(), dmam_pool_destroy() * PCI : pcim_enable_device(), pcim_pin_device(), pci_is_managed() * iomap : devm_ioport_map(), devm_ioport_unmap(), devm_ioremap(), devm_ioremap_nocache(), devm_iounmap(), pcim_iomap_table(), pcim_iomap(), pcim_iounmap() Signed-off-by: Tejun Heo <htejun@gmail.com> Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/pci.c127
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 */
753struct 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
760static 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
782static 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
796static 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 */
809int 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 */
835void 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) {}
767void 865void
768pci_disable_device(struct pci_dev *dev) 866pci_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 */
868void pci_release_region(struct pci_dev *pdev, int bar) 971void 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 */
894int pci_request_region(struct pci_dev *pdev, int bar, const char *res_name) 1003int 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
912err_out: 1027err_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);
1226EXPORT_SYMBOL_GPL(pci_restore_bars); 1349EXPORT_SYMBOL_GPL(pci_restore_bars);
1227EXPORT_SYMBOL(pci_enable_device_bars); 1350EXPORT_SYMBOL(pci_enable_device_bars);
1228EXPORT_SYMBOL(pci_enable_device); 1351EXPORT_SYMBOL(pci_enable_device);
1352EXPORT_SYMBOL(pcim_enable_device);
1353EXPORT_SYMBOL(pcim_pin_device);
1229EXPORT_SYMBOL(pci_disable_device); 1354EXPORT_SYMBOL(pci_disable_device);
1230EXPORT_SYMBOL(pci_find_capability); 1355EXPORT_SYMBOL(pci_find_capability);
1231EXPORT_SYMBOL(pci_bus_find_capability); 1356EXPORT_SYMBOL(pci_bus_find_capability);