18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2012 - 2014 Allwinner Tech
48c2ecf20Sopenharmony_ci * Pan Nan <pannan@allwinnertech.com>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 2014 Maxime Ripard
78c2ecf20Sopenharmony_ci * Maxime Ripard <maxime.ripard@free-electrons.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/bitfield.h>
118c2ecf20Sopenharmony_ci#include <linux/clk.h>
128c2ecf20Sopenharmony_ci#include <linux/delay.h>
138c2ecf20Sopenharmony_ci#include <linux/device.h>
148c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
158c2ecf20Sopenharmony_ci#include <linux/io.h>
168c2ecf20Sopenharmony_ci#include <linux/module.h>
178c2ecf20Sopenharmony_ci#include <linux/of_device.h>
188c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
198c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
208c2ecf20Sopenharmony_ci#include <linux/reset.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include <linux/spi/spi.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define SUN6I_FIFO_DEPTH		128
258c2ecf20Sopenharmony_ci#define SUN8I_FIFO_DEPTH		64
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define SUN6I_GBL_CTL_REG		0x04
288c2ecf20Sopenharmony_ci#define SUN6I_GBL_CTL_BUS_ENABLE		BIT(0)
298c2ecf20Sopenharmony_ci#define SUN6I_GBL_CTL_MASTER			BIT(1)
308c2ecf20Sopenharmony_ci#define SUN6I_GBL_CTL_TP			BIT(7)
318c2ecf20Sopenharmony_ci#define SUN6I_GBL_CTL_RST			BIT(31)
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define SUN6I_TFR_CTL_REG		0x08
348c2ecf20Sopenharmony_ci#define SUN6I_TFR_CTL_CPHA			BIT(0)
358c2ecf20Sopenharmony_ci#define SUN6I_TFR_CTL_CPOL			BIT(1)
368c2ecf20Sopenharmony_ci#define SUN6I_TFR_CTL_SPOL			BIT(2)
378c2ecf20Sopenharmony_ci#define SUN6I_TFR_CTL_CS_MASK			0x30
388c2ecf20Sopenharmony_ci#define SUN6I_TFR_CTL_CS(cs)			(((cs) << 4) & SUN6I_TFR_CTL_CS_MASK)
398c2ecf20Sopenharmony_ci#define SUN6I_TFR_CTL_CS_MANUAL			BIT(6)
408c2ecf20Sopenharmony_ci#define SUN6I_TFR_CTL_CS_LEVEL			BIT(7)
418c2ecf20Sopenharmony_ci#define SUN6I_TFR_CTL_DHB			BIT(8)
428c2ecf20Sopenharmony_ci#define SUN6I_TFR_CTL_FBS			BIT(12)
438c2ecf20Sopenharmony_ci#define SUN6I_TFR_CTL_XCH			BIT(31)
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define SUN6I_INT_CTL_REG		0x10
468c2ecf20Sopenharmony_ci#define SUN6I_INT_CTL_RF_RDY			BIT(0)
478c2ecf20Sopenharmony_ci#define SUN6I_INT_CTL_TF_ERQ			BIT(4)
488c2ecf20Sopenharmony_ci#define SUN6I_INT_CTL_RF_OVF			BIT(8)
498c2ecf20Sopenharmony_ci#define SUN6I_INT_CTL_TC			BIT(12)
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#define SUN6I_INT_STA_REG		0x14
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci#define SUN6I_FIFO_CTL_REG		0x18
548c2ecf20Sopenharmony_ci#define SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_MASK	0xff
558c2ecf20Sopenharmony_ci#define SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_BITS	0
568c2ecf20Sopenharmony_ci#define SUN6I_FIFO_CTL_RF_RST			BIT(15)
578c2ecf20Sopenharmony_ci#define SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_MASK	0xff
588c2ecf20Sopenharmony_ci#define SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_BITS	16
598c2ecf20Sopenharmony_ci#define SUN6I_FIFO_CTL_TF_RST			BIT(31)
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci#define SUN6I_FIFO_STA_REG		0x1c
628c2ecf20Sopenharmony_ci#define SUN6I_FIFO_STA_RF_CNT_MASK		GENMASK(7, 0)
638c2ecf20Sopenharmony_ci#define SUN6I_FIFO_STA_TF_CNT_MASK		GENMASK(23, 16)
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci#define SUN6I_CLK_CTL_REG		0x24
668c2ecf20Sopenharmony_ci#define SUN6I_CLK_CTL_CDR2_MASK			0xff
678c2ecf20Sopenharmony_ci#define SUN6I_CLK_CTL_CDR2(div)			(((div) & SUN6I_CLK_CTL_CDR2_MASK) << 0)
688c2ecf20Sopenharmony_ci#define SUN6I_CLK_CTL_CDR1_MASK			0xf
698c2ecf20Sopenharmony_ci#define SUN6I_CLK_CTL_CDR1(div)			(((div) & SUN6I_CLK_CTL_CDR1_MASK) << 8)
708c2ecf20Sopenharmony_ci#define SUN6I_CLK_CTL_DRS			BIT(12)
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci#define SUN6I_MAX_XFER_SIZE		0xffffff
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci#define SUN6I_BURST_CNT_REG		0x30
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci#define SUN6I_XMIT_CNT_REG		0x34
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci#define SUN6I_BURST_CTL_CNT_REG		0x38
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci#define SUN6I_TXDATA_REG		0x200
818c2ecf20Sopenharmony_ci#define SUN6I_RXDATA_REG		0x300
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistruct sun6i_spi {
848c2ecf20Sopenharmony_ci	struct spi_master	*master;
858c2ecf20Sopenharmony_ci	void __iomem		*base_addr;
868c2ecf20Sopenharmony_ci	struct clk		*hclk;
878c2ecf20Sopenharmony_ci	struct clk		*mclk;
888c2ecf20Sopenharmony_ci	struct reset_control	*rstc;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	struct completion	done;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	const u8		*tx_buf;
938c2ecf20Sopenharmony_ci	u8			*rx_buf;
948c2ecf20Sopenharmony_ci	int			len;
958c2ecf20Sopenharmony_ci	unsigned long		fifo_depth;
968c2ecf20Sopenharmony_ci};
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic inline u32 sun6i_spi_read(struct sun6i_spi *sspi, u32 reg)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	return readl(sspi->base_addr + reg);
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic inline void sun6i_spi_write(struct sun6i_spi *sspi, u32 reg, u32 value)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	writel(value, sspi->base_addr + reg);
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic inline u32 sun6i_spi_get_rx_fifo_count(struct sun6i_spi *sspi)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	u32 reg = sun6i_spi_read(sspi, SUN6I_FIFO_STA_REG);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	return FIELD_GET(SUN6I_FIFO_STA_RF_CNT_MASK, reg);
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic inline u32 sun6i_spi_get_tx_fifo_count(struct sun6i_spi *sspi)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	u32 reg = sun6i_spi_read(sspi, SUN6I_FIFO_STA_REG);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	return FIELD_GET(SUN6I_FIFO_STA_TF_CNT_MASK, reg);
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistatic inline void sun6i_spi_disable_interrupt(struct sun6i_spi *sspi, u32 mask)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	u32 reg = sun6i_spi_read(sspi, SUN6I_INT_CTL_REG);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	reg &= ~mask;
1278c2ecf20Sopenharmony_ci	sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, reg);
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic inline void sun6i_spi_drain_fifo(struct sun6i_spi *sspi)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	u32 len;
1338c2ecf20Sopenharmony_ci	u8 byte;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	/* See how much data is available */
1368c2ecf20Sopenharmony_ci	len = sun6i_spi_get_rx_fifo_count(sspi);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	while (len--) {
1398c2ecf20Sopenharmony_ci		byte = readb(sspi->base_addr + SUN6I_RXDATA_REG);
1408c2ecf20Sopenharmony_ci		if (sspi->rx_buf)
1418c2ecf20Sopenharmony_ci			*sspi->rx_buf++ = byte;
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic inline void sun6i_spi_fill_fifo(struct sun6i_spi *sspi)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	u32 cnt;
1488c2ecf20Sopenharmony_ci	int len;
1498c2ecf20Sopenharmony_ci	u8 byte;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	/* See how much data we can fit */
1528c2ecf20Sopenharmony_ci	cnt = sspi->fifo_depth - sun6i_spi_get_tx_fifo_count(sspi);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	len = min((int)cnt, sspi->len);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	while (len--) {
1578c2ecf20Sopenharmony_ci		byte = sspi->tx_buf ? *sspi->tx_buf++ : 0;
1588c2ecf20Sopenharmony_ci		writeb(byte, sspi->base_addr + SUN6I_TXDATA_REG);
1598c2ecf20Sopenharmony_ci		sspi->len--;
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistatic void sun6i_spi_set_cs(struct spi_device *spi, bool enable)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci	struct sun6i_spi *sspi = spi_master_get_devdata(spi->master);
1668c2ecf20Sopenharmony_ci	u32 reg;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG);
1698c2ecf20Sopenharmony_ci	reg &= ~SUN6I_TFR_CTL_CS_MASK;
1708c2ecf20Sopenharmony_ci	reg |= SUN6I_TFR_CTL_CS(spi->chip_select);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	if (enable)
1738c2ecf20Sopenharmony_ci		reg |= SUN6I_TFR_CTL_CS_LEVEL;
1748c2ecf20Sopenharmony_ci	else
1758c2ecf20Sopenharmony_ci		reg &= ~SUN6I_TFR_CTL_CS_LEVEL;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg);
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cistatic size_t sun6i_spi_max_transfer_size(struct spi_device *spi)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	return SUN6I_MAX_XFER_SIZE - 1;
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic int sun6i_spi_transfer_one(struct spi_master *master,
1868c2ecf20Sopenharmony_ci				  struct spi_device *spi,
1878c2ecf20Sopenharmony_ci				  struct spi_transfer *tfr)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	struct sun6i_spi *sspi = spi_master_get_devdata(master);
1908c2ecf20Sopenharmony_ci	unsigned int mclk_rate, div, div_cdr1, div_cdr2, timeout;
1918c2ecf20Sopenharmony_ci	unsigned int start, end, tx_time;
1928c2ecf20Sopenharmony_ci	unsigned int trig_level;
1938c2ecf20Sopenharmony_ci	unsigned int tx_len = 0, rx_len = 0;
1948c2ecf20Sopenharmony_ci	int ret = 0;
1958c2ecf20Sopenharmony_ci	u32 reg;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	if (tfr->len > SUN6I_MAX_XFER_SIZE)
1988c2ecf20Sopenharmony_ci		return -EINVAL;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	reinit_completion(&sspi->done);
2018c2ecf20Sopenharmony_ci	sspi->tx_buf = tfr->tx_buf;
2028c2ecf20Sopenharmony_ci	sspi->rx_buf = tfr->rx_buf;
2038c2ecf20Sopenharmony_ci	sspi->len = tfr->len;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	/* Clear pending interrupts */
2068c2ecf20Sopenharmony_ci	sun6i_spi_write(sspi, SUN6I_INT_STA_REG, ~0);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	/* Reset FIFO */
2098c2ecf20Sopenharmony_ci	sun6i_spi_write(sspi, SUN6I_FIFO_CTL_REG,
2108c2ecf20Sopenharmony_ci			SUN6I_FIFO_CTL_RF_RST | SUN6I_FIFO_CTL_TF_RST);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	/*
2138c2ecf20Sopenharmony_ci	 * Setup FIFO interrupt trigger level
2148c2ecf20Sopenharmony_ci	 * Here we choose 3/4 of the full fifo depth, as it's the hardcoded
2158c2ecf20Sopenharmony_ci	 * value used in old generation of Allwinner SPI controller.
2168c2ecf20Sopenharmony_ci	 * (See spi-sun4i.c)
2178c2ecf20Sopenharmony_ci	 */
2188c2ecf20Sopenharmony_ci	trig_level = sspi->fifo_depth / 4 * 3;
2198c2ecf20Sopenharmony_ci	sun6i_spi_write(sspi, SUN6I_FIFO_CTL_REG,
2208c2ecf20Sopenharmony_ci			(trig_level << SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_BITS) |
2218c2ecf20Sopenharmony_ci			(trig_level << SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_BITS));
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	/*
2248c2ecf20Sopenharmony_ci	 * Setup the transfer control register: Chip Select,
2258c2ecf20Sopenharmony_ci	 * polarities, etc.
2268c2ecf20Sopenharmony_ci	 */
2278c2ecf20Sopenharmony_ci	reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	if (spi->mode & SPI_CPOL)
2308c2ecf20Sopenharmony_ci		reg |= SUN6I_TFR_CTL_CPOL;
2318c2ecf20Sopenharmony_ci	else
2328c2ecf20Sopenharmony_ci		reg &= ~SUN6I_TFR_CTL_CPOL;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	if (spi->mode & SPI_CPHA)
2358c2ecf20Sopenharmony_ci		reg |= SUN6I_TFR_CTL_CPHA;
2368c2ecf20Sopenharmony_ci	else
2378c2ecf20Sopenharmony_ci		reg &= ~SUN6I_TFR_CTL_CPHA;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	if (spi->mode & SPI_LSB_FIRST)
2408c2ecf20Sopenharmony_ci		reg |= SUN6I_TFR_CTL_FBS;
2418c2ecf20Sopenharmony_ci	else
2428c2ecf20Sopenharmony_ci		reg &= ~SUN6I_TFR_CTL_FBS;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	/*
2458c2ecf20Sopenharmony_ci	 * If it's a TX only transfer, we don't want to fill the RX
2468c2ecf20Sopenharmony_ci	 * FIFO with bogus data
2478c2ecf20Sopenharmony_ci	 */
2488c2ecf20Sopenharmony_ci	if (sspi->rx_buf) {
2498c2ecf20Sopenharmony_ci		reg &= ~SUN6I_TFR_CTL_DHB;
2508c2ecf20Sopenharmony_ci		rx_len = tfr->len;
2518c2ecf20Sopenharmony_ci	} else {
2528c2ecf20Sopenharmony_ci		reg |= SUN6I_TFR_CTL_DHB;
2538c2ecf20Sopenharmony_ci	}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	/* We want to control the chip select manually */
2568c2ecf20Sopenharmony_ci	reg |= SUN6I_TFR_CTL_CS_MANUAL;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	/* Ensure that we have a parent clock fast enough */
2618c2ecf20Sopenharmony_ci	mclk_rate = clk_get_rate(sspi->mclk);
2628c2ecf20Sopenharmony_ci	if (mclk_rate < (2 * tfr->speed_hz)) {
2638c2ecf20Sopenharmony_ci		clk_set_rate(sspi->mclk, 2 * tfr->speed_hz);
2648c2ecf20Sopenharmony_ci		mclk_rate = clk_get_rate(sspi->mclk);
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	/*
2688c2ecf20Sopenharmony_ci	 * Setup clock divider.
2698c2ecf20Sopenharmony_ci	 *
2708c2ecf20Sopenharmony_ci	 * We have two choices there. Either we can use the clock
2718c2ecf20Sopenharmony_ci	 * divide rate 1, which is calculated thanks to this formula:
2728c2ecf20Sopenharmony_ci	 * SPI_CLK = MOD_CLK / (2 ^ cdr)
2738c2ecf20Sopenharmony_ci	 * Or we can use CDR2, which is calculated with the formula:
2748c2ecf20Sopenharmony_ci	 * SPI_CLK = MOD_CLK / (2 * (cdr + 1))
2758c2ecf20Sopenharmony_ci	 * Wether we use the former or the latter is set through the
2768c2ecf20Sopenharmony_ci	 * DRS bit.
2778c2ecf20Sopenharmony_ci	 *
2788c2ecf20Sopenharmony_ci	 * First try CDR2, and if we can't reach the expected
2798c2ecf20Sopenharmony_ci	 * frequency, fall back to CDR1.
2808c2ecf20Sopenharmony_ci	 */
2818c2ecf20Sopenharmony_ci	div_cdr1 = DIV_ROUND_UP(mclk_rate, tfr->speed_hz);
2828c2ecf20Sopenharmony_ci	div_cdr2 = DIV_ROUND_UP(div_cdr1, 2);
2838c2ecf20Sopenharmony_ci	if (div_cdr2 <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) {
2848c2ecf20Sopenharmony_ci		reg = SUN6I_CLK_CTL_CDR2(div_cdr2 - 1) | SUN6I_CLK_CTL_DRS;
2858c2ecf20Sopenharmony_ci		tfr->effective_speed_hz = mclk_rate / (2 * div_cdr2);
2868c2ecf20Sopenharmony_ci	} else {
2878c2ecf20Sopenharmony_ci		div = min(SUN6I_CLK_CTL_CDR1_MASK, order_base_2(div_cdr1));
2888c2ecf20Sopenharmony_ci		reg = SUN6I_CLK_CTL_CDR1(div);
2898c2ecf20Sopenharmony_ci		tfr->effective_speed_hz = mclk_rate / (1 << div);
2908c2ecf20Sopenharmony_ci	}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	sun6i_spi_write(sspi, SUN6I_CLK_CTL_REG, reg);
2938c2ecf20Sopenharmony_ci	/* Finally enable the bus - doing so before might raise SCK to HIGH */
2948c2ecf20Sopenharmony_ci	reg = sun6i_spi_read(sspi, SUN6I_GBL_CTL_REG);
2958c2ecf20Sopenharmony_ci	reg |= SUN6I_GBL_CTL_BUS_ENABLE;
2968c2ecf20Sopenharmony_ci	sun6i_spi_write(sspi, SUN6I_GBL_CTL_REG, reg);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	/* Setup the transfer now... */
2998c2ecf20Sopenharmony_ci	if (sspi->tx_buf)
3008c2ecf20Sopenharmony_ci		tx_len = tfr->len;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	/* Setup the counters */
3038c2ecf20Sopenharmony_ci	sun6i_spi_write(sspi, SUN6I_BURST_CNT_REG, tfr->len);
3048c2ecf20Sopenharmony_ci	sun6i_spi_write(sspi, SUN6I_XMIT_CNT_REG, tx_len);
3058c2ecf20Sopenharmony_ci	sun6i_spi_write(sspi, SUN6I_BURST_CTL_CNT_REG, tx_len);
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	/* Fill the TX FIFO */
3088c2ecf20Sopenharmony_ci	sun6i_spi_fill_fifo(sspi);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	/* Enable the interrupts */
3118c2ecf20Sopenharmony_ci	reg = SUN6I_INT_CTL_TC;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	if (rx_len > sspi->fifo_depth)
3148c2ecf20Sopenharmony_ci		reg |= SUN6I_INT_CTL_RF_RDY;
3158c2ecf20Sopenharmony_ci	if (tx_len > sspi->fifo_depth)
3168c2ecf20Sopenharmony_ci		reg |= SUN6I_INT_CTL_TF_ERQ;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, reg);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	/* Start the transfer */
3218c2ecf20Sopenharmony_ci	reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG);
3228c2ecf20Sopenharmony_ci	sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg | SUN6I_TFR_CTL_XCH);
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	tx_time = max(tfr->len * 8 * 2 / (tfr->speed_hz / 1000), 100U);
3258c2ecf20Sopenharmony_ci	start = jiffies;
3268c2ecf20Sopenharmony_ci	timeout = wait_for_completion_timeout(&sspi->done,
3278c2ecf20Sopenharmony_ci					      msecs_to_jiffies(tx_time));
3288c2ecf20Sopenharmony_ci	end = jiffies;
3298c2ecf20Sopenharmony_ci	if (!timeout) {
3308c2ecf20Sopenharmony_ci		dev_warn(&master->dev,
3318c2ecf20Sopenharmony_ci			 "%s: timeout transferring %u bytes@%iHz for %i(%i)ms",
3328c2ecf20Sopenharmony_ci			 dev_name(&spi->dev), tfr->len, tfr->speed_hz,
3338c2ecf20Sopenharmony_ci			 jiffies_to_msecs(end - start), tx_time);
3348c2ecf20Sopenharmony_ci		ret = -ETIMEDOUT;
3358c2ecf20Sopenharmony_ci	}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, 0);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	return ret;
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cistatic irqreturn_t sun6i_spi_handler(int irq, void *dev_id)
3438c2ecf20Sopenharmony_ci{
3448c2ecf20Sopenharmony_ci	struct sun6i_spi *sspi = dev_id;
3458c2ecf20Sopenharmony_ci	u32 status = sun6i_spi_read(sspi, SUN6I_INT_STA_REG);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	/* Transfer complete */
3488c2ecf20Sopenharmony_ci	if (status & SUN6I_INT_CTL_TC) {
3498c2ecf20Sopenharmony_ci		sun6i_spi_write(sspi, SUN6I_INT_STA_REG, SUN6I_INT_CTL_TC);
3508c2ecf20Sopenharmony_ci		sun6i_spi_drain_fifo(sspi);
3518c2ecf20Sopenharmony_ci		complete(&sspi->done);
3528c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
3538c2ecf20Sopenharmony_ci	}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	/* Receive FIFO 3/4 full */
3568c2ecf20Sopenharmony_ci	if (status & SUN6I_INT_CTL_RF_RDY) {
3578c2ecf20Sopenharmony_ci		sun6i_spi_drain_fifo(sspi);
3588c2ecf20Sopenharmony_ci		/* Only clear the interrupt _after_ draining the FIFO */
3598c2ecf20Sopenharmony_ci		sun6i_spi_write(sspi, SUN6I_INT_STA_REG, SUN6I_INT_CTL_RF_RDY);
3608c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
3618c2ecf20Sopenharmony_ci	}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	/* Transmit FIFO 3/4 empty */
3648c2ecf20Sopenharmony_ci	if (status & SUN6I_INT_CTL_TF_ERQ) {
3658c2ecf20Sopenharmony_ci		sun6i_spi_fill_fifo(sspi);
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci		if (!sspi->len)
3688c2ecf20Sopenharmony_ci			/* nothing left to transmit */
3698c2ecf20Sopenharmony_ci			sun6i_spi_disable_interrupt(sspi, SUN6I_INT_CTL_TF_ERQ);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci		/* Only clear the interrupt _after_ re-seeding the FIFO */
3728c2ecf20Sopenharmony_ci		sun6i_spi_write(sspi, SUN6I_INT_STA_REG, SUN6I_INT_CTL_TF_ERQ);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
3758c2ecf20Sopenharmony_ci	}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	return IRQ_NONE;
3788c2ecf20Sopenharmony_ci}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_cistatic int sun6i_spi_runtime_resume(struct device *dev)
3818c2ecf20Sopenharmony_ci{
3828c2ecf20Sopenharmony_ci	struct spi_master *master = dev_get_drvdata(dev);
3838c2ecf20Sopenharmony_ci	struct sun6i_spi *sspi = spi_master_get_devdata(master);
3848c2ecf20Sopenharmony_ci	int ret;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(sspi->hclk);
3878c2ecf20Sopenharmony_ci	if (ret) {
3888c2ecf20Sopenharmony_ci		dev_err(dev, "Couldn't enable AHB clock\n");
3898c2ecf20Sopenharmony_ci		goto out;
3908c2ecf20Sopenharmony_ci	}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(sspi->mclk);
3938c2ecf20Sopenharmony_ci	if (ret) {
3948c2ecf20Sopenharmony_ci		dev_err(dev, "Couldn't enable module clock\n");
3958c2ecf20Sopenharmony_ci		goto err;
3968c2ecf20Sopenharmony_ci	}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	ret = reset_control_deassert(sspi->rstc);
3998c2ecf20Sopenharmony_ci	if (ret) {
4008c2ecf20Sopenharmony_ci		dev_err(dev, "Couldn't deassert the device from reset\n");
4018c2ecf20Sopenharmony_ci		goto err2;
4028c2ecf20Sopenharmony_ci	}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	sun6i_spi_write(sspi, SUN6I_GBL_CTL_REG,
4058c2ecf20Sopenharmony_ci			SUN6I_GBL_CTL_MASTER | SUN6I_GBL_CTL_TP);
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	return 0;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_cierr2:
4108c2ecf20Sopenharmony_ci	clk_disable_unprepare(sspi->mclk);
4118c2ecf20Sopenharmony_cierr:
4128c2ecf20Sopenharmony_ci	clk_disable_unprepare(sspi->hclk);
4138c2ecf20Sopenharmony_ciout:
4148c2ecf20Sopenharmony_ci	return ret;
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_cistatic int sun6i_spi_runtime_suspend(struct device *dev)
4188c2ecf20Sopenharmony_ci{
4198c2ecf20Sopenharmony_ci	struct spi_master *master = dev_get_drvdata(dev);
4208c2ecf20Sopenharmony_ci	struct sun6i_spi *sspi = spi_master_get_devdata(master);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	reset_control_assert(sspi->rstc);
4238c2ecf20Sopenharmony_ci	clk_disable_unprepare(sspi->mclk);
4248c2ecf20Sopenharmony_ci	clk_disable_unprepare(sspi->hclk);
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	return 0;
4278c2ecf20Sopenharmony_ci}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_cistatic int sun6i_spi_probe(struct platform_device *pdev)
4308c2ecf20Sopenharmony_ci{
4318c2ecf20Sopenharmony_ci	struct spi_master *master;
4328c2ecf20Sopenharmony_ci	struct sun6i_spi *sspi;
4338c2ecf20Sopenharmony_ci	int ret = 0, irq;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	master = spi_alloc_master(&pdev->dev, sizeof(struct sun6i_spi));
4368c2ecf20Sopenharmony_ci	if (!master) {
4378c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Unable to allocate SPI Master\n");
4388c2ecf20Sopenharmony_ci		return -ENOMEM;
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, master);
4428c2ecf20Sopenharmony_ci	sspi = spi_master_get_devdata(master);
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	sspi->base_addr = devm_platform_ioremap_resource(pdev, 0);
4458c2ecf20Sopenharmony_ci	if (IS_ERR(sspi->base_addr)) {
4468c2ecf20Sopenharmony_ci		ret = PTR_ERR(sspi->base_addr);
4478c2ecf20Sopenharmony_ci		goto err_free_master;
4488c2ecf20Sopenharmony_ci	}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
4518c2ecf20Sopenharmony_ci	if (irq < 0) {
4528c2ecf20Sopenharmony_ci		ret = -ENXIO;
4538c2ecf20Sopenharmony_ci		goto err_free_master;
4548c2ecf20Sopenharmony_ci	}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	ret = devm_request_irq(&pdev->dev, irq, sun6i_spi_handler,
4578c2ecf20Sopenharmony_ci			       0, "sun6i-spi", sspi);
4588c2ecf20Sopenharmony_ci	if (ret) {
4598c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Cannot request IRQ\n");
4608c2ecf20Sopenharmony_ci		goto err_free_master;
4618c2ecf20Sopenharmony_ci	}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	sspi->master = master;
4648c2ecf20Sopenharmony_ci	sspi->fifo_depth = (unsigned long)of_device_get_match_data(&pdev->dev);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	master->max_speed_hz = 100 * 1000 * 1000;
4678c2ecf20Sopenharmony_ci	master->min_speed_hz = 3 * 1000;
4688c2ecf20Sopenharmony_ci	master->use_gpio_descriptors = true;
4698c2ecf20Sopenharmony_ci	master->set_cs = sun6i_spi_set_cs;
4708c2ecf20Sopenharmony_ci	master->transfer_one = sun6i_spi_transfer_one;
4718c2ecf20Sopenharmony_ci	master->num_chipselect = 4;
4728c2ecf20Sopenharmony_ci	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST;
4738c2ecf20Sopenharmony_ci	master->bits_per_word_mask = SPI_BPW_MASK(8);
4748c2ecf20Sopenharmony_ci	master->dev.of_node = pdev->dev.of_node;
4758c2ecf20Sopenharmony_ci	master->auto_runtime_pm = true;
4768c2ecf20Sopenharmony_ci	master->max_transfer_size = sun6i_spi_max_transfer_size;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	sspi->hclk = devm_clk_get(&pdev->dev, "ahb");
4798c2ecf20Sopenharmony_ci	if (IS_ERR(sspi->hclk)) {
4808c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Unable to acquire AHB clock\n");
4818c2ecf20Sopenharmony_ci		ret = PTR_ERR(sspi->hclk);
4828c2ecf20Sopenharmony_ci		goto err_free_master;
4838c2ecf20Sopenharmony_ci	}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	sspi->mclk = devm_clk_get(&pdev->dev, "mod");
4868c2ecf20Sopenharmony_ci	if (IS_ERR(sspi->mclk)) {
4878c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Unable to acquire module clock\n");
4888c2ecf20Sopenharmony_ci		ret = PTR_ERR(sspi->mclk);
4898c2ecf20Sopenharmony_ci		goto err_free_master;
4908c2ecf20Sopenharmony_ci	}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	init_completion(&sspi->done);
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	sspi->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
4958c2ecf20Sopenharmony_ci	if (IS_ERR(sspi->rstc)) {
4968c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Couldn't get reset controller\n");
4978c2ecf20Sopenharmony_ci		ret = PTR_ERR(sspi->rstc);
4988c2ecf20Sopenharmony_ci		goto err_free_master;
4998c2ecf20Sopenharmony_ci	}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	/*
5028c2ecf20Sopenharmony_ci	 * This wake-up/shutdown pattern is to be able to have the
5038c2ecf20Sopenharmony_ci	 * device woken up, even if runtime_pm is disabled
5048c2ecf20Sopenharmony_ci	 */
5058c2ecf20Sopenharmony_ci	ret = sun6i_spi_runtime_resume(&pdev->dev);
5068c2ecf20Sopenharmony_ci	if (ret) {
5078c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Couldn't resume the device\n");
5088c2ecf20Sopenharmony_ci		goto err_free_master;
5098c2ecf20Sopenharmony_ci	}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	pm_runtime_set_active(&pdev->dev);
5128c2ecf20Sopenharmony_ci	pm_runtime_enable(&pdev->dev);
5138c2ecf20Sopenharmony_ci	pm_runtime_idle(&pdev->dev);
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	ret = devm_spi_register_master(&pdev->dev, master);
5168c2ecf20Sopenharmony_ci	if (ret) {
5178c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "cannot register SPI master\n");
5188c2ecf20Sopenharmony_ci		goto err_pm_disable;
5198c2ecf20Sopenharmony_ci	}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	return 0;
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_cierr_pm_disable:
5248c2ecf20Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
5258c2ecf20Sopenharmony_ci	sun6i_spi_runtime_suspend(&pdev->dev);
5268c2ecf20Sopenharmony_cierr_free_master:
5278c2ecf20Sopenharmony_ci	spi_master_put(master);
5288c2ecf20Sopenharmony_ci	return ret;
5298c2ecf20Sopenharmony_ci}
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_cistatic int sun6i_spi_remove(struct platform_device *pdev)
5328c2ecf20Sopenharmony_ci{
5338c2ecf20Sopenharmony_ci	pm_runtime_force_suspend(&pdev->dev);
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	return 0;
5368c2ecf20Sopenharmony_ci}
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_cistatic const struct of_device_id sun6i_spi_match[] = {
5398c2ecf20Sopenharmony_ci	{ .compatible = "allwinner,sun6i-a31-spi", .data = (void *)SUN6I_FIFO_DEPTH },
5408c2ecf20Sopenharmony_ci	{ .compatible = "allwinner,sun8i-h3-spi",  .data = (void *)SUN8I_FIFO_DEPTH },
5418c2ecf20Sopenharmony_ci	{}
5428c2ecf20Sopenharmony_ci};
5438c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, sun6i_spi_match);
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_cistatic const struct dev_pm_ops sun6i_spi_pm_ops = {
5468c2ecf20Sopenharmony_ci	.runtime_resume		= sun6i_spi_runtime_resume,
5478c2ecf20Sopenharmony_ci	.runtime_suspend	= sun6i_spi_runtime_suspend,
5488c2ecf20Sopenharmony_ci};
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cistatic struct platform_driver sun6i_spi_driver = {
5518c2ecf20Sopenharmony_ci	.probe	= sun6i_spi_probe,
5528c2ecf20Sopenharmony_ci	.remove	= sun6i_spi_remove,
5538c2ecf20Sopenharmony_ci	.driver	= {
5548c2ecf20Sopenharmony_ci		.name		= "sun6i-spi",
5558c2ecf20Sopenharmony_ci		.of_match_table	= sun6i_spi_match,
5568c2ecf20Sopenharmony_ci		.pm		= &sun6i_spi_pm_ops,
5578c2ecf20Sopenharmony_ci	},
5588c2ecf20Sopenharmony_ci};
5598c2ecf20Sopenharmony_cimodule_platform_driver(sun6i_spi_driver);
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ciMODULE_AUTHOR("Pan Nan <pannan@allwinnertech.com>");
5628c2ecf20Sopenharmony_ciMODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
5638c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Allwinner A31 SPI controller driver");
5648c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
565