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