11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * PCI Express Hot Plug Controller Driver 31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (C) 1995,2001 Compaq Computer Corporation 51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) 61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (C) 2001 IBM Corp. 71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (C) 2003-2004 Intel Corporation 81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * All rights reserved. 101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or modify 121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * it under the terms of the GNU General Public License as published by 131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the Free Software Foundation; either version 2 of the License, or (at 141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * your option) any later version. 151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is distributed in the hope that it will be useful, but 171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * WITHOUT ANY WARRANTY; without even the implied warranty of 181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or 191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * NON INFRINGEMENT. See the GNU General Public License for more 201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * details. 211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * You should have received a copy of the GNU General Public License 231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * along with this program; if not, write to the Free Software 241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 268cf4c19523b7694c88bba716d88fb659fa702411Kristen Accardi * Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com> 271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h> 311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/moduleparam.h> 321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h> 335a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h> 341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/types.h> 351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/pci.h> 361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "pciehp.h" 371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/interrupt.h> 3834d03419f03bcfdf70d9617a9b90b60c93482c4aKristen Carlson Accardi#include <linux/time.h> 391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Global variables */ 4190ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellbool pciehp_debug; 4290ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellbool pciehp_poll_mode; 431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint pciehp_poll_time; 4490ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellbool pciehp_force; 455d386e1ac4025b4bcc6bad6811e771cb76064dfeKenji Kaneshigestruct workqueue_struct *pciehp_wq; 461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_VERSION "0.4" 481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_AUTHOR "Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>" 491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_DESC "PCI Express Hot Plug Controller Driver" 501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR(DRIVER_AUTHOR); 521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION(DRIVER_DESC); 531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL"); 541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(pciehp_debug, bool, 0644); 561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(pciehp_poll_mode, bool, 0644); 571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(pciehp_poll_time, int, 0644); 58a3a45ec8f8edaf088449e37fe81c99cbf580b9bdRajesh Shahmodule_param(pciehp_force, bool, 0644); 591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(pciehp_debug, "Debugging mode enabled or not"); 601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(pciehp_poll_mode, "Using polling mechanism for hot-plug events or not"); 611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds"); 6228eb5f274a305bf3a13b2c80c4804d4515d05c64Rafael J. WysockiMODULE_PARM_DESC(pciehp_force, "Force pciehp, even if OSHP is missing"); 631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define PCIE_MODULE_NAME "pciehp" 651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int set_attention_status (struct hotplug_slot *slot, u8 value); 671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int enable_slot (struct hotplug_slot *slot); 681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int disable_slot (struct hotplug_slot *slot); 691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int get_power_status (struct hotplug_slot *slot, u8 *value); 701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int get_attention_status (struct hotplug_slot *slot, u8 *value); 711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int get_latch_status (struct hotplug_slot *slot, u8 *value); 721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int get_adapter_status (struct hotplug_slot *slot, u8 *value); 731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 74b308240b49ff5a1bddc6e10513c2c83f37a0bc78Dely Sy/** 75b308240b49ff5a1bddc6e10513c2c83f37a0bc78Dely Sy * release_slot - free up the memory used by a slot 76b308240b49ff5a1bddc6e10513c2c83f37a0bc78Dely Sy * @hotplug_slot: slot to free 77b308240b49ff5a1bddc6e10513c2c83f37a0bc78Dely Sy */ 78b308240b49ff5a1bddc6e10513c2c83f37a0bc78Dely Systatic void release_slot(struct hotplug_slot *hotplug_slot) 79b308240b49ff5a1bddc6e10513c2c83f37a0bc78Dely Sy{ 807f2feec140f1f1e4f701e013a2bf8284a9ec2a3cTaku Izumi struct slot *slot = hotplug_slot->private; 817f2feec140f1f1e4f701e013a2bf8284a9ec2a3cTaku Izumi 8218b341b76cd99ce949806ccf5565900465ec2e7fTaku Izumi ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", 83e1acb24f059defdaa0264e925f19cc21b0a3e592Alex Chiang __func__, hotplug_slot_name(hotplug_slot)); 84b308240b49ff5a1bddc6e10513c2c83f37a0bc78Dely Sy 85586f1d6688c68a6c7fa4e6a00fa3968b16daef75Kenji Kaneshige kfree(hotplug_slot->ops); 86c4635eb06af700820d658a163f06aff12e17cfb2Kenji Kaneshige kfree(hotplug_slot->info); 87c4635eb06af700820d658a163f06aff12e17cfb2Kenji Kaneshige kfree(hotplug_slot); 88a0b1725720d9a023a1dee129234f5972056038c6Kenji Kaneshige} 89a0b1725720d9a023a1dee129234f5972056038c6Kenji Kaneshige 908720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshigestatic int init_slot(struct controller *ctrl) 911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 928720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshige struct slot *slot = ctrl->slot; 938720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshige struct hotplug_slot *hotplug = NULL; 948720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshige struct hotplug_slot_info *info = NULL; 95586f1d6688c68a6c7fa4e6a00fa3968b16daef75Kenji Kaneshige struct hotplug_slot_ops *ops = NULL; 96e1acb24f059defdaa0264e925f19cc21b0a3e592Alex Chiang char name[SLOT_NAME_SIZE]; 97a0b1725720d9a023a1dee129234f5972056038c6Kenji Kaneshige int retval = -ENOMEM; 981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 998720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshige hotplug = kzalloc(sizeof(*hotplug), GFP_KERNEL); 1008720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshige if (!hotplug) 1018720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshige goto out; 1028720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshige 1038720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshige info = kzalloc(sizeof(*info), GFP_KERNEL); 1048720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshige if (!info) 1058720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshige goto out; 1068720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshige 107586f1d6688c68a6c7fa4e6a00fa3968b16daef75Kenji Kaneshige /* Setup hotplug slot ops */ 108586f1d6688c68a6c7fa4e6a00fa3968b16daef75Kenji Kaneshige ops = kzalloc(sizeof(*ops), GFP_KERNEL); 109586f1d6688c68a6c7fa4e6a00fa3968b16daef75Kenji Kaneshige if (!ops) 110586f1d6688c68a6c7fa4e6a00fa3968b16daef75Kenji Kaneshige goto out; 111586f1d6688c68a6c7fa4e6a00fa3968b16daef75Kenji Kaneshige ops->enable_slot = enable_slot; 112586f1d6688c68a6c7fa4e6a00fa3968b16daef75Kenji Kaneshige ops->disable_slot = disable_slot; 113586f1d6688c68a6c7fa4e6a00fa3968b16daef75Kenji Kaneshige ops->get_power_status = get_power_status; 114586f1d6688c68a6c7fa4e6a00fa3968b16daef75Kenji Kaneshige ops->get_adapter_status = get_adapter_status; 115586f1d6688c68a6c7fa4e6a00fa3968b16daef75Kenji Kaneshige if (MRL_SENS(ctrl)) 116586f1d6688c68a6c7fa4e6a00fa3968b16daef75Kenji Kaneshige ops->get_latch_status = get_latch_status; 117586f1d6688c68a6c7fa4e6a00fa3968b16daef75Kenji Kaneshige if (ATTN_LED(ctrl)) { 118586f1d6688c68a6c7fa4e6a00fa3968b16daef75Kenji Kaneshige ops->get_attention_status = get_attention_status; 119586f1d6688c68a6c7fa4e6a00fa3968b16daef75Kenji Kaneshige ops->set_attention_status = set_attention_status; 120586f1d6688c68a6c7fa4e6a00fa3968b16daef75Kenji Kaneshige } 121586f1d6688c68a6c7fa4e6a00fa3968b16daef75Kenji Kaneshige 1228720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshige /* register this slot with the hotplug pci core */ 1238720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshige hotplug->info = info; 1248720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshige hotplug->private = slot; 1258720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshige hotplug->release = &release_slot; 126586f1d6688c68a6c7fa4e6a00fa3968b16daef75Kenji Kaneshige hotplug->ops = ops; 1278720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshige slot->hotplug_slot = hotplug; 12807a09694de556f307b1c5035cdf0f17c6243d1cdKenji Kaneshige snprintf(name, SLOT_NAME_SIZE, "%u", PSN(ctrl)); 1298720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshige 130a2359a334fb2c89347e031c4494282e6756e9ae7Kenji Kaneshige ctrl_dbg(ctrl, "Registering domain:bus:dev=%04x:%02x:00 sun=%x\n", 131385e24917ed8eeba25dddd8e63bf3fe3d53eafc5Kenji Kaneshige pci_domain_nr(ctrl->pcie->port->subordinate), 13207a09694de556f307b1c5035cdf0f17c6243d1cdKenji Kaneshige ctrl->pcie->port->subordinate->number, PSN(ctrl)); 1338720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshige retval = pci_hp_register(hotplug, 134385e24917ed8eeba25dddd8e63bf3fe3d53eafc5Kenji Kaneshige ctrl->pcie->port->subordinate, 0, name); 135445f798555e218a5601222ca5849e8553ddd866aKenji Kaneshige if (retval) 1368720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshige ctrl_err(ctrl, 1378720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshige "pci_hp_register failed with error %d\n", retval); 1388720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshigeout: 1398720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshige if (retval) { 140586f1d6688c68a6c7fa4e6a00fa3968b16daef75Kenji Kaneshige kfree(ops); 1418720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshige kfree(info); 1428720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshige kfree(hotplug); 1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 144a0b1725720d9a023a1dee129234f5972056038c6Kenji Kaneshige return retval; 1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1478720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshigestatic void cleanup_slot(struct controller *ctrl) 1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1498720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshige pci_hp_deregister(ctrl->slot->hotplug_slot); 1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * set_attention_status - Turns the Amber LED for a slot on, off or blink 1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status) 1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct slot *slot = hotplug_slot->private; 1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 15918b341b76cd99ce949806ccf5565900465ec2e7fTaku Izumi ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", 160e1acb24f059defdaa0264e925f19cc21b0a3e592Alex Chiang __func__, slot_name(slot)); 1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 162445f798555e218a5601222ca5849e8553ddd866aKenji Kaneshige return pciehp_set_attention_status(slot, status); 1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int enable_slot(struct hotplug_slot *hotplug_slot) 1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct slot *slot = hotplug_slot->private; 1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 17018b341b76cd99ce949806ccf5565900465ec2e7fTaku Izumi ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", 171e1acb24f059defdaa0264e925f19cc21b0a3e592Alex Chiang __func__, slot_name(slot)); 1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1735d386e1ac4025b4bcc6bad6811e771cb76064dfeKenji Kaneshige return pciehp_sysfs_enable_slot(slot); 1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int disable_slot(struct hotplug_slot *hotplug_slot) 1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct slot *slot = hotplug_slot->private; 1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 18118b341b76cd99ce949806ccf5565900465ec2e7fTaku Izumi ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", 182e1acb24f059defdaa0264e925f19cc21b0a3e592Alex Chiang __func__, slot_name(slot)); 1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1845d386e1ac4025b4bcc6bad6811e771cb76064dfeKenji Kaneshige return pciehp_sysfs_disable_slot(slot); 1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) 1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct slot *slot = hotplug_slot->private; 1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 19118b341b76cd99ce949806ccf5565900465ec2e7fTaku Izumi ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", 192e1acb24f059defdaa0264e925f19cc21b0a3e592Alex Chiang __func__, slot_name(slot)); 1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 194445f798555e218a5601222ca5849e8553ddd866aKenji Kaneshige return pciehp_get_power_status(slot, value); 1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value) 1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct slot *slot = hotplug_slot->private; 2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 20118b341b76cd99ce949806ccf5565900465ec2e7fTaku Izumi ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", 202e1acb24f059defdaa0264e925f19cc21b0a3e592Alex Chiang __func__, slot_name(slot)); 2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 204445f798555e218a5601222ca5849e8553ddd866aKenji Kaneshige return pciehp_get_attention_status(slot, value); 2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) 2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct slot *slot = hotplug_slot->private; 2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 21118b341b76cd99ce949806ccf5565900465ec2e7fTaku Izumi ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", 212e1acb24f059defdaa0264e925f19cc21b0a3e592Alex Chiang __func__, slot_name(slot)); 2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 214445f798555e218a5601222ca5849e8553ddd866aKenji Kaneshige return pciehp_get_latch_status(slot, value); 2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) 2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct slot *slot = hotplug_slot->private; 2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 22118b341b76cd99ce949806ccf5565900465ec2e7fTaku Izumi ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", 222e1acb24f059defdaa0264e925f19cc21b0a3e592Alex Chiang __func__, slot_name(slot)); 2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 224445f798555e218a5601222ca5849e8553ddd866aKenji Kaneshige return pciehp_get_adapter_status(slot, value); 2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2270516c8bcd25293f438573101c439ce25a18916adRafael J. Wysockistatic int pciehp_probe(struct pcie_device *dev) 2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int rc; 2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct controller *ctrl; 2318720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshige struct slot *slot; 2328792e11f1c54bcba34412f03959e70ee217f2231Kenji Kaneshige u8 occupied, poweron; 233125c39f7d233de28f342d80858025ffed0c4b7f4Kenji Kaneshige 234125c39f7d233de28f342d80858025ffed0c4b7f4Kenji Kaneshige if (pciehp_force) 2357f2feec140f1f1e4f701e013a2bf8284a9ec2a3cTaku Izumi dev_info(&dev->device, 2367f2feec140f1f1e4f701e013a2bf8284a9ec2a3cTaku Izumi "Bypassing BIOS check for pciehp use on %s\n", 2378792e11f1c54bcba34412f03959e70ee217f2231Kenji Kaneshige pci_name(dev->port)); 23828eb5f274a305bf3a13b2c80c4804d4515d05c64Rafael J. Wysocki else if (pciehp_acpi_slot_detection_check(dev->port)) 239125c39f7d233de28f342d80858025ffed0c4b7f4Kenji Kaneshige goto err_out_none; 240a073a8267400be2bd8acf808a45bc3ab01cf1b20Kenji Kaneshige 241c4635eb06af700820d658a163f06aff12e17cfb2Kenji Kaneshige ctrl = pcie_init(dev); 2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ctrl) { 24318b341b76cd99ce949806ccf5565900465ec2e7fTaku Izumi dev_err(&dev->device, "Controller initialization failed\n"); 244c4635eb06af700820d658a163f06aff12e17cfb2Kenji Kaneshige goto err_out_none; 2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 246b97089400d44b9e90ce5029a2e458cd087473c74Kenji Kaneshige set_service_data(dev, ctrl); 2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Setup the slot information structures */ 2498720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshige rc = init_slot(ctrl); 2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (rc) { 251f46753c5e354b857b20ab8e0fe7b2579831dc369Alex Chiang if (rc == -EBUSY) 25218b341b76cd99ce949806ccf5565900465ec2e7fTaku Izumi ctrl_warn(ctrl, "Slot already registered by another " 2537f2feec140f1f1e4f701e013a2bf8284a9ec2a3cTaku Izumi "hotplug driver\n"); 254f46753c5e354b857b20ab8e0fe7b2579831dc369Alex Chiang else 25518b341b76cd99ce949806ccf5565900465ec2e7fTaku Izumi ctrl_err(ctrl, "Slot initialization failed\n"); 256a8c2b635979823043ea7766dea1d9371773b4d8eKenji Kaneshige goto err_out_release_ctlr; 2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 259dbc7e1e567ef8cfc4b792ef6acb51d4ceb15746aEric W. Biederman /* Enable events after we have setup the data structures */ 260dbc7e1e567ef8cfc4b792ef6acb51d4ceb15746aEric W. Biederman rc = pcie_init_notification(ctrl); 261dbc7e1e567ef8cfc4b792ef6acb51d4ceb15746aEric W. Biederman if (rc) { 262dbc7e1e567ef8cfc4b792ef6acb51d4ceb15746aEric W. Biederman ctrl_err(ctrl, "Notification initialization failed\n"); 26365b947bc5f32d8d4fe1f925a6146a4088d5466f3Kenji Kaneshige goto err_out_free_ctrl_slot; 264dbc7e1e567ef8cfc4b792ef6acb51d4ceb15746aEric W. Biederman } 265dbc7e1e567ef8cfc4b792ef6acb51d4ceb15746aEric W. Biederman 266db9aaf0bf19886114935152996edd9c6683b741cKenji Kaneshige /* Check if slot is occupied */ 2678720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshige slot = ctrl->slot; 2688792e11f1c54bcba34412f03959e70ee217f2231Kenji Kaneshige pciehp_get_adapter_status(slot, &occupied); 2698792e11f1c54bcba34412f03959e70ee217f2231Kenji Kaneshige pciehp_get_power_status(slot, &poweron); 2708792e11f1c54bcba34412f03959e70ee217f2231Kenji Kaneshige if (occupied && pciehp_force) 2718792e11f1c54bcba34412f03959e70ee217f2231Kenji Kaneshige pciehp_enable_slot(slot); 2728792e11f1c54bcba34412f03959e70ee217f2231Kenji Kaneshige /* If empty slot's power status is on, turn power off */ 2738792e11f1c54bcba34412f03959e70ee217f2231Kenji Kaneshige if (!occupied && poweron && POWER_CTRL(ctrl)) 2748792e11f1c54bcba34412f03959e70ee217f2231Kenji Kaneshige pciehp_power_off_slot(slot); 2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserr_out_free_ctrl_slot: 2798720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshige cleanup_slot(ctrl); 280a8c2b635979823043ea7766dea1d9371773b4d8eKenji Kaneshigeerr_out_release_ctlr: 28182a9e79ef132cbf77de58aae35c1a14237f2fcdeKenji Kaneshige pciehp_release_ctrl(ctrl); 2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserr_out_none: 2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 28682a9e79ef132cbf77de58aae35c1a14237f2fcdeKenji Kaneshigestatic void pciehp_remove(struct pcie_device *dev) 2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 288b97089400d44b9e90ce5029a2e458cd087473c74Kenji Kaneshige struct controller *ctrl = get_service_data(dev); 2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2908720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshige cleanup_slot(ctrl); 29182a9e79ef132cbf77de58aae35c1a14237f2fcdeKenji Kaneshige pciehp_release_ctrl(ctrl); 2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_PM 2953a3c244c9a355105bc193fde873c73727bf87192Rafael J. Wysockistatic int pciehp_suspend (struct pcie_device *dev) 2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2977f2feec140f1f1e4f701e013a2bf8284a9ec2a3cTaku Izumi dev_info(&dev->device, "%s ENTRY\n", __func__); 2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int pciehp_resume (struct pcie_device *dev) 3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3037f2feec140f1f1e4f701e013a2bf8284a9ec2a3cTaku Izumi dev_info(&dev->device, "%s ENTRY\n", __func__); 304cd2fe83a81510acfd1ae29b8ffe04f7ef675c993Mark Lord if (pciehp_force) { 305b97089400d44b9e90ce5029a2e458cd087473c74Kenji Kaneshige struct controller *ctrl = get_service_data(dev); 3068720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshige struct slot *slot; 307cd2fe83a81510acfd1ae29b8ffe04f7ef675c993Mark Lord u8 status; 308cd2fe83a81510acfd1ae29b8ffe04f7ef675c993Mark Lord 309cd2fe83a81510acfd1ae29b8ffe04f7ef675c993Mark Lord /* reinitialize the chipset's event detection logic */ 310c4635eb06af700820d658a163f06aff12e17cfb2Kenji Kaneshige pcie_enable_notification(ctrl); 311cd2fe83a81510acfd1ae29b8ffe04f7ef675c993Mark Lord 3128720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshige slot = ctrl->slot; 313cd2fe83a81510acfd1ae29b8ffe04f7ef675c993Mark Lord 314cd2fe83a81510acfd1ae29b8ffe04f7ef675c993Mark Lord /* Check if slot is occupied */ 31582a9e79ef132cbf77de58aae35c1a14237f2fcdeKenji Kaneshige pciehp_get_adapter_status(slot, &status); 316cd2fe83a81510acfd1ae29b8ffe04f7ef675c993Mark Lord if (status) 3178720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshige pciehp_enable_slot(slot); 318cd2fe83a81510acfd1ae29b8ffe04f7ef675c993Mark Lord else 3198720d27dabf580278a7719fa8b5783d9878e2d42Kenji Kaneshige pciehp_disable_slot(slot); 320cd2fe83a81510acfd1ae29b8ffe04f7ef675c993Mark Lord } 3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3233a3c244c9a355105bc193fde873c73727bf87192Rafael J. Wysocki#endif /* PM */ 3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct pcie_port_service_driver hpdriver_portdrv = { 32683e9ad540b9ee23919961f9500ca254220b78d09Taku Izumi .name = PCIE_MODULE_NAME, 32722106368c999246c414610dcaacd485e741605b1Rafael J. Wysocki .port_type = PCIE_ANY_PORT, 32822106368c999246c414610dcaacd485e741605b1Rafael J. Wysocki .service = PCIE_PORT_SERVICE_HP, 3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .probe = pciehp_probe, 3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .remove = pciehp_remove, 3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_PM 3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .suspend = pciehp_suspend, 3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .resume = pciehp_resume, 3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif /* PM */ 3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init pcied_init(void) 3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int retval = 0; 3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 343a827ea307b147aeb050803433b3f6842582c6cedTejun Heo pciehp_wq = alloc_workqueue("pciehp", 0, 0); 344a827ea307b147aeb050803433b3f6842582c6cedTejun Heo if (!pciehp_wq) 345a827ea307b147aeb050803433b3f6842582c6cedTejun Heo return -ENOMEM; 346a827ea307b147aeb050803433b3f6842582c6cedTejun Heo 347c9ffa5a586a97da4d552f89b8f39eea79a63a612Kenji Kaneshige pciehp_firmware_init(); 348a8a2be949267cb0d1d933a92d9fb43eda4f4fe88Rajesh Shah retval = pcie_port_service_register(&hpdriver_portdrv); 349a8a2be949267cb0d1d933a92d9fb43eda4f4fe88Rajesh Shah dbg("pcie_port_service_register = %d\n", retval); 350a8a2be949267cb0d1d933a92d9fb43eda4f4fe88Rajesh Shah info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); 351a827ea307b147aeb050803433b3f6842582c6cedTejun Heo if (retval) { 352a827ea307b147aeb050803433b3f6842582c6cedTejun Heo destroy_workqueue(pciehp_wq); 35318b341b76cd99ce949806ccf5565900465ec2e7fTaku Izumi dbg("Failure to register service\n"); 354a827ea307b147aeb050803433b3f6842582c6cedTejun Heo } 3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return retval; 3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit pcied_cleanup(void) 3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dbg("unload_pciehpd()\n"); 361027e8d52abdd44bc00e405af83cd2fbfb96c0824Kenji Kaneshige pcie_port_service_unregister(&hpdriver_portdrv); 362a827ea307b147aeb050803433b3f6842582c6cedTejun Heo destroy_workqueue(pciehp_wq); 3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n"); 3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(pcied_init); 3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(pcied_cleanup); 368