Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.15-rc4 454 lines 12 kB view raw
1/* 2 * ACPI PCI Hot Plug Controller Driver 3 * 4 * Copyright (C) 1995,2001 Compaq Computer Corporation 5 * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) 6 * Copyright (C) 2001 IBM Corp. 7 * Copyright (C) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com) 8 * Copyright (C) 2002,2003 Takayoshi Kochi (t-kochi@bq.jp.nec.com) 9 * Copyright (C) 2002,2003 NEC Corporation 10 * Copyright (C) 2003-2005 Matthew Wilcox (matthew.wilcox@hp.com) 11 * Copyright (C) 2003-2005 Hewlett Packard 12 * 13 * All rights reserved. 14 * 15 * This program is free software; you can redistribute it and/or modify 16 * it under the terms of the GNU General Public License as published by 17 * the Free Software Foundation; either version 2 of the License, or (at 18 * your option) any later version. 19 * 20 * This program is distributed in the hope that it will be useful, but 21 * WITHOUT ANY WARRANTY; without even the implied warranty of 22 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or 23 * NON INFRINGEMENT. See the GNU General Public License for more 24 * details. 25 * 26 * You should have received a copy of the GNU General Public License 27 * along with this program; if not, write to the Free Software 28 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 29 * 30 * Send feedback to <gregkh@us.ibm.com>, 31 * <t-kochi@bq.jp.nec.com> 32 * 33 */ 34 35#include <linux/init.h> 36#include <linux/module.h> 37#include <linux/moduleparam.h> 38 39#include <linux/kernel.h> 40#include <linux/pci.h> 41#include <linux/slab.h> 42#include <linux/smp.h> 43#include <linux/smp_lock.h> 44#include "pci_hotplug.h" 45#include "acpiphp.h" 46 47static LIST_HEAD(slot_list); 48 49#define MY_NAME "acpiphp" 50 51static int debug; 52int acpiphp_debug; 53 54/* local variables */ 55static int num_slots; 56static struct acpiphp_attention_info *attention_info; 57 58#define DRIVER_VERSION "0.5" 59#define DRIVER_AUTHOR "Greg Kroah-Hartman <gregkh@us.ibm.com>, Takayoshi Kochi <t-kochi@bq.jp.nec.com>, Matthew Wilcox <willy@hp.com>" 60#define DRIVER_DESC "ACPI Hot Plug PCI Controller Driver" 61 62MODULE_AUTHOR(DRIVER_AUTHOR); 63MODULE_DESCRIPTION(DRIVER_DESC); 64MODULE_LICENSE("GPL"); 65MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); 66module_param(debug, bool, 0644); 67 68/* export the attention callback registration methods */ 69EXPORT_SYMBOL_GPL(acpiphp_register_attention); 70EXPORT_SYMBOL_GPL(acpiphp_unregister_attention); 71 72static int enable_slot (struct hotplug_slot *slot); 73static int disable_slot (struct hotplug_slot *slot); 74static int set_attention_status (struct hotplug_slot *slot, u8 value); 75static int get_power_status (struct hotplug_slot *slot, u8 *value); 76static int get_attention_status (struct hotplug_slot *slot, u8 *value); 77static int get_address (struct hotplug_slot *slot, u32 *value); 78static int get_latch_status (struct hotplug_slot *slot, u8 *value); 79static int get_adapter_status (struct hotplug_slot *slot, u8 *value); 80 81static struct hotplug_slot_ops acpi_hotplug_slot_ops = { 82 .owner = THIS_MODULE, 83 .enable_slot = enable_slot, 84 .disable_slot = disable_slot, 85 .set_attention_status = set_attention_status, 86 .get_power_status = get_power_status, 87 .get_attention_status = get_attention_status, 88 .get_latch_status = get_latch_status, 89 .get_adapter_status = get_adapter_status, 90 .get_address = get_address, 91}; 92 93 94/** 95 * acpiphp_register_attention - set attention LED callback 96 * @info: must be completely filled with LED callbacks 97 * 98 * Description: this is used to register a hardware specific ACPI 99 * driver that manipulates the attention LED. All the fields in 100 * info must be set. 101 **/ 102int acpiphp_register_attention(struct acpiphp_attention_info *info) 103{ 104 int retval = -EINVAL; 105 106 if (info && info->owner && info->set_attn && 107 info->get_attn && !attention_info) { 108 retval = 0; 109 attention_info = info; 110 } 111 return retval; 112} 113 114 115/** 116 * acpiphp_unregister_attention - unset attention LED callback 117 * @info: must match the pointer used to register 118 * 119 * Description: this is used to un-register a hardware specific acpi 120 * driver that manipulates the attention LED. The pointer to the 121 * info struct must be the same as the one used to set it. 122 **/ 123int acpiphp_unregister_attention(struct acpiphp_attention_info *info) 124{ 125 int retval = -EINVAL; 126 127 if (info && attention_info == info) { 128 attention_info = NULL; 129 retval = 0; 130 } 131 return retval; 132} 133 134 135/** 136 * enable_slot - power on and enable a slot 137 * @hotplug_slot: slot to enable 138 * 139 * Actual tasks are done in acpiphp_enable_slot() 140 * 141 */ 142static int enable_slot(struct hotplug_slot *hotplug_slot) 143{ 144 struct slot *slot = hotplug_slot->private; 145 146 dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); 147 148 /* enable the specified slot */ 149 return acpiphp_enable_slot(slot->acpi_slot); 150} 151 152 153/** 154 * disable_slot - disable and power off a slot 155 * @hotplug_slot: slot to disable 156 * 157 * Actual tasks are done in acpiphp_disable_slot() 158 * 159 */ 160static int disable_slot(struct hotplug_slot *hotplug_slot) 161{ 162 struct slot *slot = hotplug_slot->private; 163 164 dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); 165 166 /* disable the specified slot */ 167 return acpiphp_disable_slot(slot->acpi_slot); 168} 169 170 171 /** 172 * set_attention_status - set attention LED 173 * @hotplug_slot: slot to set attention LED on 174 * @status: value to set attention LED to (0 or 1) 175 * 176 * attention status LED, so we use a callback that 177 * was registered with us. This allows hardware specific 178 * ACPI implementations to blink the light for us. 179 **/ 180 static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status) 181 { 182 int retval = -ENODEV; 183 184 dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); 185 186 if (attention_info && try_module_get(attention_info->owner)) { 187 retval = attention_info->set_attn(hotplug_slot, status); 188 module_put(attention_info->owner); 189 } else 190 attention_info = NULL; 191 return retval; 192 } 193 194 195/** 196 * get_power_status - get power status of a slot 197 * @hotplug_slot: slot to get status 198 * @value: pointer to store status 199 * 200 * Some platforms may not implement _STA method properly. 201 * In that case, the value returned may not be reliable. 202 * 203 */ 204static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) 205{ 206 struct slot *slot = hotplug_slot->private; 207 208 dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); 209 210 *value = acpiphp_get_power_status(slot->acpi_slot); 211 212 return 0; 213} 214 215 216 /** 217 * get_attention_status - get attention LED status 218 * @hotplug_slot: slot to get status from 219 * @value: returns with value of attention LED 220 * 221 * ACPI doesn't have known method to determine the state 222 * of the attention status LED, so we use a callback that 223 * was registered with us. This allows hardware specific 224 * ACPI implementations to determine its state 225 **/ 226static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value) 227{ 228 int retval = -EINVAL; 229 230 dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); 231 232 if (attention_info && try_module_get(attention_info->owner)) { 233 retval = attention_info->get_attn(hotplug_slot, value); 234 module_put(attention_info->owner); 235 } else 236 attention_info = NULL; 237 return retval; 238} 239 240 241/** 242 * get_latch_status - get latch status of a slot 243 * @hotplug_slot: slot to get status 244 * @value: pointer to store status 245 * 246 * ACPI doesn't provide any formal means to access latch status. 247 * Instead, we fake latch status from _STA 248 * 249 */ 250static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) 251{ 252 struct slot *slot = hotplug_slot->private; 253 254 dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); 255 256 *value = acpiphp_get_latch_status(slot->acpi_slot); 257 258 return 0; 259} 260 261 262/** 263 * get_adapter_status - get adapter status of a slot 264 * @hotplug_slot: slot to get status 265 * @value: pointer to store status 266 * 267 * ACPI doesn't provide any formal means to access adapter status. 268 * Instead, we fake adapter status from _STA 269 * 270 */ 271static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) 272{ 273 struct slot *slot = hotplug_slot->private; 274 275 dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); 276 277 *value = acpiphp_get_adapter_status(slot->acpi_slot); 278 279 return 0; 280} 281 282 283/** 284 * get_address - get pci address of a slot 285 * @hotplug_slot: slot to get status 286 * @value: pointer to struct pci_busdev (seg, bus, dev) 287 */ 288static int get_address(struct hotplug_slot *hotplug_slot, u32 *value) 289{ 290 struct slot *slot = hotplug_slot->private; 291 292 dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); 293 294 *value = acpiphp_get_address(slot->acpi_slot); 295 296 return 0; 297} 298 299static int __init init_acpi(void) 300{ 301 int retval; 302 303 /* initialize internal data structure etc. */ 304 retval = acpiphp_glue_init(); 305 306 /* read initial number of slots */ 307 if (!retval) { 308 num_slots = acpiphp_get_num_slots(); 309 if (num_slots == 0) 310 retval = -ENODEV; 311 } 312 313 return retval; 314} 315 316 317/** 318 * make_slot_name - make a slot name that appears in pcihpfs 319 * @slot: slot to name 320 * 321 */ 322static void make_slot_name(struct slot *slot) 323{ 324 snprintf(slot->hotplug_slot->name, SLOT_NAME_SIZE, "%u", 325 slot->acpi_slot->sun); 326} 327 328/** 329 * release_slot - free up the memory used by a slot 330 * @hotplug_slot: slot to free 331 */ 332static void release_slot(struct hotplug_slot *hotplug_slot) 333{ 334 struct slot *slot = hotplug_slot->private; 335 336 dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); 337 338 kfree(slot->hotplug_slot->info); 339 kfree(slot->hotplug_slot->name); 340 kfree(slot->hotplug_slot); 341 kfree(slot); 342} 343 344/** 345 * init_slots - initialize 'struct slot' structures for each slot 346 * 347 */ 348static int __init init_slots(void) 349{ 350 struct slot *slot; 351 int retval = -ENOMEM; 352 int i; 353 354 for (i = 0; i < num_slots; ++i) { 355 slot = kmalloc(sizeof(struct slot), GFP_KERNEL); 356 if (!slot) 357 goto error; 358 memset(slot, 0, sizeof(struct slot)); 359 360 slot->hotplug_slot = kmalloc(sizeof(struct hotplug_slot), GFP_KERNEL); 361 if (!slot->hotplug_slot) 362 goto error_slot; 363 memset(slot->hotplug_slot, 0, sizeof(struct hotplug_slot)); 364 365 slot->hotplug_slot->info = kmalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL); 366 if (!slot->hotplug_slot->info) 367 goto error_hpslot; 368 memset(slot->hotplug_slot->info, 0, sizeof(struct hotplug_slot_info)); 369 370 slot->hotplug_slot->name = kmalloc(SLOT_NAME_SIZE, GFP_KERNEL); 371 if (!slot->hotplug_slot->name) 372 goto error_info; 373 374 slot->number = i; 375 376 slot->hotplug_slot->private = slot; 377 slot->hotplug_slot->release = &release_slot; 378 slot->hotplug_slot->ops = &acpi_hotplug_slot_ops; 379 380 slot->acpi_slot = get_slot_from_id(i); 381 slot->hotplug_slot->info->power_status = acpiphp_get_power_status(slot->acpi_slot); 382 slot->hotplug_slot->info->attention_status = 0; 383 slot->hotplug_slot->info->latch_status = acpiphp_get_latch_status(slot->acpi_slot); 384 slot->hotplug_slot->info->adapter_status = acpiphp_get_adapter_status(slot->acpi_slot); 385 slot->hotplug_slot->info->max_bus_speed = PCI_SPEED_UNKNOWN; 386 slot->hotplug_slot->info->cur_bus_speed = PCI_SPEED_UNKNOWN; 387 388 make_slot_name(slot); 389 390 retval = pci_hp_register(slot->hotplug_slot); 391 if (retval) { 392 err("pci_hp_register failed with error %d\n", retval); 393 goto error_name; 394 } 395 396 /* add slot to our internal list */ 397 list_add(&slot->slot_list, &slot_list); 398 info("Slot [%s] registered\n", slot->hotplug_slot->name); 399 } 400 401 return 0; 402error_name: 403 kfree(slot->hotplug_slot->name); 404error_info: 405 kfree(slot->hotplug_slot->info); 406error_hpslot: 407 kfree(slot->hotplug_slot); 408error_slot: 409 kfree(slot); 410error: 411 return retval; 412} 413 414 415static void __exit cleanup_slots (void) 416{ 417 struct list_head *tmp, *n; 418 struct slot *slot; 419 420 list_for_each_safe (tmp, n, &slot_list) { 421 /* memory will be freed in release_slot callback */ 422 slot = list_entry(tmp, struct slot, slot_list); 423 list_del(&slot->slot_list); 424 pci_hp_deregister(slot->hotplug_slot); 425 } 426} 427 428 429static int __init acpiphp_init(void) 430{ 431 int retval; 432 433 info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); 434 435 acpiphp_debug = debug; 436 437 /* read all the ACPI info from the system */ 438 retval = init_acpi(); 439 if (retval) 440 return retval; 441 442 return init_slots(); 443} 444 445 446static void __exit acpiphp_exit(void) 447{ 448 cleanup_slots(); 449 /* deallocate internal data structures etc. */ 450 acpiphp_glue_exit(); 451} 452 453module_init(acpiphp_init); 454module_exit(acpiphp_exit);