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