From 0d00a239f70fa52eb23837ffacf990f8404ecace Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 5 Dec 2018 13:50:22 -0600 Subject: [PATCH 01/14] PM / devfreq: Use of_node_name_eq for node name comparisons Convert string compares of DT node names to use of_node_name_eq helper instead. This removes direct access to the node name pointer. For instances using of_node_cmp, this has the side effect of now using case sensitive comparisons. This should not matter for any FDT based system which all of these are. Cc: Chanwoo Choi Cc: MyungJoo Ham Cc: Kyungmin Park Cc: Kukjin Kim Cc: Krzysztof Kozlowski Cc: linux-pm@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org Cc: linux-samsung-soc@vger.kernel.org Signed-off-by: Rob Herring Signed-off-by: MyungJoo Ham --- drivers/devfreq/devfreq-event.c | 2 +- drivers/devfreq/event/exynos-ppmu.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/devfreq/devfreq-event.c b/drivers/devfreq/devfreq-event.c index d67242d877443..87e93406d7cd9 100644 --- a/drivers/devfreq/devfreq-event.c +++ b/drivers/devfreq/devfreq-event.c @@ -240,7 +240,7 @@ struct devfreq_event_dev *devfreq_event_get_edev_by_phandle(struct device *dev, } list_for_each_entry(edev, &devfreq_event_list, node) { - if (!strcmp(edev->desc->name, node->name)) + if (of_node_name_eq(node, edev->desc->name)) goto out; } edev = NULL; diff --git a/drivers/devfreq/event/exynos-ppmu.c b/drivers/devfreq/event/exynos-ppmu.c index c61de0bdf0532..c2ea949575015 100644 --- a/drivers/devfreq/event/exynos-ppmu.c +++ b/drivers/devfreq/event/exynos-ppmu.c @@ -529,7 +529,7 @@ static int of_get_devfreq_events(struct device_node *np, if (!ppmu_events[i].name) continue; - if (!of_node_cmp(node->name, ppmu_events[i].name)) + if (of_node_name_eq(node, ppmu_events[i].name)) break; } From a9487917ba6728dd618ae6d418a7f2197f1b4592 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Sat, 19 Jan 2019 11:04:54 -0500 Subject: [PATCH 02/14] PM / devfreq: fix mem leak in devfreq_add_device() 'devfreq' is malloced in devfreq_add_device() and should be freed in the error handling cases, otherwise it will cause memory leak. Signed-off-by: Yangtao Li Signed-off-by: MyungJoo Ham --- drivers/devfreq/devfreq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 0ae3de76833b7..fa1bdde89ffc9 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -651,7 +651,7 @@ struct devfreq *devfreq_add_device(struct device *dev, mutex_unlock(&devfreq->lock); err = set_freq_table(devfreq); if (err < 0) - goto err_out; + goto err_dev; mutex_lock(&devfreq->lock); } From 25846fa1cedada274b65ffd2413378290a60be47 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Sat, 19 Jan 2019 11:04:53 -0500 Subject: [PATCH 03/14] PM / devfreq: fix missing check of return value in devfreq_add_device() devm_kzalloc() could fail, so insert a check of its return value. And if it fails, returns -ENOMEM. Signed-off-by: Yangtao Li Signed-off-by: MyungJoo Ham --- drivers/devfreq/devfreq.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index fa1bdde89ffc9..4af608a61cd92 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -689,10 +689,22 @@ struct devfreq *devfreq_add_device(struct device *dev, devfreq->profile->max_state, devfreq->profile->max_state), GFP_KERNEL); + if (!devfreq->trans_table) { + mutex_unlock(&devfreq->lock); + err = -ENOMEM; + goto err_devfreq; + } + devfreq->time_in_state = devm_kcalloc(&devfreq->dev, devfreq->profile->max_state, sizeof(unsigned long), GFP_KERNEL); + if (!devfreq->time_in_state) { + mutex_unlock(&devfreq->lock); + err = -ENOMEM; + goto err_devfreq; + } + devfreq->last_stat_updated = jiffies; srcu_init_notifier_head(&devfreq->transition_notifier_list); @@ -726,7 +738,7 @@ struct devfreq *devfreq_add_device(struct device *dev, err_init: mutex_unlock(&devfreq_list_lock); - +err_devfreq: devfreq_remove_device(devfreq); devfreq = NULL; err_dev: From 6d690f77932fe1f3ce5eb2de2c5ac16d33197608 Mon Sep 17 00:00:00 2001 From: MyungJoo Ham Date: Mon, 21 Jan 2019 11:11:07 +0900 Subject: [PATCH 04/14] PM / devfreq: consistent indentation Following up with complaints on inconsistent indentation from Yangtao Li, this fixes indentation inconsistency. In principle, this tries to put arguments aligned to the left including the first argument except for the case where the first argument is on the far-right side. Signed-off-by: MyungJoo Ham Reviewed-by: Chanwoo Choi Acked-by: Yangtao Li --- drivers/devfreq/devfreq.c | 49 +++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 4af608a61cd92..428a1de81008b 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -528,7 +528,7 @@ void devfreq_interval_update(struct devfreq *devfreq, unsigned int *delay) mutex_lock(&devfreq->lock); if (!devfreq->stop_polling) queue_delayed_work(devfreq_wq, &devfreq->work, - msecs_to_jiffies(devfreq->profile->polling_ms)); + msecs_to_jiffies(devfreq->profile->polling_ms)); } out: mutex_unlock(&devfreq->lock); @@ -537,7 +537,7 @@ EXPORT_SYMBOL(devfreq_interval_update); /** * devfreq_notifier_call() - Notify that the device frequency requirements - * has been changed out of devfreq framework. + * has been changed out of devfreq framework. * @nb: the notifier_block (supposed to be devfreq->nb) * @type: not used * @devp: not used @@ -683,12 +683,11 @@ struct devfreq *devfreq_add_device(struct device *dev, goto err_out; } - devfreq->trans_table = - devm_kzalloc(&devfreq->dev, - array3_size(sizeof(unsigned int), - devfreq->profile->max_state, - devfreq->profile->max_state), - GFP_KERNEL); + devfreq->trans_table = devm_kzalloc(&devfreq->dev, + array3_size(sizeof(unsigned int), + devfreq->profile->max_state, + devfreq->profile->max_state), + GFP_KERNEL); if (!devfreq->trans_table) { mutex_unlock(&devfreq->lock); err = -ENOMEM; @@ -696,9 +695,9 @@ struct devfreq *devfreq_add_device(struct device *dev, } devfreq->time_in_state = devm_kcalloc(&devfreq->dev, - devfreq->profile->max_state, - sizeof(unsigned long), - GFP_KERNEL); + devfreq->profile->max_state, + sizeof(unsigned long), + GFP_KERNEL); if (!devfreq->time_in_state) { mutex_unlock(&devfreq->lock); err = -ENOMEM; @@ -1184,7 +1183,7 @@ static ssize_t available_governors_show(struct device *d, */ if (df->governor->immutable) { count = scnprintf(&buf[count], DEVFREQ_NAME_LEN, - "%s ", df->governor_name); + "%s ", df->governor_name); /* * The devfreq device shows the registered governor except for * immutable governors such as passive governor . @@ -1497,8 +1496,8 @@ EXPORT_SYMBOL(devfreq_recommended_opp); /** * devfreq_register_opp_notifier() - Helper function to get devfreq notified - * for any changes in the OPP availability - * changes + * for any changes in the OPP availability + * changes * @dev: The devfreq user device. (parent of devfreq) * @devfreq: The devfreq object. */ @@ -1510,8 +1509,8 @@ EXPORT_SYMBOL(devfreq_register_opp_notifier); /** * devfreq_unregister_opp_notifier() - Helper function to stop getting devfreq - * notified for any changes in the OPP - * availability changes anymore. + * notified for any changes in the OPP + * availability changes anymore. * @dev: The devfreq user device. (parent of devfreq) * @devfreq: The devfreq object. * @@ -1530,8 +1529,8 @@ static void devm_devfreq_opp_release(struct device *dev, void *res) } /** - * devm_ devfreq_register_opp_notifier() - * - Resource-managed devfreq_register_opp_notifier() + * devm_devfreq_register_opp_notifier() - Resource-managed + * devfreq_register_opp_notifier() * @dev: The devfreq user device. (parent of devfreq) * @devfreq: The devfreq object. */ @@ -1559,8 +1558,8 @@ int devm_devfreq_register_opp_notifier(struct device *dev, EXPORT_SYMBOL(devm_devfreq_register_opp_notifier); /** - * devm_devfreq_unregister_opp_notifier() - * - Resource-managed devfreq_unregister_opp_notifier() + * devm_devfreq_unregister_opp_notifier() - Resource-managed + * devfreq_unregister_opp_notifier() * @dev: The devfreq user device. (parent of devfreq) * @devfreq: The devfreq object. */ @@ -1579,8 +1578,8 @@ EXPORT_SYMBOL(devm_devfreq_unregister_opp_notifier); * @list: DEVFREQ_TRANSITION_NOTIFIER. */ int devfreq_register_notifier(struct devfreq *devfreq, - struct notifier_block *nb, - unsigned int list) + struct notifier_block *nb, + unsigned int list) { int ret = 0; @@ -1686,9 +1685,9 @@ EXPORT_SYMBOL(devm_devfreq_register_notifier); * @list: DEVFREQ_TRANSITION_NOTIFIER. */ void devm_devfreq_unregister_notifier(struct device *dev, - struct devfreq *devfreq, - struct notifier_block *nb, - unsigned int list) + struct devfreq *devfreq, + struct notifier_block *nb, + unsigned int list) { WARN_ON(devres_release(dev, devm_devfreq_notifier_release, devm_devfreq_dev_match, devfreq)); From e2794d74f1ec6d31e662b147d55041bd00277278 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Sat, 16 Feb 2019 10:18:24 -0500 Subject: [PATCH 05/14] PM / devfreq: rk3399_dmc: remove unneeded semicolon The semicolon is unneeded, so remove it. Signed-off-by: Yangtao Li Reviewed-by: Chanwoo Choi Signed-off-by: MyungJoo Ham --- drivers/devfreq/rk3399_dmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/devfreq/rk3399_dmc.c b/drivers/devfreq/rk3399_dmc.c index e795ad2b3f6b8..a228dad2bee4e 100644 --- a/drivers/devfreq/rk3399_dmc.c +++ b/drivers/devfreq/rk3399_dmc.c @@ -322,7 +322,7 @@ static int rk3399_dmcfreq_probe(struct platform_device *pdev) dev_err(dev, "Cannot get the clk dmc_clk\n"); return PTR_ERR(data->dmc_clk); - }; + } data->edev = devfreq_event_get_edev_by_phandle(dev, 0); if (IS_ERR(data->edev)) From 726409698feeac20de12fd107159573539b3e063 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Sat, 16 Feb 2019 10:18:25 -0500 Subject: [PATCH 06/14] PM / devfreq: rockchip-dfi: remove unneeded semicolon The semicolon is unneeded, so remove it. Signed-off-by: Yangtao Li Reviewed-by: Chanwoo Choi Signed-off-by: MyungJoo Ham --- drivers/devfreq/event/rockchip-dfi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/devfreq/event/rockchip-dfi.c b/drivers/devfreq/event/rockchip-dfi.c index 22b113363ffc6..fcbf76ebf55dd 100644 --- a/drivers/devfreq/event/rockchip-dfi.c +++ b/drivers/devfreq/event/rockchip-dfi.c @@ -211,7 +211,7 @@ static int rockchip_dfi_probe(struct platform_device *pdev) if (IS_ERR(data->clk)) { dev_err(dev, "Cannot get the clk dmc_clk\n"); return PTR_ERR(data->clk); - }; + } /* try to find the optional reference to the pmu syscon */ node = of_parse_phandle(np, "rockchip,pmu", 0); From 1d1397c3ec1fd0bae330c376269a1c8c7e981a35 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Sat, 16 Feb 2019 10:18:26 -0500 Subject: [PATCH 07/14] PM / devfreq: tegra: remove unneeded variable This variable is not used after initialization, so remove it. And in order to unify the code style, move the location where the dev_get_drvdata is called by the way. Signed-off-by: Yangtao Li Reviewed-by: Chanwoo Choi Acked-by: Jon Hunter Signed-off-by: MyungJoo Ham --- drivers/devfreq/tegra-devfreq.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/devfreq/tegra-devfreq.c b/drivers/devfreq/tegra-devfreq.c index c59d2eee5d309..c89ba7b834ffe 100644 --- a/drivers/devfreq/tegra-devfreq.c +++ b/drivers/devfreq/tegra-devfreq.c @@ -573,10 +573,7 @@ static int tegra_governor_get_target(struct devfreq *devfreq, static int tegra_governor_event_handler(struct devfreq *devfreq, unsigned int event, void *data) { - struct tegra_devfreq *tegra; - int ret = 0; - - tegra = dev_get_drvdata(devfreq->dev.parent); + struct tegra_devfreq *tegra = dev_get_drvdata(devfreq->dev.parent); switch (event) { case DEVFREQ_GOV_START: @@ -600,7 +597,7 @@ static int tegra_governor_event_handler(struct devfreq *devfreq, break; } - return ret; + return 0; } static struct devfreq_governor tegra_devfreq_governor = { From bc658bef97a70094d4347faab7cabf2f5267d03f Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Mon, 11 Mar 2019 15:36:30 +0530 Subject: [PATCH 08/14] PM / devfreq: Restart previous governor if new governor fails to start If the new governor fails to start, switch back to old governor so that the devfreq state is not left in some weird limbo. [Myungjoo: assume fatal on revert failure and set df->governor to NULL] Signed-off-by: Sibi Sankar Signed-off-by: Saravana Kannan Reviewed-by: Chanwoo Choi Signed-off-by: MyungJoo Ham --- drivers/devfreq/devfreq.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 428a1de81008b..08dfcdc2ac58e 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -1124,7 +1124,7 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr, struct devfreq *df = to_devfreq(dev); int ret; char str_governor[DEVFREQ_NAME_LEN + 1]; - struct devfreq_governor *governor; + const struct devfreq_governor *governor, *prev_governor; ret = sscanf(buf, "%" __stringify(DEVFREQ_NAME_LEN) "s", str_governor); if (ret != 1) @@ -1153,12 +1153,24 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr, goto out; } } + prev_governor = df->governor; df->governor = governor; strncpy(df->governor_name, governor->name, DEVFREQ_NAME_LEN); ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL); - if (ret) + if (ret) { dev_warn(dev, "%s: Governor %s not started(%d)\n", __func__, df->governor->name, ret); + df->governor = prev_governor; + strncpy(df->governor_name, prev_governor->name, + DEVFREQ_NAME_LEN); + ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL); + if (ret) { + dev_err(dev, + "%s: reverting to Governor %s failed (%d)\n", + __func__, df->governor_name, ret); + df->governor = NULL; + } + } out: mutex_unlock(&devfreq_list_lock); From b53b0128052ffd687797d5f4deeb76327e7b5711 Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Wed, 13 Mar 2019 13:22:53 +0100 Subject: [PATCH 09/14] PM / devfreq: Fix static checker warning in try_then_request_governor The patch 23c7b54ca1cd: "PM / devfreq: Fix devfreq_add_device() when drivers are built as modules." leads to the following static checker warning: drivers/devfreq/devfreq.c:1043 governor_store() warn: 'governor' can also be NULL The reason is that the try_then_request_governor() function returns both error pointers and NULL. It should just return error pointers, so fix this by returning a ERR_PTR to the error intead of returning NULL. Fixes: 23c7b54ca1cd ("PM / devfreq: Fix devfreq_add_device() when drivers are built as modules.") Reported-by: Dan Carpenter Signed-off-by: Enric Balletbo i Serra Reviewed-by: Chanwoo Choi Signed-off-by: MyungJoo Ham --- drivers/devfreq/devfreq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 08dfcdc2ac58e..8928383a1fa1f 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -228,7 +228,7 @@ static struct devfreq_governor *find_devfreq_governor(const char *name) * if is not found. This can happen when both drivers (the governor driver * and the driver that call devfreq_add_device) are built as modules. * devfreq_list_lock should be held by the caller. Returns the matched - * governor's pointer. + * governor's pointer or an error pointer. */ static struct devfreq_governor *try_then_request_governor(const char *name) { @@ -254,7 +254,7 @@ static struct devfreq_governor *try_then_request_governor(const char *name) /* Restore previous state before return */ mutex_lock(&devfreq_list_lock); if (err) - return NULL; + return ERR_PTR(err); governor = find_devfreq_governor(name); } From fbb9c3c9a5acfdbcc21114de0ba16c7b488968f6 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Thu, 21 Mar 2019 10:01:10 +0100 Subject: [PATCH 10/14] PM / devfreq: exynos-bus: Suspend all devices on system shutdown Force all Exynos buses to safe operation points before doing the system reboot operation. There are board on which some aggressive power saving operation points are behind the capabilities of the bootloader to properly reset the hardware and boot the board. This way one can avoid board crash early after reboot. This fixes reboot issue on OdroidU3 board both with eMMC and SD boot. Reported-by: Markus Reichl Signed-off-by: Marek Szyprowski Acked-by: Chanwoo Choi Signed-off-by: MyungJoo Ham --- drivers/devfreq/exynos-bus.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/devfreq/exynos-bus.c b/drivers/devfreq/exynos-bus.c index c25658b265988..486cc5b422f1d 100644 --- a/drivers/devfreq/exynos-bus.c +++ b/drivers/devfreq/exynos-bus.c @@ -514,6 +514,13 @@ static int exynos_bus_probe(struct platform_device *pdev) return ret; } +static void exynos_bus_shutdown(struct platform_device *pdev) +{ + struct exynos_bus *bus = dev_get_drvdata(&pdev->dev); + + devfreq_suspend_device(bus->devfreq); +} + #ifdef CONFIG_PM_SLEEP static int exynos_bus_resume(struct device *dev) { @@ -556,6 +563,7 @@ MODULE_DEVICE_TABLE(of, exynos_bus_of_match); static struct platform_driver exynos_bus_platdrv = { .probe = exynos_bus_probe, + .shutdown = exynos_bus_shutdown, .driver = { .name = "exynos-bus", .pm = &exynos_bus_pm, From adfe3b76608ffe547af5a74415f15499b798f32a Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Thu, 21 Mar 2019 19:14:36 -0400 Subject: [PATCH 11/14] PM / devfreq: rockchip-dfi: Move GRF definitions to a common place. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some rk3399 GRF (Generic Register Files) definitions can be used for different drivers. Move these definitions to a common include so we don't need to duplicate these definitions. Signed-off-by: Enric Balletbo i Serra Acked-by: Chanwoo Choi Signed-off-by: Gaël PORTAY Signed-off-by: MyungJoo Ham --- drivers/devfreq/event/rockchip-dfi.c | 23 +++++++---------------- include/soc/rockchip/rk3399_grf.h | 21 +++++++++++++++++++++ 2 files changed, 28 insertions(+), 16 deletions(-) create mode 100644 include/soc/rockchip/rk3399_grf.h diff --git a/drivers/devfreq/event/rockchip-dfi.c b/drivers/devfreq/event/rockchip-dfi.c index fcbf76ebf55dd..a436ec4901bbe 100644 --- a/drivers/devfreq/event/rockchip-dfi.c +++ b/drivers/devfreq/event/rockchip-dfi.c @@ -26,6 +26,8 @@ #include #include +#include + #define RK3399_DMC_NUM_CH 2 /* DDRMON_CTRL */ @@ -43,18 +45,6 @@ #define DDRMON_CH1_COUNT_NUM 0x3c #define DDRMON_CH1_DFI_ACCESS_NUM 0x40 -/* pmu grf */ -#define PMUGRF_OS_REG2 0x308 -#define DDRTYPE_SHIFT 13 -#define DDRTYPE_MASK 7 - -enum { - DDR3 = 3, - LPDDR3 = 6, - LPDDR4 = 7, - UNUSED = 0xFF -}; - struct dmc_usage { u32 access; u32 total; @@ -83,16 +73,17 @@ static void rockchip_dfi_start_hardware_counter(struct devfreq_event_dev *edev) u32 ddr_type; /* get ddr type */ - regmap_read(info->regmap_pmu, PMUGRF_OS_REG2, &val); - ddr_type = (val >> DDRTYPE_SHIFT) & DDRTYPE_MASK; + regmap_read(info->regmap_pmu, RK3399_PMUGRF_OS_REG2, &val); + ddr_type = (val >> RK3399_PMUGRF_DDRTYPE_SHIFT) & + RK3399_PMUGRF_DDRTYPE_MASK; /* clear DDRMON_CTRL setting */ writel_relaxed(CLR_DDRMON_CTRL, dfi_regs + DDRMON_CTRL); /* set ddr type to dfi */ - if (ddr_type == LPDDR3) + if (ddr_type == RK3399_PMUGRF_DDRTYPE_LPDDR3) writel_relaxed(LPDDR3_EN, dfi_regs + DDRMON_CTRL); - else if (ddr_type == LPDDR4) + else if (ddr_type == RK3399_PMUGRF_DDRTYPE_LPDDR4) writel_relaxed(LPDDR4_EN, dfi_regs + DDRMON_CTRL); /* enable count, use software mode */ diff --git a/include/soc/rockchip/rk3399_grf.h b/include/soc/rockchip/rk3399_grf.h new file mode 100644 index 0000000000000..3eebabcb28123 --- /dev/null +++ b/include/soc/rockchip/rk3399_grf.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Rockchip General Register Files definitions + * + * Copyright (c) 2018, Collabora Ltd. + * Author: Enric Balletbo i Serra + */ + +#ifndef __SOC_RK3399_GRF_H +#define __SOC_RK3399_GRF_H + +/* PMU GRF Registers */ +#define RK3399_PMUGRF_OS_REG2 0x308 +#define RK3399_PMUGRF_DDRTYPE_SHIFT 13 +#define RK3399_PMUGRF_DDRTYPE_MASK 7 +#define RK3399_PMUGRF_DDRTYPE_DDR3 3 +#define RK3399_PMUGRF_DDRTYPE_LPDDR2 5 +#define RK3399_PMUGRF_DDRTYPE_LPDDR3 6 +#define RK3399_PMUGRF_DDRTYPE_LPDDR4 7 + +#endif From 9173c5ceb035aab28171cd74dfddf27f47213b99 Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Thu, 21 Mar 2019 19:14:38 -0400 Subject: [PATCH 12/14] PM / devfreq: rk3399_dmc: Pass ODT and auto power down parameters to TF-A. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trusted Firmware-A (TF-A) for rk3399 implements a SiP call to get the on-die termination (ODT) and auto power down parameters from kernel, this patch adds the functionality to do this. Also, if DDR clock frequency is lower than the on-die termination (ODT) disable frequency this driver should disable the DDR ODT. Signed-off-by: Enric Balletbo i Serra Reviewed-by: Chanwoo Choi Signed-off-by: Gaël PORTAY Signed-off-by: MyungJoo Ham --- drivers/devfreq/rk3399_dmc.c | 71 ++++++++++++++++++++++++++++- include/soc/rockchip/rockchip_sip.h | 1 + 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/drivers/devfreq/rk3399_dmc.c b/drivers/devfreq/rk3399_dmc.c index a228dad2bee4e..567c034d0301e 100644 --- a/drivers/devfreq/rk3399_dmc.c +++ b/drivers/devfreq/rk3399_dmc.c @@ -18,14 +18,17 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include +#include #include struct dram_timing { @@ -69,8 +72,11 @@ struct rk3399_dmcfreq { struct mutex lock; struct dram_timing timing; struct regulator *vdd_center; + struct regmap *regmap_pmu; unsigned long rate, target_rate; unsigned long volt, target_volt; + unsigned int odt_dis_freq; + int odt_pd_arg0, odt_pd_arg1; }; static int rk3399_dmcfreq_target(struct device *dev, unsigned long *freq, @@ -80,6 +86,8 @@ static int rk3399_dmcfreq_target(struct device *dev, unsigned long *freq, struct dev_pm_opp *opp; unsigned long old_clk_rate = dmcfreq->rate; unsigned long target_volt, target_rate; + struct arm_smccc_res res; + bool odt_enable = false; int err; opp = devfreq_recommended_opp(dev, freq, flags); @@ -95,6 +103,19 @@ static int rk3399_dmcfreq_target(struct device *dev, unsigned long *freq, mutex_lock(&dmcfreq->lock); + if (target_rate >= dmcfreq->odt_dis_freq) + odt_enable = true; + + /* + * This makes a SMC call to the TF-A to set the DDR PD (power-down) + * timings and to enable or disable the ODT (on-die termination) + * resistors. + */ + arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, dmcfreq->odt_pd_arg0, + dmcfreq->odt_pd_arg1, + ROCKCHIP_SIP_CONFIG_DRAM_SET_ODT_PD, + odt_enable, 0, 0, 0, &res); + /* * If frequency scaling from low to high, adjust voltage first. * If frequency scaling from high to low, adjust frequency first. @@ -294,11 +315,13 @@ static int rk3399_dmcfreq_probe(struct platform_device *pdev) { struct arm_smccc_res res; struct device *dev = &pdev->dev; - struct device_node *np = pdev->dev.of_node; + struct device_node *np = pdev->dev.of_node, *node; struct rk3399_dmcfreq *data; int ret, index, size; uint32_t *timing; struct dev_pm_opp *opp; + u32 ddr_type; + u32 val; data = devm_kzalloc(dev, sizeof(struct rk3399_dmcfreq), GFP_KERNEL); if (!data) @@ -354,10 +377,56 @@ static int rk3399_dmcfreq_probe(struct platform_device *pdev) } } + node = of_parse_phandle(np, "rockchip,pmu", 0); + if (node) { + data->regmap_pmu = syscon_node_to_regmap(node); + if (IS_ERR(data->regmap_pmu)) + return PTR_ERR(data->regmap_pmu); + } + + regmap_read(data->regmap_pmu, RK3399_PMUGRF_OS_REG2, &val); + ddr_type = (val >> RK3399_PMUGRF_DDRTYPE_SHIFT) & + RK3399_PMUGRF_DDRTYPE_MASK; + + switch (ddr_type) { + case RK3399_PMUGRF_DDRTYPE_DDR3: + data->odt_dis_freq = data->timing.ddr3_odt_dis_freq; + break; + case RK3399_PMUGRF_DDRTYPE_LPDDR3: + data->odt_dis_freq = data->timing.lpddr3_odt_dis_freq; + break; + case RK3399_PMUGRF_DDRTYPE_LPDDR4: + data->odt_dis_freq = data->timing.lpddr4_odt_dis_freq; + break; + default: + return -EINVAL; + }; + arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, 0, 0, ROCKCHIP_SIP_CONFIG_DRAM_INIT, 0, 0, 0, 0, &res); + /* + * In TF-A there is a platform SIP call to set the PD (power-down) + * timings and to enable or disable the ODT (on-die termination). + * This call needs three arguments as follows: + * + * arg0: + * bit[0-7] : sr_idle + * bit[8-15] : sr_mc_gate_idle + * bit[16-31] : standby idle + * arg1: + * bit[0-11] : pd_idle + * bit[16-27] : srpd_lite_idle + * arg2: + * bit[0] : odt enable + */ + data->odt_pd_arg0 = (data->timing.sr_idle & 0xff) | + ((data->timing.sr_mc_gate_idle & 0xff) << 8) | + ((data->timing.standby_idle & 0xffff) << 16); + data->odt_pd_arg1 = (data->timing.pd_idle & 0xfff) | + ((data->timing.srpd_lite_idle & 0xfff) << 16); + /* * We add a devfreq driver to our parent since it has a device tree node * with operating points. diff --git a/include/soc/rockchip/rockchip_sip.h b/include/soc/rockchip/rockchip_sip.h index 7e28092c4d3db..ad9482c56797f 100644 --- a/include/soc/rockchip/rockchip_sip.h +++ b/include/soc/rockchip/rockchip_sip.h @@ -23,5 +23,6 @@ #define ROCKCHIP_SIP_CONFIG_DRAM_GET_RATE 0x05 #define ROCKCHIP_SIP_CONFIG_DRAM_CLR_IRQ 0x06 #define ROCKCHIP_SIP_CONFIG_DRAM_SET_PARAM 0x07 +#define ROCKCHIP_SIP_CONFIG_DRAM_SET_ODT_PD 0x08 #endif From 1be0730f1dcd2971db4d2fe5497a20f438b837a7 Mon Sep 17 00:00:00 2001 From: Lukasz Luba Date: Mon, 18 Feb 2019 19:21:08 +0100 Subject: [PATCH 13/14] trace: events: add devfreq trace event file The patch adds a new file for with trace events for devfreq framework. They are used for performance analysis of the framework. It also contains updates in MAINTAINERS file adding new entry for devfreq maintainers. Signed-off-by: Lukasz Luba Reviewed-by: Chanwoo Choi Signed-off-by: MyungJoo Ham --- MAINTAINERS | 1 + include/trace/events/devfreq.h | 40 ++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 include/trace/events/devfreq.h diff --git a/MAINTAINERS b/MAINTAINERS index 3671fdea5010e..27ed10966c817 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4552,6 +4552,7 @@ S: Maintained F: drivers/devfreq/ F: include/linux/devfreq.h F: Documentation/devicetree/bindings/devfreq/ +F: include/trace/events/devfreq.h DEVICE FREQUENCY EVENT (DEVFREQ-EVENT) M: Chanwoo Choi diff --git a/include/trace/events/devfreq.h b/include/trace/events/devfreq.h new file mode 100644 index 0000000000000..cf5b8772175d9 --- /dev/null +++ b/include/trace/events/devfreq.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM devfreq + +#if !defined(_TRACE_DEVFREQ_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_DEVFREQ_H + +#include +#include + +TRACE_EVENT(devfreq_monitor, + TP_PROTO(struct devfreq *devfreq), + + TP_ARGS(devfreq), + + TP_STRUCT__entry( + __field(unsigned long, freq) + __field(unsigned long, busy_time) + __field(unsigned long, total_time) + __field(unsigned int, polling_ms) + __string(dev_name, dev_name(&devfreq->dev)) + ), + + TP_fast_assign( + __entry->freq = devfreq->previous_freq; + __entry->busy_time = devfreq->last_status.busy_time; + __entry->total_time = devfreq->last_status.total_time; + __entry->polling_ms = devfreq->profile->polling_ms; + __assign_str(dev_name, dev_name(&devfreq->dev)); + ), + + TP_printk("dev_name=%s freq=%lu polling_ms=%u load=%lu", + __get_str(dev_name), __entry->freq, __entry->polling_ms, + __entry->total_time == 0 ? 0 : + (100 * __entry->busy_time) / __entry->total_time) +); +#endif /* _TRACE_DEVFREQ_H */ + +/* This part must be outside protection */ +#include From cf451adfa392bd9ba36f31659dbe6a5010b46ef9 Mon Sep 17 00:00:00 2001 From: Lukasz Luba Date: Mon, 18 Feb 2019 19:21:09 +0100 Subject: [PATCH 14/14] PM / devfreq: add tracing for scheduling work This patch add basic tracing of the devfreq workqueue and delayed work. It aims to capture changes of the polling intervals and device state. Signed-off-by: Lukasz Luba Reviewed-by: Chanwoo Choi Signed-off-by: MyungJoo Ham --- drivers/devfreq/devfreq.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 8928383a1fa1f..6b6991f0e873f 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -29,6 +29,9 @@ #include #include "governor.h" +#define CREATE_TRACE_POINTS +#include + static struct class *devfreq_class; /* @@ -394,6 +397,8 @@ static void devfreq_monitor(struct work_struct *work) queue_delayed_work(devfreq_wq, &devfreq->work, msecs_to_jiffies(devfreq->profile->polling_ms)); mutex_unlock(&devfreq->lock); + + trace_devfreq_monitor(devfreq); } /**