Received: by 2002:a05:6a10:206:0:0:0:0 with SMTP id 6csp1987086pxj; Sun, 16 May 2021 10:23:49 -0700 (PDT) X-Google-Smtp-Source: ABdhPJwdN2IIeKskMb4E8zWIGp3hroLmXpx8Ir5GhBjXqGrMfOBnd0/CkH6oIOo0TWvDWO03oDD+ X-Received: by 2002:a05:6402:270b:: with SMTP id y11mr66658080edd.332.1621185829116; Sun, 16 May 2021 10:23:49 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1621185829; cv=none; d=google.com; s=arc-20160816; b=nHLjPH5B1/5DjPSWQEc1hLJ4SzjDis/AE8lA3AFnqfWgm5R4unvZTV649ZRpZG/1CA yBeYx4x8LYIK92XCrop2YyGTB8zBiqSloOBq+qCnuuiJUn9Pl3/Y53sBa/7AfHh3tCVD 5UJ3uWrjoyIxD1GOoUGJhK2Pp+LpoQMZ9tbiCDlqnlKiy4njx2p+LzS3eEEy4z+h+L+n HJS3lzMHLTCi28SLZo71ErMn5JMqgkYTJhs8Q9T64UPGZFX7VTsgEnEDxIMigu+hEbDO tLZIxDidn5hurL5gms4js+GK8oGHvoOphDTZaw7tal2DTsJPZANZawbvtlDWt7kKJd99 3j7Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=3LQbWgDnRWrw67JOcPZWnjtRYMIRGGi64/mZ6QT+/Lg=; b=U84hT1FYPmISiBK2aRpLvUJ9ZnzwPwOpAOzyIKX9n0fruMAnZI1rkSuKfF56GVD5cS YDxrOucO8Q6I0v4GdmQT1wK3joYLzAEXfejSGfyYZumBOCZRfaGaghBU3LgkKKHZSXQ6 27MNk9MaQpuS0z8/psh5LTyyBFag/sgggNScD9ispCjUTYWapKlkuoG4feQnIG4SABrq j6LHWnbX8eRksS5P2FVl7wXdvw892LsPjrEWGiFRJt9qb4VKRLiJ9Eltsq+T8CnV79YQ GkvhTvz23a62hU9r+Gqfl3hIN75kpF/js4A038vn84f+UCp+/7BofFUPEnPxxZX05UMP LK/w== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=B2qLJbyB; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id y22si11452260ejd.57.2021.05.16.10.23.26; Sun, 16 May 2021 10:23:49 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=B2qLJbyB; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233958AbhEPQc1 (ORCPT + 99 others); Sun, 16 May 2021 12:32:27 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58436 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231587AbhEPQcR (ORCPT ); Sun, 16 May 2021 12:32:17 -0400 Received: from mail-lf1-x12f.google.com (mail-lf1-x12f.google.com [IPv6:2a00:1450:4864:20::12f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2D13CC061756; Sun, 16 May 2021 09:31:02 -0700 (PDT) Received: by mail-lf1-x12f.google.com with SMTP id h4so5334284lfv.0; Sun, 16 May 2021 09:31:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=3LQbWgDnRWrw67JOcPZWnjtRYMIRGGi64/mZ6QT+/Lg=; b=B2qLJbyBn63ouVFVdvXbWT6UpnxprvgE9oHeFIYxGfkrtvjhfvAkjcXBCNXOPNc7Ai WvSDEQjo3lYsksEnAIh9kz2Lru6H2sQRSc+Whe28LS5QOvCtT0XdlXxFWAy964xZgBSX H6mKpSxCbwZPqPxfIjZDimoWG9Eho6h89jOIa1H1WVu9ojLcU72w8rz8uMcKxW0CPs3r 0eS0tcZ8La7AhpSCcywY7mLCod9xf+BNlub28cTbqbrTgyJkNWxsGRndKm50LtXiMGP2 nN3lNBnqWf+yAzPP3yzc5+/uQZaNID6afpLrUMGdhz8EBrxJnpAKuWd2vMVgrw3Sq87q SMcQ== 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:mime-version:content-transfer-encoding; bh=3LQbWgDnRWrw67JOcPZWnjtRYMIRGGi64/mZ6QT+/Lg=; b=HCms8anwEA3c/ZMgoNwUVbzRd4+UoGmYfzYvXurJZKYoAktZdVAWzjwW/KGEH8ZPg4 /M8GojetR2Q02nhHgaSVphVVVB29PO5HnvbmozpBmxskzVBnvOn/NW8umN2rW14k8cnZ Ggx8dcvGHAP4fkxJCaqrYNd3LnZ8qwtPGkmchJepHMa8WRfUCKgN8E+BYsh80OCQR7dX WYR8twRDFhToJ7EclbZbHL22A+1sxDqILlZs5ule9rVFApKoyp8SXTYmqvkMO5yBay6Q qLGAt88Q1jEUyJXmSwP4Lam851l6Q0Yq4wOW20mtpPWGWgOXaNqUhzvmN8bkoW4+BQ1U RW1Q== X-Gm-Message-State: AOAM532Vuz8zqTSL6wqV4JcFPrVvt55d0V4uQ3h1LOzQyYK0A17DAoDo i7Iy8seeuVDBhiRUxQP+gL4= X-Received: by 2002:a05:6512:2316:: with SMTP id o22mr10664621lfu.510.1621182660667; Sun, 16 May 2021 09:31:00 -0700 (PDT) Received: from localhost.localdomain (109-252-193-91.dynamic.spd-mgts.ru. [109.252.193.91]) by smtp.gmail.com with ESMTPSA id m2sm1704548lfo.23.2021.05.16.09.31.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 16 May 2021 09:31:00 -0700 (PDT) From: Dmitry Osipenko To: Thierry Reding , Jonathan Hunter , Peter De Schrijver , Prashant Gaikwad , Michael Turquette , Stephen Boyd , =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= Cc: Rob Herring , linux-tegra@vger.kernel.org, linux-clk@vger.kernel.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org Subject: [PATCH v8 2/9] clk: tegra: Fix refcounting of gate clocks Date: Sun, 16 May 2021 19:30:34 +0300 Message-Id: <20210516163041.12818-3-digetx@gmail.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210516163041.12818-1-digetx@gmail.com> References: <20210516163041.12818-1-digetx@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org The refcounting of the gate clocks has a bug causing the enable_refcnt to underflow when unused clocks are disabled. This happens because clk provider erroneously bumps the refcount if clock is enabled at a boot time, which it shouldn't be doing, and it does this only for the gate clocks, while peripheral clocks are using the same gate ops and the peripheral clocks are missing the initial bump. Hence the refcount of the peripheral clocks is 0 when unused clocks are disabled and then the counter is decremented further by the gate ops, causing the integer underflow. Fix this problem by removing the erroneous bump and by implementing the disable_unused() callback, which disables the unused gates properly. The visible effect of the bug is such that the unused clocks are never gated if a loaded kernel module grabs the unused clocks and starts to use them. In practice this shouldn't cause any real problems for the drivers and boards supported by the kernel today. Acked-by: Thierry Reding Signed-off-by: Dmitry Osipenko --- drivers/clk/tegra/clk-periph-gate.c | 72 +++++++++++++++++++---------- drivers/clk/tegra/clk-periph.c | 11 +++++ 2 files changed, 58 insertions(+), 25 deletions(-) diff --git a/drivers/clk/tegra/clk-periph-gate.c b/drivers/clk/tegra/clk-periph-gate.c index 4b31beefc9fc..dc3f92678407 100644 --- a/drivers/clk/tegra/clk-periph-gate.c +++ b/drivers/clk/tegra/clk-periph-gate.c @@ -48,18 +48,9 @@ static int clk_periph_is_enabled(struct clk_hw *hw) return state; } -static int clk_periph_enable(struct clk_hw *hw) +static void clk_periph_enable_locked(struct clk_hw *hw) { struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw); - unsigned long flags = 0; - - spin_lock_irqsave(&periph_ref_lock, flags); - - gate->enable_refcnt[gate->clk_num]++; - if (gate->enable_refcnt[gate->clk_num] > 1) { - spin_unlock_irqrestore(&periph_ref_lock, flags); - return 0; - } write_enb_set(periph_clk_to_bit(gate), gate); udelay(2); @@ -78,6 +69,32 @@ static int clk_periph_enable(struct clk_hw *hw) udelay(1); writel_relaxed(0, gate->clk_base + LVL2_CLK_GATE_OVRE); } +} + +static void clk_periph_disable_locked(struct clk_hw *hw) +{ + struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw); + + /* + * If peripheral is in the APB bus then read the APB bus to + * flush the write operation in apb bus. This will avoid the + * peripheral access after disabling clock + */ + if (gate->flags & TEGRA_PERIPH_ON_APB) + tegra_read_chipid(); + + write_enb_clr(periph_clk_to_bit(gate), gate); +} + +static int clk_periph_enable(struct clk_hw *hw) +{ + struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw); + unsigned long flags = 0; + + spin_lock_irqsave(&periph_ref_lock, flags); + + if (!gate->enable_refcnt[gate->clk_num]++) + clk_periph_enable_locked(hw); spin_unlock_irqrestore(&periph_ref_lock, flags); @@ -91,21 +108,28 @@ static void clk_periph_disable(struct clk_hw *hw) spin_lock_irqsave(&periph_ref_lock, flags); - gate->enable_refcnt[gate->clk_num]--; - if (gate->enable_refcnt[gate->clk_num] > 0) { - spin_unlock_irqrestore(&periph_ref_lock, flags); - return; - } + WARN_ON(!gate->enable_refcnt[gate->clk_num]); + + if (--gate->enable_refcnt[gate->clk_num] == 0) + clk_periph_disable_locked(hw); + + spin_unlock_irqrestore(&periph_ref_lock, flags); +} + +static void clk_periph_disable_unused(struct clk_hw *hw) +{ + struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw); + unsigned long flags = 0; + + spin_lock_irqsave(&periph_ref_lock, flags); /* - * If peripheral is in the APB bus then read the APB bus to - * flush the write operation in apb bus. This will avoid the - * peripheral access after disabling clock + * Some clocks are duplicated and some of them are marked as critical, + * like fuse and fuse_burn for example, thus the enable_refcnt will + * be non-zero here if the "unused" duplicate is disabled by CCF. */ - if (gate->flags & TEGRA_PERIPH_ON_APB) - tegra_read_chipid(); - - write_enb_clr(periph_clk_to_bit(gate), gate); + if (!gate->enable_refcnt[gate->clk_num]) + clk_periph_disable_locked(hw); spin_unlock_irqrestore(&periph_ref_lock, flags); } @@ -114,6 +138,7 @@ const struct clk_ops tegra_clk_periph_gate_ops = { .is_enabled = clk_periph_is_enabled, .enable = clk_periph_enable, .disable = clk_periph_disable, + .disable_unused = clk_periph_disable_unused, }; struct clk *tegra_clk_register_periph_gate(const char *name, @@ -148,9 +173,6 @@ struct clk *tegra_clk_register_periph_gate(const char *name, gate->enable_refcnt = enable_refcnt; gate->regs = pregs; - if (read_enb(gate) & periph_clk_to_bit(gate)) - enable_refcnt[clk_num]++; - /* Data in .init is copied by clk_register(), so stack variable OK */ gate->hw.init = &init; diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c index 67620c7ecd9e..79ca3aa072b7 100644 --- a/drivers/clk/tegra/clk-periph.c +++ b/drivers/clk/tegra/clk-periph.c @@ -100,6 +100,15 @@ static void clk_periph_disable(struct clk_hw *hw) gate_ops->disable(gate_hw); } +static void clk_periph_disable_unused(struct clk_hw *hw) +{ + struct tegra_clk_periph *periph = to_clk_periph(hw); + const struct clk_ops *gate_ops = periph->gate_ops; + struct clk_hw *gate_hw = &periph->gate.hw; + + gate_ops->disable_unused(gate_hw); +} + static void clk_periph_restore_context(struct clk_hw *hw) { struct tegra_clk_periph *periph = to_clk_periph(hw); @@ -126,6 +135,7 @@ const struct clk_ops tegra_clk_periph_ops = { .is_enabled = clk_periph_is_enabled, .enable = clk_periph_enable, .disable = clk_periph_disable, + .disable_unused = clk_periph_disable_unused, .restore_context = clk_periph_restore_context, }; @@ -135,6 +145,7 @@ static const struct clk_ops tegra_clk_periph_nodiv_ops = { .is_enabled = clk_periph_is_enabled, .enable = clk_periph_enable, .disable = clk_periph_disable, + .disable_unused = clk_periph_disable_unused, .restore_context = clk_periph_restore_context, }; -- 2.30.2