542 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * PCI Hot Plug Controller Driver for RPA-compliant PPC64 platform.4 * Copyright (C) 2003 Linda Xie <lxie@us.ibm.com>5 *6 * All rights reserved.7 *8 * Send feedback to <lxie@us.ibm.com>9 *10 */11#include <linux/kernel.h>12#include <linux/module.h>13#include <linux/moduleparam.h>14#include <linux/of.h>15#include <linux/pci.h>16#include <linux/pci_hotplug.h>17#include <linux/smp.h>18#include <linux/init.h>19#include <linux/vmalloc.h>20#include <asm/firmware.h>21#include <asm/eeh.h> /* for eeh_add_device() */22#include <asm/rtas.h> /* rtas_call */23#include <asm/pci-bridge.h> /* for pci_controller */24#include <asm/prom.h>25#include "../pci.h" /* for pci_add_new_bus */26 /* and pci_do_scan_bus */27#include "rpaphp.h"28 29bool rpaphp_debug;30LIST_HEAD(rpaphp_slot_head);31EXPORT_SYMBOL_GPL(rpaphp_slot_head);32 33#define DRIVER_VERSION "0.1"34#define DRIVER_AUTHOR "Linda Xie <lxie@us.ibm.com>"35#define DRIVER_DESC "RPA HOT Plug PCI Controller Driver"36 37#define MAX_LOC_CODE 12838 39MODULE_AUTHOR(DRIVER_AUTHOR);40MODULE_DESCRIPTION(DRIVER_DESC);41MODULE_LICENSE("GPL");42 43module_param_named(debug, rpaphp_debug, bool, 0644);44 45/**46 * set_attention_status - set attention LED47 * @hotplug_slot: target &hotplug_slot48 * @value: LED control value49 *50 * echo 0 > attention -- set LED OFF51 * echo 1 > attention -- set LED ON52 * echo 2 > attention -- set LED ID(identify, light is blinking)53 */54static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value)55{56 int rc;57 struct slot *slot = to_slot(hotplug_slot);58 59 switch (value) {60 case 0:61 case 1:62 case 2:63 break;64 default:65 value = 1;66 break;67 }68 69 rc = rtas_set_indicator(DR_INDICATOR, slot->index, value);70 if (!rc)71 slot->attention_status = value;72 73 return rc;74}75 76/**77 * get_power_status - get power status of a slot78 * @hotplug_slot: slot to get status79 * @value: pointer to store status80 */81static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)82{83 int retval, level;84 struct slot *slot = to_slot(hotplug_slot);85 86 retval = rtas_get_power_level(slot->power_domain, &level);87 if (!retval)88 *value = level;89 return retval;90}91 92/**93 * get_attention_status - get attention LED status94 * @hotplug_slot: slot to get status95 * @value: pointer to store status96 */97static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)98{99 struct slot *slot = to_slot(hotplug_slot);100 *value = slot->attention_status;101 return 0;102}103 104static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)105{106 struct slot *slot = to_slot(hotplug_slot);107 int rc, state;108 109 rc = rpaphp_get_sensor_state(slot, &state);110 111 *value = NOT_VALID;112 if (rc)113 return rc;114 115 if (state == EMPTY)116 *value = EMPTY;117 else if (state == PRESENT)118 *value = slot->state;119 120 return 0;121}122 123static enum pci_bus_speed get_max_bus_speed(struct slot *slot)124{125 enum pci_bus_speed speed;126 switch (slot->type) {127 case 1:128 case 2:129 case 3:130 case 4:131 case 5:132 case 6:133 speed = PCI_SPEED_33MHz; /* speed for case 1-6 */134 break;135 case 7:136 case 8:137 speed = PCI_SPEED_66MHz;138 break;139 case 11:140 case 14:141 speed = PCI_SPEED_66MHz_PCIX;142 break;143 case 12:144 case 15:145 speed = PCI_SPEED_100MHz_PCIX;146 break;147 case 13:148 case 16:149 speed = PCI_SPEED_133MHz_PCIX;150 break;151 default:152 speed = PCI_SPEED_UNKNOWN;153 break;154 }155 156 return speed;157}158 159static int get_children_props(struct device_node *dn, const __be32 **drc_indexes,160 const __be32 **drc_names, const __be32 **drc_types,161 const __be32 **drc_power_domains)162{163 const __be32 *indexes, *names, *types, *domains;164 165 indexes = of_get_property(dn, "ibm,drc-indexes", NULL);166 names = of_get_property(dn, "ibm,drc-names", NULL);167 types = of_get_property(dn, "ibm,drc-types", NULL);168 domains = of_get_property(dn, "ibm,drc-power-domains", NULL);169 170 if (!indexes || !names || !types || !domains) {171 /* Slot does not have dynamically-removable children */172 return -EINVAL;173 }174 if (drc_indexes)175 *drc_indexes = indexes;176 if (drc_names)177 /* &drc_names[1] contains NULL terminated slot names */178 *drc_names = names;179 if (drc_types)180 /* &drc_types[1] contains NULL terminated slot types */181 *drc_types = types;182 if (drc_power_domains)183 *drc_power_domains = domains;184 185 return 0;186}187 188 189/* Verify the existence of 'drc_name' and/or 'drc_type' within the190 * current node. First obtain its my-drc-index property. Next,191 * obtain the DRC info from its parent. Use the my-drc-index for192 * correlation, and obtain/validate the requested properties.193 */194 195static int rpaphp_check_drc_props_v1(struct device_node *dn, char *drc_name,196 char *drc_type, unsigned int my_index)197{198 char *name_tmp, *type_tmp;199 const __be32 *indexes, *names;200 const __be32 *types, *domains;201 int i, rc;202 203 rc = get_children_props(dn->parent, &indexes, &names, &types, &domains);204 if (rc < 0) {205 return -EINVAL;206 }207 208 name_tmp = (char *) &names[1];209 type_tmp = (char *) &types[1];210 211 /* Iterate through parent properties, looking for my-drc-index */212 for (i = 0; i < be32_to_cpu(indexes[0]); i++) {213 if (be32_to_cpu(indexes[i + 1]) == my_index)214 break;215 216 name_tmp += (strlen(name_tmp) + 1);217 type_tmp += (strlen(type_tmp) + 1);218 }219 220 if (((drc_name == NULL) || (drc_name && !strcmp(drc_name, name_tmp))) &&221 ((drc_type == NULL) || (drc_type && !strcmp(drc_type, type_tmp))))222 return 0;223 224 return -EINVAL;225}226 227static int rpaphp_check_drc_props_v2(struct device_node *dn, char *drc_name,228 char *drc_type, unsigned int my_index)229{230 struct property *info;231 unsigned int entries;232 struct of_drc_info drc;233 const __be32 *value;234 char cell_drc_name[MAX_DRC_NAME_LEN];235 int j;236 237 info = of_find_property(dn->parent, "ibm,drc-info", NULL);238 if (info == NULL)239 return -EINVAL;240 241 value = of_prop_next_u32(info, NULL, &entries);242 if (!value)243 return -EINVAL;244 else245 value++;246 247 for (j = 0; j < entries; j++) {248 of_read_drc_info_cell(&info, &value, &drc);249 250 /* Should now know end of current entry */251 252 /* Found it */253 if (my_index >= drc.drc_index_start && my_index <= drc.last_drc_index) {254 int index = my_index - drc.drc_index_start;255 sprintf(cell_drc_name, "%s%d", drc.drc_name_prefix,256 drc.drc_name_suffix_start + index);257 break;258 }259 }260 261 if (((drc_name == NULL) ||262 (drc_name && !strcmp(drc_name, cell_drc_name))) &&263 ((drc_type == NULL) ||264 (drc_type && !strcmp(drc_type, drc.drc_type))))265 return 0;266 267 return -EINVAL;268}269 270int rpaphp_check_drc_props(struct device_node *dn, char *drc_name,271 char *drc_type)272{273 const __be32 *my_index;274 275 my_index = of_get_property(dn, "ibm,my-drc-index", NULL);276 if (!my_index) {277 /* Node isn't DLPAR/hotplug capable */278 return -EINVAL;279 }280 281 if (of_property_present(dn->parent, "ibm,drc-info"))282 return rpaphp_check_drc_props_v2(dn, drc_name, drc_type,283 be32_to_cpu(*my_index));284 else285 return rpaphp_check_drc_props_v1(dn, drc_name, drc_type,286 be32_to_cpu(*my_index));287}288EXPORT_SYMBOL_GPL(rpaphp_check_drc_props);289 290 291static int is_php_type(char *drc_type)292{293 char *endptr;294 295 /* PCI Hotplug nodes have an integer for drc_type */296 simple_strtoul(drc_type, &endptr, 10);297 if (endptr == drc_type)298 return 0;299 300 return 1;301}302 303/**304 * is_php_dn() - return 1 if this is a hotpluggable pci slot, else 0305 * @dn: target &device_node306 * @indexes: passed to get_children_props()307 * @names: passed to get_children_props()308 * @types: returned from get_children_props()309 * @power_domains:310 *311 * This routine will return true only if the device node is312 * a hotpluggable slot. This routine will return false313 * for built-in pci slots (even when the built-in slots are314 * dlparable.)315 */316static int is_php_dn(struct device_node *dn, const __be32 **indexes,317 const __be32 **names, const __be32 **types,318 const __be32 **power_domains)319{320 const __be32 *drc_types;321 int rc;322 323 rc = get_children_props(dn, indexes, names, &drc_types, power_domains);324 if (rc < 0)325 return 0;326 327 if (!is_php_type((char *) &drc_types[1]))328 return 0;329 330 *types = drc_types;331 return 1;332}333 334static int rpaphp_drc_info_add_slot(struct device_node *dn)335{336 struct slot *slot;337 struct property *info;338 struct of_drc_info drc;339 char drc_name[MAX_DRC_NAME_LEN];340 const __be32 *cur;341 u32 count;342 int retval = 0;343 344 info = of_find_property(dn, "ibm,drc-info", NULL);345 if (!info)346 return 0;347 348 cur = of_prop_next_u32(info, NULL, &count);349 if (cur)350 cur++;351 else352 return 0;353 354 of_read_drc_info_cell(&info, &cur, &drc);355 if (!is_php_type(drc.drc_type))356 return 0;357 358 sprintf(drc_name, "%s%d", drc.drc_name_prefix, drc.drc_name_suffix_start);359 360 slot = alloc_slot_struct(dn, drc.drc_index_start, drc_name, drc.drc_power_domain);361 if (!slot)362 return -ENOMEM;363 364 slot->type = simple_strtoul(drc.drc_type, NULL, 10);365 retval = rpaphp_enable_slot(slot);366 if (!retval)367 retval = rpaphp_register_slot(slot);368 369 if (retval)370 dealloc_slot_struct(slot);371 372 return retval;373}374 375static int rpaphp_drc_add_slot(struct device_node *dn)376{377 struct slot *slot;378 int retval = 0;379 int i;380 const __be32 *indexes, *names, *types, *power_domains;381 char *name, *type;382 383 /* If this is not a hotplug slot, return without doing anything. */384 if (!is_php_dn(dn, &indexes, &names, &types, &power_domains))385 return 0;386 387 dbg("Entry %s: dn=%pOF\n", __func__, dn);388 389 /* register PCI devices */390 name = (char *) &names[1];391 type = (char *) &types[1];392 for (i = 0; i < be32_to_cpu(indexes[0]); i++) {393 int index;394 395 index = be32_to_cpu(indexes[i + 1]);396 slot = alloc_slot_struct(dn, index, name,397 be32_to_cpu(power_domains[i + 1]));398 if (!slot)399 return -ENOMEM;400 401 slot->type = simple_strtoul(type, NULL, 10);402 403 dbg("Found drc-index:0x%x drc-name:%s drc-type:%s\n",404 index, name, type);405 406 retval = rpaphp_enable_slot(slot);407 if (!retval)408 retval = rpaphp_register_slot(slot);409 410 if (retval)411 dealloc_slot_struct(slot);412 413 name += strlen(name) + 1;414 type += strlen(type) + 1;415 }416 dbg("%s - Exit: rc[%d]\n", __func__, retval);417 418 /* XXX FIXME: reports a failure only if last entry in loop failed */419 return retval;420}421 422/**423 * rpaphp_add_slot -- declare a hotplug slot to the hotplug subsystem.424 * @dn: device node of slot425 *426 * This subroutine will register a hotpluggable slot with the427 * PCI hotplug infrastructure. This routine is typically called428 * during boot time, if the hotplug slots are present at boot time,429 * or is called later, by the dlpar add code, if the slot is430 * being dynamically added during runtime.431 *432 * If the device node points at an embedded (built-in) slot, this433 * routine will just return without doing anything, since embedded434 * slots cannot be hotplugged.435 *436 * To remove a slot, it suffices to call rpaphp_deregister_slot().437 */438int rpaphp_add_slot(struct device_node *dn)439{440 if (!of_node_name_eq(dn, "pci"))441 return 0;442 443 if (of_property_present(dn, "ibm,drc-info"))444 return rpaphp_drc_info_add_slot(dn);445 else446 return rpaphp_drc_add_slot(dn);447}448EXPORT_SYMBOL_GPL(rpaphp_add_slot);449 450static void __exit cleanup_slots(void)451{452 struct slot *slot, *next;453 454 /*455 * Unregister all of our slots with the pci_hotplug subsystem,456 * and free up all memory that we had allocated.457 */458 459 list_for_each_entry_safe(slot, next, &rpaphp_slot_head,460 rpaphp_slot_list) {461 list_del(&slot->rpaphp_slot_list);462 pci_hp_deregister(&slot->hotplug_slot);463 dealloc_slot_struct(slot);464 }465}466 467static int __init rpaphp_init(void)468{469 struct device_node *dn;470 471 info(DRIVER_DESC " version: " DRIVER_VERSION "\n");472 473 for_each_node_by_name(dn, "pci")474 rpaphp_add_slot(dn);475 476 return 0;477}478 479static void __exit rpaphp_exit(void)480{481 cleanup_slots();482}483 484static int enable_slot(struct hotplug_slot *hotplug_slot)485{486 struct slot *slot = to_slot(hotplug_slot);487 int state;488 int retval;489 490 if (slot->state == CONFIGURED)491 return 0;492 493 retval = rpaphp_get_sensor_state(slot, &state);494 if (retval)495 return retval;496 497 if (state == PRESENT) {498 pseries_eeh_init_edev_recursive(PCI_DN(slot->dn));499 500 pci_lock_rescan_remove();501 pci_hp_add_devices(slot->bus);502 pci_unlock_rescan_remove();503 slot->state = CONFIGURED;504 } else if (state == EMPTY) {505 slot->state = EMPTY;506 } else {507 err("%s: slot[%s] is in invalid state\n", __func__, slot->name);508 slot->state = NOT_VALID;509 return -EINVAL;510 }511 512 slot->bus->max_bus_speed = get_max_bus_speed(slot);513 return 0;514}515 516static int disable_slot(struct hotplug_slot *hotplug_slot)517{518 struct slot *slot = to_slot(hotplug_slot);519 if (slot->state == NOT_CONFIGURED)520 return -EINVAL;521 522 pci_lock_rescan_remove();523 pci_hp_remove_devices(slot->bus);524 pci_unlock_rescan_remove();525 vm_unmap_aliases();526 527 slot->state = NOT_CONFIGURED;528 return 0;529}530 531const struct hotplug_slot_ops rpaphp_hotplug_slot_ops = {532 .enable_slot = enable_slot,533 .disable_slot = disable_slot,534 .set_attention_status = set_attention_status,535 .get_power_status = get_power_status,536 .get_attention_status = get_attention_status,537 .get_adapter_status = get_adapter_status,538};539 540module_init(rpaphp_init);541module_exit(rpaphp_exit);542