162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Broadcom BCM63xx SPI controller support
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2009-2012 Florian Fainelli <florian@openwrt.org>
662306a36Sopenharmony_ci * Copyright (C) 2010 Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/clk.h>
1162306a36Sopenharmony_ci#include <linux/io.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/platform_device.h>
1462306a36Sopenharmony_ci#include <linux/delay.h>
1562306a36Sopenharmony_ci#include <linux/interrupt.h>
1662306a36Sopenharmony_ci#include <linux/spi/spi.h>
1762306a36Sopenharmony_ci#include <linux/completion.h>
1862306a36Sopenharmony_ci#include <linux/err.h>
1962306a36Sopenharmony_ci#include <linux/pm_runtime.h>
2062306a36Sopenharmony_ci#include <linux/of.h>
2162306a36Sopenharmony_ci#include <linux/reset.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/* BCM 6338/6348 SPI core */
2462306a36Sopenharmony_ci#define SPI_6348_RSET_SIZE		64
2562306a36Sopenharmony_ci#define SPI_6348_CMD			0x00	/* 16-bits register */
2662306a36Sopenharmony_ci#define SPI_6348_INT_STATUS		0x02
2762306a36Sopenharmony_ci#define SPI_6348_INT_MASK_ST		0x03
2862306a36Sopenharmony_ci#define SPI_6348_INT_MASK		0x04
2962306a36Sopenharmony_ci#define SPI_6348_ST			0x05
3062306a36Sopenharmony_ci#define SPI_6348_CLK_CFG		0x06
3162306a36Sopenharmony_ci#define SPI_6348_FILL_BYTE		0x07
3262306a36Sopenharmony_ci#define SPI_6348_MSG_TAIL		0x09
3362306a36Sopenharmony_ci#define SPI_6348_RX_TAIL		0x0b
3462306a36Sopenharmony_ci#define SPI_6348_MSG_CTL		0x40	/* 8-bits register */
3562306a36Sopenharmony_ci#define SPI_6348_MSG_CTL_WIDTH		8
3662306a36Sopenharmony_ci#define SPI_6348_MSG_DATA		0x41
3762306a36Sopenharmony_ci#define SPI_6348_MSG_DATA_SIZE		0x3f
3862306a36Sopenharmony_ci#define SPI_6348_RX_DATA		0x80
3962306a36Sopenharmony_ci#define SPI_6348_RX_DATA_SIZE		0x3f
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/* BCM 3368/6358/6262/6368 SPI core */
4262306a36Sopenharmony_ci#define SPI_6358_RSET_SIZE		1804
4362306a36Sopenharmony_ci#define SPI_6358_MSG_CTL		0x00	/* 16-bits register */
4462306a36Sopenharmony_ci#define SPI_6358_MSG_CTL_WIDTH		16
4562306a36Sopenharmony_ci#define SPI_6358_MSG_DATA		0x02
4662306a36Sopenharmony_ci#define SPI_6358_MSG_DATA_SIZE		0x21e
4762306a36Sopenharmony_ci#define SPI_6358_RX_DATA		0x400
4862306a36Sopenharmony_ci#define SPI_6358_RX_DATA_SIZE		0x220
4962306a36Sopenharmony_ci#define SPI_6358_CMD			0x700	/* 16-bits register */
5062306a36Sopenharmony_ci#define SPI_6358_INT_STATUS		0x702
5162306a36Sopenharmony_ci#define SPI_6358_INT_MASK_ST		0x703
5262306a36Sopenharmony_ci#define SPI_6358_INT_MASK		0x704
5362306a36Sopenharmony_ci#define SPI_6358_ST			0x705
5462306a36Sopenharmony_ci#define SPI_6358_CLK_CFG		0x706
5562306a36Sopenharmony_ci#define SPI_6358_FILL_BYTE		0x707
5662306a36Sopenharmony_ci#define SPI_6358_MSG_TAIL		0x709
5762306a36Sopenharmony_ci#define SPI_6358_RX_TAIL		0x70B
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci/* Shared SPI definitions */
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/* Message configuration */
6262306a36Sopenharmony_ci#define SPI_FD_RW			0x00
6362306a36Sopenharmony_ci#define SPI_HD_W			0x01
6462306a36Sopenharmony_ci#define SPI_HD_R			0x02
6562306a36Sopenharmony_ci#define SPI_BYTE_CNT_SHIFT		0
6662306a36Sopenharmony_ci#define SPI_6348_MSG_TYPE_SHIFT		6
6762306a36Sopenharmony_ci#define SPI_6358_MSG_TYPE_SHIFT		14
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/* Command */
7062306a36Sopenharmony_ci#define SPI_CMD_NOOP			0x00
7162306a36Sopenharmony_ci#define SPI_CMD_SOFT_RESET		0x01
7262306a36Sopenharmony_ci#define SPI_CMD_HARD_RESET		0x02
7362306a36Sopenharmony_ci#define SPI_CMD_START_IMMEDIATE		0x03
7462306a36Sopenharmony_ci#define SPI_CMD_COMMAND_SHIFT		0
7562306a36Sopenharmony_ci#define SPI_CMD_COMMAND_MASK		0x000f
7662306a36Sopenharmony_ci#define SPI_CMD_DEVICE_ID_SHIFT		4
7762306a36Sopenharmony_ci#define SPI_CMD_PREPEND_BYTE_CNT_SHIFT	8
7862306a36Sopenharmony_ci#define SPI_CMD_ONE_BYTE_SHIFT		11
7962306a36Sopenharmony_ci#define SPI_CMD_ONE_WIRE_SHIFT		12
8062306a36Sopenharmony_ci#define SPI_DEV_ID_0			0
8162306a36Sopenharmony_ci#define SPI_DEV_ID_1			1
8262306a36Sopenharmony_ci#define SPI_DEV_ID_2			2
8362306a36Sopenharmony_ci#define SPI_DEV_ID_3			3
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/* Interrupt mask */
8662306a36Sopenharmony_ci#define SPI_INTR_CMD_DONE		0x01
8762306a36Sopenharmony_ci#define SPI_INTR_RX_OVERFLOW		0x02
8862306a36Sopenharmony_ci#define SPI_INTR_TX_UNDERFLOW		0x04
8962306a36Sopenharmony_ci#define SPI_INTR_TX_OVERFLOW		0x08
9062306a36Sopenharmony_ci#define SPI_INTR_RX_UNDERFLOW		0x10
9162306a36Sopenharmony_ci#define SPI_INTR_CLEAR_ALL		0x1f
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci/* Status */
9462306a36Sopenharmony_ci#define SPI_RX_EMPTY			0x02
9562306a36Sopenharmony_ci#define SPI_CMD_BUSY			0x04
9662306a36Sopenharmony_ci#define SPI_SERIAL_BUSY			0x08
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci/* Clock configuration */
9962306a36Sopenharmony_ci#define SPI_CLK_20MHZ			0x00
10062306a36Sopenharmony_ci#define SPI_CLK_0_391MHZ		0x01
10162306a36Sopenharmony_ci#define SPI_CLK_0_781MHZ		0x02	/* default */
10262306a36Sopenharmony_ci#define SPI_CLK_1_563MHZ		0x03
10362306a36Sopenharmony_ci#define SPI_CLK_3_125MHZ		0x04
10462306a36Sopenharmony_ci#define SPI_CLK_6_250MHZ		0x05
10562306a36Sopenharmony_ci#define SPI_CLK_12_50MHZ		0x06
10662306a36Sopenharmony_ci#define SPI_CLK_MASK			0x07
10762306a36Sopenharmony_ci#define SPI_SSOFFTIME_MASK		0x38
10862306a36Sopenharmony_ci#define SPI_SSOFFTIME_SHIFT		3
10962306a36Sopenharmony_ci#define SPI_BYTE_SWAP			0x80
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cienum bcm63xx_regs_spi {
11262306a36Sopenharmony_ci	SPI_CMD,
11362306a36Sopenharmony_ci	SPI_INT_STATUS,
11462306a36Sopenharmony_ci	SPI_INT_MASK_ST,
11562306a36Sopenharmony_ci	SPI_INT_MASK,
11662306a36Sopenharmony_ci	SPI_ST,
11762306a36Sopenharmony_ci	SPI_CLK_CFG,
11862306a36Sopenharmony_ci	SPI_FILL_BYTE,
11962306a36Sopenharmony_ci	SPI_MSG_TAIL,
12062306a36Sopenharmony_ci	SPI_RX_TAIL,
12162306a36Sopenharmony_ci	SPI_MSG_CTL,
12262306a36Sopenharmony_ci	SPI_MSG_DATA,
12362306a36Sopenharmony_ci	SPI_RX_DATA,
12462306a36Sopenharmony_ci	SPI_MSG_TYPE_SHIFT,
12562306a36Sopenharmony_ci	SPI_MSG_CTL_WIDTH,
12662306a36Sopenharmony_ci	SPI_MSG_DATA_SIZE,
12762306a36Sopenharmony_ci};
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci#define BCM63XX_SPI_MAX_PREPEND		7
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci#define BCM63XX_SPI_MAX_CS		8
13262306a36Sopenharmony_ci#define BCM63XX_SPI_BUS_NUM		0
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistruct bcm63xx_spi {
13562306a36Sopenharmony_ci	struct completion	done;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	void __iomem		*regs;
13862306a36Sopenharmony_ci	int			irq;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	/* Platform data */
14162306a36Sopenharmony_ci	const unsigned long	*reg_offsets;
14262306a36Sopenharmony_ci	unsigned int		fifo_size;
14362306a36Sopenharmony_ci	unsigned int		msg_type_shift;
14462306a36Sopenharmony_ci	unsigned int		msg_ctl_width;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	/* data iomem */
14762306a36Sopenharmony_ci	u8 __iomem		*tx_io;
14862306a36Sopenharmony_ci	const u8 __iomem	*rx_io;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	struct clk		*clk;
15162306a36Sopenharmony_ci	struct platform_device	*pdev;
15262306a36Sopenharmony_ci};
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic inline u8 bcm_spi_readb(struct bcm63xx_spi *bs,
15562306a36Sopenharmony_ci			       unsigned int offset)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	return readb(bs->regs + bs->reg_offsets[offset]);
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic inline void bcm_spi_writeb(struct bcm63xx_spi *bs,
16162306a36Sopenharmony_ci				  u8 value, unsigned int offset)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	writeb(value, bs->regs + bs->reg_offsets[offset]);
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic inline void bcm_spi_writew(struct bcm63xx_spi *bs,
16762306a36Sopenharmony_ci				  u16 value, unsigned int offset)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci#ifdef CONFIG_CPU_BIG_ENDIAN
17062306a36Sopenharmony_ci	iowrite16be(value, bs->regs + bs->reg_offsets[offset]);
17162306a36Sopenharmony_ci#else
17262306a36Sopenharmony_ci	writew(value, bs->regs + bs->reg_offsets[offset]);
17362306a36Sopenharmony_ci#endif
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic const unsigned int bcm63xx_spi_freq_table[SPI_CLK_MASK][2] = {
17762306a36Sopenharmony_ci	{ 20000000, SPI_CLK_20MHZ },
17862306a36Sopenharmony_ci	{ 12500000, SPI_CLK_12_50MHZ },
17962306a36Sopenharmony_ci	{  6250000, SPI_CLK_6_250MHZ },
18062306a36Sopenharmony_ci	{  3125000, SPI_CLK_3_125MHZ },
18162306a36Sopenharmony_ci	{  1563000, SPI_CLK_1_563MHZ },
18262306a36Sopenharmony_ci	{   781000, SPI_CLK_0_781MHZ },
18362306a36Sopenharmony_ci	{   391000, SPI_CLK_0_391MHZ }
18462306a36Sopenharmony_ci};
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic void bcm63xx_spi_setup_transfer(struct spi_device *spi,
18762306a36Sopenharmony_ci				      struct spi_transfer *t)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	struct bcm63xx_spi *bs = spi_controller_get_devdata(spi->controller);
19062306a36Sopenharmony_ci	u8 clk_cfg, reg;
19162306a36Sopenharmony_ci	int i;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	/* Default to lowest clock configuration */
19462306a36Sopenharmony_ci	clk_cfg = SPI_CLK_0_391MHZ;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	/* Find the closest clock configuration */
19762306a36Sopenharmony_ci	for (i = 0; i < SPI_CLK_MASK; i++) {
19862306a36Sopenharmony_ci		if (t->speed_hz >= bcm63xx_spi_freq_table[i][0]) {
19962306a36Sopenharmony_ci			clk_cfg = bcm63xx_spi_freq_table[i][1];
20062306a36Sopenharmony_ci			break;
20162306a36Sopenharmony_ci		}
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	/* clear existing clock configuration bits of the register */
20562306a36Sopenharmony_ci	reg = bcm_spi_readb(bs, SPI_CLK_CFG);
20662306a36Sopenharmony_ci	reg &= ~SPI_CLK_MASK;
20762306a36Sopenharmony_ci	reg |= clk_cfg;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	bcm_spi_writeb(bs, reg, SPI_CLK_CFG);
21062306a36Sopenharmony_ci	dev_dbg(&spi->dev, "Setting clock register to %02x (hz %d)\n",
21162306a36Sopenharmony_ci		clk_cfg, t->speed_hz);
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci/* the spi->mode bits understood by this driver: */
21562306a36Sopenharmony_ci#define MODEBITS (SPI_CPOL | SPI_CPHA)
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *first,
21862306a36Sopenharmony_ci				unsigned int num_transfers)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	struct bcm63xx_spi *bs = spi_controller_get_devdata(spi->controller);
22162306a36Sopenharmony_ci	u16 msg_ctl;
22262306a36Sopenharmony_ci	u16 cmd;
22362306a36Sopenharmony_ci	unsigned int i, timeout = 0, prepend_len = 0, len = 0;
22462306a36Sopenharmony_ci	struct spi_transfer *t = first;
22562306a36Sopenharmony_ci	bool do_rx = false;
22662306a36Sopenharmony_ci	bool do_tx = false;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	/* Disable the CMD_DONE interrupt */
22962306a36Sopenharmony_ci	bcm_spi_writeb(bs, 0, SPI_INT_MASK);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n",
23262306a36Sopenharmony_ci		t->tx_buf, t->rx_buf, t->len);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	if (num_transfers > 1 && t->tx_buf && t->len <= BCM63XX_SPI_MAX_PREPEND)
23562306a36Sopenharmony_ci		prepend_len = t->len;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	/* prepare the buffer */
23862306a36Sopenharmony_ci	for (i = 0; i < num_transfers; i++) {
23962306a36Sopenharmony_ci		if (t->tx_buf) {
24062306a36Sopenharmony_ci			do_tx = true;
24162306a36Sopenharmony_ci			memcpy_toio(bs->tx_io + len, t->tx_buf, t->len);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci			/* don't prepend more than one tx */
24462306a36Sopenharmony_ci			if (t != first)
24562306a36Sopenharmony_ci				prepend_len = 0;
24662306a36Sopenharmony_ci		}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci		if (t->rx_buf) {
24962306a36Sopenharmony_ci			do_rx = true;
25062306a36Sopenharmony_ci			/* prepend is half-duplex write only */
25162306a36Sopenharmony_ci			if (t == first)
25262306a36Sopenharmony_ci				prepend_len = 0;
25362306a36Sopenharmony_ci		}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci		len += t->len;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci		t = list_entry(t->transfer_list.next, struct spi_transfer,
25862306a36Sopenharmony_ci			       transfer_list);
25962306a36Sopenharmony_ci	}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	reinit_completion(&bs->done);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	/* Fill in the Message control register */
26462306a36Sopenharmony_ci	msg_ctl = (len << SPI_BYTE_CNT_SHIFT);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	if (do_rx && do_tx && prepend_len == 0)
26762306a36Sopenharmony_ci		msg_ctl |= (SPI_FD_RW << bs->msg_type_shift);
26862306a36Sopenharmony_ci	else if (do_rx)
26962306a36Sopenharmony_ci		msg_ctl |= (SPI_HD_R << bs->msg_type_shift);
27062306a36Sopenharmony_ci	else if (do_tx)
27162306a36Sopenharmony_ci		msg_ctl |= (SPI_HD_W << bs->msg_type_shift);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	switch (bs->msg_ctl_width) {
27462306a36Sopenharmony_ci	case 8:
27562306a36Sopenharmony_ci		bcm_spi_writeb(bs, msg_ctl, SPI_MSG_CTL);
27662306a36Sopenharmony_ci		break;
27762306a36Sopenharmony_ci	case 16:
27862306a36Sopenharmony_ci		bcm_spi_writew(bs, msg_ctl, SPI_MSG_CTL);
27962306a36Sopenharmony_ci		break;
28062306a36Sopenharmony_ci	}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	/* Issue the transfer */
28362306a36Sopenharmony_ci	cmd = SPI_CMD_START_IMMEDIATE;
28462306a36Sopenharmony_ci	cmd |= (prepend_len << SPI_CMD_PREPEND_BYTE_CNT_SHIFT);
28562306a36Sopenharmony_ci	cmd |= (spi_get_chipselect(spi, 0) << SPI_CMD_DEVICE_ID_SHIFT);
28662306a36Sopenharmony_ci	bcm_spi_writew(bs, cmd, SPI_CMD);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	/* Enable the CMD_DONE interrupt */
28962306a36Sopenharmony_ci	bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK);
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	timeout = wait_for_completion_timeout(&bs->done, HZ);
29262306a36Sopenharmony_ci	if (!timeout)
29362306a36Sopenharmony_ci		return -ETIMEDOUT;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	if (!do_rx)
29662306a36Sopenharmony_ci		return 0;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	len = 0;
29962306a36Sopenharmony_ci	t = first;
30062306a36Sopenharmony_ci	/* Read out all the data */
30162306a36Sopenharmony_ci	for (i = 0; i < num_transfers; i++) {
30262306a36Sopenharmony_ci		if (t->rx_buf)
30362306a36Sopenharmony_ci			memcpy_fromio(t->rx_buf, bs->rx_io + len, t->len);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci		if (t != first || prepend_len == 0)
30662306a36Sopenharmony_ci			len += t->len;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci		t = list_entry(t->transfer_list.next, struct spi_transfer,
30962306a36Sopenharmony_ci			       transfer_list);
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	return 0;
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic int bcm63xx_spi_transfer_one(struct spi_controller *host,
31662306a36Sopenharmony_ci					struct spi_message *m)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	struct bcm63xx_spi *bs = spi_controller_get_devdata(host);
31962306a36Sopenharmony_ci	struct spi_transfer *t, *first = NULL;
32062306a36Sopenharmony_ci	struct spi_device *spi = m->spi;
32162306a36Sopenharmony_ci	int status = 0;
32262306a36Sopenharmony_ci	unsigned int n_transfers = 0, total_len = 0;
32362306a36Sopenharmony_ci	bool can_use_prepend = false;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	/*
32662306a36Sopenharmony_ci	 * This SPI controller does not support keeping CS active after a
32762306a36Sopenharmony_ci	 * transfer.
32862306a36Sopenharmony_ci	 * Work around this by merging as many transfers we can into one big
32962306a36Sopenharmony_ci	 * full-duplex transfers.
33062306a36Sopenharmony_ci	 */
33162306a36Sopenharmony_ci	list_for_each_entry(t, &m->transfers, transfer_list) {
33262306a36Sopenharmony_ci		if (!first)
33362306a36Sopenharmony_ci			first = t;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci		n_transfers++;
33662306a36Sopenharmony_ci		total_len += t->len;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci		if (n_transfers == 2 && !first->rx_buf && !t->tx_buf &&
33962306a36Sopenharmony_ci		    first->len <= BCM63XX_SPI_MAX_PREPEND)
34062306a36Sopenharmony_ci			can_use_prepend = true;
34162306a36Sopenharmony_ci		else if (can_use_prepend && t->tx_buf)
34262306a36Sopenharmony_ci			can_use_prepend = false;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci		/* we can only transfer one fifo worth of data */
34562306a36Sopenharmony_ci		if ((can_use_prepend &&
34662306a36Sopenharmony_ci		     total_len > (bs->fifo_size + BCM63XX_SPI_MAX_PREPEND)) ||
34762306a36Sopenharmony_ci		    (!can_use_prepend && total_len > bs->fifo_size)) {
34862306a36Sopenharmony_ci			dev_err(&spi->dev, "unable to do transfers larger than FIFO size (%i > %i)\n",
34962306a36Sopenharmony_ci				total_len, bs->fifo_size);
35062306a36Sopenharmony_ci			status = -EINVAL;
35162306a36Sopenharmony_ci			goto exit;
35262306a36Sopenharmony_ci		}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci		/* all combined transfers have to have the same speed */
35562306a36Sopenharmony_ci		if (t->speed_hz != first->speed_hz) {
35662306a36Sopenharmony_ci			dev_err(&spi->dev, "unable to change speed between transfers\n");
35762306a36Sopenharmony_ci			status = -EINVAL;
35862306a36Sopenharmony_ci			goto exit;
35962306a36Sopenharmony_ci		}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci		/* CS will be deasserted directly after transfer */
36262306a36Sopenharmony_ci		if (t->delay.value) {
36362306a36Sopenharmony_ci			dev_err(&spi->dev, "unable to keep CS asserted after transfer\n");
36462306a36Sopenharmony_ci			status = -EINVAL;
36562306a36Sopenharmony_ci			goto exit;
36662306a36Sopenharmony_ci		}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci		if (t->cs_change ||
36962306a36Sopenharmony_ci		    list_is_last(&t->transfer_list, &m->transfers)) {
37062306a36Sopenharmony_ci			/* configure adapter for a new transfer */
37162306a36Sopenharmony_ci			bcm63xx_spi_setup_transfer(spi, first);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci			/* send the data */
37462306a36Sopenharmony_ci			status = bcm63xx_txrx_bufs(spi, first, n_transfers);
37562306a36Sopenharmony_ci			if (status)
37662306a36Sopenharmony_ci				goto exit;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci			m->actual_length += total_len;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci			first = NULL;
38162306a36Sopenharmony_ci			n_transfers = 0;
38262306a36Sopenharmony_ci			total_len = 0;
38362306a36Sopenharmony_ci			can_use_prepend = false;
38462306a36Sopenharmony_ci		}
38562306a36Sopenharmony_ci	}
38662306a36Sopenharmony_ciexit:
38762306a36Sopenharmony_ci	m->status = status;
38862306a36Sopenharmony_ci	spi_finalize_current_message(host);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	return 0;
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci/* This driver supports single host mode only. Hence
39462306a36Sopenharmony_ci * CMD_DONE is the only interrupt we care about
39562306a36Sopenharmony_ci */
39662306a36Sopenharmony_cistatic irqreturn_t bcm63xx_spi_interrupt(int irq, void *dev_id)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	struct spi_controller *host = (struct spi_controller *)dev_id;
39962306a36Sopenharmony_ci	struct bcm63xx_spi *bs = spi_controller_get_devdata(host);
40062306a36Sopenharmony_ci	u8 intr;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	/* Read interupts and clear them immediately */
40362306a36Sopenharmony_ci	intr = bcm_spi_readb(bs, SPI_INT_STATUS);
40462306a36Sopenharmony_ci	bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS);
40562306a36Sopenharmony_ci	bcm_spi_writeb(bs, 0, SPI_INT_MASK);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	/* A transfer completed */
40862306a36Sopenharmony_ci	if (intr & SPI_INTR_CMD_DONE)
40962306a36Sopenharmony_ci		complete(&bs->done);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	return IRQ_HANDLED;
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_cistatic size_t bcm63xx_spi_max_length(struct spi_device *spi)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	struct bcm63xx_spi *bs = spi_controller_get_devdata(spi->controller);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	return bs->fifo_size;
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cistatic const unsigned long bcm6348_spi_reg_offsets[] = {
42262306a36Sopenharmony_ci	[SPI_CMD]		= SPI_6348_CMD,
42362306a36Sopenharmony_ci	[SPI_INT_STATUS]	= SPI_6348_INT_STATUS,
42462306a36Sopenharmony_ci	[SPI_INT_MASK_ST]	= SPI_6348_INT_MASK_ST,
42562306a36Sopenharmony_ci	[SPI_INT_MASK]		= SPI_6348_INT_MASK,
42662306a36Sopenharmony_ci	[SPI_ST]		= SPI_6348_ST,
42762306a36Sopenharmony_ci	[SPI_CLK_CFG]		= SPI_6348_CLK_CFG,
42862306a36Sopenharmony_ci	[SPI_FILL_BYTE]		= SPI_6348_FILL_BYTE,
42962306a36Sopenharmony_ci	[SPI_MSG_TAIL]		= SPI_6348_MSG_TAIL,
43062306a36Sopenharmony_ci	[SPI_RX_TAIL]		= SPI_6348_RX_TAIL,
43162306a36Sopenharmony_ci	[SPI_MSG_CTL]		= SPI_6348_MSG_CTL,
43262306a36Sopenharmony_ci	[SPI_MSG_DATA]		= SPI_6348_MSG_DATA,
43362306a36Sopenharmony_ci	[SPI_RX_DATA]		= SPI_6348_RX_DATA,
43462306a36Sopenharmony_ci	[SPI_MSG_TYPE_SHIFT]	= SPI_6348_MSG_TYPE_SHIFT,
43562306a36Sopenharmony_ci	[SPI_MSG_CTL_WIDTH]	= SPI_6348_MSG_CTL_WIDTH,
43662306a36Sopenharmony_ci	[SPI_MSG_DATA_SIZE]	= SPI_6348_MSG_DATA_SIZE,
43762306a36Sopenharmony_ci};
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic const unsigned long bcm6358_spi_reg_offsets[] = {
44062306a36Sopenharmony_ci	[SPI_CMD]		= SPI_6358_CMD,
44162306a36Sopenharmony_ci	[SPI_INT_STATUS]	= SPI_6358_INT_STATUS,
44262306a36Sopenharmony_ci	[SPI_INT_MASK_ST]	= SPI_6358_INT_MASK_ST,
44362306a36Sopenharmony_ci	[SPI_INT_MASK]		= SPI_6358_INT_MASK,
44462306a36Sopenharmony_ci	[SPI_ST]		= SPI_6358_ST,
44562306a36Sopenharmony_ci	[SPI_CLK_CFG]		= SPI_6358_CLK_CFG,
44662306a36Sopenharmony_ci	[SPI_FILL_BYTE]		= SPI_6358_FILL_BYTE,
44762306a36Sopenharmony_ci	[SPI_MSG_TAIL]		= SPI_6358_MSG_TAIL,
44862306a36Sopenharmony_ci	[SPI_RX_TAIL]		= SPI_6358_RX_TAIL,
44962306a36Sopenharmony_ci	[SPI_MSG_CTL]		= SPI_6358_MSG_CTL,
45062306a36Sopenharmony_ci	[SPI_MSG_DATA]		= SPI_6358_MSG_DATA,
45162306a36Sopenharmony_ci	[SPI_RX_DATA]		= SPI_6358_RX_DATA,
45262306a36Sopenharmony_ci	[SPI_MSG_TYPE_SHIFT]	= SPI_6358_MSG_TYPE_SHIFT,
45362306a36Sopenharmony_ci	[SPI_MSG_CTL_WIDTH]	= SPI_6358_MSG_CTL_WIDTH,
45462306a36Sopenharmony_ci	[SPI_MSG_DATA_SIZE]	= SPI_6358_MSG_DATA_SIZE,
45562306a36Sopenharmony_ci};
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_cistatic const struct platform_device_id bcm63xx_spi_dev_match[] = {
45862306a36Sopenharmony_ci	{
45962306a36Sopenharmony_ci		.name = "bcm6348-spi",
46062306a36Sopenharmony_ci		.driver_data = (unsigned long)bcm6348_spi_reg_offsets,
46162306a36Sopenharmony_ci	},
46262306a36Sopenharmony_ci	{
46362306a36Sopenharmony_ci		.name = "bcm6358-spi",
46462306a36Sopenharmony_ci		.driver_data = (unsigned long)bcm6358_spi_reg_offsets,
46562306a36Sopenharmony_ci	},
46662306a36Sopenharmony_ci	{
46762306a36Sopenharmony_ci	},
46862306a36Sopenharmony_ci};
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_cistatic const struct of_device_id bcm63xx_spi_of_match[] = {
47162306a36Sopenharmony_ci	{ .compatible = "brcm,bcm6348-spi", .data = &bcm6348_spi_reg_offsets },
47262306a36Sopenharmony_ci	{ .compatible = "brcm,bcm6358-spi", .data = &bcm6358_spi_reg_offsets },
47362306a36Sopenharmony_ci	{ },
47462306a36Sopenharmony_ci};
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_cistatic int bcm63xx_spi_probe(struct platform_device *pdev)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	struct resource *r;
47962306a36Sopenharmony_ci	const unsigned long *bcm63xx_spireg;
48062306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
48162306a36Sopenharmony_ci	int irq, bus_num;
48262306a36Sopenharmony_ci	struct spi_controller *host;
48362306a36Sopenharmony_ci	struct clk *clk;
48462306a36Sopenharmony_ci	struct bcm63xx_spi *bs;
48562306a36Sopenharmony_ci	int ret;
48662306a36Sopenharmony_ci	u32 num_cs = BCM63XX_SPI_MAX_CS;
48762306a36Sopenharmony_ci	struct reset_control *reset;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	if (dev->of_node) {
49062306a36Sopenharmony_ci		const struct of_device_id *match;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci		match = of_match_node(bcm63xx_spi_of_match, dev->of_node);
49362306a36Sopenharmony_ci		if (!match)
49462306a36Sopenharmony_ci			return -EINVAL;
49562306a36Sopenharmony_ci		bcm63xx_spireg = match->data;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci		of_property_read_u32(dev->of_node, "num-cs", &num_cs);
49862306a36Sopenharmony_ci		if (num_cs > BCM63XX_SPI_MAX_CS) {
49962306a36Sopenharmony_ci			dev_warn(dev, "unsupported number of cs (%i), reducing to 8\n",
50062306a36Sopenharmony_ci				 num_cs);
50162306a36Sopenharmony_ci			num_cs = BCM63XX_SPI_MAX_CS;
50262306a36Sopenharmony_ci		}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci		bus_num = -1;
50562306a36Sopenharmony_ci	} else if (pdev->id_entry->driver_data) {
50662306a36Sopenharmony_ci		const struct platform_device_id *match = pdev->id_entry;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci		bcm63xx_spireg = (const unsigned long *)match->driver_data;
50962306a36Sopenharmony_ci		bus_num = BCM63XX_SPI_BUS_NUM;
51062306a36Sopenharmony_ci	} else {
51162306a36Sopenharmony_ci		return -EINVAL;
51262306a36Sopenharmony_ci	}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
51562306a36Sopenharmony_ci	if (irq < 0)
51662306a36Sopenharmony_ci		return irq;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	clk = devm_clk_get(dev, "spi");
51962306a36Sopenharmony_ci	if (IS_ERR(clk)) {
52062306a36Sopenharmony_ci		dev_err(dev, "no clock for device\n");
52162306a36Sopenharmony_ci		return PTR_ERR(clk);
52262306a36Sopenharmony_ci	}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	reset = devm_reset_control_get_optional_exclusive(dev, NULL);
52562306a36Sopenharmony_ci	if (IS_ERR(reset))
52662306a36Sopenharmony_ci		return PTR_ERR(reset);
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	host = spi_alloc_host(dev, sizeof(*bs));
52962306a36Sopenharmony_ci	if (!host) {
53062306a36Sopenharmony_ci		dev_err(dev, "out of memory\n");
53162306a36Sopenharmony_ci		return -ENOMEM;
53262306a36Sopenharmony_ci	}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	bs = spi_controller_get_devdata(host);
53562306a36Sopenharmony_ci	init_completion(&bs->done);
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	platform_set_drvdata(pdev, host);
53862306a36Sopenharmony_ci	bs->pdev = pdev;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	bs->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &r);
54162306a36Sopenharmony_ci	if (IS_ERR(bs->regs)) {
54262306a36Sopenharmony_ci		ret = PTR_ERR(bs->regs);
54362306a36Sopenharmony_ci		goto out_err;
54462306a36Sopenharmony_ci	}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	bs->irq = irq;
54762306a36Sopenharmony_ci	bs->clk = clk;
54862306a36Sopenharmony_ci	bs->reg_offsets = bcm63xx_spireg;
54962306a36Sopenharmony_ci	bs->fifo_size = bs->reg_offsets[SPI_MSG_DATA_SIZE];
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	ret = devm_request_irq(&pdev->dev, irq, bcm63xx_spi_interrupt, 0,
55262306a36Sopenharmony_ci			       pdev->name, host);
55362306a36Sopenharmony_ci	if (ret) {
55462306a36Sopenharmony_ci		dev_err(dev, "unable to request irq\n");
55562306a36Sopenharmony_ci		goto out_err;
55662306a36Sopenharmony_ci	}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	host->dev.of_node = dev->of_node;
55962306a36Sopenharmony_ci	host->bus_num = bus_num;
56062306a36Sopenharmony_ci	host->num_chipselect = num_cs;
56162306a36Sopenharmony_ci	host->transfer_one_message = bcm63xx_spi_transfer_one;
56262306a36Sopenharmony_ci	host->mode_bits = MODEBITS;
56362306a36Sopenharmony_ci	host->bits_per_word_mask = SPI_BPW_MASK(8);
56462306a36Sopenharmony_ci	host->max_transfer_size = bcm63xx_spi_max_length;
56562306a36Sopenharmony_ci	host->max_message_size = bcm63xx_spi_max_length;
56662306a36Sopenharmony_ci	host->auto_runtime_pm = true;
56762306a36Sopenharmony_ci	bs->msg_type_shift = bs->reg_offsets[SPI_MSG_TYPE_SHIFT];
56862306a36Sopenharmony_ci	bs->msg_ctl_width = bs->reg_offsets[SPI_MSG_CTL_WIDTH];
56962306a36Sopenharmony_ci	bs->tx_io = (u8 *)(bs->regs + bs->reg_offsets[SPI_MSG_DATA]);
57062306a36Sopenharmony_ci	bs->rx_io = (const u8 *)(bs->regs + bs->reg_offsets[SPI_RX_DATA]);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	/* Initialize hardware */
57362306a36Sopenharmony_ci	ret = clk_prepare_enable(bs->clk);
57462306a36Sopenharmony_ci	if (ret)
57562306a36Sopenharmony_ci		goto out_err;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	ret = reset_control_reset(reset);
57862306a36Sopenharmony_ci	if (ret) {
57962306a36Sopenharmony_ci		dev_err(dev, "unable to reset device: %d\n", ret);
58062306a36Sopenharmony_ci		goto out_clk_disable;
58162306a36Sopenharmony_ci	}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS);
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	pm_runtime_enable(&pdev->dev);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	/* register and we are done */
58862306a36Sopenharmony_ci	ret = devm_spi_register_controller(dev, host);
58962306a36Sopenharmony_ci	if (ret) {
59062306a36Sopenharmony_ci		dev_err(dev, "spi register failed\n");
59162306a36Sopenharmony_ci		goto out_pm_disable;
59262306a36Sopenharmony_ci	}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	dev_info(dev, "at %pr (irq %d, FIFOs size %d)\n",
59562306a36Sopenharmony_ci		 r, irq, bs->fifo_size);
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	return 0;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ciout_pm_disable:
60062306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
60162306a36Sopenharmony_ciout_clk_disable:
60262306a36Sopenharmony_ci	clk_disable_unprepare(clk);
60362306a36Sopenharmony_ciout_err:
60462306a36Sopenharmony_ci	spi_controller_put(host);
60562306a36Sopenharmony_ci	return ret;
60662306a36Sopenharmony_ci}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_cistatic void bcm63xx_spi_remove(struct platform_device *pdev)
60962306a36Sopenharmony_ci{
61062306a36Sopenharmony_ci	struct spi_controller *host = platform_get_drvdata(pdev);
61162306a36Sopenharmony_ci	struct bcm63xx_spi *bs = spi_controller_get_devdata(host);
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	/* reset spi block */
61462306a36Sopenharmony_ci	bcm_spi_writeb(bs, 0, SPI_INT_MASK);
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	/* HW shutdown */
61762306a36Sopenharmony_ci	clk_disable_unprepare(bs->clk);
61862306a36Sopenharmony_ci}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_cistatic int bcm63xx_spi_suspend(struct device *dev)
62162306a36Sopenharmony_ci{
62262306a36Sopenharmony_ci	struct spi_controller *host = dev_get_drvdata(dev);
62362306a36Sopenharmony_ci	struct bcm63xx_spi *bs = spi_controller_get_devdata(host);
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	spi_controller_suspend(host);
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	clk_disable_unprepare(bs->clk);
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	return 0;
63062306a36Sopenharmony_ci}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_cistatic int bcm63xx_spi_resume(struct device *dev)
63362306a36Sopenharmony_ci{
63462306a36Sopenharmony_ci	struct spi_controller *host = dev_get_drvdata(dev);
63562306a36Sopenharmony_ci	struct bcm63xx_spi *bs = spi_controller_get_devdata(host);
63662306a36Sopenharmony_ci	int ret;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	ret = clk_prepare_enable(bs->clk);
63962306a36Sopenharmony_ci	if (ret)
64062306a36Sopenharmony_ci		return ret;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	spi_controller_resume(host);
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	return 0;
64562306a36Sopenharmony_ci}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(bcm63xx_spi_pm_ops, bcm63xx_spi_suspend, bcm63xx_spi_resume);
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_cistatic struct platform_driver bcm63xx_spi_driver = {
65062306a36Sopenharmony_ci	.driver = {
65162306a36Sopenharmony_ci		.name	= "bcm63xx-spi",
65262306a36Sopenharmony_ci		.pm	= &bcm63xx_spi_pm_ops,
65362306a36Sopenharmony_ci		.of_match_table = bcm63xx_spi_of_match,
65462306a36Sopenharmony_ci	},
65562306a36Sopenharmony_ci	.id_table	= bcm63xx_spi_dev_match,
65662306a36Sopenharmony_ci	.probe		= bcm63xx_spi_probe,
65762306a36Sopenharmony_ci	.remove_new	= bcm63xx_spi_remove,
65862306a36Sopenharmony_ci};
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_cimodule_platform_driver(bcm63xx_spi_driver);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ciMODULE_ALIAS("platform:bcm63xx_spi");
66362306a36Sopenharmony_ciMODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
66462306a36Sopenharmony_ciMODULE_AUTHOR("Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>");
66562306a36Sopenharmony_ciMODULE_DESCRIPTION("Broadcom BCM63xx SPI Controller driver");
66662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
667