diff options
Diffstat (limited to 'drivers/cpuidle/sysfs.c')
-rw-r--r-- | drivers/cpuidle/sysfs.c | 201 |
1 files changed, 184 insertions, 17 deletions
diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c index 5f809e337b8..34094294610 100644 --- a/drivers/cpuidle/sysfs.c +++ b/drivers/cpuidle/sysfs.c | |||
@@ -12,6 +12,7 @@ | |||
12 | #include <linux/slab.h> | 12 | #include <linux/slab.h> |
13 | #include <linux/cpu.h> | 13 | #include <linux/cpu.h> |
14 | #include <linux/capability.h> | 14 | #include <linux/capability.h> |
15 | #include <linux/device.h> | ||
15 | 16 | ||
16 | #include "cpuidle.h" | 17 | #include "cpuidle.h" |
17 | 18 | ||
@@ -297,6 +298,13 @@ static struct attribute *cpuidle_state_default_attrs[] = { | |||
297 | NULL | 298 | NULL |
298 | }; | 299 | }; |
299 | 300 | ||
301 | struct cpuidle_state_kobj { | ||
302 | struct cpuidle_state *state; | ||
303 | struct cpuidle_state_usage *state_usage; | ||
304 | struct completion kobj_unregister; | ||
305 | struct kobject kobj; | ||
306 | }; | ||
307 | |||
300 | #define kobj_to_state_obj(k) container_of(k, struct cpuidle_state_kobj, kobj) | 308 | #define kobj_to_state_obj(k) container_of(k, struct cpuidle_state_kobj, kobj) |
301 | #define kobj_to_state(k) (kobj_to_state_obj(k)->state) | 309 | #define kobj_to_state(k) (kobj_to_state_obj(k)->state) |
302 | #define kobj_to_state_usage(k) (kobj_to_state_obj(k)->state_usage) | 310 | #define kobj_to_state_usage(k) (kobj_to_state_obj(k)->state_usage) |
@@ -356,17 +364,17 @@ static inline void cpuidle_free_state_kobj(struct cpuidle_device *device, int i) | |||
356 | } | 364 | } |
357 | 365 | ||
358 | /** | 366 | /** |
359 | * cpuidle_add_driver_sysfs - adds driver-specific sysfs attributes | 367 | * cpuidle_add_state_sysfs - adds cpuidle states sysfs attributes |
360 | * @device: the target device | 368 | * @device: the target device |
361 | */ | 369 | */ |
362 | int cpuidle_add_state_sysfs(struct cpuidle_device *device) | 370 | static int cpuidle_add_state_sysfs(struct cpuidle_device *device) |
363 | { | 371 | { |
364 | int i, ret = -ENOMEM; | 372 | int i, ret = -ENOMEM; |
365 | struct cpuidle_state_kobj *kobj; | 373 | struct cpuidle_state_kobj *kobj; |
366 | struct cpuidle_driver *drv = cpuidle_get_driver(); | 374 | struct cpuidle_driver *drv = cpuidle_get_cpu_driver(device); |
367 | 375 | ||
368 | /* state statistics */ | 376 | /* state statistics */ |
369 | for (i = 0; i < device->state_count; i++) { | 377 | for (i = 0; i < drv->state_count; i++) { |
370 | kobj = kzalloc(sizeof(struct cpuidle_state_kobj), GFP_KERNEL); | 378 | kobj = kzalloc(sizeof(struct cpuidle_state_kobj), GFP_KERNEL); |
371 | if (!kobj) | 379 | if (!kobj) |
372 | goto error_state; | 380 | goto error_state; |
@@ -374,8 +382,8 @@ int cpuidle_add_state_sysfs(struct cpuidle_device *device) | |||
374 | kobj->state_usage = &device->states_usage[i]; | 382 | kobj->state_usage = &device->states_usage[i]; |
375 | init_completion(&kobj->kobj_unregister); | 383 | init_completion(&kobj->kobj_unregister); |
376 | 384 | ||
377 | ret = kobject_init_and_add(&kobj->kobj, &ktype_state_cpuidle, &device->kobj, | 385 | ret = kobject_init_and_add(&kobj->kobj, &ktype_state_cpuidle, |
378 | "state%d", i); | 386 | &device->kobj, "state%d", i); |
379 | if (ret) { | 387 | if (ret) { |
380 | kfree(kobj); | 388 | kfree(kobj); |
381 | goto error_state; | 389 | goto error_state; |
@@ -393,10 +401,10 @@ error_state: | |||
393 | } | 401 | } |
394 | 402 | ||
395 | /** | 403 | /** |
396 | * cpuidle_remove_driver_sysfs - removes driver-specific sysfs attributes | 404 | * cpuidle_remove_driver_sysfs - removes the cpuidle states sysfs attributes |
397 | * @device: the target device | 405 | * @device: the target device |
398 | */ | 406 | */ |
399 | void cpuidle_remove_state_sysfs(struct cpuidle_device *device) | 407 | static void cpuidle_remove_state_sysfs(struct cpuidle_device *device) |
400 | { | 408 | { |
401 | int i; | 409 | int i; |
402 | 410 | ||
@@ -404,17 +412,179 @@ void cpuidle_remove_state_sysfs(struct cpuidle_device *device) | |||
404 | cpuidle_free_state_kobj(device, i); | 412 | cpuidle_free_state_kobj(device, i); |
405 | } | 413 | } |
406 | 414 | ||
415 | #ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS | ||
416 | #define kobj_to_driver_kobj(k) container_of(k, struct cpuidle_driver_kobj, kobj) | ||
417 | #define attr_to_driver_attr(a) container_of(a, struct cpuidle_driver_attr, attr) | ||
418 | |||
419 | #define define_one_driver_ro(_name, show) \ | ||
420 | static struct cpuidle_driver_attr attr_driver_##_name = \ | ||
421 | __ATTR(_name, 0644, show, NULL) | ||
422 | |||
423 | struct cpuidle_driver_kobj { | ||
424 | struct cpuidle_driver *drv; | ||
425 | struct completion kobj_unregister; | ||
426 | struct kobject kobj; | ||
427 | }; | ||
428 | |||
429 | struct cpuidle_driver_attr { | ||
430 | struct attribute attr; | ||
431 | ssize_t (*show)(struct cpuidle_driver *, char *); | ||
432 | ssize_t (*store)(struct cpuidle_driver *, const char *, size_t); | ||
433 | }; | ||
434 | |||
435 | static ssize_t show_driver_name(struct cpuidle_driver *drv, char *buf) | ||
436 | { | ||
437 | ssize_t ret; | ||
438 | |||
439 | spin_lock(&cpuidle_driver_lock); | ||
440 | ret = sprintf(buf, "%s\n", drv ? drv->name : "none"); | ||
441 | spin_unlock(&cpuidle_driver_lock); | ||
442 | |||
443 | return ret; | ||
444 | } | ||
445 | |||
446 | static void cpuidle_driver_sysfs_release(struct kobject *kobj) | ||
447 | { | ||
448 | struct cpuidle_driver_kobj *driver_kobj = kobj_to_driver_kobj(kobj); | ||
449 | complete(&driver_kobj->kobj_unregister); | ||
450 | } | ||
451 | |||
452 | static ssize_t cpuidle_driver_show(struct kobject *kobj, struct attribute * attr, | ||
453 | char * buf) | ||
454 | { | ||
455 | int ret = -EIO; | ||
456 | struct cpuidle_driver_kobj *driver_kobj = kobj_to_driver_kobj(kobj); | ||
457 | struct cpuidle_driver_attr *dattr = attr_to_driver_attr(attr); | ||
458 | |||
459 | if (dattr->show) | ||
460 | ret = dattr->show(driver_kobj->drv, buf); | ||
461 | |||
462 | return ret; | ||
463 | } | ||
464 | |||
465 | static ssize_t cpuidle_driver_store(struct kobject *kobj, struct attribute *attr, | ||
466 | const char *buf, size_t size) | ||
467 | { | ||
468 | int ret = -EIO; | ||
469 | struct cpuidle_driver_kobj *driver_kobj = kobj_to_driver_kobj(kobj); | ||
470 | struct cpuidle_driver_attr *dattr = attr_to_driver_attr(attr); | ||
471 | |||
472 | if (dattr->store) | ||
473 | ret = dattr->store(driver_kobj->drv, buf, size); | ||
474 | |||
475 | return ret; | ||
476 | } | ||
477 | |||
478 | define_one_driver_ro(name, show_driver_name); | ||
479 | |||
480 | static const struct sysfs_ops cpuidle_driver_sysfs_ops = { | ||
481 | .show = cpuidle_driver_show, | ||
482 | .store = cpuidle_driver_store, | ||
483 | }; | ||
484 | |||
485 | static struct attribute *cpuidle_driver_default_attrs[] = { | ||
486 | &attr_driver_name.attr, | ||
487 | NULL | ||
488 | }; | ||
489 | |||
490 | static struct kobj_type ktype_driver_cpuidle = { | ||
491 | .sysfs_ops = &cpuidle_driver_sysfs_ops, | ||
492 | .default_attrs = cpuidle_driver_default_attrs, | ||
493 | .release = cpuidle_driver_sysfs_release, | ||
494 | }; | ||
495 | |||
496 | /** | ||
497 | * cpuidle_add_driver_sysfs - adds the driver name sysfs attribute | ||
498 | * @device: the target device | ||
499 | */ | ||
500 | static int cpuidle_add_driver_sysfs(struct cpuidle_device *dev) | ||
501 | { | ||
502 | struct cpuidle_driver_kobj *kdrv; | ||
503 | struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev); | ||
504 | int ret; | ||
505 | |||
506 | kdrv = kzalloc(sizeof(*kdrv), GFP_KERNEL); | ||
507 | if (!kdrv) | ||
508 | return -ENOMEM; | ||
509 | |||
510 | kdrv->drv = drv; | ||
511 | init_completion(&kdrv->kobj_unregister); | ||
512 | |||
513 | ret = kobject_init_and_add(&kdrv->kobj, &ktype_driver_cpuidle, | ||
514 | &dev->kobj, "driver"); | ||
515 | if (ret) { | ||
516 | kfree(kdrv); | ||
517 | return ret; | ||
518 | } | ||
519 | |||
520 | kobject_uevent(&kdrv->kobj, KOBJ_ADD); | ||
521 | dev->kobj_driver = kdrv; | ||
522 | |||
523 | return ret; | ||
524 | } | ||
525 | |||
526 | /** | ||
527 | * cpuidle_remove_driver_sysfs - removes the driver name sysfs attribute | ||
528 | * @device: the target device | ||
529 | */ | ||
530 | static void cpuidle_remove_driver_sysfs(struct cpuidle_device *dev) | ||
531 | { | ||
532 | struct cpuidle_driver_kobj *kdrv = dev->kobj_driver; | ||
533 | kobject_put(&kdrv->kobj); | ||
534 | wait_for_completion(&kdrv->kobj_unregister); | ||
535 | kfree(kdrv); | ||
536 | } | ||
537 | #else | ||
538 | static inline int cpuidle_add_driver_sysfs(struct cpuidle_device *dev) | ||
539 | { | ||
540 | return 0; | ||
541 | } | ||
542 | |||
543 | static inline void cpuidle_remove_driver_sysfs(struct cpuidle_device *dev) | ||
544 | { | ||
545 | ; | ||
546 | } | ||
547 | #endif | ||
548 | |||
549 | /** | ||
550 | * cpuidle_add_device_sysfs - adds device specific sysfs attributes | ||
551 | * @device: the target device | ||
552 | */ | ||
553 | int cpuidle_add_device_sysfs(struct cpuidle_device *device) | ||
554 | { | ||
555 | int ret; | ||
556 | |||
557 | ret = cpuidle_add_state_sysfs(device); | ||
558 | if (ret) | ||
559 | return ret; | ||
560 | |||
561 | ret = cpuidle_add_driver_sysfs(device); | ||
562 | if (ret) | ||
563 | cpuidle_remove_state_sysfs(device); | ||
564 | return ret; | ||
565 | } | ||
566 | |||
567 | /** | ||
568 | * cpuidle_remove_device_sysfs : removes device specific sysfs attributes | ||
569 | * @device : the target device | ||
570 | */ | ||
571 | void cpuidle_remove_device_sysfs(struct cpuidle_device *device) | ||
572 | { | ||
573 | cpuidle_remove_driver_sysfs(device); | ||
574 | cpuidle_remove_state_sysfs(device); | ||
575 | } | ||
576 | |||
407 | /** | 577 | /** |
408 | * cpuidle_add_sysfs - creates a sysfs instance for the target device | 578 | * cpuidle_add_sysfs - creates a sysfs instance for the target device |
409 | * @dev: the target device | 579 | * @dev: the target device |
410 | */ | 580 | */ |
411 | int cpuidle_add_sysfs(struct device *cpu_dev) | 581 | int cpuidle_add_sysfs(struct cpuidle_device *dev) |
412 | { | 582 | { |
413 | int cpu = cpu_dev->id; | 583 | struct device *cpu_dev = get_cpu_device((unsigned long)dev->cpu); |
414 | struct cpuidle_device *dev; | ||
415 | int error; | 584 | int error; |
416 | 585 | ||
417 | dev = per_cpu(cpuidle_devices, cpu); | 586 | init_completion(&dev->kobj_unregister); |
587 | |||
418 | error = kobject_init_and_add(&dev->kobj, &ktype_cpuidle, &cpu_dev->kobj, | 588 | error = kobject_init_and_add(&dev->kobj, &ktype_cpuidle, &cpu_dev->kobj, |
419 | "cpuidle"); | 589 | "cpuidle"); |
420 | if (!error) | 590 | if (!error) |
@@ -426,11 +596,8 @@ int cpuidle_add_sysfs(struct device *cpu_dev) | |||
426 | * cpuidle_remove_sysfs - deletes a sysfs instance on the target device | 596 | * cpuidle_remove_sysfs - deletes a sysfs instance on the target device |
427 | * @dev: the target device | 597 | * @dev: the target device |
428 | */ | 598 | */ |
429 | void cpuidle_remove_sysfs(struct device *cpu_dev) | 599 | void cpuidle_remove_sysfs(struct cpuidle_device *dev) |
430 | { | 600 | { |
431 | int cpu = cpu_dev->id; | ||
432 | struct cpuidle_device *dev; | ||
433 | |||
434 | dev = per_cpu(cpuidle_devices, cpu); | ||
435 | kobject_put(&dev->kobj); | 601 | kobject_put(&dev->kobj); |
602 | wait_for_completion(&dev->kobj_unregister); | ||
436 | } | 603 | } |