diff options
Diffstat (limited to 'drivers/pci/hotplug')
60 files changed, 44057 insertions, 0 deletions
diff --git a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig new file mode 100644 index 000000000000..1a4d4ca2a4dc --- /dev/null +++ b/drivers/pci/hotplug/Kconfig | |||
@@ -0,0 +1,197 @@ | |||
1 | # | ||
2 | # PCI Hotplug support | ||
3 | # | ||
4 | |||
5 | menu "PCI Hotplug Support" | ||
6 | |||
7 | config HOTPLUG_PCI | ||
8 | tristate "Support for PCI Hotplug (EXPERIMENTAL)" | ||
9 | depends on PCI && EXPERIMENTAL | ||
10 | select HOTPLUG | ||
11 | ---help--- | ||
12 | Say Y here if you have a motherboard with a PCI Hotplug controller. | ||
13 | This allows you to add and remove PCI cards while the machine is | ||
14 | powered up and running. The file system pcihpfs must be mounted | ||
15 | in order to interact with any PCI Hotplug controllers. | ||
16 | |||
17 | To compile this driver as a module, choose M here: the | ||
18 | module will be called pci_hotplug. | ||
19 | |||
20 | When in doubt, say N. | ||
21 | |||
22 | config HOTPLUG_PCI_FAKE | ||
23 | tristate "Fake PCI Hotplug driver" | ||
24 | depends on HOTPLUG_PCI | ||
25 | help | ||
26 | Say Y here if you want to use the fake PCI hotplug driver. It can | ||
27 | be used to simulate PCI hotplug events if even if your system is | ||
28 | not PCI hotplug capable. | ||
29 | |||
30 | This driver will "emulate" removing PCI devices from the system. | ||
31 | If the "power" file is written to with "0" then the specified PCI | ||
32 | device will be completely removed from the kernel. | ||
33 | |||
34 | WARNING, this does NOT turn off the power to the PCI device. | ||
35 | This is a "logical" removal, not a physical or electrical | ||
36 | removal. | ||
37 | |||
38 | Use this module at your own risk. You have been warned! | ||
39 | |||
40 | To compile this driver as a module, choose M here: the | ||
41 | module will be called fakephp. | ||
42 | |||
43 | When in doubt, say N. | ||
44 | |||
45 | config HOTPLUG_PCI_COMPAQ | ||
46 | tristate "Compaq PCI Hotplug driver" | ||
47 | depends on HOTPLUG_PCI && X86 && PCI_BIOS | ||
48 | help | ||
49 | Say Y here if you have a motherboard with a Compaq PCI Hotplug | ||
50 | controller. | ||
51 | |||
52 | To compile this driver as a module, choose M here: the | ||
53 | module will be called cpqphp. | ||
54 | |||
55 | When in doubt, say N. | ||
56 | |||
57 | config HOTPLUG_PCI_COMPAQ_NVRAM | ||
58 | bool "Save configuration into NVRAM on Compaq servers" | ||
59 | depends on HOTPLUG_PCI_COMPAQ | ||
60 | help | ||
61 | Say Y here if you have a Compaq server that has a PCI Hotplug | ||
62 | controller. This will allow the PCI Hotplug driver to store the PCI | ||
63 | system configuration options in NVRAM. | ||
64 | |||
65 | When in doubt, say N. | ||
66 | |||
67 | config HOTPLUG_PCI_IBM | ||
68 | tristate "IBM PCI Hotplug driver" | ||
69 | depends on HOTPLUG_PCI && X86_IO_APIC && X86 && PCI_BIOS | ||
70 | help | ||
71 | Say Y here if you have a motherboard with a IBM PCI Hotplug | ||
72 | controller. | ||
73 | |||
74 | To compile this driver as a module, choose M here: the | ||
75 | module will be called ibmphp. | ||
76 | |||
77 | When in doubt, say N. | ||
78 | |||
79 | config HOTPLUG_PCI_ACPI | ||
80 | tristate "ACPI PCI Hotplug driver" | ||
81 | depends on ACPI_BUS && HOTPLUG_PCI | ||
82 | help | ||
83 | Say Y here if you have a system that supports PCI Hotplug using | ||
84 | ACPI. | ||
85 | |||
86 | To compile this driver as a module, choose M here: the | ||
87 | module will be called acpiphp. | ||
88 | |||
89 | When in doubt, say N. | ||
90 | |||
91 | config HOTPLUG_PCI_ACPI_IBM | ||
92 | tristate "ACPI PCI Hotplug driver IBM extensions" | ||
93 | depends on HOTPLUG_PCI_ACPI | ||
94 | help | ||
95 | Say Y here if you have an IBM system that supports PCI Hotplug using | ||
96 | ACPI. | ||
97 | |||
98 | To compile this driver as a module, choose M here: the | ||
99 | module will be called acpiphp_ibm. | ||
100 | |||
101 | When in doubt, say N. | ||
102 | |||
103 | config HOTPLUG_PCI_CPCI | ||
104 | bool "CompactPCI Hotplug driver" | ||
105 | depends on HOTPLUG_PCI | ||
106 | help | ||
107 | Say Y here if you have a CompactPCI system card with CompactPCI | ||
108 | hotswap support per the PICMG 2.1 specification. | ||
109 | |||
110 | When in doubt, say N. | ||
111 | |||
112 | config HOTPLUG_PCI_CPCI_ZT5550 | ||
113 | tristate "Ziatech ZT5550 CompactPCI Hotplug driver" | ||
114 | depends on HOTPLUG_PCI && HOTPLUG_PCI_CPCI && X86 | ||
115 | help | ||
116 | Say Y here if you have an Performance Technologies (formerly Intel, | ||
117 | formerly just Ziatech) Ziatech ZT5550 CompactPCI system card. | ||
118 | |||
119 | To compile this driver as a module, choose M here: the | ||
120 | module will be called cpcihp_zt5550. | ||
121 | |||
122 | When in doubt, say N. | ||
123 | |||
124 | config HOTPLUG_PCI_CPCI_GENERIC | ||
125 | tristate "Generic port I/O CompactPCI Hotplug driver" | ||
126 | depends on HOTPLUG_PCI && HOTPLUG_PCI_CPCI && X86 | ||
127 | help | ||
128 | Say Y here if you have a CompactPCI system card that exposes the #ENUM | ||
129 | hotswap signal as a bit in a system register that can be read through | ||
130 | standard port I/O. | ||
131 | |||
132 | To compile this driver as a module, choose M here: the | ||
133 | module will be called cpcihp_generic. | ||
134 | |||
135 | When in doubt, say N. | ||
136 | |||
137 | config HOTPLUG_PCI_SHPC | ||
138 | tristate "SHPC PCI Hotplug driver" | ||
139 | depends on HOTPLUG_PCI | ||
140 | help | ||
141 | Say Y here if you have a motherboard with a SHPC PCI Hotplug | ||
142 | controller. | ||
143 | |||
144 | To compile this driver as a module, choose M here: the | ||
145 | module will be called shpchp. | ||
146 | |||
147 | When in doubt, say N. | ||
148 | |||
149 | config HOTPLUG_PCI_SHPC_POLL_EVENT_MODE | ||
150 | bool "Use polling mechanism for hot-plug events (for testing purpose)" | ||
151 | depends on HOTPLUG_PCI_SHPC | ||
152 | help | ||
153 | Say Y here if you want to use the polling mechanism for hot-plug | ||
154 | events for early platform testing. | ||
155 | |||
156 | When in doubt, say N. | ||
157 | |||
158 | config HOTPLUG_PCI_SHPC_PHPRM_LEGACY | ||
159 | bool "For AMD SHPC only: Use $HRT for resource/configuration" | ||
160 | depends on HOTPLUG_PCI_SHPC && !ACPI_BUS | ||
161 | help | ||
162 | Say Y here for AMD SHPC. You have to select this option if you are | ||
163 | using this driver on platform with AMD SHPC. | ||
164 | |||
165 | config HOTPLUG_PCI_RPA | ||
166 | tristate "RPA PCI Hotplug driver" | ||
167 | depends on HOTPLUG_PCI && PPC_PSERIES && PPC64 && !HOTPLUG_PCI_FAKE | ||
168 | help | ||
169 | Say Y here if you have a a RPA system that supports PCI Hotplug. | ||
170 | |||
171 | To compile this driver as a module, choose M here: the | ||
172 | module will be called rpaphp. | ||
173 | |||
174 | When in doubt, say N. | ||
175 | |||
176 | config HOTPLUG_PCI_RPA_DLPAR | ||
177 | tristate "RPA Dynamic Logical Partitioning for I/O slots" | ||
178 | depends on HOTPLUG_PCI_RPA | ||
179 | help | ||
180 | Say Y here if your system supports Dynamic Logical Partitioning | ||
181 | for I/O slots. | ||
182 | |||
183 | To compile this driver as a module, choose M here: the | ||
184 | module will be called rpadlpar_io. | ||
185 | |||
186 | When in doubt, say N. | ||
187 | |||
188 | config HOTPLUG_PCI_SGI | ||
189 | tristate "SGI PCI Hotplug Support" | ||
190 | depends on HOTPLUG_PCI && IA64_SGI_SN2 | ||
191 | help | ||
192 | Say Y here if you have an SGI IA64 Altix system. | ||
193 | |||
194 | When in doubt, say N. | ||
195 | |||
196 | endmenu | ||
197 | |||
diff --git a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile new file mode 100644 index 000000000000..93c120ddbd39 --- /dev/null +++ b/drivers/pci/hotplug/Makefile | |||
@@ -0,0 +1,74 @@ | |||
1 | # | ||
2 | # Makefile for the Linux kernel pci hotplug controller drivers. | ||
3 | # | ||
4 | |||
5 | obj-$(CONFIG_HOTPLUG_PCI) += pci_hotplug.o | ||
6 | obj-$(CONFIG_HOTPLUG_PCI_FAKE) += fakephp.o | ||
7 | obj-$(CONFIG_HOTPLUG_PCI_COMPAQ) += cpqphp.o | ||
8 | obj-$(CONFIG_HOTPLUG_PCI_IBM) += ibmphp.o | ||
9 | obj-$(CONFIG_HOTPLUG_PCI_ACPI) += acpiphp.o | ||
10 | obj-$(CONFIG_HOTPLUG_PCI_ACPI_IBM) += acpiphp_ibm.o | ||
11 | obj-$(CONFIG_HOTPLUG_PCI_CPCI_ZT5550) += cpcihp_zt5550.o | ||
12 | obj-$(CONFIG_HOTPLUG_PCI_CPCI_GENERIC) += cpcihp_generic.o | ||
13 | obj-$(CONFIG_HOTPLUG_PCI_PCIE) += pciehp.o | ||
14 | obj-$(CONFIG_HOTPLUG_PCI_SHPC) += shpchp.o | ||
15 | obj-$(CONFIG_HOTPLUG_PCI_RPA) += rpaphp.o | ||
16 | obj-$(CONFIG_HOTPLUG_PCI_RPA_DLPAR) += rpadlpar_io.o | ||
17 | |||
18 | pci_hotplug-objs := pci_hotplug_core.o | ||
19 | |||
20 | ifdef CONFIG_HOTPLUG_PCI_CPCI | ||
21 | pci_hotplug-objs += cpci_hotplug_core.o \ | ||
22 | cpci_hotplug_pci.o | ||
23 | endif | ||
24 | |||
25 | cpqphp-objs := cpqphp_core.o \ | ||
26 | cpqphp_ctrl.o \ | ||
27 | cpqphp_sysfs.o \ | ||
28 | cpqphp_pci.o | ||
29 | cpqphp-$(CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM) += cpqphp_nvram.o | ||
30 | cpqphp-objs += $(cpqphp-y) | ||
31 | |||
32 | ibmphp-objs := ibmphp_core.o \ | ||
33 | ibmphp_ebda.o \ | ||
34 | ibmphp_pci.o \ | ||
35 | ibmphp_res.o \ | ||
36 | ibmphp_hpc.o | ||
37 | |||
38 | acpiphp-objs := acpiphp_core.o \ | ||
39 | acpiphp_glue.o \ | ||
40 | acpiphp_pci.o \ | ||
41 | acpiphp_res.o | ||
42 | |||
43 | rpaphp-objs := rpaphp_core.o \ | ||
44 | rpaphp_pci.o \ | ||
45 | rpaphp_slot.o \ | ||
46 | rpaphp_vio.o | ||
47 | |||
48 | rpadlpar_io-objs := rpadlpar_core.o \ | ||
49 | rpadlpar_sysfs.o | ||
50 | |||
51 | pciehp-objs := pciehp_core.o \ | ||
52 | pciehp_ctrl.o \ | ||
53 | pciehp_pci.o \ | ||
54 | pciehp_hpc.o | ||
55 | ifdef CONFIG_ACPI_BUS | ||
56 | pciehp-objs += pciehprm_acpi.o | ||
57 | else | ||
58 | pciehp-objs += pciehprm_nonacpi.o | ||
59 | endif | ||
60 | |||
61 | shpchp-objs := shpchp_core.o \ | ||
62 | shpchp_ctrl.o \ | ||
63 | shpchp_pci.o \ | ||
64 | shpchp_sysfs.o \ | ||
65 | shpchp_hpc.o | ||
66 | ifdef CONFIG_ACPI_BUS | ||
67 | shpchp-objs += shpchprm_acpi.o | ||
68 | else | ||
69 | ifdef CONFIG_HOTPLUG_PCI_SHPC_PHPRM_LEGACY | ||
70 | shpchp-objs += shpchprm_legacy.o | ||
71 | else | ||
72 | shpchp-objs += shpchprm_nonacpi.o | ||
73 | endif | ||
74 | endif | ||
diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h new file mode 100644 index 000000000000..d9499874c8a9 --- /dev/null +++ b/drivers/pci/hotplug/acpiphp.h | |||
@@ -0,0 +1,268 @@ | |||
1 | /* | ||
2 | * ACPI PCI Hot Plug Controller Driver | ||
3 | * | ||
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | ||
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * Copyright (C) 2001 IBM Corp. | ||
7 | * Copyright (C) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com) | ||
8 | * Copyright (C) 2002,2003 Takayoshi Kochi (t-kochi@bq.jp.nec.com) | ||
9 | * Copyright (C) 2002,2003 NEC Corporation | ||
10 | * | ||
11 | * All rights reserved. | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify | ||
14 | * it under the terms of the GNU General Public License as published by | ||
15 | * the Free Software Foundation; either version 2 of the License, or (at | ||
16 | * your option) any later version. | ||
17 | * | ||
18 | * This program is distributed in the hope that it will be useful, but | ||
19 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
20 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
21 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
22 | * details. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License | ||
25 | * along with this program; if not, write to the Free Software | ||
26 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
27 | * | ||
28 | * Send feedback to <gregkh@us.ibm.com>, | ||
29 | * <t-kochi@bq.jp.nec.com> | ||
30 | * | ||
31 | */ | ||
32 | |||
33 | #ifndef _ACPIPHP_H | ||
34 | #define _ACPIPHP_H | ||
35 | |||
36 | #include <linux/acpi.h> | ||
37 | #include <linux/kobject.h> /* for KOBJ_NAME_LEN */ | ||
38 | #include "pci_hotplug.h" | ||
39 | |||
40 | #define dbg(format, arg...) \ | ||
41 | do { \ | ||
42 | if (acpiphp_debug) \ | ||
43 | printk(KERN_DEBUG "%s: " format, \ | ||
44 | MY_NAME , ## arg); \ | ||
45 | } while (0) | ||
46 | #define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg) | ||
47 | #define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg) | ||
48 | #define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME , ## arg) | ||
49 | |||
50 | /* name size which is used for entries in pcihpfs */ | ||
51 | #define SLOT_NAME_SIZE KOBJ_NAME_LEN /* {_SUN} */ | ||
52 | |||
53 | struct acpiphp_bridge; | ||
54 | struct acpiphp_slot; | ||
55 | struct pci_resource; | ||
56 | |||
57 | /* | ||
58 | * struct slot - slot information for each *physical* slot | ||
59 | */ | ||
60 | struct slot { | ||
61 | u8 number; | ||
62 | struct hotplug_slot *hotplug_slot; | ||
63 | struct list_head slot_list; | ||
64 | |||
65 | struct acpiphp_slot *acpi_slot; | ||
66 | }; | ||
67 | |||
68 | /* | ||
69 | * struct pci_resource - describes pci resource (mem, pfmem, io, bus) | ||
70 | */ | ||
71 | struct pci_resource { | ||
72 | struct pci_resource * next; | ||
73 | u64 base; | ||
74 | u32 length; | ||
75 | }; | ||
76 | |||
77 | /** | ||
78 | * struct hpp_param - ACPI 2.0 _HPP Hot Plug Parameters | ||
79 | * @cache_line_size in DWORD | ||
80 | * @latency_timer in PCI clock | ||
81 | * @enable_SERR 0 or 1 | ||
82 | * @enable_PERR 0 or 1 | ||
83 | */ | ||
84 | struct hpp_param { | ||
85 | u8 cache_line_size; | ||
86 | u8 latency_timer; | ||
87 | u8 enable_SERR; | ||
88 | u8 enable_PERR; | ||
89 | }; | ||
90 | |||
91 | |||
92 | /** | ||
93 | * struct acpiphp_bridge - PCI bridge information | ||
94 | * | ||
95 | * for each bridge device in ACPI namespace | ||
96 | */ | ||
97 | struct acpiphp_bridge { | ||
98 | struct list_head list; | ||
99 | acpi_handle handle; | ||
100 | struct acpiphp_slot *slots; | ||
101 | int type; | ||
102 | int nr_slots; | ||
103 | |||
104 | u8 seg; | ||
105 | u8 bus; | ||
106 | u8 sub; | ||
107 | |||
108 | u32 flags; | ||
109 | |||
110 | /* This bus (host bridge) or Secondary bus (PCI-to-PCI bridge) */ | ||
111 | struct pci_bus *pci_bus; | ||
112 | |||
113 | /* PCI-to-PCI bridge device */ | ||
114 | struct pci_dev *pci_dev; | ||
115 | |||
116 | /* ACPI 2.0 _HPP parameters */ | ||
117 | struct hpp_param hpp; | ||
118 | |||
119 | spinlock_t res_lock; | ||
120 | |||
121 | /* available resources on this bus */ | ||
122 | struct pci_resource *mem_head; | ||
123 | struct pci_resource *p_mem_head; | ||
124 | struct pci_resource *io_head; | ||
125 | struct pci_resource *bus_head; | ||
126 | }; | ||
127 | |||
128 | |||
129 | /** | ||
130 | * struct acpiphp_slot - PCI slot information | ||
131 | * | ||
132 | * PCI slot information for each *physical* PCI slot | ||
133 | */ | ||
134 | struct acpiphp_slot { | ||
135 | struct acpiphp_slot *next; | ||
136 | struct acpiphp_bridge *bridge; /* parent */ | ||
137 | struct list_head funcs; /* one slot may have different | ||
138 | objects (i.e. for each function) */ | ||
139 | struct semaphore crit_sect; | ||
140 | |||
141 | u32 id; /* slot id (serial #) for hotplug core */ | ||
142 | u8 device; /* pci device# */ | ||
143 | |||
144 | u32 sun; /* ACPI _SUN (slot unique number) */ | ||
145 | u32 slotno; /* slot number relative to bridge */ | ||
146 | u32 flags; /* see below */ | ||
147 | }; | ||
148 | |||
149 | |||
150 | /** | ||
151 | * struct acpiphp_func - PCI function information | ||
152 | * | ||
153 | * PCI function information for each object in ACPI namespace | ||
154 | * typically 8 objects per slot (i.e. for each PCI function) | ||
155 | */ | ||
156 | struct acpiphp_func { | ||
157 | struct acpiphp_slot *slot; /* parent */ | ||
158 | |||
159 | struct list_head sibling; | ||
160 | struct pci_dev *pci_dev; | ||
161 | |||
162 | acpi_handle handle; | ||
163 | |||
164 | u8 function; /* pci function# */ | ||
165 | u32 flags; /* see below */ | ||
166 | |||
167 | /* resources used for this function */ | ||
168 | struct pci_resource *mem_head; | ||
169 | struct pci_resource *p_mem_head; | ||
170 | struct pci_resource *io_head; | ||
171 | struct pci_resource *bus_head; | ||
172 | }; | ||
173 | |||
174 | /** | ||
175 | * struct acpiphp_attention_info - device specific attention registration | ||
176 | * | ||
177 | * ACPI has no generic method of setting/getting attention status | ||
178 | * this allows for device specific driver registration | ||
179 | */ | ||
180 | struct acpiphp_attention_info | ||
181 | { | ||
182 | int (*set_attn)(struct hotplug_slot *slot, u8 status); | ||
183 | int (*get_attn)(struct hotplug_slot *slot, u8 *status); | ||
184 | struct module *owner; | ||
185 | }; | ||
186 | |||
187 | /* PCI bus bridge HID */ | ||
188 | #define ACPI_PCI_HOST_HID "PNP0A03" | ||
189 | |||
190 | /* PCI BRIDGE type */ | ||
191 | #define BRIDGE_TYPE_HOST 0 | ||
192 | #define BRIDGE_TYPE_P2P 1 | ||
193 | |||
194 | /* ACPI _STA method value (ignore bit 4; battery present) */ | ||
195 | #define ACPI_STA_PRESENT (0x00000001) | ||
196 | #define ACPI_STA_ENABLED (0x00000002) | ||
197 | #define ACPI_STA_SHOW_IN_UI (0x00000004) | ||
198 | #define ACPI_STA_FUNCTIONING (0x00000008) | ||
199 | #define ACPI_STA_ALL (0x0000000f) | ||
200 | |||
201 | /* bridge flags */ | ||
202 | #define BRIDGE_HAS_STA (0x00000001) | ||
203 | #define BRIDGE_HAS_EJ0 (0x00000002) | ||
204 | #define BRIDGE_HAS_HPP (0x00000004) | ||
205 | #define BRIDGE_HAS_PS0 (0x00000010) | ||
206 | #define BRIDGE_HAS_PS1 (0x00000020) | ||
207 | #define BRIDGE_HAS_PS2 (0x00000040) | ||
208 | #define BRIDGE_HAS_PS3 (0x00000080) | ||
209 | |||
210 | /* slot flags */ | ||
211 | |||
212 | #define SLOT_POWEREDON (0x00000001) | ||
213 | #define SLOT_ENABLED (0x00000002) | ||
214 | #define SLOT_MULTIFUNCTION (0x00000004) | ||
215 | |||
216 | /* function flags */ | ||
217 | |||
218 | #define FUNC_HAS_STA (0x00000001) | ||
219 | #define FUNC_HAS_EJ0 (0x00000002) | ||
220 | #define FUNC_HAS_PS0 (0x00000010) | ||
221 | #define FUNC_HAS_PS1 (0x00000020) | ||
222 | #define FUNC_HAS_PS2 (0x00000040) | ||
223 | #define FUNC_HAS_PS3 (0x00000080) | ||
224 | |||
225 | /* function prototypes */ | ||
226 | |||
227 | /* acpiphp_core.c */ | ||
228 | extern int acpiphp_register_attention(struct acpiphp_attention_info*info); | ||
229 | extern int acpiphp_unregister_attention(struct acpiphp_attention_info *info); | ||
230 | |||
231 | /* acpiphp_glue.c */ | ||
232 | extern int acpiphp_glue_init (void); | ||
233 | extern void acpiphp_glue_exit (void); | ||
234 | extern int acpiphp_get_num_slots (void); | ||
235 | extern struct acpiphp_slot *get_slot_from_id (int id); | ||
236 | typedef int (*acpiphp_callback)(struct acpiphp_slot *slot, void *data); | ||
237 | |||
238 | extern int acpiphp_enable_slot (struct acpiphp_slot *slot); | ||
239 | extern int acpiphp_disable_slot (struct acpiphp_slot *slot); | ||
240 | extern u8 acpiphp_get_power_status (struct acpiphp_slot *slot); | ||
241 | extern u8 acpiphp_get_attention_status (struct acpiphp_slot *slot); | ||
242 | extern u8 acpiphp_get_latch_status (struct acpiphp_slot *slot); | ||
243 | extern u8 acpiphp_get_adapter_status (struct acpiphp_slot *slot); | ||
244 | extern u32 acpiphp_get_address (struct acpiphp_slot *slot); | ||
245 | |||
246 | /* acpiphp_pci.c */ | ||
247 | extern struct pci_dev *acpiphp_allocate_pcidev (struct pci_bus *pbus, int dev, int fn); | ||
248 | extern int acpiphp_configure_slot (struct acpiphp_slot *slot); | ||
249 | extern int acpiphp_configure_function (struct acpiphp_func *func); | ||
250 | extern void acpiphp_unconfigure_function (struct acpiphp_func *func); | ||
251 | extern int acpiphp_detect_pci_resource (struct acpiphp_bridge *bridge); | ||
252 | extern int acpiphp_init_func_resource (struct acpiphp_func *func); | ||
253 | |||
254 | /* acpiphp_res.c */ | ||
255 | extern struct pci_resource *acpiphp_get_io_resource (struct pci_resource **head, u32 size); | ||
256 | extern struct pci_resource *acpiphp_get_resource (struct pci_resource **head, u32 size); | ||
257 | extern struct pci_resource *acpiphp_get_resource_with_base (struct pci_resource **head, u64 base, u32 size); | ||
258 | extern int acpiphp_resource_sort_and_combine (struct pci_resource **head); | ||
259 | extern struct pci_resource *acpiphp_make_resource (u64 base, u32 length); | ||
260 | extern void acpiphp_move_resource (struct pci_resource **from, struct pci_resource **to); | ||
261 | extern void acpiphp_free_resource (struct pci_resource **res); | ||
262 | extern void acpiphp_dump_resource (struct acpiphp_bridge *bridge); /* debug */ | ||
263 | extern void acpiphp_dump_func_resource (struct acpiphp_func *func); /* debug */ | ||
264 | |||
265 | /* variables */ | ||
266 | extern int acpiphp_debug; | ||
267 | |||
268 | #endif /* _ACPIPHP_H */ | ||
diff --git a/drivers/pci/hotplug/acpiphp_core.c b/drivers/pci/hotplug/acpiphp_core.c new file mode 100644 index 000000000000..4539e61a3dc1 --- /dev/null +++ b/drivers/pci/hotplug/acpiphp_core.c | |||
@@ -0,0 +1,453 @@ | |||
1 | /* | ||
2 | * ACPI PCI Hot Plug Controller Driver | ||
3 | * | ||
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | ||
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * Copyright (C) 2001 IBM Corp. | ||
7 | * Copyright (C) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com) | ||
8 | * Copyright (C) 2002,2003 Takayoshi Kochi (t-kochi@bq.jp.nec.com) | ||
9 | * Copyright (C) 2002,2003 NEC Corporation | ||
10 | * | ||
11 | * All rights reserved. | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify | ||
14 | * it under the terms of the GNU General Public License as published by | ||
15 | * the Free Software Foundation; either version 2 of the License, or (at | ||
16 | * your option) any later version. | ||
17 | * | ||
18 | * This program is distributed in the hope that it will be useful, but | ||
19 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
20 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
21 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
22 | * details. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License | ||
25 | * along with this program; if not, write to the Free Software | ||
26 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
27 | * | ||
28 | * Send feedback to <gregkh@us.ibm.com>, | ||
29 | * <t-kochi@bq.jp.nec.com> | ||
30 | * | ||
31 | */ | ||
32 | |||
33 | #include <linux/init.h> | ||
34 | #include <linux/module.h> | ||
35 | #include <linux/moduleparam.h> | ||
36 | |||
37 | #include <linux/kernel.h> | ||
38 | #include <linux/pci.h> | ||
39 | #include <linux/slab.h> | ||
40 | #include <linux/smp.h> | ||
41 | #include <linux/smp_lock.h> | ||
42 | #include "pci_hotplug.h" | ||
43 | #include "acpiphp.h" | ||
44 | |||
45 | static LIST_HEAD(slot_list); | ||
46 | |||
47 | #define MY_NAME "acpiphp" | ||
48 | |||
49 | static int debug; | ||
50 | int acpiphp_debug; | ||
51 | |||
52 | /* local variables */ | ||
53 | static int num_slots; | ||
54 | static struct acpiphp_attention_info *attention_info; | ||
55 | |||
56 | #define DRIVER_VERSION "0.4" | ||
57 | #define DRIVER_AUTHOR "Greg Kroah-Hartman <gregkh@us.ibm.com>, Takayoshi Kochi <t-kochi@bq.jp.nec.com>" | ||
58 | #define DRIVER_DESC "ACPI Hot Plug PCI Controller Driver" | ||
59 | |||
60 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
61 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
62 | MODULE_LICENSE("GPL"); | ||
63 | MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); | ||
64 | module_param(debug, bool, 0644); | ||
65 | |||
66 | /* export the attention callback registration methods */ | ||
67 | EXPORT_SYMBOL_GPL(acpiphp_register_attention); | ||
68 | EXPORT_SYMBOL_GPL(acpiphp_unregister_attention); | ||
69 | |||
70 | static int enable_slot (struct hotplug_slot *slot); | ||
71 | static int disable_slot (struct hotplug_slot *slot); | ||
72 | static int set_attention_status (struct hotplug_slot *slot, u8 value); | ||
73 | static int get_power_status (struct hotplug_slot *slot, u8 *value); | ||
74 | static int get_attention_status (struct hotplug_slot *slot, u8 *value); | ||
75 | static int get_address (struct hotplug_slot *slot, u32 *value); | ||
76 | static int get_latch_status (struct hotplug_slot *slot, u8 *value); | ||
77 | static int get_adapter_status (struct hotplug_slot *slot, u8 *value); | ||
78 | |||
79 | static struct hotplug_slot_ops acpi_hotplug_slot_ops = { | ||
80 | .owner = THIS_MODULE, | ||
81 | .enable_slot = enable_slot, | ||
82 | .disable_slot = disable_slot, | ||
83 | .set_attention_status = set_attention_status, | ||
84 | .get_power_status = get_power_status, | ||
85 | .get_attention_status = get_attention_status, | ||
86 | .get_latch_status = get_latch_status, | ||
87 | .get_adapter_status = get_adapter_status, | ||
88 | .get_address = get_address, | ||
89 | }; | ||
90 | |||
91 | |||
92 | /** | ||
93 | * acpiphp_register_attention - set attention LED callback | ||
94 | * @info: must be completely filled with LED callbacks | ||
95 | * | ||
96 | * Description: this is used to register a hardware specific ACPI | ||
97 | * driver that manipulates the attention LED. All the fields in | ||
98 | * info must be set. | ||
99 | **/ | ||
100 | int acpiphp_register_attention(struct acpiphp_attention_info *info) | ||
101 | { | ||
102 | int retval = -EINVAL; | ||
103 | |||
104 | if (info && info->owner && info->set_attn && | ||
105 | info->get_attn && !attention_info) { | ||
106 | retval = 0; | ||
107 | attention_info = info; | ||
108 | } | ||
109 | return retval; | ||
110 | } | ||
111 | |||
112 | |||
113 | /** | ||
114 | * acpiphp_unregister_attention - unset attention LED callback | ||
115 | * @info: must match the pointer used to register | ||
116 | * | ||
117 | * Description: this is used to un-register a hardware specific acpi | ||
118 | * driver that manipulates the attention LED. The pointer to the | ||
119 | * info struct must be the same as the one used to set it. | ||
120 | **/ | ||
121 | int acpiphp_unregister_attention(struct acpiphp_attention_info *info) | ||
122 | { | ||
123 | int retval = -EINVAL; | ||
124 | |||
125 | if (info && attention_info == info) { | ||
126 | attention_info = NULL; | ||
127 | retval = 0; | ||
128 | } | ||
129 | return retval; | ||
130 | } | ||
131 | |||
132 | |||
133 | /** | ||
134 | * enable_slot - power on and enable a slot | ||
135 | * @hotplug_slot: slot to enable | ||
136 | * | ||
137 | * Actual tasks are done in acpiphp_enable_slot() | ||
138 | * | ||
139 | */ | ||
140 | static int enable_slot(struct hotplug_slot *hotplug_slot) | ||
141 | { | ||
142 | struct slot *slot = hotplug_slot->private; | ||
143 | |||
144 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
145 | |||
146 | /* enable the specified slot */ | ||
147 | return acpiphp_enable_slot(slot->acpi_slot); | ||
148 | } | ||
149 | |||
150 | |||
151 | /** | ||
152 | * disable_slot - disable and power off a slot | ||
153 | * @hotplug_slot: slot to disable | ||
154 | * | ||
155 | * Actual tasks are done in acpiphp_disable_slot() | ||
156 | * | ||
157 | */ | ||
158 | static int disable_slot(struct hotplug_slot *hotplug_slot) | ||
159 | { | ||
160 | struct slot *slot = hotplug_slot->private; | ||
161 | |||
162 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
163 | |||
164 | /* disable the specified slot */ | ||
165 | return acpiphp_disable_slot(slot->acpi_slot); | ||
166 | } | ||
167 | |||
168 | |||
169 | /** | ||
170 | * set_attention_status - set attention LED | ||
171 | * @hotplug_slot: slot to set attention LED on | ||
172 | * @status: value to set attention LED to (0 or 1) | ||
173 | * | ||
174 | * attention status LED, so we use a callback that | ||
175 | * was registered with us. This allows hardware specific | ||
176 | * ACPI implementations to blink the light for us. | ||
177 | **/ | ||
178 | static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status) | ||
179 | { | ||
180 | int retval = -ENODEV; | ||
181 | |||
182 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
183 | |||
184 | if (attention_info && try_module_get(attention_info->owner)) { | ||
185 | retval = attention_info->set_attn(hotplug_slot, status); | ||
186 | module_put(attention_info->owner); | ||
187 | } else | ||
188 | attention_info = NULL; | ||
189 | return retval; | ||
190 | } | ||
191 | |||
192 | |||
193 | /** | ||
194 | * get_power_status - get power status of a slot | ||
195 | * @hotplug_slot: slot to get status | ||
196 | * @value: pointer to store status | ||
197 | * | ||
198 | * Some platforms may not implement _STA method properly. | ||
199 | * In that case, the value returned may not be reliable. | ||
200 | * | ||
201 | */ | ||
202 | static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) | ||
203 | { | ||
204 | struct slot *slot = hotplug_slot->private; | ||
205 | |||
206 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
207 | |||
208 | *value = acpiphp_get_power_status(slot->acpi_slot); | ||
209 | |||
210 | return 0; | ||
211 | } | ||
212 | |||
213 | |||
214 | /** | ||
215 | * get_attention_status - get attention LED status | ||
216 | * @hotplug_slot: slot to get status from | ||
217 | * @value: returns with value of attention LED | ||
218 | * | ||
219 | * ACPI doesn't have known method to determine the state | ||
220 | * of the attention status LED, so we use a callback that | ||
221 | * was registered with us. This allows hardware specific | ||
222 | * ACPI implementations to determine its state | ||
223 | **/ | ||
224 | static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value) | ||
225 | { | ||
226 | int retval = -EINVAL; | ||
227 | |||
228 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
229 | |||
230 | if (attention_info && try_module_get(attention_info->owner)) { | ||
231 | retval = attention_info->get_attn(hotplug_slot, value); | ||
232 | module_put(attention_info->owner); | ||
233 | } else | ||
234 | attention_info = NULL; | ||
235 | return retval; | ||
236 | } | ||
237 | |||
238 | |||
239 | /** | ||
240 | * get_latch_status - get latch status of a slot | ||
241 | * @hotplug_slot: slot to get status | ||
242 | * @value: pointer to store status | ||
243 | * | ||
244 | * ACPI doesn't provide any formal means to access latch status. | ||
245 | * Instead, we fake latch status from _STA | ||
246 | * | ||
247 | */ | ||
248 | static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) | ||
249 | { | ||
250 | struct slot *slot = hotplug_slot->private; | ||
251 | |||
252 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
253 | |||
254 | *value = acpiphp_get_latch_status(slot->acpi_slot); | ||
255 | |||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | |||
260 | /** | ||
261 | * get_adapter_status - get adapter status of a slot | ||
262 | * @hotplug_slot: slot to get status | ||
263 | * @value: pointer to store status | ||
264 | * | ||
265 | * ACPI doesn't provide any formal means to access adapter status. | ||
266 | * Instead, we fake adapter status from _STA | ||
267 | * | ||
268 | */ | ||
269 | static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) | ||
270 | { | ||
271 | struct slot *slot = hotplug_slot->private; | ||
272 | |||
273 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
274 | |||
275 | *value = acpiphp_get_adapter_status(slot->acpi_slot); | ||
276 | |||
277 | return 0; | ||
278 | } | ||
279 | |||
280 | |||
281 | /** | ||
282 | * get_address - get pci address of a slot | ||
283 | * @hotplug_slot: slot to get status | ||
284 | * @busdev: pointer to struct pci_busdev (seg, bus, dev) | ||
285 | * | ||
286 | */ | ||
287 | static int get_address(struct hotplug_slot *hotplug_slot, u32 *value) | ||
288 | { | ||
289 | struct slot *slot = hotplug_slot->private; | ||
290 | |||
291 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
292 | |||
293 | *value = acpiphp_get_address(slot->acpi_slot); | ||
294 | |||
295 | return 0; | ||
296 | } | ||
297 | |||
298 | static int __init init_acpi(void) | ||
299 | { | ||
300 | int retval; | ||
301 | |||
302 | /* initialize internal data structure etc. */ | ||
303 | retval = acpiphp_glue_init(); | ||
304 | |||
305 | /* read initial number of slots */ | ||
306 | if (!retval) { | ||
307 | num_slots = acpiphp_get_num_slots(); | ||
308 | if (num_slots == 0) | ||
309 | retval = -ENODEV; | ||
310 | } | ||
311 | |||
312 | return retval; | ||
313 | } | ||
314 | |||
315 | |||
316 | /** | ||
317 | * make_slot_name - make a slot name that appears in pcihpfs | ||
318 | * @slot: slot to name | ||
319 | * | ||
320 | */ | ||
321 | static void make_slot_name(struct slot *slot) | ||
322 | { | ||
323 | snprintf(slot->hotplug_slot->name, SLOT_NAME_SIZE, "%u", | ||
324 | slot->acpi_slot->sun); | ||
325 | } | ||
326 | |||
327 | /** | ||
328 | * release_slot - free up the memory used by a slot | ||
329 | * @hotplug_slot: slot to free | ||
330 | */ | ||
331 | static void release_slot(struct hotplug_slot *hotplug_slot) | ||
332 | { | ||
333 | struct slot *slot = hotplug_slot->private; | ||
334 | |||
335 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
336 | |||
337 | kfree(slot->hotplug_slot->info); | ||
338 | kfree(slot->hotplug_slot->name); | ||
339 | kfree(slot->hotplug_slot); | ||
340 | kfree(slot); | ||
341 | } | ||
342 | |||
343 | /** | ||
344 | * init_slots - initialize 'struct slot' structures for each slot | ||
345 | * | ||
346 | */ | ||
347 | static int __init init_slots(void) | ||
348 | { | ||
349 | struct slot *slot; | ||
350 | int retval = -ENOMEM; | ||
351 | int i; | ||
352 | |||
353 | for (i = 0; i < num_slots; ++i) { | ||
354 | slot = kmalloc(sizeof(struct slot), GFP_KERNEL); | ||
355 | if (!slot) | ||
356 | goto error; | ||
357 | memset(slot, 0, sizeof(struct slot)); | ||
358 | |||
359 | slot->hotplug_slot = kmalloc(sizeof(struct hotplug_slot), GFP_KERNEL); | ||
360 | if (!slot->hotplug_slot) | ||
361 | goto error_slot; | ||
362 | memset(slot->hotplug_slot, 0, sizeof(struct hotplug_slot)); | ||
363 | |||
364 | slot->hotplug_slot->info = kmalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL); | ||
365 | if (!slot->hotplug_slot->info) | ||
366 | goto error_hpslot; | ||
367 | memset(slot->hotplug_slot->info, 0, sizeof(struct hotplug_slot_info)); | ||
368 | |||
369 | slot->hotplug_slot->name = kmalloc(SLOT_NAME_SIZE, GFP_KERNEL); | ||
370 | if (!slot->hotplug_slot->name) | ||
371 | goto error_info; | ||
372 | |||
373 | slot->number = i; | ||
374 | |||
375 | slot->hotplug_slot->private = slot; | ||
376 | slot->hotplug_slot->release = &release_slot; | ||
377 | slot->hotplug_slot->ops = &acpi_hotplug_slot_ops; | ||
378 | |||
379 | slot->acpi_slot = get_slot_from_id(i); | ||
380 | slot->hotplug_slot->info->power_status = acpiphp_get_power_status(slot->acpi_slot); | ||
381 | slot->hotplug_slot->info->attention_status = 0; | ||
382 | slot->hotplug_slot->info->latch_status = acpiphp_get_latch_status(slot->acpi_slot); | ||
383 | slot->hotplug_slot->info->adapter_status = acpiphp_get_adapter_status(slot->acpi_slot); | ||
384 | slot->hotplug_slot->info->max_bus_speed = PCI_SPEED_UNKNOWN; | ||
385 | slot->hotplug_slot->info->cur_bus_speed = PCI_SPEED_UNKNOWN; | ||
386 | |||
387 | make_slot_name(slot); | ||
388 | |||
389 | retval = pci_hp_register(slot->hotplug_slot); | ||
390 | if (retval) { | ||
391 | err("pci_hp_register failed with error %d\n", retval); | ||
392 | goto error_name; | ||
393 | } | ||
394 | |||
395 | /* add slot to our internal list */ | ||
396 | list_add(&slot->slot_list, &slot_list); | ||
397 | info("Slot [%s] registered\n", slot->hotplug_slot->name); | ||
398 | } | ||
399 | |||
400 | return 0; | ||
401 | error_name: | ||
402 | kfree(slot->hotplug_slot->name); | ||
403 | error_info: | ||
404 | kfree(slot->hotplug_slot->info); | ||
405 | error_hpslot: | ||
406 | kfree(slot->hotplug_slot); | ||
407 | error_slot: | ||
408 | kfree(slot); | ||
409 | error: | ||
410 | return retval; | ||
411 | } | ||
412 | |||
413 | |||
414 | static void __exit cleanup_slots (void) | ||
415 | { | ||
416 | struct list_head *tmp, *n; | ||
417 | struct slot *slot; | ||
418 | |||
419 | list_for_each_safe (tmp, n, &slot_list) { | ||
420 | /* memory will be freed in release_slot callback */ | ||
421 | slot = list_entry(tmp, struct slot, slot_list); | ||
422 | list_del(&slot->slot_list); | ||
423 | pci_hp_deregister(slot->hotplug_slot); | ||
424 | } | ||
425 | } | ||
426 | |||
427 | |||
428 | static int __init acpiphp_init(void) | ||
429 | { | ||
430 | int retval; | ||
431 | |||
432 | info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); | ||
433 | |||
434 | acpiphp_debug = debug; | ||
435 | |||
436 | /* read all the ACPI info from the system */ | ||
437 | retval = init_acpi(); | ||
438 | if (retval) | ||
439 | return retval; | ||
440 | |||
441 | return init_slots(); | ||
442 | } | ||
443 | |||
444 | |||
445 | static void __exit acpiphp_exit(void) | ||
446 | { | ||
447 | cleanup_slots(); | ||
448 | /* deallocate internal data structures etc. */ | ||
449 | acpiphp_glue_exit(); | ||
450 | } | ||
451 | |||
452 | module_init(acpiphp_init); | ||
453 | module_exit(acpiphp_exit); | ||
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c new file mode 100644 index 000000000000..e7f41294f811 --- /dev/null +++ b/drivers/pci/hotplug/acpiphp_glue.c | |||
@@ -0,0 +1,1344 @@ | |||
1 | /* | ||
2 | * ACPI PCI HotPlug glue functions to ACPI CA subsystem | ||
3 | * | ||
4 | * Copyright (C) 2002,2003 Takayoshi Kochi (t-kochi@bq.jp.nec.com) | ||
5 | * Copyright (C) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com) | ||
6 | * Copyright (C) 2002,2003 NEC Corporation | ||
7 | * | ||
8 | * All rights reserved. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or (at | ||
13 | * your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
18 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
19 | * details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
24 | * | ||
25 | * Send feedback to <t-kochi@bq.jp.nec.com> | ||
26 | * | ||
27 | */ | ||
28 | |||
29 | #include <linux/init.h> | ||
30 | #include <linux/module.h> | ||
31 | |||
32 | #include <linux/kernel.h> | ||
33 | #include <linux/pci.h> | ||
34 | #include <linux/smp_lock.h> | ||
35 | #include <asm/semaphore.h> | ||
36 | |||
37 | #include "../pci.h" | ||
38 | #include "pci_hotplug.h" | ||
39 | #include "acpiphp.h" | ||
40 | |||
41 | static LIST_HEAD(bridge_list); | ||
42 | |||
43 | #define MY_NAME "acpiphp_glue" | ||
44 | |||
45 | static void handle_hotplug_event_bridge (acpi_handle, u32, void *); | ||
46 | static void handle_hotplug_event_func (acpi_handle, u32, void *); | ||
47 | |||
48 | /* | ||
49 | * initialization & terminatation routines | ||
50 | */ | ||
51 | |||
52 | /** | ||
53 | * is_ejectable - determine if a slot is ejectable | ||
54 | * @handle: handle to acpi namespace | ||
55 | * | ||
56 | * Ejectable slot should satisfy at least these conditions: | ||
57 | * | ||
58 | * 1. has _ADR method | ||
59 | * 2. has _EJ0 method | ||
60 | * | ||
61 | * optionally | ||
62 | * | ||
63 | * 1. has _STA method | ||
64 | * 2. has _PS0 method | ||
65 | * 3. has _PS3 method | ||
66 | * 4. .. | ||
67 | * | ||
68 | */ | ||
69 | static int is_ejectable(acpi_handle handle) | ||
70 | { | ||
71 | acpi_status status; | ||
72 | acpi_handle tmp; | ||
73 | |||
74 | status = acpi_get_handle(handle, "_ADR", &tmp); | ||
75 | if (ACPI_FAILURE(status)) { | ||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | status = acpi_get_handle(handle, "_EJ0", &tmp); | ||
80 | if (ACPI_FAILURE(status)) { | ||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | return 1; | ||
85 | } | ||
86 | |||
87 | |||
88 | /* callback routine to check the existence of ejectable slots */ | ||
89 | static acpi_status | ||
90 | is_ejectable_slot(acpi_handle handle, u32 lvl, void *context, void **rv) | ||
91 | { | ||
92 | int *count = (int *)context; | ||
93 | |||
94 | if (is_ejectable(handle)) { | ||
95 | (*count)++; | ||
96 | /* only one ejectable slot is enough */ | ||
97 | return AE_CTRL_TERMINATE; | ||
98 | } else { | ||
99 | return AE_OK; | ||
100 | } | ||
101 | } | ||
102 | |||
103 | |||
104 | /* callback routine to register each ACPI PCI slot object */ | ||
105 | static acpi_status | ||
106 | register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) | ||
107 | { | ||
108 | struct acpiphp_bridge *bridge = (struct acpiphp_bridge *)context; | ||
109 | struct acpiphp_slot *slot; | ||
110 | struct acpiphp_func *newfunc; | ||
111 | acpi_handle tmp; | ||
112 | acpi_status status = AE_OK; | ||
113 | unsigned long adr, sun; | ||
114 | int device, function; | ||
115 | static int num_slots = 0; /* XXX if we support I/O node hotplug... */ | ||
116 | |||
117 | status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr); | ||
118 | |||
119 | if (ACPI_FAILURE(status)) | ||
120 | return AE_OK; | ||
121 | |||
122 | status = acpi_get_handle(handle, "_EJ0", &tmp); | ||
123 | |||
124 | if (ACPI_FAILURE(status)) | ||
125 | return AE_OK; | ||
126 | |||
127 | device = (adr >> 16) & 0xffff; | ||
128 | function = adr & 0xffff; | ||
129 | |||
130 | newfunc = kmalloc(sizeof(struct acpiphp_func), GFP_KERNEL); | ||
131 | if (!newfunc) | ||
132 | return AE_NO_MEMORY; | ||
133 | memset(newfunc, 0, sizeof(struct acpiphp_func)); | ||
134 | |||
135 | INIT_LIST_HEAD(&newfunc->sibling); | ||
136 | newfunc->handle = handle; | ||
137 | newfunc->function = function; | ||
138 | newfunc->flags = FUNC_HAS_EJ0; | ||
139 | |||
140 | if (ACPI_SUCCESS(acpi_get_handle(handle, "_STA", &tmp))) | ||
141 | newfunc->flags |= FUNC_HAS_STA; | ||
142 | |||
143 | if (ACPI_SUCCESS(acpi_get_handle(handle, "_PS0", &tmp))) | ||
144 | newfunc->flags |= FUNC_HAS_PS0; | ||
145 | |||
146 | if (ACPI_SUCCESS(acpi_get_handle(handle, "_PS3", &tmp))) | ||
147 | newfunc->flags |= FUNC_HAS_PS3; | ||
148 | |||
149 | status = acpi_evaluate_integer(handle, "_SUN", NULL, &sun); | ||
150 | if (ACPI_FAILURE(status)) | ||
151 | sun = -1; | ||
152 | |||
153 | /* search for objects that share the same slot */ | ||
154 | for (slot = bridge->slots; slot; slot = slot->next) | ||
155 | if (slot->device == device) { | ||
156 | if (slot->sun != sun) | ||
157 | warn("sibling found, but _SUN doesn't match!\n"); | ||
158 | break; | ||
159 | } | ||
160 | |||
161 | if (!slot) { | ||
162 | slot = kmalloc(sizeof(struct acpiphp_slot), GFP_KERNEL); | ||
163 | if (!slot) { | ||
164 | kfree(newfunc); | ||
165 | return AE_NO_MEMORY; | ||
166 | } | ||
167 | |||
168 | memset(slot, 0, sizeof(struct acpiphp_slot)); | ||
169 | slot->bridge = bridge; | ||
170 | slot->id = num_slots++; | ||
171 | slot->device = device; | ||
172 | slot->sun = sun; | ||
173 | INIT_LIST_HEAD(&slot->funcs); | ||
174 | init_MUTEX(&slot->crit_sect); | ||
175 | |||
176 | slot->next = bridge->slots; | ||
177 | bridge->slots = slot; | ||
178 | |||
179 | bridge->nr_slots++; | ||
180 | |||
181 | dbg("found ACPI PCI Hotplug slot at PCI %02x:%02x Slot:%d\n", | ||
182 | slot->bridge->bus, slot->device, slot->sun); | ||
183 | } | ||
184 | |||
185 | newfunc->slot = slot; | ||
186 | list_add_tail(&newfunc->sibling, &slot->funcs); | ||
187 | |||
188 | /* associate corresponding pci_dev */ | ||
189 | newfunc->pci_dev = pci_find_slot(bridge->bus, | ||
190 | PCI_DEVFN(device, function)); | ||
191 | if (newfunc->pci_dev) { | ||
192 | if (acpiphp_init_func_resource(newfunc) < 0) { | ||
193 | kfree(newfunc); | ||
194 | return AE_ERROR; | ||
195 | } | ||
196 | slot->flags |= (SLOT_ENABLED | SLOT_POWEREDON); | ||
197 | } | ||
198 | |||
199 | /* install notify handler */ | ||
200 | status = acpi_install_notify_handler(handle, | ||
201 | ACPI_SYSTEM_NOTIFY, | ||
202 | handle_hotplug_event_func, | ||
203 | newfunc); | ||
204 | |||
205 | if (ACPI_FAILURE(status)) { | ||
206 | err("failed to register interrupt notify handler\n"); | ||
207 | return status; | ||
208 | } | ||
209 | |||
210 | return AE_OK; | ||
211 | } | ||
212 | |||
213 | |||
214 | /* see if it's worth looking at this bridge */ | ||
215 | static int detect_ejectable_slots(acpi_handle *bridge_handle) | ||
216 | { | ||
217 | acpi_status status; | ||
218 | int count; | ||
219 | |||
220 | count = 0; | ||
221 | |||
222 | /* only check slots defined directly below bridge object */ | ||
223 | status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle, (u32)1, | ||
224 | is_ejectable_slot, (void *)&count, NULL); | ||
225 | |||
226 | return count; | ||
227 | } | ||
228 | |||
229 | |||
230 | /* decode ACPI _CRS data and convert into our internal resource list | ||
231 | * TBD: _TRA, etc. | ||
232 | */ | ||
233 | static acpi_status | ||
234 | decode_acpi_resource(struct acpi_resource *resource, void *context) | ||
235 | { | ||
236 | struct acpiphp_bridge *bridge = (struct acpiphp_bridge *) context; | ||
237 | struct acpi_resource_address64 address; | ||
238 | struct pci_resource *res; | ||
239 | |||
240 | if (resource->id != ACPI_RSTYPE_ADDRESS16 && | ||
241 | resource->id != ACPI_RSTYPE_ADDRESS32 && | ||
242 | resource->id != ACPI_RSTYPE_ADDRESS64) | ||
243 | return AE_OK; | ||
244 | |||
245 | acpi_resource_to_address64(resource, &address); | ||
246 | |||
247 | if (address.producer_consumer == ACPI_PRODUCER && address.address_length > 0) { | ||
248 | dbg("resource type: %d: 0x%llx - 0x%llx\n", address.resource_type, | ||
249 | (unsigned long long)address.min_address_range, | ||
250 | (unsigned long long)address.max_address_range); | ||
251 | res = acpiphp_make_resource(address.min_address_range, | ||
252 | address.address_length); | ||
253 | if (!res) { | ||
254 | err("out of memory\n"); | ||
255 | return AE_OK; | ||
256 | } | ||
257 | |||
258 | switch (address.resource_type) { | ||
259 | case ACPI_MEMORY_RANGE: | ||
260 | if (address.attribute.memory.cache_attribute == ACPI_PREFETCHABLE_MEMORY) { | ||
261 | res->next = bridge->p_mem_head; | ||
262 | bridge->p_mem_head = res; | ||
263 | } else { | ||
264 | res->next = bridge->mem_head; | ||
265 | bridge->mem_head = res; | ||
266 | } | ||
267 | break; | ||
268 | case ACPI_IO_RANGE: | ||
269 | res->next = bridge->io_head; | ||
270 | bridge->io_head = res; | ||
271 | break; | ||
272 | case ACPI_BUS_NUMBER_RANGE: | ||
273 | res->next = bridge->bus_head; | ||
274 | bridge->bus_head = res; | ||
275 | break; | ||
276 | default: | ||
277 | /* invalid type */ | ||
278 | kfree(res); | ||
279 | break; | ||
280 | } | ||
281 | } | ||
282 | |||
283 | return AE_OK; | ||
284 | } | ||
285 | |||
286 | /* decode ACPI 2.0 _HPP hot plug parameters */ | ||
287 | static void decode_hpp(struct acpiphp_bridge *bridge) | ||
288 | { | ||
289 | acpi_status status; | ||
290 | struct acpi_buffer buffer = { .length = ACPI_ALLOCATE_BUFFER, | ||
291 | .pointer = NULL}; | ||
292 | union acpi_object *package; | ||
293 | int i; | ||
294 | |||
295 | /* default numbers */ | ||
296 | bridge->hpp.cache_line_size = 0x10; | ||
297 | bridge->hpp.latency_timer = 0x40; | ||
298 | bridge->hpp.enable_SERR = 0; | ||
299 | bridge->hpp.enable_PERR = 0; | ||
300 | |||
301 | status = acpi_evaluate_object(bridge->handle, "_HPP", NULL, &buffer); | ||
302 | |||
303 | if (ACPI_FAILURE(status)) { | ||
304 | dbg("_HPP evaluation failed\n"); | ||
305 | return; | ||
306 | } | ||
307 | |||
308 | package = (union acpi_object *) buffer.pointer; | ||
309 | |||
310 | if (!package || package->type != ACPI_TYPE_PACKAGE || | ||
311 | package->package.count != 4 || !package->package.elements) { | ||
312 | err("invalid _HPP object; ignoring\n"); | ||
313 | goto err_exit; | ||
314 | } | ||
315 | |||
316 | for (i = 0; i < 4; i++) { | ||
317 | if (package->package.elements[i].type != ACPI_TYPE_INTEGER) { | ||
318 | err("invalid _HPP parameter type; ignoring\n"); | ||
319 | goto err_exit; | ||
320 | } | ||
321 | } | ||
322 | |||
323 | bridge->hpp.cache_line_size = package->package.elements[0].integer.value; | ||
324 | bridge->hpp.latency_timer = package->package.elements[1].integer.value; | ||
325 | bridge->hpp.enable_SERR = package->package.elements[2].integer.value; | ||
326 | bridge->hpp.enable_PERR = package->package.elements[3].integer.value; | ||
327 | |||
328 | dbg("_HPP parameter = (%02x, %02x, %02x, %02x)\n", | ||
329 | bridge->hpp.cache_line_size, | ||
330 | bridge->hpp.latency_timer, | ||
331 | bridge->hpp.enable_SERR, | ||
332 | bridge->hpp.enable_PERR); | ||
333 | |||
334 | bridge->flags |= BRIDGE_HAS_HPP; | ||
335 | |||
336 | err_exit: | ||
337 | kfree(buffer.pointer); | ||
338 | } | ||
339 | |||
340 | |||
341 | /* initialize miscellaneous stuff for both root and PCI-to-PCI bridge */ | ||
342 | static void init_bridge_misc(struct acpiphp_bridge *bridge) | ||
343 | { | ||
344 | acpi_status status; | ||
345 | |||
346 | /* decode ACPI 2.0 _HPP (hot plug parameters) */ | ||
347 | decode_hpp(bridge); | ||
348 | |||
349 | /* subtract all resources already allocated */ | ||
350 | acpiphp_detect_pci_resource(bridge); | ||
351 | |||
352 | /* register all slot objects under this bridge */ | ||
353 | status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge->handle, (u32)1, | ||
354 | register_slot, bridge, NULL); | ||
355 | |||
356 | /* install notify handler */ | ||
357 | status = acpi_install_notify_handler(bridge->handle, | ||
358 | ACPI_SYSTEM_NOTIFY, | ||
359 | handle_hotplug_event_bridge, | ||
360 | bridge); | ||
361 | |||
362 | if (ACPI_FAILURE(status)) { | ||
363 | err("failed to register interrupt notify handler\n"); | ||
364 | } | ||
365 | |||
366 | list_add(&bridge->list, &bridge_list); | ||
367 | |||
368 | dbg("Bridge resource:\n"); | ||
369 | acpiphp_dump_resource(bridge); | ||
370 | } | ||
371 | |||
372 | |||
373 | /* allocate and initialize host bridge data structure */ | ||
374 | static void add_host_bridge(acpi_handle *handle, int seg, int bus) | ||
375 | { | ||
376 | acpi_status status; | ||
377 | struct acpiphp_bridge *bridge; | ||
378 | |||
379 | bridge = kmalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL); | ||
380 | if (bridge == NULL) | ||
381 | return; | ||
382 | |||
383 | memset(bridge, 0, sizeof(struct acpiphp_bridge)); | ||
384 | |||
385 | bridge->type = BRIDGE_TYPE_HOST; | ||
386 | bridge->handle = handle; | ||
387 | bridge->seg = seg; | ||
388 | bridge->bus = bus; | ||
389 | |||
390 | bridge->pci_bus = pci_find_bus(seg, bus); | ||
391 | |||
392 | spin_lock_init(&bridge->res_lock); | ||
393 | |||
394 | /* to be overridden when we decode _CRS */ | ||
395 | bridge->sub = bridge->bus; | ||
396 | |||
397 | /* decode resources */ | ||
398 | |||
399 | status = acpi_walk_resources(handle, METHOD_NAME__CRS, | ||
400 | decode_acpi_resource, bridge); | ||
401 | |||
402 | if (ACPI_FAILURE(status)) { | ||
403 | err("failed to decode bridge resources\n"); | ||
404 | kfree(bridge); | ||
405 | return; | ||
406 | } | ||
407 | |||
408 | acpiphp_resource_sort_and_combine(&bridge->io_head); | ||
409 | acpiphp_resource_sort_and_combine(&bridge->mem_head); | ||
410 | acpiphp_resource_sort_and_combine(&bridge->p_mem_head); | ||
411 | acpiphp_resource_sort_and_combine(&bridge->bus_head); | ||
412 | |||
413 | dbg("ACPI _CRS resource:\n"); | ||
414 | acpiphp_dump_resource(bridge); | ||
415 | |||
416 | if (bridge->bus_head) { | ||
417 | bridge->bus = bridge->bus_head->base; | ||
418 | bridge->sub = bridge->bus_head->base + bridge->bus_head->length - 1; | ||
419 | } | ||
420 | |||
421 | init_bridge_misc(bridge); | ||
422 | } | ||
423 | |||
424 | |||
425 | /* allocate and initialize PCI-to-PCI bridge data structure */ | ||
426 | static void add_p2p_bridge(acpi_handle *handle, int seg, int bus, int dev, int fn) | ||
427 | { | ||
428 | struct acpiphp_bridge *bridge; | ||
429 | u8 tmp8; | ||
430 | u16 tmp16; | ||
431 | u64 base64, limit64; | ||
432 | u32 base, limit, base32u, limit32u; | ||
433 | |||
434 | bridge = kmalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL); | ||
435 | if (bridge == NULL) { | ||
436 | err("out of memory\n"); | ||
437 | return; | ||
438 | } | ||
439 | |||
440 | memset(bridge, 0, sizeof(struct acpiphp_bridge)); | ||
441 | |||
442 | bridge->type = BRIDGE_TYPE_P2P; | ||
443 | bridge->handle = handle; | ||
444 | bridge->seg = seg; | ||
445 | |||
446 | bridge->pci_dev = pci_find_slot(bus, PCI_DEVFN(dev, fn)); | ||
447 | if (!bridge->pci_dev) { | ||
448 | err("Can't get pci_dev\n"); | ||
449 | kfree(bridge); | ||
450 | return; | ||
451 | } | ||
452 | |||
453 | bridge->pci_bus = bridge->pci_dev->subordinate; | ||
454 | if (!bridge->pci_bus) { | ||
455 | err("This is not a PCI-to-PCI bridge!\n"); | ||
456 | kfree(bridge); | ||
457 | return; | ||
458 | } | ||
459 | |||
460 | spin_lock_init(&bridge->res_lock); | ||
461 | |||
462 | bridge->bus = bridge->pci_bus->number; | ||
463 | bridge->sub = bridge->pci_bus->subordinate; | ||
464 | |||
465 | /* | ||
466 | * decode resources under this P2P bridge | ||
467 | */ | ||
468 | |||
469 | /* I/O resources */ | ||
470 | pci_read_config_byte(bridge->pci_dev, PCI_IO_BASE, &tmp8); | ||
471 | base = tmp8; | ||
472 | pci_read_config_byte(bridge->pci_dev, PCI_IO_LIMIT, &tmp8); | ||
473 | limit = tmp8; | ||
474 | |||
475 | switch (base & PCI_IO_RANGE_TYPE_MASK) { | ||
476 | case PCI_IO_RANGE_TYPE_16: | ||
477 | base = (base << 8) & 0xf000; | ||
478 | limit = ((limit << 8) & 0xf000) + 0xfff; | ||
479 | bridge->io_head = acpiphp_make_resource((u64)base, limit - base + 1); | ||
480 | if (!bridge->io_head) { | ||
481 | err("out of memory\n"); | ||
482 | kfree(bridge); | ||
483 | return; | ||
484 | } | ||
485 | dbg("16bit I/O range: %04x-%04x\n", | ||
486 | (u32)bridge->io_head->base, | ||
487 | (u32)(bridge->io_head->base + bridge->io_head->length - 1)); | ||
488 | break; | ||
489 | case PCI_IO_RANGE_TYPE_32: | ||
490 | pci_read_config_word(bridge->pci_dev, PCI_IO_BASE_UPPER16, &tmp16); | ||
491 | base = ((u32)tmp16 << 16) | ((base << 8) & 0xf000); | ||
492 | pci_read_config_word(bridge->pci_dev, PCI_IO_LIMIT_UPPER16, &tmp16); | ||
493 | limit = (((u32)tmp16 << 16) | ((limit << 8) & 0xf000)) + 0xfff; | ||
494 | bridge->io_head = acpiphp_make_resource((u64)base, limit - base + 1); | ||
495 | if (!bridge->io_head) { | ||
496 | err("out of memory\n"); | ||
497 | kfree(bridge); | ||
498 | return; | ||
499 | } | ||
500 | dbg("32bit I/O range: %08x-%08x\n", | ||
501 | (u32)bridge->io_head->base, | ||
502 | (u32)(bridge->io_head->base + bridge->io_head->length - 1)); | ||
503 | break; | ||
504 | case 0x0f: | ||
505 | dbg("I/O space unsupported\n"); | ||
506 | break; | ||
507 | default: | ||
508 | warn("Unknown I/O range type\n"); | ||
509 | } | ||
510 | |||
511 | /* Memory resources (mandatory for P2P bridge) */ | ||
512 | pci_read_config_word(bridge->pci_dev, PCI_MEMORY_BASE, &tmp16); | ||
513 | base = (tmp16 & 0xfff0) << 16; | ||
514 | pci_read_config_word(bridge->pci_dev, PCI_MEMORY_LIMIT, &tmp16); | ||
515 | limit = ((tmp16 & 0xfff0) << 16) | 0xfffff; | ||
516 | bridge->mem_head = acpiphp_make_resource((u64)base, limit - base + 1); | ||
517 | if (!bridge->mem_head) { | ||
518 | err("out of memory\n"); | ||
519 | kfree(bridge); | ||
520 | return; | ||
521 | } | ||
522 | dbg("32bit Memory range: %08x-%08x\n", | ||
523 | (u32)bridge->mem_head->base, | ||
524 | (u32)(bridge->mem_head->base + bridge->mem_head->length-1)); | ||
525 | |||
526 | /* Prefetchable Memory resources (optional) */ | ||
527 | pci_read_config_word(bridge->pci_dev, PCI_PREF_MEMORY_BASE, &tmp16); | ||
528 | base = tmp16; | ||
529 | pci_read_config_word(bridge->pci_dev, PCI_PREF_MEMORY_LIMIT, &tmp16); | ||
530 | limit = tmp16; | ||
531 | |||
532 | switch (base & PCI_MEMORY_RANGE_TYPE_MASK) { | ||
533 | case PCI_PREF_RANGE_TYPE_32: | ||
534 | base = (base & 0xfff0) << 16; | ||
535 | limit = ((limit & 0xfff0) << 16) | 0xfffff; | ||
536 | bridge->p_mem_head = acpiphp_make_resource((u64)base, limit - base + 1); | ||
537 | if (!bridge->p_mem_head) { | ||
538 | err("out of memory\n"); | ||
539 | kfree(bridge); | ||
540 | return; | ||
541 | } | ||
542 | dbg("32bit Prefetchable memory range: %08x-%08x\n", | ||
543 | (u32)bridge->p_mem_head->base, | ||
544 | (u32)(bridge->p_mem_head->base + bridge->p_mem_head->length - 1)); | ||
545 | break; | ||
546 | case PCI_PREF_RANGE_TYPE_64: | ||
547 | pci_read_config_dword(bridge->pci_dev, PCI_PREF_BASE_UPPER32, &base32u); | ||
548 | pci_read_config_dword(bridge->pci_dev, PCI_PREF_LIMIT_UPPER32, &limit32u); | ||
549 | base64 = ((u64)base32u << 32) | ((base & 0xfff0) << 16); | ||
550 | limit64 = (((u64)limit32u << 32) | ((limit & 0xfff0) << 16)) + 0xfffff; | ||
551 | |||
552 | bridge->p_mem_head = acpiphp_make_resource(base64, limit64 - base64 + 1); | ||
553 | if (!bridge->p_mem_head) { | ||
554 | err("out of memory\n"); | ||
555 | kfree(bridge); | ||
556 | return; | ||
557 | } | ||
558 | dbg("64bit Prefetchable memory range: %08x%08x-%08x%08x\n", | ||
559 | (u32)(bridge->p_mem_head->base >> 32), | ||
560 | (u32)(bridge->p_mem_head->base & 0xffffffff), | ||
561 | (u32)((bridge->p_mem_head->base + bridge->p_mem_head->length - 1) >> 32), | ||
562 | (u32)((bridge->p_mem_head->base + bridge->p_mem_head->length - 1) & 0xffffffff)); | ||
563 | break; | ||
564 | case 0x0f: | ||
565 | break; | ||
566 | default: | ||
567 | warn("Unknown prefetchale memory type\n"); | ||
568 | } | ||
569 | |||
570 | init_bridge_misc(bridge); | ||
571 | } | ||
572 | |||
573 | |||
574 | /* callback routine to find P2P bridges */ | ||
575 | static acpi_status | ||
576 | find_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv) | ||
577 | { | ||
578 | acpi_status status; | ||
579 | acpi_handle dummy_handle; | ||
580 | unsigned long *segbus = context; | ||
581 | unsigned long tmp; | ||
582 | int seg, bus, device, function; | ||
583 | struct pci_dev *dev; | ||
584 | |||
585 | /* get PCI address */ | ||
586 | seg = (*segbus >> 8) & 0xff; | ||
587 | bus = *segbus & 0xff; | ||
588 | |||
589 | status = acpi_get_handle(handle, "_ADR", &dummy_handle); | ||
590 | if (ACPI_FAILURE(status)) | ||
591 | return AE_OK; /* continue */ | ||
592 | |||
593 | status = acpi_evaluate_integer(handle, "_ADR", NULL, &tmp); | ||
594 | if (ACPI_FAILURE(status)) { | ||
595 | dbg("%s: _ADR evaluation failure\n", __FUNCTION__); | ||
596 | return AE_OK; | ||
597 | } | ||
598 | |||
599 | device = (tmp >> 16) & 0xffff; | ||
600 | function = tmp & 0xffff; | ||
601 | |||
602 | dev = pci_find_slot(bus, PCI_DEVFN(device, function)); | ||
603 | |||
604 | if (!dev) | ||
605 | return AE_OK; | ||
606 | |||
607 | if (!dev->subordinate) | ||
608 | return AE_OK; | ||
609 | |||
610 | /* check if this bridge has ejectable slots */ | ||
611 | if (detect_ejectable_slots(handle) > 0) { | ||
612 | dbg("found PCI-to-PCI bridge at PCI %s\n", pci_name(dev)); | ||
613 | add_p2p_bridge(handle, seg, bus, device, function); | ||
614 | } | ||
615 | |||
616 | return AE_OK; | ||
617 | } | ||
618 | |||
619 | |||
620 | /* find hot-pluggable slots, and then find P2P bridge */ | ||
621 | static int add_bridge(acpi_handle handle) | ||
622 | { | ||
623 | acpi_status status; | ||
624 | unsigned long tmp; | ||
625 | int seg, bus; | ||
626 | acpi_handle dummy_handle; | ||
627 | |||
628 | /* if the bridge doesn't have _STA, we assume it is always there */ | ||
629 | status = acpi_get_handle(handle, "_STA", &dummy_handle); | ||
630 | if (ACPI_SUCCESS(status)) { | ||
631 | status = acpi_evaluate_integer(handle, "_STA", NULL, &tmp); | ||
632 | if (ACPI_FAILURE(status)) { | ||
633 | dbg("%s: _STA evaluation failure\n", __FUNCTION__); | ||
634 | return 0; | ||
635 | } | ||
636 | if ((tmp & ACPI_STA_FUNCTIONING) == 0) | ||
637 | /* don't register this object */ | ||
638 | return 0; | ||
639 | } | ||
640 | |||
641 | /* get PCI segment number */ | ||
642 | status = acpi_evaluate_integer(handle, "_SEG", NULL, &tmp); | ||
643 | |||
644 | seg = ACPI_SUCCESS(status) ? tmp : 0; | ||
645 | |||
646 | /* get PCI bus number */ | ||
647 | status = acpi_evaluate_integer(handle, "_BBN", NULL, &tmp); | ||
648 | |||
649 | if (ACPI_SUCCESS(status)) { | ||
650 | bus = tmp; | ||
651 | } else { | ||
652 | warn("can't get bus number, assuming 0\n"); | ||
653 | bus = 0; | ||
654 | } | ||
655 | |||
656 | /* check if this bridge has ejectable slots */ | ||
657 | if (detect_ejectable_slots(handle) > 0) { | ||
658 | dbg("found PCI host-bus bridge with hot-pluggable slots\n"); | ||
659 | add_host_bridge(handle, seg, bus); | ||
660 | return 0; | ||
661 | } | ||
662 | |||
663 | tmp = seg << 8 | bus; | ||
664 | |||
665 | /* search P2P bridges under this host bridge */ | ||
666 | status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, | ||
667 | find_p2p_bridge, &tmp, NULL); | ||
668 | |||
669 | if (ACPI_FAILURE(status)) | ||
670 | warn("find_p2p_bridge faied (error code = 0x%x)\n",status); | ||
671 | |||
672 | return 0; | ||
673 | } | ||
674 | |||
675 | |||
676 | static void remove_bridge(acpi_handle handle) | ||
677 | { | ||
678 | /* No-op for now .. */ | ||
679 | } | ||
680 | |||
681 | |||
682 | static int power_on_slot(struct acpiphp_slot *slot) | ||
683 | { | ||
684 | acpi_status status; | ||
685 | struct acpiphp_func *func; | ||
686 | struct list_head *l; | ||
687 | int retval = 0; | ||
688 | |||
689 | /* if already enabled, just skip */ | ||
690 | if (slot->flags & SLOT_POWEREDON) | ||
691 | goto err_exit; | ||
692 | |||
693 | list_for_each (l, &slot->funcs) { | ||
694 | func = list_entry(l, struct acpiphp_func, sibling); | ||
695 | |||
696 | if (func->flags & FUNC_HAS_PS0) { | ||
697 | dbg("%s: executing _PS0\n", __FUNCTION__); | ||
698 | status = acpi_evaluate_object(func->handle, "_PS0", NULL, NULL); | ||
699 | if (ACPI_FAILURE(status)) { | ||
700 | warn("%s: _PS0 failed\n", __FUNCTION__); | ||
701 | retval = -1; | ||
702 | goto err_exit; | ||
703 | } else | ||
704 | break; | ||
705 | } | ||
706 | } | ||
707 | |||
708 | /* TBD: evaluate _STA to check if the slot is enabled */ | ||
709 | |||
710 | slot->flags |= SLOT_POWEREDON; | ||
711 | |||
712 | err_exit: | ||
713 | return retval; | ||
714 | } | ||
715 | |||
716 | |||
717 | static int power_off_slot(struct acpiphp_slot *slot) | ||
718 | { | ||
719 | acpi_status status; | ||
720 | struct acpiphp_func *func; | ||
721 | struct list_head *l; | ||
722 | struct acpi_object_list arg_list; | ||
723 | union acpi_object arg; | ||
724 | |||
725 | int retval = 0; | ||
726 | |||
727 | /* if already disabled, just skip */ | ||
728 | if ((slot->flags & SLOT_POWEREDON) == 0) | ||
729 | goto err_exit; | ||
730 | |||
731 | list_for_each (l, &slot->funcs) { | ||
732 | func = list_entry(l, struct acpiphp_func, sibling); | ||
733 | |||
734 | if (func->pci_dev && (func->flags & FUNC_HAS_PS3)) { | ||
735 | status = acpi_evaluate_object(func->handle, "_PS3", NULL, NULL); | ||
736 | if (ACPI_FAILURE(status)) { | ||
737 | warn("%s: _PS3 failed\n", __FUNCTION__); | ||
738 | retval = -1; | ||
739 | goto err_exit; | ||
740 | } else | ||
741 | break; | ||
742 | } | ||
743 | } | ||
744 | |||
745 | list_for_each (l, &slot->funcs) { | ||
746 | func = list_entry(l, struct acpiphp_func, sibling); | ||
747 | |||
748 | /* We don't want to call _EJ0 on non-existing functions. */ | ||
749 | if (func->pci_dev && (func->flags & FUNC_HAS_EJ0)) { | ||
750 | /* _EJ0 method take one argument */ | ||
751 | arg_list.count = 1; | ||
752 | arg_list.pointer = &arg; | ||
753 | arg.type = ACPI_TYPE_INTEGER; | ||
754 | arg.integer.value = 1; | ||
755 | |||
756 | status = acpi_evaluate_object(func->handle, "_EJ0", &arg_list, NULL); | ||
757 | if (ACPI_FAILURE(status)) { | ||
758 | warn("%s: _EJ0 failed\n", __FUNCTION__); | ||
759 | retval = -1; | ||
760 | goto err_exit; | ||
761 | } else | ||
762 | break; | ||
763 | } | ||
764 | } | ||
765 | |||
766 | /* TBD: evaluate _STA to check if the slot is disabled */ | ||
767 | |||
768 | slot->flags &= (~SLOT_POWEREDON); | ||
769 | |||
770 | err_exit: | ||
771 | return retval; | ||
772 | } | ||
773 | |||
774 | |||
775 | /** | ||
776 | * enable_device - enable, configure a slot | ||
777 | * @slot: slot to be enabled | ||
778 | * | ||
779 | * This function should be called per *physical slot*, | ||
780 | * not per each slot object in ACPI namespace. | ||
781 | * | ||
782 | */ | ||
783 | static int enable_device(struct acpiphp_slot *slot) | ||
784 | { | ||
785 | u8 bus; | ||
786 | struct pci_dev *dev; | ||
787 | struct pci_bus *child; | ||
788 | struct list_head *l; | ||
789 | struct acpiphp_func *func; | ||
790 | int retval = 0; | ||
791 | int num; | ||
792 | |||
793 | if (slot->flags & SLOT_ENABLED) | ||
794 | goto err_exit; | ||
795 | |||
796 | /* sanity check: dev should be NULL when hot-plugged in */ | ||
797 | dev = pci_find_slot(slot->bridge->bus, PCI_DEVFN(slot->device, 0)); | ||
798 | if (dev) { | ||
799 | /* This case shouldn't happen */ | ||
800 | err("pci_dev structure already exists.\n"); | ||
801 | retval = -1; | ||
802 | goto err_exit; | ||
803 | } | ||
804 | |||
805 | /* allocate resources to device */ | ||
806 | retval = acpiphp_configure_slot(slot); | ||
807 | if (retval) | ||
808 | goto err_exit; | ||
809 | |||
810 | /* returned `dev' is the *first function* only! */ | ||
811 | num = pci_scan_slot(slot->bridge->pci_bus, PCI_DEVFN(slot->device, 0)); | ||
812 | if (num) | ||
813 | pci_bus_add_devices(slot->bridge->pci_bus); | ||
814 | dev = pci_find_slot(slot->bridge->bus, PCI_DEVFN(slot->device, 0)); | ||
815 | |||
816 | if (!dev) { | ||
817 | err("No new device found\n"); | ||
818 | retval = -1; | ||
819 | goto err_exit; | ||
820 | } | ||
821 | |||
822 | if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { | ||
823 | pci_read_config_byte(dev, PCI_SECONDARY_BUS, &bus); | ||
824 | child = (struct pci_bus*) pci_add_new_bus(dev->bus, dev, bus); | ||
825 | pci_do_scan_bus(child); | ||
826 | } | ||
827 | |||
828 | /* associate pci_dev to our representation */ | ||
829 | list_for_each (l, &slot->funcs) { | ||
830 | func = list_entry(l, struct acpiphp_func, sibling); | ||
831 | |||
832 | func->pci_dev = pci_find_slot(slot->bridge->bus, | ||
833 | PCI_DEVFN(slot->device, | ||
834 | func->function)); | ||
835 | if (!func->pci_dev) | ||
836 | continue; | ||
837 | |||
838 | /* configure device */ | ||
839 | retval = acpiphp_configure_function(func); | ||
840 | if (retval) | ||
841 | goto err_exit; | ||
842 | } | ||
843 | |||
844 | slot->flags |= SLOT_ENABLED; | ||
845 | |||
846 | dbg("Available resources:\n"); | ||
847 | acpiphp_dump_resource(slot->bridge); | ||
848 | |||
849 | err_exit: | ||
850 | return retval; | ||
851 | } | ||
852 | |||
853 | |||
854 | /** | ||
855 | * disable_device - disable a slot | ||
856 | */ | ||
857 | static int disable_device(struct acpiphp_slot *slot) | ||
858 | { | ||
859 | int retval = 0; | ||
860 | struct acpiphp_func *func; | ||
861 | struct list_head *l; | ||
862 | |||
863 | /* is this slot already disabled? */ | ||
864 | if (!(slot->flags & SLOT_ENABLED)) | ||
865 | goto err_exit; | ||
866 | |||
867 | list_for_each (l, &slot->funcs) { | ||
868 | func = list_entry(l, struct acpiphp_func, sibling); | ||
869 | |||
870 | if (func->pci_dev) | ||
871 | acpiphp_unconfigure_function(func); | ||
872 | } | ||
873 | |||
874 | slot->flags &= (~SLOT_ENABLED); | ||
875 | |||
876 | err_exit: | ||
877 | return retval; | ||
878 | } | ||
879 | |||
880 | |||
881 | /** | ||
882 | * get_slot_status - get ACPI slot status | ||
883 | * | ||
884 | * if a slot has _STA for each function and if any one of them | ||
885 | * returned non-zero status, return it | ||
886 | * | ||
887 | * if a slot doesn't have _STA and if any one of its functions' | ||
888 | * configuration space is configured, return 0x0f as a _STA | ||
889 | * | ||
890 | * otherwise return 0 | ||
891 | */ | ||
892 | static unsigned int get_slot_status(struct acpiphp_slot *slot) | ||
893 | { | ||
894 | acpi_status status; | ||
895 | unsigned long sta = 0; | ||
896 | u32 dvid; | ||
897 | struct list_head *l; | ||
898 | struct acpiphp_func *func; | ||
899 | |||
900 | list_for_each (l, &slot->funcs) { | ||
901 | func = list_entry(l, struct acpiphp_func, sibling); | ||
902 | |||
903 | if (func->flags & FUNC_HAS_STA) { | ||
904 | status = acpi_evaluate_integer(func->handle, "_STA", NULL, &sta); | ||
905 | if (ACPI_SUCCESS(status) && sta) | ||
906 | break; | ||
907 | } else { | ||
908 | pci_bus_read_config_dword(slot->bridge->pci_bus, | ||
909 | PCI_DEVFN(slot->device, | ||
910 | func->function), | ||
911 | PCI_VENDOR_ID, &dvid); | ||
912 | if (dvid != 0xffffffff) { | ||
913 | sta = ACPI_STA_ALL; | ||
914 | break; | ||
915 | } | ||
916 | } | ||
917 | } | ||
918 | |||
919 | return (unsigned int)sta; | ||
920 | } | ||
921 | |||
922 | /** | ||
923 | * acpiphp_check_bridge - re-enumerate devices | ||
924 | * | ||
925 | * Iterate over all slots under this bridge and make sure that if a | ||
926 | * card is present they are enabled, and if not they are disabled. | ||
927 | */ | ||
928 | static int acpiphp_check_bridge(struct acpiphp_bridge *bridge) | ||
929 | { | ||
930 | struct acpiphp_slot *slot; | ||
931 | int retval = 0; | ||
932 | int enabled, disabled; | ||
933 | |||
934 | enabled = disabled = 0; | ||
935 | |||
936 | for (slot = bridge->slots; slot; slot = slot->next) { | ||
937 | unsigned int status = get_slot_status(slot); | ||
938 | if (slot->flags & SLOT_ENABLED) { | ||
939 | if (status == ACPI_STA_ALL) | ||
940 | continue; | ||
941 | retval = acpiphp_disable_slot(slot); | ||
942 | if (retval) { | ||
943 | err("Error occurred in disabling\n"); | ||
944 | goto err_exit; | ||
945 | } | ||
946 | disabled++; | ||
947 | } else { | ||
948 | if (status != ACPI_STA_ALL) | ||
949 | continue; | ||
950 | retval = acpiphp_enable_slot(slot); | ||
951 | if (retval) { | ||
952 | err("Error occurred in enabling\n"); | ||
953 | goto err_exit; | ||
954 | } | ||
955 | enabled++; | ||
956 | } | ||
957 | } | ||
958 | |||
959 | dbg("%s: %d enabled, %d disabled\n", __FUNCTION__, enabled, disabled); | ||
960 | |||
961 | err_exit: | ||
962 | return retval; | ||
963 | } | ||
964 | |||
965 | /* | ||
966 | * ACPI event handlers | ||
967 | */ | ||
968 | |||
969 | /** | ||
970 | * handle_hotplug_event_bridge - handle ACPI event on bridges | ||
971 | * | ||
972 | * @handle: Notify()'ed acpi_handle | ||
973 | * @type: Notify code | ||
974 | * @context: pointer to acpiphp_bridge structure | ||
975 | * | ||
976 | * handles ACPI event notification on {host,p2p} bridges | ||
977 | * | ||
978 | */ | ||
979 | static void handle_hotplug_event_bridge(acpi_handle handle, u32 type, void *context) | ||
980 | { | ||
981 | struct acpiphp_bridge *bridge; | ||
982 | char objname[64]; | ||
983 | struct acpi_buffer buffer = { .length = sizeof(objname), | ||
984 | .pointer = objname }; | ||
985 | |||
986 | bridge = (struct acpiphp_bridge *)context; | ||
987 | |||
988 | acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); | ||
989 | |||
990 | switch (type) { | ||
991 | case ACPI_NOTIFY_BUS_CHECK: | ||
992 | /* bus re-enumerate */ | ||
993 | dbg("%s: Bus check notify on %s\n", __FUNCTION__, objname); | ||
994 | acpiphp_check_bridge(bridge); | ||
995 | break; | ||
996 | |||
997 | case ACPI_NOTIFY_DEVICE_CHECK: | ||
998 | /* device check */ | ||
999 | dbg("%s: Device check notify on %s\n", __FUNCTION__, objname); | ||
1000 | acpiphp_check_bridge(bridge); | ||
1001 | break; | ||
1002 | |||
1003 | case ACPI_NOTIFY_DEVICE_WAKE: | ||
1004 | /* wake event */ | ||
1005 | dbg("%s: Device wake notify on %s\n", __FUNCTION__, objname); | ||
1006 | break; | ||
1007 | |||
1008 | case ACPI_NOTIFY_EJECT_REQUEST: | ||
1009 | /* request device eject */ | ||
1010 | dbg("%s: Device eject notify on %s\n", __FUNCTION__, objname); | ||
1011 | break; | ||
1012 | |||
1013 | case ACPI_NOTIFY_FREQUENCY_MISMATCH: | ||
1014 | printk(KERN_ERR "Device %s cannot be configured due" | ||
1015 | " to a frequency mismatch\n", objname); | ||
1016 | break; | ||
1017 | |||
1018 | case ACPI_NOTIFY_BUS_MODE_MISMATCH: | ||
1019 | printk(KERN_ERR "Device %s cannot be configured due" | ||
1020 | " to a bus mode mismatch\n", objname); | ||
1021 | break; | ||
1022 | |||
1023 | case ACPI_NOTIFY_POWER_FAULT: | ||
1024 | printk(KERN_ERR "Device %s has suffered a power fault\n", | ||
1025 | objname); | ||
1026 | break; | ||
1027 | |||
1028 | default: | ||
1029 | warn("notify_handler: unknown event type 0x%x for %s\n", type, objname); | ||
1030 | break; | ||
1031 | } | ||
1032 | } | ||
1033 | |||
1034 | |||
1035 | /** | ||
1036 | * handle_hotplug_event_func - handle ACPI event on functions (i.e. slots) | ||
1037 | * | ||
1038 | * @handle: Notify()'ed acpi_handle | ||
1039 | * @type: Notify code | ||
1040 | * @context: pointer to acpiphp_func structure | ||
1041 | * | ||
1042 | * handles ACPI event notification on slots | ||
1043 | * | ||
1044 | */ | ||
1045 | static void handle_hotplug_event_func(acpi_handle handle, u32 type, void *context) | ||
1046 | { | ||
1047 | struct acpiphp_func *func; | ||
1048 | char objname[64]; | ||
1049 | struct acpi_buffer buffer = { .length = sizeof(objname), | ||
1050 | .pointer = objname }; | ||
1051 | |||
1052 | acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); | ||
1053 | |||
1054 | func = (struct acpiphp_func *)context; | ||
1055 | |||
1056 | switch (type) { | ||
1057 | case ACPI_NOTIFY_BUS_CHECK: | ||
1058 | /* bus re-enumerate */ | ||
1059 | dbg("%s: Bus check notify on %s\n", __FUNCTION__, objname); | ||
1060 | acpiphp_enable_slot(func->slot); | ||
1061 | break; | ||
1062 | |||
1063 | case ACPI_NOTIFY_DEVICE_CHECK: | ||
1064 | /* device check : re-enumerate from parent bus */ | ||
1065 | dbg("%s: Device check notify on %s\n", __FUNCTION__, objname); | ||
1066 | acpiphp_check_bridge(func->slot->bridge); | ||
1067 | break; | ||
1068 | |||
1069 | case ACPI_NOTIFY_DEVICE_WAKE: | ||
1070 | /* wake event */ | ||
1071 | dbg("%s: Device wake notify on %s\n", __FUNCTION__, objname); | ||
1072 | break; | ||
1073 | |||
1074 | case ACPI_NOTIFY_EJECT_REQUEST: | ||
1075 | /* request device eject */ | ||
1076 | dbg("%s: Device eject notify on %s\n", __FUNCTION__, objname); | ||
1077 | acpiphp_disable_slot(func->slot); | ||
1078 | break; | ||
1079 | |||
1080 | default: | ||
1081 | warn("notify_handler: unknown event type 0x%x for %s\n", type, objname); | ||
1082 | break; | ||
1083 | } | ||
1084 | } | ||
1085 | |||
1086 | |||
1087 | static struct acpi_pci_driver acpi_pci_hp_driver = { | ||
1088 | .add = add_bridge, | ||
1089 | .remove = remove_bridge, | ||
1090 | }; | ||
1091 | |||
1092 | /** | ||
1093 | * acpiphp_glue_init - initializes all PCI hotplug - ACPI glue data structures | ||
1094 | * | ||
1095 | */ | ||
1096 | int __init acpiphp_glue_init(void) | ||
1097 | { | ||
1098 | int num; | ||
1099 | |||
1100 | if (list_empty(&pci_root_buses)) | ||
1101 | return -1; | ||
1102 | |||
1103 | num = acpi_pci_register_driver(&acpi_pci_hp_driver); | ||
1104 | |||
1105 | if (num <= 0) | ||
1106 | return -1; | ||
1107 | |||
1108 | return 0; | ||
1109 | } | ||
1110 | |||
1111 | |||
1112 | /** | ||
1113 | * acpiphp_glue_exit - terminates all PCI hotplug - ACPI glue data structures | ||
1114 | * | ||
1115 | * This function frees all data allocated in acpiphp_glue_init() | ||
1116 | */ | ||
1117 | void __exit acpiphp_glue_exit(void) | ||
1118 | { | ||
1119 | struct list_head *l1, *l2, *n1, *n2; | ||
1120 | struct acpiphp_bridge *bridge; | ||
1121 | struct acpiphp_slot *slot, *next; | ||
1122 | struct acpiphp_func *func; | ||
1123 | acpi_status status; | ||
1124 | |||
1125 | list_for_each_safe (l1, n1, &bridge_list) { | ||
1126 | bridge = (struct acpiphp_bridge *)l1; | ||
1127 | slot = bridge->slots; | ||
1128 | while (slot) { | ||
1129 | next = slot->next; | ||
1130 | list_for_each_safe (l2, n2, &slot->funcs) { | ||
1131 | func = list_entry(l2, struct acpiphp_func, sibling); | ||
1132 | acpiphp_free_resource(&func->io_head); | ||
1133 | acpiphp_free_resource(&func->mem_head); | ||
1134 | acpiphp_free_resource(&func->p_mem_head); | ||
1135 | acpiphp_free_resource(&func->bus_head); | ||
1136 | status = acpi_remove_notify_handler(func->handle, | ||
1137 | ACPI_SYSTEM_NOTIFY, | ||
1138 | handle_hotplug_event_func); | ||
1139 | if (ACPI_FAILURE(status)) | ||
1140 | err("failed to remove notify handler\n"); | ||
1141 | kfree(func); | ||
1142 | } | ||
1143 | kfree(slot); | ||
1144 | slot = next; | ||
1145 | } | ||
1146 | status = acpi_remove_notify_handler(bridge->handle, ACPI_SYSTEM_NOTIFY, | ||
1147 | handle_hotplug_event_bridge); | ||
1148 | if (ACPI_FAILURE(status)) | ||
1149 | err("failed to remove notify handler\n"); | ||
1150 | |||
1151 | acpiphp_free_resource(&bridge->io_head); | ||
1152 | acpiphp_free_resource(&bridge->mem_head); | ||
1153 | acpiphp_free_resource(&bridge->p_mem_head); | ||
1154 | acpiphp_free_resource(&bridge->bus_head); | ||
1155 | |||
1156 | kfree(bridge); | ||
1157 | } | ||
1158 | |||
1159 | acpi_pci_unregister_driver(&acpi_pci_hp_driver); | ||
1160 | } | ||
1161 | |||
1162 | |||
1163 | /** | ||
1164 | * acpiphp_get_num_slots - count number of slots in a system | ||
1165 | */ | ||
1166 | int __init acpiphp_get_num_slots(void) | ||
1167 | { | ||
1168 | struct list_head *node; | ||
1169 | struct acpiphp_bridge *bridge; | ||
1170 | int num_slots; | ||
1171 | |||
1172 | num_slots = 0; | ||
1173 | |||
1174 | list_for_each (node, &bridge_list) { | ||
1175 | bridge = (struct acpiphp_bridge *)node; | ||
1176 | dbg("Bus%d %dslot(s)\n", bridge->bus, bridge->nr_slots); | ||
1177 | num_slots += bridge->nr_slots; | ||
1178 | } | ||
1179 | |||
1180 | dbg("Total %dslots\n", num_slots); | ||
1181 | return num_slots; | ||
1182 | } | ||
1183 | |||
1184 | |||
1185 | #if 0 | ||
1186 | /** | ||
1187 | * acpiphp_for_each_slot - call function for each slot | ||
1188 | * @fn: callback function | ||
1189 | * @data: context to be passed to callback function | ||
1190 | * | ||
1191 | */ | ||
1192 | static int acpiphp_for_each_slot(acpiphp_callback fn, void *data) | ||
1193 | { | ||
1194 | struct list_head *node; | ||
1195 | struct acpiphp_bridge *bridge; | ||
1196 | struct acpiphp_slot *slot; | ||
1197 | int retval = 0; | ||
1198 | |||
1199 | list_for_each (node, &bridge_list) { | ||
1200 | bridge = (struct acpiphp_bridge *)node; | ||
1201 | for (slot = bridge->slots; slot; slot = slot->next) { | ||
1202 | retval = fn(slot, data); | ||
1203 | if (!retval) | ||
1204 | goto err_exit; | ||
1205 | } | ||
1206 | } | ||
1207 | |||
1208 | err_exit: | ||
1209 | return retval; | ||
1210 | } | ||
1211 | #endif | ||
1212 | |||
1213 | /* search matching slot from id */ | ||
1214 | struct acpiphp_slot *get_slot_from_id(int id) | ||
1215 | { | ||
1216 | struct list_head *node; | ||
1217 | struct acpiphp_bridge *bridge; | ||
1218 | struct acpiphp_slot *slot; | ||
1219 | |||
1220 | list_for_each (node, &bridge_list) { | ||
1221 | bridge = (struct acpiphp_bridge *)node; | ||
1222 | for (slot = bridge->slots; slot; slot = slot->next) | ||
1223 | if (slot->id == id) | ||
1224 | return slot; | ||
1225 | } | ||
1226 | |||
1227 | /* should never happen! */ | ||
1228 | err("%s: no object for id %d\n", __FUNCTION__, id); | ||
1229 | WARN_ON(1); | ||
1230 | return NULL; | ||
1231 | } | ||
1232 | |||
1233 | |||
1234 | /** | ||
1235 | * acpiphp_enable_slot - power on slot | ||
1236 | */ | ||
1237 | int acpiphp_enable_slot(struct acpiphp_slot *slot) | ||
1238 | { | ||
1239 | int retval; | ||
1240 | |||
1241 | down(&slot->crit_sect); | ||
1242 | |||
1243 | /* wake up all functions */ | ||
1244 | retval = power_on_slot(slot); | ||
1245 | if (retval) | ||
1246 | goto err_exit; | ||
1247 | |||
1248 | if (get_slot_status(slot) == ACPI_STA_ALL) | ||
1249 | /* configure all functions */ | ||
1250 | retval = enable_device(slot); | ||
1251 | |||
1252 | err_exit: | ||
1253 | up(&slot->crit_sect); | ||
1254 | return retval; | ||
1255 | } | ||
1256 | |||
1257 | |||
1258 | /** | ||
1259 | * acpiphp_disable_slot - power off slot | ||
1260 | */ | ||
1261 | int acpiphp_disable_slot(struct acpiphp_slot *slot) | ||
1262 | { | ||
1263 | int retval = 0; | ||
1264 | |||
1265 | down(&slot->crit_sect); | ||
1266 | |||
1267 | /* unconfigure all functions */ | ||
1268 | retval = disable_device(slot); | ||
1269 | if (retval) | ||
1270 | goto err_exit; | ||
1271 | |||
1272 | /* power off all functions */ | ||
1273 | retval = power_off_slot(slot); | ||
1274 | if (retval) | ||
1275 | goto err_exit; | ||
1276 | |||
1277 | acpiphp_resource_sort_and_combine(&slot->bridge->io_head); | ||
1278 | acpiphp_resource_sort_and_combine(&slot->bridge->mem_head); | ||
1279 | acpiphp_resource_sort_and_combine(&slot->bridge->p_mem_head); | ||
1280 | acpiphp_resource_sort_and_combine(&slot->bridge->bus_head); | ||
1281 | dbg("Available resources:\n"); | ||
1282 | acpiphp_dump_resource(slot->bridge); | ||
1283 | |||
1284 | err_exit: | ||
1285 | up(&slot->crit_sect); | ||
1286 | return retval; | ||
1287 | } | ||
1288 | |||
1289 | |||
1290 | /* | ||
1291 | * slot enabled: 1 | ||
1292 | * slot disabled: 0 | ||
1293 | */ | ||
1294 | u8 acpiphp_get_power_status(struct acpiphp_slot *slot) | ||
1295 | { | ||
1296 | unsigned int sta; | ||
1297 | |||
1298 | sta = get_slot_status(slot); | ||
1299 | |||
1300 | return (sta & ACPI_STA_ENABLED) ? 1 : 0; | ||
1301 | } | ||
1302 | |||
1303 | |||
1304 | /* | ||
1305 | * latch closed: 1 | ||
1306 | * latch open: 0 | ||
1307 | */ | ||
1308 | u8 acpiphp_get_latch_status(struct acpiphp_slot *slot) | ||
1309 | { | ||
1310 | unsigned int sta; | ||
1311 | |||
1312 | sta = get_slot_status(slot); | ||
1313 | |||
1314 | return (sta & ACPI_STA_SHOW_IN_UI) ? 1 : 0; | ||
1315 | } | ||
1316 | |||
1317 | |||
1318 | /* | ||
1319 | * adapter presence : 1 | ||
1320 | * absence : 0 | ||
1321 | */ | ||
1322 | u8 acpiphp_get_adapter_status(struct acpiphp_slot *slot) | ||
1323 | { | ||
1324 | unsigned int sta; | ||
1325 | |||
1326 | sta = get_slot_status(slot); | ||
1327 | |||
1328 | return (sta == 0) ? 0 : 1; | ||
1329 | } | ||
1330 | |||
1331 | |||
1332 | /* | ||
1333 | * pci address (seg/bus/dev) | ||
1334 | */ | ||
1335 | u32 acpiphp_get_address(struct acpiphp_slot *slot) | ||
1336 | { | ||
1337 | u32 address; | ||
1338 | |||
1339 | address = ((slot->bridge->seg) << 16) | | ||
1340 | ((slot->bridge->bus) << 8) | | ||
1341 | slot->device; | ||
1342 | |||
1343 | return address; | ||
1344 | } | ||
diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c new file mode 100644 index 000000000000..7e7f913ba7b9 --- /dev/null +++ b/drivers/pci/hotplug/acpiphp_ibm.c | |||
@@ -0,0 +1,499 @@ | |||
1 | /* | ||
2 | * ACPI PCI Hot Plug IBM Extension | ||
3 | * | ||
4 | * Copyright (C) 2004 Vernon Mauery <vernux@us.ibm.com> | ||
5 | * Copyright (C) 2004 IBM Corp. | ||
6 | * | ||
7 | * All rights reserved. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or (at | ||
12 | * your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, but | ||
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
17 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
18 | * details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
23 | * | ||
24 | * Send feedback to <vernux@us.ibm.com> | ||
25 | * | ||
26 | */ | ||
27 | |||
28 | #include <linux/init.h> | ||
29 | #include <linux/module.h> | ||
30 | #include <linux/kernel.h> | ||
31 | #include <acpi/acpi_bus.h> | ||
32 | #include <linux/sysfs.h> | ||
33 | #include <linux/kobject.h> | ||
34 | #include <asm/uaccess.h> | ||
35 | #include <linux/moduleparam.h> | ||
36 | |||
37 | #include "acpiphp.h" | ||
38 | #include "pci_hotplug.h" | ||
39 | |||
40 | #define DRIVER_VERSION "1.0.1" | ||
41 | #define DRIVER_AUTHOR "Irene Zubarev <zubarev@us.ibm.com>, Vernon Mauery <vernux@us.ibm.com>" | ||
42 | #define DRIVER_DESC "ACPI Hot Plug PCI Controller Driver IBM extension" | ||
43 | |||
44 | static int debug; | ||
45 | |||
46 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
47 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
48 | MODULE_LICENSE("GPL"); | ||
49 | MODULE_VERSION(DRIVER_VERSION); | ||
50 | module_param(debug, bool, 0644); | ||
51 | MODULE_PARM_DESC(debug, " Debugging mode enabled or not"); | ||
52 | #define MY_NAME "acpiphp_ibm" | ||
53 | |||
54 | #undef dbg | ||
55 | #define dbg(format, arg...) \ | ||
56 | do { \ | ||
57 | if (debug) \ | ||
58 | printk(KERN_DEBUG "%s: " format, \ | ||
59 | MY_NAME , ## arg); \ | ||
60 | } while (0) | ||
61 | |||
62 | #define FOUND_APCI 0x61504349 | ||
63 | /* these are the names for the IBM ACPI pseudo-device */ | ||
64 | #define IBM_HARDWARE_ID1 "IBM37D0" | ||
65 | #define IBM_HARDWARE_ID2 "IBM37D4" | ||
66 | |||
67 | #define hpslot_to_sun(A) (((struct slot *)((A)->private))->acpi_slot->sun) | ||
68 | |||
69 | /* union apci_descriptor - allows access to the | ||
70 | * various device descriptors that are embedded in the | ||
71 | * aPCI table | ||
72 | */ | ||
73 | union apci_descriptor { | ||
74 | struct { | ||
75 | char sig[4]; | ||
76 | u8 len; | ||
77 | } header; | ||
78 | struct { | ||
79 | u8 type; | ||
80 | u8 len; | ||
81 | u16 slot_id; | ||
82 | u8 bus_id; | ||
83 | u8 dev_num; | ||
84 | u8 slot_num; | ||
85 | u8 slot_attr[2]; | ||
86 | u8 attn; | ||
87 | u8 status[2]; | ||
88 | u8 sun; | ||
89 | u8 res[3]; | ||
90 | } slot; | ||
91 | struct { | ||
92 | u8 type; | ||
93 | u8 len; | ||
94 | } generic; | ||
95 | }; | ||
96 | |||
97 | /* struct notification - keeps info about the device | ||
98 | * that cause the ACPI notification event | ||
99 | */ | ||
100 | struct notification { | ||
101 | struct acpi_device *device; | ||
102 | u8 event; | ||
103 | }; | ||
104 | |||
105 | static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status); | ||
106 | static int ibm_get_attention_status(struct hotplug_slot *slot, u8 *status); | ||
107 | static void ibm_handle_events(acpi_handle handle, u32 event, void *context); | ||
108 | static int ibm_get_table_from_acpi(char **bufp); | ||
109 | static ssize_t ibm_read_apci_table(struct kobject *kobj, | ||
110 | char *buffer, loff_t pos, size_t size); | ||
111 | static acpi_status __init ibm_find_acpi_device(acpi_handle handle, | ||
112 | u32 lvl, void *context, void **rv); | ||
113 | static int __init ibm_acpiphp_init(void); | ||
114 | static void __exit ibm_acpiphp_exit(void); | ||
115 | |||
116 | static acpi_handle ibm_acpi_handle; | ||
117 | static struct notification ibm_note; | ||
118 | static struct bin_attribute ibm_apci_table_attr = { | ||
119 | .attr = { | ||
120 | .name = "apci_table", | ||
121 | .owner = THIS_MODULE, | ||
122 | .mode = S_IRUGO, | ||
123 | }, | ||
124 | .read = ibm_read_apci_table, | ||
125 | .write = NULL, | ||
126 | }; | ||
127 | static struct acpiphp_attention_info ibm_attention_info = | ||
128 | { | ||
129 | .set_attn = ibm_set_attention_status, | ||
130 | .get_attn = ibm_get_attention_status, | ||
131 | .owner = THIS_MODULE, | ||
132 | }; | ||
133 | |||
134 | /** | ||
135 | * ibm_slot_from_id - workaround for bad ibm hardware | ||
136 | * @id: the slot number that linux refers to the slot by | ||
137 | * | ||
138 | * Description: this method returns the aCPI slot descriptor | ||
139 | * corresponding to the Linux slot number. This descriptor | ||
140 | * has info about the aPCI slot id and attention status. | ||
141 | * This descriptor must be freed using kfree when done. | ||
142 | **/ | ||
143 | static union apci_descriptor *ibm_slot_from_id(int id) | ||
144 | { | ||
145 | int ind = 0, size; | ||
146 | union apci_descriptor *ret = NULL, *des; | ||
147 | char *table; | ||
148 | |||
149 | size = ibm_get_table_from_acpi(&table); | ||
150 | des = (union apci_descriptor *)table; | ||
151 | if (memcmp(des->header.sig, "aPCI", 4) != 0) | ||
152 | goto ibm_slot_done; | ||
153 | |||
154 | des = (union apci_descriptor *)&table[ind += des->header.len]; | ||
155 | while (ind < size && (des->generic.type != 0x82 || | ||
156 | des->slot.slot_num != id)) { | ||
157 | des = (union apci_descriptor *)&table[ind += des->generic.len]; | ||
158 | } | ||
159 | |||
160 | if (ind < size && des->slot.slot_num == id) | ||
161 | ret = des; | ||
162 | |||
163 | ibm_slot_done: | ||
164 | if (ret) { | ||
165 | ret = kmalloc(sizeof(union apci_descriptor), GFP_KERNEL); | ||
166 | memcpy(ret, des, sizeof(union apci_descriptor)); | ||
167 | } | ||
168 | kfree(table); | ||
169 | return ret; | ||
170 | } | ||
171 | |||
172 | /** | ||
173 | * ibm_set_attention_status - callback method to set the attention LED | ||
174 | * @slot: the hotplug_slot to work with | ||
175 | * @status: what to set the LED to (0 or 1) | ||
176 | * | ||
177 | * Description: this method is registered with the acpiphp module as a | ||
178 | * callback to do the device specific task of setting the LED status | ||
179 | **/ | ||
180 | static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status) | ||
181 | { | ||
182 | union acpi_object args[2]; | ||
183 | struct acpi_object_list params = { .pointer = args, .count = 2 }; | ||
184 | acpi_status stat; | ||
185 | unsigned long rc; | ||
186 | union apci_descriptor *ibm_slot; | ||
187 | |||
188 | ibm_slot = ibm_slot_from_id(hpslot_to_sun(slot)); | ||
189 | |||
190 | dbg("%s: set slot %d (%d) attention status to %d\n", __FUNCTION__, | ||
191 | ibm_slot->slot.slot_num, ibm_slot->slot.slot_id, | ||
192 | (status ? 1 : 0)); | ||
193 | |||
194 | args[0].type = ACPI_TYPE_INTEGER; | ||
195 | args[0].integer.value = ibm_slot->slot.slot_id; | ||
196 | args[1].type = ACPI_TYPE_INTEGER; | ||
197 | args[1].integer.value = (status) ? 1 : 0; | ||
198 | |||
199 | kfree(ibm_slot); | ||
200 | |||
201 | stat = acpi_evaluate_integer(ibm_acpi_handle, "APLS", ¶ms, &rc); | ||
202 | if (ACPI_FAILURE(stat)) { | ||
203 | err("APLS evaluation failed: 0x%08x\n", stat); | ||
204 | return -ENODEV; | ||
205 | } else if (!rc) { | ||
206 | err("APLS method failed: 0x%08lx\n", rc); | ||
207 | return -ERANGE; | ||
208 | } | ||
209 | return 0; | ||
210 | } | ||
211 | |||
212 | /** | ||
213 | * ibm_get_attention_status - callback method to get attention LED status | ||
214 | * @slot: the hotplug_slot to work with | ||
215 | * @status: returns what the LED is set to (0 or 1) | ||
216 | * | ||
217 | * Description: this method is registered with the acpiphp module as a | ||
218 | * callback to do the device specific task of getting the LED status | ||
219 | * | ||
220 | * Because there is no direct method of getting the LED status directly | ||
221 | * from an ACPI call, we read the aPCI table and parse out our | ||
222 | * slot descriptor to read the status from that. | ||
223 | **/ | ||
224 | static int ibm_get_attention_status(struct hotplug_slot *slot, u8 *status) | ||
225 | { | ||
226 | union apci_descriptor *ibm_slot; | ||
227 | |||
228 | ibm_slot = ibm_slot_from_id(hpslot_to_sun(slot)); | ||
229 | |||
230 | if (ibm_slot->slot.attn & 0xa0 || ibm_slot->slot.status[1] & 0x08) | ||
231 | *status = 1; | ||
232 | else | ||
233 | *status = 0; | ||
234 | |||
235 | dbg("%s: get slot %d (%d) attention status is %d\n", __FUNCTION__, | ||
236 | ibm_slot->slot.slot_num, ibm_slot->slot.slot_id, | ||
237 | *status); | ||
238 | |||
239 | kfree(ibm_slot); | ||
240 | return 0; | ||
241 | } | ||
242 | |||
243 | /** | ||
244 | * ibm_handle_events - listens for ACPI events for the IBM37D0 device | ||
245 | * @handle: an ACPI handle to the device that caused the event | ||
246 | * @event: the event info (device specific) | ||
247 | * @context: passed context (our notification struct) | ||
248 | * | ||
249 | * Description: this method is registered as a callback with the ACPI | ||
250 | * subsystem it is called when this device has an event to notify the OS of | ||
251 | * | ||
252 | * The events actually come from the device as two events that get | ||
253 | * synthesized into one event with data by this function. The event | ||
254 | * ID comes first and then the slot number that caused it. We report | ||
255 | * this as one event to the OS. | ||
256 | * | ||
257 | * From section 5.6.2.2 of the ACPI 2.0 spec, I understand that the OSPM will | ||
258 | * only re-enable the interrupt that causes this event AFTER this method | ||
259 | * has returned, thereby enforcing serial access for the notification struct. | ||
260 | **/ | ||
261 | static void ibm_handle_events(acpi_handle handle, u32 event, void *context) | ||
262 | { | ||
263 | u8 detail = event & 0x0f; | ||
264 | u8 subevent = event & 0xf0; | ||
265 | struct notification *note = context; | ||
266 | |||
267 | dbg("%s: Received notification %02x\n", __FUNCTION__, event); | ||
268 | |||
269 | if (subevent == 0x80) { | ||
270 | dbg("%s: generationg bus event\n", __FUNCTION__); | ||
271 | acpi_bus_generate_event(note->device, note->event, detail); | ||
272 | } else | ||
273 | note->event = event; | ||
274 | } | ||
275 | |||
276 | /** | ||
277 | * ibm_get_table_from_acpi - reads the APLS buffer from ACPI | ||
278 | * @bufp: address to pointer to allocate for the table | ||
279 | * | ||
280 | * Description: this method reads the APLS buffer in from ACPI and | ||
281 | * stores the "stripped" table into a single buffer | ||
282 | * it allocates and passes the address back in bufp | ||
283 | * | ||
284 | * If NULL is passed in as buffer, this method only calculates | ||
285 | * the size of the table and returns that without filling | ||
286 | * in the buffer | ||
287 | * | ||
288 | * returns < 0 on error or the size of the table on success | ||
289 | **/ | ||
290 | static int ibm_get_table_from_acpi(char **bufp) | ||
291 | { | ||
292 | union acpi_object *package; | ||
293 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
294 | acpi_status status; | ||
295 | char *lbuf = NULL; | ||
296 | int i, size = -EIO; | ||
297 | |||
298 | status = acpi_evaluate_object(ibm_acpi_handle, "APCI", NULL, &buffer); | ||
299 | if (ACPI_FAILURE(status)) { | ||
300 | err("%s: APCI evaluation failed\n", __FUNCTION__); | ||
301 | return -ENODEV; | ||
302 | } | ||
303 | |||
304 | package = (union acpi_object *) buffer.pointer; | ||
305 | if(!(package) || | ||
306 | (package->type != ACPI_TYPE_PACKAGE) || | ||
307 | !(package->package.elements)) { | ||
308 | err("%s: Invalid APCI object\n", __FUNCTION__); | ||
309 | goto read_table_done; | ||
310 | } | ||
311 | |||
312 | for(size = 0, i = 0; i < package->package.count; i++) { | ||
313 | if (package->package.elements[i].type != ACPI_TYPE_BUFFER) { | ||
314 | err("%s: Invalid APCI element %d\n", __FUNCTION__, i); | ||
315 | goto read_table_done; | ||
316 | } | ||
317 | size += package->package.elements[i].buffer.length; | ||
318 | } | ||
319 | |||
320 | if (bufp == NULL) | ||
321 | goto read_table_done; | ||
322 | |||
323 | lbuf = kmalloc(size, GFP_KERNEL); | ||
324 | dbg("%s: element count: %i, ASL table size: %i, &table = 0x%p\n", | ||
325 | __FUNCTION__, package->package.count, size, lbuf); | ||
326 | |||
327 | if (lbuf) { | ||
328 | *bufp = lbuf; | ||
329 | memset(lbuf, 0, size); | ||
330 | } else { | ||
331 | size = -ENOMEM; | ||
332 | goto read_table_done; | ||
333 | } | ||
334 | |||
335 | size = 0; | ||
336 | for (i=0; i<package->package.count; i++) { | ||
337 | memcpy(&lbuf[size], | ||
338 | package->package.elements[i].buffer.pointer, | ||
339 | package->package.elements[i].buffer.length); | ||
340 | size += package->package.elements[i].buffer.length; | ||
341 | } | ||
342 | |||
343 | read_table_done: | ||
344 | kfree(buffer.pointer); | ||
345 | return size; | ||
346 | } | ||
347 | |||
348 | /** | ||
349 | * ibm_read_apci_table - callback for the sysfs apci_table file | ||
350 | * @kobj: the kobject this binary attribute is a part of | ||
351 | * @buffer: the kernel space buffer to fill | ||
352 | * @pos: the offset into the file | ||
353 | * @size: the number of bytes requested | ||
354 | * | ||
355 | * Description: gets registered with sysfs as the reader callback | ||
356 | * to be executed when /sys/bus/pci/slots/apci_table gets read | ||
357 | * | ||
358 | * Since we don't get notified on open and close for this file, | ||
359 | * things get really tricky here... | ||
360 | * our solution is to only allow reading the table in all at once | ||
361 | **/ | ||
362 | static ssize_t ibm_read_apci_table(struct kobject *kobj, | ||
363 | char *buffer, loff_t pos, size_t size) | ||
364 | { | ||
365 | int bytes_read = -EINVAL; | ||
366 | char *table = NULL; | ||
367 | |||
368 | dbg("%s: pos = %d, size = %zd\n", __FUNCTION__, (int)pos, size); | ||
369 | |||
370 | if (pos == 0) { | ||
371 | bytes_read = ibm_get_table_from_acpi(&table); | ||
372 | if (bytes_read > 0 && bytes_read <= size) | ||
373 | memcpy(buffer, table, bytes_read); | ||
374 | kfree(table); | ||
375 | } | ||
376 | return bytes_read; | ||
377 | } | ||
378 | |||
379 | /** | ||
380 | * ibm_find_acpi_device - callback to find our ACPI device | ||
381 | * @handle: the ACPI handle of the device we are inspecting | ||
382 | * @lvl: depth into the namespace tree | ||
383 | * @context: a pointer to our handle to fill when we find the device | ||
384 | * @rv: a return value to fill if desired | ||
385 | * | ||
386 | * Description: used as a callback when calling acpi_walk_namespace | ||
387 | * to find our device. When this method returns non-zero | ||
388 | * acpi_walk_namespace quits its search and returns our value | ||
389 | **/ | ||
390 | static acpi_status __init ibm_find_acpi_device(acpi_handle handle, | ||
391 | u32 lvl, void *context, void **rv) | ||
392 | { | ||
393 | acpi_handle *phandle = (acpi_handle *)context; | ||
394 | acpi_status status; | ||
395 | struct acpi_device_info info; | ||
396 | struct acpi_buffer info_buffer = { | ||
397 | .length = sizeof(struct acpi_device_info), | ||
398 | .pointer = &info, | ||
399 | }; | ||
400 | |||
401 | status = acpi_get_object_info(handle, &info_buffer); | ||
402 | if (ACPI_FAILURE(status)) { | ||
403 | err("%s: Failed to get device information", __FUNCTION__); | ||
404 | return 0; | ||
405 | } | ||
406 | info.hardware_id.value[sizeof(info.hardware_id.value) - 1] = '\0'; | ||
407 | |||
408 | if(info.current_status && (info.valid & ACPI_VALID_HID) && | ||
409 | (!strcmp(info.hardware_id.value, IBM_HARDWARE_ID1) || | ||
410 | !strcmp(info.hardware_id.value, IBM_HARDWARE_ID2))) { | ||
411 | dbg("found hardware: %s, handle: %p\n", info.hardware_id.value, | ||
412 | handle); | ||
413 | *phandle = handle; | ||
414 | /* returning non-zero causes the search to stop | ||
415 | * and returns this value to the caller of | ||
416 | * acpi_walk_namespace, but it also causes some warnings | ||
417 | * in the acpi debug code to print... | ||
418 | */ | ||
419 | return FOUND_APCI; | ||
420 | } | ||
421 | return 0; | ||
422 | } | ||
423 | |||
424 | static int __init ibm_acpiphp_init(void) | ||
425 | { | ||
426 | int retval = 0; | ||
427 | acpi_status status; | ||
428 | struct acpi_device *device; | ||
429 | struct kobject *sysdir = &pci_hotplug_slots_subsys.kset.kobj; | ||
430 | |||
431 | dbg("%s\n", __FUNCTION__); | ||
432 | |||
433 | if (acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, | ||
434 | ACPI_UINT32_MAX, ibm_find_acpi_device, | ||
435 | &ibm_acpi_handle, NULL) != FOUND_APCI) { | ||
436 | err("%s: acpi_walk_namespace failed\n", __FUNCTION__); | ||
437 | retval = -ENODEV; | ||
438 | goto init_return; | ||
439 | } | ||
440 | dbg("%s: found IBM aPCI device\n", __FUNCTION__); | ||
441 | if (acpi_bus_get_device(ibm_acpi_handle, &device)) { | ||
442 | err("%s: acpi_bus_get_device failed\n", __FUNCTION__); | ||
443 | retval = -ENODEV; | ||
444 | goto init_return; | ||
445 | } | ||
446 | if (acpiphp_register_attention(&ibm_attention_info)) { | ||
447 | retval = -ENODEV; | ||
448 | goto init_return; | ||
449 | } | ||
450 | |||
451 | ibm_note.device = device; | ||
452 | status = acpi_install_notify_handler( | ||
453 | ibm_acpi_handle, | ||
454 | ACPI_DEVICE_NOTIFY, | ||
455 | ibm_handle_events, | ||
456 | &ibm_note); | ||
457 | if (ACPI_FAILURE(status)) { | ||
458 | err("%s: Failed to register notification handler\n", | ||
459 | __FUNCTION__); | ||
460 | retval = -EBUSY; | ||
461 | goto init_cleanup; | ||
462 | } | ||
463 | |||
464 | ibm_apci_table_attr.size = ibm_get_table_from_acpi(NULL); | ||
465 | retval = sysfs_create_bin_file(sysdir, &ibm_apci_table_attr); | ||
466 | |||
467 | return retval; | ||
468 | |||
469 | init_cleanup: | ||
470 | acpiphp_unregister_attention(&ibm_attention_info); | ||
471 | init_return: | ||
472 | return retval; | ||
473 | } | ||
474 | |||
475 | static void __exit ibm_acpiphp_exit(void) | ||
476 | { | ||
477 | acpi_status status; | ||
478 | struct kobject *sysdir = &pci_hotplug_slots_subsys.kset.kobj; | ||
479 | |||
480 | dbg("%s\n", __FUNCTION__); | ||
481 | |||
482 | if (acpiphp_unregister_attention(&ibm_attention_info)) | ||
483 | err("%s: attention info deregistration failed", __FUNCTION__); | ||
484 | |||
485 | status = acpi_remove_notify_handler( | ||
486 | ibm_acpi_handle, | ||
487 | ACPI_DEVICE_NOTIFY, | ||
488 | ibm_handle_events); | ||
489 | if (ACPI_FAILURE(status)) | ||
490 | err("%s: Notification handler removal failed\n", | ||
491 | __FUNCTION__); | ||
492 | // remove the /sys entries | ||
493 | if (sysfs_remove_bin_file(sysdir, &ibm_apci_table_attr)) | ||
494 | err("%s: removal of sysfs file apci_table failed\n", | ||
495 | __FUNCTION__); | ||
496 | } | ||
497 | |||
498 | module_init(ibm_acpiphp_init); | ||
499 | module_exit(ibm_acpiphp_exit); | ||
diff --git a/drivers/pci/hotplug/acpiphp_pci.c b/drivers/pci/hotplug/acpiphp_pci.c new file mode 100644 index 000000000000..54d97c9d1dff --- /dev/null +++ b/drivers/pci/hotplug/acpiphp_pci.c | |||
@@ -0,0 +1,449 @@ | |||
1 | /* | ||
2 | * ACPI PCI HotPlug PCI configuration space management | ||
3 | * | ||
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | ||
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * Copyright (C) 2001,2002 IBM Corp. | ||
7 | * Copyright (C) 2002 Takayoshi Kochi (t-kochi@bq.jp.nec.com) | ||
8 | * Copyright (C) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com) | ||
9 | * Copyright (C) 2002 NEC Corporation | ||
10 | * | ||
11 | * All rights reserved. | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify | ||
14 | * it under the terms of the GNU General Public License as published by | ||
15 | * the Free Software Foundation; either version 2 of the License, or (at | ||
16 | * your option) any later version. | ||
17 | * | ||
18 | * This program is distributed in the hope that it will be useful, but | ||
19 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
20 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
21 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
22 | * details. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License | ||
25 | * along with this program; if not, write to the Free Software | ||
26 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
27 | * | ||
28 | * Send feedback to <t-kochi@bq.jp.nec.com> | ||
29 | * | ||
30 | */ | ||
31 | |||
32 | #include <linux/init.h> | ||
33 | #include <linux/module.h> | ||
34 | |||
35 | #include <linux/kernel.h> | ||
36 | #include <linux/pci.h> | ||
37 | #include <linux/acpi.h> | ||
38 | #include "../pci.h" | ||
39 | #include "pci_hotplug.h" | ||
40 | #include "acpiphp.h" | ||
41 | |||
42 | #define MY_NAME "acpiphp_pci" | ||
43 | |||
44 | |||
45 | /* allocate mem/pmem/io resource to a new function */ | ||
46 | static int init_config_space (struct acpiphp_func *func) | ||
47 | { | ||
48 | u32 bar, len; | ||
49 | u32 address[] = { | ||
50 | PCI_BASE_ADDRESS_0, | ||
51 | PCI_BASE_ADDRESS_1, | ||
52 | PCI_BASE_ADDRESS_2, | ||
53 | PCI_BASE_ADDRESS_3, | ||
54 | PCI_BASE_ADDRESS_4, | ||
55 | PCI_BASE_ADDRESS_5, | ||
56 | 0 | ||
57 | }; | ||
58 | int count; | ||
59 | struct acpiphp_bridge *bridge; | ||
60 | struct pci_resource *res; | ||
61 | struct pci_bus *pbus; | ||
62 | int bus, device, function; | ||
63 | unsigned int devfn; | ||
64 | u16 tmp; | ||
65 | |||
66 | bridge = func->slot->bridge; | ||
67 | pbus = bridge->pci_bus; | ||
68 | bus = bridge->bus; | ||
69 | device = func->slot->device; | ||
70 | function = func->function; | ||
71 | devfn = PCI_DEVFN(device, function); | ||
72 | |||
73 | for (count = 0; address[count]; count++) { /* for 6 BARs */ | ||
74 | pci_bus_write_config_dword(pbus, devfn, | ||
75 | address[count], 0xFFFFFFFF); | ||
76 | pci_bus_read_config_dword(pbus, devfn, address[count], &bar); | ||
77 | |||
78 | if (!bar) /* This BAR is not implemented */ | ||
79 | continue; | ||
80 | |||
81 | dbg("Device %02x.%02x BAR %d wants %x\n", device, function, count, bar); | ||
82 | |||
83 | if (bar & PCI_BASE_ADDRESS_SPACE_IO) { | ||
84 | /* This is IO */ | ||
85 | |||
86 | len = bar & (PCI_BASE_ADDRESS_IO_MASK & 0xFFFF); | ||
87 | len = len & ~(len - 1); | ||
88 | |||
89 | dbg("len in IO %x, BAR %d\n", len, count); | ||
90 | |||
91 | spin_lock(&bridge->res_lock); | ||
92 | res = acpiphp_get_io_resource(&bridge->io_head, len); | ||
93 | spin_unlock(&bridge->res_lock); | ||
94 | |||
95 | if (!res) { | ||
96 | err("cannot allocate requested io for %02x:%02x.%d len %x\n", | ||
97 | bus, device, function, len); | ||
98 | return -1; | ||
99 | } | ||
100 | pci_bus_write_config_dword(pbus, devfn, | ||
101 | address[count], | ||
102 | (u32)res->base); | ||
103 | res->next = func->io_head; | ||
104 | func->io_head = res; | ||
105 | |||
106 | } else { | ||
107 | /* This is Memory */ | ||
108 | if (bar & PCI_BASE_ADDRESS_MEM_PREFETCH) { | ||
109 | /* pfmem */ | ||
110 | |||
111 | len = bar & 0xFFFFFFF0; | ||
112 | len = ~len + 1; | ||
113 | |||
114 | dbg("len in PFMEM %x, BAR %d\n", len, count); | ||
115 | |||
116 | spin_lock(&bridge->res_lock); | ||
117 | res = acpiphp_get_resource(&bridge->p_mem_head, len); | ||
118 | spin_unlock(&bridge->res_lock); | ||
119 | |||
120 | if (!res) { | ||
121 | err("cannot allocate requested pfmem for %02x:%02x.%d len %x\n", | ||
122 | bus, device, function, len); | ||
123 | return -1; | ||
124 | } | ||
125 | |||
126 | pci_bus_write_config_dword(pbus, devfn, | ||
127 | address[count], | ||
128 | (u32)res->base); | ||
129 | |||
130 | if (bar & PCI_BASE_ADDRESS_MEM_TYPE_64) { /* takes up another dword */ | ||
131 | dbg("inside the pfmem 64 case, count %d\n", count); | ||
132 | count += 1; | ||
133 | pci_bus_write_config_dword(pbus, devfn, | ||
134 | address[count], | ||
135 | (u32)(res->base >> 32)); | ||
136 | } | ||
137 | |||
138 | res->next = func->p_mem_head; | ||
139 | func->p_mem_head = res; | ||
140 | |||
141 | } else { | ||
142 | /* regular memory */ | ||
143 | |||
144 | len = bar & 0xFFFFFFF0; | ||
145 | len = ~len + 1; | ||
146 | |||
147 | dbg("len in MEM %x, BAR %d\n", len, count); | ||
148 | |||
149 | spin_lock(&bridge->res_lock); | ||
150 | res = acpiphp_get_resource(&bridge->mem_head, len); | ||
151 | spin_unlock(&bridge->res_lock); | ||
152 | |||
153 | if (!res) { | ||
154 | err("cannot allocate requested pfmem for %02x:%02x.%d len %x\n", | ||
155 | bus, device, function, len); | ||
156 | return -1; | ||
157 | } | ||
158 | |||
159 | pci_bus_write_config_dword(pbus, devfn, | ||
160 | address[count], | ||
161 | (u32)res->base); | ||
162 | |||
163 | if (bar & PCI_BASE_ADDRESS_MEM_TYPE_64) { | ||
164 | /* takes up another dword */ | ||
165 | dbg("inside mem 64 case, reg. mem, count %d\n", count); | ||
166 | count += 1; | ||
167 | pci_bus_write_config_dword(pbus, devfn, | ||
168 | address[count], | ||
169 | (u32)(res->base >> 32)); | ||
170 | } | ||
171 | |||
172 | res->next = func->mem_head; | ||
173 | func->mem_head = res; | ||
174 | |||
175 | } | ||
176 | } | ||
177 | } | ||
178 | |||
179 | /* disable expansion rom */ | ||
180 | pci_bus_write_config_dword(pbus, devfn, PCI_ROM_ADDRESS, 0x00000000); | ||
181 | |||
182 | /* set PCI parameters from _HPP */ | ||
183 | pci_bus_write_config_byte(pbus, devfn, PCI_CACHE_LINE_SIZE, | ||
184 | bridge->hpp.cache_line_size); | ||
185 | pci_bus_write_config_byte(pbus, devfn, PCI_LATENCY_TIMER, | ||
186 | bridge->hpp.latency_timer); | ||
187 | |||
188 | pci_bus_read_config_word(pbus, devfn, PCI_COMMAND, &tmp); | ||
189 | if (bridge->hpp.enable_SERR) | ||
190 | tmp |= PCI_COMMAND_SERR; | ||
191 | if (bridge->hpp.enable_PERR) | ||
192 | tmp |= PCI_COMMAND_PARITY; | ||
193 | pci_bus_write_config_word(pbus, devfn, PCI_COMMAND, tmp); | ||
194 | |||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | /* detect_used_resource - subtract resource under dev from bridge */ | ||
199 | static int detect_used_resource (struct acpiphp_bridge *bridge, struct pci_dev *dev) | ||
200 | { | ||
201 | int count; | ||
202 | |||
203 | dbg("Device %s\n", pci_name(dev)); | ||
204 | |||
205 | for (count = 0; count < DEVICE_COUNT_RESOURCE; count++) { | ||
206 | struct pci_resource *res; | ||
207 | struct pci_resource **head; | ||
208 | unsigned long base = dev->resource[count].start; | ||
209 | unsigned long len = dev->resource[count].end - base + 1; | ||
210 | unsigned long flags = dev->resource[count].flags; | ||
211 | |||
212 | if (!flags) | ||
213 | continue; | ||
214 | |||
215 | dbg("BAR[%d] 0x%lx - 0x%lx (0x%lx)\n", count, base, | ||
216 | base + len - 1, flags); | ||
217 | |||
218 | if (flags & IORESOURCE_IO) { | ||
219 | head = &bridge->io_head; | ||
220 | } else if (flags & IORESOURCE_PREFETCH) { | ||
221 | head = &bridge->p_mem_head; | ||
222 | } else { | ||
223 | head = &bridge->mem_head; | ||
224 | } | ||
225 | |||
226 | spin_lock(&bridge->res_lock); | ||
227 | res = acpiphp_get_resource_with_base(head, base, len); | ||
228 | spin_unlock(&bridge->res_lock); | ||
229 | if (res) | ||
230 | kfree(res); | ||
231 | } | ||
232 | |||
233 | return 0; | ||
234 | } | ||
235 | |||
236 | |||
237 | /** | ||
238 | * acpiphp_detect_pci_resource - detect resources under bridge | ||
239 | * @bridge: detect all resources already used under this bridge | ||
240 | * | ||
241 | * collect all resources already allocated for all devices under a bridge. | ||
242 | */ | ||
243 | int acpiphp_detect_pci_resource (struct acpiphp_bridge *bridge) | ||
244 | { | ||
245 | struct list_head *l; | ||
246 | struct pci_dev *dev; | ||
247 | |||
248 | list_for_each (l, &bridge->pci_bus->devices) { | ||
249 | dev = pci_dev_b(l); | ||
250 | detect_used_resource(bridge, dev); | ||
251 | } | ||
252 | |||
253 | return 0; | ||
254 | } | ||
255 | |||
256 | |||
257 | /** | ||
258 | * acpiphp_init_slot_resource - gather resource usage information of a slot | ||
259 | * @slot: ACPI slot object to be checked, should have valid pci_dev member | ||
260 | * | ||
261 | * TBD: PCI-to-PCI bridge case | ||
262 | * use pci_dev->resource[] | ||
263 | */ | ||
264 | int acpiphp_init_func_resource (struct acpiphp_func *func) | ||
265 | { | ||
266 | u64 base; | ||
267 | u32 bar, len; | ||
268 | u32 address[] = { | ||
269 | PCI_BASE_ADDRESS_0, | ||
270 | PCI_BASE_ADDRESS_1, | ||
271 | PCI_BASE_ADDRESS_2, | ||
272 | PCI_BASE_ADDRESS_3, | ||
273 | PCI_BASE_ADDRESS_4, | ||
274 | PCI_BASE_ADDRESS_5, | ||
275 | 0 | ||
276 | }; | ||
277 | int count; | ||
278 | struct pci_resource *res; | ||
279 | struct pci_dev *dev; | ||
280 | |||
281 | dev = func->pci_dev; | ||
282 | dbg("Hot-pluggable device %s\n", pci_name(dev)); | ||
283 | |||
284 | for (count = 0; address[count]; count++) { /* for 6 BARs */ | ||
285 | pci_read_config_dword(dev, address[count], &bar); | ||
286 | |||
287 | if (!bar) /* This BAR is not implemented */ | ||
288 | continue; | ||
289 | |||
290 | pci_write_config_dword(dev, address[count], 0xFFFFFFFF); | ||
291 | pci_read_config_dword(dev, address[count], &len); | ||
292 | |||
293 | if (len & PCI_BASE_ADDRESS_SPACE_IO) { | ||
294 | /* This is IO */ | ||
295 | base = bar & 0xFFFFFFFC; | ||
296 | len = len & (PCI_BASE_ADDRESS_IO_MASK & 0xFFFF); | ||
297 | len = len & ~(len - 1); | ||
298 | |||
299 | dbg("BAR[%d] %08x - %08x (IO)\n", count, (u32)base, (u32)base + len - 1); | ||
300 | |||
301 | res = acpiphp_make_resource(base, len); | ||
302 | if (!res) | ||
303 | goto no_memory; | ||
304 | |||
305 | res->next = func->io_head; | ||
306 | func->io_head = res; | ||
307 | |||
308 | } else { | ||
309 | /* This is Memory */ | ||
310 | base = bar & 0xFFFFFFF0; | ||
311 | if (len & PCI_BASE_ADDRESS_MEM_PREFETCH) { | ||
312 | /* pfmem */ | ||
313 | |||
314 | len &= 0xFFFFFFF0; | ||
315 | len = ~len + 1; | ||
316 | |||
317 | if (len & PCI_BASE_ADDRESS_MEM_TYPE_64) { /* takes up another dword */ | ||
318 | dbg("prefetch mem 64\n"); | ||
319 | count += 1; | ||
320 | } | ||
321 | dbg("BAR[%d] %08x - %08x (PMEM)\n", count, (u32)base, (u32)base + len - 1); | ||
322 | res = acpiphp_make_resource(base, len); | ||
323 | if (!res) | ||
324 | goto no_memory; | ||
325 | |||
326 | res->next = func->p_mem_head; | ||
327 | func->p_mem_head = res; | ||
328 | |||
329 | } else { | ||
330 | /* regular memory */ | ||
331 | |||
332 | len &= 0xFFFFFFF0; | ||
333 | len = ~len + 1; | ||
334 | |||
335 | if (len & PCI_BASE_ADDRESS_MEM_TYPE_64) { | ||
336 | /* takes up another dword */ | ||
337 | dbg("mem 64\n"); | ||
338 | count += 1; | ||
339 | } | ||
340 | dbg("BAR[%d] %08x - %08x (MEM)\n", count, (u32)base, (u32)base + len - 1); | ||
341 | res = acpiphp_make_resource(base, len); | ||
342 | if (!res) | ||
343 | goto no_memory; | ||
344 | |||
345 | res->next = func->mem_head; | ||
346 | func->mem_head = res; | ||
347 | |||
348 | } | ||
349 | } | ||
350 | |||
351 | pci_write_config_dword(dev, address[count], bar); | ||
352 | } | ||
353 | #if 1 | ||
354 | acpiphp_dump_func_resource(func); | ||
355 | #endif | ||
356 | |||
357 | return 0; | ||
358 | |||
359 | no_memory: | ||
360 | err("out of memory\n"); | ||
361 | acpiphp_free_resource(&func->io_head); | ||
362 | acpiphp_free_resource(&func->mem_head); | ||
363 | acpiphp_free_resource(&func->p_mem_head); | ||
364 | |||
365 | return -1; | ||
366 | } | ||
367 | |||
368 | |||
369 | /** | ||
370 | * acpiphp_configure_slot - allocate PCI resources | ||
371 | * @slot: slot to be configured | ||
372 | * | ||
373 | * initializes a PCI functions on a device inserted | ||
374 | * into the slot | ||
375 | * | ||
376 | */ | ||
377 | int acpiphp_configure_slot (struct acpiphp_slot *slot) | ||
378 | { | ||
379 | struct acpiphp_func *func; | ||
380 | struct list_head *l; | ||
381 | u8 hdr; | ||
382 | u32 dvid; | ||
383 | int retval = 0; | ||
384 | int is_multi = 0; | ||
385 | |||
386 | pci_bus_read_config_byte(slot->bridge->pci_bus, | ||
387 | PCI_DEVFN(slot->device, 0), | ||
388 | PCI_HEADER_TYPE, &hdr); | ||
389 | |||
390 | if (hdr & 0x80) | ||
391 | is_multi = 1; | ||
392 | |||
393 | list_for_each (l, &slot->funcs) { | ||
394 | func = list_entry(l, struct acpiphp_func, sibling); | ||
395 | if (is_multi || func->function == 0) { | ||
396 | pci_bus_read_config_dword(slot->bridge->pci_bus, | ||
397 | PCI_DEVFN(slot->device, | ||
398 | func->function), | ||
399 | PCI_VENDOR_ID, &dvid); | ||
400 | if (dvid != 0xffffffff) { | ||
401 | retval = init_config_space(func); | ||
402 | if (retval) | ||
403 | break; | ||
404 | } | ||
405 | } | ||
406 | } | ||
407 | |||
408 | return retval; | ||
409 | } | ||
410 | |||
411 | /** | ||
412 | * acpiphp_configure_function - configure PCI function | ||
413 | * @func: function to be configured | ||
414 | * | ||
415 | * initializes a PCI functions on a device inserted | ||
416 | * into the slot | ||
417 | * | ||
418 | */ | ||
419 | int acpiphp_configure_function (struct acpiphp_func *func) | ||
420 | { | ||
421 | /* all handled by the pci core now */ | ||
422 | return 0; | ||
423 | } | ||
424 | |||
425 | /** | ||
426 | * acpiphp_unconfigure_function - unconfigure PCI function | ||
427 | * @func: function to be unconfigured | ||
428 | * | ||
429 | */ | ||
430 | void acpiphp_unconfigure_function (struct acpiphp_func *func) | ||
431 | { | ||
432 | struct acpiphp_bridge *bridge; | ||
433 | |||
434 | /* if pci_dev is NULL, ignore it */ | ||
435 | if (!func->pci_dev) | ||
436 | return; | ||
437 | |||
438 | pci_remove_bus_device(func->pci_dev); | ||
439 | |||
440 | /* free all resources */ | ||
441 | bridge = func->slot->bridge; | ||
442 | |||
443 | spin_lock(&bridge->res_lock); | ||
444 | acpiphp_move_resource(&func->io_head, &bridge->io_head); | ||
445 | acpiphp_move_resource(&func->mem_head, &bridge->mem_head); | ||
446 | acpiphp_move_resource(&func->p_mem_head, &bridge->p_mem_head); | ||
447 | acpiphp_move_resource(&func->bus_head, &bridge->bus_head); | ||
448 | spin_unlock(&bridge->res_lock); | ||
449 | } | ||
diff --git a/drivers/pci/hotplug/acpiphp_res.c b/drivers/pci/hotplug/acpiphp_res.c new file mode 100644 index 000000000000..f54b1fa7b75a --- /dev/null +++ b/drivers/pci/hotplug/acpiphp_res.c | |||
@@ -0,0 +1,700 @@ | |||
1 | /* | ||
2 | * ACPI PCI HotPlug Utility functions | ||
3 | * | ||
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | ||
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * Copyright (C) 2001 IBM Corp. | ||
7 | * Copyright (C) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com) | ||
8 | * Copyright (C) 2002 Takayoshi Kochi (t-kochi@bq.jp.nec.com) | ||
9 | * Copyright (C) 2002 NEC Corporation | ||
10 | * | ||
11 | * All rights reserved. | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify | ||
14 | * it under the terms of the GNU General Public License as published by | ||
15 | * the Free Software Foundation; either version 2 of the License, or (at | ||
16 | * your option) any later version. | ||
17 | * | ||
18 | * This program is distributed in the hope that it will be useful, but | ||
19 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
20 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
21 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
22 | * details. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License | ||
25 | * along with this program; if not, write to the Free Software | ||
26 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
27 | * | ||
28 | * Send feedback to <gregkh@us.ibm.com>, <t-kochi@bq.jp.nec.com> | ||
29 | * | ||
30 | */ | ||
31 | |||
32 | #include <linux/init.h> | ||
33 | #include <linux/module.h> | ||
34 | |||
35 | #include <linux/kernel.h> | ||
36 | #include <linux/types.h> | ||
37 | #include <linux/proc_fs.h> | ||
38 | #include <linux/sysctl.h> | ||
39 | #include <linux/pci.h> | ||
40 | #include <linux/smp.h> | ||
41 | #include <linux/smp_lock.h> | ||
42 | |||
43 | #include <linux/string.h> | ||
44 | #include <linux/mm.h> | ||
45 | #include <linux/errno.h> | ||
46 | #include <linux/ioport.h> | ||
47 | #include <linux/slab.h> | ||
48 | #include <linux/interrupt.h> | ||
49 | #include <linux/timer.h> | ||
50 | |||
51 | #include <linux/ioctl.h> | ||
52 | #include <linux/fcntl.h> | ||
53 | |||
54 | #include <linux/list.h> | ||
55 | |||
56 | #include "pci_hotplug.h" | ||
57 | #include "acpiphp.h" | ||
58 | |||
59 | #define MY_NAME "acpiphp_res" | ||
60 | |||
61 | |||
62 | /* | ||
63 | * sort_by_size - sort nodes by their length, smallest first | ||
64 | */ | ||
65 | static int sort_by_size(struct pci_resource **head) | ||
66 | { | ||
67 | struct pci_resource *current_res; | ||
68 | struct pci_resource *next_res; | ||
69 | int out_of_order = 1; | ||
70 | |||
71 | if (!(*head)) | ||
72 | return 1; | ||
73 | |||
74 | if (!((*head)->next)) | ||
75 | return 0; | ||
76 | |||
77 | while (out_of_order) { | ||
78 | out_of_order = 0; | ||
79 | |||
80 | /* Special case for swapping list head */ | ||
81 | if (((*head)->next) && | ||
82 | ((*head)->length > (*head)->next->length)) { | ||
83 | out_of_order++; | ||
84 | current_res = *head; | ||
85 | *head = (*head)->next; | ||
86 | current_res->next = (*head)->next; | ||
87 | (*head)->next = current_res; | ||
88 | } | ||
89 | |||
90 | current_res = *head; | ||
91 | |||
92 | while (current_res->next && current_res->next->next) { | ||
93 | if (current_res->next->length > current_res->next->next->length) { | ||
94 | out_of_order++; | ||
95 | next_res = current_res->next; | ||
96 | current_res->next = current_res->next->next; | ||
97 | current_res = current_res->next; | ||
98 | next_res->next = current_res->next; | ||
99 | current_res->next = next_res; | ||
100 | } else | ||
101 | current_res = current_res->next; | ||
102 | } | ||
103 | } /* End of out_of_order loop */ | ||
104 | |||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | #if 0 | ||
109 | /* | ||
110 | * sort_by_max_size - sort nodes by their length, largest first | ||
111 | */ | ||
112 | static int sort_by_max_size(struct pci_resource **head) | ||
113 | { | ||
114 | struct pci_resource *current_res; | ||
115 | struct pci_resource *next_res; | ||
116 | int out_of_order = 1; | ||
117 | |||
118 | if (!(*head)) | ||
119 | return 1; | ||
120 | |||
121 | if (!((*head)->next)) | ||
122 | return 0; | ||
123 | |||
124 | while (out_of_order) { | ||
125 | out_of_order = 0; | ||
126 | |||
127 | /* Special case for swapping list head */ | ||
128 | if (((*head)->next) && | ||
129 | ((*head)->length < (*head)->next->length)) { | ||
130 | out_of_order++; | ||
131 | current_res = *head; | ||
132 | *head = (*head)->next; | ||
133 | current_res->next = (*head)->next; | ||
134 | (*head)->next = current_res; | ||
135 | } | ||
136 | |||
137 | current_res = *head; | ||
138 | |||
139 | while (current_res->next && current_res->next->next) { | ||
140 | if (current_res->next->length < current_res->next->next->length) { | ||
141 | out_of_order++; | ||
142 | next_res = current_res->next; | ||
143 | current_res->next = current_res->next->next; | ||
144 | current_res = current_res->next; | ||
145 | next_res->next = current_res->next; | ||
146 | current_res->next = next_res; | ||
147 | } else | ||
148 | current_res = current_res->next; | ||
149 | } | ||
150 | } /* End of out_of_order loop */ | ||
151 | |||
152 | return 0; | ||
153 | } | ||
154 | #endif | ||
155 | |||
156 | /** | ||
157 | * get_io_resource - get resource for I/O ports | ||
158 | * | ||
159 | * this function sorts the resource list by size and then | ||
160 | * returns the first node of "size" length that is not in the | ||
161 | * ISA aliasing window. If it finds a node larger than "size" | ||
162 | * it will split it up. | ||
163 | * | ||
164 | * size must be a power of two. | ||
165 | * | ||
166 | * difference from get_resource is handling of ISA aliasing space. | ||
167 | * | ||
168 | */ | ||
169 | struct pci_resource *acpiphp_get_io_resource (struct pci_resource **head, u32 size) | ||
170 | { | ||
171 | struct pci_resource *prevnode; | ||
172 | struct pci_resource *node; | ||
173 | struct pci_resource *split_node; | ||
174 | u64 temp_qword; | ||
175 | |||
176 | if (!(*head)) | ||
177 | return NULL; | ||
178 | |||
179 | if (acpiphp_resource_sort_and_combine(head)) | ||
180 | return NULL; | ||
181 | |||
182 | if (sort_by_size(head)) | ||
183 | return NULL; | ||
184 | |||
185 | for (node = *head; node; node = node->next) { | ||
186 | if (node->length < size) | ||
187 | continue; | ||
188 | |||
189 | if (node->base & (size - 1)) { | ||
190 | /* this one isn't base aligned properly | ||
191 | so we'll make a new entry and split it up */ | ||
192 | temp_qword = (node->base | (size-1)) + 1; | ||
193 | |||
194 | /* Short circuit if adjusted size is too small */ | ||
195 | if ((node->length - (temp_qword - node->base)) < size) | ||
196 | continue; | ||
197 | |||
198 | split_node = acpiphp_make_resource(node->base, temp_qword - node->base); | ||
199 | |||
200 | if (!split_node) | ||
201 | return NULL; | ||
202 | |||
203 | node->base = temp_qword; | ||
204 | node->length -= split_node->length; | ||
205 | |||
206 | /* Put it in the list */ | ||
207 | split_node->next = node->next; | ||
208 | node->next = split_node; | ||
209 | } /* End of non-aligned base */ | ||
210 | |||
211 | /* Don't need to check if too small since we already did */ | ||
212 | if (node->length > size) { | ||
213 | /* this one is longer than we need | ||
214 | so we'll make a new entry and split it up */ | ||
215 | split_node = acpiphp_make_resource(node->base + size, node->length - size); | ||
216 | |||
217 | if (!split_node) | ||
218 | return NULL; | ||
219 | |||
220 | node->length = size; | ||
221 | |||
222 | /* Put it in the list */ | ||
223 | split_node->next = node->next; | ||
224 | node->next = split_node; | ||
225 | } /* End of too big on top end */ | ||
226 | |||
227 | /* For IO make sure it's not in the ISA aliasing space */ | ||
228 | if ((node->base & 0x300L) && !(node->base & 0xfffff000)) | ||
229 | continue; | ||
230 | |||
231 | /* If we got here, then it is the right size | ||
232 | Now take it out of the list */ | ||
233 | if (*head == node) { | ||
234 | *head = node->next; | ||
235 | } else { | ||
236 | prevnode = *head; | ||
237 | while (prevnode->next != node) | ||
238 | prevnode = prevnode->next; | ||
239 | |||
240 | prevnode->next = node->next; | ||
241 | } | ||
242 | node->next = NULL; | ||
243 | /* Stop looping */ | ||
244 | break; | ||
245 | } | ||
246 | |||
247 | return node; | ||
248 | } | ||
249 | |||
250 | |||
251 | #if 0 | ||
252 | /** | ||
253 | * get_max_resource - get the largest resource | ||
254 | * | ||
255 | * Gets the largest node that is at least "size" big from the | ||
256 | * list pointed to by head. It aligns the node on top and bottom | ||
257 | * to "size" alignment before returning it. | ||
258 | */ | ||
259 | static struct pci_resource *acpiphp_get_max_resource (struct pci_resource **head, u32 size) | ||
260 | { | ||
261 | struct pci_resource *max; | ||
262 | struct pci_resource *temp; | ||
263 | struct pci_resource *split_node; | ||
264 | u64 temp_qword; | ||
265 | |||
266 | if (!(*head)) | ||
267 | return NULL; | ||
268 | |||
269 | if (acpiphp_resource_sort_and_combine(head)) | ||
270 | return NULL; | ||
271 | |||
272 | if (sort_by_max_size(head)) | ||
273 | return NULL; | ||
274 | |||
275 | for (max = *head;max; max = max->next) { | ||
276 | |||
277 | /* If not big enough we could probably just bail, | ||
278 | instead we'll continue to the next. */ | ||
279 | if (max->length < size) | ||
280 | continue; | ||
281 | |||
282 | if (max->base & (size - 1)) { | ||
283 | /* this one isn't base aligned properly | ||
284 | so we'll make a new entry and split it up */ | ||
285 | temp_qword = (max->base | (size-1)) + 1; | ||
286 | |||
287 | /* Short circuit if adjusted size is too small */ | ||
288 | if ((max->length - (temp_qword - max->base)) < size) | ||
289 | continue; | ||
290 | |||
291 | split_node = acpiphp_make_resource(max->base, temp_qword - max->base); | ||
292 | |||
293 | if (!split_node) | ||
294 | return NULL; | ||
295 | |||
296 | max->base = temp_qword; | ||
297 | max->length -= split_node->length; | ||
298 | |||
299 | /* Put it next in the list */ | ||
300 | split_node->next = max->next; | ||
301 | max->next = split_node; | ||
302 | } | ||
303 | |||
304 | if ((max->base + max->length) & (size - 1)) { | ||
305 | /* this one isn't end aligned properly at the top | ||
306 | so we'll make a new entry and split it up */ | ||
307 | temp_qword = ((max->base + max->length) & ~(size - 1)); | ||
308 | |||
309 | split_node = acpiphp_make_resource(temp_qword, | ||
310 | max->length + max->base - temp_qword); | ||
311 | |||
312 | if (!split_node) | ||
313 | return NULL; | ||
314 | |||
315 | max->length -= split_node->length; | ||
316 | |||
317 | /* Put it in the list */ | ||
318 | split_node->next = max->next; | ||
319 | max->next = split_node; | ||
320 | } | ||
321 | |||
322 | /* Make sure it didn't shrink too much when we aligned it */ | ||
323 | if (max->length < size) | ||
324 | continue; | ||
325 | |||
326 | /* Now take it out of the list */ | ||
327 | temp = (struct pci_resource*) *head; | ||
328 | if (temp == max) { | ||
329 | *head = max->next; | ||
330 | } else { | ||
331 | while (temp && temp->next != max) { | ||
332 | temp = temp->next; | ||
333 | } | ||
334 | |||
335 | temp->next = max->next; | ||
336 | } | ||
337 | |||
338 | max->next = NULL; | ||
339 | return max; | ||
340 | } | ||
341 | |||
342 | /* If we get here, we couldn't find one */ | ||
343 | return NULL; | ||
344 | } | ||
345 | #endif | ||
346 | |||
347 | /** | ||
348 | * get_resource - get resource (mem, pfmem) | ||
349 | * | ||
350 | * this function sorts the resource list by size and then | ||
351 | * returns the first node of "size" length. If it finds a node | ||
352 | * larger than "size" it will split it up. | ||
353 | * | ||
354 | * size must be a power of two. | ||
355 | * | ||
356 | */ | ||
357 | struct pci_resource *acpiphp_get_resource (struct pci_resource **head, u32 size) | ||
358 | { | ||
359 | struct pci_resource *prevnode; | ||
360 | struct pci_resource *node; | ||
361 | struct pci_resource *split_node; | ||
362 | u64 temp_qword; | ||
363 | |||
364 | if (!(*head)) | ||
365 | return NULL; | ||
366 | |||
367 | if (acpiphp_resource_sort_and_combine(head)) | ||
368 | return NULL; | ||
369 | |||
370 | if (sort_by_size(head)) | ||
371 | return NULL; | ||
372 | |||
373 | for (node = *head; node; node = node->next) { | ||
374 | dbg("%s: req_size =%x node=%p, base=%x, length=%x\n", | ||
375 | __FUNCTION__, size, node, (u32)node->base, node->length); | ||
376 | if (node->length < size) | ||
377 | continue; | ||
378 | |||
379 | if (node->base & (size - 1)) { | ||
380 | dbg("%s: not aligned\n", __FUNCTION__); | ||
381 | /* this one isn't base aligned properly | ||
382 | so we'll make a new entry and split it up */ | ||
383 | temp_qword = (node->base | (size-1)) + 1; | ||
384 | |||
385 | /* Short circuit if adjusted size is too small */ | ||
386 | if ((node->length - (temp_qword - node->base)) < size) | ||
387 | continue; | ||
388 | |||
389 | split_node = acpiphp_make_resource(node->base, temp_qword - node->base); | ||
390 | |||
391 | if (!split_node) | ||
392 | return NULL; | ||
393 | |||
394 | node->base = temp_qword; | ||
395 | node->length -= split_node->length; | ||
396 | |||
397 | /* Put it in the list */ | ||
398 | split_node->next = node->next; | ||
399 | node->next = split_node; | ||
400 | } /* End of non-aligned base */ | ||
401 | |||
402 | /* Don't need to check if too small since we already did */ | ||
403 | if (node->length > size) { | ||
404 | dbg("%s: too big\n", __FUNCTION__); | ||
405 | /* this one is longer than we need | ||
406 | so we'll make a new entry and split it up */ | ||
407 | split_node = acpiphp_make_resource(node->base + size, node->length - size); | ||
408 | |||
409 | if (!split_node) | ||
410 | return NULL; | ||
411 | |||
412 | node->length = size; | ||
413 | |||
414 | /* Put it in the list */ | ||
415 | split_node->next = node->next; | ||
416 | node->next = split_node; | ||
417 | } /* End of too big on top end */ | ||
418 | |||
419 | dbg("%s: got one!!!\n", __FUNCTION__); | ||
420 | /* If we got here, then it is the right size | ||
421 | Now take it out of the list */ | ||
422 | if (*head == node) { | ||
423 | *head = node->next; | ||
424 | } else { | ||
425 | prevnode = *head; | ||
426 | while (prevnode->next != node) | ||
427 | prevnode = prevnode->next; | ||
428 | |||
429 | prevnode->next = node->next; | ||
430 | } | ||
431 | node->next = NULL; | ||
432 | /* Stop looping */ | ||
433 | break; | ||
434 | } | ||
435 | return node; | ||
436 | } | ||
437 | |||
438 | /** | ||
439 | * get_resource_with_base - get resource with specific base address | ||
440 | * | ||
441 | * this function | ||
442 | * returns the first node of "size" length located at specified base address. | ||
443 | * If it finds a node larger than "size" it will split it up. | ||
444 | * | ||
445 | * size must be a power of two. | ||
446 | * | ||
447 | */ | ||
448 | struct pci_resource *acpiphp_get_resource_with_base (struct pci_resource **head, u64 base, u32 size) | ||
449 | { | ||
450 | struct pci_resource *prevnode; | ||
451 | struct pci_resource *node; | ||
452 | struct pci_resource *split_node; | ||
453 | u64 temp_qword; | ||
454 | |||
455 | if (!(*head)) | ||
456 | return NULL; | ||
457 | |||
458 | if (acpiphp_resource_sort_and_combine(head)) | ||
459 | return NULL; | ||
460 | |||
461 | for (node = *head; node; node = node->next) { | ||
462 | dbg(": 1st req_base=%x req_size =%x node=%p, base=%x, length=%x\n", | ||
463 | (u32)base, size, node, (u32)node->base, node->length); | ||
464 | if (node->base > base) | ||
465 | continue; | ||
466 | |||
467 | if ((node->base + node->length) < (base + size)) | ||
468 | continue; | ||
469 | |||
470 | if (node->base < base) { | ||
471 | dbg(": split 1\n"); | ||
472 | /* this one isn't base aligned properly | ||
473 | so we'll make a new entry and split it up */ | ||
474 | temp_qword = base; | ||
475 | |||
476 | /* Short circuit if adjusted size is too small */ | ||
477 | if ((node->length - (temp_qword - node->base)) < size) | ||
478 | continue; | ||
479 | |||
480 | split_node = acpiphp_make_resource(node->base, temp_qword - node->base); | ||
481 | |||
482 | if (!split_node) | ||
483 | return NULL; | ||
484 | |||
485 | node->base = temp_qword; | ||
486 | node->length -= split_node->length; | ||
487 | |||
488 | /* Put it in the list */ | ||
489 | split_node->next = node->next; | ||
490 | node->next = split_node; | ||
491 | } | ||
492 | |||
493 | dbg(": 2nd req_base=%x req_size =%x node=%p, base=%x, length=%x\n", | ||
494 | (u32)base, size, node, (u32)node->base, node->length); | ||
495 | |||
496 | /* Don't need to check if too small since we already did */ | ||
497 | if (node->length > size) { | ||
498 | dbg(": split 2\n"); | ||
499 | /* this one is longer than we need | ||
500 | so we'll make a new entry and split it up */ | ||
501 | split_node = acpiphp_make_resource(node->base + size, node->length - size); | ||
502 | |||
503 | if (!split_node) | ||
504 | return NULL; | ||
505 | |||
506 | node->length = size; | ||
507 | |||
508 | /* Put it in the list */ | ||
509 | split_node->next = node->next; | ||
510 | node->next = split_node; | ||
511 | } /* End of too big on top end */ | ||
512 | |||
513 | dbg(": got one!!!\n"); | ||
514 | /* If we got here, then it is the right size | ||
515 | Now take it out of the list */ | ||
516 | if (*head == node) { | ||
517 | *head = node->next; | ||
518 | } else { | ||
519 | prevnode = *head; | ||
520 | while (prevnode->next != node) | ||
521 | prevnode = prevnode->next; | ||
522 | |||
523 | prevnode->next = node->next; | ||
524 | } | ||
525 | node->next = NULL; | ||
526 | /* Stop looping */ | ||
527 | break; | ||
528 | } | ||
529 | return node; | ||
530 | } | ||
531 | |||
532 | |||
533 | /** | ||
534 | * acpiphp_resource_sort_and_combine | ||
535 | * | ||
536 | * Sorts all of the nodes in the list in ascending order by | ||
537 | * their base addresses. Also does garbage collection by | ||
538 | * combining adjacent nodes. | ||
539 | * | ||
540 | * returns 0 if success | ||
541 | */ | ||
542 | int acpiphp_resource_sort_and_combine (struct pci_resource **head) | ||
543 | { | ||
544 | struct pci_resource *node1; | ||
545 | struct pci_resource *node2; | ||
546 | int out_of_order = 1; | ||
547 | |||
548 | if (!(*head)) | ||
549 | return 1; | ||
550 | |||
551 | dbg("*head->next = %p\n",(*head)->next); | ||
552 | |||
553 | if (!(*head)->next) | ||
554 | return 0; /* only one item on the list, already sorted! */ | ||
555 | |||
556 | dbg("*head->base = 0x%x\n",(u32)(*head)->base); | ||
557 | dbg("*head->next->base = 0x%x\n", (u32)(*head)->next->base); | ||
558 | while (out_of_order) { | ||
559 | out_of_order = 0; | ||
560 | |||
561 | /* Special case for swapping list head */ | ||
562 | if (((*head)->next) && | ||
563 | ((*head)->base > (*head)->next->base)) { | ||
564 | node1 = *head; | ||
565 | (*head) = (*head)->next; | ||
566 | node1->next = (*head)->next; | ||
567 | (*head)->next = node1; | ||
568 | out_of_order++; | ||
569 | } | ||
570 | |||
571 | node1 = (*head); | ||
572 | |||
573 | while (node1->next && node1->next->next) { | ||
574 | if (node1->next->base > node1->next->next->base) { | ||
575 | out_of_order++; | ||
576 | node2 = node1->next; | ||
577 | node1->next = node1->next->next; | ||
578 | node1 = node1->next; | ||
579 | node2->next = node1->next; | ||
580 | node1->next = node2; | ||
581 | } else | ||
582 | node1 = node1->next; | ||
583 | } | ||
584 | } /* End of out_of_order loop */ | ||
585 | |||
586 | node1 = *head; | ||
587 | |||
588 | while (node1 && node1->next) { | ||
589 | if ((node1->base + node1->length) == node1->next->base) { | ||
590 | /* Combine */ | ||
591 | dbg("8..\n"); | ||
592 | node1->length += node1->next->length; | ||
593 | node2 = node1->next; | ||
594 | node1->next = node1->next->next; | ||
595 | kfree(node2); | ||
596 | } else | ||
597 | node1 = node1->next; | ||
598 | } | ||
599 | |||
600 | return 0; | ||
601 | } | ||
602 | |||
603 | |||
604 | /** | ||
605 | * acpiphp_make_resource - make resource structure | ||
606 | * @base: base address of a resource | ||
607 | * @length: length of a resource | ||
608 | */ | ||
609 | struct pci_resource *acpiphp_make_resource (u64 base, u32 length) | ||
610 | { | ||
611 | struct pci_resource *res; | ||
612 | |||
613 | res = kmalloc(sizeof(struct pci_resource), GFP_KERNEL); | ||
614 | if (res) { | ||
615 | memset(res, 0, sizeof(struct pci_resource)); | ||
616 | res->base = base; | ||
617 | res->length = length; | ||
618 | } | ||
619 | |||
620 | return res; | ||
621 | } | ||
622 | |||
623 | |||
624 | /** | ||
625 | * acpiphp_move_resource - move linked resources from one to another | ||
626 | * @from: head of linked resource list | ||
627 | * @to: head of linked resource list | ||
628 | */ | ||
629 | void acpiphp_move_resource (struct pci_resource **from, struct pci_resource **to) | ||
630 | { | ||
631 | struct pci_resource *tmp; | ||
632 | |||
633 | while (*from) { | ||
634 | tmp = (*from)->next; | ||
635 | (*from)->next = *to; | ||
636 | *to = *from; | ||
637 | *from = tmp; | ||
638 | } | ||
639 | |||
640 | /* *from = NULL is guaranteed */ | ||
641 | } | ||
642 | |||
643 | |||
644 | /** | ||
645 | * acpiphp_free_resource - free all linked resources | ||
646 | * @res: head of linked resource list | ||
647 | */ | ||
648 | void acpiphp_free_resource (struct pci_resource **res) | ||
649 | { | ||
650 | struct pci_resource *tmp; | ||
651 | |||
652 | while (*res) { | ||
653 | tmp = (*res)->next; | ||
654 | kfree(*res); | ||
655 | *res = tmp; | ||
656 | } | ||
657 | |||
658 | /* *res = NULL is guaranteed */ | ||
659 | } | ||
660 | |||
661 | |||
662 | /* debug support functions; will go away sometime :) */ | ||
663 | static void dump_resource(struct pci_resource *head) | ||
664 | { | ||
665 | struct pci_resource *p; | ||
666 | int cnt; | ||
667 | |||
668 | p = head; | ||
669 | cnt = 0; | ||
670 | |||
671 | while (p) { | ||
672 | dbg("[%02d] %08x - %08x\n", | ||
673 | cnt++, (u32)p->base, (u32)p->base + p->length - 1); | ||
674 | p = p->next; | ||
675 | } | ||
676 | } | ||
677 | |||
678 | void acpiphp_dump_resource(struct acpiphp_bridge *bridge) | ||
679 | { | ||
680 | dbg("I/O resource:\n"); | ||
681 | dump_resource(bridge->io_head); | ||
682 | dbg("MEM resource:\n"); | ||
683 | dump_resource(bridge->mem_head); | ||
684 | dbg("PMEM resource:\n"); | ||
685 | dump_resource(bridge->p_mem_head); | ||
686 | dbg("BUS resource:\n"); | ||
687 | dump_resource(bridge->bus_head); | ||
688 | } | ||
689 | |||
690 | void acpiphp_dump_func_resource(struct acpiphp_func *func) | ||
691 | { | ||
692 | dbg("I/O resource:\n"); | ||
693 | dump_resource(func->io_head); | ||
694 | dbg("MEM resource:\n"); | ||
695 | dump_resource(func->mem_head); | ||
696 | dbg("PMEM resource:\n"); | ||
697 | dump_resource(func->p_mem_head); | ||
698 | dbg("BUS resource:\n"); | ||
699 | dump_resource(func->bus_head); | ||
700 | } | ||
diff --git a/drivers/pci/hotplug/cpci_hotplug.h b/drivers/pci/hotplug/cpci_hotplug.h new file mode 100644 index 000000000000..3ddd75937a40 --- /dev/null +++ b/drivers/pci/hotplug/cpci_hotplug.h | |||
@@ -0,0 +1,96 @@ | |||
1 | /* | ||
2 | * CompactPCI Hot Plug Core Functions | ||
3 | * | ||
4 | * Copyright (C) 2002 SOMA Networks, Inc. | ||
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * Copyright (C) 2001 IBM Corp. | ||
7 | * | ||
8 | * All rights reserved. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or (at | ||
13 | * your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
18 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
19 | * details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
24 | * | ||
25 | * Send feedback to <scottm@somanetworks.com> | ||
26 | */ | ||
27 | |||
28 | #ifndef _CPCI_HOTPLUG_H | ||
29 | #define _CPCI_HOTPLUG_H | ||
30 | |||
31 | #include <linux/types.h> | ||
32 | #include <linux/pci.h> | ||
33 | |||
34 | /* PICMG 2.12 R2.0 HS CSR bits: */ | ||
35 | #define HS_CSR_INS 0x0080 | ||
36 | #define HS_CSR_EXT 0x0040 | ||
37 | #define HS_CSR_PI 0x0030 | ||
38 | #define HS_CSR_LOO 0x0008 | ||
39 | #define HS_CSR_PIE 0x0004 | ||
40 | #define HS_CSR_EIM 0x0002 | ||
41 | #define HS_CSR_DHA 0x0001 | ||
42 | |||
43 | struct slot { | ||
44 | u8 number; | ||
45 | unsigned int devfn; | ||
46 | struct pci_bus *bus; | ||
47 | struct pci_dev *dev; | ||
48 | unsigned int extracting; | ||
49 | struct hotplug_slot *hotplug_slot; | ||
50 | struct list_head slot_list; | ||
51 | }; | ||
52 | |||
53 | struct cpci_hp_controller_ops { | ||
54 | int (*query_enum) (void); | ||
55 | int (*enable_irq) (void); | ||
56 | int (*disable_irq) (void); | ||
57 | int (*check_irq) (void *dev_id); | ||
58 | int (*hardware_test) (struct slot* slot, u32 value); | ||
59 | u8 (*get_power) (struct slot* slot); | ||
60 | int (*set_power) (struct slot* slot, int value); | ||
61 | }; | ||
62 | |||
63 | struct cpci_hp_controller { | ||
64 | unsigned int irq; | ||
65 | unsigned long irq_flags; | ||
66 | char *devname; | ||
67 | void *dev_id; | ||
68 | char *name; | ||
69 | struct cpci_hp_controller_ops *ops; | ||
70 | }; | ||
71 | |||
72 | extern int cpci_hp_register_controller(struct cpci_hp_controller *controller); | ||
73 | extern int cpci_hp_unregister_controller(struct cpci_hp_controller *controller); | ||
74 | extern int cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last); | ||
75 | extern int cpci_hp_unregister_bus(struct pci_bus *bus); | ||
76 | extern int cpci_hp_start(void); | ||
77 | extern int cpci_hp_stop(void); | ||
78 | |||
79 | /* | ||
80 | * Internal function prototypes, these functions should not be used by | ||
81 | * board/chassis drivers. | ||
82 | */ | ||
83 | extern u8 cpci_get_attention_status(struct slot *slot); | ||
84 | extern u8 cpci_get_latch_status(struct slot *slot); | ||
85 | extern u8 cpci_get_adapter_status(struct slot *slot); | ||
86 | extern u16 cpci_get_hs_csr(struct slot * slot); | ||
87 | extern int cpci_set_attention_status(struct slot *slot, int status); | ||
88 | extern int cpci_check_and_clear_ins(struct slot * slot); | ||
89 | extern int cpci_check_ext(struct slot * slot); | ||
90 | extern int cpci_clear_ext(struct slot * slot); | ||
91 | extern int cpci_led_on(struct slot * slot); | ||
92 | extern int cpci_led_off(struct slot * slot); | ||
93 | extern int cpci_configure_slot(struct slot *slot); | ||
94 | extern int cpci_unconfigure_slot(struct slot *slot); | ||
95 | |||
96 | #endif /* _CPCI_HOTPLUG_H */ | ||
diff --git a/drivers/pci/hotplug/cpci_hotplug_core.c b/drivers/pci/hotplug/cpci_hotplug_core.c new file mode 100644 index 000000000000..ed243605dc7b --- /dev/null +++ b/drivers/pci/hotplug/cpci_hotplug_core.c | |||
@@ -0,0 +1,792 @@ | |||
1 | /* | ||
2 | * CompactPCI Hot Plug Driver | ||
3 | * | ||
4 | * Copyright (C) 2002 SOMA Networks, Inc. | ||
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * Copyright (C) 2001 IBM Corp. | ||
7 | * | ||
8 | * All rights reserved. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or (at | ||
13 | * your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
18 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
19 | * details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
24 | * | ||
25 | * Send feedback to <scottm@somanetworks.com> | ||
26 | */ | ||
27 | |||
28 | #include <linux/config.h> | ||
29 | #include <linux/module.h> | ||
30 | #include <linux/kernel.h> | ||
31 | #include <linux/slab.h> | ||
32 | #include <linux/pci.h> | ||
33 | #include <linux/init.h> | ||
34 | #include <linux/interrupt.h> | ||
35 | #include <linux/smp_lock.h> | ||
36 | #include <linux/delay.h> | ||
37 | #include "pci_hotplug.h" | ||
38 | #include "cpci_hotplug.h" | ||
39 | |||
40 | #define DRIVER_VERSION "0.2" | ||
41 | #define DRIVER_AUTHOR "Scott Murray <scottm@somanetworks.com>" | ||
42 | #define DRIVER_DESC "CompactPCI Hot Plug Core" | ||
43 | |||
44 | #define MY_NAME "cpci_hotplug" | ||
45 | |||
46 | #define dbg(format, arg...) \ | ||
47 | do { \ | ||
48 | if(cpci_debug) \ | ||
49 | printk (KERN_DEBUG "%s: " format "\n", \ | ||
50 | MY_NAME , ## arg); \ | ||
51 | } while(0) | ||
52 | #define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg) | ||
53 | #define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg) | ||
54 | #define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg) | ||
55 | |||
56 | /* local variables */ | ||
57 | static spinlock_t list_lock; | ||
58 | static LIST_HEAD(slot_list); | ||
59 | static int slots; | ||
60 | int cpci_debug; | ||
61 | static struct cpci_hp_controller *controller; | ||
62 | static struct semaphore event_semaphore; /* mutex for process loop (up if something to process) */ | ||
63 | static struct semaphore thread_exit; /* guard ensure thread has exited before calling it quits */ | ||
64 | static int thread_finished = 1; | ||
65 | |||
66 | static int enable_slot(struct hotplug_slot *slot); | ||
67 | static int disable_slot(struct hotplug_slot *slot); | ||
68 | static int set_attention_status(struct hotplug_slot *slot, u8 value); | ||
69 | static int get_power_status(struct hotplug_slot *slot, u8 * value); | ||
70 | static int get_attention_status(struct hotplug_slot *slot, u8 * value); | ||
71 | |||
72 | static struct hotplug_slot_ops cpci_hotplug_slot_ops = { | ||
73 | .owner = THIS_MODULE, | ||
74 | .enable_slot = enable_slot, | ||
75 | .disable_slot = disable_slot, | ||
76 | .set_attention_status = set_attention_status, | ||
77 | .get_power_status = get_power_status, | ||
78 | .get_attention_status = get_attention_status, | ||
79 | }; | ||
80 | |||
81 | static int | ||
82 | update_latch_status(struct hotplug_slot *hotplug_slot, u8 value) | ||
83 | { | ||
84 | struct hotplug_slot_info info; | ||
85 | |||
86 | memcpy(&info, hotplug_slot->info, sizeof(struct hotplug_slot_info)); | ||
87 | info.latch_status = value; | ||
88 | return pci_hp_change_slot_info(hotplug_slot, &info); | ||
89 | } | ||
90 | |||
91 | static int | ||
92 | update_adapter_status(struct hotplug_slot *hotplug_slot, u8 value) | ||
93 | { | ||
94 | struct hotplug_slot_info info; | ||
95 | |||
96 | memcpy(&info, hotplug_slot->info, sizeof(struct hotplug_slot_info)); | ||
97 | info.adapter_status = value; | ||
98 | return pci_hp_change_slot_info(hotplug_slot, &info); | ||
99 | } | ||
100 | |||
101 | static int | ||
102 | enable_slot(struct hotplug_slot *hotplug_slot) | ||
103 | { | ||
104 | struct slot *slot = hotplug_slot->private; | ||
105 | int retval = 0; | ||
106 | |||
107 | dbg("%s - physical_slot = %s", __FUNCTION__, hotplug_slot->name); | ||
108 | |||
109 | if(controller->ops->set_power) { | ||
110 | retval = controller->ops->set_power(slot, 1); | ||
111 | } | ||
112 | |||
113 | return retval; | ||
114 | } | ||
115 | |||
116 | static int | ||
117 | disable_slot(struct hotplug_slot *hotplug_slot) | ||
118 | { | ||
119 | struct slot *slot = hotplug_slot->private; | ||
120 | int retval = 0; | ||
121 | |||
122 | dbg("%s - physical_slot = %s", __FUNCTION__, hotplug_slot->name); | ||
123 | |||
124 | /* Unconfigure device */ | ||
125 | dbg("%s - unconfiguring slot %s", | ||
126 | __FUNCTION__, slot->hotplug_slot->name); | ||
127 | if((retval = cpci_unconfigure_slot(slot))) { | ||
128 | err("%s - could not unconfigure slot %s", | ||
129 | __FUNCTION__, slot->hotplug_slot->name); | ||
130 | return retval; | ||
131 | } | ||
132 | dbg("%s - finished unconfiguring slot %s", | ||
133 | __FUNCTION__, slot->hotplug_slot->name); | ||
134 | |||
135 | /* Clear EXT (by setting it) */ | ||
136 | if(cpci_clear_ext(slot)) { | ||
137 | err("%s - could not clear EXT for slot %s", | ||
138 | __FUNCTION__, slot->hotplug_slot->name); | ||
139 | retval = -ENODEV; | ||
140 | } | ||
141 | cpci_led_on(slot); | ||
142 | |||
143 | if(controller->ops->set_power) { | ||
144 | retval = controller->ops->set_power(slot, 0); | ||
145 | } | ||
146 | |||
147 | if(update_adapter_status(slot->hotplug_slot, 0)) { | ||
148 | warn("failure to update adapter file"); | ||
149 | } | ||
150 | |||
151 | slot->extracting = 0; | ||
152 | |||
153 | return retval; | ||
154 | } | ||
155 | |||
156 | static u8 | ||
157 | cpci_get_power_status(struct slot *slot) | ||
158 | { | ||
159 | u8 power = 1; | ||
160 | |||
161 | if(controller->ops->get_power) { | ||
162 | power = controller->ops->get_power(slot); | ||
163 | } | ||
164 | return power; | ||
165 | } | ||
166 | |||
167 | static int | ||
168 | get_power_status(struct hotplug_slot *hotplug_slot, u8 * value) | ||
169 | { | ||
170 | struct slot *slot = hotplug_slot->private; | ||
171 | |||
172 | *value = cpci_get_power_status(slot); | ||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | static int | ||
177 | get_attention_status(struct hotplug_slot *hotplug_slot, u8 * value) | ||
178 | { | ||
179 | struct slot *slot = hotplug_slot->private; | ||
180 | |||
181 | *value = cpci_get_attention_status(slot); | ||
182 | return 0; | ||
183 | } | ||
184 | |||
185 | static int | ||
186 | set_attention_status(struct hotplug_slot *hotplug_slot, u8 status) | ||
187 | { | ||
188 | return cpci_set_attention_status(hotplug_slot->private, status); | ||
189 | } | ||
190 | |||
191 | static void release_slot(struct hotplug_slot *hotplug_slot) | ||
192 | { | ||
193 | struct slot *slot = hotplug_slot->private; | ||
194 | |||
195 | kfree(slot->hotplug_slot->info); | ||
196 | kfree(slot->hotplug_slot->name); | ||
197 | kfree(slot->hotplug_slot); | ||
198 | kfree(slot); | ||
199 | } | ||
200 | |||
201 | #define SLOT_NAME_SIZE 6 | ||
202 | static void | ||
203 | make_slot_name(struct slot *slot) | ||
204 | { | ||
205 | snprintf(slot->hotplug_slot->name, | ||
206 | SLOT_NAME_SIZE, "%02x:%02x", slot->bus->number, slot->number); | ||
207 | } | ||
208 | |||
209 | int | ||
210 | cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last) | ||
211 | { | ||
212 | struct slot *slot; | ||
213 | struct hotplug_slot *hotplug_slot; | ||
214 | struct hotplug_slot_info *info; | ||
215 | char *name; | ||
216 | int status = -ENOMEM; | ||
217 | int i; | ||
218 | |||
219 | if(!(controller && bus)) { | ||
220 | return -ENODEV; | ||
221 | } | ||
222 | |||
223 | /* | ||
224 | * Create a structure for each slot, and register that slot | ||
225 | * with the pci_hotplug subsystem. | ||
226 | */ | ||
227 | for (i = first; i <= last; ++i) { | ||
228 | slot = kmalloc(sizeof (struct slot), GFP_KERNEL); | ||
229 | if (!slot) | ||
230 | goto error; | ||
231 | memset(slot, 0, sizeof (struct slot)); | ||
232 | |||
233 | hotplug_slot = | ||
234 | kmalloc(sizeof (struct hotplug_slot), GFP_KERNEL); | ||
235 | if (!hotplug_slot) | ||
236 | goto error_slot; | ||
237 | memset(hotplug_slot, 0, sizeof (struct hotplug_slot)); | ||
238 | slot->hotplug_slot = hotplug_slot; | ||
239 | |||
240 | info = kmalloc(sizeof (struct hotplug_slot_info), GFP_KERNEL); | ||
241 | if (!info) | ||
242 | goto error_hpslot; | ||
243 | memset(info, 0, sizeof (struct hotplug_slot_info)); | ||
244 | hotplug_slot->info = info; | ||
245 | |||
246 | name = kmalloc(SLOT_NAME_SIZE, GFP_KERNEL); | ||
247 | if (!name) | ||
248 | goto error_info; | ||
249 | hotplug_slot->name = name; | ||
250 | |||
251 | slot->bus = bus; | ||
252 | slot->number = i; | ||
253 | slot->devfn = PCI_DEVFN(i, 0); | ||
254 | |||
255 | hotplug_slot->private = slot; | ||
256 | hotplug_slot->release = &release_slot; | ||
257 | make_slot_name(slot); | ||
258 | hotplug_slot->ops = &cpci_hotplug_slot_ops; | ||
259 | |||
260 | /* | ||
261 | * Initialize the slot info structure with some known | ||
262 | * good values. | ||
263 | */ | ||
264 | dbg("initializing slot %s", slot->hotplug_slot->name); | ||
265 | info->power_status = cpci_get_power_status(slot); | ||
266 | info->attention_status = cpci_get_attention_status(slot); | ||
267 | |||
268 | dbg("registering slot %s", slot->hotplug_slot->name); | ||
269 | status = pci_hp_register(slot->hotplug_slot); | ||
270 | if (status) { | ||
271 | err("pci_hp_register failed with error %d", status); | ||
272 | goto error_name; | ||
273 | } | ||
274 | |||
275 | /* Add slot to our internal list */ | ||
276 | spin_lock(&list_lock); | ||
277 | list_add(&slot->slot_list, &slot_list); | ||
278 | slots++; | ||
279 | spin_unlock(&list_lock); | ||
280 | } | ||
281 | return 0; | ||
282 | error_name: | ||
283 | kfree(name); | ||
284 | error_info: | ||
285 | kfree(info); | ||
286 | error_hpslot: | ||
287 | kfree(hotplug_slot); | ||
288 | error_slot: | ||
289 | kfree(slot); | ||
290 | error: | ||
291 | return status; | ||
292 | } | ||
293 | |||
294 | int | ||
295 | cpci_hp_unregister_bus(struct pci_bus *bus) | ||
296 | { | ||
297 | struct slot *slot; | ||
298 | struct list_head *tmp; | ||
299 | struct list_head *next; | ||
300 | int status; | ||
301 | |||
302 | spin_lock(&list_lock); | ||
303 | if(!slots) { | ||
304 | spin_unlock(&list_lock); | ||
305 | return -1; | ||
306 | } | ||
307 | list_for_each_safe(tmp, next, &slot_list) { | ||
308 | slot = list_entry(tmp, struct slot, slot_list); | ||
309 | if(slot->bus == bus) { | ||
310 | dbg("deregistering slot %s", slot->hotplug_slot->name); | ||
311 | status = pci_hp_deregister(slot->hotplug_slot); | ||
312 | if(status) { | ||
313 | err("pci_hp_deregister failed with error %d", | ||
314 | status); | ||
315 | return status; | ||
316 | } | ||
317 | |||
318 | list_del(&slot->slot_list); | ||
319 | slots--; | ||
320 | } | ||
321 | } | ||
322 | spin_unlock(&list_lock); | ||
323 | return 0; | ||
324 | } | ||
325 | |||
326 | /* This is the interrupt mode interrupt handler */ | ||
327 | static irqreturn_t | ||
328 | cpci_hp_intr(int irq, void *data, struct pt_regs *regs) | ||
329 | { | ||
330 | dbg("entered cpci_hp_intr"); | ||
331 | |||
332 | /* Check to see if it was our interrupt */ | ||
333 | if((controller->irq_flags & SA_SHIRQ) && | ||
334 | !controller->ops->check_irq(controller->dev_id)) { | ||
335 | dbg("exited cpci_hp_intr, not our interrupt"); | ||
336 | return IRQ_NONE; | ||
337 | } | ||
338 | |||
339 | /* Disable ENUM interrupt */ | ||
340 | controller->ops->disable_irq(); | ||
341 | |||
342 | /* Trigger processing by the event thread */ | ||
343 | dbg("Signal event_semaphore"); | ||
344 | up(&event_semaphore); | ||
345 | dbg("exited cpci_hp_intr"); | ||
346 | return IRQ_HANDLED; | ||
347 | } | ||
348 | |||
349 | /* | ||
350 | * According to PICMG 2.12 R2.0, section 6.3.2, upon | ||
351 | * initialization, the system driver shall clear the | ||
352 | * INS bits of the cold-inserted devices. | ||
353 | */ | ||
354 | static int | ||
355 | init_slots(void) | ||
356 | { | ||
357 | struct slot *slot; | ||
358 | struct list_head *tmp; | ||
359 | struct pci_dev* dev; | ||
360 | |||
361 | dbg("%s - enter", __FUNCTION__); | ||
362 | spin_lock(&list_lock); | ||
363 | if(!slots) { | ||
364 | spin_unlock(&list_lock); | ||
365 | return -1; | ||
366 | } | ||
367 | list_for_each(tmp, &slot_list) { | ||
368 | slot = list_entry(tmp, struct slot, slot_list); | ||
369 | dbg("%s - looking at slot %s", | ||
370 | __FUNCTION__, slot->hotplug_slot->name); | ||
371 | if(cpci_check_and_clear_ins(slot)) { | ||
372 | dbg("%s - cleared INS for slot %s", | ||
373 | __FUNCTION__, slot->hotplug_slot->name); | ||
374 | dev = pci_find_slot(slot->bus->number, PCI_DEVFN(slot->number, 0)); | ||
375 | if(dev) { | ||
376 | if(update_adapter_status(slot->hotplug_slot, 1)) { | ||
377 | warn("failure to update adapter file"); | ||
378 | } | ||
379 | if(update_latch_status(slot->hotplug_slot, 1)) { | ||
380 | warn("failure to update latch file"); | ||
381 | } | ||
382 | slot->dev = dev; | ||
383 | } else { | ||
384 | err("%s - no driver attached to device in slot %s", | ||
385 | __FUNCTION__, slot->hotplug_slot->name); | ||
386 | } | ||
387 | } | ||
388 | } | ||
389 | spin_unlock(&list_lock); | ||
390 | dbg("%s - exit", __FUNCTION__); | ||
391 | return 0; | ||
392 | } | ||
393 | |||
394 | static int | ||
395 | check_slots(void) | ||
396 | { | ||
397 | struct slot *slot; | ||
398 | struct list_head *tmp; | ||
399 | int extracted; | ||
400 | int inserted; | ||
401 | |||
402 | spin_lock(&list_lock); | ||
403 | if(!slots) { | ||
404 | spin_unlock(&list_lock); | ||
405 | err("no slots registered, shutting down"); | ||
406 | return -1; | ||
407 | } | ||
408 | extracted = inserted = 0; | ||
409 | list_for_each(tmp, &slot_list) { | ||
410 | slot = list_entry(tmp, struct slot, slot_list); | ||
411 | dbg("%s - looking at slot %s", | ||
412 | __FUNCTION__, slot->hotplug_slot->name); | ||
413 | if(cpci_check_and_clear_ins(slot)) { | ||
414 | u16 hs_csr; | ||
415 | |||
416 | /* Some broken hardware (e.g. PLX 9054AB) asserts ENUM# twice... */ | ||
417 | if(slot->dev) { | ||
418 | warn("slot %s already inserted", slot->hotplug_slot->name); | ||
419 | inserted++; | ||
420 | continue; | ||
421 | } | ||
422 | |||
423 | /* Process insertion */ | ||
424 | dbg("%s - slot %s inserted", | ||
425 | __FUNCTION__, slot->hotplug_slot->name); | ||
426 | |||
427 | /* GSM, debug */ | ||
428 | hs_csr = cpci_get_hs_csr(slot); | ||
429 | dbg("%s - slot %s HS_CSR (1) = %04x", | ||
430 | __FUNCTION__, slot->hotplug_slot->name, hs_csr); | ||
431 | |||
432 | /* Configure device */ | ||
433 | dbg("%s - configuring slot %s", | ||
434 | __FUNCTION__, slot->hotplug_slot->name); | ||
435 | if(cpci_configure_slot(slot)) { | ||
436 | err("%s - could not configure slot %s", | ||
437 | __FUNCTION__, slot->hotplug_slot->name); | ||
438 | continue; | ||
439 | } | ||
440 | dbg("%s - finished configuring slot %s", | ||
441 | __FUNCTION__, slot->hotplug_slot->name); | ||
442 | |||
443 | /* GSM, debug */ | ||
444 | hs_csr = cpci_get_hs_csr(slot); | ||
445 | dbg("%s - slot %s HS_CSR (2) = %04x", | ||
446 | __FUNCTION__, slot->hotplug_slot->name, hs_csr); | ||
447 | |||
448 | if(update_latch_status(slot->hotplug_slot, 1)) { | ||
449 | warn("failure to update latch file"); | ||
450 | } | ||
451 | |||
452 | if(update_adapter_status(slot->hotplug_slot, 1)) { | ||
453 | warn("failure to update adapter file"); | ||
454 | } | ||
455 | |||
456 | cpci_led_off(slot); | ||
457 | |||
458 | /* GSM, debug */ | ||
459 | hs_csr = cpci_get_hs_csr(slot); | ||
460 | dbg("%s - slot %s HS_CSR (3) = %04x", | ||
461 | __FUNCTION__, slot->hotplug_slot->name, hs_csr); | ||
462 | |||
463 | inserted++; | ||
464 | } else if(cpci_check_ext(slot)) { | ||
465 | u16 hs_csr; | ||
466 | |||
467 | /* Process extraction request */ | ||
468 | dbg("%s - slot %s extracted", | ||
469 | __FUNCTION__, slot->hotplug_slot->name); | ||
470 | |||
471 | /* GSM, debug */ | ||
472 | hs_csr = cpci_get_hs_csr(slot); | ||
473 | dbg("%s - slot %s HS_CSR = %04x", | ||
474 | __FUNCTION__, slot->hotplug_slot->name, hs_csr); | ||
475 | |||
476 | if(!slot->extracting) { | ||
477 | if(update_latch_status(slot->hotplug_slot, 0)) { | ||
478 | warn("failure to update latch file"); | ||
479 | } | ||
480 | slot->extracting = 1; | ||
481 | } | ||
482 | extracted++; | ||
483 | } | ||
484 | } | ||
485 | spin_unlock(&list_lock); | ||
486 | if(inserted || extracted) { | ||
487 | return extracted; | ||
488 | } | ||
489 | else { | ||
490 | err("cannot find ENUM# source, shutting down"); | ||
491 | return -1; | ||
492 | } | ||
493 | } | ||
494 | |||
495 | /* This is the interrupt mode worker thread body */ | ||
496 | static int | ||
497 | event_thread(void *data) | ||
498 | { | ||
499 | int rc; | ||
500 | struct slot *slot; | ||
501 | struct list_head *tmp; | ||
502 | |||
503 | lock_kernel(); | ||
504 | daemonize("cpci_hp_eventd"); | ||
505 | unlock_kernel(); | ||
506 | |||
507 | dbg("%s - event thread started", __FUNCTION__); | ||
508 | while(1) { | ||
509 | dbg("event thread sleeping"); | ||
510 | down_interruptible(&event_semaphore); | ||
511 | dbg("event thread woken, thread_finished = %d", | ||
512 | thread_finished); | ||
513 | if(thread_finished || signal_pending(current)) | ||
514 | break; | ||
515 | while(controller->ops->query_enum()) { | ||
516 | rc = check_slots(); | ||
517 | if (rc > 0) | ||
518 | /* Give userspace a chance to handle extraction */ | ||
519 | msleep(500); | ||
520 | else if (rc < 0) { | ||
521 | dbg("%s - error checking slots", __FUNCTION__); | ||
522 | thread_finished = 1; | ||
523 | break; | ||
524 | } | ||
525 | } | ||
526 | /* Check for someone yanking out a board */ | ||
527 | list_for_each(tmp, &slot_list) { | ||
528 | slot = list_entry(tmp, struct slot, slot_list); | ||
529 | if(slot->extracting) { | ||
530 | /* | ||
531 | * Hmmm, we're likely hosed at this point, should we | ||
532 | * bother trying to tell the driver or not? | ||
533 | */ | ||
534 | err("card in slot %s was improperly removed", | ||
535 | slot->hotplug_slot->name); | ||
536 | if(update_adapter_status(slot->hotplug_slot, 0)) { | ||
537 | warn("failure to update adapter file"); | ||
538 | } | ||
539 | slot->extracting = 0; | ||
540 | } | ||
541 | } | ||
542 | |||
543 | /* Re-enable ENUM# interrupt */ | ||
544 | dbg("%s - re-enabling irq", __FUNCTION__); | ||
545 | controller->ops->enable_irq(); | ||
546 | } | ||
547 | |||
548 | dbg("%s - event thread signals exit", __FUNCTION__); | ||
549 | up(&thread_exit); | ||
550 | return 0; | ||
551 | } | ||
552 | |||
553 | /* This is the polling mode worker thread body */ | ||
554 | static int | ||
555 | poll_thread(void *data) | ||
556 | { | ||
557 | int rc; | ||
558 | struct slot *slot; | ||
559 | struct list_head *tmp; | ||
560 | |||
561 | lock_kernel(); | ||
562 | daemonize("cpci_hp_polld"); | ||
563 | unlock_kernel(); | ||
564 | |||
565 | while(1) { | ||
566 | if(thread_finished || signal_pending(current)) | ||
567 | break; | ||
568 | |||
569 | while(controller->ops->query_enum()) { | ||
570 | rc = check_slots(); | ||
571 | if(rc > 0) | ||
572 | /* Give userspace a chance to handle extraction */ | ||
573 | msleep(500); | ||
574 | else if (rc < 0) { | ||
575 | dbg("%s - error checking slots", __FUNCTION__); | ||
576 | thread_finished = 1; | ||
577 | break; | ||
578 | } | ||
579 | } | ||
580 | /* Check for someone yanking out a board */ | ||
581 | list_for_each(tmp, &slot_list) { | ||
582 | slot = list_entry(tmp, struct slot, slot_list); | ||
583 | if(slot->extracting) { | ||
584 | /* | ||
585 | * Hmmm, we're likely hosed at this point, should we | ||
586 | * bother trying to tell the driver or not? | ||
587 | */ | ||
588 | err("card in slot %s was improperly removed", | ||
589 | slot->hotplug_slot->name); | ||
590 | if(update_adapter_status(slot->hotplug_slot, 0)) { | ||
591 | warn("failure to update adapter file"); | ||
592 | } | ||
593 | slot->extracting = 0; | ||
594 | } | ||
595 | } | ||
596 | |||
597 | msleep(100); | ||
598 | } | ||
599 | dbg("poll thread signals exit"); | ||
600 | up(&thread_exit); | ||
601 | return 0; | ||
602 | } | ||
603 | |||
604 | static int | ||
605 | cpci_start_thread(void) | ||
606 | { | ||
607 | int pid; | ||
608 | |||
609 | /* initialize our semaphores */ | ||
610 | init_MUTEX_LOCKED(&event_semaphore); | ||
611 | init_MUTEX_LOCKED(&thread_exit); | ||
612 | thread_finished = 0; | ||
613 | |||
614 | if(controller->irq) { | ||
615 | pid = kernel_thread(event_thread, NULL, 0); | ||
616 | } else { | ||
617 | pid = kernel_thread(poll_thread, NULL, 0); | ||
618 | } | ||
619 | if(pid < 0) { | ||
620 | err("Can't start up our thread"); | ||
621 | return -1; | ||
622 | } | ||
623 | dbg("Our thread pid = %d", pid); | ||
624 | return 0; | ||
625 | } | ||
626 | |||
627 | static void | ||
628 | cpci_stop_thread(void) | ||
629 | { | ||
630 | thread_finished = 1; | ||
631 | dbg("thread finish command given"); | ||
632 | if(controller->irq) { | ||
633 | up(&event_semaphore); | ||
634 | } | ||
635 | dbg("wait for thread to exit"); | ||
636 | down(&thread_exit); | ||
637 | } | ||
638 | |||
639 | int | ||
640 | cpci_hp_register_controller(struct cpci_hp_controller *new_controller) | ||
641 | { | ||
642 | int status = 0; | ||
643 | |||
644 | if(!controller) { | ||
645 | controller = new_controller; | ||
646 | if(controller->irq) { | ||
647 | if(request_irq(controller->irq, | ||
648 | cpci_hp_intr, | ||
649 | controller->irq_flags, | ||
650 | MY_NAME, controller->dev_id)) { | ||
651 | err("Can't get irq %d for the hotplug cPCI controller", controller->irq); | ||
652 | status = -ENODEV; | ||
653 | } | ||
654 | dbg("%s - acquired controller irq %d", __FUNCTION__, | ||
655 | controller->irq); | ||
656 | } | ||
657 | } else { | ||
658 | err("cPCI hotplug controller already registered"); | ||
659 | status = -1; | ||
660 | } | ||
661 | return status; | ||
662 | } | ||
663 | |||
664 | int | ||
665 | cpci_hp_unregister_controller(struct cpci_hp_controller *old_controller) | ||
666 | { | ||
667 | int status = 0; | ||
668 | |||
669 | if(controller) { | ||
670 | if(!thread_finished) { | ||
671 | cpci_stop_thread(); | ||
672 | } | ||
673 | if(controller->irq) { | ||
674 | free_irq(controller->irq, controller->dev_id); | ||
675 | } | ||
676 | controller = NULL; | ||
677 | } else { | ||
678 | status = -ENODEV; | ||
679 | } | ||
680 | return status; | ||
681 | } | ||
682 | |||
683 | int | ||
684 | cpci_hp_start(void) | ||
685 | { | ||
686 | static int first = 1; | ||
687 | int status; | ||
688 | |||
689 | dbg("%s - enter", __FUNCTION__); | ||
690 | if(!controller) { | ||
691 | return -ENODEV; | ||
692 | } | ||
693 | |||
694 | spin_lock(&list_lock); | ||
695 | if(!slots) { | ||
696 | spin_unlock(&list_lock); | ||
697 | return -ENODEV; | ||
698 | } | ||
699 | spin_unlock(&list_lock); | ||
700 | |||
701 | if(first) { | ||
702 | status = init_slots(); | ||
703 | if(status) { | ||
704 | return status; | ||
705 | } | ||
706 | first = 0; | ||
707 | } | ||
708 | |||
709 | status = cpci_start_thread(); | ||
710 | if(status) { | ||
711 | return status; | ||
712 | } | ||
713 | dbg("%s - thread started", __FUNCTION__); | ||
714 | |||
715 | if(controller->irq) { | ||
716 | /* Start enum interrupt processing */ | ||
717 | dbg("%s - enabling irq", __FUNCTION__); | ||
718 | controller->ops->enable_irq(); | ||
719 | } | ||
720 | dbg("%s - exit", __FUNCTION__); | ||
721 | return 0; | ||
722 | } | ||
723 | |||
724 | int | ||
725 | cpci_hp_stop(void) | ||
726 | { | ||
727 | if(!controller) { | ||
728 | return -ENODEV; | ||
729 | } | ||
730 | |||
731 | if(controller->irq) { | ||
732 | /* Stop enum interrupt processing */ | ||
733 | dbg("%s - disabling irq", __FUNCTION__); | ||
734 | controller->ops->disable_irq(); | ||
735 | } | ||
736 | cpci_stop_thread(); | ||
737 | return 0; | ||
738 | } | ||
739 | |||
740 | static void __exit | ||
741 | cleanup_slots(void) | ||
742 | { | ||
743 | struct list_head *tmp; | ||
744 | struct slot *slot; | ||
745 | |||
746 | /* | ||
747 | * Unregister all of our slots with the pci_hotplug subsystem, | ||
748 | * and free up all memory that we had allocated. | ||
749 | */ | ||
750 | spin_lock(&list_lock); | ||
751 | if(!slots) { | ||
752 | goto null_cleanup; | ||
753 | } | ||
754 | list_for_each(tmp, &slot_list) { | ||
755 | slot = list_entry(tmp, struct slot, slot_list); | ||
756 | list_del(&slot->slot_list); | ||
757 | pci_hp_deregister(slot->hotplug_slot); | ||
758 | kfree(slot->hotplug_slot->info); | ||
759 | kfree(slot->hotplug_slot->name); | ||
760 | kfree(slot->hotplug_slot); | ||
761 | kfree(slot); | ||
762 | } | ||
763 | null_cleanup: | ||
764 | spin_unlock(&list_lock); | ||
765 | return; | ||
766 | } | ||
767 | |||
768 | int __init | ||
769 | cpci_hotplug_init(int debug) | ||
770 | { | ||
771 | spin_lock_init(&list_lock); | ||
772 | cpci_debug = debug; | ||
773 | |||
774 | info(DRIVER_DESC " version: " DRIVER_VERSION); | ||
775 | return 0; | ||
776 | } | ||
777 | |||
778 | void __exit | ||
779 | cpci_hotplug_exit(void) | ||
780 | { | ||
781 | /* | ||
782 | * Clean everything up. | ||
783 | */ | ||
784 | cleanup_slots(); | ||
785 | } | ||
786 | |||
787 | EXPORT_SYMBOL_GPL(cpci_hp_register_controller); | ||
788 | EXPORT_SYMBOL_GPL(cpci_hp_unregister_controller); | ||
789 | EXPORT_SYMBOL_GPL(cpci_hp_register_bus); | ||
790 | EXPORT_SYMBOL_GPL(cpci_hp_unregister_bus); | ||
791 | EXPORT_SYMBOL_GPL(cpci_hp_start); | ||
792 | EXPORT_SYMBOL_GPL(cpci_hp_stop); | ||
diff --git a/drivers/pci/hotplug/cpci_hotplug_pci.c b/drivers/pci/hotplug/cpci_hotplug_pci.c new file mode 100644 index 000000000000..2e969616f298 --- /dev/null +++ b/drivers/pci/hotplug/cpci_hotplug_pci.c | |||
@@ -0,0 +1,661 @@ | |||
1 | /* | ||
2 | * CompactPCI Hot Plug Driver PCI functions | ||
3 | * | ||
4 | * Copyright (C) 2002 by SOMA Networks, Inc. | ||
5 | * | ||
6 | * All rights reserved. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or (at | ||
11 | * your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, but | ||
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
16 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
17 | * details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
22 | * | ||
23 | * Send feedback to <scottm@somanetworks.com> | ||
24 | */ | ||
25 | |||
26 | #include <linux/config.h> | ||
27 | #include <linux/module.h> | ||
28 | #include <linux/kernel.h> | ||
29 | #include <linux/pci.h> | ||
30 | #include <linux/proc_fs.h> | ||
31 | #include "../pci.h" | ||
32 | #include "pci_hotplug.h" | ||
33 | #include "cpci_hotplug.h" | ||
34 | |||
35 | #if !defined(MODULE) | ||
36 | #define MY_NAME "cpci_hotplug" | ||
37 | #else | ||
38 | #define MY_NAME THIS_MODULE->name | ||
39 | #endif | ||
40 | |||
41 | extern int cpci_debug; | ||
42 | |||
43 | #define dbg(format, arg...) \ | ||
44 | do { \ | ||
45 | if(cpci_debug) \ | ||
46 | printk (KERN_DEBUG "%s: " format "\n", \ | ||
47 | MY_NAME , ## arg); \ | ||
48 | } while(0) | ||
49 | #define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg) | ||
50 | #define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg) | ||
51 | #define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg) | ||
52 | |||
53 | #define ROUND_UP(x, a) (((x) + (a) - 1) & ~((a) - 1)) | ||
54 | |||
55 | |||
56 | u8 cpci_get_attention_status(struct slot* slot) | ||
57 | { | ||
58 | int hs_cap; | ||
59 | u16 hs_csr; | ||
60 | |||
61 | hs_cap = pci_bus_find_capability(slot->bus, | ||
62 | slot->devfn, | ||
63 | PCI_CAP_ID_CHSWP); | ||
64 | if(!hs_cap) { | ||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | if(pci_bus_read_config_word(slot->bus, | ||
69 | slot->devfn, | ||
70 | hs_cap + 2, | ||
71 | &hs_csr)) { | ||
72 | return 0; | ||
73 | } | ||
74 | return hs_csr & 0x0008 ? 1 : 0; | ||
75 | } | ||
76 | |||
77 | int cpci_set_attention_status(struct slot* slot, int status) | ||
78 | { | ||
79 | int hs_cap; | ||
80 | u16 hs_csr; | ||
81 | |||
82 | hs_cap = pci_bus_find_capability(slot->bus, | ||
83 | slot->devfn, | ||
84 | PCI_CAP_ID_CHSWP); | ||
85 | if(!hs_cap) { | ||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | if(pci_bus_read_config_word(slot->bus, | ||
90 | slot->devfn, | ||
91 | hs_cap + 2, | ||
92 | &hs_csr)) { | ||
93 | return 0; | ||
94 | } | ||
95 | if(status) { | ||
96 | hs_csr |= HS_CSR_LOO; | ||
97 | } else { | ||
98 | hs_csr &= ~HS_CSR_LOO; | ||
99 | } | ||
100 | if(pci_bus_write_config_word(slot->bus, | ||
101 | slot->devfn, | ||
102 | hs_cap + 2, | ||
103 | hs_csr)) { | ||
104 | return 0; | ||
105 | } | ||
106 | return 1; | ||
107 | } | ||
108 | |||
109 | u16 cpci_get_hs_csr(struct slot* slot) | ||
110 | { | ||
111 | int hs_cap; | ||
112 | u16 hs_csr; | ||
113 | |||
114 | hs_cap = pci_bus_find_capability(slot->bus, | ||
115 | slot->devfn, | ||
116 | PCI_CAP_ID_CHSWP); | ||
117 | if(!hs_cap) { | ||
118 | return 0xFFFF; | ||
119 | } | ||
120 | |||
121 | if(pci_bus_read_config_word(slot->bus, | ||
122 | slot->devfn, | ||
123 | hs_cap + 2, | ||
124 | &hs_csr)) { | ||
125 | return 0xFFFF; | ||
126 | } | ||
127 | return hs_csr; | ||
128 | } | ||
129 | |||
130 | #if 0 | ||
131 | u16 cpci_set_hs_csr(struct slot* slot, u16 hs_csr) | ||
132 | { | ||
133 | int hs_cap; | ||
134 | u16 new_hs_csr; | ||
135 | |||
136 | hs_cap = pci_bus_find_capability(slot->bus, | ||
137 | slot->devfn, | ||
138 | PCI_CAP_ID_CHSWP); | ||
139 | if(!hs_cap) { | ||
140 | return 0xFFFF; | ||
141 | } | ||
142 | |||
143 | /* Write out the new value */ | ||
144 | if(pci_bus_write_config_word(slot->bus, | ||
145 | slot->devfn, | ||
146 | hs_cap + 2, | ||
147 | hs_csr)) { | ||
148 | return 0xFFFF; | ||
149 | } | ||
150 | |||
151 | /* Read back what we just wrote out */ | ||
152 | if(pci_bus_read_config_word(slot->bus, | ||
153 | slot->devfn, | ||
154 | hs_cap + 2, | ||
155 | &new_hs_csr)) { | ||
156 | return 0xFFFF; | ||
157 | } | ||
158 | return new_hs_csr; | ||
159 | } | ||
160 | #endif | ||
161 | |||
162 | int cpci_check_and_clear_ins(struct slot* slot) | ||
163 | { | ||
164 | int hs_cap; | ||
165 | u16 hs_csr; | ||
166 | int ins = 0; | ||
167 | |||
168 | hs_cap = pci_bus_find_capability(slot->bus, | ||
169 | slot->devfn, | ||
170 | PCI_CAP_ID_CHSWP); | ||
171 | if(!hs_cap) { | ||
172 | return 0; | ||
173 | } | ||
174 | if(pci_bus_read_config_word(slot->bus, | ||
175 | slot->devfn, | ||
176 | hs_cap + 2, | ||
177 | &hs_csr)) { | ||
178 | return 0; | ||
179 | } | ||
180 | if(hs_csr & HS_CSR_INS) { | ||
181 | /* Clear INS (by setting it) */ | ||
182 | if(pci_bus_write_config_word(slot->bus, | ||
183 | slot->devfn, | ||
184 | hs_cap + 2, | ||
185 | hs_csr)) { | ||
186 | ins = 0; | ||
187 | } | ||
188 | ins = 1; | ||
189 | } | ||
190 | return ins; | ||
191 | } | ||
192 | |||
193 | int cpci_check_ext(struct slot* slot) | ||
194 | { | ||
195 | int hs_cap; | ||
196 | u16 hs_csr; | ||
197 | int ext = 0; | ||
198 | |||
199 | hs_cap = pci_bus_find_capability(slot->bus, | ||
200 | slot->devfn, | ||
201 | PCI_CAP_ID_CHSWP); | ||
202 | if(!hs_cap) { | ||
203 | return 0; | ||
204 | } | ||
205 | if(pci_bus_read_config_word(slot->bus, | ||
206 | slot->devfn, | ||
207 | hs_cap + 2, | ||
208 | &hs_csr)) { | ||
209 | return 0; | ||
210 | } | ||
211 | if(hs_csr & HS_CSR_EXT) { | ||
212 | ext = 1; | ||
213 | } | ||
214 | return ext; | ||
215 | } | ||
216 | |||
217 | int cpci_clear_ext(struct slot* slot) | ||
218 | { | ||
219 | int hs_cap; | ||
220 | u16 hs_csr; | ||
221 | |||
222 | hs_cap = pci_bus_find_capability(slot->bus, | ||
223 | slot->devfn, | ||
224 | PCI_CAP_ID_CHSWP); | ||
225 | if(!hs_cap) { | ||
226 | return -ENODEV; | ||
227 | } | ||
228 | if(pci_bus_read_config_word(slot->bus, | ||
229 | slot->devfn, | ||
230 | hs_cap + 2, | ||
231 | &hs_csr)) { | ||
232 | return -ENODEV; | ||
233 | } | ||
234 | if(hs_csr & HS_CSR_EXT) { | ||
235 | /* Clear EXT (by setting it) */ | ||
236 | if(pci_bus_write_config_word(slot->bus, | ||
237 | slot->devfn, | ||
238 | hs_cap + 2, | ||
239 | hs_csr)) { | ||
240 | return -ENODEV; | ||
241 | } | ||
242 | } | ||
243 | return 0; | ||
244 | } | ||
245 | |||
246 | int cpci_led_on(struct slot* slot) | ||
247 | { | ||
248 | int hs_cap; | ||
249 | u16 hs_csr; | ||
250 | |||
251 | hs_cap = pci_bus_find_capability(slot->bus, | ||
252 | slot->devfn, | ||
253 | PCI_CAP_ID_CHSWP); | ||
254 | if(!hs_cap) { | ||
255 | return -ENODEV; | ||
256 | } | ||
257 | if(pci_bus_read_config_word(slot->bus, | ||
258 | slot->devfn, | ||
259 | hs_cap + 2, | ||
260 | &hs_csr)) { | ||
261 | return -ENODEV; | ||
262 | } | ||
263 | if((hs_csr & HS_CSR_LOO) != HS_CSR_LOO) { | ||
264 | /* Set LOO */ | ||
265 | hs_csr |= HS_CSR_LOO; | ||
266 | if(pci_bus_write_config_word(slot->bus, | ||
267 | slot->devfn, | ||
268 | hs_cap + 2, | ||
269 | hs_csr)) { | ||
270 | err("Could not set LOO for slot %s", | ||
271 | slot->hotplug_slot->name); | ||
272 | return -ENODEV; | ||
273 | } | ||
274 | } | ||
275 | return 0; | ||
276 | } | ||
277 | |||
278 | int cpci_led_off(struct slot* slot) | ||
279 | { | ||
280 | int hs_cap; | ||
281 | u16 hs_csr; | ||
282 | |||
283 | hs_cap = pci_bus_find_capability(slot->bus, | ||
284 | slot->devfn, | ||
285 | PCI_CAP_ID_CHSWP); | ||
286 | if(!hs_cap) { | ||
287 | return -ENODEV; | ||
288 | } | ||
289 | if(pci_bus_read_config_word(slot->bus, | ||
290 | slot->devfn, | ||
291 | hs_cap + 2, | ||
292 | &hs_csr)) { | ||
293 | return -ENODEV; | ||
294 | } | ||
295 | if(hs_csr & HS_CSR_LOO) { | ||
296 | /* Clear LOO */ | ||
297 | hs_csr &= ~HS_CSR_LOO; | ||
298 | if(pci_bus_write_config_word(slot->bus, | ||
299 | slot->devfn, | ||
300 | hs_cap + 2, | ||
301 | hs_csr)) { | ||
302 | err("Could not clear LOO for slot %s", | ||
303 | slot->hotplug_slot->name); | ||
304 | return -ENODEV; | ||
305 | } | ||
306 | } | ||
307 | return 0; | ||
308 | } | ||
309 | |||
310 | |||
311 | /* | ||
312 | * Device configuration functions | ||
313 | */ | ||
314 | |||
315 | static int cpci_configure_dev(struct pci_bus *bus, struct pci_dev *dev) | ||
316 | { | ||
317 | u8 irq_pin; | ||
318 | int r; | ||
319 | |||
320 | dbg("%s - enter", __FUNCTION__); | ||
321 | |||
322 | /* NOTE: device already setup from prior scan */ | ||
323 | |||
324 | /* FIXME: How would we know if we need to enable the expansion ROM? */ | ||
325 | pci_write_config_word(dev, PCI_ROM_ADDRESS, 0x00L); | ||
326 | |||
327 | /* Assign resources */ | ||
328 | dbg("assigning resources for %02x:%02x.%x", | ||
329 | dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); | ||
330 | for (r = 0; r < 6; r++) { | ||
331 | struct resource *res = dev->resource + r; | ||
332 | if(res->flags) | ||
333 | pci_assign_resource(dev, r); | ||
334 | } | ||
335 | dbg("finished assigning resources for %02x:%02x.%x", | ||
336 | dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); | ||
337 | |||
338 | /* Does this function have an interrupt at all? */ | ||
339 | dbg("checking for function interrupt"); | ||
340 | pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &irq_pin); | ||
341 | if(irq_pin) { | ||
342 | dbg("function uses interrupt pin %d", irq_pin); | ||
343 | } | ||
344 | |||
345 | /* | ||
346 | * Need to explicitly set irq field to 0 so that it'll get assigned | ||
347 | * by the pcibios platform dependent code called by pci_enable_device. | ||
348 | */ | ||
349 | dev->irq = 0; | ||
350 | |||
351 | dbg("enabling device"); | ||
352 | pci_enable_device(dev); /* XXX check return */ | ||
353 | dbg("now dev->irq = %d", dev->irq); | ||
354 | if(irq_pin && dev->irq) { | ||
355 | pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); | ||
356 | } | ||
357 | |||
358 | /* Can't use pci_insert_device at the moment, do it manually for now */ | ||
359 | pci_proc_attach_device(dev); | ||
360 | dbg("notifying drivers"); | ||
361 | //pci_announce_device_to_drivers(dev); | ||
362 | dbg("%s - exit", __FUNCTION__); | ||
363 | return 0; | ||
364 | } | ||
365 | |||
366 | static int cpci_configure_bridge(struct pci_bus* bus, struct pci_dev* dev) | ||
367 | { | ||
368 | int rc; | ||
369 | struct pci_bus* child; | ||
370 | struct resource* r; | ||
371 | u8 max, n; | ||
372 | u16 command; | ||
373 | |||
374 | dbg("%s - enter", __FUNCTION__); | ||
375 | |||
376 | /* Do basic bridge initialization */ | ||
377 | rc = pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x40); | ||
378 | if(rc) { | ||
379 | printk(KERN_ERR "%s - write of PCI_LATENCY_TIMER failed\n", __FUNCTION__); | ||
380 | } | ||
381 | rc = pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER, 0x40); | ||
382 | if(rc) { | ||
383 | printk(KERN_ERR "%s - write of PCI_SEC_LATENCY_TIMER failed\n", __FUNCTION__); | ||
384 | } | ||
385 | rc = pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, L1_CACHE_BYTES / 4); | ||
386 | if(rc) { | ||
387 | printk(KERN_ERR "%s - write of PCI_CACHE_LINE_SIZE failed\n", __FUNCTION__); | ||
388 | } | ||
389 | |||
390 | /* | ||
391 | * Set parent bridge's subordinate field so that configuration space | ||
392 | * access will work in pci_scan_bridge and friends. | ||
393 | */ | ||
394 | max = pci_max_busnr(); | ||
395 | bus->subordinate = max + 1; | ||
396 | pci_write_config_byte(bus->self, PCI_SUBORDINATE_BUS, max + 1); | ||
397 | |||
398 | /* Scan behind bridge */ | ||
399 | n = pci_scan_bridge(bus, dev, max, 2); | ||
400 | child = pci_find_bus(0, max + 1); | ||
401 | if (!child) | ||
402 | return -ENODEV; | ||
403 | pci_proc_attach_bus(child); | ||
404 | |||
405 | /* | ||
406 | * Update parent bridge's subordinate field if there were more bridges | ||
407 | * behind the bridge that was scanned. | ||
408 | */ | ||
409 | if(n > max) { | ||
410 | bus->subordinate = n; | ||
411 | pci_write_config_byte(bus->self, PCI_SUBORDINATE_BUS, n); | ||
412 | } | ||
413 | |||
414 | /* | ||
415 | * Update the bridge resources of the bridge to accommodate devices | ||
416 | * behind it. | ||
417 | */ | ||
418 | pci_bus_size_bridges(child); | ||
419 | pci_bus_assign_resources(child); | ||
420 | |||
421 | /* Enable resource mapping via command register */ | ||
422 | command = PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY | PCI_COMMAND_SERR; | ||
423 | r = child->resource[0]; | ||
424 | if(r && r->start) { | ||
425 | command |= PCI_COMMAND_IO; | ||
426 | } | ||
427 | r = child->resource[1]; | ||
428 | if(r && r->start) { | ||
429 | command |= PCI_COMMAND_MEMORY; | ||
430 | } | ||
431 | r = child->resource[2]; | ||
432 | if(r && r->start) { | ||
433 | command |= PCI_COMMAND_MEMORY; | ||
434 | } | ||
435 | rc = pci_write_config_word(dev, PCI_COMMAND, command); | ||
436 | if(rc) { | ||
437 | err("Error setting command register"); | ||
438 | return rc; | ||
439 | } | ||
440 | |||
441 | /* Set bridge control register */ | ||
442 | command = PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR | PCI_BRIDGE_CTL_NO_ISA; | ||
443 | rc = pci_write_config_word(dev, PCI_BRIDGE_CONTROL, command); | ||
444 | if(rc) { | ||
445 | err("Error setting bridge control register"); | ||
446 | return rc; | ||
447 | } | ||
448 | dbg("%s - exit", __FUNCTION__); | ||
449 | return 0; | ||
450 | } | ||
451 | |||
452 | static int configure_visit_pci_dev(struct pci_dev_wrapped *wrapped_dev, | ||
453 | struct pci_bus_wrapped *wrapped_bus) | ||
454 | { | ||
455 | int rc; | ||
456 | struct pci_dev *dev = wrapped_dev->dev; | ||
457 | struct pci_bus *bus = wrapped_bus->bus; | ||
458 | struct slot* slot; | ||
459 | |||
460 | dbg("%s - enter", __FUNCTION__); | ||
461 | |||
462 | /* | ||
463 | * We need to fix up the hotplug representation with the Linux | ||
464 | * representation. | ||
465 | */ | ||
466 | if(wrapped_dev->data) { | ||
467 | slot = (struct slot*) wrapped_dev->data; | ||
468 | slot->dev = dev; | ||
469 | } | ||
470 | |||
471 | /* If it's a bridge, scan behind it for devices */ | ||
472 | if(dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { | ||
473 | rc = cpci_configure_bridge(bus, dev); | ||
474 | if(rc) | ||
475 | return rc; | ||
476 | } | ||
477 | |||
478 | /* Actually configure device */ | ||
479 | if(dev) { | ||
480 | rc = cpci_configure_dev(bus, dev); | ||
481 | if(rc) | ||
482 | return rc; | ||
483 | } | ||
484 | dbg("%s - exit", __FUNCTION__); | ||
485 | return 0; | ||
486 | } | ||
487 | |||
488 | static int unconfigure_visit_pci_dev_phase2(struct pci_dev_wrapped *wrapped_dev, | ||
489 | struct pci_bus_wrapped *wrapped_bus) | ||
490 | { | ||
491 | struct pci_dev *dev = wrapped_dev->dev; | ||
492 | struct slot* slot; | ||
493 | |||
494 | dbg("%s - enter", __FUNCTION__); | ||
495 | if(!dev) | ||
496 | return -ENODEV; | ||
497 | |||
498 | /* Remove the Linux representation */ | ||
499 | if(pci_remove_device_safe(dev)) { | ||
500 | err("Could not remove device\n"); | ||
501 | return -1; | ||
502 | } | ||
503 | |||
504 | /* | ||
505 | * Now remove the hotplug representation. | ||
506 | */ | ||
507 | if(wrapped_dev->data) { | ||
508 | slot = (struct slot*) wrapped_dev->data; | ||
509 | slot->dev = NULL; | ||
510 | } else { | ||
511 | dbg("No hotplug representation for %02x:%02x.%x", | ||
512 | dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); | ||
513 | } | ||
514 | dbg("%s - exit", __FUNCTION__); | ||
515 | return 0; | ||
516 | } | ||
517 | |||
518 | static int unconfigure_visit_pci_bus_phase2(struct pci_bus_wrapped *wrapped_bus, | ||
519 | struct pci_dev_wrapped *wrapped_dev) | ||
520 | { | ||
521 | struct pci_bus *bus = wrapped_bus->bus; | ||
522 | struct pci_bus *parent = bus->self->bus; | ||
523 | |||
524 | dbg("%s - enter", __FUNCTION__); | ||
525 | |||
526 | /* The cleanup code for proc entries regarding buses should be in the kernel... */ | ||
527 | if(bus->procdir) | ||
528 | dbg("detach_pci_bus %s", bus->procdir->name); | ||
529 | pci_proc_detach_bus(bus); | ||
530 | |||
531 | /* The cleanup code should live in the kernel... */ | ||
532 | bus->self->subordinate = NULL; | ||
533 | |||
534 | /* unlink from parent bus */ | ||
535 | list_del(&bus->node); | ||
536 | |||
537 | /* Now, remove */ | ||
538 | if(bus) | ||
539 | kfree(bus); | ||
540 | |||
541 | /* Update parent's subordinate field */ | ||
542 | if(parent) { | ||
543 | u8 n = pci_bus_max_busnr(parent); | ||
544 | if(n < parent->subordinate) { | ||
545 | parent->subordinate = n; | ||
546 | pci_write_config_byte(parent->self, PCI_SUBORDINATE_BUS, n); | ||
547 | } | ||
548 | } | ||
549 | dbg("%s - exit", __FUNCTION__); | ||
550 | return 0; | ||
551 | } | ||
552 | |||
553 | static struct pci_visit configure_functions = { | ||
554 | .visit_pci_dev = configure_visit_pci_dev, | ||
555 | }; | ||
556 | |||
557 | static struct pci_visit unconfigure_functions_phase2 = { | ||
558 | .post_visit_pci_bus = unconfigure_visit_pci_bus_phase2, | ||
559 | .post_visit_pci_dev = unconfigure_visit_pci_dev_phase2 | ||
560 | }; | ||
561 | |||
562 | |||
563 | int cpci_configure_slot(struct slot* slot) | ||
564 | { | ||
565 | int rc = 0; | ||
566 | |||
567 | dbg("%s - enter", __FUNCTION__); | ||
568 | |||
569 | if(slot->dev == NULL) { | ||
570 | dbg("pci_dev null, finding %02x:%02x:%x", | ||
571 | slot->bus->number, PCI_SLOT(slot->devfn), PCI_FUNC(slot->devfn)); | ||
572 | slot->dev = pci_find_slot(slot->bus->number, slot->devfn); | ||
573 | } | ||
574 | |||
575 | /* Still NULL? Well then scan for it! */ | ||
576 | if(slot->dev == NULL) { | ||
577 | int n; | ||
578 | dbg("pci_dev still null"); | ||
579 | |||
580 | /* | ||
581 | * This will generate pci_dev structures for all functions, but | ||
582 | * we will only call this case when lookup fails. | ||
583 | */ | ||
584 | n = pci_scan_slot(slot->bus, slot->devfn); | ||
585 | dbg("%s: pci_scan_slot returned %d", __FUNCTION__, n); | ||
586 | if(n > 0) | ||
587 | pci_bus_add_devices(slot->bus); | ||
588 | slot->dev = pci_find_slot(slot->bus->number, slot->devfn); | ||
589 | if(slot->dev == NULL) { | ||
590 | err("Could not find PCI device for slot %02x", slot->number); | ||
591 | return 0; | ||
592 | } | ||
593 | } | ||
594 | dbg("slot->dev = %p", slot->dev); | ||
595 | if(slot->dev) { | ||
596 | struct pci_dev *dev; | ||
597 | struct pci_dev_wrapped wrapped_dev; | ||
598 | struct pci_bus_wrapped wrapped_bus; | ||
599 | int i; | ||
600 | |||
601 | memset(&wrapped_dev, 0, sizeof (struct pci_dev_wrapped)); | ||
602 | memset(&wrapped_bus, 0, sizeof (struct pci_bus_wrapped)); | ||
603 | |||
604 | for (i = 0; i < 8; i++) { | ||
605 | dev = pci_find_slot(slot->bus->number, | ||
606 | PCI_DEVFN(PCI_SLOT(slot->dev->devfn), i)); | ||
607 | if(!dev) | ||
608 | continue; | ||
609 | wrapped_dev.dev = dev; | ||
610 | wrapped_bus.bus = slot->dev->bus; | ||
611 | if(i) | ||
612 | wrapped_dev.data = NULL; | ||
613 | else | ||
614 | wrapped_dev.data = (void*) slot; | ||
615 | rc = pci_visit_dev(&configure_functions, &wrapped_dev, &wrapped_bus); | ||
616 | } | ||
617 | } | ||
618 | |||
619 | dbg("%s - exit, rc = %d", __FUNCTION__, rc); | ||
620 | return rc; | ||
621 | } | ||
622 | |||
623 | int cpci_unconfigure_slot(struct slot* slot) | ||
624 | { | ||
625 | int rc = 0; | ||
626 | int i; | ||
627 | struct pci_dev_wrapped wrapped_dev; | ||
628 | struct pci_bus_wrapped wrapped_bus; | ||
629 | struct pci_dev *dev; | ||
630 | |||
631 | dbg("%s - enter", __FUNCTION__); | ||
632 | |||
633 | if(!slot->dev) { | ||
634 | err("No device for slot %02x\n", slot->number); | ||
635 | return -ENODEV; | ||
636 | } | ||
637 | |||
638 | memset(&wrapped_dev, 0, sizeof (struct pci_dev_wrapped)); | ||
639 | memset(&wrapped_bus, 0, sizeof (struct pci_bus_wrapped)); | ||
640 | |||
641 | for (i = 0; i < 8; i++) { | ||
642 | dev = pci_find_slot(slot->bus->number, | ||
643 | PCI_DEVFN(PCI_SLOT(slot->devfn), i)); | ||
644 | if(dev) { | ||
645 | wrapped_dev.dev = dev; | ||
646 | wrapped_bus.bus = dev->bus; | ||
647 | if(i) | ||
648 | wrapped_dev.data = NULL; | ||
649 | else | ||
650 | wrapped_dev.data = (void*) slot; | ||
651 | dbg("%s - unconfigure phase 2", __FUNCTION__); | ||
652 | rc = pci_visit_dev(&unconfigure_functions_phase2, | ||
653 | &wrapped_dev, | ||
654 | &wrapped_bus); | ||
655 | if(rc) | ||
656 | break; | ||
657 | } | ||
658 | } | ||
659 | dbg("%s - exit, rc = %d", __FUNCTION__, rc); | ||
660 | return rc; | ||
661 | } | ||
diff --git a/drivers/pci/hotplug/cpcihp_generic.c b/drivers/pci/hotplug/cpcihp_generic.c new file mode 100644 index 000000000000..a62a4345b466 --- /dev/null +++ b/drivers/pci/hotplug/cpcihp_generic.c | |||
@@ -0,0 +1,223 @@ | |||
1 | /* | ||
2 | * cpcihp_generic.c | ||
3 | * | ||
4 | * Generic port I/O CompactPCI driver | ||
5 | * | ||
6 | * Copyright 2002 SOMA Networks, Inc. | ||
7 | * Copyright 2001 Intel San Luis Obispo | ||
8 | * Copyright 2000,2001 MontaVista Software Inc. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2 of the License, or (at your | ||
13 | * option) any later version. | ||
14 | * | ||
15 | * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | ||
16 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY | ||
17 | * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL | ||
18 | * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
19 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
20 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
22 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
25 | * | ||
26 | * You should have received a copy of the GNU General Public License along | ||
27 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
28 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
29 | * | ||
30 | * This generic CompactPCI hotplug driver should allow using the PCI hotplug | ||
31 | * mechanism on any CompactPCI board that exposes the #ENUM signal as a bit | ||
32 | * in a system register that can be read through standard port I/O. | ||
33 | * | ||
34 | * Send feedback to <scottm@somanetworks.com> | ||
35 | */ | ||
36 | |||
37 | #include <linux/config.h> | ||
38 | #include <linux/module.h> | ||
39 | #include <linux/init.h> | ||
40 | #include <linux/errno.h> | ||
41 | #include <linux/pci.h> | ||
42 | #include "cpci_hotplug.h" | ||
43 | |||
44 | #define DRIVER_VERSION "0.1" | ||
45 | #define DRIVER_AUTHOR "Scott Murray <scottm@somanetworks.com>" | ||
46 | #define DRIVER_DESC "Generic port I/O CompactPCI Hot Plug Driver" | ||
47 | |||
48 | #if !defined(MODULE) | ||
49 | #define MY_NAME "cpcihp_generic" | ||
50 | #else | ||
51 | #define MY_NAME THIS_MODULE->name | ||
52 | #endif | ||
53 | |||
54 | #define dbg(format, arg...) \ | ||
55 | do { \ | ||
56 | if(debug) \ | ||
57 | printk (KERN_DEBUG "%s: " format "\n", \ | ||
58 | MY_NAME , ## arg); \ | ||
59 | } while(0) | ||
60 | #define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg) | ||
61 | #define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg) | ||
62 | #define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg) | ||
63 | |||
64 | /* local variables */ | ||
65 | static int debug; | ||
66 | static char *bridge; | ||
67 | static u8 bridge_busnr; | ||
68 | static u8 bridge_slot; | ||
69 | static struct pci_bus *bus; | ||
70 | static u8 first_slot; | ||
71 | static u8 last_slot; | ||
72 | static u16 port; | ||
73 | static unsigned int enum_bit; | ||
74 | static u8 enum_mask; | ||
75 | |||
76 | static struct cpci_hp_controller_ops generic_hpc_ops; | ||
77 | static struct cpci_hp_controller generic_hpc; | ||
78 | |||
79 | static int __init validate_parameters(void) | ||
80 | { | ||
81 | char* str; | ||
82 | char* p; | ||
83 | unsigned long tmp; | ||
84 | |||
85 | if(!bridge) { | ||
86 | info("not configured, disabling."); | ||
87 | return 1; | ||
88 | } | ||
89 | str = bridge; | ||
90 | if(!*str) | ||
91 | return -EINVAL; | ||
92 | |||
93 | tmp = simple_strtoul(str, &p, 16); | ||
94 | if(p == str || tmp > 0xff) { | ||
95 | err("Invalid hotplug bus bridge device bus number"); | ||
96 | return -EINVAL; | ||
97 | } | ||
98 | bridge_busnr = (u8) tmp; | ||
99 | dbg("bridge_busnr = 0x%02x", bridge_busnr); | ||
100 | if(*p != ':') { | ||
101 | err("Invalid hotplug bus bridge device"); | ||
102 | return -EINVAL; | ||
103 | } | ||
104 | str = p + 1; | ||
105 | tmp = simple_strtoul(str, &p, 16); | ||
106 | if(p == str || tmp > 0x1f) { | ||
107 | err("Invalid hotplug bus bridge device slot number"); | ||
108 | return -EINVAL; | ||
109 | } | ||
110 | bridge_slot = (u8) tmp; | ||
111 | dbg("bridge_slot = 0x%02x", bridge_slot); | ||
112 | |||
113 | dbg("first_slot = 0x%02x", first_slot); | ||
114 | dbg("last_slot = 0x%02x", last_slot); | ||
115 | if(!(first_slot && last_slot)) { | ||
116 | err("Need to specify first_slot and last_slot"); | ||
117 | return -EINVAL; | ||
118 | } | ||
119 | if(last_slot < first_slot) { | ||
120 | err("first_slot must be less than last_slot"); | ||
121 | return -EINVAL; | ||
122 | } | ||
123 | |||
124 | dbg("port = 0x%04x", port); | ||
125 | dbg("enum_bit = 0x%02x", enum_bit); | ||
126 | if(enum_bit > 7) { | ||
127 | err("Invalid #ENUM bit"); | ||
128 | return -EINVAL; | ||
129 | } | ||
130 | enum_mask = 1 << enum_bit; | ||
131 | return 0; | ||
132 | } | ||
133 | |||
134 | static int query_enum(void) | ||
135 | { | ||
136 | u8 value; | ||
137 | |||
138 | value = inb_p(port); | ||
139 | return ((value & enum_mask) == enum_mask); | ||
140 | } | ||
141 | |||
142 | static int __init cpcihp_generic_init(void) | ||
143 | { | ||
144 | int status; | ||
145 | struct resource* r; | ||
146 | struct pci_dev* dev; | ||
147 | |||
148 | info(DRIVER_DESC " version: " DRIVER_VERSION); | ||
149 | status = validate_parameters(); | ||
150 | if(status != 0) | ||
151 | return status; | ||
152 | |||
153 | r = request_region(port, 1, "#ENUM hotswap signal register"); | ||
154 | if(!r) | ||
155 | return -EBUSY; | ||
156 | |||
157 | dev = pci_find_slot(bridge_busnr, PCI_DEVFN(bridge_slot, 0)); | ||
158 | if(!dev || dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) { | ||
159 | err("Invalid bridge device %s", bridge); | ||
160 | return -EINVAL; | ||
161 | } | ||
162 | bus = dev->subordinate; | ||
163 | |||
164 | memset(&generic_hpc, 0, sizeof (struct cpci_hp_controller)); | ||
165 | generic_hpc_ops.query_enum = query_enum; | ||
166 | generic_hpc.ops = &generic_hpc_ops; | ||
167 | |||
168 | status = cpci_hp_register_controller(&generic_hpc); | ||
169 | if(status != 0) { | ||
170 | err("Could not register cPCI hotplug controller"); | ||
171 | return -ENODEV; | ||
172 | } | ||
173 | dbg("registered controller"); | ||
174 | |||
175 | status = cpci_hp_register_bus(bus, first_slot, last_slot); | ||
176 | if(status != 0) { | ||
177 | err("Could not register cPCI hotplug bus"); | ||
178 | goto init_bus_register_error; | ||
179 | } | ||
180 | dbg("registered bus"); | ||
181 | |||
182 | status = cpci_hp_start(); | ||
183 | if(status != 0) { | ||
184 | err("Could not started cPCI hotplug system"); | ||
185 | goto init_start_error; | ||
186 | } | ||
187 | dbg("started cpci hp system"); | ||
188 | return 0; | ||
189 | init_start_error: | ||
190 | cpci_hp_unregister_bus(bus); | ||
191 | init_bus_register_error: | ||
192 | cpci_hp_unregister_controller(&generic_hpc); | ||
193 | err("status = %d", status); | ||
194 | return status; | ||
195 | |||
196 | } | ||
197 | |||
198 | static void __exit cpcihp_generic_exit(void) | ||
199 | { | ||
200 | cpci_hp_stop(); | ||
201 | cpci_hp_unregister_bus(bus); | ||
202 | cpci_hp_unregister_controller(&generic_hpc); | ||
203 | release_region(port, 1); | ||
204 | } | ||
205 | |||
206 | module_init(cpcihp_generic_init); | ||
207 | module_exit(cpcihp_generic_exit); | ||
208 | |||
209 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
210 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
211 | MODULE_LICENSE("GPL"); | ||
212 | module_param(debug, bool, S_IRUGO | S_IWUSR); | ||
213 | MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); | ||
214 | module_param(bridge, charp, 0); | ||
215 | MODULE_PARM_DESC(bridge, "Hotswap bus bridge device, <bus>:<slot> (bus and slot are in hexadecimal)"); | ||
216 | module_param(first_slot, byte, 0); | ||
217 | MODULE_PARM_DESC(first_slot, "Hotswap bus first slot number"); | ||
218 | module_param(last_slot, byte, 0); | ||
219 | MODULE_PARM_DESC(last_slot, "Hotswap bus last slot number"); | ||
220 | module_param(port, ushort, 0); | ||
221 | MODULE_PARM_DESC(port, "#ENUM signal I/O port"); | ||
222 | module_param(enum_bit, uint, 0); | ||
223 | MODULE_PARM_DESC(enum_bit, "#ENUM signal bit (0-7)"); | ||
diff --git a/drivers/pci/hotplug/cpcihp_zt5550.c b/drivers/pci/hotplug/cpcihp_zt5550.c new file mode 100644 index 000000000000..e9928024be78 --- /dev/null +++ b/drivers/pci/hotplug/cpcihp_zt5550.c | |||
@@ -0,0 +1,305 @@ | |||
1 | /* | ||
2 | * cpcihp_zt5550.c | ||
3 | * | ||
4 | * Intel/Ziatech ZT5550 CompactPCI Host Controller driver | ||
5 | * | ||
6 | * Copyright 2002 SOMA Networks, Inc. | ||
7 | * Copyright 2001 Intel San Luis Obispo | ||
8 | * Copyright 2000,2001 MontaVista Software Inc. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2 of the License, or (at your | ||
13 | * option) any later version. | ||
14 | * | ||
15 | * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | ||
16 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY | ||
17 | * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL | ||
18 | * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
19 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
20 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
22 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
25 | * | ||
26 | * You should have received a copy of the GNU General Public License along | ||
27 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
28 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
29 | * | ||
30 | * Send feedback to <scottm@somanetworks.com> | ||
31 | */ | ||
32 | |||
33 | #include <linux/config.h> | ||
34 | #include <linux/module.h> | ||
35 | #include <linux/moduleparam.h> | ||
36 | #include <linux/init.h> | ||
37 | #include <linux/errno.h> | ||
38 | #include <linux/pci.h> | ||
39 | #include "cpci_hotplug.h" | ||
40 | #include "cpcihp_zt5550.h" | ||
41 | |||
42 | #define DRIVER_VERSION "0.2" | ||
43 | #define DRIVER_AUTHOR "Scott Murray <scottm@somanetworks.com>" | ||
44 | #define DRIVER_DESC "ZT5550 CompactPCI Hot Plug Driver" | ||
45 | |||
46 | #define MY_NAME "cpcihp_zt5550" | ||
47 | |||
48 | #define dbg(format, arg...) \ | ||
49 | do { \ | ||
50 | if(debug) \ | ||
51 | printk (KERN_DEBUG "%s: " format "\n", \ | ||
52 | MY_NAME , ## arg); \ | ||
53 | } while(0) | ||
54 | #define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg) | ||
55 | #define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg) | ||
56 | #define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg) | ||
57 | |||
58 | /* local variables */ | ||
59 | static int debug; | ||
60 | static int poll; | ||
61 | static struct cpci_hp_controller_ops zt5550_hpc_ops; | ||
62 | static struct cpci_hp_controller zt5550_hpc; | ||
63 | |||
64 | /* Primary cPCI bus bridge device */ | ||
65 | static struct pci_dev *bus0_dev; | ||
66 | static struct pci_bus *bus0; | ||
67 | |||
68 | /* Host controller device */ | ||
69 | static struct pci_dev *hc_dev; | ||
70 | |||
71 | /* Host controller register addresses */ | ||
72 | static void __iomem *hc_registers; | ||
73 | static void __iomem *csr_hc_index; | ||
74 | static void __iomem *csr_hc_data; | ||
75 | static void __iomem *csr_int_status; | ||
76 | static void __iomem *csr_int_mask; | ||
77 | |||
78 | |||
79 | static int zt5550_hc_config(struct pci_dev *pdev) | ||
80 | { | ||
81 | /* Since we know that no boards exist with two HC chips, treat it as an error */ | ||
82 | if(hc_dev) { | ||
83 | err("too many host controller devices?"); | ||
84 | return -EBUSY; | ||
85 | } | ||
86 | hc_dev = pdev; | ||
87 | dbg("hc_dev = %p", hc_dev); | ||
88 | dbg("pci resource start %lx", pci_resource_start(hc_dev, 1)); | ||
89 | dbg("pci resource len %lx", pci_resource_len(hc_dev, 1)); | ||
90 | |||
91 | if(!request_mem_region(pci_resource_start(hc_dev, 1), | ||
92 | pci_resource_len(hc_dev, 1), MY_NAME)) { | ||
93 | err("cannot reserve MMIO region"); | ||
94 | return -ENOMEM; | ||
95 | } | ||
96 | |||
97 | hc_registers = | ||
98 | ioremap(pci_resource_start(hc_dev, 1), pci_resource_len(hc_dev, 1)); | ||
99 | if(!hc_registers) { | ||
100 | err("cannot remap MMIO region %lx @ %lx", | ||
101 | pci_resource_len(hc_dev, 1), pci_resource_start(hc_dev, 1)); | ||
102 | release_mem_region(pci_resource_start(hc_dev, 1), | ||
103 | pci_resource_len(hc_dev, 1)); | ||
104 | return -ENODEV; | ||
105 | } | ||
106 | |||
107 | csr_hc_index = hc_registers + CSR_HCINDEX; | ||
108 | csr_hc_data = hc_registers + CSR_HCDATA; | ||
109 | csr_int_status = hc_registers + CSR_INTSTAT; | ||
110 | csr_int_mask = hc_registers + CSR_INTMASK; | ||
111 | |||
112 | /* | ||
113 | * Disable host control, fault and serial interrupts | ||
114 | */ | ||
115 | dbg("disabling host control, fault and serial interrupts"); | ||
116 | writeb((u8) HC_INT_MASK_REG, csr_hc_index); | ||
117 | writeb((u8) ALL_INDEXED_INTS_MASK, csr_hc_data); | ||
118 | dbg("disabled host control, fault and serial interrupts"); | ||
119 | |||
120 | /* | ||
121 | * Disable timer0, timer1 and ENUM interrupts | ||
122 | */ | ||
123 | dbg("disabling timer0, timer1 and ENUM interrupts"); | ||
124 | writeb((u8) ALL_DIRECT_INTS_MASK, csr_int_mask); | ||
125 | dbg("disabled timer0, timer1 and ENUM interrupts"); | ||
126 | return 0; | ||
127 | } | ||
128 | |||
129 | static int zt5550_hc_cleanup(void) | ||
130 | { | ||
131 | if(!hc_dev) | ||
132 | return -ENODEV; | ||
133 | |||
134 | iounmap(hc_registers); | ||
135 | release_mem_region(pci_resource_start(hc_dev, 1), | ||
136 | pci_resource_len(hc_dev, 1)); | ||
137 | return 0; | ||
138 | } | ||
139 | |||
140 | static int zt5550_hc_query_enum(void) | ||
141 | { | ||
142 | u8 value; | ||
143 | |||
144 | value = inb_p(ENUM_PORT); | ||
145 | return ((value & ENUM_MASK) == ENUM_MASK); | ||
146 | } | ||
147 | |||
148 | static int zt5550_hc_check_irq(void *dev_id) | ||
149 | { | ||
150 | int ret; | ||
151 | u8 reg; | ||
152 | |||
153 | ret = 0; | ||
154 | if(dev_id == zt5550_hpc.dev_id) { | ||
155 | reg = readb(csr_int_status); | ||
156 | if(reg) | ||
157 | ret = 1; | ||
158 | } | ||
159 | return ret; | ||
160 | } | ||
161 | |||
162 | static int zt5550_hc_enable_irq(void) | ||
163 | { | ||
164 | u8 reg; | ||
165 | |||
166 | if(hc_dev == NULL) { | ||
167 | return -ENODEV; | ||
168 | } | ||
169 | reg = readb(csr_int_mask); | ||
170 | reg = reg & ~ENUM_INT_MASK; | ||
171 | writeb(reg, csr_int_mask); | ||
172 | return 0; | ||
173 | } | ||
174 | |||
175 | static int zt5550_hc_disable_irq(void) | ||
176 | { | ||
177 | u8 reg; | ||
178 | |||
179 | if(hc_dev == NULL) { | ||
180 | return -ENODEV; | ||
181 | } | ||
182 | |||
183 | reg = readb(csr_int_mask); | ||
184 | reg = reg | ENUM_INT_MASK; | ||
185 | writeb(reg, csr_int_mask); | ||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | static int zt5550_hc_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) | ||
190 | { | ||
191 | int status; | ||
192 | |||
193 | status = zt5550_hc_config(pdev); | ||
194 | if(status != 0) { | ||
195 | return status; | ||
196 | } | ||
197 | dbg("returned from zt5550_hc_config"); | ||
198 | |||
199 | memset(&zt5550_hpc, 0, sizeof (struct cpci_hp_controller)); | ||
200 | zt5550_hpc_ops.query_enum = zt5550_hc_query_enum; | ||
201 | zt5550_hpc.ops = &zt5550_hpc_ops; | ||
202 | if(!poll) { | ||
203 | zt5550_hpc.irq = hc_dev->irq; | ||
204 | zt5550_hpc.irq_flags = SA_SHIRQ; | ||
205 | zt5550_hpc.dev_id = hc_dev; | ||
206 | |||
207 | zt5550_hpc_ops.enable_irq = zt5550_hc_enable_irq; | ||
208 | zt5550_hpc_ops.disable_irq = zt5550_hc_disable_irq; | ||
209 | zt5550_hpc_ops.check_irq = zt5550_hc_check_irq; | ||
210 | } else { | ||
211 | info("using ENUM# polling mode"); | ||
212 | } | ||
213 | |||
214 | status = cpci_hp_register_controller(&zt5550_hpc); | ||
215 | if(status != 0) { | ||
216 | err("could not register cPCI hotplug controller"); | ||
217 | goto init_hc_error; | ||
218 | } | ||
219 | dbg("registered controller"); | ||
220 | |||
221 | /* Look for first device matching cPCI bus's bridge vendor and device IDs */ | ||
222 | if(!(bus0_dev = pci_get_device(PCI_VENDOR_ID_DEC, | ||
223 | PCI_DEVICE_ID_DEC_21154, NULL))) { | ||
224 | status = -ENODEV; | ||
225 | goto init_register_error; | ||
226 | } | ||
227 | bus0 = bus0_dev->subordinate; | ||
228 | pci_dev_put(bus0_dev); | ||
229 | |||
230 | status = cpci_hp_register_bus(bus0, 0x0a, 0x0f); | ||
231 | if(status != 0) { | ||
232 | err("could not register cPCI hotplug bus"); | ||
233 | goto init_register_error; | ||
234 | } | ||
235 | dbg("registered bus"); | ||
236 | |||
237 | status = cpci_hp_start(); | ||
238 | if(status != 0) { | ||
239 | err("could not started cPCI hotplug system"); | ||
240 | cpci_hp_unregister_bus(bus0); | ||
241 | goto init_register_error; | ||
242 | } | ||
243 | dbg("started cpci hp system"); | ||
244 | |||
245 | return 0; | ||
246 | init_register_error: | ||
247 | cpci_hp_unregister_controller(&zt5550_hpc); | ||
248 | init_hc_error: | ||
249 | err("status = %d", status); | ||
250 | zt5550_hc_cleanup(); | ||
251 | return status; | ||
252 | |||
253 | } | ||
254 | |||
255 | static void __devexit zt5550_hc_remove_one(struct pci_dev *pdev) | ||
256 | { | ||
257 | cpci_hp_stop(); | ||
258 | cpci_hp_unregister_bus(bus0); | ||
259 | cpci_hp_unregister_controller(&zt5550_hpc); | ||
260 | zt5550_hc_cleanup(); | ||
261 | } | ||
262 | |||
263 | |||
264 | static struct pci_device_id zt5550_hc_pci_tbl[] = { | ||
265 | { PCI_VENDOR_ID_ZIATECH, PCI_DEVICE_ID_ZIATECH_5550_HC, PCI_ANY_ID, PCI_ANY_ID, }, | ||
266 | { 0, } | ||
267 | }; | ||
268 | MODULE_DEVICE_TABLE(pci, zt5550_hc_pci_tbl); | ||
269 | |||
270 | static struct pci_driver zt5550_hc_driver = { | ||
271 | .name = "zt5550_hc", | ||
272 | .id_table = zt5550_hc_pci_tbl, | ||
273 | .probe = zt5550_hc_init_one, | ||
274 | .remove = __devexit_p(zt5550_hc_remove_one), | ||
275 | }; | ||
276 | |||
277 | static int __init zt5550_init(void) | ||
278 | { | ||
279 | struct resource* r; | ||
280 | |||
281 | info(DRIVER_DESC " version: " DRIVER_VERSION); | ||
282 | r = request_region(ENUM_PORT, 1, "#ENUM hotswap signal register"); | ||
283 | if(!r) | ||
284 | return -EBUSY; | ||
285 | |||
286 | return pci_register_driver(&zt5550_hc_driver); | ||
287 | } | ||
288 | |||
289 | static void __exit | ||
290 | zt5550_exit(void) | ||
291 | { | ||
292 | pci_unregister_driver(&zt5550_hc_driver); | ||
293 | release_region(ENUM_PORT, 1); | ||
294 | } | ||
295 | |||
296 | module_init(zt5550_init); | ||
297 | module_exit(zt5550_exit); | ||
298 | |||
299 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
300 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
301 | MODULE_LICENSE("GPL"); | ||
302 | module_param(debug, bool, 0644); | ||
303 | MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); | ||
304 | module_param(poll, bool, 0644); | ||
305 | MODULE_PARM_DESC(poll, "#ENUM polling mode enabled or not"); | ||
diff --git a/drivers/pci/hotplug/cpcihp_zt5550.h b/drivers/pci/hotplug/cpcihp_zt5550.h new file mode 100644 index 000000000000..bebc6060a558 --- /dev/null +++ b/drivers/pci/hotplug/cpcihp_zt5550.h | |||
@@ -0,0 +1,79 @@ | |||
1 | /* | ||
2 | * cpcihp_zt5550.h | ||
3 | * | ||
4 | * Intel/Ziatech ZT5550 CompactPCI Host Controller driver definitions | ||
5 | * | ||
6 | * Copyright 2002 SOMA Networks, Inc. | ||
7 | * Copyright 2001 Intel San Luis Obispo | ||
8 | * Copyright 2000,2001 MontaVista Software Inc. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2 of the License, or (at your | ||
13 | * option) any later version. | ||
14 | * | ||
15 | * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | ||
16 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY | ||
17 | * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL | ||
18 | * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
19 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
20 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
22 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
25 | * | ||
26 | * You should have received a copy of the GNU General Public License along | ||
27 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
28 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
29 | * | ||
30 | * Send feedback to <scottm@somanetworks.com> | ||
31 | */ | ||
32 | |||
33 | #ifndef _CPCIHP_ZT5550_H | ||
34 | #define _CPCIHP_ZT5550_H | ||
35 | |||
36 | /* Direct registers */ | ||
37 | #define CSR_HCINDEX 0x00 | ||
38 | #define CSR_HCDATA 0x04 | ||
39 | #define CSR_INTSTAT 0x08 | ||
40 | #define CSR_INTMASK 0x09 | ||
41 | #define CSR_CNT0CMD 0x0C | ||
42 | #define CSR_CNT1CMD 0x0E | ||
43 | #define CSR_CNT0 0x10 | ||
44 | #define CSR_CNT1 0x14 | ||
45 | |||
46 | /* Masks for interrupt bits in CSR_INTMASK direct register */ | ||
47 | #define CNT0_INT_MASK 0x01 | ||
48 | #define CNT1_INT_MASK 0x02 | ||
49 | #define ENUM_INT_MASK 0x04 | ||
50 | #define ALL_DIRECT_INTS_MASK 0x07 | ||
51 | |||
52 | /* Indexed registers (through CSR_INDEX, CSR_DATA) */ | ||
53 | #define HC_INT_MASK_REG 0x04 | ||
54 | #define HC_STATUS_REG 0x08 | ||
55 | #define HC_CMD_REG 0x0C | ||
56 | #define ARB_CONFIG_GNT_REG 0x10 | ||
57 | #define ARB_CONFIG_CFG_REG 0x12 | ||
58 | #define ARB_CONFIG_REG 0x10 | ||
59 | #define ISOL_CONFIG_REG 0x18 | ||
60 | #define FAULT_STATUS_REG 0x20 | ||
61 | #define FAULT_CONFIG_REG 0x24 | ||
62 | #define WD_CONFIG_REG 0x2C | ||
63 | #define HC_DIAG_REG 0x30 | ||
64 | #define SERIAL_COMM_REG 0x34 | ||
65 | #define SERIAL_OUT_REG 0x38 | ||
66 | #define SERIAL_IN_REG 0x3C | ||
67 | |||
68 | /* Masks for interrupt bits in HC_INT_MASK_REG indexed register */ | ||
69 | #define SERIAL_INT_MASK 0x01 | ||
70 | #define FAULT_INT_MASK 0x02 | ||
71 | #define HCF_INT_MASK 0x04 | ||
72 | #define ALL_INDEXED_INTS_MASK 0x07 | ||
73 | |||
74 | /* Digital I/O port storing ENUM# */ | ||
75 | #define ENUM_PORT 0xE1 | ||
76 | /* Mask to get to the ENUM# bit on the bus */ | ||
77 | #define ENUM_MASK 0x40 | ||
78 | |||
79 | #endif /* _CPCIHP_ZT5550_H */ | ||
diff --git a/drivers/pci/hotplug/cpqphp.h b/drivers/pci/hotplug/cpqphp.h new file mode 100644 index 000000000000..092491e25ef2 --- /dev/null +++ b/drivers/pci/hotplug/cpqphp.h | |||
@@ -0,0 +1,721 @@ | |||
1 | /* | ||
2 | * Compaq Hot Plug Controller Driver | ||
3 | * | ||
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | ||
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * Copyright (C) 2001 IBM | ||
7 | * | ||
8 | * All rights reserved. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or (at | ||
13 | * your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
18 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
19 | * details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
24 | * | ||
25 | * Send feedback to <greg@kroah.com> | ||
26 | * | ||
27 | */ | ||
28 | #ifndef _CPQPHP_H | ||
29 | #define _CPQPHP_H | ||
30 | |||
31 | #include "pci_hotplug.h" | ||
32 | #include <linux/interrupt.h> | ||
33 | #include <asm/io.h> /* for read? and write? functions */ | ||
34 | #include <linux/delay.h> /* for delays */ | ||
35 | |||
36 | #define MY_NAME "cpqphp" | ||
37 | |||
38 | #define dbg(fmt, arg...) do { if (cpqhp_debug) printk(KERN_DEBUG "%s: " fmt , MY_NAME , ## arg); } while (0) | ||
39 | #define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg) | ||
40 | #define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg) | ||
41 | #define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg) | ||
42 | |||
43 | |||
44 | |||
45 | struct smbios_system_slot { | ||
46 | u8 type; | ||
47 | u8 length; | ||
48 | u16 handle; | ||
49 | u8 name_string_num; | ||
50 | u8 slot_type; | ||
51 | u8 slot_width; | ||
52 | u8 slot_current_usage; | ||
53 | u8 slot_length; | ||
54 | u16 slot_number; | ||
55 | u8 properties1; | ||
56 | u8 properties2; | ||
57 | } __attribute__ ((packed)); | ||
58 | |||
59 | /* offsets to the smbios generic type based on the above structure layout */ | ||
60 | enum smbios_system_slot_offsets { | ||
61 | SMBIOS_SLOT_GENERIC_TYPE = offsetof(struct smbios_system_slot, type), | ||
62 | SMBIOS_SLOT_GENERIC_LENGTH = offsetof(struct smbios_system_slot, length), | ||
63 | SMBIOS_SLOT_GENERIC_HANDLE = offsetof(struct smbios_system_slot, handle), | ||
64 | SMBIOS_SLOT_NAME_STRING_NUM = offsetof(struct smbios_system_slot, name_string_num), | ||
65 | SMBIOS_SLOT_TYPE = offsetof(struct smbios_system_slot, slot_type), | ||
66 | SMBIOS_SLOT_WIDTH = offsetof(struct smbios_system_slot, slot_width), | ||
67 | SMBIOS_SLOT_CURRENT_USAGE = offsetof(struct smbios_system_slot, slot_current_usage), | ||
68 | SMBIOS_SLOT_LENGTH = offsetof(struct smbios_system_slot, slot_length), | ||
69 | SMBIOS_SLOT_NUMBER = offsetof(struct smbios_system_slot, slot_number), | ||
70 | SMBIOS_SLOT_PROPERTIES1 = offsetof(struct smbios_system_slot, properties1), | ||
71 | SMBIOS_SLOT_PROPERTIES2 = offsetof(struct smbios_system_slot, properties2), | ||
72 | }; | ||
73 | |||
74 | struct smbios_generic { | ||
75 | u8 type; | ||
76 | u8 length; | ||
77 | u16 handle; | ||
78 | } __attribute__ ((packed)); | ||
79 | |||
80 | /* offsets to the smbios generic type based on the above structure layout */ | ||
81 | enum smbios_generic_offsets { | ||
82 | SMBIOS_GENERIC_TYPE = offsetof(struct smbios_generic, type), | ||
83 | SMBIOS_GENERIC_LENGTH = offsetof(struct smbios_generic, length), | ||
84 | SMBIOS_GENERIC_HANDLE = offsetof(struct smbios_generic, handle), | ||
85 | }; | ||
86 | |||
87 | struct smbios_entry_point { | ||
88 | char anchor[4]; | ||
89 | u8 ep_checksum; | ||
90 | u8 ep_length; | ||
91 | u8 major_version; | ||
92 | u8 minor_version; | ||
93 | u16 max_size_entry; | ||
94 | u8 ep_rev; | ||
95 | u8 reserved[5]; | ||
96 | char int_anchor[5]; | ||
97 | u8 int_checksum; | ||
98 | u16 st_length; | ||
99 | u32 st_address; | ||
100 | u16 number_of_entrys; | ||
101 | u8 bcd_rev; | ||
102 | } __attribute__ ((packed)); | ||
103 | |||
104 | /* offsets to the smbios entry point based on the above structure layout */ | ||
105 | enum smbios_entry_point_offsets { | ||
106 | ANCHOR = offsetof(struct smbios_entry_point, anchor[0]), | ||
107 | EP_CHECKSUM = offsetof(struct smbios_entry_point, ep_checksum), | ||
108 | EP_LENGTH = offsetof(struct smbios_entry_point, ep_length), | ||
109 | MAJOR_VERSION = offsetof(struct smbios_entry_point, major_version), | ||
110 | MINOR_VERSION = offsetof(struct smbios_entry_point, minor_version), | ||
111 | MAX_SIZE_ENTRY = offsetof(struct smbios_entry_point, max_size_entry), | ||
112 | EP_REV = offsetof(struct smbios_entry_point, ep_rev), | ||
113 | INT_ANCHOR = offsetof(struct smbios_entry_point, int_anchor[0]), | ||
114 | INT_CHECKSUM = offsetof(struct smbios_entry_point, int_checksum), | ||
115 | ST_LENGTH = offsetof(struct smbios_entry_point, st_length), | ||
116 | ST_ADDRESS = offsetof(struct smbios_entry_point, st_address), | ||
117 | NUMBER_OF_ENTRYS = offsetof(struct smbios_entry_point, number_of_entrys), | ||
118 | BCD_REV = offsetof(struct smbios_entry_point, bcd_rev), | ||
119 | }; | ||
120 | |||
121 | struct ctrl_reg { /* offset */ | ||
122 | u8 slot_RST; /* 0x00 */ | ||
123 | u8 slot_enable; /* 0x01 */ | ||
124 | u16 misc; /* 0x02 */ | ||
125 | u32 led_control; /* 0x04 */ | ||
126 | u32 int_input_clear; /* 0x08 */ | ||
127 | u32 int_mask; /* 0x0a */ | ||
128 | u8 reserved0; /* 0x10 */ | ||
129 | u8 reserved1; /* 0x11 */ | ||
130 | u8 reserved2; /* 0x12 */ | ||
131 | u8 gen_output_AB; /* 0x13 */ | ||
132 | u32 non_int_input; /* 0x14 */ | ||
133 | u32 reserved3; /* 0x18 */ | ||
134 | u32 reserved4; /* 0x1a */ | ||
135 | u32 reserved5; /* 0x20 */ | ||
136 | u8 reserved6; /* 0x24 */ | ||
137 | u8 reserved7; /* 0x25 */ | ||
138 | u16 reserved8; /* 0x26 */ | ||
139 | u8 slot_mask; /* 0x28 */ | ||
140 | u8 reserved9; /* 0x29 */ | ||
141 | u8 reserved10; /* 0x2a */ | ||
142 | u8 reserved11; /* 0x2b */ | ||
143 | u8 slot_SERR; /* 0x2c */ | ||
144 | u8 slot_power; /* 0x2d */ | ||
145 | u8 reserved12; /* 0x2e */ | ||
146 | u8 reserved13; /* 0x2f */ | ||
147 | u8 next_curr_freq; /* 0x30 */ | ||
148 | u8 reset_freq_mode; /* 0x31 */ | ||
149 | } __attribute__ ((packed)); | ||
150 | |||
151 | /* offsets to the controller registers based on the above structure layout */ | ||
152 | enum ctrl_offsets { | ||
153 | SLOT_RST = offsetof(struct ctrl_reg, slot_RST), | ||
154 | SLOT_ENABLE = offsetof(struct ctrl_reg, slot_enable), | ||
155 | MISC = offsetof(struct ctrl_reg, misc), | ||
156 | LED_CONTROL = offsetof(struct ctrl_reg, led_control), | ||
157 | INT_INPUT_CLEAR = offsetof(struct ctrl_reg, int_input_clear), | ||
158 | INT_MASK = offsetof(struct ctrl_reg, int_mask), | ||
159 | CTRL_RESERVED0 = offsetof(struct ctrl_reg, reserved0), | ||
160 | CTRL_RESERVED1 = offsetof(struct ctrl_reg, reserved1), | ||
161 | CTRL_RESERVED2 = offsetof(struct ctrl_reg, reserved1), | ||
162 | GEN_OUTPUT_AB = offsetof(struct ctrl_reg, gen_output_AB), | ||
163 | NON_INT_INPUT = offsetof(struct ctrl_reg, non_int_input), | ||
164 | CTRL_RESERVED3 = offsetof(struct ctrl_reg, reserved3), | ||
165 | CTRL_RESERVED4 = offsetof(struct ctrl_reg, reserved4), | ||
166 | CTRL_RESERVED5 = offsetof(struct ctrl_reg, reserved5), | ||
167 | CTRL_RESERVED6 = offsetof(struct ctrl_reg, reserved6), | ||
168 | CTRL_RESERVED7 = offsetof(struct ctrl_reg, reserved7), | ||
169 | CTRL_RESERVED8 = offsetof(struct ctrl_reg, reserved8), | ||
170 | SLOT_MASK = offsetof(struct ctrl_reg, slot_mask), | ||
171 | CTRL_RESERVED9 = offsetof(struct ctrl_reg, reserved9), | ||
172 | CTRL_RESERVED10 = offsetof(struct ctrl_reg, reserved10), | ||
173 | CTRL_RESERVED11 = offsetof(struct ctrl_reg, reserved11), | ||
174 | SLOT_SERR = offsetof(struct ctrl_reg, slot_SERR), | ||
175 | SLOT_POWER = offsetof(struct ctrl_reg, slot_power), | ||
176 | NEXT_CURR_FREQ = offsetof(struct ctrl_reg, next_curr_freq), | ||
177 | RESET_FREQ_MODE = offsetof(struct ctrl_reg, reset_freq_mode), | ||
178 | }; | ||
179 | |||
180 | struct hrt { | ||
181 | char sig0; | ||
182 | char sig1; | ||
183 | char sig2; | ||
184 | char sig3; | ||
185 | u16 unused_IRQ; | ||
186 | u16 PCIIRQ; | ||
187 | u8 number_of_entries; | ||
188 | u8 revision; | ||
189 | u16 reserved1; | ||
190 | u32 reserved2; | ||
191 | } __attribute__ ((packed)); | ||
192 | |||
193 | /* offsets to the hotplug resource table registers based on the above structure layout */ | ||
194 | enum hrt_offsets { | ||
195 | SIG0 = offsetof(struct hrt, sig0), | ||
196 | SIG1 = offsetof(struct hrt, sig1), | ||
197 | SIG2 = offsetof(struct hrt, sig2), | ||
198 | SIG3 = offsetof(struct hrt, sig3), | ||
199 | UNUSED_IRQ = offsetof(struct hrt, unused_IRQ), | ||
200 | PCIIRQ = offsetof(struct hrt, PCIIRQ), | ||
201 | NUMBER_OF_ENTRIES = offsetof(struct hrt, number_of_entries), | ||
202 | REVISION = offsetof(struct hrt, revision), | ||
203 | HRT_RESERVED1 = offsetof(struct hrt, reserved1), | ||
204 | HRT_RESERVED2 = offsetof(struct hrt, reserved2), | ||
205 | }; | ||
206 | |||
207 | struct slot_rt { | ||
208 | u8 dev_func; | ||
209 | u8 primary_bus; | ||
210 | u8 secondary_bus; | ||
211 | u8 max_bus; | ||
212 | u16 io_base; | ||
213 | u16 io_length; | ||
214 | u16 mem_base; | ||
215 | u16 mem_length; | ||
216 | u16 pre_mem_base; | ||
217 | u16 pre_mem_length; | ||
218 | } __attribute__ ((packed)); | ||
219 | |||
220 | /* offsets to the hotplug slot resource table registers based on the above structure layout */ | ||
221 | enum slot_rt_offsets { | ||
222 | DEV_FUNC = offsetof(struct slot_rt, dev_func), | ||
223 | PRIMARY_BUS = offsetof(struct slot_rt, primary_bus), | ||
224 | SECONDARY_BUS = offsetof(struct slot_rt, secondary_bus), | ||
225 | MAX_BUS = offsetof(struct slot_rt, max_bus), | ||
226 | IO_BASE = offsetof(struct slot_rt, io_base), | ||
227 | IO_LENGTH = offsetof(struct slot_rt, io_length), | ||
228 | MEM_BASE = offsetof(struct slot_rt, mem_base), | ||
229 | MEM_LENGTH = offsetof(struct slot_rt, mem_length), | ||
230 | PRE_MEM_BASE = offsetof(struct slot_rt, pre_mem_base), | ||
231 | PRE_MEM_LENGTH = offsetof(struct slot_rt, pre_mem_length), | ||
232 | }; | ||
233 | |||
234 | struct pci_func { | ||
235 | struct pci_func *next; | ||
236 | u8 bus; | ||
237 | u8 device; | ||
238 | u8 function; | ||
239 | u8 is_a_board; | ||
240 | u16 status; | ||
241 | u8 configured; | ||
242 | u8 switch_save; | ||
243 | u8 presence_save; | ||
244 | u32 base_length[0x06]; | ||
245 | u8 base_type[0x06]; | ||
246 | u16 reserved2; | ||
247 | u32 config_space[0x20]; | ||
248 | struct pci_resource *mem_head; | ||
249 | struct pci_resource *p_mem_head; | ||
250 | struct pci_resource *io_head; | ||
251 | struct pci_resource *bus_head; | ||
252 | struct timer_list *p_task_event; | ||
253 | struct pci_dev* pci_dev; | ||
254 | }; | ||
255 | |||
256 | struct slot { | ||
257 | struct slot *next; | ||
258 | u8 bus; | ||
259 | u8 device; | ||
260 | u8 number; | ||
261 | u8 is_a_board; | ||
262 | u8 configured; | ||
263 | u8 state; | ||
264 | u8 switch_save; | ||
265 | u8 presence_save; | ||
266 | u32 capabilities; | ||
267 | u16 reserved2; | ||
268 | struct timer_list task_event; | ||
269 | u8 hp_slot; | ||
270 | struct controller *ctrl; | ||
271 | void __iomem *p_sm_slot; | ||
272 | struct hotplug_slot *hotplug_slot; | ||
273 | }; | ||
274 | |||
275 | struct pci_resource { | ||
276 | struct pci_resource * next; | ||
277 | u32 base; | ||
278 | u32 length; | ||
279 | }; | ||
280 | |||
281 | struct event_info { | ||
282 | u32 event_type; | ||
283 | u8 hp_slot; | ||
284 | }; | ||
285 | |||
286 | struct controller { | ||
287 | struct controller *next; | ||
288 | u32 ctrl_int_comp; | ||
289 | struct semaphore crit_sect; /* critical section semaphore */ | ||
290 | void __iomem *hpc_reg; /* cookie for our pci controller location */ | ||
291 | struct pci_resource *mem_head; | ||
292 | struct pci_resource *p_mem_head; | ||
293 | struct pci_resource *io_head; | ||
294 | struct pci_resource *bus_head; | ||
295 | struct pci_dev *pci_dev; | ||
296 | struct pci_bus *pci_bus; | ||
297 | struct event_info event_queue[10]; | ||
298 | struct slot *slot; | ||
299 | u8 next_event; | ||
300 | u8 interrupt; | ||
301 | u8 cfgspc_irq; | ||
302 | u8 bus; /* bus number for the pci hotplug controller */ | ||
303 | u8 rev; | ||
304 | u8 slot_device_offset; | ||
305 | u8 first_slot; | ||
306 | u8 add_support; | ||
307 | u8 push_flag; | ||
308 | enum pci_bus_speed speed; | ||
309 | enum pci_bus_speed speed_capability; | ||
310 | u8 push_button; /* 0 = no pushbutton, 1 = pushbutton present */ | ||
311 | u8 slot_switch_type; /* 0 = no switch, 1 = switch present */ | ||
312 | u8 defeature_PHP; /* 0 = PHP not supported, 1 = PHP supported */ | ||
313 | u8 alternate_base_address; /* 0 = not supported, 1 = supported */ | ||
314 | u8 pci_config_space; /* Index/data access to working registers 0 = not supported, 1 = supported */ | ||
315 | u8 pcix_speed_capability; /* PCI-X */ | ||
316 | u8 pcix_support; /* PCI-X */ | ||
317 | u16 vendor_id; | ||
318 | struct work_struct int_task_event; | ||
319 | wait_queue_head_t queue; /* sleep & wake process */ | ||
320 | }; | ||
321 | |||
322 | struct irq_mapping { | ||
323 | u8 barber_pole; | ||
324 | u8 valid_INT; | ||
325 | u8 interrupt[4]; | ||
326 | }; | ||
327 | |||
328 | struct resource_lists { | ||
329 | struct pci_resource *mem_head; | ||
330 | struct pci_resource *p_mem_head; | ||
331 | struct pci_resource *io_head; | ||
332 | struct pci_resource *bus_head; | ||
333 | struct irq_mapping *irqs; | ||
334 | }; | ||
335 | |||
336 | #define ROM_PHY_ADDR 0x0F0000 | ||
337 | #define ROM_PHY_LEN 0x00ffff | ||
338 | |||
339 | #define PCI_HPC_ID 0xA0F7 | ||
340 | #define PCI_SUB_HPC_ID 0xA2F7 | ||
341 | #define PCI_SUB_HPC_ID2 0xA2F8 | ||
342 | #define PCI_SUB_HPC_ID3 0xA2F9 | ||
343 | #define PCI_SUB_HPC_ID_INTC 0xA2FA | ||
344 | #define PCI_SUB_HPC_ID4 0xA2FD | ||
345 | |||
346 | #define INT_BUTTON_IGNORE 0 | ||
347 | #define INT_PRESENCE_ON 1 | ||
348 | #define INT_PRESENCE_OFF 2 | ||
349 | #define INT_SWITCH_CLOSE 3 | ||
350 | #define INT_SWITCH_OPEN 4 | ||
351 | #define INT_POWER_FAULT 5 | ||
352 | #define INT_POWER_FAULT_CLEAR 6 | ||
353 | #define INT_BUTTON_PRESS 7 | ||
354 | #define INT_BUTTON_RELEASE 8 | ||
355 | #define INT_BUTTON_CANCEL 9 | ||
356 | |||
357 | #define STATIC_STATE 0 | ||
358 | #define BLINKINGON_STATE 1 | ||
359 | #define BLINKINGOFF_STATE 2 | ||
360 | #define POWERON_STATE 3 | ||
361 | #define POWEROFF_STATE 4 | ||
362 | |||
363 | #define PCISLOT_INTERLOCK_CLOSED 0x00000001 | ||
364 | #define PCISLOT_ADAPTER_PRESENT 0x00000002 | ||
365 | #define PCISLOT_POWERED 0x00000004 | ||
366 | #define PCISLOT_66_MHZ_OPERATION 0x00000008 | ||
367 | #define PCISLOT_64_BIT_OPERATION 0x00000010 | ||
368 | #define PCISLOT_REPLACE_SUPPORTED 0x00000020 | ||
369 | #define PCISLOT_ADD_SUPPORTED 0x00000040 | ||
370 | #define PCISLOT_INTERLOCK_SUPPORTED 0x00000080 | ||
371 | #define PCISLOT_66_MHZ_SUPPORTED 0x00000100 | ||
372 | #define PCISLOT_64_BIT_SUPPORTED 0x00000200 | ||
373 | |||
374 | #define PCI_TO_PCI_BRIDGE_CLASS 0x00060400 | ||
375 | |||
376 | #define INTERLOCK_OPEN 0x00000002 | ||
377 | #define ADD_NOT_SUPPORTED 0x00000003 | ||
378 | #define CARD_FUNCTIONING 0x00000005 | ||
379 | #define ADAPTER_NOT_SAME 0x00000006 | ||
380 | #define NO_ADAPTER_PRESENT 0x00000009 | ||
381 | #define NOT_ENOUGH_RESOURCES 0x0000000B | ||
382 | #define DEVICE_TYPE_NOT_SUPPORTED 0x0000000C | ||
383 | #define POWER_FAILURE 0x0000000E | ||
384 | |||
385 | #define REMOVE_NOT_SUPPORTED 0x00000003 | ||
386 | |||
387 | |||
388 | /* | ||
389 | * error Messages | ||
390 | */ | ||
391 | #define msg_initialization_err "Initialization failure, error=%d\n" | ||
392 | #define msg_HPC_rev_error "Unsupported revision of the PCI hot plug controller found.\n" | ||
393 | #define msg_HPC_non_compaq_or_intel "The PCI hot plug controller is not supported by this driver.\n" | ||
394 | #define msg_HPC_not_supported "this system is not supported by this version of cpqphpd. Upgrade to a newer version of cpqphpd\n" | ||
395 | #define msg_unable_to_save "unable to store PCI hot plug add resource information. This system must be rebooted before adding any PCI devices.\n" | ||
396 | #define msg_button_on "PCI slot #%d - powering on due to button press.\n" | ||
397 | #define msg_button_off "PCI slot #%d - powering off due to button press.\n" | ||
398 | #define msg_button_cancel "PCI slot #%d - action canceled due to button press.\n" | ||
399 | #define msg_button_ignore "PCI slot #%d - button press ignored. (action in progress...)\n" | ||
400 | |||
401 | |||
402 | /* sysfs functions for the hotplug controller info */ | ||
403 | extern void cpqhp_create_ctrl_files (struct controller *ctrl); | ||
404 | |||
405 | /* controller functions */ | ||
406 | extern void cpqhp_pushbutton_thread (unsigned long event_pointer); | ||
407 | extern irqreturn_t cpqhp_ctrl_intr (int IRQ, void *data, struct pt_regs *regs); | ||
408 | extern int cpqhp_find_available_resources (struct controller *ctrl, void __iomem *rom_start); | ||
409 | extern int cpqhp_event_start_thread (void); | ||
410 | extern void cpqhp_event_stop_thread (void); | ||
411 | extern struct pci_func *cpqhp_slot_create (unsigned char busnumber); | ||
412 | extern struct pci_func *cpqhp_slot_find (unsigned char bus, unsigned char device, unsigned char index); | ||
413 | extern int cpqhp_process_SI (struct controller *ctrl, struct pci_func *func); | ||
414 | extern int cpqhp_process_SS (struct controller *ctrl, struct pci_func *func); | ||
415 | extern int cpqhp_hardware_test (struct controller *ctrl, int test_num); | ||
416 | |||
417 | /* resource functions */ | ||
418 | extern int cpqhp_resource_sort_and_combine (struct pci_resource **head); | ||
419 | |||
420 | /* pci functions */ | ||
421 | extern int cpqhp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num); | ||
422 | extern int cpqhp_get_bus_dev (struct controller *ctrl, u8 *bus_num, u8 *dev_num, u8 slot); | ||
423 | extern int cpqhp_save_config (struct controller *ctrl, int busnumber, int is_hot_plug); | ||
424 | extern int cpqhp_save_base_addr_length (struct controller *ctrl, struct pci_func * func); | ||
425 | extern int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func); | ||
426 | extern int cpqhp_configure_board (struct controller *ctrl, struct pci_func * func); | ||
427 | extern int cpqhp_save_slot_config (struct controller *ctrl, struct pci_func * new_slot); | ||
428 | extern int cpqhp_valid_replace (struct controller *ctrl, struct pci_func * func); | ||
429 | extern void cpqhp_destroy_board_resources (struct pci_func * func); | ||
430 | extern int cpqhp_return_board_resources (struct pci_func * func, struct resource_lists * resources); | ||
431 | extern void cpqhp_destroy_resource_list (struct resource_lists * resources); | ||
432 | extern int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func); | ||
433 | extern int cpqhp_unconfigure_device (struct pci_func* func); | ||
434 | |||
435 | /* Global variables */ | ||
436 | extern int cpqhp_debug; | ||
437 | extern int cpqhp_legacy_mode; | ||
438 | extern struct controller *cpqhp_ctrl_list; | ||
439 | extern struct pci_func *cpqhp_slot_list[256]; | ||
440 | |||
441 | /* these can be gotten rid of, but for debugging they are purty */ | ||
442 | extern u8 cpqhp_nic_irq; | ||
443 | extern u8 cpqhp_disk_irq; | ||
444 | |||
445 | |||
446 | /* inline functions */ | ||
447 | |||
448 | /* | ||
449 | * return_resource | ||
450 | * | ||
451 | * Puts node back in the resource list pointed to by head | ||
452 | * | ||
453 | */ | ||
454 | static inline void return_resource(struct pci_resource **head, struct pci_resource *node) | ||
455 | { | ||
456 | if (!node || !head) | ||
457 | return; | ||
458 | node->next = *head; | ||
459 | *head = node; | ||
460 | } | ||
461 | |||
462 | static inline void set_SOGO(struct controller *ctrl) | ||
463 | { | ||
464 | u16 misc; | ||
465 | |||
466 | misc = readw(ctrl->hpc_reg + MISC); | ||
467 | misc = (misc | 0x0001) & 0xFFFB; | ||
468 | writew(misc, ctrl->hpc_reg + MISC); | ||
469 | } | ||
470 | |||
471 | |||
472 | static inline void amber_LED_on(struct controller *ctrl, u8 slot) | ||
473 | { | ||
474 | u32 led_control; | ||
475 | |||
476 | led_control = readl(ctrl->hpc_reg + LED_CONTROL); | ||
477 | led_control |= (0x01010000L << slot); | ||
478 | writel(led_control, ctrl->hpc_reg + LED_CONTROL); | ||
479 | } | ||
480 | |||
481 | |||
482 | static inline void amber_LED_off(struct controller *ctrl, u8 slot) | ||
483 | { | ||
484 | u32 led_control; | ||
485 | |||
486 | led_control = readl(ctrl->hpc_reg + LED_CONTROL); | ||
487 | led_control &= ~(0x01010000L << slot); | ||
488 | writel(led_control, ctrl->hpc_reg + LED_CONTROL); | ||
489 | } | ||
490 | |||
491 | |||
492 | static inline int read_amber_LED(struct controller *ctrl, u8 slot) | ||
493 | { | ||
494 | u32 led_control; | ||
495 | |||
496 | led_control = readl(ctrl->hpc_reg + LED_CONTROL); | ||
497 | led_control &= (0x01010000L << slot); | ||
498 | |||
499 | return led_control ? 1 : 0; | ||
500 | } | ||
501 | |||
502 | |||
503 | static inline void green_LED_on(struct controller *ctrl, u8 slot) | ||
504 | { | ||
505 | u32 led_control; | ||
506 | |||
507 | led_control = readl(ctrl->hpc_reg + LED_CONTROL); | ||
508 | led_control |= 0x0101L << slot; | ||
509 | writel(led_control, ctrl->hpc_reg + LED_CONTROL); | ||
510 | } | ||
511 | |||
512 | static inline void green_LED_off(struct controller *ctrl, u8 slot) | ||
513 | { | ||
514 | u32 led_control; | ||
515 | |||
516 | led_control = readl(ctrl->hpc_reg + LED_CONTROL); | ||
517 | led_control &= ~(0x0101L << slot); | ||
518 | writel(led_control, ctrl->hpc_reg + LED_CONTROL); | ||
519 | } | ||
520 | |||
521 | |||
522 | static inline void green_LED_blink(struct controller *ctrl, u8 slot) | ||
523 | { | ||
524 | u32 led_control; | ||
525 | |||
526 | led_control = readl(ctrl->hpc_reg + LED_CONTROL); | ||
527 | led_control &= ~(0x0101L << slot); | ||
528 | led_control |= (0x0001L << slot); | ||
529 | writel(led_control, ctrl->hpc_reg + LED_CONTROL); | ||
530 | } | ||
531 | |||
532 | |||
533 | static inline void slot_disable(struct controller *ctrl, u8 slot) | ||
534 | { | ||
535 | u8 slot_enable; | ||
536 | |||
537 | slot_enable = readb(ctrl->hpc_reg + SLOT_ENABLE); | ||
538 | slot_enable &= ~(0x01 << slot); | ||
539 | writeb(slot_enable, ctrl->hpc_reg + SLOT_ENABLE); | ||
540 | } | ||
541 | |||
542 | |||
543 | static inline void slot_enable(struct controller *ctrl, u8 slot) | ||
544 | { | ||
545 | u8 slot_enable; | ||
546 | |||
547 | slot_enable = readb(ctrl->hpc_reg + SLOT_ENABLE); | ||
548 | slot_enable |= (0x01 << slot); | ||
549 | writeb(slot_enable, ctrl->hpc_reg + SLOT_ENABLE); | ||
550 | } | ||
551 | |||
552 | |||
553 | static inline u8 is_slot_enabled(struct controller *ctrl, u8 slot) | ||
554 | { | ||
555 | u8 slot_enable; | ||
556 | |||
557 | slot_enable = readb(ctrl->hpc_reg + SLOT_ENABLE); | ||
558 | slot_enable &= (0x01 << slot); | ||
559 | return slot_enable ? 1 : 0; | ||
560 | } | ||
561 | |||
562 | |||
563 | static inline u8 read_slot_enable(struct controller *ctrl) | ||
564 | { | ||
565 | return readb(ctrl->hpc_reg + SLOT_ENABLE); | ||
566 | } | ||
567 | |||
568 | |||
569 | /* | ||
570 | * get_controller_speed - find the current frequency/mode of controller. | ||
571 | * | ||
572 | * @ctrl: controller to get frequency/mode for. | ||
573 | * | ||
574 | * Returns controller speed. | ||
575 | * | ||
576 | */ | ||
577 | static inline u8 get_controller_speed(struct controller *ctrl) | ||
578 | { | ||
579 | u8 curr_freq; | ||
580 | u16 misc; | ||
581 | |||
582 | if (ctrl->pcix_support) { | ||
583 | curr_freq = readb(ctrl->hpc_reg + NEXT_CURR_FREQ); | ||
584 | if ((curr_freq & 0xB0) == 0xB0) | ||
585 | return PCI_SPEED_133MHz_PCIX; | ||
586 | if ((curr_freq & 0xA0) == 0xA0) | ||
587 | return PCI_SPEED_100MHz_PCIX; | ||
588 | if ((curr_freq & 0x90) == 0x90) | ||
589 | return PCI_SPEED_66MHz_PCIX; | ||
590 | if (curr_freq & 0x10) | ||
591 | return PCI_SPEED_66MHz; | ||
592 | |||
593 | return PCI_SPEED_33MHz; | ||
594 | } | ||
595 | |||
596 | misc = readw(ctrl->hpc_reg + MISC); | ||
597 | return (misc & 0x0800) ? PCI_SPEED_66MHz : PCI_SPEED_33MHz; | ||
598 | } | ||
599 | |||
600 | |||
601 | /* | ||
602 | * get_adapter_speed - find the max supported frequency/mode of adapter. | ||
603 | * | ||
604 | * @ctrl: hotplug controller. | ||
605 | * @hp_slot: hotplug slot where adapter is installed. | ||
606 | * | ||
607 | * Returns adapter speed. | ||
608 | * | ||
609 | */ | ||
610 | static inline u8 get_adapter_speed(struct controller *ctrl, u8 hp_slot) | ||
611 | { | ||
612 | u32 temp_dword = readl(ctrl->hpc_reg + NON_INT_INPUT); | ||
613 | dbg("slot: %d, PCIXCAP: %8x\n", hp_slot, temp_dword); | ||
614 | if (ctrl->pcix_support) { | ||
615 | if (temp_dword & (0x10000 << hp_slot)) | ||
616 | return PCI_SPEED_133MHz_PCIX; | ||
617 | if (temp_dword & (0x100 << hp_slot)) | ||
618 | return PCI_SPEED_66MHz_PCIX; | ||
619 | } | ||
620 | |||
621 | if (temp_dword & (0x01 << hp_slot)) | ||
622 | return PCI_SPEED_66MHz; | ||
623 | |||
624 | return PCI_SPEED_33MHz; | ||
625 | } | ||
626 | |||
627 | static inline void enable_slot_power(struct controller *ctrl, u8 slot) | ||
628 | { | ||
629 | u8 slot_power; | ||
630 | |||
631 | slot_power = readb(ctrl->hpc_reg + SLOT_POWER); | ||
632 | slot_power |= (0x01 << slot); | ||
633 | writeb(slot_power, ctrl->hpc_reg + SLOT_POWER); | ||
634 | } | ||
635 | |||
636 | static inline void disable_slot_power(struct controller *ctrl, u8 slot) | ||
637 | { | ||
638 | u8 slot_power; | ||
639 | |||
640 | slot_power = readb(ctrl->hpc_reg + SLOT_POWER); | ||
641 | slot_power &= ~(0x01 << slot); | ||
642 | writeb(slot_power, ctrl->hpc_reg + SLOT_POWER); | ||
643 | } | ||
644 | |||
645 | |||
646 | static inline int cpq_get_attention_status(struct controller *ctrl, struct slot *slot) | ||
647 | { | ||
648 | u8 hp_slot; | ||
649 | |||
650 | hp_slot = slot->device - ctrl->slot_device_offset; | ||
651 | |||
652 | return read_amber_LED(ctrl, hp_slot); | ||
653 | } | ||
654 | |||
655 | |||
656 | static inline int get_slot_enabled(struct controller *ctrl, struct slot *slot) | ||
657 | { | ||
658 | u8 hp_slot; | ||
659 | |||
660 | hp_slot = slot->device - ctrl->slot_device_offset; | ||
661 | |||
662 | return is_slot_enabled(ctrl, hp_slot); | ||
663 | } | ||
664 | |||
665 | |||
666 | static inline int cpq_get_latch_status(struct controller *ctrl, struct slot *slot) | ||
667 | { | ||
668 | u32 status; | ||
669 | u8 hp_slot; | ||
670 | |||
671 | hp_slot = slot->device - ctrl->slot_device_offset; | ||
672 | dbg("%s: slot->device = %d, ctrl->slot_device_offset = %d \n", | ||
673 | __FUNCTION__, slot->device, ctrl->slot_device_offset); | ||
674 | |||
675 | status = (readl(ctrl->hpc_reg + INT_INPUT_CLEAR) & (0x01L << hp_slot)); | ||
676 | |||
677 | return(status == 0) ? 1 : 0; | ||
678 | } | ||
679 | |||
680 | |||
681 | static inline int get_presence_status(struct controller *ctrl, struct slot *slot) | ||
682 | { | ||
683 | int presence_save = 0; | ||
684 | u8 hp_slot; | ||
685 | u32 tempdword; | ||
686 | |||
687 | hp_slot = slot->device - ctrl->slot_device_offset; | ||
688 | |||
689 | tempdword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR); | ||
690 | presence_save = (int) ((((~tempdword) >> 23) | ((~tempdword) >> 15)) >> hp_slot) & 0x02; | ||
691 | |||
692 | return presence_save; | ||
693 | } | ||
694 | |||
695 | #define SLOT_NAME_SIZE 10 | ||
696 | |||
697 | static inline void make_slot_name(char *buffer, int buffer_size, struct slot *slot) | ||
698 | { | ||
699 | snprintf(buffer, buffer_size, "%d", slot->number); | ||
700 | } | ||
701 | |||
702 | |||
703 | static inline int wait_for_ctrl_irq(struct controller *ctrl) | ||
704 | { | ||
705 | DECLARE_WAITQUEUE(wait, current); | ||
706 | int retval = 0; | ||
707 | |||
708 | dbg("%s - start\n", __FUNCTION__); | ||
709 | add_wait_queue(&ctrl->queue, &wait); | ||
710 | /* Sleep for up to 1 second to wait for the LED to change. */ | ||
711 | msleep_interruptible(1000); | ||
712 | remove_wait_queue(&ctrl->queue, &wait); | ||
713 | if (signal_pending(current)) | ||
714 | retval = -EINTR; | ||
715 | |||
716 | dbg("%s - end\n", __FUNCTION__); | ||
717 | return retval; | ||
718 | } | ||
719 | |||
720 | #endif | ||
721 | |||
diff --git a/drivers/pci/hotplug/cpqphp_core.c b/drivers/pci/hotplug/cpqphp_core.c new file mode 100644 index 000000000000..afbccfa5217d --- /dev/null +++ b/drivers/pci/hotplug/cpqphp_core.c | |||
@@ -0,0 +1,1509 @@ | |||
1 | /* | ||
2 | * Compaq Hot Plug Controller Driver | ||
3 | * | ||
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | ||
5 | * Copyright (C) 2001 Greg Kroah-Hartman <greg@kroah.com> | ||
6 | * Copyright (C) 2001 IBM Corp. | ||
7 | * | ||
8 | * All rights reserved. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or (at | ||
13 | * your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
18 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
19 | * details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
24 | * | ||
25 | * Send feedback to <greg@kroah.com> | ||
26 | * | ||
27 | * Jan 12, 2003 - Added 66/100/133MHz PCI-X support, | ||
28 | * Torben Mathiasen <torben.mathiasen@hp.com> | ||
29 | * | ||
30 | */ | ||
31 | |||
32 | #include <linux/config.h> | ||
33 | #include <linux/module.h> | ||
34 | #include <linux/moduleparam.h> | ||
35 | #include <linux/kernel.h> | ||
36 | #include <linux/types.h> | ||
37 | #include <linux/proc_fs.h> | ||
38 | #include <linux/slab.h> | ||
39 | #include <linux/workqueue.h> | ||
40 | #include <linux/pci.h> | ||
41 | #include <linux/init.h> | ||
42 | #include <linux/interrupt.h> | ||
43 | |||
44 | #include <asm/uaccess.h> | ||
45 | |||
46 | #include "cpqphp.h" | ||
47 | #include "cpqphp_nvram.h" | ||
48 | #include "../../../arch/i386/pci/pci.h" /* horrible hack showing how processor dependent we are... */ | ||
49 | |||
50 | |||
51 | /* Global variables */ | ||
52 | int cpqhp_debug; | ||
53 | int cpqhp_legacy_mode; | ||
54 | struct controller *cpqhp_ctrl_list; /* = NULL */ | ||
55 | struct pci_func *cpqhp_slot_list[256]; | ||
56 | |||
57 | /* local variables */ | ||
58 | static void __iomem *smbios_table; | ||
59 | static void __iomem *smbios_start; | ||
60 | static void __iomem *cpqhp_rom_start; | ||
61 | static int power_mode; | ||
62 | static int debug; | ||
63 | |||
64 | #define DRIVER_VERSION "0.9.8" | ||
65 | #define DRIVER_AUTHOR "Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>" | ||
66 | #define DRIVER_DESC "Compaq Hot Plug PCI Controller Driver" | ||
67 | |||
68 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
69 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
70 | MODULE_LICENSE("GPL"); | ||
71 | |||
72 | module_param(power_mode, bool, 0644); | ||
73 | MODULE_PARM_DESC(power_mode, "Power mode enabled or not"); | ||
74 | |||
75 | module_param(debug, bool, 0644); | ||
76 | MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); | ||
77 | |||
78 | #define CPQHPC_MODULE_MINOR 208 | ||
79 | |||
80 | static int one_time_init (void); | ||
81 | static int set_attention_status (struct hotplug_slot *slot, u8 value); | ||
82 | static int process_SI (struct hotplug_slot *slot); | ||
83 | static int process_SS (struct hotplug_slot *slot); | ||
84 | static int hardware_test (struct hotplug_slot *slot, u32 value); | ||
85 | static int get_power_status (struct hotplug_slot *slot, u8 *value); | ||
86 | static int get_attention_status (struct hotplug_slot *slot, u8 *value); | ||
87 | static int get_latch_status (struct hotplug_slot *slot, u8 *value); | ||
88 | static int get_adapter_status (struct hotplug_slot *slot, u8 *value); | ||
89 | static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value); | ||
90 | static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value); | ||
91 | |||
92 | static struct hotplug_slot_ops cpqphp_hotplug_slot_ops = { | ||
93 | .owner = THIS_MODULE, | ||
94 | .set_attention_status = set_attention_status, | ||
95 | .enable_slot = process_SI, | ||
96 | .disable_slot = process_SS, | ||
97 | .hardware_test = hardware_test, | ||
98 | .get_power_status = get_power_status, | ||
99 | .get_attention_status = get_attention_status, | ||
100 | .get_latch_status = get_latch_status, | ||
101 | .get_adapter_status = get_adapter_status, | ||
102 | .get_max_bus_speed = get_max_bus_speed, | ||
103 | .get_cur_bus_speed = get_cur_bus_speed, | ||
104 | }; | ||
105 | |||
106 | |||
107 | static inline int is_slot64bit(struct slot *slot) | ||
108 | { | ||
109 | return (readb(slot->p_sm_slot + SMBIOS_SLOT_WIDTH) == 0x06) ? 1 : 0; | ||
110 | } | ||
111 | |||
112 | static inline int is_slot66mhz(struct slot *slot) | ||
113 | { | ||
114 | return (readb(slot->p_sm_slot + SMBIOS_SLOT_TYPE) == 0x0E) ? 1 : 0; | ||
115 | } | ||
116 | |||
117 | /** | ||
118 | * detect_SMBIOS_pointer - find the System Management BIOS Table in mem region. | ||
119 | * | ||
120 | * @begin: begin pointer for region to be scanned. | ||
121 | * @end: end pointer for region to be scanned. | ||
122 | * | ||
123 | * Returns pointer to the head of the SMBIOS tables (or NULL) | ||
124 | * | ||
125 | */ | ||
126 | static void __iomem * detect_SMBIOS_pointer(void __iomem *begin, void __iomem *end) | ||
127 | { | ||
128 | void __iomem *fp; | ||
129 | void __iomem *endp; | ||
130 | u8 temp1, temp2, temp3, temp4; | ||
131 | int status = 0; | ||
132 | |||
133 | endp = (end - sizeof(u32) + 1); | ||
134 | |||
135 | for (fp = begin; fp <= endp; fp += 16) { | ||
136 | temp1 = readb(fp); | ||
137 | temp2 = readb(fp+1); | ||
138 | temp3 = readb(fp+2); | ||
139 | temp4 = readb(fp+3); | ||
140 | if (temp1 == '_' && | ||
141 | temp2 == 'S' && | ||
142 | temp3 == 'M' && | ||
143 | temp4 == '_') { | ||
144 | status = 1; | ||
145 | break; | ||
146 | } | ||
147 | } | ||
148 | |||
149 | if (!status) | ||
150 | fp = NULL; | ||
151 | |||
152 | dbg("Discovered SMBIOS Entry point at %p\n", fp); | ||
153 | |||
154 | return fp; | ||
155 | } | ||
156 | |||
157 | /** | ||
158 | * init_SERR - Initializes the per slot SERR generation. | ||
159 | * | ||
160 | * For unexpected switch opens | ||
161 | * | ||
162 | */ | ||
163 | static int init_SERR(struct controller * ctrl) | ||
164 | { | ||
165 | u32 tempdword; | ||
166 | u32 number_of_slots; | ||
167 | u8 physical_slot; | ||
168 | |||
169 | if (!ctrl) | ||
170 | return 1; | ||
171 | |||
172 | tempdword = ctrl->first_slot; | ||
173 | |||
174 | number_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0F; | ||
175 | // Loop through slots | ||
176 | while (number_of_slots) { | ||
177 | physical_slot = tempdword; | ||
178 | writeb(0, ctrl->hpc_reg + SLOT_SERR); | ||
179 | tempdword++; | ||
180 | number_of_slots--; | ||
181 | } | ||
182 | |||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | |||
187 | /* nice debugging output */ | ||
188 | static int pci_print_IRQ_route (void) | ||
189 | { | ||
190 | struct irq_routing_table *routing_table; | ||
191 | int len; | ||
192 | int loop; | ||
193 | |||
194 | u8 tbus, tdevice, tslot; | ||
195 | |||
196 | routing_table = pcibios_get_irq_routing_table(); | ||
197 | if (routing_table == NULL) { | ||
198 | err("No BIOS Routing Table??? Not good\n"); | ||
199 | return -ENOMEM; | ||
200 | } | ||
201 | |||
202 | len = (routing_table->size - sizeof(struct irq_routing_table)) / | ||
203 | sizeof(struct irq_info); | ||
204 | // Make sure I got at least one entry | ||
205 | if (len == 0) { | ||
206 | kfree(routing_table); | ||
207 | return -1; | ||
208 | } | ||
209 | |||
210 | dbg("bus dev func slot\n"); | ||
211 | |||
212 | for (loop = 0; loop < len; ++loop) { | ||
213 | tbus = routing_table->slots[loop].bus; | ||
214 | tdevice = routing_table->slots[loop].devfn; | ||
215 | tslot = routing_table->slots[loop].slot; | ||
216 | dbg("%d %d %d %d\n", tbus, tdevice >> 3, tdevice & 0x7, tslot); | ||
217 | |||
218 | } | ||
219 | kfree(routing_table); | ||
220 | return 0; | ||
221 | } | ||
222 | |||
223 | |||
224 | /** | ||
225 | * get_subsequent_smbios_entry: get the next entry from bios table. | ||
226 | * | ||
227 | * Gets the first entry if previous == NULL | ||
228 | * Otherwise, returns the next entry | ||
229 | * Uses global SMBIOS Table pointer | ||
230 | * | ||
231 | * @curr: %NULL or pointer to previously returned structure | ||
232 | * | ||
233 | * returns a pointer to an SMBIOS structure or NULL if none found | ||
234 | */ | ||
235 | static void __iomem *get_subsequent_smbios_entry(void __iomem *smbios_start, | ||
236 | void __iomem *smbios_table, | ||
237 | void __iomem *curr) | ||
238 | { | ||
239 | u8 bail = 0; | ||
240 | u8 previous_byte = 1; | ||
241 | void __iomem *p_temp; | ||
242 | void __iomem *p_max; | ||
243 | |||
244 | if (!smbios_table || !curr) | ||
245 | return(NULL); | ||
246 | |||
247 | // set p_max to the end of the table | ||
248 | p_max = smbios_start + readw(smbios_table + ST_LENGTH); | ||
249 | |||
250 | p_temp = curr; | ||
251 | p_temp += readb(curr + SMBIOS_GENERIC_LENGTH); | ||
252 | |||
253 | while ((p_temp < p_max) && !bail) { | ||
254 | /* Look for the double NULL terminator | ||
255 | * The first condition is the previous byte | ||
256 | * and the second is the curr */ | ||
257 | if (!previous_byte && !(readb(p_temp))) { | ||
258 | bail = 1; | ||
259 | } | ||
260 | |||
261 | previous_byte = readb(p_temp); | ||
262 | p_temp++; | ||
263 | } | ||
264 | |||
265 | if (p_temp < p_max) { | ||
266 | return p_temp; | ||
267 | } else { | ||
268 | return NULL; | ||
269 | } | ||
270 | } | ||
271 | |||
272 | |||
273 | /** | ||
274 | * get_SMBIOS_entry | ||
275 | * | ||
276 | * @type:SMBIOS structure type to be returned | ||
277 | * @previous: %NULL or pointer to previously returned structure | ||
278 | * | ||
279 | * Gets the first entry of the specified type if previous == NULL | ||
280 | * Otherwise, returns the next entry of the given type. | ||
281 | * Uses global SMBIOS Table pointer | ||
282 | * Uses get_subsequent_smbios_entry | ||
283 | * | ||
284 | * returns a pointer to an SMBIOS structure or %NULL if none found | ||
285 | */ | ||
286 | static void __iomem *get_SMBIOS_entry(void __iomem *smbios_start, | ||
287 | void __iomem *smbios_table, | ||
288 | u8 type, | ||
289 | void __iomem *previous) | ||
290 | { | ||
291 | if (!smbios_table) | ||
292 | return NULL; | ||
293 | |||
294 | if (!previous) { | ||
295 | previous = smbios_start; | ||
296 | } else { | ||
297 | previous = get_subsequent_smbios_entry(smbios_start, | ||
298 | smbios_table, previous); | ||
299 | } | ||
300 | |||
301 | while (previous) { | ||
302 | if (readb(previous + SMBIOS_GENERIC_TYPE) != type) { | ||
303 | previous = get_subsequent_smbios_entry(smbios_start, | ||
304 | smbios_table, previous); | ||
305 | } else { | ||
306 | break; | ||
307 | } | ||
308 | } | ||
309 | |||
310 | return previous; | ||
311 | } | ||
312 | |||
313 | static void release_slot(struct hotplug_slot *hotplug_slot) | ||
314 | { | ||
315 | struct slot *slot = hotplug_slot->private; | ||
316 | |||
317 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
318 | |||
319 | kfree(slot->hotplug_slot->info); | ||
320 | kfree(slot->hotplug_slot->name); | ||
321 | kfree(slot->hotplug_slot); | ||
322 | kfree(slot); | ||
323 | } | ||
324 | |||
325 | static int ctrl_slot_setup(struct controller *ctrl, | ||
326 | void __iomem *smbios_start, | ||
327 | void __iomem *smbios_table) | ||
328 | { | ||
329 | struct slot *new_slot; | ||
330 | u8 number_of_slots; | ||
331 | u8 slot_device; | ||
332 | u8 slot_number; | ||
333 | u8 ctrl_slot; | ||
334 | u32 tempdword; | ||
335 | void __iomem *slot_entry= NULL; | ||
336 | int result = -ENOMEM; | ||
337 | |||
338 | dbg("%s\n", __FUNCTION__); | ||
339 | |||
340 | tempdword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR); | ||
341 | |||
342 | number_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0F; | ||
343 | slot_device = readb(ctrl->hpc_reg + SLOT_MASK) >> 4; | ||
344 | slot_number = ctrl->first_slot; | ||
345 | |||
346 | while (number_of_slots) { | ||
347 | new_slot = kmalloc(sizeof(*new_slot), GFP_KERNEL); | ||
348 | if (!new_slot) | ||
349 | goto error; | ||
350 | |||
351 | memset(new_slot, 0, sizeof(struct slot)); | ||
352 | new_slot->hotplug_slot = kmalloc(sizeof(*(new_slot->hotplug_slot)), | ||
353 | GFP_KERNEL); | ||
354 | if (!new_slot->hotplug_slot) | ||
355 | goto error_slot; | ||
356 | memset(new_slot->hotplug_slot, 0, sizeof(struct hotplug_slot)); | ||
357 | |||
358 | new_slot->hotplug_slot->info = | ||
359 | kmalloc(sizeof(*(new_slot->hotplug_slot->info)), | ||
360 | GFP_KERNEL); | ||
361 | if (!new_slot->hotplug_slot->info) | ||
362 | goto error_hpslot; | ||
363 | memset(new_slot->hotplug_slot->info, 0, | ||
364 | sizeof(struct hotplug_slot_info)); | ||
365 | new_slot->hotplug_slot->name = kmalloc(SLOT_NAME_SIZE, GFP_KERNEL); | ||
366 | if (!new_slot->hotplug_slot->name) | ||
367 | goto error_info; | ||
368 | |||
369 | new_slot->ctrl = ctrl; | ||
370 | new_slot->bus = ctrl->bus; | ||
371 | new_slot->device = slot_device; | ||
372 | new_slot->number = slot_number; | ||
373 | dbg("slot->number = %d\n",new_slot->number); | ||
374 | |||
375 | slot_entry = get_SMBIOS_entry(smbios_start, smbios_table, 9, | ||
376 | slot_entry); | ||
377 | |||
378 | while (slot_entry && (readw(slot_entry + SMBIOS_SLOT_NUMBER) != new_slot->number)) { | ||
379 | slot_entry = get_SMBIOS_entry(smbios_start, | ||
380 | smbios_table, 9, slot_entry); | ||
381 | } | ||
382 | |||
383 | new_slot->p_sm_slot = slot_entry; | ||
384 | |||
385 | init_timer(&new_slot->task_event); | ||
386 | new_slot->task_event.expires = jiffies + 5 * HZ; | ||
387 | new_slot->task_event.function = cpqhp_pushbutton_thread; | ||
388 | |||
389 | //FIXME: these capabilities aren't used but if they are | ||
390 | // they need to be correctly implemented | ||
391 | new_slot->capabilities |= PCISLOT_REPLACE_SUPPORTED; | ||
392 | new_slot->capabilities |= PCISLOT_INTERLOCK_SUPPORTED; | ||
393 | |||
394 | if (is_slot64bit(new_slot)) | ||
395 | new_slot->capabilities |= PCISLOT_64_BIT_SUPPORTED; | ||
396 | if (is_slot66mhz(new_slot)) | ||
397 | new_slot->capabilities |= PCISLOT_66_MHZ_SUPPORTED; | ||
398 | if (ctrl->speed == PCI_SPEED_66MHz) | ||
399 | new_slot->capabilities |= PCISLOT_66_MHZ_OPERATION; | ||
400 | |||
401 | ctrl_slot = slot_device - (readb(ctrl->hpc_reg + SLOT_MASK) >> 4); | ||
402 | |||
403 | // Check presence | ||
404 | new_slot->capabilities |= ((((~tempdword) >> 23) | ((~tempdword) >> 15)) >> ctrl_slot) & 0x02; | ||
405 | // Check the switch state | ||
406 | new_slot->capabilities |= ((~tempdword & 0xFF) >> ctrl_slot) & 0x01; | ||
407 | // Check the slot enable | ||
408 | new_slot->capabilities |= ((read_slot_enable(ctrl) << 2) >> ctrl_slot) & 0x04; | ||
409 | |||
410 | /* register this slot with the hotplug pci core */ | ||
411 | new_slot->hotplug_slot->release = &release_slot; | ||
412 | new_slot->hotplug_slot->private = new_slot; | ||
413 | make_slot_name(new_slot->hotplug_slot->name, SLOT_NAME_SIZE, new_slot); | ||
414 | new_slot->hotplug_slot->ops = &cpqphp_hotplug_slot_ops; | ||
415 | |||
416 | new_slot->hotplug_slot->info->power_status = get_slot_enabled(ctrl, new_slot); | ||
417 | new_slot->hotplug_slot->info->attention_status = cpq_get_attention_status(ctrl, new_slot); | ||
418 | new_slot->hotplug_slot->info->latch_status = cpq_get_latch_status(ctrl, new_slot); | ||
419 | new_slot->hotplug_slot->info->adapter_status = get_presence_status(ctrl, new_slot); | ||
420 | |||
421 | dbg ("registering bus %d, dev %d, number %d, " | ||
422 | "ctrl->slot_device_offset %d, slot %d\n", | ||
423 | new_slot->bus, new_slot->device, | ||
424 | new_slot->number, ctrl->slot_device_offset, | ||
425 | slot_number); | ||
426 | result = pci_hp_register (new_slot->hotplug_slot); | ||
427 | if (result) { | ||
428 | err ("pci_hp_register failed with error %d\n", result); | ||
429 | goto error_name; | ||
430 | } | ||
431 | |||
432 | new_slot->next = ctrl->slot; | ||
433 | ctrl->slot = new_slot; | ||
434 | |||
435 | number_of_slots--; | ||
436 | slot_device++; | ||
437 | slot_number++; | ||
438 | } | ||
439 | |||
440 | return 0; | ||
441 | |||
442 | error_name: | ||
443 | kfree(new_slot->hotplug_slot->name); | ||
444 | error_info: | ||
445 | kfree(new_slot->hotplug_slot->info); | ||
446 | error_hpslot: | ||
447 | kfree(new_slot->hotplug_slot); | ||
448 | error_slot: | ||
449 | kfree(new_slot); | ||
450 | error: | ||
451 | return result; | ||
452 | } | ||
453 | |||
454 | static int ctrl_slot_cleanup (struct controller * ctrl) | ||
455 | { | ||
456 | struct slot *old_slot, *next_slot; | ||
457 | |||
458 | old_slot = ctrl->slot; | ||
459 | ctrl->slot = NULL; | ||
460 | |||
461 | while (old_slot) { | ||
462 | /* memory will be freed by the release_slot callback */ | ||
463 | next_slot = old_slot->next; | ||
464 | pci_hp_deregister (old_slot->hotplug_slot); | ||
465 | old_slot = next_slot; | ||
466 | } | ||
467 | |||
468 | //Free IRQ associated with hot plug device | ||
469 | free_irq(ctrl->interrupt, ctrl); | ||
470 | //Unmap the memory | ||
471 | iounmap(ctrl->hpc_reg); | ||
472 | //Finally reclaim PCI mem | ||
473 | release_mem_region(pci_resource_start(ctrl->pci_dev, 0), | ||
474 | pci_resource_len(ctrl->pci_dev, 0)); | ||
475 | |||
476 | return(0); | ||
477 | } | ||
478 | |||
479 | |||
480 | //============================================================================ | ||
481 | // function: get_slot_mapping | ||
482 | // | ||
483 | // Description: Attempts to determine a logical slot mapping for a PCI | ||
484 | // device. Won't work for more than one PCI-PCI bridge | ||
485 | // in a slot. | ||
486 | // | ||
487 | // Input: u8 bus_num - bus number of PCI device | ||
488 | // u8 dev_num - device number of PCI device | ||
489 | // u8 *slot - Pointer to u8 where slot number will | ||
490 | // be returned | ||
491 | // | ||
492 | // Output: SUCCESS or FAILURE | ||
493 | //============================================================================= | ||
494 | static int | ||
495 | get_slot_mapping(struct pci_bus *bus, u8 bus_num, u8 dev_num, u8 *slot) | ||
496 | { | ||
497 | struct irq_routing_table *PCIIRQRoutingInfoLength; | ||
498 | u32 work; | ||
499 | long len; | ||
500 | long loop; | ||
501 | |||
502 | u8 tbus, tdevice, tslot, bridgeSlot; | ||
503 | |||
504 | dbg("%s: %p, %d, %d, %p\n", __FUNCTION__, bus, bus_num, dev_num, slot); | ||
505 | |||
506 | bridgeSlot = 0xFF; | ||
507 | |||
508 | PCIIRQRoutingInfoLength = pcibios_get_irq_routing_table(); | ||
509 | if (!PCIIRQRoutingInfoLength) | ||
510 | return -1; | ||
511 | |||
512 | len = (PCIIRQRoutingInfoLength->size - | ||
513 | sizeof(struct irq_routing_table)) / sizeof(struct irq_info); | ||
514 | // Make sure I got at least one entry | ||
515 | if (len == 0) { | ||
516 | kfree(PCIIRQRoutingInfoLength); | ||
517 | return -1; | ||
518 | } | ||
519 | |||
520 | for (loop = 0; loop < len; ++loop) { | ||
521 | tbus = PCIIRQRoutingInfoLength->slots[loop].bus; | ||
522 | tdevice = PCIIRQRoutingInfoLength->slots[loop].devfn >> 3; | ||
523 | tslot = PCIIRQRoutingInfoLength->slots[loop].slot; | ||
524 | |||
525 | if ((tbus == bus_num) && (tdevice == dev_num)) { | ||
526 | *slot = tslot; | ||
527 | kfree(PCIIRQRoutingInfoLength); | ||
528 | return 0; | ||
529 | } else { | ||
530 | /* Did not get a match on the target PCI device. Check | ||
531 | * if the current IRQ table entry is a PCI-to-PCI bridge | ||
532 | * device. If so, and it's secondary bus matches the | ||
533 | * bus number for the target device, I need to save the | ||
534 | * bridge's slot number. If I can not find an entry for | ||
535 | * the target device, I will have to assume it's on the | ||
536 | * other side of the bridge, and assign it the bridge's | ||
537 | * slot. */ | ||
538 | bus->number = tbus; | ||
539 | pci_bus_read_config_dword(bus, PCI_DEVFN(tdevice, 0), | ||
540 | PCI_REVISION_ID, &work); | ||
541 | |||
542 | if ((work >> 8) == PCI_TO_PCI_BRIDGE_CLASS) { | ||
543 | pci_bus_read_config_dword(bus, | ||
544 | PCI_DEVFN(tdevice, 0), | ||
545 | PCI_PRIMARY_BUS, &work); | ||
546 | // See if bridge's secondary bus matches target bus. | ||
547 | if (((work >> 8) & 0x000000FF) == (long) bus_num) { | ||
548 | bridgeSlot = tslot; | ||
549 | } | ||
550 | } | ||
551 | } | ||
552 | |||
553 | } | ||
554 | |||
555 | // If we got here, we didn't find an entry in the IRQ mapping table | ||
556 | // for the target PCI device. If we did determine that the target | ||
557 | // device is on the other side of a PCI-to-PCI bridge, return the | ||
558 | // slot number for the bridge. | ||
559 | if (bridgeSlot != 0xFF) { | ||
560 | *slot = bridgeSlot; | ||
561 | kfree(PCIIRQRoutingInfoLength); | ||
562 | return 0; | ||
563 | } | ||
564 | kfree(PCIIRQRoutingInfoLength); | ||
565 | // Couldn't find an entry in the routing table for this PCI device | ||
566 | return -1; | ||
567 | } | ||
568 | |||
569 | |||
570 | /** | ||
571 | * cpqhp_set_attention_status - Turns the Amber LED for a slot on or off | ||
572 | * | ||
573 | */ | ||
574 | static int | ||
575 | cpqhp_set_attention_status(struct controller *ctrl, struct pci_func *func, | ||
576 | u32 status) | ||
577 | { | ||
578 | u8 hp_slot; | ||
579 | |||
580 | if (func == NULL) | ||
581 | return(1); | ||
582 | |||
583 | hp_slot = func->device - ctrl->slot_device_offset; | ||
584 | |||
585 | // Wait for exclusive access to hardware | ||
586 | down(&ctrl->crit_sect); | ||
587 | |||
588 | if (status == 1) { | ||
589 | amber_LED_on (ctrl, hp_slot); | ||
590 | } else if (status == 0) { | ||
591 | amber_LED_off (ctrl, hp_slot); | ||
592 | } else { | ||
593 | // Done with exclusive hardware access | ||
594 | up(&ctrl->crit_sect); | ||
595 | return(1); | ||
596 | } | ||
597 | |||
598 | set_SOGO(ctrl); | ||
599 | |||
600 | // Wait for SOBS to be unset | ||
601 | wait_for_ctrl_irq (ctrl); | ||
602 | |||
603 | // Done with exclusive hardware access | ||
604 | up(&ctrl->crit_sect); | ||
605 | |||
606 | return(0); | ||
607 | } | ||
608 | |||
609 | |||
610 | /** | ||
611 | * set_attention_status - Turns the Amber LED for a slot on or off | ||
612 | * | ||
613 | */ | ||
614 | static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 status) | ||
615 | { | ||
616 | struct pci_func *slot_func; | ||
617 | struct slot *slot = hotplug_slot->private; | ||
618 | struct controller *ctrl = slot->ctrl; | ||
619 | u8 bus; | ||
620 | u8 devfn; | ||
621 | u8 device; | ||
622 | u8 function; | ||
623 | |||
624 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
625 | |||
626 | if (cpqhp_get_bus_dev(ctrl, &bus, &devfn, slot->number) == -1) | ||
627 | return -ENODEV; | ||
628 | |||
629 | device = devfn >> 3; | ||
630 | function = devfn & 0x7; | ||
631 | dbg("bus, dev, fn = %d, %d, %d\n", bus, device, function); | ||
632 | |||
633 | slot_func = cpqhp_slot_find(bus, device, function); | ||
634 | if (!slot_func) | ||
635 | return -ENODEV; | ||
636 | |||
637 | return cpqhp_set_attention_status(ctrl, slot_func, status); | ||
638 | } | ||
639 | |||
640 | |||
641 | static int process_SI(struct hotplug_slot *hotplug_slot) | ||
642 | { | ||
643 | struct pci_func *slot_func; | ||
644 | struct slot *slot = hotplug_slot->private; | ||
645 | struct controller *ctrl = slot->ctrl; | ||
646 | u8 bus; | ||
647 | u8 devfn; | ||
648 | u8 device; | ||
649 | u8 function; | ||
650 | |||
651 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
652 | |||
653 | if (cpqhp_get_bus_dev(ctrl, &bus, &devfn, slot->number) == -1) | ||
654 | return -ENODEV; | ||
655 | |||
656 | device = devfn >> 3; | ||
657 | function = devfn & 0x7; | ||
658 | dbg("bus, dev, fn = %d, %d, %d\n", bus, device, function); | ||
659 | |||
660 | slot_func = cpqhp_slot_find(bus, device, function); | ||
661 | if (!slot_func) | ||
662 | return -ENODEV; | ||
663 | |||
664 | slot_func->bus = bus; | ||
665 | slot_func->device = device; | ||
666 | slot_func->function = function; | ||
667 | slot_func->configured = 0; | ||
668 | dbg("board_added(%p, %p)\n", slot_func, ctrl); | ||
669 | return cpqhp_process_SI(ctrl, slot_func); | ||
670 | } | ||
671 | |||
672 | |||
673 | static int process_SS(struct hotplug_slot *hotplug_slot) | ||
674 | { | ||
675 | struct pci_func *slot_func; | ||
676 | struct slot *slot = hotplug_slot->private; | ||
677 | struct controller *ctrl = slot->ctrl; | ||
678 | u8 bus; | ||
679 | u8 devfn; | ||
680 | u8 device; | ||
681 | u8 function; | ||
682 | |||
683 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
684 | |||
685 | if (cpqhp_get_bus_dev(ctrl, &bus, &devfn, slot->number) == -1) | ||
686 | return -ENODEV; | ||
687 | |||
688 | device = devfn >> 3; | ||
689 | function = devfn & 0x7; | ||
690 | dbg("bus, dev, fn = %d, %d, %d\n", bus, device, function); | ||
691 | |||
692 | slot_func = cpqhp_slot_find(bus, device, function); | ||
693 | if (!slot_func) | ||
694 | return -ENODEV; | ||
695 | |||
696 | dbg("In %s, slot_func = %p, ctrl = %p\n", __FUNCTION__, slot_func, ctrl); | ||
697 | return cpqhp_process_SS(ctrl, slot_func); | ||
698 | } | ||
699 | |||
700 | |||
701 | static int hardware_test(struct hotplug_slot *hotplug_slot, u32 value) | ||
702 | { | ||
703 | struct slot *slot = hotplug_slot->private; | ||
704 | struct controller *ctrl = slot->ctrl; | ||
705 | |||
706 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
707 | |||
708 | return cpqhp_hardware_test(ctrl, value); | ||
709 | } | ||
710 | |||
711 | |||
712 | static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) | ||
713 | { | ||
714 | struct slot *slot = hotplug_slot->private; | ||
715 | struct controller *ctrl = slot->ctrl; | ||
716 | |||
717 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
718 | |||
719 | *value = get_slot_enabled(ctrl, slot); | ||
720 | return 0; | ||
721 | } | ||
722 | |||
723 | static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value) | ||
724 | { | ||
725 | struct slot *slot = hotplug_slot->private; | ||
726 | struct controller *ctrl = slot->ctrl; | ||
727 | |||
728 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
729 | |||
730 | *value = cpq_get_attention_status(ctrl, slot); | ||
731 | return 0; | ||
732 | } | ||
733 | |||
734 | static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) | ||
735 | { | ||
736 | struct slot *slot = hotplug_slot->private; | ||
737 | struct controller *ctrl = slot->ctrl; | ||
738 | |||
739 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
740 | |||
741 | *value = cpq_get_latch_status(ctrl, slot); | ||
742 | |||
743 | return 0; | ||
744 | } | ||
745 | |||
746 | static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) | ||
747 | { | ||
748 | struct slot *slot = hotplug_slot->private; | ||
749 | struct controller *ctrl = slot->ctrl; | ||
750 | |||
751 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
752 | |||
753 | *value = get_presence_status(ctrl, slot); | ||
754 | |||
755 | return 0; | ||
756 | } | ||
757 | |||
758 | static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) | ||
759 | { | ||
760 | struct slot *slot = hotplug_slot->private; | ||
761 | struct controller *ctrl = slot->ctrl; | ||
762 | |||
763 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
764 | |||
765 | *value = ctrl->speed_capability; | ||
766 | |||
767 | return 0; | ||
768 | } | ||
769 | |||
770 | static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) | ||
771 | { | ||
772 | struct slot *slot = hotplug_slot->private; | ||
773 | struct controller *ctrl = slot->ctrl; | ||
774 | |||
775 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
776 | |||
777 | *value = ctrl->speed; | ||
778 | |||
779 | return 0; | ||
780 | } | ||
781 | |||
782 | static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | ||
783 | { | ||
784 | u8 num_of_slots = 0; | ||
785 | u8 hp_slot = 0; | ||
786 | u8 device; | ||
787 | u8 rev; | ||
788 | u8 bus_cap; | ||
789 | u16 temp_word; | ||
790 | u16 vendor_id; | ||
791 | u16 subsystem_vid; | ||
792 | u16 subsystem_deviceid; | ||
793 | u32 rc; | ||
794 | struct controller *ctrl; | ||
795 | struct pci_func *func; | ||
796 | |||
797 | // Need to read VID early b/c it's used to differentiate CPQ and INTC discovery | ||
798 | rc = pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor_id); | ||
799 | if (rc || ((vendor_id != PCI_VENDOR_ID_COMPAQ) && (vendor_id != PCI_VENDOR_ID_INTEL))) { | ||
800 | err(msg_HPC_non_compaq_or_intel); | ||
801 | return -ENODEV; | ||
802 | } | ||
803 | dbg("Vendor ID: %x\n", vendor_id); | ||
804 | |||
805 | rc = pci_read_config_byte(pdev, PCI_REVISION_ID, &rev); | ||
806 | dbg("revision: %d\n", rev); | ||
807 | if (rc || ((vendor_id == PCI_VENDOR_ID_COMPAQ) && (!rev))) { | ||
808 | err(msg_HPC_rev_error); | ||
809 | return -ENODEV; | ||
810 | } | ||
811 | |||
812 | /* Check for the proper subsytem ID's | ||
813 | * Intel uses a different SSID programming model than Compaq. | ||
814 | * For Intel, each SSID bit identifies a PHP capability. | ||
815 | * Also Intel HPC's may have RID=0. | ||
816 | */ | ||
817 | if ((rev > 2) || (vendor_id == PCI_VENDOR_ID_INTEL)) { | ||
818 | // TODO: This code can be made to support non-Compaq or Intel subsystem IDs | ||
819 | rc = pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vid); | ||
820 | if (rc) { | ||
821 | err("%s : pci_read_config_word failed\n", __FUNCTION__); | ||
822 | return rc; | ||
823 | } | ||
824 | dbg("Subsystem Vendor ID: %x\n", subsystem_vid); | ||
825 | if ((subsystem_vid != PCI_VENDOR_ID_COMPAQ) && (subsystem_vid != PCI_VENDOR_ID_INTEL)) { | ||
826 | err(msg_HPC_non_compaq_or_intel); | ||
827 | return -ENODEV; | ||
828 | } | ||
829 | |||
830 | ctrl = (struct controller *) kmalloc(sizeof(struct controller), GFP_KERNEL); | ||
831 | if (!ctrl) { | ||
832 | err("%s : out of memory\n", __FUNCTION__); | ||
833 | return -ENOMEM; | ||
834 | } | ||
835 | memset(ctrl, 0, sizeof(struct controller)); | ||
836 | |||
837 | rc = pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &subsystem_deviceid); | ||
838 | if (rc) { | ||
839 | err("%s : pci_read_config_word failed\n", __FUNCTION__); | ||
840 | goto err_free_ctrl; | ||
841 | } | ||
842 | |||
843 | info("Hot Plug Subsystem Device ID: %x\n", subsystem_deviceid); | ||
844 | |||
845 | /* Set Vendor ID, so it can be accessed later from other functions */ | ||
846 | ctrl->vendor_id = vendor_id; | ||
847 | |||
848 | switch (subsystem_vid) { | ||
849 | case PCI_VENDOR_ID_COMPAQ: | ||
850 | if (rev >= 0x13) { /* CIOBX */ | ||
851 | ctrl->push_flag = 1; | ||
852 | ctrl->slot_switch_type = 1; | ||
853 | ctrl->push_button = 1; | ||
854 | ctrl->pci_config_space = 1; | ||
855 | ctrl->defeature_PHP = 1; | ||
856 | ctrl->pcix_support = 1; | ||
857 | ctrl->pcix_speed_capability = 1; | ||
858 | pci_read_config_byte(pdev, 0x41, &bus_cap); | ||
859 | if (bus_cap & 0x80) { | ||
860 | dbg("bus max supports 133MHz PCI-X\n"); | ||
861 | ctrl->speed_capability = PCI_SPEED_133MHz_PCIX; | ||
862 | break; | ||
863 | } | ||
864 | if (bus_cap & 0x40) { | ||
865 | dbg("bus max supports 100MHz PCI-X\n"); | ||
866 | ctrl->speed_capability = PCI_SPEED_100MHz_PCIX; | ||
867 | break; | ||
868 | } | ||
869 | if (bus_cap & 20) { | ||
870 | dbg("bus max supports 66MHz PCI-X\n"); | ||
871 | ctrl->speed_capability = PCI_SPEED_66MHz_PCIX; | ||
872 | break; | ||
873 | } | ||
874 | if (bus_cap & 10) { | ||
875 | dbg("bus max supports 66MHz PCI\n"); | ||
876 | ctrl->speed_capability = PCI_SPEED_66MHz; | ||
877 | break; | ||
878 | } | ||
879 | |||
880 | break; | ||
881 | } | ||
882 | |||
883 | switch (subsystem_deviceid) { | ||
884 | case PCI_SUB_HPC_ID: | ||
885 | /* Original 6500/7000 implementation */ | ||
886 | ctrl->slot_switch_type = 1; | ||
887 | ctrl->speed_capability = PCI_SPEED_33MHz; | ||
888 | ctrl->push_button = 0; | ||
889 | ctrl->pci_config_space = 1; | ||
890 | ctrl->defeature_PHP = 1; | ||
891 | ctrl->pcix_support = 0; | ||
892 | ctrl->pcix_speed_capability = 0; | ||
893 | break; | ||
894 | case PCI_SUB_HPC_ID2: | ||
895 | /* First Pushbutton implementation */ | ||
896 | ctrl->push_flag = 1; | ||
897 | ctrl->slot_switch_type = 1; | ||
898 | ctrl->speed_capability = PCI_SPEED_33MHz; | ||
899 | ctrl->push_button = 1; | ||
900 | ctrl->pci_config_space = 1; | ||
901 | ctrl->defeature_PHP = 1; | ||
902 | ctrl->pcix_support = 0; | ||
903 | ctrl->pcix_speed_capability = 0; | ||
904 | break; | ||
905 | case PCI_SUB_HPC_ID_INTC: | ||
906 | /* Third party (6500/7000) */ | ||
907 | ctrl->slot_switch_type = 1; | ||
908 | ctrl->speed_capability = PCI_SPEED_33MHz; | ||
909 | ctrl->push_button = 0; | ||
910 | ctrl->pci_config_space = 1; | ||
911 | ctrl->defeature_PHP = 1; | ||
912 | ctrl->pcix_support = 0; | ||
913 | ctrl->pcix_speed_capability = 0; | ||
914 | break; | ||
915 | case PCI_SUB_HPC_ID3: | ||
916 | /* First 66 Mhz implementation */ | ||
917 | ctrl->push_flag = 1; | ||
918 | ctrl->slot_switch_type = 1; | ||
919 | ctrl->speed_capability = PCI_SPEED_66MHz; | ||
920 | ctrl->push_button = 1; | ||
921 | ctrl->pci_config_space = 1; | ||
922 | ctrl->defeature_PHP = 1; | ||
923 | ctrl->pcix_support = 0; | ||
924 | ctrl->pcix_speed_capability = 0; | ||
925 | break; | ||
926 | case PCI_SUB_HPC_ID4: | ||
927 | /* First PCI-X implementation, 100MHz */ | ||
928 | ctrl->push_flag = 1; | ||
929 | ctrl->slot_switch_type = 1; | ||
930 | ctrl->speed_capability = PCI_SPEED_100MHz_PCIX; | ||
931 | ctrl->push_button = 1; | ||
932 | ctrl->pci_config_space = 1; | ||
933 | ctrl->defeature_PHP = 1; | ||
934 | ctrl->pcix_support = 1; | ||
935 | ctrl->pcix_speed_capability = 0; | ||
936 | break; | ||
937 | default: | ||
938 | err(msg_HPC_not_supported); | ||
939 | rc = -ENODEV; | ||
940 | goto err_free_ctrl; | ||
941 | } | ||
942 | break; | ||
943 | |||
944 | case PCI_VENDOR_ID_INTEL: | ||
945 | /* Check for speed capability (0=33, 1=66) */ | ||
946 | if (subsystem_deviceid & 0x0001) { | ||
947 | ctrl->speed_capability = PCI_SPEED_66MHz; | ||
948 | } else { | ||
949 | ctrl->speed_capability = PCI_SPEED_33MHz; | ||
950 | } | ||
951 | |||
952 | /* Check for push button */ | ||
953 | if (subsystem_deviceid & 0x0002) { | ||
954 | /* no push button */ | ||
955 | ctrl->push_button = 0; | ||
956 | } else { | ||
957 | /* push button supported */ | ||
958 | ctrl->push_button = 1; | ||
959 | } | ||
960 | |||
961 | /* Check for slot switch type (0=mechanical, 1=not mechanical) */ | ||
962 | if (subsystem_deviceid & 0x0004) { | ||
963 | /* no switch */ | ||
964 | ctrl->slot_switch_type = 0; | ||
965 | } else { | ||
966 | /* switch */ | ||
967 | ctrl->slot_switch_type = 1; | ||
968 | } | ||
969 | |||
970 | /* PHP Status (0=De-feature PHP, 1=Normal operation) */ | ||
971 | if (subsystem_deviceid & 0x0008) { | ||
972 | ctrl->defeature_PHP = 1; // PHP supported | ||
973 | } else { | ||
974 | ctrl->defeature_PHP = 0; // PHP not supported | ||
975 | } | ||
976 | |||
977 | /* Alternate Base Address Register Interface (0=not supported, 1=supported) */ | ||
978 | if (subsystem_deviceid & 0x0010) { | ||
979 | ctrl->alternate_base_address = 1; // supported | ||
980 | } else { | ||
981 | ctrl->alternate_base_address = 0; // not supported | ||
982 | } | ||
983 | |||
984 | /* PCI Config Space Index (0=not supported, 1=supported) */ | ||
985 | if (subsystem_deviceid & 0x0020) { | ||
986 | ctrl->pci_config_space = 1; // supported | ||
987 | } else { | ||
988 | ctrl->pci_config_space = 0; // not supported | ||
989 | } | ||
990 | |||
991 | /* PCI-X support */ | ||
992 | if (subsystem_deviceid & 0x0080) { | ||
993 | /* PCI-X capable */ | ||
994 | ctrl->pcix_support = 1; | ||
995 | /* Frequency of operation in PCI-X mode */ | ||
996 | if (subsystem_deviceid & 0x0040) { | ||
997 | /* 133MHz PCI-X if bit 7 is 1 */ | ||
998 | ctrl->pcix_speed_capability = 1; | ||
999 | } else { | ||
1000 | /* 100MHz PCI-X if bit 7 is 1 and bit 0 is 0, */ | ||
1001 | /* 66MHz PCI-X if bit 7 is 1 and bit 0 is 1 */ | ||
1002 | ctrl->pcix_speed_capability = 0; | ||
1003 | } | ||
1004 | } else { | ||
1005 | /* Conventional PCI */ | ||
1006 | ctrl->pcix_support = 0; | ||
1007 | ctrl->pcix_speed_capability = 0; | ||
1008 | } | ||
1009 | break; | ||
1010 | |||
1011 | default: | ||
1012 | err(msg_HPC_not_supported); | ||
1013 | rc = -ENODEV; | ||
1014 | goto err_free_ctrl; | ||
1015 | } | ||
1016 | |||
1017 | } else { | ||
1018 | err(msg_HPC_not_supported); | ||
1019 | return -ENODEV; | ||
1020 | } | ||
1021 | |||
1022 | // Tell the user that we found one. | ||
1023 | info("Initializing the PCI hot plug controller residing on PCI bus %d\n", | ||
1024 | pdev->bus->number); | ||
1025 | |||
1026 | dbg("Hotplug controller capabilities:\n"); | ||
1027 | dbg(" speed_capability %d\n", ctrl->speed_capability); | ||
1028 | dbg(" slot_switch_type %s\n", ctrl->slot_switch_type ? | ||
1029 | "switch present" : "no switch"); | ||
1030 | dbg(" defeature_PHP %s\n", ctrl->defeature_PHP ? | ||
1031 | "PHP supported" : "PHP not supported"); | ||
1032 | dbg(" alternate_base_address %s\n", ctrl->alternate_base_address ? | ||
1033 | "supported" : "not supported"); | ||
1034 | dbg(" pci_config_space %s\n", ctrl->pci_config_space ? | ||
1035 | "supported" : "not supported"); | ||
1036 | dbg(" pcix_speed_capability %s\n", ctrl->pcix_speed_capability ? | ||
1037 | "supported" : "not supported"); | ||
1038 | dbg(" pcix_support %s\n", ctrl->pcix_support ? | ||
1039 | "supported" : "not supported"); | ||
1040 | |||
1041 | ctrl->pci_dev = pdev; | ||
1042 | pci_set_drvdata(pdev, ctrl); | ||
1043 | |||
1044 | /* make our own copy of the pci bus structure, | ||
1045 | * as we like tweaking it a lot */ | ||
1046 | ctrl->pci_bus = kmalloc(sizeof(*ctrl->pci_bus), GFP_KERNEL); | ||
1047 | if (!ctrl->pci_bus) { | ||
1048 | err("out of memory\n"); | ||
1049 | rc = -ENOMEM; | ||
1050 | goto err_free_ctrl; | ||
1051 | } | ||
1052 | memcpy(ctrl->pci_bus, pdev->bus, sizeof(*ctrl->pci_bus)); | ||
1053 | |||
1054 | ctrl->bus = pdev->bus->number; | ||
1055 | ctrl->rev = rev; | ||
1056 | dbg("bus device function rev: %d %d %d %d\n", ctrl->bus, | ||
1057 | PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), ctrl->rev); | ||
1058 | |||
1059 | init_MUTEX(&ctrl->crit_sect); | ||
1060 | init_waitqueue_head(&ctrl->queue); | ||
1061 | |||
1062 | /* initialize our threads if they haven't already been started up */ | ||
1063 | rc = one_time_init(); | ||
1064 | if (rc) { | ||
1065 | goto err_free_bus; | ||
1066 | } | ||
1067 | |||
1068 | dbg("pdev = %p\n", pdev); | ||
1069 | dbg("pci resource start %lx\n", pci_resource_start(pdev, 0)); | ||
1070 | dbg("pci resource len %lx\n", pci_resource_len(pdev, 0)); | ||
1071 | |||
1072 | if (!request_mem_region(pci_resource_start(pdev, 0), | ||
1073 | pci_resource_len(pdev, 0), MY_NAME)) { | ||
1074 | err("cannot reserve MMIO region\n"); | ||
1075 | rc = -ENOMEM; | ||
1076 | goto err_free_bus; | ||
1077 | } | ||
1078 | |||
1079 | ctrl->hpc_reg = ioremap(pci_resource_start(pdev, 0), | ||
1080 | pci_resource_len(pdev, 0)); | ||
1081 | if (!ctrl->hpc_reg) { | ||
1082 | err("cannot remap MMIO region %lx @ %lx\n", | ||
1083 | pci_resource_len(pdev, 0), | ||
1084 | pci_resource_start(pdev, 0)); | ||
1085 | rc = -ENODEV; | ||
1086 | goto err_free_mem_region; | ||
1087 | } | ||
1088 | |||
1089 | // Check for 66Mhz operation | ||
1090 | ctrl->speed = get_controller_speed(ctrl); | ||
1091 | |||
1092 | |||
1093 | /******************************************************** | ||
1094 | * | ||
1095 | * Save configuration headers for this and | ||
1096 | * subordinate PCI buses | ||
1097 | * | ||
1098 | ********************************************************/ | ||
1099 | |||
1100 | // find the physical slot number of the first hot plug slot | ||
1101 | |||
1102 | /* Get slot won't work for devices behind bridges, but | ||
1103 | * in this case it will always be called for the "base" | ||
1104 | * bus/dev/func of a slot. | ||
1105 | * CS: this is leveraging the PCIIRQ routing code from the kernel | ||
1106 | * (pci-pc.c: get_irq_routing_table) */ | ||
1107 | rc = get_slot_mapping(ctrl->pci_bus, pdev->bus->number, | ||
1108 | (readb(ctrl->hpc_reg + SLOT_MASK) >> 4), | ||
1109 | &(ctrl->first_slot)); | ||
1110 | dbg("get_slot_mapping: first_slot = %d, returned = %d\n", | ||
1111 | ctrl->first_slot, rc); | ||
1112 | if (rc) { | ||
1113 | err(msg_initialization_err, rc); | ||
1114 | goto err_iounmap; | ||
1115 | } | ||
1116 | |||
1117 | // Store PCI Config Space for all devices on this bus | ||
1118 | rc = cpqhp_save_config(ctrl, ctrl->bus, readb(ctrl->hpc_reg + SLOT_MASK)); | ||
1119 | if (rc) { | ||
1120 | err("%s: unable to save PCI configuration data, error %d\n", | ||
1121 | __FUNCTION__, rc); | ||
1122 | goto err_iounmap; | ||
1123 | } | ||
1124 | |||
1125 | /* | ||
1126 | * Get IO, memory, and IRQ resources for new devices | ||
1127 | */ | ||
1128 | // The next line is required for cpqhp_find_available_resources | ||
1129 | ctrl->interrupt = pdev->irq; | ||
1130 | if (ctrl->interrupt < 0x10) { | ||
1131 | cpqhp_legacy_mode = 1; | ||
1132 | dbg("System seems to be configured for Full Table Mapped MPS mode\n"); | ||
1133 | } | ||
1134 | |||
1135 | ctrl->cfgspc_irq = 0; | ||
1136 | pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &ctrl->cfgspc_irq); | ||
1137 | |||
1138 | rc = cpqhp_find_available_resources(ctrl, cpqhp_rom_start); | ||
1139 | ctrl->add_support = !rc; | ||
1140 | if (rc) { | ||
1141 | dbg("cpqhp_find_available_resources = 0x%x\n", rc); | ||
1142 | err("unable to locate PCI configuration resources for hot plug add.\n"); | ||
1143 | goto err_iounmap; | ||
1144 | } | ||
1145 | |||
1146 | /* | ||
1147 | * Finish setting up the hot plug ctrl device | ||
1148 | */ | ||
1149 | ctrl->slot_device_offset = readb(ctrl->hpc_reg + SLOT_MASK) >> 4; | ||
1150 | dbg("NumSlots %d \n", ctrl->slot_device_offset); | ||
1151 | |||
1152 | ctrl->next_event = 0; | ||
1153 | |||
1154 | /* Setup the slot information structures */ | ||
1155 | rc = ctrl_slot_setup(ctrl, smbios_start, smbios_table); | ||
1156 | if (rc) { | ||
1157 | err(msg_initialization_err, 6); | ||
1158 | err("%s: unable to save PCI configuration data, error %d\n", | ||
1159 | __FUNCTION__, rc); | ||
1160 | goto err_iounmap; | ||
1161 | } | ||
1162 | |||
1163 | /* Mask all general input interrupts */ | ||
1164 | writel(0xFFFFFFFFL, ctrl->hpc_reg + INT_MASK); | ||
1165 | |||
1166 | /* set up the interrupt */ | ||
1167 | dbg("HPC interrupt = %d \n", ctrl->interrupt); | ||
1168 | if (request_irq(ctrl->interrupt, cpqhp_ctrl_intr, | ||
1169 | SA_SHIRQ, MY_NAME, ctrl)) { | ||
1170 | err("Can't get irq %d for the hotplug pci controller\n", | ||
1171 | ctrl->interrupt); | ||
1172 | rc = -ENODEV; | ||
1173 | goto err_iounmap; | ||
1174 | } | ||
1175 | |||
1176 | /* Enable Shift Out interrupt and clear it, also enable SERR on power fault */ | ||
1177 | temp_word = readw(ctrl->hpc_reg + MISC); | ||
1178 | temp_word |= 0x4006; | ||
1179 | writew(temp_word, ctrl->hpc_reg + MISC); | ||
1180 | |||
1181 | // Changed 05/05/97 to clear all interrupts at start | ||
1182 | writel(0xFFFFFFFFL, ctrl->hpc_reg + INT_INPUT_CLEAR); | ||
1183 | |||
1184 | ctrl->ctrl_int_comp = readl(ctrl->hpc_reg + INT_INPUT_CLEAR); | ||
1185 | |||
1186 | writel(0x0L, ctrl->hpc_reg + INT_MASK); | ||
1187 | |||
1188 | if (!cpqhp_ctrl_list) { | ||
1189 | cpqhp_ctrl_list = ctrl; | ||
1190 | ctrl->next = NULL; | ||
1191 | } else { | ||
1192 | ctrl->next = cpqhp_ctrl_list; | ||
1193 | cpqhp_ctrl_list = ctrl; | ||
1194 | } | ||
1195 | |||
1196 | // turn off empty slots here unless command line option "ON" set | ||
1197 | // Wait for exclusive access to hardware | ||
1198 | down(&ctrl->crit_sect); | ||
1199 | |||
1200 | num_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0F; | ||
1201 | |||
1202 | // find first device number for the ctrl | ||
1203 | device = readb(ctrl->hpc_reg + SLOT_MASK) >> 4; | ||
1204 | |||
1205 | while (num_of_slots) { | ||
1206 | dbg("num_of_slots: %d\n", num_of_slots); | ||
1207 | func = cpqhp_slot_find(ctrl->bus, device, 0); | ||
1208 | if (!func) | ||
1209 | break; | ||
1210 | |||
1211 | hp_slot = func->device - ctrl->slot_device_offset; | ||
1212 | dbg("hp_slot: %d\n", hp_slot); | ||
1213 | |||
1214 | // We have to save the presence info for these slots | ||
1215 | temp_word = ctrl->ctrl_int_comp >> 16; | ||
1216 | func->presence_save = (temp_word >> hp_slot) & 0x01; | ||
1217 | func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02; | ||
1218 | |||
1219 | if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) { | ||
1220 | func->switch_save = 0; | ||
1221 | } else { | ||
1222 | func->switch_save = 0x10; | ||
1223 | } | ||
1224 | |||
1225 | if (!power_mode) { | ||
1226 | if (!func->is_a_board) { | ||
1227 | green_LED_off(ctrl, hp_slot); | ||
1228 | slot_disable(ctrl, hp_slot); | ||
1229 | } | ||
1230 | } | ||
1231 | |||
1232 | device++; | ||
1233 | num_of_slots--; | ||
1234 | } | ||
1235 | |||
1236 | if (!power_mode) { | ||
1237 | set_SOGO(ctrl); | ||
1238 | // Wait for SOBS to be unset | ||
1239 | wait_for_ctrl_irq(ctrl); | ||
1240 | } | ||
1241 | |||
1242 | rc = init_SERR(ctrl); | ||
1243 | if (rc) { | ||
1244 | err("init_SERR failed\n"); | ||
1245 | up(&ctrl->crit_sect); | ||
1246 | goto err_free_irq; | ||
1247 | } | ||
1248 | |||
1249 | // Done with exclusive hardware access | ||
1250 | up(&ctrl->crit_sect); | ||
1251 | |||
1252 | cpqhp_create_ctrl_files(ctrl); | ||
1253 | |||
1254 | return 0; | ||
1255 | |||
1256 | err_free_irq: | ||
1257 | free_irq(ctrl->interrupt, ctrl); | ||
1258 | err_iounmap: | ||
1259 | iounmap(ctrl->hpc_reg); | ||
1260 | err_free_mem_region: | ||
1261 | release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); | ||
1262 | err_free_bus: | ||
1263 | kfree(ctrl->pci_bus); | ||
1264 | err_free_ctrl: | ||
1265 | kfree(ctrl); | ||
1266 | return rc; | ||
1267 | } | ||
1268 | |||
1269 | |||
1270 | static int one_time_init(void) | ||
1271 | { | ||
1272 | int loop; | ||
1273 | int retval = 0; | ||
1274 | static int initialized = 0; | ||
1275 | |||
1276 | if (initialized) | ||
1277 | return 0; | ||
1278 | |||
1279 | power_mode = 0; | ||
1280 | |||
1281 | retval = pci_print_IRQ_route(); | ||
1282 | if (retval) | ||
1283 | goto error; | ||
1284 | |||
1285 | dbg("Initialize + Start the notification mechanism \n"); | ||
1286 | |||
1287 | retval = cpqhp_event_start_thread(); | ||
1288 | if (retval) | ||
1289 | goto error; | ||
1290 | |||
1291 | dbg("Initialize slot lists\n"); | ||
1292 | for (loop = 0; loop < 256; loop++) { | ||
1293 | cpqhp_slot_list[loop] = NULL; | ||
1294 | } | ||
1295 | |||
1296 | // FIXME: We also need to hook the NMI handler eventually. | ||
1297 | // this also needs to be worked with Christoph | ||
1298 | // register_NMI_handler(); | ||
1299 | |||
1300 | // Map rom address | ||
1301 | cpqhp_rom_start = ioremap(ROM_PHY_ADDR, ROM_PHY_LEN); | ||
1302 | if (!cpqhp_rom_start) { | ||
1303 | err ("Could not ioremap memory region for ROM\n"); | ||
1304 | retval = -EIO; | ||
1305 | goto error; | ||
1306 | } | ||
1307 | |||
1308 | /* Now, map the int15 entry point if we are on compaq specific hardware */ | ||
1309 | compaq_nvram_init(cpqhp_rom_start); | ||
1310 | |||
1311 | /* Map smbios table entry point structure */ | ||
1312 | smbios_table = detect_SMBIOS_pointer(cpqhp_rom_start, | ||
1313 | cpqhp_rom_start + ROM_PHY_LEN); | ||
1314 | if (!smbios_table) { | ||
1315 | err ("Could not find the SMBIOS pointer in memory\n"); | ||
1316 | retval = -EIO; | ||
1317 | goto error_rom_start; | ||
1318 | } | ||
1319 | |||
1320 | smbios_start = ioremap(readl(smbios_table + ST_ADDRESS), | ||
1321 | readw(smbios_table + ST_LENGTH)); | ||
1322 | if (!smbios_start) { | ||
1323 | err ("Could not ioremap memory region taken from SMBIOS values\n"); | ||
1324 | retval = -EIO; | ||
1325 | goto error_smbios_start; | ||
1326 | } | ||
1327 | |||
1328 | initialized = 1; | ||
1329 | |||
1330 | return retval; | ||
1331 | |||
1332 | error_smbios_start: | ||
1333 | iounmap(smbios_start); | ||
1334 | error_rom_start: | ||
1335 | iounmap(cpqhp_rom_start); | ||
1336 | error: | ||
1337 | return retval; | ||
1338 | } | ||
1339 | |||
1340 | |||
1341 | static void __exit unload_cpqphpd(void) | ||
1342 | { | ||
1343 | struct pci_func *next; | ||
1344 | struct pci_func *TempSlot; | ||
1345 | int loop; | ||
1346 | u32 rc; | ||
1347 | struct controller *ctrl; | ||
1348 | struct controller *tctrl; | ||
1349 | struct pci_resource *res; | ||
1350 | struct pci_resource *tres; | ||
1351 | |||
1352 | rc = compaq_nvram_store(cpqhp_rom_start); | ||
1353 | |||
1354 | ctrl = cpqhp_ctrl_list; | ||
1355 | |||
1356 | while (ctrl) { | ||
1357 | if (ctrl->hpc_reg) { | ||
1358 | u16 misc; | ||
1359 | rc = read_slot_enable (ctrl); | ||
1360 | |||
1361 | writeb(0, ctrl->hpc_reg + SLOT_SERR); | ||
1362 | writel(0xFFFFFFC0L | ~rc, ctrl->hpc_reg + INT_MASK); | ||
1363 | |||
1364 | misc = readw(ctrl->hpc_reg + MISC); | ||
1365 | misc &= 0xFFFD; | ||
1366 | writew(misc, ctrl->hpc_reg + MISC); | ||
1367 | } | ||
1368 | |||
1369 | ctrl_slot_cleanup(ctrl); | ||
1370 | |||
1371 | res = ctrl->io_head; | ||
1372 | while (res) { | ||
1373 | tres = res; | ||
1374 | res = res->next; | ||
1375 | kfree(tres); | ||
1376 | } | ||
1377 | |||
1378 | res = ctrl->mem_head; | ||
1379 | while (res) { | ||
1380 | tres = res; | ||
1381 | res = res->next; | ||
1382 | kfree(tres); | ||
1383 | } | ||
1384 | |||
1385 | res = ctrl->p_mem_head; | ||
1386 | while (res) { | ||
1387 | tres = res; | ||
1388 | res = res->next; | ||
1389 | kfree(tres); | ||
1390 | } | ||
1391 | |||
1392 | res = ctrl->bus_head; | ||
1393 | while (res) { | ||
1394 | tres = res; | ||
1395 | res = res->next; | ||
1396 | kfree(tres); | ||
1397 | } | ||
1398 | |||
1399 | kfree (ctrl->pci_bus); | ||
1400 | |||
1401 | tctrl = ctrl; | ||
1402 | ctrl = ctrl->next; | ||
1403 | kfree(tctrl); | ||
1404 | } | ||
1405 | |||
1406 | for (loop = 0; loop < 256; loop++) { | ||
1407 | next = cpqhp_slot_list[loop]; | ||
1408 | while (next != NULL) { | ||
1409 | res = next->io_head; | ||
1410 | while (res) { | ||
1411 | tres = res; | ||
1412 | res = res->next; | ||
1413 | kfree(tres); | ||
1414 | } | ||
1415 | |||
1416 | res = next->mem_head; | ||
1417 | while (res) { | ||
1418 | tres = res; | ||
1419 | res = res->next; | ||
1420 | kfree(tres); | ||
1421 | } | ||
1422 | |||
1423 | res = next->p_mem_head; | ||
1424 | while (res) { | ||
1425 | tres = res; | ||
1426 | res = res->next; | ||
1427 | kfree(tres); | ||
1428 | } | ||
1429 | |||
1430 | res = next->bus_head; | ||
1431 | while (res) { | ||
1432 | tres = res; | ||
1433 | res = res->next; | ||
1434 | kfree(tres); | ||
1435 | } | ||
1436 | |||
1437 | TempSlot = next; | ||
1438 | next = next->next; | ||
1439 | kfree(TempSlot); | ||
1440 | } | ||
1441 | } | ||
1442 | |||
1443 | // Stop the notification mechanism | ||
1444 | cpqhp_event_stop_thread(); | ||
1445 | |||
1446 | //unmap the rom address | ||
1447 | if (cpqhp_rom_start) | ||
1448 | iounmap(cpqhp_rom_start); | ||
1449 | if (smbios_start) | ||
1450 | iounmap(smbios_start); | ||
1451 | } | ||
1452 | |||
1453 | |||
1454 | |||
1455 | static struct pci_device_id hpcd_pci_tbl[] = { | ||
1456 | { | ||
1457 | /* handle any PCI Hotplug controller */ | ||
1458 | .class = ((PCI_CLASS_SYSTEM_PCI_HOTPLUG << 8) | 0x00), | ||
1459 | .class_mask = ~0, | ||
1460 | |||
1461 | /* no matter who makes it */ | ||
1462 | .vendor = PCI_ANY_ID, | ||
1463 | .device = PCI_ANY_ID, | ||
1464 | .subvendor = PCI_ANY_ID, | ||
1465 | .subdevice = PCI_ANY_ID, | ||
1466 | |||
1467 | }, { /* end: all zeroes */ } | ||
1468 | }; | ||
1469 | |||
1470 | MODULE_DEVICE_TABLE(pci, hpcd_pci_tbl); | ||
1471 | |||
1472 | |||
1473 | |||
1474 | static struct pci_driver cpqhpc_driver = { | ||
1475 | .name = "compaq_pci_hotplug", | ||
1476 | .id_table = hpcd_pci_tbl, | ||
1477 | .probe = cpqhpc_probe, | ||
1478 | /* remove: cpqhpc_remove_one, */ | ||
1479 | }; | ||
1480 | |||
1481 | |||
1482 | |||
1483 | static int __init cpqhpc_init(void) | ||
1484 | { | ||
1485 | int result; | ||
1486 | |||
1487 | cpqhp_debug = debug; | ||
1488 | |||
1489 | info (DRIVER_DESC " version: " DRIVER_VERSION "\n"); | ||
1490 | result = pci_register_driver(&cpqhpc_driver); | ||
1491 | dbg("pci_register_driver = %d\n", result); | ||
1492 | return result; | ||
1493 | } | ||
1494 | |||
1495 | |||
1496 | static void __exit cpqhpc_cleanup(void) | ||
1497 | { | ||
1498 | dbg("unload_cpqphpd()\n"); | ||
1499 | unload_cpqphpd(); | ||
1500 | |||
1501 | dbg("pci_unregister_driver\n"); | ||
1502 | pci_unregister_driver(&cpqhpc_driver); | ||
1503 | } | ||
1504 | |||
1505 | |||
1506 | module_init(cpqhpc_init); | ||
1507 | module_exit(cpqhpc_cleanup); | ||
1508 | |||
1509 | |||
diff --git a/drivers/pci/hotplug/cpqphp_ctrl.c b/drivers/pci/hotplug/cpqphp_ctrl.c new file mode 100644 index 000000000000..10a5a7674a8a --- /dev/null +++ b/drivers/pci/hotplug/cpqphp_ctrl.c | |||
@@ -0,0 +1,3096 @@ | |||
1 | /* | ||
2 | * Compaq Hot Plug Controller Driver | ||
3 | * | ||
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | ||
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * Copyright (C) 2001 IBM Corp. | ||
7 | * | ||
8 | * All rights reserved. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or (at | ||
13 | * your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
18 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
19 | * details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
24 | * | ||
25 | * Send feedback to <greg@kroah.com> | ||
26 | * | ||
27 | */ | ||
28 | |||
29 | #include <linux/config.h> | ||
30 | #include <linux/module.h> | ||
31 | #include <linux/kernel.h> | ||
32 | #include <linux/types.h> | ||
33 | #include <linux/slab.h> | ||
34 | #include <linux/workqueue.h> | ||
35 | #include <linux/interrupt.h> | ||
36 | #include <linux/delay.h> | ||
37 | #include <linux/wait.h> | ||
38 | #include <linux/smp_lock.h> | ||
39 | #include <linux/pci.h> | ||
40 | #include "cpqphp.h" | ||
41 | |||
42 | static u32 configure_new_device(struct controller* ctrl, struct pci_func *func, | ||
43 | u8 behind_bridge, struct resource_lists *resources); | ||
44 | static int configure_new_function(struct controller* ctrl, struct pci_func *func, | ||
45 | u8 behind_bridge, struct resource_lists *resources); | ||
46 | static void interrupt_event_handler(struct controller *ctrl); | ||
47 | |||
48 | static struct semaphore event_semaphore; /* mutex for process loop (up if something to process) */ | ||
49 | static struct semaphore event_exit; /* guard ensure thread has exited before calling it quits */ | ||
50 | static int event_finished; | ||
51 | static unsigned long pushbutton_pending; /* = 0 */ | ||
52 | |||
53 | /* things needed for the long_delay function */ | ||
54 | static struct semaphore delay_sem; | ||
55 | static wait_queue_head_t delay_wait; | ||
56 | |||
57 | /* delay is in jiffies to wait for */ | ||
58 | static void long_delay(int delay) | ||
59 | { | ||
60 | DECLARE_WAITQUEUE(wait, current); | ||
61 | |||
62 | /* only allow 1 customer into the delay queue at once | ||
63 | * yes this makes some people wait even longer, but who really cares? | ||
64 | * this is for _huge_ delays to make the hardware happy as the | ||
65 | * signals bounce around | ||
66 | */ | ||
67 | down (&delay_sem); | ||
68 | |||
69 | init_waitqueue_head(&delay_wait); | ||
70 | |||
71 | add_wait_queue(&delay_wait, &wait); | ||
72 | msleep_interruptible(jiffies_to_msecs(delay)); | ||
73 | remove_wait_queue(&delay_wait, &wait); | ||
74 | |||
75 | up(&delay_sem); | ||
76 | } | ||
77 | |||
78 | |||
79 | /* FIXME: The following line needs to be somewhere else... */ | ||
80 | #define WRONG_BUS_FREQUENCY 0x07 | ||
81 | static u8 handle_switch_change(u8 change, struct controller * ctrl) | ||
82 | { | ||
83 | int hp_slot; | ||
84 | u8 rc = 0; | ||
85 | u16 temp_word; | ||
86 | struct pci_func *func; | ||
87 | struct event_info *taskInfo; | ||
88 | |||
89 | if (!change) | ||
90 | return 0; | ||
91 | |||
92 | /* Switch Change */ | ||
93 | dbg("cpqsbd: Switch interrupt received.\n"); | ||
94 | |||
95 | for (hp_slot = 0; hp_slot < 6; hp_slot++) { | ||
96 | if (change & (0x1L << hp_slot)) { | ||
97 | /********************************** | ||
98 | * this one changed. | ||
99 | **********************************/ | ||
100 | func = cpqhp_slot_find(ctrl->bus, | ||
101 | (hp_slot + ctrl->slot_device_offset), 0); | ||
102 | |||
103 | /* this is the structure that tells the worker thread | ||
104 | *what to do */ | ||
105 | taskInfo = &(ctrl->event_queue[ctrl->next_event]); | ||
106 | ctrl->next_event = (ctrl->next_event + 1) % 10; | ||
107 | taskInfo->hp_slot = hp_slot; | ||
108 | |||
109 | rc++; | ||
110 | |||
111 | temp_word = ctrl->ctrl_int_comp >> 16; | ||
112 | func->presence_save = (temp_word >> hp_slot) & 0x01; | ||
113 | func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02; | ||
114 | |||
115 | if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) { | ||
116 | /********************************** | ||
117 | * Switch opened | ||
118 | **********************************/ | ||
119 | |||
120 | func->switch_save = 0; | ||
121 | |||
122 | taskInfo->event_type = INT_SWITCH_OPEN; | ||
123 | } else { | ||
124 | /********************************** | ||
125 | * Switch closed | ||
126 | **********************************/ | ||
127 | |||
128 | func->switch_save = 0x10; | ||
129 | |||
130 | taskInfo->event_type = INT_SWITCH_CLOSE; | ||
131 | } | ||
132 | } | ||
133 | } | ||
134 | |||
135 | return rc; | ||
136 | } | ||
137 | |||
138 | /** | ||
139 | * cpqhp_find_slot: find the struct slot of given device | ||
140 | * @ctrl: scan lots of this controller | ||
141 | * @device: the device id to find | ||
142 | */ | ||
143 | static struct slot *cpqhp_find_slot(struct controller *ctrl, u8 device) | ||
144 | { | ||
145 | struct slot *slot = ctrl->slot; | ||
146 | |||
147 | while (slot && (slot->device != device)) { | ||
148 | slot = slot->next; | ||
149 | } | ||
150 | |||
151 | return slot; | ||
152 | } | ||
153 | |||
154 | |||
155 | static u8 handle_presence_change(u16 change, struct controller * ctrl) | ||
156 | { | ||
157 | int hp_slot; | ||
158 | u8 rc = 0; | ||
159 | u8 temp_byte; | ||
160 | u16 temp_word; | ||
161 | struct pci_func *func; | ||
162 | struct event_info *taskInfo; | ||
163 | struct slot *p_slot; | ||
164 | |||
165 | if (!change) | ||
166 | return 0; | ||
167 | |||
168 | /********************************** | ||
169 | * Presence Change | ||
170 | **********************************/ | ||
171 | dbg("cpqsbd: Presence/Notify input change.\n"); | ||
172 | dbg(" Changed bits are 0x%4.4x\n", change ); | ||
173 | |||
174 | for (hp_slot = 0; hp_slot < 6; hp_slot++) { | ||
175 | if (change & (0x0101 << hp_slot)) { | ||
176 | /********************************** | ||
177 | * this one changed. | ||
178 | **********************************/ | ||
179 | func = cpqhp_slot_find(ctrl->bus, | ||
180 | (hp_slot + ctrl->slot_device_offset), 0); | ||
181 | |||
182 | taskInfo = &(ctrl->event_queue[ctrl->next_event]); | ||
183 | ctrl->next_event = (ctrl->next_event + 1) % 10; | ||
184 | taskInfo->hp_slot = hp_slot; | ||
185 | |||
186 | rc++; | ||
187 | |||
188 | p_slot = cpqhp_find_slot(ctrl, hp_slot + (readb(ctrl->hpc_reg + SLOT_MASK) >> 4)); | ||
189 | if (!p_slot) | ||
190 | return 0; | ||
191 | |||
192 | /* If the switch closed, must be a button | ||
193 | * If not in button mode, nevermind */ | ||
194 | if (func->switch_save && (ctrl->push_button == 1)) { | ||
195 | temp_word = ctrl->ctrl_int_comp >> 16; | ||
196 | temp_byte = (temp_word >> hp_slot) & 0x01; | ||
197 | temp_byte |= (temp_word >> (hp_slot + 7)) & 0x02; | ||
198 | |||
199 | if (temp_byte != func->presence_save) { | ||
200 | /************************************** | ||
201 | * button Pressed (doesn't do anything) | ||
202 | **************************************/ | ||
203 | dbg("hp_slot %d button pressed\n", hp_slot); | ||
204 | taskInfo->event_type = INT_BUTTON_PRESS; | ||
205 | } else { | ||
206 | /********************************** | ||
207 | * button Released - TAKE ACTION!!!! | ||
208 | **********************************/ | ||
209 | dbg("hp_slot %d button released\n", hp_slot); | ||
210 | taskInfo->event_type = INT_BUTTON_RELEASE; | ||
211 | |||
212 | /* Cancel if we are still blinking */ | ||
213 | if ((p_slot->state == BLINKINGON_STATE) | ||
214 | || (p_slot->state == BLINKINGOFF_STATE)) { | ||
215 | taskInfo->event_type = INT_BUTTON_CANCEL; | ||
216 | dbg("hp_slot %d button cancel\n", hp_slot); | ||
217 | } else if ((p_slot->state == POWERON_STATE) | ||
218 | || (p_slot->state == POWEROFF_STATE)) { | ||
219 | /* info(msg_button_ignore, p_slot->number); */ | ||
220 | taskInfo->event_type = INT_BUTTON_IGNORE; | ||
221 | dbg("hp_slot %d button ignore\n", hp_slot); | ||
222 | } | ||
223 | } | ||
224 | } else { | ||
225 | /* Switch is open, assume a presence change | ||
226 | * Save the presence state */ | ||
227 | temp_word = ctrl->ctrl_int_comp >> 16; | ||
228 | func->presence_save = (temp_word >> hp_slot) & 0x01; | ||
229 | func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02; | ||
230 | |||
231 | if ((!(ctrl->ctrl_int_comp & (0x010000 << hp_slot))) || | ||
232 | (!(ctrl->ctrl_int_comp & (0x01000000 << hp_slot)))) { | ||
233 | /* Present */ | ||
234 | taskInfo->event_type = INT_PRESENCE_ON; | ||
235 | } else { | ||
236 | /* Not Present */ | ||
237 | taskInfo->event_type = INT_PRESENCE_OFF; | ||
238 | } | ||
239 | } | ||
240 | } | ||
241 | } | ||
242 | |||
243 | return rc; | ||
244 | } | ||
245 | |||
246 | |||
247 | static u8 handle_power_fault(u8 change, struct controller * ctrl) | ||
248 | { | ||
249 | int hp_slot; | ||
250 | u8 rc = 0; | ||
251 | struct pci_func *func; | ||
252 | struct event_info *taskInfo; | ||
253 | |||
254 | if (!change) | ||
255 | return 0; | ||
256 | |||
257 | /********************************** | ||
258 | * power fault | ||
259 | **********************************/ | ||
260 | |||
261 | info("power fault interrupt\n"); | ||
262 | |||
263 | for (hp_slot = 0; hp_slot < 6; hp_slot++) { | ||
264 | if (change & (0x01 << hp_slot)) { | ||
265 | /********************************** | ||
266 | * this one changed. | ||
267 | **********************************/ | ||
268 | func = cpqhp_slot_find(ctrl->bus, | ||
269 | (hp_slot + ctrl->slot_device_offset), 0); | ||
270 | |||
271 | taskInfo = &(ctrl->event_queue[ctrl->next_event]); | ||
272 | ctrl->next_event = (ctrl->next_event + 1) % 10; | ||
273 | taskInfo->hp_slot = hp_slot; | ||
274 | |||
275 | rc++; | ||
276 | |||
277 | if (ctrl->ctrl_int_comp & (0x00000100 << hp_slot)) { | ||
278 | /********************************** | ||
279 | * power fault Cleared | ||
280 | **********************************/ | ||
281 | func->status = 0x00; | ||
282 | |||
283 | taskInfo->event_type = INT_POWER_FAULT_CLEAR; | ||
284 | } else { | ||
285 | /********************************** | ||
286 | * power fault | ||
287 | **********************************/ | ||
288 | taskInfo->event_type = INT_POWER_FAULT; | ||
289 | |||
290 | if (ctrl->rev < 4) { | ||
291 | amber_LED_on (ctrl, hp_slot); | ||
292 | green_LED_off (ctrl, hp_slot); | ||
293 | set_SOGO (ctrl); | ||
294 | |||
295 | /* this is a fatal condition, we want | ||
296 | * to crash the machine to protect from | ||
297 | * data corruption. simulated_NMI | ||
298 | * shouldn't ever return */ | ||
299 | /* FIXME | ||
300 | simulated_NMI(hp_slot, ctrl); */ | ||
301 | |||
302 | /* The following code causes a software | ||
303 | * crash just in case simulated_NMI did | ||
304 | * return */ | ||
305 | /*FIXME | ||
306 | panic(msg_power_fault); */ | ||
307 | } else { | ||
308 | /* set power fault status for this board */ | ||
309 | func->status = 0xFF; | ||
310 | info("power fault bit %x set\n", hp_slot); | ||
311 | } | ||
312 | } | ||
313 | } | ||
314 | } | ||
315 | |||
316 | return rc; | ||
317 | } | ||
318 | |||
319 | |||
320 | /** | ||
321 | * sort_by_size: sort nodes on the list by their length, smallest first. | ||
322 | * @head: list to sort | ||
323 | * | ||
324 | */ | ||
325 | static int sort_by_size(struct pci_resource **head) | ||
326 | { | ||
327 | struct pci_resource *current_res; | ||
328 | struct pci_resource *next_res; | ||
329 | int out_of_order = 1; | ||
330 | |||
331 | if (!(*head)) | ||
332 | return 1; | ||
333 | |||
334 | if (!((*head)->next)) | ||
335 | return 0; | ||
336 | |||
337 | while (out_of_order) { | ||
338 | out_of_order = 0; | ||
339 | |||
340 | /* Special case for swapping list head */ | ||
341 | if (((*head)->next) && | ||
342 | ((*head)->length > (*head)->next->length)) { | ||
343 | out_of_order++; | ||
344 | current_res = *head; | ||
345 | *head = (*head)->next; | ||
346 | current_res->next = (*head)->next; | ||
347 | (*head)->next = current_res; | ||
348 | } | ||
349 | |||
350 | current_res = *head; | ||
351 | |||
352 | while (current_res->next && current_res->next->next) { | ||
353 | if (current_res->next->length > current_res->next->next->length) { | ||
354 | out_of_order++; | ||
355 | next_res = current_res->next; | ||
356 | current_res->next = current_res->next->next; | ||
357 | current_res = current_res->next; | ||
358 | next_res->next = current_res->next; | ||
359 | current_res->next = next_res; | ||
360 | } else | ||
361 | current_res = current_res->next; | ||
362 | } | ||
363 | } /* End of out_of_order loop */ | ||
364 | |||
365 | return 0; | ||
366 | } | ||
367 | |||
368 | |||
369 | /** | ||
370 | * sort_by_max_size: sort nodes on the list by their length, largest first. | ||
371 | * @head: list to sort | ||
372 | * | ||
373 | */ | ||
374 | static int sort_by_max_size(struct pci_resource **head) | ||
375 | { | ||
376 | struct pci_resource *current_res; | ||
377 | struct pci_resource *next_res; | ||
378 | int out_of_order = 1; | ||
379 | |||
380 | if (!(*head)) | ||
381 | return 1; | ||
382 | |||
383 | if (!((*head)->next)) | ||
384 | return 0; | ||
385 | |||
386 | while (out_of_order) { | ||
387 | out_of_order = 0; | ||
388 | |||
389 | /* Special case for swapping list head */ | ||
390 | if (((*head)->next) && | ||
391 | ((*head)->length < (*head)->next->length)) { | ||
392 | out_of_order++; | ||
393 | current_res = *head; | ||
394 | *head = (*head)->next; | ||
395 | current_res->next = (*head)->next; | ||
396 | (*head)->next = current_res; | ||
397 | } | ||
398 | |||
399 | current_res = *head; | ||
400 | |||
401 | while (current_res->next && current_res->next->next) { | ||
402 | if (current_res->next->length < current_res->next->next->length) { | ||
403 | out_of_order++; | ||
404 | next_res = current_res->next; | ||
405 | current_res->next = current_res->next->next; | ||
406 | current_res = current_res->next; | ||
407 | next_res->next = current_res->next; | ||
408 | current_res->next = next_res; | ||
409 | } else | ||
410 | current_res = current_res->next; | ||
411 | } | ||
412 | } /* End of out_of_order loop */ | ||
413 | |||
414 | return 0; | ||
415 | } | ||
416 | |||
417 | |||
418 | /** | ||
419 | * do_pre_bridge_resource_split: find node of resources that are unused | ||
420 | * | ||
421 | */ | ||
422 | static struct pci_resource *do_pre_bridge_resource_split(struct pci_resource **head, | ||
423 | struct pci_resource **orig_head, u32 alignment) | ||
424 | { | ||
425 | struct pci_resource *prevnode = NULL; | ||
426 | struct pci_resource *node; | ||
427 | struct pci_resource *split_node; | ||
428 | u32 rc; | ||
429 | u32 temp_dword; | ||
430 | dbg("do_pre_bridge_resource_split\n"); | ||
431 | |||
432 | if (!(*head) || !(*orig_head)) | ||
433 | return NULL; | ||
434 | |||
435 | rc = cpqhp_resource_sort_and_combine(head); | ||
436 | |||
437 | if (rc) | ||
438 | return NULL; | ||
439 | |||
440 | if ((*head)->base != (*orig_head)->base) | ||
441 | return NULL; | ||
442 | |||
443 | if ((*head)->length == (*orig_head)->length) | ||
444 | return NULL; | ||
445 | |||
446 | |||
447 | /* If we got here, there the bridge requires some of the resource, but | ||
448 | * we may be able to split some off of the front */ | ||
449 | |||
450 | node = *head; | ||
451 | |||
452 | if (node->length & (alignment -1)) { | ||
453 | /* this one isn't an aligned length, so we'll make a new entry | ||
454 | * and split it up. */ | ||
455 | split_node = kmalloc(sizeof(*split_node), GFP_KERNEL); | ||
456 | |||
457 | if (!split_node) | ||
458 | return NULL; | ||
459 | |||
460 | temp_dword = (node->length | (alignment-1)) + 1 - alignment; | ||
461 | |||
462 | split_node->base = node->base; | ||
463 | split_node->length = temp_dword; | ||
464 | |||
465 | node->length -= temp_dword; | ||
466 | node->base += split_node->length; | ||
467 | |||
468 | /* Put it in the list */ | ||
469 | *head = split_node; | ||
470 | split_node->next = node; | ||
471 | } | ||
472 | |||
473 | if (node->length < alignment) | ||
474 | return NULL; | ||
475 | |||
476 | /* Now unlink it */ | ||
477 | if (*head == node) { | ||
478 | *head = node->next; | ||
479 | } else { | ||
480 | prevnode = *head; | ||
481 | while (prevnode->next != node) | ||
482 | prevnode = prevnode->next; | ||
483 | |||
484 | prevnode->next = node->next; | ||
485 | } | ||
486 | node->next = NULL; | ||
487 | |||
488 | return node; | ||
489 | } | ||
490 | |||
491 | |||
492 | /** | ||
493 | * do_bridge_resource_split: find one node of resources that aren't in use | ||
494 | * | ||
495 | */ | ||
496 | static struct pci_resource *do_bridge_resource_split(struct pci_resource **head, u32 alignment) | ||
497 | { | ||
498 | struct pci_resource *prevnode = NULL; | ||
499 | struct pci_resource *node; | ||
500 | u32 rc; | ||
501 | u32 temp_dword; | ||
502 | |||
503 | rc = cpqhp_resource_sort_and_combine(head); | ||
504 | |||
505 | if (rc) | ||
506 | return NULL; | ||
507 | |||
508 | node = *head; | ||
509 | |||
510 | while (node->next) { | ||
511 | prevnode = node; | ||
512 | node = node->next; | ||
513 | kfree(prevnode); | ||
514 | } | ||
515 | |||
516 | if (node->length < alignment) | ||
517 | goto error; | ||
518 | |||
519 | if (node->base & (alignment - 1)) { | ||
520 | /* Short circuit if adjusted size is too small */ | ||
521 | temp_dword = (node->base | (alignment-1)) + 1; | ||
522 | if ((node->length - (temp_dword - node->base)) < alignment) | ||
523 | goto error; | ||
524 | |||
525 | node->length -= (temp_dword - node->base); | ||
526 | node->base = temp_dword; | ||
527 | } | ||
528 | |||
529 | if (node->length & (alignment - 1)) | ||
530 | /* There's stuff in use after this node */ | ||
531 | goto error; | ||
532 | |||
533 | return node; | ||
534 | error: | ||
535 | kfree(node); | ||
536 | return NULL; | ||
537 | } | ||
538 | |||
539 | |||
540 | /** | ||
541 | * get_io_resource: find first node of given size not in ISA aliasing window. | ||
542 | * @head: list to search | ||
543 | * @size: size of node to find, must be a power of two. | ||
544 | * | ||
545 | * Description: this function sorts the resource list by size and then returns | ||
546 | * returns the first node of "size" length that is not in the ISA aliasing | ||
547 | * window. If it finds a node larger than "size" it will split it up. | ||
548 | * | ||
549 | */ | ||
550 | static struct pci_resource *get_io_resource(struct pci_resource **head, u32 size) | ||
551 | { | ||
552 | struct pci_resource *prevnode; | ||
553 | struct pci_resource *node; | ||
554 | struct pci_resource *split_node; | ||
555 | u32 temp_dword; | ||
556 | |||
557 | if (!(*head)) | ||
558 | return NULL; | ||
559 | |||
560 | if ( cpqhp_resource_sort_and_combine(head) ) | ||
561 | return NULL; | ||
562 | |||
563 | if ( sort_by_size(head) ) | ||
564 | return NULL; | ||
565 | |||
566 | for (node = *head; node; node = node->next) { | ||
567 | if (node->length < size) | ||
568 | continue; | ||
569 | |||
570 | if (node->base & (size - 1)) { | ||
571 | /* this one isn't base aligned properly | ||
572 | * so we'll make a new entry and split it up */ | ||
573 | temp_dword = (node->base | (size-1)) + 1; | ||
574 | |||
575 | /* Short circuit if adjusted size is too small */ | ||
576 | if ((node->length - (temp_dword - node->base)) < size) | ||
577 | continue; | ||
578 | |||
579 | split_node = kmalloc(sizeof(*split_node), GFP_KERNEL); | ||
580 | |||
581 | if (!split_node) | ||
582 | return NULL; | ||
583 | |||
584 | split_node->base = node->base; | ||
585 | split_node->length = temp_dword - node->base; | ||
586 | node->base = temp_dword; | ||
587 | node->length -= split_node->length; | ||
588 | |||
589 | /* Put it in the list */ | ||
590 | split_node->next = node->next; | ||
591 | node->next = split_node; | ||
592 | } /* End of non-aligned base */ | ||
593 | |||
594 | /* Don't need to check if too small since we already did */ | ||
595 | if (node->length > size) { | ||
596 | /* this one is longer than we need | ||
597 | * so we'll make a new entry and split it up */ | ||
598 | split_node = kmalloc(sizeof(*split_node), GFP_KERNEL); | ||
599 | |||
600 | if (!split_node) | ||
601 | return NULL; | ||
602 | |||
603 | split_node->base = node->base + size; | ||
604 | split_node->length = node->length - size; | ||
605 | node->length = size; | ||
606 | |||
607 | /* Put it in the list */ | ||
608 | split_node->next = node->next; | ||
609 | node->next = split_node; | ||
610 | } /* End of too big on top end */ | ||
611 | |||
612 | /* For IO make sure it's not in the ISA aliasing space */ | ||
613 | if (node->base & 0x300L) | ||
614 | continue; | ||
615 | |||
616 | /* If we got here, then it is the right size | ||
617 | * Now take it out of the list and break */ | ||
618 | if (*head == node) { | ||
619 | *head = node->next; | ||
620 | } else { | ||
621 | prevnode = *head; | ||
622 | while (prevnode->next != node) | ||
623 | prevnode = prevnode->next; | ||
624 | |||
625 | prevnode->next = node->next; | ||
626 | } | ||
627 | node->next = NULL; | ||
628 | break; | ||
629 | } | ||
630 | |||
631 | return node; | ||
632 | } | ||
633 | |||
634 | |||
635 | /** | ||
636 | * get_max_resource: get largest node which has at least the given size. | ||
637 | * @head: the list to search the node in | ||
638 | * @size: the minimum size of the node to find | ||
639 | * | ||
640 | * Description: Gets the largest node that is at least "size" big from the | ||
641 | * list pointed to by head. It aligns the node on top and bottom | ||
642 | * to "size" alignment before returning it. | ||
643 | */ | ||
644 | static struct pci_resource *get_max_resource(struct pci_resource **head, u32 size) | ||
645 | { | ||
646 | struct pci_resource *max; | ||
647 | struct pci_resource *temp; | ||
648 | struct pci_resource *split_node; | ||
649 | u32 temp_dword; | ||
650 | |||
651 | if (cpqhp_resource_sort_and_combine(head)) | ||
652 | return NULL; | ||
653 | |||
654 | if (sort_by_max_size(head)) | ||
655 | return NULL; | ||
656 | |||
657 | for (max = *head; max; max = max->next) { | ||
658 | /* If not big enough we could probably just bail, | ||
659 | * instead we'll continue to the next. */ | ||
660 | if (max->length < size) | ||
661 | continue; | ||
662 | |||
663 | if (max->base & (size - 1)) { | ||
664 | /* this one isn't base aligned properly | ||
665 | * so we'll make a new entry and split it up */ | ||
666 | temp_dword = (max->base | (size-1)) + 1; | ||
667 | |||
668 | /* Short circuit if adjusted size is too small */ | ||
669 | if ((max->length - (temp_dword - max->base)) < size) | ||
670 | continue; | ||
671 | |||
672 | split_node = kmalloc(sizeof(*split_node), GFP_KERNEL); | ||
673 | |||
674 | if (!split_node) | ||
675 | return NULL; | ||
676 | |||
677 | split_node->base = max->base; | ||
678 | split_node->length = temp_dword - max->base; | ||
679 | max->base = temp_dword; | ||
680 | max->length -= split_node->length; | ||
681 | |||
682 | split_node->next = max->next; | ||
683 | max->next = split_node; | ||
684 | } | ||
685 | |||
686 | if ((max->base + max->length) & (size - 1)) { | ||
687 | /* this one isn't end aligned properly at the top | ||
688 | * so we'll make a new entry and split it up */ | ||
689 | split_node = kmalloc(sizeof(*split_node), GFP_KERNEL); | ||
690 | |||
691 | if (!split_node) | ||
692 | return NULL; | ||
693 | temp_dword = ((max->base + max->length) & ~(size - 1)); | ||
694 | split_node->base = temp_dword; | ||
695 | split_node->length = max->length + max->base | ||
696 | - split_node->base; | ||
697 | max->length -= split_node->length; | ||
698 | |||
699 | split_node->next = max->next; | ||
700 | max->next = split_node; | ||
701 | } | ||
702 | |||
703 | /* Make sure it didn't shrink too much when we aligned it */ | ||
704 | if (max->length < size) | ||
705 | continue; | ||
706 | |||
707 | /* Now take it out of the list */ | ||
708 | temp = *head; | ||
709 | if (temp == max) { | ||
710 | *head = max->next; | ||
711 | } else { | ||
712 | while (temp && temp->next != max) { | ||
713 | temp = temp->next; | ||
714 | } | ||
715 | |||
716 | temp->next = max->next; | ||
717 | } | ||
718 | |||
719 | max->next = NULL; | ||
720 | break; | ||
721 | } | ||
722 | |||
723 | return max; | ||
724 | } | ||
725 | |||
726 | |||
727 | /** | ||
728 | * get_resource: find resource of given size and split up larger ones. | ||
729 | * @head: the list to search for resources | ||
730 | * @size: the size limit to use | ||
731 | * | ||
732 | * Description: This function sorts the resource list by size and then | ||
733 | * returns the first node of "size" length. If it finds a node | ||
734 | * larger than "size" it will split it up. | ||
735 | * | ||
736 | * size must be a power of two. | ||
737 | */ | ||
738 | static struct pci_resource *get_resource(struct pci_resource **head, u32 size) | ||
739 | { | ||
740 | struct pci_resource *prevnode; | ||
741 | struct pci_resource *node; | ||
742 | struct pci_resource *split_node; | ||
743 | u32 temp_dword; | ||
744 | |||
745 | if (cpqhp_resource_sort_and_combine(head)) | ||
746 | return NULL; | ||
747 | |||
748 | if (sort_by_size(head)) | ||
749 | return NULL; | ||
750 | |||
751 | for (node = *head; node; node = node->next) { | ||
752 | dbg("%s: req_size =%x node=%p, base=%x, length=%x\n", | ||
753 | __FUNCTION__, size, node, node->base, node->length); | ||
754 | if (node->length < size) | ||
755 | continue; | ||
756 | |||
757 | if (node->base & (size - 1)) { | ||
758 | dbg("%s: not aligned\n", __FUNCTION__); | ||
759 | /* this one isn't base aligned properly | ||
760 | * so we'll make a new entry and split it up */ | ||
761 | temp_dword = (node->base | (size-1)) + 1; | ||
762 | |||
763 | /* Short circuit if adjusted size is too small */ | ||
764 | if ((node->length - (temp_dword - node->base)) < size) | ||
765 | continue; | ||
766 | |||
767 | split_node = kmalloc(sizeof(*split_node), GFP_KERNEL); | ||
768 | |||
769 | if (!split_node) | ||
770 | return NULL; | ||
771 | |||
772 | split_node->base = node->base; | ||
773 | split_node->length = temp_dword - node->base; | ||
774 | node->base = temp_dword; | ||
775 | node->length -= split_node->length; | ||
776 | |||
777 | split_node->next = node->next; | ||
778 | node->next = split_node; | ||
779 | } /* End of non-aligned base */ | ||
780 | |||
781 | /* Don't need to check if too small since we already did */ | ||
782 | if (node->length > size) { | ||
783 | dbg("%s: too big\n", __FUNCTION__); | ||
784 | /* this one is longer than we need | ||
785 | * so we'll make a new entry and split it up */ | ||
786 | split_node = kmalloc(sizeof(*split_node), GFP_KERNEL); | ||
787 | |||
788 | if (!split_node) | ||
789 | return NULL; | ||
790 | |||
791 | split_node->base = node->base + size; | ||
792 | split_node->length = node->length - size; | ||
793 | node->length = size; | ||
794 | |||
795 | /* Put it in the list */ | ||
796 | split_node->next = node->next; | ||
797 | node->next = split_node; | ||
798 | } /* End of too big on top end */ | ||
799 | |||
800 | dbg("%s: got one!!!\n", __FUNCTION__); | ||
801 | /* If we got here, then it is the right size | ||
802 | * Now take it out of the list */ | ||
803 | if (*head == node) { | ||
804 | *head = node->next; | ||
805 | } else { | ||
806 | prevnode = *head; | ||
807 | while (prevnode->next != node) | ||
808 | prevnode = prevnode->next; | ||
809 | |||
810 | prevnode->next = node->next; | ||
811 | } | ||
812 | node->next = NULL; | ||
813 | break; | ||
814 | } | ||
815 | return node; | ||
816 | } | ||
817 | |||
818 | |||
819 | /** | ||
820 | * cpqhp_resource_sort_and_combine: sort nodes by base addresses and clean up. | ||
821 | * @head: the list to sort and clean up | ||
822 | * | ||
823 | * Description: Sorts all of the nodes in the list in ascending order by | ||
824 | * their base addresses. Also does garbage collection by | ||
825 | * combining adjacent nodes. | ||
826 | * | ||
827 | * returns 0 if success | ||
828 | */ | ||
829 | int cpqhp_resource_sort_and_combine(struct pci_resource **head) | ||
830 | { | ||
831 | struct pci_resource *node1; | ||
832 | struct pci_resource *node2; | ||
833 | int out_of_order = 1; | ||
834 | |||
835 | dbg("%s: head = %p, *head = %p\n", __FUNCTION__, head, *head); | ||
836 | |||
837 | if (!(*head)) | ||
838 | return 1; | ||
839 | |||
840 | dbg("*head->next = %p\n",(*head)->next); | ||
841 | |||
842 | if (!(*head)->next) | ||
843 | return 0; /* only one item on the list, already sorted! */ | ||
844 | |||
845 | dbg("*head->base = 0x%x\n",(*head)->base); | ||
846 | dbg("*head->next->base = 0x%x\n",(*head)->next->base); | ||
847 | while (out_of_order) { | ||
848 | out_of_order = 0; | ||
849 | |||
850 | /* Special case for swapping list head */ | ||
851 | if (((*head)->next) && | ||
852 | ((*head)->base > (*head)->next->base)) { | ||
853 | node1 = *head; | ||
854 | (*head) = (*head)->next; | ||
855 | node1->next = (*head)->next; | ||
856 | (*head)->next = node1; | ||
857 | out_of_order++; | ||
858 | } | ||
859 | |||
860 | node1 = (*head); | ||
861 | |||
862 | while (node1->next && node1->next->next) { | ||
863 | if (node1->next->base > node1->next->next->base) { | ||
864 | out_of_order++; | ||
865 | node2 = node1->next; | ||
866 | node1->next = node1->next->next; | ||
867 | node1 = node1->next; | ||
868 | node2->next = node1->next; | ||
869 | node1->next = node2; | ||
870 | } else | ||
871 | node1 = node1->next; | ||
872 | } | ||
873 | } /* End of out_of_order loop */ | ||
874 | |||
875 | node1 = *head; | ||
876 | |||
877 | while (node1 && node1->next) { | ||
878 | if ((node1->base + node1->length) == node1->next->base) { | ||
879 | /* Combine */ | ||
880 | dbg("8..\n"); | ||
881 | node1->length += node1->next->length; | ||
882 | node2 = node1->next; | ||
883 | node1->next = node1->next->next; | ||
884 | kfree(node2); | ||
885 | } else | ||
886 | node1 = node1->next; | ||
887 | } | ||
888 | |||
889 | return 0; | ||
890 | } | ||
891 | |||
892 | |||
893 | irqreturn_t cpqhp_ctrl_intr(int IRQ, void *data, struct pt_regs *regs) | ||
894 | { | ||
895 | struct controller *ctrl = data; | ||
896 | u8 schedule_flag = 0; | ||
897 | u8 reset; | ||
898 | u16 misc; | ||
899 | u32 Diff; | ||
900 | u32 temp_dword; | ||
901 | |||
902 | |||
903 | misc = readw(ctrl->hpc_reg + MISC); | ||
904 | /*************************************** | ||
905 | * Check to see if it was our interrupt | ||
906 | ***************************************/ | ||
907 | if (!(misc & 0x000C)) { | ||
908 | return IRQ_NONE; | ||
909 | } | ||
910 | |||
911 | if (misc & 0x0004) { | ||
912 | /********************************** | ||
913 | * Serial Output interrupt Pending | ||
914 | **********************************/ | ||
915 | |||
916 | /* Clear the interrupt */ | ||
917 | misc |= 0x0004; | ||
918 | writew(misc, ctrl->hpc_reg + MISC); | ||
919 | |||
920 | /* Read to clear posted writes */ | ||
921 | misc = readw(ctrl->hpc_reg + MISC); | ||
922 | |||
923 | dbg ("%s - waking up\n", __FUNCTION__); | ||
924 | wake_up_interruptible(&ctrl->queue); | ||
925 | } | ||
926 | |||
927 | if (misc & 0x0008) { | ||
928 | /* General-interrupt-input interrupt Pending */ | ||
929 | Diff = readl(ctrl->hpc_reg + INT_INPUT_CLEAR) ^ ctrl->ctrl_int_comp; | ||
930 | |||
931 | ctrl->ctrl_int_comp = readl(ctrl->hpc_reg + INT_INPUT_CLEAR); | ||
932 | |||
933 | /* Clear the interrupt */ | ||
934 | writel(Diff, ctrl->hpc_reg + INT_INPUT_CLEAR); | ||
935 | |||
936 | /* Read it back to clear any posted writes */ | ||
937 | temp_dword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR); | ||
938 | |||
939 | if (!Diff) | ||
940 | /* Clear all interrupts */ | ||
941 | writel(0xFFFFFFFF, ctrl->hpc_reg + INT_INPUT_CLEAR); | ||
942 | |||
943 | schedule_flag += handle_switch_change((u8)(Diff & 0xFFL), ctrl); | ||
944 | schedule_flag += handle_presence_change((u16)((Diff & 0xFFFF0000L) >> 16), ctrl); | ||
945 | schedule_flag += handle_power_fault((u8)((Diff & 0xFF00L) >> 8), ctrl); | ||
946 | } | ||
947 | |||
948 | reset = readb(ctrl->hpc_reg + RESET_FREQ_MODE); | ||
949 | if (reset & 0x40) { | ||
950 | /* Bus reset has completed */ | ||
951 | reset &= 0xCF; | ||
952 | writeb(reset, ctrl->hpc_reg + RESET_FREQ_MODE); | ||
953 | reset = readb(ctrl->hpc_reg + RESET_FREQ_MODE); | ||
954 | wake_up_interruptible(&ctrl->queue); | ||
955 | } | ||
956 | |||
957 | if (schedule_flag) { | ||
958 | up(&event_semaphore); | ||
959 | dbg("Signal event_semaphore\n"); | ||
960 | } | ||
961 | return IRQ_HANDLED; | ||
962 | } | ||
963 | |||
964 | |||
965 | /** | ||
966 | * cpqhp_slot_create - Creates a node and adds it to the proper bus. | ||
967 | * @busnumber - bus where new node is to be located | ||
968 | * | ||
969 | * Returns pointer to the new node or NULL if unsuccessful | ||
970 | */ | ||
971 | struct pci_func *cpqhp_slot_create(u8 busnumber) | ||
972 | { | ||
973 | struct pci_func *new_slot; | ||
974 | struct pci_func *next; | ||
975 | |||
976 | new_slot = kmalloc(sizeof(*new_slot), GFP_KERNEL); | ||
977 | |||
978 | if (new_slot == NULL) { | ||
979 | /* I'm not dead yet! | ||
980 | * You will be. */ | ||
981 | return new_slot; | ||
982 | } | ||
983 | |||
984 | memset(new_slot, 0, sizeof(struct pci_func)); | ||
985 | |||
986 | new_slot->next = NULL; | ||
987 | new_slot->configured = 1; | ||
988 | |||
989 | if (cpqhp_slot_list[busnumber] == NULL) { | ||
990 | cpqhp_slot_list[busnumber] = new_slot; | ||
991 | } else { | ||
992 | next = cpqhp_slot_list[busnumber]; | ||
993 | while (next->next != NULL) | ||
994 | next = next->next; | ||
995 | next->next = new_slot; | ||
996 | } | ||
997 | return new_slot; | ||
998 | } | ||
999 | |||
1000 | |||
1001 | /** | ||
1002 | * slot_remove - Removes a node from the linked list of slots. | ||
1003 | * @old_slot: slot to remove | ||
1004 | * | ||
1005 | * Returns 0 if successful, !0 otherwise. | ||
1006 | */ | ||
1007 | static int slot_remove(struct pci_func * old_slot) | ||
1008 | { | ||
1009 | struct pci_func *next; | ||
1010 | |||
1011 | if (old_slot == NULL) | ||
1012 | return 1; | ||
1013 | |||
1014 | next = cpqhp_slot_list[old_slot->bus]; | ||
1015 | |||
1016 | if (next == NULL) { | ||
1017 | return 1; | ||
1018 | } | ||
1019 | |||
1020 | if (next == old_slot) { | ||
1021 | cpqhp_slot_list[old_slot->bus] = old_slot->next; | ||
1022 | cpqhp_destroy_board_resources(old_slot); | ||
1023 | kfree(old_slot); | ||
1024 | return 0; | ||
1025 | } | ||
1026 | |||
1027 | while ((next->next != old_slot) && (next->next != NULL)) { | ||
1028 | next = next->next; | ||
1029 | } | ||
1030 | |||
1031 | if (next->next == old_slot) { | ||
1032 | next->next = old_slot->next; | ||
1033 | cpqhp_destroy_board_resources(old_slot); | ||
1034 | kfree(old_slot); | ||
1035 | return 0; | ||
1036 | } else | ||
1037 | return 2; | ||
1038 | } | ||
1039 | |||
1040 | |||
1041 | /** | ||
1042 | * bridge_slot_remove - Removes a node from the linked list of slots. | ||
1043 | * @bridge: bridge to remove | ||
1044 | * | ||
1045 | * Returns 0 if successful, !0 otherwise. | ||
1046 | */ | ||
1047 | static int bridge_slot_remove(struct pci_func *bridge) | ||
1048 | { | ||
1049 | u8 subordinateBus, secondaryBus; | ||
1050 | u8 tempBus; | ||
1051 | struct pci_func *next; | ||
1052 | |||
1053 | secondaryBus = (bridge->config_space[0x06] >> 8) & 0xFF; | ||
1054 | subordinateBus = (bridge->config_space[0x06] >> 16) & 0xFF; | ||
1055 | |||
1056 | for (tempBus = secondaryBus; tempBus <= subordinateBus; tempBus++) { | ||
1057 | next = cpqhp_slot_list[tempBus]; | ||
1058 | |||
1059 | while (!slot_remove(next)) { | ||
1060 | next = cpqhp_slot_list[tempBus]; | ||
1061 | } | ||
1062 | } | ||
1063 | |||
1064 | next = cpqhp_slot_list[bridge->bus]; | ||
1065 | |||
1066 | if (next == NULL) | ||
1067 | return 1; | ||
1068 | |||
1069 | if (next == bridge) { | ||
1070 | cpqhp_slot_list[bridge->bus] = bridge->next; | ||
1071 | goto out; | ||
1072 | } | ||
1073 | |||
1074 | while ((next->next != bridge) && (next->next != NULL)) | ||
1075 | next = next->next; | ||
1076 | |||
1077 | if (next->next != bridge) | ||
1078 | return 2; | ||
1079 | next->next = bridge->next; | ||
1080 | out: | ||
1081 | kfree(bridge); | ||
1082 | return 0; | ||
1083 | } | ||
1084 | |||
1085 | |||
1086 | /** | ||
1087 | * cpqhp_slot_find - Looks for a node by bus, and device, multiple functions accessed | ||
1088 | * @bus: bus to find | ||
1089 | * @device: device to find | ||
1090 | * @index: is 0 for first function found, 1 for the second... | ||
1091 | * | ||
1092 | * Returns pointer to the node if successful, %NULL otherwise. | ||
1093 | */ | ||
1094 | struct pci_func *cpqhp_slot_find(u8 bus, u8 device, u8 index) | ||
1095 | { | ||
1096 | int found = -1; | ||
1097 | struct pci_func *func; | ||
1098 | |||
1099 | func = cpqhp_slot_list[bus]; | ||
1100 | |||
1101 | if ((func == NULL) || ((func->device == device) && (index == 0))) | ||
1102 | return func; | ||
1103 | |||
1104 | if (func->device == device) | ||
1105 | found++; | ||
1106 | |||
1107 | while (func->next != NULL) { | ||
1108 | func = func->next; | ||
1109 | |||
1110 | if (func->device == device) | ||
1111 | found++; | ||
1112 | |||
1113 | if (found == index) | ||
1114 | return func; | ||
1115 | } | ||
1116 | |||
1117 | return NULL; | ||
1118 | } | ||
1119 | |||
1120 | |||
1121 | /* DJZ: I don't think is_bridge will work as is. | ||
1122 | * FIXME */ | ||
1123 | static int is_bridge(struct pci_func * func) | ||
1124 | { | ||
1125 | /* Check the header type */ | ||
1126 | if (((func->config_space[0x03] >> 16) & 0xFF) == 0x01) | ||
1127 | return 1; | ||
1128 | else | ||
1129 | return 0; | ||
1130 | } | ||
1131 | |||
1132 | |||
1133 | /** | ||
1134 | * set_controller_speed - set the frequency and/or mode of a specific | ||
1135 | * controller segment. | ||
1136 | * | ||
1137 | * @ctrl: controller to change frequency/mode for. | ||
1138 | * @adapter_speed: the speed of the adapter we want to match. | ||
1139 | * @hp_slot: the slot number where the adapter is installed. | ||
1140 | * | ||
1141 | * Returns 0 if we successfully change frequency and/or mode to match the | ||
1142 | * adapter speed. | ||
1143 | * | ||
1144 | */ | ||
1145 | static u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_slot) | ||
1146 | { | ||
1147 | struct slot *slot; | ||
1148 | u8 reg; | ||
1149 | u8 slot_power = readb(ctrl->hpc_reg + SLOT_POWER); | ||
1150 | u16 reg16; | ||
1151 | u32 leds = readl(ctrl->hpc_reg + LED_CONTROL); | ||
1152 | |||
1153 | if (ctrl->speed == adapter_speed) | ||
1154 | return 0; | ||
1155 | |||
1156 | /* We don't allow freq/mode changes if we find another adapter running | ||
1157 | * in another slot on this controller */ | ||
1158 | for(slot = ctrl->slot; slot; slot = slot->next) { | ||
1159 | if (slot->device == (hp_slot + ctrl->slot_device_offset)) | ||
1160 | continue; | ||
1161 | if (!slot->hotplug_slot && !slot->hotplug_slot->info) | ||
1162 | continue; | ||
1163 | if (slot->hotplug_slot->info->adapter_status == 0) | ||
1164 | continue; | ||
1165 | /* If another adapter is running on the same segment but at a | ||
1166 | * lower speed/mode, we allow the new adapter to function at | ||
1167 | * this rate if supported */ | ||
1168 | if (ctrl->speed < adapter_speed) | ||
1169 | return 0; | ||
1170 | |||
1171 | return 1; | ||
1172 | } | ||
1173 | |||
1174 | /* If the controller doesn't support freq/mode changes and the | ||
1175 | * controller is running at a higher mode, we bail */ | ||
1176 | if ((ctrl->speed > adapter_speed) && (!ctrl->pcix_speed_capability)) | ||
1177 | return 1; | ||
1178 | |||
1179 | /* But we allow the adapter to run at a lower rate if possible */ | ||
1180 | if ((ctrl->speed < adapter_speed) && (!ctrl->pcix_speed_capability)) | ||
1181 | return 0; | ||
1182 | |||
1183 | /* We try to set the max speed supported by both the adapter and | ||
1184 | * controller */ | ||
1185 | if (ctrl->speed_capability < adapter_speed) { | ||
1186 | if (ctrl->speed == ctrl->speed_capability) | ||
1187 | return 0; | ||
1188 | adapter_speed = ctrl->speed_capability; | ||
1189 | } | ||
1190 | |||
1191 | writel(0x0L, ctrl->hpc_reg + LED_CONTROL); | ||
1192 | writeb(0x00, ctrl->hpc_reg + SLOT_ENABLE); | ||
1193 | |||
1194 | set_SOGO(ctrl); | ||
1195 | wait_for_ctrl_irq(ctrl); | ||
1196 | |||
1197 | if (adapter_speed != PCI_SPEED_133MHz_PCIX) | ||
1198 | reg = 0xF5; | ||
1199 | else | ||
1200 | reg = 0xF4; | ||
1201 | pci_write_config_byte(ctrl->pci_dev, 0x41, reg); | ||
1202 | |||
1203 | reg16 = readw(ctrl->hpc_reg + NEXT_CURR_FREQ); | ||
1204 | reg16 &= ~0x000F; | ||
1205 | switch(adapter_speed) { | ||
1206 | case(PCI_SPEED_133MHz_PCIX): | ||
1207 | reg = 0x75; | ||
1208 | reg16 |= 0xB; | ||
1209 | break; | ||
1210 | case(PCI_SPEED_100MHz_PCIX): | ||
1211 | reg = 0x74; | ||
1212 | reg16 |= 0xA; | ||
1213 | break; | ||
1214 | case(PCI_SPEED_66MHz_PCIX): | ||
1215 | reg = 0x73; | ||
1216 | reg16 |= 0x9; | ||
1217 | break; | ||
1218 | case(PCI_SPEED_66MHz): | ||
1219 | reg = 0x73; | ||
1220 | reg16 |= 0x1; | ||
1221 | break; | ||
1222 | default: /* 33MHz PCI 2.2 */ | ||
1223 | reg = 0x71; | ||
1224 | break; | ||
1225 | |||
1226 | } | ||
1227 | reg16 |= 0xB << 12; | ||
1228 | writew(reg16, ctrl->hpc_reg + NEXT_CURR_FREQ); | ||
1229 | |||
1230 | mdelay(5); | ||
1231 | |||
1232 | /* Reenable interrupts */ | ||
1233 | writel(0, ctrl->hpc_reg + INT_MASK); | ||
1234 | |||
1235 | pci_write_config_byte(ctrl->pci_dev, 0x41, reg); | ||
1236 | |||
1237 | /* Restart state machine */ | ||
1238 | reg = ~0xF; | ||
1239 | pci_read_config_byte(ctrl->pci_dev, 0x43, ®); | ||
1240 | pci_write_config_byte(ctrl->pci_dev, 0x43, reg); | ||
1241 | |||
1242 | /* Only if mode change...*/ | ||
1243 | if (((ctrl->speed == PCI_SPEED_66MHz) && (adapter_speed == PCI_SPEED_66MHz_PCIX)) || | ||
1244 | ((ctrl->speed == PCI_SPEED_66MHz_PCIX) && (adapter_speed == PCI_SPEED_66MHz))) | ||
1245 | set_SOGO(ctrl); | ||
1246 | |||
1247 | wait_for_ctrl_irq(ctrl); | ||
1248 | mdelay(1100); | ||
1249 | |||
1250 | /* Restore LED/Slot state */ | ||
1251 | writel(leds, ctrl->hpc_reg + LED_CONTROL); | ||
1252 | writeb(slot_power, ctrl->hpc_reg + SLOT_ENABLE); | ||
1253 | |||
1254 | set_SOGO(ctrl); | ||
1255 | wait_for_ctrl_irq(ctrl); | ||
1256 | |||
1257 | ctrl->speed = adapter_speed; | ||
1258 | slot = cpqhp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | ||
1259 | |||
1260 | info("Successfully changed frequency/mode for adapter in slot %d\n", | ||
1261 | slot->number); | ||
1262 | return 0; | ||
1263 | } | ||
1264 | |||
1265 | /* the following routines constitute the bulk of the | ||
1266 | hotplug controller logic | ||
1267 | */ | ||
1268 | |||
1269 | |||
1270 | /** | ||
1271 | * board_replaced - Called after a board has been replaced in the system. | ||
1272 | * | ||
1273 | * This is only used if we don't have resources for hot add | ||
1274 | * Turns power on for the board | ||
1275 | * Checks to see if board is the same | ||
1276 | * If board is same, reconfigures it | ||
1277 | * If board isn't same, turns it back off. | ||
1278 | * | ||
1279 | */ | ||
1280 | static u32 board_replaced(struct pci_func *func, struct controller *ctrl) | ||
1281 | { | ||
1282 | u8 hp_slot; | ||
1283 | u8 temp_byte; | ||
1284 | u8 adapter_speed; | ||
1285 | u32 index; | ||
1286 | u32 rc = 0; | ||
1287 | u32 src = 8; | ||
1288 | |||
1289 | hp_slot = func->device - ctrl->slot_device_offset; | ||
1290 | |||
1291 | if (readl(ctrl->hpc_reg + INT_INPUT_CLEAR) & (0x01L << hp_slot)) { | ||
1292 | /********************************** | ||
1293 | * The switch is open. | ||
1294 | **********************************/ | ||
1295 | rc = INTERLOCK_OPEN; | ||
1296 | } else if (is_slot_enabled (ctrl, hp_slot)) { | ||
1297 | /********************************** | ||
1298 | * The board is already on | ||
1299 | **********************************/ | ||
1300 | rc = CARD_FUNCTIONING; | ||
1301 | } else { | ||
1302 | down(&ctrl->crit_sect); | ||
1303 | |||
1304 | /* turn on board without attaching to the bus */ | ||
1305 | enable_slot_power (ctrl, hp_slot); | ||
1306 | |||
1307 | set_SOGO(ctrl); | ||
1308 | |||
1309 | /* Wait for SOBS to be unset */ | ||
1310 | wait_for_ctrl_irq (ctrl); | ||
1311 | |||
1312 | /* Change bits in slot power register to force another shift out | ||
1313 | * NOTE: this is to work around the timer bug */ | ||
1314 | temp_byte = readb(ctrl->hpc_reg + SLOT_POWER); | ||
1315 | writeb(0x00, ctrl->hpc_reg + SLOT_POWER); | ||
1316 | writeb(temp_byte, ctrl->hpc_reg + SLOT_POWER); | ||
1317 | |||
1318 | set_SOGO(ctrl); | ||
1319 | |||
1320 | /* Wait for SOBS to be unset */ | ||
1321 | wait_for_ctrl_irq (ctrl); | ||
1322 | |||
1323 | adapter_speed = get_adapter_speed(ctrl, hp_slot); | ||
1324 | if (ctrl->speed != adapter_speed) | ||
1325 | if (set_controller_speed(ctrl, adapter_speed, hp_slot)) | ||
1326 | rc = WRONG_BUS_FREQUENCY; | ||
1327 | |||
1328 | /* turn off board without attaching to the bus */ | ||
1329 | disable_slot_power (ctrl, hp_slot); | ||
1330 | |||
1331 | set_SOGO(ctrl); | ||
1332 | |||
1333 | /* Wait for SOBS to be unset */ | ||
1334 | wait_for_ctrl_irq (ctrl); | ||
1335 | |||
1336 | up(&ctrl->crit_sect); | ||
1337 | |||
1338 | if (rc) | ||
1339 | return rc; | ||
1340 | |||
1341 | down(&ctrl->crit_sect); | ||
1342 | |||
1343 | slot_enable (ctrl, hp_slot); | ||
1344 | green_LED_blink (ctrl, hp_slot); | ||
1345 | |||
1346 | amber_LED_off (ctrl, hp_slot); | ||
1347 | |||
1348 | set_SOGO(ctrl); | ||
1349 | |||
1350 | /* Wait for SOBS to be unset */ | ||
1351 | wait_for_ctrl_irq (ctrl); | ||
1352 | |||
1353 | up(&ctrl->crit_sect); | ||
1354 | |||
1355 | /* Wait for ~1 second because of hot plug spec */ | ||
1356 | long_delay(1*HZ); | ||
1357 | |||
1358 | /* Check for a power fault */ | ||
1359 | if (func->status == 0xFF) { | ||
1360 | /* power fault occurred, but it was benign */ | ||
1361 | rc = POWER_FAILURE; | ||
1362 | func->status = 0; | ||
1363 | } else | ||
1364 | rc = cpqhp_valid_replace(ctrl, func); | ||
1365 | |||
1366 | if (!rc) { | ||
1367 | /* It must be the same board */ | ||
1368 | |||
1369 | rc = cpqhp_configure_board(ctrl, func); | ||
1370 | |||
1371 | if (rc || src) { | ||
1372 | /* If configuration fails, turn it off | ||
1373 | * Get slot won't work for devices behind | ||
1374 | * bridges, but in this case it will always be | ||
1375 | * called for the "base" bus/dev/func of an | ||
1376 | * adapter. */ | ||
1377 | |||
1378 | down(&ctrl->crit_sect); | ||
1379 | |||
1380 | amber_LED_on (ctrl, hp_slot); | ||
1381 | green_LED_off (ctrl, hp_slot); | ||
1382 | slot_disable (ctrl, hp_slot); | ||
1383 | |||
1384 | set_SOGO(ctrl); | ||
1385 | |||
1386 | /* Wait for SOBS to be unset */ | ||
1387 | wait_for_ctrl_irq (ctrl); | ||
1388 | |||
1389 | up(&ctrl->crit_sect); | ||
1390 | |||
1391 | if (rc) | ||
1392 | return rc; | ||
1393 | else | ||
1394 | return 1; | ||
1395 | } | ||
1396 | |||
1397 | func->status = 0; | ||
1398 | func->switch_save = 0x10; | ||
1399 | |||
1400 | index = 1; | ||
1401 | while (((func = cpqhp_slot_find(func->bus, func->device, index)) != NULL) && !rc) { | ||
1402 | rc |= cpqhp_configure_board(ctrl, func); | ||
1403 | index++; | ||
1404 | } | ||
1405 | |||
1406 | if (rc) { | ||
1407 | /* If configuration fails, turn it off | ||
1408 | * Get slot won't work for devices behind | ||
1409 | * bridges, but in this case it will always be | ||
1410 | * called for the "base" bus/dev/func of an | ||
1411 | * adapter. */ | ||
1412 | |||
1413 | down(&ctrl->crit_sect); | ||
1414 | |||
1415 | amber_LED_on (ctrl, hp_slot); | ||
1416 | green_LED_off (ctrl, hp_slot); | ||
1417 | slot_disable (ctrl, hp_slot); | ||
1418 | |||
1419 | set_SOGO(ctrl); | ||
1420 | |||
1421 | /* Wait for SOBS to be unset */ | ||
1422 | wait_for_ctrl_irq (ctrl); | ||
1423 | |||
1424 | up(&ctrl->crit_sect); | ||
1425 | |||
1426 | return rc; | ||
1427 | } | ||
1428 | /* Done configuring so turn LED on full time */ | ||
1429 | |||
1430 | down(&ctrl->crit_sect); | ||
1431 | |||
1432 | green_LED_on (ctrl, hp_slot); | ||
1433 | |||
1434 | set_SOGO(ctrl); | ||
1435 | |||
1436 | /* Wait for SOBS to be unset */ | ||
1437 | wait_for_ctrl_irq (ctrl); | ||
1438 | |||
1439 | up(&ctrl->crit_sect); | ||
1440 | rc = 0; | ||
1441 | } else { | ||
1442 | /* Something is wrong | ||
1443 | |||
1444 | * Get slot won't work for devices behind bridges, but | ||
1445 | * in this case it will always be called for the "base" | ||
1446 | * bus/dev/func of an adapter. */ | ||
1447 | |||
1448 | down(&ctrl->crit_sect); | ||
1449 | |||
1450 | amber_LED_on (ctrl, hp_slot); | ||
1451 | green_LED_off (ctrl, hp_slot); | ||
1452 | slot_disable (ctrl, hp_slot); | ||
1453 | |||
1454 | set_SOGO(ctrl); | ||
1455 | |||
1456 | /* Wait for SOBS to be unset */ | ||
1457 | wait_for_ctrl_irq (ctrl); | ||
1458 | |||
1459 | up(&ctrl->crit_sect); | ||
1460 | } | ||
1461 | |||
1462 | } | ||
1463 | return rc; | ||
1464 | |||
1465 | } | ||
1466 | |||
1467 | |||
1468 | /** | ||
1469 | * board_added - Called after a board has been added to the system. | ||
1470 | * | ||
1471 | * Turns power on for the board | ||
1472 | * Configures board | ||
1473 | * | ||
1474 | */ | ||
1475 | static u32 board_added(struct pci_func *func, struct controller *ctrl) | ||
1476 | { | ||
1477 | u8 hp_slot; | ||
1478 | u8 temp_byte; | ||
1479 | u8 adapter_speed; | ||
1480 | int index; | ||
1481 | u32 temp_register = 0xFFFFFFFF; | ||
1482 | u32 rc = 0; | ||
1483 | struct pci_func *new_slot = NULL; | ||
1484 | struct slot *p_slot; | ||
1485 | struct resource_lists res_lists; | ||
1486 | |||
1487 | hp_slot = func->device - ctrl->slot_device_offset; | ||
1488 | dbg("%s: func->device, slot_offset, hp_slot = %d, %d ,%d\n", | ||
1489 | __FUNCTION__, func->device, ctrl->slot_device_offset, hp_slot); | ||
1490 | |||
1491 | down(&ctrl->crit_sect); | ||
1492 | |||
1493 | /* turn on board without attaching to the bus */ | ||
1494 | enable_slot_power(ctrl, hp_slot); | ||
1495 | |||
1496 | set_SOGO(ctrl); | ||
1497 | |||
1498 | /* Wait for SOBS to be unset */ | ||
1499 | wait_for_ctrl_irq (ctrl); | ||
1500 | |||
1501 | /* Change bits in slot power register to force another shift out | ||
1502 | * NOTE: this is to work around the timer bug */ | ||
1503 | temp_byte = readb(ctrl->hpc_reg + SLOT_POWER); | ||
1504 | writeb(0x00, ctrl->hpc_reg + SLOT_POWER); | ||
1505 | writeb(temp_byte, ctrl->hpc_reg + SLOT_POWER); | ||
1506 | |||
1507 | set_SOGO(ctrl); | ||
1508 | |||
1509 | /* Wait for SOBS to be unset */ | ||
1510 | wait_for_ctrl_irq (ctrl); | ||
1511 | |||
1512 | adapter_speed = get_adapter_speed(ctrl, hp_slot); | ||
1513 | if (ctrl->speed != adapter_speed) | ||
1514 | if (set_controller_speed(ctrl, adapter_speed, hp_slot)) | ||
1515 | rc = WRONG_BUS_FREQUENCY; | ||
1516 | |||
1517 | /* turn off board without attaching to the bus */ | ||
1518 | disable_slot_power (ctrl, hp_slot); | ||
1519 | |||
1520 | set_SOGO(ctrl); | ||
1521 | |||
1522 | /* Wait for SOBS to be unset */ | ||
1523 | wait_for_ctrl_irq(ctrl); | ||
1524 | |||
1525 | up(&ctrl->crit_sect); | ||
1526 | |||
1527 | if (rc) | ||
1528 | return rc; | ||
1529 | |||
1530 | p_slot = cpqhp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | ||
1531 | |||
1532 | /* turn on board and blink green LED */ | ||
1533 | |||
1534 | dbg("%s: before down\n", __FUNCTION__); | ||
1535 | down(&ctrl->crit_sect); | ||
1536 | dbg("%s: after down\n", __FUNCTION__); | ||
1537 | |||
1538 | dbg("%s: before slot_enable\n", __FUNCTION__); | ||
1539 | slot_enable (ctrl, hp_slot); | ||
1540 | |||
1541 | dbg("%s: before green_LED_blink\n", __FUNCTION__); | ||
1542 | green_LED_blink (ctrl, hp_slot); | ||
1543 | |||
1544 | dbg("%s: before amber_LED_blink\n", __FUNCTION__); | ||
1545 | amber_LED_off (ctrl, hp_slot); | ||
1546 | |||
1547 | dbg("%s: before set_SOGO\n", __FUNCTION__); | ||
1548 | set_SOGO(ctrl); | ||
1549 | |||
1550 | /* Wait for SOBS to be unset */ | ||
1551 | dbg("%s: before wait_for_ctrl_irq\n", __FUNCTION__); | ||
1552 | wait_for_ctrl_irq (ctrl); | ||
1553 | dbg("%s: after wait_for_ctrl_irq\n", __FUNCTION__); | ||
1554 | |||
1555 | dbg("%s: before up\n", __FUNCTION__); | ||
1556 | up(&ctrl->crit_sect); | ||
1557 | dbg("%s: after up\n", __FUNCTION__); | ||
1558 | |||
1559 | /* Wait for ~1 second because of hot plug spec */ | ||
1560 | dbg("%s: before long_delay\n", __FUNCTION__); | ||
1561 | long_delay(1*HZ); | ||
1562 | dbg("%s: after long_delay\n", __FUNCTION__); | ||
1563 | |||
1564 | dbg("%s: func status = %x\n", __FUNCTION__, func->status); | ||
1565 | /* Check for a power fault */ | ||
1566 | if (func->status == 0xFF) { | ||
1567 | /* power fault occurred, but it was benign */ | ||
1568 | temp_register = 0xFFFFFFFF; | ||
1569 | dbg("%s: temp register set to %x by power fault\n", __FUNCTION__, temp_register); | ||
1570 | rc = POWER_FAILURE; | ||
1571 | func->status = 0; | ||
1572 | } else { | ||
1573 | /* Get vendor/device ID u32 */ | ||
1574 | ctrl->pci_bus->number = func->bus; | ||
1575 | rc = pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(func->device, func->function), PCI_VENDOR_ID, &temp_register); | ||
1576 | dbg("%s: pci_read_config_dword returns %d\n", __FUNCTION__, rc); | ||
1577 | dbg("%s: temp_register is %x\n", __FUNCTION__, temp_register); | ||
1578 | |||
1579 | if (rc != 0) { | ||
1580 | /* Something's wrong here */ | ||
1581 | temp_register = 0xFFFFFFFF; | ||
1582 | dbg("%s: temp register set to %x by error\n", __FUNCTION__, temp_register); | ||
1583 | } | ||
1584 | /* Preset return code. It will be changed later if things go okay. */ | ||
1585 | rc = NO_ADAPTER_PRESENT; | ||
1586 | } | ||
1587 | |||
1588 | /* All F's is an empty slot or an invalid board */ | ||
1589 | if (temp_register != 0xFFFFFFFF) { /* Check for a board in the slot */ | ||
1590 | res_lists.io_head = ctrl->io_head; | ||
1591 | res_lists.mem_head = ctrl->mem_head; | ||
1592 | res_lists.p_mem_head = ctrl->p_mem_head; | ||
1593 | res_lists.bus_head = ctrl->bus_head; | ||
1594 | res_lists.irqs = NULL; | ||
1595 | |||
1596 | rc = configure_new_device(ctrl, func, 0, &res_lists); | ||
1597 | |||
1598 | dbg("%s: back from configure_new_device\n", __FUNCTION__); | ||
1599 | ctrl->io_head = res_lists.io_head; | ||
1600 | ctrl->mem_head = res_lists.mem_head; | ||
1601 | ctrl->p_mem_head = res_lists.p_mem_head; | ||
1602 | ctrl->bus_head = res_lists.bus_head; | ||
1603 | |||
1604 | cpqhp_resource_sort_and_combine(&(ctrl->mem_head)); | ||
1605 | cpqhp_resource_sort_and_combine(&(ctrl->p_mem_head)); | ||
1606 | cpqhp_resource_sort_and_combine(&(ctrl->io_head)); | ||
1607 | cpqhp_resource_sort_and_combine(&(ctrl->bus_head)); | ||
1608 | |||
1609 | if (rc) { | ||
1610 | down(&ctrl->crit_sect); | ||
1611 | |||
1612 | amber_LED_on (ctrl, hp_slot); | ||
1613 | green_LED_off (ctrl, hp_slot); | ||
1614 | slot_disable (ctrl, hp_slot); | ||
1615 | |||
1616 | set_SOGO(ctrl); | ||
1617 | |||
1618 | /* Wait for SOBS to be unset */ | ||
1619 | wait_for_ctrl_irq (ctrl); | ||
1620 | |||
1621 | up(&ctrl->crit_sect); | ||
1622 | return rc; | ||
1623 | } else { | ||
1624 | cpqhp_save_slot_config(ctrl, func); | ||
1625 | } | ||
1626 | |||
1627 | |||
1628 | func->status = 0; | ||
1629 | func->switch_save = 0x10; | ||
1630 | func->is_a_board = 0x01; | ||
1631 | |||
1632 | /* next, we will instantiate the linux pci_dev structures (with | ||
1633 | * appropriate driver notification, if already present) */ | ||
1634 | dbg("%s: configure linux pci_dev structure\n", __FUNCTION__); | ||
1635 | index = 0; | ||
1636 | do { | ||
1637 | new_slot = cpqhp_slot_find(ctrl->bus, func->device, index++); | ||
1638 | if (new_slot && !new_slot->pci_dev) { | ||
1639 | cpqhp_configure_device(ctrl, new_slot); | ||
1640 | } | ||
1641 | } while (new_slot); | ||
1642 | |||
1643 | down(&ctrl->crit_sect); | ||
1644 | |||
1645 | green_LED_on (ctrl, hp_slot); | ||
1646 | |||
1647 | set_SOGO(ctrl); | ||
1648 | |||
1649 | /* Wait for SOBS to be unset */ | ||
1650 | wait_for_ctrl_irq (ctrl); | ||
1651 | |||
1652 | up(&ctrl->crit_sect); | ||
1653 | } else { | ||
1654 | down(&ctrl->crit_sect); | ||
1655 | |||
1656 | amber_LED_on (ctrl, hp_slot); | ||
1657 | green_LED_off (ctrl, hp_slot); | ||
1658 | slot_disable (ctrl, hp_slot); | ||
1659 | |||
1660 | set_SOGO(ctrl); | ||
1661 | |||
1662 | /* Wait for SOBS to be unset */ | ||
1663 | wait_for_ctrl_irq (ctrl); | ||
1664 | |||
1665 | up(&ctrl->crit_sect); | ||
1666 | |||
1667 | return rc; | ||
1668 | } | ||
1669 | return 0; | ||
1670 | } | ||
1671 | |||
1672 | |||
1673 | /** | ||
1674 | * remove_board - Turns off slot and LED's | ||
1675 | * | ||
1676 | */ | ||
1677 | static u32 remove_board(struct pci_func * func, u32 replace_flag, struct controller * ctrl) | ||
1678 | { | ||
1679 | int index; | ||
1680 | u8 skip = 0; | ||
1681 | u8 device; | ||
1682 | u8 hp_slot; | ||
1683 | u8 temp_byte; | ||
1684 | u32 rc; | ||
1685 | struct resource_lists res_lists; | ||
1686 | struct pci_func *temp_func; | ||
1687 | |||
1688 | if (cpqhp_unconfigure_device(func)) | ||
1689 | return 1; | ||
1690 | |||
1691 | device = func->device; | ||
1692 | |||
1693 | hp_slot = func->device - ctrl->slot_device_offset; | ||
1694 | dbg("In %s, hp_slot = %d\n", __FUNCTION__, hp_slot); | ||
1695 | |||
1696 | /* When we get here, it is safe to change base address registers. | ||
1697 | * We will attempt to save the base address register lengths */ | ||
1698 | if (replace_flag || !ctrl->add_support) | ||
1699 | rc = cpqhp_save_base_addr_length(ctrl, func); | ||
1700 | else if (!func->bus_head && !func->mem_head && | ||
1701 | !func->p_mem_head && !func->io_head) { | ||
1702 | /* Here we check to see if we've saved any of the board's | ||
1703 | * resources already. If so, we'll skip the attempt to | ||
1704 | * determine what's being used. */ | ||
1705 | index = 0; | ||
1706 | temp_func = cpqhp_slot_find(func->bus, func->device, index++); | ||
1707 | while (temp_func) { | ||
1708 | if (temp_func->bus_head || temp_func->mem_head | ||
1709 | || temp_func->p_mem_head || temp_func->io_head) { | ||
1710 | skip = 1; | ||
1711 | break; | ||
1712 | } | ||
1713 | temp_func = cpqhp_slot_find(temp_func->bus, temp_func->device, index++); | ||
1714 | } | ||
1715 | |||
1716 | if (!skip) | ||
1717 | rc = cpqhp_save_used_resources(ctrl, func); | ||
1718 | } | ||
1719 | /* Change status to shutdown */ | ||
1720 | if (func->is_a_board) | ||
1721 | func->status = 0x01; | ||
1722 | func->configured = 0; | ||
1723 | |||
1724 | down(&ctrl->crit_sect); | ||
1725 | |||
1726 | green_LED_off (ctrl, hp_slot); | ||
1727 | slot_disable (ctrl, hp_slot); | ||
1728 | |||
1729 | set_SOGO(ctrl); | ||
1730 | |||
1731 | /* turn off SERR for slot */ | ||
1732 | temp_byte = readb(ctrl->hpc_reg + SLOT_SERR); | ||
1733 | temp_byte &= ~(0x01 << hp_slot); | ||
1734 | writeb(temp_byte, ctrl->hpc_reg + SLOT_SERR); | ||
1735 | |||
1736 | /* Wait for SOBS to be unset */ | ||
1737 | wait_for_ctrl_irq (ctrl); | ||
1738 | |||
1739 | up(&ctrl->crit_sect); | ||
1740 | |||
1741 | if (!replace_flag && ctrl->add_support) { | ||
1742 | while (func) { | ||
1743 | res_lists.io_head = ctrl->io_head; | ||
1744 | res_lists.mem_head = ctrl->mem_head; | ||
1745 | res_lists.p_mem_head = ctrl->p_mem_head; | ||
1746 | res_lists.bus_head = ctrl->bus_head; | ||
1747 | |||
1748 | cpqhp_return_board_resources(func, &res_lists); | ||
1749 | |||
1750 | ctrl->io_head = res_lists.io_head; | ||
1751 | ctrl->mem_head = res_lists.mem_head; | ||
1752 | ctrl->p_mem_head = res_lists.p_mem_head; | ||
1753 | ctrl->bus_head = res_lists.bus_head; | ||
1754 | |||
1755 | cpqhp_resource_sort_and_combine(&(ctrl->mem_head)); | ||
1756 | cpqhp_resource_sort_and_combine(&(ctrl->p_mem_head)); | ||
1757 | cpqhp_resource_sort_and_combine(&(ctrl->io_head)); | ||
1758 | cpqhp_resource_sort_and_combine(&(ctrl->bus_head)); | ||
1759 | |||
1760 | if (is_bridge(func)) { | ||
1761 | bridge_slot_remove(func); | ||
1762 | } else | ||
1763 | slot_remove(func); | ||
1764 | |||
1765 | func = cpqhp_slot_find(ctrl->bus, device, 0); | ||
1766 | } | ||
1767 | |||
1768 | /* Setup slot structure with entry for empty slot */ | ||
1769 | func = cpqhp_slot_create(ctrl->bus); | ||
1770 | |||
1771 | if (func == NULL) | ||
1772 | return 1; | ||
1773 | |||
1774 | func->bus = ctrl->bus; | ||
1775 | func->device = device; | ||
1776 | func->function = 0; | ||
1777 | func->configured = 0; | ||
1778 | func->switch_save = 0x10; | ||
1779 | func->is_a_board = 0; | ||
1780 | func->p_task_event = NULL; | ||
1781 | } | ||
1782 | |||
1783 | return 0; | ||
1784 | } | ||
1785 | |||
1786 | static void pushbutton_helper_thread(unsigned long data) | ||
1787 | { | ||
1788 | pushbutton_pending = data; | ||
1789 | up(&event_semaphore); | ||
1790 | } | ||
1791 | |||
1792 | |||
1793 | /* this is the main worker thread */ | ||
1794 | static int event_thread(void* data) | ||
1795 | { | ||
1796 | struct controller *ctrl; | ||
1797 | lock_kernel(); | ||
1798 | daemonize("phpd_event"); | ||
1799 | |||
1800 | unlock_kernel(); | ||
1801 | |||
1802 | while (1) { | ||
1803 | dbg("!!!!event_thread sleeping\n"); | ||
1804 | down_interruptible (&event_semaphore); | ||
1805 | dbg("event_thread woken finished = %d\n", event_finished); | ||
1806 | if (event_finished) break; | ||
1807 | /* Do stuff here */ | ||
1808 | if (pushbutton_pending) | ||
1809 | cpqhp_pushbutton_thread(pushbutton_pending); | ||
1810 | else | ||
1811 | for (ctrl = cpqhp_ctrl_list; ctrl; ctrl=ctrl->next) | ||
1812 | interrupt_event_handler(ctrl); | ||
1813 | } | ||
1814 | dbg("event_thread signals exit\n"); | ||
1815 | up(&event_exit); | ||
1816 | return 0; | ||
1817 | } | ||
1818 | |||
1819 | |||
1820 | int cpqhp_event_start_thread(void) | ||
1821 | { | ||
1822 | int pid; | ||
1823 | |||
1824 | /* initialize our semaphores */ | ||
1825 | init_MUTEX(&delay_sem); | ||
1826 | init_MUTEX_LOCKED(&event_semaphore); | ||
1827 | init_MUTEX_LOCKED(&event_exit); | ||
1828 | event_finished=0; | ||
1829 | |||
1830 | pid = kernel_thread(event_thread, NULL, 0); | ||
1831 | if (pid < 0) { | ||
1832 | err ("Can't start up our event thread\n"); | ||
1833 | return -1; | ||
1834 | } | ||
1835 | dbg("Our event thread pid = %d\n", pid); | ||
1836 | return 0; | ||
1837 | } | ||
1838 | |||
1839 | |||
1840 | void cpqhp_event_stop_thread(void) | ||
1841 | { | ||
1842 | event_finished = 1; | ||
1843 | dbg("event_thread finish command given\n"); | ||
1844 | up(&event_semaphore); | ||
1845 | dbg("wait for event_thread to exit\n"); | ||
1846 | down(&event_exit); | ||
1847 | } | ||
1848 | |||
1849 | |||
1850 | static int update_slot_info(struct controller *ctrl, struct slot *slot) | ||
1851 | { | ||
1852 | struct hotplug_slot_info *info; | ||
1853 | int result; | ||
1854 | |||
1855 | info = kmalloc(sizeof(*info), GFP_KERNEL); | ||
1856 | if (!info) | ||
1857 | return -ENOMEM; | ||
1858 | |||
1859 | info->power_status = get_slot_enabled(ctrl, slot); | ||
1860 | info->attention_status = cpq_get_attention_status(ctrl, slot); | ||
1861 | info->latch_status = cpq_get_latch_status(ctrl, slot); | ||
1862 | info->adapter_status = get_presence_status(ctrl, slot); | ||
1863 | result = pci_hp_change_slot_info(slot->hotplug_slot, info); | ||
1864 | kfree (info); | ||
1865 | return result; | ||
1866 | } | ||
1867 | |||
1868 | static void interrupt_event_handler(struct controller *ctrl) | ||
1869 | { | ||
1870 | int loop = 0; | ||
1871 | int change = 1; | ||
1872 | struct pci_func *func; | ||
1873 | u8 hp_slot; | ||
1874 | struct slot *p_slot; | ||
1875 | |||
1876 | while (change) { | ||
1877 | change = 0; | ||
1878 | |||
1879 | for (loop = 0; loop < 10; loop++) { | ||
1880 | /* dbg("loop %d\n", loop); */ | ||
1881 | if (ctrl->event_queue[loop].event_type != 0) { | ||
1882 | hp_slot = ctrl->event_queue[loop].hp_slot; | ||
1883 | |||
1884 | func = cpqhp_slot_find(ctrl->bus, (hp_slot + ctrl->slot_device_offset), 0); | ||
1885 | if (!func) | ||
1886 | return; | ||
1887 | |||
1888 | p_slot = cpqhp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | ||
1889 | if (!p_slot) | ||
1890 | return; | ||
1891 | |||
1892 | dbg("hp_slot %d, func %p, p_slot %p\n", | ||
1893 | hp_slot, func, p_slot); | ||
1894 | |||
1895 | if (ctrl->event_queue[loop].event_type == INT_BUTTON_PRESS) { | ||
1896 | dbg("button pressed\n"); | ||
1897 | } else if (ctrl->event_queue[loop].event_type == | ||
1898 | INT_BUTTON_CANCEL) { | ||
1899 | dbg("button cancel\n"); | ||
1900 | del_timer(&p_slot->task_event); | ||
1901 | |||
1902 | down(&ctrl->crit_sect); | ||
1903 | |||
1904 | if (p_slot->state == BLINKINGOFF_STATE) { | ||
1905 | /* slot is on */ | ||
1906 | dbg("turn on green LED\n"); | ||
1907 | green_LED_on (ctrl, hp_slot); | ||
1908 | } else if (p_slot->state == BLINKINGON_STATE) { | ||
1909 | /* slot is off */ | ||
1910 | dbg("turn off green LED\n"); | ||
1911 | green_LED_off (ctrl, hp_slot); | ||
1912 | } | ||
1913 | |||
1914 | info(msg_button_cancel, p_slot->number); | ||
1915 | |||
1916 | p_slot->state = STATIC_STATE; | ||
1917 | |||
1918 | amber_LED_off (ctrl, hp_slot); | ||
1919 | |||
1920 | set_SOGO(ctrl); | ||
1921 | |||
1922 | /* Wait for SOBS to be unset */ | ||
1923 | wait_for_ctrl_irq (ctrl); | ||
1924 | |||
1925 | up(&ctrl->crit_sect); | ||
1926 | } | ||
1927 | /*** button Released (No action on press...) */ | ||
1928 | else if (ctrl->event_queue[loop].event_type == INT_BUTTON_RELEASE) { | ||
1929 | dbg("button release\n"); | ||
1930 | |||
1931 | if (is_slot_enabled (ctrl, hp_slot)) { | ||
1932 | dbg("slot is on\n"); | ||
1933 | p_slot->state = BLINKINGOFF_STATE; | ||
1934 | info(msg_button_off, p_slot->number); | ||
1935 | } else { | ||
1936 | dbg("slot is off\n"); | ||
1937 | p_slot->state = BLINKINGON_STATE; | ||
1938 | info(msg_button_on, p_slot->number); | ||
1939 | } | ||
1940 | down(&ctrl->crit_sect); | ||
1941 | |||
1942 | dbg("blink green LED and turn off amber\n"); | ||
1943 | |||
1944 | amber_LED_off (ctrl, hp_slot); | ||
1945 | green_LED_blink (ctrl, hp_slot); | ||
1946 | |||
1947 | set_SOGO(ctrl); | ||
1948 | |||
1949 | /* Wait for SOBS to be unset */ | ||
1950 | wait_for_ctrl_irq (ctrl); | ||
1951 | |||
1952 | up(&ctrl->crit_sect); | ||
1953 | init_timer(&p_slot->task_event); | ||
1954 | p_slot->hp_slot = hp_slot; | ||
1955 | p_slot->ctrl = ctrl; | ||
1956 | /* p_slot->physical_slot = physical_slot; */ | ||
1957 | p_slot->task_event.expires = jiffies + 5 * HZ; /* 5 second delay */ | ||
1958 | p_slot->task_event.function = pushbutton_helper_thread; | ||
1959 | p_slot->task_event.data = (u32) p_slot; | ||
1960 | |||
1961 | dbg("add_timer p_slot = %p\n", p_slot); | ||
1962 | add_timer(&p_slot->task_event); | ||
1963 | } | ||
1964 | /***********POWER FAULT */ | ||
1965 | else if (ctrl->event_queue[loop].event_type == INT_POWER_FAULT) { | ||
1966 | dbg("power fault\n"); | ||
1967 | } else { | ||
1968 | /* refresh notification */ | ||
1969 | if (p_slot) | ||
1970 | update_slot_info(ctrl, p_slot); | ||
1971 | } | ||
1972 | |||
1973 | ctrl->event_queue[loop].event_type = 0; | ||
1974 | |||
1975 | change = 1; | ||
1976 | } | ||
1977 | } /* End of FOR loop */ | ||
1978 | } | ||
1979 | |||
1980 | return; | ||
1981 | } | ||
1982 | |||
1983 | |||
1984 | /** | ||
1985 | * cpqhp_pushbutton_thread | ||
1986 | * | ||
1987 | * Scheduled procedure to handle blocking stuff for the pushbuttons | ||
1988 | * Handles all pending events and exits. | ||
1989 | * | ||
1990 | */ | ||
1991 | void cpqhp_pushbutton_thread(unsigned long slot) | ||
1992 | { | ||
1993 | u8 hp_slot; | ||
1994 | u8 device; | ||
1995 | struct pci_func *func; | ||
1996 | struct slot *p_slot = (struct slot *) slot; | ||
1997 | struct controller *ctrl = (struct controller *) p_slot->ctrl; | ||
1998 | |||
1999 | pushbutton_pending = 0; | ||
2000 | hp_slot = p_slot->hp_slot; | ||
2001 | |||
2002 | device = p_slot->device; | ||
2003 | |||
2004 | if (is_slot_enabled(ctrl, hp_slot)) { | ||
2005 | p_slot->state = POWEROFF_STATE; | ||
2006 | /* power Down board */ | ||
2007 | func = cpqhp_slot_find(p_slot->bus, p_slot->device, 0); | ||
2008 | dbg("In power_down_board, func = %p, ctrl = %p\n", func, ctrl); | ||
2009 | if (!func) { | ||
2010 | dbg("Error! func NULL in %s\n", __FUNCTION__); | ||
2011 | return ; | ||
2012 | } | ||
2013 | |||
2014 | if (func != NULL && ctrl != NULL) { | ||
2015 | if (cpqhp_process_SS(ctrl, func) != 0) { | ||
2016 | amber_LED_on (ctrl, hp_slot); | ||
2017 | green_LED_on (ctrl, hp_slot); | ||
2018 | |||
2019 | set_SOGO(ctrl); | ||
2020 | |||
2021 | /* Wait for SOBS to be unset */ | ||
2022 | wait_for_ctrl_irq (ctrl); | ||
2023 | } | ||
2024 | } | ||
2025 | |||
2026 | p_slot->state = STATIC_STATE; | ||
2027 | } else { | ||
2028 | p_slot->state = POWERON_STATE; | ||
2029 | /* slot is off */ | ||
2030 | |||
2031 | func = cpqhp_slot_find(p_slot->bus, p_slot->device, 0); | ||
2032 | dbg("In add_board, func = %p, ctrl = %p\n", func, ctrl); | ||
2033 | if (!func) { | ||
2034 | dbg("Error! func NULL in %s\n", __FUNCTION__); | ||
2035 | return ; | ||
2036 | } | ||
2037 | |||
2038 | if (func != NULL && ctrl != NULL) { | ||
2039 | if (cpqhp_process_SI(ctrl, func) != 0) { | ||
2040 | amber_LED_on(ctrl, hp_slot); | ||
2041 | green_LED_off(ctrl, hp_slot); | ||
2042 | |||
2043 | set_SOGO(ctrl); | ||
2044 | |||
2045 | /* Wait for SOBS to be unset */ | ||
2046 | wait_for_ctrl_irq (ctrl); | ||
2047 | } | ||
2048 | } | ||
2049 | |||
2050 | p_slot->state = STATIC_STATE; | ||
2051 | } | ||
2052 | |||
2053 | return; | ||
2054 | } | ||
2055 | |||
2056 | |||
2057 | int cpqhp_process_SI(struct controller *ctrl, struct pci_func *func) | ||
2058 | { | ||
2059 | u8 device, hp_slot; | ||
2060 | u16 temp_word; | ||
2061 | u32 tempdword; | ||
2062 | int rc; | ||
2063 | struct slot* p_slot; | ||
2064 | int physical_slot = 0; | ||
2065 | |||
2066 | tempdword = 0; | ||
2067 | |||
2068 | device = func->device; | ||
2069 | hp_slot = device - ctrl->slot_device_offset; | ||
2070 | p_slot = cpqhp_find_slot(ctrl, device); | ||
2071 | if (p_slot) | ||
2072 | physical_slot = p_slot->number; | ||
2073 | |||
2074 | /* Check to see if the interlock is closed */ | ||
2075 | tempdword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR); | ||
2076 | |||
2077 | if (tempdword & (0x01 << hp_slot)) { | ||
2078 | return 1; | ||
2079 | } | ||
2080 | |||
2081 | if (func->is_a_board) { | ||
2082 | rc = board_replaced(func, ctrl); | ||
2083 | } else { | ||
2084 | /* add board */ | ||
2085 | slot_remove(func); | ||
2086 | |||
2087 | func = cpqhp_slot_create(ctrl->bus); | ||
2088 | if (func == NULL) | ||
2089 | return 1; | ||
2090 | |||
2091 | func->bus = ctrl->bus; | ||
2092 | func->device = device; | ||
2093 | func->function = 0; | ||
2094 | func->configured = 0; | ||
2095 | func->is_a_board = 1; | ||
2096 | |||
2097 | /* We have to save the presence info for these slots */ | ||
2098 | temp_word = ctrl->ctrl_int_comp >> 16; | ||
2099 | func->presence_save = (temp_word >> hp_slot) & 0x01; | ||
2100 | func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02; | ||
2101 | |||
2102 | if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) { | ||
2103 | func->switch_save = 0; | ||
2104 | } else { | ||
2105 | func->switch_save = 0x10; | ||
2106 | } | ||
2107 | |||
2108 | rc = board_added(func, ctrl); | ||
2109 | if (rc) { | ||
2110 | if (is_bridge(func)) { | ||
2111 | bridge_slot_remove(func); | ||
2112 | } else | ||
2113 | slot_remove(func); | ||
2114 | |||
2115 | /* Setup slot structure with entry for empty slot */ | ||
2116 | func = cpqhp_slot_create(ctrl->bus); | ||
2117 | |||
2118 | if (func == NULL) | ||
2119 | return 1; | ||
2120 | |||
2121 | func->bus = ctrl->bus; | ||
2122 | func->device = device; | ||
2123 | func->function = 0; | ||
2124 | func->configured = 0; | ||
2125 | func->is_a_board = 0; | ||
2126 | |||
2127 | /* We have to save the presence info for these slots */ | ||
2128 | temp_word = ctrl->ctrl_int_comp >> 16; | ||
2129 | func->presence_save = (temp_word >> hp_slot) & 0x01; | ||
2130 | func->presence_save |= | ||
2131 | (temp_word >> (hp_slot + 7)) & 0x02; | ||
2132 | |||
2133 | if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) { | ||
2134 | func->switch_save = 0; | ||
2135 | } else { | ||
2136 | func->switch_save = 0x10; | ||
2137 | } | ||
2138 | } | ||
2139 | } | ||
2140 | |||
2141 | if (rc) { | ||
2142 | dbg("%s: rc = %d\n", __FUNCTION__, rc); | ||
2143 | } | ||
2144 | |||
2145 | if (p_slot) | ||
2146 | update_slot_info(ctrl, p_slot); | ||
2147 | |||
2148 | return rc; | ||
2149 | } | ||
2150 | |||
2151 | |||
2152 | int cpqhp_process_SS(struct controller *ctrl, struct pci_func *func) | ||
2153 | { | ||
2154 | u8 device, class_code, header_type, BCR; | ||
2155 | u8 index = 0; | ||
2156 | u8 replace_flag; | ||
2157 | u32 rc = 0; | ||
2158 | unsigned int devfn; | ||
2159 | struct slot* p_slot; | ||
2160 | struct pci_bus *pci_bus = ctrl->pci_bus; | ||
2161 | int physical_slot=0; | ||
2162 | |||
2163 | device = func->device; | ||
2164 | func = cpqhp_slot_find(ctrl->bus, device, index++); | ||
2165 | p_slot = cpqhp_find_slot(ctrl, device); | ||
2166 | if (p_slot) { | ||
2167 | physical_slot = p_slot->number; | ||
2168 | } | ||
2169 | |||
2170 | /* Make sure there are no video controllers here */ | ||
2171 | while (func && !rc) { | ||
2172 | pci_bus->number = func->bus; | ||
2173 | devfn = PCI_DEVFN(func->device, func->function); | ||
2174 | |||
2175 | /* Check the Class Code */ | ||
2176 | rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code); | ||
2177 | if (rc) | ||
2178 | return rc; | ||
2179 | |||
2180 | if (class_code == PCI_BASE_CLASS_DISPLAY) { | ||
2181 | /* Display/Video adapter (not supported) */ | ||
2182 | rc = REMOVE_NOT_SUPPORTED; | ||
2183 | } else { | ||
2184 | /* See if it's a bridge */ | ||
2185 | rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type); | ||
2186 | if (rc) | ||
2187 | return rc; | ||
2188 | |||
2189 | /* If it's a bridge, check the VGA Enable bit */ | ||
2190 | if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { | ||
2191 | rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_BRIDGE_CONTROL, &BCR); | ||
2192 | if (rc) | ||
2193 | return rc; | ||
2194 | |||
2195 | /* If the VGA Enable bit is set, remove isn't | ||
2196 | * supported */ | ||
2197 | if (BCR & PCI_BRIDGE_CTL_VGA) { | ||
2198 | rc = REMOVE_NOT_SUPPORTED; | ||
2199 | } | ||
2200 | } | ||
2201 | } | ||
2202 | |||
2203 | func = cpqhp_slot_find(ctrl->bus, device, index++); | ||
2204 | } | ||
2205 | |||
2206 | func = cpqhp_slot_find(ctrl->bus, device, 0); | ||
2207 | if ((func != NULL) && !rc) { | ||
2208 | /* FIXME: Replace flag should be passed into process_SS */ | ||
2209 | replace_flag = !(ctrl->add_support); | ||
2210 | rc = remove_board(func, replace_flag, ctrl); | ||
2211 | } else if (!rc) { | ||
2212 | rc = 1; | ||
2213 | } | ||
2214 | |||
2215 | if (p_slot) | ||
2216 | update_slot_info(ctrl, p_slot); | ||
2217 | |||
2218 | return rc; | ||
2219 | } | ||
2220 | |||
2221 | /** | ||
2222 | * switch_leds: switch the leds, go from one site to the other. | ||
2223 | * @ctrl: controller to use | ||
2224 | * @num_of_slots: number of slots to use | ||
2225 | * @direction: 1 to start from the left side, 0 to start right. | ||
2226 | */ | ||
2227 | static void switch_leds(struct controller *ctrl, const int num_of_slots, | ||
2228 | u32 *work_LED, const int direction) | ||
2229 | { | ||
2230 | int loop; | ||
2231 | |||
2232 | for (loop = 0; loop < num_of_slots; loop++) { | ||
2233 | if (direction) | ||
2234 | *work_LED = *work_LED >> 1; | ||
2235 | else | ||
2236 | *work_LED = *work_LED << 1; | ||
2237 | writel(*work_LED, ctrl->hpc_reg + LED_CONTROL); | ||
2238 | |||
2239 | set_SOGO(ctrl); | ||
2240 | |||
2241 | /* Wait for SOGO interrupt */ | ||
2242 | wait_for_ctrl_irq(ctrl); | ||
2243 | |||
2244 | /* Get ready for next iteration */ | ||
2245 | long_delay((2*HZ)/10); | ||
2246 | } | ||
2247 | } | ||
2248 | |||
2249 | /** | ||
2250 | * hardware_test - runs hardware tests | ||
2251 | * | ||
2252 | * For hot plug ctrl folks to play with. | ||
2253 | * test_num is the number written to the "test" file in sysfs | ||
2254 | * | ||
2255 | */ | ||
2256 | int cpqhp_hardware_test(struct controller *ctrl, int test_num) | ||
2257 | { | ||
2258 | u32 save_LED; | ||
2259 | u32 work_LED; | ||
2260 | int loop; | ||
2261 | int num_of_slots; | ||
2262 | |||
2263 | num_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0f; | ||
2264 | |||
2265 | switch (test_num) { | ||
2266 | case 1: | ||
2267 | /* Do stuff here! */ | ||
2268 | |||
2269 | /* Do that funky LED thing */ | ||
2270 | /* so we can restore them later */ | ||
2271 | save_LED = readl(ctrl->hpc_reg + LED_CONTROL); | ||
2272 | work_LED = 0x01010101; | ||
2273 | switch_leds(ctrl, num_of_slots, &work_LED, 0); | ||
2274 | switch_leds(ctrl, num_of_slots, &work_LED, 1); | ||
2275 | switch_leds(ctrl, num_of_slots, &work_LED, 0); | ||
2276 | switch_leds(ctrl, num_of_slots, &work_LED, 1); | ||
2277 | |||
2278 | work_LED = 0x01010000; | ||
2279 | writel(work_LED, ctrl->hpc_reg + LED_CONTROL); | ||
2280 | switch_leds(ctrl, num_of_slots, &work_LED, 0); | ||
2281 | switch_leds(ctrl, num_of_slots, &work_LED, 1); | ||
2282 | work_LED = 0x00000101; | ||
2283 | writel(work_LED, ctrl->hpc_reg + LED_CONTROL); | ||
2284 | switch_leds(ctrl, num_of_slots, &work_LED, 0); | ||
2285 | switch_leds(ctrl, num_of_slots, &work_LED, 1); | ||
2286 | |||
2287 | work_LED = 0x01010000; | ||
2288 | writel(work_LED, ctrl->hpc_reg + LED_CONTROL); | ||
2289 | for (loop = 0; loop < num_of_slots; loop++) { | ||
2290 | set_SOGO(ctrl); | ||
2291 | |||
2292 | /* Wait for SOGO interrupt */ | ||
2293 | wait_for_ctrl_irq (ctrl); | ||
2294 | |||
2295 | /* Get ready for next iteration */ | ||
2296 | long_delay((3*HZ)/10); | ||
2297 | work_LED = work_LED >> 16; | ||
2298 | writel(work_LED, ctrl->hpc_reg + LED_CONTROL); | ||
2299 | |||
2300 | set_SOGO(ctrl); | ||
2301 | |||
2302 | /* Wait for SOGO interrupt */ | ||
2303 | wait_for_ctrl_irq (ctrl); | ||
2304 | |||
2305 | /* Get ready for next iteration */ | ||
2306 | long_delay((3*HZ)/10); | ||
2307 | work_LED = work_LED << 16; | ||
2308 | writel(work_LED, ctrl->hpc_reg + LED_CONTROL); | ||
2309 | work_LED = work_LED << 1; | ||
2310 | writel(work_LED, ctrl->hpc_reg + LED_CONTROL); | ||
2311 | } | ||
2312 | |||
2313 | /* put it back the way it was */ | ||
2314 | writel(save_LED, ctrl->hpc_reg + LED_CONTROL); | ||
2315 | |||
2316 | set_SOGO(ctrl); | ||
2317 | |||
2318 | /* Wait for SOBS to be unset */ | ||
2319 | wait_for_ctrl_irq (ctrl); | ||
2320 | break; | ||
2321 | case 2: | ||
2322 | /* Do other stuff here! */ | ||
2323 | break; | ||
2324 | case 3: | ||
2325 | /* and more... */ | ||
2326 | break; | ||
2327 | } | ||
2328 | return 0; | ||
2329 | } | ||
2330 | |||
2331 | |||
2332 | /** | ||
2333 | * configure_new_device - Configures the PCI header information of one board. | ||
2334 | * | ||
2335 | * @ctrl: pointer to controller structure | ||
2336 | * @func: pointer to function structure | ||
2337 | * @behind_bridge: 1 if this is a recursive call, 0 if not | ||
2338 | * @resources: pointer to set of resource lists | ||
2339 | * | ||
2340 | * Returns 0 if success | ||
2341 | * | ||
2342 | */ | ||
2343 | static u32 configure_new_device(struct controller * ctrl, struct pci_func * func, | ||
2344 | u8 behind_bridge, struct resource_lists * resources) | ||
2345 | { | ||
2346 | u8 temp_byte, function, max_functions, stop_it; | ||
2347 | int rc; | ||
2348 | u32 ID; | ||
2349 | struct pci_func *new_slot; | ||
2350 | int index; | ||
2351 | |||
2352 | new_slot = func; | ||
2353 | |||
2354 | dbg("%s\n", __FUNCTION__); | ||
2355 | /* Check for Multi-function device */ | ||
2356 | ctrl->pci_bus->number = func->bus; | ||
2357 | rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(func->device, func->function), 0x0E, &temp_byte); | ||
2358 | if (rc) { | ||
2359 | dbg("%s: rc = %d\n", __FUNCTION__, rc); | ||
2360 | return rc; | ||
2361 | } | ||
2362 | |||
2363 | if (temp_byte & 0x80) /* Multi-function device */ | ||
2364 | max_functions = 8; | ||
2365 | else | ||
2366 | max_functions = 1; | ||
2367 | |||
2368 | function = 0; | ||
2369 | |||
2370 | do { | ||
2371 | rc = configure_new_function(ctrl, new_slot, behind_bridge, resources); | ||
2372 | |||
2373 | if (rc) { | ||
2374 | dbg("configure_new_function failed %d\n",rc); | ||
2375 | index = 0; | ||
2376 | |||
2377 | while (new_slot) { | ||
2378 | new_slot = cpqhp_slot_find(new_slot->bus, new_slot->device, index++); | ||
2379 | |||
2380 | if (new_slot) | ||
2381 | cpqhp_return_board_resources(new_slot, resources); | ||
2382 | } | ||
2383 | |||
2384 | return rc; | ||
2385 | } | ||
2386 | |||
2387 | function++; | ||
2388 | |||
2389 | stop_it = 0; | ||
2390 | |||
2391 | /* The following loop skips to the next present function | ||
2392 | * and creates a board structure */ | ||
2393 | |||
2394 | while ((function < max_functions) && (!stop_it)) { | ||
2395 | pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(func->device, function), 0x00, &ID); | ||
2396 | |||
2397 | if (ID == 0xFFFFFFFF) { /* There's nothing there. */ | ||
2398 | function++; | ||
2399 | } else { /* There's something there */ | ||
2400 | /* Setup slot structure. */ | ||
2401 | new_slot = cpqhp_slot_create(func->bus); | ||
2402 | |||
2403 | if (new_slot == NULL) | ||
2404 | return 1; | ||
2405 | |||
2406 | new_slot->bus = func->bus; | ||
2407 | new_slot->device = func->device; | ||
2408 | new_slot->function = function; | ||
2409 | new_slot->is_a_board = 1; | ||
2410 | new_slot->status = 0; | ||
2411 | |||
2412 | stop_it++; | ||
2413 | } | ||
2414 | } | ||
2415 | |||
2416 | } while (function < max_functions); | ||
2417 | dbg("returning from configure_new_device\n"); | ||
2418 | |||
2419 | return 0; | ||
2420 | } | ||
2421 | |||
2422 | |||
2423 | /* | ||
2424 | Configuration logic that involves the hotplug data structures and | ||
2425 | their bookkeeping | ||
2426 | */ | ||
2427 | |||
2428 | |||
2429 | /** | ||
2430 | * configure_new_function - Configures the PCI header information of one device | ||
2431 | * | ||
2432 | * @ctrl: pointer to controller structure | ||
2433 | * @func: pointer to function structure | ||
2434 | * @behind_bridge: 1 if this is a recursive call, 0 if not | ||
2435 | * @resources: pointer to set of resource lists | ||
2436 | * | ||
2437 | * Calls itself recursively for bridged devices. | ||
2438 | * Returns 0 if success | ||
2439 | * | ||
2440 | */ | ||
2441 | static int configure_new_function(struct controller *ctrl, struct pci_func *func, | ||
2442 | u8 behind_bridge, | ||
2443 | struct resource_lists *resources) | ||
2444 | { | ||
2445 | int cloop; | ||
2446 | u8 IRQ = 0; | ||
2447 | u8 temp_byte; | ||
2448 | u8 device; | ||
2449 | u8 class_code; | ||
2450 | u16 command; | ||
2451 | u16 temp_word; | ||
2452 | u32 temp_dword; | ||
2453 | u32 rc; | ||
2454 | u32 temp_register; | ||
2455 | u32 base; | ||
2456 | u32 ID; | ||
2457 | unsigned int devfn; | ||
2458 | struct pci_resource *mem_node; | ||
2459 | struct pci_resource *p_mem_node; | ||
2460 | struct pci_resource *io_node; | ||
2461 | struct pci_resource *bus_node; | ||
2462 | struct pci_resource *hold_mem_node; | ||
2463 | struct pci_resource *hold_p_mem_node; | ||
2464 | struct pci_resource *hold_IO_node; | ||
2465 | struct pci_resource *hold_bus_node; | ||
2466 | struct irq_mapping irqs; | ||
2467 | struct pci_func *new_slot; | ||
2468 | struct pci_bus *pci_bus; | ||
2469 | struct resource_lists temp_resources; | ||
2470 | |||
2471 | pci_bus = ctrl->pci_bus; | ||
2472 | pci_bus->number = func->bus; | ||
2473 | devfn = PCI_DEVFN(func->device, func->function); | ||
2474 | |||
2475 | /* Check for Bridge */ | ||
2476 | rc = pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &temp_byte); | ||
2477 | if (rc) | ||
2478 | return rc; | ||
2479 | |||
2480 | if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* PCI-PCI Bridge */ | ||
2481 | /* set Primary bus */ | ||
2482 | dbg("set Primary bus = %d\n", func->bus); | ||
2483 | rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_PRIMARY_BUS, func->bus); | ||
2484 | if (rc) | ||
2485 | return rc; | ||
2486 | |||
2487 | /* find range of busses to use */ | ||
2488 | dbg("find ranges of buses to use\n"); | ||
2489 | bus_node = get_max_resource(&(resources->bus_head), 1); | ||
2490 | |||
2491 | /* If we don't have any busses to allocate, we can't continue */ | ||
2492 | if (!bus_node) | ||
2493 | return -ENOMEM; | ||
2494 | |||
2495 | /* set Secondary bus */ | ||
2496 | temp_byte = bus_node->base; | ||
2497 | dbg("set Secondary bus = %d\n", bus_node->base); | ||
2498 | rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, temp_byte); | ||
2499 | if (rc) | ||
2500 | return rc; | ||
2501 | |||
2502 | /* set subordinate bus */ | ||
2503 | temp_byte = bus_node->base + bus_node->length - 1; | ||
2504 | dbg("set subordinate bus = %d\n", bus_node->base + bus_node->length - 1); | ||
2505 | rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte); | ||
2506 | if (rc) | ||
2507 | return rc; | ||
2508 | |||
2509 | /* set subordinate Latency Timer and base Latency Timer */ | ||
2510 | temp_byte = 0x40; | ||
2511 | rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SEC_LATENCY_TIMER, temp_byte); | ||
2512 | if (rc) | ||
2513 | return rc; | ||
2514 | rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_LATENCY_TIMER, temp_byte); | ||
2515 | if (rc) | ||
2516 | return rc; | ||
2517 | |||
2518 | /* set Cache Line size */ | ||
2519 | temp_byte = 0x08; | ||
2520 | rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_CACHE_LINE_SIZE, temp_byte); | ||
2521 | if (rc) | ||
2522 | return rc; | ||
2523 | |||
2524 | /* Setup the IO, memory, and prefetchable windows */ | ||
2525 | io_node = get_max_resource(&(resources->io_head), 0x1000); | ||
2526 | if (!io_node) | ||
2527 | return -ENOMEM; | ||
2528 | mem_node = get_max_resource(&(resources->mem_head), 0x100000); | ||
2529 | if (!mem_node) | ||
2530 | return -ENOMEM; | ||
2531 | p_mem_node = get_max_resource(&(resources->p_mem_head), 0x100000); | ||
2532 | if (!p_mem_node) | ||
2533 | return -ENOMEM; | ||
2534 | dbg("Setup the IO, memory, and prefetchable windows\n"); | ||
2535 | dbg("io_node\n"); | ||
2536 | dbg("(base, len, next) (%x, %x, %p)\n", io_node->base, | ||
2537 | io_node->length, io_node->next); | ||
2538 | dbg("mem_node\n"); | ||
2539 | dbg("(base, len, next) (%x, %x, %p)\n", mem_node->base, | ||
2540 | mem_node->length, mem_node->next); | ||
2541 | dbg("p_mem_node\n"); | ||
2542 | dbg("(base, len, next) (%x, %x, %p)\n", p_mem_node->base, | ||
2543 | p_mem_node->length, p_mem_node->next); | ||
2544 | |||
2545 | /* set up the IRQ info */ | ||
2546 | if (!resources->irqs) { | ||
2547 | irqs.barber_pole = 0; | ||
2548 | irqs.interrupt[0] = 0; | ||
2549 | irqs.interrupt[1] = 0; | ||
2550 | irqs.interrupt[2] = 0; | ||
2551 | irqs.interrupt[3] = 0; | ||
2552 | irqs.valid_INT = 0; | ||
2553 | } else { | ||
2554 | irqs.barber_pole = resources->irqs->barber_pole; | ||
2555 | irqs.interrupt[0] = resources->irqs->interrupt[0]; | ||
2556 | irqs.interrupt[1] = resources->irqs->interrupt[1]; | ||
2557 | irqs.interrupt[2] = resources->irqs->interrupt[2]; | ||
2558 | irqs.interrupt[3] = resources->irqs->interrupt[3]; | ||
2559 | irqs.valid_INT = resources->irqs->valid_INT; | ||
2560 | } | ||
2561 | |||
2562 | /* set up resource lists that are now aligned on top and bottom | ||
2563 | * for anything behind the bridge. */ | ||
2564 | temp_resources.bus_head = bus_node; | ||
2565 | temp_resources.io_head = io_node; | ||
2566 | temp_resources.mem_head = mem_node; | ||
2567 | temp_resources.p_mem_head = p_mem_node; | ||
2568 | temp_resources.irqs = &irqs; | ||
2569 | |||
2570 | /* Make copies of the nodes we are going to pass down so that | ||
2571 | * if there is a problem,we can just use these to free resources */ | ||
2572 | hold_bus_node = kmalloc(sizeof(*hold_bus_node), GFP_KERNEL); | ||
2573 | hold_IO_node = kmalloc(sizeof(*hold_IO_node), GFP_KERNEL); | ||
2574 | hold_mem_node = kmalloc(sizeof(*hold_mem_node), GFP_KERNEL); | ||
2575 | hold_p_mem_node = kmalloc(sizeof(*hold_p_mem_node), GFP_KERNEL); | ||
2576 | |||
2577 | if (!hold_bus_node || !hold_IO_node || !hold_mem_node || !hold_p_mem_node) { | ||
2578 | kfree(hold_bus_node); | ||
2579 | kfree(hold_IO_node); | ||
2580 | kfree(hold_mem_node); | ||
2581 | kfree(hold_p_mem_node); | ||
2582 | |||
2583 | return 1; | ||
2584 | } | ||
2585 | |||
2586 | memcpy(hold_bus_node, bus_node, sizeof(struct pci_resource)); | ||
2587 | |||
2588 | bus_node->base += 1; | ||
2589 | bus_node->length -= 1; | ||
2590 | bus_node->next = NULL; | ||
2591 | |||
2592 | /* If we have IO resources copy them and fill in the bridge's | ||
2593 | * IO range registers */ | ||
2594 | if (io_node) { | ||
2595 | memcpy(hold_IO_node, io_node, sizeof(struct pci_resource)); | ||
2596 | io_node->next = NULL; | ||
2597 | |||
2598 | /* set IO base and Limit registers */ | ||
2599 | temp_byte = io_node->base >> 8; | ||
2600 | rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_BASE, temp_byte); | ||
2601 | |||
2602 | temp_byte = (io_node->base + io_node->length - 1) >> 8; | ||
2603 | rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_LIMIT, temp_byte); | ||
2604 | } else { | ||
2605 | kfree(hold_IO_node); | ||
2606 | hold_IO_node = NULL; | ||
2607 | } | ||
2608 | |||
2609 | /* If we have memory resources copy them and fill in the | ||
2610 | * bridge's memory range registers. Otherwise, fill in the | ||
2611 | * range registers with values that disable them. */ | ||
2612 | if (mem_node) { | ||
2613 | memcpy(hold_mem_node, mem_node, sizeof(struct pci_resource)); | ||
2614 | mem_node->next = NULL; | ||
2615 | |||
2616 | /* set Mem base and Limit registers */ | ||
2617 | temp_word = mem_node->base >> 16; | ||
2618 | rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_BASE, temp_word); | ||
2619 | |||
2620 | temp_word = (mem_node->base + mem_node->length - 1) >> 16; | ||
2621 | rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); | ||
2622 | } else { | ||
2623 | temp_word = 0xFFFF; | ||
2624 | rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_BASE, temp_word); | ||
2625 | |||
2626 | temp_word = 0x0000; | ||
2627 | rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); | ||
2628 | |||
2629 | kfree(hold_mem_node); | ||
2630 | hold_mem_node = NULL; | ||
2631 | } | ||
2632 | |||
2633 | /* If we have prefetchable memory resources copy them and | ||
2634 | * fill in the bridge's memory range registers. Otherwise, | ||
2635 | * fill in the range registers with values that disable them. */ | ||
2636 | if (p_mem_node) { | ||
2637 | memcpy(hold_p_mem_node, p_mem_node, sizeof(struct pci_resource)); | ||
2638 | p_mem_node->next = NULL; | ||
2639 | |||
2640 | /* set Pre Mem base and Limit registers */ | ||
2641 | temp_word = p_mem_node->base >> 16; | ||
2642 | rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word); | ||
2643 | |||
2644 | temp_word = (p_mem_node->base + p_mem_node->length - 1) >> 16; | ||
2645 | rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); | ||
2646 | } else { | ||
2647 | temp_word = 0xFFFF; | ||
2648 | rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word); | ||
2649 | |||
2650 | temp_word = 0x0000; | ||
2651 | rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); | ||
2652 | |||
2653 | kfree(hold_p_mem_node); | ||
2654 | hold_p_mem_node = NULL; | ||
2655 | } | ||
2656 | |||
2657 | /* Adjust this to compensate for extra adjustment in first loop */ | ||
2658 | irqs.barber_pole--; | ||
2659 | |||
2660 | rc = 0; | ||
2661 | |||
2662 | /* Here we actually find the devices and configure them */ | ||
2663 | for (device = 0; (device <= 0x1F) && !rc; device++) { | ||
2664 | irqs.barber_pole = (irqs.barber_pole + 1) & 0x03; | ||
2665 | |||
2666 | ID = 0xFFFFFFFF; | ||
2667 | pci_bus->number = hold_bus_node->base; | ||
2668 | pci_bus_read_config_dword (pci_bus, PCI_DEVFN(device, 0), 0x00, &ID); | ||
2669 | pci_bus->number = func->bus; | ||
2670 | |||
2671 | if (ID != 0xFFFFFFFF) { /* device present */ | ||
2672 | /* Setup slot structure. */ | ||
2673 | new_slot = cpqhp_slot_create(hold_bus_node->base); | ||
2674 | |||
2675 | if (new_slot == NULL) { | ||
2676 | rc = -ENOMEM; | ||
2677 | continue; | ||
2678 | } | ||
2679 | |||
2680 | new_slot->bus = hold_bus_node->base; | ||
2681 | new_slot->device = device; | ||
2682 | new_slot->function = 0; | ||
2683 | new_slot->is_a_board = 1; | ||
2684 | new_slot->status = 0; | ||
2685 | |||
2686 | rc = configure_new_device(ctrl, new_slot, 1, &temp_resources); | ||
2687 | dbg("configure_new_device rc=0x%x\n",rc); | ||
2688 | } /* End of IF (device in slot?) */ | ||
2689 | } /* End of FOR loop */ | ||
2690 | |||
2691 | if (rc) | ||
2692 | goto free_and_out; | ||
2693 | /* save the interrupt routing information */ | ||
2694 | if (resources->irqs) { | ||
2695 | resources->irqs->interrupt[0] = irqs.interrupt[0]; | ||
2696 | resources->irqs->interrupt[1] = irqs.interrupt[1]; | ||
2697 | resources->irqs->interrupt[2] = irqs.interrupt[2]; | ||
2698 | resources->irqs->interrupt[3] = irqs.interrupt[3]; | ||
2699 | resources->irqs->valid_INT = irqs.valid_INT; | ||
2700 | } else if (!behind_bridge) { | ||
2701 | /* We need to hook up the interrupts here */ | ||
2702 | for (cloop = 0; cloop < 4; cloop++) { | ||
2703 | if (irqs.valid_INT & (0x01 << cloop)) { | ||
2704 | rc = cpqhp_set_irq(func->bus, func->device, | ||
2705 | 0x0A + cloop, irqs.interrupt[cloop]); | ||
2706 | if (rc) | ||
2707 | goto free_and_out; | ||
2708 | } | ||
2709 | } /* end of for loop */ | ||
2710 | } | ||
2711 | /* Return unused bus resources | ||
2712 | * First use the temporary node to store information for | ||
2713 | * the board */ | ||
2714 | if (hold_bus_node && bus_node && temp_resources.bus_head) { | ||
2715 | hold_bus_node->length = bus_node->base - hold_bus_node->base; | ||
2716 | |||
2717 | hold_bus_node->next = func->bus_head; | ||
2718 | func->bus_head = hold_bus_node; | ||
2719 | |||
2720 | temp_byte = temp_resources.bus_head->base - 1; | ||
2721 | |||
2722 | /* set subordinate bus */ | ||
2723 | rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte); | ||
2724 | |||
2725 | if (temp_resources.bus_head->length == 0) { | ||
2726 | kfree(temp_resources.bus_head); | ||
2727 | temp_resources.bus_head = NULL; | ||
2728 | } else { | ||
2729 | return_resource(&(resources->bus_head), temp_resources.bus_head); | ||
2730 | } | ||
2731 | } | ||
2732 | |||
2733 | /* If we have IO space available and there is some left, | ||
2734 | * return the unused portion */ | ||
2735 | if (hold_IO_node && temp_resources.io_head) { | ||
2736 | io_node = do_pre_bridge_resource_split(&(temp_resources.io_head), | ||
2737 | &hold_IO_node, 0x1000); | ||
2738 | |||
2739 | /* Check if we were able to split something off */ | ||
2740 | if (io_node) { | ||
2741 | hold_IO_node->base = io_node->base + io_node->length; | ||
2742 | |||
2743 | temp_byte = (hold_IO_node->base) >> 8; | ||
2744 | rc = pci_bus_write_config_word (pci_bus, devfn, PCI_IO_BASE, temp_byte); | ||
2745 | |||
2746 | return_resource(&(resources->io_head), io_node); | ||
2747 | } | ||
2748 | |||
2749 | io_node = do_bridge_resource_split(&(temp_resources.io_head), 0x1000); | ||
2750 | |||
2751 | /* Check if we were able to split something off */ | ||
2752 | if (io_node) { | ||
2753 | /* First use the temporary node to store | ||
2754 | * information for the board */ | ||
2755 | hold_IO_node->length = io_node->base - hold_IO_node->base; | ||
2756 | |||
2757 | /* If we used any, add it to the board's list */ | ||
2758 | if (hold_IO_node->length) { | ||
2759 | hold_IO_node->next = func->io_head; | ||
2760 | func->io_head = hold_IO_node; | ||
2761 | |||
2762 | temp_byte = (io_node->base - 1) >> 8; | ||
2763 | rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_LIMIT, temp_byte); | ||
2764 | |||
2765 | return_resource(&(resources->io_head), io_node); | ||
2766 | } else { | ||
2767 | /* it doesn't need any IO */ | ||
2768 | temp_word = 0x0000; | ||
2769 | rc = pci_bus_write_config_word (pci_bus, devfn, PCI_IO_LIMIT, temp_word); | ||
2770 | |||
2771 | return_resource(&(resources->io_head), io_node); | ||
2772 | kfree(hold_IO_node); | ||
2773 | } | ||
2774 | } else { | ||
2775 | /* it used most of the range */ | ||
2776 | hold_IO_node->next = func->io_head; | ||
2777 | func->io_head = hold_IO_node; | ||
2778 | } | ||
2779 | } else if (hold_IO_node) { | ||
2780 | /* it used the whole range */ | ||
2781 | hold_IO_node->next = func->io_head; | ||
2782 | func->io_head = hold_IO_node; | ||
2783 | } | ||
2784 | /* If we have memory space available and there is some left, | ||
2785 | * return the unused portion */ | ||
2786 | if (hold_mem_node && temp_resources.mem_head) { | ||
2787 | mem_node = do_pre_bridge_resource_split(&(temp_resources. mem_head), | ||
2788 | &hold_mem_node, 0x100000); | ||
2789 | |||
2790 | /* Check if we were able to split something off */ | ||
2791 | if (mem_node) { | ||
2792 | hold_mem_node->base = mem_node->base + mem_node->length; | ||
2793 | |||
2794 | temp_word = (hold_mem_node->base) >> 16; | ||
2795 | rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_BASE, temp_word); | ||
2796 | |||
2797 | return_resource(&(resources->mem_head), mem_node); | ||
2798 | } | ||
2799 | |||
2800 | mem_node = do_bridge_resource_split(&(temp_resources.mem_head), 0x100000); | ||
2801 | |||
2802 | /* Check if we were able to split something off */ | ||
2803 | if (mem_node) { | ||
2804 | /* First use the temporary node to store | ||
2805 | * information for the board */ | ||
2806 | hold_mem_node->length = mem_node->base - hold_mem_node->base; | ||
2807 | |||
2808 | if (hold_mem_node->length) { | ||
2809 | hold_mem_node->next = func->mem_head; | ||
2810 | func->mem_head = hold_mem_node; | ||
2811 | |||
2812 | /* configure end address */ | ||
2813 | temp_word = (mem_node->base - 1) >> 16; | ||
2814 | rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); | ||
2815 | |||
2816 | /* Return unused resources to the pool */ | ||
2817 | return_resource(&(resources->mem_head), mem_node); | ||
2818 | } else { | ||
2819 | /* it doesn't need any Mem */ | ||
2820 | temp_word = 0x0000; | ||
2821 | rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); | ||
2822 | |||
2823 | return_resource(&(resources->mem_head), mem_node); | ||
2824 | kfree(hold_mem_node); | ||
2825 | } | ||
2826 | } else { | ||
2827 | /* it used most of the range */ | ||
2828 | hold_mem_node->next = func->mem_head; | ||
2829 | func->mem_head = hold_mem_node; | ||
2830 | } | ||
2831 | } else if (hold_mem_node) { | ||
2832 | /* it used the whole range */ | ||
2833 | hold_mem_node->next = func->mem_head; | ||
2834 | func->mem_head = hold_mem_node; | ||
2835 | } | ||
2836 | /* If we have prefetchable memory space available and there | ||
2837 | * is some left at the end, return the unused portion */ | ||
2838 | if (hold_p_mem_node && temp_resources.p_mem_head) { | ||
2839 | p_mem_node = do_pre_bridge_resource_split(&(temp_resources.p_mem_head), | ||
2840 | &hold_p_mem_node, 0x100000); | ||
2841 | |||
2842 | /* Check if we were able to split something off */ | ||
2843 | if (p_mem_node) { | ||
2844 | hold_p_mem_node->base = p_mem_node->base + p_mem_node->length; | ||
2845 | |||
2846 | temp_word = (hold_p_mem_node->base) >> 16; | ||
2847 | rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word); | ||
2848 | |||
2849 | return_resource(&(resources->p_mem_head), p_mem_node); | ||
2850 | } | ||
2851 | |||
2852 | p_mem_node = do_bridge_resource_split(&(temp_resources.p_mem_head), 0x100000); | ||
2853 | |||
2854 | /* Check if we were able to split something off */ | ||
2855 | if (p_mem_node) { | ||
2856 | /* First use the temporary node to store | ||
2857 | * information for the board */ | ||
2858 | hold_p_mem_node->length = p_mem_node->base - hold_p_mem_node->base; | ||
2859 | |||
2860 | /* If we used any, add it to the board's list */ | ||
2861 | if (hold_p_mem_node->length) { | ||
2862 | hold_p_mem_node->next = func->p_mem_head; | ||
2863 | func->p_mem_head = hold_p_mem_node; | ||
2864 | |||
2865 | temp_word = (p_mem_node->base - 1) >> 16; | ||
2866 | rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); | ||
2867 | |||
2868 | return_resource(&(resources->p_mem_head), p_mem_node); | ||
2869 | } else { | ||
2870 | /* it doesn't need any PMem */ | ||
2871 | temp_word = 0x0000; | ||
2872 | rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); | ||
2873 | |||
2874 | return_resource(&(resources->p_mem_head), p_mem_node); | ||
2875 | kfree(hold_p_mem_node); | ||
2876 | } | ||
2877 | } else { | ||
2878 | /* it used the most of the range */ | ||
2879 | hold_p_mem_node->next = func->p_mem_head; | ||
2880 | func->p_mem_head = hold_p_mem_node; | ||
2881 | } | ||
2882 | } else if (hold_p_mem_node) { | ||
2883 | /* it used the whole range */ | ||
2884 | hold_p_mem_node->next = func->p_mem_head; | ||
2885 | func->p_mem_head = hold_p_mem_node; | ||
2886 | } | ||
2887 | /* We should be configuring an IRQ and the bridge's base address | ||
2888 | * registers if it needs them. Although we have never seen such | ||
2889 | * a device */ | ||
2890 | |||
2891 | /* enable card */ | ||
2892 | command = 0x0157; /* = PCI_COMMAND_IO | | ||
2893 | * PCI_COMMAND_MEMORY | | ||
2894 | * PCI_COMMAND_MASTER | | ||
2895 | * PCI_COMMAND_INVALIDATE | | ||
2896 | * PCI_COMMAND_PARITY | | ||
2897 | * PCI_COMMAND_SERR */ | ||
2898 | rc = pci_bus_write_config_word (pci_bus, devfn, PCI_COMMAND, command); | ||
2899 | |||
2900 | /* set Bridge Control Register */ | ||
2901 | command = 0x07; /* = PCI_BRIDGE_CTL_PARITY | | ||
2902 | * PCI_BRIDGE_CTL_SERR | | ||
2903 | * PCI_BRIDGE_CTL_NO_ISA */ | ||
2904 | rc = pci_bus_write_config_word (pci_bus, devfn, PCI_BRIDGE_CONTROL, command); | ||
2905 | } else if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_NORMAL) { | ||
2906 | /* Standard device */ | ||
2907 | rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code); | ||
2908 | |||
2909 | if (class_code == PCI_BASE_CLASS_DISPLAY) { | ||
2910 | /* Display (video) adapter (not supported) */ | ||
2911 | return DEVICE_TYPE_NOT_SUPPORTED; | ||
2912 | } | ||
2913 | /* Figure out IO and memory needs */ | ||
2914 | for (cloop = 0x10; cloop <= 0x24; cloop += 4) { | ||
2915 | temp_register = 0xFFFFFFFF; | ||
2916 | |||
2917 | dbg("CND: bus=%d, devfn=%d, offset=%d\n", pci_bus->number, devfn, cloop); | ||
2918 | rc = pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register); | ||
2919 | |||
2920 | rc = pci_bus_read_config_dword (pci_bus, devfn, cloop, &temp_register); | ||
2921 | dbg("CND: base = 0x%x\n", temp_register); | ||
2922 | |||
2923 | if (temp_register) { /* If this register is implemented */ | ||
2924 | if ((temp_register & 0x03L) == 0x01) { | ||
2925 | /* Map IO */ | ||
2926 | |||
2927 | /* set base = amount of IO space */ | ||
2928 | base = temp_register & 0xFFFFFFFC; | ||
2929 | base = ~base + 1; | ||
2930 | |||
2931 | dbg("CND: length = 0x%x\n", base); | ||
2932 | io_node = get_io_resource(&(resources->io_head), base); | ||
2933 | dbg("Got io_node start = %8.8x, length = %8.8x next (%p)\n", | ||
2934 | io_node->base, io_node->length, io_node->next); | ||
2935 | dbg("func (%p) io_head (%p)\n", func, func->io_head); | ||
2936 | |||
2937 | /* allocate the resource to the board */ | ||
2938 | if (io_node) { | ||
2939 | base = io_node->base; | ||
2940 | |||
2941 | io_node->next = func->io_head; | ||
2942 | func->io_head = io_node; | ||
2943 | } else | ||
2944 | return -ENOMEM; | ||
2945 | } else if ((temp_register & 0x0BL) == 0x08) { | ||
2946 | /* Map prefetchable memory */ | ||
2947 | base = temp_register & 0xFFFFFFF0; | ||
2948 | base = ~base + 1; | ||
2949 | |||
2950 | dbg("CND: length = 0x%x\n", base); | ||
2951 | p_mem_node = get_resource(&(resources->p_mem_head), base); | ||
2952 | |||
2953 | /* allocate the resource to the board */ | ||
2954 | if (p_mem_node) { | ||
2955 | base = p_mem_node->base; | ||
2956 | |||
2957 | p_mem_node->next = func->p_mem_head; | ||
2958 | func->p_mem_head = p_mem_node; | ||
2959 | } else | ||
2960 | return -ENOMEM; | ||
2961 | } else if ((temp_register & 0x0BL) == 0x00) { | ||
2962 | /* Map memory */ | ||
2963 | base = temp_register & 0xFFFFFFF0; | ||
2964 | base = ~base + 1; | ||
2965 | |||
2966 | dbg("CND: length = 0x%x\n", base); | ||
2967 | mem_node = get_resource(&(resources->mem_head), base); | ||
2968 | |||
2969 | /* allocate the resource to the board */ | ||
2970 | if (mem_node) { | ||
2971 | base = mem_node->base; | ||
2972 | |||
2973 | mem_node->next = func->mem_head; | ||
2974 | func->mem_head = mem_node; | ||
2975 | } else | ||
2976 | return -ENOMEM; | ||
2977 | } else if ((temp_register & 0x0BL) == 0x04) { | ||
2978 | /* Map memory */ | ||
2979 | base = temp_register & 0xFFFFFFF0; | ||
2980 | base = ~base + 1; | ||
2981 | |||
2982 | dbg("CND: length = 0x%x\n", base); | ||
2983 | mem_node = get_resource(&(resources->mem_head), base); | ||
2984 | |||
2985 | /* allocate the resource to the board */ | ||
2986 | if (mem_node) { | ||
2987 | base = mem_node->base; | ||
2988 | |||
2989 | mem_node->next = func->mem_head; | ||
2990 | func->mem_head = mem_node; | ||
2991 | } else | ||
2992 | return -ENOMEM; | ||
2993 | } else if ((temp_register & 0x0BL) == 0x06) { | ||
2994 | /* Those bits are reserved, we can't handle this */ | ||
2995 | return 1; | ||
2996 | } else { | ||
2997 | /* Requesting space below 1M */ | ||
2998 | return NOT_ENOUGH_RESOURCES; | ||
2999 | } | ||
3000 | |||
3001 | rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, base); | ||
3002 | |||
3003 | /* Check for 64-bit base */ | ||
3004 | if ((temp_register & 0x07L) == 0x04) { | ||
3005 | cloop += 4; | ||
3006 | |||
3007 | /* Upper 32 bits of address always zero | ||
3008 | * on today's systems */ | ||
3009 | /* FIXME this is probably not true on | ||
3010 | * Alpha and ia64??? */ | ||
3011 | base = 0; | ||
3012 | rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, base); | ||
3013 | } | ||
3014 | } | ||
3015 | } /* End of base register loop */ | ||
3016 | if (cpqhp_legacy_mode) { | ||
3017 | /* Figure out which interrupt pin this function uses */ | ||
3018 | rc = pci_bus_read_config_byte (pci_bus, devfn, | ||
3019 | PCI_INTERRUPT_PIN, &temp_byte); | ||
3020 | |||
3021 | /* If this function needs an interrupt and we are behind | ||
3022 | * a bridge and the pin is tied to something that's | ||
3023 | * alread mapped, set this one the same */ | ||
3024 | if (temp_byte && resources->irqs && | ||
3025 | (resources->irqs->valid_INT & | ||
3026 | (0x01 << ((temp_byte + resources->irqs->barber_pole - 1) & 0x03)))) { | ||
3027 | /* We have to share with something already set up */ | ||
3028 | IRQ = resources->irqs->interrupt[(temp_byte + | ||
3029 | resources->irqs->barber_pole - 1) & 0x03]; | ||
3030 | } else { | ||
3031 | /* Program IRQ based on card type */ | ||
3032 | rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code); | ||
3033 | |||
3034 | if (class_code == PCI_BASE_CLASS_STORAGE) { | ||
3035 | IRQ = cpqhp_disk_irq; | ||
3036 | } else { | ||
3037 | IRQ = cpqhp_nic_irq; | ||
3038 | } | ||
3039 | } | ||
3040 | |||
3041 | /* IRQ Line */ | ||
3042 | rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_INTERRUPT_LINE, IRQ); | ||
3043 | } | ||
3044 | |||
3045 | if (!behind_bridge) { | ||
3046 | rc = cpqhp_set_irq(func->bus, func->device, temp_byte + 0x09, IRQ); | ||
3047 | if (rc) | ||
3048 | return 1; | ||
3049 | } else { | ||
3050 | /* TBD - this code may also belong in the other clause | ||
3051 | * of this If statement */ | ||
3052 | resources->irqs->interrupt[(temp_byte + resources->irqs->barber_pole - 1) & 0x03] = IRQ; | ||
3053 | resources->irqs->valid_INT |= 0x01 << (temp_byte + resources->irqs->barber_pole - 1) & 0x03; | ||
3054 | } | ||
3055 | |||
3056 | /* Latency Timer */ | ||
3057 | temp_byte = 0x40; | ||
3058 | rc = pci_bus_write_config_byte(pci_bus, devfn, | ||
3059 | PCI_LATENCY_TIMER, temp_byte); | ||
3060 | |||
3061 | /* Cache Line size */ | ||
3062 | temp_byte = 0x08; | ||
3063 | rc = pci_bus_write_config_byte(pci_bus, devfn, | ||
3064 | PCI_CACHE_LINE_SIZE, temp_byte); | ||
3065 | |||
3066 | /* disable ROM base Address */ | ||
3067 | temp_dword = 0x00L; | ||
3068 | rc = pci_bus_write_config_word(pci_bus, devfn, | ||
3069 | PCI_ROM_ADDRESS, temp_dword); | ||
3070 | |||
3071 | /* enable card */ | ||
3072 | temp_word = 0x0157; /* = PCI_COMMAND_IO | | ||
3073 | * PCI_COMMAND_MEMORY | | ||
3074 | * PCI_COMMAND_MASTER | | ||
3075 | * PCI_COMMAND_INVALIDATE | | ||
3076 | * PCI_COMMAND_PARITY | | ||
3077 | * PCI_COMMAND_SERR */ | ||
3078 | rc = pci_bus_write_config_word (pci_bus, devfn, | ||
3079 | PCI_COMMAND, temp_word); | ||
3080 | } else { /* End of Not-A-Bridge else */ | ||
3081 | /* It's some strange type of PCI adapter (Cardbus?) */ | ||
3082 | return DEVICE_TYPE_NOT_SUPPORTED; | ||
3083 | } | ||
3084 | |||
3085 | func->configured = 1; | ||
3086 | |||
3087 | return 0; | ||
3088 | free_and_out: | ||
3089 | cpqhp_destroy_resource_list (&temp_resources); | ||
3090 | |||
3091 | return_resource(&(resources-> bus_head), hold_bus_node); | ||
3092 | return_resource(&(resources-> io_head), hold_IO_node); | ||
3093 | return_resource(&(resources-> mem_head), hold_mem_node); | ||
3094 | return_resource(&(resources-> p_mem_head), hold_p_mem_node); | ||
3095 | return rc; | ||
3096 | } | ||
diff --git a/drivers/pci/hotplug/cpqphp_nvram.c b/drivers/pci/hotplug/cpqphp_nvram.c new file mode 100644 index 000000000000..ac98a11bd1eb --- /dev/null +++ b/drivers/pci/hotplug/cpqphp_nvram.c | |||
@@ -0,0 +1,666 @@ | |||
1 | /* | ||
2 | * Compaq Hot Plug Controller Driver | ||
3 | * | ||
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | ||
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * Copyright (C) 2001 IBM Corp. | ||
7 | * | ||
8 | * All rights reserved. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or (at | ||
13 | * your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
18 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
19 | * details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
24 | * | ||
25 | * Send feedback to <greg@kroah.com> | ||
26 | * | ||
27 | */ | ||
28 | |||
29 | #include <linux/config.h> | ||
30 | #include <linux/module.h> | ||
31 | #include <linux/kernel.h> | ||
32 | #include <linux/types.h> | ||
33 | #include <linux/proc_fs.h> | ||
34 | #include <linux/slab.h> | ||
35 | #include <linux/workqueue.h> | ||
36 | #include <linux/pci.h> | ||
37 | #include <linux/init.h> | ||
38 | #include <asm/uaccess.h> | ||
39 | #include "cpqphp.h" | ||
40 | #include "cpqphp_nvram.h" | ||
41 | |||
42 | |||
43 | #define ROM_INT15_PHY_ADDR 0x0FF859 | ||
44 | #define READ_EV 0xD8A4 | ||
45 | #define WRITE_EV 0xD8A5 | ||
46 | |||
47 | struct register_foo { | ||
48 | union { | ||
49 | unsigned long lword; /* eax */ | ||
50 | unsigned short word; /* ax */ | ||
51 | |||
52 | struct { | ||
53 | unsigned char low; /* al */ | ||
54 | unsigned char high; /* ah */ | ||
55 | } byte; | ||
56 | } data; | ||
57 | |||
58 | unsigned char opcode; /* see below */ | ||
59 | unsigned long length; /* if the reg. is a pointer, how much data */ | ||
60 | } __attribute__ ((packed)); | ||
61 | |||
62 | struct all_reg { | ||
63 | struct register_foo eax_reg; | ||
64 | struct register_foo ebx_reg; | ||
65 | struct register_foo ecx_reg; | ||
66 | struct register_foo edx_reg; | ||
67 | struct register_foo edi_reg; | ||
68 | struct register_foo esi_reg; | ||
69 | struct register_foo eflags_reg; | ||
70 | } __attribute__ ((packed)); | ||
71 | |||
72 | |||
73 | struct ev_hrt_header { | ||
74 | u8 Version; | ||
75 | u8 num_of_ctrl; | ||
76 | u8 next; | ||
77 | }; | ||
78 | |||
79 | struct ev_hrt_ctrl { | ||
80 | u8 bus; | ||
81 | u8 device; | ||
82 | u8 function; | ||
83 | u8 mem_avail; | ||
84 | u8 p_mem_avail; | ||
85 | u8 io_avail; | ||
86 | u8 bus_avail; | ||
87 | u8 next; | ||
88 | }; | ||
89 | |||
90 | |||
91 | static u8 evbuffer_init; | ||
92 | static u8 evbuffer_length; | ||
93 | static u8 evbuffer[1024]; | ||
94 | |||
95 | static void __iomem *compaq_int15_entry_point; | ||
96 | |||
97 | static spinlock_t int15_lock; /* lock for ordering int15_bios_call() */ | ||
98 | |||
99 | |||
100 | /* This is a series of function that deals with | ||
101 | setting & getting the hotplug resource table in some environment variable. | ||
102 | */ | ||
103 | |||
104 | /* | ||
105 | * We really shouldn't be doing this unless there is a _very_ good reason to!!! | ||
106 | * greg k-h | ||
107 | */ | ||
108 | |||
109 | |||
110 | static u32 add_byte( u32 **p_buffer, u8 value, u32 *used, u32 *avail) | ||
111 | { | ||
112 | u8 **tByte; | ||
113 | |||
114 | if ((*used + 1) > *avail) | ||
115 | return(1); | ||
116 | |||
117 | *((u8*)*p_buffer) = value; | ||
118 | tByte = (u8**)p_buffer; | ||
119 | (*tByte)++; | ||
120 | *used+=1; | ||
121 | return(0); | ||
122 | } | ||
123 | |||
124 | |||
125 | static u32 add_dword( u32 **p_buffer, u32 value, u32 *used, u32 *avail) | ||
126 | { | ||
127 | if ((*used + 4) > *avail) | ||
128 | return(1); | ||
129 | |||
130 | **p_buffer = value; | ||
131 | (*p_buffer)++; | ||
132 | *used+=4; | ||
133 | return(0); | ||
134 | } | ||
135 | |||
136 | |||
137 | /* | ||
138 | * check_for_compaq_ROM | ||
139 | * | ||
140 | * this routine verifies that the ROM OEM string is 'COMPAQ' | ||
141 | * | ||
142 | * returns 0 for non-Compaq ROM, 1 for Compaq ROM | ||
143 | */ | ||
144 | static int check_for_compaq_ROM (void __iomem *rom_start) | ||
145 | { | ||
146 | u8 temp1, temp2, temp3, temp4, temp5, temp6; | ||
147 | int result = 0; | ||
148 | |||
149 | temp1 = readb(rom_start + 0xffea + 0); | ||
150 | temp2 = readb(rom_start + 0xffea + 1); | ||
151 | temp3 = readb(rom_start + 0xffea + 2); | ||
152 | temp4 = readb(rom_start + 0xffea + 3); | ||
153 | temp5 = readb(rom_start + 0xffea + 4); | ||
154 | temp6 = readb(rom_start + 0xffea + 5); | ||
155 | if ((temp1 == 'C') && | ||
156 | (temp2 == 'O') && | ||
157 | (temp3 == 'M') && | ||
158 | (temp4 == 'P') && | ||
159 | (temp5 == 'A') && | ||
160 | (temp6 == 'Q')) { | ||
161 | result = 1; | ||
162 | } | ||
163 | dbg ("%s - returned %d\n", __FUNCTION__, result); | ||
164 | return result; | ||
165 | } | ||
166 | |||
167 | |||
168 | static u32 access_EV (u16 operation, u8 *ev_name, u8 *buffer, u32 *buf_size) | ||
169 | { | ||
170 | unsigned long flags; | ||
171 | int op = operation; | ||
172 | int ret_val; | ||
173 | |||
174 | if (!compaq_int15_entry_point) | ||
175 | return -ENODEV; | ||
176 | |||
177 | spin_lock_irqsave(&int15_lock, flags); | ||
178 | __asm__ ( | ||
179 | "xorl %%ebx,%%ebx\n" \ | ||
180 | "xorl %%edx,%%edx\n" \ | ||
181 | "pushf\n" \ | ||
182 | "push %%cs\n" \ | ||
183 | "cli\n" \ | ||
184 | "call *%6\n" | ||
185 | : "=c" (*buf_size), "=a" (ret_val) | ||
186 | : "a" (op), "c" (*buf_size), "S" (ev_name), | ||
187 | "D" (buffer), "m" (compaq_int15_entry_point) | ||
188 | : "%ebx", "%edx"); | ||
189 | spin_unlock_irqrestore(&int15_lock, flags); | ||
190 | |||
191 | return((ret_val & 0xFF00) >> 8); | ||
192 | } | ||
193 | |||
194 | |||
195 | /* | ||
196 | * load_HRT | ||
197 | * | ||
198 | * Read the hot plug Resource Table from NVRAM | ||
199 | */ | ||
200 | static int load_HRT (void __iomem *rom_start) | ||
201 | { | ||
202 | u32 available; | ||
203 | u32 temp_dword; | ||
204 | u8 temp_byte = 0xFF; | ||
205 | u32 rc; | ||
206 | |||
207 | if (!check_for_compaq_ROM(rom_start)) { | ||
208 | return -ENODEV; | ||
209 | } | ||
210 | |||
211 | available = 1024; | ||
212 | |||
213 | // Now load the EV | ||
214 | temp_dword = available; | ||
215 | |||
216 | rc = access_EV(READ_EV, "CQTHPS", evbuffer, &temp_dword); | ||
217 | |||
218 | evbuffer_length = temp_dword; | ||
219 | |||
220 | // We're maintaining the resource lists so write FF to invalidate old info | ||
221 | temp_dword = 1; | ||
222 | |||
223 | rc = access_EV(WRITE_EV, "CQTHPS", &temp_byte, &temp_dword); | ||
224 | |||
225 | return rc; | ||
226 | } | ||
227 | |||
228 | |||
229 | /* | ||
230 | * store_HRT | ||
231 | * | ||
232 | * Save the hot plug Resource Table in NVRAM | ||
233 | */ | ||
234 | static u32 store_HRT (void __iomem *rom_start) | ||
235 | { | ||
236 | u32 *buffer; | ||
237 | u32 *pFill; | ||
238 | u32 usedbytes; | ||
239 | u32 available; | ||
240 | u32 temp_dword; | ||
241 | u32 rc; | ||
242 | u8 loop; | ||
243 | u8 numCtrl = 0; | ||
244 | struct controller *ctrl; | ||
245 | struct pci_resource *resNode; | ||
246 | struct ev_hrt_header *p_EV_header; | ||
247 | struct ev_hrt_ctrl *p_ev_ctrl; | ||
248 | |||
249 | available = 1024; | ||
250 | |||
251 | if (!check_for_compaq_ROM(rom_start)) { | ||
252 | return(1); | ||
253 | } | ||
254 | |||
255 | buffer = (u32*) evbuffer; | ||
256 | |||
257 | if (!buffer) | ||
258 | return(1); | ||
259 | |||
260 | pFill = buffer; | ||
261 | usedbytes = 0; | ||
262 | |||
263 | p_EV_header = (struct ev_hrt_header *) pFill; | ||
264 | |||
265 | ctrl = cpqhp_ctrl_list; | ||
266 | |||
267 | // The revision of this structure | ||
268 | rc = add_byte( &pFill, 1 + ctrl->push_flag, &usedbytes, &available); | ||
269 | if (rc) | ||
270 | return(rc); | ||
271 | |||
272 | // The number of controllers | ||
273 | rc = add_byte( &pFill, 1, &usedbytes, &available); | ||
274 | if (rc) | ||
275 | return(rc); | ||
276 | |||
277 | while (ctrl) { | ||
278 | p_ev_ctrl = (struct ev_hrt_ctrl *) pFill; | ||
279 | |||
280 | numCtrl++; | ||
281 | |||
282 | // The bus number | ||
283 | rc = add_byte( &pFill, ctrl->bus, &usedbytes, &available); | ||
284 | if (rc) | ||
285 | return(rc); | ||
286 | |||
287 | // The device Number | ||
288 | rc = add_byte( &pFill, PCI_SLOT(ctrl->pci_dev->devfn), &usedbytes, &available); | ||
289 | if (rc) | ||
290 | return(rc); | ||
291 | |||
292 | // The function Number | ||
293 | rc = add_byte( &pFill, PCI_FUNC(ctrl->pci_dev->devfn), &usedbytes, &available); | ||
294 | if (rc) | ||
295 | return(rc); | ||
296 | |||
297 | // Skip the number of available entries | ||
298 | rc = add_dword( &pFill, 0, &usedbytes, &available); | ||
299 | if (rc) | ||
300 | return(rc); | ||
301 | |||
302 | // Figure out memory Available | ||
303 | |||
304 | resNode = ctrl->mem_head; | ||
305 | |||
306 | loop = 0; | ||
307 | |||
308 | while (resNode) { | ||
309 | loop ++; | ||
310 | |||
311 | // base | ||
312 | rc = add_dword( &pFill, resNode->base, &usedbytes, &available); | ||
313 | if (rc) | ||
314 | return(rc); | ||
315 | |||
316 | // length | ||
317 | rc = add_dword( &pFill, resNode->length, &usedbytes, &available); | ||
318 | if (rc) | ||
319 | return(rc); | ||
320 | |||
321 | resNode = resNode->next; | ||
322 | } | ||
323 | |||
324 | // Fill in the number of entries | ||
325 | p_ev_ctrl->mem_avail = loop; | ||
326 | |||
327 | // Figure out prefetchable memory Available | ||
328 | |||
329 | resNode = ctrl->p_mem_head; | ||
330 | |||
331 | loop = 0; | ||
332 | |||
333 | while (resNode) { | ||
334 | loop ++; | ||
335 | |||
336 | // base | ||
337 | rc = add_dword( &pFill, resNode->base, &usedbytes, &available); | ||
338 | if (rc) | ||
339 | return(rc); | ||
340 | |||
341 | // length | ||
342 | rc = add_dword( &pFill, resNode->length, &usedbytes, &available); | ||
343 | if (rc) | ||
344 | return(rc); | ||
345 | |||
346 | resNode = resNode->next; | ||
347 | } | ||
348 | |||
349 | // Fill in the number of entries | ||
350 | p_ev_ctrl->p_mem_avail = loop; | ||
351 | |||
352 | // Figure out IO Available | ||
353 | |||
354 | resNode = ctrl->io_head; | ||
355 | |||
356 | loop = 0; | ||
357 | |||
358 | while (resNode) { | ||
359 | loop ++; | ||
360 | |||
361 | // base | ||
362 | rc = add_dword( &pFill, resNode->base, &usedbytes, &available); | ||
363 | if (rc) | ||
364 | return(rc); | ||
365 | |||
366 | // length | ||
367 | rc = add_dword( &pFill, resNode->length, &usedbytes, &available); | ||
368 | if (rc) | ||
369 | return(rc); | ||
370 | |||
371 | resNode = resNode->next; | ||
372 | } | ||
373 | |||
374 | // Fill in the number of entries | ||
375 | p_ev_ctrl->io_avail = loop; | ||
376 | |||
377 | // Figure out bus Available | ||
378 | |||
379 | resNode = ctrl->bus_head; | ||
380 | |||
381 | loop = 0; | ||
382 | |||
383 | while (resNode) { | ||
384 | loop ++; | ||
385 | |||
386 | // base | ||
387 | rc = add_dword( &pFill, resNode->base, &usedbytes, &available); | ||
388 | if (rc) | ||
389 | return(rc); | ||
390 | |||
391 | // length | ||
392 | rc = add_dword( &pFill, resNode->length, &usedbytes, &available); | ||
393 | if (rc) | ||
394 | return(rc); | ||
395 | |||
396 | resNode = resNode->next; | ||
397 | } | ||
398 | |||
399 | // Fill in the number of entries | ||
400 | p_ev_ctrl->bus_avail = loop; | ||
401 | |||
402 | ctrl = ctrl->next; | ||
403 | } | ||
404 | |||
405 | p_EV_header->num_of_ctrl = numCtrl; | ||
406 | |||
407 | // Now store the EV | ||
408 | |||
409 | temp_dword = usedbytes; | ||
410 | |||
411 | rc = access_EV(WRITE_EV, "CQTHPS", (u8*) buffer, &temp_dword); | ||
412 | |||
413 | dbg("usedbytes = 0x%x, length = 0x%x\n", usedbytes, temp_dword); | ||
414 | |||
415 | evbuffer_length = temp_dword; | ||
416 | |||
417 | if (rc) { | ||
418 | err(msg_unable_to_save); | ||
419 | return(1); | ||
420 | } | ||
421 | |||
422 | return(0); | ||
423 | } | ||
424 | |||
425 | |||
426 | void compaq_nvram_init (void __iomem *rom_start) | ||
427 | { | ||
428 | if (rom_start) { | ||
429 | compaq_int15_entry_point = (rom_start + ROM_INT15_PHY_ADDR - ROM_PHY_ADDR); | ||
430 | } | ||
431 | dbg("int15 entry = %p\n", compaq_int15_entry_point); | ||
432 | |||
433 | /* initialize our int15 lock */ | ||
434 | spin_lock_init(&int15_lock); | ||
435 | } | ||
436 | |||
437 | |||
438 | int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl) | ||
439 | { | ||
440 | u8 bus, device, function; | ||
441 | u8 nummem, numpmem, numio, numbus; | ||
442 | u32 rc; | ||
443 | u8 *p_byte; | ||
444 | struct pci_resource *mem_node; | ||
445 | struct pci_resource *p_mem_node; | ||
446 | struct pci_resource *io_node; | ||
447 | struct pci_resource *bus_node; | ||
448 | struct ev_hrt_ctrl *p_ev_ctrl; | ||
449 | struct ev_hrt_header *p_EV_header; | ||
450 | |||
451 | if (!evbuffer_init) { | ||
452 | // Read the resource list information in from NVRAM | ||
453 | if (load_HRT(rom_start)) | ||
454 | memset (evbuffer, 0, 1024); | ||
455 | |||
456 | evbuffer_init = 1; | ||
457 | } | ||
458 | |||
459 | // If we saved information in NVRAM, use it now | ||
460 | p_EV_header = (struct ev_hrt_header *) evbuffer; | ||
461 | |||
462 | // The following code is for systems where version 1.0 of this | ||
463 | // driver has been loaded, but doesn't support the hardware. | ||
464 | // In that case, the driver would incorrectly store something | ||
465 | // in NVRAM. | ||
466 | if ((p_EV_header->Version == 2) || | ||
467 | ((p_EV_header->Version == 1) && !ctrl->push_flag)) { | ||
468 | p_byte = &(p_EV_header->next); | ||
469 | |||
470 | p_ev_ctrl = (struct ev_hrt_ctrl *) &(p_EV_header->next); | ||
471 | |||
472 | p_byte += 3; | ||
473 | |||
474 | if (p_byte > ((u8*)p_EV_header + evbuffer_length)) | ||
475 | return 2; | ||
476 | |||
477 | bus = p_ev_ctrl->bus; | ||
478 | device = p_ev_ctrl->device; | ||
479 | function = p_ev_ctrl->function; | ||
480 | |||
481 | while ((bus != ctrl->bus) || | ||
482 | (device != PCI_SLOT(ctrl->pci_dev->devfn)) || | ||
483 | (function != PCI_FUNC(ctrl->pci_dev->devfn))) { | ||
484 | nummem = p_ev_ctrl->mem_avail; | ||
485 | numpmem = p_ev_ctrl->p_mem_avail; | ||
486 | numio = p_ev_ctrl->io_avail; | ||
487 | numbus = p_ev_ctrl->bus_avail; | ||
488 | |||
489 | p_byte += 4; | ||
490 | |||
491 | if (p_byte > ((u8*)p_EV_header + evbuffer_length)) | ||
492 | return 2; | ||
493 | |||
494 | // Skip forward to the next entry | ||
495 | p_byte += (nummem + numpmem + numio + numbus) * 8; | ||
496 | |||
497 | if (p_byte > ((u8*)p_EV_header + evbuffer_length)) | ||
498 | return 2; | ||
499 | |||
500 | p_ev_ctrl = (struct ev_hrt_ctrl *) p_byte; | ||
501 | |||
502 | p_byte += 3; | ||
503 | |||
504 | if (p_byte > ((u8*)p_EV_header + evbuffer_length)) | ||
505 | return 2; | ||
506 | |||
507 | bus = p_ev_ctrl->bus; | ||
508 | device = p_ev_ctrl->device; | ||
509 | function = p_ev_ctrl->function; | ||
510 | } | ||
511 | |||
512 | nummem = p_ev_ctrl->mem_avail; | ||
513 | numpmem = p_ev_ctrl->p_mem_avail; | ||
514 | numio = p_ev_ctrl->io_avail; | ||
515 | numbus = p_ev_ctrl->bus_avail; | ||
516 | |||
517 | p_byte += 4; | ||
518 | |||
519 | if (p_byte > ((u8*)p_EV_header + evbuffer_length)) | ||
520 | return 2; | ||
521 | |||
522 | while (nummem--) { | ||
523 | mem_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); | ||
524 | |||
525 | if (!mem_node) | ||
526 | break; | ||
527 | |||
528 | mem_node->base = *(u32*)p_byte; | ||
529 | dbg("mem base = %8.8x\n",mem_node->base); | ||
530 | p_byte += 4; | ||
531 | |||
532 | if (p_byte > ((u8*)p_EV_header + evbuffer_length)) { | ||
533 | kfree(mem_node); | ||
534 | return 2; | ||
535 | } | ||
536 | |||
537 | mem_node->length = *(u32*)p_byte; | ||
538 | dbg("mem length = %8.8x\n",mem_node->length); | ||
539 | p_byte += 4; | ||
540 | |||
541 | if (p_byte > ((u8*)p_EV_header + evbuffer_length)) { | ||
542 | kfree(mem_node); | ||
543 | return 2; | ||
544 | } | ||
545 | |||
546 | mem_node->next = ctrl->mem_head; | ||
547 | ctrl->mem_head = mem_node; | ||
548 | } | ||
549 | |||
550 | while (numpmem--) { | ||
551 | p_mem_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); | ||
552 | |||
553 | if (!p_mem_node) | ||
554 | break; | ||
555 | |||
556 | p_mem_node->base = *(u32*)p_byte; | ||
557 | dbg("pre-mem base = %8.8x\n",p_mem_node->base); | ||
558 | p_byte += 4; | ||
559 | |||
560 | if (p_byte > ((u8*)p_EV_header + evbuffer_length)) { | ||
561 | kfree(p_mem_node); | ||
562 | return 2; | ||
563 | } | ||
564 | |||
565 | p_mem_node->length = *(u32*)p_byte; | ||
566 | dbg("pre-mem length = %8.8x\n",p_mem_node->length); | ||
567 | p_byte += 4; | ||
568 | |||
569 | if (p_byte > ((u8*)p_EV_header + evbuffer_length)) { | ||
570 | kfree(p_mem_node); | ||
571 | return 2; | ||
572 | } | ||
573 | |||
574 | p_mem_node->next = ctrl->p_mem_head; | ||
575 | ctrl->p_mem_head = p_mem_node; | ||
576 | } | ||
577 | |||
578 | while (numio--) { | ||
579 | io_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); | ||
580 | |||
581 | if (!io_node) | ||
582 | break; | ||
583 | |||
584 | io_node->base = *(u32*)p_byte; | ||
585 | dbg("io base = %8.8x\n",io_node->base); | ||
586 | p_byte += 4; | ||
587 | |||
588 | if (p_byte > ((u8*)p_EV_header + evbuffer_length)) { | ||
589 | kfree(io_node); | ||
590 | return 2; | ||
591 | } | ||
592 | |||
593 | io_node->length = *(u32*)p_byte; | ||
594 | dbg("io length = %8.8x\n",io_node->length); | ||
595 | p_byte += 4; | ||
596 | |||
597 | if (p_byte > ((u8*)p_EV_header + evbuffer_length)) { | ||
598 | kfree(io_node); | ||
599 | return 2; | ||
600 | } | ||
601 | |||
602 | io_node->next = ctrl->io_head; | ||
603 | ctrl->io_head = io_node; | ||
604 | } | ||
605 | |||
606 | while (numbus--) { | ||
607 | bus_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); | ||
608 | |||
609 | if (!bus_node) | ||
610 | break; | ||
611 | |||
612 | bus_node->base = *(u32*)p_byte; | ||
613 | p_byte += 4; | ||
614 | |||
615 | if (p_byte > ((u8*)p_EV_header + evbuffer_length)) { | ||
616 | kfree(bus_node); | ||
617 | return 2; | ||
618 | } | ||
619 | |||
620 | bus_node->length = *(u32*)p_byte; | ||
621 | p_byte += 4; | ||
622 | |||
623 | if (p_byte > ((u8*)p_EV_header + evbuffer_length)) { | ||
624 | kfree(bus_node); | ||
625 | return 2; | ||
626 | } | ||
627 | |||
628 | bus_node->next = ctrl->bus_head; | ||
629 | ctrl->bus_head = bus_node; | ||
630 | } | ||
631 | |||
632 | // If all of the following fail, we don't have any resources for | ||
633 | // hot plug add | ||
634 | rc = 1; | ||
635 | rc &= cpqhp_resource_sort_and_combine(&(ctrl->mem_head)); | ||
636 | rc &= cpqhp_resource_sort_and_combine(&(ctrl->p_mem_head)); | ||
637 | rc &= cpqhp_resource_sort_and_combine(&(ctrl->io_head)); | ||
638 | rc &= cpqhp_resource_sort_and_combine(&(ctrl->bus_head)); | ||
639 | |||
640 | if (rc) | ||
641 | return(rc); | ||
642 | } else { | ||
643 | if ((evbuffer[0] != 0) && (!ctrl->push_flag)) | ||
644 | return 1; | ||
645 | } | ||
646 | |||
647 | return 0; | ||
648 | } | ||
649 | |||
650 | |||
651 | int compaq_nvram_store (void __iomem *rom_start) | ||
652 | { | ||
653 | int rc = 1; | ||
654 | |||
655 | if (rom_start == NULL) | ||
656 | return -ENODEV; | ||
657 | |||
658 | if (evbuffer_init) { | ||
659 | rc = store_HRT(rom_start); | ||
660 | if (rc) { | ||
661 | err(msg_unable_to_save); | ||
662 | } | ||
663 | } | ||
664 | return rc; | ||
665 | } | ||
666 | |||
diff --git a/drivers/pci/hotplug/cpqphp_nvram.h b/drivers/pci/hotplug/cpqphp_nvram.h new file mode 100644 index 000000000000..e89c0702119d --- /dev/null +++ b/drivers/pci/hotplug/cpqphp_nvram.h | |||
@@ -0,0 +1,57 @@ | |||
1 | /* | ||
2 | * Compaq Hot Plug Controller Driver | ||
3 | * | ||
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | ||
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * | ||
7 | * All rights reserved. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or (at | ||
12 | * your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, but | ||
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
17 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
18 | * details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
23 | * | ||
24 | * Send feedback to <greg@kroah.com> | ||
25 | * | ||
26 | */ | ||
27 | |||
28 | #ifndef _CPQPHP_NVRAM_H | ||
29 | #define _CPQPHP_NVRAM_H | ||
30 | |||
31 | #ifndef CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM | ||
32 | |||
33 | static inline void compaq_nvram_init (void __iomem *rom_start) | ||
34 | { | ||
35 | return; | ||
36 | } | ||
37 | |||
38 | static inline int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl) | ||
39 | { | ||
40 | return 0; | ||
41 | } | ||
42 | |||
43 | static inline int compaq_nvram_store (void __iomem *rom_start) | ||
44 | { | ||
45 | return 0; | ||
46 | } | ||
47 | |||
48 | #else | ||
49 | |||
50 | extern void compaq_nvram_init (void __iomem *rom_start); | ||
51 | extern int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl); | ||
52 | extern int compaq_nvram_store (void __iomem *rom_start); | ||
53 | |||
54 | #endif | ||
55 | |||
56 | #endif | ||
57 | |||
diff --git a/drivers/pci/hotplug/cpqphp_pci.c b/drivers/pci/hotplug/cpqphp_pci.c new file mode 100644 index 000000000000..93e39c4096a9 --- /dev/null +++ b/drivers/pci/hotplug/cpqphp_pci.c | |||
@@ -0,0 +1,1569 @@ | |||
1 | /* | ||
2 | * Compaq Hot Plug Controller Driver | ||
3 | * | ||
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | ||
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * Copyright (C) 2001 IBM Corp. | ||
7 | * | ||
8 | * All rights reserved. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or (at | ||
13 | * your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
18 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
19 | * details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
24 | * | ||
25 | * Send feedback to <greg@kroah.com> | ||
26 | * | ||
27 | */ | ||
28 | |||
29 | #include <linux/config.h> | ||
30 | #include <linux/module.h> | ||
31 | #include <linux/kernel.h> | ||
32 | #include <linux/types.h> | ||
33 | #include <linux/slab.h> | ||
34 | #include <linux/workqueue.h> | ||
35 | #include <linux/proc_fs.h> | ||
36 | #include <linux/pci.h> | ||
37 | #include "../pci.h" | ||
38 | #include "cpqphp.h" | ||
39 | #include "cpqphp_nvram.h" | ||
40 | #include "../../../arch/i386/pci/pci.h" /* horrible hack showing how processor dependent we are... */ | ||
41 | |||
42 | |||
43 | u8 cpqhp_nic_irq; | ||
44 | u8 cpqhp_disk_irq; | ||
45 | |||
46 | static u16 unused_IRQ; | ||
47 | |||
48 | /* | ||
49 | * detect_HRT_floating_pointer | ||
50 | * | ||
51 | * find the Hot Plug Resource Table in the specified region of memory. | ||
52 | * | ||
53 | */ | ||
54 | static void __iomem *detect_HRT_floating_pointer(void __iomem *begin, void __iomem *end) | ||
55 | { | ||
56 | void __iomem *fp; | ||
57 | void __iomem *endp; | ||
58 | u8 temp1, temp2, temp3, temp4; | ||
59 | int status = 0; | ||
60 | |||
61 | endp = (end - sizeof(struct hrt) + 1); | ||
62 | |||
63 | for (fp = begin; fp <= endp; fp += 16) { | ||
64 | temp1 = readb(fp + SIG0); | ||
65 | temp2 = readb(fp + SIG1); | ||
66 | temp3 = readb(fp + SIG2); | ||
67 | temp4 = readb(fp + SIG3); | ||
68 | if (temp1 == '$' && | ||
69 | temp2 == 'H' && | ||
70 | temp3 == 'R' && | ||
71 | temp4 == 'T') { | ||
72 | status = 1; | ||
73 | break; | ||
74 | } | ||
75 | } | ||
76 | |||
77 | if (!status) | ||
78 | fp = NULL; | ||
79 | |||
80 | dbg("Discovered Hotplug Resource Table at %p\n", fp); | ||
81 | return fp; | ||
82 | } | ||
83 | |||
84 | |||
85 | int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func) | ||
86 | { | ||
87 | unsigned char bus; | ||
88 | struct pci_bus *child; | ||
89 | int num; | ||
90 | |||
91 | if (func->pci_dev == NULL) | ||
92 | func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function)); | ||
93 | |||
94 | /* No pci device, we need to create it then */ | ||
95 | if (func->pci_dev == NULL) { | ||
96 | dbg("INFO: pci_dev still null\n"); | ||
97 | |||
98 | num = pci_scan_slot(ctrl->pci_dev->bus, PCI_DEVFN(func->device, func->function)); | ||
99 | if (num) | ||
100 | pci_bus_add_devices(ctrl->pci_dev->bus); | ||
101 | |||
102 | func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function)); | ||
103 | if (func->pci_dev == NULL) { | ||
104 | dbg("ERROR: pci_dev still null\n"); | ||
105 | return 0; | ||
106 | } | ||
107 | } | ||
108 | |||
109 | if (func->pci_dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { | ||
110 | pci_read_config_byte(func->pci_dev, PCI_SECONDARY_BUS, &bus); | ||
111 | child = (struct pci_bus*) pci_add_new_bus(func->pci_dev->bus, (func->pci_dev), bus); | ||
112 | pci_do_scan_bus(child); | ||
113 | } | ||
114 | |||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | |||
119 | int cpqhp_unconfigure_device(struct pci_func* func) | ||
120 | { | ||
121 | int j; | ||
122 | |||
123 | dbg("%s: bus/dev/func = %x/%x/%x\n", __FUNCTION__, func->bus, func->device, func->function); | ||
124 | |||
125 | for (j=0; j<8 ; j++) { | ||
126 | struct pci_dev* temp = pci_find_slot(func->bus, PCI_DEVFN(func->device, j)); | ||
127 | if (temp) | ||
128 | pci_remove_bus_device(temp); | ||
129 | } | ||
130 | return 0; | ||
131 | } | ||
132 | |||
133 | static int PCI_RefinedAccessConfig(struct pci_bus *bus, unsigned int devfn, u8 offset, u32 *value) | ||
134 | { | ||
135 | u32 vendID = 0; | ||
136 | |||
137 | if (pci_bus_read_config_dword (bus, devfn, PCI_VENDOR_ID, &vendID) == -1) | ||
138 | return -1; | ||
139 | if (vendID == 0xffffffff) | ||
140 | return -1; | ||
141 | return pci_bus_read_config_dword (bus, devfn, offset, value); | ||
142 | } | ||
143 | |||
144 | |||
145 | /* | ||
146 | * cpqhp_set_irq | ||
147 | * | ||
148 | * @bus_num: bus number of PCI device | ||
149 | * @dev_num: device number of PCI device | ||
150 | * @slot: pointer to u8 where slot number will be returned | ||
151 | */ | ||
152 | int cpqhp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num) | ||
153 | { | ||
154 | int rc = 0; | ||
155 | |||
156 | if (cpqhp_legacy_mode) { | ||
157 | struct pci_dev *fakedev; | ||
158 | struct pci_bus *fakebus; | ||
159 | u16 temp_word; | ||
160 | |||
161 | fakedev = kmalloc(sizeof(*fakedev), GFP_KERNEL); | ||
162 | fakebus = kmalloc(sizeof(*fakebus), GFP_KERNEL); | ||
163 | if (!fakedev || !fakebus) { | ||
164 | kfree(fakedev); | ||
165 | kfree(fakebus); | ||
166 | return -ENOMEM; | ||
167 | } | ||
168 | |||
169 | fakedev->devfn = dev_num << 3; | ||
170 | fakedev->bus = fakebus; | ||
171 | fakebus->number = bus_num; | ||
172 | dbg("%s: dev %d, bus %d, pin %d, num %d\n", | ||
173 | __FUNCTION__, dev_num, bus_num, int_pin, irq_num); | ||
174 | rc = pcibios_set_irq_routing(fakedev, int_pin - 0x0a, irq_num); | ||
175 | kfree(fakedev); | ||
176 | kfree(fakebus); | ||
177 | dbg("%s: rc %d\n", __FUNCTION__, rc); | ||
178 | if (!rc) | ||
179 | return !rc; | ||
180 | |||
181 | // set the Edge Level Control Register (ELCR) | ||
182 | temp_word = inb(0x4d0); | ||
183 | temp_word |= inb(0x4d1) << 8; | ||
184 | |||
185 | temp_word |= 0x01 << irq_num; | ||
186 | |||
187 | // This should only be for x86 as it sets the Edge Level Control Register | ||
188 | outb((u8) (temp_word & 0xFF), 0x4d0); | ||
189 | outb((u8) ((temp_word & 0xFF00) >> 8), 0x4d1); | ||
190 | rc = 0; | ||
191 | } | ||
192 | |||
193 | return rc; | ||
194 | } | ||
195 | |||
196 | |||
197 | /* | ||
198 | * WTF??? This function isn't in the code, yet a function calls it, but the | ||
199 | * compiler optimizes it away? strange. Here as a placeholder to keep the | ||
200 | * compiler happy. | ||
201 | */ | ||
202 | static int PCI_ScanBusNonBridge (u8 bus, u8 device) | ||
203 | { | ||
204 | return 0; | ||
205 | } | ||
206 | |||
207 | static int PCI_ScanBusForNonBridge(struct controller *ctrl, u8 bus_num, u8 * dev_num) | ||
208 | { | ||
209 | u16 tdevice; | ||
210 | u32 work; | ||
211 | u8 tbus; | ||
212 | |||
213 | ctrl->pci_bus->number = bus_num; | ||
214 | |||
215 | for (tdevice = 0; tdevice < 0xFF; tdevice++) { | ||
216 | //Scan for access first | ||
217 | if (PCI_RefinedAccessConfig(ctrl->pci_bus, tdevice, 0x08, &work) == -1) | ||
218 | continue; | ||
219 | dbg("Looking for nonbridge bus_num %d dev_num %d\n", bus_num, tdevice); | ||
220 | //Yep we got one. Not a bridge ? | ||
221 | if ((work >> 8) != PCI_TO_PCI_BRIDGE_CLASS) { | ||
222 | *dev_num = tdevice; | ||
223 | dbg("found it !\n"); | ||
224 | return 0; | ||
225 | } | ||
226 | } | ||
227 | for (tdevice = 0; tdevice < 0xFF; tdevice++) { | ||
228 | //Scan for access first | ||
229 | if (PCI_RefinedAccessConfig(ctrl->pci_bus, tdevice, 0x08, &work) == -1) | ||
230 | continue; | ||
231 | dbg("Looking for bridge bus_num %d dev_num %d\n", bus_num, tdevice); | ||
232 | //Yep we got one. bridge ? | ||
233 | if ((work >> 8) == PCI_TO_PCI_BRIDGE_CLASS) { | ||
234 | pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(tdevice, 0), PCI_SECONDARY_BUS, &tbus); | ||
235 | dbg("Recurse on bus_num %d tdevice %d\n", tbus, tdevice); | ||
236 | if (PCI_ScanBusNonBridge(tbus, tdevice) == 0) | ||
237 | return 0; | ||
238 | } | ||
239 | } | ||
240 | |||
241 | return -1; | ||
242 | } | ||
243 | |||
244 | |||
245 | static int PCI_GetBusDevHelper(struct controller *ctrl, u8 *bus_num, u8 *dev_num, u8 slot, u8 nobridge) | ||
246 | { | ||
247 | struct irq_routing_table *PCIIRQRoutingInfoLength; | ||
248 | long len; | ||
249 | long loop; | ||
250 | u32 work; | ||
251 | |||
252 | u8 tbus, tdevice, tslot; | ||
253 | |||
254 | PCIIRQRoutingInfoLength = pcibios_get_irq_routing_table(); | ||
255 | if (!PCIIRQRoutingInfoLength) | ||
256 | return -1; | ||
257 | |||
258 | len = (PCIIRQRoutingInfoLength->size - | ||
259 | sizeof(struct irq_routing_table)) / sizeof(struct irq_info); | ||
260 | // Make sure I got at least one entry | ||
261 | if (len == 0) { | ||
262 | if (PCIIRQRoutingInfoLength != NULL) | ||
263 | kfree(PCIIRQRoutingInfoLength ); | ||
264 | return -1; | ||
265 | } | ||
266 | |||
267 | for (loop = 0; loop < len; ++loop) { | ||
268 | tbus = PCIIRQRoutingInfoLength->slots[loop].bus; | ||
269 | tdevice = PCIIRQRoutingInfoLength->slots[loop].devfn; | ||
270 | tslot = PCIIRQRoutingInfoLength->slots[loop].slot; | ||
271 | |||
272 | if (tslot == slot) { | ||
273 | *bus_num = tbus; | ||
274 | *dev_num = tdevice; | ||
275 | ctrl->pci_bus->number = tbus; | ||
276 | pci_bus_read_config_dword (ctrl->pci_bus, *dev_num, PCI_VENDOR_ID, &work); | ||
277 | if (!nobridge || (work == 0xffffffff)) { | ||
278 | if (PCIIRQRoutingInfoLength != NULL) | ||
279 | kfree(PCIIRQRoutingInfoLength ); | ||
280 | return 0; | ||
281 | } | ||
282 | |||
283 | dbg("bus_num %d devfn %d\n", *bus_num, *dev_num); | ||
284 | pci_bus_read_config_dword (ctrl->pci_bus, *dev_num, PCI_CLASS_REVISION, &work); | ||
285 | dbg("work >> 8 (%x) = BRIDGE (%x)\n", work >> 8, PCI_TO_PCI_BRIDGE_CLASS); | ||
286 | |||
287 | if ((work >> 8) == PCI_TO_PCI_BRIDGE_CLASS) { | ||
288 | pci_bus_read_config_byte (ctrl->pci_bus, *dev_num, PCI_SECONDARY_BUS, &tbus); | ||
289 | dbg("Scan bus for Non Bridge: bus %d\n", tbus); | ||
290 | if (PCI_ScanBusForNonBridge(ctrl, tbus, dev_num) == 0) { | ||
291 | *bus_num = tbus; | ||
292 | if (PCIIRQRoutingInfoLength != NULL) | ||
293 | kfree(PCIIRQRoutingInfoLength ); | ||
294 | return 0; | ||
295 | } | ||
296 | } else { | ||
297 | if (PCIIRQRoutingInfoLength != NULL) | ||
298 | kfree(PCIIRQRoutingInfoLength ); | ||
299 | return 0; | ||
300 | } | ||
301 | |||
302 | } | ||
303 | } | ||
304 | if (PCIIRQRoutingInfoLength != NULL) | ||
305 | kfree(PCIIRQRoutingInfoLength ); | ||
306 | return -1; | ||
307 | } | ||
308 | |||
309 | |||
310 | int cpqhp_get_bus_dev (struct controller *ctrl, u8 * bus_num, u8 * dev_num, u8 slot) | ||
311 | { | ||
312 | return PCI_GetBusDevHelper(ctrl, bus_num, dev_num, slot, 0); //plain (bridges allowed) | ||
313 | } | ||
314 | |||
315 | |||
316 | /* More PCI configuration routines; this time centered around hotplug controller */ | ||
317 | |||
318 | |||
319 | /* | ||
320 | * cpqhp_save_config | ||
321 | * | ||
322 | * Reads configuration for all slots in a PCI bus and saves info. | ||
323 | * | ||
324 | * Note: For non-hot plug busses, the slot # saved is the device # | ||
325 | * | ||
326 | * returns 0 if success | ||
327 | */ | ||
328 | int cpqhp_save_config(struct controller *ctrl, int busnumber, int is_hot_plug) | ||
329 | { | ||
330 | long rc; | ||
331 | u8 class_code; | ||
332 | u8 header_type; | ||
333 | u32 ID; | ||
334 | u8 secondary_bus; | ||
335 | struct pci_func *new_slot; | ||
336 | int sub_bus; | ||
337 | int FirstSupported; | ||
338 | int LastSupported; | ||
339 | int max_functions; | ||
340 | int function; | ||
341 | u8 DevError; | ||
342 | int device = 0; | ||
343 | int cloop = 0; | ||
344 | int stop_it; | ||
345 | int index; | ||
346 | |||
347 | // Decide which slots are supported | ||
348 | |||
349 | if (is_hot_plug) { | ||
350 | //********************************* | ||
351 | // is_hot_plug is the slot mask | ||
352 | //********************************* | ||
353 | FirstSupported = is_hot_plug >> 4; | ||
354 | LastSupported = FirstSupported + (is_hot_plug & 0x0F) - 1; | ||
355 | } else { | ||
356 | FirstSupported = 0; | ||
357 | LastSupported = 0x1F; | ||
358 | } | ||
359 | |||
360 | // Save PCI configuration space for all devices in supported slots | ||
361 | ctrl->pci_bus->number = busnumber; | ||
362 | for (device = FirstSupported; device <= LastSupported; device++) { | ||
363 | ID = 0xFFFFFFFF; | ||
364 | rc = pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(device, 0), PCI_VENDOR_ID, &ID); | ||
365 | |||
366 | if (ID != 0xFFFFFFFF) { // device in slot | ||
367 | rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(device, 0), 0x0B, &class_code); | ||
368 | if (rc) | ||
369 | return rc; | ||
370 | |||
371 | rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(device, 0), PCI_HEADER_TYPE, &header_type); | ||
372 | if (rc) | ||
373 | return rc; | ||
374 | |||
375 | // If multi-function device, set max_functions to 8 | ||
376 | if (header_type & 0x80) | ||
377 | max_functions = 8; | ||
378 | else | ||
379 | max_functions = 1; | ||
380 | |||
381 | function = 0; | ||
382 | |||
383 | do { | ||
384 | DevError = 0; | ||
385 | |||
386 | if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // P-P Bridge | ||
387 | // Recurse the subordinate bus | ||
388 | // get the subordinate bus number | ||
389 | rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(device, function), PCI_SECONDARY_BUS, &secondary_bus); | ||
390 | if (rc) { | ||
391 | return rc; | ||
392 | } else { | ||
393 | sub_bus = (int) secondary_bus; | ||
394 | |||
395 | // Save secondary bus cfg spc | ||
396 | // with this recursive call. | ||
397 | rc = cpqhp_save_config(ctrl, sub_bus, 0); | ||
398 | if (rc) | ||
399 | return rc; | ||
400 | ctrl->pci_bus->number = busnumber; | ||
401 | } | ||
402 | } | ||
403 | |||
404 | index = 0; | ||
405 | new_slot = cpqhp_slot_find(busnumber, device, index++); | ||
406 | while (new_slot && | ||
407 | (new_slot->function != (u8) function)) | ||
408 | new_slot = cpqhp_slot_find(busnumber, device, index++); | ||
409 | |||
410 | if (!new_slot) { | ||
411 | // Setup slot structure. | ||
412 | new_slot = cpqhp_slot_create(busnumber); | ||
413 | |||
414 | if (new_slot == NULL) | ||
415 | return(1); | ||
416 | } | ||
417 | |||
418 | new_slot->bus = (u8) busnumber; | ||
419 | new_slot->device = (u8) device; | ||
420 | new_slot->function = (u8) function; | ||
421 | new_slot->is_a_board = 1; | ||
422 | new_slot->switch_save = 0x10; | ||
423 | // In case of unsupported board | ||
424 | new_slot->status = DevError; | ||
425 | new_slot->pci_dev = pci_find_slot(new_slot->bus, (new_slot->device << 3) | new_slot->function); | ||
426 | |||
427 | for (cloop = 0; cloop < 0x20; cloop++) { | ||
428 | rc = pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(device, function), cloop << 2, (u32 *) & (new_slot-> config_space [cloop])); | ||
429 | if (rc) | ||
430 | return rc; | ||
431 | } | ||
432 | |||
433 | function++; | ||
434 | |||
435 | stop_it = 0; | ||
436 | |||
437 | // this loop skips to the next present function | ||
438 | // reading in Class Code and Header type. | ||
439 | |||
440 | while ((function < max_functions)&&(!stop_it)) { | ||
441 | rc = pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(device, function), PCI_VENDOR_ID, &ID); | ||
442 | if (ID == 0xFFFFFFFF) { // nothing there. | ||
443 | function++; | ||
444 | } else { // Something there | ||
445 | rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(device, function), 0x0B, &class_code); | ||
446 | if (rc) | ||
447 | return rc; | ||
448 | |||
449 | rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(device, function), PCI_HEADER_TYPE, &header_type); | ||
450 | if (rc) | ||
451 | return rc; | ||
452 | |||
453 | stop_it++; | ||
454 | } | ||
455 | } | ||
456 | |||
457 | } while (function < max_functions); | ||
458 | } // End of IF (device in slot?) | ||
459 | else if (is_hot_plug) { | ||
460 | // Setup slot structure with entry for empty slot | ||
461 | new_slot = cpqhp_slot_create(busnumber); | ||
462 | |||
463 | if (new_slot == NULL) { | ||
464 | return(1); | ||
465 | } | ||
466 | |||
467 | new_slot->bus = (u8) busnumber; | ||
468 | new_slot->device = (u8) device; | ||
469 | new_slot->function = 0; | ||
470 | new_slot->is_a_board = 0; | ||
471 | new_slot->presence_save = 0; | ||
472 | new_slot->switch_save = 0; | ||
473 | } | ||
474 | } // End of FOR loop | ||
475 | |||
476 | return(0); | ||
477 | } | ||
478 | |||
479 | |||
480 | /* | ||
481 | * cpqhp_save_slot_config | ||
482 | * | ||
483 | * Saves configuration info for all PCI devices in a given slot | ||
484 | * including subordinate busses. | ||
485 | * | ||
486 | * returns 0 if success | ||
487 | */ | ||
488 | int cpqhp_save_slot_config (struct controller *ctrl, struct pci_func * new_slot) | ||
489 | { | ||
490 | long rc; | ||
491 | u8 class_code; | ||
492 | u8 header_type; | ||
493 | u32 ID; | ||
494 | u8 secondary_bus; | ||
495 | int sub_bus; | ||
496 | int max_functions; | ||
497 | int function; | ||
498 | int cloop = 0; | ||
499 | int stop_it; | ||
500 | |||
501 | ID = 0xFFFFFFFF; | ||
502 | |||
503 | ctrl->pci_bus->number = new_slot->bus; | ||
504 | pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), PCI_VENDOR_ID, &ID); | ||
505 | |||
506 | if (ID != 0xFFFFFFFF) { // device in slot | ||
507 | pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), 0x0B, &class_code); | ||
508 | pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), PCI_HEADER_TYPE, &header_type); | ||
509 | |||
510 | if (header_type & 0x80) // Multi-function device | ||
511 | max_functions = 8; | ||
512 | else | ||
513 | max_functions = 1; | ||
514 | |||
515 | function = 0; | ||
516 | |||
517 | do { | ||
518 | if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge | ||
519 | // Recurse the subordinate bus | ||
520 | pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_SECONDARY_BUS, &secondary_bus); | ||
521 | |||
522 | sub_bus = (int) secondary_bus; | ||
523 | |||
524 | // Save the config headers for the secondary bus. | ||
525 | rc = cpqhp_save_config(ctrl, sub_bus, 0); | ||
526 | if (rc) | ||
527 | return(rc); | ||
528 | ctrl->pci_bus->number = new_slot->bus; | ||
529 | |||
530 | } // End of IF | ||
531 | |||
532 | new_slot->status = 0; | ||
533 | |||
534 | for (cloop = 0; cloop < 0x20; cloop++) { | ||
535 | pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), cloop << 2, (u32 *) & (new_slot-> config_space [cloop])); | ||
536 | } | ||
537 | |||
538 | function++; | ||
539 | |||
540 | stop_it = 0; | ||
541 | |||
542 | // this loop skips to the next present function | ||
543 | // reading in the Class Code and the Header type. | ||
544 | |||
545 | while ((function < max_functions) && (!stop_it)) { | ||
546 | pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_VENDOR_ID, &ID); | ||
547 | |||
548 | if (ID == 0xFFFFFFFF) { // nothing there. | ||
549 | function++; | ||
550 | } else { // Something there | ||
551 | pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), 0x0B, &class_code); | ||
552 | |||
553 | pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_HEADER_TYPE, &header_type); | ||
554 | |||
555 | stop_it++; | ||
556 | } | ||
557 | } | ||
558 | |||
559 | } while (function < max_functions); | ||
560 | } // End of IF (device in slot?) | ||
561 | else { | ||
562 | return 2; | ||
563 | } | ||
564 | |||
565 | return 0; | ||
566 | } | ||
567 | |||
568 | |||
569 | /* | ||
570 | * cpqhp_save_base_addr_length | ||
571 | * | ||
572 | * Saves the length of all base address registers for the | ||
573 | * specified slot. this is for hot plug REPLACE | ||
574 | * | ||
575 | * returns 0 if success | ||
576 | */ | ||
577 | int cpqhp_save_base_addr_length(struct controller *ctrl, struct pci_func * func) | ||
578 | { | ||
579 | u8 cloop; | ||
580 | u8 header_type; | ||
581 | u8 secondary_bus; | ||
582 | u8 type; | ||
583 | int sub_bus; | ||
584 | u32 temp_register; | ||
585 | u32 base; | ||
586 | u32 rc; | ||
587 | struct pci_func *next; | ||
588 | int index = 0; | ||
589 | struct pci_bus *pci_bus = ctrl->pci_bus; | ||
590 | unsigned int devfn; | ||
591 | |||
592 | func = cpqhp_slot_find(func->bus, func->device, index++); | ||
593 | |||
594 | while (func != NULL) { | ||
595 | pci_bus->number = func->bus; | ||
596 | devfn = PCI_DEVFN(func->device, func->function); | ||
597 | |||
598 | // Check for Bridge | ||
599 | pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type); | ||
600 | |||
601 | if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { | ||
602 | // PCI-PCI Bridge | ||
603 | pci_bus_read_config_byte (pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus); | ||
604 | |||
605 | sub_bus = (int) secondary_bus; | ||
606 | |||
607 | next = cpqhp_slot_list[sub_bus]; | ||
608 | |||
609 | while (next != NULL) { | ||
610 | rc = cpqhp_save_base_addr_length(ctrl, next); | ||
611 | if (rc) | ||
612 | return rc; | ||
613 | |||
614 | next = next->next; | ||
615 | } | ||
616 | pci_bus->number = func->bus; | ||
617 | |||
618 | //FIXME: this loop is duplicated in the non-bridge case. The two could be rolled together | ||
619 | // Figure out IO and memory base lengths | ||
620 | for (cloop = 0x10; cloop <= 0x14; cloop += 4) { | ||
621 | temp_register = 0xFFFFFFFF; | ||
622 | pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register); | ||
623 | pci_bus_read_config_dword (pci_bus, devfn, cloop, &base); | ||
624 | |||
625 | if (base) { // If this register is implemented | ||
626 | if (base & 0x01L) { | ||
627 | // IO base | ||
628 | // set base = amount of IO space requested | ||
629 | base = base & 0xFFFFFFFE; | ||
630 | base = (~base) + 1; | ||
631 | |||
632 | type = 1; | ||
633 | } else { | ||
634 | // memory base | ||
635 | base = base & 0xFFFFFFF0; | ||
636 | base = (~base) + 1; | ||
637 | |||
638 | type = 0; | ||
639 | } | ||
640 | } else { | ||
641 | base = 0x0L; | ||
642 | type = 0; | ||
643 | } | ||
644 | |||
645 | // Save information in slot structure | ||
646 | func->base_length[(cloop - 0x10) >> 2] = | ||
647 | base; | ||
648 | func->base_type[(cloop - 0x10) >> 2] = type; | ||
649 | |||
650 | } // End of base register loop | ||
651 | |||
652 | |||
653 | } else if ((header_type & 0x7F) == 0x00) { // PCI-PCI Bridge | ||
654 | // Figure out IO and memory base lengths | ||
655 | for (cloop = 0x10; cloop <= 0x24; cloop += 4) { | ||
656 | temp_register = 0xFFFFFFFF; | ||
657 | pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register); | ||
658 | pci_bus_read_config_dword (pci_bus, devfn, cloop, &base); | ||
659 | |||
660 | if (base) { // If this register is implemented | ||
661 | if (base & 0x01L) { | ||
662 | // IO base | ||
663 | // base = amount of IO space requested | ||
664 | base = base & 0xFFFFFFFE; | ||
665 | base = (~base) + 1; | ||
666 | |||
667 | type = 1; | ||
668 | } else { | ||
669 | // memory base | ||
670 | // base = amount of memory space requested | ||
671 | base = base & 0xFFFFFFF0; | ||
672 | base = (~base) + 1; | ||
673 | |||
674 | type = 0; | ||
675 | } | ||
676 | } else { | ||
677 | base = 0x0L; | ||
678 | type = 0; | ||
679 | } | ||
680 | |||
681 | // Save information in slot structure | ||
682 | func->base_length[(cloop - 0x10) >> 2] = base; | ||
683 | func->base_type[(cloop - 0x10) >> 2] = type; | ||
684 | |||
685 | } // End of base register loop | ||
686 | |||
687 | } else { // Some other unknown header type | ||
688 | } | ||
689 | |||
690 | // find the next device in this slot | ||
691 | func = cpqhp_slot_find(func->bus, func->device, index++); | ||
692 | } | ||
693 | |||
694 | return(0); | ||
695 | } | ||
696 | |||
697 | |||
698 | /* | ||
699 | * cpqhp_save_used_resources | ||
700 | * | ||
701 | * Stores used resource information for existing boards. this is | ||
702 | * for boards that were in the system when this driver was loaded. | ||
703 | * this function is for hot plug ADD | ||
704 | * | ||
705 | * returns 0 if success | ||
706 | */ | ||
707 | int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func) | ||
708 | { | ||
709 | u8 cloop; | ||
710 | u8 header_type; | ||
711 | u8 secondary_bus; | ||
712 | u8 temp_byte; | ||
713 | u8 b_base; | ||
714 | u8 b_length; | ||
715 | u16 command; | ||
716 | u16 save_command; | ||
717 | u16 w_base; | ||
718 | u16 w_length; | ||
719 | u32 temp_register; | ||
720 | u32 save_base; | ||
721 | u32 base; | ||
722 | int index = 0; | ||
723 | struct pci_resource *mem_node; | ||
724 | struct pci_resource *p_mem_node; | ||
725 | struct pci_resource *io_node; | ||
726 | struct pci_resource *bus_node; | ||
727 | struct pci_bus *pci_bus = ctrl->pci_bus; | ||
728 | unsigned int devfn; | ||
729 | |||
730 | func = cpqhp_slot_find(func->bus, func->device, index++); | ||
731 | |||
732 | while ((func != NULL) && func->is_a_board) { | ||
733 | pci_bus->number = func->bus; | ||
734 | devfn = PCI_DEVFN(func->device, func->function); | ||
735 | |||
736 | // Save the command register | ||
737 | pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &save_command); | ||
738 | |||
739 | // disable card | ||
740 | command = 0x00; | ||
741 | pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command); | ||
742 | |||
743 | // Check for Bridge | ||
744 | pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &header_type); | ||
745 | |||
746 | if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge | ||
747 | // Clear Bridge Control Register | ||
748 | command = 0x00; | ||
749 | pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, command); | ||
750 | pci_bus_read_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus); | ||
751 | pci_bus_read_config_byte(pci_bus, devfn, PCI_SUBORDINATE_BUS, &temp_byte); | ||
752 | |||
753 | bus_node = kmalloc(sizeof(*bus_node), GFP_KERNEL); | ||
754 | if (!bus_node) | ||
755 | return -ENOMEM; | ||
756 | |||
757 | bus_node->base = secondary_bus; | ||
758 | bus_node->length = temp_byte - secondary_bus + 1; | ||
759 | |||
760 | bus_node->next = func->bus_head; | ||
761 | func->bus_head = bus_node; | ||
762 | |||
763 | // Save IO base and Limit registers | ||
764 | pci_bus_read_config_byte(pci_bus, devfn, PCI_IO_BASE, &b_base); | ||
765 | pci_bus_read_config_byte(pci_bus, devfn, PCI_IO_LIMIT, &b_length); | ||
766 | |||
767 | if ((b_base <= b_length) && (save_command & 0x01)) { | ||
768 | io_node = kmalloc(sizeof(*io_node), GFP_KERNEL); | ||
769 | if (!io_node) | ||
770 | return -ENOMEM; | ||
771 | |||
772 | io_node->base = (b_base & 0xF0) << 8; | ||
773 | io_node->length = (b_length - b_base + 0x10) << 8; | ||
774 | |||
775 | io_node->next = func->io_head; | ||
776 | func->io_head = io_node; | ||
777 | } | ||
778 | |||
779 | // Save memory base and Limit registers | ||
780 | pci_bus_read_config_word(pci_bus, devfn, PCI_MEMORY_BASE, &w_base); | ||
781 | pci_bus_read_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, &w_length); | ||
782 | |||
783 | if ((w_base <= w_length) && (save_command & 0x02)) { | ||
784 | mem_node = kmalloc(sizeof(*mem_node), GFP_KERNEL); | ||
785 | if (!mem_node) | ||
786 | return -ENOMEM; | ||
787 | |||
788 | mem_node->base = w_base << 16; | ||
789 | mem_node->length = (w_length - w_base + 0x10) << 16; | ||
790 | |||
791 | mem_node->next = func->mem_head; | ||
792 | func->mem_head = mem_node; | ||
793 | } | ||
794 | |||
795 | // Save prefetchable memory base and Limit registers | ||
796 | pci_bus_read_config_word(pci_bus, devfn, PCI_PREF_MEMORY_BASE, &w_base); | ||
797 | pci_bus_read_config_word(pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &w_length); | ||
798 | |||
799 | if ((w_base <= w_length) && (save_command & 0x02)) { | ||
800 | p_mem_node = kmalloc(sizeof(*p_mem_node), GFP_KERNEL); | ||
801 | if (!p_mem_node) | ||
802 | return -ENOMEM; | ||
803 | |||
804 | p_mem_node->base = w_base << 16; | ||
805 | p_mem_node->length = (w_length - w_base + 0x10) << 16; | ||
806 | |||
807 | p_mem_node->next = func->p_mem_head; | ||
808 | func->p_mem_head = p_mem_node; | ||
809 | } | ||
810 | // Figure out IO and memory base lengths | ||
811 | for (cloop = 0x10; cloop <= 0x14; cloop += 4) { | ||
812 | pci_bus_read_config_dword (pci_bus, devfn, cloop, &save_base); | ||
813 | |||
814 | temp_register = 0xFFFFFFFF; | ||
815 | pci_bus_write_config_dword(pci_bus, devfn, cloop, temp_register); | ||
816 | pci_bus_read_config_dword(pci_bus, devfn, cloop, &base); | ||
817 | |||
818 | temp_register = base; | ||
819 | |||
820 | if (base) { // If this register is implemented | ||
821 | if (((base & 0x03L) == 0x01) | ||
822 | && (save_command & 0x01)) { | ||
823 | // IO base | ||
824 | // set temp_register = amount of IO space requested | ||
825 | temp_register = base & 0xFFFFFFFE; | ||
826 | temp_register = (~temp_register) + 1; | ||
827 | |||
828 | io_node = kmalloc(sizeof(*io_node), | ||
829 | GFP_KERNEL); | ||
830 | if (!io_node) | ||
831 | return -ENOMEM; | ||
832 | |||
833 | io_node->base = | ||
834 | save_base & (~0x03L); | ||
835 | io_node->length = temp_register; | ||
836 | |||
837 | io_node->next = func->io_head; | ||
838 | func->io_head = io_node; | ||
839 | } else | ||
840 | if (((base & 0x0BL) == 0x08) | ||
841 | && (save_command & 0x02)) { | ||
842 | // prefetchable memory base | ||
843 | temp_register = base & 0xFFFFFFF0; | ||
844 | temp_register = (~temp_register) + 1; | ||
845 | |||
846 | p_mem_node = kmalloc(sizeof(*p_mem_node), | ||
847 | GFP_KERNEL); | ||
848 | if (!p_mem_node) | ||
849 | return -ENOMEM; | ||
850 | |||
851 | p_mem_node->base = save_base & (~0x0FL); | ||
852 | p_mem_node->length = temp_register; | ||
853 | |||
854 | p_mem_node->next = func->p_mem_head; | ||
855 | func->p_mem_head = p_mem_node; | ||
856 | } else | ||
857 | if (((base & 0x0BL) == 0x00) | ||
858 | && (save_command & 0x02)) { | ||
859 | // prefetchable memory base | ||
860 | temp_register = base & 0xFFFFFFF0; | ||
861 | temp_register = (~temp_register) + 1; | ||
862 | |||
863 | mem_node = kmalloc(sizeof(*mem_node), | ||
864 | GFP_KERNEL); | ||
865 | if (!mem_node) | ||
866 | return -ENOMEM; | ||
867 | |||
868 | mem_node->base = save_base & (~0x0FL); | ||
869 | mem_node->length = temp_register; | ||
870 | |||
871 | mem_node->next = func->mem_head; | ||
872 | func->mem_head = mem_node; | ||
873 | } else | ||
874 | return(1); | ||
875 | } | ||
876 | } // End of base register loop | ||
877 | } else if ((header_type & 0x7F) == 0x00) { // Standard header | ||
878 | // Figure out IO and memory base lengths | ||
879 | for (cloop = 0x10; cloop <= 0x24; cloop += 4) { | ||
880 | pci_bus_read_config_dword(pci_bus, devfn, cloop, &save_base); | ||
881 | |||
882 | temp_register = 0xFFFFFFFF; | ||
883 | pci_bus_write_config_dword(pci_bus, devfn, cloop, temp_register); | ||
884 | pci_bus_read_config_dword(pci_bus, devfn, cloop, &base); | ||
885 | |||
886 | temp_register = base; | ||
887 | |||
888 | if (base) { // If this register is implemented | ||
889 | if (((base & 0x03L) == 0x01) | ||
890 | && (save_command & 0x01)) { | ||
891 | // IO base | ||
892 | // set temp_register = amount of IO space requested | ||
893 | temp_register = base & 0xFFFFFFFE; | ||
894 | temp_register = (~temp_register) + 1; | ||
895 | |||
896 | io_node = kmalloc(sizeof(*io_node), | ||
897 | GFP_KERNEL); | ||
898 | if (!io_node) | ||
899 | return -ENOMEM; | ||
900 | |||
901 | io_node->base = save_base & (~0x01L); | ||
902 | io_node->length = temp_register; | ||
903 | |||
904 | io_node->next = func->io_head; | ||
905 | func->io_head = io_node; | ||
906 | } else | ||
907 | if (((base & 0x0BL) == 0x08) | ||
908 | && (save_command & 0x02)) { | ||
909 | // prefetchable memory base | ||
910 | temp_register = base & 0xFFFFFFF0; | ||
911 | temp_register = (~temp_register) + 1; | ||
912 | |||
913 | p_mem_node = kmalloc(sizeof(*p_mem_node), | ||
914 | GFP_KERNEL); | ||
915 | if (!p_mem_node) | ||
916 | return -ENOMEM; | ||
917 | |||
918 | p_mem_node->base = save_base & (~0x0FL); | ||
919 | p_mem_node->length = temp_register; | ||
920 | |||
921 | p_mem_node->next = func->p_mem_head; | ||
922 | func->p_mem_head = p_mem_node; | ||
923 | } else | ||
924 | if (((base & 0x0BL) == 0x00) | ||
925 | && (save_command & 0x02)) { | ||
926 | // prefetchable memory base | ||
927 | temp_register = base & 0xFFFFFFF0; | ||
928 | temp_register = (~temp_register) + 1; | ||
929 | |||
930 | mem_node = kmalloc(sizeof(*mem_node), | ||
931 | GFP_KERNEL); | ||
932 | if (!mem_node) | ||
933 | return -ENOMEM; | ||
934 | |||
935 | mem_node->base = save_base & (~0x0FL); | ||
936 | mem_node->length = temp_register; | ||
937 | |||
938 | mem_node->next = func->mem_head; | ||
939 | func->mem_head = mem_node; | ||
940 | } else | ||
941 | return(1); | ||
942 | } | ||
943 | } // End of base register loop | ||
944 | } else { // Some other unknown header type | ||
945 | } | ||
946 | |||
947 | // find the next device in this slot | ||
948 | func = cpqhp_slot_find(func->bus, func->device, index++); | ||
949 | } | ||
950 | |||
951 | return(0); | ||
952 | } | ||
953 | |||
954 | |||
955 | /* | ||
956 | * cpqhp_configure_board | ||
957 | * | ||
958 | * Copies saved configuration information to one slot. | ||
959 | * this is called recursively for bridge devices. | ||
960 | * this is for hot plug REPLACE! | ||
961 | * | ||
962 | * returns 0 if success | ||
963 | */ | ||
964 | int cpqhp_configure_board(struct controller *ctrl, struct pci_func * func) | ||
965 | { | ||
966 | int cloop; | ||
967 | u8 header_type; | ||
968 | u8 secondary_bus; | ||
969 | int sub_bus; | ||
970 | struct pci_func *next; | ||
971 | u32 temp; | ||
972 | u32 rc; | ||
973 | int index = 0; | ||
974 | struct pci_bus *pci_bus = ctrl->pci_bus; | ||
975 | unsigned int devfn; | ||
976 | |||
977 | func = cpqhp_slot_find(func->bus, func->device, index++); | ||
978 | |||
979 | while (func != NULL) { | ||
980 | pci_bus->number = func->bus; | ||
981 | devfn = PCI_DEVFN(func->device, func->function); | ||
982 | |||
983 | // Start at the top of config space so that the control | ||
984 | // registers are programmed last | ||
985 | for (cloop = 0x3C; cloop > 0; cloop -= 4) { | ||
986 | pci_bus_write_config_dword (pci_bus, devfn, cloop, func->config_space[cloop >> 2]); | ||
987 | } | ||
988 | |||
989 | pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type); | ||
990 | |||
991 | // If this is a bridge device, restore subordinate devices | ||
992 | if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge | ||
993 | pci_bus_read_config_byte (pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus); | ||
994 | |||
995 | sub_bus = (int) secondary_bus; | ||
996 | |||
997 | next = cpqhp_slot_list[sub_bus]; | ||
998 | |||
999 | while (next != NULL) { | ||
1000 | rc = cpqhp_configure_board(ctrl, next); | ||
1001 | if (rc) | ||
1002 | return rc; | ||
1003 | |||
1004 | next = next->next; | ||
1005 | } | ||
1006 | } else { | ||
1007 | |||
1008 | // Check all the base Address Registers to make sure | ||
1009 | // they are the same. If not, the board is different. | ||
1010 | |||
1011 | for (cloop = 16; cloop < 40; cloop += 4) { | ||
1012 | pci_bus_read_config_dword (pci_bus, devfn, cloop, &temp); | ||
1013 | |||
1014 | if (temp != func->config_space[cloop >> 2]) { | ||
1015 | dbg("Config space compare failure!!! offset = %x\n", cloop); | ||
1016 | dbg("bus = %x, device = %x, function = %x\n", func->bus, func->device, func->function); | ||
1017 | dbg("temp = %x, config space = %x\n\n", temp, func->config_space[cloop >> 2]); | ||
1018 | return 1; | ||
1019 | } | ||
1020 | } | ||
1021 | } | ||
1022 | |||
1023 | func->configured = 1; | ||
1024 | |||
1025 | func = cpqhp_slot_find(func->bus, func->device, index++); | ||
1026 | } | ||
1027 | |||
1028 | return 0; | ||
1029 | } | ||
1030 | |||
1031 | |||
1032 | /* | ||
1033 | * cpqhp_valid_replace | ||
1034 | * | ||
1035 | * this function checks to see if a board is the same as the | ||
1036 | * one it is replacing. this check will detect if the device's | ||
1037 | * vendor or device id's are the same | ||
1038 | * | ||
1039 | * returns 0 if the board is the same nonzero otherwise | ||
1040 | */ | ||
1041 | int cpqhp_valid_replace(struct controller *ctrl, struct pci_func * func) | ||
1042 | { | ||
1043 | u8 cloop; | ||
1044 | u8 header_type; | ||
1045 | u8 secondary_bus; | ||
1046 | u8 type; | ||
1047 | u32 temp_register = 0; | ||
1048 | u32 base; | ||
1049 | u32 rc; | ||
1050 | struct pci_func *next; | ||
1051 | int index = 0; | ||
1052 | struct pci_bus *pci_bus = ctrl->pci_bus; | ||
1053 | unsigned int devfn; | ||
1054 | |||
1055 | if (!func->is_a_board) | ||
1056 | return(ADD_NOT_SUPPORTED); | ||
1057 | |||
1058 | func = cpqhp_slot_find(func->bus, func->device, index++); | ||
1059 | |||
1060 | while (func != NULL) { | ||
1061 | pci_bus->number = func->bus; | ||
1062 | devfn = PCI_DEVFN(func->device, func->function); | ||
1063 | |||
1064 | pci_bus_read_config_dword (pci_bus, devfn, PCI_VENDOR_ID, &temp_register); | ||
1065 | |||
1066 | // No adapter present | ||
1067 | if (temp_register == 0xFFFFFFFF) | ||
1068 | return(NO_ADAPTER_PRESENT); | ||
1069 | |||
1070 | if (temp_register != func->config_space[0]) | ||
1071 | return(ADAPTER_NOT_SAME); | ||
1072 | |||
1073 | // Check for same revision number and class code | ||
1074 | pci_bus_read_config_dword (pci_bus, devfn, PCI_CLASS_REVISION, &temp_register); | ||
1075 | |||
1076 | // Adapter not the same | ||
1077 | if (temp_register != func->config_space[0x08 >> 2]) | ||
1078 | return(ADAPTER_NOT_SAME); | ||
1079 | |||
1080 | // Check for Bridge | ||
1081 | pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type); | ||
1082 | |||
1083 | if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge | ||
1084 | // In order to continue checking, we must program the | ||
1085 | // bus registers in the bridge to respond to accesses | ||
1086 | // for it's subordinate bus(es) | ||
1087 | |||
1088 | temp_register = func->config_space[0x18 >> 2]; | ||
1089 | pci_bus_write_config_dword (pci_bus, devfn, PCI_PRIMARY_BUS, temp_register); | ||
1090 | |||
1091 | secondary_bus = (temp_register >> 8) & 0xFF; | ||
1092 | |||
1093 | next = cpqhp_slot_list[secondary_bus]; | ||
1094 | |||
1095 | while (next != NULL) { | ||
1096 | rc = cpqhp_valid_replace(ctrl, next); | ||
1097 | if (rc) | ||
1098 | return rc; | ||
1099 | |||
1100 | next = next->next; | ||
1101 | } | ||
1102 | |||
1103 | } | ||
1104 | // Check to see if it is a standard config header | ||
1105 | else if ((header_type & 0x7F) == PCI_HEADER_TYPE_NORMAL) { | ||
1106 | // Check subsystem vendor and ID | ||
1107 | pci_bus_read_config_dword (pci_bus, devfn, PCI_SUBSYSTEM_VENDOR_ID, &temp_register); | ||
1108 | |||
1109 | if (temp_register != func->config_space[0x2C >> 2]) { | ||
1110 | // If it's a SMART-2 and the register isn't filled | ||
1111 | // in, ignore the difference because | ||
1112 | // they just have an old rev of the firmware | ||
1113 | |||
1114 | if (!((func->config_space[0] == 0xAE100E11) | ||
1115 | && (temp_register == 0x00L))) | ||
1116 | return(ADAPTER_NOT_SAME); | ||
1117 | } | ||
1118 | // Figure out IO and memory base lengths | ||
1119 | for (cloop = 0x10; cloop <= 0x24; cloop += 4) { | ||
1120 | temp_register = 0xFFFFFFFF; | ||
1121 | pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register); | ||
1122 | pci_bus_read_config_dword (pci_bus, devfn, cloop, &base); | ||
1123 | if (base) { // If this register is implemented | ||
1124 | if (base & 0x01L) { | ||
1125 | // IO base | ||
1126 | // set base = amount of IO space requested | ||
1127 | base = base & 0xFFFFFFFE; | ||
1128 | base = (~base) + 1; | ||
1129 | |||
1130 | type = 1; | ||
1131 | } else { | ||
1132 | // memory base | ||
1133 | base = base & 0xFFFFFFF0; | ||
1134 | base = (~base) + 1; | ||
1135 | |||
1136 | type = 0; | ||
1137 | } | ||
1138 | } else { | ||
1139 | base = 0x0L; | ||
1140 | type = 0; | ||
1141 | } | ||
1142 | |||
1143 | // Check information in slot structure | ||
1144 | if (func->base_length[(cloop - 0x10) >> 2] != base) | ||
1145 | return(ADAPTER_NOT_SAME); | ||
1146 | |||
1147 | if (func->base_type[(cloop - 0x10) >> 2] != type) | ||
1148 | return(ADAPTER_NOT_SAME); | ||
1149 | |||
1150 | } // End of base register loop | ||
1151 | |||
1152 | } // End of (type 0 config space) else | ||
1153 | else { | ||
1154 | // this is not a type 0 or 1 config space header so | ||
1155 | // we don't know how to do it | ||
1156 | return(DEVICE_TYPE_NOT_SUPPORTED); | ||
1157 | } | ||
1158 | |||
1159 | // Get the next function | ||
1160 | func = cpqhp_slot_find(func->bus, func->device, index++); | ||
1161 | } | ||
1162 | |||
1163 | |||
1164 | return 0; | ||
1165 | } | ||
1166 | |||
1167 | |||
1168 | /* | ||
1169 | * cpqhp_find_available_resources | ||
1170 | * | ||
1171 | * Finds available memory, IO, and IRQ resources for programming | ||
1172 | * devices which may be added to the system | ||
1173 | * this function is for hot plug ADD! | ||
1174 | * | ||
1175 | * returns 0 if success | ||
1176 | */ | ||
1177 | int cpqhp_find_available_resources(struct controller *ctrl, void __iomem *rom_start) | ||
1178 | { | ||
1179 | u8 temp; | ||
1180 | u8 populated_slot; | ||
1181 | u8 bridged_slot; | ||
1182 | void __iomem *one_slot; | ||
1183 | void __iomem *rom_resource_table; | ||
1184 | struct pci_func *func = NULL; | ||
1185 | int i = 10, index; | ||
1186 | u32 temp_dword, rc; | ||
1187 | struct pci_resource *mem_node; | ||
1188 | struct pci_resource *p_mem_node; | ||
1189 | struct pci_resource *io_node; | ||
1190 | struct pci_resource *bus_node; | ||
1191 | |||
1192 | rom_resource_table = detect_HRT_floating_pointer(rom_start, rom_start+0xffff); | ||
1193 | dbg("rom_resource_table = %p\n", rom_resource_table); | ||
1194 | |||
1195 | if (rom_resource_table == NULL) { | ||
1196 | return -ENODEV; | ||
1197 | } | ||
1198 | // Sum all resources and setup resource maps | ||
1199 | unused_IRQ = readl(rom_resource_table + UNUSED_IRQ); | ||
1200 | dbg("unused_IRQ = %x\n", unused_IRQ); | ||
1201 | |||
1202 | temp = 0; | ||
1203 | while (unused_IRQ) { | ||
1204 | if (unused_IRQ & 1) { | ||
1205 | cpqhp_disk_irq = temp; | ||
1206 | break; | ||
1207 | } | ||
1208 | unused_IRQ = unused_IRQ >> 1; | ||
1209 | temp++; | ||
1210 | } | ||
1211 | |||
1212 | dbg("cpqhp_disk_irq= %d\n", cpqhp_disk_irq); | ||
1213 | unused_IRQ = unused_IRQ >> 1; | ||
1214 | temp++; | ||
1215 | |||
1216 | while (unused_IRQ) { | ||
1217 | if (unused_IRQ & 1) { | ||
1218 | cpqhp_nic_irq = temp; | ||
1219 | break; | ||
1220 | } | ||
1221 | unused_IRQ = unused_IRQ >> 1; | ||
1222 | temp++; | ||
1223 | } | ||
1224 | |||
1225 | dbg("cpqhp_nic_irq= %d\n", cpqhp_nic_irq); | ||
1226 | unused_IRQ = readl(rom_resource_table + PCIIRQ); | ||
1227 | |||
1228 | temp = 0; | ||
1229 | |||
1230 | if (!cpqhp_nic_irq) { | ||
1231 | cpqhp_nic_irq = ctrl->cfgspc_irq; | ||
1232 | } | ||
1233 | |||
1234 | if (!cpqhp_disk_irq) { | ||
1235 | cpqhp_disk_irq = ctrl->cfgspc_irq; | ||
1236 | } | ||
1237 | |||
1238 | dbg("cpqhp_disk_irq, cpqhp_nic_irq= %d, %d\n", cpqhp_disk_irq, cpqhp_nic_irq); | ||
1239 | |||
1240 | rc = compaq_nvram_load(rom_start, ctrl); | ||
1241 | if (rc) | ||
1242 | return rc; | ||
1243 | |||
1244 | one_slot = rom_resource_table + sizeof (struct hrt); | ||
1245 | |||
1246 | i = readb(rom_resource_table + NUMBER_OF_ENTRIES); | ||
1247 | dbg("number_of_entries = %d\n", i); | ||
1248 | |||
1249 | if (!readb(one_slot + SECONDARY_BUS)) | ||
1250 | return 1; | ||
1251 | |||
1252 | dbg("dev|IO base|length|Mem base|length|Pre base|length|PB SB MB\n"); | ||
1253 | |||
1254 | while (i && readb(one_slot + SECONDARY_BUS)) { | ||
1255 | u8 dev_func = readb(one_slot + DEV_FUNC); | ||
1256 | u8 primary_bus = readb(one_slot + PRIMARY_BUS); | ||
1257 | u8 secondary_bus = readb(one_slot + SECONDARY_BUS); | ||
1258 | u8 max_bus = readb(one_slot + MAX_BUS); | ||
1259 | u16 io_base = readw(one_slot + IO_BASE); | ||
1260 | u16 io_length = readw(one_slot + IO_LENGTH); | ||
1261 | u16 mem_base = readw(one_slot + MEM_BASE); | ||
1262 | u16 mem_length = readw(one_slot + MEM_LENGTH); | ||
1263 | u16 pre_mem_base = readw(one_slot + PRE_MEM_BASE); | ||
1264 | u16 pre_mem_length = readw(one_slot + PRE_MEM_LENGTH); | ||
1265 | |||
1266 | dbg("%2.2x | %4.4x | %4.4x | %4.4x | %4.4x | %4.4x | %4.4x |%2.2x %2.2x %2.2x\n", | ||
1267 | dev_func, io_base, io_length, mem_base, mem_length, pre_mem_base, pre_mem_length, | ||
1268 | primary_bus, secondary_bus, max_bus); | ||
1269 | |||
1270 | // If this entry isn't for our controller's bus, ignore it | ||
1271 | if (primary_bus != ctrl->bus) { | ||
1272 | i--; | ||
1273 | one_slot += sizeof (struct slot_rt); | ||
1274 | continue; | ||
1275 | } | ||
1276 | // find out if this entry is for an occupied slot | ||
1277 | ctrl->pci_bus->number = primary_bus; | ||
1278 | pci_bus_read_config_dword (ctrl->pci_bus, dev_func, PCI_VENDOR_ID, &temp_dword); | ||
1279 | dbg("temp_D_word = %x\n", temp_dword); | ||
1280 | |||
1281 | if (temp_dword != 0xFFFFFFFF) { | ||
1282 | index = 0; | ||
1283 | func = cpqhp_slot_find(primary_bus, dev_func >> 3, 0); | ||
1284 | |||
1285 | while (func && (func->function != (dev_func & 0x07))) { | ||
1286 | dbg("func = %p (bus, dev, fun) = (%d, %d, %d)\n", func, primary_bus, dev_func >> 3, index); | ||
1287 | func = cpqhp_slot_find(primary_bus, dev_func >> 3, index++); | ||
1288 | } | ||
1289 | |||
1290 | // If we can't find a match, skip this table entry | ||
1291 | if (!func) { | ||
1292 | i--; | ||
1293 | one_slot += sizeof (struct slot_rt); | ||
1294 | continue; | ||
1295 | } | ||
1296 | // this may not work and shouldn't be used | ||
1297 | if (secondary_bus != primary_bus) | ||
1298 | bridged_slot = 1; | ||
1299 | else | ||
1300 | bridged_slot = 0; | ||
1301 | |||
1302 | populated_slot = 1; | ||
1303 | } else { | ||
1304 | populated_slot = 0; | ||
1305 | bridged_slot = 0; | ||
1306 | } | ||
1307 | |||
1308 | |||
1309 | // If we've got a valid IO base, use it | ||
1310 | |||
1311 | temp_dword = io_base + io_length; | ||
1312 | |||
1313 | if ((io_base) && (temp_dword < 0x10000)) { | ||
1314 | io_node = kmalloc(sizeof(*io_node), GFP_KERNEL); | ||
1315 | if (!io_node) | ||
1316 | return -ENOMEM; | ||
1317 | |||
1318 | io_node->base = io_base; | ||
1319 | io_node->length = io_length; | ||
1320 | |||
1321 | dbg("found io_node(base, length) = %x, %x\n", | ||
1322 | io_node->base, io_node->length); | ||
1323 | dbg("populated slot =%d \n", populated_slot); | ||
1324 | if (!populated_slot) { | ||
1325 | io_node->next = ctrl->io_head; | ||
1326 | ctrl->io_head = io_node; | ||
1327 | } else { | ||
1328 | io_node->next = func->io_head; | ||
1329 | func->io_head = io_node; | ||
1330 | } | ||
1331 | } | ||
1332 | |||
1333 | // If we've got a valid memory base, use it | ||
1334 | temp_dword = mem_base + mem_length; | ||
1335 | if ((mem_base) && (temp_dword < 0x10000)) { | ||
1336 | mem_node = kmalloc(sizeof(*mem_node), GFP_KERNEL); | ||
1337 | if (!mem_node) | ||
1338 | return -ENOMEM; | ||
1339 | |||
1340 | mem_node->base = mem_base << 16; | ||
1341 | |||
1342 | mem_node->length = mem_length << 16; | ||
1343 | |||
1344 | dbg("found mem_node(base, length) = %x, %x\n", | ||
1345 | mem_node->base, mem_node->length); | ||
1346 | dbg("populated slot =%d \n", populated_slot); | ||
1347 | if (!populated_slot) { | ||
1348 | mem_node->next = ctrl->mem_head; | ||
1349 | ctrl->mem_head = mem_node; | ||
1350 | } else { | ||
1351 | mem_node->next = func->mem_head; | ||
1352 | func->mem_head = mem_node; | ||
1353 | } | ||
1354 | } | ||
1355 | |||
1356 | // If we've got a valid prefetchable memory base, and | ||
1357 | // the base + length isn't greater than 0xFFFF | ||
1358 | temp_dword = pre_mem_base + pre_mem_length; | ||
1359 | if ((pre_mem_base) && (temp_dword < 0x10000)) { | ||
1360 | p_mem_node = kmalloc(sizeof(*p_mem_node), GFP_KERNEL); | ||
1361 | if (!p_mem_node) | ||
1362 | return -ENOMEM; | ||
1363 | |||
1364 | p_mem_node->base = pre_mem_base << 16; | ||
1365 | |||
1366 | p_mem_node->length = pre_mem_length << 16; | ||
1367 | dbg("found p_mem_node(base, length) = %x, %x\n", | ||
1368 | p_mem_node->base, p_mem_node->length); | ||
1369 | dbg("populated slot =%d \n", populated_slot); | ||
1370 | |||
1371 | if (!populated_slot) { | ||
1372 | p_mem_node->next = ctrl->p_mem_head; | ||
1373 | ctrl->p_mem_head = p_mem_node; | ||
1374 | } else { | ||
1375 | p_mem_node->next = func->p_mem_head; | ||
1376 | func->p_mem_head = p_mem_node; | ||
1377 | } | ||
1378 | } | ||
1379 | |||
1380 | // If we've got a valid bus number, use it | ||
1381 | // The second condition is to ignore bus numbers on | ||
1382 | // populated slots that don't have PCI-PCI bridges | ||
1383 | if (secondary_bus && (secondary_bus != primary_bus)) { | ||
1384 | bus_node = kmalloc(sizeof(*bus_node), GFP_KERNEL); | ||
1385 | if (!bus_node) | ||
1386 | return -ENOMEM; | ||
1387 | |||
1388 | bus_node->base = secondary_bus; | ||
1389 | bus_node->length = max_bus - secondary_bus + 1; | ||
1390 | dbg("found bus_node(base, length) = %x, %x\n", | ||
1391 | bus_node->base, bus_node->length); | ||
1392 | dbg("populated slot =%d \n", populated_slot); | ||
1393 | if (!populated_slot) { | ||
1394 | bus_node->next = ctrl->bus_head; | ||
1395 | ctrl->bus_head = bus_node; | ||
1396 | } else { | ||
1397 | bus_node->next = func->bus_head; | ||
1398 | func->bus_head = bus_node; | ||
1399 | } | ||
1400 | } | ||
1401 | |||
1402 | i--; | ||
1403 | one_slot += sizeof (struct slot_rt); | ||
1404 | } | ||
1405 | |||
1406 | // If all of the following fail, we don't have any resources for | ||
1407 | // hot plug add | ||
1408 | rc = 1; | ||
1409 | rc &= cpqhp_resource_sort_and_combine(&(ctrl->mem_head)); | ||
1410 | rc &= cpqhp_resource_sort_and_combine(&(ctrl->p_mem_head)); | ||
1411 | rc &= cpqhp_resource_sort_and_combine(&(ctrl->io_head)); | ||
1412 | rc &= cpqhp_resource_sort_and_combine(&(ctrl->bus_head)); | ||
1413 | |||
1414 | return rc; | ||
1415 | } | ||
1416 | |||
1417 | |||
1418 | /* | ||
1419 | * cpqhp_return_board_resources | ||
1420 | * | ||
1421 | * this routine returns all resources allocated to a board to | ||
1422 | * the available pool. | ||
1423 | * | ||
1424 | * returns 0 if success | ||
1425 | */ | ||
1426 | int cpqhp_return_board_resources(struct pci_func * func, struct resource_lists * resources) | ||
1427 | { | ||
1428 | int rc = 0; | ||
1429 | struct pci_resource *node; | ||
1430 | struct pci_resource *t_node; | ||
1431 | dbg("%s\n", __FUNCTION__); | ||
1432 | |||
1433 | if (!func) | ||
1434 | return 1; | ||
1435 | |||
1436 | node = func->io_head; | ||
1437 | func->io_head = NULL; | ||
1438 | while (node) { | ||
1439 | t_node = node->next; | ||
1440 | return_resource(&(resources->io_head), node); | ||
1441 | node = t_node; | ||
1442 | } | ||
1443 | |||
1444 | node = func->mem_head; | ||
1445 | func->mem_head = NULL; | ||
1446 | while (node) { | ||
1447 | t_node = node->next; | ||
1448 | return_resource(&(resources->mem_head), node); | ||
1449 | node = t_node; | ||
1450 | } | ||
1451 | |||
1452 | node = func->p_mem_head; | ||
1453 | func->p_mem_head = NULL; | ||
1454 | while (node) { | ||
1455 | t_node = node->next; | ||
1456 | return_resource(&(resources->p_mem_head), node); | ||
1457 | node = t_node; | ||
1458 | } | ||
1459 | |||
1460 | node = func->bus_head; | ||
1461 | func->bus_head = NULL; | ||
1462 | while (node) { | ||
1463 | t_node = node->next; | ||
1464 | return_resource(&(resources->bus_head), node); | ||
1465 | node = t_node; | ||
1466 | } | ||
1467 | |||
1468 | rc |= cpqhp_resource_sort_and_combine(&(resources->mem_head)); | ||
1469 | rc |= cpqhp_resource_sort_and_combine(&(resources->p_mem_head)); | ||
1470 | rc |= cpqhp_resource_sort_and_combine(&(resources->io_head)); | ||
1471 | rc |= cpqhp_resource_sort_and_combine(&(resources->bus_head)); | ||
1472 | |||
1473 | return rc; | ||
1474 | } | ||
1475 | |||
1476 | |||
1477 | /* | ||
1478 | * cpqhp_destroy_resource_list | ||
1479 | * | ||
1480 | * Puts node back in the resource list pointed to by head | ||
1481 | */ | ||
1482 | void cpqhp_destroy_resource_list (struct resource_lists * resources) | ||
1483 | { | ||
1484 | struct pci_resource *res, *tres; | ||
1485 | |||
1486 | res = resources->io_head; | ||
1487 | resources->io_head = NULL; | ||
1488 | |||
1489 | while (res) { | ||
1490 | tres = res; | ||
1491 | res = res->next; | ||
1492 | kfree(tres); | ||
1493 | } | ||
1494 | |||
1495 | res = resources->mem_head; | ||
1496 | resources->mem_head = NULL; | ||
1497 | |||
1498 | while (res) { | ||
1499 | tres = res; | ||
1500 | res = res->next; | ||
1501 | kfree(tres); | ||
1502 | } | ||
1503 | |||
1504 | res = resources->p_mem_head; | ||
1505 | resources->p_mem_head = NULL; | ||
1506 | |||
1507 | while (res) { | ||
1508 | tres = res; | ||
1509 | res = res->next; | ||
1510 | kfree(tres); | ||
1511 | } | ||
1512 | |||
1513 | res = resources->bus_head; | ||
1514 | resources->bus_head = NULL; | ||
1515 | |||
1516 | while (res) { | ||
1517 | tres = res; | ||
1518 | res = res->next; | ||
1519 | kfree(tres); | ||
1520 | } | ||
1521 | } | ||
1522 | |||
1523 | |||
1524 | /* | ||
1525 | * cpqhp_destroy_board_resources | ||
1526 | * | ||
1527 | * Puts node back in the resource list pointed to by head | ||
1528 | */ | ||
1529 | void cpqhp_destroy_board_resources (struct pci_func * func) | ||
1530 | { | ||
1531 | struct pci_resource *res, *tres; | ||
1532 | |||
1533 | res = func->io_head; | ||
1534 | func->io_head = NULL; | ||
1535 | |||
1536 | while (res) { | ||
1537 | tres = res; | ||
1538 | res = res->next; | ||
1539 | kfree(tres); | ||
1540 | } | ||
1541 | |||
1542 | res = func->mem_head; | ||
1543 | func->mem_head = NULL; | ||
1544 | |||
1545 | while (res) { | ||
1546 | tres = res; | ||
1547 | res = res->next; | ||
1548 | kfree(tres); | ||
1549 | } | ||
1550 | |||
1551 | res = func->p_mem_head; | ||
1552 | func->p_mem_head = NULL; | ||
1553 | |||
1554 | while (res) { | ||
1555 | tres = res; | ||
1556 | res = res->next; | ||
1557 | kfree(tres); | ||
1558 | } | ||
1559 | |||
1560 | res = func->bus_head; | ||
1561 | func->bus_head = NULL; | ||
1562 | |||
1563 | while (res) { | ||
1564 | tres = res; | ||
1565 | res = res->next; | ||
1566 | kfree(tres); | ||
1567 | } | ||
1568 | } | ||
1569 | |||
diff --git a/drivers/pci/hotplug/cpqphp_sysfs.c b/drivers/pci/hotplug/cpqphp_sysfs.c new file mode 100644 index 000000000000..41c7971d06c5 --- /dev/null +++ b/drivers/pci/hotplug/cpqphp_sysfs.c | |||
@@ -0,0 +1,143 @@ | |||
1 | /* | ||
2 | * Compaq Hot Plug Controller Driver | ||
3 | * | ||
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | ||
5 | * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * Copyright (C) 2001 IBM Corp. | ||
7 | * | ||
8 | * All rights reserved. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or (at | ||
13 | * your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
18 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
19 | * details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
24 | * | ||
25 | * Send feedback to <greg@kroah.com> | ||
26 | * | ||
27 | */ | ||
28 | |||
29 | #include <linux/config.h> | ||
30 | #include <linux/module.h> | ||
31 | #include <linux/kernel.h> | ||
32 | #include <linux/types.h> | ||
33 | #include <linux/proc_fs.h> | ||
34 | #include <linux/workqueue.h> | ||
35 | #include <linux/pci.h> | ||
36 | #include "cpqphp.h" | ||
37 | |||
38 | |||
39 | /* A few routines that create sysfs entries for the hot plug controller */ | ||
40 | |||
41 | static ssize_t show_ctrl (struct device *dev, char *buf) | ||
42 | { | ||
43 | struct pci_dev *pci_dev; | ||
44 | struct controller *ctrl; | ||
45 | char * out = buf; | ||
46 | int index; | ||
47 | struct pci_resource *res; | ||
48 | |||
49 | pci_dev = container_of (dev, struct pci_dev, dev); | ||
50 | ctrl = pci_get_drvdata(pci_dev); | ||
51 | |||
52 | out += sprintf(buf, "Free resources: memory\n"); | ||
53 | index = 11; | ||
54 | res = ctrl->mem_head; | ||
55 | while (res && index--) { | ||
56 | out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); | ||
57 | res = res->next; | ||
58 | } | ||
59 | out += sprintf(out, "Free resources: prefetchable memory\n"); | ||
60 | index = 11; | ||
61 | res = ctrl->p_mem_head; | ||
62 | while (res && index--) { | ||
63 | out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); | ||
64 | res = res->next; | ||
65 | } | ||
66 | out += sprintf(out, "Free resources: IO\n"); | ||
67 | index = 11; | ||
68 | res = ctrl->io_head; | ||
69 | while (res && index--) { | ||
70 | out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); | ||
71 | res = res->next; | ||
72 | } | ||
73 | out += sprintf(out, "Free resources: bus numbers\n"); | ||
74 | index = 11; | ||
75 | res = ctrl->bus_head; | ||
76 | while (res && index--) { | ||
77 | out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); | ||
78 | res = res->next; | ||
79 | } | ||
80 | |||
81 | return out - buf; | ||
82 | } | ||
83 | static DEVICE_ATTR (ctrl, S_IRUGO, show_ctrl, NULL); | ||
84 | |||
85 | static ssize_t show_dev (struct device *dev, char *buf) | ||
86 | { | ||
87 | struct pci_dev *pci_dev; | ||
88 | struct controller *ctrl; | ||
89 | char * out = buf; | ||
90 | int index; | ||
91 | struct pci_resource *res; | ||
92 | struct pci_func *new_slot; | ||
93 | struct slot *slot; | ||
94 | |||
95 | pci_dev = container_of (dev, struct pci_dev, dev); | ||
96 | ctrl = pci_get_drvdata(pci_dev); | ||
97 | |||
98 | slot=ctrl->slot; | ||
99 | |||
100 | while (slot) { | ||
101 | new_slot = cpqhp_slot_find(slot->bus, slot->device, 0); | ||
102 | if (!new_slot) | ||
103 | break; | ||
104 | out += sprintf(out, "assigned resources: memory\n"); | ||
105 | index = 11; | ||
106 | res = new_slot->mem_head; | ||
107 | while (res && index--) { | ||
108 | out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); | ||
109 | res = res->next; | ||
110 | } | ||
111 | out += sprintf(out, "assigned resources: prefetchable memory\n"); | ||
112 | index = 11; | ||
113 | res = new_slot->p_mem_head; | ||
114 | while (res && index--) { | ||
115 | out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); | ||
116 | res = res->next; | ||
117 | } | ||
118 | out += sprintf(out, "assigned resources: IO\n"); | ||
119 | index = 11; | ||
120 | res = new_slot->io_head; | ||
121 | while (res && index--) { | ||
122 | out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); | ||
123 | res = res->next; | ||
124 | } | ||
125 | out += sprintf(out, "assigned resources: bus numbers\n"); | ||
126 | index = 11; | ||
127 | res = new_slot->bus_head; | ||
128 | while (res && index--) { | ||
129 | out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); | ||
130 | res = res->next; | ||
131 | } | ||
132 | slot=slot->next; | ||
133 | } | ||
134 | |||
135 | return out - buf; | ||
136 | } | ||
137 | static DEVICE_ATTR (dev, S_IRUGO, show_dev, NULL); | ||
138 | |||
139 | void cpqhp_create_ctrl_files (struct controller *ctrl) | ||
140 | { | ||
141 | device_create_file (&ctrl->pci_dev->dev, &dev_attr_ctrl); | ||
142 | device_create_file (&ctrl->pci_dev->dev, &dev_attr_dev); | ||
143 | } | ||
diff --git a/drivers/pci/hotplug/fakephp.c b/drivers/pci/hotplug/fakephp.c new file mode 100644 index 000000000000..8e47fa66e25e --- /dev/null +++ b/drivers/pci/hotplug/fakephp.c | |||
@@ -0,0 +1,358 @@ | |||
1 | /* | ||
2 | * Fake PCI Hot Plug Controller Driver | ||
3 | * | ||
4 | * Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com> | ||
5 | * Copyright (C) 2003 IBM Corp. | ||
6 | * Copyright (C) 2003 Rolf Eike Beer <eike-kernel@sf-tec.de> | ||
7 | * | ||
8 | * Based on ideas and code from: | ||
9 | * Vladimir Kondratiev <vladimir.kondratiev@intel.com> | ||
10 | * Rolf Eike Beer <eike-kernel@sf-tec.de> | ||
11 | * | ||
12 | * All rights reserved. | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify | ||
15 | * it under the terms of the GNU General Public License as published by | ||
16 | * the Free Software Foundation, version 2 of the License. | ||
17 | * | ||
18 | * Send feedback to <greg@kroah.com> | ||
19 | */ | ||
20 | |||
21 | /* | ||
22 | * | ||
23 | * This driver will "emulate" removing PCI devices from the system. If | ||
24 | * the "power" file is written to with "0" then the specified PCI device | ||
25 | * will be completely removed from the kernel. | ||
26 | * | ||
27 | * WARNING, this does NOT turn off the power to the PCI device. This is | ||
28 | * a "logical" removal, not a physical or electrical removal. | ||
29 | * | ||
30 | * Use this module at your own risk, you have been warned! | ||
31 | * | ||
32 | * Enabling PCI devices is left as an exercise for the reader... | ||
33 | * | ||
34 | */ | ||
35 | #include <linux/config.h> | ||
36 | #include <linux/kernel.h> | ||
37 | #include <linux/module.h> | ||
38 | #include <linux/pci.h> | ||
39 | #include <linux/init.h> | ||
40 | #include "pci_hotplug.h" | ||
41 | #include "../pci.h" | ||
42 | |||
43 | #if !defined(MODULE) | ||
44 | #define MY_NAME "fakephp" | ||
45 | #else | ||
46 | #define MY_NAME THIS_MODULE->name | ||
47 | #endif | ||
48 | |||
49 | #define dbg(format, arg...) \ | ||
50 | do { \ | ||
51 | if (debug) \ | ||
52 | printk(KERN_DEBUG "%s: " format, \ | ||
53 | MY_NAME , ## arg); \ | ||
54 | } while (0) | ||
55 | #define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg) | ||
56 | #define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg) | ||
57 | |||
58 | #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>" | ||
59 | #define DRIVER_DESC "Fake PCI Hot Plug Controller Driver" | ||
60 | |||
61 | struct dummy_slot { | ||
62 | struct list_head node; | ||
63 | struct hotplug_slot *slot; | ||
64 | struct pci_dev *dev; | ||
65 | }; | ||
66 | |||
67 | static int debug; | ||
68 | static LIST_HEAD(slot_list); | ||
69 | |||
70 | static int enable_slot (struct hotplug_slot *slot); | ||
71 | static int disable_slot (struct hotplug_slot *slot); | ||
72 | |||
73 | static struct hotplug_slot_ops dummy_hotplug_slot_ops = { | ||
74 | .owner = THIS_MODULE, | ||
75 | .enable_slot = enable_slot, | ||
76 | .disable_slot = disable_slot, | ||
77 | }; | ||
78 | |||
79 | static void dummy_release(struct hotplug_slot *slot) | ||
80 | { | ||
81 | struct dummy_slot *dslot = slot->private; | ||
82 | |||
83 | list_del(&dslot->node); | ||
84 | kfree(dslot->slot->info); | ||
85 | kfree(dslot->slot); | ||
86 | pci_dev_put(dslot->dev); | ||
87 | kfree(dslot); | ||
88 | } | ||
89 | |||
90 | static int add_slot(struct pci_dev *dev) | ||
91 | { | ||
92 | struct dummy_slot *dslot; | ||
93 | struct hotplug_slot *slot; | ||
94 | int retval = -ENOMEM; | ||
95 | |||
96 | slot = kmalloc(sizeof(struct hotplug_slot), GFP_KERNEL); | ||
97 | if (!slot) | ||
98 | goto error; | ||
99 | memset(slot, 0, sizeof(*slot)); | ||
100 | |||
101 | slot->info = kmalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL); | ||
102 | if (!slot->info) | ||
103 | goto error_slot; | ||
104 | memset(slot->info, 0, sizeof(struct hotplug_slot_info)); | ||
105 | |||
106 | slot->info->power_status = 1; | ||
107 | slot->info->max_bus_speed = PCI_SPEED_UNKNOWN; | ||
108 | slot->info->cur_bus_speed = PCI_SPEED_UNKNOWN; | ||
109 | |||
110 | slot->name = &dev->dev.bus_id[0]; | ||
111 | dbg("slot->name = %s\n", slot->name); | ||
112 | |||
113 | dslot = kmalloc(sizeof(struct dummy_slot), GFP_KERNEL); | ||
114 | if (!dslot) | ||
115 | goto error_info; | ||
116 | |||
117 | slot->ops = &dummy_hotplug_slot_ops; | ||
118 | slot->release = &dummy_release; | ||
119 | slot->private = dslot; | ||
120 | |||
121 | retval = pci_hp_register(slot); | ||
122 | if (retval) { | ||
123 | err("pci_hp_register failed with error %d\n", retval); | ||
124 | goto error_dslot; | ||
125 | } | ||
126 | |||
127 | dslot->slot = slot; | ||
128 | dslot->dev = pci_dev_get(dev); | ||
129 | list_add (&dslot->node, &slot_list); | ||
130 | return retval; | ||
131 | |||
132 | error_dslot: | ||
133 | kfree(dslot); | ||
134 | error_info: | ||
135 | kfree(slot->info); | ||
136 | error_slot: | ||
137 | kfree(slot); | ||
138 | error: | ||
139 | return retval; | ||
140 | } | ||
141 | |||
142 | static int __init pci_scan_buses(void) | ||
143 | { | ||
144 | struct pci_dev *dev = NULL; | ||
145 | int retval = 0; | ||
146 | |||
147 | while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { | ||
148 | retval = add_slot(dev); | ||
149 | if (retval) { | ||
150 | pci_dev_put(dev); | ||
151 | break; | ||
152 | } | ||
153 | } | ||
154 | |||
155 | return retval; | ||
156 | } | ||
157 | |||
158 | static void remove_slot(struct dummy_slot *dslot) | ||
159 | { | ||
160 | int retval; | ||
161 | |||
162 | dbg("removing slot %s\n", dslot->slot->name); | ||
163 | retval = pci_hp_deregister(dslot->slot); | ||
164 | if (retval) | ||
165 | err("Problem unregistering a slot %s\n", dslot->slot->name); | ||
166 | } | ||
167 | |||
168 | /** | ||
169 | * Rescan slot. | ||
170 | * Tries hard not to re-enable already existing devices | ||
171 | * also handles scanning of subfunctions | ||
172 | * | ||
173 | * @param temp Device template. Should be set: bus and devfn. | ||
174 | */ | ||
175 | static void pci_rescan_slot(struct pci_dev *temp) | ||
176 | { | ||
177 | struct pci_bus *bus = temp->bus; | ||
178 | struct pci_dev *dev; | ||
179 | int func; | ||
180 | u8 hdr_type; | ||
181 | if (!pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type)) { | ||
182 | temp->hdr_type = hdr_type & 0x7f; | ||
183 | if (!pci_find_slot(bus->number, temp->devfn)) { | ||
184 | dev = pci_scan_single_device(bus, temp->devfn); | ||
185 | if (dev) { | ||
186 | dbg("New device on %s function %x:%x\n", | ||
187 | bus->name, temp->devfn >> 3, | ||
188 | temp->devfn & 7); | ||
189 | pci_bus_add_device(dev); | ||
190 | add_slot(dev); | ||
191 | } | ||
192 | } | ||
193 | /* multifunction device? */ | ||
194 | if (!(hdr_type & 0x80)) | ||
195 | return; | ||
196 | |||
197 | /* continue scanning for other functions */ | ||
198 | for (func = 1, temp->devfn++; func < 8; func++, temp->devfn++) { | ||
199 | if (pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type)) | ||
200 | continue; | ||
201 | temp->hdr_type = hdr_type & 0x7f; | ||
202 | |||
203 | if (!pci_find_slot(bus->number, temp->devfn)) { | ||
204 | dev = pci_scan_single_device(bus, temp->devfn); | ||
205 | if (dev) { | ||
206 | dbg("New device on %s function %x:%x\n", | ||
207 | bus->name, temp->devfn >> 3, | ||
208 | temp->devfn & 7); | ||
209 | pci_bus_add_device(dev); | ||
210 | add_slot(dev); | ||
211 | } | ||
212 | } | ||
213 | } | ||
214 | } | ||
215 | } | ||
216 | |||
217 | |||
218 | /** | ||
219 | * Rescan PCI bus. | ||
220 | * call pci_rescan_slot for each possible function of the bus | ||
221 | * | ||
222 | * @param bus | ||
223 | */ | ||
224 | static void pci_rescan_bus(const struct pci_bus *bus) | ||
225 | { | ||
226 | unsigned int devfn; | ||
227 | struct pci_dev *dev; | ||
228 | dev = kmalloc(sizeof(struct pci_dev), GFP_KERNEL); | ||
229 | if (!dev) | ||
230 | return; | ||
231 | |||
232 | memset(dev, 0, sizeof(dev)); | ||
233 | dev->bus = (struct pci_bus*)bus; | ||
234 | dev->sysdata = bus->sysdata; | ||
235 | for (devfn = 0; devfn < 0x100; devfn += 8) { | ||
236 | dev->devfn = devfn; | ||
237 | pci_rescan_slot(dev); | ||
238 | } | ||
239 | kfree(dev); | ||
240 | } | ||
241 | |||
242 | /* recursively scan all buses */ | ||
243 | static void pci_rescan_buses(const struct list_head *list) | ||
244 | { | ||
245 | const struct list_head *l; | ||
246 | list_for_each(l,list) { | ||
247 | const struct pci_bus *b = pci_bus_b(l); | ||
248 | pci_rescan_bus(b); | ||
249 | pci_rescan_buses(&b->children); | ||
250 | } | ||
251 | } | ||
252 | |||
253 | /* initiate rescan of all pci buses */ | ||
254 | static inline void pci_rescan(void) { | ||
255 | pci_rescan_buses(&pci_root_buses); | ||
256 | } | ||
257 | |||
258 | |||
259 | static int enable_slot(struct hotplug_slot *hotplug_slot) | ||
260 | { | ||
261 | /* mis-use enable_slot for rescanning of the pci bus */ | ||
262 | pci_rescan(); | ||
263 | return -ENODEV; | ||
264 | } | ||
265 | |||
266 | /* find the hotplug_slot for the pci_dev */ | ||
267 | static struct hotplug_slot *get_slot_from_dev(struct pci_dev *dev) | ||
268 | { | ||
269 | struct dummy_slot *dslot; | ||
270 | |||
271 | list_for_each_entry(dslot, &slot_list, node) { | ||
272 | if (dslot->dev == dev) | ||
273 | return dslot->slot; | ||
274 | } | ||
275 | return NULL; | ||
276 | } | ||
277 | |||
278 | |||
279 | static int disable_slot(struct hotplug_slot *slot) | ||
280 | { | ||
281 | struct dummy_slot *dslot; | ||
282 | struct hotplug_slot *hslot; | ||
283 | struct pci_dev *dev; | ||
284 | int func; | ||
285 | |||
286 | if (!slot) | ||
287 | return -ENODEV; | ||
288 | dslot = slot->private; | ||
289 | |||
290 | dbg("%s - physical_slot = %s\n", __FUNCTION__, slot->name); | ||
291 | |||
292 | /* don't disable bridged devices just yet, we can't handle them easily... */ | ||
293 | if (dslot->dev->subordinate) { | ||
294 | err("Can't remove PCI devices with other PCI devices behind it yet.\n"); | ||
295 | return -ENODEV; | ||
296 | } | ||
297 | /* search for subfunctions and disable them first */ | ||
298 | if (!(dslot->dev->devfn & 7)) { | ||
299 | for (func = 1; func < 8; func++) { | ||
300 | dev = pci_find_slot(dslot->dev->bus->number, | ||
301 | dslot->dev->devfn + func); | ||
302 | if (dev) { | ||
303 | hslot = get_slot_from_dev(dev); | ||
304 | if (hslot) | ||
305 | disable_slot(hslot); | ||
306 | else { | ||
307 | err("Hotplug slot not found for subfunction of PCI device\n"); | ||
308 | return -ENODEV; | ||
309 | } | ||
310 | } else | ||
311 | dbg("No device in slot found\n"); | ||
312 | } | ||
313 | } | ||
314 | |||
315 | /* remove the device from the pci core */ | ||
316 | pci_remove_bus_device(dslot->dev); | ||
317 | |||
318 | /* blow away this sysfs entry and other parts. */ | ||
319 | remove_slot(dslot); | ||
320 | |||
321 | return 0; | ||
322 | } | ||
323 | |||
324 | static void cleanup_slots (void) | ||
325 | { | ||
326 | struct list_head *tmp; | ||
327 | struct list_head *next; | ||
328 | struct dummy_slot *dslot; | ||
329 | |||
330 | list_for_each_safe (tmp, next, &slot_list) { | ||
331 | dslot = list_entry (tmp, struct dummy_slot, node); | ||
332 | remove_slot(dslot); | ||
333 | } | ||
334 | |||
335 | } | ||
336 | |||
337 | static int __init dummyphp_init(void) | ||
338 | { | ||
339 | info(DRIVER_DESC "\n"); | ||
340 | |||
341 | return pci_scan_buses(); | ||
342 | } | ||
343 | |||
344 | |||
345 | static void __exit dummyphp_exit(void) | ||
346 | { | ||
347 | cleanup_slots(); | ||
348 | } | ||
349 | |||
350 | module_init(dummyphp_init); | ||
351 | module_exit(dummyphp_exit); | ||
352 | |||
353 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
354 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
355 | MODULE_LICENSE("GPL"); | ||
356 | module_param(debug, bool, S_IRUGO | S_IWUSR); | ||
357 | MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); | ||
358 | |||
diff --git a/drivers/pci/hotplug/ibmphp.h b/drivers/pci/hotplug/ibmphp.h new file mode 100644 index 000000000000..5bc039da647f --- /dev/null +++ b/drivers/pci/hotplug/ibmphp.h | |||
@@ -0,0 +1,763 @@ | |||
1 | #ifndef __IBMPHP_H | ||
2 | #define __IBMPHP_H | ||
3 | |||
4 | /* | ||
5 | * IBM Hot Plug Controller Driver | ||
6 | * | ||
7 | * Written By: Jyoti Shah, Tong Yu, Irene Zubarev, IBM Corporation | ||
8 | * | ||
9 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
10 | * Copyright (C) 2001-2003 IBM Corp. | ||
11 | * | ||
12 | * All rights reserved. | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify | ||
15 | * it under the terms of the GNU General Public License as published by | ||
16 | * the Free Software Foundation; either version 2 of the License, or (at | ||
17 | * your option) any later version. | ||
18 | * | ||
19 | * This program is distributed in the hope that it will be useful, but | ||
20 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
21 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
22 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
23 | * details. | ||
24 | * | ||
25 | * You should have received a copy of the GNU General Public License | ||
26 | * along with this program; if not, write to the Free Software | ||
27 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
28 | * | ||
29 | * Send feedback to <gregkh@us.ibm.com> | ||
30 | * | ||
31 | */ | ||
32 | |||
33 | #include "pci_hotplug.h" | ||
34 | |||
35 | extern int ibmphp_debug; | ||
36 | |||
37 | #if !defined(MODULE) | ||
38 | #define MY_NAME "ibmphpd" | ||
39 | #else | ||
40 | #define MY_NAME THIS_MODULE->name | ||
41 | #endif | ||
42 | #define debug(fmt, arg...) do { if (ibmphp_debug == 1) printk(KERN_DEBUG "%s: " fmt , MY_NAME , ## arg); } while (0) | ||
43 | #define debug_pci(fmt, arg...) do { if (ibmphp_debug) printk(KERN_DEBUG "%s: " fmt , MY_NAME , ## arg); } while (0) | ||
44 | #define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg) | ||
45 | #define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg) | ||
46 | #define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg) | ||
47 | |||
48 | |||
49 | /* EBDA stuff */ | ||
50 | |||
51 | /*********************************************************** | ||
52 | * SLOT CAPABILITY * | ||
53 | ***********************************************************/ | ||
54 | |||
55 | #define EBDA_SLOT_133_MAX 0x20 | ||
56 | #define EBDA_SLOT_100_MAX 0x10 | ||
57 | #define EBDA_SLOT_66_MAX 0x02 | ||
58 | #define EBDA_SLOT_PCIX_CAP 0x08 | ||
59 | |||
60 | |||
61 | /************************************************************ | ||
62 | * RESOURE TYPE * | ||
63 | ************************************************************/ | ||
64 | |||
65 | #define EBDA_RSRC_TYPE_MASK 0x03 | ||
66 | #define EBDA_IO_RSRC_TYPE 0x00 | ||
67 | #define EBDA_MEM_RSRC_TYPE 0x01 | ||
68 | #define EBDA_PFM_RSRC_TYPE 0x03 | ||
69 | #define EBDA_RES_RSRC_TYPE 0x02 | ||
70 | |||
71 | |||
72 | /************************************************************* | ||
73 | * IO RESTRICTION TYPE * | ||
74 | *************************************************************/ | ||
75 | |||
76 | #define EBDA_IO_RESTRI_MASK 0x0c | ||
77 | #define EBDA_NO_RESTRI 0x00 | ||
78 | #define EBDA_AVO_VGA_ADDR 0x04 | ||
79 | #define EBDA_AVO_VGA_ADDR_AND_ALIA 0x08 | ||
80 | #define EBDA_AVO_ISA_ADDR 0x0c | ||
81 | |||
82 | |||
83 | /************************************************************** | ||
84 | * DEVICE TYPE DEF * | ||
85 | **************************************************************/ | ||
86 | |||
87 | #define EBDA_DEV_TYPE_MASK 0x10 | ||
88 | #define EBDA_PCI_DEV 0x10 | ||
89 | #define EBDA_NON_PCI_DEV 0x00 | ||
90 | |||
91 | |||
92 | /*************************************************************** | ||
93 | * PRIMARY DEF DEFINITION * | ||
94 | ***************************************************************/ | ||
95 | |||
96 | #define EBDA_PRI_DEF_MASK 0x20 | ||
97 | #define EBDA_PRI_PCI_BUS_INFO 0x20 | ||
98 | #define EBDA_NORM_DEV_RSRC_INFO 0x00 | ||
99 | |||
100 | |||
101 | //-------------------------------------------------------------- | ||
102 | // RIO TABLE DATA STRUCTURE | ||
103 | //-------------------------------------------------------------- | ||
104 | |||
105 | struct rio_table_hdr { | ||
106 | u8 ver_num; | ||
107 | u8 scal_count; | ||
108 | u8 riodev_count; | ||
109 | u16 offset; | ||
110 | }; | ||
111 | |||
112 | //------------------------------------------------------------- | ||
113 | // SCALABILITY DETAIL | ||
114 | //------------------------------------------------------------- | ||
115 | |||
116 | struct scal_detail { | ||
117 | u8 node_id; | ||
118 | u32 cbar; | ||
119 | u8 port0_node_connect; | ||
120 | u8 port0_port_connect; | ||
121 | u8 port1_node_connect; | ||
122 | u8 port1_port_connect; | ||
123 | u8 port2_node_connect; | ||
124 | u8 port2_port_connect; | ||
125 | u8 chassis_num; | ||
126 | // struct list_head scal_detail_list; | ||
127 | }; | ||
128 | |||
129 | //-------------------------------------------------------------- | ||
130 | // RIO DETAIL | ||
131 | //-------------------------------------------------------------- | ||
132 | |||
133 | struct rio_detail { | ||
134 | u8 rio_node_id; | ||
135 | u32 bbar; | ||
136 | u8 rio_type; | ||
137 | u8 owner_id; | ||
138 | u8 port0_node_connect; | ||
139 | u8 port0_port_connect; | ||
140 | u8 port1_node_connect; | ||
141 | u8 port1_port_connect; | ||
142 | u8 first_slot_num; | ||
143 | u8 status; | ||
144 | u8 wpindex; | ||
145 | u8 chassis_num; | ||
146 | struct list_head rio_detail_list; | ||
147 | }; | ||
148 | |||
149 | struct opt_rio { | ||
150 | u8 rio_type; | ||
151 | u8 chassis_num; | ||
152 | u8 first_slot_num; | ||
153 | u8 middle_num; | ||
154 | struct list_head opt_rio_list; | ||
155 | }; | ||
156 | |||
157 | struct opt_rio_lo { | ||
158 | u8 rio_type; | ||
159 | u8 chassis_num; | ||
160 | u8 first_slot_num; | ||
161 | u8 middle_num; | ||
162 | u8 pack_count; | ||
163 | struct list_head opt_rio_lo_list; | ||
164 | }; | ||
165 | |||
166 | /**************************************************************** | ||
167 | * HPC DESCRIPTOR NODE * | ||
168 | ****************************************************************/ | ||
169 | |||
170 | struct ebda_hpc_list { | ||
171 | u8 format; | ||
172 | u16 num_ctlrs; | ||
173 | short phys_addr; | ||
174 | // struct list_head ebda_hpc_list; | ||
175 | }; | ||
176 | /***************************************************************** | ||
177 | * IN HPC DATA STRUCTURE, THE ASSOCIATED SLOT AND BUS * | ||
178 | * STRUCTURE * | ||
179 | *****************************************************************/ | ||
180 | |||
181 | struct ebda_hpc_slot { | ||
182 | u8 slot_num; | ||
183 | u32 slot_bus_num; | ||
184 | u8 ctl_index; | ||
185 | u8 slot_cap; | ||
186 | }; | ||
187 | |||
188 | struct ebda_hpc_bus { | ||
189 | u32 bus_num; | ||
190 | u8 slots_at_33_conv; | ||
191 | u8 slots_at_66_conv; | ||
192 | u8 slots_at_66_pcix; | ||
193 | u8 slots_at_100_pcix; | ||
194 | u8 slots_at_133_pcix; | ||
195 | }; | ||
196 | |||
197 | |||
198 | /******************************************************************** | ||
199 | * THREE TYPE OF HOT PLUG CONTROLER * | ||
200 | ********************************************************************/ | ||
201 | |||
202 | struct isa_ctlr_access { | ||
203 | u16 io_start; | ||
204 | u16 io_end; | ||
205 | }; | ||
206 | |||
207 | struct pci_ctlr_access { | ||
208 | u8 bus; | ||
209 | u8 dev_fun; | ||
210 | }; | ||
211 | |||
212 | struct wpeg_i2c_ctlr_access { | ||
213 | ulong wpegbbar; | ||
214 | u8 i2c_addr; | ||
215 | }; | ||
216 | |||
217 | #define HPC_DEVICE_ID 0x0246 | ||
218 | #define HPC_SUBSYSTEM_ID 0x0247 | ||
219 | #define HPC_PCI_OFFSET 0x40 | ||
220 | /************************************************************************* | ||
221 | * RSTC DESCRIPTOR NODE * | ||
222 | *************************************************************************/ | ||
223 | |||
224 | struct ebda_rsrc_list { | ||
225 | u8 format; | ||
226 | u16 num_entries; | ||
227 | u16 phys_addr; | ||
228 | struct ebda_rsrc_list *next; | ||
229 | }; | ||
230 | |||
231 | |||
232 | /*************************************************************************** | ||
233 | * PCI RSRC NODE * | ||
234 | ***************************************************************************/ | ||
235 | |||
236 | struct ebda_pci_rsrc { | ||
237 | u8 rsrc_type; | ||
238 | u8 bus_num; | ||
239 | u8 dev_fun; | ||
240 | u32 start_addr; | ||
241 | u32 end_addr; | ||
242 | u8 marked; /* for NVRAM */ | ||
243 | struct list_head ebda_pci_rsrc_list; | ||
244 | }; | ||
245 | |||
246 | |||
247 | /*********************************************************** | ||
248 | * BUS_INFO DATE STRUCTURE * | ||
249 | ***********************************************************/ | ||
250 | |||
251 | struct bus_info { | ||
252 | u8 slot_min; | ||
253 | u8 slot_max; | ||
254 | u8 slot_count; | ||
255 | u8 busno; | ||
256 | u8 controller_id; | ||
257 | u8 current_speed; | ||
258 | u8 current_bus_mode; | ||
259 | u8 index; | ||
260 | u8 slots_at_33_conv; | ||
261 | u8 slots_at_66_conv; | ||
262 | u8 slots_at_66_pcix; | ||
263 | u8 slots_at_100_pcix; | ||
264 | u8 slots_at_133_pcix; | ||
265 | struct list_head bus_info_list; | ||
266 | }; | ||
267 | |||
268 | |||
269 | /*********************************************************** | ||
270 | * GLOBAL VARIABLES * | ||
271 | ***********************************************************/ | ||
272 | extern struct list_head ibmphp_ebda_pci_rsrc_head; | ||
273 | extern struct list_head ibmphp_slot_head; | ||
274 | /*********************************************************** | ||
275 | * FUNCTION PROTOTYPES * | ||
276 | ***********************************************************/ | ||
277 | |||
278 | extern void ibmphp_free_ebda_hpc_queue (void); | ||
279 | extern int ibmphp_access_ebda (void); | ||
280 | extern struct slot *ibmphp_get_slot_from_physical_num (u8); | ||
281 | extern int ibmphp_get_total_hp_slots (void); | ||
282 | extern void ibmphp_free_ibm_slot (struct slot *); | ||
283 | extern void ibmphp_free_bus_info_queue (void); | ||
284 | extern void ibmphp_free_ebda_pci_rsrc_queue (void); | ||
285 | extern struct bus_info *ibmphp_find_same_bus_num (u32); | ||
286 | extern int ibmphp_get_bus_index (u8); | ||
287 | extern u16 ibmphp_get_total_controllers (void); | ||
288 | extern int ibmphp_register_pci (void); | ||
289 | |||
290 | /* passed parameters */ | ||
291 | #define MEM 0 | ||
292 | #define IO 1 | ||
293 | #define PFMEM 2 | ||
294 | |||
295 | /* bit masks */ | ||
296 | #define RESTYPE 0x03 | ||
297 | #define IOMASK 0x00 /* will need to take its complement */ | ||
298 | #define MMASK 0x01 | ||
299 | #define PFMASK 0x03 | ||
300 | #define PCIDEVMASK 0x10 /* we should always have PCI devices */ | ||
301 | #define PRIMARYBUSMASK 0x20 | ||
302 | |||
303 | /* pci specific defines */ | ||
304 | #define PCI_VENDOR_ID_NOTVALID 0xFFFF | ||
305 | #define PCI_HEADER_TYPE_MULTIDEVICE 0x80 | ||
306 | #define PCI_HEADER_TYPE_MULTIBRIDGE 0x81 | ||
307 | |||
308 | #define LATENCY 0x64 | ||
309 | #define CACHE 64 | ||
310 | #define DEVICEENABLE 0x015F /* CPQ has 0x0157 */ | ||
311 | |||
312 | #define IOBRIDGE 0x1000 /* 4k */ | ||
313 | #define MEMBRIDGE 0x100000 /* 1M */ | ||
314 | |||
315 | /* irqs */ | ||
316 | #define SCSI_IRQ 0x09 | ||
317 | #define LAN_IRQ 0x0A | ||
318 | #define OTHER_IRQ 0x0B | ||
319 | |||
320 | /* Data Structures */ | ||
321 | |||
322 | /* type is of the form x x xx xx | ||
323 | * | | | |_ 00 - I/O, 01 - Memory, 11 - PFMemory | ||
324 | * | | - 00 - No Restrictions, 01 - Avoid VGA, 10 - Avoid | ||
325 | * | | VGA and their aliases, 11 - Avoid ISA | ||
326 | * | - 1 - PCI device, 0 - non pci device | ||
327 | * - 1 - Primary PCI Bus Information (0 if Normal device) | ||
328 | * the IO restrictions [2:3] are only for primary buses | ||
329 | */ | ||
330 | |||
331 | |||
332 | /* we need this struct because there could be several resource blocks | ||
333 | * allocated per primary bus in the EBDA | ||
334 | */ | ||
335 | struct range_node { | ||
336 | int rangeno; | ||
337 | u32 start; | ||
338 | u32 end; | ||
339 | struct range_node *next; | ||
340 | }; | ||
341 | |||
342 | struct bus_node { | ||
343 | u8 busno; | ||
344 | int noIORanges; | ||
345 | struct range_node *rangeIO; | ||
346 | int noMemRanges; | ||
347 | struct range_node *rangeMem; | ||
348 | int noPFMemRanges; | ||
349 | struct range_node *rangePFMem; | ||
350 | int needIOUpdate; | ||
351 | int needMemUpdate; | ||
352 | int needPFMemUpdate; | ||
353 | struct resource_node *firstIO; /* first IO resource on the Bus */ | ||
354 | struct resource_node *firstMem; /* first memory resource on the Bus */ | ||
355 | struct resource_node *firstPFMem; /* first prefetchable memory resource on the Bus */ | ||
356 | struct resource_node *firstPFMemFromMem; /* when run out of pfmem available, taking from Mem */ | ||
357 | struct list_head bus_list; | ||
358 | }; | ||
359 | |||
360 | struct resource_node { | ||
361 | int rangeno; | ||
362 | u8 busno; | ||
363 | u8 devfunc; | ||
364 | u32 start; | ||
365 | u32 end; | ||
366 | u32 len; | ||
367 | int type; /* MEM, IO, PFMEM */ | ||
368 | u8 fromMem; /* this is to indicate that the range is from | ||
369 | * from the Memory bucket rather than from PFMem */ | ||
370 | struct resource_node *next; | ||
371 | struct resource_node *nextRange; /* for the other mem range on bus */ | ||
372 | }; | ||
373 | |||
374 | struct res_needed { | ||
375 | u32 mem; | ||
376 | u32 pfmem; | ||
377 | u32 io; | ||
378 | u8 not_correct; /* needed for return */ | ||
379 | int devices[32]; /* for device numbers behind this bridge */ | ||
380 | }; | ||
381 | |||
382 | /* functions */ | ||
383 | |||
384 | extern int ibmphp_rsrc_init (void); | ||
385 | extern int ibmphp_add_resource (struct resource_node *); | ||
386 | extern int ibmphp_remove_resource (struct resource_node *); | ||
387 | extern int ibmphp_find_resource (struct bus_node *, u32, struct resource_node **, int); | ||
388 | extern int ibmphp_check_resource (struct resource_node *, u8); | ||
389 | extern int ibmphp_remove_bus (struct bus_node *, u8); | ||
390 | extern void ibmphp_free_resources (void); | ||
391 | extern int ibmphp_add_pfmem_from_mem (struct resource_node *); | ||
392 | extern struct bus_node *ibmphp_find_res_bus (u8); | ||
393 | extern void ibmphp_print_test (void); /* for debugging purposes */ | ||
394 | |||
395 | extern void ibmphp_hpc_initvars (void); | ||
396 | extern int ibmphp_hpc_readslot (struct slot *, u8, u8 *); | ||
397 | extern int ibmphp_hpc_writeslot (struct slot *, u8); | ||
398 | extern void ibmphp_lock_operations (void); | ||
399 | extern void ibmphp_unlock_operations (void); | ||
400 | extern int ibmphp_hpc_start_poll_thread (void); | ||
401 | extern void ibmphp_hpc_stop_poll_thread (void); | ||
402 | |||
403 | //---------------------------------------------------------------------------- | ||
404 | |||
405 | |||
406 | //---------------------------------------------------------------------------- | ||
407 | // HPC return codes | ||
408 | //---------------------------------------------------------------------------- | ||
409 | #define FALSE 0x00 | ||
410 | #define TRUE 0x01 | ||
411 | #define HPC_ERROR 0xFF | ||
412 | |||
413 | //----------------------------------------------------------------------------- | ||
414 | // BUS INFO | ||
415 | //----------------------------------------------------------------------------- | ||
416 | #define BUS_SPEED 0x30 | ||
417 | #define BUS_MODE 0x40 | ||
418 | #define BUS_MODE_PCIX 0x01 | ||
419 | #define BUS_MODE_PCI 0x00 | ||
420 | #define BUS_SPEED_2 0x20 | ||
421 | #define BUS_SPEED_1 0x10 | ||
422 | #define BUS_SPEED_33 0x00 | ||
423 | #define BUS_SPEED_66 0x01 | ||
424 | #define BUS_SPEED_100 0x02 | ||
425 | #define BUS_SPEED_133 0x03 | ||
426 | #define BUS_SPEED_66PCIX 0x04 | ||
427 | #define BUS_SPEED_66UNKNOWN 0x05 | ||
428 | #define BUS_STATUS_AVAILABLE 0x01 | ||
429 | #define BUS_CONTROL_AVAILABLE 0x02 | ||
430 | #define SLOT_LATCH_REGS_SUPPORTED 0x10 | ||
431 | |||
432 | #define PRGM_MODEL_REV_LEVEL 0xF0 | ||
433 | #define MAX_ADAPTER_NONE 0x09 | ||
434 | |||
435 | //---------------------------------------------------------------------------- | ||
436 | // HPC 'write' operations/commands | ||
437 | //---------------------------------------------------------------------------- | ||
438 | // Command Code State Write to reg | ||
439 | // Machine at index | ||
440 | //------------------------- ---- ------- ------------ | ||
441 | #define HPC_CTLR_ENABLEIRQ 0x00 // N 15 | ||
442 | #define HPC_CTLR_DISABLEIRQ 0x01 // N 15 | ||
443 | #define HPC_SLOT_OFF 0x02 // Y 0-14 | ||
444 | #define HPC_SLOT_ON 0x03 // Y 0-14 | ||
445 | #define HPC_SLOT_ATTNOFF 0x04 // N 0-14 | ||
446 | #define HPC_SLOT_ATTNON 0x05 // N 0-14 | ||
447 | #define HPC_CTLR_CLEARIRQ 0x06 // N 15 | ||
448 | #define HPC_CTLR_RESET 0x07 // Y 15 | ||
449 | #define HPC_CTLR_IRQSTEER 0x08 // N 15 | ||
450 | #define HPC_BUS_33CONVMODE 0x09 // Y 31-34 | ||
451 | #define HPC_BUS_66CONVMODE 0x0A // Y 31-34 | ||
452 | #define HPC_BUS_66PCIXMODE 0x0B // Y 31-34 | ||
453 | #define HPC_BUS_100PCIXMODE 0x0C // Y 31-34 | ||
454 | #define HPC_BUS_133PCIXMODE 0x0D // Y 31-34 | ||
455 | #define HPC_ALLSLOT_OFF 0x11 // Y 15 | ||
456 | #define HPC_ALLSLOT_ON 0x12 // Y 15 | ||
457 | #define HPC_SLOT_BLINKLED 0x13 // N 0-14 | ||
458 | |||
459 | //---------------------------------------------------------------------------- | ||
460 | // read commands | ||
461 | //---------------------------------------------------------------------------- | ||
462 | #define READ_SLOTSTATUS 0x01 | ||
463 | #define READ_EXTSLOTSTATUS 0x02 | ||
464 | #define READ_BUSSTATUS 0x03 | ||
465 | #define READ_CTLRSTATUS 0x04 | ||
466 | #define READ_ALLSTAT 0x05 | ||
467 | #define READ_ALLSLOT 0x06 | ||
468 | #define READ_SLOTLATCHLOWREG 0x07 | ||
469 | #define READ_REVLEVEL 0x08 | ||
470 | #define READ_HPCOPTIONS 0x09 | ||
471 | //---------------------------------------------------------------------------- | ||
472 | // slot status | ||
473 | //---------------------------------------------------------------------------- | ||
474 | #define HPC_SLOT_POWER 0x01 | ||
475 | #define HPC_SLOT_CONNECT 0x02 | ||
476 | #define HPC_SLOT_ATTN 0x04 | ||
477 | #define HPC_SLOT_PRSNT2 0x08 | ||
478 | #define HPC_SLOT_PRSNT1 0x10 | ||
479 | #define HPC_SLOT_PWRGD 0x20 | ||
480 | #define HPC_SLOT_BUS_SPEED 0x40 | ||
481 | #define HPC_SLOT_LATCH 0x80 | ||
482 | |||
483 | //---------------------------------------------------------------------------- | ||
484 | // HPC_SLOT_POWER status return codes | ||
485 | //---------------------------------------------------------------------------- | ||
486 | #define HPC_SLOT_POWER_OFF 0x00 | ||
487 | #define HPC_SLOT_POWER_ON 0x01 | ||
488 | |||
489 | //---------------------------------------------------------------------------- | ||
490 | // HPC_SLOT_CONNECT status return codes | ||
491 | //---------------------------------------------------------------------------- | ||
492 | #define HPC_SLOT_CONNECTED 0x00 | ||
493 | #define HPC_SLOT_DISCONNECTED 0x01 | ||
494 | |||
495 | //---------------------------------------------------------------------------- | ||
496 | // HPC_SLOT_ATTN status return codes | ||
497 | //---------------------------------------------------------------------------- | ||
498 | #define HPC_SLOT_ATTN_OFF 0x00 | ||
499 | #define HPC_SLOT_ATTN_ON 0x01 | ||
500 | #define HPC_SLOT_ATTN_BLINK 0x02 | ||
501 | |||
502 | //---------------------------------------------------------------------------- | ||
503 | // HPC_SLOT_PRSNT status return codes | ||
504 | //---------------------------------------------------------------------------- | ||
505 | #define HPC_SLOT_EMPTY 0x00 | ||
506 | #define HPC_SLOT_PRSNT_7 0x01 | ||
507 | #define HPC_SLOT_PRSNT_15 0x02 | ||
508 | #define HPC_SLOT_PRSNT_25 0x03 | ||
509 | |||
510 | //---------------------------------------------------------------------------- | ||
511 | // HPC_SLOT_PWRGD status return codes | ||
512 | //---------------------------------------------------------------------------- | ||
513 | #define HPC_SLOT_PWRGD_FAULT_NONE 0x00 | ||
514 | #define HPC_SLOT_PWRGD_GOOD 0x01 | ||
515 | |||
516 | //---------------------------------------------------------------------------- | ||
517 | // HPC_SLOT_BUS_SPEED status return codes | ||
518 | //---------------------------------------------------------------------------- | ||
519 | #define HPC_SLOT_BUS_SPEED_OK 0x00 | ||
520 | #define HPC_SLOT_BUS_SPEED_MISM 0x01 | ||
521 | |||
522 | //---------------------------------------------------------------------------- | ||
523 | // HPC_SLOT_LATCH status return codes | ||
524 | //---------------------------------------------------------------------------- | ||
525 | #define HPC_SLOT_LATCH_OPEN 0x01 // NOTE : in PCI spec bit off = open | ||
526 | #define HPC_SLOT_LATCH_CLOSED 0x00 // NOTE : in PCI spec bit on = closed | ||
527 | |||
528 | |||
529 | //---------------------------------------------------------------------------- | ||
530 | // extended slot status | ||
531 | //---------------------------------------------------------------------------- | ||
532 | #define HPC_SLOT_PCIX 0x01 | ||
533 | #define HPC_SLOT_SPEED1 0x02 | ||
534 | #define HPC_SLOT_SPEED2 0x04 | ||
535 | #define HPC_SLOT_BLINK_ATTN 0x08 | ||
536 | #define HPC_SLOT_RSRVD1 0x10 | ||
537 | #define HPC_SLOT_RSRVD2 0x20 | ||
538 | #define HPC_SLOT_BUS_MODE 0x40 | ||
539 | #define HPC_SLOT_RSRVD3 0x80 | ||
540 | |||
541 | //---------------------------------------------------------------------------- | ||
542 | // HPC_XSLOT_PCIX_CAP status return codes | ||
543 | //---------------------------------------------------------------------------- | ||
544 | #define HPC_SLOT_PCIX_NO 0x00 | ||
545 | #define HPC_SLOT_PCIX_YES 0x01 | ||
546 | |||
547 | //---------------------------------------------------------------------------- | ||
548 | // HPC_XSLOT_SPEED status return codes | ||
549 | //---------------------------------------------------------------------------- | ||
550 | #define HPC_SLOT_SPEED_33 0x00 | ||
551 | #define HPC_SLOT_SPEED_66 0x01 | ||
552 | #define HPC_SLOT_SPEED_133 0x02 | ||
553 | |||
554 | //---------------------------------------------------------------------------- | ||
555 | // HPC_XSLOT_ATTN_BLINK status return codes | ||
556 | //---------------------------------------------------------------------------- | ||
557 | #define HPC_SLOT_ATTN_BLINK_OFF 0x00 | ||
558 | #define HPC_SLOT_ATTN_BLINK_ON 0x01 | ||
559 | |||
560 | //---------------------------------------------------------------------------- | ||
561 | // HPC_XSLOT_BUS_MODE status return codes | ||
562 | //---------------------------------------------------------------------------- | ||
563 | #define HPC_SLOT_BUS_MODE_OK 0x00 | ||
564 | #define HPC_SLOT_BUS_MODE_MISM 0x01 | ||
565 | |||
566 | //---------------------------------------------------------------------------- | ||
567 | // Controller status | ||
568 | //---------------------------------------------------------------------------- | ||
569 | #define HPC_CTLR_WORKING 0x01 | ||
570 | #define HPC_CTLR_FINISHED 0x02 | ||
571 | #define HPC_CTLR_RESULT0 0x04 | ||
572 | #define HPC_CTLR_RESULT1 0x08 | ||
573 | #define HPC_CTLR_RESULE2 0x10 | ||
574 | #define HPC_CTLR_RESULT3 0x20 | ||
575 | #define HPC_CTLR_IRQ_ROUTG 0x40 | ||
576 | #define HPC_CTLR_IRQ_PENDG 0x80 | ||
577 | |||
578 | //---------------------------------------------------------------------------- | ||
579 | // HPC_CTLR_WROKING status return codes | ||
580 | //---------------------------------------------------------------------------- | ||
581 | #define HPC_CTLR_WORKING_NO 0x00 | ||
582 | #define HPC_CTLR_WORKING_YES 0x01 | ||
583 | |||
584 | //---------------------------------------------------------------------------- | ||
585 | // HPC_CTLR_FINISHED status return codes | ||
586 | //---------------------------------------------------------------------------- | ||
587 | #define HPC_CTLR_FINISHED_NO 0x00 | ||
588 | #define HPC_CTLR_FINISHED_YES 0x01 | ||
589 | |||
590 | //---------------------------------------------------------------------------- | ||
591 | // HPC_CTLR_RESULT status return codes | ||
592 | //---------------------------------------------------------------------------- | ||
593 | #define HPC_CTLR_RESULT_SUCCESS 0x00 | ||
594 | #define HPC_CTLR_RESULT_FAILED 0x01 | ||
595 | #define HPC_CTLR_RESULT_RSVD 0x02 | ||
596 | #define HPC_CTLR_RESULT_NORESP 0x03 | ||
597 | |||
598 | |||
599 | //---------------------------------------------------------------------------- | ||
600 | // macro for slot info | ||
601 | //---------------------------------------------------------------------------- | ||
602 | #define SLOT_POWER(s) ((u8) ((s & HPC_SLOT_POWER) \ | ||
603 | ? HPC_SLOT_POWER_ON : HPC_SLOT_POWER_OFF)) | ||
604 | |||
605 | #define SLOT_CONNECT(s) ((u8) ((s & HPC_SLOT_CONNECT) \ | ||
606 | ? HPC_SLOT_DISCONNECTED : HPC_SLOT_CONNECTED)) | ||
607 | |||
608 | #define SLOT_ATTN(s,es) ((u8) ((es & HPC_SLOT_BLINK_ATTN) \ | ||
609 | ? HPC_SLOT_ATTN_BLINK \ | ||
610 | : ((s & HPC_SLOT_ATTN) ? HPC_SLOT_ATTN_ON : HPC_SLOT_ATTN_OFF))) | ||
611 | |||
612 | #define SLOT_PRESENT(s) ((u8) ((s & HPC_SLOT_PRSNT1) \ | ||
613 | ? ((s & HPC_SLOT_PRSNT2) ? HPC_SLOT_EMPTY : HPC_SLOT_PRSNT_15) \ | ||
614 | : ((s & HPC_SLOT_PRSNT2) ? HPC_SLOT_PRSNT_25 : HPC_SLOT_PRSNT_7))) | ||
615 | |||
616 | #define SLOT_PWRGD(s) ((u8) ((s & HPC_SLOT_PWRGD) \ | ||
617 | ? HPC_SLOT_PWRGD_GOOD : HPC_SLOT_PWRGD_FAULT_NONE)) | ||
618 | |||
619 | #define SLOT_BUS_SPEED(s) ((u8) ((s & HPC_SLOT_BUS_SPEED) \ | ||
620 | ? HPC_SLOT_BUS_SPEED_MISM : HPC_SLOT_BUS_SPEED_OK)) | ||
621 | |||
622 | #define SLOT_LATCH(s) ((u8) ((s & HPC_SLOT_LATCH) \ | ||
623 | ? HPC_SLOT_LATCH_CLOSED : HPC_SLOT_LATCH_OPEN)) | ||
624 | |||
625 | #define SLOT_PCIX(es) ((u8) ((es & HPC_SLOT_PCIX) \ | ||
626 | ? HPC_SLOT_PCIX_YES : HPC_SLOT_PCIX_NO)) | ||
627 | |||
628 | #define SLOT_SPEED(es) ((u8) ((es & HPC_SLOT_SPEED2) \ | ||
629 | ? ((es & HPC_SLOT_SPEED1) ? HPC_SLOT_SPEED_133 \ | ||
630 | : HPC_SLOT_SPEED_66) \ | ||
631 | : HPC_SLOT_SPEED_33)) | ||
632 | |||
633 | #define SLOT_BUS_MODE(es) ((u8) ((es & HPC_SLOT_BUS_MODE) \ | ||
634 | ? HPC_SLOT_BUS_MODE_MISM : HPC_SLOT_BUS_MODE_OK)) | ||
635 | |||
636 | //-------------------------------------------------------------------------- | ||
637 | // macro for bus info | ||
638 | //--------------------------------------------------------------------------- | ||
639 | #define CURRENT_BUS_SPEED(s) ((u8) (s & BUS_SPEED_2) \ | ||
640 | ? ((s & BUS_SPEED_1) ? BUS_SPEED_133 : BUS_SPEED_100) \ | ||
641 | : ((s & BUS_SPEED_1) ? BUS_SPEED_66 : BUS_SPEED_33)) | ||
642 | |||
643 | #define CURRENT_BUS_MODE(s) ((u8) (s & BUS_MODE) ? BUS_MODE_PCIX : BUS_MODE_PCI) | ||
644 | |||
645 | #define READ_BUS_STATUS(s) ((u8) (s->options & BUS_STATUS_AVAILABLE)) | ||
646 | |||
647 | #define READ_BUS_MODE(s) ((s->revision & PRGM_MODEL_REV_LEVEL) >= 0x20) | ||
648 | |||
649 | #define SET_BUS_STATUS(s) ((u8) (s->options & BUS_CONTROL_AVAILABLE)) | ||
650 | |||
651 | #define READ_SLOT_LATCH(s) ((u8) (s->options & SLOT_LATCH_REGS_SUPPORTED)) | ||
652 | |||
653 | //---------------------------------------------------------------------------- | ||
654 | // macro for controller info | ||
655 | //---------------------------------------------------------------------------- | ||
656 | #define CTLR_WORKING(c) ((u8) ((c & HPC_CTLR_WORKING) \ | ||
657 | ? HPC_CTLR_WORKING_YES : HPC_CTLR_WORKING_NO)) | ||
658 | #define CTLR_FINISHED(c) ((u8) ((c & HPC_CTLR_FINISHED) \ | ||
659 | ? HPC_CTLR_FINISHED_YES : HPC_CTLR_FINISHED_NO)) | ||
660 | #define CTLR_RESULT(c) ((u8) ((c & HPC_CTLR_RESULT1) \ | ||
661 | ? ((c & HPC_CTLR_RESULT0) ? HPC_CTLR_RESULT_NORESP \ | ||
662 | : HPC_CTLR_RESULT_RSVD) \ | ||
663 | : ((c & HPC_CTLR_RESULT0) ? HPC_CTLR_RESULT_FAILED \ | ||
664 | : HPC_CTLR_RESULT_SUCCESS))) | ||
665 | |||
666 | // command that affect the state machine of HPC | ||
667 | #define NEEDTOCHECK_CMDSTATUS(c) ((c == HPC_SLOT_OFF) || \ | ||
668 | (c == HPC_SLOT_ON) || \ | ||
669 | (c == HPC_CTLR_RESET) || \ | ||
670 | (c == HPC_BUS_33CONVMODE) || \ | ||
671 | (c == HPC_BUS_66CONVMODE) || \ | ||
672 | (c == HPC_BUS_66PCIXMODE) || \ | ||
673 | (c == HPC_BUS_100PCIXMODE) || \ | ||
674 | (c == HPC_BUS_133PCIXMODE) || \ | ||
675 | (c == HPC_ALLSLOT_OFF) || \ | ||
676 | (c == HPC_ALLSLOT_ON)) | ||
677 | |||
678 | |||
679 | /* Core part of the driver */ | ||
680 | |||
681 | #define ENABLE 1 | ||
682 | #define DISABLE 0 | ||
683 | |||
684 | #define CARD_INFO 0x07 | ||
685 | #define PCIX133 0x07 | ||
686 | #define PCIX66 0x05 | ||
687 | #define PCI66 0x04 | ||
688 | |||
689 | extern struct pci_bus *ibmphp_pci_bus; | ||
690 | |||
691 | /* Variables */ | ||
692 | |||
693 | struct pci_func { | ||
694 | struct pci_dev *dev; /* from the OS */ | ||
695 | u8 busno; | ||
696 | u8 device; | ||
697 | u8 function; | ||
698 | struct resource_node *io[6]; | ||
699 | struct resource_node *mem[6]; | ||
700 | struct resource_node *pfmem[6]; | ||
701 | struct pci_func *next; | ||
702 | int devices[32]; /* for bridge config */ | ||
703 | u8 irq[4]; /* for interrupt config */ | ||
704 | u8 bus; /* flag for unconfiguring, to say if PPB */ | ||
705 | }; | ||
706 | |||
707 | struct slot { | ||
708 | u8 bus; | ||
709 | u8 device; | ||
710 | u8 number; | ||
711 | u8 real_physical_slot_num; | ||
712 | char name[100]; | ||
713 | u32 capabilities; | ||
714 | u8 supported_speed; | ||
715 | u8 supported_bus_mode; | ||
716 | struct hotplug_slot *hotplug_slot; | ||
717 | struct controller *ctrl; | ||
718 | struct pci_func *func; | ||
719 | u8 irq[4]; | ||
720 | u8 flag; /* this is for disable slot and polling */ | ||
721 | int bit_mode; /* 0 = 32, 1 = 64 */ | ||
722 | u8 ctlr_index; | ||
723 | struct bus_info *bus_on; | ||
724 | struct list_head ibm_slot_list; | ||
725 | u8 status; | ||
726 | u8 ext_status; | ||
727 | u8 busstatus; | ||
728 | }; | ||
729 | |||
730 | struct controller { | ||
731 | struct ebda_hpc_slot *slots; | ||
732 | struct ebda_hpc_bus *buses; | ||
733 | struct pci_dev *ctrl_dev; /* in case where controller is PCI */ | ||
734 | u8 starting_slot_num; /* starting and ending slot #'s this ctrl controls*/ | ||
735 | u8 ending_slot_num; | ||
736 | u8 revision; | ||
737 | u8 options; /* which options HPC supports */ | ||
738 | u8 status; | ||
739 | u8 ctlr_id; | ||
740 | u8 slot_count; | ||
741 | u8 bus_count; | ||
742 | u8 ctlr_relative_id; | ||
743 | u32 irq; | ||
744 | union { | ||
745 | struct isa_ctlr_access isa_ctlr; | ||
746 | struct pci_ctlr_access pci_ctlr; | ||
747 | struct wpeg_i2c_ctlr_access wpeg_ctlr; | ||
748 | } u; | ||
749 | u8 ctlr_type; | ||
750 | struct list_head ebda_hpc_list; | ||
751 | }; | ||
752 | |||
753 | /* Functions */ | ||
754 | |||
755 | extern int ibmphp_init_devno (struct slot **); /* This function is called from EBDA, so we need it not be static */ | ||
756 | extern int ibmphp_do_disable_slot (struct slot *slot_cur); | ||
757 | extern int ibmphp_update_slot_info (struct slot *); /* This function is called from HPC, so we need it to not be be static */ | ||
758 | extern int ibmphp_configure_card (struct pci_func *, u8); | ||
759 | extern int ibmphp_unconfigure_card (struct slot **, int); | ||
760 | extern struct hotplug_slot_ops ibmphp_hotplug_slot_ops; | ||
761 | |||
762 | #endif //__IBMPHP_H | ||
763 | |||
diff --git a/drivers/pci/hotplug/ibmphp_core.c b/drivers/pci/hotplug/ibmphp_core.c new file mode 100644 index 000000000000..0392e004258f --- /dev/null +++ b/drivers/pci/hotplug/ibmphp_core.c | |||
@@ -0,0 +1,1422 @@ | |||
1 | /* | ||
2 | * IBM Hot Plug Controller Driver | ||
3 | * | ||
4 | * Written By: Chuck Cole, Jyoti Shah, Tong Yu, Irene Zubarev, IBM Corporation | ||
5 | * | ||
6 | * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com) | ||
7 | * Copyright (C) 2001-2003 IBM Corp. | ||
8 | * | ||
9 | * All rights reserved. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or (at | ||
14 | * your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
19 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
20 | * details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
25 | * | ||
26 | * Send feedback to <gregkh@us.ibm.com> | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | #include <linux/init.h> | ||
31 | #include <linux/module.h> | ||
32 | #include <linux/slab.h> | ||
33 | #include <linux/pci.h> | ||
34 | #include <linux/interrupt.h> | ||
35 | #include <linux/delay.h> | ||
36 | #include <linux/wait.h> | ||
37 | #include <linux/smp_lock.h> | ||
38 | #include "../pci.h" | ||
39 | #include "../../../arch/i386/pci/pci.h" /* for struct irq_routing_table */ | ||
40 | #include "ibmphp.h" | ||
41 | |||
42 | #define attn_on(sl) ibmphp_hpc_writeslot (sl, HPC_SLOT_ATTNON) | ||
43 | #define attn_off(sl) ibmphp_hpc_writeslot (sl, HPC_SLOT_ATTNOFF) | ||
44 | #define attn_LED_blink(sl) ibmphp_hpc_writeslot (sl, HPC_SLOT_BLINKLED) | ||
45 | #define get_ctrl_revision(sl, rev) ibmphp_hpc_readslot (sl, READ_REVLEVEL, rev) | ||
46 | #define get_hpc_options(sl, opt) ibmphp_hpc_readslot (sl, READ_HPCOPTIONS, opt) | ||
47 | |||
48 | #define DRIVER_VERSION "0.6" | ||
49 | #define DRIVER_DESC "IBM Hot Plug PCI Controller Driver" | ||
50 | |||
51 | int ibmphp_debug; | ||
52 | |||
53 | static int debug; | ||
54 | module_param(debug, bool, S_IRUGO | S_IWUSR); | ||
55 | MODULE_PARM_DESC (debug, "Debugging mode enabled or not"); | ||
56 | MODULE_LICENSE ("GPL"); | ||
57 | MODULE_DESCRIPTION (DRIVER_DESC); | ||
58 | |||
59 | struct pci_bus *ibmphp_pci_bus; | ||
60 | static int max_slots; | ||
61 | |||
62 | static int irqs[16]; /* PIC mode IRQ's we're using so far (in case MPS | ||
63 | * tables don't provide default info for empty slots */ | ||
64 | |||
65 | static int init_flag; | ||
66 | |||
67 | /* | ||
68 | static int get_max_adapter_speed_1 (struct hotplug_slot *, u8 *, u8); | ||
69 | |||
70 | static inline int get_max_adapter_speed (struct hotplug_slot *hs, u8 *value) | ||
71 | { | ||
72 | return get_max_adapter_speed_1 (hs, value, 1); | ||
73 | } | ||
74 | */ | ||
75 | static inline int get_cur_bus_info(struct slot **sl) | ||
76 | { | ||
77 | int rc = 1; | ||
78 | struct slot * slot_cur = *sl; | ||
79 | |||
80 | debug("options = %x\n", slot_cur->ctrl->options); | ||
81 | debug("revision = %x\n", slot_cur->ctrl->revision); | ||
82 | |||
83 | if (READ_BUS_STATUS(slot_cur->ctrl)) | ||
84 | rc = ibmphp_hpc_readslot(slot_cur, READ_BUSSTATUS, NULL); | ||
85 | |||
86 | if (rc) | ||
87 | return rc; | ||
88 | |||
89 | slot_cur->bus_on->current_speed = CURRENT_BUS_SPEED(slot_cur->busstatus); | ||
90 | if (READ_BUS_MODE(slot_cur->ctrl)) | ||
91 | slot_cur->bus_on->current_bus_mode = | ||
92 | CURRENT_BUS_MODE(slot_cur->busstatus); | ||
93 | else | ||
94 | slot_cur->bus_on->current_bus_mode = 0xFF; | ||
95 | |||
96 | debug("busstatus = %x, bus_speed = %x, bus_mode = %x\n", | ||
97 | slot_cur->busstatus, | ||
98 | slot_cur->bus_on->current_speed, | ||
99 | slot_cur->bus_on->current_bus_mode); | ||
100 | |||
101 | *sl = slot_cur; | ||
102 | return 0; | ||
103 | } | ||
104 | |||
105 | static inline int slot_update(struct slot **sl) | ||
106 | { | ||
107 | int rc; | ||
108 | rc = ibmphp_hpc_readslot(*sl, READ_ALLSTAT, NULL); | ||
109 | if (rc) | ||
110 | return rc; | ||
111 | if (!init_flag) | ||
112 | rc = get_cur_bus_info(sl); | ||
113 | return rc; | ||
114 | } | ||
115 | |||
116 | static int __init get_max_slots (void) | ||
117 | { | ||
118 | struct slot * slot_cur; | ||
119 | struct list_head * tmp; | ||
120 | u8 slot_count = 0; | ||
121 | |||
122 | list_for_each(tmp, &ibmphp_slot_head) { | ||
123 | slot_cur = list_entry(tmp, struct slot, ibm_slot_list); | ||
124 | /* sometimes the hot-pluggable slots start with 4 (not always from 1) */ | ||
125 | slot_count = max(slot_count, slot_cur->number); | ||
126 | } | ||
127 | return slot_count; | ||
128 | } | ||
129 | |||
130 | /* This routine will put the correct slot->device information per slot. It's | ||
131 | * called from initialization of the slot structures. It will also assign | ||
132 | * interrupt numbers per each slot. | ||
133 | * Parameters: struct slot | ||
134 | * Returns 0 or errors | ||
135 | */ | ||
136 | int ibmphp_init_devno(struct slot **cur_slot) | ||
137 | { | ||
138 | struct irq_routing_table *rtable; | ||
139 | int len; | ||
140 | int loop; | ||
141 | int i; | ||
142 | |||
143 | rtable = pcibios_get_irq_routing_table(); | ||
144 | if (!rtable) { | ||
145 | err("no BIOS routing table...\n"); | ||
146 | return -ENOMEM; | ||
147 | } | ||
148 | |||
149 | len = (rtable->size - sizeof(struct irq_routing_table)) / | ||
150 | sizeof(struct irq_info); | ||
151 | |||
152 | if (!len) | ||
153 | return -1; | ||
154 | for (loop = 0; loop < len; loop++) { | ||
155 | if ((*cur_slot)->number == rtable->slots[loop].slot) { | ||
156 | if ((*cur_slot)->bus == rtable->slots[loop].bus) { | ||
157 | (*cur_slot)->device = PCI_SLOT(rtable->slots[loop].devfn); | ||
158 | for (i = 0; i < 4; i++) | ||
159 | (*cur_slot)->irq[i] = IO_APIC_get_PCI_irq_vector((int) (*cur_slot)->bus, | ||
160 | (int) (*cur_slot)->device, i); | ||
161 | |||
162 | debug("(*cur_slot)->irq[0] = %x\n", | ||
163 | (*cur_slot)->irq[0]); | ||
164 | debug("(*cur_slot)->irq[1] = %x\n", | ||
165 | (*cur_slot)->irq[1]); | ||
166 | debug("(*cur_slot)->irq[2] = %x\n", | ||
167 | (*cur_slot)->irq[2]); | ||
168 | debug("(*cur_slot)->irq[3] = %x\n", | ||
169 | (*cur_slot)->irq[3]); | ||
170 | |||
171 | debug("rtable->exlusive_irqs = %x\n", | ||
172 | rtable->exclusive_irqs); | ||
173 | debug("rtable->slots[loop].irq[0].bitmap = %x\n", | ||
174 | rtable->slots[loop].irq[0].bitmap); | ||
175 | debug("rtable->slots[loop].irq[1].bitmap = %x\n", | ||
176 | rtable->slots[loop].irq[1].bitmap); | ||
177 | debug("rtable->slots[loop].irq[2].bitmap = %x\n", | ||
178 | rtable->slots[loop].irq[2].bitmap); | ||
179 | debug("rtable->slots[loop].irq[3].bitmap = %x\n", | ||
180 | rtable->slots[loop].irq[3].bitmap); | ||
181 | |||
182 | debug("rtable->slots[loop].irq[0].link = %x\n", | ||
183 | rtable->slots[loop].irq[0].link); | ||
184 | debug("rtable->slots[loop].irq[1].link = %x\n", | ||
185 | rtable->slots[loop].irq[1].link); | ||
186 | debug("rtable->slots[loop].irq[2].link = %x\n", | ||
187 | rtable->slots[loop].irq[2].link); | ||
188 | debug("rtable->slots[loop].irq[3].link = %x\n", | ||
189 | rtable->slots[loop].irq[3].link); | ||
190 | debug("end of init_devno\n"); | ||
191 | return 0; | ||
192 | } | ||
193 | } | ||
194 | } | ||
195 | |||
196 | return -1; | ||
197 | } | ||
198 | |||
199 | static inline int power_on(struct slot *slot_cur) | ||
200 | { | ||
201 | u8 cmd = HPC_SLOT_ON; | ||
202 | int retval; | ||
203 | |||
204 | retval = ibmphp_hpc_writeslot(slot_cur, cmd); | ||
205 | if (retval) { | ||
206 | err("power on failed\n"); | ||
207 | return retval; | ||
208 | } | ||
209 | if (CTLR_RESULT(slot_cur->ctrl->status)) { | ||
210 | err("command not completed successfully in power_on\n"); | ||
211 | return -EIO; | ||
212 | } | ||
213 | msleep(3000); /* For ServeRAID cards, and some 66 PCI */ | ||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | static inline int power_off(struct slot *slot_cur) | ||
218 | { | ||
219 | u8 cmd = HPC_SLOT_OFF; | ||
220 | int retval; | ||
221 | |||
222 | retval = ibmphp_hpc_writeslot(slot_cur, cmd); | ||
223 | if (retval) { | ||
224 | err("power off failed\n"); | ||
225 | return retval; | ||
226 | } | ||
227 | if (CTLR_RESULT(slot_cur->ctrl->status)) { | ||
228 | err("command not completed successfully in power_off\n"); | ||
229 | retval = -EIO; | ||
230 | } | ||
231 | return retval; | ||
232 | } | ||
233 | |||
234 | static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value) | ||
235 | { | ||
236 | int rc = 0; | ||
237 | struct slot *pslot; | ||
238 | u8 cmd; | ||
239 | |||
240 | debug("set_attention_status - Entry hotplug_slot[%lx] value[%x]\n", | ||
241 | (ulong) hotplug_slot, value); | ||
242 | ibmphp_lock_operations(); | ||
243 | cmd = 0x00; // avoid compiler warning | ||
244 | |||
245 | if (hotplug_slot) { | ||
246 | switch (value) { | ||
247 | case HPC_SLOT_ATTN_OFF: | ||
248 | cmd = HPC_SLOT_ATTNOFF; | ||
249 | break; | ||
250 | case HPC_SLOT_ATTN_ON: | ||
251 | cmd = HPC_SLOT_ATTNON; | ||
252 | break; | ||
253 | case HPC_SLOT_ATTN_BLINK: | ||
254 | cmd = HPC_SLOT_BLINKLED; | ||
255 | break; | ||
256 | default: | ||
257 | rc = -ENODEV; | ||
258 | err("set_attention_status - Error : invalid input [%x]\n", | ||
259 | value); | ||
260 | break; | ||
261 | } | ||
262 | if (rc == 0) { | ||
263 | pslot = hotplug_slot->private; | ||
264 | if (pslot) | ||
265 | rc = ibmphp_hpc_writeslot(pslot, cmd); | ||
266 | else | ||
267 | rc = -ENODEV; | ||
268 | } | ||
269 | } else | ||
270 | rc = -ENODEV; | ||
271 | |||
272 | ibmphp_unlock_operations(); | ||
273 | |||
274 | debug("set_attention_status - Exit rc[%d]\n", rc); | ||
275 | return rc; | ||
276 | } | ||
277 | |||
278 | static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 * value) | ||
279 | { | ||
280 | int rc = -ENODEV; | ||
281 | struct slot *pslot; | ||
282 | struct slot myslot; | ||
283 | |||
284 | debug("get_attention_status - Entry hotplug_slot[%lx] pvalue[%lx]\n", | ||
285 | (ulong) hotplug_slot, (ulong) value); | ||
286 | |||
287 | ibmphp_lock_operations(); | ||
288 | if (hotplug_slot && value) { | ||
289 | pslot = hotplug_slot->private; | ||
290 | if (pslot) { | ||
291 | memcpy(&myslot, pslot, sizeof(struct slot)); | ||
292 | rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS, | ||
293 | &(myslot.status)); | ||
294 | if (!rc) | ||
295 | rc = ibmphp_hpc_readslot(pslot, | ||
296 | READ_EXTSLOTSTATUS, | ||
297 | &(myslot.ext_status)); | ||
298 | if (!rc) | ||
299 | *value = SLOT_ATTN(myslot.status, | ||
300 | myslot.ext_status); | ||
301 | } | ||
302 | } | ||
303 | |||
304 | ibmphp_unlock_operations(); | ||
305 | debug("get_attention_status - Exit rc[%d] value[%x]\n", rc, *value); | ||
306 | return rc; | ||
307 | } | ||
308 | |||
309 | static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 * value) | ||
310 | { | ||
311 | int rc = -ENODEV; | ||
312 | struct slot *pslot; | ||
313 | struct slot myslot; | ||
314 | |||
315 | debug("get_latch_status - Entry hotplug_slot[%lx] pvalue[%lx]\n", | ||
316 | (ulong) hotplug_slot, (ulong) value); | ||
317 | ibmphp_lock_operations(); | ||
318 | if (hotplug_slot && value) { | ||
319 | pslot = hotplug_slot->private; | ||
320 | if (pslot) { | ||
321 | memcpy(&myslot, pslot, sizeof(struct slot)); | ||
322 | rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS, | ||
323 | &(myslot.status)); | ||
324 | if (!rc) | ||
325 | *value = SLOT_LATCH(myslot.status); | ||
326 | } | ||
327 | } | ||
328 | |||
329 | ibmphp_unlock_operations(); | ||
330 | debug("get_latch_status - Exit rc[%d] rc[%x] value[%x]\n", | ||
331 | rc, rc, *value); | ||
332 | return rc; | ||
333 | } | ||
334 | |||
335 | |||
336 | static int get_power_status(struct hotplug_slot *hotplug_slot, u8 * value) | ||
337 | { | ||
338 | int rc = -ENODEV; | ||
339 | struct slot *pslot; | ||
340 | struct slot myslot; | ||
341 | |||
342 | debug("get_power_status - Entry hotplug_slot[%lx] pvalue[%lx]\n", | ||
343 | (ulong) hotplug_slot, (ulong) value); | ||
344 | ibmphp_lock_operations(); | ||
345 | if (hotplug_slot && value) { | ||
346 | pslot = hotplug_slot->private; | ||
347 | if (pslot) { | ||
348 | memcpy(&myslot, pslot, sizeof(struct slot)); | ||
349 | rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS, | ||
350 | &(myslot.status)); | ||
351 | if (!rc) | ||
352 | *value = SLOT_PWRGD(myslot.status); | ||
353 | } | ||
354 | } | ||
355 | |||
356 | ibmphp_unlock_operations(); | ||
357 | debug("get_power_status - Exit rc[%d] rc[%x] value[%x]\n", | ||
358 | rc, rc, *value); | ||
359 | return rc; | ||
360 | } | ||
361 | |||
362 | static int get_adapter_present(struct hotplug_slot *hotplug_slot, u8 * value) | ||
363 | { | ||
364 | int rc = -ENODEV; | ||
365 | struct slot *pslot; | ||
366 | u8 present; | ||
367 | struct slot myslot; | ||
368 | |||
369 | debug("get_adapter_status - Entry hotplug_slot[%lx] pvalue[%lx]\n", | ||
370 | (ulong) hotplug_slot, (ulong) value); | ||
371 | ibmphp_lock_operations(); | ||
372 | if (hotplug_slot && value) { | ||
373 | pslot = hotplug_slot->private; | ||
374 | if (pslot) { | ||
375 | memcpy(&myslot, pslot, sizeof(struct slot)); | ||
376 | rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS, | ||
377 | &(myslot.status)); | ||
378 | if (!rc) { | ||
379 | present = SLOT_PRESENT(myslot.status); | ||
380 | if (present == HPC_SLOT_EMPTY) | ||
381 | *value = 0; | ||
382 | else | ||
383 | *value = 1; | ||
384 | } | ||
385 | } | ||
386 | } | ||
387 | |||
388 | ibmphp_unlock_operations(); | ||
389 | debug("get_adapter_present - Exit rc[%d] value[%x]\n", rc, *value); | ||
390 | return rc; | ||
391 | } | ||
392 | |||
393 | static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) | ||
394 | { | ||
395 | int rc = -ENODEV; | ||
396 | struct slot *pslot; | ||
397 | u8 mode = 0; | ||
398 | |||
399 | debug("%s - Entry hotplug_slot[%p] pvalue[%p]\n", __FUNCTION__, | ||
400 | hotplug_slot, value); | ||
401 | |||
402 | ibmphp_lock_operations(); | ||
403 | |||
404 | if (hotplug_slot && value) { | ||
405 | pslot = hotplug_slot->private; | ||
406 | if (pslot) { | ||
407 | rc = 0; | ||
408 | mode = pslot->supported_bus_mode; | ||
409 | *value = pslot->supported_speed; | ||
410 | switch (*value) { | ||
411 | case BUS_SPEED_33: | ||
412 | break; | ||
413 | case BUS_SPEED_66: | ||
414 | if (mode == BUS_MODE_PCIX) | ||
415 | *value += 0x01; | ||
416 | break; | ||
417 | case BUS_SPEED_100: | ||
418 | case BUS_SPEED_133: | ||
419 | *value = pslot->supported_speed + 0x01; | ||
420 | break; | ||
421 | default: | ||
422 | /* Note (will need to change): there would be soon 256, 512 also */ | ||
423 | rc = -ENODEV; | ||
424 | } | ||
425 | } | ||
426 | } | ||
427 | |||
428 | ibmphp_unlock_operations(); | ||
429 | debug("%s - Exit rc[%d] value[%x]\n", __FUNCTION__, rc, *value); | ||
430 | return rc; | ||
431 | } | ||
432 | |||
433 | static int get_cur_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) | ||
434 | { | ||
435 | int rc = -ENODEV; | ||
436 | struct slot *pslot; | ||
437 | u8 mode = 0; | ||
438 | |||
439 | debug("%s - Entry hotplug_slot[%p] pvalue[%p]\n", __FUNCTION__, | ||
440 | hotplug_slot, value); | ||
441 | |||
442 | ibmphp_lock_operations(); | ||
443 | |||
444 | if (hotplug_slot && value) { | ||
445 | pslot = hotplug_slot->private; | ||
446 | if (pslot) { | ||
447 | rc = get_cur_bus_info(&pslot); | ||
448 | if (!rc) { | ||
449 | mode = pslot->bus_on->current_bus_mode; | ||
450 | *value = pslot->bus_on->current_speed; | ||
451 | switch (*value) { | ||
452 | case BUS_SPEED_33: | ||
453 | break; | ||
454 | case BUS_SPEED_66: | ||
455 | if (mode == BUS_MODE_PCIX) | ||
456 | *value += 0x01; | ||
457 | else if (mode == BUS_MODE_PCI) | ||
458 | ; | ||
459 | else | ||
460 | *value = PCI_SPEED_UNKNOWN; | ||
461 | break; | ||
462 | case BUS_SPEED_100: | ||
463 | case BUS_SPEED_133: | ||
464 | *value += 0x01; | ||
465 | break; | ||
466 | default: | ||
467 | /* Note of change: there would also be 256, 512 soon */ | ||
468 | rc = -ENODEV; | ||
469 | } | ||
470 | } | ||
471 | } | ||
472 | } | ||
473 | |||
474 | ibmphp_unlock_operations(); | ||
475 | debug("%s - Exit rc[%d] value[%x]\n", __FUNCTION__, rc, *value); | ||
476 | return rc; | ||
477 | } | ||
478 | |||
479 | /* | ||
480 | static int get_max_adapter_speed_1(struct hotplug_slot *hotplug_slot, u8 * value, u8 flag) | ||
481 | { | ||
482 | int rc = -ENODEV; | ||
483 | struct slot *pslot; | ||
484 | struct slot myslot; | ||
485 | |||
486 | debug("get_max_adapter_speed_1 - Entry hotplug_slot[%lx] pvalue[%lx]\n", | ||
487 | (ulong)hotplug_slot, (ulong) value); | ||
488 | |||
489 | if (flag) | ||
490 | ibmphp_lock_operations(); | ||
491 | |||
492 | if (hotplug_slot && value) { | ||
493 | pslot = hotplug_slot->private; | ||
494 | if (pslot) { | ||
495 | memcpy(&myslot, pslot, sizeof(struct slot)); | ||
496 | rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS, | ||
497 | &(myslot.status)); | ||
498 | |||
499 | if (!(SLOT_LATCH (myslot.status)) && | ||
500 | (SLOT_PRESENT (myslot.status))) { | ||
501 | rc = ibmphp_hpc_readslot(pslot, | ||
502 | READ_EXTSLOTSTATUS, | ||
503 | &(myslot.ext_status)); | ||
504 | if (!rc) | ||
505 | *value = SLOT_SPEED(myslot.ext_status); | ||
506 | } else | ||
507 | *value = MAX_ADAPTER_NONE; | ||
508 | } | ||
509 | } | ||
510 | |||
511 | if (flag) | ||
512 | ibmphp_unlock_operations(); | ||
513 | |||
514 | debug("get_max_adapter_speed_1 - Exit rc[%d] value[%x]\n", rc, *value); | ||
515 | return rc; | ||
516 | } | ||
517 | |||
518 | static int get_bus_name(struct hotplug_slot *hotplug_slot, char * value) | ||
519 | { | ||
520 | int rc = -ENODEV; | ||
521 | struct slot *pslot = NULL; | ||
522 | |||
523 | debug("get_bus_name - Entry hotplug_slot[%lx]\n", (ulong)hotplug_slot); | ||
524 | |||
525 | ibmphp_lock_operations(); | ||
526 | |||
527 | if (hotplug_slot) { | ||
528 | pslot = hotplug_slot->private; | ||
529 | if (pslot) { | ||
530 | rc = 0; | ||
531 | snprintf(value, 100, "Bus %x", pslot->bus); | ||
532 | } | ||
533 | } else | ||
534 | rc = -ENODEV; | ||
535 | |||
536 | ibmphp_unlock_operations(); | ||
537 | debug("get_bus_name - Exit rc[%d] value[%x]\n", rc, *value); | ||
538 | return rc; | ||
539 | } | ||
540 | */ | ||
541 | |||
542 | /**************************************************************************** | ||
543 | * This routine will initialize the ops data structure used in the validate | ||
544 | * function. It will also power off empty slots that are powered on since BIOS | ||
545 | * leaves those on, albeit disconnected | ||
546 | ****************************************************************************/ | ||
547 | static int __init init_ops(void) | ||
548 | { | ||
549 | struct slot *slot_cur; | ||
550 | struct list_head *tmp; | ||
551 | int retval; | ||
552 | int rc; | ||
553 | |||
554 | list_for_each(tmp, &ibmphp_slot_head) { | ||
555 | slot_cur = list_entry(tmp, struct slot, ibm_slot_list); | ||
556 | |||
557 | if (!slot_cur) | ||
558 | return -ENODEV; | ||
559 | |||
560 | debug("BEFORE GETTING SLOT STATUS, slot # %x\n", | ||
561 | slot_cur->number); | ||
562 | if (slot_cur->ctrl->revision == 0xFF) | ||
563 | if (get_ctrl_revision(slot_cur, | ||
564 | &slot_cur->ctrl->revision)) | ||
565 | return -1; | ||
566 | |||
567 | if (slot_cur->bus_on->current_speed == 0xFF) | ||
568 | if (get_cur_bus_info(&slot_cur)) | ||
569 | return -1; | ||
570 | |||
571 | if (slot_cur->ctrl->options == 0xFF) | ||
572 | if (get_hpc_options(slot_cur, &slot_cur->ctrl->options)) | ||
573 | return -1; | ||
574 | |||
575 | retval = slot_update(&slot_cur); | ||
576 | if (retval) | ||
577 | return retval; | ||
578 | |||
579 | debug("status = %x\n", slot_cur->status); | ||
580 | debug("ext_status = %x\n", slot_cur->ext_status); | ||
581 | debug("SLOT_POWER = %x\n", SLOT_POWER(slot_cur->status)); | ||
582 | debug("SLOT_PRESENT = %x\n", SLOT_PRESENT(slot_cur->status)); | ||
583 | debug("SLOT_LATCH = %x\n", SLOT_LATCH(slot_cur->status)); | ||
584 | |||
585 | if ((SLOT_PWRGD(slot_cur->status)) && | ||
586 | !(SLOT_PRESENT(slot_cur->status)) && | ||
587 | !(SLOT_LATCH(slot_cur->status))) { | ||
588 | debug("BEFORE POWER OFF COMMAND\n"); | ||
589 | rc = power_off(slot_cur); | ||
590 | if (rc) | ||
591 | return rc; | ||
592 | |||
593 | /* retval = slot_update(&slot_cur); | ||
594 | * if (retval) | ||
595 | * return retval; | ||
596 | * ibmphp_update_slot_info(slot_cur); | ||
597 | */ | ||
598 | } | ||
599 | } | ||
600 | init_flag = 0; | ||
601 | return 0; | ||
602 | } | ||
603 | |||
604 | /* This operation will check whether the slot is within the bounds and | ||
605 | * the operation is valid to perform on that slot | ||
606 | * Parameters: slot, operation | ||
607 | * Returns: 0 or error codes | ||
608 | */ | ||
609 | static int validate(struct slot *slot_cur, int opn) | ||
610 | { | ||
611 | int number; | ||
612 | int retval; | ||
613 | |||
614 | if (!slot_cur) | ||
615 | return -ENODEV; | ||
616 | number = slot_cur->number; | ||
617 | if ((number > max_slots) || (number < 0)) | ||
618 | return -EBADSLT; | ||
619 | debug("slot_number in validate is %d\n", slot_cur->number); | ||
620 | |||
621 | retval = slot_update(&slot_cur); | ||
622 | if (retval) | ||
623 | return retval; | ||
624 | |||
625 | switch (opn) { | ||
626 | case ENABLE: | ||
627 | if (!(SLOT_PWRGD(slot_cur->status)) && | ||
628 | (SLOT_PRESENT(slot_cur->status)) && | ||
629 | !(SLOT_LATCH(slot_cur->status))) | ||
630 | return 0; | ||
631 | break; | ||
632 | case DISABLE: | ||
633 | if ((SLOT_PWRGD(slot_cur->status)) && | ||
634 | (SLOT_PRESENT(slot_cur->status)) && | ||
635 | !(SLOT_LATCH(slot_cur->status))) | ||
636 | return 0; | ||
637 | break; | ||
638 | default: | ||
639 | break; | ||
640 | } | ||
641 | err("validate failed....\n"); | ||
642 | return -EINVAL; | ||
643 | } | ||
644 | |||
645 | /**************************************************************************** | ||
646 | * This routine is for updating the data structures in the hotplug core | ||
647 | * Parameters: struct slot | ||
648 | * Returns: 0 or error | ||
649 | ****************************************************************************/ | ||
650 | int ibmphp_update_slot_info(struct slot *slot_cur) | ||
651 | { | ||
652 | struct hotplug_slot_info *info; | ||
653 | int rc; | ||
654 | u8 bus_speed; | ||
655 | u8 mode; | ||
656 | |||
657 | info = kmalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL); | ||
658 | if (!info) { | ||
659 | err("out of system memory\n"); | ||
660 | return -ENOMEM; | ||
661 | } | ||
662 | |||
663 | info->power_status = SLOT_PWRGD(slot_cur->status); | ||
664 | info->attention_status = SLOT_ATTN(slot_cur->status, | ||
665 | slot_cur->ext_status); | ||
666 | info->latch_status = SLOT_LATCH(slot_cur->status); | ||
667 | if (!SLOT_PRESENT(slot_cur->status)) { | ||
668 | info->adapter_status = 0; | ||
669 | /* info->max_adapter_speed_status = MAX_ADAPTER_NONE; */ | ||
670 | } else { | ||
671 | info->adapter_status = 1; | ||
672 | /* get_max_adapter_speed_1(slot_cur->hotplug_slot, | ||
673 | &info->max_adapter_speed_status, 0); */ | ||
674 | } | ||
675 | |||
676 | bus_speed = slot_cur->bus_on->current_speed; | ||
677 | mode = slot_cur->bus_on->current_bus_mode; | ||
678 | |||
679 | switch (bus_speed) { | ||
680 | case BUS_SPEED_33: | ||
681 | break; | ||
682 | case BUS_SPEED_66: | ||
683 | if (mode == BUS_MODE_PCIX) | ||
684 | bus_speed += 0x01; | ||
685 | else if (mode == BUS_MODE_PCI) | ||
686 | ; | ||
687 | else | ||
688 | bus_speed = PCI_SPEED_UNKNOWN; | ||
689 | break; | ||
690 | case BUS_SPEED_100: | ||
691 | case BUS_SPEED_133: | ||
692 | bus_speed += 0x01; | ||
693 | break; | ||
694 | default: | ||
695 | bus_speed = PCI_SPEED_UNKNOWN; | ||
696 | } | ||
697 | |||
698 | info->cur_bus_speed = bus_speed; | ||
699 | info->max_bus_speed = slot_cur->hotplug_slot->info->max_bus_speed; | ||
700 | // To do: bus_names | ||
701 | |||
702 | rc = pci_hp_change_slot_info(slot_cur->hotplug_slot, info); | ||
703 | kfree(info); | ||
704 | return rc; | ||
705 | } | ||
706 | |||
707 | |||
708 | /****************************************************************************** | ||
709 | * This function will return the pci_func, given bus and devfunc, or NULL. It | ||
710 | * is called from visit routines | ||
711 | ******************************************************************************/ | ||
712 | |||
713 | static struct pci_func *ibm_slot_find(u8 busno, u8 device, u8 function) | ||
714 | { | ||
715 | struct pci_func *func_cur; | ||
716 | struct slot *slot_cur; | ||
717 | struct list_head * tmp; | ||
718 | list_for_each(tmp, &ibmphp_slot_head) { | ||
719 | slot_cur = list_entry(tmp, struct slot, ibm_slot_list); | ||
720 | if (slot_cur->func) { | ||
721 | func_cur = slot_cur->func; | ||
722 | while (func_cur) { | ||
723 | if ((func_cur->busno == busno) && | ||
724 | (func_cur->device == device) && | ||
725 | (func_cur->function == function)) | ||
726 | return func_cur; | ||
727 | func_cur = func_cur->next; | ||
728 | } | ||
729 | } | ||
730 | } | ||
731 | return NULL; | ||
732 | } | ||
733 | |||
734 | /************************************************************* | ||
735 | * This routine frees up memory used by struct slot, including | ||
736 | * the pointers to pci_func, bus, hotplug_slot, controller, | ||
737 | * and deregistering from the hotplug core | ||
738 | *************************************************************/ | ||
739 | static void free_slots(void) | ||
740 | { | ||
741 | struct slot *slot_cur; | ||
742 | struct list_head * tmp; | ||
743 | struct list_head * next; | ||
744 | |||
745 | debug("%s -- enter\n", __FUNCTION__); | ||
746 | |||
747 | list_for_each_safe(tmp, next, &ibmphp_slot_head) { | ||
748 | slot_cur = list_entry(tmp, struct slot, ibm_slot_list); | ||
749 | pci_hp_deregister(slot_cur->hotplug_slot); | ||
750 | } | ||
751 | debug("%s -- exit\n", __FUNCTION__); | ||
752 | } | ||
753 | |||
754 | static void ibm_unconfigure_device(struct pci_func *func) | ||
755 | { | ||
756 | struct pci_dev *temp; | ||
757 | u8 j; | ||
758 | |||
759 | debug("inside %s\n", __FUNCTION__); | ||
760 | debug("func->device = %x, func->function = %x\n", | ||
761 | func->device, func->function); | ||
762 | debug("func->device << 3 | 0x0 = %x\n", func->device << 3 | 0x0); | ||
763 | |||
764 | for (j = 0; j < 0x08; j++) { | ||
765 | temp = pci_find_slot(func->busno, (func->device << 3) | j); | ||
766 | if (temp) | ||
767 | pci_remove_bus_device(temp); | ||
768 | } | ||
769 | } | ||
770 | |||
771 | /* | ||
772 | * The following function is to fix kernel bug regarding | ||
773 | * getting bus entries, here we manually add those primary | ||
774 | * bus entries to kernel bus structure whenever apply | ||
775 | */ | ||
776 | static u8 bus_structure_fixup(u8 busno) | ||
777 | { | ||
778 | struct pci_bus *bus; | ||
779 | struct pci_dev *dev; | ||
780 | u16 l; | ||
781 | |||
782 | if (pci_find_bus(0, busno) || !(ibmphp_find_same_bus_num(busno))) | ||
783 | return 1; | ||
784 | |||
785 | bus = kmalloc(sizeof(*bus), GFP_KERNEL); | ||
786 | if (!bus) { | ||
787 | err("%s - out of memory\n", __FUNCTION__); | ||
788 | return 1; | ||
789 | } | ||
790 | dev = kmalloc(sizeof(*dev), GFP_KERNEL); | ||
791 | if (!dev) { | ||
792 | kfree(bus); | ||
793 | err("%s - out of memory\n", __FUNCTION__); | ||
794 | return 1; | ||
795 | } | ||
796 | |||
797 | bus->number = busno; | ||
798 | bus->ops = ibmphp_pci_bus->ops; | ||
799 | dev->bus = bus; | ||
800 | for (dev->devfn = 0; dev->devfn < 256; dev->devfn += 8) { | ||
801 | if (!pci_read_config_word(dev, PCI_VENDOR_ID, &l) && | ||
802 | (l != 0x0000) && (l != 0xffff)) { | ||
803 | debug("%s - Inside bus_struture_fixup()\n", | ||
804 | __FUNCTION__); | ||
805 | pci_scan_bus(busno, ibmphp_pci_bus->ops, NULL); | ||
806 | break; | ||
807 | } | ||
808 | } | ||
809 | |||
810 | kfree(dev); | ||
811 | kfree(bus); | ||
812 | |||
813 | return 0; | ||
814 | } | ||
815 | |||
816 | static int ibm_configure_device(struct pci_func *func) | ||
817 | { | ||
818 | unsigned char bus; | ||
819 | struct pci_bus *child; | ||
820 | int num; | ||
821 | int flag = 0; /* this is to make sure we don't double scan the bus, | ||
822 | for bridged devices primarily */ | ||
823 | |||
824 | if (!(bus_structure_fixup(func->busno))) | ||
825 | flag = 1; | ||
826 | if (func->dev == NULL) | ||
827 | func->dev = pci_find_slot(func->busno, | ||
828 | PCI_DEVFN(func->device, func->function)); | ||
829 | |||
830 | if (func->dev == NULL) { | ||
831 | struct pci_bus *bus = pci_find_bus(0, func->busno); | ||
832 | if (!bus) | ||
833 | return 0; | ||
834 | |||
835 | num = pci_scan_slot(bus, | ||
836 | PCI_DEVFN(func->device, func->function)); | ||
837 | if (num) | ||
838 | pci_bus_add_devices(bus); | ||
839 | |||
840 | func->dev = pci_find_slot(func->busno, | ||
841 | PCI_DEVFN(func->device, func->function)); | ||
842 | if (func->dev == NULL) { | ||
843 | err("ERROR... : pci_dev still NULL\n"); | ||
844 | return 0; | ||
845 | } | ||
846 | } | ||
847 | if (!(flag) && (func->dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)) { | ||
848 | pci_read_config_byte(func->dev, PCI_SECONDARY_BUS, &bus); | ||
849 | child = pci_add_new_bus(func->dev->bus, func->dev, bus); | ||
850 | pci_do_scan_bus(child); | ||
851 | } | ||
852 | |||
853 | return 0; | ||
854 | } | ||
855 | |||
856 | /******************************************************* | ||
857 | * Returns whether the bus is empty or not | ||
858 | *******************************************************/ | ||
859 | static int is_bus_empty(struct slot * slot_cur) | ||
860 | { | ||
861 | int rc; | ||
862 | struct slot * tmp_slot; | ||
863 | u8 i = slot_cur->bus_on->slot_min; | ||
864 | |||
865 | while (i <= slot_cur->bus_on->slot_max) { | ||
866 | if (i == slot_cur->number) { | ||
867 | i++; | ||
868 | continue; | ||
869 | } | ||
870 | tmp_slot = ibmphp_get_slot_from_physical_num(i); | ||
871 | if (!tmp_slot) | ||
872 | return 0; | ||
873 | rc = slot_update(&tmp_slot); | ||
874 | if (rc) | ||
875 | return 0; | ||
876 | if (SLOT_PRESENT(tmp_slot->status) && | ||
877 | SLOT_PWRGD(tmp_slot->status)) | ||
878 | return 0; | ||
879 | i++; | ||
880 | } | ||
881 | return 1; | ||
882 | } | ||
883 | |||
884 | /*********************************************************** | ||
885 | * If the HPC permits and the bus currently empty, tries to set the | ||
886 | * bus speed and mode at the maximum card and bus capability | ||
887 | * Parameters: slot | ||
888 | * Returns: bus is set (0) or error code | ||
889 | ***********************************************************/ | ||
890 | static int set_bus(struct slot * slot_cur) | ||
891 | { | ||
892 | int rc; | ||
893 | u8 speed; | ||
894 | u8 cmd = 0x0; | ||
895 | int retval; | ||
896 | static struct pci_device_id ciobx[] = { | ||
897 | { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, 0x0101) }, | ||
898 | { }, | ||
899 | }; | ||
900 | |||
901 | debug("%s - entry slot # %d\n", __FUNCTION__, slot_cur->number); | ||
902 | if (SET_BUS_STATUS(slot_cur->ctrl) && is_bus_empty(slot_cur)) { | ||
903 | rc = slot_update(&slot_cur); | ||
904 | if (rc) | ||
905 | return rc; | ||
906 | speed = SLOT_SPEED(slot_cur->ext_status); | ||
907 | debug("ext_status = %x, speed = %x\n", slot_cur->ext_status, speed); | ||
908 | switch (speed) { | ||
909 | case HPC_SLOT_SPEED_33: | ||
910 | cmd = HPC_BUS_33CONVMODE; | ||
911 | break; | ||
912 | case HPC_SLOT_SPEED_66: | ||
913 | if (SLOT_PCIX(slot_cur->ext_status)) { | ||
914 | if ((slot_cur->supported_speed >= BUS_SPEED_66) && | ||
915 | (slot_cur->supported_bus_mode == BUS_MODE_PCIX)) | ||
916 | cmd = HPC_BUS_66PCIXMODE; | ||
917 | else if (!SLOT_BUS_MODE(slot_cur->ext_status)) | ||
918 | /* if max slot/bus capability is 66 pci | ||
919 | and there's no bus mode mismatch, then | ||
920 | the adapter supports 66 pci */ | ||
921 | cmd = HPC_BUS_66CONVMODE; | ||
922 | else | ||
923 | cmd = HPC_BUS_33CONVMODE; | ||
924 | } else { | ||
925 | if (slot_cur->supported_speed >= BUS_SPEED_66) | ||
926 | cmd = HPC_BUS_66CONVMODE; | ||
927 | else | ||
928 | cmd = HPC_BUS_33CONVMODE; | ||
929 | } | ||
930 | break; | ||
931 | case HPC_SLOT_SPEED_133: | ||
932 | switch (slot_cur->supported_speed) { | ||
933 | case BUS_SPEED_33: | ||
934 | cmd = HPC_BUS_33CONVMODE; | ||
935 | break; | ||
936 | case BUS_SPEED_66: | ||
937 | if (slot_cur->supported_bus_mode == BUS_MODE_PCIX) | ||
938 | cmd = HPC_BUS_66PCIXMODE; | ||
939 | else | ||
940 | cmd = HPC_BUS_66CONVMODE; | ||
941 | break; | ||
942 | case BUS_SPEED_100: | ||
943 | cmd = HPC_BUS_100PCIXMODE; | ||
944 | break; | ||
945 | case BUS_SPEED_133: | ||
946 | /* This is to take care of the bug in CIOBX chip */ | ||
947 | if (pci_dev_present(ciobx)) | ||
948 | ibmphp_hpc_writeslot(slot_cur, | ||
949 | HPC_BUS_100PCIXMODE); | ||
950 | cmd = HPC_BUS_133PCIXMODE; | ||
951 | break; | ||
952 | default: | ||
953 | err("Wrong bus speed\n"); | ||
954 | return -ENODEV; | ||
955 | } | ||
956 | break; | ||
957 | default: | ||
958 | err("wrong slot speed\n"); | ||
959 | return -ENODEV; | ||
960 | } | ||
961 | debug("setting bus speed for slot %d, cmd %x\n", | ||
962 | slot_cur->number, cmd); | ||
963 | retval = ibmphp_hpc_writeslot(slot_cur, cmd); | ||
964 | if (retval) { | ||
965 | err("setting bus speed failed\n"); | ||
966 | return retval; | ||
967 | } | ||
968 | if (CTLR_RESULT(slot_cur->ctrl->status)) { | ||
969 | err("command not completed successfully in set_bus\n"); | ||
970 | return -EIO; | ||
971 | } | ||
972 | } | ||
973 | /* This is for x440, once Brandon fixes the firmware, | ||
974 | will not need this delay */ | ||
975 | msleep(1000); | ||
976 | debug("%s -Exit\n", __FUNCTION__); | ||
977 | return 0; | ||
978 | } | ||
979 | |||
980 | /* This routine checks the bus limitations that the slot is on from the BIOS. | ||
981 | * This is used in deciding whether or not to power up the slot. | ||
982 | * (electrical/spec limitations. For example, >1 133 MHz or >2 66 PCI cards on | ||
983 | * same bus) | ||
984 | * Parameters: slot | ||
985 | * Returns: 0 = no limitations, -EINVAL = exceeded limitations on the bus | ||
986 | */ | ||
987 | static int check_limitations(struct slot *slot_cur) | ||
988 | { | ||
989 | u8 i; | ||
990 | struct slot * tmp_slot; | ||
991 | u8 count = 0; | ||
992 | u8 limitation = 0; | ||
993 | |||
994 | for (i = slot_cur->bus_on->slot_min; i <= slot_cur->bus_on->slot_max; i++) { | ||
995 | tmp_slot = ibmphp_get_slot_from_physical_num(i); | ||
996 | if (!tmp_slot) | ||
997 | return -ENODEV; | ||
998 | if ((SLOT_PWRGD(tmp_slot->status)) && | ||
999 | !(SLOT_CONNECT(tmp_slot->status))) | ||
1000 | count++; | ||
1001 | } | ||
1002 | get_cur_bus_info(&slot_cur); | ||
1003 | switch (slot_cur->bus_on->current_speed) { | ||
1004 | case BUS_SPEED_33: | ||
1005 | limitation = slot_cur->bus_on->slots_at_33_conv; | ||
1006 | break; | ||
1007 | case BUS_SPEED_66: | ||
1008 | if (slot_cur->bus_on->current_bus_mode == BUS_MODE_PCIX) | ||
1009 | limitation = slot_cur->bus_on->slots_at_66_pcix; | ||
1010 | else | ||
1011 | limitation = slot_cur->bus_on->slots_at_66_conv; | ||
1012 | break; | ||
1013 | case BUS_SPEED_100: | ||
1014 | limitation = slot_cur->bus_on->slots_at_100_pcix; | ||
1015 | break; | ||
1016 | case BUS_SPEED_133: | ||
1017 | limitation = slot_cur->bus_on->slots_at_133_pcix; | ||
1018 | break; | ||
1019 | } | ||
1020 | |||
1021 | if ((count + 1) > limitation) | ||
1022 | return -EINVAL; | ||
1023 | return 0; | ||
1024 | } | ||
1025 | |||
1026 | static inline void print_card_capability(struct slot *slot_cur) | ||
1027 | { | ||
1028 | info("capability of the card is "); | ||
1029 | if ((slot_cur->ext_status & CARD_INFO) == PCIX133) | ||
1030 | info(" 133 MHz PCI-X\n"); | ||
1031 | else if ((slot_cur->ext_status & CARD_INFO) == PCIX66) | ||
1032 | info(" 66 MHz PCI-X\n"); | ||
1033 | else if ((slot_cur->ext_status & CARD_INFO) == PCI66) | ||
1034 | info(" 66 MHz PCI\n"); | ||
1035 | else | ||
1036 | info(" 33 MHz PCI\n"); | ||
1037 | |||
1038 | } | ||
1039 | |||
1040 | /* This routine will power on the slot, configure the device(s) and find the | ||
1041 | * drivers for them. | ||
1042 | * Parameters: hotplug_slot | ||
1043 | * Returns: 0 or failure codes | ||
1044 | */ | ||
1045 | static int enable_slot(struct hotplug_slot *hs) | ||
1046 | { | ||
1047 | int rc, i, rcpr; | ||
1048 | struct slot *slot_cur; | ||
1049 | u8 function; | ||
1050 | struct pci_func *tmp_func; | ||
1051 | |||
1052 | ibmphp_lock_operations(); | ||
1053 | |||
1054 | debug("ENABLING SLOT........\n"); | ||
1055 | slot_cur = hs->private; | ||
1056 | |||
1057 | if ((rc = validate(slot_cur, ENABLE))) { | ||
1058 | err("validate function failed\n"); | ||
1059 | goto error_nopower; | ||
1060 | } | ||
1061 | |||
1062 | attn_LED_blink(slot_cur); | ||
1063 | |||
1064 | rc = set_bus(slot_cur); | ||
1065 | if (rc) { | ||
1066 | err("was not able to set the bus\n"); | ||
1067 | goto error_nopower; | ||
1068 | } | ||
1069 | |||
1070 | /*-----------------debugging------------------------------*/ | ||
1071 | get_cur_bus_info(&slot_cur); | ||
1072 | debug("the current bus speed right after set_bus = %x\n", | ||
1073 | slot_cur->bus_on->current_speed); | ||
1074 | /*----------------------------------------------------------*/ | ||
1075 | |||
1076 | rc = check_limitations(slot_cur); | ||
1077 | if (rc) { | ||
1078 | err("Adding this card exceeds the limitations of this bus.\n"); | ||
1079 | err("(i.e., >1 133MHz cards running on same bus, or " | ||
1080 | ">2 66 PCI cards running on same bus\n."); | ||
1081 | err("Try hot-adding into another bus\n"); | ||
1082 | rc = -EINVAL; | ||
1083 | goto error_nopower; | ||
1084 | } | ||
1085 | |||
1086 | rc = power_on(slot_cur); | ||
1087 | |||
1088 | if (rc) { | ||
1089 | err("something wrong when powering up... please see below for details\n"); | ||
1090 | /* need to turn off before on, otherwise, blinking overwrites */ | ||
1091 | attn_off(slot_cur); | ||
1092 | attn_on(slot_cur); | ||
1093 | if (slot_update(&slot_cur)) { | ||
1094 | attn_off(slot_cur); | ||
1095 | attn_on(slot_cur); | ||
1096 | rc = -ENODEV; | ||
1097 | goto exit; | ||
1098 | } | ||
1099 | /* Check to see the error of why it failed */ | ||
1100 | if ((SLOT_POWER(slot_cur->status)) && | ||
1101 | !(SLOT_PWRGD(slot_cur->status))) | ||
1102 | err("power fault occurred trying to power up\n"); | ||
1103 | else if (SLOT_BUS_SPEED(slot_cur->status)) { | ||
1104 | err("bus speed mismatch occurred. please check " | ||
1105 | "current bus speed and card capability\n"); | ||
1106 | print_card_capability(slot_cur); | ||
1107 | } else if (SLOT_BUS_MODE(slot_cur->ext_status)) { | ||
1108 | err("bus mode mismatch occurred. please check " | ||
1109 | "current bus mode and card capability\n"); | ||
1110 | print_card_capability(slot_cur); | ||
1111 | } | ||
1112 | ibmphp_update_slot_info(slot_cur); | ||
1113 | goto exit; | ||
1114 | } | ||
1115 | debug("after power_on\n"); | ||
1116 | /*-----------------------debugging---------------------------*/ | ||
1117 | get_cur_bus_info(&slot_cur); | ||
1118 | debug("the current bus speed right after power_on = %x\n", | ||
1119 | slot_cur->bus_on->current_speed); | ||
1120 | /*----------------------------------------------------------*/ | ||
1121 | |||
1122 | rc = slot_update(&slot_cur); | ||
1123 | if (rc) | ||
1124 | goto error_power; | ||
1125 | |||
1126 | rc = -EINVAL; | ||
1127 | if (SLOT_POWER(slot_cur->status) && !(SLOT_PWRGD(slot_cur->status))) { | ||
1128 | err("power fault occurred trying to power up...\n"); | ||
1129 | goto error_power; | ||
1130 | } | ||
1131 | if (SLOT_POWER(slot_cur->status) && (SLOT_BUS_SPEED(slot_cur->status))) { | ||
1132 | err("bus speed mismatch occurred. please check current bus " | ||
1133 | "speed and card capability\n"); | ||
1134 | print_card_capability(slot_cur); | ||
1135 | goto error_power; | ||
1136 | } | ||
1137 | /* Don't think this case will happen after above checks... | ||
1138 | * but just in case, for paranoia sake */ | ||
1139 | if (!(SLOT_POWER(slot_cur->status))) { | ||
1140 | err("power on failed...\n"); | ||
1141 | goto error_power; | ||
1142 | } | ||
1143 | |||
1144 | slot_cur->func = kmalloc(sizeof(struct pci_func), GFP_KERNEL); | ||
1145 | if (!slot_cur->func) { | ||
1146 | /* We cannot do update_slot_info here, since no memory for | ||
1147 | * kmalloc n.e.ways, and update_slot_info allocates some */ | ||
1148 | err("out of system memory\n"); | ||
1149 | rc = -ENOMEM; | ||
1150 | goto error_power; | ||
1151 | } | ||
1152 | memset(slot_cur->func, 0, sizeof(struct pci_func)); | ||
1153 | slot_cur->func->busno = slot_cur->bus; | ||
1154 | slot_cur->func->device = slot_cur->device; | ||
1155 | for (i = 0; i < 4; i++) | ||
1156 | slot_cur->func->irq[i] = slot_cur->irq[i]; | ||
1157 | |||
1158 | debug("b4 configure_card, slot_cur->bus = %x, slot_cur->device = %x\n", | ||
1159 | slot_cur->bus, slot_cur->device); | ||
1160 | |||
1161 | if (ibmphp_configure_card(slot_cur->func, slot_cur->number)) { | ||
1162 | err("configure_card was unsuccessful...\n"); | ||
1163 | /* true because don't need to actually deallocate resources, | ||
1164 | * just remove references */ | ||
1165 | ibmphp_unconfigure_card(&slot_cur, 1); | ||
1166 | debug("after unconfigure_card\n"); | ||
1167 | slot_cur->func = NULL; | ||
1168 | rc = -ENOMEM; | ||
1169 | goto error_power; | ||
1170 | } | ||
1171 | |||
1172 | function = 0x00; | ||
1173 | do { | ||
1174 | tmp_func = ibm_slot_find(slot_cur->bus, slot_cur->func->device, | ||
1175 | function++); | ||
1176 | if (tmp_func && !(tmp_func->dev)) | ||
1177 | ibm_configure_device(tmp_func); | ||
1178 | } while (tmp_func); | ||
1179 | |||
1180 | attn_off(slot_cur); | ||
1181 | if (slot_update(&slot_cur)) { | ||
1182 | rc = -EFAULT; | ||
1183 | goto exit; | ||
1184 | } | ||
1185 | ibmphp_print_test(); | ||
1186 | rc = ibmphp_update_slot_info(slot_cur); | ||
1187 | exit: | ||
1188 | ibmphp_unlock_operations(); | ||
1189 | return rc; | ||
1190 | |||
1191 | error_nopower: | ||
1192 | attn_off(slot_cur); /* need to turn off if was blinking b4 */ | ||
1193 | attn_on(slot_cur); | ||
1194 | error_cont: | ||
1195 | rcpr = slot_update(&slot_cur); | ||
1196 | if (rcpr) { | ||
1197 | rc = rcpr; | ||
1198 | goto exit; | ||
1199 | } | ||
1200 | ibmphp_update_slot_info(slot_cur); | ||
1201 | goto exit; | ||
1202 | |||
1203 | error_power: | ||
1204 | attn_off(slot_cur); /* need to turn off if was blinking b4 */ | ||
1205 | attn_on(slot_cur); | ||
1206 | rcpr = power_off(slot_cur); | ||
1207 | if (rcpr) { | ||
1208 | rc = rcpr; | ||
1209 | goto exit; | ||
1210 | } | ||
1211 | goto error_cont; | ||
1212 | } | ||
1213 | |||
1214 | /************************************************************** | ||
1215 | * HOT REMOVING ADAPTER CARD * | ||
1216 | * INPUT: POINTER TO THE HOTPLUG SLOT STRUCTURE * | ||
1217 | * OUTPUT: SUCCESS 0 ; FAILURE: UNCONFIGURE , VALIDATE * | ||
1218 | DISABLE POWER , * | ||
1219 | **************************************************************/ | ||
1220 | static int ibmphp_disable_slot(struct hotplug_slot *hotplug_slot) | ||
1221 | { | ||
1222 | struct slot *slot = hotplug_slot->private; | ||
1223 | int rc; | ||
1224 | |||
1225 | ibmphp_lock_operations(); | ||
1226 | rc = ibmphp_do_disable_slot(slot); | ||
1227 | ibmphp_unlock_operations(); | ||
1228 | return rc; | ||
1229 | } | ||
1230 | |||
1231 | int ibmphp_do_disable_slot(struct slot *slot_cur) | ||
1232 | { | ||
1233 | int rc; | ||
1234 | u8 flag; | ||
1235 | |||
1236 | debug("DISABLING SLOT...\n"); | ||
1237 | |||
1238 | if ((slot_cur == NULL) || (slot_cur->ctrl == NULL)) { | ||
1239 | return -ENODEV; | ||
1240 | } | ||
1241 | |||
1242 | flag = slot_cur->flag; | ||
1243 | slot_cur->flag = TRUE; | ||
1244 | |||
1245 | if (flag == TRUE) { | ||
1246 | rc = validate(slot_cur, DISABLE); | ||
1247 | /* checking if powered off already & valid slot # */ | ||
1248 | if (rc) | ||
1249 | goto error; | ||
1250 | } | ||
1251 | attn_LED_blink(slot_cur); | ||
1252 | |||
1253 | if (slot_cur->func == NULL) { | ||
1254 | /* We need this for fncs's that were there on bootup */ | ||
1255 | slot_cur->func = kmalloc(sizeof(struct pci_func), GFP_KERNEL); | ||
1256 | if (!slot_cur->func) { | ||
1257 | err("out of system memory\n"); | ||
1258 | rc = -ENOMEM; | ||
1259 | goto error; | ||
1260 | } | ||
1261 | memset(slot_cur->func, 0, sizeof(struct pci_func)); | ||
1262 | slot_cur->func->busno = slot_cur->bus; | ||
1263 | slot_cur->func->device = slot_cur->device; | ||
1264 | } | ||
1265 | |||
1266 | ibm_unconfigure_device(slot_cur->func); | ||
1267 | |||
1268 | /* If we got here from latch suddenly opening on operating card or | ||
1269 | a power fault, there's no power to the card, so cannot | ||
1270 | read from it to determine what resources it occupied. This operation | ||
1271 | is forbidden anyhow. The best we can do is remove it from kernel | ||
1272 | lists at least */ | ||
1273 | |||
1274 | if (!flag) { | ||
1275 | attn_off(slot_cur); | ||
1276 | return 0; | ||
1277 | } | ||
1278 | |||
1279 | rc = ibmphp_unconfigure_card(&slot_cur, 0); | ||
1280 | slot_cur->func = NULL; | ||
1281 | debug("in disable_slot. after unconfigure_card\n"); | ||
1282 | if (rc) { | ||
1283 | err("could not unconfigure card.\n"); | ||
1284 | goto error; | ||
1285 | } | ||
1286 | |||
1287 | rc = ibmphp_hpc_writeslot(slot_cur, HPC_SLOT_OFF); | ||
1288 | if (rc) | ||
1289 | goto error; | ||
1290 | |||
1291 | attn_off(slot_cur); | ||
1292 | rc = slot_update(&slot_cur); | ||
1293 | if (rc) | ||
1294 | goto exit; | ||
1295 | |||
1296 | rc = ibmphp_update_slot_info(slot_cur); | ||
1297 | ibmphp_print_test(); | ||
1298 | exit: | ||
1299 | return rc; | ||
1300 | |||
1301 | error: | ||
1302 | /* Need to turn off if was blinking b4 */ | ||
1303 | attn_off(slot_cur); | ||
1304 | attn_on(slot_cur); | ||
1305 | if (slot_update(&slot_cur)) { | ||
1306 | rc = -EFAULT; | ||
1307 | goto exit; | ||
1308 | } | ||
1309 | if (flag) | ||
1310 | ibmphp_update_slot_info(slot_cur); | ||
1311 | goto exit; | ||
1312 | } | ||
1313 | |||
1314 | struct hotplug_slot_ops ibmphp_hotplug_slot_ops = { | ||
1315 | .owner = THIS_MODULE, | ||
1316 | .set_attention_status = set_attention_status, | ||
1317 | .enable_slot = enable_slot, | ||
1318 | .disable_slot = ibmphp_disable_slot, | ||
1319 | .hardware_test = NULL, | ||
1320 | .get_power_status = get_power_status, | ||
1321 | .get_attention_status = get_attention_status, | ||
1322 | .get_latch_status = get_latch_status, | ||
1323 | .get_adapter_status = get_adapter_present, | ||
1324 | .get_max_bus_speed = get_max_bus_speed, | ||
1325 | .get_cur_bus_speed = get_cur_bus_speed, | ||
1326 | /* .get_max_adapter_speed = get_max_adapter_speed, | ||
1327 | .get_bus_name_status = get_bus_name, | ||
1328 | */ | ||
1329 | }; | ||
1330 | |||
1331 | static void ibmphp_unload(void) | ||
1332 | { | ||
1333 | free_slots(); | ||
1334 | debug("after slots\n"); | ||
1335 | ibmphp_free_resources(); | ||
1336 | debug("after resources\n"); | ||
1337 | ibmphp_free_bus_info_queue(); | ||
1338 | debug("after bus info\n"); | ||
1339 | ibmphp_free_ebda_hpc_queue(); | ||
1340 | debug("after ebda hpc\n"); | ||
1341 | ibmphp_free_ebda_pci_rsrc_queue(); | ||
1342 | debug("after ebda pci rsrc\n"); | ||
1343 | kfree(ibmphp_pci_bus); | ||
1344 | } | ||
1345 | |||
1346 | static int __init ibmphp_init(void) | ||
1347 | { | ||
1348 | struct pci_bus *bus; | ||
1349 | int i = 0; | ||
1350 | int rc = 0; | ||
1351 | |||
1352 | init_flag = 1; | ||
1353 | |||
1354 | info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); | ||
1355 | |||
1356 | ibmphp_pci_bus = kmalloc(sizeof(*ibmphp_pci_bus), GFP_KERNEL); | ||
1357 | if (!ibmphp_pci_bus) { | ||
1358 | err("out of memory\n"); | ||
1359 | rc = -ENOMEM; | ||
1360 | goto exit; | ||
1361 | } | ||
1362 | |||
1363 | bus = pci_find_bus(0, 0); | ||
1364 | if (!bus) { | ||
1365 | err("Can't find the root pci bus, can not continue\n"); | ||
1366 | rc = -ENODEV; | ||
1367 | goto error; | ||
1368 | } | ||
1369 | memcpy(ibmphp_pci_bus, bus, sizeof(*ibmphp_pci_bus)); | ||
1370 | |||
1371 | ibmphp_debug = debug; | ||
1372 | |||
1373 | ibmphp_hpc_initvars(); | ||
1374 | |||
1375 | for (i = 0; i < 16; i++) | ||
1376 | irqs[i] = 0; | ||
1377 | |||
1378 | if ((rc = ibmphp_access_ebda())) | ||
1379 | goto error; | ||
1380 | debug("after ibmphp_access_ebda()\n"); | ||
1381 | |||
1382 | if ((rc = ibmphp_rsrc_init())) | ||
1383 | goto error; | ||
1384 | debug("AFTER Resource & EBDA INITIALIZATIONS\n"); | ||
1385 | |||
1386 | max_slots = get_max_slots(); | ||
1387 | |||
1388 | if ((rc = ibmphp_register_pci())) | ||
1389 | goto error; | ||
1390 | |||
1391 | if (init_ops()) { | ||
1392 | rc = -ENODEV; | ||
1393 | goto error; | ||
1394 | } | ||
1395 | |||
1396 | ibmphp_print_test(); | ||
1397 | if ((rc = ibmphp_hpc_start_poll_thread())) { | ||
1398 | goto error; | ||
1399 | } | ||
1400 | |||
1401 | /* lock ourselves into memory with a module | ||
1402 | * count of -1 so that no one can unload us. */ | ||
1403 | module_put(THIS_MODULE); | ||
1404 | |||
1405 | exit: | ||
1406 | return rc; | ||
1407 | |||
1408 | error: | ||
1409 | ibmphp_unload(); | ||
1410 | goto exit; | ||
1411 | } | ||
1412 | |||
1413 | static void __exit ibmphp_exit(void) | ||
1414 | { | ||
1415 | ibmphp_hpc_stop_poll_thread(); | ||
1416 | debug("after polling\n"); | ||
1417 | ibmphp_unload(); | ||
1418 | debug("done\n"); | ||
1419 | } | ||
1420 | |||
1421 | module_init(ibmphp_init); | ||
1422 | module_exit(ibmphp_exit); | ||
diff --git a/drivers/pci/hotplug/ibmphp_ebda.c b/drivers/pci/hotplug/ibmphp_ebda.c new file mode 100644 index 000000000000..aea1187c73ad --- /dev/null +++ b/drivers/pci/hotplug/ibmphp_ebda.c | |||
@@ -0,0 +1,1275 @@ | |||
1 | /* | ||
2 | * IBM Hot Plug Controller Driver | ||
3 | * | ||
4 | * Written By: Tong Yu, IBM Corporation | ||
5 | * | ||
6 | * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com) | ||
7 | * Copyright (C) 2001-2003 IBM Corp. | ||
8 | * | ||
9 | * All rights reserved. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or (at | ||
14 | * your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
19 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
20 | * details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
25 | * | ||
26 | * Send feedback to <gregkh@us.ibm.com> | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | #include <linux/module.h> | ||
31 | #include <linux/sched.h> | ||
32 | #include <linux/errno.h> | ||
33 | #include <linux/mm.h> | ||
34 | #include <linux/slab.h> | ||
35 | #include <linux/pci.h> | ||
36 | #include <linux/list.h> | ||
37 | #include <linux/init.h> | ||
38 | #include "ibmphp.h" | ||
39 | |||
40 | /* | ||
41 | * POST builds data blocks(in this data block definition, a char-1 | ||
42 | * byte, short(or word)-2 byte, long(dword)-4 byte) in the Extended | ||
43 | * BIOS Data Area which describe the configuration of the hot-plug | ||
44 | * controllers and resources used by the PCI Hot-Plug devices. | ||
45 | * | ||
46 | * This file walks EBDA, maps data block from physical addr, | ||
47 | * reconstruct linked lists about all system resource(MEM, PFM, IO) | ||
48 | * already assigned by POST, as well as linked lists about hot plug | ||
49 | * controllers (ctlr#, slot#, bus&slot features...) | ||
50 | */ | ||
51 | |||
52 | /* Global lists */ | ||
53 | LIST_HEAD (ibmphp_ebda_pci_rsrc_head); | ||
54 | LIST_HEAD (ibmphp_slot_head); | ||
55 | |||
56 | /* Local variables */ | ||
57 | static struct ebda_hpc_list *hpc_list_ptr; | ||
58 | static struct ebda_rsrc_list *rsrc_list_ptr; | ||
59 | static struct rio_table_hdr *rio_table_ptr = NULL; | ||
60 | static LIST_HEAD (ebda_hpc_head); | ||
61 | static LIST_HEAD (bus_info_head); | ||
62 | static LIST_HEAD (rio_vg_head); | ||
63 | static LIST_HEAD (rio_lo_head); | ||
64 | static LIST_HEAD (opt_vg_head); | ||
65 | static LIST_HEAD (opt_lo_head); | ||
66 | static void __iomem *io_mem; | ||
67 | |||
68 | /* Local functions */ | ||
69 | static int ebda_rsrc_controller (void); | ||
70 | static int ebda_rsrc_rsrc (void); | ||
71 | static int ebda_rio_table (void); | ||
72 | |||
73 | static struct ebda_hpc_list * __init alloc_ebda_hpc_list (void) | ||
74 | { | ||
75 | struct ebda_hpc_list *list; | ||
76 | |||
77 | list = kmalloc (sizeof (struct ebda_hpc_list), GFP_KERNEL); | ||
78 | if (!list) | ||
79 | return NULL; | ||
80 | memset (list, 0, sizeof (*list)); | ||
81 | return list; | ||
82 | } | ||
83 | |||
84 | static struct controller *alloc_ebda_hpc (u32 slot_count, u32 bus_count) | ||
85 | { | ||
86 | struct controller *controller; | ||
87 | struct ebda_hpc_slot *slots; | ||
88 | struct ebda_hpc_bus *buses; | ||
89 | |||
90 | controller = kmalloc (sizeof (struct controller), GFP_KERNEL); | ||
91 | if (!controller) | ||
92 | goto error; | ||
93 | memset (controller, 0, sizeof (*controller)); | ||
94 | |||
95 | slots = kmalloc (sizeof (struct ebda_hpc_slot) * slot_count, GFP_KERNEL); | ||
96 | if (!slots) | ||
97 | goto error_contr; | ||
98 | memset (slots, 0, sizeof (*slots) * slot_count); | ||
99 | controller->slots = slots; | ||
100 | |||
101 | buses = kmalloc (sizeof (struct ebda_hpc_bus) * bus_count, GFP_KERNEL); | ||
102 | if (!buses) | ||
103 | goto error_slots; | ||
104 | memset (buses, 0, sizeof (*buses) * bus_count); | ||
105 | controller->buses = buses; | ||
106 | |||
107 | return controller; | ||
108 | error_slots: | ||
109 | kfree(controller->slots); | ||
110 | error_contr: | ||
111 | kfree(controller); | ||
112 | error: | ||
113 | return NULL; | ||
114 | } | ||
115 | |||
116 | static void free_ebda_hpc (struct controller *controller) | ||
117 | { | ||
118 | kfree (controller->slots); | ||
119 | kfree (controller->buses); | ||
120 | kfree (controller); | ||
121 | } | ||
122 | |||
123 | static struct ebda_rsrc_list * __init alloc_ebda_rsrc_list (void) | ||
124 | { | ||
125 | struct ebda_rsrc_list *list; | ||
126 | |||
127 | list = kmalloc (sizeof (struct ebda_rsrc_list), GFP_KERNEL); | ||
128 | if (!list) | ||
129 | return NULL; | ||
130 | memset (list, 0, sizeof (*list)); | ||
131 | return list; | ||
132 | } | ||
133 | |||
134 | static struct ebda_pci_rsrc *alloc_ebda_pci_rsrc (void) | ||
135 | { | ||
136 | struct ebda_pci_rsrc *resource; | ||
137 | |||
138 | resource = kmalloc (sizeof (struct ebda_pci_rsrc), GFP_KERNEL); | ||
139 | if (!resource) | ||
140 | return NULL; | ||
141 | memset (resource, 0, sizeof (*resource)); | ||
142 | return resource; | ||
143 | } | ||
144 | |||
145 | static void __init print_bus_info (void) | ||
146 | { | ||
147 | struct bus_info *ptr; | ||
148 | struct list_head *ptr1; | ||
149 | |||
150 | list_for_each (ptr1, &bus_info_head) { | ||
151 | ptr = list_entry (ptr1, struct bus_info, bus_info_list); | ||
152 | debug ("%s - slot_min = %x\n", __FUNCTION__, ptr->slot_min); | ||
153 | debug ("%s - slot_max = %x\n", __FUNCTION__, ptr->slot_max); | ||
154 | debug ("%s - slot_count = %x\n", __FUNCTION__, ptr->slot_count); | ||
155 | debug ("%s - bus# = %x\n", __FUNCTION__, ptr->busno); | ||
156 | debug ("%s - current_speed = %x\n", __FUNCTION__, ptr->current_speed); | ||
157 | debug ("%s - controller_id = %x\n", __FUNCTION__, ptr->controller_id); | ||
158 | |||
159 | debug ("%s - slots_at_33_conv = %x\n", __FUNCTION__, ptr->slots_at_33_conv); | ||
160 | debug ("%s - slots_at_66_conv = %x\n", __FUNCTION__, ptr->slots_at_66_conv); | ||
161 | debug ("%s - slots_at_66_pcix = %x\n", __FUNCTION__, ptr->slots_at_66_pcix); | ||
162 | debug ("%s - slots_at_100_pcix = %x\n", __FUNCTION__, ptr->slots_at_100_pcix); | ||
163 | debug ("%s - slots_at_133_pcix = %x\n", __FUNCTION__, ptr->slots_at_133_pcix); | ||
164 | |||
165 | } | ||
166 | } | ||
167 | |||
168 | static void print_lo_info (void) | ||
169 | { | ||
170 | struct rio_detail *ptr; | ||
171 | struct list_head *ptr1; | ||
172 | debug ("print_lo_info ----\n"); | ||
173 | list_for_each (ptr1, &rio_lo_head) { | ||
174 | ptr = list_entry (ptr1, struct rio_detail, rio_detail_list); | ||
175 | debug ("%s - rio_node_id = %x\n", __FUNCTION__, ptr->rio_node_id); | ||
176 | debug ("%s - rio_type = %x\n", __FUNCTION__, ptr->rio_type); | ||
177 | debug ("%s - owner_id = %x\n", __FUNCTION__, ptr->owner_id); | ||
178 | debug ("%s - first_slot_num = %x\n", __FUNCTION__, ptr->first_slot_num); | ||
179 | debug ("%s - wpindex = %x\n", __FUNCTION__, ptr->wpindex); | ||
180 | debug ("%s - chassis_num = %x\n", __FUNCTION__, ptr->chassis_num); | ||
181 | |||
182 | } | ||
183 | } | ||
184 | |||
185 | static void print_vg_info (void) | ||
186 | { | ||
187 | struct rio_detail *ptr; | ||
188 | struct list_head *ptr1; | ||
189 | debug ("%s ---\n", __FUNCTION__); | ||
190 | list_for_each (ptr1, &rio_vg_head) { | ||
191 | ptr = list_entry (ptr1, struct rio_detail, rio_detail_list); | ||
192 | debug ("%s - rio_node_id = %x\n", __FUNCTION__, ptr->rio_node_id); | ||
193 | debug ("%s - rio_type = %x\n", __FUNCTION__, ptr->rio_type); | ||
194 | debug ("%s - owner_id = %x\n", __FUNCTION__, ptr->owner_id); | ||
195 | debug ("%s - first_slot_num = %x\n", __FUNCTION__, ptr->first_slot_num); | ||
196 | debug ("%s - wpindex = %x\n", __FUNCTION__, ptr->wpindex); | ||
197 | debug ("%s - chassis_num = %x\n", __FUNCTION__, ptr->chassis_num); | ||
198 | |||
199 | } | ||
200 | } | ||
201 | |||
202 | static void __init print_ebda_pci_rsrc (void) | ||
203 | { | ||
204 | struct ebda_pci_rsrc *ptr; | ||
205 | struct list_head *ptr1; | ||
206 | |||
207 | list_for_each (ptr1, &ibmphp_ebda_pci_rsrc_head) { | ||
208 | ptr = list_entry (ptr1, struct ebda_pci_rsrc, ebda_pci_rsrc_list); | ||
209 | debug ("%s - rsrc type: %x bus#: %x dev_func: %x start addr: %x end addr: %x\n", | ||
210 | __FUNCTION__, ptr->rsrc_type ,ptr->bus_num, ptr->dev_fun,ptr->start_addr, ptr->end_addr); | ||
211 | } | ||
212 | } | ||
213 | |||
214 | static void __init print_ibm_slot (void) | ||
215 | { | ||
216 | struct slot *ptr; | ||
217 | struct list_head *ptr1; | ||
218 | |||
219 | list_for_each (ptr1, &ibmphp_slot_head) { | ||
220 | ptr = list_entry (ptr1, struct slot, ibm_slot_list); | ||
221 | debug ("%s - slot_number: %x\n", __FUNCTION__, ptr->number); | ||
222 | } | ||
223 | } | ||
224 | |||
225 | static void __init print_opt_vg (void) | ||
226 | { | ||
227 | struct opt_rio *ptr; | ||
228 | struct list_head *ptr1; | ||
229 | debug ("%s ---\n", __FUNCTION__); | ||
230 | list_for_each (ptr1, &opt_vg_head) { | ||
231 | ptr = list_entry (ptr1, struct opt_rio, opt_rio_list); | ||
232 | debug ("%s - rio_type %x\n", __FUNCTION__, ptr->rio_type); | ||
233 | debug ("%s - chassis_num: %x\n", __FUNCTION__, ptr->chassis_num); | ||
234 | debug ("%s - first_slot_num: %x\n", __FUNCTION__, ptr->first_slot_num); | ||
235 | debug ("%s - middle_num: %x\n", __FUNCTION__, ptr->middle_num); | ||
236 | } | ||
237 | } | ||
238 | |||
239 | static void __init print_ebda_hpc (void) | ||
240 | { | ||
241 | struct controller *hpc_ptr; | ||
242 | struct list_head *ptr1; | ||
243 | u16 index; | ||
244 | |||
245 | list_for_each (ptr1, &ebda_hpc_head) { | ||
246 | |||
247 | hpc_ptr = list_entry (ptr1, struct controller, ebda_hpc_list); | ||
248 | |||
249 | for (index = 0; index < hpc_ptr->slot_count; index++) { | ||
250 | debug ("%s - physical slot#: %x\n", __FUNCTION__, hpc_ptr->slots[index].slot_num); | ||
251 | debug ("%s - pci bus# of the slot: %x\n", __FUNCTION__, hpc_ptr->slots[index].slot_bus_num); | ||
252 | debug ("%s - index into ctlr addr: %x\n", __FUNCTION__, hpc_ptr->slots[index].ctl_index); | ||
253 | debug ("%s - cap of the slot: %x\n", __FUNCTION__, hpc_ptr->slots[index].slot_cap); | ||
254 | } | ||
255 | |||
256 | for (index = 0; index < hpc_ptr->bus_count; index++) { | ||
257 | debug ("%s - bus# of each bus controlled by this ctlr: %x\n", __FUNCTION__, hpc_ptr->buses[index].bus_num); | ||
258 | } | ||
259 | |||
260 | debug ("%s - type of hpc: %x\n", __FUNCTION__, hpc_ptr->ctlr_type); | ||
261 | switch (hpc_ptr->ctlr_type) { | ||
262 | case 1: | ||
263 | debug ("%s - bus: %x\n", __FUNCTION__, hpc_ptr->u.pci_ctlr.bus); | ||
264 | debug ("%s - dev_fun: %x\n", __FUNCTION__, hpc_ptr->u.pci_ctlr.dev_fun); | ||
265 | debug ("%s - irq: %x\n", __FUNCTION__, hpc_ptr->irq); | ||
266 | break; | ||
267 | |||
268 | case 0: | ||
269 | debug ("%s - io_start: %x\n", __FUNCTION__, hpc_ptr->u.isa_ctlr.io_start); | ||
270 | debug ("%s - io_end: %x\n", __FUNCTION__, hpc_ptr->u.isa_ctlr.io_end); | ||
271 | debug ("%s - irq: %x\n", __FUNCTION__, hpc_ptr->irq); | ||
272 | break; | ||
273 | |||
274 | case 2: | ||
275 | case 4: | ||
276 | debug ("%s - wpegbbar: %lx\n", __FUNCTION__, hpc_ptr->u.wpeg_ctlr.wpegbbar); | ||
277 | debug ("%s - i2c_addr: %x\n", __FUNCTION__, hpc_ptr->u.wpeg_ctlr.i2c_addr); | ||
278 | debug ("%s - irq: %x\n", __FUNCTION__, hpc_ptr->irq); | ||
279 | break; | ||
280 | } | ||
281 | } | ||
282 | } | ||
283 | |||
284 | int __init ibmphp_access_ebda (void) | ||
285 | { | ||
286 | u8 format, num_ctlrs, rio_complete, hs_complete; | ||
287 | u16 ebda_seg, num_entries, next_offset, offset, blk_id, sub_addr, re, rc_id, re_id, base; | ||
288 | int rc = 0; | ||
289 | |||
290 | |||
291 | rio_complete = 0; | ||
292 | hs_complete = 0; | ||
293 | |||
294 | io_mem = ioremap ((0x40 << 4) + 0x0e, 2); | ||
295 | if (!io_mem ) | ||
296 | return -ENOMEM; | ||
297 | ebda_seg = readw (io_mem); | ||
298 | iounmap (io_mem); | ||
299 | debug ("returned ebda segment: %x\n", ebda_seg); | ||
300 | |||
301 | io_mem = ioremap (ebda_seg<<4, 65000); | ||
302 | if (!io_mem ) | ||
303 | return -ENOMEM; | ||
304 | next_offset = 0x180; | ||
305 | |||
306 | for (;;) { | ||
307 | offset = next_offset; | ||
308 | next_offset = readw (io_mem + offset); /* offset of next blk */ | ||
309 | |||
310 | offset += 2; | ||
311 | if (next_offset == 0) /* 0 indicate it's last blk */ | ||
312 | break; | ||
313 | blk_id = readw (io_mem + offset); /* this blk id */ | ||
314 | |||
315 | offset += 2; | ||
316 | /* check if it is hot swap block or rio block */ | ||
317 | if (blk_id != 0x4853 && blk_id != 0x4752) | ||
318 | continue; | ||
319 | /* found hs table */ | ||
320 | if (blk_id == 0x4853) { | ||
321 | debug ("now enter hot swap block---\n"); | ||
322 | debug ("hot blk id: %x\n", blk_id); | ||
323 | format = readb (io_mem + offset); | ||
324 | |||
325 | offset += 1; | ||
326 | if (format != 4) | ||
327 | goto error_nodev; | ||
328 | debug ("hot blk format: %x\n", format); | ||
329 | /* hot swap sub blk */ | ||
330 | base = offset; | ||
331 | |||
332 | sub_addr = base; | ||
333 | re = readw (io_mem + sub_addr); /* next sub blk */ | ||
334 | |||
335 | sub_addr += 2; | ||
336 | rc_id = readw (io_mem + sub_addr); /* sub blk id */ | ||
337 | |||
338 | sub_addr += 2; | ||
339 | if (rc_id != 0x5243) | ||
340 | goto error_nodev; | ||
341 | /* rc sub blk signature */ | ||
342 | num_ctlrs = readb (io_mem + sub_addr); | ||
343 | |||
344 | sub_addr += 1; | ||
345 | hpc_list_ptr = alloc_ebda_hpc_list (); | ||
346 | if (!hpc_list_ptr) { | ||
347 | rc = -ENOMEM; | ||
348 | goto out; | ||
349 | } | ||
350 | hpc_list_ptr->format = format; | ||
351 | hpc_list_ptr->num_ctlrs = num_ctlrs; | ||
352 | hpc_list_ptr->phys_addr = sub_addr; /* offset of RSRC_CONTROLLER blk */ | ||
353 | debug ("info about hpc descriptor---\n"); | ||
354 | debug ("hot blk format: %x\n", format); | ||
355 | debug ("num of controller: %x\n", num_ctlrs); | ||
356 | debug ("offset of hpc data structure enteries: %x\n ", sub_addr); | ||
357 | |||
358 | sub_addr = base + re; /* re sub blk */ | ||
359 | /* FIXME: rc is never used/checked */ | ||
360 | rc = readw (io_mem + sub_addr); /* next sub blk */ | ||
361 | |||
362 | sub_addr += 2; | ||
363 | re_id = readw (io_mem + sub_addr); /* sub blk id */ | ||
364 | |||
365 | sub_addr += 2; | ||
366 | if (re_id != 0x5245) | ||
367 | goto error_nodev; | ||
368 | |||
369 | /* signature of re */ | ||
370 | num_entries = readw (io_mem + sub_addr); | ||
371 | |||
372 | sub_addr += 2; /* offset of RSRC_ENTRIES blk */ | ||
373 | rsrc_list_ptr = alloc_ebda_rsrc_list (); | ||
374 | if (!rsrc_list_ptr ) { | ||
375 | rc = -ENOMEM; | ||
376 | goto out; | ||
377 | } | ||
378 | rsrc_list_ptr->format = format; | ||
379 | rsrc_list_ptr->num_entries = num_entries; | ||
380 | rsrc_list_ptr->phys_addr = sub_addr; | ||
381 | |||
382 | debug ("info about rsrc descriptor---\n"); | ||
383 | debug ("format: %x\n", format); | ||
384 | debug ("num of rsrc: %x\n", num_entries); | ||
385 | debug ("offset of rsrc data structure enteries: %x\n ", sub_addr); | ||
386 | |||
387 | hs_complete = 1; | ||
388 | } else { | ||
389 | /* found rio table, blk_id == 0x4752 */ | ||
390 | debug ("now enter io table ---\n"); | ||
391 | debug ("rio blk id: %x\n", blk_id); | ||
392 | |||
393 | rio_table_ptr = kmalloc (sizeof (struct rio_table_hdr), GFP_KERNEL); | ||
394 | if (!rio_table_ptr) | ||
395 | return -ENOMEM; | ||
396 | memset (rio_table_ptr, 0, sizeof (struct rio_table_hdr) ); | ||
397 | rio_table_ptr->ver_num = readb (io_mem + offset); | ||
398 | rio_table_ptr->scal_count = readb (io_mem + offset + 1); | ||
399 | rio_table_ptr->riodev_count = readb (io_mem + offset + 2); | ||
400 | rio_table_ptr->offset = offset +3 ; | ||
401 | |||
402 | debug("info about rio table hdr ---\n"); | ||
403 | debug("ver_num: %x\nscal_count: %x\nriodev_count: %x\noffset of rio table: %x\n ", | ||
404 | rio_table_ptr->ver_num, rio_table_ptr->scal_count, | ||
405 | rio_table_ptr->riodev_count, rio_table_ptr->offset); | ||
406 | |||
407 | rio_complete = 1; | ||
408 | } | ||
409 | } | ||
410 | |||
411 | if (!hs_complete && !rio_complete) | ||
412 | goto error_nodev; | ||
413 | |||
414 | if (rio_table_ptr) { | ||
415 | if (rio_complete && rio_table_ptr->ver_num == 3) { | ||
416 | rc = ebda_rio_table (); | ||
417 | if (rc) | ||
418 | goto out; | ||
419 | } | ||
420 | } | ||
421 | rc = ebda_rsrc_controller (); | ||
422 | if (rc) | ||
423 | goto out; | ||
424 | |||
425 | rc = ebda_rsrc_rsrc (); | ||
426 | goto out; | ||
427 | error_nodev: | ||
428 | rc = -ENODEV; | ||
429 | out: | ||
430 | iounmap (io_mem); | ||
431 | return rc; | ||
432 | } | ||
433 | |||
434 | /* | ||
435 | * map info of scalability details and rio details from physical address | ||
436 | */ | ||
437 | static int __init ebda_rio_table (void) | ||
438 | { | ||
439 | u16 offset; | ||
440 | u8 i; | ||
441 | struct rio_detail *rio_detail_ptr; | ||
442 | |||
443 | offset = rio_table_ptr->offset; | ||
444 | offset += 12 * rio_table_ptr->scal_count; | ||
445 | |||
446 | // we do concern about rio details | ||
447 | for (i = 0; i < rio_table_ptr->riodev_count; i++) { | ||
448 | rio_detail_ptr = kmalloc (sizeof (struct rio_detail), GFP_KERNEL); | ||
449 | if (!rio_detail_ptr) | ||
450 | return -ENOMEM; | ||
451 | memset (rio_detail_ptr, 0, sizeof (struct rio_detail)); | ||
452 | rio_detail_ptr->rio_node_id = readb (io_mem + offset); | ||
453 | rio_detail_ptr->bbar = readl (io_mem + offset + 1); | ||
454 | rio_detail_ptr->rio_type = readb (io_mem + offset + 5); | ||
455 | rio_detail_ptr->owner_id = readb (io_mem + offset + 6); | ||
456 | rio_detail_ptr->port0_node_connect = readb (io_mem + offset + 7); | ||
457 | rio_detail_ptr->port0_port_connect = readb (io_mem + offset + 8); | ||
458 | rio_detail_ptr->port1_node_connect = readb (io_mem + offset + 9); | ||
459 | rio_detail_ptr->port1_port_connect = readb (io_mem + offset + 10); | ||
460 | rio_detail_ptr->first_slot_num = readb (io_mem + offset + 11); | ||
461 | rio_detail_ptr->status = readb (io_mem + offset + 12); | ||
462 | rio_detail_ptr->wpindex = readb (io_mem + offset + 13); | ||
463 | rio_detail_ptr->chassis_num = readb (io_mem + offset + 14); | ||
464 | // debug ("rio_node_id: %x\nbbar: %x\nrio_type: %x\nowner_id: %x\nport0_node: %x\nport0_port: %x\nport1_node: %x\nport1_port: %x\nfirst_slot_num: %x\nstatus: %x\n", rio_detail_ptr->rio_node_id, rio_detail_ptr->bbar, rio_detail_ptr->rio_type, rio_detail_ptr->owner_id, rio_detail_ptr->port0_node_connect, rio_detail_ptr->port0_port_connect, rio_detail_ptr->port1_node_connect, rio_detail_ptr->port1_port_connect, rio_detail_ptr->first_slot_num, rio_detail_ptr->status); | ||
465 | //create linked list of chassis | ||
466 | if (rio_detail_ptr->rio_type == 4 || rio_detail_ptr->rio_type == 5) | ||
467 | list_add (&rio_detail_ptr->rio_detail_list, &rio_vg_head); | ||
468 | //create linked list of expansion box | ||
469 | else if (rio_detail_ptr->rio_type == 6 || rio_detail_ptr->rio_type == 7) | ||
470 | list_add (&rio_detail_ptr->rio_detail_list, &rio_lo_head); | ||
471 | else | ||
472 | // not in my concern | ||
473 | kfree (rio_detail_ptr); | ||
474 | offset += 15; | ||
475 | } | ||
476 | print_lo_info (); | ||
477 | print_vg_info (); | ||
478 | return 0; | ||
479 | } | ||
480 | |||
481 | /* | ||
482 | * reorganizing linked list of chassis | ||
483 | */ | ||
484 | static struct opt_rio *search_opt_vg (u8 chassis_num) | ||
485 | { | ||
486 | struct opt_rio *ptr; | ||
487 | struct list_head *ptr1; | ||
488 | list_for_each (ptr1, &opt_vg_head) { | ||
489 | ptr = list_entry (ptr1, struct opt_rio, opt_rio_list); | ||
490 | if (ptr->chassis_num == chassis_num) | ||
491 | return ptr; | ||
492 | } | ||
493 | return NULL; | ||
494 | } | ||
495 | |||
496 | static int __init combine_wpg_for_chassis (void) | ||
497 | { | ||
498 | struct opt_rio *opt_rio_ptr = NULL; | ||
499 | struct rio_detail *rio_detail_ptr = NULL; | ||
500 | struct list_head *list_head_ptr = NULL; | ||
501 | |||
502 | list_for_each (list_head_ptr, &rio_vg_head) { | ||
503 | rio_detail_ptr = list_entry (list_head_ptr, struct rio_detail, rio_detail_list); | ||
504 | opt_rio_ptr = search_opt_vg (rio_detail_ptr->chassis_num); | ||
505 | if (!opt_rio_ptr) { | ||
506 | opt_rio_ptr = (struct opt_rio *) kmalloc (sizeof (struct opt_rio), GFP_KERNEL); | ||
507 | if (!opt_rio_ptr) | ||
508 | return -ENOMEM; | ||
509 | memset (opt_rio_ptr, 0, sizeof (struct opt_rio)); | ||
510 | opt_rio_ptr->rio_type = rio_detail_ptr->rio_type; | ||
511 | opt_rio_ptr->chassis_num = rio_detail_ptr->chassis_num; | ||
512 | opt_rio_ptr->first_slot_num = rio_detail_ptr->first_slot_num; | ||
513 | opt_rio_ptr->middle_num = rio_detail_ptr->first_slot_num; | ||
514 | list_add (&opt_rio_ptr->opt_rio_list, &opt_vg_head); | ||
515 | } else { | ||
516 | opt_rio_ptr->first_slot_num = min (opt_rio_ptr->first_slot_num, rio_detail_ptr->first_slot_num); | ||
517 | opt_rio_ptr->middle_num = max (opt_rio_ptr->middle_num, rio_detail_ptr->first_slot_num); | ||
518 | } | ||
519 | } | ||
520 | print_opt_vg (); | ||
521 | return 0; | ||
522 | } | ||
523 | |||
524 | /* | ||
525 | * reorgnizing linked list of expansion box | ||
526 | */ | ||
527 | static struct opt_rio_lo *search_opt_lo (u8 chassis_num) | ||
528 | { | ||
529 | struct opt_rio_lo *ptr; | ||
530 | struct list_head *ptr1; | ||
531 | list_for_each (ptr1, &opt_lo_head) { | ||
532 | ptr = list_entry (ptr1, struct opt_rio_lo, opt_rio_lo_list); | ||
533 | if (ptr->chassis_num == chassis_num) | ||
534 | return ptr; | ||
535 | } | ||
536 | return NULL; | ||
537 | } | ||
538 | |||
539 | static int combine_wpg_for_expansion (void) | ||
540 | { | ||
541 | struct opt_rio_lo *opt_rio_lo_ptr = NULL; | ||
542 | struct rio_detail *rio_detail_ptr = NULL; | ||
543 | struct list_head *list_head_ptr = NULL; | ||
544 | |||
545 | list_for_each (list_head_ptr, &rio_lo_head) { | ||
546 | rio_detail_ptr = list_entry (list_head_ptr, struct rio_detail, rio_detail_list); | ||
547 | opt_rio_lo_ptr = search_opt_lo (rio_detail_ptr->chassis_num); | ||
548 | if (!opt_rio_lo_ptr) { | ||
549 | opt_rio_lo_ptr = (struct opt_rio_lo *) kmalloc (sizeof (struct opt_rio_lo), GFP_KERNEL); | ||
550 | if (!opt_rio_lo_ptr) | ||
551 | return -ENOMEM; | ||
552 | memset (opt_rio_lo_ptr, 0, sizeof (struct opt_rio_lo)); | ||
553 | opt_rio_lo_ptr->rio_type = rio_detail_ptr->rio_type; | ||
554 | opt_rio_lo_ptr->chassis_num = rio_detail_ptr->chassis_num; | ||
555 | opt_rio_lo_ptr->first_slot_num = rio_detail_ptr->first_slot_num; | ||
556 | opt_rio_lo_ptr->middle_num = rio_detail_ptr->first_slot_num; | ||
557 | opt_rio_lo_ptr->pack_count = 1; | ||
558 | |||
559 | list_add (&opt_rio_lo_ptr->opt_rio_lo_list, &opt_lo_head); | ||
560 | } else { | ||
561 | opt_rio_lo_ptr->first_slot_num = min (opt_rio_lo_ptr->first_slot_num, rio_detail_ptr->first_slot_num); | ||
562 | opt_rio_lo_ptr->middle_num = max (opt_rio_lo_ptr->middle_num, rio_detail_ptr->first_slot_num); | ||
563 | opt_rio_lo_ptr->pack_count = 2; | ||
564 | } | ||
565 | } | ||
566 | return 0; | ||
567 | } | ||
568 | |||
569 | |||
570 | /* Since we don't know the max slot number per each chassis, hence go | ||
571 | * through the list of all chassis to find out the range | ||
572 | * Arguments: slot_num, 1st slot number of the chassis we think we are on, | ||
573 | * var (0 = chassis, 1 = expansion box) | ||
574 | */ | ||
575 | static int first_slot_num (u8 slot_num, u8 first_slot, u8 var) | ||
576 | { | ||
577 | struct opt_rio *opt_vg_ptr = NULL; | ||
578 | struct opt_rio_lo *opt_lo_ptr = NULL; | ||
579 | struct list_head *ptr = NULL; | ||
580 | int rc = 0; | ||
581 | |||
582 | if (!var) { | ||
583 | list_for_each (ptr, &opt_vg_head) { | ||
584 | opt_vg_ptr = list_entry (ptr, struct opt_rio, opt_rio_list); | ||
585 | if ((first_slot < opt_vg_ptr->first_slot_num) && (slot_num >= opt_vg_ptr->first_slot_num)) { | ||
586 | rc = -ENODEV; | ||
587 | break; | ||
588 | } | ||
589 | } | ||
590 | } else { | ||
591 | list_for_each (ptr, &opt_lo_head) { | ||
592 | opt_lo_ptr = list_entry (ptr, struct opt_rio_lo, opt_rio_lo_list); | ||
593 | if ((first_slot < opt_lo_ptr->first_slot_num) && (slot_num >= opt_lo_ptr->first_slot_num)) { | ||
594 | rc = -ENODEV; | ||
595 | break; | ||
596 | } | ||
597 | } | ||
598 | } | ||
599 | return rc; | ||
600 | } | ||
601 | |||
602 | static struct opt_rio_lo * find_rxe_num (u8 slot_num) | ||
603 | { | ||
604 | struct opt_rio_lo *opt_lo_ptr; | ||
605 | struct list_head *ptr; | ||
606 | |||
607 | list_for_each (ptr, &opt_lo_head) { | ||
608 | opt_lo_ptr = list_entry (ptr, struct opt_rio_lo, opt_rio_lo_list); | ||
609 | //check to see if this slot_num belongs to expansion box | ||
610 | if ((slot_num >= opt_lo_ptr->first_slot_num) && (!first_slot_num (slot_num, opt_lo_ptr->first_slot_num, 1))) | ||
611 | return opt_lo_ptr; | ||
612 | } | ||
613 | return NULL; | ||
614 | } | ||
615 | |||
616 | static struct opt_rio * find_chassis_num (u8 slot_num) | ||
617 | { | ||
618 | struct opt_rio *opt_vg_ptr; | ||
619 | struct list_head *ptr; | ||
620 | |||
621 | list_for_each (ptr, &opt_vg_head) { | ||
622 | opt_vg_ptr = list_entry (ptr, struct opt_rio, opt_rio_list); | ||
623 | //check to see if this slot_num belongs to chassis | ||
624 | if ((slot_num >= opt_vg_ptr->first_slot_num) && (!first_slot_num (slot_num, opt_vg_ptr->first_slot_num, 0))) | ||
625 | return opt_vg_ptr; | ||
626 | } | ||
627 | return NULL; | ||
628 | } | ||
629 | |||
630 | /* This routine will find out how many slots are in the chassis, so that | ||
631 | * the slot numbers for rxe100 would start from 1, and not from 7, or 6 etc | ||
632 | */ | ||
633 | static u8 calculate_first_slot (u8 slot_num) | ||
634 | { | ||
635 | u8 first_slot = 1; | ||
636 | struct list_head * list; | ||
637 | struct slot * slot_cur; | ||
638 | |||
639 | list_for_each (list, &ibmphp_slot_head) { | ||
640 | slot_cur = list_entry (list, struct slot, ibm_slot_list); | ||
641 | if (slot_cur->ctrl) { | ||
642 | if ((slot_cur->ctrl->ctlr_type != 4) && (slot_cur->ctrl->ending_slot_num > first_slot) && (slot_num > slot_cur->ctrl->ending_slot_num)) | ||
643 | first_slot = slot_cur->ctrl->ending_slot_num; | ||
644 | } | ||
645 | } | ||
646 | return first_slot + 1; | ||
647 | |||
648 | } | ||
649 | static char *create_file_name (struct slot * slot_cur) | ||
650 | { | ||
651 | struct opt_rio *opt_vg_ptr = NULL; | ||
652 | struct opt_rio_lo *opt_lo_ptr = NULL; | ||
653 | static char str[30]; | ||
654 | int which = 0; /* rxe = 1, chassis = 0 */ | ||
655 | u8 number = 1; /* either chassis or rxe # */ | ||
656 | u8 first_slot = 1; | ||
657 | u8 slot_num; | ||
658 | u8 flag = 0; | ||
659 | |||
660 | if (!slot_cur) { | ||
661 | err ("Structure passed is empty\n"); | ||
662 | return NULL; | ||
663 | } | ||
664 | |||
665 | slot_num = slot_cur->number; | ||
666 | |||
667 | memset (str, 0, sizeof(str)); | ||
668 | |||
669 | if (rio_table_ptr) { | ||
670 | if (rio_table_ptr->ver_num == 3) { | ||
671 | opt_vg_ptr = find_chassis_num (slot_num); | ||
672 | opt_lo_ptr = find_rxe_num (slot_num); | ||
673 | } | ||
674 | } | ||
675 | if (opt_vg_ptr) { | ||
676 | if (opt_lo_ptr) { | ||
677 | if ((slot_num - opt_vg_ptr->first_slot_num) > (slot_num - opt_lo_ptr->first_slot_num)) { | ||
678 | number = opt_lo_ptr->chassis_num; | ||
679 | first_slot = opt_lo_ptr->first_slot_num; | ||
680 | which = 1; /* it is RXE */ | ||
681 | } else { | ||
682 | first_slot = opt_vg_ptr->first_slot_num; | ||
683 | number = opt_vg_ptr->chassis_num; | ||
684 | which = 0; | ||
685 | } | ||
686 | } else { | ||
687 | first_slot = opt_vg_ptr->first_slot_num; | ||
688 | number = opt_vg_ptr->chassis_num; | ||
689 | which = 0; | ||
690 | } | ||
691 | ++flag; | ||
692 | } else if (opt_lo_ptr) { | ||
693 | number = opt_lo_ptr->chassis_num; | ||
694 | first_slot = opt_lo_ptr->first_slot_num; | ||
695 | which = 1; | ||
696 | ++flag; | ||
697 | } else if (rio_table_ptr) { | ||
698 | if (rio_table_ptr->ver_num == 3) { | ||
699 | /* if both NULL and we DO have correct RIO table in BIOS */ | ||
700 | return NULL; | ||
701 | } | ||
702 | } | ||
703 | if (!flag) { | ||
704 | if (slot_cur->ctrl->ctlr_type == 4) { | ||
705 | first_slot = calculate_first_slot (slot_num); | ||
706 | which = 1; | ||
707 | } else { | ||
708 | which = 0; | ||
709 | } | ||
710 | } | ||
711 | |||
712 | sprintf(str, "%s%dslot%d", | ||
713 | which == 0 ? "chassis" : "rxe", | ||
714 | number, slot_num - first_slot + 1); | ||
715 | return str; | ||
716 | } | ||
717 | |||
718 | static int fillslotinfo(struct hotplug_slot *hotplug_slot) | ||
719 | { | ||
720 | struct slot *slot; | ||
721 | int rc = 0; | ||
722 | |||
723 | if (!hotplug_slot || !hotplug_slot->private) | ||
724 | return -EINVAL; | ||
725 | |||
726 | slot = hotplug_slot->private; | ||
727 | rc = ibmphp_hpc_readslot(slot, READ_ALLSTAT, NULL); | ||
728 | if (rc) | ||
729 | return rc; | ||
730 | |||
731 | // power - enabled:1 not:0 | ||
732 | hotplug_slot->info->power_status = SLOT_POWER(slot->status); | ||
733 | |||
734 | // attention - off:0, on:1, blinking:2 | ||
735 | hotplug_slot->info->attention_status = SLOT_ATTN(slot->status, slot->ext_status); | ||
736 | |||
737 | // latch - open:1 closed:0 | ||
738 | hotplug_slot->info->latch_status = SLOT_LATCH(slot->status); | ||
739 | |||
740 | // pci board - present:1 not:0 | ||
741 | if (SLOT_PRESENT (slot->status)) | ||
742 | hotplug_slot->info->adapter_status = 1; | ||
743 | else | ||
744 | hotplug_slot->info->adapter_status = 0; | ||
745 | /* | ||
746 | if (slot->bus_on->supported_bus_mode | ||
747 | && (slot->bus_on->supported_speed == BUS_SPEED_66)) | ||
748 | hotplug_slot->info->max_bus_speed_status = BUS_SPEED_66PCIX; | ||
749 | else | ||
750 | hotplug_slot->info->max_bus_speed_status = slot->bus_on->supported_speed; | ||
751 | */ | ||
752 | |||
753 | return rc; | ||
754 | } | ||
755 | |||
756 | static void release_slot(struct hotplug_slot *hotplug_slot) | ||
757 | { | ||
758 | struct slot *slot; | ||
759 | |||
760 | if (!hotplug_slot || !hotplug_slot->private) | ||
761 | return; | ||
762 | |||
763 | slot = hotplug_slot->private; | ||
764 | kfree(slot->hotplug_slot->info); | ||
765 | kfree(slot->hotplug_slot->name); | ||
766 | kfree(slot->hotplug_slot); | ||
767 | slot->ctrl = NULL; | ||
768 | slot->bus_on = NULL; | ||
769 | |||
770 | /* we don't want to actually remove the resources, since free_resources will do just that */ | ||
771 | ibmphp_unconfigure_card(&slot, -1); | ||
772 | |||
773 | kfree (slot); | ||
774 | } | ||
775 | |||
776 | static struct pci_driver ibmphp_driver; | ||
777 | |||
778 | /* | ||
779 | * map info (ctlr-id, slot count, slot#.. bus count, bus#, ctlr type...) of | ||
780 | * each hpc from physical address to a list of hot plug controllers based on | ||
781 | * hpc descriptors. | ||
782 | */ | ||
783 | static int __init ebda_rsrc_controller (void) | ||
784 | { | ||
785 | u16 addr, addr_slot, addr_bus; | ||
786 | u8 ctlr_id, temp, bus_index; | ||
787 | u16 ctlr, slot, bus; | ||
788 | u16 slot_num, bus_num, index; | ||
789 | struct hotplug_slot *hp_slot_ptr; | ||
790 | struct controller *hpc_ptr; | ||
791 | struct ebda_hpc_bus *bus_ptr; | ||
792 | struct ebda_hpc_slot *slot_ptr; | ||
793 | struct bus_info *bus_info_ptr1, *bus_info_ptr2; | ||
794 | int rc; | ||
795 | struct slot *tmp_slot; | ||
796 | struct list_head *list; | ||
797 | |||
798 | addr = hpc_list_ptr->phys_addr; | ||
799 | for (ctlr = 0; ctlr < hpc_list_ptr->num_ctlrs; ctlr++) { | ||
800 | bus_index = 1; | ||
801 | ctlr_id = readb (io_mem + addr); | ||
802 | addr += 1; | ||
803 | slot_num = readb (io_mem + addr); | ||
804 | |||
805 | addr += 1; | ||
806 | addr_slot = addr; /* offset of slot structure */ | ||
807 | addr += (slot_num * 4); | ||
808 | |||
809 | bus_num = readb (io_mem + addr); | ||
810 | |||
811 | addr += 1; | ||
812 | addr_bus = addr; /* offset of bus */ | ||
813 | addr += (bus_num * 9); /* offset of ctlr_type */ | ||
814 | temp = readb (io_mem + addr); | ||
815 | |||
816 | addr += 1; | ||
817 | /* init hpc structure */ | ||
818 | hpc_ptr = alloc_ebda_hpc (slot_num, bus_num); | ||
819 | if (!hpc_ptr ) { | ||
820 | rc = -ENOMEM; | ||
821 | goto error_no_hpc; | ||
822 | } | ||
823 | hpc_ptr->ctlr_id = ctlr_id; | ||
824 | hpc_ptr->ctlr_relative_id = ctlr; | ||
825 | hpc_ptr->slot_count = slot_num; | ||
826 | hpc_ptr->bus_count = bus_num; | ||
827 | debug ("now enter ctlr data struture ---\n"); | ||
828 | debug ("ctlr id: %x\n", ctlr_id); | ||
829 | debug ("ctlr_relative_id: %x\n", hpc_ptr->ctlr_relative_id); | ||
830 | debug ("count of slots controlled by this ctlr: %x\n", slot_num); | ||
831 | debug ("count of buses controlled by this ctlr: %x\n", bus_num); | ||
832 | |||
833 | /* init slot structure, fetch slot, bus, cap... */ | ||
834 | slot_ptr = hpc_ptr->slots; | ||
835 | for (slot = 0; slot < slot_num; slot++) { | ||
836 | slot_ptr->slot_num = readb (io_mem + addr_slot); | ||
837 | slot_ptr->slot_bus_num = readb (io_mem + addr_slot + slot_num); | ||
838 | slot_ptr->ctl_index = readb (io_mem + addr_slot + 2*slot_num); | ||
839 | slot_ptr->slot_cap = readb (io_mem + addr_slot + 3*slot_num); | ||
840 | |||
841 | // create bus_info lined list --- if only one slot per bus: slot_min = slot_max | ||
842 | |||
843 | bus_info_ptr2 = ibmphp_find_same_bus_num (slot_ptr->slot_bus_num); | ||
844 | if (!bus_info_ptr2) { | ||
845 | bus_info_ptr1 = (struct bus_info *) kmalloc (sizeof (struct bus_info), GFP_KERNEL); | ||
846 | if (!bus_info_ptr1) { | ||
847 | rc = -ENOMEM; | ||
848 | goto error_no_hp_slot; | ||
849 | } | ||
850 | memset (bus_info_ptr1, 0, sizeof (struct bus_info)); | ||
851 | bus_info_ptr1->slot_min = slot_ptr->slot_num; | ||
852 | bus_info_ptr1->slot_max = slot_ptr->slot_num; | ||
853 | bus_info_ptr1->slot_count += 1; | ||
854 | bus_info_ptr1->busno = slot_ptr->slot_bus_num; | ||
855 | bus_info_ptr1->index = bus_index++; | ||
856 | bus_info_ptr1->current_speed = 0xff; | ||
857 | bus_info_ptr1->current_bus_mode = 0xff; | ||
858 | |||
859 | bus_info_ptr1->controller_id = hpc_ptr->ctlr_id; | ||
860 | |||
861 | list_add_tail (&bus_info_ptr1->bus_info_list, &bus_info_head); | ||
862 | |||
863 | } else { | ||
864 | bus_info_ptr2->slot_min = min (bus_info_ptr2->slot_min, slot_ptr->slot_num); | ||
865 | bus_info_ptr2->slot_max = max (bus_info_ptr2->slot_max, slot_ptr->slot_num); | ||
866 | bus_info_ptr2->slot_count += 1; | ||
867 | |||
868 | } | ||
869 | |||
870 | // end of creating the bus_info linked list | ||
871 | |||
872 | slot_ptr++; | ||
873 | addr_slot += 1; | ||
874 | } | ||
875 | |||
876 | /* init bus structure */ | ||
877 | bus_ptr = hpc_ptr->buses; | ||
878 | for (bus = 0; bus < bus_num; bus++) { | ||
879 | bus_ptr->bus_num = readb (io_mem + addr_bus + bus); | ||
880 | bus_ptr->slots_at_33_conv = readb (io_mem + addr_bus + bus_num + 8 * bus); | ||
881 | bus_ptr->slots_at_66_conv = readb (io_mem + addr_bus + bus_num + 8 * bus + 1); | ||
882 | |||
883 | bus_ptr->slots_at_66_pcix = readb (io_mem + addr_bus + bus_num + 8 * bus + 2); | ||
884 | |||
885 | bus_ptr->slots_at_100_pcix = readb (io_mem + addr_bus + bus_num + 8 * bus + 3); | ||
886 | |||
887 | bus_ptr->slots_at_133_pcix = readb (io_mem + addr_bus + bus_num + 8 * bus + 4); | ||
888 | |||
889 | bus_info_ptr2 = ibmphp_find_same_bus_num (bus_ptr->bus_num); | ||
890 | if (bus_info_ptr2) { | ||
891 | bus_info_ptr2->slots_at_33_conv = bus_ptr->slots_at_33_conv; | ||
892 | bus_info_ptr2->slots_at_66_conv = bus_ptr->slots_at_66_conv; | ||
893 | bus_info_ptr2->slots_at_66_pcix = bus_ptr->slots_at_66_pcix; | ||
894 | bus_info_ptr2->slots_at_100_pcix = bus_ptr->slots_at_100_pcix; | ||
895 | bus_info_ptr2->slots_at_133_pcix = bus_ptr->slots_at_133_pcix; | ||
896 | } | ||
897 | bus_ptr++; | ||
898 | } | ||
899 | |||
900 | hpc_ptr->ctlr_type = temp; | ||
901 | |||
902 | switch (hpc_ptr->ctlr_type) { | ||
903 | case 1: | ||
904 | hpc_ptr->u.pci_ctlr.bus = readb (io_mem + addr); | ||
905 | hpc_ptr->u.pci_ctlr.dev_fun = readb (io_mem + addr + 1); | ||
906 | hpc_ptr->irq = readb (io_mem + addr + 2); | ||
907 | addr += 3; | ||
908 | debug ("ctrl bus = %x, ctlr devfun = %x, irq = %x\n", | ||
909 | hpc_ptr->u.pci_ctlr.bus, | ||
910 | hpc_ptr->u.pci_ctlr.dev_fun, hpc_ptr->irq); | ||
911 | break; | ||
912 | |||
913 | case 0: | ||
914 | hpc_ptr->u.isa_ctlr.io_start = readw (io_mem + addr); | ||
915 | hpc_ptr->u.isa_ctlr.io_end = readw (io_mem + addr + 2); | ||
916 | if (!request_region (hpc_ptr->u.isa_ctlr.io_start, | ||
917 | (hpc_ptr->u.isa_ctlr.io_end - hpc_ptr->u.isa_ctlr.io_start + 1), | ||
918 | "ibmphp")) { | ||
919 | rc = -ENODEV; | ||
920 | goto error_no_hp_slot; | ||
921 | } | ||
922 | hpc_ptr->irq = readb (io_mem + addr + 4); | ||
923 | addr += 5; | ||
924 | break; | ||
925 | |||
926 | case 2: | ||
927 | case 4: | ||
928 | hpc_ptr->u.wpeg_ctlr.wpegbbar = readl (io_mem + addr); | ||
929 | hpc_ptr->u.wpeg_ctlr.i2c_addr = readb (io_mem + addr + 4); | ||
930 | hpc_ptr->irq = readb (io_mem + addr + 5); | ||
931 | addr += 6; | ||
932 | break; | ||
933 | default: | ||
934 | rc = -ENODEV; | ||
935 | goto error_no_hp_slot; | ||
936 | } | ||
937 | |||
938 | //reorganize chassis' linked list | ||
939 | combine_wpg_for_chassis (); | ||
940 | combine_wpg_for_expansion (); | ||
941 | hpc_ptr->revision = 0xff; | ||
942 | hpc_ptr->options = 0xff; | ||
943 | hpc_ptr->starting_slot_num = hpc_ptr->slots[0].slot_num; | ||
944 | hpc_ptr->ending_slot_num = hpc_ptr->slots[slot_num-1].slot_num; | ||
945 | |||
946 | // register slots with hpc core as well as create linked list of ibm slot | ||
947 | for (index = 0; index < hpc_ptr->slot_count; index++) { | ||
948 | |||
949 | hp_slot_ptr = kmalloc(sizeof(*hp_slot_ptr), GFP_KERNEL); | ||
950 | if (!hp_slot_ptr) { | ||
951 | rc = -ENOMEM; | ||
952 | goto error_no_hp_slot; | ||
953 | } | ||
954 | memset(hp_slot_ptr, 0, sizeof(*hp_slot_ptr)); | ||
955 | |||
956 | hp_slot_ptr->info = kmalloc (sizeof(struct hotplug_slot_info), GFP_KERNEL); | ||
957 | if (!hp_slot_ptr->info) { | ||
958 | rc = -ENOMEM; | ||
959 | goto error_no_hp_info; | ||
960 | } | ||
961 | memset(hp_slot_ptr->info, 0, sizeof(struct hotplug_slot_info)); | ||
962 | |||
963 | hp_slot_ptr->name = kmalloc(30, GFP_KERNEL); | ||
964 | if (!hp_slot_ptr->name) { | ||
965 | rc = -ENOMEM; | ||
966 | goto error_no_hp_name; | ||
967 | } | ||
968 | |||
969 | tmp_slot = kmalloc(sizeof(*tmp_slot), GFP_KERNEL); | ||
970 | if (!tmp_slot) { | ||
971 | rc = -ENOMEM; | ||
972 | goto error_no_slot; | ||
973 | } | ||
974 | memset(tmp_slot, 0, sizeof(*tmp_slot)); | ||
975 | |||
976 | tmp_slot->flag = TRUE; | ||
977 | |||
978 | tmp_slot->capabilities = hpc_ptr->slots[index].slot_cap; | ||
979 | if ((hpc_ptr->slots[index].slot_cap & EBDA_SLOT_133_MAX) == EBDA_SLOT_133_MAX) | ||
980 | tmp_slot->supported_speed = 3; | ||
981 | else if ((hpc_ptr->slots[index].slot_cap & EBDA_SLOT_100_MAX) == EBDA_SLOT_100_MAX) | ||
982 | tmp_slot->supported_speed = 2; | ||
983 | else if ((hpc_ptr->slots[index].slot_cap & EBDA_SLOT_66_MAX) == EBDA_SLOT_66_MAX) | ||
984 | tmp_slot->supported_speed = 1; | ||
985 | |||
986 | if ((hpc_ptr->slots[index].slot_cap & EBDA_SLOT_PCIX_CAP) == EBDA_SLOT_PCIX_CAP) | ||
987 | tmp_slot->supported_bus_mode = 1; | ||
988 | else | ||
989 | tmp_slot->supported_bus_mode = 0; | ||
990 | |||
991 | |||
992 | tmp_slot->bus = hpc_ptr->slots[index].slot_bus_num; | ||
993 | |||
994 | bus_info_ptr1 = ibmphp_find_same_bus_num (hpc_ptr->slots[index].slot_bus_num); | ||
995 | if (!bus_info_ptr1) { | ||
996 | rc = -ENODEV; | ||
997 | goto error; | ||
998 | } | ||
999 | tmp_slot->bus_on = bus_info_ptr1; | ||
1000 | bus_info_ptr1 = NULL; | ||
1001 | tmp_slot->ctrl = hpc_ptr; | ||
1002 | |||
1003 | tmp_slot->ctlr_index = hpc_ptr->slots[index].ctl_index; | ||
1004 | tmp_slot->number = hpc_ptr->slots[index].slot_num; | ||
1005 | tmp_slot->hotplug_slot = hp_slot_ptr; | ||
1006 | |||
1007 | hp_slot_ptr->private = tmp_slot; | ||
1008 | hp_slot_ptr->release = release_slot; | ||
1009 | |||
1010 | rc = fillslotinfo(hp_slot_ptr); | ||
1011 | if (rc) | ||
1012 | goto error; | ||
1013 | |||
1014 | rc = ibmphp_init_devno ((struct slot **) &hp_slot_ptr->private); | ||
1015 | if (rc) | ||
1016 | goto error; | ||
1017 | hp_slot_ptr->ops = &ibmphp_hotplug_slot_ops; | ||
1018 | |||
1019 | // end of registering ibm slot with hotplug core | ||
1020 | |||
1021 | list_add (& ((struct slot *)(hp_slot_ptr->private))->ibm_slot_list, &ibmphp_slot_head); | ||
1022 | } | ||
1023 | |||
1024 | print_bus_info (); | ||
1025 | list_add (&hpc_ptr->ebda_hpc_list, &ebda_hpc_head ); | ||
1026 | |||
1027 | } /* each hpc */ | ||
1028 | |||
1029 | list_for_each (list, &ibmphp_slot_head) { | ||
1030 | tmp_slot = list_entry (list, struct slot, ibm_slot_list); | ||
1031 | |||
1032 | snprintf (tmp_slot->hotplug_slot->name, 30, "%s", create_file_name (tmp_slot)); | ||
1033 | pci_hp_register (tmp_slot->hotplug_slot); | ||
1034 | } | ||
1035 | |||
1036 | print_ebda_hpc (); | ||
1037 | print_ibm_slot (); | ||
1038 | return 0; | ||
1039 | |||
1040 | error: | ||
1041 | kfree (hp_slot_ptr->private); | ||
1042 | error_no_slot: | ||
1043 | kfree (hp_slot_ptr->name); | ||
1044 | error_no_hp_name: | ||
1045 | kfree (hp_slot_ptr->info); | ||
1046 | error_no_hp_info: | ||
1047 | kfree (hp_slot_ptr); | ||
1048 | error_no_hp_slot: | ||
1049 | free_ebda_hpc (hpc_ptr); | ||
1050 | error_no_hpc: | ||
1051 | iounmap (io_mem); | ||
1052 | return rc; | ||
1053 | } | ||
1054 | |||
1055 | /* | ||
1056 | * map info (bus, devfun, start addr, end addr..) of i/o, memory, | ||
1057 | * pfm from the physical addr to a list of resource. | ||
1058 | */ | ||
1059 | static int __init ebda_rsrc_rsrc (void) | ||
1060 | { | ||
1061 | u16 addr; | ||
1062 | short rsrc; | ||
1063 | u8 type, rsrc_type; | ||
1064 | struct ebda_pci_rsrc *rsrc_ptr; | ||
1065 | |||
1066 | addr = rsrc_list_ptr->phys_addr; | ||
1067 | debug ("now entering rsrc land\n"); | ||
1068 | debug ("offset of rsrc: %x\n", rsrc_list_ptr->phys_addr); | ||
1069 | |||
1070 | for (rsrc = 0; rsrc < rsrc_list_ptr->num_entries; rsrc++) { | ||
1071 | type = readb (io_mem + addr); | ||
1072 | |||
1073 | addr += 1; | ||
1074 | rsrc_type = type & EBDA_RSRC_TYPE_MASK; | ||
1075 | |||
1076 | if (rsrc_type == EBDA_IO_RSRC_TYPE) { | ||
1077 | rsrc_ptr = alloc_ebda_pci_rsrc (); | ||
1078 | if (!rsrc_ptr) { | ||
1079 | iounmap (io_mem); | ||
1080 | return -ENOMEM; | ||
1081 | } | ||
1082 | rsrc_ptr->rsrc_type = type; | ||
1083 | |||
1084 | rsrc_ptr->bus_num = readb (io_mem + addr); | ||
1085 | rsrc_ptr->dev_fun = readb (io_mem + addr + 1); | ||
1086 | rsrc_ptr->start_addr = readw (io_mem + addr + 2); | ||
1087 | rsrc_ptr->end_addr = readw (io_mem + addr + 4); | ||
1088 | addr += 6; | ||
1089 | |||
1090 | debug ("rsrc from io type ----\n"); | ||
1091 | debug ("rsrc type: %x bus#: %x dev_func: %x start addr: %x end addr: %x\n", | ||
1092 | rsrc_ptr->rsrc_type, rsrc_ptr->bus_num, rsrc_ptr->dev_fun, rsrc_ptr->start_addr, rsrc_ptr->end_addr); | ||
1093 | |||
1094 | list_add (&rsrc_ptr->ebda_pci_rsrc_list, &ibmphp_ebda_pci_rsrc_head); | ||
1095 | } | ||
1096 | |||
1097 | if (rsrc_type == EBDA_MEM_RSRC_TYPE || rsrc_type == EBDA_PFM_RSRC_TYPE) { | ||
1098 | rsrc_ptr = alloc_ebda_pci_rsrc (); | ||
1099 | if (!rsrc_ptr ) { | ||
1100 | iounmap (io_mem); | ||
1101 | return -ENOMEM; | ||
1102 | } | ||
1103 | rsrc_ptr->rsrc_type = type; | ||
1104 | |||
1105 | rsrc_ptr->bus_num = readb (io_mem + addr); | ||
1106 | rsrc_ptr->dev_fun = readb (io_mem + addr + 1); | ||
1107 | rsrc_ptr->start_addr = readl (io_mem + addr + 2); | ||
1108 | rsrc_ptr->end_addr = readl (io_mem + addr + 6); | ||
1109 | addr += 10; | ||
1110 | |||
1111 | debug ("rsrc from mem or pfm ---\n"); | ||
1112 | debug ("rsrc type: %x bus#: %x dev_func: %x start addr: %x end addr: %x\n", | ||
1113 | rsrc_ptr->rsrc_type, rsrc_ptr->bus_num, rsrc_ptr->dev_fun, rsrc_ptr->start_addr, rsrc_ptr->end_addr); | ||
1114 | |||
1115 | list_add (&rsrc_ptr->ebda_pci_rsrc_list, &ibmphp_ebda_pci_rsrc_head); | ||
1116 | } | ||
1117 | } | ||
1118 | kfree (rsrc_list_ptr); | ||
1119 | rsrc_list_ptr = NULL; | ||
1120 | print_ebda_pci_rsrc (); | ||
1121 | return 0; | ||
1122 | } | ||
1123 | |||
1124 | u16 ibmphp_get_total_controllers (void) | ||
1125 | { | ||
1126 | return hpc_list_ptr->num_ctlrs; | ||
1127 | } | ||
1128 | |||
1129 | struct slot *ibmphp_get_slot_from_physical_num (u8 physical_num) | ||
1130 | { | ||
1131 | struct slot *slot; | ||
1132 | struct list_head *list; | ||
1133 | |||
1134 | list_for_each (list, &ibmphp_slot_head) { | ||
1135 | slot = list_entry (list, struct slot, ibm_slot_list); | ||
1136 | if (slot->number == physical_num) | ||
1137 | return slot; | ||
1138 | } | ||
1139 | return NULL; | ||
1140 | } | ||
1141 | |||
1142 | /* To find: | ||
1143 | * - the smallest slot number | ||
1144 | * - the largest slot number | ||
1145 | * - the total number of the slots based on each bus | ||
1146 | * (if only one slot per bus slot_min = slot_max ) | ||
1147 | */ | ||
1148 | struct bus_info *ibmphp_find_same_bus_num (u32 num) | ||
1149 | { | ||
1150 | struct bus_info *ptr; | ||
1151 | struct list_head *ptr1; | ||
1152 | |||
1153 | list_for_each (ptr1, &bus_info_head) { | ||
1154 | ptr = list_entry (ptr1, struct bus_info, bus_info_list); | ||
1155 | if (ptr->busno == num) | ||
1156 | return ptr; | ||
1157 | } | ||
1158 | return NULL; | ||
1159 | } | ||
1160 | |||
1161 | /* Finding relative bus number, in order to map corresponding | ||
1162 | * bus register | ||
1163 | */ | ||
1164 | int ibmphp_get_bus_index (u8 num) | ||
1165 | { | ||
1166 | struct bus_info *ptr; | ||
1167 | struct list_head *ptr1; | ||
1168 | |||
1169 | list_for_each (ptr1, &bus_info_head) { | ||
1170 | ptr = list_entry (ptr1, struct bus_info, bus_info_list); | ||
1171 | if (ptr->busno == num) | ||
1172 | return ptr->index; | ||
1173 | } | ||
1174 | return -ENODEV; | ||
1175 | } | ||
1176 | |||
1177 | void ibmphp_free_bus_info_queue (void) | ||
1178 | { | ||
1179 | struct bus_info *bus_info; | ||
1180 | struct list_head *list; | ||
1181 | struct list_head *next; | ||
1182 | |||
1183 | list_for_each_safe (list, next, &bus_info_head ) { | ||
1184 | bus_info = list_entry (list, struct bus_info, bus_info_list); | ||
1185 | kfree (bus_info); | ||
1186 | } | ||
1187 | } | ||
1188 | |||
1189 | void ibmphp_free_ebda_hpc_queue (void) | ||
1190 | { | ||
1191 | struct controller *controller = NULL; | ||
1192 | struct list_head *list; | ||
1193 | struct list_head *next; | ||
1194 | int pci_flag = 0; | ||
1195 | |||
1196 | list_for_each_safe (list, next, &ebda_hpc_head) { | ||
1197 | controller = list_entry (list, struct controller, ebda_hpc_list); | ||
1198 | if (controller->ctlr_type == 0) | ||
1199 | release_region (controller->u.isa_ctlr.io_start, (controller->u.isa_ctlr.io_end - controller->u.isa_ctlr.io_start + 1)); | ||
1200 | else if ((controller->ctlr_type == 1) && (!pci_flag)) { | ||
1201 | ++pci_flag; | ||
1202 | pci_unregister_driver (&ibmphp_driver); | ||
1203 | } | ||
1204 | free_ebda_hpc (controller); | ||
1205 | } | ||
1206 | } | ||
1207 | |||
1208 | void ibmphp_free_ebda_pci_rsrc_queue (void) | ||
1209 | { | ||
1210 | struct ebda_pci_rsrc *resource; | ||
1211 | struct list_head *list; | ||
1212 | struct list_head *next; | ||
1213 | |||
1214 | list_for_each_safe (list, next, &ibmphp_ebda_pci_rsrc_head) { | ||
1215 | resource = list_entry (list, struct ebda_pci_rsrc, ebda_pci_rsrc_list); | ||
1216 | kfree (resource); | ||
1217 | resource = NULL; | ||
1218 | } | ||
1219 | } | ||
1220 | |||
1221 | static struct pci_device_id id_table[] = { | ||
1222 | { | ||
1223 | .vendor = PCI_VENDOR_ID_IBM, | ||
1224 | .device = HPC_DEVICE_ID, | ||
1225 | .subvendor = PCI_VENDOR_ID_IBM, | ||
1226 | .subdevice = HPC_SUBSYSTEM_ID, | ||
1227 | .class = ((PCI_CLASS_SYSTEM_PCI_HOTPLUG << 8) | 0x00), | ||
1228 | }, {} | ||
1229 | }; | ||
1230 | |||
1231 | MODULE_DEVICE_TABLE(pci, id_table); | ||
1232 | |||
1233 | static int ibmphp_probe (struct pci_dev *, const struct pci_device_id *); | ||
1234 | static struct pci_driver ibmphp_driver = { | ||
1235 | .name = "ibmphp", | ||
1236 | .id_table = id_table, | ||
1237 | .probe = ibmphp_probe, | ||
1238 | }; | ||
1239 | |||
1240 | int ibmphp_register_pci (void) | ||
1241 | { | ||
1242 | struct controller *ctrl; | ||
1243 | struct list_head *tmp; | ||
1244 | int rc = 0; | ||
1245 | |||
1246 | list_for_each (tmp, &ebda_hpc_head) { | ||
1247 | ctrl = list_entry (tmp, struct controller, ebda_hpc_list); | ||
1248 | if (ctrl->ctlr_type == 1) { | ||
1249 | rc = pci_register_driver(&ibmphp_driver); | ||
1250 | break; | ||
1251 | } | ||
1252 | } | ||
1253 | return rc; | ||
1254 | } | ||
1255 | static int ibmphp_probe (struct pci_dev * dev, const struct pci_device_id *ids) | ||
1256 | { | ||
1257 | struct controller *ctrl; | ||
1258 | struct list_head *tmp; | ||
1259 | |||
1260 | debug ("inside ibmphp_probe\n"); | ||
1261 | |||
1262 | list_for_each (tmp, &ebda_hpc_head) { | ||
1263 | ctrl = list_entry (tmp, struct controller, ebda_hpc_list); | ||
1264 | if (ctrl->ctlr_type == 1) { | ||
1265 | if ((dev->devfn == ctrl->u.pci_ctlr.dev_fun) && (dev->bus->number == ctrl->u.pci_ctlr.bus)) { | ||
1266 | ctrl->ctrl_dev = dev; | ||
1267 | debug ("found device!!!\n"); | ||
1268 | debug ("dev->device = %x, dev->subsystem_device = %x\n", dev->device, dev->subsystem_device); | ||
1269 | return 0; | ||
1270 | } | ||
1271 | } | ||
1272 | } | ||
1273 | return -ENODEV; | ||
1274 | } | ||
1275 | |||
diff --git a/drivers/pci/hotplug/ibmphp_hpc.c b/drivers/pci/hotplug/ibmphp_hpc.c new file mode 100644 index 000000000000..6894b548c8ca --- /dev/null +++ b/drivers/pci/hotplug/ibmphp_hpc.c | |||
@@ -0,0 +1,1161 @@ | |||
1 | /* | ||
2 | * IBM Hot Plug Controller Driver | ||
3 | * | ||
4 | * Written By: Jyoti Shah, IBM Corporation | ||
5 | * | ||
6 | * Copyright (C) 2001-2003 IBM Corp. | ||
7 | * | ||
8 | * All rights reserved. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or (at | ||
13 | * your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
18 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
19 | * details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
24 | * | ||
25 | * Send feedback to <gregkh@us.ibm.com> | ||
26 | * <jshah@us.ibm.com> | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | #include <linux/wait.h> | ||
31 | #include <linux/time.h> | ||
32 | #include <linux/delay.h> | ||
33 | #include <linux/module.h> | ||
34 | #include <linux/pci.h> | ||
35 | #include <linux/smp_lock.h> | ||
36 | #include <linux/init.h> | ||
37 | #include "ibmphp.h" | ||
38 | |||
39 | static int to_debug = FALSE; | ||
40 | #define debug_polling(fmt, arg...) do { if (to_debug) debug (fmt, arg); } while (0) | ||
41 | |||
42 | //---------------------------------------------------------------------------- | ||
43 | // timeout values | ||
44 | //---------------------------------------------------------------------------- | ||
45 | #define CMD_COMPLETE_TOUT_SEC 60 // give HPC 60 sec to finish cmd | ||
46 | #define HPC_CTLR_WORKING_TOUT 60 // give HPC 60 sec to finish cmd | ||
47 | #define HPC_GETACCESS_TIMEOUT 60 // seconds | ||
48 | #define POLL_INTERVAL_SEC 2 // poll HPC every 2 seconds | ||
49 | #define POLL_LATCH_CNT 5 // poll latch 5 times, then poll slots | ||
50 | |||
51 | //---------------------------------------------------------------------------- | ||
52 | // Winnipeg Architected Register Offsets | ||
53 | //---------------------------------------------------------------------------- | ||
54 | #define WPG_I2CMBUFL_OFFSET 0x08 // I2C Message Buffer Low | ||
55 | #define WPG_I2CMOSUP_OFFSET 0x10 // I2C Master Operation Setup Reg | ||
56 | #define WPG_I2CMCNTL_OFFSET 0x20 // I2C Master Control Register | ||
57 | #define WPG_I2CPARM_OFFSET 0x40 // I2C Parameter Register | ||
58 | #define WPG_I2CSTAT_OFFSET 0x70 // I2C Status Register | ||
59 | |||
60 | //---------------------------------------------------------------------------- | ||
61 | // Winnipeg Store Type commands (Add this commands to the register offset) | ||
62 | //---------------------------------------------------------------------------- | ||
63 | #define WPG_I2C_AND 0x1000 // I2C AND operation | ||
64 | #define WPG_I2C_OR 0x2000 // I2C OR operation | ||
65 | |||
66 | //---------------------------------------------------------------------------- | ||
67 | // Command set for I2C Master Operation Setup Regisetr | ||
68 | //---------------------------------------------------------------------------- | ||
69 | #define WPG_READATADDR_MASK 0x00010000 // read,bytes,I2C shifted,index | ||
70 | #define WPG_WRITEATADDR_MASK 0x40010000 // write,bytes,I2C shifted,index | ||
71 | #define WPG_READDIRECT_MASK 0x10010000 | ||
72 | #define WPG_WRITEDIRECT_MASK 0x60010000 | ||
73 | |||
74 | |||
75 | //---------------------------------------------------------------------------- | ||
76 | // bit masks for I2C Master Control Register | ||
77 | //---------------------------------------------------------------------------- | ||
78 | #define WPG_I2CMCNTL_STARTOP_MASK 0x00000002 // Start the Operation | ||
79 | |||
80 | //---------------------------------------------------------------------------- | ||
81 | // | ||
82 | //---------------------------------------------------------------------------- | ||
83 | #define WPG_I2C_IOREMAP_SIZE 0x2044 // size of linear address interval | ||
84 | |||
85 | //---------------------------------------------------------------------------- | ||
86 | // command index | ||
87 | //---------------------------------------------------------------------------- | ||
88 | #define WPG_1ST_SLOT_INDEX 0x01 // index - 1st slot for ctlr | ||
89 | #define WPG_CTLR_INDEX 0x0F // index - ctlr | ||
90 | #define WPG_1ST_EXTSLOT_INDEX 0x10 // index - 1st ext slot for ctlr | ||
91 | #define WPG_1ST_BUS_INDEX 0x1F // index - 1st bus for ctlr | ||
92 | |||
93 | //---------------------------------------------------------------------------- | ||
94 | // macro utilities | ||
95 | //---------------------------------------------------------------------------- | ||
96 | // if bits 20,22,25,26,27,29,30 are OFF return TRUE | ||
97 | #define HPC_I2CSTATUS_CHECK(s) ((u8)((s & 0x00000A76) ? FALSE : TRUE)) | ||
98 | |||
99 | //---------------------------------------------------------------------------- | ||
100 | // global variables | ||
101 | //---------------------------------------------------------------------------- | ||
102 | static int ibmphp_shutdown; | ||
103 | static int tid_poll; | ||
104 | static struct semaphore sem_hpcaccess; // lock access to HPC | ||
105 | static struct semaphore semOperations; // lock all operations and | ||
106 | // access to data structures | ||
107 | static struct semaphore sem_exit; // make sure polling thread goes away | ||
108 | //---------------------------------------------------------------------------- | ||
109 | // local function prototypes | ||
110 | //---------------------------------------------------------------------------- | ||
111 | static u8 i2c_ctrl_read (struct controller *, void __iomem *, u8); | ||
112 | static u8 i2c_ctrl_write (struct controller *, void __iomem *, u8, u8); | ||
113 | static u8 hpc_writecmdtoindex (u8, u8); | ||
114 | static u8 hpc_readcmdtoindex (u8, u8); | ||
115 | static void get_hpc_access (void); | ||
116 | static void free_hpc_access (void); | ||
117 | static void poll_hpc (void); | ||
118 | static int process_changeinstatus (struct slot *, struct slot *); | ||
119 | static int process_changeinlatch (u8, u8, struct controller *); | ||
120 | static int hpc_poll_thread (void *); | ||
121 | static int hpc_wait_ctlr_notworking (int, struct controller *, void __iomem *, u8 *); | ||
122 | //---------------------------------------------------------------------------- | ||
123 | |||
124 | |||
125 | /*---------------------------------------------------------------------- | ||
126 | * Name: ibmphp_hpc_initvars | ||
127 | * | ||
128 | * Action: initialize semaphores and variables | ||
129 | *---------------------------------------------------------------------*/ | ||
130 | void __init ibmphp_hpc_initvars (void) | ||
131 | { | ||
132 | debug ("%s - Entry\n", __FUNCTION__); | ||
133 | |||
134 | init_MUTEX (&sem_hpcaccess); | ||
135 | init_MUTEX (&semOperations); | ||
136 | init_MUTEX_LOCKED (&sem_exit); | ||
137 | to_debug = FALSE; | ||
138 | ibmphp_shutdown = FALSE; | ||
139 | tid_poll = 0; | ||
140 | |||
141 | debug ("%s - Exit\n", __FUNCTION__); | ||
142 | } | ||
143 | |||
144 | /*---------------------------------------------------------------------- | ||
145 | * Name: i2c_ctrl_read | ||
146 | * | ||
147 | * Action: read from HPC over I2C | ||
148 | * | ||
149 | *---------------------------------------------------------------------*/ | ||
150 | static u8 i2c_ctrl_read (struct controller *ctlr_ptr, void __iomem *WPGBbar, u8 index) | ||
151 | { | ||
152 | u8 status; | ||
153 | int i; | ||
154 | void __iomem *wpg_addr; // base addr + offset | ||
155 | unsigned long wpg_data; // data to/from WPG LOHI format | ||
156 | unsigned long ultemp; | ||
157 | unsigned long data; // actual data HILO format | ||
158 | |||
159 | debug_polling ("%s - Entry WPGBbar[%p] index[%x] \n", __FUNCTION__, WPGBbar, index); | ||
160 | |||
161 | //-------------------------------------------------------------------- | ||
162 | // READ - step 1 | ||
163 | // read at address, byte length, I2C address (shifted), index | ||
164 | // or read direct, byte length, index | ||
165 | if (ctlr_ptr->ctlr_type == 0x02) { | ||
166 | data = WPG_READATADDR_MASK; | ||
167 | // fill in I2C address | ||
168 | ultemp = (unsigned long)ctlr_ptr->u.wpeg_ctlr.i2c_addr; | ||
169 | ultemp = ultemp >> 1; | ||
170 | data |= (ultemp << 8); | ||
171 | |||
172 | // fill in index | ||
173 | data |= (unsigned long)index; | ||
174 | } else if (ctlr_ptr->ctlr_type == 0x04) { | ||
175 | data = WPG_READDIRECT_MASK; | ||
176 | |||
177 | // fill in index | ||
178 | ultemp = (unsigned long)index; | ||
179 | ultemp = ultemp << 8; | ||
180 | data |= ultemp; | ||
181 | } else { | ||
182 | err ("this controller type is not supported \n"); | ||
183 | return HPC_ERROR; | ||
184 | } | ||
185 | |||
186 | wpg_data = swab32 (data); // swap data before writing | ||
187 | wpg_addr = WPGBbar + WPG_I2CMOSUP_OFFSET; | ||
188 | writel (wpg_data, wpg_addr); | ||
189 | |||
190 | //-------------------------------------------------------------------- | ||
191 | // READ - step 2 : clear the message buffer | ||
192 | data = 0x00000000; | ||
193 | wpg_data = swab32 (data); | ||
194 | wpg_addr = WPGBbar + WPG_I2CMBUFL_OFFSET; | ||
195 | writel (wpg_data, wpg_addr); | ||
196 | |||
197 | //-------------------------------------------------------------------- | ||
198 | // READ - step 3 : issue start operation, I2C master control bit 30:ON | ||
199 | // 2020 : [20] OR operation at [20] offset 0x20 | ||
200 | data = WPG_I2CMCNTL_STARTOP_MASK; | ||
201 | wpg_data = swab32 (data); | ||
202 | wpg_addr = WPGBbar + WPG_I2CMCNTL_OFFSET + WPG_I2C_OR; | ||
203 | writel (wpg_data, wpg_addr); | ||
204 | |||
205 | //-------------------------------------------------------------------- | ||
206 | // READ - step 4 : wait until start operation bit clears | ||
207 | i = CMD_COMPLETE_TOUT_SEC; | ||
208 | while (i) { | ||
209 | msleep(10); | ||
210 | wpg_addr = WPGBbar + WPG_I2CMCNTL_OFFSET; | ||
211 | wpg_data = readl (wpg_addr); | ||
212 | data = swab32 (wpg_data); | ||
213 | if (!(data & WPG_I2CMCNTL_STARTOP_MASK)) | ||
214 | break; | ||
215 | i--; | ||
216 | } | ||
217 | if (i == 0) { | ||
218 | debug ("%s - Error : WPG timeout\n", __FUNCTION__); | ||
219 | return HPC_ERROR; | ||
220 | } | ||
221 | //-------------------------------------------------------------------- | ||
222 | // READ - step 5 : read I2C status register | ||
223 | i = CMD_COMPLETE_TOUT_SEC; | ||
224 | while (i) { | ||
225 | msleep(10); | ||
226 | wpg_addr = WPGBbar + WPG_I2CSTAT_OFFSET; | ||
227 | wpg_data = readl (wpg_addr); | ||
228 | data = swab32 (wpg_data); | ||
229 | if (HPC_I2CSTATUS_CHECK (data)) | ||
230 | break; | ||
231 | i--; | ||
232 | } | ||
233 | if (i == 0) { | ||
234 | debug ("ctrl_read - Exit Error:I2C timeout\n"); | ||
235 | return HPC_ERROR; | ||
236 | } | ||
237 | |||
238 | //-------------------------------------------------------------------- | ||
239 | // READ - step 6 : get DATA | ||
240 | wpg_addr = WPGBbar + WPG_I2CMBUFL_OFFSET; | ||
241 | wpg_data = readl (wpg_addr); | ||
242 | data = swab32 (wpg_data); | ||
243 | |||
244 | status = (u8) data; | ||
245 | |||
246 | debug_polling ("%s - Exit index[%x] status[%x]\n", __FUNCTION__, index, status); | ||
247 | |||
248 | return (status); | ||
249 | } | ||
250 | |||
251 | /*---------------------------------------------------------------------- | ||
252 | * Name: i2c_ctrl_write | ||
253 | * | ||
254 | * Action: write to HPC over I2C | ||
255 | * | ||
256 | * Return 0 or error codes | ||
257 | *---------------------------------------------------------------------*/ | ||
258 | static u8 i2c_ctrl_write (struct controller *ctlr_ptr, void __iomem *WPGBbar, u8 index, u8 cmd) | ||
259 | { | ||
260 | u8 rc; | ||
261 | void __iomem *wpg_addr; // base addr + offset | ||
262 | unsigned long wpg_data; // data to/from WPG LOHI format | ||
263 | unsigned long ultemp; | ||
264 | unsigned long data; // actual data HILO format | ||
265 | int i; | ||
266 | |||
267 | debug_polling ("%s - Entry WPGBbar[%p] index[%x] cmd[%x]\n", __FUNCTION__, WPGBbar, index, cmd); | ||
268 | |||
269 | rc = 0; | ||
270 | //-------------------------------------------------------------------- | ||
271 | // WRITE - step 1 | ||
272 | // write at address, byte length, I2C address (shifted), index | ||
273 | // or write direct, byte length, index | ||
274 | data = 0x00000000; | ||
275 | |||
276 | if (ctlr_ptr->ctlr_type == 0x02) { | ||
277 | data = WPG_WRITEATADDR_MASK; | ||
278 | // fill in I2C address | ||
279 | ultemp = (unsigned long)ctlr_ptr->u.wpeg_ctlr.i2c_addr; | ||
280 | ultemp = ultemp >> 1; | ||
281 | data |= (ultemp << 8); | ||
282 | |||
283 | // fill in index | ||
284 | data |= (unsigned long)index; | ||
285 | } else if (ctlr_ptr->ctlr_type == 0x04) { | ||
286 | data = WPG_WRITEDIRECT_MASK; | ||
287 | |||
288 | // fill in index | ||
289 | ultemp = (unsigned long)index; | ||
290 | ultemp = ultemp << 8; | ||
291 | data |= ultemp; | ||
292 | } else { | ||
293 | err ("this controller type is not supported \n"); | ||
294 | return HPC_ERROR; | ||
295 | } | ||
296 | |||
297 | wpg_data = swab32 (data); // swap data before writing | ||
298 | wpg_addr = WPGBbar + WPG_I2CMOSUP_OFFSET; | ||
299 | writel (wpg_data, wpg_addr); | ||
300 | |||
301 | //-------------------------------------------------------------------- | ||
302 | // WRITE - step 2 : clear the message buffer | ||
303 | data = 0x00000000 | (unsigned long)cmd; | ||
304 | wpg_data = swab32 (data); | ||
305 | wpg_addr = WPGBbar + WPG_I2CMBUFL_OFFSET; | ||
306 | writel (wpg_data, wpg_addr); | ||
307 | |||
308 | //-------------------------------------------------------------------- | ||
309 | // WRITE - step 3 : issue start operation,I2C master control bit 30:ON | ||
310 | // 2020 : [20] OR operation at [20] offset 0x20 | ||
311 | data = WPG_I2CMCNTL_STARTOP_MASK; | ||
312 | wpg_data = swab32 (data); | ||
313 | wpg_addr = WPGBbar + WPG_I2CMCNTL_OFFSET + WPG_I2C_OR; | ||
314 | writel (wpg_data, wpg_addr); | ||
315 | |||
316 | //-------------------------------------------------------------------- | ||
317 | // WRITE - step 4 : wait until start operation bit clears | ||
318 | i = CMD_COMPLETE_TOUT_SEC; | ||
319 | while (i) { | ||
320 | msleep(10); | ||
321 | wpg_addr = WPGBbar + WPG_I2CMCNTL_OFFSET; | ||
322 | wpg_data = readl (wpg_addr); | ||
323 | data = swab32 (wpg_data); | ||
324 | if (!(data & WPG_I2CMCNTL_STARTOP_MASK)) | ||
325 | break; | ||
326 | i--; | ||
327 | } | ||
328 | if (i == 0) { | ||
329 | debug ("%s - Exit Error:WPG timeout\n", __FUNCTION__); | ||
330 | rc = HPC_ERROR; | ||
331 | } | ||
332 | |||
333 | //-------------------------------------------------------------------- | ||
334 | // WRITE - step 5 : read I2C status register | ||
335 | i = CMD_COMPLETE_TOUT_SEC; | ||
336 | while (i) { | ||
337 | msleep(10); | ||
338 | wpg_addr = WPGBbar + WPG_I2CSTAT_OFFSET; | ||
339 | wpg_data = readl (wpg_addr); | ||
340 | data = swab32 (wpg_data); | ||
341 | if (HPC_I2CSTATUS_CHECK (data)) | ||
342 | break; | ||
343 | i--; | ||
344 | } | ||
345 | if (i == 0) { | ||
346 | debug ("ctrl_read - Error : I2C timeout\n"); | ||
347 | rc = HPC_ERROR; | ||
348 | } | ||
349 | |||
350 | debug_polling ("%s Exit rc[%x]\n", __FUNCTION__, rc); | ||
351 | return (rc); | ||
352 | } | ||
353 | |||
354 | //------------------------------------------------------------ | ||
355 | // Read from ISA type HPC | ||
356 | //------------------------------------------------------------ | ||
357 | static u8 isa_ctrl_read (struct controller *ctlr_ptr, u8 offset) | ||
358 | { | ||
359 | u16 start_address; | ||
360 | u16 end_address; | ||
361 | u8 data; | ||
362 | |||
363 | start_address = ctlr_ptr->u.isa_ctlr.io_start; | ||
364 | end_address = ctlr_ptr->u.isa_ctlr.io_end; | ||
365 | data = inb (start_address + offset); | ||
366 | return data; | ||
367 | } | ||
368 | |||
369 | //-------------------------------------------------------------- | ||
370 | // Write to ISA type HPC | ||
371 | //-------------------------------------------------------------- | ||
372 | static void isa_ctrl_write (struct controller *ctlr_ptr, u8 offset, u8 data) | ||
373 | { | ||
374 | u16 start_address; | ||
375 | u16 port_address; | ||
376 | |||
377 | start_address = ctlr_ptr->u.isa_ctlr.io_start; | ||
378 | port_address = start_address + (u16) offset; | ||
379 | outb (data, port_address); | ||
380 | } | ||
381 | |||
382 | static u8 pci_ctrl_read (struct controller *ctrl, u8 offset) | ||
383 | { | ||
384 | u8 data = 0x00; | ||
385 | debug ("inside pci_ctrl_read\n"); | ||
386 | if (ctrl->ctrl_dev) | ||
387 | pci_read_config_byte (ctrl->ctrl_dev, HPC_PCI_OFFSET + offset, &data); | ||
388 | return data; | ||
389 | } | ||
390 | |||
391 | static u8 pci_ctrl_write (struct controller *ctrl, u8 offset, u8 data) | ||
392 | { | ||
393 | u8 rc = -ENODEV; | ||
394 | debug ("inside pci_ctrl_write\n"); | ||
395 | if (ctrl->ctrl_dev) { | ||
396 | pci_write_config_byte (ctrl->ctrl_dev, HPC_PCI_OFFSET + offset, data); | ||
397 | rc = 0; | ||
398 | } | ||
399 | return rc; | ||
400 | } | ||
401 | |||
402 | static u8 ctrl_read (struct controller *ctlr, void __iomem *base, u8 offset) | ||
403 | { | ||
404 | u8 rc; | ||
405 | switch (ctlr->ctlr_type) { | ||
406 | case 0: | ||
407 | rc = isa_ctrl_read (ctlr, offset); | ||
408 | break; | ||
409 | case 1: | ||
410 | rc = pci_ctrl_read (ctlr, offset); | ||
411 | break; | ||
412 | case 2: | ||
413 | case 4: | ||
414 | rc = i2c_ctrl_read (ctlr, base, offset); | ||
415 | break; | ||
416 | default: | ||
417 | return -ENODEV; | ||
418 | } | ||
419 | return rc; | ||
420 | } | ||
421 | |||
422 | static u8 ctrl_write (struct controller *ctlr, void __iomem *base, u8 offset, u8 data) | ||
423 | { | ||
424 | u8 rc = 0; | ||
425 | switch (ctlr->ctlr_type) { | ||
426 | case 0: | ||
427 | isa_ctrl_write(ctlr, offset, data); | ||
428 | break; | ||
429 | case 1: | ||
430 | rc = pci_ctrl_write (ctlr, offset, data); | ||
431 | break; | ||
432 | case 2: | ||
433 | case 4: | ||
434 | rc = i2c_ctrl_write(ctlr, base, offset, data); | ||
435 | break; | ||
436 | default: | ||
437 | return -ENODEV; | ||
438 | } | ||
439 | return rc; | ||
440 | } | ||
441 | /*---------------------------------------------------------------------- | ||
442 | * Name: hpc_writecmdtoindex() | ||
443 | * | ||
444 | * Action: convert a write command to proper index within a controller | ||
445 | * | ||
446 | * Return index, HPC_ERROR | ||
447 | *---------------------------------------------------------------------*/ | ||
448 | static u8 hpc_writecmdtoindex (u8 cmd, u8 index) | ||
449 | { | ||
450 | u8 rc; | ||
451 | |||
452 | switch (cmd) { | ||
453 | case HPC_CTLR_ENABLEIRQ: // 0x00.N.15 | ||
454 | case HPC_CTLR_CLEARIRQ: // 0x06.N.15 | ||
455 | case HPC_CTLR_RESET: // 0x07.N.15 | ||
456 | case HPC_CTLR_IRQSTEER: // 0x08.N.15 | ||
457 | case HPC_CTLR_DISABLEIRQ: // 0x01.N.15 | ||
458 | case HPC_ALLSLOT_ON: // 0x11.N.15 | ||
459 | case HPC_ALLSLOT_OFF: // 0x12.N.15 | ||
460 | rc = 0x0F; | ||
461 | break; | ||
462 | |||
463 | case HPC_SLOT_OFF: // 0x02.Y.0-14 | ||
464 | case HPC_SLOT_ON: // 0x03.Y.0-14 | ||
465 | case HPC_SLOT_ATTNOFF: // 0x04.N.0-14 | ||
466 | case HPC_SLOT_ATTNON: // 0x05.N.0-14 | ||
467 | case HPC_SLOT_BLINKLED: // 0x13.N.0-14 | ||
468 | rc = index; | ||
469 | break; | ||
470 | |||
471 | case HPC_BUS_33CONVMODE: | ||
472 | case HPC_BUS_66CONVMODE: | ||
473 | case HPC_BUS_66PCIXMODE: | ||
474 | case HPC_BUS_100PCIXMODE: | ||
475 | case HPC_BUS_133PCIXMODE: | ||
476 | rc = index + WPG_1ST_BUS_INDEX - 1; | ||
477 | break; | ||
478 | |||
479 | default: | ||
480 | err ("hpc_writecmdtoindex - Error invalid cmd[%x]\n", cmd); | ||
481 | rc = HPC_ERROR; | ||
482 | } | ||
483 | |||
484 | return rc; | ||
485 | } | ||
486 | |||
487 | /*---------------------------------------------------------------------- | ||
488 | * Name: hpc_readcmdtoindex() | ||
489 | * | ||
490 | * Action: convert a read command to proper index within a controller | ||
491 | * | ||
492 | * Return index, HPC_ERROR | ||
493 | *---------------------------------------------------------------------*/ | ||
494 | static u8 hpc_readcmdtoindex (u8 cmd, u8 index) | ||
495 | { | ||
496 | u8 rc; | ||
497 | |||
498 | switch (cmd) { | ||
499 | case READ_CTLRSTATUS: | ||
500 | rc = 0x0F; | ||
501 | break; | ||
502 | case READ_SLOTSTATUS: | ||
503 | case READ_ALLSTAT: | ||
504 | rc = index; | ||
505 | break; | ||
506 | case READ_EXTSLOTSTATUS: | ||
507 | rc = index + WPG_1ST_EXTSLOT_INDEX; | ||
508 | break; | ||
509 | case READ_BUSSTATUS: | ||
510 | rc = index + WPG_1ST_BUS_INDEX - 1; | ||
511 | break; | ||
512 | case READ_SLOTLATCHLOWREG: | ||
513 | rc = 0x28; | ||
514 | break; | ||
515 | case READ_REVLEVEL: | ||
516 | rc = 0x25; | ||
517 | break; | ||
518 | case READ_HPCOPTIONS: | ||
519 | rc = 0x27; | ||
520 | break; | ||
521 | default: | ||
522 | rc = HPC_ERROR; | ||
523 | } | ||
524 | return rc; | ||
525 | } | ||
526 | |||
527 | /*---------------------------------------------------------------------- | ||
528 | * Name: HPCreadslot() | ||
529 | * | ||
530 | * Action: issue a READ command to HPC | ||
531 | * | ||
532 | * Input: pslot - can not be NULL for READ_ALLSTAT | ||
533 | * pstatus - can be NULL for READ_ALLSTAT | ||
534 | * | ||
535 | * Return 0 or error codes | ||
536 | *---------------------------------------------------------------------*/ | ||
537 | int ibmphp_hpc_readslot (struct slot * pslot, u8 cmd, u8 * pstatus) | ||
538 | { | ||
539 | void __iomem *wpg_bbar = NULL; | ||
540 | struct controller *ctlr_ptr; | ||
541 | struct list_head *pslotlist; | ||
542 | u8 index, status; | ||
543 | int rc = 0; | ||
544 | int busindex; | ||
545 | |||
546 | debug_polling ("%s - Entry pslot[%p] cmd[%x] pstatus[%p]\n", __FUNCTION__, pslot, cmd, pstatus); | ||
547 | |||
548 | if ((pslot == NULL) | ||
549 | || ((pstatus == NULL) && (cmd != READ_ALLSTAT) && (cmd != READ_BUSSTATUS))) { | ||
550 | rc = -EINVAL; | ||
551 | err ("%s - Error invalid pointer, rc[%d]\n", __FUNCTION__, rc); | ||
552 | return rc; | ||
553 | } | ||
554 | |||
555 | if (cmd == READ_BUSSTATUS) { | ||
556 | busindex = ibmphp_get_bus_index (pslot->bus); | ||
557 | if (busindex < 0) { | ||
558 | rc = -EINVAL; | ||
559 | err ("%s - Exit Error:invalid bus, rc[%d]\n", __FUNCTION__, rc); | ||
560 | return rc; | ||
561 | } else | ||
562 | index = (u8) busindex; | ||
563 | } else | ||
564 | index = pslot->ctlr_index; | ||
565 | |||
566 | index = hpc_readcmdtoindex (cmd, index); | ||
567 | |||
568 | if (index == HPC_ERROR) { | ||
569 | rc = -EINVAL; | ||
570 | err ("%s - Exit Error:invalid index, rc[%d]\n", __FUNCTION__, rc); | ||
571 | return rc; | ||
572 | } | ||
573 | |||
574 | ctlr_ptr = pslot->ctrl; | ||
575 | |||
576 | get_hpc_access (); | ||
577 | |||
578 | //-------------------------------------------------------------------- | ||
579 | // map physical address to logical address | ||
580 | //-------------------------------------------------------------------- | ||
581 | if ((ctlr_ptr->ctlr_type == 2) || (ctlr_ptr->ctlr_type == 4)) | ||
582 | wpg_bbar = ioremap (ctlr_ptr->u.wpeg_ctlr.wpegbbar, WPG_I2C_IOREMAP_SIZE); | ||
583 | |||
584 | //-------------------------------------------------------------------- | ||
585 | // check controller status before reading | ||
586 | //-------------------------------------------------------------------- | ||
587 | rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, wpg_bbar, &status); | ||
588 | if (!rc) { | ||
589 | switch (cmd) { | ||
590 | case READ_ALLSTAT: | ||
591 | // update the slot structure | ||
592 | pslot->ctrl->status = status; | ||
593 | pslot->status = ctrl_read (ctlr_ptr, wpg_bbar, index); | ||
594 | rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, wpg_bbar, | ||
595 | &status); | ||
596 | if (!rc) | ||
597 | pslot->ext_status = ctrl_read (ctlr_ptr, wpg_bbar, index + WPG_1ST_EXTSLOT_INDEX); | ||
598 | |||
599 | break; | ||
600 | |||
601 | case READ_SLOTSTATUS: | ||
602 | // DO NOT update the slot structure | ||
603 | *pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index); | ||
604 | break; | ||
605 | |||
606 | case READ_EXTSLOTSTATUS: | ||
607 | // DO NOT update the slot structure | ||
608 | *pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index); | ||
609 | break; | ||
610 | |||
611 | case READ_CTLRSTATUS: | ||
612 | // DO NOT update the slot structure | ||
613 | *pstatus = status; | ||
614 | break; | ||
615 | |||
616 | case READ_BUSSTATUS: | ||
617 | pslot->busstatus = ctrl_read (ctlr_ptr, wpg_bbar, index); | ||
618 | break; | ||
619 | case READ_REVLEVEL: | ||
620 | *pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index); | ||
621 | break; | ||
622 | case READ_HPCOPTIONS: | ||
623 | *pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index); | ||
624 | break; | ||
625 | case READ_SLOTLATCHLOWREG: | ||
626 | // DO NOT update the slot structure | ||
627 | *pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index); | ||
628 | break; | ||
629 | |||
630 | // Not used | ||
631 | case READ_ALLSLOT: | ||
632 | list_for_each (pslotlist, &ibmphp_slot_head) { | ||
633 | pslot = list_entry (pslotlist, struct slot, ibm_slot_list); | ||
634 | index = pslot->ctlr_index; | ||
635 | rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, | ||
636 | wpg_bbar, &status); | ||
637 | if (!rc) { | ||
638 | pslot->status = ctrl_read (ctlr_ptr, wpg_bbar, index); | ||
639 | rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, | ||
640 | ctlr_ptr, wpg_bbar, &status); | ||
641 | if (!rc) | ||
642 | pslot->ext_status = | ||
643 | ctrl_read (ctlr_ptr, wpg_bbar, | ||
644 | index + WPG_1ST_EXTSLOT_INDEX); | ||
645 | } else { | ||
646 | err ("%s - Error ctrl_read failed\n", __FUNCTION__); | ||
647 | rc = -EINVAL; | ||
648 | break; | ||
649 | } | ||
650 | } | ||
651 | break; | ||
652 | default: | ||
653 | rc = -EINVAL; | ||
654 | break; | ||
655 | } | ||
656 | } | ||
657 | //-------------------------------------------------------------------- | ||
658 | // cleanup | ||
659 | //-------------------------------------------------------------------- | ||
660 | |||
661 | // remove physical to logical address mapping | ||
662 | if ((ctlr_ptr->ctlr_type == 2) || (ctlr_ptr->ctlr_type == 4)) | ||
663 | iounmap (wpg_bbar); | ||
664 | |||
665 | free_hpc_access (); | ||
666 | |||
667 | debug_polling ("%s - Exit rc[%d]\n", __FUNCTION__, rc); | ||
668 | return rc; | ||
669 | } | ||
670 | |||
671 | /*---------------------------------------------------------------------- | ||
672 | * Name: ibmphp_hpc_writeslot() | ||
673 | * | ||
674 | * Action: issue a WRITE command to HPC | ||
675 | *---------------------------------------------------------------------*/ | ||
676 | int ibmphp_hpc_writeslot (struct slot * pslot, u8 cmd) | ||
677 | { | ||
678 | void __iomem *wpg_bbar = NULL; | ||
679 | struct controller *ctlr_ptr; | ||
680 | u8 index, status; | ||
681 | int busindex; | ||
682 | u8 done; | ||
683 | int rc = 0; | ||
684 | int timeout; | ||
685 | |||
686 | debug_polling ("%s - Entry pslot[%p] cmd[%x]\n", __FUNCTION__, pslot, cmd); | ||
687 | if (pslot == NULL) { | ||
688 | rc = -EINVAL; | ||
689 | err ("%s - Error Exit rc[%d]\n", __FUNCTION__, rc); | ||
690 | return rc; | ||
691 | } | ||
692 | |||
693 | if ((cmd == HPC_BUS_33CONVMODE) || (cmd == HPC_BUS_66CONVMODE) || | ||
694 | (cmd == HPC_BUS_66PCIXMODE) || (cmd == HPC_BUS_100PCIXMODE) || | ||
695 | (cmd == HPC_BUS_133PCIXMODE)) { | ||
696 | busindex = ibmphp_get_bus_index (pslot->bus); | ||
697 | if (busindex < 0) { | ||
698 | rc = -EINVAL; | ||
699 | err ("%s - Exit Error:invalid bus, rc[%d]\n", __FUNCTION__, rc); | ||
700 | return rc; | ||
701 | } else | ||
702 | index = (u8) busindex; | ||
703 | } else | ||
704 | index = pslot->ctlr_index; | ||
705 | |||
706 | index = hpc_writecmdtoindex (cmd, index); | ||
707 | |||
708 | if (index == HPC_ERROR) { | ||
709 | rc = -EINVAL; | ||
710 | err ("%s - Error Exit rc[%d]\n", __FUNCTION__, rc); | ||
711 | return rc; | ||
712 | } | ||
713 | |||
714 | ctlr_ptr = pslot->ctrl; | ||
715 | |||
716 | get_hpc_access (); | ||
717 | |||
718 | //-------------------------------------------------------------------- | ||
719 | // map physical address to logical address | ||
720 | //-------------------------------------------------------------------- | ||
721 | if ((ctlr_ptr->ctlr_type == 2) || (ctlr_ptr->ctlr_type == 4)) { | ||
722 | wpg_bbar = ioremap (ctlr_ptr->u.wpeg_ctlr.wpegbbar, WPG_I2C_IOREMAP_SIZE); | ||
723 | |||
724 | debug ("%s - ctlr id[%x] physical[%lx] logical[%lx] i2c[%x]\n", __FUNCTION__, | ||
725 | ctlr_ptr->ctlr_id, (ulong) (ctlr_ptr->u.wpeg_ctlr.wpegbbar), (ulong) wpg_bbar, | ||
726 | ctlr_ptr->u.wpeg_ctlr.i2c_addr); | ||
727 | } | ||
728 | //-------------------------------------------------------------------- | ||
729 | // check controller status before writing | ||
730 | //-------------------------------------------------------------------- | ||
731 | rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, wpg_bbar, &status); | ||
732 | if (!rc) { | ||
733 | |||
734 | ctrl_write (ctlr_ptr, wpg_bbar, index, cmd); | ||
735 | |||
736 | //-------------------------------------------------------------------- | ||
737 | // check controller is still not working on the command | ||
738 | //-------------------------------------------------------------------- | ||
739 | timeout = CMD_COMPLETE_TOUT_SEC; | ||
740 | done = FALSE; | ||
741 | while (!done) { | ||
742 | rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, wpg_bbar, | ||
743 | &status); | ||
744 | if (!rc) { | ||
745 | if (NEEDTOCHECK_CMDSTATUS (cmd)) { | ||
746 | if (CTLR_FINISHED (status) == HPC_CTLR_FINISHED_YES) | ||
747 | done = TRUE; | ||
748 | } else | ||
749 | done = TRUE; | ||
750 | } | ||
751 | if (!done) { | ||
752 | msleep(1000); | ||
753 | if (timeout < 1) { | ||
754 | done = TRUE; | ||
755 | err ("%s - Error command complete timeout\n", __FUNCTION__); | ||
756 | rc = -EFAULT; | ||
757 | } else | ||
758 | timeout--; | ||
759 | } | ||
760 | } | ||
761 | ctlr_ptr->status = status; | ||
762 | } | ||
763 | // cleanup | ||
764 | |||
765 | // remove physical to logical address mapping | ||
766 | if ((ctlr_ptr->ctlr_type == 2) || (ctlr_ptr->ctlr_type == 4)) | ||
767 | iounmap (wpg_bbar); | ||
768 | free_hpc_access (); | ||
769 | |||
770 | debug_polling ("%s - Exit rc[%d]\n", __FUNCTION__, rc); | ||
771 | return rc; | ||
772 | } | ||
773 | |||
774 | /*---------------------------------------------------------------------- | ||
775 | * Name: get_hpc_access() | ||
776 | * | ||
777 | * Action: make sure only one process can access HPC at one time | ||
778 | *---------------------------------------------------------------------*/ | ||
779 | static void get_hpc_access (void) | ||
780 | { | ||
781 | down (&sem_hpcaccess); | ||
782 | } | ||
783 | |||
784 | /*---------------------------------------------------------------------- | ||
785 | * Name: free_hpc_access() | ||
786 | *---------------------------------------------------------------------*/ | ||
787 | void free_hpc_access (void) | ||
788 | { | ||
789 | up (&sem_hpcaccess); | ||
790 | } | ||
791 | |||
792 | /*---------------------------------------------------------------------- | ||
793 | * Name: ibmphp_lock_operations() | ||
794 | * | ||
795 | * Action: make sure only one process can change the data structure | ||
796 | *---------------------------------------------------------------------*/ | ||
797 | void ibmphp_lock_operations (void) | ||
798 | { | ||
799 | down (&semOperations); | ||
800 | to_debug = TRUE; | ||
801 | } | ||
802 | |||
803 | /*---------------------------------------------------------------------- | ||
804 | * Name: ibmphp_unlock_operations() | ||
805 | *---------------------------------------------------------------------*/ | ||
806 | void ibmphp_unlock_operations (void) | ||
807 | { | ||
808 | debug ("%s - Entry\n", __FUNCTION__); | ||
809 | up (&semOperations); | ||
810 | to_debug = FALSE; | ||
811 | debug ("%s - Exit\n", __FUNCTION__); | ||
812 | } | ||
813 | |||
814 | /*---------------------------------------------------------------------- | ||
815 | * Name: poll_hpc() | ||
816 | *---------------------------------------------------------------------*/ | ||
817 | #define POLL_LATCH_REGISTER 0 | ||
818 | #define POLL_SLOTS 1 | ||
819 | #define POLL_SLEEP 2 | ||
820 | static void poll_hpc (void) | ||
821 | { | ||
822 | struct slot myslot; | ||
823 | struct slot *pslot = NULL; | ||
824 | struct list_head *pslotlist; | ||
825 | int rc; | ||
826 | int poll_state = POLL_LATCH_REGISTER; | ||
827 | u8 oldlatchlow = 0x00; | ||
828 | u8 curlatchlow = 0x00; | ||
829 | int poll_count = 0; | ||
830 | u8 ctrl_count = 0x00; | ||
831 | |||
832 | debug ("%s - Entry\n", __FUNCTION__); | ||
833 | |||
834 | while (!ibmphp_shutdown) { | ||
835 | if (ibmphp_shutdown) | ||
836 | break; | ||
837 | |||
838 | /* try to get the lock to do some kind of harware access */ | ||
839 | down (&semOperations); | ||
840 | |||
841 | switch (poll_state) { | ||
842 | case POLL_LATCH_REGISTER: | ||
843 | oldlatchlow = curlatchlow; | ||
844 | ctrl_count = 0x00; | ||
845 | list_for_each (pslotlist, &ibmphp_slot_head) { | ||
846 | if (ctrl_count >= ibmphp_get_total_controllers()) | ||
847 | break; | ||
848 | pslot = list_entry (pslotlist, struct slot, ibm_slot_list); | ||
849 | if (pslot->ctrl->ctlr_relative_id == ctrl_count) { | ||
850 | ctrl_count++; | ||
851 | if (READ_SLOT_LATCH (pslot->ctrl)) { | ||
852 | rc = ibmphp_hpc_readslot (pslot, | ||
853 | READ_SLOTLATCHLOWREG, | ||
854 | &curlatchlow); | ||
855 | if (oldlatchlow != curlatchlow) | ||
856 | process_changeinlatch (oldlatchlow, | ||
857 | curlatchlow, | ||
858 | pslot->ctrl); | ||
859 | } | ||
860 | } | ||
861 | } | ||
862 | ++poll_count; | ||
863 | poll_state = POLL_SLEEP; | ||
864 | break; | ||
865 | case POLL_SLOTS: | ||
866 | list_for_each (pslotlist, &ibmphp_slot_head) { | ||
867 | pslot = list_entry (pslotlist, struct slot, ibm_slot_list); | ||
868 | // make a copy of the old status | ||
869 | memcpy ((void *) &myslot, (void *) pslot, | ||
870 | sizeof (struct slot)); | ||
871 | rc = ibmphp_hpc_readslot (pslot, READ_ALLSTAT, NULL); | ||
872 | if ((myslot.status != pslot->status) | ||
873 | || (myslot.ext_status != pslot->ext_status)) | ||
874 | process_changeinstatus (pslot, &myslot); | ||
875 | } | ||
876 | ctrl_count = 0x00; | ||
877 | list_for_each (pslotlist, &ibmphp_slot_head) { | ||
878 | if (ctrl_count >= ibmphp_get_total_controllers()) | ||
879 | break; | ||
880 | pslot = list_entry (pslotlist, struct slot, ibm_slot_list); | ||
881 | if (pslot->ctrl->ctlr_relative_id == ctrl_count) { | ||
882 | ctrl_count++; | ||
883 | if (READ_SLOT_LATCH (pslot->ctrl)) | ||
884 | rc = ibmphp_hpc_readslot (pslot, | ||
885 | READ_SLOTLATCHLOWREG, | ||
886 | &curlatchlow); | ||
887 | } | ||
888 | } | ||
889 | ++poll_count; | ||
890 | poll_state = POLL_SLEEP; | ||
891 | break; | ||
892 | case POLL_SLEEP: | ||
893 | /* don't sleep with a lock on the hardware */ | ||
894 | up (&semOperations); | ||
895 | msleep(POLL_INTERVAL_SEC * 1000); | ||
896 | |||
897 | if (ibmphp_shutdown) | ||
898 | break; | ||
899 | |||
900 | down (&semOperations); | ||
901 | |||
902 | if (poll_count >= POLL_LATCH_CNT) { | ||
903 | poll_count = 0; | ||
904 | poll_state = POLL_SLOTS; | ||
905 | } else | ||
906 | poll_state = POLL_LATCH_REGISTER; | ||
907 | break; | ||
908 | } | ||
909 | /* give up the harware semaphore */ | ||
910 | up (&semOperations); | ||
911 | /* sleep for a short time just for good measure */ | ||
912 | msleep(100); | ||
913 | } | ||
914 | up (&sem_exit); | ||
915 | debug ("%s - Exit\n", __FUNCTION__); | ||
916 | } | ||
917 | |||
918 | |||
919 | /*---------------------------------------------------------------------- | ||
920 | * Name: process_changeinstatus | ||
921 | * | ||
922 | * Action: compare old and new slot status, process the change in status | ||
923 | * | ||
924 | * Input: pointer to slot struct, old slot struct | ||
925 | * | ||
926 | * Return 0 or error codes | ||
927 | * Value: | ||
928 | * | ||
929 | * Side | ||
930 | * Effects: None. | ||
931 | * | ||
932 | * Notes: | ||
933 | *---------------------------------------------------------------------*/ | ||
934 | static int process_changeinstatus (struct slot *pslot, struct slot *poldslot) | ||
935 | { | ||
936 | u8 status; | ||
937 | int rc = 0; | ||
938 | u8 disable = FALSE; | ||
939 | u8 update = FALSE; | ||
940 | |||
941 | debug ("process_changeinstatus - Entry pslot[%p], poldslot[%p]\n", pslot, poldslot); | ||
942 | |||
943 | // bit 0 - HPC_SLOT_POWER | ||
944 | if ((pslot->status & 0x01) != (poldslot->status & 0x01)) | ||
945 | update = TRUE; | ||
946 | |||
947 | // bit 1 - HPC_SLOT_CONNECT | ||
948 | // ignore | ||
949 | |||
950 | // bit 2 - HPC_SLOT_ATTN | ||
951 | if ((pslot->status & 0x04) != (poldslot->status & 0x04)) | ||
952 | update = TRUE; | ||
953 | |||
954 | // bit 3 - HPC_SLOT_PRSNT2 | ||
955 | // bit 4 - HPC_SLOT_PRSNT1 | ||
956 | if (((pslot->status & 0x08) != (poldslot->status & 0x08)) | ||
957 | || ((pslot->status & 0x10) != (poldslot->status & 0x10))) | ||
958 | update = TRUE; | ||
959 | |||
960 | // bit 5 - HPC_SLOT_PWRGD | ||
961 | if ((pslot->status & 0x20) != (poldslot->status & 0x20)) | ||
962 | // OFF -> ON: ignore, ON -> OFF: disable slot | ||
963 | if ((poldslot->status & 0x20) && (SLOT_CONNECT (poldslot->status) == HPC_SLOT_CONNECTED) && (SLOT_PRESENT (poldslot->status))) | ||
964 | disable = TRUE; | ||
965 | |||
966 | // bit 6 - HPC_SLOT_BUS_SPEED | ||
967 | // ignore | ||
968 | |||
969 | // bit 7 - HPC_SLOT_LATCH | ||
970 | if ((pslot->status & 0x80) != (poldslot->status & 0x80)) { | ||
971 | update = TRUE; | ||
972 | // OPEN -> CLOSE | ||
973 | if (pslot->status & 0x80) { | ||
974 | if (SLOT_PWRGD (pslot->status)) { | ||
975 | // power goes on and off after closing latch | ||
976 | // check again to make sure power is still ON | ||
977 | msleep(1000); | ||
978 | rc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &status); | ||
979 | if (SLOT_PWRGD (status)) | ||
980 | update = TRUE; | ||
981 | else // overwrite power in pslot to OFF | ||
982 | pslot->status &= ~HPC_SLOT_POWER; | ||
983 | } | ||
984 | } | ||
985 | // CLOSE -> OPEN | ||
986 | else if ((SLOT_PWRGD (poldslot->status) == HPC_SLOT_PWRGD_GOOD) | ||
987 | && (SLOT_CONNECT (poldslot->status) == HPC_SLOT_CONNECTED) && (SLOT_PRESENT (poldslot->status))) { | ||
988 | disable = TRUE; | ||
989 | } | ||
990 | // else - ignore | ||
991 | } | ||
992 | // bit 4 - HPC_SLOT_BLINK_ATTN | ||
993 | if ((pslot->ext_status & 0x08) != (poldslot->ext_status & 0x08)) | ||
994 | update = TRUE; | ||
995 | |||
996 | if (disable) { | ||
997 | debug ("process_changeinstatus - disable slot\n"); | ||
998 | pslot->flag = FALSE; | ||
999 | rc = ibmphp_do_disable_slot (pslot); | ||
1000 | } | ||
1001 | |||
1002 | if (update || disable) { | ||
1003 | ibmphp_update_slot_info (pslot); | ||
1004 | } | ||
1005 | |||
1006 | debug ("%s - Exit rc[%d] disable[%x] update[%x]\n", __FUNCTION__, rc, disable, update); | ||
1007 | |||
1008 | return rc; | ||
1009 | } | ||
1010 | |||
1011 | /*---------------------------------------------------------------------- | ||
1012 | * Name: process_changeinlatch | ||
1013 | * | ||
1014 | * Action: compare old and new latch reg status, process the change | ||
1015 | * | ||
1016 | * Input: old and current latch register status | ||
1017 | * | ||
1018 | * Return 0 or error codes | ||
1019 | * Value: | ||
1020 | *---------------------------------------------------------------------*/ | ||
1021 | static int process_changeinlatch (u8 old, u8 new, struct controller *ctrl) | ||
1022 | { | ||
1023 | struct slot myslot, *pslot; | ||
1024 | u8 i; | ||
1025 | u8 mask; | ||
1026 | int rc = 0; | ||
1027 | |||
1028 | debug ("%s - Entry old[%x], new[%x]\n", __FUNCTION__, old, new); | ||
1029 | // bit 0 reserved, 0 is LSB, check bit 1-6 for 6 slots | ||
1030 | |||
1031 | for (i = ctrl->starting_slot_num; i <= ctrl->ending_slot_num; i++) { | ||
1032 | mask = 0x01 << i; | ||
1033 | if ((mask & old) != (mask & new)) { | ||
1034 | pslot = ibmphp_get_slot_from_physical_num (i); | ||
1035 | if (pslot) { | ||
1036 | memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot)); | ||
1037 | rc = ibmphp_hpc_readslot (pslot, READ_ALLSTAT, NULL); | ||
1038 | debug ("%s - call process_changeinstatus for slot[%d]\n", __FUNCTION__, i); | ||
1039 | process_changeinstatus (pslot, &myslot); | ||
1040 | } else { | ||
1041 | rc = -EINVAL; | ||
1042 | err ("%s - Error bad pointer for slot[%d]\n", __FUNCTION__, i); | ||
1043 | } | ||
1044 | } | ||
1045 | } | ||
1046 | debug ("%s - Exit rc[%d]\n", __FUNCTION__, rc); | ||
1047 | return rc; | ||
1048 | } | ||
1049 | |||
1050 | /*---------------------------------------------------------------------- | ||
1051 | * Name: hpc_poll_thread | ||
1052 | * | ||
1053 | * Action: polling | ||
1054 | * | ||
1055 | * Return 0 | ||
1056 | * Value: | ||
1057 | *---------------------------------------------------------------------*/ | ||
1058 | static int hpc_poll_thread (void *data) | ||
1059 | { | ||
1060 | debug ("%s - Entry\n", __FUNCTION__); | ||
1061 | |||
1062 | daemonize("hpc_poll"); | ||
1063 | allow_signal(SIGKILL); | ||
1064 | |||
1065 | poll_hpc (); | ||
1066 | |||
1067 | tid_poll = 0; | ||
1068 | debug ("%s - Exit\n", __FUNCTION__); | ||
1069 | return 0; | ||
1070 | } | ||
1071 | |||
1072 | |||
1073 | /*---------------------------------------------------------------------- | ||
1074 | * Name: ibmphp_hpc_start_poll_thread | ||
1075 | * | ||
1076 | * Action: start polling thread | ||
1077 | *---------------------------------------------------------------------*/ | ||
1078 | int __init ibmphp_hpc_start_poll_thread (void) | ||
1079 | { | ||
1080 | int rc = 0; | ||
1081 | |||
1082 | debug ("%s - Entry\n", __FUNCTION__); | ||
1083 | |||
1084 | tid_poll = kernel_thread (hpc_poll_thread, NULL, 0); | ||
1085 | if (tid_poll < 0) { | ||
1086 | err ("%s - Error, thread not started\n", __FUNCTION__); | ||
1087 | rc = -1; | ||
1088 | } | ||
1089 | |||
1090 | debug ("%s - Exit tid_poll[%d] rc[%d]\n", __FUNCTION__, tid_poll, rc); | ||
1091 | return rc; | ||
1092 | } | ||
1093 | |||
1094 | /*---------------------------------------------------------------------- | ||
1095 | * Name: ibmphp_hpc_stop_poll_thread | ||
1096 | * | ||
1097 | * Action: stop polling thread and cleanup | ||
1098 | *---------------------------------------------------------------------*/ | ||
1099 | void __exit ibmphp_hpc_stop_poll_thread (void) | ||
1100 | { | ||
1101 | debug ("%s - Entry\n", __FUNCTION__); | ||
1102 | |||
1103 | ibmphp_shutdown = TRUE; | ||
1104 | debug ("before locking operations \n"); | ||
1105 | ibmphp_lock_operations (); | ||
1106 | debug ("after locking operations \n"); | ||
1107 | |||
1108 | // wait for poll thread to exit | ||
1109 | debug ("before sem_exit down \n"); | ||
1110 | down (&sem_exit); | ||
1111 | debug ("after sem_exit down \n"); | ||
1112 | |||
1113 | // cleanup | ||
1114 | debug ("before free_hpc_access \n"); | ||
1115 | free_hpc_access (); | ||
1116 | debug ("after free_hpc_access \n"); | ||
1117 | ibmphp_unlock_operations (); | ||
1118 | debug ("after unlock operations \n"); | ||
1119 | up (&sem_exit); | ||
1120 | debug ("after sem exit up\n"); | ||
1121 | |||
1122 | debug ("%s - Exit\n", __FUNCTION__); | ||
1123 | } | ||
1124 | |||
1125 | /*---------------------------------------------------------------------- | ||
1126 | * Name: hpc_wait_ctlr_notworking | ||
1127 | * | ||
1128 | * Action: wait until the controller is in a not working state | ||
1129 | * | ||
1130 | * Return 0, HPC_ERROR | ||
1131 | * Value: | ||
1132 | *---------------------------------------------------------------------*/ | ||
1133 | static int hpc_wait_ctlr_notworking (int timeout, struct controller *ctlr_ptr, void __iomem *wpg_bbar, | ||
1134 | u8 * pstatus) | ||
1135 | { | ||
1136 | int rc = 0; | ||
1137 | u8 done = FALSE; | ||
1138 | |||
1139 | debug_polling ("hpc_wait_ctlr_notworking - Entry timeout[%d]\n", timeout); | ||
1140 | |||
1141 | while (!done) { | ||
1142 | *pstatus = ctrl_read (ctlr_ptr, wpg_bbar, WPG_CTLR_INDEX); | ||
1143 | if (*pstatus == HPC_ERROR) { | ||
1144 | rc = HPC_ERROR; | ||
1145 | done = TRUE; | ||
1146 | } | ||
1147 | if (CTLR_WORKING (*pstatus) == HPC_CTLR_WORKING_NO) | ||
1148 | done = TRUE; | ||
1149 | if (!done) { | ||
1150 | msleep(1000); | ||
1151 | if (timeout < 1) { | ||
1152 | done = TRUE; | ||
1153 | err ("HPCreadslot - Error ctlr timeout\n"); | ||
1154 | rc = HPC_ERROR; | ||
1155 | } else | ||
1156 | timeout--; | ||
1157 | } | ||
1158 | } | ||
1159 | debug_polling ("hpc_wait_ctlr_notworking - Exit rc[%x] status[%x]\n", rc, *pstatus); | ||
1160 | return rc; | ||
1161 | } | ||
diff --git a/drivers/pci/hotplug/ibmphp_pci.c b/drivers/pci/hotplug/ibmphp_pci.c new file mode 100644 index 000000000000..2335fac65fb4 --- /dev/null +++ b/drivers/pci/hotplug/ibmphp_pci.c | |||
@@ -0,0 +1,1747 @@ | |||
1 | /* | ||
2 | * IBM Hot Plug Controller Driver | ||
3 | * | ||
4 | * Written By: Irene Zubarev, IBM Corporation | ||
5 | * | ||
6 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
7 | * Copyright (C) 2001,2002 IBM Corp. | ||
8 | * | ||
9 | * All rights reserved. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or (at | ||
14 | * your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
19 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
20 | * details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
25 | * | ||
26 | * Send feedback to <gregkh@us.ibm.com> | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | #include <linux/module.h> | ||
31 | #include <linux/slab.h> | ||
32 | #include <linux/pci.h> | ||
33 | #include <linux/list.h> | ||
34 | #include "ibmphp.h" | ||
35 | |||
36 | |||
37 | static int configure_device(struct pci_func *); | ||
38 | static int configure_bridge(struct pci_func **, u8); | ||
39 | static struct res_needed *scan_behind_bridge(struct pci_func *, u8); | ||
40 | static int add_new_bus (struct bus_node *, struct resource_node *, struct resource_node *, struct resource_node *, u8); | ||
41 | static u8 find_sec_number (u8 primary_busno, u8 slotno); | ||
42 | |||
43 | /* | ||
44 | * NOTE..... If BIOS doesn't provide default routing, we assign: | ||
45 | * 9 for SCSI, 10 for LAN adapters, and 11 for everything else. | ||
46 | * If adapter is bridged, then we assign 11 to it and devices behind it. | ||
47 | * We also assign the same irq numbers for multi function devices. | ||
48 | * These are PIC mode, so shouldn't matter n.e.ways (hopefully) | ||
49 | */ | ||
50 | static void assign_alt_irq (struct pci_func * cur_func, u8 class_code) | ||
51 | { | ||
52 | int j; | ||
53 | for (j = 0; j < 4; j++) { | ||
54 | if (cur_func->irq[j] == 0xff) { | ||
55 | switch (class_code) { | ||
56 | case PCI_BASE_CLASS_STORAGE: | ||
57 | cur_func->irq[j] = SCSI_IRQ; | ||
58 | break; | ||
59 | case PCI_BASE_CLASS_NETWORK: | ||
60 | cur_func->irq[j] = LAN_IRQ; | ||
61 | break; | ||
62 | default: | ||
63 | cur_func->irq[j] = OTHER_IRQ; | ||
64 | break; | ||
65 | } | ||
66 | } | ||
67 | } | ||
68 | } | ||
69 | |||
70 | /* | ||
71 | * Configures the device to be added (will allocate needed resources if it | ||
72 | * can), the device can be a bridge or a regular pci device, can also be | ||
73 | * multi-functional | ||
74 | * | ||
75 | * Input: function to be added | ||
76 | * | ||
77 | * TO DO: The error case with Multifunction device or multi function bridge, | ||
78 | * if there is an error, will need to go through all previous functions and | ||
79 | * unconfigure....or can add some code into unconfigure_card.... | ||
80 | */ | ||
81 | int ibmphp_configure_card (struct pci_func *func, u8 slotno) | ||
82 | { | ||
83 | u16 vendor_id; | ||
84 | u32 class; | ||
85 | u8 class_code; | ||
86 | u8 hdr_type, device, sec_number; | ||
87 | u8 function; | ||
88 | struct pci_func *newfunc; /* for multi devices */ | ||
89 | struct pci_func *cur_func, *prev_func; | ||
90 | int rc, i, j; | ||
91 | int cleanup_count; | ||
92 | u8 flag; | ||
93 | u8 valid_device = 0x00; /* to see if we are able to read from card any device info at all */ | ||
94 | |||
95 | debug ("inside configure_card, func->busno = %x\n", func->busno); | ||
96 | |||
97 | device = func->device; | ||
98 | cur_func = func; | ||
99 | |||
100 | /* We only get bus and device from IRQ routing table. So at this point, | ||
101 | * func->busno is correct, and func->device contains only device (at the 5 | ||
102 | * highest bits) | ||
103 | */ | ||
104 | |||
105 | /* For every function on the card */ | ||
106 | for (function = 0x00; function < 0x08; function++) { | ||
107 | unsigned int devfn = PCI_DEVFN(device, function); | ||
108 | ibmphp_pci_bus->number = cur_func->busno; | ||
109 | |||
110 | cur_func->function = function; | ||
111 | |||
112 | debug ("inside the loop, cur_func->busno = %x, cur_func->device = %x, cur_func->funcion = %x\n", | ||
113 | cur_func->busno, cur_func->device, cur_func->function); | ||
114 | |||
115 | pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_VENDOR_ID, &vendor_id); | ||
116 | |||
117 | debug ("vendor_id is %x\n", vendor_id); | ||
118 | if (vendor_id != PCI_VENDOR_ID_NOTVALID) { | ||
119 | /* found correct device!!! */ | ||
120 | debug ("found valid device, vendor_id = %x\n", vendor_id); | ||
121 | |||
122 | ++valid_device; | ||
123 | |||
124 | /* header: x x x x x x x x | ||
125 | * | |___________|=> 1=PPB bridge, 0=normal device, 2=CardBus Bridge | ||
126 | * |_=> 0 = single function device, 1 = multi-function device | ||
127 | */ | ||
128 | |||
129 | pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_HEADER_TYPE, &hdr_type); | ||
130 | pci_bus_read_config_dword (ibmphp_pci_bus, devfn, PCI_CLASS_REVISION, &class); | ||
131 | |||
132 | class_code = class >> 24; | ||
133 | debug ("hrd_type = %x, class = %x, class_code %x\n", hdr_type, class, class_code); | ||
134 | class >>= 8; /* to take revision out, class = class.subclass.prog i/f */ | ||
135 | if (class == PCI_CLASS_NOT_DEFINED_VGA) { | ||
136 | err ("The device %x is VGA compatible and as is not supported for hot plugging. " | ||
137 | "Please choose another device.\n", cur_func->device); | ||
138 | return -ENODEV; | ||
139 | } else if (class == PCI_CLASS_DISPLAY_VGA) { | ||
140 | err ("The device %x is not supported for hot plugging. " | ||
141 | "Please choose another device.\n", cur_func->device); | ||
142 | return -ENODEV; | ||
143 | } | ||
144 | switch (hdr_type) { | ||
145 | case PCI_HEADER_TYPE_NORMAL: | ||
146 | debug ("single device case.... vendor id = %x, hdr_type = %x, class = %x\n", vendor_id, hdr_type, class); | ||
147 | assign_alt_irq (cur_func, class_code); | ||
148 | if ((rc = configure_device (cur_func)) < 0) { | ||
149 | /* We need to do this in case some other BARs were properly inserted */ | ||
150 | err ("was not able to configure devfunc %x on bus %x.\n", | ||
151 | cur_func->device, cur_func->busno); | ||
152 | cleanup_count = 6; | ||
153 | goto error; | ||
154 | } | ||
155 | cur_func->next = NULL; | ||
156 | function = 0x8; | ||
157 | break; | ||
158 | case PCI_HEADER_TYPE_MULTIDEVICE: | ||
159 | assign_alt_irq (cur_func, class_code); | ||
160 | if ((rc = configure_device (cur_func)) < 0) { | ||
161 | /* We need to do this in case some other BARs were properly inserted */ | ||
162 | err ("was not able to configure devfunc %x on bus %x...bailing out\n", | ||
163 | cur_func->device, cur_func->busno); | ||
164 | cleanup_count = 6; | ||
165 | goto error; | ||
166 | } | ||
167 | newfunc = kmalloc(sizeof(*newfunc), GFP_KERNEL); | ||
168 | if (!newfunc) { | ||
169 | err ("out of system memory\n"); | ||
170 | return -ENOMEM; | ||
171 | } | ||
172 | memset (newfunc, 0, sizeof (struct pci_func)); | ||
173 | newfunc->busno = cur_func->busno; | ||
174 | newfunc->device = device; | ||
175 | cur_func->next = newfunc; | ||
176 | cur_func = newfunc; | ||
177 | for (j = 0; j < 4; j++) | ||
178 | newfunc->irq[j] = cur_func->irq[j]; | ||
179 | break; | ||
180 | case PCI_HEADER_TYPE_MULTIBRIDGE: | ||
181 | class >>= 8; | ||
182 | if (class != PCI_CLASS_BRIDGE_PCI) { | ||
183 | err ("This %x is not PCI-to-PCI bridge, and as is not supported for hot-plugging. " | ||
184 | "Please insert another card.\n", cur_func->device); | ||
185 | return -ENODEV; | ||
186 | } | ||
187 | assign_alt_irq (cur_func, class_code); | ||
188 | rc = configure_bridge (&cur_func, slotno); | ||
189 | if (rc == -ENODEV) { | ||
190 | err ("You chose to insert Single Bridge, or nested bridges, this is not supported...\n"); | ||
191 | err ("Bus %x, devfunc %x\n", cur_func->busno, cur_func->device); | ||
192 | return rc; | ||
193 | } | ||
194 | if (rc) { | ||
195 | /* We need to do this in case some other BARs were properly inserted */ | ||
196 | err ("was not able to hot-add PPB properly.\n"); | ||
197 | func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */ | ||
198 | cleanup_count = 2; | ||
199 | goto error; | ||
200 | } | ||
201 | |||
202 | pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_number); | ||
203 | flag = FALSE; | ||
204 | for (i = 0; i < 32; i++) { | ||
205 | if (func->devices[i]) { | ||
206 | newfunc = kmalloc(sizeof(*newfunc), GFP_KERNEL); | ||
207 | if (!newfunc) { | ||
208 | err ("out of system memory\n"); | ||
209 | return -ENOMEM; | ||
210 | } | ||
211 | memset (newfunc, 0, sizeof (struct pci_func)); | ||
212 | newfunc->busno = sec_number; | ||
213 | newfunc->device = (u8) i; | ||
214 | for (j = 0; j < 4; j++) | ||
215 | newfunc->irq[j] = cur_func->irq[j]; | ||
216 | |||
217 | if (flag) { | ||
218 | for (prev_func = cur_func; prev_func->next; prev_func = prev_func->next) ; | ||
219 | prev_func->next = newfunc; | ||
220 | } else | ||
221 | cur_func->next = newfunc; | ||
222 | |||
223 | rc = ibmphp_configure_card (newfunc, slotno); | ||
224 | /* This could only happen if kmalloc failed */ | ||
225 | if (rc) { | ||
226 | /* We need to do this in case bridge itself got configured properly, but devices behind it failed */ | ||
227 | func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */ | ||
228 | cleanup_count = 2; | ||
229 | goto error; | ||
230 | } | ||
231 | flag = TRUE; | ||
232 | } | ||
233 | } | ||
234 | |||
235 | newfunc = kmalloc(sizeof(*newfunc), GFP_KERNEL); | ||
236 | if (!newfunc) { | ||
237 | err ("out of system memory\n"); | ||
238 | return -ENOMEM; | ||
239 | } | ||
240 | memset (newfunc, 0, sizeof (struct pci_func)); | ||
241 | newfunc->busno = cur_func->busno; | ||
242 | newfunc->device = device; | ||
243 | for (j = 0; j < 4; j++) | ||
244 | newfunc->irq[j] = cur_func->irq[j]; | ||
245 | for (prev_func = cur_func; prev_func->next; prev_func = prev_func->next) ; | ||
246 | prev_func->next = newfunc; | ||
247 | cur_func = newfunc; | ||
248 | break; | ||
249 | case PCI_HEADER_TYPE_BRIDGE: | ||
250 | class >>= 8; | ||
251 | debug ("class now is %x\n", class); | ||
252 | if (class != PCI_CLASS_BRIDGE_PCI) { | ||
253 | err ("This %x is not PCI-to-PCI bridge, and as is not supported for hot-plugging. " | ||
254 | "Please insert another card.\n", cur_func->device); | ||
255 | return -ENODEV; | ||
256 | } | ||
257 | |||
258 | assign_alt_irq (cur_func, class_code); | ||
259 | |||
260 | debug ("cur_func->busno b4 configure_bridge is %x\n", cur_func->busno); | ||
261 | rc = configure_bridge (&cur_func, slotno); | ||
262 | if (rc == -ENODEV) { | ||
263 | err ("You chose to insert Single Bridge, or nested bridges, this is not supported...\n"); | ||
264 | err ("Bus %x, devfunc %x\n", cur_func->busno, cur_func->device); | ||
265 | return rc; | ||
266 | } | ||
267 | if (rc) { | ||
268 | /* We need to do this in case some other BARs were properly inserted */ | ||
269 | func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */ | ||
270 | err ("was not able to hot-add PPB properly.\n"); | ||
271 | cleanup_count = 2; | ||
272 | goto error; | ||
273 | } | ||
274 | debug ("cur_func->busno = %x, device = %x, function = %x\n", | ||
275 | cur_func->busno, device, function); | ||
276 | pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_number); | ||
277 | debug ("after configuring bridge..., sec_number = %x\n", sec_number); | ||
278 | flag = FALSE; | ||
279 | for (i = 0; i < 32; i++) { | ||
280 | if (func->devices[i]) { | ||
281 | debug ("inside for loop, device is %x\n", i); | ||
282 | newfunc = kmalloc(sizeof(*newfunc), GFP_KERNEL); | ||
283 | if (!newfunc) { | ||
284 | err (" out of system memory\n"); | ||
285 | return -ENOMEM; | ||
286 | } | ||
287 | memset (newfunc, 0, sizeof (struct pci_func)); | ||
288 | newfunc->busno = sec_number; | ||
289 | newfunc->device = (u8) i; | ||
290 | for (j = 0; j < 4; j++) | ||
291 | newfunc->irq[j] = cur_func->irq[j]; | ||
292 | |||
293 | if (flag) { | ||
294 | for (prev_func = cur_func; prev_func->next; prev_func = prev_func->next) ; | ||
295 | prev_func->next = newfunc; | ||
296 | } else | ||
297 | cur_func->next = newfunc; | ||
298 | |||
299 | rc = ibmphp_configure_card (newfunc, slotno); | ||
300 | |||
301 | /* Again, this case should not happen... For complete paranoia, will need to call remove_bus */ | ||
302 | if (rc) { | ||
303 | /* We need to do this in case some other BARs were properly inserted */ | ||
304 | func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */ | ||
305 | cleanup_count = 2; | ||
306 | goto error; | ||
307 | } | ||
308 | flag = TRUE; | ||
309 | } | ||
310 | } | ||
311 | |||
312 | function = 0x8; | ||
313 | break; | ||
314 | default: | ||
315 | err ("MAJOR PROBLEM!!!!, header type not supported? %x\n", hdr_type); | ||
316 | return -ENXIO; | ||
317 | break; | ||
318 | } /* end of switch */ | ||
319 | } /* end of valid device */ | ||
320 | } /* end of for */ | ||
321 | |||
322 | if (!valid_device) { | ||
323 | err ("Cannot find any valid devices on the card. Or unable to read from card.\n"); | ||
324 | return -ENODEV; | ||
325 | } | ||
326 | |||
327 | return 0; | ||
328 | |||
329 | error: | ||
330 | for (i = 0; i < cleanup_count; i++) { | ||
331 | if (cur_func->io[i]) { | ||
332 | ibmphp_remove_resource (cur_func->io[i]); | ||
333 | cur_func->io[i] = NULL; | ||
334 | } else if (cur_func->pfmem[i]) { | ||
335 | ibmphp_remove_resource (cur_func->pfmem[i]); | ||
336 | cur_func->pfmem[i] = NULL; | ||
337 | } else if (cur_func->mem[i]) { | ||
338 | ibmphp_remove_resource (cur_func->mem[i]); | ||
339 | cur_func->mem[i] = NULL; | ||
340 | } | ||
341 | } | ||
342 | return rc; | ||
343 | } | ||
344 | |||
345 | /* | ||
346 | * This function configures the pci BARs of a single device. | ||
347 | * Input: pointer to the pci_func | ||
348 | * Output: configured PCI, 0, or error | ||
349 | */ | ||
350 | static int configure_device (struct pci_func *func) | ||
351 | { | ||
352 | u32 bar[6]; | ||
353 | u32 address[] = { | ||
354 | PCI_BASE_ADDRESS_0, | ||
355 | PCI_BASE_ADDRESS_1, | ||
356 | PCI_BASE_ADDRESS_2, | ||
357 | PCI_BASE_ADDRESS_3, | ||
358 | PCI_BASE_ADDRESS_4, | ||
359 | PCI_BASE_ADDRESS_5, | ||
360 | 0 | ||
361 | }; | ||
362 | u8 irq; | ||
363 | int count; | ||
364 | int len[6]; | ||
365 | struct resource_node *io[6]; | ||
366 | struct resource_node *mem[6]; | ||
367 | struct resource_node *mem_tmp; | ||
368 | struct resource_node *pfmem[6]; | ||
369 | unsigned int devfn; | ||
370 | |||
371 | debug ("%s - inside\n", __FUNCTION__); | ||
372 | |||
373 | devfn = PCI_DEVFN(func->device, func->function); | ||
374 | ibmphp_pci_bus->number = func->busno; | ||
375 | |||
376 | for (count = 0; address[count]; count++) { /* for 6 BARs */ | ||
377 | |||
378 | /* not sure if i need this. per scott, said maybe need smth like this | ||
379 | if devices don't adhere 100% to the spec, so don't want to write | ||
380 | to the reserved bits | ||
381 | |||
382 | pcibios_read_config_byte(cur_func->busno, cur_func->device, | ||
383 | PCI_BASE_ADDRESS_0 + 4 * count, &tmp); | ||
384 | if (tmp & 0x01) // IO | ||
385 | pcibios_write_config_dword(cur_func->busno, cur_func->device, | ||
386 | PCI_BASE_ADDRESS_0 + 4 * count, 0xFFFFFFFD); | ||
387 | else // Memory | ||
388 | pcibios_write_config_dword(cur_func->busno, cur_func->device, | ||
389 | PCI_BASE_ADDRESS_0 + 4 * count, 0xFFFFFFFF); | ||
390 | */ | ||
391 | pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFF); | ||
392 | pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &bar[count]); | ||
393 | |||
394 | if (!bar[count]) /* This BAR is not implemented */ | ||
395 | continue; | ||
396 | |||
397 | debug ("Device %x BAR %d wants %x\n", func->device, count, bar[count]); | ||
398 | |||
399 | if (bar[count] & PCI_BASE_ADDRESS_SPACE_IO) { | ||
400 | /* This is IO */ | ||
401 | debug ("inside IO SPACE\n"); | ||
402 | |||
403 | len[count] = bar[count] & 0xFFFFFFFC; | ||
404 | len[count] = ~len[count] + 1; | ||
405 | |||
406 | debug ("len[count] in IO %x, count %d\n", len[count], count); | ||
407 | |||
408 | io[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL); | ||
409 | |||
410 | if (!io[count]) { | ||
411 | err ("out of system memory\n"); | ||
412 | return -ENOMEM; | ||
413 | } | ||
414 | memset (io[count], 0, sizeof (struct resource_node)); | ||
415 | io[count]->type = IO; | ||
416 | io[count]->busno = func->busno; | ||
417 | io[count]->devfunc = PCI_DEVFN(func->device, func->function); | ||
418 | io[count]->len = len[count]; | ||
419 | if (ibmphp_check_resource(io[count], 0) == 0) { | ||
420 | ibmphp_add_resource (io[count]); | ||
421 | func->io[count] = io[count]; | ||
422 | } else { | ||
423 | err ("cannot allocate requested io for bus %x device %x function %x len %x\n", | ||
424 | func->busno, func->device, func->function, len[count]); | ||
425 | kfree (io[count]); | ||
426 | return -EIO; | ||
427 | } | ||
428 | pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], func->io[count]->start); | ||
429 | |||
430 | /* _______________This is for debugging purposes only_____________________ */ | ||
431 | debug ("b4 writing, the IO address is %x\n", func->io[count]->start); | ||
432 | pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &bar[count]); | ||
433 | debug ("after writing.... the start address is %x\n", bar[count]); | ||
434 | /* _________________________________________________________________________*/ | ||
435 | |||
436 | } else { | ||
437 | /* This is Memory */ | ||
438 | if (bar[count] & PCI_BASE_ADDRESS_MEM_PREFETCH) { | ||
439 | /* pfmem */ | ||
440 | debug ("PFMEM SPACE\n"); | ||
441 | |||
442 | len[count] = bar[count] & 0xFFFFFFF0; | ||
443 | len[count] = ~len[count] + 1; | ||
444 | |||
445 | debug ("len[count] in PFMEM %x, count %d\n", len[count], count); | ||
446 | |||
447 | pfmem[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL); | ||
448 | if (!pfmem[count]) { | ||
449 | err ("out of system memory\n"); | ||
450 | return -ENOMEM; | ||
451 | } | ||
452 | memset (pfmem[count], 0, sizeof (struct resource_node)); | ||
453 | pfmem[count]->type = PFMEM; | ||
454 | pfmem[count]->busno = func->busno; | ||
455 | pfmem[count]->devfunc = PCI_DEVFN(func->device, | ||
456 | func->function); | ||
457 | pfmem[count]->len = len[count]; | ||
458 | pfmem[count]->fromMem = FALSE; | ||
459 | if (ibmphp_check_resource (pfmem[count], 0) == 0) { | ||
460 | ibmphp_add_resource (pfmem[count]); | ||
461 | func->pfmem[count] = pfmem[count]; | ||
462 | } else { | ||
463 | mem_tmp = kmalloc(sizeof(*mem_tmp), GFP_KERNEL); | ||
464 | if (!mem_tmp) { | ||
465 | err ("out of system memory\n"); | ||
466 | kfree (pfmem[count]); | ||
467 | return -ENOMEM; | ||
468 | } | ||
469 | memset (mem_tmp, 0, sizeof (struct resource_node)); | ||
470 | mem_tmp->type = MEM; | ||
471 | mem_tmp->busno = pfmem[count]->busno; | ||
472 | mem_tmp->devfunc = pfmem[count]->devfunc; | ||
473 | mem_tmp->len = pfmem[count]->len; | ||
474 | debug ("there's no pfmem... going into mem.\n"); | ||
475 | if (ibmphp_check_resource (mem_tmp, 0) == 0) { | ||
476 | ibmphp_add_resource (mem_tmp); | ||
477 | pfmem[count]->fromMem = TRUE; | ||
478 | pfmem[count]->rangeno = mem_tmp->rangeno; | ||
479 | pfmem[count]->start = mem_tmp->start; | ||
480 | pfmem[count]->end = mem_tmp->end; | ||
481 | ibmphp_add_pfmem_from_mem (pfmem[count]); | ||
482 | func->pfmem[count] = pfmem[count]; | ||
483 | } else { | ||
484 | err ("cannot allocate requested pfmem for bus %x, device %x, len %x\n", | ||
485 | func->busno, func->device, len[count]); | ||
486 | kfree (mem_tmp); | ||
487 | kfree (pfmem[count]); | ||
488 | return -EIO; | ||
489 | } | ||
490 | } | ||
491 | |||
492 | pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], func->pfmem[count]->start); | ||
493 | |||
494 | /*_______________This is for debugging purposes only______________________________*/ | ||
495 | debug ("b4 writing, start address is %x\n", func->pfmem[count]->start); | ||
496 | pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &bar[count]); | ||
497 | debug ("after writing, start address is %x\n", bar[count]); | ||
498 | /*_________________________________________________________________________________*/ | ||
499 | |||
500 | if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) { /* takes up another dword */ | ||
501 | debug ("inside the mem 64 case, count %d\n", count); | ||
502 | count += 1; | ||
503 | /* on the 2nd dword, write all 0s, since we can't handle them n.e.ways */ | ||
504 | pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0x00000000); | ||
505 | } | ||
506 | } else { | ||
507 | /* regular memory */ | ||
508 | debug ("REGULAR MEM SPACE\n"); | ||
509 | |||
510 | len[count] = bar[count] & 0xFFFFFFF0; | ||
511 | len[count] = ~len[count] + 1; | ||
512 | |||
513 | debug ("len[count] in Mem %x, count %d\n", len[count], count); | ||
514 | |||
515 | mem[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL); | ||
516 | if (!mem[count]) { | ||
517 | err ("out of system memory\n"); | ||
518 | return -ENOMEM; | ||
519 | } | ||
520 | memset (mem[count], 0, sizeof (struct resource_node)); | ||
521 | mem[count]->type = MEM; | ||
522 | mem[count]->busno = func->busno; | ||
523 | mem[count]->devfunc = PCI_DEVFN(func->device, | ||
524 | func->function); | ||
525 | mem[count]->len = len[count]; | ||
526 | if (ibmphp_check_resource (mem[count], 0) == 0) { | ||
527 | ibmphp_add_resource (mem[count]); | ||
528 | func->mem[count] = mem[count]; | ||
529 | } else { | ||
530 | err ("cannot allocate requested mem for bus %x, device %x, len %x\n", | ||
531 | func->busno, func->device, len[count]); | ||
532 | kfree (mem[count]); | ||
533 | return -EIO; | ||
534 | } | ||
535 | pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], func->mem[count]->start); | ||
536 | /* _______________________This is for debugging purposes only _______________________*/ | ||
537 | debug ("b4 writing, start address is %x\n", func->mem[count]->start); | ||
538 | pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &bar[count]); | ||
539 | debug ("after writing, the address is %x\n", bar[count]); | ||
540 | /* __________________________________________________________________________________*/ | ||
541 | |||
542 | if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) { | ||
543 | /* takes up another dword */ | ||
544 | debug ("inside mem 64 case, reg. mem, count %d\n", count); | ||
545 | count += 1; | ||
546 | /* on the 2nd dword, write all 0s, since we can't handle them n.e.ways */ | ||
547 | pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0x00000000); | ||
548 | } | ||
549 | } | ||
550 | } /* end of mem */ | ||
551 | } /* end of for */ | ||
552 | |||
553 | func->bus = 0; /* To indicate that this is not a PPB */ | ||
554 | pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_INTERRUPT_PIN, &irq); | ||
555 | if ((irq > 0x00) && (irq < 0x05)) | ||
556 | pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_INTERRUPT_LINE, func->irq[irq - 1]); | ||
557 | |||
558 | pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_CACHE_LINE_SIZE, CACHE); | ||
559 | pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_LATENCY_TIMER, LATENCY); | ||
560 | |||
561 | pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_ROM_ADDRESS, 0x00L); | ||
562 | pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_COMMAND, DEVICEENABLE); | ||
563 | |||
564 | return 0; | ||
565 | } | ||
566 | |||
567 | /****************************************************************************** | ||
568 | * This routine configures a PCI-2-PCI bridge and the functions behind it | ||
569 | * Parameters: pci_func | ||
570 | * Returns: | ||
571 | ******************************************************************************/ | ||
572 | static int configure_bridge (struct pci_func **func_passed, u8 slotno) | ||
573 | { | ||
574 | int count; | ||
575 | int i; | ||
576 | int rc; | ||
577 | u8 sec_number; | ||
578 | u8 io_base; | ||
579 | u16 pfmem_base; | ||
580 | u32 bar[2]; | ||
581 | u32 len[2]; | ||
582 | u8 flag_io = FALSE; | ||
583 | u8 flag_mem = FALSE; | ||
584 | u8 flag_pfmem = FALSE; | ||
585 | u8 need_io_upper = FALSE; | ||
586 | u8 need_pfmem_upper = FALSE; | ||
587 | struct res_needed *amount_needed = NULL; | ||
588 | struct resource_node *io = NULL; | ||
589 | struct resource_node *bus_io[2] = {NULL, NULL}; | ||
590 | struct resource_node *mem = NULL; | ||
591 | struct resource_node *bus_mem[2] = {NULL, NULL}; | ||
592 | struct resource_node *mem_tmp = NULL; | ||
593 | struct resource_node *pfmem = NULL; | ||
594 | struct resource_node *bus_pfmem[2] = {NULL, NULL}; | ||
595 | struct bus_node *bus; | ||
596 | u32 address[] = { | ||
597 | PCI_BASE_ADDRESS_0, | ||
598 | PCI_BASE_ADDRESS_1, | ||
599 | 0 | ||
600 | }; | ||
601 | struct pci_func *func = *func_passed; | ||
602 | unsigned int devfn; | ||
603 | u8 irq; | ||
604 | int retval; | ||
605 | |||
606 | debug ("%s - enter\n", __FUNCTION__); | ||
607 | |||
608 | devfn = PCI_DEVFN(func->function, func->device); | ||
609 | ibmphp_pci_bus->number = func->busno; | ||
610 | |||
611 | /* Configuring necessary info for the bridge so that we could see the devices | ||
612 | * behind it | ||
613 | */ | ||
614 | |||
615 | pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_PRIMARY_BUS, func->busno); | ||
616 | |||
617 | /* _____________________For debugging purposes only __________________________ | ||
618 | pci_bus_config_byte (ibmphp_pci_bus, devfn, PCI_PRIMARY_BUS, &pri_number); | ||
619 | debug ("primary # written into the bridge is %x\n", pri_number); | ||
620 | ___________________________________________________________________________*/ | ||
621 | |||
622 | /* in EBDA, only get allocated 1 additional bus # per slot */ | ||
623 | sec_number = find_sec_number (func->busno, slotno); | ||
624 | if (sec_number == 0xff) { | ||
625 | err ("cannot allocate secondary bus number for the bridged device\n"); | ||
626 | return -EINVAL; | ||
627 | } | ||
628 | |||
629 | debug ("after find_sec_number, the number we got is %x\n", sec_number); | ||
630 | debug ("AFTER FIND_SEC_NUMBER, func->busno IS %x\n", func->busno); | ||
631 | |||
632 | pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, sec_number); | ||
633 | |||
634 | /* __________________For debugging purposes only __________________________________ | ||
635 | pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_number); | ||
636 | debug ("sec_number after write/read is %x\n", sec_number); | ||
637 | ________________________________________________________________________________*/ | ||
638 | |||
639 | pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_SUBORDINATE_BUS, sec_number); | ||
640 | |||
641 | /* __________________For debugging purposes only ____________________________________ | ||
642 | pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SUBORDINATE_BUS, &sec_number); | ||
643 | debug ("subordinate number after write/read is %x\n", sec_number); | ||
644 | __________________________________________________________________________________*/ | ||
645 | |||
646 | pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_CACHE_LINE_SIZE, CACHE); | ||
647 | pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_LATENCY_TIMER, LATENCY); | ||
648 | pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_SEC_LATENCY_TIMER, LATENCY); | ||
649 | |||
650 | debug ("func->busno is %x\n", func->busno); | ||
651 | debug ("sec_number after writing is %x\n", sec_number); | ||
652 | |||
653 | |||
654 | /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
655 | !!!!!!!!!!!!!!!NEED TO ADD!!! FAST BACK-TO-BACK ENABLE!!!!!!!!!!!!!!!!!!!! | ||
656 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ | ||
657 | |||
658 | |||
659 | /* First we need to allocate mem/io for the bridge itself in case it needs it */ | ||
660 | for (count = 0; address[count]; count++) { /* for 2 BARs */ | ||
661 | pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFF); | ||
662 | pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &bar[count]); | ||
663 | |||
664 | if (!bar[count]) { | ||
665 | /* This BAR is not implemented */ | ||
666 | debug ("so we come here then, eh?, count = %d\n", count); | ||
667 | continue; | ||
668 | } | ||
669 | // tmp_bar = bar[count]; | ||
670 | |||
671 | debug ("Bar %d wants %x\n", count, bar[count]); | ||
672 | |||
673 | if (bar[count] & PCI_BASE_ADDRESS_SPACE_IO) { | ||
674 | /* This is IO */ | ||
675 | len[count] = bar[count] & 0xFFFFFFFC; | ||
676 | len[count] = ~len[count] + 1; | ||
677 | |||
678 | debug ("len[count] in IO = %x\n", len[count]); | ||
679 | |||
680 | bus_io[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL); | ||
681 | |||
682 | if (!bus_io[count]) { | ||
683 | err ("out of system memory\n"); | ||
684 | retval = -ENOMEM; | ||
685 | goto error; | ||
686 | } | ||
687 | memset (bus_io[count], 0, sizeof (struct resource_node)); | ||
688 | bus_io[count]->type = IO; | ||
689 | bus_io[count]->busno = func->busno; | ||
690 | bus_io[count]->devfunc = PCI_DEVFN(func->device, | ||
691 | func->function); | ||
692 | bus_io[count]->len = len[count]; | ||
693 | if (ibmphp_check_resource (bus_io[count], 0) == 0) { | ||
694 | ibmphp_add_resource (bus_io[count]); | ||
695 | func->io[count] = bus_io[count]; | ||
696 | } else { | ||
697 | err ("cannot allocate requested io for bus %x, device %x, len %x\n", | ||
698 | func->busno, func->device, len[count]); | ||
699 | kfree (bus_io[count]); | ||
700 | return -EIO; | ||
701 | } | ||
702 | |||
703 | pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], func->io[count]->start); | ||
704 | |||
705 | } else { | ||
706 | /* This is Memory */ | ||
707 | if (bar[count] & PCI_BASE_ADDRESS_MEM_PREFETCH) { | ||
708 | /* pfmem */ | ||
709 | len[count] = bar[count] & 0xFFFFFFF0; | ||
710 | len[count] = ~len[count] + 1; | ||
711 | |||
712 | debug ("len[count] in PFMEM = %x\n", len[count]); | ||
713 | |||
714 | bus_pfmem[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL); | ||
715 | if (!bus_pfmem[count]) { | ||
716 | err ("out of system memory\n"); | ||
717 | retval = -ENOMEM; | ||
718 | goto error; | ||
719 | } | ||
720 | memset (bus_pfmem[count], 0, sizeof (struct resource_node)); | ||
721 | bus_pfmem[count]->type = PFMEM; | ||
722 | bus_pfmem[count]->busno = func->busno; | ||
723 | bus_pfmem[count]->devfunc = PCI_DEVFN(func->device, | ||
724 | func->function); | ||
725 | bus_pfmem[count]->len = len[count]; | ||
726 | bus_pfmem[count]->fromMem = FALSE; | ||
727 | if (ibmphp_check_resource (bus_pfmem[count], 0) == 0) { | ||
728 | ibmphp_add_resource (bus_pfmem[count]); | ||
729 | func->pfmem[count] = bus_pfmem[count]; | ||
730 | } else { | ||
731 | mem_tmp = kmalloc(sizeof(*mem_tmp), GFP_KERNEL); | ||
732 | if (!mem_tmp) { | ||
733 | err ("out of system memory\n"); | ||
734 | retval = -ENOMEM; | ||
735 | goto error; | ||
736 | } | ||
737 | memset (mem_tmp, 0, sizeof (struct resource_node)); | ||
738 | mem_tmp->type = MEM; | ||
739 | mem_tmp->busno = bus_pfmem[count]->busno; | ||
740 | mem_tmp->devfunc = bus_pfmem[count]->devfunc; | ||
741 | mem_tmp->len = bus_pfmem[count]->len; | ||
742 | if (ibmphp_check_resource (mem_tmp, 0) == 0) { | ||
743 | ibmphp_add_resource (mem_tmp); | ||
744 | bus_pfmem[count]->fromMem = TRUE; | ||
745 | bus_pfmem[count]->rangeno = mem_tmp->rangeno; | ||
746 | ibmphp_add_pfmem_from_mem (bus_pfmem[count]); | ||
747 | func->pfmem[count] = bus_pfmem[count]; | ||
748 | } else { | ||
749 | err ("cannot allocate requested pfmem for bus %x, device %x, len %x\n", | ||
750 | func->busno, func->device, len[count]); | ||
751 | kfree (mem_tmp); | ||
752 | kfree (bus_pfmem[count]); | ||
753 | return -EIO; | ||
754 | } | ||
755 | } | ||
756 | |||
757 | pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], func->pfmem[count]->start); | ||
758 | |||
759 | if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) { | ||
760 | /* takes up another dword */ | ||
761 | count += 1; | ||
762 | /* on the 2nd dword, write all 0s, since we can't handle them n.e.ways */ | ||
763 | pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0x00000000); | ||
764 | |||
765 | } | ||
766 | } else { | ||
767 | /* regular memory */ | ||
768 | len[count] = bar[count] & 0xFFFFFFF0; | ||
769 | len[count] = ~len[count] + 1; | ||
770 | |||
771 | debug ("len[count] in Memory is %x\n", len[count]); | ||
772 | |||
773 | bus_mem[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL); | ||
774 | if (!bus_mem[count]) { | ||
775 | err ("out of system memory\n"); | ||
776 | retval = -ENOMEM; | ||
777 | goto error; | ||
778 | } | ||
779 | memset (bus_mem[count], 0, sizeof (struct resource_node)); | ||
780 | bus_mem[count]->type = MEM; | ||
781 | bus_mem[count]->busno = func->busno; | ||
782 | bus_mem[count]->devfunc = PCI_DEVFN(func->device, | ||
783 | func->function); | ||
784 | bus_mem[count]->len = len[count]; | ||
785 | if (ibmphp_check_resource (bus_mem[count], 0) == 0) { | ||
786 | ibmphp_add_resource (bus_mem[count]); | ||
787 | func->mem[count] = bus_mem[count]; | ||
788 | } else { | ||
789 | err ("cannot allocate requested mem for bus %x, device %x, len %x\n", | ||
790 | func->busno, func->device, len[count]); | ||
791 | kfree (bus_mem[count]); | ||
792 | return -EIO; | ||
793 | } | ||
794 | |||
795 | pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], func->mem[count]->start); | ||
796 | |||
797 | if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) { | ||
798 | /* takes up another dword */ | ||
799 | count += 1; | ||
800 | /* on the 2nd dword, write all 0s, since we can't handle them n.e.ways */ | ||
801 | pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0x00000000); | ||
802 | |||
803 | } | ||
804 | } | ||
805 | } /* end of mem */ | ||
806 | } /* end of for */ | ||
807 | |||
808 | /* Now need to see how much space the devices behind the bridge needed */ | ||
809 | amount_needed = scan_behind_bridge (func, sec_number); | ||
810 | if (amount_needed == NULL) | ||
811 | return -ENOMEM; | ||
812 | |||
813 | ibmphp_pci_bus->number = func->busno; | ||
814 | debug ("after coming back from scan_behind_bridge\n"); | ||
815 | debug ("amount_needed->not_correct = %x\n", amount_needed->not_correct); | ||
816 | debug ("amount_needed->io = %x\n", amount_needed->io); | ||
817 | debug ("amount_needed->mem = %x\n", amount_needed->mem); | ||
818 | debug ("amount_needed->pfmem = %x\n", amount_needed->pfmem); | ||
819 | |||
820 | if (amount_needed->not_correct) { | ||
821 | debug ("amount_needed is not correct\n"); | ||
822 | for (count = 0; address[count]; count++) { | ||
823 | /* for 2 BARs */ | ||
824 | if (bus_io[count]) { | ||
825 | ibmphp_remove_resource (bus_io[count]); | ||
826 | func->io[count] = NULL; | ||
827 | } else if (bus_pfmem[count]) { | ||
828 | ibmphp_remove_resource (bus_pfmem[count]); | ||
829 | func->pfmem[count] = NULL; | ||
830 | } else if (bus_mem[count]) { | ||
831 | ibmphp_remove_resource (bus_mem[count]); | ||
832 | func->mem[count] = NULL; | ||
833 | } | ||
834 | } | ||
835 | kfree (amount_needed); | ||
836 | return -ENODEV; | ||
837 | } | ||
838 | |||
839 | if (!amount_needed->io) { | ||
840 | debug ("it doesn't want IO?\n"); | ||
841 | flag_io = TRUE; | ||
842 | } else { | ||
843 | debug ("it wants %x IO behind the bridge\n", amount_needed->io); | ||
844 | io = kmalloc(sizeof(*io), GFP_KERNEL); | ||
845 | |||
846 | if (!io) { | ||
847 | err ("out of system memory\n"); | ||
848 | retval = -ENOMEM; | ||
849 | goto error; | ||
850 | } | ||
851 | memset (io, 0, sizeof (struct resource_node)); | ||
852 | io->type = IO; | ||
853 | io->busno = func->busno; | ||
854 | io->devfunc = PCI_DEVFN(func->device, func->function); | ||
855 | io->len = amount_needed->io; | ||
856 | if (ibmphp_check_resource (io, 1) == 0) { | ||
857 | debug ("were we able to add io\n"); | ||
858 | ibmphp_add_resource (io); | ||
859 | flag_io = TRUE; | ||
860 | } | ||
861 | } | ||
862 | |||
863 | if (!amount_needed->mem) { | ||
864 | debug ("it doesn't want n.e.memory?\n"); | ||
865 | flag_mem = TRUE; | ||
866 | } else { | ||
867 | debug ("it wants %x memory behind the bridge\n", amount_needed->mem); | ||
868 | mem = kmalloc(sizeof(*mem), GFP_KERNEL); | ||
869 | if (!mem) { | ||
870 | err ("out of system memory\n"); | ||
871 | retval = -ENOMEM; | ||
872 | goto error; | ||
873 | } | ||
874 | memset (mem, 0, sizeof (struct resource_node)); | ||
875 | mem->type = MEM; | ||
876 | mem->busno = func->busno; | ||
877 | mem->devfunc = PCI_DEVFN(func->device, func->function); | ||
878 | mem->len = amount_needed->mem; | ||
879 | if (ibmphp_check_resource (mem, 1) == 0) { | ||
880 | ibmphp_add_resource (mem); | ||
881 | flag_mem = TRUE; | ||
882 | debug ("were we able to add mem\n"); | ||
883 | } | ||
884 | } | ||
885 | |||
886 | if (!amount_needed->pfmem) { | ||
887 | debug ("it doesn't want n.e.pfmem mem?\n"); | ||
888 | flag_pfmem = TRUE; | ||
889 | } else { | ||
890 | debug ("it wants %x pfmemory behind the bridge\n", amount_needed->pfmem); | ||
891 | pfmem = kmalloc(sizeof(*pfmem), GFP_KERNEL); | ||
892 | if (!pfmem) { | ||
893 | err ("out of system memory\n"); | ||
894 | retval = -ENOMEM; | ||
895 | goto error; | ||
896 | } | ||
897 | memset (pfmem, 0, sizeof (struct resource_node)); | ||
898 | pfmem->type = PFMEM; | ||
899 | pfmem->busno = func->busno; | ||
900 | pfmem->devfunc = PCI_DEVFN(func->device, func->function); | ||
901 | pfmem->len = amount_needed->pfmem; | ||
902 | pfmem->fromMem = FALSE; | ||
903 | if (ibmphp_check_resource (pfmem, 1) == 0) { | ||
904 | ibmphp_add_resource (pfmem); | ||
905 | flag_pfmem = TRUE; | ||
906 | } else { | ||
907 | mem_tmp = kmalloc(sizeof(*mem_tmp), GFP_KERNEL); | ||
908 | if (!mem_tmp) { | ||
909 | err ("out of system memory\n"); | ||
910 | retval = -ENOMEM; | ||
911 | goto error; | ||
912 | } | ||
913 | memset (mem_tmp, 0, sizeof (struct resource_node)); | ||
914 | mem_tmp->type = MEM; | ||
915 | mem_tmp->busno = pfmem->busno; | ||
916 | mem_tmp->devfunc = pfmem->devfunc; | ||
917 | mem_tmp->len = pfmem->len; | ||
918 | if (ibmphp_check_resource (mem_tmp, 1) == 0) { | ||
919 | ibmphp_add_resource (mem_tmp); | ||
920 | pfmem->fromMem = TRUE; | ||
921 | pfmem->rangeno = mem_tmp->rangeno; | ||
922 | ibmphp_add_pfmem_from_mem (pfmem); | ||
923 | flag_pfmem = TRUE; | ||
924 | } | ||
925 | } | ||
926 | } | ||
927 | |||
928 | debug ("b4 if (flag_io && flag_mem && flag_pfmem)\n"); | ||
929 | debug ("flag_io = %x, flag_mem = %x, flag_pfmem = %x\n", flag_io, flag_mem, flag_pfmem); | ||
930 | |||
931 | if (flag_io && flag_mem && flag_pfmem) { | ||
932 | /* If on bootup, there was a bridged card in this slot, | ||
933 | * then card was removed and ibmphp got unloaded and loaded | ||
934 | * back again, there's no way for us to remove the bus | ||
935 | * struct, so no need to kmalloc, can use existing node | ||
936 | */ | ||
937 | bus = ibmphp_find_res_bus (sec_number); | ||
938 | if (!bus) { | ||
939 | bus = kmalloc(sizeof(*bus), GFP_KERNEL); | ||
940 | if (!bus) { | ||
941 | err ("out of system memory\n"); | ||
942 | retval = -ENOMEM; | ||
943 | goto error; | ||
944 | } | ||
945 | memset (bus, 0, sizeof (struct bus_node)); | ||
946 | bus->busno = sec_number; | ||
947 | debug ("b4 adding new bus\n"); | ||
948 | rc = add_new_bus (bus, io, mem, pfmem, func->busno); | ||
949 | } else if (!(bus->rangeIO) && !(bus->rangeMem) && !(bus->rangePFMem)) | ||
950 | rc = add_new_bus (bus, io, mem, pfmem, 0xFF); | ||
951 | else { | ||
952 | err ("expected bus structure not empty?\n"); | ||
953 | retval = -EIO; | ||
954 | goto error; | ||
955 | } | ||
956 | if (rc) { | ||
957 | if (rc == -ENOMEM) { | ||
958 | ibmphp_remove_bus (bus, func->busno); | ||
959 | kfree (amount_needed); | ||
960 | return rc; | ||
961 | } | ||
962 | retval = rc; | ||
963 | goto error; | ||
964 | } | ||
965 | pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_IO_BASE, &io_base); | ||
966 | pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_BASE, &pfmem_base); | ||
967 | |||
968 | if ((io_base & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) { | ||
969 | debug ("io 32\n"); | ||
970 | need_io_upper = TRUE; | ||
971 | } | ||
972 | if ((io_base & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) { | ||
973 | debug ("pfmem 64\n"); | ||
974 | need_pfmem_upper = TRUE; | ||
975 | } | ||
976 | |||
977 | if (bus->noIORanges) { | ||
978 | pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_IO_BASE, 0x00 | bus->rangeIO->start >> 8); | ||
979 | pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_IO_LIMIT, 0x00 | bus->rangeIO->end >> 8); | ||
980 | |||
981 | /* _______________This is for debugging purposes only ____________________ | ||
982 | pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_IO_BASE, &temp); | ||
983 | debug ("io_base = %x\n", (temp & PCI_IO_RANGE_TYPE_MASK) << 8); | ||
984 | pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_IO_LIMIT, &temp); | ||
985 | debug ("io_limit = %x\n", (temp & PCI_IO_RANGE_TYPE_MASK) << 8); | ||
986 | ________________________________________________________________________*/ | ||
987 | |||
988 | if (need_io_upper) { /* since can't support n.e.ways */ | ||
989 | pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_IO_BASE_UPPER16, 0x0000); | ||
990 | pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_IO_LIMIT_UPPER16, 0x0000); | ||
991 | } | ||
992 | } else { | ||
993 | pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_IO_BASE, 0x00); | ||
994 | pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_IO_LIMIT, 0x00); | ||
995 | } | ||
996 | |||
997 | if (bus->noMemRanges) { | ||
998 | pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_BASE, 0x0000 | bus->rangeMem->start >> 16); | ||
999 | pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_LIMIT, 0x0000 | bus->rangeMem->end >> 16); | ||
1000 | |||
1001 | /* ____________________This is for debugging purposes only ________________________ | ||
1002 | pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_BASE, &temp); | ||
1003 | debug ("mem_base = %x\n", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16); | ||
1004 | pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_LIMIT, &temp); | ||
1005 | debug ("mem_limit = %x\n", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16); | ||
1006 | __________________________________________________________________________________*/ | ||
1007 | |||
1008 | } else { | ||
1009 | pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_BASE, 0xffff); | ||
1010 | pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_LIMIT, 0x0000); | ||
1011 | } | ||
1012 | if (bus->noPFMemRanges) { | ||
1013 | pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_BASE, 0x0000 | bus->rangePFMem->start >> 16); | ||
1014 | pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, 0x0000 | bus->rangePFMem->end >> 16); | ||
1015 | |||
1016 | /* __________________________This is for debugging purposes only _______________________ | ||
1017 | pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_BASE, &temp); | ||
1018 | debug ("pfmem_base = %x", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16); | ||
1019 | pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &temp); | ||
1020 | debug ("pfmem_limit = %x\n", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16); | ||
1021 | ______________________________________________________________________________________*/ | ||
1022 | |||
1023 | if (need_pfmem_upper) { /* since can't support n.e.ways */ | ||
1024 | pci_bus_write_config_dword (ibmphp_pci_bus, devfn, PCI_PREF_BASE_UPPER32, 0x00000000); | ||
1025 | pci_bus_write_config_dword (ibmphp_pci_bus, devfn, PCI_PREF_LIMIT_UPPER32, 0x00000000); | ||
1026 | } | ||
1027 | } else { | ||
1028 | pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_BASE, 0xffff); | ||
1029 | pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, 0x0000); | ||
1030 | } | ||
1031 | |||
1032 | debug ("b4 writing control information\n"); | ||
1033 | |||
1034 | pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_INTERRUPT_PIN, &irq); | ||
1035 | if ((irq > 0x00) && (irq < 0x05)) | ||
1036 | pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_INTERRUPT_LINE, func->irq[irq - 1]); | ||
1037 | /* | ||
1038 | pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_BRIDGE_CONTROL, ctrl); | ||
1039 | pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_BRIDGE_CONTROL, PCI_BRIDGE_CTL_PARITY); | ||
1040 | pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_BRIDGE_CONTROL, PCI_BRIDGE_CTL_SERR); | ||
1041 | */ | ||
1042 | |||
1043 | pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_COMMAND, DEVICEENABLE); | ||
1044 | pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_BRIDGE_CONTROL, 0x07); | ||
1045 | for (i = 0; i < 32; i++) { | ||
1046 | if (amount_needed->devices[i]) { | ||
1047 | debug ("device where devices[i] is 1 = %x\n", i); | ||
1048 | func->devices[i] = 1; | ||
1049 | } | ||
1050 | } | ||
1051 | func->bus = 1; /* For unconfiguring, to indicate it's PPB */ | ||
1052 | func_passed = &func; | ||
1053 | debug ("func->busno b4 returning is %x\n", func->busno); | ||
1054 | debug ("func->busno b4 returning in the other structure is %x\n", (*func_passed)->busno); | ||
1055 | kfree (amount_needed); | ||
1056 | return 0; | ||
1057 | } else { | ||
1058 | err ("Configuring bridge was unsuccessful...\n"); | ||
1059 | mem_tmp = NULL; | ||
1060 | retval = -EIO; | ||
1061 | goto error; | ||
1062 | } | ||
1063 | |||
1064 | error: | ||
1065 | kfree(amount_needed); | ||
1066 | if (pfmem) | ||
1067 | ibmphp_remove_resource (pfmem); | ||
1068 | if (io) | ||
1069 | ibmphp_remove_resource (io); | ||
1070 | if (mem) | ||
1071 | ibmphp_remove_resource (mem); | ||
1072 | for (i = 0; i < 2; i++) { /* for 2 BARs */ | ||
1073 | if (bus_io[i]) { | ||
1074 | ibmphp_remove_resource (bus_io[i]); | ||
1075 | func->io[i] = NULL; | ||
1076 | } else if (bus_pfmem[i]) { | ||
1077 | ibmphp_remove_resource (bus_pfmem[i]); | ||
1078 | func->pfmem[i] = NULL; | ||
1079 | } else if (bus_mem[i]) { | ||
1080 | ibmphp_remove_resource (bus_mem[i]); | ||
1081 | func->mem[i] = NULL; | ||
1082 | } | ||
1083 | } | ||
1084 | return retval; | ||
1085 | } | ||
1086 | |||
1087 | /***************************************************************************** | ||
1088 | * This function adds up the amount of resources needed behind the PPB bridge | ||
1089 | * and passes it to the configure_bridge function | ||
1090 | * Input: bridge function | ||
1091 | * Ouput: amount of resources needed | ||
1092 | *****************************************************************************/ | ||
1093 | static struct res_needed *scan_behind_bridge (struct pci_func * func, u8 busno) | ||
1094 | { | ||
1095 | int count, len[6]; | ||
1096 | u16 vendor_id; | ||
1097 | u8 hdr_type; | ||
1098 | u8 device, function; | ||
1099 | unsigned int devfn; | ||
1100 | int howmany = 0; /*this is to see if there are any devices behind the bridge */ | ||
1101 | |||
1102 | u32 bar[6], class; | ||
1103 | u32 address[] = { | ||
1104 | PCI_BASE_ADDRESS_0, | ||
1105 | PCI_BASE_ADDRESS_1, | ||
1106 | PCI_BASE_ADDRESS_2, | ||
1107 | PCI_BASE_ADDRESS_3, | ||
1108 | PCI_BASE_ADDRESS_4, | ||
1109 | PCI_BASE_ADDRESS_5, | ||
1110 | 0 | ||
1111 | }; | ||
1112 | struct res_needed *amount; | ||
1113 | |||
1114 | amount = kmalloc(sizeof(*amount), GFP_KERNEL); | ||
1115 | if (amount == NULL) | ||
1116 | return NULL; | ||
1117 | memset (amount, 0, sizeof (struct res_needed)); | ||
1118 | |||
1119 | ibmphp_pci_bus->number = busno; | ||
1120 | |||
1121 | debug ("the bus_no behind the bridge is %x\n", busno); | ||
1122 | debug ("scanning devices behind the bridge...\n"); | ||
1123 | for (device = 0; device < 32; device++) { | ||
1124 | amount->devices[device] = 0; | ||
1125 | for (function = 0; function < 8; function++) { | ||
1126 | devfn = PCI_DEVFN(device, function); | ||
1127 | |||
1128 | pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_VENDOR_ID, &vendor_id); | ||
1129 | |||
1130 | if (vendor_id != PCI_VENDOR_ID_NOTVALID) { | ||
1131 | /* found correct device!!! */ | ||
1132 | howmany++; | ||
1133 | |||
1134 | pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_HEADER_TYPE, &hdr_type); | ||
1135 | pci_bus_read_config_dword (ibmphp_pci_bus, devfn, PCI_CLASS_REVISION, &class); | ||
1136 | |||
1137 | debug ("hdr_type behind the bridge is %x\n", hdr_type); | ||
1138 | if (hdr_type & PCI_HEADER_TYPE_BRIDGE) { | ||
1139 | err ("embedded bridges not supported for hot-plugging.\n"); | ||
1140 | amount->not_correct = TRUE; | ||
1141 | return amount; | ||
1142 | } | ||
1143 | |||
1144 | class >>= 8; /* to take revision out, class = class.subclass.prog i/f */ | ||
1145 | if (class == PCI_CLASS_NOT_DEFINED_VGA) { | ||
1146 | err ("The device %x is VGA compatible and as is not supported for hot plugging. " | ||
1147 | "Please choose another device.\n", device); | ||
1148 | amount->not_correct = TRUE; | ||
1149 | return amount; | ||
1150 | } else if (class == PCI_CLASS_DISPLAY_VGA) { | ||
1151 | err ("The device %x is not supported for hot plugging. " | ||
1152 | "Please choose another device.\n", device); | ||
1153 | amount->not_correct = TRUE; | ||
1154 | return amount; | ||
1155 | } | ||
1156 | |||
1157 | amount->devices[device] = 1; | ||
1158 | |||
1159 | for (count = 0; address[count]; count++) { | ||
1160 | /* for 6 BARs */ | ||
1161 | /* | ||
1162 | pci_bus_read_config_byte (ibmphp_pci_bus, devfn, address[count], &tmp); | ||
1163 | if (tmp & 0x01) // IO | ||
1164 | pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFD); | ||
1165 | else // MEMORY | ||
1166 | pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFF); | ||
1167 | */ | ||
1168 | pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFF); | ||
1169 | pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &bar[count]); | ||
1170 | |||
1171 | debug ("what is bar[count]? %x, count = %d\n", bar[count], count); | ||
1172 | |||
1173 | if (!bar[count]) /* This BAR is not implemented */ | ||
1174 | continue; | ||
1175 | |||
1176 | //tmp_bar = bar[count]; | ||
1177 | |||
1178 | debug ("count %d device %x function %x wants %x resources\n", count, device, function, bar[count]); | ||
1179 | |||
1180 | if (bar[count] & PCI_BASE_ADDRESS_SPACE_IO) { | ||
1181 | /* This is IO */ | ||
1182 | len[count] = bar[count] & 0xFFFFFFFC; | ||
1183 | len[count] = ~len[count] + 1; | ||
1184 | amount->io += len[count]; | ||
1185 | } else { | ||
1186 | /* This is Memory */ | ||
1187 | if (bar[count] & PCI_BASE_ADDRESS_MEM_PREFETCH) { | ||
1188 | /* pfmem */ | ||
1189 | len[count] = bar[count] & 0xFFFFFFF0; | ||
1190 | len[count] = ~len[count] + 1; | ||
1191 | amount->pfmem += len[count]; | ||
1192 | if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) | ||
1193 | /* takes up another dword */ | ||
1194 | count += 1; | ||
1195 | |||
1196 | } else { | ||
1197 | /* regular memory */ | ||
1198 | len[count] = bar[count] & 0xFFFFFFF0; | ||
1199 | len[count] = ~len[count] + 1; | ||
1200 | amount->mem += len[count]; | ||
1201 | if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) { | ||
1202 | /* takes up another dword */ | ||
1203 | count += 1; | ||
1204 | } | ||
1205 | } | ||
1206 | } | ||
1207 | } /* end for */ | ||
1208 | } /* end if (valid) */ | ||
1209 | } /* end for */ | ||
1210 | } /* end for */ | ||
1211 | |||
1212 | if (!howmany) | ||
1213 | amount->not_correct = TRUE; | ||
1214 | else | ||
1215 | amount->not_correct = FALSE; | ||
1216 | if ((amount->io) && (amount->io < IOBRIDGE)) | ||
1217 | amount->io = IOBRIDGE; | ||
1218 | if ((amount->mem) && (amount->mem < MEMBRIDGE)) | ||
1219 | amount->mem = MEMBRIDGE; | ||
1220 | if ((amount->pfmem) && (amount->pfmem < MEMBRIDGE)) | ||
1221 | amount->pfmem = MEMBRIDGE; | ||
1222 | return amount; | ||
1223 | } | ||
1224 | |||
1225 | /* The following 3 unconfigure_boot_ routines deal with the case when we had the card | ||
1226 | * upon bootup in the system, since we don't allocate func to such case, we need to read | ||
1227 | * the start addresses from pci config space and then find the corresponding entries in | ||
1228 | * our resource lists. The functions return either 0, -ENODEV, or -1 (general failure) | ||
1229 | * Change: we also call these functions even if we configured the card ourselves (i.e., not | ||
1230 | * the bootup case), since it should work same way | ||
1231 | */ | ||
1232 | static int unconfigure_boot_device (u8 busno, u8 device, u8 function) | ||
1233 | { | ||
1234 | u32 start_address; | ||
1235 | u32 address[] = { | ||
1236 | PCI_BASE_ADDRESS_0, | ||
1237 | PCI_BASE_ADDRESS_1, | ||
1238 | PCI_BASE_ADDRESS_2, | ||
1239 | PCI_BASE_ADDRESS_3, | ||
1240 | PCI_BASE_ADDRESS_4, | ||
1241 | PCI_BASE_ADDRESS_5, | ||
1242 | 0 | ||
1243 | }; | ||
1244 | int count; | ||
1245 | struct resource_node *io; | ||
1246 | struct resource_node *mem; | ||
1247 | struct resource_node *pfmem; | ||
1248 | struct bus_node *bus; | ||
1249 | u32 end_address; | ||
1250 | u32 temp_end; | ||
1251 | u32 size; | ||
1252 | u32 tmp_address; | ||
1253 | unsigned int devfn; | ||
1254 | |||
1255 | debug ("%s - enter\n", __FUNCTION__); | ||
1256 | |||
1257 | bus = ibmphp_find_res_bus (busno); | ||
1258 | if (!bus) { | ||
1259 | debug ("cannot find corresponding bus.\n"); | ||
1260 | return -EINVAL; | ||
1261 | } | ||
1262 | |||
1263 | devfn = PCI_DEVFN(device, function); | ||
1264 | ibmphp_pci_bus->number = busno; | ||
1265 | for (count = 0; address[count]; count++) { /* for 6 BARs */ | ||
1266 | pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &start_address); | ||
1267 | |||
1268 | /* We can do this here, b/c by that time the device driver of the card has been stopped */ | ||
1269 | |||
1270 | pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFF); | ||
1271 | pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &size); | ||
1272 | pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], start_address); | ||
1273 | |||
1274 | debug ("start_address is %x\n", start_address); | ||
1275 | debug ("busno, device, function %x %x %x\n", busno, device, function); | ||
1276 | if (!size) { | ||
1277 | /* This BAR is not implemented */ | ||
1278 | debug ("is this bar no implemented?, count = %d\n", count); | ||
1279 | continue; | ||
1280 | } | ||
1281 | tmp_address = start_address; | ||
1282 | if (start_address & PCI_BASE_ADDRESS_SPACE_IO) { | ||
1283 | /* This is IO */ | ||
1284 | start_address &= PCI_BASE_ADDRESS_IO_MASK; | ||
1285 | size = size & 0xFFFFFFFC; | ||
1286 | size = ~size + 1; | ||
1287 | end_address = start_address + size - 1; | ||
1288 | if (ibmphp_find_resource (bus, start_address, &io, IO) < 0) { | ||
1289 | err ("cannot find corresponding IO resource to remove\n"); | ||
1290 | return -EIO; | ||
1291 | } | ||
1292 | debug ("io->start = %x\n", io->start); | ||
1293 | temp_end = io->end; | ||
1294 | start_address = io->end + 1; | ||
1295 | ibmphp_remove_resource (io); | ||
1296 | /* This is needed b/c of the old I/O restrictions in the BIOS */ | ||
1297 | while (temp_end < end_address) { | ||
1298 | if (ibmphp_find_resource (bus, start_address, &io, IO) < 0) { | ||
1299 | err ("cannot find corresponding IO resource to remove\n"); | ||
1300 | return -EIO; | ||
1301 | } | ||
1302 | debug ("io->start = %x\n", io->start); | ||
1303 | temp_end = io->end; | ||
1304 | start_address = io->end + 1; | ||
1305 | ibmphp_remove_resource (io); | ||
1306 | } | ||
1307 | |||
1308 | /* ????????? DO WE NEED TO WRITE ANYTHING INTO THE PCI CONFIG SPACE BACK ?????????? */ | ||
1309 | } else { | ||
1310 | /* This is Memory */ | ||
1311 | start_address &= PCI_BASE_ADDRESS_MEM_MASK; | ||
1312 | if (start_address & PCI_BASE_ADDRESS_MEM_PREFETCH) { | ||
1313 | /* pfmem */ | ||
1314 | debug ("start address of pfmem is %x\n", start_address); | ||
1315 | |||
1316 | if (ibmphp_find_resource (bus, start_address, &pfmem, PFMEM) < 0) { | ||
1317 | err ("cannot find corresponding PFMEM resource to remove\n"); | ||
1318 | return -EIO; | ||
1319 | } | ||
1320 | if (pfmem) { | ||
1321 | debug ("pfmem->start = %x\n", pfmem->start); | ||
1322 | |||
1323 | ibmphp_remove_resource(pfmem); | ||
1324 | } | ||
1325 | } else { | ||
1326 | /* regular memory */ | ||
1327 | debug ("start address of mem is %x\n", start_address); | ||
1328 | if (ibmphp_find_resource (bus, start_address, &mem, MEM) < 0) { | ||
1329 | err ("cannot find corresponding MEM resource to remove\n"); | ||
1330 | return -EIO; | ||
1331 | } | ||
1332 | if (mem) { | ||
1333 | debug ("mem->start = %x\n", mem->start); | ||
1334 | |||
1335 | ibmphp_remove_resource(mem); | ||
1336 | } | ||
1337 | } | ||
1338 | if (tmp_address & PCI_BASE_ADDRESS_MEM_TYPE_64) { | ||
1339 | /* takes up another dword */ | ||
1340 | count += 1; | ||
1341 | } | ||
1342 | } /* end of mem */ | ||
1343 | } /* end of for */ | ||
1344 | |||
1345 | return 0; | ||
1346 | } | ||
1347 | |||
1348 | static int unconfigure_boot_bridge (u8 busno, u8 device, u8 function) | ||
1349 | { | ||
1350 | int count; | ||
1351 | int bus_no, pri_no, sub_no, sec_no = 0; | ||
1352 | u32 start_address, tmp_address; | ||
1353 | u8 sec_number, sub_number, pri_number; | ||
1354 | struct resource_node *io = NULL; | ||
1355 | struct resource_node *mem = NULL; | ||
1356 | struct resource_node *pfmem = NULL; | ||
1357 | struct bus_node *bus; | ||
1358 | u32 address[] = { | ||
1359 | PCI_BASE_ADDRESS_0, | ||
1360 | PCI_BASE_ADDRESS_1, | ||
1361 | 0 | ||
1362 | }; | ||
1363 | unsigned int devfn; | ||
1364 | |||
1365 | devfn = PCI_DEVFN(device, function); | ||
1366 | ibmphp_pci_bus->number = busno; | ||
1367 | bus_no = (int) busno; | ||
1368 | debug ("busno is %x\n", busno); | ||
1369 | pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_PRIMARY_BUS, &pri_number); | ||
1370 | debug ("%s - busno = %x, primary_number = %x\n", __FUNCTION__, busno, pri_number); | ||
1371 | |||
1372 | pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_number); | ||
1373 | debug ("sec_number is %x\n", sec_number); | ||
1374 | sec_no = (int) sec_number; | ||
1375 | pri_no = (int) pri_number; | ||
1376 | if (pri_no != bus_no) { | ||
1377 | err ("primary numbers in our structures and pci config space don't match.\n"); | ||
1378 | return -EINVAL; | ||
1379 | } | ||
1380 | |||
1381 | pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SUBORDINATE_BUS, &sub_number); | ||
1382 | sub_no = (int) sub_number; | ||
1383 | debug ("sub_no is %d, sec_no is %d\n", sub_no, sec_no); | ||
1384 | if (sec_no != sub_number) { | ||
1385 | err ("there're more buses behind this bridge. Hot removal is not supported. Please choose another card\n"); | ||
1386 | return -ENODEV; | ||
1387 | } | ||
1388 | |||
1389 | bus = ibmphp_find_res_bus (sec_number); | ||
1390 | debug ("bus->busno is %x\n", bus->busno); | ||
1391 | debug ("sec_number is %x\n", sec_number); | ||
1392 | if (!bus) { | ||
1393 | err ("cannot find Bus structure for the bridged device\n"); | ||
1394 | return -EINVAL; | ||
1395 | } | ||
1396 | |||
1397 | ibmphp_remove_bus (bus, busno); | ||
1398 | |||
1399 | for (count = 0; address[count]; count++) { | ||
1400 | /* for 2 BARs */ | ||
1401 | pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &start_address); | ||
1402 | |||
1403 | if (!start_address) { | ||
1404 | /* This BAR is not implemented */ | ||
1405 | continue; | ||
1406 | } | ||
1407 | |||
1408 | tmp_address = start_address; | ||
1409 | |||
1410 | if (start_address & PCI_BASE_ADDRESS_SPACE_IO) { | ||
1411 | /* This is IO */ | ||
1412 | start_address &= PCI_BASE_ADDRESS_IO_MASK; | ||
1413 | if (ibmphp_find_resource (bus, start_address, &io, IO) < 0) { | ||
1414 | err ("cannot find corresponding IO resource to remove\n"); | ||
1415 | return -EIO; | ||
1416 | } | ||
1417 | if (io) | ||
1418 | debug ("io->start = %x\n", io->start); | ||
1419 | |||
1420 | ibmphp_remove_resource (io); | ||
1421 | |||
1422 | /* ????????? DO WE NEED TO WRITE ANYTHING INTO THE PCI CONFIG SPACE BACK ?????????? */ | ||
1423 | } else { | ||
1424 | /* This is Memory */ | ||
1425 | start_address &= PCI_BASE_ADDRESS_MEM_MASK; | ||
1426 | if (start_address & PCI_BASE_ADDRESS_MEM_PREFETCH) { | ||
1427 | /* pfmem */ | ||
1428 | if (ibmphp_find_resource (bus, start_address, &pfmem, PFMEM) < 0) { | ||
1429 | err ("cannot find corresponding PFMEM resource to remove\n"); | ||
1430 | return -EINVAL; | ||
1431 | } | ||
1432 | if (pfmem) { | ||
1433 | debug ("pfmem->start = %x\n", pfmem->start); | ||
1434 | |||
1435 | ibmphp_remove_resource(pfmem); | ||
1436 | } | ||
1437 | } else { | ||
1438 | /* regular memory */ | ||
1439 | if (ibmphp_find_resource (bus, start_address, &mem, MEM) < 0) { | ||
1440 | err ("cannot find corresponding MEM resource to remove\n"); | ||
1441 | return -EINVAL; | ||
1442 | } | ||
1443 | if (mem) { | ||
1444 | debug ("mem->start = %x\n", mem->start); | ||
1445 | |||
1446 | ibmphp_remove_resource(mem); | ||
1447 | } | ||
1448 | } | ||
1449 | if (tmp_address & PCI_BASE_ADDRESS_MEM_TYPE_64) { | ||
1450 | /* takes up another dword */ | ||
1451 | count += 1; | ||
1452 | } | ||
1453 | } /* end of mem */ | ||
1454 | } /* end of for */ | ||
1455 | debug ("%s - exiting, returning success\n", __FUNCTION__); | ||
1456 | return 0; | ||
1457 | } | ||
1458 | |||
1459 | static int unconfigure_boot_card (struct slot *slot_cur) | ||
1460 | { | ||
1461 | u16 vendor_id; | ||
1462 | u32 class; | ||
1463 | u8 hdr_type; | ||
1464 | u8 device; | ||
1465 | u8 busno; | ||
1466 | u8 function; | ||
1467 | int rc; | ||
1468 | unsigned int devfn; | ||
1469 | u8 valid_device = 0x00; /* To see if we are ever able to find valid device and read it */ | ||
1470 | |||
1471 | debug ("%s - enter\n", __FUNCTION__); | ||
1472 | |||
1473 | device = slot_cur->device; | ||
1474 | busno = slot_cur->bus; | ||
1475 | |||
1476 | debug ("b4 for loop, device is %x\n", device); | ||
1477 | /* For every function on the card */ | ||
1478 | for (function = 0x0; function < 0x08; function++) { | ||
1479 | devfn = PCI_DEVFN(device, function); | ||
1480 | ibmphp_pci_bus->number = busno; | ||
1481 | |||
1482 | pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_VENDOR_ID, &vendor_id); | ||
1483 | |||
1484 | if (vendor_id != PCI_VENDOR_ID_NOTVALID) { | ||
1485 | /* found correct device!!! */ | ||
1486 | ++valid_device; | ||
1487 | |||
1488 | debug ("%s - found correct device\n", __FUNCTION__); | ||
1489 | |||
1490 | /* header: x x x x x x x x | ||
1491 | * | |___________|=> 1=PPB bridge, 0=normal device, 2=CardBus Bridge | ||
1492 | * |_=> 0 = single function device, 1 = multi-function device | ||
1493 | */ | ||
1494 | |||
1495 | pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_HEADER_TYPE, &hdr_type); | ||
1496 | pci_bus_read_config_dword (ibmphp_pci_bus, devfn, PCI_CLASS_REVISION, &class); | ||
1497 | |||
1498 | debug ("hdr_type %x, class %x\n", hdr_type, class); | ||
1499 | class >>= 8; /* to take revision out, class = class.subclass.prog i/f */ | ||
1500 | if (class == PCI_CLASS_NOT_DEFINED_VGA) { | ||
1501 | err ("The device %x function %x is VGA compatible and is not supported for hot removing. " | ||
1502 | "Please choose another device.\n", device, function); | ||
1503 | return -ENODEV; | ||
1504 | } else if (class == PCI_CLASS_DISPLAY_VGA) { | ||
1505 | err ("The device %x function %x is not supported for hot removing. " | ||
1506 | "Please choose another device.\n", device, function); | ||
1507 | return -ENODEV; | ||
1508 | } | ||
1509 | |||
1510 | switch (hdr_type) { | ||
1511 | case PCI_HEADER_TYPE_NORMAL: | ||
1512 | rc = unconfigure_boot_device (busno, device, function); | ||
1513 | if (rc) { | ||
1514 | err ("was not able to unconfigure device %x func %x on bus %x. bailing out...\n", | ||
1515 | device, function, busno); | ||
1516 | return rc; | ||
1517 | } | ||
1518 | function = 0x8; | ||
1519 | break; | ||
1520 | case PCI_HEADER_TYPE_MULTIDEVICE: | ||
1521 | rc = unconfigure_boot_device (busno, device, function); | ||
1522 | if (rc) { | ||
1523 | err ("was not able to unconfigure device %x func %x on bus %x. bailing out...\n", | ||
1524 | device, function, busno); | ||
1525 | return rc; | ||
1526 | } | ||
1527 | break; | ||
1528 | case PCI_HEADER_TYPE_BRIDGE: | ||
1529 | class >>= 8; | ||
1530 | if (class != PCI_CLASS_BRIDGE_PCI) { | ||
1531 | err ("This device %x function %x is not PCI-to-PCI bridge, " | ||
1532 | "and is not supported for hot-removing. " | ||
1533 | "Please try another card.\n", device, function); | ||
1534 | return -ENODEV; | ||
1535 | } | ||
1536 | rc = unconfigure_boot_bridge (busno, device, function); | ||
1537 | if (rc != 0) { | ||
1538 | err ("was not able to hot-remove PPB properly.\n"); | ||
1539 | return rc; | ||
1540 | } | ||
1541 | |||
1542 | function = 0x8; | ||
1543 | break; | ||
1544 | case PCI_HEADER_TYPE_MULTIBRIDGE: | ||
1545 | class >>= 8; | ||
1546 | if (class != PCI_CLASS_BRIDGE_PCI) { | ||
1547 | err ("This device %x function %x is not PCI-to-PCI bridge, " | ||
1548 | "and is not supported for hot-removing. " | ||
1549 | "Please try another card.\n", device, function); | ||
1550 | return -ENODEV; | ||
1551 | } | ||
1552 | rc = unconfigure_boot_bridge (busno, device, function); | ||
1553 | if (rc != 0) { | ||
1554 | err ("was not able to hot-remove PPB properly.\n"); | ||
1555 | return rc; | ||
1556 | } | ||
1557 | break; | ||
1558 | default: | ||
1559 | err ("MAJOR PROBLEM!!!! Cannot read device's header\n"); | ||
1560 | return -1; | ||
1561 | break; | ||
1562 | } /* end of switch */ | ||
1563 | } /* end of valid device */ | ||
1564 | } /* end of for */ | ||
1565 | |||
1566 | if (!valid_device) { | ||
1567 | err ("Could not find device to unconfigure. Or could not read the card.\n"); | ||
1568 | return -1; | ||
1569 | } | ||
1570 | return 0; | ||
1571 | } | ||
1572 | |||
1573 | /* | ||
1574 | * free the resources of the card (multi, single, or bridged) | ||
1575 | * Parameters: slot, flag to say if this is for removing entire module or just | ||
1576 | * unconfiguring the device | ||
1577 | * TO DO: will probably need to add some code in case there was some resource, | ||
1578 | * to remove it... this is from when we have errors in the configure_card... | ||
1579 | * !!!!!!!!!!!!!!!!!!!!!!!!!FOR BUSES!!!!!!!!!!!! | ||
1580 | * Returns: 0, -1, -ENODEV | ||
1581 | */ | ||
1582 | int ibmphp_unconfigure_card (struct slot **slot_cur, int the_end) | ||
1583 | { | ||
1584 | int i; | ||
1585 | int count; | ||
1586 | int rc; | ||
1587 | struct slot *sl = *slot_cur; | ||
1588 | struct pci_func *cur_func = NULL; | ||
1589 | struct pci_func *temp_func; | ||
1590 | |||
1591 | debug ("%s - enter\n", __FUNCTION__); | ||
1592 | |||
1593 | if (!the_end) { | ||
1594 | /* Need to unconfigure the card */ | ||
1595 | rc = unconfigure_boot_card (sl); | ||
1596 | if ((rc == -ENODEV) || (rc == -EIO) || (rc == -EINVAL)) { | ||
1597 | /* In all other cases, will still need to get rid of func structure if it exists */ | ||
1598 | return rc; | ||
1599 | } | ||
1600 | } | ||
1601 | |||
1602 | if (sl->func) { | ||
1603 | cur_func = sl->func; | ||
1604 | while (cur_func) { | ||
1605 | /* TO DO: WILL MOST LIKELY NEED TO GET RID OF THE BUS STRUCTURE FROM RESOURCES AS WELL */ | ||
1606 | if (cur_func->bus) { | ||
1607 | /* in other words, it's a PPB */ | ||
1608 | count = 2; | ||
1609 | } else { | ||
1610 | count = 6; | ||
1611 | } | ||
1612 | |||
1613 | for (i = 0; i < count; i++) { | ||
1614 | if (cur_func->io[i]) { | ||
1615 | debug ("io[%d] exists\n", i); | ||
1616 | if (the_end > 0) | ||
1617 | ibmphp_remove_resource (cur_func->io[i]); | ||
1618 | cur_func->io[i] = NULL; | ||
1619 | } | ||
1620 | if (cur_func->mem[i]) { | ||
1621 | debug ("mem[%d] exists\n", i); | ||
1622 | if (the_end > 0) | ||
1623 | ibmphp_remove_resource (cur_func->mem[i]); | ||
1624 | cur_func->mem[i] = NULL; | ||
1625 | } | ||
1626 | if (cur_func->pfmem[i]) { | ||
1627 | debug ("pfmem[%d] exists\n", i); | ||
1628 | if (the_end > 0) | ||
1629 | ibmphp_remove_resource (cur_func->pfmem[i]); | ||
1630 | cur_func->pfmem[i] = NULL; | ||
1631 | } | ||
1632 | } | ||
1633 | |||
1634 | temp_func = cur_func->next; | ||
1635 | kfree (cur_func); | ||
1636 | cur_func = temp_func; | ||
1637 | } | ||
1638 | } | ||
1639 | |||
1640 | sl->func = NULL; | ||
1641 | *slot_cur = sl; | ||
1642 | debug ("%s - exit\n", __FUNCTION__); | ||
1643 | return 0; | ||
1644 | } | ||
1645 | |||
1646 | /* | ||
1647 | * add a new bus resulting from hot-plugging a PPB bridge with devices | ||
1648 | * | ||
1649 | * Input: bus and the amount of resources needed (we know we can assign those, | ||
1650 | * since they've been checked already | ||
1651 | * Output: bus added to the correct spot | ||
1652 | * 0, -1, error | ||
1653 | */ | ||
1654 | static int add_new_bus (struct bus_node *bus, struct resource_node *io, struct resource_node *mem, struct resource_node *pfmem, u8 parent_busno) | ||
1655 | { | ||
1656 | struct range_node *io_range = NULL; | ||
1657 | struct range_node *mem_range = NULL; | ||
1658 | struct range_node *pfmem_range = NULL; | ||
1659 | struct bus_node *cur_bus = NULL; | ||
1660 | |||
1661 | /* Trying to find the parent bus number */ | ||
1662 | if (parent_busno != 0xFF) { | ||
1663 | cur_bus = ibmphp_find_res_bus (parent_busno); | ||
1664 | if (!cur_bus) { | ||
1665 | err ("strange, cannot find bus which is supposed to be at the system... something is terribly wrong...\n"); | ||
1666 | return -ENODEV; | ||
1667 | } | ||
1668 | |||
1669 | list_add (&bus->bus_list, &cur_bus->bus_list); | ||
1670 | } | ||
1671 | if (io) { | ||
1672 | io_range = kmalloc(sizeof(*io_range), GFP_KERNEL); | ||
1673 | if (!io_range) { | ||
1674 | err ("out of system memory\n"); | ||
1675 | return -ENOMEM; | ||
1676 | } | ||
1677 | memset (io_range, 0, sizeof (struct range_node)); | ||
1678 | io_range->start = io->start; | ||
1679 | io_range->end = io->end; | ||
1680 | io_range->rangeno = 1; | ||
1681 | bus->noIORanges = 1; | ||
1682 | bus->rangeIO = io_range; | ||
1683 | } | ||
1684 | if (mem) { | ||
1685 | mem_range = kmalloc(sizeof(*mem_range), GFP_KERNEL); | ||
1686 | if (!mem_range) { | ||
1687 | err ("out of system memory\n"); | ||
1688 | return -ENOMEM; | ||
1689 | } | ||
1690 | memset (mem_range, 0, sizeof (struct range_node)); | ||
1691 | mem_range->start = mem->start; | ||
1692 | mem_range->end = mem->end; | ||
1693 | mem_range->rangeno = 1; | ||
1694 | bus->noMemRanges = 1; | ||
1695 | bus->rangeMem = mem_range; | ||
1696 | } | ||
1697 | if (pfmem) { | ||
1698 | pfmem_range = kmalloc(sizeof(*pfmem_range), GFP_KERNEL); | ||
1699 | if (!pfmem_range) { | ||
1700 | err ("out of system memory\n"); | ||
1701 | return -ENOMEM; | ||
1702 | } | ||
1703 | memset (pfmem_range, 0, sizeof (struct range_node)); | ||
1704 | pfmem_range->start = pfmem->start; | ||
1705 | pfmem_range->end = pfmem->end; | ||
1706 | pfmem_range->rangeno = 1; | ||
1707 | bus->noPFMemRanges = 1; | ||
1708 | bus->rangePFMem = pfmem_range; | ||
1709 | } | ||
1710 | return 0; | ||
1711 | } | ||
1712 | |||
1713 | /* | ||
1714 | * find the 1st available bus number for PPB to set as its secondary bus | ||
1715 | * Parameters: bus_number of the primary bus | ||
1716 | * Returns: bus_number of the secondary bus or 0xff in case of failure | ||
1717 | */ | ||
1718 | static u8 find_sec_number (u8 primary_busno, u8 slotno) | ||
1719 | { | ||
1720 | int min, max; | ||
1721 | u8 busno; | ||
1722 | struct bus_info *bus; | ||
1723 | struct bus_node *bus_cur; | ||
1724 | |||
1725 | bus = ibmphp_find_same_bus_num (primary_busno); | ||
1726 | if (!bus) { | ||
1727 | err ("cannot get slot range of the bus from the BIOS\n"); | ||
1728 | return 0xff; | ||
1729 | } | ||
1730 | max = bus->slot_max; | ||
1731 | min = bus->slot_min; | ||
1732 | if ((slotno > max) || (slotno < min)) { | ||
1733 | err ("got the wrong range\n"); | ||
1734 | return 0xff; | ||
1735 | } | ||
1736 | busno = (u8) (slotno - (u8) min); | ||
1737 | busno += primary_busno + 0x01; | ||
1738 | bus_cur = ibmphp_find_res_bus (busno); | ||
1739 | /* either there is no such bus number, or there are no ranges, which | ||
1740 | * can only happen if we removed the bridged device in previous load | ||
1741 | * of the driver, and now only have the skeleton bus struct | ||
1742 | */ | ||
1743 | if ((!bus_cur) || (!(bus_cur->rangeIO) && !(bus_cur->rangeMem) && !(bus_cur->rangePFMem))) | ||
1744 | return busno; | ||
1745 | return 0xff; | ||
1746 | } | ||
1747 | |||
diff --git a/drivers/pci/hotplug/ibmphp_res.c b/drivers/pci/hotplug/ibmphp_res.c new file mode 100644 index 000000000000..9c224c94d698 --- /dev/null +++ b/drivers/pci/hotplug/ibmphp_res.c | |||
@@ -0,0 +1,2156 @@ | |||
1 | /* | ||
2 | * IBM Hot Plug Controller Driver | ||
3 | * | ||
4 | * Written By: Irene Zubarev, IBM Corporation | ||
5 | * | ||
6 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
7 | * Copyright (C) 2001,2002 IBM Corp. | ||
8 | * | ||
9 | * All rights reserved. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or (at | ||
14 | * your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
19 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
20 | * details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
25 | * | ||
26 | * Send feedback to <gregkh@us.ibm.com> | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | #include <linux/module.h> | ||
31 | #include <linux/slab.h> | ||
32 | #include <linux/pci.h> | ||
33 | #include <linux/list.h> | ||
34 | #include <linux/init.h> | ||
35 | #include "ibmphp.h" | ||
36 | |||
37 | static int flags = 0; /* for testing */ | ||
38 | |||
39 | static void update_resources (struct bus_node *bus_cur, int type, int rangeno); | ||
40 | static int once_over (void); | ||
41 | static int remove_ranges (struct bus_node *, struct bus_node *); | ||
42 | static int update_bridge_ranges (struct bus_node **); | ||
43 | static int add_range (int type, struct range_node *, struct bus_node *); | ||
44 | static void fix_resources (struct bus_node *); | ||
45 | static struct bus_node *find_bus_wprev (u8, struct bus_node **, u8); | ||
46 | |||
47 | static LIST_HEAD(gbuses); | ||
48 | |||
49 | static struct bus_node * __init alloc_error_bus (struct ebda_pci_rsrc * curr, u8 busno, int flag) | ||
50 | { | ||
51 | struct bus_node * newbus; | ||
52 | |||
53 | if (!(curr) && !(flag)) { | ||
54 | err ("NULL pointer passed\n"); | ||
55 | return NULL; | ||
56 | } | ||
57 | |||
58 | newbus = kmalloc (sizeof (struct bus_node), GFP_KERNEL); | ||
59 | if (!newbus) { | ||
60 | err ("out of system memory\n"); | ||
61 | return NULL; | ||
62 | } | ||
63 | |||
64 | memset (newbus, 0, sizeof (struct bus_node)); | ||
65 | if (flag) | ||
66 | newbus->busno = busno; | ||
67 | else | ||
68 | newbus->busno = curr->bus_num; | ||
69 | list_add_tail (&newbus->bus_list, &gbuses); | ||
70 | return newbus; | ||
71 | } | ||
72 | |||
73 | static struct resource_node * __init alloc_resources (struct ebda_pci_rsrc * curr) | ||
74 | { | ||
75 | struct resource_node *rs; | ||
76 | |||
77 | if (!curr) { | ||
78 | err ("NULL passed to allocate\n"); | ||
79 | return NULL; | ||
80 | } | ||
81 | |||
82 | rs = kmalloc (sizeof (struct resource_node), GFP_KERNEL); | ||
83 | if (!rs) { | ||
84 | err ("out of system memory\n"); | ||
85 | return NULL; | ||
86 | } | ||
87 | memset (rs, 0, sizeof (struct resource_node)); | ||
88 | rs->busno = curr->bus_num; | ||
89 | rs->devfunc = curr->dev_fun; | ||
90 | rs->start = curr->start_addr; | ||
91 | rs->end = curr->end_addr; | ||
92 | rs->len = curr->end_addr - curr->start_addr + 1; | ||
93 | return rs; | ||
94 | } | ||
95 | |||
96 | static int __init alloc_bus_range (struct bus_node **new_bus, struct range_node **new_range, struct ebda_pci_rsrc *curr, int flag, u8 first_bus) | ||
97 | { | ||
98 | struct bus_node * newbus; | ||
99 | struct range_node *newrange; | ||
100 | u8 num_ranges = 0; | ||
101 | |||
102 | if (first_bus) { | ||
103 | newbus = kmalloc (sizeof (struct bus_node), GFP_KERNEL); | ||
104 | if (!newbus) { | ||
105 | err ("out of system memory.\n"); | ||
106 | return -ENOMEM; | ||
107 | } | ||
108 | memset (newbus, 0, sizeof (struct bus_node)); | ||
109 | newbus->busno = curr->bus_num; | ||
110 | } else { | ||
111 | newbus = *new_bus; | ||
112 | switch (flag) { | ||
113 | case MEM: | ||
114 | num_ranges = newbus->noMemRanges; | ||
115 | break; | ||
116 | case PFMEM: | ||
117 | num_ranges = newbus->noPFMemRanges; | ||
118 | break; | ||
119 | case IO: | ||
120 | num_ranges = newbus->noIORanges; | ||
121 | break; | ||
122 | } | ||
123 | } | ||
124 | |||
125 | newrange = kmalloc (sizeof (struct range_node), GFP_KERNEL); | ||
126 | if (!newrange) { | ||
127 | if (first_bus) | ||
128 | kfree (newbus); | ||
129 | err ("out of system memory\n"); | ||
130 | return -ENOMEM; | ||
131 | } | ||
132 | memset (newrange, 0, sizeof (struct range_node)); | ||
133 | newrange->start = curr->start_addr; | ||
134 | newrange->end = curr->end_addr; | ||
135 | |||
136 | if (first_bus || (!num_ranges)) | ||
137 | newrange->rangeno = 1; | ||
138 | else { | ||
139 | /* need to insert our range */ | ||
140 | add_range (flag, newrange, newbus); | ||
141 | debug ("%d resource Primary Bus inserted on bus %x [%x - %x]\n", flag, newbus->busno, newrange->start, newrange->end); | ||
142 | } | ||
143 | |||
144 | switch (flag) { | ||
145 | case MEM: | ||
146 | newbus->rangeMem = newrange; | ||
147 | if (first_bus) | ||
148 | newbus->noMemRanges = 1; | ||
149 | else { | ||
150 | debug ("First Memory Primary on bus %x, [%x - %x]\n", newbus->busno, newrange->start, newrange->end); | ||
151 | ++newbus->noMemRanges; | ||
152 | fix_resources (newbus); | ||
153 | } | ||
154 | break; | ||
155 | case IO: | ||
156 | newbus->rangeIO = newrange; | ||
157 | if (first_bus) | ||
158 | newbus->noIORanges = 1; | ||
159 | else { | ||
160 | debug ("First IO Primary on bus %x, [%x - %x]\n", newbus->busno, newrange->start, newrange->end); | ||
161 | ++newbus->noIORanges; | ||
162 | fix_resources (newbus); | ||
163 | } | ||
164 | break; | ||
165 | case PFMEM: | ||
166 | newbus->rangePFMem = newrange; | ||
167 | if (first_bus) | ||
168 | newbus->noPFMemRanges = 1; | ||
169 | else { | ||
170 | debug ("1st PFMemory Primary on Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); | ||
171 | ++newbus->noPFMemRanges; | ||
172 | fix_resources (newbus); | ||
173 | } | ||
174 | |||
175 | break; | ||
176 | } | ||
177 | |||
178 | *new_bus = newbus; | ||
179 | *new_range = newrange; | ||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | |||
184 | /* Notes: | ||
185 | * 1. The ranges are ordered. The buses are not ordered. (First come) | ||
186 | * | ||
187 | * 2. If cannot allocate out of PFMem range, allocate from Mem ranges. PFmemFromMem | ||
188 | * are not sorted. (no need since use mem node). To not change the entire code, we | ||
189 | * also add mem node whenever this case happens so as not to change | ||
190 | * ibmphp_check_mem_resource etc (and since it really is taking Mem resource) | ||
191 | */ | ||
192 | |||
193 | /***************************************************************************** | ||
194 | * This is the Resource Management initialization function. It will go through | ||
195 | * the Resource list taken from EBDA and fill in this module's data structures | ||
196 | * | ||
197 | * THIS IS NOT TAKING INTO CONSIDERATION IO RESTRICTIONS OF PRIMARY BUSES, | ||
198 | * SINCE WE'RE GOING TO ASSUME FOR NOW WE DON'T HAVE THOSE ON OUR BUSES FOR NOW | ||
199 | * | ||
200 | * Input: ptr to the head of the resource list from EBDA | ||
201 | * Output: 0, -1 or error codes | ||
202 | ***************************************************************************/ | ||
203 | int __init ibmphp_rsrc_init (void) | ||
204 | { | ||
205 | struct ebda_pci_rsrc *curr; | ||
206 | struct range_node *newrange = NULL; | ||
207 | struct bus_node *newbus = NULL; | ||
208 | struct bus_node *bus_cur; | ||
209 | struct bus_node *bus_prev; | ||
210 | struct list_head *tmp; | ||
211 | struct resource_node *new_io = NULL; | ||
212 | struct resource_node *new_mem = NULL; | ||
213 | struct resource_node *new_pfmem = NULL; | ||
214 | int rc; | ||
215 | struct list_head *tmp_ebda; | ||
216 | |||
217 | list_for_each (tmp_ebda, &ibmphp_ebda_pci_rsrc_head) { | ||
218 | curr = list_entry (tmp_ebda, struct ebda_pci_rsrc, ebda_pci_rsrc_list); | ||
219 | if (!(curr->rsrc_type & PCIDEVMASK)) { | ||
220 | /* EBDA still lists non PCI devices, so ignore... */ | ||
221 | debug ("this is not a PCI DEVICE in rsrc_init, please take care\n"); | ||
222 | // continue; | ||
223 | } | ||
224 | |||
225 | /* this is a primary bus resource */ | ||
226 | if (curr->rsrc_type & PRIMARYBUSMASK) { | ||
227 | /* memory */ | ||
228 | if ((curr->rsrc_type & RESTYPE) == MMASK) { | ||
229 | /* no bus structure exists in place yet */ | ||
230 | if (list_empty (&gbuses)) { | ||
231 | if ((rc = alloc_bus_range (&newbus, &newrange, curr, MEM, 1))) | ||
232 | return rc; | ||
233 | list_add_tail (&newbus->bus_list, &gbuses); | ||
234 | debug ("gbuses = NULL, Memory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); | ||
235 | } else { | ||
236 | bus_cur = find_bus_wprev (curr->bus_num, &bus_prev, 1); | ||
237 | /* found our bus */ | ||
238 | if (bus_cur) { | ||
239 | rc = alloc_bus_range (&bus_cur, &newrange, curr, MEM, 0); | ||
240 | if (rc) | ||
241 | return rc; | ||
242 | } else { | ||
243 | /* went through all the buses and didn't find ours, need to create a new bus node */ | ||
244 | if ((rc = alloc_bus_range (&newbus, &newrange, curr, MEM, 1))) | ||
245 | return rc; | ||
246 | |||
247 | list_add_tail (&newbus->bus_list, &gbuses); | ||
248 | debug ("New Bus, Memory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); | ||
249 | } | ||
250 | } | ||
251 | } else if ((curr->rsrc_type & RESTYPE) == PFMASK) { | ||
252 | /* prefetchable memory */ | ||
253 | if (list_empty (&gbuses)) { | ||
254 | /* no bus structure exists in place yet */ | ||
255 | if ((rc = alloc_bus_range (&newbus, &newrange, curr, PFMEM, 1))) | ||
256 | return rc; | ||
257 | list_add_tail (&newbus->bus_list, &gbuses); | ||
258 | debug ("gbuses = NULL, PFMemory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); | ||
259 | } else { | ||
260 | bus_cur = find_bus_wprev (curr->bus_num, &bus_prev, 1); | ||
261 | if (bus_cur) { | ||
262 | /* found our bus */ | ||
263 | rc = alloc_bus_range (&bus_cur, &newrange, curr, PFMEM, 0); | ||
264 | if (rc) | ||
265 | return rc; | ||
266 | } else { | ||
267 | /* went through all the buses and didn't find ours, need to create a new bus node */ | ||
268 | if ((rc = alloc_bus_range (&newbus, &newrange, curr, PFMEM, 1))) | ||
269 | return rc; | ||
270 | list_add_tail (&newbus->bus_list, &gbuses); | ||
271 | debug ("1st Bus, PFMemory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); | ||
272 | } | ||
273 | } | ||
274 | } else if ((curr->rsrc_type & RESTYPE) == IOMASK) { | ||
275 | /* IO */ | ||
276 | if (list_empty (&gbuses)) { | ||
277 | /* no bus structure exists in place yet */ | ||
278 | if ((rc = alloc_bus_range (&newbus, &newrange, curr, IO, 1))) | ||
279 | return rc; | ||
280 | list_add_tail (&newbus->bus_list, &gbuses); | ||
281 | debug ("gbuses = NULL, IO Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); | ||
282 | } else { | ||
283 | bus_cur = find_bus_wprev (curr->bus_num, &bus_prev, 1); | ||
284 | if (bus_cur) { | ||
285 | rc = alloc_bus_range (&bus_cur, &newrange, curr, IO, 0); | ||
286 | if (rc) | ||
287 | return rc; | ||
288 | } else { | ||
289 | /* went through all the buses and didn't find ours, need to create a new bus node */ | ||
290 | if ((rc = alloc_bus_range (&newbus, &newrange, curr, IO, 1))) | ||
291 | return rc; | ||
292 | list_add_tail (&newbus->bus_list, &gbuses); | ||
293 | debug ("1st Bus, IO Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); | ||
294 | } | ||
295 | } | ||
296 | |||
297 | } else { | ||
298 | ; /* type is reserved WHAT TO DO IN THIS CASE??? | ||
299 | NOTHING TO DO??? */ | ||
300 | } | ||
301 | } else { | ||
302 | /* regular pci device resource */ | ||
303 | if ((curr->rsrc_type & RESTYPE) == MMASK) { | ||
304 | /* Memory resource */ | ||
305 | new_mem = alloc_resources (curr); | ||
306 | if (!new_mem) | ||
307 | return -ENOMEM; | ||
308 | new_mem->type = MEM; | ||
309 | /* | ||
310 | * if it didn't find the bus, means PCI dev | ||
311 | * came b4 the Primary Bus info, so need to | ||
312 | * create a bus rangeno becomes a problem... | ||
313 | * assign a -1 and then update once the range | ||
314 | * actually appears... | ||
315 | */ | ||
316 | if (ibmphp_add_resource (new_mem) < 0) { | ||
317 | newbus = alloc_error_bus (curr, 0, 0); | ||
318 | if (!newbus) | ||
319 | return -ENOMEM; | ||
320 | newbus->firstMem = new_mem; | ||
321 | ++newbus->needMemUpdate; | ||
322 | new_mem->rangeno = -1; | ||
323 | } | ||
324 | debug ("Memory resource for device %x, bus %x, [%x - %x]\n", new_mem->devfunc, new_mem->busno, new_mem->start, new_mem->end); | ||
325 | |||
326 | } else if ((curr->rsrc_type & RESTYPE) == PFMASK) { | ||
327 | /* PFMemory resource */ | ||
328 | new_pfmem = alloc_resources (curr); | ||
329 | if (!new_pfmem) | ||
330 | return -ENOMEM; | ||
331 | new_pfmem->type = PFMEM; | ||
332 | new_pfmem->fromMem = FALSE; | ||
333 | if (ibmphp_add_resource (new_pfmem) < 0) { | ||
334 | newbus = alloc_error_bus (curr, 0, 0); | ||
335 | if (!newbus) | ||
336 | return -ENOMEM; | ||
337 | newbus->firstPFMem = new_pfmem; | ||
338 | ++newbus->needPFMemUpdate; | ||
339 | new_pfmem->rangeno = -1; | ||
340 | } | ||
341 | |||
342 | debug ("PFMemory resource for device %x, bus %x, [%x - %x]\n", new_pfmem->devfunc, new_pfmem->busno, new_pfmem->start, new_pfmem->end); | ||
343 | } else if ((curr->rsrc_type & RESTYPE) == IOMASK) { | ||
344 | /* IO resource */ | ||
345 | new_io = alloc_resources (curr); | ||
346 | if (!new_io) | ||
347 | return -ENOMEM; | ||
348 | new_io->type = IO; | ||
349 | |||
350 | /* | ||
351 | * if it didn't find the bus, means PCI dev | ||
352 | * came b4 the Primary Bus info, so need to | ||
353 | * create a bus rangeno becomes a problem... | ||
354 | * Can assign a -1 and then update once the | ||
355 | * range actually appears... | ||
356 | */ | ||
357 | if (ibmphp_add_resource (new_io) < 0) { | ||
358 | newbus = alloc_error_bus (curr, 0, 0); | ||
359 | if (!newbus) | ||
360 | return -ENOMEM; | ||
361 | newbus->firstIO = new_io; | ||
362 | ++newbus->needIOUpdate; | ||
363 | new_io->rangeno = -1; | ||
364 | } | ||
365 | debug ("IO resource for device %x, bus %x, [%x - %x]\n", new_io->devfunc, new_io->busno, new_io->start, new_io->end); | ||
366 | } | ||
367 | } | ||
368 | } | ||
369 | |||
370 | list_for_each (tmp, &gbuses) { | ||
371 | bus_cur = list_entry (tmp, struct bus_node, bus_list); | ||
372 | /* This is to get info about PPB resources, since EBDA doesn't put this info into the primary bus info */ | ||
373 | rc = update_bridge_ranges (&bus_cur); | ||
374 | if (rc) | ||
375 | return rc; | ||
376 | } | ||
377 | rc = once_over (); /* This is to align ranges (so no -1) */ | ||
378 | if (rc) | ||
379 | return rc; | ||
380 | return 0; | ||
381 | } | ||
382 | |||
383 | /******************************************************************************** | ||
384 | * This function adds a range into a sorted list of ranges per bus for a particular | ||
385 | * range type, it then calls another routine to update the range numbers on the | ||
386 | * pci devices' resources for the appropriate resource | ||
387 | * | ||
388 | * Input: type of the resource, range to add, current bus | ||
389 | * Output: 0 or -1, bus and range ptrs | ||
390 | ********************************************************************************/ | ||
391 | static int add_range (int type, struct range_node *range, struct bus_node *bus_cur) | ||
392 | { | ||
393 | struct range_node *range_cur = NULL; | ||
394 | struct range_node *range_prev; | ||
395 | int count = 0, i_init; | ||
396 | int noRanges = 0; | ||
397 | |||
398 | switch (type) { | ||
399 | case MEM: | ||
400 | range_cur = bus_cur->rangeMem; | ||
401 | noRanges = bus_cur->noMemRanges; | ||
402 | break; | ||
403 | case PFMEM: | ||
404 | range_cur = bus_cur->rangePFMem; | ||
405 | noRanges = bus_cur->noPFMemRanges; | ||
406 | break; | ||
407 | case IO: | ||
408 | range_cur = bus_cur->rangeIO; | ||
409 | noRanges = bus_cur->noIORanges; | ||
410 | break; | ||
411 | } | ||
412 | |||
413 | range_prev = NULL; | ||
414 | while (range_cur) { | ||
415 | if (range->start < range_cur->start) | ||
416 | break; | ||
417 | range_prev = range_cur; | ||
418 | range_cur = range_cur->next; | ||
419 | count = count + 1; | ||
420 | } | ||
421 | if (!count) { | ||
422 | /* our range will go at the beginning of the list */ | ||
423 | switch (type) { | ||
424 | case MEM: | ||
425 | bus_cur->rangeMem = range; | ||
426 | break; | ||
427 | case PFMEM: | ||
428 | bus_cur->rangePFMem = range; | ||
429 | break; | ||
430 | case IO: | ||
431 | bus_cur->rangeIO = range; | ||
432 | break; | ||
433 | } | ||
434 | range->next = range_cur; | ||
435 | range->rangeno = 1; | ||
436 | i_init = 0; | ||
437 | } else if (!range_cur) { | ||
438 | /* our range will go at the end of the list */ | ||
439 | range->next = NULL; | ||
440 | range_prev->next = range; | ||
441 | range->rangeno = range_prev->rangeno + 1; | ||
442 | return 0; | ||
443 | } else { | ||
444 | /* the range is in the middle */ | ||
445 | range_prev->next = range; | ||
446 | range->next = range_cur; | ||
447 | range->rangeno = range_cur->rangeno; | ||
448 | i_init = range_prev->rangeno; | ||
449 | } | ||
450 | |||
451 | for (count = i_init; count < noRanges; ++count) { | ||
452 | ++range_cur->rangeno; | ||
453 | range_cur = range_cur->next; | ||
454 | } | ||
455 | |||
456 | update_resources (bus_cur, type, i_init + 1); | ||
457 | return 0; | ||
458 | } | ||
459 | |||
460 | /******************************************************************************* | ||
461 | * This routine goes through the list of resources of type 'type' and updates | ||
462 | * the range numbers that they correspond to. It was called from add_range fnc | ||
463 | * | ||
464 | * Input: bus, type of the resource, the rangeno starting from which to update | ||
465 | ******************************************************************************/ | ||
466 | static void update_resources (struct bus_node *bus_cur, int type, int rangeno) | ||
467 | { | ||
468 | struct resource_node *res = NULL; | ||
469 | u8 eol = FALSE; /* end of list indicator */ | ||
470 | |||
471 | switch (type) { | ||
472 | case MEM: | ||
473 | if (bus_cur->firstMem) | ||
474 | res = bus_cur->firstMem; | ||
475 | break; | ||
476 | case PFMEM: | ||
477 | if (bus_cur->firstPFMem) | ||
478 | res = bus_cur->firstPFMem; | ||
479 | break; | ||
480 | case IO: | ||
481 | if (bus_cur->firstIO) | ||
482 | res = bus_cur->firstIO; | ||
483 | break; | ||
484 | } | ||
485 | |||
486 | if (res) { | ||
487 | while (res) { | ||
488 | if (res->rangeno == rangeno) | ||
489 | break; | ||
490 | if (res->next) | ||
491 | res = res->next; | ||
492 | else if (res->nextRange) | ||
493 | res = res->nextRange; | ||
494 | else { | ||
495 | eol = TRUE; | ||
496 | break; | ||
497 | } | ||
498 | } | ||
499 | |||
500 | if (!eol) { | ||
501 | /* found the range */ | ||
502 | while (res) { | ||
503 | ++res->rangeno; | ||
504 | res = res->next; | ||
505 | } | ||
506 | } | ||
507 | } | ||
508 | } | ||
509 | |||
510 | static void fix_me (struct resource_node *res, struct bus_node *bus_cur, struct range_node *range) | ||
511 | { | ||
512 | char * str = ""; | ||
513 | switch (res->type) { | ||
514 | case IO: | ||
515 | str = "io"; | ||
516 | break; | ||
517 | case MEM: | ||
518 | str = "mem"; | ||
519 | break; | ||
520 | case PFMEM: | ||
521 | str = "pfmem"; | ||
522 | break; | ||
523 | } | ||
524 | |||
525 | while (res) { | ||
526 | if (res->rangeno == -1) { | ||
527 | while (range) { | ||
528 | if ((res->start >= range->start) && (res->end <= range->end)) { | ||
529 | res->rangeno = range->rangeno; | ||
530 | debug ("%s->rangeno in fix_resources is %d\n", str, res->rangeno); | ||
531 | switch (res->type) { | ||
532 | case IO: | ||
533 | --bus_cur->needIOUpdate; | ||
534 | break; | ||
535 | case MEM: | ||
536 | --bus_cur->needMemUpdate; | ||
537 | break; | ||
538 | case PFMEM: | ||
539 | --bus_cur->needPFMemUpdate; | ||
540 | break; | ||
541 | } | ||
542 | break; | ||
543 | } | ||
544 | range = range->next; | ||
545 | } | ||
546 | } | ||
547 | if (res->next) | ||
548 | res = res->next; | ||
549 | else | ||
550 | res = res->nextRange; | ||
551 | } | ||
552 | |||
553 | } | ||
554 | |||
555 | /***************************************************************************** | ||
556 | * This routine reassigns the range numbers to the resources that had a -1 | ||
557 | * This case can happen only if upon initialization, resources taken by pci dev | ||
558 | * appear in EBDA before the resources allocated for that bus, since we don't | ||
559 | * know the range, we assign -1, and this routine is called after a new range | ||
560 | * is assigned to see the resources with unknown range belong to the added range | ||
561 | * | ||
562 | * Input: current bus | ||
563 | * Output: none, list of resources for that bus are fixed if can be | ||
564 | *******************************************************************************/ | ||
565 | static void fix_resources (struct bus_node *bus_cur) | ||
566 | { | ||
567 | struct range_node *range; | ||
568 | struct resource_node *res; | ||
569 | |||
570 | debug ("%s - bus_cur->busno = %d\n", __FUNCTION__, bus_cur->busno); | ||
571 | |||
572 | if (bus_cur->needIOUpdate) { | ||
573 | res = bus_cur->firstIO; | ||
574 | range = bus_cur->rangeIO; | ||
575 | fix_me (res, bus_cur, range); | ||
576 | } | ||
577 | if (bus_cur->needMemUpdate) { | ||
578 | res = bus_cur->firstMem; | ||
579 | range = bus_cur->rangeMem; | ||
580 | fix_me (res, bus_cur, range); | ||
581 | } | ||
582 | if (bus_cur->needPFMemUpdate) { | ||
583 | res = bus_cur->firstPFMem; | ||
584 | range = bus_cur->rangePFMem; | ||
585 | fix_me (res, bus_cur, range); | ||
586 | } | ||
587 | } | ||
588 | |||
589 | /******************************************************************************* | ||
590 | * This routine adds a resource to the list of resources to the appropriate bus | ||
591 | * based on their resource type and sorted by their starting addresses. It assigns | ||
592 | * the ptrs to next and nextRange if needed. | ||
593 | * | ||
594 | * Input: resource ptr | ||
595 | * Output: ptrs assigned (to the node) | ||
596 | * 0 or -1 | ||
597 | *******************************************************************************/ | ||
598 | int ibmphp_add_resource (struct resource_node *res) | ||
599 | { | ||
600 | struct resource_node *res_cur; | ||
601 | struct resource_node *res_prev; | ||
602 | struct bus_node *bus_cur; | ||
603 | struct range_node *range_cur = NULL; | ||
604 | struct resource_node *res_start = NULL; | ||
605 | |||
606 | debug ("%s - enter\n", __FUNCTION__); | ||
607 | |||
608 | if (!res) { | ||
609 | err ("NULL passed to add\n"); | ||
610 | return -ENODEV; | ||
611 | } | ||
612 | |||
613 | bus_cur = find_bus_wprev (res->busno, NULL, 0); | ||
614 | |||
615 | if (!bus_cur) { | ||
616 | /* didn't find a bus, smth's wrong!!! */ | ||
617 | debug ("no bus in the system, either pci_dev's wrong or allocation failed\n"); | ||
618 | return -ENODEV; | ||
619 | } | ||
620 | |||
621 | /* Normal case */ | ||
622 | switch (res->type) { | ||
623 | case IO: | ||
624 | range_cur = bus_cur->rangeIO; | ||
625 | res_start = bus_cur->firstIO; | ||
626 | break; | ||
627 | case MEM: | ||
628 | range_cur = bus_cur->rangeMem; | ||
629 | res_start = bus_cur->firstMem; | ||
630 | break; | ||
631 | case PFMEM: | ||
632 | range_cur = bus_cur->rangePFMem; | ||
633 | res_start = bus_cur->firstPFMem; | ||
634 | break; | ||
635 | default: | ||
636 | err ("cannot read the type of the resource to add... problem\n"); | ||
637 | return -EINVAL; | ||
638 | } | ||
639 | while (range_cur) { | ||
640 | if ((res->start >= range_cur->start) && (res->end <= range_cur->end)) { | ||
641 | res->rangeno = range_cur->rangeno; | ||
642 | break; | ||
643 | } | ||
644 | range_cur = range_cur->next; | ||
645 | } | ||
646 | |||
647 | /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
648 | * this is again the case of rangeno = -1 | ||
649 | * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
650 | */ | ||
651 | |||
652 | if (!range_cur) { | ||
653 | switch (res->type) { | ||
654 | case IO: | ||
655 | ++bus_cur->needIOUpdate; | ||
656 | break; | ||
657 | case MEM: | ||
658 | ++bus_cur->needMemUpdate; | ||
659 | break; | ||
660 | case PFMEM: | ||
661 | ++bus_cur->needPFMemUpdate; | ||
662 | break; | ||
663 | } | ||
664 | res->rangeno = -1; | ||
665 | } | ||
666 | |||
667 | debug ("The range is %d\n", res->rangeno); | ||
668 | if (!res_start) { | ||
669 | /* no first{IO,Mem,Pfmem} on the bus, 1st IO/Mem/Pfmem resource ever */ | ||
670 | switch (res->type) { | ||
671 | case IO: | ||
672 | bus_cur->firstIO = res; | ||
673 | break; | ||
674 | case MEM: | ||
675 | bus_cur->firstMem = res; | ||
676 | break; | ||
677 | case PFMEM: | ||
678 | bus_cur->firstPFMem = res; | ||
679 | break; | ||
680 | } | ||
681 | res->next = NULL; | ||
682 | res->nextRange = NULL; | ||
683 | } else { | ||
684 | res_cur = res_start; | ||
685 | res_prev = NULL; | ||
686 | |||
687 | debug ("res_cur->rangeno is %d\n", res_cur->rangeno); | ||
688 | |||
689 | while (res_cur) { | ||
690 | if (res_cur->rangeno >= res->rangeno) | ||
691 | break; | ||
692 | res_prev = res_cur; | ||
693 | if (res_cur->next) | ||
694 | res_cur = res_cur->next; | ||
695 | else | ||
696 | res_cur = res_cur->nextRange; | ||
697 | } | ||
698 | |||
699 | if (!res_cur) { | ||
700 | /* at the end of the resource list */ | ||
701 | debug ("i should be here, [%x - %x]\n", res->start, res->end); | ||
702 | res_prev->nextRange = res; | ||
703 | res->next = NULL; | ||
704 | res->nextRange = NULL; | ||
705 | } else if (res_cur->rangeno == res->rangeno) { | ||
706 | /* in the same range */ | ||
707 | while (res_cur) { | ||
708 | if (res->start < res_cur->start) | ||
709 | break; | ||
710 | res_prev = res_cur; | ||
711 | res_cur = res_cur->next; | ||
712 | } | ||
713 | if (!res_cur) { | ||
714 | /* the last resource in this range */ | ||
715 | res_prev->next = res; | ||
716 | res->next = NULL; | ||
717 | res->nextRange = res_prev->nextRange; | ||
718 | res_prev->nextRange = NULL; | ||
719 | } else if (res->start < res_cur->start) { | ||
720 | /* at the beginning or middle of the range */ | ||
721 | if (!res_prev) { | ||
722 | switch (res->type) { | ||
723 | case IO: | ||
724 | bus_cur->firstIO = res; | ||
725 | break; | ||
726 | case MEM: | ||
727 | bus_cur->firstMem = res; | ||
728 | break; | ||
729 | case PFMEM: | ||
730 | bus_cur->firstPFMem = res; | ||
731 | break; | ||
732 | } | ||
733 | } else if (res_prev->rangeno == res_cur->rangeno) | ||
734 | res_prev->next = res; | ||
735 | else | ||
736 | res_prev->nextRange = res; | ||
737 | |||
738 | res->next = res_cur; | ||
739 | res->nextRange = NULL; | ||
740 | } | ||
741 | } else { | ||
742 | /* this is the case where it is 1st occurrence of the range */ | ||
743 | if (!res_prev) { | ||
744 | /* at the beginning of the resource list */ | ||
745 | res->next = NULL; | ||
746 | switch (res->type) { | ||
747 | case IO: | ||
748 | res->nextRange = bus_cur->firstIO; | ||
749 | bus_cur->firstIO = res; | ||
750 | break; | ||
751 | case MEM: | ||
752 | res->nextRange = bus_cur->firstMem; | ||
753 | bus_cur->firstMem = res; | ||
754 | break; | ||
755 | case PFMEM: | ||
756 | res->nextRange = bus_cur->firstPFMem; | ||
757 | bus_cur->firstPFMem = res; | ||
758 | break; | ||
759 | } | ||
760 | } else if (res_cur->rangeno > res->rangeno) { | ||
761 | /* in the middle of the resource list */ | ||
762 | res_prev->nextRange = res; | ||
763 | res->next = NULL; | ||
764 | res->nextRange = res_cur; | ||
765 | } | ||
766 | } | ||
767 | } | ||
768 | |||
769 | debug ("%s - exit\n", __FUNCTION__); | ||
770 | return 0; | ||
771 | } | ||
772 | |||
773 | /**************************************************************************** | ||
774 | * This routine will remove the resource from the list of resources | ||
775 | * | ||
776 | * Input: io, mem, and/or pfmem resource to be deleted | ||
777 | * Ouput: modified resource list | ||
778 | * 0 or error code | ||
779 | ****************************************************************************/ | ||
780 | int ibmphp_remove_resource (struct resource_node *res) | ||
781 | { | ||
782 | struct bus_node *bus_cur; | ||
783 | struct resource_node *res_cur = NULL; | ||
784 | struct resource_node *res_prev; | ||
785 | struct resource_node *mem_cur; | ||
786 | char * type = ""; | ||
787 | |||
788 | if (!res) { | ||
789 | err ("resource to remove is NULL\n"); | ||
790 | return -ENODEV; | ||
791 | } | ||
792 | |||
793 | bus_cur = find_bus_wprev (res->busno, NULL, 0); | ||
794 | |||
795 | if (!bus_cur) { | ||
796 | err ("cannot find corresponding bus of the io resource to remove " | ||
797 | "bailing out...\n"); | ||
798 | return -ENODEV; | ||
799 | } | ||
800 | |||
801 | switch (res->type) { | ||
802 | case IO: | ||
803 | res_cur = bus_cur->firstIO; | ||
804 | type = "io"; | ||
805 | break; | ||
806 | case MEM: | ||
807 | res_cur = bus_cur->firstMem; | ||
808 | type = "mem"; | ||
809 | break; | ||
810 | case PFMEM: | ||
811 | res_cur = bus_cur->firstPFMem; | ||
812 | type = "pfmem"; | ||
813 | break; | ||
814 | default: | ||
815 | err ("unknown type for resource to remove\n"); | ||
816 | return -EINVAL; | ||
817 | } | ||
818 | res_prev = NULL; | ||
819 | |||
820 | while (res_cur) { | ||
821 | if ((res_cur->start == res->start) && (res_cur->end == res->end)) | ||
822 | break; | ||
823 | res_prev = res_cur; | ||
824 | if (res_cur->next) | ||
825 | res_cur = res_cur->next; | ||
826 | else | ||
827 | res_cur = res_cur->nextRange; | ||
828 | } | ||
829 | |||
830 | if (!res_cur) { | ||
831 | if (res->type == PFMEM) { | ||
832 | /* | ||
833 | * case where pfmem might be in the PFMemFromMem list | ||
834 | * so will also need to remove the corresponding mem | ||
835 | * entry | ||
836 | */ | ||
837 | res_cur = bus_cur->firstPFMemFromMem; | ||
838 | res_prev = NULL; | ||
839 | |||
840 | while (res_cur) { | ||
841 | if ((res_cur->start == res->start) && (res_cur->end == res->end)) { | ||
842 | mem_cur = bus_cur->firstMem; | ||
843 | while (mem_cur) { | ||
844 | if ((mem_cur->start == res_cur->start) | ||
845 | && (mem_cur->end == res_cur->end)) | ||
846 | break; | ||
847 | if (mem_cur->next) | ||
848 | mem_cur = mem_cur->next; | ||
849 | else | ||
850 | mem_cur = mem_cur->nextRange; | ||
851 | } | ||
852 | if (!mem_cur) { | ||
853 | err ("cannot find corresponding mem node for pfmem...\n"); | ||
854 | return -EINVAL; | ||
855 | } | ||
856 | |||
857 | ibmphp_remove_resource (mem_cur); | ||
858 | if (!res_prev) | ||
859 | bus_cur->firstPFMemFromMem = res_cur->next; | ||
860 | else | ||
861 | res_prev->next = res_cur->next; | ||
862 | kfree (res_cur); | ||
863 | return 0; | ||
864 | } | ||
865 | res_prev = res_cur; | ||
866 | if (res_cur->next) | ||
867 | res_cur = res_cur->next; | ||
868 | else | ||
869 | res_cur = res_cur->nextRange; | ||
870 | } | ||
871 | if (!res_cur) { | ||
872 | err ("cannot find pfmem to delete...\n"); | ||
873 | return -EINVAL; | ||
874 | } | ||
875 | } else { | ||
876 | err ("the %s resource is not in the list to be deleted...\n", type); | ||
877 | return -EINVAL; | ||
878 | } | ||
879 | } | ||
880 | if (!res_prev) { | ||
881 | /* first device to be deleted */ | ||
882 | if (res_cur->next) { | ||
883 | switch (res->type) { | ||
884 | case IO: | ||
885 | bus_cur->firstIO = res_cur->next; | ||
886 | break; | ||
887 | case MEM: | ||
888 | bus_cur->firstMem = res_cur->next; | ||
889 | break; | ||
890 | case PFMEM: | ||
891 | bus_cur->firstPFMem = res_cur->next; | ||
892 | break; | ||
893 | } | ||
894 | } else if (res_cur->nextRange) { | ||
895 | switch (res->type) { | ||
896 | case IO: | ||
897 | bus_cur->firstIO = res_cur->nextRange; | ||
898 | break; | ||
899 | case MEM: | ||
900 | bus_cur->firstMem = res_cur->nextRange; | ||
901 | break; | ||
902 | case PFMEM: | ||
903 | bus_cur->firstPFMem = res_cur->nextRange; | ||
904 | break; | ||
905 | } | ||
906 | } else { | ||
907 | switch (res->type) { | ||
908 | case IO: | ||
909 | bus_cur->firstIO = NULL; | ||
910 | break; | ||
911 | case MEM: | ||
912 | bus_cur->firstMem = NULL; | ||
913 | break; | ||
914 | case PFMEM: | ||
915 | bus_cur->firstPFMem = NULL; | ||
916 | break; | ||
917 | } | ||
918 | } | ||
919 | kfree (res_cur); | ||
920 | return 0; | ||
921 | } else { | ||
922 | if (res_cur->next) { | ||
923 | if (res_prev->rangeno == res_cur->rangeno) | ||
924 | res_prev->next = res_cur->next; | ||
925 | else | ||
926 | res_prev->nextRange = res_cur->next; | ||
927 | } else if (res_cur->nextRange) { | ||
928 | res_prev->next = NULL; | ||
929 | res_prev->nextRange = res_cur->nextRange; | ||
930 | } else { | ||
931 | res_prev->next = NULL; | ||
932 | res_prev->nextRange = NULL; | ||
933 | } | ||
934 | kfree (res_cur); | ||
935 | return 0; | ||
936 | } | ||
937 | |||
938 | return 0; | ||
939 | } | ||
940 | |||
941 | static struct range_node * find_range (struct bus_node *bus_cur, struct resource_node * res) | ||
942 | { | ||
943 | struct range_node * range = NULL; | ||
944 | |||
945 | switch (res->type) { | ||
946 | case IO: | ||
947 | range = bus_cur->rangeIO; | ||
948 | break; | ||
949 | case MEM: | ||
950 | range = bus_cur->rangeMem; | ||
951 | break; | ||
952 | case PFMEM: | ||
953 | range = bus_cur->rangePFMem; | ||
954 | break; | ||
955 | default: | ||
956 | err ("cannot read resource type in find_range\n"); | ||
957 | } | ||
958 | |||
959 | while (range) { | ||
960 | if (res->rangeno == range->rangeno) | ||
961 | break; | ||
962 | range = range->next; | ||
963 | } | ||
964 | return range; | ||
965 | } | ||
966 | |||
967 | /***************************************************************************** | ||
968 | * This routine will check to make sure the io/mem/pfmem->len that the device asked for | ||
969 | * can fit w/i our list of available IO/MEM/PFMEM resources. If cannot, returns -EINVAL, | ||
970 | * otherwise, returns 0 | ||
971 | * | ||
972 | * Input: resource | ||
973 | * Ouput: the correct start and end address are inputted into the resource node, | ||
974 | * 0 or -EINVAL | ||
975 | *****************************************************************************/ | ||
976 | int ibmphp_check_resource (struct resource_node *res, u8 bridge) | ||
977 | { | ||
978 | struct bus_node *bus_cur; | ||
979 | struct range_node *range = NULL; | ||
980 | struct resource_node *res_prev; | ||
981 | struct resource_node *res_cur = NULL; | ||
982 | u32 len_cur = 0, start_cur = 0, len_tmp = 0; | ||
983 | int noranges = 0; | ||
984 | u32 tmp_start; /* this is to make sure start address is divisible by the length needed */ | ||
985 | u32 tmp_divide; | ||
986 | u8 flag = FALSE; | ||
987 | |||
988 | if (!res) | ||
989 | return -EINVAL; | ||
990 | |||
991 | if (bridge) { | ||
992 | /* The rules for bridges are different, 4K divisible for IO, 1M for (pf)mem*/ | ||
993 | if (res->type == IO) | ||
994 | tmp_divide = IOBRIDGE; | ||
995 | else | ||
996 | tmp_divide = MEMBRIDGE; | ||
997 | } else | ||
998 | tmp_divide = res->len; | ||
999 | |||
1000 | bus_cur = find_bus_wprev (res->busno, NULL, 0); | ||
1001 | |||
1002 | if (!bus_cur) { | ||
1003 | /* didn't find a bus, smth's wrong!!! */ | ||
1004 | debug ("no bus in the system, either pci_dev's wrong or allocation failed\n"); | ||
1005 | return -EINVAL; | ||
1006 | } | ||
1007 | |||
1008 | debug ("%s - enter\n", __FUNCTION__); | ||
1009 | debug ("bus_cur->busno is %d\n", bus_cur->busno); | ||
1010 | |||
1011 | /* This is a quick fix to not mess up with the code very much. i.e., | ||
1012 | * 2000-2fff, len = 1000, but when we compare, we need it to be fff */ | ||
1013 | res->len -= 1; | ||
1014 | |||
1015 | switch (res->type) { | ||
1016 | case IO: | ||
1017 | res_cur = bus_cur->firstIO; | ||
1018 | noranges = bus_cur->noIORanges; | ||
1019 | break; | ||
1020 | case MEM: | ||
1021 | res_cur = bus_cur->firstMem; | ||
1022 | noranges = bus_cur->noMemRanges; | ||
1023 | break; | ||
1024 | case PFMEM: | ||
1025 | res_cur = bus_cur->firstPFMem; | ||
1026 | noranges = bus_cur->noPFMemRanges; | ||
1027 | break; | ||
1028 | default: | ||
1029 | err ("wrong type of resource to check\n"); | ||
1030 | return -EINVAL; | ||
1031 | } | ||
1032 | res_prev = NULL; | ||
1033 | |||
1034 | while (res_cur) { | ||
1035 | range = find_range (bus_cur, res_cur); | ||
1036 | debug ("%s - rangeno = %d\n", __FUNCTION__, res_cur->rangeno); | ||
1037 | |||
1038 | if (!range) { | ||
1039 | err ("no range for the device exists... bailing out...\n"); | ||
1040 | return -EINVAL; | ||
1041 | } | ||
1042 | |||
1043 | /* found our range */ | ||
1044 | if (!res_prev) { | ||
1045 | /* first time in the loop */ | ||
1046 | if ((res_cur->start != range->start) && ((len_tmp = res_cur->start - 1 - range->start) >= res->len)) { | ||
1047 | debug ("len_tmp = %x\n", len_tmp); | ||
1048 | |||
1049 | if ((len_tmp < len_cur) || (len_cur == 0)) { | ||
1050 | |||
1051 | if ((range->start % tmp_divide) == 0) { | ||
1052 | /* just perfect, starting address is divisible by length */ | ||
1053 | flag = TRUE; | ||
1054 | len_cur = len_tmp; | ||
1055 | start_cur = range->start; | ||
1056 | } else { | ||
1057 | /* Needs adjusting */ | ||
1058 | tmp_start = range->start; | ||
1059 | flag = FALSE; | ||
1060 | |||
1061 | while ((len_tmp = res_cur->start - 1 - tmp_start) >= res->len) { | ||
1062 | if ((tmp_start % tmp_divide) == 0) { | ||
1063 | flag = TRUE; | ||
1064 | len_cur = len_tmp; | ||
1065 | start_cur = tmp_start; | ||
1066 | break; | ||
1067 | } | ||
1068 | tmp_start += tmp_divide - tmp_start % tmp_divide; | ||
1069 | if (tmp_start >= res_cur->start - 1) | ||
1070 | break; | ||
1071 | } | ||
1072 | } | ||
1073 | |||
1074 | if (flag && len_cur == res->len) { | ||
1075 | debug ("but we are not here, right?\n"); | ||
1076 | res->start = start_cur; | ||
1077 | res->len += 1; /* To restore the balance */ | ||
1078 | res->end = res->start + res->len - 1; | ||
1079 | return 0; | ||
1080 | } | ||
1081 | } | ||
1082 | } | ||
1083 | } | ||
1084 | if (!res_cur->next) { | ||
1085 | /* last device on the range */ | ||
1086 | if ((range->end != res_cur->end) && ((len_tmp = range->end - (res_cur->end + 1)) >= res->len)) { | ||
1087 | debug ("len_tmp = %x\n", len_tmp); | ||
1088 | if ((len_tmp < len_cur) || (len_cur == 0)) { | ||
1089 | |||
1090 | if (((res_cur->end + 1) % tmp_divide) == 0) { | ||
1091 | /* just perfect, starting address is divisible by length */ | ||
1092 | flag = TRUE; | ||
1093 | len_cur = len_tmp; | ||
1094 | start_cur = res_cur->end + 1; | ||
1095 | } else { | ||
1096 | /* Needs adjusting */ | ||
1097 | tmp_start = res_cur->end + 1; | ||
1098 | flag = FALSE; | ||
1099 | |||
1100 | while ((len_tmp = range->end - tmp_start) >= res->len) { | ||
1101 | if ((tmp_start % tmp_divide) == 0) { | ||
1102 | flag = TRUE; | ||
1103 | len_cur = len_tmp; | ||
1104 | start_cur = tmp_start; | ||
1105 | break; | ||
1106 | } | ||
1107 | tmp_start += tmp_divide - tmp_start % tmp_divide; | ||
1108 | if (tmp_start >= range->end) | ||
1109 | break; | ||
1110 | } | ||
1111 | } | ||
1112 | if (flag && len_cur == res->len) { | ||
1113 | res->start = start_cur; | ||
1114 | res->len += 1; /* To restore the balance */ | ||
1115 | res->end = res->start + res->len - 1; | ||
1116 | return 0; | ||
1117 | } | ||
1118 | } | ||
1119 | } | ||
1120 | } | ||
1121 | |||
1122 | if (res_prev) { | ||
1123 | if (res_prev->rangeno != res_cur->rangeno) { | ||
1124 | /* 1st device on this range */ | ||
1125 | if ((res_cur->start != range->start) && | ||
1126 | ((len_tmp = res_cur->start - 1 - range->start) >= res->len)) { | ||
1127 | if ((len_tmp < len_cur) || (len_cur == 0)) { | ||
1128 | if ((range->start % tmp_divide) == 0) { | ||
1129 | /* just perfect, starting address is divisible by length */ | ||
1130 | flag = TRUE; | ||
1131 | len_cur = len_tmp; | ||
1132 | start_cur = range->start; | ||
1133 | } else { | ||
1134 | /* Needs adjusting */ | ||
1135 | tmp_start = range->start; | ||
1136 | flag = FALSE; | ||
1137 | |||
1138 | while ((len_tmp = res_cur->start - 1 - tmp_start) >= res->len) { | ||
1139 | if ((tmp_start % tmp_divide) == 0) { | ||
1140 | flag = TRUE; | ||
1141 | len_cur = len_tmp; | ||
1142 | start_cur = tmp_start; | ||
1143 | break; | ||
1144 | } | ||
1145 | tmp_start += tmp_divide - tmp_start % tmp_divide; | ||
1146 | if (tmp_start >= res_cur->start - 1) | ||
1147 | break; | ||
1148 | } | ||
1149 | } | ||
1150 | |||
1151 | if (flag && len_cur == res->len) { | ||
1152 | res->start = start_cur; | ||
1153 | res->len += 1; /* To restore the balance */ | ||
1154 | res->end = res->start + res->len - 1; | ||
1155 | return 0; | ||
1156 | } | ||
1157 | } | ||
1158 | } | ||
1159 | } else { | ||
1160 | /* in the same range */ | ||
1161 | if ((len_tmp = res_cur->start - 1 - res_prev->end - 1) >= res->len) { | ||
1162 | if ((len_tmp < len_cur) || (len_cur == 0)) { | ||
1163 | if (((res_prev->end + 1) % tmp_divide) == 0) { | ||
1164 | /* just perfect, starting address's divisible by length */ | ||
1165 | flag = TRUE; | ||
1166 | len_cur = len_tmp; | ||
1167 | start_cur = res_prev->end + 1; | ||
1168 | } else { | ||
1169 | /* Needs adjusting */ | ||
1170 | tmp_start = res_prev->end + 1; | ||
1171 | flag = FALSE; | ||
1172 | |||
1173 | while ((len_tmp = res_cur->start - 1 - tmp_start) >= res->len) { | ||
1174 | if ((tmp_start % tmp_divide) == 0) { | ||
1175 | flag = TRUE; | ||
1176 | len_cur = len_tmp; | ||
1177 | start_cur = tmp_start; | ||
1178 | break; | ||
1179 | } | ||
1180 | tmp_start += tmp_divide - tmp_start % tmp_divide; | ||
1181 | if (tmp_start >= res_cur->start - 1) | ||
1182 | break; | ||
1183 | } | ||
1184 | } | ||
1185 | |||
1186 | if (flag && len_cur == res->len) { | ||
1187 | res->start = start_cur; | ||
1188 | res->len += 1; /* To restore the balance */ | ||
1189 | res->end = res->start + res->len - 1; | ||
1190 | return 0; | ||
1191 | } | ||
1192 | } | ||
1193 | } | ||
1194 | } | ||
1195 | } | ||
1196 | /* end if (res_prev) */ | ||
1197 | res_prev = res_cur; | ||
1198 | if (res_cur->next) | ||
1199 | res_cur = res_cur->next; | ||
1200 | else | ||
1201 | res_cur = res_cur->nextRange; | ||
1202 | } /* end of while */ | ||
1203 | |||
1204 | |||
1205 | if (!res_prev) { | ||
1206 | /* 1st device ever */ | ||
1207 | /* need to find appropriate range */ | ||
1208 | switch (res->type) { | ||
1209 | case IO: | ||
1210 | range = bus_cur->rangeIO; | ||
1211 | break; | ||
1212 | case MEM: | ||
1213 | range = bus_cur->rangeMem; | ||
1214 | break; | ||
1215 | case PFMEM: | ||
1216 | range = bus_cur->rangePFMem; | ||
1217 | break; | ||
1218 | } | ||
1219 | while (range) { | ||
1220 | if ((len_tmp = range->end - range->start) >= res->len) { | ||
1221 | if ((len_tmp < len_cur) || (len_cur == 0)) { | ||
1222 | if ((range->start % tmp_divide) == 0) { | ||
1223 | /* just perfect, starting address's divisible by length */ | ||
1224 | flag = TRUE; | ||
1225 | len_cur = len_tmp; | ||
1226 | start_cur = range->start; | ||
1227 | } else { | ||
1228 | /* Needs adjusting */ | ||
1229 | tmp_start = range->start; | ||
1230 | flag = FALSE; | ||
1231 | |||
1232 | while ((len_tmp = range->end - tmp_start) >= res->len) { | ||
1233 | if ((tmp_start % tmp_divide) == 0) { | ||
1234 | flag = TRUE; | ||
1235 | len_cur = len_tmp; | ||
1236 | start_cur = tmp_start; | ||
1237 | break; | ||
1238 | } | ||
1239 | tmp_start += tmp_divide - tmp_start % tmp_divide; | ||
1240 | if (tmp_start >= range->end) | ||
1241 | break; | ||
1242 | } | ||
1243 | } | ||
1244 | |||
1245 | if (flag && len_cur == res->len) { | ||
1246 | res->start = start_cur; | ||
1247 | res->len += 1; /* To restore the balance */ | ||
1248 | res->end = res->start + res->len - 1; | ||
1249 | return 0; | ||
1250 | } | ||
1251 | } | ||
1252 | } | ||
1253 | range = range->next; | ||
1254 | } /* end of while */ | ||
1255 | |||
1256 | if ((!range) && (len_cur == 0)) { | ||
1257 | /* have gone through the list of devices and ranges and haven't found n.e.thing */ | ||
1258 | err ("no appropriate range.. bailing out...\n"); | ||
1259 | return -EINVAL; | ||
1260 | } else if (len_cur) { | ||
1261 | res->start = start_cur; | ||
1262 | res->len += 1; /* To restore the balance */ | ||
1263 | res->end = res->start + res->len - 1; | ||
1264 | return 0; | ||
1265 | } | ||
1266 | } | ||
1267 | |||
1268 | if (!res_cur) { | ||
1269 | debug ("prev->rangeno = %d, noranges = %d\n", res_prev->rangeno, noranges); | ||
1270 | if (res_prev->rangeno < noranges) { | ||
1271 | /* if there're more ranges out there to check */ | ||
1272 | switch (res->type) { | ||
1273 | case IO: | ||
1274 | range = bus_cur->rangeIO; | ||
1275 | break; | ||
1276 | case MEM: | ||
1277 | range = bus_cur->rangeMem; | ||
1278 | break; | ||
1279 | case PFMEM: | ||
1280 | range = bus_cur->rangePFMem; | ||
1281 | break; | ||
1282 | } | ||
1283 | while (range) { | ||
1284 | if ((len_tmp = range->end - range->start) >= res->len) { | ||
1285 | if ((len_tmp < len_cur) || (len_cur == 0)) { | ||
1286 | if ((range->start % tmp_divide) == 0) { | ||
1287 | /* just perfect, starting address's divisible by length */ | ||
1288 | flag = TRUE; | ||
1289 | len_cur = len_tmp; | ||
1290 | start_cur = range->start; | ||
1291 | } else { | ||
1292 | /* Needs adjusting */ | ||
1293 | tmp_start = range->start; | ||
1294 | flag = FALSE; | ||
1295 | |||
1296 | while ((len_tmp = range->end - tmp_start) >= res->len) { | ||
1297 | if ((tmp_start % tmp_divide) == 0) { | ||
1298 | flag = TRUE; | ||
1299 | len_cur = len_tmp; | ||
1300 | start_cur = tmp_start; | ||
1301 | break; | ||
1302 | } | ||
1303 | tmp_start += tmp_divide - tmp_start % tmp_divide; | ||
1304 | if (tmp_start >= range->end) | ||
1305 | break; | ||
1306 | } | ||
1307 | } | ||
1308 | |||
1309 | if (flag && len_cur == res->len) { | ||
1310 | res->start = start_cur; | ||
1311 | res->len += 1; /* To restore the balance */ | ||
1312 | res->end = res->start + res->len - 1; | ||
1313 | return 0; | ||
1314 | } | ||
1315 | } | ||
1316 | } | ||
1317 | range = range->next; | ||
1318 | } /* end of while */ | ||
1319 | |||
1320 | if ((!range) && (len_cur == 0)) { | ||
1321 | /* have gone through the list of devices and ranges and haven't found n.e.thing */ | ||
1322 | err ("no appropriate range.. bailing out...\n"); | ||
1323 | return -EINVAL; | ||
1324 | } else if (len_cur) { | ||
1325 | res->start = start_cur; | ||
1326 | res->len += 1; /* To restore the balance */ | ||
1327 | res->end = res->start + res->len - 1; | ||
1328 | return 0; | ||
1329 | } | ||
1330 | } else { | ||
1331 | /* no more ranges to check on */ | ||
1332 | if (len_cur) { | ||
1333 | res->start = start_cur; | ||
1334 | res->len += 1; /* To restore the balance */ | ||
1335 | res->end = res->start + res->len - 1; | ||
1336 | return 0; | ||
1337 | } else { | ||
1338 | /* have gone through the list of devices and haven't found n.e.thing */ | ||
1339 | err ("no appropriate range.. bailing out...\n"); | ||
1340 | return -EINVAL; | ||
1341 | } | ||
1342 | } | ||
1343 | } /* end if(!res_cur) */ | ||
1344 | return -EINVAL; | ||
1345 | } | ||
1346 | |||
1347 | /******************************************************************************** | ||
1348 | * This routine is called from remove_card if the card contained PPB. | ||
1349 | * It will remove all the resources on the bus as well as the bus itself | ||
1350 | * Input: Bus | ||
1351 | * Ouput: 0, -ENODEV | ||
1352 | ********************************************************************************/ | ||
1353 | int ibmphp_remove_bus (struct bus_node *bus, u8 parent_busno) | ||
1354 | { | ||
1355 | struct resource_node *res_cur; | ||
1356 | struct resource_node *res_tmp; | ||
1357 | struct bus_node *prev_bus; | ||
1358 | int rc; | ||
1359 | |||
1360 | prev_bus = find_bus_wprev (parent_busno, NULL, 0); | ||
1361 | |||
1362 | if (!prev_bus) { | ||
1363 | debug ("something terribly wrong. Cannot find parent bus to the one to remove\n"); | ||
1364 | return -ENODEV; | ||
1365 | } | ||
1366 | |||
1367 | debug ("In ibmphp_remove_bus... prev_bus->busno is %x\n", prev_bus->busno); | ||
1368 | |||
1369 | rc = remove_ranges (bus, prev_bus); | ||
1370 | if (rc) | ||
1371 | return rc; | ||
1372 | |||
1373 | if (bus->firstIO) { | ||
1374 | res_cur = bus->firstIO; | ||
1375 | while (res_cur) { | ||
1376 | res_tmp = res_cur; | ||
1377 | if (res_cur->next) | ||
1378 | res_cur = res_cur->next; | ||
1379 | else | ||
1380 | res_cur = res_cur->nextRange; | ||
1381 | kfree (res_tmp); | ||
1382 | res_tmp = NULL; | ||
1383 | } | ||
1384 | bus->firstIO = NULL; | ||
1385 | } | ||
1386 | if (bus->firstMem) { | ||
1387 | res_cur = bus->firstMem; | ||
1388 | while (res_cur) { | ||
1389 | res_tmp = res_cur; | ||
1390 | if (res_cur->next) | ||
1391 | res_cur = res_cur->next; | ||
1392 | else | ||
1393 | res_cur = res_cur->nextRange; | ||
1394 | kfree (res_tmp); | ||
1395 | res_tmp = NULL; | ||
1396 | } | ||
1397 | bus->firstMem = NULL; | ||
1398 | } | ||
1399 | if (bus->firstPFMem) { | ||
1400 | res_cur = bus->firstPFMem; | ||
1401 | while (res_cur) { | ||
1402 | res_tmp = res_cur; | ||
1403 | if (res_cur->next) | ||
1404 | res_cur = res_cur->next; | ||
1405 | else | ||
1406 | res_cur = res_cur->nextRange; | ||
1407 | kfree (res_tmp); | ||
1408 | res_tmp = NULL; | ||
1409 | } | ||
1410 | bus->firstPFMem = NULL; | ||
1411 | } | ||
1412 | |||
1413 | if (bus->firstPFMemFromMem) { | ||
1414 | res_cur = bus->firstPFMemFromMem; | ||
1415 | while (res_cur) { | ||
1416 | res_tmp = res_cur; | ||
1417 | res_cur = res_cur->next; | ||
1418 | |||
1419 | kfree (res_tmp); | ||
1420 | res_tmp = NULL; | ||
1421 | } | ||
1422 | bus->firstPFMemFromMem = NULL; | ||
1423 | } | ||
1424 | |||
1425 | list_del (&bus->bus_list); | ||
1426 | kfree (bus); | ||
1427 | return 0; | ||
1428 | } | ||
1429 | |||
1430 | /****************************************************************************** | ||
1431 | * This routine deletes the ranges from a given bus, and the entries from the | ||
1432 | * parent's bus in the resources | ||
1433 | * Input: current bus, previous bus | ||
1434 | * Output: 0, -EINVAL | ||
1435 | ******************************************************************************/ | ||
1436 | static int remove_ranges (struct bus_node *bus_cur, struct bus_node *bus_prev) | ||
1437 | { | ||
1438 | struct range_node *range_cur; | ||
1439 | struct range_node *range_tmp; | ||
1440 | int i; | ||
1441 | struct resource_node *res = NULL; | ||
1442 | |||
1443 | if (bus_cur->noIORanges) { | ||
1444 | range_cur = bus_cur->rangeIO; | ||
1445 | for (i = 0; i < bus_cur->noIORanges; i++) { | ||
1446 | if (ibmphp_find_resource (bus_prev, range_cur->start, &res, IO) < 0) | ||
1447 | return -EINVAL; | ||
1448 | ibmphp_remove_resource (res); | ||
1449 | |||
1450 | range_tmp = range_cur; | ||
1451 | range_cur = range_cur->next; | ||
1452 | kfree (range_tmp); | ||
1453 | range_tmp = NULL; | ||
1454 | } | ||
1455 | bus_cur->rangeIO = NULL; | ||
1456 | } | ||
1457 | if (bus_cur->noMemRanges) { | ||
1458 | range_cur = bus_cur->rangeMem; | ||
1459 | for (i = 0; i < bus_cur->noMemRanges; i++) { | ||
1460 | if (ibmphp_find_resource (bus_prev, range_cur->start, &res, MEM) < 0) | ||
1461 | return -EINVAL; | ||
1462 | |||
1463 | ibmphp_remove_resource (res); | ||
1464 | range_tmp = range_cur; | ||
1465 | range_cur = range_cur->next; | ||
1466 | kfree (range_tmp); | ||
1467 | range_tmp = NULL; | ||
1468 | } | ||
1469 | bus_cur->rangeMem = NULL; | ||
1470 | } | ||
1471 | if (bus_cur->noPFMemRanges) { | ||
1472 | range_cur = bus_cur->rangePFMem; | ||
1473 | for (i = 0; i < bus_cur->noPFMemRanges; i++) { | ||
1474 | if (ibmphp_find_resource (bus_prev, range_cur->start, &res, PFMEM) < 0) | ||
1475 | return -EINVAL; | ||
1476 | |||
1477 | ibmphp_remove_resource (res); | ||
1478 | range_tmp = range_cur; | ||
1479 | range_cur = range_cur->next; | ||
1480 | kfree (range_tmp); | ||
1481 | range_tmp = NULL; | ||
1482 | } | ||
1483 | bus_cur->rangePFMem = NULL; | ||
1484 | } | ||
1485 | return 0; | ||
1486 | } | ||
1487 | |||
1488 | /* | ||
1489 | * find the resource node in the bus | ||
1490 | * Input: Resource needed, start address of the resource, type of resource | ||
1491 | */ | ||
1492 | int ibmphp_find_resource (struct bus_node *bus, u32 start_address, struct resource_node **res, int flag) | ||
1493 | { | ||
1494 | struct resource_node *res_cur = NULL; | ||
1495 | char * type = ""; | ||
1496 | |||
1497 | if (!bus) { | ||
1498 | err ("The bus passed in NULL to find resource\n"); | ||
1499 | return -ENODEV; | ||
1500 | } | ||
1501 | |||
1502 | switch (flag) { | ||
1503 | case IO: | ||
1504 | res_cur = bus->firstIO; | ||
1505 | type = "io"; | ||
1506 | break; | ||
1507 | case MEM: | ||
1508 | res_cur = bus->firstMem; | ||
1509 | type = "mem"; | ||
1510 | break; | ||
1511 | case PFMEM: | ||
1512 | res_cur = bus->firstPFMem; | ||
1513 | type = "pfmem"; | ||
1514 | break; | ||
1515 | default: | ||
1516 | err ("wrong type of flag\n"); | ||
1517 | return -EINVAL; | ||
1518 | } | ||
1519 | |||
1520 | while (res_cur) { | ||
1521 | if (res_cur->start == start_address) { | ||
1522 | *res = res_cur; | ||
1523 | break; | ||
1524 | } | ||
1525 | if (res_cur->next) | ||
1526 | res_cur = res_cur->next; | ||
1527 | else | ||
1528 | res_cur = res_cur->nextRange; | ||
1529 | } | ||
1530 | |||
1531 | if (!res_cur) { | ||
1532 | if (flag == PFMEM) { | ||
1533 | res_cur = bus->firstPFMemFromMem; | ||
1534 | while (res_cur) { | ||
1535 | if (res_cur->start == start_address) { | ||
1536 | *res = res_cur; | ||
1537 | break; | ||
1538 | } | ||
1539 | res_cur = res_cur->next; | ||
1540 | } | ||
1541 | if (!res_cur) { | ||
1542 | debug ("SOS...cannot find %s resource in the bus.\n", type); | ||
1543 | return -EINVAL; | ||
1544 | } | ||
1545 | } else { | ||
1546 | debug ("SOS... cannot find %s resource in the bus.\n", type); | ||
1547 | return -EINVAL; | ||
1548 | } | ||
1549 | } | ||
1550 | |||
1551 | if (*res) | ||
1552 | debug ("*res->start = %x\n", (*res)->start); | ||
1553 | |||
1554 | return 0; | ||
1555 | } | ||
1556 | |||
1557 | /*********************************************************************** | ||
1558 | * This routine will free the resource structures used by the | ||
1559 | * system. It is called from cleanup routine for the module | ||
1560 | * Parameters: none | ||
1561 | * Returns: none | ||
1562 | ***********************************************************************/ | ||
1563 | void ibmphp_free_resources (void) | ||
1564 | { | ||
1565 | struct bus_node *bus_cur = NULL; | ||
1566 | struct bus_node *bus_tmp; | ||
1567 | struct range_node *range_cur; | ||
1568 | struct range_node *range_tmp; | ||
1569 | struct resource_node *res_cur; | ||
1570 | struct resource_node *res_tmp; | ||
1571 | struct list_head *tmp; | ||
1572 | struct list_head *next; | ||
1573 | int i = 0; | ||
1574 | flags = 1; | ||
1575 | |||
1576 | list_for_each_safe (tmp, next, &gbuses) { | ||
1577 | bus_cur = list_entry (tmp, struct bus_node, bus_list); | ||
1578 | if (bus_cur->noIORanges) { | ||
1579 | range_cur = bus_cur->rangeIO; | ||
1580 | for (i = 0; i < bus_cur->noIORanges; i++) { | ||
1581 | if (!range_cur) | ||
1582 | break; | ||
1583 | range_tmp = range_cur; | ||
1584 | range_cur = range_cur->next; | ||
1585 | kfree (range_tmp); | ||
1586 | range_tmp = NULL; | ||
1587 | } | ||
1588 | } | ||
1589 | if (bus_cur->noMemRanges) { | ||
1590 | range_cur = bus_cur->rangeMem; | ||
1591 | for (i = 0; i < bus_cur->noMemRanges; i++) { | ||
1592 | if (!range_cur) | ||
1593 | break; | ||
1594 | range_tmp = range_cur; | ||
1595 | range_cur = range_cur->next; | ||
1596 | kfree (range_tmp); | ||
1597 | range_tmp = NULL; | ||
1598 | } | ||
1599 | } | ||
1600 | if (bus_cur->noPFMemRanges) { | ||
1601 | range_cur = bus_cur->rangePFMem; | ||
1602 | for (i = 0; i < bus_cur->noPFMemRanges; i++) { | ||
1603 | if (!range_cur) | ||
1604 | break; | ||
1605 | range_tmp = range_cur; | ||
1606 | range_cur = range_cur->next; | ||
1607 | kfree (range_tmp); | ||
1608 | range_tmp = NULL; | ||
1609 | } | ||
1610 | } | ||
1611 | |||
1612 | if (bus_cur->firstIO) { | ||
1613 | res_cur = bus_cur->firstIO; | ||
1614 | while (res_cur) { | ||
1615 | res_tmp = res_cur; | ||
1616 | if (res_cur->next) | ||
1617 | res_cur = res_cur->next; | ||
1618 | else | ||
1619 | res_cur = res_cur->nextRange; | ||
1620 | kfree (res_tmp); | ||
1621 | res_tmp = NULL; | ||
1622 | } | ||
1623 | bus_cur->firstIO = NULL; | ||
1624 | } | ||
1625 | if (bus_cur->firstMem) { | ||
1626 | res_cur = bus_cur->firstMem; | ||
1627 | while (res_cur) { | ||
1628 | res_tmp = res_cur; | ||
1629 | if (res_cur->next) | ||
1630 | res_cur = res_cur->next; | ||
1631 | else | ||
1632 | res_cur = res_cur->nextRange; | ||
1633 | kfree (res_tmp); | ||
1634 | res_tmp = NULL; | ||
1635 | } | ||
1636 | bus_cur->firstMem = NULL; | ||
1637 | } | ||
1638 | if (bus_cur->firstPFMem) { | ||
1639 | res_cur = bus_cur->firstPFMem; | ||
1640 | while (res_cur) { | ||
1641 | res_tmp = res_cur; | ||
1642 | if (res_cur->next) | ||
1643 | res_cur = res_cur->next; | ||
1644 | else | ||
1645 | res_cur = res_cur->nextRange; | ||
1646 | kfree (res_tmp); | ||
1647 | res_tmp = NULL; | ||
1648 | } | ||
1649 | bus_cur->firstPFMem = NULL; | ||
1650 | } | ||
1651 | |||
1652 | if (bus_cur->firstPFMemFromMem) { | ||
1653 | res_cur = bus_cur->firstPFMemFromMem; | ||
1654 | while (res_cur) { | ||
1655 | res_tmp = res_cur; | ||
1656 | res_cur = res_cur->next; | ||
1657 | |||
1658 | kfree (res_tmp); | ||
1659 | res_tmp = NULL; | ||
1660 | } | ||
1661 | bus_cur->firstPFMemFromMem = NULL; | ||
1662 | } | ||
1663 | |||
1664 | bus_tmp = bus_cur; | ||
1665 | list_del (&bus_cur->bus_list); | ||
1666 | kfree (bus_tmp); | ||
1667 | bus_tmp = NULL; | ||
1668 | } | ||
1669 | } | ||
1670 | |||
1671 | /********************************************************************************* | ||
1672 | * This function will go over the PFmem resources to check if the EBDA allocated | ||
1673 | * pfmem out of memory buckets of the bus. If so, it will change the range numbers | ||
1674 | * and a flag to indicate that this resource is out of memory. It will also move the | ||
1675 | * Pfmem out of the pfmem resource list to the PFMemFromMem list, and will create | ||
1676 | * a new Mem node | ||
1677 | * This routine is called right after initialization | ||
1678 | *******************************************************************************/ | ||
1679 | static int __init once_over (void) | ||
1680 | { | ||
1681 | struct resource_node *pfmem_cur; | ||
1682 | struct resource_node *pfmem_prev; | ||
1683 | struct resource_node *mem; | ||
1684 | struct bus_node *bus_cur; | ||
1685 | struct list_head *tmp; | ||
1686 | |||
1687 | list_for_each (tmp, &gbuses) { | ||
1688 | bus_cur = list_entry (tmp, struct bus_node, bus_list); | ||
1689 | if ((!bus_cur->rangePFMem) && (bus_cur->firstPFMem)) { | ||
1690 | for (pfmem_cur = bus_cur->firstPFMem, pfmem_prev = NULL; pfmem_cur; pfmem_prev = pfmem_cur, pfmem_cur = pfmem_cur->next) { | ||
1691 | pfmem_cur->fromMem = TRUE; | ||
1692 | if (pfmem_prev) | ||
1693 | pfmem_prev->next = pfmem_cur->next; | ||
1694 | else | ||
1695 | bus_cur->firstPFMem = pfmem_cur->next; | ||
1696 | |||
1697 | if (!bus_cur->firstPFMemFromMem) | ||
1698 | pfmem_cur->next = NULL; | ||
1699 | else | ||
1700 | /* we don't need to sort PFMemFromMem since we're using mem node for | ||
1701 | all the real work anyways, so just insert at the beginning of the | ||
1702 | list | ||
1703 | */ | ||
1704 | pfmem_cur->next = bus_cur->firstPFMemFromMem; | ||
1705 | |||
1706 | bus_cur->firstPFMemFromMem = pfmem_cur; | ||
1707 | |||
1708 | mem = kmalloc (sizeof (struct resource_node), GFP_KERNEL); | ||
1709 | if (!mem) { | ||
1710 | err ("out of system memory\n"); | ||
1711 | return -ENOMEM; | ||
1712 | } | ||
1713 | memset (mem, 0, sizeof (struct resource_node)); | ||
1714 | mem->type = MEM; | ||
1715 | mem->busno = pfmem_cur->busno; | ||
1716 | mem->devfunc = pfmem_cur->devfunc; | ||
1717 | mem->start = pfmem_cur->start; | ||
1718 | mem->end = pfmem_cur->end; | ||
1719 | mem->len = pfmem_cur->len; | ||
1720 | if (ibmphp_add_resource (mem) < 0) | ||
1721 | err ("Trouble...trouble... EBDA allocated pfmem from mem, but system doesn't display it has this space... unless not PCI device...\n"); | ||
1722 | pfmem_cur->rangeno = mem->rangeno; | ||
1723 | } /* end for pfmem */ | ||
1724 | } /* end if */ | ||
1725 | } /* end list_for_each bus */ | ||
1726 | return 0; | ||
1727 | } | ||
1728 | |||
1729 | int ibmphp_add_pfmem_from_mem (struct resource_node *pfmem) | ||
1730 | { | ||
1731 | struct bus_node *bus_cur = find_bus_wprev (pfmem->busno, NULL, 0); | ||
1732 | |||
1733 | if (!bus_cur) { | ||
1734 | err ("cannot find bus of pfmem to add...\n"); | ||
1735 | return -ENODEV; | ||
1736 | } | ||
1737 | |||
1738 | if (bus_cur->firstPFMemFromMem) | ||
1739 | pfmem->next = bus_cur->firstPFMemFromMem; | ||
1740 | else | ||
1741 | pfmem->next = NULL; | ||
1742 | |||
1743 | bus_cur->firstPFMemFromMem = pfmem; | ||
1744 | |||
1745 | return 0; | ||
1746 | } | ||
1747 | |||
1748 | /* This routine just goes through the buses to see if the bus already exists. | ||
1749 | * It is called from ibmphp_find_sec_number, to find out a secondary bus number for | ||
1750 | * bridged cards | ||
1751 | * Parameters: bus_number | ||
1752 | * Returns: Bus pointer or NULL | ||
1753 | */ | ||
1754 | struct bus_node *ibmphp_find_res_bus (u8 bus_number) | ||
1755 | { | ||
1756 | return find_bus_wprev (bus_number, NULL, 0); | ||
1757 | } | ||
1758 | |||
1759 | static struct bus_node *find_bus_wprev (u8 bus_number, struct bus_node **prev, u8 flag) | ||
1760 | { | ||
1761 | struct bus_node *bus_cur; | ||
1762 | struct list_head *tmp; | ||
1763 | struct list_head *tmp_prev; | ||
1764 | |||
1765 | list_for_each (tmp, &gbuses) { | ||
1766 | tmp_prev = tmp->prev; | ||
1767 | bus_cur = list_entry (tmp, struct bus_node, bus_list); | ||
1768 | if (flag) | ||
1769 | *prev = list_entry (tmp_prev, struct bus_node, bus_list); | ||
1770 | if (bus_cur->busno == bus_number) | ||
1771 | return bus_cur; | ||
1772 | } | ||
1773 | |||
1774 | return NULL; | ||
1775 | } | ||
1776 | |||
1777 | void ibmphp_print_test (void) | ||
1778 | { | ||
1779 | int i = 0; | ||
1780 | struct bus_node *bus_cur = NULL; | ||
1781 | struct range_node *range; | ||
1782 | struct resource_node *res; | ||
1783 | struct list_head *tmp; | ||
1784 | |||
1785 | debug_pci ("*****************START**********************\n"); | ||
1786 | |||
1787 | if ((!list_empty(&gbuses)) && flags) { | ||
1788 | err ("The GBUSES is not NULL?!?!?!?!?\n"); | ||
1789 | return; | ||
1790 | } | ||
1791 | |||
1792 | list_for_each (tmp, &gbuses) { | ||
1793 | bus_cur = list_entry (tmp, struct bus_node, bus_list); | ||
1794 | debug_pci ("This is bus # %d. There are\n", bus_cur->busno); | ||
1795 | debug_pci ("IORanges = %d\t", bus_cur->noIORanges); | ||
1796 | debug_pci ("MemRanges = %d\t", bus_cur->noMemRanges); | ||
1797 | debug_pci ("PFMemRanges = %d\n", bus_cur->noPFMemRanges); | ||
1798 | debug_pci ("The IO Ranges are as follows:\n"); | ||
1799 | if (bus_cur->rangeIO) { | ||
1800 | range = bus_cur->rangeIO; | ||
1801 | for (i = 0; i < bus_cur->noIORanges; i++) { | ||
1802 | debug_pci ("rangeno is %d\n", range->rangeno); | ||
1803 | debug_pci ("[%x - %x]\n", range->start, range->end); | ||
1804 | range = range->next; | ||
1805 | } | ||
1806 | } | ||
1807 | |||
1808 | debug_pci ("The Mem Ranges are as follows:\n"); | ||
1809 | if (bus_cur->rangeMem) { | ||
1810 | range = bus_cur->rangeMem; | ||
1811 | for (i = 0; i < bus_cur->noMemRanges; i++) { | ||
1812 | debug_pci ("rangeno is %d\n", range->rangeno); | ||
1813 | debug_pci ("[%x - %x]\n", range->start, range->end); | ||
1814 | range = range->next; | ||
1815 | } | ||
1816 | } | ||
1817 | |||
1818 | debug_pci ("The PFMem Ranges are as follows:\n"); | ||
1819 | |||
1820 | if (bus_cur->rangePFMem) { | ||
1821 | range = bus_cur->rangePFMem; | ||
1822 | for (i = 0; i < bus_cur->noPFMemRanges; i++) { | ||
1823 | debug_pci ("rangeno is %d\n", range->rangeno); | ||
1824 | debug_pci ("[%x - %x]\n", range->start, range->end); | ||
1825 | range = range->next; | ||
1826 | } | ||
1827 | } | ||
1828 | |||
1829 | debug_pci ("The resources on this bus are as follows\n"); | ||
1830 | |||
1831 | debug_pci ("IO...\n"); | ||
1832 | if (bus_cur->firstIO) { | ||
1833 | res = bus_cur->firstIO; | ||
1834 | while (res) { | ||
1835 | debug_pci ("The range # is %d\n", res->rangeno); | ||
1836 | debug_pci ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc); | ||
1837 | debug_pci ("[%x - %x], len=%x\n", res->start, res->end, res->len); | ||
1838 | if (res->next) | ||
1839 | res = res->next; | ||
1840 | else if (res->nextRange) | ||
1841 | res = res->nextRange; | ||
1842 | else | ||
1843 | break; | ||
1844 | } | ||
1845 | } | ||
1846 | debug_pci ("Mem...\n"); | ||
1847 | if (bus_cur->firstMem) { | ||
1848 | res = bus_cur->firstMem; | ||
1849 | while (res) { | ||
1850 | debug_pci ("The range # is %d\n", res->rangeno); | ||
1851 | debug_pci ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc); | ||
1852 | debug_pci ("[%x - %x], len=%x\n", res->start, res->end, res->len); | ||
1853 | if (res->next) | ||
1854 | res = res->next; | ||
1855 | else if (res->nextRange) | ||
1856 | res = res->nextRange; | ||
1857 | else | ||
1858 | break; | ||
1859 | } | ||
1860 | } | ||
1861 | debug_pci ("PFMem...\n"); | ||
1862 | if (bus_cur->firstPFMem) { | ||
1863 | res = bus_cur->firstPFMem; | ||
1864 | while (res) { | ||
1865 | debug_pci ("The range # is %d\n", res->rangeno); | ||
1866 | debug_pci ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc); | ||
1867 | debug_pci ("[%x - %x], len=%x\n", res->start, res->end, res->len); | ||
1868 | if (res->next) | ||
1869 | res = res->next; | ||
1870 | else if (res->nextRange) | ||
1871 | res = res->nextRange; | ||
1872 | else | ||
1873 | break; | ||
1874 | } | ||
1875 | } | ||
1876 | |||
1877 | debug_pci ("PFMemFromMem...\n"); | ||
1878 | if (bus_cur->firstPFMemFromMem) { | ||
1879 | res = bus_cur->firstPFMemFromMem; | ||
1880 | while (res) { | ||
1881 | debug_pci ("The range # is %d\n", res->rangeno); | ||
1882 | debug_pci ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc); | ||
1883 | debug_pci ("[%x - %x], len=%x\n", res->start, res->end, res->len); | ||
1884 | res = res->next; | ||
1885 | } | ||
1886 | } | ||
1887 | } | ||
1888 | debug_pci ("***********************END***********************\n"); | ||
1889 | } | ||
1890 | |||
1891 | static int range_exists_already (struct range_node * range, struct bus_node * bus_cur, u8 type) | ||
1892 | { | ||
1893 | struct range_node * range_cur = NULL; | ||
1894 | switch (type) { | ||
1895 | case IO: | ||
1896 | range_cur = bus_cur->rangeIO; | ||
1897 | break; | ||
1898 | case MEM: | ||
1899 | range_cur = bus_cur->rangeMem; | ||
1900 | break; | ||
1901 | case PFMEM: | ||
1902 | range_cur = bus_cur->rangePFMem; | ||
1903 | break; | ||
1904 | default: | ||
1905 | err ("wrong type passed to find out if range already exists\n"); | ||
1906 | return -ENODEV; | ||
1907 | } | ||
1908 | |||
1909 | while (range_cur) { | ||
1910 | if ((range_cur->start == range->start) && (range_cur->end == range->end)) | ||
1911 | return 1; | ||
1912 | range_cur = range_cur->next; | ||
1913 | } | ||
1914 | |||
1915 | return 0; | ||
1916 | } | ||
1917 | |||
1918 | /* This routine will read the windows for any PPB we have and update the | ||
1919 | * range info for the secondary bus, and will also input this info into | ||
1920 | * primary bus, since BIOS doesn't. This is for PPB that are in the system | ||
1921 | * on bootup. For bridged cards that were added during previous load of the | ||
1922 | * driver, only the ranges and the bus structure are added, the devices are | ||
1923 | * added from NVRAM | ||
1924 | * Input: primary busno | ||
1925 | * Returns: none | ||
1926 | * Note: this function doesn't take into account IO restrictions etc, | ||
1927 | * so will only work for bridges with no video/ISA devices behind them It | ||
1928 | * also will not work for onboard PPB's that can have more than 1 *bus | ||
1929 | * behind them All these are TO DO. | ||
1930 | * Also need to add more error checkings... (from fnc returns etc) | ||
1931 | */ | ||
1932 | static int __init update_bridge_ranges (struct bus_node **bus) | ||
1933 | { | ||
1934 | u8 sec_busno, device, function, hdr_type, start_io_address, end_io_address; | ||
1935 | u16 vendor_id, upper_io_start, upper_io_end, start_mem_address, end_mem_address; | ||
1936 | u32 start_address, end_address, upper_start, upper_end; | ||
1937 | struct bus_node *bus_sec; | ||
1938 | struct bus_node *bus_cur; | ||
1939 | struct resource_node *io; | ||
1940 | struct resource_node *mem; | ||
1941 | struct resource_node *pfmem; | ||
1942 | struct range_node *range; | ||
1943 | unsigned int devfn; | ||
1944 | |||
1945 | bus_cur = *bus; | ||
1946 | if (!bus_cur) | ||
1947 | return -ENODEV; | ||
1948 | ibmphp_pci_bus->number = bus_cur->busno; | ||
1949 | |||
1950 | debug ("inside %s\n", __FUNCTION__); | ||
1951 | debug ("bus_cur->busno = %x\n", bus_cur->busno); | ||
1952 | |||
1953 | for (device = 0; device < 32; device++) { | ||
1954 | for (function = 0x00; function < 0x08; function++) { | ||
1955 | devfn = PCI_DEVFN(device, function); | ||
1956 | pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_VENDOR_ID, &vendor_id); | ||
1957 | |||
1958 | if (vendor_id != PCI_VENDOR_ID_NOTVALID) { | ||
1959 | /* found correct device!!! */ | ||
1960 | pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_HEADER_TYPE, &hdr_type); | ||
1961 | |||
1962 | switch (hdr_type) { | ||
1963 | case PCI_HEADER_TYPE_NORMAL: | ||
1964 | function = 0x8; | ||
1965 | break; | ||
1966 | case PCI_HEADER_TYPE_MULTIDEVICE: | ||
1967 | break; | ||
1968 | case PCI_HEADER_TYPE_BRIDGE: | ||
1969 | function = 0x8; | ||
1970 | case PCI_HEADER_TYPE_MULTIBRIDGE: | ||
1971 | /* We assume here that only 1 bus behind the bridge | ||
1972 | TO DO: add functionality for several: | ||
1973 | temp = secondary; | ||
1974 | while (temp < subordinate) { | ||
1975 | ... | ||
1976 | temp++; | ||
1977 | } | ||
1978 | */ | ||
1979 | pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_busno); | ||
1980 | bus_sec = find_bus_wprev (sec_busno, NULL, 0); | ||
1981 | /* this bus structure doesn't exist yet, PPB was configured during previous loading of ibmphp */ | ||
1982 | if (!bus_sec) { | ||
1983 | bus_sec = alloc_error_bus (NULL, sec_busno, 1); | ||
1984 | /* the rest will be populated during NVRAM call */ | ||
1985 | return 0; | ||
1986 | } | ||
1987 | pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_IO_BASE, &start_io_address); | ||
1988 | pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_IO_LIMIT, &end_io_address); | ||
1989 | pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_IO_BASE_UPPER16, &upper_io_start); | ||
1990 | pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_IO_LIMIT_UPPER16, &upper_io_end); | ||
1991 | start_address = (start_io_address & PCI_IO_RANGE_MASK) << 8; | ||
1992 | start_address |= (upper_io_start << 16); | ||
1993 | end_address = (end_io_address & PCI_IO_RANGE_MASK) << 8; | ||
1994 | end_address |= (upper_io_end << 16); | ||
1995 | |||
1996 | if ((start_address) && (start_address <= end_address)) { | ||
1997 | range = kmalloc (sizeof (struct range_node), GFP_KERNEL); | ||
1998 | if (!range) { | ||
1999 | err ("out of system memory\n"); | ||
2000 | return -ENOMEM; | ||
2001 | } | ||
2002 | memset (range, 0, sizeof (struct range_node)); | ||
2003 | range->start = start_address; | ||
2004 | range->end = end_address + 0xfff; | ||
2005 | |||
2006 | if (bus_sec->noIORanges > 0) { | ||
2007 | if (!range_exists_already (range, bus_sec, IO)) { | ||
2008 | add_range (IO, range, bus_sec); | ||
2009 | ++bus_sec->noIORanges; | ||
2010 | } else { | ||
2011 | kfree (range); | ||
2012 | range = NULL; | ||
2013 | } | ||
2014 | } else { | ||
2015 | /* 1st IO Range on the bus */ | ||
2016 | range->rangeno = 1; | ||
2017 | bus_sec->rangeIO = range; | ||
2018 | ++bus_sec->noIORanges; | ||
2019 | } | ||
2020 | fix_resources (bus_sec); | ||
2021 | |||
2022 | if (ibmphp_find_resource (bus_cur, start_address, &io, IO)) { | ||
2023 | io = kmalloc (sizeof (struct resource_node), GFP_KERNEL); | ||
2024 | if (!io) { | ||
2025 | kfree (range); | ||
2026 | err ("out of system memory\n"); | ||
2027 | return -ENOMEM; | ||
2028 | } | ||
2029 | memset (io, 0, sizeof (struct resource_node)); | ||
2030 | io->type = IO; | ||
2031 | io->busno = bus_cur->busno; | ||
2032 | io->devfunc = ((device << 3) | (function & 0x7)); | ||
2033 | io->start = start_address; | ||
2034 | io->end = end_address + 0xfff; | ||
2035 | io->len = io->end - io->start + 1; | ||
2036 | ibmphp_add_resource (io); | ||
2037 | } | ||
2038 | } | ||
2039 | |||
2040 | pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_BASE, &start_mem_address); | ||
2041 | pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_LIMIT, &end_mem_address); | ||
2042 | |||
2043 | start_address = 0x00000000 | (start_mem_address & PCI_MEMORY_RANGE_MASK) << 16; | ||
2044 | end_address = 0x00000000 | (end_mem_address & PCI_MEMORY_RANGE_MASK) << 16; | ||
2045 | |||
2046 | if ((start_address) && (start_address <= end_address)) { | ||
2047 | |||
2048 | range = kmalloc (sizeof (struct range_node), GFP_KERNEL); | ||
2049 | if (!range) { | ||
2050 | err ("out of system memory\n"); | ||
2051 | return -ENOMEM; | ||
2052 | } | ||
2053 | memset (range, 0, sizeof (struct range_node)); | ||
2054 | range->start = start_address; | ||
2055 | range->end = end_address + 0xfffff; | ||
2056 | |||
2057 | if (bus_sec->noMemRanges > 0) { | ||
2058 | if (!range_exists_already (range, bus_sec, MEM)) { | ||
2059 | add_range (MEM, range, bus_sec); | ||
2060 | ++bus_sec->noMemRanges; | ||
2061 | } else { | ||
2062 | kfree (range); | ||
2063 | range = NULL; | ||
2064 | } | ||
2065 | } else { | ||
2066 | /* 1st Mem Range on the bus */ | ||
2067 | range->rangeno = 1; | ||
2068 | bus_sec->rangeMem = range; | ||
2069 | ++bus_sec->noMemRanges; | ||
2070 | } | ||
2071 | |||
2072 | fix_resources (bus_sec); | ||
2073 | |||
2074 | if (ibmphp_find_resource (bus_cur, start_address, &mem, MEM)) { | ||
2075 | mem = kmalloc (sizeof (struct resource_node), GFP_KERNEL); | ||
2076 | if (!mem) { | ||
2077 | kfree (range); | ||
2078 | err ("out of system memory\n"); | ||
2079 | return -ENOMEM; | ||
2080 | } | ||
2081 | memset (mem, 0, sizeof (struct resource_node)); | ||
2082 | mem->type = MEM; | ||
2083 | mem->busno = bus_cur->busno; | ||
2084 | mem->devfunc = ((device << 3) | (function & 0x7)); | ||
2085 | mem->start = start_address; | ||
2086 | mem->end = end_address + 0xfffff; | ||
2087 | mem->len = mem->end - mem->start + 1; | ||
2088 | ibmphp_add_resource (mem); | ||
2089 | } | ||
2090 | } | ||
2091 | pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_BASE, &start_mem_address); | ||
2092 | pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &end_mem_address); | ||
2093 | pci_bus_read_config_dword (ibmphp_pci_bus, devfn, PCI_PREF_BASE_UPPER32, &upper_start); | ||
2094 | pci_bus_read_config_dword (ibmphp_pci_bus, devfn, PCI_PREF_LIMIT_UPPER32, &upper_end); | ||
2095 | start_address = 0x00000000 | (start_mem_address & PCI_MEMORY_RANGE_MASK) << 16; | ||
2096 | end_address = 0x00000000 | (end_mem_address & PCI_MEMORY_RANGE_MASK) << 16; | ||
2097 | #if BITS_PER_LONG == 64 | ||
2098 | start_address |= ((long) upper_start) << 32; | ||
2099 | end_address |= ((long) upper_end) << 32; | ||
2100 | #endif | ||
2101 | |||
2102 | if ((start_address) && (start_address <= end_address)) { | ||
2103 | |||
2104 | range = kmalloc (sizeof (struct range_node), GFP_KERNEL); | ||
2105 | if (!range) { | ||
2106 | err ("out of system memory\n"); | ||
2107 | return -ENOMEM; | ||
2108 | } | ||
2109 | memset (range, 0, sizeof (struct range_node)); | ||
2110 | range->start = start_address; | ||
2111 | range->end = end_address + 0xfffff; | ||
2112 | |||
2113 | if (bus_sec->noPFMemRanges > 0) { | ||
2114 | if (!range_exists_already (range, bus_sec, PFMEM)) { | ||
2115 | add_range (PFMEM, range, bus_sec); | ||
2116 | ++bus_sec->noPFMemRanges; | ||
2117 | } else { | ||
2118 | kfree (range); | ||
2119 | range = NULL; | ||
2120 | } | ||
2121 | } else { | ||
2122 | /* 1st PFMem Range on the bus */ | ||
2123 | range->rangeno = 1; | ||
2124 | bus_sec->rangePFMem = range; | ||
2125 | ++bus_sec->noPFMemRanges; | ||
2126 | } | ||
2127 | |||
2128 | fix_resources (bus_sec); | ||
2129 | if (ibmphp_find_resource (bus_cur, start_address, &pfmem, PFMEM)) { | ||
2130 | pfmem = kmalloc (sizeof (struct resource_node), GFP_KERNEL); | ||
2131 | if (!pfmem) { | ||
2132 | kfree (range); | ||
2133 | err ("out of system memory\n"); | ||
2134 | return -ENOMEM; | ||
2135 | } | ||
2136 | memset (pfmem, 0, sizeof (struct resource_node)); | ||
2137 | pfmem->type = PFMEM; | ||
2138 | pfmem->busno = bus_cur->busno; | ||
2139 | pfmem->devfunc = ((device << 3) | (function & 0x7)); | ||
2140 | pfmem->start = start_address; | ||
2141 | pfmem->end = end_address + 0xfffff; | ||
2142 | pfmem->len = pfmem->end - pfmem->start + 1; | ||
2143 | pfmem->fromMem = FALSE; | ||
2144 | |||
2145 | ibmphp_add_resource (pfmem); | ||
2146 | } | ||
2147 | } | ||
2148 | break; | ||
2149 | } /* end of switch */ | ||
2150 | } /* end if vendor */ | ||
2151 | } /* end for function */ | ||
2152 | } /* end for device */ | ||
2153 | |||
2154 | bus = &bus_cur; | ||
2155 | return 0; | ||
2156 | } | ||
diff --git a/drivers/pci/hotplug/pci_hotplug.h b/drivers/pci/hotplug/pci_hotplug.h new file mode 100644 index 000000000000..57ace325168d --- /dev/null +++ b/drivers/pci/hotplug/pci_hotplug.h | |||
@@ -0,0 +1,180 @@ | |||
1 | /* | ||
2 | * PCI HotPlug Core Functions | ||
3 | * | ||
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | ||
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * Copyright (C) 2001 IBM Corp. | ||
7 | * | ||
8 | * All rights reserved. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or (at | ||
13 | * your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
18 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
19 | * details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
24 | * | ||
25 | * Send feedback to <greg@kroah.com> | ||
26 | * | ||
27 | */ | ||
28 | #ifndef _PCI_HOTPLUG_H | ||
29 | #define _PCI_HOTPLUG_H | ||
30 | |||
31 | |||
32 | /* These values come from the PCI Hotplug Spec */ | ||
33 | enum pci_bus_speed { | ||
34 | PCI_SPEED_33MHz = 0x00, | ||
35 | PCI_SPEED_66MHz = 0x01, | ||
36 | PCI_SPEED_66MHz_PCIX = 0x02, | ||
37 | PCI_SPEED_100MHz_PCIX = 0x03, | ||
38 | PCI_SPEED_133MHz_PCIX = 0x04, | ||
39 | PCI_SPEED_66MHz_PCIX_ECC = 0x05, | ||
40 | PCI_SPEED_100MHz_PCIX_ECC = 0x06, | ||
41 | PCI_SPEED_133MHz_PCIX_ECC = 0x07, | ||
42 | PCI_SPEED_66MHz_PCIX_266 = 0x09, | ||
43 | PCI_SPEED_100MHz_PCIX_266 = 0x0a, | ||
44 | PCI_SPEED_133MHz_PCIX_266 = 0x0b, | ||
45 | PCI_SPEED_66MHz_PCIX_533 = 0x11, | ||
46 | PCI_SPEED_100MHz_PCIX_533 = 0x12, | ||
47 | PCI_SPEED_133MHz_PCIX_533 = 0x13, | ||
48 | PCI_SPEED_UNKNOWN = 0xff, | ||
49 | }; | ||
50 | |||
51 | /* These values come from the PCI Express Spec */ | ||
52 | enum pcie_link_width { | ||
53 | PCIE_LNK_WIDTH_RESRV = 0x00, | ||
54 | PCIE_LNK_X1 = 0x01, | ||
55 | PCIE_LNK_X2 = 0x02, | ||
56 | PCIE_LNK_X4 = 0x04, | ||
57 | PCIE_LNK_X8 = 0x08, | ||
58 | PCIE_LNK_X12 = 0x0C, | ||
59 | PCIE_LNK_X16 = 0x10, | ||
60 | PCIE_LNK_X32 = 0x20, | ||
61 | PCIE_LNK_WIDTH_UNKNOWN = 0xFF, | ||
62 | }; | ||
63 | |||
64 | enum pcie_link_speed { | ||
65 | PCIE_2PT5GB = 0x14, | ||
66 | PCIE_LNK_SPEED_UNKNOWN = 0xFF, | ||
67 | }; | ||
68 | |||
69 | struct hotplug_slot; | ||
70 | struct hotplug_slot_attribute { | ||
71 | struct attribute attr; | ||
72 | ssize_t (*show)(struct hotplug_slot *, char *); | ||
73 | ssize_t (*store)(struct hotplug_slot *, const char *, size_t); | ||
74 | }; | ||
75 | #define to_hotplug_attr(n) container_of(n, struct hotplug_slot_attribute, attr); | ||
76 | |||
77 | /** | ||
78 | * struct hotplug_slot_ops -the callbacks that the hotplug pci core can use | ||
79 | * @owner: The module owner of this structure | ||
80 | * @enable_slot: Called when the user wants to enable a specific pci slot | ||
81 | * @disable_slot: Called when the user wants to disable a specific pci slot | ||
82 | * @set_attention_status: Called to set the specific slot's attention LED to | ||
83 | * the specified value | ||
84 | * @hardware_test: Called to run a specified hardware test on the specified | ||
85 | * slot. | ||
86 | * @get_power_status: Called to get the current power status of a slot. | ||
87 | * If this field is NULL, the value passed in the struct hotplug_slot_info | ||
88 | * will be used when this value is requested by a user. | ||
89 | * @get_attention_status: Called to get the current attention status of a slot. | ||
90 | * If this field is NULL, the value passed in the struct hotplug_slot_info | ||
91 | * will be used when this value is requested by a user. | ||
92 | * @get_latch_status: Called to get the current latch status of a slot. | ||
93 | * If this field is NULL, the value passed in the struct hotplug_slot_info | ||
94 | * will be used when this value is requested by a user. | ||
95 | * @get_adapter_status: Called to get see if an adapter is present in the slot or not. | ||
96 | * If this field is NULL, the value passed in the struct hotplug_slot_info | ||
97 | * will be used when this value is requested by a user. | ||
98 | * @get_address: Called to get pci address of a slot. | ||
99 | * If this field is NULL, the value passed in the struct hotplug_slot_info | ||
100 | * will be used when this value is requested by a user. | ||
101 | * @get_max_bus_speed: Called to get the max bus speed for a slot. | ||
102 | * If this field is NULL, the value passed in the struct hotplug_slot_info | ||
103 | * will be used when this value is requested by a user. | ||
104 | * @get_cur_bus_speed: Called to get the current bus speed for a slot. | ||
105 | * If this field is NULL, the value passed in the struct hotplug_slot_info | ||
106 | * will be used when this value is requested by a user. | ||
107 | * | ||
108 | * The table of function pointers that is passed to the hotplug pci core by a | ||
109 | * hotplug pci driver. These functions are called by the hotplug pci core when | ||
110 | * the user wants to do something to a specific slot (query it for information, | ||
111 | * set an LED, enable / disable power, etc.) | ||
112 | */ | ||
113 | struct hotplug_slot_ops { | ||
114 | struct module *owner; | ||
115 | int (*enable_slot) (struct hotplug_slot *slot); | ||
116 | int (*disable_slot) (struct hotplug_slot *slot); | ||
117 | int (*set_attention_status) (struct hotplug_slot *slot, u8 value); | ||
118 | int (*hardware_test) (struct hotplug_slot *slot, u32 value); | ||
119 | int (*get_power_status) (struct hotplug_slot *slot, u8 *value); | ||
120 | int (*get_attention_status) (struct hotplug_slot *slot, u8 *value); | ||
121 | int (*get_latch_status) (struct hotplug_slot *slot, u8 *value); | ||
122 | int (*get_adapter_status) (struct hotplug_slot *slot, u8 *value); | ||
123 | int (*get_address) (struct hotplug_slot *slot, u32 *value); | ||
124 | int (*get_max_bus_speed) (struct hotplug_slot *slot, enum pci_bus_speed *value); | ||
125 | int (*get_cur_bus_speed) (struct hotplug_slot *slot, enum pci_bus_speed *value); | ||
126 | }; | ||
127 | |||
128 | /** | ||
129 | * struct hotplug_slot_info - used to notify the hotplug pci core of the state of the slot | ||
130 | * @power: if power is enabled or not (1/0) | ||
131 | * @attention_status: if the attention light is enabled or not (1/0) | ||
132 | * @latch_status: if the latch (if any) is open or closed (1/0) | ||
133 | * @adapter_present: if there is a pci board present in the slot or not (1/0) | ||
134 | * @address: (domain << 16 | bus << 8 | dev) | ||
135 | * | ||
136 | * Used to notify the hotplug pci core of the status of a specific slot. | ||
137 | */ | ||
138 | struct hotplug_slot_info { | ||
139 | u8 power_status; | ||
140 | u8 attention_status; | ||
141 | u8 latch_status; | ||
142 | u8 adapter_status; | ||
143 | u32 address; | ||
144 | enum pci_bus_speed max_bus_speed; | ||
145 | enum pci_bus_speed cur_bus_speed; | ||
146 | }; | ||
147 | |||
148 | /** | ||
149 | * struct hotplug_slot - used to register a physical slot with the hotplug pci core | ||
150 | * @name: the name of the slot being registered. This string must | ||
151 | * be unique amoung slots registered on this system. | ||
152 | * @ops: pointer to the &struct hotplug_slot_ops to be used for this slot | ||
153 | * @info: pointer to the &struct hotplug_slot_info for the inital values for | ||
154 | * this slot. | ||
155 | * @release: called during pci_hp_deregister to free memory allocated in a | ||
156 | * hotplug_slot structure. | ||
157 | * @private: used by the hotplug pci controller driver to store whatever it | ||
158 | * needs. | ||
159 | */ | ||
160 | struct hotplug_slot { | ||
161 | char *name; | ||
162 | struct hotplug_slot_ops *ops; | ||
163 | struct hotplug_slot_info *info; | ||
164 | void (*release) (struct hotplug_slot *slot); | ||
165 | void *private; | ||
166 | |||
167 | /* Variables below this are for use only by the hotplug pci core. */ | ||
168 | struct list_head slot_list; | ||
169 | struct kobject kobj; | ||
170 | }; | ||
171 | #define to_hotplug_slot(n) container_of(n, struct hotplug_slot, kobj) | ||
172 | |||
173 | extern int pci_hp_register (struct hotplug_slot *slot); | ||
174 | extern int pci_hp_deregister (struct hotplug_slot *slot); | ||
175 | extern int pci_hp_change_slot_info (struct hotplug_slot *slot, | ||
176 | struct hotplug_slot_info *info); | ||
177 | extern struct subsystem pci_hotplug_slots_subsys; | ||
178 | |||
179 | #endif | ||
180 | |||
diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c new file mode 100644 index 000000000000..c802f6270b89 --- /dev/null +++ b/drivers/pci/hotplug/pci_hotplug_core.c | |||
@@ -0,0 +1,715 @@ | |||
1 | /* | ||
2 | * PCI HotPlug Controller Core | ||
3 | * | ||
4 | * Copyright (C) 2001-2002 Greg Kroah-Hartman (greg@kroah.com) | ||
5 | * Copyright (C) 2001-2002 IBM Corp. | ||
6 | * | ||
7 | * All rights reserved. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or (at | ||
12 | * your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, but | ||
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
17 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
18 | * details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
23 | * | ||
24 | * Send feedback to <greg@kroah.com> | ||
25 | * | ||
26 | * Filesystem portion based on work done by Pat Mochel on ddfs/driverfs | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | #include <linux/config.h> | ||
31 | #include <linux/module.h> | ||
32 | #include <linux/moduleparam.h> | ||
33 | #include <linux/kernel.h> | ||
34 | #include <linux/types.h> | ||
35 | #include <linux/list.h> | ||
36 | #include <linux/pagemap.h> | ||
37 | #include <linux/slab.h> | ||
38 | #include <linux/smp_lock.h> | ||
39 | #include <linux/init.h> | ||
40 | #include <linux/mount.h> | ||
41 | #include <linux/namei.h> | ||
42 | #include <linux/pci.h> | ||
43 | #include <asm/uaccess.h> | ||
44 | #include <linux/kobject.h> | ||
45 | #include <linux/sysfs.h> | ||
46 | #include "pci_hotplug.h" | ||
47 | |||
48 | |||
49 | #define MY_NAME "pci_hotplug" | ||
50 | |||
51 | #define dbg(fmt, arg...) do { if (debug) printk(KERN_DEBUG "%s: %s: " fmt , MY_NAME , __FUNCTION__ , ## arg); } while (0) | ||
52 | #define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg) | ||
53 | #define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg) | ||
54 | #define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg) | ||
55 | |||
56 | |||
57 | /* local variables */ | ||
58 | static int debug; | ||
59 | |||
60 | #define DRIVER_VERSION "0.5" | ||
61 | #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Scott Murray <scottm@somanetworks.com>" | ||
62 | #define DRIVER_DESC "PCI Hot Plug PCI Core" | ||
63 | |||
64 | |||
65 | ////////////////////////////////////////////////////////////////// | ||
66 | |||
67 | static LIST_HEAD(pci_hotplug_slot_list); | ||
68 | |||
69 | struct subsystem pci_hotplug_slots_subsys; | ||
70 | |||
71 | static ssize_t hotplug_slot_attr_show(struct kobject *kobj, | ||
72 | struct attribute *attr, char *buf) | ||
73 | { | ||
74 | struct hotplug_slot *slot = to_hotplug_slot(kobj); | ||
75 | struct hotplug_slot_attribute *attribute = to_hotplug_attr(attr); | ||
76 | return attribute->show ? attribute->show(slot, buf) : 0; | ||
77 | } | ||
78 | |||
79 | static ssize_t hotplug_slot_attr_store(struct kobject *kobj, | ||
80 | struct attribute *attr, const char *buf, size_t len) | ||
81 | { | ||
82 | struct hotplug_slot *slot = to_hotplug_slot(kobj); | ||
83 | struct hotplug_slot_attribute *attribute = to_hotplug_attr(attr); | ||
84 | return attribute->store ? attribute->store(slot, buf, len) : 0; | ||
85 | } | ||
86 | |||
87 | static struct sysfs_ops hotplug_slot_sysfs_ops = { | ||
88 | .show = hotplug_slot_attr_show, | ||
89 | .store = hotplug_slot_attr_store, | ||
90 | }; | ||
91 | |||
92 | static void hotplug_slot_release(struct kobject *kobj) | ||
93 | { | ||
94 | struct hotplug_slot *slot = to_hotplug_slot(kobj); | ||
95 | if (slot->release) | ||
96 | slot->release(slot); | ||
97 | } | ||
98 | |||
99 | static struct kobj_type hotplug_slot_ktype = { | ||
100 | .sysfs_ops = &hotplug_slot_sysfs_ops, | ||
101 | .release = &hotplug_slot_release, | ||
102 | }; | ||
103 | |||
104 | decl_subsys_name(pci_hotplug_slots, slots, &hotplug_slot_ktype, NULL); | ||
105 | |||
106 | /* these strings match up with the values in pci_bus_speed */ | ||
107 | static char *pci_bus_speed_strings[] = { | ||
108 | "33 MHz PCI", /* 0x00 */ | ||
109 | "66 MHz PCI", /* 0x01 */ | ||
110 | "66 MHz PCIX", /* 0x02 */ | ||
111 | "100 MHz PCIX", /* 0x03 */ | ||
112 | "133 MHz PCIX", /* 0x04 */ | ||
113 | NULL, /* 0x05 */ | ||
114 | NULL, /* 0x06 */ | ||
115 | NULL, /* 0x07 */ | ||
116 | NULL, /* 0x08 */ | ||
117 | "66 MHz PCIX 266", /* 0x09 */ | ||
118 | "100 MHz PCIX 266", /* 0x0a */ | ||
119 | "133 MHz PCIX 266", /* 0x0b */ | ||
120 | NULL, /* 0x0c */ | ||
121 | NULL, /* 0x0d */ | ||
122 | NULL, /* 0x0e */ | ||
123 | NULL, /* 0x0f */ | ||
124 | NULL, /* 0x10 */ | ||
125 | "66 MHz PCIX 533", /* 0x11 */ | ||
126 | "100 MHz PCIX 533", /* 0x12 */ | ||
127 | "133 MHz PCIX 533", /* 0x13 */ | ||
128 | "25 GBps PCI-E", /* 0x14 */ | ||
129 | }; | ||
130 | |||
131 | #ifdef CONFIG_HOTPLUG_PCI_CPCI | ||
132 | extern int cpci_hotplug_init(int debug); | ||
133 | extern void cpci_hotplug_exit(void); | ||
134 | #else | ||
135 | static inline int cpci_hotplug_init(int debug) { return 0; } | ||
136 | static inline void cpci_hotplug_exit(void) { } | ||
137 | #endif | ||
138 | |||
139 | /* Weee, fun with macros... */ | ||
140 | #define GET_STATUS(name,type) \ | ||
141 | static int get_##name (struct hotplug_slot *slot, type *value) \ | ||
142 | { \ | ||
143 | struct hotplug_slot_ops *ops = slot->ops; \ | ||
144 | int retval = 0; \ | ||
145 | if (try_module_get(ops->owner)) { \ | ||
146 | if (ops->get_##name) \ | ||
147 | retval = ops->get_##name (slot, value); \ | ||
148 | else \ | ||
149 | *value = slot->info->name; \ | ||
150 | module_put(ops->owner); \ | ||
151 | } \ | ||
152 | return retval; \ | ||
153 | } | ||
154 | |||
155 | GET_STATUS(power_status, u8) | ||
156 | GET_STATUS(attention_status, u8) | ||
157 | GET_STATUS(latch_status, u8) | ||
158 | GET_STATUS(adapter_status, u8) | ||
159 | GET_STATUS(address, u32) | ||
160 | GET_STATUS(max_bus_speed, enum pci_bus_speed) | ||
161 | GET_STATUS(cur_bus_speed, enum pci_bus_speed) | ||
162 | |||
163 | static ssize_t power_read_file (struct hotplug_slot *slot, char *buf) | ||
164 | { | ||
165 | int retval; | ||
166 | u8 value; | ||
167 | |||
168 | retval = get_power_status (slot, &value); | ||
169 | if (retval) | ||
170 | goto exit; | ||
171 | retval = sprintf (buf, "%d\n", value); | ||
172 | exit: | ||
173 | return retval; | ||
174 | } | ||
175 | |||
176 | static ssize_t power_write_file (struct hotplug_slot *slot, const char *buf, | ||
177 | size_t count) | ||
178 | { | ||
179 | unsigned long lpower; | ||
180 | u8 power; | ||
181 | int retval = 0; | ||
182 | |||
183 | lpower = simple_strtoul (buf, NULL, 10); | ||
184 | power = (u8)(lpower & 0xff); | ||
185 | dbg ("power = %d\n", power); | ||
186 | |||
187 | if (!try_module_get(slot->ops->owner)) { | ||
188 | retval = -ENODEV; | ||
189 | goto exit; | ||
190 | } | ||
191 | switch (power) { | ||
192 | case 0: | ||
193 | if (slot->ops->disable_slot) | ||
194 | retval = slot->ops->disable_slot(slot); | ||
195 | break; | ||
196 | |||
197 | case 1: | ||
198 | if (slot->ops->enable_slot) | ||
199 | retval = slot->ops->enable_slot(slot); | ||
200 | break; | ||
201 | |||
202 | default: | ||
203 | err ("Illegal value specified for power\n"); | ||
204 | retval = -EINVAL; | ||
205 | } | ||
206 | module_put(slot->ops->owner); | ||
207 | |||
208 | exit: | ||
209 | if (retval) | ||
210 | return retval; | ||
211 | return count; | ||
212 | } | ||
213 | |||
214 | static struct hotplug_slot_attribute hotplug_slot_attr_power = { | ||
215 | .attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR}, | ||
216 | .show = power_read_file, | ||
217 | .store = power_write_file | ||
218 | }; | ||
219 | |||
220 | static ssize_t attention_read_file (struct hotplug_slot *slot, char *buf) | ||
221 | { | ||
222 | int retval; | ||
223 | u8 value; | ||
224 | |||
225 | retval = get_attention_status (slot, &value); | ||
226 | if (retval) | ||
227 | goto exit; | ||
228 | retval = sprintf (buf, "%d\n", value); | ||
229 | |||
230 | exit: | ||
231 | return retval; | ||
232 | } | ||
233 | |||
234 | static ssize_t attention_write_file (struct hotplug_slot *slot, const char *buf, | ||
235 | size_t count) | ||
236 | { | ||
237 | unsigned long lattention; | ||
238 | u8 attention; | ||
239 | int retval = 0; | ||
240 | |||
241 | lattention = simple_strtoul (buf, NULL, 10); | ||
242 | attention = (u8)(lattention & 0xff); | ||
243 | dbg (" - attention = %d\n", attention); | ||
244 | |||
245 | if (!try_module_get(slot->ops->owner)) { | ||
246 | retval = -ENODEV; | ||
247 | goto exit; | ||
248 | } | ||
249 | if (slot->ops->set_attention_status) | ||
250 | retval = slot->ops->set_attention_status(slot, attention); | ||
251 | module_put(slot->ops->owner); | ||
252 | |||
253 | exit: | ||
254 | if (retval) | ||
255 | return retval; | ||
256 | return count; | ||
257 | } | ||
258 | |||
259 | static struct hotplug_slot_attribute hotplug_slot_attr_attention = { | ||
260 | .attr = {.name = "attention", .mode = S_IFREG | S_IRUGO | S_IWUSR}, | ||
261 | .show = attention_read_file, | ||
262 | .store = attention_write_file | ||
263 | }; | ||
264 | |||
265 | static ssize_t latch_read_file (struct hotplug_slot *slot, char *buf) | ||
266 | { | ||
267 | int retval; | ||
268 | u8 value; | ||
269 | |||
270 | retval = get_latch_status (slot, &value); | ||
271 | if (retval) | ||
272 | goto exit; | ||
273 | retval = sprintf (buf, "%d\n", value); | ||
274 | |||
275 | exit: | ||
276 | return retval; | ||
277 | } | ||
278 | |||
279 | static struct hotplug_slot_attribute hotplug_slot_attr_latch = { | ||
280 | .attr = {.name = "latch", .mode = S_IFREG | S_IRUGO}, | ||
281 | .show = latch_read_file, | ||
282 | }; | ||
283 | |||
284 | static ssize_t presence_read_file (struct hotplug_slot *slot, char *buf) | ||
285 | { | ||
286 | int retval; | ||
287 | u8 value; | ||
288 | |||
289 | retval = get_adapter_status (slot, &value); | ||
290 | if (retval) | ||
291 | goto exit; | ||
292 | retval = sprintf (buf, "%d\n", value); | ||
293 | |||
294 | exit: | ||
295 | return retval; | ||
296 | } | ||
297 | |||
298 | static struct hotplug_slot_attribute hotplug_slot_attr_presence = { | ||
299 | .attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO}, | ||
300 | .show = presence_read_file, | ||
301 | }; | ||
302 | |||
303 | static ssize_t address_read_file (struct hotplug_slot *slot, char *buf) | ||
304 | { | ||
305 | int retval; | ||
306 | u32 address; | ||
307 | |||
308 | retval = get_address (slot, &address); | ||
309 | if (retval) | ||
310 | goto exit; | ||
311 | retval = sprintf (buf, "%04x:%02x:%02x\n", | ||
312 | (address >> 16) & 0xffff, | ||
313 | (address >> 8) & 0xff, | ||
314 | address & 0xff); | ||
315 | |||
316 | exit: | ||
317 | return retval; | ||
318 | } | ||
319 | |||
320 | static struct hotplug_slot_attribute hotplug_slot_attr_address = { | ||
321 | .attr = {.name = "address", .mode = S_IFREG | S_IRUGO}, | ||
322 | .show = address_read_file, | ||
323 | }; | ||
324 | |||
325 | static char *unknown_speed = "Unknown bus speed"; | ||
326 | |||
327 | static ssize_t max_bus_speed_read_file (struct hotplug_slot *slot, char *buf) | ||
328 | { | ||
329 | char *speed_string; | ||
330 | int retval; | ||
331 | enum pci_bus_speed value; | ||
332 | |||
333 | retval = get_max_bus_speed (slot, &value); | ||
334 | if (retval) | ||
335 | goto exit; | ||
336 | |||
337 | if (value == PCI_SPEED_UNKNOWN) | ||
338 | speed_string = unknown_speed; | ||
339 | else | ||
340 | speed_string = pci_bus_speed_strings[value]; | ||
341 | |||
342 | retval = sprintf (buf, "%s\n", speed_string); | ||
343 | |||
344 | exit: | ||
345 | return retval; | ||
346 | } | ||
347 | |||
348 | static struct hotplug_slot_attribute hotplug_slot_attr_max_bus_speed = { | ||
349 | .attr = {.name = "max_bus_speed", .mode = S_IFREG | S_IRUGO}, | ||
350 | .show = max_bus_speed_read_file, | ||
351 | }; | ||
352 | |||
353 | static ssize_t cur_bus_speed_read_file (struct hotplug_slot *slot, char *buf) | ||
354 | { | ||
355 | char *speed_string; | ||
356 | int retval; | ||
357 | enum pci_bus_speed value; | ||
358 | |||
359 | retval = get_cur_bus_speed (slot, &value); | ||
360 | if (retval) | ||
361 | goto exit; | ||
362 | |||
363 | if (value == PCI_SPEED_UNKNOWN) | ||
364 | speed_string = unknown_speed; | ||
365 | else | ||
366 | speed_string = pci_bus_speed_strings[value]; | ||
367 | |||
368 | retval = sprintf (buf, "%s\n", speed_string); | ||
369 | |||
370 | exit: | ||
371 | return retval; | ||
372 | } | ||
373 | |||
374 | static struct hotplug_slot_attribute hotplug_slot_attr_cur_bus_speed = { | ||
375 | .attr = {.name = "cur_bus_speed", .mode = S_IFREG | S_IRUGO}, | ||
376 | .show = cur_bus_speed_read_file, | ||
377 | }; | ||
378 | |||
379 | static ssize_t test_write_file (struct hotplug_slot *slot, const char *buf, | ||
380 | size_t count) | ||
381 | { | ||
382 | unsigned long ltest; | ||
383 | u32 test; | ||
384 | int retval = 0; | ||
385 | |||
386 | ltest = simple_strtoul (buf, NULL, 10); | ||
387 | test = (u32)(ltest & 0xffffffff); | ||
388 | dbg ("test = %d\n", test); | ||
389 | |||
390 | if (!try_module_get(slot->ops->owner)) { | ||
391 | retval = -ENODEV; | ||
392 | goto exit; | ||
393 | } | ||
394 | if (slot->ops->hardware_test) | ||
395 | retval = slot->ops->hardware_test(slot, test); | ||
396 | module_put(slot->ops->owner); | ||
397 | |||
398 | exit: | ||
399 | if (retval) | ||
400 | return retval; | ||
401 | return count; | ||
402 | } | ||
403 | |||
404 | static struct hotplug_slot_attribute hotplug_slot_attr_test = { | ||
405 | .attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR}, | ||
406 | .store = test_write_file | ||
407 | }; | ||
408 | |||
409 | static int has_power_file (struct hotplug_slot *slot) | ||
410 | { | ||
411 | if ((!slot) || (!slot->ops)) | ||
412 | return -ENODEV; | ||
413 | if ((slot->ops->enable_slot) || | ||
414 | (slot->ops->disable_slot) || | ||
415 | (slot->ops->get_power_status)) | ||
416 | return 0; | ||
417 | return -ENOENT; | ||
418 | } | ||
419 | |||
420 | static int has_attention_file (struct hotplug_slot *slot) | ||
421 | { | ||
422 | if ((!slot) || (!slot->ops)) | ||
423 | return -ENODEV; | ||
424 | if ((slot->ops->set_attention_status) || | ||
425 | (slot->ops->get_attention_status)) | ||
426 | return 0; | ||
427 | return -ENOENT; | ||
428 | } | ||
429 | |||
430 | static int has_latch_file (struct hotplug_slot *slot) | ||
431 | { | ||
432 | if ((!slot) || (!slot->ops)) | ||
433 | return -ENODEV; | ||
434 | if (slot->ops->get_latch_status) | ||
435 | return 0; | ||
436 | return -ENOENT; | ||
437 | } | ||
438 | |||
439 | static int has_adapter_file (struct hotplug_slot *slot) | ||
440 | { | ||
441 | if ((!slot) || (!slot->ops)) | ||
442 | return -ENODEV; | ||
443 | if (slot->ops->get_adapter_status) | ||
444 | return 0; | ||
445 | return -ENOENT; | ||
446 | } | ||
447 | |||
448 | static int has_address_file (struct hotplug_slot *slot) | ||
449 | { | ||
450 | if ((!slot) || (!slot->ops)) | ||
451 | return -ENODEV; | ||
452 | if (slot->ops->get_address) | ||
453 | return 0; | ||
454 | return -ENOENT; | ||
455 | } | ||
456 | |||
457 | static int has_max_bus_speed_file (struct hotplug_slot *slot) | ||
458 | { | ||
459 | if ((!slot) || (!slot->ops)) | ||
460 | return -ENODEV; | ||
461 | if (slot->ops->get_max_bus_speed) | ||
462 | return 0; | ||
463 | return -ENOENT; | ||
464 | } | ||
465 | |||
466 | static int has_cur_bus_speed_file (struct hotplug_slot *slot) | ||
467 | { | ||
468 | if ((!slot) || (!slot->ops)) | ||
469 | return -ENODEV; | ||
470 | if (slot->ops->get_cur_bus_speed) | ||
471 | return 0; | ||
472 | return -ENOENT; | ||
473 | } | ||
474 | |||
475 | static int has_test_file (struct hotplug_slot *slot) | ||
476 | { | ||
477 | if ((!slot) || (!slot->ops)) | ||
478 | return -ENODEV; | ||
479 | if (slot->ops->hardware_test) | ||
480 | return 0; | ||
481 | return -ENOENT; | ||
482 | } | ||
483 | |||
484 | static int fs_add_slot (struct hotplug_slot *slot) | ||
485 | { | ||
486 | if (has_power_file(slot) == 0) | ||
487 | sysfs_create_file(&slot->kobj, &hotplug_slot_attr_power.attr); | ||
488 | |||
489 | if (has_attention_file(slot) == 0) | ||
490 | sysfs_create_file(&slot->kobj, &hotplug_slot_attr_attention.attr); | ||
491 | |||
492 | if (has_latch_file(slot) == 0) | ||
493 | sysfs_create_file(&slot->kobj, &hotplug_slot_attr_latch.attr); | ||
494 | |||
495 | if (has_adapter_file(slot) == 0) | ||
496 | sysfs_create_file(&slot->kobj, &hotplug_slot_attr_presence.attr); | ||
497 | |||
498 | if (has_address_file(slot) == 0) | ||
499 | sysfs_create_file(&slot->kobj, &hotplug_slot_attr_address.attr); | ||
500 | |||
501 | if (has_max_bus_speed_file(slot) == 0) | ||
502 | sysfs_create_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr); | ||
503 | |||
504 | if (has_cur_bus_speed_file(slot) == 0) | ||
505 | sysfs_create_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr); | ||
506 | |||
507 | if (has_test_file(slot) == 0) | ||
508 | sysfs_create_file(&slot->kobj, &hotplug_slot_attr_test.attr); | ||
509 | |||
510 | return 0; | ||
511 | } | ||
512 | |||
513 | static void fs_remove_slot (struct hotplug_slot *slot) | ||
514 | { | ||
515 | if (has_power_file(slot) == 0) | ||
516 | sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr); | ||
517 | |||
518 | if (has_attention_file(slot) == 0) | ||
519 | sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_attention.attr); | ||
520 | |||
521 | if (has_latch_file(slot) == 0) | ||
522 | sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr); | ||
523 | |||
524 | if (has_adapter_file(slot) == 0) | ||
525 | sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr); | ||
526 | |||
527 | if (has_address_file(slot) == 0) | ||
528 | sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_address.attr); | ||
529 | |||
530 | if (has_max_bus_speed_file(slot) == 0) | ||
531 | sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr); | ||
532 | |||
533 | if (has_cur_bus_speed_file(slot) == 0) | ||
534 | sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr); | ||
535 | |||
536 | if (has_test_file(slot) == 0) | ||
537 | sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_test.attr); | ||
538 | } | ||
539 | |||
540 | static struct hotplug_slot *get_slot_from_name (const char *name) | ||
541 | { | ||
542 | struct hotplug_slot *slot; | ||
543 | struct list_head *tmp; | ||
544 | |||
545 | list_for_each (tmp, &pci_hotplug_slot_list) { | ||
546 | slot = list_entry (tmp, struct hotplug_slot, slot_list); | ||
547 | if (strcmp(slot->name, name) == 0) | ||
548 | return slot; | ||
549 | } | ||
550 | return NULL; | ||
551 | } | ||
552 | |||
553 | /** | ||
554 | * pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem | ||
555 | * @slot: pointer to the &struct hotplug_slot to register | ||
556 | * | ||
557 | * Registers a hotplug slot with the pci hotplug subsystem, which will allow | ||
558 | * userspace interaction to the slot. | ||
559 | * | ||
560 | * Returns 0 if successful, anything else for an error. | ||
561 | */ | ||
562 | int pci_hp_register (struct hotplug_slot *slot) | ||
563 | { | ||
564 | int result; | ||
565 | |||
566 | if (slot == NULL) | ||
567 | return -ENODEV; | ||
568 | if ((slot->info == NULL) || (slot->ops == NULL)) | ||
569 | return -EINVAL; | ||
570 | if (slot->release == NULL) { | ||
571 | dbg("Why are you trying to register a hotplug slot" | ||
572 | "without a proper release function?\n"); | ||
573 | return -EINVAL; | ||
574 | } | ||
575 | |||
576 | kobject_set_name(&slot->kobj, "%s", slot->name); | ||
577 | kobj_set_kset_s(slot, pci_hotplug_slots_subsys); | ||
578 | |||
579 | /* this can fail if we have already registered a slot with the same name */ | ||
580 | if (kobject_register(&slot->kobj)) { | ||
581 | err("Unable to register kobject"); | ||
582 | return -EINVAL; | ||
583 | } | ||
584 | |||
585 | list_add (&slot->slot_list, &pci_hotplug_slot_list); | ||
586 | |||
587 | result = fs_add_slot (slot); | ||
588 | dbg ("Added slot %s to the list\n", slot->name); | ||
589 | return result; | ||
590 | } | ||
591 | |||
592 | /** | ||
593 | * pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem | ||
594 | * @slot: pointer to the &struct hotplug_slot to deregister | ||
595 | * | ||
596 | * The @slot must have been registered with the pci hotplug subsystem | ||
597 | * previously with a call to pci_hp_register(). | ||
598 | * | ||
599 | * Returns 0 if successful, anything else for an error. | ||
600 | */ | ||
601 | int pci_hp_deregister (struct hotplug_slot *slot) | ||
602 | { | ||
603 | struct hotplug_slot *temp; | ||
604 | |||
605 | if (slot == NULL) | ||
606 | return -ENODEV; | ||
607 | |||
608 | temp = get_slot_from_name (slot->name); | ||
609 | if (temp != slot) { | ||
610 | return -ENODEV; | ||
611 | } | ||
612 | list_del (&slot->slot_list); | ||
613 | |||
614 | fs_remove_slot (slot); | ||
615 | dbg ("Removed slot %s from the list\n", slot->name); | ||
616 | kobject_unregister(&slot->kobj); | ||
617 | return 0; | ||
618 | } | ||
619 | |||
620 | /** | ||
621 | * pci_hp_change_slot_info - changes the slot's information structure in the core | ||
622 | * @slot: pointer to the slot whose info has changed | ||
623 | * @info: pointer to the info copy into the slot's info structure | ||
624 | * | ||
625 | * @slot must have been registered with the pci | ||
626 | * hotplug subsystem previously with a call to pci_hp_register(). | ||
627 | * | ||
628 | * Returns 0 if successful, anything else for an error. | ||
629 | */ | ||
630 | int pci_hp_change_slot_info (struct hotplug_slot *slot, struct hotplug_slot_info *info) | ||
631 | { | ||
632 | if ((slot == NULL) || (info == NULL)) | ||
633 | return -ENODEV; | ||
634 | |||
635 | /* | ||
636 | * check all fields in the info structure, and update timestamps | ||
637 | * for the files referring to the fields that have now changed. | ||
638 | */ | ||
639 | if ((has_power_file(slot) == 0) && | ||
640 | (slot->info->power_status != info->power_status)) | ||
641 | sysfs_update_file(&slot->kobj, &hotplug_slot_attr_power.attr); | ||
642 | |||
643 | if ((has_attention_file(slot) == 0) && | ||
644 | (slot->info->attention_status != info->attention_status)) | ||
645 | sysfs_update_file(&slot->kobj, &hotplug_slot_attr_attention.attr); | ||
646 | |||
647 | if ((has_latch_file(slot) == 0) && | ||
648 | (slot->info->latch_status != info->latch_status)) | ||
649 | sysfs_update_file(&slot->kobj, &hotplug_slot_attr_latch.attr); | ||
650 | |||
651 | if ((has_adapter_file(slot) == 0) && | ||
652 | (slot->info->adapter_status != info->adapter_status)) | ||
653 | sysfs_update_file(&slot->kobj, &hotplug_slot_attr_presence.attr); | ||
654 | |||
655 | if ((has_address_file(slot) == 0) && | ||
656 | (slot->info->address != info->address)) | ||
657 | sysfs_update_file(&slot->kobj, &hotplug_slot_attr_address.attr); | ||
658 | |||
659 | if ((has_max_bus_speed_file(slot) == 0) && | ||
660 | (slot->info->max_bus_speed != info->max_bus_speed)) | ||
661 | sysfs_update_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr); | ||
662 | |||
663 | if ((has_cur_bus_speed_file(slot) == 0) && | ||
664 | (slot->info->cur_bus_speed != info->cur_bus_speed)) | ||
665 | sysfs_update_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr); | ||
666 | |||
667 | memcpy (slot->info, info, sizeof (struct hotplug_slot_info)); | ||
668 | |||
669 | return 0; | ||
670 | } | ||
671 | |||
672 | static int __init pci_hotplug_init (void) | ||
673 | { | ||
674 | int result; | ||
675 | |||
676 | kset_set_kset_s(&pci_hotplug_slots_subsys, pci_bus_type.subsys); | ||
677 | result = subsystem_register(&pci_hotplug_slots_subsys); | ||
678 | if (result) { | ||
679 | err("Register subsys with error %d\n", result); | ||
680 | goto exit; | ||
681 | } | ||
682 | result = cpci_hotplug_init(debug); | ||
683 | if (result) { | ||
684 | err ("cpci_hotplug_init with error %d\n", result); | ||
685 | goto err_subsys; | ||
686 | } | ||
687 | |||
688 | info (DRIVER_DESC " version: " DRIVER_VERSION "\n"); | ||
689 | goto exit; | ||
690 | |||
691 | err_subsys: | ||
692 | subsystem_unregister(&pci_hotplug_slots_subsys); | ||
693 | exit: | ||
694 | return result; | ||
695 | } | ||
696 | |||
697 | static void __exit pci_hotplug_exit (void) | ||
698 | { | ||
699 | cpci_hotplug_exit(); | ||
700 | subsystem_unregister(&pci_hotplug_slots_subsys); | ||
701 | } | ||
702 | |||
703 | module_init(pci_hotplug_init); | ||
704 | module_exit(pci_hotplug_exit); | ||
705 | |||
706 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
707 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
708 | MODULE_LICENSE("GPL"); | ||
709 | module_param(debug, bool, 0644); | ||
710 | MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); | ||
711 | |||
712 | EXPORT_SYMBOL_GPL(pci_hotplug_slots_subsys); | ||
713 | EXPORT_SYMBOL_GPL(pci_hp_register); | ||
714 | EXPORT_SYMBOL_GPL(pci_hp_deregister); | ||
715 | EXPORT_SYMBOL_GPL(pci_hp_change_slot_info); | ||
diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h new file mode 100644 index 000000000000..f313121d5141 --- /dev/null +++ b/drivers/pci/hotplug/pciehp.h | |||
@@ -0,0 +1,352 @@ | |||
1 | /* | ||
2 | * PCI Express Hot Plug Controller Driver | ||
3 | * | ||
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | ||
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * Copyright (C) 2001 IBM Corp. | ||
7 | * Copyright (C) 2003-2004 Intel Corporation | ||
8 | * | ||
9 | * All rights reserved. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or (at | ||
14 | * your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
19 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
20 | * details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
25 | * | ||
26 | * Send feedback to <greg@kroah.com>, <dely.l.sy@intel.com> | ||
27 | * | ||
28 | */ | ||
29 | #ifndef _PCIEHP_H | ||
30 | #define _PCIEHP_H | ||
31 | |||
32 | #include <linux/types.h> | ||
33 | #include <linux/pci.h> | ||
34 | #include <linux/delay.h> | ||
35 | #include <asm/semaphore.h> | ||
36 | #include <asm/io.h> | ||
37 | #include <linux/pcieport_if.h> | ||
38 | #include "pci_hotplug.h" | ||
39 | |||
40 | #define MY_NAME "pciehp" | ||
41 | |||
42 | extern int pciehp_poll_mode; | ||
43 | extern int pciehp_poll_time; | ||
44 | extern int pciehp_debug; | ||
45 | |||
46 | /*#define dbg(format, arg...) do { if (pciehp_debug) printk(KERN_DEBUG "%s: " format, MY_NAME , ## arg); } while (0)*/ | ||
47 | #define dbg(format, arg...) do { if (pciehp_debug) printk("%s: " format, MY_NAME , ## arg); } while (0) | ||
48 | #define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg) | ||
49 | #define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg) | ||
50 | #define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME , ## arg) | ||
51 | |||
52 | struct pci_func { | ||
53 | struct pci_func *next; | ||
54 | u8 bus; | ||
55 | u8 device; | ||
56 | u8 function; | ||
57 | u8 is_a_board; | ||
58 | u16 status; | ||
59 | u8 configured; | ||
60 | u8 switch_save; | ||
61 | u8 presence_save; | ||
62 | u32 base_length[0x06]; | ||
63 | u8 base_type[0x06]; | ||
64 | u16 reserved2; | ||
65 | u32 config_space[0x20]; | ||
66 | struct pci_resource *mem_head; | ||
67 | struct pci_resource *p_mem_head; | ||
68 | struct pci_resource *io_head; | ||
69 | struct pci_resource *bus_head; | ||
70 | struct pci_dev* pci_dev; | ||
71 | }; | ||
72 | |||
73 | struct slot { | ||
74 | struct slot *next; | ||
75 | u8 bus; | ||
76 | u8 device; | ||
77 | u32 number; | ||
78 | u8 is_a_board; | ||
79 | u8 configured; | ||
80 | u8 state; | ||
81 | u8 switch_save; | ||
82 | u8 presence_save; | ||
83 | u32 capabilities; | ||
84 | u16 reserved2; | ||
85 | struct timer_list task_event; | ||
86 | u8 hp_slot; | ||
87 | struct controller *ctrl; | ||
88 | struct hpc_ops *hpc_ops; | ||
89 | struct hotplug_slot *hotplug_slot; | ||
90 | struct list_head slot_list; | ||
91 | }; | ||
92 | |||
93 | struct pci_resource { | ||
94 | struct pci_resource * next; | ||
95 | u32 base; | ||
96 | u32 length; | ||
97 | }; | ||
98 | |||
99 | struct event_info { | ||
100 | u32 event_type; | ||
101 | u8 hp_slot; | ||
102 | }; | ||
103 | |||
104 | struct controller { | ||
105 | struct controller *next; | ||
106 | struct semaphore crit_sect; /* critical section semaphore */ | ||
107 | void *hpc_ctlr_handle; /* HPC controller handle */ | ||
108 | int num_slots; /* Number of slots on ctlr */ | ||
109 | int slot_num_inc; /* 1 or -1 */ | ||
110 | struct pci_resource *mem_head; | ||
111 | struct pci_resource *p_mem_head; | ||
112 | struct pci_resource *io_head; | ||
113 | struct pci_resource *bus_head; | ||
114 | struct pci_dev *pci_dev; | ||
115 | struct pci_bus *pci_bus; | ||
116 | struct event_info event_queue[10]; | ||
117 | struct slot *slot; | ||
118 | struct hpc_ops *hpc_ops; | ||
119 | wait_queue_head_t queue; /* sleep & wake process */ | ||
120 | u8 next_event; | ||
121 | u8 seg; | ||
122 | u8 bus; | ||
123 | u8 device; | ||
124 | u8 function; | ||
125 | u8 rev; | ||
126 | u8 slot_device_offset; | ||
127 | u8 add_support; | ||
128 | enum pci_bus_speed speed; | ||
129 | u32 first_slot; /* First physical slot number */ /* PCIE only has 1 slot */ | ||
130 | u8 slot_bus; /* Bus where the slots handled by this controller sit */ | ||
131 | u8 ctrlcap; | ||
132 | u16 vendor_id; | ||
133 | }; | ||
134 | |||
135 | struct irq_mapping { | ||
136 | u8 barber_pole; | ||
137 | u8 valid_INT; | ||
138 | u8 interrupt[4]; | ||
139 | }; | ||
140 | |||
141 | struct resource_lists { | ||
142 | struct pci_resource *mem_head; | ||
143 | struct pci_resource *p_mem_head; | ||
144 | struct pci_resource *io_head; | ||
145 | struct pci_resource *bus_head; | ||
146 | struct irq_mapping *irqs; | ||
147 | }; | ||
148 | |||
149 | #define INT_BUTTON_IGNORE 0 | ||
150 | #define INT_PRESENCE_ON 1 | ||
151 | #define INT_PRESENCE_OFF 2 | ||
152 | #define INT_SWITCH_CLOSE 3 | ||
153 | #define INT_SWITCH_OPEN 4 | ||
154 | #define INT_POWER_FAULT 5 | ||
155 | #define INT_POWER_FAULT_CLEAR 6 | ||
156 | #define INT_BUTTON_PRESS 7 | ||
157 | #define INT_BUTTON_RELEASE 8 | ||
158 | #define INT_BUTTON_CANCEL 9 | ||
159 | |||
160 | #define STATIC_STATE 0 | ||
161 | #define BLINKINGON_STATE 1 | ||
162 | #define BLINKINGOFF_STATE 2 | ||
163 | #define POWERON_STATE 3 | ||
164 | #define POWEROFF_STATE 4 | ||
165 | |||
166 | #define PCI_TO_PCI_BRIDGE_CLASS 0x00060400 | ||
167 | |||
168 | /* Error messages */ | ||
169 | #define INTERLOCK_OPEN 0x00000002 | ||
170 | #define ADD_NOT_SUPPORTED 0x00000003 | ||
171 | #define CARD_FUNCTIONING 0x00000005 | ||
172 | #define ADAPTER_NOT_SAME 0x00000006 | ||
173 | #define NO_ADAPTER_PRESENT 0x00000009 | ||
174 | #define NOT_ENOUGH_RESOURCES 0x0000000B | ||
175 | #define DEVICE_TYPE_NOT_SUPPORTED 0x0000000C | ||
176 | #define WRONG_BUS_FREQUENCY 0x0000000D | ||
177 | #define POWER_FAILURE 0x0000000E | ||
178 | |||
179 | #define REMOVE_NOT_SUPPORTED 0x00000003 | ||
180 | |||
181 | #define DISABLE_CARD 1 | ||
182 | |||
183 | /* Field definitions in Slot Capabilities Register */ | ||
184 | #define ATTN_BUTTN_PRSN 0x00000001 | ||
185 | #define PWR_CTRL_PRSN 0x00000002 | ||
186 | #define MRL_SENS_PRSN 0x00000004 | ||
187 | #define ATTN_LED_PRSN 0x00000008 | ||
188 | #define PWR_LED_PRSN 0x00000010 | ||
189 | #define HP_SUPR_RM_SUP 0x00000020 | ||
190 | |||
191 | #define ATTN_BUTTN(cap) (cap & ATTN_BUTTN_PRSN) | ||
192 | #define POWER_CTRL(cap) (cap & PWR_CTRL_PRSN) | ||
193 | #define MRL_SENS(cap) (cap & MRL_SENS_PRSN) | ||
194 | #define ATTN_LED(cap) (cap & ATTN_LED_PRSN) | ||
195 | #define PWR_LED(cap) (cap & PWR_LED_PRSN) | ||
196 | #define HP_SUPR_RM(cap) (cap & HP_SUPR_RM_SUP) | ||
197 | |||
198 | /* | ||
199 | * error Messages | ||
200 | */ | ||
201 | #define msg_initialization_err "Initialization failure, error=%d\n" | ||
202 | #define msg_HPC_rev_error "Unsupported revision of the PCI hot plug controller found.\n" | ||
203 | #define msg_HPC_non_pcie "The PCI hot plug controller is not supported by this driver.\n" | ||
204 | #define msg_HPC_not_supported "This system is not supported by this version of pciephd module. Upgrade to a newer version of pciehpd\n" | ||
205 | #define msg_unable_to_save "Unable to store PCI hot plug add resource information. This system must be rebooted before adding any PCI devices.\n" | ||
206 | #define msg_button_on "PCI slot #%d - powering on due to button press.\n" | ||
207 | #define msg_button_off "PCI slot #%d - powering off due to button press.\n" | ||
208 | #define msg_button_cancel "PCI slot #%d - action canceled due to button press.\n" | ||
209 | #define msg_button_ignore "PCI slot #%d - button press ignored. (action in progress...)\n" | ||
210 | |||
211 | /* controller functions */ | ||
212 | extern int pciehprm_find_available_resources (struct controller *ctrl); | ||
213 | extern int pciehp_event_start_thread (void); | ||
214 | extern void pciehp_event_stop_thread (void); | ||
215 | extern struct pci_func *pciehp_slot_create (unsigned char busnumber); | ||
216 | extern struct pci_func *pciehp_slot_find (unsigned char bus, unsigned char device, unsigned char index); | ||
217 | extern int pciehp_enable_slot (struct slot *slot); | ||
218 | extern int pciehp_disable_slot (struct slot *slot); | ||
219 | |||
220 | extern u8 pciehp_handle_attention_button (u8 hp_slot, void *inst_id); | ||
221 | extern u8 pciehp_handle_switch_change (u8 hp_slot, void *inst_id); | ||
222 | extern u8 pciehp_handle_presence_change (u8 hp_slot, void *inst_id); | ||
223 | extern u8 pciehp_handle_power_fault (u8 hp_slot, void *inst_id); | ||
224 | /* extern void long_delay (int delay); */ | ||
225 | |||
226 | /* resource functions */ | ||
227 | extern int pciehp_resource_sort_and_combine (struct pci_resource **head); | ||
228 | |||
229 | /* pci functions */ | ||
230 | extern int pciehp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num); | ||
231 | /*extern int pciehp_get_bus_dev (struct controller *ctrl, u8 *bus_num, u8 *dev_num, struct slot *slot);*/ | ||
232 | extern int pciehp_save_config (struct controller *ctrl, int busnumber, int num_ctlr_slots, int first_device_num); | ||
233 | extern int pciehp_save_used_resources (struct controller *ctrl, struct pci_func * func, int flag); | ||
234 | extern int pciehp_save_slot_config (struct controller *ctrl, struct pci_func * new_slot); | ||
235 | extern void pciehp_destroy_board_resources (struct pci_func * func); | ||
236 | extern int pciehp_return_board_resources (struct pci_func * func, struct resource_lists * resources); | ||
237 | extern void pciehp_destroy_resource_list (struct resource_lists * resources); | ||
238 | extern int pciehp_configure_device (struct controller* ctrl, struct pci_func* func); | ||
239 | extern int pciehp_unconfigure_device (struct pci_func* func); | ||
240 | |||
241 | |||
242 | /* Global variables */ | ||
243 | extern struct controller *pciehp_ctrl_list; | ||
244 | extern struct pci_func *pciehp_slot_list[256]; | ||
245 | |||
246 | /* Inline functions */ | ||
247 | |||
248 | static inline struct slot *pciehp_find_slot(struct controller *ctrl, u8 device) | ||
249 | { | ||
250 | struct slot *p_slot, *tmp_slot = NULL; | ||
251 | |||
252 | p_slot = ctrl->slot; | ||
253 | |||
254 | dbg("p_slot = %p\n", p_slot); | ||
255 | |||
256 | while (p_slot && (p_slot->device != device)) { | ||
257 | tmp_slot = p_slot; | ||
258 | p_slot = p_slot->next; | ||
259 | dbg("In while loop, p_slot = %p\n", p_slot); | ||
260 | } | ||
261 | if (p_slot == NULL) { | ||
262 | err("ERROR: pciehp_find_slot device=0x%x\n", device); | ||
263 | p_slot = tmp_slot; | ||
264 | } | ||
265 | |||
266 | return p_slot; | ||
267 | } | ||
268 | |||
269 | static inline int wait_for_ctrl_irq(struct controller *ctrl) | ||
270 | { | ||
271 | int retval = 0; | ||
272 | |||
273 | DECLARE_WAITQUEUE(wait, current); | ||
274 | |||
275 | dbg("%s : start\n", __FUNCTION__); | ||
276 | add_wait_queue(&ctrl->queue, &wait); | ||
277 | if (!pciehp_poll_mode) | ||
278 | /* Sleep for up to 1 second */ | ||
279 | msleep_interruptible(1000); | ||
280 | else | ||
281 | msleep_interruptible(2500); | ||
282 | |||
283 | remove_wait_queue(&ctrl->queue, &wait); | ||
284 | if (signal_pending(current)) | ||
285 | retval = -EINTR; | ||
286 | |||
287 | dbg("%s : end\n", __FUNCTION__); | ||
288 | return retval; | ||
289 | } | ||
290 | |||
291 | /* Puts node back in the resource list pointed to by head */ | ||
292 | static inline void return_resource(struct pci_resource **head, struct pci_resource *node) | ||
293 | { | ||
294 | if (!node || !head) | ||
295 | return; | ||
296 | node->next = *head; | ||
297 | *head = node; | ||
298 | } | ||
299 | |||
300 | #define SLOT_NAME_SIZE 10 | ||
301 | |||
302 | static inline void make_slot_name(char *buffer, int buffer_size, struct slot *slot) | ||
303 | { | ||
304 | snprintf(buffer, buffer_size, "%d", slot->number); | ||
305 | } | ||
306 | |||
307 | enum php_ctlr_type { | ||
308 | PCI, | ||
309 | ISA, | ||
310 | ACPI | ||
311 | }; | ||
312 | |||
313 | typedef u8(*php_intr_callback_t) (unsigned int change_id, void *instance_id); | ||
314 | |||
315 | int pcie_init(struct controller *ctrl, struct pcie_device *dev, | ||
316 | php_intr_callback_t attention_button_callback, | ||
317 | php_intr_callback_t switch_change_callback, | ||
318 | php_intr_callback_t presence_change_callback, | ||
319 | php_intr_callback_t power_fault_callback); | ||
320 | |||
321 | |||
322 | /* This has no meaning for PCI Express, as there is only 1 slot per port */ | ||
323 | int pcie_get_ctlr_slot_config(struct controller *ctrl, | ||
324 | int *num_ctlr_slots, | ||
325 | int *first_device_num, | ||
326 | int *physical_slot_num, | ||
327 | u8 *ctrlcap); | ||
328 | |||
329 | struct hpc_ops { | ||
330 | int (*power_on_slot) (struct slot *slot); | ||
331 | int (*power_off_slot) (struct slot *slot); | ||
332 | int (*get_power_status) (struct slot *slot, u8 *status); | ||
333 | int (*get_attention_status) (struct slot *slot, u8 *status); | ||
334 | int (*set_attention_status) (struct slot *slot, u8 status); | ||
335 | int (*get_latch_status) (struct slot *slot, u8 *status); | ||
336 | int (*get_adapter_status) (struct slot *slot, u8 *status); | ||
337 | |||
338 | int (*get_max_bus_speed) (struct slot *slot, enum pci_bus_speed *speed); | ||
339 | int (*get_cur_bus_speed) (struct slot *slot, enum pci_bus_speed *speed); | ||
340 | |||
341 | int (*get_max_lnk_width) (struct slot *slot, enum pcie_link_width *value); | ||
342 | int (*get_cur_lnk_width) (struct slot *slot, enum pcie_link_width *value); | ||
343 | |||
344 | int (*query_power_fault) (struct slot *slot); | ||
345 | void (*green_led_on) (struct slot *slot); | ||
346 | void (*green_led_off) (struct slot *slot); | ||
347 | void (*green_led_blink) (struct slot *slot); | ||
348 | void (*release_ctlr) (struct controller *ctrl); | ||
349 | int (*check_lnk_status) (struct controller *ctrl); | ||
350 | }; | ||
351 | |||
352 | #endif /* _PCIEHP_H */ | ||
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c new file mode 100644 index 000000000000..8a5b2b51bde4 --- /dev/null +++ b/drivers/pci/hotplug/pciehp_core.c | |||
@@ -0,0 +1,662 @@ | |||
1 | /* | ||
2 | * PCI Express Hot Plug Controller Driver | ||
3 | * | ||
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | ||
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * Copyright (C) 2001 IBM Corp. | ||
7 | * Copyright (C) 2003-2004 Intel Corporation | ||
8 | * | ||
9 | * All rights reserved. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or (at | ||
14 | * your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
19 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
20 | * details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
25 | * | ||
26 | * Send feedback to <greg@kroah.com>, <dely.l.sy@intel.com> | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | #include <linux/config.h> | ||
31 | #include <linux/module.h> | ||
32 | #include <linux/moduleparam.h> | ||
33 | #include <linux/kernel.h> | ||
34 | #include <linux/types.h> | ||
35 | #include <linux/proc_fs.h> | ||
36 | #include <linux/slab.h> | ||
37 | #include <linux/workqueue.h> | ||
38 | #include <linux/pci.h> | ||
39 | #include <linux/init.h> | ||
40 | #include <asm/uaccess.h> | ||
41 | #include "pciehp.h" | ||
42 | #include "pciehprm.h" | ||
43 | #include <linux/interrupt.h> | ||
44 | |||
45 | /* Global variables */ | ||
46 | int pciehp_debug; | ||
47 | int pciehp_poll_mode; | ||
48 | int pciehp_poll_time; | ||
49 | struct controller *pciehp_ctrl_list; | ||
50 | struct pci_func *pciehp_slot_list[256]; | ||
51 | |||
52 | #define DRIVER_VERSION "0.4" | ||
53 | #define DRIVER_AUTHOR "Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>" | ||
54 | #define DRIVER_DESC "PCI Express Hot Plug Controller Driver" | ||
55 | |||
56 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
57 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
58 | MODULE_LICENSE("GPL"); | ||
59 | |||
60 | module_param(pciehp_debug, bool, 0644); | ||
61 | module_param(pciehp_poll_mode, bool, 0644); | ||
62 | module_param(pciehp_poll_time, int, 0644); | ||
63 | MODULE_PARM_DESC(pciehp_debug, "Debugging mode enabled or not"); | ||
64 | MODULE_PARM_DESC(pciehp_poll_mode, "Using polling mechanism for hot-plug events or not"); | ||
65 | MODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds"); | ||
66 | |||
67 | #define PCIE_MODULE_NAME "pciehp" | ||
68 | |||
69 | static int pcie_start_thread (void); | ||
70 | static int set_attention_status (struct hotplug_slot *slot, u8 value); | ||
71 | static int enable_slot (struct hotplug_slot *slot); | ||
72 | static int disable_slot (struct hotplug_slot *slot); | ||
73 | static int get_power_status (struct hotplug_slot *slot, u8 *value); | ||
74 | static int get_attention_status (struct hotplug_slot *slot, u8 *value); | ||
75 | static int get_latch_status (struct hotplug_slot *slot, u8 *value); | ||
76 | static int get_adapter_status (struct hotplug_slot *slot, u8 *value); | ||
77 | static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value); | ||
78 | static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value); | ||
79 | |||
80 | static struct hotplug_slot_ops pciehp_hotplug_slot_ops = { | ||
81 | .owner = THIS_MODULE, | ||
82 | .set_attention_status = set_attention_status, | ||
83 | .enable_slot = enable_slot, | ||
84 | .disable_slot = disable_slot, | ||
85 | .get_power_status = get_power_status, | ||
86 | .get_attention_status = get_attention_status, | ||
87 | .get_latch_status = get_latch_status, | ||
88 | .get_adapter_status = get_adapter_status, | ||
89 | .get_max_bus_speed = get_max_bus_speed, | ||
90 | .get_cur_bus_speed = get_cur_bus_speed, | ||
91 | }; | ||
92 | |||
93 | static int init_slots(struct controller *ctrl) | ||
94 | { | ||
95 | struct slot *new_slot; | ||
96 | u8 number_of_slots; | ||
97 | u8 slot_device; | ||
98 | u32 slot_number; | ||
99 | int result = -ENOMEM; | ||
100 | |||
101 | dbg("%s\n",__FUNCTION__); | ||
102 | |||
103 | number_of_slots = ctrl->num_slots; | ||
104 | slot_device = ctrl->slot_device_offset; | ||
105 | slot_number = ctrl->first_slot; | ||
106 | |||
107 | while (number_of_slots) { | ||
108 | new_slot = kmalloc(sizeof(*new_slot), GFP_KERNEL); | ||
109 | if (!new_slot) | ||
110 | goto error; | ||
111 | |||
112 | memset(new_slot, 0, sizeof(struct slot)); | ||
113 | new_slot->hotplug_slot = | ||
114 | kmalloc(sizeof(*(new_slot->hotplug_slot)), | ||
115 | GFP_KERNEL); | ||
116 | if (!new_slot->hotplug_slot) | ||
117 | goto error_slot; | ||
118 | memset(new_slot->hotplug_slot, 0, sizeof(struct hotplug_slot)); | ||
119 | |||
120 | new_slot->hotplug_slot->info = | ||
121 | kmalloc(sizeof(*(new_slot->hotplug_slot->info)), | ||
122 | GFP_KERNEL); | ||
123 | if (!new_slot->hotplug_slot->info) | ||
124 | goto error_hpslot; | ||
125 | memset(new_slot->hotplug_slot->info, 0, | ||
126 | sizeof(struct hotplug_slot_info)); | ||
127 | new_slot->hotplug_slot->name = kmalloc(SLOT_NAME_SIZE, | ||
128 | GFP_KERNEL); | ||
129 | if (!new_slot->hotplug_slot->name) | ||
130 | goto error_info; | ||
131 | |||
132 | new_slot->ctrl = ctrl; | ||
133 | new_slot->bus = ctrl->slot_bus; | ||
134 | new_slot->device = slot_device; | ||
135 | new_slot->hpc_ops = ctrl->hpc_ops; | ||
136 | |||
137 | new_slot->number = ctrl->first_slot; | ||
138 | new_slot->hp_slot = slot_device - ctrl->slot_device_offset; | ||
139 | |||
140 | /* register this slot with the hotplug pci core */ | ||
141 | new_slot->hotplug_slot->private = new_slot; | ||
142 | make_slot_name (new_slot->hotplug_slot->name, SLOT_NAME_SIZE, new_slot); | ||
143 | new_slot->hotplug_slot->ops = &pciehp_hotplug_slot_ops; | ||
144 | |||
145 | new_slot->hpc_ops->get_power_status(new_slot, &(new_slot->hotplug_slot->info->power_status)); | ||
146 | new_slot->hpc_ops->get_attention_status(new_slot, &(new_slot->hotplug_slot->info->attention_status)); | ||
147 | new_slot->hpc_ops->get_latch_status(new_slot, &(new_slot->hotplug_slot->info->latch_status)); | ||
148 | new_slot->hpc_ops->get_adapter_status(new_slot, &(new_slot->hotplug_slot->info->adapter_status)); | ||
149 | |||
150 | dbg("Registering bus=%x dev=%x hp_slot=%x sun=%x slot_device_offset=%x\n", | ||
151 | new_slot->bus, new_slot->device, new_slot->hp_slot, new_slot->number, ctrl->slot_device_offset); | ||
152 | result = pci_hp_register (new_slot->hotplug_slot); | ||
153 | if (result) { | ||
154 | err ("pci_hp_register failed with error %d\n", result); | ||
155 | goto error_name; | ||
156 | } | ||
157 | |||
158 | new_slot->next = ctrl->slot; | ||
159 | ctrl->slot = new_slot; | ||
160 | |||
161 | number_of_slots--; | ||
162 | slot_device++; | ||
163 | slot_number += ctrl->slot_num_inc; | ||
164 | } | ||
165 | |||
166 | return 0; | ||
167 | |||
168 | error_name: | ||
169 | kfree(new_slot->hotplug_slot->name); | ||
170 | error_info: | ||
171 | kfree(new_slot->hotplug_slot->info); | ||
172 | error_hpslot: | ||
173 | kfree(new_slot->hotplug_slot); | ||
174 | error_slot: | ||
175 | kfree(new_slot); | ||
176 | error: | ||
177 | return result; | ||
178 | } | ||
179 | |||
180 | |||
181 | static int cleanup_slots (struct controller * ctrl) | ||
182 | { | ||
183 | struct slot *old_slot, *next_slot; | ||
184 | |||
185 | old_slot = ctrl->slot; | ||
186 | ctrl->slot = NULL; | ||
187 | |||
188 | while (old_slot) { | ||
189 | next_slot = old_slot->next; | ||
190 | pci_hp_deregister (old_slot->hotplug_slot); | ||
191 | kfree(old_slot->hotplug_slot->info); | ||
192 | kfree(old_slot->hotplug_slot->name); | ||
193 | kfree(old_slot->hotplug_slot); | ||
194 | kfree(old_slot); | ||
195 | old_slot = next_slot; | ||
196 | } | ||
197 | |||
198 | |||
199 | return(0); | ||
200 | } | ||
201 | |||
202 | static int get_ctlr_slot_config(struct controller *ctrl) | ||
203 | { | ||
204 | int num_ctlr_slots; /* Not needed; PCI Express has 1 slot per port*/ | ||
205 | int first_device_num; /* Not needed */ | ||
206 | int physical_slot_num; | ||
207 | u8 ctrlcap; | ||
208 | int rc; | ||
209 | |||
210 | rc = pcie_get_ctlr_slot_config(ctrl, &num_ctlr_slots, &first_device_num, &physical_slot_num, &ctrlcap); | ||
211 | if (rc) { | ||
212 | err("%s: get_ctlr_slot_config fail for b:d (%x:%x)\n", __FUNCTION__, ctrl->bus, ctrl->device); | ||
213 | return (-1); | ||
214 | } | ||
215 | |||
216 | ctrl->num_slots = num_ctlr_slots; /* PCI Express has 1 slot per port */ | ||
217 | ctrl->slot_device_offset = first_device_num; | ||
218 | ctrl->first_slot = physical_slot_num; | ||
219 | ctrl->ctrlcap = ctrlcap; | ||
220 | |||
221 | dbg("%s: bus(0x%x) num_slot(0x%x) 1st_dev(0x%x) psn(0x%x) ctrlcap(%x) for b:d (%x:%x)\n", | ||
222 | __FUNCTION__, ctrl->slot_bus, num_ctlr_slots, first_device_num, physical_slot_num, ctrlcap, | ||
223 | ctrl->bus, ctrl->device); | ||
224 | |||
225 | return (0); | ||
226 | } | ||
227 | |||
228 | |||
229 | /* | ||
230 | * set_attention_status - Turns the Amber LED for a slot on, off or blink | ||
231 | */ | ||
232 | static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status) | ||
233 | { | ||
234 | struct slot *slot = hotplug_slot->private; | ||
235 | |||
236 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
237 | |||
238 | hotplug_slot->info->attention_status = status; | ||
239 | |||
240 | if (ATTN_LED(slot->ctrl->ctrlcap)) | ||
241 | slot->hpc_ops->set_attention_status(slot, status); | ||
242 | |||
243 | return 0; | ||
244 | } | ||
245 | |||
246 | |||
247 | static int enable_slot(struct hotplug_slot *hotplug_slot) | ||
248 | { | ||
249 | struct slot *slot = hotplug_slot->private; | ||
250 | |||
251 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
252 | |||
253 | return pciehp_enable_slot(slot); | ||
254 | } | ||
255 | |||
256 | |||
257 | static int disable_slot(struct hotplug_slot *hotplug_slot) | ||
258 | { | ||
259 | struct slot *slot = hotplug_slot->private; | ||
260 | |||
261 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
262 | |||
263 | return pciehp_disable_slot(slot); | ||
264 | } | ||
265 | |||
266 | static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) | ||
267 | { | ||
268 | struct slot *slot = hotplug_slot->private; | ||
269 | int retval; | ||
270 | |||
271 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
272 | |||
273 | retval = slot->hpc_ops->get_power_status(slot, value); | ||
274 | if (retval < 0) | ||
275 | *value = hotplug_slot->info->power_status; | ||
276 | |||
277 | return 0; | ||
278 | } | ||
279 | |||
280 | static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value) | ||
281 | { | ||
282 | struct slot *slot = hotplug_slot->private; | ||
283 | int retval; | ||
284 | |||
285 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
286 | |||
287 | retval = slot->hpc_ops->get_attention_status(slot, value); | ||
288 | if (retval < 0) | ||
289 | *value = hotplug_slot->info->attention_status; | ||
290 | |||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) | ||
295 | { | ||
296 | struct slot *slot = hotplug_slot->private; | ||
297 | int retval; | ||
298 | |||
299 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
300 | |||
301 | retval = slot->hpc_ops->get_latch_status(slot, value); | ||
302 | if (retval < 0) | ||
303 | *value = hotplug_slot->info->latch_status; | ||
304 | |||
305 | return 0; | ||
306 | } | ||
307 | |||
308 | static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) | ||
309 | { | ||
310 | struct slot *slot = hotplug_slot->private; | ||
311 | int retval; | ||
312 | |||
313 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
314 | |||
315 | retval = slot->hpc_ops->get_adapter_status(slot, value); | ||
316 | if (retval < 0) | ||
317 | *value = hotplug_slot->info->adapter_status; | ||
318 | |||
319 | return 0; | ||
320 | } | ||
321 | |||
322 | static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) | ||
323 | { | ||
324 | struct slot *slot = hotplug_slot->private; | ||
325 | int retval; | ||
326 | |||
327 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
328 | |||
329 | retval = slot->hpc_ops->get_max_bus_speed(slot, value); | ||
330 | if (retval < 0) | ||
331 | *value = PCI_SPEED_UNKNOWN; | ||
332 | |||
333 | return 0; | ||
334 | } | ||
335 | |||
336 | static int get_cur_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) | ||
337 | { | ||
338 | struct slot *slot = hotplug_slot->private; | ||
339 | int retval; | ||
340 | |||
341 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
342 | |||
343 | retval = slot->hpc_ops->get_cur_bus_speed(slot, value); | ||
344 | if (retval < 0) | ||
345 | *value = PCI_SPEED_UNKNOWN; | ||
346 | |||
347 | return 0; | ||
348 | } | ||
349 | |||
350 | static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_id *id) | ||
351 | { | ||
352 | int rc; | ||
353 | struct controller *ctrl; | ||
354 | struct slot *t_slot; | ||
355 | int first_device_num = 0 ; /* first PCI device number supported by this PCIE */ | ||
356 | int num_ctlr_slots; /* number of slots supported by this HPC */ | ||
357 | u8 value; | ||
358 | struct pci_dev *pdev; | ||
359 | |||
360 | dbg("%s: Called by hp_drv\n", __FUNCTION__); | ||
361 | ctrl = kmalloc(sizeof(*ctrl), GFP_KERNEL); | ||
362 | if (!ctrl) { | ||
363 | err("%s : out of memory\n", __FUNCTION__); | ||
364 | goto err_out_none; | ||
365 | } | ||
366 | memset(ctrl, 0, sizeof(struct controller)); | ||
367 | |||
368 | dbg("%s: DRV_thread pid = %d\n", __FUNCTION__, current->pid); | ||
369 | |||
370 | pdev = dev->port; | ||
371 | |||
372 | rc = pcie_init(ctrl, dev, | ||
373 | (php_intr_callback_t) pciehp_handle_attention_button, | ||
374 | (php_intr_callback_t) pciehp_handle_switch_change, | ||
375 | (php_intr_callback_t) pciehp_handle_presence_change, | ||
376 | (php_intr_callback_t) pciehp_handle_power_fault); | ||
377 | if (rc) { | ||
378 | dbg("%s: controller initialization failed\n", PCIE_MODULE_NAME); | ||
379 | goto err_out_free_ctrl; | ||
380 | } | ||
381 | |||
382 | ctrl->pci_dev = pdev; | ||
383 | |||
384 | pci_set_drvdata(pdev, ctrl); | ||
385 | |||
386 | ctrl->pci_bus = kmalloc(sizeof(*ctrl->pci_bus), GFP_KERNEL); | ||
387 | if (!ctrl->pci_bus) { | ||
388 | err("%s: out of memory\n", __FUNCTION__); | ||
389 | rc = -ENOMEM; | ||
390 | goto err_out_unmap_mmio_region; | ||
391 | } | ||
392 | dbg("%s: ctrl->pci_bus %p\n", __FUNCTION__, ctrl->pci_bus); | ||
393 | memcpy (ctrl->pci_bus, pdev->bus, sizeof (*ctrl->pci_bus)); | ||
394 | ctrl->bus = pdev->bus->number; /* ctrl bus */ | ||
395 | ctrl->slot_bus = pdev->subordinate->number; /* bus controlled by this HPC */ | ||
396 | |||
397 | ctrl->device = PCI_SLOT(pdev->devfn); | ||
398 | ctrl->function = PCI_FUNC(pdev->devfn); | ||
399 | dbg("%s: ctrl bus=0x%x, device=%x, function=%x, irq=%x\n", __FUNCTION__, | ||
400 | ctrl->bus, ctrl->device, ctrl->function, pdev->irq); | ||
401 | |||
402 | /* | ||
403 | * Save configuration headers for this and subordinate PCI buses | ||
404 | */ | ||
405 | |||
406 | rc = get_ctlr_slot_config(ctrl); | ||
407 | if (rc) { | ||
408 | err(msg_initialization_err, rc); | ||
409 | goto err_out_free_ctrl_bus; | ||
410 | } | ||
411 | first_device_num = ctrl->slot_device_offset; | ||
412 | num_ctlr_slots = ctrl->num_slots; | ||
413 | |||
414 | /* Store PCI Config Space for all devices on this bus */ | ||
415 | dbg("%s: Before calling pciehp_save_config, ctrl->bus %x,ctrl->slot_bus %x\n", | ||
416 | __FUNCTION__,ctrl->bus, ctrl->slot_bus); | ||
417 | rc = pciehp_save_config(ctrl, ctrl->slot_bus, num_ctlr_slots, first_device_num); | ||
418 | if (rc) { | ||
419 | err("%s: unable to save PCI configuration data, error %d\n", __FUNCTION__, rc); | ||
420 | goto err_out_free_ctrl_bus; | ||
421 | } | ||
422 | |||
423 | /* Get IO, memory, and IRQ resources for new devices */ | ||
424 | rc = pciehprm_find_available_resources(ctrl); | ||
425 | ctrl->add_support = !rc; | ||
426 | |||
427 | if (rc) { | ||
428 | dbg("pciehprm_find_available_resources = %#x\n", rc); | ||
429 | err("unable to locate PCI configuration resources for hot plug add.\n"); | ||
430 | goto err_out_free_ctrl_bus; | ||
431 | } | ||
432 | |||
433 | /* Setup the slot information structures */ | ||
434 | rc = init_slots(ctrl); | ||
435 | if (rc) { | ||
436 | err(msg_initialization_err, 6); | ||
437 | goto err_out_free_ctrl_slot; | ||
438 | } | ||
439 | |||
440 | t_slot = pciehp_find_slot(ctrl, first_device_num); | ||
441 | dbg("%s: t_slot %p\n", __FUNCTION__, t_slot); | ||
442 | |||
443 | /* Finish setting up the hot plug ctrl device */ | ||
444 | ctrl->next_event = 0; | ||
445 | |||
446 | if (!pciehp_ctrl_list) { | ||
447 | pciehp_ctrl_list = ctrl; | ||
448 | ctrl->next = NULL; | ||
449 | } else { | ||
450 | ctrl->next = pciehp_ctrl_list; | ||
451 | pciehp_ctrl_list = ctrl; | ||
452 | } | ||
453 | |||
454 | /* Wait for exclusive access to hardware */ | ||
455 | down(&ctrl->crit_sect); | ||
456 | |||
457 | t_slot->hpc_ops->get_adapter_status(t_slot, &value); /* Check if slot is occupied */ | ||
458 | dbg("%s: adpater value %x\n", __FUNCTION__, value); | ||
459 | |||
460 | if ((POWER_CTRL(ctrl->ctrlcap)) && !value) { | ||
461 | rc = t_slot->hpc_ops->power_off_slot(t_slot); /* Power off slot if not occupied*/ | ||
462 | if (rc) { | ||
463 | /* Done with exclusive hardware access */ | ||
464 | up(&ctrl->crit_sect); | ||
465 | goto err_out_free_ctrl_slot; | ||
466 | } else | ||
467 | /* Wait for the command to complete */ | ||
468 | wait_for_ctrl_irq (ctrl); | ||
469 | } | ||
470 | |||
471 | /* Done with exclusive hardware access */ | ||
472 | up(&ctrl->crit_sect); | ||
473 | |||
474 | return 0; | ||
475 | |||
476 | err_out_free_ctrl_slot: | ||
477 | cleanup_slots(ctrl); | ||
478 | err_out_free_ctrl_bus: | ||
479 | kfree(ctrl->pci_bus); | ||
480 | err_out_unmap_mmio_region: | ||
481 | ctrl->hpc_ops->release_ctlr(ctrl); | ||
482 | err_out_free_ctrl: | ||
483 | kfree(ctrl); | ||
484 | err_out_none: | ||
485 | return -ENODEV; | ||
486 | } | ||
487 | |||
488 | |||
489 | static int pcie_start_thread(void) | ||
490 | { | ||
491 | int loop; | ||
492 | int retval = 0; | ||
493 | |||
494 | dbg("Initialize + Start the notification/polling mechanism \n"); | ||
495 | |||
496 | retval = pciehp_event_start_thread(); | ||
497 | if (retval) { | ||
498 | dbg("pciehp_event_start_thread() failed\n"); | ||
499 | return retval; | ||
500 | } | ||
501 | |||
502 | dbg("Initialize slot lists\n"); | ||
503 | /* One slot list for each bus in the system */ | ||
504 | for (loop = 0; loop < 256; loop++) { | ||
505 | pciehp_slot_list[loop] = NULL; | ||
506 | } | ||
507 | |||
508 | return retval; | ||
509 | } | ||
510 | |||
511 | static inline void __exit | ||
512 | free_pciehp_res(struct pci_resource *res) | ||
513 | { | ||
514 | struct pci_resource *tres; | ||
515 | |||
516 | while (res) { | ||
517 | tres = res; | ||
518 | res = res->next; | ||
519 | kfree(tres); | ||
520 | } | ||
521 | } | ||
522 | |||
523 | static void __exit unload_pciehpd(void) | ||
524 | { | ||
525 | struct pci_func *next; | ||
526 | struct pci_func *TempSlot; | ||
527 | int loop; | ||
528 | struct controller *ctrl; | ||
529 | struct controller *tctrl; | ||
530 | |||
531 | ctrl = pciehp_ctrl_list; | ||
532 | |||
533 | while (ctrl) { | ||
534 | cleanup_slots(ctrl); | ||
535 | |||
536 | free_pciehp_res(ctrl->io_head); | ||
537 | free_pciehp_res(ctrl->mem_head); | ||
538 | free_pciehp_res(ctrl->p_mem_head); | ||
539 | free_pciehp_res(ctrl->bus_head); | ||
540 | |||
541 | kfree (ctrl->pci_bus); | ||
542 | |||
543 | ctrl->hpc_ops->release_ctlr(ctrl); | ||
544 | |||
545 | tctrl = ctrl; | ||
546 | ctrl = ctrl->next; | ||
547 | |||
548 | kfree(tctrl); | ||
549 | } | ||
550 | |||
551 | for (loop = 0; loop < 256; loop++) { | ||
552 | next = pciehp_slot_list[loop]; | ||
553 | while (next != NULL) { | ||
554 | free_pciehp_res(next->io_head); | ||
555 | free_pciehp_res(next->mem_head); | ||
556 | free_pciehp_res(next->p_mem_head); | ||
557 | free_pciehp_res(next->bus_head); | ||
558 | |||
559 | TempSlot = next; | ||
560 | next = next->next; | ||
561 | kfree(TempSlot); | ||
562 | } | ||
563 | } | ||
564 | |||
565 | /* Stop the notification mechanism */ | ||
566 | pciehp_event_stop_thread(); | ||
567 | |||
568 | } | ||
569 | |||
570 | int hpdriver_context = 0; | ||
571 | |||
572 | static void pciehp_remove (struct pcie_device *device) | ||
573 | { | ||
574 | printk("%s ENTRY\n", __FUNCTION__); | ||
575 | printk("%s -> Call free_irq for irq = %d\n", | ||
576 | __FUNCTION__, device->irq); | ||
577 | free_irq(device->irq, &hpdriver_context); | ||
578 | } | ||
579 | |||
580 | #ifdef CONFIG_PM | ||
581 | static int pciehp_suspend (struct pcie_device *dev, u32 state) | ||
582 | { | ||
583 | printk("%s ENTRY\n", __FUNCTION__); | ||
584 | return 0; | ||
585 | } | ||
586 | |||
587 | static int pciehp_resume (struct pcie_device *dev) | ||
588 | { | ||
589 | printk("%s ENTRY\n", __FUNCTION__); | ||
590 | return 0; | ||
591 | } | ||
592 | #endif | ||
593 | |||
594 | static struct pcie_port_service_id port_pci_ids[] = { { | ||
595 | .vendor = PCI_ANY_ID, | ||
596 | .device = PCI_ANY_ID, | ||
597 | .port_type = PCIE_RC_PORT, | ||
598 | .service_type = PCIE_PORT_SERVICE_HP, | ||
599 | .driver_data = 0, | ||
600 | }, { /* end: all zeroes */ } | ||
601 | }; | ||
602 | static const char device_name[] = "hpdriver"; | ||
603 | |||
604 | static struct pcie_port_service_driver hpdriver_portdrv = { | ||
605 | .name = (char *)device_name, | ||
606 | .id_table = &port_pci_ids[0], | ||
607 | |||
608 | .probe = pciehp_probe, | ||
609 | .remove = pciehp_remove, | ||
610 | |||
611 | #ifdef CONFIG_PM | ||
612 | .suspend = pciehp_suspend, | ||
613 | .resume = pciehp_resume, | ||
614 | #endif /* PM */ | ||
615 | }; | ||
616 | |||
617 | static int __init pcied_init(void) | ||
618 | { | ||
619 | int retval = 0; | ||
620 | |||
621 | #ifdef CONFIG_HOTPLUG_PCI_PCIE_POLL_EVENT_MODE | ||
622 | pciehp_poll_mode = 1; | ||
623 | #endif | ||
624 | |||
625 | retval = pcie_start_thread(); | ||
626 | if (retval) | ||
627 | goto error_hpc_init; | ||
628 | |||
629 | retval = pciehprm_init(PCI); | ||
630 | if (!retval) { | ||
631 | retval = pcie_port_service_register(&hpdriver_portdrv); | ||
632 | dbg("pcie_port_service_register = %d\n", retval); | ||
633 | info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); | ||
634 | if (retval) | ||
635 | dbg("%s: Failure to register service\n", __FUNCTION__); | ||
636 | } | ||
637 | |||
638 | error_hpc_init: | ||
639 | if (retval) { | ||
640 | pciehprm_cleanup(); | ||
641 | pciehp_event_stop_thread(); | ||
642 | } else | ||
643 | pciehprm_print_pirt(); | ||
644 | |||
645 | return retval; | ||
646 | } | ||
647 | |||
648 | static void __exit pcied_cleanup(void) | ||
649 | { | ||
650 | dbg("unload_pciehpd()\n"); | ||
651 | unload_pciehpd(); | ||
652 | |||
653 | pciehprm_cleanup(); | ||
654 | |||
655 | dbg("pcie_port_service_unregister\n"); | ||
656 | pcie_port_service_unregister(&hpdriver_portdrv); | ||
657 | |||
658 | info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n"); | ||
659 | } | ||
660 | |||
661 | module_init(pcied_init); | ||
662 | module_exit(pcied_cleanup); | ||
diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c new file mode 100644 index 000000000000..0dbcf04aa35e --- /dev/null +++ b/drivers/pci/hotplug/pciehp_ctrl.c | |||
@@ -0,0 +1,2706 @@ | |||
1 | /* | ||
2 | * PCI Express Hot Plug Controller Driver | ||
3 | * | ||
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | ||
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * Copyright (C) 2001 IBM Corp. | ||
7 | * Copyright (C) 2003-2004 Intel Corporation | ||
8 | * | ||
9 | * All rights reserved. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or (at | ||
14 | * your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
19 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
20 | * details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
25 | * | ||
26 | * Send feedback to <greg@kroah.com>, <dely.l.sy@intel.com> | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | #include <linux/config.h> | ||
31 | #include <linux/module.h> | ||
32 | #include <linux/kernel.h> | ||
33 | #include <linux/types.h> | ||
34 | #include <linux/slab.h> | ||
35 | #include <linux/workqueue.h> | ||
36 | #include <linux/interrupt.h> | ||
37 | #include <linux/delay.h> | ||
38 | #include <linux/wait.h> | ||
39 | #include <linux/smp_lock.h> | ||
40 | #include <linux/pci.h> | ||
41 | #include "../pci.h" | ||
42 | #include "pciehp.h" | ||
43 | #include "pciehprm.h" | ||
44 | |||
45 | static u32 configure_new_device(struct controller *ctrl, struct pci_func *func, | ||
46 | u8 behind_bridge, struct resource_lists *resources, u8 bridge_bus, u8 bridge_dev); | ||
47 | static int configure_new_function( struct controller *ctrl, struct pci_func *func, | ||
48 | u8 behind_bridge, struct resource_lists *resources, u8 bridge_bus, u8 bridge_dev); | ||
49 | static void interrupt_event_handler(struct controller *ctrl); | ||
50 | |||
51 | static struct semaphore event_semaphore; /* mutex for process loop (up if something to process) */ | ||
52 | static struct semaphore event_exit; /* guard ensure thread has exited before calling it quits */ | ||
53 | static int event_finished; | ||
54 | static unsigned long pushbutton_pending; /* = 0 */ | ||
55 | static unsigned long surprise_rm_pending; /* = 0 */ | ||
56 | |||
57 | u8 pciehp_handle_attention_button(u8 hp_slot, void *inst_id) | ||
58 | { | ||
59 | struct controller *ctrl = (struct controller *) inst_id; | ||
60 | struct slot *p_slot; | ||
61 | u8 rc = 0; | ||
62 | u8 getstatus; | ||
63 | struct pci_func *func; | ||
64 | struct event_info *taskInfo; | ||
65 | |||
66 | /* Attention Button Change */ | ||
67 | dbg("pciehp: Attention button interrupt received.\n"); | ||
68 | |||
69 | func = pciehp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0); | ||
70 | |||
71 | /* This is the structure that tells the worker thread what to do */ | ||
72 | taskInfo = &(ctrl->event_queue[ctrl->next_event]); | ||
73 | p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | ||
74 | |||
75 | p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save)); | ||
76 | p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); | ||
77 | |||
78 | ctrl->next_event = (ctrl->next_event + 1) % 10; | ||
79 | taskInfo->hp_slot = hp_slot; | ||
80 | |||
81 | rc++; | ||
82 | |||
83 | /* | ||
84 | * Button pressed - See if need to TAKE ACTION!!! | ||
85 | */ | ||
86 | info("Button pressed on Slot(%d)\n", ctrl->first_slot + hp_slot); | ||
87 | taskInfo->event_type = INT_BUTTON_PRESS; | ||
88 | |||
89 | if ((p_slot->state == BLINKINGON_STATE) | ||
90 | || (p_slot->state == BLINKINGOFF_STATE)) { | ||
91 | /* Cancel if we are still blinking; this means that we press the | ||
92 | * attention again before the 5 sec. limit expires to cancel hot-add | ||
93 | * or hot-remove | ||
94 | */ | ||
95 | taskInfo->event_type = INT_BUTTON_CANCEL; | ||
96 | info("Button cancel on Slot(%d)\n", ctrl->first_slot + hp_slot); | ||
97 | } else if ((p_slot->state == POWERON_STATE) | ||
98 | || (p_slot->state == POWEROFF_STATE)) { | ||
99 | /* Ignore if the slot is on power-on or power-off state; this | ||
100 | * means that the previous attention button action to hot-add or | ||
101 | * hot-remove is undergoing | ||
102 | */ | ||
103 | taskInfo->event_type = INT_BUTTON_IGNORE; | ||
104 | info("Button ignore on Slot(%d)\n", ctrl->first_slot + hp_slot); | ||
105 | } | ||
106 | |||
107 | if (rc) | ||
108 | up(&event_semaphore); /* signal event thread that new event is posted */ | ||
109 | |||
110 | return 0; | ||
111 | |||
112 | } | ||
113 | |||
114 | u8 pciehp_handle_switch_change(u8 hp_slot, void *inst_id) | ||
115 | { | ||
116 | struct controller *ctrl = (struct controller *) inst_id; | ||
117 | struct slot *p_slot; | ||
118 | u8 rc = 0; | ||
119 | u8 getstatus; | ||
120 | struct pci_func *func; | ||
121 | struct event_info *taskInfo; | ||
122 | |||
123 | /* Switch Change */ | ||
124 | dbg("pciehp: Switch interrupt received.\n"); | ||
125 | |||
126 | func = pciehp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0); | ||
127 | |||
128 | /* This is the structure that tells the worker thread | ||
129 | * what to do | ||
130 | */ | ||
131 | taskInfo = &(ctrl->event_queue[ctrl->next_event]); | ||
132 | ctrl->next_event = (ctrl->next_event + 1) % 10; | ||
133 | taskInfo->hp_slot = hp_slot; | ||
134 | |||
135 | rc++; | ||
136 | p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | ||
137 | p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save)); | ||
138 | p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); | ||
139 | |||
140 | if (getstatus) { | ||
141 | /* | ||
142 | * Switch opened | ||
143 | */ | ||
144 | info("Latch open on Slot(%d)\n", ctrl->first_slot + hp_slot); | ||
145 | func->switch_save = 0; | ||
146 | taskInfo->event_type = INT_SWITCH_OPEN; | ||
147 | } else { | ||
148 | /* | ||
149 | * Switch closed | ||
150 | */ | ||
151 | info("Latch close on Slot(%d)\n", ctrl->first_slot + hp_slot); | ||
152 | func->switch_save = 0x10; | ||
153 | taskInfo->event_type = INT_SWITCH_CLOSE; | ||
154 | } | ||
155 | |||
156 | if (rc) | ||
157 | up(&event_semaphore); /* signal event thread that new event is posted */ | ||
158 | |||
159 | return rc; | ||
160 | } | ||
161 | |||
162 | u8 pciehp_handle_presence_change(u8 hp_slot, void *inst_id) | ||
163 | { | ||
164 | struct controller *ctrl = (struct controller *) inst_id; | ||
165 | struct slot *p_slot; | ||
166 | u8 rc = 0; | ||
167 | struct pci_func *func; | ||
168 | struct event_info *taskInfo; | ||
169 | |||
170 | /* Presence Change */ | ||
171 | dbg("pciehp: Presence/Notify input change.\n"); | ||
172 | |||
173 | func = pciehp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0); | ||
174 | |||
175 | /* This is the structure that tells the worker thread | ||
176 | * what to do | ||
177 | */ | ||
178 | taskInfo = &(ctrl->event_queue[ctrl->next_event]); | ||
179 | ctrl->next_event = (ctrl->next_event + 1) % 10; | ||
180 | taskInfo->hp_slot = hp_slot; | ||
181 | |||
182 | rc++; | ||
183 | p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | ||
184 | |||
185 | /* Switch is open, assume a presence change | ||
186 | * Save the presence state | ||
187 | */ | ||
188 | p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save)); | ||
189 | if (func->presence_save) { | ||
190 | /* | ||
191 | * Card Present | ||
192 | */ | ||
193 | info("Card present on Slot(%d)\n", ctrl->first_slot + hp_slot); | ||
194 | taskInfo->event_type = INT_PRESENCE_ON; | ||
195 | } else { | ||
196 | /* | ||
197 | * Not Present | ||
198 | */ | ||
199 | info("Card not present on Slot(%d)\n", ctrl->first_slot + hp_slot); | ||
200 | taskInfo->event_type = INT_PRESENCE_OFF; | ||
201 | } | ||
202 | |||
203 | if (rc) | ||
204 | up(&event_semaphore); /* signal event thread that new event is posted */ | ||
205 | |||
206 | return rc; | ||
207 | } | ||
208 | |||
209 | u8 pciehp_handle_power_fault(u8 hp_slot, void *inst_id) | ||
210 | { | ||
211 | struct controller *ctrl = (struct controller *) inst_id; | ||
212 | struct slot *p_slot; | ||
213 | u8 rc = 0; | ||
214 | struct pci_func *func; | ||
215 | struct event_info *taskInfo; | ||
216 | |||
217 | /* power fault */ | ||
218 | dbg("pciehp: Power fault interrupt received.\n"); | ||
219 | |||
220 | func = pciehp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0); | ||
221 | |||
222 | /* this is the structure that tells the worker thread | ||
223 | * what to do | ||
224 | */ | ||
225 | taskInfo = &(ctrl->event_queue[ctrl->next_event]); | ||
226 | ctrl->next_event = (ctrl->next_event + 1) % 10; | ||
227 | taskInfo->hp_slot = hp_slot; | ||
228 | |||
229 | rc++; | ||
230 | p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | ||
231 | |||
232 | if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) { | ||
233 | /* | ||
234 | * power fault Cleared | ||
235 | */ | ||
236 | info("Power fault cleared on Slot(%d)\n", ctrl->first_slot + hp_slot); | ||
237 | func->status = 0x00; | ||
238 | taskInfo->event_type = INT_POWER_FAULT_CLEAR; | ||
239 | } else { | ||
240 | /* | ||
241 | * power fault | ||
242 | */ | ||
243 | info("Power fault on Slot(%d)\n", ctrl->first_slot + hp_slot); | ||
244 | taskInfo->event_type = INT_POWER_FAULT; | ||
245 | /* set power fault status for this board */ | ||
246 | func->status = 0xFF; | ||
247 | info("power fault bit %x set\n", hp_slot); | ||
248 | } | ||
249 | if (rc) | ||
250 | up(&event_semaphore); /* signal event thread that new event is posted */ | ||
251 | |||
252 | return rc; | ||
253 | } | ||
254 | |||
255 | |||
256 | /** | ||
257 | * sort_by_size: sort nodes by their length, smallest first. | ||
258 | * | ||
259 | * @head: list to sort | ||
260 | */ | ||
261 | static int sort_by_size(struct pci_resource **head) | ||
262 | { | ||
263 | struct pci_resource *current_res; | ||
264 | struct pci_resource *next_res; | ||
265 | int out_of_order = 1; | ||
266 | |||
267 | if (!(*head)) | ||
268 | return 1; | ||
269 | |||
270 | if (!((*head)->next)) | ||
271 | return 0; | ||
272 | |||
273 | while (out_of_order) { | ||
274 | out_of_order = 0; | ||
275 | |||
276 | /* Special case for swapping list head */ | ||
277 | if (((*head)->next) && | ||
278 | ((*head)->length > (*head)->next->length)) { | ||
279 | out_of_order++; | ||
280 | current_res = *head; | ||
281 | *head = (*head)->next; | ||
282 | current_res->next = (*head)->next; | ||
283 | (*head)->next = current_res; | ||
284 | } | ||
285 | |||
286 | current_res = *head; | ||
287 | |||
288 | while (current_res->next && current_res->next->next) { | ||
289 | if (current_res->next->length > current_res->next->next->length) { | ||
290 | out_of_order++; | ||
291 | next_res = current_res->next; | ||
292 | current_res->next = current_res->next->next; | ||
293 | current_res = current_res->next; | ||
294 | next_res->next = current_res->next; | ||
295 | current_res->next = next_res; | ||
296 | } else | ||
297 | current_res = current_res->next; | ||
298 | } | ||
299 | } /* End of out_of_order loop */ | ||
300 | |||
301 | return 0; | ||
302 | } | ||
303 | |||
304 | |||
305 | /* | ||
306 | * sort_by_max_size | ||
307 | * | ||
308 | * Sorts nodes on the list by their length. | ||
309 | * Largest first. | ||
310 | * | ||
311 | */ | ||
312 | static int sort_by_max_size(struct pci_resource **head) | ||
313 | { | ||
314 | struct pci_resource *current_res; | ||
315 | struct pci_resource *next_res; | ||
316 | int out_of_order = 1; | ||
317 | |||
318 | if (!(*head)) | ||
319 | return 1; | ||
320 | |||
321 | if (!((*head)->next)) | ||
322 | return 0; | ||
323 | |||
324 | while (out_of_order) { | ||
325 | out_of_order = 0; | ||
326 | |||
327 | /* Special case for swapping list head */ | ||
328 | if (((*head)->next) && | ||
329 | ((*head)->length < (*head)->next->length)) { | ||
330 | out_of_order++; | ||
331 | current_res = *head; | ||
332 | *head = (*head)->next; | ||
333 | current_res->next = (*head)->next; | ||
334 | (*head)->next = current_res; | ||
335 | } | ||
336 | |||
337 | current_res = *head; | ||
338 | |||
339 | while (current_res->next && current_res->next->next) { | ||
340 | if (current_res->next->length < current_res->next->next->length) { | ||
341 | out_of_order++; | ||
342 | next_res = current_res->next; | ||
343 | current_res->next = current_res->next->next; | ||
344 | current_res = current_res->next; | ||
345 | next_res->next = current_res->next; | ||
346 | current_res->next = next_res; | ||
347 | } else | ||
348 | current_res = current_res->next; | ||
349 | } | ||
350 | } /* End of out_of_order loop */ | ||
351 | |||
352 | return 0; | ||
353 | } | ||
354 | |||
355 | |||
356 | /** | ||
357 | * do_pre_bridge_resource_split: return one unused resource node | ||
358 | * @head: list to scan | ||
359 | * | ||
360 | */ | ||
361 | static struct pci_resource * | ||
362 | do_pre_bridge_resource_split(struct pci_resource **head, | ||
363 | struct pci_resource **orig_head, u32 alignment) | ||
364 | { | ||
365 | struct pci_resource *prevnode = NULL; | ||
366 | struct pci_resource *node; | ||
367 | struct pci_resource *split_node; | ||
368 | u32 rc; | ||
369 | u32 temp_dword; | ||
370 | dbg("do_pre_bridge_resource_split\n"); | ||
371 | |||
372 | if (!(*head) || !(*orig_head)) | ||
373 | return NULL; | ||
374 | |||
375 | rc = pciehp_resource_sort_and_combine(head); | ||
376 | |||
377 | if (rc) | ||
378 | return NULL; | ||
379 | |||
380 | if ((*head)->base != (*orig_head)->base) | ||
381 | return NULL; | ||
382 | |||
383 | if ((*head)->length == (*orig_head)->length) | ||
384 | return NULL; | ||
385 | |||
386 | |||
387 | /* If we got here, there the bridge requires some of the resource, but | ||
388 | * we may be able to split some off of the front | ||
389 | */ | ||
390 | node = *head; | ||
391 | |||
392 | if (node->length & (alignment -1)) { | ||
393 | /* this one isn't an aligned length, so we'll make a new entry | ||
394 | * and split it up. | ||
395 | */ | ||
396 | split_node = kmalloc(sizeof(struct pci_resource), GFP_KERNEL); | ||
397 | |||
398 | if (!split_node) | ||
399 | return NULL; | ||
400 | |||
401 | temp_dword = (node->length | (alignment-1)) + 1 - alignment; | ||
402 | |||
403 | split_node->base = node->base; | ||
404 | split_node->length = temp_dword; | ||
405 | |||
406 | node->length -= temp_dword; | ||
407 | node->base += split_node->length; | ||
408 | |||
409 | /* Put it in the list */ | ||
410 | *head = split_node; | ||
411 | split_node->next = node; | ||
412 | } | ||
413 | |||
414 | if (node->length < alignment) | ||
415 | return NULL; | ||
416 | |||
417 | /* Now unlink it */ | ||
418 | if (*head == node) { | ||
419 | *head = node->next; | ||
420 | } else { | ||
421 | prevnode = *head; | ||
422 | while (prevnode->next != node) | ||
423 | prevnode = prevnode->next; | ||
424 | |||
425 | prevnode->next = node->next; | ||
426 | } | ||
427 | node->next = NULL; | ||
428 | |||
429 | return node; | ||
430 | } | ||
431 | |||
432 | |||
433 | /** | ||
434 | * do_bridge_resource_split: return one unused resource node | ||
435 | * @head: list to scan | ||
436 | * | ||
437 | */ | ||
438 | static struct pci_resource * | ||
439 | do_bridge_resource_split(struct pci_resource **head, u32 alignment) | ||
440 | { | ||
441 | struct pci_resource *prevnode = NULL; | ||
442 | struct pci_resource *node; | ||
443 | u32 rc; | ||
444 | u32 temp_dword; | ||
445 | |||
446 | if (!(*head)) | ||
447 | return NULL; | ||
448 | |||
449 | rc = pciehp_resource_sort_and_combine(head); | ||
450 | |||
451 | if (rc) | ||
452 | return NULL; | ||
453 | |||
454 | node = *head; | ||
455 | |||
456 | while (node->next) { | ||
457 | prevnode = node; | ||
458 | node = node->next; | ||
459 | kfree(prevnode); | ||
460 | } | ||
461 | |||
462 | if (node->length < alignment) { | ||
463 | kfree(node); | ||
464 | return NULL; | ||
465 | } | ||
466 | |||
467 | if (node->base & (alignment - 1)) { | ||
468 | /* Short circuit if adjusted size is too small */ | ||
469 | temp_dword = (node->base | (alignment-1)) + 1; | ||
470 | if ((node->length - (temp_dword - node->base)) < alignment) { | ||
471 | kfree(node); | ||
472 | return NULL; | ||
473 | } | ||
474 | |||
475 | node->length -= (temp_dword - node->base); | ||
476 | node->base = temp_dword; | ||
477 | } | ||
478 | |||
479 | if (node->length & (alignment - 1)) { | ||
480 | /* There's stuff in use after this node */ | ||
481 | kfree(node); | ||
482 | return NULL; | ||
483 | } | ||
484 | |||
485 | return node; | ||
486 | } | ||
487 | |||
488 | |||
489 | /* | ||
490 | * get_io_resource | ||
491 | * | ||
492 | * this function sorts the resource list by size and then | ||
493 | * returns the first node of "size" length that is not in the | ||
494 | * ISA aliasing window. If it finds a node larger than "size" | ||
495 | * it will split it up. | ||
496 | * | ||
497 | * size must be a power of two. | ||
498 | */ | ||
499 | static struct pci_resource *get_io_resource(struct pci_resource **head, u32 size) | ||
500 | { | ||
501 | struct pci_resource *prevnode; | ||
502 | struct pci_resource *node; | ||
503 | struct pci_resource *split_node = NULL; | ||
504 | u32 temp_dword; | ||
505 | |||
506 | if (!(*head)) | ||
507 | return NULL; | ||
508 | |||
509 | if ( pciehp_resource_sort_and_combine(head) ) | ||
510 | return NULL; | ||
511 | |||
512 | if ( sort_by_size(head) ) | ||
513 | return NULL; | ||
514 | |||
515 | for (node = *head; node; node = node->next) { | ||
516 | if (node->length < size) | ||
517 | continue; | ||
518 | |||
519 | if (node->base & (size - 1)) { | ||
520 | /* this one isn't base aligned properly | ||
521 | so we'll make a new entry and split it up */ | ||
522 | temp_dword = (node->base | (size-1)) + 1; | ||
523 | |||
524 | /*/ Short circuit if adjusted size is too small */ | ||
525 | if ((node->length - (temp_dword - node->base)) < size) | ||
526 | continue; | ||
527 | |||
528 | split_node = kmalloc(sizeof(struct pci_resource), | ||
529 | GFP_KERNEL); | ||
530 | |||
531 | if (!split_node) | ||
532 | return NULL; | ||
533 | |||
534 | split_node->base = node->base; | ||
535 | split_node->length = temp_dword - node->base; | ||
536 | node->base = temp_dword; | ||
537 | node->length -= split_node->length; | ||
538 | |||
539 | /* Put it in the list */ | ||
540 | split_node->next = node->next; | ||
541 | node->next = split_node; | ||
542 | } /* End of non-aligned base */ | ||
543 | |||
544 | /* Don't need to check if too small since we already did */ | ||
545 | if (node->length > size) { | ||
546 | /* this one is longer than we need | ||
547 | so we'll make a new entry and split it up */ | ||
548 | split_node = kmalloc(sizeof(struct pci_resource), | ||
549 | GFP_KERNEL); | ||
550 | |||
551 | if (!split_node) | ||
552 | return NULL; | ||
553 | |||
554 | split_node->base = node->base + size; | ||
555 | split_node->length = node->length - size; | ||
556 | node->length = size; | ||
557 | |||
558 | /* Put it in the list */ | ||
559 | split_node->next = node->next; | ||
560 | node->next = split_node; | ||
561 | } /* End of too big on top end */ | ||
562 | |||
563 | /* For IO make sure it's not in the ISA aliasing space */ | ||
564 | if (node->base & 0x300L) | ||
565 | continue; | ||
566 | |||
567 | /* If we got here, then it is the right size | ||
568 | Now take it out of the list */ | ||
569 | if (*head == node) { | ||
570 | *head = node->next; | ||
571 | } else { | ||
572 | prevnode = *head; | ||
573 | while (prevnode->next != node) | ||
574 | prevnode = prevnode->next; | ||
575 | |||
576 | prevnode->next = node->next; | ||
577 | } | ||
578 | node->next = NULL; | ||
579 | /* Stop looping */ | ||
580 | break; | ||
581 | } | ||
582 | |||
583 | return node; | ||
584 | } | ||
585 | |||
586 | |||
587 | /* | ||
588 | * get_max_resource | ||
589 | * | ||
590 | * Gets the largest node that is at least "size" big from the | ||
591 | * list pointed to by head. It aligns the node on top and bottom | ||
592 | * to "size" alignment before returning it. | ||
593 | * J.I. modified to put max size limits of; 64M->32M->16M->8M->4M->1M | ||
594 | * This is needed to avoid allocating entire ACPI _CRS res to one child bridge/slot. | ||
595 | */ | ||
596 | static struct pci_resource *get_max_resource(struct pci_resource **head, u32 size) | ||
597 | { | ||
598 | struct pci_resource *max; | ||
599 | struct pci_resource *temp; | ||
600 | struct pci_resource *split_node; | ||
601 | u32 temp_dword; | ||
602 | u32 max_size[] = { 0x4000000, 0x2000000, 0x1000000, 0x0800000, 0x0400000, 0x0200000, 0x0100000, 0x00 }; | ||
603 | int i; | ||
604 | |||
605 | if (!(*head)) | ||
606 | return NULL; | ||
607 | |||
608 | if (pciehp_resource_sort_and_combine(head)) | ||
609 | return NULL; | ||
610 | |||
611 | if (sort_by_max_size(head)) | ||
612 | return NULL; | ||
613 | |||
614 | for (max = *head;max; max = max->next) { | ||
615 | |||
616 | /* If not big enough we could probably just bail, | ||
617 | instead we'll continue to the next. */ | ||
618 | if (max->length < size) | ||
619 | continue; | ||
620 | |||
621 | if (max->base & (size - 1)) { | ||
622 | /* this one isn't base aligned properly | ||
623 | so we'll make a new entry and split it up */ | ||
624 | temp_dword = (max->base | (size-1)) + 1; | ||
625 | |||
626 | /* Short circuit if adjusted size is too small */ | ||
627 | if ((max->length - (temp_dword - max->base)) < size) | ||
628 | continue; | ||
629 | |||
630 | split_node = kmalloc(sizeof(struct pci_resource), | ||
631 | GFP_KERNEL); | ||
632 | |||
633 | if (!split_node) | ||
634 | return NULL; | ||
635 | |||
636 | split_node->base = max->base; | ||
637 | split_node->length = temp_dword - max->base; | ||
638 | max->base = temp_dword; | ||
639 | max->length -= split_node->length; | ||
640 | |||
641 | /* Put it next in the list */ | ||
642 | split_node->next = max->next; | ||
643 | max->next = split_node; | ||
644 | } | ||
645 | |||
646 | if ((max->base + max->length) & (size - 1)) { | ||
647 | /* this one isn't end aligned properly at the top | ||
648 | so we'll make a new entry and split it up */ | ||
649 | split_node = kmalloc(sizeof(struct pci_resource), | ||
650 | GFP_KERNEL); | ||
651 | |||
652 | if (!split_node) | ||
653 | return NULL; | ||
654 | temp_dword = ((max->base + max->length) & ~(size - 1)); | ||
655 | split_node->base = temp_dword; | ||
656 | split_node->length = max->length + max->base | ||
657 | - split_node->base; | ||
658 | max->length -= split_node->length; | ||
659 | |||
660 | /* Put it in the list */ | ||
661 | split_node->next = max->next; | ||
662 | max->next = split_node; | ||
663 | } | ||
664 | |||
665 | /* Make sure it didn't shrink too much when we aligned it */ | ||
666 | if (max->length < size) | ||
667 | continue; | ||
668 | |||
669 | for ( i = 0; max_size[i] > size; i++) { | ||
670 | if (max->length > max_size[i]) { | ||
671 | split_node = kmalloc(sizeof(struct pci_resource), | ||
672 | GFP_KERNEL); | ||
673 | if (!split_node) | ||
674 | break; /* return NULL; */ | ||
675 | split_node->base = max->base + max_size[i]; | ||
676 | split_node->length = max->length - max_size[i]; | ||
677 | max->length = max_size[i]; | ||
678 | /* Put it next in the list */ | ||
679 | split_node->next = max->next; | ||
680 | max->next = split_node; | ||
681 | break; | ||
682 | } | ||
683 | } | ||
684 | |||
685 | /* Now take it out of the list */ | ||
686 | temp = (struct pci_resource*) *head; | ||
687 | if (temp == max) { | ||
688 | *head = max->next; | ||
689 | } else { | ||
690 | while (temp && temp->next != max) { | ||
691 | temp = temp->next; | ||
692 | } | ||
693 | |||
694 | temp->next = max->next; | ||
695 | } | ||
696 | |||
697 | max->next = NULL; | ||
698 | return max; | ||
699 | } | ||
700 | |||
701 | /* If we get here, we couldn't find one */ | ||
702 | return NULL; | ||
703 | } | ||
704 | |||
705 | |||
706 | /* | ||
707 | * get_resource | ||
708 | * | ||
709 | * this function sorts the resource list by size and then | ||
710 | * returns the first node of "size" length. If it finds a node | ||
711 | * larger than "size" it will split it up. | ||
712 | * | ||
713 | * size must be a power of two. | ||
714 | */ | ||
715 | static struct pci_resource *get_resource(struct pci_resource **head, u32 size) | ||
716 | { | ||
717 | struct pci_resource *prevnode; | ||
718 | struct pci_resource *node; | ||
719 | struct pci_resource *split_node; | ||
720 | u32 temp_dword; | ||
721 | |||
722 | if (!(*head)) | ||
723 | return NULL; | ||
724 | |||
725 | if ( pciehp_resource_sort_and_combine(head) ) | ||
726 | return NULL; | ||
727 | |||
728 | if ( sort_by_size(head) ) | ||
729 | return NULL; | ||
730 | |||
731 | for (node = *head; node; node = node->next) { | ||
732 | dbg("%s: req_size =0x%x node=%p, base=0x%x, length=0x%x\n", | ||
733 | __FUNCTION__, size, node, node->base, node->length); | ||
734 | if (node->length < size) | ||
735 | continue; | ||
736 | |||
737 | if (node->base & (size - 1)) { | ||
738 | dbg("%s: not aligned\n", __FUNCTION__); | ||
739 | /* this one isn't base aligned properly | ||
740 | so we'll make a new entry and split it up */ | ||
741 | temp_dword = (node->base | (size-1)) + 1; | ||
742 | |||
743 | /* Short circuit if adjusted size is too small */ | ||
744 | if ((node->length - (temp_dword - node->base)) < size) | ||
745 | continue; | ||
746 | |||
747 | split_node = kmalloc(sizeof(struct pci_resource), | ||
748 | GFP_KERNEL); | ||
749 | |||
750 | if (!split_node) | ||
751 | return NULL; | ||
752 | |||
753 | split_node->base = node->base; | ||
754 | split_node->length = temp_dword - node->base; | ||
755 | node->base = temp_dword; | ||
756 | node->length -= split_node->length; | ||
757 | |||
758 | /* Put it in the list */ | ||
759 | split_node->next = node->next; | ||
760 | node->next = split_node; | ||
761 | } /* End of non-aligned base */ | ||
762 | |||
763 | /* Don't need to check if too small since we already did */ | ||
764 | if (node->length > size) { | ||
765 | dbg("%s: too big\n", __FUNCTION__); | ||
766 | /* this one is longer than we need | ||
767 | so we'll make a new entry and split it up */ | ||
768 | split_node = kmalloc(sizeof(struct pci_resource), | ||
769 | GFP_KERNEL); | ||
770 | |||
771 | if (!split_node) | ||
772 | return NULL; | ||
773 | |||
774 | split_node->base = node->base + size; | ||
775 | split_node->length = node->length - size; | ||
776 | node->length = size; | ||
777 | |||
778 | /* Put it in the list */ | ||
779 | split_node->next = node->next; | ||
780 | node->next = split_node; | ||
781 | } /* End of too big on top end */ | ||
782 | |||
783 | dbg("%s: got one!!!\n", __FUNCTION__); | ||
784 | /* If we got here, then it is the right size | ||
785 | Now take it out of the list */ | ||
786 | if (*head == node) { | ||
787 | *head = node->next; | ||
788 | } else { | ||
789 | prevnode = *head; | ||
790 | while (prevnode->next != node) | ||
791 | prevnode = prevnode->next; | ||
792 | |||
793 | prevnode->next = node->next; | ||
794 | } | ||
795 | node->next = NULL; | ||
796 | /* Stop looping */ | ||
797 | break; | ||
798 | } | ||
799 | return node; | ||
800 | } | ||
801 | |||
802 | |||
803 | /* | ||
804 | * pciehp_resource_sort_and_combine | ||
805 | * | ||
806 | * Sorts all of the nodes in the list in ascending order by | ||
807 | * their base addresses. Also does garbage collection by | ||
808 | * combining adjacent nodes. | ||
809 | * | ||
810 | * returns 0 if success | ||
811 | */ | ||
812 | int pciehp_resource_sort_and_combine(struct pci_resource **head) | ||
813 | { | ||
814 | struct pci_resource *node1; | ||
815 | struct pci_resource *node2; | ||
816 | int out_of_order = 1; | ||
817 | |||
818 | dbg("%s: head = %p, *head = %p\n", __FUNCTION__, head, *head); | ||
819 | |||
820 | if (!(*head)) | ||
821 | return 1; | ||
822 | |||
823 | dbg("*head->next = %p\n",(*head)->next); | ||
824 | |||
825 | if (!(*head)->next) | ||
826 | return 0; /* only one item on the list, already sorted! */ | ||
827 | |||
828 | dbg("*head->base = 0x%x\n",(*head)->base); | ||
829 | dbg("*head->next->base = 0x%x\n",(*head)->next->base); | ||
830 | while (out_of_order) { | ||
831 | out_of_order = 0; | ||
832 | |||
833 | /* Special case for swapping list head */ | ||
834 | if (((*head)->next) && | ||
835 | ((*head)->base > (*head)->next->base)) { | ||
836 | node1 = *head; | ||
837 | (*head) = (*head)->next; | ||
838 | node1->next = (*head)->next; | ||
839 | (*head)->next = node1; | ||
840 | out_of_order++; | ||
841 | } | ||
842 | |||
843 | node1 = (*head); | ||
844 | |||
845 | while (node1->next && node1->next->next) { | ||
846 | if (node1->next->base > node1->next->next->base) { | ||
847 | out_of_order++; | ||
848 | node2 = node1->next; | ||
849 | node1->next = node1->next->next; | ||
850 | node1 = node1->next; | ||
851 | node2->next = node1->next; | ||
852 | node1->next = node2; | ||
853 | } else | ||
854 | node1 = node1->next; | ||
855 | } | ||
856 | } /* End of out_of_order loop */ | ||
857 | |||
858 | node1 = *head; | ||
859 | |||
860 | while (node1 && node1->next) { | ||
861 | if ((node1->base + node1->length) == node1->next->base) { | ||
862 | /* Combine */ | ||
863 | dbg("8..\n"); | ||
864 | node1->length += node1->next->length; | ||
865 | node2 = node1->next; | ||
866 | node1->next = node1->next->next; | ||
867 | kfree(node2); | ||
868 | } else | ||
869 | node1 = node1->next; | ||
870 | } | ||
871 | |||
872 | return 0; | ||
873 | } | ||
874 | |||
875 | |||
876 | /** | ||
877 | * pciehp_slot_create - Creates a node and adds it to the proper bus. | ||
878 | * @busnumber - bus where new node is to be located | ||
879 | * | ||
880 | * Returns pointer to the new node or NULL if unsuccessful | ||
881 | */ | ||
882 | struct pci_func *pciehp_slot_create(u8 busnumber) | ||
883 | { | ||
884 | struct pci_func *new_slot; | ||
885 | struct pci_func *next; | ||
886 | dbg("%s: busnumber %x\n", __FUNCTION__, busnumber); | ||
887 | new_slot = kmalloc(sizeof(struct pci_func), GFP_KERNEL); | ||
888 | |||
889 | if (new_slot == NULL) | ||
890 | return new_slot; | ||
891 | |||
892 | memset(new_slot, 0, sizeof(struct pci_func)); | ||
893 | |||
894 | new_slot->next = NULL; | ||
895 | new_slot->configured = 1; | ||
896 | |||
897 | if (pciehp_slot_list[busnumber] == NULL) { | ||
898 | pciehp_slot_list[busnumber] = new_slot; | ||
899 | } else { | ||
900 | next = pciehp_slot_list[busnumber]; | ||
901 | while (next->next != NULL) | ||
902 | next = next->next; | ||
903 | next->next = new_slot; | ||
904 | } | ||
905 | return new_slot; | ||
906 | } | ||
907 | |||
908 | |||
909 | /** | ||
910 | * slot_remove - Removes a node from the linked list of slots. | ||
911 | * @old_slot: slot to remove | ||
912 | * | ||
913 | * Returns 0 if successful, !0 otherwise. | ||
914 | */ | ||
915 | static int slot_remove(struct pci_func * old_slot) | ||
916 | { | ||
917 | struct pci_func *next; | ||
918 | |||
919 | if (old_slot == NULL) | ||
920 | return 1; | ||
921 | |||
922 | next = pciehp_slot_list[old_slot->bus]; | ||
923 | |||
924 | if (next == NULL) | ||
925 | return 1; | ||
926 | |||
927 | if (next == old_slot) { | ||
928 | pciehp_slot_list[old_slot->bus] = old_slot->next; | ||
929 | pciehp_destroy_board_resources(old_slot); | ||
930 | kfree(old_slot); | ||
931 | return 0; | ||
932 | } | ||
933 | |||
934 | while ((next->next != old_slot) && (next->next != NULL)) { | ||
935 | next = next->next; | ||
936 | } | ||
937 | |||
938 | if (next->next == old_slot) { | ||
939 | next->next = old_slot->next; | ||
940 | pciehp_destroy_board_resources(old_slot); | ||
941 | kfree(old_slot); | ||
942 | return 0; | ||
943 | } else | ||
944 | return 2; | ||
945 | } | ||
946 | |||
947 | |||
948 | /** | ||
949 | * bridge_slot_remove - Removes a node from the linked list of slots. | ||
950 | * @bridge: bridge to remove | ||
951 | * | ||
952 | * Returns 0 if successful, !0 otherwise. | ||
953 | */ | ||
954 | static int bridge_slot_remove(struct pci_func *bridge) | ||
955 | { | ||
956 | u8 subordinateBus, secondaryBus; | ||
957 | u8 tempBus; | ||
958 | struct pci_func *next; | ||
959 | |||
960 | if (bridge == NULL) | ||
961 | return 1; | ||
962 | |||
963 | secondaryBus = (bridge->config_space[0x06] >> 8) & 0xFF; | ||
964 | subordinateBus = (bridge->config_space[0x06] >> 16) & 0xFF; | ||
965 | |||
966 | for (tempBus = secondaryBus; tempBus <= subordinateBus; tempBus++) { | ||
967 | next = pciehp_slot_list[tempBus]; | ||
968 | |||
969 | while (!slot_remove(next)) { | ||
970 | next = pciehp_slot_list[tempBus]; | ||
971 | } | ||
972 | } | ||
973 | |||
974 | next = pciehp_slot_list[bridge->bus]; | ||
975 | |||
976 | if (next == NULL) { | ||
977 | return 1; | ||
978 | } | ||
979 | |||
980 | if (next == bridge) { | ||
981 | pciehp_slot_list[bridge->bus] = bridge->next; | ||
982 | kfree(bridge); | ||
983 | return 0; | ||
984 | } | ||
985 | |||
986 | while ((next->next != bridge) && (next->next != NULL)) { | ||
987 | next = next->next; | ||
988 | } | ||
989 | |||
990 | if (next->next == bridge) { | ||
991 | next->next = bridge->next; | ||
992 | kfree(bridge); | ||
993 | return 0; | ||
994 | } else | ||
995 | return 2; | ||
996 | } | ||
997 | |||
998 | |||
999 | /** | ||
1000 | * pciehp_slot_find - Looks for a node by bus, and device, multiple functions accessed | ||
1001 | * @bus: bus to find | ||
1002 | * @device: device to find | ||
1003 | * @index: is 0 for first function found, 1 for the second... | ||
1004 | * | ||
1005 | * Returns pointer to the node if successful, %NULL otherwise. | ||
1006 | */ | ||
1007 | struct pci_func *pciehp_slot_find(u8 bus, u8 device, u8 index) | ||
1008 | { | ||
1009 | int found = -1; | ||
1010 | struct pci_func *func; | ||
1011 | |||
1012 | func = pciehp_slot_list[bus]; | ||
1013 | dbg("%s: bus %x device %x index %x\n", | ||
1014 | __FUNCTION__, bus, device, index); | ||
1015 | if (func != NULL) { | ||
1016 | dbg("%s: func-> bus %x device %x function %x pci_dev %p\n", | ||
1017 | __FUNCTION__, func->bus, func->device, func->function, | ||
1018 | func->pci_dev); | ||
1019 | } else | ||
1020 | dbg("%s: func == NULL\n", __FUNCTION__); | ||
1021 | |||
1022 | if ((func == NULL) || ((func->device == device) && (index == 0))) | ||
1023 | return func; | ||
1024 | |||
1025 | if (func->device == device) | ||
1026 | found++; | ||
1027 | |||
1028 | while (func->next != NULL) { | ||
1029 | func = func->next; | ||
1030 | |||
1031 | dbg("%s: In while loop, func-> bus %x device %x function %x pci_dev %p\n", | ||
1032 | __FUNCTION__, func->bus, func->device, func->function, | ||
1033 | func->pci_dev); | ||
1034 | if (func->device == device) | ||
1035 | found++; | ||
1036 | dbg("%s: while loop, found %d, index %d\n", __FUNCTION__, | ||
1037 | found, index); | ||
1038 | |||
1039 | if ((found == index) || (func->function == index)) { | ||
1040 | dbg("%s: Found bus %x dev %x func %x\n", __FUNCTION__, | ||
1041 | func->bus, func->device, func->function); | ||
1042 | return func; | ||
1043 | } | ||
1044 | } | ||
1045 | |||
1046 | return NULL; | ||
1047 | } | ||
1048 | |||
1049 | static int is_bridge(struct pci_func * func) | ||
1050 | { | ||
1051 | /* Check the header type */ | ||
1052 | if (((func->config_space[0x03] >> 16) & 0xFF) == 0x01) | ||
1053 | return 1; | ||
1054 | else | ||
1055 | return 0; | ||
1056 | } | ||
1057 | |||
1058 | |||
1059 | /* The following routines constitute the bulk of the | ||
1060 | hotplug controller logic | ||
1061 | */ | ||
1062 | |||
1063 | static void set_slot_off(struct controller *ctrl, struct slot * pslot) | ||
1064 | { | ||
1065 | /* Wait for exclusive access to hardware */ | ||
1066 | down(&ctrl->crit_sect); | ||
1067 | |||
1068 | /* turn off slot, turn on Amber LED, turn off Green LED if supported*/ | ||
1069 | if (POWER_CTRL(ctrl->ctrlcap)) { | ||
1070 | if (pslot->hpc_ops->power_off_slot(pslot)) { | ||
1071 | err("%s: Issue of Slot Power Off command failed\n", __FUNCTION__); | ||
1072 | up(&ctrl->crit_sect); | ||
1073 | return; | ||
1074 | } | ||
1075 | wait_for_ctrl_irq (ctrl); | ||
1076 | } | ||
1077 | |||
1078 | if (PWR_LED(ctrl->ctrlcap)) { | ||
1079 | pslot->hpc_ops->green_led_off(pslot); | ||
1080 | wait_for_ctrl_irq (ctrl); | ||
1081 | } | ||
1082 | |||
1083 | if (ATTN_LED(ctrl->ctrlcap)) { | ||
1084 | if (pslot->hpc_ops->set_attention_status(pslot, 1)) { | ||
1085 | err("%s: Issue of Set Attention Led command failed\n", __FUNCTION__); | ||
1086 | up(&ctrl->crit_sect); | ||
1087 | return; | ||
1088 | } | ||
1089 | wait_for_ctrl_irq (ctrl); | ||
1090 | } | ||
1091 | |||
1092 | /* Done with exclusive hardware access */ | ||
1093 | up(&ctrl->crit_sect); | ||
1094 | } | ||
1095 | |||
1096 | /** | ||
1097 | * board_added - Called after a board has been added to the system. | ||
1098 | * | ||
1099 | * Turns power on for the board | ||
1100 | * Configures board | ||
1101 | * | ||
1102 | */ | ||
1103 | static u32 board_added(struct pci_func * func, struct controller * ctrl) | ||
1104 | { | ||
1105 | u8 hp_slot; | ||
1106 | int index; | ||
1107 | u32 temp_register = 0xFFFFFFFF; | ||
1108 | u32 rc = 0; | ||
1109 | struct pci_func *new_func = NULL; | ||
1110 | struct slot *p_slot; | ||
1111 | struct resource_lists res_lists; | ||
1112 | |||
1113 | p_slot = pciehp_find_slot(ctrl, func->device); | ||
1114 | hp_slot = func->device - ctrl->slot_device_offset; | ||
1115 | |||
1116 | dbg("%s: func->device, slot_offset, hp_slot = %d, %d ,%d\n", __FUNCTION__, func->device, ctrl->slot_device_offset, hp_slot); | ||
1117 | |||
1118 | /* Wait for exclusive access to hardware */ | ||
1119 | down(&ctrl->crit_sect); | ||
1120 | |||
1121 | if (POWER_CTRL(ctrl->ctrlcap)) { | ||
1122 | /* Power on slot */ | ||
1123 | rc = p_slot->hpc_ops->power_on_slot(p_slot); | ||
1124 | if (rc) { | ||
1125 | up(&ctrl->crit_sect); | ||
1126 | return -1; | ||
1127 | } | ||
1128 | |||
1129 | /* Wait for the command to complete */ | ||
1130 | wait_for_ctrl_irq (ctrl); | ||
1131 | } | ||
1132 | |||
1133 | if (PWR_LED(ctrl->ctrlcap)) { | ||
1134 | p_slot->hpc_ops->green_led_blink(p_slot); | ||
1135 | |||
1136 | /* Wait for the command to complete */ | ||
1137 | wait_for_ctrl_irq (ctrl); | ||
1138 | } | ||
1139 | |||
1140 | /* Done with exclusive hardware access */ | ||
1141 | up(&ctrl->crit_sect); | ||
1142 | |||
1143 | /* Wait for ~1 second */ | ||
1144 | dbg("%s: before long_delay\n", __FUNCTION__); | ||
1145 | wait_for_ctrl_irq (ctrl); | ||
1146 | dbg("%s: afterlong_delay\n", __FUNCTION__); | ||
1147 | |||
1148 | /* Check link training status */ | ||
1149 | rc = p_slot->hpc_ops->check_lnk_status(ctrl); | ||
1150 | if (rc) { | ||
1151 | err("%s: Failed to check link status\n", __FUNCTION__); | ||
1152 | set_slot_off(ctrl, p_slot); | ||
1153 | return rc; | ||
1154 | } | ||
1155 | |||
1156 | dbg("%s: func status = %x\n", __FUNCTION__, func->status); | ||
1157 | |||
1158 | /* Check for a power fault */ | ||
1159 | if (func->status == 0xFF) { | ||
1160 | /* power fault occurred, but it was benign */ | ||
1161 | temp_register = 0xFFFFFFFF; | ||
1162 | dbg("%s: temp register set to %x by power fault\n", __FUNCTION__, temp_register); | ||
1163 | rc = POWER_FAILURE; | ||
1164 | func->status = 0; | ||
1165 | } else { | ||
1166 | /* Get vendor/device ID u32 */ | ||
1167 | rc = pci_bus_read_config_dword (ctrl->pci_dev->subordinate, PCI_DEVFN(func->device, func->function), | ||
1168 | PCI_VENDOR_ID, &temp_register); | ||
1169 | dbg("%s: pci_bus_read_config_dword returns %d\n", __FUNCTION__, rc); | ||
1170 | dbg("%s: temp_register is %x\n", __FUNCTION__, temp_register); | ||
1171 | |||
1172 | if (rc != 0) { | ||
1173 | /* Something's wrong here */ | ||
1174 | temp_register = 0xFFFFFFFF; | ||
1175 | dbg("%s: temp register set to %x by error\n", __FUNCTION__, temp_register); | ||
1176 | } | ||
1177 | /* Preset return code. It will be changed later if things go okay. */ | ||
1178 | rc = NO_ADAPTER_PRESENT; | ||
1179 | } | ||
1180 | |||
1181 | /* All F's is an empty slot or an invalid board */ | ||
1182 | if (temp_register != 0xFFFFFFFF) { /* Check for a board in the slot */ | ||
1183 | res_lists.io_head = ctrl->io_head; | ||
1184 | res_lists.mem_head = ctrl->mem_head; | ||
1185 | res_lists.p_mem_head = ctrl->p_mem_head; | ||
1186 | res_lists.bus_head = ctrl->bus_head; | ||
1187 | res_lists.irqs = NULL; | ||
1188 | |||
1189 | rc = configure_new_device(ctrl, func, 0, &res_lists, 0, 0); | ||
1190 | dbg("%s: back from configure_new_device\n", __FUNCTION__); | ||
1191 | |||
1192 | ctrl->io_head = res_lists.io_head; | ||
1193 | ctrl->mem_head = res_lists.mem_head; | ||
1194 | ctrl->p_mem_head = res_lists.p_mem_head; | ||
1195 | ctrl->bus_head = res_lists.bus_head; | ||
1196 | |||
1197 | pciehp_resource_sort_and_combine(&(ctrl->mem_head)); | ||
1198 | pciehp_resource_sort_and_combine(&(ctrl->p_mem_head)); | ||
1199 | pciehp_resource_sort_and_combine(&(ctrl->io_head)); | ||
1200 | pciehp_resource_sort_and_combine(&(ctrl->bus_head)); | ||
1201 | |||
1202 | if (rc) { | ||
1203 | set_slot_off(ctrl, p_slot); | ||
1204 | return rc; | ||
1205 | } | ||
1206 | pciehp_save_slot_config(ctrl, func); | ||
1207 | |||
1208 | func->status = 0; | ||
1209 | func->switch_save = 0x10; | ||
1210 | func->is_a_board = 0x01; | ||
1211 | |||
1212 | /* next, we will instantiate the linux pci_dev structures | ||
1213 | * (with appropriate driver notification, if already present) | ||
1214 | */ | ||
1215 | index = 0; | ||
1216 | do { | ||
1217 | new_func = pciehp_slot_find(ctrl->slot_bus, func->device, index++); | ||
1218 | if (new_func && !new_func->pci_dev) { | ||
1219 | dbg("%s:call pci_hp_configure_dev, func %x\n", | ||
1220 | __FUNCTION__, index); | ||
1221 | pciehp_configure_device(ctrl, new_func); | ||
1222 | } | ||
1223 | } while (new_func); | ||
1224 | |||
1225 | /* | ||
1226 | * Some PCI Express root ports require fixup after hot-plug operation. | ||
1227 | */ | ||
1228 | if (pcie_mch_quirk) | ||
1229 | pci_fixup_device(pci_fixup_final, ctrl->pci_dev); | ||
1230 | |||
1231 | if (PWR_LED(ctrl->ctrlcap)) { | ||
1232 | /* Wait for exclusive access to hardware */ | ||
1233 | down(&ctrl->crit_sect); | ||
1234 | |||
1235 | p_slot->hpc_ops->green_led_on(p_slot); | ||
1236 | |||
1237 | /* Wait for the command to complete */ | ||
1238 | wait_for_ctrl_irq (ctrl); | ||
1239 | |||
1240 | /* Done with exclusive hardware access */ | ||
1241 | up(&ctrl->crit_sect); | ||
1242 | } | ||
1243 | } else { | ||
1244 | set_slot_off(ctrl, p_slot); | ||
1245 | return -1; | ||
1246 | } | ||
1247 | return 0; | ||
1248 | } | ||
1249 | |||
1250 | |||
1251 | /** | ||
1252 | * remove_board - Turns off slot and LED's | ||
1253 | * | ||
1254 | */ | ||
1255 | static u32 remove_board(struct pci_func *func, struct controller *ctrl) | ||
1256 | { | ||
1257 | int index; | ||
1258 | u8 skip = 0; | ||
1259 | u8 device; | ||
1260 | u8 hp_slot; | ||
1261 | u32 rc; | ||
1262 | struct resource_lists res_lists; | ||
1263 | struct pci_func *temp_func; | ||
1264 | struct slot *p_slot; | ||
1265 | |||
1266 | if (func == NULL) | ||
1267 | return 1; | ||
1268 | |||
1269 | if (pciehp_unconfigure_device(func)) | ||
1270 | return 1; | ||
1271 | |||
1272 | device = func->device; | ||
1273 | |||
1274 | hp_slot = func->device - ctrl->slot_device_offset; | ||
1275 | p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | ||
1276 | |||
1277 | dbg("In %s, hp_slot = %d\n", __FUNCTION__, hp_slot); | ||
1278 | |||
1279 | if ((ctrl->add_support) && | ||
1280 | !(func->bus_head || func->mem_head || func->p_mem_head || func->io_head)) { | ||
1281 | /* Here we check to see if we've saved any of the board's | ||
1282 | * resources already. If so, we'll skip the attempt to | ||
1283 | * determine what's being used. | ||
1284 | */ | ||
1285 | index = 0; | ||
1286 | |||
1287 | temp_func = func; | ||
1288 | |||
1289 | while ((temp_func = pciehp_slot_find(temp_func->bus, temp_func->device, index++))) { | ||
1290 | if (temp_func->bus_head || temp_func->mem_head | ||
1291 | || temp_func->p_mem_head || temp_func->io_head) { | ||
1292 | skip = 1; | ||
1293 | break; | ||
1294 | } | ||
1295 | } | ||
1296 | |||
1297 | if (!skip) | ||
1298 | rc = pciehp_save_used_resources(ctrl, func, DISABLE_CARD); | ||
1299 | } | ||
1300 | /* Change status to shutdown */ | ||
1301 | if (func->is_a_board) | ||
1302 | func->status = 0x01; | ||
1303 | func->configured = 0; | ||
1304 | |||
1305 | /* Wait for exclusive access to hardware */ | ||
1306 | down(&ctrl->crit_sect); | ||
1307 | |||
1308 | if (POWER_CTRL(ctrl->ctrlcap)) { | ||
1309 | /* power off slot */ | ||
1310 | rc = p_slot->hpc_ops->power_off_slot(p_slot); | ||
1311 | if (rc) { | ||
1312 | err("%s: Issue of Slot Disable command failed\n", __FUNCTION__); | ||
1313 | up(&ctrl->crit_sect); | ||
1314 | return rc; | ||
1315 | } | ||
1316 | /* Wait for the command to complete */ | ||
1317 | wait_for_ctrl_irq (ctrl); | ||
1318 | } | ||
1319 | |||
1320 | if (PWR_LED(ctrl->ctrlcap)) { | ||
1321 | /* turn off Green LED */ | ||
1322 | p_slot->hpc_ops->green_led_off(p_slot); | ||
1323 | |||
1324 | /* Wait for the command to complete */ | ||
1325 | wait_for_ctrl_irq (ctrl); | ||
1326 | } | ||
1327 | |||
1328 | /* Done with exclusive hardware access */ | ||
1329 | up(&ctrl->crit_sect); | ||
1330 | |||
1331 | if (ctrl->add_support) { | ||
1332 | while (func) { | ||
1333 | res_lists.io_head = ctrl->io_head; | ||
1334 | res_lists.mem_head = ctrl->mem_head; | ||
1335 | res_lists.p_mem_head = ctrl->p_mem_head; | ||
1336 | res_lists.bus_head = ctrl->bus_head; | ||
1337 | |||
1338 | dbg("Returning resources to ctlr lists for (B/D/F) = (%#x/%#x/%#x)\n", | ||
1339 | func->bus, func->device, func->function); | ||
1340 | |||
1341 | pciehp_return_board_resources(func, &res_lists); | ||
1342 | |||
1343 | ctrl->io_head = res_lists.io_head; | ||
1344 | ctrl->mem_head = res_lists.mem_head; | ||
1345 | ctrl->p_mem_head = res_lists.p_mem_head; | ||
1346 | ctrl->bus_head = res_lists.bus_head; | ||
1347 | |||
1348 | pciehp_resource_sort_and_combine(&(ctrl->mem_head)); | ||
1349 | pciehp_resource_sort_and_combine(&(ctrl->p_mem_head)); | ||
1350 | pciehp_resource_sort_and_combine(&(ctrl->io_head)); | ||
1351 | pciehp_resource_sort_and_combine(&(ctrl->bus_head)); | ||
1352 | |||
1353 | if (is_bridge(func)) { | ||
1354 | dbg("PCI Bridge Hot-Remove s:b:d:f(%02x:%02x:%02x:%02x)\n", | ||
1355 | ctrl->seg, func->bus, func->device, func->function); | ||
1356 | bridge_slot_remove(func); | ||
1357 | } else { | ||
1358 | dbg("PCI Function Hot-Remove s:b:d:f(%02x:%02x:%02x:%02x)\n", | ||
1359 | ctrl->seg, func->bus, func->device, func->function); | ||
1360 | slot_remove(func); | ||
1361 | } | ||
1362 | |||
1363 | func = pciehp_slot_find(ctrl->slot_bus, device, 0); | ||
1364 | } | ||
1365 | |||
1366 | /* Setup slot structure with entry for empty slot */ | ||
1367 | func = pciehp_slot_create(ctrl->slot_bus); | ||
1368 | |||
1369 | if (func == NULL) { | ||
1370 | return 1; | ||
1371 | } | ||
1372 | |||
1373 | func->bus = ctrl->slot_bus; | ||
1374 | func->device = device; | ||
1375 | func->function = 0; | ||
1376 | func->configured = 0; | ||
1377 | func->switch_save = 0x10; | ||
1378 | func->is_a_board = 0; | ||
1379 | } | ||
1380 | |||
1381 | return 0; | ||
1382 | } | ||
1383 | |||
1384 | |||
1385 | static void pushbutton_helper_thread(unsigned long data) | ||
1386 | { | ||
1387 | pushbutton_pending = data; | ||
1388 | |||
1389 | up(&event_semaphore); | ||
1390 | } | ||
1391 | |||
1392 | /** | ||
1393 | * pciehp_pushbutton_thread | ||
1394 | * | ||
1395 | * Scheduled procedure to handle blocking stuff for the pushbuttons | ||
1396 | * Handles all pending events and exits. | ||
1397 | * | ||
1398 | */ | ||
1399 | static void pciehp_pushbutton_thread(unsigned long slot) | ||
1400 | { | ||
1401 | struct slot *p_slot = (struct slot *) slot; | ||
1402 | u8 getstatus; | ||
1403 | |||
1404 | pushbutton_pending = 0; | ||
1405 | |||
1406 | if (!p_slot) { | ||
1407 | dbg("%s: Error! slot NULL\n", __FUNCTION__); | ||
1408 | return; | ||
1409 | } | ||
1410 | |||
1411 | p_slot->hpc_ops->get_power_status(p_slot, &getstatus); | ||
1412 | if (getstatus) { | ||
1413 | p_slot->state = POWEROFF_STATE; | ||
1414 | dbg("In power_down_board, b:d(%x:%x)\n", p_slot->bus, p_slot->device); | ||
1415 | |||
1416 | pciehp_disable_slot(p_slot); | ||
1417 | p_slot->state = STATIC_STATE; | ||
1418 | } else { | ||
1419 | p_slot->state = POWERON_STATE; | ||
1420 | dbg("In add_board, b:d(%x:%x)\n", p_slot->bus, p_slot->device); | ||
1421 | |||
1422 | if (pciehp_enable_slot(p_slot) && PWR_LED(p_slot->ctrl->ctrlcap)) { | ||
1423 | /* Wait for exclusive access to hardware */ | ||
1424 | down(&p_slot->ctrl->crit_sect); | ||
1425 | |||
1426 | p_slot->hpc_ops->green_led_off(p_slot); | ||
1427 | |||
1428 | /* Wait for the command to complete */ | ||
1429 | wait_for_ctrl_irq (p_slot->ctrl); | ||
1430 | |||
1431 | /* Done with exclusive hardware access */ | ||
1432 | up(&p_slot->ctrl->crit_sect); | ||
1433 | } | ||
1434 | p_slot->state = STATIC_STATE; | ||
1435 | } | ||
1436 | |||
1437 | return; | ||
1438 | } | ||
1439 | |||
1440 | /** | ||
1441 | * pciehp_surprise_rm_thread | ||
1442 | * | ||
1443 | * Scheduled procedure to handle blocking stuff for the surprise removal | ||
1444 | * Handles all pending events and exits. | ||
1445 | * | ||
1446 | */ | ||
1447 | static void pciehp_surprise_rm_thread(unsigned long slot) | ||
1448 | { | ||
1449 | struct slot *p_slot = (struct slot *) slot; | ||
1450 | u8 getstatus; | ||
1451 | |||
1452 | surprise_rm_pending = 0; | ||
1453 | |||
1454 | if (!p_slot) { | ||
1455 | dbg("%s: Error! slot NULL\n", __FUNCTION__); | ||
1456 | return; | ||
1457 | } | ||
1458 | |||
1459 | p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus); | ||
1460 | if (!getstatus) { | ||
1461 | p_slot->state = POWEROFF_STATE; | ||
1462 | dbg("In removing board, b:d(%x:%x)\n", p_slot->bus, p_slot->device); | ||
1463 | |||
1464 | pciehp_disable_slot(p_slot); | ||
1465 | p_slot->state = STATIC_STATE; | ||
1466 | } else { | ||
1467 | p_slot->state = POWERON_STATE; | ||
1468 | dbg("In add_board, b:d(%x:%x)\n", p_slot->bus, p_slot->device); | ||
1469 | |||
1470 | if (pciehp_enable_slot(p_slot) && PWR_LED(p_slot->ctrl->ctrlcap)) { | ||
1471 | /* Wait for exclusive access to hardware */ | ||
1472 | down(&p_slot->ctrl->crit_sect); | ||
1473 | |||
1474 | p_slot->hpc_ops->green_led_off(p_slot); | ||
1475 | |||
1476 | /* Wait for the command to complete */ | ||
1477 | wait_for_ctrl_irq (p_slot->ctrl); | ||
1478 | |||
1479 | /* Done with exclusive hardware access */ | ||
1480 | up(&p_slot->ctrl->crit_sect); | ||
1481 | } | ||
1482 | p_slot->state = STATIC_STATE; | ||
1483 | } | ||
1484 | |||
1485 | return; | ||
1486 | } | ||
1487 | |||
1488 | |||
1489 | |||
1490 | /* this is the main worker thread */ | ||
1491 | static int event_thread(void* data) | ||
1492 | { | ||
1493 | struct controller *ctrl; | ||
1494 | lock_kernel(); | ||
1495 | daemonize("pciehpd_event"); | ||
1496 | |||
1497 | unlock_kernel(); | ||
1498 | |||
1499 | while (1) { | ||
1500 | dbg("!!!!event_thread sleeping\n"); | ||
1501 | down_interruptible (&event_semaphore); | ||
1502 | dbg("event_thread woken finished = %d\n", event_finished); | ||
1503 | if (event_finished || signal_pending(current)) | ||
1504 | break; | ||
1505 | /* Do stuff here */ | ||
1506 | if (pushbutton_pending) | ||
1507 | pciehp_pushbutton_thread(pushbutton_pending); | ||
1508 | else if (surprise_rm_pending) | ||
1509 | pciehp_surprise_rm_thread(surprise_rm_pending); | ||
1510 | else | ||
1511 | for (ctrl = pciehp_ctrl_list; ctrl; ctrl=ctrl->next) | ||
1512 | interrupt_event_handler(ctrl); | ||
1513 | } | ||
1514 | dbg("event_thread signals exit\n"); | ||
1515 | up(&event_exit); | ||
1516 | return 0; | ||
1517 | } | ||
1518 | |||
1519 | int pciehp_event_start_thread(void) | ||
1520 | { | ||
1521 | int pid; | ||
1522 | |||
1523 | /* initialize our semaphores */ | ||
1524 | init_MUTEX_LOCKED(&event_exit); | ||
1525 | event_finished=0; | ||
1526 | |||
1527 | init_MUTEX_LOCKED(&event_semaphore); | ||
1528 | pid = kernel_thread(event_thread, NULL, 0); | ||
1529 | |||
1530 | if (pid < 0) { | ||
1531 | err ("Can't start up our event thread\n"); | ||
1532 | return -1; | ||
1533 | } | ||
1534 | dbg("Our event thread pid = %d\n", pid); | ||
1535 | return 0; | ||
1536 | } | ||
1537 | |||
1538 | |||
1539 | void pciehp_event_stop_thread(void) | ||
1540 | { | ||
1541 | event_finished = 1; | ||
1542 | dbg("event_thread finish command given\n"); | ||
1543 | up(&event_semaphore); | ||
1544 | dbg("wait for event_thread to exit\n"); | ||
1545 | down(&event_exit); | ||
1546 | } | ||
1547 | |||
1548 | |||
1549 | static int update_slot_info(struct slot *slot) | ||
1550 | { | ||
1551 | struct hotplug_slot_info *info; | ||
1552 | /* char buffer[SLOT_NAME_SIZE]; */ | ||
1553 | int result; | ||
1554 | |||
1555 | info = kmalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL); | ||
1556 | if (!info) | ||
1557 | return -ENOMEM; | ||
1558 | |||
1559 | /* make_slot_name (&buffer[0], SLOT_NAME_SIZE, slot); */ | ||
1560 | |||
1561 | slot->hpc_ops->get_power_status(slot, &(info->power_status)); | ||
1562 | slot->hpc_ops->get_attention_status(slot, &(info->attention_status)); | ||
1563 | slot->hpc_ops->get_latch_status(slot, &(info->latch_status)); | ||
1564 | slot->hpc_ops->get_adapter_status(slot, &(info->adapter_status)); | ||
1565 | |||
1566 | /* result = pci_hp_change_slot_info(buffer, info); */ | ||
1567 | result = pci_hp_change_slot_info(slot->hotplug_slot, info); | ||
1568 | kfree (info); | ||
1569 | return result; | ||
1570 | } | ||
1571 | |||
1572 | static void interrupt_event_handler(struct controller *ctrl) | ||
1573 | { | ||
1574 | int loop = 0; | ||
1575 | int change = 1; | ||
1576 | struct pci_func *func; | ||
1577 | u8 hp_slot; | ||
1578 | u8 getstatus; | ||
1579 | struct slot *p_slot; | ||
1580 | |||
1581 | while (change) { | ||
1582 | change = 0; | ||
1583 | |||
1584 | for (loop = 0; loop < 10; loop++) { | ||
1585 | if (ctrl->event_queue[loop].event_type != 0) { | ||
1586 | hp_slot = ctrl->event_queue[loop].hp_slot; | ||
1587 | |||
1588 | func = pciehp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0); | ||
1589 | |||
1590 | p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | ||
1591 | |||
1592 | dbg("hp_slot %d, func %p, p_slot %p\n", hp_slot, func, p_slot); | ||
1593 | |||
1594 | if (ctrl->event_queue[loop].event_type == INT_BUTTON_CANCEL) { | ||
1595 | dbg("button cancel\n"); | ||
1596 | del_timer(&p_slot->task_event); | ||
1597 | |||
1598 | switch (p_slot->state) { | ||
1599 | case BLINKINGOFF_STATE: | ||
1600 | /* Wait for exclusive access to hardware */ | ||
1601 | down(&ctrl->crit_sect); | ||
1602 | |||
1603 | if (PWR_LED(ctrl->ctrlcap)) { | ||
1604 | p_slot->hpc_ops->green_led_on(p_slot); | ||
1605 | /* Wait for the command to complete */ | ||
1606 | wait_for_ctrl_irq (ctrl); | ||
1607 | } | ||
1608 | if (ATTN_LED(ctrl->ctrlcap)) { | ||
1609 | p_slot->hpc_ops->set_attention_status(p_slot, 0); | ||
1610 | |||
1611 | /* Wait for the command to complete */ | ||
1612 | wait_for_ctrl_irq (ctrl); | ||
1613 | } | ||
1614 | /* Done with exclusive hardware access */ | ||
1615 | up(&ctrl->crit_sect); | ||
1616 | break; | ||
1617 | case BLINKINGON_STATE: | ||
1618 | /* Wait for exclusive access to hardware */ | ||
1619 | down(&ctrl->crit_sect); | ||
1620 | |||
1621 | if (PWR_LED(ctrl->ctrlcap)) { | ||
1622 | p_slot->hpc_ops->green_led_off(p_slot); | ||
1623 | /* Wait for the command to complete */ | ||
1624 | wait_for_ctrl_irq (ctrl); | ||
1625 | } | ||
1626 | if (ATTN_LED(ctrl->ctrlcap)){ | ||
1627 | p_slot->hpc_ops->set_attention_status(p_slot, 0); | ||
1628 | /* Wait for the command to complete */ | ||
1629 | wait_for_ctrl_irq (ctrl); | ||
1630 | } | ||
1631 | /* Done with exclusive hardware access */ | ||
1632 | up(&ctrl->crit_sect); | ||
1633 | |||
1634 | break; | ||
1635 | default: | ||
1636 | warn("Not a valid state\n"); | ||
1637 | return; | ||
1638 | } | ||
1639 | info(msg_button_cancel, p_slot->number); | ||
1640 | p_slot->state = STATIC_STATE; | ||
1641 | } | ||
1642 | /* ***********Button Pressed (No action on 1st press...) */ | ||
1643 | else if (ctrl->event_queue[loop].event_type == INT_BUTTON_PRESS) { | ||
1644 | |||
1645 | if (ATTN_BUTTN(ctrl->ctrlcap)) { | ||
1646 | dbg("Button pressed\n"); | ||
1647 | p_slot->hpc_ops->get_power_status(p_slot, &getstatus); | ||
1648 | if (getstatus) { | ||
1649 | /* slot is on */ | ||
1650 | dbg("slot is on\n"); | ||
1651 | p_slot->state = BLINKINGOFF_STATE; | ||
1652 | info(msg_button_off, p_slot->number); | ||
1653 | } else { | ||
1654 | /* slot is off */ | ||
1655 | dbg("slot is off\n"); | ||
1656 | p_slot->state = BLINKINGON_STATE; | ||
1657 | info(msg_button_on, p_slot->number); | ||
1658 | } | ||
1659 | |||
1660 | /* Wait for exclusive access to hardware */ | ||
1661 | down(&ctrl->crit_sect); | ||
1662 | |||
1663 | /* blink green LED and turn off amber */ | ||
1664 | if (PWR_LED(ctrl->ctrlcap)) { | ||
1665 | p_slot->hpc_ops->green_led_blink(p_slot); | ||
1666 | /* Wait for the command to complete */ | ||
1667 | wait_for_ctrl_irq (ctrl); | ||
1668 | } | ||
1669 | |||
1670 | if (ATTN_LED(ctrl->ctrlcap)) { | ||
1671 | p_slot->hpc_ops->set_attention_status(p_slot, 0); | ||
1672 | |||
1673 | /* Wait for the command to complete */ | ||
1674 | wait_for_ctrl_irq (ctrl); | ||
1675 | } | ||
1676 | |||
1677 | /* Done with exclusive hardware access */ | ||
1678 | up(&ctrl->crit_sect); | ||
1679 | |||
1680 | init_timer(&p_slot->task_event); | ||
1681 | p_slot->task_event.expires = jiffies + 5 * HZ; /* 5 second delay */ | ||
1682 | p_slot->task_event.function = (void (*)(unsigned long)) pushbutton_helper_thread; | ||
1683 | p_slot->task_event.data = (unsigned long) p_slot; | ||
1684 | |||
1685 | dbg("add_timer p_slot = %p\n", (void *) p_slot); | ||
1686 | add_timer(&p_slot->task_event); | ||
1687 | } | ||
1688 | } | ||
1689 | /***********POWER FAULT********************/ | ||
1690 | else if (ctrl->event_queue[loop].event_type == INT_POWER_FAULT) { | ||
1691 | if (POWER_CTRL(ctrl->ctrlcap)) { | ||
1692 | dbg("power fault\n"); | ||
1693 | /* Wait for exclusive access to hardware */ | ||
1694 | down(&ctrl->crit_sect); | ||
1695 | |||
1696 | if (ATTN_LED(ctrl->ctrlcap)) { | ||
1697 | p_slot->hpc_ops->set_attention_status(p_slot, 1); | ||
1698 | wait_for_ctrl_irq (ctrl); | ||
1699 | } | ||
1700 | |||
1701 | if (PWR_LED(ctrl->ctrlcap)) { | ||
1702 | p_slot->hpc_ops->green_led_off(p_slot); | ||
1703 | wait_for_ctrl_irq (ctrl); | ||
1704 | } | ||
1705 | |||
1706 | /* Done with exclusive hardware access */ | ||
1707 | up(&ctrl->crit_sect); | ||
1708 | } | ||
1709 | } | ||
1710 | /***********SURPRISE REMOVAL********************/ | ||
1711 | else if ((ctrl->event_queue[loop].event_type == INT_PRESENCE_ON) || | ||
1712 | (ctrl->event_queue[loop].event_type == INT_PRESENCE_OFF)) { | ||
1713 | if (HP_SUPR_RM(ctrl->ctrlcap)) { | ||
1714 | dbg("Surprise Removal\n"); | ||
1715 | if (p_slot) { | ||
1716 | surprise_rm_pending = (unsigned long) p_slot; | ||
1717 | up(&event_semaphore); | ||
1718 | update_slot_info(p_slot); | ||
1719 | } | ||
1720 | } | ||
1721 | } else { | ||
1722 | /* refresh notification */ | ||
1723 | if (p_slot) | ||
1724 | update_slot_info(p_slot); | ||
1725 | } | ||
1726 | |||
1727 | ctrl->event_queue[loop].event_type = 0; | ||
1728 | |||
1729 | change = 1; | ||
1730 | } | ||
1731 | } /* End of FOR loop */ | ||
1732 | } | ||
1733 | } | ||
1734 | |||
1735 | |||
1736 | int pciehp_enable_slot(struct slot *p_slot) | ||
1737 | { | ||
1738 | u8 getstatus = 0; | ||
1739 | int rc; | ||
1740 | struct pci_func *func; | ||
1741 | |||
1742 | func = pciehp_slot_find(p_slot->bus, p_slot->device, 0); | ||
1743 | if (!func) { | ||
1744 | dbg("%s: Error! slot NULL\n", __FUNCTION__); | ||
1745 | return 1; | ||
1746 | } | ||
1747 | |||
1748 | /* Check to see if (latch closed, card present, power off) */ | ||
1749 | down(&p_slot->ctrl->crit_sect); | ||
1750 | |||
1751 | rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus); | ||
1752 | if (rc || !getstatus) { | ||
1753 | info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number); | ||
1754 | up(&p_slot->ctrl->crit_sect); | ||
1755 | return 1; | ||
1756 | } | ||
1757 | if (MRL_SENS(p_slot->ctrl->ctrlcap)) { | ||
1758 | rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); | ||
1759 | if (rc || getstatus) { | ||
1760 | info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number); | ||
1761 | up(&p_slot->ctrl->crit_sect); | ||
1762 | return 1; | ||
1763 | } | ||
1764 | } | ||
1765 | |||
1766 | if (POWER_CTRL(p_slot->ctrl->ctrlcap)) { | ||
1767 | rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus); | ||
1768 | if (rc || getstatus) { | ||
1769 | info("%s: already enabled on slot(%x)\n", __FUNCTION__, p_slot->number); | ||
1770 | up(&p_slot->ctrl->crit_sect); | ||
1771 | return 1; | ||
1772 | } | ||
1773 | } | ||
1774 | up(&p_slot->ctrl->crit_sect); | ||
1775 | |||
1776 | slot_remove(func); | ||
1777 | |||
1778 | func = pciehp_slot_create(p_slot->bus); | ||
1779 | if (func == NULL) | ||
1780 | return 1; | ||
1781 | |||
1782 | func->bus = p_slot->bus; | ||
1783 | func->device = p_slot->device; | ||
1784 | func->function = 0; | ||
1785 | func->configured = 0; | ||
1786 | func->is_a_board = 1; | ||
1787 | |||
1788 | /* We have to save the presence info for these slots */ | ||
1789 | p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save)); | ||
1790 | p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); | ||
1791 | func->switch_save = !getstatus? 0x10:0; | ||
1792 | |||
1793 | rc = board_added(func, p_slot->ctrl); | ||
1794 | if (rc) { | ||
1795 | if (is_bridge(func)) | ||
1796 | bridge_slot_remove(func); | ||
1797 | else | ||
1798 | slot_remove(func); | ||
1799 | |||
1800 | /* Setup slot structure with entry for empty slot */ | ||
1801 | func = pciehp_slot_create(p_slot->bus); | ||
1802 | if (func == NULL) | ||
1803 | return 1; /* Out of memory */ | ||
1804 | |||
1805 | func->bus = p_slot->bus; | ||
1806 | func->device = p_slot->device; | ||
1807 | func->function = 0; | ||
1808 | func->configured = 0; | ||
1809 | func->is_a_board = 1; | ||
1810 | |||
1811 | /* We have to save the presence info for these slots */ | ||
1812 | p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save)); | ||
1813 | p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); | ||
1814 | func->switch_save = !getstatus? 0x10:0; | ||
1815 | } | ||
1816 | |||
1817 | if (p_slot) | ||
1818 | update_slot_info(p_slot); | ||
1819 | |||
1820 | return rc; | ||
1821 | } | ||
1822 | |||
1823 | |||
1824 | int pciehp_disable_slot(struct slot *p_slot) | ||
1825 | { | ||
1826 | u8 class_code, header_type, BCR; | ||
1827 | u8 index = 0; | ||
1828 | u8 getstatus = 0; | ||
1829 | u32 rc = 0; | ||
1830 | int ret = 0; | ||
1831 | unsigned int devfn; | ||
1832 | struct pci_bus *pci_bus = p_slot->ctrl->pci_dev->subordinate; | ||
1833 | struct pci_func *func; | ||
1834 | |||
1835 | if (!p_slot->ctrl) | ||
1836 | return 1; | ||
1837 | |||
1838 | /* Check to see if (latch closed, card present, power on) */ | ||
1839 | down(&p_slot->ctrl->crit_sect); | ||
1840 | |||
1841 | if (!HP_SUPR_RM(p_slot->ctrl->ctrlcap)) { | ||
1842 | ret = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus); | ||
1843 | if (ret || !getstatus) { | ||
1844 | info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number); | ||
1845 | up(&p_slot->ctrl->crit_sect); | ||
1846 | return 1; | ||
1847 | } | ||
1848 | } | ||
1849 | |||
1850 | if (MRL_SENS(p_slot->ctrl->ctrlcap)) { | ||
1851 | ret = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); | ||
1852 | if (ret || getstatus) { | ||
1853 | info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number); | ||
1854 | up(&p_slot->ctrl->crit_sect); | ||
1855 | return 1; | ||
1856 | } | ||
1857 | } | ||
1858 | |||
1859 | if (POWER_CTRL(p_slot->ctrl->ctrlcap)) { | ||
1860 | ret = p_slot->hpc_ops->get_power_status(p_slot, &getstatus); | ||
1861 | if (ret || !getstatus) { | ||
1862 | info("%s: already disabled slot(%x)\n", __FUNCTION__, p_slot->number); | ||
1863 | up(&p_slot->ctrl->crit_sect); | ||
1864 | return 1; | ||
1865 | } | ||
1866 | } | ||
1867 | |||
1868 | up(&p_slot->ctrl->crit_sect); | ||
1869 | |||
1870 | func = pciehp_slot_find(p_slot->bus, p_slot->device, index++); | ||
1871 | |||
1872 | /* Make sure there are no video controllers here | ||
1873 | * for all func of p_slot | ||
1874 | */ | ||
1875 | while (func && !rc) { | ||
1876 | pci_bus->number = func->bus; | ||
1877 | devfn = PCI_DEVFN(func->device, func->function); | ||
1878 | |||
1879 | /* Check the Class Code */ | ||
1880 | rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code); | ||
1881 | if (rc) | ||
1882 | return rc; | ||
1883 | |||
1884 | if (class_code == PCI_BASE_CLASS_DISPLAY) { | ||
1885 | /* Display/Video adapter (not supported) */ | ||
1886 | rc = REMOVE_NOT_SUPPORTED; | ||
1887 | } else { | ||
1888 | /* See if it's a bridge */ | ||
1889 | rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type); | ||
1890 | if (rc) | ||
1891 | return rc; | ||
1892 | |||
1893 | /* If it's a bridge, check the VGA Enable bit */ | ||
1894 | if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { | ||
1895 | rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_BRIDGE_CONTROL, &BCR); | ||
1896 | if (rc) | ||
1897 | return rc; | ||
1898 | |||
1899 | /* If the VGA Enable bit is set, remove isn't supported */ | ||
1900 | if (BCR & PCI_BRIDGE_CTL_VGA) { | ||
1901 | rc = REMOVE_NOT_SUPPORTED; | ||
1902 | } | ||
1903 | } | ||
1904 | } | ||
1905 | |||
1906 | func = pciehp_slot_find(p_slot->bus, p_slot->device, index++); | ||
1907 | } | ||
1908 | |||
1909 | func = pciehp_slot_find(p_slot->bus, p_slot->device, 0); | ||
1910 | if ((func != NULL) && !rc) { | ||
1911 | rc = remove_board(func, p_slot->ctrl); | ||
1912 | } else if (!rc) | ||
1913 | rc = 1; | ||
1914 | |||
1915 | if (p_slot) | ||
1916 | update_slot_info(p_slot); | ||
1917 | |||
1918 | return rc; | ||
1919 | } | ||
1920 | |||
1921 | |||
1922 | /** | ||
1923 | * configure_new_device - Configures the PCI header information of one board. | ||
1924 | * | ||
1925 | * @ctrl: pointer to controller structure | ||
1926 | * @func: pointer to function structure | ||
1927 | * @behind_bridge: 1 if this is a recursive call, 0 if not | ||
1928 | * @resources: pointer to set of resource lists | ||
1929 | * | ||
1930 | * Returns 0 if success | ||
1931 | * | ||
1932 | */ | ||
1933 | static u32 configure_new_device(struct controller * ctrl, struct pci_func * func, | ||
1934 | u8 behind_bridge, struct resource_lists * resources, u8 bridge_bus, u8 bridge_dev) | ||
1935 | { | ||
1936 | u8 temp_byte, function, max_functions, stop_it; | ||
1937 | int rc; | ||
1938 | u32 ID; | ||
1939 | struct pci_func *new_slot; | ||
1940 | struct pci_bus lpci_bus, *pci_bus; | ||
1941 | int index; | ||
1942 | |||
1943 | new_slot = func; | ||
1944 | |||
1945 | dbg("%s\n", __FUNCTION__); | ||
1946 | memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus)); | ||
1947 | pci_bus = &lpci_bus; | ||
1948 | pci_bus->number = func->bus; | ||
1949 | |||
1950 | /* Check for Multi-function device */ | ||
1951 | rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(func->device, func->function), 0x0E, &temp_byte); | ||
1952 | if (rc) { | ||
1953 | dbg("%s: rc = %d\n", __FUNCTION__, rc); | ||
1954 | return rc; | ||
1955 | } | ||
1956 | |||
1957 | if (temp_byte & 0x80) /* Multi-function device */ | ||
1958 | max_functions = 8; | ||
1959 | else | ||
1960 | max_functions = 1; | ||
1961 | |||
1962 | function = 0; | ||
1963 | |||
1964 | do { | ||
1965 | rc = configure_new_function(ctrl, new_slot, behind_bridge, | ||
1966 | resources, bridge_bus, bridge_dev); | ||
1967 | |||
1968 | if (rc) { | ||
1969 | dbg("configure_new_function failed: %d\n", rc); | ||
1970 | index = 0; | ||
1971 | |||
1972 | while (new_slot) { | ||
1973 | new_slot = pciehp_slot_find(new_slot->bus, | ||
1974 | new_slot->device, index++); | ||
1975 | |||
1976 | if (new_slot) | ||
1977 | pciehp_return_board_resources(new_slot, | ||
1978 | resources); | ||
1979 | } | ||
1980 | |||
1981 | return rc; | ||
1982 | } | ||
1983 | |||
1984 | function++; | ||
1985 | |||
1986 | stop_it = 0; | ||
1987 | |||
1988 | /* The following loop skips to the next present function | ||
1989 | * and creates a board structure | ||
1990 | */ | ||
1991 | |||
1992 | while ((function < max_functions) && (!stop_it)) { | ||
1993 | pci_bus_read_config_dword(pci_bus, PCI_DEVFN(func->device, function), 0x00, &ID); | ||
1994 | |||
1995 | if (ID == 0xFFFFFFFF) { /* There's nothing there. */ | ||
1996 | function++; | ||
1997 | } else { /* There's something there */ | ||
1998 | /* Setup slot structure. */ | ||
1999 | new_slot = pciehp_slot_create(func->bus); | ||
2000 | |||
2001 | if (new_slot == NULL) { | ||
2002 | /* Out of memory */ | ||
2003 | return 1; | ||
2004 | } | ||
2005 | |||
2006 | new_slot->bus = func->bus; | ||
2007 | new_slot->device = func->device; | ||
2008 | new_slot->function = function; | ||
2009 | new_slot->is_a_board = 1; | ||
2010 | new_slot->status = 0; | ||
2011 | |||
2012 | stop_it++; | ||
2013 | } | ||
2014 | } | ||
2015 | |||
2016 | } while (function < max_functions); | ||
2017 | dbg("returning from %s\n", __FUNCTION__); | ||
2018 | |||
2019 | return 0; | ||
2020 | } | ||
2021 | |||
2022 | /* | ||
2023 | * Configuration logic that involves the hotplug data structures and | ||
2024 | * their bookkeeping | ||
2025 | */ | ||
2026 | |||
2027 | /** | ||
2028 | * configure_bridge: fill bridge's registers, either configure or disable it. | ||
2029 | */ | ||
2030 | static int | ||
2031 | configure_bridge(struct pci_bus *pci_bus, unsigned int devfn, | ||
2032 | struct pci_resource *mem_node, | ||
2033 | struct pci_resource **hold_mem_node, | ||
2034 | int base_addr, int limit_addr) | ||
2035 | { | ||
2036 | u16 temp_word; | ||
2037 | u32 rc; | ||
2038 | |||
2039 | if (mem_node) { | ||
2040 | memcpy(*hold_mem_node, mem_node, sizeof(struct pci_resource)); | ||
2041 | mem_node->next = NULL; | ||
2042 | |||
2043 | /* set Mem base and Limit registers */ | ||
2044 | RES_CHECK(mem_node->base, 16); | ||
2045 | temp_word = (u16)(mem_node->base >> 16); | ||
2046 | rc = pci_bus_write_config_word(pci_bus, devfn, base_addr, temp_word); | ||
2047 | |||
2048 | RES_CHECK(mem_node->base + mem_node->length - 1, 16); | ||
2049 | temp_word = (u16)((mem_node->base + mem_node->length - 1) >> 16); | ||
2050 | rc = pci_bus_write_config_word(pci_bus, devfn, limit_addr, temp_word); | ||
2051 | } else { | ||
2052 | temp_word = 0xFFFF; | ||
2053 | rc = pci_bus_write_config_word(pci_bus, devfn, base_addr, temp_word); | ||
2054 | |||
2055 | temp_word = 0x0000; | ||
2056 | rc = pci_bus_write_config_word(pci_bus, devfn, limit_addr, temp_word); | ||
2057 | |||
2058 | kfree(*hold_mem_node); | ||
2059 | *hold_mem_node = NULL; | ||
2060 | } | ||
2061 | return rc; | ||
2062 | } | ||
2063 | |||
2064 | static int | ||
2065 | configure_new_bridge(struct controller *ctrl, struct pci_func *func, | ||
2066 | u8 behind_bridge, struct resource_lists *resources, | ||
2067 | struct pci_bus *pci_bus) | ||
2068 | { | ||
2069 | int cloop; | ||
2070 | u8 temp_byte; | ||
2071 | u8 device; | ||
2072 | u16 temp_word; | ||
2073 | u32 rc; | ||
2074 | u32 ID; | ||
2075 | unsigned int devfn; | ||
2076 | struct pci_resource *mem_node; | ||
2077 | struct pci_resource *p_mem_node; | ||
2078 | struct pci_resource *io_node; | ||
2079 | struct pci_resource *bus_node; | ||
2080 | struct pci_resource *hold_mem_node; | ||
2081 | struct pci_resource *hold_p_mem_node; | ||
2082 | struct pci_resource *hold_IO_node; | ||
2083 | struct pci_resource *hold_bus_node; | ||
2084 | struct irq_mapping irqs; | ||
2085 | struct pci_func *new_slot; | ||
2086 | struct resource_lists temp_resources; | ||
2087 | |||
2088 | devfn = PCI_DEVFN(func->device, func->function); | ||
2089 | |||
2090 | /* set Primary bus */ | ||
2091 | dbg("set Primary bus = 0x%x\n", func->bus); | ||
2092 | rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_PRIMARY_BUS, func->bus); | ||
2093 | if (rc) | ||
2094 | return rc; | ||
2095 | |||
2096 | /* find range of busses to use */ | ||
2097 | bus_node = get_max_resource(&resources->bus_head, 1L); | ||
2098 | |||
2099 | /* If we don't have any busses to allocate, we can't continue */ | ||
2100 | if (!bus_node) { | ||
2101 | err("Got NO bus resource to use\n"); | ||
2102 | return -ENOMEM; | ||
2103 | } | ||
2104 | dbg("Got ranges of buses to use: base:len=0x%x:%x\n", bus_node->base, bus_node->length); | ||
2105 | |||
2106 | /* set Secondary bus */ | ||
2107 | temp_byte = (u8)bus_node->base; | ||
2108 | dbg("set Secondary bus = 0x%x\n", temp_byte); | ||
2109 | rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, temp_byte); | ||
2110 | if (rc) | ||
2111 | return rc; | ||
2112 | |||
2113 | /* set subordinate bus */ | ||
2114 | temp_byte = (u8)(bus_node->base + bus_node->length - 1); | ||
2115 | dbg("set subordinate bus = 0x%x\n", temp_byte); | ||
2116 | rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte); | ||
2117 | if (rc) | ||
2118 | return rc; | ||
2119 | |||
2120 | /* Set HP parameters (Cache Line Size, Latency Timer) */ | ||
2121 | rc = pciehprm_set_hpp(ctrl, func, PCI_HEADER_TYPE_BRIDGE); | ||
2122 | if (rc) | ||
2123 | return rc; | ||
2124 | |||
2125 | /* Setup the IO, memory, and prefetchable windows */ | ||
2126 | |||
2127 | io_node = get_max_resource(&(resources->io_head), 0x1000L); | ||
2128 | if (io_node) { | ||
2129 | dbg("io_node(base, len, next) (%x, %x, %p)\n", io_node->base, | ||
2130 | io_node->length, io_node->next); | ||
2131 | } | ||
2132 | |||
2133 | mem_node = get_max_resource(&(resources->mem_head), 0x100000L); | ||
2134 | if (mem_node) { | ||
2135 | dbg("mem_node(base, len, next) (%x, %x, %p)\n", mem_node->base, | ||
2136 | mem_node->length, mem_node->next); | ||
2137 | } | ||
2138 | |||
2139 | if (resources->p_mem_head) | ||
2140 | p_mem_node = get_max_resource(&(resources->p_mem_head), 0x100000L); | ||
2141 | else { | ||
2142 | /* | ||
2143 | * In some platform implementation, MEM and PMEM are not | ||
2144 | * distinguished, and hence ACPI _CRS has only MEM entries | ||
2145 | * for both MEM and PMEM. | ||
2146 | */ | ||
2147 | dbg("using MEM for PMEM\n"); | ||
2148 | p_mem_node = get_max_resource(&(resources->mem_head), 0x100000L); | ||
2149 | } | ||
2150 | if (p_mem_node) { | ||
2151 | dbg("p_mem_node(base, len, next) (%x, %x, %p)\n", p_mem_node->base, | ||
2152 | p_mem_node->length, p_mem_node->next); | ||
2153 | } | ||
2154 | |||
2155 | /* set up the IRQ info */ | ||
2156 | if (!resources->irqs) { | ||
2157 | irqs.barber_pole = 0; | ||
2158 | irqs.interrupt[0] = 0; | ||
2159 | irqs.interrupt[1] = 0; | ||
2160 | irqs.interrupt[2] = 0; | ||
2161 | irqs.interrupt[3] = 0; | ||
2162 | irqs.valid_INT = 0; | ||
2163 | } else { | ||
2164 | irqs.barber_pole = resources->irqs->barber_pole; | ||
2165 | irqs.interrupt[0] = resources->irqs->interrupt[0]; | ||
2166 | irqs.interrupt[1] = resources->irqs->interrupt[1]; | ||
2167 | irqs.interrupt[2] = resources->irqs->interrupt[2]; | ||
2168 | irqs.interrupt[3] = resources->irqs->interrupt[3]; | ||
2169 | irqs.valid_INT = resources->irqs->valid_INT; | ||
2170 | } | ||
2171 | |||
2172 | /* set up resource lists that are now aligned on top and bottom | ||
2173 | * for anything behind the bridge. | ||
2174 | */ | ||
2175 | temp_resources.bus_head = bus_node; | ||
2176 | temp_resources.io_head = io_node; | ||
2177 | temp_resources.mem_head = mem_node; | ||
2178 | temp_resources.p_mem_head = p_mem_node; | ||
2179 | temp_resources.irqs = &irqs; | ||
2180 | |||
2181 | /* Make copies of the nodes we are going to pass down so that | ||
2182 | * if there is a problem,we can just use these to free resources | ||
2183 | */ | ||
2184 | hold_bus_node = kmalloc(sizeof(struct pci_resource), GFP_KERNEL); | ||
2185 | hold_IO_node = kmalloc(sizeof(struct pci_resource), GFP_KERNEL); | ||
2186 | hold_mem_node = kmalloc(sizeof(struct pci_resource), GFP_KERNEL); | ||
2187 | hold_p_mem_node = kmalloc(sizeof(struct pci_resource), GFP_KERNEL); | ||
2188 | |||
2189 | if (!hold_bus_node || !hold_IO_node || !hold_mem_node || !hold_p_mem_node) { | ||
2190 | kfree(hold_bus_node); | ||
2191 | kfree(hold_IO_node); | ||
2192 | kfree(hold_mem_node); | ||
2193 | kfree(hold_p_mem_node); | ||
2194 | |||
2195 | return 1; | ||
2196 | } | ||
2197 | |||
2198 | memcpy(hold_bus_node, bus_node, sizeof(struct pci_resource)); | ||
2199 | |||
2200 | bus_node->base += 1; | ||
2201 | bus_node->length -= 1; | ||
2202 | bus_node->next = NULL; | ||
2203 | |||
2204 | /* If we have IO resources copy them and fill in the bridge's | ||
2205 | * IO range registers | ||
2206 | */ | ||
2207 | if (io_node) { | ||
2208 | memcpy(hold_IO_node, io_node, sizeof(struct pci_resource)); | ||
2209 | io_node->next = NULL; | ||
2210 | |||
2211 | /* set IO base and Limit registers */ | ||
2212 | RES_CHECK(io_node->base, 8); | ||
2213 | temp_byte = (u8)(io_node->base >> 8); | ||
2214 | rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_BASE, temp_byte); | ||
2215 | |||
2216 | RES_CHECK(io_node->base + io_node->length - 1, 8); | ||
2217 | temp_byte = (u8)((io_node->base + io_node->length - 1) >> 8); | ||
2218 | rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_LIMIT, temp_byte); | ||
2219 | } else { | ||
2220 | kfree(hold_IO_node); | ||
2221 | hold_IO_node = NULL; | ||
2222 | } | ||
2223 | |||
2224 | /* If we have memory resources copy them and fill in the bridge's | ||
2225 | * memory range registers. Otherwise, fill in the range | ||
2226 | * registers with values that disable them. | ||
2227 | */ | ||
2228 | rc = configure_bridge(pci_bus, devfn, mem_node, &hold_mem_node, | ||
2229 | PCI_MEMORY_BASE, PCI_MEMORY_LIMIT); | ||
2230 | |||
2231 | /* If we have prefetchable memory resources copy them and | ||
2232 | * fill in the bridge's memory range registers. Otherwise, | ||
2233 | * fill in the range registers with values that disable them. | ||
2234 | */ | ||
2235 | rc = configure_bridge(pci_bus, devfn, p_mem_node, &hold_p_mem_node, | ||
2236 | PCI_PREF_MEMORY_BASE, PCI_PREF_MEMORY_LIMIT); | ||
2237 | |||
2238 | /* Adjust this to compensate for extra adjustment in first loop */ | ||
2239 | irqs.barber_pole--; | ||
2240 | |||
2241 | rc = 0; | ||
2242 | |||
2243 | /* Here we actually find the devices and configure them */ | ||
2244 | for (device = 0; (device <= 0x1F) && !rc; device++) { | ||
2245 | irqs.barber_pole = (irqs.barber_pole + 1) & 0x03; | ||
2246 | |||
2247 | ID = 0xFFFFFFFF; | ||
2248 | pci_bus->number = hold_bus_node->base; | ||
2249 | pci_bus_read_config_dword (pci_bus, PCI_DEVFN(device, 0), PCI_VENDOR_ID, &ID); | ||
2250 | pci_bus->number = func->bus; | ||
2251 | |||
2252 | if (ID != 0xFFFFFFFF) { /* device Present */ | ||
2253 | /* Setup slot structure. */ | ||
2254 | new_slot = pciehp_slot_create(hold_bus_node->base); | ||
2255 | |||
2256 | if (new_slot == NULL) { | ||
2257 | /* Out of memory */ | ||
2258 | rc = -ENOMEM; | ||
2259 | continue; | ||
2260 | } | ||
2261 | |||
2262 | new_slot->bus = hold_bus_node->base; | ||
2263 | new_slot->device = device; | ||
2264 | new_slot->function = 0; | ||
2265 | new_slot->is_a_board = 1; | ||
2266 | new_slot->status = 0; | ||
2267 | |||
2268 | rc = configure_new_device(ctrl, new_slot, 1, | ||
2269 | &temp_resources, func->bus, | ||
2270 | func->device); | ||
2271 | dbg("configure_new_device rc=0x%x\n",rc); | ||
2272 | } /* End of IF (device in slot?) */ | ||
2273 | } /* End of FOR loop */ | ||
2274 | |||
2275 | if (rc) { | ||
2276 | pciehp_destroy_resource_list(&temp_resources); | ||
2277 | |||
2278 | return_resource(&(resources->bus_head), hold_bus_node); | ||
2279 | return_resource(&(resources->io_head), hold_IO_node); | ||
2280 | return_resource(&(resources->mem_head), hold_mem_node); | ||
2281 | return_resource(&(resources->p_mem_head), hold_p_mem_node); | ||
2282 | return(rc); | ||
2283 | } | ||
2284 | |||
2285 | /* save the interrupt routing information */ | ||
2286 | if (resources->irqs) { | ||
2287 | resources->irqs->interrupt[0] = irqs.interrupt[0]; | ||
2288 | resources->irqs->interrupt[1] = irqs.interrupt[1]; | ||
2289 | resources->irqs->interrupt[2] = irqs.interrupt[2]; | ||
2290 | resources->irqs->interrupt[3] = irqs.interrupt[3]; | ||
2291 | resources->irqs->valid_INT = irqs.valid_INT; | ||
2292 | } else if (!behind_bridge) { | ||
2293 | /* We need to hook up the interrupts here */ | ||
2294 | for (cloop = 0; cloop < 4; cloop++) { | ||
2295 | if (irqs.valid_INT & (0x01 << cloop)) { | ||
2296 | rc = pciehp_set_irq(func->bus, func->device, | ||
2297 | 0x0A + cloop, irqs.interrupt[cloop]); | ||
2298 | if (rc) { | ||
2299 | pciehp_destroy_resource_list (&temp_resources); | ||
2300 | return_resource(&(resources->bus_head), hold_bus_node); | ||
2301 | return_resource(&(resources->io_head), hold_IO_node); | ||
2302 | return_resource(&(resources->mem_head), hold_mem_node); | ||
2303 | return_resource(&(resources->p_mem_head), hold_p_mem_node); | ||
2304 | return rc; | ||
2305 | } | ||
2306 | } | ||
2307 | } /* end of for loop */ | ||
2308 | } | ||
2309 | |||
2310 | /* Return unused bus resources | ||
2311 | * First use the temporary node to store information for the board | ||
2312 | */ | ||
2313 | if (hold_bus_node && bus_node && temp_resources.bus_head) { | ||
2314 | hold_bus_node->length = bus_node->base - hold_bus_node->base; | ||
2315 | |||
2316 | hold_bus_node->next = func->bus_head; | ||
2317 | func->bus_head = hold_bus_node; | ||
2318 | |||
2319 | temp_byte = (u8)(temp_resources.bus_head->base - 1); | ||
2320 | |||
2321 | /* set subordinate bus */ | ||
2322 | dbg("re-set subordinate bus = 0x%x\n", temp_byte); | ||
2323 | rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte); | ||
2324 | |||
2325 | if (temp_resources.bus_head->length == 0) { | ||
2326 | kfree(temp_resources.bus_head); | ||
2327 | temp_resources.bus_head = NULL; | ||
2328 | } else { | ||
2329 | dbg("return bus res of b:d(0x%x:%x) base:len(0x%x:%x)\n", | ||
2330 | func->bus, func->device, temp_resources.bus_head->base, temp_resources.bus_head->length); | ||
2331 | return_resource(&(resources->bus_head), temp_resources.bus_head); | ||
2332 | } | ||
2333 | } | ||
2334 | |||
2335 | /* If we have IO space available and there is some left, | ||
2336 | * return the unused portion | ||
2337 | */ | ||
2338 | if (hold_IO_node && temp_resources.io_head) { | ||
2339 | io_node = do_pre_bridge_resource_split(&(temp_resources.io_head), | ||
2340 | &hold_IO_node, 0x1000); | ||
2341 | |||
2342 | /* Check if we were able to split something off */ | ||
2343 | if (io_node) { | ||
2344 | hold_IO_node->base = io_node->base + io_node->length; | ||
2345 | |||
2346 | RES_CHECK(hold_IO_node->base, 8); | ||
2347 | temp_byte = (u8)((hold_IO_node->base) >> 8); | ||
2348 | rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_BASE, temp_byte); | ||
2349 | |||
2350 | return_resource(&(resources->io_head), io_node); | ||
2351 | } | ||
2352 | |||
2353 | io_node = do_bridge_resource_split(&(temp_resources.io_head), 0x1000); | ||
2354 | |||
2355 | /* Check if we were able to split something off */ | ||
2356 | if (io_node) { | ||
2357 | /* First use the temporary node to store information for the board */ | ||
2358 | hold_IO_node->length = io_node->base - hold_IO_node->base; | ||
2359 | |||
2360 | /* If we used any, add it to the board's list */ | ||
2361 | if (hold_IO_node->length) { | ||
2362 | hold_IO_node->next = func->io_head; | ||
2363 | func->io_head = hold_IO_node; | ||
2364 | |||
2365 | RES_CHECK(io_node->base - 1, 8); | ||
2366 | temp_byte = (u8)((io_node->base - 1) >> 8); | ||
2367 | rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_LIMIT, temp_byte); | ||
2368 | |||
2369 | return_resource(&(resources->io_head), io_node); | ||
2370 | } else { | ||
2371 | /* it doesn't need any IO */ | ||
2372 | temp_byte = 0x00; | ||
2373 | rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_LIMIT, temp_byte); | ||
2374 | |||
2375 | return_resource(&(resources->io_head), io_node); | ||
2376 | kfree(hold_IO_node); | ||
2377 | } | ||
2378 | } else { | ||
2379 | /* it used most of the range */ | ||
2380 | hold_IO_node->next = func->io_head; | ||
2381 | func->io_head = hold_IO_node; | ||
2382 | } | ||
2383 | } else if (hold_IO_node) { | ||
2384 | /* it used the whole range */ | ||
2385 | hold_IO_node->next = func->io_head; | ||
2386 | func->io_head = hold_IO_node; | ||
2387 | } | ||
2388 | |||
2389 | /* If we have memory space available and there is some left, | ||
2390 | * return the unused portion | ||
2391 | */ | ||
2392 | if (hold_mem_node && temp_resources.mem_head) { | ||
2393 | mem_node = do_pre_bridge_resource_split(&(temp_resources.mem_head), &hold_mem_node, 0x100000L); | ||
2394 | |||
2395 | /* Check if we were able to split something off */ | ||
2396 | if (mem_node) { | ||
2397 | hold_mem_node->base = mem_node->base + mem_node->length; | ||
2398 | |||
2399 | RES_CHECK(hold_mem_node->base, 16); | ||
2400 | temp_word = (u16)((hold_mem_node->base) >> 16); | ||
2401 | rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_BASE, temp_word); | ||
2402 | |||
2403 | return_resource(&(resources->mem_head), mem_node); | ||
2404 | } | ||
2405 | |||
2406 | mem_node = do_bridge_resource_split(&(temp_resources.mem_head), 0x100000L); | ||
2407 | |||
2408 | /* Check if we were able to split something off */ | ||
2409 | if (mem_node) { | ||
2410 | /* First use the temporary node to store information for the board */ | ||
2411 | hold_mem_node->length = mem_node->base - hold_mem_node->base; | ||
2412 | |||
2413 | if (hold_mem_node->length) { | ||
2414 | hold_mem_node->next = func->mem_head; | ||
2415 | func->mem_head = hold_mem_node; | ||
2416 | |||
2417 | /* configure end address */ | ||
2418 | RES_CHECK(mem_node->base - 1, 16); | ||
2419 | temp_word = (u16)((mem_node->base - 1) >> 16); | ||
2420 | rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); | ||
2421 | |||
2422 | /* Return unused resources to the pool */ | ||
2423 | return_resource(&(resources->mem_head), mem_node); | ||
2424 | } else { | ||
2425 | /* it doesn't need any Mem */ | ||
2426 | temp_word = 0x0000; | ||
2427 | rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); | ||
2428 | |||
2429 | return_resource(&(resources->mem_head), mem_node); | ||
2430 | kfree(hold_mem_node); | ||
2431 | } | ||
2432 | } else { | ||
2433 | /* it used most of the range */ | ||
2434 | hold_mem_node->next = func->mem_head; | ||
2435 | func->mem_head = hold_mem_node; | ||
2436 | } | ||
2437 | } else if (hold_mem_node) { | ||
2438 | /* it used the whole range */ | ||
2439 | hold_mem_node->next = func->mem_head; | ||
2440 | func->mem_head = hold_mem_node; | ||
2441 | } | ||
2442 | |||
2443 | /* If we have prefetchable memory space available and there is some | ||
2444 | * left at the end, return the unused portion | ||
2445 | */ | ||
2446 | if (hold_p_mem_node && temp_resources.p_mem_head) { | ||
2447 | p_mem_node = do_pre_bridge_resource_split(&(temp_resources.p_mem_head), | ||
2448 | &hold_p_mem_node, 0x100000L); | ||
2449 | |||
2450 | /* Check if we were able to split something off */ | ||
2451 | if (p_mem_node) { | ||
2452 | hold_p_mem_node->base = p_mem_node->base + p_mem_node->length; | ||
2453 | |||
2454 | RES_CHECK(hold_p_mem_node->base, 16); | ||
2455 | temp_word = (u16)((hold_p_mem_node->base) >> 16); | ||
2456 | rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word); | ||
2457 | |||
2458 | return_resource(&(resources->p_mem_head), p_mem_node); | ||
2459 | } | ||
2460 | |||
2461 | p_mem_node = do_bridge_resource_split(&(temp_resources.p_mem_head), 0x100000L); | ||
2462 | |||
2463 | /* Check if we were able to split something off */ | ||
2464 | if (p_mem_node) { | ||
2465 | /* First use the temporary node to store information for the board */ | ||
2466 | hold_p_mem_node->length = p_mem_node->base - hold_p_mem_node->base; | ||
2467 | |||
2468 | /* If we used any, add it to the board's list */ | ||
2469 | if (hold_p_mem_node->length) { | ||
2470 | hold_p_mem_node->next = func->p_mem_head; | ||
2471 | func->p_mem_head = hold_p_mem_node; | ||
2472 | |||
2473 | RES_CHECK(p_mem_node->base - 1, 16); | ||
2474 | temp_word = (u16)((p_mem_node->base - 1) >> 16); | ||
2475 | rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); | ||
2476 | |||
2477 | return_resource(&(resources->p_mem_head), p_mem_node); | ||
2478 | } else { | ||
2479 | /* it doesn't need any PMem */ | ||
2480 | temp_word = 0x0000; | ||
2481 | rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); | ||
2482 | |||
2483 | return_resource(&(resources->p_mem_head), p_mem_node); | ||
2484 | kfree(hold_p_mem_node); | ||
2485 | } | ||
2486 | } else { | ||
2487 | /* it used the most of the range */ | ||
2488 | hold_p_mem_node->next = func->p_mem_head; | ||
2489 | func->p_mem_head = hold_p_mem_node; | ||
2490 | } | ||
2491 | } else if (hold_p_mem_node) { | ||
2492 | /* it used the whole range */ | ||
2493 | hold_p_mem_node->next = func->p_mem_head; | ||
2494 | func->p_mem_head = hold_p_mem_node; | ||
2495 | } | ||
2496 | |||
2497 | /* We should be configuring an IRQ and the bridge's base address | ||
2498 | * registers if it needs them. Although we have never seen such | ||
2499 | * a device | ||
2500 | */ | ||
2501 | |||
2502 | pciehprm_enable_card(ctrl, func, PCI_HEADER_TYPE_BRIDGE); | ||
2503 | |||
2504 | dbg("PCI Bridge Hot-Added s:b:d:f(%02x:%02x:%02x:%02x)\n", ctrl->seg, func->bus, func->device, func->function); | ||
2505 | |||
2506 | return rc; | ||
2507 | } | ||
2508 | |||
2509 | /** | ||
2510 | * configure_new_function - Configures the PCI header information of one device | ||
2511 | * | ||
2512 | * @ctrl: pointer to controller structure | ||
2513 | * @func: pointer to function structure | ||
2514 | * @behind_bridge: 1 if this is a recursive call, 0 if not | ||
2515 | * @resources: pointer to set of resource lists | ||
2516 | * | ||
2517 | * Calls itself recursively for bridged devices. | ||
2518 | * Returns 0 if success | ||
2519 | * | ||
2520 | */ | ||
2521 | static int | ||
2522 | configure_new_function(struct controller *ctrl, struct pci_func *func, | ||
2523 | u8 behind_bridge, struct resource_lists *resources, | ||
2524 | u8 bridge_bus, u8 bridge_dev) | ||
2525 | { | ||
2526 | int cloop; | ||
2527 | u8 temp_byte; | ||
2528 | u8 class_code; | ||
2529 | u16 temp_word; | ||
2530 | u32 rc; | ||
2531 | u32 temp_register; | ||
2532 | u32 base; | ||
2533 | unsigned int devfn; | ||
2534 | struct pci_resource *mem_node; | ||
2535 | struct pci_resource *io_node; | ||
2536 | struct pci_bus lpci_bus, *pci_bus; | ||
2537 | |||
2538 | memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus)); | ||
2539 | pci_bus = &lpci_bus; | ||
2540 | pci_bus->number = func->bus; | ||
2541 | devfn = PCI_DEVFN(func->device, func->function); | ||
2542 | |||
2543 | /* Check for Bridge */ | ||
2544 | rc = pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &temp_byte); | ||
2545 | if (rc) | ||
2546 | return rc; | ||
2547 | dbg("%s: bus %x dev %x func %x temp_byte = %x\n", __FUNCTION__, | ||
2548 | func->bus, func->device, func->function, temp_byte); | ||
2549 | |||
2550 | if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* PCI-PCI Bridge */ | ||
2551 | rc = configure_new_bridge(ctrl, func, behind_bridge, resources, | ||
2552 | pci_bus); | ||
2553 | |||
2554 | if (rc) | ||
2555 | return rc; | ||
2556 | } else if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_NORMAL) { | ||
2557 | /* Standard device */ | ||
2558 | u64 base64; | ||
2559 | rc = pci_bus_read_config_byte(pci_bus, devfn, 0x0B, &class_code); | ||
2560 | |||
2561 | if (class_code == PCI_BASE_CLASS_DISPLAY) | ||
2562 | return DEVICE_TYPE_NOT_SUPPORTED; | ||
2563 | |||
2564 | /* Figure out IO and memory needs */ | ||
2565 | for (cloop = PCI_BASE_ADDRESS_0; cloop <= PCI_BASE_ADDRESS_5; cloop += 4) { | ||
2566 | temp_register = 0xFFFFFFFF; | ||
2567 | |||
2568 | rc = pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register); | ||
2569 | rc = pci_bus_read_config_dword(pci_bus, devfn, cloop, &temp_register); | ||
2570 | dbg("Bar[%x]=0x%x on bus:dev:func(0x%x:%x:%x)\n", cloop, temp_register, | ||
2571 | func->bus, func->device, func->function); | ||
2572 | |||
2573 | if (!temp_register) | ||
2574 | continue; | ||
2575 | |||
2576 | base64 = 0L; | ||
2577 | if (temp_register & PCI_BASE_ADDRESS_SPACE_IO) { | ||
2578 | /* Map IO */ | ||
2579 | |||
2580 | /* set base = amount of IO space */ | ||
2581 | base = temp_register & 0xFFFFFFFC; | ||
2582 | base = ~base + 1; | ||
2583 | |||
2584 | dbg("NEED IO length(0x%x)\n", base); | ||
2585 | io_node = get_io_resource(&(resources->io_head),(ulong)base); | ||
2586 | |||
2587 | /* allocate the resource to the board */ | ||
2588 | if (io_node) { | ||
2589 | dbg("Got IO base=0x%x(length=0x%x)\n", io_node->base, io_node->length); | ||
2590 | base = (u32)io_node->base; | ||
2591 | io_node->next = func->io_head; | ||
2592 | func->io_head = io_node; | ||
2593 | } else { | ||
2594 | err("Got NO IO resource(length=0x%x)\n", base); | ||
2595 | return -ENOMEM; | ||
2596 | } | ||
2597 | } else { /* map MEM */ | ||
2598 | int prefetchable = 1; | ||
2599 | struct pci_resource **res_node = &func->p_mem_head; | ||
2600 | char *res_type_str = "PMEM"; | ||
2601 | u32 temp_register2; | ||
2602 | |||
2603 | if (!(temp_register & PCI_BASE_ADDRESS_MEM_PREFETCH)) { | ||
2604 | prefetchable = 0; | ||
2605 | res_node = &func->mem_head; | ||
2606 | res_type_str++; | ||
2607 | } | ||
2608 | |||
2609 | base = temp_register & 0xFFFFFFF0; | ||
2610 | base = ~base + 1; | ||
2611 | |||
2612 | switch (temp_register & PCI_BASE_ADDRESS_MEM_TYPE_MASK) { | ||
2613 | case PCI_BASE_ADDRESS_MEM_TYPE_32: | ||
2614 | dbg("NEED 32 %s bar=0x%x(length=0x%x)\n", res_type_str, temp_register, base); | ||
2615 | |||
2616 | if (prefetchable && resources->p_mem_head) | ||
2617 | mem_node=get_resource(&(resources->p_mem_head), (ulong)base); | ||
2618 | else { | ||
2619 | if (prefetchable) | ||
2620 | dbg("using MEM for PMEM\n"); | ||
2621 | mem_node = get_resource(&(resources->mem_head), (ulong)base); | ||
2622 | } | ||
2623 | |||
2624 | /* allocate the resource to the board */ | ||
2625 | if (mem_node) { | ||
2626 | base = (u32)mem_node->base; | ||
2627 | mem_node->next = *res_node; | ||
2628 | *res_node = mem_node; | ||
2629 | dbg("Got 32 %s base=0x%x(length=0x%x)\n", res_type_str, mem_node->base, | ||
2630 | mem_node->length); | ||
2631 | } else { | ||
2632 | err("Got NO 32 %s resource(length=0x%x)\n", res_type_str, base); | ||
2633 | return -ENOMEM; | ||
2634 | } | ||
2635 | break; | ||
2636 | case PCI_BASE_ADDRESS_MEM_TYPE_64: | ||
2637 | rc = pci_bus_read_config_dword(pci_bus, devfn, cloop+4, &temp_register2); | ||
2638 | dbg("NEED 64 %s bar=0x%x:%x(length=0x%x)\n", res_type_str, temp_register2, | ||
2639 | temp_register, base); | ||
2640 | |||
2641 | if (prefetchable && resources->p_mem_head) | ||
2642 | mem_node = get_resource(&(resources->p_mem_head), (ulong)base); | ||
2643 | else { | ||
2644 | if (prefetchable) | ||
2645 | dbg("using MEM for PMEM\n"); | ||
2646 | mem_node = get_resource(&(resources->mem_head), (ulong)base); | ||
2647 | } | ||
2648 | |||
2649 | /* allocate the resource to the board */ | ||
2650 | if (mem_node) { | ||
2651 | base64 = mem_node->base; | ||
2652 | mem_node->next = *res_node; | ||
2653 | *res_node = mem_node; | ||
2654 | dbg("Got 64 %s base=0x%x:%x(length=%x)\n", res_type_str, (u32)(base64 >> 32), | ||
2655 | (u32)base64, mem_node->length); | ||
2656 | } else { | ||
2657 | err("Got NO 64 %s resource(length=0x%x)\n", res_type_str, base); | ||
2658 | return -ENOMEM; | ||
2659 | } | ||
2660 | break; | ||
2661 | default: | ||
2662 | dbg("reserved BAR type=0x%x\n", temp_register); | ||
2663 | break; | ||
2664 | } | ||
2665 | |||
2666 | } | ||
2667 | |||
2668 | if (base64) { | ||
2669 | rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, (u32)base64); | ||
2670 | cloop += 4; | ||
2671 | base64 >>= 32; | ||
2672 | |||
2673 | if (base64) { | ||
2674 | dbg("%s: high dword of base64(0x%x) set to 0\n", __FUNCTION__, (u32)base64); | ||
2675 | base64 = 0x0L; | ||
2676 | } | ||
2677 | |||
2678 | rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, (u32)base64); | ||
2679 | } else { | ||
2680 | rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, base); | ||
2681 | } | ||
2682 | } /* End of base register loop */ | ||
2683 | |||
2684 | /* disable ROM base Address */ | ||
2685 | temp_word = 0x00L; | ||
2686 | rc = pci_bus_write_config_word (pci_bus, devfn, PCI_ROM_ADDRESS, temp_word); | ||
2687 | |||
2688 | /* Set HP parameters (Cache Line Size, Latency Timer) */ | ||
2689 | rc = pciehprm_set_hpp(ctrl, func, PCI_HEADER_TYPE_NORMAL); | ||
2690 | if (rc) | ||
2691 | return rc; | ||
2692 | |||
2693 | pciehprm_enable_card(ctrl, func, PCI_HEADER_TYPE_NORMAL); | ||
2694 | |||
2695 | dbg("PCI function Hot-Added s:b:d:f(%02x:%02x:%02x:%02x)\n", ctrl->seg, func->bus, func->device, | ||
2696 | func->function); | ||
2697 | } /* End of Not-A-Bridge else */ | ||
2698 | else { | ||
2699 | /* It's some strange type of PCI adapter (Cardbus?) */ | ||
2700 | return DEVICE_TYPE_NOT_SUPPORTED; | ||
2701 | } | ||
2702 | |||
2703 | func->configured = 1; | ||
2704 | |||
2705 | return 0; | ||
2706 | } | ||
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c new file mode 100644 index 000000000000..9e70c4681f77 --- /dev/null +++ b/drivers/pci/hotplug/pciehp_hpc.c | |||
@@ -0,0 +1,1501 @@ | |||
1 | /* | ||
2 | * PCI Express PCI Hot Plug Driver | ||
3 | * | ||
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | ||
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * Copyright (C) 2001 IBM Corp. | ||
7 | * Copyright (C) 2003-2004 Intel Corporation | ||
8 | * | ||
9 | * All rights reserved. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or (at | ||
14 | * your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
19 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
20 | * details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
25 | * | ||
26 | * Send feedback to <greg@kroah.com>,<dely.l.sy@intel.com> | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | #include <linux/config.h> | ||
31 | #include <linux/kernel.h> | ||
32 | #include <linux/module.h> | ||
33 | #include <linux/types.h> | ||
34 | #include <linux/slab.h> | ||
35 | #include <linux/vmalloc.h> | ||
36 | #include <linux/interrupt.h> | ||
37 | #include <linux/spinlock.h> | ||
38 | #include <linux/pci.h> | ||
39 | #include <asm/system.h> | ||
40 | #include "../pci.h" | ||
41 | #include "pciehp.h" | ||
42 | |||
43 | #ifdef DEBUG | ||
44 | #define DBG_K_TRACE_ENTRY ((unsigned int)0x00000001) /* On function entry */ | ||
45 | #define DBG_K_TRACE_EXIT ((unsigned int)0x00000002) /* On function exit */ | ||
46 | #define DBG_K_INFO ((unsigned int)0x00000004) /* Info messages */ | ||
47 | #define DBG_K_ERROR ((unsigned int)0x00000008) /* Error messages */ | ||
48 | #define DBG_K_TRACE (DBG_K_TRACE_ENTRY|DBG_K_TRACE_EXIT) | ||
49 | #define DBG_K_STANDARD (DBG_K_INFO|DBG_K_ERROR|DBG_K_TRACE) | ||
50 | /* Redefine this flagword to set debug level */ | ||
51 | #define DEBUG_LEVEL DBG_K_STANDARD | ||
52 | |||
53 | #define DEFINE_DBG_BUFFER char __dbg_str_buf[256]; | ||
54 | |||
55 | #define DBG_PRINT( dbg_flags, args... ) \ | ||
56 | do { \ | ||
57 | if ( DEBUG_LEVEL & ( dbg_flags ) ) \ | ||
58 | { \ | ||
59 | int len; \ | ||
60 | len = sprintf( __dbg_str_buf, "%s:%d: %s: ", \ | ||
61 | __FILE__, __LINE__, __FUNCTION__ ); \ | ||
62 | sprintf( __dbg_str_buf + len, args ); \ | ||
63 | printk( KERN_NOTICE "%s\n", __dbg_str_buf ); \ | ||
64 | } \ | ||
65 | } while (0) | ||
66 | |||
67 | #define DBG_ENTER_ROUTINE DBG_PRINT (DBG_K_TRACE_ENTRY, "%s", "[Entry]"); | ||
68 | #define DBG_LEAVE_ROUTINE DBG_PRINT (DBG_K_TRACE_EXIT, "%s", "[Exit]"); | ||
69 | #else | ||
70 | #define DEFINE_DBG_BUFFER | ||
71 | #define DBG_ENTER_ROUTINE | ||
72 | #define DBG_LEAVE_ROUTINE | ||
73 | #endif /* DEBUG */ | ||
74 | |||
75 | struct ctrl_reg { | ||
76 | u8 cap_id; | ||
77 | u8 nxt_ptr; | ||
78 | u16 cap_reg; | ||
79 | u32 dev_cap; | ||
80 | u16 dev_ctrl; | ||
81 | u16 dev_status; | ||
82 | u32 lnk_cap; | ||
83 | u16 lnk_ctrl; | ||
84 | u16 lnk_status; | ||
85 | u32 slot_cap; | ||
86 | u16 slot_ctrl; | ||
87 | u16 slot_status; | ||
88 | u16 root_ctrl; | ||
89 | u16 rsvp; | ||
90 | u32 root_status; | ||
91 | } __attribute__ ((packed)); | ||
92 | |||
93 | /* offsets to the controller registers based on the above structure layout */ | ||
94 | enum ctrl_offsets { | ||
95 | PCIECAPID = offsetof(struct ctrl_reg, cap_id), | ||
96 | NXTCAPPTR = offsetof(struct ctrl_reg, nxt_ptr), | ||
97 | CAPREG = offsetof(struct ctrl_reg, cap_reg), | ||
98 | DEVCAP = offsetof(struct ctrl_reg, dev_cap), | ||
99 | DEVCTRL = offsetof(struct ctrl_reg, dev_ctrl), | ||
100 | DEVSTATUS = offsetof(struct ctrl_reg, dev_status), | ||
101 | LNKCAP = offsetof(struct ctrl_reg, lnk_cap), | ||
102 | LNKCTRL = offsetof(struct ctrl_reg, lnk_ctrl), | ||
103 | LNKSTATUS = offsetof(struct ctrl_reg, lnk_status), | ||
104 | SLOTCAP = offsetof(struct ctrl_reg, slot_cap), | ||
105 | SLOTCTRL = offsetof(struct ctrl_reg, slot_ctrl), | ||
106 | SLOTSTATUS = offsetof(struct ctrl_reg, slot_status), | ||
107 | ROOTCTRL = offsetof(struct ctrl_reg, root_ctrl), | ||
108 | ROOTSTATUS = offsetof(struct ctrl_reg, root_status), | ||
109 | }; | ||
110 | static int pcie_cap_base = 0; /* Base of the PCI Express capability item structure */ | ||
111 | |||
112 | #define PCIE_CAP_ID ( pcie_cap_base + PCIECAPID ) | ||
113 | #define NXT_CAP_PTR ( pcie_cap_base + NXTCAPPTR ) | ||
114 | #define CAP_REG ( pcie_cap_base + CAPREG ) | ||
115 | #define DEV_CAP ( pcie_cap_base + DEVCAP ) | ||
116 | #define DEV_CTRL ( pcie_cap_base + DEVCTRL ) | ||
117 | #define DEV_STATUS ( pcie_cap_base + DEVSTATUS ) | ||
118 | #define LNK_CAP ( pcie_cap_base + LNKCAP ) | ||
119 | #define LNK_CTRL ( pcie_cap_base + LNKCTRL ) | ||
120 | #define LNK_STATUS ( pcie_cap_base + LNKSTATUS ) | ||
121 | #define SLOT_CAP ( pcie_cap_base + SLOTCAP ) | ||
122 | #define SLOT_CTRL ( pcie_cap_base + SLOTCTRL ) | ||
123 | #define SLOT_STATUS ( pcie_cap_base + SLOTSTATUS ) | ||
124 | #define ROOT_CTRL ( pcie_cap_base + ROOTCTRL ) | ||
125 | #define ROOT_STATUS ( pcie_cap_base + ROOTSTATUS ) | ||
126 | |||
127 | #define hp_register_read_word(pdev, reg , value) \ | ||
128 | pci_read_config_word(pdev, reg, &value) | ||
129 | |||
130 | #define hp_register_read_dword(pdev, reg , value) \ | ||
131 | pci_read_config_dword(pdev, reg, &value) | ||
132 | |||
133 | #define hp_register_write_word(pdev, reg , value) \ | ||
134 | pci_write_config_word(pdev, reg, value) | ||
135 | |||
136 | #define hp_register_dwrite_word(pdev, reg , value) \ | ||
137 | pci_write_config_dword(pdev, reg, value) | ||
138 | |||
139 | /* Field definitions in PCI Express Capabilities Register */ | ||
140 | #define CAP_VER 0x000F | ||
141 | #define DEV_PORT_TYPE 0x00F0 | ||
142 | #define SLOT_IMPL 0x0100 | ||
143 | #define MSG_NUM 0x3E00 | ||
144 | |||
145 | /* Device or Port Type */ | ||
146 | #define NAT_ENDPT 0x00 | ||
147 | #define LEG_ENDPT 0x01 | ||
148 | #define ROOT_PORT 0x04 | ||
149 | #define UP_STREAM 0x05 | ||
150 | #define DN_STREAM 0x06 | ||
151 | #define PCIE_PCI_BRDG 0x07 | ||
152 | #define PCI_PCIE_BRDG 0x10 | ||
153 | |||
154 | /* Field definitions in Device Capabilities Register */ | ||
155 | #define DATTN_BUTTN_PRSN 0x1000 | ||
156 | #define DATTN_LED_PRSN 0x2000 | ||
157 | #define DPWR_LED_PRSN 0x4000 | ||
158 | |||
159 | /* Field definitions in Link Capabilities Register */ | ||
160 | #define MAX_LNK_SPEED 0x000F | ||
161 | #define MAX_LNK_WIDTH 0x03F0 | ||
162 | |||
163 | /* Link Width Encoding */ | ||
164 | #define LNK_X1 0x01 | ||
165 | #define LNK_X2 0x02 | ||
166 | #define LNK_X4 0x04 | ||
167 | #define LNK_X8 0x08 | ||
168 | #define LNK_X12 0x0C | ||
169 | #define LNK_X16 0x10 | ||
170 | #define LNK_X32 0x20 | ||
171 | |||
172 | /*Field definitions of Link Status Register */ | ||
173 | #define LNK_SPEED 0x000F | ||
174 | #define NEG_LINK_WD 0x03F0 | ||
175 | #define LNK_TRN_ERR 0x0400 | ||
176 | #define LNK_TRN 0x0800 | ||
177 | #define SLOT_CLK_CONF 0x1000 | ||
178 | |||
179 | /* Field definitions in Slot Capabilities Register */ | ||
180 | #define ATTN_BUTTN_PRSN 0x00000001 | ||
181 | #define PWR_CTRL_PRSN 0x00000002 | ||
182 | #define MRL_SENS_PRSN 0x00000004 | ||
183 | #define ATTN_LED_PRSN 0x00000008 | ||
184 | #define PWR_LED_PRSN 0x00000010 | ||
185 | #define HP_SUPR_RM_SUP 0x00000020 | ||
186 | #define HP_CAP 0x00000040 | ||
187 | #define SLOT_PWR_VALUE 0x000003F8 | ||
188 | #define SLOT_PWR_LIMIT 0x00000C00 | ||
189 | #define PSN 0xFFF80000 /* PSN: Physical Slot Number */ | ||
190 | |||
191 | /* Field definitions in Slot Control Register */ | ||
192 | #define ATTN_BUTTN_ENABLE 0x0001 | ||
193 | #define PWR_FAULT_DETECT_ENABLE 0x0002 | ||
194 | #define MRL_DETECT_ENABLE 0x0004 | ||
195 | #define PRSN_DETECT_ENABLE 0x0008 | ||
196 | #define CMD_CMPL_INTR_ENABLE 0x0010 | ||
197 | #define HP_INTR_ENABLE 0x0020 | ||
198 | #define ATTN_LED_CTRL 0x00C0 | ||
199 | #define PWR_LED_CTRL 0x0300 | ||
200 | #define PWR_CTRL 0x0400 | ||
201 | |||
202 | /* Attention indicator and Power indicator states */ | ||
203 | #define LED_ON 0x01 | ||
204 | #define LED_BLINK 0x10 | ||
205 | #define LED_OFF 0x11 | ||
206 | |||
207 | /* Power Control Command */ | ||
208 | #define POWER_ON 0 | ||
209 | #define POWER_OFF 0x0400 | ||
210 | |||
211 | /* Field definitions in Slot Status Register */ | ||
212 | #define ATTN_BUTTN_PRESSED 0x0001 | ||
213 | #define PWR_FAULT_DETECTED 0x0002 | ||
214 | #define MRL_SENS_CHANGED 0x0004 | ||
215 | #define PRSN_DETECT_CHANGED 0x0008 | ||
216 | #define CMD_COMPLETED 0x0010 | ||
217 | #define MRL_STATE 0x0020 | ||
218 | #define PRSN_STATE 0x0040 | ||
219 | |||
220 | struct php_ctlr_state_s { | ||
221 | struct php_ctlr_state_s *pnext; | ||
222 | struct pci_dev *pci_dev; | ||
223 | unsigned int irq; | ||
224 | unsigned long flags; /* spinlock's */ | ||
225 | u32 slot_device_offset; | ||
226 | u32 num_slots; | ||
227 | struct timer_list int_poll_timer; /* Added for poll event */ | ||
228 | php_intr_callback_t attention_button_callback; | ||
229 | php_intr_callback_t switch_change_callback; | ||
230 | php_intr_callback_t presence_change_callback; | ||
231 | php_intr_callback_t power_fault_callback; | ||
232 | void *callback_instance_id; | ||
233 | struct ctrl_reg *creg; /* Ptr to controller register space */ | ||
234 | }; | ||
235 | |||
236 | |||
237 | static spinlock_t hpc_event_lock; | ||
238 | |||
239 | DEFINE_DBG_BUFFER /* Debug string buffer for entire HPC defined here */ | ||
240 | static struct php_ctlr_state_s *php_ctlr_list_head; /* HPC state linked list */ | ||
241 | static int ctlr_seq_num = 0; /* Controller sequence # */ | ||
242 | static spinlock_t list_lock; | ||
243 | |||
244 | static irqreturn_t pcie_isr(int IRQ, void *dev_id, struct pt_regs *regs); | ||
245 | |||
246 | static void start_int_poll_timer(struct php_ctlr_state_s *php_ctlr, int seconds); | ||
247 | |||
248 | /* This is the interrupt polling timeout function. */ | ||
249 | static void int_poll_timeout(unsigned long lphp_ctlr) | ||
250 | { | ||
251 | struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *)lphp_ctlr; | ||
252 | |||
253 | DBG_ENTER_ROUTINE | ||
254 | |||
255 | if ( !php_ctlr ) { | ||
256 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
257 | return; | ||
258 | } | ||
259 | |||
260 | /* Poll for interrupt events. regs == NULL => polling */ | ||
261 | pcie_isr( 0, (void *)php_ctlr, NULL ); | ||
262 | |||
263 | init_timer(&php_ctlr->int_poll_timer); | ||
264 | |||
265 | if (!pciehp_poll_time) | ||
266 | pciehp_poll_time = 2; /* reset timer to poll in 2 secs if user doesn't specify at module installation*/ | ||
267 | |||
268 | start_int_poll_timer(php_ctlr, pciehp_poll_time); | ||
269 | |||
270 | return; | ||
271 | } | ||
272 | |||
273 | /* This function starts the interrupt polling timer. */ | ||
274 | static void start_int_poll_timer(struct php_ctlr_state_s *php_ctlr, int seconds) | ||
275 | { | ||
276 | if (!php_ctlr) { | ||
277 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
278 | return; | ||
279 | } | ||
280 | |||
281 | if ( ( seconds <= 0 ) || ( seconds > 60 ) ) | ||
282 | seconds = 2; /* Clamp to sane value */ | ||
283 | |||
284 | php_ctlr->int_poll_timer.function = &int_poll_timeout; | ||
285 | php_ctlr->int_poll_timer.data = (unsigned long)php_ctlr; /* Instance data */ | ||
286 | php_ctlr->int_poll_timer.expires = jiffies + seconds * HZ; | ||
287 | add_timer(&php_ctlr->int_poll_timer); | ||
288 | |||
289 | return; | ||
290 | } | ||
291 | |||
292 | static int pcie_write_cmd(struct slot *slot, u16 cmd) | ||
293 | { | ||
294 | struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; | ||
295 | int retval = 0; | ||
296 | u16 slot_status; | ||
297 | |||
298 | DBG_ENTER_ROUTINE | ||
299 | |||
300 | dbg("%s : Enter\n", __FUNCTION__); | ||
301 | if (!php_ctlr) { | ||
302 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
303 | return -1; | ||
304 | } | ||
305 | |||
306 | retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status); | ||
307 | if (retval) { | ||
308 | err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__); | ||
309 | return retval; | ||
310 | } | ||
311 | dbg("%s : hp_register_read_word SLOT_STATUS %x\n", __FUNCTION__, slot_status); | ||
312 | |||
313 | if ((slot_status & CMD_COMPLETED) == CMD_COMPLETED ) { | ||
314 | /* After 1 sec and CMD_COMPLETED still not set, just proceed forward to issue | ||
315 | the next command according to spec. Just print out the error message */ | ||
316 | dbg("%s : CMD_COMPLETED not clear after 1 sec.\n", __FUNCTION__); | ||
317 | } | ||
318 | |||
319 | dbg("%s: Before hp_register_write_word SLOT_CTRL %x\n", __FUNCTION__, cmd); | ||
320 | retval = hp_register_write_word(php_ctlr->pci_dev, SLOT_CTRL, cmd | CMD_CMPL_INTR_ENABLE); | ||
321 | if (retval) { | ||
322 | err("%s : hp_register_write_word SLOT_CTRL failed\n", __FUNCTION__); | ||
323 | return retval; | ||
324 | } | ||
325 | dbg("%s : hp_register_write_word SLOT_CTRL %x\n", __FUNCTION__, cmd | CMD_CMPL_INTR_ENABLE); | ||
326 | dbg("%s : Exit\n", __FUNCTION__); | ||
327 | |||
328 | DBG_LEAVE_ROUTINE | ||
329 | return retval; | ||
330 | } | ||
331 | |||
332 | static int hpc_check_lnk_status(struct controller *ctrl) | ||
333 | { | ||
334 | struct php_ctlr_state_s *php_ctlr = ctrl->hpc_ctlr_handle; | ||
335 | u16 lnk_status; | ||
336 | int retval = 0; | ||
337 | |||
338 | DBG_ENTER_ROUTINE | ||
339 | |||
340 | if (!php_ctlr) { | ||
341 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
342 | return -1; | ||
343 | } | ||
344 | |||
345 | retval = hp_register_read_word(php_ctlr->pci_dev, LNK_STATUS, lnk_status); | ||
346 | |||
347 | if (retval) { | ||
348 | err("%s : hp_register_read_word LNK_STATUS failed\n", __FUNCTION__); | ||
349 | return retval; | ||
350 | } | ||
351 | |||
352 | dbg("%s: lnk_status = %x\n", __FUNCTION__, lnk_status); | ||
353 | if ( (lnk_status & LNK_TRN) || (lnk_status & LNK_TRN_ERR) || | ||
354 | !(lnk_status & NEG_LINK_WD)) { | ||
355 | err("%s : Link Training Error occurs \n", __FUNCTION__); | ||
356 | retval = -1; | ||
357 | return retval; | ||
358 | } | ||
359 | |||
360 | DBG_LEAVE_ROUTINE | ||
361 | return retval; | ||
362 | } | ||
363 | |||
364 | |||
365 | static int hpc_get_attention_status(struct slot *slot, u8 *status) | ||
366 | { | ||
367 | struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; | ||
368 | u16 slot_ctrl; | ||
369 | u8 atten_led_state; | ||
370 | int retval = 0; | ||
371 | |||
372 | DBG_ENTER_ROUTINE | ||
373 | |||
374 | if (!php_ctlr) { | ||
375 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
376 | return -1; | ||
377 | } | ||
378 | |||
379 | retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl); | ||
380 | |||
381 | if (retval) { | ||
382 | err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); | ||
383 | return retval; | ||
384 | } | ||
385 | |||
386 | dbg("%s: SLOT_CTRL %x, value read %x\n", __FUNCTION__,SLOT_CTRL, slot_ctrl); | ||
387 | |||
388 | atten_led_state = (slot_ctrl & ATTN_LED_CTRL) >> 6; | ||
389 | |||
390 | switch (atten_led_state) { | ||
391 | case 0: | ||
392 | *status = 0xFF; /* Reserved */ | ||
393 | break; | ||
394 | case 1: | ||
395 | *status = 1; /* On */ | ||
396 | break; | ||
397 | case 2: | ||
398 | *status = 2; /* Blink */ | ||
399 | break; | ||
400 | case 3: | ||
401 | *status = 0; /* Off */ | ||
402 | break; | ||
403 | default: | ||
404 | *status = 0xFF; | ||
405 | break; | ||
406 | } | ||
407 | |||
408 | DBG_LEAVE_ROUTINE | ||
409 | return 0; | ||
410 | } | ||
411 | |||
412 | static int hpc_get_power_status(struct slot * slot, u8 *status) | ||
413 | { | ||
414 | struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; | ||
415 | u16 slot_ctrl; | ||
416 | u8 pwr_state; | ||
417 | int retval = 0; | ||
418 | |||
419 | DBG_ENTER_ROUTINE | ||
420 | |||
421 | if (!php_ctlr) { | ||
422 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
423 | return -1; | ||
424 | } | ||
425 | |||
426 | retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl); | ||
427 | |||
428 | if (retval) { | ||
429 | err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); | ||
430 | return retval; | ||
431 | } | ||
432 | dbg("%s: SLOT_CTRL %x value read %x\n", __FUNCTION__, SLOT_CTRL, slot_ctrl); | ||
433 | |||
434 | pwr_state = (slot_ctrl & PWR_CTRL) >> 10; | ||
435 | |||
436 | switch (pwr_state) { | ||
437 | case 0: | ||
438 | *status = 1; | ||
439 | break; | ||
440 | case 1: | ||
441 | *status = 0; | ||
442 | break; | ||
443 | default: | ||
444 | *status = 0xFF; | ||
445 | break; | ||
446 | } | ||
447 | |||
448 | DBG_LEAVE_ROUTINE | ||
449 | return retval; | ||
450 | } | ||
451 | |||
452 | |||
453 | static int hpc_get_latch_status(struct slot *slot, u8 *status) | ||
454 | { | ||
455 | struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; | ||
456 | u16 slot_status; | ||
457 | int retval = 0; | ||
458 | |||
459 | DBG_ENTER_ROUTINE | ||
460 | |||
461 | if (!php_ctlr) { | ||
462 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
463 | return -1; | ||
464 | } | ||
465 | |||
466 | retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status); | ||
467 | |||
468 | if (retval) { | ||
469 | err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__); | ||
470 | return retval; | ||
471 | } | ||
472 | |||
473 | *status = (((slot_status & MRL_STATE) >> 5) == 0) ? 0 : 1; | ||
474 | |||
475 | DBG_LEAVE_ROUTINE | ||
476 | return 0; | ||
477 | } | ||
478 | |||
479 | static int hpc_get_adapter_status(struct slot *slot, u8 *status) | ||
480 | { | ||
481 | struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; | ||
482 | u16 slot_status; | ||
483 | u8 card_state; | ||
484 | int retval = 0; | ||
485 | |||
486 | DBG_ENTER_ROUTINE | ||
487 | |||
488 | if (!php_ctlr) { | ||
489 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
490 | return -1; | ||
491 | } | ||
492 | |||
493 | retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status); | ||
494 | |||
495 | if (retval) { | ||
496 | err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__); | ||
497 | return retval; | ||
498 | } | ||
499 | card_state = (u8)((slot_status & PRSN_STATE) >> 6); | ||
500 | *status = (card_state == 1) ? 1 : 0; | ||
501 | |||
502 | DBG_LEAVE_ROUTINE | ||
503 | return 0; | ||
504 | } | ||
505 | |||
506 | static int hpc_query_power_fault(struct slot * slot) | ||
507 | { | ||
508 | struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; | ||
509 | u16 slot_status; | ||
510 | u8 pwr_fault; | ||
511 | int retval = 0; | ||
512 | u8 status; | ||
513 | |||
514 | DBG_ENTER_ROUTINE | ||
515 | |||
516 | if (!php_ctlr) { | ||
517 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
518 | return -1; | ||
519 | } | ||
520 | |||
521 | retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status); | ||
522 | |||
523 | if (retval) { | ||
524 | err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__); | ||
525 | return retval; | ||
526 | } | ||
527 | pwr_fault = (u8)((slot_status & PWR_FAULT_DETECTED) >> 1); | ||
528 | status = (pwr_fault != 1) ? 1 : 0; | ||
529 | |||
530 | DBG_LEAVE_ROUTINE | ||
531 | /* Note: Logic 0 => fault */ | ||
532 | return status; | ||
533 | } | ||
534 | |||
535 | static int hpc_set_attention_status(struct slot *slot, u8 value) | ||
536 | { | ||
537 | struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; | ||
538 | u16 slot_cmd = 0; | ||
539 | u16 slot_ctrl; | ||
540 | int rc = 0; | ||
541 | |||
542 | dbg("%s: \n", __FUNCTION__); | ||
543 | if (!php_ctlr) { | ||
544 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
545 | return -1; | ||
546 | } | ||
547 | |||
548 | if (slot->hp_slot >= php_ctlr->num_slots) { | ||
549 | err("%s: Invalid HPC slot number!\n", __FUNCTION__); | ||
550 | return -1; | ||
551 | } | ||
552 | rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl); | ||
553 | |||
554 | if (rc) { | ||
555 | err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); | ||
556 | return rc; | ||
557 | } | ||
558 | dbg("%s : hp_register_read_word SLOT_CTRL %x\n", __FUNCTION__, slot_ctrl); | ||
559 | |||
560 | switch (value) { | ||
561 | case 0 : /* turn off */ | ||
562 | slot_cmd = (slot_ctrl & ~ATTN_LED_CTRL) | 0x00C0; | ||
563 | break; | ||
564 | case 1: /* turn on */ | ||
565 | slot_cmd = (slot_ctrl & ~ATTN_LED_CTRL) | 0x0040; | ||
566 | break; | ||
567 | case 2: /* turn blink */ | ||
568 | slot_cmd = (slot_ctrl & ~ATTN_LED_CTRL) | 0x0080; | ||
569 | break; | ||
570 | default: | ||
571 | return -1; | ||
572 | } | ||
573 | if (!pciehp_poll_mode) | ||
574 | slot_cmd = slot_cmd | HP_INTR_ENABLE; | ||
575 | |||
576 | pcie_write_cmd(slot, slot_cmd); | ||
577 | dbg("%s: SLOT_CTRL %x write cmd %x\n", __FUNCTION__, SLOT_CTRL, slot_cmd); | ||
578 | |||
579 | return rc; | ||
580 | } | ||
581 | |||
582 | |||
583 | static void hpc_set_green_led_on(struct slot *slot) | ||
584 | { | ||
585 | struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; | ||
586 | u16 slot_cmd; | ||
587 | u16 slot_ctrl; | ||
588 | int rc = 0; | ||
589 | |||
590 | dbg("%s: \n", __FUNCTION__); | ||
591 | if (!php_ctlr) { | ||
592 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
593 | return ; | ||
594 | } | ||
595 | |||
596 | if (slot->hp_slot >= php_ctlr->num_slots) { | ||
597 | err("%s: Invalid HPC slot number!\n", __FUNCTION__); | ||
598 | return ; | ||
599 | } | ||
600 | |||
601 | rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl); | ||
602 | |||
603 | if (rc) { | ||
604 | err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); | ||
605 | return; | ||
606 | } | ||
607 | dbg("%s : hp_register_read_word SLOT_CTRL %x\n", __FUNCTION__, slot_ctrl); | ||
608 | slot_cmd = (slot_ctrl & ~PWR_LED_CTRL) | 0x0100; | ||
609 | if (!pciehp_poll_mode) | ||
610 | slot_cmd = slot_cmd | HP_INTR_ENABLE; | ||
611 | |||
612 | pcie_write_cmd(slot, slot_cmd); | ||
613 | |||
614 | dbg("%s: SLOT_CTRL %x write cmd %x\n",__FUNCTION__, SLOT_CTRL, slot_cmd); | ||
615 | return; | ||
616 | } | ||
617 | |||
618 | static void hpc_set_green_led_off(struct slot *slot) | ||
619 | { | ||
620 | struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; | ||
621 | u16 slot_cmd; | ||
622 | u16 slot_ctrl; | ||
623 | int rc = 0; | ||
624 | |||
625 | dbg("%s: \n", __FUNCTION__); | ||
626 | if (!php_ctlr) { | ||
627 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
628 | return ; | ||
629 | } | ||
630 | |||
631 | if (slot->hp_slot >= php_ctlr->num_slots) { | ||
632 | err("%s: Invalid HPC slot number!\n", __FUNCTION__); | ||
633 | return ; | ||
634 | } | ||
635 | |||
636 | rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl); | ||
637 | |||
638 | if (rc) { | ||
639 | err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); | ||
640 | return; | ||
641 | } | ||
642 | dbg("%s : hp_register_read_word SLOT_CTRL %x\n", __FUNCTION__, slot_ctrl); | ||
643 | |||
644 | slot_cmd = (slot_ctrl & ~PWR_LED_CTRL) | 0x0300; | ||
645 | |||
646 | if (!pciehp_poll_mode) | ||
647 | slot_cmd = slot_cmd | HP_INTR_ENABLE; | ||
648 | pcie_write_cmd(slot, slot_cmd); | ||
649 | dbg("%s: SLOT_CTRL %x write cmd %x\n", __FUNCTION__, SLOT_CTRL, slot_cmd); | ||
650 | |||
651 | return; | ||
652 | } | ||
653 | |||
654 | static void hpc_set_green_led_blink(struct slot *slot) | ||
655 | { | ||
656 | struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; | ||
657 | u16 slot_cmd; | ||
658 | u16 slot_ctrl; | ||
659 | int rc = 0; | ||
660 | |||
661 | dbg("%s: \n", __FUNCTION__); | ||
662 | if (!php_ctlr) { | ||
663 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
664 | return ; | ||
665 | } | ||
666 | |||
667 | if (slot->hp_slot >= php_ctlr->num_slots) { | ||
668 | err("%s: Invalid HPC slot number!\n", __FUNCTION__); | ||
669 | return ; | ||
670 | } | ||
671 | |||
672 | rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl); | ||
673 | |||
674 | if (rc) { | ||
675 | err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); | ||
676 | return; | ||
677 | } | ||
678 | dbg("%s : hp_register_read_word SLOT_CTRL %x\n", __FUNCTION__, slot_ctrl); | ||
679 | |||
680 | slot_cmd = (slot_ctrl & ~PWR_LED_CTRL) | 0x0200; | ||
681 | |||
682 | if (!pciehp_poll_mode) | ||
683 | slot_cmd = slot_cmd | HP_INTR_ENABLE; | ||
684 | pcie_write_cmd(slot, slot_cmd); | ||
685 | |||
686 | dbg("%s: SLOT_CTRL %x write cmd %x\n",__FUNCTION__, SLOT_CTRL, slot_cmd); | ||
687 | return; | ||
688 | } | ||
689 | |||
690 | int pcie_get_ctlr_slot_config(struct controller *ctrl, | ||
691 | int *num_ctlr_slots, /* number of slots in this HPC; only 1 in PCIE */ | ||
692 | int *first_device_num, /* PCI dev num of the first slot in this PCIE */ | ||
693 | int *physical_slot_num, /* phy slot num of the first slot in this PCIE */ | ||
694 | u8 *ctrlcap) | ||
695 | { | ||
696 | struct php_ctlr_state_s *php_ctlr = ctrl->hpc_ctlr_handle; | ||
697 | u32 slot_cap; | ||
698 | int rc = 0; | ||
699 | |||
700 | DBG_ENTER_ROUTINE | ||
701 | |||
702 | if (!php_ctlr) { | ||
703 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
704 | return -1; | ||
705 | } | ||
706 | |||
707 | *first_device_num = 0; | ||
708 | *num_ctlr_slots = 1; | ||
709 | |||
710 | rc = hp_register_read_dword(php_ctlr->pci_dev, SLOT_CAP, slot_cap); | ||
711 | |||
712 | if (rc) { | ||
713 | err("%s : hp_register_read_dword SLOT_CAP failed\n", __FUNCTION__); | ||
714 | return -1; | ||
715 | } | ||
716 | |||
717 | *physical_slot_num = slot_cap >> 19; | ||
718 | dbg("%s: PSN %d \n", __FUNCTION__, *physical_slot_num); | ||
719 | |||
720 | *ctrlcap = slot_cap & 0x0000007f; | ||
721 | |||
722 | DBG_LEAVE_ROUTINE | ||
723 | return 0; | ||
724 | } | ||
725 | |||
726 | static void hpc_release_ctlr(struct controller *ctrl) | ||
727 | { | ||
728 | struct php_ctlr_state_s *php_ctlr = ctrl->hpc_ctlr_handle; | ||
729 | struct php_ctlr_state_s *p, *p_prev; | ||
730 | |||
731 | DBG_ENTER_ROUTINE | ||
732 | |||
733 | if (!php_ctlr) { | ||
734 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
735 | return ; | ||
736 | } | ||
737 | |||
738 | if (pciehp_poll_mode) { | ||
739 | del_timer(&php_ctlr->int_poll_timer); | ||
740 | } else { | ||
741 | if (php_ctlr->irq) { | ||
742 | free_irq(php_ctlr->irq, ctrl); | ||
743 | php_ctlr->irq = 0; | ||
744 | if (!pcie_mch_quirk) | ||
745 | pci_disable_msi(php_ctlr->pci_dev); | ||
746 | } | ||
747 | } | ||
748 | if (php_ctlr->pci_dev) | ||
749 | php_ctlr->pci_dev = NULL; | ||
750 | |||
751 | spin_lock(&list_lock); | ||
752 | p = php_ctlr_list_head; | ||
753 | p_prev = NULL; | ||
754 | while (p) { | ||
755 | if (p == php_ctlr) { | ||
756 | if (p_prev) | ||
757 | p_prev->pnext = p->pnext; | ||
758 | else | ||
759 | php_ctlr_list_head = p->pnext; | ||
760 | break; | ||
761 | } else { | ||
762 | p_prev = p; | ||
763 | p = p->pnext; | ||
764 | } | ||
765 | } | ||
766 | spin_unlock(&list_lock); | ||
767 | |||
768 | kfree(php_ctlr); | ||
769 | |||
770 | DBG_LEAVE_ROUTINE | ||
771 | |||
772 | } | ||
773 | |||
774 | static int hpc_power_on_slot(struct slot * slot) | ||
775 | { | ||
776 | struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; | ||
777 | u16 slot_cmd; | ||
778 | u16 slot_ctrl; | ||
779 | |||
780 | int retval = 0; | ||
781 | |||
782 | DBG_ENTER_ROUTINE | ||
783 | dbg("%s: \n", __FUNCTION__); | ||
784 | |||
785 | if (!php_ctlr) { | ||
786 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
787 | return -1; | ||
788 | } | ||
789 | |||
790 | dbg("%s: slot->hp_slot %x\n", __FUNCTION__, slot->hp_slot); | ||
791 | if (slot->hp_slot >= php_ctlr->num_slots) { | ||
792 | err("%s: Invalid HPC slot number!\n", __FUNCTION__); | ||
793 | return -1; | ||
794 | } | ||
795 | |||
796 | retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl); | ||
797 | |||
798 | if (retval) { | ||
799 | err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); | ||
800 | return retval; | ||
801 | } | ||
802 | dbg("%s: SLOT_CTRL %x, value read %xn", __FUNCTION__, SLOT_CTRL, | ||
803 | slot_ctrl); | ||
804 | |||
805 | slot_cmd = (slot_ctrl & ~PWR_CTRL) | POWER_ON; | ||
806 | |||
807 | if (!pciehp_poll_mode) | ||
808 | slot_cmd = slot_cmd | HP_INTR_ENABLE; | ||
809 | |||
810 | retval = pcie_write_cmd(slot, slot_cmd); | ||
811 | |||
812 | if (retval) { | ||
813 | err("%s: Write %x command failed!\n", __FUNCTION__, slot_cmd); | ||
814 | return -1; | ||
815 | } | ||
816 | dbg("%s: SLOT_CTRL %x write cmd %x\n",__FUNCTION__, SLOT_CTRL, slot_cmd); | ||
817 | |||
818 | DBG_LEAVE_ROUTINE | ||
819 | |||
820 | return retval; | ||
821 | } | ||
822 | |||
823 | static int hpc_power_off_slot(struct slot * slot) | ||
824 | { | ||
825 | struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; | ||
826 | u16 slot_cmd; | ||
827 | u16 slot_ctrl; | ||
828 | |||
829 | int retval = 0; | ||
830 | |||
831 | DBG_ENTER_ROUTINE | ||
832 | dbg("%s: \n", __FUNCTION__); | ||
833 | |||
834 | if (!php_ctlr) { | ||
835 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
836 | return -1; | ||
837 | } | ||
838 | |||
839 | dbg("%s: slot->hp_slot %x\n", __FUNCTION__, slot->hp_slot); | ||
840 | slot->hp_slot = 0; | ||
841 | if (slot->hp_slot >= php_ctlr->num_slots) { | ||
842 | err("%s: Invalid HPC slot number!\n", __FUNCTION__); | ||
843 | return -1; | ||
844 | } | ||
845 | retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl); | ||
846 | |||
847 | if (retval) { | ||
848 | err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); | ||
849 | return retval; | ||
850 | } | ||
851 | dbg("%s: SLOT_CTRL %x, value read %x\n", __FUNCTION__, SLOT_CTRL, | ||
852 | slot_ctrl); | ||
853 | |||
854 | slot_cmd = (slot_ctrl & ~PWR_CTRL) | POWER_OFF; | ||
855 | |||
856 | if (!pciehp_poll_mode) | ||
857 | slot_cmd = slot_cmd | HP_INTR_ENABLE; | ||
858 | |||
859 | retval = pcie_write_cmd(slot, slot_cmd); | ||
860 | |||
861 | if (retval) { | ||
862 | err("%s: Write command failed!\n", __FUNCTION__); | ||
863 | return -1; | ||
864 | } | ||
865 | dbg("%s: SLOT_CTRL %x write cmd %x\n",__FUNCTION__, SLOT_CTRL, slot_cmd); | ||
866 | |||
867 | DBG_LEAVE_ROUTINE | ||
868 | |||
869 | return retval; | ||
870 | } | ||
871 | |||
872 | static irqreturn_t pcie_isr(int IRQ, void *dev_id, struct pt_regs *regs) | ||
873 | { | ||
874 | struct controller *ctrl = NULL; | ||
875 | struct php_ctlr_state_s *php_ctlr; | ||
876 | u8 schedule_flag = 0; | ||
877 | u16 slot_status, intr_detect, intr_loc; | ||
878 | u16 temp_word; | ||
879 | int hp_slot = 0; /* only 1 slot per PCI Express port */ | ||
880 | int rc = 0; | ||
881 | |||
882 | if (!dev_id) | ||
883 | return IRQ_NONE; | ||
884 | |||
885 | if (!pciehp_poll_mode) { | ||
886 | ctrl = dev_id; | ||
887 | php_ctlr = ctrl->hpc_ctlr_handle; | ||
888 | } else { | ||
889 | php_ctlr = dev_id; | ||
890 | ctrl = (struct controller *)php_ctlr->callback_instance_id; | ||
891 | } | ||
892 | |||
893 | if (!ctrl) { | ||
894 | dbg("%s: dev_id %p ctlr == NULL\n", __FUNCTION__, (void*) dev_id); | ||
895 | return IRQ_NONE; | ||
896 | } | ||
897 | |||
898 | if (!php_ctlr) { | ||
899 | dbg("%s: php_ctlr == NULL\n", __FUNCTION__); | ||
900 | return IRQ_NONE; | ||
901 | } | ||
902 | |||
903 | rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status); | ||
904 | if (rc) { | ||
905 | err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__); | ||
906 | return IRQ_NONE; | ||
907 | } | ||
908 | |||
909 | intr_detect = ( ATTN_BUTTN_PRESSED | PWR_FAULT_DETECTED | MRL_SENS_CHANGED | | ||
910 | PRSN_DETECT_CHANGED | CMD_COMPLETED ); | ||
911 | |||
912 | intr_loc = slot_status & intr_detect; | ||
913 | |||
914 | /* Check to see if it was our interrupt */ | ||
915 | if ( !intr_loc ) | ||
916 | return IRQ_NONE; | ||
917 | |||
918 | dbg("%s: intr_loc %x\n", __FUNCTION__, intr_loc); | ||
919 | /* Mask Hot-plug Interrupt Enable */ | ||
920 | if (!pciehp_poll_mode) { | ||
921 | rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, temp_word); | ||
922 | if (rc) { | ||
923 | err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); | ||
924 | return IRQ_NONE; | ||
925 | } | ||
926 | |||
927 | dbg("%s: Set Mask Hot-plug Interrupt Enable\n", __FUNCTION__); | ||
928 | dbg("%s: hp_register_read_word SLOT_CTRL with value %x\n", __FUNCTION__, temp_word); | ||
929 | temp_word = (temp_word & ~HP_INTR_ENABLE & ~CMD_CMPL_INTR_ENABLE) | 0x00; | ||
930 | |||
931 | rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_CTRL, temp_word); | ||
932 | if (rc) { | ||
933 | err("%s : hp_register_write_word SLOT_CTRL failed\n", __FUNCTION__); | ||
934 | return IRQ_NONE; | ||
935 | } | ||
936 | dbg("%s: hp_register_write_word SLOT_CTRL with value %x\n", __FUNCTION__, temp_word); | ||
937 | |||
938 | rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status); | ||
939 | if (rc) { | ||
940 | err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__); | ||
941 | return IRQ_NONE; | ||
942 | } | ||
943 | dbg("%s: hp_register_read_word SLOT_STATUS with value %x\n", __FUNCTION__, slot_status); | ||
944 | |||
945 | /* Clear command complete interrupt caused by this write */ | ||
946 | temp_word = 0x1f; | ||
947 | rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_STATUS, temp_word); | ||
948 | if (rc) { | ||
949 | err("%s : hp_register_write_word SLOT_STATUS failed\n", __FUNCTION__); | ||
950 | return IRQ_NONE; | ||
951 | } | ||
952 | dbg("%s: hp_register_write_word SLOT_STATUS with value %x\n", __FUNCTION__, temp_word); | ||
953 | } | ||
954 | |||
955 | if (intr_loc & CMD_COMPLETED) { | ||
956 | /* | ||
957 | * Command Complete Interrupt Pending | ||
958 | */ | ||
959 | dbg("%s: In Command Complete Interrupt Pending\n", __FUNCTION__); | ||
960 | wake_up_interruptible(&ctrl->queue); | ||
961 | } | ||
962 | |||
963 | if ((php_ctlr->switch_change_callback) && (intr_loc & MRL_SENS_CHANGED)) | ||
964 | schedule_flag += php_ctlr->switch_change_callback( | ||
965 | hp_slot, php_ctlr->callback_instance_id); | ||
966 | if ((php_ctlr->attention_button_callback) && (intr_loc & ATTN_BUTTN_PRESSED)) | ||
967 | schedule_flag += php_ctlr->attention_button_callback( | ||
968 | hp_slot, php_ctlr->callback_instance_id); | ||
969 | if ((php_ctlr->presence_change_callback) && (intr_loc & PRSN_DETECT_CHANGED)) | ||
970 | schedule_flag += php_ctlr->presence_change_callback( | ||
971 | hp_slot , php_ctlr->callback_instance_id); | ||
972 | if ((php_ctlr->power_fault_callback) && (intr_loc & PWR_FAULT_DETECTED)) | ||
973 | schedule_flag += php_ctlr->power_fault_callback( | ||
974 | hp_slot, php_ctlr->callback_instance_id); | ||
975 | |||
976 | /* Clear all events after serving them */ | ||
977 | temp_word = 0x1F; | ||
978 | rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_STATUS, temp_word); | ||
979 | if (rc) { | ||
980 | err("%s : hp_register_write_word SLOT_STATUS failed\n", __FUNCTION__); | ||
981 | return IRQ_NONE; | ||
982 | } | ||
983 | /* Unmask Hot-plug Interrupt Enable */ | ||
984 | if (!pciehp_poll_mode) { | ||
985 | rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, temp_word); | ||
986 | if (rc) { | ||
987 | err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); | ||
988 | return IRQ_NONE; | ||
989 | } | ||
990 | |||
991 | dbg("%s: Unmask Hot-plug Interrupt Enable\n", __FUNCTION__); | ||
992 | dbg("%s: hp_register_read_word SLOT_CTRL with value %x\n", __FUNCTION__, temp_word); | ||
993 | temp_word = (temp_word & ~HP_INTR_ENABLE) | HP_INTR_ENABLE; | ||
994 | |||
995 | rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_CTRL, temp_word); | ||
996 | if (rc) { | ||
997 | err("%s : hp_register_write_word SLOT_CTRL failed\n", __FUNCTION__); | ||
998 | return IRQ_NONE; | ||
999 | } | ||
1000 | dbg("%s: hp_register_write_word SLOT_CTRL with value %x\n", __FUNCTION__, temp_word); | ||
1001 | |||
1002 | rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status); | ||
1003 | if (rc) { | ||
1004 | err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__); | ||
1005 | return IRQ_NONE; | ||
1006 | } | ||
1007 | dbg("%s: hp_register_read_word SLOT_STATUS with value %x\n", __FUNCTION__, slot_status); | ||
1008 | |||
1009 | /* Clear command complete interrupt caused by this write */ | ||
1010 | temp_word = 0x1F; | ||
1011 | rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_STATUS, temp_word); | ||
1012 | if (rc) { | ||
1013 | err("%s : hp_register_write_word SLOT_STATUS failed\n", __FUNCTION__); | ||
1014 | return IRQ_NONE; | ||
1015 | } | ||
1016 | dbg("%s: hp_register_write_word SLOT_STATUS with value %x\n", __FUNCTION__, temp_word); | ||
1017 | } | ||
1018 | |||
1019 | return IRQ_HANDLED; | ||
1020 | } | ||
1021 | |||
1022 | static int hpc_get_max_lnk_speed (struct slot *slot, enum pci_bus_speed *value) | ||
1023 | { | ||
1024 | struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; | ||
1025 | enum pcie_link_speed lnk_speed; | ||
1026 | u32 lnk_cap; | ||
1027 | int retval = 0; | ||
1028 | |||
1029 | DBG_ENTER_ROUTINE | ||
1030 | |||
1031 | if (!php_ctlr) { | ||
1032 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
1033 | return -1; | ||
1034 | } | ||
1035 | |||
1036 | if (slot->hp_slot >= php_ctlr->num_slots) { | ||
1037 | err("%s: Invalid HPC slot number!\n", __FUNCTION__); | ||
1038 | return -1; | ||
1039 | } | ||
1040 | |||
1041 | retval = hp_register_read_dword(php_ctlr->pci_dev, LNK_CAP, lnk_cap); | ||
1042 | |||
1043 | if (retval) { | ||
1044 | err("%s : hp_register_read_dword LNK_CAP failed\n", __FUNCTION__); | ||
1045 | return retval; | ||
1046 | } | ||
1047 | |||
1048 | switch (lnk_cap & 0x000F) { | ||
1049 | case 1: | ||
1050 | lnk_speed = PCIE_2PT5GB; | ||
1051 | break; | ||
1052 | default: | ||
1053 | lnk_speed = PCIE_LNK_SPEED_UNKNOWN; | ||
1054 | break; | ||
1055 | } | ||
1056 | |||
1057 | *value = lnk_speed; | ||
1058 | dbg("Max link speed = %d\n", lnk_speed); | ||
1059 | DBG_LEAVE_ROUTINE | ||
1060 | return retval; | ||
1061 | } | ||
1062 | |||
1063 | static int hpc_get_max_lnk_width (struct slot *slot, enum pcie_link_width *value) | ||
1064 | { | ||
1065 | struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; | ||
1066 | enum pcie_link_width lnk_wdth; | ||
1067 | u32 lnk_cap; | ||
1068 | int retval = 0; | ||
1069 | |||
1070 | DBG_ENTER_ROUTINE | ||
1071 | |||
1072 | if (!php_ctlr) { | ||
1073 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
1074 | return -1; | ||
1075 | } | ||
1076 | |||
1077 | if (slot->hp_slot >= php_ctlr->num_slots) { | ||
1078 | err("%s: Invalid HPC slot number!\n", __FUNCTION__); | ||
1079 | return -1; | ||
1080 | } | ||
1081 | |||
1082 | retval = hp_register_read_dword(php_ctlr->pci_dev, LNK_CAP, lnk_cap); | ||
1083 | |||
1084 | if (retval) { | ||
1085 | err("%s : hp_register_read_dword LNK_CAP failed\n", __FUNCTION__); | ||
1086 | return retval; | ||
1087 | } | ||
1088 | |||
1089 | switch ((lnk_cap & 0x03F0) >> 4){ | ||
1090 | case 0: | ||
1091 | lnk_wdth = PCIE_LNK_WIDTH_RESRV; | ||
1092 | break; | ||
1093 | case 1: | ||
1094 | lnk_wdth = PCIE_LNK_X1; | ||
1095 | break; | ||
1096 | case 2: | ||
1097 | lnk_wdth = PCIE_LNK_X2; | ||
1098 | break; | ||
1099 | case 4: | ||
1100 | lnk_wdth = PCIE_LNK_X4; | ||
1101 | break; | ||
1102 | case 8: | ||
1103 | lnk_wdth = PCIE_LNK_X8; | ||
1104 | break; | ||
1105 | case 12: | ||
1106 | lnk_wdth = PCIE_LNK_X12; | ||
1107 | break; | ||
1108 | case 16: | ||
1109 | lnk_wdth = PCIE_LNK_X16; | ||
1110 | break; | ||
1111 | case 32: | ||
1112 | lnk_wdth = PCIE_LNK_X32; | ||
1113 | break; | ||
1114 | default: | ||
1115 | lnk_wdth = PCIE_LNK_WIDTH_UNKNOWN; | ||
1116 | break; | ||
1117 | } | ||
1118 | |||
1119 | *value = lnk_wdth; | ||
1120 | dbg("Max link width = %d\n", lnk_wdth); | ||
1121 | DBG_LEAVE_ROUTINE | ||
1122 | return retval; | ||
1123 | } | ||
1124 | |||
1125 | static int hpc_get_cur_lnk_speed (struct slot *slot, enum pci_bus_speed *value) | ||
1126 | { | ||
1127 | struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; | ||
1128 | enum pcie_link_speed lnk_speed = PCI_SPEED_UNKNOWN; | ||
1129 | int retval = 0; | ||
1130 | u16 lnk_status; | ||
1131 | |||
1132 | DBG_ENTER_ROUTINE | ||
1133 | |||
1134 | if (!php_ctlr) { | ||
1135 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
1136 | return -1; | ||
1137 | } | ||
1138 | |||
1139 | if (slot->hp_slot >= php_ctlr->num_slots) { | ||
1140 | err("%s: Invalid HPC slot number!\n", __FUNCTION__); | ||
1141 | return -1; | ||
1142 | } | ||
1143 | |||
1144 | retval = hp_register_read_word(php_ctlr->pci_dev, LNK_STATUS, lnk_status); | ||
1145 | |||
1146 | if (retval) { | ||
1147 | err("%s : hp_register_read_word LNK_STATUS failed\n", __FUNCTION__); | ||
1148 | return retval; | ||
1149 | } | ||
1150 | |||
1151 | switch (lnk_status & 0x0F) { | ||
1152 | case 1: | ||
1153 | lnk_speed = PCIE_2PT5GB; | ||
1154 | break; | ||
1155 | default: | ||
1156 | lnk_speed = PCIE_LNK_SPEED_UNKNOWN; | ||
1157 | break; | ||
1158 | } | ||
1159 | |||
1160 | *value = lnk_speed; | ||
1161 | dbg("Current link speed = %d\n", lnk_speed); | ||
1162 | DBG_LEAVE_ROUTINE | ||
1163 | return retval; | ||
1164 | } | ||
1165 | |||
1166 | static int hpc_get_cur_lnk_width (struct slot *slot, enum pcie_link_width *value) | ||
1167 | { | ||
1168 | struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; | ||
1169 | enum pcie_link_width lnk_wdth = PCIE_LNK_WIDTH_UNKNOWN; | ||
1170 | int retval = 0; | ||
1171 | u16 lnk_status; | ||
1172 | |||
1173 | DBG_ENTER_ROUTINE | ||
1174 | |||
1175 | if (!php_ctlr) { | ||
1176 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
1177 | return -1; | ||
1178 | } | ||
1179 | |||
1180 | if (slot->hp_slot >= php_ctlr->num_slots) { | ||
1181 | err("%s: Invalid HPC slot number!\n", __FUNCTION__); | ||
1182 | return -1; | ||
1183 | } | ||
1184 | |||
1185 | retval = hp_register_read_word(php_ctlr->pci_dev, LNK_STATUS, lnk_status); | ||
1186 | |||
1187 | if (retval) { | ||
1188 | err("%s : hp_register_read_word LNK_STATUS failed\n", __FUNCTION__); | ||
1189 | return retval; | ||
1190 | } | ||
1191 | |||
1192 | switch ((lnk_status & 0x03F0) >> 4){ | ||
1193 | case 0: | ||
1194 | lnk_wdth = PCIE_LNK_WIDTH_RESRV; | ||
1195 | break; | ||
1196 | case 1: | ||
1197 | lnk_wdth = PCIE_LNK_X1; | ||
1198 | break; | ||
1199 | case 2: | ||
1200 | lnk_wdth = PCIE_LNK_X2; | ||
1201 | break; | ||
1202 | case 4: | ||
1203 | lnk_wdth = PCIE_LNK_X4; | ||
1204 | break; | ||
1205 | case 8: | ||
1206 | lnk_wdth = PCIE_LNK_X8; | ||
1207 | break; | ||
1208 | case 12: | ||
1209 | lnk_wdth = PCIE_LNK_X12; | ||
1210 | break; | ||
1211 | case 16: | ||
1212 | lnk_wdth = PCIE_LNK_X16; | ||
1213 | break; | ||
1214 | case 32: | ||
1215 | lnk_wdth = PCIE_LNK_X32; | ||
1216 | break; | ||
1217 | default: | ||
1218 | lnk_wdth = PCIE_LNK_WIDTH_UNKNOWN; | ||
1219 | break; | ||
1220 | } | ||
1221 | |||
1222 | *value = lnk_wdth; | ||
1223 | dbg("Current link width = %d\n", lnk_wdth); | ||
1224 | DBG_LEAVE_ROUTINE | ||
1225 | return retval; | ||
1226 | } | ||
1227 | |||
1228 | static struct hpc_ops pciehp_hpc_ops = { | ||
1229 | .power_on_slot = hpc_power_on_slot, | ||
1230 | .power_off_slot = hpc_power_off_slot, | ||
1231 | .set_attention_status = hpc_set_attention_status, | ||
1232 | .get_power_status = hpc_get_power_status, | ||
1233 | .get_attention_status = hpc_get_attention_status, | ||
1234 | .get_latch_status = hpc_get_latch_status, | ||
1235 | .get_adapter_status = hpc_get_adapter_status, | ||
1236 | |||
1237 | .get_max_bus_speed = hpc_get_max_lnk_speed, | ||
1238 | .get_cur_bus_speed = hpc_get_cur_lnk_speed, | ||
1239 | .get_max_lnk_width = hpc_get_max_lnk_width, | ||
1240 | .get_cur_lnk_width = hpc_get_cur_lnk_width, | ||
1241 | |||
1242 | .query_power_fault = hpc_query_power_fault, | ||
1243 | .green_led_on = hpc_set_green_led_on, | ||
1244 | .green_led_off = hpc_set_green_led_off, | ||
1245 | .green_led_blink = hpc_set_green_led_blink, | ||
1246 | |||
1247 | .release_ctlr = hpc_release_ctlr, | ||
1248 | .check_lnk_status = hpc_check_lnk_status, | ||
1249 | }; | ||
1250 | |||
1251 | int pcie_init(struct controller * ctrl, | ||
1252 | struct pcie_device *dev, | ||
1253 | php_intr_callback_t attention_button_callback, | ||
1254 | php_intr_callback_t switch_change_callback, | ||
1255 | php_intr_callback_t presence_change_callback, | ||
1256 | php_intr_callback_t power_fault_callback) | ||
1257 | { | ||
1258 | struct php_ctlr_state_s *php_ctlr, *p; | ||
1259 | void *instance_id = ctrl; | ||
1260 | int rc; | ||
1261 | static int first = 1; | ||
1262 | u16 temp_word; | ||
1263 | u16 cap_reg; | ||
1264 | u16 intr_enable = 0; | ||
1265 | u32 slot_cap; | ||
1266 | int cap_base, saved_cap_base; | ||
1267 | u16 slot_status, slot_ctrl; | ||
1268 | struct pci_dev *pdev; | ||
1269 | |||
1270 | DBG_ENTER_ROUTINE | ||
1271 | |||
1272 | spin_lock_init(&list_lock); | ||
1273 | php_ctlr = (struct php_ctlr_state_s *) kmalloc(sizeof(struct php_ctlr_state_s), GFP_KERNEL); | ||
1274 | |||
1275 | if (!php_ctlr) { /* allocate controller state data */ | ||
1276 | err("%s: HPC controller memory allocation error!\n", __FUNCTION__); | ||
1277 | goto abort; | ||
1278 | } | ||
1279 | |||
1280 | memset(php_ctlr, 0, sizeof(struct php_ctlr_state_s)); | ||
1281 | |||
1282 | pdev = dev->port; | ||
1283 | php_ctlr->pci_dev = pdev; /* save pci_dev in context */ | ||
1284 | |||
1285 | dbg("%s: pdev->vendor %x pdev->device %x\n", __FUNCTION__, | ||
1286 | pdev->vendor, pdev->device); | ||
1287 | |||
1288 | saved_cap_base = pcie_cap_base; | ||
1289 | |||
1290 | if ((cap_base = pci_find_capability(pdev, PCI_CAP_ID_EXP)) == 0) { | ||
1291 | dbg("%s: Can't find PCI_CAP_ID_EXP (0x10)\n", __FUNCTION__); | ||
1292 | goto abort_free_ctlr; | ||
1293 | } | ||
1294 | |||
1295 | pcie_cap_base = cap_base; | ||
1296 | |||
1297 | dbg("%s: pcie_cap_base %x\n", __FUNCTION__, pcie_cap_base); | ||
1298 | |||
1299 | rc = hp_register_read_word(pdev, CAP_REG, cap_reg); | ||
1300 | if (rc) { | ||
1301 | err("%s : hp_register_read_word CAP_REG failed\n", __FUNCTION__); | ||
1302 | goto abort_free_ctlr; | ||
1303 | } | ||
1304 | dbg("%s: CAP_REG offset %x cap_reg %x\n", __FUNCTION__, CAP_REG, cap_reg); | ||
1305 | |||
1306 | if (((cap_reg & SLOT_IMPL) == 0) || ((cap_reg & DEV_PORT_TYPE) != 0x0040)){ | ||
1307 | dbg("%s : This is not a root port or the port is not connected to a slot\n", __FUNCTION__); | ||
1308 | goto abort_free_ctlr; | ||
1309 | } | ||
1310 | |||
1311 | rc = hp_register_read_dword(php_ctlr->pci_dev, SLOT_CAP, slot_cap); | ||
1312 | if (rc) { | ||
1313 | err("%s : hp_register_read_word CAP_REG failed\n", __FUNCTION__); | ||
1314 | goto abort_free_ctlr; | ||
1315 | } | ||
1316 | dbg("%s: SLOT_CAP offset %x slot_cap %x\n", __FUNCTION__, SLOT_CAP, slot_cap); | ||
1317 | |||
1318 | if (!(slot_cap & HP_CAP)) { | ||
1319 | dbg("%s : This slot is not hot-plug capable\n", __FUNCTION__); | ||
1320 | goto abort_free_ctlr; | ||
1321 | } | ||
1322 | /* For debugging purpose */ | ||
1323 | rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status); | ||
1324 | if (rc) { | ||
1325 | err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__); | ||
1326 | goto abort_free_ctlr; | ||
1327 | } | ||
1328 | dbg("%s: SLOT_STATUS offset %x slot_status %x\n", __FUNCTION__, SLOT_STATUS, slot_status); | ||
1329 | |||
1330 | rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl); | ||
1331 | if (rc) { | ||
1332 | err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); | ||
1333 | goto abort_free_ctlr; | ||
1334 | } | ||
1335 | dbg("%s: SLOT_CTRL offset %x slot_ctrl %x\n", __FUNCTION__, SLOT_CTRL, slot_ctrl); | ||
1336 | |||
1337 | if (first) { | ||
1338 | spin_lock_init(&hpc_event_lock); | ||
1339 | first = 0; | ||
1340 | } | ||
1341 | |||
1342 | dbg("pdev = %p: b:d:f:irq=0x%x:%x:%x:%x\n", pdev, pdev->bus->number, | ||
1343 | PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), dev->irq); | ||
1344 | for ( rc = 0; rc < DEVICE_COUNT_RESOURCE; rc++) | ||
1345 | if (pci_resource_len(pdev, rc) > 0) | ||
1346 | dbg("pci resource[%d] start=0x%lx(len=0x%lx)\n", rc, | ||
1347 | pci_resource_start(pdev, rc), pci_resource_len(pdev, rc)); | ||
1348 | |||
1349 | info("HPC vendor_id %x device_id %x ss_vid %x ss_did %x\n", pdev->vendor, pdev->device, | ||
1350 | pdev->subsystem_vendor, pdev->subsystem_device); | ||
1351 | |||
1352 | if (pci_enable_device(pdev)) | ||
1353 | goto abort_free_ctlr; | ||
1354 | |||
1355 | init_MUTEX(&ctrl->crit_sect); | ||
1356 | /* setup wait queue */ | ||
1357 | init_waitqueue_head(&ctrl->queue); | ||
1358 | |||
1359 | /* find the IRQ */ | ||
1360 | php_ctlr->irq = dev->irq; | ||
1361 | dbg("HPC interrupt = %d\n", php_ctlr->irq); | ||
1362 | |||
1363 | /* Save interrupt callback info */ | ||
1364 | php_ctlr->attention_button_callback = attention_button_callback; | ||
1365 | php_ctlr->switch_change_callback = switch_change_callback; | ||
1366 | php_ctlr->presence_change_callback = presence_change_callback; | ||
1367 | php_ctlr->power_fault_callback = power_fault_callback; | ||
1368 | php_ctlr->callback_instance_id = instance_id; | ||
1369 | |||
1370 | /* return PCI Controller Info */ | ||
1371 | php_ctlr->slot_device_offset = 0; | ||
1372 | php_ctlr->num_slots = 1; | ||
1373 | |||
1374 | /* Mask Hot-plug Interrupt Enable */ | ||
1375 | rc = hp_register_read_word(pdev, SLOT_CTRL, temp_word); | ||
1376 | if (rc) { | ||
1377 | err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); | ||
1378 | goto abort_free_ctlr; | ||
1379 | } | ||
1380 | |||
1381 | dbg("%s: SLOT_CTRL %x value read %x\n", __FUNCTION__, SLOT_CTRL, temp_word); | ||
1382 | temp_word = (temp_word & ~HP_INTR_ENABLE & ~CMD_CMPL_INTR_ENABLE) | 0x00; | ||
1383 | |||
1384 | rc = hp_register_write_word(pdev, SLOT_CTRL, temp_word); | ||
1385 | if (rc) { | ||
1386 | err("%s : hp_register_write_word SLOT_CTRL failed\n", __FUNCTION__); | ||
1387 | goto abort_free_ctlr; | ||
1388 | } | ||
1389 | dbg("%s : Mask HPIE hp_register_write_word SLOT_CTRL %x\n", __FUNCTION__, temp_word); | ||
1390 | |||
1391 | rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status); | ||
1392 | if (rc) { | ||
1393 | err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__); | ||
1394 | goto abort_free_ctlr; | ||
1395 | } | ||
1396 | dbg("%s: Mask HPIE SLOT_STATUS offset %x reads slot_status %x\n", __FUNCTION__, SLOT_STATUS, slot_status); | ||
1397 | |||
1398 | temp_word = 0x1F; /* Clear all events */ | ||
1399 | rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_STATUS, temp_word); | ||
1400 | if (rc) { | ||
1401 | err("%s : hp_register_write_word SLOT_STATUS failed\n", __FUNCTION__); | ||
1402 | goto abort_free_ctlr; | ||
1403 | } | ||
1404 | dbg("%s: SLOT_STATUS offset %x writes slot_status %x\n", __FUNCTION__, SLOT_STATUS, temp_word); | ||
1405 | |||
1406 | if (pciehp_poll_mode) {/* Install interrupt polling code */ | ||
1407 | /* Install and start the interrupt polling timer */ | ||
1408 | init_timer(&php_ctlr->int_poll_timer); | ||
1409 | start_int_poll_timer( php_ctlr, 10 ); /* start with 10 second delay */ | ||
1410 | } else { | ||
1411 | /* Installs the interrupt handler */ | ||
1412 | rc = request_irq(php_ctlr->irq, pcie_isr, SA_SHIRQ, MY_NAME, (void *) ctrl); | ||
1413 | dbg("%s: request_irq %d for hpc%d (returns %d)\n", __FUNCTION__, php_ctlr->irq, ctlr_seq_num, rc); | ||
1414 | if (rc) { | ||
1415 | err("Can't get irq %d for the hotplug controller\n", php_ctlr->irq); | ||
1416 | goto abort_free_ctlr; | ||
1417 | } | ||
1418 | } | ||
1419 | |||
1420 | rc = hp_register_read_word(pdev, SLOT_CTRL, temp_word); | ||
1421 | if (rc) { | ||
1422 | err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); | ||
1423 | goto abort_free_ctlr; | ||
1424 | } | ||
1425 | dbg("%s: SLOT_CTRL %x value read %x\n", __FUNCTION__, SLOT_CTRL, temp_word); | ||
1426 | dbg("%s: slot_cap %x\n", __FUNCTION__, slot_cap); | ||
1427 | |||
1428 | intr_enable = intr_enable | PRSN_DETECT_ENABLE; | ||
1429 | |||
1430 | if (ATTN_BUTTN(slot_cap)) | ||
1431 | intr_enable = intr_enable | ATTN_BUTTN_ENABLE; | ||
1432 | |||
1433 | if (POWER_CTRL(slot_cap)) | ||
1434 | intr_enable = intr_enable | PWR_FAULT_DETECT_ENABLE; | ||
1435 | |||
1436 | if (MRL_SENS(slot_cap)) | ||
1437 | intr_enable = intr_enable | MRL_DETECT_ENABLE; | ||
1438 | |||
1439 | temp_word = (temp_word & ~intr_enable) | intr_enable; | ||
1440 | |||
1441 | if (pciehp_poll_mode) { | ||
1442 | temp_word = (temp_word & ~HP_INTR_ENABLE) | 0x0; | ||
1443 | } else { | ||
1444 | temp_word = (temp_word & ~HP_INTR_ENABLE) | HP_INTR_ENABLE; | ||
1445 | } | ||
1446 | dbg("%s: temp_word %x\n", __FUNCTION__, temp_word); | ||
1447 | |||
1448 | /* Unmask Hot-plug Interrupt Enable for the interrupt notification mechanism case */ | ||
1449 | rc = hp_register_write_word(pdev, SLOT_CTRL, temp_word); | ||
1450 | if (rc) { | ||
1451 | err("%s : hp_register_write_word SLOT_CTRL failed\n", __FUNCTION__); | ||
1452 | goto abort_free_ctlr; | ||
1453 | } | ||
1454 | dbg("%s : Unmask HPIE hp_register_write_word SLOT_CTRL with %x\n", __FUNCTION__, temp_word); | ||
1455 | rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status); | ||
1456 | if (rc) { | ||
1457 | err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__); | ||
1458 | goto abort_free_ctlr; | ||
1459 | } | ||
1460 | dbg("%s: Unmask HPIE SLOT_STATUS offset %x reads slot_status %x\n", __FUNCTION__, | ||
1461 | SLOT_STATUS, slot_status); | ||
1462 | |||
1463 | temp_word = 0x1F; /* Clear all events */ | ||
1464 | rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_STATUS, temp_word); | ||
1465 | if (rc) { | ||
1466 | err("%s : hp_register_write_word SLOT_STATUS failed\n", __FUNCTION__); | ||
1467 | goto abort_free_ctlr; | ||
1468 | } | ||
1469 | dbg("%s: SLOT_STATUS offset %x writes slot_status %x\n", __FUNCTION__, SLOT_STATUS, temp_word); | ||
1470 | |||
1471 | /* Add this HPC instance into the HPC list */ | ||
1472 | spin_lock(&list_lock); | ||
1473 | if (php_ctlr_list_head == 0) { | ||
1474 | php_ctlr_list_head = php_ctlr; | ||
1475 | p = php_ctlr_list_head; | ||
1476 | p->pnext = NULL; | ||
1477 | } else { | ||
1478 | p = php_ctlr_list_head; | ||
1479 | |||
1480 | while (p->pnext) | ||
1481 | p = p->pnext; | ||
1482 | |||
1483 | p->pnext = php_ctlr; | ||
1484 | } | ||
1485 | spin_unlock(&list_lock); | ||
1486 | |||
1487 | ctlr_seq_num++; | ||
1488 | ctrl->hpc_ctlr_handle = php_ctlr; | ||
1489 | ctrl->hpc_ops = &pciehp_hpc_ops; | ||
1490 | |||
1491 | DBG_LEAVE_ROUTINE | ||
1492 | return 0; | ||
1493 | |||
1494 | /* We end up here for the many possible ways to fail this API. */ | ||
1495 | abort_free_ctlr: | ||
1496 | pcie_cap_base = saved_cap_base; | ||
1497 | kfree(php_ctlr); | ||
1498 | abort: | ||
1499 | DBG_LEAVE_ROUTINE | ||
1500 | return -1; | ||
1501 | } | ||
diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c new file mode 100644 index 000000000000..723b12c0bb7c --- /dev/null +++ b/drivers/pci/hotplug/pciehp_pci.c | |||
@@ -0,0 +1,827 @@ | |||
1 | /* | ||
2 | * PCI Express Hot Plug Controller Driver | ||
3 | * | ||
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | ||
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * Copyright (C) 2001 IBM Corp. | ||
7 | * Copyright (C) 2003-2004 Intel Corporation | ||
8 | * | ||
9 | * All rights reserved. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or (at | ||
14 | * your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
19 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
20 | * details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
25 | * | ||
26 | * Send feedback to <greg@kroah.com>, <dely.l.sy@intel.com> | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | #include <linux/config.h> | ||
31 | #include <linux/module.h> | ||
32 | #include <linux/kernel.h> | ||
33 | #include <linux/types.h> | ||
34 | #include <linux/slab.h> | ||
35 | #include <linux/workqueue.h> | ||
36 | #include <linux/proc_fs.h> | ||
37 | #include <linux/pci.h> | ||
38 | #include "../pci.h" | ||
39 | #include "pciehp.h" | ||
40 | #ifndef CONFIG_IA64 | ||
41 | #include "../../../arch/i386/pci/pci.h" /* horrible hack showing how processor dependant we are... */ | ||
42 | #endif | ||
43 | |||
44 | |||
45 | int pciehp_configure_device (struct controller* ctrl, struct pci_func* func) | ||
46 | { | ||
47 | unsigned char bus; | ||
48 | struct pci_bus *child; | ||
49 | int num; | ||
50 | |||
51 | if (func->pci_dev == NULL) | ||
52 | func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function)); | ||
53 | |||
54 | /* Still NULL ? Well then scan for it ! */ | ||
55 | if (func->pci_dev == NULL) { | ||
56 | dbg("%s: pci_dev still null. do pci_scan_slot\n", __FUNCTION__); | ||
57 | |||
58 | num = pci_scan_slot(ctrl->pci_dev->subordinate, PCI_DEVFN(func->device, func->function)); | ||
59 | |||
60 | if (num) | ||
61 | pci_bus_add_devices(ctrl->pci_dev->subordinate); | ||
62 | |||
63 | func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function)); | ||
64 | if (func->pci_dev == NULL) { | ||
65 | dbg("ERROR: pci_dev still null\n"); | ||
66 | return 0; | ||
67 | } | ||
68 | } | ||
69 | |||
70 | if (func->pci_dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { | ||
71 | pci_read_config_byte(func->pci_dev, PCI_SECONDARY_BUS, &bus); | ||
72 | child = pci_add_new_bus(func->pci_dev->bus, (func->pci_dev), bus); | ||
73 | pci_do_scan_bus(child); | ||
74 | |||
75 | } | ||
76 | |||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | |||
81 | int pciehp_unconfigure_device(struct pci_func* func) | ||
82 | { | ||
83 | int rc = 0; | ||
84 | int j; | ||
85 | struct pci_bus *pbus; | ||
86 | |||
87 | dbg("%s: bus/dev/func = %x/%x/%x\n", __FUNCTION__, func->bus, | ||
88 | func->device, func->function); | ||
89 | pbus = func->pci_dev->bus; | ||
90 | |||
91 | for (j=0; j<8 ; j++) { | ||
92 | struct pci_dev* temp = pci_find_slot(func->bus, | ||
93 | (func->device << 3) | j); | ||
94 | if (temp) { | ||
95 | pci_remove_bus_device(temp); | ||
96 | } | ||
97 | } | ||
98 | /* | ||
99 | * Some PCI Express root ports require fixup after hot-plug operation. | ||
100 | */ | ||
101 | if (pcie_mch_quirk) | ||
102 | pci_fixup_device(pci_fixup_final, pbus->self); | ||
103 | |||
104 | return rc; | ||
105 | } | ||
106 | |||
107 | /* | ||
108 | * pciehp_set_irq | ||
109 | * | ||
110 | * @bus_num: bus number of PCI device | ||
111 | * @dev_num: device number of PCI device | ||
112 | * @slot: pointer to u8 where slot number will be returned | ||
113 | */ | ||
114 | int pciehp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num) | ||
115 | { | ||
116 | #if defined(CONFIG_X86) && !defined(CONFIG_X86_IO_APIC) && !defined(CONFIG_X86_64) | ||
117 | int rc; | ||
118 | u16 temp_word; | ||
119 | struct pci_dev fakedev; | ||
120 | struct pci_bus fakebus; | ||
121 | |||
122 | fakedev.devfn = dev_num << 3; | ||
123 | fakedev.bus = &fakebus; | ||
124 | fakebus.number = bus_num; | ||
125 | dbg("%s: dev %d, bus %d, pin %d, num %d\n", | ||
126 | __FUNCTION__, dev_num, bus_num, int_pin, irq_num); | ||
127 | rc = pcibios_set_irq_routing(&fakedev, int_pin - 0x0a, irq_num); | ||
128 | dbg("%s: rc %d\n", __FUNCTION__, rc); | ||
129 | if (!rc) | ||
130 | return !rc; | ||
131 | |||
132 | /* set the Edge Level Control Register (ELCR) */ | ||
133 | temp_word = inb(0x4d0); | ||
134 | temp_word |= inb(0x4d1) << 8; | ||
135 | |||
136 | temp_word |= 0x01 << irq_num; | ||
137 | |||
138 | /* This should only be for x86 as it sets the Edge Level Control Register */ | ||
139 | outb((u8) (temp_word & 0xFF), 0x4d0); | ||
140 | outb((u8) ((temp_word & 0xFF00) >> 8), 0x4d1); | ||
141 | #endif | ||
142 | return 0; | ||
143 | } | ||
144 | |||
145 | /* More PCI configuration routines; this time centered around hotplug controller */ | ||
146 | |||
147 | |||
148 | /* | ||
149 | * pciehp_save_config | ||
150 | * | ||
151 | * Reads configuration for all slots in a PCI bus and saves info. | ||
152 | * | ||
153 | * Note: For non-hot plug busses, the slot # saved is the device # | ||
154 | * | ||
155 | * returns 0 if success | ||
156 | */ | ||
157 | int pciehp_save_config(struct controller *ctrl, int busnumber, int num_ctlr_slots, int first_device_num) | ||
158 | { | ||
159 | int rc; | ||
160 | u8 class_code; | ||
161 | u8 header_type; | ||
162 | u32 ID; | ||
163 | u8 secondary_bus; | ||
164 | struct pci_func *new_slot; | ||
165 | int sub_bus; | ||
166 | int max_functions; | ||
167 | int function; | ||
168 | u8 DevError; | ||
169 | int device = 0; | ||
170 | int cloop = 0; | ||
171 | int stop_it; | ||
172 | int index; | ||
173 | int is_hot_plug = num_ctlr_slots || first_device_num; | ||
174 | struct pci_bus lpci_bus, *pci_bus; | ||
175 | int FirstSupported, LastSupported; | ||
176 | |||
177 | dbg("%s: Enter\n", __FUNCTION__); | ||
178 | |||
179 | memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus)); | ||
180 | pci_bus = &lpci_bus; | ||
181 | |||
182 | dbg("%s: num_ctlr_slots = %d, first_device_num = %d\n", __FUNCTION__, | ||
183 | num_ctlr_slots, first_device_num); | ||
184 | |||
185 | /* Decide which slots are supported */ | ||
186 | if (is_hot_plug) { | ||
187 | /********************************* | ||
188 | * is_hot_plug is the slot mask | ||
189 | *********************************/ | ||
190 | FirstSupported = first_device_num; | ||
191 | LastSupported = FirstSupported + num_ctlr_slots - 1; | ||
192 | } else { | ||
193 | FirstSupported = 0; | ||
194 | LastSupported = 0x1F; | ||
195 | } | ||
196 | |||
197 | dbg("FirstSupported = %d, LastSupported = %d\n", FirstSupported, | ||
198 | LastSupported); | ||
199 | |||
200 | /* Save PCI configuration space for all devices in supported slots */ | ||
201 | dbg("%s: pci_bus->number = %x\n", __FUNCTION__, pci_bus->number); | ||
202 | pci_bus->number = busnumber; | ||
203 | dbg("%s: bus = %x, dev = %x\n", __FUNCTION__, busnumber, device); | ||
204 | for (device = FirstSupported; device <= LastSupported; device++) { | ||
205 | ID = 0xFFFFFFFF; | ||
206 | rc = pci_bus_read_config_dword(pci_bus, PCI_DEVFN(device, 0), | ||
207 | PCI_VENDOR_ID, &ID); | ||
208 | |||
209 | if (ID != 0xFFFFFFFF) { /* device in slot */ | ||
210 | dbg("%s: ID = %x\n", __FUNCTION__, ID); | ||
211 | rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, 0), | ||
212 | 0x0B, &class_code); | ||
213 | if (rc) | ||
214 | return rc; | ||
215 | |||
216 | rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, 0), | ||
217 | PCI_HEADER_TYPE, &header_type); | ||
218 | if (rc) | ||
219 | return rc; | ||
220 | |||
221 | dbg("class_code = %x, header_type = %x\n", class_code, header_type); | ||
222 | |||
223 | /* If multi-function device, set max_functions to 8 */ | ||
224 | if (header_type & 0x80) | ||
225 | max_functions = 8; | ||
226 | else | ||
227 | max_functions = 1; | ||
228 | |||
229 | function = 0; | ||
230 | |||
231 | do { | ||
232 | DevError = 0; | ||
233 | dbg("%s: In do loop\n", __FUNCTION__); | ||
234 | |||
235 | if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* P-P Bridge */ | ||
236 | /* Recurse the subordinate bus | ||
237 | * get the subordinate bus number | ||
238 | */ | ||
239 | rc = pci_bus_read_config_byte(pci_bus, | ||
240 | PCI_DEVFN(device, function), | ||
241 | PCI_SECONDARY_BUS, &secondary_bus); | ||
242 | if (rc) { | ||
243 | return rc; | ||
244 | } else { | ||
245 | sub_bus = (int) secondary_bus; | ||
246 | |||
247 | /* Save secondary bus cfg spc with this recursive call. */ | ||
248 | rc = pciehp_save_config(ctrl, sub_bus, 0, 0); | ||
249 | if (rc) | ||
250 | return rc; | ||
251 | } | ||
252 | } | ||
253 | |||
254 | index = 0; | ||
255 | new_slot = pciehp_slot_find(busnumber, device, index++); | ||
256 | |||
257 | dbg("%s: new_slot = %p bus %x dev %x fun %x\n", | ||
258 | __FUNCTION__, new_slot, busnumber, device, index-1); | ||
259 | |||
260 | while (new_slot && (new_slot->function != (u8) function)) { | ||
261 | new_slot = pciehp_slot_find(busnumber, device, index++); | ||
262 | dbg("%s: while loop, new_slot = %p bus %x dev %x fun %x\n", | ||
263 | __FUNCTION__, new_slot, busnumber, device, index-1); | ||
264 | } | ||
265 | if (!new_slot) { | ||
266 | /* Setup slot structure. */ | ||
267 | new_slot = pciehp_slot_create(busnumber); | ||
268 | dbg("%s: if, new_slot = %p bus %x dev %x fun %x\n", | ||
269 | __FUNCTION__, new_slot, busnumber, device, function); | ||
270 | |||
271 | if (new_slot == NULL) | ||
272 | return(1); | ||
273 | } | ||
274 | |||
275 | new_slot->bus = (u8) busnumber; | ||
276 | new_slot->device = (u8) device; | ||
277 | new_slot->function = (u8) function; | ||
278 | new_slot->is_a_board = 1; | ||
279 | new_slot->switch_save = 0x10; | ||
280 | /* In case of unsupported board */ | ||
281 | new_slot->status = DevError; | ||
282 | new_slot->pci_dev = pci_find_slot(new_slot->bus, | ||
283 | (new_slot->device << 3) | new_slot->function); | ||
284 | dbg("new_slot->pci_dev = %p\n", new_slot->pci_dev); | ||
285 | |||
286 | for (cloop = 0; cloop < 0x20; cloop++) { | ||
287 | rc = pci_bus_read_config_dword(pci_bus, | ||
288 | PCI_DEVFN(device, function), | ||
289 | cloop << 2, | ||
290 | (u32 *) &(new_slot->config_space [cloop])); | ||
291 | /* dbg("new_slot->config_space[%x] = %x\n", | ||
292 | cloop, new_slot->config_space[cloop]); */ | ||
293 | if (rc) | ||
294 | return rc; | ||
295 | } | ||
296 | |||
297 | function++; | ||
298 | |||
299 | stop_it = 0; | ||
300 | |||
301 | /* this loop skips to the next present function | ||
302 | * reading in Class Code and Header type. | ||
303 | */ | ||
304 | |||
305 | while ((function < max_functions)&&(!stop_it)) { | ||
306 | dbg("%s: In while loop \n", __FUNCTION__); | ||
307 | rc = pci_bus_read_config_dword(pci_bus, | ||
308 | PCI_DEVFN(device, function), | ||
309 | PCI_VENDOR_ID, &ID); | ||
310 | |||
311 | if (ID == 0xFFFFFFFF) { /* nothing there. */ | ||
312 | function++; | ||
313 | dbg("Nothing there\n"); | ||
314 | } else { /* Something there */ | ||
315 | rc = pci_bus_read_config_byte(pci_bus, | ||
316 | PCI_DEVFN(device, function), | ||
317 | 0x0B, &class_code); | ||
318 | if (rc) | ||
319 | return rc; | ||
320 | |||
321 | rc = pci_bus_read_config_byte(pci_bus, | ||
322 | PCI_DEVFN(device, function), | ||
323 | PCI_HEADER_TYPE, &header_type); | ||
324 | if (rc) | ||
325 | return rc; | ||
326 | |||
327 | dbg("class_code = %x, header_type = %x\n", class_code, header_type); | ||
328 | stop_it++; | ||
329 | } | ||
330 | } | ||
331 | |||
332 | } while (function < max_functions); | ||
333 | /* End of IF (device in slot?) */ | ||
334 | } else if (is_hot_plug) { | ||
335 | /* Setup slot structure with entry for empty slot */ | ||
336 | new_slot = pciehp_slot_create(busnumber); | ||
337 | |||
338 | if (new_slot == NULL) { | ||
339 | return(1); | ||
340 | } | ||
341 | dbg("new_slot = %p, bus = %x, dev = %x, fun = %x\n", new_slot, | ||
342 | new_slot->bus, new_slot->device, new_slot->function); | ||
343 | |||
344 | new_slot->bus = (u8) busnumber; | ||
345 | new_slot->device = (u8) device; | ||
346 | new_slot->function = 0; | ||
347 | new_slot->is_a_board = 0; | ||
348 | new_slot->presence_save = 0; | ||
349 | new_slot->switch_save = 0; | ||
350 | } | ||
351 | } /* End of FOR loop */ | ||
352 | |||
353 | dbg("%s: Exit\n", __FUNCTION__); | ||
354 | return(0); | ||
355 | } | ||
356 | |||
357 | |||
358 | /* | ||
359 | * pciehp_save_slot_config | ||
360 | * | ||
361 | * Saves configuration info for all PCI devices in a given slot | ||
362 | * including subordinate busses. | ||
363 | * | ||
364 | * returns 0 if success | ||
365 | */ | ||
366 | int pciehp_save_slot_config(struct controller *ctrl, struct pci_func * new_slot) | ||
367 | { | ||
368 | int rc; | ||
369 | u8 class_code; | ||
370 | u8 header_type; | ||
371 | u32 ID; | ||
372 | u8 secondary_bus; | ||
373 | int sub_bus; | ||
374 | int max_functions; | ||
375 | int function; | ||
376 | int cloop = 0; | ||
377 | int stop_it; | ||
378 | struct pci_bus lpci_bus, *pci_bus; | ||
379 | memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus)); | ||
380 | pci_bus = &lpci_bus; | ||
381 | pci_bus->number = new_slot->bus; | ||
382 | |||
383 | ID = 0xFFFFFFFF; | ||
384 | |||
385 | pci_bus_read_config_dword(pci_bus, PCI_DEVFN(new_slot->device, 0), | ||
386 | PCI_VENDOR_ID, &ID); | ||
387 | |||
388 | if (ID != 0xFFFFFFFF) { /* device in slot */ | ||
389 | pci_bus_read_config_byte(pci_bus, PCI_DEVFN(new_slot->device, 0), | ||
390 | 0x0B, &class_code); | ||
391 | |||
392 | pci_bus_read_config_byte(pci_bus, PCI_DEVFN(new_slot->device, 0), | ||
393 | PCI_HEADER_TYPE, &header_type); | ||
394 | |||
395 | if (header_type & 0x80) /* Multi-function device */ | ||
396 | max_functions = 8; | ||
397 | else | ||
398 | max_functions = 1; | ||
399 | |||
400 | function = 0; | ||
401 | |||
402 | do { | ||
403 | if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* PCI-PCI Bridge */ | ||
404 | /* Recurse the subordinate bus */ | ||
405 | pci_bus_read_config_byte(pci_bus, | ||
406 | PCI_DEVFN(new_slot->device, function), | ||
407 | PCI_SECONDARY_BUS, &secondary_bus); | ||
408 | |||
409 | sub_bus = (int) secondary_bus; | ||
410 | |||
411 | /* Save the config headers for the secondary bus. */ | ||
412 | rc = pciehp_save_config(ctrl, sub_bus, 0, 0); | ||
413 | |||
414 | if (rc) | ||
415 | return rc; | ||
416 | |||
417 | } /* End of IF */ | ||
418 | |||
419 | new_slot->status = 0; | ||
420 | |||
421 | for (cloop = 0; cloop < 0x20; cloop++) { | ||
422 | pci_bus_read_config_dword(pci_bus, | ||
423 | PCI_DEVFN(new_slot->device, function), | ||
424 | cloop << 2, | ||
425 | (u32 *) &(new_slot->config_space [cloop])); | ||
426 | } | ||
427 | |||
428 | function++; | ||
429 | |||
430 | stop_it = 0; | ||
431 | |||
432 | /* this loop skips to the next present function | ||
433 | * reading in the Class Code and the Header type. | ||
434 | */ | ||
435 | |||
436 | while ((function < max_functions) && (!stop_it)) { | ||
437 | pci_bus_read_config_dword(pci_bus, | ||
438 | PCI_DEVFN(new_slot->device, function), | ||
439 | PCI_VENDOR_ID, &ID); | ||
440 | |||
441 | if (ID == 0xFFFFFFFF) { /* nothing there. */ | ||
442 | function++; | ||
443 | } else { /* Something there */ | ||
444 | pci_bus_read_config_byte(pci_bus, | ||
445 | PCI_DEVFN(new_slot->device, function), | ||
446 | 0x0B, &class_code); | ||
447 | |||
448 | pci_bus_read_config_byte(pci_bus, | ||
449 | PCI_DEVFN(new_slot->device, function), | ||
450 | PCI_HEADER_TYPE, &header_type); | ||
451 | |||
452 | stop_it++; | ||
453 | } | ||
454 | } | ||
455 | |||
456 | } while (function < max_functions); | ||
457 | } /* End of IF (device in slot?) */ | ||
458 | else { | ||
459 | return 2; | ||
460 | } | ||
461 | |||
462 | return 0; | ||
463 | } | ||
464 | |||
465 | |||
466 | /* | ||
467 | * pciehp_save_used_resources | ||
468 | * | ||
469 | * Stores used resource information for existing boards. this is | ||
470 | * for boards that were in the system when this driver was loaded. | ||
471 | * this function is for hot plug ADD | ||
472 | * | ||
473 | * returns 0 if success | ||
474 | * if disable == 1(DISABLE_CARD), | ||
475 | * it loops for all functions of the slot and disables them. | ||
476 | * else, it just get resources of the function and return. | ||
477 | */ | ||
478 | int pciehp_save_used_resources(struct controller *ctrl, struct pci_func *func, int disable) | ||
479 | { | ||
480 | u8 cloop; | ||
481 | u8 header_type; | ||
482 | u8 secondary_bus; | ||
483 | u8 temp_byte; | ||
484 | u16 command; | ||
485 | u16 save_command; | ||
486 | u16 w_base, w_length; | ||
487 | u32 temp_register; | ||
488 | u32 save_base; | ||
489 | u32 base, length; | ||
490 | u64 base64 = 0; | ||
491 | int index = 0; | ||
492 | unsigned int devfn; | ||
493 | struct pci_resource *mem_node = NULL; | ||
494 | struct pci_resource *p_mem_node = NULL; | ||
495 | struct pci_resource *t_mem_node; | ||
496 | struct pci_resource *io_node; | ||
497 | struct pci_resource *bus_node; | ||
498 | struct pci_bus lpci_bus, *pci_bus; | ||
499 | memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus)); | ||
500 | pci_bus = &lpci_bus; | ||
501 | |||
502 | if (disable) | ||
503 | func = pciehp_slot_find(func->bus, func->device, index++); | ||
504 | |||
505 | while ((func != NULL) && func->is_a_board) { | ||
506 | pci_bus->number = func->bus; | ||
507 | devfn = PCI_DEVFN(func->device, func->function); | ||
508 | |||
509 | /* Save the command register */ | ||
510 | pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &save_command); | ||
511 | |||
512 | if (disable) { | ||
513 | /* disable card */ | ||
514 | command = 0x00; | ||
515 | pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command); | ||
516 | } | ||
517 | |||
518 | /* Check for Bridge */ | ||
519 | pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &header_type); | ||
520 | |||
521 | if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* PCI-PCI Bridge */ | ||
522 | dbg("Save_used_res of PCI bridge b:d=0x%x:%x, sc=0x%x\n", | ||
523 | func->bus, func->device, save_command); | ||
524 | if (disable) { | ||
525 | /* Clear Bridge Control Register */ | ||
526 | command = 0x00; | ||
527 | pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, command); | ||
528 | } | ||
529 | |||
530 | pci_bus_read_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus); | ||
531 | pci_bus_read_config_byte(pci_bus, devfn, PCI_SUBORDINATE_BUS, &temp_byte); | ||
532 | |||
533 | bus_node = kmalloc(sizeof(struct pci_resource), | ||
534 | GFP_KERNEL); | ||
535 | if (!bus_node) | ||
536 | return -ENOMEM; | ||
537 | |||
538 | bus_node->base = (ulong)secondary_bus; | ||
539 | bus_node->length = (ulong)(temp_byte - secondary_bus + 1); | ||
540 | |||
541 | bus_node->next = func->bus_head; | ||
542 | func->bus_head = bus_node; | ||
543 | |||
544 | /* Save IO base and Limit registers */ | ||
545 | pci_bus_read_config_byte(pci_bus, devfn, PCI_IO_BASE, &temp_byte); | ||
546 | base = temp_byte; | ||
547 | pci_bus_read_config_byte(pci_bus, devfn, PCI_IO_LIMIT, &temp_byte); | ||
548 | length = temp_byte; | ||
549 | |||
550 | if ((base <= length) && (!disable || (save_command & PCI_COMMAND_IO))) { | ||
551 | io_node = kmalloc(sizeof(struct pci_resource), | ||
552 | GFP_KERNEL); | ||
553 | if (!io_node) | ||
554 | return -ENOMEM; | ||
555 | |||
556 | io_node->base = (ulong)(base & PCI_IO_RANGE_MASK) << 8; | ||
557 | io_node->length = (ulong)(length - base + 0x10) << 8; | ||
558 | |||
559 | io_node->next = func->io_head; | ||
560 | func->io_head = io_node; | ||
561 | } | ||
562 | |||
563 | /* Save memory base and Limit registers */ | ||
564 | pci_bus_read_config_word(pci_bus, devfn, PCI_MEMORY_BASE, &w_base); | ||
565 | pci_bus_read_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, &w_length); | ||
566 | |||
567 | if ((w_base <= w_length) && (!disable || (save_command & PCI_COMMAND_MEMORY))) { | ||
568 | mem_node = kmalloc(sizeof(struct pci_resource), | ||
569 | GFP_KERNEL); | ||
570 | if (!mem_node) | ||
571 | return -ENOMEM; | ||
572 | |||
573 | mem_node->base = (ulong)w_base << 16; | ||
574 | mem_node->length = (ulong)(w_length - w_base + 0x10) << 16; | ||
575 | |||
576 | mem_node->next = func->mem_head; | ||
577 | func->mem_head = mem_node; | ||
578 | } | ||
579 | /* Save prefetchable memory base and Limit registers */ | ||
580 | pci_bus_read_config_word(pci_bus, devfn, PCI_PREF_MEMORY_BASE, &w_base); | ||
581 | pci_bus_read_config_word(pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &w_length); | ||
582 | |||
583 | if ((w_base <= w_length) && (!disable || (save_command & PCI_COMMAND_MEMORY))) { | ||
584 | p_mem_node = kmalloc(sizeof(struct pci_resource), | ||
585 | GFP_KERNEL); | ||
586 | if (!p_mem_node) | ||
587 | return -ENOMEM; | ||
588 | |||
589 | p_mem_node->base = (ulong)w_base << 16; | ||
590 | p_mem_node->length = (ulong)(w_length - w_base + 0x10) << 16; | ||
591 | |||
592 | p_mem_node->next = func->p_mem_head; | ||
593 | func->p_mem_head = p_mem_node; | ||
594 | } | ||
595 | } else if ((header_type & 0x7F) == PCI_HEADER_TYPE_NORMAL) { | ||
596 | dbg("Save_used_res of PCI adapter b:d=0x%x:%x, sc=0x%x\n", | ||
597 | func->bus, func->device, save_command); | ||
598 | |||
599 | /* Figure out IO and memory base lengths */ | ||
600 | for (cloop = PCI_BASE_ADDRESS_0; cloop <= PCI_BASE_ADDRESS_5; cloop += 4) { | ||
601 | pci_bus_read_config_dword(pci_bus, devfn, cloop, &save_base); | ||
602 | |||
603 | temp_register = 0xFFFFFFFF; | ||
604 | pci_bus_write_config_dword(pci_bus, devfn, cloop, temp_register); | ||
605 | pci_bus_read_config_dword(pci_bus, devfn, cloop, &temp_register); | ||
606 | |||
607 | if (!disable) | ||
608 | pci_bus_write_config_dword(pci_bus, devfn, cloop, save_base); | ||
609 | |||
610 | if (!temp_register) | ||
611 | continue; | ||
612 | |||
613 | base = temp_register; | ||
614 | |||
615 | if ((base & PCI_BASE_ADDRESS_SPACE_IO) && | ||
616 | (!disable || (save_command & PCI_COMMAND_IO))) { | ||
617 | /* IO base */ | ||
618 | /* set temp_register = amount of IO space requested */ | ||
619 | base = base & 0xFFFFFFFCL; | ||
620 | base = (~base) + 1; | ||
621 | |||
622 | io_node = kmalloc(sizeof (struct pci_resource), | ||
623 | GFP_KERNEL); | ||
624 | if (!io_node) | ||
625 | return -ENOMEM; | ||
626 | |||
627 | io_node->base = (ulong)save_base & PCI_BASE_ADDRESS_IO_MASK; | ||
628 | io_node->length = (ulong)base; | ||
629 | dbg("sur adapter: IO bar=0x%x(length=0x%x)\n", | ||
630 | io_node->base, io_node->length); | ||
631 | |||
632 | io_node->next = func->io_head; | ||
633 | func->io_head = io_node; | ||
634 | } else { /* map Memory */ | ||
635 | int prefetchable = 1; | ||
636 | /* struct pci_resources **res_node; */ | ||
637 | char *res_type_str = "PMEM"; | ||
638 | u32 temp_register2; | ||
639 | |||
640 | t_mem_node = kmalloc(sizeof (struct pci_resource), | ||
641 | GFP_KERNEL); | ||
642 | if (!t_mem_node) | ||
643 | return -ENOMEM; | ||
644 | |||
645 | if (!(base & PCI_BASE_ADDRESS_MEM_PREFETCH) && | ||
646 | (!disable || (save_command & PCI_COMMAND_MEMORY))) { | ||
647 | prefetchable = 0; | ||
648 | mem_node = t_mem_node; | ||
649 | res_type_str++; | ||
650 | } else | ||
651 | p_mem_node = t_mem_node; | ||
652 | |||
653 | base = base & 0xFFFFFFF0L; | ||
654 | base = (~base) + 1; | ||
655 | |||
656 | switch (temp_register & PCI_BASE_ADDRESS_MEM_TYPE_MASK) { | ||
657 | case PCI_BASE_ADDRESS_MEM_TYPE_32: | ||
658 | if (prefetchable) { | ||
659 | p_mem_node->base = (ulong)save_base & PCI_BASE_ADDRESS_MEM_MASK; | ||
660 | p_mem_node->length = (ulong)base; | ||
661 | dbg("sur adapter: 32 %s bar=0x%x(length=0x%x)\n", | ||
662 | res_type_str, | ||
663 | p_mem_node->base, | ||
664 | p_mem_node->length); | ||
665 | |||
666 | p_mem_node->next = func->p_mem_head; | ||
667 | func->p_mem_head = p_mem_node; | ||
668 | } else { | ||
669 | mem_node->base = (ulong)save_base & PCI_BASE_ADDRESS_MEM_MASK; | ||
670 | mem_node->length = (ulong)base; | ||
671 | dbg("sur adapter: 32 %s bar=0x%x(length=0x%x)\n", | ||
672 | res_type_str, | ||
673 | mem_node->base, | ||
674 | mem_node->length); | ||
675 | |||
676 | mem_node->next = func->mem_head; | ||
677 | func->mem_head = mem_node; | ||
678 | } | ||
679 | break; | ||
680 | case PCI_BASE_ADDRESS_MEM_TYPE_64: | ||
681 | pci_bus_read_config_dword(pci_bus, devfn, cloop+4, &temp_register2); | ||
682 | base64 = temp_register2; | ||
683 | base64 = (base64 << 32) | save_base; | ||
684 | |||
685 | if (temp_register2) { | ||
686 | dbg("sur adapter: 64 %s high dword of base64(0x%x:%x) masked to 0\n", | ||
687 | res_type_str, temp_register2, (u32)base64); | ||
688 | base64 &= 0x00000000FFFFFFFFL; | ||
689 | } | ||
690 | |||
691 | if (prefetchable) { | ||
692 | p_mem_node->base = base64 & PCI_BASE_ADDRESS_MEM_MASK; | ||
693 | p_mem_node->length = base; | ||
694 | dbg("sur adapter: 64 %s base=0x%x(len=0x%x)\n", | ||
695 | res_type_str, | ||
696 | p_mem_node->base, | ||
697 | p_mem_node->length); | ||
698 | |||
699 | p_mem_node->next = func->p_mem_head; | ||
700 | func->p_mem_head = p_mem_node; | ||
701 | } else { | ||
702 | mem_node->base = base64 & PCI_BASE_ADDRESS_MEM_MASK; | ||
703 | mem_node->length = base; | ||
704 | dbg("sur adapter: 64 %s base=0x%x(len=0x%x)\n", | ||
705 | res_type_str, | ||
706 | mem_node->base, | ||
707 | mem_node->length); | ||
708 | |||
709 | mem_node->next = func->mem_head; | ||
710 | func->mem_head = mem_node; | ||
711 | } | ||
712 | cloop += 4; | ||
713 | break; | ||
714 | default: | ||
715 | dbg("asur: reserved BAR type=0x%x\n", | ||
716 | temp_register); | ||
717 | break; | ||
718 | } | ||
719 | } | ||
720 | } /* End of base register loop */ | ||
721 | } else { /* Some other unknown header type */ | ||
722 | dbg("Save_used_res of PCI unknown type b:d=0x%x:%x. skip.\n", | ||
723 | func->bus, func->device); | ||
724 | } | ||
725 | |||
726 | /* find the next device in this slot */ | ||
727 | if (!disable) | ||
728 | break; | ||
729 | func = pciehp_slot_find(func->bus, func->device, index++); | ||
730 | } | ||
731 | |||
732 | return 0; | ||
733 | } | ||
734 | |||
735 | |||
736 | /** | ||
737 | * kfree_resource_list: release memory of all list members | ||
738 | * @res: resource list to free | ||
739 | */ | ||
740 | static inline void | ||
741 | return_resource_list(struct pci_resource **func, struct pci_resource **res) | ||
742 | { | ||
743 | struct pci_resource *node; | ||
744 | struct pci_resource *t_node; | ||
745 | |||
746 | node = *func; | ||
747 | *func = NULL; | ||
748 | while (node) { | ||
749 | t_node = node->next; | ||
750 | return_resource(res, node); | ||
751 | node = t_node; | ||
752 | } | ||
753 | } | ||
754 | |||
755 | /* | ||
756 | * pciehp_return_board_resources | ||
757 | * | ||
758 | * this routine returns all resources allocated to a board to | ||
759 | * the available pool. | ||
760 | * | ||
761 | * returns 0 if success | ||
762 | */ | ||
763 | int pciehp_return_board_resources(struct pci_func * func, | ||
764 | struct resource_lists * resources) | ||
765 | { | ||
766 | int rc; | ||
767 | |||
768 | dbg("%s\n", __FUNCTION__); | ||
769 | |||
770 | if (!func) | ||
771 | return 1; | ||
772 | |||
773 | return_resource_list(&(func->io_head),&(resources->io_head)); | ||
774 | return_resource_list(&(func->mem_head),&(resources->mem_head)); | ||
775 | return_resource_list(&(func->p_mem_head),&(resources->p_mem_head)); | ||
776 | return_resource_list(&(func->bus_head),&(resources->bus_head)); | ||
777 | |||
778 | rc = pciehp_resource_sort_and_combine(&(resources->mem_head)); | ||
779 | rc |= pciehp_resource_sort_and_combine(&(resources->p_mem_head)); | ||
780 | rc |= pciehp_resource_sort_and_combine(&(resources->io_head)); | ||
781 | rc |= pciehp_resource_sort_and_combine(&(resources->bus_head)); | ||
782 | |||
783 | return rc; | ||
784 | } | ||
785 | |||
786 | /** | ||
787 | * kfree_resource_list: release memory of all list members | ||
788 | * @res: resource list to free | ||
789 | */ | ||
790 | static inline void | ||
791 | kfree_resource_list(struct pci_resource **r) | ||
792 | { | ||
793 | struct pci_resource *res, *tres; | ||
794 | |||
795 | res = *r; | ||
796 | *r = NULL; | ||
797 | |||
798 | while (res) { | ||
799 | tres = res; | ||
800 | res = res->next; | ||
801 | kfree(tres); | ||
802 | } | ||
803 | } | ||
804 | |||
805 | /** | ||
806 | * pciehp_destroy_resource_list: put node back in the resource list | ||
807 | * @resources: list to put nodes back | ||
808 | */ | ||
809 | void pciehp_destroy_resource_list(struct resource_lists * resources) | ||
810 | { | ||
811 | kfree_resource_list(&(resources->io_head)); | ||
812 | kfree_resource_list(&(resources->mem_head)); | ||
813 | kfree_resource_list(&(resources->p_mem_head)); | ||
814 | kfree_resource_list(&(resources->bus_head)); | ||
815 | } | ||
816 | |||
817 | /** | ||
818 | * pciehp_destroy_board_resources: put node back in the resource list | ||
819 | * @resources: list to put nodes back | ||
820 | */ | ||
821 | void pciehp_destroy_board_resources(struct pci_func * func) | ||
822 | { | ||
823 | kfree_resource_list(&(func->io_head)); | ||
824 | kfree_resource_list(&(func->mem_head)); | ||
825 | kfree_resource_list(&(func->p_mem_head)); | ||
826 | kfree_resource_list(&(func->bus_head)); | ||
827 | } | ||
diff --git a/drivers/pci/hotplug/pciehprm.h b/drivers/pci/hotplug/pciehprm.h new file mode 100644 index 000000000000..966775ffb0ff --- /dev/null +++ b/drivers/pci/hotplug/pciehprm.h | |||
@@ -0,0 +1,52 @@ | |||
1 | /* | ||
2 | * PCIEHPRM : PCIEHP Resource Manager for ACPI/non-ACPI platform | ||
3 | * | ||
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | ||
5 | * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * Copyright (C) 2001 IBM Corp. | ||
7 | * Copyright (C) 2003-2004 Intel Corporation | ||
8 | * | ||
9 | * All rights reserved. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or (at | ||
14 | * your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
19 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
20 | * details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
25 | * | ||
26 | * Send feedback to <greg@kroah.com>, <dely.l.sy@intel.com> | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | #ifndef _PCIEHPRM_H_ | ||
31 | #define _PCIEHPRM_H_ | ||
32 | |||
33 | #ifdef CONFIG_HOTPLUG_PCI_PCIE_PHPRM_NONACPI | ||
34 | #include "pciehprm_nonacpi.h" | ||
35 | #endif | ||
36 | |||
37 | int pciehprm_init(enum php_ctlr_type ct); | ||
38 | void pciehprm_cleanup(void); | ||
39 | int pciehprm_print_pirt(void); | ||
40 | int pciehprm_find_available_resources(struct controller *ctrl); | ||
41 | int pciehprm_set_hpp(struct controller *ctrl, struct pci_func *func, u8 card_type); | ||
42 | void pciehprm_enable_card(struct controller *ctrl, struct pci_func *func, u8 card_type); | ||
43 | |||
44 | #ifdef DEBUG | ||
45 | #define RES_CHECK(this, bits) \ | ||
46 | { if (((this) & (bits - 1))) \ | ||
47 | printk("%s:%d ERR: potential res loss!\n", __FUNCTION__, __LINE__); } | ||
48 | #else | ||
49 | #define RES_CHECK(this, bits) | ||
50 | #endif | ||
51 | |||
52 | #endif /* _PCIEHPRM_H_ */ | ||
diff --git a/drivers/pci/hotplug/pciehprm_acpi.c b/drivers/pci/hotplug/pciehprm_acpi.c new file mode 100644 index 000000000000..57f4e6d1b27c --- /dev/null +++ b/drivers/pci/hotplug/pciehprm_acpi.c | |||
@@ -0,0 +1,1737 @@ | |||
1 | /* | ||
2 | * PCIEHPRM ACPI: PHP Resource Manager for ACPI platform | ||
3 | * | ||
4 | * Copyright (C) 2003-2004 Intel Corporation | ||
5 | * | ||
6 | * All rights reserved. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or (at | ||
11 | * your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, but | ||
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
16 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
17 | * details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
22 | * | ||
23 | * Send feedback to <dely.l.sy@intel.com> | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include <linux/config.h> | ||
28 | #include <linux/module.h> | ||
29 | #include <linux/kernel.h> | ||
30 | #include <linux/types.h> | ||
31 | #include <linux/pci.h> | ||
32 | #include <linux/init.h> | ||
33 | #include <linux/acpi.h> | ||
34 | #include <linux/efi.h> | ||
35 | #include <linux/pci-acpi.h> | ||
36 | #include <asm/uaccess.h> | ||
37 | #include <asm/system.h> | ||
38 | #ifdef CONFIG_IA64 | ||
39 | #include <asm/iosapic.h> | ||
40 | #endif | ||
41 | #include <acpi/acpi.h> | ||
42 | #include <acpi/acpi_bus.h> | ||
43 | #include <acpi/actypes.h> | ||
44 | #include "pciehp.h" | ||
45 | #include "pciehprm.h" | ||
46 | |||
47 | #define PCI_MAX_BUS 0x100 | ||
48 | #define ACPI_STA_DEVICE_PRESENT 0x01 | ||
49 | |||
50 | #define METHOD_NAME__SUN "_SUN" | ||
51 | #define METHOD_NAME__HPP "_HPP" | ||
52 | #define METHOD_NAME_OSHP "OSHP" | ||
53 | |||
54 | /* Status code for running acpi method to gain native control */ | ||
55 | #define NC_NOT_RUN 0 | ||
56 | #define OSC_NOT_EXIST 1 | ||
57 | #define OSC_RUN_FAILED 2 | ||
58 | #define OSHP_NOT_EXIST 3 | ||
59 | #define OSHP_RUN_FAILED 4 | ||
60 | #define NC_RUN_SUCCESS 5 | ||
61 | |||
62 | #define PHP_RES_BUS 0xA0 | ||
63 | #define PHP_RES_IO 0xA1 | ||
64 | #define PHP_RES_MEM 0xA2 | ||
65 | #define PHP_RES_PMEM 0xA3 | ||
66 | |||
67 | #define BRIDGE_TYPE_P2P 0x00 | ||
68 | #define BRIDGE_TYPE_HOST 0x01 | ||
69 | |||
70 | /* this should go to drivers/acpi/include/ */ | ||
71 | struct acpi__hpp { | ||
72 | u8 cache_line_size; | ||
73 | u8 latency_timer; | ||
74 | u8 enable_serr; | ||
75 | u8 enable_perr; | ||
76 | }; | ||
77 | |||
78 | struct acpi_php_slot { | ||
79 | struct acpi_php_slot *next; | ||
80 | struct acpi_bridge *bridge; | ||
81 | acpi_handle handle; | ||
82 | int seg; | ||
83 | int bus; | ||
84 | int dev; | ||
85 | int fun; | ||
86 | u32 sun; | ||
87 | struct pci_resource *mem_head; | ||
88 | struct pci_resource *p_mem_head; | ||
89 | struct pci_resource *io_head; | ||
90 | struct pci_resource *bus_head; | ||
91 | void *slot_ops; /* _STA, _EJx, etc */ | ||
92 | struct slot *slot; | ||
93 | }; /* per func */ | ||
94 | |||
95 | struct acpi_bridge { | ||
96 | struct acpi_bridge *parent; | ||
97 | struct acpi_bridge *next; | ||
98 | struct acpi_bridge *child; | ||
99 | acpi_handle handle; | ||
100 | int seg; | ||
101 | int pbus; /* pdev->bus->number */ | ||
102 | int pdevice; /* PCI_SLOT(pdev->devfn) */ | ||
103 | int pfunction; /* PCI_DEVFN(pdev->devfn) */ | ||
104 | int bus; /* pdev->subordinate->number */ | ||
105 | struct acpi__hpp *_hpp; | ||
106 | struct acpi_php_slot *slots; | ||
107 | struct pci_resource *tmem_head; /* total from crs */ | ||
108 | struct pci_resource *tp_mem_head; /* total from crs */ | ||
109 | struct pci_resource *tio_head; /* total from crs */ | ||
110 | struct pci_resource *tbus_head; /* total from crs */ | ||
111 | struct pci_resource *mem_head; /* available */ | ||
112 | struct pci_resource *p_mem_head; /* available */ | ||
113 | struct pci_resource *io_head; /* available */ | ||
114 | struct pci_resource *bus_head; /* available */ | ||
115 | int scanned; | ||
116 | int type; | ||
117 | }; | ||
118 | |||
119 | static struct acpi_bridge *acpi_bridges_head; | ||
120 | |||
121 | static u8 * acpi_path_name( acpi_handle handle) | ||
122 | { | ||
123 | acpi_status status; | ||
124 | static u8 path_name[ACPI_PATHNAME_MAX]; | ||
125 | struct acpi_buffer ret_buf = { ACPI_PATHNAME_MAX, path_name }; | ||
126 | |||
127 | memset(path_name, 0, sizeof (path_name)); | ||
128 | status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &ret_buf); | ||
129 | |||
130 | if (ACPI_FAILURE(status)) | ||
131 | return NULL; | ||
132 | else | ||
133 | return path_name; | ||
134 | } | ||
135 | |||
136 | static void acpi_get__hpp ( struct acpi_bridge *ab); | ||
137 | static int acpi_run_oshp ( struct acpi_bridge *ab); | ||
138 | static int osc_run_status = NC_NOT_RUN; | ||
139 | static int oshp_run_status = NC_NOT_RUN; | ||
140 | |||
141 | static int acpi_add_slot_to_php_slots( | ||
142 | struct acpi_bridge *ab, | ||
143 | int bus_num, | ||
144 | acpi_handle handle, | ||
145 | u32 adr, | ||
146 | u32 sun | ||
147 | ) | ||
148 | { | ||
149 | struct acpi_php_slot *aps; | ||
150 | static long samesun = -1; | ||
151 | |||
152 | aps = (struct acpi_php_slot *) kmalloc (sizeof(struct acpi_php_slot), GFP_KERNEL); | ||
153 | if (!aps) { | ||
154 | err ("acpi_pciehprm: alloc for aps fail\n"); | ||
155 | return -1; | ||
156 | } | ||
157 | memset(aps, 0, sizeof(struct acpi_php_slot)); | ||
158 | |||
159 | aps->handle = handle; | ||
160 | aps->bus = bus_num; | ||
161 | aps->dev = (adr >> 16) & 0xffff; | ||
162 | aps->fun = adr & 0xffff; | ||
163 | aps->sun = sun; | ||
164 | |||
165 | aps->next = ab->slots; /* cling to the bridge */ | ||
166 | aps->bridge = ab; | ||
167 | ab->slots = aps; | ||
168 | |||
169 | ab->scanned += 1; | ||
170 | if (!ab->_hpp) | ||
171 | acpi_get__hpp(ab); | ||
172 | |||
173 | if (osc_run_status == OSC_NOT_EXIST) | ||
174 | oshp_run_status = acpi_run_oshp(ab); | ||
175 | |||
176 | if (sun != samesun) { | ||
177 | info("acpi_pciehprm: Slot sun(%x) at s:b:d:f=0x%02x:%02x:%02x:%02x\n", | ||
178 | aps->sun, ab->seg, aps->bus, aps->dev, aps->fun); | ||
179 | samesun = sun; | ||
180 | } | ||
181 | return 0; | ||
182 | } | ||
183 | |||
184 | static void acpi_get__hpp ( struct acpi_bridge *ab) | ||
185 | { | ||
186 | acpi_status status; | ||
187 | u8 nui[4]; | ||
188 | struct acpi_buffer ret_buf = { 0, NULL}; | ||
189 | union acpi_object *ext_obj, *package; | ||
190 | u8 *path_name = acpi_path_name(ab->handle); | ||
191 | int i, len = 0; | ||
192 | |||
193 | /* get _hpp */ | ||
194 | status = acpi_evaluate_object(ab->handle, METHOD_NAME__HPP, NULL, &ret_buf); | ||
195 | switch (status) { | ||
196 | case AE_BUFFER_OVERFLOW: | ||
197 | ret_buf.pointer = kmalloc (ret_buf.length, GFP_KERNEL); | ||
198 | if (!ret_buf.pointer) { | ||
199 | err ("acpi_pciehprm:%s alloc for _HPP fail\n", path_name); | ||
200 | return; | ||
201 | } | ||
202 | status = acpi_evaluate_object(ab->handle, METHOD_NAME__HPP, NULL, &ret_buf); | ||
203 | if (ACPI_SUCCESS(status)) | ||
204 | break; | ||
205 | default: | ||
206 | if (ACPI_FAILURE(status)) { | ||
207 | err("acpi_pciehprm:%s _HPP fail=0x%x\n", path_name, status); | ||
208 | return; | ||
209 | } | ||
210 | } | ||
211 | |||
212 | ext_obj = (union acpi_object *) ret_buf.pointer; | ||
213 | if (ext_obj->type != ACPI_TYPE_PACKAGE) { | ||
214 | err ("acpi_pciehprm:%s _HPP obj not a package\n", path_name); | ||
215 | goto free_and_return; | ||
216 | } | ||
217 | |||
218 | len = ext_obj->package.count; | ||
219 | package = (union acpi_object *) ret_buf.pointer; | ||
220 | for ( i = 0; (i < len) || (i < 4); i++) { | ||
221 | ext_obj = (union acpi_object *) &package->package.elements[i]; | ||
222 | switch (ext_obj->type) { | ||
223 | case ACPI_TYPE_INTEGER: | ||
224 | nui[i] = (u8)ext_obj->integer.value; | ||
225 | break; | ||
226 | default: | ||
227 | err ("acpi_pciehprm:%s _HPP obj type incorrect\n", path_name); | ||
228 | goto free_and_return; | ||
229 | } | ||
230 | } | ||
231 | |||
232 | ab->_hpp = kmalloc (sizeof (struct acpi__hpp), GFP_KERNEL); | ||
233 | if (!ab->_hpp) { | ||
234 | err ("acpi_pciehprm:%s alloc for _HPP failed\n", path_name); | ||
235 | goto free_and_return; | ||
236 | } | ||
237 | memset(ab->_hpp, 0, sizeof(struct acpi__hpp)); | ||
238 | |||
239 | ab->_hpp->cache_line_size = nui[0]; | ||
240 | ab->_hpp->latency_timer = nui[1]; | ||
241 | ab->_hpp->enable_serr = nui[2]; | ||
242 | ab->_hpp->enable_perr = nui[3]; | ||
243 | |||
244 | dbg(" _HPP: cache_line_size=0x%x\n", ab->_hpp->cache_line_size); | ||
245 | dbg(" _HPP: latency timer =0x%x\n", ab->_hpp->latency_timer); | ||
246 | dbg(" _HPP: enable SERR =0x%x\n", ab->_hpp->enable_serr); | ||
247 | dbg(" _HPP: enable PERR =0x%x\n", ab->_hpp->enable_perr); | ||
248 | |||
249 | free_and_return: | ||
250 | kfree(ret_buf.pointer); | ||
251 | } | ||
252 | |||
253 | static int acpi_run_oshp ( struct acpi_bridge *ab) | ||
254 | { | ||
255 | acpi_status status; | ||
256 | u8 *path_name = acpi_path_name(ab->handle); | ||
257 | |||
258 | /* run OSHP */ | ||
259 | status = acpi_evaluate_object(ab->handle, METHOD_NAME_OSHP, NULL, NULL); | ||
260 | if (ACPI_FAILURE(status)) { | ||
261 | err("acpi_pciehprm:%s OSHP fails=0x%x\n", path_name, status); | ||
262 | oshp_run_status = (status == AE_NOT_FOUND) ? OSHP_NOT_EXIST : OSHP_RUN_FAILED; | ||
263 | } else { | ||
264 | oshp_run_status = NC_RUN_SUCCESS; | ||
265 | dbg("acpi_pciehprm:%s OSHP passes =0x%x\n", path_name, status); | ||
266 | dbg("acpi_pciehprm:%s oshp_run_status =0x%x\n", path_name, oshp_run_status); | ||
267 | } | ||
268 | return oshp_run_status; | ||
269 | } | ||
270 | |||
271 | static acpi_status acpi_evaluate_crs( | ||
272 | acpi_handle handle, | ||
273 | struct acpi_resource **retbuf | ||
274 | ) | ||
275 | { | ||
276 | acpi_status status; | ||
277 | struct acpi_buffer crsbuf; | ||
278 | u8 *path_name = acpi_path_name(handle); | ||
279 | |||
280 | crsbuf.length = 0; | ||
281 | crsbuf.pointer = NULL; | ||
282 | |||
283 | status = acpi_get_current_resources (handle, &crsbuf); | ||
284 | |||
285 | switch (status) { | ||
286 | case AE_BUFFER_OVERFLOW: | ||
287 | break; /* found */ | ||
288 | case AE_NOT_FOUND: | ||
289 | dbg("acpi_pciehprm:%s _CRS not found\n", path_name); | ||
290 | return status; | ||
291 | default: | ||
292 | err ("acpi_pciehprm:%s _CRS fail=0x%x\n", path_name, status); | ||
293 | return status; | ||
294 | } | ||
295 | |||
296 | crsbuf.pointer = kmalloc (crsbuf.length, GFP_KERNEL); | ||
297 | if (!crsbuf.pointer) { | ||
298 | err ("acpi_pciehprm: alloc %ld bytes for %s _CRS fail\n", (ulong)crsbuf.length, path_name); | ||
299 | return AE_NO_MEMORY; | ||
300 | } | ||
301 | |||
302 | status = acpi_get_current_resources (handle, &crsbuf); | ||
303 | if (ACPI_FAILURE(status)) { | ||
304 | err("acpi_pciehprm: %s _CRS fail=0x%x.\n", path_name, status); | ||
305 | kfree(crsbuf.pointer); | ||
306 | return status; | ||
307 | } | ||
308 | |||
309 | *retbuf = crsbuf.pointer; | ||
310 | |||
311 | return status; | ||
312 | } | ||
313 | |||
314 | static void free_pci_resource ( struct pci_resource *aprh) | ||
315 | { | ||
316 | struct pci_resource *res, *next; | ||
317 | |||
318 | for (res = aprh; res; res = next) { | ||
319 | next = res->next; | ||
320 | kfree(res); | ||
321 | } | ||
322 | } | ||
323 | |||
324 | static void print_pci_resource ( struct pci_resource *aprh) | ||
325 | { | ||
326 | struct pci_resource *res; | ||
327 | |||
328 | for (res = aprh; res; res = res->next) | ||
329 | dbg(" base= 0x%x length= 0x%x\n", res->base, res->length); | ||
330 | } | ||
331 | |||
332 | static void print_slot_resources( struct acpi_php_slot *aps) | ||
333 | { | ||
334 | if (aps->bus_head) { | ||
335 | dbg(" BUS Resources:\n"); | ||
336 | print_pci_resource (aps->bus_head); | ||
337 | } | ||
338 | |||
339 | if (aps->io_head) { | ||
340 | dbg(" IO Resources:\n"); | ||
341 | print_pci_resource (aps->io_head); | ||
342 | } | ||
343 | |||
344 | if (aps->mem_head) { | ||
345 | dbg(" MEM Resources:\n"); | ||
346 | print_pci_resource (aps->mem_head); | ||
347 | } | ||
348 | |||
349 | if (aps->p_mem_head) { | ||
350 | dbg(" PMEM Resources:\n"); | ||
351 | print_pci_resource (aps->p_mem_head); | ||
352 | } | ||
353 | } | ||
354 | |||
355 | static void print_pci_resources( struct acpi_bridge *ab) | ||
356 | { | ||
357 | if (ab->tbus_head) { | ||
358 | dbg(" Total BUS Resources:\n"); | ||
359 | print_pci_resource (ab->tbus_head); | ||
360 | } | ||
361 | if (ab->bus_head) { | ||
362 | dbg(" BUS Resources:\n"); | ||
363 | print_pci_resource (ab->bus_head); | ||
364 | } | ||
365 | |||
366 | if (ab->tio_head) { | ||
367 | dbg(" Total IO Resources:\n"); | ||
368 | print_pci_resource (ab->tio_head); | ||
369 | } | ||
370 | if (ab->io_head) { | ||
371 | dbg(" IO Resources:\n"); | ||
372 | print_pci_resource (ab->io_head); | ||
373 | } | ||
374 | |||
375 | if (ab->tmem_head) { | ||
376 | dbg(" Total MEM Resources:\n"); | ||
377 | print_pci_resource (ab->tmem_head); | ||
378 | } | ||
379 | if (ab->mem_head) { | ||
380 | dbg(" MEM Resources:\n"); | ||
381 | print_pci_resource (ab->mem_head); | ||
382 | } | ||
383 | |||
384 | if (ab->tp_mem_head) { | ||
385 | dbg(" Total PMEM Resources:\n"); | ||
386 | print_pci_resource (ab->tp_mem_head); | ||
387 | } | ||
388 | if (ab->p_mem_head) { | ||
389 | dbg(" PMEM Resources:\n"); | ||
390 | print_pci_resource (ab->p_mem_head); | ||
391 | } | ||
392 | if (ab->_hpp) { | ||
393 | dbg(" _HPP: cache_line_size=0x%x\n", ab->_hpp->cache_line_size); | ||
394 | dbg(" _HPP: latency timer =0x%x\n", ab->_hpp->latency_timer); | ||
395 | dbg(" _HPP: enable SERR =0x%x\n", ab->_hpp->enable_serr); | ||
396 | dbg(" _HPP: enable PERR =0x%x\n", ab->_hpp->enable_perr); | ||
397 | } | ||
398 | } | ||
399 | |||
400 | static int pciehprm_delete_resource( | ||
401 | struct pci_resource **aprh, | ||
402 | ulong base, | ||
403 | ulong size) | ||
404 | { | ||
405 | struct pci_resource *res; | ||
406 | struct pci_resource *prevnode; | ||
407 | struct pci_resource *split_node; | ||
408 | ulong tbase; | ||
409 | |||
410 | pciehp_resource_sort_and_combine(aprh); | ||
411 | |||
412 | for (res = *aprh; res; res = res->next) { | ||
413 | if (res->base > base) | ||
414 | continue; | ||
415 | |||
416 | if ((res->base + res->length) < (base + size)) | ||
417 | continue; | ||
418 | |||
419 | if (res->base < base) { | ||
420 | tbase = base; | ||
421 | |||
422 | if ((res->length - (tbase - res->base)) < size) | ||
423 | continue; | ||
424 | |||
425 | split_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); | ||
426 | if (!split_node) | ||
427 | return -ENOMEM; | ||
428 | |||
429 | split_node->base = res->base; | ||
430 | split_node->length = tbase - res->base; | ||
431 | res->base = tbase; | ||
432 | res->length -= split_node->length; | ||
433 | |||
434 | split_node->next = res->next; | ||
435 | res->next = split_node; | ||
436 | } | ||
437 | |||
438 | if (res->length >= size) { | ||
439 | split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); | ||
440 | if (!split_node) | ||
441 | return -ENOMEM; | ||
442 | |||
443 | split_node->base = res->base + size; | ||
444 | split_node->length = res->length - size; | ||
445 | res->length = size; | ||
446 | |||
447 | split_node->next = res->next; | ||
448 | res->next = split_node; | ||
449 | } | ||
450 | |||
451 | if (*aprh == res) { | ||
452 | *aprh = res->next; | ||
453 | } else { | ||
454 | prevnode = *aprh; | ||
455 | while (prevnode->next != res) | ||
456 | prevnode = prevnode->next; | ||
457 | |||
458 | prevnode->next = res->next; | ||
459 | } | ||
460 | res->next = NULL; | ||
461 | kfree(res); | ||
462 | break; | ||
463 | } | ||
464 | |||
465 | return 0; | ||
466 | } | ||
467 | |||
468 | static int pciehprm_delete_resources( | ||
469 | struct pci_resource **aprh, | ||
470 | struct pci_resource *this | ||
471 | ) | ||
472 | { | ||
473 | struct pci_resource *res; | ||
474 | |||
475 | for (res = this; res; res = res->next) | ||
476 | pciehprm_delete_resource(aprh, res->base, res->length); | ||
477 | |||
478 | return 0; | ||
479 | } | ||
480 | |||
481 | static int pciehprm_add_resource( | ||
482 | struct pci_resource **aprh, | ||
483 | ulong base, | ||
484 | ulong size) | ||
485 | { | ||
486 | struct pci_resource *res; | ||
487 | |||
488 | for (res = *aprh; res; res = res->next) { | ||
489 | if ((res->base + res->length) == base) { | ||
490 | res->length += size; | ||
491 | size = 0L; | ||
492 | break; | ||
493 | } | ||
494 | if (res->next == *aprh) | ||
495 | break; | ||
496 | } | ||
497 | |||
498 | if (size) { | ||
499 | res = kmalloc(sizeof(struct pci_resource), GFP_KERNEL); | ||
500 | if (!res) { | ||
501 | err ("acpi_pciehprm: alloc for res fail\n"); | ||
502 | return -ENOMEM; | ||
503 | } | ||
504 | memset(res, 0, sizeof (struct pci_resource)); | ||
505 | |||
506 | res->base = base; | ||
507 | res->length = size; | ||
508 | res->next = *aprh; | ||
509 | *aprh = res; | ||
510 | } | ||
511 | |||
512 | return 0; | ||
513 | } | ||
514 | |||
515 | static int pciehprm_add_resources( | ||
516 | struct pci_resource **aprh, | ||
517 | struct pci_resource *this | ||
518 | ) | ||
519 | { | ||
520 | struct pci_resource *res; | ||
521 | int rc = 0; | ||
522 | |||
523 | for (res = this; res && !rc; res = res->next) | ||
524 | rc = pciehprm_add_resource(aprh, res->base, res->length); | ||
525 | |||
526 | return rc; | ||
527 | } | ||
528 | |||
529 | static void acpi_parse_io ( | ||
530 | struct acpi_bridge *ab, | ||
531 | union acpi_resource_data *data | ||
532 | ) | ||
533 | { | ||
534 | struct acpi_resource_io *dataio; | ||
535 | dataio = (struct acpi_resource_io *) data; | ||
536 | |||
537 | dbg("Io Resource\n"); | ||
538 | dbg(" %d bit decode\n", ACPI_DECODE_16 == dataio->io_decode ? 16:10); | ||
539 | dbg(" Range minimum base: %08X\n", dataio->min_base_address); | ||
540 | dbg(" Range maximum base: %08X\n", dataio->max_base_address); | ||
541 | dbg(" Alignment: %08X\n", dataio->alignment); | ||
542 | dbg(" Range Length: %08X\n", dataio->range_length); | ||
543 | } | ||
544 | |||
545 | static void acpi_parse_fixed_io ( | ||
546 | struct acpi_bridge *ab, | ||
547 | union acpi_resource_data *data | ||
548 | ) | ||
549 | { | ||
550 | struct acpi_resource_fixed_io *datafio; | ||
551 | datafio = (struct acpi_resource_fixed_io *) data; | ||
552 | |||
553 | dbg("Fixed Io Resource\n"); | ||
554 | dbg(" Range base address: %08X", datafio->base_address); | ||
555 | dbg(" Range length: %08X", datafio->range_length); | ||
556 | } | ||
557 | |||
558 | static void acpi_parse_address16_32 ( | ||
559 | struct acpi_bridge *ab, | ||
560 | union acpi_resource_data *data, | ||
561 | acpi_resource_type id | ||
562 | ) | ||
563 | { | ||
564 | /* | ||
565 | * acpi_resource_address16 == acpi_resource_address32 | ||
566 | * acpi_resource_address16 *data16 = (acpi_resource_address16 *) data; | ||
567 | */ | ||
568 | struct acpi_resource_address32 *data32 = (struct acpi_resource_address32 *) data; | ||
569 | struct pci_resource **aprh, **tprh; | ||
570 | |||
571 | if (id == ACPI_RSTYPE_ADDRESS16) | ||
572 | dbg("acpi_pciehprm:16-Bit Address Space Resource\n"); | ||
573 | else | ||
574 | dbg("acpi_pciehprm:32-Bit Address Space Resource\n"); | ||
575 | |||
576 | switch (data32->resource_type) { | ||
577 | case ACPI_MEMORY_RANGE: | ||
578 | dbg(" Resource Type: Memory Range\n"); | ||
579 | aprh = &ab->mem_head; | ||
580 | tprh = &ab->tmem_head; | ||
581 | |||
582 | switch (data32->attribute.memory.cache_attribute) { | ||
583 | case ACPI_NON_CACHEABLE_MEMORY: | ||
584 | dbg(" Type Specific: Noncacheable memory\n"); | ||
585 | break; | ||
586 | case ACPI_CACHABLE_MEMORY: | ||
587 | dbg(" Type Specific: Cacheable memory\n"); | ||
588 | break; | ||
589 | case ACPI_WRITE_COMBINING_MEMORY: | ||
590 | dbg(" Type Specific: Write-combining memory\n"); | ||
591 | break; | ||
592 | case ACPI_PREFETCHABLE_MEMORY: | ||
593 | aprh = &ab->p_mem_head; | ||
594 | dbg(" Type Specific: Prefetchable memory\n"); | ||
595 | break; | ||
596 | default: | ||
597 | dbg(" Type Specific: Invalid cache attribute\n"); | ||
598 | break; | ||
599 | } | ||
600 | |||
601 | dbg(" Type Specific: Read%s\n", ACPI_READ_WRITE_MEMORY == data32->attribute.memory.read_write_attribute ? "/Write":" Only"); | ||
602 | break; | ||
603 | |||
604 | case ACPI_IO_RANGE: | ||
605 | dbg(" Resource Type: I/O Range\n"); | ||
606 | aprh = &ab->io_head; | ||
607 | tprh = &ab->tio_head; | ||
608 | |||
609 | switch (data32->attribute.io.range_attribute) { | ||
610 | case ACPI_NON_ISA_ONLY_RANGES: | ||
611 | dbg(" Type Specific: Non-ISA Io Addresses\n"); | ||
612 | break; | ||
613 | case ACPI_ISA_ONLY_RANGES: | ||
614 | dbg(" Type Specific: ISA Io Addresses\n"); | ||
615 | break; | ||
616 | case ACPI_ENTIRE_RANGE: | ||
617 | dbg(" Type Specific: ISA and non-ISA Io Addresses\n"); | ||
618 | break; | ||
619 | default: | ||
620 | dbg(" Type Specific: Invalid range attribute\n"); | ||
621 | break; | ||
622 | } | ||
623 | break; | ||
624 | |||
625 | case ACPI_BUS_NUMBER_RANGE: | ||
626 | dbg(" Resource Type: Bus Number Range(fixed)\n"); | ||
627 | /* fixup to be compatible with the rest of php driver */ | ||
628 | data32->min_address_range++; | ||
629 | data32->address_length--; | ||
630 | aprh = &ab->bus_head; | ||
631 | tprh = &ab->tbus_head; | ||
632 | break; | ||
633 | default: | ||
634 | dbg(" Resource Type: Invalid resource type. Exiting.\n"); | ||
635 | return; | ||
636 | } | ||
637 | |||
638 | dbg(" Resource %s\n", ACPI_CONSUMER == data32->producer_consumer ? "Consumer":"Producer"); | ||
639 | dbg(" %s decode\n", ACPI_SUB_DECODE == data32->decode ? "Subtractive":"Positive"); | ||
640 | dbg(" Min address is %s fixed\n", ACPI_ADDRESS_FIXED == data32->min_address_fixed ? "":"not"); | ||
641 | dbg(" Max address is %s fixed\n", ACPI_ADDRESS_FIXED == data32->max_address_fixed ? "":"not"); | ||
642 | dbg(" Granularity: %08X\n", data32->granularity); | ||
643 | dbg(" Address range min: %08X\n", data32->min_address_range); | ||
644 | dbg(" Address range max: %08X\n", data32->max_address_range); | ||
645 | dbg(" Address translation offset: %08X\n", data32->address_translation_offset); | ||
646 | dbg(" Address Length: %08X\n", data32->address_length); | ||
647 | |||
648 | if (0xFF != data32->resource_source.index) { | ||
649 | dbg(" Resource Source Index: %X\n", data32->resource_source.index); | ||
650 | /* dbg(" Resource Source: %s\n", data32->resource_source.string_ptr); */ | ||
651 | } | ||
652 | |||
653 | pciehprm_add_resource(aprh, data32->min_address_range, data32->address_length); | ||
654 | } | ||
655 | |||
656 | static acpi_status acpi_parse_crs( | ||
657 | struct acpi_bridge *ab, | ||
658 | struct acpi_resource *crsbuf | ||
659 | ) | ||
660 | { | ||
661 | acpi_status status = AE_OK; | ||
662 | struct acpi_resource *resource = crsbuf; | ||
663 | u8 count = 0; | ||
664 | u8 done = 0; | ||
665 | |||
666 | while (!done) { | ||
667 | dbg("acpi_pciehprm: PCI bus 0x%x Resource structure %x.\n", ab->bus, count++); | ||
668 | switch (resource->id) { | ||
669 | case ACPI_RSTYPE_IRQ: | ||
670 | dbg("Irq -------- Resource\n"); | ||
671 | break; | ||
672 | case ACPI_RSTYPE_DMA: | ||
673 | dbg("DMA -------- Resource\n"); | ||
674 | break; | ||
675 | case ACPI_RSTYPE_START_DPF: | ||
676 | dbg("Start DPF -------- Resource\n"); | ||
677 | break; | ||
678 | case ACPI_RSTYPE_END_DPF: | ||
679 | dbg("End DPF -------- Resource\n"); | ||
680 | break; | ||
681 | case ACPI_RSTYPE_IO: | ||
682 | acpi_parse_io (ab, &resource->data); | ||
683 | break; | ||
684 | case ACPI_RSTYPE_FIXED_IO: | ||
685 | acpi_parse_fixed_io (ab, &resource->data); | ||
686 | break; | ||
687 | case ACPI_RSTYPE_VENDOR: | ||
688 | dbg("Vendor -------- Resource\n"); | ||
689 | break; | ||
690 | case ACPI_RSTYPE_END_TAG: | ||
691 | dbg("End_tag -------- Resource\n"); | ||
692 | done = 1; | ||
693 | break; | ||
694 | case ACPI_RSTYPE_MEM24: | ||
695 | dbg("Mem24 -------- Resource\n"); | ||
696 | break; | ||
697 | case ACPI_RSTYPE_MEM32: | ||
698 | dbg("Mem32 -------- Resource\n"); | ||
699 | break; | ||
700 | case ACPI_RSTYPE_FIXED_MEM32: | ||
701 | dbg("Fixed Mem32 -------- Resource\n"); | ||
702 | break; | ||
703 | case ACPI_RSTYPE_ADDRESS16: | ||
704 | acpi_parse_address16_32(ab, &resource->data, ACPI_RSTYPE_ADDRESS16); | ||
705 | break; | ||
706 | case ACPI_RSTYPE_ADDRESS32: | ||
707 | acpi_parse_address16_32(ab, &resource->data, ACPI_RSTYPE_ADDRESS32); | ||
708 | break; | ||
709 | case ACPI_RSTYPE_ADDRESS64: | ||
710 | info("Address64 -------- Resource unparsed\n"); | ||
711 | break; | ||
712 | case ACPI_RSTYPE_EXT_IRQ: | ||
713 | dbg("Ext Irq -------- Resource\n"); | ||
714 | break; | ||
715 | default: | ||
716 | dbg("Invalid -------- resource type 0x%x\n", resource->id); | ||
717 | break; | ||
718 | } | ||
719 | |||
720 | resource = (struct acpi_resource *) ((char *)resource + resource->length); | ||
721 | } | ||
722 | |||
723 | return status; | ||
724 | } | ||
725 | |||
726 | static acpi_status acpi_get_crs( struct acpi_bridge *ab) | ||
727 | { | ||
728 | acpi_status status; | ||
729 | struct acpi_resource *crsbuf; | ||
730 | |||
731 | status = acpi_evaluate_crs(ab->handle, &crsbuf); | ||
732 | if (ACPI_SUCCESS(status)) { | ||
733 | status = acpi_parse_crs(ab, crsbuf); | ||
734 | kfree(crsbuf); | ||
735 | |||
736 | pciehp_resource_sort_and_combine(&ab->bus_head); | ||
737 | pciehp_resource_sort_and_combine(&ab->io_head); | ||
738 | pciehp_resource_sort_and_combine(&ab->mem_head); | ||
739 | pciehp_resource_sort_and_combine(&ab->p_mem_head); | ||
740 | |||
741 | pciehprm_add_resources (&ab->tbus_head, ab->bus_head); | ||
742 | pciehprm_add_resources (&ab->tio_head, ab->io_head); | ||
743 | pciehprm_add_resources (&ab->tmem_head, ab->mem_head); | ||
744 | pciehprm_add_resources (&ab->tp_mem_head, ab->p_mem_head); | ||
745 | } | ||
746 | |||
747 | return status; | ||
748 | } | ||
749 | |||
750 | /* find acpi_bridge downword from ab. */ | ||
751 | static struct acpi_bridge * | ||
752 | find_acpi_bridge_by_bus( | ||
753 | struct acpi_bridge *ab, | ||
754 | int seg, | ||
755 | int bus /* pdev->subordinate->number */ | ||
756 | ) | ||
757 | { | ||
758 | struct acpi_bridge *lab = NULL; | ||
759 | |||
760 | if (!ab) | ||
761 | return NULL; | ||
762 | |||
763 | if ((ab->bus == bus) && (ab->seg == seg)) | ||
764 | return ab; | ||
765 | |||
766 | if (ab->child) | ||
767 | lab = find_acpi_bridge_by_bus(ab->child, seg, bus); | ||
768 | |||
769 | if (!lab) | ||
770 | if (ab->next) | ||
771 | lab = find_acpi_bridge_by_bus(ab->next, seg, bus); | ||
772 | |||
773 | return lab; | ||
774 | } | ||
775 | |||
776 | /* | ||
777 | * Build a device tree of ACPI PCI Bridges | ||
778 | */ | ||
779 | static void pciehprm_acpi_register_a_bridge ( | ||
780 | struct acpi_bridge **head, | ||
781 | struct acpi_bridge *pab, /* parent bridge to which child bridge is added */ | ||
782 | struct acpi_bridge *cab /* child bridge to add */ | ||
783 | ) | ||
784 | { | ||
785 | struct acpi_bridge *lpab; | ||
786 | struct acpi_bridge *lcab; | ||
787 | |||
788 | lpab = find_acpi_bridge_by_bus(*head, pab->seg, pab->bus); | ||
789 | if (!lpab) { | ||
790 | if (!(pab->type & BRIDGE_TYPE_HOST)) | ||
791 | warn("PCI parent bridge s:b(%x:%x) not in list.\n", pab->seg, pab->bus); | ||
792 | pab->next = *head; | ||
793 | *head = pab; | ||
794 | lpab = pab; | ||
795 | } | ||
796 | |||
797 | if ((cab->type & BRIDGE_TYPE_HOST) && (pab == cab)) | ||
798 | return; | ||
799 | |||
800 | lcab = find_acpi_bridge_by_bus(*head, cab->seg, cab->bus); | ||
801 | if (lcab) { | ||
802 | if ((pab->bus != lcab->parent->bus) || (lcab->bus != cab->bus)) | ||
803 | err("PCI child bridge s:b(%x:%x) in list with diff parent.\n", cab->seg, cab->bus); | ||
804 | return; | ||
805 | } else | ||
806 | lcab = cab; | ||
807 | |||
808 | lcab->parent = lpab; | ||
809 | lcab->next = lpab->child; | ||
810 | lpab->child = lcab; | ||
811 | } | ||
812 | |||
813 | static acpi_status pciehprm_acpi_build_php_slots_callback( | ||
814 | acpi_handle handle, | ||
815 | u32 Level, | ||
816 | void *context, | ||
817 | void **retval | ||
818 | ) | ||
819 | { | ||
820 | ulong bus_num; | ||
821 | ulong seg_num; | ||
822 | ulong sun, adr; | ||
823 | ulong padr = 0; | ||
824 | acpi_handle phandle = NULL; | ||
825 | struct acpi_bridge *pab = (struct acpi_bridge *)context; | ||
826 | struct acpi_bridge *lab; | ||
827 | acpi_status status; | ||
828 | u8 *path_name = acpi_path_name(handle); | ||
829 | |||
830 | /* get _SUN */ | ||
831 | status = acpi_evaluate_integer(handle, METHOD_NAME__SUN, NULL, &sun); | ||
832 | switch(status) { | ||
833 | case AE_NOT_FOUND: | ||
834 | return AE_OK; | ||
835 | default: | ||
836 | if (ACPI_FAILURE(status)) { | ||
837 | err("acpi_pciehprm:%s _SUN fail=0x%x\n", path_name, status); | ||
838 | return status; | ||
839 | } | ||
840 | } | ||
841 | |||
842 | /* get _ADR. _ADR must exist if _SUN exists */ | ||
843 | status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr); | ||
844 | if (ACPI_FAILURE(status)) { | ||
845 | err("acpi_pciehprm:%s _ADR fail=0x%x\n", path_name, status); | ||
846 | return status; | ||
847 | } | ||
848 | |||
849 | dbg("acpi_pciehprm:%s sun=0x%08x adr=0x%08x\n", path_name, (u32)sun, (u32)adr); | ||
850 | |||
851 | status = acpi_get_parent(handle, &phandle); | ||
852 | if (ACPI_FAILURE(status)) { | ||
853 | err("acpi_pciehprm:%s get_parent fail=0x%x\n", path_name, status); | ||
854 | return (status); | ||
855 | } | ||
856 | |||
857 | bus_num = pab->bus; | ||
858 | seg_num = pab->seg; | ||
859 | |||
860 | if (pab->bus == bus_num) { | ||
861 | lab = pab; | ||
862 | } else { | ||
863 | dbg("WARN: pab is not parent\n"); | ||
864 | lab = find_acpi_bridge_by_bus(pab, seg_num, bus_num); | ||
865 | if (!lab) { | ||
866 | dbg("acpi_pciehprm: alloc new P2P bridge(%x) for sun(%08x)\n", (u32)bus_num, (u32)sun); | ||
867 | lab = (struct acpi_bridge *)kmalloc(sizeof(struct acpi_bridge), GFP_KERNEL); | ||
868 | if (!lab) { | ||
869 | err("acpi_pciehprm: alloc for ab fail\n"); | ||
870 | return AE_NO_MEMORY; | ||
871 | } | ||
872 | memset(lab, 0, sizeof(struct acpi_bridge)); | ||
873 | |||
874 | lab->handle = phandle; | ||
875 | lab->pbus = pab->bus; | ||
876 | lab->pdevice = (int)(padr >> 16) & 0xffff; | ||
877 | lab->pfunction = (int)(padr & 0xffff); | ||
878 | lab->bus = (int)bus_num; | ||
879 | lab->scanned = 0; | ||
880 | lab->type = BRIDGE_TYPE_P2P; | ||
881 | |||
882 | pciehprm_acpi_register_a_bridge (&acpi_bridges_head, pab, lab); | ||
883 | } else | ||
884 | dbg("acpi_pciehprm: found P2P bridge(%x) for sun(%08x)\n", (u32)bus_num, (u32)sun); | ||
885 | } | ||
886 | |||
887 | acpi_add_slot_to_php_slots(lab, (int)bus_num, handle, (u32)adr, (u32)sun); | ||
888 | |||
889 | return (status); | ||
890 | } | ||
891 | |||
892 | static int pciehprm_acpi_build_php_slots( | ||
893 | struct acpi_bridge *ab, | ||
894 | u32 depth | ||
895 | ) | ||
896 | { | ||
897 | acpi_status status; | ||
898 | u8 *path_name = acpi_path_name(ab->handle); | ||
899 | |||
900 | /* Walk down this pci bridge to get _SUNs if any behind P2P */ | ||
901 | status = acpi_walk_namespace ( ACPI_TYPE_DEVICE, | ||
902 | ab->handle, | ||
903 | depth, | ||
904 | pciehprm_acpi_build_php_slots_callback, | ||
905 | ab, | ||
906 | NULL ); | ||
907 | if (ACPI_FAILURE(status)) { | ||
908 | dbg("acpi_pciehprm:%s walk for _SUN on pci bridge seg:bus(%x:%x) fail=0x%x\n", path_name, ab->seg, ab->bus, status); | ||
909 | return -1; | ||
910 | } | ||
911 | |||
912 | return 0; | ||
913 | } | ||
914 | |||
915 | static void build_a_bridge( | ||
916 | struct acpi_bridge *pab, | ||
917 | struct acpi_bridge *ab | ||
918 | ) | ||
919 | { | ||
920 | u8 *path_name = acpi_path_name(ab->handle); | ||
921 | |||
922 | pciehprm_acpi_register_a_bridge (&acpi_bridges_head, pab, ab); | ||
923 | |||
924 | switch (ab->type) { | ||
925 | case BRIDGE_TYPE_HOST: | ||
926 | dbg("acpi_pciehprm: Registered PCI HOST Bridge(%02x) on s:b:d:f(%02x:%02x:%02x:%02x) [%s]\n", | ||
927 | ab->bus, ab->seg, ab->pbus, ab->pdevice, ab->pfunction, path_name); | ||
928 | break; | ||
929 | case BRIDGE_TYPE_P2P: | ||
930 | dbg("acpi_pciehprm: Registered PCI P2P Bridge(%02x-%02x) on s:b:d:f(%02x:%02x:%02x:%02x) [%s]\n", | ||
931 | ab->pbus, ab->bus, ab->seg, ab->pbus, ab->pdevice, ab->pfunction, path_name); | ||
932 | break; | ||
933 | }; | ||
934 | |||
935 | /* build any immediate PHP slots under this pci bridge */ | ||
936 | pciehprm_acpi_build_php_slots(ab, 1); | ||
937 | } | ||
938 | |||
939 | static struct acpi_bridge * add_p2p_bridge( | ||
940 | acpi_handle handle, | ||
941 | struct acpi_bridge *pab, /* parent */ | ||
942 | ulong adr | ||
943 | ) | ||
944 | { | ||
945 | struct acpi_bridge *ab; | ||
946 | struct pci_dev *pdev; | ||
947 | ulong devnum, funcnum; | ||
948 | u8 *path_name = acpi_path_name(handle); | ||
949 | |||
950 | ab = (struct acpi_bridge *) kmalloc (sizeof(struct acpi_bridge), GFP_KERNEL); | ||
951 | if (!ab) { | ||
952 | err("acpi_pciehprm: alloc for ab fail\n"); | ||
953 | return NULL; | ||
954 | } | ||
955 | memset(ab, 0, sizeof(struct acpi_bridge)); | ||
956 | |||
957 | devnum = (adr >> 16) & 0xffff; | ||
958 | funcnum = adr & 0xffff; | ||
959 | |||
960 | pdev = pci_find_slot(pab->bus, PCI_DEVFN(devnum, funcnum)); | ||
961 | if (!pdev || !pdev->subordinate) { | ||
962 | err("acpi_pciehprm:%s is not a P2P Bridge\n", path_name); | ||
963 | kfree(ab); | ||
964 | return NULL; | ||
965 | } | ||
966 | |||
967 | ab->handle = handle; | ||
968 | ab->seg = pab->seg; | ||
969 | ab->pbus = pab->bus; /* or pdev->bus->number */ | ||
970 | ab->pdevice = devnum; /* or PCI_SLOT(pdev->devfn) */ | ||
971 | ab->pfunction = funcnum; /* or PCI_FUNC(pdev->devfn) */ | ||
972 | ab->bus = pdev->subordinate->number; | ||
973 | ab->scanned = 0; | ||
974 | ab->type = BRIDGE_TYPE_P2P; | ||
975 | |||
976 | dbg("acpi_pciehprm: P2P(%x-%x) on pci=b:d:f(%x:%x:%x) acpi=b:d:f(%x:%x:%x) [%s]\n", | ||
977 | pab->bus, ab->bus, pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), | ||
978 | pab->bus, (u32)devnum, (u32)funcnum, path_name); | ||
979 | |||
980 | build_a_bridge(pab, ab); | ||
981 | |||
982 | return ab; | ||
983 | } | ||
984 | |||
985 | static acpi_status scan_p2p_bridge( | ||
986 | acpi_handle handle, | ||
987 | u32 Level, | ||
988 | void *context, | ||
989 | void **retval | ||
990 | ) | ||
991 | { | ||
992 | struct acpi_bridge *pab = (struct acpi_bridge *)context; | ||
993 | struct acpi_bridge *ab; | ||
994 | acpi_status status; | ||
995 | ulong adr = 0; | ||
996 | u8 *path_name = acpi_path_name(handle); | ||
997 | ulong devnum, funcnum; | ||
998 | struct pci_dev *pdev; | ||
999 | |||
1000 | /* get device, function */ | ||
1001 | status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr); | ||
1002 | if (ACPI_FAILURE(status)) { | ||
1003 | if (status != AE_NOT_FOUND) | ||
1004 | err("acpi_pciehprm:%s _ADR fail=0x%x\n", path_name, status); | ||
1005 | return AE_OK; | ||
1006 | } | ||
1007 | |||
1008 | devnum = (adr >> 16) & 0xffff; | ||
1009 | funcnum = adr & 0xffff; | ||
1010 | |||
1011 | pdev = pci_find_slot(pab->bus, PCI_DEVFN(devnum, funcnum)); | ||
1012 | if (!pdev) | ||
1013 | return AE_OK; | ||
1014 | if (!pdev->subordinate) | ||
1015 | return AE_OK; | ||
1016 | |||
1017 | ab = add_p2p_bridge(handle, pab, adr); | ||
1018 | if (ab) { | ||
1019 | status = acpi_walk_namespace ( ACPI_TYPE_DEVICE, | ||
1020 | handle, | ||
1021 | (u32)1, | ||
1022 | scan_p2p_bridge, | ||
1023 | ab, | ||
1024 | NULL); | ||
1025 | if (ACPI_FAILURE(status)) | ||
1026 | dbg("acpi_pciehprm:%s find_p2p fail=0x%x\n", path_name, status); | ||
1027 | } | ||
1028 | |||
1029 | return AE_OK; | ||
1030 | } | ||
1031 | |||
1032 | static struct acpi_bridge * add_host_bridge( | ||
1033 | acpi_handle handle, | ||
1034 | ulong segnum, | ||
1035 | ulong busnum | ||
1036 | ) | ||
1037 | { | ||
1038 | ulong adr = 0; | ||
1039 | acpi_status status; | ||
1040 | struct acpi_bridge *ab; | ||
1041 | u8 *path_name = acpi_path_name(handle); | ||
1042 | |||
1043 | /* get device, function: host br adr is always 0000 though. */ | ||
1044 | status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr); | ||
1045 | if (ACPI_FAILURE(status)) { | ||
1046 | err("acpi_pciehprm:%s _ADR fail=0x%x\n", path_name, status); | ||
1047 | return NULL; | ||
1048 | } | ||
1049 | dbg("acpi_pciehprm: ROOT PCI seg(0x%x)bus(0x%x)dev(0x%x)func(0x%x) [%s]\n", (u32)segnum, | ||
1050 | (u32)busnum, (u32)(adr >> 16) & 0xffff, (u32)adr & 0xffff, path_name); | ||
1051 | |||
1052 | ab = (struct acpi_bridge *) kmalloc (sizeof(struct acpi_bridge), GFP_KERNEL); | ||
1053 | if (!ab) { | ||
1054 | err("acpi_pciehprm: alloc for ab fail\n"); | ||
1055 | return NULL; | ||
1056 | } | ||
1057 | memset(ab, 0, sizeof(struct acpi_bridge)); | ||
1058 | |||
1059 | ab->handle = handle; | ||
1060 | ab->seg = (int)segnum; | ||
1061 | ab->bus = ab->pbus = (int)busnum; | ||
1062 | ab->pdevice = (int)(adr >> 16) & 0xffff; | ||
1063 | ab->pfunction = (int)(adr & 0xffff); | ||
1064 | ab->scanned = 0; | ||
1065 | ab->type = BRIDGE_TYPE_HOST; | ||
1066 | |||
1067 | /* get root pci bridge's current resources */ | ||
1068 | status = acpi_get_crs(ab); | ||
1069 | if (ACPI_FAILURE(status)) { | ||
1070 | err("acpi_pciehprm:%s evaluate _CRS fail=0x%x\n", path_name, status); | ||
1071 | kfree(ab); | ||
1072 | return NULL; | ||
1073 | } | ||
1074 | |||
1075 | status = pci_osc_control_set (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL); | ||
1076 | if (ACPI_FAILURE(status)) { | ||
1077 | err("%s: status %x\n", __FUNCTION__, status); | ||
1078 | osc_run_status = (status == AE_NOT_FOUND) ? OSC_NOT_EXIST : OSC_RUN_FAILED; | ||
1079 | } else { | ||
1080 | osc_run_status = NC_RUN_SUCCESS; | ||
1081 | } | ||
1082 | dbg("%s: osc_run_status %x\n", __FUNCTION__, osc_run_status); | ||
1083 | |||
1084 | build_a_bridge(ab, ab); | ||
1085 | |||
1086 | return ab; | ||
1087 | } | ||
1088 | |||
1089 | static acpi_status acpi_scan_from_root_pci_callback ( | ||
1090 | acpi_handle handle, | ||
1091 | u32 Level, | ||
1092 | void *context, | ||
1093 | void **retval | ||
1094 | ) | ||
1095 | { | ||
1096 | ulong segnum = 0; | ||
1097 | ulong busnum = 0; | ||
1098 | acpi_status status; | ||
1099 | struct acpi_bridge *ab; | ||
1100 | u8 *path_name = acpi_path_name(handle); | ||
1101 | |||
1102 | /* get bus number of this pci root bridge */ | ||
1103 | status = acpi_evaluate_integer(handle, METHOD_NAME__SEG, NULL, &segnum); | ||
1104 | if (ACPI_FAILURE(status)) { | ||
1105 | if (status != AE_NOT_FOUND) { | ||
1106 | err("acpi_pciehprm:%s evaluate _SEG fail=0x%x\n", path_name, status); | ||
1107 | return status; | ||
1108 | } | ||
1109 | segnum = 0; | ||
1110 | } | ||
1111 | |||
1112 | /* get bus number of this pci root bridge */ | ||
1113 | status = acpi_evaluate_integer(handle, METHOD_NAME__BBN, NULL, &busnum); | ||
1114 | if (ACPI_FAILURE(status)) { | ||
1115 | err("acpi_pciehprm:%s evaluate _BBN fail=0x%x\n", path_name, status); | ||
1116 | return (status); | ||
1117 | } | ||
1118 | |||
1119 | ab = add_host_bridge(handle, segnum, busnum); | ||
1120 | if (ab) { | ||
1121 | status = acpi_walk_namespace ( ACPI_TYPE_DEVICE, | ||
1122 | handle, | ||
1123 | 1, | ||
1124 | scan_p2p_bridge, | ||
1125 | ab, | ||
1126 | NULL); | ||
1127 | if (ACPI_FAILURE(status)) | ||
1128 | dbg("acpi_pciehprm:%s find_p2p fail=0x%x\n", path_name, status); | ||
1129 | } | ||
1130 | |||
1131 | return AE_OK; | ||
1132 | } | ||
1133 | |||
1134 | static int pciehprm_acpi_scan_pci (void) | ||
1135 | { | ||
1136 | acpi_status status; | ||
1137 | |||
1138 | /* | ||
1139 | * TBD: traverse LDM device tree with the help of | ||
1140 | * unified ACPI augmented for php device population. | ||
1141 | */ | ||
1142 | status = acpi_get_devices ( PCI_ROOT_HID_STRING, | ||
1143 | acpi_scan_from_root_pci_callback, | ||
1144 | NULL, | ||
1145 | NULL ); | ||
1146 | if (ACPI_FAILURE(status)) { | ||
1147 | err("acpi_pciehprm:get_device PCI ROOT HID fail=0x%x\n", status); | ||
1148 | return -1; | ||
1149 | } | ||
1150 | |||
1151 | return 0; | ||
1152 | } | ||
1153 | |||
1154 | int pciehprm_init(enum php_ctlr_type ctlr_type) | ||
1155 | { | ||
1156 | int rc; | ||
1157 | |||
1158 | if (ctlr_type != PCI) | ||
1159 | return -ENODEV; | ||
1160 | |||
1161 | dbg("pciehprm ACPI init <enter>\n"); | ||
1162 | acpi_bridges_head = NULL; | ||
1163 | |||
1164 | /* construct PCI bus:device tree of acpi_handles */ | ||
1165 | rc = pciehprm_acpi_scan_pci(); | ||
1166 | if (rc) | ||
1167 | return rc; | ||
1168 | |||
1169 | if ((oshp_run_status != NC_RUN_SUCCESS) && (osc_run_status != NC_RUN_SUCCESS)) { | ||
1170 | err("Fails to gain control of native hot-plug\n"); | ||
1171 | rc = -ENODEV; | ||
1172 | } | ||
1173 | |||
1174 | dbg("pciehprm ACPI init %s\n", (rc)?"fail":"success"); | ||
1175 | return rc; | ||
1176 | } | ||
1177 | |||
1178 | static void free_a_slot(struct acpi_php_slot *aps) | ||
1179 | { | ||
1180 | dbg(" free a php func of slot(0x%02x) on PCI b:d:f=0x%02x:%02x:%02x\n", aps->sun, aps->bus, aps->dev, aps->fun); | ||
1181 | |||
1182 | free_pci_resource (aps->io_head); | ||
1183 | free_pci_resource (aps->bus_head); | ||
1184 | free_pci_resource (aps->mem_head); | ||
1185 | free_pci_resource (aps->p_mem_head); | ||
1186 | |||
1187 | kfree(aps); | ||
1188 | } | ||
1189 | |||
1190 | static void free_a_bridge( struct acpi_bridge *ab) | ||
1191 | { | ||
1192 | struct acpi_php_slot *aps, *next; | ||
1193 | |||
1194 | switch (ab->type) { | ||
1195 | case BRIDGE_TYPE_HOST: | ||
1196 | dbg("Free ACPI PCI HOST Bridge(%x) [%s] on s:b:d:f(%x:%x:%x:%x)\n", | ||
1197 | ab->bus, acpi_path_name(ab->handle), ab->seg, ab->pbus, ab->pdevice, ab->pfunction); | ||
1198 | break; | ||
1199 | case BRIDGE_TYPE_P2P: | ||
1200 | dbg("Free ACPI PCI P2P Bridge(%x-%x) [%s] on s:b:d:f(%x:%x:%x:%x)\n", | ||
1201 | ab->pbus, ab->bus, acpi_path_name(ab->handle), ab->seg, ab->pbus, ab->pdevice, ab->pfunction); | ||
1202 | break; | ||
1203 | }; | ||
1204 | |||
1205 | /* free slots first */ | ||
1206 | for (aps = ab->slots; aps; aps = next) { | ||
1207 | next = aps->next; | ||
1208 | free_a_slot(aps); | ||
1209 | } | ||
1210 | |||
1211 | free_pci_resource (ab->io_head); | ||
1212 | free_pci_resource (ab->tio_head); | ||
1213 | free_pci_resource (ab->bus_head); | ||
1214 | free_pci_resource (ab->tbus_head); | ||
1215 | free_pci_resource (ab->mem_head); | ||
1216 | free_pci_resource (ab->tmem_head); | ||
1217 | free_pci_resource (ab->p_mem_head); | ||
1218 | free_pci_resource (ab->tp_mem_head); | ||
1219 | |||
1220 | kfree(ab); | ||
1221 | } | ||
1222 | |||
1223 | static void pciehprm_free_bridges ( struct acpi_bridge *ab) | ||
1224 | { | ||
1225 | if (!ab) | ||
1226 | return; | ||
1227 | |||
1228 | if (ab->child) | ||
1229 | pciehprm_free_bridges (ab->child); | ||
1230 | |||
1231 | if (ab->next) | ||
1232 | pciehprm_free_bridges (ab->next); | ||
1233 | |||
1234 | free_a_bridge(ab); | ||
1235 | } | ||
1236 | |||
1237 | void pciehprm_cleanup(void) | ||
1238 | { | ||
1239 | pciehprm_free_bridges (acpi_bridges_head); | ||
1240 | } | ||
1241 | |||
1242 | static int get_number_of_slots ( | ||
1243 | struct acpi_bridge *ab, | ||
1244 | int selfonly | ||
1245 | ) | ||
1246 | { | ||
1247 | struct acpi_php_slot *aps; | ||
1248 | int prev_slot = -1; | ||
1249 | int slot_num = 0; | ||
1250 | |||
1251 | for ( aps = ab->slots; aps; aps = aps->next) | ||
1252 | if (aps->dev != prev_slot) { | ||
1253 | prev_slot = aps->dev; | ||
1254 | slot_num++; | ||
1255 | } | ||
1256 | |||
1257 | if (ab->child) | ||
1258 | slot_num += get_number_of_slots (ab->child, 0); | ||
1259 | |||
1260 | if (selfonly) | ||
1261 | return slot_num; | ||
1262 | |||
1263 | if (ab->next) | ||
1264 | slot_num += get_number_of_slots (ab->next, 0); | ||
1265 | |||
1266 | return slot_num; | ||
1267 | } | ||
1268 | |||
1269 | static int print_acpi_resources (struct acpi_bridge *ab) | ||
1270 | { | ||
1271 | struct acpi_php_slot *aps; | ||
1272 | int i; | ||
1273 | |||
1274 | switch (ab->type) { | ||
1275 | case BRIDGE_TYPE_HOST: | ||
1276 | dbg("PCI HOST Bridge (%x) [%s]\n", ab->bus, acpi_path_name(ab->handle)); | ||
1277 | break; | ||
1278 | case BRIDGE_TYPE_P2P: | ||
1279 | dbg("PCI P2P Bridge (%x-%x) [%s]\n", ab->pbus, ab->bus, acpi_path_name(ab->handle)); | ||
1280 | break; | ||
1281 | }; | ||
1282 | |||
1283 | print_pci_resources (ab); | ||
1284 | |||
1285 | for ( i = -1, aps = ab->slots; aps; aps = aps->next) { | ||
1286 | if (aps->dev == i) | ||
1287 | continue; | ||
1288 | dbg(" Slot sun(%x) s:b:d:f(%02x:%02x:%02x:%02x)\n", aps->sun, aps->seg, aps->bus, aps->dev, aps->fun); | ||
1289 | print_slot_resources(aps); | ||
1290 | i = aps->dev; | ||
1291 | } | ||
1292 | |||
1293 | if (ab->child) | ||
1294 | print_acpi_resources (ab->child); | ||
1295 | |||
1296 | if (ab->next) | ||
1297 | print_acpi_resources (ab->next); | ||
1298 | |||
1299 | return 0; | ||
1300 | } | ||
1301 | |||
1302 | int pciehprm_print_pirt(void) | ||
1303 | { | ||
1304 | dbg("PCIEHPRM ACPI Slots\n"); | ||
1305 | if (acpi_bridges_head) | ||
1306 | print_acpi_resources (acpi_bridges_head); | ||
1307 | |||
1308 | return 0; | ||
1309 | } | ||
1310 | |||
1311 | static struct acpi_php_slot * get_acpi_slot ( | ||
1312 | struct acpi_bridge *ab, | ||
1313 | u32 sun | ||
1314 | ) | ||
1315 | { | ||
1316 | struct acpi_php_slot *aps = NULL; | ||
1317 | |||
1318 | for ( aps = ab->slots; aps; aps = aps->next) | ||
1319 | if (aps->sun == sun) | ||
1320 | return aps; | ||
1321 | |||
1322 | if (!aps && ab->child) { | ||
1323 | aps = (struct acpi_php_slot *)get_acpi_slot (ab->child, sun); | ||
1324 | if (aps) | ||
1325 | return aps; | ||
1326 | } | ||
1327 | |||
1328 | if (!aps && ab->next) { | ||
1329 | aps = (struct acpi_php_slot *)get_acpi_slot (ab->next, sun); | ||
1330 | if (aps) | ||
1331 | return aps; | ||
1332 | } | ||
1333 | |||
1334 | return aps; | ||
1335 | |||
1336 | } | ||
1337 | |||
1338 | #if 0 | ||
1339 | void * pciehprm_get_slot(struct slot *slot) | ||
1340 | { | ||
1341 | struct acpi_bridge *ab = acpi_bridges_head; | ||
1342 | struct acpi_php_slot *aps = get_acpi_slot (ab, slot->number); | ||
1343 | |||
1344 | aps->slot = slot; | ||
1345 | |||
1346 | dbg("Got acpi slot sun(%x): s:b:d:f(%x:%x:%x:%x)\n", aps->sun, aps->seg, aps->bus, aps->dev, aps->fun); | ||
1347 | |||
1348 | return (void *)aps; | ||
1349 | } | ||
1350 | #endif | ||
1351 | |||
1352 | static void pciehprm_dump_func_res( struct pci_func *fun) | ||
1353 | { | ||
1354 | struct pci_func *func = fun; | ||
1355 | |||
1356 | if (func->bus_head) { | ||
1357 | dbg(": BUS Resources:\n"); | ||
1358 | print_pci_resource (func->bus_head); | ||
1359 | } | ||
1360 | if (func->io_head) { | ||
1361 | dbg(": IO Resources:\n"); | ||
1362 | print_pci_resource (func->io_head); | ||
1363 | } | ||
1364 | if (func->mem_head) { | ||
1365 | dbg(": MEM Resources:\n"); | ||
1366 | print_pci_resource (func->mem_head); | ||
1367 | } | ||
1368 | if (func->p_mem_head) { | ||
1369 | dbg(": PMEM Resources:\n"); | ||
1370 | print_pci_resource (func->p_mem_head); | ||
1371 | } | ||
1372 | } | ||
1373 | |||
1374 | static void pciehprm_dump_ctrl_res( struct controller *ctlr) | ||
1375 | { | ||
1376 | struct controller *ctrl = ctlr; | ||
1377 | |||
1378 | if (ctrl->bus_head) { | ||
1379 | dbg(": BUS Resources:\n"); | ||
1380 | print_pci_resource (ctrl->bus_head); | ||
1381 | } | ||
1382 | if (ctrl->io_head) { | ||
1383 | dbg(": IO Resources:\n"); | ||
1384 | print_pci_resource (ctrl->io_head); | ||
1385 | } | ||
1386 | if (ctrl->mem_head) { | ||
1387 | dbg(": MEM Resources:\n"); | ||
1388 | print_pci_resource (ctrl->mem_head); | ||
1389 | } | ||
1390 | if (ctrl->p_mem_head) { | ||
1391 | dbg(": PMEM Resources:\n"); | ||
1392 | print_pci_resource (ctrl->p_mem_head); | ||
1393 | } | ||
1394 | } | ||
1395 | |||
1396 | static int pciehprm_get_used_resources ( | ||
1397 | struct controller *ctrl, | ||
1398 | struct pci_func *func | ||
1399 | ) | ||
1400 | { | ||
1401 | return pciehp_save_used_resources (ctrl, func, !DISABLE_CARD); | ||
1402 | } | ||
1403 | |||
1404 | static int configure_existing_function( | ||
1405 | struct controller *ctrl, | ||
1406 | struct pci_func *func | ||
1407 | ) | ||
1408 | { | ||
1409 | int rc; | ||
1410 | |||
1411 | /* see how much resources the func has used. */ | ||
1412 | rc = pciehprm_get_used_resources (ctrl, func); | ||
1413 | |||
1414 | if (!rc) { | ||
1415 | /* subtract the resources used by the func from ctrl resources */ | ||
1416 | rc = pciehprm_delete_resources (&ctrl->bus_head, func->bus_head); | ||
1417 | rc |= pciehprm_delete_resources (&ctrl->io_head, func->io_head); | ||
1418 | rc |= pciehprm_delete_resources (&ctrl->mem_head, func->mem_head); | ||
1419 | rc |= pciehprm_delete_resources (&ctrl->p_mem_head, func->p_mem_head); | ||
1420 | if (rc) | ||
1421 | warn("aCEF: cannot del used resources\n"); | ||
1422 | } else | ||
1423 | err("aCEF: cannot get used resources\n"); | ||
1424 | |||
1425 | return rc; | ||
1426 | } | ||
1427 | |||
1428 | static int bind_pci_resources_to_slots ( struct controller *ctrl) | ||
1429 | { | ||
1430 | struct pci_func *func, new_func; | ||
1431 | int busn = ctrl->slot_bus; | ||
1432 | int devn, funn; | ||
1433 | u32 vid; | ||
1434 | |||
1435 | for (devn = 0; devn < 32; devn++) { | ||
1436 | for (funn = 0; funn < 8; funn++) { | ||
1437 | /* | ||
1438 | if (devn == ctrl->device && funn == ctrl->function) | ||
1439 | continue; | ||
1440 | */ | ||
1441 | /* find out if this entry is for an occupied slot */ | ||
1442 | vid = 0xFFFFFFFF; | ||
1443 | pci_bus_read_config_dword(ctrl->pci_dev->subordinate, PCI_DEVFN(devn, funn), PCI_VENDOR_ID, &vid); | ||
1444 | |||
1445 | if (vid != 0xFFFFFFFF) { | ||
1446 | dbg("%s: vid = %x\n", __FUNCTION__, vid); | ||
1447 | func = pciehp_slot_find(busn, devn, funn); | ||
1448 | if (!func) { | ||
1449 | memset(&new_func, 0, sizeof(struct pci_func)); | ||
1450 | new_func.bus = busn; | ||
1451 | new_func.device = devn; | ||
1452 | new_func.function = funn; | ||
1453 | new_func.is_a_board = 1; | ||
1454 | configure_existing_function(ctrl, &new_func); | ||
1455 | pciehprm_dump_func_res(&new_func); | ||
1456 | } else { | ||
1457 | configure_existing_function(ctrl, func); | ||
1458 | pciehprm_dump_func_res(func); | ||
1459 | } | ||
1460 | dbg("aCCF:existing PCI 0x%x Func ResourceDump\n", ctrl->bus); | ||
1461 | } | ||
1462 | } | ||
1463 | } | ||
1464 | |||
1465 | return 0; | ||
1466 | } | ||
1467 | |||
1468 | static int bind_pci_resources( | ||
1469 | struct controller *ctrl, | ||
1470 | struct acpi_bridge *ab | ||
1471 | ) | ||
1472 | { | ||
1473 | int status = 0; | ||
1474 | |||
1475 | if (ab->bus_head) { | ||
1476 | dbg("bapr: BUS Resources add on PCI 0x%x\n", ab->bus); | ||
1477 | status = pciehprm_add_resources (&ctrl->bus_head, ab->bus_head); | ||
1478 | if (pciehprm_delete_resources (&ab->bus_head, ctrl->bus_head)) | ||
1479 | warn("bapr: cannot sub BUS Resource on PCI 0x%x\n", ab->bus); | ||
1480 | if (status) { | ||
1481 | err("bapr: BUS Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status); | ||
1482 | return status; | ||
1483 | } | ||
1484 | } else | ||
1485 | info("bapr: No BUS Resource on PCI 0x%x.\n", ab->bus); | ||
1486 | |||
1487 | if (ab->io_head) { | ||
1488 | dbg("bapr: IO Resources add on PCI 0x%x\n", ab->bus); | ||
1489 | status = pciehprm_add_resources (&ctrl->io_head, ab->io_head); | ||
1490 | if (pciehprm_delete_resources (&ab->io_head, ctrl->io_head)) | ||
1491 | warn("bapr: cannot sub IO Resource on PCI 0x%x\n", ab->bus); | ||
1492 | if (status) { | ||
1493 | err("bapr: IO Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status); | ||
1494 | return status; | ||
1495 | } | ||
1496 | } else | ||
1497 | info("bapr: No IO Resource on PCI 0x%x.\n", ab->bus); | ||
1498 | |||
1499 | if (ab->mem_head) { | ||
1500 | dbg("bapr: MEM Resources add on PCI 0x%x\n", ab->bus); | ||
1501 | status = pciehprm_add_resources (&ctrl->mem_head, ab->mem_head); | ||
1502 | if (pciehprm_delete_resources (&ab->mem_head, ctrl->mem_head)) | ||
1503 | warn("bapr: cannot sub MEM Resource on PCI 0x%x\n", ab->bus); | ||
1504 | if (status) { | ||
1505 | err("bapr: MEM Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status); | ||
1506 | return status; | ||
1507 | } | ||
1508 | } else | ||
1509 | info("bapr: No MEM Resource on PCI 0x%x.\n", ab->bus); | ||
1510 | |||
1511 | if (ab->p_mem_head) { | ||
1512 | dbg("bapr: PMEM Resources add on PCI 0x%x\n", ab->bus); | ||
1513 | status = pciehprm_add_resources (&ctrl->p_mem_head, ab->p_mem_head); | ||
1514 | if (pciehprm_delete_resources (&ab->p_mem_head, ctrl->p_mem_head)) | ||
1515 | warn("bapr: cannot sub PMEM Resource on PCI 0x%x\n", ab->bus); | ||
1516 | if (status) { | ||
1517 | err("bapr: PMEM Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status); | ||
1518 | return status; | ||
1519 | } | ||
1520 | } else | ||
1521 | info("bapr: No PMEM Resource on PCI 0x%x.\n", ab->bus); | ||
1522 | |||
1523 | return status; | ||
1524 | } | ||
1525 | |||
1526 | static int no_pci_resources( struct acpi_bridge *ab) | ||
1527 | { | ||
1528 | return !(ab->p_mem_head || ab->mem_head || ab->io_head || ab->bus_head); | ||
1529 | } | ||
1530 | |||
1531 | static int find_pci_bridge_resources ( | ||
1532 | struct controller *ctrl, | ||
1533 | struct acpi_bridge *ab | ||
1534 | ) | ||
1535 | { | ||
1536 | int rc = 0; | ||
1537 | struct pci_func func; | ||
1538 | |||
1539 | memset(&func, 0, sizeof(struct pci_func)); | ||
1540 | |||
1541 | func.bus = ab->pbus; | ||
1542 | func.device = ab->pdevice; | ||
1543 | func.function = ab->pfunction; | ||
1544 | func.is_a_board = 1; | ||
1545 | |||
1546 | /* Get used resources for this PCI bridge */ | ||
1547 | rc = pciehp_save_used_resources (ctrl, &func, !DISABLE_CARD); | ||
1548 | |||
1549 | ab->io_head = func.io_head; | ||
1550 | ab->mem_head = func.mem_head; | ||
1551 | ab->p_mem_head = func.p_mem_head; | ||
1552 | ab->bus_head = func.bus_head; | ||
1553 | if (ab->bus_head) | ||
1554 | pciehprm_delete_resource(&ab->bus_head, ctrl->pci_dev->subordinate->number, 1); | ||
1555 | |||
1556 | return rc; | ||
1557 | } | ||
1558 | |||
1559 | static int get_pci_resources_from_bridge( | ||
1560 | struct controller *ctrl, | ||
1561 | struct acpi_bridge *ab | ||
1562 | ) | ||
1563 | { | ||
1564 | int rc = 0; | ||
1565 | |||
1566 | dbg("grfb: Get Resources for PCI 0x%x from actual PCI bridge 0x%x.\n", ctrl->bus, ab->bus); | ||
1567 | |||
1568 | rc = find_pci_bridge_resources (ctrl, ab); | ||
1569 | |||
1570 | pciehp_resource_sort_and_combine(&ab->bus_head); | ||
1571 | pciehp_resource_sort_and_combine(&ab->io_head); | ||
1572 | pciehp_resource_sort_and_combine(&ab->mem_head); | ||
1573 | pciehp_resource_sort_and_combine(&ab->p_mem_head); | ||
1574 | |||
1575 | pciehprm_add_resources (&ab->tbus_head, ab->bus_head); | ||
1576 | pciehprm_add_resources (&ab->tio_head, ab->io_head); | ||
1577 | pciehprm_add_resources (&ab->tmem_head, ab->mem_head); | ||
1578 | pciehprm_add_resources (&ab->tp_mem_head, ab->p_mem_head); | ||
1579 | |||
1580 | return rc; | ||
1581 | } | ||
1582 | |||
1583 | static int get_pci_resources( | ||
1584 | struct controller *ctrl, | ||
1585 | struct acpi_bridge *ab | ||
1586 | ) | ||
1587 | { | ||
1588 | int rc = 0; | ||
1589 | |||
1590 | if (no_pci_resources(ab)) { | ||
1591 | dbg("spbr:PCI 0x%x has no resources. Get parent resources.\n", ab->bus); | ||
1592 | rc = get_pci_resources_from_bridge(ctrl, ab); | ||
1593 | } | ||
1594 | |||
1595 | return rc; | ||
1596 | } | ||
1597 | |||
1598 | /* | ||
1599 | * Get resources for this ctrl. | ||
1600 | * 1. get total resources from ACPI _CRS or bridge (this ctrl) | ||
1601 | * 2. find used resources of existing adapters | ||
1602 | * 3. subtract used resources from total resources | ||
1603 | */ | ||
1604 | int pciehprm_find_available_resources( struct controller *ctrl) | ||
1605 | { | ||
1606 | int rc = 0; | ||
1607 | struct acpi_bridge *ab; | ||
1608 | |||
1609 | ab = find_acpi_bridge_by_bus(acpi_bridges_head, ctrl->seg, ctrl->pci_dev->subordinate->number); | ||
1610 | if (!ab) { | ||
1611 | err("pfar:cannot locate acpi bridge of PCI 0x%x.\n", ctrl->pci_dev->subordinate->number); | ||
1612 | return -1; | ||
1613 | } | ||
1614 | if (no_pci_resources(ab)) { | ||
1615 | rc = get_pci_resources(ctrl, ab); | ||
1616 | if (rc) { | ||
1617 | err("pfar:cannot get pci resources of PCI 0x%x.\n", ctrl->pci_dev->subordinate->number); | ||
1618 | return -1; | ||
1619 | } | ||
1620 | } | ||
1621 | |||
1622 | rc = bind_pci_resources(ctrl, ab); | ||
1623 | dbg("pfar:pre-Bind PCI 0x%x Ctrl Resource Dump\n", ctrl->pci_dev->subordinate->number); | ||
1624 | pciehprm_dump_ctrl_res(ctrl); | ||
1625 | |||
1626 | bind_pci_resources_to_slots (ctrl); | ||
1627 | |||
1628 | dbg("pfar:post-Bind PCI 0x%x Ctrl Resource Dump\n", ctrl->pci_dev->subordinate->number); | ||
1629 | pciehprm_dump_ctrl_res(ctrl); | ||
1630 | |||
1631 | return rc; | ||
1632 | } | ||
1633 | |||
1634 | int pciehprm_set_hpp( | ||
1635 | struct controller *ctrl, | ||
1636 | struct pci_func *func, | ||
1637 | u8 card_type | ||
1638 | ) | ||
1639 | { | ||
1640 | struct acpi_bridge *ab; | ||
1641 | struct pci_bus lpci_bus, *pci_bus; | ||
1642 | int rc = 0; | ||
1643 | unsigned int devfn; | ||
1644 | u8 cls= 0x08; /* default cache line size */ | ||
1645 | u8 lt = 0x40; /* default latency timer */ | ||
1646 | u8 ep = 0; | ||
1647 | u8 es = 0; | ||
1648 | |||
1649 | memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus)); | ||
1650 | pci_bus = &lpci_bus; | ||
1651 | pci_bus->number = func->bus; | ||
1652 | devfn = PCI_DEVFN(func->device, func->function); | ||
1653 | |||
1654 | ab = find_acpi_bridge_by_bus(acpi_bridges_head, ctrl->seg, ctrl->bus); | ||
1655 | |||
1656 | if (ab) { | ||
1657 | if (ab->_hpp) { | ||
1658 | lt = (u8)ab->_hpp->latency_timer; | ||
1659 | cls = (u8)ab->_hpp->cache_line_size; | ||
1660 | ep = (u8)ab->_hpp->enable_perr; | ||
1661 | es = (u8)ab->_hpp->enable_serr; | ||
1662 | } else | ||
1663 | dbg("_hpp: no _hpp for B/D/F=%#x/%#x/%#x. use default value\n", func->bus, func->device, func->function); | ||
1664 | } else | ||
1665 | dbg("_hpp: no acpi bridge for B/D/F = %#x/%#x/%#x. use default value\n", func->bus, func->device, func->function); | ||
1666 | |||
1667 | |||
1668 | if (card_type == PCI_HEADER_TYPE_BRIDGE) { | ||
1669 | /* set subordinate Latency Timer */ | ||
1670 | rc |= pci_bus_write_config_byte(pci_bus, devfn, PCI_SEC_LATENCY_TIMER, lt); | ||
1671 | } | ||
1672 | |||
1673 | /* set base Latency Timer */ | ||
1674 | rc |= pci_bus_write_config_byte(pci_bus, devfn, PCI_LATENCY_TIMER, lt); | ||
1675 | dbg(" set latency timer =0x%02x: %x\n", lt, rc); | ||
1676 | |||
1677 | rc |= pci_bus_write_config_byte(pci_bus, devfn, PCI_CACHE_LINE_SIZE, cls); | ||
1678 | dbg(" set cache_line_size=0x%02x: %x\n", cls, rc); | ||
1679 | |||
1680 | return rc; | ||
1681 | } | ||
1682 | |||
1683 | void pciehprm_enable_card( | ||
1684 | struct controller *ctrl, | ||
1685 | struct pci_func *func, | ||
1686 | u8 card_type) | ||
1687 | { | ||
1688 | u16 command, cmd, bcommand, bcmd; | ||
1689 | struct pci_bus lpci_bus, *pci_bus; | ||
1690 | struct acpi_bridge *ab; | ||
1691 | unsigned int devfn; | ||
1692 | int rc; | ||
1693 | |||
1694 | memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus)); | ||
1695 | pci_bus = &lpci_bus; | ||
1696 | pci_bus->number = func->bus; | ||
1697 | devfn = PCI_DEVFN(func->device, func->function); | ||
1698 | |||
1699 | rc = pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &command); | ||
1700 | |||
1701 | if (card_type == PCI_HEADER_TYPE_BRIDGE) { | ||
1702 | rc = pci_bus_read_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, &bcommand); | ||
1703 | } | ||
1704 | |||
1705 | cmd = command = command | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE | ||
1706 | | PCI_COMMAND_IO | PCI_COMMAND_MEMORY; | ||
1707 | bcmd = bcommand = bcommand | PCI_BRIDGE_CTL_NO_ISA; | ||
1708 | |||
1709 | ab = find_acpi_bridge_by_bus(acpi_bridges_head, ctrl->seg, ctrl->bus); | ||
1710 | if (ab) { | ||
1711 | if (ab->_hpp) { | ||
1712 | if (ab->_hpp->enable_perr) { | ||
1713 | command |= PCI_COMMAND_PARITY; | ||
1714 | bcommand |= PCI_BRIDGE_CTL_PARITY; | ||
1715 | } else { | ||
1716 | command &= ~PCI_COMMAND_PARITY; | ||
1717 | bcommand &= ~PCI_BRIDGE_CTL_PARITY; | ||
1718 | } | ||
1719 | if (ab->_hpp->enable_serr) { | ||
1720 | command |= PCI_COMMAND_SERR; | ||
1721 | bcommand |= PCI_BRIDGE_CTL_SERR; | ||
1722 | } else { | ||
1723 | command &= ~PCI_COMMAND_SERR; | ||
1724 | bcommand &= ~PCI_BRIDGE_CTL_SERR; | ||
1725 | } | ||
1726 | } else | ||
1727 | dbg("no _hpp for B/D/F = %#x/%#x/%#x.\n", func->bus, func->device, func->function); | ||
1728 | } else | ||
1729 | dbg("no acpi bridge for B/D/F = %#x/%#x/%#x.\n", func->bus, func->device, func->function); | ||
1730 | |||
1731 | if (command != cmd) { | ||
1732 | rc = pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command); | ||
1733 | } | ||
1734 | if ((card_type == PCI_HEADER_TYPE_BRIDGE) && (bcommand != bcmd)) { | ||
1735 | rc = pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, bcommand); | ||
1736 | } | ||
1737 | } | ||
diff --git a/drivers/pci/hotplug/pciehprm_nonacpi.c b/drivers/pci/hotplug/pciehprm_nonacpi.c new file mode 100644 index 000000000000..79a0aa6238ef --- /dev/null +++ b/drivers/pci/hotplug/pciehprm_nonacpi.c | |||
@@ -0,0 +1,501 @@ | |||
1 | /* | ||
2 | * PCIEHPRM NONACPI: PHP Resource Manager for Non-ACPI/Legacy platform | ||
3 | * | ||
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | ||
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * Copyright (C) 2001 IBM Corp. | ||
7 | * Copyright (C) 2003-2004 Intel Corporation | ||
8 | * | ||
9 | * All rights reserved. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or (at | ||
14 | * your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
19 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
20 | * details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
25 | * | ||
26 | * Send feedback to <greg@kroah.com>, <dely.l.sy@intel.com> | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | #include <linux/config.h> | ||
31 | #include <linux/module.h> | ||
32 | #include <linux/kernel.h> | ||
33 | #include <linux/types.h> | ||
34 | #include <linux/pci.h> | ||
35 | #include <linux/init.h> | ||
36 | #include <asm/uaccess.h> | ||
37 | #ifdef CONFIG_IA64 | ||
38 | #include <asm/iosapic.h> | ||
39 | #endif | ||
40 | #include "pciehp.h" | ||
41 | #include "pciehprm.h" | ||
42 | #include "pciehprm_nonacpi.h" | ||
43 | |||
44 | |||
45 | void pciehprm_cleanup(void) | ||
46 | { | ||
47 | return; | ||
48 | } | ||
49 | |||
50 | int pciehprm_print_pirt(void) | ||
51 | { | ||
52 | return 0; | ||
53 | } | ||
54 | |||
55 | int pciehprm_get_physical_slot_number(struct controller *ctrl, u32 *sun, u8 busnum, u8 devnum) | ||
56 | { | ||
57 | |||
58 | *sun = (u8) (ctrl->first_slot); | ||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | |||
63 | static void print_pci_resource ( struct pci_resource *aprh) | ||
64 | { | ||
65 | struct pci_resource *res; | ||
66 | |||
67 | for (res = aprh; res; res = res->next) | ||
68 | dbg(" base= 0x%x length= 0x%x\n", res->base, res->length); | ||
69 | } | ||
70 | |||
71 | |||
72 | static void phprm_dump_func_res( struct pci_func *fun) | ||
73 | { | ||
74 | struct pci_func *func = fun; | ||
75 | |||
76 | if (func->bus_head) { | ||
77 | dbg(": BUS Resources:\n"); | ||
78 | print_pci_resource (func->bus_head); | ||
79 | } | ||
80 | if (func->io_head) { | ||
81 | dbg(": IO Resources:\n"); | ||
82 | print_pci_resource (func->io_head); | ||
83 | } | ||
84 | if (func->mem_head) { | ||
85 | dbg(": MEM Resources:\n"); | ||
86 | print_pci_resource (func->mem_head); | ||
87 | } | ||
88 | if (func->p_mem_head) { | ||
89 | dbg(": PMEM Resources:\n"); | ||
90 | print_pci_resource (func->p_mem_head); | ||
91 | } | ||
92 | } | ||
93 | |||
94 | static int phprm_get_used_resources ( | ||
95 | struct controller *ctrl, | ||
96 | struct pci_func *func | ||
97 | ) | ||
98 | { | ||
99 | return pciehp_save_used_resources (ctrl, func, !DISABLE_CARD); | ||
100 | } | ||
101 | |||
102 | static int phprm_delete_resource( | ||
103 | struct pci_resource **aprh, | ||
104 | ulong base, | ||
105 | ulong size) | ||
106 | { | ||
107 | struct pci_resource *res; | ||
108 | struct pci_resource *prevnode; | ||
109 | struct pci_resource *split_node; | ||
110 | ulong tbase; | ||
111 | |||
112 | pciehp_resource_sort_and_combine(aprh); | ||
113 | |||
114 | for (res = *aprh; res; res = res->next) { | ||
115 | if (res->base > base) | ||
116 | continue; | ||
117 | |||
118 | if ((res->base + res->length) < (base + size)) | ||
119 | continue; | ||
120 | |||
121 | if (res->base < base) { | ||
122 | tbase = base; | ||
123 | |||
124 | if ((res->length - (tbase - res->base)) < size) | ||
125 | continue; | ||
126 | |||
127 | split_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); | ||
128 | if (!split_node) | ||
129 | return -ENOMEM; | ||
130 | |||
131 | split_node->base = res->base; | ||
132 | split_node->length = tbase - res->base; | ||
133 | res->base = tbase; | ||
134 | res->length -= split_node->length; | ||
135 | |||
136 | split_node->next = res->next; | ||
137 | res->next = split_node; | ||
138 | } | ||
139 | |||
140 | if (res->length >= size) { | ||
141 | split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); | ||
142 | if (!split_node) | ||
143 | return -ENOMEM; | ||
144 | |||
145 | split_node->base = res->base + size; | ||
146 | split_node->length = res->length - size; | ||
147 | res->length = size; | ||
148 | |||
149 | split_node->next = res->next; | ||
150 | res->next = split_node; | ||
151 | } | ||
152 | |||
153 | if (*aprh == res) { | ||
154 | *aprh = res->next; | ||
155 | } else { | ||
156 | prevnode = *aprh; | ||
157 | while (prevnode->next != res) | ||
158 | prevnode = prevnode->next; | ||
159 | |||
160 | prevnode->next = res->next; | ||
161 | } | ||
162 | res->next = NULL; | ||
163 | kfree(res); | ||
164 | break; | ||
165 | } | ||
166 | |||
167 | return 0; | ||
168 | } | ||
169 | |||
170 | |||
171 | static int phprm_delete_resources( | ||
172 | struct pci_resource **aprh, | ||
173 | struct pci_resource *this | ||
174 | ) | ||
175 | { | ||
176 | struct pci_resource *res; | ||
177 | |||
178 | for (res = this; res; res = res->next) | ||
179 | phprm_delete_resource(aprh, res->base, res->length); | ||
180 | |||
181 | return 0; | ||
182 | } | ||
183 | |||
184 | |||
185 | static int configure_existing_function( | ||
186 | struct controller *ctrl, | ||
187 | struct pci_func *func | ||
188 | ) | ||
189 | { | ||
190 | int rc; | ||
191 | |||
192 | /* see how much resources the func has used. */ | ||
193 | rc = phprm_get_used_resources (ctrl, func); | ||
194 | |||
195 | if (!rc) { | ||
196 | /* subtract the resources used by the func from ctrl resources */ | ||
197 | rc = phprm_delete_resources (&ctrl->bus_head, func->bus_head); | ||
198 | rc |= phprm_delete_resources (&ctrl->io_head, func->io_head); | ||
199 | rc |= phprm_delete_resources (&ctrl->mem_head, func->mem_head); | ||
200 | rc |= phprm_delete_resources (&ctrl->p_mem_head, func->p_mem_head); | ||
201 | if (rc) | ||
202 | warn("aCEF: cannot del used resources\n"); | ||
203 | } else | ||
204 | err("aCEF: cannot get used resources\n"); | ||
205 | |||
206 | return rc; | ||
207 | } | ||
208 | |||
209 | static int pciehprm_delete_resource( | ||
210 | struct pci_resource **aprh, | ||
211 | ulong base, | ||
212 | ulong size) | ||
213 | { | ||
214 | struct pci_resource *res; | ||
215 | struct pci_resource *prevnode; | ||
216 | struct pci_resource *split_node; | ||
217 | ulong tbase; | ||
218 | |||
219 | pciehp_resource_sort_and_combine(aprh); | ||
220 | |||
221 | for (res = *aprh; res; res = res->next) { | ||
222 | if (res->base > base) | ||
223 | continue; | ||
224 | |||
225 | if ((res->base + res->length) < (base + size)) | ||
226 | continue; | ||
227 | |||
228 | if (res->base < base) { | ||
229 | tbase = base; | ||
230 | |||
231 | if ((res->length - (tbase - res->base)) < size) | ||
232 | continue; | ||
233 | |||
234 | split_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); | ||
235 | if (!split_node) | ||
236 | return -ENOMEM; | ||
237 | |||
238 | split_node->base = res->base; | ||
239 | split_node->length = tbase - res->base; | ||
240 | res->base = tbase; | ||
241 | res->length -= split_node->length; | ||
242 | |||
243 | split_node->next = res->next; | ||
244 | res->next = split_node; | ||
245 | } | ||
246 | |||
247 | if (res->length >= size) { | ||
248 | split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); | ||
249 | if (!split_node) | ||
250 | return -ENOMEM; | ||
251 | |||
252 | split_node->base = res->base + size; | ||
253 | split_node->length = res->length - size; | ||
254 | res->length = size; | ||
255 | |||
256 | split_node->next = res->next; | ||
257 | res->next = split_node; | ||
258 | } | ||
259 | |||
260 | if (*aprh == res) { | ||
261 | *aprh = res->next; | ||
262 | } else { | ||
263 | prevnode = *aprh; | ||
264 | while (prevnode->next != res) | ||
265 | prevnode = prevnode->next; | ||
266 | |||
267 | prevnode->next = res->next; | ||
268 | } | ||
269 | res->next = NULL; | ||
270 | kfree(res); | ||
271 | break; | ||
272 | } | ||
273 | |||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | static int bind_pci_resources_to_slots ( struct controller *ctrl) | ||
278 | { | ||
279 | struct pci_func *func, new_func; | ||
280 | int busn = ctrl->slot_bus; | ||
281 | int devn, funn; | ||
282 | u32 vid; | ||
283 | |||
284 | for (devn = 0; devn < 32; devn++) { | ||
285 | for (funn = 0; funn < 8; funn++) { | ||
286 | /* | ||
287 | if (devn == ctrl->device && funn == ctrl->function) | ||
288 | continue; | ||
289 | */ | ||
290 | /* find out if this entry is for an occupied slot */ | ||
291 | vid = 0xFFFFFFFF; | ||
292 | |||
293 | pci_bus_read_config_dword(ctrl->pci_dev->subordinate, PCI_DEVFN(devn, funn), PCI_VENDOR_ID, &vid); | ||
294 | |||
295 | if (vid != 0xFFFFFFFF) { | ||
296 | dbg("%s: vid = %x bus %x dev %x fun %x\n", __FUNCTION__, | ||
297 | vid, busn, devn, funn); | ||
298 | func = pciehp_slot_find(busn, devn, funn); | ||
299 | dbg("%s: func = %p\n", __FUNCTION__,func); | ||
300 | if (!func) { | ||
301 | memset(&new_func, 0, sizeof(struct pci_func)); | ||
302 | new_func.bus = busn; | ||
303 | new_func.device = devn; | ||
304 | new_func.function = funn; | ||
305 | new_func.is_a_board = 1; | ||
306 | configure_existing_function(ctrl, &new_func); | ||
307 | phprm_dump_func_res(&new_func); | ||
308 | } else { | ||
309 | configure_existing_function(ctrl, func); | ||
310 | phprm_dump_func_res(func); | ||
311 | } | ||
312 | dbg("aCCF:existing PCI 0x%x Func ResourceDump\n", ctrl->bus); | ||
313 | } | ||
314 | } | ||
315 | } | ||
316 | |||
317 | return 0; | ||
318 | } | ||
319 | |||
320 | static void phprm_dump_ctrl_res( struct controller *ctlr) | ||
321 | { | ||
322 | struct controller *ctrl = ctlr; | ||
323 | |||
324 | if (ctrl->bus_head) { | ||
325 | dbg(": BUS Resources:\n"); | ||
326 | print_pci_resource (ctrl->bus_head); | ||
327 | } | ||
328 | if (ctrl->io_head) { | ||
329 | dbg(": IO Resources:\n"); | ||
330 | print_pci_resource (ctrl->io_head); | ||
331 | } | ||
332 | if (ctrl->mem_head) { | ||
333 | dbg(": MEM Resources:\n"); | ||
334 | print_pci_resource (ctrl->mem_head); | ||
335 | } | ||
336 | if (ctrl->p_mem_head) { | ||
337 | dbg(": PMEM Resources:\n"); | ||
338 | print_pci_resource (ctrl->p_mem_head); | ||
339 | } | ||
340 | } | ||
341 | |||
342 | /* | ||
343 | * phprm_find_available_resources | ||
344 | * | ||
345 | * Finds available memory, IO, and IRQ resources for programming | ||
346 | * devices which may be added to the system | ||
347 | * this function is for hot plug ADD! | ||
348 | * | ||
349 | * returns 0 if success | ||
350 | */ | ||
351 | int pciehprm_find_available_resources(struct controller *ctrl) | ||
352 | { | ||
353 | struct pci_func func; | ||
354 | u32 rc; | ||
355 | |||
356 | memset(&func, 0, sizeof(struct pci_func)); | ||
357 | |||
358 | func.bus = ctrl->bus; | ||
359 | func.device = ctrl->device; | ||
360 | func.function = ctrl->function; | ||
361 | func.is_a_board = 1; | ||
362 | |||
363 | /* Get resources for this PCI bridge */ | ||
364 | rc = pciehp_save_used_resources (ctrl, &func, !DISABLE_CARD); | ||
365 | dbg("%s: pciehp_save_used_resources rc = %d\n", __FUNCTION__, rc); | ||
366 | |||
367 | if (func.mem_head) | ||
368 | func.mem_head->next = ctrl->mem_head; | ||
369 | ctrl->mem_head = func.mem_head; | ||
370 | |||
371 | if (func.p_mem_head) | ||
372 | func.p_mem_head->next = ctrl->p_mem_head; | ||
373 | ctrl->p_mem_head = func.p_mem_head; | ||
374 | |||
375 | if (func.io_head) | ||
376 | func.io_head->next = ctrl->io_head; | ||
377 | ctrl->io_head = func.io_head; | ||
378 | |||
379 | if(func.bus_head) | ||
380 | func.bus_head->next = ctrl->bus_head; | ||
381 | ctrl->bus_head = func.bus_head; | ||
382 | |||
383 | if (ctrl->bus_head) | ||
384 | pciehprm_delete_resource(&ctrl->bus_head, ctrl->pci_dev->subordinate->number, 1); | ||
385 | |||
386 | dbg("%s:pre-Bind PCI 0x%x Ctrl Resource Dump\n", __FUNCTION__, ctrl->bus); | ||
387 | phprm_dump_ctrl_res(ctrl); | ||
388 | |||
389 | dbg("%s: before bind_pci_resources_to slots\n", __FUNCTION__); | ||
390 | |||
391 | bind_pci_resources_to_slots (ctrl); | ||
392 | |||
393 | dbg("%s:post-Bind PCI 0x%x Ctrl Resource Dump\n", __FUNCTION__, ctrl->bus); | ||
394 | phprm_dump_ctrl_res(ctrl); | ||
395 | |||
396 | return (rc); | ||
397 | } | ||
398 | |||
399 | int pciehprm_set_hpp( | ||
400 | struct controller *ctrl, | ||
401 | struct pci_func *func, | ||
402 | u8 card_type) | ||
403 | { | ||
404 | u32 rc; | ||
405 | u8 temp_byte; | ||
406 | struct pci_bus lpci_bus, *pci_bus; | ||
407 | unsigned int devfn; | ||
408 | memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus)); | ||
409 | pci_bus = &lpci_bus; | ||
410 | pci_bus->number = func->bus; | ||
411 | devfn = PCI_DEVFN(func->device, func->function); | ||
412 | |||
413 | temp_byte = 0x40; /* hard coded value for LT */ | ||
414 | if (card_type == PCI_HEADER_TYPE_BRIDGE) { | ||
415 | /* set subordinate Latency Timer */ | ||
416 | rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SEC_LATENCY_TIMER, temp_byte); | ||
417 | |||
418 | if (rc) { | ||
419 | dbg("%s: set secondary LT error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, | ||
420 | func->bus, func->device, func->function); | ||
421 | return rc; | ||
422 | } | ||
423 | } | ||
424 | |||
425 | /* set base Latency Timer */ | ||
426 | rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_LATENCY_TIMER, temp_byte); | ||
427 | |||
428 | if (rc) { | ||
429 | dbg("%s: set LT error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function); | ||
430 | return rc; | ||
431 | } | ||
432 | |||
433 | /* set Cache Line size */ | ||
434 | temp_byte = 0x08; /* hard coded value for CLS */ | ||
435 | |||
436 | rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_CACHE_LINE_SIZE, temp_byte); | ||
437 | |||
438 | if (rc) { | ||
439 | dbg("%s: set CLS error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function); | ||
440 | } | ||
441 | |||
442 | /* set enable_perr */ | ||
443 | /* set enable_serr */ | ||
444 | |||
445 | return rc; | ||
446 | } | ||
447 | |||
448 | void pciehprm_enable_card( | ||
449 | struct controller *ctrl, | ||
450 | struct pci_func *func, | ||
451 | u8 card_type) | ||
452 | { | ||
453 | u16 command, bcommand; | ||
454 | struct pci_bus lpci_bus, *pci_bus; | ||
455 | unsigned int devfn; | ||
456 | int rc; | ||
457 | |||
458 | memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus)); | ||
459 | pci_bus = &lpci_bus; | ||
460 | pci_bus->number = func->bus; | ||
461 | devfn = PCI_DEVFN(func->device, func->function); | ||
462 | |||
463 | rc = pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &command); | ||
464 | |||
465 | command |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR | ||
466 | | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE | ||
467 | | PCI_COMMAND_IO | PCI_COMMAND_MEMORY; | ||
468 | |||
469 | rc = pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command); | ||
470 | |||
471 | if (card_type == PCI_HEADER_TYPE_BRIDGE) { | ||
472 | |||
473 | rc = pci_bus_read_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, &bcommand); | ||
474 | |||
475 | bcommand |= PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR | ||
476 | | PCI_BRIDGE_CTL_NO_ISA; | ||
477 | |||
478 | rc = pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, bcommand); | ||
479 | } | ||
480 | } | ||
481 | |||
482 | static int legacy_pciehprm_init_pci(void) | ||
483 | { | ||
484 | return 0; | ||
485 | } | ||
486 | |||
487 | int pciehprm_init(enum php_ctlr_type ctrl_type) | ||
488 | { | ||
489 | int retval; | ||
490 | |||
491 | switch (ctrl_type) { | ||
492 | case PCI: | ||
493 | retval = legacy_pciehprm_init_pci(); | ||
494 | break; | ||
495 | default: | ||
496 | retval = -ENODEV; | ||
497 | break; | ||
498 | } | ||
499 | |||
500 | return retval; | ||
501 | } | ||
diff --git a/drivers/pci/hotplug/pciehprm_nonacpi.h b/drivers/pci/hotplug/pciehprm_nonacpi.h new file mode 100644 index 000000000000..87c90e85ede9 --- /dev/null +++ b/drivers/pci/hotplug/pciehprm_nonacpi.h | |||
@@ -0,0 +1,56 @@ | |||
1 | /* | ||
2 | * PCIEHPRM NONACPI: PHP Resource Manager for Non-ACPI/Legacy platform | ||
3 | * | ||
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | ||
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * Copyright (C) 2001 IBM Corp. | ||
7 | * Copyright (C) 2003-2004 Intel Corporation | ||
8 | * | ||
9 | * All rights reserved. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or (at | ||
14 | * your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
19 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
20 | * details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
25 | * | ||
26 | * Send feedback to <greg@kroah.com>, <dely.l.sy@intel.com> | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | #ifndef _PCIEHPRM_NONACPI_H_ | ||
31 | #define _PCIEHPRM_NONACPI_H_ | ||
32 | |||
33 | struct irq_info { | ||
34 | u8 bus, devfn; /* bus, device and function */ | ||
35 | struct { | ||
36 | u8 link; /* IRQ line ID, chipset dependent, 0=not routed */ | ||
37 | u16 bitmap; /* Available IRQs */ | ||
38 | } __attribute__ ((packed)) irq[4]; | ||
39 | u8 slot; /* slot number, 0=onboard */ | ||
40 | u8 rfu; | ||
41 | } __attribute__ ((packed)); | ||
42 | |||
43 | struct irq_routing_table { | ||
44 | u32 signature; /* PIRQ_SIGNATURE should be here */ | ||
45 | u16 version; /* PIRQ_VERSION */ | ||
46 | u16 size; /* Table size in bytes */ | ||
47 | u8 rtr_bus, rtr_devfn; /* Where the interrupt router lies */ | ||
48 | u16 exclusive_irqs; /* IRQs devoted exclusively to PCI usage */ | ||
49 | u16 rtr_vendor, rtr_device; /* Vendor and device ID of interrupt router */ | ||
50 | u32 miniport_data; /* Crap */ | ||
51 | u8 rfu[11]; | ||
52 | u8 checksum; /* Modulo 256 checksum must give zero */ | ||
53 | struct irq_info slots[0]; | ||
54 | } __attribute__ ((packed)); | ||
55 | |||
56 | #endif /* _PCIEHPRM_NONACPI_H_ */ | ||
diff --git a/drivers/pci/hotplug/pcihp_skeleton.c b/drivers/pci/hotplug/pcihp_skeleton.c new file mode 100644 index 000000000000..6605d6bda529 --- /dev/null +++ b/drivers/pci/hotplug/pcihp_skeleton.c | |||
@@ -0,0 +1,375 @@ | |||
1 | /* | ||
2 | * PCI Hot Plug Controller Skeleton Driver - 0.2 | ||
3 | * | ||
4 | * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com) | ||
5 | * Copyright (C) 2001,2003 IBM Corp. | ||
6 | * | ||
7 | * All rights reserved. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or (at | ||
12 | * your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, but | ||
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
17 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
18 | * details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
23 | * | ||
24 | * This driver is to be used as a skeleton driver to be show how to interface | ||
25 | * with the pci hotplug core easily. | ||
26 | * | ||
27 | * Send feedback to <greg@kroah.com> | ||
28 | * | ||
29 | */ | ||
30 | |||
31 | #include <linux/config.h> | ||
32 | #include <linux/module.h> | ||
33 | #include <linux/moduleparam.h> | ||
34 | #include <linux/kernel.h> | ||
35 | #include <linux/slab.h> | ||
36 | #include <linux/pci.h> | ||
37 | #include <linux/init.h> | ||
38 | #include "pci_hotplug.h" | ||
39 | |||
40 | struct slot { | ||
41 | u8 number; | ||
42 | struct hotplug_slot *hotplug_slot; | ||
43 | struct list_head slot_list; | ||
44 | }; | ||
45 | |||
46 | static LIST_HEAD(slot_list); | ||
47 | |||
48 | #define MY_NAME "pcihp_skeleton" | ||
49 | |||
50 | #define dbg(format, arg...) \ | ||
51 | do { \ | ||
52 | if (debug) \ | ||
53 | printk (KERN_DEBUG "%s: " format "\n", \ | ||
54 | MY_NAME , ## arg); \ | ||
55 | } while (0) | ||
56 | #define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg) | ||
57 | #define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg) | ||
58 | #define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg) | ||
59 | |||
60 | |||
61 | |||
62 | /* local variables */ | ||
63 | static int debug; | ||
64 | static int num_slots; | ||
65 | |||
66 | #define DRIVER_VERSION "0.3" | ||
67 | #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>" | ||
68 | #define DRIVER_DESC "Hot Plug PCI Controller Skeleton Driver" | ||
69 | |||
70 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
71 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
72 | MODULE_LICENSE("GPL"); | ||
73 | module_param(debug, bool, 0644); | ||
74 | MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); | ||
75 | |||
76 | static int enable_slot (struct hotplug_slot *slot); | ||
77 | static int disable_slot (struct hotplug_slot *slot); | ||
78 | static int set_attention_status (struct hotplug_slot *slot, u8 value); | ||
79 | static int hardware_test (struct hotplug_slot *slot, u32 value); | ||
80 | static int get_power_status (struct hotplug_slot *slot, u8 *value); | ||
81 | static int get_attention_status (struct hotplug_slot *slot, u8 *value); | ||
82 | static int get_latch_status (struct hotplug_slot *slot, u8 *value); | ||
83 | static int get_adapter_status (struct hotplug_slot *slot, u8 *value); | ||
84 | |||
85 | static struct hotplug_slot_ops skel_hotplug_slot_ops = { | ||
86 | .owner = THIS_MODULE, | ||
87 | .enable_slot = enable_slot, | ||
88 | .disable_slot = disable_slot, | ||
89 | .set_attention_status = set_attention_status, | ||
90 | .hardware_test = hardware_test, | ||
91 | .get_power_status = get_power_status, | ||
92 | .get_attention_status = get_attention_status, | ||
93 | .get_latch_status = get_latch_status, | ||
94 | .get_adapter_status = get_adapter_status, | ||
95 | }; | ||
96 | |||
97 | static int enable_slot(struct hotplug_slot *hotplug_slot) | ||
98 | { | ||
99 | struct slot *slot = hotplug_slot->private; | ||
100 | int retval = 0; | ||
101 | |||
102 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
103 | |||
104 | /* | ||
105 | * Fill in code here to enable the specified slot | ||
106 | */ | ||
107 | |||
108 | return retval; | ||
109 | } | ||
110 | |||
111 | |||
112 | static int disable_slot(struct hotplug_slot *hotplug_slot) | ||
113 | { | ||
114 | struct slot *slot = hotplug_slot->private; | ||
115 | int retval = 0; | ||
116 | |||
117 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
118 | |||
119 | /* | ||
120 | * Fill in code here to disable the specified slot | ||
121 | */ | ||
122 | |||
123 | return retval; | ||
124 | } | ||
125 | |||
126 | static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status) | ||
127 | { | ||
128 | struct slot *slot = hotplug_slot->private; | ||
129 | int retval = 0; | ||
130 | |||
131 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
132 | |||
133 | switch (status) { | ||
134 | case 0: | ||
135 | /* | ||
136 | * Fill in code here to turn light off | ||
137 | */ | ||
138 | break; | ||
139 | |||
140 | case 1: | ||
141 | default: | ||
142 | /* | ||
143 | * Fill in code here to turn light on | ||
144 | */ | ||
145 | break; | ||
146 | } | ||
147 | |||
148 | return retval; | ||
149 | } | ||
150 | |||
151 | static int hardware_test(struct hotplug_slot *hotplug_slot, u32 value) | ||
152 | { | ||
153 | struct slot *slot = hotplug_slot->private; | ||
154 | int retval = 0; | ||
155 | |||
156 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
157 | |||
158 | switch (value) { | ||
159 | case 0: | ||
160 | /* Specify a test here */ | ||
161 | break; | ||
162 | case 1: | ||
163 | /* Specify another test here */ | ||
164 | break; | ||
165 | } | ||
166 | |||
167 | return retval; | ||
168 | } | ||
169 | |||
170 | static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) | ||
171 | { | ||
172 | struct slot *slot = hotplug_slot->private; | ||
173 | int retval = 0; | ||
174 | |||
175 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
176 | |||
177 | /* | ||
178 | * Fill in logic to get the current power status of the specific | ||
179 | * slot and store it in the *value location. | ||
180 | */ | ||
181 | |||
182 | return retval; | ||
183 | } | ||
184 | |||
185 | static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value) | ||
186 | { | ||
187 | struct slot *slot = hotplug_slot->private; | ||
188 | int retval = 0; | ||
189 | |||
190 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
191 | |||
192 | /* | ||
193 | * Fill in logic to get the current attention status of the specific | ||
194 | * slot and store it in the *value location. | ||
195 | */ | ||
196 | |||
197 | return retval; | ||
198 | } | ||
199 | |||
200 | static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) | ||
201 | { | ||
202 | struct slot *slot = hotplug_slot->private; | ||
203 | int retval = 0; | ||
204 | |||
205 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
206 | |||
207 | /* | ||
208 | * Fill in logic to get the current latch status of the specific | ||
209 | * slot and store it in the *value location. | ||
210 | */ | ||
211 | |||
212 | return retval; | ||
213 | } | ||
214 | |||
215 | static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) | ||
216 | { | ||
217 | struct slot *slot = hotplug_slot->private; | ||
218 | int retval = 0; | ||
219 | |||
220 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
221 | |||
222 | /* | ||
223 | * Fill in logic to get the current adapter status of the specific | ||
224 | * slot and store it in the *value location. | ||
225 | */ | ||
226 | |||
227 | return retval; | ||
228 | } | ||
229 | |||
230 | static void release_slot(struct hotplug_slot *hotplug_slot) | ||
231 | { | ||
232 | struct slot *slot = hotplug_slot->private; | ||
233 | |||
234 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
235 | kfree(slot->hotplug_slot->info); | ||
236 | kfree(slot->hotplug_slot->name); | ||
237 | kfree(slot->hotplug_slot); | ||
238 | kfree(slot); | ||
239 | } | ||
240 | |||
241 | #define SLOT_NAME_SIZE 10 | ||
242 | static void make_slot_name(struct slot *slot) | ||
243 | { | ||
244 | /* | ||
245 | * Stupid way to make a filename out of the slot name. | ||
246 | * replace this if your hardware provides a better way to name slots. | ||
247 | */ | ||
248 | snprintf(slot->hotplug_slot->name, SLOT_NAME_SIZE, "%d", slot->number); | ||
249 | } | ||
250 | |||
251 | /** | ||
252 | * init_slots - initialize 'struct slot' structures for each slot | ||
253 | * | ||
254 | */ | ||
255 | static int __init init_slots(void) | ||
256 | { | ||
257 | struct slot *slot; | ||
258 | struct hotplug_slot *hotplug_slot; | ||
259 | struct hotplug_slot_info *info; | ||
260 | char *name; | ||
261 | int retval = -ENOMEM; | ||
262 | int i; | ||
263 | |||
264 | /* | ||
265 | * Create a structure for each slot, and register that slot | ||
266 | * with the pci_hotplug subsystem. | ||
267 | */ | ||
268 | for (i = 0; i < num_slots; ++i) { | ||
269 | slot = kmalloc(sizeof(struct slot), GFP_KERNEL); | ||
270 | if (!slot) | ||
271 | goto error; | ||
272 | memset(slot, 0, sizeof(struct slot)); | ||
273 | |||
274 | hotplug_slot = kmalloc(sizeof(struct hotplug_slot), | ||
275 | GFP_KERNEL); | ||
276 | if (!hotplug_slot) | ||
277 | goto error_slot; | ||
278 | memset(hotplug_slot, 0, sizeof (struct hotplug_slot)); | ||
279 | slot->hotplug_slot = hotplug_slot; | ||
280 | |||
281 | info = kmalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL); | ||
282 | if (!info) | ||
283 | goto error_hpslot; | ||
284 | memset(info, 0, sizeof (struct hotplug_slot_info)); | ||
285 | hotplug_slot->info = info; | ||
286 | |||
287 | name = kmalloc(SLOT_NAME_SIZE, GFP_KERNEL); | ||
288 | if (!name) | ||
289 | goto error_info; | ||
290 | hotplug_slot->name = name; | ||
291 | |||
292 | slot->number = i; | ||
293 | |||
294 | hotplug_slot->private = slot; | ||
295 | hotplug_slot->release = &release_slot; | ||
296 | make_slot_name(slot); | ||
297 | hotplug_slot->ops = &skel_hotplug_slot_ops; | ||
298 | |||
299 | /* | ||
300 | * Initilize the slot info structure with some known | ||
301 | * good values. | ||
302 | */ | ||
303 | info->power_status = get_power_status(slot); | ||
304 | info->attention_status = get_attention_status(slot); | ||
305 | info->latch_status = get_latch_status(slot); | ||
306 | info->adapter_status = get_adapter_status(slot); | ||
307 | |||
308 | dbg("registering slot %d\n", i); | ||
309 | retval = pci_hp_register(slot->hotplug_slot); | ||
310 | if (retval) { | ||
311 | err("pci_hp_register failed with error %d\n", retval); | ||
312 | goto error_name; | ||
313 | } | ||
314 | |||
315 | /* add slot to our internal list */ | ||
316 | list_add(&slot->slot_list, &slot_list); | ||
317 | } | ||
318 | |||
319 | return 0; | ||
320 | error_name: | ||
321 | kfree(name); | ||
322 | error_info: | ||
323 | kfree(info); | ||
324 | error_hpslot: | ||
325 | kfree(hotplug_slot); | ||
326 | error_slot: | ||
327 | kfree(slot); | ||
328 | error: | ||
329 | return retval; | ||
330 | } | ||
331 | |||
332 | static void __exit cleanup_slots(void) | ||
333 | { | ||
334 | struct list_head *tmp; | ||
335 | struct list_head *next; | ||
336 | struct slot *slot; | ||
337 | |||
338 | /* | ||
339 | * Unregister all of our slots with the pci_hotplug subsystem. | ||
340 | * Memory will be freed in release_slot() callback after slot's | ||
341 | * lifespan is finished. | ||
342 | */ | ||
343 | list_for_each_safe(tmp, next, &slot_list) { | ||
344 | slot = list_entry(tmp, struct slot, slot_list); | ||
345 | list_del(&slot->slot_list); | ||
346 | pci_hp_deregister(slot->hotplug_slot); | ||
347 | } | ||
348 | } | ||
349 | |||
350 | static int __init pcihp_skel_init(void) | ||
351 | { | ||
352 | int retval; | ||
353 | |||
354 | info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); | ||
355 | /* | ||
356 | * Do specific initialization stuff for your driver here | ||
357 | * Like initializing your controller hardware (if any) and | ||
358 | * determining the number of slots you have in the system | ||
359 | * right now. | ||
360 | */ | ||
361 | num_slots = 5; | ||
362 | |||
363 | return init_slots(); | ||
364 | } | ||
365 | |||
366 | static void __exit pcihp_skel_exit(void) | ||
367 | { | ||
368 | /* | ||
369 | * Clean everything up. | ||
370 | */ | ||
371 | cleanup_slots(); | ||
372 | } | ||
373 | |||
374 | module_init(pcihp_skel_init); | ||
375 | module_exit(pcihp_skel_exit); | ||
diff --git a/drivers/pci/hotplug/rpadlpar.h b/drivers/pci/hotplug/rpadlpar.h new file mode 100644 index 000000000000..4a0a59b82eae --- /dev/null +++ b/drivers/pci/hotplug/rpadlpar.h | |||
@@ -0,0 +1,24 @@ | |||
1 | /* | ||
2 | * Interface for Dynamic Logical Partitioning of I/O Slots on | ||
3 | * RPA-compliant PPC64 platform. | ||
4 | * | ||
5 | * John Rose <johnrose@austin.ibm.com> | ||
6 | * October 2003 | ||
7 | * | ||
8 | * Copyright (C) 2003 IBM. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License | ||
12 | * as published by the Free Software Foundation; either version | ||
13 | * 2 of the License, or (at your option) any later version. | ||
14 | */ | ||
15 | #ifndef _RPADLPAR_IO_H_ | ||
16 | #define _RPADLPAR_IO_H_ | ||
17 | |||
18 | extern int dlpar_sysfs_init(void); | ||
19 | extern void dlpar_sysfs_exit(void); | ||
20 | |||
21 | extern int dlpar_add_slot(char *drc_name); | ||
22 | extern int dlpar_remove_slot(char *drc_name); | ||
23 | |||
24 | #endif | ||
diff --git a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c new file mode 100644 index 000000000000..86b384e42717 --- /dev/null +++ b/drivers/pci/hotplug/rpadlpar_core.c | |||
@@ -0,0 +1,503 @@ | |||
1 | /* | ||
2 | * Interface for Dynamic Logical Partitioning of I/O Slots on | ||
3 | * RPA-compliant PPC64 platform. | ||
4 | * | ||
5 | * John Rose <johnrose@austin.ibm.com> | ||
6 | * Linda Xie <lxie@us.ibm.com> | ||
7 | * | ||
8 | * October 2003 | ||
9 | * | ||
10 | * Copyright (C) 2003 IBM. | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version | ||
15 | * 2 of the License, or (at your option) any later version. | ||
16 | */ | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/pci.h> | ||
19 | #include <asm/pci-bridge.h> | ||
20 | #include <asm/semaphore.h> | ||
21 | #include <asm/rtas.h> | ||
22 | #include "../pci.h" | ||
23 | #include "rpaphp.h" | ||
24 | #include "rpadlpar.h" | ||
25 | |||
26 | static DECLARE_MUTEX(rpadlpar_sem); | ||
27 | |||
28 | #define NODE_TYPE_VIO 1 | ||
29 | #define NODE_TYPE_SLOT 2 | ||
30 | #define NODE_TYPE_PHB 3 | ||
31 | |||
32 | static struct device_node *find_php_slot_vio_node(char *drc_name) | ||
33 | { | ||
34 | struct device_node *child; | ||
35 | struct device_node *parent = of_find_node_by_name(NULL, "vdevice"); | ||
36 | char *loc_code; | ||
37 | |||
38 | if (!parent) | ||
39 | return NULL; | ||
40 | |||
41 | for (child = of_get_next_child(parent, NULL); | ||
42 | child; child = of_get_next_child(parent, child)) { | ||
43 | loc_code = get_property(child, "ibm,loc-code", NULL); | ||
44 | if (loc_code && !strncmp(loc_code, drc_name, strlen(drc_name))) | ||
45 | return child; | ||
46 | } | ||
47 | |||
48 | return NULL; | ||
49 | } | ||
50 | |||
51 | /* Find dlpar-capable pci node that contains the specified name and type */ | ||
52 | static struct device_node *find_php_slot_pci_node(char *drc_name, | ||
53 | char *drc_type) | ||
54 | { | ||
55 | struct device_node *np = NULL; | ||
56 | char *name; | ||
57 | char *type; | ||
58 | int rc; | ||
59 | |||
60 | while ((np = of_find_node_by_type(np, "pci"))) { | ||
61 | rc = rpaphp_get_drc_props(np, NULL, &name, &type, NULL); | ||
62 | if (rc == 0) | ||
63 | if (!strcmp(drc_name, name) && !strcmp(drc_type, type)) | ||
64 | break; | ||
65 | } | ||
66 | |||
67 | return np; | ||
68 | } | ||
69 | |||
70 | static struct device_node *find_newly_added_node(char *drc_name, int *node_type) | ||
71 | { | ||
72 | struct device_node *dn; | ||
73 | |||
74 | dn = find_php_slot_pci_node(drc_name, "SLOT"); | ||
75 | if (dn) { | ||
76 | *node_type = NODE_TYPE_SLOT; | ||
77 | return dn; | ||
78 | } | ||
79 | |||
80 | dn = find_php_slot_pci_node(drc_name, "PHB"); | ||
81 | if (dn) { | ||
82 | *node_type = NODE_TYPE_PHB; | ||
83 | return dn; | ||
84 | } | ||
85 | |||
86 | dn = find_php_slot_vio_node(drc_name); | ||
87 | if (dn) { | ||
88 | *node_type = NODE_TYPE_VIO; | ||
89 | return dn; | ||
90 | } | ||
91 | |||
92 | return NULL; | ||
93 | } | ||
94 | |||
95 | static struct slot *find_slot(char *drc_name) | ||
96 | { | ||
97 | struct list_head *tmp, *n; | ||
98 | struct slot *slot; | ||
99 | |||
100 | list_for_each_safe(tmp, n, &rpaphp_slot_head) { | ||
101 | slot = list_entry(tmp, struct slot, rpaphp_slot_list); | ||
102 | if (strcmp(slot->location, drc_name) == 0) | ||
103 | return slot; | ||
104 | } | ||
105 | |||
106 | return NULL; | ||
107 | } | ||
108 | |||
109 | static void rpadlpar_claim_one_bus(struct pci_bus *b) | ||
110 | { | ||
111 | struct list_head *ld; | ||
112 | struct pci_bus *child_bus; | ||
113 | |||
114 | for (ld = b->devices.next; ld != &b->devices; ld = ld->next) { | ||
115 | struct pci_dev *dev = pci_dev_b(ld); | ||
116 | int i; | ||
117 | |||
118 | for (i = 0; i < PCI_NUM_RESOURCES; i++) { | ||
119 | struct resource *r = &dev->resource[i]; | ||
120 | |||
121 | if (r->parent || !r->start || !r->flags) | ||
122 | continue; | ||
123 | rpaphp_claim_resource(dev, i); | ||
124 | } | ||
125 | } | ||
126 | |||
127 | list_for_each_entry(child_bus, &b->children, node) | ||
128 | rpadlpar_claim_one_bus(child_bus); | ||
129 | } | ||
130 | |||
131 | static int pci_add_secondary_bus(struct device_node *dn, | ||
132 | struct pci_dev *bridge_dev) | ||
133 | { | ||
134 | struct pci_controller *hose = dn->phb; | ||
135 | struct pci_bus *child; | ||
136 | u8 sec_busno; | ||
137 | |||
138 | /* Get busno of downstream bus */ | ||
139 | pci_read_config_byte(bridge_dev, PCI_SECONDARY_BUS, &sec_busno); | ||
140 | |||
141 | /* Allocate and add to children of bridge_dev->bus */ | ||
142 | child = pci_add_new_bus(bridge_dev->bus, bridge_dev, sec_busno); | ||
143 | if (!child) { | ||
144 | printk(KERN_ERR "%s: could not add secondary bus\n", __FUNCTION__); | ||
145 | return -ENOMEM; | ||
146 | } | ||
147 | |||
148 | sprintf(child->name, "PCI Bus #%02x", child->number); | ||
149 | |||
150 | /* Fixup subordinate bridge bases and resources */ | ||
151 | pcibios_fixup_bus(child); | ||
152 | |||
153 | /* Claim new bus resources */ | ||
154 | rpadlpar_claim_one_bus(bridge_dev->bus); | ||
155 | |||
156 | if (hose->last_busno < child->number) | ||
157 | hose->last_busno = child->number; | ||
158 | |||
159 | dn->bussubno = child->number; | ||
160 | |||
161 | /* ioremap() for child bus, which may or may not succeed */ | ||
162 | remap_bus_range(child); | ||
163 | |||
164 | return 0; | ||
165 | } | ||
166 | |||
167 | static struct pci_dev *dlpar_pci_add_bus(struct device_node *dn) | ||
168 | { | ||
169 | struct pci_controller *hose = dn->phb; | ||
170 | struct pci_dev *dev = NULL; | ||
171 | |||
172 | /* Scan phb bus for EADS device, adding new one to bus->devices */ | ||
173 | if (!pci_scan_single_device(hose->bus, dn->devfn)) { | ||
174 | printk(KERN_ERR "%s: found no device on bus\n", __FUNCTION__); | ||
175 | return NULL; | ||
176 | } | ||
177 | |||
178 | /* Add new devices to global lists. Register in proc, sysfs. */ | ||
179 | pci_bus_add_devices(hose->bus); | ||
180 | |||
181 | /* Confirm new bridge dev was created */ | ||
182 | dev = rpaphp_find_pci_dev(dn); | ||
183 | if (!dev) { | ||
184 | printk(KERN_ERR "%s: failed to add pci device\n", __FUNCTION__); | ||
185 | return NULL; | ||
186 | } | ||
187 | |||
188 | if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) { | ||
189 | printk(KERN_ERR "%s: unexpected header type %d\n", | ||
190 | __FUNCTION__, dev->hdr_type); | ||
191 | return NULL; | ||
192 | } | ||
193 | |||
194 | if (pci_add_secondary_bus(dn, dev)) | ||
195 | return NULL; | ||
196 | |||
197 | return dev; | ||
198 | } | ||
199 | |||
200 | static int dlpar_pci_remove_bus(struct pci_dev *bridge_dev) | ||
201 | { | ||
202 | struct pci_bus *secondary_bus; | ||
203 | |||
204 | if (!bridge_dev) { | ||
205 | printk(KERN_ERR "%s: unexpected null device\n", | ||
206 | __FUNCTION__); | ||
207 | return -EINVAL; | ||
208 | } | ||
209 | |||
210 | secondary_bus = bridge_dev->subordinate; | ||
211 | |||
212 | if (unmap_bus_range(secondary_bus)) { | ||
213 | printk(KERN_ERR "%s: failed to unmap bus range\n", | ||
214 | __FUNCTION__); | ||
215 | return -ERANGE; | ||
216 | } | ||
217 | |||
218 | pci_remove_bus_device(bridge_dev); | ||
219 | return 0; | ||
220 | } | ||
221 | |||
222 | static inline int dlpar_add_pci_slot(char *drc_name, struct device_node *dn) | ||
223 | { | ||
224 | struct pci_dev *dev; | ||
225 | |||
226 | /* Add pci bus */ | ||
227 | dev = dlpar_pci_add_bus(dn); | ||
228 | if (!dev) { | ||
229 | printk(KERN_ERR "%s: unable to add bus %s\n", __FUNCTION__, | ||
230 | drc_name); | ||
231 | return -EIO; | ||
232 | } | ||
233 | |||
234 | return 0; | ||
235 | } | ||
236 | |||
237 | static int dlpar_remove_root_bus(struct pci_controller *phb) | ||
238 | { | ||
239 | struct pci_bus *phb_bus; | ||
240 | int rc; | ||
241 | |||
242 | phb_bus = phb->bus; | ||
243 | if (!(list_empty(&phb_bus->children) && | ||
244 | list_empty(&phb_bus->devices))) { | ||
245 | return -EBUSY; | ||
246 | } | ||
247 | |||
248 | rc = pcibios_remove_root_bus(phb); | ||
249 | if (rc) | ||
250 | return -EIO; | ||
251 | |||
252 | device_unregister(phb_bus->bridge); | ||
253 | pci_remove_bus(phb_bus); | ||
254 | |||
255 | return 0; | ||
256 | } | ||
257 | |||
258 | static int dlpar_remove_phb(struct slot *slot) | ||
259 | { | ||
260 | struct pci_controller *phb; | ||
261 | struct device_node *dn; | ||
262 | int rc = 0; | ||
263 | |||
264 | dn = slot->dn; | ||
265 | if (!dn) { | ||
266 | printk(KERN_ERR "%s: unexpected NULL slot device node\n", | ||
267 | __FUNCTION__); | ||
268 | return -EIO; | ||
269 | } | ||
270 | |||
271 | phb = dn->phb; | ||
272 | if (!phb) { | ||
273 | printk(KERN_ERR "%s: unexpected NULL phb pointer\n", | ||
274 | __FUNCTION__); | ||
275 | return -EIO; | ||
276 | } | ||
277 | |||
278 | if (rpaphp_remove_slot(slot)) { | ||
279 | printk(KERN_ERR "%s: unable to remove hotplug slot %s\n", | ||
280 | __FUNCTION__, slot->location); | ||
281 | return -EIO; | ||
282 | } | ||
283 | |||
284 | rc = dlpar_remove_root_bus(phb); | ||
285 | if (rc < 0) | ||
286 | return rc; | ||
287 | |||
288 | return 0; | ||
289 | } | ||
290 | |||
291 | static int dlpar_add_phb(struct device_node *dn) | ||
292 | { | ||
293 | struct pci_controller *phb; | ||
294 | |||
295 | phb = init_phb_dynamic(dn); | ||
296 | if (!phb) | ||
297 | return -EINVAL; | ||
298 | |||
299 | return 0; | ||
300 | } | ||
301 | |||
302 | /** | ||
303 | * dlpar_add_slot - DLPAR add an I/O Slot | ||
304 | * @drc_name: drc-name of newly added slot | ||
305 | * | ||
306 | * Make the hotplug module and the kernel aware | ||
307 | * of a newly added I/O Slot. | ||
308 | * Return Codes - | ||
309 | * 0 Success | ||
310 | * -ENODEV Not a valid drc_name | ||
311 | * -EINVAL Slot already added | ||
312 | * -ERESTARTSYS Signalled before obtaining lock | ||
313 | * -EIO Internal PCI Error | ||
314 | */ | ||
315 | int dlpar_add_slot(char *drc_name) | ||
316 | { | ||
317 | struct device_node *dn = NULL; | ||
318 | int node_type; | ||
319 | int rc = 0; | ||
320 | |||
321 | if (down_interruptible(&rpadlpar_sem)) | ||
322 | return -ERESTARTSYS; | ||
323 | |||
324 | /* Check for existing hotplug slot */ | ||
325 | if (find_slot(drc_name)) { | ||
326 | rc = -EINVAL; | ||
327 | goto exit; | ||
328 | } | ||
329 | |||
330 | dn = find_newly_added_node(drc_name, &node_type); | ||
331 | if (!dn) { | ||
332 | rc = -ENODEV; | ||
333 | goto exit; | ||
334 | } | ||
335 | |||
336 | switch (node_type) { | ||
337 | case NODE_TYPE_VIO: | ||
338 | /* Just add hotplug slot */ | ||
339 | break; | ||
340 | case NODE_TYPE_SLOT: | ||
341 | rc = dlpar_add_pci_slot(drc_name, dn); | ||
342 | break; | ||
343 | case NODE_TYPE_PHB: | ||
344 | rc = dlpar_add_phb(dn); | ||
345 | break; | ||
346 | default: | ||
347 | printk("%s: unexpected node type\n", __FUNCTION__); | ||
348 | return -EIO; | ||
349 | } | ||
350 | |||
351 | if (!rc && rpaphp_add_slot(dn)) { | ||
352 | printk(KERN_ERR "%s: unable to add hotplug slot %s\n", | ||
353 | __FUNCTION__, drc_name); | ||
354 | rc = -EIO; | ||
355 | } | ||
356 | exit: | ||
357 | up(&rpadlpar_sem); | ||
358 | return rc; | ||
359 | } | ||
360 | |||
361 | /** | ||
362 | * dlpar_remove_vio_slot - DLPAR remove a virtual I/O Slot | ||
363 | * @drc_name: drc-name of newly added slot | ||
364 | * | ||
365 | * Remove the kernel and hotplug representations | ||
366 | * of an I/O Slot. | ||
367 | * Return Codes: | ||
368 | * 0 Success | ||
369 | * -EIO Internal Error | ||
370 | */ | ||
371 | int dlpar_remove_vio_slot(struct slot *slot, char *drc_name) | ||
372 | { | ||
373 | /* Remove hotplug slot */ | ||
374 | |||
375 | if (rpaphp_remove_slot(slot)) { | ||
376 | printk(KERN_ERR "%s: unable to remove hotplug slot %s\n", | ||
377 | __FUNCTION__, drc_name); | ||
378 | return -EIO; | ||
379 | } | ||
380 | return 0; | ||
381 | } | ||
382 | |||
383 | /** | ||
384 | * dlpar_remove_slot - DLPAR remove a PCI I/O Slot | ||
385 | * @drc_name: drc-name of newly added slot | ||
386 | * | ||
387 | * Remove the kernel and hotplug representations | ||
388 | * of a PCI I/O Slot. | ||
389 | * Return Codes: | ||
390 | * 0 Success | ||
391 | * -ENODEV Not a valid drc_name | ||
392 | * -EIO Internal PCI Error | ||
393 | */ | ||
394 | int dlpar_remove_pci_slot(struct slot *slot, char *drc_name) | ||
395 | { | ||
396 | struct pci_dev *bridge_dev; | ||
397 | |||
398 | bridge_dev = slot->bridge; | ||
399 | if (!bridge_dev) { | ||
400 | printk(KERN_ERR "%s: unexpected null bridge device\n", | ||
401 | __FUNCTION__); | ||
402 | return -EIO; | ||
403 | } | ||
404 | |||
405 | /* Remove hotplug slot */ | ||
406 | if (rpaphp_remove_slot(slot)) { | ||
407 | printk(KERN_ERR "%s: unable to remove hotplug slot %s\n", | ||
408 | __FUNCTION__, drc_name); | ||
409 | return -EIO; | ||
410 | } | ||
411 | |||
412 | /* Remove pci bus */ | ||
413 | |||
414 | if (dlpar_pci_remove_bus(bridge_dev)) { | ||
415 | printk(KERN_ERR "%s: unable to remove pci bus %s\n", | ||
416 | __FUNCTION__, drc_name); | ||
417 | return -EIO; | ||
418 | } | ||
419 | return 0; | ||
420 | } | ||
421 | |||
422 | /** | ||
423 | * dlpar_remove_slot - DLPAR remove an I/O Slot | ||
424 | * @drc_name: drc-name of newly added slot | ||
425 | * | ||
426 | * Remove the kernel and hotplug representations | ||
427 | * of an I/O Slot. | ||
428 | * Return Codes: | ||
429 | * 0 Success | ||
430 | * -ENODEV Not a valid drc_name | ||
431 | * -EINVAL Slot already removed | ||
432 | * -ERESTARTSYS Signalled before obtaining lock | ||
433 | * -EIO Internal Error | ||
434 | */ | ||
435 | int dlpar_remove_slot(char *drc_name) | ||
436 | { | ||
437 | struct slot *slot; | ||
438 | int rc = 0; | ||
439 | |||
440 | if (down_interruptible(&rpadlpar_sem)) | ||
441 | return -ERESTARTSYS; | ||
442 | |||
443 | if (!find_php_slot_vio_node(drc_name) && | ||
444 | !find_php_slot_pci_node(drc_name, "SLOT") && | ||
445 | !find_php_slot_pci_node(drc_name, "PHB")) { | ||
446 | rc = -ENODEV; | ||
447 | goto exit; | ||
448 | } | ||
449 | |||
450 | slot = find_slot(drc_name); | ||
451 | if (!slot) { | ||
452 | rc = -EINVAL; | ||
453 | goto exit; | ||
454 | } | ||
455 | |||
456 | if (slot->type == PHB) { | ||
457 | rc = dlpar_remove_phb(slot); | ||
458 | } else { | ||
459 | switch (slot->dev_type) { | ||
460 | case PCI_DEV: | ||
461 | rc = dlpar_remove_pci_slot(slot, drc_name); | ||
462 | break; | ||
463 | |||
464 | case VIO_DEV: | ||
465 | rc = dlpar_remove_vio_slot(slot, drc_name); | ||
466 | break; | ||
467 | } | ||
468 | } | ||
469 | exit: | ||
470 | up(&rpadlpar_sem); | ||
471 | return rc; | ||
472 | } | ||
473 | |||
474 | static inline int is_dlpar_capable(void) | ||
475 | { | ||
476 | int rc = rtas_token("ibm,configure-connector"); | ||
477 | |||
478 | return (int) (rc != RTAS_UNKNOWN_SERVICE); | ||
479 | } | ||
480 | |||
481 | int __init rpadlpar_io_init(void) | ||
482 | { | ||
483 | int rc = 0; | ||
484 | |||
485 | if (!is_dlpar_capable()) { | ||
486 | printk(KERN_WARNING "%s: partition not DLPAR capable\n", | ||
487 | __FUNCTION__); | ||
488 | return -EPERM; | ||
489 | } | ||
490 | |||
491 | rc = dlpar_sysfs_init(); | ||
492 | return rc; | ||
493 | } | ||
494 | |||
495 | void rpadlpar_io_exit(void) | ||
496 | { | ||
497 | dlpar_sysfs_exit(); | ||
498 | return; | ||
499 | } | ||
500 | |||
501 | module_init(rpadlpar_io_init); | ||
502 | module_exit(rpadlpar_io_exit); | ||
503 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/pci/hotplug/rpadlpar_sysfs.c b/drivers/pci/hotplug/rpadlpar_sysfs.c new file mode 100644 index 000000000000..3285b822478d --- /dev/null +++ b/drivers/pci/hotplug/rpadlpar_sysfs.c | |||
@@ -0,0 +1,151 @@ | |||
1 | /* | ||
2 | * Interface for Dynamic Logical Partitioning of I/O Slots on | ||
3 | * RPA-compliant PPC64 platform. | ||
4 | * | ||
5 | * John Rose <johnrose@austin.ibm.com> | ||
6 | * October 2003 | ||
7 | * | ||
8 | * Copyright (C) 2003 IBM. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License | ||
12 | * as published by the Free Software Foundation; either version | ||
13 | * 2 of the License, or (at your option) any later version. | ||
14 | */ | ||
15 | #include <linux/kobject.h> | ||
16 | #include <linux/string.h> | ||
17 | #include "pci_hotplug.h" | ||
18 | #include "rpadlpar.h" | ||
19 | |||
20 | #define DLPAR_KOBJ_NAME "control" | ||
21 | #define ADD_SLOT_ATTR_NAME "add_slot" | ||
22 | #define REMOVE_SLOT_ATTR_NAME "remove_slot" | ||
23 | |||
24 | #define MAX_DRC_NAME_LEN 64 | ||
25 | |||
26 | /* Store return code of dlpar operation in attribute struct */ | ||
27 | struct dlpar_io_attr { | ||
28 | int rc; | ||
29 | struct attribute attr; | ||
30 | ssize_t (*store)(struct dlpar_io_attr *dlpar_attr, const char *buf, | ||
31 | size_t nbytes); | ||
32 | }; | ||
33 | |||
34 | /* Common show callback for all attrs, display the return code | ||
35 | * of the dlpar op */ | ||
36 | static ssize_t | ||
37 | dlpar_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) | ||
38 | { | ||
39 | struct dlpar_io_attr *dlpar_attr = container_of(attr, | ||
40 | struct dlpar_io_attr, attr); | ||
41 | return sprintf(buf, "%d\n", dlpar_attr->rc); | ||
42 | } | ||
43 | |||
44 | static ssize_t | ||
45 | dlpar_attr_store(struct kobject * kobj, struct attribute * attr, | ||
46 | const char *buf, size_t nbytes) | ||
47 | { | ||
48 | struct dlpar_io_attr *dlpar_attr = container_of(attr, | ||
49 | struct dlpar_io_attr, attr); | ||
50 | return dlpar_attr->store ? | ||
51 | dlpar_attr->store(dlpar_attr, buf, nbytes) : 0; | ||
52 | } | ||
53 | |||
54 | static struct sysfs_ops dlpar_attr_sysfs_ops = { | ||
55 | .show = dlpar_attr_show, | ||
56 | .store = dlpar_attr_store, | ||
57 | }; | ||
58 | |||
59 | static ssize_t add_slot_store(struct dlpar_io_attr *dlpar_attr, | ||
60 | const char *buf, size_t nbytes) | ||
61 | { | ||
62 | char drc_name[MAX_DRC_NAME_LEN]; | ||
63 | char *end; | ||
64 | |||
65 | if (nbytes > MAX_DRC_NAME_LEN) | ||
66 | return 0; | ||
67 | |||
68 | memcpy(drc_name, buf, nbytes); | ||
69 | |||
70 | end = strchr(drc_name, '\n'); | ||
71 | if (!end) | ||
72 | end = &drc_name[nbytes]; | ||
73 | *end = '\0'; | ||
74 | |||
75 | dlpar_attr->rc = dlpar_add_slot(drc_name); | ||
76 | |||
77 | return nbytes; | ||
78 | } | ||
79 | |||
80 | static ssize_t remove_slot_store(struct dlpar_io_attr *dlpar_attr, | ||
81 | const char *buf, size_t nbytes) | ||
82 | { | ||
83 | char drc_name[MAX_DRC_NAME_LEN]; | ||
84 | char *end; | ||
85 | |||
86 | if (nbytes > MAX_DRC_NAME_LEN) | ||
87 | return 0; | ||
88 | |||
89 | memcpy(drc_name, buf, nbytes); | ||
90 | |||
91 | end = strchr(drc_name, '\n'); | ||
92 | if (!end) | ||
93 | end = &drc_name[nbytes]; | ||
94 | *end = '\0'; | ||
95 | |||
96 | dlpar_attr->rc = dlpar_remove_slot(drc_name); | ||
97 | |||
98 | return nbytes; | ||
99 | } | ||
100 | |||
101 | static struct dlpar_io_attr add_slot_attr = { | ||
102 | .rc = 0, | ||
103 | .attr = { .name = ADD_SLOT_ATTR_NAME, .mode = 0644, }, | ||
104 | .store = add_slot_store, | ||
105 | }; | ||
106 | |||
107 | static struct dlpar_io_attr remove_slot_attr = { | ||
108 | .rc = 0, | ||
109 | .attr = { .name = REMOVE_SLOT_ATTR_NAME, .mode = 0644}, | ||
110 | .store = remove_slot_store, | ||
111 | }; | ||
112 | |||
113 | static struct attribute *default_attrs[] = { | ||
114 | &add_slot_attr.attr, | ||
115 | &remove_slot_attr.attr, | ||
116 | NULL, | ||
117 | }; | ||
118 | |||
119 | static void dlpar_io_release(struct kobject *kobj) | ||
120 | { | ||
121 | /* noop */ | ||
122 | return; | ||
123 | } | ||
124 | |||
125 | struct kobj_type ktype_dlpar_io = { | ||
126 | .release = dlpar_io_release, | ||
127 | .sysfs_ops = &dlpar_attr_sysfs_ops, | ||
128 | .default_attrs = default_attrs, | ||
129 | }; | ||
130 | |||
131 | struct kset dlpar_io_kset = { | ||
132 | .subsys = &pci_hotplug_slots_subsys, | ||
133 | .kobj = {.name = DLPAR_KOBJ_NAME, .ktype=&ktype_dlpar_io,}, | ||
134 | .ktype = &ktype_dlpar_io, | ||
135 | }; | ||
136 | |||
137 | int dlpar_sysfs_init(void) | ||
138 | { | ||
139 | if (kset_register(&dlpar_io_kset)) { | ||
140 | printk(KERN_ERR "rpadlpar_io: cannot register kset for %s\n", | ||
141 | dlpar_io_kset.kobj.name); | ||
142 | return -EINVAL; | ||
143 | } | ||
144 | |||
145 | return 0; | ||
146 | } | ||
147 | |||
148 | void dlpar_sysfs_exit(void) | ||
149 | { | ||
150 | kset_unregister(&dlpar_io_kset); | ||
151 | } | ||
diff --git a/drivers/pci/hotplug/rpaphp.h b/drivers/pci/hotplug/rpaphp.h new file mode 100644 index 000000000000..81746e6e0e0f --- /dev/null +++ b/drivers/pci/hotplug/rpaphp.h | |||
@@ -0,0 +1,138 @@ | |||
1 | /* | ||
2 | * PCI Hot Plug Controller Driver for RPA-compliant PPC64 platform. | ||
3 | * | ||
4 | * Copyright (C) 2003 Linda Xie <lxie@us.ibm.com> | ||
5 | * | ||
6 | * All rights reserved. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or (at | ||
11 | * your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, but | ||
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
16 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
17 | * details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
22 | * | ||
23 | * Send feedback to <lxie@us.ibm.com>, | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #ifndef _PPC64PHP_H | ||
28 | #define _PPC64PHP_H | ||
29 | |||
30 | #include <linux/pci.h> | ||
31 | #include "pci_hotplug.h" | ||
32 | |||
33 | #define PHB 2 | ||
34 | #define HOTPLUG 1 | ||
35 | #define EMBEDDED 0 | ||
36 | |||
37 | #define DR_INDICATOR 9002 | ||
38 | #define DR_ENTITY_SENSE 9003 | ||
39 | |||
40 | #define POWER_ON 100 | ||
41 | #define POWER_OFF 0 | ||
42 | |||
43 | #define LED_OFF 0 | ||
44 | #define LED_ON 1 /* continuous on */ | ||
45 | #define LED_ID 2 /* slow blinking */ | ||
46 | #define LED_ACTION 3 /* fast blinking */ | ||
47 | |||
48 | /* Sensor values from rtas_get-sensor */ | ||
49 | #define EMPTY 0 /* No card in slot */ | ||
50 | #define PRESENT 1 /* Card in slot */ | ||
51 | |||
52 | #define MY_NAME "rpaphp" | ||
53 | extern int debug; | ||
54 | #define dbg(format, arg...) \ | ||
55 | do { \ | ||
56 | if (debug) \ | ||
57 | printk(KERN_DEBUG "%s: " format, \ | ||
58 | MY_NAME , ## arg); \ | ||
59 | } while (0) | ||
60 | #define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg) | ||
61 | #define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg) | ||
62 | #define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME , ## arg) | ||
63 | |||
64 | /* slot types */ | ||
65 | #define VIO_DEV 1 | ||
66 | #define PCI_DEV 2 | ||
67 | |||
68 | /* slot states */ | ||
69 | |||
70 | #define NOT_VALID 3 | ||
71 | #define NOT_CONFIGURED 2 | ||
72 | #define CONFIGURED 1 | ||
73 | #define EMPTY 0 | ||
74 | |||
75 | struct rpaphp_pci_func { | ||
76 | struct pci_dev *pci_dev; | ||
77 | struct list_head sibling; | ||
78 | }; | ||
79 | |||
80 | /* | ||
81 | * struct slot - slot information for each *physical* slot | ||
82 | */ | ||
83 | struct slot { | ||
84 | struct list_head rpaphp_slot_list; | ||
85 | int state; | ||
86 | u32 index; | ||
87 | u32 type; | ||
88 | u32 power_domain; | ||
89 | char *name; | ||
90 | char *location; | ||
91 | u8 removable; | ||
92 | u8 dev_type; /* VIO or PCI */ | ||
93 | struct device_node *dn; /* slot's device_node in OFDT */ | ||
94 | /* dn has phb info */ | ||
95 | struct pci_dev *bridge; /* slot's pci_dev in pci_devices */ | ||
96 | union { | ||
97 | struct list_head *pci_devs; /* pci_devs in PCI slot */ | ||
98 | struct vio_dev *vio_dev; /* vio_dev in VIO slot */ | ||
99 | } dev; | ||
100 | struct hotplug_slot *hotplug_slot; | ||
101 | }; | ||
102 | |||
103 | extern struct hotplug_slot_ops rpaphp_hotplug_slot_ops; | ||
104 | extern struct list_head rpaphp_slot_head; | ||
105 | extern int num_slots; | ||
106 | |||
107 | /* function prototypes */ | ||
108 | |||
109 | /* rpaphp_pci.c */ | ||
110 | extern struct pci_dev *rpaphp_find_pci_dev(struct device_node *dn); | ||
111 | extern int rpaphp_claim_resource(struct pci_dev *dev, int resource); | ||
112 | extern int rpaphp_enable_pci_slot(struct slot *slot); | ||
113 | extern int register_pci_slot(struct slot *slot); | ||
114 | extern int rpaphp_unconfig_pci_adapter(struct slot *slot); | ||
115 | extern int rpaphp_get_pci_adapter_status(struct slot *slot, int is_init, u8 * value); | ||
116 | extern struct hotplug_slot *rpaphp_find_hotplug_slot(struct pci_dev *dev); | ||
117 | |||
118 | /* rpaphp_core.c */ | ||
119 | extern int rpaphp_add_slot(struct device_node *dn); | ||
120 | extern int rpaphp_remove_slot(struct slot *slot); | ||
121 | extern int rpaphp_get_drc_props(struct device_node *dn, int *drc_index, | ||
122 | char **drc_name, char **drc_type, int *drc_power_domain); | ||
123 | |||
124 | /* rpaphp_vio.c */ | ||
125 | extern int rpaphp_get_vio_adapter_status(struct slot *slot, int is_init, u8 * value); | ||
126 | extern int rpaphp_unconfig_vio_adapter(struct slot *slot); | ||
127 | extern int register_vio_slot(struct device_node *dn); | ||
128 | extern int rpaphp_enable_vio_slot(struct slot *slot); | ||
129 | |||
130 | /* rpaphp_slot.c */ | ||
131 | extern void dealloc_slot_struct(struct slot *slot); | ||
132 | extern struct slot *alloc_slot_struct(struct device_node *dn, int drc_index, char *drc_name, int power_domain); | ||
133 | extern int register_slot(struct slot *slot); | ||
134 | extern int deregister_slot(struct slot *slot); | ||
135 | extern int rpaphp_get_power_status(struct slot *slot, u8 * value); | ||
136 | extern int rpaphp_set_attention_status(struct slot *slot, u8 status); | ||
137 | |||
138 | #endif /* _PPC64PHP_H */ | ||
diff --git a/drivers/pci/hotplug/rpaphp_core.c b/drivers/pci/hotplug/rpaphp_core.c new file mode 100644 index 000000000000..29117a3a3287 --- /dev/null +++ b/drivers/pci/hotplug/rpaphp_core.c | |||
@@ -0,0 +1,536 @@ | |||
1 | /* | ||
2 | * PCI Hot Plug Controller Driver for RPA-compliant PPC64 platform. | ||
3 | * Copyright (C) 2003 Linda Xie <lxie@us.ibm.com> | ||
4 | * | ||
5 | * All rights reserved. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or (at | ||
10 | * your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, but | ||
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
15 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
16 | * details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
21 | * | ||
22 | * Send feedback to <lxie@us.ibm.com> | ||
23 | * | ||
24 | */ | ||
25 | #include <linux/config.h> | ||
26 | #include <linux/kernel.h> | ||
27 | #include <linux/module.h> | ||
28 | #include <linux/moduleparam.h> | ||
29 | #include <linux/pci.h> | ||
30 | #include <linux/slab.h> | ||
31 | #include <linux/smp.h> | ||
32 | #include <linux/smp_lock.h> | ||
33 | #include <linux/init.h> | ||
34 | #include <asm/eeh.h> /* for eeh_add_device() */ | ||
35 | #include <asm/rtas.h> /* rtas_call */ | ||
36 | #include <asm/pci-bridge.h> /* for pci_controller */ | ||
37 | #include "../pci.h" /* for pci_add_new_bus */ | ||
38 | /* and pci_do_scan_bus */ | ||
39 | #include "rpaphp.h" | ||
40 | #include "pci_hotplug.h" | ||
41 | |||
42 | int debug; | ||
43 | static struct semaphore rpaphp_sem; | ||
44 | LIST_HEAD(rpaphp_slot_head); | ||
45 | int num_slots; | ||
46 | |||
47 | #define DRIVER_VERSION "0.1" | ||
48 | #define DRIVER_AUTHOR "Linda Xie <lxie@us.ibm.com>" | ||
49 | #define DRIVER_DESC "RPA HOT Plug PCI Controller Driver" | ||
50 | |||
51 | #define MAX_LOC_CODE 128 | ||
52 | |||
53 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
54 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
55 | MODULE_LICENSE("GPL"); | ||
56 | |||
57 | module_param(debug, bool, 0644); | ||
58 | |||
59 | static int enable_slot(struct hotplug_slot *slot); | ||
60 | static int disable_slot(struct hotplug_slot *slot); | ||
61 | static int set_attention_status(struct hotplug_slot *slot, u8 value); | ||
62 | static int get_power_status(struct hotplug_slot *slot, u8 * value); | ||
63 | static int get_attention_status(struct hotplug_slot *slot, u8 * value); | ||
64 | static int get_adapter_status(struct hotplug_slot *slot, u8 * value); | ||
65 | static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value); | ||
66 | |||
67 | struct hotplug_slot_ops rpaphp_hotplug_slot_ops = { | ||
68 | .owner = THIS_MODULE, | ||
69 | .enable_slot = enable_slot, | ||
70 | .disable_slot = disable_slot, | ||
71 | .set_attention_status = set_attention_status, | ||
72 | .get_power_status = get_power_status, | ||
73 | .get_attention_status = get_attention_status, | ||
74 | .get_adapter_status = get_adapter_status, | ||
75 | .get_max_bus_speed = get_max_bus_speed, | ||
76 | }; | ||
77 | |||
78 | static int rpaphp_get_attention_status(struct slot *slot) | ||
79 | { | ||
80 | return slot->hotplug_slot->info->attention_status; | ||
81 | } | ||
82 | |||
83 | /** | ||
84 | * set_attention_status - set attention LED | ||
85 | * echo 0 > attention -- set LED OFF | ||
86 | * echo 1 > attention -- set LED ON | ||
87 | * echo 2 > attention -- set LED ID(identify, light is blinking) | ||
88 | * | ||
89 | */ | ||
90 | static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value) | ||
91 | { | ||
92 | int retval = 0; | ||
93 | struct slot *slot = (struct slot *)hotplug_slot->private; | ||
94 | |||
95 | down(&rpaphp_sem); | ||
96 | switch (value) { | ||
97 | case 0: | ||
98 | retval = rpaphp_set_attention_status(slot, LED_OFF); | ||
99 | hotplug_slot->info->attention_status = 0; | ||
100 | break; | ||
101 | case 1: | ||
102 | default: | ||
103 | retval = rpaphp_set_attention_status(slot, LED_ON); | ||
104 | hotplug_slot->info->attention_status = 1; | ||
105 | break; | ||
106 | case 2: | ||
107 | retval = rpaphp_set_attention_status(slot, LED_ID); | ||
108 | hotplug_slot->info->attention_status = 2; | ||
109 | break; | ||
110 | } | ||
111 | up(&rpaphp_sem); | ||
112 | return retval; | ||
113 | } | ||
114 | |||
115 | /** | ||
116 | * get_power_status - get power status of a slot | ||
117 | * @hotplug_slot: slot to get status | ||
118 | * @value: pointer to store status | ||
119 | * | ||
120 | * | ||
121 | */ | ||
122 | static int get_power_status(struct hotplug_slot *hotplug_slot, u8 * value) | ||
123 | { | ||
124 | int retval; | ||
125 | struct slot *slot = (struct slot *)hotplug_slot->private; | ||
126 | |||
127 | down(&rpaphp_sem); | ||
128 | retval = rpaphp_get_power_status(slot, value); | ||
129 | up(&rpaphp_sem); | ||
130 | return retval; | ||
131 | } | ||
132 | |||
133 | /** | ||
134 | * get_attention_status - get attention LED status | ||
135 | * | ||
136 | * | ||
137 | */ | ||
138 | static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 * value) | ||
139 | { | ||
140 | int retval = 0; | ||
141 | struct slot *slot = (struct slot *)hotplug_slot->private; | ||
142 | |||
143 | down(&rpaphp_sem); | ||
144 | *value = rpaphp_get_attention_status(slot); | ||
145 | up(&rpaphp_sem); | ||
146 | return retval; | ||
147 | } | ||
148 | |||
149 | static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 * value) | ||
150 | { | ||
151 | struct slot *slot = (struct slot *)hotplug_slot->private; | ||
152 | int retval = 0; | ||
153 | |||
154 | down(&rpaphp_sem); | ||
155 | /* have to go through this */ | ||
156 | switch (slot->dev_type) { | ||
157 | case PCI_DEV: | ||
158 | retval = rpaphp_get_pci_adapter_status(slot, 0, value); | ||
159 | break; | ||
160 | case VIO_DEV: | ||
161 | retval = rpaphp_get_vio_adapter_status(slot, 0, value); | ||
162 | break; | ||
163 | default: | ||
164 | retval = -EINVAL; | ||
165 | } | ||
166 | up(&rpaphp_sem); | ||
167 | return retval; | ||
168 | } | ||
169 | |||
170 | static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) | ||
171 | { | ||
172 | struct slot *slot = (struct slot *)hotplug_slot->private; | ||
173 | |||
174 | down(&rpaphp_sem); | ||
175 | switch (slot->type) { | ||
176 | case 1: | ||
177 | case 2: | ||
178 | case 3: | ||
179 | case 4: | ||
180 | case 5: | ||
181 | case 6: | ||
182 | *value = PCI_SPEED_33MHz; /* speed for case 1-6 */ | ||
183 | break; | ||
184 | case 7: | ||
185 | case 8: | ||
186 | *value = PCI_SPEED_66MHz; | ||
187 | break; | ||
188 | case 11: | ||
189 | case 14: | ||
190 | *value = PCI_SPEED_66MHz_PCIX; | ||
191 | break; | ||
192 | case 12: | ||
193 | case 15: | ||
194 | *value = PCI_SPEED_100MHz_PCIX; | ||
195 | break; | ||
196 | case 13: | ||
197 | case 16: | ||
198 | *value = PCI_SPEED_133MHz_PCIX; | ||
199 | break; | ||
200 | default: | ||
201 | *value = PCI_SPEED_UNKNOWN; | ||
202 | break; | ||
203 | |||
204 | } | ||
205 | up(&rpaphp_sem); | ||
206 | return 0; | ||
207 | } | ||
208 | |||
209 | int rpaphp_remove_slot(struct slot *slot) | ||
210 | { | ||
211 | return deregister_slot(slot); | ||
212 | } | ||
213 | |||
214 | static int get_children_props(struct device_node *dn, int **drc_indexes, | ||
215 | int **drc_names, int **drc_types, int **drc_power_domains) | ||
216 | { | ||
217 | int *indexes, *names; | ||
218 | int *types, *domains; | ||
219 | |||
220 | indexes = (int *) get_property(dn, "ibm,drc-indexes", NULL); | ||
221 | names = (int *) get_property(dn, "ibm,drc-names", NULL); | ||
222 | types = (int *) get_property(dn, "ibm,drc-types", NULL); | ||
223 | domains = (int *) get_property(dn, "ibm,drc-power-domains", NULL); | ||
224 | |||
225 | if (!indexes || !names || !types || !domains) { | ||
226 | /* Slot does not have dynamically-removable children */ | ||
227 | return -EINVAL; | ||
228 | } | ||
229 | if (drc_indexes) | ||
230 | *drc_indexes = indexes; | ||
231 | if (drc_names) | ||
232 | /* &drc_names[1] contains NULL terminated slot names */ | ||
233 | *drc_names = names; | ||
234 | if (drc_types) | ||
235 | /* &drc_types[1] contains NULL terminated slot types */ | ||
236 | *drc_types = types; | ||
237 | if (drc_power_domains) | ||
238 | *drc_power_domains = domains; | ||
239 | |||
240 | return 0; | ||
241 | } | ||
242 | |||
243 | /* To get the DRC props describing the current node, first obtain it's | ||
244 | * my-drc-index property. Next obtain the DRC list from it's parent. Use | ||
245 | * the my-drc-index for correlation, and obtain the requested properties. | ||
246 | */ | ||
247 | int rpaphp_get_drc_props(struct device_node *dn, int *drc_index, | ||
248 | char **drc_name, char **drc_type, int *drc_power_domain) | ||
249 | { | ||
250 | int *indexes, *names; | ||
251 | int *types, *domains; | ||
252 | unsigned int *my_index; | ||
253 | char *name_tmp, *type_tmp; | ||
254 | int i, rc; | ||
255 | |||
256 | my_index = (int *) get_property(dn, "ibm,my-drc-index", NULL); | ||
257 | if (!my_index) { | ||
258 | /* Node isn't DLPAR/hotplug capable */ | ||
259 | return -EINVAL; | ||
260 | } | ||
261 | |||
262 | rc = get_children_props(dn->parent, &indexes, &names, &types, &domains); | ||
263 | if (rc < 0) { | ||
264 | return -EINVAL; | ||
265 | } | ||
266 | |||
267 | name_tmp = (char *) &names[1]; | ||
268 | type_tmp = (char *) &types[1]; | ||
269 | |||
270 | /* Iterate through parent properties, looking for my-drc-index */ | ||
271 | for (i = 0; i < indexes[0]; i++) { | ||
272 | if ((unsigned int) indexes[i + 1] == *my_index) { | ||
273 | if (drc_name) | ||
274 | *drc_name = name_tmp; | ||
275 | if (drc_type) | ||
276 | *drc_type = type_tmp; | ||
277 | if (drc_index) | ||
278 | *drc_index = *my_index; | ||
279 | if (drc_power_domain) | ||
280 | *drc_power_domain = domains[i+1]; | ||
281 | return 0; | ||
282 | } | ||
283 | name_tmp += (strlen(name_tmp) + 1); | ||
284 | type_tmp += (strlen(type_tmp) + 1); | ||
285 | } | ||
286 | |||
287 | return -EINVAL; | ||
288 | } | ||
289 | |||
290 | static int is_php_type(char *drc_type) | ||
291 | { | ||
292 | unsigned long value; | ||
293 | char *endptr; | ||
294 | |||
295 | /* PCI Hotplug nodes have an integer for drc_type */ | ||
296 | value = simple_strtoul(drc_type, &endptr, 10); | ||
297 | if (endptr == drc_type) | ||
298 | return 0; | ||
299 | |||
300 | return 1; | ||
301 | } | ||
302 | |||
303 | static int is_php_dn(struct device_node *dn, int **indexes, int **names, | ||
304 | int **types, int **power_domains) | ||
305 | { | ||
306 | int *drc_types; | ||
307 | int rc; | ||
308 | |||
309 | rc = get_children_props(dn, indexes, names, &drc_types, power_domains); | ||
310 | if (rc >= 0) { | ||
311 | if (is_php_type((char *) &drc_types[1])) { | ||
312 | *types = drc_types; | ||
313 | return 1; | ||
314 | } | ||
315 | } | ||
316 | |||
317 | return 0; | ||
318 | } | ||
319 | |||
320 | static int is_dr_dn(struct device_node *dn, int **indexes, int **names, | ||
321 | int **types, int **power_domains, int **my_drc_index) | ||
322 | { | ||
323 | int rc; | ||
324 | |||
325 | *my_drc_index = (int *) get_property(dn, "ibm,my-drc-index", NULL); | ||
326 | if(!*my_drc_index) | ||
327 | return (0); | ||
328 | |||
329 | if (!dn->parent) | ||
330 | return (0); | ||
331 | |||
332 | rc = get_children_props(dn->parent, indexes, names, types, | ||
333 | power_domains); | ||
334 | return (rc >= 0); | ||
335 | } | ||
336 | |||
337 | static inline int is_vdevice_root(struct device_node *dn) | ||
338 | { | ||
339 | return !strcmp(dn->name, "vdevice"); | ||
340 | } | ||
341 | |||
342 | int is_dlpar_type(const char *type_str) | ||
343 | { | ||
344 | /* Only register DLPAR-capable nodes of drc-type PHB or SLOT */ | ||
345 | return (!strcmp(type_str, "PHB") || !strcmp(type_str, "SLOT")); | ||
346 | } | ||
347 | |||
348 | /**************************************************************** | ||
349 | * rpaphp not only registers PCI hotplug slots(HOTPLUG), | ||
350 | * but also logical DR slots(EMBEDDED). | ||
351 | * HOTPLUG slot: An adapter can be physically added/removed. | ||
352 | * EMBEDDED slot: An adapter can be logically removed/added | ||
353 | * from/to a partition with the slot. | ||
354 | ***************************************************************/ | ||
355 | int rpaphp_add_slot(struct device_node *dn) | ||
356 | { | ||
357 | struct slot *slot; | ||
358 | int retval = 0; | ||
359 | int i, *my_drc_index, slot_type; | ||
360 | int *indexes, *names, *types, *power_domains; | ||
361 | char *name, *type; | ||
362 | |||
363 | dbg("Entry %s: dn->full_name=%s\n", __FUNCTION__, dn->full_name); | ||
364 | |||
365 | if (dn->parent && is_vdevice_root(dn->parent)) { | ||
366 | /* register a VIO device */ | ||
367 | retval = register_vio_slot(dn); | ||
368 | goto exit; | ||
369 | } | ||
370 | |||
371 | /* register PCI devices */ | ||
372 | if (dn->name != 0 && strcmp(dn->name, "pci") == 0) { | ||
373 | if (is_php_dn(dn, &indexes, &names, &types, &power_domains)) | ||
374 | slot_type = HOTPLUG; | ||
375 | else if (is_dr_dn(dn, &indexes, &names, &types, &power_domains, &my_drc_index)) | ||
376 | slot_type = EMBEDDED; | ||
377 | else goto exit; | ||
378 | |||
379 | name = (char *) &names[1]; | ||
380 | type = (char *) &types[1]; | ||
381 | for (i = 0; i < indexes[0]; i++, | ||
382 | name += (strlen(name) + 1), type += (strlen(type) + 1)) { | ||
383 | |||
384 | if (slot_type == HOTPLUG || | ||
385 | (slot_type == EMBEDDED && | ||
386 | indexes[i + 1] == my_drc_index[0] && | ||
387 | is_dlpar_type(type))) { | ||
388 | if (!(slot = alloc_slot_struct(dn, indexes[i + 1], name, | ||
389 | power_domains[i + 1]))) { | ||
390 | retval = -ENOMEM; | ||
391 | goto exit; | ||
392 | } | ||
393 | if (!strcmp(type, "PHB")) | ||
394 | slot->type = PHB; | ||
395 | else if (slot_type == EMBEDDED) | ||
396 | slot->type = EMBEDDED; | ||
397 | else | ||
398 | slot->type = simple_strtoul(type, NULL, 10); | ||
399 | |||
400 | dbg(" Found drc-index:0x%x drc-name:%s drc-type:%s\n", | ||
401 | indexes[i + 1], name, type); | ||
402 | |||
403 | retval = register_pci_slot(slot); | ||
404 | if (slot_type == EMBEDDED) | ||
405 | goto exit; | ||
406 | } | ||
407 | } | ||
408 | } | ||
409 | exit: | ||
410 | dbg("%s - Exit: num_slots=%d rc[%d]\n", | ||
411 | __FUNCTION__, num_slots, retval); | ||
412 | return retval; | ||
413 | } | ||
414 | |||
415 | /* | ||
416 | * init_slots - initialize 'struct slot' structures for each slot | ||
417 | * | ||
418 | */ | ||
419 | static void init_slots(void) | ||
420 | { | ||
421 | struct device_node *dn; | ||
422 | |||
423 | for (dn = find_all_nodes(); dn; dn = dn->next) | ||
424 | rpaphp_add_slot(dn); | ||
425 | } | ||
426 | |||
427 | static int __init init_rpa(void) | ||
428 | { | ||
429 | |||
430 | init_MUTEX(&rpaphp_sem); | ||
431 | |||
432 | /* initialize internal data structure etc. */ | ||
433 | init_slots(); | ||
434 | if (!num_slots) | ||
435 | return -ENODEV; | ||
436 | |||
437 | return 0; | ||
438 | } | ||
439 | |||
440 | static void __exit cleanup_slots(void) | ||
441 | { | ||
442 | struct list_head *tmp, *n; | ||
443 | struct slot *slot; | ||
444 | |||
445 | /* | ||
446 | * Unregister all of our slots with the pci_hotplug subsystem, | ||
447 | * and free up all memory that we had allocated. | ||
448 | * memory will be freed in release_slot callback. | ||
449 | */ | ||
450 | |||
451 | list_for_each_safe(tmp, n, &rpaphp_slot_head) { | ||
452 | slot = list_entry(tmp, struct slot, rpaphp_slot_list); | ||
453 | list_del(&slot->rpaphp_slot_list); | ||
454 | pci_hp_deregister(slot->hotplug_slot); | ||
455 | } | ||
456 | return; | ||
457 | } | ||
458 | |||
459 | static int __init rpaphp_init(void) | ||
460 | { | ||
461 | info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); | ||
462 | |||
463 | /* read all the PRA info from the system */ | ||
464 | return init_rpa(); | ||
465 | } | ||
466 | |||
467 | static void __exit rpaphp_exit(void) | ||
468 | { | ||
469 | cleanup_slots(); | ||
470 | } | ||
471 | |||
472 | static int enable_slot(struct hotplug_slot *hotplug_slot) | ||
473 | { | ||
474 | int retval = 0; | ||
475 | struct slot *slot = (struct slot *)hotplug_slot->private; | ||
476 | |||
477 | if (slot->state == CONFIGURED) { | ||
478 | dbg("%s: %s is already enabled\n", __FUNCTION__, slot->name); | ||
479 | goto exit; | ||
480 | } | ||
481 | |||
482 | dbg("ENABLING SLOT %s\n", slot->name); | ||
483 | down(&rpaphp_sem); | ||
484 | switch (slot->dev_type) { | ||
485 | case PCI_DEV: | ||
486 | retval = rpaphp_enable_pci_slot(slot); | ||
487 | break; | ||
488 | case VIO_DEV: | ||
489 | retval = rpaphp_enable_vio_slot(slot); | ||
490 | break; | ||
491 | default: | ||
492 | retval = -EINVAL; | ||
493 | } | ||
494 | up(&rpaphp_sem); | ||
495 | exit: | ||
496 | dbg("%s - Exit: rc[%d]\n", __FUNCTION__, retval); | ||
497 | return retval; | ||
498 | } | ||
499 | |||
500 | static int disable_slot(struct hotplug_slot *hotplug_slot) | ||
501 | { | ||
502 | int retval = -EINVAL; | ||
503 | struct slot *slot = (struct slot *)hotplug_slot->private; | ||
504 | |||
505 | dbg("%s - Entry: slot[%s]\n", __FUNCTION__, slot->name); | ||
506 | |||
507 | if (slot->state == NOT_CONFIGURED) { | ||
508 | dbg("%s: %s is already disabled\n", __FUNCTION__, slot->name); | ||
509 | goto exit; | ||
510 | } | ||
511 | |||
512 | dbg("DISABLING SLOT %s\n", slot->name); | ||
513 | down(&rpaphp_sem); | ||
514 | switch (slot->dev_type) { | ||
515 | case PCI_DEV: | ||
516 | retval = rpaphp_unconfig_pci_adapter(slot); | ||
517 | break; | ||
518 | case VIO_DEV: | ||
519 | retval = rpaphp_unconfig_vio_adapter(slot); | ||
520 | break; | ||
521 | default: | ||
522 | retval = -ENODEV; | ||
523 | } | ||
524 | up(&rpaphp_sem); | ||
525 | exit: | ||
526 | dbg("%s - Exit: rc[%d]\n", __FUNCTION__, retval); | ||
527 | return retval; | ||
528 | } | ||
529 | |||
530 | module_init(rpaphp_init); | ||
531 | module_exit(rpaphp_exit); | ||
532 | |||
533 | EXPORT_SYMBOL_GPL(rpaphp_add_slot); | ||
534 | EXPORT_SYMBOL_GPL(rpaphp_remove_slot); | ||
535 | EXPORT_SYMBOL_GPL(rpaphp_slot_head); | ||
536 | EXPORT_SYMBOL_GPL(rpaphp_get_drc_props); | ||
diff --git a/drivers/pci/hotplug/rpaphp_pci.c b/drivers/pci/hotplug/rpaphp_pci.c new file mode 100644 index 000000000000..d8305a935aab --- /dev/null +++ b/drivers/pci/hotplug/rpaphp_pci.c | |||
@@ -0,0 +1,538 @@ | |||
1 | /* | ||
2 | * PCI Hot Plug Controller Driver for RPA-compliant PPC64 platform. | ||
3 | * Copyright (C) 2003 Linda Xie <lxie@us.ibm.com> | ||
4 | * | ||
5 | * All rights reserved. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or (at | ||
10 | * your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, but | ||
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
15 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
16 | * details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
21 | * | ||
22 | * Send feedback to <lxie@us.ibm.com> | ||
23 | * | ||
24 | */ | ||
25 | #include <linux/pci.h> | ||
26 | #include <asm/pci-bridge.h> | ||
27 | #include <asm/rtas.h> | ||
28 | #include <asm/machdep.h> | ||
29 | #include "../pci.h" /* for pci_add_new_bus */ | ||
30 | |||
31 | #include "rpaphp.h" | ||
32 | |||
33 | struct pci_dev *rpaphp_find_pci_dev(struct device_node *dn) | ||
34 | { | ||
35 | struct pci_dev *dev = NULL; | ||
36 | char bus_id[BUS_ID_SIZE]; | ||
37 | |||
38 | sprintf(bus_id, "%04x:%02x:%02x.%d", dn->phb->global_number, | ||
39 | dn->busno, PCI_SLOT(dn->devfn), PCI_FUNC(dn->devfn)); | ||
40 | for_each_pci_dev(dev) { | ||
41 | if (!strcmp(pci_name(dev), bus_id)) { | ||
42 | break; | ||
43 | } | ||
44 | } | ||
45 | return dev; | ||
46 | } | ||
47 | |||
48 | EXPORT_SYMBOL_GPL(rpaphp_find_pci_dev); | ||
49 | |||
50 | int rpaphp_claim_resource(struct pci_dev *dev, int resource) | ||
51 | { | ||
52 | struct resource *res = &dev->resource[resource]; | ||
53 | struct resource *root = pci_find_parent_resource(dev, res); | ||
54 | char *dtype = resource < PCI_BRIDGE_RESOURCES ? "device" : "bridge"; | ||
55 | int err = -EINVAL; | ||
56 | |||
57 | if (root != NULL) { | ||
58 | err = request_resource(root, res); | ||
59 | } | ||
60 | |||
61 | if (err) { | ||
62 | err("PCI: %s region %d of %s %s [%lx:%lx]\n", | ||
63 | root ? "Address space collision on" : | ||
64 | "No parent found for", | ||
65 | resource, dtype, pci_name(dev), res->start, res->end); | ||
66 | } | ||
67 | return err; | ||
68 | } | ||
69 | |||
70 | EXPORT_SYMBOL_GPL(rpaphp_claim_resource); | ||
71 | |||
72 | static struct pci_dev *rpaphp_find_bridge_pdev(struct slot *slot) | ||
73 | { | ||
74 | return rpaphp_find_pci_dev(slot->dn); | ||
75 | } | ||
76 | |||
77 | static int rpaphp_get_sensor_state(struct slot *slot, int *state) | ||
78 | { | ||
79 | int rc; | ||
80 | int setlevel; | ||
81 | |||
82 | rc = rtas_get_sensor(DR_ENTITY_SENSE, slot->index, state); | ||
83 | |||
84 | if (rc < 0) { | ||
85 | if (rc == -EFAULT || rc == -EEXIST) { | ||
86 | dbg("%s: slot must be power up to get sensor-state\n", | ||
87 | __FUNCTION__); | ||
88 | |||
89 | /* some slots have to be powered up | ||
90 | * before get-sensor will succeed. | ||
91 | */ | ||
92 | rc = rtas_set_power_level(slot->power_domain, POWER_ON, | ||
93 | &setlevel); | ||
94 | if (rc < 0) { | ||
95 | dbg("%s: power on slot[%s] failed rc=%d.\n", | ||
96 | __FUNCTION__, slot->name, rc); | ||
97 | } else { | ||
98 | rc = rtas_get_sensor(DR_ENTITY_SENSE, | ||
99 | slot->index, state); | ||
100 | } | ||
101 | } else if (rc == -ENODEV) | ||
102 | info("%s: slot is unusable\n", __FUNCTION__); | ||
103 | else | ||
104 | err("%s failed to get sensor state\n", __FUNCTION__); | ||
105 | } | ||
106 | return rc; | ||
107 | } | ||
108 | |||
109 | /** | ||
110 | * get_pci_adapter_status - get the status of a slot | ||
111 | * | ||
112 | * 0-- slot is empty | ||
113 | * 1-- adapter is configured | ||
114 | * 2-- adapter is not configured | ||
115 | * 3-- not valid | ||
116 | */ | ||
117 | int rpaphp_get_pci_adapter_status(struct slot *slot, int is_init, u8 * value) | ||
118 | { | ||
119 | int state, rc; | ||
120 | struct device_node *child_dn; | ||
121 | struct pci_dev *child_dev = NULL; | ||
122 | |||
123 | *value = NOT_VALID; | ||
124 | rc = rpaphp_get_sensor_state(slot, &state); | ||
125 | if (rc) | ||
126 | goto exit; | ||
127 | |||
128 | if ((state == EMPTY) || (slot->type == PHB)) { | ||
129 | dbg("slot is empty\n"); | ||
130 | *value = EMPTY; | ||
131 | } | ||
132 | else if (state == PRESENT) { | ||
133 | if (!is_init) { | ||
134 | /* at run-time slot->state can be changed by */ | ||
135 | /* config/unconfig adapter */ | ||
136 | *value = slot->state; | ||
137 | } else { | ||
138 | child_dn = slot->dn->child; | ||
139 | if (child_dn) | ||
140 | child_dev = rpaphp_find_pci_dev(child_dn); | ||
141 | |||
142 | if (child_dev) | ||
143 | *value = CONFIGURED; | ||
144 | else if (!child_dn) | ||
145 | dbg("%s: %s is not valid OFDT node\n", | ||
146 | __FUNCTION__, slot->dn->full_name); | ||
147 | else { | ||
148 | err("%s: can't find pdev of adapter in slot[%s]\n", | ||
149 | __FUNCTION__, slot->dn->full_name); | ||
150 | *value = NOT_CONFIGURED; | ||
151 | } | ||
152 | } | ||
153 | } | ||
154 | exit: | ||
155 | return rc; | ||
156 | } | ||
157 | |||
158 | /* Must be called before pci_bus_add_devices */ | ||
159 | static void | ||
160 | rpaphp_fixup_new_pci_devices(struct pci_bus *bus, int fix_bus) | ||
161 | { | ||
162 | struct pci_dev *dev; | ||
163 | |||
164 | list_for_each_entry(dev, &bus->devices, bus_list) { | ||
165 | /* | ||
166 | * Skip already-present devices (which are on the | ||
167 | * global device list.) | ||
168 | */ | ||
169 | if (list_empty(&dev->global_list)) { | ||
170 | int i; | ||
171 | |||
172 | /* Need to setup IOMMU tables */ | ||
173 | ppc_md.iommu_dev_setup(dev); | ||
174 | |||
175 | if(fix_bus) | ||
176 | pcibios_fixup_device_resources(dev, bus); | ||
177 | pci_read_irq_line(dev); | ||
178 | for (i = 0; i < PCI_NUM_RESOURCES; i++) { | ||
179 | struct resource *r = &dev->resource[i]; | ||
180 | |||
181 | if (r->parent || !r->start || !r->flags) | ||
182 | continue; | ||
183 | rpaphp_claim_resource(dev, i); | ||
184 | } | ||
185 | } | ||
186 | } | ||
187 | } | ||
188 | |||
189 | static int rpaphp_pci_config_bridge(struct pci_dev *dev); | ||
190 | |||
191 | /***************************************************************************** | ||
192 | rpaphp_pci_config_slot() will configure all devices under the | ||
193 | given slot->dn and return the the first pci_dev. | ||
194 | *****************************************************************************/ | ||
195 | static struct pci_dev * | ||
196 | rpaphp_pci_config_slot(struct device_node *dn, struct pci_bus *bus) | ||
197 | { | ||
198 | struct device_node *eads_first_child = dn->child; | ||
199 | struct pci_dev *dev = NULL; | ||
200 | int num; | ||
201 | |||
202 | dbg("Enter %s: dn=%s bus=%s\n", __FUNCTION__, dn->full_name, bus->name); | ||
203 | |||
204 | if (eads_first_child) { | ||
205 | /* pci_scan_slot should find all children of EADs */ | ||
206 | num = pci_scan_slot(bus, PCI_DEVFN(PCI_SLOT(eads_first_child->devfn), 0)); | ||
207 | if (num) { | ||
208 | rpaphp_fixup_new_pci_devices(bus, 1); | ||
209 | pci_bus_add_devices(bus); | ||
210 | } | ||
211 | dev = rpaphp_find_pci_dev(eads_first_child); | ||
212 | if (!dev) { | ||
213 | err("No new device found\n"); | ||
214 | return NULL; | ||
215 | } | ||
216 | if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) | ||
217 | rpaphp_pci_config_bridge(dev); | ||
218 | } | ||
219 | return dev; | ||
220 | } | ||
221 | |||
222 | static int rpaphp_pci_config_bridge(struct pci_dev *dev) | ||
223 | { | ||
224 | u8 sec_busno; | ||
225 | struct pci_bus *child_bus; | ||
226 | struct pci_dev *child_dev; | ||
227 | |||
228 | dbg("Enter %s: BRIDGE dev=%s\n", __FUNCTION__, pci_name(dev)); | ||
229 | |||
230 | /* get busno of downstream bus */ | ||
231 | pci_read_config_byte(dev, PCI_SECONDARY_BUS, &sec_busno); | ||
232 | |||
233 | /* add to children of PCI bridge dev->bus */ | ||
234 | child_bus = pci_add_new_bus(dev->bus, dev, sec_busno); | ||
235 | if (!child_bus) { | ||
236 | err("%s: could not add second bus\n", __FUNCTION__); | ||
237 | return -EIO; | ||
238 | } | ||
239 | sprintf(child_bus->name, "PCI Bus #%02x", child_bus->number); | ||
240 | /* do pci_scan_child_bus */ | ||
241 | pci_scan_child_bus(child_bus); | ||
242 | |||
243 | list_for_each_entry(child_dev, &child_bus->devices, bus_list) { | ||
244 | eeh_add_device_late(child_dev); | ||
245 | } | ||
246 | |||
247 | /* fixup new pci devices without touching bus struct */ | ||
248 | rpaphp_fixup_new_pci_devices(child_bus, 0); | ||
249 | |||
250 | /* Make the discovered devices available */ | ||
251 | pci_bus_add_devices(child_bus); | ||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | static void enable_eeh(struct device_node *dn) | ||
256 | { | ||
257 | struct device_node *sib; | ||
258 | |||
259 | for (sib = dn->child; sib; sib = sib->sibling) | ||
260 | enable_eeh(sib); | ||
261 | eeh_add_device_early(dn); | ||
262 | return; | ||
263 | |||
264 | } | ||
265 | |||
266 | static void print_slot_pci_funcs(struct slot *slot) | ||
267 | { | ||
268 | struct pci_dev *dev; | ||
269 | |||
270 | if (slot->dev_type == PCI_DEV) { | ||
271 | dbg("%s: pci_devs of slot[%s]\n", __FUNCTION__, slot->name); | ||
272 | list_for_each_entry (dev, slot->dev.pci_devs, bus_list) | ||
273 | dbg("\t%s\n", pci_name(dev)); | ||
274 | } | ||
275 | return; | ||
276 | } | ||
277 | |||
278 | static int rpaphp_config_pci_adapter(struct slot *slot) | ||
279 | { | ||
280 | struct pci_bus *pci_bus; | ||
281 | struct pci_dev *dev; | ||
282 | int rc = -ENODEV; | ||
283 | |||
284 | dbg("Entry %s: slot[%s]\n", __FUNCTION__, slot->name); | ||
285 | |||
286 | if (slot->bridge) { | ||
287 | |||
288 | pci_bus = slot->bridge->subordinate; | ||
289 | if (!pci_bus) { | ||
290 | err("%s: can't find bus structure\n", __FUNCTION__); | ||
291 | goto exit; | ||
292 | } | ||
293 | enable_eeh(slot->dn); | ||
294 | dev = rpaphp_pci_config_slot(slot->dn, pci_bus); | ||
295 | if (!dev) { | ||
296 | err("%s: can't find any devices.\n", __FUNCTION__); | ||
297 | goto exit; | ||
298 | } | ||
299 | print_slot_pci_funcs(slot); | ||
300 | rc = 0; | ||
301 | } else { | ||
302 | /* slot is not enabled */ | ||
303 | err("slot doesn't have pci_dev structure\n"); | ||
304 | } | ||
305 | exit: | ||
306 | dbg("Exit %s: rc=%d\n", __FUNCTION__, rc); | ||
307 | return rc; | ||
308 | } | ||
309 | |||
310 | static void rpaphp_eeh_remove_bus_device(struct pci_dev *dev) | ||
311 | { | ||
312 | eeh_remove_device(dev); | ||
313 | if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { | ||
314 | struct pci_bus *bus = dev->subordinate; | ||
315 | struct list_head *ln; | ||
316 | if (!bus) | ||
317 | return; | ||
318 | for (ln = bus->devices.next; ln != &bus->devices; ln = ln->next) { | ||
319 | struct pci_dev *pdev = pci_dev_b(ln); | ||
320 | if (pdev) | ||
321 | rpaphp_eeh_remove_bus_device(pdev); | ||
322 | } | ||
323 | |||
324 | } | ||
325 | return; | ||
326 | } | ||
327 | |||
328 | int rpaphp_unconfig_pci_adapter(struct slot *slot) | ||
329 | { | ||
330 | struct pci_dev *dev; | ||
331 | int retval = 0; | ||
332 | |||
333 | list_for_each_entry(dev, slot->dev.pci_devs, bus_list) | ||
334 | rpaphp_eeh_remove_bus_device(dev); | ||
335 | |||
336 | pci_remove_behind_bridge(slot->bridge); | ||
337 | slot->state = NOT_CONFIGURED; | ||
338 | info("%s: devices in slot[%s] unconfigured.\n", __FUNCTION__, | ||
339 | slot->name); | ||
340 | return retval; | ||
341 | } | ||
342 | |||
343 | static int setup_pci_hotplug_slot_info(struct slot *slot) | ||
344 | { | ||
345 | dbg("%s Initilize the PCI slot's hotplug->info structure ...\n", | ||
346 | __FUNCTION__); | ||
347 | rpaphp_get_power_status(slot, &slot->hotplug_slot->info->power_status); | ||
348 | rpaphp_get_pci_adapter_status(slot, 1, | ||
349 | &slot->hotplug_slot->info-> | ||
350 | adapter_status); | ||
351 | if (slot->hotplug_slot->info->adapter_status == NOT_VALID) { | ||
352 | err("%s: NOT_VALID: skip dn->full_name=%s\n", | ||
353 | __FUNCTION__, slot->dn->full_name); | ||
354 | return -EINVAL; | ||
355 | } | ||
356 | return 0; | ||
357 | } | ||
358 | |||
359 | static int set_phb_slot_name(struct slot *slot) | ||
360 | { | ||
361 | struct device_node *dn; | ||
362 | struct pci_controller *phb; | ||
363 | struct pci_bus *bus; | ||
364 | |||
365 | dn = slot->dn; | ||
366 | if (!dn) { | ||
367 | return -EINVAL; | ||
368 | } | ||
369 | phb = dn->phb; | ||
370 | if (!phb) { | ||
371 | return -EINVAL; | ||
372 | } | ||
373 | bus = phb->bus; | ||
374 | if (!bus) { | ||
375 | return -EINVAL; | ||
376 | } | ||
377 | |||
378 | sprintf(slot->name, "%04x:%02x:%02x.%x", pci_domain_nr(bus), | ||
379 | bus->number, 0, 0); | ||
380 | return 0; | ||
381 | } | ||
382 | |||
383 | static int setup_pci_slot(struct slot *slot) | ||
384 | { | ||
385 | struct pci_bus *bus; | ||
386 | int rc; | ||
387 | |||
388 | if (slot->type == PHB) { | ||
389 | rc = set_phb_slot_name(slot); | ||
390 | if (rc < 0) { | ||
391 | err("%s: failed to set phb slot name\n", __FUNCTION__); | ||
392 | goto exit_rc; | ||
393 | } | ||
394 | } else { | ||
395 | slot->bridge = rpaphp_find_bridge_pdev(slot); | ||
396 | if (!slot->bridge) { | ||
397 | /* slot being added doesn't have pci_dev yet */ | ||
398 | err("%s: no pci_dev for bridge dn %s\n", | ||
399 | __FUNCTION__, slot->name); | ||
400 | goto exit_rc; | ||
401 | } | ||
402 | |||
403 | bus = slot->bridge->subordinate; | ||
404 | if (!bus) | ||
405 | goto exit_rc; | ||
406 | slot->dev.pci_devs = &bus->devices; | ||
407 | |||
408 | dbg("%s set slot->name to %s\n", __FUNCTION__, | ||
409 | pci_name(slot->bridge)); | ||
410 | strcpy(slot->name, pci_name(slot->bridge)); | ||
411 | } | ||
412 | |||
413 | /* find slot's pci_dev if it's not empty */ | ||
414 | if (slot->hotplug_slot->info->adapter_status == EMPTY) { | ||
415 | slot->state = EMPTY; /* slot is empty */ | ||
416 | } else { | ||
417 | /* slot is occupied */ | ||
418 | if (!(slot->dn->child)) { | ||
419 | /* non-empty slot has to have child */ | ||
420 | err("%s: slot[%s]'s device_node doesn't have child for adapter\n", | ||
421 | __FUNCTION__, slot->name); | ||
422 | goto exit_rc; | ||
423 | } | ||
424 | |||
425 | if (slot->hotplug_slot->info->adapter_status == NOT_CONFIGURED) { | ||
426 | dbg("%s CONFIGURING pci adapter in slot[%s]\n", | ||
427 | __FUNCTION__, slot->name); | ||
428 | if (rpaphp_config_pci_adapter(slot)) { | ||
429 | err("%s: CONFIG pci adapter failed\n", __FUNCTION__); | ||
430 | goto exit_rc; | ||
431 | } | ||
432 | |||
433 | } else if (slot->hotplug_slot->info->adapter_status != CONFIGURED) { | ||
434 | err("%s: slot[%s]'s adapter_status is NOT_VALID.\n", | ||
435 | __FUNCTION__, slot->name); | ||
436 | goto exit_rc; | ||
437 | } | ||
438 | print_slot_pci_funcs(slot); | ||
439 | if (!list_empty(slot->dev.pci_devs)) { | ||
440 | slot->state = CONFIGURED; | ||
441 | } else { | ||
442 | /* DLPAR add as opposed to | ||
443 | * boot time */ | ||
444 | slot->state = NOT_CONFIGURED; | ||
445 | } | ||
446 | } | ||
447 | return 0; | ||
448 | exit_rc: | ||
449 | dealloc_slot_struct(slot); | ||
450 | return -EINVAL; | ||
451 | } | ||
452 | |||
453 | int register_pci_slot(struct slot *slot) | ||
454 | { | ||
455 | int rc = -EINVAL; | ||
456 | |||
457 | slot->dev_type = PCI_DEV; | ||
458 | if ((slot->type == EMBEDDED) || (slot->type == PHB)) | ||
459 | slot->removable = 0; | ||
460 | else | ||
461 | slot->removable = 1; | ||
462 | if (setup_pci_hotplug_slot_info(slot)) | ||
463 | goto exit_rc; | ||
464 | if (setup_pci_slot(slot)) | ||
465 | goto exit_rc; | ||
466 | rc = register_slot(slot); | ||
467 | exit_rc: | ||
468 | return rc; | ||
469 | } | ||
470 | |||
471 | int rpaphp_enable_pci_slot(struct slot *slot) | ||
472 | { | ||
473 | int retval = 0, state; | ||
474 | |||
475 | retval = rpaphp_get_sensor_state(slot, &state); | ||
476 | if (retval) | ||
477 | goto exit; | ||
478 | dbg("%s: sensor state[%d]\n", __FUNCTION__, state); | ||
479 | /* if slot is not empty, enable the adapter */ | ||
480 | if (state == PRESENT) { | ||
481 | dbg("%s : slot[%s] is occupied.\n", __FUNCTION__, slot->name); | ||
482 | retval = rpaphp_config_pci_adapter(slot); | ||
483 | if (!retval) { | ||
484 | slot->state = CONFIGURED; | ||
485 | dbg("%s: PCI devices in slot[%s] has been configured\n", | ||
486 | __FUNCTION__, slot->name); | ||
487 | } else { | ||
488 | slot->state = NOT_CONFIGURED; | ||
489 | dbg("%s: no pci_dev struct for adapter in slot[%s]\n", | ||
490 | __FUNCTION__, slot->name); | ||
491 | } | ||
492 | } else if (state == EMPTY) { | ||
493 | dbg("%s : slot[%s] is empty\n", __FUNCTION__, slot->name); | ||
494 | slot->state = EMPTY; | ||
495 | } else { | ||
496 | err("%s: slot[%s] is in invalid state\n", __FUNCTION__, | ||
497 | slot->name); | ||
498 | slot->state = NOT_VALID; | ||
499 | retval = -EINVAL; | ||
500 | } | ||
501 | exit: | ||
502 | dbg("%s - Exit: rc[%d]\n", __FUNCTION__, retval); | ||
503 | return retval; | ||
504 | } | ||
505 | |||
506 | struct hotplug_slot *rpaphp_find_hotplug_slot(struct pci_dev *dev) | ||
507 | { | ||
508 | struct list_head *tmp, *n; | ||
509 | struct slot *slot; | ||
510 | |||
511 | list_for_each_safe(tmp, n, &rpaphp_slot_head) { | ||
512 | struct pci_bus *bus; | ||
513 | struct list_head *ln; | ||
514 | |||
515 | slot = list_entry(tmp, struct slot, rpaphp_slot_list); | ||
516 | if (slot->bridge == NULL) { | ||
517 | if (slot->dev_type == PCI_DEV) { | ||
518 | printk(KERN_WARNING "PCI slot missing bridge %s %s \n", | ||
519 | slot->name, slot->location); | ||
520 | } | ||
521 | continue; | ||
522 | } | ||
523 | |||
524 | bus = slot->bridge->subordinate; | ||
525 | if (!bus) { | ||
526 | continue; /* should never happen? */ | ||
527 | } | ||
528 | for (ln = bus->devices.next; ln != &bus->devices; ln = ln->next) { | ||
529 | struct pci_dev *pdev = pci_dev_b(ln); | ||
530 | if (pdev == dev) | ||
531 | return slot->hotplug_slot; | ||
532 | } | ||
533 | } | ||
534 | |||
535 | return NULL; | ||
536 | } | ||
537 | |||
538 | EXPORT_SYMBOL_GPL(rpaphp_find_hotplug_slot); | ||
diff --git a/drivers/pci/hotplug/rpaphp_slot.c b/drivers/pci/hotplug/rpaphp_slot.c new file mode 100644 index 000000000000..ff2cbf0652d8 --- /dev/null +++ b/drivers/pci/hotplug/rpaphp_slot.c | |||
@@ -0,0 +1,267 @@ | |||
1 | /* | ||
2 | * RPA Virtual I/O device functions | ||
3 | * Copyright (C) 2004 Linda Xie <lxie@us.ibm.com> | ||
4 | * | ||
5 | * All rights reserved. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or (at | ||
10 | * your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, but | ||
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
15 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
16 | * details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
21 | * | ||
22 | * Send feedback to <lxie@us.ibm.com> | ||
23 | * | ||
24 | */ | ||
25 | #include <linux/kernel.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/kobject.h> | ||
28 | #include <linux/sysfs.h> | ||
29 | #include <linux/pci.h> | ||
30 | #include <asm/rtas.h> | ||
31 | #include "rpaphp.h" | ||
32 | |||
33 | static ssize_t removable_read_file (struct hotplug_slot *php_slot, char *buf) | ||
34 | { | ||
35 | u8 value; | ||
36 | int retval = -ENOENT; | ||
37 | struct slot *slot = (struct slot *)php_slot->private; | ||
38 | |||
39 | if (!slot) | ||
40 | return retval; | ||
41 | |||
42 | value = slot->removable; | ||
43 | retval = sprintf (buf, "%d\n", value); | ||
44 | return retval; | ||
45 | } | ||
46 | |||
47 | static struct hotplug_slot_attribute hotplug_slot_attr_removable = { | ||
48 | .attr = {.name = "phy_removable", .mode = S_IFREG | S_IRUGO}, | ||
49 | .show = removable_read_file, | ||
50 | }; | ||
51 | |||
52 | static void rpaphp_sysfs_add_attr_removable (struct hotplug_slot *slot) | ||
53 | { | ||
54 | sysfs_create_file(&slot->kobj, &hotplug_slot_attr_removable.attr); | ||
55 | } | ||
56 | |||
57 | static void rpaphp_sysfs_remove_attr_removable (struct hotplug_slot *slot) | ||
58 | { | ||
59 | sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_removable.attr); | ||
60 | } | ||
61 | |||
62 | static ssize_t location_read_file (struct hotplug_slot *php_slot, char *buf) | ||
63 | { | ||
64 | char *value; | ||
65 | int retval = -ENOENT; | ||
66 | struct slot *slot = (struct slot *)php_slot->private; | ||
67 | |||
68 | if (!slot) | ||
69 | return retval; | ||
70 | |||
71 | value = slot->location; | ||
72 | retval = sprintf (buf, "%s\n", value); | ||
73 | return retval; | ||
74 | } | ||
75 | |||
76 | static struct hotplug_slot_attribute hotplug_slot_attr_location = { | ||
77 | .attr = {.name = "phy_location", .mode = S_IFREG | S_IRUGO}, | ||
78 | .show = location_read_file, | ||
79 | }; | ||
80 | |||
81 | static void rpaphp_sysfs_add_attr_location (struct hotplug_slot *slot) | ||
82 | { | ||
83 | sysfs_create_file(&slot->kobj, &hotplug_slot_attr_location.attr); | ||
84 | } | ||
85 | |||
86 | static void rpaphp_sysfs_remove_attr_location (struct hotplug_slot *slot) | ||
87 | { | ||
88 | sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_location.attr); | ||
89 | } | ||
90 | |||
91 | /* free up the memory used by a slot */ | ||
92 | static void rpaphp_release_slot(struct hotplug_slot *hotplug_slot) | ||
93 | { | ||
94 | struct slot *slot = (struct slot *) hotplug_slot->private; | ||
95 | |||
96 | dealloc_slot_struct(slot); | ||
97 | } | ||
98 | |||
99 | void dealloc_slot_struct(struct slot *slot) | ||
100 | { | ||
101 | kfree(slot->hotplug_slot->info); | ||
102 | kfree(slot->hotplug_slot->name); | ||
103 | kfree(slot->hotplug_slot); | ||
104 | kfree(slot); | ||
105 | return; | ||
106 | } | ||
107 | |||
108 | struct slot *alloc_slot_struct(struct device_node *dn, int drc_index, char *drc_name, | ||
109 | int power_domain) | ||
110 | { | ||
111 | struct slot *slot; | ||
112 | |||
113 | slot = kmalloc(sizeof (struct slot), GFP_KERNEL); | ||
114 | if (!slot) | ||
115 | goto error_nomem; | ||
116 | memset(slot, 0, sizeof (struct slot)); | ||
117 | slot->hotplug_slot = kmalloc(sizeof (struct hotplug_slot), GFP_KERNEL); | ||
118 | if (!slot->hotplug_slot) | ||
119 | goto error_slot; | ||
120 | memset(slot->hotplug_slot, 0, sizeof (struct hotplug_slot)); | ||
121 | slot->hotplug_slot->info = kmalloc(sizeof (struct hotplug_slot_info), | ||
122 | GFP_KERNEL); | ||
123 | if (!slot->hotplug_slot->info) | ||
124 | goto error_hpslot; | ||
125 | memset(slot->hotplug_slot->info, 0, sizeof (struct hotplug_slot_info)); | ||
126 | slot->hotplug_slot->name = kmalloc(BUS_ID_SIZE + 1, GFP_KERNEL); | ||
127 | if (!slot->hotplug_slot->name) | ||
128 | goto error_info; | ||
129 | slot->location = kmalloc(strlen(drc_name) + 1, GFP_KERNEL); | ||
130 | if (!slot->location) | ||
131 | goto error_name; | ||
132 | slot->name = slot->hotplug_slot->name; | ||
133 | slot->dn = dn; | ||
134 | slot->index = drc_index; | ||
135 | strcpy(slot->location, drc_name); | ||
136 | slot->power_domain = power_domain; | ||
137 | slot->hotplug_slot->private = slot; | ||
138 | slot->hotplug_slot->ops = &rpaphp_hotplug_slot_ops; | ||
139 | slot->hotplug_slot->release = &rpaphp_release_slot; | ||
140 | |||
141 | return (slot); | ||
142 | |||
143 | error_name: | ||
144 | kfree(slot->hotplug_slot->name); | ||
145 | error_info: | ||
146 | kfree(slot->hotplug_slot->info); | ||
147 | error_hpslot: | ||
148 | kfree(slot->hotplug_slot); | ||
149 | error_slot: | ||
150 | kfree(slot); | ||
151 | error_nomem: | ||
152 | return NULL; | ||
153 | } | ||
154 | |||
155 | static int is_registered(struct slot *slot) | ||
156 | { | ||
157 | struct slot *tmp_slot; | ||
158 | |||
159 | list_for_each_entry(tmp_slot, &rpaphp_slot_head, rpaphp_slot_list) { | ||
160 | if (!strcmp(tmp_slot->name, slot->name)) | ||
161 | return 1; | ||
162 | } | ||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | int deregister_slot(struct slot *slot) | ||
167 | { | ||
168 | int retval = 0; | ||
169 | struct hotplug_slot *php_slot = slot->hotplug_slot; | ||
170 | |||
171 | dbg("%s - Entry: deregistering slot=%s\n", | ||
172 | __FUNCTION__, slot->name); | ||
173 | |||
174 | list_del(&slot->rpaphp_slot_list); | ||
175 | |||
176 | /* remove "phy_location" file */ | ||
177 | rpaphp_sysfs_remove_attr_location(php_slot); | ||
178 | |||
179 | /* remove "phy_removable" file */ | ||
180 | rpaphp_sysfs_remove_attr_removable(php_slot); | ||
181 | |||
182 | retval = pci_hp_deregister(php_slot); | ||
183 | if (retval) | ||
184 | err("Problem unregistering a slot %s\n", slot->name); | ||
185 | else | ||
186 | num_slots--; | ||
187 | |||
188 | dbg("%s - Exit: rc[%d]\n", __FUNCTION__, retval); | ||
189 | return retval; | ||
190 | } | ||
191 | |||
192 | int register_slot(struct slot *slot) | ||
193 | { | ||
194 | int retval; | ||
195 | |||
196 | dbg("%s registering slot:path[%s] index[%x], name[%s] pdomain[%x] type[%d]\n", | ||
197 | __FUNCTION__, slot->dn->full_name, slot->index, slot->name, | ||
198 | slot->power_domain, slot->type); | ||
199 | /* should not try to register the same slot twice */ | ||
200 | if (is_registered(slot)) { /* should't be here */ | ||
201 | err("register_slot: slot[%s] is already registered\n", slot->name); | ||
202 | rpaphp_release_slot(slot->hotplug_slot); | ||
203 | return -EAGAIN; | ||
204 | } | ||
205 | retval = pci_hp_register(slot->hotplug_slot); | ||
206 | if (retval) { | ||
207 | err("pci_hp_register failed with error %d\n", retval); | ||
208 | rpaphp_release_slot(slot->hotplug_slot); | ||
209 | return retval; | ||
210 | } | ||
211 | |||
212 | /* create "phy_locatoin" file */ | ||
213 | rpaphp_sysfs_add_attr_location(slot->hotplug_slot); | ||
214 | |||
215 | /* create "phy_removable" file */ | ||
216 | rpaphp_sysfs_add_attr_removable(slot->hotplug_slot); | ||
217 | |||
218 | /* add slot to our internal list */ | ||
219 | dbg("%s adding slot[%s] to rpaphp_slot_list\n", | ||
220 | __FUNCTION__, slot->name); | ||
221 | |||
222 | list_add(&slot->rpaphp_slot_list, &rpaphp_slot_head); | ||
223 | |||
224 | if (slot->dev_type == VIO_DEV) | ||
225 | info("Slot [%s](VIO location=%s) registered\n", | ||
226 | slot->name, slot->location); | ||
227 | else | ||
228 | info("Slot [%s](PCI location=%s) registered\n", | ||
229 | slot->name, slot->location); | ||
230 | num_slots++; | ||
231 | return 0; | ||
232 | } | ||
233 | |||
234 | int rpaphp_get_power_status(struct slot *slot, u8 * value) | ||
235 | { | ||
236 | int rc = 0, level; | ||
237 | |||
238 | if (slot->type == HOTPLUG) { | ||
239 | rc = rtas_get_power_level(slot->power_domain, &level); | ||
240 | if (!rc) { | ||
241 | dbg("%s the power level of slot %s(pwd-domain:0x%x) is %d\n", | ||
242 | __FUNCTION__, slot->name, slot->power_domain, level); | ||
243 | *value = level; | ||
244 | } else | ||
245 | err("failed to get power-level for slot(%s), rc=0x%x\n", | ||
246 | slot->location, rc); | ||
247 | } else { | ||
248 | dbg("%s report POWER_ON for EMBEDDED or PHB slot %s\n", | ||
249 | __FUNCTION__, slot->location); | ||
250 | *value = (u8) POWER_ON; | ||
251 | } | ||
252 | |||
253 | return rc; | ||
254 | } | ||
255 | |||
256 | int rpaphp_set_attention_status(struct slot *slot, u8 status) | ||
257 | { | ||
258 | int rc; | ||
259 | |||
260 | /* status: LED_OFF or LED_ON */ | ||
261 | rc = rtas_set_indicator(DR_INDICATOR, slot->index, status); | ||
262 | if (rc < 0) | ||
263 | err("slot(name=%s location=%s index=0x%x) set attention-status(%d) failed! rc=0x%x\n", | ||
264 | slot->name, slot->location, slot->index, status, rc); | ||
265 | |||
266 | return rc; | ||
267 | } | ||
diff --git a/drivers/pci/hotplug/rpaphp_vio.c b/drivers/pci/hotplug/rpaphp_vio.c new file mode 100644 index 000000000000..74df6a305e64 --- /dev/null +++ b/drivers/pci/hotplug/rpaphp_vio.c | |||
@@ -0,0 +1,129 @@ | |||
1 | /* | ||
2 | * RPA Hot Plug Virtual I/O device functions | ||
3 | * Copyright (C) 2004 Linda Xie <lxie@us.ibm.com> | ||
4 | * | ||
5 | * All rights reserved. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or (at | ||
10 | * your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, but | ||
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
15 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
16 | * details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
21 | * | ||
22 | * Send feedback to <lxie@us.ibm.com> | ||
23 | * | ||
24 | */ | ||
25 | #include <asm/vio.h> | ||
26 | #include "rpaphp.h" | ||
27 | |||
28 | /* | ||
29 | * get_vio_adapter_status - get the status of a slot | ||
30 | * | ||
31 | * status: | ||
32 | * | ||
33 | * 1-- adapter is configured | ||
34 | * 2-- adapter is not configured | ||
35 | * 3-- not valid | ||
36 | */ | ||
37 | inline int rpaphp_get_vio_adapter_status(struct slot *slot, int is_init, u8 *value) | ||
38 | { | ||
39 | *value = slot->state; | ||
40 | return 0; | ||
41 | } | ||
42 | |||
43 | int rpaphp_unconfig_vio_adapter(struct slot *slot) | ||
44 | { | ||
45 | int retval = 0; | ||
46 | |||
47 | dbg("Entry %s: slot[%s]\n", __FUNCTION__, slot->name); | ||
48 | if (!slot->dev.vio_dev) { | ||
49 | info("%s: no VIOA in slot[%s]\n", __FUNCTION__, slot->name); | ||
50 | retval = -EINVAL; | ||
51 | goto exit; | ||
52 | } | ||
53 | /* remove the device from the vio core */ | ||
54 | vio_unregister_device(slot->dev.vio_dev); | ||
55 | slot->state = NOT_CONFIGURED; | ||
56 | info("%s: adapter in slot[%s] unconfigured.\n", __FUNCTION__, slot->name); | ||
57 | exit: | ||
58 | dbg("Exit %s, rc=0x%x\n", __FUNCTION__, retval); | ||
59 | return retval; | ||
60 | } | ||
61 | |||
62 | static int setup_vio_hotplug_slot_info(struct slot *slot) | ||
63 | { | ||
64 | slot->hotplug_slot->info->power_status = 1; | ||
65 | rpaphp_get_vio_adapter_status(slot, 1, | ||
66 | &slot->hotplug_slot->info->adapter_status); | ||
67 | return 0; | ||
68 | } | ||
69 | |||
70 | int register_vio_slot(struct device_node *dn) | ||
71 | { | ||
72 | u32 *index; | ||
73 | char *name; | ||
74 | int rc = -EINVAL; | ||
75 | struct slot *slot = NULL; | ||
76 | |||
77 | rc = rpaphp_get_drc_props(dn, NULL, &name, NULL, NULL); | ||
78 | if (rc < 0) | ||
79 | goto exit_rc; | ||
80 | index = (u32 *) get_property(dn, "ibm,my-drc-index", NULL); | ||
81 | if (!index) | ||
82 | goto exit_rc; | ||
83 | if (!(slot = alloc_slot_struct(dn, *index, name, 0))) { | ||
84 | rc = -ENOMEM; | ||
85 | goto exit_rc; | ||
86 | } | ||
87 | slot->dev_type = VIO_DEV; | ||
88 | slot->dev.vio_dev = vio_find_node(dn); | ||
89 | if (slot->dev.vio_dev) { | ||
90 | /* | ||
91 | * rpaphp is the only owner of vio devices and | ||
92 | * does not need extra reference taken by | ||
93 | * vio_find_node | ||
94 | */ | ||
95 | put_device(&slot->dev.vio_dev->dev); | ||
96 | } else | ||
97 | slot->dev.vio_dev = vio_register_device_node(dn); | ||
98 | if (slot->dev.vio_dev) | ||
99 | slot->state = CONFIGURED; | ||
100 | else | ||
101 | slot->state = NOT_CONFIGURED; | ||
102 | if (setup_vio_hotplug_slot_info(slot)) | ||
103 | goto exit_rc; | ||
104 | strcpy(slot->name, slot->dev.vio_dev->dev.bus_id); | ||
105 | info("%s: registered VIO device[name=%s vio_dev=%p]\n", | ||
106 | __FUNCTION__, slot->name, slot->dev.vio_dev); | ||
107 | rc = register_slot(slot); | ||
108 | exit_rc: | ||
109 | if (rc && slot) | ||
110 | dealloc_slot_struct(slot); | ||
111 | return (rc); | ||
112 | } | ||
113 | |||
114 | int rpaphp_enable_vio_slot(struct slot *slot) | ||
115 | { | ||
116 | int retval = 0; | ||
117 | |||
118 | if ((slot->dev.vio_dev = vio_register_device_node(slot->dn))) { | ||
119 | info("%s: VIO adapter %s in slot[%s] has been configured\n", | ||
120 | __FUNCTION__, slot->dn->name, slot->name); | ||
121 | slot->state = CONFIGURED; | ||
122 | } else { | ||
123 | info("%s: no vio_dev struct for adapter in slot[%s]\n", | ||
124 | __FUNCTION__, slot->name); | ||
125 | slot->state = NOT_CONFIGURED; | ||
126 | } | ||
127 | |||
128 | return retval; | ||
129 | } | ||
diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h new file mode 100644 index 000000000000..67b6a3370ceb --- /dev/null +++ b/drivers/pci/hotplug/shpchp.h | |||
@@ -0,0 +1,463 @@ | |||
1 | /* | ||
2 | * Standard Hot Plug Controller Driver | ||
3 | * | ||
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | ||
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * Copyright (C) 2001 IBM | ||
7 | * Copyright (C) 2003-2004 Intel Corporation | ||
8 | * | ||
9 | * All rights reserved. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or (at | ||
14 | * your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
19 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
20 | * details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
25 | * | ||
26 | * Send feedback to <greg@kroah.com>,<dely.l.sy@intel.com> | ||
27 | * | ||
28 | */ | ||
29 | #ifndef _SHPCHP_H | ||
30 | #define _SHPCHP_H | ||
31 | |||
32 | #include <linux/types.h> | ||
33 | #include <linux/pci.h> | ||
34 | #include <linux/delay.h> | ||
35 | #include <asm/semaphore.h> | ||
36 | #include <asm/io.h> | ||
37 | #include "pci_hotplug.h" | ||
38 | |||
39 | #if !defined(MODULE) | ||
40 | #define MY_NAME "shpchp" | ||
41 | #else | ||
42 | #define MY_NAME THIS_MODULE->name | ||
43 | #endif | ||
44 | |||
45 | extern int shpchp_poll_mode; | ||
46 | extern int shpchp_poll_time; | ||
47 | extern int shpchp_debug; | ||
48 | |||
49 | /*#define dbg(format, arg...) do { if (shpchp_debug) printk(KERN_DEBUG "%s: " format, MY_NAME , ## arg); } while (0)*/ | ||
50 | #define dbg(format, arg...) do { if (shpchp_debug) printk("%s: " format, MY_NAME , ## arg); } while (0) | ||
51 | #define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg) | ||
52 | #define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg) | ||
53 | #define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME , ## arg) | ||
54 | |||
55 | struct pci_func { | ||
56 | struct pci_func *next; | ||
57 | u8 bus; | ||
58 | u8 device; | ||
59 | u8 function; | ||
60 | u8 is_a_board; | ||
61 | u16 status; | ||
62 | u8 configured; | ||
63 | u8 switch_save; | ||
64 | u8 presence_save; | ||
65 | u8 pwr_save; | ||
66 | u32 base_length[0x06]; | ||
67 | u8 base_type[0x06]; | ||
68 | u16 reserved2; | ||
69 | u32 config_space[0x20]; | ||
70 | struct pci_resource *mem_head; | ||
71 | struct pci_resource *p_mem_head; | ||
72 | struct pci_resource *io_head; | ||
73 | struct pci_resource *bus_head; | ||
74 | struct pci_dev* pci_dev; | ||
75 | }; | ||
76 | |||
77 | #define SLOT_MAGIC 0x67267321 | ||
78 | struct slot { | ||
79 | u32 magic; | ||
80 | struct slot *next; | ||
81 | u8 bus; | ||
82 | u8 device; | ||
83 | u32 number; | ||
84 | u8 is_a_board; | ||
85 | u8 configured; | ||
86 | u8 state; | ||
87 | u8 switch_save; | ||
88 | u8 presence_save; | ||
89 | u32 capabilities; | ||
90 | u16 reserved2; | ||
91 | struct timer_list task_event; | ||
92 | u8 hp_slot; | ||
93 | struct controller *ctrl; | ||
94 | struct hpc_ops *hpc_ops; | ||
95 | struct hotplug_slot *hotplug_slot; | ||
96 | struct list_head slot_list; | ||
97 | }; | ||
98 | |||
99 | struct pci_resource { | ||
100 | struct pci_resource * next; | ||
101 | u32 base; | ||
102 | u32 length; | ||
103 | }; | ||
104 | |||
105 | struct event_info { | ||
106 | u32 event_type; | ||
107 | u8 hp_slot; | ||
108 | }; | ||
109 | |||
110 | struct controller { | ||
111 | struct controller *next; | ||
112 | struct semaphore crit_sect; /* critical section semaphore */ | ||
113 | void * hpc_ctlr_handle; /* HPC controller handle */ | ||
114 | int num_slots; /* Number of slots on ctlr */ | ||
115 | int slot_num_inc; /* 1 or -1 */ | ||
116 | struct pci_resource *mem_head; | ||
117 | struct pci_resource *p_mem_head; | ||
118 | struct pci_resource *io_head; | ||
119 | struct pci_resource *bus_head; | ||
120 | struct pci_dev *pci_dev; | ||
121 | struct pci_bus *pci_bus; | ||
122 | struct event_info event_queue[10]; | ||
123 | struct slot *slot; | ||
124 | struct hpc_ops *hpc_ops; | ||
125 | wait_queue_head_t queue; /* sleep & wake process */ | ||
126 | u8 next_event; | ||
127 | u8 seg; | ||
128 | u8 bus; | ||
129 | u8 device; | ||
130 | u8 function; | ||
131 | u8 rev; | ||
132 | u8 slot_device_offset; | ||
133 | u8 add_support; | ||
134 | enum pci_bus_speed speed; | ||
135 | u32 first_slot; /* First physical slot number */ | ||
136 | u8 slot_bus; /* Bus where the slots handled by this controller sit */ | ||
137 | u8 push_flag; | ||
138 | u16 ctlrcap; | ||
139 | u16 vendor_id; | ||
140 | }; | ||
141 | |||
142 | struct irq_mapping { | ||
143 | u8 barber_pole; | ||
144 | u8 valid_INT; | ||
145 | u8 interrupt[4]; | ||
146 | }; | ||
147 | |||
148 | struct resource_lists { | ||
149 | struct pci_resource *mem_head; | ||
150 | struct pci_resource *p_mem_head; | ||
151 | struct pci_resource *io_head; | ||
152 | struct pci_resource *bus_head; | ||
153 | struct irq_mapping *irqs; | ||
154 | }; | ||
155 | |||
156 | /* Define AMD SHPC ID */ | ||
157 | #define PCI_DEVICE_ID_AMD_GOLAM_7450 0x7450 | ||
158 | |||
159 | #define INT_BUTTON_IGNORE 0 | ||
160 | #define INT_PRESENCE_ON 1 | ||
161 | #define INT_PRESENCE_OFF 2 | ||
162 | #define INT_SWITCH_CLOSE 3 | ||
163 | #define INT_SWITCH_OPEN 4 | ||
164 | #define INT_POWER_FAULT 5 | ||
165 | #define INT_POWER_FAULT_CLEAR 6 | ||
166 | #define INT_BUTTON_PRESS 7 | ||
167 | #define INT_BUTTON_RELEASE 8 | ||
168 | #define INT_BUTTON_CANCEL 9 | ||
169 | |||
170 | #define STATIC_STATE 0 | ||
171 | #define BLINKINGON_STATE 1 | ||
172 | #define BLINKINGOFF_STATE 2 | ||
173 | #define POWERON_STATE 3 | ||
174 | #define POWEROFF_STATE 4 | ||
175 | |||
176 | #define PCI_TO_PCI_BRIDGE_CLASS 0x00060400 | ||
177 | |||
178 | /* Error messages */ | ||
179 | #define INTERLOCK_OPEN 0x00000002 | ||
180 | #define ADD_NOT_SUPPORTED 0x00000003 | ||
181 | #define CARD_FUNCTIONING 0x00000005 | ||
182 | #define ADAPTER_NOT_SAME 0x00000006 | ||
183 | #define NO_ADAPTER_PRESENT 0x00000009 | ||
184 | #define NOT_ENOUGH_RESOURCES 0x0000000B | ||
185 | #define DEVICE_TYPE_NOT_SUPPORTED 0x0000000C | ||
186 | #define WRONG_BUS_FREQUENCY 0x0000000D | ||
187 | #define POWER_FAILURE 0x0000000E | ||
188 | |||
189 | #define REMOVE_NOT_SUPPORTED 0x00000003 | ||
190 | |||
191 | #define DISABLE_CARD 1 | ||
192 | |||
193 | /* | ||
194 | * error Messages | ||
195 | */ | ||
196 | #define msg_initialization_err "Initialization failure, error=%d\n" | ||
197 | #define msg_HPC_rev_error "Unsupported revision of the PCI hot plug controller found.\n" | ||
198 | #define msg_HPC_non_shpc "The PCI hot plug controller is not supported by this driver.\n" | ||
199 | #define msg_HPC_not_supported "This system is not supported by this version of shpcphd mdoule. Upgrade to a newer version of shpchpd\n" | ||
200 | #define msg_unable_to_save "Unable to store PCI hot plug add resource information. This system must be rebooted before adding any PCI devices.\n" | ||
201 | #define msg_button_on "PCI slot #%d - powering on due to button press.\n" | ||
202 | #define msg_button_off "PCI slot #%d - powering off due to button press.\n" | ||
203 | #define msg_button_cancel "PCI slot #%d - action canceled due to button press.\n" | ||
204 | #define msg_button_ignore "PCI slot #%d - button press ignored. (action in progress...)\n" | ||
205 | |||
206 | /* sysfs functions for the hotplug controller info */ | ||
207 | extern void shpchp_create_ctrl_files (struct controller *ctrl); | ||
208 | |||
209 | /* controller functions */ | ||
210 | extern int shpchprm_find_available_resources(struct controller *ctrl); | ||
211 | extern int shpchp_event_start_thread(void); | ||
212 | extern void shpchp_event_stop_thread(void); | ||
213 | extern struct pci_func *shpchp_slot_create(unsigned char busnumber); | ||
214 | extern struct pci_func *shpchp_slot_find(unsigned char bus, unsigned char device, unsigned char index); | ||
215 | extern int shpchp_enable_slot(struct slot *slot); | ||
216 | extern int shpchp_disable_slot(struct slot *slot); | ||
217 | |||
218 | extern u8 shpchp_handle_attention_button(u8 hp_slot, void *inst_id); | ||
219 | extern u8 shpchp_handle_switch_change(u8 hp_slot, void *inst_id); | ||
220 | extern u8 shpchp_handle_presence_change(u8 hp_slot, void *inst_id); | ||
221 | extern u8 shpchp_handle_power_fault(u8 hp_slot, void *inst_id); | ||
222 | |||
223 | /* resource functions */ | ||
224 | extern int shpchp_resource_sort_and_combine(struct pci_resource **head); | ||
225 | |||
226 | /* pci functions */ | ||
227 | extern int shpchp_set_irq(u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num); | ||
228 | /*extern int shpchp_get_bus_dev(struct controller *ctrl, u8 *bus_num, u8 *dev_num, struct slot *slot);*/ | ||
229 | extern int shpchp_save_config(struct controller *ctrl, int busnumber, int num_ctlr_slots, int first_device_num); | ||
230 | extern int shpchp_save_used_resources(struct controller *ctrl, struct pci_func * func, int flag); | ||
231 | extern int shpchp_save_slot_config(struct controller *ctrl, struct pci_func * new_slot); | ||
232 | extern void shpchp_destroy_board_resources(struct pci_func * func); | ||
233 | extern int shpchp_return_board_resources(struct pci_func * func, struct resource_lists * resources); | ||
234 | extern void shpchp_destroy_resource_list(struct resource_lists * resources); | ||
235 | extern int shpchp_configure_device(struct controller* ctrl, struct pci_func* func); | ||
236 | extern int shpchp_unconfigure_device(struct pci_func* func); | ||
237 | |||
238 | |||
239 | /* Global variables */ | ||
240 | extern struct controller *shpchp_ctrl_list; | ||
241 | extern struct pci_func *shpchp_slot_list[256]; | ||
242 | |||
243 | /* These are added to support AMD shpc */ | ||
244 | extern u8 shpchp_nic_irq; | ||
245 | extern u8 shpchp_disk_irq; | ||
246 | |||
247 | struct ctrl_reg { | ||
248 | volatile u32 base_offset; | ||
249 | volatile u32 slot_avail1; | ||
250 | volatile u32 slot_avail2; | ||
251 | volatile u32 slot_config; | ||
252 | volatile u16 sec_bus_config; | ||
253 | volatile u8 msi_ctrl; | ||
254 | volatile u8 prog_interface; | ||
255 | volatile u16 cmd; | ||
256 | volatile u16 cmd_status; | ||
257 | volatile u32 intr_loc; | ||
258 | volatile u32 serr_loc; | ||
259 | volatile u32 serr_intr_enable; | ||
260 | volatile u32 slot1; | ||
261 | volatile u32 slot2; | ||
262 | volatile u32 slot3; | ||
263 | volatile u32 slot4; | ||
264 | volatile u32 slot5; | ||
265 | volatile u32 slot6; | ||
266 | volatile u32 slot7; | ||
267 | volatile u32 slot8; | ||
268 | volatile u32 slot9; | ||
269 | volatile u32 slot10; | ||
270 | volatile u32 slot11; | ||
271 | volatile u32 slot12; | ||
272 | } __attribute__ ((packed)); | ||
273 | |||
274 | /* offsets to the controller registers based on the above structure layout */ | ||
275 | enum ctrl_offsets { | ||
276 | BASE_OFFSET = offsetof(struct ctrl_reg, base_offset), | ||
277 | SLOT_AVAIL1 = offsetof(struct ctrl_reg, slot_avail1), | ||
278 | SLOT_AVAIL2 = offsetof(struct ctrl_reg, slot_avail2), | ||
279 | SLOT_CONFIG = offsetof(struct ctrl_reg, slot_config), | ||
280 | SEC_BUS_CONFIG = offsetof(struct ctrl_reg, sec_bus_config), | ||
281 | MSI_CTRL = offsetof(struct ctrl_reg, msi_ctrl), | ||
282 | PROG_INTERFACE = offsetof(struct ctrl_reg, prog_interface), | ||
283 | CMD = offsetof(struct ctrl_reg, cmd), | ||
284 | CMD_STATUS = offsetof(struct ctrl_reg, cmd_status), | ||
285 | INTR_LOC = offsetof(struct ctrl_reg, intr_loc), | ||
286 | SERR_LOC = offsetof(struct ctrl_reg, serr_loc), | ||
287 | SERR_INTR_ENABLE = offsetof(struct ctrl_reg, serr_intr_enable), | ||
288 | SLOT1 = offsetof(struct ctrl_reg, slot1), | ||
289 | SLOT2 = offsetof(struct ctrl_reg, slot2), | ||
290 | SLOT3 = offsetof(struct ctrl_reg, slot3), | ||
291 | SLOT4 = offsetof(struct ctrl_reg, slot4), | ||
292 | SLOT5 = offsetof(struct ctrl_reg, slot5), | ||
293 | SLOT6 = offsetof(struct ctrl_reg, slot6), | ||
294 | SLOT7 = offsetof(struct ctrl_reg, slot7), | ||
295 | SLOT8 = offsetof(struct ctrl_reg, slot8), | ||
296 | SLOT9 = offsetof(struct ctrl_reg, slot9), | ||
297 | SLOT10 = offsetof(struct ctrl_reg, slot10), | ||
298 | SLOT11 = offsetof(struct ctrl_reg, slot11), | ||
299 | SLOT12 = offsetof(struct ctrl_reg, slot12), | ||
300 | }; | ||
301 | typedef u8(*php_intr_callback_t) (unsigned int change_id, void *instance_id); | ||
302 | struct php_ctlr_state_s { | ||
303 | struct php_ctlr_state_s *pnext; | ||
304 | struct pci_dev *pci_dev; | ||
305 | unsigned int irq; | ||
306 | unsigned long flags; /* spinlock's */ | ||
307 | u32 slot_device_offset; | ||
308 | u32 num_slots; | ||
309 | struct timer_list int_poll_timer; /* Added for poll event */ | ||
310 | php_intr_callback_t attention_button_callback; | ||
311 | php_intr_callback_t switch_change_callback; | ||
312 | php_intr_callback_t presence_change_callback; | ||
313 | php_intr_callback_t power_fault_callback; | ||
314 | void *callback_instance_id; | ||
315 | void __iomem *creg; /* Ptr to controller register space */ | ||
316 | }; | ||
317 | /* Inline functions */ | ||
318 | |||
319 | |||
320 | /* Inline functions to check the sanity of a pointer that is passed to us */ | ||
321 | static inline int slot_paranoia_check (struct slot *slot, const char *function) | ||
322 | { | ||
323 | if (!slot) { | ||
324 | dbg("%s - slot == NULL", function); | ||
325 | return -1; | ||
326 | } | ||
327 | if (slot->magic != SLOT_MAGIC) { | ||
328 | dbg("%s - bad magic number for slot", function); | ||
329 | return -1; | ||
330 | } | ||
331 | if (!slot->hotplug_slot) { | ||
332 | dbg("%s - slot->hotplug_slot == NULL!", function); | ||
333 | return -1; | ||
334 | } | ||
335 | return 0; | ||
336 | } | ||
337 | |||
338 | static inline struct slot *get_slot (struct hotplug_slot *hotplug_slot, const char *function) | ||
339 | { | ||
340 | struct slot *slot; | ||
341 | |||
342 | if (!hotplug_slot) { | ||
343 | dbg("%s - hotplug_slot == NULL\n", function); | ||
344 | return NULL; | ||
345 | } | ||
346 | |||
347 | slot = (struct slot *)hotplug_slot->private; | ||
348 | if (slot_paranoia_check (slot, function)) | ||
349 | return NULL; | ||
350 | return slot; | ||
351 | } | ||
352 | |||
353 | static inline struct slot *shpchp_find_slot (struct controller *ctrl, u8 device) | ||
354 | { | ||
355 | struct slot *p_slot, *tmp_slot = NULL; | ||
356 | |||
357 | if (!ctrl) | ||
358 | return NULL; | ||
359 | |||
360 | p_slot = ctrl->slot; | ||
361 | |||
362 | dbg("p_slot = %p\n", p_slot); | ||
363 | |||
364 | while (p_slot && (p_slot->device != device)) { | ||
365 | tmp_slot = p_slot; | ||
366 | p_slot = p_slot->next; | ||
367 | dbg("In while loop, p_slot = %p\n", p_slot); | ||
368 | } | ||
369 | if (p_slot == NULL) { | ||
370 | err("ERROR: shpchp_find_slot device=0x%x\n", device); | ||
371 | p_slot = tmp_slot; | ||
372 | } | ||
373 | |||
374 | return (p_slot); | ||
375 | } | ||
376 | |||
377 | static inline int wait_for_ctrl_irq (struct controller *ctrl) | ||
378 | { | ||
379 | DECLARE_WAITQUEUE(wait, current); | ||
380 | int retval = 0; | ||
381 | |||
382 | dbg("%s : start\n",__FUNCTION__); | ||
383 | |||
384 | add_wait_queue(&ctrl->queue, &wait); | ||
385 | |||
386 | if (!shpchp_poll_mode) { | ||
387 | /* Sleep for up to 1 second */ | ||
388 | msleep_interruptible(1000); | ||
389 | } else { | ||
390 | /* Sleep for up to 2 seconds */ | ||
391 | msleep_interruptible(2000); | ||
392 | } | ||
393 | remove_wait_queue(&ctrl->queue, &wait); | ||
394 | if (signal_pending(current)) | ||
395 | retval = -EINTR; | ||
396 | |||
397 | dbg("%s : end\n", __FUNCTION__); | ||
398 | return retval; | ||
399 | } | ||
400 | |||
401 | /* Puts node back in the resource list pointed to by head */ | ||
402 | static inline void return_resource(struct pci_resource **head, struct pci_resource *node) | ||
403 | { | ||
404 | if (!node || !head) | ||
405 | return; | ||
406 | node->next = *head; | ||
407 | *head = node; | ||
408 | } | ||
409 | |||
410 | #define SLOT_NAME_SIZE 10 | ||
411 | |||
412 | static inline void make_slot_name(char *buffer, int buffer_size, struct slot *slot) | ||
413 | { | ||
414 | snprintf(buffer, buffer_size, "%d", slot->number); | ||
415 | } | ||
416 | |||
417 | enum php_ctlr_type { | ||
418 | PCI, | ||
419 | ISA, | ||
420 | ACPI | ||
421 | }; | ||
422 | |||
423 | int shpc_init( struct controller *ctrl, struct pci_dev *pdev, | ||
424 | php_intr_callback_t attention_button_callback, | ||
425 | php_intr_callback_t switch_change_callback, | ||
426 | php_intr_callback_t presence_change_callback, | ||
427 | php_intr_callback_t power_fault_callback); | ||
428 | |||
429 | int shpc_get_ctlr_slot_config( struct controller *ctrl, | ||
430 | int *num_ctlr_slots, | ||
431 | int *first_device_num, | ||
432 | int *physical_slot_num, | ||
433 | int *updown, | ||
434 | int *flags); | ||
435 | |||
436 | struct hpc_ops { | ||
437 | int (*power_on_slot ) (struct slot *slot); | ||
438 | int (*slot_enable ) (struct slot *slot); | ||
439 | int (*slot_disable ) (struct slot *slot); | ||
440 | int (*enable_all_slots) (struct slot *slot); | ||
441 | int (*pwr_on_all_slots) (struct slot *slot); | ||
442 | int (*set_bus_speed_mode) (struct slot *slot, enum pci_bus_speed speed); | ||
443 | int (*get_power_status) (struct slot *slot, u8 *status); | ||
444 | int (*get_attention_status) (struct slot *slot, u8 *status); | ||
445 | int (*set_attention_status) (struct slot *slot, u8 status); | ||
446 | int (*get_latch_status) (struct slot *slot, u8 *status); | ||
447 | int (*get_adapter_status) (struct slot *slot, u8 *status); | ||
448 | |||
449 | int (*get_max_bus_speed) (struct slot *slot, enum pci_bus_speed *speed); | ||
450 | int (*get_cur_bus_speed) (struct slot *slot, enum pci_bus_speed *speed); | ||
451 | int (*get_adapter_speed) (struct slot *slot, enum pci_bus_speed *speed); | ||
452 | int (*get_mode1_ECC_cap) (struct slot *slot, u8 *mode); | ||
453 | int (*get_prog_int) (struct slot *slot, u8 *prog_int); | ||
454 | |||
455 | int (*query_power_fault) (struct slot *slot); | ||
456 | void (*green_led_on) (struct slot *slot); | ||
457 | void (*green_led_off) (struct slot *slot); | ||
458 | void (*green_led_blink) (struct slot *slot); | ||
459 | void (*release_ctlr) (struct controller *ctrl); | ||
460 | int (*check_cmd_status) (struct controller *ctrl); | ||
461 | }; | ||
462 | |||
463 | #endif /* _SHPCHP_H */ | ||
diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c new file mode 100644 index 000000000000..f0c53f850aed --- /dev/null +++ b/drivers/pci/hotplug/shpchp_core.c | |||
@@ -0,0 +1,630 @@ | |||
1 | /* | ||
2 | * Standard Hot Plug Controller Driver | ||
3 | * | ||
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | ||
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * Copyright (C) 2001 IBM Corp. | ||
7 | * Copyright (C) 2003-2004 Intel Corporation | ||
8 | * | ||
9 | * All rights reserved. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or (at | ||
14 | * your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
19 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
20 | * details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
25 | * | ||
26 | * Send feedback to <greg@kroah.com>, <dely.l.sy@intel.com> | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | #include <linux/config.h> | ||
31 | #include <linux/module.h> | ||
32 | #include <linux/moduleparam.h> | ||
33 | #include <linux/kernel.h> | ||
34 | #include <linux/types.h> | ||
35 | #include <linux/proc_fs.h> | ||
36 | #include <linux/slab.h> | ||
37 | #include <linux/workqueue.h> | ||
38 | #include <linux/pci.h> | ||
39 | #include <linux/init.h> | ||
40 | #include <asm/uaccess.h> | ||
41 | #include "shpchp.h" | ||
42 | #include "shpchprm.h" | ||
43 | |||
44 | /* Global variables */ | ||
45 | int shpchp_debug; | ||
46 | int shpchp_poll_mode; | ||
47 | int shpchp_poll_time; | ||
48 | struct controller *shpchp_ctrl_list; /* = NULL */ | ||
49 | struct pci_func *shpchp_slot_list[256]; | ||
50 | |||
51 | #define DRIVER_VERSION "0.4" | ||
52 | #define DRIVER_AUTHOR "Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>" | ||
53 | #define DRIVER_DESC "Standard Hot Plug PCI Controller Driver" | ||
54 | |||
55 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
56 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
57 | MODULE_LICENSE("GPL"); | ||
58 | |||
59 | module_param(shpchp_debug, bool, 0644); | ||
60 | module_param(shpchp_poll_mode, bool, 0644); | ||
61 | module_param(shpchp_poll_time, int, 0644); | ||
62 | MODULE_PARM_DESC(shpchp_debug, "Debugging mode enabled or not"); | ||
63 | MODULE_PARM_DESC(shpchp_poll_mode, "Using polling mechanism for hot-plug events or not"); | ||
64 | MODULE_PARM_DESC(shpchp_poll_time, "Polling mechanism frequency, in seconds"); | ||
65 | |||
66 | #define SHPC_MODULE_NAME "shpchp" | ||
67 | |||
68 | static int shpc_start_thread (void); | ||
69 | static int set_attention_status (struct hotplug_slot *slot, u8 value); | ||
70 | static int enable_slot (struct hotplug_slot *slot); | ||
71 | static int disable_slot (struct hotplug_slot *slot); | ||
72 | static int get_power_status (struct hotplug_slot *slot, u8 *value); | ||
73 | static int get_attention_status (struct hotplug_slot *slot, u8 *value); | ||
74 | static int get_latch_status (struct hotplug_slot *slot, u8 *value); | ||
75 | static int get_adapter_status (struct hotplug_slot *slot, u8 *value); | ||
76 | static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value); | ||
77 | static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value); | ||
78 | |||
79 | static struct hotplug_slot_ops shpchp_hotplug_slot_ops = { | ||
80 | .owner = THIS_MODULE, | ||
81 | .set_attention_status = set_attention_status, | ||
82 | .enable_slot = enable_slot, | ||
83 | .disable_slot = disable_slot, | ||
84 | .get_power_status = get_power_status, | ||
85 | .get_attention_status = get_attention_status, | ||
86 | .get_latch_status = get_latch_status, | ||
87 | .get_adapter_status = get_adapter_status, | ||
88 | .get_max_bus_speed = get_max_bus_speed, | ||
89 | .get_cur_bus_speed = get_cur_bus_speed, | ||
90 | }; | ||
91 | |||
92 | /** | ||
93 | * release_slot - free up the memory used by a slot | ||
94 | * @hotplug_slot: slot to free | ||
95 | */ | ||
96 | static void release_slot(struct hotplug_slot *hotplug_slot) | ||
97 | { | ||
98 | struct slot *slot = (struct slot *)hotplug_slot->private; | ||
99 | |||
100 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
101 | |||
102 | kfree(slot->hotplug_slot->info); | ||
103 | kfree(slot->hotplug_slot->name); | ||
104 | kfree(slot->hotplug_slot); | ||
105 | kfree(slot); | ||
106 | } | ||
107 | |||
108 | static int init_slots(struct controller *ctrl) | ||
109 | { | ||
110 | struct slot *new_slot; | ||
111 | u8 number_of_slots; | ||
112 | u8 slot_device; | ||
113 | u32 slot_number, sun; | ||
114 | int result = -ENOMEM; | ||
115 | |||
116 | dbg("%s\n",__FUNCTION__); | ||
117 | |||
118 | number_of_slots = ctrl->num_slots; | ||
119 | slot_device = ctrl->slot_device_offset; | ||
120 | slot_number = ctrl->first_slot; | ||
121 | |||
122 | while (number_of_slots) { | ||
123 | new_slot = (struct slot *) kmalloc(sizeof(struct slot), GFP_KERNEL); | ||
124 | if (!new_slot) | ||
125 | goto error; | ||
126 | |||
127 | memset(new_slot, 0, sizeof(struct slot)); | ||
128 | new_slot->hotplug_slot = kmalloc (sizeof (struct hotplug_slot), GFP_KERNEL); | ||
129 | if (!new_slot->hotplug_slot) | ||
130 | goto error_slot; | ||
131 | memset(new_slot->hotplug_slot, 0, sizeof (struct hotplug_slot)); | ||
132 | |||
133 | new_slot->hotplug_slot->info = kmalloc (sizeof (struct hotplug_slot_info), GFP_KERNEL); | ||
134 | if (!new_slot->hotplug_slot->info) | ||
135 | goto error_hpslot; | ||
136 | memset(new_slot->hotplug_slot->info, 0, sizeof (struct hotplug_slot_info)); | ||
137 | new_slot->hotplug_slot->name = kmalloc (SLOT_NAME_SIZE, GFP_KERNEL); | ||
138 | if (!new_slot->hotplug_slot->name) | ||
139 | goto error_info; | ||
140 | |||
141 | new_slot->magic = SLOT_MAGIC; | ||
142 | new_slot->ctrl = ctrl; | ||
143 | new_slot->bus = ctrl->slot_bus; | ||
144 | new_slot->device = slot_device; | ||
145 | new_slot->hpc_ops = ctrl->hpc_ops; | ||
146 | |||
147 | if (shpchprm_get_physical_slot_number(ctrl, &sun, | ||
148 | new_slot->bus, new_slot->device)) | ||
149 | goto error_name; | ||
150 | |||
151 | new_slot->number = sun; | ||
152 | new_slot->hp_slot = slot_device - ctrl->slot_device_offset; | ||
153 | |||
154 | /* register this slot with the hotplug pci core */ | ||
155 | new_slot->hotplug_slot->private = new_slot; | ||
156 | new_slot->hotplug_slot->release = &release_slot; | ||
157 | make_slot_name(new_slot->hotplug_slot->name, SLOT_NAME_SIZE, new_slot); | ||
158 | new_slot->hotplug_slot->ops = &shpchp_hotplug_slot_ops; | ||
159 | |||
160 | new_slot->hpc_ops->get_power_status(new_slot, &(new_slot->hotplug_slot->info->power_status)); | ||
161 | new_slot->hpc_ops->get_attention_status(new_slot, &(new_slot->hotplug_slot->info->attention_status)); | ||
162 | new_slot->hpc_ops->get_latch_status(new_slot, &(new_slot->hotplug_slot->info->latch_status)); | ||
163 | new_slot->hpc_ops->get_adapter_status(new_slot, &(new_slot->hotplug_slot->info->adapter_status)); | ||
164 | |||
165 | dbg("Registering bus=%x dev=%x hp_slot=%x sun=%x slot_device_offset=%x\n", new_slot->bus, | ||
166 | new_slot->device, new_slot->hp_slot, new_slot->number, ctrl->slot_device_offset); | ||
167 | result = pci_hp_register (new_slot->hotplug_slot); | ||
168 | if (result) { | ||
169 | err ("pci_hp_register failed with error %d\n", result); | ||
170 | goto error_name; | ||
171 | } | ||
172 | |||
173 | new_slot->next = ctrl->slot; | ||
174 | ctrl->slot = new_slot; | ||
175 | |||
176 | number_of_slots--; | ||
177 | slot_device++; | ||
178 | slot_number += ctrl->slot_num_inc; | ||
179 | } | ||
180 | |||
181 | return 0; | ||
182 | |||
183 | error_name: | ||
184 | kfree(new_slot->hotplug_slot->name); | ||
185 | error_info: | ||
186 | kfree(new_slot->hotplug_slot->info); | ||
187 | error_hpslot: | ||
188 | kfree(new_slot->hotplug_slot); | ||
189 | error_slot: | ||
190 | kfree(new_slot); | ||
191 | error: | ||
192 | return result; | ||
193 | } | ||
194 | |||
195 | static void cleanup_slots(struct controller *ctrl) | ||
196 | { | ||
197 | struct slot *old_slot, *next_slot; | ||
198 | |||
199 | old_slot = ctrl->slot; | ||
200 | ctrl->slot = NULL; | ||
201 | |||
202 | while (old_slot) { | ||
203 | next_slot = old_slot->next; | ||
204 | pci_hp_deregister(old_slot->hotplug_slot); | ||
205 | old_slot = next_slot; | ||
206 | } | ||
207 | } | ||
208 | |||
209 | static int get_ctlr_slot_config(struct controller *ctrl) | ||
210 | { | ||
211 | int num_ctlr_slots; | ||
212 | int first_device_num; | ||
213 | int physical_slot_num; | ||
214 | int updown; | ||
215 | int rc; | ||
216 | int flags; | ||
217 | |||
218 | rc = shpc_get_ctlr_slot_config(ctrl, &num_ctlr_slots, &first_device_num, &physical_slot_num, &updown, &flags); | ||
219 | if (rc) { | ||
220 | err("%s: get_ctlr_slot_config fail for b:d (%x:%x)\n", __FUNCTION__, ctrl->bus, ctrl->device); | ||
221 | return -1; | ||
222 | } | ||
223 | |||
224 | ctrl->num_slots = num_ctlr_slots; | ||
225 | ctrl->slot_device_offset = first_device_num; | ||
226 | ctrl->first_slot = physical_slot_num; | ||
227 | ctrl->slot_num_inc = updown; /* either -1 or 1 */ | ||
228 | |||
229 | dbg("%s: num_slot(0x%x) 1st_dev(0x%x) psn(0x%x) updown(%d) for b:d (%x:%x)\n", | ||
230 | __FUNCTION__, num_ctlr_slots, first_device_num, physical_slot_num, updown, ctrl->bus, ctrl->device); | ||
231 | |||
232 | return 0; | ||
233 | } | ||
234 | |||
235 | |||
236 | /* | ||
237 | * set_attention_status - Turns the Amber LED for a slot on, off or blink | ||
238 | */ | ||
239 | static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 status) | ||
240 | { | ||
241 | struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); | ||
242 | |||
243 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
244 | |||
245 | hotplug_slot->info->attention_status = status; | ||
246 | slot->hpc_ops->set_attention_status(slot, status); | ||
247 | |||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | |||
252 | static int enable_slot (struct hotplug_slot *hotplug_slot) | ||
253 | { | ||
254 | struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); | ||
255 | |||
256 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
257 | |||
258 | return shpchp_enable_slot(slot); | ||
259 | } | ||
260 | |||
261 | |||
262 | static int disable_slot (struct hotplug_slot *hotplug_slot) | ||
263 | { | ||
264 | struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); | ||
265 | |||
266 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
267 | |||
268 | return shpchp_disable_slot(slot); | ||
269 | } | ||
270 | |||
271 | static int get_power_status (struct hotplug_slot *hotplug_slot, u8 *value) | ||
272 | { | ||
273 | struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); | ||
274 | int retval; | ||
275 | |||
276 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
277 | |||
278 | retval = slot->hpc_ops->get_power_status(slot, value); | ||
279 | if (retval < 0) | ||
280 | *value = hotplug_slot->info->power_status; | ||
281 | |||
282 | return 0; | ||
283 | } | ||
284 | |||
285 | static int get_attention_status (struct hotplug_slot *hotplug_slot, u8 *value) | ||
286 | { | ||
287 | struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); | ||
288 | int retval; | ||
289 | |||
290 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
291 | |||
292 | retval = slot->hpc_ops->get_attention_status(slot, value); | ||
293 | if (retval < 0) | ||
294 | *value = hotplug_slot->info->attention_status; | ||
295 | |||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | static int get_latch_status (struct hotplug_slot *hotplug_slot, u8 *value) | ||
300 | { | ||
301 | struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); | ||
302 | int retval; | ||
303 | |||
304 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
305 | |||
306 | retval = slot->hpc_ops->get_latch_status(slot, value); | ||
307 | if (retval < 0) | ||
308 | *value = hotplug_slot->info->latch_status; | ||
309 | |||
310 | return 0; | ||
311 | } | ||
312 | |||
313 | static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value) | ||
314 | { | ||
315 | struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); | ||
316 | int retval; | ||
317 | |||
318 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
319 | |||
320 | retval = slot->hpc_ops->get_adapter_status(slot, value); | ||
321 | if (retval < 0) | ||
322 | *value = hotplug_slot->info->adapter_status; | ||
323 | |||
324 | return 0; | ||
325 | } | ||
326 | |||
327 | static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) | ||
328 | { | ||
329 | struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); | ||
330 | int retval; | ||
331 | |||
332 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
333 | |||
334 | retval = slot->hpc_ops->get_max_bus_speed(slot, value); | ||
335 | if (retval < 0) | ||
336 | *value = PCI_SPEED_UNKNOWN; | ||
337 | |||
338 | return 0; | ||
339 | } | ||
340 | |||
341 | static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) | ||
342 | { | ||
343 | struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); | ||
344 | int retval; | ||
345 | |||
346 | dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); | ||
347 | |||
348 | retval = slot->hpc_ops->get_cur_bus_speed(slot, value); | ||
349 | if (retval < 0) | ||
350 | *value = PCI_SPEED_UNKNOWN; | ||
351 | |||
352 | return 0; | ||
353 | } | ||
354 | |||
355 | static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | ||
356 | { | ||
357 | int rc; | ||
358 | struct controller *ctrl; | ||
359 | struct slot *t_slot; | ||
360 | int first_device_num; /* first PCI device number supported by this SHPC */ | ||
361 | int num_ctlr_slots; /* number of slots supported by this SHPC */ | ||
362 | |||
363 | ctrl = (struct controller *) kmalloc(sizeof(struct controller), GFP_KERNEL); | ||
364 | if (!ctrl) { | ||
365 | err("%s : out of memory\n", __FUNCTION__); | ||
366 | goto err_out_none; | ||
367 | } | ||
368 | memset(ctrl, 0, sizeof(struct controller)); | ||
369 | |||
370 | dbg("DRV_thread pid = %d\n", current->pid); | ||
371 | |||
372 | rc = shpc_init(ctrl, pdev, | ||
373 | (php_intr_callback_t) shpchp_handle_attention_button, | ||
374 | (php_intr_callback_t) shpchp_handle_switch_change, | ||
375 | (php_intr_callback_t) shpchp_handle_presence_change, | ||
376 | (php_intr_callback_t) shpchp_handle_power_fault); | ||
377 | if (rc) { | ||
378 | dbg("%s: controller initialization failed\n", SHPC_MODULE_NAME); | ||
379 | goto err_out_free_ctrl; | ||
380 | } | ||
381 | |||
382 | dbg("%s: controller initialization success\n", __FUNCTION__); | ||
383 | ctrl->pci_dev = pdev; /* pci_dev of the P2P bridge */ | ||
384 | |||
385 | pci_set_drvdata(pdev, ctrl); | ||
386 | |||
387 | ctrl->pci_bus = kmalloc (sizeof (*ctrl->pci_bus), GFP_KERNEL); | ||
388 | if (!ctrl->pci_bus) { | ||
389 | err("out of memory\n"); | ||
390 | rc = -ENOMEM; | ||
391 | goto err_out_unmap_mmio_region; | ||
392 | } | ||
393 | |||
394 | memcpy (ctrl->pci_bus, pdev->bus, sizeof (*ctrl->pci_bus)); | ||
395 | ctrl->bus = pdev->bus->number; | ||
396 | ctrl->slot_bus = pdev->subordinate->number; | ||
397 | |||
398 | ctrl->device = PCI_SLOT(pdev->devfn); | ||
399 | ctrl->function = PCI_FUNC(pdev->devfn); | ||
400 | dbg("ctrl bus=0x%x, device=%x, function=%x, irq=%x\n", ctrl->bus, ctrl->device, ctrl->function, pdev->irq); | ||
401 | |||
402 | /* | ||
403 | * Save configuration headers for this and subordinate PCI buses | ||
404 | */ | ||
405 | |||
406 | rc = get_ctlr_slot_config(ctrl); | ||
407 | if (rc) { | ||
408 | err(msg_initialization_err, rc); | ||
409 | goto err_out_free_ctrl_bus; | ||
410 | } | ||
411 | first_device_num = ctrl->slot_device_offset; | ||
412 | num_ctlr_slots = ctrl->num_slots; | ||
413 | |||
414 | /* Store PCI Config Space for all devices on this bus */ | ||
415 | rc = shpchp_save_config(ctrl, ctrl->slot_bus, num_ctlr_slots, first_device_num); | ||
416 | if (rc) { | ||
417 | err("%s: unable to save PCI configuration data, error %d\n", __FUNCTION__, rc); | ||
418 | goto err_out_free_ctrl_bus; | ||
419 | } | ||
420 | |||
421 | /* Get IO, memory, and IRQ resources for new devices */ | ||
422 | rc = shpchprm_find_available_resources(ctrl); | ||
423 | ctrl->add_support = !rc; | ||
424 | |||
425 | if (rc) { | ||
426 | dbg("shpchprm_find_available_resources = %#x\n", rc); | ||
427 | err("unable to locate PCI configuration resources for hot plug add.\n"); | ||
428 | goto err_out_free_ctrl_bus; | ||
429 | } | ||
430 | |||
431 | /* Setup the slot information structures */ | ||
432 | rc = init_slots(ctrl); | ||
433 | if (rc) { | ||
434 | err(msg_initialization_err, 6); | ||
435 | goto err_out_free_ctrl_slot; | ||
436 | } | ||
437 | |||
438 | /* Now hpc_functions (slot->hpc_ops->functions) are ready */ | ||
439 | t_slot = shpchp_find_slot(ctrl, first_device_num); | ||
440 | |||
441 | /* Check for operation bus speed */ | ||
442 | rc = t_slot->hpc_ops->get_cur_bus_speed(t_slot, &ctrl->speed); | ||
443 | dbg("%s: t_slot->hp_slot %x\n", __FUNCTION__,t_slot->hp_slot); | ||
444 | |||
445 | if (rc || ctrl->speed == PCI_SPEED_UNKNOWN) { | ||
446 | err(SHPC_MODULE_NAME ": Can't get current bus speed. Set to 33MHz PCI.\n"); | ||
447 | ctrl->speed = PCI_SPEED_33MHz; | ||
448 | } | ||
449 | |||
450 | /* Finish setting up the hot plug ctrl device */ | ||
451 | ctrl->next_event = 0; | ||
452 | |||
453 | if (!shpchp_ctrl_list) { | ||
454 | shpchp_ctrl_list = ctrl; | ||
455 | ctrl->next = NULL; | ||
456 | } else { | ||
457 | ctrl->next = shpchp_ctrl_list; | ||
458 | shpchp_ctrl_list = ctrl; | ||
459 | } | ||
460 | |||
461 | shpchp_create_ctrl_files(ctrl); | ||
462 | |||
463 | return 0; | ||
464 | |||
465 | err_out_free_ctrl_slot: | ||
466 | cleanup_slots(ctrl); | ||
467 | err_out_free_ctrl_bus: | ||
468 | kfree(ctrl->pci_bus); | ||
469 | err_out_unmap_mmio_region: | ||
470 | ctrl->hpc_ops->release_ctlr(ctrl); | ||
471 | err_out_free_ctrl: | ||
472 | kfree(ctrl); | ||
473 | err_out_none: | ||
474 | return -ENODEV; | ||
475 | } | ||
476 | |||
477 | |||
478 | static int shpc_start_thread(void) | ||
479 | { | ||
480 | int loop; | ||
481 | int retval = 0; | ||
482 | |||
483 | dbg("Initialize + Start the notification/polling mechanism \n"); | ||
484 | |||
485 | retval = shpchp_event_start_thread(); | ||
486 | if (retval) { | ||
487 | dbg("shpchp_event_start_thread() failed\n"); | ||
488 | return retval; | ||
489 | } | ||
490 | |||
491 | dbg("Initialize slot lists\n"); | ||
492 | /* One slot list for each bus in the system */ | ||
493 | for (loop = 0; loop < 256; loop++) { | ||
494 | shpchp_slot_list[loop] = NULL; | ||
495 | } | ||
496 | |||
497 | return retval; | ||
498 | } | ||
499 | |||
500 | static inline void __exit | ||
501 | free_shpchp_res(struct pci_resource *res) | ||
502 | { | ||
503 | struct pci_resource *tres; | ||
504 | |||
505 | while (res) { | ||
506 | tres = res; | ||
507 | res = res->next; | ||
508 | kfree(tres); | ||
509 | } | ||
510 | } | ||
511 | |||
512 | static void __exit unload_shpchpd(void) | ||
513 | { | ||
514 | struct pci_func *next; | ||
515 | struct pci_func *TempSlot; | ||
516 | int loop; | ||
517 | struct controller *ctrl; | ||
518 | struct controller *tctrl; | ||
519 | |||
520 | ctrl = shpchp_ctrl_list; | ||
521 | |||
522 | while (ctrl) { | ||
523 | cleanup_slots(ctrl); | ||
524 | |||
525 | free_shpchp_res(ctrl->io_head); | ||
526 | free_shpchp_res(ctrl->mem_head); | ||
527 | free_shpchp_res(ctrl->p_mem_head); | ||
528 | free_shpchp_res(ctrl->bus_head); | ||
529 | |||
530 | kfree (ctrl->pci_bus); | ||
531 | |||
532 | dbg("%s: calling release_ctlr\n", __FUNCTION__); | ||
533 | ctrl->hpc_ops->release_ctlr(ctrl); | ||
534 | |||
535 | tctrl = ctrl; | ||
536 | ctrl = ctrl->next; | ||
537 | |||
538 | kfree(tctrl); | ||
539 | } | ||
540 | |||
541 | for (loop = 0; loop < 256; loop++) { | ||
542 | next = shpchp_slot_list[loop]; | ||
543 | while (next != NULL) { | ||
544 | free_shpchp_res(next->io_head); | ||
545 | free_shpchp_res(next->mem_head); | ||
546 | free_shpchp_res(next->p_mem_head); | ||
547 | free_shpchp_res(next->bus_head); | ||
548 | |||
549 | TempSlot = next; | ||
550 | next = next->next; | ||
551 | kfree(TempSlot); | ||
552 | } | ||
553 | } | ||
554 | |||
555 | /* Stop the notification mechanism */ | ||
556 | shpchp_event_stop_thread(); | ||
557 | |||
558 | } | ||
559 | |||
560 | |||
561 | static struct pci_device_id shpcd_pci_tbl[] = { | ||
562 | { | ||
563 | .class = ((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), | ||
564 | .class_mask = ~0, | ||
565 | .vendor = PCI_ANY_ID, | ||
566 | .device = PCI_ANY_ID, | ||
567 | .subvendor = PCI_ANY_ID, | ||
568 | .subdevice = PCI_ANY_ID, | ||
569 | }, | ||
570 | |||
571 | { /* end: all zeroes */ } | ||
572 | }; | ||
573 | |||
574 | MODULE_DEVICE_TABLE(pci, shpcd_pci_tbl); | ||
575 | |||
576 | |||
577 | |||
578 | static struct pci_driver shpc_driver = { | ||
579 | .name = SHPC_MODULE_NAME, | ||
580 | .id_table = shpcd_pci_tbl, | ||
581 | .probe = shpc_probe, | ||
582 | /* remove: shpc_remove_one, */ | ||
583 | }; | ||
584 | |||
585 | |||
586 | |||
587 | static int __init shpcd_init(void) | ||
588 | { | ||
589 | int retval = 0; | ||
590 | |||
591 | #ifdef CONFIG_HOTPLUG_PCI_SHPC_POLL_EVENT_MODE | ||
592 | shpchp_poll_mode = 1; | ||
593 | #endif | ||
594 | |||
595 | retval = shpc_start_thread(); | ||
596 | if (retval) | ||
597 | goto error_hpc_init; | ||
598 | |||
599 | retval = shpchprm_init(PCI); | ||
600 | if (!retval) { | ||
601 | retval = pci_register_driver(&shpc_driver); | ||
602 | dbg("%s: pci_register_driver = %d\n", __FUNCTION__, retval); | ||
603 | info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); | ||
604 | } | ||
605 | |||
606 | error_hpc_init: | ||
607 | if (retval) { | ||
608 | shpchprm_cleanup(); | ||
609 | shpchp_event_stop_thread(); | ||
610 | } else | ||
611 | shpchprm_print_pirt(); | ||
612 | |||
613 | return retval; | ||
614 | } | ||
615 | |||
616 | static void __exit shpcd_cleanup(void) | ||
617 | { | ||
618 | dbg("unload_shpchpd()\n"); | ||
619 | unload_shpchpd(); | ||
620 | |||
621 | shpchprm_cleanup(); | ||
622 | |||
623 | dbg("pci_unregister_driver\n"); | ||
624 | pci_unregister_driver(&shpc_driver); | ||
625 | |||
626 | info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n"); | ||
627 | } | ||
628 | |||
629 | module_init(shpcd_init); | ||
630 | module_exit(shpcd_cleanup); | ||
diff --git a/drivers/pci/hotplug/shpchp_ctrl.c b/drivers/pci/hotplug/shpchp_ctrl.c new file mode 100644 index 000000000000..9f90eb8e6ecd --- /dev/null +++ b/drivers/pci/hotplug/shpchp_ctrl.c | |||
@@ -0,0 +1,2848 @@ | |||
1 | /* | ||
2 | * Standard Hot Plug Controller Driver | ||
3 | * | ||
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | ||
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * Copyright (C) 2001 IBM Corp. | ||
7 | * Copyright (C) 2003-2004 Intel Corporation | ||
8 | * | ||
9 | * All rights reserved. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or (at | ||
14 | * your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
19 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
20 | * details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
25 | * | ||
26 | * Send feedback to <greg@kroah.com>, <dely.l.sy@intel.com> | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | #include <linux/config.h> | ||
31 | #include <linux/module.h> | ||
32 | #include <linux/kernel.h> | ||
33 | #include <linux/types.h> | ||
34 | #include <linux/slab.h> | ||
35 | #include <linux/workqueue.h> | ||
36 | #include <linux/interrupt.h> | ||
37 | #include <linux/delay.h> | ||
38 | #include <linux/wait.h> | ||
39 | #include <linux/smp_lock.h> | ||
40 | #include <linux/pci.h> | ||
41 | #include "shpchp.h" | ||
42 | #include "shpchprm.h" | ||
43 | |||
44 | static u32 configure_new_device(struct controller *ctrl, struct pci_func *func, | ||
45 | u8 behind_bridge, struct resource_lists *resources, u8 bridge_bus, u8 bridge_dev); | ||
46 | static int configure_new_function( struct controller *ctrl, struct pci_func *func, | ||
47 | u8 behind_bridge, struct resource_lists *resources, u8 bridge_bus, u8 bridge_dev); | ||
48 | static void interrupt_event_handler(struct controller *ctrl); | ||
49 | |||
50 | static struct semaphore event_semaphore; /* mutex for process loop (up if something to process) */ | ||
51 | static struct semaphore event_exit; /* guard ensure thread has exited before calling it quits */ | ||
52 | static int event_finished; | ||
53 | static unsigned long pushbutton_pending; /* = 0 */ | ||
54 | |||
55 | u8 shpchp_disk_irq; | ||
56 | u8 shpchp_nic_irq; | ||
57 | |||
58 | u8 shpchp_handle_attention_button(u8 hp_slot, void *inst_id) | ||
59 | { | ||
60 | struct controller *ctrl = (struct controller *) inst_id; | ||
61 | struct slot *p_slot; | ||
62 | u8 rc = 0; | ||
63 | u8 getstatus; | ||
64 | struct pci_func *func; | ||
65 | struct event_info *taskInfo; | ||
66 | |||
67 | /* Attention Button Change */ | ||
68 | dbg("shpchp: Attention button interrupt received.\n"); | ||
69 | |||
70 | func = shpchp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0); | ||
71 | |||
72 | /* This is the structure that tells the worker thread what to do */ | ||
73 | taskInfo = &(ctrl->event_queue[ctrl->next_event]); | ||
74 | p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | ||
75 | |||
76 | p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save)); | ||
77 | p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); | ||
78 | |||
79 | ctrl->next_event = (ctrl->next_event + 1) % 10; | ||
80 | taskInfo->hp_slot = hp_slot; | ||
81 | |||
82 | rc++; | ||
83 | |||
84 | /* | ||
85 | * Button pressed - See if need to TAKE ACTION!!! | ||
86 | */ | ||
87 | info("Button pressed on Slot(%d)\n", ctrl->first_slot + hp_slot); | ||
88 | taskInfo->event_type = INT_BUTTON_PRESS; | ||
89 | |||
90 | if ((p_slot->state == BLINKINGON_STATE) | ||
91 | || (p_slot->state == BLINKINGOFF_STATE)) { | ||
92 | /* Cancel if we are still blinking; this means that we press the | ||
93 | * attention again before the 5 sec. limit expires to cancel hot-add | ||
94 | * or hot-remove | ||
95 | */ | ||
96 | taskInfo->event_type = INT_BUTTON_CANCEL; | ||
97 | info("Button cancel on Slot(%d)\n", ctrl->first_slot + hp_slot); | ||
98 | } else if ((p_slot->state == POWERON_STATE) | ||
99 | || (p_slot->state == POWEROFF_STATE)) { | ||
100 | /* Ignore if the slot is on power-on or power-off state; this | ||
101 | * means that the previous attention button action to hot-add or | ||
102 | * hot-remove is undergoing | ||
103 | */ | ||
104 | taskInfo->event_type = INT_BUTTON_IGNORE; | ||
105 | info("Button ignore on Slot(%d)\n", ctrl->first_slot + hp_slot); | ||
106 | } | ||
107 | |||
108 | if (rc) | ||
109 | up(&event_semaphore); /* signal event thread that new event is posted */ | ||
110 | |||
111 | return 0; | ||
112 | |||
113 | } | ||
114 | |||
115 | u8 shpchp_handle_switch_change(u8 hp_slot, void *inst_id) | ||
116 | { | ||
117 | struct controller *ctrl = (struct controller *) inst_id; | ||
118 | struct slot *p_slot; | ||
119 | u8 rc = 0; | ||
120 | u8 getstatus; | ||
121 | struct pci_func *func; | ||
122 | struct event_info *taskInfo; | ||
123 | |||
124 | /* Switch Change */ | ||
125 | dbg("shpchp: Switch interrupt received.\n"); | ||
126 | |||
127 | func = shpchp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0); | ||
128 | |||
129 | /* This is the structure that tells the worker thread | ||
130 | * what to do | ||
131 | */ | ||
132 | taskInfo = &(ctrl->event_queue[ctrl->next_event]); | ||
133 | ctrl->next_event = (ctrl->next_event + 1) % 10; | ||
134 | taskInfo->hp_slot = hp_slot; | ||
135 | |||
136 | rc++; | ||
137 | p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | ||
138 | p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save)); | ||
139 | p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); | ||
140 | dbg("%s: Card present %x Power status %x\n", __FUNCTION__, | ||
141 | func->presence_save, func->pwr_save); | ||
142 | |||
143 | if (getstatus) { | ||
144 | /* | ||
145 | * Switch opened | ||
146 | */ | ||
147 | info("Latch open on Slot(%d)\n", ctrl->first_slot + hp_slot); | ||
148 | func->switch_save = 0; | ||
149 | taskInfo->event_type = INT_SWITCH_OPEN; | ||
150 | if (func->pwr_save && func->presence_save) { | ||
151 | taskInfo->event_type = INT_POWER_FAULT; | ||
152 | err("Surprise Removal of card\n"); | ||
153 | } | ||
154 | } else { | ||
155 | /* | ||
156 | * Switch closed | ||
157 | */ | ||
158 | info("Latch close on Slot(%d)\n", ctrl->first_slot + hp_slot); | ||
159 | func->switch_save = 0x10; | ||
160 | taskInfo->event_type = INT_SWITCH_CLOSE; | ||
161 | } | ||
162 | |||
163 | if (rc) | ||
164 | up(&event_semaphore); /* signal event thread that new event is posted */ | ||
165 | |||
166 | return rc; | ||
167 | } | ||
168 | |||
169 | u8 shpchp_handle_presence_change(u8 hp_slot, void *inst_id) | ||
170 | { | ||
171 | struct controller *ctrl = (struct controller *) inst_id; | ||
172 | struct slot *p_slot; | ||
173 | u8 rc = 0; | ||
174 | /*u8 temp_byte;*/ | ||
175 | struct pci_func *func; | ||
176 | struct event_info *taskInfo; | ||
177 | |||
178 | /* Presence Change */ | ||
179 | dbg("shpchp: Presence/Notify input change.\n"); | ||
180 | |||
181 | func = shpchp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0); | ||
182 | |||
183 | /* This is the structure that tells the worker thread | ||
184 | * what to do | ||
185 | */ | ||
186 | taskInfo = &(ctrl->event_queue[ctrl->next_event]); | ||
187 | ctrl->next_event = (ctrl->next_event + 1) % 10; | ||
188 | taskInfo->hp_slot = hp_slot; | ||
189 | |||
190 | rc++; | ||
191 | p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | ||
192 | |||
193 | /* | ||
194 | * Save the presence state | ||
195 | */ | ||
196 | p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save)); | ||
197 | if (func->presence_save) { | ||
198 | /* | ||
199 | * Card Present | ||
200 | */ | ||
201 | info("Card present on Slot(%d)\n", ctrl->first_slot + hp_slot); | ||
202 | taskInfo->event_type = INT_PRESENCE_ON; | ||
203 | } else { | ||
204 | /* | ||
205 | * Not Present | ||
206 | */ | ||
207 | info("Card not present on Slot(%d)\n", ctrl->first_slot + hp_slot); | ||
208 | taskInfo->event_type = INT_PRESENCE_OFF; | ||
209 | } | ||
210 | |||
211 | if (rc) | ||
212 | up(&event_semaphore); /* signal event thread that new event is posted */ | ||
213 | |||
214 | return rc; | ||
215 | } | ||
216 | |||
217 | u8 shpchp_handle_power_fault(u8 hp_slot, void *inst_id) | ||
218 | { | ||
219 | struct controller *ctrl = (struct controller *) inst_id; | ||
220 | struct slot *p_slot; | ||
221 | u8 rc = 0; | ||
222 | struct pci_func *func; | ||
223 | struct event_info *taskInfo; | ||
224 | |||
225 | /* Power fault */ | ||
226 | dbg("shpchp: Power fault interrupt received.\n"); | ||
227 | |||
228 | func = shpchp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0); | ||
229 | |||
230 | /* This is the structure that tells the worker thread | ||
231 | * what to do | ||
232 | */ | ||
233 | taskInfo = &(ctrl->event_queue[ctrl->next_event]); | ||
234 | ctrl->next_event = (ctrl->next_event + 1) % 10; | ||
235 | taskInfo->hp_slot = hp_slot; | ||
236 | |||
237 | rc++; | ||
238 | p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | ||
239 | |||
240 | if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) { | ||
241 | /* | ||
242 | * Power fault Cleared | ||
243 | */ | ||
244 | info("Power fault cleared on Slot(%d)\n", ctrl->first_slot + hp_slot); | ||
245 | func->status = 0x00; | ||
246 | taskInfo->event_type = INT_POWER_FAULT_CLEAR; | ||
247 | } else { | ||
248 | /* | ||
249 | * Power fault | ||
250 | */ | ||
251 | info("Power fault on Slot(%d)\n", ctrl->first_slot + hp_slot); | ||
252 | taskInfo->event_type = INT_POWER_FAULT; | ||
253 | /* set power fault status for this board */ | ||
254 | func->status = 0xFF; | ||
255 | info("power fault bit %x set\n", hp_slot); | ||
256 | } | ||
257 | if (rc) | ||
258 | up(&event_semaphore); /* signal event thread that new event is posted */ | ||
259 | |||
260 | return rc; | ||
261 | } | ||
262 | |||
263 | |||
264 | /* | ||
265 | * sort_by_size | ||
266 | * | ||
267 | * Sorts nodes on the list by their length. | ||
268 | * Smallest first. | ||
269 | * | ||
270 | */ | ||
271 | static int sort_by_size(struct pci_resource **head) | ||
272 | { | ||
273 | struct pci_resource *current_res; | ||
274 | struct pci_resource *next_res; | ||
275 | int out_of_order = 1; | ||
276 | |||
277 | if (!(*head)) | ||
278 | return(1); | ||
279 | |||
280 | if (!((*head)->next)) | ||
281 | return(0); | ||
282 | |||
283 | while (out_of_order) { | ||
284 | out_of_order = 0; | ||
285 | |||
286 | /* Special case for swapping list head */ | ||
287 | if (((*head)->next) && | ||
288 | ((*head)->length > (*head)->next->length)) { | ||
289 | out_of_order++; | ||
290 | current_res = *head; | ||
291 | *head = (*head)->next; | ||
292 | current_res->next = (*head)->next; | ||
293 | (*head)->next = current_res; | ||
294 | } | ||
295 | |||
296 | current_res = *head; | ||
297 | |||
298 | while (current_res->next && current_res->next->next) { | ||
299 | if (current_res->next->length > current_res->next->next->length) { | ||
300 | out_of_order++; | ||
301 | next_res = current_res->next; | ||
302 | current_res->next = current_res->next->next; | ||
303 | current_res = current_res->next; | ||
304 | next_res->next = current_res->next; | ||
305 | current_res->next = next_res; | ||
306 | } else | ||
307 | current_res = current_res->next; | ||
308 | } | ||
309 | } /* End of out_of_order loop */ | ||
310 | |||
311 | return(0); | ||
312 | } | ||
313 | |||
314 | |||
315 | /* | ||
316 | * sort_by_max_size | ||
317 | * | ||
318 | * Sorts nodes on the list by their length. | ||
319 | * Largest first. | ||
320 | * | ||
321 | */ | ||
322 | static int sort_by_max_size(struct pci_resource **head) | ||
323 | { | ||
324 | struct pci_resource *current_res; | ||
325 | struct pci_resource *next_res; | ||
326 | int out_of_order = 1; | ||
327 | |||
328 | if (!(*head)) | ||
329 | return(1); | ||
330 | |||
331 | if (!((*head)->next)) | ||
332 | return(0); | ||
333 | |||
334 | while (out_of_order) { | ||
335 | out_of_order = 0; | ||
336 | |||
337 | /* Special case for swapping list head */ | ||
338 | if (((*head)->next) && | ||
339 | ((*head)->length < (*head)->next->length)) { | ||
340 | out_of_order++; | ||
341 | current_res = *head; | ||
342 | *head = (*head)->next; | ||
343 | current_res->next = (*head)->next; | ||
344 | (*head)->next = current_res; | ||
345 | } | ||
346 | |||
347 | current_res = *head; | ||
348 | |||
349 | while (current_res->next && current_res->next->next) { | ||
350 | if (current_res->next->length < current_res->next->next->length) { | ||
351 | out_of_order++; | ||
352 | next_res = current_res->next; | ||
353 | current_res->next = current_res->next->next; | ||
354 | current_res = current_res->next; | ||
355 | next_res->next = current_res->next; | ||
356 | current_res->next = next_res; | ||
357 | } else | ||
358 | current_res = current_res->next; | ||
359 | } | ||
360 | } /* End of out_of_order loop */ | ||
361 | |||
362 | return(0); | ||
363 | } | ||
364 | |||
365 | |||
366 | /* | ||
367 | * do_pre_bridge_resource_split | ||
368 | * | ||
369 | * Returns zero or one node of resources that aren't in use | ||
370 | * | ||
371 | */ | ||
372 | static struct pci_resource *do_pre_bridge_resource_split (struct pci_resource **head, struct pci_resource **orig_head, u32 alignment) | ||
373 | { | ||
374 | struct pci_resource *prevnode = NULL; | ||
375 | struct pci_resource *node; | ||
376 | struct pci_resource *split_node; | ||
377 | u32 rc; | ||
378 | u32 temp_dword; | ||
379 | dbg("do_pre_bridge_resource_split\n"); | ||
380 | |||
381 | if (!(*head) || !(*orig_head)) | ||
382 | return(NULL); | ||
383 | |||
384 | rc = shpchp_resource_sort_and_combine(head); | ||
385 | |||
386 | if (rc) | ||
387 | return(NULL); | ||
388 | |||
389 | if ((*head)->base != (*orig_head)->base) | ||
390 | return(NULL); | ||
391 | |||
392 | if ((*head)->length == (*orig_head)->length) | ||
393 | return(NULL); | ||
394 | |||
395 | |||
396 | /* If we got here, there the bridge requires some of the resource, but | ||
397 | * we may be able to split some off of the front | ||
398 | */ | ||
399 | node = *head; | ||
400 | |||
401 | if (node->length & (alignment -1)) { | ||
402 | /* This one isn't an aligned length, so we'll make a new entry | ||
403 | * and split it up. | ||
404 | */ | ||
405 | split_node = kmalloc(sizeof(*split_node), GFP_KERNEL); | ||
406 | |||
407 | if (!split_node) | ||
408 | return(NULL); | ||
409 | |||
410 | temp_dword = (node->length | (alignment-1)) + 1 - alignment; | ||
411 | |||
412 | split_node->base = node->base; | ||
413 | split_node->length = temp_dword; | ||
414 | |||
415 | node->length -= temp_dword; | ||
416 | node->base += split_node->length; | ||
417 | |||
418 | /* Put it in the list */ | ||
419 | *head = split_node; | ||
420 | split_node->next = node; | ||
421 | } | ||
422 | |||
423 | if (node->length < alignment) { | ||
424 | return(NULL); | ||
425 | } | ||
426 | |||
427 | /* Now unlink it */ | ||
428 | if (*head == node) { | ||
429 | *head = node->next; | ||
430 | node->next = NULL; | ||
431 | } else { | ||
432 | prevnode = *head; | ||
433 | while (prevnode->next != node) | ||
434 | prevnode = prevnode->next; | ||
435 | |||
436 | prevnode->next = node->next; | ||
437 | node->next = NULL; | ||
438 | } | ||
439 | |||
440 | return(node); | ||
441 | } | ||
442 | |||
443 | |||
444 | /* | ||
445 | * do_bridge_resource_split | ||
446 | * | ||
447 | * Returns zero or one node of resources that aren't in use | ||
448 | * | ||
449 | */ | ||
450 | static struct pci_resource *do_bridge_resource_split (struct pci_resource **head, u32 alignment) | ||
451 | { | ||
452 | struct pci_resource *prevnode = NULL; | ||
453 | struct pci_resource *node; | ||
454 | u32 rc; | ||
455 | u32 temp_dword; | ||
456 | |||
457 | if (!(*head)) | ||
458 | return(NULL); | ||
459 | |||
460 | rc = shpchp_resource_sort_and_combine(head); | ||
461 | |||
462 | if (rc) | ||
463 | return(NULL); | ||
464 | |||
465 | node = *head; | ||
466 | |||
467 | while (node->next) { | ||
468 | prevnode = node; | ||
469 | node = node->next; | ||
470 | kfree(prevnode); | ||
471 | } | ||
472 | |||
473 | if (node->length < alignment) { | ||
474 | kfree(node); | ||
475 | return(NULL); | ||
476 | } | ||
477 | |||
478 | if (node->base & (alignment - 1)) { | ||
479 | /* Short circuit if adjusted size is too small */ | ||
480 | temp_dword = (node->base | (alignment-1)) + 1; | ||
481 | if ((node->length - (temp_dword - node->base)) < alignment) { | ||
482 | kfree(node); | ||
483 | return(NULL); | ||
484 | } | ||
485 | |||
486 | node->length -= (temp_dword - node->base); | ||
487 | node->base = temp_dword; | ||
488 | } | ||
489 | |||
490 | if (node->length & (alignment - 1)) { | ||
491 | /* There's stuff in use after this node */ | ||
492 | kfree(node); | ||
493 | return(NULL); | ||
494 | } | ||
495 | |||
496 | return(node); | ||
497 | } | ||
498 | |||
499 | |||
500 | /* | ||
501 | * get_io_resource | ||
502 | * | ||
503 | * this function sorts the resource list by size and then | ||
504 | * returns the first node of "size" length that is not in the | ||
505 | * ISA aliasing window. If it finds a node larger than "size" | ||
506 | * it will split it up. | ||
507 | * | ||
508 | * size must be a power of two. | ||
509 | */ | ||
510 | static struct pci_resource *get_io_resource (struct pci_resource **head, u32 size) | ||
511 | { | ||
512 | struct pci_resource *prevnode; | ||
513 | struct pci_resource *node; | ||
514 | struct pci_resource *split_node = NULL; | ||
515 | u32 temp_dword; | ||
516 | |||
517 | if (!(*head)) | ||
518 | return(NULL); | ||
519 | |||
520 | if ( shpchp_resource_sort_and_combine(head) ) | ||
521 | return(NULL); | ||
522 | |||
523 | if ( sort_by_size(head) ) | ||
524 | return(NULL); | ||
525 | |||
526 | for (node = *head; node; node = node->next) { | ||
527 | if (node->length < size) | ||
528 | continue; | ||
529 | |||
530 | if (node->base & (size - 1)) { | ||
531 | /* This one isn't base aligned properly | ||
532 | so we'll make a new entry and split it up */ | ||
533 | temp_dword = (node->base | (size-1)) + 1; | ||
534 | |||
535 | /*/ Short circuit if adjusted size is too small */ | ||
536 | if ((node->length - (temp_dword - node->base)) < size) | ||
537 | continue; | ||
538 | |||
539 | split_node = kmalloc(sizeof(*split_node), GFP_KERNEL); | ||
540 | |||
541 | if (!split_node) | ||
542 | return(NULL); | ||
543 | |||
544 | split_node->base = node->base; | ||
545 | split_node->length = temp_dword - node->base; | ||
546 | node->base = temp_dword; | ||
547 | node->length -= split_node->length; | ||
548 | |||
549 | /* Put it in the list */ | ||
550 | split_node->next = node->next; | ||
551 | node->next = split_node; | ||
552 | } /* End of non-aligned base */ | ||
553 | |||
554 | /* Don't need to check if too small since we already did */ | ||
555 | if (node->length > size) { | ||
556 | /* This one is longer than we need | ||
557 | so we'll make a new entry and split it up */ | ||
558 | split_node = kmalloc(sizeof(*split_node), GFP_KERNEL); | ||
559 | |||
560 | if (!split_node) | ||
561 | return(NULL); | ||
562 | |||
563 | split_node->base = node->base + size; | ||
564 | split_node->length = node->length - size; | ||
565 | node->length = size; | ||
566 | |||
567 | /* Put it in the list */ | ||
568 | split_node->next = node->next; | ||
569 | node->next = split_node; | ||
570 | } /* End of too big on top end */ | ||
571 | |||
572 | /* For IO make sure it's not in the ISA aliasing space */ | ||
573 | if (node->base & 0x300L) | ||
574 | continue; | ||
575 | |||
576 | /* If we got here, then it is the right size | ||
577 | Now take it out of the list */ | ||
578 | if (*head == node) { | ||
579 | *head = node->next; | ||
580 | } else { | ||
581 | prevnode = *head; | ||
582 | while (prevnode->next != node) | ||
583 | prevnode = prevnode->next; | ||
584 | |||
585 | prevnode->next = node->next; | ||
586 | } | ||
587 | node->next = NULL; | ||
588 | /* Stop looping */ | ||
589 | break; | ||
590 | } | ||
591 | |||
592 | return(node); | ||
593 | } | ||
594 | |||
595 | |||
596 | /* | ||
597 | * get_max_resource | ||
598 | * | ||
599 | * Gets the largest node that is at least "size" big from the | ||
600 | * list pointed to by head. It aligns the node on top and bottom | ||
601 | * to "size" alignment before returning it. | ||
602 | * J.I. modified to put max size limits of; 64M->32M->16M->8M->4M->1M | ||
603 | * This is needed to avoid allocating entire ACPI _CRS res to one child bridge/slot. | ||
604 | */ | ||
605 | static struct pci_resource *get_max_resource (struct pci_resource **head, u32 size) | ||
606 | { | ||
607 | struct pci_resource *max; | ||
608 | struct pci_resource *temp; | ||
609 | struct pci_resource *split_node; | ||
610 | u32 temp_dword; | ||
611 | u32 max_size[] = { 0x4000000, 0x2000000, 0x1000000, 0x0800000, 0x0400000, 0x0200000, 0x0100000, 0x00 }; | ||
612 | int i; | ||
613 | |||
614 | if (!(*head)) | ||
615 | return(NULL); | ||
616 | |||
617 | if (shpchp_resource_sort_and_combine(head)) | ||
618 | return(NULL); | ||
619 | |||
620 | if (sort_by_max_size(head)) | ||
621 | return(NULL); | ||
622 | |||
623 | for (max = *head;max; max = max->next) { | ||
624 | |||
625 | /* If not big enough we could probably just bail, | ||
626 | instead we'll continue to the next. */ | ||
627 | if (max->length < size) | ||
628 | continue; | ||
629 | |||
630 | if (max->base & (size - 1)) { | ||
631 | /* This one isn't base aligned properly | ||
632 | so we'll make a new entry and split it up */ | ||
633 | temp_dword = (max->base | (size-1)) + 1; | ||
634 | |||
635 | /* Short circuit if adjusted size is too small */ | ||
636 | if ((max->length - (temp_dword - max->base)) < size) | ||
637 | continue; | ||
638 | |||
639 | split_node = kmalloc(sizeof(*split_node), GFP_KERNEL); | ||
640 | |||
641 | if (!split_node) | ||
642 | return(NULL); | ||
643 | |||
644 | split_node->base = max->base; | ||
645 | split_node->length = temp_dword - max->base; | ||
646 | max->base = temp_dword; | ||
647 | max->length -= split_node->length; | ||
648 | |||
649 | /* Put it next in the list */ | ||
650 | split_node->next = max->next; | ||
651 | max->next = split_node; | ||
652 | } | ||
653 | |||
654 | if ((max->base + max->length) & (size - 1)) { | ||
655 | /* This one isn't end aligned properly at the top | ||
656 | so we'll make a new entry and split it up */ | ||
657 | split_node = kmalloc(sizeof(*split_node), GFP_KERNEL); | ||
658 | |||
659 | if (!split_node) | ||
660 | return(NULL); | ||
661 | temp_dword = ((max->base + max->length) & ~(size - 1)); | ||
662 | split_node->base = temp_dword; | ||
663 | split_node->length = max->length + max->base | ||
664 | - split_node->base; | ||
665 | max->length -= split_node->length; | ||
666 | |||
667 | /* Put it in the list */ | ||
668 | split_node->next = max->next; | ||
669 | max->next = split_node; | ||
670 | } | ||
671 | |||
672 | /* Make sure it didn't shrink too much when we aligned it */ | ||
673 | if (max->length < size) | ||
674 | continue; | ||
675 | |||
676 | for ( i = 0; max_size[i] > size; i++) { | ||
677 | if (max->length > max_size[i]) { | ||
678 | split_node = kmalloc(sizeof(*split_node), | ||
679 | GFP_KERNEL); | ||
680 | if (!split_node) | ||
681 | break; /* return (NULL); */ | ||
682 | split_node->base = max->base + max_size[i]; | ||
683 | split_node->length = max->length - max_size[i]; | ||
684 | max->length = max_size[i]; | ||
685 | /* Put it next in the list */ | ||
686 | split_node->next = max->next; | ||
687 | max->next = split_node; | ||
688 | break; | ||
689 | } | ||
690 | } | ||
691 | |||
692 | /* Now take it out of the list */ | ||
693 | temp = (struct pci_resource*) *head; | ||
694 | if (temp == max) { | ||
695 | *head = max->next; | ||
696 | } else { | ||
697 | while (temp && temp->next != max) { | ||
698 | temp = temp->next; | ||
699 | } | ||
700 | |||
701 | temp->next = max->next; | ||
702 | } | ||
703 | |||
704 | max->next = NULL; | ||
705 | return(max); | ||
706 | } | ||
707 | |||
708 | /* If we get here, we couldn't find one */ | ||
709 | return(NULL); | ||
710 | } | ||
711 | |||
712 | |||
713 | /* | ||
714 | * get_resource | ||
715 | * | ||
716 | * this function sorts the resource list by size and then | ||
717 | * returns the first node of "size" length. If it finds a node | ||
718 | * larger than "size" it will split it up. | ||
719 | * | ||
720 | * size must be a power of two. | ||
721 | */ | ||
722 | static struct pci_resource *get_resource (struct pci_resource **head, u32 size) | ||
723 | { | ||
724 | struct pci_resource *prevnode; | ||
725 | struct pci_resource *node; | ||
726 | struct pci_resource *split_node; | ||
727 | u32 temp_dword; | ||
728 | |||
729 | if (!(*head)) | ||
730 | return(NULL); | ||
731 | |||
732 | if ( shpchp_resource_sort_and_combine(head) ) | ||
733 | return(NULL); | ||
734 | |||
735 | if ( sort_by_size(head) ) | ||
736 | return(NULL); | ||
737 | |||
738 | for (node = *head; node; node = node->next) { | ||
739 | dbg("%s: req_size =0x%x node=%p, base=0x%x, length=0x%x\n", | ||
740 | __FUNCTION__, size, node, node->base, node->length); | ||
741 | if (node->length < size) | ||
742 | continue; | ||
743 | |||
744 | if (node->base & (size - 1)) { | ||
745 | dbg("%s: not aligned\n", __FUNCTION__); | ||
746 | /* this one isn't base aligned properly | ||
747 | so we'll make a new entry and split it up */ | ||
748 | temp_dword = (node->base | (size-1)) + 1; | ||
749 | |||
750 | /* Short circuit if adjusted size is too small */ | ||
751 | if ((node->length - (temp_dword - node->base)) < size) | ||
752 | continue; | ||
753 | |||
754 | split_node = kmalloc(sizeof(*split_node), GFP_KERNEL); | ||
755 | |||
756 | if (!split_node) | ||
757 | return(NULL); | ||
758 | |||
759 | split_node->base = node->base; | ||
760 | split_node->length = temp_dword - node->base; | ||
761 | node->base = temp_dword; | ||
762 | node->length -= split_node->length; | ||
763 | |||
764 | /* Put it in the list */ | ||
765 | split_node->next = node->next; | ||
766 | node->next = split_node; | ||
767 | } /* End of non-aligned base */ | ||
768 | |||
769 | /* Don't need to check if too small since we already did */ | ||
770 | if (node->length > size) { | ||
771 | dbg("%s: too big\n", __FUNCTION__); | ||
772 | /* this one is longer than we need | ||
773 | so we'll make a new entry and split it up */ | ||
774 | split_node = kmalloc(sizeof(*split_node), GFP_KERNEL); | ||
775 | |||
776 | if (!split_node) | ||
777 | return(NULL); | ||
778 | |||
779 | split_node->base = node->base + size; | ||
780 | split_node->length = node->length - size; | ||
781 | node->length = size; | ||
782 | |||
783 | /* Put it in the list */ | ||
784 | split_node->next = node->next; | ||
785 | node->next = split_node; | ||
786 | } /* End of too big on top end */ | ||
787 | |||
788 | dbg("%s: got one!!!\n", __FUNCTION__); | ||
789 | /* If we got here, then it is the right size | ||
790 | Now take it out of the list */ | ||
791 | if (*head == node) { | ||
792 | *head = node->next; | ||
793 | } else { | ||
794 | prevnode = *head; | ||
795 | while (prevnode->next != node) | ||
796 | prevnode = prevnode->next; | ||
797 | |||
798 | prevnode->next = node->next; | ||
799 | } | ||
800 | node->next = NULL; | ||
801 | /* Stop looping */ | ||
802 | break; | ||
803 | } | ||
804 | return(node); | ||
805 | } | ||
806 | |||
807 | |||
808 | /* | ||
809 | * shpchp_resource_sort_and_combine | ||
810 | * | ||
811 | * Sorts all of the nodes in the list in ascending order by | ||
812 | * their base addresses. Also does garbage collection by | ||
813 | * combining adjacent nodes. | ||
814 | * | ||
815 | * returns 0 if success | ||
816 | */ | ||
817 | int shpchp_resource_sort_and_combine(struct pci_resource **head) | ||
818 | { | ||
819 | struct pci_resource *node1; | ||
820 | struct pci_resource *node2; | ||
821 | int out_of_order = 1; | ||
822 | |||
823 | dbg("%s: head = %p, *head = %p\n", __FUNCTION__, head, *head); | ||
824 | |||
825 | if (!(*head)) | ||
826 | return(1); | ||
827 | |||
828 | dbg("*head->next = %p\n",(*head)->next); | ||
829 | |||
830 | if (!(*head)->next) | ||
831 | return(0); /* only one item on the list, already sorted! */ | ||
832 | |||
833 | dbg("*head->base = 0x%x\n",(*head)->base); | ||
834 | dbg("*head->next->base = 0x%x\n",(*head)->next->base); | ||
835 | while (out_of_order) { | ||
836 | out_of_order = 0; | ||
837 | |||
838 | /* Special case for swapping list head */ | ||
839 | if (((*head)->next) && | ||
840 | ((*head)->base > (*head)->next->base)) { | ||
841 | node1 = *head; | ||
842 | (*head) = (*head)->next; | ||
843 | node1->next = (*head)->next; | ||
844 | (*head)->next = node1; | ||
845 | out_of_order++; | ||
846 | } | ||
847 | |||
848 | node1 = (*head); | ||
849 | |||
850 | while (node1->next && node1->next->next) { | ||
851 | if (node1->next->base > node1->next->next->base) { | ||
852 | out_of_order++; | ||
853 | node2 = node1->next; | ||
854 | node1->next = node1->next->next; | ||
855 | node1 = node1->next; | ||
856 | node2->next = node1->next; | ||
857 | node1->next = node2; | ||
858 | } else | ||
859 | node1 = node1->next; | ||
860 | } | ||
861 | } /* End of out_of_order loop */ | ||
862 | |||
863 | node1 = *head; | ||
864 | |||
865 | while (node1 && node1->next) { | ||
866 | if ((node1->base + node1->length) == node1->next->base) { | ||
867 | /* Combine */ | ||
868 | dbg("8..\n"); | ||
869 | node1->length += node1->next->length; | ||
870 | node2 = node1->next; | ||
871 | node1->next = node1->next->next; | ||
872 | kfree(node2); | ||
873 | } else | ||
874 | node1 = node1->next; | ||
875 | } | ||
876 | |||
877 | return(0); | ||
878 | } | ||
879 | |||
880 | |||
881 | /** | ||
882 | * shpchp_slot_create - Creates a node and adds it to the proper bus. | ||
883 | * @busnumber - bus where new node is to be located | ||
884 | * | ||
885 | * Returns pointer to the new node or NULL if unsuccessful | ||
886 | */ | ||
887 | struct pci_func *shpchp_slot_create(u8 busnumber) | ||
888 | { | ||
889 | struct pci_func *new_slot; | ||
890 | struct pci_func *next; | ||
891 | |||
892 | new_slot = kmalloc(sizeof(*new_slot), GFP_KERNEL); | ||
893 | |||
894 | if (new_slot == NULL) { | ||
895 | return(new_slot); | ||
896 | } | ||
897 | |||
898 | memset(new_slot, 0, sizeof(struct pci_func)); | ||
899 | |||
900 | new_slot->next = NULL; | ||
901 | new_slot->configured = 1; | ||
902 | |||
903 | if (shpchp_slot_list[busnumber] == NULL) { | ||
904 | shpchp_slot_list[busnumber] = new_slot; | ||
905 | } else { | ||
906 | next = shpchp_slot_list[busnumber]; | ||
907 | while (next->next != NULL) | ||
908 | next = next->next; | ||
909 | next->next = new_slot; | ||
910 | } | ||
911 | return(new_slot); | ||
912 | } | ||
913 | |||
914 | |||
915 | /* | ||
916 | * slot_remove - Removes a node from the linked list of slots. | ||
917 | * @old_slot: slot to remove | ||
918 | * | ||
919 | * Returns 0 if successful, !0 otherwise. | ||
920 | */ | ||
921 | static int slot_remove(struct pci_func * old_slot) | ||
922 | { | ||
923 | struct pci_func *next; | ||
924 | |||
925 | if (old_slot == NULL) | ||
926 | return(1); | ||
927 | |||
928 | next = shpchp_slot_list[old_slot->bus]; | ||
929 | |||
930 | if (next == NULL) { | ||
931 | return(1); | ||
932 | } | ||
933 | |||
934 | if (next == old_slot) { | ||
935 | shpchp_slot_list[old_slot->bus] = old_slot->next; | ||
936 | shpchp_destroy_board_resources(old_slot); | ||
937 | kfree(old_slot); | ||
938 | return(0); | ||
939 | } | ||
940 | |||
941 | while ((next->next != old_slot) && (next->next != NULL)) { | ||
942 | next = next->next; | ||
943 | } | ||
944 | |||
945 | if (next->next == old_slot) { | ||
946 | next->next = old_slot->next; | ||
947 | shpchp_destroy_board_resources(old_slot); | ||
948 | kfree(old_slot); | ||
949 | return(0); | ||
950 | } else | ||
951 | return(2); | ||
952 | } | ||
953 | |||
954 | |||
955 | /** | ||
956 | * bridge_slot_remove - Removes a node from the linked list of slots. | ||
957 | * @bridge: bridge to remove | ||
958 | * | ||
959 | * Returns 0 if successful, !0 otherwise. | ||
960 | */ | ||
961 | static int bridge_slot_remove(struct pci_func *bridge) | ||
962 | { | ||
963 | u8 subordinateBus, secondaryBus; | ||
964 | u8 tempBus; | ||
965 | struct pci_func *next; | ||
966 | |||
967 | if (bridge == NULL) | ||
968 | return(1); | ||
969 | |||
970 | secondaryBus = (bridge->config_space[0x06] >> 8) & 0xFF; | ||
971 | subordinateBus = (bridge->config_space[0x06] >> 16) & 0xFF; | ||
972 | |||
973 | for (tempBus = secondaryBus; tempBus <= subordinateBus; tempBus++) { | ||
974 | next = shpchp_slot_list[tempBus]; | ||
975 | |||
976 | while (!slot_remove(next)) { | ||
977 | next = shpchp_slot_list[tempBus]; | ||
978 | } | ||
979 | } | ||
980 | |||
981 | next = shpchp_slot_list[bridge->bus]; | ||
982 | |||
983 | if (next == NULL) { | ||
984 | return(1); | ||
985 | } | ||
986 | |||
987 | if (next == bridge) { | ||
988 | shpchp_slot_list[bridge->bus] = bridge->next; | ||
989 | kfree(bridge); | ||
990 | return(0); | ||
991 | } | ||
992 | |||
993 | while ((next->next != bridge) && (next->next != NULL)) { | ||
994 | next = next->next; | ||
995 | } | ||
996 | |||
997 | if (next->next == bridge) { | ||
998 | next->next = bridge->next; | ||
999 | kfree(bridge); | ||
1000 | return(0); | ||
1001 | } else | ||
1002 | return(2); | ||
1003 | } | ||
1004 | |||
1005 | |||
1006 | /** | ||
1007 | * shpchp_slot_find - Looks for a node by bus, and device, multiple functions accessed | ||
1008 | * @bus: bus to find | ||
1009 | * @device: device to find | ||
1010 | * @index: is 0 for first function found, 1 for the second... | ||
1011 | * | ||
1012 | * Returns pointer to the node if successful, %NULL otherwise. | ||
1013 | */ | ||
1014 | struct pci_func *shpchp_slot_find(u8 bus, u8 device, u8 index) | ||
1015 | { | ||
1016 | int found = -1; | ||
1017 | struct pci_func *func; | ||
1018 | |||
1019 | func = shpchp_slot_list[bus]; | ||
1020 | |||
1021 | if ((func == NULL) || ((func->device == device) && (index == 0))) | ||
1022 | return(func); | ||
1023 | |||
1024 | if (func->device == device) | ||
1025 | found++; | ||
1026 | |||
1027 | while (func->next != NULL) { | ||
1028 | func = func->next; | ||
1029 | |||
1030 | if (func->device == device) | ||
1031 | found++; | ||
1032 | |||
1033 | if (found == index) | ||
1034 | return(func); | ||
1035 | } | ||
1036 | |||
1037 | return(NULL); | ||
1038 | } | ||
1039 | |||
1040 | static int is_bridge(struct pci_func * func) | ||
1041 | { | ||
1042 | /* Check the header type */ | ||
1043 | if (((func->config_space[0x03] >> 16) & 0xFF) == 0x01) | ||
1044 | return 1; | ||
1045 | else | ||
1046 | return 0; | ||
1047 | } | ||
1048 | |||
1049 | |||
1050 | /* The following routines constitute the bulk of the | ||
1051 | hotplug controller logic | ||
1052 | */ | ||
1053 | static u32 change_bus_speed(struct controller *ctrl, struct slot *p_slot, enum pci_bus_speed speed) | ||
1054 | { | ||
1055 | u32 rc = 0; | ||
1056 | |||
1057 | dbg("%s: change to speed %d\n", __FUNCTION__, speed); | ||
1058 | down(&ctrl->crit_sect); | ||
1059 | if ((rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, speed))) { | ||
1060 | err("%s: Issue of set bus speed mode command failed\n", __FUNCTION__); | ||
1061 | up(&ctrl->crit_sect); | ||
1062 | return WRONG_BUS_FREQUENCY; | ||
1063 | } | ||
1064 | wait_for_ctrl_irq (ctrl); | ||
1065 | |||
1066 | if ((rc = p_slot->hpc_ops->check_cmd_status(ctrl))) { | ||
1067 | err("%s: Can't set bus speed/mode in the case of adapter & bus mismatch\n", | ||
1068 | __FUNCTION__); | ||
1069 | err("%s: Error code (%d)\n", __FUNCTION__, rc); | ||
1070 | up(&ctrl->crit_sect); | ||
1071 | return WRONG_BUS_FREQUENCY; | ||
1072 | } | ||
1073 | up(&ctrl->crit_sect); | ||
1074 | return rc; | ||
1075 | } | ||
1076 | |||
1077 | static u32 fix_bus_speed(struct controller *ctrl, struct slot *pslot, u8 flag, | ||
1078 | enum pci_bus_speed asp, enum pci_bus_speed bsp, enum pci_bus_speed msp) | ||
1079 | { | ||
1080 | u32 rc = 0; | ||
1081 | |||
1082 | if (flag != 0) { /* Other slots on the same bus are occupied */ | ||
1083 | if ( asp < bsp ) { | ||
1084 | err("%s: speed of bus %x and adapter %x mismatch\n", __FUNCTION__, bsp, asp); | ||
1085 | return WRONG_BUS_FREQUENCY; | ||
1086 | } | ||
1087 | } else { | ||
1088 | /* Other slots on the same bus are empty */ | ||
1089 | if (msp == bsp) { | ||
1090 | /* if adapter_speed >= bus_speed, do nothing */ | ||
1091 | if (asp < bsp) { | ||
1092 | /* | ||
1093 | * Try to lower bus speed to accommodate the adapter if other slots | ||
1094 | * on the same controller are empty | ||
1095 | */ | ||
1096 | if ((rc = change_bus_speed(ctrl, pslot, asp))) | ||
1097 | return rc; | ||
1098 | } | ||
1099 | } else { | ||
1100 | if (asp < msp) { | ||
1101 | if ((rc = change_bus_speed(ctrl, pslot, asp))) | ||
1102 | return rc; | ||
1103 | } else { | ||
1104 | if ((rc = change_bus_speed(ctrl, pslot, msp))) | ||
1105 | return rc; | ||
1106 | } | ||
1107 | } | ||
1108 | } | ||
1109 | return rc; | ||
1110 | } | ||
1111 | |||
1112 | /** | ||
1113 | * board_added - Called after a board has been added to the system. | ||
1114 | * | ||
1115 | * Turns power on for the board | ||
1116 | * Configures board | ||
1117 | * | ||
1118 | */ | ||
1119 | static u32 board_added(struct pci_func * func, struct controller * ctrl) | ||
1120 | { | ||
1121 | u8 hp_slot; | ||
1122 | u8 slots_not_empty = 0; | ||
1123 | int index; | ||
1124 | u32 temp_register = 0xFFFFFFFF; | ||
1125 | u32 retval, rc = 0; | ||
1126 | struct pci_func *new_func = NULL; | ||
1127 | struct slot *p_slot; | ||
1128 | struct resource_lists res_lists; | ||
1129 | enum pci_bus_speed adapter_speed, bus_speed, max_bus_speed; | ||
1130 | u8 pi, mode; | ||
1131 | |||
1132 | p_slot = shpchp_find_slot(ctrl, func->device); | ||
1133 | hp_slot = func->device - ctrl->slot_device_offset; | ||
1134 | |||
1135 | dbg("%s: func->device, slot_offset, hp_slot = %d, %d ,%d\n", __FUNCTION__, func->device, ctrl->slot_device_offset, hp_slot); | ||
1136 | |||
1137 | /* Wait for exclusive access to hardware */ | ||
1138 | down(&ctrl->crit_sect); | ||
1139 | |||
1140 | /* Power on slot without connecting to bus */ | ||
1141 | rc = p_slot->hpc_ops->power_on_slot(p_slot); | ||
1142 | if (rc) { | ||
1143 | err("%s: Failed to power on slot\n", __FUNCTION__); | ||
1144 | /* Done with exclusive hardware access */ | ||
1145 | up(&ctrl->crit_sect); | ||
1146 | return -1; | ||
1147 | } | ||
1148 | |||
1149 | /* Wait for the command to complete */ | ||
1150 | wait_for_ctrl_irq (ctrl); | ||
1151 | |||
1152 | rc = p_slot->hpc_ops->check_cmd_status(ctrl); | ||
1153 | if (rc) { | ||
1154 | err("%s: Failed to power on slot, error code(%d)\n", __FUNCTION__, rc); | ||
1155 | /* Done with exclusive hardware access */ | ||
1156 | up(&ctrl->crit_sect); | ||
1157 | return -1; | ||
1158 | } | ||
1159 | |||
1160 | |||
1161 | if ((ctrl->pci_dev->vendor == 0x8086) && (ctrl->pci_dev->device == 0x0332)) { | ||
1162 | if (slots_not_empty) | ||
1163 | return WRONG_BUS_FREQUENCY; | ||
1164 | |||
1165 | if ((rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, PCI_SPEED_33MHz))) { | ||
1166 | err("%s: Issue of set bus speed mode command failed\n", __FUNCTION__); | ||
1167 | up(&ctrl->crit_sect); | ||
1168 | return WRONG_BUS_FREQUENCY; | ||
1169 | } | ||
1170 | wait_for_ctrl_irq (ctrl); | ||
1171 | |||
1172 | if ((rc = p_slot->hpc_ops->check_cmd_status(ctrl))) { | ||
1173 | err("%s: Can't set bus speed/mode in the case of adapter & bus mismatch\n", | ||
1174 | __FUNCTION__); | ||
1175 | err("%s: Error code (%d)\n", __FUNCTION__, rc); | ||
1176 | up(&ctrl->crit_sect); | ||
1177 | return WRONG_BUS_FREQUENCY; | ||
1178 | } | ||
1179 | /* turn on board, blink green LED, turn off Amber LED */ | ||
1180 | if ((rc = p_slot->hpc_ops->slot_enable(p_slot))) { | ||
1181 | err("%s: Issue of Slot Enable command failed\n", __FUNCTION__); | ||
1182 | up(&ctrl->crit_sect); | ||
1183 | return rc; | ||
1184 | } | ||
1185 | wait_for_ctrl_irq (ctrl); | ||
1186 | |||
1187 | if ((rc = p_slot->hpc_ops->check_cmd_status(ctrl))) { | ||
1188 | err("%s: Failed to enable slot, error code(%d)\n", __FUNCTION__, rc); | ||
1189 | up(&ctrl->crit_sect); | ||
1190 | return rc; | ||
1191 | } | ||
1192 | } | ||
1193 | |||
1194 | rc = p_slot->hpc_ops->get_adapter_speed(p_slot, &adapter_speed); | ||
1195 | /* 0 = PCI 33Mhz, 1 = PCI 66 Mhz, 2 = PCI-X 66 PA, 4 = PCI-X 66 ECC, */ | ||
1196 | /* 5 = PCI-X 133 PA, 7 = PCI-X 133 ECC, 0xa = PCI-X 133 Mhz 266, */ | ||
1197 | /* 0xd = PCI-X 133 Mhz 533 */ | ||
1198 | /* This encoding is different from the one used in cur_bus_speed & */ | ||
1199 | /* max_bus_speed */ | ||
1200 | |||
1201 | if (rc || adapter_speed == PCI_SPEED_UNKNOWN) { | ||
1202 | err("%s: Can't get adapter speed or bus mode mismatch\n", __FUNCTION__); | ||
1203 | /* Done with exclusive hardware access */ | ||
1204 | up(&ctrl->crit_sect); | ||
1205 | return WRONG_BUS_FREQUENCY; | ||
1206 | } | ||
1207 | |||
1208 | rc = p_slot->hpc_ops->get_cur_bus_speed(p_slot, &bus_speed); | ||
1209 | if (rc || bus_speed == PCI_SPEED_UNKNOWN) { | ||
1210 | err("%s: Can't get bus operation speed\n", __FUNCTION__); | ||
1211 | /* Done with exclusive hardware access */ | ||
1212 | up(&ctrl->crit_sect); | ||
1213 | return WRONG_BUS_FREQUENCY; | ||
1214 | } | ||
1215 | |||
1216 | rc = p_slot->hpc_ops->get_max_bus_speed(p_slot, &max_bus_speed); | ||
1217 | if (rc || max_bus_speed == PCI_SPEED_UNKNOWN) { | ||
1218 | err("%s: Can't get max bus operation speed\n", __FUNCTION__); | ||
1219 | max_bus_speed = bus_speed; | ||
1220 | } | ||
1221 | |||
1222 | /* Done with exclusive hardware access */ | ||
1223 | up(&ctrl->crit_sect); | ||
1224 | |||
1225 | if ((rc = p_slot->hpc_ops->get_prog_int(p_slot, &pi))) { | ||
1226 | err("%s: Can't get controller programming interface, set it to 1\n", __FUNCTION__); | ||
1227 | pi = 1; | ||
1228 | } | ||
1229 | |||
1230 | /* Check if there are other slots or devices on the same bus */ | ||
1231 | if (!list_empty(&ctrl->pci_dev->subordinate->devices)) | ||
1232 | slots_not_empty = 1; | ||
1233 | |||
1234 | dbg("%s: slots_not_empty %d, pi %d\n", __FUNCTION__, | ||
1235 | slots_not_empty, pi); | ||
1236 | dbg("adapter_speed %d, bus_speed %d, max_bus_speed %d\n", | ||
1237 | adapter_speed, bus_speed, max_bus_speed); | ||
1238 | |||
1239 | if (pi == 2) { | ||
1240 | dbg("%s: In PI = %d\n", __FUNCTION__, pi); | ||
1241 | if ((rc = p_slot->hpc_ops->get_mode1_ECC_cap(p_slot, &mode))) { | ||
1242 | err("%s: Can't get Mode1_ECC, set mode to 0\n", __FUNCTION__); | ||
1243 | mode = 0; | ||
1244 | } | ||
1245 | |||
1246 | switch (adapter_speed) { | ||
1247 | case PCI_SPEED_133MHz_PCIX_533: | ||
1248 | case PCI_SPEED_133MHz_PCIX_266: | ||
1249 | if ((bus_speed != adapter_speed) && | ||
1250 | ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed)))) | ||
1251 | return rc; | ||
1252 | break; | ||
1253 | case PCI_SPEED_133MHz_PCIX_ECC: | ||
1254 | case PCI_SPEED_133MHz_PCIX: | ||
1255 | if (mode) { /* Bus - Mode 1 ECC */ | ||
1256 | if ((bus_speed != 0x7) && | ||
1257 | ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed)))) | ||
1258 | return rc; | ||
1259 | } else { | ||
1260 | if ((bus_speed != 0x4) && | ||
1261 | ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed)))) | ||
1262 | return rc; | ||
1263 | } | ||
1264 | break; | ||
1265 | case PCI_SPEED_66MHz_PCIX_ECC: | ||
1266 | case PCI_SPEED_66MHz_PCIX: | ||
1267 | if (mode) { /* Bus - Mode 1 ECC */ | ||
1268 | if ((bus_speed != 0x5) && | ||
1269 | ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed)))) | ||
1270 | return rc; | ||
1271 | } else { | ||
1272 | if ((bus_speed != 0x2) && | ||
1273 | ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed)))) | ||
1274 | return rc; | ||
1275 | } | ||
1276 | break; | ||
1277 | case PCI_SPEED_66MHz: | ||
1278 | if ((bus_speed != 0x1) && | ||
1279 | ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed)))) | ||
1280 | return rc; | ||
1281 | break; | ||
1282 | case PCI_SPEED_33MHz: | ||
1283 | if (bus_speed > 0x0) { | ||
1284 | if (slots_not_empty == 0) { | ||
1285 | if ((rc = change_bus_speed(ctrl, p_slot, adapter_speed))) | ||
1286 | return rc; | ||
1287 | } else { | ||
1288 | err("%s: speed of bus %x and adapter %x mismatch\n", __FUNCTION__, bus_speed, adapter_speed); | ||
1289 | return WRONG_BUS_FREQUENCY; | ||
1290 | } | ||
1291 | } | ||
1292 | break; | ||
1293 | default: | ||
1294 | err("%s: speed of bus %x and adapter %x mismatch\n", __FUNCTION__, bus_speed, adapter_speed); | ||
1295 | return WRONG_BUS_FREQUENCY; | ||
1296 | } | ||
1297 | } else { | ||
1298 | /* If adpater_speed == bus_speed, nothing to do here */ | ||
1299 | dbg("%s: In PI = %d\n", __FUNCTION__, pi); | ||
1300 | if ((adapter_speed != bus_speed) && | ||
1301 | ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed)))) | ||
1302 | return rc; | ||
1303 | } | ||
1304 | |||
1305 | down(&ctrl->crit_sect); | ||
1306 | /* turn on board, blink green LED, turn off Amber LED */ | ||
1307 | if ((rc = p_slot->hpc_ops->slot_enable(p_slot))) { | ||
1308 | err("%s: Issue of Slot Enable command failed\n", __FUNCTION__); | ||
1309 | up(&ctrl->crit_sect); | ||
1310 | return rc; | ||
1311 | } | ||
1312 | wait_for_ctrl_irq (ctrl); | ||
1313 | |||
1314 | if ((rc = p_slot->hpc_ops->check_cmd_status(ctrl))) { | ||
1315 | err("%s: Failed to enable slot, error code(%d)\n", __FUNCTION__, rc); | ||
1316 | up(&ctrl->crit_sect); | ||
1317 | return rc; | ||
1318 | } | ||
1319 | |||
1320 | up(&ctrl->crit_sect); | ||
1321 | |||
1322 | /* Wait for ~1 second */ | ||
1323 | dbg("%s: before long_delay\n", __FUNCTION__); | ||
1324 | wait_for_ctrl_irq (ctrl); | ||
1325 | dbg("%s: after long_delay\n", __FUNCTION__); | ||
1326 | |||
1327 | dbg("%s: func status = %x\n", __FUNCTION__, func->status); | ||
1328 | /* Check for a power fault */ | ||
1329 | if (func->status == 0xFF) { | ||
1330 | /* power fault occurred, but it was benign */ | ||
1331 | temp_register = 0xFFFFFFFF; | ||
1332 | dbg("%s: temp register set to %x by power fault\n", __FUNCTION__, temp_register); | ||
1333 | rc = POWER_FAILURE; | ||
1334 | func->status = 0; | ||
1335 | } else { | ||
1336 | /* Get vendor/device ID u32 */ | ||
1337 | rc = pci_bus_read_config_dword (ctrl->pci_dev->subordinate, PCI_DEVFN(func->device, func->function), | ||
1338 | PCI_VENDOR_ID, &temp_register); | ||
1339 | dbg("%s: pci_bus_read_config_dword returns %d\n", __FUNCTION__, rc); | ||
1340 | dbg("%s: temp_register is %x\n", __FUNCTION__, temp_register); | ||
1341 | |||
1342 | if (rc != 0) { | ||
1343 | /* Something's wrong here */ | ||
1344 | temp_register = 0xFFFFFFFF; | ||
1345 | dbg("%s: temp register set to %x by error\n", __FUNCTION__, temp_register); | ||
1346 | } | ||
1347 | /* Preset return code. It will be changed later if things go okay. */ | ||
1348 | rc = NO_ADAPTER_PRESENT; | ||
1349 | } | ||
1350 | |||
1351 | /* All F's is an empty slot or an invalid board */ | ||
1352 | if (temp_register != 0xFFFFFFFF) { /* Check for a board in the slot */ | ||
1353 | res_lists.io_head = ctrl->io_head; | ||
1354 | res_lists.mem_head = ctrl->mem_head; | ||
1355 | res_lists.p_mem_head = ctrl->p_mem_head; | ||
1356 | res_lists.bus_head = ctrl->bus_head; | ||
1357 | res_lists.irqs = NULL; | ||
1358 | |||
1359 | rc = configure_new_device(ctrl, func, 0, &res_lists, 0, 0); | ||
1360 | dbg("%s: back from configure_new_device\n", __FUNCTION__); | ||
1361 | |||
1362 | ctrl->io_head = res_lists.io_head; | ||
1363 | ctrl->mem_head = res_lists.mem_head; | ||
1364 | ctrl->p_mem_head = res_lists.p_mem_head; | ||
1365 | ctrl->bus_head = res_lists.bus_head; | ||
1366 | |||
1367 | shpchp_resource_sort_and_combine(&(ctrl->mem_head)); | ||
1368 | shpchp_resource_sort_and_combine(&(ctrl->p_mem_head)); | ||
1369 | shpchp_resource_sort_and_combine(&(ctrl->io_head)); | ||
1370 | shpchp_resource_sort_and_combine(&(ctrl->bus_head)); | ||
1371 | |||
1372 | if (rc) { | ||
1373 | /* Wait for exclusive access to hardware */ | ||
1374 | down(&ctrl->crit_sect); | ||
1375 | |||
1376 | /* turn off slot, turn on Amber LED, turn off Green LED */ | ||
1377 | retval = p_slot->hpc_ops->slot_disable(p_slot); | ||
1378 | if (retval) { | ||
1379 | err("%s: Issue of Slot Enable command failed\n", __FUNCTION__); | ||
1380 | /* Done with exclusive hardware access */ | ||
1381 | up(&ctrl->crit_sect); | ||
1382 | return retval; | ||
1383 | } | ||
1384 | /* Wait for the command to complete */ | ||
1385 | wait_for_ctrl_irq (ctrl); | ||
1386 | |||
1387 | retval = p_slot->hpc_ops->check_cmd_status(ctrl); | ||
1388 | if (retval) { | ||
1389 | err("%s: Failed to disable slot, error code(%d)\n", __FUNCTION__, retval); | ||
1390 | /* Done with exclusive hardware access */ | ||
1391 | up(&ctrl->crit_sect); | ||
1392 | return retval; | ||
1393 | } | ||
1394 | |||
1395 | /* Done with exclusive hardware access */ | ||
1396 | up(&ctrl->crit_sect); | ||
1397 | |||
1398 | return(rc); | ||
1399 | } | ||
1400 | shpchp_save_slot_config(ctrl, func); | ||
1401 | |||
1402 | func->status = 0; | ||
1403 | func->switch_save = 0x10; | ||
1404 | func->is_a_board = 0x01; | ||
1405 | func->pwr_save = 1; | ||
1406 | |||
1407 | /* Next, we will instantiate the linux pci_dev structures | ||
1408 | * (with appropriate driver notification, if already present) | ||
1409 | */ | ||
1410 | index = 0; | ||
1411 | do { | ||
1412 | new_func = shpchp_slot_find(ctrl->slot_bus, func->device, index++); | ||
1413 | if (new_func && !new_func->pci_dev) { | ||
1414 | dbg("%s:call pci_hp_configure_dev\n", __FUNCTION__); | ||
1415 | shpchp_configure_device(ctrl, new_func); | ||
1416 | } | ||
1417 | } while (new_func); | ||
1418 | |||
1419 | /* Wait for exclusive access to hardware */ | ||
1420 | down(&ctrl->crit_sect); | ||
1421 | |||
1422 | p_slot->hpc_ops->green_led_on(p_slot); | ||
1423 | |||
1424 | /* Wait for the command to complete */ | ||
1425 | wait_for_ctrl_irq (ctrl); | ||
1426 | |||
1427 | |||
1428 | /* Done with exclusive hardware access */ | ||
1429 | up(&ctrl->crit_sect); | ||
1430 | |||
1431 | } else { | ||
1432 | /* Wait for exclusive access to hardware */ | ||
1433 | down(&ctrl->crit_sect); | ||
1434 | |||
1435 | /* turn off slot, turn on Amber LED, turn off Green LED */ | ||
1436 | rc = p_slot->hpc_ops->slot_disable(p_slot); | ||
1437 | if (rc) { | ||
1438 | err("%s: Issue of Slot Disable command failed\n", __FUNCTION__); | ||
1439 | /* Done with exclusive hardware access */ | ||
1440 | up(&ctrl->crit_sect); | ||
1441 | return rc; | ||
1442 | } | ||
1443 | /* Wait for the command to complete */ | ||
1444 | wait_for_ctrl_irq (ctrl); | ||
1445 | |||
1446 | rc = p_slot->hpc_ops->check_cmd_status(ctrl); | ||
1447 | if (rc) { | ||
1448 | err("%s: Failed to disable slot, error code(%d)\n", __FUNCTION__, rc); | ||
1449 | /* Done with exclusive hardware access */ | ||
1450 | up(&ctrl->crit_sect); | ||
1451 | return rc; | ||
1452 | } | ||
1453 | |||
1454 | /* Done with exclusive hardware access */ | ||
1455 | up(&ctrl->crit_sect); | ||
1456 | |||
1457 | return(rc); | ||
1458 | } | ||
1459 | return 0; | ||
1460 | } | ||
1461 | |||
1462 | |||
1463 | /** | ||
1464 | * remove_board - Turns off slot and LED's | ||
1465 | * | ||
1466 | */ | ||
1467 | static u32 remove_board(struct pci_func *func, struct controller *ctrl) | ||
1468 | { | ||
1469 | int index; | ||
1470 | u8 skip = 0; | ||
1471 | u8 device; | ||
1472 | u8 hp_slot; | ||
1473 | u32 rc; | ||
1474 | struct resource_lists res_lists; | ||
1475 | struct pci_func *temp_func; | ||
1476 | struct slot *p_slot; | ||
1477 | |||
1478 | if (func == NULL) | ||
1479 | return(1); | ||
1480 | |||
1481 | if (shpchp_unconfigure_device(func)) | ||
1482 | return(1); | ||
1483 | |||
1484 | device = func->device; | ||
1485 | |||
1486 | hp_slot = func->device - ctrl->slot_device_offset; | ||
1487 | p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | ||
1488 | |||
1489 | dbg("In %s, hp_slot = %d\n", __FUNCTION__, hp_slot); | ||
1490 | |||
1491 | if ((ctrl->add_support) && | ||
1492 | !(func->bus_head || func->mem_head || func->p_mem_head || func->io_head)) { | ||
1493 | /* Here we check to see if we've saved any of the board's | ||
1494 | * resources already. If so, we'll skip the attempt to | ||
1495 | * determine what's being used. | ||
1496 | */ | ||
1497 | index = 0; | ||
1498 | |||
1499 | temp_func = func; | ||
1500 | |||
1501 | while ((temp_func = shpchp_slot_find(temp_func->bus, temp_func->device, index++))) { | ||
1502 | if (temp_func->bus_head || temp_func->mem_head | ||
1503 | || temp_func->p_mem_head || temp_func->io_head) { | ||
1504 | skip = 1; | ||
1505 | break; | ||
1506 | } | ||
1507 | } | ||
1508 | |||
1509 | if (!skip) | ||
1510 | rc = shpchp_save_used_resources(ctrl, func, DISABLE_CARD); | ||
1511 | } | ||
1512 | /* Change status to shutdown */ | ||
1513 | if (func->is_a_board) | ||
1514 | func->status = 0x01; | ||
1515 | func->configured = 0; | ||
1516 | |||
1517 | /* Wait for exclusive access to hardware */ | ||
1518 | down(&ctrl->crit_sect); | ||
1519 | |||
1520 | /* turn off slot, turn on Amber LED, turn off Green LED */ | ||
1521 | rc = p_slot->hpc_ops->slot_disable(p_slot); | ||
1522 | if (rc) { | ||
1523 | err("%s: Issue of Slot Disable command failed\n", __FUNCTION__); | ||
1524 | /* Done with exclusive hardware access */ | ||
1525 | up(&ctrl->crit_sect); | ||
1526 | return rc; | ||
1527 | } | ||
1528 | /* Wait for the command to complete */ | ||
1529 | wait_for_ctrl_irq (ctrl); | ||
1530 | |||
1531 | rc = p_slot->hpc_ops->check_cmd_status(ctrl); | ||
1532 | if (rc) { | ||
1533 | err("%s: Failed to disable slot, error code(%d)\n", __FUNCTION__, rc); | ||
1534 | /* Done with exclusive hardware access */ | ||
1535 | up(&ctrl->crit_sect); | ||
1536 | return rc; | ||
1537 | } | ||
1538 | |||
1539 | rc = p_slot->hpc_ops->set_attention_status(p_slot, 0); | ||
1540 | if (rc) { | ||
1541 | err("%s: Issue of Set Attention command failed\n", __FUNCTION__); | ||
1542 | /* Done with exclusive hardware access */ | ||
1543 | up(&ctrl->crit_sect); | ||
1544 | return rc; | ||
1545 | } | ||
1546 | /* Wait for the command to complete */ | ||
1547 | wait_for_ctrl_irq (ctrl); | ||
1548 | |||
1549 | /* Done with exclusive hardware access */ | ||
1550 | up(&ctrl->crit_sect); | ||
1551 | |||
1552 | if (ctrl->add_support) { | ||
1553 | while (func) { | ||
1554 | res_lists.io_head = ctrl->io_head; | ||
1555 | res_lists.mem_head = ctrl->mem_head; | ||
1556 | res_lists.p_mem_head = ctrl->p_mem_head; | ||
1557 | res_lists.bus_head = ctrl->bus_head; | ||
1558 | |||
1559 | dbg("Returning resources to ctlr lists for (B/D/F) = (%#x/%#x/%#x)\n", func->bus, | ||
1560 | func->device, func->function); | ||
1561 | |||
1562 | shpchp_return_board_resources(func, &res_lists); | ||
1563 | |||
1564 | ctrl->io_head = res_lists.io_head; | ||
1565 | ctrl->mem_head = res_lists.mem_head; | ||
1566 | ctrl->p_mem_head = res_lists.p_mem_head; | ||
1567 | ctrl->bus_head = res_lists.bus_head; | ||
1568 | |||
1569 | shpchp_resource_sort_and_combine(&(ctrl->mem_head)); | ||
1570 | shpchp_resource_sort_and_combine(&(ctrl->p_mem_head)); | ||
1571 | shpchp_resource_sort_and_combine(&(ctrl->io_head)); | ||
1572 | shpchp_resource_sort_and_combine(&(ctrl->bus_head)); | ||
1573 | |||
1574 | if (is_bridge(func)) { | ||
1575 | dbg("PCI Bridge Hot-Remove s:b:d:f(%02x:%02x:%02x:%02x)\n", ctrl->seg, func->bus, | ||
1576 | func->device, func->function); | ||
1577 | bridge_slot_remove(func); | ||
1578 | } else | ||
1579 | dbg("PCI Function Hot-Remove s:b:d:f(%02x:%02x:%02x:%02x)\n", ctrl->seg, func->bus, | ||
1580 | func->device, func->function); | ||
1581 | slot_remove(func); | ||
1582 | |||
1583 | func = shpchp_slot_find(ctrl->slot_bus, device, 0); | ||
1584 | } | ||
1585 | |||
1586 | /* Setup slot structure with entry for empty slot */ | ||
1587 | func = shpchp_slot_create(ctrl->slot_bus); | ||
1588 | |||
1589 | if (func == NULL) { | ||
1590 | return(1); | ||
1591 | } | ||
1592 | |||
1593 | func->bus = ctrl->slot_bus; | ||
1594 | func->device = device; | ||
1595 | func->function = 0; | ||
1596 | func->configured = 0; | ||
1597 | func->switch_save = 0x10; | ||
1598 | func->pwr_save = 0; | ||
1599 | func->is_a_board = 0; | ||
1600 | } | ||
1601 | |||
1602 | return 0; | ||
1603 | } | ||
1604 | |||
1605 | |||
1606 | static void pushbutton_helper_thread (unsigned long data) | ||
1607 | { | ||
1608 | pushbutton_pending = data; | ||
1609 | |||
1610 | up(&event_semaphore); | ||
1611 | } | ||
1612 | |||
1613 | |||
1614 | /** | ||
1615 | * shpchp_pushbutton_thread | ||
1616 | * | ||
1617 | * Scheduled procedure to handle blocking stuff for the pushbuttons | ||
1618 | * Handles all pending events and exits. | ||
1619 | * | ||
1620 | */ | ||
1621 | static void shpchp_pushbutton_thread (unsigned long slot) | ||
1622 | { | ||
1623 | struct slot *p_slot = (struct slot *) slot; | ||
1624 | u8 getstatus; | ||
1625 | |||
1626 | pushbutton_pending = 0; | ||
1627 | |||
1628 | if (!p_slot) { | ||
1629 | dbg("%s: Error! slot NULL\n", __FUNCTION__); | ||
1630 | return; | ||
1631 | } | ||
1632 | |||
1633 | p_slot->hpc_ops->get_power_status(p_slot, &getstatus); | ||
1634 | if (getstatus) { | ||
1635 | p_slot->state = POWEROFF_STATE; | ||
1636 | dbg("In power_down_board, b:d(%x:%x)\n", p_slot->bus, p_slot->device); | ||
1637 | |||
1638 | shpchp_disable_slot(p_slot); | ||
1639 | p_slot->state = STATIC_STATE; | ||
1640 | } else { | ||
1641 | p_slot->state = POWERON_STATE; | ||
1642 | dbg("In add_board, b:d(%x:%x)\n", p_slot->bus, p_slot->device); | ||
1643 | |||
1644 | if (shpchp_enable_slot(p_slot)) { | ||
1645 | /* Wait for exclusive access to hardware */ | ||
1646 | down(&p_slot->ctrl->crit_sect); | ||
1647 | |||
1648 | p_slot->hpc_ops->green_led_off(p_slot); | ||
1649 | |||
1650 | /* Wait for the command to complete */ | ||
1651 | wait_for_ctrl_irq (p_slot->ctrl); | ||
1652 | |||
1653 | /* Done with exclusive hardware access */ | ||
1654 | up(&p_slot->ctrl->crit_sect); | ||
1655 | } | ||
1656 | p_slot->state = STATIC_STATE; | ||
1657 | } | ||
1658 | |||
1659 | return; | ||
1660 | } | ||
1661 | |||
1662 | |||
1663 | /* this is the main worker thread */ | ||
1664 | static int event_thread(void* data) | ||
1665 | { | ||
1666 | struct controller *ctrl; | ||
1667 | lock_kernel(); | ||
1668 | daemonize("shpchpd_event"); | ||
1669 | unlock_kernel(); | ||
1670 | |||
1671 | while (1) { | ||
1672 | dbg("!!!!event_thread sleeping\n"); | ||
1673 | down_interruptible (&event_semaphore); | ||
1674 | dbg("event_thread woken finished = %d\n", event_finished); | ||
1675 | if (event_finished || signal_pending(current)) | ||
1676 | break; | ||
1677 | /* Do stuff here */ | ||
1678 | if (pushbutton_pending) | ||
1679 | shpchp_pushbutton_thread(pushbutton_pending); | ||
1680 | else | ||
1681 | for (ctrl = shpchp_ctrl_list; ctrl; ctrl=ctrl->next) | ||
1682 | interrupt_event_handler(ctrl); | ||
1683 | } | ||
1684 | dbg("event_thread signals exit\n"); | ||
1685 | up(&event_exit); | ||
1686 | return 0; | ||
1687 | } | ||
1688 | |||
1689 | int shpchp_event_start_thread (void) | ||
1690 | { | ||
1691 | int pid; | ||
1692 | |||
1693 | /* initialize our semaphores */ | ||
1694 | init_MUTEX_LOCKED(&event_exit); | ||
1695 | event_finished=0; | ||
1696 | |||
1697 | init_MUTEX_LOCKED(&event_semaphore); | ||
1698 | pid = kernel_thread(event_thread, NULL, 0); | ||
1699 | |||
1700 | if (pid < 0) { | ||
1701 | err ("Can't start up our event thread\n"); | ||
1702 | return -1; | ||
1703 | } | ||
1704 | dbg("Our event thread pid = %d\n", pid); | ||
1705 | return 0; | ||
1706 | } | ||
1707 | |||
1708 | |||
1709 | void shpchp_event_stop_thread (void) | ||
1710 | { | ||
1711 | event_finished = 1; | ||
1712 | dbg("event_thread finish command given\n"); | ||
1713 | up(&event_semaphore); | ||
1714 | dbg("wait for event_thread to exit\n"); | ||
1715 | down(&event_exit); | ||
1716 | } | ||
1717 | |||
1718 | |||
1719 | static int update_slot_info (struct slot *slot) | ||
1720 | { | ||
1721 | struct hotplug_slot_info *info; | ||
1722 | int result; | ||
1723 | |||
1724 | info = kmalloc(sizeof(*info), GFP_KERNEL); | ||
1725 | if (!info) | ||
1726 | return -ENOMEM; | ||
1727 | |||
1728 | slot->hpc_ops->get_power_status(slot, &(info->power_status)); | ||
1729 | slot->hpc_ops->get_attention_status(slot, &(info->attention_status)); | ||
1730 | slot->hpc_ops->get_latch_status(slot, &(info->latch_status)); | ||
1731 | slot->hpc_ops->get_adapter_status(slot, &(info->adapter_status)); | ||
1732 | |||
1733 | result = pci_hp_change_slot_info(slot->hotplug_slot, info); | ||
1734 | kfree (info); | ||
1735 | return result; | ||
1736 | } | ||
1737 | |||
1738 | static void interrupt_event_handler(struct controller *ctrl) | ||
1739 | { | ||
1740 | int loop = 0; | ||
1741 | int change = 1; | ||
1742 | struct pci_func *func; | ||
1743 | u8 hp_slot; | ||
1744 | u8 getstatus; | ||
1745 | struct slot *p_slot; | ||
1746 | |||
1747 | dbg("%s:\n", __FUNCTION__); | ||
1748 | while (change) { | ||
1749 | change = 0; | ||
1750 | |||
1751 | for (loop = 0; loop < 10; loop++) { | ||
1752 | if (ctrl->event_queue[loop].event_type != 0) { | ||
1753 | dbg("%s:loop %x event_type %x\n", __FUNCTION__, loop, | ||
1754 | ctrl->event_queue[loop].event_type); | ||
1755 | hp_slot = ctrl->event_queue[loop].hp_slot; | ||
1756 | |||
1757 | func = shpchp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0); | ||
1758 | |||
1759 | p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | ||
1760 | |||
1761 | dbg("%s: hp_slot %d, func %p, p_slot %p\n", __FUNCTION__, hp_slot, func, p_slot); | ||
1762 | |||
1763 | if (ctrl->event_queue[loop].event_type == INT_BUTTON_CANCEL) { | ||
1764 | dbg("%s: button cancel\n", __FUNCTION__); | ||
1765 | del_timer(&p_slot->task_event); | ||
1766 | |||
1767 | switch (p_slot->state) { | ||
1768 | case BLINKINGOFF_STATE: | ||
1769 | /* Wait for exclusive access to hardware */ | ||
1770 | down(&ctrl->crit_sect); | ||
1771 | |||
1772 | p_slot->hpc_ops->green_led_on(p_slot); | ||
1773 | /* Wait for the command to complete */ | ||
1774 | wait_for_ctrl_irq (ctrl); | ||
1775 | |||
1776 | p_slot->hpc_ops->set_attention_status(p_slot, 0); | ||
1777 | |||
1778 | /* Wait for the command to complete */ | ||
1779 | wait_for_ctrl_irq (ctrl); | ||
1780 | |||
1781 | /* Done with exclusive hardware access */ | ||
1782 | up(&ctrl->crit_sect); | ||
1783 | break; | ||
1784 | case BLINKINGON_STATE: | ||
1785 | /* Wait for exclusive access to hardware */ | ||
1786 | down(&ctrl->crit_sect); | ||
1787 | |||
1788 | p_slot->hpc_ops->green_led_off(p_slot); | ||
1789 | /* Wait for the command to complete */ | ||
1790 | wait_for_ctrl_irq (ctrl); | ||
1791 | |||
1792 | p_slot->hpc_ops->set_attention_status(p_slot, 0); | ||
1793 | /* Wait for the command to complete */ | ||
1794 | wait_for_ctrl_irq (ctrl); | ||
1795 | |||
1796 | /* Done with exclusive hardware access */ | ||
1797 | up(&ctrl->crit_sect); | ||
1798 | |||
1799 | break; | ||
1800 | default: | ||
1801 | warn("Not a valid state\n"); | ||
1802 | return; | ||
1803 | } | ||
1804 | info(msg_button_cancel, p_slot->number); | ||
1805 | p_slot->state = STATIC_STATE; | ||
1806 | } else if (ctrl->event_queue[loop].event_type == INT_BUTTON_PRESS) { | ||
1807 | /* Button Pressed (No action on 1st press...) */ | ||
1808 | dbg("%s: Button pressed\n", __FUNCTION__); | ||
1809 | |||
1810 | p_slot->hpc_ops->get_power_status(p_slot, &getstatus); | ||
1811 | if (getstatus) { | ||
1812 | /* slot is on */ | ||
1813 | dbg("%s: slot is on\n", __FUNCTION__); | ||
1814 | p_slot->state = BLINKINGOFF_STATE; | ||
1815 | info(msg_button_off, p_slot->number); | ||
1816 | } else { | ||
1817 | /* slot is off */ | ||
1818 | dbg("%s: slot is off\n", __FUNCTION__); | ||
1819 | p_slot->state = BLINKINGON_STATE; | ||
1820 | info(msg_button_on, p_slot->number); | ||
1821 | } | ||
1822 | |||
1823 | /* Wait for exclusive access to hardware */ | ||
1824 | down(&ctrl->crit_sect); | ||
1825 | |||
1826 | /* blink green LED and turn off amber */ | ||
1827 | p_slot->hpc_ops->green_led_blink(p_slot); | ||
1828 | /* Wait for the command to complete */ | ||
1829 | wait_for_ctrl_irq (ctrl); | ||
1830 | |||
1831 | p_slot->hpc_ops->set_attention_status(p_slot, 0); | ||
1832 | |||
1833 | /* Wait for the command to complete */ | ||
1834 | wait_for_ctrl_irq (ctrl); | ||
1835 | |||
1836 | /* Done with exclusive hardware access */ | ||
1837 | up(&ctrl->crit_sect); | ||
1838 | |||
1839 | init_timer(&p_slot->task_event); | ||
1840 | p_slot->task_event.expires = jiffies + 5 * HZ; /* 5 second delay */ | ||
1841 | p_slot->task_event.function = (void (*)(unsigned long)) pushbutton_helper_thread; | ||
1842 | p_slot->task_event.data = (unsigned long) p_slot; | ||
1843 | |||
1844 | dbg("%s: add_timer p_slot = %p\n", __FUNCTION__,(void *) p_slot); | ||
1845 | add_timer(&p_slot->task_event); | ||
1846 | } else if (ctrl->event_queue[loop].event_type == INT_POWER_FAULT) { | ||
1847 | /***********POWER FAULT********************/ | ||
1848 | dbg("%s: power fault\n", __FUNCTION__); | ||
1849 | /* Wait for exclusive access to hardware */ | ||
1850 | down(&ctrl->crit_sect); | ||
1851 | |||
1852 | p_slot->hpc_ops->set_attention_status(p_slot, 1); | ||
1853 | /* Wait for the command to complete */ | ||
1854 | wait_for_ctrl_irq (ctrl); | ||
1855 | |||
1856 | p_slot->hpc_ops->green_led_off(p_slot); | ||
1857 | /* Wait for the command to complete */ | ||
1858 | wait_for_ctrl_irq (ctrl); | ||
1859 | |||
1860 | /* Done with exclusive hardware access */ | ||
1861 | up(&ctrl->crit_sect); | ||
1862 | } else { | ||
1863 | /* refresh notification */ | ||
1864 | if (p_slot) | ||
1865 | update_slot_info(p_slot); | ||
1866 | } | ||
1867 | |||
1868 | ctrl->event_queue[loop].event_type = 0; | ||
1869 | |||
1870 | change = 1; | ||
1871 | } | ||
1872 | } /* End of FOR loop */ | ||
1873 | } | ||
1874 | |||
1875 | return; | ||
1876 | } | ||
1877 | |||
1878 | |||
1879 | int shpchp_enable_slot (struct slot *p_slot) | ||
1880 | { | ||
1881 | u8 getstatus = 0; | ||
1882 | int rc; | ||
1883 | struct pci_func *func; | ||
1884 | |||
1885 | func = shpchp_slot_find(p_slot->bus, p_slot->device, 0); | ||
1886 | if (!func) { | ||
1887 | dbg("%s: Error! slot NULL\n", __FUNCTION__); | ||
1888 | return 1; | ||
1889 | } | ||
1890 | |||
1891 | /* Check to see if (latch closed, card present, power off) */ | ||
1892 | down(&p_slot->ctrl->crit_sect); | ||
1893 | rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus); | ||
1894 | if (rc || !getstatus) { | ||
1895 | info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number); | ||
1896 | up(&p_slot->ctrl->crit_sect); | ||
1897 | return 1; | ||
1898 | } | ||
1899 | rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); | ||
1900 | if (rc || getstatus) { | ||
1901 | info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number); | ||
1902 | up(&p_slot->ctrl->crit_sect); | ||
1903 | return 1; | ||
1904 | } | ||
1905 | rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus); | ||
1906 | if (rc || getstatus) { | ||
1907 | info("%s: already enabled on slot(%x)\n", __FUNCTION__, p_slot->number); | ||
1908 | up(&p_slot->ctrl->crit_sect); | ||
1909 | return 1; | ||
1910 | } | ||
1911 | up(&p_slot->ctrl->crit_sect); | ||
1912 | |||
1913 | slot_remove(func); | ||
1914 | |||
1915 | func = shpchp_slot_create(p_slot->bus); | ||
1916 | if (func == NULL) | ||
1917 | return 1; | ||
1918 | |||
1919 | func->bus = p_slot->bus; | ||
1920 | func->device = p_slot->device; | ||
1921 | func->function = 0; | ||
1922 | func->configured = 0; | ||
1923 | func->is_a_board = 1; | ||
1924 | |||
1925 | /* We have to save the presence info for these slots */ | ||
1926 | p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save)); | ||
1927 | p_slot->hpc_ops->get_power_status(p_slot, &(func->pwr_save)); | ||
1928 | dbg("%s: func->pwr_save %x\n", __FUNCTION__, func->pwr_save); | ||
1929 | p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); | ||
1930 | func->switch_save = !getstatus? 0x10:0; | ||
1931 | |||
1932 | rc = board_added(func, p_slot->ctrl); | ||
1933 | if (rc) { | ||
1934 | if (is_bridge(func)) | ||
1935 | bridge_slot_remove(func); | ||
1936 | else | ||
1937 | slot_remove(func); | ||
1938 | |||
1939 | /* Setup slot structure with entry for empty slot */ | ||
1940 | func = shpchp_slot_create(p_slot->bus); | ||
1941 | if (func == NULL) | ||
1942 | return (1); /* Out of memory */ | ||
1943 | |||
1944 | func->bus = p_slot->bus; | ||
1945 | func->device = p_slot->device; | ||
1946 | func->function = 0; | ||
1947 | func->configured = 0; | ||
1948 | func->is_a_board = 1; | ||
1949 | |||
1950 | /* We have to save the presence info for these slots */ | ||
1951 | p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save)); | ||
1952 | p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); | ||
1953 | func->switch_save = !getstatus? 0x10:0; | ||
1954 | } | ||
1955 | |||
1956 | if (p_slot) | ||
1957 | update_slot_info(p_slot); | ||
1958 | |||
1959 | return rc; | ||
1960 | } | ||
1961 | |||
1962 | |||
1963 | int shpchp_disable_slot (struct slot *p_slot) | ||
1964 | { | ||
1965 | u8 class_code, header_type, BCR; | ||
1966 | u8 index = 0; | ||
1967 | u8 getstatus = 0; | ||
1968 | u32 rc = 0; | ||
1969 | int ret = 0; | ||
1970 | unsigned int devfn; | ||
1971 | struct pci_bus *pci_bus; | ||
1972 | struct pci_func *func; | ||
1973 | |||
1974 | if (!p_slot->ctrl) | ||
1975 | return 1; | ||
1976 | |||
1977 | pci_bus = p_slot->ctrl->pci_dev->subordinate; | ||
1978 | |||
1979 | /* Check to see if (latch closed, card present, power on) */ | ||
1980 | down(&p_slot->ctrl->crit_sect); | ||
1981 | |||
1982 | ret = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus); | ||
1983 | if (ret || !getstatus) { | ||
1984 | info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number); | ||
1985 | up(&p_slot->ctrl->crit_sect); | ||
1986 | return 1; | ||
1987 | } | ||
1988 | ret = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); | ||
1989 | if (ret || getstatus) { | ||
1990 | info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number); | ||
1991 | up(&p_slot->ctrl->crit_sect); | ||
1992 | return 1; | ||
1993 | } | ||
1994 | ret = p_slot->hpc_ops->get_power_status(p_slot, &getstatus); | ||
1995 | if (ret || !getstatus) { | ||
1996 | info("%s: already disabled slot(%x)\n", __FUNCTION__, p_slot->number); | ||
1997 | up(&p_slot->ctrl->crit_sect); | ||
1998 | return 1; | ||
1999 | } | ||
2000 | up(&p_slot->ctrl->crit_sect); | ||
2001 | |||
2002 | func = shpchp_slot_find(p_slot->bus, p_slot->device, index++); | ||
2003 | |||
2004 | /* Make sure there are no video controllers here | ||
2005 | * for all func of p_slot | ||
2006 | */ | ||
2007 | while (func && !rc) { | ||
2008 | pci_bus->number = func->bus; | ||
2009 | devfn = PCI_DEVFN(func->device, func->function); | ||
2010 | |||
2011 | /* Check the Class Code */ | ||
2012 | rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code); | ||
2013 | if (rc) | ||
2014 | return rc; | ||
2015 | |||
2016 | if (class_code == PCI_BASE_CLASS_DISPLAY) { | ||
2017 | /* Display/Video adapter (not supported) */ | ||
2018 | rc = REMOVE_NOT_SUPPORTED; | ||
2019 | } else { | ||
2020 | /* See if it's a bridge */ | ||
2021 | rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type); | ||
2022 | if (rc) | ||
2023 | return rc; | ||
2024 | |||
2025 | /* If it's a bridge, check the VGA Enable bit */ | ||
2026 | if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { | ||
2027 | rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_BRIDGE_CONTROL, &BCR); | ||
2028 | if (rc) | ||
2029 | return rc; | ||
2030 | |||
2031 | /* If the VGA Enable bit is set, remove isn't supported */ | ||
2032 | if (BCR & PCI_BRIDGE_CTL_VGA) { | ||
2033 | rc = REMOVE_NOT_SUPPORTED; | ||
2034 | } | ||
2035 | } | ||
2036 | } | ||
2037 | |||
2038 | func = shpchp_slot_find(p_slot->bus, p_slot->device, index++); | ||
2039 | } | ||
2040 | |||
2041 | func = shpchp_slot_find(p_slot->bus, p_slot->device, 0); | ||
2042 | if ((func != NULL) && !rc) { | ||
2043 | rc = remove_board(func, p_slot->ctrl); | ||
2044 | } else if (!rc) | ||
2045 | rc = 1; | ||
2046 | |||
2047 | if (p_slot) | ||
2048 | update_slot_info(p_slot); | ||
2049 | |||
2050 | return(rc); | ||
2051 | } | ||
2052 | |||
2053 | |||
2054 | /** | ||
2055 | * configure_new_device - Configures the PCI header information of one board. | ||
2056 | * | ||
2057 | * @ctrl: pointer to controller structure | ||
2058 | * @func: pointer to function structure | ||
2059 | * @behind_bridge: 1 if this is a recursive call, 0 if not | ||
2060 | * @resources: pointer to set of resource lists | ||
2061 | * | ||
2062 | * Returns 0 if success | ||
2063 | * | ||
2064 | */ | ||
2065 | static u32 configure_new_device (struct controller * ctrl, struct pci_func * func, | ||
2066 | u8 behind_bridge, struct resource_lists * resources, u8 bridge_bus, u8 bridge_dev) | ||
2067 | { | ||
2068 | u8 temp_byte, function, max_functions, stop_it; | ||
2069 | int rc; | ||
2070 | u32 ID; | ||
2071 | struct pci_func *new_slot; | ||
2072 | struct pci_bus lpci_bus, *pci_bus; | ||
2073 | int index; | ||
2074 | |||
2075 | new_slot = func; | ||
2076 | |||
2077 | dbg("%s\n", __FUNCTION__); | ||
2078 | memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus)); | ||
2079 | pci_bus = &lpci_bus; | ||
2080 | pci_bus->number = func->bus; | ||
2081 | |||
2082 | /* Check for Multi-function device */ | ||
2083 | rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(func->device, func->function), 0x0E, &temp_byte); | ||
2084 | if (rc) { | ||
2085 | dbg("%s: rc = %d\n", __FUNCTION__, rc); | ||
2086 | return rc; | ||
2087 | } | ||
2088 | |||
2089 | if (temp_byte & 0x80) /* Multi-function device */ | ||
2090 | max_functions = 8; | ||
2091 | else | ||
2092 | max_functions = 1; | ||
2093 | |||
2094 | function = 0; | ||
2095 | |||
2096 | do { | ||
2097 | rc = configure_new_function(ctrl, new_slot, behind_bridge, resources, bridge_bus, bridge_dev); | ||
2098 | |||
2099 | if (rc) { | ||
2100 | dbg("configure_new_function failed %d\n",rc); | ||
2101 | index = 0; | ||
2102 | |||
2103 | while (new_slot) { | ||
2104 | new_slot = shpchp_slot_find(new_slot->bus, new_slot->device, index++); | ||
2105 | |||
2106 | if (new_slot) | ||
2107 | shpchp_return_board_resources(new_slot, resources); | ||
2108 | } | ||
2109 | |||
2110 | return(rc); | ||
2111 | } | ||
2112 | |||
2113 | function++; | ||
2114 | |||
2115 | stop_it = 0; | ||
2116 | |||
2117 | /* The following loop skips to the next present function | ||
2118 | * and creates a board structure | ||
2119 | */ | ||
2120 | |||
2121 | while ((function < max_functions) && (!stop_it)) { | ||
2122 | pci_bus_read_config_dword(pci_bus, PCI_DEVFN(func->device, function), 0x00, &ID); | ||
2123 | |||
2124 | if (ID == 0xFFFFFFFF) { /* There's nothing there. */ | ||
2125 | function++; | ||
2126 | } else { /* There's something there */ | ||
2127 | /* Setup slot structure. */ | ||
2128 | new_slot = shpchp_slot_create(func->bus); | ||
2129 | |||
2130 | if (new_slot == NULL) { | ||
2131 | /* Out of memory */ | ||
2132 | return(1); | ||
2133 | } | ||
2134 | |||
2135 | new_slot->bus = func->bus; | ||
2136 | new_slot->device = func->device; | ||
2137 | new_slot->function = function; | ||
2138 | new_slot->is_a_board = 1; | ||
2139 | new_slot->status = 0; | ||
2140 | |||
2141 | stop_it++; | ||
2142 | } | ||
2143 | } | ||
2144 | |||
2145 | } while (function < max_functions); | ||
2146 | dbg("returning from configure_new_device\n"); | ||
2147 | |||
2148 | return 0; | ||
2149 | } | ||
2150 | |||
2151 | |||
2152 | /* | ||
2153 | * Configuration logic that involves the hotplug data structures and | ||
2154 | * their bookkeeping | ||
2155 | */ | ||
2156 | |||
2157 | |||
2158 | /** | ||
2159 | * configure_new_function - Configures the PCI header information of one device | ||
2160 | * | ||
2161 | * @ctrl: pointer to controller structure | ||
2162 | * @func: pointer to function structure | ||
2163 | * @behind_bridge: 1 if this is a recursive call, 0 if not | ||
2164 | * @resources: pointer to set of resource lists | ||
2165 | * | ||
2166 | * Calls itself recursively for bridged devices. | ||
2167 | * Returns 0 if success | ||
2168 | * | ||
2169 | */ | ||
2170 | static int configure_new_function (struct controller * ctrl, struct pci_func * func, | ||
2171 | u8 behind_bridge, struct resource_lists *resources, u8 bridge_bus, u8 bridge_dev) | ||
2172 | { | ||
2173 | int cloop; | ||
2174 | u8 temp_byte; | ||
2175 | u8 device; | ||
2176 | u8 class_code; | ||
2177 | u16 temp_word; | ||
2178 | u32 rc; | ||
2179 | u32 temp_register; | ||
2180 | u32 base; | ||
2181 | u32 ID; | ||
2182 | unsigned int devfn; | ||
2183 | struct pci_resource *mem_node; | ||
2184 | struct pci_resource *p_mem_node; | ||
2185 | struct pci_resource *io_node; | ||
2186 | struct pci_resource *bus_node; | ||
2187 | struct pci_resource *hold_mem_node; | ||
2188 | struct pci_resource *hold_p_mem_node; | ||
2189 | struct pci_resource *hold_IO_node; | ||
2190 | struct pci_resource *hold_bus_node; | ||
2191 | struct irq_mapping irqs; | ||
2192 | struct pci_func *new_slot; | ||
2193 | struct pci_bus lpci_bus, *pci_bus; | ||
2194 | struct resource_lists temp_resources; | ||
2195 | #if defined(CONFIG_X86_64) | ||
2196 | u8 IRQ=0; | ||
2197 | #endif | ||
2198 | |||
2199 | memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus)); | ||
2200 | pci_bus = &lpci_bus; | ||
2201 | pci_bus->number = func->bus; | ||
2202 | devfn = PCI_DEVFN(func->device, func->function); | ||
2203 | |||
2204 | /* Check for Bridge */ | ||
2205 | rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &temp_byte); | ||
2206 | if (rc) | ||
2207 | return rc; | ||
2208 | |||
2209 | if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* PCI-PCI Bridge */ | ||
2210 | /* set Primary bus */ | ||
2211 | dbg("set Primary bus = 0x%x\n", func->bus); | ||
2212 | rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_PRIMARY_BUS, func->bus); | ||
2213 | if (rc) | ||
2214 | return rc; | ||
2215 | |||
2216 | /* find range of busses to use */ | ||
2217 | bus_node = get_max_resource(&resources->bus_head, 1L); | ||
2218 | |||
2219 | /* If we don't have any busses to allocate, we can't continue */ | ||
2220 | if (!bus_node) { | ||
2221 | err("Got NO bus resource to use\n"); | ||
2222 | return -ENOMEM; | ||
2223 | } | ||
2224 | dbg("Got ranges of buses to use: base:len=0x%x:%x\n", bus_node->base, bus_node->length); | ||
2225 | |||
2226 | /* set Secondary bus */ | ||
2227 | temp_byte = (u8)bus_node->base; | ||
2228 | dbg("set Secondary bus = 0x%x\n", temp_byte); | ||
2229 | rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, temp_byte); | ||
2230 | if (rc) | ||
2231 | return rc; | ||
2232 | |||
2233 | /* set subordinate bus */ | ||
2234 | temp_byte = (u8)(bus_node->base + bus_node->length - 1); | ||
2235 | dbg("set subordinate bus = 0x%x\n", temp_byte); | ||
2236 | rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte); | ||
2237 | if (rc) | ||
2238 | return rc; | ||
2239 | |||
2240 | /* Set HP parameters (Cache Line Size, Latency Timer) */ | ||
2241 | rc = shpchprm_set_hpp(ctrl, func, PCI_HEADER_TYPE_BRIDGE); | ||
2242 | if (rc) | ||
2243 | return rc; | ||
2244 | |||
2245 | /* Setup the IO, memory, and prefetchable windows */ | ||
2246 | |||
2247 | io_node = get_max_resource(&(resources->io_head), 0x1000L); | ||
2248 | if (io_node) { | ||
2249 | dbg("io_node(base, len, next) (%x, %x, %p)\n", io_node->base, io_node->length, io_node->next); | ||
2250 | } | ||
2251 | |||
2252 | mem_node = get_max_resource(&(resources->mem_head), 0x100000L); | ||
2253 | if (mem_node) { | ||
2254 | dbg("mem_node(base, len, next) (%x, %x, %p)\n", mem_node->base, mem_node->length, mem_node->next); | ||
2255 | } | ||
2256 | |||
2257 | if (resources->p_mem_head) | ||
2258 | p_mem_node = get_max_resource(&(resources->p_mem_head), 0x100000L); | ||
2259 | else { | ||
2260 | /* | ||
2261 | * In some platform implementation, MEM and PMEM are not | ||
2262 | * distinguished, and hence ACPI _CRS has only MEM entries | ||
2263 | * for both MEM and PMEM. | ||
2264 | */ | ||
2265 | dbg("using MEM for PMEM\n"); | ||
2266 | p_mem_node = get_max_resource(&(resources->mem_head), 0x100000L); | ||
2267 | } | ||
2268 | if (p_mem_node) { | ||
2269 | dbg("p_mem_node(base, len, next) (%x, %x, %p)\n", p_mem_node->base, p_mem_node->length, p_mem_node->next); | ||
2270 | } | ||
2271 | |||
2272 | /* set up the IRQ info */ | ||
2273 | if (!resources->irqs) { | ||
2274 | irqs.barber_pole = 0; | ||
2275 | irqs.interrupt[0] = 0; | ||
2276 | irqs.interrupt[1] = 0; | ||
2277 | irqs.interrupt[2] = 0; | ||
2278 | irqs.interrupt[3] = 0; | ||
2279 | irqs.valid_INT = 0; | ||
2280 | } else { | ||
2281 | irqs.barber_pole = resources->irqs->barber_pole; | ||
2282 | irqs.interrupt[0] = resources->irqs->interrupt[0]; | ||
2283 | irqs.interrupt[1] = resources->irqs->interrupt[1]; | ||
2284 | irqs.interrupt[2] = resources->irqs->interrupt[2]; | ||
2285 | irqs.interrupt[3] = resources->irqs->interrupt[3]; | ||
2286 | irqs.valid_INT = resources->irqs->valid_INT; | ||
2287 | } | ||
2288 | |||
2289 | /* set up resource lists that are now aligned on top and bottom | ||
2290 | * for anything behind the bridge. | ||
2291 | */ | ||
2292 | temp_resources.bus_head = bus_node; | ||
2293 | temp_resources.io_head = io_node; | ||
2294 | temp_resources.mem_head = mem_node; | ||
2295 | temp_resources.p_mem_head = p_mem_node; | ||
2296 | temp_resources.irqs = &irqs; | ||
2297 | |||
2298 | /* Make copies of the nodes we are going to pass down so that | ||
2299 | * if there is a problem,we can just use these to free resources | ||
2300 | */ | ||
2301 | hold_bus_node = kmalloc(sizeof(*hold_bus_node), GFP_KERNEL); | ||
2302 | hold_IO_node = kmalloc(sizeof(*hold_IO_node), GFP_KERNEL); | ||
2303 | hold_mem_node = kmalloc(sizeof(*hold_mem_node), GFP_KERNEL); | ||
2304 | hold_p_mem_node = kmalloc(sizeof(*hold_p_mem_node), GFP_KERNEL); | ||
2305 | |||
2306 | if (!hold_bus_node || !hold_IO_node || !hold_mem_node || !hold_p_mem_node) { | ||
2307 | kfree(hold_bus_node); | ||
2308 | kfree(hold_IO_node); | ||
2309 | kfree(hold_mem_node); | ||
2310 | kfree(hold_p_mem_node); | ||
2311 | |||
2312 | return 1; | ||
2313 | } | ||
2314 | |||
2315 | memcpy(hold_bus_node, bus_node, sizeof(struct pci_resource)); | ||
2316 | |||
2317 | bus_node->base += 1; | ||
2318 | bus_node->length -= 1; | ||
2319 | bus_node->next = NULL; | ||
2320 | |||
2321 | /* If we have IO resources copy them and fill in the bridge's | ||
2322 | * IO range registers | ||
2323 | */ | ||
2324 | if (io_node) { | ||
2325 | memcpy(hold_IO_node, io_node, sizeof(struct pci_resource)); | ||
2326 | io_node->next = NULL; | ||
2327 | |||
2328 | /* set IO base and Limit registers */ | ||
2329 | RES_CHECK(io_node->base, 8); | ||
2330 | temp_byte = (u8)(io_node->base >> 8); | ||
2331 | rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_BASE, temp_byte); | ||
2332 | |||
2333 | RES_CHECK(io_node->base + io_node->length - 1, 8); | ||
2334 | temp_byte = (u8)((io_node->base + io_node->length - 1) >> 8); | ||
2335 | rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_LIMIT, temp_byte); | ||
2336 | } else { | ||
2337 | kfree(hold_IO_node); | ||
2338 | hold_IO_node = NULL; | ||
2339 | } | ||
2340 | |||
2341 | /* If we have memory resources copy them and fill in the bridge's | ||
2342 | * memory range registers. Otherwise, fill in the range | ||
2343 | * registers with values that disable them. | ||
2344 | */ | ||
2345 | if (mem_node) { | ||
2346 | memcpy(hold_mem_node, mem_node, sizeof(struct pci_resource)); | ||
2347 | mem_node->next = NULL; | ||
2348 | |||
2349 | /* set Mem base and Limit registers */ | ||
2350 | RES_CHECK(mem_node->base, 16); | ||
2351 | temp_word = (u32)(mem_node->base >> 16); | ||
2352 | rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_BASE, temp_word); | ||
2353 | |||
2354 | RES_CHECK(mem_node->base + mem_node->length - 1, 16); | ||
2355 | temp_word = (u32)((mem_node->base + mem_node->length - 1) >> 16); | ||
2356 | rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); | ||
2357 | } else { | ||
2358 | temp_word = 0xFFFF; | ||
2359 | rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_BASE, temp_word); | ||
2360 | |||
2361 | temp_word = 0x0000; | ||
2362 | rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); | ||
2363 | |||
2364 | kfree(hold_mem_node); | ||
2365 | hold_mem_node = NULL; | ||
2366 | } | ||
2367 | |||
2368 | /* If we have prefetchable memory resources copy them and | ||
2369 | * fill in the bridge's memory range registers. Otherwise, | ||
2370 | * fill in the range registers with values that disable them. | ||
2371 | */ | ||
2372 | if (p_mem_node) { | ||
2373 | memcpy(hold_p_mem_node, p_mem_node, sizeof(struct pci_resource)); | ||
2374 | p_mem_node->next = NULL; | ||
2375 | |||
2376 | /* set Pre Mem base and Limit registers */ | ||
2377 | RES_CHECK(p_mem_node->base, 16); | ||
2378 | temp_word = (u32)(p_mem_node->base >> 16); | ||
2379 | rc = pci_bus_write_config_word(pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word); | ||
2380 | |||
2381 | RES_CHECK(p_mem_node->base + p_mem_node->length - 1, 16); | ||
2382 | temp_word = (u32)((p_mem_node->base + p_mem_node->length - 1) >> 16); | ||
2383 | rc = pci_bus_write_config_word(pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); | ||
2384 | } else { | ||
2385 | temp_word = 0xFFFF; | ||
2386 | rc = pci_bus_write_config_word(pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word); | ||
2387 | |||
2388 | temp_word = 0x0000; | ||
2389 | rc = pci_bus_write_config_word(pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); | ||
2390 | |||
2391 | kfree(hold_p_mem_node); | ||
2392 | hold_p_mem_node = NULL; | ||
2393 | } | ||
2394 | |||
2395 | /* Adjust this to compensate for extra adjustment in first loop */ | ||
2396 | irqs.barber_pole--; | ||
2397 | |||
2398 | rc = 0; | ||
2399 | |||
2400 | /* Here we actually find the devices and configure them */ | ||
2401 | for (device = 0; (device <= 0x1F) && !rc; device++) { | ||
2402 | irqs.barber_pole = (irqs.barber_pole + 1) & 0x03; | ||
2403 | |||
2404 | ID = 0xFFFFFFFF; | ||
2405 | pci_bus->number = hold_bus_node->base; | ||
2406 | pci_bus_read_config_dword(pci_bus, PCI_DEVFN(device, 0), | ||
2407 | PCI_VENDOR_ID, &ID); | ||
2408 | pci_bus->number = func->bus; | ||
2409 | |||
2410 | if (ID != 0xFFFFFFFF) { /* device Present */ | ||
2411 | /* Setup slot structure. */ | ||
2412 | new_slot = shpchp_slot_create(hold_bus_node->base); | ||
2413 | |||
2414 | if (new_slot == NULL) { | ||
2415 | /* Out of memory */ | ||
2416 | rc = -ENOMEM; | ||
2417 | continue; | ||
2418 | } | ||
2419 | |||
2420 | new_slot->bus = hold_bus_node->base; | ||
2421 | new_slot->device = device; | ||
2422 | new_slot->function = 0; | ||
2423 | new_slot->is_a_board = 1; | ||
2424 | new_slot->status = 0; | ||
2425 | |||
2426 | rc = configure_new_device(ctrl, new_slot, 1, &temp_resources, func->bus, func->device); | ||
2427 | dbg("configure_new_device rc=0x%x\n",rc); | ||
2428 | } /* End of IF (device in slot?) */ | ||
2429 | } /* End of FOR loop */ | ||
2430 | |||
2431 | if (rc) { | ||
2432 | shpchp_destroy_resource_list(&temp_resources); | ||
2433 | |||
2434 | return_resource(&(resources->bus_head), hold_bus_node); | ||
2435 | return_resource(&(resources->io_head), hold_IO_node); | ||
2436 | return_resource(&(resources->mem_head), hold_mem_node); | ||
2437 | return_resource(&(resources->p_mem_head), hold_p_mem_node); | ||
2438 | return(rc); | ||
2439 | } | ||
2440 | |||
2441 | /* save the interrupt routing information */ | ||
2442 | if (resources->irqs) { | ||
2443 | resources->irqs->interrupt[0] = irqs.interrupt[0]; | ||
2444 | resources->irqs->interrupt[1] = irqs.interrupt[1]; | ||
2445 | resources->irqs->interrupt[2] = irqs.interrupt[2]; | ||
2446 | resources->irqs->interrupt[3] = irqs.interrupt[3]; | ||
2447 | resources->irqs->valid_INT = irqs.valid_INT; | ||
2448 | } else if (!behind_bridge) { | ||
2449 | /* We need to hook up the interrupts here */ | ||
2450 | for (cloop = 0; cloop < 4; cloop++) { | ||
2451 | if (irqs.valid_INT & (0x01 << cloop)) { | ||
2452 | rc = shpchp_set_irq(func->bus, func->device, | ||
2453 | 0x0A + cloop, irqs.interrupt[cloop]); | ||
2454 | if (rc) { | ||
2455 | shpchp_destroy_resource_list (&temp_resources); | ||
2456 | return_resource(&(resources->bus_head), hold_bus_node); | ||
2457 | return_resource(&(resources->io_head), hold_IO_node); | ||
2458 | return_resource(&(resources->mem_head), hold_mem_node); | ||
2459 | return_resource(&(resources->p_mem_head), hold_p_mem_node); | ||
2460 | return rc; | ||
2461 | } | ||
2462 | } | ||
2463 | } /* end of for loop */ | ||
2464 | } | ||
2465 | |||
2466 | /* Return unused bus resources | ||
2467 | * First use the temporary node to store information for the board | ||
2468 | */ | ||
2469 | if (hold_bus_node && bus_node && temp_resources.bus_head) { | ||
2470 | hold_bus_node->length = bus_node->base - hold_bus_node->base; | ||
2471 | |||
2472 | hold_bus_node->next = func->bus_head; | ||
2473 | func->bus_head = hold_bus_node; | ||
2474 | |||
2475 | temp_byte = (u8)(temp_resources.bus_head->base - 1); | ||
2476 | |||
2477 | /* set subordinate bus */ | ||
2478 | dbg("re-set subordinate bus = 0x%x\n", temp_byte); | ||
2479 | rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte); | ||
2480 | |||
2481 | if (temp_resources.bus_head->length == 0) { | ||
2482 | kfree(temp_resources.bus_head); | ||
2483 | temp_resources.bus_head = NULL; | ||
2484 | } else { | ||
2485 | dbg("return bus res of b:d(0x%x:%x) base:len(0x%x:%x)\n", | ||
2486 | func->bus, func->device, temp_resources.bus_head->base, temp_resources.bus_head->length); | ||
2487 | return_resource(&(resources->bus_head), temp_resources.bus_head); | ||
2488 | } | ||
2489 | } | ||
2490 | |||
2491 | /* If we have IO space available and there is some left, | ||
2492 | * return the unused portion | ||
2493 | */ | ||
2494 | if (hold_IO_node && temp_resources.io_head) { | ||
2495 | io_node = do_pre_bridge_resource_split(&(temp_resources.io_head), | ||
2496 | &hold_IO_node, 0x1000); | ||
2497 | |||
2498 | /* Check if we were able to split something off */ | ||
2499 | if (io_node) { | ||
2500 | hold_IO_node->base = io_node->base + io_node->length; | ||
2501 | |||
2502 | RES_CHECK(hold_IO_node->base, 8); | ||
2503 | temp_byte = (u8)((hold_IO_node->base) >> 8); | ||
2504 | rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_BASE, temp_byte); | ||
2505 | |||
2506 | return_resource(&(resources->io_head), io_node); | ||
2507 | } | ||
2508 | |||
2509 | io_node = do_bridge_resource_split(&(temp_resources.io_head), 0x1000); | ||
2510 | |||
2511 | /* Check if we were able to split something off */ | ||
2512 | if (io_node) { | ||
2513 | /* First use the temporary node to store information for the board */ | ||
2514 | hold_IO_node->length = io_node->base - hold_IO_node->base; | ||
2515 | |||
2516 | /* If we used any, add it to the board's list */ | ||
2517 | if (hold_IO_node->length) { | ||
2518 | hold_IO_node->next = func->io_head; | ||
2519 | func->io_head = hold_IO_node; | ||
2520 | |||
2521 | RES_CHECK(io_node->base - 1, 8); | ||
2522 | temp_byte = (u8)((io_node->base - 1) >> 8); | ||
2523 | rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_LIMIT, temp_byte); | ||
2524 | |||
2525 | return_resource(&(resources->io_head), io_node); | ||
2526 | } else { | ||
2527 | /* it doesn't need any IO */ | ||
2528 | temp_byte = 0x00; | ||
2529 | rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_LIMIT, temp_byte); | ||
2530 | |||
2531 | return_resource(&(resources->io_head), io_node); | ||
2532 | kfree(hold_IO_node); | ||
2533 | } | ||
2534 | } else { | ||
2535 | /* it used most of the range */ | ||
2536 | hold_IO_node->next = func->io_head; | ||
2537 | func->io_head = hold_IO_node; | ||
2538 | } | ||
2539 | } else if (hold_IO_node) { | ||
2540 | /* it used the whole range */ | ||
2541 | hold_IO_node->next = func->io_head; | ||
2542 | func->io_head = hold_IO_node; | ||
2543 | } | ||
2544 | |||
2545 | /* If we have memory space available and there is some left, | ||
2546 | * return the unused portion | ||
2547 | */ | ||
2548 | if (hold_mem_node && temp_resources.mem_head) { | ||
2549 | mem_node = do_pre_bridge_resource_split(&(temp_resources.mem_head), &hold_mem_node, 0x100000L); | ||
2550 | |||
2551 | /* Check if we were able to split something off */ | ||
2552 | if (mem_node) { | ||
2553 | hold_mem_node->base = mem_node->base + mem_node->length; | ||
2554 | |||
2555 | RES_CHECK(hold_mem_node->base, 16); | ||
2556 | temp_word = (u32)((hold_mem_node->base) >> 16); | ||
2557 | rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_BASE, temp_word); | ||
2558 | |||
2559 | return_resource(&(resources->mem_head), mem_node); | ||
2560 | } | ||
2561 | |||
2562 | mem_node = do_bridge_resource_split(&(temp_resources.mem_head), 0x100000L); | ||
2563 | |||
2564 | /* Check if we were able to split something off */ | ||
2565 | if (mem_node) { | ||
2566 | /* First use the temporary node to store information for the board */ | ||
2567 | hold_mem_node->length = mem_node->base - hold_mem_node->base; | ||
2568 | |||
2569 | if (hold_mem_node->length) { | ||
2570 | hold_mem_node->next = func->mem_head; | ||
2571 | func->mem_head = hold_mem_node; | ||
2572 | |||
2573 | /* configure end address */ | ||
2574 | RES_CHECK(mem_node->base - 1, 16); | ||
2575 | temp_word = (u32)((mem_node->base - 1) >> 16); | ||
2576 | rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); | ||
2577 | |||
2578 | /* Return unused resources to the pool */ | ||
2579 | return_resource(&(resources->mem_head), mem_node); | ||
2580 | } else { | ||
2581 | /* it doesn't need any Mem */ | ||
2582 | temp_word = 0x0000; | ||
2583 | rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); | ||
2584 | |||
2585 | return_resource(&(resources->mem_head), mem_node); | ||
2586 | kfree(hold_mem_node); | ||
2587 | } | ||
2588 | } else { | ||
2589 | /* it used most of the range */ | ||
2590 | hold_mem_node->next = func->mem_head; | ||
2591 | func->mem_head = hold_mem_node; | ||
2592 | } | ||
2593 | } else if (hold_mem_node) { | ||
2594 | /* it used the whole range */ | ||
2595 | hold_mem_node->next = func->mem_head; | ||
2596 | func->mem_head = hold_mem_node; | ||
2597 | } | ||
2598 | |||
2599 | /* If we have prefetchable memory space available and there is some | ||
2600 | * left at the end, return the unused portion | ||
2601 | */ | ||
2602 | if (hold_p_mem_node && temp_resources.p_mem_head) { | ||
2603 | p_mem_node = do_pre_bridge_resource_split(&(temp_resources.p_mem_head), | ||
2604 | &hold_p_mem_node, 0x100000L); | ||
2605 | |||
2606 | /* Check if we were able to split something off */ | ||
2607 | if (p_mem_node) { | ||
2608 | hold_p_mem_node->base = p_mem_node->base + p_mem_node->length; | ||
2609 | |||
2610 | RES_CHECK(hold_p_mem_node->base, 16); | ||
2611 | temp_word = (u32)((hold_p_mem_node->base) >> 16); | ||
2612 | rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word); | ||
2613 | |||
2614 | return_resource(&(resources->p_mem_head), p_mem_node); | ||
2615 | } | ||
2616 | |||
2617 | p_mem_node = do_bridge_resource_split(&(temp_resources.p_mem_head), 0x100000L); | ||
2618 | |||
2619 | /* Check if we were able to split something off */ | ||
2620 | if (p_mem_node) { | ||
2621 | /* First use the temporary node to store information for the board */ | ||
2622 | hold_p_mem_node->length = p_mem_node->base - hold_p_mem_node->base; | ||
2623 | |||
2624 | /* If we used any, add it to the board's list */ | ||
2625 | if (hold_p_mem_node->length) { | ||
2626 | hold_p_mem_node->next = func->p_mem_head; | ||
2627 | func->p_mem_head = hold_p_mem_node; | ||
2628 | |||
2629 | RES_CHECK(p_mem_node->base - 1, 16); | ||
2630 | temp_word = (u32)((p_mem_node->base - 1) >> 16); | ||
2631 | rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); | ||
2632 | |||
2633 | return_resource(&(resources->p_mem_head), p_mem_node); | ||
2634 | } else { | ||
2635 | /* it doesn't need any PMem */ | ||
2636 | temp_word = 0x0000; | ||
2637 | rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); | ||
2638 | |||
2639 | return_resource(&(resources->p_mem_head), p_mem_node); | ||
2640 | kfree(hold_p_mem_node); | ||
2641 | } | ||
2642 | } else { | ||
2643 | /* it used the most of the range */ | ||
2644 | hold_p_mem_node->next = func->p_mem_head; | ||
2645 | func->p_mem_head = hold_p_mem_node; | ||
2646 | } | ||
2647 | } else if (hold_p_mem_node) { | ||
2648 | /* it used the whole range */ | ||
2649 | hold_p_mem_node->next = func->p_mem_head; | ||
2650 | func->p_mem_head = hold_p_mem_node; | ||
2651 | } | ||
2652 | |||
2653 | /* We should be configuring an IRQ and the bridge's base address | ||
2654 | * registers if it needs them. Although we have never seen such | ||
2655 | * a device | ||
2656 | */ | ||
2657 | |||
2658 | shpchprm_enable_card(ctrl, func, PCI_HEADER_TYPE_BRIDGE); | ||
2659 | |||
2660 | dbg("PCI Bridge Hot-Added s:b:d:f(%02x:%02x:%02x:%02x)\n", ctrl->seg, func->bus, func->device, func->function); | ||
2661 | } else if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_NORMAL) { | ||
2662 | /* Standard device */ | ||
2663 | u64 base64; | ||
2664 | rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code); | ||
2665 | |||
2666 | if (class_code == PCI_BASE_CLASS_DISPLAY) | ||
2667 | return (DEVICE_TYPE_NOT_SUPPORTED); | ||
2668 | |||
2669 | /* Figure out IO and memory needs */ | ||
2670 | for (cloop = PCI_BASE_ADDRESS_0; cloop <= PCI_BASE_ADDRESS_5; cloop += 4) { | ||
2671 | temp_register = 0xFFFFFFFF; | ||
2672 | |||
2673 | rc = pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register); | ||
2674 | rc = pci_bus_read_config_dword(pci_bus, devfn, cloop, &temp_register); | ||
2675 | dbg("Bar[%x]=0x%x on bus:dev:func(0x%x:%x:%x)\n", cloop, temp_register, func->bus, func->device, | ||
2676 | func->function); | ||
2677 | |||
2678 | if (!temp_register) | ||
2679 | continue; | ||
2680 | |||
2681 | base64 = 0L; | ||
2682 | if (temp_register & PCI_BASE_ADDRESS_SPACE_IO) { | ||
2683 | /* Map IO */ | ||
2684 | |||
2685 | /* set base = amount of IO space */ | ||
2686 | base = temp_register & 0xFFFFFFFC; | ||
2687 | base = ~base + 1; | ||
2688 | |||
2689 | dbg("NEED IO length(0x%x)\n", base); | ||
2690 | io_node = get_io_resource(&(resources->io_head),(ulong)base); | ||
2691 | |||
2692 | /* allocate the resource to the board */ | ||
2693 | if (io_node) { | ||
2694 | dbg("Got IO base=0x%x(length=0x%x)\n", io_node->base, io_node->length); | ||
2695 | base = (u32)io_node->base; | ||
2696 | io_node->next = func->io_head; | ||
2697 | func->io_head = io_node; | ||
2698 | } else { | ||
2699 | err("Got NO IO resource(length=0x%x)\n", base); | ||
2700 | return -ENOMEM; | ||
2701 | } | ||
2702 | } else { /* map MEM */ | ||
2703 | int prefetchable = 1; | ||
2704 | struct pci_resource **res_node = &func->p_mem_head; | ||
2705 | char *res_type_str = "PMEM"; | ||
2706 | u32 temp_register2; | ||
2707 | |||
2708 | if (!(temp_register & PCI_BASE_ADDRESS_MEM_PREFETCH)) { | ||
2709 | prefetchable = 0; | ||
2710 | res_node = &func->mem_head; | ||
2711 | res_type_str++; | ||
2712 | } | ||
2713 | |||
2714 | base = temp_register & 0xFFFFFFF0; | ||
2715 | base = ~base + 1; | ||
2716 | |||
2717 | switch (temp_register & PCI_BASE_ADDRESS_MEM_TYPE_MASK) { | ||
2718 | case PCI_BASE_ADDRESS_MEM_TYPE_32: | ||
2719 | dbg("NEED 32 %s bar=0x%x(length=0x%x)\n", res_type_str, temp_register, base); | ||
2720 | |||
2721 | if (prefetchable && resources->p_mem_head) | ||
2722 | mem_node=get_resource(&(resources->p_mem_head), (ulong)base); | ||
2723 | else { | ||
2724 | if (prefetchable) | ||
2725 | dbg("using MEM for PMEM\n"); | ||
2726 | mem_node=get_resource(&(resources->mem_head), (ulong)base); | ||
2727 | } | ||
2728 | |||
2729 | /* allocate the resource to the board */ | ||
2730 | if (mem_node) { | ||
2731 | base = (u32)mem_node->base; | ||
2732 | mem_node->next = *res_node; | ||
2733 | *res_node = mem_node; | ||
2734 | dbg("Got 32 %s base=0x%x(length=0x%x)\n", res_type_str, mem_node->base, | ||
2735 | mem_node->length); | ||
2736 | } else { | ||
2737 | err("Got NO 32 %s resource(length=0x%x)\n", res_type_str, base); | ||
2738 | return -ENOMEM; | ||
2739 | } | ||
2740 | break; | ||
2741 | case PCI_BASE_ADDRESS_MEM_TYPE_64: | ||
2742 | rc = pci_bus_read_config_dword(pci_bus, devfn, cloop+4, &temp_register2); | ||
2743 | dbg("NEED 64 %s bar=0x%x:%x(length=0x%x)\n", res_type_str, temp_register2, | ||
2744 | temp_register, base); | ||
2745 | |||
2746 | if (prefetchable && resources->p_mem_head) | ||
2747 | mem_node = get_resource(&(resources->p_mem_head), (ulong)base); | ||
2748 | else { | ||
2749 | if (prefetchable) | ||
2750 | dbg("using MEM for PMEM\n"); | ||
2751 | mem_node = get_resource(&(resources->mem_head), (ulong)base); | ||
2752 | } | ||
2753 | |||
2754 | /* allocate the resource to the board */ | ||
2755 | if (mem_node) { | ||
2756 | base64 = mem_node->base; | ||
2757 | mem_node->next = *res_node; | ||
2758 | *res_node = mem_node; | ||
2759 | dbg("Got 64 %s base=0x%x:%x(length=%x)\n", res_type_str, (u32)(base64 >> 32), | ||
2760 | (u32)base64, mem_node->length); | ||
2761 | } else { | ||
2762 | err("Got NO 64 %s resource(length=0x%x)\n", res_type_str, base); | ||
2763 | return -ENOMEM; | ||
2764 | } | ||
2765 | break; | ||
2766 | default: | ||
2767 | dbg("reserved BAR type=0x%x\n", temp_register); | ||
2768 | break; | ||
2769 | } | ||
2770 | |||
2771 | } | ||
2772 | |||
2773 | if (base64) { | ||
2774 | rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, (u32)base64); | ||
2775 | cloop += 4; | ||
2776 | base64 >>= 32; | ||
2777 | |||
2778 | if (base64) { | ||
2779 | dbg("%s: high dword of base64(0x%x) set to 0\n", __FUNCTION__, (u32)base64); | ||
2780 | base64 = 0x0L; | ||
2781 | } | ||
2782 | |||
2783 | rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, (u32)base64); | ||
2784 | } else { | ||
2785 | rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, base); | ||
2786 | } | ||
2787 | } /* End of base register loop */ | ||
2788 | |||
2789 | #if defined(CONFIG_X86_64) | ||
2790 | /* Figure out which interrupt pin this function uses */ | ||
2791 | rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_INTERRUPT_PIN, &temp_byte); | ||
2792 | |||
2793 | /* If this function needs an interrupt and we are behind a bridge | ||
2794 | and the pin is tied to something that's alread mapped, | ||
2795 | set this one the same | ||
2796 | */ | ||
2797 | if (temp_byte && resources->irqs && | ||
2798 | (resources->irqs->valid_INT & | ||
2799 | (0x01 << ((temp_byte + resources->irqs->barber_pole - 1) & 0x03)))) { | ||
2800 | /* We have to share with something already set up */ | ||
2801 | IRQ = resources->irqs->interrupt[(temp_byte + resources->irqs->barber_pole - 1) & 0x03]; | ||
2802 | } else { | ||
2803 | /* Program IRQ based on card type */ | ||
2804 | rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code); | ||
2805 | |||
2806 | if (class_code == PCI_BASE_CLASS_STORAGE) { | ||
2807 | IRQ = shpchp_disk_irq; | ||
2808 | } else { | ||
2809 | IRQ = shpchp_nic_irq; | ||
2810 | } | ||
2811 | } | ||
2812 | |||
2813 | /* IRQ Line */ | ||
2814 | rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_INTERRUPT_LINE, IRQ); | ||
2815 | |||
2816 | if (!behind_bridge) { | ||
2817 | rc = shpchp_set_irq(func->bus, func->device, temp_byte + 0x09, IRQ); | ||
2818 | if (rc) | ||
2819 | return(1); | ||
2820 | } else { | ||
2821 | /* TBD - this code may also belong in the other clause of this If statement */ | ||
2822 | resources->irqs->interrupt[(temp_byte + resources->irqs->barber_pole - 1) & 0x03] = IRQ; | ||
2823 | resources->irqs->valid_INT |= 0x01 << (temp_byte + resources->irqs->barber_pole - 1) & 0x03; | ||
2824 | } | ||
2825 | #endif | ||
2826 | /* Disable ROM base Address */ | ||
2827 | temp_word = 0x00L; | ||
2828 | rc = pci_bus_write_config_word (pci_bus, devfn, PCI_ROM_ADDRESS, temp_word); | ||
2829 | |||
2830 | /* Set HP parameters (Cache Line Size, Latency Timer) */ | ||
2831 | rc = shpchprm_set_hpp(ctrl, func, PCI_HEADER_TYPE_NORMAL); | ||
2832 | if (rc) | ||
2833 | return rc; | ||
2834 | |||
2835 | shpchprm_enable_card(ctrl, func, PCI_HEADER_TYPE_NORMAL); | ||
2836 | |||
2837 | dbg("PCI function Hot-Added s:b:d:f(%02x:%02x:%02x:%02x)\n", ctrl->seg, func->bus, func->device, func->function); | ||
2838 | } /* End of Not-A-Bridge else */ | ||
2839 | else { | ||
2840 | /* It's some strange type of PCI adapter (Cardbus?) */ | ||
2841 | return(DEVICE_TYPE_NOT_SUPPORTED); | ||
2842 | } | ||
2843 | |||
2844 | func->configured = 1; | ||
2845 | |||
2846 | return 0; | ||
2847 | } | ||
2848 | |||
diff --git a/drivers/pci/hotplug/shpchp_hpc.c b/drivers/pci/hotplug/shpchp_hpc.c new file mode 100644 index 000000000000..38c5d9066697 --- /dev/null +++ b/drivers/pci/hotplug/shpchp_hpc.c | |||
@@ -0,0 +1,1620 @@ | |||
1 | /* | ||
2 | * Standard PCI Hot Plug Driver | ||
3 | * | ||
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | ||
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * Copyright (C) 2001 IBM Corp. | ||
7 | * Copyright (C) 2003-2004 Intel Corporation | ||
8 | * | ||
9 | * All rights reserved. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or (at | ||
14 | * your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
19 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
20 | * details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
25 | * | ||
26 | * Send feedback to <greg@kroah.com>,<dely.l.sy@intel.com> | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | #include <linux/config.h> | ||
31 | #include <linux/kernel.h> | ||
32 | #include <linux/module.h> | ||
33 | #include <linux/types.h> | ||
34 | #include <linux/slab.h> | ||
35 | #include <linux/vmalloc.h> | ||
36 | #include <linux/interrupt.h> | ||
37 | #include <linux/spinlock.h> | ||
38 | #include <linux/delay.h> | ||
39 | #include <linux/pci.h> | ||
40 | #include <asm/system.h> | ||
41 | #include "shpchp.h" | ||
42 | |||
43 | #ifdef DEBUG | ||
44 | #define DBG_K_TRACE_ENTRY ((unsigned int)0x00000001) /* On function entry */ | ||
45 | #define DBG_K_TRACE_EXIT ((unsigned int)0x00000002) /* On function exit */ | ||
46 | #define DBG_K_INFO ((unsigned int)0x00000004) /* Info messages */ | ||
47 | #define DBG_K_ERROR ((unsigned int)0x00000008) /* Error messages */ | ||
48 | #define DBG_K_TRACE (DBG_K_TRACE_ENTRY|DBG_K_TRACE_EXIT) | ||
49 | #define DBG_K_STANDARD (DBG_K_INFO|DBG_K_ERROR|DBG_K_TRACE) | ||
50 | /* Redefine this flagword to set debug level */ | ||
51 | #define DEBUG_LEVEL DBG_K_STANDARD | ||
52 | |||
53 | #define DEFINE_DBG_BUFFER char __dbg_str_buf[256]; | ||
54 | |||
55 | #define DBG_PRINT( dbg_flags, args... ) \ | ||
56 | do { \ | ||
57 | if ( DEBUG_LEVEL & ( dbg_flags ) ) \ | ||
58 | { \ | ||
59 | int len; \ | ||
60 | len = sprintf( __dbg_str_buf, "%s:%d: %s: ", \ | ||
61 | __FILE__, __LINE__, __FUNCTION__ ); \ | ||
62 | sprintf( __dbg_str_buf + len, args ); \ | ||
63 | printk( KERN_NOTICE "%s\n", __dbg_str_buf ); \ | ||
64 | } \ | ||
65 | } while (0) | ||
66 | |||
67 | #define DBG_ENTER_ROUTINE DBG_PRINT (DBG_K_TRACE_ENTRY, "%s", "[Entry]"); | ||
68 | #define DBG_LEAVE_ROUTINE DBG_PRINT (DBG_K_TRACE_EXIT, "%s", "[Exit]"); | ||
69 | #else | ||
70 | #define DEFINE_DBG_BUFFER | ||
71 | #define DBG_ENTER_ROUTINE | ||
72 | #define DBG_LEAVE_ROUTINE | ||
73 | #endif /* DEBUG */ | ||
74 | |||
75 | /* Slot Available Register I field definition */ | ||
76 | #define SLOT_33MHZ 0x0000001f | ||
77 | #define SLOT_66MHZ_PCIX 0x00001f00 | ||
78 | #define SLOT_100MHZ_PCIX 0x001f0000 | ||
79 | #define SLOT_133MHZ_PCIX 0x1f000000 | ||
80 | |||
81 | /* Slot Available Register II field definition */ | ||
82 | #define SLOT_66MHZ 0x0000001f | ||
83 | #define SLOT_66MHZ_PCIX_266 0x00000f00 | ||
84 | #define SLOT_100MHZ_PCIX_266 0x0000f000 | ||
85 | #define SLOT_133MHZ_PCIX_266 0x000f0000 | ||
86 | #define SLOT_66MHZ_PCIX_533 0x00f00000 | ||
87 | #define SLOT_100MHZ_PCIX_533 0x0f000000 | ||
88 | #define SLOT_133MHZ_PCIX_533 0xf0000000 | ||
89 | |||
90 | |||
91 | /* Secondary Bus Configuration Register */ | ||
92 | /* For PI = 1, Bits 0 to 2 have been encoded as follows to show current bus speed/mode */ | ||
93 | #define PCI_33MHZ 0x0 | ||
94 | #define PCI_66MHZ 0x1 | ||
95 | #define PCIX_66MHZ 0x2 | ||
96 | #define PCIX_100MHZ 0x3 | ||
97 | #define PCIX_133MHZ 0x4 | ||
98 | |||
99 | /* For PI = 2, Bits 0 to 3 have been encoded as follows to show current bus speed/mode */ | ||
100 | #define PCI_33MHZ 0x0 | ||
101 | #define PCI_66MHZ 0x1 | ||
102 | #define PCIX_66MHZ 0x2 | ||
103 | #define PCIX_100MHZ 0x3 | ||
104 | #define PCIX_133MHZ 0x4 | ||
105 | #define PCIX_66MHZ_ECC 0x5 | ||
106 | #define PCIX_100MHZ_ECC 0x6 | ||
107 | #define PCIX_133MHZ_ECC 0x7 | ||
108 | #define PCIX_66MHZ_266 0x9 | ||
109 | #define PCIX_100MHZ_266 0xa | ||
110 | #define PCIX_133MHZ_266 0xb | ||
111 | #define PCIX_66MHZ_533 0x11 | ||
112 | #define PCIX_100MHZ_533 0x12 | ||
113 | #define PCIX_133MHZ_533 0x13 | ||
114 | |||
115 | /* Slot Configuration */ | ||
116 | #define SLOT_NUM 0x0000001F | ||
117 | #define FIRST_DEV_NUM 0x00001F00 | ||
118 | #define PSN 0x07FF0000 | ||
119 | #define UPDOWN 0x20000000 | ||
120 | #define MRLSENSOR 0x40000000 | ||
121 | #define ATTN_BUTTON 0x80000000 | ||
122 | |||
123 | /* Slot Status Field Definitions */ | ||
124 | /* Slot State */ | ||
125 | #define PWR_ONLY 0x0001 | ||
126 | #define ENABLED 0x0002 | ||
127 | #define DISABLED 0x0003 | ||
128 | |||
129 | /* Power Indicator State */ | ||
130 | #define PWR_LED_ON 0x0004 | ||
131 | #define PWR_LED_BLINK 0x0008 | ||
132 | #define PWR_LED_OFF 0x000c | ||
133 | |||
134 | /* Attention Indicator State */ | ||
135 | #define ATTEN_LED_ON 0x0010 | ||
136 | #define ATTEN_LED_BLINK 0x0020 | ||
137 | #define ATTEN_LED_OFF 0x0030 | ||
138 | |||
139 | /* Power Fault */ | ||
140 | #define pwr_fault 0x0040 | ||
141 | |||
142 | /* Attention Button */ | ||
143 | #define ATTEN_BUTTON 0x0080 | ||
144 | |||
145 | /* MRL Sensor */ | ||
146 | #define MRL_SENSOR 0x0100 | ||
147 | |||
148 | /* 66 MHz Capable */ | ||
149 | #define IS_66MHZ_CAP 0x0200 | ||
150 | |||
151 | /* PRSNT1#/PRSNT2# */ | ||
152 | #define SLOT_EMP 0x0c00 | ||
153 | |||
154 | /* PCI-X Capability */ | ||
155 | #define NON_PCIX 0x0000 | ||
156 | #define PCIX_66 0x1000 | ||
157 | #define PCIX_133 0x3000 | ||
158 | #define PCIX_266 0x4000 /* For PI = 2 only */ | ||
159 | #define PCIX_533 0x5000 /* For PI = 2 only */ | ||
160 | |||
161 | /* SHPC 'write' operations/commands */ | ||
162 | |||
163 | /* Slot operation - 0x00h to 0x3Fh */ | ||
164 | |||
165 | #define NO_CHANGE 0x00 | ||
166 | |||
167 | /* Slot state - Bits 0 & 1 of controller command register */ | ||
168 | #define SET_SLOT_PWR 0x01 | ||
169 | #define SET_SLOT_ENABLE 0x02 | ||
170 | #define SET_SLOT_DISABLE 0x03 | ||
171 | |||
172 | /* Power indicator state - Bits 2 & 3 of controller command register*/ | ||
173 | #define SET_PWR_ON 0x04 | ||
174 | #define SET_PWR_BLINK 0x08 | ||
175 | #define SET_PWR_OFF 0x0C | ||
176 | |||
177 | /* Attention indicator state - Bits 4 & 5 of controller command register*/ | ||
178 | #define SET_ATTN_ON 0x010 | ||
179 | #define SET_ATTN_BLINK 0x020 | ||
180 | #define SET_ATTN_OFF 0x030 | ||
181 | |||
182 | /* Set bus speed/mode A - 0x40h to 0x47h */ | ||
183 | #define SETA_PCI_33MHZ 0x40 | ||
184 | #define SETA_PCI_66MHZ 0x41 | ||
185 | #define SETA_PCIX_66MHZ 0x42 | ||
186 | #define SETA_PCIX_100MHZ 0x43 | ||
187 | #define SETA_PCIX_133MHZ 0x44 | ||
188 | #define RESERV_1 0x45 | ||
189 | #define RESERV_2 0x46 | ||
190 | #define RESERV_3 0x47 | ||
191 | |||
192 | /* Set bus speed/mode B - 0x50h to 0x5fh */ | ||
193 | #define SETB_PCI_33MHZ 0x50 | ||
194 | #define SETB_PCI_66MHZ 0x51 | ||
195 | #define SETB_PCIX_66MHZ_PM 0x52 | ||
196 | #define SETB_PCIX_100MHZ_PM 0x53 | ||
197 | #define SETB_PCIX_133MHZ_PM 0x54 | ||
198 | #define SETB_PCIX_66MHZ_EM 0x55 | ||
199 | #define SETB_PCIX_100MHZ_EM 0x56 | ||
200 | #define SETB_PCIX_133MHZ_EM 0x57 | ||
201 | #define SETB_PCIX_66MHZ_266 0x58 | ||
202 | #define SETB_PCIX_100MHZ_266 0x59 | ||
203 | #define SETB_PCIX_133MHZ_266 0x5a | ||
204 | #define SETB_PCIX_66MHZ_533 0x5b | ||
205 | #define SETB_PCIX_100MHZ_533 0x5c | ||
206 | #define SETB_PCIX_133MHZ_533 0x5d | ||
207 | |||
208 | |||
209 | /* Power-on all slots - 0x48h */ | ||
210 | #define SET_PWR_ON_ALL 0x48 | ||
211 | |||
212 | /* Enable all slots - 0x49h */ | ||
213 | #define SET_ENABLE_ALL 0x49 | ||
214 | |||
215 | /* SHPC controller command error code */ | ||
216 | #define SWITCH_OPEN 0x1 | ||
217 | #define INVALID_CMD 0x2 | ||
218 | #define INVALID_SPEED_MODE 0x4 | ||
219 | |||
220 | /* For accessing SHPC Working Register Set */ | ||
221 | #define DWORD_SELECT 0x2 | ||
222 | #define DWORD_DATA 0x4 | ||
223 | #define BASE_OFFSET 0x0 | ||
224 | |||
225 | /* Field Offset in Logical Slot Register - byte boundary */ | ||
226 | #define SLOT_EVENT_LATCH 0x2 | ||
227 | #define SLOT_SERR_INT_MASK 0x3 | ||
228 | |||
229 | static spinlock_t hpc_event_lock; | ||
230 | |||
231 | DEFINE_DBG_BUFFER /* Debug string buffer for entire HPC defined here */ | ||
232 | static struct php_ctlr_state_s *php_ctlr_list_head; /* HPC state linked list */ | ||
233 | static int ctlr_seq_num = 0; /* Controller sequenc # */ | ||
234 | static spinlock_t list_lock; | ||
235 | |||
236 | static irqreturn_t shpc_isr(int IRQ, void *dev_id, struct pt_regs *regs); | ||
237 | |||
238 | static void start_int_poll_timer(struct php_ctlr_state_s *php_ctlr, int seconds); | ||
239 | |||
240 | /* This is the interrupt polling timeout function. */ | ||
241 | static void int_poll_timeout(unsigned long lphp_ctlr) | ||
242 | { | ||
243 | struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *)lphp_ctlr; | ||
244 | |||
245 | DBG_ENTER_ROUTINE | ||
246 | |||
247 | if ( !php_ctlr ) { | ||
248 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
249 | return; | ||
250 | } | ||
251 | |||
252 | /* Poll for interrupt events. regs == NULL => polling */ | ||
253 | shpc_isr( 0, (void *)php_ctlr, NULL ); | ||
254 | |||
255 | init_timer(&php_ctlr->int_poll_timer); | ||
256 | if (!shpchp_poll_time) | ||
257 | shpchp_poll_time = 2; /* reset timer to poll in 2 secs if user doesn't specify at module installation*/ | ||
258 | |||
259 | start_int_poll_timer(php_ctlr, shpchp_poll_time); | ||
260 | |||
261 | return; | ||
262 | } | ||
263 | |||
264 | /* This function starts the interrupt polling timer. */ | ||
265 | static void start_int_poll_timer(struct php_ctlr_state_s *php_ctlr, int seconds) | ||
266 | { | ||
267 | if (!php_ctlr) { | ||
268 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
269 | return; | ||
270 | } | ||
271 | |||
272 | if ( ( seconds <= 0 ) || ( seconds > 60 ) ) | ||
273 | seconds = 2; /* Clamp to sane value */ | ||
274 | |||
275 | php_ctlr->int_poll_timer.function = &int_poll_timeout; | ||
276 | php_ctlr->int_poll_timer.data = (unsigned long)php_ctlr; /* Instance data */ | ||
277 | php_ctlr->int_poll_timer.expires = jiffies + seconds * HZ; | ||
278 | add_timer(&php_ctlr->int_poll_timer); | ||
279 | |||
280 | return; | ||
281 | } | ||
282 | |||
283 | static int shpc_write_cmd(struct slot *slot, u8 t_slot, u8 cmd) | ||
284 | { | ||
285 | struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; | ||
286 | u16 cmd_status; | ||
287 | int retval = 0; | ||
288 | u16 temp_word; | ||
289 | int i; | ||
290 | |||
291 | DBG_ENTER_ROUTINE | ||
292 | |||
293 | if (!php_ctlr) { | ||
294 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
295 | return -1; | ||
296 | } | ||
297 | |||
298 | for (i = 0; i < 10; i++) { | ||
299 | cmd_status = readw(php_ctlr->creg + CMD_STATUS); | ||
300 | |||
301 | if (!(cmd_status & 0x1)) | ||
302 | break; | ||
303 | /* Check every 0.1 sec for a total of 1 sec*/ | ||
304 | msleep(100); | ||
305 | } | ||
306 | |||
307 | cmd_status = readw(php_ctlr->creg + CMD_STATUS); | ||
308 | |||
309 | if (cmd_status & 0x1) { | ||
310 | /* After 1 sec and and the controller is still busy */ | ||
311 | err("%s : Controller is still busy after 1 sec.\n", __FUNCTION__); | ||
312 | return -1; | ||
313 | } | ||
314 | |||
315 | ++t_slot; | ||
316 | temp_word = (t_slot << 8) | (cmd & 0xFF); | ||
317 | dbg("%s: t_slot %x cmd %x\n", __FUNCTION__, t_slot, cmd); | ||
318 | |||
319 | /* To make sure the Controller Busy bit is 0 before we send out the | ||
320 | * command. | ||
321 | */ | ||
322 | writew(temp_word, php_ctlr->creg + CMD); | ||
323 | dbg("%s: temp_word written %x\n", __FUNCTION__, temp_word); | ||
324 | |||
325 | DBG_LEAVE_ROUTINE | ||
326 | return retval; | ||
327 | } | ||
328 | |||
329 | static int hpc_check_cmd_status(struct controller *ctrl) | ||
330 | { | ||
331 | struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) ctrl->hpc_ctlr_handle; | ||
332 | u16 cmd_status; | ||
333 | int retval = 0; | ||
334 | |||
335 | DBG_ENTER_ROUTINE | ||
336 | |||
337 | if (!ctrl->hpc_ctlr_handle) { | ||
338 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
339 | return -1; | ||
340 | } | ||
341 | |||
342 | cmd_status = readw(php_ctlr->creg + CMD_STATUS) & 0x000F; | ||
343 | |||
344 | switch (cmd_status >> 1) { | ||
345 | case 0: | ||
346 | retval = 0; | ||
347 | break; | ||
348 | case 1: | ||
349 | retval = SWITCH_OPEN; | ||
350 | err("%s: Switch opened!\n", __FUNCTION__); | ||
351 | break; | ||
352 | case 2: | ||
353 | retval = INVALID_CMD; | ||
354 | err("%s: Invalid HPC command!\n", __FUNCTION__); | ||
355 | break; | ||
356 | case 4: | ||
357 | retval = INVALID_SPEED_MODE; | ||
358 | err("%s: Invalid bus speed/mode!\n", __FUNCTION__); | ||
359 | break; | ||
360 | default: | ||
361 | retval = cmd_status; | ||
362 | } | ||
363 | |||
364 | DBG_LEAVE_ROUTINE | ||
365 | return retval; | ||
366 | } | ||
367 | |||
368 | |||
369 | static int hpc_get_attention_status(struct slot *slot, u8 *status) | ||
370 | { | ||
371 | struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; | ||
372 | u32 slot_reg; | ||
373 | u16 slot_status; | ||
374 | u8 atten_led_state; | ||
375 | |||
376 | DBG_ENTER_ROUTINE | ||
377 | |||
378 | if (!slot->ctrl->hpc_ctlr_handle) { | ||
379 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
380 | return -1; | ||
381 | } | ||
382 | |||
383 | slot_reg = readl(php_ctlr->creg + SLOT1 + 4*(slot->hp_slot)); | ||
384 | slot_status = (u16) slot_reg; | ||
385 | atten_led_state = (slot_status & 0x0030) >> 4; | ||
386 | |||
387 | switch (atten_led_state) { | ||
388 | case 0: | ||
389 | *status = 0xFF; /* Reserved */ | ||
390 | break; | ||
391 | case 1: | ||
392 | *status = 1; /* On */ | ||
393 | break; | ||
394 | case 2: | ||
395 | *status = 2; /* Blink */ | ||
396 | break; | ||
397 | case 3: | ||
398 | *status = 0; /* Off */ | ||
399 | break; | ||
400 | default: | ||
401 | *status = 0xFF; | ||
402 | break; | ||
403 | } | ||
404 | |||
405 | DBG_LEAVE_ROUTINE | ||
406 | return 0; | ||
407 | } | ||
408 | |||
409 | static int hpc_get_power_status(struct slot * slot, u8 *status) | ||
410 | { | ||
411 | struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; | ||
412 | u32 slot_reg; | ||
413 | u16 slot_status; | ||
414 | u8 slot_state; | ||
415 | int retval = 0; | ||
416 | |||
417 | DBG_ENTER_ROUTINE | ||
418 | |||
419 | if (!slot->ctrl->hpc_ctlr_handle) { | ||
420 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
421 | return -1; | ||
422 | } | ||
423 | |||
424 | slot_reg = readl(php_ctlr->creg + SLOT1 + 4*(slot->hp_slot)); | ||
425 | slot_status = (u16) slot_reg; | ||
426 | slot_state = (slot_status & 0x0003); | ||
427 | |||
428 | switch (slot_state) { | ||
429 | case 0: | ||
430 | *status = 0xFF; | ||
431 | break; | ||
432 | case 1: | ||
433 | *status = 2; /* Powered only */ | ||
434 | break; | ||
435 | case 2: | ||
436 | *status = 1; /* Enabled */ | ||
437 | break; | ||
438 | case 3: | ||
439 | *status = 0; /* Disabled */ | ||
440 | break; | ||
441 | default: | ||
442 | *status = 0xFF; | ||
443 | break; | ||
444 | } | ||
445 | |||
446 | DBG_LEAVE_ROUTINE | ||
447 | return retval; | ||
448 | } | ||
449 | |||
450 | |||
451 | static int hpc_get_latch_status(struct slot *slot, u8 *status) | ||
452 | { | ||
453 | struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; | ||
454 | u32 slot_reg; | ||
455 | u16 slot_status; | ||
456 | |||
457 | DBG_ENTER_ROUTINE | ||
458 | |||
459 | if (!slot->ctrl->hpc_ctlr_handle) { | ||
460 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
461 | return -1; | ||
462 | } | ||
463 | |||
464 | slot_reg = readl(php_ctlr->creg + SLOT1 + 4*(slot->hp_slot)); | ||
465 | slot_status = (u16)slot_reg; | ||
466 | |||
467 | *status = ((slot_status & 0x0100) == 0) ? 0 : 1; /* 0 -> close; 1 -> open */ | ||
468 | |||
469 | |||
470 | DBG_LEAVE_ROUTINE | ||
471 | return 0; | ||
472 | } | ||
473 | |||
474 | static int hpc_get_adapter_status(struct slot *slot, u8 *status) | ||
475 | { | ||
476 | struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; | ||
477 | u32 slot_reg; | ||
478 | u16 slot_status; | ||
479 | u8 card_state; | ||
480 | |||
481 | DBG_ENTER_ROUTINE | ||
482 | |||
483 | if (!slot->ctrl->hpc_ctlr_handle) { | ||
484 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
485 | return -1; | ||
486 | } | ||
487 | |||
488 | slot_reg = readl(php_ctlr->creg + SLOT1 + 4*(slot->hp_slot)); | ||
489 | slot_status = (u16)slot_reg; | ||
490 | card_state = (u8)((slot_status & 0x0C00) >> 10); | ||
491 | *status = (card_state != 0x3) ? 1 : 0; | ||
492 | |||
493 | DBG_LEAVE_ROUTINE | ||
494 | return 0; | ||
495 | } | ||
496 | |||
497 | static int hpc_get_prog_int(struct slot *slot, u8 *prog_int) | ||
498 | { | ||
499 | struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; | ||
500 | |||
501 | DBG_ENTER_ROUTINE | ||
502 | |||
503 | if (!slot->ctrl->hpc_ctlr_handle) { | ||
504 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
505 | return -1; | ||
506 | } | ||
507 | |||
508 | *prog_int = readb(php_ctlr->creg + PROG_INTERFACE); | ||
509 | |||
510 | DBG_LEAVE_ROUTINE | ||
511 | return 0; | ||
512 | } | ||
513 | |||
514 | static int hpc_get_adapter_speed(struct slot *slot, enum pci_bus_speed *value) | ||
515 | { | ||
516 | struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; | ||
517 | u32 slot_reg; | ||
518 | u16 slot_status, sec_bus_status; | ||
519 | u8 m66_cap, pcix_cap, pi; | ||
520 | int retval = 0; | ||
521 | |||
522 | DBG_ENTER_ROUTINE | ||
523 | |||
524 | if (!slot->ctrl->hpc_ctlr_handle) { | ||
525 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
526 | return -1; | ||
527 | } | ||
528 | |||
529 | if (slot->hp_slot >= php_ctlr->num_slots) { | ||
530 | err("%s: Invalid HPC slot number!\n", __FUNCTION__); | ||
531 | return -1; | ||
532 | } | ||
533 | |||
534 | pi = readb(php_ctlr->creg + PROG_INTERFACE); | ||
535 | slot_reg = readl(php_ctlr->creg + SLOT1 + 4*(slot->hp_slot)); | ||
536 | dbg("%s: pi = %d, slot_reg = %x\n", __FUNCTION__, pi, slot_reg); | ||
537 | slot_status = (u16) slot_reg; | ||
538 | dbg("%s: slot_status = %x\n", __FUNCTION__, slot_status); | ||
539 | sec_bus_status = readw(php_ctlr->creg + SEC_BUS_CONFIG); | ||
540 | |||
541 | pcix_cap = (u8) ((slot_status & 0x3000) >> 12); | ||
542 | dbg("%s: pcix_cap = %x\n", __FUNCTION__, pcix_cap); | ||
543 | m66_cap = (u8) ((slot_status & 0x0200) >> 9); | ||
544 | dbg("%s: m66_cap = %x\n", __FUNCTION__, m66_cap); | ||
545 | |||
546 | |||
547 | if (pi == 2) { | ||
548 | switch (pcix_cap) { | ||
549 | case 0: | ||
550 | *value = m66_cap ? PCI_SPEED_66MHz : PCI_SPEED_33MHz; | ||
551 | break; | ||
552 | case 1: | ||
553 | *value = PCI_SPEED_66MHz_PCIX; | ||
554 | break; | ||
555 | case 3: | ||
556 | *value = PCI_SPEED_133MHz_PCIX; | ||
557 | break; | ||
558 | case 4: | ||
559 | *value = PCI_SPEED_133MHz_PCIX_266; | ||
560 | break; | ||
561 | case 5: | ||
562 | *value = PCI_SPEED_133MHz_PCIX_533; | ||
563 | break; | ||
564 | case 2: /* Reserved */ | ||
565 | default: | ||
566 | *value = PCI_SPEED_UNKNOWN; | ||
567 | retval = -ENODEV; | ||
568 | break; | ||
569 | } | ||
570 | } else { | ||
571 | switch (pcix_cap) { | ||
572 | case 0: | ||
573 | *value = m66_cap ? PCI_SPEED_66MHz : PCI_SPEED_33MHz; | ||
574 | break; | ||
575 | case 1: | ||
576 | *value = PCI_SPEED_66MHz_PCIX; | ||
577 | break; | ||
578 | case 3: | ||
579 | *value = PCI_SPEED_133MHz_PCIX; | ||
580 | break; | ||
581 | case 2: /* Reserved */ | ||
582 | default: | ||
583 | *value = PCI_SPEED_UNKNOWN; | ||
584 | retval = -ENODEV; | ||
585 | break; | ||
586 | } | ||
587 | } | ||
588 | |||
589 | dbg("Adapter speed = %d\n", *value); | ||
590 | |||
591 | DBG_LEAVE_ROUTINE | ||
592 | return retval; | ||
593 | } | ||
594 | |||
595 | static int hpc_get_mode1_ECC_cap(struct slot *slot, u8 *mode) | ||
596 | { | ||
597 | struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; | ||
598 | u16 sec_bus_status; | ||
599 | u8 pi; | ||
600 | int retval = 0; | ||
601 | |||
602 | DBG_ENTER_ROUTINE | ||
603 | |||
604 | if (!slot->ctrl->hpc_ctlr_handle) { | ||
605 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
606 | return -1; | ||
607 | } | ||
608 | |||
609 | pi = readb(php_ctlr->creg + PROG_INTERFACE); | ||
610 | sec_bus_status = readw(php_ctlr->creg + SEC_BUS_CONFIG); | ||
611 | |||
612 | if (pi == 2) { | ||
613 | *mode = (sec_bus_status & 0x0100) >> 7; | ||
614 | } else { | ||
615 | retval = -1; | ||
616 | } | ||
617 | |||
618 | dbg("Mode 1 ECC cap = %d\n", *mode); | ||
619 | |||
620 | DBG_LEAVE_ROUTINE | ||
621 | return retval; | ||
622 | } | ||
623 | |||
624 | static int hpc_query_power_fault(struct slot * slot) | ||
625 | { | ||
626 | struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; | ||
627 | u32 slot_reg; | ||
628 | u16 slot_status; | ||
629 | u8 pwr_fault_state, status; | ||
630 | |||
631 | DBG_ENTER_ROUTINE | ||
632 | |||
633 | if (!slot->ctrl->hpc_ctlr_handle) { | ||
634 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
635 | return -1; | ||
636 | } | ||
637 | |||
638 | slot_reg = readl(php_ctlr->creg + SLOT1 + 4*(slot->hp_slot)); | ||
639 | slot_status = (u16) slot_reg; | ||
640 | pwr_fault_state = (slot_status & 0x0040) >> 7; | ||
641 | status = (pwr_fault_state == 1) ? 0 : 1; | ||
642 | |||
643 | DBG_LEAVE_ROUTINE | ||
644 | /* Note: Logic 0 => fault */ | ||
645 | return status; | ||
646 | } | ||
647 | |||
648 | static int hpc_set_attention_status(struct slot *slot, u8 value) | ||
649 | { | ||
650 | struct php_ctlr_state_s *php_ctlr =(struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; | ||
651 | u8 slot_cmd = 0; | ||
652 | int rc = 0; | ||
653 | |||
654 | if (!slot->ctrl->hpc_ctlr_handle) { | ||
655 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
656 | return -1; | ||
657 | } | ||
658 | |||
659 | if (slot->hp_slot >= php_ctlr->num_slots) { | ||
660 | err("%s: Invalid HPC slot number!\n", __FUNCTION__); | ||
661 | return -1; | ||
662 | } | ||
663 | |||
664 | switch (value) { | ||
665 | case 0 : | ||
666 | slot_cmd = 0x30; /* OFF */ | ||
667 | break; | ||
668 | case 1: | ||
669 | slot_cmd = 0x10; /* ON */ | ||
670 | break; | ||
671 | case 2: | ||
672 | slot_cmd = 0x20; /* BLINK */ | ||
673 | break; | ||
674 | default: | ||
675 | return -1; | ||
676 | } | ||
677 | |||
678 | shpc_write_cmd(slot, slot->hp_slot, slot_cmd); | ||
679 | |||
680 | return rc; | ||
681 | } | ||
682 | |||
683 | |||
684 | static void hpc_set_green_led_on(struct slot *slot) | ||
685 | { | ||
686 | struct php_ctlr_state_s *php_ctlr =(struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; | ||
687 | u8 slot_cmd; | ||
688 | |||
689 | if (!slot->ctrl->hpc_ctlr_handle) { | ||
690 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
691 | return ; | ||
692 | } | ||
693 | |||
694 | if (slot->hp_slot >= php_ctlr->num_slots) { | ||
695 | err("%s: Invalid HPC slot number!\n", __FUNCTION__); | ||
696 | return ; | ||
697 | } | ||
698 | |||
699 | slot_cmd = 0x04; | ||
700 | |||
701 | shpc_write_cmd(slot, slot->hp_slot, slot_cmd); | ||
702 | |||
703 | return; | ||
704 | } | ||
705 | |||
706 | static void hpc_set_green_led_off(struct slot *slot) | ||
707 | { | ||
708 | struct php_ctlr_state_s *php_ctlr =(struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; | ||
709 | u8 slot_cmd; | ||
710 | |||
711 | if (!slot->ctrl->hpc_ctlr_handle) { | ||
712 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
713 | return ; | ||
714 | } | ||
715 | |||
716 | if (slot->hp_slot >= php_ctlr->num_slots) { | ||
717 | err("%s: Invalid HPC slot number!\n", __FUNCTION__); | ||
718 | return ; | ||
719 | } | ||
720 | |||
721 | slot_cmd = 0x0C; | ||
722 | |||
723 | shpc_write_cmd(slot, slot->hp_slot, slot_cmd); | ||
724 | |||
725 | return; | ||
726 | } | ||
727 | |||
728 | static void hpc_set_green_led_blink(struct slot *slot) | ||
729 | { | ||
730 | struct php_ctlr_state_s *php_ctlr =(struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; | ||
731 | u8 slot_cmd; | ||
732 | |||
733 | if (!slot->ctrl->hpc_ctlr_handle) { | ||
734 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
735 | return ; | ||
736 | } | ||
737 | |||
738 | if (slot->hp_slot >= php_ctlr->num_slots) { | ||
739 | err("%s: Invalid HPC slot number!\n", __FUNCTION__); | ||
740 | return ; | ||
741 | } | ||
742 | |||
743 | slot_cmd = 0x08; | ||
744 | |||
745 | shpc_write_cmd(slot, slot->hp_slot, slot_cmd); | ||
746 | |||
747 | return; | ||
748 | } | ||
749 | |||
750 | int shpc_get_ctlr_slot_config(struct controller *ctrl, | ||
751 | int *num_ctlr_slots, /* number of slots in this HPC */ | ||
752 | int *first_device_num, /* PCI dev num of the first slot in this SHPC */ | ||
753 | int *physical_slot_num, /* phy slot num of the first slot in this SHPC */ | ||
754 | int *updown, /* physical_slot_num increament: 1 or -1 */ | ||
755 | int *flags) | ||
756 | { | ||
757 | struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) ctrl->hpc_ctlr_handle; | ||
758 | |||
759 | DBG_ENTER_ROUTINE | ||
760 | |||
761 | if (!ctrl->hpc_ctlr_handle) { | ||
762 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
763 | return -1; | ||
764 | } | ||
765 | |||
766 | *first_device_num = php_ctlr->slot_device_offset; /* Obtained in shpc_init() */ | ||
767 | *num_ctlr_slots = php_ctlr->num_slots; /* Obtained in shpc_init() */ | ||
768 | |||
769 | *physical_slot_num = (readl(php_ctlr->creg + SLOT_CONFIG) & PSN) >> 16; | ||
770 | dbg("%s: physical_slot_num = %x\n", __FUNCTION__, *physical_slot_num); | ||
771 | *updown = ((readl(php_ctlr->creg + SLOT_CONFIG) & UPDOWN ) >> 29) ? 1 : -1; | ||
772 | |||
773 | DBG_LEAVE_ROUTINE | ||
774 | return 0; | ||
775 | } | ||
776 | |||
777 | static void hpc_release_ctlr(struct controller *ctrl) | ||
778 | { | ||
779 | struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) ctrl->hpc_ctlr_handle; | ||
780 | struct php_ctlr_state_s *p, *p_prev; | ||
781 | |||
782 | DBG_ENTER_ROUTINE | ||
783 | |||
784 | if (!ctrl->hpc_ctlr_handle) { | ||
785 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
786 | return ; | ||
787 | } | ||
788 | |||
789 | if (shpchp_poll_mode) { | ||
790 | del_timer(&php_ctlr->int_poll_timer); | ||
791 | } else { | ||
792 | if (php_ctlr->irq) { | ||
793 | free_irq(php_ctlr->irq, ctrl); | ||
794 | php_ctlr->irq = 0; | ||
795 | pci_disable_msi(php_ctlr->pci_dev); | ||
796 | } | ||
797 | } | ||
798 | if (php_ctlr->pci_dev) { | ||
799 | dbg("%s: before calling iounmap & release_mem_region\n", __FUNCTION__); | ||
800 | iounmap(php_ctlr->creg); | ||
801 | release_mem_region(pci_resource_start(php_ctlr->pci_dev, 0), pci_resource_len(php_ctlr->pci_dev, 0)); | ||
802 | dbg("%s: before calling iounmap & release_mem_region\n", __FUNCTION__); | ||
803 | php_ctlr->pci_dev = NULL; | ||
804 | } | ||
805 | |||
806 | spin_lock(&list_lock); | ||
807 | p = php_ctlr_list_head; | ||
808 | p_prev = NULL; | ||
809 | while (p) { | ||
810 | if (p == php_ctlr) { | ||
811 | if (p_prev) | ||
812 | p_prev->pnext = p->pnext; | ||
813 | else | ||
814 | php_ctlr_list_head = p->pnext; | ||
815 | break; | ||
816 | } else { | ||
817 | p_prev = p; | ||
818 | p = p->pnext; | ||
819 | } | ||
820 | } | ||
821 | spin_unlock(&list_lock); | ||
822 | |||
823 | kfree(php_ctlr); | ||
824 | |||
825 | DBG_LEAVE_ROUTINE | ||
826 | |||
827 | } | ||
828 | |||
829 | static int hpc_power_on_slot(struct slot * slot) | ||
830 | { | ||
831 | struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; | ||
832 | u8 slot_cmd; | ||
833 | int retval = 0; | ||
834 | |||
835 | DBG_ENTER_ROUTINE | ||
836 | |||
837 | if (!slot->ctrl->hpc_ctlr_handle) { | ||
838 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
839 | return -1; | ||
840 | } | ||
841 | |||
842 | if (slot->hp_slot >= php_ctlr->num_slots) { | ||
843 | err("%s: Invalid HPC slot number!\n", __FUNCTION__); | ||
844 | return -1; | ||
845 | } | ||
846 | slot_cmd = 0x01; | ||
847 | |||
848 | retval = shpc_write_cmd(slot, slot->hp_slot, slot_cmd); | ||
849 | |||
850 | if (retval) { | ||
851 | err("%s: Write command failed!\n", __FUNCTION__); | ||
852 | return -1; | ||
853 | } | ||
854 | |||
855 | DBG_LEAVE_ROUTINE | ||
856 | |||
857 | return retval; | ||
858 | } | ||
859 | |||
860 | static int hpc_slot_enable(struct slot * slot) | ||
861 | { | ||
862 | struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; | ||
863 | u8 slot_cmd; | ||
864 | int retval = 0; | ||
865 | |||
866 | DBG_ENTER_ROUTINE | ||
867 | |||
868 | if (!slot->ctrl->hpc_ctlr_handle) { | ||
869 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
870 | return -1; | ||
871 | } | ||
872 | |||
873 | if (slot->hp_slot >= php_ctlr->num_slots) { | ||
874 | err("%s: Invalid HPC slot number!\n", __FUNCTION__); | ||
875 | return -1; | ||
876 | } | ||
877 | /* 3A => Slot - Enable, Power Indicator - Blink, Attention Indicator - Off */ | ||
878 | slot_cmd = 0x3A; | ||
879 | |||
880 | retval = shpc_write_cmd(slot, slot->hp_slot, slot_cmd); | ||
881 | |||
882 | if (retval) { | ||
883 | err("%s: Write command failed!\n", __FUNCTION__); | ||
884 | return -1; | ||
885 | } | ||
886 | |||
887 | DBG_LEAVE_ROUTINE | ||
888 | return retval; | ||
889 | } | ||
890 | |||
891 | static int hpc_slot_disable(struct slot * slot) | ||
892 | { | ||
893 | struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; | ||
894 | u8 slot_cmd; | ||
895 | int retval = 0; | ||
896 | |||
897 | DBG_ENTER_ROUTINE | ||
898 | |||
899 | if (!slot->ctrl->hpc_ctlr_handle) { | ||
900 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
901 | return -1; | ||
902 | } | ||
903 | |||
904 | if (slot->hp_slot >= php_ctlr->num_slots) { | ||
905 | err("%s: Invalid HPC slot number!\n", __FUNCTION__); | ||
906 | return -1; | ||
907 | } | ||
908 | |||
909 | /* 1F => Slot - Disable, Power Indicator - Off, Attention Indicator - On */ | ||
910 | slot_cmd = 0x1F; | ||
911 | |||
912 | retval = shpc_write_cmd(slot, slot->hp_slot, slot_cmd); | ||
913 | |||
914 | if (retval) { | ||
915 | err("%s: Write command failed!\n", __FUNCTION__); | ||
916 | return -1; | ||
917 | } | ||
918 | |||
919 | DBG_LEAVE_ROUTINE | ||
920 | return retval; | ||
921 | } | ||
922 | |||
923 | static int hpc_enable_all_slots( struct slot *slot ) | ||
924 | { | ||
925 | int retval = 0; | ||
926 | |||
927 | DBG_ENTER_ROUTINE | ||
928 | |||
929 | if (!slot->ctrl->hpc_ctlr_handle) { | ||
930 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
931 | return -1; | ||
932 | } | ||
933 | |||
934 | retval = shpc_write_cmd(slot, 0, SET_ENABLE_ALL); | ||
935 | if (retval) { | ||
936 | err("%s: Write command failed!\n", __FUNCTION__); | ||
937 | return -1; | ||
938 | } | ||
939 | |||
940 | DBG_LEAVE_ROUTINE | ||
941 | |||
942 | return retval; | ||
943 | } | ||
944 | |||
945 | static int hpc_pwr_on_all_slots(struct slot *slot) | ||
946 | { | ||
947 | int retval = 0; | ||
948 | |||
949 | DBG_ENTER_ROUTINE | ||
950 | |||
951 | retval = shpc_write_cmd(slot, 0, SET_PWR_ON_ALL); | ||
952 | |||
953 | if (retval) { | ||
954 | err("%s: Write command failed!\n", __FUNCTION__); | ||
955 | return -1; | ||
956 | } | ||
957 | |||
958 | DBG_LEAVE_ROUTINE | ||
959 | return retval; | ||
960 | } | ||
961 | |||
962 | static int hpc_set_bus_speed_mode(struct slot * slot, enum pci_bus_speed value) | ||
963 | { | ||
964 | u8 slot_cmd; | ||
965 | u8 pi; | ||
966 | int retval = 0; | ||
967 | struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; | ||
968 | |||
969 | DBG_ENTER_ROUTINE | ||
970 | |||
971 | if (!slot->ctrl->hpc_ctlr_handle) { | ||
972 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
973 | return -1; | ||
974 | } | ||
975 | |||
976 | pi = readb(php_ctlr->creg + PROG_INTERFACE); | ||
977 | |||
978 | if (pi == 1) { | ||
979 | switch (value) { | ||
980 | case 0: | ||
981 | slot_cmd = SETA_PCI_33MHZ; | ||
982 | break; | ||
983 | case 1: | ||
984 | slot_cmd = SETA_PCI_66MHZ; | ||
985 | break; | ||
986 | case 2: | ||
987 | slot_cmd = SETA_PCIX_66MHZ; | ||
988 | break; | ||
989 | case 3: | ||
990 | slot_cmd = SETA_PCIX_100MHZ; | ||
991 | break; | ||
992 | case 4: | ||
993 | slot_cmd = SETA_PCIX_133MHZ; | ||
994 | break; | ||
995 | default: | ||
996 | slot_cmd = PCI_SPEED_UNKNOWN; | ||
997 | retval = -ENODEV; | ||
998 | return retval; | ||
999 | } | ||
1000 | } else { | ||
1001 | switch (value) { | ||
1002 | case 0: | ||
1003 | slot_cmd = SETB_PCI_33MHZ; | ||
1004 | break; | ||
1005 | case 1: | ||
1006 | slot_cmd = SETB_PCI_66MHZ; | ||
1007 | break; | ||
1008 | case 2: | ||
1009 | slot_cmd = SETB_PCIX_66MHZ_PM; | ||
1010 | break; | ||
1011 | case 3: | ||
1012 | slot_cmd = SETB_PCIX_100MHZ_PM; | ||
1013 | break; | ||
1014 | case 4: | ||
1015 | slot_cmd = SETB_PCIX_133MHZ_PM; | ||
1016 | break; | ||
1017 | case 5: | ||
1018 | slot_cmd = SETB_PCIX_66MHZ_EM; | ||
1019 | break; | ||
1020 | case 6: | ||
1021 | slot_cmd = SETB_PCIX_100MHZ_EM; | ||
1022 | break; | ||
1023 | case 7: | ||
1024 | slot_cmd = SETB_PCIX_133MHZ_EM; | ||
1025 | break; | ||
1026 | case 8: | ||
1027 | slot_cmd = SETB_PCIX_66MHZ_266; | ||
1028 | break; | ||
1029 | case 0x9: | ||
1030 | slot_cmd = SETB_PCIX_100MHZ_266; | ||
1031 | break; | ||
1032 | case 0xa: | ||
1033 | slot_cmd = SETB_PCIX_133MHZ_266; | ||
1034 | break; | ||
1035 | case 0xb: | ||
1036 | slot_cmd = SETB_PCIX_66MHZ_533; | ||
1037 | break; | ||
1038 | case 0xc: | ||
1039 | slot_cmd = SETB_PCIX_100MHZ_533; | ||
1040 | break; | ||
1041 | case 0xd: | ||
1042 | slot_cmd = SETB_PCIX_133MHZ_533; | ||
1043 | break; | ||
1044 | default: | ||
1045 | slot_cmd = PCI_SPEED_UNKNOWN; | ||
1046 | retval = -ENODEV; | ||
1047 | return retval; | ||
1048 | } | ||
1049 | |||
1050 | } | ||
1051 | retval = shpc_write_cmd(slot, 0, slot_cmd); | ||
1052 | if (retval) { | ||
1053 | err("%s: Write command failed!\n", __FUNCTION__); | ||
1054 | return -1; | ||
1055 | } | ||
1056 | |||
1057 | DBG_LEAVE_ROUTINE | ||
1058 | return retval; | ||
1059 | } | ||
1060 | |||
1061 | static irqreturn_t shpc_isr(int IRQ, void *dev_id, struct pt_regs *regs) | ||
1062 | { | ||
1063 | struct controller *ctrl = NULL; | ||
1064 | struct php_ctlr_state_s *php_ctlr; | ||
1065 | u8 schedule_flag = 0; | ||
1066 | u8 temp_byte; | ||
1067 | u32 temp_dword, intr_loc, intr_loc2; | ||
1068 | int hp_slot; | ||
1069 | |||
1070 | if (!dev_id) | ||
1071 | return IRQ_NONE; | ||
1072 | |||
1073 | if (!shpchp_poll_mode) { | ||
1074 | ctrl = (struct controller *)dev_id; | ||
1075 | php_ctlr = ctrl->hpc_ctlr_handle; | ||
1076 | } else { | ||
1077 | php_ctlr = (struct php_ctlr_state_s *) dev_id; | ||
1078 | ctrl = (struct controller *)php_ctlr->callback_instance_id; | ||
1079 | } | ||
1080 | |||
1081 | if (!ctrl) | ||
1082 | return IRQ_NONE; | ||
1083 | |||
1084 | if (!php_ctlr || !php_ctlr->creg) | ||
1085 | return IRQ_NONE; | ||
1086 | |||
1087 | /* Check to see if it was our interrupt */ | ||
1088 | intr_loc = readl(php_ctlr->creg + INTR_LOC); | ||
1089 | |||
1090 | if (!intr_loc) | ||
1091 | return IRQ_NONE; | ||
1092 | dbg("%s: shpc_isr proceeds\n", __FUNCTION__); | ||
1093 | dbg("%s: intr_loc = %x\n",__FUNCTION__, intr_loc); | ||
1094 | |||
1095 | if(!shpchp_poll_mode) { | ||
1096 | /* Mask Global Interrupt Mask - see implementation note on p. 139 */ | ||
1097 | /* of SHPC spec rev 1.0*/ | ||
1098 | temp_dword = readl(php_ctlr->creg + SERR_INTR_ENABLE); | ||
1099 | dbg("%s: Before masking global interrupt, temp_dword = %x\n", | ||
1100 | __FUNCTION__, temp_dword); | ||
1101 | temp_dword |= 0x00000001; | ||
1102 | dbg("%s: After masking global interrupt, temp_dword = %x\n", | ||
1103 | __FUNCTION__, temp_dword); | ||
1104 | writel(temp_dword, php_ctlr->creg + SERR_INTR_ENABLE); | ||
1105 | |||
1106 | intr_loc2 = readl(php_ctlr->creg + INTR_LOC); | ||
1107 | dbg("%s: intr_loc2 = %x\n",__FUNCTION__, intr_loc2); | ||
1108 | } | ||
1109 | |||
1110 | if (intr_loc & 0x0001) { | ||
1111 | /* | ||
1112 | * Command Complete Interrupt Pending | ||
1113 | * RO only - clear by writing 0 to the Command Completion | ||
1114 | * Detect bit in Controller SERR-INT register | ||
1115 | */ | ||
1116 | temp_dword = readl(php_ctlr->creg + SERR_INTR_ENABLE); | ||
1117 | dbg("%s: Before clearing CCIP, temp_dword = %x\n", | ||
1118 | __FUNCTION__, temp_dword); | ||
1119 | temp_dword &= 0xfffeffff; | ||
1120 | dbg("%s: After clearing CCIP, temp_dword = %x\n", | ||
1121 | __FUNCTION__, temp_dword); | ||
1122 | writel(temp_dword, php_ctlr->creg + SERR_INTR_ENABLE); | ||
1123 | wake_up_interruptible(&ctrl->queue); | ||
1124 | } | ||
1125 | |||
1126 | if ((intr_loc = (intr_loc >> 1)) == 0) { | ||
1127 | /* Unmask Global Interrupt Mask */ | ||
1128 | temp_dword = readl(php_ctlr->creg + SERR_INTR_ENABLE); | ||
1129 | dbg("%s: 1-Before unmasking global interrupt, temp_dword = %x\n", | ||
1130 | __FUNCTION__, temp_dword); | ||
1131 | temp_dword &= 0xfffffffe; | ||
1132 | dbg("%s: 1-After unmasking global interrupt, temp_dword = %x\n", | ||
1133 | __FUNCTION__, temp_dword); | ||
1134 | writel(temp_dword, php_ctlr->creg + SERR_INTR_ENABLE); | ||
1135 | |||
1136 | return IRQ_NONE; | ||
1137 | } | ||
1138 | |||
1139 | for (hp_slot = 0; hp_slot < ctrl->num_slots; hp_slot++) { | ||
1140 | /* To find out which slot has interrupt pending */ | ||
1141 | if ((intr_loc >> hp_slot) & 0x01) { | ||
1142 | temp_dword = readl(php_ctlr->creg + SLOT1 + (4*hp_slot)); | ||
1143 | dbg("%s: Slot %x with intr, temp_dword = %x\n", | ||
1144 | __FUNCTION__, hp_slot, temp_dword); | ||
1145 | temp_byte = (temp_dword >> 16) & 0xFF; | ||
1146 | dbg("%s: Slot with intr, temp_byte = %x\n", | ||
1147 | __FUNCTION__, temp_byte); | ||
1148 | if ((php_ctlr->switch_change_callback) && (temp_byte & 0x08)) | ||
1149 | schedule_flag += php_ctlr->switch_change_callback( | ||
1150 | hp_slot, php_ctlr->callback_instance_id); | ||
1151 | if ((php_ctlr->attention_button_callback) && (temp_byte & 0x04)) | ||
1152 | schedule_flag += php_ctlr->attention_button_callback( | ||
1153 | hp_slot, php_ctlr->callback_instance_id); | ||
1154 | if ((php_ctlr->presence_change_callback) && (temp_byte & 0x01)) | ||
1155 | schedule_flag += php_ctlr->presence_change_callback( | ||
1156 | hp_slot , php_ctlr->callback_instance_id); | ||
1157 | if ((php_ctlr->power_fault_callback) && (temp_byte & 0x12)) | ||
1158 | schedule_flag += php_ctlr->power_fault_callback( | ||
1159 | hp_slot, php_ctlr->callback_instance_id); | ||
1160 | |||
1161 | /* Clear all slot events */ | ||
1162 | temp_dword = 0xe01f3fff; | ||
1163 | dbg("%s: Clearing slot events, temp_dword = %x\n", | ||
1164 | __FUNCTION__, temp_dword); | ||
1165 | writel(temp_dword, php_ctlr->creg + SLOT1 + (4*hp_slot)); | ||
1166 | |||
1167 | intr_loc2 = readl(php_ctlr->creg + INTR_LOC); | ||
1168 | dbg("%s: intr_loc2 = %x\n",__FUNCTION__, intr_loc2); | ||
1169 | } | ||
1170 | } | ||
1171 | if (!shpchp_poll_mode) { | ||
1172 | /* Unmask Global Interrupt Mask */ | ||
1173 | temp_dword = readl(php_ctlr->creg + SERR_INTR_ENABLE); | ||
1174 | dbg("%s: 2-Before unmasking global interrupt, temp_dword = %x\n", | ||
1175 | __FUNCTION__, temp_dword); | ||
1176 | temp_dword &= 0xfffffffe; | ||
1177 | dbg("%s: 2-After unmasking global interrupt, temp_dword = %x\n", | ||
1178 | __FUNCTION__, temp_dword); | ||
1179 | writel(temp_dword, php_ctlr->creg + SERR_INTR_ENABLE); | ||
1180 | } | ||
1181 | |||
1182 | return IRQ_HANDLED; | ||
1183 | } | ||
1184 | |||
1185 | static int hpc_get_max_bus_speed (struct slot *slot, enum pci_bus_speed *value) | ||
1186 | { | ||
1187 | struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; | ||
1188 | enum pci_bus_speed bus_speed = PCI_SPEED_UNKNOWN; | ||
1189 | int retval = 0; | ||
1190 | u8 pi; | ||
1191 | u32 slot_avail1, slot_avail2; | ||
1192 | int slot_num; | ||
1193 | |||
1194 | DBG_ENTER_ROUTINE | ||
1195 | |||
1196 | if (!slot->ctrl->hpc_ctlr_handle) { | ||
1197 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
1198 | return -1; | ||
1199 | } | ||
1200 | |||
1201 | if (slot->hp_slot >= php_ctlr->num_slots) { | ||
1202 | err("%s: Invalid HPC slot number!\n", __FUNCTION__); | ||
1203 | return -1; | ||
1204 | } | ||
1205 | |||
1206 | pi = readb(php_ctlr->creg + PROG_INTERFACE); | ||
1207 | slot_avail1 = readl(php_ctlr->creg + SLOT_AVAIL1); | ||
1208 | slot_avail2 = readl(php_ctlr->creg + SLOT_AVAIL2); | ||
1209 | |||
1210 | if (pi == 2) { | ||
1211 | if ((slot_num = ((slot_avail2 & SLOT_133MHZ_PCIX_533) >> 27) ) != 0 ) | ||
1212 | bus_speed = PCIX_133MHZ_533; | ||
1213 | else if ((slot_num = ((slot_avail2 & SLOT_100MHZ_PCIX_533) >> 23) ) != 0 ) | ||
1214 | bus_speed = PCIX_100MHZ_533; | ||
1215 | else if ((slot_num = ((slot_avail2 & SLOT_66MHZ_PCIX_533) >> 19) ) != 0 ) | ||
1216 | bus_speed = PCIX_66MHZ_533; | ||
1217 | else if ((slot_num = ((slot_avail2 & SLOT_133MHZ_PCIX_266) >> 15) ) != 0 ) | ||
1218 | bus_speed = PCIX_133MHZ_266; | ||
1219 | else if ((slot_num = ((slot_avail2 & SLOT_100MHZ_PCIX_266) >> 11) ) != 0 ) | ||
1220 | bus_speed = PCIX_100MHZ_266; | ||
1221 | else if ((slot_num = ((slot_avail2 & SLOT_66MHZ_PCIX_266) >> 7) ) != 0 ) | ||
1222 | bus_speed = PCIX_66MHZ_266; | ||
1223 | else if ((slot_num = ((slot_avail1 & SLOT_133MHZ_PCIX) >> 23) ) != 0 ) | ||
1224 | bus_speed = PCIX_133MHZ; | ||
1225 | else if ((slot_num = ((slot_avail1 & SLOT_100MHZ_PCIX) >> 15) ) != 0 ) | ||
1226 | bus_speed = PCIX_100MHZ; | ||
1227 | else if ((slot_num = ((slot_avail1 & SLOT_66MHZ_PCIX) >> 7) ) != 0 ) | ||
1228 | bus_speed = PCIX_66MHZ; | ||
1229 | else if ((slot_num = (slot_avail2 & SLOT_66MHZ)) != 0 ) | ||
1230 | bus_speed = PCI_66MHZ; | ||
1231 | else if ((slot_num = (slot_avail1 & SLOT_33MHZ)) != 0 ) | ||
1232 | bus_speed = PCI_33MHZ; | ||
1233 | else bus_speed = PCI_SPEED_UNKNOWN; | ||
1234 | } else { | ||
1235 | if ((slot_num = ((slot_avail1 & SLOT_133MHZ_PCIX) >> 23) ) != 0 ) | ||
1236 | bus_speed = PCIX_133MHZ; | ||
1237 | else if ((slot_num = ((slot_avail1 & SLOT_100MHZ_PCIX) >> 15) ) != 0 ) | ||
1238 | bus_speed = PCIX_100MHZ; | ||
1239 | else if ((slot_num = ((slot_avail1 & SLOT_66MHZ_PCIX) >> 7) ) != 0 ) | ||
1240 | bus_speed = PCIX_66MHZ; | ||
1241 | else if ((slot_num = (slot_avail2 & SLOT_66MHZ)) != 0 ) | ||
1242 | bus_speed = PCI_66MHZ; | ||
1243 | else if ((slot_num = (slot_avail1 & SLOT_33MHZ)) != 0 ) | ||
1244 | bus_speed = PCI_33MHZ; | ||
1245 | else bus_speed = PCI_SPEED_UNKNOWN; | ||
1246 | } | ||
1247 | |||
1248 | *value = bus_speed; | ||
1249 | dbg("Max bus speed = %d\n", bus_speed); | ||
1250 | DBG_LEAVE_ROUTINE | ||
1251 | return retval; | ||
1252 | } | ||
1253 | |||
1254 | static int hpc_get_cur_bus_speed (struct slot *slot, enum pci_bus_speed *value) | ||
1255 | { | ||
1256 | struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; | ||
1257 | enum pci_bus_speed bus_speed = PCI_SPEED_UNKNOWN; | ||
1258 | u16 sec_bus_status; | ||
1259 | int retval = 0; | ||
1260 | u8 pi; | ||
1261 | |||
1262 | DBG_ENTER_ROUTINE | ||
1263 | |||
1264 | if (!slot->ctrl->hpc_ctlr_handle) { | ||
1265 | err("%s: Invalid HPC controller handle!\n", __FUNCTION__); | ||
1266 | return -1; | ||
1267 | } | ||
1268 | |||
1269 | if (slot->hp_slot >= php_ctlr->num_slots) { | ||
1270 | err("%s: Invalid HPC slot number!\n", __FUNCTION__); | ||
1271 | return -1; | ||
1272 | } | ||
1273 | |||
1274 | pi = readb(php_ctlr->creg + PROG_INTERFACE); | ||
1275 | sec_bus_status = readw(php_ctlr->creg + SEC_BUS_CONFIG); | ||
1276 | |||
1277 | if (pi == 2) { | ||
1278 | switch (sec_bus_status & 0x000f) { | ||
1279 | case 0: | ||
1280 | bus_speed = PCI_SPEED_33MHz; | ||
1281 | break; | ||
1282 | case 1: | ||
1283 | bus_speed = PCI_SPEED_66MHz; | ||
1284 | break; | ||
1285 | case 2: | ||
1286 | bus_speed = PCI_SPEED_66MHz_PCIX; | ||
1287 | break; | ||
1288 | case 3: | ||
1289 | bus_speed = PCI_SPEED_100MHz_PCIX; | ||
1290 | break; | ||
1291 | case 4: | ||
1292 | bus_speed = PCI_SPEED_133MHz_PCIX; | ||
1293 | break; | ||
1294 | case 5: | ||
1295 | bus_speed = PCI_SPEED_66MHz_PCIX_ECC; | ||
1296 | break; | ||
1297 | case 6: | ||
1298 | bus_speed = PCI_SPEED_100MHz_PCIX_ECC; | ||
1299 | break; | ||
1300 | case 7: | ||
1301 | bus_speed = PCI_SPEED_133MHz_PCIX_ECC; | ||
1302 | break; | ||
1303 | case 8: | ||
1304 | bus_speed = PCI_SPEED_66MHz_PCIX_266; | ||
1305 | break; | ||
1306 | case 9: | ||
1307 | bus_speed = PCI_SPEED_100MHz_PCIX_266; | ||
1308 | break; | ||
1309 | case 0xa: | ||
1310 | bus_speed = PCI_SPEED_133MHz_PCIX_266; | ||
1311 | break; | ||
1312 | case 0xb: | ||
1313 | bus_speed = PCI_SPEED_66MHz_PCIX_533; | ||
1314 | break; | ||
1315 | case 0xc: | ||
1316 | bus_speed = PCI_SPEED_100MHz_PCIX_533; | ||
1317 | break; | ||
1318 | case 0xd: | ||
1319 | bus_speed = PCI_SPEED_133MHz_PCIX_533; | ||
1320 | break; | ||
1321 | case 0xe: | ||
1322 | case 0xf: | ||
1323 | default: | ||
1324 | bus_speed = PCI_SPEED_UNKNOWN; | ||
1325 | break; | ||
1326 | } | ||
1327 | } else { | ||
1328 | /* In the case where pi is undefined, default it to 1 */ | ||
1329 | switch (sec_bus_status & 0x0007) { | ||
1330 | case 0: | ||
1331 | bus_speed = PCI_SPEED_33MHz; | ||
1332 | break; | ||
1333 | case 1: | ||
1334 | bus_speed = PCI_SPEED_66MHz; | ||
1335 | break; | ||
1336 | case 2: | ||
1337 | bus_speed = PCI_SPEED_66MHz_PCIX; | ||
1338 | break; | ||
1339 | case 3: | ||
1340 | bus_speed = PCI_SPEED_100MHz_PCIX; | ||
1341 | break; | ||
1342 | case 4: | ||
1343 | bus_speed = PCI_SPEED_133MHz_PCIX; | ||
1344 | break; | ||
1345 | case 5: | ||
1346 | bus_speed = PCI_SPEED_UNKNOWN; /* Reserved */ | ||
1347 | break; | ||
1348 | case 6: | ||
1349 | bus_speed = PCI_SPEED_UNKNOWN; /* Reserved */ | ||
1350 | break; | ||
1351 | case 7: | ||
1352 | bus_speed = PCI_SPEED_UNKNOWN; /* Reserved */ | ||
1353 | break; | ||
1354 | default: | ||
1355 | bus_speed = PCI_SPEED_UNKNOWN; | ||
1356 | break; | ||
1357 | } | ||
1358 | } | ||
1359 | |||
1360 | *value = bus_speed; | ||
1361 | dbg("Current bus speed = %d\n", bus_speed); | ||
1362 | DBG_LEAVE_ROUTINE | ||
1363 | return retval; | ||
1364 | } | ||
1365 | |||
1366 | static struct hpc_ops shpchp_hpc_ops = { | ||
1367 | .power_on_slot = hpc_power_on_slot, | ||
1368 | .slot_enable = hpc_slot_enable, | ||
1369 | .slot_disable = hpc_slot_disable, | ||
1370 | .enable_all_slots = hpc_enable_all_slots, | ||
1371 | .pwr_on_all_slots = hpc_pwr_on_all_slots, | ||
1372 | .set_bus_speed_mode = hpc_set_bus_speed_mode, | ||
1373 | .set_attention_status = hpc_set_attention_status, | ||
1374 | .get_power_status = hpc_get_power_status, | ||
1375 | .get_attention_status = hpc_get_attention_status, | ||
1376 | .get_latch_status = hpc_get_latch_status, | ||
1377 | .get_adapter_status = hpc_get_adapter_status, | ||
1378 | |||
1379 | .get_max_bus_speed = hpc_get_max_bus_speed, | ||
1380 | .get_cur_bus_speed = hpc_get_cur_bus_speed, | ||
1381 | .get_adapter_speed = hpc_get_adapter_speed, | ||
1382 | .get_mode1_ECC_cap = hpc_get_mode1_ECC_cap, | ||
1383 | .get_prog_int = hpc_get_prog_int, | ||
1384 | |||
1385 | .query_power_fault = hpc_query_power_fault, | ||
1386 | .green_led_on = hpc_set_green_led_on, | ||
1387 | .green_led_off = hpc_set_green_led_off, | ||
1388 | .green_led_blink = hpc_set_green_led_blink, | ||
1389 | |||
1390 | .release_ctlr = hpc_release_ctlr, | ||
1391 | .check_cmd_status = hpc_check_cmd_status, | ||
1392 | }; | ||
1393 | |||
1394 | int shpc_init(struct controller * ctrl, | ||
1395 | struct pci_dev * pdev, | ||
1396 | php_intr_callback_t attention_button_callback, | ||
1397 | php_intr_callback_t switch_change_callback, | ||
1398 | php_intr_callback_t presence_change_callback, | ||
1399 | php_intr_callback_t power_fault_callback) | ||
1400 | { | ||
1401 | struct php_ctlr_state_s *php_ctlr, *p; | ||
1402 | void *instance_id = ctrl; | ||
1403 | int rc; | ||
1404 | u8 hp_slot; | ||
1405 | static int first = 1; | ||
1406 | u32 shpc_cap_offset, shpc_base_offset; | ||
1407 | u32 tempdword, slot_reg; | ||
1408 | u16 vendor_id, device_id; | ||
1409 | u8 i; | ||
1410 | |||
1411 | DBG_ENTER_ROUTINE | ||
1412 | |||
1413 | spin_lock_init(&list_lock); | ||
1414 | php_ctlr = (struct php_ctlr_state_s *) kmalloc(sizeof(struct php_ctlr_state_s), GFP_KERNEL); | ||
1415 | |||
1416 | if (!php_ctlr) { /* allocate controller state data */ | ||
1417 | err("%s: HPC controller memory allocation error!\n", __FUNCTION__); | ||
1418 | goto abort; | ||
1419 | } | ||
1420 | |||
1421 | memset(php_ctlr, 0, sizeof(struct php_ctlr_state_s)); | ||
1422 | |||
1423 | php_ctlr->pci_dev = pdev; /* save pci_dev in context */ | ||
1424 | |||
1425 | rc = pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor_id); | ||
1426 | dbg("%s: Vendor ID: %x\n",__FUNCTION__, vendor_id); | ||
1427 | if (rc) { | ||
1428 | err("%s: unable to read PCI configuration data\n", __FUNCTION__); | ||
1429 | goto abort_free_ctlr; | ||
1430 | } | ||
1431 | |||
1432 | rc = pci_read_config_word(pdev, PCI_DEVICE_ID, &device_id); | ||
1433 | dbg("%s: Device ID: %x\n",__FUNCTION__, device_id); | ||
1434 | if (rc) { | ||
1435 | err("%s: unable to read PCI configuration data\n", __FUNCTION__); | ||
1436 | goto abort_free_ctlr; | ||
1437 | } | ||
1438 | |||
1439 | if ((vendor_id == PCI_VENDOR_ID_AMD) || (device_id == PCI_DEVICE_ID_AMD_GOLAM_7450)) { | ||
1440 | shpc_base_offset = 0; /* amd shpc driver doesn't use this; assume 0 */ | ||
1441 | } else { | ||
1442 | if ((shpc_cap_offset = pci_find_capability(pdev, PCI_CAP_ID_SHPC)) == 0) { | ||
1443 | err("%s : shpc_cap_offset == 0\n", __FUNCTION__); | ||
1444 | goto abort_free_ctlr; | ||
1445 | } | ||
1446 | dbg("%s: shpc_cap_offset = %x\n", __FUNCTION__, shpc_cap_offset); | ||
1447 | |||
1448 | rc = pci_write_config_byte(pdev, (u8)shpc_cap_offset + DWORD_SELECT , BASE_OFFSET); | ||
1449 | if (rc) { | ||
1450 | err("%s : pci_word_config_byte failed\n", __FUNCTION__); | ||
1451 | goto abort_free_ctlr; | ||
1452 | } | ||
1453 | |||
1454 | rc = pci_read_config_dword(pdev, (u8)shpc_cap_offset + DWORD_DATA, &shpc_base_offset); | ||
1455 | if (rc) { | ||
1456 | err("%s : pci_read_config_dword failed\n", __FUNCTION__); | ||
1457 | goto abort_free_ctlr; | ||
1458 | } | ||
1459 | |||
1460 | for (i = 0; i <= 14; i++) { | ||
1461 | rc = pci_write_config_byte(pdev, (u8)shpc_cap_offset + DWORD_SELECT , i); | ||
1462 | if (rc) { | ||
1463 | err("%s : pci_word_config_byte failed\n", __FUNCTION__); | ||
1464 | goto abort_free_ctlr; | ||
1465 | } | ||
1466 | |||
1467 | rc = pci_read_config_dword(pdev, (u8)shpc_cap_offset + DWORD_DATA, &tempdword); | ||
1468 | if (rc) { | ||
1469 | err("%s : pci_read_config_dword failed\n", __FUNCTION__); | ||
1470 | goto abort_free_ctlr; | ||
1471 | } | ||
1472 | dbg("%s: offset %d: tempdword %x\n", __FUNCTION__,i, tempdword); | ||
1473 | } | ||
1474 | } | ||
1475 | |||
1476 | if (first) { | ||
1477 | spin_lock_init(&hpc_event_lock); | ||
1478 | first = 0; | ||
1479 | } | ||
1480 | |||
1481 | dbg("pdev = %p: b:d:f:irq=0x%x:%x:%x:%x\n", pdev, pdev->bus->number, PCI_SLOT(pdev->devfn), | ||
1482 | PCI_FUNC(pdev->devfn), pdev->irq); | ||
1483 | for ( rc = 0; rc < DEVICE_COUNT_RESOURCE; rc++) | ||
1484 | if (pci_resource_len(pdev, rc) > 0) | ||
1485 | dbg("pci resource[%d] start=0x%lx(len=0x%lx), shpc_base_offset %x\n", rc, | ||
1486 | pci_resource_start(pdev, rc), pci_resource_len(pdev, rc), shpc_base_offset); | ||
1487 | |||
1488 | info("HPC vendor_id %x device_id %x ss_vid %x ss_did %x\n", pdev->vendor, pdev->device, pdev->subsystem_vendor, | ||
1489 | pdev->subsystem_device); | ||
1490 | |||
1491 | if (pci_enable_device(pdev)) | ||
1492 | goto abort_free_ctlr; | ||
1493 | |||
1494 | if (!request_mem_region(pci_resource_start(pdev, 0) + shpc_base_offset, pci_resource_len(pdev, 0), MY_NAME)) { | ||
1495 | err("%s: cannot reserve MMIO region\n", __FUNCTION__); | ||
1496 | goto abort_free_ctlr; | ||
1497 | } | ||
1498 | |||
1499 | php_ctlr->creg = ioremap(pci_resource_start(pdev, 0) + shpc_base_offset, pci_resource_len(pdev, 0)); | ||
1500 | if (!php_ctlr->creg) { | ||
1501 | err("%s: cannot remap MMIO region %lx @ %lx\n", __FUNCTION__, pci_resource_len(pdev, 0), | ||
1502 | pci_resource_start(pdev, 0) + shpc_base_offset); | ||
1503 | release_mem_region(pci_resource_start(pdev, 0) + shpc_base_offset, pci_resource_len(pdev, 0)); | ||
1504 | goto abort_free_ctlr; | ||
1505 | } | ||
1506 | dbg("%s: php_ctlr->creg %p\n", __FUNCTION__, php_ctlr->creg); | ||
1507 | dbg("%s: physical addr %p\n", __FUNCTION__, (void*)pci_resource_start(pdev, 0)); | ||
1508 | |||
1509 | init_MUTEX(&ctrl->crit_sect); | ||
1510 | /* Setup wait queue */ | ||
1511 | init_waitqueue_head(&ctrl->queue); | ||
1512 | |||
1513 | /* Find the IRQ */ | ||
1514 | php_ctlr->irq = pdev->irq; | ||
1515 | dbg("HPC interrupt = %d\n", php_ctlr->irq); | ||
1516 | |||
1517 | /* Save interrupt callback info */ | ||
1518 | php_ctlr->attention_button_callback = attention_button_callback; | ||
1519 | php_ctlr->switch_change_callback = switch_change_callback; | ||
1520 | php_ctlr->presence_change_callback = presence_change_callback; | ||
1521 | php_ctlr->power_fault_callback = power_fault_callback; | ||
1522 | php_ctlr->callback_instance_id = instance_id; | ||
1523 | |||
1524 | /* Return PCI Controller Info */ | ||
1525 | php_ctlr->slot_device_offset = (readl(php_ctlr->creg + SLOT_CONFIG) & FIRST_DEV_NUM ) >> 8; | ||
1526 | php_ctlr->num_slots = readl(php_ctlr->creg + SLOT_CONFIG) & SLOT_NUM; | ||
1527 | dbg("%s: slot_device_offset %x\n", __FUNCTION__, php_ctlr->slot_device_offset); | ||
1528 | dbg("%s: num_slots %x\n", __FUNCTION__, php_ctlr->num_slots); | ||
1529 | |||
1530 | /* Mask Global Interrupt Mask & Command Complete Interrupt Mask */ | ||
1531 | tempdword = readl(php_ctlr->creg + SERR_INTR_ENABLE); | ||
1532 | dbg("%s: SERR_INTR_ENABLE = %x\n", __FUNCTION__, tempdword); | ||
1533 | tempdword = 0x0003000f; | ||
1534 | writel(tempdword, php_ctlr->creg + SERR_INTR_ENABLE); | ||
1535 | tempdword = readl(php_ctlr->creg + SERR_INTR_ENABLE); | ||
1536 | dbg("%s: SERR_INTR_ENABLE = %x\n", __FUNCTION__, tempdword); | ||
1537 | |||
1538 | /* Mask the MRL sensor SERR Mask of individual slot in | ||
1539 | * Slot SERR-INT Mask & clear all the existing event if any | ||
1540 | */ | ||
1541 | for (hp_slot = 0; hp_slot < php_ctlr->num_slots; hp_slot++) { | ||
1542 | slot_reg = readl(php_ctlr->creg + SLOT1 + 4*hp_slot ); | ||
1543 | dbg("%s: Default Logical Slot Register %d value %x\n", __FUNCTION__, | ||
1544 | hp_slot, slot_reg); | ||
1545 | tempdword = 0xffff3fff; | ||
1546 | writel(tempdword, php_ctlr->creg + SLOT1 + (4*hp_slot)); | ||
1547 | } | ||
1548 | |||
1549 | if (shpchp_poll_mode) {/* Install interrupt polling code */ | ||
1550 | /* Install and start the interrupt polling timer */ | ||
1551 | init_timer(&php_ctlr->int_poll_timer); | ||
1552 | start_int_poll_timer( php_ctlr, 10 ); /* start with 10 second delay */ | ||
1553 | } else { | ||
1554 | /* Installs the interrupt handler */ | ||
1555 | rc = pci_enable_msi(pdev); | ||
1556 | if (rc) { | ||
1557 | info("Can't get msi for the hotplug controller\n"); | ||
1558 | info("Use INTx for the hotplug controller\n"); | ||
1559 | dbg("%s: rc = %x\n", __FUNCTION__, rc); | ||
1560 | } else | ||
1561 | php_ctlr->irq = pdev->irq; | ||
1562 | |||
1563 | rc = request_irq(php_ctlr->irq, shpc_isr, SA_SHIRQ, MY_NAME, (void *) ctrl); | ||
1564 | dbg("%s: request_irq %d for hpc%d (returns %d)\n", __FUNCTION__, php_ctlr->irq, ctlr_seq_num, rc); | ||
1565 | if (rc) { | ||
1566 | err("Can't get irq %d for the hotplug controller\n", php_ctlr->irq); | ||
1567 | goto abort_free_ctlr; | ||
1568 | } | ||
1569 | /* Execute OSHP method here */ | ||
1570 | } | ||
1571 | dbg("%s: Before adding HPC to HPC list\n", __FUNCTION__); | ||
1572 | |||
1573 | /* Add this HPC instance into the HPC list */ | ||
1574 | spin_lock(&list_lock); | ||
1575 | if (php_ctlr_list_head == 0) { | ||
1576 | php_ctlr_list_head = php_ctlr; | ||
1577 | p = php_ctlr_list_head; | ||
1578 | p->pnext = NULL; | ||
1579 | } else { | ||
1580 | p = php_ctlr_list_head; | ||
1581 | |||
1582 | while (p->pnext) | ||
1583 | p = p->pnext; | ||
1584 | |||
1585 | p->pnext = php_ctlr; | ||
1586 | } | ||
1587 | spin_unlock(&list_lock); | ||
1588 | |||
1589 | |||
1590 | ctlr_seq_num++; | ||
1591 | ctrl->hpc_ctlr_handle = php_ctlr; | ||
1592 | ctrl->hpc_ops = &shpchp_hpc_ops; | ||
1593 | |||
1594 | for (hp_slot = 0; hp_slot < php_ctlr->num_slots; hp_slot++) { | ||
1595 | slot_reg = readl(php_ctlr->creg + SLOT1 + 4*hp_slot ); | ||
1596 | dbg("%s: Default Logical Slot Register %d value %x\n", __FUNCTION__, | ||
1597 | hp_slot, slot_reg); | ||
1598 | tempdword = 0xe01f3fff; | ||
1599 | writel(tempdword, php_ctlr->creg + SLOT1 + (4*hp_slot)); | ||
1600 | } | ||
1601 | if (!shpchp_poll_mode) { | ||
1602 | /* Unmask all general input interrupts and SERR */ | ||
1603 | tempdword = readl(php_ctlr->creg + SERR_INTR_ENABLE); | ||
1604 | tempdword = 0x0000000a; | ||
1605 | writel(tempdword, php_ctlr->creg + SERR_INTR_ENABLE); | ||
1606 | tempdword = readl(php_ctlr->creg + SERR_INTR_ENABLE); | ||
1607 | dbg("%s: SERR_INTR_ENABLE = %x\n", __FUNCTION__, tempdword); | ||
1608 | } | ||
1609 | |||
1610 | dbg("%s: Leaving shpc_init\n", __FUNCTION__); | ||
1611 | DBG_LEAVE_ROUTINE | ||
1612 | return 0; | ||
1613 | |||
1614 | /* We end up here for the many possible ways to fail this API. */ | ||
1615 | abort_free_ctlr: | ||
1616 | kfree(php_ctlr); | ||
1617 | abort: | ||
1618 | DBG_LEAVE_ROUTINE | ||
1619 | return -1; | ||
1620 | } | ||
diff --git a/drivers/pci/hotplug/shpchp_pci.c b/drivers/pci/hotplug/shpchp_pci.c new file mode 100644 index 000000000000..90113e9cd69b --- /dev/null +++ b/drivers/pci/hotplug/shpchp_pci.c | |||
@@ -0,0 +1,810 @@ | |||
1 | /* | ||
2 | * Standard Hot Plug Controller Driver | ||
3 | * | ||
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | ||
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * Copyright (C) 2001 IBM Corp. | ||
7 | * Copyright (C) 2003-2004 Intel Corporation | ||
8 | * | ||
9 | * All rights reserved. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or (at | ||
14 | * your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
19 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
20 | * details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
25 | * | ||
26 | * Send feedback to <greg@kroah.com>, <dely.l.sy@intel.com> | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | #include <linux/config.h> | ||
31 | #include <linux/module.h> | ||
32 | #include <linux/kernel.h> | ||
33 | #include <linux/types.h> | ||
34 | #include <linux/slab.h> | ||
35 | #include <linux/workqueue.h> | ||
36 | #include <linux/proc_fs.h> | ||
37 | #include <linux/pci.h> | ||
38 | #include "../pci.h" | ||
39 | #include "shpchp.h" | ||
40 | #ifndef CONFIG_IA64 | ||
41 | #include "../../../arch/i386/pci/pci.h" /* horrible hack showing how processor dependant we are... */ | ||
42 | #endif | ||
43 | |||
44 | int shpchp_configure_device (struct controller* ctrl, struct pci_func* func) | ||
45 | { | ||
46 | unsigned char bus; | ||
47 | struct pci_bus *child; | ||
48 | int num; | ||
49 | |||
50 | if (func->pci_dev == NULL) | ||
51 | func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function)); | ||
52 | |||
53 | /* Still NULL ? Well then scan for it ! */ | ||
54 | if (func->pci_dev == NULL) { | ||
55 | num = pci_scan_slot(ctrl->pci_dev->subordinate, PCI_DEVFN(func->device, func->function)); | ||
56 | if (num) { | ||
57 | dbg("%s: subordiante %p number %x\n", __FUNCTION__, ctrl->pci_dev->subordinate, | ||
58 | ctrl->pci_dev->subordinate->number); | ||
59 | pci_bus_add_devices(ctrl->pci_dev->subordinate); | ||
60 | } | ||
61 | |||
62 | func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function)); | ||
63 | if (func->pci_dev == NULL) { | ||
64 | dbg("ERROR: pci_dev still null\n"); | ||
65 | return 0; | ||
66 | } | ||
67 | } | ||
68 | |||
69 | if (func->pci_dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { | ||
70 | pci_read_config_byte(func->pci_dev, PCI_SECONDARY_BUS, &bus); | ||
71 | child = pci_add_new_bus(func->pci_dev->bus, (func->pci_dev), bus); | ||
72 | pci_do_scan_bus(child); | ||
73 | |||
74 | } | ||
75 | |||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | |||
80 | int shpchp_unconfigure_device(struct pci_func* func) | ||
81 | { | ||
82 | int rc = 0; | ||
83 | int j; | ||
84 | |||
85 | dbg("%s: bus/dev/func = %x/%x/%x\n", __FUNCTION__, func->bus, | ||
86 | func->device, func->function); | ||
87 | |||
88 | for (j=0; j<8 ; j++) { | ||
89 | struct pci_dev* temp = pci_find_slot(func->bus, | ||
90 | (func->device << 3) | j); | ||
91 | if (temp) { | ||
92 | pci_remove_bus_device(temp); | ||
93 | } | ||
94 | } | ||
95 | return rc; | ||
96 | } | ||
97 | |||
98 | /* | ||
99 | * shpchp_set_irq | ||
100 | * | ||
101 | * @bus_num: bus number of PCI device | ||
102 | * @dev_num: device number of PCI device | ||
103 | * @slot: pointer to u8 where slot number will be returned | ||
104 | */ | ||
105 | int shpchp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num) | ||
106 | { | ||
107 | #if defined(CONFIG_X86) && !defined(CONFIG_X86_IO_APIC) && !defined(CONFIG_X86_64) | ||
108 | int rc; | ||
109 | u16 temp_word; | ||
110 | struct pci_dev fakedev; | ||
111 | struct pci_bus fakebus; | ||
112 | |||
113 | fakedev.devfn = dev_num << 3; | ||
114 | fakedev.bus = &fakebus; | ||
115 | fakebus.number = bus_num; | ||
116 | dbg("%s: dev %d, bus %d, pin %d, num %d\n", | ||
117 | __FUNCTION__, dev_num, bus_num, int_pin, irq_num); | ||
118 | rc = pcibios_set_irq_routing(&fakedev, int_pin - 0x0a, irq_num); | ||
119 | dbg("%s: rc %d\n", __FUNCTION__, rc); | ||
120 | if (!rc) | ||
121 | return !rc; | ||
122 | |||
123 | /* set the Edge Level Control Register (ELCR) */ | ||
124 | temp_word = inb(0x4d0); | ||
125 | temp_word |= inb(0x4d1) << 8; | ||
126 | |||
127 | temp_word |= 0x01 << irq_num; | ||
128 | |||
129 | /* This should only be for x86 as it sets the Edge Level Control Register */ | ||
130 | outb((u8) (temp_word & 0xFF), 0x4d0); | ||
131 | outb((u8) ((temp_word & 0xFF00) >> 8), 0x4d1); | ||
132 | #endif | ||
133 | return 0; | ||
134 | } | ||
135 | |||
136 | /* More PCI configuration routines; this time centered around hotplug controller */ | ||
137 | |||
138 | |||
139 | /* | ||
140 | * shpchp_save_config | ||
141 | * | ||
142 | * Reads configuration for all slots in a PCI bus and saves info. | ||
143 | * | ||
144 | * Note: For non-hot plug busses, the slot # saved is the device # | ||
145 | * | ||
146 | * returns 0 if success | ||
147 | */ | ||
148 | int shpchp_save_config(struct controller *ctrl, int busnumber, int num_ctlr_slots, int first_device_num) | ||
149 | { | ||
150 | int rc; | ||
151 | u8 class_code; | ||
152 | u8 header_type; | ||
153 | u32 ID; | ||
154 | u8 secondary_bus; | ||
155 | struct pci_func *new_slot; | ||
156 | int sub_bus; | ||
157 | int FirstSupported; | ||
158 | int LastSupported; | ||
159 | int max_functions; | ||
160 | int function; | ||
161 | u8 DevError; | ||
162 | int device = 0; | ||
163 | int cloop = 0; | ||
164 | int stop_it; | ||
165 | int index; | ||
166 | int is_hot_plug = num_ctlr_slots || first_device_num; | ||
167 | struct pci_bus lpci_bus, *pci_bus; | ||
168 | |||
169 | dbg("%s: num_ctlr_slots = %d, first_device_num = %d\n", __FUNCTION__, | ||
170 | num_ctlr_slots, first_device_num); | ||
171 | |||
172 | memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus)); | ||
173 | pci_bus = &lpci_bus; | ||
174 | |||
175 | dbg("%s: num_ctlr_slots = %d, first_device_num = %d\n", __FUNCTION__, | ||
176 | num_ctlr_slots, first_device_num); | ||
177 | |||
178 | /* Decide which slots are supported */ | ||
179 | if (is_hot_plug) { | ||
180 | /********************************* | ||
181 | * is_hot_plug is the slot mask | ||
182 | *********************************/ | ||
183 | FirstSupported = first_device_num; | ||
184 | LastSupported = FirstSupported + num_ctlr_slots - 1; | ||
185 | } else { | ||
186 | FirstSupported = 0; | ||
187 | LastSupported = 0x1F; | ||
188 | } | ||
189 | |||
190 | dbg("FirstSupported = %d, LastSupported = %d\n", FirstSupported, | ||
191 | LastSupported); | ||
192 | |||
193 | /* Save PCI configuration space for all devices in supported slots */ | ||
194 | pci_bus->number = busnumber; | ||
195 | for (device = FirstSupported; device <= LastSupported; device++) { | ||
196 | ID = 0xFFFFFFFF; | ||
197 | rc = pci_bus_read_config_dword(pci_bus, PCI_DEVFN(device, 0), | ||
198 | PCI_VENDOR_ID, &ID); | ||
199 | |||
200 | if (ID != 0xFFFFFFFF) { /* device in slot */ | ||
201 | rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, 0), | ||
202 | 0x0B, &class_code); | ||
203 | if (rc) | ||
204 | return rc; | ||
205 | |||
206 | rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, 0), | ||
207 | PCI_HEADER_TYPE, &header_type); | ||
208 | if (rc) | ||
209 | return rc; | ||
210 | |||
211 | dbg("class_code = %x, header_type = %x\n", class_code, header_type); | ||
212 | |||
213 | /* If multi-function device, set max_functions to 8 */ | ||
214 | if (header_type & 0x80) | ||
215 | max_functions = 8; | ||
216 | else | ||
217 | max_functions = 1; | ||
218 | |||
219 | function = 0; | ||
220 | |||
221 | do { | ||
222 | DevError = 0; | ||
223 | |||
224 | if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* P-P Bridge */ | ||
225 | /* Recurse the subordinate bus | ||
226 | * get the subordinate bus number | ||
227 | */ | ||
228 | rc = pci_bus_read_config_byte(pci_bus, | ||
229 | PCI_DEVFN(device, function), | ||
230 | PCI_SECONDARY_BUS, &secondary_bus); | ||
231 | if (rc) { | ||
232 | return rc; | ||
233 | } else { | ||
234 | sub_bus = (int) secondary_bus; | ||
235 | |||
236 | /* Save secondary bus cfg spc with this recursive call. */ | ||
237 | rc = shpchp_save_config(ctrl, sub_bus, 0, 0); | ||
238 | if (rc) | ||
239 | return rc; | ||
240 | } | ||
241 | } | ||
242 | |||
243 | index = 0; | ||
244 | new_slot = shpchp_slot_find(busnumber, device, index++); | ||
245 | |||
246 | dbg("new_slot = %p\n", new_slot); | ||
247 | |||
248 | while (new_slot && (new_slot->function != (u8) function)) { | ||
249 | new_slot = shpchp_slot_find(busnumber, device, index++); | ||
250 | dbg("new_slot = %p\n", new_slot); | ||
251 | } | ||
252 | if (!new_slot) { | ||
253 | /* Setup slot structure. */ | ||
254 | new_slot = shpchp_slot_create(busnumber); | ||
255 | dbg("new_slot = %p\n", new_slot); | ||
256 | |||
257 | if (new_slot == NULL) | ||
258 | return(1); | ||
259 | } | ||
260 | |||
261 | new_slot->bus = (u8) busnumber; | ||
262 | new_slot->device = (u8) device; | ||
263 | new_slot->function = (u8) function; | ||
264 | new_slot->is_a_board = 1; | ||
265 | new_slot->switch_save = 0x10; | ||
266 | new_slot->pwr_save = 1; | ||
267 | /* In case of unsupported board */ | ||
268 | new_slot->status = DevError; | ||
269 | new_slot->pci_dev = pci_find_slot(new_slot->bus, | ||
270 | (new_slot->device << 3) | new_slot->function); | ||
271 | dbg("new_slot->pci_dev = %p\n", new_slot->pci_dev); | ||
272 | |||
273 | for (cloop = 0; cloop < 0x20; cloop++) { | ||
274 | rc = pci_bus_read_config_dword(pci_bus, | ||
275 | PCI_DEVFN(device, function), | ||
276 | cloop << 2, | ||
277 | (u32 *) &(new_slot->config_space [cloop])); | ||
278 | /* dbg("new_slot->config_space[%x] = %x\n", | ||
279 | cloop, new_slot->config_space[cloop]); */ | ||
280 | if (rc) | ||
281 | return rc; | ||
282 | } | ||
283 | |||
284 | function++; | ||
285 | |||
286 | stop_it = 0; | ||
287 | |||
288 | /* this loop skips to the next present function | ||
289 | * reading in Class Code and Header type. | ||
290 | */ | ||
291 | |||
292 | while ((function < max_functions)&&(!stop_it)) { | ||
293 | rc = pci_bus_read_config_dword(pci_bus, | ||
294 | PCI_DEVFN(device, function), | ||
295 | PCI_VENDOR_ID, &ID); | ||
296 | |||
297 | if (ID == 0xFFFFFFFF) { /* nothing there. */ | ||
298 | function++; | ||
299 | dbg("Nothing there\n"); | ||
300 | } else { /* Something there */ | ||
301 | rc = pci_bus_read_config_byte(pci_bus, | ||
302 | PCI_DEVFN(device, function), | ||
303 | 0x0B, &class_code); | ||
304 | if (rc) | ||
305 | return rc; | ||
306 | |||
307 | rc = pci_bus_read_config_byte(pci_bus, | ||
308 | PCI_DEVFN(device, function), | ||
309 | PCI_HEADER_TYPE, &header_type); | ||
310 | if (rc) | ||
311 | return rc; | ||
312 | |||
313 | dbg("class_code = %x, header_type = %x\n", | ||
314 | class_code, header_type); | ||
315 | stop_it++; | ||
316 | } | ||
317 | } | ||
318 | |||
319 | } while (function < max_functions); | ||
320 | /* End of IF (device in slot?) */ | ||
321 | } else if (is_hot_plug) { | ||
322 | /* Setup slot structure with entry for empty slot */ | ||
323 | new_slot = shpchp_slot_create(busnumber); | ||
324 | |||
325 | if (new_slot == NULL) { | ||
326 | return(1); | ||
327 | } | ||
328 | dbg("new_slot = %p\n", new_slot); | ||
329 | |||
330 | new_slot->bus = (u8) busnumber; | ||
331 | new_slot->device = (u8) device; | ||
332 | new_slot->function = 0; | ||
333 | new_slot->is_a_board = 0; | ||
334 | new_slot->presence_save = 0; | ||
335 | new_slot->switch_save = 0; | ||
336 | } | ||
337 | } /* End of FOR loop */ | ||
338 | |||
339 | return(0); | ||
340 | } | ||
341 | |||
342 | |||
343 | /* | ||
344 | * shpchp_save_slot_config | ||
345 | * | ||
346 | * Saves configuration info for all PCI devices in a given slot | ||
347 | * including subordinate busses. | ||
348 | * | ||
349 | * returns 0 if success | ||
350 | */ | ||
351 | int shpchp_save_slot_config(struct controller *ctrl, struct pci_func * new_slot) | ||
352 | { | ||
353 | int rc; | ||
354 | u8 class_code; | ||
355 | u8 header_type; | ||
356 | u32 ID; | ||
357 | u8 secondary_bus; | ||
358 | int sub_bus; | ||
359 | int max_functions; | ||
360 | int function; | ||
361 | int cloop = 0; | ||
362 | int stop_it; | ||
363 | struct pci_bus lpci_bus, *pci_bus; | ||
364 | memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus)); | ||
365 | pci_bus = &lpci_bus; | ||
366 | pci_bus->number = new_slot->bus; | ||
367 | |||
368 | ID = 0xFFFFFFFF; | ||
369 | |||
370 | pci_bus_read_config_dword(pci_bus, PCI_DEVFN(new_slot->device, 0), | ||
371 | PCI_VENDOR_ID, &ID); | ||
372 | |||
373 | if (ID != 0xFFFFFFFF) { /* device in slot */ | ||
374 | pci_bus_read_config_byte(pci_bus, PCI_DEVFN(new_slot->device, 0), | ||
375 | 0x0B, &class_code); | ||
376 | |||
377 | pci_bus_read_config_byte(pci_bus, PCI_DEVFN(new_slot->device, 0), | ||
378 | PCI_HEADER_TYPE, &header_type); | ||
379 | |||
380 | if (header_type & 0x80) /* Multi-function device */ | ||
381 | max_functions = 8; | ||
382 | else | ||
383 | max_functions = 1; | ||
384 | |||
385 | function = 0; | ||
386 | |||
387 | do { | ||
388 | if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* PCI-PCI Bridge */ | ||
389 | /* Recurse the subordinate bus */ | ||
390 | pci_bus_read_config_byte(pci_bus, | ||
391 | PCI_DEVFN(new_slot->device, function), | ||
392 | PCI_SECONDARY_BUS, &secondary_bus); | ||
393 | |||
394 | sub_bus = (int) secondary_bus; | ||
395 | |||
396 | /* Save the config headers for the secondary bus. */ | ||
397 | rc = shpchp_save_config(ctrl, sub_bus, 0, 0); | ||
398 | |||
399 | if (rc) | ||
400 | return rc; | ||
401 | |||
402 | } /* End of IF */ | ||
403 | |||
404 | new_slot->status = 0; | ||
405 | |||
406 | for (cloop = 0; cloop < 0x20; cloop++) { | ||
407 | pci_bus_read_config_dword(pci_bus, | ||
408 | PCI_DEVFN(new_slot->device, function), | ||
409 | cloop << 2, | ||
410 | (u32 *) &(new_slot->config_space [cloop])); | ||
411 | } | ||
412 | |||
413 | function++; | ||
414 | |||
415 | stop_it = 0; | ||
416 | |||
417 | /* this loop skips to the next present function | ||
418 | * reading in the Class Code and the Header type. | ||
419 | */ | ||
420 | |||
421 | while ((function < max_functions) && (!stop_it)) { | ||
422 | pci_bus_read_config_dword(pci_bus, | ||
423 | PCI_DEVFN(new_slot->device, function), | ||
424 | PCI_VENDOR_ID, &ID); | ||
425 | |||
426 | if (ID == 0xFFFFFFFF) { /* nothing there. */ | ||
427 | function++; | ||
428 | } else { /* Something there */ | ||
429 | pci_bus_read_config_byte(pci_bus, | ||
430 | PCI_DEVFN(new_slot->device, function), | ||
431 | 0x0B, &class_code); | ||
432 | |||
433 | pci_bus_read_config_byte(pci_bus, | ||
434 | PCI_DEVFN(new_slot->device, function), | ||
435 | PCI_HEADER_TYPE, &header_type); | ||
436 | |||
437 | stop_it++; | ||
438 | } | ||
439 | } | ||
440 | |||
441 | } while (function < max_functions); | ||
442 | } /* End of IF (device in slot?) */ | ||
443 | else { | ||
444 | return 2; | ||
445 | } | ||
446 | |||
447 | return 0; | ||
448 | } | ||
449 | |||
450 | |||
451 | /* | ||
452 | * shpchp_save_used_resources | ||
453 | * | ||
454 | * Stores used resource information for existing boards. this is | ||
455 | * for boards that were in the system when this driver was loaded. | ||
456 | * this function is for hot plug ADD | ||
457 | * | ||
458 | * returns 0 if success | ||
459 | * if disable == 1(DISABLE_CARD), | ||
460 | * it loops for all functions of the slot and disables them. | ||
461 | * else, it just get resources of the function and return. | ||
462 | */ | ||
463 | int shpchp_save_used_resources(struct controller *ctrl, struct pci_func *func, int disable) | ||
464 | { | ||
465 | u8 cloop; | ||
466 | u8 header_type; | ||
467 | u8 secondary_bus; | ||
468 | u8 temp_byte; | ||
469 | u16 command; | ||
470 | u16 save_command; | ||
471 | u16 w_base, w_length; | ||
472 | u32 temp_register; | ||
473 | u32 save_base; | ||
474 | u32 base, length; | ||
475 | u64 base64 = 0; | ||
476 | int index = 0; | ||
477 | unsigned int devfn; | ||
478 | struct pci_resource *mem_node = NULL; | ||
479 | struct pci_resource *p_mem_node = NULL; | ||
480 | struct pci_resource *t_mem_node; | ||
481 | struct pci_resource *io_node; | ||
482 | struct pci_resource *bus_node; | ||
483 | struct pci_bus lpci_bus, *pci_bus; | ||
484 | memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus)); | ||
485 | pci_bus = &lpci_bus; | ||
486 | |||
487 | if (disable) | ||
488 | func = shpchp_slot_find(func->bus, func->device, index++); | ||
489 | |||
490 | while ((func != NULL) && func->is_a_board) { | ||
491 | pci_bus->number = func->bus; | ||
492 | devfn = PCI_DEVFN(func->device, func->function); | ||
493 | |||
494 | /* Save the command register */ | ||
495 | pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &save_command); | ||
496 | |||
497 | if (disable) { | ||
498 | /* disable card */ | ||
499 | command = 0x00; | ||
500 | pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command); | ||
501 | } | ||
502 | |||
503 | /* Check for Bridge */ | ||
504 | pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &header_type); | ||
505 | |||
506 | if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* PCI-PCI Bridge */ | ||
507 | dbg("Save_used_res of PCI bridge b:d=0x%x:%x, sc=0x%x\n", | ||
508 | func->bus, func->device, save_command); | ||
509 | if (disable) { | ||
510 | /* Clear Bridge Control Register */ | ||
511 | command = 0x00; | ||
512 | pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, command); | ||
513 | } | ||
514 | |||
515 | pci_bus_read_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus); | ||
516 | pci_bus_read_config_byte(pci_bus, devfn, PCI_SUBORDINATE_BUS, &temp_byte); | ||
517 | |||
518 | bus_node = kmalloc(sizeof(struct pci_resource), | ||
519 | GFP_KERNEL); | ||
520 | if (!bus_node) | ||
521 | return -ENOMEM; | ||
522 | |||
523 | bus_node->base = (ulong)secondary_bus; | ||
524 | bus_node->length = (ulong)(temp_byte - secondary_bus + 1); | ||
525 | |||
526 | bus_node->next = func->bus_head; | ||
527 | func->bus_head = bus_node; | ||
528 | |||
529 | /* Save IO base and Limit registers */ | ||
530 | pci_bus_read_config_byte(pci_bus, devfn, PCI_IO_BASE, &temp_byte); | ||
531 | base = temp_byte; | ||
532 | pci_bus_read_config_byte(pci_bus, devfn, PCI_IO_LIMIT, &temp_byte); | ||
533 | length = temp_byte; | ||
534 | |||
535 | if ((base <= length) && (!disable || (save_command & PCI_COMMAND_IO))) { | ||
536 | io_node = kmalloc(sizeof(struct pci_resource), | ||
537 | GFP_KERNEL); | ||
538 | if (!io_node) | ||
539 | return -ENOMEM; | ||
540 | |||
541 | io_node->base = (ulong)(base & PCI_IO_RANGE_MASK) << 8; | ||
542 | io_node->length = (ulong)(length - base + 0x10) << 8; | ||
543 | |||
544 | io_node->next = func->io_head; | ||
545 | func->io_head = io_node; | ||
546 | } | ||
547 | |||
548 | /* Save memory base and Limit registers */ | ||
549 | pci_bus_read_config_word(pci_bus, devfn, PCI_MEMORY_BASE, &w_base); | ||
550 | pci_bus_read_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, &w_length); | ||
551 | |||
552 | if ((w_base <= w_length) && (!disable || (save_command & PCI_COMMAND_MEMORY))) { | ||
553 | mem_node = kmalloc(sizeof(struct pci_resource), | ||
554 | GFP_KERNEL); | ||
555 | if (!mem_node) | ||
556 | return -ENOMEM; | ||
557 | |||
558 | mem_node->base = (ulong)w_base << 16; | ||
559 | mem_node->length = (ulong)(w_length - w_base + 0x10) << 16; | ||
560 | |||
561 | mem_node->next = func->mem_head; | ||
562 | func->mem_head = mem_node; | ||
563 | } | ||
564 | /* Save prefetchable memory base and Limit registers */ | ||
565 | pci_bus_read_config_word(pci_bus, devfn, PCI_PREF_MEMORY_BASE, &w_base); | ||
566 | pci_bus_read_config_word(pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &w_length); | ||
567 | |||
568 | if ((w_base <= w_length) && (!disable || (save_command & PCI_COMMAND_MEMORY))) { | ||
569 | p_mem_node = kmalloc(sizeof(struct pci_resource), | ||
570 | GFP_KERNEL); | ||
571 | if (!p_mem_node) | ||
572 | return -ENOMEM; | ||
573 | |||
574 | p_mem_node->base = (ulong)w_base << 16; | ||
575 | p_mem_node->length = (ulong)(w_length - w_base + 0x10) << 16; | ||
576 | |||
577 | p_mem_node->next = func->p_mem_head; | ||
578 | func->p_mem_head = p_mem_node; | ||
579 | } | ||
580 | } else if ((header_type & 0x7F) == PCI_HEADER_TYPE_NORMAL) { | ||
581 | dbg("Save_used_res of PCI adapter b:d=0x%x:%x, sc=0x%x\n", | ||
582 | func->bus, func->device, save_command); | ||
583 | |||
584 | /* Figure out IO and memory base lengths */ | ||
585 | for (cloop = PCI_BASE_ADDRESS_0; cloop <= PCI_BASE_ADDRESS_5; cloop += 4) { | ||
586 | pci_bus_read_config_dword(pci_bus, devfn, cloop, &save_base); | ||
587 | |||
588 | temp_register = 0xFFFFFFFF; | ||
589 | pci_bus_write_config_dword(pci_bus, devfn, cloop, temp_register); | ||
590 | pci_bus_read_config_dword(pci_bus, devfn, cloop, &temp_register); | ||
591 | |||
592 | if (!disable) | ||
593 | pci_bus_write_config_dword(pci_bus, devfn, cloop, save_base); | ||
594 | |||
595 | if (!temp_register) | ||
596 | continue; | ||
597 | |||
598 | base = temp_register; | ||
599 | |||
600 | if ((base & PCI_BASE_ADDRESS_SPACE_IO) && | ||
601 | (!disable || (save_command & PCI_COMMAND_IO))) { | ||
602 | /* IO base */ | ||
603 | /* set temp_register = amount of IO space requested */ | ||
604 | base = base & 0xFFFFFFFCL; | ||
605 | base = (~base) + 1; | ||
606 | |||
607 | io_node = kmalloc(sizeof (struct pci_resource), | ||
608 | GFP_KERNEL); | ||
609 | if (!io_node) | ||
610 | return -ENOMEM; | ||
611 | |||
612 | io_node->base = (ulong)save_base & PCI_BASE_ADDRESS_IO_MASK; | ||
613 | io_node->length = (ulong)base; | ||
614 | dbg("sur adapter: IO bar=0x%x(length=0x%x)\n", | ||
615 | io_node->base, io_node->length); | ||
616 | |||
617 | io_node->next = func->io_head; | ||
618 | func->io_head = io_node; | ||
619 | } else { /* map Memory */ | ||
620 | int prefetchable = 1; | ||
621 | /* struct pci_resources **res_node; */ | ||
622 | char *res_type_str = "PMEM"; | ||
623 | u32 temp_register2; | ||
624 | |||
625 | t_mem_node = kmalloc(sizeof (struct pci_resource), | ||
626 | GFP_KERNEL); | ||
627 | if (!t_mem_node) | ||
628 | return -ENOMEM; | ||
629 | |||
630 | if (!(base & PCI_BASE_ADDRESS_MEM_PREFETCH) && | ||
631 | (!disable || (save_command & PCI_COMMAND_MEMORY))) { | ||
632 | prefetchable = 0; | ||
633 | mem_node = t_mem_node; | ||
634 | res_type_str++; | ||
635 | } else | ||
636 | p_mem_node = t_mem_node; | ||
637 | |||
638 | base = base & 0xFFFFFFF0L; | ||
639 | base = (~base) + 1; | ||
640 | |||
641 | switch (temp_register & PCI_BASE_ADDRESS_MEM_TYPE_MASK) { | ||
642 | case PCI_BASE_ADDRESS_MEM_TYPE_32: | ||
643 | if (prefetchable) { | ||
644 | p_mem_node->base = (ulong)save_base & PCI_BASE_ADDRESS_MEM_MASK; | ||
645 | p_mem_node->length = (ulong)base; | ||
646 | dbg("sur adapter: 32 %s bar=0x%x(length=0x%x)\n", | ||
647 | res_type_str, | ||
648 | p_mem_node->base, | ||
649 | p_mem_node->length); | ||
650 | |||
651 | p_mem_node->next = func->p_mem_head; | ||
652 | func->p_mem_head = p_mem_node; | ||
653 | } else { | ||
654 | mem_node->base = (ulong)save_base & PCI_BASE_ADDRESS_MEM_MASK; | ||
655 | mem_node->length = (ulong)base; | ||
656 | dbg("sur adapter: 32 %s bar=0x%x(length=0x%x)\n", | ||
657 | res_type_str, | ||
658 | mem_node->base, | ||
659 | mem_node->length); | ||
660 | |||
661 | mem_node->next = func->mem_head; | ||
662 | func->mem_head = mem_node; | ||
663 | } | ||
664 | break; | ||
665 | case PCI_BASE_ADDRESS_MEM_TYPE_64: | ||
666 | pci_bus_read_config_dword(pci_bus, devfn, cloop+4, &temp_register2); | ||
667 | base64 = temp_register2; | ||
668 | base64 = (base64 << 32) | save_base; | ||
669 | |||
670 | if (temp_register2) { | ||
671 | dbg("sur adapter: 64 %s high dword of base64(0x%x:%x) masked to 0\n", | ||
672 | res_type_str, temp_register2, (u32)base64); | ||
673 | base64 &= 0x00000000FFFFFFFFL; | ||
674 | } | ||
675 | |||
676 | if (prefetchable) { | ||
677 | p_mem_node->base = base64 & PCI_BASE_ADDRESS_MEM_MASK; | ||
678 | p_mem_node->length = base; | ||
679 | dbg("sur adapter: 64 %s base=0x%x(len=0x%x)\n", | ||
680 | res_type_str, | ||
681 | p_mem_node->base, | ||
682 | p_mem_node->length); | ||
683 | |||
684 | p_mem_node->next = func->p_mem_head; | ||
685 | func->p_mem_head = p_mem_node; | ||
686 | } else { | ||
687 | mem_node->base = base64 & PCI_BASE_ADDRESS_MEM_MASK; | ||
688 | mem_node->length = base; | ||
689 | dbg("sur adapter: 64 %s base=0x%x(len=0x%x)\n", | ||
690 | res_type_str, | ||
691 | mem_node->base, | ||
692 | mem_node->length); | ||
693 | |||
694 | mem_node->next = func->mem_head; | ||
695 | func->mem_head = mem_node; | ||
696 | } | ||
697 | cloop += 4; | ||
698 | break; | ||
699 | default: | ||
700 | dbg("asur: reserved BAR type=0x%x\n", | ||
701 | temp_register); | ||
702 | break; | ||
703 | } | ||
704 | } | ||
705 | } /* End of base register loop */ | ||
706 | } else { /* Some other unknown header type */ | ||
707 | dbg("Save_used_res of PCI unknown type b:d=0x%x:%x. skip.\n", | ||
708 | func->bus, func->device); | ||
709 | } | ||
710 | |||
711 | /* find the next device in this slot */ | ||
712 | if (!disable) | ||
713 | break; | ||
714 | func = shpchp_slot_find(func->bus, func->device, index++); | ||
715 | } | ||
716 | |||
717 | return 0; | ||
718 | } | ||
719 | |||
720 | /** | ||
721 | * kfree_resource_list: release memory of all list members | ||
722 | * @res: resource list to free | ||
723 | */ | ||
724 | static inline void | ||
725 | return_resource_list(struct pci_resource **func, struct pci_resource **res) | ||
726 | { | ||
727 | struct pci_resource *node; | ||
728 | struct pci_resource *t_node; | ||
729 | |||
730 | node = *func; | ||
731 | *func = NULL; | ||
732 | while (node) { | ||
733 | t_node = node->next; | ||
734 | return_resource(res, node); | ||
735 | node = t_node; | ||
736 | } | ||
737 | } | ||
738 | |||
739 | /* | ||
740 | * shpchp_return_board_resources | ||
741 | * | ||
742 | * this routine returns all resources allocated to a board to | ||
743 | * the available pool. | ||
744 | * | ||
745 | * returns 0 if success | ||
746 | */ | ||
747 | int shpchp_return_board_resources(struct pci_func * func, | ||
748 | struct resource_lists * resources) | ||
749 | { | ||
750 | int rc; | ||
751 | dbg("%s\n", __FUNCTION__); | ||
752 | |||
753 | if (!func) | ||
754 | return 1; | ||
755 | |||
756 | return_resource_list(&(func->io_head),&(resources->io_head)); | ||
757 | return_resource_list(&(func->mem_head),&(resources->mem_head)); | ||
758 | return_resource_list(&(func->p_mem_head),&(resources->p_mem_head)); | ||
759 | return_resource_list(&(func->bus_head),&(resources->bus_head)); | ||
760 | |||
761 | rc = shpchp_resource_sort_and_combine(&(resources->mem_head)); | ||
762 | rc |= shpchp_resource_sort_and_combine(&(resources->p_mem_head)); | ||
763 | rc |= shpchp_resource_sort_and_combine(&(resources->io_head)); | ||
764 | rc |= shpchp_resource_sort_and_combine(&(resources->bus_head)); | ||
765 | |||
766 | return rc; | ||
767 | } | ||
768 | |||
769 | /** | ||
770 | * kfree_resource_list: release memory of all list members | ||
771 | * @res: resource list to free | ||
772 | */ | ||
773 | static inline void | ||
774 | kfree_resource_list(struct pci_resource **r) | ||
775 | { | ||
776 | struct pci_resource *res, *tres; | ||
777 | |||
778 | res = *r; | ||
779 | *r = NULL; | ||
780 | |||
781 | while (res) { | ||
782 | tres = res; | ||
783 | res = res->next; | ||
784 | kfree(tres); | ||
785 | } | ||
786 | } | ||
787 | |||
788 | /** | ||
789 | * shpchp_destroy_resource_list: put node back in the resource list | ||
790 | * @resources: list to put nodes back | ||
791 | */ | ||
792 | void shpchp_destroy_resource_list(struct resource_lists *resources) | ||
793 | { | ||
794 | kfree_resource_list(&(resources->io_head)); | ||
795 | kfree_resource_list(&(resources->mem_head)); | ||
796 | kfree_resource_list(&(resources->p_mem_head)); | ||
797 | kfree_resource_list(&(resources->bus_head)); | ||
798 | } | ||
799 | |||
800 | /** | ||
801 | * shpchp_destroy_board_resources: put node back in the resource list | ||
802 | * @resources: list to put nodes back | ||
803 | */ | ||
804 | void shpchp_destroy_board_resources(struct pci_func * func) | ||
805 | { | ||
806 | kfree_resource_list(&(func->io_head)); | ||
807 | kfree_resource_list(&(func->mem_head)); | ||
808 | kfree_resource_list(&(func->p_mem_head)); | ||
809 | kfree_resource_list(&(func->bus_head)); | ||
810 | } | ||
diff --git a/drivers/pci/hotplug/shpchp_sysfs.c b/drivers/pci/hotplug/shpchp_sysfs.c new file mode 100644 index 000000000000..9a1ee132d12c --- /dev/null +++ b/drivers/pci/hotplug/shpchp_sysfs.c | |||
@@ -0,0 +1,143 @@ | |||
1 | /* | ||
2 | * Compaq Hot Plug Controller Driver | ||
3 | * | ||
4 | * Copyright (c) 1995,2001 Compaq Computer Corporation | ||
5 | * Copyright (c) 2001,2003 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * Copyright (c) 2001 IBM Corp. | ||
7 | * | ||
8 | * All rights reserved. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or (at | ||
13 | * your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
18 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
19 | * details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
24 | * | ||
25 | * Send feedback to <greg@kroah.com> | ||
26 | * | ||
27 | */ | ||
28 | |||
29 | #include <linux/config.h> | ||
30 | #include <linux/module.h> | ||
31 | #include <linux/kernel.h> | ||
32 | #include <linux/types.h> | ||
33 | #include <linux/proc_fs.h> | ||
34 | #include <linux/workqueue.h> | ||
35 | #include <linux/pci.h> | ||
36 | #include "shpchp.h" | ||
37 | |||
38 | |||
39 | /* A few routines that create sysfs entries for the hot plug controller */ | ||
40 | |||
41 | static ssize_t show_ctrl (struct device *dev, char *buf) | ||
42 | { | ||
43 | struct pci_dev *pci_dev; | ||
44 | struct controller *ctrl; | ||
45 | char * out = buf; | ||
46 | int index; | ||
47 | struct pci_resource *res; | ||
48 | |||
49 | pci_dev = container_of (dev, struct pci_dev, dev); | ||
50 | ctrl = pci_get_drvdata(pci_dev); | ||
51 | |||
52 | out += sprintf(buf, "Free resources: memory\n"); | ||
53 | index = 11; | ||
54 | res = ctrl->mem_head; | ||
55 | while (res && index--) { | ||
56 | out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); | ||
57 | res = res->next; | ||
58 | } | ||
59 | out += sprintf(out, "Free resources: prefetchable memory\n"); | ||
60 | index = 11; | ||
61 | res = ctrl->p_mem_head; | ||
62 | while (res && index--) { | ||
63 | out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); | ||
64 | res = res->next; | ||
65 | } | ||
66 | out += sprintf(out, "Free resources: IO\n"); | ||
67 | index = 11; | ||
68 | res = ctrl->io_head; | ||
69 | while (res && index--) { | ||
70 | out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); | ||
71 | res = res->next; | ||
72 | } | ||
73 | out += sprintf(out, "Free resources: bus numbers\n"); | ||
74 | index = 11; | ||
75 | res = ctrl->bus_head; | ||
76 | while (res && index--) { | ||
77 | out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); | ||
78 | res = res->next; | ||
79 | } | ||
80 | |||
81 | return out - buf; | ||
82 | } | ||
83 | static DEVICE_ATTR (ctrl, S_IRUGO, show_ctrl, NULL); | ||
84 | |||
85 | static ssize_t show_dev (struct device *dev, char *buf) | ||
86 | { | ||
87 | struct pci_dev *pci_dev; | ||
88 | struct controller *ctrl; | ||
89 | char * out = buf; | ||
90 | int index; | ||
91 | struct pci_resource *res; | ||
92 | struct pci_func *new_slot; | ||
93 | struct slot *slot; | ||
94 | |||
95 | pci_dev = container_of (dev, struct pci_dev, dev); | ||
96 | ctrl = pci_get_drvdata(pci_dev); | ||
97 | |||
98 | slot=ctrl->slot; | ||
99 | |||
100 | while (slot) { | ||
101 | new_slot = shpchp_slot_find(slot->bus, slot->device, 0); | ||
102 | if (!new_slot) | ||
103 | break; | ||
104 | out += sprintf(out, "assigned resources: memory\n"); | ||
105 | index = 11; | ||
106 | res = new_slot->mem_head; | ||
107 | while (res && index--) { | ||
108 | out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); | ||
109 | res = res->next; | ||
110 | } | ||
111 | out += sprintf(out, "assigned resources: prefetchable memory\n"); | ||
112 | index = 11; | ||
113 | res = new_slot->p_mem_head; | ||
114 | while (res && index--) { | ||
115 | out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); | ||
116 | res = res->next; | ||
117 | } | ||
118 | out += sprintf(out, "assigned resources: IO\n"); | ||
119 | index = 11; | ||
120 | res = new_slot->io_head; | ||
121 | while (res && index--) { | ||
122 | out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); | ||
123 | res = res->next; | ||
124 | } | ||
125 | out += sprintf(out, "assigned resources: bus numbers\n"); | ||
126 | index = 11; | ||
127 | res = new_slot->bus_head; | ||
128 | while (res && index--) { | ||
129 | out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); | ||
130 | res = res->next; | ||
131 | } | ||
132 | slot=slot->next; | ||
133 | } | ||
134 | |||
135 | return out - buf; | ||
136 | } | ||
137 | static DEVICE_ATTR (dev, S_IRUGO, show_dev, NULL); | ||
138 | |||
139 | void shpchp_create_ctrl_files (struct controller *ctrl) | ||
140 | { | ||
141 | device_create_file (&ctrl->pci_dev->dev, &dev_attr_ctrl); | ||
142 | device_create_file (&ctrl->pci_dev->dev, &dev_attr_dev); | ||
143 | } | ||
diff --git a/drivers/pci/hotplug/shpchprm.h b/drivers/pci/hotplug/shpchprm.h new file mode 100644 index 000000000000..88aeb978c911 --- /dev/null +++ b/drivers/pci/hotplug/shpchprm.h | |||
@@ -0,0 +1,55 @@ | |||
1 | /* | ||
2 | * SHPCHPRM : SHPCHP Resource Manager for ACPI/non-ACPI platform | ||
3 | * | ||
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | ||
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * Copyright (C) 2001 IBM Corp. | ||
7 | * Copyright (C) 2003-2004 Intel Corporation | ||
8 | * | ||
9 | * All rights reserved. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or (at | ||
14 | * your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
19 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
20 | * details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
25 | * | ||
26 | * Send feedback to <greg@kroah.com>, <dely.l.sy@intel.com> | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | #ifndef _SHPCHPRM_H_ | ||
31 | #define _SHPCHPRM_H_ | ||
32 | |||
33 | #ifdef CONFIG_HOTPLUG_PCI_SHPC_PHPRM_LEGACY | ||
34 | #include "shpchprm_legacy.h" | ||
35 | #else | ||
36 | #include "shpchprm_nonacpi.h" | ||
37 | #endif | ||
38 | |||
39 | int shpchprm_init(enum php_ctlr_type ct); | ||
40 | void shpchprm_cleanup(void); | ||
41 | int shpchprm_print_pirt(void); | ||
42 | int shpchprm_find_available_resources(struct controller *ctrl); | ||
43 | int shpchprm_set_hpp(struct controller *ctrl, struct pci_func *func, u8 card_type); | ||
44 | void shpchprm_enable_card(struct controller *ctrl, struct pci_func *func, u8 card_type); | ||
45 | int shpchprm_get_physical_slot_number(struct controller *ctrl, u32 *sun, u8 busnum, u8 devnum); | ||
46 | |||
47 | #ifdef DEBUG | ||
48 | #define RES_CHECK(this, bits) \ | ||
49 | { if (((this) & (bits - 1))) \ | ||
50 | printk("%s:%d ERR: potential res loss!\n", __FUNCTION__, __LINE__); } | ||
51 | #else | ||
52 | #define RES_CHECK(this, bits) | ||
53 | #endif | ||
54 | |||
55 | #endif /* _SHPCHPRM_H_ */ | ||
diff --git a/drivers/pci/hotplug/shpchprm_acpi.c b/drivers/pci/hotplug/shpchprm_acpi.c new file mode 100644 index 000000000000..243a51d88b86 --- /dev/null +++ b/drivers/pci/hotplug/shpchprm_acpi.c | |||
@@ -0,0 +1,1713 @@ | |||
1 | /* | ||
2 | * SHPCHPRM ACPI: PHP Resource Manager for ACPI platform | ||
3 | * | ||
4 | * Copyright (C) 2003-2004 Intel Corporation | ||
5 | * | ||
6 | * All rights reserved. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or (at | ||
11 | * your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, but | ||
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
16 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
17 | * details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
22 | * | ||
23 | * Send feedback to <dely.l.sy@intel.com> | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include <linux/config.h> | ||
28 | #include <linux/module.h> | ||
29 | #include <linux/kernel.h> | ||
30 | #include <linux/types.h> | ||
31 | #include <linux/pci.h> | ||
32 | #include <linux/init.h> | ||
33 | #include <linux/acpi.h> | ||
34 | #include <linux/efi.h> | ||
35 | #include <asm/uaccess.h> | ||
36 | #include <asm/system.h> | ||
37 | #ifdef CONFIG_IA64 | ||
38 | #include <asm/iosapic.h> | ||
39 | #endif | ||
40 | #include <acpi/acpi.h> | ||
41 | #include <acpi/acpi_bus.h> | ||
42 | #include <acpi/actypes.h> | ||
43 | #include "shpchp.h" | ||
44 | #include "shpchprm.h" | ||
45 | |||
46 | #define PCI_MAX_BUS 0x100 | ||
47 | #define ACPI_STA_DEVICE_PRESENT 0x01 | ||
48 | |||
49 | #define METHOD_NAME__SUN "_SUN" | ||
50 | #define METHOD_NAME__HPP "_HPP" | ||
51 | #define METHOD_NAME_OSHP "OSHP" | ||
52 | |||
53 | #define PHP_RES_BUS 0xA0 | ||
54 | #define PHP_RES_IO 0xA1 | ||
55 | #define PHP_RES_MEM 0xA2 | ||
56 | #define PHP_RES_PMEM 0xA3 | ||
57 | |||
58 | #define BRIDGE_TYPE_P2P 0x00 | ||
59 | #define BRIDGE_TYPE_HOST 0x01 | ||
60 | |||
61 | /* this should go to drivers/acpi/include/ */ | ||
62 | struct acpi__hpp { | ||
63 | u8 cache_line_size; | ||
64 | u8 latency_timer; | ||
65 | u8 enable_serr; | ||
66 | u8 enable_perr; | ||
67 | }; | ||
68 | |||
69 | struct acpi_php_slot { | ||
70 | struct acpi_php_slot *next; | ||
71 | struct acpi_bridge *bridge; | ||
72 | acpi_handle handle; | ||
73 | int seg; | ||
74 | int bus; | ||
75 | int dev; | ||
76 | int fun; | ||
77 | u32 sun; | ||
78 | struct pci_resource *mem_head; | ||
79 | struct pci_resource *p_mem_head; | ||
80 | struct pci_resource *io_head; | ||
81 | struct pci_resource *bus_head; | ||
82 | void *slot_ops; /* _STA, _EJx, etc */ | ||
83 | struct slot *slot; | ||
84 | }; /* per func */ | ||
85 | |||
86 | struct acpi_bridge { | ||
87 | struct acpi_bridge *parent; | ||
88 | struct acpi_bridge *next; | ||
89 | struct acpi_bridge *child; | ||
90 | acpi_handle handle; | ||
91 | int seg; | ||
92 | int pbus; /* pdev->bus->number */ | ||
93 | int pdevice; /* PCI_SLOT(pdev->devfn) */ | ||
94 | int pfunction; /* PCI_DEVFN(pdev->devfn) */ | ||
95 | int bus; /* pdev->subordinate->number */ | ||
96 | struct acpi__hpp *_hpp; | ||
97 | struct acpi_php_slot *slots; | ||
98 | struct pci_resource *tmem_head; /* total from crs */ | ||
99 | struct pci_resource *tp_mem_head; /* total from crs */ | ||
100 | struct pci_resource *tio_head; /* total from crs */ | ||
101 | struct pci_resource *tbus_head; /* total from crs */ | ||
102 | struct pci_resource *mem_head; /* available */ | ||
103 | struct pci_resource *p_mem_head; /* available */ | ||
104 | struct pci_resource *io_head; /* available */ | ||
105 | struct pci_resource *bus_head; /* available */ | ||
106 | int scanned; | ||
107 | int type; | ||
108 | }; | ||
109 | |||
110 | static struct acpi_bridge *acpi_bridges_head; | ||
111 | |||
112 | static u8 * acpi_path_name( acpi_handle handle) | ||
113 | { | ||
114 | acpi_status status; | ||
115 | static u8 path_name[ACPI_PATHNAME_MAX]; | ||
116 | struct acpi_buffer ret_buf = { ACPI_PATHNAME_MAX, path_name }; | ||
117 | |||
118 | memset(path_name, 0, sizeof (path_name)); | ||
119 | status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &ret_buf); | ||
120 | |||
121 | if (ACPI_FAILURE(status)) | ||
122 | return NULL; | ||
123 | else | ||
124 | return path_name; | ||
125 | } | ||
126 | |||
127 | static void acpi_get__hpp ( struct acpi_bridge *ab); | ||
128 | static void acpi_run_oshp ( struct acpi_bridge *ab); | ||
129 | |||
130 | static int acpi_add_slot_to_php_slots( | ||
131 | struct acpi_bridge *ab, | ||
132 | int bus_num, | ||
133 | acpi_handle handle, | ||
134 | u32 adr, | ||
135 | u32 sun | ||
136 | ) | ||
137 | { | ||
138 | struct acpi_php_slot *aps; | ||
139 | static long samesun = -1; | ||
140 | |||
141 | aps = (struct acpi_php_slot *) kmalloc (sizeof(struct acpi_php_slot), GFP_KERNEL); | ||
142 | if (!aps) { | ||
143 | err ("acpi_shpchprm: alloc for aps fail\n"); | ||
144 | return -1; | ||
145 | } | ||
146 | memset(aps, 0, sizeof(struct acpi_php_slot)); | ||
147 | |||
148 | aps->handle = handle; | ||
149 | aps->bus = bus_num; | ||
150 | aps->dev = (adr >> 16) & 0xffff; | ||
151 | aps->fun = adr & 0xffff; | ||
152 | aps->sun = sun; | ||
153 | |||
154 | aps->next = ab->slots; /* cling to the bridge */ | ||
155 | aps->bridge = ab; | ||
156 | ab->slots = aps; | ||
157 | |||
158 | ab->scanned += 1; | ||
159 | if (!ab->_hpp) | ||
160 | acpi_get__hpp(ab); | ||
161 | |||
162 | acpi_run_oshp(ab); | ||
163 | |||
164 | if (sun != samesun) { | ||
165 | info("acpi_shpchprm: Slot sun(%x) at s:b:d:f=0x%02x:%02x:%02x:%02x\n", aps->sun, ab->seg, | ||
166 | aps->bus, aps->dev, aps->fun); | ||
167 | samesun = sun; | ||
168 | } | ||
169 | return 0; | ||
170 | } | ||
171 | |||
172 | static void acpi_get__hpp ( struct acpi_bridge *ab) | ||
173 | { | ||
174 | acpi_status status; | ||
175 | u8 nui[4]; | ||
176 | struct acpi_buffer ret_buf = { 0, NULL}; | ||
177 | union acpi_object *ext_obj, *package; | ||
178 | u8 *path_name = acpi_path_name(ab->handle); | ||
179 | int i, len = 0; | ||
180 | |||
181 | /* get _hpp */ | ||
182 | status = acpi_evaluate_object(ab->handle, METHOD_NAME__HPP, NULL, &ret_buf); | ||
183 | switch (status) { | ||
184 | case AE_BUFFER_OVERFLOW: | ||
185 | ret_buf.pointer = kmalloc (ret_buf.length, GFP_KERNEL); | ||
186 | if (!ret_buf.pointer) { | ||
187 | err ("acpi_shpchprm:%s alloc for _HPP fail\n", path_name); | ||
188 | return; | ||
189 | } | ||
190 | status = acpi_evaluate_object(ab->handle, METHOD_NAME__HPP, NULL, &ret_buf); | ||
191 | if (ACPI_SUCCESS(status)) | ||
192 | break; | ||
193 | default: | ||
194 | if (ACPI_FAILURE(status)) { | ||
195 | err("acpi_shpchprm:%s _HPP fail=0x%x\n", path_name, status); | ||
196 | return; | ||
197 | } | ||
198 | } | ||
199 | |||
200 | ext_obj = (union acpi_object *) ret_buf.pointer; | ||
201 | if (ext_obj->type != ACPI_TYPE_PACKAGE) { | ||
202 | err ("acpi_shpchprm:%s _HPP obj not a package\n", path_name); | ||
203 | goto free_and_return; | ||
204 | } | ||
205 | |||
206 | len = ext_obj->package.count; | ||
207 | package = (union acpi_object *) ret_buf.pointer; | ||
208 | for ( i = 0; (i < len) || (i < 4); i++) { | ||
209 | ext_obj = (union acpi_object *) &package->package.elements[i]; | ||
210 | switch (ext_obj->type) { | ||
211 | case ACPI_TYPE_INTEGER: | ||
212 | nui[i] = (u8)ext_obj->integer.value; | ||
213 | break; | ||
214 | default: | ||
215 | err ("acpi_shpchprm:%s _HPP obj type incorrect\n", path_name); | ||
216 | goto free_and_return; | ||
217 | } | ||
218 | } | ||
219 | |||
220 | ab->_hpp = kmalloc (sizeof (struct acpi__hpp), GFP_KERNEL); | ||
221 | if (!ab->_hpp) { | ||
222 | err ("acpi_shpchprm:%s alloc for _HPP failed\n", path_name); | ||
223 | goto free_and_return; | ||
224 | } | ||
225 | memset(ab->_hpp, 0, sizeof(struct acpi__hpp)); | ||
226 | |||
227 | ab->_hpp->cache_line_size = nui[0]; | ||
228 | ab->_hpp->latency_timer = nui[1]; | ||
229 | ab->_hpp->enable_serr = nui[2]; | ||
230 | ab->_hpp->enable_perr = nui[3]; | ||
231 | |||
232 | dbg(" _HPP: cache_line_size=0x%x\n", ab->_hpp->cache_line_size); | ||
233 | dbg(" _HPP: latency timer =0x%x\n", ab->_hpp->latency_timer); | ||
234 | dbg(" _HPP: enable SERR =0x%x\n", ab->_hpp->enable_serr); | ||
235 | dbg(" _HPP: enable PERR =0x%x\n", ab->_hpp->enable_perr); | ||
236 | |||
237 | free_and_return: | ||
238 | kfree(ret_buf.pointer); | ||
239 | } | ||
240 | |||
241 | static void acpi_run_oshp ( struct acpi_bridge *ab) | ||
242 | { | ||
243 | acpi_status status; | ||
244 | u8 *path_name = acpi_path_name(ab->handle); | ||
245 | |||
246 | /* run OSHP */ | ||
247 | status = acpi_evaluate_object(ab->handle, METHOD_NAME_OSHP, NULL, NULL); | ||
248 | if (ACPI_FAILURE(status)) { | ||
249 | err("acpi_pciehprm:%s OSHP fails=0x%x\n", path_name, status); | ||
250 | } else | ||
251 | dbg("acpi_pciehprm:%s OSHP passes =0x%x\n", path_name, status); | ||
252 | return; | ||
253 | } | ||
254 | |||
255 | static acpi_status acpi_evaluate_crs( | ||
256 | acpi_handle handle, | ||
257 | struct acpi_resource **retbuf | ||
258 | ) | ||
259 | { | ||
260 | acpi_status status; | ||
261 | struct acpi_buffer crsbuf; | ||
262 | u8 *path_name = acpi_path_name(handle); | ||
263 | |||
264 | crsbuf.length = 0; | ||
265 | crsbuf.pointer = NULL; | ||
266 | |||
267 | status = acpi_get_current_resources (handle, &crsbuf); | ||
268 | |||
269 | switch (status) { | ||
270 | case AE_BUFFER_OVERFLOW: | ||
271 | break; /* found */ | ||
272 | case AE_NOT_FOUND: | ||
273 | dbg("acpi_shpchprm:%s _CRS not found\n", path_name); | ||
274 | return status; | ||
275 | default: | ||
276 | err ("acpi_shpchprm:%s _CRS fail=0x%x\n", path_name, status); | ||
277 | return status; | ||
278 | } | ||
279 | |||
280 | crsbuf.pointer = kmalloc (crsbuf.length, GFP_KERNEL); | ||
281 | if (!crsbuf.pointer) { | ||
282 | err ("acpi_shpchprm: alloc %ld bytes for %s _CRS fail\n", (ulong)crsbuf.length, path_name); | ||
283 | return AE_NO_MEMORY; | ||
284 | } | ||
285 | |||
286 | status = acpi_get_current_resources (handle, &crsbuf); | ||
287 | if (ACPI_FAILURE(status)) { | ||
288 | err("acpi_shpchprm: %s _CRS fail=0x%x.\n", path_name, status); | ||
289 | kfree(crsbuf.pointer); | ||
290 | return status; | ||
291 | } | ||
292 | |||
293 | *retbuf = crsbuf.pointer; | ||
294 | |||
295 | return status; | ||
296 | } | ||
297 | |||
298 | static void free_pci_resource ( struct pci_resource *aprh) | ||
299 | { | ||
300 | struct pci_resource *res, *next; | ||
301 | |||
302 | for (res = aprh; res; res = next) { | ||
303 | next = res->next; | ||
304 | kfree(res); | ||
305 | } | ||
306 | } | ||
307 | |||
308 | static void print_pci_resource ( struct pci_resource *aprh) | ||
309 | { | ||
310 | struct pci_resource *res; | ||
311 | |||
312 | for (res = aprh; res; res = res->next) | ||
313 | dbg(" base= 0x%x length= 0x%x\n", res->base, res->length); | ||
314 | } | ||
315 | |||
316 | static void print_slot_resources( struct acpi_php_slot *aps) | ||
317 | { | ||
318 | if (aps->bus_head) { | ||
319 | dbg(" BUS Resources:\n"); | ||
320 | print_pci_resource (aps->bus_head); | ||
321 | } | ||
322 | |||
323 | if (aps->io_head) { | ||
324 | dbg(" IO Resources:\n"); | ||
325 | print_pci_resource (aps->io_head); | ||
326 | } | ||
327 | |||
328 | if (aps->mem_head) { | ||
329 | dbg(" MEM Resources:\n"); | ||
330 | print_pci_resource (aps->mem_head); | ||
331 | } | ||
332 | |||
333 | if (aps->p_mem_head) { | ||
334 | dbg(" PMEM Resources:\n"); | ||
335 | print_pci_resource (aps->p_mem_head); | ||
336 | } | ||
337 | } | ||
338 | |||
339 | static void print_pci_resources( struct acpi_bridge *ab) | ||
340 | { | ||
341 | if (ab->tbus_head) { | ||
342 | dbg(" Total BUS Resources:\n"); | ||
343 | print_pci_resource (ab->tbus_head); | ||
344 | } | ||
345 | if (ab->bus_head) { | ||
346 | dbg(" BUS Resources:\n"); | ||
347 | print_pci_resource (ab->bus_head); | ||
348 | } | ||
349 | |||
350 | if (ab->tio_head) { | ||
351 | dbg(" Total IO Resources:\n"); | ||
352 | print_pci_resource (ab->tio_head); | ||
353 | } | ||
354 | if (ab->io_head) { | ||
355 | dbg(" IO Resources:\n"); | ||
356 | print_pci_resource (ab->io_head); | ||
357 | } | ||
358 | |||
359 | if (ab->tmem_head) { | ||
360 | dbg(" Total MEM Resources:\n"); | ||
361 | print_pci_resource (ab->tmem_head); | ||
362 | } | ||
363 | if (ab->mem_head) { | ||
364 | dbg(" MEM Resources:\n"); | ||
365 | print_pci_resource (ab->mem_head); | ||
366 | } | ||
367 | |||
368 | if (ab->tp_mem_head) { | ||
369 | dbg(" Total PMEM Resources:\n"); | ||
370 | print_pci_resource (ab->tp_mem_head); | ||
371 | } | ||
372 | if (ab->p_mem_head) { | ||
373 | dbg(" PMEM Resources:\n"); | ||
374 | print_pci_resource (ab->p_mem_head); | ||
375 | } | ||
376 | if (ab->_hpp) { | ||
377 | dbg(" _HPP: cache_line_size=0x%x\n", ab->_hpp->cache_line_size); | ||
378 | dbg(" _HPP: latency timer =0x%x\n", ab->_hpp->latency_timer); | ||
379 | dbg(" _HPP: enable SERR =0x%x\n", ab->_hpp->enable_serr); | ||
380 | dbg(" _HPP: enable PERR =0x%x\n", ab->_hpp->enable_perr); | ||
381 | } | ||
382 | } | ||
383 | |||
384 | static int shpchprm_delete_resource( | ||
385 | struct pci_resource **aprh, | ||
386 | ulong base, | ||
387 | ulong size) | ||
388 | { | ||
389 | struct pci_resource *res; | ||
390 | struct pci_resource *prevnode; | ||
391 | struct pci_resource *split_node; | ||
392 | ulong tbase; | ||
393 | |||
394 | shpchp_resource_sort_and_combine(aprh); | ||
395 | |||
396 | for (res = *aprh; res; res = res->next) { | ||
397 | if (res->base > base) | ||
398 | continue; | ||
399 | |||
400 | if ((res->base + res->length) < (base + size)) | ||
401 | continue; | ||
402 | |||
403 | if (res->base < base) { | ||
404 | tbase = base; | ||
405 | |||
406 | if ((res->length - (tbase - res->base)) < size) | ||
407 | continue; | ||
408 | |||
409 | split_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); | ||
410 | if (!split_node) | ||
411 | return -ENOMEM; | ||
412 | |||
413 | split_node->base = res->base; | ||
414 | split_node->length = tbase - res->base; | ||
415 | res->base = tbase; | ||
416 | res->length -= split_node->length; | ||
417 | |||
418 | split_node->next = res->next; | ||
419 | res->next = split_node; | ||
420 | } | ||
421 | |||
422 | if (res->length >= size) { | ||
423 | split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); | ||
424 | if (!split_node) | ||
425 | return -ENOMEM; | ||
426 | |||
427 | split_node->base = res->base + size; | ||
428 | split_node->length = res->length - size; | ||
429 | res->length = size; | ||
430 | |||
431 | split_node->next = res->next; | ||
432 | res->next = split_node; | ||
433 | } | ||
434 | |||
435 | if (*aprh == res) { | ||
436 | *aprh = res->next; | ||
437 | } else { | ||
438 | prevnode = *aprh; | ||
439 | while (prevnode->next != res) | ||
440 | prevnode = prevnode->next; | ||
441 | |||
442 | prevnode->next = res->next; | ||
443 | } | ||
444 | res->next = NULL; | ||
445 | kfree(res); | ||
446 | break; | ||
447 | } | ||
448 | |||
449 | return 0; | ||
450 | } | ||
451 | |||
452 | static int shpchprm_delete_resources( | ||
453 | struct pci_resource **aprh, | ||
454 | struct pci_resource *this | ||
455 | ) | ||
456 | { | ||
457 | struct pci_resource *res; | ||
458 | |||
459 | for (res = this; res; res = res->next) | ||
460 | shpchprm_delete_resource(aprh, res->base, res->length); | ||
461 | |||
462 | return 0; | ||
463 | } | ||
464 | |||
465 | static int shpchprm_add_resource( | ||
466 | struct pci_resource **aprh, | ||
467 | ulong base, | ||
468 | ulong size) | ||
469 | { | ||
470 | struct pci_resource *res; | ||
471 | |||
472 | for (res = *aprh; res; res = res->next) { | ||
473 | if ((res->base + res->length) == base) { | ||
474 | res->length += size; | ||
475 | size = 0L; | ||
476 | break; | ||
477 | } | ||
478 | if (res->next == *aprh) | ||
479 | break; | ||
480 | } | ||
481 | |||
482 | if (size) { | ||
483 | res = kmalloc(sizeof(struct pci_resource), GFP_KERNEL); | ||
484 | if (!res) { | ||
485 | err ("acpi_shpchprm: alloc for res fail\n"); | ||
486 | return -ENOMEM; | ||
487 | } | ||
488 | memset(res, 0, sizeof (struct pci_resource)); | ||
489 | |||
490 | res->base = base; | ||
491 | res->length = size; | ||
492 | res->next = *aprh; | ||
493 | *aprh = res; | ||
494 | } | ||
495 | |||
496 | return 0; | ||
497 | } | ||
498 | |||
499 | static int shpchprm_add_resources( | ||
500 | struct pci_resource **aprh, | ||
501 | struct pci_resource *this | ||
502 | ) | ||
503 | { | ||
504 | struct pci_resource *res; | ||
505 | int rc = 0; | ||
506 | |||
507 | for (res = this; res && !rc; res = res->next) | ||
508 | rc = shpchprm_add_resource(aprh, res->base, res->length); | ||
509 | |||
510 | return rc; | ||
511 | } | ||
512 | |||
513 | static void acpi_parse_io ( | ||
514 | struct acpi_bridge *ab, | ||
515 | union acpi_resource_data *data | ||
516 | ) | ||
517 | { | ||
518 | struct acpi_resource_io *dataio; | ||
519 | dataio = (struct acpi_resource_io *) data; | ||
520 | |||
521 | dbg("Io Resource\n"); | ||
522 | dbg(" %d bit decode\n", ACPI_DECODE_16 == dataio->io_decode ? 16:10); | ||
523 | dbg(" Range minimum base: %08X\n", dataio->min_base_address); | ||
524 | dbg(" Range maximum base: %08X\n", dataio->max_base_address); | ||
525 | dbg(" Alignment: %08X\n", dataio->alignment); | ||
526 | dbg(" Range Length: %08X\n", dataio->range_length); | ||
527 | } | ||
528 | |||
529 | static void acpi_parse_fixed_io ( | ||
530 | struct acpi_bridge *ab, | ||
531 | union acpi_resource_data *data | ||
532 | ) | ||
533 | { | ||
534 | struct acpi_resource_fixed_io *datafio; | ||
535 | datafio = (struct acpi_resource_fixed_io *) data; | ||
536 | |||
537 | dbg("Fixed Io Resource\n"); | ||
538 | dbg(" Range base address: %08X", datafio->base_address); | ||
539 | dbg(" Range length: %08X", datafio->range_length); | ||
540 | } | ||
541 | |||
542 | static void acpi_parse_address16_32 ( | ||
543 | struct acpi_bridge *ab, | ||
544 | union acpi_resource_data *data, | ||
545 | acpi_resource_type id | ||
546 | ) | ||
547 | { | ||
548 | /* | ||
549 | * acpi_resource_address16 == acpi_resource_address32 | ||
550 | * acpi_resource_address16 *data16 = (acpi_resource_address16 *) data; | ||
551 | */ | ||
552 | struct acpi_resource_address32 *data32 = (struct acpi_resource_address32 *) data; | ||
553 | struct pci_resource **aprh, **tprh; | ||
554 | |||
555 | if (id == ACPI_RSTYPE_ADDRESS16) | ||
556 | dbg("acpi_shpchprm:16-Bit Address Space Resource\n"); | ||
557 | else | ||
558 | dbg("acpi_shpchprm:32-Bit Address Space Resource\n"); | ||
559 | |||
560 | switch (data32->resource_type) { | ||
561 | case ACPI_MEMORY_RANGE: | ||
562 | dbg(" Resource Type: Memory Range\n"); | ||
563 | aprh = &ab->mem_head; | ||
564 | tprh = &ab->tmem_head; | ||
565 | |||
566 | switch (data32->attribute.memory.cache_attribute) { | ||
567 | case ACPI_NON_CACHEABLE_MEMORY: | ||
568 | dbg(" Type Specific: Noncacheable memory\n"); | ||
569 | break; | ||
570 | case ACPI_CACHABLE_MEMORY: | ||
571 | dbg(" Type Specific: Cacheable memory\n"); | ||
572 | break; | ||
573 | case ACPI_WRITE_COMBINING_MEMORY: | ||
574 | dbg(" Type Specific: Write-combining memory\n"); | ||
575 | break; | ||
576 | case ACPI_PREFETCHABLE_MEMORY: | ||
577 | aprh = &ab->p_mem_head; | ||
578 | dbg(" Type Specific: Prefetchable memory\n"); | ||
579 | break; | ||
580 | default: | ||
581 | dbg(" Type Specific: Invalid cache attribute\n"); | ||
582 | break; | ||
583 | } | ||
584 | |||
585 | dbg(" Type Specific: Read%s\n", ACPI_READ_WRITE_MEMORY == data32->attribute.memory.read_write_attribute ? "/Write":" Only"); | ||
586 | break; | ||
587 | |||
588 | case ACPI_IO_RANGE: | ||
589 | dbg(" Resource Type: I/O Range\n"); | ||
590 | aprh = &ab->io_head; | ||
591 | tprh = &ab->tio_head; | ||
592 | |||
593 | switch (data32->attribute.io.range_attribute) { | ||
594 | case ACPI_NON_ISA_ONLY_RANGES: | ||
595 | dbg(" Type Specific: Non-ISA Io Addresses\n"); | ||
596 | break; | ||
597 | case ACPI_ISA_ONLY_RANGES: | ||
598 | dbg(" Type Specific: ISA Io Addresses\n"); | ||
599 | break; | ||
600 | case ACPI_ENTIRE_RANGE: | ||
601 | dbg(" Type Specific: ISA and non-ISA Io Addresses\n"); | ||
602 | break; | ||
603 | default: | ||
604 | dbg(" Type Specific: Invalid range attribute\n"); | ||
605 | break; | ||
606 | } | ||
607 | break; | ||
608 | |||
609 | case ACPI_BUS_NUMBER_RANGE: | ||
610 | dbg(" Resource Type: Bus Number Range(fixed)\n"); | ||
611 | /* fixup to be compatible with the rest of php driver */ | ||
612 | data32->min_address_range++; | ||
613 | data32->address_length--; | ||
614 | aprh = &ab->bus_head; | ||
615 | tprh = &ab->tbus_head; | ||
616 | break; | ||
617 | default: | ||
618 | dbg(" Resource Type: Invalid resource type. Exiting.\n"); | ||
619 | return; | ||
620 | } | ||
621 | |||
622 | dbg(" Resource %s\n", ACPI_CONSUMER == data32->producer_consumer ? "Consumer":"Producer"); | ||
623 | dbg(" %s decode\n", ACPI_SUB_DECODE == data32->decode ? "Subtractive":"Positive"); | ||
624 | dbg(" Min address is %s fixed\n", ACPI_ADDRESS_FIXED == data32->min_address_fixed ? "":"not"); | ||
625 | dbg(" Max address is %s fixed\n", ACPI_ADDRESS_FIXED == data32->max_address_fixed ? "":"not"); | ||
626 | dbg(" Granularity: %08X\n", data32->granularity); | ||
627 | dbg(" Address range min: %08X\n", data32->min_address_range); | ||
628 | dbg(" Address range max: %08X\n", data32->max_address_range); | ||
629 | dbg(" Address translation offset: %08X\n", data32->address_translation_offset); | ||
630 | dbg(" Address Length: %08X\n", data32->address_length); | ||
631 | |||
632 | if (0xFF != data32->resource_source.index) { | ||
633 | dbg(" Resource Source Index: %X\n", data32->resource_source.index); | ||
634 | /* dbg(" Resource Source: %s\n", data32->resource_source.string_ptr); */ | ||
635 | } | ||
636 | |||
637 | shpchprm_add_resource(aprh, data32->min_address_range, data32->address_length); | ||
638 | } | ||
639 | |||
640 | static acpi_status acpi_parse_crs( | ||
641 | struct acpi_bridge *ab, | ||
642 | struct acpi_resource *crsbuf | ||
643 | ) | ||
644 | { | ||
645 | acpi_status status = AE_OK; | ||
646 | struct acpi_resource *resource = crsbuf; | ||
647 | u8 count = 0; | ||
648 | u8 done = 0; | ||
649 | |||
650 | while (!done) { | ||
651 | dbg("acpi_shpchprm: PCI bus 0x%x Resource structure %x.\n", ab->bus, count++); | ||
652 | switch (resource->id) { | ||
653 | case ACPI_RSTYPE_IRQ: | ||
654 | dbg("Irq -------- Resource\n"); | ||
655 | break; | ||
656 | case ACPI_RSTYPE_DMA: | ||
657 | dbg("DMA -------- Resource\n"); | ||
658 | break; | ||
659 | case ACPI_RSTYPE_START_DPF: | ||
660 | dbg("Start DPF -------- Resource\n"); | ||
661 | break; | ||
662 | case ACPI_RSTYPE_END_DPF: | ||
663 | dbg("End DPF -------- Resource\n"); | ||
664 | break; | ||
665 | case ACPI_RSTYPE_IO: | ||
666 | acpi_parse_io (ab, &resource->data); | ||
667 | break; | ||
668 | case ACPI_RSTYPE_FIXED_IO: | ||
669 | acpi_parse_fixed_io (ab, &resource->data); | ||
670 | break; | ||
671 | case ACPI_RSTYPE_VENDOR: | ||
672 | dbg("Vendor -------- Resource\n"); | ||
673 | break; | ||
674 | case ACPI_RSTYPE_END_TAG: | ||
675 | dbg("End_tag -------- Resource\n"); | ||
676 | done = 1; | ||
677 | break; | ||
678 | case ACPI_RSTYPE_MEM24: | ||
679 | dbg("Mem24 -------- Resource\n"); | ||
680 | break; | ||
681 | case ACPI_RSTYPE_MEM32: | ||
682 | dbg("Mem32 -------- Resource\n"); | ||
683 | break; | ||
684 | case ACPI_RSTYPE_FIXED_MEM32: | ||
685 | dbg("Fixed Mem32 -------- Resource\n"); | ||
686 | break; | ||
687 | case ACPI_RSTYPE_ADDRESS16: | ||
688 | acpi_parse_address16_32(ab, &resource->data, ACPI_RSTYPE_ADDRESS16); | ||
689 | break; | ||
690 | case ACPI_RSTYPE_ADDRESS32: | ||
691 | acpi_parse_address16_32(ab, &resource->data, ACPI_RSTYPE_ADDRESS32); | ||
692 | break; | ||
693 | case ACPI_RSTYPE_ADDRESS64: | ||
694 | info("Address64 -------- Resource unparsed\n"); | ||
695 | break; | ||
696 | case ACPI_RSTYPE_EXT_IRQ: | ||
697 | dbg("Ext Irq -------- Resource\n"); | ||
698 | break; | ||
699 | default: | ||
700 | dbg("Invalid -------- resource type 0x%x\n", resource->id); | ||
701 | break; | ||
702 | } | ||
703 | |||
704 | resource = (struct acpi_resource *) ((char *)resource + resource->length); | ||
705 | } | ||
706 | |||
707 | return status; | ||
708 | } | ||
709 | |||
710 | static acpi_status acpi_get_crs( struct acpi_bridge *ab) | ||
711 | { | ||
712 | acpi_status status; | ||
713 | struct acpi_resource *crsbuf; | ||
714 | |||
715 | status = acpi_evaluate_crs(ab->handle, &crsbuf); | ||
716 | if (ACPI_SUCCESS(status)) { | ||
717 | status = acpi_parse_crs(ab, crsbuf); | ||
718 | kfree(crsbuf); | ||
719 | |||
720 | shpchp_resource_sort_and_combine(&ab->bus_head); | ||
721 | shpchp_resource_sort_and_combine(&ab->io_head); | ||
722 | shpchp_resource_sort_and_combine(&ab->mem_head); | ||
723 | shpchp_resource_sort_and_combine(&ab->p_mem_head); | ||
724 | |||
725 | shpchprm_add_resources (&ab->tbus_head, ab->bus_head); | ||
726 | shpchprm_add_resources (&ab->tio_head, ab->io_head); | ||
727 | shpchprm_add_resources (&ab->tmem_head, ab->mem_head); | ||
728 | shpchprm_add_resources (&ab->tp_mem_head, ab->p_mem_head); | ||
729 | } | ||
730 | |||
731 | return status; | ||
732 | } | ||
733 | |||
734 | /* find acpi_bridge downword from ab. */ | ||
735 | static struct acpi_bridge * | ||
736 | find_acpi_bridge_by_bus( | ||
737 | struct acpi_bridge *ab, | ||
738 | int seg, | ||
739 | int bus /* pdev->subordinate->number */ | ||
740 | ) | ||
741 | { | ||
742 | struct acpi_bridge *lab = NULL; | ||
743 | |||
744 | if (!ab) | ||
745 | return NULL; | ||
746 | |||
747 | if ((ab->bus == bus) && (ab->seg == seg)) | ||
748 | return ab; | ||
749 | |||
750 | if (ab->child) | ||
751 | lab = find_acpi_bridge_by_bus(ab->child, seg, bus); | ||
752 | |||
753 | if (!lab) | ||
754 | if (ab->next) | ||
755 | lab = find_acpi_bridge_by_bus(ab->next, seg, bus); | ||
756 | |||
757 | return lab; | ||
758 | } | ||
759 | |||
760 | /* | ||
761 | * Build a device tree of ACPI PCI Bridges | ||
762 | */ | ||
763 | static void shpchprm_acpi_register_a_bridge ( | ||
764 | struct acpi_bridge **head, | ||
765 | struct acpi_bridge *pab, /* parent bridge to which child bridge is added */ | ||
766 | struct acpi_bridge *cab /* child bridge to add */ | ||
767 | ) | ||
768 | { | ||
769 | struct acpi_bridge *lpab; | ||
770 | struct acpi_bridge *lcab; | ||
771 | |||
772 | lpab = find_acpi_bridge_by_bus(*head, pab->seg, pab->bus); | ||
773 | if (!lpab) { | ||
774 | if (!(pab->type & BRIDGE_TYPE_HOST)) | ||
775 | warn("PCI parent bridge s:b(%x:%x) not in list.\n", pab->seg, pab->bus); | ||
776 | pab->next = *head; | ||
777 | *head = pab; | ||
778 | lpab = pab; | ||
779 | } | ||
780 | |||
781 | if ((cab->type & BRIDGE_TYPE_HOST) && (pab == cab)) | ||
782 | return; | ||
783 | |||
784 | lcab = find_acpi_bridge_by_bus(*head, cab->seg, cab->bus); | ||
785 | if (lcab) { | ||
786 | if ((pab->bus != lcab->parent->bus) || (lcab->bus != cab->bus)) | ||
787 | err("PCI child bridge s:b(%x:%x) in list with diff parent.\n", cab->seg, cab->bus); | ||
788 | return; | ||
789 | } else | ||
790 | lcab = cab; | ||
791 | |||
792 | lcab->parent = lpab; | ||
793 | lcab->next = lpab->child; | ||
794 | lpab->child = lcab; | ||
795 | } | ||
796 | |||
797 | static acpi_status shpchprm_acpi_build_php_slots_callback( | ||
798 | acpi_handle handle, | ||
799 | u32 Level, | ||
800 | void *context, | ||
801 | void **retval | ||
802 | ) | ||
803 | { | ||
804 | ulong bus_num; | ||
805 | ulong seg_num; | ||
806 | ulong sun, adr; | ||
807 | ulong padr = 0; | ||
808 | acpi_handle phandle = NULL; | ||
809 | struct acpi_bridge *pab = (struct acpi_bridge *)context; | ||
810 | struct acpi_bridge *lab; | ||
811 | acpi_status status; | ||
812 | u8 *path_name = acpi_path_name(handle); | ||
813 | |||
814 | /* get _SUN */ | ||
815 | status = acpi_evaluate_integer(handle, METHOD_NAME__SUN, NULL, &sun); | ||
816 | switch(status) { | ||
817 | case AE_NOT_FOUND: | ||
818 | return AE_OK; | ||
819 | default: | ||
820 | if (ACPI_FAILURE(status)) { | ||
821 | err("acpi_shpchprm:%s _SUN fail=0x%x\n", path_name, status); | ||
822 | return status; | ||
823 | } | ||
824 | } | ||
825 | |||
826 | /* get _ADR. _ADR must exist if _SUN exists */ | ||
827 | status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr); | ||
828 | if (ACPI_FAILURE(status)) { | ||
829 | err("acpi_shpchprm:%s _ADR fail=0x%x\n", path_name, status); | ||
830 | return status; | ||
831 | } | ||
832 | |||
833 | dbg("acpi_shpchprm:%s sun=0x%08x adr=0x%08x\n", path_name, (u32)sun, (u32)adr); | ||
834 | |||
835 | status = acpi_get_parent(handle, &phandle); | ||
836 | if (ACPI_FAILURE(status)) { | ||
837 | err("acpi_shpchprm:%s get_parent fail=0x%x\n", path_name, status); | ||
838 | return (status); | ||
839 | } | ||
840 | |||
841 | bus_num = pab->bus; | ||
842 | seg_num = pab->seg; | ||
843 | |||
844 | if (pab->bus == bus_num) { | ||
845 | lab = pab; | ||
846 | } else { | ||
847 | dbg("WARN: pab is not parent\n"); | ||
848 | lab = find_acpi_bridge_by_bus(pab, seg_num, bus_num); | ||
849 | if (!lab) { | ||
850 | dbg("acpi_shpchprm: alloc new P2P bridge(%x) for sun(%08x)\n", (u32)bus_num, (u32)sun); | ||
851 | lab = (struct acpi_bridge *)kmalloc(sizeof(struct acpi_bridge), GFP_KERNEL); | ||
852 | if (!lab) { | ||
853 | err("acpi_shpchprm: alloc for ab fail\n"); | ||
854 | return AE_NO_MEMORY; | ||
855 | } | ||
856 | memset(lab, 0, sizeof(struct acpi_bridge)); | ||
857 | |||
858 | lab->handle = phandle; | ||
859 | lab->pbus = pab->bus; | ||
860 | lab->pdevice = (int)(padr >> 16) & 0xffff; | ||
861 | lab->pfunction = (int)(padr & 0xffff); | ||
862 | lab->bus = (int)bus_num; | ||
863 | lab->scanned = 0; | ||
864 | lab->type = BRIDGE_TYPE_P2P; | ||
865 | |||
866 | shpchprm_acpi_register_a_bridge (&acpi_bridges_head, pab, lab); | ||
867 | } else | ||
868 | dbg("acpi_shpchprm: found P2P bridge(%x) for sun(%08x)\n", (u32)bus_num, (u32)sun); | ||
869 | } | ||
870 | |||
871 | acpi_add_slot_to_php_slots(lab, (int)bus_num, handle, (u32)adr, (u32)sun); | ||
872 | return (status); | ||
873 | } | ||
874 | |||
875 | static int shpchprm_acpi_build_php_slots( | ||
876 | struct acpi_bridge *ab, | ||
877 | u32 depth | ||
878 | ) | ||
879 | { | ||
880 | acpi_status status; | ||
881 | u8 *path_name = acpi_path_name(ab->handle); | ||
882 | |||
883 | /* Walk down this pci bridge to get _SUNs if any behind P2P */ | ||
884 | status = acpi_walk_namespace ( ACPI_TYPE_DEVICE, | ||
885 | ab->handle, | ||
886 | depth, | ||
887 | shpchprm_acpi_build_php_slots_callback, | ||
888 | ab, | ||
889 | NULL ); | ||
890 | if (ACPI_FAILURE(status)) { | ||
891 | dbg("acpi_shpchprm:%s walk for _SUN on pci bridge seg:bus(%x:%x) fail=0x%x\n", path_name, ab->seg, ab->bus, status); | ||
892 | return -1; | ||
893 | } | ||
894 | |||
895 | return 0; | ||
896 | } | ||
897 | |||
898 | static void build_a_bridge( | ||
899 | struct acpi_bridge *pab, | ||
900 | struct acpi_bridge *ab | ||
901 | ) | ||
902 | { | ||
903 | u8 *path_name = acpi_path_name(ab->handle); | ||
904 | |||
905 | shpchprm_acpi_register_a_bridge (&acpi_bridges_head, pab, ab); | ||
906 | |||
907 | switch (ab->type) { | ||
908 | case BRIDGE_TYPE_HOST: | ||
909 | dbg("acpi_shpchprm: Registered PCI HOST Bridge(%02x) on s:b:d:f(%02x:%02x:%02x:%02x) [%s]\n", | ||
910 | ab->bus, ab->seg, ab->pbus, ab->pdevice, ab->pfunction, path_name); | ||
911 | break; | ||
912 | case BRIDGE_TYPE_P2P: | ||
913 | dbg("acpi_shpchprm: Registered PCI P2P Bridge(%02x-%02x) on s:b:d:f(%02x:%02x:%02x:%02x) [%s]\n", | ||
914 | ab->pbus, ab->bus, ab->seg, ab->pbus, ab->pdevice, ab->pfunction, path_name); | ||
915 | break; | ||
916 | }; | ||
917 | |||
918 | /* build any immediate PHP slots under this pci bridge */ | ||
919 | shpchprm_acpi_build_php_slots(ab, 1); | ||
920 | } | ||
921 | |||
922 | static struct acpi_bridge * add_p2p_bridge( | ||
923 | acpi_handle handle, | ||
924 | struct acpi_bridge *pab, /* parent */ | ||
925 | ulong adr | ||
926 | ) | ||
927 | { | ||
928 | struct acpi_bridge *ab; | ||
929 | struct pci_dev *pdev; | ||
930 | ulong devnum, funcnum; | ||
931 | u8 *path_name = acpi_path_name(handle); | ||
932 | |||
933 | ab = (struct acpi_bridge *) kmalloc (sizeof(struct acpi_bridge), GFP_KERNEL); | ||
934 | if (!ab) { | ||
935 | err("acpi_shpchprm: alloc for ab fail\n"); | ||
936 | return NULL; | ||
937 | } | ||
938 | memset(ab, 0, sizeof(struct acpi_bridge)); | ||
939 | |||
940 | devnum = (adr >> 16) & 0xffff; | ||
941 | funcnum = adr & 0xffff; | ||
942 | |||
943 | pdev = pci_find_slot(pab->bus, PCI_DEVFN(devnum, funcnum)); | ||
944 | if (!pdev || !pdev->subordinate) { | ||
945 | err("acpi_shpchprm:%s is not a P2P Bridge\n", path_name); | ||
946 | kfree(ab); | ||
947 | return NULL; | ||
948 | } | ||
949 | |||
950 | ab->handle = handle; | ||
951 | ab->seg = pab->seg; | ||
952 | ab->pbus = pab->bus; /* or pdev->bus->number */ | ||
953 | ab->pdevice = devnum; /* or PCI_SLOT(pdev->devfn) */ | ||
954 | ab->pfunction = funcnum; /* or PCI_FUNC(pdev->devfn) */ | ||
955 | ab->bus = pdev->subordinate->number; | ||
956 | ab->scanned = 0; | ||
957 | ab->type = BRIDGE_TYPE_P2P; | ||
958 | |||
959 | dbg("acpi_shpchprm: P2P(%x-%x) on pci=b:d:f(%x:%x:%x) acpi=b:d:f(%x:%x:%x) [%s]\n", | ||
960 | pab->bus, ab->bus, pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), | ||
961 | pab->bus, (u32)devnum, (u32)funcnum, path_name); | ||
962 | |||
963 | build_a_bridge(pab, ab); | ||
964 | |||
965 | return ab; | ||
966 | } | ||
967 | |||
968 | static acpi_status scan_p2p_bridge( | ||
969 | acpi_handle handle, | ||
970 | u32 Level, | ||
971 | void *context, | ||
972 | void **retval | ||
973 | ) | ||
974 | { | ||
975 | struct acpi_bridge *pab = (struct acpi_bridge *)context; | ||
976 | struct acpi_bridge *ab; | ||
977 | acpi_status status; | ||
978 | ulong adr = 0; | ||
979 | u8 *path_name = acpi_path_name(handle); | ||
980 | ulong devnum, funcnum; | ||
981 | struct pci_dev *pdev; | ||
982 | |||
983 | /* get device, function */ | ||
984 | status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr); | ||
985 | if (ACPI_FAILURE(status)) { | ||
986 | if (status != AE_NOT_FOUND) | ||
987 | err("acpi_shpchprm:%s _ADR fail=0x%x\n", path_name, status); | ||
988 | return AE_OK; | ||
989 | } | ||
990 | |||
991 | devnum = (adr >> 16) & 0xffff; | ||
992 | funcnum = adr & 0xffff; | ||
993 | |||
994 | pdev = pci_find_slot(pab->bus, PCI_DEVFN(devnum, funcnum)); | ||
995 | if (!pdev) | ||
996 | return AE_OK; | ||
997 | if (!pdev->subordinate) | ||
998 | return AE_OK; | ||
999 | |||
1000 | ab = add_p2p_bridge(handle, pab, adr); | ||
1001 | if (ab) { | ||
1002 | status = acpi_walk_namespace ( ACPI_TYPE_DEVICE, | ||
1003 | handle, | ||
1004 | (u32)1, | ||
1005 | scan_p2p_bridge, | ||
1006 | ab, | ||
1007 | NULL); | ||
1008 | if (ACPI_FAILURE(status)) | ||
1009 | dbg("acpi_shpchprm:%s find_p2p fail=0x%x\n", path_name, status); | ||
1010 | } | ||
1011 | |||
1012 | return AE_OK; | ||
1013 | } | ||
1014 | |||
1015 | static struct acpi_bridge * add_host_bridge( | ||
1016 | acpi_handle handle, | ||
1017 | ulong segnum, | ||
1018 | ulong busnum | ||
1019 | ) | ||
1020 | { | ||
1021 | ulong adr = 0; | ||
1022 | acpi_status status; | ||
1023 | struct acpi_bridge *ab; | ||
1024 | u8 *path_name = acpi_path_name(handle); | ||
1025 | |||
1026 | /* get device, function: host br adr is always 0000 though. */ | ||
1027 | status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr); | ||
1028 | if (ACPI_FAILURE(status)) { | ||
1029 | err("acpi_shpchprm:%s _ADR fail=0x%x\n", path_name, status); | ||
1030 | return NULL; | ||
1031 | } | ||
1032 | dbg("acpi_shpchprm: ROOT PCI seg(0x%x)bus(0x%x)dev(0x%x)func(0x%x) [%s]\n", (u32)segnum, (u32)busnum, | ||
1033 | (u32)(adr >> 16) & 0xffff, (u32)adr & 0xffff, path_name); | ||
1034 | |||
1035 | ab = (struct acpi_bridge *) kmalloc (sizeof(struct acpi_bridge), GFP_KERNEL); | ||
1036 | if (!ab) { | ||
1037 | err("acpi_shpchprm: alloc for ab fail\n"); | ||
1038 | return NULL; | ||
1039 | } | ||
1040 | memset(ab, 0, sizeof(struct acpi_bridge)); | ||
1041 | |||
1042 | ab->handle = handle; | ||
1043 | ab->seg = (int)segnum; | ||
1044 | ab->bus = ab->pbus = (int)busnum; | ||
1045 | ab->pdevice = (int)(adr >> 16) & 0xffff; | ||
1046 | ab->pfunction = (int)(adr & 0xffff); | ||
1047 | ab->scanned = 0; | ||
1048 | ab->type = BRIDGE_TYPE_HOST; | ||
1049 | |||
1050 | /* get root pci bridge's current resources */ | ||
1051 | status = acpi_get_crs(ab); | ||
1052 | if (ACPI_FAILURE(status)) { | ||
1053 | err("acpi_shpchprm:%s evaluate _CRS fail=0x%x\n", path_name, status); | ||
1054 | kfree(ab); | ||
1055 | return NULL; | ||
1056 | } | ||
1057 | build_a_bridge(ab, ab); | ||
1058 | |||
1059 | return ab; | ||
1060 | } | ||
1061 | |||
1062 | static acpi_status acpi_scan_from_root_pci_callback ( | ||
1063 | acpi_handle handle, | ||
1064 | u32 Level, | ||
1065 | void *context, | ||
1066 | void **retval | ||
1067 | ) | ||
1068 | { | ||
1069 | ulong segnum = 0; | ||
1070 | ulong busnum = 0; | ||
1071 | acpi_status status; | ||
1072 | struct acpi_bridge *ab; | ||
1073 | u8 *path_name = acpi_path_name(handle); | ||
1074 | |||
1075 | /* get bus number of this pci root bridge */ | ||
1076 | status = acpi_evaluate_integer(handle, METHOD_NAME__SEG, NULL, &segnum); | ||
1077 | if (ACPI_FAILURE(status)) { | ||
1078 | if (status != AE_NOT_FOUND) { | ||
1079 | err("acpi_shpchprm:%s evaluate _SEG fail=0x%x\n", path_name, status); | ||
1080 | return status; | ||
1081 | } | ||
1082 | segnum = 0; | ||
1083 | } | ||
1084 | |||
1085 | /* get bus number of this pci root bridge */ | ||
1086 | status = acpi_evaluate_integer(handle, METHOD_NAME__BBN, NULL, &busnum); | ||
1087 | if (ACPI_FAILURE(status)) { | ||
1088 | err("acpi_shpchprm:%s evaluate _BBN fail=0x%x\n", path_name, status); | ||
1089 | return (status); | ||
1090 | } | ||
1091 | |||
1092 | ab = add_host_bridge(handle, segnum, busnum); | ||
1093 | if (ab) { | ||
1094 | status = acpi_walk_namespace ( ACPI_TYPE_DEVICE, | ||
1095 | handle, | ||
1096 | 1, | ||
1097 | scan_p2p_bridge, | ||
1098 | ab, | ||
1099 | NULL); | ||
1100 | if (ACPI_FAILURE(status)) | ||
1101 | dbg("acpi_shpchprm:%s find_p2p fail=0x%x\n", path_name, status); | ||
1102 | } | ||
1103 | |||
1104 | return AE_OK; | ||
1105 | } | ||
1106 | |||
1107 | static int shpchprm_acpi_scan_pci (void) | ||
1108 | { | ||
1109 | acpi_status status; | ||
1110 | |||
1111 | /* | ||
1112 | * TBD: traverse LDM device tree with the help of | ||
1113 | * unified ACPI augmented for php device population. | ||
1114 | */ | ||
1115 | status = acpi_get_devices ( PCI_ROOT_HID_STRING, | ||
1116 | acpi_scan_from_root_pci_callback, | ||
1117 | NULL, | ||
1118 | NULL ); | ||
1119 | if (ACPI_FAILURE(status)) { | ||
1120 | err("acpi_shpchprm:get_device PCI ROOT HID fail=0x%x\n", status); | ||
1121 | return -1; | ||
1122 | } | ||
1123 | |||
1124 | return 0; | ||
1125 | } | ||
1126 | |||
1127 | int shpchprm_init(enum php_ctlr_type ctlr_type) | ||
1128 | { | ||
1129 | int rc; | ||
1130 | |||
1131 | if (ctlr_type != PCI) | ||
1132 | return -ENODEV; | ||
1133 | |||
1134 | dbg("shpchprm ACPI init <enter>\n"); | ||
1135 | acpi_bridges_head = NULL; | ||
1136 | |||
1137 | /* construct PCI bus:device tree of acpi_handles */ | ||
1138 | rc = shpchprm_acpi_scan_pci(); | ||
1139 | if (rc) | ||
1140 | return rc; | ||
1141 | |||
1142 | dbg("shpchprm ACPI init %s\n", (rc)?"fail":"success"); | ||
1143 | return rc; | ||
1144 | } | ||
1145 | |||
1146 | static void free_a_slot(struct acpi_php_slot *aps) | ||
1147 | { | ||
1148 | dbg(" free a php func of slot(0x%02x) on PCI b:d:f=0x%02x:%02x:%02x\n", aps->sun, aps->bus, aps->dev, aps->fun); | ||
1149 | |||
1150 | free_pci_resource (aps->io_head); | ||
1151 | free_pci_resource (aps->bus_head); | ||
1152 | free_pci_resource (aps->mem_head); | ||
1153 | free_pci_resource (aps->p_mem_head); | ||
1154 | |||
1155 | kfree(aps); | ||
1156 | } | ||
1157 | |||
1158 | static void free_a_bridge( struct acpi_bridge *ab) | ||
1159 | { | ||
1160 | struct acpi_php_slot *aps, *next; | ||
1161 | |||
1162 | switch (ab->type) { | ||
1163 | case BRIDGE_TYPE_HOST: | ||
1164 | dbg("Free ACPI PCI HOST Bridge(%x) [%s] on s:b:d:f(%x:%x:%x:%x)\n", | ||
1165 | ab->bus, acpi_path_name(ab->handle), ab->seg, ab->pbus, ab->pdevice, ab->pfunction); | ||
1166 | break; | ||
1167 | case BRIDGE_TYPE_P2P: | ||
1168 | dbg("Free ACPI PCI P2P Bridge(%x-%x) [%s] on s:b:d:f(%x:%x:%x:%x)\n", | ||
1169 | ab->pbus, ab->bus, acpi_path_name(ab->handle), ab->seg, ab->pbus, ab->pdevice, ab->pfunction); | ||
1170 | break; | ||
1171 | }; | ||
1172 | |||
1173 | /* free slots first */ | ||
1174 | for (aps = ab->slots; aps; aps = next) { | ||
1175 | next = aps->next; | ||
1176 | free_a_slot(aps); | ||
1177 | } | ||
1178 | |||
1179 | free_pci_resource (ab->io_head); | ||
1180 | free_pci_resource (ab->tio_head); | ||
1181 | free_pci_resource (ab->bus_head); | ||
1182 | free_pci_resource (ab->tbus_head); | ||
1183 | free_pci_resource (ab->mem_head); | ||
1184 | free_pci_resource (ab->tmem_head); | ||
1185 | free_pci_resource (ab->p_mem_head); | ||
1186 | free_pci_resource (ab->tp_mem_head); | ||
1187 | |||
1188 | kfree(ab); | ||
1189 | } | ||
1190 | |||
1191 | static void shpchprm_free_bridges ( struct acpi_bridge *ab) | ||
1192 | { | ||
1193 | if (!ab) | ||
1194 | return; | ||
1195 | |||
1196 | if (ab->child) | ||
1197 | shpchprm_free_bridges (ab->child); | ||
1198 | |||
1199 | if (ab->next) | ||
1200 | shpchprm_free_bridges (ab->next); | ||
1201 | |||
1202 | free_a_bridge(ab); | ||
1203 | } | ||
1204 | |||
1205 | void shpchprm_cleanup(void) | ||
1206 | { | ||
1207 | shpchprm_free_bridges (acpi_bridges_head); | ||
1208 | } | ||
1209 | |||
1210 | static int get_number_of_slots ( | ||
1211 | struct acpi_bridge *ab, | ||
1212 | int selfonly | ||
1213 | ) | ||
1214 | { | ||
1215 | struct acpi_php_slot *aps; | ||
1216 | int prev_slot = -1; | ||
1217 | int slot_num = 0; | ||
1218 | |||
1219 | for ( aps = ab->slots; aps; aps = aps->next) | ||
1220 | if (aps->dev != prev_slot) { | ||
1221 | prev_slot = aps->dev; | ||
1222 | slot_num++; | ||
1223 | } | ||
1224 | |||
1225 | if (ab->child) | ||
1226 | slot_num += get_number_of_slots (ab->child, 0); | ||
1227 | |||
1228 | if (selfonly) | ||
1229 | return slot_num; | ||
1230 | |||
1231 | if (ab->next) | ||
1232 | slot_num += get_number_of_slots (ab->next, 0); | ||
1233 | |||
1234 | return slot_num; | ||
1235 | } | ||
1236 | |||
1237 | static int print_acpi_resources (struct acpi_bridge *ab) | ||
1238 | { | ||
1239 | struct acpi_php_slot *aps; | ||
1240 | int i; | ||
1241 | |||
1242 | switch (ab->type) { | ||
1243 | case BRIDGE_TYPE_HOST: | ||
1244 | dbg("PCI HOST Bridge (%x) [%s]\n", ab->bus, acpi_path_name(ab->handle)); | ||
1245 | break; | ||
1246 | case BRIDGE_TYPE_P2P: | ||
1247 | dbg("PCI P2P Bridge (%x-%x) [%s]\n", ab->pbus, ab->bus, acpi_path_name(ab->handle)); | ||
1248 | break; | ||
1249 | }; | ||
1250 | |||
1251 | print_pci_resources (ab); | ||
1252 | |||
1253 | for ( i = -1, aps = ab->slots; aps; aps = aps->next) { | ||
1254 | if (aps->dev == i) | ||
1255 | continue; | ||
1256 | dbg(" Slot sun(%x) s:b:d:f(%02x:%02x:%02x:%02x)\n", aps->sun, aps->seg, aps->bus, aps->dev, aps->fun); | ||
1257 | print_slot_resources(aps); | ||
1258 | i = aps->dev; | ||
1259 | } | ||
1260 | |||
1261 | if (ab->child) | ||
1262 | print_acpi_resources (ab->child); | ||
1263 | |||
1264 | if (ab->next) | ||
1265 | print_acpi_resources (ab->next); | ||
1266 | |||
1267 | return 0; | ||
1268 | } | ||
1269 | |||
1270 | int shpchprm_print_pirt(void) | ||
1271 | { | ||
1272 | dbg("SHPCHPRM ACPI Slots\n"); | ||
1273 | if (acpi_bridges_head) | ||
1274 | print_acpi_resources (acpi_bridges_head); | ||
1275 | return 0; | ||
1276 | } | ||
1277 | |||
1278 | static struct acpi_php_slot * get_acpi_slot ( | ||
1279 | struct acpi_bridge *ab, | ||
1280 | u32 sun | ||
1281 | ) | ||
1282 | { | ||
1283 | struct acpi_php_slot *aps = NULL; | ||
1284 | |||
1285 | for ( aps = ab->slots; aps; aps = aps->next) | ||
1286 | if (aps->sun == sun) | ||
1287 | return aps; | ||
1288 | |||
1289 | if (!aps && ab->child) { | ||
1290 | aps = (struct acpi_php_slot *)get_acpi_slot (ab->child, sun); | ||
1291 | if (aps) | ||
1292 | return aps; | ||
1293 | } | ||
1294 | |||
1295 | if (!aps && ab->next) { | ||
1296 | aps = (struct acpi_php_slot *)get_acpi_slot (ab->next, sun); | ||
1297 | if (aps) | ||
1298 | return aps; | ||
1299 | } | ||
1300 | |||
1301 | return aps; | ||
1302 | |||
1303 | } | ||
1304 | |||
1305 | #if 0 | ||
1306 | static void * shpchprm_get_slot(struct slot *slot) | ||
1307 | { | ||
1308 | struct acpi_bridge *ab = acpi_bridges_head; | ||
1309 | struct acpi_php_slot *aps = get_acpi_slot (ab, slot->number); | ||
1310 | |||
1311 | aps->slot = slot; | ||
1312 | |||
1313 | dbg("Got acpi slot sun(%x): s:b:d:f(%x:%x:%x:%x)\n", aps->sun, aps->seg, aps->bus, aps->dev, aps->fun); | ||
1314 | |||
1315 | return (void *)aps; | ||
1316 | } | ||
1317 | #endif | ||
1318 | |||
1319 | static void shpchprm_dump_func_res( struct pci_func *fun) | ||
1320 | { | ||
1321 | struct pci_func *func = fun; | ||
1322 | |||
1323 | if (func->bus_head) { | ||
1324 | dbg(": BUS Resources:\n"); | ||
1325 | print_pci_resource (func->bus_head); | ||
1326 | } | ||
1327 | if (func->io_head) { | ||
1328 | dbg(": IO Resources:\n"); | ||
1329 | print_pci_resource (func->io_head); | ||
1330 | } | ||
1331 | if (func->mem_head) { | ||
1332 | dbg(": MEM Resources:\n"); | ||
1333 | print_pci_resource (func->mem_head); | ||
1334 | } | ||
1335 | if (func->p_mem_head) { | ||
1336 | dbg(": PMEM Resources:\n"); | ||
1337 | print_pci_resource (func->p_mem_head); | ||
1338 | } | ||
1339 | } | ||
1340 | |||
1341 | static void shpchprm_dump_ctrl_res( struct controller *ctlr) | ||
1342 | { | ||
1343 | struct controller *ctrl = ctlr; | ||
1344 | |||
1345 | if (ctrl->bus_head) { | ||
1346 | dbg(": BUS Resources:\n"); | ||
1347 | print_pci_resource (ctrl->bus_head); | ||
1348 | } | ||
1349 | if (ctrl->io_head) { | ||
1350 | dbg(": IO Resources:\n"); | ||
1351 | print_pci_resource (ctrl->io_head); | ||
1352 | } | ||
1353 | if (ctrl->mem_head) { | ||
1354 | dbg(": MEM Resources:\n"); | ||
1355 | print_pci_resource (ctrl->mem_head); | ||
1356 | } | ||
1357 | if (ctrl->p_mem_head) { | ||
1358 | dbg(": PMEM Resources:\n"); | ||
1359 | print_pci_resource (ctrl->p_mem_head); | ||
1360 | } | ||
1361 | } | ||
1362 | |||
1363 | static int shpchprm_get_used_resources ( | ||
1364 | struct controller *ctrl, | ||
1365 | struct pci_func *func | ||
1366 | ) | ||
1367 | { | ||
1368 | return shpchp_save_used_resources (ctrl, func, !DISABLE_CARD); | ||
1369 | } | ||
1370 | |||
1371 | static int configure_existing_function( | ||
1372 | struct controller *ctrl, | ||
1373 | struct pci_func *func | ||
1374 | ) | ||
1375 | { | ||
1376 | int rc; | ||
1377 | |||
1378 | /* see how much resources the func has used. */ | ||
1379 | rc = shpchprm_get_used_resources (ctrl, func); | ||
1380 | |||
1381 | if (!rc) { | ||
1382 | /* subtract the resources used by the func from ctrl resources */ | ||
1383 | rc = shpchprm_delete_resources (&ctrl->bus_head, func->bus_head); | ||
1384 | rc |= shpchprm_delete_resources (&ctrl->io_head, func->io_head); | ||
1385 | rc |= shpchprm_delete_resources (&ctrl->mem_head, func->mem_head); | ||
1386 | rc |= shpchprm_delete_resources (&ctrl->p_mem_head, func->p_mem_head); | ||
1387 | if (rc) | ||
1388 | warn("aCEF: cannot del used resources\n"); | ||
1389 | } else | ||
1390 | err("aCEF: cannot get used resources\n"); | ||
1391 | |||
1392 | return rc; | ||
1393 | } | ||
1394 | |||
1395 | static int bind_pci_resources_to_slots ( struct controller *ctrl) | ||
1396 | { | ||
1397 | struct pci_func *func, new_func; | ||
1398 | int busn = ctrl->slot_bus; | ||
1399 | int devn, funn; | ||
1400 | u32 vid; | ||
1401 | |||
1402 | for (devn = 0; devn < 32; devn++) { | ||
1403 | for (funn = 0; funn < 8; funn++) { | ||
1404 | /* | ||
1405 | if (devn == ctrl->device && funn == ctrl->function) | ||
1406 | continue; | ||
1407 | */ | ||
1408 | /* find out if this entry is for an occupied slot */ | ||
1409 | vid = 0xFFFFFFFF; | ||
1410 | pci_bus_read_config_dword(ctrl->pci_dev->subordinate, PCI_DEVFN(devn, funn), PCI_VENDOR_ID, &vid); | ||
1411 | |||
1412 | if (vid != 0xFFFFFFFF) { | ||
1413 | func = shpchp_slot_find(busn, devn, funn); | ||
1414 | if (!func) { | ||
1415 | memset(&new_func, 0, sizeof(struct pci_func)); | ||
1416 | new_func.bus = busn; | ||
1417 | new_func.device = devn; | ||
1418 | new_func.function = funn; | ||
1419 | new_func.is_a_board = 1; | ||
1420 | configure_existing_function(ctrl, &new_func); | ||
1421 | shpchprm_dump_func_res(&new_func); | ||
1422 | } else { | ||
1423 | configure_existing_function(ctrl, func); | ||
1424 | shpchprm_dump_func_res(func); | ||
1425 | } | ||
1426 | dbg("aCCF:existing PCI 0x%x Func ResourceDump\n", ctrl->bus); | ||
1427 | } | ||
1428 | } | ||
1429 | } | ||
1430 | |||
1431 | return 0; | ||
1432 | } | ||
1433 | |||
1434 | static int bind_pci_resources( | ||
1435 | struct controller *ctrl, | ||
1436 | struct acpi_bridge *ab | ||
1437 | ) | ||
1438 | { | ||
1439 | int status = 0; | ||
1440 | |||
1441 | if (ab->bus_head) { | ||
1442 | dbg("bapr: BUS Resources add on PCI 0x%x\n", ab->bus); | ||
1443 | status = shpchprm_add_resources (&ctrl->bus_head, ab->bus_head); | ||
1444 | if (shpchprm_delete_resources (&ab->bus_head, ctrl->bus_head)) | ||
1445 | warn("bapr: cannot sub BUS Resource on PCI 0x%x\n", ab->bus); | ||
1446 | if (status) { | ||
1447 | err("bapr: BUS Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status); | ||
1448 | return status; | ||
1449 | } | ||
1450 | } else | ||
1451 | info("bapr: No BUS Resource on PCI 0x%x.\n", ab->bus); | ||
1452 | |||
1453 | if (ab->io_head) { | ||
1454 | dbg("bapr: IO Resources add on PCI 0x%x\n", ab->bus); | ||
1455 | status = shpchprm_add_resources (&ctrl->io_head, ab->io_head); | ||
1456 | if (shpchprm_delete_resources (&ab->io_head, ctrl->io_head)) | ||
1457 | warn("bapr: cannot sub IO Resource on PCI 0x%x\n", ab->bus); | ||
1458 | if (status) { | ||
1459 | err("bapr: IO Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status); | ||
1460 | return status; | ||
1461 | } | ||
1462 | } else | ||
1463 | info("bapr: No IO Resource on PCI 0x%x.\n", ab->bus); | ||
1464 | |||
1465 | if (ab->mem_head) { | ||
1466 | dbg("bapr: MEM Resources add on PCI 0x%x\n", ab->bus); | ||
1467 | status = shpchprm_add_resources (&ctrl->mem_head, ab->mem_head); | ||
1468 | if (shpchprm_delete_resources (&ab->mem_head, ctrl->mem_head)) | ||
1469 | warn("bapr: cannot sub MEM Resource on PCI 0x%x\n", ab->bus); | ||
1470 | if (status) { | ||
1471 | err("bapr: MEM Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status); | ||
1472 | return status; | ||
1473 | } | ||
1474 | } else | ||
1475 | info("bapr: No MEM Resource on PCI 0x%x.\n", ab->bus); | ||
1476 | |||
1477 | if (ab->p_mem_head) { | ||
1478 | dbg("bapr: PMEM Resources add on PCI 0x%x\n", ab->bus); | ||
1479 | status = shpchprm_add_resources (&ctrl->p_mem_head, ab->p_mem_head); | ||
1480 | if (shpchprm_delete_resources (&ab->p_mem_head, ctrl->p_mem_head)) | ||
1481 | warn("bapr: cannot sub PMEM Resource on PCI 0x%x\n", ab->bus); | ||
1482 | if (status) { | ||
1483 | err("bapr: PMEM Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status); | ||
1484 | return status; | ||
1485 | } | ||
1486 | } else | ||
1487 | info("bapr: No PMEM Resource on PCI 0x%x.\n", ab->bus); | ||
1488 | |||
1489 | return status; | ||
1490 | } | ||
1491 | |||
1492 | static int no_pci_resources( struct acpi_bridge *ab) | ||
1493 | { | ||
1494 | return !(ab->p_mem_head || ab->mem_head || ab->io_head || ab->bus_head); | ||
1495 | } | ||
1496 | |||
1497 | static int find_pci_bridge_resources ( | ||
1498 | struct controller *ctrl, | ||
1499 | struct acpi_bridge *ab | ||
1500 | ) | ||
1501 | { | ||
1502 | int rc = 0; | ||
1503 | struct pci_func func; | ||
1504 | |||
1505 | memset(&func, 0, sizeof(struct pci_func)); | ||
1506 | |||
1507 | func.bus = ab->pbus; | ||
1508 | func.device = ab->pdevice; | ||
1509 | func.function = ab->pfunction; | ||
1510 | func.is_a_board = 1; | ||
1511 | |||
1512 | /* Get used resources for this PCI bridge */ | ||
1513 | rc = shpchp_save_used_resources (ctrl, &func, !DISABLE_CARD); | ||
1514 | |||
1515 | ab->io_head = func.io_head; | ||
1516 | ab->mem_head = func.mem_head; | ||
1517 | ab->p_mem_head = func.p_mem_head; | ||
1518 | ab->bus_head = func.bus_head; | ||
1519 | if (ab->bus_head) | ||
1520 | shpchprm_delete_resource(&ab->bus_head, ctrl->bus, 1); | ||
1521 | |||
1522 | return rc; | ||
1523 | } | ||
1524 | |||
1525 | static int get_pci_resources_from_bridge( | ||
1526 | struct controller *ctrl, | ||
1527 | struct acpi_bridge *ab | ||
1528 | ) | ||
1529 | { | ||
1530 | int rc = 0; | ||
1531 | |||
1532 | dbg("grfb: Get Resources for PCI 0x%x from actual PCI bridge 0x%x.\n", ctrl->bus, ab->bus); | ||
1533 | |||
1534 | rc = find_pci_bridge_resources (ctrl, ab); | ||
1535 | |||
1536 | shpchp_resource_sort_and_combine(&ab->bus_head); | ||
1537 | shpchp_resource_sort_and_combine(&ab->io_head); | ||
1538 | shpchp_resource_sort_and_combine(&ab->mem_head); | ||
1539 | shpchp_resource_sort_and_combine(&ab->p_mem_head); | ||
1540 | |||
1541 | shpchprm_add_resources (&ab->tbus_head, ab->bus_head); | ||
1542 | shpchprm_add_resources (&ab->tio_head, ab->io_head); | ||
1543 | shpchprm_add_resources (&ab->tmem_head, ab->mem_head); | ||
1544 | shpchprm_add_resources (&ab->tp_mem_head, ab->p_mem_head); | ||
1545 | |||
1546 | return rc; | ||
1547 | } | ||
1548 | |||
1549 | static int get_pci_resources( | ||
1550 | struct controller *ctrl, | ||
1551 | struct acpi_bridge *ab | ||
1552 | ) | ||
1553 | { | ||
1554 | int rc = 0; | ||
1555 | |||
1556 | if (no_pci_resources(ab)) { | ||
1557 | dbg("spbr:PCI 0x%x has no resources. Get parent resources.\n", ab->bus); | ||
1558 | rc = get_pci_resources_from_bridge(ctrl, ab); | ||
1559 | } | ||
1560 | |||
1561 | return rc; | ||
1562 | } | ||
1563 | |||
1564 | int shpchprm_get_physical_slot_number(struct controller *ctrl, u32 *sun, u8 busnum, u8 devnum) | ||
1565 | { | ||
1566 | int offset = devnum - ctrl->slot_device_offset; | ||
1567 | |||
1568 | dbg("%s: ctrl->slot_num_inc %d, offset %d\n", __FUNCTION__, ctrl->slot_num_inc, offset); | ||
1569 | *sun = (u8) (ctrl->first_slot + ctrl->slot_num_inc *offset); | ||
1570 | return 0; | ||
1571 | } | ||
1572 | |||
1573 | /* | ||
1574 | * Get resources for this ctrl. | ||
1575 | * 1. get total resources from ACPI _CRS or bridge (this ctrl) | ||
1576 | * 2. find used resources of existing adapters | ||
1577 | * 3. subtract used resources from total resources | ||
1578 | */ | ||
1579 | int shpchprm_find_available_resources( struct controller *ctrl) | ||
1580 | { | ||
1581 | int rc = 0; | ||
1582 | struct acpi_bridge *ab; | ||
1583 | |||
1584 | ab = find_acpi_bridge_by_bus(acpi_bridges_head, ctrl->seg, ctrl->pci_dev->subordinate->number); | ||
1585 | if (!ab) { | ||
1586 | err("pfar:cannot locate acpi bridge of PCI 0x%x.\n", ctrl->pci_dev->subordinate->number); | ||
1587 | return -1; | ||
1588 | } | ||
1589 | if (no_pci_resources(ab)) { | ||
1590 | rc = get_pci_resources(ctrl, ab); | ||
1591 | if (rc) { | ||
1592 | err("pfar:cannot get pci resources of PCI 0x%x.\n",ctrl->pci_dev->subordinate->number); | ||
1593 | return -1; | ||
1594 | } | ||
1595 | } | ||
1596 | |||
1597 | rc = bind_pci_resources(ctrl, ab); | ||
1598 | dbg("pfar:pre-Bind PCI 0x%x Ctrl Resource Dump\n", ctrl->pci_dev->subordinate->number); | ||
1599 | shpchprm_dump_ctrl_res(ctrl); | ||
1600 | |||
1601 | bind_pci_resources_to_slots (ctrl); | ||
1602 | |||
1603 | dbg("pfar:post-Bind PCI 0x%x Ctrl Resource Dump\n", ctrl->pci_dev->subordinate->number); | ||
1604 | shpchprm_dump_ctrl_res(ctrl); | ||
1605 | |||
1606 | return rc; | ||
1607 | } | ||
1608 | |||
1609 | int shpchprm_set_hpp( | ||
1610 | struct controller *ctrl, | ||
1611 | struct pci_func *func, | ||
1612 | u8 card_type | ||
1613 | ) | ||
1614 | { | ||
1615 | struct acpi_bridge *ab; | ||
1616 | struct pci_bus lpci_bus, *pci_bus; | ||
1617 | int rc = 0; | ||
1618 | unsigned int devfn; | ||
1619 | u8 cls= 0x08; /* default cache line size */ | ||
1620 | u8 lt = 0x40; /* default latency timer */ | ||
1621 | u8 ep = 0; | ||
1622 | u8 es = 0; | ||
1623 | |||
1624 | memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus)); | ||
1625 | pci_bus = &lpci_bus; | ||
1626 | pci_bus->number = func->bus; | ||
1627 | devfn = PCI_DEVFN(func->device, func->function); | ||
1628 | |||
1629 | ab = find_acpi_bridge_by_bus(acpi_bridges_head, ctrl->seg, ctrl->bus); | ||
1630 | |||
1631 | if (ab) { | ||
1632 | if (ab->_hpp) { | ||
1633 | lt = (u8)ab->_hpp->latency_timer; | ||
1634 | cls = (u8)ab->_hpp->cache_line_size; | ||
1635 | ep = (u8)ab->_hpp->enable_perr; | ||
1636 | es = (u8)ab->_hpp->enable_serr; | ||
1637 | } else | ||
1638 | dbg("_hpp: no _hpp for B/D/F=%#x/%#x/%#x. use default value\n", func->bus, func->device, func->function); | ||
1639 | } else | ||
1640 | dbg("_hpp: no acpi bridge for B/D/F = %#x/%#x/%#x. use default value\n", func->bus, func->device, func->function); | ||
1641 | |||
1642 | |||
1643 | if (card_type == PCI_HEADER_TYPE_BRIDGE) { | ||
1644 | /* set subordinate Latency Timer */ | ||
1645 | rc |= pci_bus_write_config_byte(pci_bus, devfn, PCI_SEC_LATENCY_TIMER, lt); | ||
1646 | } | ||
1647 | |||
1648 | /* set base Latency Timer */ | ||
1649 | rc |= pci_bus_write_config_byte(pci_bus, devfn, PCI_LATENCY_TIMER, lt); | ||
1650 | dbg(" set latency timer =0x%02x: %x\n", lt, rc); | ||
1651 | |||
1652 | rc |= pci_bus_write_config_byte(pci_bus, devfn, PCI_CACHE_LINE_SIZE, cls); | ||
1653 | dbg(" set cache_line_size=0x%02x: %x\n", cls, rc); | ||
1654 | |||
1655 | return rc; | ||
1656 | } | ||
1657 | |||
1658 | void shpchprm_enable_card( | ||
1659 | struct controller *ctrl, | ||
1660 | struct pci_func *func, | ||
1661 | u8 card_type) | ||
1662 | { | ||
1663 | u16 command, cmd, bcommand, bcmd; | ||
1664 | struct pci_bus lpci_bus, *pci_bus; | ||
1665 | struct acpi_bridge *ab; | ||
1666 | unsigned int devfn; | ||
1667 | int rc; | ||
1668 | |||
1669 | memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus)); | ||
1670 | pci_bus = &lpci_bus; | ||
1671 | pci_bus->number = func->bus; | ||
1672 | devfn = PCI_DEVFN(func->device, func->function); | ||
1673 | |||
1674 | rc = pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &command); | ||
1675 | |||
1676 | if (card_type == PCI_HEADER_TYPE_BRIDGE) { | ||
1677 | rc = pci_bus_read_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, &bcommand); | ||
1678 | } | ||
1679 | |||
1680 | cmd = command = command | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE | ||
1681 | | PCI_COMMAND_IO | PCI_COMMAND_MEMORY; | ||
1682 | bcmd = bcommand = bcommand | PCI_BRIDGE_CTL_NO_ISA; | ||
1683 | |||
1684 | ab = find_acpi_bridge_by_bus(acpi_bridges_head, ctrl->seg, ctrl->bus); | ||
1685 | if (ab) { | ||
1686 | if (ab->_hpp) { | ||
1687 | if (ab->_hpp->enable_perr) { | ||
1688 | command |= PCI_COMMAND_PARITY; | ||
1689 | bcommand |= PCI_BRIDGE_CTL_PARITY; | ||
1690 | } else { | ||
1691 | command &= ~PCI_COMMAND_PARITY; | ||
1692 | bcommand &= ~PCI_BRIDGE_CTL_PARITY; | ||
1693 | } | ||
1694 | if (ab->_hpp->enable_serr) { | ||
1695 | command |= PCI_COMMAND_SERR; | ||
1696 | bcommand |= PCI_BRIDGE_CTL_SERR; | ||
1697 | } else { | ||
1698 | command &= ~PCI_COMMAND_SERR; | ||
1699 | bcommand &= ~PCI_BRIDGE_CTL_SERR; | ||
1700 | } | ||
1701 | } else | ||
1702 | dbg("no _hpp for B/D/F = %#x/%#x/%#x.\n", func->bus, func->device, func->function); | ||
1703 | } else | ||
1704 | dbg("no acpi bridge for B/D/F = %#x/%#x/%#x.\n", func->bus, func->device, func->function); | ||
1705 | |||
1706 | if (command != cmd) { | ||
1707 | rc = pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command); | ||
1708 | } | ||
1709 | if ((card_type == PCI_HEADER_TYPE_BRIDGE) && (bcommand != bcmd)) { | ||
1710 | rc = pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, bcommand); | ||
1711 | } | ||
1712 | } | ||
1713 | |||
diff --git a/drivers/pci/hotplug/shpchprm_legacy.c b/drivers/pci/hotplug/shpchprm_legacy.c new file mode 100644 index 000000000000..37fa77a98289 --- /dev/null +++ b/drivers/pci/hotplug/shpchprm_legacy.c | |||
@@ -0,0 +1,439 @@ | |||
1 | /* | ||
2 | * SHPCHPRM Legacy: PHP Resource Manager for Non-ACPI/Legacy platform | ||
3 | * | ||
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | ||
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * Copyright (C) 2001 IBM Corp. | ||
7 | * Copyright (C) 2003-2004 Intel Corporation | ||
8 | * | ||
9 | * All rights reserved. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or (at | ||
14 | * your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
19 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
20 | * details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
25 | * | ||
26 | * Send feedback to <greg@kroah.com>,<dely.l.sy@intel.com> | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | #include <linux/config.h> | ||
31 | #include <linux/module.h> | ||
32 | #include <linux/kernel.h> | ||
33 | #include <linux/types.h> | ||
34 | #include <linux/pci.h> | ||
35 | #include <linux/init.h> | ||
36 | #include <asm/uaccess.h> | ||
37 | #ifdef CONFIG_IA64 | ||
38 | #include <asm/iosapic.h> | ||
39 | #endif | ||
40 | #include "shpchp.h" | ||
41 | #include "shpchprm.h" | ||
42 | #include "shpchprm_legacy.h" | ||
43 | |||
44 | static void __iomem *shpchp_rom_start; | ||
45 | static u16 unused_IRQ; | ||
46 | |||
47 | void shpchprm_cleanup(void) | ||
48 | { | ||
49 | if (shpchp_rom_start) | ||
50 | iounmap(shpchp_rom_start); | ||
51 | } | ||
52 | |||
53 | int shpchprm_print_pirt(void) | ||
54 | { | ||
55 | return 0; | ||
56 | } | ||
57 | |||
58 | int shpchprm_get_physical_slot_number(struct controller *ctrl, u32 *sun, u8 busnum, u8 devnum) | ||
59 | { | ||
60 | int offset = devnum - ctrl->slot_device_offset; | ||
61 | |||
62 | *sun = (u8) (ctrl->first_slot + ctrl->slot_num_inc * offset); | ||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | /* Find the Hot Plug Resource Table in the specified region of memory */ | ||
67 | static void __iomem *detect_HRT_floating_pointer(void __iomem *begin, void __iomem *end) | ||
68 | { | ||
69 | void __iomem *fp; | ||
70 | void __iomem *endp; | ||
71 | u8 temp1, temp2, temp3, temp4; | ||
72 | int status = 0; | ||
73 | |||
74 | endp = (end - sizeof(struct hrt) + 1); | ||
75 | |||
76 | for (fp = begin; fp <= endp; fp += 16) { | ||
77 | temp1 = readb(fp + SIG0); | ||
78 | temp2 = readb(fp + SIG1); | ||
79 | temp3 = readb(fp + SIG2); | ||
80 | temp4 = readb(fp + SIG3); | ||
81 | if (temp1 == '$' && temp2 == 'H' && temp3 == 'R' && temp4 == 'T') { | ||
82 | status = 1; | ||
83 | break; | ||
84 | } | ||
85 | } | ||
86 | |||
87 | if (!status) | ||
88 | fp = NULL; | ||
89 | |||
90 | dbg("Discovered Hotplug Resource Table at %p\n", fp); | ||
91 | return fp; | ||
92 | } | ||
93 | |||
94 | /* | ||
95 | * shpchprm_find_available_resources | ||
96 | * | ||
97 | * Finds available memory, IO, and IRQ resources for programming | ||
98 | * devices which may be added to the system | ||
99 | * this function is for hot plug ADD! | ||
100 | * | ||
101 | * returns 0 if success | ||
102 | */ | ||
103 | int shpchprm_find_available_resources(struct controller *ctrl) | ||
104 | { | ||
105 | u8 populated_slot; | ||
106 | u8 bridged_slot; | ||
107 | void __iomem *one_slot; | ||
108 | struct pci_func *func = NULL; | ||
109 | int i = 10, index = 0; | ||
110 | u32 temp_dword, rc; | ||
111 | ulong temp_ulong; | ||
112 | struct pci_resource *mem_node; | ||
113 | struct pci_resource *p_mem_node; | ||
114 | struct pci_resource *io_node; | ||
115 | struct pci_resource *bus_node; | ||
116 | void __iomem *rom_resource_table; | ||
117 | struct pci_bus lpci_bus, *pci_bus; | ||
118 | u8 cfgspc_irq, temp; | ||
119 | |||
120 | memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus)); | ||
121 | pci_bus = &lpci_bus; | ||
122 | rom_resource_table = detect_HRT_floating_pointer(shpchp_rom_start, shpchp_rom_start + 0xffff); | ||
123 | dbg("rom_resource_table = %p\n", rom_resource_table); | ||
124 | if (rom_resource_table == NULL) | ||
125 | return -ENODEV; | ||
126 | |||
127 | /* Sum all resources and setup resource maps */ | ||
128 | unused_IRQ = readl(rom_resource_table + UNUSED_IRQ); | ||
129 | dbg("unused_IRQ = %x\n", unused_IRQ); | ||
130 | |||
131 | temp = 0; | ||
132 | while (unused_IRQ) { | ||
133 | if (unused_IRQ & 1) { | ||
134 | shpchp_disk_irq = temp; | ||
135 | break; | ||
136 | } | ||
137 | unused_IRQ = unused_IRQ >> 1; | ||
138 | temp++; | ||
139 | } | ||
140 | |||
141 | dbg("shpchp_disk_irq= %d\n", shpchp_disk_irq); | ||
142 | unused_IRQ = unused_IRQ >> 1; | ||
143 | temp++; | ||
144 | |||
145 | while (unused_IRQ) { | ||
146 | if (unused_IRQ & 1) { | ||
147 | shpchp_nic_irq = temp; | ||
148 | break; | ||
149 | } | ||
150 | unused_IRQ = unused_IRQ >> 1; | ||
151 | temp++; | ||
152 | } | ||
153 | |||
154 | dbg("shpchp_nic_irq= %d\n", shpchp_nic_irq); | ||
155 | unused_IRQ = readl(rom_resource_table + PCIIRQ); | ||
156 | |||
157 | temp = 0; | ||
158 | |||
159 | pci_read_config_byte(ctrl->pci_dev, PCI_INTERRUPT_LINE, &cfgspc_irq); | ||
160 | |||
161 | if (!shpchp_nic_irq) { | ||
162 | shpchp_nic_irq = cfgspc_irq; | ||
163 | } | ||
164 | |||
165 | if (!shpchp_disk_irq) { | ||
166 | shpchp_disk_irq = cfgspc_irq; | ||
167 | } | ||
168 | |||
169 | dbg("shpchp_disk_irq, shpchp_nic_irq= %d, %d\n", shpchp_disk_irq, shpchp_nic_irq); | ||
170 | |||
171 | one_slot = rom_resource_table + sizeof(struct hrt); | ||
172 | |||
173 | i = readb(rom_resource_table + NUMBER_OF_ENTRIES); | ||
174 | dbg("number_of_entries = %d\n", i); | ||
175 | |||
176 | if (!readb(one_slot + SECONDARY_BUS)) | ||
177 | return (1); | ||
178 | |||
179 | dbg("dev|IO base|length|MEMbase|length|PM base|length|PB SB MB\n"); | ||
180 | |||
181 | while (i && readb(one_slot + SECONDARY_BUS)) { | ||
182 | u8 dev_func = readb(one_slot + DEV_FUNC); | ||
183 | u8 primary_bus = readb(one_slot + PRIMARY_BUS); | ||
184 | u8 secondary_bus = readb(one_slot + SECONDARY_BUS); | ||
185 | u8 max_bus = readb(one_slot + MAX_BUS); | ||
186 | u16 io_base = readw(one_slot + IO_BASE); | ||
187 | u16 io_length = readw(one_slot + IO_LENGTH); | ||
188 | u16 mem_base = readw(one_slot + MEM_BASE); | ||
189 | u16 mem_length = readw(one_slot + MEM_LENGTH); | ||
190 | u16 pre_mem_base = readw(one_slot + PRE_MEM_BASE); | ||
191 | u16 pre_mem_length = readw(one_slot + PRE_MEM_LENGTH); | ||
192 | |||
193 | dbg("%2.2x | %4.4x | %4.4x | %4.4x | %4.4x | %4.4x | %4.4x |%2.2x %2.2x %2.2x\n", | ||
194 | dev_func, io_base, io_length, mem_base, mem_length, pre_mem_base, pre_mem_length, | ||
195 | primary_bus, secondary_bus, max_bus); | ||
196 | |||
197 | /* If this entry isn't for our controller's bus, ignore it */ | ||
198 | if (primary_bus != ctrl->slot_bus) { | ||
199 | i--; | ||
200 | one_slot += sizeof(struct slot_rt); | ||
201 | continue; | ||
202 | } | ||
203 | /* find out if this entry is for an occupied slot */ | ||
204 | temp_dword = 0xFFFFFFFF; | ||
205 | pci_bus->number = primary_bus; | ||
206 | pci_bus_read_config_dword(pci_bus, dev_func, PCI_VENDOR_ID, &temp_dword); | ||
207 | |||
208 | dbg("temp_D_word = %x\n", temp_dword); | ||
209 | |||
210 | if (temp_dword != 0xFFFFFFFF) { | ||
211 | index = 0; | ||
212 | func = shpchp_slot_find(primary_bus, dev_func >> 3, 0); | ||
213 | |||
214 | while (func && (func->function != (dev_func & 0x07))) { | ||
215 | dbg("func = %p b:d:f(%x:%x:%x)\n", func, primary_bus, dev_func >> 3, index); | ||
216 | func = shpchp_slot_find(primary_bus, dev_func >> 3, index++); | ||
217 | } | ||
218 | |||
219 | /* If we can't find a match, skip this table entry */ | ||
220 | if (!func) { | ||
221 | i--; | ||
222 | one_slot += sizeof(struct slot_rt); | ||
223 | continue; | ||
224 | } | ||
225 | /* this may not work and shouldn't be used */ | ||
226 | if (secondary_bus != primary_bus) | ||
227 | bridged_slot = 1; | ||
228 | else | ||
229 | bridged_slot = 0; | ||
230 | |||
231 | populated_slot = 1; | ||
232 | } else { | ||
233 | populated_slot = 0; | ||
234 | bridged_slot = 0; | ||
235 | } | ||
236 | dbg("slot populated =%s \n", populated_slot?"yes":"no"); | ||
237 | |||
238 | /* If we've got a valid IO base, use it */ | ||
239 | |||
240 | temp_ulong = io_base + io_length; | ||
241 | |||
242 | if ((io_base) && (temp_ulong <= 0x10000)) { | ||
243 | io_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); | ||
244 | if (!io_node) | ||
245 | return -ENOMEM; | ||
246 | |||
247 | io_node->base = (ulong)io_base; | ||
248 | io_node->length = (ulong)io_length; | ||
249 | dbg("found io_node(base, length) = %x, %x\n", io_node->base, io_node->length); | ||
250 | |||
251 | if (!populated_slot) { | ||
252 | io_node->next = ctrl->io_head; | ||
253 | ctrl->io_head = io_node; | ||
254 | } else { | ||
255 | io_node->next = func->io_head; | ||
256 | func->io_head = io_node; | ||
257 | } | ||
258 | } | ||
259 | |||
260 | /* If we've got a valid memory base, use it */ | ||
261 | temp_ulong = mem_base + mem_length; | ||
262 | if ((mem_base) && (temp_ulong <= 0x10000)) { | ||
263 | mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); | ||
264 | if (!mem_node) | ||
265 | return -ENOMEM; | ||
266 | |||
267 | mem_node->base = (ulong)mem_base << 16; | ||
268 | mem_node->length = (ulong)(mem_length << 16); | ||
269 | dbg("found mem_node(base, length) = %x, %x\n", mem_node->base, mem_node->length); | ||
270 | |||
271 | if (!populated_slot) { | ||
272 | mem_node->next = ctrl->mem_head; | ||
273 | ctrl->mem_head = mem_node; | ||
274 | } else { | ||
275 | mem_node->next = func->mem_head; | ||
276 | func->mem_head = mem_node; | ||
277 | } | ||
278 | } | ||
279 | |||
280 | /* | ||
281 | * If we've got a valid prefetchable memory base, and | ||
282 | * the base + length isn't greater than 0xFFFF | ||
283 | */ | ||
284 | temp_ulong = pre_mem_base + pre_mem_length; | ||
285 | if ((pre_mem_base) && (temp_ulong <= 0x10000)) { | ||
286 | p_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); | ||
287 | if (!p_mem_node) | ||
288 | return -ENOMEM; | ||
289 | |||
290 | p_mem_node->base = (ulong)pre_mem_base << 16; | ||
291 | p_mem_node->length = (ulong)pre_mem_length << 16; | ||
292 | dbg("found p_mem_node(base, length) = %x, %x\n", p_mem_node->base, p_mem_node->length); | ||
293 | |||
294 | if (!populated_slot) { | ||
295 | p_mem_node->next = ctrl->p_mem_head; | ||
296 | ctrl->p_mem_head = p_mem_node; | ||
297 | } else { | ||
298 | p_mem_node->next = func->p_mem_head; | ||
299 | func->p_mem_head = p_mem_node; | ||
300 | } | ||
301 | } | ||
302 | |||
303 | /* | ||
304 | * If we've got a valid bus number, use it | ||
305 | * The second condition is to ignore bus numbers on | ||
306 | * populated slots that don't have PCI-PCI bridges | ||
307 | */ | ||
308 | if (secondary_bus && (secondary_bus != primary_bus)) { | ||
309 | bus_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); | ||
310 | if (!bus_node) | ||
311 | return -ENOMEM; | ||
312 | |||
313 | bus_node->base = (ulong)secondary_bus; | ||
314 | bus_node->length = (ulong)(max_bus - secondary_bus + 1); | ||
315 | dbg("found bus_node(base, length) = %x, %x\n", bus_node->base, bus_node->length); | ||
316 | |||
317 | if (!populated_slot) { | ||
318 | bus_node->next = ctrl->bus_head; | ||
319 | ctrl->bus_head = bus_node; | ||
320 | } else { | ||
321 | bus_node->next = func->bus_head; | ||
322 | func->bus_head = bus_node; | ||
323 | } | ||
324 | } | ||
325 | |||
326 | i--; | ||
327 | one_slot += sizeof(struct slot_rt); | ||
328 | } | ||
329 | |||
330 | /* If all of the following fail, we don't have any resources for hot plug add */ | ||
331 | rc = 1; | ||
332 | rc &= shpchp_resource_sort_and_combine(&(ctrl->mem_head)); | ||
333 | rc &= shpchp_resource_sort_and_combine(&(ctrl->p_mem_head)); | ||
334 | rc &= shpchp_resource_sort_and_combine(&(ctrl->io_head)); | ||
335 | rc &= shpchp_resource_sort_and_combine(&(ctrl->bus_head)); | ||
336 | |||
337 | return (rc); | ||
338 | } | ||
339 | |||
340 | int shpchprm_set_hpp( | ||
341 | struct controller *ctrl, | ||
342 | struct pci_func *func, | ||
343 | u8 card_type) | ||
344 | { | ||
345 | u32 rc; | ||
346 | u8 temp_byte; | ||
347 | struct pci_bus lpci_bus, *pci_bus; | ||
348 | unsigned int devfn; | ||
349 | memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus)); | ||
350 | pci_bus = &lpci_bus; | ||
351 | pci_bus->number = func->bus; | ||
352 | devfn = PCI_DEVFN(func->device, func->function); | ||
353 | |||
354 | temp_byte = 0x40; /* hard coded value for LT */ | ||
355 | if (card_type == PCI_HEADER_TYPE_BRIDGE) { | ||
356 | /* set subordinate Latency Timer */ | ||
357 | rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SEC_LATENCY_TIMER, temp_byte); | ||
358 | if (rc) { | ||
359 | dbg("%s: set secondary LT error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, | ||
360 | func->device, func->function); | ||
361 | return rc; | ||
362 | } | ||
363 | } | ||
364 | |||
365 | /* set base Latency Timer */ | ||
366 | rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_LATENCY_TIMER, temp_byte); | ||
367 | if (rc) { | ||
368 | dbg("%s: set LT error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function); | ||
369 | return rc; | ||
370 | } | ||
371 | |||
372 | /* set Cache Line size */ | ||
373 | temp_byte = 0x08; /* hard coded value for CLS */ | ||
374 | rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_CACHE_LINE_SIZE, temp_byte); | ||
375 | if (rc) { | ||
376 | dbg("%s: set CLS error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function); | ||
377 | } | ||
378 | |||
379 | /* set enable_perr */ | ||
380 | /* set enable_serr */ | ||
381 | |||
382 | return rc; | ||
383 | } | ||
384 | |||
385 | void shpchprm_enable_card( | ||
386 | struct controller *ctrl, | ||
387 | struct pci_func *func, | ||
388 | u8 card_type) | ||
389 | { | ||
390 | u16 command, bcommand; | ||
391 | struct pci_bus lpci_bus, *pci_bus; | ||
392 | unsigned int devfn; | ||
393 | int rc; | ||
394 | |||
395 | memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus)); | ||
396 | pci_bus = &lpci_bus; | ||
397 | pci_bus->number = func->bus; | ||
398 | devfn = PCI_DEVFN(func->device, func->function); | ||
399 | |||
400 | rc = pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &command); | ||
401 | command |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR | ||
402 | | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE | ||
403 | | PCI_COMMAND_IO | PCI_COMMAND_MEMORY; | ||
404 | rc = pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command); | ||
405 | |||
406 | if (card_type == PCI_HEADER_TYPE_BRIDGE) { | ||
407 | rc = pci_bus_read_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, &bcommand); | ||
408 | bcommand |= PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR | ||
409 | | PCI_BRIDGE_CTL_NO_ISA; | ||
410 | rc = pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, bcommand); | ||
411 | } | ||
412 | } | ||
413 | |||
414 | static int legacy_shpchprm_init_pci(void) | ||
415 | { | ||
416 | shpchp_rom_start = ioremap(ROM_PHY_ADDR, ROM_PHY_LEN); | ||
417 | if (!shpchp_rom_start) { | ||
418 | err("Could not ioremap memory region for ROM\n"); | ||
419 | return -EIO; | ||
420 | } | ||
421 | |||
422 | return 0; | ||
423 | } | ||
424 | |||
425 | int shpchprm_init(enum php_ctlr_type ctrl_type) | ||
426 | { | ||
427 | int retval; | ||
428 | |||
429 | switch (ctrl_type) { | ||
430 | case PCI: | ||
431 | retval = legacy_shpchprm_init_pci(); | ||
432 | break; | ||
433 | default: | ||
434 | retval = -ENODEV; | ||
435 | break; | ||
436 | } | ||
437 | |||
438 | return retval; | ||
439 | } | ||
diff --git a/drivers/pci/hotplug/shpchprm_legacy.h b/drivers/pci/hotplug/shpchprm_legacy.h new file mode 100644 index 000000000000..29ccea5e57e5 --- /dev/null +++ b/drivers/pci/hotplug/shpchprm_legacy.h | |||
@@ -0,0 +1,113 @@ | |||
1 | /* | ||
2 | * SHPCHPRM Legacy: PHP Resource Manager for Non-ACPI/Legacy platform using HRT | ||
3 | * | ||
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | ||
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * Copyright (C) 2001 IBM Corp. | ||
7 | * Copyright (C) 2003-2004 Intel Corporation | ||
8 | * | ||
9 | * All rights reserved. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or (at | ||
14 | * your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
19 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
20 | * details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
25 | * | ||
26 | * Send feedback to <greg@kroah.com>, <dely.l.sy@intel.com> | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | #ifndef _SHPCHPRM_LEGACY_H_ | ||
31 | #define _SHPCHPRM_LEGACY_H_ | ||
32 | |||
33 | #define ROM_PHY_ADDR 0x0F0000 | ||
34 | #define ROM_PHY_LEN 0x00FFFF | ||
35 | |||
36 | struct slot_rt { | ||
37 | u8 dev_func; | ||
38 | u8 primary_bus; | ||
39 | u8 secondary_bus; | ||
40 | u8 max_bus; | ||
41 | u16 io_base; | ||
42 | u16 io_length; | ||
43 | u16 mem_base; | ||
44 | u16 mem_length; | ||
45 | u16 pre_mem_base; | ||
46 | u16 pre_mem_length; | ||
47 | } __attribute__ ((packed)); | ||
48 | |||
49 | /* offsets to the hotplug slot resource table registers based on the above structure layout */ | ||
50 | enum slot_rt_offsets { | ||
51 | DEV_FUNC = offsetof(struct slot_rt, dev_func), | ||
52 | PRIMARY_BUS = offsetof(struct slot_rt, primary_bus), | ||
53 | SECONDARY_BUS = offsetof(struct slot_rt, secondary_bus), | ||
54 | MAX_BUS = offsetof(struct slot_rt, max_bus), | ||
55 | IO_BASE = offsetof(struct slot_rt, io_base), | ||
56 | IO_LENGTH = offsetof(struct slot_rt, io_length), | ||
57 | MEM_BASE = offsetof(struct slot_rt, mem_base), | ||
58 | MEM_LENGTH = offsetof(struct slot_rt, mem_length), | ||
59 | PRE_MEM_BASE = offsetof(struct slot_rt, pre_mem_base), | ||
60 | PRE_MEM_LENGTH = offsetof(struct slot_rt, pre_mem_length), | ||
61 | }; | ||
62 | |||
63 | struct hrt { | ||
64 | char sig0; | ||
65 | char sig1; | ||
66 | char sig2; | ||
67 | char sig3; | ||
68 | u16 unused_IRQ; | ||
69 | u16 PCIIRQ; | ||
70 | u8 number_of_entries; | ||
71 | u8 revision; | ||
72 | u16 reserved1; | ||
73 | u32 reserved2; | ||
74 | } __attribute__ ((packed)); | ||
75 | |||
76 | /* offsets to the hotplug resource table registers based on the above structure layout */ | ||
77 | enum hrt_offsets { | ||
78 | SIG0 = offsetof(struct hrt, sig0), | ||
79 | SIG1 = offsetof(struct hrt, sig1), | ||
80 | SIG2 = offsetof(struct hrt, sig2), | ||
81 | SIG3 = offsetof(struct hrt, sig3), | ||
82 | UNUSED_IRQ = offsetof(struct hrt, unused_IRQ), | ||
83 | PCIIRQ = offsetof(struct hrt, PCIIRQ), | ||
84 | NUMBER_OF_ENTRIES = offsetof(struct hrt, number_of_entries), | ||
85 | REVISION = offsetof(struct hrt, revision), | ||
86 | HRT_RESERVED1 = offsetof(struct hrt, reserved1), | ||
87 | HRT_RESERVED2 = offsetof(struct hrt, reserved2), | ||
88 | }; | ||
89 | |||
90 | struct irq_info { | ||
91 | u8 bus, devfn; /* bus, device and function */ | ||
92 | struct { | ||
93 | u8 link; /* IRQ line ID, chipset dependent, 0=not routed */ | ||
94 | u16 bitmap; /* Available IRQs */ | ||
95 | } __attribute__ ((packed)) irq[4]; | ||
96 | u8 slot; /* slot number, 0=onboard */ | ||
97 | u8 rfu; | ||
98 | } __attribute__ ((packed)); | ||
99 | |||
100 | struct irq_routing_table { | ||
101 | u32 signature; /* PIRQ_SIGNATURE should be here */ | ||
102 | u16 version; /* PIRQ_VERSION */ | ||
103 | u16 size; /* Table size in bytes */ | ||
104 | u8 rtr_bus, rtr_devfn; /* Where the interrupt router lies */ | ||
105 | u16 exclusive_irqs; /* IRQs devoted exclusively to PCI usage */ | ||
106 | u16 rtr_vendor, rtr_device; /* Vendor and device ID of interrupt router */ | ||
107 | u32 miniport_data; /* Crap */ | ||
108 | u8 rfu[11]; | ||
109 | u8 checksum; /* Modulo 256 checksum must give zero */ | ||
110 | struct irq_info slots[0]; | ||
111 | } __attribute__ ((packed)); | ||
112 | |||
113 | #endif /* _SHPCHPRM_LEGACY_H_ */ | ||
diff --git a/drivers/pci/hotplug/shpchprm_nonacpi.c b/drivers/pci/hotplug/shpchprm_nonacpi.c new file mode 100644 index 000000000000..88f4d9f41886 --- /dev/null +++ b/drivers/pci/hotplug/shpchprm_nonacpi.c | |||
@@ -0,0 +1,434 @@ | |||
1 | /* | ||
2 | * SHPCHPRM NONACPI: PHP Resource Manager for Non-ACPI/Legacy platform | ||
3 | * | ||
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | ||
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * Copyright (C) 2001 IBM Corp. | ||
7 | * Copyright (C) 2003-2004 Intel Corporation | ||
8 | * | ||
9 | * All rights reserved. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or (at | ||
14 | * your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
19 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
20 | * details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
25 | * | ||
26 | * Send feedback to <greg@kroah.com>, <dely.l.sy@intel.com> | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | #include <linux/config.h> | ||
31 | #include <linux/module.h> | ||
32 | #include <linux/kernel.h> | ||
33 | #include <linux/types.h> | ||
34 | #include <linux/pci.h> | ||
35 | #include <linux/init.h> | ||
36 | #include <asm/uaccess.h> | ||
37 | #ifdef CONFIG_IA64 | ||
38 | #include <asm/iosapic.h> | ||
39 | #endif | ||
40 | #include "shpchp.h" | ||
41 | #include "shpchprm.h" | ||
42 | #include "shpchprm_nonacpi.h" | ||
43 | |||
44 | void shpchprm_cleanup(void) | ||
45 | { | ||
46 | return; | ||
47 | } | ||
48 | |||
49 | int shpchprm_print_pirt(void) | ||
50 | { | ||
51 | return 0; | ||
52 | } | ||
53 | |||
54 | int shpchprm_get_physical_slot_number(struct controller *ctrl, u32 *sun, u8 busnum, u8 devnum) | ||
55 | { | ||
56 | int offset = devnum - ctrl->slot_device_offset; | ||
57 | |||
58 | dbg("%s: ctrl->slot_num_inc %d, offset %d\n", __FUNCTION__, ctrl->slot_num_inc, offset); | ||
59 | *sun = (u8) (ctrl->first_slot + ctrl->slot_num_inc * offset); | ||
60 | return 0; | ||
61 | } | ||
62 | |||
63 | static void print_pci_resource ( struct pci_resource *aprh) | ||
64 | { | ||
65 | struct pci_resource *res; | ||
66 | |||
67 | for (res = aprh; res; res = res->next) | ||
68 | dbg(" base= 0x%x length= 0x%x\n", res->base, res->length); | ||
69 | } | ||
70 | |||
71 | |||
72 | static void phprm_dump_func_res( struct pci_func *fun) | ||
73 | { | ||
74 | struct pci_func *func = fun; | ||
75 | |||
76 | if (func->bus_head) { | ||
77 | dbg(": BUS Resources:\n"); | ||
78 | print_pci_resource (func->bus_head); | ||
79 | } | ||
80 | if (func->io_head) { | ||
81 | dbg(": IO Resources:\n"); | ||
82 | print_pci_resource (func->io_head); | ||
83 | } | ||
84 | if (func->mem_head) { | ||
85 | dbg(": MEM Resources:\n"); | ||
86 | print_pci_resource (func->mem_head); | ||
87 | } | ||
88 | if (func->p_mem_head) { | ||
89 | dbg(": PMEM Resources:\n"); | ||
90 | print_pci_resource (func->p_mem_head); | ||
91 | } | ||
92 | } | ||
93 | |||
94 | static int phprm_get_used_resources ( | ||
95 | struct controller *ctrl, | ||
96 | struct pci_func *func | ||
97 | ) | ||
98 | { | ||
99 | return shpchp_save_used_resources (ctrl, func, !DISABLE_CARD); | ||
100 | } | ||
101 | |||
102 | static int phprm_delete_resource( | ||
103 | struct pci_resource **aprh, | ||
104 | ulong base, | ||
105 | ulong size) | ||
106 | { | ||
107 | struct pci_resource *res; | ||
108 | struct pci_resource *prevnode; | ||
109 | struct pci_resource *split_node; | ||
110 | ulong tbase; | ||
111 | |||
112 | shpchp_resource_sort_and_combine(aprh); | ||
113 | |||
114 | for (res = *aprh; res; res = res->next) { | ||
115 | if (res->base > base) | ||
116 | continue; | ||
117 | |||
118 | if ((res->base + res->length) < (base + size)) | ||
119 | continue; | ||
120 | |||
121 | if (res->base < base) { | ||
122 | tbase = base; | ||
123 | |||
124 | if ((res->length - (tbase - res->base)) < size) | ||
125 | continue; | ||
126 | |||
127 | split_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); | ||
128 | if (!split_node) | ||
129 | return -ENOMEM; | ||
130 | |||
131 | split_node->base = res->base; | ||
132 | split_node->length = tbase - res->base; | ||
133 | res->base = tbase; | ||
134 | res->length -= split_node->length; | ||
135 | |||
136 | split_node->next = res->next; | ||
137 | res->next = split_node; | ||
138 | } | ||
139 | |||
140 | if (res->length >= size) { | ||
141 | split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); | ||
142 | if (!split_node) | ||
143 | return -ENOMEM; | ||
144 | |||
145 | split_node->base = res->base + size; | ||
146 | split_node->length = res->length - size; | ||
147 | res->length = size; | ||
148 | |||
149 | split_node->next = res->next; | ||
150 | res->next = split_node; | ||
151 | } | ||
152 | |||
153 | if (*aprh == res) { | ||
154 | *aprh = res->next; | ||
155 | } else { | ||
156 | prevnode = *aprh; | ||
157 | while (prevnode->next != res) | ||
158 | prevnode = prevnode->next; | ||
159 | |||
160 | prevnode->next = res->next; | ||
161 | } | ||
162 | res->next = NULL; | ||
163 | kfree(res); | ||
164 | break; | ||
165 | } | ||
166 | |||
167 | return 0; | ||
168 | } | ||
169 | |||
170 | |||
171 | static int phprm_delete_resources( | ||
172 | struct pci_resource **aprh, | ||
173 | struct pci_resource *this | ||
174 | ) | ||
175 | { | ||
176 | struct pci_resource *res; | ||
177 | |||
178 | for (res = this; res; res = res->next) | ||
179 | phprm_delete_resource(aprh, res->base, res->length); | ||
180 | |||
181 | return 0; | ||
182 | } | ||
183 | |||
184 | |||
185 | static int configure_existing_function( | ||
186 | struct controller *ctrl, | ||
187 | struct pci_func *func | ||
188 | ) | ||
189 | { | ||
190 | int rc; | ||
191 | |||
192 | /* see how much resources the func has used. */ | ||
193 | rc = phprm_get_used_resources (ctrl, func); | ||
194 | |||
195 | if (!rc) { | ||
196 | /* subtract the resources used by the func from ctrl resources */ | ||
197 | rc = phprm_delete_resources (&ctrl->bus_head, func->bus_head); | ||
198 | rc |= phprm_delete_resources (&ctrl->io_head, func->io_head); | ||
199 | rc |= phprm_delete_resources (&ctrl->mem_head, func->mem_head); | ||
200 | rc |= phprm_delete_resources (&ctrl->p_mem_head, func->p_mem_head); | ||
201 | if (rc) | ||
202 | warn("aCEF: cannot del used resources\n"); | ||
203 | } else | ||
204 | err("aCEF: cannot get used resources\n"); | ||
205 | |||
206 | return rc; | ||
207 | } | ||
208 | |||
209 | static int bind_pci_resources_to_slots ( struct controller *ctrl) | ||
210 | { | ||
211 | struct pci_func *func, new_func; | ||
212 | int busn = ctrl->slot_bus; | ||
213 | int devn, funn; | ||
214 | u32 vid; | ||
215 | |||
216 | for (devn = 0; devn < 32; devn++) { | ||
217 | for (funn = 0; funn < 8; funn++) { | ||
218 | /* | ||
219 | if (devn == ctrl->device && funn == ctrl->function) | ||
220 | continue; | ||
221 | */ | ||
222 | /* find out if this entry is for an occupied slot */ | ||
223 | vid = 0xFFFFFFFF; | ||
224 | |||
225 | pci_bus_read_config_dword(ctrl->pci_dev->subordinate, PCI_DEVFN(devn, funn), PCI_VENDOR_ID, &vid); | ||
226 | |||
227 | if (vid != 0xFFFFFFFF) { | ||
228 | func = shpchp_slot_find(busn, devn, funn); | ||
229 | if (!func) { | ||
230 | memset(&new_func, 0, sizeof(struct pci_func)); | ||
231 | new_func.bus = busn; | ||
232 | new_func.device = devn; | ||
233 | new_func.function = funn; | ||
234 | new_func.is_a_board = 1; | ||
235 | configure_existing_function(ctrl, &new_func); | ||
236 | phprm_dump_func_res(&new_func); | ||
237 | } else { | ||
238 | configure_existing_function(ctrl, func); | ||
239 | phprm_dump_func_res(func); | ||
240 | } | ||
241 | dbg("aCCF:existing PCI 0x%x Func ResourceDump\n", ctrl->bus); | ||
242 | } | ||
243 | } | ||
244 | } | ||
245 | |||
246 | return 0; | ||
247 | } | ||
248 | |||
249 | static void phprm_dump_ctrl_res( struct controller *ctlr) | ||
250 | { | ||
251 | struct controller *ctrl = ctlr; | ||
252 | |||
253 | if (ctrl->bus_head) { | ||
254 | dbg(": BUS Resources:\n"); | ||
255 | print_pci_resource (ctrl->bus_head); | ||
256 | } | ||
257 | if (ctrl->io_head) { | ||
258 | dbg(": IO Resources:\n"); | ||
259 | print_pci_resource (ctrl->io_head); | ||
260 | } | ||
261 | if (ctrl->mem_head) { | ||
262 | dbg(": MEM Resources:\n"); | ||
263 | print_pci_resource (ctrl->mem_head); | ||
264 | } | ||
265 | if (ctrl->p_mem_head) { | ||
266 | dbg(": PMEM Resources:\n"); | ||
267 | print_pci_resource (ctrl->p_mem_head); | ||
268 | } | ||
269 | } | ||
270 | |||
271 | /* | ||
272 | * phprm_find_available_resources | ||
273 | * | ||
274 | * Finds available memory, IO, and IRQ resources for programming | ||
275 | * devices which may be added to the system | ||
276 | * this function is for hot plug ADD! | ||
277 | * | ||
278 | * returns 0 if success | ||
279 | */ | ||
280 | int shpchprm_find_available_resources(struct controller *ctrl) | ||
281 | { | ||
282 | struct pci_func func; | ||
283 | u32 rc; | ||
284 | |||
285 | memset(&func, 0, sizeof(struct pci_func)); | ||
286 | |||
287 | func.bus = ctrl->bus; | ||
288 | func.device = ctrl->device; | ||
289 | func.function = ctrl->function; | ||
290 | func.is_a_board = 1; | ||
291 | |||
292 | /* Get resources for this PCI bridge */ | ||
293 | rc = shpchp_save_used_resources (ctrl, &func, !DISABLE_CARD); | ||
294 | dbg("%s: shpchp_save_used_resources rc = %d\n", __FUNCTION__, rc); | ||
295 | |||
296 | if (func.mem_head) | ||
297 | func.mem_head->next = ctrl->mem_head; | ||
298 | ctrl->mem_head = func.mem_head; | ||
299 | |||
300 | if (func.p_mem_head) | ||
301 | func.p_mem_head->next = ctrl->p_mem_head; | ||
302 | ctrl->p_mem_head = func.p_mem_head; | ||
303 | |||
304 | if (func.io_head) | ||
305 | func.io_head->next = ctrl->io_head; | ||
306 | ctrl->io_head = func.io_head; | ||
307 | |||
308 | if(func.bus_head) | ||
309 | func.bus_head->next = ctrl->bus_head; | ||
310 | ctrl->bus_head = func.bus_head; | ||
311 | if (ctrl->bus_head) | ||
312 | phprm_delete_resource(&ctrl->bus_head, ctrl->pci_dev->subordinate->number, 1); | ||
313 | |||
314 | dbg("%s:pre-Bind PCI 0x%x Ctrl Resource Dump\n", __FUNCTION__, ctrl->bus); | ||
315 | phprm_dump_ctrl_res(ctrl); | ||
316 | bind_pci_resources_to_slots (ctrl); | ||
317 | |||
318 | dbg("%s:post-Bind PCI 0x%x Ctrl Resource Dump\n", __FUNCTION__, ctrl->bus); | ||
319 | phprm_dump_ctrl_res(ctrl); | ||
320 | |||
321 | |||
322 | /* If all of the following fail, we don't have any resources for hot plug add */ | ||
323 | rc = 1; | ||
324 | rc &= shpchp_resource_sort_and_combine(&(ctrl->mem_head)); | ||
325 | rc &= shpchp_resource_sort_and_combine(&(ctrl->p_mem_head)); | ||
326 | rc &= shpchp_resource_sort_and_combine(&(ctrl->io_head)); | ||
327 | rc &= shpchp_resource_sort_and_combine(&(ctrl->bus_head)); | ||
328 | |||
329 | return (rc); | ||
330 | } | ||
331 | |||
332 | int shpchprm_set_hpp( | ||
333 | struct controller *ctrl, | ||
334 | struct pci_func *func, | ||
335 | u8 card_type) | ||
336 | { | ||
337 | u32 rc; | ||
338 | u8 temp_byte; | ||
339 | struct pci_bus lpci_bus, *pci_bus; | ||
340 | unsigned int devfn; | ||
341 | memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus)); | ||
342 | pci_bus = &lpci_bus; | ||
343 | pci_bus->number = func->bus; | ||
344 | devfn = PCI_DEVFN(func->device, func->function); | ||
345 | |||
346 | temp_byte = 0x40; /* hard coded value for LT */ | ||
347 | if (card_type == PCI_HEADER_TYPE_BRIDGE) { | ||
348 | /* set subordinate Latency Timer */ | ||
349 | rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SEC_LATENCY_TIMER, temp_byte); | ||
350 | |||
351 | if (rc) { | ||
352 | dbg("%s: set secondary LT error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, | ||
353 | func->device, func->function); | ||
354 | return rc; | ||
355 | } | ||
356 | } | ||
357 | |||
358 | /* set base Latency Timer */ | ||
359 | rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_LATENCY_TIMER, temp_byte); | ||
360 | |||
361 | if (rc) { | ||
362 | dbg("%s: set LT error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function); | ||
363 | return rc; | ||
364 | } | ||
365 | |||
366 | /* set Cache Line size */ | ||
367 | temp_byte = 0x08; /* hard coded value for CLS */ | ||
368 | |||
369 | rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_CACHE_LINE_SIZE, temp_byte); | ||
370 | |||
371 | if (rc) { | ||
372 | dbg("%s: set CLS error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function); | ||
373 | } | ||
374 | |||
375 | /* set enable_perr */ | ||
376 | /* set enable_serr */ | ||
377 | |||
378 | return rc; | ||
379 | } | ||
380 | |||
381 | void shpchprm_enable_card( | ||
382 | struct controller *ctrl, | ||
383 | struct pci_func *func, | ||
384 | u8 card_type) | ||
385 | { | ||
386 | u16 command, bcommand; | ||
387 | struct pci_bus lpci_bus, *pci_bus; | ||
388 | unsigned int devfn; | ||
389 | int rc; | ||
390 | |||
391 | memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus)); | ||
392 | pci_bus = &lpci_bus; | ||
393 | pci_bus->number = func->bus; | ||
394 | devfn = PCI_DEVFN(func->device, func->function); | ||
395 | |||
396 | rc = pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &command); | ||
397 | |||
398 | command |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR | ||
399 | | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE | ||
400 | | PCI_COMMAND_IO | PCI_COMMAND_MEMORY; | ||
401 | |||
402 | rc = pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command); | ||
403 | |||
404 | if (card_type == PCI_HEADER_TYPE_BRIDGE) { | ||
405 | |||
406 | rc = pci_bus_read_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, &bcommand); | ||
407 | |||
408 | bcommand |= PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR | ||
409 | | PCI_BRIDGE_CTL_NO_ISA; | ||
410 | |||
411 | rc = pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, bcommand); | ||
412 | } | ||
413 | } | ||
414 | |||
415 | static int legacy_shpchprm_init_pci(void) | ||
416 | { | ||
417 | return 0; | ||
418 | } | ||
419 | |||
420 | int shpchprm_init(enum php_ctlr_type ctrl_type) | ||
421 | { | ||
422 | int retval; | ||
423 | |||
424 | switch (ctrl_type) { | ||
425 | case PCI: | ||
426 | retval = legacy_shpchprm_init_pci(); | ||
427 | break; | ||
428 | default: | ||
429 | retval = -ENODEV; | ||
430 | break; | ||
431 | } | ||
432 | |||
433 | return retval; | ||
434 | } | ||
diff --git a/drivers/pci/hotplug/shpchprm_nonacpi.h b/drivers/pci/hotplug/shpchprm_nonacpi.h new file mode 100644 index 000000000000..6bc8668023c3 --- /dev/null +++ b/drivers/pci/hotplug/shpchprm_nonacpi.h | |||
@@ -0,0 +1,56 @@ | |||
1 | /* | ||
2 | * SHPCHPRM NONACPI: PHP Resource Manager for Non-ACPI/Legacy platform | ||
3 | * | ||
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | ||
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | ||
6 | * Copyright (C) 2001 IBM Corp. | ||
7 | * Copyright (C) 2003-2004 Intel Corporation | ||
8 | * | ||
9 | * All rights reserved. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or (at | ||
14 | * your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
19 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
20 | * details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
25 | * | ||
26 | * Send feedback to <greg@kroah.com>, <dely.l.sy@intel.com> | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | #ifndef _SHPCHPRM_NONACPI_H_ | ||
31 | #define _SHPCHPRM_NONACPI_H_ | ||
32 | |||
33 | struct irq_info { | ||
34 | u8 bus, devfn; /* bus, device and function */ | ||
35 | struct { | ||
36 | u8 link; /* IRQ line ID, chipset dependent, 0=not routed */ | ||
37 | u16 bitmap; /* Available IRQs */ | ||
38 | } __attribute__ ((packed)) irq[4]; | ||
39 | u8 slot; /* slot number, 0=onboard */ | ||
40 | u8 rfu; | ||
41 | } __attribute__ ((packed)); | ||
42 | |||
43 | struct irq_routing_table { | ||
44 | u32 signature; /* PIRQ_SIGNATURE should be here */ | ||
45 | u16 version; /* PIRQ_VERSION */ | ||
46 | u16 size; /* Table size in bytes */ | ||
47 | u8 rtr_bus, rtr_devfn; /* Where the interrupt router lies */ | ||
48 | u16 exclusive_irqs; /* IRQs devoted exclusively to PCI usage */ | ||
49 | u16 rtr_vendor, rtr_device; /* Vendor and device ID of interrupt router */ | ||
50 | u32 miniport_data; /* Crap */ | ||
51 | u8 rfu[11]; | ||
52 | u8 checksum; /* Modulo 256 checksum must give zero */ | ||
53 | struct irq_info slots[0]; | ||
54 | } __attribute__ ((packed)); | ||
55 | |||
56 | #endif /* _SHPCHPRM_NONACPI_H_ */ | ||