brintos

brintos / linux-shallow public Read only

0
0
Text · 8.4 KiB · a92e28b Raw
345 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Standard 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 */15 16#include <linux/module.h>17#include <linux/moduleparam.h>18#include <linux/kernel.h>19#include <linux/types.h>20#include <linux/slab.h>21#include <linux/pci.h>22#include "shpchp.h"23 24/* Global variables */25bool shpchp_debug;26bool shpchp_poll_mode;27int shpchp_poll_time;28 29#define DRIVER_VERSION	"0.4"30#define DRIVER_AUTHOR	"Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>"31#define DRIVER_DESC	"Standard Hot Plug PCI Controller Driver"32 33MODULE_AUTHOR(DRIVER_AUTHOR);34MODULE_DESCRIPTION(DRIVER_DESC);35 36module_param(shpchp_debug, bool, 0644);37module_param(shpchp_poll_mode, bool, 0644);38module_param(shpchp_poll_time, int, 0644);39MODULE_PARM_DESC(shpchp_debug, "Debugging mode enabled or not");40MODULE_PARM_DESC(shpchp_poll_mode, "Using polling mechanism for hot-plug events or not");41MODULE_PARM_DESC(shpchp_poll_time, "Polling mechanism frequency, in seconds");42 43#define SHPC_MODULE_NAME "shpchp"44 45static int set_attention_status(struct hotplug_slot *slot, u8 value);46static int enable_slot(struct hotplug_slot *slot);47static int disable_slot(struct hotplug_slot *slot);48static int get_power_status(struct hotplug_slot *slot, u8 *value);49static int get_attention_status(struct hotplug_slot *slot, u8 *value);50static int get_latch_status(struct hotplug_slot *slot, u8 *value);51static int get_adapter_status(struct hotplug_slot *slot, u8 *value);52 53static const struct hotplug_slot_ops shpchp_hotplug_slot_ops = {54	.set_attention_status =	set_attention_status,55	.enable_slot =		enable_slot,56	.disable_slot =		disable_slot,57	.get_power_status =	get_power_status,58	.get_attention_status =	get_attention_status,59	.get_latch_status =	get_latch_status,60	.get_adapter_status =	get_adapter_status,61};62 63static int init_slots(struct controller *ctrl)64{65	struct slot *slot;66	struct hotplug_slot *hotplug_slot;67	char name[SLOT_NAME_SIZE];68	int retval;69	int i;70 71	for (i = 0; i < ctrl->num_slots; i++) {72		slot = kzalloc(sizeof(*slot), GFP_KERNEL);73		if (!slot) {74			retval = -ENOMEM;75			goto error;76		}77 78		hotplug_slot = &slot->hotplug_slot;79 80		slot->hp_slot = i;81		slot->ctrl = ctrl;82		slot->bus = ctrl->pci_dev->subordinate->number;83		slot->device = ctrl->slot_device_offset + i;84		slot->number = ctrl->first_slot + (ctrl->slot_num_inc * i);85 86		slot->wq = alloc_workqueue("shpchp-%d", 0, 0, slot->number);87		if (!slot->wq) {88			retval = -ENOMEM;89			goto error_slot;90		}91 92		mutex_init(&slot->lock);93		INIT_DELAYED_WORK(&slot->work, shpchp_queue_pushbutton_work);94 95		/* register this slot with the hotplug pci core */96		snprintf(name, SLOT_NAME_SIZE, "%d", slot->number);97		hotplug_slot->ops = &shpchp_hotplug_slot_ops;98 99		ctrl_dbg(ctrl, "Registering domain:bus:dev=%04x:%02x:%02x hp_slot=%x sun=%x slot_device_offset=%x\n",100			 pci_domain_nr(ctrl->pci_dev->subordinate),101			 slot->bus, slot->device, slot->hp_slot, slot->number,102			 ctrl->slot_device_offset);103		retval = pci_hp_register(hotplug_slot,104				ctrl->pci_dev->subordinate, slot->device, name);105		if (retval) {106			ctrl_err(ctrl, "pci_hp_register failed with error %d\n",107				 retval);108			goto error_slotwq;109		}110 111		get_power_status(hotplug_slot, &slot->pwr_save);112		get_attention_status(hotplug_slot, &slot->attention_save);113		get_latch_status(hotplug_slot, &slot->latch_save);114		get_adapter_status(hotplug_slot, &slot->presence_save);115 116		list_add(&slot->slot_list, &ctrl->slot_list);117	}118 119	return 0;120error_slotwq:121	destroy_workqueue(slot->wq);122error_slot:123	kfree(slot);124error:125	return retval;126}127 128void cleanup_slots(struct controller *ctrl)129{130	struct slot *slot, *next;131 132	list_for_each_entry_safe(slot, next, &ctrl->slot_list, slot_list) {133		list_del(&slot->slot_list);134		cancel_delayed_work(&slot->work);135		destroy_workqueue(slot->wq);136		pci_hp_deregister(&slot->hotplug_slot);137		kfree(slot);138	}139}140 141/*142 * set_attention_status - Turns the Amber LED for a slot on, off or blink143 */144static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)145{146	struct slot *slot = get_slot(hotplug_slot);147 148	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",149		 __func__, slot_name(slot));150 151	slot->attention_save = status;152	shpchp_set_attention_status(slot, status);153 154	return 0;155}156 157static int enable_slot(struct hotplug_slot *hotplug_slot)158{159	struct slot *slot = get_slot(hotplug_slot);160 161	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",162		 __func__, slot_name(slot));163 164	return shpchp_sysfs_enable_slot(slot);165}166 167static int disable_slot(struct hotplug_slot *hotplug_slot)168{169	struct slot *slot = get_slot(hotplug_slot);170 171	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",172		 __func__, slot_name(slot));173 174	return shpchp_sysfs_disable_slot(slot);175}176 177static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)178{179	struct slot *slot = get_slot(hotplug_slot);180	int retval;181 182	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",183		 __func__, slot_name(slot));184 185	retval = shpchp_get_power_status(slot, value);186	if (retval < 0)187		*value = slot->pwr_save;188 189	return 0;190}191 192static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)193{194	struct slot *slot = get_slot(hotplug_slot);195	int retval;196 197	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",198		 __func__, slot_name(slot));199 200	retval = shpchp_get_attention_status(slot, value);201	if (retval < 0)202		*value = slot->attention_save;203 204	return 0;205}206 207static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)208{209	struct slot *slot = get_slot(hotplug_slot);210	int retval;211 212	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",213		 __func__, slot_name(slot));214 215	retval = shpchp_get_latch_status(slot, value);216	if (retval < 0)217		*value = slot->latch_save;218 219	return 0;220}221 222static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)223{224	struct slot *slot = get_slot(hotplug_slot);225	int retval;226 227	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",228		 __func__, slot_name(slot));229 230	retval = shpchp_get_adapter_status(slot, value);231	if (retval < 0)232		*value = slot->presence_save;233 234	return 0;235}236 237static bool shpc_capable(struct pci_dev *bridge)238{239	/*240	 * It is assumed that AMD GOLAM chips support SHPC but they do not241	 * have SHPC capability.242	 */243	if (bridge->vendor == PCI_VENDOR_ID_AMD &&244	    bridge->device == PCI_DEVICE_ID_AMD_GOLAM_7450)245		return true;246 247	if (pci_find_capability(bridge, PCI_CAP_ID_SHPC))248		return true;249 250	return false;251}252 253static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)254{255	int rc;256	struct controller *ctrl;257 258	if (!shpc_capable(pdev))259		return -ENODEV;260 261	if (acpi_get_hp_hw_control_from_firmware(pdev))262		return -ENODEV;263 264	ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);265	if (!ctrl)266		goto err_out_none;267 268	INIT_LIST_HEAD(&ctrl->slot_list);269 270	rc = shpc_init(ctrl, pdev);271	if (rc) {272		ctrl_dbg(ctrl, "Controller initialization failed\n");273		goto err_out_free_ctrl;274	}275 276	pci_set_drvdata(pdev, ctrl);277 278	/* Setup the slot information structures */279	rc = init_slots(ctrl);280	if (rc) {281		ctrl_err(ctrl, "Slot initialization failed\n");282		goto err_out_release_ctlr;283	}284 285	rc = shpchp_create_ctrl_files(ctrl);286	if (rc)287		goto err_cleanup_slots;288 289	pdev->shpc_managed = 1;290	return 0;291 292err_cleanup_slots:293	cleanup_slots(ctrl);294err_out_release_ctlr:295	shpchp_release_ctlr(ctrl);296err_out_free_ctrl:297	kfree(ctrl);298err_out_none:299	return -ENODEV;300}301 302static void shpc_remove(struct pci_dev *dev)303{304	struct controller *ctrl = pci_get_drvdata(dev);305 306	dev->shpc_managed = 0;307	shpchp_remove_ctrl_files(ctrl);308	shpchp_release_ctlr(ctrl);309	kfree(ctrl);310}311 312static const struct pci_device_id shpcd_pci_tbl[] = {313	{PCI_DEVICE_CLASS(PCI_CLASS_BRIDGE_PCI_NORMAL, ~0)},314	{ /* end: all zeroes */ }315};316MODULE_DEVICE_TABLE(pci, shpcd_pci_tbl);317 318static struct pci_driver shpc_driver = {319	.name =		SHPC_MODULE_NAME,320	.id_table =	shpcd_pci_tbl,321	.probe =	shpc_probe,322	.remove =	shpc_remove,323};324 325static int __init shpcd_init(void)326{327	int retval;328 329	retval = pci_register_driver(&shpc_driver);330	dbg("%s: pci_register_driver = %d\n", __func__, retval);331	info(DRIVER_DESC " version: " DRIVER_VERSION "\n");332 333	return retval;334}335 336static void __exit shpcd_cleanup(void)337{338	dbg("unload_shpchpd()\n");339	pci_unregister_driver(&shpc_driver);340	info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n");341}342 343module_init(shpcd_init);344module_exit(shpcd_cleanup);345