[rdkb][common][bsp][Refactor and sync kernel from openwrt]

[Description]
0ecd22e [MAC80211][hnat][Update conntrack of per flow accounting data lost issue]
4f4c741 [kernel][common][eth][Change heartbeat check cycle for Aquantia firwmare download]
ca26ee5 [Change spi driver]
8c2d0df [kernel][common][eth][Refactor heartbeat check for Aquantia firwmare download]
cce7a6e [kernel][mt7988][eth][Refactor USXGMII reset sequence]
1991df1 [SBC][MT7988][Add reserved memory for BL32]

[Release-log]

Change-Id: I245a0cba885b3735ed4d9e46a500c30e56bc07f6
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988.dtsi b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988.dtsi
index 1f881ea..364deef 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988.dtsi
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988.dtsi
@@ -294,9 +294,9 @@
 			pmsg-size = <0x2000>;
 		};
 
-		/* 256 KiB reserved for ARM Trusted Firmware (BL31) */
+		/* 320 KiB reserved for ARM Trusted Firmware (BL31 + BL32) */
 		secmon_reserved: secmon@43000000 {
-			reg = <0 0x43000000 0 0x40000>;
+			reg = <0 0x43000000 0 0x50000>;
 			no-map;
 		};
 
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h
index 5b860d6..32b7f6b 100755
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -923,6 +923,8 @@
 #define SWSYSRST_XFI_PLL_GRST	BIT(16)
 #define SWSYSRST_XFI_PEXPT1_GRST	BIT(15)
 #define SWSYSRST_XFI_PEXPT0_GRST	BIT(14)
+#define SWSYSRST_XFI1_GRST	BIT(13)
+#define SWSYSRST_XFI0_GRST	BIT(12)
 #define SWSYSRST_SGMII1_GRST	BIT(2)
 #define SWSYSRST_SGMII0_GRST	BIT(1)
 #define TOPRGU_SWSYSRST_EN		0xFC
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_usxgmii.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_usxgmii.c
index 4a936ec..8fb28ca 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_usxgmii.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_usxgmii.c
@@ -466,24 +466,69 @@
 {
 	struct mtk_eth *eth = ss->eth;
 	u32 id = mtk_mac2xgmii_id(eth, mac_id);
+	u32 val = 0;
 
 	if (id >= MTK_MAX_DEVS || !eth->toprgu)
 		return;
 
 	switch (mac_id) {
 	case MTK_GMAC2_ID:
-		regmap_update_bits(eth->toprgu, 0xFC, GENMASK(31, 0), 0x0000A004);
-		regmap_update_bits(eth->toprgu, 0x18, GENMASK(31, 0), 0x88F0A004);
-		regmap_update_bits(eth->toprgu, 0xFC, GENMASK(31, 0), 0x00000000);
-		regmap_update_bits(eth->toprgu, 0x18, GENMASK(31, 0), 0x88F00000);
-		regmap_update_bits(eth->toprgu, 0x18, GENMASK(31, 0), 0x00F00000);
+		/* Enable software reset */
+		regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val);
+		val |= SWSYSRST_XFI_PEXPT1_GRST |
+		       SWSYSRST_XFI1_GRST;
+		regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val);
+
+		/* Assert USXGMII reset */
+		regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val);
+		val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88) |
+		       SWSYSRST_XFI_PEXPT1_GRST |
+		       SWSYSRST_XFI1_GRST;
+		regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val);
+
+		udelay(100);
+
+		/* De-assert USXGMII reset */
+		regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val);
+		val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88);
+		val &= ~(SWSYSRST_XFI_PEXPT1_GRST |
+			 SWSYSRST_XFI1_GRST);
+		regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val);
+
+		/* Disable software reset */
+		regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val);
+		val &= ~(SWSYSRST_XFI_PEXPT1_GRST |
+			 SWSYSRST_XFI1_GRST);
+		regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val);
 		break;
 	case MTK_GMAC3_ID:
-		regmap_update_bits(eth->toprgu, 0xFC, GENMASK(31, 0), 0x00005002);
-		regmap_update_bits(eth->toprgu, 0x18, GENMASK(31, 0), 0x88F05002);
-		regmap_update_bits(eth->toprgu, 0xFC, GENMASK(31, 0), 0x00000000);
-		regmap_update_bits(eth->toprgu, 0x18, GENMASK(31, 0), 0x88F00000);
-		regmap_update_bits(eth->toprgu, 0x18, GENMASK(31, 0), 0x00F00000);
+		/* Enable software reset */
+		regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val);
+		val |= SWSYSRST_XFI_PEXPT0_GRST |
+		       SWSYSRST_XFI0_GRST;
+		regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val);
+
+		/* Assert USXGMII reset */
+		regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val);
+		val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88) |
+		       SWSYSRST_XFI_PEXPT0_GRST |
+		       SWSYSRST_XFI0_GRST;
+		regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val);
+
+		udelay(100);
+
+		/* De-assert USXGMII reset */
+		regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val);
+		val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88);
+		val &= ~(SWSYSRST_XFI_PEXPT0_GRST |
+			 SWSYSRST_XFI0_GRST);
+		regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val);
+
+		/* Disable software reset */
+		regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val);
+		val &= ~(SWSYSRST_XFI_PEXPT0_GRST |
+			 SWSYSRST_XFI0_GRST);
+		regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val);
 		break;
 	}
 
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/9990-mt7622-backport-nf-hw-offload-framework-and-ups.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/9990-mt7622-backport-nf-hw-offload-framework-and-ups.patch
index 13b754e..7d4c5d6 100755
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/9990-mt7622-backport-nf-hw-offload-framework-and-ups.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/9990-mt7622-backport-nf-hw-offload-framework-and-ups.patch
@@ -2415,7 +2415,7 @@
  	};
  };
  
-@@ -67,52 +152,139 @@ struct flow_offload_tuple_rhash {
+@@ -67,52 +152,140 @@ struct flow_offload_tuple_rhash {
  	struct flow_offload_tuple	tuple;
  };
  
@@ -2428,6 +2428,7 @@
 +	NF_FLOW_DNAT,
 +	NF_FLOW_TEARDOWN,
 +	NF_FLOW_HW,
++	NF_FLOW_HW_ACCT_DYING,
 +	NF_FLOW_HW_DYING,
 +	NF_FLOW_HW_DEAD,
 +	NF_FLOW_HW_PENDING,
@@ -2586,7 +2587,7 @@
 +void nf_flow_offload_del(struct nf_flowtable *flowtable,
 +			 struct flow_offload *flow);
 +void nf_flow_offload_stats(struct nf_flowtable *flowtable,
-+			   struct flow_offload *flow);
++			   struct flow_offload *flow, bool force);
 +
 +void nf_flow_table_offload_flush(struct nf_flowtable *flowtable);
 +int nf_flow_table_offload_setup(struct nf_flowtable *flowtable,
@@ -3903,7 +3904,7 @@
  		if (IS_ERR(tuplehash)) {
  			if (PTR_ERR(tuplehash) != -EAGAIN) {
  				err = PTR_ERR(tuplehash);
-@@ -359,23 +430,49 @@ nf_flow_table_iterate(struct nf_flowtable *flow_table,
+@@ -359,23 +430,52 @@ nf_flow_table_iterate(struct nf_flowtable *flow_table,
  
  	return err;
  }
@@ -3951,6 +3952,9 @@
 +
 +	if (test_bit(NF_FLOW_TEARDOWN, &flow->flags)) {
 +		if (test_bit(NF_FLOW_HW, &flow->flags)) {
++			if (!test_and_set_bit(NF_FLOW_HW_ACCT_DYING, &flow->flags))
++				nf_flow_offload_stats(flow_table, flow, true);
++
 +			if (!test_bit(NF_FLOW_HW_DYING, &flow->flags))
 +				nf_flow_offload_del(flow_table, flow);
 +			else if (test_bit(NF_FLOW_HW_DEAD, &flow->flags))
@@ -3959,7 +3963,7 @@
 +			flow_offload_del(flow_table, flow);
 +		}
 +	} else if (test_bit(NF_FLOW_HW, &flow->flags)) {
-+		nf_flow_offload_stats(flow_table, flow);
++		nf_flow_offload_stats(flow_table, flow, false);
 +	}
  }
  
@@ -4918,7 +4922,7 @@
 index 000000000..d94c6fb92
 --- /dev/null
 +++ b/net/netfilter/nf_flow_table_offload.c
-@@ -0,0 +1,1195 @@
+@@ -0,0 +1,1197 @@
 +#include <linux/kernel.h>
 +#include <linux/init.h>
 +#include <linux/module.h>
@@ -5956,14 +5960,16 @@
 +}
 +
 +void nf_flow_offload_stats(struct nf_flowtable *flowtable,
-+			   struct flow_offload *flow)
++			   struct flow_offload *flow, bool force)
 +{
 +	struct flow_offload_work *offload;
 +	__s32 delta;
 +
-+	delta = nf_flow_timeout_delta(flow->timeout);
-+	if ((delta >= (9 * flow_offload_get_timeout(flow)) / 10))
-+		return;
++	if (!force) {
++		delta = nf_flow_timeout_delta(flow->timeout);
++		if ((delta >= (9 * flow_offload_get_timeout(flow)) / 10))
++			return;
++	}
 +
 +	offload = nf_flow_offload_work_alloc(flowtable, flow, FLOW_CLS_STATS);
 +	if (!offload)
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/mt7986.cfg b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/mt7986.cfg
index 7e8a783..4d7fe1d 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/mt7986.cfg
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/mt7986.cfg
@@ -543,3 +543,4 @@
 CONFIG_ZONE_DMA32=y
 # CONFIG_BPF_KPROBE_OVERRIDE is not set
 # CONFIG_HIST_TRIGGERS is not set
+# CONFIG_FUNCTION_ERROR_INJECTION is not set
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/mt7988.cfg b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/mt7988.cfg
index 2675268..a2322b2 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/mt7988.cfg
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/mt7988.cfg
@@ -504,3 +504,6 @@
 CONFIG_ZLIB_DEFLATE=y
 CONFIG_ZLIB_INFLATE=y
 CONFIG_ZONE_DMA32=y
+# CONFIG_BPF_KPROBE_OVERRIDE is not set
+# CONFIG_HIST_TRIGGERS is not set
+# CONFIG_FUNCTION_ERROR_INJECTION is not set
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/751-net-phy-aquantia-add-firmware-download.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/751-net-phy-aquantia-add-firmware-download.patch
index 2fe3132..28248f4 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/751-net-phy-aquantia-add-firmware-download.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/751-net-phy-aquantia-add-firmware-download.patch
@@ -77,7 +77,7 @@
 index 5a16caa..912bbe6 100644
 --- a/drivers/net/phy/aquantia.h
 +++ b/drivers/net/phy/aquantia.h
-@@ -9,8 +9,65 @@
+@@ -9,8 +9,66 @@
  #include <linux/device.h>
  #include <linux/phy.h>
  
@@ -121,9 +121,10 @@
 +	u64 sgmii_stats[AQR107_SGMII_STAT_SZ];
 +#ifdef CONFIG_AQUANTIA_PHY_FW_DOWNLOAD
 +	struct phy_device *phydevs[1];
-+	struct task_struct *heartbeat;
++	struct task_struct *heartbeat_thread;
 +	bool fw_initialized;
 +	int fw_dl_mode;
++	u16 heartbeat;
 +#endif
 +};
 +
@@ -295,7 +296,6 @@
 +struct task_struct *gangload_kthread = NULL;
 +struct phy_device *gangload_phydevs[MAX_GANGLOAD_DEVICES];
 +static int gangload = 0;
-+static DEFINE_SPINLOCK(gangload_lock);
 +
 +static int aqr_firmware_download_single(struct phy_device *phydev);
 +
@@ -1030,46 +1030,43 @@
 +
 +static int aqr_firmware_check_heartbeat(struct phy_device *phydev)
 +{
-+	int prev, cur, ret;
-+
-+	ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_RSVD_STAT2);
-+	if (ret < 0)
-+		return ret;
-+
-+	prev = ret;
-+
-+	msleep(1500);
++	struct aqr107_priv *priv = phydev->priv;
++	int stopped = 0, ret;
 +
 +	ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_RSVD_STAT2);
 +	if (ret < 0)
 +		return ret;
 +
-+	cur = ret;
++	if (priv->heartbeat == ret)
++		stopped = 1;
 +
-+	if (prev == cur)
-+		return 1;
++	priv->heartbeat = ret;
 +
-+	return 0;
++	return stopped;
 +}
 +
 +static int aqr_firmware_heartbeat_thread(void *data)
 +{
 +	struct phy_device *phydev = data;
 +	struct aqr107_priv *priv = phydev->priv;
++	struct device *dev;
 +	int ret = 0;
 +
++	dev = &phydev->mdio.dev;
++
 +	for (;;) {
 +		if (kthread_should_stop())
 +			break;
 +
 +		if (phydev->state != PHY_HALTED &&
 +		    aqr_firmware_check_heartbeat(phydev) == 1) {
++			dev_err(dev, "Detect heartbeat stopped, start to realod firmware...\n");
 +			priv->fw_initialized = false;
 +			aqr_firmware_download_single(phydev);
 +		}
 +
 +		set_current_state(TASK_INTERRUPTIBLE);
-+		schedule_timeout(HZ);
++		schedule_timeout(2 * HZ);
 +	}
 +
 +	return ret;
@@ -1087,12 +1084,16 @@
 +	if (!fw)
 +		return;
 +
-+	spin_lock(&gangload_lock);
-+
 +	num_phydevs = priv->fw_dl_mode == FW_DL_GNAGLOAD ?
 +		      MAX_GANGLOAD_DEVICES : 1;
 +
 +retry:
++	if (gandload_phydev->state == PHY_HALTED) {
++		dev = &phydevs[0]->mdio.dev;
++		dev_info(dev, "Detect PHY power down, stop to reload firmware...\n");
++		goto out;
++	}
++
 +	memset(result, 0, sizeof(result));
 +
 +	ret = AQ_API_WriteBootLoadImage(phydevs, num_phydevs, gandload_phydev,
@@ -1126,24 +1127,22 @@
 +				phy_queue_state_machine(phydevs[i], 0);
 +			}
 +
-+			/* create a thread for monitor heartbeats status */
-+			if (!priv->heartbeat) {
-+				priv->heartbeat = kthread_create(aqr_firmware_heartbeat_thread,
-+								 phydevs[i],
-+								 "aqr_firmware_heartbeat_thread");
-+				if (IS_ERR(priv->heartbeat)) {
++			/* create a thread for monitor heartbeat status */
++			if (!priv->heartbeat_thread) {
++				priv->heartbeat_thread = kthread_create(aqr_firmware_heartbeat_thread,
++									phydevs[i],
++									"aqr_firmware_heartbeat_thread");
++				if (IS_ERR(priv->heartbeat_thread)) {
 +					dev_err(dev,
 +						"%s Failed to create thread for aqr_firmware_heartbeat_thread\n",
 +						__func__);
 +				}
-+				wake_up_process(priv->heartbeat);
++				wake_up_process(priv->heartbeat_thread);
 +			}
 +		}
 +	}
-+
++out:
 +	release_firmware(fw);
-+
-+	spin_unlock(&gangload_lock);
 +}
 +
 +static int aqr_firmware_download_single(struct phy_device *phydev)
@@ -1158,6 +1157,7 @@
 +
 +	priv->fw_dl_mode = FW_DL_SINGLE;
 +	priv->phydevs[0] = phydev;
++	priv->heartbeat = -1;
 +
 +	ret = request_firmware(&fw, AQR_FIRMWARE, dev);
 +	if (ret) {
@@ -1229,6 +1229,7 @@
 +	}
 +
 +	priv->fw_dl_mode = FW_DL_GNAGLOAD;
++	priv->heartbeat = -1;
 +	gangload_phydevs[gangload] = phydev;
 +	gangload++;
 +
@@ -1350,7 +1351,7 @@
 +#ifdef CONFIG_AQUANTIA_PHY_FW_DOWNLOAD
 +	struct aqr107_priv *priv = phydev->priv;
 +
-+	kthread_stop(priv->heartbeat);
++	kthread_stop(priv->heartbeat_thread);
 +#endif
 +}
 +
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/9102-spi-update-driver.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/9102-spi-update-driver.patch
new file mode 100644
index 0000000..4900733
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/9102-spi-update-driver.patch
@@ -0,0 +1,829 @@
+--- a/drivers/spi/spi-mt65xx.c
++++ b/drivers/spi/spi-mt65xx.c
+@@ -12,7 +12,7 @@
+ #include <linux/ioport.h>
+ #include <linux/module.h>
+ #include <linux/of.h>
+-#include <linux/of_gpio.h>
++#include <linux/gpio/consumer.h>
+ #include <linux/platform_device.h>
+ #include <linux/pm_runtime.h>
+ #include <linux/spi/spi.h>
+@@ -43,9 +43,11 @@
+ #define SPI_CFG1_CS_IDLE_OFFSET           0
+ #define SPI_CFG1_PACKET_LOOP_OFFSET       8
+ #define SPI_CFG1_PACKET_LENGTH_OFFSET     16
+-#define SPI_CFG1_GET_TICKDLY_OFFSET       29
++#define SPI_CFG1_GET_TICK_DLY_OFFSET	29
++#define SPI_CFG1_GET_TICK_DLY_OFFSET_V1	30
+ 
+-#define SPI_CFG1_GET_TICKDLY_MASK	  GENMASK(31, 29)
++#define SPI_CFG1_GET_TICK_DLY_MASK	0xe0000000
++#define SPI_CFG1_GET_TICK_DLY_MASK_V1	0xc0000000
+ #define SPI_CFG1_CS_IDLE_MASK             0xff
+ #define SPI_CFG1_PACKET_LOOP_MASK         0xff00
+ #define SPI_CFG1_PACKET_LENGTH_MASK       0x3ff0000
+@@ -78,7 +80,6 @@
+ 
+ #define PIN_MODE_CFG(x)	((x) / 2)
+ 
+-#define SPI_CFG3_IPM_PIN_MODE_OFFSET		0
+ #define SPI_CFG3_IPM_HALF_DUPLEX_DIR		BIT(2)
+ #define SPI_CFG3_IPM_HALF_DUPLEX_EN		BIT(3)
+ #define SPI_CFG3_IPM_XMODE_EN			BIT(4)
+@@ -94,14 +95,14 @@
+ 
+ #define MTK_SPI_PAUSE_INT_STATUS 0x2
+ 
+-#define MTK_SPI_IDLE 0
+-#define MTK_SPI_PAUSED 1
+-
+ #define MTK_SPI_MAX_FIFO_SIZE 32U
+ #define MTK_SPI_PACKET_SIZE 1024
+ #define MTK_SPI_IPM_PACKET_SIZE SZ_64K
+ #define MTK_SPI_IPM_PACKET_LOOP SZ_256
+ 
++#define MTK_SPI_IDLE			0
++#define MTK_SPI_PAUSED			1
++
+ #define MTK_SPI_32BITS_MASK  (0xffffffff)
+ 
+ #define DMA_ADDR_EXT_BITS (36)
+@@ -115,11 +116,8 @@ struct mtk_spi_compatible {
+ 	bool enhance_timing;
+ 	/* some IC support DMA addr extension */
+ 	bool dma_ext;
+-	/* the IPM IP design improve some feature, and support dual/quad mode */
++	bool no_need_unprepare;
+ 	bool ipm_design;
+-	bool support_quad;
+-	/* some IC ahb & apb clk is different and also need to be enabled */
+-	bool need_ahb_clk;
+ };
+ 
+ struct mtk_spi_config {
+@@ -140,7 +138,7 @@ struct mtk_spi {
+ 	u32 tx_sgl_len, rx_sgl_len;
+ 	const struct mtk_spi_compatible *dev_comp;
+ 	struct mtk_spi_config dev_config;
+-
++	u32 spi_clk_hz;
+ 	struct completion spimem_done;
+ 	bool use_spimem;
+ 	struct device *dev;
+@@ -154,21 +152,10 @@ static const struct mtk_spi_compatible m
+ 	.must_tx = true,
+ };
+ 
+-static const struct mtk_spi_compatible ipm_compat_single = {
+-	.must_tx = true,
++static const struct mtk_spi_compatible mtk_ipm_compat = {
+ 	.enhance_timing = true,
+ 	.dma_ext = true,
+ 	.ipm_design = true,
+-	.need_ahb_clk = true,
+-};
+-
+-static const struct mtk_spi_compatible ipm_compat_quad = {
+-	.must_tx = true,
+-	.enhance_timing = true,
+-	.dma_ext = true,
+-	.ipm_design = true,
+-	.support_quad = true,
+-	.need_ahb_clk = true,
+ };
+ 
+ static const struct mtk_spi_compatible mt6765_compat = {
+@@ -194,13 +181,25 @@ static const struct mtk_spi_compatible m
+ 	.enhance_timing = true,
+ };
+ 
++static const struct mtk_spi_compatible mt6893_compat = {
++	.need_pad_sel = true,
++	.must_tx = true,
++	.enhance_timing = true,
++	.dma_ext = true,
++	.no_need_unprepare = true,
++};
++
+ static const struct of_device_id mtk_spi_of_match[] = {
++	{ .compatible = "mediatek,spi-ipm",
++		.data = (void *)&mtk_ipm_compat,
++	},
+ 	{ .compatible = "mediatek,ipm-spi-single",
+-		.data = (void *)&ipm_compat_single,
++		.data = (void *)&mtk_ipm_compat,
+ 	},
+ 	{ .compatible = "mediatek,ipm-spi-quad",
+-		.data = (void *)&ipm_compat_quad,
++		.data = (void *)&mtk_ipm_compat,
+ 	},
++
+ 	{ .compatible = "mediatek,mt2701-spi",
+ 		.data = (void *)&mtk_common_compat,
+ 	},
+@@ -228,6 +227,12 @@ static const struct of_device_id mtk_spi
+ 	{ .compatible = "mediatek,mt8183-spi",
+ 		.data = (void *)&mt8183_compat,
+ 	},
++	{ .compatible = "mediatek,mt8192-spi",
++		.data = (void *)&mt6765_compat,
++	},
++	{ .compatible = "mediatek,mt6893-spi",
++		.data = (void *)&mt6893_compat,
++	},
+ 	{}
+ };
+ MODULE_DEVICE_TABLE(of, mtk_spi_of_match);
+@@ -256,27 +261,30 @@ static int mtk_spi_hw_init(struct spi_ma
+ 	cpha = spi->mode & SPI_CPHA ? 1 : 0;
+ 	cpol = spi->mode & SPI_CPOL ? 1 : 0;
+ 
++		/* tick delay */
+ 	if (mdata->dev_comp->enhance_timing) {
+ 		if (mdata->dev_comp->ipm_design) {
+-			/* CFG3 reg only used for spi-mem,
+-			 * here write to default value
+-			 */
+-			writel(0x0, mdata->base + SPI_CFG3_IPM_REG);
+-
+ 			reg_val = readl(mdata->base + SPI_CMD_REG);
+ 			reg_val &= ~SPI_CMD_IPM_GET_TICKDLY_MASK;
+-			reg_val |= mdata->dev_config.get_tick_dly
+-				   << SPI_CMD_IPM_GET_TICKDLY_OFFSET;
++			reg_val |= ((mdata->dev_config.get_tick_dly & 0x7)
++				    << SPI_CMD_IPM_GET_TICKDLY_OFFSET);
+ 			writel(reg_val, mdata->base + SPI_CMD_REG);
+ 		} else {
+ 			reg_val = readl(mdata->base + SPI_CFG1_REG);
+-			reg_val &= ~SPI_CFG1_GET_TICKDLY_MASK;
+-			reg_val |= mdata->dev_config.get_tick_dly
+-				   << SPI_CFG1_GET_TICKDLY_OFFSET;
++			reg_val &= ~SPI_CFG1_GET_TICK_DLY_MASK;
++			reg_val |= ((mdata->dev_config.get_tick_dly & 0x7)
++				    << SPI_CFG1_GET_TICK_DLY_OFFSET);
+ 			writel(reg_val, mdata->base + SPI_CFG1_REG);
+ 		}
++	} else {
++		reg_val = readl(mdata->base + SPI_CFG1_REG);
++		reg_val &= ~SPI_CFG1_GET_TICK_DLY_MASK_V1;
++		reg_val |= ((mdata->dev_config.get_tick_dly & 0x3)
++			    << SPI_CFG1_GET_TICK_DLY_OFFSET_V1);
++		writel(reg_val, mdata->base + SPI_CFG1_REG);
+ 	}
+ 
++
+ 	reg_val = readl(mdata->base + SPI_CMD_REG);
+ 	if (mdata->dev_comp->ipm_design) {
+ 		/* SPI transfer without idle time until packet length done */
+@@ -375,12 +383,11 @@ static void mtk_spi_set_cs(struct spi_de
+ static void mtk_spi_prepare_transfer(struct spi_master *master,
+ 				     u32 speed_hz)
+ {
+-	u32 spi_clk_hz, div, sck_time, cs_time, reg_val;
++	u32 div, sck_time, cs_time, reg_val;
+ 	struct mtk_spi *mdata = spi_master_get_devdata(master);
+ 
+-	spi_clk_hz = clk_get_rate(mdata->spi_clk);
+-	if (speed_hz < spi_clk_hz / 2)
+-		div = DIV_ROUND_UP(spi_clk_hz, speed_hz);
++	if (speed_hz < mdata->spi_clk_hz / 2)
++		div = DIV_ROUND_UP(mdata->spi_clk_hz, speed_hz);
+ 	else
+ 		div = 1;
+ 
+@@ -388,13 +395,19 @@ static void mtk_spi_prepare_transfer(str
+ 	cs_time = sck_time * 2;
+ 
+ 	if (mdata->dev_comp->enhance_timing) {
+-		reg_val = (((sck_time - 1) & 0xffff)
++		reg_val = readl(mdata->base + SPI_CFG2_REG);
++		reg_val &= ~(0xffff << SPI_CFG2_SCK_HIGH_OFFSET);
++		reg_val |= (((sck_time - 1) & 0xffff)
+ 			   << SPI_CFG2_SCK_HIGH_OFFSET);
++		reg_val &= ~(0xffff << SPI_CFG2_SCK_LOW_OFFSET);
+ 		reg_val |= (((sck_time - 1) & 0xffff)
+ 			   << SPI_CFG2_SCK_LOW_OFFSET);
+ 		writel(reg_val, mdata->base + SPI_CFG2_REG);
+-		reg_val = (((cs_time - 1) & 0xffff)
++		reg_val = readl(mdata->base + SPI_CFG0_REG);
++		reg_val &= ~(0xffff << SPI_ADJUST_CFG0_CS_HOLD_OFFSET);
++		reg_val |= (((cs_time - 1) & 0xffff)
+ 			   << SPI_ADJUST_CFG0_CS_HOLD_OFFSET);
++		reg_val &= ~(0xffff << SPI_ADJUST_CFG0_CS_SETUP_OFFSET);
+ 		reg_val |= (((cs_time - 1) & 0xffff)
+ 			   << SPI_ADJUST_CFG0_CS_SETUP_OFFSET);
+ 		writel(reg_val, mdata->base + SPI_CFG0_REG);
+@@ -453,14 +466,17 @@ static void mtk_spi_enable_transfer(stru
+ 	writel(cmd, mdata->base + SPI_CMD_REG);
+ }
+ 
+-static int mtk_spi_get_mult_delta(u32 xfer_len)
++static int mtk_spi_get_mult_delta(struct mtk_spi *mdata, u32 xfer_len)
+ {
+-	u32 mult_delta;
++	u32 mult_delta = 0;
+ 
+-	if (xfer_len > MTK_SPI_PACKET_SIZE)
+-		mult_delta = xfer_len % MTK_SPI_PACKET_SIZE;
+-	else
+-		mult_delta = 0;
++	if (mdata->dev_comp->ipm_design) {
++		if (xfer_len > MTK_SPI_IPM_PACKET_SIZE)
++			mult_delta = xfer_len % MTK_SPI_IPM_PACKET_SIZE;
++	} else {
++		if (xfer_len > MTK_SPI_PACKET_SIZE)
++			mult_delta = xfer_len % MTK_SPI_PACKET_SIZE;
++	}
+ 
+ 	return mult_delta;
+ }
+@@ -472,22 +488,22 @@ static void mtk_spi_update_mdata_len(str
+ 
+ 	if (mdata->tx_sgl_len && mdata->rx_sgl_len) {
+ 		if (mdata->tx_sgl_len > mdata->rx_sgl_len) {
+-			mult_delta = mtk_spi_get_mult_delta(mdata->rx_sgl_len);
++			mult_delta = mtk_spi_get_mult_delta(mdata, mdata->rx_sgl_len);
+ 			mdata->xfer_len = mdata->rx_sgl_len - mult_delta;
+ 			mdata->rx_sgl_len = mult_delta;
+ 			mdata->tx_sgl_len -= mdata->xfer_len;
+ 		} else {
+-			mult_delta = mtk_spi_get_mult_delta(mdata->tx_sgl_len);
++			mult_delta = mtk_spi_get_mult_delta(mdata, mdata->tx_sgl_len);
+ 			mdata->xfer_len = mdata->tx_sgl_len - mult_delta;
+ 			mdata->tx_sgl_len = mult_delta;
+ 			mdata->rx_sgl_len -= mdata->xfer_len;
+ 		}
+ 	} else if (mdata->tx_sgl_len) {
+-		mult_delta = mtk_spi_get_mult_delta(mdata->tx_sgl_len);
++		mult_delta = mtk_spi_get_mult_delta(mdata, mdata->tx_sgl_len);
+ 		mdata->xfer_len = mdata->tx_sgl_len - mult_delta;
+ 		mdata->tx_sgl_len = mult_delta;
+ 	} else if (mdata->rx_sgl_len) {
+-		mult_delta = mtk_spi_get_mult_delta(mdata->rx_sgl_len);
++		mult_delta = mtk_spi_get_mult_delta(mdata, mdata->rx_sgl_len);
+ 		mdata->xfer_len = mdata->rx_sgl_len - mult_delta;
+ 		mdata->rx_sgl_len = mult_delta;
+ 	}
+@@ -598,6 +614,19 @@ static int mtk_spi_transfer_one(struct s
+ 				struct spi_device *spi,
+ 				struct spi_transfer *xfer)
+ {
++	struct mtk_spi *mdata = spi_master_get_devdata(spi->master);
++	u32 reg_val = 0;
++
++	/* prepare xfer direction and duplex mode */
++	if (mdata->dev_comp->ipm_design) {
++		if (!xfer->tx_buf || !xfer->rx_buf) {
++			reg_val |= SPI_CFG3_IPM_HALF_DUPLEX_EN;
++			if (xfer->rx_buf)
++				reg_val |= SPI_CFG3_IPM_HALF_DUPLEX_DIR;
++		}
++		writel(reg_val, mdata->base + SPI_CFG3_IPM_REG);
++	}
++
+ 	if (master->can_dma(master, spi, xfer))
+ 		return mtk_spi_dma_transfer(master, spi, xfer);
+ 	else
+@@ -618,8 +647,9 @@ static int mtk_spi_setup(struct spi_devi
+ {
+ 	struct mtk_spi *mdata = spi_master_get_devdata(spi->master);
+ 
+-	if (mdata->dev_comp->need_pad_sel && gpio_is_valid(spi->cs_gpio))
+-		gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH));
++	if (mdata->dev_comp->need_pad_sel && spi->cs_gpiod)
++		/* CS de-asserted, gpiolib will handle inversion */
++		gpiod_direction_output(spi->cs_gpiod, 0);
+ 
+ 	return 0;
+ }
+@@ -747,9 +777,6 @@ static int mtk_spi_mem_adjust_op_size(st
+ {
+ 	int opcode_len;
+ 
+-	if(!op->data.nbytes)
+-		return 0;
+-
+ 	if (op->data.dir != SPI_MEM_NO_DATA) {
+ 		opcode_len = 1 + op->addr.nbytes + op->dummy.nbytes;
+ 		if (opcode_len + op->data.nbytes > MTK_SPI_IPM_PACKET_SIZE) {
+@@ -765,8 +792,7 @@ static int mtk_spi_mem_adjust_op_size(st
+ static bool mtk_spi_mem_supports_op(struct spi_mem *mem,
+ 				     const struct spi_mem_op *op)
+ {
+-	if (op->data.buswidth > 4 || op->addr.buswidth > 4 ||
+-	    op->dummy.buswidth > 4 || op->cmd.buswidth > 4)
++	if (!spi_mem_default_supports_op(mem, op))
+ 		return false;
+ 
+ 	if (op->addr.nbytes && op->dummy.nbytes &&
+@@ -814,13 +840,18 @@ static int mtk_spi_transfer_wait(struct
+ 				 const struct spi_mem_op *op)
+ {
+ 	struct mtk_spi *mdata = spi_master_get_devdata(mem->spi->master);
+-	unsigned long long ms = 1;
++	/*
++	 * For each byte we wait for 8 cycles of the SPI clock.
++	 * Since speed is defined in Hz and we want milliseconds,
++	 * so it should be 8 * 1000.
++	 */
++	u64 ms = 8000LL;
+ 
+ 	if (op->data.dir == SPI_MEM_NO_DATA)
+-		ms = 8LL * 1000LL * 32;
++		ms *= 32; /* prevent we may get 0 for short transfers. */
+ 	else
+-		ms = 8LL * 1000LL * op->data.nbytes;
+-	do_div(ms, mem->spi->max_speed_hz);
++		ms *= op->data.nbytes;
++	ms = div_u64(ms, mem->spi->max_speed_hz);
+ 	ms += ms + 1000; /* 1s tolerance */
+ 
+ 	if (ms > UINT_MAX)
+@@ -839,9 +870,8 @@ static int mtk_spi_mem_exec_op(struct sp
+ 				const struct spi_mem_op *op)
+ {
+ 	struct mtk_spi *mdata = spi_master_get_devdata(mem->spi->master);
+-	u32 reg_val, nio = 1, tx_size;
+-	char *tx_tmp_buf;
+-	char *rx_tmp_buf;
++	u32 reg_val, nio, tx_size;
++	char *tx_tmp_buf, *rx_tmp_buf;
+ 	int ret = 0;
+ 
+ 	mdata->use_spimem = true;
+@@ -887,9 +917,11 @@ static int mtk_spi_mem_exec_op(struct sp
+ 		 op->dummy.buswidth == 4 ||
+ 		 op->data.buswidth == 4)
+ 		nio = 4;
++	else
++		nio = 1;
+ 
+ 	reg_val &= ~SPI_CFG3_IPM_CMD_PIN_MODE_MASK;
+-	reg_val |= PIN_MODE_CFG(nio) << SPI_CFG3_IPM_PIN_MODE_OFFSET;
++	reg_val |= PIN_MODE_CFG(nio);
+ 
+ 	reg_val |= SPI_CFG3_IPM_HALF_DUPLEX_EN;
+ 	if (op->data.dir == SPI_MEM_DATA_IN)
+@@ -902,11 +934,13 @@ static int mtk_spi_mem_exec_op(struct sp
+ 	if (op->data.dir == SPI_MEM_DATA_OUT)
+ 		tx_size += op->data.nbytes;
+ 
+-	tx_size = max(tx_size, (u32)32);
++	tx_size = max_t(u32, tx_size, 32);
+ 
+ 	tx_tmp_buf = kzalloc(tx_size, GFP_KERNEL | GFP_DMA);
+-	if (!tx_tmp_buf)
++	if (!tx_tmp_buf) {
++		mdata->use_spimem = false;
+ 		return -ENOMEM;
++	}
+ 
+ 	tx_tmp_buf[0] = op->cmd.opcode;
+ 
+@@ -937,12 +971,15 @@ static int mtk_spi_mem_exec_op(struct sp
+ 
+ 	if (op->data.dir == SPI_MEM_DATA_IN) {
+ 		if(!IS_ALIGNED((size_t)op->data.buf.in, 4)) {
+-			rx_tmp_buf = kzalloc(op->data.nbytes, GFP_KERNEL | GFP_DMA);
+-			if (!rx_tmp_buf)
+-				return -ENOMEM;
+-		}
+-		else
++			rx_tmp_buf = kzalloc(op->data.nbytes,
++					     GFP_KERNEL | GFP_DMA);
++			if (!rx_tmp_buf) {
++				ret = -ENOMEM;
++				goto unmap_tx_dma;
++			}
++		} else {
+ 			rx_tmp_buf = op->data.buf.in;
++		}
+ 
+ 		mdata->rx_dma = dma_map_single(mdata->dev,
+ 						   rx_tmp_buf,
+@@ -950,7 +987,7 @@ static int mtk_spi_mem_exec_op(struct sp
+ 						   DMA_FROM_DEVICE);
+ 		if (dma_mapping_error(mdata->dev, mdata->rx_dma)) {
+ 			ret = -ENOMEM;
+-			goto unmap_tx_dma;
++			goto kfree_rx_tmp_buf;
+ 		}
+ 	}
+ 
+@@ -980,11 +1017,13 @@ unmap_rx_dma:
+ 	if (op->data.dir == SPI_MEM_DATA_IN) {
+ 		dma_unmap_single(mdata->dev, mdata->rx_dma,
+ 				 op->data.nbytes, DMA_FROM_DEVICE);
+-		if(!IS_ALIGNED((size_t)op->data.buf.in, 4)) {
++		if(!IS_ALIGNED((size_t)op->data.buf.in, 4))
+ 			memcpy(op->data.buf.in, rx_tmp_buf, op->data.nbytes);
+-			kfree(rx_tmp_buf);
+-		}
+ 	}
++kfree_rx_tmp_buf:
++	if (op->data.dir == SPI_MEM_DATA_IN &&
++	    !IS_ALIGNED((size_t)op->data.buf.in, 4))
++		kfree(rx_tmp_buf);
+ unmap_tx_dma:
+ 	dma_unmap_single(mdata->dev, mdata->tx_dma,
+ 			 tx_size, DMA_TO_DEVICE);
+@@ -1003,19 +1042,19 @@ static const struct spi_controller_mem_o
+ 
+ static int mtk_spi_probe(struct platform_device *pdev)
+ {
++	struct device *dev = &pdev->dev;
+ 	struct spi_master *master;
+ 	struct mtk_spi *mdata;
+-	const struct of_device_id *of_id;
+ 	int i, irq, ret, addr_bits;
+ 
+-	master = spi_alloc_master(&pdev->dev, sizeof(*mdata));
++	master = devm_spi_alloc_master(dev, sizeof(*mdata));
+ 	if (!master) {
+-		dev_err(&pdev->dev, "failed to alloc spi master\n");
++		dev_err(dev, "failed to alloc spi master\n");
+ 		return -ENOMEM;
+ 	}
+ 
+ 	master->auto_runtime_pm = true;
+-	master->dev.of_node = pdev->dev.of_node;
++	master->dev.of_node = dev->of_node;
+ 	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST;
+ 
+ 	master->set_cs = mtk_spi_set_cs;
+@@ -1023,23 +1062,16 @@ static int mtk_spi_probe(struct platform
+ 	master->transfer_one = mtk_spi_transfer_one;
+ 	master->can_dma = mtk_spi_can_dma;
+ 	master->setup = mtk_spi_setup;
+-
++	master->use_gpio_descriptors = true;
+ 	master->append_caldata = mtk_spi_append_caldata;
+ 
+-	of_id = of_match_node(mtk_spi_of_match, pdev->dev.of_node);
+-	if (!of_id) {
+-		dev_err(&pdev->dev, "failed to probe of_node\n");
+-		ret = -EINVAL;
+-		goto err_put_master;
+-	}
+-
+ 	mdata = spi_master_get_devdata(master);
+ 
+ 	/* Set device configs to default first. Calibrate it later. */
+ 	mdata->dev_config.sample_sel = 0;
+ 	mdata->dev_config.get_tick_dly = 2;
+ 
+-	mdata->dev_comp = of_id->data;
++	mdata->dev_comp = device_get_match_data(dev);
+ 
+ 	if (mdata->dev_comp->enhance_timing)
+ 		master->mode_bits |= SPI_CS_HIGH;
+@@ -1050,27 +1082,23 @@ static int mtk_spi_probe(struct platform
+ 	if (mdata->dev_comp->ipm_design)
+ 		master->mode_bits |= SPI_LOOP;
+ 
+-	if (mdata->dev_comp->support_quad) {
++	if (mdata->dev_comp->ipm_design) {
++		mdata->dev = dev;
+ 		master->mem_ops = &mtk_spi_mem_ops;
+-		master->mode_bits |= SPI_RX_DUAL | SPI_TX_DUAL |
+-				     SPI_RX_QUAD | SPI_TX_QUAD;
+-
+-		mdata->dev = &pdev->dev;
+ 		init_completion(&mdata->spimem_done);
+ 	}
+ 
+ 	if (mdata->dev_comp->need_pad_sel) {
+-		mdata->pad_num = of_property_count_u32_elems(
+-			pdev->dev.of_node,
++		mdata->pad_num = of_property_count_u32_elems(dev->of_node,
+ 			"mediatek,pad-select");
+ 		if (mdata->pad_num < 0) {
+-			dev_err(&pdev->dev,
++			dev_err(dev,
+ 				"No 'mediatek,pad-select' property\n");
+ 			ret = -EINVAL;
+ 			goto err_put_master;
+ 		}
+ 
+-		mdata->pad_sel = devm_kmalloc_array(&pdev->dev, mdata->pad_num,
++		mdata->pad_sel = devm_kmalloc_array(dev, mdata->pad_num,
+ 						    sizeof(u32), GFP_KERNEL);
+ 		if (!mdata->pad_sel) {
+ 			ret = -ENOMEM;
+@@ -1078,11 +1106,11 @@ static int mtk_spi_probe(struct platform
+ 		}
+ 
+ 		for (i = 0; i < mdata->pad_num; i++) {
+-			of_property_read_u32_index(pdev->dev.of_node,
++			of_property_read_u32_index(dev->of_node,
+ 						   "mediatek,pad-select",
+ 						   i, &mdata->pad_sel[i]);
+ 			if (mdata->pad_sel[i] > MT8173_SPI_MAX_PAD_SEL) {
+-				dev_err(&pdev->dev, "wrong pad-sel[%d]: %u\n",
++				dev_err(dev, "wrong pad-sel[%d]: %u\n",
+ 					i, mdata->pad_sel[i]);
+ 				ret = -EINVAL;
+ 				goto err_put_master;
+@@ -1103,122 +1131,118 @@ static int mtk_spi_probe(struct platform
+ 		goto err_put_master;
+ 	}
+ 
+-	if (!pdev->dev.dma_mask)
+-		pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
++	if (!dev->dma_mask)
++		dev->dma_mask = &dev->coherent_dma_mask;
++
++	if (mdata->dev_comp->ipm_design)
++		dma_set_max_seg_size(dev, SZ_16M);
++	else
++		dma_set_max_seg_size(dev, SZ_256K);
+ 
+-	ret = devm_request_irq(&pdev->dev, irq, mtk_spi_interrupt,
+-			       IRQF_TRIGGER_NONE, dev_name(&pdev->dev), master);
++	ret = devm_request_irq(dev, irq, mtk_spi_interrupt,
++			       IRQF_TRIGGER_NONE, dev_name(dev), master);
+ 	if (ret) {
+-		dev_err(&pdev->dev, "failed to register irq (%d)\n", ret);
++		dev_err(dev, "failed to register irq (%d)\n", ret);
+ 		goto err_put_master;
+ 	}
+ 
+ 
+-	mdata->parent_clk = devm_clk_get(&pdev->dev, "parent-clk");
++	mdata->parent_clk = devm_clk_get(dev, "parent-clk");
+ 	if (IS_ERR(mdata->parent_clk)) {
+ 		ret = PTR_ERR(mdata->parent_clk);
+-		dev_err(&pdev->dev, "failed to get parent-clk: %d\n", ret);
++		dev_err(dev, "failed to get parent-clk: %d\n", ret);
+ 		goto err_put_master;
+ 	}
+ 
+-	mdata->sel_clk = devm_clk_get(&pdev->dev, "sel-clk");
++	mdata->sel_clk = devm_clk_get(dev, "sel-clk");
+ 	if (IS_ERR(mdata->sel_clk)) {
+ 		ret = PTR_ERR(mdata->sel_clk);
+-		dev_err(&pdev->dev, "failed to get sel-clk: %d\n", ret);
++		dev_err(dev, "failed to get sel-clk: %d\n", ret);
+ 		goto err_put_master;
+ 	}
+ 
+-	mdata->spi_clk = devm_clk_get(&pdev->dev, "spi-clk");
++	mdata->spi_clk = devm_clk_get(dev, "spi-clk");
+ 	if (IS_ERR(mdata->spi_clk)) {
+ 		ret = PTR_ERR(mdata->spi_clk);
+-		dev_err(&pdev->dev, "failed to get spi-clk: %d\n", ret);
++		dev_err(dev, "failed to get spi-clk: %d\n", ret);
+ 		goto err_put_master;
+ 	}
+ 
+-	if (mdata->dev_comp->need_ahb_clk) {
+-		mdata->spi_hclk = devm_clk_get(&pdev->dev, "spi-hclk");
+-		if (IS_ERR(mdata->spi_hclk)) {
+-			ret = PTR_ERR(mdata->spi_hclk);
+-			dev_err(&pdev->dev, "failed to get spi-hclk: %d\n", ret);
+-			goto err_put_master;
+-		}
+-
+-		ret = clk_prepare_enable(mdata->spi_hclk);
+-		if (ret < 0) {
+-			dev_err(&pdev->dev, "failed to enable spi_hclk (%d)\n", ret);
+-			goto err_put_master;
+-		}
++	mdata->spi_hclk = devm_clk_get_optional(dev, "spi-hclk");
++	if (IS_ERR(mdata->spi_hclk)) {
++		ret = PTR_ERR(mdata->spi_hclk);
++		dev_err(dev, "failed to get spi-hclk: %d\n", ret);
++		goto err_put_master;
+ 	}
+ 
+-	ret = clk_prepare_enable(mdata->spi_clk);
++	ret = clk_set_parent(mdata->sel_clk, mdata->parent_clk);
+ 	if (ret < 0) {
+-		dev_err(&pdev->dev, "failed to enable spi_clk (%d)\n", ret);
++		dev_err(dev, "failed to clk_set_parent (%d)\n", ret);
+ 		goto err_put_master;
+ 	}
+-
+-	ret = clk_set_parent(mdata->sel_clk, mdata->parent_clk);
++	
++	ret = clk_prepare_enable(mdata->spi_hclk);
+ 	if (ret < 0) {
+-		dev_err(&pdev->dev, "failed to clk_set_parent (%d)\n", ret);
+-		clk_disable_unprepare(mdata->spi_clk);
++		dev_err(dev, "failed to enable spi_hclk (%d)\n", ret);
+ 		goto err_put_master;
+ 	}
+ 
+-	clk_disable_unprepare(mdata->spi_clk);
+-
+-	if (mdata->dev_comp->need_ahb_clk)
++	ret = clk_prepare_enable(mdata->spi_clk);
++	if (ret < 0) {
+ 		clk_disable_unprepare(mdata->spi_hclk);
++		dev_err(dev, "failed to enable spi_clk (%d)\n", ret);
++		goto err_put_master;
++	}
+ 
+-	pm_runtime_enable(&pdev->dev);
++	mdata->spi_clk_hz = clk_get_rate(mdata->spi_clk);
+ 
+-	ret = devm_spi_register_master(&pdev->dev, master);
+-	if (ret) {
+-		dev_err(&pdev->dev, "failed to register master (%d)\n", ret);
+-		goto err_disable_runtime_pm;
++	if (mdata->dev_comp->no_need_unprepare) {
++		clk_disable(mdata->spi_clk);
++		clk_disable(mdata->spi_hclk);
++	} else {
++		clk_disable_unprepare(mdata->spi_clk);
++		clk_disable_unprepare(mdata->spi_hclk);
+ 	}
+ 
+ 	if (mdata->dev_comp->need_pad_sel) {
+ 		if (mdata->pad_num != master->num_chipselect) {
+-			dev_err(&pdev->dev,
++			dev_err(dev,
+ 				"pad_num does not match num_chipselect(%d != %d)\n",
+ 				mdata->pad_num, master->num_chipselect);
+ 			ret = -EINVAL;
+-			goto err_disable_runtime_pm;
++			goto err_put_master;
+ 		}
+ 
+-		if (!master->cs_gpios && master->num_chipselect > 1) {
+-			dev_err(&pdev->dev,
++		if (!master->cs_gpiods && master->num_chipselect > 1) {
++			dev_err(dev,
+ 				"cs_gpios not specified and num_chipselect > 1\n");
+ 			ret = -EINVAL;
+-			goto err_disable_runtime_pm;
++			goto err_put_master;
+ 		}
+ 
+-		if (master->cs_gpios) {
+-			for (i = 0; i < master->num_chipselect; i++) {
+-				ret = devm_gpio_request(&pdev->dev,
+-							master->cs_gpios[i],
+-							dev_name(&pdev->dev));
+-				if (ret) {
+-					dev_err(&pdev->dev,
+-						"can't get CS GPIO %i\n", i);
+-					goto err_disable_runtime_pm;
+-				}
+-			}
+-		}
+ 	}
+ 
+ 	if (mdata->dev_comp->dma_ext)
+ 		addr_bits = DMA_ADDR_EXT_BITS;
+ 	else
+ 		addr_bits = DMA_ADDR_DEF_BITS;
+-	ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(addr_bits));
++	ret = dma_set_mask(dev, DMA_BIT_MASK(addr_bits));
+ 	if (ret)
+-		dev_notice(&pdev->dev, "SPI dma_set_mask(%d) failed, ret:%d\n",
++		dev_notice(dev, "SPI dma_set_mask(%d) failed, ret:%d\n",
+ 			   addr_bits, ret);
+ 
++	pm_runtime_enable(dev);
++
++	ret = devm_spi_register_master(dev, master);
++	if (ret) {
++		dev_err(dev, "failed to register master (%d)\n", ret);
++		goto err_disable_runtime_pm;
++	}
++	
+ 	return 0;
+ 
+ err_disable_runtime_pm:
+-	pm_runtime_disable(&pdev->dev);
++	pm_runtime_disable(dev);
+ err_put_master:
+ 	spi_master_put(master);
+ 
+@@ -1229,11 +1253,22 @@ static int mtk_spi_remove(struct platfor
+ {
+ 	struct spi_master *master = platform_get_drvdata(pdev);
+ 	struct mtk_spi *mdata = spi_master_get_devdata(master);
++	int ret;
+ 
+-	pm_runtime_disable(&pdev->dev);
++	ret = pm_runtime_resume_and_get(&pdev->dev);
++	if (ret < 0)
++		return ret;
+ 
+ 	mtk_spi_reset(mdata);
+ 
++	if (mdata->dev_comp->no_need_unprepare) {
++		clk_unprepare(mdata->spi_clk);
++		clk_unprepare(mdata->spi_hclk);
++	}
++
++	pm_runtime_put_noidle(&pdev->dev);
++	pm_runtime_disable(&pdev->dev);
++
+ 	return 0;
+ }
+ 
+@@ -1250,8 +1285,7 @@ static int mtk_spi_suspend(struct device
+ 
+ 	if (!pm_runtime_suspended(dev)) {
+ 		clk_disable_unprepare(mdata->spi_clk);
+-		if (mdata->dev_comp->need_ahb_clk)
+-			clk_disable_unprepare(mdata->spi_hclk);
++		clk_disable_unprepare(mdata->spi_hclk);
+ 	}
+ 
+ 	return ret;
+@@ -1264,26 +1298,24 @@ static int mtk_spi_resume(struct device
+ 	struct mtk_spi *mdata = spi_master_get_devdata(master);
+ 
+ 	if (!pm_runtime_suspended(dev)) {
+-		if (mdata->dev_comp->need_ahb_clk) {
+-			ret = clk_prepare_enable(mdata->spi_hclk);
+-			if (ret < 0) {
+-				dev_err(dev, "failed to enable spi_hclk (%d)\n", ret);
+-				return ret;
+-			}
+-		}
+-
+ 		ret = clk_prepare_enable(mdata->spi_clk);
+ 		if (ret < 0) {
+ 			dev_err(dev, "failed to enable spi_clk (%d)\n", ret);
+ 			return ret;
+ 		}
++
++		ret = clk_prepare_enable(mdata->spi_hclk);
++		if (ret < 0) {
++			dev_err(dev, "failed to enable spi_hclk (%d)\n", ret);
++			clk_disable_unprepare(mdata->spi_clk);
++			return ret;
++		}
+ 	}
+ 
+ 	ret = spi_master_resume(master);
+ 	if (ret < 0) {
+ 		clk_disable_unprepare(mdata->spi_clk);
+-		if (mdata->dev_comp->need_ahb_clk)
+-			clk_disable_unprepare(mdata->spi_hclk);
++		clk_disable_unprepare(mdata->spi_hclk);
+ 	}
+ 
+ 	return ret;
+@@ -1296,10 +1328,13 @@ static int mtk_spi_runtime_suspend(struc
+ 	struct spi_master *master = dev_get_drvdata(dev);
+ 	struct mtk_spi *mdata = spi_master_get_devdata(master);
+ 
+-	clk_disable_unprepare(mdata->spi_clk);
+-
+-	if (mdata->dev_comp->need_ahb_clk)
++	if (mdata->dev_comp->no_need_unprepare) {
++		clk_disable(mdata->spi_clk);
++		clk_disable(mdata->spi_hclk);
++	} else {
++		clk_disable_unprepare(mdata->spi_clk);
+ 		clk_disable_unprepare(mdata->spi_hclk);
++	}
+ 
+ 	return 0;
+ }
+@@ -1310,18 +1345,31 @@ static int mtk_spi_runtime_resume(struct
+ 	struct mtk_spi *mdata = spi_master_get_devdata(master);
+ 	int ret;
+ 
+-	if (mdata->dev_comp->need_ahb_clk) {
+-		ret = clk_prepare_enable(mdata->spi_hclk);
++	if (mdata->dev_comp->no_need_unprepare) {
++		ret = clk_enable(mdata->spi_clk);
++		if (ret < 0) {
++			dev_err(dev, "failed to enable spi_clk (%d)\n", ret);
++			return ret;
++		}
++		ret = clk_enable(mdata->spi_hclk);
+ 		if (ret < 0) {
+ 			dev_err(dev, "failed to enable spi_hclk (%d)\n", ret);
++			clk_disable(mdata->spi_clk);
++			return ret;
++		}
++	} else {
++		ret = clk_prepare_enable(mdata->spi_clk);
++		if (ret < 0) {
++			dev_err(dev, "failed to prepare_enable spi_clk (%d)\n", ret);
+ 			return ret;
+ 		}
+-	}
+ 
+-	ret = clk_prepare_enable(mdata->spi_clk);
+-	if (ret < 0) {
+-		dev_err(dev, "failed to enable spi_clk (%d)\n", ret);
+-		return ret;
++		ret = clk_prepare_enable(mdata->spi_hclk);
++		if (ret < 0) {
++			dev_err(dev, "failed to prepare_enable spi_hclk (%d)\n", ret);
++			clk_disable_unprepare(mdata->spi_clk);
++			return ret;
++		}
+ 	}
+ 
+ 	return 0;
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/patches-5.4.inc b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/patches-5.4.inc
index b77fdc5..6ece5b2 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/patches-5.4.inc
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/patches-5.4.inc
@@ -145,4 +145,5 @@
     file://9017-drivers-mtd-spinand-Add-calibration-support-for-spinand.patch \
     file://9018-drivers-mtd-spi-nor-Add-calibration-support-for-spi-nor.patch \
     file://9019-drivers-char-tpm-Add-calibration-example-for-SPI-TPM-module.patch \
+    file://9102-spi-update-driver.patch \
     "