Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
shpchp_core.c
Go to the documentation of this file.
1 /*
2  * Standard Hot Plug Controller Driver
3  *
4  * Copyright (C) 1995,2001 Compaq Computer Corporation
5  * Copyright (C) 2001 Greg Kroah-Hartman ([email protected])
6  * Copyright (C) 2001 IBM Corp.
7  * Copyright (C) 2003-2004 Intel Corporation
8  *
9  * All rights reserved.
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or (at
14  * your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful, but
17  * WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
19  * NON INFRINGEMENT. See the GNU General Public License for more
20  * details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25  *
26  * Send feedback to <[email protected]>, <[email protected]>
27  *
28  */
29 
30 #include <linux/module.h>
31 #include <linux/moduleparam.h>
32 #include <linux/kernel.h>
33 #include <linux/types.h>
34 #include <linux/slab.h>
35 #include <linux/pci.h>
36 #include "shpchp.h"
37 
38 /* Global variables */
44 
45 #define DRIVER_VERSION "0.4"
46 #define DRIVER_AUTHOR "Dan Zink <[email protected]>, Greg Kroah-Hartman <[email protected]>, Dely Sy <[email protected]>"
47 #define DRIVER_DESC "Standard Hot Plug PCI Controller Driver"
48 
51 MODULE_LICENSE("GPL");
52 
53 module_param(shpchp_debug, bool, 0644);
54 module_param(shpchp_poll_mode, bool, 0644);
55 module_param(shpchp_poll_time, int, 0644);
56 MODULE_PARM_DESC(shpchp_debug, "Debugging mode enabled or not");
57 MODULE_PARM_DESC(shpchp_poll_mode, "Using polling mechanism for hot-plug events or not");
58 MODULE_PARM_DESC(shpchp_poll_time, "Polling mechanism frequency, in seconds");
59 
60 #define SHPC_MODULE_NAME "shpchp"
61 
62 static int set_attention_status (struct hotplug_slot *slot, u8 value);
63 static int enable_slot (struct hotplug_slot *slot);
64 static int disable_slot (struct hotplug_slot *slot);
65 static int get_power_status (struct hotplug_slot *slot, u8 *value);
66 static int get_attention_status (struct hotplug_slot *slot, u8 *value);
67 static int get_latch_status (struct hotplug_slot *slot, u8 *value);
68 static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
69 
70 static struct hotplug_slot_ops shpchp_hotplug_slot_ops = {
71  .set_attention_status = set_attention_status,
72  .enable_slot = enable_slot,
73  .disable_slot = disable_slot,
74  .get_power_status = get_power_status,
75  .get_attention_status = get_attention_status,
76  .get_latch_status = get_latch_status,
77  .get_adapter_status = get_adapter_status,
78 };
79 
84 static void release_slot(struct hotplug_slot *hotplug_slot)
85 {
86  struct slot *slot = hotplug_slot->private;
87 
88  ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
89  __func__, slot_name(slot));
90 
91  kfree(slot->hotplug_slot->info);
92  kfree(slot->hotplug_slot);
93  kfree(slot);
94 }
95 
96 static int init_slots(struct controller *ctrl)
97 {
98  struct slot *slot;
99  struct hotplug_slot *hotplug_slot;
100  struct hotplug_slot_info *info;
101  char name[SLOT_NAME_SIZE];
102  int retval;
103  int i;
104 
105  for (i = 0; i < ctrl->num_slots; i++) {
106  slot = kzalloc(sizeof(*slot), GFP_KERNEL);
107  if (!slot) {
108  retval = -ENOMEM;
109  goto error;
110  }
111 
112  hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL);
113  if (!hotplug_slot) {
114  retval = -ENOMEM;
115  goto error_slot;
116  }
117  slot->hotplug_slot = hotplug_slot;
118 
119  info = kzalloc(sizeof(*info), GFP_KERNEL);
120  if (!info) {
121  retval = -ENOMEM;
122  goto error_hpslot;
123  }
124  hotplug_slot->info = info;
125 
126  slot->hp_slot = i;
127  slot->ctrl = ctrl;
128  slot->bus = ctrl->pci_dev->subordinate->number;
129  slot->device = ctrl->slot_device_offset + i;
130  slot->hpc_ops = ctrl->hpc_ops;
131  slot->number = ctrl->first_slot + (ctrl->slot_num_inc * i);
132  mutex_init(&slot->lock);
134 
135  /* register this slot with the hotplug pci core */
136  hotplug_slot->private = slot;
137  hotplug_slot->release = &release_slot;
138  snprintf(name, SLOT_NAME_SIZE, "%d", slot->number);
139  hotplug_slot->ops = &shpchp_hotplug_slot_ops;
140 
141  ctrl_dbg(ctrl, "Registering domain:bus:dev=%04x:%02x:%02x "
142  "hp_slot=%x sun=%x slot_device_offset=%x\n",
143  pci_domain_nr(ctrl->pci_dev->subordinate),
144  slot->bus, slot->device, slot->hp_slot, slot->number,
145  ctrl->slot_device_offset);
146  retval = pci_hp_register(slot->hotplug_slot,
147  ctrl->pci_dev->subordinate, slot->device, name);
148  if (retval) {
149  ctrl_err(ctrl, "pci_hp_register failed with error %d\n",
150  retval);
151  goto error_info;
152  }
153 
154  get_power_status(hotplug_slot, &info->power_status);
155  get_attention_status(hotplug_slot, &info->attention_status);
156  get_latch_status(hotplug_slot, &info->latch_status);
157  get_adapter_status(hotplug_slot, &info->adapter_status);
158 
159  list_add(&slot->slot_list, &ctrl->slot_list);
160  }
161 
162  return 0;
163 error_info:
164  kfree(info);
165 error_hpslot:
166  kfree(hotplug_slot);
167 error_slot:
168  kfree(slot);
169 error:
170  return retval;
171 }
172 
173 void cleanup_slots(struct controller *ctrl)
174 {
175  struct list_head *tmp;
176  struct list_head *next;
177  struct slot *slot;
178 
179  list_for_each_safe(tmp, next, &ctrl->slot_list) {
180  slot = list_entry(tmp, struct slot, slot_list);
181  list_del(&slot->slot_list);
182  cancel_delayed_work(&slot->work);
183  flush_workqueue(shpchp_wq);
184  flush_workqueue(shpchp_ordered_wq);
186  }
187 }
188 
189 /*
190  * set_attention_status - Turns the Amber LED for a slot on, off or blink
191  */
192 static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 status)
193 {
194  struct slot *slot = get_slot(hotplug_slot);
195 
196  ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
197  __func__, slot_name(slot));
198 
199  hotplug_slot->info->attention_status = status;
200  slot->hpc_ops->set_attention_status(slot, status);
201 
202  return 0;
203 }
204 
205 static int enable_slot (struct hotplug_slot *hotplug_slot)
206 {
207  struct slot *slot = get_slot(hotplug_slot);
208 
209  ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
210  __func__, slot_name(slot));
211 
212  return shpchp_sysfs_enable_slot(slot);
213 }
214 
215 static int disable_slot (struct hotplug_slot *hotplug_slot)
216 {
217  struct slot *slot = get_slot(hotplug_slot);
218 
219  ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
220  __func__, slot_name(slot));
221 
222  return shpchp_sysfs_disable_slot(slot);
223 }
224 
225 static int get_power_status (struct hotplug_slot *hotplug_slot, u8 *value)
226 {
227  struct slot *slot = get_slot(hotplug_slot);
228  int retval;
229 
230  ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
231  __func__, slot_name(slot));
232 
233  retval = slot->hpc_ops->get_power_status(slot, value);
234  if (retval < 0)
235  *value = hotplug_slot->info->power_status;
236 
237  return 0;
238 }
239 
240 static int get_attention_status (struct hotplug_slot *hotplug_slot, u8 *value)
241 {
242  struct slot *slot = get_slot(hotplug_slot);
243  int retval;
244 
245  ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
246  __func__, slot_name(slot));
247 
248  retval = slot->hpc_ops->get_attention_status(slot, value);
249  if (retval < 0)
250  *value = hotplug_slot->info->attention_status;
251 
252  return 0;
253 }
254 
255 static int get_latch_status (struct hotplug_slot *hotplug_slot, u8 *value)
256 {
257  struct slot *slot = get_slot(hotplug_slot);
258  int retval;
259 
260  ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
261  __func__, slot_name(slot));
262 
263  retval = slot->hpc_ops->get_latch_status(slot, value);
264  if (retval < 0)
265  *value = hotplug_slot->info->latch_status;
266 
267  return 0;
268 }
269 
270 static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value)
271 {
272  struct slot *slot = get_slot(hotplug_slot);
273  int retval;
274 
275  ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
276  __func__, slot_name(slot));
277 
278  retval = slot->hpc_ops->get_adapter_status(slot, value);
279  if (retval < 0)
280  *value = hotplug_slot->info->adapter_status;
281 
282  return 0;
283 }
284 
285 static int is_shpc_capable(struct pci_dev *dev)
286 {
287  if (dev->vendor == PCI_VENDOR_ID_AMD &&
289  return 1;
291  return 0;
293  return 0;
294  return 1;
295 }
296 
297 static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
298 {
299  int rc;
300  struct controller *ctrl;
301 
302  if (!is_shpc_capable(pdev))
303  return -ENODEV;
304 
305  ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
306  if (!ctrl) {
307  dev_err(&pdev->dev, "%s: Out of memory\n", __func__);
308  goto err_out_none;
309  }
310  INIT_LIST_HEAD(&ctrl->slot_list);
311 
312  rc = shpc_init(ctrl, pdev);
313  if (rc) {
314  ctrl_dbg(ctrl, "Controller initialization failed\n");
315  goto err_out_free_ctrl;
316  }
317 
318  pci_set_drvdata(pdev, ctrl);
319 
320  /* Setup the slot information structures */
321  rc = init_slots(ctrl);
322  if (rc) {
323  ctrl_err(ctrl, "Slot initialization failed\n");
324  goto err_out_release_ctlr;
325  }
326 
327  rc = shpchp_create_ctrl_files(ctrl);
328  if (rc)
329  goto err_cleanup_slots;
330 
331  return 0;
332 
333 err_cleanup_slots:
334  cleanup_slots(ctrl);
335 err_out_release_ctlr:
336  ctrl->hpc_ops->release_ctlr(ctrl);
337 err_out_free_ctrl:
338  kfree(ctrl);
339 err_out_none:
340  return -ENODEV;
341 }
342 
343 static void shpc_remove(struct pci_dev *dev)
344 {
345  struct controller *ctrl = pci_get_drvdata(dev);
346 
348  ctrl->hpc_ops->release_ctlr(ctrl);
349  kfree(ctrl);
350 }
351 
352 static struct pci_device_id shpcd_pci_tbl[] = {
353  {PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), ~0)},
354  { /* end: all zeroes */ }
355 };
356 MODULE_DEVICE_TABLE(pci, shpcd_pci_tbl);
357 
358 static struct pci_driver shpc_driver = {
359  .name = SHPC_MODULE_NAME,
360  .id_table = shpcd_pci_tbl,
361  .probe = shpc_probe,
362  .remove = shpc_remove,
363 };
364 
365 static int __init shpcd_init(void)
366 {
367  int retval = 0;
368 
369  shpchp_wq = alloc_ordered_workqueue("shpchp", 0);
370  if (!shpchp_wq)
371  return -ENOMEM;
372 
373  shpchp_ordered_wq = alloc_ordered_workqueue("shpchp_ordered", 0);
374  if (!shpchp_ordered_wq) {
375  destroy_workqueue(shpchp_wq);
376  return -ENOMEM;
377  }
378 
379  retval = pci_register_driver(&shpc_driver);
380  dbg("%s: pci_register_driver = %d\n", __func__, retval);
381  info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
382  if (retval) {
383  destroy_workqueue(shpchp_ordered_wq);
384  destroy_workqueue(shpchp_wq);
385  }
386  return retval;
387 }
388 
389 static void __exit shpcd_cleanup(void)
390 {
391  dbg("unload_shpchpd()\n");
392  pci_unregister_driver(&shpc_driver);
393  destroy_workqueue(shpchp_ordered_wq);
394  destroy_workqueue(shpchp_wq);
395  info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n");
396 }
397 
398 module_init(shpcd_init);
399 module_exit(shpcd_cleanup);