From: Chukun Pan Date: Tue, 6 Sep 2022 15:15:39 +0000 (+0800) Subject: mediatek: 5.15: add missing patch suffix X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=c91dd139d6e5c6583de3266eaca7312d0fb0eeb2;p=openwrt%2Fstaging%2Fneocturne.git mediatek: 5.15: add missing patch suffix The 213 patch is missing filename suffix. Fix it. Fixes: dabcaac ("mediatek: add mt7986 soc support to the target") Signed-off-by: Chukun Pan --- diff --git a/target/linux/mediatek/patches-5.15/213-spi-mediatek-add-mt7986-spi-support b/target/linux/mediatek/patches-5.15/213-spi-mediatek-add-mt7986-spi-support deleted file mode 100644 index f10d57a785..0000000000 --- a/target/linux/mediatek/patches-5.15/213-spi-mediatek-add-mt7986-spi-support +++ /dev/null @@ -1,917 +0,0 @@ -From 7d99750f96fc6904d54affebdc8c9b0bfae1e9e8 Mon Sep 17 00:00:00 2001 -From: Sam Shih -Date: Sun, 17 Apr 2022 11:40:22 +0800 -Subject: [PATCH] spi: mediatek: backport document and driver to support mt7986 - spi design - -this patch add the support of ipm design and upgrade devicetree binding - -The patch is comming from following threads -- https://lore.kernel.org/all/20220315032411.2826-1-leilk.liu@mediatek.com/ -- https://lore.kernel.org/all/20220401071616.8874-1-leilk.liu@mediatek.com/ - -Signed-off-by: Sam Shih ---- - .../bindings/spi/mediatek,spi-mt65xx.yaml | 111 ++++ - drivers/spi/spi-mt65xx.c | 509 ++++++++++++++++-- - 2 files changed, 572 insertions(+), 48 deletions(-) - create mode 100644 Documentation/devicetree/bindings/spi/mediatek,spi-mt65xx.yaml - ---- /dev/null -+++ b/Documentation/devicetree/bindings/spi/mediatek,spi-mt65xx.yaml -@@ -0,0 +1,111 @@ -+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -+%YAML 1.2 -+--- -+$id: http://devicetree.org/schemas/spi/mediatek,spi-mt65xx.yaml# -+$schema: http://devicetree.org/meta-schemas/core.yaml# -+ -+title: SPI Bus controller for MediaTek ARM SoCs -+ -+maintainers: -+ - Leilk Liu -+ -+allOf: -+ - $ref: "/schemas/spi/spi-controller.yaml#" -+ -+properties: -+ compatible: -+ oneOf: -+ - items: -+ - enum: -+ - mediatek,mt7629-spi -+ - const: mediatek,mt7622-spi -+ - items: -+ - enum: -+ - mediatek,mt8516-spi -+ - const: mediatek,mt2712-spi -+ - items: -+ - enum: -+ - mediatek,mt6779-spi -+ - mediatek,mt8186-spi -+ - mediatek,mt8192-spi -+ - mediatek,mt8195-spi -+ - const: mediatek,mt6765-spi -+ - items: -+ - enum: -+ - mediatek,mt7986-spi-ipm -+ - const: mediatek,spi-ipm -+ - items: -+ - enum: -+ - mediatek,mt2701-spi -+ - mediatek,mt2712-spi -+ - mediatek,mt6589-spi -+ - mediatek,mt6765-spi -+ - mediatek,mt6893-spi -+ - mediatek,mt7622-spi -+ - mediatek,mt8135-spi -+ - mediatek,mt8173-spi -+ - mediatek,mt8183-spi -+ -+ reg: -+ maxItems: 1 -+ -+ interrupts: -+ maxItems: 1 -+ -+ clocks: -+ minItems: 3 -+ items: -+ - description: clock used for the parent clock -+ - description: clock used for the muxes clock -+ - description: clock used for the clock gate -+ - description: clock used for the AHB bus, this clock is optional -+ -+ clock-names: -+ minItems: 3 -+ items: -+ - const: parent-clk -+ - const: sel-clk -+ - const: spi-clk -+ - const: hclk -+ -+ mediatek,pad-select: -+ $ref: /schemas/types.yaml#/definitions/uint32-array -+ minItems: 1 -+ maxItems: 4 -+ items: -+ enum: [0, 1, 2, 3] -+ description: -+ specify which pins group(ck/mi/mo/cs) spi controller used. -+ This is an array. -+ -+required: -+ - compatible -+ - reg -+ - interrupts -+ - clocks -+ - clock-names -+ - '#address-cells' -+ - '#size-cells' -+ -+unevaluatedProperties: false -+ -+examples: -+ - | -+ #include -+ #include -+ #include -+ #include -+ -+ spi@1100a000 { -+ compatible = "mediatek,mt8173-spi"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ reg = <0x1100a000 0x1000>; -+ interrupts = ; -+ clocks = <&topckgen CLK_TOP_SYSPLL3_D2>, -+ <&topckgen CLK_TOP_SPI_SEL>, -+ <&pericfg CLK_PERI_SPI0>; -+ clock-names = "parent-clk", "sel-clk", "spi-clk"; -+ cs-gpios = <&pio 105 GPIO_ACTIVE_LOW>, <&pio 72 GPIO_ACTIVE_LOW>; -+ mediatek,pad-select = <1>, <0>; -+ }; ---- a/drivers/spi/spi-mt65xx.c -+++ b/drivers/spi/spi-mt65xx.c -@@ -12,11 +12,12 @@ - #include - #include - #include --#include -+#include - #include - #include - #include - #include -+#include - #include - - #define SPI_CFG0_REG 0x0000 -@@ -31,6 +32,7 @@ - #define SPI_CFG2_REG 0x0028 - #define SPI_TX_SRC_REG_64 0x002c - #define SPI_RX_DST_REG_64 0x0030 -+#define SPI_CFG3_IPM_REG 0x0040 - - #define SPI_CFG0_SCK_HIGH_OFFSET 0 - #define SPI_CFG0_SCK_LOW_OFFSET 8 -@@ -51,6 +53,7 @@ - #define SPI_CFG1_CS_IDLE_MASK 0xff - #define SPI_CFG1_PACKET_LOOP_MASK 0xff00 - #define SPI_CFG1_PACKET_LENGTH_MASK 0x3ff0000 -+#define SPI_CFG1_IPM_PACKET_LENGTH_MASK GENMASK(31, 16) - #define SPI_CFG2_SCK_HIGH_OFFSET 0 - #define SPI_CFG2_SCK_LOW_OFFSET 16 - -@@ -71,6 +74,24 @@ - #define SPI_CMD_TX_ENDIAN BIT(15) - #define SPI_CMD_FINISH_IE BIT(16) - #define SPI_CMD_PAUSE_IE BIT(17) -+#define SPI_CMD_IPM_NONIDLE_MODE BIT(19) -+#define SPI_CMD_IPM_SPIM_LOOP BIT(21) -+#define SPI_CMD_IPM_GET_TICKDLY_OFFSET 22 -+ -+#define SPI_CMD_IPM_GET_TICKDLY_MASK GENMASK(24, 22) -+ -+#define PIN_MODE_CFG(x) ((x) / 2) -+ -+#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) -+#define SPI_CFG3_IPM_NODATA_FLAG BIT(5) -+#define SPI_CFG3_IPM_CMD_BYTELEN_OFFSET 8 -+#define SPI_CFG3_IPM_ADDR_BYTELEN_OFFSET 12 -+ -+#define SPI_CFG3_IPM_CMD_PIN_MODE_MASK GENMASK(1, 0) -+#define SPI_CFG3_IPM_CMD_BYTELEN_MASK GENMASK(11, 8) -+#define SPI_CFG3_IPM_ADDR_BYTELEN_MASK GENMASK(15, 12) - - #define MT8173_SPI_MAX_PAD_SEL 3 - -@@ -81,6 +102,9 @@ - - #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_32BITS_MASK (0xffffffff) - - #define DMA_ADDR_EXT_BITS (36) -@@ -96,6 +120,8 @@ struct mtk_spi_compatible { - bool dma_ext; - /* some IC no need unprepare SPI clk */ - bool no_need_unprepare; -+ /* IPM design adjust and extend register to support more features */ -+ bool ipm_design; - }; - - struct mtk_spi { -@@ -103,7 +129,7 @@ struct mtk_spi { - u32 state; - int pad_num; - u32 *pad_sel; -- struct clk *parent_clk, *sel_clk, *spi_clk; -+ struct clk *parent_clk, *sel_clk, *spi_clk, *spi_hclk; - struct spi_transfer *cur_transfer; - u32 xfer_len; - u32 num_xfered; -@@ -111,6 +137,11 @@ struct mtk_spi { - u32 tx_sgl_len, rx_sgl_len; - const struct mtk_spi_compatible *dev_comp; - u32 spi_clk_hz; -+ struct completion spimem_done; -+ bool use_spimem; -+ struct device *dev; -+ dma_addr_t tx_dma; -+ dma_addr_t rx_dma; - }; - - static const struct mtk_spi_compatible mtk_common_compat; -@@ -119,6 +150,12 @@ static const struct mtk_spi_compatible m - .must_tx = true, - }; - -+static const struct mtk_spi_compatible mtk_ipm_compat = { -+ .enhance_timing = true, -+ .dma_ext = true, -+ .ipm_design = true, -+}; -+ - static const struct mtk_spi_compatible mt6765_compat = { - .need_pad_sel = true, - .must_tx = true, -@@ -160,6 +197,9 @@ static const struct mtk_chip_config mtk_ - }; - - static const struct of_device_id mtk_spi_of_match[] = { -+ { .compatible = "mediatek,spi-ipm", -+ .data = (void *)&mtk_ipm_compat, -+ }, - { .compatible = "mediatek,mt2701-spi", - .data = (void *)&mtk_common_compat, - }, -@@ -278,12 +318,11 @@ static int mtk_spi_set_hw_cs_timing(stru - return 0; - } - --static int mtk_spi_prepare_message(struct spi_master *master, -- struct spi_message *msg) -+static int mtk_spi_hw_init(struct spi_master *master, -+ struct spi_device *spi) - { - u16 cpha, cpol; - u32 reg_val; -- struct spi_device *spi = msg->spi; - struct mtk_chip_config *chip_config = spi->controller_data; - struct mtk_spi *mdata = spi_master_get_devdata(master); - -@@ -291,6 +330,15 @@ static int mtk_spi_prepare_message(struc - cpol = spi->mode & SPI_CPOL ? 1 : 0; - - reg_val = readl(mdata->base + SPI_CMD_REG); -+ if (mdata->dev_comp->ipm_design) { -+ /* SPI transfer without idle time until packet length done */ -+ reg_val |= SPI_CMD_IPM_NONIDLE_MODE; -+ if (spi->mode & SPI_LOOP) -+ reg_val |= SPI_CMD_IPM_SPIM_LOOP; -+ else -+ reg_val &= ~SPI_CMD_IPM_SPIM_LOOP; -+ } -+ - if (cpha) - reg_val |= SPI_CMD_CPHA; - else -@@ -348,23 +396,39 @@ static int mtk_spi_prepare_message(struc - mdata->base + SPI_PAD_SEL_REG); - - /* tick delay */ -- reg_val = readl(mdata->base + SPI_CFG1_REG); - if (mdata->dev_comp->enhance_timing) { -- reg_val &= ~SPI_CFG1_GET_TICK_DLY_MASK; -- reg_val |= ((chip_config->tick_delay & 0x7) -- << SPI_CFG1_GET_TICK_DLY_OFFSET); -+ if (mdata->dev_comp->ipm_design) { -+ reg_val = readl(mdata->base + SPI_CMD_REG); -+ reg_val &= ~SPI_CMD_IPM_GET_TICKDLY_MASK; -+ reg_val |= ((chip_config->tick_delay & 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_TICK_DLY_MASK; -+ reg_val |= ((chip_config->tick_delay & 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 |= ((chip_config->tick_delay & 0x3) - << SPI_CFG1_GET_TICK_DLY_OFFSET_V1); -+ writel(reg_val, mdata->base + SPI_CFG1_REG); - } -- writel(reg_val, mdata->base + SPI_CFG1_REG); - - /* set hw cs timing */ - mtk_spi_set_hw_cs_timing(spi); - return 0; - } - -+static int mtk_spi_prepare_message(struct spi_master *master, -+ struct spi_message *msg) -+{ -+ return mtk_spi_hw_init(master, msg->spi); -+} -+ - static void mtk_spi_set_cs(struct spi_device *spi, bool enable) - { - u32 reg_val; -@@ -386,13 +450,13 @@ static void mtk_spi_set_cs(struct spi_de - } - - static void mtk_spi_prepare_transfer(struct spi_master *master, -- struct spi_transfer *xfer) -+ u32 speed_hz) - { - u32 div, sck_time, reg_val; - struct mtk_spi *mdata = spi_master_get_devdata(master); - -- if (xfer->speed_hz < mdata->spi_clk_hz / 2) -- div = DIV_ROUND_UP(mdata->spi_clk_hz, xfer->speed_hz); -+ if (speed_hz < mdata->spi_clk_hz / 2) -+ div = DIV_ROUND_UP(mdata->spi_clk_hz, speed_hz); - else - div = 1; - -@@ -423,12 +487,24 @@ static void mtk_spi_setup_packet(struct - u32 packet_size, packet_loop, reg_val; - struct mtk_spi *mdata = spi_master_get_devdata(master); - -- packet_size = min_t(u32, mdata->xfer_len, MTK_SPI_PACKET_SIZE); -+ if (mdata->dev_comp->ipm_design) -+ packet_size = min_t(u32, -+ mdata->xfer_len, -+ MTK_SPI_IPM_PACKET_SIZE); -+ else -+ packet_size = min_t(u32, -+ mdata->xfer_len, -+ MTK_SPI_PACKET_SIZE); -+ - packet_loop = mdata->xfer_len / packet_size; - - reg_val = readl(mdata->base + SPI_CFG1_REG); -- reg_val &= ~(SPI_CFG1_PACKET_LENGTH_MASK | SPI_CFG1_PACKET_LOOP_MASK); -+ if (mdata->dev_comp->ipm_design) -+ reg_val &= ~SPI_CFG1_IPM_PACKET_LENGTH_MASK; -+ else -+ reg_val &= ~SPI_CFG1_PACKET_LENGTH_MASK; - reg_val |= (packet_size - 1) << SPI_CFG1_PACKET_LENGTH_OFFSET; -+ reg_val &= ~SPI_CFG1_PACKET_LOOP_MASK; - reg_val |= (packet_loop - 1) << SPI_CFG1_PACKET_LOOP_OFFSET; - writel(reg_val, mdata->base + SPI_CFG1_REG); - } -@@ -523,7 +599,7 @@ static int mtk_spi_fifo_transfer(struct - mdata->cur_transfer = xfer; - mdata->xfer_len = min(MTK_SPI_MAX_FIFO_SIZE, xfer->len); - mdata->num_xfered = 0; -- mtk_spi_prepare_transfer(master, xfer); -+ mtk_spi_prepare_transfer(master, xfer->speed_hz); - mtk_spi_setup_packet(master); - - if (xfer->tx_buf) { -@@ -556,7 +632,7 @@ static int mtk_spi_dma_transfer(struct s - mdata->cur_transfer = xfer; - mdata->num_xfered = 0; - -- mtk_spi_prepare_transfer(master, xfer); -+ mtk_spi_prepare_transfer(master, xfer->speed_hz); - - cmd = readl(mdata->base + SPI_CMD_REG); - if (xfer->tx_buf) -@@ -591,6 +667,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 -@@ -614,8 +703,9 @@ static int mtk_spi_setup(struct spi_devi - if (!spi->controller_data) - spi->controller_data = (void *)&mtk_default_chip_info; - -- 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; - } -@@ -633,6 +723,12 @@ static irqreturn_t mtk_spi_interrupt(int - else - mdata->state = MTK_SPI_IDLE; - -+ /* SPI-MEM ops */ -+ if (mdata->use_spimem) { -+ complete(&mdata->spimem_done); -+ return IRQ_HANDLED; -+ } -+ - if (!master->can_dma(master, NULL, trans)) { - if (trans->rx_buf) { - cnt = mdata->xfer_len / 4; -@@ -716,6 +812,274 @@ static irqreturn_t mtk_spi_interrupt(int - return IRQ_HANDLED; - } - -+static int mtk_spi_mem_adjust_op_size(struct spi_mem *mem, -+ struct spi_mem_op *op) -+{ -+ int opcode_len; -+ -+ 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) { -+ op->data.nbytes = MTK_SPI_IPM_PACKET_SIZE - opcode_len; -+ /* force data buffer dma-aligned. */ -+ op->data.nbytes -= op->data.nbytes % 4; -+ } -+ } -+ -+ return 0; -+} -+ -+static bool mtk_spi_mem_supports_op(struct spi_mem *mem, -+ const struct spi_mem_op *op) -+{ -+ if (!spi_mem_default_supports_op(mem, op)) -+ return false; -+ -+ if (op->addr.nbytes && op->dummy.nbytes && -+ op->addr.buswidth != op->dummy.buswidth) -+ return false; -+ -+ if (op->addr.nbytes + op->dummy.nbytes > 16) -+ return false; -+ -+ if (op->data.nbytes > MTK_SPI_IPM_PACKET_SIZE) { -+ if (op->data.nbytes / MTK_SPI_IPM_PACKET_SIZE > -+ MTK_SPI_IPM_PACKET_LOOP || -+ op->data.nbytes % MTK_SPI_IPM_PACKET_SIZE != 0) -+ return false; -+ } -+ -+ return true; -+} -+ -+static void mtk_spi_mem_setup_dma_xfer(struct spi_master *master, -+ const struct spi_mem_op *op) -+{ -+ struct mtk_spi *mdata = spi_master_get_devdata(master); -+ -+ writel((u32)(mdata->tx_dma & MTK_SPI_32BITS_MASK), -+ mdata->base + SPI_TX_SRC_REG); -+#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT -+ if (mdata->dev_comp->dma_ext) -+ writel((u32)(mdata->tx_dma >> 32), -+ mdata->base + SPI_TX_SRC_REG_64); -+#endif -+ -+ if (op->data.dir == SPI_MEM_DATA_IN) { -+ writel((u32)(mdata->rx_dma & MTK_SPI_32BITS_MASK), -+ mdata->base + SPI_RX_DST_REG); -+#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT -+ if (mdata->dev_comp->dma_ext) -+ writel((u32)(mdata->rx_dma >> 32), -+ mdata->base + SPI_RX_DST_REG_64); -+#endif -+ } -+} -+ -+static int mtk_spi_transfer_wait(struct spi_mem *mem, -+ const struct spi_mem_op *op) -+{ -+ struct mtk_spi *mdata = spi_master_get_devdata(mem->spi->master); -+ /* -+ * 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 *= 32; /* prevent we may get 0 for short transfers. */ -+ else -+ ms *= op->data.nbytes; -+ ms = div_u64(ms, mem->spi->max_speed_hz); -+ ms += ms + 1000; /* 1s tolerance */ -+ -+ if (ms > UINT_MAX) -+ ms = UINT_MAX; -+ -+ if (!wait_for_completion_timeout(&mdata->spimem_done, -+ msecs_to_jiffies(ms))) { -+ dev_err(mdata->dev, "spi-mem transfer timeout\n"); -+ return -ETIMEDOUT; -+ } -+ -+ return 0; -+} -+ -+static int mtk_spi_mem_exec_op(struct spi_mem *mem, -+ const struct spi_mem_op *op) -+{ -+ struct mtk_spi *mdata = spi_master_get_devdata(mem->spi->master); -+ u32 reg_val, nio, tx_size; -+ char *tx_tmp_buf, *rx_tmp_buf; -+ int ret = 0; -+ -+ mdata->use_spimem = true; -+ reinit_completion(&mdata->spimem_done); -+ -+ mtk_spi_reset(mdata); -+ mtk_spi_hw_init(mem->spi->master, mem->spi); -+ mtk_spi_prepare_transfer(mem->spi->master, mem->spi->max_speed_hz); -+ -+ reg_val = readl(mdata->base + SPI_CFG3_IPM_REG); -+ /* opcode byte len */ -+ reg_val &= ~SPI_CFG3_IPM_CMD_BYTELEN_MASK; -+ reg_val |= 1 << SPI_CFG3_IPM_CMD_BYTELEN_OFFSET; -+ -+ /* addr & dummy byte len */ -+ reg_val &= ~SPI_CFG3_IPM_ADDR_BYTELEN_MASK; -+ if (op->addr.nbytes || op->dummy.nbytes) -+ reg_val |= (op->addr.nbytes + op->dummy.nbytes) << -+ SPI_CFG3_IPM_ADDR_BYTELEN_OFFSET; -+ -+ /* data byte len */ -+ if (op->data.dir == SPI_MEM_NO_DATA) { -+ reg_val |= SPI_CFG3_IPM_NODATA_FLAG; -+ writel(0, mdata->base + SPI_CFG1_REG); -+ } else { -+ reg_val &= ~SPI_CFG3_IPM_NODATA_FLAG; -+ mdata->xfer_len = op->data.nbytes; -+ mtk_spi_setup_packet(mem->spi->master); -+ } -+ -+ if (op->addr.nbytes || op->dummy.nbytes) { -+ if (op->addr.buswidth == 1 || op->dummy.buswidth == 1) -+ reg_val |= SPI_CFG3_IPM_XMODE_EN; -+ else -+ reg_val &= ~SPI_CFG3_IPM_XMODE_EN; -+ } -+ -+ if (op->addr.buswidth == 2 || -+ op->dummy.buswidth == 2 || -+ op->data.buswidth == 2) -+ nio = 2; -+ else if (op->addr.buswidth == 4 || -+ 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); -+ -+ reg_val |= SPI_CFG3_IPM_HALF_DUPLEX_EN; -+ if (op->data.dir == SPI_MEM_DATA_IN) -+ reg_val |= SPI_CFG3_IPM_HALF_DUPLEX_DIR; -+ else -+ reg_val &= ~SPI_CFG3_IPM_HALF_DUPLEX_DIR; -+ writel(reg_val, mdata->base + SPI_CFG3_IPM_REG); -+ -+ tx_size = 1 + op->addr.nbytes + op->dummy.nbytes; -+ if (op->data.dir == SPI_MEM_DATA_OUT) -+ tx_size += op->data.nbytes; -+ -+ tx_size = max_t(u32, tx_size, 32); -+ -+ tx_tmp_buf = kzalloc(tx_size, GFP_KERNEL | GFP_DMA); -+ if (!tx_tmp_buf) { -+ mdata->use_spimem = false; -+ return -ENOMEM; -+ } -+ -+ tx_tmp_buf[0] = op->cmd.opcode; -+ -+ if (op->addr.nbytes) { -+ int i; -+ -+ for (i = 0; i < op->addr.nbytes; i++) -+ tx_tmp_buf[i + 1] = op->addr.val >> -+ (8 * (op->addr.nbytes - i - 1)); -+ } -+ -+ if (op->dummy.nbytes) -+ memset(tx_tmp_buf + op->addr.nbytes + 1, -+ 0xff, -+ op->dummy.nbytes); -+ -+ if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT) -+ memcpy(tx_tmp_buf + op->dummy.nbytes + op->addr.nbytes + 1, -+ op->data.buf.out, -+ op->data.nbytes); -+ -+ mdata->tx_dma = dma_map_single(mdata->dev, tx_tmp_buf, -+ tx_size, DMA_TO_DEVICE); -+ if (dma_mapping_error(mdata->dev, mdata->tx_dma)) { -+ ret = -ENOMEM; -+ goto err_exit; -+ } -+ -+ 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) { -+ 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, -+ op->data.nbytes, -+ DMA_FROM_DEVICE); -+ if (dma_mapping_error(mdata->dev, mdata->rx_dma)) { -+ ret = -ENOMEM; -+ goto kfree_rx_tmp_buf; -+ } -+ } -+ -+ reg_val = readl(mdata->base + SPI_CMD_REG); -+ reg_val |= SPI_CMD_TX_DMA; -+ if (op->data.dir == SPI_MEM_DATA_IN) -+ reg_val |= SPI_CMD_RX_DMA; -+ writel(reg_val, mdata->base + SPI_CMD_REG); -+ -+ mtk_spi_mem_setup_dma_xfer(mem->spi->master, op); -+ -+ mtk_spi_enable_transfer(mem->spi->master); -+ -+ /* Wait for the interrupt. */ -+ ret = mtk_spi_transfer_wait(mem, op); -+ if (ret) -+ goto unmap_rx_dma; -+ -+ /* spi disable dma */ -+ reg_val = readl(mdata->base + SPI_CMD_REG); -+ reg_val &= ~SPI_CMD_TX_DMA; -+ if (op->data.dir == SPI_MEM_DATA_IN) -+ reg_val &= ~SPI_CMD_RX_DMA; -+ writel(reg_val, mdata->base + SPI_CMD_REG); -+ -+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)) -+ memcpy(op->data.buf.in, rx_tmp_buf, op->data.nbytes); -+ } -+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); -+err_exit: -+ kfree(tx_tmp_buf); -+ mdata->use_spimem = false; -+ -+ return ret; -+} -+ -+static const struct spi_controller_mem_ops mtk_spi_mem_ops = { -+ .adjust_op_size = mtk_spi_mem_adjust_op_size, -+ .supports_op = mtk_spi_mem_supports_op, -+ .exec_op = mtk_spi_mem_exec_op, -+}; -+ - static int mtk_spi_probe(struct platform_device *pdev) - { - struct spi_master *master; -@@ -739,6 +1103,7 @@ static int mtk_spi_probe(struct platform - master->can_dma = mtk_spi_can_dma; - master->setup = mtk_spi_setup; - master->set_cs_timing = mtk_spi_set_hw_cs_timing; -+ master->use_gpio_descriptors = true; - - of_id = of_match_node(mtk_spi_of_match, pdev->dev.of_node); - if (!of_id) { -@@ -755,6 +1120,14 @@ static int mtk_spi_probe(struct platform - - if (mdata->dev_comp->must_tx) - master->flags = SPI_MASTER_MUST_TX; -+ if (mdata->dev_comp->ipm_design) -+ master->mode_bits |= SPI_LOOP; -+ -+ if (mdata->dev_comp->ipm_design) { -+ mdata->dev = &pdev->dev; -+ master->mem_ops = &mtk_spi_mem_ops; -+ init_completion(&mdata->spimem_done); -+ } - - if (mdata->dev_comp->need_pad_sel) { - mdata->pad_num = of_property_count_u32_elems( -@@ -831,25 +1204,40 @@ static int mtk_spi_probe(struct platform - goto err_put_master; - } - -+ mdata->spi_hclk = devm_clk_get_optional(&pdev->dev, "hclk"); -+ if (IS_ERR(mdata->spi_hclk)) { -+ ret = PTR_ERR(mdata->spi_hclk); -+ dev_err(&pdev->dev, "failed to get 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 hclk (%d)\n", ret); -+ goto err_put_master; -+ } -+ - ret = clk_prepare_enable(mdata->spi_clk); - if (ret < 0) { - dev_err(&pdev->dev, "failed to enable spi_clk (%d)\n", ret); -- goto err_put_master; -+ goto err_disable_spi_hclk; - } - - ret = clk_set_parent(mdata->sel_clk, mdata->parent_clk); - if (ret < 0) { - dev_err(&pdev->dev, "failed to clk_set_parent (%d)\n", ret); -- clk_disable_unprepare(mdata->spi_clk); -- goto err_put_master; -+ goto err_disable_spi_clk; - } - - mdata->spi_clk_hz = clk_get_rate(mdata->spi_clk); - -- if (mdata->dev_comp->no_need_unprepare) -+ if (mdata->dev_comp->no_need_unprepare) { - clk_disable(mdata->spi_clk); -- else -+ clk_disable(mdata->spi_hclk); -+ } else { - clk_disable_unprepare(mdata->spi_clk); -+ clk_disable_unprepare(mdata->spi_hclk); -+ } - - pm_runtime_enable(&pdev->dev); - -@@ -862,25 +1250,12 @@ static int mtk_spi_probe(struct platform - goto err_disable_runtime_pm; - } - -- if (!master->cs_gpios && master->num_chipselect > 1) { -+ if (!master->cs_gpiods && master->num_chipselect > 1) { - dev_err(&pdev->dev, - "cs_gpios not specified and num_chipselect > 1\n"); - ret = -EINVAL; - goto err_disable_runtime_pm; - } -- -- 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) -@@ -902,6 +1277,10 @@ static int mtk_spi_probe(struct platform - - err_disable_runtime_pm: - pm_runtime_disable(&pdev->dev); -+err_disable_spi_clk: -+ clk_disable_unprepare(mdata->spi_clk); -+err_disable_spi_hclk: -+ clk_disable_unprepare(mdata->spi_hclk); - err_put_master: - spi_master_put(master); - -@@ -917,8 +1296,10 @@ static int mtk_spi_remove(struct platfor - - mtk_spi_reset(mdata); - -- if (mdata->dev_comp->no_need_unprepare) -+ if (mdata->dev_comp->no_need_unprepare) { - clk_unprepare(mdata->spi_clk); -+ clk_unprepare(mdata->spi_hclk); -+ } - - return 0; - } -@@ -934,8 +1315,10 @@ static int mtk_spi_suspend(struct device - if (ret) - return ret; - -- if (!pm_runtime_suspended(dev)) -+ if (!pm_runtime_suspended(dev)) { - clk_disable_unprepare(mdata->spi_clk); -+ clk_disable_unprepare(mdata->spi_hclk); -+ } - - return ret; - } -@@ -952,11 +1335,20 @@ static int mtk_spi_resume(struct device - 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) -+ if (ret < 0) { - clk_disable_unprepare(mdata->spi_clk); -+ clk_disable_unprepare(mdata->spi_hclk); -+ } - - return ret; - } -@@ -968,10 +1360,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); - -- if (mdata->dev_comp->no_need_unprepare) -+ if (mdata->dev_comp->no_need_unprepare) { - clk_disable(mdata->spi_clk); -- else -+ clk_disable(mdata->spi_hclk); -+ } else { - clk_disable_unprepare(mdata->spi_clk); -+ clk_disable_unprepare(mdata->spi_hclk); -+ } - - return 0; - } -@@ -982,13 +1377,31 @@ static int mtk_spi_runtime_resume(struct - struct mtk_spi *mdata = spi_master_get_devdata(master); - int ret; - -- if (mdata->dev_comp->no_need_unprepare) -+ if (mdata->dev_comp->no_need_unprepare) { - ret = clk_enable(mdata->spi_clk); -- else -+ 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 enable spi_clk (%d)\n", ret); -- return ret; -+ if (ret < 0) { -+ dev_err(dev, "failed to prepare_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/target/linux/mediatek/patches-5.15/213-spi-mediatek-add-mt7986-spi-support.patch b/target/linux/mediatek/patches-5.15/213-spi-mediatek-add-mt7986-spi-support.patch new file mode 100644 index 0000000000..f10d57a785 --- /dev/null +++ b/target/linux/mediatek/patches-5.15/213-spi-mediatek-add-mt7986-spi-support.patch @@ -0,0 +1,917 @@ +From 7d99750f96fc6904d54affebdc8c9b0bfae1e9e8 Mon Sep 17 00:00:00 2001 +From: Sam Shih +Date: Sun, 17 Apr 2022 11:40:22 +0800 +Subject: [PATCH] spi: mediatek: backport document and driver to support mt7986 + spi design + +this patch add the support of ipm design and upgrade devicetree binding + +The patch is comming from following threads +- https://lore.kernel.org/all/20220315032411.2826-1-leilk.liu@mediatek.com/ +- https://lore.kernel.org/all/20220401071616.8874-1-leilk.liu@mediatek.com/ + +Signed-off-by: Sam Shih +--- + .../bindings/spi/mediatek,spi-mt65xx.yaml | 111 ++++ + drivers/spi/spi-mt65xx.c | 509 ++++++++++++++++-- + 2 files changed, 572 insertions(+), 48 deletions(-) + create mode 100644 Documentation/devicetree/bindings/spi/mediatek,spi-mt65xx.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/spi/mediatek,spi-mt65xx.yaml +@@ -0,0 +1,111 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/spi/mediatek,spi-mt65xx.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: SPI Bus controller for MediaTek ARM SoCs ++ ++maintainers: ++ - Leilk Liu ++ ++allOf: ++ - $ref: "/schemas/spi/spi-controller.yaml#" ++ ++properties: ++ compatible: ++ oneOf: ++ - items: ++ - enum: ++ - mediatek,mt7629-spi ++ - const: mediatek,mt7622-spi ++ - items: ++ - enum: ++ - mediatek,mt8516-spi ++ - const: mediatek,mt2712-spi ++ - items: ++ - enum: ++ - mediatek,mt6779-spi ++ - mediatek,mt8186-spi ++ - mediatek,mt8192-spi ++ - mediatek,mt8195-spi ++ - const: mediatek,mt6765-spi ++ - items: ++ - enum: ++ - mediatek,mt7986-spi-ipm ++ - const: mediatek,spi-ipm ++ - items: ++ - enum: ++ - mediatek,mt2701-spi ++ - mediatek,mt2712-spi ++ - mediatek,mt6589-spi ++ - mediatek,mt6765-spi ++ - mediatek,mt6893-spi ++ - mediatek,mt7622-spi ++ - mediatek,mt8135-spi ++ - mediatek,mt8173-spi ++ - mediatek,mt8183-spi ++ ++ reg: ++ maxItems: 1 ++ ++ interrupts: ++ maxItems: 1 ++ ++ clocks: ++ minItems: 3 ++ items: ++ - description: clock used for the parent clock ++ - description: clock used for the muxes clock ++ - description: clock used for the clock gate ++ - description: clock used for the AHB bus, this clock is optional ++ ++ clock-names: ++ minItems: 3 ++ items: ++ - const: parent-clk ++ - const: sel-clk ++ - const: spi-clk ++ - const: hclk ++ ++ mediatek,pad-select: ++ $ref: /schemas/types.yaml#/definitions/uint32-array ++ minItems: 1 ++ maxItems: 4 ++ items: ++ enum: [0, 1, 2, 3] ++ description: ++ specify which pins group(ck/mi/mo/cs) spi controller used. ++ This is an array. ++ ++required: ++ - compatible ++ - reg ++ - interrupts ++ - clocks ++ - clock-names ++ - '#address-cells' ++ - '#size-cells' ++ ++unevaluatedProperties: false ++ ++examples: ++ - | ++ #include ++ #include ++ #include ++ #include ++ ++ spi@1100a000 { ++ compatible = "mediatek,mt8173-spi"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <0x1100a000 0x1000>; ++ interrupts = ; ++ clocks = <&topckgen CLK_TOP_SYSPLL3_D2>, ++ <&topckgen CLK_TOP_SPI_SEL>, ++ <&pericfg CLK_PERI_SPI0>; ++ clock-names = "parent-clk", "sel-clk", "spi-clk"; ++ cs-gpios = <&pio 105 GPIO_ACTIVE_LOW>, <&pio 72 GPIO_ACTIVE_LOW>; ++ mediatek,pad-select = <1>, <0>; ++ }; +--- a/drivers/spi/spi-mt65xx.c ++++ b/drivers/spi/spi-mt65xx.c +@@ -12,11 +12,12 @@ + #include + #include + #include +-#include ++#include + #include + #include + #include + #include ++#include + #include + + #define SPI_CFG0_REG 0x0000 +@@ -31,6 +32,7 @@ + #define SPI_CFG2_REG 0x0028 + #define SPI_TX_SRC_REG_64 0x002c + #define SPI_RX_DST_REG_64 0x0030 ++#define SPI_CFG3_IPM_REG 0x0040 + + #define SPI_CFG0_SCK_HIGH_OFFSET 0 + #define SPI_CFG0_SCK_LOW_OFFSET 8 +@@ -51,6 +53,7 @@ + #define SPI_CFG1_CS_IDLE_MASK 0xff + #define SPI_CFG1_PACKET_LOOP_MASK 0xff00 + #define SPI_CFG1_PACKET_LENGTH_MASK 0x3ff0000 ++#define SPI_CFG1_IPM_PACKET_LENGTH_MASK GENMASK(31, 16) + #define SPI_CFG2_SCK_HIGH_OFFSET 0 + #define SPI_CFG2_SCK_LOW_OFFSET 16 + +@@ -71,6 +74,24 @@ + #define SPI_CMD_TX_ENDIAN BIT(15) + #define SPI_CMD_FINISH_IE BIT(16) + #define SPI_CMD_PAUSE_IE BIT(17) ++#define SPI_CMD_IPM_NONIDLE_MODE BIT(19) ++#define SPI_CMD_IPM_SPIM_LOOP BIT(21) ++#define SPI_CMD_IPM_GET_TICKDLY_OFFSET 22 ++ ++#define SPI_CMD_IPM_GET_TICKDLY_MASK GENMASK(24, 22) ++ ++#define PIN_MODE_CFG(x) ((x) / 2) ++ ++#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) ++#define SPI_CFG3_IPM_NODATA_FLAG BIT(5) ++#define SPI_CFG3_IPM_CMD_BYTELEN_OFFSET 8 ++#define SPI_CFG3_IPM_ADDR_BYTELEN_OFFSET 12 ++ ++#define SPI_CFG3_IPM_CMD_PIN_MODE_MASK GENMASK(1, 0) ++#define SPI_CFG3_IPM_CMD_BYTELEN_MASK GENMASK(11, 8) ++#define SPI_CFG3_IPM_ADDR_BYTELEN_MASK GENMASK(15, 12) + + #define MT8173_SPI_MAX_PAD_SEL 3 + +@@ -81,6 +102,9 @@ + + #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_32BITS_MASK (0xffffffff) + + #define DMA_ADDR_EXT_BITS (36) +@@ -96,6 +120,8 @@ struct mtk_spi_compatible { + bool dma_ext; + /* some IC no need unprepare SPI clk */ + bool no_need_unprepare; ++ /* IPM design adjust and extend register to support more features */ ++ bool ipm_design; + }; + + struct mtk_spi { +@@ -103,7 +129,7 @@ struct mtk_spi { + u32 state; + int pad_num; + u32 *pad_sel; +- struct clk *parent_clk, *sel_clk, *spi_clk; ++ struct clk *parent_clk, *sel_clk, *spi_clk, *spi_hclk; + struct spi_transfer *cur_transfer; + u32 xfer_len; + u32 num_xfered; +@@ -111,6 +137,11 @@ struct mtk_spi { + u32 tx_sgl_len, rx_sgl_len; + const struct mtk_spi_compatible *dev_comp; + u32 spi_clk_hz; ++ struct completion spimem_done; ++ bool use_spimem; ++ struct device *dev; ++ dma_addr_t tx_dma; ++ dma_addr_t rx_dma; + }; + + static const struct mtk_spi_compatible mtk_common_compat; +@@ -119,6 +150,12 @@ static const struct mtk_spi_compatible m + .must_tx = true, + }; + ++static const struct mtk_spi_compatible mtk_ipm_compat = { ++ .enhance_timing = true, ++ .dma_ext = true, ++ .ipm_design = true, ++}; ++ + static const struct mtk_spi_compatible mt6765_compat = { + .need_pad_sel = true, + .must_tx = true, +@@ -160,6 +197,9 @@ static const struct mtk_chip_config mtk_ + }; + + static const struct of_device_id mtk_spi_of_match[] = { ++ { .compatible = "mediatek,spi-ipm", ++ .data = (void *)&mtk_ipm_compat, ++ }, + { .compatible = "mediatek,mt2701-spi", + .data = (void *)&mtk_common_compat, + }, +@@ -278,12 +318,11 @@ static int mtk_spi_set_hw_cs_timing(stru + return 0; + } + +-static int mtk_spi_prepare_message(struct spi_master *master, +- struct spi_message *msg) ++static int mtk_spi_hw_init(struct spi_master *master, ++ struct spi_device *spi) + { + u16 cpha, cpol; + u32 reg_val; +- struct spi_device *spi = msg->spi; + struct mtk_chip_config *chip_config = spi->controller_data; + struct mtk_spi *mdata = spi_master_get_devdata(master); + +@@ -291,6 +330,15 @@ static int mtk_spi_prepare_message(struc + cpol = spi->mode & SPI_CPOL ? 1 : 0; + + reg_val = readl(mdata->base + SPI_CMD_REG); ++ if (mdata->dev_comp->ipm_design) { ++ /* SPI transfer without idle time until packet length done */ ++ reg_val |= SPI_CMD_IPM_NONIDLE_MODE; ++ if (spi->mode & SPI_LOOP) ++ reg_val |= SPI_CMD_IPM_SPIM_LOOP; ++ else ++ reg_val &= ~SPI_CMD_IPM_SPIM_LOOP; ++ } ++ + if (cpha) + reg_val |= SPI_CMD_CPHA; + else +@@ -348,23 +396,39 @@ static int mtk_spi_prepare_message(struc + mdata->base + SPI_PAD_SEL_REG); + + /* tick delay */ +- reg_val = readl(mdata->base + SPI_CFG1_REG); + if (mdata->dev_comp->enhance_timing) { +- reg_val &= ~SPI_CFG1_GET_TICK_DLY_MASK; +- reg_val |= ((chip_config->tick_delay & 0x7) +- << SPI_CFG1_GET_TICK_DLY_OFFSET); ++ if (mdata->dev_comp->ipm_design) { ++ reg_val = readl(mdata->base + SPI_CMD_REG); ++ reg_val &= ~SPI_CMD_IPM_GET_TICKDLY_MASK; ++ reg_val |= ((chip_config->tick_delay & 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_TICK_DLY_MASK; ++ reg_val |= ((chip_config->tick_delay & 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 |= ((chip_config->tick_delay & 0x3) + << SPI_CFG1_GET_TICK_DLY_OFFSET_V1); ++ writel(reg_val, mdata->base + SPI_CFG1_REG); + } +- writel(reg_val, mdata->base + SPI_CFG1_REG); + + /* set hw cs timing */ + mtk_spi_set_hw_cs_timing(spi); + return 0; + } + ++static int mtk_spi_prepare_message(struct spi_master *master, ++ struct spi_message *msg) ++{ ++ return mtk_spi_hw_init(master, msg->spi); ++} ++ + static void mtk_spi_set_cs(struct spi_device *spi, bool enable) + { + u32 reg_val; +@@ -386,13 +450,13 @@ static void mtk_spi_set_cs(struct spi_de + } + + static void mtk_spi_prepare_transfer(struct spi_master *master, +- struct spi_transfer *xfer) ++ u32 speed_hz) + { + u32 div, sck_time, reg_val; + struct mtk_spi *mdata = spi_master_get_devdata(master); + +- if (xfer->speed_hz < mdata->spi_clk_hz / 2) +- div = DIV_ROUND_UP(mdata->spi_clk_hz, xfer->speed_hz); ++ if (speed_hz < mdata->spi_clk_hz / 2) ++ div = DIV_ROUND_UP(mdata->spi_clk_hz, speed_hz); + else + div = 1; + +@@ -423,12 +487,24 @@ static void mtk_spi_setup_packet(struct + u32 packet_size, packet_loop, reg_val; + struct mtk_spi *mdata = spi_master_get_devdata(master); + +- packet_size = min_t(u32, mdata->xfer_len, MTK_SPI_PACKET_SIZE); ++ if (mdata->dev_comp->ipm_design) ++ packet_size = min_t(u32, ++ mdata->xfer_len, ++ MTK_SPI_IPM_PACKET_SIZE); ++ else ++ packet_size = min_t(u32, ++ mdata->xfer_len, ++ MTK_SPI_PACKET_SIZE); ++ + packet_loop = mdata->xfer_len / packet_size; + + reg_val = readl(mdata->base + SPI_CFG1_REG); +- reg_val &= ~(SPI_CFG1_PACKET_LENGTH_MASK | SPI_CFG1_PACKET_LOOP_MASK); ++ if (mdata->dev_comp->ipm_design) ++ reg_val &= ~SPI_CFG1_IPM_PACKET_LENGTH_MASK; ++ else ++ reg_val &= ~SPI_CFG1_PACKET_LENGTH_MASK; + reg_val |= (packet_size - 1) << SPI_CFG1_PACKET_LENGTH_OFFSET; ++ reg_val &= ~SPI_CFG1_PACKET_LOOP_MASK; + reg_val |= (packet_loop - 1) << SPI_CFG1_PACKET_LOOP_OFFSET; + writel(reg_val, mdata->base + SPI_CFG1_REG); + } +@@ -523,7 +599,7 @@ static int mtk_spi_fifo_transfer(struct + mdata->cur_transfer = xfer; + mdata->xfer_len = min(MTK_SPI_MAX_FIFO_SIZE, xfer->len); + mdata->num_xfered = 0; +- mtk_spi_prepare_transfer(master, xfer); ++ mtk_spi_prepare_transfer(master, xfer->speed_hz); + mtk_spi_setup_packet(master); + + if (xfer->tx_buf) { +@@ -556,7 +632,7 @@ static int mtk_spi_dma_transfer(struct s + mdata->cur_transfer = xfer; + mdata->num_xfered = 0; + +- mtk_spi_prepare_transfer(master, xfer); ++ mtk_spi_prepare_transfer(master, xfer->speed_hz); + + cmd = readl(mdata->base + SPI_CMD_REG); + if (xfer->tx_buf) +@@ -591,6 +667,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 +@@ -614,8 +703,9 @@ static int mtk_spi_setup(struct spi_devi + if (!spi->controller_data) + spi->controller_data = (void *)&mtk_default_chip_info; + +- 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; + } +@@ -633,6 +723,12 @@ static irqreturn_t mtk_spi_interrupt(int + else + mdata->state = MTK_SPI_IDLE; + ++ /* SPI-MEM ops */ ++ if (mdata->use_spimem) { ++ complete(&mdata->spimem_done); ++ return IRQ_HANDLED; ++ } ++ + if (!master->can_dma(master, NULL, trans)) { + if (trans->rx_buf) { + cnt = mdata->xfer_len / 4; +@@ -716,6 +812,274 @@ static irqreturn_t mtk_spi_interrupt(int + return IRQ_HANDLED; + } + ++static int mtk_spi_mem_adjust_op_size(struct spi_mem *mem, ++ struct spi_mem_op *op) ++{ ++ int opcode_len; ++ ++ 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) { ++ op->data.nbytes = MTK_SPI_IPM_PACKET_SIZE - opcode_len; ++ /* force data buffer dma-aligned. */ ++ op->data.nbytes -= op->data.nbytes % 4; ++ } ++ } ++ ++ return 0; ++} ++ ++static bool mtk_spi_mem_supports_op(struct spi_mem *mem, ++ const struct spi_mem_op *op) ++{ ++ if (!spi_mem_default_supports_op(mem, op)) ++ return false; ++ ++ if (op->addr.nbytes && op->dummy.nbytes && ++ op->addr.buswidth != op->dummy.buswidth) ++ return false; ++ ++ if (op->addr.nbytes + op->dummy.nbytes > 16) ++ return false; ++ ++ if (op->data.nbytes > MTK_SPI_IPM_PACKET_SIZE) { ++ if (op->data.nbytes / MTK_SPI_IPM_PACKET_SIZE > ++ MTK_SPI_IPM_PACKET_LOOP || ++ op->data.nbytes % MTK_SPI_IPM_PACKET_SIZE != 0) ++ return false; ++ } ++ ++ return true; ++} ++ ++static void mtk_spi_mem_setup_dma_xfer(struct spi_master *master, ++ const struct spi_mem_op *op) ++{ ++ struct mtk_spi *mdata = spi_master_get_devdata(master); ++ ++ writel((u32)(mdata->tx_dma & MTK_SPI_32BITS_MASK), ++ mdata->base + SPI_TX_SRC_REG); ++#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT ++ if (mdata->dev_comp->dma_ext) ++ writel((u32)(mdata->tx_dma >> 32), ++ mdata->base + SPI_TX_SRC_REG_64); ++#endif ++ ++ if (op->data.dir == SPI_MEM_DATA_IN) { ++ writel((u32)(mdata->rx_dma & MTK_SPI_32BITS_MASK), ++ mdata->base + SPI_RX_DST_REG); ++#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT ++ if (mdata->dev_comp->dma_ext) ++ writel((u32)(mdata->rx_dma >> 32), ++ mdata->base + SPI_RX_DST_REG_64); ++#endif ++ } ++} ++ ++static int mtk_spi_transfer_wait(struct spi_mem *mem, ++ const struct spi_mem_op *op) ++{ ++ struct mtk_spi *mdata = spi_master_get_devdata(mem->spi->master); ++ /* ++ * 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 *= 32; /* prevent we may get 0 for short transfers. */ ++ else ++ ms *= op->data.nbytes; ++ ms = div_u64(ms, mem->spi->max_speed_hz); ++ ms += ms + 1000; /* 1s tolerance */ ++ ++ if (ms > UINT_MAX) ++ ms = UINT_MAX; ++ ++ if (!wait_for_completion_timeout(&mdata->spimem_done, ++ msecs_to_jiffies(ms))) { ++ dev_err(mdata->dev, "spi-mem transfer timeout\n"); ++ return -ETIMEDOUT; ++ } ++ ++ return 0; ++} ++ ++static int mtk_spi_mem_exec_op(struct spi_mem *mem, ++ const struct spi_mem_op *op) ++{ ++ struct mtk_spi *mdata = spi_master_get_devdata(mem->spi->master); ++ u32 reg_val, nio, tx_size; ++ char *tx_tmp_buf, *rx_tmp_buf; ++ int ret = 0; ++ ++ mdata->use_spimem = true; ++ reinit_completion(&mdata->spimem_done); ++ ++ mtk_spi_reset(mdata); ++ mtk_spi_hw_init(mem->spi->master, mem->spi); ++ mtk_spi_prepare_transfer(mem->spi->master, mem->spi->max_speed_hz); ++ ++ reg_val = readl(mdata->base + SPI_CFG3_IPM_REG); ++ /* opcode byte len */ ++ reg_val &= ~SPI_CFG3_IPM_CMD_BYTELEN_MASK; ++ reg_val |= 1 << SPI_CFG3_IPM_CMD_BYTELEN_OFFSET; ++ ++ /* addr & dummy byte len */ ++ reg_val &= ~SPI_CFG3_IPM_ADDR_BYTELEN_MASK; ++ if (op->addr.nbytes || op->dummy.nbytes) ++ reg_val |= (op->addr.nbytes + op->dummy.nbytes) << ++ SPI_CFG3_IPM_ADDR_BYTELEN_OFFSET; ++ ++ /* data byte len */ ++ if (op->data.dir == SPI_MEM_NO_DATA) { ++ reg_val |= SPI_CFG3_IPM_NODATA_FLAG; ++ writel(0, mdata->base + SPI_CFG1_REG); ++ } else { ++ reg_val &= ~SPI_CFG3_IPM_NODATA_FLAG; ++ mdata->xfer_len = op->data.nbytes; ++ mtk_spi_setup_packet(mem->spi->master); ++ } ++ ++ if (op->addr.nbytes || op->dummy.nbytes) { ++ if (op->addr.buswidth == 1 || op->dummy.buswidth == 1) ++ reg_val |= SPI_CFG3_IPM_XMODE_EN; ++ else ++ reg_val &= ~SPI_CFG3_IPM_XMODE_EN; ++ } ++ ++ if (op->addr.buswidth == 2 || ++ op->dummy.buswidth == 2 || ++ op->data.buswidth == 2) ++ nio = 2; ++ else if (op->addr.buswidth == 4 || ++ 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); ++ ++ reg_val |= SPI_CFG3_IPM_HALF_DUPLEX_EN; ++ if (op->data.dir == SPI_MEM_DATA_IN) ++ reg_val |= SPI_CFG3_IPM_HALF_DUPLEX_DIR; ++ else ++ reg_val &= ~SPI_CFG3_IPM_HALF_DUPLEX_DIR; ++ writel(reg_val, mdata->base + SPI_CFG3_IPM_REG); ++ ++ tx_size = 1 + op->addr.nbytes + op->dummy.nbytes; ++ if (op->data.dir == SPI_MEM_DATA_OUT) ++ tx_size += op->data.nbytes; ++ ++ tx_size = max_t(u32, tx_size, 32); ++ ++ tx_tmp_buf = kzalloc(tx_size, GFP_KERNEL | GFP_DMA); ++ if (!tx_tmp_buf) { ++ mdata->use_spimem = false; ++ return -ENOMEM; ++ } ++ ++ tx_tmp_buf[0] = op->cmd.opcode; ++ ++ if (op->addr.nbytes) { ++ int i; ++ ++ for (i = 0; i < op->addr.nbytes; i++) ++ tx_tmp_buf[i + 1] = op->addr.val >> ++ (8 * (op->addr.nbytes - i - 1)); ++ } ++ ++ if (op->dummy.nbytes) ++ memset(tx_tmp_buf + op->addr.nbytes + 1, ++ 0xff, ++ op->dummy.nbytes); ++ ++ if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT) ++ memcpy(tx_tmp_buf + op->dummy.nbytes + op->addr.nbytes + 1, ++ op->data.buf.out, ++ op->data.nbytes); ++ ++ mdata->tx_dma = dma_map_single(mdata->dev, tx_tmp_buf, ++ tx_size, DMA_TO_DEVICE); ++ if (dma_mapping_error(mdata->dev, mdata->tx_dma)) { ++ ret = -ENOMEM; ++ goto err_exit; ++ } ++ ++ 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) { ++ 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, ++ op->data.nbytes, ++ DMA_FROM_DEVICE); ++ if (dma_mapping_error(mdata->dev, mdata->rx_dma)) { ++ ret = -ENOMEM; ++ goto kfree_rx_tmp_buf; ++ } ++ } ++ ++ reg_val = readl(mdata->base + SPI_CMD_REG); ++ reg_val |= SPI_CMD_TX_DMA; ++ if (op->data.dir == SPI_MEM_DATA_IN) ++ reg_val |= SPI_CMD_RX_DMA; ++ writel(reg_val, mdata->base + SPI_CMD_REG); ++ ++ mtk_spi_mem_setup_dma_xfer(mem->spi->master, op); ++ ++ mtk_spi_enable_transfer(mem->spi->master); ++ ++ /* Wait for the interrupt. */ ++ ret = mtk_spi_transfer_wait(mem, op); ++ if (ret) ++ goto unmap_rx_dma; ++ ++ /* spi disable dma */ ++ reg_val = readl(mdata->base + SPI_CMD_REG); ++ reg_val &= ~SPI_CMD_TX_DMA; ++ if (op->data.dir == SPI_MEM_DATA_IN) ++ reg_val &= ~SPI_CMD_RX_DMA; ++ writel(reg_val, mdata->base + SPI_CMD_REG); ++ ++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)) ++ memcpy(op->data.buf.in, rx_tmp_buf, op->data.nbytes); ++ } ++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); ++err_exit: ++ kfree(tx_tmp_buf); ++ mdata->use_spimem = false; ++ ++ return ret; ++} ++ ++static const struct spi_controller_mem_ops mtk_spi_mem_ops = { ++ .adjust_op_size = mtk_spi_mem_adjust_op_size, ++ .supports_op = mtk_spi_mem_supports_op, ++ .exec_op = mtk_spi_mem_exec_op, ++}; ++ + static int mtk_spi_probe(struct platform_device *pdev) + { + struct spi_master *master; +@@ -739,6 +1103,7 @@ static int mtk_spi_probe(struct platform + master->can_dma = mtk_spi_can_dma; + master->setup = mtk_spi_setup; + master->set_cs_timing = mtk_spi_set_hw_cs_timing; ++ master->use_gpio_descriptors = true; + + of_id = of_match_node(mtk_spi_of_match, pdev->dev.of_node); + if (!of_id) { +@@ -755,6 +1120,14 @@ static int mtk_spi_probe(struct platform + + if (mdata->dev_comp->must_tx) + master->flags = SPI_MASTER_MUST_TX; ++ if (mdata->dev_comp->ipm_design) ++ master->mode_bits |= SPI_LOOP; ++ ++ if (mdata->dev_comp->ipm_design) { ++ mdata->dev = &pdev->dev; ++ master->mem_ops = &mtk_spi_mem_ops; ++ init_completion(&mdata->spimem_done); ++ } + + if (mdata->dev_comp->need_pad_sel) { + mdata->pad_num = of_property_count_u32_elems( +@@ -831,25 +1204,40 @@ static int mtk_spi_probe(struct platform + goto err_put_master; + } + ++ mdata->spi_hclk = devm_clk_get_optional(&pdev->dev, "hclk"); ++ if (IS_ERR(mdata->spi_hclk)) { ++ ret = PTR_ERR(mdata->spi_hclk); ++ dev_err(&pdev->dev, "failed to get 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 hclk (%d)\n", ret); ++ goto err_put_master; ++ } ++ + ret = clk_prepare_enable(mdata->spi_clk); + if (ret < 0) { + dev_err(&pdev->dev, "failed to enable spi_clk (%d)\n", ret); +- goto err_put_master; ++ goto err_disable_spi_hclk; + } + + ret = clk_set_parent(mdata->sel_clk, mdata->parent_clk); + if (ret < 0) { + dev_err(&pdev->dev, "failed to clk_set_parent (%d)\n", ret); +- clk_disable_unprepare(mdata->spi_clk); +- goto err_put_master; ++ goto err_disable_spi_clk; + } + + mdata->spi_clk_hz = clk_get_rate(mdata->spi_clk); + +- if (mdata->dev_comp->no_need_unprepare) ++ if (mdata->dev_comp->no_need_unprepare) { + clk_disable(mdata->spi_clk); +- else ++ clk_disable(mdata->spi_hclk); ++ } else { + clk_disable_unprepare(mdata->spi_clk); ++ clk_disable_unprepare(mdata->spi_hclk); ++ } + + pm_runtime_enable(&pdev->dev); + +@@ -862,25 +1250,12 @@ static int mtk_spi_probe(struct platform + goto err_disable_runtime_pm; + } + +- if (!master->cs_gpios && master->num_chipselect > 1) { ++ if (!master->cs_gpiods && master->num_chipselect > 1) { + dev_err(&pdev->dev, + "cs_gpios not specified and num_chipselect > 1\n"); + ret = -EINVAL; + goto err_disable_runtime_pm; + } +- +- 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) +@@ -902,6 +1277,10 @@ static int mtk_spi_probe(struct platform + + err_disable_runtime_pm: + pm_runtime_disable(&pdev->dev); ++err_disable_spi_clk: ++ clk_disable_unprepare(mdata->spi_clk); ++err_disable_spi_hclk: ++ clk_disable_unprepare(mdata->spi_hclk); + err_put_master: + spi_master_put(master); + +@@ -917,8 +1296,10 @@ static int mtk_spi_remove(struct platfor + + mtk_spi_reset(mdata); + +- if (mdata->dev_comp->no_need_unprepare) ++ if (mdata->dev_comp->no_need_unprepare) { + clk_unprepare(mdata->spi_clk); ++ clk_unprepare(mdata->spi_hclk); ++ } + + return 0; + } +@@ -934,8 +1315,10 @@ static int mtk_spi_suspend(struct device + if (ret) + return ret; + +- if (!pm_runtime_suspended(dev)) ++ if (!pm_runtime_suspended(dev)) { + clk_disable_unprepare(mdata->spi_clk); ++ clk_disable_unprepare(mdata->spi_hclk); ++ } + + return ret; + } +@@ -952,11 +1335,20 @@ static int mtk_spi_resume(struct device + 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) ++ if (ret < 0) { + clk_disable_unprepare(mdata->spi_clk); ++ clk_disable_unprepare(mdata->spi_hclk); ++ } + + return ret; + } +@@ -968,10 +1360,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); + +- if (mdata->dev_comp->no_need_unprepare) ++ if (mdata->dev_comp->no_need_unprepare) { + clk_disable(mdata->spi_clk); +- else ++ clk_disable(mdata->spi_hclk); ++ } else { + clk_disable_unprepare(mdata->spi_clk); ++ clk_disable_unprepare(mdata->spi_hclk); ++ } + + return 0; + } +@@ -982,13 +1377,31 @@ static int mtk_spi_runtime_resume(struct + struct mtk_spi *mdata = spi_master_get_devdata(master); + int ret; + +- if (mdata->dev_comp->no_need_unprepare) ++ if (mdata->dev_comp->no_need_unprepare) { + ret = clk_enable(mdata->spi_clk); +- else ++ 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 enable spi_clk (%d)\n", ret); +- return ret; ++ if (ret < 0) { ++ dev_err(dev, "failed to prepare_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;