* Re: [PATCH 5/7] soc: aspeed: Add eSPI flash channel support
From: Markus Elfring @ 2026-06-24 12:02 UTC (permalink / raw)
To: YH Chung, linux-aspeed, openbmc, linux-arm-kernel, devicetree,
Andrew Jeffery, Conor Dooley, Joel Stanley, Krzysztof Kozlowski,
Philipp Zabel, Rob Herring, Ryan Chen
Cc: LKML, Maciej Lawniczak
In-Reply-To: <20260313-upstream_espi-v1-5-9504428e1f43@aspeedtech.com>
…
> +++ b/drivers/soc/aspeed/espi/aspeed-espi-comm.h
> @@ -0,0 +1,62 @@
…
> +/*
> + * eSPI cycle type encoding
> + *
> + * Section 5.1 Cycle Types and Packet Format,
> + * Intel eSPI Interface Base Specification, Rev 1.0, Jan. 2016.
> + */
> +#define ESPI_FLASH_READ 0x00
> +#define ESPI_FLASH_WRITE 0x01
> +#define ESPI_FLASH_ERASE 0x02
…
How do you think about to use an enumeration for such data?
https://en.wikipedia.org/wiki/Enumerated_type#C_and_syntactically_similar_languages
Regards,
Markus
^ permalink raw reply
* Re: [PATCH v6 9/9] arm64: dts: freescale: imx95: Add video codec node
From: Francesco Dolcini @ 2026-06-24 11:50 UTC (permalink / raw)
To: Nas Chung
Cc: mchehab, hverkuil, robh, krzk+dt, conor+dt, shawnguo, s.hauer,
linux-media, devicetree, linux-kernel, linux-imx,
linux-arm-kernel, jackson.lee, lafley.kim, marek.vasut
In-Reply-To: <20260624072043.238-10-nas.chung@chipsnmedia.com>
On Wed, Jun 24, 2026 at 04:20:43PM +0900, Nas Chung wrote:
> Add the Chips and Media wave633 video codec node on IMX95 SoCs.
>
> Signed-off-by: Nas Chung <nas.chung@chipsnmedia.com>
> ---
> .../boot/dts/freescale/imx95-19x19-evk.dts | 11 ++++++
> arch/arm64/boot/dts/freescale/imx95.dtsi | 36 +++++++++++++++++++
> 2 files changed, 47 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts b/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts
> index 041fd838fabb..7edd1c69966a 100644
> --- a/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts
> +++ b/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts
> @@ -76,6 +76,11 @@ linux_cma: linux,cma {
> linux,cma-default;
> reusable;
> };
> +
> + vpu_boot: memory@a0000000 {
> + reg = <0 0xa0000000 0 0x100000>;
> + no-map;
> + };
> };
>
> flexcan1_phy: can-phy0 {
> @@ -1142,3 +1147,9 @@ &tpm6 {
> pinctrl-0 = <&pinctrl_tpm6>;
> status = "okay";
> };
> +
> +&vpu {
> + memory-region = <&vpu_boot>;
> + sram = <&sram1>;
Can the `sram` node moved to imx95.dtsi or not?
Francesco
^ permalink raw reply
* Re: [PATCH] media: cedrus: fix memory leak in cedrus_init_ctrls()
From: Dan Carpenter @ 2026-06-24 11:50 UTC (permalink / raw)
To: Dawei Feng
Cc: mripard, paulk, mchehab, gregkh, wens, jernej.skrabec, samuel,
hverkuil, linux-media, linux-staging, linux-arm-kernel,
linux-sunxi, linux-kernel, jianhao.xu, zilin, stable
In-Reply-To: <20260624085920.578446-1-dawei.feng@seu.edu.cn>
On Wed, Jun 24, 2026 at 04:59:20PM +0800, Dawei Feng wrote:
> In cedrus_init_ctrls(), the V4L2 control handler is initialized before
> allocating memory for ctx->ctrls. If this allocation fails, the function
> returns -ENOMEM without freeing the previously allocated handler
> resources, leading to a memory leak.
>
> Fix this by calling v4l2_ctrl_handler_free() on the ctx->ctrls allocation
> failure path.
>
> The bug was first flagged by an experimental analysis tool we are
> developing for kernel memory-management bugs while analyzing
> v6.13-rc1. The tool is still under development and is not yet publicly
> available. Manual inspection confirms that the bug is still
> present in v7.1.1.
>
> An x86_64 allyesconfig build showed no new warnings. As we do not have an
> Allwinner SoC or board with a Cedrus VPU available to test with, no
> runtime testing was able to be performed.
>
> Fixes: 50e761516f2b ("media: platform: Add Cedrus VPU decoder driver")
> Cc: stable@vger.kernel.org
> Signed-off-by: Dawei Feng <dawei.feng@seu.edu.cn>
> ---
Looks good.
Reviewed-by: Dan Carpenter <error27@gmail.com>
regards,
dan carpenter
^ permalink raw reply
* Re: [PATCH 3/7] dt-bindings: net: rockchip-dwmac: Allow 9 clocks
From: Heiko Stübner @ 2026-06-24 11:50 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
David Wu, Maxime Coquelin, Alexandre Torgue, Yanan He
Cc: devicetree, linux-kernel, linux-arm-kernel, linux-rockchip,
netdev, linux-stm32, Yanan He
In-Reply-To: <20260624-rv1126-alientek-dlrv1126-v1-3-5aef608a3f64@gmail.com>
Am Mittwoch, 24. Juni 2026, 10:44:40 Mitteleuropäische Sommerzeit schrieb Yanan He:
> RV1126 has a separate GMAC Ethernet output clock used as the external
> PHY reference clock. This clock is described in addition to the existing
> GMAC clocks.
AS stated in the driver patch, this is the input clock for the phy
and ethernet phys are perfectly capable of handling their input
clocks. See reference in the driver patch.
Heiko
> Signed-off-by: Yanan He <grumpycat921013@gmail.com>
> ---
> Documentation/devicetree/bindings/net/rockchip-dwmac.yaml | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/Documentation/devicetree/bindings/net/rockchip-dwmac.yaml b/Documentation/devicetree/bindings/net/rockchip-dwmac.yaml
> index 80c252845349..86a7e83675ae 100644
> --- a/Documentation/devicetree/bindings/net/rockchip-dwmac.yaml
> +++ b/Documentation/devicetree/bindings/net/rockchip-dwmac.yaml
> @@ -71,7 +71,7 @@ properties:
>
> clocks:
> minItems: 4
> - maxItems: 8
> + maxItems: 9
>
> clock-names:
> contains:
>
>
^ permalink raw reply
* Re: [PATCH 4/7] net: stmmac: dwmac-rk: Enable refout clock for RGMII
From: Heiko Stübner @ 2026-06-24 11:49 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
David Wu, Maxime Coquelin, Alexandre Torgue, Yanan He
Cc: devicetree, linux-kernel, linux-arm-kernel, linux-rockchip,
netdev, linux-stm32, Yanan He
In-Reply-To: <20260624-rv1126-alientek-dlrv1126-v1-4-5aef608a3f64@gmail.com>
Hi,
Am Mittwoch, 24. Juni 2026, 10:44:41 Mitteleuropäische Sommerzeit schrieb Yanan He:
> Some Rockchip GMAC integrations use clk_mac_refout as an external PHY
> reference clock even when the MAC is configured for RGMII.
>
> RV1126 boards can route CLK_GMAC_ETHERNET_OUT to the external PHY as a
> 25 MHz reference clock. If the driver does not acquire and enable this
> clock in RGMII mode, the common clock framework may disable it as unused
> and the PHY can lose its reference clock.
>
> Enable the refout clock handling for RGMII in addition to RMII.
the clock your referencing is not limited to your rv1126 but instead
present on most (all?) Rockchip SoCs.
And it is an input clock for the phy itself, so should be handled there.
See for example
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/arm64/boot/dts/rockchip/rk3588-tiger.dtsi#n313
as a reference.
Heiko
> Signed-off-by: Yanan He <grumpycat921013@gmail.com>
> ---
> drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c | 6 ++++--
> 1 file changed, 4 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
> index 8d7042e68926..f6fdc0c5b475 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
> @@ -1112,7 +1112,8 @@ static int rk_gmac_clk_init(struct plat_stmmacenet_data *plat)
> bsp_priv->clk_enabled = false;
>
> bsp_priv->num_clks = ARRAY_SIZE(rk_clocks);
> - if (phy_iface == PHY_INTERFACE_MODE_RMII)
> + if (phy_iface == PHY_INTERFACE_MODE_RMII ||
> + phy_iface == PHY_INTERFACE_MODE_RGMII)
> bsp_priv->num_clks += ARRAY_SIZE(rk_rmii_clocks);
>
> bsp_priv->clks = devm_kcalloc(dev, bsp_priv->num_clks,
> @@ -1123,7 +1124,8 @@ static int rk_gmac_clk_init(struct plat_stmmacenet_data *plat)
> for (i = 0; i < ARRAY_SIZE(rk_clocks); i++)
> bsp_priv->clks[i].id = rk_clocks[i];
>
> - if (phy_iface == PHY_INTERFACE_MODE_RMII) {
> + if (phy_iface == PHY_INTERFACE_MODE_RMII ||
> + phy_iface == PHY_INTERFACE_MODE_RGMII) {
> for (j = 0; j < ARRAY_SIZE(rk_rmii_clocks); j++)
> bsp_priv->clks[i++].id = rk_rmii_clocks[j];
> }
>
>
^ permalink raw reply
* Re: [PATCH 15/37] drm/display: bridge-connector: allocate the connector dynamically
From: Maxime Ripard @ 2026-06-24 11:48 UTC (permalink / raw)
To: Luca Ceresoli
Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie, Simona Vetter,
Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Inki Dae, Jagan Teki,
Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam, Hui Pu,
Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel
In-Reply-To: <DJ72WOUW5FA2.G1MPCAHJQ13F@bootlin.com>
[-- Attachment #1: Type: text/plain, Size: 1856 bytes --]
On Fri, Jun 12, 2026 at 02:44:43PM +0200, Luca Ceresoli wrote:
> On Mon Jun 8, 2026 at 1:46 PM CEST, Maxime Ripard wrote:
> > On Tue, May 19, 2026 at 12:37:32PM +0200, Luca Ceresoli wrote:
> >> Currently the drm_bridge_connector has an embedded drm_connector, so their
> >> allocation lifetimes are tied to each other. This is insufficient to
> >> support DRM bridge hotplugging, which requires the connector to be added
> >> and removed dynamically at runtime multiple times based on hotplug/unplug
> >> events while the drm_bridge_connector is persistent.
> >>
> >> Moreover the drm_connector is exposed to user space and thus an ongoing
> >> operation (e.g. an ioctl) might last for an arbitrarily long time even
> >> after the hardware gets removed. This means a new connector might have to
> >> be added when the previous one is still referenced by user space.
> >>
> >> In preparation to handle hotplug, allocate the drm-connector dynamically,
> >> to allow:
> >>
> >> * creating and destroying a connector multiple times during a single
> >> drm_bridge_connector lifetime
> >> * creating a new connector even though the previous one is still in use
> >> and thus still refcounted and not yet freed
> >>
> >> This commit does not introduce the actions in the two bullets (it will
> >> happen in a later commit), it only moves to dynamic APIs for connector
> >> allocation and init.
> >>
> >> Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
> >
> > I think this patch should be split in half, with the switch to using
> > destroy first, and then the actual move to the dynamically allocated
> > connector API.
>
> Is it doable? drm_connector_dynamic_init() mandates a .destroy callback,
> drm_connector_init() forbids it.
drmm_connector_init forbids it. drm_connector_init mandates it.
Maxime
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]
^ permalink raw reply
* Re: [PATCH 06/37] drm/display: bridge-connector: use a drm_bridge_connector internally, not a drm_connector
From: Maxime Ripard @ 2026-06-24 11:47 UTC (permalink / raw)
To: Luca Ceresoli
Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie, Simona Vetter,
Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Inki Dae, Jagan Teki,
Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam, Hui Pu,
Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel
In-Reply-To: <DJ736L2DR8H8.35296DRNZ1OW@bootlin.com>
[-- Attachment #1: Type: text/plain, Size: 1728 bytes --]
On Fri, Jun 12, 2026 at 02:57:39PM +0200, Luca Ceresoli wrote:
> On Mon Jun 8, 2026 at 1:41 PM CEST, Maxime Ripard wrote:
> > On Tue, May 19, 2026 at 12:37:23PM +0200, Luca Ceresoli wrote:
> >> Currently drm_bridge_connector_init() always returns the added connector or
> >> errors out. When adding bridge hotplug the bridge-connector can be
> >> successfully initialized without creating a connector, which can be added
> >> later when the pipeline will be complete.
> >>
> >> For this the internal function drm_bridge_connector_add_connector() must be
> >> able to return a valid drm_bridge_connector even without any drm_connector.
> >>
> >> In preparation to support bridge hotplug, change its return value to be the
> >> same drm_bridge_connector pointer it gets as input, or a PTR_ERR.
> >>
> >> No functional changes, just changing an internal API.
> >>
> >> Note the return value could now become an int (0 or negative error) because
> >> returning the same value received as input does not carry any added
> >> value. However this would be change a lot of lines, so leave such change as
> >> a future cleanup.
> >
> > You just created that function and changed "a lot of lines" already, so
> > I'm not sure that argument holds.
>
> Do you refer to the previous patch?
>
> My comment is more about the following patches. It means I separated
> changes moving code to a subfunction from changes to the the return value
> in separate patches, so that each patch is trivial to review for
> correctness.
>
> Makes sense?
What confused me is that I took it as "I'm not going to do that work
(yet?)". If you do it later on, I'd drop the "future cleanup" part, or
rephrase it.
Maxime
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]
^ permalink raw reply
* Re: [PATCH v4 5/5] clk: rockchip: rk3588: add GATE_GRF clocks for I2S MCLK output to IO
From: Heiko Stübner @ 2026-06-24 11:45 UTC (permalink / raw)
To: Daniele Briguglio, Diederik de Haas, Michael Turquette,
Stephen Boyd, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Diederik de Haas
Cc: Nicolas Frattaroli, linux-clk, devicetree, linux-arm-kernel,
linux-rockchip, linux-kernel, Ricardo Pardini
In-Reply-To: <DJGM4D19H31O.4I5N79CG5Z8C@cknow-tech.com>
Am Dienstag, 23. Juni 2026, 19:42:05 Mitteleuropäische Sommerzeit schrieb Diederik de Haas:
> Hi,
>
> On Tue Jun 23, 2026 at 4:10 PM CEST, Daniele Briguglio wrote:
> >> So IIUC that means I'd be testing both variants.
> >
> > Right, that covers both: the mux path and the consumer path. Looking
> > forward to the results.
>
> Analog audio works on both. Plus with _TO_IO and LTS without in their
> respective DTS's. So I guess CLK_IGNORE_UNUSED works.
> Whether it's a good/right fix, I'll leave up to others.
It is the correct fix, as it returns the original way things worked for
boards not activly handling that clock.
So while boards should do that, this makes the clock-addition
backwards compatible.
Care to send a patch for that change? :-)
Heiko
^ permalink raw reply
* [PATCH] arm64: dts: mediatek: mt8395-radxa-nio-12l: Enable I2C3 on 40-pin header
From: Ricardo Pardini via B4 Relay @ 2026-06-24 11:41 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno
Cc: devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
Ricardo Pardini
From: Ricardo Pardini <ricardo@pardini.net>
I2C3 (SDA3/SCL3 on GPIO14/GPIO15) is routed to the 40-pin GPIO header,
exposed on the blue-colored pins 27 (SCL3) and 28 (SDA3). Enable the
controller, add the corresponding pin configuration in the pinctrl node
so users can use external I2C devices.
Signed-off-by: Ricardo Pardini <ricardo@pardini.net>
---
The Radxa NIO 12L exposes I2C3 (SDA3/SCL3, GPIO14/GPIO15) on its 40-pin
GPIO header, on the blue-coloured pins 27 (SCL3) and 28 (SDA3).
Enable the I2C3 controller, add the matching pinctrl configuration and run
the bus at 400 kHz, matching the other I2C buses already enabled on this
board.
Tested using a SD1306 I2C OLED display.
---
arch/arm64/boot/dts/mediatek/mt8395-radxa-nio-12l.dts | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/arch/arm64/boot/dts/mediatek/mt8395-radxa-nio-12l.dts b/arch/arm64/boot/dts/mediatek/mt8395-radxa-nio-12l.dts
index bf91305e8e4a5..03a2f2e746475 100644
--- a/arch/arm64/boot/dts/mediatek/mt8395-radxa-nio-12l.dts
+++ b/arch/arm64/boot/dts/mediatek/mt8395-radxa-nio-12l.dts
@@ -371,6 +371,14 @@ it5205_sbu_mux: endpoint {
};
};
+/* Exposed on 40-pin header (blue-colored pins 27:SCL3 28:SDA3) */
+&i2c3 {
+ clock-frequency = <400000>;
+ pinctrl-0 = <&i2c3_pins>;
+ pinctrl-names = "default";
+ status = "okay";
+};
+
&i2c4 {
clock-frequency = <400000>;
pinctrl-0 = <&i2c4_pins>;
@@ -789,6 +797,16 @@ pins-bus {
};
};
+ i2c3_pins: i2c3-pins {
+ pins-bus {
+ pinmux = <PINMUX_GPIO14__FUNC_SDA3>,
+ <PINMUX_GPIO15__FUNC_SCL3>;
+ bias-pull-up = <1000>;
+ drive-strength = <6>;
+ drive-strength-microamp = <1000>;
+ };
+ };
+
i2c4_pins: i2c4-pins {
pins-bus {
pinmux = <PINMUX_GPIO16__FUNC_SDA4>,
---
base-commit: 8cd9520d35a6c38db6567e97dd93b1f11f185dc6
change-id: 20260624-nio-12l-add-i2c-40-pin-19e0482fd835
Best regards,
--
Ricardo Pardini <ricardo@pardini.net>
^ permalink raw reply related
* Re: [PATCH 05/37] drm/display: bridge-connector: split code creating the connector to a subfunction
From: Maxime Ripard @ 2026-06-24 11:41 UTC (permalink / raw)
To: Luca Ceresoli
Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie, Simona Vetter,
Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Inki Dae, Jagan Teki,
Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam, Hui Pu,
Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel
In-Reply-To: <DJ735MTNMYD1.3LPNV404V8PIU@bootlin.com>
[-- Attachment #1: Type: text/plain, Size: 2110 bytes --]
Hi,x
On Fri, Jun 12, 2026 at 02:56:24PM +0200, Luca Ceresoli wrote:
> On Mon Jun 8, 2026 at 1:40 PM CEST, Maxime Ripard wrote:
> > On Tue, May 19, 2026 at 12:37:22PM +0200, Luca Ceresoli wrote:
> >> In preparation to introduce bridge hotplug, split out from
> >> drm_bridge_connector_init() the code adding the drm_connector into a
> >> dedicated function. This will be needed to be able to add (and re-add) the
> >> connector from different code paths.
> >
> > Same story here, explaining what you need later on that calls for that
> > change would be nice.
>
> Here's a more verbose version:
>
> Currently drm_bridge_connector_init() does two things:
>
> * allocate and initialize the drm_bridge_connector
> (which embeds a drm_connector)
> * initialize and register the embedded drm_connector
>
> For bridge hotplug we need to separate these two actions:
>
> * the drm_connector needs to be added and removed at any time based on
> hotplug events
> * the drm_bridge_connector is designated to create and remove the
> drm_connector, so it must be persistent for the card lifetime
>
> As the lifetimes of drm_bridge_connector and drm_connector become
> different, we need to create them in different moments.
>
> In preparation to support that, split out from
> drm_bridge_connector_init() the code adding the drm_connector into a
> dedicated function. No functional changes, just moving code around for
> now. A future commit will make the drm_connector be created based on
> hotplug events.
>
> Looks good?
The message itself, yes, thanks.
However, I have questions now :)
Do we really expect drm_bridge_connector to stick around when a bridge
gets unplugged? If so, how does it cope with having, say, an HDMI
connector, and then swapping out the hotplugged part for an LVDS one?
Does the HDMI connector sticks around indefinitely?
*Especially* if we're using overlays for this, I'd expect everything
after the first hotplugged bridge to be destroyed, no?
Maxime
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]
^ permalink raw reply
* [PATCH v9 11/12] clk: realtek: Add RTD1625-ISO clock controller driver
From: Yu-Chun Lin @ 2026-06-24 11:29 UTC (permalink / raw)
To: mturquette, sboyd, robh, krzk+dt, conor+dt, p.zabel, cylee12,
afaerber, jyanchou, bmasney
Cc: devicetree, linux-clk, linux-kernel, linux-arm-kernel,
linux-realtek-soc, james.tai, cy.huang, stanley_chang,
eleanor.lin
In-Reply-To: <20260624112940.3475605-1-eleanor.lin@realtek.com>
From: Cheng-Yu Lee <cylee12@realtek.com>
Add support for the ISO (Isolation) domain clock controller on the Realtek
RTD1625 SoC. This controller manages clocks in the always-on power domain,
ensuring essential services remain functional even when the main system
power is gated.
Because the reset controller shares the same register space with this ISO
clock controller, this driver also acts as the parent device and registers
the reset controller as an auxiliary device on the auxiliary bus.
Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
Co-developed-by: Yu-Chun Lin <eleanor.lin@realtek.com>
Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
---
Changes in v9:
- Remove reset-related code to patch 4.
---
drivers/clk/realtek/Makefile | 1 +
drivers/clk/realtek/clk-rtd1625-iso.c | 151 ++++++++++++++++++++++++++
2 files changed, 152 insertions(+)
create mode 100644 drivers/clk/realtek/clk-rtd1625-iso.c
diff --git a/drivers/clk/realtek/Makefile b/drivers/clk/realtek/Makefile
index 15b9eec74e36..fbf8cb0db2f0 100644
--- a/drivers/clk/realtek/Makefile
+++ b/drivers/clk/realtek/Makefile
@@ -9,3 +9,4 @@ clk-rtk-y += clk-regmap-mux.o
clk-rtk-$(CONFIG_RTK_CLK_PLL_MMC) += clk-pll-mmc.o
obj-$(CONFIG_CLK_RTD1625) += clk-rtd1625-crt.o
+obj-$(CONFIG_CLK_RTD1625) += clk-rtd1625-iso.o
diff --git a/drivers/clk/realtek/clk-rtd1625-iso.c b/drivers/clk/realtek/clk-rtd1625-iso.c
new file mode 100644
index 000000000000..52c4a4304284
--- /dev/null
+++ b/drivers/clk/realtek/clk-rtd1625-iso.c
@@ -0,0 +1,151 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2024-2026 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include <dt-bindings/clock/realtek,rtd1625-clk.h>
+#include <linux/array_size.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include "clk-regmap-gate.h"
+
+#define RTD1625_ISO_CLK_MAX 19
+#define RTD1625_ISO_RSTN_MAX 29
+#define RTD1625_ISO_S_CLK_MAX 5
+#define RTD1625_ISO_S_RSTN_MAX 5
+
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_usb_p4, 0, 0x4, 0, 0);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_usb_p3, 0, 0x4, 1, 0);
+static CLK_REGMAP_GATE(clk_en_misc_cec0, "clk_en_misc", 0, 0x4, 2, 0);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_cbusrx_sys, 0, 0x4, 3, 0);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_cbustx_sys, 0, 0x4, 4, 0);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_cbus_sys, 0, 0x4, 5, 0);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_cbus_osc, 0, 0x4, 6, 0);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_i2c0, 0, 0x4, 9, 0);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_i2c1, 0, 0x4, 10, 0);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_etn_250m, 0, 0x4, 11, 0);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_etn_sys, 0, 0x4, 12, 0);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_usb_drd, 0, 0x4, 13, 0);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_usb_host, 0, 0x4, 14, 0);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_usb_u3_host, 0, 0x4, 15, 0);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_usb, 0, 0x4, 16, 0);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_vtc, 0, 0x4, 17, 0);
+static CLK_REGMAP_GATE(clk_en_misc_vfd, "clk_en_misc", 0, 0x4, 18, 0);
+
+static struct clk_regmap * const rtd1625_clk_regmap_list[] = {
+ &clk_en_usb_p4.clkr,
+ &clk_en_usb_p3.clkr,
+ &clk_en_misc_cec0.clkr,
+ &clk_en_cbusrx_sys.clkr,
+ &clk_en_cbustx_sys.clkr,
+ &clk_en_cbus_sys.clkr,
+ &clk_en_cbus_osc.clkr,
+ &clk_en_i2c0.clkr,
+ &clk_en_i2c1.clkr,
+ &clk_en_etn_250m.clkr,
+ &clk_en_etn_sys.clkr,
+ &clk_en_usb_drd.clkr,
+ &clk_en_usb_host.clkr,
+ &clk_en_usb_u3_host.clkr,
+ &clk_en_usb.clkr,
+ &clk_en_vtc.clkr,
+ &clk_en_misc_vfd.clkr,
+};
+
+static struct clk_hw_onecell_data rtd1625_iso_clk_data = {
+ .num = RTD1625_ISO_CLK_MAX,
+ .hws = {
+ [RTD1625_ISO_CLK_EN_USB_P4] = &__clk_regmap_gate_hw(&clk_en_usb_p4),
+ [RTD1625_ISO_CLK_EN_USB_P3] = &__clk_regmap_gate_hw(&clk_en_usb_p3),
+ [RTD1625_ISO_CLK_EN_MISC_CEC0] = &__clk_regmap_gate_hw(&clk_en_misc_cec0),
+ [RTD1625_ISO_CLK_EN_CBUSRX_SYS] = &__clk_regmap_gate_hw(&clk_en_cbusrx_sys),
+ [RTD1625_ISO_CLK_EN_CBUSTX_SYS] = &__clk_regmap_gate_hw(&clk_en_cbustx_sys),
+ [RTD1625_ISO_CLK_EN_CBUS_SYS] = &__clk_regmap_gate_hw(&clk_en_cbus_sys),
+ [RTD1625_ISO_CLK_EN_CBUS_OSC] = &__clk_regmap_gate_hw(&clk_en_cbus_osc),
+ [RTD1625_ISO_CLK_EN_I2C0] = &__clk_regmap_gate_hw(&clk_en_i2c0),
+ [RTD1625_ISO_CLK_EN_I2C1] = &__clk_regmap_gate_hw(&clk_en_i2c1),
+ [RTD1625_ISO_CLK_EN_ETN_250M] = &__clk_regmap_gate_hw(&clk_en_etn_250m),
+ [RTD1625_ISO_CLK_EN_ETN_SYS] = &__clk_regmap_gate_hw(&clk_en_etn_sys),
+ [RTD1625_ISO_CLK_EN_USB_DRD] = &__clk_regmap_gate_hw(&clk_en_usb_drd),
+ [RTD1625_ISO_CLK_EN_USB_HOST] = &__clk_regmap_gate_hw(&clk_en_usb_host),
+ [RTD1625_ISO_CLK_EN_USB_U3_HOST] = &__clk_regmap_gate_hw(&clk_en_usb_u3_host),
+ [RTD1625_ISO_CLK_EN_USB] = &__clk_regmap_gate_hw(&clk_en_usb),
+ [RTD1625_ISO_CLK_EN_VTC] = &__clk_regmap_gate_hw(&clk_en_vtc),
+ [RTD1625_ISO_CLK_EN_MISC_VFD] = &__clk_regmap_gate_hw(&clk_en_misc_vfd),
+ },
+};
+
+static const struct rtk_clk_desc rtd1625_iso_desc = {
+ .clk_data = &rtd1625_iso_clk_data,
+ .clks = rtd1625_clk_regmap_list,
+ .num_clks = ARRAY_SIZE(rtd1625_clk_regmap_list),
+};
+
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_irda, 0, 0x4, 6, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_ur10, 0, 0x4, 8, 1);
+
+static struct clk_regmap * const rtd1625_iso_s_clk_regmap_list[] = {
+ &clk_en_irda.clkr,
+ &clk_en_ur10.clkr,
+};
+
+static struct clk_hw_onecell_data rtd1625_iso_s_clk_data = {
+ .num = RTD1625_ISO_S_CLK_MAX,
+ .hws = {
+ [RTD1625_ISO_S_CLK_EN_IRDA] = &__clk_regmap_gate_hw(&clk_en_irda),
+ [RTD1625_ISO_S_CLK_EN_UR10] = &__clk_regmap_gate_hw(&clk_en_ur10),
+ },
+};
+
+static const struct rtk_clk_desc rtd1625_iso_s_desc = {
+ .clk_data = &rtd1625_iso_s_clk_data,
+ .clks = rtd1625_iso_s_clk_regmap_list,
+ .num_clks = ARRAY_SIZE(rtd1625_iso_s_clk_regmap_list),
+};
+
+static int rtd1625_iso_probe(struct platform_device *pdev)
+{
+ const struct rtk_clk_desc *desc;
+ const char *aux_name = NULL;
+
+ desc = of_device_get_match_data(&pdev->dev);
+ if (!desc)
+ return -EINVAL;
+
+ if (of_device_is_compatible(pdev->dev.of_node, "realtek,rtd1625-iso-clk"))
+ aux_name = "iso_rst";
+ else
+ aux_name = "iso_s_rst";
+
+ return rtk_clk_probe(pdev, desc, aux_name);
+}
+
+static const struct of_device_id rtd1625_iso_match[] = {
+ {.compatible = "realtek,rtd1625-iso-clk", .data = &rtd1625_iso_desc},
+ {.compatible = "realtek,rtd1625-iso-s-clk", .data = &rtd1625_iso_s_desc},
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rtd1625_iso_match);
+
+static struct platform_driver rtd1625_iso_driver = {
+ .probe = rtd1625_iso_probe,
+ .driver = {
+ .name = "rtk-rtd1625-iso-clk",
+ .of_match_table = rtd1625_iso_match,
+ .suppress_bind_attrs = true,
+ },
+};
+
+static int __init rtd1625_iso_init(void)
+{
+ return platform_driver_register(&rtd1625_iso_driver);
+}
+subsys_initcall(rtd1625_iso_init);
+
+MODULE_DESCRIPTION("Realtek RTD1625 ISO Clock Controller Driver");
+MODULE_AUTHOR("Cheng-Yu Lee <cylee12@realtek.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("REALTEK_CLK");
--
2.43.0
^ permalink raw reply related
* [PATCH v9 09/12] clk: realtek: Add support for MMC-tuned PLL clocks
From: Yu-Chun Lin @ 2026-06-24 11:29 UTC (permalink / raw)
To: mturquette, sboyd, robh, krzk+dt, conor+dt, p.zabel, cylee12,
afaerber, jyanchou, bmasney
Cc: devicetree, linux-clk, linux-kernel, linux-arm-kernel,
linux-realtek-soc, james.tai, cy.huang, stanley_chang,
eleanor.lin
In-Reply-To: <20260624112940.3475605-1-eleanor.lin@realtek.com>
From: Cheng-Yu Lee <cylee12@realtek.com>
Add clk_pll_mmc_ops for enable/disable, prepare, rate control, and status
operations on MMC PLL clocks.
Also add clk_pll_mmc_phase_ops to support phase get/set operations.
Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
Co-developed-by: Jyan Chou <jyanchou@realtek.com>
Signed-off-by: Jyan Chou <jyanchou@realtek.com>
Co-developed-by: Yu-Chun Lin <eleanor.lin@realtek.com>
Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
---
Changes in v9:
- Fix potential integer overflow on 32-bit architectures in
'clk_pll_mmc_determine_rate()' by casting to 'u64' and using
'DIV_ROUND_CLOSEST_ULL()'.
- Add comments in 'clk_pll_mmc_set_rate()' to clarify why 'rate' and
'parent_rate' are intentionally unused.
MAINTAINERS | 8 +
drivers/clk/realtek/Kconfig | 3 +
drivers/clk/realtek/Makefile | 2 +
drivers/clk/realtek/clk-pll-mmc.c | 430 ++++++++++++++++++++++++++++++
drivers/clk/realtek/clk-pll.h | 13 +
5 files changed, 456 insertions(+)
create mode 100644 drivers/clk/realtek/clk-pll-mmc.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 9cdcb333b68f..071110948cc8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22680,6 +22680,14 @@ F: drivers/reset/realtek/*
F: include/dt-bindings/clock/realtek*
F: include/dt-bindings/reset/realtek*
+REALTEK SOC PLL CLOCK FOR MMC SUPPORT
+M: Cheng-Yu Lee <cylee12@realtek.com>
+M: Jyan Chou <jyanchou@realtek.com>
+M: Yu-Chun Lin <eleanor.lin@realtek.com>
+L: linux-clk@vger.kernel.org
+S: Supported
+F: drivers/clk/realtek/clk-pll-mmc.c
+
REALTEK SPI-NAND
M: Chris Packham <chris.packham@alliedtelesis.co.nz>
S: Maintained
diff --git a/drivers/clk/realtek/Kconfig b/drivers/clk/realtek/Kconfig
index ed97531e321d..2ff780581ae0 100644
--- a/drivers/clk/realtek/Kconfig
+++ b/drivers/clk/realtek/Kconfig
@@ -27,4 +27,7 @@ config RTK_CLK_COMMON
multiple Realtek clock implementations, and include integration
with reset controllers where required.
+config RTK_CLK_PLL_MMC
+ bool
+
endif
diff --git a/drivers/clk/realtek/Makefile b/drivers/clk/realtek/Makefile
index 3b014240a211..97447e92bc35 100644
--- a/drivers/clk/realtek/Makefile
+++ b/drivers/clk/realtek/Makefile
@@ -6,3 +6,5 @@ clk-rtk-y += clk-pll.o
clk-rtk-y += freq_table.o
clk-rtk-y += clk-regmap-gate.o
clk-rtk-y += clk-regmap-mux.o
+
+clk-rtk-$(CONFIG_RTK_CLK_PLL_MMC) += clk-pll-mmc.o
diff --git a/drivers/clk/realtek/clk-pll-mmc.c b/drivers/clk/realtek/clk-pll-mmc.c
new file mode 100644
index 000000000000..eefb0da04823
--- /dev/null
+++ b/drivers/clk/realtek/clk-pll-mmc.c
@@ -0,0 +1,430 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2021-2026 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include <linux/bits.h>
+#include <linux/export.h>
+#include <linux/math64.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include "clk-pll.h"
+
+#define PLL_MMC_VAL_MIN 1
+#define PLL_MMC_VAL_MAX 255
+
+#define PLL_EMMC1_OFFSET 0x0
+#define PLL_EMMC2_OFFSET 0x4
+#define PLL_EMMC3_OFFSET 0x8
+#define PLL_EMMC4_OFFSET 0xc
+#define PLL_SSC_DIG_EMMC1_OFFSET 0x0
+#define PLL_SSC_DIG_EMMC3_OFFSET 0xc
+#define PLL_SSC_DIG_EMMC4_OFFSET 0x10
+
+#define PLL_MMC_SSC_DIV_N_VAL 0x1b
+
+#define PLL_PHRT0_MASK BIT(0)
+#define PLL_PHSEL_MASK GENMASK(4, 0)
+#define PLL_SSCPLL_RS_MASK GENMASK(12, 10)
+#define PLL_SSCPLL_ICP_MASK GENMASK(9, 5)
+#define PLL_SSC_DIV_EXT_F_MASK GENMASK(25, 13)
+#define PLL_PI_IBSELH_MASK GENMASK(28, 27)
+#define PLL_SSC_DIV_N_MASK GENMASK(23, 16)
+#define PLL_NCODE_SSC_EMMC_MASK GENMASK(20, 13)
+#define PLL_FCODE_SSC_EMMC_MASK GENMASK(12, 0)
+#define PLL_GRAN_EST_EM_MC_MASK GENMASK(20, 0)
+#define PLL_EN_SSC_EMMC_MASK BIT(0)
+#define PLL_FLAG_INITAL_EMMC_MASK BIT(8)
+
+#define PLL_PHRT0_SHIFT 1
+#define PLL_SSCPLL_RS_SHIFT 10
+#define PLL_SSCPLL_ICP_SHIFT 5
+#define PLL_SSC_DIV_EXT_F_SHIFT 13
+#define PLL_PI_IBSELH_SHIFT 27
+#define PLL_SSC_DIV_N_SHIFT 16
+#define PLL_NCODE_SSC_EMMC_SHIFT 13
+#define PLL_FLAG_INITAL_EMMC_SHIFT 8
+
+#define CYCLE_DEGREES 360
+#define PHASE_STEPS 32
+#define PHASE_SCALE_FACTOR 1125
+
+static inline struct clk_pll_mmc *to_clk_pll_mmc(struct clk_hw *hw)
+{
+ struct clk_regmap *clkr = to_clk_regmap(hw);
+
+ return container_of(clkr, struct clk_pll_mmc, clkr);
+}
+
+static inline int get_phrt0(struct clk_pll_mmc *clkm, u32 *val)
+{
+ u32 reg;
+ int ret;
+
+ ret = regmap_read(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC1_OFFSET, ®);
+ if (ret)
+ return ret;
+
+ *val = (reg >> PLL_PHRT0_SHIFT) & PLL_PHRT0_MASK;
+
+ return 0;
+}
+
+static inline int set_phrt0(struct clk_pll_mmc *clkm, u32 val)
+{
+ return regmap_update_bits(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC1_OFFSET,
+ PLL_PHRT0_MASK << PLL_PHRT0_SHIFT, val << PLL_PHRT0_SHIFT);
+}
+
+static inline int get_phsel(struct clk_pll_mmc *clkm, int id, u32 *val)
+{
+ u32 sft = id ? 8 : 3;
+ u32 raw_val;
+ int ret;
+
+ ret = regmap_read(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC1_OFFSET, &raw_val);
+ if (ret)
+ return ret;
+
+ *val = (raw_val >> sft) & PLL_PHSEL_MASK;
+
+ return 0;
+}
+
+static inline int set_phsel(struct clk_pll_mmc *clkm, int id, u32 val)
+{
+ u32 sft = id ? 8 : 3;
+
+ return regmap_update_bits(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC1_OFFSET,
+ PLL_PHSEL_MASK << sft, val << sft);
+}
+
+static inline int set_sscpll_rs(struct clk_pll_mmc *clkm, u32 val)
+{
+ return regmap_update_bits(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC2_OFFSET,
+ PLL_SSCPLL_RS_MASK, val << PLL_SSCPLL_RS_SHIFT);
+}
+
+static inline int set_sscpll_icp(struct clk_pll_mmc *clkm, u32 val)
+{
+ return regmap_update_bits(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC2_OFFSET,
+ PLL_SSCPLL_ICP_MASK, val << PLL_SSCPLL_ICP_SHIFT);
+}
+
+static inline int get_ssc_div_ext_f(struct clk_pll_mmc *clkm, u32 *val)
+{
+ u32 raw_val;
+ int ret;
+
+ ret = regmap_read(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC2_OFFSET, &raw_val);
+ if (ret)
+ return ret;
+
+ *val = (raw_val & PLL_SSC_DIV_EXT_F_MASK) >> PLL_SSC_DIV_EXT_F_SHIFT;
+
+ return 0;
+}
+
+static inline int set_ssc_div_ext_f(struct clk_pll_mmc *clkm, u32 val)
+{
+ return regmap_update_bits(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC2_OFFSET,
+ PLL_SSC_DIV_EXT_F_MASK,
+ val << PLL_SSC_DIV_EXT_F_SHIFT);
+}
+
+static inline int set_pi_ibselh(struct clk_pll_mmc *clkm, u32 val)
+{
+ return regmap_update_bits(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC2_OFFSET,
+ PLL_PI_IBSELH_MASK, val << PLL_PI_IBSELH_SHIFT);
+}
+
+static inline int set_ssc_div_n(struct clk_pll_mmc *clkm, u32 val)
+{
+ return regmap_update_bits(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC3_OFFSET,
+ PLL_SSC_DIV_N_MASK, val << PLL_SSC_DIV_N_SHIFT);
+}
+
+static inline int get_ssc_div_n(struct clk_pll_mmc *clkm, u32 *val)
+{
+ int ret;
+ u32 raw_val;
+
+ ret = regmap_read(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC3_OFFSET, &raw_val);
+ if (ret)
+ return ret;
+
+ *val = (raw_val & PLL_SSC_DIV_N_MASK) >> PLL_SSC_DIV_N_SHIFT;
+
+ return 0;
+}
+
+static inline int set_pow_ctl(struct clk_pll_mmc *clkm, u32 val)
+{
+ return regmap_write(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC4_OFFSET, val);
+}
+
+static inline int get_pow_ctl(struct clk_pll_mmc *clkm, u32 *val)
+{
+ int ret;
+ u32 raw_val;
+
+ ret = regmap_read(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC4_OFFSET, &raw_val);
+ if (ret)
+ return ret;
+
+ *val = raw_val;
+
+ return 0;
+}
+
+static int clk_pll_mmc_phase_set_phase(struct clk_hw *hw, int degrees)
+{
+ struct clk_hw *hwp = clk_hw_get_parent(hw);
+ struct clk_pll_mmc *clkm;
+ int phase_id;
+ int ret;
+ u32 val;
+
+ if (!hwp)
+ return -ENOENT;
+
+ clkm = to_clk_pll_mmc(hwp);
+ phase_id = (hw == &clkm->phase0_hw) ? 0 : 1;
+ val = DIV_ROUND_CLOSEST(degrees * 100, PHASE_SCALE_FACTOR);
+ ret = set_phsel(clkm, phase_id, val);
+ if (ret)
+ return ret;
+
+ usleep_range(10, 20);
+
+ return 0;
+}
+
+static int clk_pll_mmc_phase_get_phase(struct clk_hw *hw)
+{
+ struct clk_hw *hwp;
+ struct clk_pll_mmc *clkm;
+ int phase_id;
+ int ret;
+ u32 val;
+
+ hwp = clk_hw_get_parent(hw);
+ if (!hwp)
+ return -ENOENT;
+
+ clkm = to_clk_pll_mmc(hwp);
+ phase_id = (hw == &clkm->phase0_hw) ? 0 : 1;
+ ret = get_phsel(clkm, phase_id, &val);
+ if (ret)
+ return ret;
+
+ val = DIV_ROUND_CLOSEST(val * CYCLE_DEGREES, PHASE_STEPS);
+
+ return val;
+}
+
+const struct clk_ops rtk_clk_pll_mmc_phase_ops = {
+ .set_phase = clk_pll_mmc_phase_set_phase,
+ .get_phase = clk_pll_mmc_phase_get_phase,
+};
+EXPORT_SYMBOL_NS_GPL(rtk_clk_pll_mmc_phase_ops, "REALTEK_CLK");
+
+static int clk_pll_mmc_prepare(struct clk_hw *hw)
+{
+ struct clk_pll_mmc *clkm = to_clk_pll_mmc(hw);
+
+ return set_pow_ctl(clkm, 7);
+}
+
+static void clk_pll_mmc_unprepare(struct clk_hw *hw)
+{
+ struct clk_pll_mmc *clkm = to_clk_pll_mmc(hw);
+
+ set_pow_ctl(clkm, 0);
+}
+
+static int clk_pll_mmc_is_prepared(struct clk_hw *hw)
+{
+ struct clk_pll_mmc *clkm = to_clk_pll_mmc(hw);
+ u32 val;
+ int ret;
+
+ ret = get_pow_ctl(clkm, &val);
+ if (ret)
+ return 1;
+
+ return val != 0x0;
+}
+
+static int clk_pll_mmc_enable(struct clk_hw *hw)
+{
+ struct clk_pll_mmc *clkm = to_clk_pll_mmc(hw);
+ int ret;
+
+ ret = set_phrt0(clkm, 1);
+ if (ret)
+ return ret;
+
+ udelay(10);
+
+ return 0;
+}
+
+static void clk_pll_mmc_disable(struct clk_hw *hw)
+{
+ struct clk_pll_mmc *clkm = to_clk_pll_mmc(hw);
+
+ set_phrt0(clkm, 0);
+ udelay(10);
+}
+
+static int clk_pll_mmc_is_enabled(struct clk_hw *hw)
+{
+ struct clk_pll_mmc *clkm = to_clk_pll_mmc(hw);
+ u32 val;
+ int ret;
+
+ ret = get_phrt0(clkm, &val);
+ if (ret)
+ return 1;
+
+ return val == 0x1;
+}
+
+static unsigned long clk_pll_mmc_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ struct clk_pll_mmc *clkm = to_clk_pll_mmc(hw);
+ u32 val, ext_f;
+ u64 rate, base;
+ int ret;
+
+ ret = get_ssc_div_n(clkm, &val);
+ if (ret)
+ return 0;
+
+ ret = get_ssc_div_ext_f(clkm, &ext_f);
+ if (ret)
+ return 0;
+
+ base = parent_rate / 4;
+ rate = base * (val + 2);
+ rate += div_u64(base * ext_f, 8192);
+
+ return rate;
+}
+
+static int clk_pll_mmc_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
+{
+ u32 val;
+
+ if (!req->best_parent_rate)
+ return -EINVAL;
+
+ val = DIV_ROUND_CLOSEST_ULL((u64)req->rate * 4, req->best_parent_rate);
+ val = clamp_t(u32, val, PLL_MMC_VAL_MIN, PLL_MMC_VAL_MAX);
+ req->rate = req->best_parent_rate * val / 4;
+
+ return 0;
+}
+
+static int clk_pll_mmc_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate)
+{
+ struct clk_pll_mmc *clkm = to_clk_pll_mmc(hw);
+ u32 val = PLL_MMC_SSC_DIV_N_VAL;
+ int ret;
+
+ /*
+ * The 'rate' and 'parent_rate' are intentionally unused here.
+ *
+ * Despite receiving various rate requests (e.g., 26MHz, 52MHz, 200MHz),
+ * this function consistently configures the hardware for 27MHz (0x1b).
+ * This is because these settings reflect the input reference clock
+ * frequency to the SSCPLL, not the final PLL output frequency.
+ *
+ * The actual frequency division to achieve the requested eMMC rate
+ * is handled internally by the downstream eMMC host controller.
+ */
+
+ ret = regmap_update_bits(clkm->clkr.regmap,
+ clkm->ssc_dig_ofs + PLL_SSC_DIG_EMMC1_OFFSET,
+ PLL_FLAG_INITAL_EMMC_MASK, 0x0 << PLL_FLAG_INITAL_EMMC_SHIFT);
+ if (ret)
+ return ret;
+
+ ret = set_ssc_div_n(clkm, val);
+ if (ret)
+ return ret;
+
+ ret = set_ssc_div_ext_f(clkm, 1517);
+ if (ret)
+ return ret;
+
+ ret = set_pi_ibselh(clkm, 2);
+ if (ret)
+ return ret;
+
+ ret = set_sscpll_rs(clkm, 3);
+ if (ret)
+ return ret;
+
+ ret = set_sscpll_icp(clkm, 1);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(clkm->clkr.regmap,
+ clkm->ssc_dig_ofs + PLL_SSC_DIG_EMMC3_OFFSET,
+ PLL_NCODE_SSC_EMMC_MASK,
+ 27 << PLL_NCODE_SSC_EMMC_SHIFT);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(clkm->clkr.regmap,
+ clkm->ssc_dig_ofs + PLL_SSC_DIG_EMMC3_OFFSET,
+ PLL_FCODE_SSC_EMMC_MASK, 321);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(clkm->clkr.regmap,
+ clkm->ssc_dig_ofs + PLL_SSC_DIG_EMMC4_OFFSET,
+ PLL_GRAN_EST_EM_MC_MASK, 5985);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(clkm->clkr.regmap,
+ clkm->ssc_dig_ofs + PLL_SSC_DIG_EMMC1_OFFSET,
+ PLL_EN_SSC_EMMC_MASK, 0x1);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(clkm->clkr.regmap,
+ clkm->ssc_dig_ofs + PLL_SSC_DIG_EMMC1_OFFSET,
+ PLL_EN_SSC_EMMC_MASK, 0x0);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(clkm->clkr.regmap,
+ clkm->ssc_dig_ofs + PLL_SSC_DIG_EMMC1_OFFSET,
+ PLL_FLAG_INITAL_EMMC_MASK,
+ 0x1 << PLL_FLAG_INITAL_EMMC_SHIFT);
+ if (ret)
+ return ret;
+
+ usleep_range(10, 20);
+
+ return 0;
+}
+
+const struct clk_ops rtk_clk_pll_mmc_ops = {
+ .prepare = clk_pll_mmc_prepare,
+ .unprepare = clk_pll_mmc_unprepare,
+ .is_prepared = clk_pll_mmc_is_prepared,
+ .enable = clk_pll_mmc_enable,
+ .disable = clk_pll_mmc_disable,
+ .is_enabled = clk_pll_mmc_is_enabled,
+ .recalc_rate = clk_pll_mmc_recalc_rate,
+ .determine_rate = clk_pll_mmc_determine_rate,
+ .set_rate = clk_pll_mmc_set_rate,
+};
+EXPORT_SYMBOL_NS_GPL(rtk_clk_pll_mmc_ops, "REALTEK_CLK");
diff --git a/drivers/clk/realtek/clk-pll.h b/drivers/clk/realtek/clk-pll.h
index b70d6b3ec61e..ecc51898ae2d 100644
--- a/drivers/clk/realtek/clk-pll.h
+++ b/drivers/clk/realtek/clk-pll.h
@@ -44,4 +44,17 @@ struct clk_pll {
extern const struct clk_ops rtk_clk_pll_ops;
extern const struct clk_ops rtk_clk_pll_ro_ops;
+struct clk_pll_mmc {
+ struct clk_regmap clkr;
+ unsigned int pll_ofs;
+ unsigned int ssc_dig_ofs;
+ struct clk_hw phase0_hw;
+ struct clk_hw phase1_hw;
+};
+
+#define __clk_pll_mmc_hw(_ptr) __clk_regmap_hw(&(_ptr)->clkr)
+
+extern const struct clk_ops rtk_clk_pll_mmc_ops;
+extern const struct clk_ops rtk_clk_pll_mmc_phase_ops;
+
#endif /* __CLK_REALTEK_CLK_PLL_H */
--
2.43.0
^ permalink raw reply related
* [PATCH v9 03/12] reset: realtek: Add RTD1625-CRT reset driver
From: Yu-Chun Lin @ 2026-06-24 11:29 UTC (permalink / raw)
To: mturquette, sboyd, robh, krzk+dt, conor+dt, p.zabel, cylee12,
afaerber, jyanchou, bmasney
Cc: devicetree, linux-clk, linux-kernel, linux-arm-kernel,
linux-realtek-soc, james.tai, cy.huang, stanley_chang,
eleanor.lin
In-Reply-To: <20260624112940.3475605-1-eleanor.lin@realtek.com>
From: Cheng-Yu Lee <cylee12@realtek.com>
Add support for the CRT (Clock, Reset, and Test) domain reset controller on
the Realtek RTD1625 SoC.
The reset controller shares the same register space with the CRT clock
controller. To handle this shared register space, the reset driver is
implemented as an auxiliary driver. It will be instantiated and probed via
the auxiliary bus by the RTD1625-CRT clock controller driver.
Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
Co-developed-by: Yu-Chun Lin <eleanor.lin@realtek.com>
Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
---
Changes in v9:
- Extract reset-related code from the previous clock driver patch
(formerly patch 8 in v8).
---
drivers/reset/realtek/Kconfig | 11 ++
drivers/reset/realtek/Makefile | 1 +
drivers/reset/realtek/reset-rtd1625-crt.c | 187 ++++++++++++++++++++++
3 files changed, 199 insertions(+)
create mode 100644 drivers/reset/realtek/reset-rtd1625-crt.c
diff --git a/drivers/reset/realtek/Kconfig b/drivers/reset/realtek/Kconfig
index bb6dd856a64a..b122e6334508 100644
--- a/drivers/reset/realtek/Kconfig
+++ b/drivers/reset/realtek/Kconfig
@@ -6,3 +6,14 @@ config RESET_RTK_COMMON
Realtek SoCs. It provides shared reset control operations
(assert, deassert, status) and a registration helper function
that other Realtek-specific reset drivers can use.
+
+config RESET_RTD1625
+ tristate "RTD1625 Reset Controller"
+ depends on ARCH_REALTEK || COMPILE_TEST
+ select RESET_RTK_COMMON
+ select AUXILIARY_BUS
+ help
+ This enables the reset controller driver for Realtek RTD1625 SoC.
+ The driver controls reset lines for various peripherals including
+ PCIe, SATA, HDMI, display, video encoder/decoder, USB, SD, audio,
+ and other subsystems.
diff --git a/drivers/reset/realtek/Makefile b/drivers/reset/realtek/Makefile
index 6d68e41748bf..c3f605ffb11c 100644
--- a/drivers/reset/realtek/Makefile
+++ b/drivers/reset/realtek/Makefile
@@ -1,2 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_RESET_RTK_COMMON) += reset-rtk-common.o
+obj-$(CONFIG_RESET_RTD1625) += reset-rtd1625-crt.o
diff --git a/drivers/reset/realtek/reset-rtd1625-crt.c b/drivers/reset/realtek/reset-rtd1625-crt.c
new file mode 100644
index 000000000000..5c0508577141
--- /dev/null
+++ b/drivers/reset/realtek/reset-rtd1625-crt.c
@@ -0,0 +1,187 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 Realtek Semiconductor Corporation
+ */
+
+#include <dt-bindings/reset/realtek,rtd1625.h>
+#include <linux/auxiliary_bus.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include "reset-rtk-common.h"
+
+#define RTD1625_CRT_RSTN_MAX 123
+
+static const struct rtk_reset_desc rtd1625_crt_reset_descs[] = {
+ /* Bank 0: offset 0x0 */
+ [RTD1625_CRT_RSTN_MISC] = { .ofs = 0x0, .bit = 0, .write_en = 1 },
+ [RTD1625_CRT_RSTN_DIP] = { .ofs = 0x0, .bit = 2, .write_en = 1 },
+ [RTD1625_CRT_RSTN_GSPI] = { .ofs = 0x0, .bit = 4, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SDS] = { .ofs = 0x0, .bit = 6, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SDS_REG] = { .ofs = 0x0, .bit = 8, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SDS_PHY] = { .ofs = 0x0, .bit = 10, .write_en = 1 },
+ [RTD1625_CRT_RSTN_GPU2D] = { .ofs = 0x0, .bit = 12, .write_en = 1 },
+ [RTD1625_CRT_RSTN_DC_PHY] = { .ofs = 0x0, .bit = 22, .write_en = 1 },
+ [RTD1625_CRT_RSTN_DCPHY_CRT] = { .ofs = 0x0, .bit = 24, .write_en = 1 },
+ [RTD1625_CRT_RSTN_LSADC] = { .ofs = 0x0, .bit = 26, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SE] = { .ofs = 0x0, .bit = 28, .write_en = 1 },
+ [RTD1625_CRT_RSTN_DLA] = { .ofs = 0x0, .bit = 30, .write_en = 1 },
+ /* Bank 1: offset 0x4 */
+ [RTD1625_CRT_RSTN_JPEG] = { .ofs = 0x4, .bit = 0, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SD] = { .ofs = 0x4, .bit = 2, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SDIO] = { .ofs = 0x4, .bit = 6, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCR_CNT] = { .ofs = 0x4, .bit = 8, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE0_STITCH] = { .ofs = 0x4, .bit = 10, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE0_PHY] = { .ofs = 0x4, .bit = 12, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE0] = { .ofs = 0x4, .bit = 14, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE0_CORE] = { .ofs = 0x4, .bit = 16, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE0_POWER] = { .ofs = 0x4, .bit = 18, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE0_NONSTICH] = { .ofs = 0x4, .bit = 20, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE0_PHY_MDIO] = { .ofs = 0x4, .bit = 22, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE0_SGMII_MDIO] = { .ofs = 0x4, .bit = 24, .write_en = 1 },
+ [RTD1625_CRT_RSTN_VO2] = { .ofs = 0x4, .bit = 28, .write_en = 1 },
+ [RTD1625_CRT_RSTN_MISC_SC0] = { .ofs = 0x4, .bit = 30, .write_en = 1 },
+ /* Bank 2: offset 0x8 */
+ [RTD1625_CRT_RSTN_MD] = { .ofs = 0x8, .bit = 4, .write_en = 1 },
+ [RTD1625_CRT_RSTN_LVDS1] = { .ofs = 0x8, .bit = 6, .write_en = 1 },
+ [RTD1625_CRT_RSTN_LVDS2] = { .ofs = 0x8, .bit = 8, .write_en = 1 },
+ [RTD1625_CRT_RSTN_MISC_SC1] = { .ofs = 0x8, .bit = 10, .write_en = 1 },
+ [RTD1625_CRT_RSTN_I2C_3] = { .ofs = 0x8, .bit = 12, .write_en = 1 },
+ [RTD1625_CRT_RSTN_FAN] = { .ofs = 0x8, .bit = 14, .write_en = 1 },
+ [RTD1625_CRT_RSTN_TVE] = { .ofs = 0x8, .bit = 16, .write_en = 1 },
+ [RTD1625_CRT_RSTN_AIO] = { .ofs = 0x8, .bit = 18, .write_en = 1 },
+ [RTD1625_CRT_RSTN_VO] = { .ofs = 0x8, .bit = 20, .write_en = 1 },
+ [RTD1625_CRT_RSTN_MIPI_CSI] = { .ofs = 0x8, .bit = 22, .write_en = 1 },
+ [RTD1625_CRT_RSTN_HDMIRX] = { .ofs = 0x8, .bit = 24, .write_en = 1 },
+ [RTD1625_CRT_RSTN_HDMIRX_WRAP] = { .ofs = 0x8, .bit = 26, .write_en = 1 },
+ [RTD1625_CRT_RSTN_HDMI] = { .ofs = 0x8, .bit = 28, .write_en = 1 },
+ [RTD1625_CRT_RSTN_DISP] = { .ofs = 0x8, .bit = 30, .write_en = 1 },
+ /* Bank 3: offset 0xc */
+ [RTD1625_CRT_RSTN_SATA_PHY_POW1] = { .ofs = 0xc, .bit = 0, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SATA_PHY_POW0] = { .ofs = 0xc, .bit = 2, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SATA_MDIO1] = { .ofs = 0xc, .bit = 4, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SATA_MDIO0] = { .ofs = 0xc, .bit = 6, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SATA_WRAP] = { .ofs = 0xc, .bit = 8, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SATA_MAC_P1] = { .ofs = 0xc, .bit = 10, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SATA_MAC_P0] = { .ofs = 0xc, .bit = 12, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SATA_MAC_COM] = { .ofs = 0xc, .bit = 14, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE1_STITCH] = { .ofs = 0xc, .bit = 16, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE1_PHY] = { .ofs = 0xc, .bit = 18, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE1] = { .ofs = 0xc, .bit = 20, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE1_CORE] = { .ofs = 0xc, .bit = 22, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE1_POWER] = { .ofs = 0xc, .bit = 24, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE1_NONSTICH] = { .ofs = 0xc, .bit = 26, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE1_PHY_MDIO] = { .ofs = 0xc, .bit = 28, .write_en = 1 },
+ [RTD1625_CRT_RSTN_HDMITOP] = { .ofs = 0xc, .bit = 30, .write_en = 1 },
+ /* Bank 4: offset 0x68 */
+ [RTD1625_CRT_RSTN_I2C_4] = { .ofs = 0x68, .bit = 2, .write_en = 1 },
+ [RTD1625_CRT_RSTN_I2C_5] = { .ofs = 0x68, .bit = 4, .write_en = 1 },
+ [RTD1625_CRT_RSTN_TSIO] = { .ofs = 0x68, .bit = 6, .write_en = 1 },
+ [RTD1625_CRT_RSTN_VI] = { .ofs = 0x68, .bit = 8, .write_en = 1 },
+ [RTD1625_CRT_RSTN_EDP] = { .ofs = 0x68, .bit = 10, .write_en = 1 },
+ [RTD1625_CRT_RSTN_VE1_MMU] = { .ofs = 0x68, .bit = 12, .write_en = 1 },
+ [RTD1625_CRT_RSTN_VE1_MMU_FUNC] = { .ofs = 0x68, .bit = 14, .write_en = 1 },
+ [RTD1625_CRT_RSTN_HSE_MMU] = { .ofs = 0x68, .bit = 16, .write_en = 1 },
+ [RTD1625_CRT_RSTN_HSE_MMU_FUNC] = { .ofs = 0x68, .bit = 18, .write_en = 1 },
+ [RTD1625_CRT_RSTN_MDLM2M] = { .ofs = 0x68, .bit = 20, .write_en = 1 },
+ [RTD1625_CRT_RSTN_ISO_GSPI] = { .ofs = 0x68, .bit = 22, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SOFT_NPU] = { .ofs = 0x68, .bit = 24, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SPI2EMMC] = { .ofs = 0x68, .bit = 26, .write_en = 1 },
+ [RTD1625_CRT_RSTN_EARC] = { .ofs = 0x68, .bit = 28, .write_en = 1 },
+ [RTD1625_CRT_RSTN_VE1] = { .ofs = 0x68, .bit = 30, .write_en = 1 },
+ /* Bank 5: offset 0x90 */
+ [RTD1625_CRT_RSTN_PCIE2_STITCH] = { .ofs = 0x90, .bit = 0, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE2_PHY] = { .ofs = 0x90, .bit = 2, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE2] = { .ofs = 0x90, .bit = 4, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE2_CORE] = { .ofs = 0x90, .bit = 6, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE2_POWER] = { .ofs = 0x90, .bit = 8, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE2_NONSTICH] = { .ofs = 0x90, .bit = 10, .write_en = 1 },
+ [RTD1625_CRT_RSTN_PCIE2_PHY_MDIO] = { .ofs = 0x90, .bit = 12, .write_en = 1 },
+ [RTD1625_CRT_RSTN_DCPHY_UMCTL2] = { .ofs = 0x90, .bit = 14, .write_en = 1 },
+ [RTD1625_CRT_RSTN_MIPI_DSI] = { .ofs = 0x90, .bit = 16, .write_en = 1 },
+ [RTD1625_CRT_RSTN_HIFM] = { .ofs = 0x90, .bit = 18, .write_en = 1 },
+ [RTD1625_CRT_RSTN_NSRAM] = { .ofs = 0x90, .bit = 20, .write_en = 1 },
+ [RTD1625_CRT_RSTN_AUCPU0_REG] = { .ofs = 0x90, .bit = 22, .write_en = 1 },
+ [RTD1625_CRT_RSTN_MDL_GENPW] = { .ofs = 0x90, .bit = 24, .write_en = 1 },
+ [RTD1625_CRT_RSTN_MDL_CHIP] = { .ofs = 0x90, .bit = 26, .write_en = 1 },
+ [RTD1625_CRT_RSTN_MDL_IP] = { .ofs = 0x90, .bit = 28, .write_en = 1 },
+ [RTD1625_CRT_RSTN_TEST_MUX] = { .ofs = 0x90, .bit = 30, .write_en = 1 },
+ /* Bank 6: offset 0xb8 */
+ [RTD1625_CRT_RSTN_ISO_BIST] = { .ofs = 0xb8, .bit = 0, .write_en = 1 },
+ [RTD1625_CRT_RSTN_MAIN_BIST] = { .ofs = 0xb8, .bit = 2, .write_en = 1 },
+ [RTD1625_CRT_RSTN_MAIN2_BIST] = { .ofs = 0xb8, .bit = 4, .write_en = 1 },
+ [RTD1625_CRT_RSTN_VE1_BIST] = { .ofs = 0xb8, .bit = 6, .write_en = 1 },
+ [RTD1625_CRT_RSTN_VE2_BIST] = { .ofs = 0xb8, .bit = 8, .write_en = 1 },
+ [RTD1625_CRT_RSTN_DCPHY_BIST] = { .ofs = 0xb8, .bit = 10, .write_en = 1 },
+ [RTD1625_CRT_RSTN_GPU_BIST] = { .ofs = 0xb8, .bit = 12, .write_en = 1 },
+ [RTD1625_CRT_RSTN_DISP_BIST] = { .ofs = 0xb8, .bit = 14, .write_en = 1 },
+ [RTD1625_CRT_RSTN_NPU_BIST] = { .ofs = 0xb8, .bit = 16, .write_en = 1 },
+ [RTD1625_CRT_RSTN_CAS_BIST] = { .ofs = 0xb8, .bit = 18, .write_en = 1 },
+ [RTD1625_CRT_RSTN_VE4_BIST] = { .ofs = 0xb8, .bit = 20, .write_en = 1 },
+ /* Bank 7: offset 0x454 (DUMMY0, no write_en) */
+ [RTD1625_CRT_RSTN_EMMC] = { .ofs = 0x454, .bit = 0 },
+ /* Bank 8: offset 0x458 (DUMMY1, no write_en) */
+ [RTD1625_CRT_RSTN_GPU] = { .ofs = 0x458, .bit = 0 },
+ /* Bank 9: offset 0x464 (DUMMY4, no write_en) */
+ [RTD1625_CRT_RSTN_VE2] = { .ofs = 0x464, .bit = 0 },
+ /* Bank 10: offset 0x880 */
+ [RTD1625_CRT_RSTN_UR1] = { .ofs = 0x880, .bit = 0, .write_en = 1 },
+ [RTD1625_CRT_RSTN_UR2] = { .ofs = 0x880, .bit = 2, .write_en = 1 },
+ [RTD1625_CRT_RSTN_UR3] = { .ofs = 0x880, .bit = 4, .write_en = 1 },
+ [RTD1625_CRT_RSTN_UR4] = { .ofs = 0x880, .bit = 6, .write_en = 1 },
+ [RTD1625_CRT_RSTN_UR5] = { .ofs = 0x880, .bit = 8, .write_en = 1 },
+ [RTD1625_CRT_RSTN_UR6] = { .ofs = 0x880, .bit = 10, .write_en = 1 },
+ [RTD1625_CRT_RSTN_UR7] = { .ofs = 0x880, .bit = 12, .write_en = 1 },
+ [RTD1625_CRT_RSTN_UR8] = { .ofs = 0x880, .bit = 14, .write_en = 1 },
+ [RTD1625_CRT_RSTN_UR9] = { .ofs = 0x880, .bit = 16, .write_en = 1 },
+ [RTD1625_CRT_RSTN_UR_TOP] = { .ofs = 0x880, .bit = 18, .write_en = 1 },
+ [RTD1625_CRT_RSTN_I2C_7] = { .ofs = 0x880, .bit = 28, .write_en = 1 },
+ [RTD1625_CRT_RSTN_I2C_6] = { .ofs = 0x880, .bit = 30, .write_en = 1 },
+ /* Bank 11: offset 0x890 */
+ [RTD1625_CRT_RSTN_SPI0] = { .ofs = 0x890, .bit = 0, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SPI1] = { .ofs = 0x890, .bit = 2, .write_en = 1 },
+ [RTD1625_CRT_RSTN_SPI2] = { .ofs = 0x890, .bit = 4, .write_en = 1 },
+ [RTD1625_CRT_RSTN_LSADC0] = { .ofs = 0x890, .bit = 16, .write_en = 1 },
+ [RTD1625_CRT_RSTN_LSADC1] = { .ofs = 0x890, .bit = 18, .write_en = 1 },
+ [RTD1625_CRT_RSTN_ISOMIS_DMA] = { .ofs = 0x890, .bit = 20, .write_en = 1 },
+ [RTD1625_CRT_RSTN_AUDIO_ADC] = { .ofs = 0x890, .bit = 22, .write_en = 1 },
+ [RTD1625_CRT_RSTN_DPTX] = { .ofs = 0x890, .bit = 24, .write_en = 1 },
+ [RTD1625_CRT_RSTN_AUCPU1_REG] = { .ofs = 0x890, .bit = 26, .write_en = 1 },
+ [RTD1625_CRT_RSTN_EDPTX] = { .ofs = 0x890, .bit = 28, .write_en = 1 },
+};
+
+static int rtd1625_crt_reset_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ struct device *dev = &adev->dev;
+ struct rtk_reset_data *data;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->descs = rtd1625_crt_reset_descs;
+ data->rcdev.nr_resets = RTD1625_CRT_RSTN_MAX;
+ data->rcdev.owner = THIS_MODULE;
+
+ return rtk_reset_controller_add(dev, data);
+}
+
+static const struct auxiliary_device_id rtd1625_crt_reset_ids[] = {
+ { .name = "clk_rtk.crt_rst" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(auxiliary, rtd1625_crt_reset_ids);
+
+static struct auxiliary_driver rtd1625_crt_driver = {
+ .probe = rtd1625_crt_reset_probe,
+ .id_table = rtd1625_crt_reset_ids,
+ .driver = {
+ .name = "rtd1625-crt-reset",
+ },
+};
+module_auxiliary_driver(rtd1625_crt_driver);
+
+MODULE_DESCRIPTION("Realtek RTD1625 CRT Reset Controller Driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("REALTEK_RESET");
--
2.43.0
^ permalink raw reply related
* [PATCH v9 07/12] clk: realtek: Add support for gate clock
From: Yu-Chun Lin @ 2026-06-24 11:29 UTC (permalink / raw)
To: mturquette, sboyd, robh, krzk+dt, conor+dt, p.zabel, cylee12,
afaerber, jyanchou, bmasney
Cc: devicetree, linux-clk, linux-kernel, linux-arm-kernel,
linux-realtek-soc, james.tai, cy.huang, stanley_chang,
eleanor.lin
In-Reply-To: <20260624112940.3475605-1-eleanor.lin@realtek.com>
From: Cheng-Yu Lee <cylee12@realtek.com>
Introduce clk_regmap_gate_ops supporting enable, disable, is_enabled, and
for standard regmap gate clocks.
Add clk_regmap_gate_ro_ops as a read-only variant exposing only is_enabled.
Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
Co-developed-by: Yu-Chun Lin <eleanor.lin@realtek.com>
Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
---
Changes in v9:
- None.
---
drivers/clk/realtek/Makefile | 1 +
drivers/clk/realtek/clk-regmap-gate.c | 70 +++++++++++++++++++++++++++
drivers/clk/realtek/clk-regmap-gate.h | 65 +++++++++++++++++++++++++
3 files changed, 136 insertions(+)
create mode 100644 drivers/clk/realtek/clk-regmap-gate.c
create mode 100644 drivers/clk/realtek/clk-regmap-gate.h
diff --git a/drivers/clk/realtek/Makefile b/drivers/clk/realtek/Makefile
index 71edc18121c6..90c0658a83bf 100644
--- a/drivers/clk/realtek/Makefile
+++ b/drivers/clk/realtek/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_RTK_CLK_COMMON) += clk-rtk.o
clk-rtk-y += clk-rtk-common.o
clk-rtk-y += clk-pll.o
clk-rtk-y += freq_table.o
+clk-rtk-y += clk-regmap-gate.o
diff --git a/drivers/clk/realtek/clk-regmap-gate.c b/drivers/clk/realtek/clk-regmap-gate.c
new file mode 100644
index 000000000000..0db0057215e3
--- /dev/null
+++ b/drivers/clk/realtek/clk-regmap-gate.c
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2017-2026 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include <linux/bits.h>
+#include <linux/clk-provider.h>
+#include <linux/export.h>
+#include <linux/regmap.h>
+#include "clk-regmap-gate.h"
+
+static int clk_regmap_gate_enable(struct clk_hw *hw)
+{
+ struct clk_regmap_gate *clkg = to_clk_regmap_gate(hw);
+ unsigned int mask;
+ unsigned int val;
+
+ mask = BIT(clkg->bit_idx);
+ val = BIT(clkg->bit_idx);
+
+ if (clkg->write_en) {
+ mask |= BIT(clkg->bit_idx + 1);
+ val |= BIT(clkg->bit_idx + 1);
+ }
+
+ return regmap_update_bits(clkg->clkr.regmap, clkg->gate_ofs, mask, val);
+}
+
+static void clk_regmap_gate_disable(struct clk_hw *hw)
+{
+ struct clk_regmap_gate *clkg = to_clk_regmap_gate(hw);
+ unsigned int mask;
+ unsigned int val;
+
+ mask = BIT(clkg->bit_idx);
+ val = 0;
+
+ if (clkg->write_en) {
+ mask |= BIT(clkg->bit_idx + 1);
+ val |= BIT(clkg->bit_idx + 1);
+ }
+
+ regmap_update_bits(clkg->clkr.regmap, clkg->gate_ofs, mask, val);
+}
+
+static int clk_regmap_gate_is_enabled(struct clk_hw *hw)
+{
+ struct clk_regmap_gate *clkg = to_clk_regmap_gate(hw);
+ int ret;
+ u32 val;
+
+ ret = regmap_read(clkg->clkr.regmap, clkg->gate_ofs, &val);
+ if (ret < 0)
+ return ret;
+
+ return !!(val & BIT(clkg->bit_idx));
+}
+
+const struct clk_ops rtk_clk_regmap_gate_ops = {
+ .enable = clk_regmap_gate_enable,
+ .disable = clk_regmap_gate_disable,
+ .is_enabled = clk_regmap_gate_is_enabled,
+};
+EXPORT_SYMBOL_NS_GPL(rtk_clk_regmap_gate_ops, "REALTEK_CLK");
+
+const struct clk_ops rtk_clk_regmap_gate_ro_ops = {
+ .is_enabled = clk_regmap_gate_is_enabled,
+};
+EXPORT_SYMBOL_NS_GPL(rtk_clk_regmap_gate_ro_ops, "REALTEK_CLK");
diff --git a/drivers/clk/realtek/clk-regmap-gate.h b/drivers/clk/realtek/clk-regmap-gate.h
new file mode 100644
index 000000000000..3f4c7a784eb0
--- /dev/null
+++ b/drivers/clk/realtek/clk-regmap-gate.h
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2017-2026 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#ifndef __CLK_REALTEK_CLK_REGMAP_GATE_H
+#define __CLK_REALTEK_CLK_REGMAP_GATE_H
+
+#include "clk-rtk-common.h"
+
+struct clk_regmap_gate {
+ struct clk_regmap clkr;
+ int gate_ofs;
+ u8 bit_idx;
+ u32 write_en : 1;
+};
+
+#define __clk_regmap_gate_hw(_p) __clk_regmap_hw(&(_p)->clkr)
+
+#define __CLK_REGMAP_GATE(_name, _parent, _ops, _flags, _ofs, _bit_idx, \
+ _write_en) \
+ struct clk_regmap_gate _name = { \
+ .clkr.hw.init = CLK_HW_INIT(#_name, _parent, _ops, _flags), \
+ .gate_ofs = _ofs, \
+ .bit_idx = _bit_idx, \
+ .write_en = _write_en, \
+ }
+
+#define CLK_REGMAP_GATE(_name, _parent, _flags, _ofs, _bit_idx, _write_en) \
+ __CLK_REGMAP_GATE(_name, _parent, &rtk_clk_regmap_gate_ops, _flags, _ofs, \
+ _bit_idx, _write_en)
+
+#define CLK_REGMAP_GATE_RO(_name, _parent, _flags, _ofs, _bit_idx, _write_en) \
+ __CLK_REGMAP_GATE(_name, _parent, &rtk_clk_regmap_gate_ro_ops, _flags, \
+ _ofs, _bit_idx, _write_en)
+
+#define __CLK_REGMAP_GATE_NO_PARENT(_name, _ops, _flags, _ofs, _bit_idx, \
+ _write_en) \
+ struct clk_regmap_gate _name = { \
+ .clkr.hw.init = CLK_HW_INIT_NO_PARENT(#_name, _ops, _flags), \
+ .gate_ofs = _ofs, \
+ .bit_idx = _bit_idx, \
+ .write_en = _write_en, \
+ }
+
+#define CLK_REGMAP_GATE_NO_PARENT(_name, _flags, _ofs, _bit_idx, _write_en) \
+ __CLK_REGMAP_GATE_NO_PARENT(_name, &rtk_clk_regmap_gate_ops, _flags, _ofs, \
+ _bit_idx, _write_en)
+
+#define CLK_REGMAP_GATE_NO_PARENT_RO(_name, _flags, _ofs, _bit_idx, _write_en) \
+ __CLK_REGMAP_GATE_NO_PARENT(_name, &rtk_clk_regmap_gate_ro_ops, _flags, \
+ _ofs, _bit_idx, _write_en)
+
+static inline struct clk_regmap_gate *to_clk_regmap_gate(struct clk_hw *hw)
+{
+ struct clk_regmap *clkr = to_clk_regmap(hw);
+
+ return container_of(clkr, struct clk_regmap_gate, clkr);
+}
+
+extern const struct clk_ops rtk_clk_regmap_gate_ops;
+extern const struct clk_ops rtk_clk_regmap_gate_ro_ops;
+
+#endif /* __CLK_REALTEK_CLK_REGMAP_GATE_H */
--
2.43.0
^ permalink raw reply related
* [PATCH v9 06/12] clk: realtek: Add support for phase locked loops (PLLs)
From: Yu-Chun Lin @ 2026-06-24 11:29 UTC (permalink / raw)
To: mturquette, sboyd, robh, krzk+dt, conor+dt, p.zabel, cylee12,
afaerber, jyanchou, bmasney
Cc: devicetree, linux-clk, linux-kernel, linux-arm-kernel,
linux-realtek-soc, james.tai, cy.huang, stanley_chang,
eleanor.lin
In-Reply-To: <20260624112940.3475605-1-eleanor.lin@realtek.com>
From: Cheng-Yu Lee <cylee12@realtek.com>
Provide a full set of PLL operations for programmable PLLs and a read-only
variant for fixed or hardware-managed PLLs.
Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
Co-developed-by: Yu-Chun Lin <eleanor.lin@realtek.com>
Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
---
Changes in v9:
- Add 'ftbl_find_ceil_by_rate()' as a fallback in 'clk_pll_determine_rate()' to
properly handle 'min_rate' and 'max_rate' boundaries when the floor result falls
below 'min_rate'.
---
drivers/clk/realtek/Makefile | 2 +
drivers/clk/realtek/clk-pll.c | 217 +++++++++++++++++++++++++++++++
drivers/clk/realtek/clk-pll.h | 47 +++++++
drivers/clk/realtek/freq_table.c | 57 ++++++++
drivers/clk/realtek/freq_table.h | 18 +++
5 files changed, 341 insertions(+)
create mode 100644 drivers/clk/realtek/clk-pll.c
create mode 100644 drivers/clk/realtek/clk-pll.h
create mode 100644 drivers/clk/realtek/freq_table.c
create mode 100644 drivers/clk/realtek/freq_table.h
diff --git a/drivers/clk/realtek/Makefile b/drivers/clk/realtek/Makefile
index 13000ed4ba11..71edc18121c6 100644
--- a/drivers/clk/realtek/Makefile
+++ b/drivers/clk/realtek/Makefile
@@ -2,3 +2,5 @@
obj-$(CONFIG_RTK_CLK_COMMON) += clk-rtk.o
clk-rtk-y += clk-rtk-common.o
+clk-rtk-y += clk-pll.o
+clk-rtk-y += freq_table.o
diff --git a/drivers/clk/realtek/clk-pll.c b/drivers/clk/realtek/clk-pll.c
new file mode 100644
index 000000000000..54c284070e47
--- /dev/null
+++ b/drivers/clk/realtek/clk-pll.c
@@ -0,0 +1,217 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2024-2026 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include <linux/export.h>
+#include <linux/regmap.h>
+#include <linux/spinlock.h>
+#include "clk-pll.h"
+
+#define TIMEOUT 500
+
+static inline struct clk_pll *to_clk_pll(struct clk_hw *hw)
+{
+ struct clk_regmap *clkr = to_clk_regmap(hw);
+
+ return container_of(clkr, struct clk_pll, clkr);
+}
+
+static int wait_freq_ready(struct clk_pll *clkp)
+{
+ u32 pollval;
+
+ /* reg == 0 means not configured.
+ * Register offset 0 is never a valid address on Realtek SoCs.
+ */
+ if (!clkp->freq_ready_reg)
+ return 0;
+
+ return regmap_read_poll_timeout_atomic(clkp->clkr.regmap, clkp->freq_ready_reg, pollval,
+ (pollval & clkp->freq_ready_mask) == clkp->freq_ready_val, 1, TIMEOUT);
+}
+
+static bool is_power_on(struct clk_pll *clkp)
+{
+ u32 val;
+
+ /* reg == 0 means not configured (assume always on).
+ * Register offset 0 is never a valid address on Realtek SoCs.
+ */
+ if (!clkp->power_reg)
+ return true;
+
+ if (regmap_read(clkp->clkr.regmap, clkp->power_reg, &val))
+ return false;
+
+ return (val & clkp->power_mask) == clkp->power_val_on;
+}
+
+static void clk_pll_disable(struct clk_hw *hw)
+{
+ struct clk_pll *clkp = to_clk_pll(hw);
+ unsigned long flags;
+
+ if (!clkp->seq_power_off)
+ return;
+
+ spin_lock_irqsave(&clkp->lock, flags);
+
+ regmap_multi_reg_write(clkp->clkr.regmap, clkp->seq_power_off,
+ clkp->num_seq_power_off);
+
+ spin_unlock_irqrestore(&clkp->lock, flags);
+}
+
+static int clk_pll_is_enabled(struct clk_hw *hw)
+{
+ struct clk_pll *clkp = to_clk_pll(hw);
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&clkp->lock, flags);
+
+ ret = is_power_on(clkp);
+
+ spin_unlock_irqrestore(&clkp->lock, flags);
+
+ return ret;
+}
+
+static int clk_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct clk_pll *clkp = to_clk_pll(hw);
+ const struct freq_table *ftblv = NULL;
+
+ ftblv = ftbl_find_by_rate(clkp->freq_tbl, req->rate);
+
+ if (ftblv && ftblv->rate >= req->min_rate) {
+ req->rate = ftblv->rate;
+ return 0;
+ }
+
+ /* floor result is below min_rate; find the smallest rate >= min_rate */
+ ftblv = ftbl_find_ceil_by_rate(clkp->freq_tbl, req->min_rate);
+ if (!ftblv || ftblv->rate > req->max_rate)
+ return -EINVAL;
+
+ req->rate = ftblv->rate;
+
+ return 0;
+}
+
+static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_pll *clkp = to_clk_pll(hw);
+ const struct freq_table *fv;
+ unsigned long flags;
+ u32 freq_val;
+
+ spin_lock_irqsave(&clkp->lock, flags);
+
+ if (regmap_read(clkp->clkr.regmap, clkp->freq_reg, &freq_val)) {
+ spin_unlock_irqrestore(&clkp->lock, flags);
+ return 0;
+ }
+
+ freq_val &= clkp->freq_mask;
+
+ fv = ftbl_find_by_val_with_mask(clkp->freq_tbl, clkp->freq_mask,
+ freq_val);
+
+ spin_unlock_irqrestore(&clkp->lock, flags);
+
+ return fv ? fv->rate : 0;
+}
+
+static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_pll *clkp = to_clk_pll(hw);
+ const struct freq_table *fv;
+ unsigned long flags;
+ int ret;
+
+ fv = ftbl_find_by_rate(clkp->freq_tbl, rate);
+ if (!fv || fv->rate != rate)
+ return -EINVAL;
+
+ spin_lock_irqsave(&clkp->lock, flags);
+
+ if (clkp->seq_pre_set_freq) {
+ ret = regmap_multi_reg_write(clkp->clkr.regmap, clkp->seq_pre_set_freq,
+ clkp->num_seq_pre_set_freq);
+ if (ret)
+ goto unlock;
+ }
+
+ ret = regmap_update_bits(clkp->clkr.regmap, clkp->freq_reg,
+ clkp->freq_mask, fv->val);
+ if (ret)
+ goto unlock;
+
+ if (clkp->seq_post_set_freq) {
+ ret = regmap_multi_reg_write(clkp->clkr.regmap, clkp->seq_post_set_freq,
+ clkp->num_seq_post_set_freq);
+ if (ret)
+ goto unlock;
+ }
+
+ if (is_power_on(clkp)) {
+ ret = wait_freq_ready(clkp);
+ if (ret)
+ goto unlock;
+ }
+
+unlock:
+ spin_unlock_irqrestore(&clkp->lock, flags);
+
+ return ret;
+}
+
+static int clk_pll_enable(struct clk_hw *hw)
+{
+ struct clk_pll *clkp = to_clk_pll(hw);
+ unsigned long flags;
+ int ret = 0;
+
+ if (!clkp->seq_power_on)
+ return ret;
+
+ spin_lock_irqsave(&clkp->lock, flags);
+
+ if (is_power_on(clkp))
+ goto unlock;
+
+ ret = regmap_multi_reg_write(clkp->clkr.regmap, clkp->seq_power_on,
+ clkp->num_seq_power_on);
+ if (ret)
+ goto unlock;
+
+ ret = wait_freq_ready(clkp);
+ if (ret)
+ goto unlock;
+
+unlock:
+ spin_unlock_irqrestore(&clkp->lock, flags);
+
+ return ret;
+}
+
+const struct clk_ops rtk_clk_pll_ops = {
+ .enable = clk_pll_enable,
+ .disable = clk_pll_disable,
+ .is_enabled = clk_pll_is_enabled,
+ .recalc_rate = clk_pll_recalc_rate,
+ .determine_rate = clk_pll_determine_rate,
+ .set_rate = clk_pll_set_rate,
+};
+EXPORT_SYMBOL_NS_GPL(rtk_clk_pll_ops, "REALTEK_CLK");
+
+const struct clk_ops rtk_clk_pll_ro_ops = {
+ .recalc_rate = clk_pll_recalc_rate,
+};
+EXPORT_SYMBOL_NS_GPL(rtk_clk_pll_ro_ops, "REALTEK_CLK");
diff --git a/drivers/clk/realtek/clk-pll.h b/drivers/clk/realtek/clk-pll.h
new file mode 100644
index 000000000000..b70d6b3ec61e
--- /dev/null
+++ b/drivers/clk/realtek/clk-pll.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2017-2026 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#ifndef __CLK_REALTEK_CLK_PLL_H
+#define __CLK_REALTEK_CLK_PLL_H
+
+#include <linux/spinlock.h>
+#include "clk-rtk-common.h"
+#include "freq_table.h"
+
+struct reg_sequence;
+
+struct clk_pll {
+ struct clk_regmap clkr;
+ const struct reg_sequence *seq_power_on;
+ u32 num_seq_power_on;
+ const struct reg_sequence *seq_power_off;
+ u32 num_seq_power_off;
+ const struct reg_sequence *seq_pre_set_freq;
+ u32 num_seq_pre_set_freq;
+ const struct reg_sequence *seq_post_set_freq;
+ u32 num_seq_post_set_freq;
+ const struct freq_table *freq_tbl;
+ u32 freq_reg;
+ u32 freq_mask;
+ u32 freq_ready_mask;
+ u32 freq_ready_reg;
+ u32 freq_ready_val;
+ u32 power_reg;
+ u32 power_mask;
+ u32 power_val_on;
+
+ /* This lock prevents race conditions when multiple CPUs or contexts
+ * simultaneously access this PLL's registers during multi-step operations
+ */
+ spinlock_t lock;
+};
+
+#define __clk_pll_hw(_ptr) __clk_regmap_hw(&(_ptr)->clkr)
+
+extern const struct clk_ops rtk_clk_pll_ops;
+extern const struct clk_ops rtk_clk_pll_ro_ops;
+
+#endif /* __CLK_REALTEK_CLK_PLL_H */
diff --git a/drivers/clk/realtek/freq_table.c b/drivers/clk/realtek/freq_table.c
new file mode 100644
index 000000000000..6ff8dbf60cc6
--- /dev/null
+++ b/drivers/clk/realtek/freq_table.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/bitops.h>
+#include "freq_table.h"
+
+#define IS_FREQ_TABLE_END(_f) ((_f)->rate == 0)
+
+const struct freq_table *ftbl_find_by_rate(const struct freq_table *ftbl,
+ unsigned long rate)
+{
+ const struct freq_table *best = NULL;
+ unsigned long best_rate = 0;
+
+ for (; !IS_FREQ_TABLE_END(ftbl); ftbl++) {
+ if (ftbl->rate == rate)
+ return ftbl;
+
+ if (ftbl->rate > rate)
+ continue;
+
+ if (ftbl->rate > best_rate) {
+ best_rate = ftbl->rate;
+ best = ftbl;
+ }
+ }
+
+ return best;
+}
+
+const struct freq_table *ftbl_find_ceil_by_rate(const struct freq_table *ftbl,
+ unsigned long rate)
+{
+ const struct freq_table *best = NULL;
+ unsigned long best_rate = ULONG_MAX;
+
+ for (; !IS_FREQ_TABLE_END(ftbl); ftbl++) {
+ if (ftbl->rate < rate)
+ continue;
+
+ if (ftbl->rate < best_rate) {
+ best_rate = ftbl->rate;
+ best = ftbl;
+ }
+ }
+
+ return best;
+}
+
+const struct freq_table *
+ftbl_find_by_val_with_mask(const struct freq_table *ftbl, u32 mask, u32 value)
+{
+ for (; !IS_FREQ_TABLE_END(ftbl); ftbl++) {
+ if ((ftbl->val & mask) == (value & mask))
+ return ftbl;
+ }
+ return NULL;
+}
diff --git a/drivers/clk/realtek/freq_table.h b/drivers/clk/realtek/freq_table.h
new file mode 100644
index 000000000000..78215aee3300
--- /dev/null
+++ b/drivers/clk/realtek/freq_table.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+struct freq_table {
+ u32 val;
+ unsigned long rate;
+};
+
+#define FREQ_TABLE_END \
+ { \
+ .rate = 0 \
+ }
+
+const struct freq_table *ftbl_find_by_rate(const struct freq_table *ftbl,
+ unsigned long rate);
+const struct freq_table *ftbl_find_ceil_by_rate(const struct freq_table *ftbl,
+ unsigned long rate);
+const struct freq_table *
+ftbl_find_by_val_with_mask(const struct freq_table *ftbl, u32 mask, u32 value);
--
2.43.0
^ permalink raw reply related
* [PATCH v9 02/12] reset: Add Realtek basic reset support
From: Yu-Chun Lin @ 2026-06-24 11:29 UTC (permalink / raw)
To: mturquette, sboyd, robh, krzk+dt, conor+dt, p.zabel, cylee12,
afaerber, jyanchou, bmasney
Cc: devicetree, linux-clk, linux-kernel, linux-arm-kernel,
linux-realtek-soc, james.tai, cy.huang, stanley_chang,
eleanor.lin
In-Reply-To: <20260624112940.3475605-1-eleanor.lin@realtek.com>
From: Cheng-Yu Lee <cylee12@realtek.com>
Define the reset operations backed by a regmap-based register interface
and prepare the reset controller to be registered through the reset
framework.
Since the reset controllers on Realtek SoCs often share the same register
space with the clock controllers, this common framework is designed to
extract the regmap and device tree node from the parent device
(e.g., an auxiliary device parent).
Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
Co-developed-by: Yu-Chun Lin <eleanor.lin@realtek.com>
Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
---
Changes in v8:
- Rename common.[ch] to reset-rtk-common.[ch].
---
MAINTAINERS | 1 +
drivers/reset/Kconfig | 1 +
drivers/reset/Makefile | 1 +
drivers/reset/realtek/Kconfig | 8 +++
drivers/reset/realtek/Makefile | 2 +
drivers/reset/realtek/reset-rtk-common.c | 90 ++++++++++++++++++++++++
drivers/reset/realtek/reset-rtk-common.h | 29 ++++++++
7 files changed, 132 insertions(+)
create mode 100644 drivers/reset/realtek/Kconfig
create mode 100644 drivers/reset/realtek/Makefile
create mode 100644 drivers/reset/realtek/reset-rtk-common.c
create mode 100644 drivers/reset/realtek/reset-rtk-common.h
diff --git a/MAINTAINERS b/MAINTAINERS
index d9df9b120e55..3bc431a2a7d1 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22675,6 +22675,7 @@ L: devicetree@vger.kernel.org
L: linux-clk@vger.kernel.org
S: Supported
F: Documentation/devicetree/bindings/clock/realtek*
+F: drivers/reset/realtek/*
F: include/dt-bindings/clock/realtek*
F: include/dt-bindings/reset/realtek*
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index d009eb0849a3..45a4227b5f44 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -418,6 +418,7 @@ config RESET_ZYNQMP
source "drivers/reset/amlogic/Kconfig"
source "drivers/reset/hisilicon/Kconfig"
+source "drivers/reset/realtek/Kconfig"
source "drivers/reset/spacemit/Kconfig"
source "drivers/reset/starfive/Kconfig"
source "drivers/reset/sti/Kconfig"
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 3e52569bd276..7330fee91365 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -2,6 +2,7 @@
obj-y += core.o
obj-y += amlogic/
obj-y += hisilicon/
+obj-y += realtek/
obj-y += spacemit/
obj-y += starfive/
obj-y += sti/
diff --git a/drivers/reset/realtek/Kconfig b/drivers/reset/realtek/Kconfig
new file mode 100644
index 000000000000..bb6dd856a64a
--- /dev/null
+++ b/drivers/reset/realtek/Kconfig
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config RESET_RTK_COMMON
+ tristate "Realtek common reset driver" if COMPILE_TEST
+ help
+ This option enables the common reset controller library for
+ Realtek SoCs. It provides shared reset control operations
+ (assert, deassert, status) and a registration helper function
+ that other Realtek-specific reset drivers can use.
diff --git a/drivers/reset/realtek/Makefile b/drivers/reset/realtek/Makefile
new file mode 100644
index 000000000000..6d68e41748bf
--- /dev/null
+++ b/drivers/reset/realtek/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_RESET_RTK_COMMON) += reset-rtk-common.o
diff --git a/drivers/reset/realtek/reset-rtk-common.c b/drivers/reset/realtek/reset-rtk-common.c
new file mode 100644
index 000000000000..75b27cb2a208
--- /dev/null
+++ b/drivers/reset/realtek/reset-rtk-common.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2019-2026 Realtek Semiconductor Corporation
+ */
+
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include "reset-rtk-common.h"
+
+static inline struct rtk_reset_data *to_rtk_reset_controller(struct reset_controller_dev *r)
+{
+ return container_of(r, struct rtk_reset_data, rcdev);
+}
+
+static inline const struct rtk_reset_desc *rtk_reset_get_desc(struct rtk_reset_data *data,
+ unsigned long idx)
+{
+ return &data->descs[idx];
+}
+
+static int rtk_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long idx)
+{
+ struct rtk_reset_data *data = to_rtk_reset_controller(rcdev);
+ const struct rtk_reset_desc *desc;
+ u32 mask, val;
+
+ desc = rtk_reset_get_desc(data, idx);
+ mask = desc->write_en ? (0x3U << desc->bit) : BIT(desc->bit);
+ val = desc->write_en ? (0x2U << desc->bit) : 0;
+
+ return regmap_update_bits(data->regmap, desc->ofs, mask, val);
+}
+
+static int rtk_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long idx)
+{
+ struct rtk_reset_data *data = to_rtk_reset_controller(rcdev);
+ const struct rtk_reset_desc *desc;
+ u32 mask, val;
+
+ desc = rtk_reset_get_desc(data, idx);
+ mask = desc->write_en ? (0x3U << desc->bit) : BIT(desc->bit);
+ val = mask;
+
+ return regmap_update_bits(data->regmap, desc->ofs, mask, val);
+}
+
+static int rtk_reset_status(struct reset_controller_dev *rcdev,
+ unsigned long idx)
+{
+ struct rtk_reset_data *data = to_rtk_reset_controller(rcdev);
+ const struct rtk_reset_desc *desc;
+ u32 val;
+ int ret;
+
+ desc = rtk_reset_get_desc(data, idx);
+ ret = regmap_read(data->regmap, desc->ofs, &val);
+ if (ret)
+ return ret;
+
+ return !((val >> desc->bit) & 1);
+}
+
+static const struct reset_control_ops rtk_reset_ops = {
+ .assert = rtk_reset_assert,
+ .deassert = rtk_reset_deassert,
+ .status = rtk_reset_status,
+};
+
+/* The caller must initialize data->descs, data->rcdev.nr_resets and
+ * data->rcdev.owner before calling rtk_reset_controller_add().
+ */
+int rtk_reset_controller_add(struct device *dev,
+ struct rtk_reset_data *data)
+{
+ data->regmap = dev_get_platdata(dev);
+ data->rcdev.ops = &rtk_reset_ops;
+ data->rcdev.dev = dev;
+ data->rcdev.of_node = dev->parent->of_node;
+
+ return devm_reset_controller_register(dev, &data->rcdev);
+}
+EXPORT_SYMBOL_NS_GPL(rtk_reset_controller_add, "REALTEK_RESET");
+
+MODULE_DESCRIPTION("realtek reset infrastructure");
+MODULE_LICENSE("GPL");
diff --git a/drivers/reset/realtek/reset-rtk-common.h b/drivers/reset/realtek/reset-rtk-common.h
new file mode 100644
index 000000000000..42eb41eae2ec
--- /dev/null
+++ b/drivers/reset/realtek/reset-rtk-common.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2026 Realtek Semiconductor Corporation
+ * Author: Yu-Chun Lin <eleanor.lin@realtek.com>
+ */
+
+#ifndef __RESET_REALTEK_COMMON_H
+#define __RESET_REALTEK_COMMON_H
+
+#include <linux/reset-controller.h>
+
+struct regmap;
+
+struct rtk_reset_desc {
+ u32 ofs;
+ u32 bit;
+ bool write_en;
+};
+
+struct rtk_reset_data {
+ struct reset_controller_dev rcdev;
+ const struct rtk_reset_desc *descs;
+ struct regmap *regmap;
+};
+
+int rtk_reset_controller_add(struct device *dev,
+ struct rtk_reset_data *initdata);
+
+#endif /* __RESET_REALTEK_COMMON_H */
--
2.43.0
^ permalink raw reply related
* [PATCH v9 12/12] arm64: dts: realtek: Add clock support for RTD1625
From: Yu-Chun Lin @ 2026-06-24 11:29 UTC (permalink / raw)
To: mturquette, sboyd, robh, krzk+dt, conor+dt, p.zabel, cylee12,
afaerber, jyanchou, bmasney
Cc: devicetree, linux-clk, linux-kernel, linux-arm-kernel,
linux-realtek-soc, james.tai, cy.huang, stanley_chang,
eleanor.lin
In-Reply-To: <20260624112940.3475605-1-eleanor.lin@realtek.com>
Add the clock controller nodes and osc27m fixed clock for the
Realtek RTD1625 SoC.
Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
---
Changes in v9:
- None.
---
arch/arm64/boot/dts/realtek/kent.dtsi | 33 +++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/arch/arm64/boot/dts/realtek/kent.dtsi b/arch/arm64/boot/dts/realtek/kent.dtsi
index 8d4293cd4c03..409d46a73c91 100644
--- a/arch/arm64/boot/dts/realtek/kent.dtsi
+++ b/arch/arm64/boot/dts/realtek/kent.dtsi
@@ -26,6 +26,15 @@ timer {
<GIC_PPI 9 IRQ_TYPE_LEVEL_HIGH>;
};
+ clocks {
+ osc27m: osc {
+ compatible = "fixed-clock";
+ clock-frequency = <27000000>;
+ clock-output-names = "osc27m";
+ #clock-cells = <0>;
+ };
+ };
+
cpus {
#address-cells = <1>;
#size-cells = <0>;
@@ -141,6 +150,22 @@ rbus: bus@98000000 {
#address-cells = <1>;
#size-cells = <1>;
+ cc: clock-controller@0 {
+ compatible = "realtek,rtd1625-crt-clk";
+ reg = <0x0 0x900>;
+ clocks = <&osc27m>;
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ };
+
+ ic: clock-controller@7088 {
+ compatible = "realtek,rtd1625-iso-clk";
+ reg = <0x7088 0x8>;
+ clocks = <&osc27m>;
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ };
+
uart0: serial@7800 {
compatible = "snps,dw-apb-uart";
reg = <0x7800 0x100>;
@@ -166,6 +191,14 @@ isom_pinctrl: pinctrl@146200 {
reg = <0x146200 0x34>;
};
+ iso_s_cc: clock-controller@146310 {
+ compatible = "realtek,rtd1625-iso-s-clk";
+ reg = <0x146310 0x8>;
+ clocks = <&osc27m>;
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ };
+
ve4_pinctrl: pinctrl@14e000 {
compatible = "realtek,rtd1625-ve4-pinctrl";
reg = <0x14e000 0x84>;
--
2.43.0
^ permalink raw reply related
* [PATCH v9 10/12] clk: realtek: Add RTD1625-CRT clock controller driver
From: Yu-Chun Lin @ 2026-06-24 11:29 UTC (permalink / raw)
To: mturquette, sboyd, robh, krzk+dt, conor+dt, p.zabel, cylee12,
afaerber, jyanchou, bmasney
Cc: devicetree, linux-clk, linux-kernel, linux-arm-kernel,
linux-realtek-soc, james.tai, cy.huang, stanley_chang,
eleanor.lin
In-Reply-To: <20260624112940.3475605-1-eleanor.lin@realtek.com>
From: Cheng-Yu Lee <cylee12@realtek.com>
Add support for the CRT (Clock, Reset, and Test) domain clock controller
on the Realtek RTD1625 SoC. This driver provides essential clock sources
(including PLLs), gating, and multiplexing functionalities for the
platform's peripherals.
Because the reset controller shares the same register space with this
CRT clock controller, this driver also acts as the parent device and
registers the reset controller as an auxiliary device on the auxiliary
bus.
Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
Co-developed-by: Yu-Chun Lin <eleanor.lin@realtek.com>
Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
---
Changes in v9:
- Remove reset-related code to patch 3.
---
drivers/clk/realtek/Kconfig | 15 +
drivers/clk/realtek/Makefile | 1 +
drivers/clk/realtek/clk-rtd1625-crt.c | 792 ++++++++++++++++++++++++++
3 files changed, 808 insertions(+)
create mode 100644 drivers/clk/realtek/clk-rtd1625-crt.c
diff --git a/drivers/clk/realtek/Kconfig b/drivers/clk/realtek/Kconfig
index 2ff780581ae0..94a92b29d891 100644
--- a/drivers/clk/realtek/Kconfig
+++ b/drivers/clk/realtek/Kconfig
@@ -30,4 +30,19 @@ config RTK_CLK_COMMON
config RTK_CLK_PLL_MMC
bool
+config CLK_RTD1625
+ tristate "RTD1625 Clock Controller"
+ depends on RESET_CONTROLLER
+ select RESET_RTD1625
+ select RTK_CLK_COMMON
+ select RTK_CLK_PLL_MMC
+ help
+ Support for the clock controller on Realtek RTD1625 SoCs.
+
+ This driver provides clock sources, gating, multiplexing, and
+ reset control for peripherals on the RTD1625 platform.
+
+ Say Y here if your system is based on the RTD1625 and you need
+ its peripheral devices to function.
+
endif
diff --git a/drivers/clk/realtek/Makefile b/drivers/clk/realtek/Makefile
index 97447e92bc35..15b9eec74e36 100644
--- a/drivers/clk/realtek/Makefile
+++ b/drivers/clk/realtek/Makefile
@@ -8,3 +8,4 @@ clk-rtk-y += clk-regmap-gate.o
clk-rtk-y += clk-regmap-mux.o
clk-rtk-$(CONFIG_RTK_CLK_PLL_MMC) += clk-pll-mmc.o
+obj-$(CONFIG_CLK_RTD1625) += clk-rtd1625-crt.o
diff --git a/drivers/clk/realtek/clk-rtd1625-crt.c b/drivers/clk/realtek/clk-rtd1625-crt.c
new file mode 100644
index 000000000000..6fa1c1f0d4f3
--- /dev/null
+++ b/drivers/clk/realtek/clk-rtd1625-crt.c
@@ -0,0 +1,792 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022-2026 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include <dt-bindings/clock/realtek,rtd1625-clk.h>
+#include <linux/array_size.h>
+#include <linux/bits.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/spinlock.h>
+#include "clk-pll.h"
+#include "clk-regmap-gate.h"
+#include "clk-regmap-mux.h"
+
+#define RTD1625_CRT_CLK_MAX 172
+#define RTD1625_CRT_RSTN_MAX 123
+
+#define RTD1625_REG_PLL_ACPU1 0x10c
+#define RTD1625_REG_PLL_ACPU2 0x110
+#define RTD1625_REG_PLL_SSC_DIG_ACPU0 0x5c0
+#define RTD1625_REG_PLL_SSC_DIG_ACPU1 0x5c4
+#define RTD1625_REG_PLL_SSC_DIG_ACPU2 0x5c8
+#define RTD1625_REG_PLL_SSC_DIG_ACPU_DBG2 0x5dc
+
+#define RTD1625_REG_PLL_VE1_1 0x114
+#define RTD1625_REG_PLL_VE1_2 0x118
+#define RTD1625_REG_PLL_SSC_DIG_VE1_0 0x580
+#define RTD1625_REG_PLL_SSC_DIG_VE1_1 0x584
+#define RTD1625_REG_PLL_SSC_DIG_VE1_2 0x588
+#define RTD1625_REG_PLL_SSC_DIG_VE1_DBG2 0x59c
+
+#define RTD1625_REG_PLL_GPU1 0x1c0
+#define RTD1625_REG_PLL_GPU2 0x1c4
+#define RTD1625_REG_PLL_SSC_DIG_GPU0 0x5a0
+#define RTD1625_REG_PLL_SSC_DIG_GPU1 0x5a4
+#define RTD1625_REG_PLL_SSC_DIG_GPU2 0x5a8
+#define RTD1625_REG_PLL_SSC_DIG_GPU_DBG2 0x5bc
+
+#define RTD1625_REG_PLL_NPU1 0x1c8
+#define RTD1625_REG_PLL_NPU2 0x1cc
+#define RTD1625_REG_PLL_SSC_DIG_NPU0 0x800
+#define RTD1625_REG_PLL_SSC_DIG_NPU1 0x804
+#define RTD1625_REG_PLL_SSC_DIG_NPU2 0x808
+#define RTD1625_REG_PLL_SSC_DIG_NPU_DBG2 0x81c
+
+#define RTD1625_REG_PLL_VE2_1 0x1d0
+#define RTD1625_REG_PLL_VE2_2 0x1d4
+#define RTD1625_REG_PLL_SSC_DIG_VE2_0 0x5e0
+#define RTD1625_REG_PLL_SSC_DIG_VE2_1 0x5e4
+#define RTD1625_REG_PLL_SSC_DIG_VE2_2 0x5e8
+#define RTD1625_REG_PLL_SSC_DIG_VE2_DBG2 0x5fc
+
+#define RTD1625_REG_PLL_HIFI1 0x1d8
+#define RTD1625_REG_PLL_HIFI2 0x1dc
+#define RTD1625_REG_PLL_SSC_DIG_HIFI0 0x6e0
+#define RTD1625_REG_PLL_SSC_DIG_HIFI1 0x6e4
+#define RTD1625_REG_PLL_SSC_DIG_HIFI2 0x6e8
+#define RTD1625_REG_PLL_SSC_DIG_HIFI_DBG2 0x6fc
+
+#define RTD1625_REG_PLL_BUS1 0x524
+
+#define RTD1625_REG_PLL_SSC_DIG_DDSA1 0x564
+
+#define RTD1625_REG_PLL_SSC_DIG_DCSB1 0x544
+
+static const char * const clk_gpu_parents[] = {"pll_gpu", "clk_sys"};
+static CLK_REGMAP_MUX(clk_gpu, clk_gpu_parents, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
+ 0x28, 12, 0x1);
+static const char * const clk_ve_parents[] = {"pll_vo", "clk_sysh", "pll_ve1", "pll_ve2"};
+static CLK_REGMAP_MUX(clk_ve1, clk_ve_parents, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
+ 0x4c, 0, 0x3);
+static CLK_REGMAP_MUX(clk_ve2, clk_ve_parents, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
+ 0x4c, 3, 0x3);
+static CLK_REGMAP_MUX(clk_ve4, clk_ve_parents, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
+ 0x4c, 6, 0x3);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_misc, CLK_IS_CRITICAL, 0x50, 0, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_pcie0, 0, 0x50, 2, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_gspi, 0, 0x50, 6, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_iso_misc, 0, 0x50, 10, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_sds, 0, 0x50, 12, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_hdmi, 0, 0x50, 14, 1);
+static CLK_REGMAP_GATE(clk_en_gpu, "clk_gpu", CLK_SET_RATE_PARENT, 0x50, 18, 1);
+static CLK_REGMAP_GATE(clk_en_ve1, "clk_ve1", CLK_SET_RATE_PARENT, 0x50, 20, 1);
+static CLK_REGMAP_GATE(clk_en_ve2, "clk_ve2", CLK_SET_RATE_PARENT, 0x50, 22, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_se, 0, 0x50, 30, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_md, 0, 0x54, 4, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_tp, CLK_IS_CRITICAL, 0x54, 6, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_rcic, 0, 0x54, 8, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_nf, 0, 0x54, 10, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_emmc, 0, 0x54, 12, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_sd, 0, 0x54, 14, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_sdio_ip, 0, 0x54, 16, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_mipi_csi, 0, 0x54, 18, 1);
+static CLK_REGMAP_GATE(clk_en_emmc_ip, "pll_emmc", CLK_SET_RATE_PARENT, 0x54, 20, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_sdio, 0, 0x54, 22, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_sd_ip, 0, 0x54, 24, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_tpb, 0, 0x54, 28, 1);
+static CLK_REGMAP_GATE(clk_en_misc_sc1, "clk_en_misc", 0, 0x54, 30, 1);
+static CLK_REGMAP_GATE(clk_en_misc_i2c_3, "clk_en_misc", 0, 0x58, 0, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_jpeg, 0, 0x58, 4, 1);
+static CLK_REGMAP_GATE(clk_en_acpu, "pll_acpu", CLK_SET_RATE_PARENT,
+ 0x58, 6, 1);
+static CLK_REGMAP_GATE(clk_en_misc_sc0, "clk_en_misc", 0, 0x58, 10, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_hdmirx, 0, 0x58, 26, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_hse, CLK_IS_CRITICAL, 0x58, 28, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_fan, 0, 0x5c, 2, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_sata_wrap_sys, 0, 0x5c, 8, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_sata_wrap_sysh, 0, 0x5c, 10, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_sata_mac_sysh, 0, 0x5c, 12, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_r2rdsc, 0, 0x5c, 14, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_pcie1, 0, 0x5c, 18, 1);
+static CLK_REGMAP_GATE(clk_en_misc_i2c_4, "clk_en_misc", 0, 0x5c, 20, 1);
+static CLK_REGMAP_GATE(clk_en_misc_i2c_5, "clk_en_misc", 0, 0x5c, 22, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_tsio, 0, 0x5c, 24, 1);
+static CLK_REGMAP_GATE(clk_en_ve4, "clk_ve4", CLK_SET_RATE_PARENT,
+ 0x5c, 26, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_edp, 0, 0x5c, 28, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_tsio_trx, 0, 0x5c, 30, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_pcie2, 0, 0x8c, 0, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_earc, 0, 0x8c, 4, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_lite, 0, 0x8c, 6, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_mipi_dsi, 0, 0x8c, 8, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_npupp, 0, 0x8c, 10, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_npu, 0, 0x8c, 12, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_aucpu0, 0, 0x8c, 14, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_aucpu1, 0, 0x8c, 16, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_nsram, 0, 0x8c, 18, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_hdmitop, 0, 0x8c, 20, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_aucpu_iso_npu, 0, 0x8c, 24, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_keyladder, 0, 0x8c, 26, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_ifcp_klm, 0, 0x8c, 28, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_ifcp, 0, 0x8c, 30, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_mdl_genpw, 0, 0xb0, 0, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_mdl_chip, 0, 0xb0, 2, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_mdl_ip, 0, 0xb0, 4, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_mdlm2m, 0, 0xb0, 6, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_mdl_xtal, 0, 0xb0, 8, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_test_mux, 0, 0xb0, 10, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_dla, 0, 0xb0, 12, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_tpcw, 0, 0xb0, 16, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_gpu_ts_src, 0, 0xb0, 18, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_vi, 0, 0xb0, 22, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_lvds1, 0, 0xb0, 24, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_lvds2, 0, 0xb0, 26, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_aucpu, 0, 0xb0, 28, 1);
+static CLK_REGMAP_GATE(clk_en_ur1, "clk_en_ur_top", 0, 0x884, 0, 1);
+static CLK_REGMAP_GATE(clk_en_ur2, "clk_en_ur_top", 0, 0x884, 2, 1);
+static CLK_REGMAP_GATE(clk_en_ur3, "clk_en_ur_top", 0, 0x884, 4, 1);
+static CLK_REGMAP_GATE(clk_en_ur4, "clk_en_ur_top", 0, 0x884, 6, 1);
+static CLK_REGMAP_GATE(clk_en_ur5, "clk_en_ur_top", 0, 0x884, 8, 1);
+static CLK_REGMAP_GATE(clk_en_ur6, "clk_en_ur_top", 0, 0x884, 10, 1);
+static CLK_REGMAP_GATE(clk_en_ur7, "clk_en_ur_top", 0, 0x884, 12, 1);
+static CLK_REGMAP_GATE(clk_en_ur8, "clk_en_ur_top", 0, 0x884, 14, 1);
+static CLK_REGMAP_GATE(clk_en_ur9, "clk_en_ur_top", 0, 0x884, 16, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_ur_top, CLK_IS_CRITICAL, 0x884, 18, 1);
+static CLK_REGMAP_GATE(clk_en_misc_i2c_7, "clk_en_misc", 0, 0x884, 28, 1);
+static CLK_REGMAP_GATE(clk_en_misc_i2c_6, "clk_en_misc", 0, 0x884, 30, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_spi0, 0, 0x894, 0, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_spi1, 0, 0x894, 2, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_spi2, 0, 0x894, 4, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_lsadc0, 0, 0x894, 16, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_lsadc1, 0, 0x894, 18, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_isomis_dma, 0, 0x894, 20, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_dptx, 0, 0x894, 24, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_npu_mipi_csi, 0, 0x894, 26, 1);
+static CLK_REGMAP_GATE_NO_PARENT(clk_en_edptx, 0, 0x894, 28, 1);
+
+#define FREQ_NF_MASK 0x7ffff
+#define FREQ_NF(_r, _nf) {.rate = _r, .val = (_nf),}
+
+static const struct freq_table acpu_tbl[] = {
+ FREQ_NF(513000000, 0x11000),
+ FREQ_TABLE_END
+};
+
+static const struct freq_table ve_tbl[] = {
+ FREQ_NF(553500000, 0x12800),
+ FREQ_NF(661500000, 0x16800),
+ FREQ_NF(688500000, 0x17800),
+ FREQ_TABLE_END
+};
+
+static const struct freq_table bus_tbl[] = {
+ FREQ_NF(513000000, 0x11000),
+ FREQ_NF(540000000, 0x12000),
+ FREQ_NF(553500000, 0x12800),
+ FREQ_TABLE_END
+};
+
+static const struct freq_table ddsa_tbl[] = {
+ FREQ_NF(432000000, 0xe000),
+ FREQ_TABLE_END
+};
+
+static const struct freq_table gpu_tbl[] = {
+ FREQ_NF(405000000, 0xd000),
+ FREQ_NF(540000000, 0x12000),
+ FREQ_NF(661500000, 0x16800),
+ FREQ_NF(729000000, 0x19000),
+ FREQ_NF(810000000, 0x1c000),
+ FREQ_NF(850500000, 0x1d800),
+ FREQ_TABLE_END
+};
+
+static const struct freq_table hifi_tbl[] = {
+ FREQ_NF(756000000, 0x1a000),
+ FREQ_NF(810000000, 0x1c000),
+ FREQ_TABLE_END
+};
+
+static const struct freq_table npu_tbl[] = {
+ FREQ_NF(661500000, 0x16800),
+ FREQ_NF(729000000, 0x19000),
+ FREQ_NF(810000000, 0x1c000),
+ FREQ_TABLE_END
+};
+
+static const struct reg_sequence pll_acpu_seq_power_on[] = {
+ {RTD1625_REG_PLL_ACPU2, 0x5},
+ {RTD1625_REG_PLL_ACPU2, 0x7},
+ {RTD1625_REG_PLL_ACPU1, 0x54000},
+ {RTD1625_REG_PLL_SSC_DIG_ACPU2, 0x1e1f8e},
+ {RTD1625_REG_PLL_SSC_DIG_ACPU0, 0x4},
+ {RTD1625_REG_PLL_SSC_DIG_ACPU0, 0x5, 200},
+ {RTD1625_REG_PLL_ACPU2, 0x3},
+};
+
+static const struct reg_sequence pll_acpu_seq_power_off[] = {
+ {RTD1625_REG_PLL_ACPU2, 0x4},
+};
+
+static const struct reg_sequence pll_acpu_seq_pre_set_freq[] = {
+ {RTD1625_REG_PLL_SSC_DIG_ACPU0, 0x4},
+};
+
+static const struct reg_sequence pll_acpu_seq_post_set_freq[] = {
+ {RTD1625_REG_PLL_SSC_DIG_ACPU0, 0x5},
+};
+
+static struct clk_pll pll_acpu = {
+ .clkr.hw.init = CLK_HW_INIT("pll_acpu", "osc27m", &rtk_clk_pll_ops, CLK_GET_RATE_NOCACHE),
+ .seq_power_on = pll_acpu_seq_power_on,
+ .num_seq_power_on = ARRAY_SIZE(pll_acpu_seq_power_on),
+ .seq_power_off = pll_acpu_seq_power_off,
+ .num_seq_power_off = ARRAY_SIZE(pll_acpu_seq_power_off),
+ .seq_pre_set_freq = pll_acpu_seq_pre_set_freq,
+ .num_seq_pre_set_freq = ARRAY_SIZE(pll_acpu_seq_pre_set_freq),
+ .seq_post_set_freq = pll_acpu_seq_post_set_freq,
+ .num_seq_post_set_freq = ARRAY_SIZE(pll_acpu_seq_post_set_freq),
+ .freq_reg = RTD1625_REG_PLL_SSC_DIG_ACPU1,
+ .freq_tbl = acpu_tbl,
+ .freq_mask = FREQ_NF_MASK,
+ .freq_ready_reg = RTD1625_REG_PLL_SSC_DIG_ACPU_DBG2,
+ .freq_ready_mask = BIT(20),
+ .freq_ready_val = BIT(20),
+ .power_reg = RTD1625_REG_PLL_ACPU2,
+ .power_mask = 0x7,
+ .power_val_on = 0x3,
+ .lock = __SPIN_LOCK_UNLOCKED(pll_acpu.lock),
+};
+
+static const struct reg_sequence pll_ve1_seq_power_on[] = {
+ {RTD1625_REG_PLL_VE1_2, 0x5},
+ {RTD1625_REG_PLL_VE1_2, 0x7},
+ {RTD1625_REG_PLL_VE1_1, 0x54000},
+ {RTD1625_REG_PLL_SSC_DIG_VE1_0, 0x4},
+ {RTD1625_REG_PLL_SSC_DIG_VE1_0, 0x5, 200},
+ {RTD1625_REG_PLL_VE1_2, 0x3},
+};
+
+static const struct reg_sequence pll_ve1_seq_power_off[] = {
+ {RTD1625_REG_PLL_VE1_2, 0x4},
+};
+
+static const struct reg_sequence pll_ve1_seq_pre_set_freq[] = {
+ {RTD1625_REG_PLL_SSC_DIG_VE1_0, 0x4},
+};
+
+static const struct reg_sequence pll_ve1_seq_post_set_freq[] = {
+ {RTD1625_REG_PLL_SSC_DIG_VE1_0, 0x5},
+};
+
+static struct clk_pll pll_ve1 = {
+ .clkr.hw.init = CLK_HW_INIT("pll_ve1", "osc27m", &rtk_clk_pll_ops, CLK_GET_RATE_NOCACHE),
+ .seq_power_on = pll_ve1_seq_power_on,
+ .num_seq_power_on = ARRAY_SIZE(pll_ve1_seq_power_on),
+ .seq_power_off = pll_ve1_seq_power_off,
+ .num_seq_power_off = ARRAY_SIZE(pll_ve1_seq_power_off),
+ .seq_pre_set_freq = pll_ve1_seq_pre_set_freq,
+ .num_seq_pre_set_freq = ARRAY_SIZE(pll_ve1_seq_pre_set_freq),
+ .seq_post_set_freq = pll_ve1_seq_post_set_freq,
+ .num_seq_post_set_freq = ARRAY_SIZE(pll_ve1_seq_post_set_freq),
+ .freq_reg = RTD1625_REG_PLL_SSC_DIG_VE1_1,
+ .freq_tbl = ve_tbl,
+ .freq_mask = FREQ_NF_MASK,
+ .freq_ready_reg = RTD1625_REG_PLL_SSC_DIG_VE1_DBG2,
+ .freq_ready_mask = BIT(20),
+ .freq_ready_val = BIT(20),
+ .power_reg = RTD1625_REG_PLL_VE1_2,
+ .power_mask = 0x7,
+ .power_val_on = 0x3,
+ .lock = __SPIN_LOCK_UNLOCKED(pll_ve1.lock),
+};
+
+static struct clk_pll pll_ddsa = {
+ .clkr.hw.init = CLK_HW_INIT("pll_ddsa", "osc27m", &rtk_clk_pll_ro_ops,
+ CLK_GET_RATE_NOCACHE),
+ .freq_reg = RTD1625_REG_PLL_SSC_DIG_DDSA1,
+ .freq_tbl = ddsa_tbl,
+ .freq_mask = FREQ_NF_MASK,
+ .lock = __SPIN_LOCK_UNLOCKED(pll_ddsa.lock),
+};
+
+static struct clk_pll pll_bus = {
+ .clkr.hw.init = CLK_HW_INIT("pll_bus", "osc27m", &rtk_clk_pll_ro_ops,
+ CLK_GET_RATE_NOCACHE),
+ .freq_reg = RTD1625_REG_PLL_BUS1,
+ .freq_tbl = bus_tbl,
+ .freq_mask = FREQ_NF_MASK,
+ .lock = __SPIN_LOCK_UNLOCKED(pll_bus.lock),
+};
+
+static CLK_FIXED_FACTOR(clk_sys, "clk_sys", "pll_bus", 2, 1, 0);
+
+static struct clk_pll pll_dcsb = {
+ .clkr.hw.init = CLK_HW_INIT("pll_dcsb", "osc27m", &rtk_clk_pll_ro_ops,
+ CLK_GET_RATE_NOCACHE),
+ .freq_reg = RTD1625_REG_PLL_SSC_DIG_DCSB1,
+ .freq_tbl = bus_tbl,
+ .freq_mask = FREQ_NF_MASK,
+ .lock = __SPIN_LOCK_UNLOCKED(pll_dcsb.lock),
+};
+
+static CLK_FIXED_FACTOR(clk_sysh, "clk_sysh", "pll_dcsb", 1, 1, 0);
+
+static const struct reg_sequence pll_gpu_seq_power_on[] = {
+ {RTD1625_REG_PLL_GPU2, 0x5},
+ {RTD1625_REG_PLL_GPU2, 0x7},
+ {RTD1625_REG_PLL_GPU1, 0x54000},
+ {RTD1625_REG_PLL_SSC_DIG_GPU0, 0x4},
+ {RTD1625_REG_PLL_SSC_DIG_GPU0, 0x5, 200},
+ {RTD1625_REG_PLL_GPU2, 0x3},
+};
+
+static const struct reg_sequence pll_gpu_seq_power_off[] = {
+ {RTD1625_REG_PLL_GPU2, 0x4},
+};
+
+static const struct reg_sequence pll_gpu_seq_pre_set_freq[] = {
+ {RTD1625_REG_PLL_SSC_DIG_GPU0, 0x4},
+};
+
+static const struct reg_sequence pll_gpu_seq_post_set_freq[] = {
+ {RTD1625_REG_PLL_SSC_DIG_GPU0, 0x5},
+};
+
+static struct clk_pll pll_gpu = {
+ .clkr.hw.init = CLK_HW_INIT("pll_gpu", "osc27m", &rtk_clk_pll_ops, CLK_GET_RATE_NOCACHE),
+ .seq_power_on = pll_gpu_seq_power_on,
+ .num_seq_power_on = ARRAY_SIZE(pll_gpu_seq_power_on),
+ .seq_power_off = pll_gpu_seq_power_off,
+ .num_seq_power_off = ARRAY_SIZE(pll_gpu_seq_power_off),
+ .seq_pre_set_freq = pll_gpu_seq_pre_set_freq,
+ .num_seq_pre_set_freq = ARRAY_SIZE(pll_gpu_seq_pre_set_freq),
+ .seq_post_set_freq = pll_gpu_seq_post_set_freq,
+ .num_seq_post_set_freq = ARRAY_SIZE(pll_gpu_seq_post_set_freq),
+ .freq_reg = RTD1625_REG_PLL_SSC_DIG_GPU1,
+ .freq_tbl = gpu_tbl,
+ .freq_mask = FREQ_NF_MASK,
+ .freq_ready_reg = RTD1625_REG_PLL_SSC_DIG_GPU_DBG2,
+ .freq_ready_mask = BIT(20),
+ .freq_ready_val = BIT(20),
+ .power_reg = RTD1625_REG_PLL_GPU2,
+ .power_mask = 0x7,
+ .power_val_on = 0x3,
+ .lock = __SPIN_LOCK_UNLOCKED(pll_gpu.lock),
+};
+
+static const struct reg_sequence pll_npu_seq_power_on[] = {
+ {RTD1625_REG_PLL_NPU2, 0x5},
+ {RTD1625_REG_PLL_NPU2, 0x7},
+ {RTD1625_REG_PLL_NPU1, 0x54000},
+ {RTD1625_REG_PLL_SSC_DIG_NPU0, 0x4},
+ {RTD1625_REG_PLL_SSC_DIG_NPU0, 0x5, 200},
+ {RTD1625_REG_PLL_NPU2, 0x3},
+};
+
+static const struct reg_sequence pll_npu_seq_power_off[] = {
+ {RTD1625_REG_PLL_NPU2, 0x4},
+ {RTD1625_REG_PLL_NPU1, 0x54010},
+};
+
+static const struct reg_sequence pll_npu_seq_pre_set_freq[] = {
+ {RTD1625_REG_PLL_SSC_DIG_NPU0, 0x4},
+};
+
+static const struct reg_sequence pll_npu_seq_post_set_freq[] = {
+ {RTD1625_REG_PLL_SSC_DIG_NPU0, 0x5},
+};
+
+static struct clk_pll pll_npu = {
+ .clkr.hw.init = CLK_HW_INIT("pll_npu", "osc27m", &rtk_clk_pll_ops,
+ CLK_GET_RATE_NOCACHE),
+ .seq_power_on = pll_npu_seq_power_on,
+ .num_seq_power_on = ARRAY_SIZE(pll_npu_seq_power_on),
+ .seq_power_off = pll_npu_seq_power_off,
+ .num_seq_power_off = ARRAY_SIZE(pll_npu_seq_power_off),
+ .seq_pre_set_freq = pll_npu_seq_pre_set_freq,
+ .num_seq_pre_set_freq = ARRAY_SIZE(pll_npu_seq_pre_set_freq),
+ .seq_post_set_freq = pll_npu_seq_post_set_freq,
+ .num_seq_post_set_freq = ARRAY_SIZE(pll_npu_seq_post_set_freq),
+ .freq_reg = RTD1625_REG_PLL_SSC_DIG_NPU1,
+ .freq_tbl = npu_tbl,
+ .freq_mask = FREQ_NF_MASK,
+ .freq_ready_reg = RTD1625_REG_PLL_SSC_DIG_NPU_DBG2,
+ .freq_ready_mask = BIT(20),
+ .freq_ready_val = BIT(20),
+ .power_reg = RTD1625_REG_PLL_NPU2,
+ .power_mask = 0x7,
+ .power_val_on = 0x3,
+ .lock = __SPIN_LOCK_UNLOCKED(pll_npu.lock),
+};
+
+static CLK_FIXED_FACTOR(clk_npu, "clk_npu", "pll_npu", 1, 1, CLK_SET_RATE_PARENT);
+static CLK_FIXED_FACTOR(clk_npu_mipi_csi, "clk_npu_mipi_csi", "pll_npu", 1, 1,
+ CLK_SET_RATE_PARENT);
+
+static const struct reg_sequence pll_ve2_seq_power_on[] = {
+ {RTD1625_REG_PLL_VE2_2, 0x5},
+ {RTD1625_REG_PLL_VE2_2, 0x7},
+ {RTD1625_REG_PLL_VE2_1, 0x54000},
+ {RTD1625_REG_PLL_SSC_DIG_VE2_0, 0x4},
+ {RTD1625_REG_PLL_SSC_DIG_VE2_0, 0x5, 200},
+ {RTD1625_REG_PLL_VE2_2, 0x3},
+};
+
+static const struct reg_sequence pll_ve2_seq_power_off[] = {
+ {RTD1625_REG_PLL_VE2_2, 0x4},
+};
+
+static const struct reg_sequence pll_ve2_seq_pre_set_freq[] = {
+ {RTD1625_REG_PLL_SSC_DIG_VE2_0, 0x4},
+};
+
+static const struct reg_sequence pll_ve2_seq_post_set_freq[] = {
+ {RTD1625_REG_PLL_SSC_DIG_VE2_0, 0x5},
+};
+
+static struct clk_pll pll_ve2 = {
+ .clkr.hw.init = CLK_HW_INIT("pll_ve2", "osc27m", &rtk_clk_pll_ops, CLK_GET_RATE_NOCACHE),
+ .seq_power_on = pll_ve2_seq_power_on,
+ .num_seq_power_on = ARRAY_SIZE(pll_ve2_seq_power_on),
+ .seq_power_off = pll_ve2_seq_power_off,
+ .num_seq_power_off = ARRAY_SIZE(pll_ve2_seq_power_off),
+ .seq_pre_set_freq = pll_ve2_seq_pre_set_freq,
+ .num_seq_pre_set_freq = ARRAY_SIZE(pll_ve2_seq_pre_set_freq),
+ .seq_post_set_freq = pll_ve2_seq_post_set_freq,
+ .num_seq_post_set_freq = ARRAY_SIZE(pll_ve2_seq_post_set_freq),
+ .freq_reg = RTD1625_REG_PLL_SSC_DIG_VE2_1,
+ .freq_tbl = ve_tbl,
+ .freq_mask = FREQ_NF_MASK,
+ .freq_ready_reg = RTD1625_REG_PLL_SSC_DIG_VE2_DBG2,
+ .freq_ready_mask = BIT(20),
+ .freq_ready_val = BIT(20),
+ .power_reg = RTD1625_REG_PLL_VE2_2,
+ .power_mask = 0x7,
+ .power_val_on = 0x3,
+ .lock = __SPIN_LOCK_UNLOCKED(pll_ve2.lock),
+};
+
+static const struct reg_sequence pll_hifi_seq_power_on[] = {
+ {RTD1625_REG_PLL_HIFI2, 0x5},
+ {RTD1625_REG_PLL_HIFI2, 0x7},
+ {RTD1625_REG_PLL_HIFI1, 0x54000},
+ {RTD1625_REG_PLL_SSC_DIG_HIFI0, 0x4},
+ {RTD1625_REG_PLL_SSC_DIG_HIFI0, 0x5, 200},
+ {RTD1625_REG_PLL_HIFI2, 0x3},
+};
+
+static const struct reg_sequence pll_hifi_seq_power_off[] = {
+ {RTD1625_REG_PLL_HIFI2, 0x4},
+};
+
+static const struct reg_sequence pll_hifi_seq_pre_set_freq[] = {
+ {RTD1625_REG_PLL_SSC_DIG_HIFI0, 0x4},
+};
+
+static const struct reg_sequence pll_hifi_seq_post_set_freq[] = {
+ {RTD1625_REG_PLL_SSC_DIG_HIFI0, 0x5},
+};
+
+static struct clk_pll pll_hifi = {
+ .clkr.hw.init = CLK_HW_INIT("pll_hifi", "osc27m", &rtk_clk_pll_ops, CLK_GET_RATE_NOCACHE),
+ .seq_power_on = pll_hifi_seq_power_on,
+ .num_seq_power_on = ARRAY_SIZE(pll_hifi_seq_power_on),
+ .seq_power_off = pll_hifi_seq_power_off,
+ .num_seq_power_off = ARRAY_SIZE(pll_hifi_seq_power_off),
+ .seq_pre_set_freq = pll_hifi_seq_pre_set_freq,
+ .num_seq_pre_set_freq = ARRAY_SIZE(pll_hifi_seq_pre_set_freq),
+ .seq_post_set_freq = pll_hifi_seq_post_set_freq,
+ .num_seq_post_set_freq = ARRAY_SIZE(pll_hifi_seq_post_set_freq),
+ .freq_reg = RTD1625_REG_PLL_SSC_DIG_HIFI1,
+ .freq_tbl = hifi_tbl,
+ .freq_mask = FREQ_NF_MASK,
+ .freq_ready_reg = RTD1625_REG_PLL_SSC_DIG_HIFI_DBG2,
+ .freq_ready_mask = BIT(20),
+ .freq_ready_val = BIT(20),
+ .power_reg = RTD1625_REG_PLL_HIFI2,
+ .power_mask = 0x7,
+ .power_val_on = 0x3,
+ .lock = __SPIN_LOCK_UNLOCKED(pll_hifi.lock),
+};
+
+static CLK_FIXED_FACTOR(pll_emmc_ref, "pll_emmc_ref", "osc27m", 1, 1, 0);
+
+static struct clk_pll_mmc pll_emmc = {
+ .pll_ofs = 0x1f0,
+ .ssc_dig_ofs = 0x6b0,
+ .clkr.hw.init = CLK_HW_INIT("pll_emmc", "pll_emmc_ref", &rtk_clk_pll_mmc_ops, 0),
+ .phase0_hw.init = CLK_HW_INIT("pll_emmc_vp0", "pll_emmc", &rtk_clk_pll_mmc_phase_ops, 0),
+ .phase1_hw.init = CLK_HW_INIT("pll_emmc_vp1", "pll_emmc", &rtk_clk_pll_mmc_phase_ops, 0),
+};
+
+static struct clk_regmap * const rtd1625_crt_regmap_clks[] = {
+ &clk_en_misc.clkr,
+ &clk_en_pcie0.clkr,
+ &clk_en_gspi.clkr,
+ &clk_en_iso_misc.clkr,
+ &clk_en_sds.clkr,
+ &clk_en_hdmi.clkr,
+ &clk_en_gpu.clkr,
+ &clk_en_ve1.clkr,
+ &clk_en_ve2.clkr,
+ &clk_en_se.clkr,
+ &clk_en_md.clkr,
+ &clk_en_tp.clkr,
+ &clk_en_rcic.clkr,
+ &clk_en_nf.clkr,
+ &clk_en_emmc.clkr,
+ &clk_en_sd.clkr,
+ &clk_en_sdio_ip.clkr,
+ &clk_en_mipi_csi.clkr,
+ &clk_en_emmc_ip.clkr,
+ &clk_en_sdio.clkr,
+ &clk_en_sd_ip.clkr,
+ &clk_en_tpb.clkr,
+ &clk_en_misc_sc1.clkr,
+ &clk_en_misc_i2c_3.clkr,
+ &clk_en_jpeg.clkr,
+ &clk_en_acpu.clkr,
+ &clk_en_misc_sc0.clkr,
+ &clk_en_hdmirx.clkr,
+ &clk_en_hse.clkr,
+ &clk_en_fan.clkr,
+ &clk_en_sata_wrap_sys.clkr,
+ &clk_en_sata_wrap_sysh.clkr,
+ &clk_en_sata_mac_sysh.clkr,
+ &clk_en_r2rdsc.clkr,
+ &clk_en_pcie1.clkr,
+ &clk_en_misc_i2c_4.clkr,
+ &clk_en_misc_i2c_5.clkr,
+ &clk_en_tsio.clkr,
+ &clk_en_ve4.clkr,
+ &clk_en_edp.clkr,
+ &clk_en_tsio_trx.clkr,
+ &clk_en_pcie2.clkr,
+ &clk_en_earc.clkr,
+ &clk_en_lite.clkr,
+ &clk_en_mipi_dsi.clkr,
+ &clk_en_npupp.clkr,
+ &clk_en_npu.clkr,
+ &clk_en_aucpu0.clkr,
+ &clk_en_aucpu1.clkr,
+ &clk_en_nsram.clkr,
+ &clk_en_hdmitop.clkr,
+ &clk_en_aucpu_iso_npu.clkr,
+ &clk_en_keyladder.clkr,
+ &clk_en_ifcp_klm.clkr,
+ &clk_en_ifcp.clkr,
+ &clk_en_mdl_genpw.clkr,
+ &clk_en_mdl_chip.clkr,
+ &clk_en_mdl_ip.clkr,
+ &clk_en_mdlm2m.clkr,
+ &clk_en_mdl_xtal.clkr,
+ &clk_en_test_mux.clkr,
+ &clk_en_dla.clkr,
+ &clk_en_tpcw.clkr,
+ &clk_en_gpu_ts_src.clkr,
+ &clk_en_vi.clkr,
+ &clk_en_lvds1.clkr,
+ &clk_en_lvds2.clkr,
+ &clk_en_aucpu.clkr,
+ &clk_en_ur1.clkr,
+ &clk_en_ur2.clkr,
+ &clk_en_ur3.clkr,
+ &clk_en_ur4.clkr,
+ &clk_en_ur5.clkr,
+ &clk_en_ur6.clkr,
+ &clk_en_ur7.clkr,
+ &clk_en_ur8.clkr,
+ &clk_en_ur9.clkr,
+ &clk_en_ur_top.clkr,
+ &clk_en_misc_i2c_7.clkr,
+ &clk_en_misc_i2c_6.clkr,
+ &clk_en_spi0.clkr,
+ &clk_en_spi1.clkr,
+ &clk_en_spi2.clkr,
+ &clk_en_lsadc0.clkr,
+ &clk_en_lsadc1.clkr,
+ &clk_en_isomis_dma.clkr,
+ &clk_en_dptx.clkr,
+ &clk_en_npu_mipi_csi.clkr,
+ &clk_en_edptx.clkr,
+ &clk_gpu.clkr,
+ &clk_ve1.clkr,
+ &clk_ve2.clkr,
+ &clk_ve4.clkr,
+ &pll_ve1.clkr,
+ &pll_ddsa.clkr,
+ &pll_bus.clkr,
+ &pll_dcsb.clkr,
+ &pll_gpu.clkr,
+ &pll_npu.clkr,
+ &pll_ve2.clkr,
+ &pll_hifi.clkr,
+ &pll_emmc.clkr,
+ &pll_acpu.clkr,
+};
+
+static struct clk_hw_onecell_data rtd1625_crt_hw_data = {
+ .num = RTD1625_CRT_CLK_MAX,
+ .hws = {
+ [RTD1625_CRT_CLK_EN_MISC] = &__clk_regmap_gate_hw(&clk_en_misc),
+ [RTD1625_CRT_CLK_EN_PCIE0] = &__clk_regmap_gate_hw(&clk_en_pcie0),
+ [RTD1625_CRT_CLK_EN_GSPI] = &__clk_regmap_gate_hw(&clk_en_gspi),
+ [RTD1625_CRT_CLK_EN_ISO_MISC] = &__clk_regmap_gate_hw(&clk_en_iso_misc),
+ [RTD1625_CRT_CLK_EN_SDS] = &__clk_regmap_gate_hw(&clk_en_sds),
+ [RTD1625_CRT_CLK_EN_HDMI] = &__clk_regmap_gate_hw(&clk_en_hdmi),
+ [RTD1625_CRT_CLK_EN_GPU] = &__clk_regmap_gate_hw(&clk_en_gpu),
+ [RTD1625_CRT_CLK_EN_VE1] = &__clk_regmap_gate_hw(&clk_en_ve1),
+ [RTD1625_CRT_CLK_EN_VE2] = &__clk_regmap_gate_hw(&clk_en_ve2),
+ [RTD1625_CRT_CLK_EN_MD] = &__clk_regmap_gate_hw(&clk_en_md),
+ [RTD1625_CRT_CLK_EN_TP] = &__clk_regmap_gate_hw(&clk_en_tp),
+ [RTD1625_CRT_CLK_EN_RCIC] = &__clk_regmap_gate_hw(&clk_en_rcic),
+ [RTD1625_CRT_CLK_EN_NF] = &__clk_regmap_gate_hw(&clk_en_nf),
+ [RTD1625_CRT_CLK_EN_EMMC] = &__clk_regmap_gate_hw(&clk_en_emmc),
+ [RTD1625_CRT_CLK_EN_SD] = &__clk_regmap_gate_hw(&clk_en_sd),
+ [RTD1625_CRT_CLK_EN_SDIO_IP] = &__clk_regmap_gate_hw(&clk_en_sdio_ip),
+ [RTD1625_CRT_CLK_EN_MIPI_CSI] = &__clk_regmap_gate_hw(&clk_en_mipi_csi),
+ [RTD1625_CRT_CLK_EN_EMMC_IP] = &__clk_regmap_gate_hw(&clk_en_emmc_ip),
+ [RTD1625_CRT_CLK_EN_SDIO] = &__clk_regmap_gate_hw(&clk_en_sdio),
+ [RTD1625_CRT_CLK_EN_SD_IP] = &__clk_regmap_gate_hw(&clk_en_sd_ip),
+ [RTD1625_CRT_CLK_EN_TPB] = &__clk_regmap_gate_hw(&clk_en_tpb),
+ [RTD1625_CRT_CLK_EN_MISC_SC1] = &__clk_regmap_gate_hw(&clk_en_misc_sc1),
+ [RTD1625_CRT_CLK_EN_MISC_I2C_3] = &__clk_regmap_gate_hw(&clk_en_misc_i2c_3),
+ [RTD1625_CRT_CLK_EN_ACPU] = &__clk_regmap_gate_hw(&clk_en_acpu),
+ [RTD1625_CRT_CLK_EN_JPEG] = &__clk_regmap_gate_hw(&clk_en_jpeg),
+ [RTD1625_CRT_CLK_EN_MISC_SC0] = &__clk_regmap_gate_hw(&clk_en_misc_sc0),
+ [RTD1625_CRT_CLK_EN_HDMIRX] = &__clk_regmap_gate_hw(&clk_en_hdmirx),
+ [RTD1625_CRT_CLK_EN_HSE] = &__clk_regmap_gate_hw(&clk_en_hse),
+ [RTD1625_CRT_CLK_EN_FAN] = &__clk_regmap_gate_hw(&clk_en_fan),
+ [RTD1625_CRT_CLK_EN_SATA_WRAP_SYS] = &__clk_regmap_gate_hw(&clk_en_sata_wrap_sys),
+ [RTD1625_CRT_CLK_EN_SATA_WRAP_SYSH] = &__clk_regmap_gate_hw(&clk_en_sata_wrap_sysh),
+ [RTD1625_CRT_CLK_EN_SATA_MAC_SYSH] = &__clk_regmap_gate_hw(&clk_en_sata_mac_sysh),
+ [RTD1625_CRT_CLK_EN_R2RDSC] = &__clk_regmap_gate_hw(&clk_en_r2rdsc),
+ [RTD1625_CRT_CLK_EN_PCIE1] = &__clk_regmap_gate_hw(&clk_en_pcie1),
+ [RTD1625_CRT_CLK_EN_MISC_I2C_4] = &__clk_regmap_gate_hw(&clk_en_misc_i2c_4),
+ [RTD1625_CRT_CLK_EN_MISC_I2C_5] = &__clk_regmap_gate_hw(&clk_en_misc_i2c_5),
+ [RTD1625_CRT_CLK_EN_TSIO] = &__clk_regmap_gate_hw(&clk_en_tsio),
+ [RTD1625_CRT_CLK_EN_VE4] = &__clk_regmap_gate_hw(&clk_en_ve4),
+ [RTD1625_CRT_CLK_EN_EDP] = &__clk_regmap_gate_hw(&clk_en_edp),
+ [RTD1625_CRT_CLK_EN_TSIO_TRX] = &__clk_regmap_gate_hw(&clk_en_tsio_trx),
+ [RTD1625_CRT_CLK_EN_PCIE2] = &__clk_regmap_gate_hw(&clk_en_pcie2),
+ [RTD1625_CRT_CLK_EN_EARC] = &__clk_regmap_gate_hw(&clk_en_earc),
+ [RTD1625_CRT_CLK_EN_LITE] = &__clk_regmap_gate_hw(&clk_en_lite),
+ [RTD1625_CRT_CLK_EN_MIPI_DSI] = &__clk_regmap_gate_hw(&clk_en_mipi_dsi),
+ [RTD1625_CRT_CLK_EN_NPUPP] = &__clk_regmap_gate_hw(&clk_en_npupp),
+ [RTD1625_CRT_CLK_EN_NPU] = &__clk_regmap_gate_hw(&clk_en_npu),
+ [RTD1625_CRT_CLK_EN_AUCPU0] = &__clk_regmap_gate_hw(&clk_en_aucpu0),
+ [RTD1625_CRT_CLK_EN_AUCPU1] = &__clk_regmap_gate_hw(&clk_en_aucpu1),
+ [RTD1625_CRT_CLK_EN_NSRAM] = &__clk_regmap_gate_hw(&clk_en_nsram),
+ [RTD1625_CRT_CLK_EN_HDMITOP] = &__clk_regmap_gate_hw(&clk_en_hdmitop),
+ [RTD1625_CRT_CLK_EN_AUCPU_ISO_NPU] = &__clk_regmap_gate_hw(&clk_en_aucpu_iso_npu),
+ [RTD1625_CRT_CLK_EN_KEYLADDER] = &__clk_regmap_gate_hw(&clk_en_keyladder),
+ [RTD1625_CRT_CLK_EN_IFCP_KLM] = &__clk_regmap_gate_hw(&clk_en_ifcp_klm),
+ [RTD1625_CRT_CLK_EN_IFCP] = &__clk_regmap_gate_hw(&clk_en_ifcp),
+ [RTD1625_CRT_CLK_EN_MDL_GENPW] = &__clk_regmap_gate_hw(&clk_en_mdl_genpw),
+ [RTD1625_CRT_CLK_EN_MDL_CHIP] = &__clk_regmap_gate_hw(&clk_en_mdl_chip),
+ [RTD1625_CRT_CLK_EN_MDL_IP] = &__clk_regmap_gate_hw(&clk_en_mdl_ip),
+ [RTD1625_CRT_CLK_EN_MDLM2M] = &__clk_regmap_gate_hw(&clk_en_mdlm2m),
+ [RTD1625_CRT_CLK_EN_MDL_XTAL] = &__clk_regmap_gate_hw(&clk_en_mdl_xtal),
+ [RTD1625_CRT_CLK_EN_TEST_MUX] = &__clk_regmap_gate_hw(&clk_en_test_mux),
+ [RTD1625_CRT_CLK_EN_DLA] = &__clk_regmap_gate_hw(&clk_en_dla),
+ [RTD1625_CRT_CLK_EN_TPCW] = &__clk_regmap_gate_hw(&clk_en_tpcw),
+ [RTD1625_CRT_CLK_EN_GPU_TS_SRC] = &__clk_regmap_gate_hw(&clk_en_gpu_ts_src),
+ [RTD1625_CRT_CLK_EN_VI] = &__clk_regmap_gate_hw(&clk_en_vi),
+ [RTD1625_CRT_CLK_EN_LVDS1] = &__clk_regmap_gate_hw(&clk_en_lvds1),
+ [RTD1625_CRT_CLK_EN_LVDS2] = &__clk_regmap_gate_hw(&clk_en_lvds2),
+ [RTD1625_CRT_CLK_EN_AUCPU] = &__clk_regmap_gate_hw(&clk_en_aucpu),
+ [RTD1625_CRT_CLK_EN_UR1] = &__clk_regmap_gate_hw(&clk_en_ur1),
+ [RTD1625_CRT_CLK_EN_UR2] = &__clk_regmap_gate_hw(&clk_en_ur2),
+ [RTD1625_CRT_CLK_EN_UR3] = &__clk_regmap_gate_hw(&clk_en_ur3),
+ [RTD1625_CRT_CLK_EN_UR4] = &__clk_regmap_gate_hw(&clk_en_ur4),
+ [RTD1625_CRT_CLK_EN_UR5] = &__clk_regmap_gate_hw(&clk_en_ur5),
+ [RTD1625_CRT_CLK_EN_UR6] = &__clk_regmap_gate_hw(&clk_en_ur6),
+ [RTD1625_CRT_CLK_EN_UR7] = &__clk_regmap_gate_hw(&clk_en_ur7),
+ [RTD1625_CRT_CLK_EN_UR8] = &__clk_regmap_gate_hw(&clk_en_ur8),
+ [RTD1625_CRT_CLK_EN_UR9] = &__clk_regmap_gate_hw(&clk_en_ur9),
+ [RTD1625_CRT_CLK_EN_UR_TOP] = &__clk_regmap_gate_hw(&clk_en_ur_top),
+ [RTD1625_CRT_CLK_EN_MISC_I2C_7] = &__clk_regmap_gate_hw(&clk_en_misc_i2c_7),
+ [RTD1625_CRT_CLK_EN_MISC_I2C_6] = &__clk_regmap_gate_hw(&clk_en_misc_i2c_6),
+ [RTD1625_CRT_CLK_EN_SPI0] = &__clk_regmap_gate_hw(&clk_en_spi0),
+ [RTD1625_CRT_CLK_EN_SPI1] = &__clk_regmap_gate_hw(&clk_en_spi1),
+ [RTD1625_CRT_CLK_EN_SPI2] = &__clk_regmap_gate_hw(&clk_en_spi2),
+ [RTD1625_CRT_CLK_EN_LSADC0] = &__clk_regmap_gate_hw(&clk_en_lsadc0),
+ [RTD1625_CRT_CLK_EN_LSADC1] = &__clk_regmap_gate_hw(&clk_en_lsadc1),
+ [RTD1625_CRT_CLK_EN_ISOMIS_DMA] = &__clk_regmap_gate_hw(&clk_en_isomis_dma),
+ [RTD1625_CRT_CLK_EN_DPTX] = &__clk_regmap_gate_hw(&clk_en_dptx),
+ [RTD1625_CRT_CLK_EN_NPU_MIPI_CSI] = &__clk_regmap_gate_hw(&clk_en_npu_mipi_csi),
+ [RTD1625_CRT_CLK_EN_EDPTX] = &__clk_regmap_gate_hw(&clk_en_edptx),
+ [RTD1625_CRT_CLK_GPU] = &__clk_regmap_mux_hw(&clk_gpu),
+ [RTD1625_CRT_CLK_VE1] = &__clk_regmap_mux_hw(&clk_ve1),
+ [RTD1625_CRT_CLK_VE2] = &__clk_regmap_mux_hw(&clk_ve2),
+ [RTD1625_CRT_CLK_VE4] = &__clk_regmap_mux_hw(&clk_ve4),
+ [RTD1625_CRT_PLL_VE1] = &__clk_pll_hw(&pll_ve1),
+ [RTD1625_CRT_PLL_DDSA] = &__clk_pll_hw(&pll_ddsa),
+ [RTD1625_CRT_PLL_BUS] = &__clk_pll_hw(&pll_bus),
+ [RTD1625_CRT_CLK_SYS] = &clk_sys.hw,
+ [RTD1625_CRT_PLL_DCSB] = &__clk_pll_hw(&pll_dcsb),
+ [RTD1625_CRT_CLK_SYSH] = &clk_sysh.hw,
+ [RTD1625_CRT_PLL_GPU] = &__clk_pll_hw(&pll_gpu),
+ [RTD1625_CRT_PLL_NPU] = &__clk_pll_hw(&pll_npu),
+ [RTD1625_CRT_PLL_VE2] = &__clk_pll_hw(&pll_ve2),
+ [RTD1625_CRT_PLL_HIFI] = &__clk_pll_hw(&pll_hifi),
+ [RTD1625_CRT_PLL_EMMC_REF] = &pll_emmc_ref.hw,
+ [RTD1625_CRT_PLL_EMMC] = &__clk_pll_mmc_hw(&pll_emmc),
+ [RTD1625_CRT_PLL_EMMC_VP0] = &pll_emmc.phase0_hw,
+ [RTD1625_CRT_PLL_EMMC_VP1] = &pll_emmc.phase1_hw,
+ [RTD1625_CRT_PLL_ACPU] = &__clk_pll_hw(&pll_acpu),
+ [RTD1625_CRT_CLK_NPU] = &clk_npu.hw,
+ [RTD1625_CRT_CLK_NPU_MIPI_CSI] = &clk_npu_mipi_csi.hw,
+ [RTD1625_CRT_CLK_MAX - 1] = NULL,
+ },
+};
+
+static const struct rtk_clk_desc rtd1625_crt_desc = {
+ .clk_data = &rtd1625_crt_hw_data,
+ .clks = rtd1625_crt_regmap_clks,
+ .num_clks = ARRAY_SIZE(rtd1625_crt_regmap_clks),
+};
+
+static int rtd1625_crt_probe(struct platform_device *pdev)
+{
+ const struct rtk_clk_desc *desc;
+
+ desc = of_device_get_match_data(&pdev->dev);
+ if (!desc)
+ return -EINVAL;
+
+ return rtk_clk_probe(pdev, desc, "crt_rst");
+}
+
+static const struct of_device_id rtd1625_crt_match[] = {
+ {.compatible = "realtek,rtd1625-crt-clk", .data = &rtd1625_crt_desc,},
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rtd1625_crt_match);
+
+static struct platform_driver rtd1625_crt_driver = {
+ .probe = rtd1625_crt_probe,
+ .driver = {
+ .name = "rtk-rtd1625-crt-clk",
+ .of_match_table = rtd1625_crt_match,
+ .suppress_bind_attrs = true,
+ },
+};
+
+static int __init rtd1625_crt_init(void)
+{
+ return platform_driver_register(&rtd1625_crt_driver);
+}
+subsys_initcall(rtd1625_crt_init);
+
+MODULE_DESCRIPTION("Realtek RTD1625 CRT Clock Controller Driver");
+MODULE_AUTHOR("Cheng-Yu Lee <cylee12@realtek.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("REALTEK_CLK");
--
2.43.0
^ permalink raw reply related
* [PATCH v9 05/12] clk: realtek: Introduce a common probe()
From: Yu-Chun Lin @ 2026-06-24 11:29 UTC (permalink / raw)
To: mturquette, sboyd, robh, krzk+dt, conor+dt, p.zabel, cylee12,
afaerber, jyanchou, bmasney
Cc: devicetree, linux-clk, linux-kernel, linux-arm-kernel,
linux-realtek-soc, james.tai, cy.huang, stanley_chang,
eleanor.lin
In-Reply-To: <20260624112940.3475605-1-eleanor.lin@realtek.com>
From: Cheng-Yu Lee <cylee12@realtek.com>
Add rtk_clk_probe() to set up the shared regmap, register clock hardware,
and add the clock provider.
Additionally, if the "#reset-cells" property is present in the device tree,
it creates and registers an auxiliary device using the provided aux_name.
This allows the dedicated reset driver to bind to this device, enabling
both clock and reset drivers to share the same regmap.
Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
Co-developed-by: Yu-Chun Lin <eleanor.lin@realtek.com>
Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
---
Changes in v9:
- Rename common.[ch] to clk-rtk-common.[ch].
---
MAINTAINERS | 1 +
drivers/clk/Kconfig | 1 +
drivers/clk/Makefile | 1 +
drivers/clk/realtek/Kconfig | 30 +++++++++++++
drivers/clk/realtek/Makefile | 4 ++
drivers/clk/realtek/clk-rtk-common.c | 66 ++++++++++++++++++++++++++++
drivers/clk/realtek/clk-rtk-common.h | 37 ++++++++++++++++
7 files changed, 140 insertions(+)
create mode 100644 drivers/clk/realtek/Kconfig
create mode 100644 drivers/clk/realtek/Makefile
create mode 100644 drivers/clk/realtek/clk-rtk-common.c
create mode 100644 drivers/clk/realtek/clk-rtk-common.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 3bc431a2a7d1..9cdcb333b68f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22675,6 +22675,7 @@ L: devicetree@vger.kernel.org
L: linux-clk@vger.kernel.org
S: Supported
F: Documentation/devicetree/bindings/clock/realtek*
+F: drivers/clk/realtek/*
F: drivers/reset/realtek/*
F: include/dt-bindings/clock/realtek*
F: include/dt-bindings/reset/realtek*
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index b2efbe9f6acb..8bf262dd23a9 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -519,6 +519,7 @@ source "drivers/clk/nuvoton/Kconfig"
source "drivers/clk/pistachio/Kconfig"
source "drivers/clk/qcom/Kconfig"
source "drivers/clk/ralink/Kconfig"
+source "drivers/clk/realtek/Kconfig"
source "drivers/clk/renesas/Kconfig"
source "drivers/clk/rockchip/Kconfig"
source "drivers/clk/samsung/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index a3e2862ebd7e..e226bee2d039 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -140,6 +140,7 @@ obj-$(CONFIG_COMMON_CLK_PISTACHIO) += pistachio/
obj-$(CONFIG_COMMON_CLK_PXA) += pxa/
obj-$(CONFIG_COMMON_CLK_QCOM) += qcom/
obj-y += ralink/
+obj-$(CONFIG_COMMON_CLK_REALTEK) += realtek/
obj-y += renesas/
obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/
diff --git a/drivers/clk/realtek/Kconfig b/drivers/clk/realtek/Kconfig
new file mode 100644
index 000000000000..ed97531e321d
--- /dev/null
+++ b/drivers/clk/realtek/Kconfig
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config COMMON_CLK_REALTEK
+ tristate "Clock driver for Realtek SoCs"
+ depends on ARCH_REALTEK || COMPILE_TEST
+ default ARCH_REALTEK
+ help
+ Enable the common clock framework infrastructure for Realtek
+ system-on-chip platforms.
+
+ This provides the base support required by individual Realtek
+ clock controller drivers to expose clocks to peripheral devices.
+
+ If you have a Realtek-based platform, say Y.
+
+if COMMON_CLK_REALTEK
+
+config RTK_CLK_COMMON
+ tristate "Realtek Clock Common"
+ depends on RESET_CONTROLLER
+ select AUXILIARY_BUS
+ select MFD_SYSCON
+ select RESET_RTK_COMMON
+ help
+ Common helper code shared by Realtek clock controller drivers.
+
+ This provides utility functions and data structures used by
+ multiple Realtek clock implementations, and include integration
+ with reset controllers where required.
+
+endif
diff --git a/drivers/clk/realtek/Makefile b/drivers/clk/realtek/Makefile
new file mode 100644
index 000000000000..13000ed4ba11
--- /dev/null
+++ b/drivers/clk/realtek/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_RTK_CLK_COMMON) += clk-rtk.o
+
+clk-rtk-y += clk-rtk-common.o
diff --git a/drivers/clk/realtek/clk-rtk-common.c b/drivers/clk/realtek/clk-rtk-common.c
new file mode 100644
index 000000000000..4fd16585dbf4
--- /dev/null
+++ b/drivers/clk/realtek/clk-rtk-common.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2019-2026 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include "clk-rtk-common.h"
+
+static int rtk_reset_controller_register(struct device *dev, const char *aux_name,
+ struct regmap *map)
+{
+ struct auxiliary_device *adev;
+
+ if (!of_property_present(dev->of_node, "#reset-cells"))
+ return 0;
+
+ adev = devm_auxiliary_device_create(dev, aux_name, (void *)map);
+
+ if (!adev)
+ return -ENOMEM;
+
+ return 0;
+}
+
+int rtk_clk_probe(struct platform_device *pdev, const struct rtk_clk_desc *desc,
+ const char *aux_name)
+{
+ struct device *dev = &pdev->dev;
+ struct regmap *regmap;
+ int i, ret;
+
+ regmap = device_node_to_regmap(dev->of_node);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap), "failed to get regmap\n");
+
+ for (i = 0; i < desc->num_clks; i++)
+ desc->clks[i]->regmap = regmap;
+
+ for (i = 0; i < desc->clk_data->num; i++) {
+ struct clk_hw *hw = desc->clk_data->hws[i];
+
+ if (!hw)
+ continue;
+
+ ret = devm_clk_hw_register(dev, hw);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register hw of clk%d\n", i);
+ }
+
+ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
+ desc->clk_data);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to add clock provider\n");
+
+ return rtk_reset_controller_register(dev, aux_name, regmap);
+}
+EXPORT_SYMBOL_NS_GPL(rtk_clk_probe, "REALTEK_CLK");
+
+MODULE_DESCRIPTION("Realtek clock infrastructure");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/realtek/clk-rtk-common.h b/drivers/clk/realtek/clk-rtk-common.h
new file mode 100644
index 000000000000..c52fcdbff5ee
--- /dev/null
+++ b/drivers/clk/realtek/clk-rtk-common.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2016-2026 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#ifndef __CLK_REALTEK_COMMON_H
+#define __CLK_REALTEK_COMMON_H
+
+#include <linux/clk-provider.h>
+
+#define __clk_regmap_hw(_p) ((_p)->hw)
+
+struct device;
+struct platform_device;
+struct regmap;
+
+struct clk_regmap {
+ struct clk_hw hw;
+ struct regmap *regmap;
+};
+
+struct rtk_clk_desc {
+ struct clk_hw_onecell_data *clk_data;
+ struct clk_regmap * const *clks;
+ size_t num_clks;
+};
+
+static inline struct clk_regmap *to_clk_regmap(struct clk_hw *hw)
+{
+ return container_of(hw, struct clk_regmap, hw);
+}
+
+int rtk_clk_probe(struct platform_device *pdev, const struct rtk_clk_desc *desc,
+ const char *aux_name);
+
+#endif /* __CLK_REALTEK_COMMON_H */
--
2.43.0
^ permalink raw reply related
* [PATCH v9 00/12] clk / reset: realtek: Add RTD1625 clock and reset support
From: Yu-Chun Lin @ 2026-06-24 11:29 UTC (permalink / raw)
To: mturquette, sboyd, robh, krzk+dt, conor+dt, p.zabel, cylee12,
afaerber, jyanchou, bmasney
Cc: devicetree, linux-clk, linux-kernel, linux-arm-kernel,
linux-realtek-soc, james.tai, cy.huang, stanley_chang,
eleanor.lin
Hello,
This patch series adds the clock and reset controller support for Realtek's
RTD1625 SoC platform.
Because the reset controllers share the same register space with the
clock controllers on this platform, we utilize the Auxiliary Bus framework
to decouple them. The clock controllers act as the primary devices,
registering the reset controllers as auxiliary devices.
To make it easier for maintainers to review, the series has been organized
by subsystem:
1. Device Tree Bindings:
- Add bindings for the Realtek RTD1625 Clock & Reset Controllers.
2. Reset Subsystem:
- Introduce the basic Realtek reset infrastructure.
- Add the RTD1625-CRT and RTD1625-ISO platform reset drivers.
3. Clock Subsystem Infrastructure:
- Introduce a common probe, and add support for basic clocks including
PLLs, gate clocks, mux clocks, and MMC-tuned PLLs.
4. Clock Platform Drivers:
- Add the clock controller drivers for RTD1625-CRT and RTD1625-ISO.
- These drivers provide the clock sources and instantiate the
corresponding auxiliary reset devices.
Best regards,
Yu-Chun Lin
---
Changes in v9:
General:
- Split the combined clock and reset drivers into separate patches.
- Rename 'common.[ch]' to 'clk-rtk-common.[ch]' and 'reset-rtk-common.[ch]' to
avoid generic names.
- Adapt suggestions from AI review.
- Link: https://sashiko.dev/#/patchset/20260610080824.255063-1-eleanor.lin%40realtek.com.
Patch 6:
- Add 'ftbl_find_ceil_by_rate()' as a fallback in 'clk_pll_determine_rate()'.
Patch 8:
- Fix error handling in 'clk_regmap_mux_get_parent()'.
Patch 9:
- Fix potential integer overflow on 32-bit architectures in
'clk_pll_mmc_determine_rate()'.
- Add comments in 'clk_pll_mmc_set_rate()'.
v8: https://lore.kernel.org/lkml/20260610080824.255063-1-eleanor.lin@realtek.com/
v7: https://lore.kernel.org/lkml/20260508111641.3192177-1-eleanor.lin@realtek.com/
v6: https://lore.kernel.org/lkml/20260402073957.2742459-1-eleanor.lin@realtek.com/
v5: https://lore.kernel.org/lkml/20260324025332.3416977-1-eleanor.lin@realtek.com/
v4: https://lore.kernel.org/lkml/20260313081100.596224-1-eleanor.lin@realtek.com/
v3: https://lore.kernel.org/lkml/20260122110857.12995-1-eleanor.lin@realtek.com/
v2: https://lore.kernel.org/lkml/20260113112333.821-1-eleanor.lin@realtek.com/
v1: https://lore.kernel.org/lkml/20251229075313.27254-1-eleanor.lin@realtek.com/
Cheng-Yu Lee (10):
reset: Add Realtek basic reset support
reset: realtek: Add RTD1625-CRT reset driver
reset: realtek: Add RTD1625-ISO reset controller driver
clk: realtek: Introduce a common probe()
clk: realtek: Add support for phase locked loops (PLLs)
clk: realtek: Add support for gate clock
clk: realtek: Add support for mux clock
clk: realtek: Add support for MMC-tuned PLL clocks
clk: realtek: Add RTD1625-CRT clock controller driver
clk: realtek: Add RTD1625-ISO clock controller driver
Yu-Chun Lin (2):
dt-bindings: clock: Add Realtek RTD1625 Clock & Reset Controller
arm64: dts: realtek: Add clock support for RTD1625
.../bindings/clock/realtek,rtd1625-clk.yaml | 58 ++
MAINTAINERS | 20 +
arch/arm64/boot/dts/realtek/kent.dtsi | 33 +
drivers/clk/Kconfig | 1 +
drivers/clk/Makefile | 1 +
drivers/clk/realtek/Kconfig | 48 ++
drivers/clk/realtek/Makefile | 12 +
drivers/clk/realtek/clk-pll-mmc.c | 430 ++++++++++
drivers/clk/realtek/clk-pll.c | 217 +++++
drivers/clk/realtek/clk-pll.h | 60 ++
drivers/clk/realtek/clk-regmap-gate.c | 70 ++
drivers/clk/realtek/clk-regmap-gate.h | 65 ++
drivers/clk/realtek/clk-regmap-mux.c | 41 +
drivers/clk/realtek/clk-regmap-mux.h | 43 +
drivers/clk/realtek/clk-rtd1625-crt.c | 792 ++++++++++++++++++
drivers/clk/realtek/clk-rtd1625-iso.c | 151 ++++
drivers/clk/realtek/clk-rtk-common.c | 66 ++
drivers/clk/realtek/clk-rtk-common.h | 37 +
drivers/clk/realtek/freq_table.c | 57 ++
drivers/clk/realtek/freq_table.h | 18 +
drivers/reset/Kconfig | 1 +
drivers/reset/Makefile | 1 +
drivers/reset/realtek/Kconfig | 19 +
drivers/reset/realtek/Makefile | 3 +
drivers/reset/realtek/reset-rtd1625-crt.c | 187 +++++
drivers/reset/realtek/reset-rtd1625-iso.c | 99 +++
drivers/reset/realtek/reset-rtk-common.c | 90 ++
drivers/reset/realtek/reset-rtk-common.h | 29 +
.../dt-bindings/clock/realtek,rtd1625-clk.h | 164 ++++
include/dt-bindings/reset/realtek,rtd1625.h | 171 ++++
30 files changed, 2984 insertions(+)
create mode 100644 Documentation/devicetree/bindings/clock/realtek,rtd1625-clk.yaml
create mode 100644 drivers/clk/realtek/Kconfig
create mode 100644 drivers/clk/realtek/Makefile
create mode 100644 drivers/clk/realtek/clk-pll-mmc.c
create mode 100644 drivers/clk/realtek/clk-pll.c
create mode 100644 drivers/clk/realtek/clk-pll.h
create mode 100644 drivers/clk/realtek/clk-regmap-gate.c
create mode 100644 drivers/clk/realtek/clk-regmap-gate.h
create mode 100644 drivers/clk/realtek/clk-regmap-mux.c
create mode 100644 drivers/clk/realtek/clk-regmap-mux.h
create mode 100644 drivers/clk/realtek/clk-rtd1625-crt.c
create mode 100644 drivers/clk/realtek/clk-rtd1625-iso.c
create mode 100644 drivers/clk/realtek/clk-rtk-common.c
create mode 100644 drivers/clk/realtek/clk-rtk-common.h
create mode 100644 drivers/clk/realtek/freq_table.c
create mode 100644 drivers/clk/realtek/freq_table.h
create mode 100644 drivers/reset/realtek/Kconfig
create mode 100644 drivers/reset/realtek/Makefile
create mode 100644 drivers/reset/realtek/reset-rtd1625-crt.c
create mode 100644 drivers/reset/realtek/reset-rtd1625-iso.c
create mode 100644 drivers/reset/realtek/reset-rtk-common.c
create mode 100644 drivers/reset/realtek/reset-rtk-common.h
create mode 100644 include/dt-bindings/clock/realtek,rtd1625-clk.h
create mode 100644 include/dt-bindings/reset/realtek,rtd1625.h
--
2.43.0
^ permalink raw reply
* [PATCH v9 08/12] clk: realtek: Add support for mux clock
From: Yu-Chun Lin @ 2026-06-24 11:29 UTC (permalink / raw)
To: mturquette, sboyd, robh, krzk+dt, conor+dt, p.zabel, cylee12,
afaerber, jyanchou, bmasney
Cc: devicetree, linux-clk, linux-kernel, linux-arm-kernel,
linux-realtek-soc, james.tai, cy.huang, stanley_chang,
eleanor.lin
In-Reply-To: <20260624112940.3475605-1-eleanor.lin@realtek.com>
From: Cheng-Yu Lee <cylee12@realtek.com>
Add a simple regmap-based clk_ops implementation for Realtek mux clocks.
The implementation supports parent selection and rate determination through
regmap-backed register access.
Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
Co-developed-by: Yu-Chun Lin <eleanor.lin@realtek.com>
Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
---
Changes in v9:
- Fix error handling in 'clk_regmap_mux_get_parent()'.
---
drivers/clk/realtek/Makefile | 1 +
drivers/clk/realtek/clk-regmap-mux.c | 41 ++++++++++++++++++++++++++
drivers/clk/realtek/clk-regmap-mux.h | 43 ++++++++++++++++++++++++++++
3 files changed, 85 insertions(+)
create mode 100644 drivers/clk/realtek/clk-regmap-mux.c
create mode 100644 drivers/clk/realtek/clk-regmap-mux.h
diff --git a/drivers/clk/realtek/Makefile b/drivers/clk/realtek/Makefile
index 90c0658a83bf..3b014240a211 100644
--- a/drivers/clk/realtek/Makefile
+++ b/drivers/clk/realtek/Makefile
@@ -5,3 +5,4 @@ clk-rtk-y += clk-rtk-common.o
clk-rtk-y += clk-pll.o
clk-rtk-y += freq_table.o
clk-rtk-y += clk-regmap-gate.o
+clk-rtk-y += clk-regmap-mux.o
diff --git a/drivers/clk/realtek/clk-regmap-mux.c b/drivers/clk/realtek/clk-regmap-mux.c
new file mode 100644
index 000000000000..bdd579e1165d
--- /dev/null
+++ b/drivers/clk/realtek/clk-regmap-mux.c
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2017-2026 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/export.h>
+#include <linux/regmap.h>
+#include "clk-regmap-mux.h"
+
+static u8 clk_regmap_mux_get_parent(struct clk_hw *hw)
+{
+ struct clk_regmap_mux *clkm = to_clk_regmap_mux(hw);
+ int num_parents = clk_hw_get_num_parents(hw);
+ u32 val;
+ int ret;
+
+ ret = regmap_read(clkm->clkr.regmap, clkm->mux_ofs, &val);
+ if (ret)
+ return 0xff;
+
+ val = (val >> clkm->shift) & clkm->mask;
+
+ return val >= num_parents ? 0xff : val;
+}
+
+static int clk_regmap_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_regmap_mux *clkm = to_clk_regmap_mux(hw);
+
+ return regmap_update_bits(clkm->clkr.regmap, clkm->mux_ofs,
+ clkm->mask << clkm->shift, (u32)index << clkm->shift);
+}
+
+const struct clk_ops rtk_clk_regmap_mux_ops = {
+ .set_parent = clk_regmap_mux_set_parent,
+ .get_parent = clk_regmap_mux_get_parent,
+ .determine_rate = __clk_mux_determine_rate,
+};
+EXPORT_SYMBOL_NS_GPL(rtk_clk_regmap_mux_ops, "REALTEK_CLK");
diff --git a/drivers/clk/realtek/clk-regmap-mux.h b/drivers/clk/realtek/clk-regmap-mux.h
new file mode 100644
index 000000000000..3ab8dc5032fc
--- /dev/null
+++ b/drivers/clk/realtek/clk-regmap-mux.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2017-2026 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#ifndef __CLK_REALTEK_CLK_REGMAP_MUX_H
+#define __CLK_REALTEK_CLK_REGMAP_MUX_H
+
+#include "clk-rtk-common.h"
+
+struct clk_regmap_mux {
+ struct clk_regmap clkr;
+ int mux_ofs;
+ unsigned int mask;
+ unsigned int shift;
+};
+
+#define __clk_regmap_mux_hw(_p) __clk_regmap_hw(&(_p)->clkr)
+
+#define __CLK_REGMAP_MUX(_name, _parents, _ops, _flags, _ofs, _sft, _mask) \
+ struct clk_regmap_mux _name = { \
+ .clkr.hw.init = \
+ CLK_HW_INIT_PARENTS(#_name, _parents, _ops, _flags), \
+ .mux_ofs = _ofs, \
+ .shift = _sft, \
+ .mask = _mask, \
+ }
+
+#define CLK_REGMAP_MUX(_name, _parents, _flags, _ofs, _sft, _mask) \
+ __CLK_REGMAP_MUX(_name, _parents, &rtk_clk_regmap_mux_ops, _flags, _ofs, \
+ _sft, _mask)
+
+static inline struct clk_regmap_mux *to_clk_regmap_mux(struct clk_hw *hw)
+{
+ struct clk_regmap *clkr = to_clk_regmap(hw);
+
+ return container_of(clkr, struct clk_regmap_mux, clkr);
+}
+
+extern const struct clk_ops rtk_clk_regmap_mux_ops;
+
+#endif /* __CLK_REALTEK_CLK_REGMAP_MUX_H */
--
2.43.0
^ permalink raw reply related
* [PATCH v9 04/12] reset: realtek: Add RTD1625-ISO reset controller driver
From: Yu-Chun Lin @ 2026-06-24 11:29 UTC (permalink / raw)
To: mturquette, sboyd, robh, krzk+dt, conor+dt, p.zabel, cylee12,
afaerber, jyanchou, bmasney
Cc: devicetree, linux-clk, linux-kernel, linux-arm-kernel,
linux-realtek-soc, james.tai, cy.huang, stanley_chang,
eleanor.lin
In-Reply-To: <20260624112940.3475605-1-eleanor.lin@realtek.com>
From: Cheng-Yu Lee <cylee12@realtek.com>
Add support for the ISO (Isolation) domain reset controller on the Realtek
RTD1625 SoC.
The reset controller shares the same register space with the ISO clock
controller. To handle this shared register space, the reset driver is
implemented as an auxiliary driver. It will be instantiated and probed via
the auxiliary bus by the RTD1625-ISO clock controller driver.
Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
Co-developed-by: Yu-Chun Lin <eleanor.lin@realtek.com>
Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
---
Changes in v9:
- Extract reset-related code from the previous clock driver patch
(formerly patch 9 in v8).
---
drivers/reset/realtek/Makefile | 2 +-
drivers/reset/realtek/reset-rtd1625-iso.c | 99 +++++++++++++++++++++++
2 files changed, 100 insertions(+), 1 deletion(-)
create mode 100644 drivers/reset/realtek/reset-rtd1625-iso.c
diff --git a/drivers/reset/realtek/Makefile b/drivers/reset/realtek/Makefile
index c3f605ffb11c..9007c9d5683b 100644
--- a/drivers/reset/realtek/Makefile
+++ b/drivers/reset/realtek/Makefile
@@ -1,3 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_RESET_RTK_COMMON) += reset-rtk-common.o
-obj-$(CONFIG_RESET_RTD1625) += reset-rtd1625-crt.o
+obj-$(CONFIG_RESET_RTD1625) += reset-rtd1625-crt.o reset-rtd1625-iso.o
diff --git a/drivers/reset/realtek/reset-rtd1625-iso.c b/drivers/reset/realtek/reset-rtd1625-iso.c
new file mode 100644
index 000000000000..78eaabb408f0
--- /dev/null
+++ b/drivers/reset/realtek/reset-rtd1625-iso.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 Realtek Semiconductor Corporation
+ */
+
+#include <dt-bindings/reset/realtek,rtd1625.h>
+#include <linux/auxiliary_bus.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include "reset-rtk-common.h"
+
+#define RTD1625_ISO_RSTN_MAX 29
+#define RTD1625_ISO_S_RSTN_MAX 5
+
+static const struct rtk_reset_desc rtd1625_iso_reset_descs[] = {
+ [RTD1625_ISO_RSTN_VFD] = { .ofs = 0x88, .bit = 0 },
+ [RTD1625_ISO_RSTN_CEC0] = { .ofs = 0x88, .bit = 2 },
+ [RTD1625_ISO_RSTN_CEC1] = { .ofs = 0x88, .bit = 3 },
+ [RTD1625_ISO_RSTN_CBUSTX] = { .ofs = 0x88, .bit = 5 },
+ [RTD1625_ISO_RSTN_CBUSRX] = { .ofs = 0x88, .bit = 6 },
+ [RTD1625_ISO_RSTN_USB3_PHY2_XTAL_POW] = { .ofs = 0x88, .bit = 7 },
+ [RTD1625_ISO_RSTN_UR0] = { .ofs = 0x88, .bit = 8 },
+ [RTD1625_ISO_RSTN_GMAC] = { .ofs = 0x88, .bit = 9 },
+ [RTD1625_ISO_RSTN_GPHY] = { .ofs = 0x88, .bit = 10 },
+ [RTD1625_ISO_RSTN_I2C_0] = { .ofs = 0x88, .bit = 11 },
+ [RTD1625_ISO_RSTN_I2C_1] = { .ofs = 0x88, .bit = 12 },
+ [RTD1625_ISO_RSTN_CBUS] = { .ofs = 0x88, .bit = 13 },
+ [RTD1625_ISO_RSTN_USB_DRD] = { .ofs = 0x88, .bit = 14 },
+ [RTD1625_ISO_RSTN_USB_HOST] = { .ofs = 0x88, .bit = 15 },
+ [RTD1625_ISO_RSTN_USB_PHY_0] = { .ofs = 0x88, .bit = 16 },
+ [RTD1625_ISO_RSTN_USB_PHY_1] = { .ofs = 0x88, .bit = 17 },
+ [RTD1625_ISO_RSTN_USB_PHY_2] = { .ofs = 0x88, .bit = 18 },
+ [RTD1625_ISO_RSTN_USB] = { .ofs = 0x88, .bit = 19 },
+ [RTD1625_ISO_RSTN_TYPE_C] = { .ofs = 0x88, .bit = 20 },
+ [RTD1625_ISO_RSTN_USB_U3_HOST] = { .ofs = 0x88, .bit = 21 },
+ [RTD1625_ISO_RSTN_USB3_PHY0_POW] = { .ofs = 0x88, .bit = 22 },
+ [RTD1625_ISO_RSTN_USB3_P0_MDIO] = { .ofs = 0x88, .bit = 23 },
+ [RTD1625_ISO_RSTN_USB3_PHY1_POW] = { .ofs = 0x88, .bit = 24 },
+ [RTD1625_ISO_RSTN_USB3_P1_MDIO] = { .ofs = 0x88, .bit = 25 },
+ [RTD1625_ISO_RSTN_VTC] = { .ofs = 0x88, .bit = 26 },
+ [RTD1625_ISO_RSTN_USB3_PHY2_POW] = { .ofs = 0x88, .bit = 27 },
+ [RTD1625_ISO_RSTN_USB3_P2_MDIO] = { .ofs = 0x88, .bit = 28 },
+ [RTD1625_ISO_RSTN_USB_PHY_3] = { .ofs = 0x88, .bit = 29 },
+ [RTD1625_ISO_RSTN_USB_PHY_4] = { .ofs = 0x88, .bit = 30 },
+};
+
+static const struct rtk_reset_desc rtd1625_iso_s_reset_descs[] = {
+ [RTD1625_ISO_S_RSTN_ISOM_MIS] = { .ofs = 0x310, .bit = 0, .write_en = 1 },
+ [RTD1625_ISO_S_RSTN_GPIOM] = { .ofs = 0x310, .bit = 2, .write_en = 1 },
+ [RTD1625_ISO_S_RSTN_TIMER7] = { .ofs = 0x310, .bit = 4, .write_en = 1 },
+ [RTD1625_ISO_S_RSTN_IRDA] = { .ofs = 0x310, .bit = 6, .write_en = 1 },
+ [RTD1625_ISO_S_RSTN_UR10] = { .ofs = 0x310, .bit = 8, .write_en = 1 },
+};
+
+static int rtd1625_iso_reset_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ struct device *dev = &adev->dev;
+ struct device *parent = dev->parent;
+ struct rtk_reset_data *data;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ if (of_device_is_compatible(parent->of_node, "realtek,rtd1625-iso-s-clk")) {
+ data->descs = rtd1625_iso_s_reset_descs;
+ data->rcdev.nr_resets = RTD1625_ISO_S_RSTN_MAX;
+ } else {
+ data->descs = rtd1625_iso_reset_descs;
+ data->rcdev.nr_resets = RTD1625_ISO_RSTN_MAX;
+ }
+
+ data->rcdev.owner = THIS_MODULE;
+
+ return rtk_reset_controller_add(dev, data);
+}
+
+static const struct auxiliary_device_id rtd1625_iso_reset_ids[] = {
+ { .name = "clk_rtk.iso_rst" },
+ { .name = "clk_rtk.iso_s_rst" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(auxiliary, rtd1625_iso_reset_ids);
+
+static struct auxiliary_driver rtd1625_iso_driver = {
+ .probe = rtd1625_iso_reset_probe,
+ .id_table = rtd1625_iso_reset_ids,
+ .driver = {
+ .name = "rtd1625-iso-reset",
+ },
+};
+module_auxiliary_driver(rtd1625_iso_driver);
+
+MODULE_DESCRIPTION("Realtek RTD1625 ISO Reset Controller Driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("REALTEK_RESET");
--
2.43.0
^ permalink raw reply related
* [PATCH v3 1/7] net: wwan: t9xx: Add PCIe core
From: Jack Wu via B4 Relay @ 2026-06-24 10:04 UTC (permalink / raw)
To: Loic Poulain, Sergey Ryazanov, Johannes Berg, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Jack Wu, Wen-Zhi Huang, Shi-Wei Yeh, Minano Tseng,
Matthias Brugger, AngeloGioacchino Del Regno, Simon Horman,
Jonathan Corbet, Shuah Khan
Cc: linux-kernel, netdev, linux-arm-kernel, linux-mediatek, linux-doc
In-Reply-To: <20260624-t9xx_driver_v1-v3-0-73ff03f60c48@compal.com>
From: Jack Wu <jackbb_wu@compal.com>
Registers the T900 device driver with the kernel. Set up all
the fundamental configurations for the device: PCIe layer,
Modem Host Cross Core Interface (MHCCIF), Reset Generation
Unit (RGU), modem common control operations and build
infrastructure.
* PCIe layer code implements driver probe and removal, MSI-X
interrupt initialization and de-initialization, and the way
of resetting the device.
* MHCCIF provides interrupt channels to communicate events
such as handshake, PM and port enumeration.
* RGU provides interrupt channels to generate notifications
from the device so that the T900 driver could get the
device reset.
* Modem common control operations provide the basic read/write
functions of the device's hardware registers,
mask/unmask/get/clear functions of the device's interrupt
registers and inquiry functions of the device's status.
Signed-off-by: Jack Wu <jackbb_wu@compal.com>
---
drivers/net/wwan/Kconfig | 12 +
drivers/net/wwan/Makefile | 1 +
drivers/net/wwan/t9xx/Makefile | 10 +
drivers/net/wwan/t9xx/mtk_dev.h | 108 +++
drivers/net/wwan/t9xx/pcie/mtk_pci.c | 1049 +++++++++++++++++++++++++
drivers/net/wwan/t9xx/pcie/mtk_pci.h | 234 ++++++
drivers/net/wwan/t9xx/pcie/mtk_pci_drv_m9xx.c | 69 ++
drivers/net/wwan/t9xx/pcie/mtk_pci_reg.h | 70 ++
8 files changed, 1553 insertions(+)
diff --git a/drivers/net/wwan/Kconfig b/drivers/net/wwan/Kconfig
index 88df55d78d90..4cee537c739f 100644
--- a/drivers/net/wwan/Kconfig
+++ b/drivers/net/wwan/Kconfig
@@ -121,6 +121,18 @@ config MTK_T7XX
If unsure, say N.
+config MTK_T9XX
+ tristate "MediaTek PCIe 5G WWAN modem T9xx device"
+ depends on PCI
+ select NET_DEVLINK
+ help
+ Enables MediaTek PCIe based 5G WWAN modem (T9xx series) device.
+
+ To compile this driver as a module, choose M here: the module will be
+ called mtk_t9xx.
+
+ If unsure, say N.
+
endif # WWAN
endmenu
diff --git a/drivers/net/wwan/Makefile b/drivers/net/wwan/Makefile
index 3960c0ae2445..7361eef4c472 100644
--- a/drivers/net/wwan/Makefile
+++ b/drivers/net/wwan/Makefile
@@ -14,3 +14,4 @@ obj-$(CONFIG_QCOM_BAM_DMUX) += qcom_bam_dmux.o
obj-$(CONFIG_RPMSG_WWAN_CTRL) += rpmsg_wwan_ctrl.o
obj-$(CONFIG_IOSM) += iosm/
obj-$(CONFIG_MTK_T7XX) += t7xx/
+obj-$(CONFIG_MTK_T9XX) += t9xx/
diff --git a/drivers/net/wwan/t9xx/Makefile b/drivers/net/wwan/t9xx/Makefile
new file mode 100644
index 000000000000..6f2dd3f91454
--- /dev/null
+++ b/drivers/net/wwan/t9xx/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+ccflags-y += -I$(src)/pcie
+ccflags-y += -I$(src)
+
+obj-$(CONFIG_MTK_T9XX) += mtk_t9xx.o
+
+mtk_t9xx-y := \
+ pcie/mtk_pci.o \
+ pcie/mtk_pci_drv_m9xx.o
diff --git a/drivers/net/wwan/t9xx/mtk_dev.h b/drivers/net/wwan/t9xx/mtk_dev.h
new file mode 100644
index 000000000000..8278a0e2875e
--- /dev/null
+++ b/drivers/net/wwan/t9xx/mtk_dev.h
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (c) 2022, MediaTek Inc.
+ */
+
+#ifndef __MTK_DEV_H__
+#define __MTK_DEV_H__
+
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#define MTK_DEV_STR_LEN 16
+
+enum mtk_user_id {
+ MTK_USER_MIN,
+ MTK_USER_CTRL,
+ MTK_USER_DATA,
+ MTK_USER_MAX
+};
+
+enum mtk_dev_evt_h2d {
+ DEV_EVT_H2D_DEVICE_RESET = BIT(2),
+ DEV_EVT_H2D_MAX = BIT(5)
+};
+
+enum mtk_dev_evt_d2h {
+ DEV_EVT_D2H_BOOT_FLOW_SYNC = BIT(4),
+ DEV_EVT_D2H_ASYNC_HS_NOTIFY_SAP = BIT(5),
+ DEV_EVT_D2H_ASYNC_HS_NOTIFY_MD = BIT(6),
+ DEV_EVT_D2H_MAX = BIT(11)
+};
+
+struct mtk_md_dev;
+
+struct mtk_dev_ops {
+ u32 (*get_dev_state)(struct mtk_md_dev *mdev);
+ void (*ack_dev_state)(struct mtk_md_dev *mdev, u32 state);
+ u32 (*get_dev_cfg)(struct mtk_md_dev *mdev);
+ int (*register_dev_evt)(struct mtk_md_dev *mdev, u32 dev_evt,
+ int (*evt_cb)(u32 status, void *data), void *data);
+ void (*unregister_dev_evt)(struct mtk_md_dev *mdev, u32 dev_evt);
+ void (*mask_dev_evt)(struct mtk_md_dev *mdev, u32 dev_evt);
+ void (*unmask_dev_evt)(struct mtk_md_dev *mdev, u32 dev_evt);
+ void (*clear_dev_evt)(struct mtk_md_dev *mdev, u32 dev_evt);
+ int (*send_dev_evt)(struct mtk_md_dev *mdev, u32 dev_evt);
+};
+
+/* mtk_md_dev defines the structure of MTK modem device */
+struct mtk_md_dev {
+ struct device *dev;
+ const struct mtk_dev_ops *dev_ops;
+ void *hw_priv;
+ u32 hw_ver;
+ char dev_str[MTK_DEV_STR_LEN];
+};
+
+static inline u32 mtk_dev_get_dev_state(struct mtk_md_dev *mdev)
+{
+ return mdev->dev_ops->get_dev_state(mdev);
+}
+
+static inline void mtk_dev_ack_dev_state(struct mtk_md_dev *mdev, u32 state)
+{
+ return mdev->dev_ops->ack_dev_state(mdev, state);
+}
+
+static inline u32 mtk_dev_get_dev_cfg(struct mtk_md_dev *mdev)
+{
+ return mdev->dev_ops->get_dev_cfg(mdev);
+}
+
+static inline int mtk_dev_register_dev_evt(struct mtk_md_dev *mdev, u32 dev_evt,
+ int (*evt_cb)(u32 status, void *data), void *data)
+{
+ return mdev->dev_ops->register_dev_evt(mdev, dev_evt, evt_cb, data);
+}
+
+static inline void mtk_dev_unregister_dev_evt(struct mtk_md_dev *mdev, u32 dev_evt)
+{
+ mdev->dev_ops->unregister_dev_evt(mdev, dev_evt);
+}
+
+static inline void mtk_dev_mask_dev_evt(struct mtk_md_dev *mdev, u32 dev_evt)
+{
+ mdev->dev_ops->mask_dev_evt(mdev, dev_evt);
+}
+
+static inline void mtk_dev_unmask_dev_evt(struct mtk_md_dev *mdev, u32 dev_evt)
+{
+ mdev->dev_ops->unmask_dev_evt(mdev, dev_evt);
+}
+
+static inline void mtk_dev_clear_dev_evt(struct mtk_md_dev *mdev, u32 dev_evt)
+{
+ mdev->dev_ops->clear_dev_evt(mdev, dev_evt);
+}
+
+static inline int mtk_dev_send_dev_evt(struct mtk_md_dev *mdev, u32 dev_evt)
+{
+ return mdev->dev_ops->send_dev_evt(mdev, dev_evt);
+}
+
+#endif /* __MTK_DEV_H__ */
diff --git a/drivers/net/wwan/t9xx/pcie/mtk_pci.c b/drivers/net/wwan/t9xx/pcie/mtk_pci.c
new file mode 100644
index 000000000000..c6a7196fcdd6
--- /dev/null
+++ b/drivers/net/wwan/t9xx/pcie/mtk_pci.c
@@ -0,0 +1,1049 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022, MediaTek Inc.
+ */
+
+#include <linux/acpi.h>
+#include <linux/aer.h>
+#include <linux/bitfield.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "mtk_dev.h"
+#include "mtk_pci.h"
+#include "mtk_pci_reg.h"
+
+#define MTK_PCI_BAR_NUM 6
+#define MTK_PCI_TRANSPARENT_ATR_SIZE (0x3F)
+#define MTK_PCI_MINIMUM_ATR_SIZE (0x1000)
+#define ATR_SIZE_LO32_MASK GENMASK_ULL(31, 0)
+#define ATR_SIZE_HI32_MASK GENMASK_ULL(63, 32)
+#define ATR_SIZE_BIAS_FROM_LO32 2
+#define ATR_ADDR_ALIGN_MASK 0xFFFFF000
+#define ATR_EN BIT(0)
+#define ATR_PARAM_OFFSET 16
+/* Delay between ACPI PXP._OFF and _ON for modem power cycle stabilization */
+#define MTK_PLDR_POWER_OFF_DELAY_MS 500
+#define LE32_TO_U32(x) ((__force u32)(__le32)(x))
+#define SET_HW_BITS(dest, chs, mhccif, dev) \
+ ({ \
+ if ((chs) & (dev)) \
+ (dest) |= FIELD_PREP(mhccif, 1); \
+ })
+
+struct mtk_mhccif_cb {
+ struct list_head entry;
+ int (*evt_cb)(u32 status, void *data);
+ void *data;
+ u32 chs;
+};
+
+/**
+ * mtk_pci_setup_atr() - Configure a PCIe address translation rule
+ * @mdev: MTK MD device
+ * @cfg: ATR configuration parameters
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int mtk_pci_setup_atr(struct mtk_md_dev *mdev, struct mtk_atr_cfg *cfg)
+{
+ struct mtk_pci_priv *priv = mdev->hw_priv;
+ u32 addr, val, size_h, size_l;
+ int atr_size, pos, offset;
+
+ if (cfg->transparent) {
+ /* No address conversion is performed */
+ atr_size = MTK_PCI_TRANSPARENT_ATR_SIZE;
+ } else {
+ if (cfg->size < MTK_PCI_MINIMUM_ATR_SIZE)
+ cfg->size = MTK_PCI_MINIMUM_ATR_SIZE;
+
+ if (cfg->src_addr & (cfg->size - 1)) {
+ dev_err((mdev)->dev, "Invalid atr src addr is not aligned to size\n");
+ return -EFAULT;
+ }
+
+ if (cfg->trsl_addr & (cfg->size - 1)) {
+ dev_err((mdev)->dev,
+ "Invalid atr trsl addr is not aligned to size, %llx, %llx\n",
+ cfg->trsl_addr, cfg->size - 1);
+ return -EFAULT;
+ }
+
+ size_l = FIELD_GET(ATR_SIZE_LO32_MASK, cfg->size);
+ size_h = FIELD_GET(ATR_SIZE_HI32_MASK, cfg->size);
+ pos = ffs(size_l);
+ if (pos) {
+ atr_size = pos - ATR_SIZE_BIAS_FROM_LO32;
+ } else {
+ pos = ffs(size_h);
+ atr_size = pos + 32 - ATR_SIZE_BIAS_FROM_LO32;
+ }
+ }
+
+ /* Calculate table offset */
+ offset = ATR_PORT_OFFSET * cfg->port + ATR_TABLE_OFFSET * cfg->table;
+ addr = REG_ATR_PCIE_WIN0_T0_SRC_ADDR_MSB + offset;
+ val = (u32)(cfg->src_addr >> 32);
+ mtk_pci_mac_write32(priv, addr, val);
+
+ addr = REG_ATR_PCIE_WIN0_T0_SRC_ADDR_LSB + offset;
+ val = (u32)(cfg->src_addr & ATR_ADDR_ALIGN_MASK) | (atr_size << 1) | ATR_EN;
+ mtk_pci_mac_write32(priv, addr, val);
+
+ addr = REG_ATR_PCIE_WIN0_T0_TRSL_ADDR_MSB + offset;
+ val = (u32)(cfg->trsl_addr >> 32);
+ mtk_pci_mac_write32(priv, addr, val);
+
+ addr = REG_ATR_PCIE_WIN0_T0_TRSL_ADDR_LSB + offset;
+ val = (u32)(cfg->trsl_addr & ATR_ADDR_ALIGN_MASK);
+ mtk_pci_mac_write32(priv, addr, val);
+
+ /* TRSL_PARAM */
+ addr = REG_ATR_PCIE_WIN0_T0_TRSL_PARAM + offset;
+ val = (cfg->trsl_param << ATR_PARAM_OFFSET) | cfg->trsl_id;
+ mtk_pci_mac_write32(priv, addr, val);
+
+ return 0;
+}
+
+/**
+ * mtk_pci_atr_disable() - Disable all PCIe address translation rules
+ * @priv: MTK PCI private data
+ */
+void mtk_pci_atr_disable(struct mtk_pci_priv *priv)
+{
+ int port, tbl, offset;
+ u32 val;
+
+ /* Disable all ATR table for all ports */
+ for (port = ATR_SRC_PCI_WIN0; port <= ATR_SRC_AXIS_3; port++)
+ for (tbl = 0; tbl < ATR_TABLE_NUM_PER_ATR; tbl++) {
+ /* Calculate table offset */
+ offset = ATR_PORT_OFFSET * port + ATR_TABLE_OFFSET * tbl;
+ val = mtk_pci_mac_read32(priv, REG_ATR_PCIE_WIN0_T0_SRC_ADDR_LSB + offset);
+ val = val & (~BIT(0));
+ /* Disable table by SRC_ADDR_L */
+ mtk_pci_mac_write32(priv, REG_ATR_PCIE_WIN0_T0_SRC_ADDR_LSB + offset, val);
+ }
+}
+
+static void mtk_pci_set_msix_merged(struct mtk_pci_priv *priv, int irq_cnt)
+{
+ mtk_pci_mac_write32(priv, REG_PCIE_CFG_MSIX, ffs(irq_cnt) * 2 - 1);
+}
+
+/**
+ * mtk_pci_get_dev_state() - Read the device state from the modem
+ * @mdev: MTK MD device
+ *
+ * Return: Device state value.
+ */
+u32 mtk_pci_get_dev_state(struct mtk_md_dev *mdev)
+{
+ return mtk_pci_mac_read32(mdev->hw_priv, REG_PCIE_DEBUG_DUMMY_7);
+}
+
+/**
+ * mtk_pci_ack_dev_state() - Acknowledge the device state to the modem
+ * @mdev: MTK MD device
+ * @state: State value to acknowledge
+ */
+void mtk_pci_ack_dev_state(struct mtk_md_dev *mdev, u32 state)
+{
+ mtk_pci_mac_write32(mdev->hw_priv, REG_PCIE_DEBUG_DUMMY_7, state);
+}
+
+/**
+ * mtk_pci_get_irq_id() - Map an IRQ source to its hardware IRQ ID
+ * @mdev: MTK MD device
+ * @irq_src: IRQ source enum
+ *
+ * Return: IRQ ID on success, -EINVAL on failure.
+ */
+int mtk_pci_get_irq_id(struct mtk_md_dev *mdev, enum mtk_irq_src irq_src)
+{
+ struct mtk_pci_priv *priv = mdev->hw_priv;
+ const int *irq_tbl = priv->cfg->irq_tbl;
+ int irq_id = -EINVAL;
+
+ if (irq_src > MTK_IRQ_SRC_MIN && irq_src < MTK_IRQ_SRC_MAX) {
+ irq_id = irq_tbl[irq_src];
+ if (irq_id < 0 || irq_id >= MTK_IRQ_CNT_MAX)
+ irq_id = -EINVAL;
+ }
+
+ return irq_id;
+}
+
+/**
+ * mtk_pci_get_virq_id() - Get the Linux virtual IRQ for a hardware IRQ ID
+ * @mdev: MTK MD device
+ * @irq_id: Hardware IRQ ID
+ *
+ * Return: Virtual IRQ number on success, negative error code on failure.
+ */
+int mtk_pci_get_virq_id(struct mtk_md_dev *mdev, int irq_id)
+{
+ struct pci_dev *pdev = to_pci_dev(mdev->dev);
+ struct mtk_pci_priv *priv = mdev->hw_priv;
+
+ if (!priv->irq_cnt || irq_id < 0)
+ return -EINVAL;
+
+ return pci_irq_vector(pdev, irq_id % priv->irq_cnt);
+}
+
+/**
+ * mtk_pci_register_irq() - Register a callback for a hardware IRQ
+ * @mdev: MTK MD device
+ * @irq_id: Hardware IRQ ID
+ * @irq_cb: Callback function
+ * @data: Private data passed to callback
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int mtk_pci_register_irq(struct mtk_md_dev *mdev, int irq_id,
+ int (*irq_cb)(int irq_id, void *data), void *data)
+{
+ struct mtk_pci_priv *priv = mdev->hw_priv;
+
+ if ((irq_id < 0 || irq_id >= MTK_IRQ_CNT_MAX) || !irq_cb)
+ return -EINVAL;
+
+ if (priv->irq_cb_list[irq_id]) {
+ dev_err((mdev)->dev,
+ "Unable to register irq, irq_id=%d, it's already been register by %ps.\n",
+ irq_id, priv->irq_cb_list[irq_id]);
+ return -EFAULT;
+ }
+ priv->irq_cb_list[irq_id] = irq_cb;
+ priv->irq_cb_data[irq_id] = data;
+
+ return 0;
+}
+
+/**
+ * mtk_pci_unregister_irq() - Unregister a hardware IRQ callback
+ * @mdev: MTK MD device
+ * @irq_id: Hardware IRQ ID
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int mtk_pci_unregister_irq(struct mtk_md_dev *mdev, int irq_id)
+{
+ struct mtk_pci_priv *priv = mdev->hw_priv;
+
+ if (irq_id < 0 || irq_id >= MTK_IRQ_CNT_MAX)
+ return -EINVAL;
+
+ if (!priv->irq_cb_list[irq_id]) {
+ dev_err((mdev)->dev, "irq_id=%d has not been registered\n", irq_id);
+ return -EFAULT;
+ }
+ priv->irq_cb_list[irq_id] = NULL;
+ priv->irq_cb_data[irq_id] = NULL;
+
+ return 0;
+}
+
+/**
+ * mtk_pci_mask_irq() - Mask (disable) a hardware IRQ
+ * @mdev: MTK MD device
+ * @irq_id: Hardware IRQ ID
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int mtk_pci_mask_irq(struct mtk_md_dev *mdev, int irq_id)
+{
+ struct mtk_pci_priv *priv = mdev->hw_priv;
+
+ if (irq_id < 0 || irq_id >= MTK_IRQ_CNT_MAX ||
+ priv->irq_type != PCI_IRQ_MSIX) {
+ dev_err(mdev->dev, "Failed to mask irq: input irq_id=%d\n", irq_id);
+ return -EINVAL;
+ }
+
+ mtk_pci_mac_write32(priv, REG_IMASK_HOST_MSIX_CLR_GRP0_0, BIT(irq_id));
+
+ return 0;
+}
+
+/**
+ * mtk_pci_unmask_irq() - Unmask (enable) a hardware IRQ
+ * @mdev: MTK MD device
+ * @irq_id: Hardware IRQ ID
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int mtk_pci_unmask_irq(struct mtk_md_dev *mdev, int irq_id)
+{
+ struct mtk_pci_priv *priv = mdev->hw_priv;
+
+ if (irq_id < 0 || irq_id >= MTK_IRQ_CNT_MAX ||
+ priv->irq_type != PCI_IRQ_MSIX) {
+ dev_err(mdev->dev, "Failed to unmask irq: input irq_id=%d\n", irq_id);
+ return -EINVAL;
+ }
+
+ mtk_pci_mac_write32(priv, REG_IMASK_HOST_MSIX_SET_GRP0_0, BIT(irq_id));
+
+ return 0;
+}
+
+/**
+ * mtk_pci_clear_irq() - Clear (acknowledge) a hardware IRQ
+ * @mdev: MTK MD device
+ * @irq_id: Hardware IRQ ID
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int mtk_pci_clear_irq(struct mtk_md_dev *mdev, int irq_id)
+{
+ struct mtk_pci_priv *priv = mdev->hw_priv;
+
+ if (irq_id < 0 || irq_id >= MTK_IRQ_CNT_MAX ||
+ priv->irq_type != PCI_IRQ_MSIX) {
+ dev_err(mdev->dev, "Failed to clear irq: input irq_id=%d\n", irq_id);
+ return -EINVAL;
+ }
+
+ mtk_pci_mac_write32(priv, REG_MSIX_ISTATUS_HOST_GRP0_0, BIT(irq_id));
+
+ return 0;
+}
+
+static u32 mtk_pci_ext_d2h_evt_hw_bits(u32 chs)
+{
+ u32 hw_bits = 0;
+
+ SET_HW_BITS(hw_bits, chs, MHCCIF_EP2RC_EVT_BOOT_FLOW_SYNC,
+ DEV_EVT_D2H_BOOT_FLOW_SYNC);
+ SET_HW_BITS(hw_bits, chs, MHCCIF_EP2RC_EVT_ASYNC_HS_NOTIFY_SAP,
+ DEV_EVT_D2H_ASYNC_HS_NOTIFY_SAP);
+ SET_HW_BITS(hw_bits, chs, MHCCIF_EP2RC_EVT_ASYNC_HS_NOTIFY_MD,
+ DEV_EVT_D2H_ASYNC_HS_NOTIFY_MD);
+
+ return LE32_TO_U32(cpu_to_le32(hw_bits));
+}
+
+static u32 mtk_pci_ext_d2h_evt_chs(u32 hw_bits)
+{
+ u32 chs = 0;
+
+ if (!hw_bits)
+ return chs;
+
+ chs = FIELD_PREP(DEV_EVT_D2H_BOOT_FLOW_SYNC,
+ FIELD_GET(MHCCIF_EP2RC_EVT_BOOT_FLOW_SYNC, hw_bits)) |
+ FIELD_PREP(DEV_EVT_D2H_ASYNC_HS_NOTIFY_SAP,
+ FIELD_GET(MHCCIF_EP2RC_EVT_ASYNC_HS_NOTIFY_SAP, hw_bits)) |
+ FIELD_PREP(DEV_EVT_D2H_ASYNC_HS_NOTIFY_MD,
+ FIELD_GET(MHCCIF_EP2RC_EVT_ASYNC_HS_NOTIFY_MD, hw_bits));
+
+ return chs;
+}
+
+/**
+ * mtk_pci_register_ext_evt() - Register a callback for MHCCIF device events
+ * @mdev: MTK MD device
+ * @chs: Bitmask of event channels to register
+ * @evt_cb: Callback function
+ * @data: Private data passed to callback
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int mtk_pci_register_ext_evt(struct mtk_md_dev *mdev, u32 chs,
+ int (*evt_cb)(u32 status, void *data), void *data)
+{
+ struct mtk_pci_priv *priv = mdev->hw_priv;
+ struct mtk_mhccif_cb *cb;
+ int ret = 0;
+
+ if (!chs || !evt_cb)
+ return -EINVAL;
+
+ spin_lock_bh(&priv->mhccif_lock);
+ list_for_each_entry(cb, &priv->mhccif_cb_list, entry) {
+ if (cb->chs & chs) {
+ ret = -EFAULT;
+ dev_err((mdev)->dev,
+ "Unable to register evt, intersection: chs=0x%08x&0x%08x cb=%ps\n",
+ chs, cb->chs, cb->evt_cb);
+ goto err_spin_unlock;
+ }
+ }
+ cb = devm_kzalloc(mdev->dev, sizeof(*cb), GFP_ATOMIC);
+ if (!cb) {
+ ret = -ENOMEM;
+ goto err_spin_unlock;
+ }
+ cb->evt_cb = evt_cb;
+ cb->data = data;
+ cb->chs = chs;
+ list_add_tail(&cb->entry, &priv->mhccif_cb_list);
+err_spin_unlock:
+ spin_unlock_bh(&priv->mhccif_lock);
+
+ return ret;
+}
+
+/**
+ * mtk_pci_unregister_ext_evt() - Unregister an MHCCIF device event callback
+ * @mdev: MTK MD device
+ * @chs: Bitmask of event channels to unregister
+ */
+void mtk_pci_unregister_ext_evt(struct mtk_md_dev *mdev, u32 chs)
+{
+ struct mtk_pci_priv *priv = mdev->hw_priv;
+ struct mtk_mhccif_cb *cb, *next;
+
+ if (!chs)
+ return;
+
+ spin_lock_bh(&priv->mhccif_lock);
+ list_for_each_entry_safe(cb, next, &priv->mhccif_cb_list, entry) {
+ if (cb->chs == chs) {
+ list_del(&cb->entry);
+ devm_kfree(mdev->dev, cb);
+ goto out;
+ }
+ }
+ dev_warn((mdev)->dev,
+ "Unable to unregister evt, no chs=0x%08x has been registered.\n", chs);
+out:
+ spin_unlock_bh(&priv->mhccif_lock);
+}
+
+/**
+ * mtk_pci_mask_ext_evt() - Mask (disable) MHCCIF device events
+ * @mdev: MTK MD device
+ * @chs: Bitmask of event channels to mask
+ */
+void mtk_pci_mask_ext_evt(struct mtk_md_dev *mdev, u32 chs)
+{
+ struct mtk_pci_priv *priv = mdev->hw_priv;
+ u32 hw_bits = mtk_pci_ext_d2h_evt_hw_bits(chs);
+
+ mtk_pci_write32(mdev, priv->cfg->mhccif_rc_base_addr +
+ MHCCIF_EP2RC_SW_INT_EAP_MASK_SET, hw_bits);
+}
+
+/**
+ * mtk_pci_unmask_ext_evt() - Unmask (enable) MHCCIF device events
+ * @mdev: MTK MD device
+ * @chs: Bitmask of event channels to unmask
+ */
+void mtk_pci_unmask_ext_evt(struct mtk_md_dev *mdev, u32 chs)
+{
+ struct mtk_pci_priv *priv = mdev->hw_priv;
+ u32 hw_bits = mtk_pci_ext_d2h_evt_hw_bits(chs);
+
+ mtk_pci_write32(mdev, priv->cfg->mhccif_rc_base_addr +
+ MHCCIF_EP2RC_SW_INT_EAP_MASK_CLR, hw_bits);
+}
+
+/**
+ * mtk_pci_clear_ext_evt() - Clear (acknowledge) MHCCIF device events
+ * @mdev: MTK MD device
+ * @chs: Bitmask of event channels to clear
+ */
+void mtk_pci_clear_ext_evt(struct mtk_md_dev *mdev, u32 chs)
+{
+ struct mtk_pci_priv *priv = mdev->hw_priv;
+ u32 hw_bits = mtk_pci_ext_d2h_evt_hw_bits(chs);
+
+ mtk_pci_write32(mdev, priv->cfg->mhccif_rc_base_addr +
+ MHCCIF_EP2RC_SW_INT_ACK, hw_bits);
+}
+
+static u32 mtk_pci_ext_h2d_evt_hw_bits(u32 chs)
+{
+ u32 hw_bits = 0;
+
+ SET_HW_BITS(hw_bits, chs, MHCCIF_RC2EP_EVT_DEVICE_RESET,
+ DEV_EVT_H2D_DEVICE_RESET);
+ return LE32_TO_U32(cpu_to_le32(hw_bits));
+}
+
+/**
+ * mtk_pci_send_ext_evt() - Send an MHCCIF event to the modem
+ * @mdev: MTK MD device
+ * @ch: Event channel to trigger (must be a single bit)
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int mtk_pci_send_ext_evt(struct mtk_md_dev *mdev, u32 ch)
+{
+ struct mtk_pci_priv *priv = mdev->hw_priv;
+ u32 rc_base, hw_bits;
+
+ rc_base = priv->cfg->mhccif_rc_base_addr;
+
+ /* Only allow one ch to be triggered at a time */
+ if (!is_power_of_2(ch)) {
+ dev_err((mdev)->dev, "Unsupported ext evt ch=0x%08x\n", ch);
+ return -EINVAL;
+ }
+
+ hw_bits = mtk_pci_ext_h2d_evt_hw_bits(ch);
+ mtk_pci_write32(mdev, rc_base + MHCCIF_RC2EP_SW_BSY, hw_bits);
+ mtk_pci_write32(mdev, rc_base + MHCCIF_RC2EP_SW_TCHNUM, ffs(hw_bits) - 1);
+ return 0;
+}
+
+static u32 mtk_pci_get_ext_evt_hw_status(struct mtk_md_dev *mdev)
+{
+ struct mtk_pci_priv *priv = mdev->hw_priv;
+
+ return mtk_pci_read32(mdev, priv->cfg->mhccif_rc_base_addr +
+ MHCCIF_EP2RC_SW_INT_STS);
+}
+
+/**
+ * mtk_pci_fldr() - Perform a Function Level Device Reset via ACPI _RST
+ * @mdev: MTK MD device
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int mtk_pci_fldr(struct mtk_md_dev *mdev)
+{
+#ifdef CONFIG_ACPI
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ acpi_status acpi_ret;
+ acpi_handle handle;
+
+ if (acpi_disabled) {
+ dev_err((mdev)->dev, "Unsupported, acpi function isn't enable\n");
+ return -ENODEV;
+ }
+
+ handle = ACPI_HANDLE(mdev->dev);
+
+ if (!handle) {
+ dev_err((mdev)->dev, "Unsupported, acpi handle isn't found\n");
+ return -ENODEV;
+ }
+
+ if (!acpi_has_method(handle, "_RST")) {
+ dev_err((mdev)->dev, "Unsupported, _RST method isn't found\n");
+ return -ENODEV;
+ }
+
+ acpi_ret = acpi_evaluate_object(handle, "_RST", NULL, &buffer);
+ if (ACPI_FAILURE(acpi_ret)) {
+ dev_err((mdev)->dev, "Failed to execute _RST method: %s\n",
+ acpi_format_exception(acpi_ret));
+ return -EFAULT;
+ }
+
+ acpi_os_free(buffer.pointer);
+
+ return 0;
+#else /* !CONFIG_ACPI */
+ dev_err((mdev)->dev, "Unsupported, CONFIG ACPI hasn't been set to 'y'\n");
+
+ return -ENODEV;
+#endif /* !CONFIG_ACPI */
+}
+
+/**
+ * mtk_pci_pldr() - Perform a PCIe Link Down Reset via ACPI PXP._OFF/_ON
+ * @mdev: MTK MD device
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int mtk_pci_pldr(struct mtk_md_dev *mdev)
+{
+#ifdef CONFIG_ACPI
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct pci_dev *bridge;
+ acpi_status acpi_ret;
+ acpi_handle handle;
+
+ if (acpi_disabled) {
+ dev_err((mdev)->dev, "Unsupported, acpi function isn't enable\n");
+ return -ENODEV;
+ }
+
+ bridge = pci_upstream_bridge(to_pci_dev(mdev->dev));
+ if (!bridge) {
+ dev_err((mdev)->dev, "Unable to find bridge\n");
+ return -ENODEV;
+ }
+
+ handle = ACPI_HANDLE(&bridge->dev);
+ if (!handle) {
+ dev_err((mdev)->dev, "Unsupported, acpi handle isn't found\n");
+ return -ENODEV;
+ }
+ if (!acpi_has_method(handle, "PXP._OFF") ||
+ !acpi_has_method(handle, "PXP._ON")) {
+ dev_err((mdev)->dev, "Unsupported, pldr method isn't supported\n");
+ return -ENODEV;
+ }
+ acpi_ret = acpi_evaluate_object(handle, "PXP._OFF", NULL, &buffer);
+ if (ACPI_FAILURE(acpi_ret)) {
+ dev_err((mdev)->dev, "Failed to execute _OFF method: %s\n",
+ acpi_format_exception(acpi_ret));
+ return -EFAULT;
+ }
+ acpi_os_free(buffer.pointer);
+
+ msleep(MTK_PLDR_POWER_OFF_DELAY_MS);
+
+ buffer.length = ACPI_ALLOCATE_BUFFER;
+ buffer.pointer = NULL;
+ acpi_ret = acpi_evaluate_object(handle, "PXP._ON", NULL, &buffer);
+ if (ACPI_FAILURE(acpi_ret)) {
+ dev_err((mdev)->dev, "Failed to execute _ON method: %s\n",
+ acpi_format_exception(acpi_ret));
+ return -EFAULT;
+ }
+ acpi_os_free(buffer.pointer);
+
+ return 0;
+#else
+ dev_err((mdev)->dev, "Unsupported, CONFIG ACPI hasn't been set to 'y'\n");
+
+ return -ENODEV;
+#endif
+}
+
+/**
+ * mtk_pci_get_dev_cfg() - Read the device configuration from the modem
+ * @mdev: MTK MD device
+ *
+ * Return: Device configuration value.
+ */
+u32 mtk_pci_get_dev_cfg(struct mtk_md_dev *mdev)
+{
+ u32 val;
+
+ val = mtk_pci_mac_read32(mdev->hw_priv, REG_PCIE_DEBUG_DUMMY_4);
+ return (val >> MTK_CFG_INFO_BIT_SHIFT);
+}
+
+static int mtk_pci_dev_reset(struct mtk_md_dev *mdev, enum mtk_reset_type type)
+{
+ switch (type) {
+ case RESET_MHCCIF:
+ return mtk_pci_send_ext_evt(mdev, DEV_EVT_H2D_DEVICE_RESET);
+ case RESET_FLDR:
+ return mtk_pci_fldr(mdev);
+ case RESET_PLDR:
+ return mtk_pci_pldr(mdev);
+ default:
+ return -EINVAL;
+ }
+}
+
+/**
+ * mtk_pci_reset() - Reset the modem device
+ * @mdev: MTK MD device
+ * @type: Reset type (MHCCIF, FLDR, or PLDR)
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int mtk_pci_reset(struct mtk_md_dev *mdev, enum mtk_reset_type type)
+{
+ return mtk_pci_dev_reset(mdev, type);
+}
+
+/**
+ * mtk_pci_link_check() - Check if the PCIe link to the modem is active
+ * @mdev: MTK MD device
+ *
+ * Return: true if the device is present, false otherwise.
+ */
+bool mtk_pci_link_check(struct mtk_md_dev *mdev)
+{
+ return pci_device_is_present(to_pci_dev(mdev->dev));
+}
+
+static void mtk_mhccif_isr_work(struct work_struct *work)
+{
+ struct mtk_pci_priv *priv =
+ container_of(work, struct mtk_pci_priv, mhccif_work);
+ struct mtk_md_dev *mdev = priv->irq_desc->mdev;
+ struct mtk_mhccif_cb *cb;
+ u32 stat, mask, chs;
+
+ stat = mtk_pci_get_ext_evt_hw_status(mdev);
+ mask = mtk_pci_read32(mdev, priv->cfg->mhccif_rc_base_addr
+ + MHCCIF_EP2RC_SW_INT_EAP_MASK);
+ if (unlikely(stat == U32_MAX && !(mtk_pci_link_check(mdev)))) {
+ /* When link failed, we don't need to unmask/clear. */
+ dev_err((mdev)->dev, "Failed to check link in MHCCIF handler.\n");
+ return;
+ }
+
+ stat &= ~mask;
+ chs = mtk_pci_ext_d2h_evt_chs(stat);
+ spin_lock_bh(&priv->mhccif_lock);
+ list_for_each_entry(cb, &priv->mhccif_cb_list, entry) {
+ if (cb->chs & chs)
+ cb->evt_cb(cb->chs & chs, cb->data);
+ }
+ spin_unlock_bh(&priv->mhccif_lock);
+
+ mtk_pci_clear_irq(mdev, priv->mhccif_irq_id);
+ mtk_pci_unmask_irq(mdev, priv->mhccif_irq_id);
+}
+
+static const struct pci_device_id t9xx_pci_table[] = {
+ MTK_PCI_DEV_CFG(0x0900, mtk_dev_cfg_0900),
+ CEI_PCI_DEV_CFG(0x01CA, mtk_dev_cfg_0900),
+ {/* end: all zeroes */}
+};
+
+MODULE_DEVICE_TABLE(pci, t9xx_pci_table);
+
+static int mtk_pci_bar_init(struct mtk_md_dev *mdev)
+{
+ struct pci_dev *pdev = to_pci_dev(mdev->dev);
+ struct mtk_pci_priv *priv = mdev->hw_priv;
+ u32 bar[MTK_PCI_BAR_NUM];
+ int i, ret;
+
+ for (i = 0; i < MTK_PCI_BAR_NUM; i++)
+ pci_read_config_dword(to_pci_dev(mdev->dev),
+ PCI_BASE_ADDRESS_0 + (i << 2), bar + i);
+
+ ret = pcim_iomap_regions(pdev, MTK_REQUESTED_BARS, mdev->dev_str);
+ if (ret) {
+ dev_err((mdev)->dev, "Failed to init MMIO. ret=%d\n", ret);
+ return ret;
+ }
+
+ /* get ioremapped memory */
+ priv->mac_reg_base = pcim_iomap_table(pdev)[MTK_BAR_0_1_IDX];
+ priv->bar23_addr = pcim_iomap_table(pdev)[MTK_BAR_2_3_IDX];
+ if (!priv->mac_reg_base || !priv->bar23_addr) {
+ dev_err((mdev)->dev, "Failed to init BAR.\n");
+ return -EINVAL;
+ }
+ /* We use MD view base address "0" to observe registers */
+ priv->ext_reg_base = priv->bar23_addr - ATR_PCIE_REG_TRSL_ADDR;
+
+ return 0;
+}
+
+static int mtk_mhccif_irq_cb(int irq_id, void *data)
+{
+ struct mtk_md_dev *mdev = data;
+ struct mtk_pci_priv *priv;
+
+ priv = mdev->hw_priv;
+ queue_work(system_highpri_wq, &priv->mhccif_work);
+
+ return 0;
+}
+
+static int mtk_mhccif_init(struct mtk_md_dev *mdev)
+{
+ struct mtk_pci_priv *priv = mdev->hw_priv;
+ int ret;
+
+ INIT_LIST_HEAD(&priv->mhccif_cb_list);
+ spin_lock_init(&priv->mhccif_lock);
+ INIT_WORK(&priv->mhccif_work, mtk_mhccif_isr_work);
+
+ ret = mtk_pci_get_irq_id(mdev, MTK_IRQ_SRC_MHCCIF);
+ if (ret < 0) {
+ dev_err((mdev)->dev, "Failed to get mhccif_irq_id. ret=%d\n", ret);
+ return ret;
+ }
+ priv->mhccif_irq_id = ret;
+
+ ret = mtk_pci_register_irq(mdev, priv->mhccif_irq_id, mtk_mhccif_irq_cb, mdev);
+ if (ret) {
+ dev_err((mdev)->dev, "Failed to register mhccif_irq callback\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void mtk_mhccif_exit(struct mtk_md_dev *mdev)
+{
+ struct mtk_pci_priv *priv = mdev->hw_priv;
+
+ mtk_pci_unregister_irq(mdev, priv->mhccif_irq_id);
+ cancel_work_sync(&priv->mhccif_work);
+}
+
+static irqreturn_t mtk_pci_irq_handler(struct mtk_md_dev *mdev, u32 irq_state)
+{
+ struct mtk_pci_priv *priv = mdev->hw_priv;
+ int irq_id;
+
+ /* Check whether each set bit has a callback, if has, call it */
+ do {
+ irq_id = fls(irq_state) - 1;
+ irq_state &= ~BIT(irq_id);
+ if (likely(priv->irq_cb_list[irq_id]))
+ priv->irq_cb_list[irq_id](irq_id, priv->irq_cb_data[irq_id]);
+ else
+ dev_err((mdev)->dev, "Unhandled irq_id=%d, no callback for it.\n", irq_id);
+ } while (irq_state);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mtk_pci_irq_msix(int irq, void *data)
+{
+ struct mtk_pci_irq_desc *irq_desc = data;
+ struct mtk_md_dev *mdev = irq_desc->mdev;
+ struct mtk_pci_priv *priv;
+ u32 irq_state, irq_enable;
+
+ priv = mdev->hw_priv;
+ irq_state = mtk_pci_mac_read32(priv, REG_MSIX_ISTATUS_HOST_GRP0_0);
+ irq_enable = mtk_pci_mac_read32(priv, REG_IMASK_HOST_MSIX_GRP0_0);
+ irq_state &= irq_enable;
+
+ if (unlikely(!irq_state) ||
+ unlikely(!((irq_state & GENMASK(priv->irq_cnt - 1, 0)) &
+ irq_desc->msix_bits)))
+ return IRQ_NONE;
+
+ /* Mask the bit and user needs to unmask by itself */
+ mtk_pci_mac_write32(priv, REG_IMASK_HOST_MSIX_CLR_GRP0_0,
+ irq_state & ~BIT(30));
+
+ return mtk_pci_irq_handler(mdev, irq_state);
+}
+
+static int mtk_pci_request_irq_msix(struct mtk_md_dev *mdev,
+ int irq_cnt_allocated)
+{
+ struct mtk_pci_priv *priv = mdev->hw_priv;
+ struct mtk_pci_irq_desc *irq_desc;
+ struct pci_dev *pdev;
+ int irq_cnt;
+ int ret, i;
+
+ /* calculate the nearest 2's power number */
+ irq_cnt = BIT(fls(irq_cnt_allocated) - 1);
+ pdev = to_pci_dev(mdev->dev);
+ irq_desc = priv->irq_desc;
+ for (i = 0; i < irq_cnt; i++) {
+ irq_desc[i].mdev = mdev;
+ irq_desc[i].msix_bits = BIT(i);
+ snprintf(irq_desc[i].name, MTK_IRQ_NAME_LEN, "msix%d-%s", i, mdev->dev_str);
+ ret = pci_request_irq(pdev, i, mtk_pci_irq_msix, NULL,
+ &irq_desc[i], irq_desc[i].name);
+ if (ret) {
+ dev_err((mdev)->dev, "Failed to request %s: ret=%d\n",
+ irq_desc[i].name, ret);
+ for (i--; i >= 0; i--)
+ pci_free_irq(pdev, i, &irq_desc[i]);
+ return ret;
+ }
+ }
+ priv->irq_cnt = irq_cnt;
+ priv->irq_type = PCI_IRQ_MSIX;
+
+ if (irq_cnt != MTK_IRQ_CNT_MAX)
+ mtk_pci_set_msix_merged(priv, irq_cnt);
+
+ return 0;
+}
+
+static int mtk_pci_request_irq(struct mtk_md_dev *mdev)
+{
+ struct pci_dev *pdev = to_pci_dev(mdev->dev);
+ int irq_cnt, ret;
+
+ irq_cnt = pci_alloc_irq_vectors(pdev, MTK_IRQ_CNT_MIN,
+ MTK_IRQ_CNT_MAX, PCI_IRQ_MSIX);
+
+ if (irq_cnt < MTK_IRQ_CNT_MIN) {
+ dev_err(mdev->dev,
+ "Unable to alloc pci irq vectors. ret=%d maxirqcnt=%d irqtype=0x%x\n",
+ irq_cnt, MTK_IRQ_CNT_MAX, PCI_IRQ_MSIX);
+ return -EFAULT;
+ }
+
+ ret = mtk_pci_request_irq_msix(mdev, irq_cnt);
+ if (ret)
+ pci_free_irq_vectors(pdev);
+
+ return ret;
+}
+
+static void mtk_pci_free_irq(struct mtk_md_dev *mdev)
+{
+ struct pci_dev *pdev = to_pci_dev(mdev->dev);
+ struct mtk_pci_priv *priv = mdev->hw_priv;
+ int i;
+
+ for (i = 0; i < priv->irq_cnt; i++)
+ pci_free_irq(pdev, i, &priv->irq_desc[i]);
+
+ pci_free_irq_vectors(pdev);
+}
+
+static const struct mtk_dev_ops pci_hw_ops = {
+ .get_dev_state = mtk_pci_get_dev_state,
+ .ack_dev_state = mtk_pci_ack_dev_state,
+ .get_dev_cfg = mtk_pci_get_dev_cfg,
+ .register_dev_evt = mtk_pci_register_ext_evt,
+ .unregister_dev_evt = mtk_pci_unregister_ext_evt,
+ .mask_dev_evt = mtk_pci_mask_ext_evt,
+ .unmask_dev_evt = mtk_pci_unmask_ext_evt,
+ .clear_dev_evt = mtk_pci_clear_ext_evt,
+ .send_dev_evt = mtk_pci_send_ext_evt,
+};
+
+static int mtk_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct device *dev = &pdev->dev;
+ struct mtk_pci_priv *priv;
+ struct mtk_md_dev *mdev;
+ int ret;
+
+ mdev = devm_kzalloc(dev, sizeof(*mdev), GFP_KERNEL);
+ if (!mdev) {
+ ret = -ENOMEM;
+ goto log_err;
+ }
+ mdev->dev_ops = &pci_hw_ops;
+ mdev->dev = dev;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ ret = -ENOMEM;
+ goto free_cntx_data;
+ }
+
+ pci_set_drvdata(pdev, mdev);
+ priv->cfg = (void *)id->driver_data;
+ priv->mdev = mdev;
+ mdev->hw_ver = pdev->device;
+ mdev->hw_priv = priv;
+ mdev->dev = dev;
+ snprintf(mdev->dev_str, MTK_DEV_STR_LEN, "%02x%02x%d",
+ pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
+ if (pdev->state_saved)
+ pci_restore_state(pdev);
+
+ ret = pcim_enable_device(pdev);
+ if (ret) {
+ dev_err((mdev)->dev, "Failed to enable pci device.\n");
+ goto free_priv_data;
+ }
+
+ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+ if (ret) {
+ dev_err((mdev)->dev, "Failed to set DMA Mask and Coherent. (ret=%d)\n", ret);
+ goto free_priv_data;
+ }
+
+ ret = mtk_pci_bar_init(mdev);
+ if (ret)
+ goto free_priv_data;
+
+ ret = priv->cfg->atr_init(mdev);
+ if (ret)
+ goto free_priv_data;
+
+ ret = mtk_mhccif_init(mdev);
+ if (ret)
+ goto free_priv_data;
+
+ /* mask all irqs */
+ if (priv->cfg->flag & MTK_CFG_IRQ_DFLT_MASK)
+ mtk_pci_mac_write32(priv, REG_IMASK_HOST_MSIX_CLR_GRP0_0, U32_MAX);
+
+ ret = mtk_pci_request_irq(mdev);
+ if (ret)
+ goto free_mhccif;
+
+ pci_set_master(pdev);
+ mtk_pci_unmask_irq(mdev, priv->mhccif_irq_id);
+
+ if (mtk_pci_link_check(mdev)) {
+ pci_save_state(pdev);
+ } else {
+ ret = -ENOLINK;
+ goto clear_master;
+ }
+
+ priv->saved_state = pci_store_saved_state(pdev);
+ if (!priv->saved_state) {
+ ret = -EFAULT;
+ goto clear_master;
+ }
+
+ return 0;
+
+clear_master:
+ pci_clear_master(pdev);
+ mtk_pci_free_irq(mdev);
+free_mhccif:
+ mtk_mhccif_exit(mdev);
+free_priv_data:
+ devm_kfree(dev, priv);
+free_cntx_data:
+ devm_kfree(dev, mdev);
+log_err:
+ dev_err(dev, "Failed to probe device, ret=%d\n", ret);
+
+ return ret;
+}
+
+static void mtk_pci_remove(struct pci_dev *pdev)
+{
+ struct mtk_md_dev *mdev = pci_get_drvdata(pdev);
+ struct mtk_pci_priv *priv = mdev->hw_priv;
+ struct device *dev = &pdev->dev;
+
+ mtk_pci_mask_irq(mdev, priv->mhccif_irq_id);
+
+ if (mtk_pci_pldr(mdev)) {
+ dev_warn(dev, "Failed to execute PLDR, try external event\n");
+ mtk_pci_reset(mdev, RESET_MHCCIF);
+ }
+
+ pci_clear_master(pdev);
+ mtk_pci_free_irq(mdev);
+ mtk_mhccif_exit(mdev);
+ pci_load_and_free_saved_state(pdev, &priv->saved_state);
+
+ devm_kfree(dev, priv);
+ devm_kfree(dev, mdev);
+}
+
+static pci_ers_result_t mtk_pci_error_detected(struct pci_dev *pdev,
+ pci_channel_state_t state)
+{
+ struct mtk_md_dev *mdev = pci_get_drvdata(pdev);
+
+ dev_err((mdev)->dev, "AER detected: pci_channel_state_t=%d\n", state);
+
+ /* Request a slot reset. */
+ return PCI_ERS_RESULT_CAN_RECOVER;
+}
+
+static const struct pci_error_handlers mtk_pci_err_handler = {
+ .error_detected = mtk_pci_error_detected,
+};
+
+static struct pci_driver mtk_pci_drv = {
+ .name = "mtk_pci_drv",
+ .id_table = t9xx_pci_table,
+ .probe = mtk_pci_probe,
+ .remove = mtk_pci_remove,
+ .err_handler = &mtk_pci_err_handler
+};
+
+module_pci_driver(mtk_pci_drv);
+
+MODULE_DESCRIPTION("MediaTek T9xx PCIe WWAN driver pcie layer");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wwan/t9xx/pcie/mtk_pci.h b/drivers/net/wwan/t9xx/pcie/mtk_pci.h
new file mode 100644
index 000000000000..9819a1b07c1b
--- /dev/null
+++ b/drivers/net/wwan/t9xx/pcie/mtk_pci.h
@@ -0,0 +1,234 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (c) 2022, MediaTek Inc.
+ */
+
+#ifndef __MTK_PCI_H__
+#define __MTK_PCI_H__
+
+#include <linux/pci.h>
+
+#include "../mtk_dev.h"
+
+enum mtk_irq_src {
+ MTK_IRQ_SRC_MIN,
+ MTK_IRQ_SRC_MHCCIF,
+ MTK_IRQ_SRC_DPMAIF,
+ MTK_IRQ_SRC_DPMAIF2,
+ MTK_IRQ_SRC_CLDMA0,
+ MTK_IRQ_SRC_CLDMA1,
+ MTK_IRQ_SRC_CLDMA2,
+ MTK_IRQ_SRC_CLDMA3,
+ MTK_IRQ_SRC_PM_LOCK,
+ MTK_IRQ_SRC_DPMAIF3,
+ MTK_IRQ_SRC_DPMAIF6,
+ MTK_IRQ_SRC_MAX
+};
+
+enum mtk_reset_type {
+ RESET_FLDR,
+ RESET_PLDR,
+ RESET_MHCCIF,
+};
+
+enum mtk_atr_type {
+ ATR_PCI2AXI = 0,
+ ATR_AXI2PCI,
+};
+
+enum mtk_atr_src_port {
+ ATR_SRC_PCI_WIN0 = 0,
+ ATR_SRC_PCI_WIN1,
+ ATR_SRC_AXIS_0,
+ ATR_SRC_AXIS_1,
+ ATR_SRC_AXIS_2,
+ ATR_SRC_AXIS_3,
+};
+
+enum mtk_atr_dst_port {
+ ATR_DST_PCI_TRX = 0,
+ ATR_DST_AXIM_0 = 4,
+ ATR_DST_AXIM_1,
+ ATR_DST_AXIM_2,
+ ATR_DST_AXIM_3,
+};
+
+enum mtk_pci_evt_h2d {
+ DEV_EVT_H2D_EXTEND_BASE = DEV_EVT_H2D_MAX,
+ EXT_EVT_H2D_RESERVED_FOR_CLDMA0 = DEV_EVT_H2D_EXTEND_BASE << 1,
+ EXT_EVT_H2D_RESERVED_FOR_CLDMA1 = DEV_EVT_H2D_EXTEND_BASE << 2,
+ EXT_EVT_H2D_RESERVED_FOR_CLDMA3 = DEV_EVT_H2D_EXTEND_BASE << 3,
+ EXT_EVT_H2D_RESERVED_FOR_CLDMA2 = DEV_EVT_H2D_EXTEND_BASE << 4,
+ EXT_EVT_H2D_RESERVED_FOR_DPMAIF = DEV_EVT_H2D_EXTEND_BASE << 5,
+ EXT_EVT_H2D_PCIE_PM_SUSPEND_REQ = DEV_EVT_H2D_EXTEND_BASE << 6,
+ EXT_EVT_H2D_PCIE_PM_RESUME_REQ = DEV_EVT_H2D_EXTEND_BASE << 7,
+ EXT_EVT_H2D_PCIE_PM_SUSPEND_REQ_AP = DEV_EVT_H2D_EXTEND_BASE << 8,
+ EXT_EVT_H2D_PCIE_PM_RESUME_REQ_AP = DEV_EVT_H2D_EXTEND_BASE << 9,
+ EXT_EVT_H2D_RESERVED_FOR_TEST = DEV_EVT_H2D_EXTEND_BASE << 11,
+};
+
+enum mtk_pci_evt_d2h {
+ DEV_EVT_D2H_EXTEND_BASE = DEV_EVT_D2H_MAX,
+ EXT_EVT_D2H_RESERVED_FOR_CLDMA0 = DEV_EVT_D2H_EXTEND_BASE << 1,
+ EXT_EVT_D2H_RESERVED_FOR_CLDMA1 = DEV_EVT_D2H_EXTEND_BASE << 2,
+ EXT_EVT_D2H_RESERVED_FOR_CLDMA3 = DEV_EVT_D2H_EXTEND_BASE << 3,
+ EXT_EVT_D2H_RESERVED_FOR_CLDMA2 = DEV_EVT_D2H_EXTEND_BASE << 4,
+ EXT_EVT_D2H_RESERVED_FOR_DPMAIF = DEV_EVT_D2H_EXTEND_BASE << 5,
+ EXT_EVT_D2H_PCIE_PM_SUSPEND_ACK = DEV_EVT_D2H_EXTEND_BASE << 6,
+ EXT_EVT_D2H_PCIE_PM_RESUME_ACK = DEV_EVT_D2H_EXTEND_BASE << 7,
+ EXT_EVT_D2H_PCIE_PM_SUSPEND_ACK_AP = DEV_EVT_D2H_EXTEND_BASE << 8,
+ EXT_EVT_D2H_PCIE_PM_RESUME_ACK_AP = DEV_EVT_D2H_EXTEND_BASE << 9,
+ EXT_EVT_D2H_SOFT_OFF_NOTIFY = DEV_EVT_D2H_EXTEND_BASE << 10,
+ EXT_EVT_D2H_FRC_DONE_NOTIFY = DEV_EVT_D2H_EXTEND_BASE << 11,
+ EXT_EVT_D2H_RESERVED_FOR_TEST1 = DEV_EVT_D2H_EXTEND_BASE << 12,
+ EXT_EVT_D2H_RESERVED_FOR_TEST2 = DEV_EVT_D2H_EXTEND_BASE << 13,
+};
+
+#define MTK_PCI_CLASS 0x0D4000
+#define MTK_PCI_VENDOR_ID 0x14C3
+#define CEI_PCI_VENDOR_ID 0x03F0
+
+#define MTK_CFG_INFO_BIT_SHIFT 4
+
+#define MTK_PCI_DEV_CFG(id, cfg) \
+{ \
+ PCI_DEVICE(MTK_PCI_VENDOR_ID, id), \
+ MTK_PCI_CLASS, PCI_ANY_ID, \
+ .driver_data = (kernel_ulong_t)&(cfg), \
+}
+
+#define CEI_PCI_DEV_CFG(id, cfg) \
+{ \
+ PCI_DEVICE(CEI_PCI_VENDOR_ID, id), \
+ MTK_PCI_CLASS, PCI_ANY_ID, \
+ .driver_data = (kernel_ulong_t)&(cfg), \
+}
+
+#define MTK_CFG_IRQ_DFLT_MASK BIT(0)
+#define MTK_CFG_DISABLE_AP_DRM BIT(2)
+#define MTK_CFG_PM_SW_IRQ BIT(6)
+
+#define MTK_BAR_0_1_IDX 0
+#define MTK_BAR_2_3_IDX 2
+
+#define MTK_REQUESTED_BARS \
+ ((1 << MTK_BAR_0_1_IDX) | \
+ (1 << MTK_BAR_2_3_IDX))
+
+#define MTK_IRQ_CNT_MIN 1
+#define MTK_IRQ_CNT_MAX 32
+#define MTK_IRQ_NAME_LEN 32
+
+#define ATR_PORT_OFFSET 0x100
+#define ATR_TABLE_OFFSET 0x20
+#define ATR_TABLE_NUM_PER_ATR 8
+#define ATR_PCIE_REG_TRSL_ADDR 0x10000000
+#define ATR_PCIE_REG_SIZE 0x00400000
+#define ATR_PCIE_REG_PORT ATR_SRC_PCI_WIN0
+#define ATR_PCIE_REG_TABLE_NUM 1
+#define ATR_PCIE_REG_TRSL_PORT ATR_DST_AXIM_0
+#define ATR_PCIE_DEV_DMA_SRC_ADDR 0x00000000
+#define ATR_PCIE_DEV_DMA_TRANSPARENT 1
+#define ATR_PCIE_DEV_DMA_SIZE 0
+#define ATR_PCIE_DEV_DMA_TABLE_NUM 0
+#define ATR_PCIE_DEV_DMA_TRSL_ADDR 0x00000000
+
+struct mtk_pci_irq_desc {
+ struct mtk_md_dev *mdev;
+ u32 msix_bits;
+ char name[MTK_IRQ_NAME_LEN];
+};
+
+struct mtk_pci_dev_cfg {
+ u32 flag;
+ u32 mhccif_rc_base_addr;
+ u32 istatus_host_ctrl_addr;
+ int irq_tbl[MTK_IRQ_SRC_MAX];
+ int (*atr_init)(struct mtk_md_dev *mdev);
+};
+
+extern const struct mtk_pci_dev_cfg mtk_dev_cfg_0900;
+
+struct mtk_pci_priv {
+ struct mtk_md_dev *mdev;
+ const struct mtk_pci_dev_cfg *cfg;
+ void __iomem *bar23_addr;
+ void __iomem *mac_reg_base;
+ void __iomem *ext_reg_base;
+ int irq_cnt;
+ int irq_type;
+ void *irq_cb_data[MTK_IRQ_CNT_MAX];
+
+ int (*irq_cb_list[MTK_IRQ_CNT_MAX])(int irq_id, void *data);
+ struct mtk_pci_irq_desc irq_desc[MTK_IRQ_CNT_MAX];
+ struct list_head mhccif_cb_list;
+ /* mhccif_lock: lock to protect mhccif_cb_list */
+ spinlock_t mhccif_lock;
+ struct work_struct mhccif_work;
+ int mhccif_irq_id;
+ struct pci_saved_state *saved_state;
+};
+
+struct mtk_atr_cfg {
+ u64 src_addr;
+ u64 trsl_addr;
+ u64 size;
+ u32 type; /* Port type */
+ u32 port; /* Port number */
+ u32 table; /* Table number (8 tables for each port) */
+ u32 trsl_id;
+ u32 trsl_param;
+ u32 transparent;
+};
+
+/* BAR 0/1 MMIO access */
+static inline u32 mtk_pci_mac_read32(struct mtk_pci_priv *priv, u64 addr)
+{
+ return ioread32(priv->mac_reg_base + addr);
+}
+
+static inline void mtk_pci_mac_write32(struct mtk_pci_priv *priv, u64 addr, u32 val)
+{
+ iowrite32(val, priv->mac_reg_base + addr);
+}
+
+/* BAR 2/3 MMIO access */
+static inline u32 mtk_pci_read32(struct mtk_md_dev *mdev, u64 addr)
+{
+ return ioread32(((struct mtk_pci_priv *)mdev->hw_priv)->ext_reg_base + addr);
+}
+
+static inline void mtk_pci_write32(struct mtk_md_dev *mdev, u64 addr, u32 val)
+{
+ iowrite32(val, ((struct mtk_pci_priv *)mdev->hw_priv)->ext_reg_base + addr);
+}
+
+/* Device operations */
+u32 mtk_pci_get_dev_state(struct mtk_md_dev *mdev);
+void mtk_pci_ack_dev_state(struct mtk_md_dev *mdev, u32 state);
+u32 mtk_pci_get_dev_cfg(struct mtk_md_dev *mdev);
+/* IRQ Related operations */
+int mtk_pci_get_irq_id(struct mtk_md_dev *mdev, enum mtk_irq_src irq_src);
+int mtk_pci_get_virq_id(struct mtk_md_dev *mdev, int irq_id);
+int mtk_pci_register_irq(struct mtk_md_dev *mdev, int irq_id,
+ int (*irq_cb)(int irq_id, void *data), void *data);
+int mtk_pci_unregister_irq(struct mtk_md_dev *mdev, int irq_id);
+int mtk_pci_mask_irq(struct mtk_md_dev *mdev, int irq_id);
+int mtk_pci_unmask_irq(struct mtk_md_dev *mdev, int irq_id);
+int mtk_pci_clear_irq(struct mtk_md_dev *mdev, int irq_id);
+/* External event related */
+int mtk_pci_register_ext_evt(struct mtk_md_dev *mdev, u32 chs,
+ int (*evt_cb)(u32 status, void *data), void *data);
+void mtk_pci_unregister_ext_evt(struct mtk_md_dev *mdev, u32 chs);
+void mtk_pci_mask_ext_evt(struct mtk_md_dev *mdev, u32 chs);
+void mtk_pci_unmask_ext_evt(struct mtk_md_dev *mdev, u32 chs);
+void mtk_pci_clear_ext_evt(struct mtk_md_dev *mdev, u32 chs);
+int mtk_pci_send_ext_evt(struct mtk_md_dev *mdev, u32 ch);
+int mtk_pci_fldr(struct mtk_md_dev *mdev);
+int mtk_pci_pldr(struct mtk_md_dev *mdev);
+int mtk_pci_reset(struct mtk_md_dev *mdev, enum mtk_reset_type type);
+bool mtk_pci_link_check(struct mtk_md_dev *mdev);
+int mtk_pci_setup_atr(struct mtk_md_dev *mdev, struct mtk_atr_cfg *cfg);
+void mtk_pci_atr_disable(struct mtk_pci_priv *priv);
+
+#endif /* __MTK_PCI_H__ */
diff --git a/drivers/net/wwan/t9xx/pcie/mtk_pci_drv_m9xx.c b/drivers/net/wwan/t9xx/pcie/mtk_pci_drv_m9xx.c
new file mode 100644
index 000000000000..88b44142afb7
--- /dev/null
+++ b/drivers/net/wwan/t9xx/pcie/mtk_pci_drv_m9xx.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022, MediaTek Inc.
+ */
+#include <linux/types.h>
+#include "mtk_pci.h"
+#include "mtk_pci_reg.h"
+
+static int mtk_pci_atr_init_m9xx(struct mtk_md_dev *mdev)
+{
+ struct pci_dev *pdev = to_pci_dev(mdev->dev);
+ struct mtk_pci_priv *priv = mdev->hw_priv;
+ struct mtk_atr_cfg cfg;
+ int port, ret;
+
+ mtk_pci_atr_disable(priv);
+
+ /* Config ATR for RC to access device's register */
+ cfg.src_addr = pci_resource_start(pdev, MTK_BAR_2_3_IDX);
+ cfg.size = ATR_PCIE_REG_SIZE;
+ cfg.trsl_addr = ATR_PCIE_REG_TRSL_ADDR;
+ cfg.type = ATR_PCI2AXI;
+ cfg.port = ATR_PCIE_REG_PORT;
+ cfg.table = ATR_PCIE_REG_TABLE_NUM;
+ cfg.trsl_id = ATR_PCIE_REG_TRSL_PORT;
+ cfg.trsl_param = 0x0;
+ cfg.transparent = 0x0;
+ ret = mtk_pci_setup_atr(mdev, &cfg);
+ if (ret)
+ return ret;
+
+ /* Config ATR for EP to access RC's memory */
+ for (port = ATR_SRC_AXIS_0; port <= ATR_SRC_AXIS_3; port++) {
+ cfg.src_addr = ATR_PCIE_DEV_DMA_SRC_ADDR;
+ cfg.size = ATR_PCIE_DEV_DMA_SIZE;
+ cfg.trsl_addr = ATR_PCIE_DEV_DMA_TRSL_ADDR;
+ cfg.type = ATR_AXI2PCI;
+ cfg.port = port;
+ cfg.table = ATR_PCIE_DEV_DMA_TABLE_NUM;
+ cfg.trsl_id = ATR_DST_PCI_TRX;
+ cfg.trsl_param = 0x0;
+ /* Enable transparent translation */
+ cfg.transparent = ATR_PCIE_DEV_DMA_TRANSPARENT;
+ ret = mtk_pci_setup_atr(mdev, &cfg);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+const struct mtk_pci_dev_cfg mtk_dev_cfg_0900 = {
+ .flag = MTK_CFG_PM_SW_IRQ,
+ .mhccif_rc_base_addr = 0x1000A000,
+ .istatus_host_ctrl_addr = REG_ISTATUS_HOST_CTRL_NEW,
+ .irq_tbl = {
+ [MTK_IRQ_SRC_DPMAIF] = 24,
+ [MTK_IRQ_SRC_CLDMA0] = 27,
+ [MTK_IRQ_SRC_CLDMA1] = 26,
+ [MTK_IRQ_SRC_CLDMA2] = 25,
+ [MTK_IRQ_SRC_MHCCIF] = 28,
+ [MTK_IRQ_SRC_DPMAIF2] = 29,
+ [MTK_IRQ_SRC_CLDMA3] = 31,
+ [MTK_IRQ_SRC_PM_LOCK] = 0,
+ [MTK_IRQ_SRC_DPMAIF3] = 7,
+ [MTK_IRQ_SRC_DPMAIF6] = 10,
+ },
+ .atr_init = mtk_pci_atr_init_m9xx,
+};
diff --git a/drivers/net/wwan/t9xx/pcie/mtk_pci_reg.h b/drivers/net/wwan/t9xx/pcie/mtk_pci_reg.h
new file mode 100644
index 000000000000..3f0667e8a846
--- /dev/null
+++ b/drivers/net/wwan/t9xx/pcie/mtk_pci_reg.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (c) 2022, MediaTek Inc.
+ */
+
+#ifndef __MTK_PCI_REG_H__
+#define __MTK_PCI_REG_H__
+
+#define REG_ISTATUS_HOST_CTRL_NEW 0x031C
+#define REG_PCIE_MISC_CTRL 0x0348
+#define REG_PCIE_CFG_MSIX 0x03EC
+#define REG_ATR_PCIE_WIN0_T0_SRC_ADDR_LSB 0x0600
+#define REG_ATR_PCIE_WIN0_T0_SRC_ADDR_MSB 0x0604
+#define REG_ATR_PCIE_WIN0_T0_TRSL_ADDR_LSB 0x0608
+#define REG_ATR_PCIE_WIN0_T0_TRSL_ADDR_MSB 0x060C
+#define REG_ATR_PCIE_WIN0_T0_TRSL_PARAM 0x0610
+#define REG_PCIE_DEBUG_DUMMY_3 0x0D0C
+#define REG_PCIE_DEBUG_DUMMY_4 0x0D10
+#define REG_PCIE_DEBUG_DUMMY_7 0x0D1C
+#define REG_MSIX_ISTATUS_HOST_GRP0_0 0x0F00
+#define REG_IMASK_HOST_MSIX_SET_GRP0_0 0x3000
+#define REG_IMASK_HOST_MSIX_CLR_GRP0_0 0x3080
+#define REG_IMASK_HOST_MSIX_GRP0_0 0x3100
+
+/* mhccif registers */
+#define MHCCIF_RC2EP_SW_BSY 0x4
+#define MHCCIF_RC2EP_SW_TCHNUM 0xC
+#define MHCCIF_RC2EP_EVT_RESERVED_FOR_CLDMA0 BIT(4)
+#define MHCCIF_RC2EP_EVT_RESERVED_FOR_CLDMA1 BIT(5)
+#define MHCCIF_RC2EP_EVT_RESERVED_FOR_CLDMA3 BIT(6)
+#define MHCCIF_RC2EP_EVT_RESERVED_FOR_CLDMA2 BIT(7)
+#define MHCCIF_RC2EP_EVT_RESERVED_FOR_DPMAIF BIT(8)
+#define MHCCIF_RC2EP_EVT_PCIE_PM_SUSPEND_REQ BIT(9)
+#define MHCCIF_RC2EP_EVT_PCIE_PM_RESUME_REQ BIT(10)
+#define MHCCIF_RC2EP_EVT_PCIE_PM_SUSPEND_REQ_AP BIT(11)
+#define MHCCIF_RC2EP_EVT_PCIE_PM_RESUME_REQ_AP BIT(12)
+#define MHCCIF_RC2EP_EVT_DEVICE_RESET BIT(13)
+#define MHCCIF_RC2EP_EVT_RESERVED_FOR_TEST BIT(31)
+
+#define MHCCIF_EP2RC_SW_INT_STS 0x10
+#define MHCCIF_EP2RC_SW_INT_ACK 0x14
+#define MHCCIF_EP2RC_SW_INT_EAP_MASK 0x20
+#define MHCCIF_EP2RC_SW_INT_EAP_MASK_SET 0x30
+#define MHCCIF_EP2RC_SW_INT_EAP_MASK_CLR 0x40
+#define MHCCIF_EP2RC_SPARE_REG_1 0x0104
+#define MHCCIF_EP2RC_SPARE_REG_5 0x0114
+#define MHCCIF_EP2RC_SPARE_REG_13 0x0134
+#define MHCCIF_EP2RC_SPARE_REG_14 0x0138
+#define MHCCIF_EP2RC_EVT_BOOT_FLOW_SYNC BIT(5)
+#define MHCCIF_EP2RC_EVT_RESERVED_FOR_CLDMA0 BIT(6)
+#define MHCCIF_EP2RC_EVT_RESERVED_FOR_CLDMA1 BIT(7)
+#define MHCCIF_EP2RC_EVT_RESERVED_FOR_CLDMA3 BIT(8)
+#define MHCCIF_EP2RC_EVT_RESERVED_FOR_CLDMA2 BIT(9)
+#define MHCCIF_EP2RC_EVT_RESERVED_FOR_DPMAIF BIT(10)
+#define MHCCIF_EP2RC_EVT_PCIE_PM_SUSPEND_ACK BIT(11)
+#define MHCCIF_EP2RC_EVT_PCIE_PM_RESUME_ACK BIT(12)
+#define MHCCIF_EP2RC_EVT_PCIE_PM_SUSPEND_ACK_AP BIT(13)
+#define MHCCIF_EP2RC_EVT_PCIE_PM_RESUME_ACK_AP BIT(14)
+#define MHCCIF_EP2RC_EVT_ASYNC_HS_NOTIFY_SAP BIT(15)
+#define MHCCIF_EP2RC_EVT_ASYNC_HS_NOTIFY_MD BIT(16)
+#define MHCCIF_EP2RC_EVT_SOFT_OFF_NOTIFY BIT(17)
+#define MHCCIF_EP2RC_EVT_MD_REBOOT BIT(19)
+#define MHCCIF_EP2RC_EVT_MD_POWEROFF BIT(20)
+#define MHCCIF_EP2RC_EVT_GNSS_ENABLE BIT(21)
+#define MHCCIF_EP2RC_EVT_GNSS_DISABLE BIT(22)
+#define MHCCIF_EP2RC_EVT_FRC_DONE_NOTIFY BIT(24)
+#define MHCCIF_EP2RC_EVT_RESERVED_FOR_TEST1 BIT(30)
+#define MHCCIF_EP2RC_EVT_RESERVED_FOR_TEST2 BIT(31)
+
+#endif /* __MTK_PCI_REG_H__ */
--
2.34.1
^ permalink raw reply related
* [PATCH v3 2/7] net: wwan: t9xx: Add control plane transaction layer
From: Jack Wu via B4 Relay @ 2026-06-24 10:04 UTC (permalink / raw)
To: Loic Poulain, Sergey Ryazanov, Johannes Berg, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Jack Wu, Wen-Zhi Huang, Shi-Wei Yeh, Minano Tseng,
Matthias Brugger, AngeloGioacchino Del Regno, Simon Horman,
Jonathan Corbet, Shuah Khan
Cc: linux-kernel, netdev, linux-arm-kernel, linux-mediatek, linux-doc
In-Reply-To: <20260624-t9xx_driver_v1-v3-0-73ff03f60c48@compal.com>
From: Jack Wu <jackbb_wu@compal.com>
The control plane implements TX services that reside in the
transaction layer. The services receive the packets from the
port layer and call the corresponding DMA components to
transmit data to the device. Meanwhile, TX services receive
and manage the port control commands from the port layer.
The control plane implements RX services that reside in the
transaction layer. The services receive the downlink packets
from the modem and transfer the packets to the corresponding
port layer interfaces.
Signed-off-by: Jack Wu <jackbb_wu@compal.com>
---
drivers/net/wwan/Kconfig | 5 +++
drivers/net/wwan/t9xx/Makefile | 5 +--
drivers/net/wwan/t9xx/mtk_ctrl_plane.c | 48 +++++++++++++++++++++++++++++
drivers/net/wwan/t9xx/mtk_ctrl_plane.h | 22 +++++++++++++
drivers/net/wwan/t9xx/mtk_dev.c | 44 ++++++++++++++++++++++++++
drivers/net/wwan/t9xx/mtk_dev.h | 5 +++
drivers/net/wwan/t9xx/pcie/Makefile | 10 ++++++
drivers/net/wwan/t9xx/pcie/mtk_pci.c | 10 +++---
drivers/net/wwan/t9xx/pcie/mtk_trans_ctrl.h | 21 +++++++++++++
9 files changed, 163 insertions(+), 7 deletions(-)
diff --git a/drivers/net/wwan/Kconfig b/drivers/net/wwan/Kconfig
index 4cee537c739f..7019b44494f8 100644
--- a/drivers/net/wwan/Kconfig
+++ b/drivers/net/wwan/Kconfig
@@ -124,6 +124,7 @@ config MTK_T7XX
config MTK_T9XX
tristate "MediaTek PCIe 5G WWAN modem T9xx device"
depends on PCI
+ select MTK_T9XX_PCI
select NET_DEVLINK
help
Enables MediaTek PCIe based 5G WWAN modem (T9xx series) device.
@@ -133,6 +134,10 @@ config MTK_T9XX
If unsure, say N.
+config MTK_T9XX_PCI
+ tristate
+ depends on PCI
+
endif # WWAN
endmenu
diff --git a/drivers/net/wwan/t9xx/Makefile b/drivers/net/wwan/t9xx/Makefile
index 6f2dd3f91454..ae9d6f2344ab 100644
--- a/drivers/net/wwan/t9xx/Makefile
+++ b/drivers/net/wwan/t9xx/Makefile
@@ -4,7 +4,8 @@ ccflags-y += -I$(src)/pcie
ccflags-y += -I$(src)
obj-$(CONFIG_MTK_T9XX) += mtk_t9xx.o
+obj-$(CONFIG_MTK_T9XX_PCI) += pcie/
mtk_t9xx-y := \
- pcie/mtk_pci.o \
- pcie/mtk_pci_drv_m9xx.o
+ mtk_dev.o \
+ mtk_ctrl_plane.o
diff --git a/drivers/net/wwan/t9xx/mtk_ctrl_plane.c b/drivers/net/wwan/t9xx/mtk_ctrl_plane.c
new file mode 100644
index 000000000000..07938f3e6fe2
--- /dev/null
+++ b/drivers/net/wwan/t9xx/mtk_ctrl_plane.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022, MediaTek Inc.
+ * Copyright (c) 2022-2023, Intel Corporation.
+ */
+
+#include <linux/device.h>
+
+#include "mtk_ctrl_plane.h"
+
+/**
+ * mtk_ctrl_init() - Initialize the control plane block.
+ * @mdev: Pointer to the MTK modem device.
+ *
+ * Allocates and initializes the control plane block
+ * associated with @mdev.
+ *
+ * Return: 0 on success, -ENOMEM on allocation failure.
+ */
+int mtk_ctrl_init(struct mtk_md_dev *mdev)
+{
+ struct mtk_ctrl_blk *ctrl_blk;
+
+ ctrl_blk = devm_kzalloc(mdev->dev, sizeof(*ctrl_blk), GFP_KERNEL);
+ if (!ctrl_blk)
+ return -ENOMEM;
+
+ ctrl_blk->mdev = mdev;
+ mdev->ctrl_blk = ctrl_blk;
+
+ return 0;
+}
+EXPORT_SYMBOL(mtk_ctrl_init);
+
+/**
+ * mtk_ctrl_exit() - Clean up the control plane block.
+ * @mdev: Pointer to the MTK modem device.
+ *
+ * Frees the control plane block associated with @mdev.
+ */
+void mtk_ctrl_exit(struct mtk_md_dev *mdev)
+{
+ struct mtk_ctrl_blk *ctrl_blk = mdev->ctrl_blk;
+
+ devm_kfree(mdev->dev, ctrl_blk);
+ mdev->ctrl_blk = NULL;
+}
+EXPORT_SYMBOL(mtk_ctrl_exit);
diff --git a/drivers/net/wwan/t9xx/mtk_ctrl_plane.h b/drivers/net/wwan/t9xx/mtk_ctrl_plane.h
new file mode 100644
index 000000000000..c141876ef95d
--- /dev/null
+++ b/drivers/net/wwan/t9xx/mtk_ctrl_plane.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (c) 2022, MediaTek Inc.
+ */
+
+#ifndef __MTK_CTRL_PLANE_H__
+#define __MTK_CTRL_PLANE_H__
+
+#include <linux/kref.h>
+#include <linux/skbuff.h>
+
+#include "mtk_dev.h"
+
+struct mtk_ctrl_blk {
+ struct mtk_md_dev *mdev;
+ struct mtk_ctrl_trans *trans;
+};
+
+int mtk_ctrl_init(struct mtk_md_dev *mdev);
+void mtk_ctrl_exit(struct mtk_md_dev *mdev);
+
+#endif /* __MTK_CTRL_PLANE_H__ */
diff --git a/drivers/net/wwan/t9xx/mtk_dev.c b/drivers/net/wwan/t9xx/mtk_dev.c
new file mode 100644
index 000000000000..f254ca7ed877
--- /dev/null
+++ b/drivers/net/wwan/t9xx/mtk_dev.c
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022, MediaTek Inc.
+ */
+
+#include <linux/module.h>
+
+#include "mtk_dev.h"
+
+struct mtk_md_dev *mtk_dev_alloc(struct device *pdev, const struct mtk_dev_ops *dev_ops)
+{
+ struct mtk_md_dev *mdev;
+
+ mdev = devm_kzalloc(pdev, sizeof(*mdev), GFP_KERNEL);
+ if (!mdev)
+ return NULL;
+
+ mdev->dev_ops = dev_ops;
+ mdev->dev = pdev;
+ return mdev;
+}
+EXPORT_SYMBOL(mtk_dev_alloc);
+
+void mtk_dev_free(struct mtk_md_dev *mdev)
+{
+ struct device *dev = mdev->dev;
+
+ devm_kfree(dev, mdev);
+}
+EXPORT_SYMBOL(mtk_dev_free);
+
+static int __init mtk_common_drv_init(void)
+{
+ return 0;
+}
+module_init(mtk_common_drv_init);
+
+static void __exit mtk_common_drv_exit(void)
+{
+}
+module_exit(mtk_common_drv_exit);
+
+MODULE_DESCRIPTION("MediaTek T9xx PCIe WWAN driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wwan/t9xx/mtk_dev.h b/drivers/net/wwan/t9xx/mtk_dev.h
index 8278a0e2875e..bb3ea68890ea 100644
--- a/drivers/net/wwan/t9xx/mtk_dev.h
+++ b/drivers/net/wwan/t9xx/mtk_dev.h
@@ -36,6 +36,7 @@ enum mtk_dev_evt_d2h {
};
struct mtk_md_dev;
+struct mtk_ctrl_blk;
struct mtk_dev_ops {
u32 (*get_dev_state)(struct mtk_md_dev *mdev);
@@ -57,6 +58,7 @@ struct mtk_md_dev {
void *hw_priv;
u32 hw_ver;
char dev_str[MTK_DEV_STR_LEN];
+ struct mtk_ctrl_blk *ctrl_blk;
};
static inline u32 mtk_dev_get_dev_state(struct mtk_md_dev *mdev)
@@ -105,4 +107,7 @@ static inline int mtk_dev_send_dev_evt(struct mtk_md_dev *mdev, u32 dev_evt)
return mdev->dev_ops->send_dev_evt(mdev, dev_evt);
}
+struct mtk_md_dev *mtk_dev_alloc(struct device *pdev, const struct mtk_dev_ops *dev_ops);
+void mtk_dev_free(struct mtk_md_dev *mdev);
+
#endif /* __MTK_DEV_H__ */
diff --git a/drivers/net/wwan/t9xx/pcie/Makefile b/drivers/net/wwan/t9xx/pcie/Makefile
new file mode 100644
index 000000000000..7410d1796d27
--- /dev/null
+++ b/drivers/net/wwan/t9xx/pcie/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+ccflags-y += -I$(src)
+ccflags-y += -I$(src)/..
+
+obj-$(CONFIG_MTK_T9XX_PCI) += mtk_t9xx_pcie.o
+
+mtk_t9xx_pcie-y := \
+ mtk_pci_drv_m9xx.o \
+ mtk_pci.o
diff --git a/drivers/net/wwan/t9xx/pcie/mtk_pci.c b/drivers/net/wwan/t9xx/pcie/mtk_pci.c
index c6a7196fcdd6..90b33dd6effd 100644
--- a/drivers/net/wwan/t9xx/pcie/mtk_pci.c
+++ b/drivers/net/wwan/t9xx/pcie/mtk_pci.c
@@ -14,6 +14,7 @@
#include <linux/module.h>
#include "mtk_dev.h"
+#include "mtk_trans_ctrl.h"
#include "mtk_pci.h"
#include "mtk_pci_reg.h"
@@ -467,6 +468,7 @@ static u32 mtk_pci_ext_h2d_evt_hw_bits(u32 chs)
SET_HW_BITS(hw_bits, chs, MHCCIF_RC2EP_EVT_DEVICE_RESET,
DEV_EVT_H2D_DEVICE_RESET);
+
return LE32_TO_U32(cpu_to_le32(hw_bits));
}
@@ -908,13 +910,11 @@ static int mtk_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
struct mtk_md_dev *mdev;
int ret;
- mdev = devm_kzalloc(dev, sizeof(*mdev), GFP_KERNEL);
+ mdev = mtk_dev_alloc(dev, &pci_hw_ops);
if (!mdev) {
ret = -ENOMEM;
goto log_err;
}
- mdev->dev_ops = &pci_hw_ops;
- mdev->dev = dev;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv) {
@@ -991,7 +991,7 @@ static int mtk_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
free_priv_data:
devm_kfree(dev, priv);
free_cntx_data:
- devm_kfree(dev, mdev);
+ mtk_dev_free(mdev);
log_err:
dev_err(dev, "Failed to probe device, ret=%d\n", ret);
@@ -1017,7 +1017,7 @@ static void mtk_pci_remove(struct pci_dev *pdev)
pci_load_and_free_saved_state(pdev, &priv->saved_state);
devm_kfree(dev, priv);
- devm_kfree(dev, mdev);
+ mtk_dev_free(mdev);
}
static pci_ers_result_t mtk_pci_error_detected(struct pci_dev *pdev,
diff --git a/drivers/net/wwan/t9xx/pcie/mtk_trans_ctrl.h b/drivers/net/wwan/t9xx/pcie/mtk_trans_ctrl.h
new file mode 100644
index 000000000000..d6de4c43b529
--- /dev/null
+++ b/drivers/net/wwan/t9xx/pcie/mtk_trans_ctrl.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (c) 2022, MediaTek Inc.
+ */
+
+#ifndef __MTK_TRANS_CTRL_H__
+#define __MTK_TRANS_CTRL_H__
+
+#include <linux/kref.h>
+#include <linux/list.h>
+#include <linux/skbuff.h>
+#include <linux/types.h>
+
+#include "mtk_dev.h"
+
+struct mtk_ctrl_trans {
+ struct mtk_ctrl_blk *ctrl_blk;
+ struct mtk_md_dev *mdev;
+};
+
+#endif
--
2.34.1
^ permalink raw reply related
* [PATCH v3 5/7] net: wwan: t9xx: Add FSM thread
From: Jack Wu via B4 Relay @ 2026-06-24 10:04 UTC (permalink / raw)
To: Loic Poulain, Sergey Ryazanov, Johannes Berg, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Jack Wu, Wen-Zhi Huang, Shi-Wei Yeh, Minano Tseng,
Matthias Brugger, AngeloGioacchino Del Regno, Simon Horman,
Jonathan Corbet, Shuah Khan
Cc: linux-kernel, netdev, linux-arm-kernel, linux-mediatek, linux-doc
In-Reply-To: <20260624-t9xx_driver_v1-v3-0-73ff03f60c48@compal.com>
From: Jack Wu <jackbb_wu@compal.com>
The FSM (Finite-state Machine) thread is responsible for
synchronizing the actions of different modules. The
asynchronous events from the device or the OS will trigger
a state transition.
The FSM thread will append it to the event queue when an
event arrives. It handles the events sequentially. After
processing the event, the FSM thread notifies other modules
before and after the state transition.
Seven FSM states are defined. They can transition from one
state to another, self-transition in some states, and
transition in some sub-states.
Signed-off-by: Jack Wu <jackbb_wu@compal.com>
---
drivers/net/wwan/t9xx/Makefile | 3 +-
drivers/net/wwan/t9xx/mtk_ctrl_plane.c | 46 ++
drivers/net/wwan/t9xx/mtk_ctrl_plane.h | 2 +
drivers/net/wwan/t9xx/mtk_dev.h | 1 +
drivers/net/wwan/t9xx/mtk_fsm.c | 948 ++++++++++++++++++++++++
drivers/net/wwan/t9xx/mtk_fsm.h | 140 ++++
drivers/net/wwan/t9xx/mtk_port.c | 65 ++
drivers/net/wwan/t9xx/mtk_port.h | 2 +
drivers/net/wwan/t9xx/mtk_utility.h | 33 +
drivers/net/wwan/t9xx/pcie/mtk_cldma.c | 222 +++++-
drivers/net/wwan/t9xx/pcie/mtk_cldma.h | 3 +
drivers/net/wwan/t9xx/pcie/mtk_cldma_drv.h | 3 -
drivers/net/wwan/t9xx/pcie/mtk_cldma_drv_m9xx.c | 7 +-
drivers/net/wwan/t9xx/pcie/mtk_cldma_drv_m9xx.h | 2 -
drivers/net/wwan/t9xx/pcie/mtk_pci.c | 16 +-
drivers/net/wwan/t9xx/pcie/mtk_trans_ctrl.c | 10 +
drivers/net/wwan/t9xx/pcie/mtk_trans_ctrl.h | 1 -
17 files changed, 1488 insertions(+), 16 deletions(-)
diff --git a/drivers/net/wwan/t9xx/Makefile b/drivers/net/wwan/t9xx/Makefile
index db3b1aa1928b..75760b2039dc 100644
--- a/drivers/net/wwan/t9xx/Makefile
+++ b/drivers/net/wwan/t9xx/Makefile
@@ -10,4 +10,5 @@ mtk_t9xx-y := \
mtk_dev.o \
mtk_ctrl_plane.o \
mtk_port.o \
- mtk_port_io.o
+ mtk_port_io.o \
+ mtk_fsm.o
diff --git a/drivers/net/wwan/t9xx/mtk_ctrl_plane.c b/drivers/net/wwan/t9xx/mtk_ctrl_plane.c
index b9a0443ce8ec..dc6a0670fe2b 100644
--- a/drivers/net/wwan/t9xx/mtk_ctrl_plane.c
+++ b/drivers/net/wwan/t9xx/mtk_ctrl_plane.c
@@ -5,10 +5,46 @@
*/
#include <linux/device.h>
+#include <linux/freezer.h>
+#include <linux/kthread.h>
+#include <linux/list.h>
+#include <linux/pm_runtime.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
#include "mtk_ctrl_plane.h"
#include "mtk_port.h"
+#define TAG "CTRL"
+
+static void mtk_ctrl_trans_fsm_state_handler(struct mtk_fsm_param *param,
+ struct mtk_ctrl_blk *ctrl_blk)
+{
+ struct mtk_md_dev *mdev = ctrl_blk->mdev;
+
+ switch (param->to) {
+ case FSM_STATE_OFF:
+ ctrl_blk->ops->fsm_indication(mdev, param);
+ ctrl_blk->ops->exit(mdev);
+ break;
+ case FSM_STATE_ON:
+ ctrl_blk->ops->init(mdev);
+ fallthrough;
+ default:
+ ctrl_blk->ops->fsm_indication(mdev, param);
+ break;
+ }
+}
+
+static void mtk_ctrl_fsm_state_listener(struct mtk_fsm_param *param, void *data)
+{
+ struct mtk_ctrl_blk *ctrl_blk = data;
+
+ mtk_port_mngr_fsm_state_handler(param, ctrl_blk->port_mngr);
+ mtk_ctrl_trans_fsm_state_handler(param, ctrl_blk);
+ mtk_port_mngr_fsm_state_handler_late(param, ctrl_blk->port_mngr);
+}
+
/**
* mtk_ctrl_init() - Initialize the control plane block.
* @mdev: Pointer to the MTK modem device.
@@ -39,8 +75,17 @@ int mtk_ctrl_init(struct mtk_md_dev *mdev, struct mtk_ctrl_hif_ops *ops, struct
if (err)
goto err_free_mem;
+ err = mtk_fsm_notifier_register(mdev, MTK_USER_CTRL, mtk_ctrl_fsm_state_listener,
+ ctrl_blk, FSM_PRIO_1, false);
+ if (err) {
+ dev_err((mdev)->dev, "Fail to register fsm notification(ret = %d)\n", err);
+ goto err_port_exit;
+ }
+
return 0;
+err_port_exit:
+ mtk_port_mngr_exit(ctrl_blk);
err_free_mem:
devm_kfree(mdev->dev, ctrl_blk);
@@ -58,6 +103,7 @@ void mtk_ctrl_exit(struct mtk_md_dev *mdev)
{
struct mtk_ctrl_blk *ctrl_blk = mdev->ctrl_blk;
+ mtk_fsm_notifier_unregister(mdev, MTK_USER_CTRL);
mtk_port_mngr_exit(ctrl_blk);
devm_kfree(mdev->dev, ctrl_blk);
mdev->ctrl_blk = NULL;
diff --git a/drivers/net/wwan/t9xx/mtk_ctrl_plane.h b/drivers/net/wwan/t9xx/mtk_ctrl_plane.h
index d7fcccde8a1b..92817e92a2e4 100644
--- a/drivers/net/wwan/t9xx/mtk_ctrl_plane.h
+++ b/drivers/net/wwan/t9xx/mtk_ctrl_plane.h
@@ -10,6 +10,7 @@
#include <linux/skbuff.h>
#include "mtk_dev.h"
+#include "mtk_fsm.h"
#define Q_MTU_2K (0x800)
#define Q_MTU_3_5K (0xE00)
@@ -62,6 +63,7 @@ struct mtk_ctrl_hif_ops {
int (*init)(struct mtk_md_dev *mdev);
int (*exit)(struct mtk_md_dev *mdev);
int (*submit_skb)(struct mtk_md_dev *mdev, struct sk_buff *skb, bool force_send);
+ void (*fsm_indication)(struct mtk_md_dev *mdev, struct mtk_fsm_param *param);
int (*send_cmd)(struct mtk_md_dev *mdev, int cmd, void *data);
};
diff --git a/drivers/net/wwan/t9xx/mtk_dev.h b/drivers/net/wwan/t9xx/mtk_dev.h
index bb3ea68890ea..2388ada2c6a6 100644
--- a/drivers/net/wwan/t9xx/mtk_dev.h
+++ b/drivers/net/wwan/t9xx/mtk_dev.h
@@ -59,6 +59,7 @@ struct mtk_md_dev {
u32 hw_ver;
char dev_str[MTK_DEV_STR_LEN];
struct mtk_ctrl_blk *ctrl_blk;
+ struct mtk_md_fsm *fsm;
};
static inline u32 mtk_dev_get_dev_state(struct mtk_md_dev *mdev)
diff --git a/drivers/net/wwan/t9xx/mtk_fsm.c b/drivers/net/wwan/t9xx/mtk_fsm.c
new file mode 100644
index 000000000000..a9943c63986c
--- /dev/null
+++ b/drivers/net/wwan/t9xx/mtk_fsm.c
@@ -0,0 +1,948 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022, MediaTek Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/kref.h>
+#include <linux/kthread.h>
+#include <linux/list.h>
+#include <linux/pci.h>
+#include <linux/sched/signal.h>
+#include <linux/skbuff.h>
+#include <linux/wait.h>
+
+#include "mtk_fsm.h"
+#include "mtk_port.h"
+#include "mtk_port_io.h"
+#include "mtk_utility.h"
+
+#define EVT_TF_GATECLOSED (1)
+#define MTK_FSM_INFO_LEN (64)
+
+#define FSM_HS_START_MASK (FSM_F_SAP_HS_START | FSM_F_MD_HS_START)
+#define FSM_HS2_DONE_MASK (FSM_F_SAP_HS2_DONE | FSM_F_MD_HS2_DONE)
+
+#define RTFT_DATA_SIZE (3 * 1024)
+#define EVT_HANDLER_TIMEOUT (HZ * 30)
+#define BLOCKING_EVT_TIMEOUT (2 * EVT_HANDLER_TIMEOUT)
+
+#define REGION_BITMASK 0xF
+#define DEVICE_CFG_SHIFT 24
+#define DEVICE_CFG_REGION_MASK 0x3
+
+enum device_stage {
+ DEV_STAGE_IDLE = 4,
+ DEV_STAGE_MAX
+};
+
+enum device_cfg {
+ DEV_CFG_NORMAL = 0,
+ DEV_CFG_MD_ONLY,
+};
+
+enum runtime_feature_support_type {
+ RTFT_TYPE_NOT_EXIST = 0,
+ RTFT_TYPE_NOT_SUPPORT = 1,
+ RTFT_TYPE_MUST_SUPPORT = 2,
+ RTFT_TYPE_OPTIONAL_SUPPORT = 3,
+ RTFT_TYPE_SUPPORT_BACKWARD_COMPAT = 4,
+};
+
+enum query_runtime_feature_id {
+ QUERY_RTFT_ID_MD_PORT_ENUM = 0,
+ QUERY_RTFT_ID_SAP_PORT_ENUM = 1,
+ QUERY_RTFT_ID_MD_PORT_CFG = 2,
+ QUERY_RTFT_ID_MAX
+};
+
+enum ctrl_msg_id {
+ CTRL_MSG_HS1 = 0,
+ CTRL_MSG_HS2 = 1,
+ CTRL_MSG_HS3 = 2,
+};
+
+struct ctrl_msg_header {
+ __le32 id;
+ __le32 ex_msg;
+ __le32 data_len;
+ u8 reserved[];
+} __packed;
+
+struct runtime_feature_entry {
+ u8 feature_id;
+ struct runtime_feature_info support_info;
+ u8 reserved[2];
+ __le32 data_len;
+ u8 data[];
+};
+
+struct feature_query {
+ __le32 head_pattern;
+ struct runtime_feature_info ft_set[FEATURE_CNT];
+ __le32 tail_pattern;
+};
+
+static int mtk_fsm_send_hs1_msg(struct fsm_hs_info *hs_info)
+{
+ struct ctrl_msg_header *ctrl_msg_h;
+ struct feature_query *ft_query;
+ struct sk_buff *skb;
+ int ret, msg_size;
+
+ msg_size = sizeof(*ctrl_msg_h) + sizeof(*ft_query);
+ skb = __dev_alloc_skb(msg_size, GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+
+ skb_put(skb, msg_size);
+ ctrl_msg_h = (struct ctrl_msg_header *)skb->data;
+ ctrl_msg_h->id = cpu_to_le32(CTRL_MSG_HS1);
+ ctrl_msg_h->ex_msg = 0;
+ ctrl_msg_h->data_len = cpu_to_le32(sizeof(*ft_query));
+
+ ft_query = (struct feature_query *)(skb->data + sizeof(*ctrl_msg_h));
+ ft_query->head_pattern = cpu_to_le32(FEATURE_QUERY_PATTERN);
+ memcpy(ft_query->ft_set, hs_info->query_ft_set, sizeof(hs_info->query_ft_set));
+ ft_query->tail_pattern = cpu_to_le32(FEATURE_QUERY_PATTERN);
+
+ /* send handshake1 message to device */
+ ret = mtk_port_internal_write(hs_info->ctrl_port, skb);
+ if (ret <= 0)
+ return ret;
+
+ return 0;
+}
+
+static int mtk_fsm_feature_set_match(enum runtime_feature_support_type *cur_ft_spt,
+ struct runtime_feature_info rtft_info_st,
+ struct runtime_feature_info rtft_info_cfg)
+{
+ int ret = 0;
+
+ switch (FIELD_GET(FEATURE_TYPE, rtft_info_st.feature)) {
+ case RTFT_TYPE_NOT_EXIST:
+ fallthrough;
+ case RTFT_TYPE_NOT_SUPPORT:
+ *cur_ft_spt = RTFT_TYPE_NOT_EXIST;
+ break;
+ case RTFT_TYPE_MUST_SUPPORT:
+ if (FIELD_GET(FEATURE_TYPE, rtft_info_cfg.feature) == RTFT_TYPE_NOT_EXIST ||
+ FIELD_GET(FEATURE_TYPE, rtft_info_cfg.feature) == RTFT_TYPE_NOT_SUPPORT)
+ ret = -EPROTO;
+ else
+ *cur_ft_spt = RTFT_TYPE_MUST_SUPPORT;
+ break;
+ case RTFT_TYPE_OPTIONAL_SUPPORT:
+ if (FIELD_GET(FEATURE_TYPE, rtft_info_cfg.feature) == RTFT_TYPE_NOT_EXIST ||
+ FIELD_GET(FEATURE_TYPE, rtft_info_cfg.feature) == RTFT_TYPE_NOT_SUPPORT) {
+ *cur_ft_spt = RTFT_TYPE_NOT_SUPPORT;
+ } else {
+ if (FIELD_GET(FEATURE_VER, rtft_info_st.feature) ==
+ FIELD_GET(FEATURE_VER, rtft_info_cfg.feature))
+ *cur_ft_spt = RTFT_TYPE_MUST_SUPPORT;
+ else
+ *cur_ft_spt = RTFT_TYPE_NOT_SUPPORT;
+ }
+ break;
+ case RTFT_TYPE_SUPPORT_BACKWARD_COMPAT:
+ if (FIELD_GET(FEATURE_VER, rtft_info_st.feature) >=
+ FIELD_GET(FEATURE_VER, rtft_info_cfg.feature))
+ *cur_ft_spt = RTFT_TYPE_MUST_SUPPORT;
+ else
+ *cur_ft_spt = RTFT_TYPE_NOT_EXIST;
+ break;
+ default:
+ ret = -EPROTO;
+ }
+
+ return ret;
+}
+
+static int (*query_rtft_action[FEATURE_CNT])(struct mtk_md_dev *mdev, void *rt_data) = {
+ [QUERY_RTFT_ID_MD_PORT_ENUM] = mtk_port_status_update,
+ [QUERY_RTFT_ID_SAP_PORT_ENUM] = mtk_port_status_update,
+};
+
+static int mtk_fsm_parse_hs2_msg(struct fsm_hs_info *hs_info)
+{
+ struct mtk_md_fsm *fsm = container_of(hs_info, struct mtk_md_fsm, hs_info[hs_info->id]);
+ char *rt_data = ((struct sk_buff *)hs_info->rt_data)->data;
+ enum runtime_feature_support_type cur_ft_spt;
+ struct runtime_feature_entry *rtft_entry;
+ unsigned int ft_id, offset, data_len;
+ int ret = 0;
+
+ offset = sizeof(struct feature_query);
+ for (ft_id = 0; ft_id < FEATURE_CNT; ft_id++) {
+ if (offset + sizeof(*rtft_entry) > hs_info->rt_data_len)
+ break;
+
+ rtft_entry = (struct runtime_feature_entry *)(rt_data + offset);
+ ret = mtk_fsm_feature_set_match(&cur_ft_spt,
+ rtft_entry->support_info,
+ hs_info->query_ft_set[ft_id]);
+ if (ret < 0)
+ break;
+
+ if (cur_ft_spt == RTFT_TYPE_MUST_SUPPORT)
+ if (query_rtft_action[ft_id])
+ ret = query_rtft_action[ft_id](fsm->mdev, rtft_entry->data);
+ if (ret < 0)
+ break;
+
+ data_len = le32_to_cpu(rtft_entry->data_len);
+ if (data_len > hs_info->rt_data_len - offset - sizeof(*rtft_entry))
+ break;
+
+ offset += sizeof(*rtft_entry) + data_len;
+ }
+
+ if (ft_id != FEATURE_CNT) {
+ dev_err((fsm->mdev)->dev, "Unable to handle mistake hs2 msg, ft_id=%d\n", ft_id);
+ ret = -EPROTO;
+ }
+
+ return ret;
+}
+
+static int mtk_fsm_append_rtft_entries(struct mtk_md_dev *mdev, void *feature_data,
+ unsigned int *len, struct fsm_hs_info *hs_info)
+{
+ char *rt_data = ((struct sk_buff *)hs_info->rt_data)->data;
+ struct runtime_feature_entry *rtft_entry;
+ int ft_id, ret = 0, rtdata_len = 0;
+ struct feature_query *ft_query;
+
+ ft_query = (struct feature_query *)rt_data;
+ if (le32_to_cpu(ft_query->head_pattern) != FEATURE_QUERY_PATTERN ||
+ le32_to_cpu(ft_query->tail_pattern) != FEATURE_QUERY_PATTERN) {
+ ret = -EPROTO;
+ goto hs_err;
+ }
+
+ /* parse runtime feature query and fill runtime feature entry */
+ rtft_entry = feature_data;
+ for (ft_id = 0; ft_id < FEATURE_CNT && rtdata_len < RTFT_DATA_SIZE; ft_id++) {
+ rtft_entry->feature_id = ft_id;
+ rtft_entry->data_len = 0;
+
+ switch (FIELD_GET(FEATURE_TYPE, ft_query->ft_set[ft_id].feature)) {
+ case RTFT_TYPE_NOT_EXIST:
+ fallthrough;
+ case RTFT_TYPE_NOT_SUPPORT:
+ fallthrough;
+ case RTFT_TYPE_MUST_SUPPORT:
+ rtft_entry->support_info = ft_query->ft_set[ft_id];
+ break;
+ case RTFT_TYPE_OPTIONAL_SUPPORT:
+ fallthrough;
+ case RTFT_TYPE_SUPPORT_BACKWARD_COMPAT:
+ rtft_entry->support_info.feature = FEATURE_TYPE_NOT;
+ rtft_entry->support_info.feature |= FEATURE_VER_0;
+ break;
+ }
+
+ rtdata_len += sizeof(*rtft_entry) + le32_to_cpu(rtft_entry->data_len);
+ rtft_entry = (struct runtime_feature_entry *)(feature_data + rtdata_len);
+ }
+ *len = rtdata_len;
+ return 0;
+
+hs_err:
+ *len = 0;
+ return ret;
+}
+
+static int mtk_fsm_send_hs3_msg(struct fsm_hs_info *hs_info)
+{
+ struct mtk_md_fsm *fsm = container_of(hs_info, struct mtk_md_fsm, hs_info[hs_info->id]);
+ unsigned int data_len, msg_size = 0;
+ struct ctrl_msg_header *ctrl_msg_h;
+ struct sk_buff *skb;
+ int ret;
+
+ skb = __dev_alloc_skb(RTFT_DATA_SIZE, GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+
+ msg_size += sizeof(*ctrl_msg_h);
+ ctrl_msg_h = (struct ctrl_msg_header *)skb->data;
+ ctrl_msg_h->id = cpu_to_le32(CTRL_MSG_HS3);
+ ctrl_msg_h->ex_msg = 0;
+ ret = mtk_fsm_append_rtft_entries(fsm->mdev,
+ skb->data + sizeof(*ctrl_msg_h),
+ &data_len, hs_info);
+ if (ret) {
+ dev_kfree_skb(skb);
+ return ret;
+ }
+
+ ctrl_msg_h->data_len = cpu_to_le32(data_len);
+ msg_size += data_len;
+ skb_put(skb, msg_size);
+ ret = mtk_port_internal_write(hs_info->ctrl_port, skb);
+ if (ret <= 0)
+ return ret;
+
+ return 0;
+}
+
+static int mtk_fsm_sap_ctrl_msg_handler(void *__fsm, struct sk_buff *skb)
+{
+ struct ctrl_msg_header *ctrl_msg_h;
+ struct mtk_md_fsm *fsm = __fsm;
+ struct fsm_hs_info *hs_info;
+ int ret;
+
+ if (skb->len < sizeof(*ctrl_msg_h)) {
+ dev_kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ ctrl_msg_h = (struct ctrl_msg_header *)skb->data;
+ skb_pull(skb, sizeof(*ctrl_msg_h));
+
+ hs_info = &fsm->hs_info[HS_ID_SAP];
+ if (le32_to_cpu(ctrl_msg_h->id) != CTRL_MSG_HS2) {
+ dev_kfree_skb(skb);
+ return -EPROTO;
+ }
+
+ hs_info->rt_data = skb;
+ hs_info->rt_data_len = skb->len;
+ ret = mtk_fsm_evt_submit(fsm->mdev, FSM_EVT_STARTUP,
+ hs_info->fsm_flag_hs2, hs_info, sizeof(*hs_info), 0);
+ if (ret == FSM_EVT_RET_FAIL)
+ dev_kfree_skb(skb);
+
+ return 0;
+}
+
+static int mtk_fsm_md_ctrl_msg_handler(void *__fsm, struct sk_buff *skb)
+{
+ struct ctrl_msg_header *ctrl_msg_h;
+ struct mtk_md_fsm *fsm = __fsm;
+ struct fsm_hs_info *hs_info;
+ bool consumed_skb = false;
+ int ret;
+
+ if (skb->len < sizeof(*ctrl_msg_h)) {
+ dev_kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ ctrl_msg_h = (struct ctrl_msg_header *)skb->data;
+ hs_info = &fsm->hs_info[HS_ID_MD];
+ switch (le32_to_cpu(ctrl_msg_h->id)) {
+ case CTRL_MSG_HS2:
+ skb_pull(skb, sizeof(*ctrl_msg_h));
+ hs_info->rt_data = skb;
+ hs_info->rt_data_len = skb->len;
+ ret = mtk_fsm_evt_submit(fsm->mdev, FSM_EVT_STARTUP,
+ hs_info->fsm_flag_hs2, hs_info, sizeof(*hs_info), 0);
+ if (ret != FSM_EVT_RET_FAIL)
+ consumed_skb = true;
+ break;
+ default:
+ dev_err(fsm->mdev->dev, "Invalid ctrl msg id\n");
+ }
+
+ if (!consumed_skb)
+ dev_kfree_skb(skb);
+
+ return 0;
+}
+
+static int (*ctrl_msg_handler[HS_ID_MAX])(void *__fsm, struct sk_buff *skb) = {
+ [HS_ID_MD] = mtk_fsm_md_ctrl_msg_handler,
+ [HS_ID_SAP] = mtk_fsm_sap_ctrl_msg_handler,
+};
+
+static void mtk_fsm_idle_evt_handler(struct mtk_md_dev *mdev,
+ u32 dev_state, struct mtk_md_fsm *fsm)
+{
+ u32 dev_cfg = dev_state >> DEVICE_CFG_SHIFT & DEVICE_CFG_REGION_MASK;
+ int hs_id;
+
+ if (dev_cfg == DEV_CFG_MD_ONLY)
+ fsm->hs_done_flag = FSM_F_MD_HS_START | FSM_F_MD_HS2_DONE;
+ else
+ fsm->hs_done_flag = FSM_HS_START_MASK | FSM_HS2_DONE_MASK;
+
+ mtk_fsm_evt_submit(mdev, FSM_EVT_STARTUP, FSM_F_DFLT, NULL, 0, 0);
+
+ for (hs_id = 0; hs_id < HS_ID_MAX; hs_id++)
+ mtk_dev_unmask_dev_evt(mdev, fsm->hs_info[hs_id].mhccif_ch);
+}
+
+static int mtk_fsm_early_bootup_handler(u32 status, void *__fsm)
+{
+ struct mtk_md_fsm *fsm = __fsm;
+ struct mtk_md_dev *mdev;
+ u32 dev_state, dev_stage;
+
+ mdev = fsm->mdev;
+ mtk_dev_mask_dev_evt(mdev, status);
+ mtk_dev_clear_dev_evt(mdev, status);
+
+ dev_state = mtk_dev_get_dev_state(mdev);
+ dev_stage = dev_state & REGION_BITMASK;
+ if (dev_stage >= DEV_STAGE_MAX) {
+ dev_err(mdev->dev, "Invalid dev state 0x%x\n", dev_state);
+ return -ENXIO;
+ }
+
+ if (dev_state == fsm->last_dev_state)
+ goto exit;
+ fsm->last_dev_state = dev_state;
+
+ if (dev_stage == DEV_STAGE_IDLE)
+ mtk_fsm_idle_evt_handler(mdev, dev_state, fsm);
+
+exit:
+ return 0;
+}
+
+static int mtk_fsm_ctrl_ch_start(struct mtk_md_fsm *fsm, struct fsm_hs_info *hs_info, int flag)
+{
+ if (!hs_info->ctrl_port) {
+ hs_info->ctrl_port = mtk_port_internal_open(fsm->mdev, hs_info->port_name, flag);
+ if (!hs_info->ctrl_port) {
+ dev_err(fsm->mdev->dev, "Failed to open ctrl port(%s)\n",
+ hs_info->port_name);
+ return -ENODEV;
+ }
+
+ mtk_port_internal_recv_register(hs_info->ctrl_port,
+ ctrl_msg_handler[hs_info->id], fsm);
+ }
+
+ return 0;
+}
+
+static void mtk_fsm_ctrl_ch_stop(struct mtk_md_fsm *fsm)
+{
+ struct fsm_hs_info *hs_info;
+ int hs_id;
+
+ for (hs_id = 0; hs_id < HS_ID_MAX; hs_id++) {
+ hs_info = &fsm->hs_info[hs_id];
+ if (hs_info->ctrl_port) {
+ mtk_port_internal_close(hs_info->ctrl_port);
+ hs_info->ctrl_port = NULL;
+ }
+ }
+}
+
+static void mtk_fsm_switch_state(struct mtk_md_fsm *fsm,
+ enum mtk_fsm_state to_state, struct mtk_fsm_evt *event)
+{
+ char fsm_info[MTK_FSM_INFO_LEN];
+ struct mtk_fsm_notifier *nt;
+ struct mtk_fsm_param param;
+
+ param.from = fsm->state;
+ param.to = to_state;
+ param.evt_id = event ? event->id : FSM_EVT_MAX;
+ param.fsm_flag = event ? event->fsm_flag : FSM_F_DFLT;
+
+ list_for_each_entry(nt, &fsm->pre_notifiers, entry)
+ nt->cb(¶m, nt->data);
+
+ fsm->state = to_state;
+ fsm->fsm_flag |= event ? event->fsm_flag : FSM_F_DFLT;
+
+ snprintf(fsm_info, MTK_FSM_INFO_LEN,
+ "state=%d, fsm_flag=0x%x", to_state, fsm->fsm_flag);
+ mtk_uevent_notify(fsm->mdev->dev, MTK_UEVENT_FSM, fsm_info);
+
+ list_for_each_entry(nt, &fsm->post_notifiers, entry)
+ nt->cb(¶m, nt->data);
+}
+
+static int mtk_fsm_startup_act(struct mtk_md_fsm *fsm, struct mtk_fsm_evt *event)
+{
+ enum mtk_fsm_state to_state = FSM_STATE_BOOTUP;
+ struct fsm_hs_info *hs_info = event->data;
+ struct mtk_md_dev *mdev = fsm->mdev;
+ int ret = 0;
+
+ if (fsm->state != FSM_STATE_ON && fsm->state != FSM_STATE_BOOTUP) {
+ ret = -EPROTO;
+ goto free_rt_data;
+ }
+
+ if (fsm->state != FSM_STATE_BOOTUP) {
+ mtk_fsm_switch_state(fsm, to_state, event);
+ return 0;
+ }
+
+ if (event->fsm_flag & FSM_HS_START_MASK) {
+ mtk_fsm_switch_state(fsm, to_state, event);
+
+ ret = mtk_fsm_ctrl_ch_start(fsm, hs_info, O_NONBLOCK);
+ if (!ret)
+ ret = mtk_fsm_send_hs1_msg(hs_info);
+ if (ret)
+ goto hs_err;
+ } else if (event->fsm_flag & FSM_HS2_DONE_MASK) {
+ ret = mtk_fsm_parse_hs2_msg(hs_info);
+ if (!ret) {
+ mtk_fsm_switch_state(fsm, to_state, event);
+ ret = mtk_fsm_send_hs3_msg(hs_info);
+ }
+ dev_kfree_skb(hs_info->rt_data);
+ hs_info->rt_data = NULL;
+ if (ret)
+ goto hs_err;
+ }
+
+ if (((fsm->fsm_flag | event->fsm_flag) & fsm->hs_done_flag) == fsm->hs_done_flag) {
+ to_state = FSM_STATE_READY;
+ mtk_fsm_switch_state(fsm, to_state, NULL);
+ }
+
+ return 0;
+
+free_rt_data:
+ if (hs_info && hs_info->rt_data) {
+ dev_kfree_skb(hs_info->rt_data);
+ hs_info->rt_data = NULL;
+ }
+hs_err:
+ dev_err((mdev)->dev, "Failed to hs with device %d:0x%x, ret=%d",
+ fsm->state, fsm->fsm_flag, ret);
+ return ret;
+}
+
+static void mtk_fsm_evt_release(struct kref *kref)
+{
+ struct mtk_fsm_evt *event = container_of(kref, struct mtk_fsm_evt, kref);
+
+ kfree(event);
+}
+
+static void mtk_fsm_evt_put(struct mtk_fsm_evt *event)
+{
+ kref_put(&event->kref, mtk_fsm_evt_release);
+}
+
+static void mtk_fsm_evt_finish(struct mtk_md_fsm *fsm,
+ struct mtk_fsm_evt *event, int retval)
+{
+ if (event->mode & EVT_MODE_BLOCKING) {
+ event->status = retval;
+ wake_up(&fsm->evt_waitq);
+ }
+ mtk_fsm_evt_put(event);
+}
+
+static void mtk_fsm_evt_cleanup(struct mtk_md_fsm *fsm, struct list_head *evtq)
+{
+ struct mtk_fsm_evt *event, *tmp;
+
+ list_for_each_entry_safe(event, tmp, evtq, entry) {
+ list_del(&event->entry);
+ mtk_fsm_evt_finish(fsm, event, FSM_EVT_RET_FAIL);
+ }
+}
+
+static int mtk_fsm_enter_off_state(struct mtk_md_fsm *fsm, struct mtk_fsm_evt *event)
+{
+ struct mtk_md_dev *mdev = fsm->mdev;
+ int hs_id;
+
+ if (fsm->state == FSM_STATE_OFF || fsm->state == FSM_STATE_INVALID)
+ return -EPROTO;
+
+ mtk_dev_mask_dev_evt(mdev, DEV_EVT_D2H_BOOT_FLOW_SYNC);
+ for (hs_id = 0; hs_id < HS_ID_MAX; hs_id++)
+ mtk_dev_mask_dev_evt(mdev, fsm->hs_info[hs_id].mhccif_ch);
+
+ mtk_fsm_ctrl_ch_stop(fsm);
+ mtk_fsm_switch_state(fsm, FSM_STATE_OFF, event);
+
+ return 0;
+}
+
+static int mtk_fsm_dev_rm_act(struct mtk_md_fsm *fsm, struct mtk_fsm_evt *event)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&fsm->evtq_lock, flags);
+ set_bit(EVT_TF_GATECLOSED, &fsm->t_flag);
+ mtk_fsm_evt_cleanup(fsm, &fsm->evtq);
+ spin_unlock_irqrestore(&fsm->evtq_lock, flags);
+
+ return mtk_fsm_enter_off_state(fsm, event);
+}
+
+static int mtk_fsm_hs1_handler(u32 status, void *__hs_info)
+{
+ struct fsm_hs_info *hs_info = __hs_info;
+ struct mtk_md_dev *mdev;
+ struct mtk_md_fsm *fsm;
+
+ fsm = container_of(hs_info, struct mtk_md_fsm, hs_info[hs_info->id]);
+ mdev = fsm->mdev;
+ mtk_fsm_evt_submit(mdev, FSM_EVT_STARTUP,
+ hs_info->fsm_flag_hs1, hs_info, sizeof(*hs_info), 0);
+ mtk_dev_mask_dev_evt(mdev, hs_info->mhccif_ch);
+ mtk_dev_clear_dev_evt(mdev, hs_info->mhccif_ch);
+
+ return 0;
+}
+
+static void mtk_fsm_hs_info_init_by_hsid(struct mtk_md_fsm *fsm, int hs_id)
+{
+ struct fsm_hs_info *hs_info;
+
+ if (hs_id < 0 || hs_id >= HS_ID_MAX) {
+ dev_warn((fsm->mdev)->dev, "hs_id = %d, invalid.\n", hs_id);
+ return;
+ }
+
+ hs_info = &fsm->hs_info[hs_id];
+ hs_info->id = hs_id;
+ hs_info->ctrl_port = NULL;
+ hs_info->rt_data = NULL;
+ switch (hs_id) {
+ case HS_ID_MD:
+ snprintf(hs_info->port_name, PORT_NAME_LEN, "MDCTRL");
+ hs_info->mhccif_ch = DEV_EVT_D2H_ASYNC_HS_NOTIFY_MD;
+ hs_info->fsm_flag_hs1 = FSM_F_MD_HS_START;
+ hs_info->fsm_flag_hs2 = FSM_F_MD_HS2_DONE;
+ hs_info->query_ft_set[QUERY_RTFT_ID_MD_PORT_ENUM].feature =
+ FIELD_PREP(FEATURE_TYPE, RTFT_TYPE_MUST_SUPPORT);
+ hs_info->query_ft_set[QUERY_RTFT_ID_MD_PORT_ENUM].feature |=
+ FIELD_PREP(FEATURE_VER, 0);
+ hs_info->query_ft_set[QUERY_RTFT_ID_MD_PORT_CFG].feature =
+ FIELD_PREP(FEATURE_TYPE, RTFT_TYPE_NOT_SUPPORT);
+ break;
+ case HS_ID_SAP:
+ snprintf(hs_info->port_name, PORT_NAME_LEN, "SAPCTRL");
+ hs_info->mhccif_ch = DEV_EVT_D2H_ASYNC_HS_NOTIFY_SAP;
+ hs_info->fsm_flag_hs1 = FSM_F_SAP_HS_START;
+ hs_info->fsm_flag_hs2 = FSM_F_SAP_HS2_DONE;
+ hs_info->query_ft_set[QUERY_RTFT_ID_SAP_PORT_ENUM].feature =
+ FIELD_PREP(FEATURE_TYPE, RTFT_TYPE_MUST_SUPPORT);
+ hs_info->query_ft_set[QUERY_RTFT_ID_SAP_PORT_ENUM].feature |=
+ FIELD_PREP(FEATURE_VER, 0);
+ break;
+ }
+}
+
+static void mtk_fsm_hs_info_init(struct mtk_md_fsm *fsm)
+{
+ struct mtk_md_dev *mdev = fsm->mdev;
+ struct fsm_hs_info *hs_info;
+ int hs_id;
+
+ for (hs_id = 0; hs_id < HS_ID_MAX; hs_id++) {
+ mtk_fsm_hs_info_init_by_hsid(fsm, hs_id);
+ hs_info = &fsm->hs_info[hs_id];
+ mtk_dev_register_dev_evt(mdev, hs_info->mhccif_ch,
+ mtk_fsm_hs1_handler, hs_info);
+ }
+}
+
+static void mtk_fsm_hs_info_exit(struct mtk_md_fsm *fsm)
+{
+ struct mtk_md_dev *mdev = fsm->mdev;
+ struct fsm_hs_info *hs_info;
+ int hs_id;
+
+ for (hs_id = 0; hs_id < HS_ID_MAX; hs_id++) {
+ hs_info = &fsm->hs_info[hs_id];
+ mtk_dev_unregister_dev_evt(mdev, hs_info->mhccif_ch);
+ }
+}
+
+static int mtk_fsm_dev_add_act(struct mtk_md_fsm *fsm, struct mtk_fsm_evt *event)
+{
+ if (fsm->state != FSM_STATE_OFF && fsm->state != FSM_STATE_INVALID)
+ return -EPROTO;
+
+ mtk_fsm_switch_state(fsm, FSM_STATE_ON, event);
+ mtk_dev_unmask_dev_evt(fsm->mdev, DEV_EVT_D2H_BOOT_FLOW_SYNC);
+
+ return 0;
+}
+
+static int (*evts_act_tbl[FSM_EVT_MAX])(struct mtk_md_fsm *__fsm, struct mtk_fsm_evt *event) = {
+ [FSM_EVT_STARTUP] = mtk_fsm_startup_act,
+ [FSM_EVT_DEV_RM] = mtk_fsm_dev_rm_act,
+ [FSM_EVT_DEV_ADD] = mtk_fsm_dev_add_act,
+};
+
+int mtk_fsm_start(struct mtk_md_dev *mdev)
+{
+ struct mtk_md_fsm *fsm = mdev->fsm;
+
+ if (!fsm)
+ return -EINVAL;
+
+ if (!fsm->fsm_handler)
+ return -EFAULT;
+
+ wake_up_process(fsm->fsm_handler);
+ return 0;
+}
+EXPORT_SYMBOL(mtk_fsm_start);
+
+static void mkt_fsm_notifier_cleanup(struct mtk_md_dev *mdev, struct list_head *ntq)
+{
+ struct mtk_fsm_notifier *nt, *tmp;
+
+ list_for_each_entry_safe(nt, tmp, ntq, entry) {
+ list_del(&nt->entry);
+ dev_warn((mdev)->dev, "Having to free notifier(%d) by FSM!\n", nt->id);
+ devm_kfree(mdev->dev, nt);
+ }
+}
+
+static void mtk_fsm_notifier_insert(struct mtk_fsm_notifier *notifier, struct list_head *head)
+{
+ struct mtk_fsm_notifier *nt;
+
+ list_for_each_entry(nt, head, entry) {
+ if (notifier->prio > nt->prio) {
+ list_add(¬ifier->entry, nt->entry.prev);
+ return;
+ }
+ }
+ list_add_tail(¬ifier->entry, head);
+}
+
+int mtk_fsm_notifier_register(struct mtk_md_dev *mdev, enum mtk_user_id id,
+ void (*cb)(struct mtk_fsm_param *, void *data),
+ void *data, enum mtk_fsm_prio prio, bool is_pre)
+{
+ struct mtk_md_fsm *fsm = mdev->fsm;
+ struct mtk_fsm_notifier *notifier;
+
+ if (!fsm)
+ return -EINVAL;
+
+ if (id >= MTK_USER_MAX || !cb || prio >= FSM_PRIO_MAX)
+ return -EINVAL;
+
+ notifier = devm_kzalloc(mdev->dev, sizeof(*notifier), GFP_KERNEL);
+ if (!notifier)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(¬ifier->entry);
+ notifier->id = id;
+ notifier->cb = cb;
+ notifier->data = data;
+ notifier->prio = prio;
+
+ if (is_pre)
+ mtk_fsm_notifier_insert(notifier, &fsm->pre_notifiers);
+ else
+ mtk_fsm_notifier_insert(notifier, &fsm->post_notifiers);
+
+ return 0;
+}
+EXPORT_SYMBOL(mtk_fsm_notifier_register);
+
+int mtk_fsm_notifier_unregister(struct mtk_md_dev *mdev, enum mtk_user_id id)
+{
+ struct mtk_md_fsm *fsm = mdev->fsm;
+ struct mtk_fsm_notifier *nt, *tmp;
+
+ if (!fsm)
+ return -EINVAL;
+
+ list_for_each_entry_safe(nt, tmp, &fsm->pre_notifiers, entry) {
+ if (nt->id == id) {
+ list_del(&nt->entry);
+ devm_kfree(mdev->dev, nt);
+ break;
+ }
+ }
+ list_for_each_entry_safe(nt, tmp, &fsm->post_notifiers, entry) {
+ if (nt->id == id) {
+ list_del(&nt->entry);
+ devm_kfree(mdev->dev, nt);
+ break;
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL(mtk_fsm_notifier_unregister);
+
+int mtk_fsm_evt_submit(struct mtk_md_dev *mdev,
+ enum mtk_fsm_evt_id id, enum mtk_fsm_flag flag,
+ void *data, unsigned int len, unsigned char mode)
+{
+ struct mtk_md_fsm *fsm = mdev->fsm;
+ struct mtk_fsm_evt *event;
+ unsigned long flags;
+ int ret = 0;
+
+ if (!fsm || id >= FSM_EVT_MAX) {
+ dev_err((mdev)->dev, "Invalid param!\n");
+ return FSM_EVT_RET_FAIL;
+ }
+
+ if (test_bit(EVT_TF_GATECLOSED, &fsm->t_flag)) {
+ dev_err((mdev)->dev, "Failed to submit evt, fsm has been removed!\n");
+ return FSM_EVT_RET_FAIL;
+ }
+
+ event = kzalloc(sizeof(*event),
+ (in_hardirq() || in_softirq() || irqs_disabled()) ?
+ GFP_ATOMIC : GFP_KERNEL);
+ if (!event)
+ return FSM_EVT_RET_FAIL;
+
+ kref_init(&event->kref);
+ event->mdev = mdev;
+ event->id = id;
+ event->fsm_flag = flag;
+ event->status = FSM_EVT_RET_ONGOING;
+ event->data = data;
+ event->len = len;
+ event->mode = mode;
+
+ spin_lock_irqsave(&fsm->evtq_lock, flags);
+ if (test_bit(EVT_TF_GATECLOSED, &fsm->t_flag)) {
+ spin_unlock_irqrestore(&fsm->evtq_lock, flags);
+ mtk_fsm_evt_put(event);
+ dev_err(mdev->dev, "Failed to add event, fsm dev has been removed!\n");
+ return FSM_EVT_RET_FAIL;
+ }
+
+ kref_get(&event->kref);
+ if (mode & EVT_MODE_TOHEAD)
+ list_add(&event->entry, &fsm->evtq);
+ else
+ list_add_tail(&event->entry, &fsm->evtq);
+ spin_unlock_irqrestore(&fsm->evtq_lock, flags);
+
+ wake_up_process(fsm->fsm_handler);
+ if (mode & EVT_MODE_BLOCKING) {
+ ret = wait_event_timeout(fsm->evt_waitq,
+ (event->status != 0), BLOCKING_EVT_TIMEOUT);
+ if (!ret && event->status != FSM_EVT_RET_DONE) {
+ dev_err((mdev)->dev, "Handling fsm blocking event timeout!\n");
+ ret = -ETIMEDOUT;
+ } else {
+ ret = event->status;
+ }
+ }
+ mtk_fsm_evt_put(event);
+
+ return ret;
+}
+EXPORT_SYMBOL(mtk_fsm_evt_submit);
+
+static int mtk_fsm_evt_handler(void *__fsm)
+{
+ struct mtk_md_fsm *fsm = __fsm;
+ struct mtk_fsm_evt *event;
+ unsigned long flags;
+ int ret;
+
+wake_up:
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ while (!kthread_should_stop() && !list_empty(&fsm->evtq)) {
+ set_current_state(TASK_RUNNING);
+ spin_lock_irqsave(&fsm->evtq_lock, flags);
+ event = list_first_entry(&fsm->evtq, struct mtk_fsm_evt, entry);
+ list_del(&event->entry);
+ spin_unlock_irqrestore(&fsm->evtq_lock, flags);
+
+ if (event->id < FSM_EVT_MAX) {
+ ret = evts_act_tbl[event->id](fsm, event);
+ if (ret) {
+ dev_err((fsm->mdev)->dev,
+ "Failed to handle evt, fsm state = %d, ret = %d\n",
+ fsm->state, ret);
+ mtk_fsm_evt_finish(fsm, event, FSM_EVT_RET_FAIL);
+ } else {
+ mtk_fsm_evt_finish(fsm, event, FSM_EVT_RET_DONE);
+ }
+ } else {
+ mtk_fsm_evt_finish(fsm, event, FSM_EVT_RET_DONE);
+ }
+ }
+
+ if (kthread_should_stop()) {
+ set_current_state(TASK_RUNNING);
+ return 0;
+ }
+
+ schedule();
+ goto wake_up;
+}
+
+int mtk_fsm_init(struct mtk_md_dev *mdev)
+{
+ struct mtk_md_fsm *fsm;
+ int ret;
+
+ fsm = devm_kzalloc(mdev->dev, sizeof(*fsm), GFP_KERNEL);
+ if (!fsm)
+ return -ENOMEM;
+
+ fsm->fsm_handler = kthread_create(mtk_fsm_evt_handler, fsm, "fsm_evt_thread%d_%s",
+ mdev->hw_ver, mdev->dev_str);
+ if (IS_ERR(fsm->fsm_handler)) {
+ ret = PTR_ERR(fsm->fsm_handler);
+ goto exit;
+ }
+
+ fsm->mdev = mdev;
+ fsm->state = FSM_STATE_INVALID;
+ fsm->fsm_flag = FSM_F_DFLT;
+
+ INIT_LIST_HEAD(&fsm->evtq);
+ spin_lock_init(&fsm->evtq_lock);
+ init_waitqueue_head(&fsm->evt_waitq);
+
+ INIT_LIST_HEAD(&fsm->pre_notifiers);
+ INIT_LIST_HEAD(&fsm->post_notifiers);
+
+ mtk_fsm_hs_info_init(fsm);
+ mtk_dev_register_dev_evt(mdev, DEV_EVT_D2H_BOOT_FLOW_SYNC,
+ mtk_fsm_early_bootup_handler, fsm);
+ mdev->fsm = fsm;
+ return 0;
+exit:
+ devm_kfree(mdev->dev, fsm);
+ return ret;
+}
+EXPORT_SYMBOL(mtk_fsm_init);
+
+int mtk_fsm_exit(struct mtk_md_dev *mdev)
+{
+ struct mtk_md_fsm *fsm = mdev->fsm;
+ unsigned long flags;
+
+ if (!fsm)
+ return -EINVAL;
+
+ if (fsm->fsm_handler) {
+ kthread_stop(fsm->fsm_handler);
+ fsm->fsm_handler = NULL;
+ }
+
+ spin_lock_irqsave(&fsm->evtq_lock, flags);
+ if (WARN_ON(!list_empty(&fsm->evtq)))
+ mtk_fsm_evt_cleanup(fsm, &fsm->evtq);
+ spin_unlock_irqrestore(&fsm->evtq_lock, flags);
+
+ mkt_fsm_notifier_cleanup(mdev, &fsm->pre_notifiers);
+ mkt_fsm_notifier_cleanup(mdev, &fsm->post_notifiers);
+
+ mtk_dev_unregister_dev_evt(mdev, DEV_EVT_D2H_BOOT_FLOW_SYNC);
+ mtk_fsm_hs_info_exit(fsm);
+
+ devm_kfree(mdev->dev, fsm);
+ return 0;
+}
+EXPORT_SYMBOL(mtk_fsm_exit);
diff --git a/drivers/net/wwan/t9xx/mtk_fsm.h b/drivers/net/wwan/t9xx/mtk_fsm.h
new file mode 100644
index 000000000000..f2fc66bcef61
--- /dev/null
+++ b/drivers/net/wwan/t9xx/mtk_fsm.h
@@ -0,0 +1,140 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (c) 2022, MediaTek Inc.
+ */
+
+#ifndef __MTK_FSM_H__
+#define __MTK_FSM_H__
+
+#include "mtk_dev.h"
+
+#define FEATURE_CNT (64)
+#define FEATURE_QUERY_PATTERN (0x49434343)
+
+#define FEATURE_TYPE GENMASK(3, 0)
+#define FEATURE_VER GENMASK(7, 4)
+
+#define FEATURE_TYPE_NOT FIELD_PREP(FEATURE_TYPE, RTFT_TYPE_NOT_SUPPORT)
+#define FEATURE_TYPE_MUST FIELD_PREP(FEATURE_TYPE, RTFT_TYPE_MUST_SUPPORT)
+#define FEATURE_TYPE_OPTIONAL FIELD_PREP(FEATURE_TYPE, RTFT_TYPE_OPTIONAL_SUPPORT)
+#define FEATURE_VER_0 FIELD_PREP(FEATURE_VER, 0)
+
+#define EVT_MODE_BLOCKING (0x01)
+#define EVT_MODE_TOHEAD (0x02)
+
+#define FSM_EVT_RET_FAIL (-1)
+#define FSM_EVT_RET_ONGOING (0)
+#define FSM_EVT_RET_DONE (1)
+
+enum mtk_fsm_flag {
+ FSM_F_DFLT = 0,
+ FSM_F_SAP_HS_START = BIT(0),
+ FSM_F_SAP_HS2_DONE = BIT(1),
+ FSM_F_MD_HS_START = BIT(2),
+ FSM_F_MD_HS2_DONE = BIT(3),
+};
+
+enum mtk_fsm_state {
+ FSM_STATE_INVALID = 0,
+ FSM_STATE_OFF,
+ FSM_STATE_ON,
+ FSM_STATE_BOOTUP,
+ FSM_STATE_READY,
+};
+
+enum mtk_fsm_evt_id {
+ FSM_EVT_STARTUP = 0,
+ FSM_EVT_DEV_RM,
+ FSM_EVT_DEV_ADD,
+ FSM_EVT_MAX
+};
+
+enum mtk_fsm_prio {
+ FSM_PRIO_0 = 0,
+ FSM_PRIO_1 = 1,
+ FSM_PRIO_MAX
+};
+
+struct mtk_fsm_param {
+ enum mtk_fsm_state from;
+ enum mtk_fsm_state to;
+ enum mtk_fsm_evt_id evt_id;
+ enum mtk_fsm_flag fsm_flag;
+};
+
+#define PORT_NAME_LEN 20
+
+enum handshake_info_id {
+ HS_ID_MD = 0,
+ HS_ID_SAP,
+ HS_ID_MAX
+};
+
+struct runtime_feature_info {
+ u8 feature;
+};
+
+struct fsm_hs_info {
+ unsigned char id;
+ void *ctrl_port;
+ char port_name[PORT_NAME_LEN];
+ unsigned int mhccif_ch;
+ unsigned int fsm_flag_hs1;
+ unsigned int fsm_flag_hs2;
+ /* the feature that the device should support */
+ struct runtime_feature_info query_ft_set[FEATURE_CNT];
+ /* runtime data from device need to be parsed by host */
+ void *rt_data;
+ unsigned int rt_data_len;
+};
+
+struct mtk_md_fsm {
+ struct mtk_md_dev *mdev;
+ struct task_struct *fsm_handler;
+ struct fsm_hs_info hs_info[HS_ID_MAX];
+ unsigned int hs_done_flag;
+ unsigned long t_flag;
+ u32 last_dev_state;
+ enum mtk_fsm_state state;
+ unsigned int fsm_flag;
+ struct list_head evtq;
+ /* protect evtq */
+ spinlock_t evtq_lock;
+ /* waitq for fsm blocking submit */
+ wait_queue_head_t evt_waitq;
+ struct list_head pre_notifiers;
+ struct list_head post_notifiers;
+};
+
+struct mtk_fsm_evt {
+ struct list_head entry;
+ struct kref kref;
+ struct mtk_md_dev *mdev;
+ enum mtk_fsm_evt_id id;
+ unsigned int fsm_flag;
+ int status;
+ unsigned char mode;
+ unsigned int len;
+ void *data;
+};
+
+struct mtk_fsm_notifier {
+ struct list_head entry;
+ enum mtk_user_id id;
+ void (*cb)(struct mtk_fsm_param *param, void *data);
+ void *data;
+ enum mtk_fsm_prio prio;
+};
+
+int mtk_fsm_init(struct mtk_md_dev *mdev);
+int mtk_fsm_exit(struct mtk_md_dev *mdev);
+int mtk_fsm_start(struct mtk_md_dev *mdev);
+int mtk_fsm_notifier_register(struct mtk_md_dev *mdev, enum mtk_user_id id,
+ void (*cb)(struct mtk_fsm_param *, void *data),
+ void *data, enum mtk_fsm_prio prio, bool is_pre);
+int mtk_fsm_notifier_unregister(struct mtk_md_dev *mdev, enum mtk_user_id id);
+int mtk_fsm_evt_submit(struct mtk_md_dev *mdev,
+ enum mtk_fsm_evt_id id, enum mtk_fsm_flag flag,
+ void *data, unsigned int len, unsigned char mode);
+
+#endif /* __MTK_FSM_H__ */
diff --git a/drivers/net/wwan/t9xx/mtk_port.c b/drivers/net/wwan/t9xx/mtk_port.c
index 034c9ad0f892..fbc2fb7bdc2d 100644
--- a/drivers/net/wwan/t9xx/mtk_port.c
+++ b/drivers/net/wwan/t9xx/mtk_port.c
@@ -819,6 +819,71 @@ int mtk_port_ch_disable(struct mtk_port *port)
return ret;
}
+static void mtk_port_disable(struct mtk_port_mngr *port_mngr)
+{
+ struct mtk_port **ports;
+ int tbl_type;
+ int ret, idx;
+
+ ports = kcalloc(port_mngr->port_cnt, sizeof(struct mtk_port *), GFP_KERNEL);
+ if (!ports)
+ return;
+
+ tbl_type = PORT_TBL_SAP;
+ do {
+ ret = radix_tree_gang_lookup(&port_mngr->port_tbl[tbl_type],
+ (void **)ports, 0, port_mngr->port_cnt);
+ for (idx = 0; idx < ret; idx++)
+ ports_ops[ports[idx]->info.type]->disable(ports[idx]);
+ } while (++tbl_type < PORT_TBL_MAX);
+ kfree(ports);
+}
+
+void mtk_port_mngr_fsm_state_handler(struct mtk_fsm_param *fsm_param, void *arg)
+{
+ struct mtk_port_mngr *port_mngr;
+
+ if (!fsm_param || !arg)
+ return;
+
+ port_mngr = arg;
+
+ switch (fsm_param->to) {
+ case FSM_STATE_OFF:
+ mtk_port_disable(port_mngr);
+ break;
+ default:
+ break;
+ }
+}
+
+void mtk_port_mngr_fsm_state_handler_late(struct mtk_fsm_param *fsm_param, void *arg)
+{
+ struct mtk_port_mngr *port_mngr;
+ struct mtk_port *port;
+
+ if (!fsm_param || !arg)
+ return;
+
+ port_mngr = arg;
+
+ switch (fsm_param->to) {
+ case FSM_STATE_BOOTUP:
+ if (fsm_param->fsm_flag & FSM_F_MD_HS_START) {
+ port = mtk_port_search_by_id(port_mngr, CCCI_CONTROL_RX);
+ if (port)
+ ports_ops[port->info.type]->enable(port);
+ } else if (fsm_param->fsm_flag & FSM_F_SAP_HS_START) {
+ port = mtk_port_search_by_id(port_mngr, CCCI_SAP_CONTROL_RX);
+ if (port)
+ ports_ops[port->info.type]->enable(port);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
int mtk_port_mngr_init(struct mtk_ctrl_blk *ctrl_blk, struct mtk_port_cfg *port_cfg, int port_cnt)
{
struct mtk_port_mngr *port_mngr;
diff --git a/drivers/net/wwan/t9xx/mtk_port.h b/drivers/net/wwan/t9xx/mtk_port.h
index bd4291408bc2..a201c0007878 100644
--- a/drivers/net/wwan/t9xx/mtk_port.h
+++ b/drivers/net/wwan/t9xx/mtk_port.h
@@ -152,6 +152,8 @@ int mtk_port_send_data(struct mtk_port *port, void *data);
int mtk_port_status_update(struct mtk_md_dev *mdev, void *data);
int mtk_port_ch_enable(struct mtk_port *port);
int mtk_port_ch_disable(struct mtk_port *port);
+void mtk_port_mngr_fsm_state_handler(struct mtk_fsm_param *fsm_param, void *arg);
+void mtk_port_mngr_fsm_state_handler_late(struct mtk_fsm_param *fsm_param, void *arg);
int mtk_port_mngr_init(struct mtk_ctrl_blk *ctrl_blk, struct mtk_port_cfg *port_cfg, int port_cnt);
void mtk_port_mngr_exit(struct mtk_ctrl_blk *ctrl_blk);
void mtk_port_trb_init(struct mtk_port *port, struct trb *trb, enum mtk_trb_cmd_type cmd,
diff --git a/drivers/net/wwan/t9xx/mtk_utility.h b/drivers/net/wwan/t9xx/mtk_utility.h
new file mode 100644
index 000000000000..b72db3842d2d
--- /dev/null
+++ b/drivers/net/wwan/t9xx/mtk_utility.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (c) 2022, MediaTek Inc.
+ */
+
+#ifndef __MTK_UTILITY_H__
+#define __MTK_UTILITY_H__
+
+#include <linux/device.h>
+#include "mtk_dev.h"
+
+#define MTK_UEVENT_INFO_LEN 128
+
+/* MTK uevent */
+enum mtk_uevent_id {
+ MTK_UEVENT_UNDEF = 0,
+ MTK_UEVENT_FSM = 1,
+ MTK_UEVENT_MINIDUMP = 2,
+ MTK_UEVENT_LOWPOWER = 3,
+ MTK_UEVENT_MAX
+};
+
+static inline void mtk_uevent_notify(struct device *dev, enum mtk_uevent_id id, const char *info)
+{
+ char buf[MTK_UEVENT_INFO_LEN];
+ char *ext[2] = {NULL, NULL};
+
+ snprintf(buf, MTK_UEVENT_INFO_LEN, "%s:event_id=%d, info=%s",
+ dev->kobj.name, id, info);
+ ext[0] = buf;
+ kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, ext);
+}
+#endif /* __MTK_UTILITY_H__ */
diff --git a/drivers/net/wwan/t9xx/pcie/mtk_cldma.c b/drivers/net/wwan/t9xx/pcie/mtk_cldma.c
index 7a0815aa2fc8..0ad475d31c34 100644
--- a/drivers/net/wwan/t9xx/pcie/mtk_cldma.c
+++ b/drivers/net/wwan/t9xx/pcie/mtk_cldma.c
@@ -34,12 +34,172 @@
#define CLDMA_RETRY_DELAY_MS (100)
#define NO_BUDGET (0)
+static struct cldma_drv_info_desc cldma_drv_info_tbl[] = {
+ {0x01CA, &drv_ops_name(m9xx), &cldma_regs_name(m9xx)},
+ {0, NULL},
+};
+
+static void mtk_cldma_get_drv_info(struct cldma_drv_info *drv_info, u32 hw_ver)
+{
+ struct cldma_drv_info_desc *p_drv_info;
+ u8 i;
+
+ for (i = 0; (p_drv_info = &cldma_drv_info_tbl[i]) && p_drv_info &&
+ p_drv_info->drv_ops && p_drv_info->hw_regs; i++)
+ if (p_drv_info->hw_ver == hw_ver) {
+ drv_info->drv_ops = p_drv_info->drv_ops;
+ drv_info->hw_regs = p_drv_info->hw_regs;
+ }
+}
+
+static int mtk_cldma_isr(int irq_id, void *param)
+{
+ struct cldma_drv_info *drv_info = param;
+ struct mtk_md_dev *mdev;
+ u32 tx_done, rx_done;
+ u32 tx_sta, rx_sta;
+ struct txq *txq;
+ struct rxq *rxq;
+ int i;
+
+ mdev = drv_info->mdev;
+ drv_info->drv_ops->cldma_get_intr_status(drv_info, &tx_sta, &rx_sta);
+ tx_done = (tx_sta >> QUEUE_XFER_DONE) & 0xFF;
+ rx_done = (rx_sta >> QUEUE_XFER_DONE) & 0xFF;
+
+ if (tx_done) {
+ for (i = 0; i < HW_QUEUE_NUM; i++) {
+ txq = drv_info->txq[i];
+ if (!(tx_done & BIT(i)) || !txq)
+ continue;
+ queue_work(drv_info->wq, &txq->tx_done_work);
+ }
+ }
+ if (rx_done) {
+ for (i = 0; i < HW_QUEUE_NUM; i++) {
+ rxq = drv_info->rxq[i];
+ if (!(rx_done & BIT(i)) || !rxq)
+ continue;
+ queue_work(drv_info->wq, &rxq->rx_done_work);
+ }
+ }
+
+ mtk_pci_clear_irq(mdev, drv_info->pci_ext_irq_id);
+ mtk_pci_unmask_irq(mdev, drv_info->pci_ext_irq_id);
+
+ return IRQ_HANDLED;
+}
+
static const int mtk_cldma_hw_id_tbl[NR_CLDMA] = {
[CLDMA0] = CLDMA0_HW_ID,
[CLDMA1] = CLDMA1_HW_ID,
- [CLDMA4] = CLDMA4_HW_ID,
};
+static int mtk_cldma_dev_init(struct cldma_dev *cd, int hif_id)
+{
+ char gpd_pool_name[DMA_POOL_NAME_LEN];
+ char bd_pool_name[DMA_POOL_NAME_LEN];
+ struct cldma_drv_info *drv_info;
+ struct cldma_hw_regs *hw_regs;
+ struct mtk_md_dev *mdev;
+ unsigned int flag;
+ int hw_id, ret;
+
+ if (!cd || hif_id >= NR_CLDMA)
+ return -EINVAL;
+
+ if (cd->cldma_drv_info[hif_id])
+ return 0;
+
+ hw_id = mtk_cldma_hw_id_tbl[hif_id];
+ mdev = cd->trans->mdev;
+ drv_info = devm_kzalloc(mdev->dev, sizeof(*drv_info), GFP_KERNEL);
+ if (!drv_info)
+ return -ENOMEM;
+
+ drv_info->cd = cd;
+ drv_info->mdev = mdev;
+ drv_info->hif_id = hif_id;
+ drv_info->hw_id = hw_id;
+ mtk_cldma_get_drv_info(drv_info, mdev->hw_ver);
+
+ if (!drv_info->drv_ops || !drv_info->hw_regs) {
+ dev_err((mdev)->dev, "Failed to find CLDMA Driver for PCI %x\n", mdev->hw_ver);
+ ret = -EIO;
+ goto err_free_drv_info;
+ }
+
+ hw_regs = drv_info->hw_regs;
+ snprintf(gpd_pool_name, DMA_POOL_NAME_LEN, "cldma%d_gpd_pool_%s",
+ hw_id, mdev->dev_str);
+ snprintf(bd_pool_name, DMA_POOL_NAME_LEN, "cldma%d_bd_pool_%s",
+ hw_id, mdev->dev_str);
+ drv_info->gpd_dma_pool = dma_pool_create(gpd_pool_name, mdev->dev,
+ sizeof(union gpd), 4, 0);
+ if (!drv_info->gpd_dma_pool) {
+ dev_err((mdev)->dev, "Failed to alloc gpd dma pool for cldma%d\n", hw_id);
+ ret = -ENOMEM;
+ goto err_free_drv_info;
+ }
+ drv_info->bd_dma_pool = dma_pool_create(bd_pool_name, mdev->dev,
+ sizeof(union bd), 4, 0);
+ if (!drv_info->bd_dma_pool) {
+ dev_err((mdev)->dev, "Failed to alloc bd dma pool for cldma%d\n", hw_id);
+ ret = -ENOMEM;
+ goto err_destroy_gpd_pool;
+ }
+
+ switch (hif_id) {
+ case CLDMA0:
+ drv_info->pci_ext_irq_id = mtk_pci_get_irq_id(mdev, MTK_IRQ_SRC_CLDMA0);
+ drv_info->base_addr = hw_regs->cldma0_base_addr;
+ break;
+ case CLDMA1:
+ drv_info->pci_ext_irq_id = mtk_pci_get_irq_id(mdev, MTK_IRQ_SRC_CLDMA1);
+ drv_info->base_addr = hw_regs->cldma1_base_addr;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_destroy_dma_pool;
+ }
+
+ flag = WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_HIGHPRI;
+ drv_info->wq = alloc_workqueue("cldma%d_workq_%s", flag, 0, hw_id, mdev->dev_str);
+ if (!drv_info->wq) {
+ dev_err((mdev)->dev, "Failed to alloc work queue for cldma%d\n", hw_id);
+ ret = -ENOMEM;
+ goto err_destroy_dma_pool;
+ }
+
+ drv_info->drv_ops->cldma_drv_init(drv_info);
+
+ /* mask/clear PCI CLDMA L1 interrupt */
+ mtk_pci_mask_irq(mdev, drv_info->pci_ext_irq_id);
+ mtk_pci_clear_irq(mdev, drv_info->pci_ext_irq_id);
+
+ /* register CLDMA interrupt handler */
+ ret = mtk_pci_register_irq(mdev, drv_info->pci_ext_irq_id, mtk_cldma_isr, drv_info);
+ if (ret)
+ goto err_destroy_wq;
+
+ /* unmask PCI CLDMA L1 interrupt */
+ mtk_pci_unmask_irq(mdev, drv_info->pci_ext_irq_id);
+
+ cd->cldma_drv_info[hif_id] = drv_info;
+ return 0;
+
+err_destroy_wq:
+ destroy_workqueue(drv_info->wq);
+err_destroy_dma_pool:
+ dma_pool_destroy(drv_info->bd_dma_pool);
+err_destroy_gpd_pool:
+ dma_pool_destroy(drv_info->gpd_dma_pool);
+err_free_drv_info:
+ devm_kfree(mdev->dev, drv_info);
+
+ return ret;
+}
+
static inline void mtk_cldma_clr_bd_dsc(struct cldma_drv_info *drv_info,
struct bd_dsc *bd_dsc_pool, int nr_bds)
{
@@ -824,6 +984,7 @@ static void mtk_cldma_rxq_free(struct cldma_drv_info *drv_info, u32 rxqno)
if (req->skb) {
if (rxq->nr_bds) {
skb_shinfo(req->skb)->frag_list = NULL;
+ dev_kfree_skb_any(req->skb);
} else {
if (req->data_dma_addr)
dma_unmap_single(mdev->dev, req->data_dma_addr,
@@ -853,6 +1014,44 @@ static void mtk_cldma_rxq_free(struct cldma_drv_info *drv_info, u32 rxqno)
devm_kfree(mdev->dev, rxq);
}
+static int mtk_cldma_dev_exit(struct cldma_dev *cd, int hif_id)
+{
+ struct cldma_drv_info *drv_info;
+ struct mtk_md_dev *mdev;
+ int virq_id;
+ int i;
+
+ if (!cd || hif_id >= NR_CLDMA)
+ return -EINVAL;
+
+ if (!cd->cldma_drv_info[hif_id])
+ return 0;
+
+ /* free cldma descriptor */
+ drv_info = cd->cldma_drv_info[hif_id];
+ mdev = cd->trans->mdev;
+ virq_id = mtk_pci_get_virq_id(mdev, drv_info->pci_ext_irq_id);
+ mtk_pci_mask_irq(mdev, drv_info->pci_ext_irq_id);
+ synchronize_irq(virq_id);
+ for (i = 0; i < HW_QUEUE_NUM; i++) {
+ if (drv_info->txq[i])
+ mtk_cldma_txq_free(drv_info, drv_info->txq[i]->txqno);
+ if (drv_info->rxq[i])
+ mtk_cldma_rxq_free(drv_info, drv_info->rxq[i]->rxqno);
+ }
+
+ flush_workqueue(drv_info->wq);
+ destroy_workqueue(drv_info->wq);
+ dma_pool_destroy(drv_info->bd_dma_pool);
+ dma_pool_destroy(drv_info->gpd_dma_pool);
+ mtk_pci_unregister_irq(mdev, drv_info->pci_ext_irq_id);
+
+ devm_kfree(mdev->dev, drv_info);
+ cd->cldma_drv_info[hif_id] = NULL;
+
+ return 0;
+}
+
static int mtk_cldma_start_xfer(struct cldma_drv_info *drv_info, u32 qno)
{
struct cldma_drv_ops *drv_ops;
@@ -1163,6 +1362,27 @@ int mtk_cldma_trb_process(void *dev, struct sk_buff *skb)
return trb_act_tbl[trb->cmd](cd, skb);
}
+void mtk_cldma_fsm_state_listener(struct mtk_fsm_param *param, struct mtk_ctrl_trans *trans)
+{
+ struct cldma_dev *cd = trans->dev;
+ int i;
+
+ switch (param->to) {
+ case FSM_STATE_BOOTUP:
+ if (param->fsm_flag & FSM_F_SAP_HS_START)
+ mtk_cldma_dev_init(cd, CLDMA0);
+ else if (param->fsm_flag & FSM_F_MD_HS_START)
+ mtk_cldma_dev_init(cd, CLDMA1);
+ break;
+ case FSM_STATE_OFF:
+ for (i = 0; i < NR_CLDMA; i++)
+ mtk_cldma_dev_exit(cd, i);
+ break;
+ default:
+ break;
+ }
+}
+
int mtk_cldma_check_ch_cfg(void *dev, struct queue_info *que)
{
struct cldma_drv_info *drv_info;
diff --git a/drivers/net/wwan/t9xx/pcie/mtk_cldma.h b/drivers/net/wwan/t9xx/pcie/mtk_cldma.h
index 74ce4f2f0b30..4686f7b178e5 100644
--- a/drivers/net/wwan/t9xx/pcie/mtk_cldma.h
+++ b/drivers/net/wwan/t9xx/pcie/mtk_cldma.h
@@ -167,4 +167,7 @@ int mtk_cldma_check_ch_cfg(void *dev, struct queue_info *que);
#define drv_ops_name(NAME) cldma_drv_ops_##NAME
#define cldma_regs_name(NAME) mtk_cldma_regs_##NAME
+extern struct cldma_drv_ops cldma_drv_ops_m9xx;
+extern struct cldma_hw_regs mtk_cldma_regs_m9xx;
+
#endif
diff --git a/drivers/net/wwan/t9xx/pcie/mtk_cldma_drv.h b/drivers/net/wwan/t9xx/pcie/mtk_cldma_drv.h
index 8763c23abf54..6de87b7ffd45 100644
--- a/drivers/net/wwan/t9xx/pcie/mtk_cldma_drv.h
+++ b/drivers/net/wwan/t9xx/pcie/mtk_cldma_drv.h
@@ -11,7 +11,6 @@
#define LINK_ERROR_VAL (0xFFFFFFFF)
#define CLDMA0_HW_ID (0)
#define CLDMA1_HW_ID (1)
-#define CLDMA4_HW_ID (4)
struct cldma_hw_regs {
u8 cldma_rx_skb_pool_max_size;
@@ -36,7 +35,6 @@ struct cldma_hw_regs {
u16 reg_cldma_l2rimsr0;
u16 reg_cldma_l2rimsr1;
u16 reg_cldma_int_mask;
- u16 reg_cldma4_int_mask;
u16 reg_cldma_slp_mem_ctl;
u16 reg_cldma_busy_mask;
u16 reg_cldma_ip_busy_to_pcie_mask;
@@ -58,7 +56,6 @@ struct cldma_hw_regs {
u32 rq_err_int_bitmask;
u32 cldma0_base_addr;
u32 cldma1_base_addr;
- u32 cldma4_base_addr;
u32 rq_active_start_err_int_bitmask;
u32 reg_cldma_ul_start_addrl_0;
u32 reg_cldma_ul_start_addrh_0;
diff --git a/drivers/net/wwan/t9xx/pcie/mtk_cldma_drv_m9xx.c b/drivers/net/wwan/t9xx/pcie/mtk_cldma_drv_m9xx.c
index d9145d146a5c..a59c35fc1577 100644
--- a/drivers/net/wwan/t9xx/pcie/mtk_cldma_drv_m9xx.c
+++ b/drivers/net/wwan/t9xx/pcie/mtk_cldma_drv_m9xx.c
@@ -34,7 +34,6 @@
struct cldma_hw_regs mtk_cldma_regs_m9xx = {
.cldma0_base_addr = CLDMA0_BASE_ADDR,
.cldma1_base_addr = CLDMA1_BASE_ADDR,
- .cldma4_base_addr = CLDMA4_BASE_ADDR,
.cldma_rx_skb_pool_max_size = CLDMA_RX_SKB_POOL_MAX_SIZE,
.cldma_rx_skb_reload_threshold = CLDMA_RX_SKB_RELOAD_THRESHOLD,
.tq_err_int_offset = TQ_ERR_INT_OFFSET,
@@ -93,7 +92,6 @@ struct cldma_hw_regs mtk_cldma_regs_m9xx = {
.reg_cldma_l3risar1 = REG_CLDMA_L3RISAR1,
.reg_cldma_ip_busy = REG_CLDMA_IP_BUSY,
.reg_cldma_int_mask = REG_CLDMA_INT_EAP_USIP_MASK,
- .reg_cldma4_int_mask = REG_CLDMA_INT_WF_MASK,
.reg_cldma_ip_busy_to_pcie_mask = REG_CLDMA_IP_BUSY_TO_PCIE_MASK,
.reg_cldma_ip_busy_to_pcie_mask_set = REG_CLDMA_IP_BUSY_TO_PCIE_MASK_SET,
.reg_cldma_ip_busy_to_pcie_mask_clr = REG_CLDMA_IP_BUSY_TO_PCIE_MASK_CLR,
@@ -135,10 +133,7 @@ static void mtk_cldma_drv_init_m9xx(struct cldma_drv_info *drv_info)
ALLQ << 24);
/* enable interrupt to PCIe */
- if (drv_info->hw_id == CLDMA4_HW_ID)
- mtk_pci_write32(mdev, base + hw_regs->reg_cldma4_int_mask, 0);
- else
- mtk_pci_write32(mdev, base + hw_regs->reg_cldma_int_mask, 0);
+ mtk_pci_write32(mdev, base + hw_regs->reg_cldma_int_mask, 0);
/* disable illegal memory check */
mtk_pci_write32(mdev, base + hw_regs->reg_cldma_ul_dummy_0, 1);
diff --git a/drivers/net/wwan/t9xx/pcie/mtk_cldma_drv_m9xx.h b/drivers/net/wwan/t9xx/pcie/mtk_cldma_drv_m9xx.h
index 2c63c43ff065..f113c4c1068a 100644
--- a/drivers/net/wwan/t9xx/pcie/mtk_cldma_drv_m9xx.h
+++ b/drivers/net/wwan/t9xx/pcie/mtk_cldma_drv_m9xx.h
@@ -8,7 +8,6 @@
#define CLDMA0_BASE_ADDR (0x1021C000)
#define CLDMA1_BASE_ADDR (0x1021E000)
-#define CLDMA4_BASE_ADDR (0x10224000)
#define CLDMA_RX_SKB_POOL_MAX_SIZE (64)
#define CLDMA_RX_SKB_RELOAD_THRESHOLD (16)
@@ -80,7 +79,6 @@
#define REG_CLDMA_L2RIMSR1 (0x0800 + 0x00FC)
#define REG_CLDMA_INT_EAP_USIP_MASK (0x0800 + 0x011C)
-#define REG_CLDMA_INT_WF_MASK (0x0800 + 0x0120)
#define REG_CLDMA_RQ1_GPD_DONE_CNT (0x0800 + 0x0174)
#define REG_CLDMA_TQ1_GPD_DONE_CNT (0x0800 + 0x0184)
diff --git a/drivers/net/wwan/t9xx/pcie/mtk_pci.c b/drivers/net/wwan/t9xx/pcie/mtk_pci.c
index d3f862098a1d..4b93da5833db 100644
--- a/drivers/net/wwan/t9xx/pcie/mtk_pci.c
+++ b/drivers/net/wwan/t9xx/pcie/mtk_pci.c
@@ -897,22 +897,34 @@ static int mtk_pci_dev_init(struct mtk_md_dev *mdev)
{
int ret;
- ret = mtk_trans_ctrl_init(mdev);
+ ret = mtk_fsm_init(mdev);
if (ret) {
- dev_err(mdev->dev, "Failed to initialize control plane: %d\n", ret);
+ dev_err(mdev->dev, "Failed to initialize FSM: %d\n", ret);
return ret;
}
+ ret = mtk_trans_ctrl_init(mdev);
+ if (ret)
+ goto free_fsm;
+
return 0;
+free_fsm:
+ mtk_fsm_exit(mdev);
+ return ret;
}
static void mtk_pci_dev_exit(struct mtk_md_dev *mdev)
{
+ mtk_fsm_evt_submit(mdev, FSM_EVT_DEV_RM, 0, NULL, 0,
+ EVT_MODE_BLOCKING | EVT_MODE_TOHEAD);
mtk_trans_ctrl_exit(mdev);
+ mtk_fsm_exit(mdev);
}
static int mtk_pci_dev_start(struct mtk_md_dev *mdev)
{
+ mtk_fsm_evt_submit(mdev, FSM_EVT_DEV_ADD, 0, NULL, 0, 0);
+ mtk_fsm_start(mdev);
return 0;
}
static const struct mtk_dev_ops pci_hw_ops = {
diff --git a/drivers/net/wwan/t9xx/pcie/mtk_trans_ctrl.c b/drivers/net/wwan/t9xx/pcie/mtk_trans_ctrl.c
index 6eeed6935550..f905c9055b2b 100644
--- a/drivers/net/wwan/t9xx/pcie/mtk_trans_ctrl.c
+++ b/drivers/net/wwan/t9xx/pcie/mtk_trans_ctrl.c
@@ -481,6 +481,15 @@ static int mtk_pcie_hif_submit_skb(struct mtk_md_dev *mdev, struct sk_buff *skb,
return 0;
}
+static void mtk_pcie_hif_fsm_indication(struct mtk_md_dev *mdev, struct mtk_fsm_param *param)
+{
+ struct mtk_ctrl_blk *ctrl_blk = mdev->ctrl_blk;
+ struct mtk_ctrl_trans *trans;
+
+ trans = ctrl_blk->ctrl_hw_priv;
+ mtk_cldma_fsm_state_listener(param, trans);
+}
+
static int mtk_pcie_hif_cmd_func(struct mtk_md_dev *mdev, int cmd, void *data)
{
struct mtk_ctrl_blk *ctrl_blk = mdev->ctrl_blk;
@@ -508,6 +517,7 @@ static struct mtk_ctrl_hif_ops pcie_ctrl_ops = {
.init = mtk_pcie_hif_init,
.exit = mtk_pcie_hif_exit,
.submit_skb = mtk_pcie_hif_submit_skb,
+ .fsm_indication = mtk_pcie_hif_fsm_indication,
.send_cmd = mtk_pcie_hif_cmd_func,
};
diff --git a/drivers/net/wwan/t9xx/pcie/mtk_trans_ctrl.h b/drivers/net/wwan/t9xx/pcie/mtk_trans_ctrl.h
index a3ff56ddf86f..4b9c9db6ad71 100644
--- a/drivers/net/wwan/t9xx/pcie/mtk_trans_ctrl.h
+++ b/drivers/net/wwan/t9xx/pcie/mtk_trans_ctrl.h
@@ -29,7 +29,6 @@
enum mtk_hif_id {
CLDMA0,
CLDMA1,
- CLDMA4,
NR_CLDMA
};
--
2.34.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox