From patchwork Fri Dec 1 21:51:56 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jerome Brunet X-Patchwork-Id: 120393 Delivered-To: patch@linaro.org Received: by 10.140.22.227 with SMTP id 90csp1678924qgn; Fri, 1 Dec 2017 13:53:11 -0800 (PST) X-Google-Smtp-Source: AGs4zMY/eT0wDVbyabnQely91WFeZ33UcvX7clfu40V/U3+DzUZOj4TRKTAYcD0/RA3+Z8LcjE7i X-Received: by 10.84.217.206 with SMTP id d14mr7407142plj.132.1512165191341; Fri, 01 Dec 2017 13:53:11 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1512165191; cv=none; d=google.com; s=arc-20160816; b=HQH+HpuyHh0DPH+eBmx4/aEX0KGxI1XWFXqsRlLucK9CpMz3lu7h+Omx7B1MEa6OP1 ViLw75H4cWy9lKOSZAYjY4oP5wOoJhTaJC1DneixhvGQiU0XR4aZWV4AZqZXuSUcfp3A rVpX1B/uEqF8VBARIyU5vacNaXMJGu/HwWE6VlYK3GDKnRJHLuQmnpP+WXu7nLhjJolt 5RLaVGMsHQ/qbTscrv4V19v/jFmq8LqU+L6gtsAyez69IvodkyK9S1o3DMqZdVhB33d+ 1lyC7aRcvoR11u/gMUMOEAATEcAcLP2Hm9PtFt3UxnjmdEYH4jpW1xDV3zWj7y6HjRzh jrEA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=u7NJaAVbZhezob2/UUUtnFwXw+Kt142WNlZ8TlIbpG8=; b=s76Ba3YqCIQQFQ9CA4eAJuV1lu59Saf42pieZTSuihE4+wqJCb1l8yV1zWs3S33jms o4umIyrsAMykIHYhijSnpMEdV/GV+qUDQpFZBorbzuSvOHLJmb7z0AUJhX9a5VmRCDdM 5VYCSQAwIyytGcYxSuJ3/sMDlUaXnXtRbSHHhFipjp4Cp/9LRELjQIUlpWUiNSSWV1kJ sBUptvAo2ThsQQeCki1amFTyBIpI3clGZS632zLAHBNvf7WCBDctUH+LVNIYakVeEJ+0 on1ONWv7P+OElO6sZxtcx4uKUt1xfk38w+N7QNPgZYQklHO82yD3jc8SSBRpuHNB1ai9 WPsA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@baylibre-com.20150623.gappssmtp.com header.s=20150623 header.b=fiy4j37w; 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 Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id v128si5443516pgv.276.2017.12.01.13.53.11; Fri, 01 Dec 2017 13:53:11 -0800 (PST) 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=@baylibre-com.20150623.gappssmtp.com header.s=20150623 header.b=fiy4j37w; 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 Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752104AbdLAVxJ (ORCPT + 28 others); Fri, 1 Dec 2017 16:53:09 -0500 Received: from mail-wm0-f67.google.com ([74.125.82.67]:43758 "EHLO mail-wm0-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751687AbdLAVwQ (ORCPT ); Fri, 1 Dec 2017 16:52:16 -0500 Received: by mail-wm0-f67.google.com with SMTP id n138so5878548wmg.2 for ; Fri, 01 Dec 2017 13:52:15 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=u7NJaAVbZhezob2/UUUtnFwXw+Kt142WNlZ8TlIbpG8=; b=fiy4j37wnvWi7Ga4uFaS+3Zoh6Gf93+tlbOEP95nZXB0hHvV/7GIAWtD8+Zxi+yo6n EgMmXbsO87bpf7sAcJg3AbLDERE8T0FMW5SEdvtH8L53JIrt5F831I0c4WNzJ7Fgvwzp FFydXykH+2CeFucWgD2NA7ZW1Oc4T7uKn+FjBovetncmsjewfDMsLYD4DhmPeE0vqV+t V8OkZPv3y2OUiy+wfGf2lKbmCTkitPertdNWr7UfOpGAdWK/XBTbXYVBUVsTkL2ueQNy VO2LNQhFANjts9sJhAIOP76UVbh4k2UAeVbhUpABMBirQYTfv6aoDrmWEaBr91T+rZtP Bvpg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=u7NJaAVbZhezob2/UUUtnFwXw+Kt142WNlZ8TlIbpG8=; b=LqnKH37444GyAEkZQSDGrbpRUrC6EXxsOxgPVRlTwV68I/xlWVUukDht6QkeZgFsDv fgWBQnF/FtsVI+cq6Kuav2Zx3Zd2fuP3Vnn635KsisbGSaSaIbgm/Uj7DxxQfIa1t68H SYwWVt5o8RLcTJ1QvuetHK0Gi+jDQARempvMSjNKDOox6+bDjrl2nl7V/0Evvs+2XxKq uXtkArj7BNzDgkBs8BMlo6Wyx9NVHuTcKxtibLX0KKBhG6/n7onR2aCsfDkFMwRS6W9S uPKWJVkFNhF7IChO8FN6HWcIXfJTRWMdcUAFXFaXqoS8TsTgQEae0Q/+UtVbBBo2MTKv DlxQ== X-Gm-Message-State: AJaThX4cH9Rv/FwiwBa7oD5qLh91dInSI9OiVxO0jqJ6iaMoDfthp0f8 JTUEMvBQEyRefZhY3o6jo0Z/7Q== X-Received: by 10.28.50.70 with SMTP id y67mr2136262wmy.159.1512165134948; Fri, 01 Dec 2017 13:52:14 -0800 (PST) Received: from localhost.localdomain (cag06-3-82-243-161-21.fbx.proxad.net. [82.243.161.21]) by smtp.googlemail.com with ESMTPSA id m134sm2078804wmg.6.2017.12.01.13.52.13 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Fri, 01 Dec 2017 13:52:14 -0800 (PST) From: Jerome Brunet To: Stephen Boyd , Michael Turquette Cc: Jerome Brunet , linux-clk@vger.kernel.org, linux-kernel@vger.kernel.org, Russell King , Linus Walleij , Quentin Schulz , Kevin Hilman , Maxime Ripard Subject: [PATCH v5 06/10] clk: add clock protection mechanism to clk core Date: Fri, 1 Dec 2017 22:51:56 +0100 Message-Id: <20171201215200.23523-7-jbrunet@baylibre.com> X-Mailer: git-send-email 2.14.3 In-Reply-To: <20171201215200.23523-1-jbrunet@baylibre.com> References: <20171201215200.23523-1-jbrunet@baylibre.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org The patch adds clk_core_protect and clk_core_unprotect to the internal CCF API. These functions allow to set a new constraint along the clock tree to prevent any change, even indirect, which may result in rate change or glitch. Tested-by: Maxime Ripard Acked-by: Michael Turquette Signed-off-by: Jerome Brunet --- drivers/clk/clk.c | 119 ++++++++++++++++++++++++++++++++++++++++--- include/linux/clk-provider.h | 1 + 2 files changed, 113 insertions(+), 7 deletions(-) -- 2.14.3 diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index bbe90babdae4..f69a2176cde1 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -62,6 +62,7 @@ struct clk_core { bool orphan; unsigned int enable_count; unsigned int prepare_count; + unsigned int protect_count; unsigned long min_rate; unsigned long max_rate; unsigned long accuracy; @@ -170,6 +171,11 @@ static void clk_enable_unlock(unsigned long flags) spin_unlock_irqrestore(&enable_lock, flags); } +static bool clk_core_rate_is_protected(struct clk_core *core) +{ + return core->protect_count; +} + static bool clk_core_is_prepared(struct clk_core *core) { bool ret = false; @@ -381,6 +387,11 @@ bool clk_hw_is_prepared(const struct clk_hw *hw) return clk_core_is_prepared(hw->core); } +bool clk_hw_rate_is_protected(const struct clk_hw *hw) +{ + return clk_core_rate_is_protected(hw->core); +} + bool clk_hw_is_enabled(const struct clk_hw *hw) { return clk_core_is_enabled(hw->core); @@ -519,6 +530,68 @@ EXPORT_SYMBOL_GPL(__clk_mux_determine_rate_closest); /*** clk api ***/ +static void clk_core_rate_unprotect(struct clk_core *core) +{ + lockdep_assert_held(&prepare_lock); + + if (!core) + return; + + if (WARN_ON(core->protect_count == 0)) + return; + + if (--core->protect_count > 0) + return; + + clk_core_rate_unprotect(core->parent); +} + +static int clk_core_rate_nuke_protect(struct clk_core *core) +{ + int ret; + + lockdep_assert_held(&prepare_lock); + + if (!core) + return -EINVAL; + + if (core->protect_count == 0) + return 0; + + ret = core->protect_count; + core->protect_count = 1; + clk_core_rate_unprotect(core); + + return ret; +} + +static void clk_core_rate_protect(struct clk_core *core) +{ + lockdep_assert_held(&prepare_lock); + + if (!core) + return; + + if (core->protect_count == 0) + clk_core_rate_protect(core->parent); + + core->protect_count++; +} + +static void clk_core_rate_restore_protect(struct clk_core *core, int count) +{ + lockdep_assert_held(&prepare_lock); + + if (!core) + return; + + if (count == 0) + return; + + clk_core_rate_protect(core); + core->protect_count = count; +} + static void clk_core_unprepare(struct clk_core *core) { lockdep_assert_held(&prepare_lock); @@ -915,7 +988,9 @@ static int clk_core_determine_round_nolock(struct clk_core *core, if (!core) return 0; - if (core->ops->determine_rate) { + if (clk_core_rate_is_protected(core)) { + req->rate = core->rate; + } else if (core->ops->determine_rate) { return core->ops->determine_rate(core->hw, req); } else if (core->ops->round_rate) { rate = core->ops->round_rate(core->hw, req->rate, @@ -1661,7 +1736,7 @@ static void clk_change_rate(struct clk_core *core) static unsigned long clk_core_req_round_rate_nolock(struct clk_core *core, unsigned long req_rate) { - int ret; + int ret, cnt; struct clk_rate_request req; lockdep_assert_held(&prepare_lock); @@ -1669,11 +1744,19 @@ static unsigned long clk_core_req_round_rate_nolock(struct clk_core *core, if (!core) return 0; + /* simulate what the rate would be if it could be freely set */ + cnt = clk_core_rate_nuke_protect(core); + if (cnt < 0) + return cnt; + clk_core_get_boundaries(core, &req.min_rate, &req.max_rate); req.rate = req_rate; ret = clk_core_round_rate_nolock(core, &req); + /* restore the protection */ + clk_core_rate_restore_protect(core, cnt); + return ret ? 0 : req.rate; } @@ -1693,6 +1776,10 @@ static int clk_core_set_rate_nolock(struct clk_core *core, if (rate == clk_core_get_rate_nolock(core)) return 0; + /* fail on a direct rate set of a protected provider */ + if (clk_core_rate_is_protected(core)) + return -EBUSY; + if ((core->flags & CLK_SET_RATE_GATE) && core->prepare_count) return -EBUSY; @@ -1937,6 +2024,9 @@ static int clk_core_set_parent_nolock(struct clk_core *core, if ((core->flags & CLK_SET_PARENT_GATE) && core->prepare_count) return -EBUSY; + if (clk_core_rate_is_protected(core)) + return -EBUSY; + /* try finding the new parent index */ if (parent) { p_index = clk_fetch_parent_index(core, parent); @@ -2018,6 +2108,9 @@ static int clk_core_set_phase_nolock(struct clk_core *core, int degrees) if (!core) return 0; + if (clk_core_rate_is_protected(core)) + return -EBUSY; + trace_clk_set_phase(core, degrees); if (core->ops->set_phase) @@ -2148,11 +2241,12 @@ static void clk_summary_show_one(struct seq_file *s, struct clk_core *c, if (!c) return; - seq_printf(s, "%*s%-*s %11d %12d %11lu %10lu %-3d\n", + seq_printf(s, "%*s%-*s %11d %12d %12d %11lu %10lu %-3d\n", level * 3 + 1, "", 30 - level * 3, c->name, - c->enable_count, c->prepare_count, clk_core_get_rate(c), - clk_core_get_accuracy(c), clk_core_get_phase(c)); + c->enable_count, c->prepare_count, c->protect_count, + clk_core_get_rate(c), clk_core_get_accuracy(c), + clk_core_get_phase(c)); } static void clk_summary_show_subtree(struct seq_file *s, struct clk_core *c, @@ -2174,8 +2268,8 @@ static int clk_summary_show(struct seq_file *s, void *data) struct clk_core *c; struct hlist_head **lists = (struct hlist_head **)s->private; - seq_puts(s, " clock enable_cnt prepare_cnt rate accuracy phase\n"); - seq_puts(s, "----------------------------------------------------------------------------------------\n"); + seq_puts(s, " clock enable_cnt prepare_cnt protect_cnt rate accuracy phase\n"); + seq_puts(s, "----------------------------------------------------------------------------------------------------\n"); clk_prepare_lock(); @@ -2210,6 +2304,7 @@ static void clk_dump_one(struct seq_file *s, struct clk_core *c, int level) seq_printf(s, "\"%s\": { ", c->name); seq_printf(s, "\"enable_count\": %d,", c->enable_count); seq_printf(s, "\"prepare_count\": %d,", c->prepare_count); + seq_printf(s, "\"protect_count\": %d,", c->protect_count); seq_printf(s, "\"rate\": %lu,", clk_core_get_rate(c)); seq_printf(s, "\"accuracy\": %lu,", clk_core_get_accuracy(c)); seq_printf(s, "\"phase\": %d", clk_core_get_phase(c)); @@ -2340,6 +2435,11 @@ static int clk_debug_create_one(struct clk_core *core, struct dentry *pdentry) if (!d) goto err_out; + d = debugfs_create_u32("clk_protect_count", S_IRUGO, core->dentry, + (u32 *)&core->protect_count); + if (!d) + goto err_out; + d = debugfs_create_u32("clk_notifier_count", S_IRUGO, core->dentry, (u32 *)&core->notifier_count); if (!d) @@ -2911,6 +3011,11 @@ void clk_unregister(struct clk *clk) if (clk->core->prepare_count) pr_warn("%s: unregistering prepared clock: %s\n", __func__, clk->core->name); + + if (clk->core->protect_count) + pr_warn("%s: unregistering protected clock: %s\n", + __func__, clk->core->name); + kref_put(&clk->core->ref, __clk_release); unlock: clk_prepare_unlock(); diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 7c925e6211f1..73ac87f34df9 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -744,6 +744,7 @@ unsigned long clk_hw_get_rate(const struct clk_hw *hw); unsigned long __clk_get_flags(struct clk *clk); unsigned long clk_hw_get_flags(const struct clk_hw *hw); bool clk_hw_is_prepared(const struct clk_hw *hw); +bool clk_hw_rate_is_protected(const struct clk_hw *hw); bool clk_hw_is_enabled(const struct clk_hw *hw); bool __clk_is_enabled(struct clk *clk); struct clk *__clk_lookup(const char *name);