diff options
author | Michael Holzheu <holzheu@de.ibm.com> | 2009-06-16 04:30:27 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2009-06-16 04:31:11 -0400 |
commit | 14532095dfe9e8faf2d314d9c2170f64737c7dff (patch) | |
tree | 12435ebeb365a709daa199f9af8ae24753bde6dc /drivers/s390/block | |
parent | 7db11a363fc41cec170a94a3542031e5e64bb333 (diff) |
[S390] pm: xpram driver power management callbacks
Signed-off-by: Michael Holzheu <holzheu@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/block')
-rw-r--r-- | drivers/s390/block/xpram.c | 129 |
1 files changed, 126 insertions, 3 deletions
diff --git a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c index 0ae0c83ef879..2e9e1ecd6d82 100644 --- a/drivers/s390/block/xpram.c +++ b/drivers/s390/block/xpram.c | |||
@@ -39,7 +39,10 @@ | |||
39 | #include <linux/hdreg.h> /* HDIO_GETGEO */ | 39 | #include <linux/hdreg.h> /* HDIO_GETGEO */ |
40 | #include <linux/sysdev.h> | 40 | #include <linux/sysdev.h> |
41 | #include <linux/bio.h> | 41 | #include <linux/bio.h> |
42 | #include <linux/suspend.h> | ||
43 | #include <linux/platform_device.h> | ||
42 | #include <asm/uaccess.h> | 44 | #include <asm/uaccess.h> |
45 | #include <asm/checksum.h> | ||
43 | 46 | ||
44 | #define XPRAM_NAME "xpram" | 47 | #define XPRAM_NAME "xpram" |
45 | #define XPRAM_DEVS 1 /* one partition */ | 48 | #define XPRAM_DEVS 1 /* one partition */ |
@@ -48,6 +51,7 @@ | |||
48 | typedef struct { | 51 | typedef struct { |
49 | unsigned int size; /* size of xpram segment in pages */ | 52 | unsigned int size; /* size of xpram segment in pages */ |
50 | unsigned int offset; /* start page of xpram segment */ | 53 | unsigned int offset; /* start page of xpram segment */ |
54 | unsigned int csum; /* partition checksum for suspend */ | ||
51 | } xpram_device_t; | 55 | } xpram_device_t; |
52 | 56 | ||
53 | static xpram_device_t xpram_devices[XPRAM_MAX_DEVS]; | 57 | static xpram_device_t xpram_devices[XPRAM_MAX_DEVS]; |
@@ -138,7 +142,7 @@ static long xpram_page_out (unsigned long page_addr, unsigned int xpage_index) | |||
138 | /* | 142 | /* |
139 | * Check if xpram is available. | 143 | * Check if xpram is available. |
140 | */ | 144 | */ |
141 | static int __init xpram_present(void) | 145 | static int xpram_present(void) |
142 | { | 146 | { |
143 | unsigned long mem_page; | 147 | unsigned long mem_page; |
144 | int rc; | 148 | int rc; |
@@ -154,7 +158,7 @@ static int __init xpram_present(void) | |||
154 | /* | 158 | /* |
155 | * Return index of the last available xpram page. | 159 | * Return index of the last available xpram page. |
156 | */ | 160 | */ |
157 | static unsigned long __init xpram_highest_page_index(void) | 161 | static unsigned long xpram_highest_page_index(void) |
158 | { | 162 | { |
159 | unsigned int page_index, add_bit; | 163 | unsigned int page_index, add_bit; |
160 | unsigned long mem_page; | 164 | unsigned long mem_page; |
@@ -383,6 +387,106 @@ out: | |||
383 | } | 387 | } |
384 | 388 | ||
385 | /* | 389 | /* |
390 | * Save checksums for all partitions. | ||
391 | */ | ||
392 | static int xpram_save_checksums(void) | ||
393 | { | ||
394 | unsigned long mem_page; | ||
395 | int rc, i; | ||
396 | |||
397 | rc = 0; | ||
398 | mem_page = (unsigned long) __get_free_page(GFP_KERNEL); | ||
399 | if (!mem_page) | ||
400 | return -ENOMEM; | ||
401 | for (i = 0; i < xpram_devs; i++) { | ||
402 | rc = xpram_page_in(mem_page, xpram_devices[i].offset); | ||
403 | if (rc) | ||
404 | goto fail; | ||
405 | xpram_devices[i].csum = csum_partial((const void *) mem_page, | ||
406 | PAGE_SIZE, 0); | ||
407 | } | ||
408 | fail: | ||
409 | free_page(mem_page); | ||
410 | return rc ? -ENXIO : 0; | ||
411 | } | ||
412 | |||
413 | /* | ||
414 | * Verify checksums for all partitions. | ||
415 | */ | ||
416 | static int xpram_validate_checksums(void) | ||
417 | { | ||
418 | unsigned long mem_page; | ||
419 | unsigned int csum; | ||
420 | int rc, i; | ||
421 | |||
422 | rc = 0; | ||
423 | mem_page = (unsigned long) __get_free_page(GFP_KERNEL); | ||
424 | if (!mem_page) | ||
425 | return -ENOMEM; | ||
426 | for (i = 0; i < xpram_devs; i++) { | ||
427 | rc = xpram_page_in(mem_page, xpram_devices[i].offset); | ||
428 | if (rc) | ||
429 | goto fail; | ||
430 | csum = csum_partial((const void *) mem_page, PAGE_SIZE, 0); | ||
431 | if (xpram_devices[i].csum != csum) { | ||
432 | rc = -EINVAL; | ||
433 | goto fail; | ||
434 | } | ||
435 | } | ||
436 | fail: | ||
437 | free_page(mem_page); | ||
438 | return rc ? -ENXIO : 0; | ||
439 | } | ||
440 | |||
441 | /* | ||
442 | * Resume failed: Print error message and call panic. | ||
443 | */ | ||
444 | static void xpram_resume_error(const char *message) | ||
445 | { | ||
446 | pr_err("Resume error: %s\n", message); | ||
447 | panic("xpram resume error\n"); | ||
448 | } | ||
449 | |||
450 | /* | ||
451 | * Check if xpram setup changed between suspend and resume. | ||
452 | */ | ||
453 | static int xpram_restore(struct device *dev) | ||
454 | { | ||
455 | if (!xpram_pages) | ||
456 | return 0; | ||
457 | if (xpram_present() != 0) | ||
458 | xpram_resume_error("xpram disappeared"); | ||
459 | if (xpram_pages != xpram_highest_page_index() + 1) | ||
460 | xpram_resume_error("Size of xpram changed"); | ||
461 | if (xpram_validate_checksums()) | ||
462 | xpram_resume_error("Data of xpram changed"); | ||
463 | return 0; | ||
464 | } | ||
465 | |||
466 | /* | ||
467 | * Save necessary state in suspend. | ||
468 | */ | ||
469 | static int xpram_freeze(struct device *dev) | ||
470 | { | ||
471 | return xpram_save_checksums(); | ||
472 | } | ||
473 | |||
474 | static struct dev_pm_ops xpram_pm_ops = { | ||
475 | .freeze = xpram_freeze, | ||
476 | .restore = xpram_restore, | ||
477 | }; | ||
478 | |||
479 | static struct platform_driver xpram_pdrv = { | ||
480 | .driver = { | ||
481 | .name = XPRAM_NAME, | ||
482 | .owner = THIS_MODULE, | ||
483 | .pm = &xpram_pm_ops, | ||
484 | }, | ||
485 | }; | ||
486 | |||
487 | static struct platform_device *xpram_pdev; | ||
488 | |||
489 | /* | ||
386 | * Finally, the init/exit functions. | 490 | * Finally, the init/exit functions. |
387 | */ | 491 | */ |
388 | static void __exit xpram_exit(void) | 492 | static void __exit xpram_exit(void) |
@@ -394,6 +498,8 @@ static void __exit xpram_exit(void) | |||
394 | put_disk(xpram_disks[i]); | 498 | put_disk(xpram_disks[i]); |
395 | } | 499 | } |
396 | unregister_blkdev(XPRAM_MAJOR, XPRAM_NAME); | 500 | unregister_blkdev(XPRAM_MAJOR, XPRAM_NAME); |
501 | platform_device_unregister(xpram_pdev); | ||
502 | platform_driver_unregister(&xpram_pdrv); | ||
397 | } | 503 | } |
398 | 504 | ||
399 | static int __init xpram_init(void) | 505 | static int __init xpram_init(void) |
@@ -411,7 +517,24 @@ static int __init xpram_init(void) | |||
411 | rc = xpram_setup_sizes(xpram_pages); | 517 | rc = xpram_setup_sizes(xpram_pages); |
412 | if (rc) | 518 | if (rc) |
413 | return rc; | 519 | return rc; |
414 | return xpram_setup_blkdev(); | 520 | rc = platform_driver_register(&xpram_pdrv); |
521 | if (rc) | ||
522 | return rc; | ||
523 | xpram_pdev = platform_device_register_simple(XPRAM_NAME, -1, NULL, 0); | ||
524 | if (IS_ERR(xpram_pdev)) { | ||
525 | rc = PTR_ERR(xpram_pdev); | ||
526 | goto fail_platform_driver_unregister; | ||
527 | } | ||
528 | rc = xpram_setup_blkdev(); | ||
529 | if (rc) | ||
530 | goto fail_platform_device_unregister; | ||
531 | return 0; | ||
532 | |||
533 | fail_platform_device_unregister: | ||
534 | platform_device_unregister(xpram_pdev); | ||
535 | fail_platform_driver_unregister: | ||
536 | platform_driver_unregister(&xpram_pdrv); | ||
537 | return rc; | ||
415 | } | 538 | } |
416 | 539 | ||
417 | module_init(xpram_init); | 540 | module_init(xpram_init); |