diff options
author | Johan Rudholm <johan.rudholm@stericsson.com> | 2011-09-05 11:06:55 +0200 |
---|---|---|
committer | Rabin Vincent <rabin.vincent@stericsson.com> | 2011-09-22 15:41:21 +0530 |
commit | 2c2d45d3b5b47e3fbe055ea4793e5917a1cbfa6a (patch) | |
tree | 208a2c4b14389fe8a01943c908e82c179162163c | |
parent | cbac4637451932ebb5f8174690dd14e00e41fc40 (diff) |
ARM: ux500: pm: Workaround for MMC performance
This is a temporary workaround for supervising and boosting MMC
performance. The workaround is implemented in a common file for
similar supervisors which may need to increase the platform
performance. This is configurable via CONFIG_UX500_PM_PERFORMANCE.
ST-Ericsson ID: 348349
ST-Ericsson FOSS-OUT ID: Trivial
ST-Ericsson Linux next: NA
Depends-On: Ic7bf069aa199ff6436b52192898c09d8fffb82bf
Signed-off-by: Johan Rudholm <johan.rudholm@stericsson.com>
Change-Id: I630deebc62e506fe80d1ef241c1a193294c29538
Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/30087
Tested-by: Johan RUDHOLM <johan.rudholm@stericsson.com>
Reviewed-by: QATOOLS
Reviewed-by: QABUILD
Reviewed-by: Sebastian RASMUSSEN <sebastian.rasmussen@stericsson.com>
-rw-r--r-- | arch/arm/mach-ux500/pm/Kconfig | 8 | ||||
-rw-r--r-- | arch/arm/mach-ux500/pm/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/mach-ux500/pm/performance.c | 143 |
3 files changed, 153 insertions, 0 deletions
diff --git a/arch/arm/mach-ux500/pm/Kconfig b/arch/arm/mach-ux500/pm/Kconfig index 26dbe19d558..22a677b4f0c 100644 --- a/arch/arm/mach-ux500/pm/Kconfig +++ b/arch/arm/mach-ux500/pm/Kconfig @@ -91,3 +91,11 @@ config UX500_USECASE_GOVERNOR default y help Adjusts CPU_IDLE, CPU_FREQ, HOTPLUG_CPU and L2 cache parameters + +config UX500_PM_PERFORMANCE + bool "Performance supervision" + depends on UX500_PRCMU_QOS_POWER + default y + help + Enable supervision of events which may require a boost + of platform performance. diff --git a/arch/arm/mach-ux500/pm/Makefile b/arch/arm/mach-ux500/pm/Makefile index 79a0ea49ca8..2d191ff8081 100644 --- a/arch/arm/mach-ux500/pm/Makefile +++ b/arch/arm/mach-ux500/pm/Makefile @@ -18,3 +18,5 @@ endif ifeq ($(CONFIG_UX500_SOC_DB5500), y) obj-$(CONFIG_UX500_CPUFREQ) += cpufreq-db5500.o endif + +obj-$(CONFIG_UX500_PM_PERFORMANCE) += performance.o diff --git a/arch/arm/mach-ux500/pm/performance.c b/arch/arm/mach-ux500/pm/performance.c new file mode 100644 index 00000000000..81d918107b9 --- /dev/null +++ b/arch/arm/mach-ux500/pm/performance.c @@ -0,0 +1,143 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * License Terms: GNU General Public License v2 + * Author: Johan Rudholm <johan.rudholm@stericsson.com> + */ + +#include <linux/kernel.h> +#include <mach/prcmu-qos.h> +#include <linux/genhd.h> +#include <linux/major.h> +#include <linux/cdev.h> + +/* + * TODO: + * o Develop a more power-aware algorithm + * o Make the parameters visible through debugfs + * o Get the value of CONFIG_MMC_BLOCK_MINORS in runtime instead, since + * it may be altered by drivers/mmc/card/block.c + */ + +/* Sample reads and writes every n ms */ +#define PERF_MMC_PROBE_DELAY 1000 +/* Read threshold, sectors/second */ +#define PERF_MMC_LIMIT_READ 10240 +/* Write threshold, sectors/second */ +#define PERF_MMC_LIMIT_WRITE 8192 +/* Nr of MMC devices */ +#define PERF_MMC_HOSTS 8 + +/* + * Rescan for new MMC devices every + * PERF_MMC_PROBE_DELAY * PERF_MMC_RESCAN_CYCLES ms + */ +#define PERF_MMC_RESCAN_CYCLES 10 + +static struct delayed_work work_mmc; + +/* + * Loop through every CONFIG_MMC_BLOCK_MINORS'th minor device for + * MMC_BLOCK_MAJOR, get the struct gendisk for each device. Returns + * nr of found disks. Populate mmc_disks. + */ +static int scan_mmc_devices(struct gendisk *mmc_disks[]) +{ + dev_t devnr; + int i, j = 0, part; + struct gendisk *mmc_devices[256 / CONFIG_MMC_BLOCK_MINORS]; + + memset(&mmc_devices, 0, sizeof(mmc_devices)); + + for (i = 0; i * CONFIG_MMC_BLOCK_MINORS < 256; i++) { + devnr = MKDEV(MMC_BLOCK_MAJOR, i * CONFIG_MMC_BLOCK_MINORS); + mmc_devices[i] = get_gendisk(devnr, &part); + + /* Invalid capacity of device, do not add to list */ + if (!mmc_devices[i] || !get_capacity(mmc_devices[i])) + continue; + + mmc_disks[j] = mmc_devices[i]; + j++; + + if (j == PERF_MMC_HOSTS) + break; + } + + return j; +} + +/* + * Sample sectors read and written to any MMC devices, update PRCMU + * qos requirement + */ +static void mmc_load(struct work_struct *work) +{ + static unsigned long long old_sectors_read[PERF_MMC_HOSTS]; + static unsigned long long old_sectors_written[PERF_MMC_HOSTS]; + static struct gendisk *mmc_disks[PERF_MMC_HOSTS]; + static int cycle, nrdisk; + static bool old_mode; + unsigned long long sectors; + bool new_mode = false; + int i; + + if (!cycle) { + memset(&mmc_disks, 0, sizeof(mmc_disks)); + nrdisk = scan_mmc_devices(mmc_disks); + cycle = PERF_MMC_RESCAN_CYCLES; + } + cycle--; + + for (i = 0; i < nrdisk; i++) { + sectors = part_stat_read(&(mmc_disks[i]->part0), + sectors[READ]); + + if (old_sectors_read[i] && + sectors > old_sectors_read[i] && + (sectors - old_sectors_read[i]) > + PERF_MMC_LIMIT_READ) + new_mode = true; + + old_sectors_read[i] = sectors; + sectors = part_stat_read(&(mmc_disks[i]->part0), + sectors[WRITE]); + + if (old_sectors_written[i] && + sectors > old_sectors_written[i] && + (sectors - old_sectors_written[i]) > + PERF_MMC_LIMIT_WRITE) + new_mode = true; + + old_sectors_written[i] = sectors; + } + + if (!old_mode && new_mode) + prcmu_qos_update_requirement(PRCMU_QOS_ARM_OPP, + "mmc", 125); + + if (old_mode && !new_mode) + prcmu_qos_update_requirement(PRCMU_QOS_ARM_OPP, + "mmc", 25); + + old_mode = new_mode; + + schedule_delayed_work(&work_mmc, + msecs_to_jiffies(PERF_MMC_PROBE_DELAY)); + +} + + +static int __init performance_register(void) +{ + int ret; + + prcmu_qos_add_requirement(PRCMU_QOS_ARM_OPP, "mmc", 25); + + INIT_DELAYED_WORK_DEFERRABLE(&work_mmc, mmc_load); + ret = schedule_delayed_work(&work_mmc, + msecs_to_jiffies(PERF_MMC_PROBE_DELAY)); + + return ret; +} +late_initcall(performance_register); |