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