brintos

brintos / linux-shallow public Read only

0
0
Text · 12.5 KiB · 2316de0 Raw
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