18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// Copyright (c) 2017-2018, The Linux foundation. All rights reserved. 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/clk.h> 58c2ecf20Sopenharmony_ci#include <linux/interconnect.h> 68c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 78c2ecf20Sopenharmony_ci#include <linux/io.h> 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/of.h> 108c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 118c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 128c2ecf20Sopenharmony_ci#include <linux/pm_opp.h> 138c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 148c2ecf20Sopenharmony_ci#include <linux/spi/spi-mem.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define QSPI_NUM_CS 2 188c2ecf20Sopenharmony_ci#define QSPI_BYTES_PER_WORD 4 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define MSTR_CONFIG 0x0000 218c2ecf20Sopenharmony_ci#define FULL_CYCLE_MODE BIT(3) 228c2ecf20Sopenharmony_ci#define FB_CLK_EN BIT(4) 238c2ecf20Sopenharmony_ci#define PIN_HOLDN BIT(6) 248c2ecf20Sopenharmony_ci#define PIN_WPN BIT(7) 258c2ecf20Sopenharmony_ci#define DMA_ENABLE BIT(8) 268c2ecf20Sopenharmony_ci#define BIG_ENDIAN_MODE BIT(9) 278c2ecf20Sopenharmony_ci#define SPI_MODE_MSK 0xc00 288c2ecf20Sopenharmony_ci#define SPI_MODE_SHFT 10 298c2ecf20Sopenharmony_ci#define CHIP_SELECT_NUM BIT(12) 308c2ecf20Sopenharmony_ci#define SBL_EN BIT(13) 318c2ecf20Sopenharmony_ci#define LPA_BASE_MSK 0x3c000 328c2ecf20Sopenharmony_ci#define LPA_BASE_SHFT 14 338c2ecf20Sopenharmony_ci#define TX_DATA_DELAY_MSK 0xc0000 348c2ecf20Sopenharmony_ci#define TX_DATA_DELAY_SHFT 18 358c2ecf20Sopenharmony_ci#define TX_CLK_DELAY_MSK 0x300000 368c2ecf20Sopenharmony_ci#define TX_CLK_DELAY_SHFT 20 378c2ecf20Sopenharmony_ci#define TX_CS_N_DELAY_MSK 0xc00000 388c2ecf20Sopenharmony_ci#define TX_CS_N_DELAY_SHFT 22 398c2ecf20Sopenharmony_ci#define TX_DATA_OE_DELAY_MSK 0x3000000 408c2ecf20Sopenharmony_ci#define TX_DATA_OE_DELAY_SHFT 24 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define AHB_MASTER_CFG 0x0004 438c2ecf20Sopenharmony_ci#define HMEM_TYPE_START_MID_TRANS_MSK 0x7 448c2ecf20Sopenharmony_ci#define HMEM_TYPE_START_MID_TRANS_SHFT 0 458c2ecf20Sopenharmony_ci#define HMEM_TYPE_LAST_TRANS_MSK 0x38 468c2ecf20Sopenharmony_ci#define HMEM_TYPE_LAST_TRANS_SHFT 3 478c2ecf20Sopenharmony_ci#define USE_HMEMTYPE_LAST_ON_DESC_OR_CHAIN_MSK 0xc0 488c2ecf20Sopenharmony_ci#define USE_HMEMTYPE_LAST_ON_DESC_OR_CHAIN_SHFT 6 498c2ecf20Sopenharmony_ci#define HMEMTYPE_READ_TRANS_MSK 0x700 508c2ecf20Sopenharmony_ci#define HMEMTYPE_READ_TRANS_SHFT 8 518c2ecf20Sopenharmony_ci#define HSHARED BIT(11) 528c2ecf20Sopenharmony_ci#define HINNERSHARED BIT(12) 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#define MSTR_INT_EN 0x000C 558c2ecf20Sopenharmony_ci#define MSTR_INT_STATUS 0x0010 568c2ecf20Sopenharmony_ci#define RESP_FIFO_UNDERRUN BIT(0) 578c2ecf20Sopenharmony_ci#define RESP_FIFO_NOT_EMPTY BIT(1) 588c2ecf20Sopenharmony_ci#define RESP_FIFO_RDY BIT(2) 598c2ecf20Sopenharmony_ci#define HRESP_FROM_NOC_ERR BIT(3) 608c2ecf20Sopenharmony_ci#define WR_FIFO_EMPTY BIT(9) 618c2ecf20Sopenharmony_ci#define WR_FIFO_FULL BIT(10) 628c2ecf20Sopenharmony_ci#define WR_FIFO_OVERRUN BIT(11) 638c2ecf20Sopenharmony_ci#define TRANSACTION_DONE BIT(16) 648c2ecf20Sopenharmony_ci#define QSPI_ERR_IRQS (RESP_FIFO_UNDERRUN | HRESP_FROM_NOC_ERR | \ 658c2ecf20Sopenharmony_ci WR_FIFO_OVERRUN) 668c2ecf20Sopenharmony_ci#define QSPI_ALL_IRQS (QSPI_ERR_IRQS | RESP_FIFO_RDY | \ 678c2ecf20Sopenharmony_ci WR_FIFO_EMPTY | WR_FIFO_FULL | \ 688c2ecf20Sopenharmony_ci TRANSACTION_DONE) 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci#define PIO_XFER_CTRL 0x0014 718c2ecf20Sopenharmony_ci#define REQUEST_COUNT_MSK 0xffff 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci#define PIO_XFER_CFG 0x0018 748c2ecf20Sopenharmony_ci#define TRANSFER_DIRECTION BIT(0) 758c2ecf20Sopenharmony_ci#define MULTI_IO_MODE_MSK 0xe 768c2ecf20Sopenharmony_ci#define MULTI_IO_MODE_SHFT 1 778c2ecf20Sopenharmony_ci#define TRANSFER_FRAGMENT BIT(8) 788c2ecf20Sopenharmony_ci#define SDR_1BIT 1 798c2ecf20Sopenharmony_ci#define SDR_2BIT 2 808c2ecf20Sopenharmony_ci#define SDR_4BIT 3 818c2ecf20Sopenharmony_ci#define DDR_1BIT 5 828c2ecf20Sopenharmony_ci#define DDR_2BIT 6 838c2ecf20Sopenharmony_ci#define DDR_4BIT 7 848c2ecf20Sopenharmony_ci#define DMA_DESC_SINGLE_SPI 1 858c2ecf20Sopenharmony_ci#define DMA_DESC_DUAL_SPI 2 868c2ecf20Sopenharmony_ci#define DMA_DESC_QUAD_SPI 3 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci#define PIO_XFER_STATUS 0x001c 898c2ecf20Sopenharmony_ci#define WR_FIFO_BYTES_MSK 0xffff0000 908c2ecf20Sopenharmony_ci#define WR_FIFO_BYTES_SHFT 16 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci#define PIO_DATAOUT_1B 0x0020 938c2ecf20Sopenharmony_ci#define PIO_DATAOUT_4B 0x0024 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci#define RD_FIFO_CFG 0x0028 968c2ecf20Sopenharmony_ci#define CONTINUOUS_MODE BIT(0) 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci#define RD_FIFO_STATUS 0x002c 998c2ecf20Sopenharmony_ci#define FIFO_EMPTY BIT(11) 1008c2ecf20Sopenharmony_ci#define WR_CNTS_MSK 0x7f0 1018c2ecf20Sopenharmony_ci#define WR_CNTS_SHFT 4 1028c2ecf20Sopenharmony_ci#define RDY_64BYTE BIT(3) 1038c2ecf20Sopenharmony_ci#define RDY_32BYTE BIT(2) 1048c2ecf20Sopenharmony_ci#define RDY_16BYTE BIT(1) 1058c2ecf20Sopenharmony_ci#define FIFO_RDY BIT(0) 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci#define RD_FIFO_RESET 0x0030 1088c2ecf20Sopenharmony_ci#define RESET_FIFO BIT(0) 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci#define CUR_MEM_ADDR 0x0048 1118c2ecf20Sopenharmony_ci#define HW_VERSION 0x004c 1128c2ecf20Sopenharmony_ci#define RD_FIFO 0x0050 1138c2ecf20Sopenharmony_ci#define SAMPLING_CLK_CFG 0x0090 1148c2ecf20Sopenharmony_ci#define SAMPLING_CLK_STATUS 0x0094 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cienum qspi_dir { 1188c2ecf20Sopenharmony_ci QSPI_READ, 1198c2ecf20Sopenharmony_ci QSPI_WRITE, 1208c2ecf20Sopenharmony_ci}; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistruct qspi_xfer { 1238c2ecf20Sopenharmony_ci union { 1248c2ecf20Sopenharmony_ci const void *tx_buf; 1258c2ecf20Sopenharmony_ci void *rx_buf; 1268c2ecf20Sopenharmony_ci }; 1278c2ecf20Sopenharmony_ci unsigned int rem_bytes; 1288c2ecf20Sopenharmony_ci unsigned int buswidth; 1298c2ecf20Sopenharmony_ci enum qspi_dir dir; 1308c2ecf20Sopenharmony_ci bool is_last; 1318c2ecf20Sopenharmony_ci}; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cienum qspi_clocks { 1348c2ecf20Sopenharmony_ci QSPI_CLK_CORE, 1358c2ecf20Sopenharmony_ci QSPI_CLK_IFACE, 1368c2ecf20Sopenharmony_ci QSPI_NUM_CLKS 1378c2ecf20Sopenharmony_ci}; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistruct qcom_qspi { 1408c2ecf20Sopenharmony_ci void __iomem *base; 1418c2ecf20Sopenharmony_ci struct device *dev; 1428c2ecf20Sopenharmony_ci struct clk_bulk_data *clks; 1438c2ecf20Sopenharmony_ci struct qspi_xfer xfer; 1448c2ecf20Sopenharmony_ci struct icc_path *icc_path_cpu_to_qspi; 1458c2ecf20Sopenharmony_ci struct opp_table *opp_table; 1468c2ecf20Sopenharmony_ci unsigned long last_speed; 1478c2ecf20Sopenharmony_ci /* Lock to protect data accessed by IRQs */ 1488c2ecf20Sopenharmony_ci spinlock_t lock; 1498c2ecf20Sopenharmony_ci}; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic u32 qspi_buswidth_to_iomode(struct qcom_qspi *ctrl, 1528c2ecf20Sopenharmony_ci unsigned int buswidth) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci switch (buswidth) { 1558c2ecf20Sopenharmony_ci case 1: 1568c2ecf20Sopenharmony_ci return SDR_1BIT << MULTI_IO_MODE_SHFT; 1578c2ecf20Sopenharmony_ci case 2: 1588c2ecf20Sopenharmony_ci return SDR_2BIT << MULTI_IO_MODE_SHFT; 1598c2ecf20Sopenharmony_ci case 4: 1608c2ecf20Sopenharmony_ci return SDR_4BIT << MULTI_IO_MODE_SHFT; 1618c2ecf20Sopenharmony_ci default: 1628c2ecf20Sopenharmony_ci dev_warn_once(ctrl->dev, 1638c2ecf20Sopenharmony_ci "Unexpected bus width: %u\n", buswidth); 1648c2ecf20Sopenharmony_ci return SDR_1BIT << MULTI_IO_MODE_SHFT; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic void qcom_qspi_pio_xfer_cfg(struct qcom_qspi *ctrl) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci u32 pio_xfer_cfg; 1718c2ecf20Sopenharmony_ci const struct qspi_xfer *xfer; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci xfer = &ctrl->xfer; 1748c2ecf20Sopenharmony_ci pio_xfer_cfg = readl(ctrl->base + PIO_XFER_CFG); 1758c2ecf20Sopenharmony_ci pio_xfer_cfg &= ~TRANSFER_DIRECTION; 1768c2ecf20Sopenharmony_ci pio_xfer_cfg |= xfer->dir; 1778c2ecf20Sopenharmony_ci if (xfer->is_last) 1788c2ecf20Sopenharmony_ci pio_xfer_cfg &= ~TRANSFER_FRAGMENT; 1798c2ecf20Sopenharmony_ci else 1808c2ecf20Sopenharmony_ci pio_xfer_cfg |= TRANSFER_FRAGMENT; 1818c2ecf20Sopenharmony_ci pio_xfer_cfg &= ~MULTI_IO_MODE_MSK; 1828c2ecf20Sopenharmony_ci pio_xfer_cfg |= qspi_buswidth_to_iomode(ctrl, xfer->buswidth); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci writel(pio_xfer_cfg, ctrl->base + PIO_XFER_CFG); 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic void qcom_qspi_pio_xfer_ctrl(struct qcom_qspi *ctrl) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci u32 pio_xfer_ctrl; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci pio_xfer_ctrl = readl(ctrl->base + PIO_XFER_CTRL); 1928c2ecf20Sopenharmony_ci pio_xfer_ctrl &= ~REQUEST_COUNT_MSK; 1938c2ecf20Sopenharmony_ci pio_xfer_ctrl |= ctrl->xfer.rem_bytes; 1948c2ecf20Sopenharmony_ci writel(pio_xfer_ctrl, ctrl->base + PIO_XFER_CTRL); 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic void qcom_qspi_pio_xfer(struct qcom_qspi *ctrl) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci u32 ints; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci qcom_qspi_pio_xfer_cfg(ctrl); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* Ack any previous interrupts that might be hanging around */ 2048c2ecf20Sopenharmony_ci writel(QSPI_ALL_IRQS, ctrl->base + MSTR_INT_STATUS); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* Setup new interrupts */ 2078c2ecf20Sopenharmony_ci if (ctrl->xfer.dir == QSPI_WRITE) 2088c2ecf20Sopenharmony_ci ints = QSPI_ERR_IRQS | WR_FIFO_EMPTY; 2098c2ecf20Sopenharmony_ci else 2108c2ecf20Sopenharmony_ci ints = QSPI_ERR_IRQS | RESP_FIFO_RDY; 2118c2ecf20Sopenharmony_ci writel(ints, ctrl->base + MSTR_INT_EN); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* Kick off the transfer */ 2148c2ecf20Sopenharmony_ci qcom_qspi_pio_xfer_ctrl(ctrl); 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic void qcom_qspi_handle_err(struct spi_master *master, 2188c2ecf20Sopenharmony_ci struct spi_message *msg) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci struct qcom_qspi *ctrl = spi_master_get_devdata(master); 2218c2ecf20Sopenharmony_ci unsigned long flags; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctrl->lock, flags); 2248c2ecf20Sopenharmony_ci writel(0, ctrl->base + MSTR_INT_EN); 2258c2ecf20Sopenharmony_ci ctrl->xfer.rem_bytes = 0; 2268c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctrl->lock, flags); 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic int qcom_qspi_set_speed(struct qcom_qspi *ctrl, unsigned long speed_hz) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci int ret; 2328c2ecf20Sopenharmony_ci unsigned int avg_bw_cpu; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if (speed_hz == ctrl->last_speed) 2358c2ecf20Sopenharmony_ci return 0; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* In regular operation (SBL_EN=1) core must be 4x transfer clock */ 2388c2ecf20Sopenharmony_ci ret = dev_pm_opp_set_rate(ctrl->dev, speed_hz * 4); 2398c2ecf20Sopenharmony_ci if (ret) { 2408c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "Failed to set core clk %d\n", ret); 2418c2ecf20Sopenharmony_ci return ret; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci /* 2458c2ecf20Sopenharmony_ci * Set BW quota for CPU as driver supports FIFO mode only. 2468c2ecf20Sopenharmony_ci * We don't have explicit peak requirement so keep it equal to avg_bw. 2478c2ecf20Sopenharmony_ci */ 2488c2ecf20Sopenharmony_ci avg_bw_cpu = Bps_to_icc(speed_hz); 2498c2ecf20Sopenharmony_ci ret = icc_set_bw(ctrl->icc_path_cpu_to_qspi, avg_bw_cpu, avg_bw_cpu); 2508c2ecf20Sopenharmony_ci if (ret) { 2518c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "%s: ICC BW voting failed for cpu: %d\n", 2528c2ecf20Sopenharmony_ci __func__, ret); 2538c2ecf20Sopenharmony_ci return ret; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci ctrl->last_speed = speed_hz; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci return 0; 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic int qcom_qspi_transfer_one(struct spi_master *master, 2628c2ecf20Sopenharmony_ci struct spi_device *slv, 2638c2ecf20Sopenharmony_ci struct spi_transfer *xfer) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci struct qcom_qspi *ctrl = spi_master_get_devdata(master); 2668c2ecf20Sopenharmony_ci int ret; 2678c2ecf20Sopenharmony_ci unsigned long speed_hz; 2688c2ecf20Sopenharmony_ci unsigned long flags; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci speed_hz = slv->max_speed_hz; 2718c2ecf20Sopenharmony_ci if (xfer->speed_hz) 2728c2ecf20Sopenharmony_ci speed_hz = xfer->speed_hz; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci ret = qcom_qspi_set_speed(ctrl, speed_hz); 2758c2ecf20Sopenharmony_ci if (ret) 2768c2ecf20Sopenharmony_ci return ret; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctrl->lock, flags); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* We are half duplex, so either rx or tx will be set */ 2818c2ecf20Sopenharmony_ci if (xfer->rx_buf) { 2828c2ecf20Sopenharmony_ci ctrl->xfer.dir = QSPI_READ; 2838c2ecf20Sopenharmony_ci ctrl->xfer.buswidth = xfer->rx_nbits; 2848c2ecf20Sopenharmony_ci ctrl->xfer.rx_buf = xfer->rx_buf; 2858c2ecf20Sopenharmony_ci } else { 2868c2ecf20Sopenharmony_ci ctrl->xfer.dir = QSPI_WRITE; 2878c2ecf20Sopenharmony_ci ctrl->xfer.buswidth = xfer->tx_nbits; 2888c2ecf20Sopenharmony_ci ctrl->xfer.tx_buf = xfer->tx_buf; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci ctrl->xfer.is_last = list_is_last(&xfer->transfer_list, 2918c2ecf20Sopenharmony_ci &master->cur_msg->transfers); 2928c2ecf20Sopenharmony_ci ctrl->xfer.rem_bytes = xfer->len; 2938c2ecf20Sopenharmony_ci qcom_qspi_pio_xfer(ctrl); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctrl->lock, flags); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci /* We'll call spi_finalize_current_transfer() when done */ 2988c2ecf20Sopenharmony_ci return 1; 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic int qcom_qspi_prepare_message(struct spi_master *master, 3028c2ecf20Sopenharmony_ci struct spi_message *message) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci u32 mstr_cfg; 3058c2ecf20Sopenharmony_ci struct qcom_qspi *ctrl; 3068c2ecf20Sopenharmony_ci int tx_data_oe_delay = 1; 3078c2ecf20Sopenharmony_ci int tx_data_delay = 1; 3088c2ecf20Sopenharmony_ci unsigned long flags; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci ctrl = spi_master_get_devdata(master); 3118c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctrl->lock, flags); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci mstr_cfg = readl(ctrl->base + MSTR_CONFIG); 3148c2ecf20Sopenharmony_ci mstr_cfg &= ~CHIP_SELECT_NUM; 3158c2ecf20Sopenharmony_ci if (message->spi->chip_select) 3168c2ecf20Sopenharmony_ci mstr_cfg |= CHIP_SELECT_NUM; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci mstr_cfg |= FB_CLK_EN | PIN_WPN | PIN_HOLDN | SBL_EN | FULL_CYCLE_MODE; 3198c2ecf20Sopenharmony_ci mstr_cfg &= ~(SPI_MODE_MSK | TX_DATA_OE_DELAY_MSK | TX_DATA_DELAY_MSK); 3208c2ecf20Sopenharmony_ci mstr_cfg |= message->spi->mode << SPI_MODE_SHFT; 3218c2ecf20Sopenharmony_ci mstr_cfg |= tx_data_oe_delay << TX_DATA_OE_DELAY_SHFT; 3228c2ecf20Sopenharmony_ci mstr_cfg |= tx_data_delay << TX_DATA_DELAY_SHFT; 3238c2ecf20Sopenharmony_ci mstr_cfg &= ~DMA_ENABLE; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci writel(mstr_cfg, ctrl->base + MSTR_CONFIG); 3268c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctrl->lock, flags); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci return 0; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic irqreturn_t pio_read(struct qcom_qspi *ctrl) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci u32 rd_fifo_status; 3348c2ecf20Sopenharmony_ci u32 rd_fifo; 3358c2ecf20Sopenharmony_ci unsigned int wr_cnts; 3368c2ecf20Sopenharmony_ci unsigned int bytes_to_read; 3378c2ecf20Sopenharmony_ci unsigned int words_to_read; 3388c2ecf20Sopenharmony_ci u32 *word_buf; 3398c2ecf20Sopenharmony_ci u8 *byte_buf; 3408c2ecf20Sopenharmony_ci int i; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci rd_fifo_status = readl(ctrl->base + RD_FIFO_STATUS); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (!(rd_fifo_status & FIFO_RDY)) { 3458c2ecf20Sopenharmony_ci dev_dbg(ctrl->dev, "Spurious IRQ %#x\n", rd_fifo_status); 3468c2ecf20Sopenharmony_ci return IRQ_NONE; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci wr_cnts = (rd_fifo_status & WR_CNTS_MSK) >> WR_CNTS_SHFT; 3508c2ecf20Sopenharmony_ci wr_cnts = min(wr_cnts, ctrl->xfer.rem_bytes); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci words_to_read = wr_cnts / QSPI_BYTES_PER_WORD; 3538c2ecf20Sopenharmony_ci bytes_to_read = wr_cnts % QSPI_BYTES_PER_WORD; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (words_to_read) { 3568c2ecf20Sopenharmony_ci word_buf = ctrl->xfer.rx_buf; 3578c2ecf20Sopenharmony_ci ctrl->xfer.rem_bytes -= words_to_read * QSPI_BYTES_PER_WORD; 3588c2ecf20Sopenharmony_ci ioread32_rep(ctrl->base + RD_FIFO, word_buf, words_to_read); 3598c2ecf20Sopenharmony_ci ctrl->xfer.rx_buf = word_buf + words_to_read; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (bytes_to_read) { 3638c2ecf20Sopenharmony_ci byte_buf = ctrl->xfer.rx_buf; 3648c2ecf20Sopenharmony_ci rd_fifo = readl(ctrl->base + RD_FIFO); 3658c2ecf20Sopenharmony_ci ctrl->xfer.rem_bytes -= bytes_to_read; 3668c2ecf20Sopenharmony_ci for (i = 0; i < bytes_to_read; i++) 3678c2ecf20Sopenharmony_ci *byte_buf++ = rd_fifo >> (i * BITS_PER_BYTE); 3688c2ecf20Sopenharmony_ci ctrl->xfer.rx_buf = byte_buf; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic irqreturn_t pio_write(struct qcom_qspi *ctrl) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci const void *xfer_buf = ctrl->xfer.tx_buf; 3778c2ecf20Sopenharmony_ci const int *word_buf; 3788c2ecf20Sopenharmony_ci const char *byte_buf; 3798c2ecf20Sopenharmony_ci unsigned int wr_fifo_bytes; 3808c2ecf20Sopenharmony_ci unsigned int wr_fifo_words; 3818c2ecf20Sopenharmony_ci unsigned int wr_size; 3828c2ecf20Sopenharmony_ci unsigned int rem_words; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci wr_fifo_bytes = readl(ctrl->base + PIO_XFER_STATUS); 3858c2ecf20Sopenharmony_ci wr_fifo_bytes >>= WR_FIFO_BYTES_SHFT; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci if (ctrl->xfer.rem_bytes < QSPI_BYTES_PER_WORD) { 3888c2ecf20Sopenharmony_ci /* Process the last 1-3 bytes */ 3898c2ecf20Sopenharmony_ci wr_size = min(wr_fifo_bytes, ctrl->xfer.rem_bytes); 3908c2ecf20Sopenharmony_ci ctrl->xfer.rem_bytes -= wr_size; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci byte_buf = xfer_buf; 3938c2ecf20Sopenharmony_ci while (wr_size--) 3948c2ecf20Sopenharmony_ci writel(*byte_buf++, 3958c2ecf20Sopenharmony_ci ctrl->base + PIO_DATAOUT_1B); 3968c2ecf20Sopenharmony_ci ctrl->xfer.tx_buf = byte_buf; 3978c2ecf20Sopenharmony_ci } else { 3988c2ecf20Sopenharmony_ci /* 3998c2ecf20Sopenharmony_ci * Process all the whole words; to keep things simple we'll 4008c2ecf20Sopenharmony_ci * just wait for the next interrupt to handle the last 1-3 4018c2ecf20Sopenharmony_ci * bytes if we don't have an even number of words. 4028c2ecf20Sopenharmony_ci */ 4038c2ecf20Sopenharmony_ci rem_words = ctrl->xfer.rem_bytes / QSPI_BYTES_PER_WORD; 4048c2ecf20Sopenharmony_ci wr_fifo_words = wr_fifo_bytes / QSPI_BYTES_PER_WORD; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci wr_size = min(rem_words, wr_fifo_words); 4078c2ecf20Sopenharmony_ci ctrl->xfer.rem_bytes -= wr_size * QSPI_BYTES_PER_WORD; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci word_buf = xfer_buf; 4108c2ecf20Sopenharmony_ci iowrite32_rep(ctrl->base + PIO_DATAOUT_4B, word_buf, wr_size); 4118c2ecf20Sopenharmony_ci ctrl->xfer.tx_buf = word_buf + wr_size; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistatic irqreturn_t qcom_qspi_irq(int irq, void *dev_id) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci u32 int_status; 4218c2ecf20Sopenharmony_ci struct qcom_qspi *ctrl = dev_id; 4228c2ecf20Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci spin_lock(&ctrl->lock); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci int_status = readl(ctrl->base + MSTR_INT_STATUS); 4278c2ecf20Sopenharmony_ci writel(int_status, ctrl->base + MSTR_INT_STATUS); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci if (ctrl->xfer.dir == QSPI_WRITE) { 4308c2ecf20Sopenharmony_ci if (int_status & WR_FIFO_EMPTY) 4318c2ecf20Sopenharmony_ci ret = pio_write(ctrl); 4328c2ecf20Sopenharmony_ci } else { 4338c2ecf20Sopenharmony_ci if (int_status & RESP_FIFO_RDY) 4348c2ecf20Sopenharmony_ci ret = pio_read(ctrl); 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if (int_status & QSPI_ERR_IRQS) { 4388c2ecf20Sopenharmony_ci if (int_status & RESP_FIFO_UNDERRUN) 4398c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "IRQ error: FIFO underrun\n"); 4408c2ecf20Sopenharmony_ci if (int_status & WR_FIFO_OVERRUN) 4418c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "IRQ error: FIFO overrun\n"); 4428c2ecf20Sopenharmony_ci if (int_status & HRESP_FROM_NOC_ERR) 4438c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "IRQ error: NOC response error\n"); 4448c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (!ctrl->xfer.rem_bytes) { 4488c2ecf20Sopenharmony_ci writel(0, ctrl->base + MSTR_INT_EN); 4498c2ecf20Sopenharmony_ci spi_finalize_current_transfer(dev_get_drvdata(ctrl->dev)); 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci spin_unlock(&ctrl->lock); 4538c2ecf20Sopenharmony_ci return ret; 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic int qcom_qspi_probe(struct platform_device *pdev) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci int ret; 4598c2ecf20Sopenharmony_ci struct device *dev; 4608c2ecf20Sopenharmony_ci struct spi_master *master; 4618c2ecf20Sopenharmony_ci struct qcom_qspi *ctrl; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci dev = &pdev->dev; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci master = devm_spi_alloc_master(dev, sizeof(*ctrl)); 4668c2ecf20Sopenharmony_ci if (!master) 4678c2ecf20Sopenharmony_ci return -ENOMEM; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, master); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci ctrl = spi_master_get_devdata(master); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci spin_lock_init(&ctrl->lock); 4748c2ecf20Sopenharmony_ci ctrl->dev = dev; 4758c2ecf20Sopenharmony_ci ctrl->base = devm_platform_ioremap_resource(pdev, 0); 4768c2ecf20Sopenharmony_ci if (IS_ERR(ctrl->base)) 4778c2ecf20Sopenharmony_ci return PTR_ERR(ctrl->base); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci ctrl->clks = devm_kcalloc(dev, QSPI_NUM_CLKS, 4808c2ecf20Sopenharmony_ci sizeof(*ctrl->clks), GFP_KERNEL); 4818c2ecf20Sopenharmony_ci if (!ctrl->clks) 4828c2ecf20Sopenharmony_ci return -ENOMEM; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci ctrl->clks[QSPI_CLK_CORE].id = "core"; 4858c2ecf20Sopenharmony_ci ctrl->clks[QSPI_CLK_IFACE].id = "iface"; 4868c2ecf20Sopenharmony_ci ret = devm_clk_bulk_get(dev, QSPI_NUM_CLKS, ctrl->clks); 4878c2ecf20Sopenharmony_ci if (ret) 4888c2ecf20Sopenharmony_ci return ret; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci ctrl->icc_path_cpu_to_qspi = devm_of_icc_get(dev, "qspi-config"); 4918c2ecf20Sopenharmony_ci if (IS_ERR(ctrl->icc_path_cpu_to_qspi)) 4928c2ecf20Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(ctrl->icc_path_cpu_to_qspi), 4938c2ecf20Sopenharmony_ci "Failed to get cpu path\n"); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci /* Set BW vote for register access */ 4968c2ecf20Sopenharmony_ci ret = icc_set_bw(ctrl->icc_path_cpu_to_qspi, Bps_to_icc(1000), 4978c2ecf20Sopenharmony_ci Bps_to_icc(1000)); 4988c2ecf20Sopenharmony_ci if (ret) { 4998c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "%s: ICC BW voting failed for cpu: %d\n", 5008c2ecf20Sopenharmony_ci __func__, ret); 5018c2ecf20Sopenharmony_ci return ret; 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci ret = icc_disable(ctrl->icc_path_cpu_to_qspi); 5058c2ecf20Sopenharmony_ci if (ret) { 5068c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "%s: ICC disable failed for cpu: %d\n", 5078c2ecf20Sopenharmony_ci __func__, ret); 5088c2ecf20Sopenharmony_ci return ret; 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci ret = platform_get_irq(pdev, 0); 5128c2ecf20Sopenharmony_ci if (ret < 0) 5138c2ecf20Sopenharmony_ci return ret; 5148c2ecf20Sopenharmony_ci ret = devm_request_irq(dev, ret, qcom_qspi_irq, 5158c2ecf20Sopenharmony_ci IRQF_TRIGGER_HIGH, dev_name(dev), ctrl); 5168c2ecf20Sopenharmony_ci if (ret) { 5178c2ecf20Sopenharmony_ci dev_err(dev, "Failed to request irq %d\n", ret); 5188c2ecf20Sopenharmony_ci return ret; 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci master->max_speed_hz = 300000000; 5228c2ecf20Sopenharmony_ci master->num_chipselect = QSPI_NUM_CS; 5238c2ecf20Sopenharmony_ci master->bus_num = -1; 5248c2ecf20Sopenharmony_ci master->dev.of_node = pdev->dev.of_node; 5258c2ecf20Sopenharmony_ci master->mode_bits = SPI_MODE_0 | 5268c2ecf20Sopenharmony_ci SPI_TX_DUAL | SPI_RX_DUAL | 5278c2ecf20Sopenharmony_ci SPI_TX_QUAD | SPI_RX_QUAD; 5288c2ecf20Sopenharmony_ci master->flags = SPI_MASTER_HALF_DUPLEX; 5298c2ecf20Sopenharmony_ci master->prepare_message = qcom_qspi_prepare_message; 5308c2ecf20Sopenharmony_ci master->transfer_one = qcom_qspi_transfer_one; 5318c2ecf20Sopenharmony_ci master->handle_err = qcom_qspi_handle_err; 5328c2ecf20Sopenharmony_ci master->auto_runtime_pm = true; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci ctrl->opp_table = dev_pm_opp_set_clkname(&pdev->dev, "core"); 5358c2ecf20Sopenharmony_ci if (IS_ERR(ctrl->opp_table)) 5368c2ecf20Sopenharmony_ci return PTR_ERR(ctrl->opp_table); 5378c2ecf20Sopenharmony_ci /* OPP table is optional */ 5388c2ecf20Sopenharmony_ci ret = dev_pm_opp_of_add_table(&pdev->dev); 5398c2ecf20Sopenharmony_ci if (ret && ret != -ENODEV) { 5408c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "invalid OPP table in device tree\n"); 5418c2ecf20Sopenharmony_ci goto exit_probe_put_clkname; 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci pm_runtime_use_autosuspend(dev); 5458c2ecf20Sopenharmony_ci pm_runtime_set_autosuspend_delay(dev, 250); 5468c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci ret = spi_register_master(master); 5498c2ecf20Sopenharmony_ci if (!ret) 5508c2ecf20Sopenharmony_ci return 0; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci pm_runtime_disable(dev); 5538c2ecf20Sopenharmony_ci dev_pm_opp_of_remove_table(&pdev->dev); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ciexit_probe_put_clkname: 5568c2ecf20Sopenharmony_ci dev_pm_opp_put_clkname(ctrl->opp_table); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci return ret; 5598c2ecf20Sopenharmony_ci} 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_cistatic int qcom_qspi_remove(struct platform_device *pdev) 5628c2ecf20Sopenharmony_ci{ 5638c2ecf20Sopenharmony_ci struct spi_master *master = platform_get_drvdata(pdev); 5648c2ecf20Sopenharmony_ci struct qcom_qspi *ctrl = spi_master_get_devdata(master); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci /* Unregister _before_ disabling pm_runtime() so we stop transfers */ 5678c2ecf20Sopenharmony_ci spi_unregister_master(master); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 5708c2ecf20Sopenharmony_ci dev_pm_opp_of_remove_table(&pdev->dev); 5718c2ecf20Sopenharmony_ci dev_pm_opp_put_clkname(ctrl->opp_table); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci return 0; 5748c2ecf20Sopenharmony_ci} 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_cistatic int __maybe_unused qcom_qspi_runtime_suspend(struct device *dev) 5778c2ecf20Sopenharmony_ci{ 5788c2ecf20Sopenharmony_ci struct spi_master *master = dev_get_drvdata(dev); 5798c2ecf20Sopenharmony_ci struct qcom_qspi *ctrl = spi_master_get_devdata(master); 5808c2ecf20Sopenharmony_ci int ret; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci /* Drop the performance state vote */ 5838c2ecf20Sopenharmony_ci dev_pm_opp_set_rate(dev, 0); 5848c2ecf20Sopenharmony_ci clk_bulk_disable_unprepare(QSPI_NUM_CLKS, ctrl->clks); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci ret = icc_disable(ctrl->icc_path_cpu_to_qspi); 5878c2ecf20Sopenharmony_ci if (ret) { 5888c2ecf20Sopenharmony_ci dev_err_ratelimited(ctrl->dev, "%s: ICC disable failed for cpu: %d\n", 5898c2ecf20Sopenharmony_ci __func__, ret); 5908c2ecf20Sopenharmony_ci return ret; 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci return 0; 5948c2ecf20Sopenharmony_ci} 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_cistatic int __maybe_unused qcom_qspi_runtime_resume(struct device *dev) 5978c2ecf20Sopenharmony_ci{ 5988c2ecf20Sopenharmony_ci struct spi_master *master = dev_get_drvdata(dev); 5998c2ecf20Sopenharmony_ci struct qcom_qspi *ctrl = spi_master_get_devdata(master); 6008c2ecf20Sopenharmony_ci int ret; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci ret = icc_enable(ctrl->icc_path_cpu_to_qspi); 6038c2ecf20Sopenharmony_ci if (ret) { 6048c2ecf20Sopenharmony_ci dev_err_ratelimited(ctrl->dev, "%s: ICC enable failed for cpu: %d\n", 6058c2ecf20Sopenharmony_ci __func__, ret); 6068c2ecf20Sopenharmony_ci return ret; 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci ret = clk_bulk_prepare_enable(QSPI_NUM_CLKS, ctrl->clks); 6108c2ecf20Sopenharmony_ci if (ret) 6118c2ecf20Sopenharmony_ci return ret; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci return dev_pm_opp_set_rate(dev, ctrl->last_speed * 4); 6148c2ecf20Sopenharmony_ci} 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_cistatic int __maybe_unused qcom_qspi_suspend(struct device *dev) 6178c2ecf20Sopenharmony_ci{ 6188c2ecf20Sopenharmony_ci struct spi_master *master = dev_get_drvdata(dev); 6198c2ecf20Sopenharmony_ci int ret; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci ret = spi_master_suspend(master); 6228c2ecf20Sopenharmony_ci if (ret) 6238c2ecf20Sopenharmony_ci return ret; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci ret = pm_runtime_force_suspend(dev); 6268c2ecf20Sopenharmony_ci if (ret) 6278c2ecf20Sopenharmony_ci spi_master_resume(master); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci return ret; 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_cistatic int __maybe_unused qcom_qspi_resume(struct device *dev) 6338c2ecf20Sopenharmony_ci{ 6348c2ecf20Sopenharmony_ci struct spi_master *master = dev_get_drvdata(dev); 6358c2ecf20Sopenharmony_ci int ret; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci ret = pm_runtime_force_resume(dev); 6388c2ecf20Sopenharmony_ci if (ret) 6398c2ecf20Sopenharmony_ci return ret; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci ret = spi_master_resume(master); 6428c2ecf20Sopenharmony_ci if (ret) 6438c2ecf20Sopenharmony_ci pm_runtime_force_suspend(dev); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci return ret; 6468c2ecf20Sopenharmony_ci} 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_cistatic const struct dev_pm_ops qcom_qspi_dev_pm_ops = { 6498c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(qcom_qspi_runtime_suspend, 6508c2ecf20Sopenharmony_ci qcom_qspi_runtime_resume, NULL) 6518c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(qcom_qspi_suspend, qcom_qspi_resume) 6528c2ecf20Sopenharmony_ci}; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_cistatic const struct of_device_id qcom_qspi_dt_match[] = { 6558c2ecf20Sopenharmony_ci { .compatible = "qcom,qspi-v1", }, 6568c2ecf20Sopenharmony_ci { } 6578c2ecf20Sopenharmony_ci}; 6588c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, qcom_qspi_dt_match); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_cistatic struct platform_driver qcom_qspi_driver = { 6618c2ecf20Sopenharmony_ci .driver = { 6628c2ecf20Sopenharmony_ci .name = "qcom_qspi", 6638c2ecf20Sopenharmony_ci .pm = &qcom_qspi_dev_pm_ops, 6648c2ecf20Sopenharmony_ci .of_match_table = qcom_qspi_dt_match, 6658c2ecf20Sopenharmony_ci }, 6668c2ecf20Sopenharmony_ci .probe = qcom_qspi_probe, 6678c2ecf20Sopenharmony_ci .remove = qcom_qspi_remove, 6688c2ecf20Sopenharmony_ci}; 6698c2ecf20Sopenharmony_cimodule_platform_driver(qcom_qspi_driver); 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SPI driver for QSPI cores"); 6728c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 673