From patchwork Mon Jul 8 01:44:26 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mike Turquette X-Patchwork-Id: 2824671 Return-Path: X-Original-To: patchwork-linux-pm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id B13CBC0AB2 for ; Mon, 8 Jul 2013 01:45:53 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id A77C420122 for ; Mon, 8 Jul 2013 01:45:52 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 813B72013C for ; Mon, 8 Jul 2013 01:45:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753437Ab3GHBow (ORCPT ); Sun, 7 Jul 2013 21:44:52 -0400 Received: from mail-pa0-f43.google.com ([209.85.220.43]:62668 "EHLO mail-pa0-f43.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753404Ab3GHBou (ORCPT ); Sun, 7 Jul 2013 21:44:50 -0400 Received: by mail-pa0-f43.google.com with SMTP id hz11so3827611pad.16 for ; Sun, 07 Jul 2013 18:44:50 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references :x-gm-message-state; bh=5M5XYmB14WhvK4hgOI039ZcAktZcYmJT+vZYLZlDnbU=; b=SeOkfP515dV0wBBHiHzWcAg+JkejDLQzfucbihfzPoVJlk0QS0Z9OzeeFsWcJCI+FQ u8WKHBhIjIchRatyRAQFmalWbO3btiNIYGBZDFl6RRKKXOY1WwiOpJBasQyfc5BMCtPV 3yILiAqOws/jFvK3BeQGpM3vHswlgDIw5ek9QcImYdwNUhN/VF/tY//o8rdJ4cPrXx2c 7b3Zm5HX4reN49SqmBREOhEm/2sQUtNTP9FIYvRiuEiKrdq9PEBo9RqCt4bJ9o8rZP2X L5ZcCbd70OUyn8MqU79ZkPhWzSpnkDaZgmSJWbpr5Lw5pt+uwTu4xjIBpolKXdi497bF R4Fw== X-Received: by 10.66.171.231 with SMTP id ax7mr20472321pac.32.1373247890311; Sun, 07 Jul 2013 18:44:50 -0700 (PDT) Received: from localhost.localdomain (c-50-152-203-145.hsd1.ca.comcast.net. [50.152.203.145]) by mx.google.com with ESMTPSA id ib9sm19378164pbc.43.2013.07.07.18.44.48 for (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Sun, 07 Jul 2013 18:44:49 -0700 (PDT) From: Mike Turquette To: linux-kernel@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org, linux-pm@vger.kernel.org, Mike Turquette Subject: [PATCH RFC 1/3] clk: notifier handler for dynamic voltage scaling Date: Sun, 7 Jul 2013 18:44:26 -0700 Message-Id: <1373247868-21444-2-git-send-email-mturquette@linaro.org> X-Mailer: git-send-email 1.8.1.2 In-Reply-To: <1373247868-21444-1-git-send-email-mturquette@linaro.org> References: <1373247868-21444-1-git-send-email-mturquette@linaro.org> X-Gm-Message-State: ALoCoQmqY2UD+3aQOf1ZwAvxhUMiTBEigtfw4ufB1Rol+uwVcBoWpyagPkTp2rMj6Q97d63MzHwn Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Spam-Status: No, score=-7.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This patch provides helper functions for drivers that wish to scale voltage through the clock rate-change notifiers. The approach taken is that the driver does not care about the details of the OPP table, nor does it care about handling the voltage regulator directly. The driver only has a pointer to the struct clk object; the other details are hidden in the helper functions. The assumptions about DT structure mostly come from the cpufreq-cpu0 binding. Since this is a helper function it does not have a binding document of its own. A similar patch using platform data instead of DT was proposed in February[1]. [1] http://patches.linaro.org/15128/ Signed-off-by: Mike Turquette --- drivers/clk/Makefile | 1 + drivers/clk/clk-voltage-notifier.c | 135 +++++++++++++++++++++++++++++++++++++ include/linux/clk.h | 7 +- 3 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/clk-voltage-notifier.c diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index d3c3733..beb39f1 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_COMMON_CLK) += clk-fixed-rate.o obj-$(CONFIG_COMMON_CLK) += clk-gate.o obj-$(CONFIG_COMMON_CLK) += clk-mux.o obj-$(CONFIG_COMMON_CLK) += clk-composite.o +obj-$(CONFIG_COMMON_CLK) += clk-voltage-notifier.o # SoCs specific obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o diff --git a/drivers/clk/clk-voltage-notifier.c b/drivers/clk/clk-voltage-notifier.c new file mode 100644 index 0000000..cb6b85f --- /dev/null +++ b/drivers/clk/clk-voltage-notifier.c @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2013 Linaro Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Helper functions for registering clock rate-change notifier handlers + * that scale voltage when a clock changes its output frequency. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct volt_scale_data { + struct device *dev; + struct clk *clk; + struct regulator *reg; + int tol; + struct notifier_block nb; +}; + +#define to_volt_scale_data(_nb) container_of(_nb, \ + struct volt_scale_data, nb) + +static int clk_volt_notifier_handler(struct notifier_block *nb, + unsigned long flags, void *data) +{ + struct clk_notifier_data *cnd = data; + struct volt_scale_data *vsd = to_volt_scale_data(nb); + int ret, new_volt, old_volt, tol; + struct opp *opp; + unsigned long old_rate = cnd->old_rate; + unsigned long new_rate = cnd->new_rate; + + rcu_read_lock(); + opp = opp_find_freq_ceil(vsd->dev, &new_rate); + if (IS_ERR(opp)) { + rcu_read_unlock(); + dev_err(vsd->dev, "%s: failed to find OPP for %lu\n", + __func__, new_rate); + return notifier_from_errno(PTR_ERR(opp)); + } + new_volt = opp_get_voltage(opp); + rcu_read_unlock(); + + tol = new_volt * vsd->tol / 100; + old_volt = regulator_get_voltage(vsd->reg); + + /* scaling up? scale voltage before frequency */ + if (new_rate > old_rate) { + ret = regulator_set_voltage_tol(vsd->reg, new_volt, tol); + if (ret) { + dev_err(vsd->dev, "%s: failed to scale voltage up: %d\n", + __func__, ret); + return notifier_from_errno(ret); + } + } + + /* scaling down? scale voltage after frequency */ + if (new_rate < old_rate) { + ret = regulator_set_voltage_tol(vsd->reg, new_volt, tol); + if (ret) { + dev_err(vsd->dev, "%s: failed to scale voltage down: %d\n", + __func__, ret); + return notifier_from_errno(ret); + } + } + + return NOTIFY_OK; +} + +/** + * of_clk_volt_notifier_register - register clock notifier to scale voltage + * @dev: device that scales clock and voltage regulator + * @np: pointer to DeviceTree node + * @supply: regulator id string + */ +struct notifier_block *of_clk_volt_notifier_register(struct device *dev, + struct device_node *np, struct clk *clk, const char *supply, + int *voltage_latency) +{ + struct volt_scale_data *vsd; + int ret; + + vsd = kzalloc(sizeof(struct volt_scale_data), GFP_KERNEL); + if (!vsd) + return ERR_PTR(-ENOMEM); + + vsd->dev = dev; + vsd->clk = clk; + vsd->nb.notifier_call = clk_volt_notifier_handler; + + vsd->reg = devm_regulator_get(dev, supply); + if (IS_ERR(vsd->reg)) { + ret = PTR_ERR(vsd->reg); + goto err_free_vsd; + } + + of_property_read_u32(np, "voltage-tolerance", &vsd->tol); + + ret = of_init_opp_table(dev); + if (ret) { + pr_err("%s: failed to init OPP table: %d\n", __func__, ret); + goto err_free_vsd; + } + + ret = clk_notifier_register(clk, &vsd->nb); + + if (ret) + goto err_free_vsd; + + return &vsd->nb; + +err_free_vsd: + kfree(vsd); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(of_clk_volt_notifier_register); + +void of_clk_volt_notifier_unregister(struct notifier_block *nb) +{ + struct volt_scale_data *vsd = to_volt_scale_data(nb); + struct clk *clk = vsd->clk; + + clk_notifier_unregister(clk, nb); + kfree(vsd); +} +EXPORT_SYMBOL_GPL(of_clk_volt_notifier_unregister); diff --git a/include/linux/clk.h b/include/linux/clk.h index 9a6d045..85ea520 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -15,6 +15,8 @@ #include #include #include +#include +#include struct device; @@ -79,8 +81,11 @@ struct clk_notifier_data { }; int clk_notifier_register(struct clk *clk, struct notifier_block *nb); - int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb); +struct notifier_block *of_clk_volt_notifier_register(struct device *dev, + struct device_node *np, struct clk *clk, const char *supply, + int *voltage_latency); +void of_clk_volt_notifier_unregister(struct notifier_block *nb); #endif