From nobody Sat Jul 25 03:46:49 2026 Received: from mail-wr1-f48.google.com (mail-wr1-f48.google.com [209.85.221.48]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9E90E23392C for ; Sun, 19 Jul 2026 15:31:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.48 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784475107; cv=none; b=UOnmKcS0CNp9YsmNXHA0cqPAcD3pj70rPI4Zq8yGT/QLAs/20beAgeaKfqrsysQynmBSiiRzSXSDdfvbhxn+vI74Hm43mpL1A7eqWbdtx6VwWz6Eh970picXvzKtIGVqo2l6fv0Mv+QoYeJNYspMhnu+iOS8iKdRriOYERKL1xg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784475107; c=relaxed/simple; bh=7rKizl1VwNt3GDPCkBsLZAl45i1jv9HwynJC+jxYgbk=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version:Content-Type; b=LnS5zdRgsJRsKSAUhgwbEIPETCk3j/Pvg0pky+RzhhVpkDzkbPQHLHRvXLL3B8A+ki3IT6zPaVz1LMKFOut9q+b/EbMxkuhFNHJ3VlTYnH3ob0cp96p52jeV49k8B2KnAkYA8xGk/WQiUjwDPifE8onyqt9tKgRYG7BYYG93Iyg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Yfyh/hRx; arc=none smtp.client-ip=209.85.221.48 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Yfyh/hRx" Received: by mail-wr1-f48.google.com with SMTP id ffacd0b85a97d-47ddf7b09e5so5483776f8f.1 for ; Sun, 19 Jul 2026 08:31:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1784475103; x=1785079903; darn=vger.kernel.org; h=content-transfer-encoding:content-type:mime-version:message-id:date :subject:cc:to:from:from:to:cc:subject:date:message-id:reply-to :content-type; bh=RAIvFoHSot582y5nPwOQ/4JfZFN3JPm5c7b4D+Z0OhU=; b=Yfyh/hRxuwhSbwta1e4GpE6+eJ3zz4BYuPUJrBVxyo0mTO4e5IJ7y1gnAkz2w9HZQm jMGKg2LV0CE9Z3XXvQZeXBaVZY6Alne4NidzfZTRFwX1U9cAMdoeyvgP8/N8GnopILNY 7UK47oHIgdAbLnT7DuyrS/qPOIf5YTVMYwo6xVHrViU+cbJom/5Zl8nQDrWyKXOCd/Uh RdbQfrxLdAEjuLTeXzcCYYf53GfQWbY/RQFyy3DwmKepHl6MNPfK3Bdj7uNWd3iZ+PPP VOoRofYsRyC6JTycpX2YYGdZfOsHHBcaRuhlLoDXS4cbOnLXdsLS6azDvglv+ZbKtNjW QUwQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784475103; x=1785079903; h=content-transfer-encoding:content-type:mime-version:message-id:date :subject:cc:to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject :date:message-id:reply-to:content-type; bh=RAIvFoHSot582y5nPwOQ/4JfZFN3JPm5c7b4D+Z0OhU=; b=adB0hlr4adMjTrNTcpeCu3bTG77v7HFMtjTfLTGStglntcQq2LehjUaJfoXXXXkv9x vJ/me2r0v+lDxLrG1hmcjln4U2CKVcygNfdyZm1nD2Bo/6rMHXyWxHFoN3eXzsaoi/5H gr/Fi5iva4pqUdBveVtIw7HO2fSmCJ5NyW8jKMwGB1kOqz29u92N75DR1j/K+fEDgi+e CZo08cpqC/4ufwNsE0V4pOf8tOSHzALZtbbsXgryaZdCWrO0myWoB+uTp4vlIEOkdedF CPK9/tjjiCL7dY5w5/L02hUFO69z4kfsvvI0CuRzSOe/wLN3f70DoVw1LGCwzIvOUf+X 1eug== X-Forwarded-Encrypted: i=1; AHgh+Rp7H6F/N86DyK3fldG6Tu5uVNTZf+R0j3ait2ce8q3JWBKQiVj7+TlEs5j9MjJFKQfywymkrGHByBGdn2M=@vger.kernel.org X-Gm-Message-State: AOJu0Yyh3asYLUdQgJt1SPa1+T9fsiVppCVPw5GAEkztOZZUhfZxvH3B xdDa8ooefMc4I5ayld6GeWi3WFkTdGXGEGyGBS+19jKUsIzxyc26eD4s X-Gm-Gg: AfdE7ckLm5Eck+SsfMG+MQJMbphHCE2fD6MP0WYXVDWqFwQElp+lakqpIBb63954A1+ sZ3SwDxhh56+eECncYBOHLrrduqUALzdssoI0rHMcKvpZIZ8O2nJrrZDoOfXpAbm6L4AqoMPmH7 HcboyvRl+L9vp+YQ+X+GBy/fnYH/tLEJhrxGKS/vx/IsdyuTir5h/rWBn4lHowQjkWfMpk3SzyT OLmCkZsWWanA27ux4C/HSnNCuYuz14ebxOL84rp1DTBI2U7jfdrYJV4vV0z7OFXstss+OgtdQcQ gECrZxWIQvMXxcAH+M5t6pYWWhSc18Cv/RiZh+Ly+XL1DS0GUZrQFqQd+eFQ//G6pK+7D2EyQiC 3t8FL35MHSfrkYxKvyOb+lq1muHL36XsY4e4GZ0VyrFPatJxxaSnEkssblcwlYbRZL/bgtw5ITM ABTaaJBLTLwNnpsL86lfGOv+FyB/z9BMXTE5+N X-Received: by 2002:a05:6000:4b11:b0:476:7036:f854 with SMTP id ffacd0b85a97d-47f622f83ccmr11899817f8f.21.1784475102735; Sun, 19 Jul 2026 08:31:42 -0700 (PDT) Received: from localhost.localdomain ([2a0d:3344:2841:7708:a101:2b8a:f76:a00f]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-47f63edd7d3sm20420729f8f.25.2026.07.19.08.31.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 19 Jul 2026 08:31:42 -0700 (PDT) From: Aureal To: Michael Turquette , Stephen Boyd , Chen-Yu Tsai , Jernej Skrabec , Samuel Holland Cc: Brian Masney , linux-clk@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-sunxi@lists.linux.dev, linux-kernel@vger.kernel.org, =?UTF-8?q?Juan=20Manuel=20L=C3=B3pez=20Carrillo?= Subject: [PATCH] clk: sunxi-ng: div: implement set_rate_and_parent Date: Sun, 19 Jul 2026 17:31:22 +0200 Message-ID: <20260719153122.892013-1-juanmanuellopezcarrillo@gmail.com> X-Mailer: git-send-email 2.47.3 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable From: Juan Manuel L=C3=B3pez Carrillo When a rate change on a ccu_div clock also switches its parent, the clk core, in the absence of a .set_rate_and_parent op, programs the parent first and the divider second. If the new parent is faster than the old one, the clock transiently runs at new_parent_rate/old_divider between the two register writes, overshooting both the old and the requested rate and potentially violating the consumer's maximum allowed frequency. This is not theoretical. On the Allwinner A523/T527 the GPU clock is a ccu_div muxing between pll-gpu and the fixed pll-periph0 outputs: going from 600 MHz (pll-periph0-600M, M=3D1) down to 400 or 200 MHz (both derived from pll-periph0-800M) makes the Mali G57 run at 800 MHz for the window between the two writes, 33% above the vendor's maximum operating point. Observed and validated on an Orange Pi 4A (T527) with a downstream GPU devfreq setup; mainline does not yet describe GPU OPPs for this SoC, but any ccu_div consumer whose set_rate ends up crossing parents is affected. Implement .set_rate_and_parent with the same ordering rule as clk_composite_set_rate_and_parent(): if keeping the current divider while switching the mux would overshoot the requested rate, program the divider first, otherwise switch the mux first. The intermediate rate then never exceeds both the old and the new rate. Clocks using a prediv feature keep the historical mux-then-divider order: the prediv helpers look the prediv up by the current parent, which is ambiguous while both are changing. Fixes: e9b93213103f ("clk: sunxi-ng: Add divider") Signed-off-by: Juan Manuel L=C3=B3pez Carrillo Reviewed-by: Chen-Yu Tsai --- Note: this touches the same area of ccu_div.c as patch 2/4 of the pending series "clk: sun6i-rtc: Add support for Allwinner A733 SoC" v5 (<20260717-a733-rtc-v5-2-3874cc26abf7@baylibre.com>), which adds ccu_rodiv_ops right after ccu_div_ops. The conflict is trivial (context only); happy to rebase on top of it if it lands first. drivers/clk/sunxi-ng/ccu_div.c | 36 ++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/drivers/clk/sunxi-ng/ccu_div.c b/drivers/clk/sunxi-ng/ccu_div.c index 62d680ccb..9024bcd8c 100644 --- a/drivers/clk/sunxi-ng/ccu_div.c +++ b/drivers/clk/sunxi-ng/ccu_div.c @@ -130,6 +130,41 @@ static int ccu_div_set_parent(struct clk_hw *hw, u8 in= dex) return ccu_mux_helper_set_parent(&cd->common, &cd->mux, index); } =20 +static int ccu_div_set_rate_and_parent(struct clk_hw *hw, unsigned long ra= te, + unsigned long parent_rate, u8 index) +{ + struct ccu_div *cd =3D hw_to_ccu_div(hw); + + /* + * The predivider helpers look it up through the current parent, + * which is ambiguous while both the parent and the divider are + * changing, so keep the mux-then-divider order the core would + * have used for those clocks. + */ + if (cd->common.features & (CCU_FEATURE_VARIABLE_PREDIV | + CCU_FEATURE_FIXED_PREDIV | + CCU_FEATURE_ALL_PREDIV)) { + ccu_div_set_parent(hw, index); + return ccu_div_set_rate(hw, rate, parent_rate); + } + + /* + * Same ordering rule as clk_composite_set_rate_and_parent(): if + * switching the mux with the current divider would overshoot the + * requested rate, program the divider first, so the intermediate + * rate never exceeds both the old and the new rate. + */ + if (ccu_div_recalc_rate(hw, parent_rate) > rate) { + ccu_div_set_rate(hw, rate, parent_rate); + ccu_div_set_parent(hw, index); + } else { + ccu_div_set_parent(hw, index); + ccu_div_set_rate(hw, rate, parent_rate); + } + + return 0; +} + const struct clk_ops ccu_div_ops =3D { .disable =3D ccu_div_disable, .enable =3D ccu_div_enable, @@ -141,5 +176,6 @@ const struct clk_ops ccu_div_ops =3D { .determine_rate =3D ccu_div_determine_rate, .recalc_rate =3D ccu_div_recalc_rate, .set_rate =3D ccu_div_set_rate, + .set_rate_and_parent =3D ccu_div_set_rate_and_parent, }; EXPORT_SYMBOL_NS_GPL(ccu_div_ops, "SUNXI_CCU"); base-commit: 8cdeaa50eae8dad34885515f62559ee83e7e8dda --=20 2.47.3