diff options
author | Rajesh Shah <rajesh.shah@intel.com> | 2005-04-28 03:25:56 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2005-06-28 00:52:43 -0400 |
commit | 8e7561cfbdf00fb1cee694cef0e825d0548aedbc (patch) | |
tree | e17b88f3200fb35ea62c7f6896cf21977d551b8a /drivers/pci | |
parent | 2f523b15901f654a9448bbd47ebe1e783ec3195b (diff) |
[PATCH] acpi hotplug: aCPI based root bridge hot-add
acpiphp changes to support acpi based root bridge hot-add.
Signed-off-by: Rajesh Shah <rajesh.shah@intel.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/hotplug/acpiphp_glue.c | 211 |
1 files changed, 202 insertions, 9 deletions
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 1501eb26af33..a6270cc218f6 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c | |||
@@ -6,6 +6,8 @@ | |||
6 | * Copyright (C) 2002,2003 NEC Corporation | 6 | * Copyright (C) 2002,2003 NEC Corporation |
7 | * Copyright (C) 2003-2005 Matthew Wilcox (matthew.wilcox@hp.com) | 7 | * Copyright (C) 2003-2005 Matthew Wilcox (matthew.wilcox@hp.com) |
8 | * Copyright (C) 2003-2005 Hewlett Packard | 8 | * Copyright (C) 2003-2005 Hewlett Packard |
9 | * Copyright (C) 2005 Rajesh Shah (rajesh.shah@intel.com) | ||
10 | * Copyright (C) 2005 Intel Corporation | ||
9 | * | 11 | * |
10 | * All rights reserved. | 12 | * All rights reserved. |
11 | * | 13 | * |
@@ -304,13 +306,15 @@ static void init_bridge_misc(struct acpiphp_bridge *bridge) | |||
304 | register_slot, bridge, NULL); | 306 | register_slot, bridge, NULL); |
305 | 307 | ||
306 | /* install notify handler */ | 308 | /* install notify handler */ |
307 | status = acpi_install_notify_handler(bridge->handle, | 309 | if (bridge->type != BRIDGE_TYPE_HOST) { |
310 | status = acpi_install_notify_handler(bridge->handle, | ||
308 | ACPI_SYSTEM_NOTIFY, | 311 | ACPI_SYSTEM_NOTIFY, |
309 | handle_hotplug_event_bridge, | 312 | handle_hotplug_event_bridge, |
310 | bridge); | 313 | bridge); |
311 | 314 | ||
312 | if (ACPI_FAILURE(status)) { | 315 | if (ACPI_FAILURE(status)) { |
313 | err("failed to register interrupt notify handler\n"); | 316 | err("failed to register interrupt notify handler\n"); |
317 | } | ||
314 | } | 318 | } |
315 | 319 | ||
316 | list_add(&bridge->list, &bridge_list); | 320 | list_add(&bridge->list, &bridge_list); |
@@ -820,6 +824,143 @@ static int acpiphp_check_bridge(struct acpiphp_bridge *bridge) | |||
820 | return retval; | 824 | return retval; |
821 | } | 825 | } |
822 | 826 | ||
827 | static void program_hpp(struct pci_dev *dev, struct acpiphp_bridge *bridge) | ||
828 | { | ||
829 | u16 pci_cmd, pci_bctl; | ||
830 | struct pci_dev *cdev; | ||
831 | |||
832 | /* Program hpp values for this device */ | ||
833 | if (!(dev->hdr_type == PCI_HEADER_TYPE_NORMAL || | ||
834 | (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE && | ||
835 | (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI))) | ||
836 | return; | ||
837 | pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, | ||
838 | bridge->hpp.cache_line_size); | ||
839 | pci_write_config_byte(dev, PCI_LATENCY_TIMER, | ||
840 | bridge->hpp.latency_timer); | ||
841 | pci_read_config_word(dev, PCI_COMMAND, &pci_cmd); | ||
842 | if (bridge->hpp.enable_SERR) | ||
843 | pci_cmd |= PCI_COMMAND_SERR; | ||
844 | else | ||
845 | pci_cmd &= ~PCI_COMMAND_SERR; | ||
846 | if (bridge->hpp.enable_PERR) | ||
847 | pci_cmd |= PCI_COMMAND_PARITY; | ||
848 | else | ||
849 | pci_cmd &= ~PCI_COMMAND_PARITY; | ||
850 | pci_write_config_word(dev, PCI_COMMAND, pci_cmd); | ||
851 | |||
852 | /* Program bridge control value and child devices */ | ||
853 | if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) { | ||
854 | pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER, | ||
855 | bridge->hpp.latency_timer); | ||
856 | pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &pci_bctl); | ||
857 | if (bridge->hpp.enable_SERR) | ||
858 | pci_bctl |= PCI_BRIDGE_CTL_SERR; | ||
859 | else | ||
860 | pci_bctl &= ~PCI_BRIDGE_CTL_SERR; | ||
861 | if (bridge->hpp.enable_PERR) | ||
862 | pci_bctl |= PCI_BRIDGE_CTL_PARITY; | ||
863 | else | ||
864 | pci_bctl &= ~PCI_BRIDGE_CTL_PARITY; | ||
865 | pci_write_config_word(dev, PCI_BRIDGE_CONTROL, pci_bctl); | ||
866 | if (dev->subordinate) { | ||
867 | list_for_each_entry(cdev, &dev->subordinate->devices, | ||
868 | bus_list) | ||
869 | program_hpp(cdev, bridge); | ||
870 | } | ||
871 | } | ||
872 | } | ||
873 | |||
874 | static void acpiphp_set_hpp_values(acpi_handle handle, struct pci_bus *bus) | ||
875 | { | ||
876 | struct acpiphp_bridge bridge; | ||
877 | struct pci_dev *dev; | ||
878 | |||
879 | memset(&bridge, 0, sizeof(bridge)); | ||
880 | bridge.handle = handle; | ||
881 | decode_hpp(&bridge); | ||
882 | list_for_each_entry(dev, &bus->devices, bus_list) | ||
883 | program_hpp(dev, &bridge); | ||
884 | |||
885 | } | ||
886 | |||
887 | /* | ||
888 | * Remove devices for which we could not assign resources, call | ||
889 | * arch specific code to fix-up the bus | ||
890 | */ | ||
891 | static void acpiphp_sanitize_bus(struct pci_bus *bus) | ||
892 | { | ||
893 | struct pci_dev *dev; | ||
894 | int i; | ||
895 | unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM; | ||
896 | |||
897 | list_for_each_entry(dev, &bus->devices, bus_list) { | ||
898 | for (i=0; i<PCI_BRIDGE_RESOURCES; i++) { | ||
899 | struct resource *res = &dev->resource[i]; | ||
900 | if ((res->flags & type_mask) && !res->start && | ||
901 | res->end) { | ||
902 | /* Could not assign a required resources | ||
903 | * for this device, remove it */ | ||
904 | pci_remove_bus_device(dev); | ||
905 | break; | ||
906 | } | ||
907 | } | ||
908 | } | ||
909 | } | ||
910 | |||
911 | /* Program resources in newly inserted bridge */ | ||
912 | static int acpiphp_configure_bridge (acpi_handle handle) | ||
913 | { | ||
914 | struct acpi_pci_id pci_id; | ||
915 | struct pci_bus *bus; | ||
916 | |||
917 | if (ACPI_FAILURE(acpi_get_pci_id(handle, &pci_id))) { | ||
918 | err("cannot get PCI domain and bus number for bridge\n"); | ||
919 | return -EINVAL; | ||
920 | } | ||
921 | bus = pci_find_bus(pci_id.segment, pci_id.bus); | ||
922 | if (!bus) { | ||
923 | err("cannot find bus %d:%d\n", | ||
924 | pci_id.segment, pci_id.bus); | ||
925 | return -EINVAL; | ||
926 | } | ||
927 | |||
928 | pci_bus_size_bridges(bus); | ||
929 | pci_bus_assign_resources(bus); | ||
930 | acpiphp_sanitize_bus(bus); | ||
931 | acpiphp_set_hpp_values(handle, bus); | ||
932 | pci_enable_bridges(bus); | ||
933 | return 0; | ||
934 | } | ||
935 | |||
936 | static void handle_bridge_insertion(acpi_handle handle, u32 type) | ||
937 | { | ||
938 | struct acpi_device *device, *pdevice; | ||
939 | acpi_handle phandle; | ||
940 | |||
941 | if ((type != ACPI_NOTIFY_BUS_CHECK) && | ||
942 | (type != ACPI_NOTIFY_DEVICE_CHECK)) { | ||
943 | err("unexpected notification type %d\n", type); | ||
944 | return; | ||
945 | } | ||
946 | |||
947 | acpi_get_parent(handle, &phandle); | ||
948 | if (acpi_bus_get_device(phandle, &pdevice)) { | ||
949 | dbg("no parent device, assuming NULL\n"); | ||
950 | pdevice = NULL; | ||
951 | } | ||
952 | if (acpi_bus_add(&device, pdevice, handle, ACPI_BUS_TYPE_DEVICE)) { | ||
953 | err("cannot add bridge to acpi list\n"); | ||
954 | return; | ||
955 | } | ||
956 | if (!acpiphp_configure_bridge(handle) && | ||
957 | !acpi_bus_start(device)) | ||
958 | add_bridge(handle); | ||
959 | else | ||
960 | err("cannot configure and start bridge\n"); | ||
961 | |||
962 | } | ||
963 | |||
823 | /* | 964 | /* |
824 | * ACPI event handlers | 965 | * ACPI event handlers |
825 | */ | 966 | */ |
@@ -840,8 +981,19 @@ static void handle_hotplug_event_bridge(acpi_handle handle, u32 type, void *cont | |||
840 | char objname[64]; | 981 | char objname[64]; |
841 | struct acpi_buffer buffer = { .length = sizeof(objname), | 982 | struct acpi_buffer buffer = { .length = sizeof(objname), |
842 | .pointer = objname }; | 983 | .pointer = objname }; |
984 | struct acpi_device *device; | ||
843 | 985 | ||
844 | bridge = (struct acpiphp_bridge *)context; | 986 | if (acpi_bus_get_device(handle, &device)) { |
987 | /* This bridge must have just been physically inserted */ | ||
988 | handle_bridge_insertion(handle, type); | ||
989 | return; | ||
990 | } | ||
991 | |||
992 | bridge = acpiphp_handle_to_bridge(handle); | ||
993 | if (!bridge) { | ||
994 | err("cannot get bridge info\n"); | ||
995 | return; | ||
996 | } | ||
845 | 997 | ||
846 | acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); | 998 | acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); |
847 | 999 | ||
@@ -941,6 +1093,47 @@ static void handle_hotplug_event_func(acpi_handle handle, u32 type, void *contex | |||
941 | } | 1093 | } |
942 | } | 1094 | } |
943 | 1095 | ||
1096 | static int is_root_bridge(acpi_handle handle) | ||
1097 | { | ||
1098 | acpi_status status; | ||
1099 | struct acpi_device_info *info; | ||
1100 | struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; | ||
1101 | int i; | ||
1102 | |||
1103 | status = acpi_get_object_info(handle, &buffer); | ||
1104 | if (ACPI_SUCCESS(status)) { | ||
1105 | info = buffer.pointer; | ||
1106 | if ((info->valid & ACPI_VALID_HID) && | ||
1107 | !strcmp(PCI_ROOT_HID_STRING, | ||
1108 | info->hardware_id.value)) { | ||
1109 | acpi_os_free(buffer.pointer); | ||
1110 | return 1; | ||
1111 | } | ||
1112 | if (info->valid & ACPI_VALID_CID) { | ||
1113 | for (i=0; i < info->compatibility_id.count; i++) { | ||
1114 | if (!strcmp(PCI_ROOT_HID_STRING, | ||
1115 | info->compatibility_id.id[i].value)) { | ||
1116 | acpi_os_free(buffer.pointer); | ||
1117 | return 1; | ||
1118 | } | ||
1119 | } | ||
1120 | } | ||
1121 | } | ||
1122 | return 0; | ||
1123 | } | ||
1124 | |||
1125 | static acpi_status | ||
1126 | find_root_bridges(acpi_handle handle, u32 lvl, void *context, void **rv) | ||
1127 | { | ||
1128 | int *count = (int *)context; | ||
1129 | |||
1130 | if (is_root_bridge(handle)) { | ||
1131 | acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, | ||
1132 | handle_hotplug_event_bridge, NULL); | ||
1133 | (*count)++; | ||
1134 | } | ||
1135 | return AE_OK ; | ||
1136 | } | ||
944 | 1137 | ||
945 | static struct acpi_pci_driver acpi_pci_hp_driver = { | 1138 | static struct acpi_pci_driver acpi_pci_hp_driver = { |
946 | .add = add_bridge, | 1139 | .add = add_bridge, |
@@ -953,15 +1146,15 @@ static struct acpi_pci_driver acpi_pci_hp_driver = { | |||
953 | */ | 1146 | */ |
954 | int __init acpiphp_glue_init(void) | 1147 | int __init acpiphp_glue_init(void) |
955 | { | 1148 | { |
956 | int num; | 1149 | int num = 0; |
957 | |||
958 | if (list_empty(&pci_root_buses)) | ||
959 | return -1; | ||
960 | 1150 | ||
961 | num = acpi_pci_register_driver(&acpi_pci_hp_driver); | 1151 | acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, |
1152 | ACPI_UINT32_MAX, find_root_bridges, &num, NULL); | ||
962 | 1153 | ||
963 | if (num <= 0) | 1154 | if (num <= 0) |
964 | return -1; | 1155 | return -1; |
1156 | else | ||
1157 | acpi_pci_register_driver(&acpi_pci_hp_driver); | ||
965 | 1158 | ||
966 | return 0; | 1159 | return 0; |
967 | } | 1160 | } |