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 140f6afbad8Slinas@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 } 162*61ee9cd5SLinas Vepstas EXPORT_SYMBOL_GPL(rpaphp_deregister_slot); 1631da177e4SLinus Torvalds 164f6afbad8Slinas@austin.ibm.com int rpaphp_register_slot(struct slot *slot) 1651da177e4SLinus Torvalds { 1661da177e4SLinus Torvalds int retval; 1671da177e4SLinus Torvalds 1681da177e4SLinus Torvalds dbg("%s registering slot:path[%s] index[%x], name[%s] pdomain[%x] type[%d]\n", 1691da177e4SLinus Torvalds __FUNCTION__, slot->dn->full_name, slot->index, slot->name, 1701da177e4SLinus Torvalds slot->power_domain, slot->type); 1711da177e4SLinus Torvalds /* should not try to register the same slot twice */ 1721da177e4SLinus Torvalds if (is_registered(slot)) { /* should't be here */ 173f6afbad8Slinas@austin.ibm.com err("rpaphp_register_slot: slot[%s] is already registered\n", slot->name); 1741da177e4SLinus Torvalds rpaphp_release_slot(slot->hotplug_slot); 1751da177e4SLinus Torvalds return -EAGAIN; 1761da177e4SLinus Torvalds } 1771da177e4SLinus Torvalds retval = pci_hp_register(slot->hotplug_slot); 1781da177e4SLinus Torvalds if (retval) { 1791da177e4SLinus Torvalds err("pci_hp_register failed with error %d\n", retval); 1801da177e4SLinus Torvalds rpaphp_release_slot(slot->hotplug_slot); 1811da177e4SLinus Torvalds return retval; 1821da177e4SLinus Torvalds } 1831da177e4SLinus Torvalds 1841da177e4SLinus Torvalds /* create "phy_locatoin" file */ 1851da177e4SLinus Torvalds rpaphp_sysfs_add_attr_location(slot->hotplug_slot); 1861da177e4SLinus Torvalds 1871da177e4SLinus Torvalds /* add slot to our internal list */ 1881da177e4SLinus Torvalds dbg("%s adding slot[%s] to rpaphp_slot_list\n", 1891da177e4SLinus Torvalds __FUNCTION__, slot->name); 1901da177e4SLinus Torvalds 1911da177e4SLinus Torvalds list_add(&slot->rpaphp_slot_list, &rpaphp_slot_head); 1925eeb8c63SJohn Rose info("Slot [%s](PCI location=%s) registered\n", slot->name, 1935eeb8c63SJohn Rose slot->location); 1941da177e4SLinus Torvalds num_slots++; 1951da177e4SLinus Torvalds return 0; 1961da177e4SLinus Torvalds } 1971da177e4SLinus Torvalds 1981da177e4SLinus Torvalds int rpaphp_get_power_status(struct slot *slot, u8 * value) 1991da177e4SLinus Torvalds { 2001da177e4SLinus Torvalds int rc = 0, level; 2011da177e4SLinus Torvalds 2021da177e4SLinus Torvalds rc = rtas_get_power_level(slot->power_domain, &level); 20356d8456bSJohn Rose if (rc < 0) { 20456d8456bSJohn Rose err("failed to get power-level for slot(%s), rc=0x%x\n", 20556d8456bSJohn Rose slot->location, rc); 20656d8456bSJohn Rose return rc; 20756d8456bSJohn Rose } 20856d8456bSJohn Rose 2091da177e4SLinus Torvalds dbg("%s the power level of slot %s(pwd-domain:0x%x) is %d\n", 2101da177e4SLinus Torvalds __FUNCTION__, slot->name, slot->power_domain, level); 2111da177e4SLinus Torvalds *value = level; 2121da177e4SLinus Torvalds 2131da177e4SLinus Torvalds return rc; 2141da177e4SLinus Torvalds } 2151da177e4SLinus Torvalds 2161da177e4SLinus Torvalds int rpaphp_set_attention_status(struct slot *slot, u8 status) 2171da177e4SLinus Torvalds { 2181da177e4SLinus Torvalds int rc; 2191da177e4SLinus Torvalds 2201da177e4SLinus Torvalds /* status: LED_OFF or LED_ON */ 2211da177e4SLinus Torvalds rc = rtas_set_indicator(DR_INDICATOR, slot->index, status); 2221da177e4SLinus Torvalds if (rc < 0) 2231da177e4SLinus Torvalds err("slot(name=%s location=%s index=0x%x) set attention-status(%d) failed! rc=0x%x\n", 2241da177e4SLinus Torvalds slot->name, slot->location, slot->index, status, rc); 2251da177e4SLinus Torvalds 2261da177e4SLinus Torvalds return rc; 2271da177e4SLinus Torvalds } 228