diff options
Diffstat (limited to 'drivers/pci/hotplug/shpchprm_legacy.c')
-rw-r--r-- | drivers/pci/hotplug/shpchprm_legacy.c | 439 |
1 files changed, 439 insertions, 0 deletions
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 | } | ||