diff options
author | Bastian Hecht <hechtb@googlemail.com> | 2012-03-18 10:13:20 -0400 |
---|---|---|
committer | David Woodhouse <David.Woodhouse@intel.com> | 2012-03-26 19:53:34 -0400 |
commit | cfe781946dac7f5ff42e23cd7054c75e7201fbdc (patch) | |
tree | 75d102cf44ab8d2fe190a67594fee88790005b88 | |
parent | 42d7fbe223ab878b23de9e3b0166f8cd665a2aa5 (diff) |
mtd: sh_flctl: Add power management with QoS request
Adds power management code with fine granularity. Every flash control
command is enclosed by runtime_put()/get()s. To make sure that no
overhead is generated by too frequent power state switches, a quality of
service request is issued.
Signed-off-by: Bastian Hecht <hechtb@gmail.com>
Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
-rw-r--r-- | drivers/mtd/nand/sh_flctl.c | 51 | ||||
-rw-r--r-- | include/linux/mtd/sh_flctl.h | 3 |
2 files changed, 45 insertions, 9 deletions
diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index d0f1f3c13e76..2ee9a1b50a22 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include <linux/delay.h> | 26 | #include <linux/delay.h> |
27 | #include <linux/io.h> | 27 | #include <linux/io.h> |
28 | #include <linux/platform_device.h> | 28 | #include <linux/platform_device.h> |
29 | #include <linux/pm_runtime.h> | ||
29 | #include <linux/slab.h> | 30 | #include <linux/slab.h> |
30 | 31 | ||
31 | #include <linux/mtd/mtd.h> | 32 | #include <linux/mtd/mtd.h> |
@@ -515,6 +516,8 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command, | |||
515 | struct sh_flctl *flctl = mtd_to_flctl(mtd); | 516 | struct sh_flctl *flctl = mtd_to_flctl(mtd); |
516 | uint32_t read_cmd = 0; | 517 | uint32_t read_cmd = 0; |
517 | 518 | ||
519 | pm_runtime_get_sync(&flctl->pdev->dev); | ||
520 | |||
518 | flctl->read_bytes = 0; | 521 | flctl->read_bytes = 0; |
519 | if (command != NAND_CMD_PAGEPROG) | 522 | if (command != NAND_CMD_PAGEPROG) |
520 | flctl->index = 0; | 523 | flctl->index = 0; |
@@ -670,7 +673,7 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command, | |||
670 | default: | 673 | default: |
671 | break; | 674 | break; |
672 | } | 675 | } |
673 | return; | 676 | goto runtime_exit; |
674 | 677 | ||
675 | read_normal_exit: | 678 | read_normal_exit: |
676 | writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */ | 679 | writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */ |
@@ -678,23 +681,47 @@ read_normal_exit: | |||
678 | start_translation(flctl); | 681 | start_translation(flctl); |
679 | read_fiforeg(flctl, flctl->read_bytes, 0); | 682 | read_fiforeg(flctl, flctl->read_bytes, 0); |
680 | wait_completion(flctl); | 683 | wait_completion(flctl); |
684 | runtime_exit: | ||
685 | pm_runtime_put_sync(&flctl->pdev->dev); | ||
681 | return; | 686 | return; |
682 | } | 687 | } |
683 | 688 | ||
684 | static void flctl_select_chip(struct mtd_info *mtd, int chipnr) | 689 | static void flctl_select_chip(struct mtd_info *mtd, int chipnr) |
685 | { | 690 | { |
686 | struct sh_flctl *flctl = mtd_to_flctl(mtd); | 691 | struct sh_flctl *flctl = mtd_to_flctl(mtd); |
692 | int ret; | ||
687 | 693 | ||
688 | switch (chipnr) { | 694 | switch (chipnr) { |
689 | case -1: | 695 | case -1: |
690 | flctl->flcmncr_base &= ~CE0_ENABLE; | 696 | flctl->flcmncr_base &= ~CE0_ENABLE; |
697 | |||
698 | pm_runtime_get_sync(&flctl->pdev->dev); | ||
691 | writel(flctl->flcmncr_base, FLCMNCR(flctl)); | 699 | writel(flctl->flcmncr_base, FLCMNCR(flctl)); |
700 | |||
701 | if (flctl->qos_request) { | ||
702 | dev_pm_qos_remove_request(&flctl->pm_qos); | ||
703 | flctl->qos_request = 0; | ||
704 | } | ||
705 | |||
706 | pm_runtime_put_sync(&flctl->pdev->dev); | ||
692 | break; | 707 | break; |
693 | case 0: | 708 | case 0: |
694 | flctl->flcmncr_base |= CE0_ENABLE; | 709 | flctl->flcmncr_base |= CE0_ENABLE; |
695 | writel(flctl->flcmncr_base, FLCMNCR(flctl)); | 710 | |
696 | if (flctl->holden) | 711 | if (!flctl->qos_request) { |
712 | ret = dev_pm_qos_add_request(&flctl->pdev->dev, | ||
713 | &flctl->pm_qos, 100); | ||
714 | if (ret < 0) | ||
715 | dev_err(&flctl->pdev->dev, | ||
716 | "PM QoS request failed: %d\n", ret); | ||
717 | flctl->qos_request = 1; | ||
718 | } | ||
719 | |||
720 | if (flctl->holden) { | ||
721 | pm_runtime_get_sync(&flctl->pdev->dev); | ||
697 | writel(HOLDEN, FLHOLDCR(flctl)); | 722 | writel(HOLDEN, FLHOLDCR(flctl)); |
723 | pm_runtime_put_sync(&flctl->pdev->dev); | ||
724 | } | ||
698 | break; | 725 | break; |
699 | default: | 726 | default: |
700 | BUG(); | 727 | BUG(); |
@@ -835,13 +862,13 @@ static int __devinit flctl_probe(struct platform_device *pdev) | |||
835 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 862 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
836 | if (!res) { | 863 | if (!res) { |
837 | dev_err(&pdev->dev, "failed to get I/O memory\n"); | 864 | dev_err(&pdev->dev, "failed to get I/O memory\n"); |
838 | goto err; | 865 | goto err_iomap; |
839 | } | 866 | } |
840 | 867 | ||
841 | flctl->reg = ioremap(res->start, resource_size(res)); | 868 | flctl->reg = ioremap(res->start, resource_size(res)); |
842 | if (flctl->reg == NULL) { | 869 | if (flctl->reg == NULL) { |
843 | dev_err(&pdev->dev, "failed to remap I/O memory\n"); | 870 | dev_err(&pdev->dev, "failed to remap I/O memory\n"); |
844 | goto err; | 871 | goto err_iomap; |
845 | } | 872 | } |
846 | 873 | ||
847 | platform_set_drvdata(pdev, flctl); | 874 | platform_set_drvdata(pdev, flctl); |
@@ -871,23 +898,28 @@ static int __devinit flctl_probe(struct platform_device *pdev) | |||
871 | nand->read_word = flctl_read_word; | 898 | nand->read_word = flctl_read_word; |
872 | } | 899 | } |
873 | 900 | ||
901 | pm_runtime_enable(&pdev->dev); | ||
902 | pm_runtime_resume(&pdev->dev); | ||
903 | |||
874 | ret = nand_scan_ident(flctl_mtd, 1, NULL); | 904 | ret = nand_scan_ident(flctl_mtd, 1, NULL); |
875 | if (ret) | 905 | if (ret) |
876 | goto err; | 906 | goto err_chip; |
877 | 907 | ||
878 | ret = flctl_chip_init_tail(flctl_mtd); | 908 | ret = flctl_chip_init_tail(flctl_mtd); |
879 | if (ret) | 909 | if (ret) |
880 | goto err; | 910 | goto err_chip; |
881 | 911 | ||
882 | ret = nand_scan_tail(flctl_mtd); | 912 | ret = nand_scan_tail(flctl_mtd); |
883 | if (ret) | 913 | if (ret) |
884 | goto err; | 914 | goto err_chip; |
885 | 915 | ||
886 | mtd_device_register(flctl_mtd, pdata->parts, pdata->nr_parts); | 916 | mtd_device_register(flctl_mtd, pdata->parts, pdata->nr_parts); |
887 | 917 | ||
888 | return 0; | 918 | return 0; |
889 | 919 | ||
890 | err: | 920 | err_chip: |
921 | pm_runtime_disable(&pdev->dev); | ||
922 | err_iomap: | ||
891 | kfree(flctl); | 923 | kfree(flctl); |
892 | return ret; | 924 | return ret; |
893 | } | 925 | } |
@@ -897,6 +929,7 @@ static int __devexit flctl_remove(struct platform_device *pdev) | |||
897 | struct sh_flctl *flctl = platform_get_drvdata(pdev); | 929 | struct sh_flctl *flctl = platform_get_drvdata(pdev); |
898 | 930 | ||
899 | nand_release(&flctl->mtd); | 931 | nand_release(&flctl->mtd); |
932 | pm_runtime_disable(&pdev->dev); | ||
900 | kfree(flctl); | 933 | kfree(flctl); |
901 | 934 | ||
902 | return 0; | 935 | return 0; |
diff --git a/include/linux/mtd/sh_flctl.h b/include/linux/mtd/sh_flctl.h index 8bcf299cb9bf..a38e1fa8af01 100644 --- a/include/linux/mtd/sh_flctl.h +++ b/include/linux/mtd/sh_flctl.h | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/mtd/mtd.h> | 23 | #include <linux/mtd/mtd.h> |
24 | #include <linux/mtd/nand.h> | 24 | #include <linux/mtd/nand.h> |
25 | #include <linux/mtd/partitions.h> | 25 | #include <linux/mtd/partitions.h> |
26 | #include <linux/pm_qos.h> | ||
26 | 27 | ||
27 | /* FLCTL registers */ | 28 | /* FLCTL registers */ |
28 | #define FLCMNCR(f) (f->reg + 0x0) | 29 | #define FLCMNCR(f) (f->reg + 0x0) |
@@ -131,6 +132,7 @@ struct sh_flctl { | |||
131 | struct mtd_info mtd; | 132 | struct mtd_info mtd; |
132 | struct nand_chip chip; | 133 | struct nand_chip chip; |
133 | struct platform_device *pdev; | 134 | struct platform_device *pdev; |
135 | struct dev_pm_qos_request pm_qos; | ||
134 | void __iomem *reg; | 136 | void __iomem *reg; |
135 | 137 | ||
136 | uint8_t done_buff[2048 + 64]; /* max size 2048 + 64 */ | 138 | uint8_t done_buff[2048 + 64]; /* max size 2048 + 64 */ |
@@ -149,6 +151,7 @@ struct sh_flctl { | |||
149 | unsigned page_size:1; /* NAND page size (0 = 512, 1 = 2048) */ | 151 | unsigned page_size:1; /* NAND page size (0 = 512, 1 = 2048) */ |
150 | unsigned hwecc:1; /* Hardware ECC (0 = disabled, 1 = enabled) */ | 152 | unsigned hwecc:1; /* Hardware ECC (0 = disabled, 1 = enabled) */ |
151 | unsigned holden:1; /* Hardware has FLHOLDCR and HOLDEN is set */ | 153 | unsigned holden:1; /* Hardware has FLHOLDCR and HOLDEN is set */ |
154 | unsigned qos_request:1; /* QoS request to prevent deep power shutdown */ | ||
152 | }; | 155 | }; |
153 | 156 | ||
154 | struct sh_flctl_platform_data { | 157 | struct sh_flctl_platform_data { |