File Index
Files and directories
Sign in
FILE

shpchp_core.c

File preview with syntax highlighting.

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