brintos

brintos / linux-shallow public Read only

0
0
Text · 10.6 KiB · ff458e6 Raw
410 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * PCI Express Hot Plug Controller Driver4 *5 * Copyright (C) 1995,2001 Compaq Computer Corporation6 * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)7 * Copyright (C) 2001 IBM Corp.8 * Copyright (C) 2003-2004 Intel Corporation9 *10 * All rights reserved.11 *12 * Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com>13 *14 * Authors:15 *   Dan Zink <dan.zink@compaq.com>16 *   Greg Kroah-Hartman <greg@kroah.com>17 *   Dely Sy <dely.l.sy@intel.com>"18 */19 20#define pr_fmt(fmt) "pciehp: " fmt21#define dev_fmt pr_fmt22 23#include <linux/bitfield.h>24#include <linux/moduleparam.h>25#include <linux/kernel.h>26#include <linux/slab.h>27#include <linux/types.h>28#include <linux/pci.h>29#include "pciehp.h"30 31#include "../pci.h"32 33/* Global variables */34bool pciehp_poll_mode;35int pciehp_poll_time;36 37/*38 * not really modular, but the easiest way to keep compat with existing39 * bootargs behaviour is to continue using module_param here.40 */41module_param(pciehp_poll_mode, bool, 0644);42module_param(pciehp_poll_time, int, 0644);43MODULE_PARM_DESC(pciehp_poll_mode, "Using polling mechanism for hot-plug events or not");44MODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds");45 46static int set_attention_status(struct hotplug_slot *slot, u8 value);47static int get_power_status(struct hotplug_slot *slot, u8 *value);48static int get_latch_status(struct hotplug_slot *slot, u8 *value);49static int get_adapter_status(struct hotplug_slot *slot, u8 *value);50 51static int init_slot(struct controller *ctrl)52{53	struct hotplug_slot_ops *ops;54	char name[SLOT_NAME_SIZE];55	int retval;56 57	/* Setup hotplug slot ops */58	ops = kzalloc(sizeof(*ops), GFP_KERNEL);59	if (!ops)60		return -ENOMEM;61 62	ops->enable_slot = pciehp_sysfs_enable_slot;63	ops->disable_slot = pciehp_sysfs_disable_slot;64	ops->get_power_status = get_power_status;65	ops->get_adapter_status = get_adapter_status;66	ops->reset_slot = pciehp_reset_slot;67	if (MRL_SENS(ctrl))68		ops->get_latch_status = get_latch_status;69	if (ATTN_LED(ctrl)) {70		ops->get_attention_status = pciehp_get_attention_status;71		ops->set_attention_status = set_attention_status;72	} else if (ctrl->pcie->port->hotplug_user_indicators) {73		ops->get_attention_status = pciehp_get_raw_indicator_status;74		ops->set_attention_status = pciehp_set_raw_indicator_status;75	}76 77	/* register this slot with the hotplug pci core */78	ctrl->hotplug_slot.ops = ops;79	snprintf(name, SLOT_NAME_SIZE, "%u", PSN(ctrl));80 81	retval = pci_hp_initialize(&ctrl->hotplug_slot,82				   ctrl->pcie->port->subordinate, 0, name);83	if (retval) {84		ctrl_err(ctrl, "pci_hp_initialize failed: error %d\n", retval);85		kfree(ops);86	}87	return retval;88}89 90static void cleanup_slot(struct controller *ctrl)91{92	struct hotplug_slot *hotplug_slot = &ctrl->hotplug_slot;93 94	pci_hp_destroy(hotplug_slot);95	kfree(hotplug_slot->ops);96}97 98/*99 * set_attention_status - Turns the Attention Indicator on, off or blinking100 */101static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)102{103	struct controller *ctrl = to_ctrl(hotplug_slot);104	struct pci_dev *pdev = ctrl->pcie->port;105 106	if (status)107		status = FIELD_PREP(PCI_EXP_SLTCTL_AIC, status);108	else109		status = PCI_EXP_SLTCTL_ATTN_IND_OFF;110 111	pci_config_pm_runtime_get(pdev);112	pciehp_set_indicators(ctrl, INDICATOR_NOOP, status);113	pci_config_pm_runtime_put(pdev);114	return 0;115}116 117static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)118{119	struct controller *ctrl = to_ctrl(hotplug_slot);120	struct pci_dev *pdev = ctrl->pcie->port;121 122	pci_config_pm_runtime_get(pdev);123	pciehp_get_power_status(ctrl, value);124	pci_config_pm_runtime_put(pdev);125	return 0;126}127 128static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)129{130	struct controller *ctrl = to_ctrl(hotplug_slot);131	struct pci_dev *pdev = ctrl->pcie->port;132 133	pci_config_pm_runtime_get(pdev);134	pciehp_get_latch_status(ctrl, value);135	pci_config_pm_runtime_put(pdev);136	return 0;137}138 139static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)140{141	struct controller *ctrl = to_ctrl(hotplug_slot);142	struct pci_dev *pdev = ctrl->pcie->port;143	int ret;144 145	pci_config_pm_runtime_get(pdev);146	ret = pciehp_card_present_or_link_active(ctrl);147	pci_config_pm_runtime_put(pdev);148	if (ret < 0)149		return ret;150 151	*value = ret;152	return 0;153}154 155/**156 * pciehp_check_presence() - synthesize event if presence has changed157 * @ctrl: controller to check158 *159 * On probe and resume, an explicit presence check is necessary to bring up an160 * occupied slot or bring down an unoccupied slot.  This can't be triggered by161 * events in the Slot Status register, they may be stale and are therefore162 * cleared.  Secondly, sending an interrupt for "events that occur while163 * interrupt generation is disabled [when] interrupt generation is subsequently164 * enabled" is optional per PCIe r4.0, sec 6.7.3.4.165 */166static void pciehp_check_presence(struct controller *ctrl)167{168	int occupied;169 170	down_read_nested(&ctrl->reset_lock, ctrl->depth);171	mutex_lock(&ctrl->state_lock);172 173	occupied = pciehp_card_present_or_link_active(ctrl);174	if ((occupied > 0 && (ctrl->state == OFF_STATE ||175			  ctrl->state == BLINKINGON_STATE)) ||176	    (!occupied && (ctrl->state == ON_STATE ||177			   ctrl->state == BLINKINGOFF_STATE)))178		pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC);179 180	mutex_unlock(&ctrl->state_lock);181	up_read(&ctrl->reset_lock);182}183 184static int pciehp_probe(struct pcie_device *dev)185{186	int rc;187	struct controller *ctrl;188 189	/* If this is not a "hotplug" service, we have no business here. */190	if (dev->service != PCIE_PORT_SERVICE_HP)191		return -ENODEV;192 193	if (!dev->port->subordinate) {194		/* Can happen if we run out of bus numbers during probe */195		pci_err(dev->port,196			"Hotplug bridge without secondary bus, ignoring\n");197		return -ENODEV;198	}199 200	ctrl = pcie_init(dev);201	if (!ctrl) {202		pci_err(dev->port, "Controller initialization failed\n");203		return -ENODEV;204	}205	set_service_data(dev, ctrl);206 207	/* Setup the slot information structures */208	rc = init_slot(ctrl);209	if (rc) {210		if (rc == -EBUSY)211			ctrl_warn(ctrl, "Slot already registered by another hotplug driver\n");212		else213			ctrl_err(ctrl, "Slot initialization failed (%d)\n", rc);214		goto err_out_release_ctlr;215	}216 217	/* Enable events after we have setup the data structures */218	rc = pcie_init_notification(ctrl);219	if (rc) {220		ctrl_err(ctrl, "Notification initialization failed (%d)\n", rc);221		goto err_out_free_ctrl_slot;222	}223 224	/* Publish to user space */225	rc = pci_hp_add(&ctrl->hotplug_slot);226	if (rc) {227		ctrl_err(ctrl, "Publication to user space failed (%d)\n", rc);228		goto err_out_shutdown_notification;229	}230 231	pciehp_check_presence(ctrl);232 233	return 0;234 235err_out_shutdown_notification:236	pcie_shutdown_notification(ctrl);237err_out_free_ctrl_slot:238	cleanup_slot(ctrl);239err_out_release_ctlr:240	pciehp_release_ctrl(ctrl);241	return -ENODEV;242}243 244static void pciehp_remove(struct pcie_device *dev)245{246	struct controller *ctrl = get_service_data(dev);247 248	pci_hp_del(&ctrl->hotplug_slot);249	pcie_shutdown_notification(ctrl);250	cleanup_slot(ctrl);251	pciehp_release_ctrl(ctrl);252}253 254#ifdef CONFIG_PM255static bool pme_is_native(struct pcie_device *dev)256{257	const struct pci_host_bridge *host;258 259	host = pci_find_host_bridge(dev->port->bus);260	return pcie_ports_native || host->native_pme;261}262 263static void pciehp_disable_interrupt(struct pcie_device *dev)264{265	/*266	 * Disable hotplug interrupt so that it does not trigger267	 * immediately when the downstream link goes down.268	 */269	if (pme_is_native(dev))270		pcie_disable_interrupt(get_service_data(dev));271}272 273#ifdef CONFIG_PM_SLEEP274static int pciehp_suspend(struct pcie_device *dev)275{276	/*277	 * If the port is already runtime suspended we can keep it that278	 * way.279	 */280	if (dev_pm_skip_suspend(&dev->port->dev))281		return 0;282 283	pciehp_disable_interrupt(dev);284	return 0;285}286 287static bool pciehp_device_replaced(struct controller *ctrl)288{289	struct pci_dev *pdev __free(pci_dev_put);290	u32 reg;291 292	pdev = pci_get_slot(ctrl->pcie->port->subordinate, PCI_DEVFN(0, 0));293	if (!pdev)294		return true;295 296	if (pci_read_config_dword(pdev, PCI_VENDOR_ID, &reg) ||297	    reg != (pdev->vendor | (pdev->device << 16)) ||298	    pci_read_config_dword(pdev, PCI_CLASS_REVISION, &reg) ||299	    reg != (pdev->revision | (pdev->class << 8)))300		return true;301 302	if (pdev->hdr_type == PCI_HEADER_TYPE_NORMAL &&303	    (pci_read_config_dword(pdev, PCI_SUBSYSTEM_VENDOR_ID, &reg) ||304	     reg != (pdev->subsystem_vendor | (pdev->subsystem_device << 16))))305		return true;306 307	if (pci_get_dsn(pdev) != ctrl->dsn)308		return true;309 310	return false;311}312 313static int pciehp_resume_noirq(struct pcie_device *dev)314{315	struct controller *ctrl = get_service_data(dev);316 317	/* pci_restore_state() just wrote to the Slot Control register */318	ctrl->cmd_started = jiffies;319	ctrl->cmd_busy = true;320 321	/* clear spurious events from rediscovery of inserted card */322	if (ctrl->state == ON_STATE || ctrl->state == BLINKINGOFF_STATE) {323		pcie_clear_hotplug_events(ctrl);324 325		/*326		 * If hotplugged device was replaced with a different one327		 * during system sleep, mark the old device disconnected328		 * (to prevent its driver from accessing the new device)329		 * and synthesize a Presence Detect Changed event.330		 */331		if (pciehp_device_replaced(ctrl)) {332			ctrl_dbg(ctrl, "device replaced during system sleep\n");333			pci_walk_bus(ctrl->pcie->port->subordinate,334				     pci_dev_set_disconnected, NULL);335			pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC);336		}337	}338 339	return 0;340}341#endif342 343static int pciehp_resume(struct pcie_device *dev)344{345	struct controller *ctrl = get_service_data(dev);346 347	if (pme_is_native(dev))348		pcie_enable_interrupt(ctrl);349 350	pciehp_check_presence(ctrl);351 352	return 0;353}354 355static int pciehp_runtime_suspend(struct pcie_device *dev)356{357	pciehp_disable_interrupt(dev);358	return 0;359}360 361static int pciehp_runtime_resume(struct pcie_device *dev)362{363	struct controller *ctrl = get_service_data(dev);364 365	/* pci_restore_state() just wrote to the Slot Control register */366	ctrl->cmd_started = jiffies;367	ctrl->cmd_busy = true;368 369	/* clear spurious events from rediscovery of inserted card */370	if ((ctrl->state == ON_STATE || ctrl->state == BLINKINGOFF_STATE) &&371	     pme_is_native(dev))372		pcie_clear_hotplug_events(ctrl);373 374	return pciehp_resume(dev);375}376#endif /* PM */377 378static struct pcie_port_service_driver hpdriver_portdrv = {379	.name		= "pciehp",380	.port_type	= PCIE_ANY_PORT,381	.service	= PCIE_PORT_SERVICE_HP,382 383	.probe		= pciehp_probe,384	.remove		= pciehp_remove,385 386#ifdef	CONFIG_PM387#ifdef	CONFIG_PM_SLEEP388	.suspend	= pciehp_suspend,389	.resume_noirq	= pciehp_resume_noirq,390	.resume		= pciehp_resume,391#endif392	.runtime_suspend = pciehp_runtime_suspend,393	.runtime_resume	= pciehp_runtime_resume,394#endif	/* PM */395 396	.slot_reset	= pciehp_slot_reset,397};398 399int __init pcie_hp_init(void)400{401	int retval = 0;402 403	retval = pcie_port_service_register(&hpdriver_portdrv);404	pr_debug("pcie_port_service_register = %d\n", retval);405	if (retval)406		pr_debug("Failure to register service\n");407 408	return retval;409}410