162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// HiSilicon SPI Controller Driver for Kunpeng SoCs
462306a36Sopenharmony_ci//
562306a36Sopenharmony_ci// Copyright (c) 2021 HiSilicon Technologies Co., Ltd.
662306a36Sopenharmony_ci// Author: Jay Fang <f.fangjian@huawei.com>
762306a36Sopenharmony_ci//
862306a36Sopenharmony_ci// This code is based on spi-dw-core.c.
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/acpi.h>
1162306a36Sopenharmony_ci#include <linux/bitfield.h>
1262306a36Sopenharmony_ci#include <linux/debugfs.h>
1362306a36Sopenharmony_ci#include <linux/delay.h>
1462306a36Sopenharmony_ci#include <linux/err.h>
1562306a36Sopenharmony_ci#include <linux/interrupt.h>
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/property.h>
1862306a36Sopenharmony_ci#include <linux/platform_device.h>
1962306a36Sopenharmony_ci#include <linux/slab.h>
2062306a36Sopenharmony_ci#include <linux/spi/spi.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/* Register offsets */
2362306a36Sopenharmony_ci#define HISI_SPI_CSCR		0x00	/* cs control register */
2462306a36Sopenharmony_ci#define HISI_SPI_CR		0x04	/* spi common control register */
2562306a36Sopenharmony_ci#define HISI_SPI_ENR		0x08	/* spi enable register */
2662306a36Sopenharmony_ci#define HISI_SPI_FIFOC		0x0c	/* fifo level control register */
2762306a36Sopenharmony_ci#define HISI_SPI_IMR		0x10	/* interrupt mask register */
2862306a36Sopenharmony_ci#define HISI_SPI_DIN		0x14	/* data in register */
2962306a36Sopenharmony_ci#define HISI_SPI_DOUT		0x18	/* data out register */
3062306a36Sopenharmony_ci#define HISI_SPI_SR		0x1c	/* status register */
3162306a36Sopenharmony_ci#define HISI_SPI_RISR		0x20	/* raw interrupt status register */
3262306a36Sopenharmony_ci#define HISI_SPI_ISR		0x24	/* interrupt status register */
3362306a36Sopenharmony_ci#define HISI_SPI_ICR		0x28	/* interrupt clear register */
3462306a36Sopenharmony_ci#define HISI_SPI_VERSION	0xe0	/* version register */
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci/* Bit fields in HISI_SPI_CR */
3762306a36Sopenharmony_ci#define CR_LOOP_MASK		GENMASK(1, 1)
3862306a36Sopenharmony_ci#define CR_CPOL_MASK		GENMASK(2, 2)
3962306a36Sopenharmony_ci#define CR_CPHA_MASK		GENMASK(3, 3)
4062306a36Sopenharmony_ci#define CR_DIV_PRE_MASK		GENMASK(11, 4)
4162306a36Sopenharmony_ci#define CR_DIV_POST_MASK	GENMASK(19, 12)
4262306a36Sopenharmony_ci#define CR_BPW_MASK		GENMASK(24, 20)
4362306a36Sopenharmony_ci#define CR_SPD_MODE_MASK	GENMASK(25, 25)
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/* Bit fields in HISI_SPI_FIFOC */
4662306a36Sopenharmony_ci#define FIFOC_TX_MASK		GENMASK(5, 3)
4762306a36Sopenharmony_ci#define FIFOC_RX_MASK		GENMASK(11, 9)
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/* Bit fields in HISI_SPI_IMR, 4 bits */
5062306a36Sopenharmony_ci#define IMR_RXOF		BIT(0)		/* Receive Overflow */
5162306a36Sopenharmony_ci#define IMR_RXTO		BIT(1)		/* Receive Timeout */
5262306a36Sopenharmony_ci#define IMR_RX			BIT(2)		/* Receive */
5362306a36Sopenharmony_ci#define IMR_TX			BIT(3)		/* Transmit */
5462306a36Sopenharmony_ci#define IMR_MASK		(IMR_RXOF | IMR_RXTO | IMR_RX | IMR_TX)
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci/* Bit fields in HISI_SPI_SR, 5 bits */
5762306a36Sopenharmony_ci#define SR_TXE			BIT(0)		/* Transmit FIFO empty */
5862306a36Sopenharmony_ci#define SR_TXNF			BIT(1)		/* Transmit FIFO not full */
5962306a36Sopenharmony_ci#define SR_RXNE			BIT(2)		/* Receive FIFO not empty */
6062306a36Sopenharmony_ci#define SR_RXF			BIT(3)		/* Receive FIFO full */
6162306a36Sopenharmony_ci#define SR_BUSY			BIT(4)		/* Busy Flag */
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/* Bit fields in HISI_SPI_ISR, 4 bits */
6462306a36Sopenharmony_ci#define ISR_RXOF		BIT(0)		/* Receive Overflow */
6562306a36Sopenharmony_ci#define ISR_RXTO		BIT(1)		/* Receive Timeout */
6662306a36Sopenharmony_ci#define ISR_RX			BIT(2)		/* Receive */
6762306a36Sopenharmony_ci#define ISR_TX			BIT(3)		/* Transmit */
6862306a36Sopenharmony_ci#define ISR_MASK		(ISR_RXOF | ISR_RXTO | ISR_RX | ISR_TX)
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci/* Bit fields in HISI_SPI_ICR, 2 bits */
7162306a36Sopenharmony_ci#define ICR_RXOF		BIT(0)		/* Receive Overflow */
7262306a36Sopenharmony_ci#define ICR_RXTO		BIT(1)		/* Receive Timeout */
7362306a36Sopenharmony_ci#define ICR_MASK		(ICR_RXOF | ICR_RXTO)
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci#define DIV_POST_MAX		0xFF
7662306a36Sopenharmony_ci#define DIV_POST_MIN		0x00
7762306a36Sopenharmony_ci#define DIV_PRE_MAX		0xFE
7862306a36Sopenharmony_ci#define DIV_PRE_MIN		0x02
7962306a36Sopenharmony_ci#define CLK_DIV_MAX		((1 + DIV_POST_MAX) * DIV_PRE_MAX)
8062306a36Sopenharmony_ci#define CLK_DIV_MIN		((1 + DIV_POST_MIN) * DIV_PRE_MIN)
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci#define DEFAULT_NUM_CS		1
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci#define HISI_SPI_WAIT_TIMEOUT_MS	10UL
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cienum hisi_spi_rx_level_trig {
8762306a36Sopenharmony_ci	HISI_SPI_RX_1,
8862306a36Sopenharmony_ci	HISI_SPI_RX_4,
8962306a36Sopenharmony_ci	HISI_SPI_RX_8,
9062306a36Sopenharmony_ci	HISI_SPI_RX_16,
9162306a36Sopenharmony_ci	HISI_SPI_RX_32,
9262306a36Sopenharmony_ci	HISI_SPI_RX_64,
9362306a36Sopenharmony_ci	HISI_SPI_RX_128
9462306a36Sopenharmony_ci};
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cienum hisi_spi_tx_level_trig {
9762306a36Sopenharmony_ci	HISI_SPI_TX_1_OR_LESS,
9862306a36Sopenharmony_ci	HISI_SPI_TX_4_OR_LESS,
9962306a36Sopenharmony_ci	HISI_SPI_TX_8_OR_LESS,
10062306a36Sopenharmony_ci	HISI_SPI_TX_16_OR_LESS,
10162306a36Sopenharmony_ci	HISI_SPI_TX_32_OR_LESS,
10262306a36Sopenharmony_ci	HISI_SPI_TX_64_OR_LESS,
10362306a36Sopenharmony_ci	HISI_SPI_TX_128_OR_LESS
10462306a36Sopenharmony_ci};
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cienum hisi_spi_frame_n_bytes {
10762306a36Sopenharmony_ci	HISI_SPI_N_BYTES_NULL,
10862306a36Sopenharmony_ci	HISI_SPI_N_BYTES_U8,
10962306a36Sopenharmony_ci	HISI_SPI_N_BYTES_U16,
11062306a36Sopenharmony_ci	HISI_SPI_N_BYTES_U32 = 4
11162306a36Sopenharmony_ci};
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci/* Slave spi_dev related */
11462306a36Sopenharmony_cistruct hisi_chip_data {
11562306a36Sopenharmony_ci	u32 cr;
11662306a36Sopenharmony_ci	u32 speed_hz;	/* baud rate */
11762306a36Sopenharmony_ci	u16 clk_div;	/* baud rate divider */
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	/* clk_div = (1 + div_post) * div_pre */
12062306a36Sopenharmony_ci	u8 div_post;	/* value from 0 to 255 */
12162306a36Sopenharmony_ci	u8 div_pre;	/* value from 2 to 254 (even only!) */
12262306a36Sopenharmony_ci};
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistruct hisi_spi {
12562306a36Sopenharmony_ci	struct device		*dev;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	void __iomem		*regs;
12862306a36Sopenharmony_ci	int			irq;
12962306a36Sopenharmony_ci	u32			fifo_len; /* depth of the FIFO buffer */
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	/* Current message transfer state info */
13262306a36Sopenharmony_ci	const void		*tx;
13362306a36Sopenharmony_ci	unsigned int		tx_len;
13462306a36Sopenharmony_ci	void			*rx;
13562306a36Sopenharmony_ci	unsigned int		rx_len;
13662306a36Sopenharmony_ci	u8			n_bytes; /* current is a 1/2/4 bytes op */
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	struct dentry *debugfs;
13962306a36Sopenharmony_ci	struct debugfs_regset32 regset;
14062306a36Sopenharmony_ci};
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci#define HISI_SPI_DBGFS_REG(_name, _off)	\
14362306a36Sopenharmony_ci{					\
14462306a36Sopenharmony_ci	.name = _name,			\
14562306a36Sopenharmony_ci	.offset = _off,			\
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic const struct debugfs_reg32 hisi_spi_regs[] = {
14962306a36Sopenharmony_ci	HISI_SPI_DBGFS_REG("CSCR", HISI_SPI_CSCR),
15062306a36Sopenharmony_ci	HISI_SPI_DBGFS_REG("CR", HISI_SPI_CR),
15162306a36Sopenharmony_ci	HISI_SPI_DBGFS_REG("ENR", HISI_SPI_ENR),
15262306a36Sopenharmony_ci	HISI_SPI_DBGFS_REG("FIFOC", HISI_SPI_FIFOC),
15362306a36Sopenharmony_ci	HISI_SPI_DBGFS_REG("IMR", HISI_SPI_IMR),
15462306a36Sopenharmony_ci	HISI_SPI_DBGFS_REG("DIN", HISI_SPI_DIN),
15562306a36Sopenharmony_ci	HISI_SPI_DBGFS_REG("DOUT", HISI_SPI_DOUT),
15662306a36Sopenharmony_ci	HISI_SPI_DBGFS_REG("SR", HISI_SPI_SR),
15762306a36Sopenharmony_ci	HISI_SPI_DBGFS_REG("RISR", HISI_SPI_RISR),
15862306a36Sopenharmony_ci	HISI_SPI_DBGFS_REG("ISR", HISI_SPI_ISR),
15962306a36Sopenharmony_ci	HISI_SPI_DBGFS_REG("ICR", HISI_SPI_ICR),
16062306a36Sopenharmony_ci	HISI_SPI_DBGFS_REG("VERSION", HISI_SPI_VERSION),
16162306a36Sopenharmony_ci};
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic int hisi_spi_debugfs_init(struct hisi_spi *hs)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	char name[32];
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	struct spi_controller *host;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	host = container_of(hs->dev, struct spi_controller, dev);
17062306a36Sopenharmony_ci	snprintf(name, 32, "hisi_spi%d", host->bus_num);
17162306a36Sopenharmony_ci	hs->debugfs = debugfs_create_dir(name, NULL);
17262306a36Sopenharmony_ci	if (IS_ERR(hs->debugfs))
17362306a36Sopenharmony_ci		return -ENOMEM;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	hs->regset.regs = hisi_spi_regs;
17662306a36Sopenharmony_ci	hs->regset.nregs = ARRAY_SIZE(hisi_spi_regs);
17762306a36Sopenharmony_ci	hs->regset.base = hs->regs;
17862306a36Sopenharmony_ci	debugfs_create_regset32("registers", 0400, hs->debugfs, &hs->regset);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	return 0;
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic u32 hisi_spi_busy(struct hisi_spi *hs)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	return readl(hs->regs + HISI_SPI_SR) & SR_BUSY;
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic u32 hisi_spi_rx_not_empty(struct hisi_spi *hs)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	return readl(hs->regs + HISI_SPI_SR) & SR_RXNE;
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistatic u32 hisi_spi_tx_not_full(struct hisi_spi *hs)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	return readl(hs->regs + HISI_SPI_SR) & SR_TXNF;
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cistatic void hisi_spi_flush_fifo(struct hisi_spi *hs)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	unsigned long limit = loops_per_jiffy << 1;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	do {
20362306a36Sopenharmony_ci		while (hisi_spi_rx_not_empty(hs))
20462306a36Sopenharmony_ci			readl(hs->regs + HISI_SPI_DOUT);
20562306a36Sopenharmony_ci	} while (hisi_spi_busy(hs) && limit--);
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci/* Disable the controller and all interrupts */
20962306a36Sopenharmony_cistatic void hisi_spi_disable(struct hisi_spi *hs)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	writel(0, hs->regs + HISI_SPI_ENR);
21262306a36Sopenharmony_ci	writel(IMR_MASK, hs->regs + HISI_SPI_IMR);
21362306a36Sopenharmony_ci	writel(ICR_MASK, hs->regs + HISI_SPI_ICR);
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistatic u8 hisi_spi_n_bytes(struct spi_transfer *transfer)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	if (transfer->bits_per_word <= 8)
21962306a36Sopenharmony_ci		return HISI_SPI_N_BYTES_U8;
22062306a36Sopenharmony_ci	else if (transfer->bits_per_word <= 16)
22162306a36Sopenharmony_ci		return HISI_SPI_N_BYTES_U16;
22262306a36Sopenharmony_ci	else
22362306a36Sopenharmony_ci		return HISI_SPI_N_BYTES_U32;
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic void hisi_spi_reader(struct hisi_spi *hs)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	u32 max = min_t(u32, hs->rx_len, hs->fifo_len);
22962306a36Sopenharmony_ci	u32 rxw;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	while (hisi_spi_rx_not_empty(hs) && max--) {
23262306a36Sopenharmony_ci		rxw = readl(hs->regs + HISI_SPI_DOUT);
23362306a36Sopenharmony_ci		/* Check the transfer's original "rx" is not null */
23462306a36Sopenharmony_ci		if (hs->rx) {
23562306a36Sopenharmony_ci			switch (hs->n_bytes) {
23662306a36Sopenharmony_ci			case HISI_SPI_N_BYTES_U8:
23762306a36Sopenharmony_ci				*(u8 *)(hs->rx) = rxw;
23862306a36Sopenharmony_ci				break;
23962306a36Sopenharmony_ci			case HISI_SPI_N_BYTES_U16:
24062306a36Sopenharmony_ci				*(u16 *)(hs->rx) = rxw;
24162306a36Sopenharmony_ci				break;
24262306a36Sopenharmony_ci			case HISI_SPI_N_BYTES_U32:
24362306a36Sopenharmony_ci				*(u32 *)(hs->rx) = rxw;
24462306a36Sopenharmony_ci				break;
24562306a36Sopenharmony_ci			}
24662306a36Sopenharmony_ci			hs->rx += hs->n_bytes;
24762306a36Sopenharmony_ci		}
24862306a36Sopenharmony_ci		--hs->rx_len;
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic void hisi_spi_writer(struct hisi_spi *hs)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	u32 max = min_t(u32, hs->tx_len, hs->fifo_len);
25562306a36Sopenharmony_ci	u32 txw = 0;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	while (hisi_spi_tx_not_full(hs) && max--) {
25862306a36Sopenharmony_ci		/* Check the transfer's original "tx" is not null */
25962306a36Sopenharmony_ci		if (hs->tx) {
26062306a36Sopenharmony_ci			switch (hs->n_bytes) {
26162306a36Sopenharmony_ci			case HISI_SPI_N_BYTES_U8:
26262306a36Sopenharmony_ci				txw = *(u8 *)(hs->tx);
26362306a36Sopenharmony_ci				break;
26462306a36Sopenharmony_ci			case HISI_SPI_N_BYTES_U16:
26562306a36Sopenharmony_ci				txw = *(u16 *)(hs->tx);
26662306a36Sopenharmony_ci				break;
26762306a36Sopenharmony_ci			case HISI_SPI_N_BYTES_U32:
26862306a36Sopenharmony_ci				txw = *(u32 *)(hs->tx);
26962306a36Sopenharmony_ci				break;
27062306a36Sopenharmony_ci			}
27162306a36Sopenharmony_ci			hs->tx += hs->n_bytes;
27262306a36Sopenharmony_ci		}
27362306a36Sopenharmony_ci		writel(txw, hs->regs + HISI_SPI_DIN);
27462306a36Sopenharmony_ci		--hs->tx_len;
27562306a36Sopenharmony_ci	}
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cistatic void __hisi_calc_div_reg(struct hisi_chip_data *chip)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	chip->div_pre = DIV_PRE_MAX;
28162306a36Sopenharmony_ci	while (chip->div_pre >= DIV_PRE_MIN) {
28262306a36Sopenharmony_ci		if (chip->clk_div % chip->div_pre == 0)
28362306a36Sopenharmony_ci			break;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci		chip->div_pre -= 2;
28662306a36Sopenharmony_ci	}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	if (chip->div_pre > chip->clk_div)
28962306a36Sopenharmony_ci		chip->div_pre = chip->clk_div;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	chip->div_post = (chip->clk_div / chip->div_pre) - 1;
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic u32 hisi_calc_effective_speed(struct spi_controller *host,
29562306a36Sopenharmony_ci			struct hisi_chip_data *chip, u32 speed_hz)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	u32 effective_speed;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	/* Note clock divider doesn't support odd numbers */
30062306a36Sopenharmony_ci	chip->clk_div = DIV_ROUND_UP(host->max_speed_hz, speed_hz) + 1;
30162306a36Sopenharmony_ci	chip->clk_div &= 0xfffe;
30262306a36Sopenharmony_ci	if (chip->clk_div > CLK_DIV_MAX)
30362306a36Sopenharmony_ci		chip->clk_div = CLK_DIV_MAX;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	effective_speed = host->max_speed_hz / chip->clk_div;
30662306a36Sopenharmony_ci	if (chip->speed_hz != effective_speed) {
30762306a36Sopenharmony_ci		__hisi_calc_div_reg(chip);
30862306a36Sopenharmony_ci		chip->speed_hz = effective_speed;
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	return effective_speed;
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic u32 hisi_spi_prepare_cr(struct spi_device *spi)
31562306a36Sopenharmony_ci{
31662306a36Sopenharmony_ci	u32 cr = FIELD_PREP(CR_SPD_MODE_MASK, 1);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	cr |= FIELD_PREP(CR_CPHA_MASK, (spi->mode & SPI_CPHA) ? 1 : 0);
31962306a36Sopenharmony_ci	cr |= FIELD_PREP(CR_CPOL_MASK, (spi->mode & SPI_CPOL) ? 1 : 0);
32062306a36Sopenharmony_ci	cr |= FIELD_PREP(CR_LOOP_MASK, (spi->mode & SPI_LOOP) ? 1 : 0);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	return cr;
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic void hisi_spi_hw_init(struct hisi_spi *hs)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	hisi_spi_disable(hs);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	/* FIFO default config */
33062306a36Sopenharmony_ci	writel(FIELD_PREP(FIFOC_TX_MASK, HISI_SPI_TX_64_OR_LESS) |
33162306a36Sopenharmony_ci		FIELD_PREP(FIFOC_RX_MASK, HISI_SPI_RX_16),
33262306a36Sopenharmony_ci		hs->regs + HISI_SPI_FIFOC);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	hs->fifo_len = 256;
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic irqreturn_t hisi_spi_irq(int irq, void *dev_id)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	struct spi_controller *host = dev_id;
34062306a36Sopenharmony_ci	struct hisi_spi *hs = spi_controller_get_devdata(host);
34162306a36Sopenharmony_ci	u32 irq_status = readl(hs->regs + HISI_SPI_ISR) & ISR_MASK;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	if (!irq_status)
34462306a36Sopenharmony_ci		return IRQ_NONE;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	if (!host->cur_msg)
34762306a36Sopenharmony_ci		return IRQ_HANDLED;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	/* Error handling */
35062306a36Sopenharmony_ci	if (irq_status & ISR_RXOF) {
35162306a36Sopenharmony_ci		dev_err(hs->dev, "interrupt_transfer: fifo overflow\n");
35262306a36Sopenharmony_ci		host->cur_msg->status = -EIO;
35362306a36Sopenharmony_ci		goto finalize_transfer;
35462306a36Sopenharmony_ci	}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	/*
35762306a36Sopenharmony_ci	 * Read data from the Rx FIFO every time. If there is
35862306a36Sopenharmony_ci	 * nothing left to receive, finalize the transfer.
35962306a36Sopenharmony_ci	 */
36062306a36Sopenharmony_ci	hisi_spi_reader(hs);
36162306a36Sopenharmony_ci	if (!hs->rx_len)
36262306a36Sopenharmony_ci		goto finalize_transfer;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	/* Send data out when Tx FIFO IRQ triggered */
36562306a36Sopenharmony_ci	if (irq_status & ISR_TX)
36662306a36Sopenharmony_ci		hisi_spi_writer(hs);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	return IRQ_HANDLED;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cifinalize_transfer:
37162306a36Sopenharmony_ci	hisi_spi_disable(hs);
37262306a36Sopenharmony_ci	spi_finalize_current_transfer(host);
37362306a36Sopenharmony_ci	return IRQ_HANDLED;
37462306a36Sopenharmony_ci}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_cistatic int hisi_spi_transfer_one(struct spi_controller *host,
37762306a36Sopenharmony_ci		struct spi_device *spi, struct spi_transfer *transfer)
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci	struct hisi_spi *hs = spi_controller_get_devdata(host);
38062306a36Sopenharmony_ci	struct hisi_chip_data *chip = spi_get_ctldata(spi);
38162306a36Sopenharmony_ci	u32 cr = chip->cr;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	/* Update per transfer options for speed and bpw */
38462306a36Sopenharmony_ci	transfer->effective_speed_hz =
38562306a36Sopenharmony_ci		hisi_calc_effective_speed(host, chip, transfer->speed_hz);
38662306a36Sopenharmony_ci	cr |= FIELD_PREP(CR_DIV_PRE_MASK, chip->div_pre);
38762306a36Sopenharmony_ci	cr |= FIELD_PREP(CR_DIV_POST_MASK, chip->div_post);
38862306a36Sopenharmony_ci	cr |= FIELD_PREP(CR_BPW_MASK, transfer->bits_per_word - 1);
38962306a36Sopenharmony_ci	writel(cr, hs->regs + HISI_SPI_CR);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	hisi_spi_flush_fifo(hs);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	hs->n_bytes = hisi_spi_n_bytes(transfer);
39462306a36Sopenharmony_ci	hs->tx = transfer->tx_buf;
39562306a36Sopenharmony_ci	hs->tx_len = transfer->len / hs->n_bytes;
39662306a36Sopenharmony_ci	hs->rx = transfer->rx_buf;
39762306a36Sopenharmony_ci	hs->rx_len = hs->tx_len;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	/*
40062306a36Sopenharmony_ci	 * Ensure that the transfer data above has been updated
40162306a36Sopenharmony_ci	 * before the interrupt to start.
40262306a36Sopenharmony_ci	 */
40362306a36Sopenharmony_ci	smp_mb();
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	/* Enable all interrupts and the controller */
40662306a36Sopenharmony_ci	writel(~(u32)IMR_MASK, hs->regs + HISI_SPI_IMR);
40762306a36Sopenharmony_ci	writel(1, hs->regs + HISI_SPI_ENR);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	return 1;
41062306a36Sopenharmony_ci}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_cistatic void hisi_spi_handle_err(struct spi_controller *host,
41362306a36Sopenharmony_ci		struct spi_message *msg)
41462306a36Sopenharmony_ci{
41562306a36Sopenharmony_ci	struct hisi_spi *hs = spi_controller_get_devdata(host);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	hisi_spi_disable(hs);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	/*
42062306a36Sopenharmony_ci	 * Wait for interrupt handler that is
42162306a36Sopenharmony_ci	 * already in timeout to complete.
42262306a36Sopenharmony_ci	 */
42362306a36Sopenharmony_ci	msleep(HISI_SPI_WAIT_TIMEOUT_MS);
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_cistatic int hisi_spi_setup(struct spi_device *spi)
42762306a36Sopenharmony_ci{
42862306a36Sopenharmony_ci	struct hisi_chip_data *chip;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	/* Only alloc on first setup */
43162306a36Sopenharmony_ci	chip = spi_get_ctldata(spi);
43262306a36Sopenharmony_ci	if (!chip) {
43362306a36Sopenharmony_ci		chip = kzalloc(sizeof(*chip), GFP_KERNEL);
43462306a36Sopenharmony_ci		if (!chip)
43562306a36Sopenharmony_ci			return -ENOMEM;
43662306a36Sopenharmony_ci		spi_set_ctldata(spi, chip);
43762306a36Sopenharmony_ci	}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	chip->cr = hisi_spi_prepare_cr(spi);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	return 0;
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_cistatic void hisi_spi_cleanup(struct spi_device *spi)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	struct hisi_chip_data *chip = spi_get_ctldata(spi);
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	kfree(chip);
44962306a36Sopenharmony_ci	spi_set_ctldata(spi, NULL);
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_cistatic int hisi_spi_probe(struct platform_device *pdev)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
45562306a36Sopenharmony_ci	struct spi_controller *host;
45662306a36Sopenharmony_ci	struct hisi_spi *hs;
45762306a36Sopenharmony_ci	int ret, irq;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
46062306a36Sopenharmony_ci	if (irq < 0)
46162306a36Sopenharmony_ci		return irq;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	host = devm_spi_alloc_host(dev, sizeof(*hs));
46462306a36Sopenharmony_ci	if (!host)
46562306a36Sopenharmony_ci		return -ENOMEM;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	platform_set_drvdata(pdev, host);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	hs = spi_controller_get_devdata(host);
47062306a36Sopenharmony_ci	hs->dev = dev;
47162306a36Sopenharmony_ci	hs->irq = irq;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	hs->regs = devm_platform_ioremap_resource(pdev, 0);
47462306a36Sopenharmony_ci	if (IS_ERR(hs->regs))
47562306a36Sopenharmony_ci		return PTR_ERR(hs->regs);
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	/* Specify maximum SPI clocking speed (host only) by firmware */
47862306a36Sopenharmony_ci	ret = device_property_read_u32(dev, "spi-max-frequency",
47962306a36Sopenharmony_ci					&host->max_speed_hz);
48062306a36Sopenharmony_ci	if (ret) {
48162306a36Sopenharmony_ci		dev_err(dev, "failed to get max SPI clocking speed, ret=%d\n",
48262306a36Sopenharmony_ci			ret);
48362306a36Sopenharmony_ci		return -EINVAL;
48462306a36Sopenharmony_ci	}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	ret = device_property_read_u16(dev, "num-cs",
48762306a36Sopenharmony_ci					&host->num_chipselect);
48862306a36Sopenharmony_ci	if (ret)
48962306a36Sopenharmony_ci		host->num_chipselect = DEFAULT_NUM_CS;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	host->use_gpio_descriptors = true;
49262306a36Sopenharmony_ci	host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP;
49362306a36Sopenharmony_ci	host->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
49462306a36Sopenharmony_ci	host->bus_num = pdev->id;
49562306a36Sopenharmony_ci	host->setup = hisi_spi_setup;
49662306a36Sopenharmony_ci	host->cleanup = hisi_spi_cleanup;
49762306a36Sopenharmony_ci	host->transfer_one = hisi_spi_transfer_one;
49862306a36Sopenharmony_ci	host->handle_err = hisi_spi_handle_err;
49962306a36Sopenharmony_ci	host->dev.fwnode = dev->fwnode;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	hisi_spi_hw_init(hs);
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	ret = devm_request_irq(dev, hs->irq, hisi_spi_irq, 0, dev_name(dev),
50462306a36Sopenharmony_ci			       host);
50562306a36Sopenharmony_ci	if (ret < 0) {
50662306a36Sopenharmony_ci		dev_err(dev, "failed to get IRQ=%d, ret=%d\n", hs->irq, ret);
50762306a36Sopenharmony_ci		return ret;
50862306a36Sopenharmony_ci	}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	ret = spi_register_controller(host);
51162306a36Sopenharmony_ci	if (ret) {
51262306a36Sopenharmony_ci		dev_err(dev, "failed to register spi host, ret=%d\n", ret);
51362306a36Sopenharmony_ci		return ret;
51462306a36Sopenharmony_ci	}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	if (hisi_spi_debugfs_init(hs))
51762306a36Sopenharmony_ci		dev_info(dev, "failed to create debugfs dir\n");
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	dev_info(dev, "hw version:0x%x max-freq:%u kHz\n",
52062306a36Sopenharmony_ci		readl(hs->regs + HISI_SPI_VERSION),
52162306a36Sopenharmony_ci		host->max_speed_hz / 1000);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	return 0;
52462306a36Sopenharmony_ci}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_cistatic void hisi_spi_remove(struct platform_device *pdev)
52762306a36Sopenharmony_ci{
52862306a36Sopenharmony_ci	struct spi_controller *host = platform_get_drvdata(pdev);
52962306a36Sopenharmony_ci	struct hisi_spi *hs = spi_controller_get_devdata(host);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	debugfs_remove_recursive(hs->debugfs);
53262306a36Sopenharmony_ci	spi_unregister_controller(host);
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_cistatic const struct acpi_device_id hisi_spi_acpi_match[] = {
53662306a36Sopenharmony_ci	{"HISI03E1", 0},
53762306a36Sopenharmony_ci	{}
53862306a36Sopenharmony_ci};
53962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, hisi_spi_acpi_match);
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_cistatic struct platform_driver hisi_spi_driver = {
54262306a36Sopenharmony_ci	.probe		= hisi_spi_probe,
54362306a36Sopenharmony_ci	.remove_new	= hisi_spi_remove,
54462306a36Sopenharmony_ci	.driver		= {
54562306a36Sopenharmony_ci		.name	= "hisi-kunpeng-spi",
54662306a36Sopenharmony_ci		.acpi_match_table = hisi_spi_acpi_match,
54762306a36Sopenharmony_ci	},
54862306a36Sopenharmony_ci};
54962306a36Sopenharmony_cimodule_platform_driver(hisi_spi_driver);
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ciMODULE_AUTHOR("Jay Fang <f.fangjian@huawei.com>");
55262306a36Sopenharmony_ciMODULE_DESCRIPTION("HiSilicon SPI Controller Driver for Kunpeng SoCs");
55362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
554