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