xref: /openbmc/linux/drivers/pci/hotplug/rpaphp_slot.c (revision f6afbad82c6b7bab0198442592b110341fb419ba)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * RPA Virtual I/O device functions
31da177e4SLinus Torvalds  * Copyright (C) 2004 Linda Xie <lxie@us.ibm.com>
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * All rights reserved.
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * This program is free software; you can redistribute it and/or modify
81da177e4SLinus Torvalds  * it under the terms of the GNU General Public License as published by
91da177e4SLinus Torvalds  * the Free Software Foundation; either version 2 of the License, or (at
101da177e4SLinus Torvalds  * your option) any later version.
111da177e4SLinus Torvalds  *
121da177e4SLinus Torvalds  * This program is distributed in the hope that it will be useful, but
131da177e4SLinus Torvalds  * WITHOUT ANY WARRANTY; without even the implied warranty of
141da177e4SLinus Torvalds  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
151da177e4SLinus Torvalds  * NON INFRINGEMENT.  See the GNU General Public License for more
161da177e4SLinus Torvalds  * details.
171da177e4SLinus Torvalds  *
181da177e4SLinus Torvalds  * You should have received a copy of the GNU General Public License
191da177e4SLinus Torvalds  * along with this program; if not, write to the Free Software
201da177e4SLinus Torvalds  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
211da177e4SLinus Torvalds  *
221da177e4SLinus Torvalds  * Send feedback to <lxie@us.ibm.com>
231da177e4SLinus Torvalds  *
241da177e4SLinus Torvalds  */
251da177e4SLinus Torvalds #include <linux/kernel.h>
261da177e4SLinus Torvalds #include <linux/module.h>
271da177e4SLinus Torvalds #include <linux/kobject.h>
281da177e4SLinus Torvalds #include <linux/sysfs.h>
291da177e4SLinus Torvalds #include <linux/pci.h>
304e57b681STim Schmielau #include <linux/string.h>
314e57b681STim Schmielau #include <linux/slab.h>
324e57b681STim Schmielau 
331da177e4SLinus Torvalds #include <asm/rtas.h>
341da177e4SLinus Torvalds #include "rpaphp.h"
351da177e4SLinus Torvalds 
361da177e4SLinus Torvalds static ssize_t location_read_file (struct hotplug_slot *php_slot, char *buf)
371da177e4SLinus Torvalds {
381da177e4SLinus Torvalds 	char *value;
391da177e4SLinus Torvalds 	int retval = -ENOENT;
401da177e4SLinus Torvalds 	struct slot *slot = (struct slot *)php_slot->private;
411da177e4SLinus Torvalds 
421da177e4SLinus Torvalds 	if (!slot)
431da177e4SLinus Torvalds 		return retval;
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds 	value = slot->location;
461da177e4SLinus Torvalds 	retval = sprintf (buf, "%s\n", value);
471da177e4SLinus Torvalds 	return retval;
481da177e4SLinus Torvalds }
491da177e4SLinus Torvalds 
501da177e4SLinus Torvalds static struct hotplug_slot_attribute hotplug_slot_attr_location = {
511da177e4SLinus Torvalds 	.attr = {.name = "phy_location", .mode = S_IFREG | S_IRUGO},
521da177e4SLinus Torvalds 	.show = location_read_file,
531da177e4SLinus Torvalds };
541da177e4SLinus Torvalds 
551da177e4SLinus Torvalds static void rpaphp_sysfs_add_attr_location (struct hotplug_slot *slot)
561da177e4SLinus Torvalds {
571da177e4SLinus Torvalds 	sysfs_create_file(&slot->kobj, &hotplug_slot_attr_location.attr);
581da177e4SLinus Torvalds }
591da177e4SLinus Torvalds 
601da177e4SLinus Torvalds static void rpaphp_sysfs_remove_attr_location (struct hotplug_slot *slot)
611da177e4SLinus Torvalds {
621da177e4SLinus Torvalds 	sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_location.attr);
631da177e4SLinus Torvalds }
641da177e4SLinus Torvalds 
651da177e4SLinus Torvalds /* free up the memory used by a slot */
661da177e4SLinus Torvalds static void rpaphp_release_slot(struct hotplug_slot *hotplug_slot)
671da177e4SLinus Torvalds {
681da177e4SLinus Torvalds 	struct slot *slot = (struct slot *) hotplug_slot->private;
691da177e4SLinus Torvalds 
701da177e4SLinus Torvalds 	dealloc_slot_struct(slot);
711da177e4SLinus Torvalds }
721da177e4SLinus Torvalds 
731da177e4SLinus Torvalds void dealloc_slot_struct(struct slot *slot)
741da177e4SLinus Torvalds {
751da177e4SLinus Torvalds 	kfree(slot->hotplug_slot->info);
761da177e4SLinus Torvalds 	kfree(slot->hotplug_slot->name);
771da177e4SLinus Torvalds 	kfree(slot->hotplug_slot);
781da177e4SLinus Torvalds 	kfree(slot);
791da177e4SLinus Torvalds 	return;
801da177e4SLinus Torvalds }
811da177e4SLinus Torvalds 
821da177e4SLinus Torvalds struct slot *alloc_slot_struct(struct device_node *dn, int drc_index, char *drc_name,
831da177e4SLinus Torvalds 		  int power_domain)
841da177e4SLinus Torvalds {
851da177e4SLinus Torvalds 	struct slot *slot;
861da177e4SLinus Torvalds 
871da177e4SLinus Torvalds 	slot = kmalloc(sizeof (struct slot), GFP_KERNEL);
881da177e4SLinus Torvalds 	if (!slot)
891da177e4SLinus Torvalds 		goto error_nomem;
901da177e4SLinus Torvalds 	memset(slot, 0, sizeof (struct slot));
911da177e4SLinus Torvalds 	slot->hotplug_slot = kmalloc(sizeof (struct hotplug_slot), GFP_KERNEL);
921da177e4SLinus Torvalds 	if (!slot->hotplug_slot)
931da177e4SLinus Torvalds 		goto error_slot;
941da177e4SLinus Torvalds 	memset(slot->hotplug_slot, 0, sizeof (struct hotplug_slot));
951da177e4SLinus Torvalds 	slot->hotplug_slot->info = kmalloc(sizeof (struct hotplug_slot_info),
961da177e4SLinus Torvalds 					   GFP_KERNEL);
971da177e4SLinus Torvalds 	if (!slot->hotplug_slot->info)
981da177e4SLinus Torvalds 		goto error_hpslot;
991da177e4SLinus Torvalds 	memset(slot->hotplug_slot->info, 0, sizeof (struct hotplug_slot_info));
1001da177e4SLinus Torvalds 	slot->hotplug_slot->name = kmalloc(BUS_ID_SIZE + 1, GFP_KERNEL);
1011da177e4SLinus Torvalds 	if (!slot->hotplug_slot->name)
1021da177e4SLinus Torvalds 		goto error_info;
1031da177e4SLinus Torvalds 	slot->location = kmalloc(strlen(drc_name) + 1, GFP_KERNEL);
1041da177e4SLinus Torvalds 	if (!slot->location)
1051da177e4SLinus Torvalds 		goto error_name;
1061da177e4SLinus Torvalds 	slot->name = slot->hotplug_slot->name;
1071da177e4SLinus Torvalds 	slot->dn = dn;
1081da177e4SLinus Torvalds 	slot->index = drc_index;
1091da177e4SLinus Torvalds 	strcpy(slot->location, drc_name);
1101da177e4SLinus Torvalds 	slot->power_domain = power_domain;
1111da177e4SLinus Torvalds 	slot->hotplug_slot->private = slot;
1121da177e4SLinus Torvalds 	slot->hotplug_slot->ops = &rpaphp_hotplug_slot_ops;
1131da177e4SLinus Torvalds 	slot->hotplug_slot->release = &rpaphp_release_slot;
1141da177e4SLinus Torvalds 
1151da177e4SLinus Torvalds 	return (slot);
1161da177e4SLinus Torvalds 
1171da177e4SLinus Torvalds error_name:
1181da177e4SLinus Torvalds 	kfree(slot->hotplug_slot->name);
1191da177e4SLinus Torvalds error_info:
1201da177e4SLinus Torvalds 	kfree(slot->hotplug_slot->info);
1211da177e4SLinus Torvalds error_hpslot:
1221da177e4SLinus Torvalds 	kfree(slot->hotplug_slot);
1231da177e4SLinus Torvalds error_slot:
1241da177e4SLinus Torvalds 	kfree(slot);
1251da177e4SLinus Torvalds error_nomem:
1261da177e4SLinus Torvalds 	return NULL;
1271da177e4SLinus Torvalds }
1281da177e4SLinus Torvalds 
1291da177e4SLinus Torvalds static int is_registered(struct slot *slot)
1301da177e4SLinus Torvalds {
1311da177e4SLinus Torvalds 	struct slot             *tmp_slot;
1321da177e4SLinus Torvalds 
1331da177e4SLinus Torvalds 	list_for_each_entry(tmp_slot, &rpaphp_slot_head, rpaphp_slot_list) {
1341da177e4SLinus Torvalds 		if (!strcmp(tmp_slot->name, slot->name))
1351da177e4SLinus Torvalds 			return 1;
1361da177e4SLinus Torvalds 	}
1371da177e4SLinus Torvalds 	return 0;
1381da177e4SLinus Torvalds }
1391da177e4SLinus Torvalds 
140*f6afbad8Slinas@austin.ibm.com int rpaphp_deregister_slot(struct slot *slot)
1411da177e4SLinus Torvalds {
1421da177e4SLinus Torvalds 	int retval = 0;
1431da177e4SLinus Torvalds 	struct hotplug_slot *php_slot = slot->hotplug_slot;
1441da177e4SLinus Torvalds 
1451da177e4SLinus Torvalds 	 dbg("%s - Entry: deregistering slot=%s\n",
1461da177e4SLinus Torvalds 		__FUNCTION__, slot->name);
1471da177e4SLinus Torvalds 
1481da177e4SLinus Torvalds 	list_del(&slot->rpaphp_slot_list);
1491da177e4SLinus Torvalds 
1501da177e4SLinus Torvalds 	/* remove "phy_location" file */
1511da177e4SLinus Torvalds 	rpaphp_sysfs_remove_attr_location(php_slot);
1521da177e4SLinus Torvalds 
1531da177e4SLinus Torvalds 	retval = pci_hp_deregister(php_slot);
1541da177e4SLinus Torvalds 	if (retval)
1551da177e4SLinus Torvalds 		err("Problem unregistering a slot %s\n", slot->name);
1561da177e4SLinus Torvalds 	else
1571da177e4SLinus Torvalds 		num_slots--;
1581da177e4SLinus Torvalds 
1591da177e4SLinus Torvalds 	dbg("%s - Exit: rc[%d]\n", __FUNCTION__, retval);
1601da177e4SLinus Torvalds 	return retval;
1611da177e4SLinus Torvalds }
1621da177e4SLinus Torvalds 
163*f6afbad8Slinas@austin.ibm.com int rpaphp_register_slot(struct slot *slot)
1641da177e4SLinus Torvalds {
1651da177e4SLinus Torvalds 	int retval;
1661da177e4SLinus Torvalds 
1671da177e4SLinus Torvalds 	dbg("%s registering slot:path[%s] index[%x], name[%s] pdomain[%x] type[%d]\n",
1681da177e4SLinus Torvalds 		__FUNCTION__, slot->dn->full_name, slot->index, slot->name,
1691da177e4SLinus Torvalds 		slot->power_domain, slot->type);
1701da177e4SLinus Torvalds 	/* should not try to register the same slot twice */
1711da177e4SLinus Torvalds 	if (is_registered(slot)) { /* should't be here */
172*f6afbad8Slinas@austin.ibm.com 		err("rpaphp_register_slot: slot[%s] is already registered\n", slot->name);
1731da177e4SLinus Torvalds 		rpaphp_release_slot(slot->hotplug_slot);
1741da177e4SLinus Torvalds 		return -EAGAIN;
1751da177e4SLinus Torvalds 	}
1761da177e4SLinus Torvalds 	retval = pci_hp_register(slot->hotplug_slot);
1771da177e4SLinus Torvalds 	if (retval) {
1781da177e4SLinus Torvalds 		err("pci_hp_register failed with error %d\n", retval);
1791da177e4SLinus Torvalds 		rpaphp_release_slot(slot->hotplug_slot);
1801da177e4SLinus Torvalds 		return retval;
1811da177e4SLinus Torvalds 	}
1821da177e4SLinus Torvalds 
1831da177e4SLinus Torvalds 	/* create "phy_locatoin" file */
1841da177e4SLinus Torvalds 	rpaphp_sysfs_add_attr_location(slot->hotplug_slot);
1851da177e4SLinus Torvalds 
1861da177e4SLinus Torvalds 	/* add slot to our internal list */
1871da177e4SLinus Torvalds 	dbg("%s adding slot[%s] to rpaphp_slot_list\n",
1881da177e4SLinus Torvalds 	    __FUNCTION__, slot->name);
1891da177e4SLinus Torvalds 
1901da177e4SLinus Torvalds 	list_add(&slot->rpaphp_slot_list, &rpaphp_slot_head);
1915eeb8c63SJohn Rose 	info("Slot [%s](PCI location=%s) registered\n", slot->name,
1925eeb8c63SJohn Rose 			slot->location);
1931da177e4SLinus Torvalds 	num_slots++;
1941da177e4SLinus Torvalds 	return 0;
1951da177e4SLinus Torvalds }
1961da177e4SLinus Torvalds 
1971da177e4SLinus Torvalds int rpaphp_get_power_status(struct slot *slot, u8 * value)
1981da177e4SLinus Torvalds {
1991da177e4SLinus Torvalds 	int rc = 0, level;
2001da177e4SLinus Torvalds 
2011da177e4SLinus Torvalds 	rc = rtas_get_power_level(slot->power_domain, &level);
20256d8456bSJohn Rose 	if (rc < 0) {
20356d8456bSJohn Rose 		err("failed to get power-level for slot(%s), rc=0x%x\n",
20456d8456bSJohn Rose 			slot->location, rc);
20556d8456bSJohn Rose 		return rc;
20656d8456bSJohn Rose 	}
20756d8456bSJohn Rose 
2081da177e4SLinus Torvalds 	dbg("%s the power level of slot %s(pwd-domain:0x%x) is %d\n",
2091da177e4SLinus Torvalds 		__FUNCTION__, slot->name, slot->power_domain, level);
2101da177e4SLinus Torvalds 	*value = level;
2111da177e4SLinus Torvalds 
2121da177e4SLinus Torvalds 	return rc;
2131da177e4SLinus Torvalds }
2141da177e4SLinus Torvalds 
2151da177e4SLinus Torvalds int rpaphp_set_attention_status(struct slot *slot, u8 status)
2161da177e4SLinus Torvalds {
2171da177e4SLinus Torvalds 	int rc;
2181da177e4SLinus Torvalds 
2191da177e4SLinus Torvalds 	/* status: LED_OFF or LED_ON */
2201da177e4SLinus Torvalds 	rc = rtas_set_indicator(DR_INDICATOR, slot->index, status);
2211da177e4SLinus Torvalds 	if (rc < 0)
2221da177e4SLinus Torvalds 		err("slot(name=%s location=%s index=0x%x) set attention-status(%d) failed! rc=0x%x\n",
2231da177e4SLinus Torvalds 		    slot->name, slot->location, slot->index, status, rc);
2241da177e4SLinus Torvalds 
2251da177e4SLinus Torvalds 	return rc;
2261da177e4SLinus Torvalds }
227