162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2015 MediaTek Inc. 462306a36Sopenharmony_ci * Author: Leilk Liu <leilk.liu@mediatek.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/clk.h> 862306a36Sopenharmony_ci#include <linux/device.h> 962306a36Sopenharmony_ci#include <linux/err.h> 1062306a36Sopenharmony_ci#include <linux/interrupt.h> 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/ioport.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/of.h> 1562306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci#include <linux/platform_data/spi-mt65xx.h> 1862306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1962306a36Sopenharmony_ci#include <linux/spi/spi.h> 2062306a36Sopenharmony_ci#include <linux/spi/spi-mem.h> 2162306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define SPI_CFG0_REG 0x0000 2462306a36Sopenharmony_ci#define SPI_CFG1_REG 0x0004 2562306a36Sopenharmony_ci#define SPI_TX_SRC_REG 0x0008 2662306a36Sopenharmony_ci#define SPI_RX_DST_REG 0x000c 2762306a36Sopenharmony_ci#define SPI_TX_DATA_REG 0x0010 2862306a36Sopenharmony_ci#define SPI_RX_DATA_REG 0x0014 2962306a36Sopenharmony_ci#define SPI_CMD_REG 0x0018 3062306a36Sopenharmony_ci#define SPI_STATUS0_REG 0x001c 3162306a36Sopenharmony_ci#define SPI_PAD_SEL_REG 0x0024 3262306a36Sopenharmony_ci#define SPI_CFG2_REG 0x0028 3362306a36Sopenharmony_ci#define SPI_TX_SRC_REG_64 0x002c 3462306a36Sopenharmony_ci#define SPI_RX_DST_REG_64 0x0030 3562306a36Sopenharmony_ci#define SPI_CFG3_IPM_REG 0x0040 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define SPI_CFG0_SCK_HIGH_OFFSET 0 3862306a36Sopenharmony_ci#define SPI_CFG0_SCK_LOW_OFFSET 8 3962306a36Sopenharmony_ci#define SPI_CFG0_CS_HOLD_OFFSET 16 4062306a36Sopenharmony_ci#define SPI_CFG0_CS_SETUP_OFFSET 24 4162306a36Sopenharmony_ci#define SPI_ADJUST_CFG0_CS_HOLD_OFFSET 0 4262306a36Sopenharmony_ci#define SPI_ADJUST_CFG0_CS_SETUP_OFFSET 16 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define SPI_CFG1_CS_IDLE_OFFSET 0 4562306a36Sopenharmony_ci#define SPI_CFG1_PACKET_LOOP_OFFSET 8 4662306a36Sopenharmony_ci#define SPI_CFG1_PACKET_LENGTH_OFFSET 16 4762306a36Sopenharmony_ci#define SPI_CFG1_GET_TICK_DLY_OFFSET 29 4862306a36Sopenharmony_ci#define SPI_CFG1_GET_TICK_DLY_OFFSET_V1 30 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define SPI_CFG1_GET_TICK_DLY_MASK 0xe0000000 5162306a36Sopenharmony_ci#define SPI_CFG1_GET_TICK_DLY_MASK_V1 0xc0000000 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define SPI_CFG1_CS_IDLE_MASK 0xff 5462306a36Sopenharmony_ci#define SPI_CFG1_PACKET_LOOP_MASK 0xff00 5562306a36Sopenharmony_ci#define SPI_CFG1_PACKET_LENGTH_MASK 0x3ff0000 5662306a36Sopenharmony_ci#define SPI_CFG1_IPM_PACKET_LENGTH_MASK GENMASK(31, 16) 5762306a36Sopenharmony_ci#define SPI_CFG2_SCK_HIGH_OFFSET 0 5862306a36Sopenharmony_ci#define SPI_CFG2_SCK_LOW_OFFSET 16 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci#define SPI_CMD_ACT BIT(0) 6162306a36Sopenharmony_ci#define SPI_CMD_RESUME BIT(1) 6262306a36Sopenharmony_ci#define SPI_CMD_RST BIT(2) 6362306a36Sopenharmony_ci#define SPI_CMD_PAUSE_EN BIT(4) 6462306a36Sopenharmony_ci#define SPI_CMD_DEASSERT BIT(5) 6562306a36Sopenharmony_ci#define SPI_CMD_SAMPLE_SEL BIT(6) 6662306a36Sopenharmony_ci#define SPI_CMD_CS_POL BIT(7) 6762306a36Sopenharmony_ci#define SPI_CMD_CPHA BIT(8) 6862306a36Sopenharmony_ci#define SPI_CMD_CPOL BIT(9) 6962306a36Sopenharmony_ci#define SPI_CMD_RX_DMA BIT(10) 7062306a36Sopenharmony_ci#define SPI_CMD_TX_DMA BIT(11) 7162306a36Sopenharmony_ci#define SPI_CMD_TXMSBF BIT(12) 7262306a36Sopenharmony_ci#define SPI_CMD_RXMSBF BIT(13) 7362306a36Sopenharmony_ci#define SPI_CMD_RX_ENDIAN BIT(14) 7462306a36Sopenharmony_ci#define SPI_CMD_TX_ENDIAN BIT(15) 7562306a36Sopenharmony_ci#define SPI_CMD_FINISH_IE BIT(16) 7662306a36Sopenharmony_ci#define SPI_CMD_PAUSE_IE BIT(17) 7762306a36Sopenharmony_ci#define SPI_CMD_IPM_NONIDLE_MODE BIT(19) 7862306a36Sopenharmony_ci#define SPI_CMD_IPM_SPIM_LOOP BIT(21) 7962306a36Sopenharmony_ci#define SPI_CMD_IPM_GET_TICKDLY_OFFSET 22 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci#define SPI_CMD_IPM_GET_TICKDLY_MASK GENMASK(24, 22) 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci#define PIN_MODE_CFG(x) ((x) / 2) 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci#define SPI_CFG3_IPM_HALF_DUPLEX_DIR BIT(2) 8662306a36Sopenharmony_ci#define SPI_CFG3_IPM_HALF_DUPLEX_EN BIT(3) 8762306a36Sopenharmony_ci#define SPI_CFG3_IPM_XMODE_EN BIT(4) 8862306a36Sopenharmony_ci#define SPI_CFG3_IPM_NODATA_FLAG BIT(5) 8962306a36Sopenharmony_ci#define SPI_CFG3_IPM_CMD_BYTELEN_OFFSET 8 9062306a36Sopenharmony_ci#define SPI_CFG3_IPM_ADDR_BYTELEN_OFFSET 12 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci#define SPI_CFG3_IPM_CMD_PIN_MODE_MASK GENMASK(1, 0) 9362306a36Sopenharmony_ci#define SPI_CFG3_IPM_CMD_BYTELEN_MASK GENMASK(11, 8) 9462306a36Sopenharmony_ci#define SPI_CFG3_IPM_ADDR_BYTELEN_MASK GENMASK(15, 12) 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci#define MT8173_SPI_MAX_PAD_SEL 3 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci#define MTK_SPI_PAUSE_INT_STATUS 0x2 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci#define MTK_SPI_MAX_FIFO_SIZE 32U 10162306a36Sopenharmony_ci#define MTK_SPI_PACKET_SIZE 1024 10262306a36Sopenharmony_ci#define MTK_SPI_IPM_PACKET_SIZE SZ_64K 10362306a36Sopenharmony_ci#define MTK_SPI_IPM_PACKET_LOOP SZ_256 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci#define MTK_SPI_IDLE 0 10662306a36Sopenharmony_ci#define MTK_SPI_PAUSED 1 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci#define MTK_SPI_32BITS_MASK (0xffffffff) 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci#define DMA_ADDR_EXT_BITS (36) 11162306a36Sopenharmony_ci#define DMA_ADDR_DEF_BITS (32) 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/** 11462306a36Sopenharmony_ci * struct mtk_spi_compatible - device data structure 11562306a36Sopenharmony_ci * @need_pad_sel: Enable pad (pins) selection in SPI controller 11662306a36Sopenharmony_ci * @must_tx: Must explicitly send dummy TX bytes to do RX only transfer 11762306a36Sopenharmony_ci * @enhance_timing: Enable adjusting cfg register to enhance time accuracy 11862306a36Sopenharmony_ci * @dma_ext: DMA address extension supported 11962306a36Sopenharmony_ci * @no_need_unprepare: Don't unprepare the SPI clk during runtime 12062306a36Sopenharmony_ci * @ipm_design: Adjust/extend registers to support IPM design IP features 12162306a36Sopenharmony_ci */ 12262306a36Sopenharmony_cistruct mtk_spi_compatible { 12362306a36Sopenharmony_ci bool need_pad_sel; 12462306a36Sopenharmony_ci bool must_tx; 12562306a36Sopenharmony_ci bool enhance_timing; 12662306a36Sopenharmony_ci bool dma_ext; 12762306a36Sopenharmony_ci bool no_need_unprepare; 12862306a36Sopenharmony_ci bool ipm_design; 12962306a36Sopenharmony_ci}; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci/** 13262306a36Sopenharmony_ci * struct mtk_spi - SPI driver instance 13362306a36Sopenharmony_ci * @base: Start address of the SPI controller registers 13462306a36Sopenharmony_ci * @state: SPI controller state 13562306a36Sopenharmony_ci * @pad_num: Number of pad_sel entries 13662306a36Sopenharmony_ci * @pad_sel: Groups of pins to select 13762306a36Sopenharmony_ci * @parent_clk: Parent of sel_clk 13862306a36Sopenharmony_ci * @sel_clk: SPI master mux clock 13962306a36Sopenharmony_ci * @spi_clk: Peripheral clock 14062306a36Sopenharmony_ci * @spi_hclk: AHB bus clock 14162306a36Sopenharmony_ci * @cur_transfer: Currently processed SPI transfer 14262306a36Sopenharmony_ci * @xfer_len: Number of bytes to transfer 14362306a36Sopenharmony_ci * @num_xfered: Number of transferred bytes 14462306a36Sopenharmony_ci * @tx_sgl: TX transfer scatterlist 14562306a36Sopenharmony_ci * @rx_sgl: RX transfer scatterlist 14662306a36Sopenharmony_ci * @tx_sgl_len: Size of TX DMA transfer 14762306a36Sopenharmony_ci * @rx_sgl_len: Size of RX DMA transfer 14862306a36Sopenharmony_ci * @dev_comp: Device data structure 14962306a36Sopenharmony_ci * @spi_clk_hz: Current SPI clock in Hz 15062306a36Sopenharmony_ci * @spimem_done: SPI-MEM operation completion 15162306a36Sopenharmony_ci * @use_spimem: Enables SPI-MEM 15262306a36Sopenharmony_ci * @dev: Device pointer 15362306a36Sopenharmony_ci * @tx_dma: DMA start for SPI-MEM TX 15462306a36Sopenharmony_ci * @rx_dma: DMA start for SPI-MEM RX 15562306a36Sopenharmony_ci */ 15662306a36Sopenharmony_cistruct mtk_spi { 15762306a36Sopenharmony_ci void __iomem *base; 15862306a36Sopenharmony_ci u32 state; 15962306a36Sopenharmony_ci int pad_num; 16062306a36Sopenharmony_ci u32 *pad_sel; 16162306a36Sopenharmony_ci struct clk *parent_clk, *sel_clk, *spi_clk, *spi_hclk; 16262306a36Sopenharmony_ci struct spi_transfer *cur_transfer; 16362306a36Sopenharmony_ci u32 xfer_len; 16462306a36Sopenharmony_ci u32 num_xfered; 16562306a36Sopenharmony_ci struct scatterlist *tx_sgl, *rx_sgl; 16662306a36Sopenharmony_ci u32 tx_sgl_len, rx_sgl_len; 16762306a36Sopenharmony_ci const struct mtk_spi_compatible *dev_comp; 16862306a36Sopenharmony_ci u32 spi_clk_hz; 16962306a36Sopenharmony_ci struct completion spimem_done; 17062306a36Sopenharmony_ci bool use_spimem; 17162306a36Sopenharmony_ci struct device *dev; 17262306a36Sopenharmony_ci dma_addr_t tx_dma; 17362306a36Sopenharmony_ci dma_addr_t rx_dma; 17462306a36Sopenharmony_ci}; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic const struct mtk_spi_compatible mtk_common_compat; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic const struct mtk_spi_compatible mt2712_compat = { 17962306a36Sopenharmony_ci .must_tx = true, 18062306a36Sopenharmony_ci}; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic const struct mtk_spi_compatible mtk_ipm_compat = { 18362306a36Sopenharmony_ci .enhance_timing = true, 18462306a36Sopenharmony_ci .dma_ext = true, 18562306a36Sopenharmony_ci .ipm_design = true, 18662306a36Sopenharmony_ci}; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic const struct mtk_spi_compatible mt6765_compat = { 18962306a36Sopenharmony_ci .need_pad_sel = true, 19062306a36Sopenharmony_ci .must_tx = true, 19162306a36Sopenharmony_ci .enhance_timing = true, 19262306a36Sopenharmony_ci .dma_ext = true, 19362306a36Sopenharmony_ci}; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic const struct mtk_spi_compatible mt7622_compat = { 19662306a36Sopenharmony_ci .must_tx = true, 19762306a36Sopenharmony_ci .enhance_timing = true, 19862306a36Sopenharmony_ci}; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic const struct mtk_spi_compatible mt8173_compat = { 20162306a36Sopenharmony_ci .need_pad_sel = true, 20262306a36Sopenharmony_ci .must_tx = true, 20362306a36Sopenharmony_ci}; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic const struct mtk_spi_compatible mt8183_compat = { 20662306a36Sopenharmony_ci .need_pad_sel = true, 20762306a36Sopenharmony_ci .must_tx = true, 20862306a36Sopenharmony_ci .enhance_timing = true, 20962306a36Sopenharmony_ci}; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic const struct mtk_spi_compatible mt6893_compat = { 21262306a36Sopenharmony_ci .need_pad_sel = true, 21362306a36Sopenharmony_ci .must_tx = true, 21462306a36Sopenharmony_ci .enhance_timing = true, 21562306a36Sopenharmony_ci .dma_ext = true, 21662306a36Sopenharmony_ci .no_need_unprepare = true, 21762306a36Sopenharmony_ci}; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci/* 22062306a36Sopenharmony_ci * A piece of default chip info unless the platform 22162306a36Sopenharmony_ci * supplies it. 22262306a36Sopenharmony_ci */ 22362306a36Sopenharmony_cistatic const struct mtk_chip_config mtk_default_chip_info = { 22462306a36Sopenharmony_ci .sample_sel = 0, 22562306a36Sopenharmony_ci .tick_delay = 0, 22662306a36Sopenharmony_ci}; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic const struct of_device_id mtk_spi_of_match[] = { 22962306a36Sopenharmony_ci { .compatible = "mediatek,spi-ipm", 23062306a36Sopenharmony_ci .data = (void *)&mtk_ipm_compat, 23162306a36Sopenharmony_ci }, 23262306a36Sopenharmony_ci { .compatible = "mediatek,mt2701-spi", 23362306a36Sopenharmony_ci .data = (void *)&mtk_common_compat, 23462306a36Sopenharmony_ci }, 23562306a36Sopenharmony_ci { .compatible = "mediatek,mt2712-spi", 23662306a36Sopenharmony_ci .data = (void *)&mt2712_compat, 23762306a36Sopenharmony_ci }, 23862306a36Sopenharmony_ci { .compatible = "mediatek,mt6589-spi", 23962306a36Sopenharmony_ci .data = (void *)&mtk_common_compat, 24062306a36Sopenharmony_ci }, 24162306a36Sopenharmony_ci { .compatible = "mediatek,mt6765-spi", 24262306a36Sopenharmony_ci .data = (void *)&mt6765_compat, 24362306a36Sopenharmony_ci }, 24462306a36Sopenharmony_ci { .compatible = "mediatek,mt7622-spi", 24562306a36Sopenharmony_ci .data = (void *)&mt7622_compat, 24662306a36Sopenharmony_ci }, 24762306a36Sopenharmony_ci { .compatible = "mediatek,mt7629-spi", 24862306a36Sopenharmony_ci .data = (void *)&mt7622_compat, 24962306a36Sopenharmony_ci }, 25062306a36Sopenharmony_ci { .compatible = "mediatek,mt8135-spi", 25162306a36Sopenharmony_ci .data = (void *)&mtk_common_compat, 25262306a36Sopenharmony_ci }, 25362306a36Sopenharmony_ci { .compatible = "mediatek,mt8173-spi", 25462306a36Sopenharmony_ci .data = (void *)&mt8173_compat, 25562306a36Sopenharmony_ci }, 25662306a36Sopenharmony_ci { .compatible = "mediatek,mt8183-spi", 25762306a36Sopenharmony_ci .data = (void *)&mt8183_compat, 25862306a36Sopenharmony_ci }, 25962306a36Sopenharmony_ci { .compatible = "mediatek,mt8192-spi", 26062306a36Sopenharmony_ci .data = (void *)&mt6765_compat, 26162306a36Sopenharmony_ci }, 26262306a36Sopenharmony_ci { .compatible = "mediatek,mt6893-spi", 26362306a36Sopenharmony_ci .data = (void *)&mt6893_compat, 26462306a36Sopenharmony_ci }, 26562306a36Sopenharmony_ci {} 26662306a36Sopenharmony_ci}; 26762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mtk_spi_of_match); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic void mtk_spi_reset(struct mtk_spi *mdata) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci u32 reg_val; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* set the software reset bit in SPI_CMD_REG. */ 27462306a36Sopenharmony_ci reg_val = readl(mdata->base + SPI_CMD_REG); 27562306a36Sopenharmony_ci reg_val |= SPI_CMD_RST; 27662306a36Sopenharmony_ci writel(reg_val, mdata->base + SPI_CMD_REG); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci reg_val = readl(mdata->base + SPI_CMD_REG); 27962306a36Sopenharmony_ci reg_val &= ~SPI_CMD_RST; 28062306a36Sopenharmony_ci writel(reg_val, mdata->base + SPI_CMD_REG); 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic int mtk_spi_set_hw_cs_timing(struct spi_device *spi) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci struct mtk_spi *mdata = spi_master_get_devdata(spi->master); 28662306a36Sopenharmony_ci struct spi_delay *cs_setup = &spi->cs_setup; 28762306a36Sopenharmony_ci struct spi_delay *cs_hold = &spi->cs_hold; 28862306a36Sopenharmony_ci struct spi_delay *cs_inactive = &spi->cs_inactive; 28962306a36Sopenharmony_ci u32 setup, hold, inactive; 29062306a36Sopenharmony_ci u32 reg_val; 29162306a36Sopenharmony_ci int delay; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci delay = spi_delay_to_ns(cs_setup, NULL); 29462306a36Sopenharmony_ci if (delay < 0) 29562306a36Sopenharmony_ci return delay; 29662306a36Sopenharmony_ci setup = (delay * DIV_ROUND_UP(mdata->spi_clk_hz, 1000000)) / 1000; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci delay = spi_delay_to_ns(cs_hold, NULL); 29962306a36Sopenharmony_ci if (delay < 0) 30062306a36Sopenharmony_ci return delay; 30162306a36Sopenharmony_ci hold = (delay * DIV_ROUND_UP(mdata->spi_clk_hz, 1000000)) / 1000; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci delay = spi_delay_to_ns(cs_inactive, NULL); 30462306a36Sopenharmony_ci if (delay < 0) 30562306a36Sopenharmony_ci return delay; 30662306a36Sopenharmony_ci inactive = (delay * DIV_ROUND_UP(mdata->spi_clk_hz, 1000000)) / 1000; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci if (hold || setup) { 30962306a36Sopenharmony_ci reg_val = readl(mdata->base + SPI_CFG0_REG); 31062306a36Sopenharmony_ci if (mdata->dev_comp->enhance_timing) { 31162306a36Sopenharmony_ci if (hold) { 31262306a36Sopenharmony_ci hold = min_t(u32, hold, 0x10000); 31362306a36Sopenharmony_ci reg_val &= ~(0xffff << SPI_ADJUST_CFG0_CS_HOLD_OFFSET); 31462306a36Sopenharmony_ci reg_val |= (((hold - 1) & 0xffff) 31562306a36Sopenharmony_ci << SPI_ADJUST_CFG0_CS_HOLD_OFFSET); 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci if (setup) { 31862306a36Sopenharmony_ci setup = min_t(u32, setup, 0x10000); 31962306a36Sopenharmony_ci reg_val &= ~(0xffff << SPI_ADJUST_CFG0_CS_SETUP_OFFSET); 32062306a36Sopenharmony_ci reg_val |= (((setup - 1) & 0xffff) 32162306a36Sopenharmony_ci << SPI_ADJUST_CFG0_CS_SETUP_OFFSET); 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci } else { 32462306a36Sopenharmony_ci if (hold) { 32562306a36Sopenharmony_ci hold = min_t(u32, hold, 0x100); 32662306a36Sopenharmony_ci reg_val &= ~(0xff << SPI_CFG0_CS_HOLD_OFFSET); 32762306a36Sopenharmony_ci reg_val |= (((hold - 1) & 0xff) << SPI_CFG0_CS_HOLD_OFFSET); 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci if (setup) { 33062306a36Sopenharmony_ci setup = min_t(u32, setup, 0x100); 33162306a36Sopenharmony_ci reg_val &= ~(0xff << SPI_CFG0_CS_SETUP_OFFSET); 33262306a36Sopenharmony_ci reg_val |= (((setup - 1) & 0xff) 33362306a36Sopenharmony_ci << SPI_CFG0_CS_SETUP_OFFSET); 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci writel(reg_val, mdata->base + SPI_CFG0_REG); 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (inactive) { 34062306a36Sopenharmony_ci inactive = min_t(u32, inactive, 0x100); 34162306a36Sopenharmony_ci reg_val = readl(mdata->base + SPI_CFG1_REG); 34262306a36Sopenharmony_ci reg_val &= ~SPI_CFG1_CS_IDLE_MASK; 34362306a36Sopenharmony_ci reg_val |= (((inactive - 1) & 0xff) << SPI_CFG1_CS_IDLE_OFFSET); 34462306a36Sopenharmony_ci writel(reg_val, mdata->base + SPI_CFG1_REG); 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci return 0; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic int mtk_spi_hw_init(struct spi_master *master, 35162306a36Sopenharmony_ci struct spi_device *spi) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci u16 cpha, cpol; 35462306a36Sopenharmony_ci u32 reg_val; 35562306a36Sopenharmony_ci struct mtk_chip_config *chip_config = spi->controller_data; 35662306a36Sopenharmony_ci struct mtk_spi *mdata = spi_master_get_devdata(master); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci cpha = spi->mode & SPI_CPHA ? 1 : 0; 35962306a36Sopenharmony_ci cpol = spi->mode & SPI_CPOL ? 1 : 0; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci reg_val = readl(mdata->base + SPI_CMD_REG); 36262306a36Sopenharmony_ci if (mdata->dev_comp->ipm_design) { 36362306a36Sopenharmony_ci /* SPI transfer without idle time until packet length done */ 36462306a36Sopenharmony_ci reg_val |= SPI_CMD_IPM_NONIDLE_MODE; 36562306a36Sopenharmony_ci if (spi->mode & SPI_LOOP) 36662306a36Sopenharmony_ci reg_val |= SPI_CMD_IPM_SPIM_LOOP; 36762306a36Sopenharmony_ci else 36862306a36Sopenharmony_ci reg_val &= ~SPI_CMD_IPM_SPIM_LOOP; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (cpha) 37262306a36Sopenharmony_ci reg_val |= SPI_CMD_CPHA; 37362306a36Sopenharmony_ci else 37462306a36Sopenharmony_ci reg_val &= ~SPI_CMD_CPHA; 37562306a36Sopenharmony_ci if (cpol) 37662306a36Sopenharmony_ci reg_val |= SPI_CMD_CPOL; 37762306a36Sopenharmony_ci else 37862306a36Sopenharmony_ci reg_val &= ~SPI_CMD_CPOL; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci /* set the mlsbx and mlsbtx */ 38162306a36Sopenharmony_ci if (spi->mode & SPI_LSB_FIRST) { 38262306a36Sopenharmony_ci reg_val &= ~SPI_CMD_TXMSBF; 38362306a36Sopenharmony_ci reg_val &= ~SPI_CMD_RXMSBF; 38462306a36Sopenharmony_ci } else { 38562306a36Sopenharmony_ci reg_val |= SPI_CMD_TXMSBF; 38662306a36Sopenharmony_ci reg_val |= SPI_CMD_RXMSBF; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci /* set the tx/rx endian */ 39062306a36Sopenharmony_ci#ifdef __LITTLE_ENDIAN 39162306a36Sopenharmony_ci reg_val &= ~SPI_CMD_TX_ENDIAN; 39262306a36Sopenharmony_ci reg_val &= ~SPI_CMD_RX_ENDIAN; 39362306a36Sopenharmony_ci#else 39462306a36Sopenharmony_ci reg_val |= SPI_CMD_TX_ENDIAN; 39562306a36Sopenharmony_ci reg_val |= SPI_CMD_RX_ENDIAN; 39662306a36Sopenharmony_ci#endif 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci if (mdata->dev_comp->enhance_timing) { 39962306a36Sopenharmony_ci /* set CS polarity */ 40062306a36Sopenharmony_ci if (spi->mode & SPI_CS_HIGH) 40162306a36Sopenharmony_ci reg_val |= SPI_CMD_CS_POL; 40262306a36Sopenharmony_ci else 40362306a36Sopenharmony_ci reg_val &= ~SPI_CMD_CS_POL; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (chip_config->sample_sel) 40662306a36Sopenharmony_ci reg_val |= SPI_CMD_SAMPLE_SEL; 40762306a36Sopenharmony_ci else 40862306a36Sopenharmony_ci reg_val &= ~SPI_CMD_SAMPLE_SEL; 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci /* set finish and pause interrupt always enable */ 41262306a36Sopenharmony_ci reg_val |= SPI_CMD_FINISH_IE | SPI_CMD_PAUSE_IE; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci /* disable dma mode */ 41562306a36Sopenharmony_ci reg_val &= ~(SPI_CMD_TX_DMA | SPI_CMD_RX_DMA); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci /* disable deassert mode */ 41862306a36Sopenharmony_ci reg_val &= ~SPI_CMD_DEASSERT; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci writel(reg_val, mdata->base + SPI_CMD_REG); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci /* pad select */ 42362306a36Sopenharmony_ci if (mdata->dev_comp->need_pad_sel) 42462306a36Sopenharmony_ci writel(mdata->pad_sel[spi_get_chipselect(spi, 0)], 42562306a36Sopenharmony_ci mdata->base + SPI_PAD_SEL_REG); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* tick delay */ 42862306a36Sopenharmony_ci if (mdata->dev_comp->enhance_timing) { 42962306a36Sopenharmony_ci if (mdata->dev_comp->ipm_design) { 43062306a36Sopenharmony_ci reg_val = readl(mdata->base + SPI_CMD_REG); 43162306a36Sopenharmony_ci reg_val &= ~SPI_CMD_IPM_GET_TICKDLY_MASK; 43262306a36Sopenharmony_ci reg_val |= ((chip_config->tick_delay & 0x7) 43362306a36Sopenharmony_ci << SPI_CMD_IPM_GET_TICKDLY_OFFSET); 43462306a36Sopenharmony_ci writel(reg_val, mdata->base + SPI_CMD_REG); 43562306a36Sopenharmony_ci } else { 43662306a36Sopenharmony_ci reg_val = readl(mdata->base + SPI_CFG1_REG); 43762306a36Sopenharmony_ci reg_val &= ~SPI_CFG1_GET_TICK_DLY_MASK; 43862306a36Sopenharmony_ci reg_val |= ((chip_config->tick_delay & 0x7) 43962306a36Sopenharmony_ci << SPI_CFG1_GET_TICK_DLY_OFFSET); 44062306a36Sopenharmony_ci writel(reg_val, mdata->base + SPI_CFG1_REG); 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci } else { 44362306a36Sopenharmony_ci reg_val = readl(mdata->base + SPI_CFG1_REG); 44462306a36Sopenharmony_ci reg_val &= ~SPI_CFG1_GET_TICK_DLY_MASK_V1; 44562306a36Sopenharmony_ci reg_val |= ((chip_config->tick_delay & 0x3) 44662306a36Sopenharmony_ci << SPI_CFG1_GET_TICK_DLY_OFFSET_V1); 44762306a36Sopenharmony_ci writel(reg_val, mdata->base + SPI_CFG1_REG); 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci /* set hw cs timing */ 45162306a36Sopenharmony_ci mtk_spi_set_hw_cs_timing(spi); 45262306a36Sopenharmony_ci return 0; 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic int mtk_spi_prepare_message(struct spi_master *master, 45662306a36Sopenharmony_ci struct spi_message *msg) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci return mtk_spi_hw_init(master, msg->spi); 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cistatic void mtk_spi_set_cs(struct spi_device *spi, bool enable) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci u32 reg_val; 46462306a36Sopenharmony_ci struct mtk_spi *mdata = spi_master_get_devdata(spi->master); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (spi->mode & SPI_CS_HIGH) 46762306a36Sopenharmony_ci enable = !enable; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci reg_val = readl(mdata->base + SPI_CMD_REG); 47062306a36Sopenharmony_ci if (!enable) { 47162306a36Sopenharmony_ci reg_val |= SPI_CMD_PAUSE_EN; 47262306a36Sopenharmony_ci writel(reg_val, mdata->base + SPI_CMD_REG); 47362306a36Sopenharmony_ci } else { 47462306a36Sopenharmony_ci reg_val &= ~SPI_CMD_PAUSE_EN; 47562306a36Sopenharmony_ci writel(reg_val, mdata->base + SPI_CMD_REG); 47662306a36Sopenharmony_ci mdata->state = MTK_SPI_IDLE; 47762306a36Sopenharmony_ci mtk_spi_reset(mdata); 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic void mtk_spi_prepare_transfer(struct spi_master *master, 48262306a36Sopenharmony_ci u32 speed_hz) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci u32 div, sck_time, reg_val; 48562306a36Sopenharmony_ci struct mtk_spi *mdata = spi_master_get_devdata(master); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci if (speed_hz < mdata->spi_clk_hz / 2) 48862306a36Sopenharmony_ci div = DIV_ROUND_UP(mdata->spi_clk_hz, speed_hz); 48962306a36Sopenharmony_ci else 49062306a36Sopenharmony_ci div = 1; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci sck_time = (div + 1) / 2; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (mdata->dev_comp->enhance_timing) { 49562306a36Sopenharmony_ci reg_val = readl(mdata->base + SPI_CFG2_REG); 49662306a36Sopenharmony_ci reg_val &= ~(0xffff << SPI_CFG2_SCK_HIGH_OFFSET); 49762306a36Sopenharmony_ci reg_val |= (((sck_time - 1) & 0xffff) 49862306a36Sopenharmony_ci << SPI_CFG2_SCK_HIGH_OFFSET); 49962306a36Sopenharmony_ci reg_val &= ~(0xffff << SPI_CFG2_SCK_LOW_OFFSET); 50062306a36Sopenharmony_ci reg_val |= (((sck_time - 1) & 0xffff) 50162306a36Sopenharmony_ci << SPI_CFG2_SCK_LOW_OFFSET); 50262306a36Sopenharmony_ci writel(reg_val, mdata->base + SPI_CFG2_REG); 50362306a36Sopenharmony_ci } else { 50462306a36Sopenharmony_ci reg_val = readl(mdata->base + SPI_CFG0_REG); 50562306a36Sopenharmony_ci reg_val &= ~(0xff << SPI_CFG0_SCK_HIGH_OFFSET); 50662306a36Sopenharmony_ci reg_val |= (((sck_time - 1) & 0xff) 50762306a36Sopenharmony_ci << SPI_CFG0_SCK_HIGH_OFFSET); 50862306a36Sopenharmony_ci reg_val &= ~(0xff << SPI_CFG0_SCK_LOW_OFFSET); 50962306a36Sopenharmony_ci reg_val |= (((sck_time - 1) & 0xff) << SPI_CFG0_SCK_LOW_OFFSET); 51062306a36Sopenharmony_ci writel(reg_val, mdata->base + SPI_CFG0_REG); 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic void mtk_spi_setup_packet(struct spi_master *master) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci u32 packet_size, packet_loop, reg_val; 51762306a36Sopenharmony_ci struct mtk_spi *mdata = spi_master_get_devdata(master); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci if (mdata->dev_comp->ipm_design) 52062306a36Sopenharmony_ci packet_size = min_t(u32, 52162306a36Sopenharmony_ci mdata->xfer_len, 52262306a36Sopenharmony_ci MTK_SPI_IPM_PACKET_SIZE); 52362306a36Sopenharmony_ci else 52462306a36Sopenharmony_ci packet_size = min_t(u32, 52562306a36Sopenharmony_ci mdata->xfer_len, 52662306a36Sopenharmony_ci MTK_SPI_PACKET_SIZE); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci packet_loop = mdata->xfer_len / packet_size; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci reg_val = readl(mdata->base + SPI_CFG1_REG); 53162306a36Sopenharmony_ci if (mdata->dev_comp->ipm_design) 53262306a36Sopenharmony_ci reg_val &= ~SPI_CFG1_IPM_PACKET_LENGTH_MASK; 53362306a36Sopenharmony_ci else 53462306a36Sopenharmony_ci reg_val &= ~SPI_CFG1_PACKET_LENGTH_MASK; 53562306a36Sopenharmony_ci reg_val |= (packet_size - 1) << SPI_CFG1_PACKET_LENGTH_OFFSET; 53662306a36Sopenharmony_ci reg_val &= ~SPI_CFG1_PACKET_LOOP_MASK; 53762306a36Sopenharmony_ci reg_val |= (packet_loop - 1) << SPI_CFG1_PACKET_LOOP_OFFSET; 53862306a36Sopenharmony_ci writel(reg_val, mdata->base + SPI_CFG1_REG); 53962306a36Sopenharmony_ci} 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cistatic void mtk_spi_enable_transfer(struct spi_master *master) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci u32 cmd; 54462306a36Sopenharmony_ci struct mtk_spi *mdata = spi_master_get_devdata(master); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci cmd = readl(mdata->base + SPI_CMD_REG); 54762306a36Sopenharmony_ci if (mdata->state == MTK_SPI_IDLE) 54862306a36Sopenharmony_ci cmd |= SPI_CMD_ACT; 54962306a36Sopenharmony_ci else 55062306a36Sopenharmony_ci cmd |= SPI_CMD_RESUME; 55162306a36Sopenharmony_ci writel(cmd, mdata->base + SPI_CMD_REG); 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_cistatic int mtk_spi_get_mult_delta(struct mtk_spi *mdata, u32 xfer_len) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci u32 mult_delta = 0; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (mdata->dev_comp->ipm_design) { 55962306a36Sopenharmony_ci if (xfer_len > MTK_SPI_IPM_PACKET_SIZE) 56062306a36Sopenharmony_ci mult_delta = xfer_len % MTK_SPI_IPM_PACKET_SIZE; 56162306a36Sopenharmony_ci } else { 56262306a36Sopenharmony_ci if (xfer_len > MTK_SPI_PACKET_SIZE) 56362306a36Sopenharmony_ci mult_delta = xfer_len % MTK_SPI_PACKET_SIZE; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci return mult_delta; 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_cistatic void mtk_spi_update_mdata_len(struct spi_master *master) 57062306a36Sopenharmony_ci{ 57162306a36Sopenharmony_ci int mult_delta; 57262306a36Sopenharmony_ci struct mtk_spi *mdata = spi_master_get_devdata(master); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci if (mdata->tx_sgl_len && mdata->rx_sgl_len) { 57562306a36Sopenharmony_ci if (mdata->tx_sgl_len > mdata->rx_sgl_len) { 57662306a36Sopenharmony_ci mult_delta = mtk_spi_get_mult_delta(mdata, mdata->rx_sgl_len); 57762306a36Sopenharmony_ci mdata->xfer_len = mdata->rx_sgl_len - mult_delta; 57862306a36Sopenharmony_ci mdata->rx_sgl_len = mult_delta; 57962306a36Sopenharmony_ci mdata->tx_sgl_len -= mdata->xfer_len; 58062306a36Sopenharmony_ci } else { 58162306a36Sopenharmony_ci mult_delta = mtk_spi_get_mult_delta(mdata, mdata->tx_sgl_len); 58262306a36Sopenharmony_ci mdata->xfer_len = mdata->tx_sgl_len - mult_delta; 58362306a36Sopenharmony_ci mdata->tx_sgl_len = mult_delta; 58462306a36Sopenharmony_ci mdata->rx_sgl_len -= mdata->xfer_len; 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci } else if (mdata->tx_sgl_len) { 58762306a36Sopenharmony_ci mult_delta = mtk_spi_get_mult_delta(mdata, mdata->tx_sgl_len); 58862306a36Sopenharmony_ci mdata->xfer_len = mdata->tx_sgl_len - mult_delta; 58962306a36Sopenharmony_ci mdata->tx_sgl_len = mult_delta; 59062306a36Sopenharmony_ci } else if (mdata->rx_sgl_len) { 59162306a36Sopenharmony_ci mult_delta = mtk_spi_get_mult_delta(mdata, mdata->rx_sgl_len); 59262306a36Sopenharmony_ci mdata->xfer_len = mdata->rx_sgl_len - mult_delta; 59362306a36Sopenharmony_ci mdata->rx_sgl_len = mult_delta; 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci} 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_cistatic void mtk_spi_setup_dma_addr(struct spi_master *master, 59862306a36Sopenharmony_ci struct spi_transfer *xfer) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci struct mtk_spi *mdata = spi_master_get_devdata(master); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci if (mdata->tx_sgl) { 60362306a36Sopenharmony_ci writel((u32)(xfer->tx_dma & MTK_SPI_32BITS_MASK), 60462306a36Sopenharmony_ci mdata->base + SPI_TX_SRC_REG); 60562306a36Sopenharmony_ci#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT 60662306a36Sopenharmony_ci if (mdata->dev_comp->dma_ext) 60762306a36Sopenharmony_ci writel((u32)(xfer->tx_dma >> 32), 60862306a36Sopenharmony_ci mdata->base + SPI_TX_SRC_REG_64); 60962306a36Sopenharmony_ci#endif 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci if (mdata->rx_sgl) { 61362306a36Sopenharmony_ci writel((u32)(xfer->rx_dma & MTK_SPI_32BITS_MASK), 61462306a36Sopenharmony_ci mdata->base + SPI_RX_DST_REG); 61562306a36Sopenharmony_ci#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT 61662306a36Sopenharmony_ci if (mdata->dev_comp->dma_ext) 61762306a36Sopenharmony_ci writel((u32)(xfer->rx_dma >> 32), 61862306a36Sopenharmony_ci mdata->base + SPI_RX_DST_REG_64); 61962306a36Sopenharmony_ci#endif 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cistatic int mtk_spi_fifo_transfer(struct spi_master *master, 62462306a36Sopenharmony_ci struct spi_device *spi, 62562306a36Sopenharmony_ci struct spi_transfer *xfer) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci int cnt, remainder; 62862306a36Sopenharmony_ci u32 reg_val; 62962306a36Sopenharmony_ci struct mtk_spi *mdata = spi_master_get_devdata(master); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci mdata->cur_transfer = xfer; 63262306a36Sopenharmony_ci mdata->xfer_len = min(MTK_SPI_MAX_FIFO_SIZE, xfer->len); 63362306a36Sopenharmony_ci mdata->num_xfered = 0; 63462306a36Sopenharmony_ci mtk_spi_prepare_transfer(master, xfer->speed_hz); 63562306a36Sopenharmony_ci mtk_spi_setup_packet(master); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci if (xfer->tx_buf) { 63862306a36Sopenharmony_ci cnt = xfer->len / 4; 63962306a36Sopenharmony_ci iowrite32_rep(mdata->base + SPI_TX_DATA_REG, xfer->tx_buf, cnt); 64062306a36Sopenharmony_ci remainder = xfer->len % 4; 64162306a36Sopenharmony_ci if (remainder > 0) { 64262306a36Sopenharmony_ci reg_val = 0; 64362306a36Sopenharmony_ci memcpy(®_val, xfer->tx_buf + (cnt * 4), remainder); 64462306a36Sopenharmony_ci writel(reg_val, mdata->base + SPI_TX_DATA_REG); 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci mtk_spi_enable_transfer(master); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci return 1; 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_cistatic int mtk_spi_dma_transfer(struct spi_master *master, 65462306a36Sopenharmony_ci struct spi_device *spi, 65562306a36Sopenharmony_ci struct spi_transfer *xfer) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci int cmd; 65862306a36Sopenharmony_ci struct mtk_spi *mdata = spi_master_get_devdata(master); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci mdata->tx_sgl = NULL; 66162306a36Sopenharmony_ci mdata->rx_sgl = NULL; 66262306a36Sopenharmony_ci mdata->tx_sgl_len = 0; 66362306a36Sopenharmony_ci mdata->rx_sgl_len = 0; 66462306a36Sopenharmony_ci mdata->cur_transfer = xfer; 66562306a36Sopenharmony_ci mdata->num_xfered = 0; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci mtk_spi_prepare_transfer(master, xfer->speed_hz); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci cmd = readl(mdata->base + SPI_CMD_REG); 67062306a36Sopenharmony_ci if (xfer->tx_buf) 67162306a36Sopenharmony_ci cmd |= SPI_CMD_TX_DMA; 67262306a36Sopenharmony_ci if (xfer->rx_buf) 67362306a36Sopenharmony_ci cmd |= SPI_CMD_RX_DMA; 67462306a36Sopenharmony_ci writel(cmd, mdata->base + SPI_CMD_REG); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci if (xfer->tx_buf) 67762306a36Sopenharmony_ci mdata->tx_sgl = xfer->tx_sg.sgl; 67862306a36Sopenharmony_ci if (xfer->rx_buf) 67962306a36Sopenharmony_ci mdata->rx_sgl = xfer->rx_sg.sgl; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci if (mdata->tx_sgl) { 68262306a36Sopenharmony_ci xfer->tx_dma = sg_dma_address(mdata->tx_sgl); 68362306a36Sopenharmony_ci mdata->tx_sgl_len = sg_dma_len(mdata->tx_sgl); 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci if (mdata->rx_sgl) { 68662306a36Sopenharmony_ci xfer->rx_dma = sg_dma_address(mdata->rx_sgl); 68762306a36Sopenharmony_ci mdata->rx_sgl_len = sg_dma_len(mdata->rx_sgl); 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci mtk_spi_update_mdata_len(master); 69162306a36Sopenharmony_ci mtk_spi_setup_packet(master); 69262306a36Sopenharmony_ci mtk_spi_setup_dma_addr(master, xfer); 69362306a36Sopenharmony_ci mtk_spi_enable_transfer(master); 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci return 1; 69662306a36Sopenharmony_ci} 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_cistatic int mtk_spi_transfer_one(struct spi_master *master, 69962306a36Sopenharmony_ci struct spi_device *spi, 70062306a36Sopenharmony_ci struct spi_transfer *xfer) 70162306a36Sopenharmony_ci{ 70262306a36Sopenharmony_ci struct mtk_spi *mdata = spi_master_get_devdata(spi->master); 70362306a36Sopenharmony_ci u32 reg_val = 0; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci /* prepare xfer direction and duplex mode */ 70662306a36Sopenharmony_ci if (mdata->dev_comp->ipm_design) { 70762306a36Sopenharmony_ci if (!xfer->tx_buf || !xfer->rx_buf) { 70862306a36Sopenharmony_ci reg_val |= SPI_CFG3_IPM_HALF_DUPLEX_EN; 70962306a36Sopenharmony_ci if (xfer->rx_buf) 71062306a36Sopenharmony_ci reg_val |= SPI_CFG3_IPM_HALF_DUPLEX_DIR; 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci writel(reg_val, mdata->base + SPI_CFG3_IPM_REG); 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci if (master->can_dma(master, spi, xfer)) 71662306a36Sopenharmony_ci return mtk_spi_dma_transfer(master, spi, xfer); 71762306a36Sopenharmony_ci else 71862306a36Sopenharmony_ci return mtk_spi_fifo_transfer(master, spi, xfer); 71962306a36Sopenharmony_ci} 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_cistatic bool mtk_spi_can_dma(struct spi_master *master, 72262306a36Sopenharmony_ci struct spi_device *spi, 72362306a36Sopenharmony_ci struct spi_transfer *xfer) 72462306a36Sopenharmony_ci{ 72562306a36Sopenharmony_ci /* Buffers for DMA transactions must be 4-byte aligned */ 72662306a36Sopenharmony_ci return (xfer->len > MTK_SPI_MAX_FIFO_SIZE && 72762306a36Sopenharmony_ci (unsigned long)xfer->tx_buf % 4 == 0 && 72862306a36Sopenharmony_ci (unsigned long)xfer->rx_buf % 4 == 0); 72962306a36Sopenharmony_ci} 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_cistatic int mtk_spi_setup(struct spi_device *spi) 73262306a36Sopenharmony_ci{ 73362306a36Sopenharmony_ci struct mtk_spi *mdata = spi_master_get_devdata(spi->master); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci if (!spi->controller_data) 73662306a36Sopenharmony_ci spi->controller_data = (void *)&mtk_default_chip_info; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci if (mdata->dev_comp->need_pad_sel && spi_get_csgpiod(spi, 0)) 73962306a36Sopenharmony_ci /* CS de-asserted, gpiolib will handle inversion */ 74062306a36Sopenharmony_ci gpiod_direction_output(spi_get_csgpiod(spi, 0), 0); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci return 0; 74362306a36Sopenharmony_ci} 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_cistatic irqreturn_t mtk_spi_interrupt(int irq, void *dev_id) 74662306a36Sopenharmony_ci{ 74762306a36Sopenharmony_ci u32 cmd, reg_val, cnt, remainder, len; 74862306a36Sopenharmony_ci struct spi_master *master = dev_id; 74962306a36Sopenharmony_ci struct mtk_spi *mdata = spi_master_get_devdata(master); 75062306a36Sopenharmony_ci struct spi_transfer *trans = mdata->cur_transfer; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci reg_val = readl(mdata->base + SPI_STATUS0_REG); 75362306a36Sopenharmony_ci if (reg_val & MTK_SPI_PAUSE_INT_STATUS) 75462306a36Sopenharmony_ci mdata->state = MTK_SPI_PAUSED; 75562306a36Sopenharmony_ci else 75662306a36Sopenharmony_ci mdata->state = MTK_SPI_IDLE; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci /* SPI-MEM ops */ 75962306a36Sopenharmony_ci if (mdata->use_spimem) { 76062306a36Sopenharmony_ci complete(&mdata->spimem_done); 76162306a36Sopenharmony_ci return IRQ_HANDLED; 76262306a36Sopenharmony_ci } 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci if (!master->can_dma(master, NULL, trans)) { 76562306a36Sopenharmony_ci if (trans->rx_buf) { 76662306a36Sopenharmony_ci cnt = mdata->xfer_len / 4; 76762306a36Sopenharmony_ci ioread32_rep(mdata->base + SPI_RX_DATA_REG, 76862306a36Sopenharmony_ci trans->rx_buf + mdata->num_xfered, cnt); 76962306a36Sopenharmony_ci remainder = mdata->xfer_len % 4; 77062306a36Sopenharmony_ci if (remainder > 0) { 77162306a36Sopenharmony_ci reg_val = readl(mdata->base + SPI_RX_DATA_REG); 77262306a36Sopenharmony_ci memcpy(trans->rx_buf + 77362306a36Sopenharmony_ci mdata->num_xfered + 77462306a36Sopenharmony_ci (cnt * 4), 77562306a36Sopenharmony_ci ®_val, 77662306a36Sopenharmony_ci remainder); 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci mdata->num_xfered += mdata->xfer_len; 78162306a36Sopenharmony_ci if (mdata->num_xfered == trans->len) { 78262306a36Sopenharmony_ci spi_finalize_current_transfer(master); 78362306a36Sopenharmony_ci return IRQ_HANDLED; 78462306a36Sopenharmony_ci } 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci len = trans->len - mdata->num_xfered; 78762306a36Sopenharmony_ci mdata->xfer_len = min(MTK_SPI_MAX_FIFO_SIZE, len); 78862306a36Sopenharmony_ci mtk_spi_setup_packet(master); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci if (trans->tx_buf) { 79162306a36Sopenharmony_ci cnt = mdata->xfer_len / 4; 79262306a36Sopenharmony_ci iowrite32_rep(mdata->base + SPI_TX_DATA_REG, 79362306a36Sopenharmony_ci trans->tx_buf + mdata->num_xfered, cnt); 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci remainder = mdata->xfer_len % 4; 79662306a36Sopenharmony_ci if (remainder > 0) { 79762306a36Sopenharmony_ci reg_val = 0; 79862306a36Sopenharmony_ci memcpy(®_val, 79962306a36Sopenharmony_ci trans->tx_buf + (cnt * 4) + mdata->num_xfered, 80062306a36Sopenharmony_ci remainder); 80162306a36Sopenharmony_ci writel(reg_val, mdata->base + SPI_TX_DATA_REG); 80262306a36Sopenharmony_ci } 80362306a36Sopenharmony_ci } 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci mtk_spi_enable_transfer(master); 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci return IRQ_HANDLED; 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci if (mdata->tx_sgl) 81162306a36Sopenharmony_ci trans->tx_dma += mdata->xfer_len; 81262306a36Sopenharmony_ci if (mdata->rx_sgl) 81362306a36Sopenharmony_ci trans->rx_dma += mdata->xfer_len; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci if (mdata->tx_sgl && (mdata->tx_sgl_len == 0)) { 81662306a36Sopenharmony_ci mdata->tx_sgl = sg_next(mdata->tx_sgl); 81762306a36Sopenharmony_ci if (mdata->tx_sgl) { 81862306a36Sopenharmony_ci trans->tx_dma = sg_dma_address(mdata->tx_sgl); 81962306a36Sopenharmony_ci mdata->tx_sgl_len = sg_dma_len(mdata->tx_sgl); 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci if (mdata->rx_sgl && (mdata->rx_sgl_len == 0)) { 82362306a36Sopenharmony_ci mdata->rx_sgl = sg_next(mdata->rx_sgl); 82462306a36Sopenharmony_ci if (mdata->rx_sgl) { 82562306a36Sopenharmony_ci trans->rx_dma = sg_dma_address(mdata->rx_sgl); 82662306a36Sopenharmony_ci mdata->rx_sgl_len = sg_dma_len(mdata->rx_sgl); 82762306a36Sopenharmony_ci } 82862306a36Sopenharmony_ci } 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci if (!mdata->tx_sgl && !mdata->rx_sgl) { 83162306a36Sopenharmony_ci /* spi disable dma */ 83262306a36Sopenharmony_ci cmd = readl(mdata->base + SPI_CMD_REG); 83362306a36Sopenharmony_ci cmd &= ~SPI_CMD_TX_DMA; 83462306a36Sopenharmony_ci cmd &= ~SPI_CMD_RX_DMA; 83562306a36Sopenharmony_ci writel(cmd, mdata->base + SPI_CMD_REG); 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci spi_finalize_current_transfer(master); 83862306a36Sopenharmony_ci return IRQ_HANDLED; 83962306a36Sopenharmony_ci } 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci mtk_spi_update_mdata_len(master); 84262306a36Sopenharmony_ci mtk_spi_setup_packet(master); 84362306a36Sopenharmony_ci mtk_spi_setup_dma_addr(master, trans); 84462306a36Sopenharmony_ci mtk_spi_enable_transfer(master); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci return IRQ_HANDLED; 84762306a36Sopenharmony_ci} 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_cistatic int mtk_spi_mem_adjust_op_size(struct spi_mem *mem, 85062306a36Sopenharmony_ci struct spi_mem_op *op) 85162306a36Sopenharmony_ci{ 85262306a36Sopenharmony_ci int opcode_len; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci if (op->data.dir != SPI_MEM_NO_DATA) { 85562306a36Sopenharmony_ci opcode_len = 1 + op->addr.nbytes + op->dummy.nbytes; 85662306a36Sopenharmony_ci if (opcode_len + op->data.nbytes > MTK_SPI_IPM_PACKET_SIZE) { 85762306a36Sopenharmony_ci op->data.nbytes = MTK_SPI_IPM_PACKET_SIZE - opcode_len; 85862306a36Sopenharmony_ci /* force data buffer dma-aligned. */ 85962306a36Sopenharmony_ci op->data.nbytes -= op->data.nbytes % 4; 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci } 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci return 0; 86462306a36Sopenharmony_ci} 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_cistatic bool mtk_spi_mem_supports_op(struct spi_mem *mem, 86762306a36Sopenharmony_ci const struct spi_mem_op *op) 86862306a36Sopenharmony_ci{ 86962306a36Sopenharmony_ci if (!spi_mem_default_supports_op(mem, op)) 87062306a36Sopenharmony_ci return false; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci if (op->addr.nbytes && op->dummy.nbytes && 87362306a36Sopenharmony_ci op->addr.buswidth != op->dummy.buswidth) 87462306a36Sopenharmony_ci return false; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci if (op->addr.nbytes + op->dummy.nbytes > 16) 87762306a36Sopenharmony_ci return false; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci if (op->data.nbytes > MTK_SPI_IPM_PACKET_SIZE) { 88062306a36Sopenharmony_ci if (op->data.nbytes / MTK_SPI_IPM_PACKET_SIZE > 88162306a36Sopenharmony_ci MTK_SPI_IPM_PACKET_LOOP || 88262306a36Sopenharmony_ci op->data.nbytes % MTK_SPI_IPM_PACKET_SIZE != 0) 88362306a36Sopenharmony_ci return false; 88462306a36Sopenharmony_ci } 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci return true; 88762306a36Sopenharmony_ci} 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_cistatic void mtk_spi_mem_setup_dma_xfer(struct spi_master *master, 89062306a36Sopenharmony_ci const struct spi_mem_op *op) 89162306a36Sopenharmony_ci{ 89262306a36Sopenharmony_ci struct mtk_spi *mdata = spi_master_get_devdata(master); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci writel((u32)(mdata->tx_dma & MTK_SPI_32BITS_MASK), 89562306a36Sopenharmony_ci mdata->base + SPI_TX_SRC_REG); 89662306a36Sopenharmony_ci#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT 89762306a36Sopenharmony_ci if (mdata->dev_comp->dma_ext) 89862306a36Sopenharmony_ci writel((u32)(mdata->tx_dma >> 32), 89962306a36Sopenharmony_ci mdata->base + SPI_TX_SRC_REG_64); 90062306a36Sopenharmony_ci#endif 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci if (op->data.dir == SPI_MEM_DATA_IN) { 90362306a36Sopenharmony_ci writel((u32)(mdata->rx_dma & MTK_SPI_32BITS_MASK), 90462306a36Sopenharmony_ci mdata->base + SPI_RX_DST_REG); 90562306a36Sopenharmony_ci#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT 90662306a36Sopenharmony_ci if (mdata->dev_comp->dma_ext) 90762306a36Sopenharmony_ci writel((u32)(mdata->rx_dma >> 32), 90862306a36Sopenharmony_ci mdata->base + SPI_RX_DST_REG_64); 90962306a36Sopenharmony_ci#endif 91062306a36Sopenharmony_ci } 91162306a36Sopenharmony_ci} 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_cistatic int mtk_spi_transfer_wait(struct spi_mem *mem, 91462306a36Sopenharmony_ci const struct spi_mem_op *op) 91562306a36Sopenharmony_ci{ 91662306a36Sopenharmony_ci struct mtk_spi *mdata = spi_master_get_devdata(mem->spi->master); 91762306a36Sopenharmony_ci /* 91862306a36Sopenharmony_ci * For each byte we wait for 8 cycles of the SPI clock. 91962306a36Sopenharmony_ci * Since speed is defined in Hz and we want milliseconds, 92062306a36Sopenharmony_ci * so it should be 8 * 1000. 92162306a36Sopenharmony_ci */ 92262306a36Sopenharmony_ci u64 ms = 8000LL; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci if (op->data.dir == SPI_MEM_NO_DATA) 92562306a36Sopenharmony_ci ms *= 32; /* prevent we may get 0 for short transfers. */ 92662306a36Sopenharmony_ci else 92762306a36Sopenharmony_ci ms *= op->data.nbytes; 92862306a36Sopenharmony_ci ms = div_u64(ms, mem->spi->max_speed_hz); 92962306a36Sopenharmony_ci ms += ms + 1000; /* 1s tolerance */ 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci if (ms > UINT_MAX) 93262306a36Sopenharmony_ci ms = UINT_MAX; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci if (!wait_for_completion_timeout(&mdata->spimem_done, 93562306a36Sopenharmony_ci msecs_to_jiffies(ms))) { 93662306a36Sopenharmony_ci dev_err(mdata->dev, "spi-mem transfer timeout\n"); 93762306a36Sopenharmony_ci return -ETIMEDOUT; 93862306a36Sopenharmony_ci } 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci return 0; 94162306a36Sopenharmony_ci} 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_cistatic int mtk_spi_mem_exec_op(struct spi_mem *mem, 94462306a36Sopenharmony_ci const struct spi_mem_op *op) 94562306a36Sopenharmony_ci{ 94662306a36Sopenharmony_ci struct mtk_spi *mdata = spi_master_get_devdata(mem->spi->master); 94762306a36Sopenharmony_ci u32 reg_val, nio, tx_size; 94862306a36Sopenharmony_ci char *tx_tmp_buf, *rx_tmp_buf; 94962306a36Sopenharmony_ci int ret = 0; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci mdata->use_spimem = true; 95262306a36Sopenharmony_ci reinit_completion(&mdata->spimem_done); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci mtk_spi_reset(mdata); 95562306a36Sopenharmony_ci mtk_spi_hw_init(mem->spi->master, mem->spi); 95662306a36Sopenharmony_ci mtk_spi_prepare_transfer(mem->spi->master, mem->spi->max_speed_hz); 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci reg_val = readl(mdata->base + SPI_CFG3_IPM_REG); 95962306a36Sopenharmony_ci /* opcode byte len */ 96062306a36Sopenharmony_ci reg_val &= ~SPI_CFG3_IPM_CMD_BYTELEN_MASK; 96162306a36Sopenharmony_ci reg_val |= 1 << SPI_CFG3_IPM_CMD_BYTELEN_OFFSET; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci /* addr & dummy byte len */ 96462306a36Sopenharmony_ci reg_val &= ~SPI_CFG3_IPM_ADDR_BYTELEN_MASK; 96562306a36Sopenharmony_ci if (op->addr.nbytes || op->dummy.nbytes) 96662306a36Sopenharmony_ci reg_val |= (op->addr.nbytes + op->dummy.nbytes) << 96762306a36Sopenharmony_ci SPI_CFG3_IPM_ADDR_BYTELEN_OFFSET; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci /* data byte len */ 97062306a36Sopenharmony_ci if (op->data.dir == SPI_MEM_NO_DATA) { 97162306a36Sopenharmony_ci reg_val |= SPI_CFG3_IPM_NODATA_FLAG; 97262306a36Sopenharmony_ci writel(0, mdata->base + SPI_CFG1_REG); 97362306a36Sopenharmony_ci } else { 97462306a36Sopenharmony_ci reg_val &= ~SPI_CFG3_IPM_NODATA_FLAG; 97562306a36Sopenharmony_ci mdata->xfer_len = op->data.nbytes; 97662306a36Sopenharmony_ci mtk_spi_setup_packet(mem->spi->master); 97762306a36Sopenharmony_ci } 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci if (op->addr.nbytes || op->dummy.nbytes) { 98062306a36Sopenharmony_ci if (op->addr.buswidth == 1 || op->dummy.buswidth == 1) 98162306a36Sopenharmony_ci reg_val |= SPI_CFG3_IPM_XMODE_EN; 98262306a36Sopenharmony_ci else 98362306a36Sopenharmony_ci reg_val &= ~SPI_CFG3_IPM_XMODE_EN; 98462306a36Sopenharmony_ci } 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci if (op->addr.buswidth == 2 || 98762306a36Sopenharmony_ci op->dummy.buswidth == 2 || 98862306a36Sopenharmony_ci op->data.buswidth == 2) 98962306a36Sopenharmony_ci nio = 2; 99062306a36Sopenharmony_ci else if (op->addr.buswidth == 4 || 99162306a36Sopenharmony_ci op->dummy.buswidth == 4 || 99262306a36Sopenharmony_ci op->data.buswidth == 4) 99362306a36Sopenharmony_ci nio = 4; 99462306a36Sopenharmony_ci else 99562306a36Sopenharmony_ci nio = 1; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci reg_val &= ~SPI_CFG3_IPM_CMD_PIN_MODE_MASK; 99862306a36Sopenharmony_ci reg_val |= PIN_MODE_CFG(nio); 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci reg_val |= SPI_CFG3_IPM_HALF_DUPLEX_EN; 100162306a36Sopenharmony_ci if (op->data.dir == SPI_MEM_DATA_IN) 100262306a36Sopenharmony_ci reg_val |= SPI_CFG3_IPM_HALF_DUPLEX_DIR; 100362306a36Sopenharmony_ci else 100462306a36Sopenharmony_ci reg_val &= ~SPI_CFG3_IPM_HALF_DUPLEX_DIR; 100562306a36Sopenharmony_ci writel(reg_val, mdata->base + SPI_CFG3_IPM_REG); 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci tx_size = 1 + op->addr.nbytes + op->dummy.nbytes; 100862306a36Sopenharmony_ci if (op->data.dir == SPI_MEM_DATA_OUT) 100962306a36Sopenharmony_ci tx_size += op->data.nbytes; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci tx_size = max_t(u32, tx_size, 32); 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci tx_tmp_buf = kzalloc(tx_size, GFP_KERNEL | GFP_DMA); 101462306a36Sopenharmony_ci if (!tx_tmp_buf) { 101562306a36Sopenharmony_ci mdata->use_spimem = false; 101662306a36Sopenharmony_ci return -ENOMEM; 101762306a36Sopenharmony_ci } 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci tx_tmp_buf[0] = op->cmd.opcode; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci if (op->addr.nbytes) { 102262306a36Sopenharmony_ci int i; 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci for (i = 0; i < op->addr.nbytes; i++) 102562306a36Sopenharmony_ci tx_tmp_buf[i + 1] = op->addr.val >> 102662306a36Sopenharmony_ci (8 * (op->addr.nbytes - i - 1)); 102762306a36Sopenharmony_ci } 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci if (op->dummy.nbytes) 103062306a36Sopenharmony_ci memset(tx_tmp_buf + op->addr.nbytes + 1, 103162306a36Sopenharmony_ci 0xff, 103262306a36Sopenharmony_ci op->dummy.nbytes); 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT) 103562306a36Sopenharmony_ci memcpy(tx_tmp_buf + op->dummy.nbytes + op->addr.nbytes + 1, 103662306a36Sopenharmony_ci op->data.buf.out, 103762306a36Sopenharmony_ci op->data.nbytes); 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci mdata->tx_dma = dma_map_single(mdata->dev, tx_tmp_buf, 104062306a36Sopenharmony_ci tx_size, DMA_TO_DEVICE); 104162306a36Sopenharmony_ci if (dma_mapping_error(mdata->dev, mdata->tx_dma)) { 104262306a36Sopenharmony_ci ret = -ENOMEM; 104362306a36Sopenharmony_ci goto err_exit; 104462306a36Sopenharmony_ci } 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci if (op->data.dir == SPI_MEM_DATA_IN) { 104762306a36Sopenharmony_ci if (!IS_ALIGNED((size_t)op->data.buf.in, 4)) { 104862306a36Sopenharmony_ci rx_tmp_buf = kzalloc(op->data.nbytes, 104962306a36Sopenharmony_ci GFP_KERNEL | GFP_DMA); 105062306a36Sopenharmony_ci if (!rx_tmp_buf) { 105162306a36Sopenharmony_ci ret = -ENOMEM; 105262306a36Sopenharmony_ci goto unmap_tx_dma; 105362306a36Sopenharmony_ci } 105462306a36Sopenharmony_ci } else { 105562306a36Sopenharmony_ci rx_tmp_buf = op->data.buf.in; 105662306a36Sopenharmony_ci } 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci mdata->rx_dma = dma_map_single(mdata->dev, 105962306a36Sopenharmony_ci rx_tmp_buf, 106062306a36Sopenharmony_ci op->data.nbytes, 106162306a36Sopenharmony_ci DMA_FROM_DEVICE); 106262306a36Sopenharmony_ci if (dma_mapping_error(mdata->dev, mdata->rx_dma)) { 106362306a36Sopenharmony_ci ret = -ENOMEM; 106462306a36Sopenharmony_ci goto kfree_rx_tmp_buf; 106562306a36Sopenharmony_ci } 106662306a36Sopenharmony_ci } 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci reg_val = readl(mdata->base + SPI_CMD_REG); 106962306a36Sopenharmony_ci reg_val |= SPI_CMD_TX_DMA; 107062306a36Sopenharmony_ci if (op->data.dir == SPI_MEM_DATA_IN) 107162306a36Sopenharmony_ci reg_val |= SPI_CMD_RX_DMA; 107262306a36Sopenharmony_ci writel(reg_val, mdata->base + SPI_CMD_REG); 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci mtk_spi_mem_setup_dma_xfer(mem->spi->master, op); 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci mtk_spi_enable_transfer(mem->spi->master); 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci /* Wait for the interrupt. */ 107962306a36Sopenharmony_ci ret = mtk_spi_transfer_wait(mem, op); 108062306a36Sopenharmony_ci if (ret) 108162306a36Sopenharmony_ci goto unmap_rx_dma; 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci /* spi disable dma */ 108462306a36Sopenharmony_ci reg_val = readl(mdata->base + SPI_CMD_REG); 108562306a36Sopenharmony_ci reg_val &= ~SPI_CMD_TX_DMA; 108662306a36Sopenharmony_ci if (op->data.dir == SPI_MEM_DATA_IN) 108762306a36Sopenharmony_ci reg_val &= ~SPI_CMD_RX_DMA; 108862306a36Sopenharmony_ci writel(reg_val, mdata->base + SPI_CMD_REG); 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ciunmap_rx_dma: 109162306a36Sopenharmony_ci if (op->data.dir == SPI_MEM_DATA_IN) { 109262306a36Sopenharmony_ci dma_unmap_single(mdata->dev, mdata->rx_dma, 109362306a36Sopenharmony_ci op->data.nbytes, DMA_FROM_DEVICE); 109462306a36Sopenharmony_ci if (!IS_ALIGNED((size_t)op->data.buf.in, 4)) 109562306a36Sopenharmony_ci memcpy(op->data.buf.in, rx_tmp_buf, op->data.nbytes); 109662306a36Sopenharmony_ci } 109762306a36Sopenharmony_cikfree_rx_tmp_buf: 109862306a36Sopenharmony_ci if (op->data.dir == SPI_MEM_DATA_IN && 109962306a36Sopenharmony_ci !IS_ALIGNED((size_t)op->data.buf.in, 4)) 110062306a36Sopenharmony_ci kfree(rx_tmp_buf); 110162306a36Sopenharmony_ciunmap_tx_dma: 110262306a36Sopenharmony_ci dma_unmap_single(mdata->dev, mdata->tx_dma, 110362306a36Sopenharmony_ci tx_size, DMA_TO_DEVICE); 110462306a36Sopenharmony_cierr_exit: 110562306a36Sopenharmony_ci kfree(tx_tmp_buf); 110662306a36Sopenharmony_ci mdata->use_spimem = false; 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci return ret; 110962306a36Sopenharmony_ci} 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_cistatic const struct spi_controller_mem_ops mtk_spi_mem_ops = { 111262306a36Sopenharmony_ci .adjust_op_size = mtk_spi_mem_adjust_op_size, 111362306a36Sopenharmony_ci .supports_op = mtk_spi_mem_supports_op, 111462306a36Sopenharmony_ci .exec_op = mtk_spi_mem_exec_op, 111562306a36Sopenharmony_ci}; 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_cistatic int mtk_spi_probe(struct platform_device *pdev) 111862306a36Sopenharmony_ci{ 111962306a36Sopenharmony_ci struct device *dev = &pdev->dev; 112062306a36Sopenharmony_ci struct spi_master *master; 112162306a36Sopenharmony_ci struct mtk_spi *mdata; 112262306a36Sopenharmony_ci int i, irq, ret, addr_bits; 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci master = devm_spi_alloc_master(dev, sizeof(*mdata)); 112562306a36Sopenharmony_ci if (!master) 112662306a36Sopenharmony_ci return dev_err_probe(dev, -ENOMEM, "failed to alloc spi master\n"); 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci master->auto_runtime_pm = true; 112962306a36Sopenharmony_ci master->dev.of_node = dev->of_node; 113062306a36Sopenharmony_ci master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci master->set_cs = mtk_spi_set_cs; 113362306a36Sopenharmony_ci master->prepare_message = mtk_spi_prepare_message; 113462306a36Sopenharmony_ci master->transfer_one = mtk_spi_transfer_one; 113562306a36Sopenharmony_ci master->can_dma = mtk_spi_can_dma; 113662306a36Sopenharmony_ci master->setup = mtk_spi_setup; 113762306a36Sopenharmony_ci master->set_cs_timing = mtk_spi_set_hw_cs_timing; 113862306a36Sopenharmony_ci master->use_gpio_descriptors = true; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci mdata = spi_master_get_devdata(master); 114162306a36Sopenharmony_ci mdata->dev_comp = device_get_match_data(dev); 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci if (mdata->dev_comp->enhance_timing) 114462306a36Sopenharmony_ci master->mode_bits |= SPI_CS_HIGH; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci if (mdata->dev_comp->must_tx) 114762306a36Sopenharmony_ci master->flags = SPI_CONTROLLER_MUST_TX; 114862306a36Sopenharmony_ci if (mdata->dev_comp->ipm_design) 114962306a36Sopenharmony_ci master->mode_bits |= SPI_LOOP | SPI_RX_DUAL | SPI_TX_DUAL | 115062306a36Sopenharmony_ci SPI_RX_QUAD | SPI_TX_QUAD; 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci if (mdata->dev_comp->ipm_design) { 115362306a36Sopenharmony_ci mdata->dev = dev; 115462306a36Sopenharmony_ci master->mem_ops = &mtk_spi_mem_ops; 115562306a36Sopenharmony_ci init_completion(&mdata->spimem_done); 115662306a36Sopenharmony_ci } 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci if (mdata->dev_comp->need_pad_sel) { 115962306a36Sopenharmony_ci mdata->pad_num = of_property_count_u32_elems(dev->of_node, 116062306a36Sopenharmony_ci "mediatek,pad-select"); 116162306a36Sopenharmony_ci if (mdata->pad_num < 0) 116262306a36Sopenharmony_ci return dev_err_probe(dev, -EINVAL, 116362306a36Sopenharmony_ci "No 'mediatek,pad-select' property\n"); 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci mdata->pad_sel = devm_kmalloc_array(dev, mdata->pad_num, 116662306a36Sopenharmony_ci sizeof(u32), GFP_KERNEL); 116762306a36Sopenharmony_ci if (!mdata->pad_sel) 116862306a36Sopenharmony_ci return -ENOMEM; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci for (i = 0; i < mdata->pad_num; i++) { 117162306a36Sopenharmony_ci of_property_read_u32_index(dev->of_node, 117262306a36Sopenharmony_ci "mediatek,pad-select", 117362306a36Sopenharmony_ci i, &mdata->pad_sel[i]); 117462306a36Sopenharmony_ci if (mdata->pad_sel[i] > MT8173_SPI_MAX_PAD_SEL) 117562306a36Sopenharmony_ci return dev_err_probe(dev, -EINVAL, 117662306a36Sopenharmony_ci "wrong pad-sel[%d]: %u\n", 117762306a36Sopenharmony_ci i, mdata->pad_sel[i]); 117862306a36Sopenharmony_ci } 117962306a36Sopenharmony_ci } 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci platform_set_drvdata(pdev, master); 118262306a36Sopenharmony_ci mdata->base = devm_platform_ioremap_resource(pdev, 0); 118362306a36Sopenharmony_ci if (IS_ERR(mdata->base)) 118462306a36Sopenharmony_ci return PTR_ERR(mdata->base); 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 118762306a36Sopenharmony_ci if (irq < 0) 118862306a36Sopenharmony_ci return irq; 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci if (!dev->dma_mask) 119162306a36Sopenharmony_ci dev->dma_mask = &dev->coherent_dma_mask; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci if (mdata->dev_comp->ipm_design) 119462306a36Sopenharmony_ci dma_set_max_seg_size(dev, SZ_16M); 119562306a36Sopenharmony_ci else 119662306a36Sopenharmony_ci dma_set_max_seg_size(dev, SZ_256K); 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci mdata->parent_clk = devm_clk_get(dev, "parent-clk"); 119962306a36Sopenharmony_ci if (IS_ERR(mdata->parent_clk)) 120062306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(mdata->parent_clk), 120162306a36Sopenharmony_ci "failed to get parent-clk\n"); 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci mdata->sel_clk = devm_clk_get(dev, "sel-clk"); 120462306a36Sopenharmony_ci if (IS_ERR(mdata->sel_clk)) 120562306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(mdata->sel_clk), "failed to get sel-clk\n"); 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci mdata->spi_clk = devm_clk_get(dev, "spi-clk"); 120862306a36Sopenharmony_ci if (IS_ERR(mdata->spi_clk)) 120962306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(mdata->spi_clk), "failed to get spi-clk\n"); 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci mdata->spi_hclk = devm_clk_get_optional(dev, "hclk"); 121262306a36Sopenharmony_ci if (IS_ERR(mdata->spi_hclk)) 121362306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(mdata->spi_hclk), "failed to get hclk\n"); 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci ret = clk_set_parent(mdata->sel_clk, mdata->parent_clk); 121662306a36Sopenharmony_ci if (ret < 0) 121762306a36Sopenharmony_ci return dev_err_probe(dev, ret, "failed to clk_set_parent\n"); 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci ret = clk_prepare_enable(mdata->spi_hclk); 122062306a36Sopenharmony_ci if (ret < 0) 122162306a36Sopenharmony_ci return dev_err_probe(dev, ret, "failed to enable hclk\n"); 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci ret = clk_prepare_enable(mdata->spi_clk); 122462306a36Sopenharmony_ci if (ret < 0) { 122562306a36Sopenharmony_ci clk_disable_unprepare(mdata->spi_hclk); 122662306a36Sopenharmony_ci return dev_err_probe(dev, ret, "failed to enable spi_clk\n"); 122762306a36Sopenharmony_ci } 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci mdata->spi_clk_hz = clk_get_rate(mdata->spi_clk); 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci if (mdata->dev_comp->no_need_unprepare) { 123262306a36Sopenharmony_ci clk_disable(mdata->spi_clk); 123362306a36Sopenharmony_ci clk_disable(mdata->spi_hclk); 123462306a36Sopenharmony_ci } else { 123562306a36Sopenharmony_ci clk_disable_unprepare(mdata->spi_clk); 123662306a36Sopenharmony_ci clk_disable_unprepare(mdata->spi_hclk); 123762306a36Sopenharmony_ci } 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci if (mdata->dev_comp->need_pad_sel) { 124062306a36Sopenharmony_ci if (mdata->pad_num != master->num_chipselect) 124162306a36Sopenharmony_ci return dev_err_probe(dev, -EINVAL, 124262306a36Sopenharmony_ci "pad_num does not match num_chipselect(%d != %d)\n", 124362306a36Sopenharmony_ci mdata->pad_num, master->num_chipselect); 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci if (!master->cs_gpiods && master->num_chipselect > 1) 124662306a36Sopenharmony_ci return dev_err_probe(dev, -EINVAL, 124762306a36Sopenharmony_ci "cs_gpios not specified and num_chipselect > 1\n"); 124862306a36Sopenharmony_ci } 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci if (mdata->dev_comp->dma_ext) 125162306a36Sopenharmony_ci addr_bits = DMA_ADDR_EXT_BITS; 125262306a36Sopenharmony_ci else 125362306a36Sopenharmony_ci addr_bits = DMA_ADDR_DEF_BITS; 125462306a36Sopenharmony_ci ret = dma_set_mask(dev, DMA_BIT_MASK(addr_bits)); 125562306a36Sopenharmony_ci if (ret) 125662306a36Sopenharmony_ci dev_notice(dev, "SPI dma_set_mask(%d) failed, ret:%d\n", 125762306a36Sopenharmony_ci addr_bits, ret); 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci ret = devm_request_irq(dev, irq, mtk_spi_interrupt, 126062306a36Sopenharmony_ci IRQF_TRIGGER_NONE, dev_name(dev), master); 126162306a36Sopenharmony_ci if (ret) 126262306a36Sopenharmony_ci return dev_err_probe(dev, ret, "failed to register irq\n"); 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci pm_runtime_enable(dev); 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci ret = devm_spi_register_master(dev, master); 126762306a36Sopenharmony_ci if (ret) { 126862306a36Sopenharmony_ci pm_runtime_disable(dev); 126962306a36Sopenharmony_ci return dev_err_probe(dev, ret, "failed to register master\n"); 127062306a36Sopenharmony_ci } 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci return 0; 127362306a36Sopenharmony_ci} 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_cistatic void mtk_spi_remove(struct platform_device *pdev) 127662306a36Sopenharmony_ci{ 127762306a36Sopenharmony_ci struct spi_master *master = platform_get_drvdata(pdev); 127862306a36Sopenharmony_ci struct mtk_spi *mdata = spi_master_get_devdata(master); 127962306a36Sopenharmony_ci int ret; 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci if (mdata->use_spimem && !completion_done(&mdata->spimem_done)) 128262306a36Sopenharmony_ci complete(&mdata->spimem_done); 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci ret = pm_runtime_get_sync(&pdev->dev); 128562306a36Sopenharmony_ci if (ret < 0) { 128662306a36Sopenharmony_ci dev_warn(&pdev->dev, "Failed to resume hardware (%pe)\n", ERR_PTR(ret)); 128762306a36Sopenharmony_ci } else { 128862306a36Sopenharmony_ci /* 128962306a36Sopenharmony_ci * If pm runtime resume failed, clks are disabled and 129062306a36Sopenharmony_ci * unprepared. So don't access the hardware and skip clk 129162306a36Sopenharmony_ci * unpreparing. 129262306a36Sopenharmony_ci */ 129362306a36Sopenharmony_ci mtk_spi_reset(mdata); 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci if (mdata->dev_comp->no_need_unprepare) { 129662306a36Sopenharmony_ci clk_unprepare(mdata->spi_clk); 129762306a36Sopenharmony_ci clk_unprepare(mdata->spi_hclk); 129862306a36Sopenharmony_ci } 129962306a36Sopenharmony_ci } 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci pm_runtime_put_noidle(&pdev->dev); 130262306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 130362306a36Sopenharmony_ci} 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 130662306a36Sopenharmony_cistatic int mtk_spi_suspend(struct device *dev) 130762306a36Sopenharmony_ci{ 130862306a36Sopenharmony_ci int ret; 130962306a36Sopenharmony_ci struct spi_master *master = dev_get_drvdata(dev); 131062306a36Sopenharmony_ci struct mtk_spi *mdata = spi_master_get_devdata(master); 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci ret = spi_master_suspend(master); 131362306a36Sopenharmony_ci if (ret) 131462306a36Sopenharmony_ci return ret; 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci if (!pm_runtime_suspended(dev)) { 131762306a36Sopenharmony_ci clk_disable_unprepare(mdata->spi_clk); 131862306a36Sopenharmony_ci clk_disable_unprepare(mdata->spi_hclk); 131962306a36Sopenharmony_ci } 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci return 0; 132262306a36Sopenharmony_ci} 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_cistatic int mtk_spi_resume(struct device *dev) 132562306a36Sopenharmony_ci{ 132662306a36Sopenharmony_ci int ret; 132762306a36Sopenharmony_ci struct spi_master *master = dev_get_drvdata(dev); 132862306a36Sopenharmony_ci struct mtk_spi *mdata = spi_master_get_devdata(master); 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci if (!pm_runtime_suspended(dev)) { 133162306a36Sopenharmony_ci ret = clk_prepare_enable(mdata->spi_clk); 133262306a36Sopenharmony_ci if (ret < 0) { 133362306a36Sopenharmony_ci dev_err(dev, "failed to enable spi_clk (%d)\n", ret); 133462306a36Sopenharmony_ci return ret; 133562306a36Sopenharmony_ci } 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci ret = clk_prepare_enable(mdata->spi_hclk); 133862306a36Sopenharmony_ci if (ret < 0) { 133962306a36Sopenharmony_ci dev_err(dev, "failed to enable spi_hclk (%d)\n", ret); 134062306a36Sopenharmony_ci clk_disable_unprepare(mdata->spi_clk); 134162306a36Sopenharmony_ci return ret; 134262306a36Sopenharmony_ci } 134362306a36Sopenharmony_ci } 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci ret = spi_master_resume(master); 134662306a36Sopenharmony_ci if (ret < 0) { 134762306a36Sopenharmony_ci clk_disable_unprepare(mdata->spi_clk); 134862306a36Sopenharmony_ci clk_disable_unprepare(mdata->spi_hclk); 134962306a36Sopenharmony_ci } 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci return ret; 135262306a36Sopenharmony_ci} 135362306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci#ifdef CONFIG_PM 135662306a36Sopenharmony_cistatic int mtk_spi_runtime_suspend(struct device *dev) 135762306a36Sopenharmony_ci{ 135862306a36Sopenharmony_ci struct spi_master *master = dev_get_drvdata(dev); 135962306a36Sopenharmony_ci struct mtk_spi *mdata = spi_master_get_devdata(master); 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci if (mdata->dev_comp->no_need_unprepare) { 136262306a36Sopenharmony_ci clk_disable(mdata->spi_clk); 136362306a36Sopenharmony_ci clk_disable(mdata->spi_hclk); 136462306a36Sopenharmony_ci } else { 136562306a36Sopenharmony_ci clk_disable_unprepare(mdata->spi_clk); 136662306a36Sopenharmony_ci clk_disable_unprepare(mdata->spi_hclk); 136762306a36Sopenharmony_ci } 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci return 0; 137062306a36Sopenharmony_ci} 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_cistatic int mtk_spi_runtime_resume(struct device *dev) 137362306a36Sopenharmony_ci{ 137462306a36Sopenharmony_ci struct spi_master *master = dev_get_drvdata(dev); 137562306a36Sopenharmony_ci struct mtk_spi *mdata = spi_master_get_devdata(master); 137662306a36Sopenharmony_ci int ret; 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci if (mdata->dev_comp->no_need_unprepare) { 137962306a36Sopenharmony_ci ret = clk_enable(mdata->spi_clk); 138062306a36Sopenharmony_ci if (ret < 0) { 138162306a36Sopenharmony_ci dev_err(dev, "failed to enable spi_clk (%d)\n", ret); 138262306a36Sopenharmony_ci return ret; 138362306a36Sopenharmony_ci } 138462306a36Sopenharmony_ci ret = clk_enable(mdata->spi_hclk); 138562306a36Sopenharmony_ci if (ret < 0) { 138662306a36Sopenharmony_ci dev_err(dev, "failed to enable spi_hclk (%d)\n", ret); 138762306a36Sopenharmony_ci clk_disable(mdata->spi_clk); 138862306a36Sopenharmony_ci return ret; 138962306a36Sopenharmony_ci } 139062306a36Sopenharmony_ci } else { 139162306a36Sopenharmony_ci ret = clk_prepare_enable(mdata->spi_clk); 139262306a36Sopenharmony_ci if (ret < 0) { 139362306a36Sopenharmony_ci dev_err(dev, "failed to prepare_enable spi_clk (%d)\n", ret); 139462306a36Sopenharmony_ci return ret; 139562306a36Sopenharmony_ci } 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci ret = clk_prepare_enable(mdata->spi_hclk); 139862306a36Sopenharmony_ci if (ret < 0) { 139962306a36Sopenharmony_ci dev_err(dev, "failed to prepare_enable spi_hclk (%d)\n", ret); 140062306a36Sopenharmony_ci clk_disable_unprepare(mdata->spi_clk); 140162306a36Sopenharmony_ci return ret; 140262306a36Sopenharmony_ci } 140362306a36Sopenharmony_ci } 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci return 0; 140662306a36Sopenharmony_ci} 140762306a36Sopenharmony_ci#endif /* CONFIG_PM */ 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_cistatic const struct dev_pm_ops mtk_spi_pm = { 141062306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(mtk_spi_suspend, mtk_spi_resume) 141162306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(mtk_spi_runtime_suspend, 141262306a36Sopenharmony_ci mtk_spi_runtime_resume, NULL) 141362306a36Sopenharmony_ci}; 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_cistatic struct platform_driver mtk_spi_driver = { 141662306a36Sopenharmony_ci .driver = { 141762306a36Sopenharmony_ci .name = "mtk-spi", 141862306a36Sopenharmony_ci .pm = &mtk_spi_pm, 141962306a36Sopenharmony_ci .of_match_table = mtk_spi_of_match, 142062306a36Sopenharmony_ci }, 142162306a36Sopenharmony_ci .probe = mtk_spi_probe, 142262306a36Sopenharmony_ci .remove_new = mtk_spi_remove, 142362306a36Sopenharmony_ci}; 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_cimodule_platform_driver(mtk_spi_driver); 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ciMODULE_DESCRIPTION("MTK SPI Controller driver"); 142862306a36Sopenharmony_ciMODULE_AUTHOR("Leilk Liu <leilk.liu@mediatek.com>"); 142962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 143062306a36Sopenharmony_ciMODULE_ALIAS("platform:mtk-spi"); 1431