aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPrarit Bhargava <prarit@sgi.com>2005-07-06 18:29:53 -0400
committerTony Luck <tony.luck@intel.com>2005-07-06 18:29:53 -0400
commit6f354b014b51716166f13f68b29212d3c44ed2c4 (patch)
tree396c09a5d519630a53652a1187bb85fceba82cee
parent283c7f6ac6adb57e7dd13cdbc8d60b6ea4de6faf (diff)
[IA64] hotplug/ia64: SN Hotplug Driver - SN Hotplug Driver code
This patch is the SGI hotplug driver and additional changes required for the driver. These modifications include changes to the SN io_init.c code for memory management, the inclusion of new SAL calls to enable and disable PCI slots, and a hotplug-style driver. Signed-off-by: Prarit Bhargava <prarit@sgi.com> Signed-off-by: Tony Luck <tony.luck@intel.com>
-rw-r--r--arch/ia64/sn/kernel/io_init.c134
-rw-r--r--arch/ia64/sn/kernel/irq.c6
-rw-r--r--arch/ia64/sn/pci/pcibr/pcibr_provider.c37
-rw-r--r--drivers/pci/hotplug/Kconfig5
-rw-r--r--drivers/pci/hotplug/Makefile1
-rw-r--r--drivers/pci/hotplug/sgi_hotplug.c611
-rw-r--r--include/asm-ia64/sn/pcibr_provider.h4
-rw-r--r--include/asm-ia64/sn/pcidev.h6
8 files changed, 754 insertions, 50 deletions
diff --git a/arch/ia64/sn/kernel/io_init.c b/arch/ia64/sn/kernel/io_init.c
index 041c4be02b2a..a67f39e448cb 100644
--- a/arch/ia64/sn/kernel/io_init.c
+++ b/arch/ia64/sn/kernel/io_init.c
@@ -23,6 +23,14 @@
23 23
24nasid_t master_nasid = INVALID_NASID; /* Partition Master */ 24nasid_t master_nasid = INVALID_NASID; /* Partition Master */
25 25
26static struct list_head sn_sysdata_list;
27
28/* sysdata list struct */
29struct sysdata_el {
30 struct list_head entry;
31 void *sysdata;
32};
33
26struct slab_info { 34struct slab_info {
27 struct hubdev_info hubdev; 35 struct hubdev_info hubdev;
28}; 36};
@@ -137,23 +145,6 @@ sal_get_pcidev_info(u64 segment, u64 bus_number, u64 devfn, u64 pci_dev,
137} 145}
138 146
139/* 147/*
140 * sn_alloc_pci_sysdata() - This routine allocates a pci controller
141 * which is expected as the pci_dev and pci_bus sysdata by the Linux
142 * PCI infrastructure.
143 */
144static inline struct pci_controller *sn_alloc_pci_sysdata(void)
145{
146 struct pci_controller *pci_sysdata;
147
148 pci_sysdata = kmalloc(sizeof(*pci_sysdata), GFP_KERNEL);
149 if (!pci_sysdata)
150 BUG();
151
152 memset(pci_sysdata, 0, sizeof(*pci_sysdata));
153 return pci_sysdata;
154}
155
156/*
157 * sn_fixup_ionodes() - This routine initializes the HUB data strcuture for 148 * sn_fixup_ionodes() - This routine initializes the HUB data strcuture for
158 * each node in the system. 149 * each node in the system.
159 */ 150 */
@@ -220,6 +211,15 @@ static void sn_fixup_ionodes(void)
220 211
221} 212}
222 213
214void sn_pci_unfixup_slot(struct pci_dev *dev)
215{
216 struct pci_dev *host_pci_dev = SN_PCIDEV_INFO(dev)->host_pci_dev;
217
218 sn_irq_unfixup(dev);
219 pci_dev_put(host_pci_dev);
220 pci_dev_put(dev);
221}
222
223/* 223/*
224 * sn_pci_fixup_slot() - This routine sets up a slot's resources 224 * sn_pci_fixup_slot() - This routine sets up a slot's resources
225 * consistent with the Linux PCI abstraction layer. Resources acquired 225 * consistent with the Linux PCI abstraction layer. Resources acquired
@@ -238,6 +238,7 @@ void sn_pci_fixup_slot(struct pci_dev *dev)
238 unsigned long size; 238 unsigned long size;
239 unsigned int bus_no, devfn; 239 unsigned int bus_no, devfn;
240 240
241 pci_dev_get(dev); /* for the sysdata pointer */
241 dev->sysdata = kmalloc(sizeof(struct pcidev_info), GFP_KERNEL); 242 dev->sysdata = kmalloc(sizeof(struct pcidev_info), GFP_KERNEL);
242 if (SN_PCIDEV_INFO(dev) <= 0) 243 if (SN_PCIDEV_INFO(dev) <= 0)
243 BUG(); /* Cannot afford to run out of memory */ 244 BUG(); /* Cannot afford to run out of memory */
@@ -276,7 +277,8 @@ void sn_pci_fixup_slot(struct pci_dev *dev)
276 dev->resource[idx].parent = &iomem_resource; 277 dev->resource[idx].parent = &iomem_resource;
277 } 278 }
278 279
279 /* Using the PROMs values for the PCI host bus, get the Linux 280 /*
281 * Using the PROMs values for the PCI host bus, get the Linux
280 * PCI host_pci_dev struct and set up host bus linkages 282 * PCI host_pci_dev struct and set up host bus linkages
281 */ 283 */
282 284
@@ -313,55 +315,57 @@ void sn_pci_fixup_slot(struct pci_dev *dev)
313 * sn_pci_controller_fixup() - This routine sets up a bus's resources 315 * sn_pci_controller_fixup() - This routine sets up a bus's resources
314 * consistent with the Linux PCI abstraction layer. 316 * consistent with the Linux PCI abstraction layer.
315 */ 317 */
316static void sn_pci_controller_fixup(int segment, int busnum) 318void sn_pci_controller_fixup(int segment, int busnum, struct pci_bus *bus)
317{ 319{
318 int status = 0; 320 int status = 0;
319 int nasid, cnode; 321 int nasid, cnode;
320 struct pci_bus *bus;
321 struct pci_controller *controller; 322 struct pci_controller *controller;
322 struct pcibus_bussoft *prom_bussoft_ptr; 323 struct pcibus_bussoft *prom_bussoft_ptr;
323 struct hubdev_info *hubdev_info; 324 struct hubdev_info *hubdev_info;
324 void *provider_soft; 325 void *provider_soft;
325 struct sn_pcibus_provider *provider; 326 struct sn_pcibus_provider *provider;
326 327
327 status = 328 status = sal_get_pcibus_info((u64) segment, (u64) busnum,
328 sal_get_pcibus_info((u64) segment, (u64) busnum, 329 (u64) ia64_tpa(&prom_bussoft_ptr));
329 (u64) ia64_tpa(&prom_bussoft_ptr)); 330 if (status > 0)
330 if (status > 0) { 331 return; /*bus # does not exist */
331 return; /* bus # does not exist */
332 }
333
334 prom_bussoft_ptr = __va(prom_bussoft_ptr); 332 prom_bussoft_ptr = __va(prom_bussoft_ptr);
335 controller = sn_alloc_pci_sysdata();
336 /* controller non-zero is BUG'd in sn_alloc_pci_sysdata */
337 333
338 bus = pci_scan_bus(busnum, &pci_root_ops, controller); 334 controller = kcalloc(1,sizeof(struct pci_controller), GFP_KERNEL);
335 if (!controller)
336 BUG();
337
339 if (bus == NULL) { 338 if (bus == NULL) {
340 return; /* error, or bus already scanned */ 339 bus = pci_scan_bus(busnum, &pci_root_ops, controller);
340 if (bus == NULL)
341 return; /* error, or bus already scanned */
342 bus->sysdata = NULL;
341 } 343 }
342 344
345 if (bus->sysdata)
346 goto error_return; /* sysdata already alloc'd */
347
343 /* 348 /*
344 * Per-provider fixup. Copies the contents from prom to local 349 * Per-provider fixup. Copies the contents from prom to local
345 * area and links SN_PCIBUS_BUSSOFT(). 350 * area and links SN_PCIBUS_BUSSOFT().
346 */ 351 */
347 352
348 if (prom_bussoft_ptr->bs_asic_type >= PCIIO_ASIC_MAX_TYPES) { 353 if (prom_bussoft_ptr->bs_asic_type >= PCIIO_ASIC_MAX_TYPES)
349 return; /* unsupported asic type */ 354 return; /* unsupported asic type */
350 } 355
356 if (prom_bussoft_ptr->bs_asic_type == PCIIO_ASIC_TYPE_PPB)
357 goto error_return; /* no further fixup necessary */
351 358
352 provider = sn_pci_provider[prom_bussoft_ptr->bs_asic_type]; 359 provider = sn_pci_provider[prom_bussoft_ptr->bs_asic_type];
353 if (provider == NULL) { 360 if (provider == NULL)
354 return; /* no provider registerd for this asic */ 361 return; /* no provider registerd for this asic */
355 }
356 362
357 provider_soft = NULL; 363 provider_soft = NULL;
358 if (provider->bus_fixup) { 364 if (provider->bus_fixup)
359 provider_soft = (*provider->bus_fixup) (prom_bussoft_ptr); 365 provider_soft = (*provider->bus_fixup) (prom_bussoft_ptr);
360 }
361 366
362 if (provider_soft == NULL) { 367 if (provider_soft == NULL)
363 return; /* fixup failed or not applicable */ 368 return; /* fixup failed or not applicable */
364 }
365 369
366 /* 370 /*
367 * Generic bus fixup goes here. Don't reference prom_bussoft_ptr 371 * Generic bus fixup goes here. Don't reference prom_bussoft_ptr
@@ -370,12 +374,47 @@ static void sn_pci_controller_fixup(int segment, int busnum)
370 374
371 bus->sysdata = controller; 375 bus->sysdata = controller;
372 PCI_CONTROLLER(bus)->platform_data = provider_soft; 376 PCI_CONTROLLER(bus)->platform_data = provider_soft;
373
374 nasid = NASID_GET(SN_PCIBUS_BUSSOFT(bus)->bs_base); 377 nasid = NASID_GET(SN_PCIBUS_BUSSOFT(bus)->bs_base);
375 cnode = nasid_to_cnodeid(nasid); 378 cnode = nasid_to_cnodeid(nasid);
376 hubdev_info = (struct hubdev_info *)(NODEPDA(cnode)->pdinfo); 379 hubdev_info = (struct hubdev_info *)(NODEPDA(cnode)->pdinfo);
377 SN_PCIBUS_BUSSOFT(bus)->bs_xwidget_info = 380 SN_PCIBUS_BUSSOFT(bus)->bs_xwidget_info =
378 &(hubdev_info->hdi_xwidget_info[SN_PCIBUS_BUSSOFT(bus)->bs_xid]); 381 &(hubdev_info->hdi_xwidget_info[SN_PCIBUS_BUSSOFT(bus)->bs_xid]);
382
383 return;
384
385error_return:
386
387 kfree(controller);
388 return;
389}
390
391void sn_bus_store_sysdata(struct pci_dev *dev)
392{
393 struct sysdata_el *element;
394
395 element = kcalloc(1, sizeof(struct sysdata_el), GFP_KERNEL);
396 if (!element) {
397 dev_dbg(dev, "%s: out of memory!\n", __FUNCTION__);
398 return;
399 }
400 element->sysdata = dev->sysdata;
401 list_add(&element->entry, &sn_sysdata_list);
402}
403
404void sn_bus_free_sysdata(void)
405{
406 struct sysdata_el *element;
407 struct list_head *list;
408
409sn_sysdata_free_start:
410 list_for_each(list, &sn_sysdata_list) {
411 element = list_entry(list, struct sysdata_el, entry);
412 list_del(&element->entry);
413 kfree(element->sysdata);
414 kfree(element);
415 goto sn_sysdata_free_start;
416 }
417 return;
379} 418}
380 419
381/* 420/*
@@ -413,15 +452,16 @@ static int __init sn_pci_init(void)
413 ia64_max_iommu_merge_mask = ~PAGE_MASK; 452 ia64_max_iommu_merge_mask = ~PAGE_MASK;
414 sn_fixup_ionodes(); 453 sn_fixup_ionodes();
415 sn_irq_lh_init(); 454 sn_irq_lh_init();
455 INIT_LIST_HEAD(&sn_sysdata_list);
416 sn_init_cpei_timer(); 456 sn_init_cpei_timer();
417 457
418#ifdef CONFIG_PROC_FS 458#ifdef CONFIG_PROC_FS
419 register_sn_procfs(); 459 register_sn_procfs();
420#endif 460#endif
421 461
422 for (i = 0; i < PCI_BUSES_TO_SCAN; i++) { 462 /* busses are not known yet ... */
423 sn_pci_controller_fixup(0, i); 463 for (i = 0; i < PCI_BUSES_TO_SCAN; i++)
424 } 464 sn_pci_controller_fixup(0, i, NULL);
425 465
426 /* 466 /*
427 * Generic Linux PCI Layer has created the pci_bus and pci_dev 467 * Generic Linux PCI Layer has created the pci_bus and pci_dev
@@ -430,9 +470,8 @@ static int __init sn_pci_init(void)
430 */ 470 */
431 471
432 while ((pci_dev = 472 while ((pci_dev =
433 pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pci_dev)) != NULL) { 473 pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pci_dev)) != NULL)
434 sn_pci_fixup_slot(pci_dev); 474 sn_pci_fixup_slot(pci_dev);
435 }
436 475
437 sn_ioif_inited = 1; /* sn I/O infrastructure now initialized */ 476 sn_ioif_inited = 1; /* sn I/O infrastructure now initialized */
438 477
@@ -474,3 +513,8 @@ cnodeid_get_geoid(cnodeid_t cnode)
474} 513}
475 514
476subsys_initcall(sn_pci_init); 515subsys_initcall(sn_pci_init);
516EXPORT_SYMBOL(sn_pci_fixup_slot);
517EXPORT_SYMBOL(sn_pci_unfixup_slot);
518EXPORT_SYMBOL(sn_pci_controller_fixup);
519EXPORT_SYMBOL(sn_bus_store_sysdata);
520EXPORT_SYMBOL(sn_bus_free_sysdata);
diff --git a/arch/ia64/sn/kernel/irq.c b/arch/ia64/sn/kernel/irq.c
index cf4dbf9645f1..84d276a14ecb 100644
--- a/arch/ia64/sn/kernel/irq.c
+++ b/arch/ia64/sn/kernel/irq.c
@@ -284,7 +284,6 @@ void sn_irq_fixup(struct pci_dev *pci_dev, struct sn_irq_info *sn_irq_info)
284 int cpu = nasid_slice_to_cpuid(nasid, slice); 284 int cpu = nasid_slice_to_cpuid(nasid, slice);
285 285
286 pci_dev_get(pci_dev); 286 pci_dev_get(pci_dev);
287
288 sn_irq_info->irq_cpuid = cpu; 287 sn_irq_info->irq_cpuid = cpu;
289 sn_irq_info->irq_pciioinfo = SN_PCIDEV_INFO(pci_dev); 288 sn_irq_info->irq_pciioinfo = SN_PCIDEV_INFO(pci_dev);
290 289
@@ -305,15 +304,16 @@ void sn_irq_unfixup(struct pci_dev *pci_dev)
305 return; 304 return;
306 305
307 sn_irq_info = SN_PCIDEV_INFO(pci_dev)->pdi_sn_irq_info; 306 sn_irq_info = SN_PCIDEV_INFO(pci_dev)->pdi_sn_irq_info;
308 if (!sn_irq_info || !sn_irq_info->irq_irq) 307 if (!sn_irq_info || !sn_irq_info->irq_irq) {
308 kfree(sn_irq_info);
309 return; 309 return;
310 }
310 311
311 unregister_intr_pda(sn_irq_info); 312 unregister_intr_pda(sn_irq_info);
312 spin_lock(&sn_irq_info_lock); 313 spin_lock(&sn_irq_info_lock);
313 list_del_rcu(&sn_irq_info->list); 314 list_del_rcu(&sn_irq_info->list);
314 spin_unlock(&sn_irq_info_lock); 315 spin_unlock(&sn_irq_info_lock);
315 call_rcu(&sn_irq_info->rcu, sn_irq_info_free); 316 call_rcu(&sn_irq_info->rcu, sn_irq_info_free);
316
317 pci_dev_put(pci_dev); 317 pci_dev_put(pci_dev);
318} 318}
319 319
diff --git a/arch/ia64/sn/pci/pcibr/pcibr_provider.c b/arch/ia64/sn/pci/pcibr/pcibr_provider.c
index 9bc4de4a3ec0..9813da56d311 100644
--- a/arch/ia64/sn/pci/pcibr/pcibr_provider.c
+++ b/arch/ia64/sn/pci/pcibr/pcibr_provider.c
@@ -18,6 +18,40 @@
18#include "xtalk/xwidgetdev.h" 18#include "xtalk/xwidgetdev.h"
19#include "xtalk/hubdev.h" 19#include "xtalk/hubdev.h"
20 20
21int
22sal_pcibr_slot_enable(struct pcibus_info *soft, int device, void *resp)
23{
24 struct ia64_sal_retval ret_stuff;
25 uint64_t busnum;
26
27 ret_stuff.status = 0;
28 ret_stuff.v0 = 0;
29
30 busnum = soft->pbi_buscommon.bs_persist_busnum;
31 SAL_CALL_NOLOCK(ret_stuff, (u64) SN_SAL_IOIF_SLOT_ENABLE, (u64) busnum,
32 (u64) device, (u64) resp, 0, 0, 0, 0);
33
34 return (int)ret_stuff.v0;
35}
36
37int
38sal_pcibr_slot_disable(struct pcibus_info *soft, int device, int action,
39 void *resp)
40{
41 struct ia64_sal_retval ret_stuff;
42 uint64_t busnum;
43
44 ret_stuff.status = 0;
45 ret_stuff.v0 = 0;
46
47 busnum = soft->pbi_buscommon.bs_persist_busnum;
48 SAL_CALL_NOLOCK(ret_stuff, (u64) SN_SAL_IOIF_SLOT_DISABLE,
49 (u64) busnum, (u64) device, (u64) action,
50 (u64) resp, 0, 0, 0);
51
52 return (int)ret_stuff.v0;
53}
54
21static int sal_pcibr_error_interrupt(struct pcibus_info *soft) 55static int sal_pcibr_error_interrupt(struct pcibus_info *soft)
22{ 56{
23 struct ia64_sal_retval ret_stuff; 57 struct ia64_sal_retval ret_stuff;
@@ -187,3 +221,6 @@ pcibr_init_provider(void)
187 221
188 return 0; 222 return 0;
189} 223}
224
225EXPORT_SYMBOL_GPL(sal_pcibr_slot_enable);
226EXPORT_SYMBOL_GPL(sal_pcibr_slot_disable);
diff --git a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig
index 1a4d4ca2a4dc..9c4a39ee89b5 100644
--- a/drivers/pci/hotplug/Kconfig
+++ b/drivers/pci/hotplug/Kconfig
@@ -187,9 +187,10 @@ config HOTPLUG_PCI_RPA_DLPAR
187 187
188config HOTPLUG_PCI_SGI 188config HOTPLUG_PCI_SGI
189 tristate "SGI PCI Hotplug Support" 189 tristate "SGI PCI Hotplug Support"
190 depends on HOTPLUG_PCI && IA64_SGI_SN2 190 depends on HOTPLUG_PCI && (IA64_SGI_SN2 || IA64_GENERIC)
191 help 191 help
192 Say Y here if you have an SGI IA64 Altix system. 192 Say Y here if you want to use the SGI Altix Hotplug
193 Driver for PCI devices.
193 194
194 When in doubt, say N. 195 When in doubt, say N.
195 196
diff --git a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile
index 3e632ff8c717..31a307004b94 100644
--- a/drivers/pci/hotplug/Makefile
+++ b/drivers/pci/hotplug/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_HOTPLUG_PCI_PCIE) += pciehp.o
14obj-$(CONFIG_HOTPLUG_PCI_SHPC) += shpchp.o 14obj-$(CONFIG_HOTPLUG_PCI_SHPC) += shpchp.o
15obj-$(CONFIG_HOTPLUG_PCI_RPA) += rpaphp.o 15obj-$(CONFIG_HOTPLUG_PCI_RPA) += rpaphp.o
16obj-$(CONFIG_HOTPLUG_PCI_RPA_DLPAR) += rpadlpar_io.o 16obj-$(CONFIG_HOTPLUG_PCI_RPA_DLPAR) += rpadlpar_io.o
17obj-$(CONFIG_HOTPLUG_PCI_SGI) += sgi_hotplug.o
17 18
18pci_hotplug-objs := pci_hotplug_core.o 19pci_hotplug-objs := pci_hotplug_core.o
19 20
diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c
new file mode 100644
index 000000000000..323041fd41dc
--- /dev/null
+++ b/drivers/pci/hotplug/sgi_hotplug.c
@@ -0,0 +1,611 @@
1/*
2 * This file is subject to the terms and conditions of the GNU General Public
3 * License. See the file "COPYING" in the main directory of this archive
4 * for more details.
5 *
6 * Copyright (C) 2005 Silicon Graphics, Inc. All rights reserved.
7 *
8 * This work was based on the 2.4/2.6 kernel development by Dick Reigner.
9 * Work to add BIOS PROM support was completed by Mike Habeck.
10 */
11
12#include <linux/init.h>
13#include <linux/kernel.h>
14#include <linux/module.h>
15#include <linux/pci.h>
16#include <linux/proc_fs.h>
17#include <linux/types.h>
18
19#include <asm/sn/addrs.h>
20#include <asm/sn/l1.h>
21#include <asm/sn/module.h>
22#include <asm/sn/pcibr_provider.h>
23#include <asm/sn/pcibus_provider_defs.h>
24#include <asm/sn/pcidev.h>
25#include <asm/sn/sn_sal.h>
26#include <asm/sn/types.h>
27
28#include "../pci.h"
29#include "pci_hotplug.h"
30
31MODULE_LICENSE("GPL");
32MODULE_AUTHOR("SGI (prarit@sgi.com, dickie@sgi.com, habeck@sgi.com)");
33MODULE_DESCRIPTION("SGI Altix Hot Plug PCI Controller Driver");
34
35#define PCIIO_ASIC_TYPE_TIOCA 4
36#define PCI_SLOT_ALREADY_UP 2 /* slot already up */
37#define PCI_SLOT_ALREADY_DOWN 3 /* slot already down */
38#define PCI_L1_ERR 7 /* L1 console command error */
39#define PCI_EMPTY_33MHZ 15 /* empty 33 MHz bus */
40#define PCI_L1_QSIZE 128 /* our L1 message buffer size */
41#define SN_MAX_HP_SLOTS 32 /* max number of hotplug slots */
42#define SGI_HOTPLUG_PROM_REV 0x0420 /* Min. required PROM version */
43
44/* internal list head */
45static struct list_head sn_hp_list;
46
47/* hotplug_slot struct's private pointer */
48struct slot {
49 int device_num;
50 struct pci_bus *pci_bus;
51 /* this struct for glue internal only */
52 struct hotplug_slot *hotplug_slot;
53 struct list_head hp_list;
54};
55
56struct pcibr_slot_enable_resp {
57 int resp_sub_errno;
58 char resp_l1_msg[PCI_L1_QSIZE + 1];
59};
60
61struct pcibr_slot_disable_resp {
62 int resp_sub_errno;
63 char resp_l1_msg[PCI_L1_QSIZE + 1];
64};
65
66enum sn_pci_req_e {
67 PCI_REQ_SLOT_ELIGIBLE,
68 PCI_REQ_SLOT_DISABLE
69};
70
71static int enable_slot(struct hotplug_slot *slot);
72static int disable_slot(struct hotplug_slot *slot);
73static int get_power_status(struct hotplug_slot *slot, u8 *value);
74
75static struct hotplug_slot_ops sn_hotplug_slot_ops = {
76 .owner = THIS_MODULE,
77 .enable_slot = enable_slot,
78 .disable_slot = disable_slot,
79 .get_power_status = get_power_status,
80};
81
82static DECLARE_MUTEX(sn_hotplug_sem);
83
84static int sn_pci_slot_valid(struct pci_bus *pci_bus, int device)
85{
86 struct pcibus_info *pcibus_info;
87 int bricktype;
88 int bus_num;
89
90 pcibus_info = SN_PCIBUS_BUSSOFT_INFO(pci_bus);
91
92 /* Check to see if this is a valid slot on 'pci_bus' */
93 if (!(pcibus_info->pbi_valid_devices & (1 << device)))
94 return -EPERM;
95
96 bricktype = MODULE_GET_BTYPE(pcibus_info->pbi_moduleid);
97 bus_num = pcibus_info->pbi_buscommon.bs_persist_busnum & 0xf;
98
99 /* Do not allow hotplug operations on base I/O cards */
100 if ((bricktype == L1_BRICKTYPE_IX || bricktype == L1_BRICKTYPE_IA) &&
101 (bus_num == 1 && device != 1))
102 return -EPERM;
103
104 return 1;
105}
106
107static int sn_pci_bus_valid(struct pci_bus *pci_bus)
108{
109 struct pcibus_info *pcibus_info;
110 int asic_type;
111 int bricktype;
112
113 pcibus_info = SN_PCIBUS_BUSSOFT_INFO(pci_bus);
114
115 /* Don't register slots hanging off the TIOCA bus */
116 asic_type = pcibus_info->pbi_buscommon.bs_asic_type;
117 if (asic_type == PCIIO_ASIC_TYPE_TIOCA)
118 return -EPERM;
119
120 /* Only register slots in I/O Bricks that support hotplug */
121 bricktype = MODULE_GET_BTYPE(pcibus_info->pbi_moduleid);
122 switch (bricktype) {
123 case L1_BRICKTYPE_IX:
124 case L1_BRICKTYPE_PX:
125 case L1_BRICKTYPE_IA:
126 case L1_BRICKTYPE_PA:
127 return 1;
128 break;
129 default:
130 return -EPERM;
131 break;
132 }
133
134 return -EIO;
135}
136
137static int sn_hp_slot_private_alloc(struct hotplug_slot *bss_hotplug_slot,
138 struct pci_bus *pci_bus, int device)
139{
140 struct pcibus_info *pcibus_info;
141 struct slot *slot;
142
143 pcibus_info = SN_PCIBUS_BUSSOFT_INFO(pci_bus);
144
145 bss_hotplug_slot->private = kcalloc(1, sizeof(struct slot),
146 GFP_KERNEL);
147 if (!bss_hotplug_slot->private)
148 return -ENOMEM;
149 slot = (struct slot *)bss_hotplug_slot->private;
150
151 bss_hotplug_slot->name = kmalloc(33, GFP_KERNEL);
152 if (!bss_hotplug_slot->name) {
153 kfree(bss_hotplug_slot->private);
154 return -ENOMEM;
155 }
156
157 slot->device_num = device;
158 slot->pci_bus = pci_bus;
159
160 sprintf(bss_hotplug_slot->name, "module_%c%c%c%c%.2d_b_%d_s_%d",
161 '0'+RACK_GET_CLASS(MODULE_GET_RACK(pcibus_info->pbi_moduleid)),
162 '0'+RACK_GET_GROUP(MODULE_GET_RACK(pcibus_info->pbi_moduleid)),
163 '0'+RACK_GET_NUM(MODULE_GET_RACK(pcibus_info->pbi_moduleid)),
164 MODULE_GET_BTCHAR(pcibus_info->pbi_moduleid),
165 MODULE_GET_BPOS(pcibus_info->pbi_moduleid),
166 ((int)pcibus_info->pbi_buscommon.bs_persist_busnum) & 0xf,
167 device + 1);
168
169 slot->hotplug_slot = bss_hotplug_slot;
170 list_add(&slot->hp_list, &sn_hp_list);
171
172 return 0;
173}
174
175static struct hotplug_slot * sn_hp_destroy(void)
176{
177 struct slot *slot;
178 struct list_head *list;
179 struct hotplug_slot *bss_hotplug_slot = NULL;
180
181 list_for_each(list, &sn_hp_list) {
182 slot = list_entry(list, struct slot, hp_list);
183 bss_hotplug_slot = slot->hotplug_slot;
184 list_del(&((struct slot *)bss_hotplug_slot->private)->
185 hp_list);
186 break;
187 }
188 return bss_hotplug_slot;
189}
190
191static void sn_bus_alloc_data(struct pci_dev *dev)
192{
193 struct list_head *node;
194 struct pci_bus *subordinate_bus;
195 struct pci_dev *child;
196
197 sn_pci_fixup_slot(dev);
198
199 /* Recursively sets up the sn_irq_info structs */
200 if (dev->subordinate) {
201 subordinate_bus = dev->subordinate;
202 list_for_each(node, &subordinate_bus->devices) {
203 child = list_entry(node, struct pci_dev, bus_list);
204 sn_bus_alloc_data(child);
205 }
206 }
207}
208
209static void sn_bus_free_data(struct pci_dev *dev)
210{
211 struct list_head *node;
212 struct pci_bus *subordinate_bus;
213 struct pci_dev *child;
214
215 /* Recursively clean up sn_irq_info structs */
216 if (dev->subordinate) {
217 subordinate_bus = dev->subordinate;
218 list_for_each(node, &subordinate_bus->devices) {
219 child = list_entry(node, struct pci_dev, bus_list);
220 sn_bus_free_data(child);
221 }
222 }
223 sn_pci_unfixup_slot(dev);
224}
225
226static u8 sn_power_status_get(struct hotplug_slot *bss_hotplug_slot)
227{
228 struct slot *slot = (struct slot *)bss_hotplug_slot->private;
229 struct pcibus_info *pcibus_info;
230 u8 retval;
231
232 pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus);
233 retval = pcibus_info->pbi_enabled_devices & (1 << slot->device_num);
234
235 return retval ? 1 : 0;
236}
237
238static void sn_slot_mark_enable(struct hotplug_slot *bss_hotplug_slot,
239 int device_num)
240{
241 struct slot *slot = (struct slot *)bss_hotplug_slot->private;
242 struct pcibus_info *pcibus_info;
243
244 pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus);
245 pcibus_info->pbi_enabled_devices |= (1 << device_num);
246}
247
248static void sn_slot_mark_disable(struct hotplug_slot *bss_hotplug_slot,
249 int device_num)
250{
251 struct slot *slot = (struct slot *)bss_hotplug_slot->private;
252 struct pcibus_info *pcibus_info;
253
254 pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus);
255 pcibus_info->pbi_enabled_devices &= ~(1 << device_num);
256}
257
258static int sn_slot_enable(struct hotplug_slot *bss_hotplug_slot,
259 int device_num)
260{
261 struct slot *slot = (struct slot *)bss_hotplug_slot->private;
262 struct pcibus_info *pcibus_info;
263 struct pcibr_slot_enable_resp resp;
264 int rc;
265
266 pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus);
267
268 /*
269 * Power-on and initialize the slot in the SN
270 * PCI infrastructure.
271 */
272 rc = sal_pcibr_slot_enable(pcibus_info, device_num, &resp);
273
274 if (rc == PCI_SLOT_ALREADY_UP) {
275 dev_dbg(slot->pci_bus->self, "is already active\n");
276 return -EPERM;
277 }
278
279 if (rc == PCI_L1_ERR) {
280 dev_dbg(slot->pci_bus->self,
281 "L1 failure %d with message: %s",
282 resp.resp_sub_errno, resp.resp_l1_msg);
283 return -EPERM;
284 }
285
286 if (rc) {
287 dev_dbg(slot->pci_bus->self,
288 "insert failed with error %d sub-error %d\n",
289 rc, resp.resp_sub_errno);
290 return -EIO;
291 }
292
293 sn_slot_mark_enable(bss_hotplug_slot, device_num);
294
295 return 0;
296}
297
298static int sn_slot_disable(struct hotplug_slot *bss_hotplug_slot,
299 int device_num, int action)
300{
301 struct slot *slot = (struct slot *)bss_hotplug_slot->private;
302 struct pcibus_info *pcibus_info;
303 struct pcibr_slot_disable_resp resp;
304 int rc;
305
306 pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus);
307
308 rc = sal_pcibr_slot_disable(pcibus_info, device_num, action, &resp);
309
310 if (action == PCI_REQ_SLOT_ELIGIBLE && rc == PCI_SLOT_ALREADY_DOWN) {
311 dev_dbg(slot->pci_bus->self, "Slot %s already inactive\n");
312 return -ENODEV;
313 }
314
315 if (action == PCI_REQ_SLOT_ELIGIBLE && rc == PCI_EMPTY_33MHZ) {
316 dev_dbg(slot->pci_bus->self,
317 "Cannot remove last 33MHz card\n");
318 return -EPERM;
319 }
320
321 if (action == PCI_REQ_SLOT_ELIGIBLE && rc == PCI_L1_ERR) {
322 dev_dbg(slot->pci_bus->self,
323 "L1 failure %d with message \n%s\n",
324 resp.resp_sub_errno, resp.resp_l1_msg);
325 return -EPERM;
326 }
327
328 if (action == PCI_REQ_SLOT_ELIGIBLE && rc) {
329 dev_dbg(slot->pci_bus->self,
330 "remove failed with error %d sub-error %d\n",
331 rc, resp.resp_sub_errno);
332 return -EIO;
333 }
334
335 if (action == PCI_REQ_SLOT_ELIGIBLE && !rc)
336 return 0;
337
338 if (action == PCI_REQ_SLOT_DISABLE && !rc) {
339 sn_slot_mark_disable(bss_hotplug_slot, device_num);
340 dev_dbg(slot->pci_bus->self, "remove successful\n");
341 return 0;
342 }
343
344 if (action == PCI_REQ_SLOT_DISABLE && rc) {
345 dev_dbg(slot->pci_bus->self,"remove failed rc = %d\n", rc);
346 return rc;
347 }
348
349 return rc;
350}
351
352static int enable_slot(struct hotplug_slot *bss_hotplug_slot)
353{
354 struct slot *slot = (struct slot *)bss_hotplug_slot->private;
355 struct pci_bus *new_bus = NULL;
356 struct pci_dev *dev;
357 int func, num_funcs;
358 int new_ppb = 0;
359 int rc;
360
361 /* Serialize the Linux PCI infrastructure */
362 down(&sn_hotplug_sem);
363
364 /*
365 * Power-on and initialize the slot in the SN
366 * PCI infrastructure.
367 */
368 rc = sn_slot_enable(bss_hotplug_slot, slot->device_num);
369 if (rc) {
370 up(&sn_hotplug_sem);
371 return rc;
372 }
373
374 num_funcs = pci_scan_slot(slot->pci_bus, PCI_DEVFN(slot->device_num+1,
375 PCI_FUNC(0)));
376 if (!num_funcs) {
377 dev_dbg(slot->pci_bus->self, "no device in slot\n");
378 up(&sn_hotplug_sem);
379 return -ENODEV;
380 }
381
382 sn_pci_controller_fixup(pci_domain_nr(slot->pci_bus),
383 slot->pci_bus->number,
384 slot->pci_bus);
385 /*
386 * Map SN resources for all functions on the card
387 * to the Linux PCI interface and tell the drivers
388 * about them.
389 */
390 for (func = 0; func < num_funcs; func++) {
391 dev = pci_get_slot(slot->pci_bus,
392 PCI_DEVFN(slot->device_num + 1,
393 PCI_FUNC(func)));
394
395
396 if (dev) {
397 if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
398 unsigned char sec_bus;
399 pci_read_config_byte(dev, PCI_SECONDARY_BUS,
400 &sec_bus);
401 new_bus = pci_add_new_bus(dev->bus, dev,
402 sec_bus);
403 pci_scan_child_bus(new_bus);
404 sn_pci_controller_fixup(pci_domain_nr(new_bus),
405 new_bus->number,
406 new_bus);
407 new_ppb = 1;
408 }
409 sn_bus_alloc_data(dev);
410 pci_dev_put(dev);
411 }
412 }
413
414 /* Call the driver for the new device */
415 pci_bus_add_devices(slot->pci_bus);
416 /* Call the drivers for the new devices subordinate to PPB */
417 if (new_ppb)
418 pci_bus_add_devices(new_bus);
419
420 up(&sn_hotplug_sem);
421
422 if (rc == 0)
423 dev_dbg(slot->pci_bus->self,
424 "insert operation successful\n");
425 else
426 dev_dbg(slot->pci_bus->self,
427 "insert operation failed rc = %d\n", rc);
428
429 return rc;
430}
431
432static int disable_slot(struct hotplug_slot *bss_hotplug_slot)
433{
434 struct slot *slot = (struct slot *)bss_hotplug_slot->private;
435 struct pci_dev *dev;
436 int func;
437 int rc;
438
439 /* Acquire update access to the bus */
440 down(&sn_hotplug_sem);
441
442 /* is it okay to bring this slot down? */
443 rc = sn_slot_disable(bss_hotplug_slot, slot->device_num,
444 PCI_REQ_SLOT_ELIGIBLE);
445 if (rc)
446 goto leaving;
447
448 /* Free the SN resources assigned to the Linux device.*/
449 for (func = 0; func < 8; func++) {
450 dev = pci_get_slot(slot->pci_bus,
451 PCI_DEVFN(slot->device_num+1,
452 PCI_FUNC(func)));
453 if (dev) {
454 /*
455 * Some drivers may use dma accesses during the
456 * driver remove function. We release the sysdata
457 * areas after the driver remove functions have
458 * been called.
459 */
460 sn_bus_store_sysdata(dev);
461 sn_bus_free_data(dev);
462 pci_remove_bus_device(dev);
463 pci_dev_put(dev);
464 }
465 }
466
467 /* free the collected sysdata pointers */
468 sn_bus_free_sysdata();
469
470 /* Deactivate slot */
471 rc = sn_slot_disable(bss_hotplug_slot, slot->device_num,
472 PCI_REQ_SLOT_DISABLE);
473 leaving:
474 /* Release the bus lock */
475 up(&sn_hotplug_sem);
476
477 return rc;
478}
479
480static int get_power_status(struct hotplug_slot *bss_hotplug_slot, u8 *value)
481{
482 down(&sn_hotplug_sem);
483 *value = sn_power_status_get(bss_hotplug_slot);
484 up(&sn_hotplug_sem);
485 return 0;
486}
487
488static void sn_release_slot(struct hotplug_slot *bss_hotplug_slot)
489{
490 kfree(bss_hotplug_slot->info);
491 kfree(bss_hotplug_slot->name);
492 kfree(bss_hotplug_slot->private);
493 kfree(bss_hotplug_slot);
494}
495
496static int sn_hotplug_slot_register(struct pci_bus *pci_bus)
497{
498 int device;
499 struct hotplug_slot *bss_hotplug_slot;
500 int rc = 0;
501
502 /*
503 * Currently only four devices are supported,
504 * in the future there maybe more -- up to 32.
505 */
506
507 for (device = 0; device < SN_MAX_HP_SLOTS ; device++) {
508 if (sn_pci_slot_valid(pci_bus, device) != 1)
509 continue;
510
511 bss_hotplug_slot = kcalloc(1,sizeof(struct hotplug_slot),
512 GFP_KERNEL);
513 if (!bss_hotplug_slot) {
514 rc = -ENOMEM;
515 goto alloc_err;
516 }
517
518 bss_hotplug_slot->info =
519 kcalloc(1,sizeof(struct hotplug_slot_info),
520 GFP_KERNEL);
521 if (!bss_hotplug_slot->info) {
522 rc = -ENOMEM;
523 goto alloc_err;
524 }
525
526 if (sn_hp_slot_private_alloc(bss_hotplug_slot,
527 pci_bus, device)) {
528 rc = -ENOMEM;
529 goto alloc_err;
530 }
531
532 bss_hotplug_slot->ops = &sn_hotplug_slot_ops;
533 bss_hotplug_slot->release = &sn_release_slot;
534
535 rc = pci_hp_register(bss_hotplug_slot);
536 if (rc)
537 goto register_err;
538 }
539 dev_dbg(pci_bus->self, "Registered bus with hotplug\n");
540 return rc;
541
542register_err:
543 dev_dbg(pci_bus->self, "bus failed to register with err = %d\n",
544 rc);
545
546alloc_err:
547 if (rc == -ENOMEM)
548 dev_dbg(pci_bus->self, "Memory allocation error\n");
549
550 /* destroy THIS element */
551 if (bss_hotplug_slot)
552 sn_release_slot(bss_hotplug_slot);
553
554 /* destroy anything else on the list */
555 while ((bss_hotplug_slot = sn_hp_destroy()))
556 pci_hp_deregister(bss_hotplug_slot);
557
558 return rc;
559}
560
561static int sn_pci_hotplug_init(void)
562{
563 struct pci_bus *pci_bus = NULL;
564 int rc;
565 int registered = 0;
566
567 INIT_LIST_HEAD(&sn_hp_list);
568
569 if (sn_sal_rev() < SGI_HOTPLUG_PROM_REV) {
570 printk(KERN_ERR "%s: PROM version must be greater than 4.05\n",
571 __FUNCTION__);
572 return -EPERM;
573 }
574
575 while ((pci_bus = pci_find_next_bus(pci_bus))) {
576 if (!pci_bus->sysdata)
577 continue;
578
579 rc = sn_pci_bus_valid(pci_bus);
580 if (rc != 1) {
581 dev_dbg(pci_bus->self, "not a valid hotplug bus\n");
582 continue;
583 }
584 dev_dbg(pci_bus->self, "valid hotplug bus\n");
585
586 rc = sn_hotplug_slot_register(pci_bus);
587 if (!rc)
588 registered = 1;
589 else {
590 registered = 0;
591 break;
592 }
593 }
594
595 return registered == 1 ? 0 : -ENODEV;
596}
597
598static void sn_pci_hotplug_exit(void)
599{
600 struct hotplug_slot *bss_hotplug_slot;
601
602 while ((bss_hotplug_slot = sn_hp_destroy())) {
603 pci_hp_deregister(bss_hotplug_slot);
604 }
605
606 if (!list_empty(&sn_hp_list))
607 printk(KERN_ERR "%s: internal list is not empty\n", __FILE__);
608}
609
610module_init(sn_pci_hotplug_init);
611module_exit(sn_pci_hotplug_exit);
diff --git a/include/asm-ia64/sn/pcibr_provider.h b/include/asm-ia64/sn/pcibr_provider.h
index cbb4604c9349..2299c3ad2e33 100644
--- a/include/asm-ia64/sn/pcibr_provider.h
+++ b/include/asm-ia64/sn/pcibr_provider.h
@@ -151,4 +151,8 @@ extern void pcibr_change_devices_irq(struct sn_irq_info *sn_irq_info);
151extern int pcibr_ate_alloc(struct pcibus_info *, int); 151extern int pcibr_ate_alloc(struct pcibus_info *, int);
152extern void pcibr_ate_free(struct pcibus_info *, int); 152extern void pcibr_ate_free(struct pcibus_info *, int);
153extern void ate_write(struct pcibus_info *, int, int, uint64_t); 153extern void ate_write(struct pcibus_info *, int, int, uint64_t);
154extern int sal_pcibr_slot_enable(struct pcibus_info *soft, int device,
155 void *resp);
156extern int sal_pcibr_slot_disable(struct pcibus_info *soft, int device,
157 int action, void *resp);
154#endif 158#endif
diff --git a/include/asm-ia64/sn/pcidev.h b/include/asm-ia64/sn/pcidev.h
index 9610fcc63545..49711d00ad04 100644
--- a/include/asm-ia64/sn/pcidev.h
+++ b/include/asm-ia64/sn/pcidev.h
@@ -23,6 +23,8 @@
23#define SN_PCIBUS_BUSSOFT(pci_bus) \ 23#define SN_PCIBUS_BUSSOFT(pci_bus) \
24 ((struct pcibus_bussoft *)(PCI_CONTROLLER((pci_bus))->platform_data)) 24 ((struct pcibus_bussoft *)(PCI_CONTROLLER((pci_bus))->platform_data))
25 25
26#define SN_PCIBUS_BUSSOFT_INFO(pci_bus) \
27 (struct pcibus_info *)((struct pcibus_bussoft *)(PCI_CONTROLLER((pci_bus))->platform_data))
26/* 28/*
27 * Given a struct pci_dev, return the sn pcibus_bussoft struct. Note 29 * Given a struct pci_dev, return the sn pcibus_bussoft struct. Note
28 * that this is not equivalent to SN_PCIBUS_BUSSOFT(pci_dev->bus) due 30 * that this is not equivalent to SN_PCIBUS_BUSSOFT(pci_dev->bus) due
@@ -56,6 +58,10 @@ struct pcidev_info {
56extern void sn_irq_fixup(struct pci_dev *pci_dev, 58extern void sn_irq_fixup(struct pci_dev *pci_dev,
57 struct sn_irq_info *sn_irq_info); 59 struct sn_irq_info *sn_irq_info);
58extern void sn_irq_unfixup(struct pci_dev *pci_dev); 60extern void sn_irq_unfixup(struct pci_dev *pci_dev);
61extern void sn_pci_controller_fixup(int segment, int busnum,
62 struct pci_bus *bus);
63extern void sn_bus_store_sysdata(struct pci_dev *dev);
64extern void sn_bus_free_sysdata(void);
59extern void sn_pci_fixup_slot(struct pci_dev *dev); 65extern void sn_pci_fixup_slot(struct pci_dev *dev);
60extern void sn_pci_unfixup_slot(struct pci_dev *dev); 66extern void sn_pci_unfixup_slot(struct pci_dev *dev);
61extern void sn_irq_lh_init(void); 67extern void sn_irq_lh_init(void);