| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * PCI Hot Plug Controller Driver for RPA-compliant PPC64 platform. | 
					
						
							|  |  |  |  * Copyright (C) 2003 Linda Xie <lxie@us.ibm.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * All rights reserved. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or modify | 
					
						
							|  |  |  |  * it under the terms of the GNU General Public License as published by | 
					
						
							|  |  |  |  * the Free Software Foundation; either version 2 of the License, or (at | 
					
						
							|  |  |  |  * your option) any later version. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is distributed in the hope that it will be useful, but | 
					
						
							|  |  |  |  * WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | 
					
						
							|  |  |  |  * NON INFRINGEMENT.  See the GNU General Public License for more | 
					
						
							|  |  |  |  * details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * You should have received a copy of the GNU General Public License | 
					
						
							|  |  |  |  * along with this program; if not, write to the Free Software | 
					
						
							|  |  |  |  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Send feedback to <lxie@us.ibm.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #include <linux/kernel.h>
 | 
					
						
							|  |  |  | #include <linux/module.h>
 | 
					
						
							|  |  |  | #include <linux/moduleparam.h>
 | 
					
						
							|  |  |  | #include <linux/pci.h>
 | 
					
						
							| 
									
										
										
										
											2006-10-13 20:05:19 -07:00
										 |  |  | #include <linux/pci_hotplug.h>
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #include <linux/smp.h>
 | 
					
						
							|  |  |  | #include <linux/init.h>
 | 
					
						
							| 
									
										
										
										
											2010-04-06 15:03:40 +00:00
										 |  |  | #include <linux/vmalloc.h>
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #include <asm/eeh.h>       /* for eeh_add_device() */
 | 
					
						
							|  |  |  | #include <asm/rtas.h>		/* rtas_call */
 | 
					
						
							|  |  |  | #include <asm/pci-bridge.h>	/* for pci_controller */
 | 
					
						
							|  |  |  | #include "../pci.h"		/* for pci_add_new_bus */
 | 
					
						
							|  |  |  | 				/* and pci_do_scan_bus */ | 
					
						
							|  |  |  | #include "rpaphp.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-13 09:32:20 +10:30
										 |  |  | bool rpaphp_debug; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | LIST_HEAD(rpaphp_slot_head); | 
					
						
							| 
									
										
										
										
											2014-04-25 14:32:25 -06:00
										 |  |  | EXPORT_SYMBOL_GPL(rpaphp_slot_head); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | #define DRIVER_VERSION	"0.1"
 | 
					
						
							|  |  |  | #define DRIVER_AUTHOR	"Linda Xie <lxie@us.ibm.com>"
 | 
					
						
							|  |  |  | #define DRIVER_DESC	"RPA HOT Plug PCI Controller Driver"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define MAX_LOC_CODE 128
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | MODULE_AUTHOR(DRIVER_AUTHOR); | 
					
						
							|  |  |  | MODULE_DESCRIPTION(DRIVER_DESC); | 
					
						
							|  |  |  | MODULE_LICENSE("GPL"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-10-13 09:59:12 -07:00
										 |  |  | module_param_named(debug, rpaphp_debug, bool, 0644); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * set_attention_status - set attention LED | 
					
						
							| 
									
										
										
										
											2007-11-28 09:04:30 -08:00
										 |  |  |  * @hotplug_slot: target &hotplug_slot | 
					
						
							|  |  |  |  * @value: LED control value | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  |  * echo 0 > attention -- set LED OFF | 
					
						
							|  |  |  |  * echo 1 > attention -- set LED ON | 
					
						
							|  |  |  |  * echo 2 > attention -- set LED ID(identify, light is blinking) | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2007-04-13 15:34:18 -07:00
										 |  |  | 	int rc; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	struct slot *slot = (struct slot *)hotplug_slot->private; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (value) { | 
					
						
							|  |  |  | 	case 0: | 
					
						
							|  |  |  | 	case 1: | 
					
						
							|  |  |  | 	case 2: | 
					
						
							| 
									
										
										
										
											2007-04-13 15:34:18 -07:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		value = 1; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2007-04-13 15:34:18 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	rc = rtas_set_indicator(DR_INDICATOR, slot->index, value); | 
					
						
							|  |  |  | 	if (!rc) | 
					
						
							|  |  |  | 		hotplug_slot->info->attention_status = value; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return rc; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * get_power_status - get power status of a slot | 
					
						
							|  |  |  |  * @hotplug_slot: slot to get status | 
					
						
							|  |  |  |  * @value: pointer to store status | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2014-04-18 20:13:49 -04:00
										 |  |  | static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2007-04-13 15:34:13 -07:00
										 |  |  | 	int retval, level; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	struct slot *slot = (struct slot *)hotplug_slot->private; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-04-13 15:34:13 -07:00
										 |  |  | 	retval = rtas_get_power_level (slot->power_domain, &level); | 
					
						
							|  |  |  | 	if (!retval) | 
					
						
							|  |  |  | 		*value = level; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	return retval; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * get_attention_status - get attention LED status | 
					
						
							| 
									
										
										
										
											2007-11-28 09:04:30 -08:00
										 |  |  |  * @hotplug_slot: slot to get status | 
					
						
							|  |  |  |  * @value: pointer to store status | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2014-04-18 20:13:49 -04:00
										 |  |  | static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct slot *slot = (struct slot *)hotplug_slot->private; | 
					
						
							| 
									
										
										
										
											2007-04-13 15:34:15 -07:00
										 |  |  | 	*value = slot->hotplug_slot->info->attention_status; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-04-18 20:13:49 -04:00
										 |  |  | static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct slot *slot = (struct slot *)hotplug_slot->private; | 
					
						
							| 
									
										
										
										
											2007-04-13 15:34:14 -07:00
										 |  |  | 	int rc, state; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-04-13 15:34:14 -07:00
										 |  |  | 	rc = rpaphp_get_sensor_state(slot, &state); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	*value = NOT_VALID; | 
					
						
							|  |  |  | 	if (rc) | 
					
						
							|  |  |  | 		return rc; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (state == EMPTY) | 
					
						
							|  |  |  | 		*value = EMPTY; | 
					
						
							|  |  |  | 	else if (state == PRESENT) | 
					
						
							|  |  |  | 		*value = slot->state; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-13 08:11:32 -05:00
										 |  |  | static enum pci_bus_speed get_max_bus_speed(struct slot *slot) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-12-13 08:11:32 -05:00
										 |  |  | 	enum pci_bus_speed speed; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	switch (slot->type) { | 
					
						
							|  |  |  | 	case 1: | 
					
						
							|  |  |  | 	case 2: | 
					
						
							|  |  |  | 	case 3: | 
					
						
							|  |  |  | 	case 4: | 
					
						
							|  |  |  | 	case 5: | 
					
						
							|  |  |  | 	case 6: | 
					
						
							| 
									
										
										
										
											2009-12-13 08:11:32 -05:00
										 |  |  | 		speed = PCI_SPEED_33MHz;	/* speed for case 1-6 */ | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	case 7: | 
					
						
							|  |  |  | 	case 8: | 
					
						
							| 
									
										
										
										
											2009-12-13 08:11:32 -05:00
										 |  |  | 		speed = PCI_SPEED_66MHz; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	case 11: | 
					
						
							|  |  |  | 	case 14: | 
					
						
							| 
									
										
										
										
											2009-12-13 08:11:32 -05:00
										 |  |  | 		speed = PCI_SPEED_66MHz_PCIX; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	case 12: | 
					
						
							|  |  |  | 	case 15: | 
					
						
							| 
									
										
										
										
											2009-12-13 08:11:32 -05:00
										 |  |  | 		speed = PCI_SPEED_100MHz_PCIX; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	case 13: | 
					
						
							|  |  |  | 	case 16: | 
					
						
							| 
									
										
										
										
											2009-12-13 08:11:32 -05:00
										 |  |  | 		speed = PCI_SPEED_133MHz_PCIX; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2009-12-13 08:11:32 -05:00
										 |  |  | 		speed = PCI_SPEED_UNKNOWN; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-12-13 08:11:32 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return speed; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-07-12 15:39:43 +10:00
										 |  |  | static int get_children_props(struct device_node *dn, const int **drc_indexes, | 
					
						
							|  |  |  | 		const int **drc_names, const int **drc_types, | 
					
						
							|  |  |  | 		const int **drc_power_domains) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2006-07-12 15:39:43 +10:00
										 |  |  | 	const int *indexes, *names, *types, *domains; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-05-01 13:54:02 +10:00
										 |  |  | 	indexes = of_get_property(dn, "ibm,drc-indexes", NULL); | 
					
						
							|  |  |  | 	names = of_get_property(dn, "ibm,drc-names", NULL); | 
					
						
							|  |  |  | 	types = of_get_property(dn, "ibm,drc-types", NULL); | 
					
						
							|  |  |  | 	domains = of_get_property(dn, "ibm,drc-power-domains", NULL); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!indexes || !names || !types || !domains) { | 
					
						
							|  |  |  | 		/* Slot does not have dynamically-removable children */ | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (drc_indexes) | 
					
						
							|  |  |  | 		*drc_indexes = indexes; | 
					
						
							|  |  |  | 	if (drc_names) | 
					
						
							|  |  |  | 		/* &drc_names[1] contains NULL terminated slot names */ | 
					
						
							|  |  |  | 		*drc_names = names; | 
					
						
							|  |  |  | 	if (drc_types) | 
					
						
							|  |  |  | 		/* &drc_types[1] contains NULL terminated slot types */ | 
					
						
							|  |  |  | 		*drc_types = types; | 
					
						
							|  |  |  | 	if (drc_power_domains) | 
					
						
							|  |  |  | 		*drc_power_domains = domains; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* To get the DRC props describing the current node, first obtain it's
 | 
					
						
							|  |  |  |  * my-drc-index property.  Next obtain the DRC list from it's parent.  Use | 
					
						
							|  |  |  |  * the my-drc-index for correlation, and obtain the requested properties. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int rpaphp_get_drc_props(struct device_node *dn, int *drc_index, | 
					
						
							|  |  |  | 		char **drc_name, char **drc_type, int *drc_power_domain) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2006-07-12 15:39:43 +10:00
										 |  |  | 	const int *indexes, *names; | 
					
						
							|  |  |  | 	const int *types, *domains; | 
					
						
							|  |  |  | 	const unsigned int *my_index; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	char *name_tmp, *type_tmp; | 
					
						
							|  |  |  | 	int i, rc; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-05-01 13:54:02 +10:00
										 |  |  | 	my_index = of_get_property(dn, "ibm,my-drc-index", NULL); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	if (!my_index) { | 
					
						
							|  |  |  | 		/* Node isn't DLPAR/hotplug capable */ | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rc = get_children_props(dn->parent, &indexes, &names, &types, &domains); | 
					
						
							|  |  |  | 	if (rc < 0) { | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	name_tmp = (char *) &names[1]; | 
					
						
							|  |  |  | 	type_tmp = (char *) &types[1]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Iterate through parent properties, looking for my-drc-index */ | 
					
						
							| 
									
										
										
										
											2014-04-10 15:02:13 +02:00
										 |  |  | 	for (i = 0; i < be32_to_cpu(indexes[0]); i++) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		if ((unsigned int) indexes[i + 1] == *my_index) { | 
					
						
							|  |  |  | 			if (drc_name) | 
					
						
							| 
									
										
										
										
											2013-11-14 11:28:18 -07:00
										 |  |  | 				*drc_name = name_tmp; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			if (drc_type) | 
					
						
							|  |  |  | 				*drc_type = type_tmp; | 
					
						
							|  |  |  | 			if (drc_index) | 
					
						
							| 
									
										
										
										
											2014-04-10 15:02:13 +02:00
										 |  |  | 				*drc_index = be32_to_cpu(*my_index); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			if (drc_power_domain) | 
					
						
							| 
									
										
										
										
											2014-04-10 15:02:13 +02:00
										 |  |  | 				*drc_power_domain = be32_to_cpu(domains[i+1]); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			return 0; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		name_tmp += (strlen(name_tmp) + 1); | 
					
						
							|  |  |  | 		type_tmp += (strlen(type_tmp) + 1); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return -EINVAL; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2014-04-25 14:32:25 -06:00
										 |  |  | EXPORT_SYMBOL_GPL(rpaphp_get_drc_props); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | static int is_php_type(char *drc_type) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned long value; | 
					
						
							|  |  |  | 	char *endptr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* PCI Hotplug nodes have an integer for drc_type */ | 
					
						
							|  |  |  | 	value = simple_strtoul(drc_type, &endptr, 10); | 
					
						
							|  |  |  | 	if (endptr == drc_type) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-04-13 15:34:22 -07:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * is_php_dn() - return 1 if this is a hotpluggable pci slot, else 0 | 
					
						
							| 
									
										
										
										
											2007-11-28 09:04:30 -08:00
										 |  |  |  * @dn: target &device_node | 
					
						
							|  |  |  |  * @indexes: passed to get_children_props() | 
					
						
							|  |  |  |  * @names: passed to get_children_props() | 
					
						
							|  |  |  |  * @types: returned from get_children_props() | 
					
						
							|  |  |  |  * @power_domains: | 
					
						
							| 
									
										
										
										
											2007-04-13 15:34:22 -07:00
										 |  |  |  * | 
					
						
							|  |  |  |  * This routine will return true only if the device node is | 
					
						
							|  |  |  |  * a hotpluggable slot. This routine will return false | 
					
						
							|  |  |  |  * for built-in pci slots (even when the built-in slots are | 
					
						
							|  |  |  |  * dlparable.) | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2006-07-12 15:39:43 +10:00
										 |  |  | static int is_php_dn(struct device_node *dn, const int **indexes, | 
					
						
							|  |  |  | 		const int **names, const int **types, const int **power_domains) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2006-07-12 15:39:43 +10:00
										 |  |  | 	const int *drc_types; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	int rc; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rc = get_children_props(dn, indexes, names, &drc_types, power_domains); | 
					
						
							| 
									
										
										
										
											2007-04-13 15:34:22 -07:00
										 |  |  | 	if (rc < 0) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-04-13 15:34:22 -07:00
										 |  |  | 	if (!is_php_type((char *) &drc_types[1])) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	*types = drc_types; | 
					
						
							|  |  |  | 	return 1; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-12 18:31:01 -06:00
										 |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2007-04-13 15:34:22 -07:00
										 |  |  |  * rpaphp_add_slot -- declare a hotplug slot to the hotplug subsystem. | 
					
						
							| 
									
										
										
										
											2007-11-28 09:04:30 -08:00
										 |  |  |  * @dn: device node of slot | 
					
						
							| 
									
										
										
										
											2007-04-13 15:34:22 -07:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2013-11-14 11:28:18 -07:00
										 |  |  |  * This subroutine will register a hotpluggable slot with the | 
					
						
							| 
									
										
										
										
											2011-03-30 22:57:33 -03:00
										 |  |  |  * PCI hotplug infrastructure. This routine is typically called | 
					
						
							| 
									
										
										
										
											2007-04-13 15:34:22 -07:00
										 |  |  |  * during boot time, if the hotplug slots are present at boot time, | 
					
						
							|  |  |  |  * or is called later, by the dlpar add code, if the slot is | 
					
						
							|  |  |  |  * being dynamically added during runtime. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * If the device node points at an embedded (built-in) slot, this | 
					
						
							|  |  |  |  * routine will just return without doing anything, since embedded | 
					
						
							|  |  |  |  * slots cannot be hotplugged. | 
					
						
							| 
									
										
										
										
											2006-01-12 18:31:01 -06:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2007-11-28 09:04:30 -08:00
										 |  |  |  * To remove a slot, it suffices to call rpaphp_deregister_slot(). | 
					
						
							| 
									
										
										
										
											2006-01-12 18:31:01 -06:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | int rpaphp_add_slot(struct device_node *dn) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct slot *slot; | 
					
						
							|  |  |  | 	int retval = 0; | 
					
						
							| 
									
										
										
										
											2005-07-25 10:17:03 -05:00
										 |  |  | 	int i; | 
					
						
							| 
									
										
										
										
											2006-07-12 15:39:43 +10:00
										 |  |  | 	const int *indexes, *names, *types, *power_domains; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	char *name, *type; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-04-13 15:34:07 -07:00
										 |  |  | 	if (!dn->name || strcmp(dn->name, "pci")) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-04-13 15:34:22 -07:00
										 |  |  | 	/* If this is not a hotplug slot, return without doing anything. */ | 
					
						
							| 
									
										
										
										
											2007-04-13 15:34:07 -07:00
										 |  |  | 	if (!is_php_dn(dn, &indexes, &names, &types, &power_domains)) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-03-03 19:09:46 -08:00
										 |  |  | 	dbg("Entry %s: dn->full_name=%s\n", __func__, dn->full_name); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* register PCI devices */ | 
					
						
							| 
									
										
										
										
											2007-04-13 15:34:07 -07:00
										 |  |  | 	name = (char *) &names[1]; | 
					
						
							|  |  |  | 	type = (char *) &types[1]; | 
					
						
							| 
									
										
										
										
											2014-04-10 15:02:13 +02:00
										 |  |  | 	for (i = 0; i < be32_to_cpu(indexes[0]); i++) { | 
					
						
							|  |  |  | 		int index; | 
					
						
							| 
									
										
										
										
											2007-04-13 15:34:07 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-04-10 15:02:13 +02:00
										 |  |  | 		index = be32_to_cpu(indexes[i + 1]); | 
					
						
							|  |  |  | 		slot = alloc_slot_struct(dn, index, name, | 
					
						
							|  |  |  | 					 be32_to_cpu(power_domains[i + 1])); | 
					
						
							| 
									
										
										
										
											2007-04-13 15:34:07 -07:00
										 |  |  | 		if (!slot) | 
					
						
							|  |  |  | 			return -ENOMEM; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		slot->type = simple_strtoul(type, NULL, 10); | 
					
						
							| 
									
										
										
										
											2013-11-14 11:28:18 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-04-13 15:34:07 -07:00
										 |  |  | 		dbg("Found drc-index:0x%x drc-name:%s drc-type:%s\n", | 
					
						
							| 
									
										
										
										
											2014-04-10 15:02:13 +02:00
										 |  |  | 				index, name, type); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-04-13 15:34:20 -07:00
										 |  |  | 		retval = rpaphp_enable_slot(slot); | 
					
						
							| 
									
										
										
										
											2007-04-13 15:34:19 -07:00
										 |  |  | 		if (!retval) | 
					
						
							|  |  |  | 			retval = rpaphp_register_slot(slot); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-04-13 15:34:09 -07:00
										 |  |  | 		if (retval) | 
					
						
							|  |  |  | 			dealloc_slot_struct(slot); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-04-13 15:34:07 -07:00
										 |  |  | 		name += strlen(name) + 1; | 
					
						
							|  |  |  | 		type += strlen(type) + 1; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-03-03 19:09:46 -08:00
										 |  |  | 	dbg("%s - Exit: rc[%d]\n", __func__, retval); | 
					
						
							| 
									
										
										
										
											2007-04-13 15:34:09 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* XXX FIXME: reports a failure only if last entry in loop failed */ | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	return retval; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2014-04-25 14:32:25 -06:00
										 |  |  | EXPORT_SYMBOL_GPL(rpaphp_add_slot); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | static void __exit cleanup_slots(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct list_head *tmp, *n; | 
					
						
							|  |  |  | 	struct slot *slot; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Unregister all of our slots with the pci_hotplug subsystem, | 
					
						
							|  |  |  | 	 * and free up all memory that we had allocated. | 
					
						
							| 
									
										
										
										
											2013-11-14 11:28:18 -07:00
										 |  |  | 	 * memory will be freed in release_slot callback. | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	 */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	list_for_each_safe(tmp, n, &rpaphp_slot_head) { | 
					
						
							|  |  |  | 		slot = list_entry(tmp, struct slot, rpaphp_slot_list); | 
					
						
							|  |  |  | 		list_del(&slot->rpaphp_slot_list); | 
					
						
							|  |  |  | 		pci_hp_deregister(slot->hotplug_slot); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __init rpaphp_init(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2014-06-04 16:42:26 +01:00
										 |  |  | 	struct device_node *dn; | 
					
						
							| 
									
										
										
										
											2005-07-25 10:16:42 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); | 
					
						
							| 
									
										
										
										
											2005-07-25 10:16:42 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-04 16:42:26 +01:00
										 |  |  | 	for_each_node_by_name(dn, "pci") | 
					
						
							| 
									
										
										
										
											2005-07-25 10:16:42 -05:00
										 |  |  | 		rpaphp_add_slot(dn); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void __exit rpaphp_exit(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	cleanup_slots(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-04-13 15:34:25 -07:00
										 |  |  | static int enable_slot(struct hotplug_slot *hotplug_slot) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2007-04-13 15:34:25 -07:00
										 |  |  | 	struct slot *slot = (struct slot *)hotplug_slot->private; | 
					
						
							| 
									
										
										
										
											2006-01-12 18:28:22 -06:00
										 |  |  | 	int state; | 
					
						
							|  |  |  | 	int retval; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-12 18:28:22 -06:00
										 |  |  | 	if (slot->state == CONFIGURED) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	retval = rpaphp_get_sensor_state(slot, &state); | 
					
						
							|  |  |  | 	if (retval) | 
					
						
							|  |  |  | 		return retval; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (state == PRESENT) { | 
					
						
							| 
									
										
										
										
											2014-01-14 12:03:14 -07:00
										 |  |  | 		pci_lock_rescan_remove(); | 
					
						
							| 
									
										
										
										
											2006-01-12 18:28:22 -06:00
										 |  |  | 		pcibios_add_pci_devices(slot->bus); | 
					
						
							| 
									
										
										
										
											2014-01-14 12:03:14 -07:00
										 |  |  | 		pci_unlock_rescan_remove(); | 
					
						
							| 
									
										
										
										
											2006-01-12 18:28:22 -06:00
										 |  |  | 		slot->state = CONFIGURED; | 
					
						
							|  |  |  | 	} else if (state == EMPTY) { | 
					
						
							|  |  |  | 		slot->state = EMPTY; | 
					
						
							|  |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2008-03-03 19:09:46 -08:00
										 |  |  | 		err("%s: slot[%s] is in invalid state\n", __func__, slot->name); | 
					
						
							| 
									
										
										
										
											2006-01-12 18:28:22 -06:00
										 |  |  | 		slot->state = NOT_VALID; | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-12-13 08:11:32 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	slot->bus->max_bus_speed = get_max_bus_speed(slot); | 
					
						
							| 
									
										
										
										
											2006-01-12 18:28:22 -06:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-04-13 15:34:25 -07:00
										 |  |  | static int disable_slot(struct hotplug_slot *hotplug_slot) | 
					
						
							| 
									
										
										
										
											2006-01-12 18:28:22 -06:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct slot *slot = (struct slot *)hotplug_slot->private; | 
					
						
							| 
									
										
										
										
											2006-01-12 18:26:27 -06:00
										 |  |  | 	if (slot->state == NOT_CONFIGURED) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-14 12:03:14 -07:00
										 |  |  | 	pci_lock_rescan_remove(); | 
					
						
							| 
									
										
										
										
											2007-04-13 15:34:23 -07:00
										 |  |  | 	pcibios_remove_pci_devices(slot->bus); | 
					
						
							| 
									
										
										
										
											2014-01-14 12:03:14 -07:00
										 |  |  | 	pci_unlock_rescan_remove(); | 
					
						
							| 
									
										
										
										
											2010-04-06 15:03:40 +00:00
										 |  |  | 	vm_unmap_aliases(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-12 18:26:27 -06:00
										 |  |  | 	slot->state = NOT_CONFIGURED; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-12 18:32:58 -06:00
										 |  |  | struct hotplug_slot_ops rpaphp_hotplug_slot_ops = { | 
					
						
							|  |  |  | 	.enable_slot = enable_slot, | 
					
						
							|  |  |  | 	.disable_slot = disable_slot, | 
					
						
							|  |  |  | 	.set_attention_status = set_attention_status, | 
					
						
							|  |  |  | 	.get_power_status = get_power_status, | 
					
						
							|  |  |  | 	.get_attention_status = get_attention_status, | 
					
						
							|  |  |  | 	.get_adapter_status = get_adapter_status, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | module_init(rpaphp_init); | 
					
						
							|  |  |  | module_exit(rpaphp_exit); |