Received: by 2002:ac0:a5b6:0:0:0:0:0 with SMTP id m51-v6csp2064595imm; Mon, 28 May 2018 00:33:18 -0700 (PDT) X-Google-Smtp-Source: AB8JxZolrN1rX+jOIl7aEf7Y7Wi0heR3TivH4S1URRQVwKH/N+adoR0GZT+8drZ4jpxBQLO+9/yK X-Received: by 2002:a17:902:694b:: with SMTP id k11-v6mr12631755plt.334.1527492798466; Mon, 28 May 2018 00:33:18 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1527492798; cv=none; d=google.com; s=arc-20160816; b=vsDZM8SKmsT4GpTFrRBUB6vtWBT4AORHN+586TU8fgnXQGF6QjwEcTTzCs77zNzRwQ Xjkuhsvov7HUsiCT2Pc5Snc5R31lGqK0vBN+qe8XRHv67GGWOLAq3PtMXiS6ctWET+2p Baa3RmSen/g2yCe56p3Q11lQukxtfq1i9MhqQB/er53iZVm6xAOT6mg44aBPaYxJ6+I1 NaSKnSOVZ6Fx+4DakE9A1L8fGmYHs9wTO0HwD6j1fj9DAGju2vbYukvCr/WnBgZIXloM 4DViS+FcmYI8i01pWGN84dIhokDuQvwDGXnqlD4+4yga/KnzV05NeMkad6qW1oJSaQ7g wOTg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:dlp-filter:cms-type :in-reply-to:subject:cc:to:user-agent:organization:from:date :message-id:content-transfer-encoding:mime-version:dkim-signature :dkim-filter:arc-authentication-results; bh=2l/Zc8quv9HT24Rg06/HyKmaw+CjPf9LfMVgf7Llb0I=; b=CPJxyad02om7AHruN9r4M745e2r+AyypWq12eRDG9cUGQNbMJLL3z9zIYuvPoht3LZ 73S8wgSV3cJCth5yoLvsG1/cdCo4pL2Nn17eTcEHzzxIeYLXz6ic4hbz/XCHrlpNGQB4 Fq67vj4BEummvfNbR08l8Pc7Rc/Vqww2WyWZC2VuCg8B4N3Yh4AmW6gSeakghbfvUTSW 9LgmJTSS/9PHbHTPVfY57yWAGcNTL64p4XjpbE3WFuI6Oe3mjll0xIK79YElI7NZcmJs 5PP316Ng+EtO6ztlOgb7NjN6D4B7HHPyhUplCFYIG7ZDxCVN6+qseHfVGNCdEZNLj9Dc dx/A== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@samsung.com header.s=mail20170921 header.b=M/sFwGwj; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=samsung.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id s13-v6si29017719plp.350.2018.05.28.00.33.03; Mon, 28 May 2018 00:33:18 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@samsung.com header.s=mail20170921 header.b=M/sFwGwj; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=samsung.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753718AbeE1Hcs (ORCPT + 99 others); Mon, 28 May 2018 03:32:48 -0400 Received: from mailout3.samsung.com ([203.254.224.33]:25502 "EHLO mailout3.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753612AbeE1Hcp (ORCPT ); Mon, 28 May 2018 03:32:45 -0400 Received: from epcas1p1.samsung.com (unknown [182.195.41.45]) by mailout3.samsung.com (KnoxPortal) with ESMTP id 20180528073242epoutp038d35063105743a4d44c5ba3961b32abc~yvpMZIPk83043730437epoutp03M; Mon, 28 May 2018 07:32:42 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout3.samsung.com 20180528073242epoutp038d35063105743a4d44c5ba3961b32abc~yvpMZIPk83043730437epoutp03M DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1527492762; bh=2l/Zc8quv9HT24Rg06/HyKmaw+CjPf9LfMVgf7Llb0I=; h=Date:From:To:Cc:Subject:In-reply-to:References:From; b=M/sFwGwjTPZ4QpFSuqOsCZYx17zfpjQFGWI6OcrUT6GzVNK9Hd/2CREqIuUx6amEP iOQlX542uWHr0aykmDgT0hjIIfkaukHlCOKgJZ/8//ERTtFQkzLjnKxVjwRWr5tmmp qF14n4KAdXkF4bYVjd02IARE6aUnoR1TwR/e0/Ts= Received: from epsmges1p2.samsung.com (unknown [182.195.40.156]) by epcas1p3.samsung.com (KnoxPortal) with ESMTP id 20180528073238epcas1p3aa1ef001eb3ee0c29028e4fa262a7ec6~yvpIQk75a2297822978epcas1p3R; Mon, 28 May 2018 07:32:38 +0000 (GMT) Received: from epcas1p2.samsung.com ( [182.195.41.46]) by epsmges1p2.samsung.com (Symantec Messaging Gateway) with SMTP id 77.3F.04068.690BB0B5; Mon, 28 May 2018 16:32:38 +0900 (KST) Received: from epsmgms2p1new.samsung.com (unknown [182.195.42.142]) by epcas1p1.samsung.com (KnoxPortal) with ESMTP id 20180528073237epcas1p13a15496dacfd4afe8b7036973a4e12ce~yvpIB6fVg2864028640epcas1p1p; Mon, 28 May 2018 07:32:37 +0000 (GMT) X-AuditID: b6c32a36-be9ff70000000fe4-2a-5b0bb0969523 Received: from epmmp1.local.host ( [203.254.227.16]) by epsmgms2p1new.samsung.com (Symantec Messaging Gateway) with SMTP id 00.6D.03915.590BB0B5; Mon, 28 May 2018 16:32:37 +0900 (KST) MIME-version: 1.0 Content-transfer-encoding: 8BIT Content-type: text/plain; charset="UTF-8" Received: from [10.113.63.77] by mmp1.samsung.com (Oracle Communications Messaging Server 7.0.5.31.0 64bit (built May 5 2014)) with ESMTPA id <0P9F00996GYD2A50@mmp1.samsung.com>; Mon, 28 May 2018 16:32:37 +0900 (KST) Message-id: <5B0BB095.4040106@samsung.com> Date: Mon, 28 May 2018 16:32:37 +0900 From: Chanwoo Choi Organization: Samsung Electronics User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Thunderbird/31.6.0 To: Matthias Kaehlcke , MyungJoo Ham Cc: Kyungmin Park , Arnd Bergmann , Greg Kroah-Hartman , Rob Herring , Mark Rutland , linux-pm@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Brian Norris , Douglas Anderson Subject: Re: [PATCH 09/11] misc: throttler: Add core support for non-thermal throttling In-reply-to: <20180525203043.249193-10-mka@chromium.org> X-Brightmail-Tracker: H4sIAAAAAAAAA01SfUgTcRjud7fdndby18z8ZVh2EaHk8ramV7jIPkcJSdKnlB3uckN3s92U 7A9Z2eew8KsE06aJYiIYVmaJibqsIK0sP0ASKpI+tAixsg/rtsvqr/d5H5736+GlcHUREUJZ BAdvF7h0mvBXNHWGayIvXJ2ZFFU8SbE/C7tItvHTRyXr9vQo2e6adoLNrWog2O5joyT79HYZ wY6f9QC2euAJxo5ffQXYoaO1BHui1UOunWWsv1QPjN+/FQLjRecThbGx7gxhbCuvJ43nrtcB 43jjwgRyLx9r5jkTbw/jhRSbySKkGuiticnrk/XRUUwks4qNocMEzsob6A3xCZGbLOnSpnRY FpeeKVEJnCjSK9bE2m2ZDj7MbBMdBjqJYbQaJipGo9VKUbdvtVYvSQ7w5uYvNXhG6aHDzuJy 3Aku7HEBPwrBlcjlOq90AX9KDZuBlPwi5OQLQEPtw2Ba5e7qIrxYDVsAepx/0ItVcA76WjSs cAGKwuEi5OlN89I4DEc3WhpIuc9zgEabrmCyPgLdH5nw9VHApejBwwIfT0h825tBHx8AF6O+ r698c4PgbnTL/Zn04rkwEVVMPlJ4m+JwAkO3P7h9okC4Bw22jim92A+yqODXa99kBE+RqLIy D5Mv2IA6B2pIGQeid/euk96tEVyAeu8a/ugBOtV6CZeTYoB6Bob/FOvQSKULk2+bjT5M5Cnl YhU6fVItS4yo/3UN9tfHhpIqRT4ILf3PpdJ/LpX+51IFwOvAPD5DtKbyIpOh1YicVcwUUjUp Nmsj8D1kRHQzuNwT3wEgBehZKud3/yS1kssSs60dAFE4PVflHylRKhOXfYS325Ltmem82AH0 kskFeEhQik16b8GRzOi1Op2OXclE6xmGDladjJnaq4apnINP4/kM3j5dh1F+IU5Q9uy9qtAk TA4FtyzfP3Joi8JzJAd7fEed234zwI1bxkzMsRcfl6KscrpEyE3YNrj5ZZJhvDJ7hvPHxmvH La1OUcx/vzAYliEYu4xcZxJCR9P6t+cuGBwLiNuBFT0NrO4/YzHPmazQhM4/Xvd2iTWupBc7 O5XTtzNxate+2rYQWiGaOSYCt4vcb2ht+memAwAA X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFmpikeLIzCtJLcpLzFFi42I5/e+xgO7UDdzRBqtXmVr8nXSM3WLTx/es FvOPnGO1OLvsIJtF8+L1bBZnm96wW1zeNYfN4nPvEUaLpdcvMll83vCY0eJ24wo2i9a9R9gd eDzWzFvD6PH71yRGj9kNF1k8Nq3qZPPYP3cNu0ffllWMHp83yQWwR3HZpKTmZJalFunbJXBl 7Pi+jLlgVmFFw5S5zA2M0yK7GDk5JARMJOYfO8bWxcjFISSwk1FifttWRpAEr4CgxI/J91i6 GDk4mAXkJY5cygYJMwuoS0yat4gZov4+o8S5E/9ZIOq1JE48+8oGYrMIqEqcPDORCcRmA4rv f3EDLM4voChx9cdjRpCZogIREt0nKkHCIgLBEh+ae8BuYBb4yiTx7G87M0hCWCBS4sbet6wQ y3YwSvx8/AZsEKeAhcTE/0/ZJzAKzEJy6yyEW2chuXUBI/MqRsnUguLc9NxiowLDvNRyveLE 3OLSvHS95PzcTYzAmNl2WKtvB+P9JfGHGAU4GJV4eBt+c0ULsSaWFVfmHmKU4GBWEuHl0gUK 8aYkVlalFuXHF5XmpBYfYpTmYFES572ddyxSSCA9sSQ1OzW1ILUIJsvEwSnVwFh3j/HUDu6f 937NuTvl+fzKFSurjU7O7F34p+XmVQnO6LVPyx+UGGz8feLJnAVKb1ZanTHfcUplz182vwes gpPlJjR+2SMw8fE1/iPBcxWZb0kfza67tTtpJ99k50srHxc82e/969DdKLtYzTd1dlPfMXRI VrmXTy/5XqAY/XK3zgbRVqZXby46K7EUZyQaajEXFScCAPPkwm6VAgAA X-CMS-MailID: 20180528073237epcas1p13a15496dacfd4afe8b7036973a4e12ce X-Msg-Generator: CA CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20180525203233epcas1p2f6bd8aa08aa6f259428bf53784b656a9 References: <20180525203043.249193-1-mka@chromium.org> <20180525203043.249193-10-mka@chromium.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Hi Matthias, IMHO, you better to split out the devfreq patches from 'throttler' patch set. Because I'm not sure throttler is either necessary or not. After finishing the review of 'throttler' patches without devfreq handling, it would be better for you to send devfreq patches separately. Regards, Chanwoo Choi On 2018년 05월 26일 05:30, Matthias Kaehlcke wrote: > The purpose of the throttler is to provide support for non-thermal > throttling. Throttling is triggered by external event, e.g. the > detection of a high battery discharge current, close to the OCP limit > of the battery. The throttler is only in charge of the throttling, not > the monitoring, which is done by another (possibly platform specific) > driver. > > Signed-off-by: Matthias Kaehlcke > --- > drivers/misc/Kconfig | 1 + > drivers/misc/Makefile | 1 + > drivers/misc/throttler/Kconfig | 13 ++ > drivers/misc/throttler/Makefile | 1 + > drivers/misc/throttler/core.c | 373 ++++++++++++++++++++++++++++++++ > include/linux/throttler.h | 10 + > 6 files changed, 399 insertions(+) > create mode 100644 drivers/misc/throttler/Kconfig > create mode 100644 drivers/misc/throttler/Makefile > create mode 100644 drivers/misc/throttler/core.c > create mode 100644 include/linux/throttler.h > > diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig > index 5d713008749b..691d9625d83c 100644 > --- a/drivers/misc/Kconfig > +++ b/drivers/misc/Kconfig > @@ -513,4 +513,5 @@ source "drivers/misc/echo/Kconfig" > source "drivers/misc/cxl/Kconfig" > source "drivers/misc/ocxl/Kconfig" > source "drivers/misc/cardreader/Kconfig" > +source "drivers/misc/throttler/Kconfig" > endmenu > diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile > index 20be70c3f118..01a1714dd2ad 100644 > --- a/drivers/misc/Makefile > +++ b/drivers/misc/Makefile > @@ -57,3 +57,4 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o > obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o > obj-$(CONFIG_OCXL) += ocxl/ > obj-$(CONFIG_MISC_RTSX) += cardreader/ > +obj-y += throttler/ > diff --git a/drivers/misc/throttler/Kconfig b/drivers/misc/throttler/Kconfig > new file mode 100644 > index 000000000000..ef8388f6bc0a > --- /dev/null > +++ b/drivers/misc/throttler/Kconfig > @@ -0,0 +1,13 @@ > +menuconfig THROTTLER > + bool "Throttler support" > + default n > + depends on OF > + select CPU_FREQ > + select PM_DEVFREQ > + help > + This option enables core support for non-thermal throttling of CPUs > + and devfreq devices. > + > + Note that you also need a event monitor module usually called > + *_throttler. > + > diff --git a/drivers/misc/throttler/Makefile b/drivers/misc/throttler/Makefile > new file mode 100644 > index 000000000000..c8d920cee315 > --- /dev/null > +++ b/drivers/misc/throttler/Makefile > @@ -0,0 +1 @@ > +obj-$(CONFIG_THROTTLER) += core.o > diff --git a/drivers/misc/throttler/core.c b/drivers/misc/throttler/core.c > new file mode 100644 > index 000000000000..c058d03212b8 > --- /dev/null > +++ b/drivers/misc/throttler/core.c > @@ -0,0 +1,373 @@ > +/* > + * Core code for non-thermal throttling > + * > + * Copyright (C) 2018 Google, Inc. > + * > + * This software is licensed under the terms of the GNU General Public > + * License version 2, as published by the Free Software Foundation, and > + * may be copied, distributed, and modified under those terms. > + * > + * 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. > + * > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +/* > + * Non-thermal throttling: throttling of system components in response to > + * external events (e.g. high battery discharge current). > + * > + * The throttler supports throttling through cpufreq and devfreq. Multiple > + * levels of throttling can be configured. At level 0 no throttling is > + * active on behalf of the throttler, for values > 0 throttling is typically > + * configured to be increasingly aggressive with each level. > + * The number of throttling levels is not limited by the throttler (though > + * it is likely limited by the throttling devices). It is not necessary to > + * configure the same number of levels for all throttling devices. If the > + * requested throttling level for a device is higher than the maximum level > + * of the device the throttler will sleect the maximum throttling level of > + * the device. > + * > + * Non-thermal throttling is split in two parts: > + * > + * - throttler core > + * - parses the thermal policy > + * - applies throttling settings for a requested level of throttling > + * > + * - event monitor driver > + * - monitors the events that trigger throttling > + * - determines the throttling level (often limited to on/off) > + * - requests throttler core to apply throttling settings > + * > + * It is possible for a system to have more than one throttler and the > + * throttlers may make use of the same throttling devices, in case of > + * conflicting settings for a device the more aggressive values will be > + * applied. > + * > + */ > + > +struct thrcfg { > + uint32_t *freqs; > + int num_levels; > +}; > + > +struct cpufreq_thrdev { > + uint32_t cpu; > + struct thrcfg cfg; > +}; > + > +struct devfreq_thrdev { > + struct devfreq *devfreq; > + struct thrcfg cfg; > + struct throttler *thr; > + struct notifier_block nb; > +}; > + > +struct __thr_cpufreq { > + struct cpufreq_thrdev *devs; > + int ndevs; > + struct notifier_block nb; > +}; > + > +struct __thr_devfreq { > + struct devfreq_thrdev *devs; > + int ndevs; > +}; > + > +struct throttler { > + struct device *dev; > + int level; > + struct __thr_cpufreq cpufreq; > + struct __thr_devfreq devfreq; > +}; > + > +static unsigned long thr_get_throttling_freq(struct thrcfg *cfg, int level) > +{ > + if (level == 0 ) { > + WARN(true, "level == 0"); > + return 0; > + } > + > + if (level <= cfg->num_levels) > + return cfg->freqs[level - 1]; > + else > + return cfg->freqs[cfg->num_levels - 1]; > +} > + > +static int thr_cpufreq_event(struct notifier_block *nb, > + unsigned long event, void *data) > +{ > + struct throttler *thr = > + container_of(nb, struct throttler, cpufreq.nb); > + struct cpufreq_policy *policy = data; > + struct cpufreq_thrdev *ctd; > + int i; > + > + if ((event != CPUFREQ_ADJUST) || (thr->level == 0)) > + return NOTIFY_DONE; > + > + for (i = 0; i < thr->cpufreq.ndevs; i++) { > + ctd = &thr->cpufreq.devs[i]; > + > + if (ctd->cpu == policy->cpu) { > + unsigned long clamp_freq = > + thr_get_throttling_freq(&ctd->cfg, thr->level); > + if (clamp_freq < policy->max) { > + cpufreq_verify_within_limits(policy, 0, clamp_freq); > + } > + } > + } > + > + return NOTIFY_DONE; > +} > + > +static int thr_devfreq_event(struct notifier_block *nb, > + unsigned long event, void *data) > +{ > + struct devfreq_thrdev *dtd = > + container_of(nb, struct devfreq_thrdev, nb); > + struct throttler *thr = dtd->thr; > + struct devfreq_policy *policy = data; > + unsigned long clamp_freq; > + > + if ((event != DEVFREQ_ADJUST) || (thr->level == 0)) > + return NOTIFY_DONE; > + > + clamp_freq = thr_get_throttling_freq(&dtd->cfg, thr->level); > + if (clamp_freq < policy->max) > + devfreq_verify_within_limits(policy, 0, clamp_freq); > + > + return NOTIFY_DONE; > +} > + > +static void thr_cpufreq_update_policy(struct throttler *thr) > +{ > + int i; > + > + for (i = 0; i < thr->cpufreq.ndevs; i++) { > + struct cpufreq_thrdev *ctd = &thr->cpufreq.devs[i]; > + struct cpufreq_policy *policy = cpufreq_cpu_get(ctd->cpu); > + > + if (!policy) { > + dev_warn(thr->dev, "CPU%d does have no cpufreq policy!\n", ctd->cpu); > + continue; > + } > + > + cpufreq_update_policy(ctd->cpu); > + cpufreq_cpu_put(policy); > + } > +} > + > +static int thr_parse_thrcfg(struct throttler *thr, > + struct device_node *np, struct thrcfg *cfg) { > + int err; > + > + cfg->num_levels = > + of_property_count_u32_elems(np, "throttling-frequencies"); > + if (cfg->num_levels < 0) { > + pr_err("%s: failed to determine number of throttling frequencies\n", > + np->full_name); > + return cfg->num_levels; > + } > + > + cfg->freqs = devm_kzalloc(thr->dev, > + cfg->num_levels * sizeof(u32), GFP_KERNEL); > + if (!cfg->freqs) > + return -ENOMEM; > + > + err = of_property_read_u32_array(np, "throttling-frequencies", > + cfg->freqs, cfg->num_levels); > + if (err) { > + pr_err("%s: failed to read throttling frequencies\n", np->full_name); > + return err; > + } > + > + return 0; > +} > + > +static struct devfreq *thr_find_devfreq_dev(struct throttler *thr, > + struct device_node *np_df) { > + struct device_node *node; > + struct platform_device *pdev; > + > + node = of_parse_phandle(np_df, "device", 0); > + if (!node) { > + pr_err("%s: failed to get devfreq parent device\n", > + np_df->full_name); > + return ERR_PTR(-EINVAL); > + } > + > + pdev = of_find_device_by_node(node); > + if (!pdev) { > + pr_err("%s: could not find devfreq parent device\n", > + node->full_name); > + return ERR_PTR(-EINVAL); > + } > + > + return dev_to_devfreq(&pdev->dev); > +} > + > +static int thr_parse_dt(struct throttler *thr, struct device_node *np) > +{ > + struct device_node *node, *child; > + int err, i; > + > + node = of_get_child_by_name(np, "cpufreq"); > + if (node) { > + thr->cpufreq.ndevs = of_get_child_count(node); > + thr->cpufreq.devs = devm_kzalloc(thr->dev, > + sizeof(*thr->cpufreq.devs) * thr->cpufreq.ndevs, > + GFP_KERNEL); > + > + i = 0; > + for_each_child_of_node(node, child) { > + struct cpufreq_thrdev *ctd = &thr->cpufreq.devs[i]; > + > + err = of_property_read_u32(child, "cpu", &ctd->cpu); > + if (err) { > + pr_err("%s: failed to read CPU id\n", child->full_name); > + return err; > + } > + > + err = thr_parse_thrcfg(thr, child, &ctd->cfg); > + if (err) > + return err; > + > + i++; > + } > + } > + > + node = of_get_child_by_name(np, "devfreq"); > + if (node) { > + thr->devfreq.ndevs = of_get_child_count(node); > + thr->devfreq.devs = devm_kzalloc(thr->dev, > + sizeof(*thr->devfreq.devs) * thr->devfreq.ndevs, > + GFP_KERNEL); > + > + i = 0; > + for_each_child_of_node(node, child) { > + struct devfreq_thrdev *dtd = &thr->devfreq.devs[i]; > + > + dtd->thr = thr; > + > + dtd->devfreq = thr_find_devfreq_dev(thr, child); > + if (IS_ERR(dtd->devfreq)) > + return PTR_ERR(dtd->devfreq); > + > + err = thr_parse_thrcfg(thr, child, &dtd->cfg); > + if (err) > + return err; > + > + i++; > + } > + } > + > + return 0; > +} > + > +static void thr_update_devfreq(struct devfreq *devfreq) > +{ > + mutex_lock(&devfreq->lock); > + update_devfreq(devfreq); > + mutex_unlock(&devfreq->lock); > +} > + > +void throttler_set_level(struct throttler *thr, int level) > +{ > + int i; > + > + if (level == thr->level) > + return; > + > + dev_dbg(thr->dev, "throttling level: %d\n", level); > + thr->level = level; > + > + if (thr->cpufreq.ndevs > 0) > + thr_cpufreq_update_policy(thr); > + > + if (thr->devfreq.ndevs > 0) > + for (i = 0; i < thr->devfreq.ndevs; i++) > + thr_update_devfreq(thr->devfreq.devs[i].devfreq); > +} > +EXPORT_SYMBOL_GPL(throttler_set_level); > + > +struct throttler *throttler_setup(struct device *dev) > +{ > + struct throttler *thr; > + struct device_node *np = dev->of_node; > + int err, i; > + > + if (!np) > + /* should never happen */ > + return ERR_PTR(-EINVAL); > + > + thr = devm_kzalloc(dev, sizeof(*thr), GFP_KERNEL); > + if (!thr) > + return ERR_PTR(-ENOMEM); > + > + thr->dev = dev; > + > + err = thr_parse_dt(thr, np); > + if (err) > + return ERR_PTR(err); > + > + if (thr->cpufreq.ndevs > 0) { > + thr->cpufreq.nb.notifier_call = thr_cpufreq_event; > + err = cpufreq_register_notifier(&thr->cpufreq.nb, > + CPUFREQ_POLICY_NOTIFIER); > + if (err < 0) { > + dev_err(dev, "failed to register cpufreq notifier\n"); > + return ERR_PTR(err); > + } > + } > + > + for (i = 0; i < thr->devfreq.ndevs; i++) { > + struct devfreq_thrdev *dtd = &thr->devfreq.devs[i]; > + > + dtd->nb.notifier_call = thr_devfreq_event; > + err = devm_devfreq_register_notifier(dev, dtd->devfreq, > + &dtd->nb, DEVFREQ_POLICY_NOTIFIER); > + if (err < 0) { > + dev_err(dev, "failed to register devfreq notifier\n"); > + goto err_cpufreq_unregister; > + } > + } > + > + return thr; > + > +err_cpufreq_unregister: > + if (thr->cpufreq.ndevs > 0) > + cpufreq_unregister_notifier(&thr->cpufreq.nb, > + CPUFREQ_POLICY_NOTIFIER); > + > + return ERR_PTR(err); > +} > +EXPORT_SYMBOL_GPL(throttler_setup); > + > +void throttler_teardown(struct throttler *thr) > +{ > + int i; > + > + thr->level = 0; > + > + if (thr->cpufreq.ndevs > 0) { > + thr_cpufreq_update_policy(thr); > + > + cpufreq_unregister_notifier(&thr->cpufreq.nb, > + CPUFREQ_POLICY_NOTIFIER); > + } > + > + if (thr->devfreq.ndevs > 0) > + for (i = 0; i < thr->devfreq.ndevs; i++) > + thr_update_devfreq(thr->devfreq.devs[i].devfreq); > +} > +EXPORT_SYMBOL_GPL(throttler_teardown); > diff --git a/include/linux/throttler.h b/include/linux/throttler.h > new file mode 100644 > index 000000000000..cab8c466da4b > --- /dev/null > +++ b/include/linux/throttler.h > @@ -0,0 +1,10 @@ > +#ifndef __LINUX_THROTTLER_H__ > +#define __LINUX_THROTTLER_H__ > + > +struct throttler; > + > +extern struct throttler *throttler_setup(struct device *dev); > +extern void throttler_teardown(struct throttler *thr); > +extern void throttler_set_level(struct throttler *thr, int level); > + > +#endif /* __LINUX_THROTTLER_H__ */ >