xref: /openbmc/linux/drivers/pci/hotplug/rpaphp_slot.c (revision 5fd39c35a016150e93b68c472a04b2d4b5574a2b)
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 
50f1e79092SLinas Vepstas static struct hotplug_slot_attribute php_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 /* free up the memory used by a slot */
561da177e4SLinus Torvalds static void rpaphp_release_slot(struct hotplug_slot *hotplug_slot)
571da177e4SLinus Torvalds {
581da177e4SLinus Torvalds 	struct slot *slot = (struct slot *) hotplug_slot->private;
591da177e4SLinus Torvalds 	dealloc_slot_struct(slot);
601da177e4SLinus Torvalds }
611da177e4SLinus Torvalds 
621da177e4SLinus Torvalds void dealloc_slot_struct(struct slot *slot)
631da177e4SLinus Torvalds {
641da177e4SLinus Torvalds 	kfree(slot->hotplug_slot->info);
651da177e4SLinus Torvalds 	kfree(slot->hotplug_slot->name);
661da177e4SLinus Torvalds 	kfree(slot->hotplug_slot);
67*5fd39c35SLinas Vepstas 	kfree(slot->location);
681da177e4SLinus Torvalds 	kfree(slot);
691da177e4SLinus Torvalds }
701da177e4SLinus Torvalds 
71*5fd39c35SLinas Vepstas struct slot *alloc_slot_struct(struct device_node *dn,
72*5fd39c35SLinas Vepstas                        int drc_index, char *drc_name, int power_domain)
731da177e4SLinus Torvalds {
741da177e4SLinus Torvalds 	struct slot *slot;
751da177e4SLinus Torvalds 
76f5afe806SEric Sesterhenn 	slot = kzalloc(sizeof(struct slot), GFP_KERNEL);
771da177e4SLinus Torvalds 	if (!slot)
781da177e4SLinus Torvalds 		goto error_nomem;
79f5afe806SEric Sesterhenn 	slot->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
801da177e4SLinus Torvalds 	if (!slot->hotplug_slot)
811da177e4SLinus Torvalds 		goto error_slot;
82f5afe806SEric Sesterhenn 	slot->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
831da177e4SLinus Torvalds 					   GFP_KERNEL);
841da177e4SLinus Torvalds 	if (!slot->hotplug_slot->info)
851da177e4SLinus Torvalds 		goto error_hpslot;
861da177e4SLinus Torvalds 	slot->hotplug_slot->name = kmalloc(BUS_ID_SIZE + 1, GFP_KERNEL);
871da177e4SLinus Torvalds 	if (!slot->hotplug_slot->name)
881da177e4SLinus Torvalds 		goto error_info;
891da177e4SLinus Torvalds 	slot->location = kmalloc(strlen(drc_name) + 1, GFP_KERNEL);
901da177e4SLinus Torvalds 	if (!slot->location)
911da177e4SLinus Torvalds 		goto error_name;
921da177e4SLinus Torvalds 	slot->name = slot->hotplug_slot->name;
931da177e4SLinus Torvalds 	slot->dn = dn;
941da177e4SLinus Torvalds 	slot->index = drc_index;
951da177e4SLinus Torvalds 	strcpy(slot->location, drc_name);
961da177e4SLinus Torvalds 	slot->power_domain = power_domain;
971da177e4SLinus Torvalds 	slot->hotplug_slot->private = slot;
981da177e4SLinus Torvalds 	slot->hotplug_slot->ops = &rpaphp_hotplug_slot_ops;
991da177e4SLinus Torvalds 	slot->hotplug_slot->release = &rpaphp_release_slot;
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds 	return (slot);
1021da177e4SLinus Torvalds 
1031da177e4SLinus Torvalds error_name:
1041da177e4SLinus Torvalds 	kfree(slot->hotplug_slot->name);
1051da177e4SLinus Torvalds error_info:
1061da177e4SLinus Torvalds 	kfree(slot->hotplug_slot->info);
1071da177e4SLinus Torvalds error_hpslot:
1081da177e4SLinus Torvalds 	kfree(slot->hotplug_slot);
1091da177e4SLinus Torvalds error_slot:
1101da177e4SLinus Torvalds 	kfree(slot);
1111da177e4SLinus Torvalds error_nomem:
1121da177e4SLinus Torvalds 	return NULL;
1131da177e4SLinus Torvalds }
1141da177e4SLinus Torvalds 
1151da177e4SLinus Torvalds static int is_registered(struct slot *slot)
1161da177e4SLinus Torvalds {
1171da177e4SLinus Torvalds 	struct slot *tmp_slot;
1181da177e4SLinus Torvalds 
1191da177e4SLinus Torvalds 	list_for_each_entry(tmp_slot, &rpaphp_slot_head, rpaphp_slot_list) {
1201da177e4SLinus Torvalds 		if (!strcmp(tmp_slot->name, slot->name))
1211da177e4SLinus Torvalds 			return 1;
1221da177e4SLinus Torvalds 	}
1231da177e4SLinus Torvalds 	return 0;
1241da177e4SLinus Torvalds }
1251da177e4SLinus Torvalds 
126f6afbad8Slinas@austin.ibm.com int rpaphp_deregister_slot(struct slot *slot)
1271da177e4SLinus Torvalds {
1281da177e4SLinus Torvalds 	int retval = 0;
1291da177e4SLinus Torvalds 	struct hotplug_slot *php_slot = slot->hotplug_slot;
1301da177e4SLinus Torvalds 
1311da177e4SLinus Torvalds 	 dbg("%s - Entry: deregistering slot=%s\n",
1321da177e4SLinus Torvalds 		__FUNCTION__, slot->name);
1331da177e4SLinus Torvalds 
1341da177e4SLinus Torvalds 	list_del(&slot->rpaphp_slot_list);
1351da177e4SLinus Torvalds 
1361da177e4SLinus Torvalds 	/* remove "phy_location" file */
137f1e79092SLinas Vepstas 	sysfs_remove_file(&php_slot->kobj, &php_attr_location.attr);
1381da177e4SLinus Torvalds 
1391da177e4SLinus Torvalds 	retval = pci_hp_deregister(php_slot);
1401da177e4SLinus Torvalds 	if (retval)
1411da177e4SLinus Torvalds 		err("Problem unregistering a slot %s\n", slot->name);
1421da177e4SLinus Torvalds 
1431da177e4SLinus Torvalds 	dbg("%s - Exit: rc[%d]\n", __FUNCTION__, retval);
1441da177e4SLinus Torvalds 	return retval;
1451da177e4SLinus Torvalds }
14661ee9cd5SLinas Vepstas EXPORT_SYMBOL_GPL(rpaphp_deregister_slot);
1471da177e4SLinus Torvalds 
148f6afbad8Slinas@austin.ibm.com int rpaphp_register_slot(struct slot *slot)
1491da177e4SLinus Torvalds {
150f1e79092SLinas Vepstas 	struct hotplug_slot *php_slot = slot->hotplug_slot;
1511da177e4SLinus Torvalds 	int retval;
1521da177e4SLinus Torvalds 
1531da177e4SLinus Torvalds 	dbg("%s registering slot:path[%s] index[%x], name[%s] pdomain[%x] type[%d]\n",
1541da177e4SLinus Torvalds 		__FUNCTION__, slot->dn->full_name, slot->index, slot->name,
1551da177e4SLinus Torvalds 		slot->power_domain, slot->type);
156f1e79092SLinas Vepstas 
1571da177e4SLinus Torvalds 	/* should not try to register the same slot twice */
158f1e79092SLinas Vepstas 	if (is_registered(slot)) {
159f6afbad8Slinas@austin.ibm.com 		err("rpaphp_register_slot: slot[%s] is already registered\n", slot->name);
160f1e79092SLinas Vepstas 		retval = -EAGAIN;
161f1e79092SLinas Vepstas 		goto register_fail;
1621da177e4SLinus Torvalds 	}
163f1e79092SLinas Vepstas 
164f1e79092SLinas Vepstas 	retval = pci_hp_register(php_slot);
1651da177e4SLinus Torvalds 	if (retval) {
1661da177e4SLinus Torvalds 		err("pci_hp_register failed with error %d\n", retval);
167f1e79092SLinas Vepstas 		goto register_fail;
1681da177e4SLinus Torvalds 	}
1691da177e4SLinus Torvalds 
170f1e79092SLinas Vepstas 	/* create "phy_location" file */
171f1e79092SLinas Vepstas 	retval = sysfs_create_file(&php_slot->kobj, &php_attr_location.attr);
172f1e79092SLinas Vepstas 	if (retval) {
173f1e79092SLinas Vepstas 		err("sysfs_create_file failed with error %d\n", retval);
174f1e79092SLinas Vepstas 		goto sysfs_fail;
175f1e79092SLinas Vepstas 	}
1761da177e4SLinus Torvalds 
1771da177e4SLinus Torvalds 	/* add slot to our internal list */
1781da177e4SLinus Torvalds 	list_add(&slot->rpaphp_slot_list, &rpaphp_slot_head);
1795eeb8c63SJohn Rose 	info("Slot [%s](PCI location=%s) registered\n", slot->name,
1805eeb8c63SJohn Rose 			slot->location);
1811da177e4SLinus Torvalds 	return 0;
182f1e79092SLinas Vepstas 
183f1e79092SLinas Vepstas sysfs_fail:
184f1e79092SLinas Vepstas 	pci_hp_deregister(php_slot);
185f1e79092SLinas Vepstas register_fail:
186f1e79092SLinas Vepstas 	return retval;
1871da177e4SLinus Torvalds }
1881da177e4SLinus Torvalds 
1891da177e4SLinus Torvalds int rpaphp_get_power_status(struct slot *slot, u8 * value)
1901da177e4SLinus Torvalds {
1911da177e4SLinus Torvalds 	int rc = 0, level;
1921da177e4SLinus Torvalds 
1931da177e4SLinus Torvalds 	rc = rtas_get_power_level(slot->power_domain, &level);
19456d8456bSJohn Rose 	if (rc < 0) {
19556d8456bSJohn Rose 		err("failed to get power-level for slot(%s), rc=0x%x\n",
19656d8456bSJohn Rose 			slot->location, rc);
19756d8456bSJohn Rose 		return rc;
19856d8456bSJohn Rose 	}
19956d8456bSJohn Rose 
2001da177e4SLinus Torvalds 	dbg("%s the power level of slot %s(pwd-domain:0x%x) is %d\n",
2011da177e4SLinus Torvalds 		__FUNCTION__, slot->name, slot->power_domain, level);
2021da177e4SLinus Torvalds 	*value = level;
2031da177e4SLinus Torvalds 
2041da177e4SLinus Torvalds 	return rc;
2051da177e4SLinus Torvalds }
2061da177e4SLinus Torvalds 
2071da177e4SLinus Torvalds int rpaphp_set_attention_status(struct slot *slot, u8 status)
2081da177e4SLinus Torvalds {
2091da177e4SLinus Torvalds 	int rc;
2101da177e4SLinus Torvalds 
2111da177e4SLinus Torvalds 	/* status: LED_OFF or LED_ON */
2121da177e4SLinus Torvalds 	rc = rtas_set_indicator(DR_INDICATOR, slot->index, status);
2131da177e4SLinus Torvalds 	if (rc < 0)
2141da177e4SLinus Torvalds 		err("slot(name=%s location=%s index=0x%x) set attention-status(%d) failed! rc=0x%x\n",
2151da177e4SLinus Torvalds 		    slot->name, slot->location, slot->index, status, rc);
2161da177e4SLinus Torvalds 
2171da177e4SLinus Torvalds 	return rc;
2181da177e4SLinus Torvalds }
219