From nobody Tue Dec 2 02:51:51 2025 Received: from mail-wr1-f54.google.com (mail-wr1-f54.google.com [209.85.221.54]) (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 ECBF534BA2D for ; Mon, 17 Nov 2025 20:57:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763413028; cv=none; b=QW0xairxeHd8vXe6LjQ7hsUEqpBowJYLlRilrFvLxHAGl1/EuYQTi8EAVCnEwY3RWcpZ81xLSXdWY/g08PRZ0UfMPGtJufAGqpNWc/wwIgFmQRfHyUxlRBzsn+FpV8wuYLKxAk0IHtd7ZOg74lrIYj22suNP47Wo+eva6ahDGuw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763413028; c=relaxed/simple; bh=YV1cT30Fs84kRwoFjvogDxZle/2IR7xAJ5WUTPhzP/k=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=g+MpTpMH1gajgpwgt2qSVuyiUgE5vh4jxYO664PxAuygwF8I6MYBC+xUMh0DqRksKw3Yordh620E81bRDPp3W4x8c5XtKKXjOEXbv5jAOppV0XhyKY/oYC/bknWU7rtvjdWzsm87oYcQ/sgpu5RK10HzF6rmYkFjd94+g7Hs7Zs= 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=Q+mw19tH; arc=none smtp.client-ip=209.85.221.54 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="Q+mw19tH" Received: by mail-wr1-f54.google.com with SMTP id ffacd0b85a97d-42bb288c17bso1419890f8f.2 for ; Mon, 17 Nov 2025 12:57:05 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1763413024; x=1764017824; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=kbV4QFu7SV5wp1gKegpOjdnpu0Me/oc7lWbeUbQn2wc=; b=Q+mw19tHesXBzDuJxHq6jY+Pty9Yc31XELIW25zLSrKpnMYQvyDDojmfJvY2x/2bP9 VZY6h7GEYHDcKU33eB5sEr0pmrDnydPrrQg0FmZlGVoQaewKb4eeuNidnJqeMcKF2QqH lhcqY5P2AZZ0U1s44vJIRPzM1Z/O5VDt3TmbzGkdotQS3r6ZCCIy5waAk0O33TkL0AEK zLRu43y6BieSSCsA8kJE9mJSzPnrqfbYyVmgOZ0tcJNhuDyFp3sPynpf9QRtgj3/Wwha AvdrjdHJkpNuR7hI456NXXWn7iVCtH8ZaWiMLUwpAVep6jwXFXVhqVQ+dt8RH1MZWKBn 1Xxg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1763413024; x=1764017824; h=content-transfer-encoding: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; bh=kbV4QFu7SV5wp1gKegpOjdnpu0Me/oc7lWbeUbQn2wc=; b=IdM8Xx8IoYZxG4T1fHhzdF4BIt6PjBwgU8CvNz5qLjVdIn2BIgNDbDgGZxzO+Wga8Q Qzdw/Sl9gtJ5uCP7iOHhdbi9Q3HVe97xiLhj70eakxc2UKcc1R9JXgo773UBJptYK0JR CAJEcwoov9XHIpWhgQM91yLJsi6+i0xPGEmXxQPSNa/GoE6+lfh5+N9oe7WQt0eIrT56 oeHzhY2e4ig54HPHHkQc+Sr0aYUHN1Cm52ey6rLA4Q9fOhXwnnTULh/Hps/0UPEVqGZt iNqFKoZpUsyjzKWay95KZBWoSCdxHw6c+aDoLqoFo1Eo0JDlFjxLEaiQHhpSjZPahT9T aoKw== X-Forwarded-Encrypted: i=1; AJvYcCWWPTQMbTwV8guH9WboAhUUbxKR5zUByZYjMtUjSFI8sHlIiFXW5/zO1cLoM/Q48WvMZF9oFxf6FiSULog=@vger.kernel.org X-Gm-Message-State: AOJu0YxsATZW6I5XILK2G84mwWIQH7vb5EO02mqp3gciVpEPWH6pSBvD sz7MyuBq18dJ9FUTyOQ+37ivgx92ZbuWrXB27UBNLijaJYxsuIh0w1Uz X-Gm-Gg: ASbGncuRUxJFgipxH07OgKNH2E++AxWqabHA2xbIkDWTg0quN7IjkBiz8HQ+hNm3LnO zzUWbNGgOE3sHo1DH/TvsQolyGFnTUYA8eiQ7cvgWGev7JhVcFFrfHlTBNRu8xpkNTuTZDENSzh 8Sbb4dOOEQMVYz5a6oeALXvVw3dohL8pVft2EdePkstVpzU38+5n6tadQnyX8l4YWPbYtc/dZrD qrOhgT2Mpf0r9E5HtHnJhQxesnqEdUkZwtCHhaNJAtv6LaekU1Ho/lNeV7s0u/zc+OD3TafeqMk oFe+wsP52AXY7ctLmtoxtcDNwgNqzdWhr6atoZHvBRBGzL+AeQFqmQlXoWzKf7UuuJ20xNLsBHm KkKZjd0V54PCmIDJUUoxWobAClDtKCIst2wg4Ya1cUwgMxCn7HnZqLIr61+zfXi7GHViYBZDb4R OrlBGfbyXIhxtOVHn7JQ5TXfXuQQtBZNhcKOY= X-Google-Smtp-Source: AGHT+IHIvfWl18rNuBEc0KEc0UirxD2tBSwWoyvLORHNoqoTK7Mk85rYZRNurUeLeUXAKFV0X8BrIQ== X-Received: by 2002:a05:6000:22c9:b0:42b:3963:d08e with SMTP id ffacd0b85a97d-42b593391a8mr14256692f8f.22.1763413023945; Mon, 17 Nov 2025 12:57:03 -0800 (PST) Received: from iku.Home ([2a06:5906:61b:2d00:c6f8:3eed:d120:33b3]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-42b53f206aasm28203490f8f.40.2025.11.17.12.57.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 17 Nov 2025 12:57:03 -0800 (PST) From: Prabhakar X-Google-Original-From: Prabhakar To: Geert Uytterhoeven , Michael Turquette , Stephen Boyd Cc: linux-renesas-soc@vger.kernel.org, linux-clk@vger.kernel.org, linux-kernel@vger.kernel.org, Prabhakar , Biju Das , Fabrizio Castro , Lad Prabhakar Subject: [PATCH v3] clk: renesas: r9a09g077: Add xSPI core and module clocks Date: Mon, 17 Nov 2025 20:56:27 +0000 Message-ID: <20251117205627.39376-1-prabhakar.mahadev-lad.rj@bp.renesas.com> X-Mailer: git-send-email 2.51.2 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Lad Prabhakar Add core clocks and module clock definitions required by the xSPI (Expanded SPI) IP on the R9A09G077 SoC. Define the new SCKCR fields FSELXSPI0/FSELXSPI1 and DIVSEL_XSPI0/1 and add two new core clocks XSPI_CLK0 and XSPI_CLK1. The xSPI block uses PCLKH as its bus clock (use as module clock parent) while the operation clock (XSPI_CLKn) is derived from PLL4. To support this arrangement provide mux/div selectors and divider tables for the supported XSPI operating rates. Add CLK_TYPE_RZT2H_FSELXSPI to implement a custom divider/mux clock where the determine_rate() callback enforces the hardware constraint: when the parent output is 600MHz only dividers 8 and 16 are valid, whereas for 800MHz operation the full divider set (6,8,16,32,64) may be used. The custom determine_rate() picks the best parent/divider pair to match the requested rate and programs the appropriate SCKCR fields. Signed-off-by: Lad Prabhakar Reviewed-by: Geert Uytterhoeven --- Note this patch was originally part of series [0] as rest of the series is already merged into linux-next, sending only this patch as v3. [0] https://lore.kernel.org/all/20251028165127.991351-1-prabhakar.mahadev-l= ad.rj@bp.renesas.com/ v2->v3: - Dropped check for 800/600 MHz in r9a09g077_cpg_fselxspi_determine_rate() instead compared divider values 3= /4. - Sorted pll4d1_div3/pll4d1_div4 definitions to be with other pll4d1 divide= rs. - Dropped u64 caset in DIV_ROUND_UP_ULL() - Hardcoded the maxdiv when no dividers are found - Rebaesed to latest -next v1->v2: - Added custom divider clock type for XSPI clocks to enforce hardware constraints on supported operating rates. --- drivers/clk/renesas/r9a09g077-cpg.c | 189 +++++++++++++++++++++++++++- 1 file changed, 188 insertions(+), 1 deletion(-) diff --git a/drivers/clk/renesas/r9a09g077-cpg.c b/drivers/clk/renesas/r9a0= 9g077-cpg.c index fb6cc94d08a1..7367a713991d 100644 --- a/drivers/clk/renesas/r9a09g077-cpg.c +++ b/drivers/clk/renesas/r9a09g077-cpg.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include =20 #include #include @@ -58,11 +60,16 @@ #define DIVSCI3ASYNC CONF_PACK(SCKCR3, 12, 2) #define DIVSCI4ASYNC CONF_PACK(SCKCR3, 14, 2) =20 +#define FSELXSPI0 CONF_PACK(SCKCR, 0, 3) +#define FSELXSPI1 CONF_PACK(SCKCR, 8, 3) +#define DIVSEL_XSPI0 CONF_PACK(SCKCR, 6, 1) +#define DIVSEL_XSPI1 CONF_PACK(SCKCR, 14, 1) #define SEL_PLL CONF_PACK(SCKCR, 22, 1) =20 enum rzt2h_clk_types { CLK_TYPE_RZT2H_DIV =3D CLK_TYPE_CUSTOM, /* Clock with divider */ CLK_TYPE_RZT2H_MUX, /* Clock with clock source selector */ + CLK_TYPE_RZT2H_FSELXSPI, /* Clock with FSELXSPIn source selector */ }; =20 #define DEF_DIV(_name, _id, _parent, _conf, _dtable) \ @@ -72,10 +79,13 @@ enum rzt2h_clk_types { DEF_TYPE(_name, _id, CLK_TYPE_RZT2H_MUX, .conf =3D _conf, \ .parent_names =3D _parent_names, .num_parents =3D _num_parents, \ .flag =3D 0, .mux_flags =3D _mux_flags) +#define DEF_DIV_FSELXSPI(_name, _id, _parent, _conf, _dtable) \ + DEF_TYPE(_name, _id, CLK_TYPE_RZT2H_FSELXSPI, .conf =3D _conf, \ + .parent =3D _parent, .dtable =3D _dtable, .flag =3D 0) =20 enum clk_ids { /* Core Clock Outputs exported to DT */ - LAST_DT_CORE_CLK =3D R9A09G077_ETCLKE, + LAST_DT_CORE_CLK =3D R9A09G077_XSPI_CLK1, =20 /* External Input Clocks */ CLK_EXTAL, @@ -91,6 +101,8 @@ enum clk_ids { CLK_SEL_CLK_PLL2, CLK_SEL_CLK_PLL4, CLK_PLL4D1, + CLK_PLL4D1_DIV3, + CLK_PLL4D1_DIV4, CLK_SCI0ASYNC, CLK_SCI1ASYNC, CLK_SCI2ASYNC, @@ -101,6 +113,8 @@ enum clk_ids { CLK_SPI1ASYNC, CLK_SPI2ASYNC, CLK_SPI3ASYNC, + CLK_DIVSELXSPI0_SCKCR, + CLK_DIVSELXSPI1_SCKCR, =20 /* Module Clocks */ MOD_CLK_BASE, @@ -112,6 +126,15 @@ static const struct clk_div_table dtable_1_2[] =3D { {0, 0}, }; =20 +static const struct clk_div_table dtable_6_8_16_32_64[] =3D { + {6, 64}, + {5, 32}, + {4, 16}, + {3, 8}, + {2, 6}, + {0, 0}, +}; + static const struct clk_div_table dtable_24_25_30_32[] =3D { {0, 32}, {1, 30}, @@ -126,6 +149,7 @@ static const char * const sel_clk_pll0[] =3D { ".loco",= ".pll0" }; static const char * const sel_clk_pll1[] =3D { ".loco", ".pll1" }; static const char * const sel_clk_pll2[] =3D { ".loco", ".pll2" }; static const char * const sel_clk_pll4[] =3D { ".loco", ".pll4" }; +static const char * const sel_clk_pll4d1_div3_div4[] =3D { ".pll4d1_div3",= ".pll4d1_div4" }; =20 static const struct cpg_core_clk r9a09g077_core_clks[] __initconst =3D { /* External Clock Inputs */ @@ -148,6 +172,8 @@ static const struct cpg_core_clk r9a09g077_core_clks[] = __initconst =3D { sel_clk_pll4, ARRAY_SIZE(sel_clk_pll4), CLK_MUX_READ_ONLY), =20 DEF_FIXED(".pll4d1", CLK_PLL4D1, CLK_SEL_CLK_PLL4, 1, 1), + DEF_FIXED(".pll4d1_div3", CLK_PLL4D1_DIV3, CLK_PLL4D1, 3, 1), + DEF_FIXED(".pll4d1_div4", CLK_PLL4D1_DIV4, CLK_PLL4D1, 4, 1), DEF_DIV(".sci0async", CLK_SCI0ASYNC, CLK_PLL4D1, DIVSCI0ASYNC, dtable_24_25_30_32), DEF_DIV(".sci1async", CLK_SCI1ASYNC, CLK_PLL4D1, DIVSCI1ASYNC, @@ -170,6 +196,13 @@ static const struct cpg_core_clk r9a09g077_core_clks[]= __initconst =3D { DEF_DIV(".spi3async", CLK_SPI3ASYNC, CLK_PLL4D1, DIVSPI3ASYNC, dtable_24_25_30_32), =20 + DEF_MUX(".divselxspi0", CLK_DIVSELXSPI0_SCKCR, DIVSEL_XSPI0, + sel_clk_pll4d1_div3_div4, + ARRAY_SIZE(sel_clk_pll4d1_div3_div4), 0), + DEF_MUX(".divselxspi1", CLK_DIVSELXSPI1_SCKCR, DIVSEL_XSPI1, + sel_clk_pll4d1_div3_div4, + ARRAY_SIZE(sel_clk_pll4d1_div3_div4), 0), + /* Core output clk */ DEF_DIV("CA55C0", R9A09G077_CLK_CA55C0, CLK_SEL_CLK_PLL0, DIVCA55C0, dtable_1_2), @@ -194,9 +227,15 @@ static const struct cpg_core_clk r9a09g077_core_clks[]= __initconst =3D { DEF_FIXED("ETCLKC", R9A09G077_ETCLKC, CLK_SEL_CLK_PLL1, 10, 1), DEF_FIXED("ETCLKD", R9A09G077_ETCLKD, CLK_SEL_CLK_PLL1, 20, 1), DEF_FIXED("ETCLKE", R9A09G077_ETCLKE, CLK_SEL_CLK_PLL1, 40, 1), + DEF_DIV_FSELXSPI("XSPI_CLK0", R9A09G077_XSPI_CLK0, CLK_DIVSELXSPI0_SCKCR, + FSELXSPI0, dtable_6_8_16_32_64), + DEF_DIV_FSELXSPI("XSPI_CLK1", R9A09G077_XSPI_CLK1, CLK_DIVSELXSPI1_SCKCR, + FSELXSPI1, dtable_6_8_16_32_64), }; =20 static const struct mssr_mod_clk r9a09g077_mod_clks[] __initconst =3D { + DEF_MOD("xspi0", 4, R9A09G077_CLK_PCLKH), + DEF_MOD("xspi1", 5, R9A09G077_CLK_PCLKH), DEF_MOD("sci0fck", 8, CLK_SCI0ASYNC), DEF_MOD("sci1fck", 9, CLK_SCI1ASYNC), DEF_MOD("sci2fck", 10, CLK_SCI2ASYNC), @@ -284,6 +323,152 @@ r9a09g077_cpg_mux_clk_register(struct device *dev, return clk_hw->clk; } =20 +static unsigned int r9a09g077_cpg_fselxspi_get_divider(struct clk_hw *hw, = unsigned long rate, + unsigned int num_parents) +{ + struct clk_fixed_factor *ff; + struct clk_hw *parent_hw; + unsigned long best_rate; + unsigned int i; + + for (i =3D 0; i < num_parents; i++) { + parent_hw =3D clk_hw_get_parent_by_index(hw, i); + best_rate =3D clk_hw_round_rate(parent_hw, rate); + + if (best_rate =3D=3D rate) { + ff =3D to_clk_fixed_factor(parent_hw); + return ff->div; + } + } + + /* No parent could provide the exact rate - this should not happen */ + return 0; +} + +static int r9a09g077_cpg_fselxspi_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_divider *divider =3D to_clk_divider(hw); + unsigned long parent_rate, best =3D 0, now; + const struct clk_div_table *clkt; + unsigned long rate =3D req->rate; + unsigned int num_parents; + unsigned int divselxspi; + unsigned int div =3D 0; + + if (!rate) + rate =3D 1; + + /* Get the number of parents for FSELXSPIn */ + num_parents =3D clk_hw_get_num_parents(req->best_parent_hw); + + for (clkt =3D divider->table; clkt->div; clkt++) { + parent_rate =3D clk_hw_round_rate(req->best_parent_hw, rate * clkt->div); + /* Skip if parent can't provide any valid rate */ + if (!parent_rate) + continue; + + /* Determine which DIVSELXSPIn divider (3 or 4) provides this parent_rat= e */ + divselxspi =3D r9a09g077_cpg_fselxspi_get_divider(req->best_parent_hw, p= arent_rate, + num_parents); + if (!divselxspi) + continue; + + /* + * DIVSELXSPIx supports 800MHz and 600MHz operation. + * When divselxspi is 4 (600MHz operation), only FSELXSPIn dividers of 8= and 16 + * are supported. Otherwise, when divselxspi is 3 (800MHz operation), + * dividers of 6, 8, 16, 32, and 64 are supported. This check ensures th= at + * FSELXSPIx is set correctly based on hardware limitations. + */ + if (divselxspi =3D=3D 4 && (clkt->div !=3D 8 && clkt->div !=3D 16)) + continue; + now =3D DIV_ROUND_UP_ULL(parent_rate, clkt->div); + if (abs(rate - now) < abs(rate - best)) { + div =3D clkt->div; + best =3D now; + req->best_parent_rate =3D parent_rate; + } + } + + if (!div) { + req->best_parent_rate =3D clk_hw_round_rate(req->best_parent_hw, 1); + divselxspi =3D r9a09g077_cpg_fselxspi_get_divider(req->best_parent_hw, + req->best_parent_rate, + num_parents); + /* default to divider 3 which will result DIVSELXSPIn =3D 800 MHz */ + if (!divselxspi) + divselxspi =3D 3; + /* + * Use the maximum divider based on the parent clock rate: + * - 64 when DIVSELXSPIx is 800 MHz (divider =3D 3) + * - 16 when DIVSELXSPIx is 600 MHz (divider =3D 4) + */ + if (divselxspi =3D=3D 3) + div =3D 64; + else + div =3D 16; + } + + req->rate =3D DIV_ROUND_UP_ULL(req->best_parent_rate, div); + + return 0; +} + +static struct clk * __init +r9a09g077_cpg_fselxspi_div_clk_register(struct device *dev, + const struct cpg_core_clk *core, + void __iomem *addr, + struct cpg_mssr_pub *pub) +{ + static struct clk_ops *xspi_div_ops; + struct clk_init_data init =3D {}; + const struct clk *parent; + const char *parent_name; + struct clk_divider *div; + struct clk_hw *hw; + int ret; + + parent =3D pub->clks[core->parent]; + if (IS_ERR(parent)) + return ERR_CAST(parent); + + div =3D devm_kzalloc(dev, sizeof(*div), GFP_KERNEL); + if (!div) + return ERR_PTR(-ENOMEM); + + if (!xspi_div_ops) { + xspi_div_ops =3D devm_kzalloc(dev, sizeof(*xspi_div_ops), GFP_KERNEL); + if (!xspi_div_ops) + return ERR_PTR(-ENOMEM); + memcpy(xspi_div_ops, &clk_divider_ops, + sizeof(const struct clk_ops)); + xspi_div_ops->determine_rate =3D r9a09g077_cpg_fselxspi_determine_rate; + } + + parent_name =3D __clk_get_name(parent); + init.name =3D core->name; + init.ops =3D xspi_div_ops; + init.flags =3D CLK_SET_RATE_PARENT; + init.parent_names =3D &parent_name; + init.num_parents =3D 1; + + div->reg =3D addr; + div->shift =3D GET_SHIFT(core->conf); + div->width =3D GET_WIDTH(core->conf); + div->flags =3D core->flag; + div->lock =3D &pub->rmw_lock; + div->hw.init =3D &init; + div->table =3D core->dtable; + + hw =3D &div->hw; + ret =3D devm_clk_hw_register(dev, hw); + if (ret) + return ERR_PTR(ret); + + return hw->clk; +} + static struct clk * __init r9a09g077_cpg_clk_register(struct device *dev, const struct cpg_core_clk *= core, const struct cpg_mssr_info *info, @@ -298,6 +483,8 @@ r9a09g077_cpg_clk_register(struct device *dev, const st= ruct cpg_core_clk *core, return r9a09g077_cpg_div_clk_register(dev, core, addr, pub); case CLK_TYPE_RZT2H_MUX: return r9a09g077_cpg_mux_clk_register(dev, core, addr, pub); + case CLK_TYPE_RZT2H_FSELXSPI: + return r9a09g077_cpg_fselxspi_div_clk_register(dev, core, addr, pub); default: return ERR_PTR(-EINVAL); } --=20 2.51.2