xref: /openbmc/linux/drivers/pci/hotplug/rpaphp_core.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1736759efSBjorn Helgaas // SPDX-License-Identifier: GPL-2.0+
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * PCI Hot Plug Controller Driver for RPA-compliant PPC64 platform.
41da177e4SLinus Torvalds  * Copyright (C) 2003 Linda Xie <lxie@us.ibm.com>
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * All rights reserved.
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  * Send feedback to <lxie@us.ibm.com>
91da177e4SLinus Torvalds  *
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds #include <linux/kernel.h>
121da177e4SLinus Torvalds #include <linux/module.h>
131da177e4SLinus Torvalds #include <linux/moduleparam.h>
14b2851926SChristophe Leroy #include <linux/of.h>
151da177e4SLinus Torvalds #include <linux/pci.h>
167a54f25cSGreg Kroah-Hartman #include <linux/pci_hotplug.h>
171da177e4SLinus Torvalds #include <linux/smp.h>
181da177e4SLinus Torvalds #include <linux/init.h>
19b4a26be9SBenjamin Herrenschmidt #include <linux/vmalloc.h>
202fcf3ae5SMichael Bringmann #include <asm/firmware.h>
211da177e4SLinus Torvalds #include <asm/eeh.h>       /* for eeh_add_device() */
221da177e4SLinus Torvalds #include <asm/rtas.h>		/* rtas_call */
231da177e4SLinus Torvalds #include <asm/pci-bridge.h>	/* for pci_controller */
24b2851926SChristophe Leroy #include <asm/prom.h>
251da177e4SLinus Torvalds #include "../pci.h"		/* for pci_add_new_bus */
261da177e4SLinus Torvalds 				/* and pci_do_scan_bus */
271da177e4SLinus Torvalds #include "rpaphp.h"
281da177e4SLinus Torvalds 
2990ab5ee9SRusty Russell bool rpaphp_debug;
301da177e4SLinus Torvalds LIST_HEAD(rpaphp_slot_head);
31b7fe9434SRyan Desfosses EXPORT_SYMBOL_GPL(rpaphp_slot_head);
321da177e4SLinus Torvalds 
331da177e4SLinus Torvalds #define DRIVER_VERSION	"0.1"
341da177e4SLinus Torvalds #define DRIVER_AUTHOR	"Linda Xie <lxie@us.ibm.com>"
351da177e4SLinus Torvalds #define DRIVER_DESC	"RPA HOT Plug PCI Controller Driver"
361da177e4SLinus Torvalds 
371da177e4SLinus Torvalds #define MAX_LOC_CODE 128
381da177e4SLinus Torvalds 
391da177e4SLinus Torvalds MODULE_AUTHOR(DRIVER_AUTHOR);
401da177e4SLinus Torvalds MODULE_DESCRIPTION(DRIVER_DESC);
411da177e4SLinus Torvalds MODULE_LICENSE("GPL");
421da177e4SLinus Torvalds 
435d9bc1faSKristen Carlson Accardi module_param_named(debug, rpaphp_debug, bool, 0644);
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds /**
461da177e4SLinus Torvalds  * set_attention_status - set attention LED
4726e6c66eSRandy Dunlap  * @hotplug_slot: target &hotplug_slot
4826e6c66eSRandy Dunlap  * @value: LED control value
4926e6c66eSRandy Dunlap  *
501da177e4SLinus Torvalds  * echo 0 > attention -- set LED OFF
511da177e4SLinus Torvalds  * echo 1 > attention -- set LED ON
521da177e4SLinus Torvalds  * echo 2 > attention -- set LED ID(identify, light is blinking)
531da177e4SLinus Torvalds  */
set_attention_status(struct hotplug_slot * hotplug_slot,u8 value)541da177e4SLinus Torvalds static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value)
551da177e4SLinus Torvalds {
56c02929c2SLinas Vepstas 	int rc;
57125450f8SLukas Wunner 	struct slot *slot = to_slot(hotplug_slot);
581da177e4SLinus Torvalds 
591da177e4SLinus Torvalds 	switch (value) {
601da177e4SLinus Torvalds 	case 0:
611da177e4SLinus Torvalds 	case 1:
621da177e4SLinus Torvalds 	case 2:
63c02929c2SLinas Vepstas 		break;
64c02929c2SLinas Vepstas 	default:
65c02929c2SLinas Vepstas 		value = 1;
661da177e4SLinus Torvalds 		break;
671da177e4SLinus Torvalds 	}
68c02929c2SLinas Vepstas 
69c02929c2SLinas Vepstas 	rc = rtas_set_indicator(DR_INDICATOR, slot->index, value);
70c02929c2SLinas Vepstas 	if (!rc)
71a7da2161SLukas Wunner 		slot->attention_status = value;
72c02929c2SLinas Vepstas 
73c02929c2SLinas Vepstas 	return rc;
741da177e4SLinus Torvalds }
751da177e4SLinus Torvalds 
761da177e4SLinus Torvalds /**
771da177e4SLinus Torvalds  * get_power_status - get power status of a slot
781da177e4SLinus Torvalds  * @hotplug_slot: slot to get status
791da177e4SLinus Torvalds  * @value: pointer to store status
801da177e4SLinus Torvalds  */
get_power_status(struct hotplug_slot * hotplug_slot,u8 * value)811da177e4SLinus Torvalds static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
821da177e4SLinus Torvalds {
83427310ffSLinas Vepstas 	int retval, level;
84125450f8SLukas Wunner 	struct slot *slot = to_slot(hotplug_slot);
851da177e4SLinus Torvalds 
86427310ffSLinas Vepstas 	retval = rtas_get_power_level(slot->power_domain, &level);
87427310ffSLinas Vepstas 	if (!retval)
88427310ffSLinas Vepstas 		*value = level;
891da177e4SLinus Torvalds 	return retval;
901da177e4SLinus Torvalds }
911da177e4SLinus Torvalds 
921da177e4SLinus Torvalds /**
931da177e4SLinus Torvalds  * get_attention_status - get attention LED status
9426e6c66eSRandy Dunlap  * @hotplug_slot: slot to get status
9526e6c66eSRandy Dunlap  * @value: pointer to store status
961da177e4SLinus Torvalds  */
get_attention_status(struct hotplug_slot * hotplug_slot,u8 * value)971da177e4SLinus Torvalds static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
981da177e4SLinus Torvalds {
99125450f8SLukas Wunner 	struct slot *slot = to_slot(hotplug_slot);
100a7da2161SLukas Wunner 	*value = slot->attention_status;
101ebf42c0eSLinas Vepstas 	return 0;
1021da177e4SLinus Torvalds }
1031da177e4SLinus Torvalds 
get_adapter_status(struct hotplug_slot * hotplug_slot,u8 * value)1041da177e4SLinus Torvalds static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
1051da177e4SLinus Torvalds {
106125450f8SLukas Wunner 	struct slot *slot = to_slot(hotplug_slot);
107bf0af511SLinas Vepstas 	int rc, state;
1081da177e4SLinus Torvalds 
109bf0af511SLinas Vepstas 	rc = rpaphp_get_sensor_state(slot, &state);
110bf0af511SLinas Vepstas 
111bf0af511SLinas Vepstas 	*value = NOT_VALID;
112bf0af511SLinas Vepstas 	if (rc)
113bf0af511SLinas Vepstas 		return rc;
114bf0af511SLinas Vepstas 
115bf0af511SLinas Vepstas 	if (state == EMPTY)
116bf0af511SLinas Vepstas 		*value = EMPTY;
117bf0af511SLinas Vepstas 	else if (state == PRESENT)
118bf0af511SLinas Vepstas 		*value = slot->state;
119bf0af511SLinas Vepstas 
120bf0af511SLinas Vepstas 	return 0;
1211da177e4SLinus Torvalds }
1221da177e4SLinus Torvalds 
get_max_bus_speed(struct slot * slot)1233749c51aSMatthew Wilcox static enum pci_bus_speed get_max_bus_speed(struct slot *slot)
1241da177e4SLinus Torvalds {
1253749c51aSMatthew Wilcox 	enum pci_bus_speed speed;
1261da177e4SLinus Torvalds 	switch (slot->type) {
1271da177e4SLinus Torvalds 	case 1:
1281da177e4SLinus Torvalds 	case 2:
1291da177e4SLinus Torvalds 	case 3:
1301da177e4SLinus Torvalds 	case 4:
1311da177e4SLinus Torvalds 	case 5:
1321da177e4SLinus Torvalds 	case 6:
1333749c51aSMatthew Wilcox 		speed = PCI_SPEED_33MHz;	/* speed for case 1-6 */
1341da177e4SLinus Torvalds 		break;
1351da177e4SLinus Torvalds 	case 7:
1361da177e4SLinus Torvalds 	case 8:
1373749c51aSMatthew Wilcox 		speed = PCI_SPEED_66MHz;
1381da177e4SLinus Torvalds 		break;
1391da177e4SLinus Torvalds 	case 11:
1401da177e4SLinus Torvalds 	case 14:
1413749c51aSMatthew Wilcox 		speed = PCI_SPEED_66MHz_PCIX;
1421da177e4SLinus Torvalds 		break;
1431da177e4SLinus Torvalds 	case 12:
1441da177e4SLinus Torvalds 	case 15:
1453749c51aSMatthew Wilcox 		speed = PCI_SPEED_100MHz_PCIX;
1461da177e4SLinus Torvalds 		break;
1471da177e4SLinus Torvalds 	case 13:
1481da177e4SLinus Torvalds 	case 16:
1493749c51aSMatthew Wilcox 		speed = PCI_SPEED_133MHz_PCIX;
1501da177e4SLinus Torvalds 		break;
1511da177e4SLinus Torvalds 	default:
1523749c51aSMatthew Wilcox 		speed = PCI_SPEED_UNKNOWN;
1531da177e4SLinus Torvalds 		break;
1541da177e4SLinus Torvalds 	}
1553749c51aSMatthew Wilcox 
1563749c51aSMatthew Wilcox 	return speed;
1571da177e4SLinus Torvalds }
1581da177e4SLinus Torvalds 
get_children_props(struct device_node * dn,const __be32 ** drc_indexes,const __be32 ** drc_names,const __be32 ** drc_types,const __be32 ** drc_power_domains)15907376867STyrel Datwyler static int get_children_props(struct device_node *dn, const __be32 **drc_indexes,
16007376867STyrel Datwyler 			      const __be32 **drc_names, const __be32 **drc_types,
16107376867STyrel Datwyler 			      const __be32 **drc_power_domains)
1621da177e4SLinus Torvalds {
16307376867STyrel Datwyler 	const __be32 *indexes, *names, *types, *domains;
1641da177e4SLinus Torvalds 
16540cd3a45SStephen Rothwell 	indexes = of_get_property(dn, "ibm,drc-indexes", NULL);
16640cd3a45SStephen Rothwell 	names = of_get_property(dn, "ibm,drc-names", NULL);
16740cd3a45SStephen Rothwell 	types = of_get_property(dn, "ibm,drc-types", NULL);
16840cd3a45SStephen Rothwell 	domains = of_get_property(dn, "ibm,drc-power-domains", NULL);
1691da177e4SLinus Torvalds 
1701da177e4SLinus Torvalds 	if (!indexes || !names || !types || !domains) {
1711da177e4SLinus Torvalds 		/* Slot does not have dynamically-removable children */
1721da177e4SLinus Torvalds 		return -EINVAL;
1731da177e4SLinus Torvalds 	}
1741da177e4SLinus Torvalds 	if (drc_indexes)
1751da177e4SLinus Torvalds 		*drc_indexes = indexes;
1761da177e4SLinus Torvalds 	if (drc_names)
1771da177e4SLinus Torvalds 		/* &drc_names[1] contains NULL terminated slot names */
1781da177e4SLinus Torvalds 		*drc_names = names;
1791da177e4SLinus Torvalds 	if (drc_types)
1801da177e4SLinus Torvalds 		/* &drc_types[1] contains NULL terminated slot types */
1811da177e4SLinus Torvalds 		*drc_types = types;
1821da177e4SLinus Torvalds 	if (drc_power_domains)
1831da177e4SLinus Torvalds 		*drc_power_domains = domains;
1841da177e4SLinus Torvalds 
1851da177e4SLinus Torvalds 	return 0;
1861da177e4SLinus Torvalds }
1871da177e4SLinus Torvalds 
1882fcf3ae5SMichael Bringmann 
1892fcf3ae5SMichael Bringmann /* Verify the existence of 'drc_name' and/or 'drc_type' within the
1909d09e5a9SBjorn Helgaas  * current node.  First obtain its my-drc-index property.  Next,
1919d09e5a9SBjorn Helgaas  * obtain the DRC info from its parent.  Use the my-drc-index for
1922fcf3ae5SMichael Bringmann  * correlation, and obtain/validate the requested properties.
1931da177e4SLinus Torvalds  */
1942fcf3ae5SMichael Bringmann 
rpaphp_check_drc_props_v1(struct device_node * dn,char * drc_name,char * drc_type,unsigned int my_index)1952fcf3ae5SMichael Bringmann static int rpaphp_check_drc_props_v1(struct device_node *dn, char *drc_name,
1962fcf3ae5SMichael Bringmann 				char *drc_type, unsigned int my_index)
1971da177e4SLinus Torvalds {
1982fcf3ae5SMichael Bringmann 	char *name_tmp, *type_tmp;
19907376867STyrel Datwyler 	const __be32 *indexes, *names;
20007376867STyrel Datwyler 	const __be32 *types, *domains;
2011da177e4SLinus Torvalds 	int i, rc;
2021da177e4SLinus Torvalds 
2031da177e4SLinus Torvalds 	rc = get_children_props(dn->parent, &indexes, &names, &types, &domains);
2041da177e4SLinus Torvalds 	if (rc < 0) {
2051da177e4SLinus Torvalds 		return -EINVAL;
2061da177e4SLinus Torvalds 	}
2071da177e4SLinus Torvalds 
2081da177e4SLinus Torvalds 	name_tmp = (char *) &names[1];
2091da177e4SLinus Torvalds 	type_tmp = (char *) &types[1];
2101da177e4SLinus Torvalds 
2111da177e4SLinus Torvalds 	/* Iterate through parent properties, looking for my-drc-index */
212761ce533SLaurent Dufour 	for (i = 0; i < be32_to_cpu(indexes[0]); i++) {
21307376867STyrel Datwyler 		if (be32_to_cpu(indexes[i + 1]) == my_index)
2142fcf3ae5SMichael Bringmann 			break;
2152fcf3ae5SMichael Bringmann 
2161da177e4SLinus Torvalds 		name_tmp += (strlen(name_tmp) + 1);
2171da177e4SLinus Torvalds 		type_tmp += (strlen(type_tmp) + 1);
2181da177e4SLinus Torvalds 	}
2191da177e4SLinus Torvalds 
2202fcf3ae5SMichael Bringmann 	if (((drc_name == NULL) || (drc_name && !strcmp(drc_name, name_tmp))) &&
2212fcf3ae5SMichael Bringmann 	    ((drc_type == NULL) || (drc_type && !strcmp(drc_type, type_tmp))))
2222fcf3ae5SMichael Bringmann 		return 0;
2232fcf3ae5SMichael Bringmann 
2241da177e4SLinus Torvalds 	return -EINVAL;
2251da177e4SLinus Torvalds }
2262fcf3ae5SMichael Bringmann 
rpaphp_check_drc_props_v2(struct device_node * dn,char * drc_name,char * drc_type,unsigned int my_index)2272fcf3ae5SMichael Bringmann static int rpaphp_check_drc_props_v2(struct device_node *dn, char *drc_name,
2282fcf3ae5SMichael Bringmann 				char *drc_type, unsigned int my_index)
2292fcf3ae5SMichael Bringmann {
2302fcf3ae5SMichael Bringmann 	struct property *info;
2312fcf3ae5SMichael Bringmann 	unsigned int entries;
2322fcf3ae5SMichael Bringmann 	struct of_drc_info drc;
2332fcf3ae5SMichael Bringmann 	const __be32 *value;
2342fcf3ae5SMichael Bringmann 	char cell_drc_name[MAX_DRC_NAME_LEN];
2350df3e421SNathan Chancellor 	int j;
2362fcf3ae5SMichael Bringmann 
2372fcf3ae5SMichael Bringmann 	info = of_find_property(dn->parent, "ibm,drc-info", NULL);
2382fcf3ae5SMichael Bringmann 	if (info == NULL)
2392fcf3ae5SMichael Bringmann 		return -EINVAL;
2402fcf3ae5SMichael Bringmann 
2412fcf3ae5SMichael Bringmann 	value = of_prop_next_u32(info, NULL, &entries);
2422fcf3ae5SMichael Bringmann 	if (!value)
2432fcf3ae5SMichael Bringmann 		return -EINVAL;
2449723c25fSTyrel Datwyler 	else
2459723c25fSTyrel Datwyler 		value++;
2462fcf3ae5SMichael Bringmann 
2472fcf3ae5SMichael Bringmann 	for (j = 0; j < entries; j++) {
2482fcf3ae5SMichael Bringmann 		of_read_drc_info_cell(&info, &value, &drc);
2492fcf3ae5SMichael Bringmann 
2502fcf3ae5SMichael Bringmann 		/* Should now know end of current entry */
2512fcf3ae5SMichael Bringmann 
2522fcf3ae5SMichael Bringmann 		/* Found it */
2534f9f2d3dSTyrel Datwyler 		if (my_index >= drc.drc_index_start && my_index <= drc.last_drc_index) {
2544f9f2d3dSTyrel Datwyler 			int index = my_index - drc.drc_index_start;
2552fcf3ae5SMichael Bringmann 			sprintf(cell_drc_name, "%s%d", drc.drc_name_prefix,
2564f9f2d3dSTyrel Datwyler 				drc.drc_name_suffix_start + index);
2570df3e421SNathan Chancellor 			break;
2580df3e421SNathan Chancellor 		}
2590df3e421SNathan Chancellor 	}
2602fcf3ae5SMichael Bringmann 
2612fcf3ae5SMichael Bringmann 	if (((drc_name == NULL) ||
2622fcf3ae5SMichael Bringmann 	     (drc_name && !strcmp(drc_name, cell_drc_name))) &&
2632fcf3ae5SMichael Bringmann 	    ((drc_type == NULL) ||
2642fcf3ae5SMichael Bringmann 	     (drc_type && !strcmp(drc_type, drc.drc_type))))
2652fcf3ae5SMichael Bringmann 		return 0;
2662fcf3ae5SMichael Bringmann 
2672fcf3ae5SMichael Bringmann 	return -EINVAL;
2682fcf3ae5SMichael Bringmann }
2692fcf3ae5SMichael Bringmann 
rpaphp_check_drc_props(struct device_node * dn,char * drc_name,char * drc_type)2702fcf3ae5SMichael Bringmann int rpaphp_check_drc_props(struct device_node *dn, char *drc_name,
2712fcf3ae5SMichael Bringmann 			char *drc_type)
2722fcf3ae5SMichael Bringmann {
27307376867STyrel Datwyler 	const __be32 *my_index;
2742fcf3ae5SMichael Bringmann 
2752fcf3ae5SMichael Bringmann 	my_index = of_get_property(dn, "ibm,my-drc-index", NULL);
2762fcf3ae5SMichael Bringmann 	if (!my_index) {
2772fcf3ae5SMichael Bringmann 		/* Node isn't DLPAR/hotplug capable */
2782fcf3ae5SMichael Bringmann 		return -EINVAL;
2792fcf3ae5SMichael Bringmann 	}
2802fcf3ae5SMichael Bringmann 
281*9195ee1aSRob Herring 	if (of_property_present(dn->parent, "ibm,drc-info"))
2822fcf3ae5SMichael Bringmann 		return rpaphp_check_drc_props_v2(dn, drc_name, drc_type,
28307376867STyrel Datwyler 						be32_to_cpu(*my_index));
2842fcf3ae5SMichael Bringmann 	else
2852fcf3ae5SMichael Bringmann 		return rpaphp_check_drc_props_v1(dn, drc_name, drc_type,
28607376867STyrel Datwyler 						be32_to_cpu(*my_index));
2872fcf3ae5SMichael Bringmann }
2882fcf3ae5SMichael Bringmann EXPORT_SYMBOL_GPL(rpaphp_check_drc_props);
2892fcf3ae5SMichael Bringmann 
2901da177e4SLinus Torvalds 
is_php_type(char * drc_type)2911da177e4SLinus Torvalds static int is_php_type(char *drc_type)
2921da177e4SLinus Torvalds {
2931da177e4SLinus Torvalds 	char *endptr;
2941da177e4SLinus Torvalds 
2951da177e4SLinus Torvalds 	/* PCI Hotplug nodes have an integer for drc_type */
2969475af08SChen Zhou 	simple_strtoul(drc_type, &endptr, 10);
2971da177e4SLinus Torvalds 	if (endptr == drc_type)
2981da177e4SLinus Torvalds 		return 0;
2991da177e4SLinus Torvalds 
3001da177e4SLinus Torvalds 	return 1;
3011da177e4SLinus Torvalds }
3021da177e4SLinus Torvalds 
303da65944bSLinas Vepstas /**
304da65944bSLinas Vepstas  * is_php_dn() - return 1 if this is a hotpluggable pci slot, else 0
30526e6c66eSRandy Dunlap  * @dn: target &device_node
30626e6c66eSRandy Dunlap  * @indexes: passed to get_children_props()
30726e6c66eSRandy Dunlap  * @names: passed to get_children_props()
30826e6c66eSRandy Dunlap  * @types: returned from get_children_props()
30926e6c66eSRandy Dunlap  * @power_domains:
310da65944bSLinas Vepstas  *
311da65944bSLinas Vepstas  * This routine will return true only if the device node is
312da65944bSLinas Vepstas  * a hotpluggable slot. This routine will return false
313da65944bSLinas Vepstas  * for built-in pci slots (even when the built-in slots are
314da65944bSLinas Vepstas  * dlparable.)
315da65944bSLinas Vepstas  */
is_php_dn(struct device_node * dn,const __be32 ** indexes,const __be32 ** names,const __be32 ** types,const __be32 ** power_domains)31607376867STyrel Datwyler static int is_php_dn(struct device_node *dn, const __be32 **indexes,
31707376867STyrel Datwyler 		     const __be32 **names, const __be32 **types,
31807376867STyrel Datwyler 		     const __be32 **power_domains)
3191da177e4SLinus Torvalds {
32007376867STyrel Datwyler 	const __be32 *drc_types;
3211da177e4SLinus Torvalds 	int rc;
3221da177e4SLinus Torvalds 
3231da177e4SLinus Torvalds 	rc = get_children_props(dn, indexes, names, &drc_types, power_domains);
324da65944bSLinas Vepstas 	if (rc < 0)
325da65944bSLinas Vepstas 		return 0;
326da65944bSLinas Vepstas 
327da65944bSLinas Vepstas 	if (!is_php_type((char *) &drc_types[1]))
328da65944bSLinas Vepstas 		return 0;
329da65944bSLinas Vepstas 
3301da177e4SLinus Torvalds 	*types = drc_types;
3311da177e4SLinus Torvalds 	return 1;
3321da177e4SLinus Torvalds }
3331da177e4SLinus Torvalds 
rpaphp_drc_info_add_slot(struct device_node * dn)334efeda8faSTyrel Datwyler static int rpaphp_drc_info_add_slot(struct device_node *dn)
335efeda8faSTyrel Datwyler {
336efeda8faSTyrel Datwyler 	struct slot *slot;
337efeda8faSTyrel Datwyler 	struct property *info;
338efeda8faSTyrel Datwyler 	struct of_drc_info drc;
339efeda8faSTyrel Datwyler 	char drc_name[MAX_DRC_NAME_LEN];
340efeda8faSTyrel Datwyler 	const __be32 *cur;
341efeda8faSTyrel Datwyler 	u32 count;
342efeda8faSTyrel Datwyler 	int retval = 0;
343efeda8faSTyrel Datwyler 
344efeda8faSTyrel Datwyler 	info = of_find_property(dn, "ibm,drc-info", NULL);
345efeda8faSTyrel Datwyler 	if (!info)
346efeda8faSTyrel Datwyler 		return 0;
347efeda8faSTyrel Datwyler 
348efeda8faSTyrel Datwyler 	cur = of_prop_next_u32(info, NULL, &count);
349efeda8faSTyrel Datwyler 	if (cur)
350efeda8faSTyrel Datwyler 		cur++;
351efeda8faSTyrel Datwyler 	else
352efeda8faSTyrel Datwyler 		return 0;
353efeda8faSTyrel Datwyler 
354efeda8faSTyrel Datwyler 	of_read_drc_info_cell(&info, &cur, &drc);
355efeda8faSTyrel Datwyler 	if (!is_php_type(drc.drc_type))
356efeda8faSTyrel Datwyler 		return 0;
357efeda8faSTyrel Datwyler 
358efeda8faSTyrel Datwyler 	sprintf(drc_name, "%s%d", drc.drc_name_prefix, drc.drc_name_suffix_start);
359efeda8faSTyrel Datwyler 
360efeda8faSTyrel Datwyler 	slot = alloc_slot_struct(dn, drc.drc_index_start, drc_name, drc.drc_power_domain);
361efeda8faSTyrel Datwyler 	if (!slot)
362efeda8faSTyrel Datwyler 		return -ENOMEM;
363efeda8faSTyrel Datwyler 
364efeda8faSTyrel Datwyler 	slot->type = simple_strtoul(drc.drc_type, NULL, 10);
365efeda8faSTyrel Datwyler 	retval = rpaphp_enable_slot(slot);
366efeda8faSTyrel Datwyler 	if (!retval)
367efeda8faSTyrel Datwyler 		retval = rpaphp_register_slot(slot);
368efeda8faSTyrel Datwyler 
369efeda8faSTyrel Datwyler 	if (retval)
370efeda8faSTyrel Datwyler 		dealloc_slot_struct(slot);
371efeda8faSTyrel Datwyler 
372efeda8faSTyrel Datwyler 	return retval;
373efeda8faSTyrel Datwyler }
374efeda8faSTyrel Datwyler 
rpaphp_drc_add_slot(struct device_node * dn)375efeda8faSTyrel Datwyler static int rpaphp_drc_add_slot(struct device_node *dn)
3761da177e4SLinus Torvalds {
3771da177e4SLinus Torvalds 	struct slot *slot;
3781da177e4SLinus Torvalds 	int retval = 0;
37956d8456bSJohn Rose 	int i;
38007376867STyrel Datwyler 	const __be32 *indexes, *names, *types, *power_domains;
3811da177e4SLinus Torvalds 	char *name, *type;
3821da177e4SLinus Torvalds 
383da65944bSLinas Vepstas 	/* If this is not a hotplug slot, return without doing anything. */
384bf8cbae4SLinas Vepstas 	if (!is_php_dn(dn, &indexes, &names, &types, &power_domains))
385bf8cbae4SLinas Vepstas 		return 0;
386bf8cbae4SLinas Vepstas 
387b63773a8SRob Herring 	dbg("Entry %s: dn=%pOF\n", __func__, dn);
3881da177e4SLinus Torvalds 
3891da177e4SLinus Torvalds 	/* register PCI devices */
3901da177e4SLinus Torvalds 	name = (char *) &names[1];
3911da177e4SLinus Torvalds 	type = (char *) &types[1];
392761ce533SLaurent Dufour 	for (i = 0; i < be32_to_cpu(indexes[0]); i++) {
393761ce533SLaurent Dufour 		int index;
3941da177e4SLinus Torvalds 
395761ce533SLaurent Dufour 		index = be32_to_cpu(indexes[i + 1]);
396761ce533SLaurent Dufour 		slot = alloc_slot_struct(dn, index, name,
397761ce533SLaurent Dufour 					 be32_to_cpu(power_domains[i + 1]));
398bf8cbae4SLinas Vepstas 		if (!slot)
399bf8cbae4SLinas Vepstas 			return -ENOMEM;
400bf8cbae4SLinas Vepstas 
4011da177e4SLinus Torvalds 		slot->type = simple_strtoul(type, NULL, 10);
4021da177e4SLinus Torvalds 
4031da177e4SLinus Torvalds 		dbg("Found drc-index:0x%x drc-name:%s drc-type:%s\n",
404761ce533SLaurent Dufour 				index, name, type);
4051da177e4SLinus Torvalds 
406fea54b8cSLinas Vepstas 		retval = rpaphp_enable_slot(slot);
4076f79eb74SLinas Vepstas 		if (!retval)
4086f79eb74SLinas Vepstas 			retval = rpaphp_register_slot(slot);
4096f79eb74SLinas Vepstas 
41031be7586SLinas Vepstas 		if (retval)
41131be7586SLinas Vepstas 			dealloc_slot_struct(slot);
41231be7586SLinas Vepstas 
413bf8cbae4SLinas Vepstas 		name += strlen(name) + 1;
414bf8cbae4SLinas Vepstas 		type += strlen(type) + 1;
4151da177e4SLinus Torvalds 	}
41666bef8c0SHarvey Harrison 	dbg("%s - Exit: rc[%d]\n", __func__, retval);
41731be7586SLinas Vepstas 
41831be7586SLinas Vepstas 	/* XXX FIXME: reports a failure only if last entry in loop failed */
4191da177e4SLinus Torvalds 	return retval;
4201da177e4SLinus Torvalds }
421efeda8faSTyrel Datwyler 
422efeda8faSTyrel Datwyler /**
423efeda8faSTyrel Datwyler  * rpaphp_add_slot -- declare a hotplug slot to the hotplug subsystem.
424efeda8faSTyrel Datwyler  * @dn: device node of slot
425efeda8faSTyrel Datwyler  *
426efeda8faSTyrel Datwyler  * This subroutine will register a hotpluggable slot with the
427efeda8faSTyrel Datwyler  * PCI hotplug infrastructure. This routine is typically called
428efeda8faSTyrel Datwyler  * during boot time, if the hotplug slots are present at boot time,
429efeda8faSTyrel Datwyler  * or is called later, by the dlpar add code, if the slot is
430efeda8faSTyrel Datwyler  * being dynamically added during runtime.
431efeda8faSTyrel Datwyler  *
432efeda8faSTyrel Datwyler  * If the device node points at an embedded (built-in) slot, this
433efeda8faSTyrel Datwyler  * routine will just return without doing anything, since embedded
434efeda8faSTyrel Datwyler  * slots cannot be hotplugged.
435efeda8faSTyrel Datwyler  *
436efeda8faSTyrel Datwyler  * To remove a slot, it suffices to call rpaphp_deregister_slot().
437efeda8faSTyrel Datwyler  */
rpaphp_add_slot(struct device_node * dn)438efeda8faSTyrel Datwyler int rpaphp_add_slot(struct device_node *dn)
439efeda8faSTyrel Datwyler {
440adc9fbcdSRob Herring 	if (!of_node_name_eq(dn, "pci"))
441efeda8faSTyrel Datwyler 		return 0;
442efeda8faSTyrel Datwyler 
443*9195ee1aSRob Herring 	if (of_property_present(dn, "ibm,drc-info"))
444efeda8faSTyrel Datwyler 		return rpaphp_drc_info_add_slot(dn);
445efeda8faSTyrel Datwyler 	else
446efeda8faSTyrel Datwyler 		return rpaphp_drc_add_slot(dn);
447efeda8faSTyrel Datwyler }
448b7fe9434SRyan Desfosses EXPORT_SYMBOL_GPL(rpaphp_add_slot);
4491da177e4SLinus Torvalds 
cleanup_slots(void)4501da177e4SLinus Torvalds static void __exit cleanup_slots(void)
4511da177e4SLinus Torvalds {
4522ac83cccSGeliang Tang 	struct slot *slot, *next;
4531da177e4SLinus Torvalds 
4541da177e4SLinus Torvalds 	/*
4551da177e4SLinus Torvalds 	 * Unregister all of our slots with the pci_hotplug subsystem,
4561da177e4SLinus Torvalds 	 * and free up all memory that we had allocated.
4571da177e4SLinus Torvalds 	 */
4581da177e4SLinus Torvalds 
4592ac83cccSGeliang Tang 	list_for_each_entry_safe(slot, next, &rpaphp_slot_head,
4602ac83cccSGeliang Tang 				 rpaphp_slot_list) {
4611da177e4SLinus Torvalds 		list_del(&slot->rpaphp_slot_list);
462125450f8SLukas Wunner 		pci_hp_deregister(&slot->hotplug_slot);
46351bbf9beSLukas Wunner 		dealloc_slot_struct(slot);
4641da177e4SLinus Torvalds 	}
4651da177e4SLinus Torvalds }
4661da177e4SLinus Torvalds 
rpaphp_init(void)4671da177e4SLinus Torvalds static int __init rpaphp_init(void)
4681da177e4SLinus Torvalds {
469ccdb8ed3SGrant Likely 	struct device_node *dn;
4701da177e4SLinus Torvalds 
4715eeb8c63SJohn Rose 	info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
4725eeb8c63SJohn Rose 
473ccdb8ed3SGrant Likely 	for_each_node_by_name(dn, "pci")
4745eeb8c63SJohn Rose 		rpaphp_add_slot(dn);
4755eeb8c63SJohn Rose 
4765eeb8c63SJohn Rose 	return 0;
4771da177e4SLinus Torvalds }
4781da177e4SLinus Torvalds 
rpaphp_exit(void)4791da177e4SLinus Torvalds static void __exit rpaphp_exit(void)
4801da177e4SLinus Torvalds {
4811da177e4SLinus Torvalds 	cleanup_slots();
4821da177e4SLinus Torvalds }
4831da177e4SLinus Torvalds 
enable_slot(struct hotplug_slot * hotplug_slot)484ac1f0e99SLinas Vepstas static int enable_slot(struct hotplug_slot *hotplug_slot)
4851da177e4SLinus Torvalds {
486125450f8SLukas Wunner 	struct slot *slot = to_slot(hotplug_slot);
487e06b80b7Slinas@austin.ibm.com 	int state;
488e06b80b7Slinas@austin.ibm.com 	int retval;
4891da177e4SLinus Torvalds 
490e06b80b7Slinas@austin.ibm.com 	if (slot->state == CONFIGURED)
491e06b80b7Slinas@austin.ibm.com 		return 0;
492e06b80b7Slinas@austin.ibm.com 
493e06b80b7Slinas@austin.ibm.com 	retval = rpaphp_get_sensor_state(slot, &state);
494e06b80b7Slinas@austin.ibm.com 	if (retval)
495e06b80b7Slinas@austin.ibm.com 		return retval;
496e06b80b7Slinas@austin.ibm.com 
497e06b80b7Slinas@austin.ibm.com 	if (state == PRESENT) {
498b6eebb09SOliver O'Halloran 		pseries_eeh_init_edev_recursive(PCI_DN(slot->dn));
499a4b4f61dSOliver O'Halloran 
500c4ec84c7SRafael J. Wysocki 		pci_lock_rescan_remove();
501bd251b89SGavin Shan 		pci_hp_add_devices(slot->bus);
502c4ec84c7SRafael J. Wysocki 		pci_unlock_rescan_remove();
503e06b80b7Slinas@austin.ibm.com 		slot->state = CONFIGURED;
504e06b80b7Slinas@austin.ibm.com 	} else if (state == EMPTY) {
505e06b80b7Slinas@austin.ibm.com 		slot->state = EMPTY;
506e06b80b7Slinas@austin.ibm.com 	} else {
50766bef8c0SHarvey Harrison 		err("%s: slot[%s] is in invalid state\n", __func__, slot->name);
508e06b80b7Slinas@austin.ibm.com 		slot->state = NOT_VALID;
509e06b80b7Slinas@austin.ibm.com 		return -EINVAL;
510e06b80b7Slinas@austin.ibm.com 	}
5113749c51aSMatthew Wilcox 
5123749c51aSMatthew Wilcox 	slot->bus->max_bus_speed = get_max_bus_speed(slot);
513e06b80b7Slinas@austin.ibm.com 	return 0;
5141da177e4SLinus Torvalds }
5151da177e4SLinus Torvalds 
disable_slot(struct hotplug_slot * hotplug_slot)516ac1f0e99SLinas Vepstas static int disable_slot(struct hotplug_slot *hotplug_slot)
517e06b80b7Slinas@austin.ibm.com {
518125450f8SLukas Wunner 	struct slot *slot = to_slot(hotplug_slot);
5198fe64399Slinas@austin.ibm.com 	if (slot->state == NOT_CONFIGURED)
5208fe64399Slinas@austin.ibm.com 		return -EINVAL;
5211da177e4SLinus Torvalds 
522c4ec84c7SRafael J. Wysocki 	pci_lock_rescan_remove();
523bd251b89SGavin Shan 	pci_hp_remove_devices(slot->bus);
524c4ec84c7SRafael J. Wysocki 	pci_unlock_rescan_remove();
525b4a26be9SBenjamin Herrenschmidt 	vm_unmap_aliases();
526b4a26be9SBenjamin Herrenschmidt 
527934199e9Slinas 	slot->state = NOT_CONFIGURED;
5288fe64399Slinas@austin.ibm.com 	return 0;
5298fe64399Slinas@austin.ibm.com }
5308fe64399Slinas@austin.ibm.com 
53181c4b5bfSLukas Wunner const struct hotplug_slot_ops rpaphp_hotplug_slot_ops = {
532b64a71abSlinas@austin.ibm.com 	.enable_slot = enable_slot,
533b64a71abSlinas@austin.ibm.com 	.disable_slot = disable_slot,
534b64a71abSlinas@austin.ibm.com 	.set_attention_status = set_attention_status,
535b64a71abSlinas@austin.ibm.com 	.get_power_status = get_power_status,
536b64a71abSlinas@austin.ibm.com 	.get_attention_status = get_attention_status,
537b64a71abSlinas@austin.ibm.com 	.get_adapter_status = get_adapter_status,
538b64a71abSlinas@austin.ibm.com };
539b64a71abSlinas@austin.ibm.com 
5401da177e4SLinus Torvalds module_init(rpaphp_init);
5411da177e4SLinus Torvalds module_exit(rpaphp_exit);
542