Loading Documentation/devicetree/bindings/arm/msm/cpubw.txt 0 → 100644 +30 −0 Original line number Diff line number Diff line MSM CPU bandwidth device cpubw is a device that represents the CPU subsystem master ports in a MSM SoC and the related info that is needed to make CPU to DDR bandwidth votes. Required properties: - compatible: Must be "qcom,cpubw" - qcom,cpu-mem-ports: A list of tuples where each tuple consists of a bus master (CPU subsystem) port number and a bus slave (memory) port number. - qcom,bw-tbl: A list of meaningful instantaneous bandwidth values (in MB/s) that can be requested from the CPU subsystem to DDR. The list of values depend on the supported DDR frequencies and the bus widths. Example: qcom,cpubw { compatible = "qcom,cpubw"; qcom,cpu-mem-ports = <1 512>, <2 512>; qcom,bw-tbl = < 572 /* 75 MHz */ >, < 1144 /* 150 MHz */ >, < 1525 /* 200 MHz */ >, < 2342 /* 307 MHz */ >, < 3509 /* 460 MHz */ >, < 4684 /* 614 MHz */ >, < 6103 /* 800 MHz */ >, < 7102 /* 931 MHz */ >; }; arch/arm/mach-msm/Kconfig +29 −0 Original line number Diff line number Diff line Loading @@ -11,6 +11,8 @@ config ARCH_MSM8974 select MSM_SCM select MSM_GPIOMUX select MSM_MULTIMEDIA_USE_ION select PM_DEVFREQ select MSM_DEVFREQ_CPUBW select MSM_PIL select MSM_SPM_V2 select MSM_L2_SPM Loading Loading @@ -66,6 +68,8 @@ config ARCH_APQ8084 select MSM_RPM_LOG select USB_ARCH_HAS_XHCI select KRAIT_REGULATOR select PM_DEVFREQ select MSM_DEVFREQ_CPUBW select MSM_PIL select ENABLE_VMALLOC_SAVINGS select MSM_ULTRASOUND_B Loading @@ -75,6 +79,8 @@ config ARCH_MPQ8092 bool "MPQ8092" select REGULATOR select ARCH_MSM_KRAITMP select PM_DEVFREQ select MSM_DEVFREQ_CPUBW select GPIO_MSM_V3 select ARM_GIC select HAVE_ARM_ARCH_TIMER Loading Loading @@ -103,6 +109,8 @@ config ARCH_FSM9900 select MSM_SCM select HAVE_ARM_ARCH_TIMER select MSM_GPIOMUX select PM_DEVFREQ select MSM_DEVFREQ_CPUBW select MSM_PIL select MSM_NATIVE_RESTART select REGULATOR Loading Loading @@ -133,6 +141,8 @@ config ARCH_MSMKRYPTON select QMI_ENCDEC select MSM_JTAG_MM if CORESIGHT_ETM select MSM_CORTEX_A7 select PM_DEVFREQ select MSM_DEVFREQ_CPUBW config ARCH_MSM8610 bool "MSM8610" Loading @@ -159,6 +169,8 @@ config ARCH_MSM8610 select MSM_CORTEX_A7 select CPU_FREQ_MSM select CPU_FREQ select PM_DEVFREQ select MSM_DEVFREQ_CPUBW select MSM_PIL select MSM_RUN_QUEUE_STATS select ARM_HAS_SG_CHAIN Loading Loading @@ -200,6 +212,8 @@ config ARCH_MSM8226 select MSM_CORTEX_A7 select CPU_FREQ_MSM select CPU_FREQ select PM_DEVFREQ select MSM_DEVFREQ_CPUBW select MSM_PIL select MSM_RUN_QUEUE_STATS select ARM_HAS_SG_CHAIN Loading @@ -224,6 +238,8 @@ config ARCH_MSMSAMARIUM select CPU_V7 select HAVE_ARM_ARCH_TIMER select MSM_SCM select PM_DEVFREQ select MSM_DEVFREQ_CPUBW select MSM_PIL select MSM_GPIOMUX select MSM_SPM_V2 Loading Loading @@ -807,6 +823,19 @@ config MSM_CPU_FREQ_MIN endif # CPU_FREQ_MSM config MSM_DEVFREQ_CPUBW bool "Devfreq device for CPU<->DDR IB/AB BW voting" depends on PM_DEVFREQ select DEVFREQ_GOV_PERFORMANCE select DEVFREQ_GOV_POWERSAVE select DEVFREQ_GOV_USERSPACE default n help Different devfreq governors use this devfreq device to make CPU to DDR IB/AB bandwidth votes. This driver provides a SoC topology agnostic interface to so that some of the devfreq governors can be shared across SoCs. config MSM_AVS_HW bool "Enable Adaptive Voltage Scaling (AVS)" default n Loading arch/arm/mach-msm/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -173,6 +173,7 @@ obj-$(CONFIG_MSM_SMCMOD) += smcmod.o obj-$(CONFIG_ARCH_MSM8974) += msm_mpmctr.o obj-$(CONFIG_MSM_CPR_REGULATOR) += cpr-regulator.o obj-$(CONFIG_CPU_FREQ_MSM) += cpufreq.o obj-$(CONFIG_MSM_DEVFREQ_CPUBW) += devfreq_cpubw.o obj-$(CONFIG_ARCH_RANDOM) += early_random.o Loading arch/arm/mach-msm/devfreq_cpubw.c 0 → 100644 +218 −0 Original line number Diff line number Diff line /* * Copyright (c) 2013, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #define pr_fmt(fmt) "cpubw: " fmt #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/io.h> #include <linux/delay.h> #include <linux/ktime.h> #include <linux/time.h> #include <linux/err.h> #include <linux/errno.h> #include <linux/mutex.h> #include <linux/interrupt.h> #include <linux/devfreq.h> #include <linux/of.h> #include <trace/events/power.h> #include <mach/msm_bus.h> #include <mach/msm_bus_board.h> /* Has to be ULL to prevent overflow where this macro is used. */ #define MBYTE (1ULL << 20) #define MAX_PATHS 2 static struct msm_bus_vectors vectors[MAX_PATHS * 2]; static struct msm_bus_paths bw_levels[] = { { .vectors = &vectors[0] }, { .vectors = &vectors[MAX_PATHS] }, }; static struct msm_bus_scale_pdata bw_data = { .usecase = bw_levels, .num_usecases = ARRAY_SIZE(bw_levels), .name = "devfreq_cpubw", .active_only = 1, }; static int num_paths; static u32 bus_client; static int set_bw(int new_ib, int new_ab) { static int cur_idx, cur_ab, cur_ib; int i, ret; if (cur_ib == new_ib && cur_ab == new_ab) return 0; i = (cur_idx + 1) % ARRAY_SIZE(bw_levels); bw_levels[i].vectors[0].ib = new_ib * MBYTE; bw_levels[i].vectors[0].ab = new_ab / num_paths * MBYTE; bw_levels[i].vectors[1].ib = new_ib * MBYTE; bw_levels[i].vectors[1].ab = new_ab / num_paths * MBYTE; pr_debug("BW MBps: AB: %d IB: %d\n", new_ab, new_ib); ret = msm_bus_scale_client_update_request(bus_client, i); if (ret) { pr_err("bandwidth request failed (%d)\n", ret); } else { cur_idx = i; cur_ib = new_ib; cur_ab = new_ab; } return ret; } static void find_freq(struct devfreq_dev_profile *p, unsigned long *freq, u32 flags) { int i; unsigned long atmost, atleast, f; atmost = p->freq_table[0]; atleast = p->freq_table[p->max_state-1]; for (i = 0; i < p->max_state; i++) { f = p->freq_table[i]; if (f <= *freq) atmost = max(f, atmost); if (f >= *freq) atleast = min(f, atleast); } if (flags & DEVFREQ_FLAG_LEAST_UPPER_BOUND) *freq = atmost; else *freq = atleast; } struct devfreq_dev_profile cpubw_profile; static long gov_ab; int cpubw_target(struct device *dev, unsigned long *freq, u32 flags) { find_freq(&cpubw_profile, freq, flags); return set_bw(*freq, gov_ab); } static struct devfreq_governor_data gov_data[] = { { .name = "performance" }, { .name = "powersave" }, { .name = "userspace" }, }; struct devfreq_dev_profile cpubw_profile = { .polling_ms = 50, .target = cpubw_target, .governor_data = gov_data, .num_governor_data = ARRAY_SIZE(gov_data), }; #define PROP_PORTS "qcom,cpu-mem-ports" #define PROP_TBL "qcom,bw-tbl" static int __init cpubw_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct devfreq_dev_profile *p = &cpubw_profile; struct devfreq *df; u32 *data, ports[MAX_PATHS * 2]; int ret, len, i; if (of_find_property(dev->of_node, PROP_PORTS, &len)) { len /= sizeof(ports[0]); if (len % 2 || len > ARRAY_SIZE(ports)) { dev_err(dev, "Unexpected number of ports\n"); return -EINVAL; } ret = of_property_read_u32_array(dev->of_node, PROP_PORTS, ports, len); if (ret) return ret; num_paths = len / 2; } else { return -EINVAL; } for (i = 0; i < num_paths; i++) { bw_levels[0].vectors[i].src = ports[2 * i]; bw_levels[0].vectors[i].dst = ports[2 * i + 1]; bw_levels[1].vectors[i].src = ports[2 * i]; bw_levels[1].vectors[i].dst = ports[2 * i + 1]; } bw_levels[0].num_paths = num_paths; bw_levels[1].num_paths = num_paths; if (of_find_property(dev->of_node, PROP_TBL, &len)) { len /= sizeof(*data); data = devm_kzalloc(dev, len * sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; p->freq_table = devm_kzalloc(dev, len * sizeof(*p->freq_table), GFP_KERNEL); if (!p->freq_table) return -ENOMEM; ret = of_property_read_u32_array(dev->of_node, PROP_TBL, data, len); if (ret) return ret; for (i = 0; i < len; i++) p->freq_table[i] = data[i]; p->max_state = len; } bus_client = msm_bus_scale_register_client(&bw_data); if (!bus_client) { dev_err(dev, "Unable to register bus client\n"); return -ENODEV; } df = devfreq_add_device(dev, &cpubw_profile, "powersave", NULL); if (IS_ERR(df)) { msm_bus_scale_unregister_client(bus_client); return PTR_ERR(df); } return 0; } static struct of_device_id match_table[] = { { .compatible = "qcom,cpubw" }, {} }; static struct platform_driver cpubw_driver = { .driver = { .name = "cpubw", .of_match_table = match_table, .owner = THIS_MODULE, }, }; static int __init cpubw_init(void) { platform_driver_probe(&cpubw_driver, cpubw_probe); return 0; } device_initcall(cpubw_init); MODULE_DESCRIPTION("CPU DDR bandwidth voting driver MSM CPUs"); MODULE_LICENSE("GPL v2"); Loading
Documentation/devicetree/bindings/arm/msm/cpubw.txt 0 → 100644 +30 −0 Original line number Diff line number Diff line MSM CPU bandwidth device cpubw is a device that represents the CPU subsystem master ports in a MSM SoC and the related info that is needed to make CPU to DDR bandwidth votes. Required properties: - compatible: Must be "qcom,cpubw" - qcom,cpu-mem-ports: A list of tuples where each tuple consists of a bus master (CPU subsystem) port number and a bus slave (memory) port number. - qcom,bw-tbl: A list of meaningful instantaneous bandwidth values (in MB/s) that can be requested from the CPU subsystem to DDR. The list of values depend on the supported DDR frequencies and the bus widths. Example: qcom,cpubw { compatible = "qcom,cpubw"; qcom,cpu-mem-ports = <1 512>, <2 512>; qcom,bw-tbl = < 572 /* 75 MHz */ >, < 1144 /* 150 MHz */ >, < 1525 /* 200 MHz */ >, < 2342 /* 307 MHz */ >, < 3509 /* 460 MHz */ >, < 4684 /* 614 MHz */ >, < 6103 /* 800 MHz */ >, < 7102 /* 931 MHz */ >; };
arch/arm/mach-msm/Kconfig +29 −0 Original line number Diff line number Diff line Loading @@ -11,6 +11,8 @@ config ARCH_MSM8974 select MSM_SCM select MSM_GPIOMUX select MSM_MULTIMEDIA_USE_ION select PM_DEVFREQ select MSM_DEVFREQ_CPUBW select MSM_PIL select MSM_SPM_V2 select MSM_L2_SPM Loading Loading @@ -66,6 +68,8 @@ config ARCH_APQ8084 select MSM_RPM_LOG select USB_ARCH_HAS_XHCI select KRAIT_REGULATOR select PM_DEVFREQ select MSM_DEVFREQ_CPUBW select MSM_PIL select ENABLE_VMALLOC_SAVINGS select MSM_ULTRASOUND_B Loading @@ -75,6 +79,8 @@ config ARCH_MPQ8092 bool "MPQ8092" select REGULATOR select ARCH_MSM_KRAITMP select PM_DEVFREQ select MSM_DEVFREQ_CPUBW select GPIO_MSM_V3 select ARM_GIC select HAVE_ARM_ARCH_TIMER Loading Loading @@ -103,6 +109,8 @@ config ARCH_FSM9900 select MSM_SCM select HAVE_ARM_ARCH_TIMER select MSM_GPIOMUX select PM_DEVFREQ select MSM_DEVFREQ_CPUBW select MSM_PIL select MSM_NATIVE_RESTART select REGULATOR Loading Loading @@ -133,6 +141,8 @@ config ARCH_MSMKRYPTON select QMI_ENCDEC select MSM_JTAG_MM if CORESIGHT_ETM select MSM_CORTEX_A7 select PM_DEVFREQ select MSM_DEVFREQ_CPUBW config ARCH_MSM8610 bool "MSM8610" Loading @@ -159,6 +169,8 @@ config ARCH_MSM8610 select MSM_CORTEX_A7 select CPU_FREQ_MSM select CPU_FREQ select PM_DEVFREQ select MSM_DEVFREQ_CPUBW select MSM_PIL select MSM_RUN_QUEUE_STATS select ARM_HAS_SG_CHAIN Loading Loading @@ -200,6 +212,8 @@ config ARCH_MSM8226 select MSM_CORTEX_A7 select CPU_FREQ_MSM select CPU_FREQ select PM_DEVFREQ select MSM_DEVFREQ_CPUBW select MSM_PIL select MSM_RUN_QUEUE_STATS select ARM_HAS_SG_CHAIN Loading @@ -224,6 +238,8 @@ config ARCH_MSMSAMARIUM select CPU_V7 select HAVE_ARM_ARCH_TIMER select MSM_SCM select PM_DEVFREQ select MSM_DEVFREQ_CPUBW select MSM_PIL select MSM_GPIOMUX select MSM_SPM_V2 Loading Loading @@ -807,6 +823,19 @@ config MSM_CPU_FREQ_MIN endif # CPU_FREQ_MSM config MSM_DEVFREQ_CPUBW bool "Devfreq device for CPU<->DDR IB/AB BW voting" depends on PM_DEVFREQ select DEVFREQ_GOV_PERFORMANCE select DEVFREQ_GOV_POWERSAVE select DEVFREQ_GOV_USERSPACE default n help Different devfreq governors use this devfreq device to make CPU to DDR IB/AB bandwidth votes. This driver provides a SoC topology agnostic interface to so that some of the devfreq governors can be shared across SoCs. config MSM_AVS_HW bool "Enable Adaptive Voltage Scaling (AVS)" default n Loading
arch/arm/mach-msm/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -173,6 +173,7 @@ obj-$(CONFIG_MSM_SMCMOD) += smcmod.o obj-$(CONFIG_ARCH_MSM8974) += msm_mpmctr.o obj-$(CONFIG_MSM_CPR_REGULATOR) += cpr-regulator.o obj-$(CONFIG_CPU_FREQ_MSM) += cpufreq.o obj-$(CONFIG_MSM_DEVFREQ_CPUBW) += devfreq_cpubw.o obj-$(CONFIG_ARCH_RANDOM) += early_random.o Loading
arch/arm/mach-msm/devfreq_cpubw.c 0 → 100644 +218 −0 Original line number Diff line number Diff line /* * Copyright (c) 2013, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #define pr_fmt(fmt) "cpubw: " fmt #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/io.h> #include <linux/delay.h> #include <linux/ktime.h> #include <linux/time.h> #include <linux/err.h> #include <linux/errno.h> #include <linux/mutex.h> #include <linux/interrupt.h> #include <linux/devfreq.h> #include <linux/of.h> #include <trace/events/power.h> #include <mach/msm_bus.h> #include <mach/msm_bus_board.h> /* Has to be ULL to prevent overflow where this macro is used. */ #define MBYTE (1ULL << 20) #define MAX_PATHS 2 static struct msm_bus_vectors vectors[MAX_PATHS * 2]; static struct msm_bus_paths bw_levels[] = { { .vectors = &vectors[0] }, { .vectors = &vectors[MAX_PATHS] }, }; static struct msm_bus_scale_pdata bw_data = { .usecase = bw_levels, .num_usecases = ARRAY_SIZE(bw_levels), .name = "devfreq_cpubw", .active_only = 1, }; static int num_paths; static u32 bus_client; static int set_bw(int new_ib, int new_ab) { static int cur_idx, cur_ab, cur_ib; int i, ret; if (cur_ib == new_ib && cur_ab == new_ab) return 0; i = (cur_idx + 1) % ARRAY_SIZE(bw_levels); bw_levels[i].vectors[0].ib = new_ib * MBYTE; bw_levels[i].vectors[0].ab = new_ab / num_paths * MBYTE; bw_levels[i].vectors[1].ib = new_ib * MBYTE; bw_levels[i].vectors[1].ab = new_ab / num_paths * MBYTE; pr_debug("BW MBps: AB: %d IB: %d\n", new_ab, new_ib); ret = msm_bus_scale_client_update_request(bus_client, i); if (ret) { pr_err("bandwidth request failed (%d)\n", ret); } else { cur_idx = i; cur_ib = new_ib; cur_ab = new_ab; } return ret; } static void find_freq(struct devfreq_dev_profile *p, unsigned long *freq, u32 flags) { int i; unsigned long atmost, atleast, f; atmost = p->freq_table[0]; atleast = p->freq_table[p->max_state-1]; for (i = 0; i < p->max_state; i++) { f = p->freq_table[i]; if (f <= *freq) atmost = max(f, atmost); if (f >= *freq) atleast = min(f, atleast); } if (flags & DEVFREQ_FLAG_LEAST_UPPER_BOUND) *freq = atmost; else *freq = atleast; } struct devfreq_dev_profile cpubw_profile; static long gov_ab; int cpubw_target(struct device *dev, unsigned long *freq, u32 flags) { find_freq(&cpubw_profile, freq, flags); return set_bw(*freq, gov_ab); } static struct devfreq_governor_data gov_data[] = { { .name = "performance" }, { .name = "powersave" }, { .name = "userspace" }, }; struct devfreq_dev_profile cpubw_profile = { .polling_ms = 50, .target = cpubw_target, .governor_data = gov_data, .num_governor_data = ARRAY_SIZE(gov_data), }; #define PROP_PORTS "qcom,cpu-mem-ports" #define PROP_TBL "qcom,bw-tbl" static int __init cpubw_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct devfreq_dev_profile *p = &cpubw_profile; struct devfreq *df; u32 *data, ports[MAX_PATHS * 2]; int ret, len, i; if (of_find_property(dev->of_node, PROP_PORTS, &len)) { len /= sizeof(ports[0]); if (len % 2 || len > ARRAY_SIZE(ports)) { dev_err(dev, "Unexpected number of ports\n"); return -EINVAL; } ret = of_property_read_u32_array(dev->of_node, PROP_PORTS, ports, len); if (ret) return ret; num_paths = len / 2; } else { return -EINVAL; } for (i = 0; i < num_paths; i++) { bw_levels[0].vectors[i].src = ports[2 * i]; bw_levels[0].vectors[i].dst = ports[2 * i + 1]; bw_levels[1].vectors[i].src = ports[2 * i]; bw_levels[1].vectors[i].dst = ports[2 * i + 1]; } bw_levels[0].num_paths = num_paths; bw_levels[1].num_paths = num_paths; if (of_find_property(dev->of_node, PROP_TBL, &len)) { len /= sizeof(*data); data = devm_kzalloc(dev, len * sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; p->freq_table = devm_kzalloc(dev, len * sizeof(*p->freq_table), GFP_KERNEL); if (!p->freq_table) return -ENOMEM; ret = of_property_read_u32_array(dev->of_node, PROP_TBL, data, len); if (ret) return ret; for (i = 0; i < len; i++) p->freq_table[i] = data[i]; p->max_state = len; } bus_client = msm_bus_scale_register_client(&bw_data); if (!bus_client) { dev_err(dev, "Unable to register bus client\n"); return -ENODEV; } df = devfreq_add_device(dev, &cpubw_profile, "powersave", NULL); if (IS_ERR(df)) { msm_bus_scale_unregister_client(bus_client); return PTR_ERR(df); } return 0; } static struct of_device_id match_table[] = { { .compatible = "qcom,cpubw" }, {} }; static struct platform_driver cpubw_driver = { .driver = { .name = "cpubw", .of_match_table = match_table, .owner = THIS_MODULE, }, }; static int __init cpubw_init(void) { platform_driver_probe(&cpubw_driver, cpubw_probe); return 0; } device_initcall(cpubw_init); MODULE_DESCRIPTION("CPU DDR bandwidth voting driver MSM CPUs"); MODULE_LICENSE("GPL v2");