162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Microchip PIC32 SPI controller driver.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Purna Chandra Mandal <purna.mandal@microchip.com>
662306a36Sopenharmony_ci * Copyright (c) 2016, Microchip Technology Inc.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/clk.h>
1062306a36Sopenharmony_ci#include <linux/clkdev.h>
1162306a36Sopenharmony_ci#include <linux/delay.h>
1262306a36Sopenharmony_ci#include <linux/dmaengine.h>
1362306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1462306a36Sopenharmony_ci#include <linux/highmem.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/io.h>
1762306a36Sopenharmony_ci#include <linux/interrupt.h>
1862306a36Sopenharmony_ci#include <linux/of.h>
1962306a36Sopenharmony_ci#include <linux/of_irq.h>
2062306a36Sopenharmony_ci#include <linux/of_gpio.h>
2162306a36Sopenharmony_ci#include <linux/of_address.h>
2262306a36Sopenharmony_ci#include <linux/platform_device.h>
2362306a36Sopenharmony_ci#include <linux/spi/spi.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/* SPI controller registers */
2662306a36Sopenharmony_cistruct pic32_spi_regs {
2762306a36Sopenharmony_ci	u32 ctrl;
2862306a36Sopenharmony_ci	u32 ctrl_clr;
2962306a36Sopenharmony_ci	u32 ctrl_set;
3062306a36Sopenharmony_ci	u32 ctrl_inv;
3162306a36Sopenharmony_ci	u32 status;
3262306a36Sopenharmony_ci	u32 status_clr;
3362306a36Sopenharmony_ci	u32 status_set;
3462306a36Sopenharmony_ci	u32 status_inv;
3562306a36Sopenharmony_ci	u32 buf;
3662306a36Sopenharmony_ci	u32 dontuse[3];
3762306a36Sopenharmony_ci	u32 baud;
3862306a36Sopenharmony_ci	u32 dontuse2[3];
3962306a36Sopenharmony_ci	u32 ctrl2;
4062306a36Sopenharmony_ci	u32 ctrl2_clr;
4162306a36Sopenharmony_ci	u32 ctrl2_set;
4262306a36Sopenharmony_ci	u32 ctrl2_inv;
4362306a36Sopenharmony_ci};
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/* Bit fields of SPI Control Register */
4662306a36Sopenharmony_ci#define CTRL_RX_INT_SHIFT	0  /* Rx interrupt generation */
4762306a36Sopenharmony_ci#define  RX_FIFO_EMPTY		0
4862306a36Sopenharmony_ci#define  RX_FIFO_NOT_EMPTY	1 /* not empty */
4962306a36Sopenharmony_ci#define  RX_FIFO_HALF_FULL	2 /* full by half or more */
5062306a36Sopenharmony_ci#define  RX_FIFO_FULL		3 /* completely full */
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#define CTRL_TX_INT_SHIFT	2  /* TX interrupt generation */
5362306a36Sopenharmony_ci#define  TX_FIFO_ALL_EMPTY	0 /* completely empty */
5462306a36Sopenharmony_ci#define  TX_FIFO_EMPTY		1 /* empty */
5562306a36Sopenharmony_ci#define  TX_FIFO_HALF_EMPTY	2 /* empty by half or more */
5662306a36Sopenharmony_ci#define  TX_FIFO_NOT_FULL	3 /* atleast one empty */
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci#define CTRL_MSTEN	BIT(5) /* enable master mode */
5962306a36Sopenharmony_ci#define CTRL_CKP	BIT(6) /* active low */
6062306a36Sopenharmony_ci#define CTRL_CKE	BIT(8) /* Tx on falling edge */
6162306a36Sopenharmony_ci#define CTRL_SMP	BIT(9) /* Rx at middle or end of tx */
6262306a36Sopenharmony_ci#define CTRL_BPW_MASK	0x03   /* bits per word/sample */
6362306a36Sopenharmony_ci#define CTRL_BPW_SHIFT	10
6462306a36Sopenharmony_ci#define  PIC32_BPW_8	0
6562306a36Sopenharmony_ci#define  PIC32_BPW_16	1
6662306a36Sopenharmony_ci#define  PIC32_BPW_32	2
6762306a36Sopenharmony_ci#define CTRL_SIDL	BIT(13) /* sleep when idle */
6862306a36Sopenharmony_ci#define CTRL_ON		BIT(15) /* enable macro */
6962306a36Sopenharmony_ci#define CTRL_ENHBUF	BIT(16) /* enable enhanced buffering */
7062306a36Sopenharmony_ci#define CTRL_MCLKSEL	BIT(23) /* select clock source */
7162306a36Sopenharmony_ci#define CTRL_MSSEN	BIT(28) /* macro driven /SS */
7262306a36Sopenharmony_ci#define CTRL_FRMEN	BIT(31) /* enable framing mode */
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci/* Bit fields of SPI Status Register */
7562306a36Sopenharmony_ci#define STAT_RF_EMPTY	BIT(5) /* RX Fifo empty */
7662306a36Sopenharmony_ci#define STAT_RX_OV	BIT(6) /* err, s/w needs to clear */
7762306a36Sopenharmony_ci#define STAT_TX_UR	BIT(8) /* UR in Framed SPI modes */
7862306a36Sopenharmony_ci#define STAT_FRM_ERR	BIT(12) /* Multiple Frame Sync pulse */
7962306a36Sopenharmony_ci#define STAT_TF_LVL_MASK	0x1F
8062306a36Sopenharmony_ci#define STAT_TF_LVL_SHIFT	16
8162306a36Sopenharmony_ci#define STAT_RF_LVL_MASK	0x1F
8262306a36Sopenharmony_ci#define STAT_RF_LVL_SHIFT	24
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci/* Bit fields of SPI Baud Register */
8562306a36Sopenharmony_ci#define BAUD_MASK		0x1ff
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/* Bit fields of SPI Control2 Register */
8862306a36Sopenharmony_ci#define CTRL2_TX_UR_EN		BIT(10) /* Enable int on Tx under-run */
8962306a36Sopenharmony_ci#define CTRL2_RX_OV_EN		BIT(11) /* Enable int on Rx over-run */
9062306a36Sopenharmony_ci#define CTRL2_FRM_ERR_EN	BIT(12) /* Enable frame err int */
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci/* Minimum DMA transfer size */
9362306a36Sopenharmony_ci#define PIC32_DMA_LEN_MIN	64
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistruct pic32_spi {
9662306a36Sopenharmony_ci	dma_addr_t		dma_base;
9762306a36Sopenharmony_ci	struct pic32_spi_regs __iomem *regs;
9862306a36Sopenharmony_ci	int			fault_irq;
9962306a36Sopenharmony_ci	int			rx_irq;
10062306a36Sopenharmony_ci	int			tx_irq;
10162306a36Sopenharmony_ci	u32			fifo_n_byte; /* FIFO depth in bytes */
10262306a36Sopenharmony_ci	struct clk		*clk;
10362306a36Sopenharmony_ci	struct spi_controller	*host;
10462306a36Sopenharmony_ci	/* Current controller setting */
10562306a36Sopenharmony_ci	u32			speed_hz; /* spi-clk rate */
10662306a36Sopenharmony_ci	u32			mode;
10762306a36Sopenharmony_ci	u32			bits_per_word;
10862306a36Sopenharmony_ci	u32			fifo_n_elm; /* FIFO depth in words */
10962306a36Sopenharmony_ci#define PIC32F_DMA_PREP		0 /* DMA chnls configured */
11062306a36Sopenharmony_ci	unsigned long		flags;
11162306a36Sopenharmony_ci	/* Current transfer state */
11262306a36Sopenharmony_ci	struct completion	xfer_done;
11362306a36Sopenharmony_ci	/* PIO transfer specific */
11462306a36Sopenharmony_ci	const void		*tx;
11562306a36Sopenharmony_ci	const void		*tx_end;
11662306a36Sopenharmony_ci	const void		*rx;
11762306a36Sopenharmony_ci	const void		*rx_end;
11862306a36Sopenharmony_ci	int			len;
11962306a36Sopenharmony_ci	void (*rx_fifo)(struct pic32_spi *);
12062306a36Sopenharmony_ci	void (*tx_fifo)(struct pic32_spi *);
12162306a36Sopenharmony_ci};
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic inline void pic32_spi_enable(struct pic32_spi *pic32s)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	writel(CTRL_ON | CTRL_SIDL, &pic32s->regs->ctrl_set);
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic inline void pic32_spi_disable(struct pic32_spi *pic32s)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	writel(CTRL_ON | CTRL_SIDL, &pic32s->regs->ctrl_clr);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	/* avoid SPI registers read/write at immediate next CPU clock */
13362306a36Sopenharmony_ci	ndelay(20);
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic void pic32_spi_set_clk_rate(struct pic32_spi *pic32s, u32 spi_ck)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	u32 div;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	/* div = (clk_in / 2 * spi_ck) - 1 */
14162306a36Sopenharmony_ci	div = DIV_ROUND_CLOSEST(clk_get_rate(pic32s->clk), 2 * spi_ck) - 1;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	writel(div & BAUD_MASK, &pic32s->regs->baud);
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic inline u32 pic32_rx_fifo_level(struct pic32_spi *pic32s)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	u32 sr = readl(&pic32s->regs->status);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	return (sr >> STAT_RF_LVL_SHIFT) & STAT_RF_LVL_MASK;
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic inline u32 pic32_tx_fifo_level(struct pic32_spi *pic32s)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	u32 sr = readl(&pic32s->regs->status);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	return (sr >> STAT_TF_LVL_SHIFT) & STAT_TF_LVL_MASK;
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci/* Return the max entries we can fill into tx fifo */
16162306a36Sopenharmony_cistatic u32 pic32_tx_max(struct pic32_spi *pic32s, int n_bytes)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	u32 tx_left, tx_room, rxtx_gap;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	tx_left = (pic32s->tx_end - pic32s->tx) / n_bytes;
16662306a36Sopenharmony_ci	tx_room = pic32s->fifo_n_elm - pic32_tx_fifo_level(pic32s);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	/*
16962306a36Sopenharmony_ci	 * Another concern is about the tx/rx mismatch, we
17062306a36Sopenharmony_ci	 * though to use (pic32s->fifo_n_byte - rxfl - txfl) as
17162306a36Sopenharmony_ci	 * one maximum value for tx, but it doesn't cover the
17262306a36Sopenharmony_ci	 * data which is out of tx/rx fifo and inside the
17362306a36Sopenharmony_ci	 * shift registers. So a ctrl from sw point of
17462306a36Sopenharmony_ci	 * view is taken.
17562306a36Sopenharmony_ci	 */
17662306a36Sopenharmony_ci	rxtx_gap = ((pic32s->rx_end - pic32s->rx) -
17762306a36Sopenharmony_ci		    (pic32s->tx_end - pic32s->tx)) / n_bytes;
17862306a36Sopenharmony_ci	return min3(tx_left, tx_room, (u32)(pic32s->fifo_n_elm - rxtx_gap));
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci/* Return the max entries we should read out of rx fifo */
18262306a36Sopenharmony_cistatic u32 pic32_rx_max(struct pic32_spi *pic32s, int n_bytes)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	u32 rx_left = (pic32s->rx_end - pic32s->rx) / n_bytes;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	return min_t(u32, rx_left, pic32_rx_fifo_level(pic32s));
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci#define BUILD_SPI_FIFO_RW(__name, __type, __bwl)		\
19062306a36Sopenharmony_cistatic void pic32_spi_rx_##__name(struct pic32_spi *pic32s)	\
19162306a36Sopenharmony_ci{								\
19262306a36Sopenharmony_ci	__type v;						\
19362306a36Sopenharmony_ci	u32 mx = pic32_rx_max(pic32s, sizeof(__type));		\
19462306a36Sopenharmony_ci	for (; mx; mx--) {					\
19562306a36Sopenharmony_ci		v = read##__bwl(&pic32s->regs->buf);		\
19662306a36Sopenharmony_ci		if (pic32s->rx_end - pic32s->len)		\
19762306a36Sopenharmony_ci			*(__type *)(pic32s->rx) = v;		\
19862306a36Sopenharmony_ci		pic32s->rx += sizeof(__type);			\
19962306a36Sopenharmony_ci	}							\
20062306a36Sopenharmony_ci}								\
20162306a36Sopenharmony_ci								\
20262306a36Sopenharmony_cistatic void pic32_spi_tx_##__name(struct pic32_spi *pic32s)	\
20362306a36Sopenharmony_ci{								\
20462306a36Sopenharmony_ci	__type v;						\
20562306a36Sopenharmony_ci	u32 mx = pic32_tx_max(pic32s, sizeof(__type));		\
20662306a36Sopenharmony_ci	for (; mx ; mx--) {					\
20762306a36Sopenharmony_ci		v = (__type)~0U;				\
20862306a36Sopenharmony_ci		if (pic32s->tx_end - pic32s->len)		\
20962306a36Sopenharmony_ci			v = *(__type *)(pic32s->tx);		\
21062306a36Sopenharmony_ci		write##__bwl(v, &pic32s->regs->buf);		\
21162306a36Sopenharmony_ci		pic32s->tx += sizeof(__type);			\
21262306a36Sopenharmony_ci	}							\
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ciBUILD_SPI_FIFO_RW(byte, u8, b);
21662306a36Sopenharmony_ciBUILD_SPI_FIFO_RW(word, u16, w);
21762306a36Sopenharmony_ciBUILD_SPI_FIFO_RW(dword, u32, l);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic void pic32_err_stop(struct pic32_spi *pic32s, const char *msg)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	/* disable all interrupts */
22262306a36Sopenharmony_ci	disable_irq_nosync(pic32s->fault_irq);
22362306a36Sopenharmony_ci	disable_irq_nosync(pic32s->rx_irq);
22462306a36Sopenharmony_ci	disable_irq_nosync(pic32s->tx_irq);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	/* Show err message and abort xfer with err */
22762306a36Sopenharmony_ci	dev_err(&pic32s->host->dev, "%s\n", msg);
22862306a36Sopenharmony_ci	if (pic32s->host->cur_msg)
22962306a36Sopenharmony_ci		pic32s->host->cur_msg->status = -EIO;
23062306a36Sopenharmony_ci	complete(&pic32s->xfer_done);
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic irqreturn_t pic32_spi_fault_irq(int irq, void *dev_id)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	struct pic32_spi *pic32s = dev_id;
23662306a36Sopenharmony_ci	u32 status;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	status = readl(&pic32s->regs->status);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	/* Error handling */
24162306a36Sopenharmony_ci	if (status & (STAT_RX_OV | STAT_TX_UR)) {
24262306a36Sopenharmony_ci		writel(STAT_RX_OV, &pic32s->regs->status_clr);
24362306a36Sopenharmony_ci		writel(STAT_TX_UR, &pic32s->regs->status_clr);
24462306a36Sopenharmony_ci		pic32_err_stop(pic32s, "err_irq: fifo ov/ur-run\n");
24562306a36Sopenharmony_ci		return IRQ_HANDLED;
24662306a36Sopenharmony_ci	}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	if (status & STAT_FRM_ERR) {
24962306a36Sopenharmony_ci		pic32_err_stop(pic32s, "err_irq: frame error");
25062306a36Sopenharmony_ci		return IRQ_HANDLED;
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	if (!pic32s->host->cur_msg) {
25462306a36Sopenharmony_ci		pic32_err_stop(pic32s, "err_irq: no mesg");
25562306a36Sopenharmony_ci		return IRQ_NONE;
25662306a36Sopenharmony_ci	}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	return IRQ_NONE;
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_cistatic irqreturn_t pic32_spi_rx_irq(int irq, void *dev_id)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	struct pic32_spi *pic32s = dev_id;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	pic32s->rx_fifo(pic32s);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	/* rx complete ? */
26862306a36Sopenharmony_ci	if (pic32s->rx_end == pic32s->rx) {
26962306a36Sopenharmony_ci		/* disable all interrupts */
27062306a36Sopenharmony_ci		disable_irq_nosync(pic32s->fault_irq);
27162306a36Sopenharmony_ci		disable_irq_nosync(pic32s->rx_irq);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci		/* complete current xfer */
27462306a36Sopenharmony_ci		complete(&pic32s->xfer_done);
27562306a36Sopenharmony_ci	}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	return IRQ_HANDLED;
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic irqreturn_t pic32_spi_tx_irq(int irq, void *dev_id)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	struct pic32_spi *pic32s = dev_id;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	pic32s->tx_fifo(pic32s);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	/* tx complete? disable tx interrupt */
28762306a36Sopenharmony_ci	if (pic32s->tx_end == pic32s->tx)
28862306a36Sopenharmony_ci		disable_irq_nosync(pic32s->tx_irq);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	return IRQ_HANDLED;
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic void pic32_spi_dma_rx_notify(void *data)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	struct pic32_spi *pic32s = data;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	complete(&pic32s->xfer_done);
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cistatic int pic32_spi_dma_transfer(struct pic32_spi *pic32s,
30162306a36Sopenharmony_ci				  struct spi_transfer *xfer)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	struct spi_controller *host = pic32s->host;
30462306a36Sopenharmony_ci	struct dma_async_tx_descriptor *desc_rx;
30562306a36Sopenharmony_ci	struct dma_async_tx_descriptor *desc_tx;
30662306a36Sopenharmony_ci	dma_cookie_t cookie;
30762306a36Sopenharmony_ci	int ret;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	if (!host->dma_rx || !host->dma_tx)
31062306a36Sopenharmony_ci		return -ENODEV;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	desc_rx = dmaengine_prep_slave_sg(host->dma_rx,
31362306a36Sopenharmony_ci					  xfer->rx_sg.sgl,
31462306a36Sopenharmony_ci					  xfer->rx_sg.nents,
31562306a36Sopenharmony_ci					  DMA_DEV_TO_MEM,
31662306a36Sopenharmony_ci					  DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
31762306a36Sopenharmony_ci	if (!desc_rx) {
31862306a36Sopenharmony_ci		ret = -EINVAL;
31962306a36Sopenharmony_ci		goto err_dma;
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	desc_tx = dmaengine_prep_slave_sg(host->dma_tx,
32362306a36Sopenharmony_ci					  xfer->tx_sg.sgl,
32462306a36Sopenharmony_ci					  xfer->tx_sg.nents,
32562306a36Sopenharmony_ci					  DMA_MEM_TO_DEV,
32662306a36Sopenharmony_ci					  DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
32762306a36Sopenharmony_ci	if (!desc_tx) {
32862306a36Sopenharmony_ci		ret = -EINVAL;
32962306a36Sopenharmony_ci		goto err_dma;
33062306a36Sopenharmony_ci	}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	/* Put callback on the RX transfer, that should finish last */
33362306a36Sopenharmony_ci	desc_rx->callback = pic32_spi_dma_rx_notify;
33462306a36Sopenharmony_ci	desc_rx->callback_param = pic32s;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	cookie = dmaengine_submit(desc_rx);
33762306a36Sopenharmony_ci	ret = dma_submit_error(cookie);
33862306a36Sopenharmony_ci	if (ret)
33962306a36Sopenharmony_ci		goto err_dma;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	cookie = dmaengine_submit(desc_tx);
34262306a36Sopenharmony_ci	ret = dma_submit_error(cookie);
34362306a36Sopenharmony_ci	if (ret)
34462306a36Sopenharmony_ci		goto err_dma_tx;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	dma_async_issue_pending(host->dma_rx);
34762306a36Sopenharmony_ci	dma_async_issue_pending(host->dma_tx);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	return 0;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cierr_dma_tx:
35262306a36Sopenharmony_ci	dmaengine_terminate_all(host->dma_rx);
35362306a36Sopenharmony_cierr_dma:
35462306a36Sopenharmony_ci	return ret;
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic int pic32_spi_dma_config(struct pic32_spi *pic32s, u32 dma_width)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	int buf_offset = offsetof(struct pic32_spi_regs, buf);
36062306a36Sopenharmony_ci	struct spi_controller *host = pic32s->host;
36162306a36Sopenharmony_ci	struct dma_slave_config cfg;
36262306a36Sopenharmony_ci	int ret;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	memset(&cfg, 0, sizeof(cfg));
36562306a36Sopenharmony_ci	cfg.device_fc = true;
36662306a36Sopenharmony_ci	cfg.src_addr = pic32s->dma_base + buf_offset;
36762306a36Sopenharmony_ci	cfg.dst_addr = pic32s->dma_base + buf_offset;
36862306a36Sopenharmony_ci	cfg.src_maxburst = pic32s->fifo_n_elm / 2; /* fill one-half */
36962306a36Sopenharmony_ci	cfg.dst_maxburst = pic32s->fifo_n_elm / 2; /* drain one-half */
37062306a36Sopenharmony_ci	cfg.src_addr_width = dma_width;
37162306a36Sopenharmony_ci	cfg.dst_addr_width = dma_width;
37262306a36Sopenharmony_ci	/* tx channel */
37362306a36Sopenharmony_ci	cfg.direction = DMA_MEM_TO_DEV;
37462306a36Sopenharmony_ci	ret = dmaengine_slave_config(host->dma_tx, &cfg);
37562306a36Sopenharmony_ci	if (ret) {
37662306a36Sopenharmony_ci		dev_err(&host->dev, "tx channel setup failed\n");
37762306a36Sopenharmony_ci		return ret;
37862306a36Sopenharmony_ci	}
37962306a36Sopenharmony_ci	/* rx channel */
38062306a36Sopenharmony_ci	cfg.direction = DMA_DEV_TO_MEM;
38162306a36Sopenharmony_ci	ret = dmaengine_slave_config(host->dma_rx, &cfg);
38262306a36Sopenharmony_ci	if (ret)
38362306a36Sopenharmony_ci		dev_err(&host->dev, "rx channel setup failed\n");
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	return ret;
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_cistatic int pic32_spi_set_word_size(struct pic32_spi *pic32s, u8 bits_per_word)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	enum dma_slave_buswidth dmawidth;
39162306a36Sopenharmony_ci	u32 buswidth, v;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	switch (bits_per_word) {
39462306a36Sopenharmony_ci	case 8:
39562306a36Sopenharmony_ci		pic32s->rx_fifo = pic32_spi_rx_byte;
39662306a36Sopenharmony_ci		pic32s->tx_fifo = pic32_spi_tx_byte;
39762306a36Sopenharmony_ci		buswidth = PIC32_BPW_8;
39862306a36Sopenharmony_ci		dmawidth = DMA_SLAVE_BUSWIDTH_1_BYTE;
39962306a36Sopenharmony_ci		break;
40062306a36Sopenharmony_ci	case 16:
40162306a36Sopenharmony_ci		pic32s->rx_fifo = pic32_spi_rx_word;
40262306a36Sopenharmony_ci		pic32s->tx_fifo = pic32_spi_tx_word;
40362306a36Sopenharmony_ci		buswidth = PIC32_BPW_16;
40462306a36Sopenharmony_ci		dmawidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
40562306a36Sopenharmony_ci		break;
40662306a36Sopenharmony_ci	case 32:
40762306a36Sopenharmony_ci		pic32s->rx_fifo = pic32_spi_rx_dword;
40862306a36Sopenharmony_ci		pic32s->tx_fifo = pic32_spi_tx_dword;
40962306a36Sopenharmony_ci		buswidth = PIC32_BPW_32;
41062306a36Sopenharmony_ci		dmawidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
41162306a36Sopenharmony_ci		break;
41262306a36Sopenharmony_ci	default:
41362306a36Sopenharmony_ci		/* not supported */
41462306a36Sopenharmony_ci		return -EINVAL;
41562306a36Sopenharmony_ci	}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	/* calculate maximum number of words fifos can hold */
41862306a36Sopenharmony_ci	pic32s->fifo_n_elm = DIV_ROUND_UP(pic32s->fifo_n_byte,
41962306a36Sopenharmony_ci					  bits_per_word / 8);
42062306a36Sopenharmony_ci	/* set word size */
42162306a36Sopenharmony_ci	v = readl(&pic32s->regs->ctrl);
42262306a36Sopenharmony_ci	v &= ~(CTRL_BPW_MASK << CTRL_BPW_SHIFT);
42362306a36Sopenharmony_ci	v |= buswidth << CTRL_BPW_SHIFT;
42462306a36Sopenharmony_ci	writel(v, &pic32s->regs->ctrl);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	/* re-configure dma width, if required */
42762306a36Sopenharmony_ci	if (test_bit(PIC32F_DMA_PREP, &pic32s->flags))
42862306a36Sopenharmony_ci		pic32_spi_dma_config(pic32s, dmawidth);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	return 0;
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_cistatic int pic32_spi_prepare_hardware(struct spi_controller *host)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	struct pic32_spi *pic32s = spi_controller_get_devdata(host);
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	pic32_spi_enable(pic32s);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	return 0;
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic int pic32_spi_prepare_message(struct spi_controller *host,
44362306a36Sopenharmony_ci				     struct spi_message *msg)
44462306a36Sopenharmony_ci{
44562306a36Sopenharmony_ci	struct pic32_spi *pic32s = spi_controller_get_devdata(host);
44662306a36Sopenharmony_ci	struct spi_device *spi = msg->spi;
44762306a36Sopenharmony_ci	u32 val;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	/* set device specific bits_per_word */
45062306a36Sopenharmony_ci	if (pic32s->bits_per_word != spi->bits_per_word) {
45162306a36Sopenharmony_ci		pic32_spi_set_word_size(pic32s, spi->bits_per_word);
45262306a36Sopenharmony_ci		pic32s->bits_per_word = spi->bits_per_word;
45362306a36Sopenharmony_ci	}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	/* device specific speed change */
45662306a36Sopenharmony_ci	if (pic32s->speed_hz != spi->max_speed_hz) {
45762306a36Sopenharmony_ci		pic32_spi_set_clk_rate(pic32s, spi->max_speed_hz);
45862306a36Sopenharmony_ci		pic32s->speed_hz = spi->max_speed_hz;
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	/* device specific mode change */
46262306a36Sopenharmony_ci	if (pic32s->mode != spi->mode) {
46362306a36Sopenharmony_ci		val = readl(&pic32s->regs->ctrl);
46462306a36Sopenharmony_ci		/* active low */
46562306a36Sopenharmony_ci		if (spi->mode & SPI_CPOL)
46662306a36Sopenharmony_ci			val |= CTRL_CKP;
46762306a36Sopenharmony_ci		else
46862306a36Sopenharmony_ci			val &= ~CTRL_CKP;
46962306a36Sopenharmony_ci		/* tx on rising edge */
47062306a36Sopenharmony_ci		if (spi->mode & SPI_CPHA)
47162306a36Sopenharmony_ci			val &= ~CTRL_CKE;
47262306a36Sopenharmony_ci		else
47362306a36Sopenharmony_ci			val |= CTRL_CKE;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci		/* rx at end of tx */
47662306a36Sopenharmony_ci		val |= CTRL_SMP;
47762306a36Sopenharmony_ci		writel(val, &pic32s->regs->ctrl);
47862306a36Sopenharmony_ci		pic32s->mode = spi->mode;
47962306a36Sopenharmony_ci	}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	return 0;
48262306a36Sopenharmony_ci}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistatic bool pic32_spi_can_dma(struct spi_controller *host,
48562306a36Sopenharmony_ci			      struct spi_device *spi,
48662306a36Sopenharmony_ci			      struct spi_transfer *xfer)
48762306a36Sopenharmony_ci{
48862306a36Sopenharmony_ci	struct pic32_spi *pic32s = spi_controller_get_devdata(host);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	/* skip using DMA on small size transfer to avoid overhead.*/
49162306a36Sopenharmony_ci	return (xfer->len >= PIC32_DMA_LEN_MIN) &&
49262306a36Sopenharmony_ci	       test_bit(PIC32F_DMA_PREP, &pic32s->flags);
49362306a36Sopenharmony_ci}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_cistatic int pic32_spi_one_transfer(struct spi_controller *host,
49662306a36Sopenharmony_ci				  struct spi_device *spi,
49762306a36Sopenharmony_ci				  struct spi_transfer *transfer)
49862306a36Sopenharmony_ci{
49962306a36Sopenharmony_ci	struct pic32_spi *pic32s;
50062306a36Sopenharmony_ci	bool dma_issued = false;
50162306a36Sopenharmony_ci	unsigned long timeout;
50262306a36Sopenharmony_ci	int ret;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	pic32s = spi_controller_get_devdata(host);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	/* handle transfer specific word size change */
50762306a36Sopenharmony_ci	if (transfer->bits_per_word &&
50862306a36Sopenharmony_ci	    (transfer->bits_per_word != pic32s->bits_per_word)) {
50962306a36Sopenharmony_ci		ret = pic32_spi_set_word_size(pic32s, transfer->bits_per_word);
51062306a36Sopenharmony_ci		if (ret)
51162306a36Sopenharmony_ci			return ret;
51262306a36Sopenharmony_ci		pic32s->bits_per_word = transfer->bits_per_word;
51362306a36Sopenharmony_ci	}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	/* handle transfer specific speed change */
51662306a36Sopenharmony_ci	if (transfer->speed_hz && (transfer->speed_hz != pic32s->speed_hz)) {
51762306a36Sopenharmony_ci		pic32_spi_set_clk_rate(pic32s, transfer->speed_hz);
51862306a36Sopenharmony_ci		pic32s->speed_hz = transfer->speed_hz;
51962306a36Sopenharmony_ci	}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	reinit_completion(&pic32s->xfer_done);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	/* transact by DMA mode */
52462306a36Sopenharmony_ci	if (transfer->rx_sg.nents && transfer->tx_sg.nents) {
52562306a36Sopenharmony_ci		ret = pic32_spi_dma_transfer(pic32s, transfer);
52662306a36Sopenharmony_ci		if (ret) {
52762306a36Sopenharmony_ci			dev_err(&spi->dev, "dma submit error\n");
52862306a36Sopenharmony_ci			return ret;
52962306a36Sopenharmony_ci		}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci		/* DMA issued */
53262306a36Sopenharmony_ci		dma_issued = true;
53362306a36Sopenharmony_ci	} else {
53462306a36Sopenharmony_ci		/* set current transfer information */
53562306a36Sopenharmony_ci		pic32s->tx = (const void *)transfer->tx_buf;
53662306a36Sopenharmony_ci		pic32s->rx = (const void *)transfer->rx_buf;
53762306a36Sopenharmony_ci		pic32s->tx_end = pic32s->tx + transfer->len;
53862306a36Sopenharmony_ci		pic32s->rx_end = pic32s->rx + transfer->len;
53962306a36Sopenharmony_ci		pic32s->len = transfer->len;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci		/* transact by interrupt driven PIO */
54262306a36Sopenharmony_ci		enable_irq(pic32s->fault_irq);
54362306a36Sopenharmony_ci		enable_irq(pic32s->rx_irq);
54462306a36Sopenharmony_ci		enable_irq(pic32s->tx_irq);
54562306a36Sopenharmony_ci	}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	/* wait for completion */
54862306a36Sopenharmony_ci	timeout = wait_for_completion_timeout(&pic32s->xfer_done, 2 * HZ);
54962306a36Sopenharmony_ci	if (timeout == 0) {
55062306a36Sopenharmony_ci		dev_err(&spi->dev, "wait error/timedout\n");
55162306a36Sopenharmony_ci		if (dma_issued) {
55262306a36Sopenharmony_ci			dmaengine_terminate_all(host->dma_rx);
55362306a36Sopenharmony_ci			dmaengine_terminate_all(host->dma_tx);
55462306a36Sopenharmony_ci		}
55562306a36Sopenharmony_ci		ret = -ETIMEDOUT;
55662306a36Sopenharmony_ci	} else {
55762306a36Sopenharmony_ci		ret = 0;
55862306a36Sopenharmony_ci	}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	return ret;
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_cistatic int pic32_spi_unprepare_message(struct spi_controller *host,
56462306a36Sopenharmony_ci				       struct spi_message *msg)
56562306a36Sopenharmony_ci{
56662306a36Sopenharmony_ci	/* nothing to do */
56762306a36Sopenharmony_ci	return 0;
56862306a36Sopenharmony_ci}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_cistatic int pic32_spi_unprepare_hardware(struct spi_controller *host)
57162306a36Sopenharmony_ci{
57262306a36Sopenharmony_ci	struct pic32_spi *pic32s = spi_controller_get_devdata(host);
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	pic32_spi_disable(pic32s);
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	return 0;
57762306a36Sopenharmony_ci}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci/* This may be called multiple times by same spi dev */
58062306a36Sopenharmony_cistatic int pic32_spi_setup(struct spi_device *spi)
58162306a36Sopenharmony_ci{
58262306a36Sopenharmony_ci	if (!spi->max_speed_hz) {
58362306a36Sopenharmony_ci		dev_err(&spi->dev, "No max speed HZ parameter\n");
58462306a36Sopenharmony_ci		return -EINVAL;
58562306a36Sopenharmony_ci	}
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	/* PIC32 spi controller can drive /CS during transfer depending
58862306a36Sopenharmony_ci	 * on tx fifo fill-level. /CS will stay asserted as long as TX
58962306a36Sopenharmony_ci	 * fifo is non-empty, else will be deasserted indicating
59062306a36Sopenharmony_ci	 * completion of the ongoing transfer. This might result into
59162306a36Sopenharmony_ci	 * unreliable/erroneous SPI transactions.
59262306a36Sopenharmony_ci	 * To avoid that we will always handle /CS by toggling GPIO.
59362306a36Sopenharmony_ci	 */
59462306a36Sopenharmony_ci	if (!spi_get_csgpiod(spi, 0))
59562306a36Sopenharmony_ci		return -EINVAL;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	return 0;
59862306a36Sopenharmony_ci}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_cistatic void pic32_spi_cleanup(struct spi_device *spi)
60162306a36Sopenharmony_ci{
60262306a36Sopenharmony_ci	/* de-activate cs-gpio, gpiolib will handle inversion */
60362306a36Sopenharmony_ci	gpiod_direction_output(spi_get_csgpiod(spi, 0), 0);
60462306a36Sopenharmony_ci}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_cistatic int pic32_spi_dma_prep(struct pic32_spi *pic32s, struct device *dev)
60762306a36Sopenharmony_ci{
60862306a36Sopenharmony_ci	struct spi_controller *host = pic32s->host;
60962306a36Sopenharmony_ci	int ret = 0;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	host->dma_rx = dma_request_chan(dev, "spi-rx");
61262306a36Sopenharmony_ci	if (IS_ERR(host->dma_rx)) {
61362306a36Sopenharmony_ci		if (PTR_ERR(host->dma_rx) == -EPROBE_DEFER)
61462306a36Sopenharmony_ci			ret = -EPROBE_DEFER;
61562306a36Sopenharmony_ci		else
61662306a36Sopenharmony_ci			dev_warn(dev, "RX channel not found.\n");
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci		host->dma_rx = NULL;
61962306a36Sopenharmony_ci		goto out_err;
62062306a36Sopenharmony_ci	}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	host->dma_tx = dma_request_chan(dev, "spi-tx");
62362306a36Sopenharmony_ci	if (IS_ERR(host->dma_tx)) {
62462306a36Sopenharmony_ci		if (PTR_ERR(host->dma_tx) == -EPROBE_DEFER)
62562306a36Sopenharmony_ci			ret = -EPROBE_DEFER;
62662306a36Sopenharmony_ci		else
62762306a36Sopenharmony_ci			dev_warn(dev, "TX channel not found.\n");
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci		host->dma_tx = NULL;
63062306a36Sopenharmony_ci		goto out_err;
63162306a36Sopenharmony_ci	}
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	if (pic32_spi_dma_config(pic32s, DMA_SLAVE_BUSWIDTH_1_BYTE))
63462306a36Sopenharmony_ci		goto out_err;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	/* DMA chnls allocated and prepared */
63762306a36Sopenharmony_ci	set_bit(PIC32F_DMA_PREP, &pic32s->flags);
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	return 0;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ciout_err:
64262306a36Sopenharmony_ci	if (host->dma_rx) {
64362306a36Sopenharmony_ci		dma_release_channel(host->dma_rx);
64462306a36Sopenharmony_ci		host->dma_rx = NULL;
64562306a36Sopenharmony_ci	}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	if (host->dma_tx) {
64862306a36Sopenharmony_ci		dma_release_channel(host->dma_tx);
64962306a36Sopenharmony_ci		host->dma_tx = NULL;
65062306a36Sopenharmony_ci	}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	return ret;
65362306a36Sopenharmony_ci}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_cistatic void pic32_spi_dma_unprep(struct pic32_spi *pic32s)
65662306a36Sopenharmony_ci{
65762306a36Sopenharmony_ci	if (!test_bit(PIC32F_DMA_PREP, &pic32s->flags))
65862306a36Sopenharmony_ci		return;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	clear_bit(PIC32F_DMA_PREP, &pic32s->flags);
66162306a36Sopenharmony_ci	if (pic32s->host->dma_rx)
66262306a36Sopenharmony_ci		dma_release_channel(pic32s->host->dma_rx);
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	if (pic32s->host->dma_tx)
66562306a36Sopenharmony_ci		dma_release_channel(pic32s->host->dma_tx);
66662306a36Sopenharmony_ci}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_cistatic void pic32_spi_hw_init(struct pic32_spi *pic32s)
66962306a36Sopenharmony_ci{
67062306a36Sopenharmony_ci	u32 ctrl;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	/* disable hardware */
67362306a36Sopenharmony_ci	pic32_spi_disable(pic32s);
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	ctrl = readl(&pic32s->regs->ctrl);
67662306a36Sopenharmony_ci	/* enable enhanced fifo of 128bit deep */
67762306a36Sopenharmony_ci	ctrl |= CTRL_ENHBUF;
67862306a36Sopenharmony_ci	pic32s->fifo_n_byte = 16;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	/* disable framing mode */
68162306a36Sopenharmony_ci	ctrl &= ~CTRL_FRMEN;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	/* enable host mode while disabled */
68462306a36Sopenharmony_ci	ctrl |= CTRL_MSTEN;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	/* set tx fifo threshold interrupt */
68762306a36Sopenharmony_ci	ctrl &= ~(0x3 << CTRL_TX_INT_SHIFT);
68862306a36Sopenharmony_ci	ctrl |= (TX_FIFO_HALF_EMPTY << CTRL_TX_INT_SHIFT);
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	/* set rx fifo threshold interrupt */
69162306a36Sopenharmony_ci	ctrl &= ~(0x3 << CTRL_RX_INT_SHIFT);
69262306a36Sopenharmony_ci	ctrl |= (RX_FIFO_NOT_EMPTY << CTRL_RX_INT_SHIFT);
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	/* select clk source */
69562306a36Sopenharmony_ci	ctrl &= ~CTRL_MCLKSEL;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	/* set manual /CS mode */
69862306a36Sopenharmony_ci	ctrl &= ~CTRL_MSSEN;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	writel(ctrl, &pic32s->regs->ctrl);
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	/* enable error reporting */
70362306a36Sopenharmony_ci	ctrl = CTRL2_TX_UR_EN | CTRL2_RX_OV_EN | CTRL2_FRM_ERR_EN;
70462306a36Sopenharmony_ci	writel(ctrl, &pic32s->regs->ctrl2_set);
70562306a36Sopenharmony_ci}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_cistatic int pic32_spi_hw_probe(struct platform_device *pdev,
70862306a36Sopenharmony_ci			      struct pic32_spi *pic32s)
70962306a36Sopenharmony_ci{
71062306a36Sopenharmony_ci	struct resource *mem;
71162306a36Sopenharmony_ci	int ret;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	pic32s->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
71462306a36Sopenharmony_ci	if (IS_ERR(pic32s->regs))
71562306a36Sopenharmony_ci		return PTR_ERR(pic32s->regs);
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	pic32s->dma_base = mem->start;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	/* get irq resources: err-irq, rx-irq, tx-irq */
72062306a36Sopenharmony_ci	pic32s->fault_irq = platform_get_irq_byname(pdev, "fault");
72162306a36Sopenharmony_ci	if (pic32s->fault_irq < 0)
72262306a36Sopenharmony_ci		return pic32s->fault_irq;
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	pic32s->rx_irq = platform_get_irq_byname(pdev, "rx");
72562306a36Sopenharmony_ci	if (pic32s->rx_irq < 0)
72662306a36Sopenharmony_ci		return pic32s->rx_irq;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	pic32s->tx_irq = platform_get_irq_byname(pdev, "tx");
72962306a36Sopenharmony_ci	if (pic32s->tx_irq < 0)
73062306a36Sopenharmony_ci		return pic32s->tx_irq;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	/* get clock */
73362306a36Sopenharmony_ci	pic32s->clk = devm_clk_get(&pdev->dev, "mck0");
73462306a36Sopenharmony_ci	if (IS_ERR(pic32s->clk)) {
73562306a36Sopenharmony_ci		dev_err(&pdev->dev, "clk not found\n");
73662306a36Sopenharmony_ci		ret = PTR_ERR(pic32s->clk);
73762306a36Sopenharmony_ci		goto err_unmap_mem;
73862306a36Sopenharmony_ci	}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	ret = clk_prepare_enable(pic32s->clk);
74162306a36Sopenharmony_ci	if (ret)
74262306a36Sopenharmony_ci		goto err_unmap_mem;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	pic32_spi_hw_init(pic32s);
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	return 0;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_cierr_unmap_mem:
74962306a36Sopenharmony_ci	dev_err(&pdev->dev, "%s failed, err %d\n", __func__, ret);
75062306a36Sopenharmony_ci	return ret;
75162306a36Sopenharmony_ci}
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_cistatic int pic32_spi_probe(struct platform_device *pdev)
75462306a36Sopenharmony_ci{
75562306a36Sopenharmony_ci	struct spi_controller *host;
75662306a36Sopenharmony_ci	struct pic32_spi *pic32s;
75762306a36Sopenharmony_ci	int ret;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	host = spi_alloc_host(&pdev->dev, sizeof(*pic32s));
76062306a36Sopenharmony_ci	if (!host)
76162306a36Sopenharmony_ci		return -ENOMEM;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	pic32s = spi_controller_get_devdata(host);
76462306a36Sopenharmony_ci	pic32s->host = host;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	ret = pic32_spi_hw_probe(pdev, pic32s);
76762306a36Sopenharmony_ci	if (ret)
76862306a36Sopenharmony_ci		goto err_host;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	host->dev.of_node	= pdev->dev.of_node;
77162306a36Sopenharmony_ci	host->mode_bits	= SPI_MODE_3 | SPI_MODE_0 | SPI_CS_HIGH;
77262306a36Sopenharmony_ci	host->num_chipselect	= 1; /* single chip-select */
77362306a36Sopenharmony_ci	host->max_speed_hz	= clk_get_rate(pic32s->clk);
77462306a36Sopenharmony_ci	host->setup		= pic32_spi_setup;
77562306a36Sopenharmony_ci	host->cleanup		= pic32_spi_cleanup;
77662306a36Sopenharmony_ci	host->flags		= SPI_CONTROLLER_MUST_TX | SPI_CONTROLLER_MUST_RX;
77762306a36Sopenharmony_ci	host->bits_per_word_mask	= SPI_BPW_MASK(8) | SPI_BPW_MASK(16) |
77862306a36Sopenharmony_ci					  SPI_BPW_MASK(32);
77962306a36Sopenharmony_ci	host->transfer_one		= pic32_spi_one_transfer;
78062306a36Sopenharmony_ci	host->prepare_message		= pic32_spi_prepare_message;
78162306a36Sopenharmony_ci	host->unprepare_message	= pic32_spi_unprepare_message;
78262306a36Sopenharmony_ci	host->prepare_transfer_hardware	= pic32_spi_prepare_hardware;
78362306a36Sopenharmony_ci	host->unprepare_transfer_hardware	= pic32_spi_unprepare_hardware;
78462306a36Sopenharmony_ci	host->use_gpio_descriptors = true;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	/* optional DMA support */
78762306a36Sopenharmony_ci	ret = pic32_spi_dma_prep(pic32s, &pdev->dev);
78862306a36Sopenharmony_ci	if (ret)
78962306a36Sopenharmony_ci		goto err_bailout;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	if (test_bit(PIC32F_DMA_PREP, &pic32s->flags))
79262306a36Sopenharmony_ci		host->can_dma	= pic32_spi_can_dma;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	init_completion(&pic32s->xfer_done);
79562306a36Sopenharmony_ci	pic32s->mode = -1;
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	/* install irq handlers (with irq-disabled) */
79862306a36Sopenharmony_ci	irq_set_status_flags(pic32s->fault_irq, IRQ_NOAUTOEN);
79962306a36Sopenharmony_ci	ret = devm_request_irq(&pdev->dev, pic32s->fault_irq,
80062306a36Sopenharmony_ci			       pic32_spi_fault_irq, IRQF_NO_THREAD,
80162306a36Sopenharmony_ci			       dev_name(&pdev->dev), pic32s);
80262306a36Sopenharmony_ci	if (ret < 0) {
80362306a36Sopenharmony_ci		dev_err(&pdev->dev, "request fault-irq %d\n", pic32s->rx_irq);
80462306a36Sopenharmony_ci		goto err_bailout;
80562306a36Sopenharmony_ci	}
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	/* receive interrupt handler */
80862306a36Sopenharmony_ci	irq_set_status_flags(pic32s->rx_irq, IRQ_NOAUTOEN);
80962306a36Sopenharmony_ci	ret = devm_request_irq(&pdev->dev, pic32s->rx_irq,
81062306a36Sopenharmony_ci			       pic32_spi_rx_irq, IRQF_NO_THREAD,
81162306a36Sopenharmony_ci			       dev_name(&pdev->dev), pic32s);
81262306a36Sopenharmony_ci	if (ret < 0) {
81362306a36Sopenharmony_ci		dev_err(&pdev->dev, "request rx-irq %d\n", pic32s->rx_irq);
81462306a36Sopenharmony_ci		goto err_bailout;
81562306a36Sopenharmony_ci	}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	/* transmit interrupt handler */
81862306a36Sopenharmony_ci	irq_set_status_flags(pic32s->tx_irq, IRQ_NOAUTOEN);
81962306a36Sopenharmony_ci	ret = devm_request_irq(&pdev->dev, pic32s->tx_irq,
82062306a36Sopenharmony_ci			       pic32_spi_tx_irq, IRQF_NO_THREAD,
82162306a36Sopenharmony_ci			       dev_name(&pdev->dev), pic32s);
82262306a36Sopenharmony_ci	if (ret < 0) {
82362306a36Sopenharmony_ci		dev_err(&pdev->dev, "request tx-irq %d\n", pic32s->tx_irq);
82462306a36Sopenharmony_ci		goto err_bailout;
82562306a36Sopenharmony_ci	}
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	/* register host */
82862306a36Sopenharmony_ci	ret = devm_spi_register_controller(&pdev->dev, host);
82962306a36Sopenharmony_ci	if (ret) {
83062306a36Sopenharmony_ci		dev_err(&host->dev, "failed registering spi host\n");
83162306a36Sopenharmony_ci		goto err_bailout;
83262306a36Sopenharmony_ci	}
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	platform_set_drvdata(pdev, pic32s);
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	return 0;
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_cierr_bailout:
83962306a36Sopenharmony_ci	pic32_spi_dma_unprep(pic32s);
84062306a36Sopenharmony_ci	clk_disable_unprepare(pic32s->clk);
84162306a36Sopenharmony_cierr_host:
84262306a36Sopenharmony_ci	spi_controller_put(host);
84362306a36Sopenharmony_ci	return ret;
84462306a36Sopenharmony_ci}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_cistatic void pic32_spi_remove(struct platform_device *pdev)
84762306a36Sopenharmony_ci{
84862306a36Sopenharmony_ci	struct pic32_spi *pic32s;
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	pic32s = platform_get_drvdata(pdev);
85162306a36Sopenharmony_ci	pic32_spi_disable(pic32s);
85262306a36Sopenharmony_ci	clk_disable_unprepare(pic32s->clk);
85362306a36Sopenharmony_ci	pic32_spi_dma_unprep(pic32s);
85462306a36Sopenharmony_ci}
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_cistatic const struct of_device_id pic32_spi_of_match[] = {
85762306a36Sopenharmony_ci	{.compatible = "microchip,pic32mzda-spi",},
85862306a36Sopenharmony_ci	{},
85962306a36Sopenharmony_ci};
86062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, pic32_spi_of_match);
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_cistatic struct platform_driver pic32_spi_driver = {
86362306a36Sopenharmony_ci	.driver = {
86462306a36Sopenharmony_ci		.name = "spi-pic32",
86562306a36Sopenharmony_ci		.of_match_table = of_match_ptr(pic32_spi_of_match),
86662306a36Sopenharmony_ci	},
86762306a36Sopenharmony_ci	.probe = pic32_spi_probe,
86862306a36Sopenharmony_ci	.remove_new = pic32_spi_remove,
86962306a36Sopenharmony_ci};
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_cimodule_platform_driver(pic32_spi_driver);
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ciMODULE_AUTHOR("Purna Chandra Mandal <purna.mandal@microchip.com>");
87462306a36Sopenharmony_ciMODULE_DESCRIPTION("Microchip SPI driver for PIC32 SPI controller.");
87562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
876