162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Rockchip Serial Flash Controller Driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2017-2021, Rockchip Inc.
662306a36Sopenharmony_ci * Author: Shawn Lin <shawn.lin@rock-chips.com>
762306a36Sopenharmony_ci *	   Chris Morgan <macroalpha82@gmail.com>
862306a36Sopenharmony_ci *	   Jon Lin <Jon.lin@rock-chips.com>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/bitops.h>
1262306a36Sopenharmony_ci#include <linux/clk.h>
1362306a36Sopenharmony_ci#include <linux/completion.h>
1462306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1562306a36Sopenharmony_ci#include <linux/iopoll.h>
1662306a36Sopenharmony_ci#include <linux/mm.h>
1762306a36Sopenharmony_ci#include <linux/module.h>
1862306a36Sopenharmony_ci#include <linux/of.h>
1962306a36Sopenharmony_ci#include <linux/platform_device.h>
2062306a36Sopenharmony_ci#include <linux/slab.h>
2162306a36Sopenharmony_ci#include <linux/interrupt.h>
2262306a36Sopenharmony_ci#include <linux/spi/spi-mem.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/* System control */
2562306a36Sopenharmony_ci#define SFC_CTRL			0x0
2662306a36Sopenharmony_ci#define  SFC_CTRL_PHASE_SEL_NEGETIVE	BIT(1)
2762306a36Sopenharmony_ci#define  SFC_CTRL_CMD_BITS_SHIFT	8
2862306a36Sopenharmony_ci#define  SFC_CTRL_ADDR_BITS_SHIFT	10
2962306a36Sopenharmony_ci#define  SFC_CTRL_DATA_BITS_SHIFT	12
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci/* Interrupt mask */
3262306a36Sopenharmony_ci#define SFC_IMR				0x4
3362306a36Sopenharmony_ci#define  SFC_IMR_RX_FULL		BIT(0)
3462306a36Sopenharmony_ci#define  SFC_IMR_RX_UFLOW		BIT(1)
3562306a36Sopenharmony_ci#define  SFC_IMR_TX_OFLOW		BIT(2)
3662306a36Sopenharmony_ci#define  SFC_IMR_TX_EMPTY		BIT(3)
3762306a36Sopenharmony_ci#define  SFC_IMR_TRAN_FINISH		BIT(4)
3862306a36Sopenharmony_ci#define  SFC_IMR_BUS_ERR		BIT(5)
3962306a36Sopenharmony_ci#define  SFC_IMR_NSPI_ERR		BIT(6)
4062306a36Sopenharmony_ci#define  SFC_IMR_DMA			BIT(7)
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/* Interrupt clear */
4362306a36Sopenharmony_ci#define SFC_ICLR			0x8
4462306a36Sopenharmony_ci#define  SFC_ICLR_RX_FULL		BIT(0)
4562306a36Sopenharmony_ci#define  SFC_ICLR_RX_UFLOW		BIT(1)
4662306a36Sopenharmony_ci#define  SFC_ICLR_TX_OFLOW		BIT(2)
4762306a36Sopenharmony_ci#define  SFC_ICLR_TX_EMPTY		BIT(3)
4862306a36Sopenharmony_ci#define  SFC_ICLR_TRAN_FINISH		BIT(4)
4962306a36Sopenharmony_ci#define  SFC_ICLR_BUS_ERR		BIT(5)
5062306a36Sopenharmony_ci#define  SFC_ICLR_NSPI_ERR		BIT(6)
5162306a36Sopenharmony_ci#define  SFC_ICLR_DMA			BIT(7)
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci/* FIFO threshold level */
5462306a36Sopenharmony_ci#define SFC_FTLR			0xc
5562306a36Sopenharmony_ci#define  SFC_FTLR_TX_SHIFT		0
5662306a36Sopenharmony_ci#define  SFC_FTLR_TX_MASK		0x1f
5762306a36Sopenharmony_ci#define  SFC_FTLR_RX_SHIFT		8
5862306a36Sopenharmony_ci#define  SFC_FTLR_RX_MASK		0x1f
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci/* Reset FSM and FIFO */
6162306a36Sopenharmony_ci#define SFC_RCVR			0x10
6262306a36Sopenharmony_ci#define  SFC_RCVR_RESET			BIT(0)
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/* Enhanced mode */
6562306a36Sopenharmony_ci#define SFC_AX				0x14
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci/* Address Bit number */
6862306a36Sopenharmony_ci#define SFC_ABIT			0x18
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci/* Interrupt status */
7162306a36Sopenharmony_ci#define SFC_ISR				0x1c
7262306a36Sopenharmony_ci#define  SFC_ISR_RX_FULL_SHIFT		BIT(0)
7362306a36Sopenharmony_ci#define  SFC_ISR_RX_UFLOW_SHIFT		BIT(1)
7462306a36Sopenharmony_ci#define  SFC_ISR_TX_OFLOW_SHIFT		BIT(2)
7562306a36Sopenharmony_ci#define  SFC_ISR_TX_EMPTY_SHIFT		BIT(3)
7662306a36Sopenharmony_ci#define  SFC_ISR_TX_FINISH_SHIFT	BIT(4)
7762306a36Sopenharmony_ci#define  SFC_ISR_BUS_ERR_SHIFT		BIT(5)
7862306a36Sopenharmony_ci#define  SFC_ISR_NSPI_ERR_SHIFT		BIT(6)
7962306a36Sopenharmony_ci#define  SFC_ISR_DMA_SHIFT		BIT(7)
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci/* FIFO status */
8262306a36Sopenharmony_ci#define SFC_FSR				0x20
8362306a36Sopenharmony_ci#define  SFC_FSR_TX_IS_FULL		BIT(0)
8462306a36Sopenharmony_ci#define  SFC_FSR_TX_IS_EMPTY		BIT(1)
8562306a36Sopenharmony_ci#define  SFC_FSR_RX_IS_EMPTY		BIT(2)
8662306a36Sopenharmony_ci#define  SFC_FSR_RX_IS_FULL		BIT(3)
8762306a36Sopenharmony_ci#define  SFC_FSR_TXLV_MASK		GENMASK(12, 8)
8862306a36Sopenharmony_ci#define  SFC_FSR_TXLV_SHIFT		8
8962306a36Sopenharmony_ci#define  SFC_FSR_RXLV_MASK		GENMASK(20, 16)
9062306a36Sopenharmony_ci#define  SFC_FSR_RXLV_SHIFT		16
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci/* FSM status */
9362306a36Sopenharmony_ci#define SFC_SR				0x24
9462306a36Sopenharmony_ci#define  SFC_SR_IS_IDLE			0x0
9562306a36Sopenharmony_ci#define  SFC_SR_IS_BUSY			0x1
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci/* Raw interrupt status */
9862306a36Sopenharmony_ci#define SFC_RISR			0x28
9962306a36Sopenharmony_ci#define  SFC_RISR_RX_FULL		BIT(0)
10062306a36Sopenharmony_ci#define  SFC_RISR_RX_UNDERFLOW		BIT(1)
10162306a36Sopenharmony_ci#define  SFC_RISR_TX_OVERFLOW		BIT(2)
10262306a36Sopenharmony_ci#define  SFC_RISR_TX_EMPTY		BIT(3)
10362306a36Sopenharmony_ci#define  SFC_RISR_TRAN_FINISH		BIT(4)
10462306a36Sopenharmony_ci#define  SFC_RISR_BUS_ERR		BIT(5)
10562306a36Sopenharmony_ci#define  SFC_RISR_NSPI_ERR		BIT(6)
10662306a36Sopenharmony_ci#define  SFC_RISR_DMA			BIT(7)
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci/* Version */
10962306a36Sopenharmony_ci#define SFC_VER				0x2C
11062306a36Sopenharmony_ci#define  SFC_VER_3			0x3
11162306a36Sopenharmony_ci#define  SFC_VER_4			0x4
11262306a36Sopenharmony_ci#define  SFC_VER_5			0x5
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci/* Delay line controller resiter */
11562306a36Sopenharmony_ci#define SFC_DLL_CTRL0			0x3C
11662306a36Sopenharmony_ci#define SFC_DLL_CTRL0_SCLK_SMP_DLL	BIT(15)
11762306a36Sopenharmony_ci#define SFC_DLL_CTRL0_DLL_MAX_VER4	0xFFU
11862306a36Sopenharmony_ci#define SFC_DLL_CTRL0_DLL_MAX_VER5	0x1FFU
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci/* Master trigger */
12162306a36Sopenharmony_ci#define SFC_DMA_TRIGGER			0x80
12262306a36Sopenharmony_ci#define SFC_DMA_TRIGGER_START		1
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci/* Src or Dst addr for master */
12562306a36Sopenharmony_ci#define SFC_DMA_ADDR			0x84
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci/* Length control register extension 32GB */
12862306a36Sopenharmony_ci#define SFC_LEN_CTRL			0x88
12962306a36Sopenharmony_ci#define SFC_LEN_CTRL_TRB_SEL		1
13062306a36Sopenharmony_ci#define SFC_LEN_EXT			0x8C
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci/* Command */
13362306a36Sopenharmony_ci#define SFC_CMD				0x100
13462306a36Sopenharmony_ci#define  SFC_CMD_IDX_SHIFT		0
13562306a36Sopenharmony_ci#define  SFC_CMD_DUMMY_SHIFT		8
13662306a36Sopenharmony_ci#define  SFC_CMD_DIR_SHIFT		12
13762306a36Sopenharmony_ci#define  SFC_CMD_DIR_RD			0
13862306a36Sopenharmony_ci#define  SFC_CMD_DIR_WR			1
13962306a36Sopenharmony_ci#define  SFC_CMD_ADDR_SHIFT		14
14062306a36Sopenharmony_ci#define  SFC_CMD_ADDR_0BITS		0
14162306a36Sopenharmony_ci#define  SFC_CMD_ADDR_24BITS		1
14262306a36Sopenharmony_ci#define  SFC_CMD_ADDR_32BITS		2
14362306a36Sopenharmony_ci#define  SFC_CMD_ADDR_XBITS		3
14462306a36Sopenharmony_ci#define  SFC_CMD_TRAN_BYTES_SHIFT	16
14562306a36Sopenharmony_ci#define  SFC_CMD_CS_SHIFT		30
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci/* Address */
14862306a36Sopenharmony_ci#define SFC_ADDR			0x104
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci/* Data */
15162306a36Sopenharmony_ci#define SFC_DATA			0x108
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci/* The controller and documentation reports that it supports up to 4 CS
15462306a36Sopenharmony_ci * devices (0-3), however I have only been able to test a single CS (CS 0)
15562306a36Sopenharmony_ci * due to the configuration of my device.
15662306a36Sopenharmony_ci */
15762306a36Sopenharmony_ci#define SFC_MAX_CHIPSELECT_NUM		4
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci/* The SFC can transfer max 16KB - 1 at one time
16062306a36Sopenharmony_ci * we set it to 15.5KB here for alignment.
16162306a36Sopenharmony_ci */
16262306a36Sopenharmony_ci#define SFC_MAX_IOSIZE_VER3		(512 * 31)
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci/* DMA is only enabled for large data transmission */
16562306a36Sopenharmony_ci#define SFC_DMA_TRANS_THRETHOLD		(0x40)
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci/* Maximum clock values from datasheet suggest keeping clock value under
16862306a36Sopenharmony_ci * 150MHz. No minimum or average value is suggested.
16962306a36Sopenharmony_ci */
17062306a36Sopenharmony_ci#define SFC_MAX_SPEED		(150 * 1000 * 1000)
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistruct rockchip_sfc {
17362306a36Sopenharmony_ci	struct device *dev;
17462306a36Sopenharmony_ci	void __iomem *regbase;
17562306a36Sopenharmony_ci	struct clk *hclk;
17662306a36Sopenharmony_ci	struct clk *clk;
17762306a36Sopenharmony_ci	u32 frequency;
17862306a36Sopenharmony_ci	/* virtual mapped addr for dma_buffer */
17962306a36Sopenharmony_ci	void *buffer;
18062306a36Sopenharmony_ci	dma_addr_t dma_buffer;
18162306a36Sopenharmony_ci	struct completion cp;
18262306a36Sopenharmony_ci	bool use_dma;
18362306a36Sopenharmony_ci	u32 max_iosize;
18462306a36Sopenharmony_ci	u16 version;
18562306a36Sopenharmony_ci};
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic int rockchip_sfc_reset(struct rockchip_sfc *sfc)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	int err;
19062306a36Sopenharmony_ci	u32 status;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	writel_relaxed(SFC_RCVR_RESET, sfc->regbase + SFC_RCVR);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	err = readl_poll_timeout(sfc->regbase + SFC_RCVR, status,
19562306a36Sopenharmony_ci				 !(status & SFC_RCVR_RESET), 20,
19662306a36Sopenharmony_ci				 jiffies_to_usecs(HZ));
19762306a36Sopenharmony_ci	if (err)
19862306a36Sopenharmony_ci		dev_err(sfc->dev, "SFC reset never finished\n");
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	/* Still need to clear the masked interrupt from RISR */
20162306a36Sopenharmony_ci	writel_relaxed(0xFFFFFFFF, sfc->regbase + SFC_ICLR);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	dev_dbg(sfc->dev, "reset\n");
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	return err;
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic u16 rockchip_sfc_get_version(struct rockchip_sfc *sfc)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	return  (u16)(readl(sfc->regbase + SFC_VER) & 0xffff);
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic u32 rockchip_sfc_get_max_iosize(struct rockchip_sfc *sfc)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	return SFC_MAX_IOSIZE_VER3;
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic void rockchip_sfc_irq_unmask(struct rockchip_sfc *sfc, u32 mask)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	u32 reg;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	/* Enable transfer complete interrupt */
22362306a36Sopenharmony_ci	reg = readl(sfc->regbase + SFC_IMR);
22462306a36Sopenharmony_ci	reg &= ~mask;
22562306a36Sopenharmony_ci	writel(reg, sfc->regbase + SFC_IMR);
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic void rockchip_sfc_irq_mask(struct rockchip_sfc *sfc, u32 mask)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	u32 reg;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	/* Disable transfer finish interrupt */
23362306a36Sopenharmony_ci	reg = readl(sfc->regbase + SFC_IMR);
23462306a36Sopenharmony_ci	reg |= mask;
23562306a36Sopenharmony_ci	writel(reg, sfc->regbase + SFC_IMR);
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic int rockchip_sfc_init(struct rockchip_sfc *sfc)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	writel(0, sfc->regbase + SFC_CTRL);
24162306a36Sopenharmony_ci	writel(0xFFFFFFFF, sfc->regbase + SFC_ICLR);
24262306a36Sopenharmony_ci	rockchip_sfc_irq_mask(sfc, 0xFFFFFFFF);
24362306a36Sopenharmony_ci	if (rockchip_sfc_get_version(sfc) >= SFC_VER_4)
24462306a36Sopenharmony_ci		writel(SFC_LEN_CTRL_TRB_SEL, sfc->regbase + SFC_LEN_CTRL);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	return 0;
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistatic int rockchip_sfc_wait_txfifo_ready(struct rockchip_sfc *sfc, u32 timeout_us)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	int ret = 0;
25262306a36Sopenharmony_ci	u32 status;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	ret = readl_poll_timeout(sfc->regbase + SFC_FSR, status,
25562306a36Sopenharmony_ci				 status & SFC_FSR_TXLV_MASK, 0,
25662306a36Sopenharmony_ci				 timeout_us);
25762306a36Sopenharmony_ci	if (ret) {
25862306a36Sopenharmony_ci		dev_dbg(sfc->dev, "sfc wait tx fifo timeout\n");
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci		return -ETIMEDOUT;
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	return (status & SFC_FSR_TXLV_MASK) >> SFC_FSR_TXLV_SHIFT;
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic int rockchip_sfc_wait_rxfifo_ready(struct rockchip_sfc *sfc, u32 timeout_us)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	int ret = 0;
26962306a36Sopenharmony_ci	u32 status;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	ret = readl_poll_timeout(sfc->regbase + SFC_FSR, status,
27262306a36Sopenharmony_ci				 status & SFC_FSR_RXLV_MASK, 0,
27362306a36Sopenharmony_ci				 timeout_us);
27462306a36Sopenharmony_ci	if (ret) {
27562306a36Sopenharmony_ci		dev_dbg(sfc->dev, "sfc wait rx fifo timeout\n");
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci		return -ETIMEDOUT;
27862306a36Sopenharmony_ci	}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	return (status & SFC_FSR_RXLV_MASK) >> SFC_FSR_RXLV_SHIFT;
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic void rockchip_sfc_adjust_op_work(struct spi_mem_op *op)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	if (unlikely(op->dummy.nbytes && !op->addr.nbytes)) {
28662306a36Sopenharmony_ci		/*
28762306a36Sopenharmony_ci		 * SFC not support output DUMMY cycles right after CMD cycles, so
28862306a36Sopenharmony_ci		 * treat it as ADDR cycles.
28962306a36Sopenharmony_ci		 */
29062306a36Sopenharmony_ci		op->addr.nbytes = op->dummy.nbytes;
29162306a36Sopenharmony_ci		op->addr.buswidth = op->dummy.buswidth;
29262306a36Sopenharmony_ci		op->addr.val = 0xFFFFFFFFF;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci		op->dummy.nbytes = 0;
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic int rockchip_sfc_xfer_setup(struct rockchip_sfc *sfc,
29962306a36Sopenharmony_ci				   struct spi_mem *mem,
30062306a36Sopenharmony_ci				   const struct spi_mem_op *op,
30162306a36Sopenharmony_ci				   u32 len)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	u32 ctrl = 0, cmd = 0;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	/* set CMD */
30662306a36Sopenharmony_ci	cmd = op->cmd.opcode;
30762306a36Sopenharmony_ci	ctrl |= ((op->cmd.buswidth >> 1) << SFC_CTRL_CMD_BITS_SHIFT);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	/* set ADDR */
31062306a36Sopenharmony_ci	if (op->addr.nbytes) {
31162306a36Sopenharmony_ci		if (op->addr.nbytes == 4) {
31262306a36Sopenharmony_ci			cmd |= SFC_CMD_ADDR_32BITS << SFC_CMD_ADDR_SHIFT;
31362306a36Sopenharmony_ci		} else if (op->addr.nbytes == 3) {
31462306a36Sopenharmony_ci			cmd |= SFC_CMD_ADDR_24BITS << SFC_CMD_ADDR_SHIFT;
31562306a36Sopenharmony_ci		} else {
31662306a36Sopenharmony_ci			cmd |= SFC_CMD_ADDR_XBITS << SFC_CMD_ADDR_SHIFT;
31762306a36Sopenharmony_ci			writel(op->addr.nbytes * 8 - 1, sfc->regbase + SFC_ABIT);
31862306a36Sopenharmony_ci		}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci		ctrl |= ((op->addr.buswidth >> 1) << SFC_CTRL_ADDR_BITS_SHIFT);
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	/* set DUMMY */
32462306a36Sopenharmony_ci	if (op->dummy.nbytes) {
32562306a36Sopenharmony_ci		if (op->dummy.buswidth == 4)
32662306a36Sopenharmony_ci			cmd |= op->dummy.nbytes * 2 << SFC_CMD_DUMMY_SHIFT;
32762306a36Sopenharmony_ci		else if (op->dummy.buswidth == 2)
32862306a36Sopenharmony_ci			cmd |= op->dummy.nbytes * 4 << SFC_CMD_DUMMY_SHIFT;
32962306a36Sopenharmony_ci		else
33062306a36Sopenharmony_ci			cmd |= op->dummy.nbytes * 8 << SFC_CMD_DUMMY_SHIFT;
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	/* set DATA */
33462306a36Sopenharmony_ci	if (sfc->version >= SFC_VER_4) /* Clear it if no data to transfer */
33562306a36Sopenharmony_ci		writel(len, sfc->regbase + SFC_LEN_EXT);
33662306a36Sopenharmony_ci	else
33762306a36Sopenharmony_ci		cmd |= len << SFC_CMD_TRAN_BYTES_SHIFT;
33862306a36Sopenharmony_ci	if (len) {
33962306a36Sopenharmony_ci		if (op->data.dir == SPI_MEM_DATA_OUT)
34062306a36Sopenharmony_ci			cmd |= SFC_CMD_DIR_WR << SFC_CMD_DIR_SHIFT;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci		ctrl |= ((op->data.buswidth >> 1) << SFC_CTRL_DATA_BITS_SHIFT);
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci	if (!len && op->addr.nbytes)
34562306a36Sopenharmony_ci		cmd |= SFC_CMD_DIR_WR << SFC_CMD_DIR_SHIFT;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	/* set the Controller */
34862306a36Sopenharmony_ci	ctrl |= SFC_CTRL_PHASE_SEL_NEGETIVE;
34962306a36Sopenharmony_ci	cmd |= spi_get_chipselect(mem->spi, 0) << SFC_CMD_CS_SHIFT;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	dev_dbg(sfc->dev, "sfc addr.nbytes=%x(x%d) dummy.nbytes=%x(x%d)\n",
35262306a36Sopenharmony_ci		op->addr.nbytes, op->addr.buswidth,
35362306a36Sopenharmony_ci		op->dummy.nbytes, op->dummy.buswidth);
35462306a36Sopenharmony_ci	dev_dbg(sfc->dev, "sfc ctrl=%x cmd=%x addr=%llx len=%x\n",
35562306a36Sopenharmony_ci		ctrl, cmd, op->addr.val, len);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	writel(ctrl, sfc->regbase + SFC_CTRL);
35862306a36Sopenharmony_ci	writel(cmd, sfc->regbase + SFC_CMD);
35962306a36Sopenharmony_ci	if (op->addr.nbytes)
36062306a36Sopenharmony_ci		writel(op->addr.val, sfc->regbase + SFC_ADDR);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	return 0;
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_cistatic int rockchip_sfc_write_fifo(struct rockchip_sfc *sfc, const u8 *buf, int len)
36662306a36Sopenharmony_ci{
36762306a36Sopenharmony_ci	u8 bytes = len & 0x3;
36862306a36Sopenharmony_ci	u32 dwords;
36962306a36Sopenharmony_ci	int tx_level;
37062306a36Sopenharmony_ci	u32 write_words;
37162306a36Sopenharmony_ci	u32 tmp = 0;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	dwords = len >> 2;
37462306a36Sopenharmony_ci	while (dwords) {
37562306a36Sopenharmony_ci		tx_level = rockchip_sfc_wait_txfifo_ready(sfc, 1000);
37662306a36Sopenharmony_ci		if (tx_level < 0)
37762306a36Sopenharmony_ci			return tx_level;
37862306a36Sopenharmony_ci		write_words = min_t(u32, tx_level, dwords);
37962306a36Sopenharmony_ci		iowrite32_rep(sfc->regbase + SFC_DATA, buf, write_words);
38062306a36Sopenharmony_ci		buf += write_words << 2;
38162306a36Sopenharmony_ci		dwords -= write_words;
38262306a36Sopenharmony_ci	}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	/* write the rest non word aligned bytes */
38562306a36Sopenharmony_ci	if (bytes) {
38662306a36Sopenharmony_ci		tx_level = rockchip_sfc_wait_txfifo_ready(sfc, 1000);
38762306a36Sopenharmony_ci		if (tx_level < 0)
38862306a36Sopenharmony_ci			return tx_level;
38962306a36Sopenharmony_ci		memcpy(&tmp, buf, bytes);
39062306a36Sopenharmony_ci		writel(tmp, sfc->regbase + SFC_DATA);
39162306a36Sopenharmony_ci	}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	return len;
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_cistatic int rockchip_sfc_read_fifo(struct rockchip_sfc *sfc, u8 *buf, int len)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	u8 bytes = len & 0x3;
39962306a36Sopenharmony_ci	u32 dwords;
40062306a36Sopenharmony_ci	u8 read_words;
40162306a36Sopenharmony_ci	int rx_level;
40262306a36Sopenharmony_ci	int tmp;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	/* word aligned access only */
40562306a36Sopenharmony_ci	dwords = len >> 2;
40662306a36Sopenharmony_ci	while (dwords) {
40762306a36Sopenharmony_ci		rx_level = rockchip_sfc_wait_rxfifo_ready(sfc, 1000);
40862306a36Sopenharmony_ci		if (rx_level < 0)
40962306a36Sopenharmony_ci			return rx_level;
41062306a36Sopenharmony_ci		read_words = min_t(u32, rx_level, dwords);
41162306a36Sopenharmony_ci		ioread32_rep(sfc->regbase + SFC_DATA, buf, read_words);
41262306a36Sopenharmony_ci		buf += read_words << 2;
41362306a36Sopenharmony_ci		dwords -= read_words;
41462306a36Sopenharmony_ci	}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	/* read the rest non word aligned bytes */
41762306a36Sopenharmony_ci	if (bytes) {
41862306a36Sopenharmony_ci		rx_level = rockchip_sfc_wait_rxfifo_ready(sfc, 1000);
41962306a36Sopenharmony_ci		if (rx_level < 0)
42062306a36Sopenharmony_ci			return rx_level;
42162306a36Sopenharmony_ci		tmp = readl(sfc->regbase + SFC_DATA);
42262306a36Sopenharmony_ci		memcpy(buf, &tmp, bytes);
42362306a36Sopenharmony_ci	}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	return len;
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_cistatic int rockchip_sfc_fifo_transfer_dma(struct rockchip_sfc *sfc, dma_addr_t dma_buf, size_t len)
42962306a36Sopenharmony_ci{
43062306a36Sopenharmony_ci	writel(0xFFFFFFFF, sfc->regbase + SFC_ICLR);
43162306a36Sopenharmony_ci	writel((u32)dma_buf, sfc->regbase + SFC_DMA_ADDR);
43262306a36Sopenharmony_ci	writel(SFC_DMA_TRIGGER_START, sfc->regbase + SFC_DMA_TRIGGER);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	return len;
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_cistatic int rockchip_sfc_xfer_data_poll(struct rockchip_sfc *sfc,
43862306a36Sopenharmony_ci				       const struct spi_mem_op *op, u32 len)
43962306a36Sopenharmony_ci{
44062306a36Sopenharmony_ci	dev_dbg(sfc->dev, "sfc xfer_poll len=%x\n", len);
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	if (op->data.dir == SPI_MEM_DATA_OUT)
44362306a36Sopenharmony_ci		return rockchip_sfc_write_fifo(sfc, op->data.buf.out, len);
44462306a36Sopenharmony_ci	else
44562306a36Sopenharmony_ci		return rockchip_sfc_read_fifo(sfc, op->data.buf.in, len);
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_cistatic int rockchip_sfc_xfer_data_dma(struct rockchip_sfc *sfc,
44962306a36Sopenharmony_ci				      const struct spi_mem_op *op, u32 len)
45062306a36Sopenharmony_ci{
45162306a36Sopenharmony_ci	int ret;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	dev_dbg(sfc->dev, "sfc xfer_dma len=%x\n", len);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	if (op->data.dir == SPI_MEM_DATA_OUT)
45662306a36Sopenharmony_ci		memcpy(sfc->buffer, op->data.buf.out, len);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	ret = rockchip_sfc_fifo_transfer_dma(sfc, sfc->dma_buffer, len);
45962306a36Sopenharmony_ci	if (!wait_for_completion_timeout(&sfc->cp, msecs_to_jiffies(2000))) {
46062306a36Sopenharmony_ci		dev_err(sfc->dev, "DMA wait for transfer finish timeout\n");
46162306a36Sopenharmony_ci		ret = -ETIMEDOUT;
46262306a36Sopenharmony_ci	}
46362306a36Sopenharmony_ci	rockchip_sfc_irq_mask(sfc, SFC_IMR_DMA);
46462306a36Sopenharmony_ci	if (op->data.dir == SPI_MEM_DATA_IN)
46562306a36Sopenharmony_ci		memcpy(op->data.buf.in, sfc->buffer, len);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	return ret;
46862306a36Sopenharmony_ci}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_cistatic int rockchip_sfc_xfer_done(struct rockchip_sfc *sfc, u32 timeout_us)
47162306a36Sopenharmony_ci{
47262306a36Sopenharmony_ci	int ret = 0;
47362306a36Sopenharmony_ci	u32 status;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	ret = readl_poll_timeout(sfc->regbase + SFC_SR, status,
47662306a36Sopenharmony_ci				 !(status & SFC_SR_IS_BUSY),
47762306a36Sopenharmony_ci				 20, timeout_us);
47862306a36Sopenharmony_ci	if (ret) {
47962306a36Sopenharmony_ci		dev_err(sfc->dev, "wait sfc idle timeout\n");
48062306a36Sopenharmony_ci		rockchip_sfc_reset(sfc);
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci		ret = -EIO;
48362306a36Sopenharmony_ci	}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	return ret;
48662306a36Sopenharmony_ci}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_cistatic int rockchip_sfc_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
48962306a36Sopenharmony_ci{
49062306a36Sopenharmony_ci	struct rockchip_sfc *sfc = spi_controller_get_devdata(mem->spi->controller);
49162306a36Sopenharmony_ci	u32 len = op->data.nbytes;
49262306a36Sopenharmony_ci	int ret;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	if (unlikely(mem->spi->max_speed_hz != sfc->frequency)) {
49562306a36Sopenharmony_ci		ret = clk_set_rate(sfc->clk, mem->spi->max_speed_hz);
49662306a36Sopenharmony_ci		if (ret)
49762306a36Sopenharmony_ci			return ret;
49862306a36Sopenharmony_ci		sfc->frequency = mem->spi->max_speed_hz;
49962306a36Sopenharmony_ci		dev_dbg(sfc->dev, "set_freq=%dHz real_freq=%ldHz\n",
50062306a36Sopenharmony_ci			sfc->frequency, clk_get_rate(sfc->clk));
50162306a36Sopenharmony_ci	}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	rockchip_sfc_adjust_op_work((struct spi_mem_op *)op);
50462306a36Sopenharmony_ci	rockchip_sfc_xfer_setup(sfc, mem, op, len);
50562306a36Sopenharmony_ci	if (len) {
50662306a36Sopenharmony_ci		if (likely(sfc->use_dma) && len >= SFC_DMA_TRANS_THRETHOLD) {
50762306a36Sopenharmony_ci			init_completion(&sfc->cp);
50862306a36Sopenharmony_ci			rockchip_sfc_irq_unmask(sfc, SFC_IMR_DMA);
50962306a36Sopenharmony_ci			ret = rockchip_sfc_xfer_data_dma(sfc, op, len);
51062306a36Sopenharmony_ci		} else {
51162306a36Sopenharmony_ci			ret = rockchip_sfc_xfer_data_poll(sfc, op, len);
51262306a36Sopenharmony_ci		}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci		if (ret != len) {
51562306a36Sopenharmony_ci			dev_err(sfc->dev, "xfer data failed ret %d dir %d\n", ret, op->data.dir);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci			return -EIO;
51862306a36Sopenharmony_ci		}
51962306a36Sopenharmony_ci	}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	return rockchip_sfc_xfer_done(sfc, 100000);
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_cistatic int rockchip_sfc_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
52562306a36Sopenharmony_ci{
52662306a36Sopenharmony_ci	struct rockchip_sfc *sfc = spi_controller_get_devdata(mem->spi->controller);
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	op->data.nbytes = min(op->data.nbytes, sfc->max_iosize);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	return 0;
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_cistatic const struct spi_controller_mem_ops rockchip_sfc_mem_ops = {
53462306a36Sopenharmony_ci	.exec_op = rockchip_sfc_exec_mem_op,
53562306a36Sopenharmony_ci	.adjust_op_size = rockchip_sfc_adjust_op_size,
53662306a36Sopenharmony_ci};
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_cistatic irqreturn_t rockchip_sfc_irq_handler(int irq, void *dev_id)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	struct rockchip_sfc *sfc = dev_id;
54162306a36Sopenharmony_ci	u32 reg;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	reg = readl(sfc->regbase + SFC_RISR);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	/* Clear interrupt */
54662306a36Sopenharmony_ci	writel_relaxed(reg, sfc->regbase + SFC_ICLR);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	if (reg & SFC_RISR_DMA) {
54962306a36Sopenharmony_ci		complete(&sfc->cp);
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci		return IRQ_HANDLED;
55262306a36Sopenharmony_ci	}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	return IRQ_NONE;
55562306a36Sopenharmony_ci}
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_cistatic int rockchip_sfc_probe(struct platform_device *pdev)
55862306a36Sopenharmony_ci{
55962306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
56062306a36Sopenharmony_ci	struct spi_controller *host;
56162306a36Sopenharmony_ci	struct rockchip_sfc *sfc;
56262306a36Sopenharmony_ci	int ret;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	host = devm_spi_alloc_host(&pdev->dev, sizeof(*sfc));
56562306a36Sopenharmony_ci	if (!host)
56662306a36Sopenharmony_ci		return -ENOMEM;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	host->flags = SPI_CONTROLLER_HALF_DUPLEX;
56962306a36Sopenharmony_ci	host->mem_ops = &rockchip_sfc_mem_ops;
57062306a36Sopenharmony_ci	host->dev.of_node = pdev->dev.of_node;
57162306a36Sopenharmony_ci	host->mode_bits = SPI_TX_QUAD | SPI_TX_DUAL | SPI_RX_QUAD | SPI_RX_DUAL;
57262306a36Sopenharmony_ci	host->max_speed_hz = SFC_MAX_SPEED;
57362306a36Sopenharmony_ci	host->num_chipselect = SFC_MAX_CHIPSELECT_NUM;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	sfc = spi_controller_get_devdata(host);
57662306a36Sopenharmony_ci	sfc->dev = dev;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	sfc->regbase = devm_platform_ioremap_resource(pdev, 0);
57962306a36Sopenharmony_ci	if (IS_ERR(sfc->regbase))
58062306a36Sopenharmony_ci		return PTR_ERR(sfc->regbase);
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	sfc->clk = devm_clk_get(&pdev->dev, "clk_sfc");
58362306a36Sopenharmony_ci	if (IS_ERR(sfc->clk)) {
58462306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to get sfc interface clk\n");
58562306a36Sopenharmony_ci		return PTR_ERR(sfc->clk);
58662306a36Sopenharmony_ci	}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	sfc->hclk = devm_clk_get(&pdev->dev, "hclk_sfc");
58962306a36Sopenharmony_ci	if (IS_ERR(sfc->hclk)) {
59062306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to get sfc ahb clk\n");
59162306a36Sopenharmony_ci		return PTR_ERR(sfc->hclk);
59262306a36Sopenharmony_ci	}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	sfc->use_dma = !of_property_read_bool(sfc->dev->of_node,
59562306a36Sopenharmony_ci					      "rockchip,sfc-no-dma");
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	if (sfc->use_dma) {
59862306a36Sopenharmony_ci		ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
59962306a36Sopenharmony_ci		if (ret) {
60062306a36Sopenharmony_ci			dev_warn(dev, "Unable to set dma mask\n");
60162306a36Sopenharmony_ci			return ret;
60262306a36Sopenharmony_ci		}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci		sfc->buffer = dmam_alloc_coherent(dev, SFC_MAX_IOSIZE_VER3,
60562306a36Sopenharmony_ci						  &sfc->dma_buffer,
60662306a36Sopenharmony_ci						  GFP_KERNEL);
60762306a36Sopenharmony_ci		if (!sfc->buffer)
60862306a36Sopenharmony_ci			return -ENOMEM;
60962306a36Sopenharmony_ci	}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	ret = clk_prepare_enable(sfc->hclk);
61262306a36Sopenharmony_ci	if (ret) {
61362306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to enable ahb clk\n");
61462306a36Sopenharmony_ci		goto err_hclk;
61562306a36Sopenharmony_ci	}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	ret = clk_prepare_enable(sfc->clk);
61862306a36Sopenharmony_ci	if (ret) {
61962306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to enable interface clk\n");
62062306a36Sopenharmony_ci		goto err_clk;
62162306a36Sopenharmony_ci	}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	/* Find the irq */
62462306a36Sopenharmony_ci	ret = platform_get_irq(pdev, 0);
62562306a36Sopenharmony_ci	if (ret < 0)
62662306a36Sopenharmony_ci		goto err_irq;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	ret = devm_request_irq(dev, ret, rockchip_sfc_irq_handler,
62962306a36Sopenharmony_ci			       0, pdev->name, sfc);
63062306a36Sopenharmony_ci	if (ret) {
63162306a36Sopenharmony_ci		dev_err(dev, "Failed to request irq\n");
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci		goto err_irq;
63462306a36Sopenharmony_ci	}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	ret = rockchip_sfc_init(sfc);
63762306a36Sopenharmony_ci	if (ret)
63862306a36Sopenharmony_ci		goto err_irq;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	sfc->max_iosize = rockchip_sfc_get_max_iosize(sfc);
64162306a36Sopenharmony_ci	sfc->version = rockchip_sfc_get_version(sfc);
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	ret = spi_register_controller(host);
64462306a36Sopenharmony_ci	if (ret)
64562306a36Sopenharmony_ci		goto err_irq;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	return 0;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_cierr_irq:
65062306a36Sopenharmony_ci	clk_disable_unprepare(sfc->clk);
65162306a36Sopenharmony_cierr_clk:
65262306a36Sopenharmony_ci	clk_disable_unprepare(sfc->hclk);
65362306a36Sopenharmony_cierr_hclk:
65462306a36Sopenharmony_ci	return ret;
65562306a36Sopenharmony_ci}
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_cistatic void rockchip_sfc_remove(struct platform_device *pdev)
65862306a36Sopenharmony_ci{
65962306a36Sopenharmony_ci	struct spi_controller *host = platform_get_drvdata(pdev);
66062306a36Sopenharmony_ci	struct rockchip_sfc *sfc = platform_get_drvdata(pdev);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	spi_unregister_controller(host);
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	clk_disable_unprepare(sfc->clk);
66562306a36Sopenharmony_ci	clk_disable_unprepare(sfc->hclk);
66662306a36Sopenharmony_ci}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_cistatic const struct of_device_id rockchip_sfc_dt_ids[] = {
66962306a36Sopenharmony_ci	{ .compatible = "rockchip,sfc"},
67062306a36Sopenharmony_ci	{ /* sentinel */ }
67162306a36Sopenharmony_ci};
67262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, rockchip_sfc_dt_ids);
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_cistatic struct platform_driver rockchip_sfc_driver = {
67562306a36Sopenharmony_ci	.driver = {
67662306a36Sopenharmony_ci		.name	= "rockchip-sfc",
67762306a36Sopenharmony_ci		.of_match_table = rockchip_sfc_dt_ids,
67862306a36Sopenharmony_ci	},
67962306a36Sopenharmony_ci	.probe	= rockchip_sfc_probe,
68062306a36Sopenharmony_ci	.remove_new = rockchip_sfc_remove,
68162306a36Sopenharmony_ci};
68262306a36Sopenharmony_cimodule_platform_driver(rockchip_sfc_driver);
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
68562306a36Sopenharmony_ciMODULE_DESCRIPTION("Rockchip Serial Flash Controller Driver");
68662306a36Sopenharmony_ciMODULE_AUTHOR("Shawn Lin <shawn.lin@rock-chips.com>");
68762306a36Sopenharmony_ciMODULE_AUTHOR("Chris Morgan <macromorgan@hotmail.com>");
68862306a36Sopenharmony_ciMODULE_AUTHOR("Jon Lin <Jon.lin@rock-chips.com>");
689