[PATCH 6/8] PCI/hotplug/rpa: Abstract slot operations
Benjamin Herrenschmidt
benh at kernel.crashing.org
Wed Nov 26 10:04:36 AEDT 2014
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 <gwshan at linux.vnet.ibm.com>
> ---
> 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 <lxie at 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");
> -
> 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 <lxie at 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 at us.ibm.com>
> - *
> - */
> -#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 <lxie at 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 at us.ibm.com>
> - *
> - */
> -#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;
> -}
More information about the Linuxppc-dev
mailing list