Subject: Re: [PATCH 6/8] PCI/hotplug/rpa: Abstract slot operations

From: Benjamin Herrenschmidt <[email-masked]>
Date: Wed, 26 Nov 2014 10:04:36 +1100

On Tue, 2014-11-25 at 09:49 +1100, Gavin Shan wrote:
> The patch splits the code into 2 parts: RPA PCI hotplug slot
> management and RTAS backend. It enables us to support PowerNV,
> which is built on top of OPAL firmware in future.
> 
> The patch also refactors the code for a bit:
> 
>     * Rename "struct slot" to "struct rpa_php_slot"
>     * All macros have prefix "RPA_PHP_SLOT"
>     * rpaphp_slot.c is removed and all logics moved to rpaphp_core.c

I don't see the point of this. rpaphp is already itself a "backend", so
we end up with yet another layer.

Just create a powernv-php or opal-php and if there is common code,
factor it into a common helper but I wouldn't bother too much initially
unless there is a lot of it.

> Signed-off-by: Gavin Shan <[email-masked]>
> ---
>  drivers/pci/hotplug/Makefile        |   3 +-
>  drivers/pci/hotplug/rpadlpar_core.c |  10 +-
>  drivers/pci/hotplug/rpaphp.h        |  64 +++----
>  drivers/pci/hotplug/rpaphp_core.c   | 347 +++++++++++-------------------------
>  drivers/pci/hotplug/rpaphp_pci.c    | 136 --------------
>  drivers/pci/hotplug/rpaphp_rtas.c   | 320 +++++++++++++++++++++++++++++++++
>  drivers/pci/hotplug/rpaphp_slot.c   | 140 ---------------
>  7 files changed, 459 insertions(+), 561 deletions(-)
>  delete mode 100644 drivers/pci/hotplug/rpaphp_pci.c
>  create mode 100644 drivers/pci/hotplug/rpaphp_rtas.c
>  delete mode 100644 drivers/pci/hotplug/rpaphp_slot.c
> 
> diff --git a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile
> index 4a9aa08..630313da 100644
> --- a/drivers/pci/hotplug/Makefile
> +++ b/drivers/pci/hotplug/Makefile
> @@ -51,8 +51,7 @@ acpiphp-objs		:=	acpiphp_core.o	\
>  				acpiphp_glue.o
>  
>  rpaphp-objs		:=	rpaphp_core.o	\
> -				rpaphp_pci.o	\
> -				rpaphp_slot.o
> +				rpaphp_rtas.o
>  
>  rpadlpar_io-objs	:=	rpadlpar_core.o \
>  				rpadlpar_sysfs.o
> diff --git a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c
> index 35da3b3..a36d2c9 100644
> --- a/drivers/pci/hotplug/rpadlpar_core.c
> +++ b/drivers/pci/hotplug/rpadlpar_core.c
> @@ -117,13 +117,13 @@ static struct device_node *find_dlpar_node(char *drc_name, int *node_type)
>   * may be dlpar-able, but not hot-pluggable, so this routine
>   * will return NULL for built-in PCI slots.
>   */
> -static struct slot *find_php_slot(struct device_node *dn)
> +static struct rpa_php_slot *find_php_slot(struct device_node *dn)
>  {
>  	struct list_head *tmp, *n;
> -	struct slot *slot;
> +	struct rpa_php_slot *slot;
>  
>  	list_for_each_safe(tmp, n, &rpaphp_slot_head) {
> -		slot = list_entry(tmp, struct slot, rpaphp_slot_list);
> +		slot = list_entry(tmp, struct rpa_php_slot, rpaphp_slot_list);
>  		if (slot->dn == dn)
>  			return slot;
>  	}
> @@ -214,7 +214,7 @@ static int dlpar_add_pci_slot(char *drc_name, struct device_node *dn)
>  
>  static int dlpar_remove_phb(char *drc_name, struct device_node *dn)
>  {
> -	struct slot *slot;
> +	struct rpa_php_slot *slot;
>  	struct pci_dn *pdn;
>  	int rc = 0;
>  
> @@ -359,7 +359,7 @@ static int dlpar_remove_vio_slot(char *drc_name, struct device_node *dn)
>  int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn)
>  {
>  	struct pci_bus *bus;
> -	struct slot *slot;
> +	struct rpa_php_slot *slot;
>  	int ret = 0;
>  
>  	pci_lock_rescan_remove();
> diff --git a/drivers/pci/hotplug/rpaphp.h b/drivers/pci/hotplug/rpaphp.h
> index 39ddbdf..09dd516 100644
> --- a/drivers/pci/hotplug/rpaphp.h
> +++ b/drivers/pci/hotplug/rpaphp.h
> @@ -30,21 +30,6 @@
>  #include <linux/pci.h>
>  #include <linux/pci_hotplug.h>
>  
> -#define DR_INDICATOR 9002
> -#define DR_ENTITY_SENSE 9003
> -
> -#define POWER_ON	100
> -#define POWER_OFF	0
> -
> -#define LED_OFF		0
> -#define LED_ON		1	/* continuous on */
> -#define LED_ID		2	/* slow blinking */
> -#define LED_ACTION	3	/* fast blinking */
> -
> -/* Sensor values from rtas_get-sensor */
> -#define EMPTY           0	/* No card in slot */
> -#define PRESENT         1	/* Card in slot */
> -
>  #define MY_NAME "rpaphp"
>  extern bool rpaphp_debug;
>  #define dbg(format, arg...)					\
> @@ -57,19 +42,26 @@ extern bool rpaphp_debug;
>  #define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg)
>  #define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME , ## arg)
>  
> -/* slot states */
> +/* Power */
> +#define RPA_PHP_SLOT_POWER_ON	1	/* On            */
> +#define RPA_PHP_SLOT_POWER_OFF	0	/* Off           */
>  
> -#define	NOT_VALID	3
> -#define	NOT_CONFIGURED	2
> -#define	CONFIGURED	1
> -#define	EMPTY		0
> +/* Attention */
> +#define RPA_PHP_SLOT_ATTEN_OFF	0	/* Off           */
> +#define RPA_PHP_SLOT_ATTEN_ON	1	/* On            */
> +#define RPA_PHP_SLOT_ATTEN_IND	2	/* Slow blinking */
> +#define RPA_PHP_SLOT_ATTEN_ACT	3	/* Fast blinking */
>  
> -/*
> - * struct slot - slot information for each *physical* slot
> - */
> -struct slot {
> +/* Presence */
> +#define RPA_PHP_SLOT_EMPTY	0	/* No card       */
> +#define RPA_PHP_SLOT_PRESENT	1	/* Presented     */
> +
> +struct rpa_php_slot {
>  	struct list_head rpaphp_slot_list;
>  	int state;
> +#define RPA_PHP_SLOT_NOT_CONFIGURED	0
> +#define RPA_PHP_SLOT_CONFIGURED		1
> +#define RPA_PHP_SLOT_NOT_VALID		2
>  	u32 index;
>  	u32 type;
>  	u32 power_domain;
> @@ -80,24 +72,20 @@ struct slot {
>  	struct hotplug_slot *hotplug_slot;
>  };
>  
> -extern struct hotplug_slot_ops rpaphp_hotplug_slot_ops;
>  extern struct list_head rpaphp_slot_head;
>  
> -/* function prototypes */
> -
> -/* rpaphp_pci.c */
> -int rpaphp_enable_slot(struct slot *slot);
> -int rpaphp_get_sensor_state(struct slot *slot, int *state);
> -
>  /* rpaphp_core.c */
>  int rpaphp_add_slot(struct device_node *dn);
> -int rpaphp_get_drc_props(struct device_node *dn, int *drc_index,
> -		char **drc_name, char **drc_type, int *drc_power);
> +void dealloc_slot_struct(struct rpa_php_slot *slot);
> +struct rpa_php_slot *alloc_slot_struct(struct device_node *dn, int drc_index,
> +				       char *drc_name, int power_domain);
> +int rpaphp_register_slot(struct rpa_php_slot *slot);
> +int rpaphp_deregister_slot(struct rpa_php_slot *slot);
> +int rpaphp_add_slot(struct device_node *dn);
>  
> -/* rpaphp_slot.c */
> -void dealloc_slot_struct(struct slot *slot);
> -struct slot *alloc_slot_struct(struct device_node *dn, int drc_index, char *drc_name, int power_domain);
> -int rpaphp_register_slot(struct slot *slot);
> -int rpaphp_deregister_slot(struct slot *slot);
> +/* rpaphp_rtas.c */
> +int rpaphp_get_drc_props(struct device_node *dn, int *drc_index,
> +			 char **drc_name, char **drc_type, int *drc_power);
> +struct rpa_php_slot *rpaphp_rtas_add_slot(struct device_node *dn);
>  
>  #endif				/* _PPC64PHP_H */
> diff --git a/drivers/pci/hotplug/rpaphp_core.c b/drivers/pci/hotplug/rpaphp_core.c
> index a639c5c..91eff8f 100644
> --- a/drivers/pci/hotplug/rpaphp_core.c
> +++ b/drivers/pci/hotplug/rpaphp_core.c
> @@ -45,194 +45,118 @@ EXPORT_SYMBOL_GPL(rpaphp_slot_head);
>  #define DRIVER_AUTHOR	"Linda Xie <[email-masked]>"
>  #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");
> -
>  module_param_named(debug, rpaphp_debug, bool, 0644);
>  
> -/**
> - * set_attention_status - set attention LED
> - * @hotplug_slot: target &hotplug_slot
> - * @value: LED control value
> - *
> - * 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)
> +/* free up the memory used by a slot */
> +static void rpaphp_release_slot(struct hotplug_slot *hotplug_slot)
>  {
> -	int rc;
> -	struct slot *slot = (struct slot *)hotplug_slot->private;
> -
> -	switch (value) {
> -	case 0:
> -	case 1:
> -	case 2:
> -		break;
> -	default:
> -		value = 1;
> -	}
> +	struct rpa_php_slot *slot = hotplug_slot->private;
>  
> -	rc = rtas_set_indicator(DR_INDICATOR, slot->index, value);
> -	if (!rc)
> -		hotplug_slot->info->attention_status = value;
> -
> -	return rc;
> +	dealloc_slot_struct(slot);
>  }
>  
> -/**
> - * get_power_status - get power status of a slot
> - * @hotplug_slot: slot to get status
> - * @value: pointer to store status
> - */
> -static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
> +void dealloc_slot_struct(struct rpa_php_slot *slot)
>  {
> -	int retval, level;
> -	struct slot *slot = (struct slot *)hotplug_slot->private;
> -
> -	retval = rtas_get_power_level(slot->power_domain, &level);
> -	if (!retval)
> -		*value = level;
> -	return retval;
> +	kfree(slot->hotplug_slot->info);
> +	kfree(slot->name);
> +	kfree(slot->hotplug_slot);
> +	kfree(slot);
>  }
>  
> -/**
> - * get_attention_status - get attention LED status
> - * @hotplug_slot: slot to get status
> - * @value: pointer to store status
> - */
> -static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
> +struct rpa_php_slot *alloc_slot_struct(struct device_node *dn,
> +				       int drc_index, char *drc_name,
> +				       int power_domain)
>  {
> -	struct slot *slot = (struct slot *)hotplug_slot->private;
> -	*value = slot->hotplug_slot->info->attention_status;
> -	return 0;
> -}
> -
> -static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
> -{
> -	struct slot *slot = (struct slot *)hotplug_slot->private;
> -	int rc, state;
> -
> -	rc = rpaphp_get_sensor_state(slot, &state);
> -
> -	*value = NOT_VALID;
> -	if (rc)
> -		return rc;
> +	struct rpa_php_slot *slot;
>  
> -	if (state == EMPTY)
> -		*value = EMPTY;
> -	else if (state == PRESENT)
> -		*value = slot->state;
> -
> -	return 0;
> +	slot = kzalloc(sizeof(*slot), GFP_KERNEL);
> +	if (!slot)
> +		goto error_nomem;
> +	slot->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
> +	if (!slot->hotplug_slot)
> +		goto error_slot;
> +	slot->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
> +					   GFP_KERNEL);
> +	if (!slot->hotplug_slot->info)
> +		goto error_hpslot;
> +	slot->name = kstrdup(drc_name, GFP_KERNEL);
> +	if (!slot->name)
> +		goto error_info;
> +	slot->dn = dn;
> +	slot->index = drc_index;
> +	slot->power_domain = power_domain;
> +	slot->hotplug_slot->private = slot;
> +	slot->hotplug_slot->release = &rpaphp_release_slot;
> +
> +	slot->hotplug_slot->info->power_status = RPA_PHP_SLOT_POWER_ON;
> +	slot->hotplug_slot->info->attention_status = RPA_PHP_SLOT_ATTEN_OFF;
> +	slot->hotplug_slot->info->adapter_status = RPA_PHP_SLOT_EMPTY;
> +	slot->state = RPA_PHP_SLOT_NOT_VALID;
> +
> +	return slot;
> +
> +error_info:
> +	kfree(slot->hotplug_slot->info);
> +error_hpslot:
> +	kfree(slot->hotplug_slot);
> +error_slot:
> +	kfree(slot);
> +error_nomem:
> +	return NULL;
>  }
>  
> -static enum pci_bus_speed get_max_bus_speed(struct slot *slot)
> +int rpaphp_register_slot(struct rpa_php_slot *slot)
>  {
> -	enum pci_bus_speed speed;
> -	switch (slot->type) {
> -	case 1:
> -	case 2:
> -	case 3:
> -	case 4:
> -	case 5:
> -	case 6:
> -		speed = PCI_SPEED_33MHz;	/* speed for case 1-6 */
> -		break;
> -	case 7:
> -	case 8:
> -		speed = PCI_SPEED_66MHz;
> -		break;
> -	case 11:
> -	case 14:
> -		speed = PCI_SPEED_66MHz_PCIX;
> -		break;
> -	case 12:
> -	case 15:
> -		speed = PCI_SPEED_100MHz_PCIX;
> -		break;
> -	case 13:
> -	case 16:
> -		speed = PCI_SPEED_133MHz_PCIX;
> -		break;
> -	default:
> -		speed = PCI_SPEED_UNKNOWN;
> +	struct hotplug_slot *php_slot = slot->hotplug_slot;
> +	struct rpa_php_slot *tmp;
> +	int slotno, retval;
> +
> +	dbg("%s registering slot:path[%s] index[%x], name[%s] pdomain[%x] type[%d]\n",
> +	    __func__, slot->dn->full_name, slot->index, slot->name,
> +	    slot->power_domain, slot->type);
> +
> +	/* Should not try to register the same slot twice */
> +	list_for_each_entry(tmp, &rpaphp_slot_head, rpaphp_slot_list) {
> +		if (!strcmp(tmp->name, slot->name)) {
> +			err("%s: Slot[%s] is already registered\n",
> +			    __func__, slot->name);
> +			return -EAGAIN;
> +		}
> +	}
> +	if (slot->dn->child)
> +		slotno = PCI_SLOT(PCI_DN(slot->dn->child)->devfn);
> +	else
> +		slotno = -1;
> +	retval = pci_hp_register(php_slot, slot->bus, slotno, slot->name);
> +	if (retval) {
> +		err("pci_hp_register failed with error %d\n", retval);
> +		return retval;
>  	}
>  
> -	return speed;
> +	/* add slot to our internal list */
> +	list_add(&slot->rpaphp_slot_list, &rpaphp_slot_head);
> +	info("Slot [%s] registered\n", slot->name);
> +	return 0;
>  }
>  
> -static int parse_drc_props(struct device_node *dn, u32 drc_index,
> -			   char **drc_name, char **drc_type, u32 *drc_power)
> +int rpaphp_deregister_slot(struct rpa_php_slot *slot)
>  {
> -	const u32 *indexes, *names, *types, *domains;
> -	char *name, *type;
> -	struct device_node *parent = dn;
> -	u32 i;
> -
> -	while ((parent = of_get_parent(parent))) {
> -		indexes = of_get_property(parent, "ibm,drc-indexes", NULL);
> -		names   = of_get_property(parent, "ibm,drc-names", NULL);
> -		types   = of_get_property(parent, "ibm,drc-types", NULL);
> -		domains = of_get_property(parent, "ibm,drc-power-domains", NULL);
> -
> -		if (!indexes || !names || !types || !domains) {
> -			of_node_put(parent);
> -			continue;
> -		}
> +	struct hotplug_slot *php_slot = slot->hotplug_slot;
> +	int retval = 0;
>  
> -		name = (char *)&names[1];
> -		type = (char *)&types[1];
> -		for (i = 0; i < be32_to_cpu(indexes[0]); i++) {
> -			if (be32_to_cpu(indexes[i + 1]) != drc_index) {
> -				name += (strlen(name) + 1);
> -				type += (strlen(type) + 1);
> -				continue;
> -			}
> -
> -			/* Matched index */
> -			if (drc_name)
> -				*drc_name = name;
> -			if (drc_type)
> -				*drc_type = type;
> -			if (drc_power)
> -				*drc_power = be32_to_cpu(domains[i + 1]);
> -
> -			of_node_put(parent);
> -			return 0;
> -		}
> -
> -		/* Next level parent */
> -		of_node_put(parent);
> -	}
> +	dbg("%s - Entry: deregistering slot=%s\n",
> +	    __func__, slot->name);
>  
> -	return -ENODEV;
> -}
> +	list_del(&slot->rpaphp_slot_list);
>  
> -/*
> - * 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)
> -{
> -	const u32 *my_index;
> -
> -	/* Check if node is capable of hotplug */
> -	my_index = of_get_property(dn, "ibm,my-drc-index", NULL);
> -	if (!my_index)
> -		return -EINVAL;
> -	if (drc_index)
> -		*drc_index = be32_to_cpu(*my_index);
> +	retval = pci_hp_deregister(php_slot);
> +	if (retval)
> +		err("Problem unregistering a slot %s\n", slot->name);
>  
> -	return parse_drc_props(dn, be32_to_cpu(*my_index),
> -			       drc_name, drc_type, drc_power);
> +	dbg("%s - Exit: rc[%d]\n", __func__, retval);
> +	return retval;
>  }
> -EXPORT_SYMBOL_GPL(rpaphp_get_drc_props);
> +EXPORT_SYMBOL_GPL(rpaphp_deregister_slot);
>  
>  /**
>   * rpaphp_add_slot -- declare a hotplug slot to the hotplug subsystem.
> @@ -252,29 +176,22 @@ EXPORT_SYMBOL_GPL(rpaphp_get_drc_props);
>   */
>  int rpaphp_add_slot(struct device_node *dn)
>  {
> -	char *name, *type, *endptr;
> -	int index, power_domain;
> -	struct slot *slot;
> -	int val, ret;
> -
> -	/* Get and parse the hotplug properties */
> -	ret = rpaphp_get_drc_props(dn, &index, &name, &type, &power_domain);
> -	if (ret)
> -		return ret;
> -
> -	/* PCI Hotplug nodes have an integer for drc_type */
> -	val = simple_strtoul(type, &endptr, 10);
> -	if (endptr == type)
> -		return -EINVAL;
> +	struct rpa_php_slot *slot = NULL;
> +	int ret;
>  
> -	slot = alloc_slot_struct(dn, index, name, power_domain);
> +	/* Create slot */
> +	if (machine_is(pseries))
> +		slot = rpaphp_rtas_add_slot(dn);
>  	if (!slot)
> -		return -ENOMEM;
> +		return -EIO;
>  
> -	slot->type = val;
> -	ret = rpaphp_enable_slot(slot);
> -	if (!ret)
> -		ret = rpaphp_register_slot(slot);
> +	/* Enable slot */
> +	ret = slot->hotplug_slot->ops->enable_slot(slot->hotplug_slot);
> +	if (ret)
> +		goto fail;
> +
> +	/* Register slot */
> +	ret = rpaphp_register_slot(slot);
>  	if (ret)
>  		goto fail;
>  
> @@ -288,7 +205,7 @@ EXPORT_SYMBOL_GPL(rpaphp_add_slot);
>  static void __exit cleanup_slots(void)
>  {
>  	struct list_head *tmp, *n;
> -	struct slot *slot;
> +	struct rpa_php_slot *slot;
>  
>  	/*
>  	 * Unregister all of our slots with the pci_hotplug subsystem,
> @@ -297,7 +214,7 @@ static void __exit cleanup_slots(void)
>  	 */
>  
>  	list_for_each_safe(tmp, n, &rpaphp_slot_head) {
> -		slot = list_entry(tmp, struct slot, rpaphp_slot_list);
> +		slot = list_entry(tmp, struct rpa_php_slot, rpaphp_slot_list);
>  		list_del(&slot->rpaphp_slot_list);
>  		pci_hp_deregister(slot->hotplug_slot);
>  	}
> @@ -320,59 +237,9 @@ static void __exit rpaphp_exit(void)
>  	cleanup_slots();
>  }
>  
> -static int enable_slot(struct hotplug_slot *hotplug_slot)
> -{
> -	struct slot *slot = (struct slot *)hotplug_slot->private;
> -	int state;
> -	int retval;
> -
> -	if (slot->state == CONFIGURED)
> -		return 0;
> -
> -	retval = rpaphp_get_sensor_state(slot, &state);
> -	if (retval)
> -		return retval;
> -
> -	if (state == PRESENT) {
> -		pci_lock_rescan_remove();
> -		pcibios_add_pci_devices(slot->bus);
> -		pci_unlock_rescan_remove();
> -		slot->state = CONFIGURED;
> -	} else if (state == EMPTY) {
> -		slot->state = EMPTY;
> -	} else {
> -		err("%s: slot[%s] is in invalid state\n", __func__, slot->name);
> -		slot->state = NOT_VALID;
> -		return -EINVAL;
> -	}
> -
> -	slot->bus->max_bus_speed = get_max_bus_speed(slot);
> -	return 0;
> -}
> -
> -static int disable_slot(struct hotplug_slot *hotplug_slot)
> -{
> -	struct slot *slot = (struct slot *)hotplug_slot->private;
> -	if (slot->state == NOT_CONFIGURED)
> -		return -EINVAL;
> -
> -	pci_lock_rescan_remove();
> -	pcibios_remove_pci_devices(slot->bus);
> -	pci_unlock_rescan_remove();
> -	vm_unmap_aliases();
> -
> -	slot->state = NOT_CONFIGURED;
> -	return 0;
> -}
> -
> -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,
> -};
> -
>  module_init(rpaphp_init);
>  module_exit(rpaphp_exit);
> +
> +MODULE_AUTHOR(DRIVER_AUTHOR);
> +MODULE_DESCRIPTION(DRIVER_DESC);
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/pci/hotplug/rpaphp_pci.c b/drivers/pci/hotplug/rpaphp_pci.c
> deleted file mode 100644
> index a4aa65c..0000000
> --- a/drivers/pci/hotplug/rpaphp_pci.c
> +++ /dev/null
> @@ -1,136 +0,0 @@
> -/*
> - * PCI Hot Plug Controller Driver for RPA-compliant PPC64 platform.
> - * Copyright (C) 2003 Linda Xie <[email-masked]>
> - *
> - * 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 <[email-masked]>
> - *
> - */
> -#include <linux/pci.h>
> -#include <linux/string.h>
> -
> -#include <asm/pci-bridge.h>
> -#include <asm/rtas.h>
> -#include <asm/machdep.h>
> -
> -#include "../pci.h"		/* for pci_add_new_bus */
> -#include "rpaphp.h"
> -
> -int rpaphp_get_sensor_state(struct slot *slot, int *state)
> -{
> -	int rc;
> -	int setlevel;
> -
> -	rc = rtas_get_sensor(DR_ENTITY_SENSE, slot->index, state);
> -	if (rc >= 0)
> -		return rc;
> -	if (rc != -EFAULT && rc != -EEXIST) {
> -		err("%s: Failure %d getting sensor state on slot[%s]\n",
> -		    __func__, rc, slot->name);
> -		return rc;
> -	}
> -
> -
> -	/*
> -	 * Some slots have to be powered up before
> -	 * get-sensor will succeed
> -	 */
> -	dbg("%s: Slot[%s] must be power up to get sensor-state\n",
> -	    __func__, slot->name);
> -	rc = rtas_set_power_level(slot->power_domain, POWER_ON,
> -				  &setlevel);
> -	if (rc < 0) {
> -		dbg("%s: Failure %d powerng on slot[%s]\n",
> -		    __func__, rc, slot->name);
> -		return rc;
> -	}
> -
> -	return rtas_get_sensor(DR_ENTITY_SENSE,
> -			       slot->index, state);
> -}
> -
> -/**
> - * rpaphp_enable_slot - record slot state, config pci device
> - * @slot: target &slot
> - *
> - * Initialize values in the slot, and the hotplug_slot info
> - * structures to indicate if there is a pci card plugged into
> - * the slot. If the slot is not empty, run the pcibios routine
> - * to get pcibios stuff correctly set up.
> - */
> -int rpaphp_enable_slot(struct slot *slot)
> -{
> -	int rc, level, state;
> -	struct pci_bus *bus;
> -	struct hotplug_slot_info *info = slot->hotplug_slot->info;
> -
> -	info->adapter_status = NOT_VALID;
> -	slot->state = EMPTY;
> -
> -	/* Find out if the power is turned on for the slot */
> -	rc = rtas_get_power_level(slot->power_domain, &level);
> -	if (rc)
> -		return rc;
> -	info->power_status = level;
> -
> -	/* Figure out if there is an adapter in the slot */
> -	rc = rpaphp_get_sensor_state(slot, &state);
> -	if (rc)
> -		return rc;
> -
> -	bus = pcibios_find_pci_bus(slot->dn);
> -	if (!bus) {
> -		err("%s: no pci_bus for dn %s\n", __func__, slot->dn->full_name);
> -		return -EINVAL;
> -	}
> -
> -	info->adapter_status = EMPTY;
> -	slot->bus = bus;
> -	slot->pci_devs = &bus->devices;
> -
> -	/* if there's an adapter in the slot, go add the pci devices */
> -	if (state == PRESENT) {
> -		info->adapter_status = NOT_CONFIGURED;
> -		slot->state = NOT_CONFIGURED;
> -
> -		/* non-empty slot has to have child */
> -		if (!slot->dn->child) {
> -			err("%s: slot[%s]'s device_node doesn't have child for adapter\n",
> -			    __func__, slot->name);
> -			return -EINVAL;
> -		}
> -
> -		if (list_empty(&bus->devices))
> -			pcibios_add_pci_devices(bus);
> -
> -		if (!list_empty(&bus->devices)) {
> -			info->adapter_status = CONFIGURED;
> -			slot->state = CONFIGURED;
> -		}
> -
> -		if (rpaphp_debug) {
> -			struct pci_dev *dev;
> -			dbg("%s: pci_devs of slot[%s]\n", __func__, slot->dn->full_name);
> -			list_for_each_entry (dev, &bus->devices, bus_list)
> -				dbg("\t%s\n", pci_name(dev));
> -		}
> -	}
> -
> -	return 0;
> -}
> diff --git a/drivers/pci/hotplug/rpaphp_rtas.c b/drivers/pci/hotplug/rpaphp_rtas.c
> new file mode 100644
> index 0000000..74f024a
> --- /dev/null
> +++ b/drivers/pci/hotplug/rpaphp_rtas.c
> @@ -0,0 +1,320 @@
> +/*
> + * RTAS backend for RPA-compliant PP64 platform
> + *
> + * 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.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/pci.h>
> +#include <linux/pci_hotplug.h>
> +#include <linux/smp.h>
> +#include <linux/init.h>
> +#include <linux/vmalloc.h>
> +#include <asm/eeh.h>
> +#include <asm/rtas.h>
> +#include <asm/pci-bridge.h>
> +
> +#include "../pci.h"
> +#include "rpaphp.h"
> +
> +#define	RPA_PHP_DR_INDICATOR	9002
> +#define RPA_PHP_DR_ENTITY_SENSE	9003
> +
> +static int get_power_status(struct hotplug_slot *hp_slot, u8 *val)
> +{
> +	struct rpa_php_slot *slot = hp_slot->private;
> +	int state, ret;
> +
> +	/* By default, the power is on */
> +	*val = RPA_PHP_SLOT_POWER_ON;
> +
> +	/* Retrieve power state from firmware, which might fail */
> +	ret = rtas_get_power_level(slot->power_domain, &state);
> +	if (!ret) {
> +		if (state > 0)
> +			hp_slot->info->power_status = RPA_PHP_SLOT_POWER_ON;
> +		else
> +			hp_slot->info->power_status = RPA_PHP_SLOT_POWER_OFF;
> +		*val = hp_slot->info->power_status;
> +	}
> +
> +	return 0;
> +}
> +
> +static int get_adapter_status(struct hotplug_slot *hp_slot, u8 *val)
> +{
> +	struct rpa_php_slot *slot = hp_slot->private;
> +        int state, ret;
> +
> +	/* By default, the slot is empty */
> +	*val = RPA_PHP_SLOT_EMPTY;
> +
> +	/* Retrieve presence from firmware */
> +	ret = rtas_get_sensor(RPA_PHP_DR_ENTITY_SENSE, slot->index, &state);
> +	if (ret >= 0) {
> +		if (state > 0)
> +			hp_slot->info->adapter_status = RPA_PHP_SLOT_PRESENT;
> +		else
> +			hp_slot->info->adapter_status = RPA_PHP_SLOT_EMPTY;
> +		*val = hp_slot->info->adapter_status;
> +		return 0;
> +	}
> +
> +	/* Check if we need power slot on and retry */
> +	if (ret != -EFAULT && ret != -EEXIST) {
> +		err("%s: Error %d getting slot[%s] presence\n",
> +		    __func__, ret, slot->name);
> +		return ret;
> +	}
> +
> +	/* Power slot on, which might fail */
> +	ret = rtas_set_power_level(slot->power_domain,
> +				   RPA_PHP_SLOT_POWER_ON, &state);
> +	if (!ret)
> +		hp_slot->info->power_status = RPA_PHP_SLOT_POWER_ON;
> +
> +	/* Recheck the presence */
> +	ret = rtas_get_sensor(RPA_PHP_DR_ENTITY_SENSE, slot->index, &state);
> +	if (ret >= 0) {
> +		if (state > 0)
> +			hp_slot->info->adapter_status = RPA_PHP_SLOT_PRESENT;
> +		else
> +			hp_slot->info->adapter_status = RPA_PHP_SLOT_EMPTY;
> +		*val = hp_slot->info->adapter_status;
> +	}
> +
> +	return 0;
> +}
> +
> +static int set_attention_status(struct hotplug_slot *hp_slot, u8 val)
> +{
> +	struct rpa_php_slot *slot = hp_slot->private;
> +	int ret;
> +
> +	/*
> +	 * The default operation would to turn on
> +	 * the attention
> +	 */
> +	switch (val) {
> +	case RPA_PHP_SLOT_ATTEN_OFF:
> +	case RPA_PHP_SLOT_ATTEN_ON:
> +	case RPA_PHP_SLOT_ATTEN_IND:
> +	case RPA_PHP_SLOT_ATTEN_ACT:
> +		break;
> +	default:
> +		val = RPA_PHP_SLOT_ATTEN_ON;
> +	}
> +
> +	/* Set the attention */
> +	ret = rtas_set_indicator(RPA_PHP_DR_INDICATOR, slot->index, val);
> +	if (!ret)
> +		hp_slot->info->attention_status = val;
> +
> +	return ret;
> +}
> +
> +static enum pci_bus_speed get_max_bus_speed(struct rpa_php_slot *slot)
> +{
> +	enum pci_bus_speed speed;
> +
> +	switch (slot->type) {
> +	case 1 ... 6:
> +		speed = PCI_SPEED_33MHz;
> +		break;
> +	case 7 ... 8:
> +		speed = PCI_SPEED_66MHz;
> +		break;
> +	case 11:
> +	case 14:
> +		speed = PCI_SPEED_66MHz_PCIX;
> +		break;
> +	case 12:
> +	case 15:
> +		speed = PCI_SPEED_100MHz_PCIX;
> +		break;
> +	case 13:
> +	case 16:
> +		speed = PCI_SPEED_133MHz_PCIX;
> +		break;
> +	default:
> +		speed = PCI_SPEED_UNKNOWN;
> +	}
> +
> +	return speed;
> +}
> +
> +static int enable_slot(struct hotplug_slot *hp_slot)
> +{
> +	struct rpa_php_slot *slot = hp_slot->private;
> +	uint8_t presence;
> +	int ret;
> +
> +	/* Check if the slot has been configured */
> +	if (slot->state == RPA_PHP_SLOT_CONFIGURED)
> +		return 0;
> +
> +	/* Retrieve slot presence status */
> +	ret = hp_slot->ops->get_adapter_status(hp_slot, &presence);
> +	if (ret)
> +		return ret;
> +
> +	switch (presence) {
> +	case RPA_PHP_SLOT_PRESENT:
> +		pci_lock_rescan_remove();
> +		pcibios_add_pci_devices(slot->bus);
> +		pci_unlock_rescan_remove();
> +		slot->state = RPA_PHP_SLOT_CONFIGURED;
> +		break;
> +	case RPA_PHP_SLOT_EMPTY:
> +		slot->state = RPA_PHP_SLOT_NOT_CONFIGURED;
> +		break;
> +	default:
> +		slot->state = RPA_PHP_SLOT_NOT_VALID;
> +		return -EINVAL;
> +	}
> +
> +	/* Fix the bus maximal speed */
> +	slot->bus->max_bus_speed = get_max_bus_speed(slot);
> +	return 0;
> +}
> +
> +static int disable_slot(struct hotplug_slot *hp_slot)
> +{
> +	struct rpa_php_slot *slot = hp_slot->private;
> +
> +	if (slot->state != RPA_PHP_SLOT_CONFIGURED)
> +		return 0;
> +
> +	pci_lock_rescan_remove();
> +	pcibios_remove_pci_devices(slot->bus);
> +	pci_unlock_rescan_remove();
> +	vm_unmap_aliases();
> +
> +	slot->state = RPA_PHP_SLOT_NOT_CONFIGURED;
> +	return 0;
> +}
> +
> +static struct hotplug_slot_ops rpaphp_rtas_ops = {
> +	.enable_slot		= enable_slot,
> +	.disable_slot		= disable_slot,
> +	.set_attention_status	= set_attention_status,
> +	.get_power_status	= get_power_status,
> +	.get_adapter_status	= get_adapter_status,
> +};
> +
> +static int parse_drc_props(struct device_node *dn, u32 drc_index,
> +                           char **drc_name, char **drc_type, u32 *drc_power)
> +{
> +	const u32 *indexes, *names, *types, *domains;
> +	char *name, *type;
> +	struct device_node *parent = dn;
> +	u32 i;
> +
> +	while ((parent = of_get_parent(parent))) {
> +		indexes = of_get_property(parent, "ibm,drc-indexes", NULL);
> +		names   = of_get_property(parent, "ibm,drc-names", NULL);
> +		types   = of_get_property(parent, "ibm,drc-types", NULL);
> +		domains = of_get_property(parent, "ibm,drc-power-domains", NULL);
> +
> +		if (!indexes || !names || !types || !domains) {
> +			of_node_put(parent);
> +			continue;
> +		}
> +
> +		name = (char *)&names[1];
> +		type = (char *)&types[1];
> +		for (i = 0; i < be32_to_cpu(indexes[0]); i++) {
> +			if (be32_to_cpu(indexes[i + 1]) != drc_index) {
> +				name += (strlen(name) + 1);
> +				type += (strlen(type) + 1);
> +				continue;
> +			}
> +
> +			/* Matched index */
> +			if (drc_name)
> +				*drc_name = name;
> +			if (drc_type)
> +				*drc_type = type;
> +			if (drc_power)
> +				*drc_power = be32_to_cpu(domains[i + 1]);
> +
> +			of_node_put(parent);
> +			return 0;
> +		}
> +
> +		/* Next level parent */
> +		of_node_put(parent);
> +	}
> +
> +	return -ENODEV;
> +}
> +
> +/*
> + * 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)
> +{
> +	const u32 *my_index;
> +
> +	/* Check if node is capable of hotplug */
> +	my_index = of_get_property(dn, "ibm,my-drc-index", NULL);
> +	if (!my_index)
> +		return -EINVAL;
> +	if (drc_index)
> +		*drc_index = be32_to_cpu(*my_index);
> +
> +	return parse_drc_props(dn, be32_to_cpu(*my_index),
> +			       drc_name, drc_type, drc_power);
> +}
> +EXPORT_SYMBOL_GPL(rpaphp_get_drc_props);
> +
> +struct rpa_php_slot *rpaphp_rtas_add_slot(struct device_node *dn)
> +{
> +	char *name, *type, *endptr;
> +	int index, power_domain;
> +	struct rpa_php_slot *slot;
> +	struct pci_bus *bus;
> +	int val, ret;
> +
> +	/* Get and parse the hotplug properties */
> +	ret = rpaphp_get_drc_props(dn, &index, &name, &type, &power_domain);
> +	if (ret)
> +		return NULL;
> +
> +	/*
> +	 * PCI hotplug slots have integer DRC type. That of
> +	 * PHB slot is fixed to "PHB"
> +	 */
> +        val = simple_strtoul(type, &endptr, 10);
> +	if (strcmp(type, "PHB") && (endptr == type))
> +		return NULL;
> +
> +	slot = alloc_slot_struct(dn, index, name, power_domain);
> +	if (!slot)
> +		return NULL;
> +
> +        /* The slot should have an associated bus */
> +	bus = pcibios_find_pci_bus(dn);
> +	if (!bus) {
> +		err("%s: No PCI bus for device node %s\n",
> +			__func__, dn->full_name);
> +		goto fail;
> +	}
> +
> +	slot->hotplug_slot->ops = &rpaphp_rtas_ops;
> +	slot->type     = val;
> +	slot->bus      = bus;
> +	slot->pci_devs = &bus->devices;
> +	return slot;
> +fail:
> +	dealloc_slot_struct(slot);
> +	return NULL;
> +}
> diff --git a/drivers/pci/hotplug/rpaphp_slot.c b/drivers/pci/hotplug/rpaphp_slot.c
> deleted file mode 100644
> index be48e69..0000000
> --- a/drivers/pci/hotplug/rpaphp_slot.c
> +++ /dev/null
> @@ -1,140 +0,0 @@
> -/*
> - * RPA Virtual I/O device functions
> - * Copyright (C) 2004 Linda Xie <[email-masked]>
> - *
> - * 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 <[email-masked]>
> - *
> - */
> -#include <linux/kernel.h>
> -#include <linux/module.h>
> -#include <linux/sysfs.h>
> -#include <linux/pci.h>
> -#include <linux/string.h>
> -#include <linux/slab.h>
> -
> -#include <asm/rtas.h>
> -#include "rpaphp.h"
> -
> -/* free up the memory used by a slot */
> -static void rpaphp_release_slot(struct hotplug_slot *hotplug_slot)
> -{
> -	struct slot *slot = (struct slot *) hotplug_slot->private;
> -	dealloc_slot_struct(slot);
> -}
> -
> -void dealloc_slot_struct(struct slot *slot)
> -{
> -	kfree(slot->hotplug_slot->info);
> -	kfree(slot->name);
> -	kfree(slot->hotplug_slot);
> -	kfree(slot);
> -}
> -
> -struct slot *alloc_slot_struct(struct device_node *dn,
> -                       int drc_index, char *drc_name, int power_domain)
> -{
> -	struct slot *slot;
> -
> -	slot = kzalloc(sizeof(struct slot), GFP_KERNEL);
> -	if (!slot)
> -		goto error_nomem;
> -	slot->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
> -	if (!slot->hotplug_slot)
> -		goto error_slot;
> -	slot->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
> -					   GFP_KERNEL);
> -	if (!slot->hotplug_slot->info)
> -		goto error_hpslot;
> -	slot->name = kstrdup(drc_name, GFP_KERNEL);
> -	if (!slot->name)
> -		goto error_info;
> -	slot->dn = dn;
> -	slot->index = drc_index;
> -	slot->power_domain = power_domain;
> -	slot->hotplug_slot->private = slot;
> -	slot->hotplug_slot->ops = &rpaphp_hotplug_slot_ops;
> -	slot->hotplug_slot->release = &rpaphp_release_slot;
> -
> -	return slot;
> -
> -error_info:
> -	kfree(slot->hotplug_slot->info);
> -error_hpslot:
> -	kfree(slot->hotplug_slot);
> -error_slot:
> -	kfree(slot);
> -error_nomem:
> -	return NULL;
> -}
> -
> -int rpaphp_deregister_slot(struct slot *slot)
> -{
> -	int retval = 0;
> -	struct hotplug_slot *php_slot = slot->hotplug_slot;
> -
> -	 dbg("%s - Entry: deregistering slot=%s\n",
> -		__func__, slot->name);
> -
> -	list_del(&slot->rpaphp_slot_list);
> -
> -	retval = pci_hp_deregister(php_slot);
> -	if (retval)
> -		err("Problem unregistering a slot %s\n", slot->name);
> -
> -	dbg("%s - Exit: rc[%d]\n", __func__, retval);
> -	return retval;
> -}
> -EXPORT_SYMBOL_GPL(rpaphp_deregister_slot);
> -
> -int rpaphp_register_slot(struct slot *slot)
> -{
> -	struct hotplug_slot *php_slot = slot->hotplug_slot;
> -	struct slot *tmp;
> -	int retval;
> -	int slotno;
> -
> -	dbg("%s registering slot:path[%s] index[%x], name[%s] pdomain[%x] type[%d]\n",
> -		__func__, slot->dn->full_name, slot->index, slot->name,
> -		slot->power_domain, slot->type);
> -
> -	/* Should not try to register the same slot twice */
> -	list_for_each_entry(tmp, &rpaphp_slot_head, rpaphp_slot_list) {
> -		if (!strcmp(tmp->name, slot->name)) {
> -			err("%s: Slot[%s] is already registered\n",
> -			    __func__, slot->name);
> -			return -EAGAIN;
> -		}
> -	}
> -
> -	if (slot->dn->child)
> -		slotno = PCI_SLOT(PCI_DN(slot->dn->child)->devfn);
> -	else
> -		slotno = -1;
> -	retval = pci_hp_register(php_slot, slot->bus, slotno, slot->name);
> -	if (retval) {
> -		err("pci_hp_register failed with error %d\n", retval);
> -		return retval;
> -	}
> -
> -	/* add slot to our internal list */
> -	list_add(&slot->rpaphp_slot_list, &rpaphp_slot_head);
> -	info("Slot [%s] registered\n", slot->name);
> -	return 0;
> -}