xref: /linux/drivers/spi/spi-bcm63xx.c (revision e03ad65cea610b24c6991aebf432d5c6824cd002)
1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2b42dfed8SFlorian Fainelli /*
3b42dfed8SFlorian Fainelli  * Broadcom BCM63xx SPI controller support
4b42dfed8SFlorian Fainelli  *
5cde4384eSFlorian Fainelli  * Copyright (C) 2009-2012 Florian Fainelli <florian@openwrt.org>
6b42dfed8SFlorian Fainelli  * Copyright (C) 2010 Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>
7b42dfed8SFlorian Fainelli  */
8b42dfed8SFlorian Fainelli 
9b42dfed8SFlorian Fainelli #include <linux/kernel.h>
10b42dfed8SFlorian Fainelli #include <linux/clk.h>
11b42dfed8SFlorian Fainelli #include <linux/io.h>
12b42dfed8SFlorian Fainelli #include <linux/module.h>
13b42dfed8SFlorian Fainelli #include <linux/platform_device.h>
14b42dfed8SFlorian Fainelli #include <linux/delay.h>
15b42dfed8SFlorian Fainelli #include <linux/interrupt.h>
16b42dfed8SFlorian Fainelli #include <linux/spi/spi.h>
17b42dfed8SFlorian Fainelli #include <linux/completion.h>
18b42dfed8SFlorian Fainelli #include <linux/err.h>
19cde4384eSFlorian Fainelli #include <linux/pm_runtime.h>
20c29f0889SJonas Gorski #include <linux/of.h>
2138807adeSÁlvaro Fernández Rojas #include <linux/reset.h>
22b42dfed8SFlorian Fainelli 
2344d8fb30SJonas Gorski /* BCM 6338/6348 SPI core */
2444d8fb30SJonas Gorski #define SPI_6348_RSET_SIZE		64
2544d8fb30SJonas Gorski #define SPI_6348_CMD			0x00	/* 16-bits register */
2644d8fb30SJonas Gorski #define SPI_6348_INT_STATUS		0x02
2744d8fb30SJonas Gorski #define SPI_6348_INT_MASK_ST		0x03
2844d8fb30SJonas Gorski #define SPI_6348_INT_MASK		0x04
2944d8fb30SJonas Gorski #define SPI_6348_ST			0x05
3044d8fb30SJonas Gorski #define SPI_6348_CLK_CFG		0x06
3144d8fb30SJonas Gorski #define SPI_6348_FILL_BYTE		0x07
3244d8fb30SJonas Gorski #define SPI_6348_MSG_TAIL		0x09
3344d8fb30SJonas Gorski #define SPI_6348_RX_TAIL		0x0b
3444d8fb30SJonas Gorski #define SPI_6348_MSG_CTL		0x40	/* 8-bits register */
3544d8fb30SJonas Gorski #define SPI_6348_MSG_CTL_WIDTH		8
3644d8fb30SJonas Gorski #define SPI_6348_MSG_DATA		0x41
3744d8fb30SJonas Gorski #define SPI_6348_MSG_DATA_SIZE		0x3f
3844d8fb30SJonas Gorski #define SPI_6348_RX_DATA		0x80
3944d8fb30SJonas Gorski #define SPI_6348_RX_DATA_SIZE		0x3f
4044d8fb30SJonas Gorski 
4144d8fb30SJonas Gorski /* BCM 3368/6358/6262/6368 SPI core */
4244d8fb30SJonas Gorski #define SPI_6358_RSET_SIZE		1804
4344d8fb30SJonas Gorski #define SPI_6358_MSG_CTL		0x00	/* 16-bits register */
4444d8fb30SJonas Gorski #define SPI_6358_MSG_CTL_WIDTH		16
4544d8fb30SJonas Gorski #define SPI_6358_MSG_DATA		0x02
4644d8fb30SJonas Gorski #define SPI_6358_MSG_DATA_SIZE		0x21e
4744d8fb30SJonas Gorski #define SPI_6358_RX_DATA		0x400
4844d8fb30SJonas Gorski #define SPI_6358_RX_DATA_SIZE		0x220
4944d8fb30SJonas Gorski #define SPI_6358_CMD			0x700	/* 16-bits register */
5044d8fb30SJonas Gorski #define SPI_6358_INT_STATUS		0x702
5144d8fb30SJonas Gorski #define SPI_6358_INT_MASK_ST		0x703
5244d8fb30SJonas Gorski #define SPI_6358_INT_MASK		0x704
5344d8fb30SJonas Gorski #define SPI_6358_ST			0x705
5444d8fb30SJonas Gorski #define SPI_6358_CLK_CFG		0x706
5544d8fb30SJonas Gorski #define SPI_6358_FILL_BYTE		0x707
5644d8fb30SJonas Gorski #define SPI_6358_MSG_TAIL		0x709
5744d8fb30SJonas Gorski #define SPI_6358_RX_TAIL		0x70B
5844d8fb30SJonas Gorski 
5944d8fb30SJonas Gorski /* Shared SPI definitions */
6044d8fb30SJonas Gorski 
6144d8fb30SJonas Gorski /* Message configuration */
6244d8fb30SJonas Gorski #define SPI_FD_RW			0x00
6344d8fb30SJonas Gorski #define SPI_HD_W			0x01
6444d8fb30SJonas Gorski #define SPI_HD_R			0x02
6544d8fb30SJonas Gorski #define SPI_BYTE_CNT_SHIFT		0
6644d8fb30SJonas Gorski #define SPI_6348_MSG_TYPE_SHIFT		6
6744d8fb30SJonas Gorski #define SPI_6358_MSG_TYPE_SHIFT		14
6844d8fb30SJonas Gorski 
6944d8fb30SJonas Gorski /* Command */
7044d8fb30SJonas Gorski #define SPI_CMD_NOOP			0x00
7144d8fb30SJonas Gorski #define SPI_CMD_SOFT_RESET		0x01
7244d8fb30SJonas Gorski #define SPI_CMD_HARD_RESET		0x02
7344d8fb30SJonas Gorski #define SPI_CMD_START_IMMEDIATE		0x03
7444d8fb30SJonas Gorski #define SPI_CMD_COMMAND_SHIFT		0
7544d8fb30SJonas Gorski #define SPI_CMD_COMMAND_MASK		0x000f
7644d8fb30SJonas Gorski #define SPI_CMD_DEVICE_ID_SHIFT		4
7744d8fb30SJonas Gorski #define SPI_CMD_PREPEND_BYTE_CNT_SHIFT	8
7844d8fb30SJonas Gorski #define SPI_CMD_ONE_BYTE_SHIFT		11
7944d8fb30SJonas Gorski #define SPI_CMD_ONE_WIRE_SHIFT		12
8044d8fb30SJonas Gorski #define SPI_DEV_ID_0			0
8144d8fb30SJonas Gorski #define SPI_DEV_ID_1			1
8244d8fb30SJonas Gorski #define SPI_DEV_ID_2			2
8344d8fb30SJonas Gorski #define SPI_DEV_ID_3			3
8444d8fb30SJonas Gorski 
8544d8fb30SJonas Gorski /* Interrupt mask */
8644d8fb30SJonas Gorski #define SPI_INTR_CMD_DONE		0x01
8744d8fb30SJonas Gorski #define SPI_INTR_RX_OVERFLOW		0x02
8844d8fb30SJonas Gorski #define SPI_INTR_TX_UNDERFLOW		0x04
8944d8fb30SJonas Gorski #define SPI_INTR_TX_OVERFLOW		0x08
9044d8fb30SJonas Gorski #define SPI_INTR_RX_UNDERFLOW		0x10
9144d8fb30SJonas Gorski #define SPI_INTR_CLEAR_ALL		0x1f
9244d8fb30SJonas Gorski 
9344d8fb30SJonas Gorski /* Status */
9444d8fb30SJonas Gorski #define SPI_RX_EMPTY			0x02
9544d8fb30SJonas Gorski #define SPI_CMD_BUSY			0x04
9644d8fb30SJonas Gorski #define SPI_SERIAL_BUSY			0x08
9744d8fb30SJonas Gorski 
9844d8fb30SJonas Gorski /* Clock configuration */
9944d8fb30SJonas Gorski #define SPI_CLK_20MHZ			0x00
10044d8fb30SJonas Gorski #define SPI_CLK_0_391MHZ		0x01
10144d8fb30SJonas Gorski #define SPI_CLK_0_781MHZ		0x02	/* default */
10244d8fb30SJonas Gorski #define SPI_CLK_1_563MHZ		0x03
10344d8fb30SJonas Gorski #define SPI_CLK_3_125MHZ		0x04
10444d8fb30SJonas Gorski #define SPI_CLK_6_250MHZ		0x05
10544d8fb30SJonas Gorski #define SPI_CLK_12_50MHZ		0x06
10644d8fb30SJonas Gorski #define SPI_CLK_MASK			0x07
10744d8fb30SJonas Gorski #define SPI_SSOFFTIME_MASK		0x38
10844d8fb30SJonas Gorski #define SPI_SSOFFTIME_SHIFT		3
10944d8fb30SJonas Gorski #define SPI_BYTE_SWAP			0x80
11044d8fb30SJonas Gorski 
11144d8fb30SJonas Gorski enum bcm63xx_regs_spi {
11244d8fb30SJonas Gorski 	SPI_CMD,
11344d8fb30SJonas Gorski 	SPI_INT_STATUS,
11444d8fb30SJonas Gorski 	SPI_INT_MASK_ST,
11544d8fb30SJonas Gorski 	SPI_INT_MASK,
11644d8fb30SJonas Gorski 	SPI_ST,
11744d8fb30SJonas Gorski 	SPI_CLK_CFG,
11844d8fb30SJonas Gorski 	SPI_FILL_BYTE,
11944d8fb30SJonas Gorski 	SPI_MSG_TAIL,
12044d8fb30SJonas Gorski 	SPI_RX_TAIL,
12144d8fb30SJonas Gorski 	SPI_MSG_CTL,
12244d8fb30SJonas Gorski 	SPI_MSG_DATA,
12344d8fb30SJonas Gorski 	SPI_RX_DATA,
12444d8fb30SJonas Gorski 	SPI_MSG_TYPE_SHIFT,
12544d8fb30SJonas Gorski 	SPI_MSG_CTL_WIDTH,
12644d8fb30SJonas Gorski 	SPI_MSG_DATA_SIZE,
12744d8fb30SJonas Gorski };
128b42dfed8SFlorian Fainelli 
1295158814cSJonas Gorski #define BCM63XX_SPI_MAX_PREPEND		7
130b17de076SJonas Gorski 
13165059997SJonas Gorski #define BCM63XX_SPI_MAX_CS		8
132a45fcea5SJonas Gorski #define BCM63XX_SPI_BUS_NUM		0
13365059997SJonas Gorski 
134b42dfed8SFlorian Fainelli struct bcm63xx_spi {
135b42dfed8SFlorian Fainelli 	struct completion	done;
136b42dfed8SFlorian Fainelli 
137b42dfed8SFlorian Fainelli 	void __iomem		*regs;
138b42dfed8SFlorian Fainelli 	int			irq;
139b42dfed8SFlorian Fainelli 
140b42dfed8SFlorian Fainelli 	/* Platform data */
14144d8fb30SJonas Gorski 	const unsigned long	*reg_offsets;
142b85d65ddSAravind Thokala 	unsigned int		fifo_size;
1435a670445SFlorian Fainelli 	unsigned int		msg_type_shift;
1445a670445SFlorian Fainelli 	unsigned int		msg_ctl_width;
145b42dfed8SFlorian Fainelli 
146b42dfed8SFlorian Fainelli 	/* data iomem */
147b42dfed8SFlorian Fainelli 	u8 __iomem		*tx_io;
148b42dfed8SFlorian Fainelli 	const u8 __iomem	*rx_io;
149b42dfed8SFlorian Fainelli 
150b42dfed8SFlorian Fainelli 	struct clk		*clk;
151b42dfed8SFlorian Fainelli 	struct platform_device	*pdev;
152b42dfed8SFlorian Fainelli };
153b42dfed8SFlorian Fainelli 
154b42dfed8SFlorian Fainelli static inline u8 bcm_spi_readb(struct bcm63xx_spi *bs,
155b42dfed8SFlorian Fainelli 			       unsigned int offset)
156b42dfed8SFlorian Fainelli {
15744d8fb30SJonas Gorski 	return readb(bs->regs + bs->reg_offsets[offset]);
158b42dfed8SFlorian Fainelli }
159b42dfed8SFlorian Fainelli 
160b42dfed8SFlorian Fainelli static inline void bcm_spi_writeb(struct bcm63xx_spi *bs,
161b42dfed8SFlorian Fainelli 				  u8 value, unsigned int offset)
162b42dfed8SFlorian Fainelli {
16344d8fb30SJonas Gorski 	writeb(value, bs->regs + bs->reg_offsets[offset]);
164b42dfed8SFlorian Fainelli }
165b42dfed8SFlorian Fainelli 
166b42dfed8SFlorian Fainelli static inline void bcm_spi_writew(struct bcm63xx_spi *bs,
167b42dfed8SFlorian Fainelli 				  u16 value, unsigned int offset)
168b42dfed8SFlorian Fainelli {
169682b5280SJonas Gorski #ifdef CONFIG_CPU_BIG_ENDIAN
17044d8fb30SJonas Gorski 	iowrite16be(value, bs->regs + bs->reg_offsets[offset]);
171158fcc4eSJonas Gorski #else
17244d8fb30SJonas Gorski 	writew(value, bs->regs + bs->reg_offsets[offset]);
173158fcc4eSJonas Gorski #endif
174b42dfed8SFlorian Fainelli }
175b42dfed8SFlorian Fainelli 
176b85d65ddSAravind Thokala static const unsigned int bcm63xx_spi_freq_table[SPI_CLK_MASK][2] = {
177b42dfed8SFlorian Fainelli 	{ 20000000, SPI_CLK_20MHZ },
178b42dfed8SFlorian Fainelli 	{ 12500000, SPI_CLK_12_50MHZ },
179b42dfed8SFlorian Fainelli 	{  6250000, SPI_CLK_6_250MHZ },
180b42dfed8SFlorian Fainelli 	{  3125000, SPI_CLK_3_125MHZ },
181b42dfed8SFlorian Fainelli 	{  1563000, SPI_CLK_1_563MHZ },
182b42dfed8SFlorian Fainelli 	{   781000, SPI_CLK_0_781MHZ },
183b42dfed8SFlorian Fainelli 	{   391000, SPI_CLK_0_391MHZ }
184b42dfed8SFlorian Fainelli };
185b42dfed8SFlorian Fainelli 
186cde4384eSFlorian Fainelli static void bcm63xx_spi_setup_transfer(struct spi_device *spi,
187cde4384eSFlorian Fainelli 				      struct spi_transfer *t)
188cde4384eSFlorian Fainelli {
1891a9e7619SYang Yingliang 	struct bcm63xx_spi *bs = spi_controller_get_devdata(spi->controller);
190cde4384eSFlorian Fainelli 	u8 clk_cfg, reg;
191cde4384eSFlorian Fainelli 	int i;
192cde4384eSFlorian Fainelli 
1934dde99bdSGeert Uytterhoeven 	/* Default to lowest clock configuration */
1944dde99bdSGeert Uytterhoeven 	clk_cfg = SPI_CLK_0_391MHZ;
1954dde99bdSGeert Uytterhoeven 
196b42dfed8SFlorian Fainelli 	/* Find the closest clock configuration */
197b42dfed8SFlorian Fainelli 	for (i = 0; i < SPI_CLK_MASK; i++) {
19868792e2aSJonas Gorski 		if (t->speed_hz >= bcm63xx_spi_freq_table[i][0]) {
199b42dfed8SFlorian Fainelli 			clk_cfg = bcm63xx_spi_freq_table[i][1];
200b42dfed8SFlorian Fainelli 			break;
201b42dfed8SFlorian Fainelli 		}
202b42dfed8SFlorian Fainelli 	}
203b42dfed8SFlorian Fainelli 
204b42dfed8SFlorian Fainelli 	/* clear existing clock configuration bits of the register */
205b42dfed8SFlorian Fainelli 	reg = bcm_spi_readb(bs, SPI_CLK_CFG);
206b42dfed8SFlorian Fainelli 	reg &= ~SPI_CLK_MASK;
207b42dfed8SFlorian Fainelli 	reg |= clk_cfg;
208b42dfed8SFlorian Fainelli 
209b42dfed8SFlorian Fainelli 	bcm_spi_writeb(bs, reg, SPI_CLK_CFG);
210b42dfed8SFlorian Fainelli 	dev_dbg(&spi->dev, "Setting clock register to %02x (hz %d)\n",
21168792e2aSJonas Gorski 		clk_cfg, t->speed_hz);
212b42dfed8SFlorian Fainelli }
213b42dfed8SFlorian Fainelli 
214b42dfed8SFlorian Fainelli /* the spi->mode bits understood by this driver: */
215b42dfed8SFlorian Fainelli #define MODEBITS (SPI_CPOL | SPI_CPHA)
216b42dfed8SFlorian Fainelli 
217b17de076SJonas Gorski static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *first,
218b17de076SJonas Gorski 				unsigned int num_transfers)
219b42dfed8SFlorian Fainelli {
2201a9e7619SYang Yingliang 	struct bcm63xx_spi *bs = spi_controller_get_devdata(spi->controller);
221b42dfed8SFlorian Fainelli 	u16 msg_ctl;
222b42dfed8SFlorian Fainelli 	u16 cmd;
223b17de076SJonas Gorski 	unsigned int i, timeout = 0, prepend_len = 0, len = 0;
224b17de076SJonas Gorski 	struct spi_transfer *t = first;
225b17de076SJonas Gorski 	bool do_rx = false;
226b17de076SJonas Gorski 	bool do_tx = false;
227b42dfed8SFlorian Fainelli 
228cde4384eSFlorian Fainelli 	/* Disable the CMD_DONE interrupt */
229cde4384eSFlorian Fainelli 	bcm_spi_writeb(bs, 0, SPI_INT_MASK);
230cde4384eSFlorian Fainelli 
231b42dfed8SFlorian Fainelli 	dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n",
232b42dfed8SFlorian Fainelli 		t->tx_buf, t->rx_buf, t->len);
233b42dfed8SFlorian Fainelli 
234b17de076SJonas Gorski 	if (num_transfers > 1 && t->tx_buf && t->len <= BCM63XX_SPI_MAX_PREPEND)
235b17de076SJonas Gorski 		prepend_len = t->len;
236b17de076SJonas Gorski 
237b17de076SJonas Gorski 	/* prepare the buffer */
238b17de076SJonas Gorski 	for (i = 0; i < num_transfers; i++) {
239b17de076SJonas Gorski 		if (t->tx_buf) {
240b17de076SJonas Gorski 			do_tx = true;
241b17de076SJonas Gorski 			memcpy_toio(bs->tx_io + len, t->tx_buf, t->len);
242b17de076SJonas Gorski 
243b17de076SJonas Gorski 			/* don't prepend more than one tx */
244b17de076SJonas Gorski 			if (t != first)
245b17de076SJonas Gorski 				prepend_len = 0;
246b17de076SJonas Gorski 		}
247b17de076SJonas Gorski 
248b17de076SJonas Gorski 		if (t->rx_buf) {
249b17de076SJonas Gorski 			do_rx = true;
250b17de076SJonas Gorski 			/* prepend is half-duplex write only */
251b17de076SJonas Gorski 			if (t == first)
252b17de076SJonas Gorski 				prepend_len = 0;
253b17de076SJonas Gorski 		}
254b17de076SJonas Gorski 
255b17de076SJonas Gorski 		len += t->len;
256b17de076SJonas Gorski 
257b17de076SJonas Gorski 		t = list_entry(t->transfer_list.next, struct spi_transfer,
258b17de076SJonas Gorski 			       transfer_list);
259b17de076SJonas Gorski 	}
260b17de076SJonas Gorski 
261aa0fe826SAxel Lin 	reinit_completion(&bs->done);
262b42dfed8SFlorian Fainelli 
263b42dfed8SFlorian Fainelli 	/* Fill in the Message control register */
264b17de076SJonas Gorski 	msg_ctl = (len << SPI_BYTE_CNT_SHIFT);
265b42dfed8SFlorian Fainelli 
266b17de076SJonas Gorski 	if (do_rx && do_tx && prepend_len == 0)
2675a670445SFlorian Fainelli 		msg_ctl |= (SPI_FD_RW << bs->msg_type_shift);
268b17de076SJonas Gorski 	else if (do_rx)
2695a670445SFlorian Fainelli 		msg_ctl |= (SPI_HD_R << bs->msg_type_shift);
270b17de076SJonas Gorski 	else if (do_tx)
2715a670445SFlorian Fainelli 		msg_ctl |= (SPI_HD_W << bs->msg_type_shift);
272b42dfed8SFlorian Fainelli 
2735a670445SFlorian Fainelli 	switch (bs->msg_ctl_width) {
2745a670445SFlorian Fainelli 	case 8:
2755a670445SFlorian Fainelli 		bcm_spi_writeb(bs, msg_ctl, SPI_MSG_CTL);
2765a670445SFlorian Fainelli 		break;
2775a670445SFlorian Fainelli 	case 16:
278b42dfed8SFlorian Fainelli 		bcm_spi_writew(bs, msg_ctl, SPI_MSG_CTL);
2795a670445SFlorian Fainelli 		break;
2805a670445SFlorian Fainelli 	}
281b42dfed8SFlorian Fainelli 
282b42dfed8SFlorian Fainelli 	/* Issue the transfer */
283b42dfed8SFlorian Fainelli 	cmd = SPI_CMD_START_IMMEDIATE;
284b17de076SJonas Gorski 	cmd |= (prepend_len << SPI_CMD_PREPEND_BYTE_CNT_SHIFT);
2859e264f3fSAmit Kumar Mahapatra via Alsa-devel 	cmd |= (spi_get_chipselect(spi, 0) << SPI_CMD_DEVICE_ID_SHIFT);
286b42dfed8SFlorian Fainelli 	bcm_spi_writew(bs, cmd, SPI_CMD);
287b42dfed8SFlorian Fainelli 
288cde4384eSFlorian Fainelli 	/* Enable the CMD_DONE interrupt */
289cde4384eSFlorian Fainelli 	bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK);
290b42dfed8SFlorian Fainelli 
291c0fde3baSJonas Gorski 	timeout = wait_for_completion_timeout(&bs->done, HZ);
292c0fde3baSJonas Gorski 	if (!timeout)
293c0fde3baSJonas Gorski 		return -ETIMEDOUT;
294c0fde3baSJonas Gorski 
29520e9e78fSJonas Gorski 	if (!do_rx)
296b17de076SJonas Gorski 		return 0;
297b17de076SJonas Gorski 
298b17de076SJonas Gorski 	len = 0;
299b17de076SJonas Gorski 	t = first;
300c0fde3baSJonas Gorski 	/* Read out all the data */
301b17de076SJonas Gorski 	for (i = 0; i < num_transfers; i++) {
302b17de076SJonas Gorski 		if (t->rx_buf)
303b17de076SJonas Gorski 			memcpy_fromio(t->rx_buf, bs->rx_io + len, t->len);
304b17de076SJonas Gorski 
305b17de076SJonas Gorski 		if (t != first || prepend_len == 0)
306b17de076SJonas Gorski 			len += t->len;
307b17de076SJonas Gorski 
308b17de076SJonas Gorski 		t = list_entry(t->transfer_list.next, struct spi_transfer,
309b17de076SJonas Gorski 			       transfer_list);
310b17de076SJonas Gorski 	}
311c0fde3baSJonas Gorski 
312c0fde3baSJonas Gorski 	return 0;
313b42dfed8SFlorian Fainelli }
314b42dfed8SFlorian Fainelli 
3151a9e7619SYang Yingliang static int bcm63xx_spi_transfer_one(struct spi_controller *host,
316cde4384eSFlorian Fainelli 					struct spi_message *m)
317cde4384eSFlorian Fainelli {
3181a9e7619SYang Yingliang 	struct bcm63xx_spi *bs = spi_controller_get_devdata(host);
319b17de076SJonas Gorski 	struct spi_transfer *t, *first = NULL;
320cde4384eSFlorian Fainelli 	struct spi_device *spi = m->spi;
321cde4384eSFlorian Fainelli 	int status = 0;
322b17de076SJonas Gorski 	unsigned int n_transfers = 0, total_len = 0;
323b17de076SJonas Gorski 	bool can_use_prepend = false;
324cde4384eSFlorian Fainelli 
325b17de076SJonas Gorski 	/*
326b17de076SJonas Gorski 	 * This SPI controller does not support keeping CS active after a
327b17de076SJonas Gorski 	 * transfer.
328b17de076SJonas Gorski 	 * Work around this by merging as many transfers we can into one big
329b17de076SJonas Gorski 	 * full-duplex transfers.
330b17de076SJonas Gorski 	 */
331cde4384eSFlorian Fainelli 	list_for_each_entry(t, &m->transfers, transfer_list) {
332b17de076SJonas Gorski 		if (!first)
333b17de076SJonas Gorski 			first = t;
334b17de076SJonas Gorski 
335b17de076SJonas Gorski 		n_transfers++;
336b17de076SJonas Gorski 		total_len += t->len;
337b17de076SJonas Gorski 
338b17de076SJonas Gorski 		if (n_transfers == 2 && !first->rx_buf && !t->tx_buf &&
339b17de076SJonas Gorski 		    first->len <= BCM63XX_SPI_MAX_PREPEND)
340b17de076SJonas Gorski 			can_use_prepend = true;
341b17de076SJonas Gorski 		else if (can_use_prepend && t->tx_buf)
342b17de076SJonas Gorski 			can_use_prepend = false;
343b17de076SJonas Gorski 
344c0fde3baSJonas Gorski 		/* we can only transfer one fifo worth of data */
345b17de076SJonas Gorski 		if ((can_use_prepend &&
346b17de076SJonas Gorski 		     total_len > (bs->fifo_size + BCM63XX_SPI_MAX_PREPEND)) ||
347b17de076SJonas Gorski 		    (!can_use_prepend && total_len > bs->fifo_size)) {
348c0fde3baSJonas Gorski 			dev_err(&spi->dev, "unable to do transfers larger than FIFO size (%i > %i)\n",
349b17de076SJonas Gorski 				total_len, bs->fifo_size);
350b17de076SJonas Gorski 			status = -EINVAL;
351b17de076SJonas Gorski 			goto exit;
352b17de076SJonas Gorski 		}
353b17de076SJonas Gorski 
354b17de076SJonas Gorski 		/* all combined transfers have to have the same speed */
355b17de076SJonas Gorski 		if (t->speed_hz != first->speed_hz) {
356b17de076SJonas Gorski 			dev_err(&spi->dev, "unable to change speed between transfers\n");
357c0fde3baSJonas Gorski 			status = -EINVAL;
358cde4384eSFlorian Fainelli 			goto exit;
359cde4384eSFlorian Fainelli 		}
360cde4384eSFlorian Fainelli 
361c0fde3baSJonas Gorski 		/* CS will be deasserted directly after transfer */
362e7f2d4c6SAlexandru Ardelean 		if (t->delay.value) {
363c0fde3baSJonas Gorski 			dev_err(&spi->dev, "unable to keep CS asserted after transfer\n");
364c0fde3baSJonas Gorski 			status = -EINVAL;
365c0fde3baSJonas Gorski 			goto exit;
366cde4384eSFlorian Fainelli 		}
367cde4384eSFlorian Fainelli 
368b17de076SJonas Gorski 		if (t->cs_change ||
369b17de076SJonas Gorski 		    list_is_last(&t->transfer_list, &m->transfers)) {
370c0fde3baSJonas Gorski 			/* configure adapter for a new transfer */
371b17de076SJonas Gorski 			bcm63xx_spi_setup_transfer(spi, first);
372c0fde3baSJonas Gorski 
373c0fde3baSJonas Gorski 			/* send the data */
374b17de076SJonas Gorski 			status = bcm63xx_txrx_bufs(spi, first, n_transfers);
375c0fde3baSJonas Gorski 			if (status)
376c0fde3baSJonas Gorski 				goto exit;
377c0fde3baSJonas Gorski 
378b17de076SJonas Gorski 			m->actual_length += total_len;
379b17de076SJonas Gorski 
380b17de076SJonas Gorski 			first = NULL;
381b17de076SJonas Gorski 			n_transfers = 0;
382b17de076SJonas Gorski 			total_len = 0;
383b17de076SJonas Gorski 			can_use_prepend = false;
384b17de076SJonas Gorski 		}
385cde4384eSFlorian Fainelli 	}
386cde4384eSFlorian Fainelli exit:
387cde4384eSFlorian Fainelli 	m->status = status;
3881a9e7619SYang Yingliang 	spi_finalize_current_message(host);
389cde4384eSFlorian Fainelli 
390cde4384eSFlorian Fainelli 	return 0;
391b42dfed8SFlorian Fainelli }
392b42dfed8SFlorian Fainelli 
3931a9e7619SYang Yingliang /* This driver supports single host mode only. Hence
394b42dfed8SFlorian Fainelli  * CMD_DONE is the only interrupt we care about
395b42dfed8SFlorian Fainelli  */
396b42dfed8SFlorian Fainelli static irqreturn_t bcm63xx_spi_interrupt(int irq, void *dev_id)
397b42dfed8SFlorian Fainelli {
3981a9e7619SYang Yingliang 	struct spi_controller *host = (struct spi_controller *)dev_id;
3991a9e7619SYang Yingliang 	struct bcm63xx_spi *bs = spi_controller_get_devdata(host);
400b42dfed8SFlorian Fainelli 	u8 intr;
401b42dfed8SFlorian Fainelli 
402b42dfed8SFlorian Fainelli 	/* Read interupts and clear them immediately */
403b42dfed8SFlorian Fainelli 	intr = bcm_spi_readb(bs, SPI_INT_STATUS);
404b42dfed8SFlorian Fainelli 	bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS);
405b42dfed8SFlorian Fainelli 	bcm_spi_writeb(bs, 0, SPI_INT_MASK);
406b42dfed8SFlorian Fainelli 
407cde4384eSFlorian Fainelli 	/* A transfer completed */
408cde4384eSFlorian Fainelli 	if (intr & SPI_INTR_CMD_DONE)
409b42dfed8SFlorian Fainelli 		complete(&bs->done);
410b42dfed8SFlorian Fainelli 
411b42dfed8SFlorian Fainelli 	return IRQ_HANDLED;
412b42dfed8SFlorian Fainelli }
413b42dfed8SFlorian Fainelli 
414ccd0657cSJonas Gorski static size_t bcm63xx_spi_max_length(struct spi_device *spi)
4150135c03dSJonas Gorski {
4161a9e7619SYang Yingliang 	struct bcm63xx_spi *bs = spi_controller_get_devdata(spi->controller);
4170135c03dSJonas Gorski 
4180135c03dSJonas Gorski 	return bs->fifo_size;
4190135c03dSJonas Gorski }
4200135c03dSJonas Gorski 
42144d8fb30SJonas Gorski static const unsigned long bcm6348_spi_reg_offsets[] = {
42244d8fb30SJonas Gorski 	[SPI_CMD]		= SPI_6348_CMD,
42344d8fb30SJonas Gorski 	[SPI_INT_STATUS]	= SPI_6348_INT_STATUS,
42444d8fb30SJonas Gorski 	[SPI_INT_MASK_ST]	= SPI_6348_INT_MASK_ST,
42544d8fb30SJonas Gorski 	[SPI_INT_MASK]		= SPI_6348_INT_MASK,
42644d8fb30SJonas Gorski 	[SPI_ST]		= SPI_6348_ST,
42744d8fb30SJonas Gorski 	[SPI_CLK_CFG]		= SPI_6348_CLK_CFG,
42844d8fb30SJonas Gorski 	[SPI_FILL_BYTE]		= SPI_6348_FILL_BYTE,
42944d8fb30SJonas Gorski 	[SPI_MSG_TAIL]		= SPI_6348_MSG_TAIL,
43044d8fb30SJonas Gorski 	[SPI_RX_TAIL]		= SPI_6348_RX_TAIL,
43144d8fb30SJonas Gorski 	[SPI_MSG_CTL]		= SPI_6348_MSG_CTL,
43244d8fb30SJonas Gorski 	[SPI_MSG_DATA]		= SPI_6348_MSG_DATA,
43344d8fb30SJonas Gorski 	[SPI_RX_DATA]		= SPI_6348_RX_DATA,
43444d8fb30SJonas Gorski 	[SPI_MSG_TYPE_SHIFT]	= SPI_6348_MSG_TYPE_SHIFT,
43544d8fb30SJonas Gorski 	[SPI_MSG_CTL_WIDTH]	= SPI_6348_MSG_CTL_WIDTH,
43644d8fb30SJonas Gorski 	[SPI_MSG_DATA_SIZE]	= SPI_6348_MSG_DATA_SIZE,
43744d8fb30SJonas Gorski };
43844d8fb30SJonas Gorski 
43944d8fb30SJonas Gorski static const unsigned long bcm6358_spi_reg_offsets[] = {
44044d8fb30SJonas Gorski 	[SPI_CMD]		= SPI_6358_CMD,
44144d8fb30SJonas Gorski 	[SPI_INT_STATUS]	= SPI_6358_INT_STATUS,
44244d8fb30SJonas Gorski 	[SPI_INT_MASK_ST]	= SPI_6358_INT_MASK_ST,
44344d8fb30SJonas Gorski 	[SPI_INT_MASK]		= SPI_6358_INT_MASK,
44444d8fb30SJonas Gorski 	[SPI_ST]		= SPI_6358_ST,
44544d8fb30SJonas Gorski 	[SPI_CLK_CFG]		= SPI_6358_CLK_CFG,
44644d8fb30SJonas Gorski 	[SPI_FILL_BYTE]		= SPI_6358_FILL_BYTE,
44744d8fb30SJonas Gorski 	[SPI_MSG_TAIL]		= SPI_6358_MSG_TAIL,
44844d8fb30SJonas Gorski 	[SPI_RX_TAIL]		= SPI_6358_RX_TAIL,
44944d8fb30SJonas Gorski 	[SPI_MSG_CTL]		= SPI_6358_MSG_CTL,
45044d8fb30SJonas Gorski 	[SPI_MSG_DATA]		= SPI_6358_MSG_DATA,
45144d8fb30SJonas Gorski 	[SPI_RX_DATA]		= SPI_6358_RX_DATA,
45244d8fb30SJonas Gorski 	[SPI_MSG_TYPE_SHIFT]	= SPI_6358_MSG_TYPE_SHIFT,
45344d8fb30SJonas Gorski 	[SPI_MSG_CTL_WIDTH]	= SPI_6358_MSG_CTL_WIDTH,
45444d8fb30SJonas Gorski 	[SPI_MSG_DATA_SIZE]	= SPI_6358_MSG_DATA_SIZE,
45544d8fb30SJonas Gorski };
45644d8fb30SJonas Gorski 
45744d8fb30SJonas Gorski static const struct platform_device_id bcm63xx_spi_dev_match[] = {
45844d8fb30SJonas Gorski 	{
45944d8fb30SJonas Gorski 		.name = "bcm6348-spi",
46044d8fb30SJonas Gorski 		.driver_data = (unsigned long)bcm6348_spi_reg_offsets,
46144d8fb30SJonas Gorski 	},
46244d8fb30SJonas Gorski 	{
46344d8fb30SJonas Gorski 		.name = "bcm6358-spi",
46444d8fb30SJonas Gorski 		.driver_data = (unsigned long)bcm6358_spi_reg_offsets,
46544d8fb30SJonas Gorski 	},
46644d8fb30SJonas Gorski 	{
46744d8fb30SJonas Gorski 	},
46844d8fb30SJonas Gorski };
469b42dfed8SFlorian Fainelli 
470c29f0889SJonas Gorski static const struct of_device_id bcm63xx_spi_of_match[] = {
471c29f0889SJonas Gorski 	{ .compatible = "brcm,bcm6348-spi", .data = &bcm6348_spi_reg_offsets },
472c29f0889SJonas Gorski 	{ .compatible = "brcm,bcm6358-spi", .data = &bcm6358_spi_reg_offsets },
473c29f0889SJonas Gorski 	{ },
474c29f0889SJonas Gorski };
475*709df70aSLiao Chen MODULE_DEVICE_TABLE(of, bcm63xx_spi_of_match);
476c29f0889SJonas Gorski 
477fd4a319bSGrant Likely static int bcm63xx_spi_probe(struct platform_device *pdev)
478b42dfed8SFlorian Fainelli {
479b42dfed8SFlorian Fainelli 	struct resource *r;
48044d8fb30SJonas Gorski 	const unsigned long *bcm63xx_spireg;
481b42dfed8SFlorian Fainelli 	struct device *dev = &pdev->dev;
482c29f0889SJonas Gorski 	int irq, bus_num;
4831a9e7619SYang Yingliang 	struct spi_controller *host;
484b42dfed8SFlorian Fainelli 	struct clk *clk;
485b42dfed8SFlorian Fainelli 	struct bcm63xx_spi *bs;
486b42dfed8SFlorian Fainelli 	int ret;
487c29f0889SJonas Gorski 	u32 num_cs = BCM63XX_SPI_MAX_CS;
48838807adeSÁlvaro Fernández Rojas 	struct reset_control *reset;
489b42dfed8SFlorian Fainelli 
490c29f0889SJonas Gorski 	if (dev->of_node) {
491c29f0889SJonas Gorski 		const struct of_device_id *match;
492c29f0889SJonas Gorski 
493c29f0889SJonas Gorski 		match = of_match_node(bcm63xx_spi_of_match, dev->of_node);
494c29f0889SJonas Gorski 		if (!match)
49544d8fb30SJonas Gorski 			return -EINVAL;
496c29f0889SJonas Gorski 		bcm63xx_spireg = match->data;
49744d8fb30SJonas Gorski 
498c29f0889SJonas Gorski 		of_property_read_u32(dev->of_node, "num-cs", &num_cs);
499c29f0889SJonas Gorski 		if (num_cs > BCM63XX_SPI_MAX_CS) {
500c29f0889SJonas Gorski 			dev_warn(dev, "unsupported number of cs (%i), reducing to 8\n",
501c29f0889SJonas Gorski 				 num_cs);
502c29f0889SJonas Gorski 			num_cs = BCM63XX_SPI_MAX_CS;
503c29f0889SJonas Gorski 		}
504c29f0889SJonas Gorski 
505c29f0889SJonas Gorski 		bus_num = -1;
506c29f0889SJonas Gorski 	} else if (pdev->id_entry->driver_data) {
507c29f0889SJonas Gorski 		const struct platform_device_id *match = pdev->id_entry;
508c29f0889SJonas Gorski 
509c29f0889SJonas Gorski 		bcm63xx_spireg = (const unsigned long *)match->driver_data;
510c29f0889SJonas Gorski 		bus_num = BCM63XX_SPI_BUS_NUM;
511c29f0889SJonas Gorski 	} else {
512c29f0889SJonas Gorski 		return -EINVAL;
513c29f0889SJonas Gorski 	}
51444d8fb30SJonas Gorski 
515b42dfed8SFlorian Fainelli 	irq = platform_get_irq(pdev, 0);
5166b8ac10eSStephen Boyd 	if (irq < 0)
517ba8afe94SGustavo A. R. Silva 		return irq;
518b42dfed8SFlorian Fainelli 
519acf4fc6fSJingoo Han 	clk = devm_clk_get(dev, "spi");
520b42dfed8SFlorian Fainelli 	if (IS_ERR(clk)) {
521b42dfed8SFlorian Fainelli 		dev_err(dev, "no clock for device\n");
522acf4fc6fSJingoo Han 		return PTR_ERR(clk);
523b42dfed8SFlorian Fainelli 	}
524b42dfed8SFlorian Fainelli 
52538807adeSÁlvaro Fernández Rojas 	reset = devm_reset_control_get_optional_exclusive(dev, NULL);
52638807adeSÁlvaro Fernández Rojas 	if (IS_ERR(reset))
52738807adeSÁlvaro Fernández Rojas 		return PTR_ERR(reset);
52838807adeSÁlvaro Fernández Rojas 
5291a9e7619SYang Yingliang 	host = spi_alloc_host(dev, sizeof(*bs));
5301a9e7619SYang Yingliang 	if (!host) {
531b42dfed8SFlorian Fainelli 		dev_err(dev, "out of memory\n");
532acf4fc6fSJingoo Han 		return -ENOMEM;
533b42dfed8SFlorian Fainelli 	}
534b42dfed8SFlorian Fainelli 
5351a9e7619SYang Yingliang 	bs = spi_controller_get_devdata(host);
536aa0fe826SAxel Lin 	init_completion(&bs->done);
537b42dfed8SFlorian Fainelli 
5381a9e7619SYang Yingliang 	platform_set_drvdata(pdev, host);
539b42dfed8SFlorian Fainelli 	bs->pdev = pdev;
540b42dfed8SFlorian Fainelli 
541a008ae9fSYang Yingliang 	bs->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &r);
542b66c7730SJonas Gorski 	if (IS_ERR(bs->regs)) {
543b66c7730SJonas Gorski 		ret = PTR_ERR(bs->regs);
544b42dfed8SFlorian Fainelli 		goto out_err;
545b42dfed8SFlorian Fainelli 	}
546b42dfed8SFlorian Fainelli 
547b42dfed8SFlorian Fainelli 	bs->irq = irq;
548b42dfed8SFlorian Fainelli 	bs->clk = clk;
54944d8fb30SJonas Gorski 	bs->reg_offsets = bcm63xx_spireg;
55044d8fb30SJonas Gorski 	bs->fifo_size = bs->reg_offsets[SPI_MSG_DATA_SIZE];
551b42dfed8SFlorian Fainelli 
552b42dfed8SFlorian Fainelli 	ret = devm_request_irq(&pdev->dev, irq, bcm63xx_spi_interrupt, 0,
5531a9e7619SYang Yingliang 			       pdev->name, host);
554b42dfed8SFlorian Fainelli 	if (ret) {
555b42dfed8SFlorian Fainelli 		dev_err(dev, "unable to request irq\n");
556b42dfed8SFlorian Fainelli 		goto out_err;
557b42dfed8SFlorian Fainelli 	}
558b42dfed8SFlorian Fainelli 
5591a9e7619SYang Yingliang 	host->dev.of_node = dev->of_node;
5601a9e7619SYang Yingliang 	host->bus_num = bus_num;
5611a9e7619SYang Yingliang 	host->num_chipselect = num_cs;
5621a9e7619SYang Yingliang 	host->transfer_one_message = bcm63xx_spi_transfer_one;
5631a9e7619SYang Yingliang 	host->mode_bits = MODEBITS;
5641a9e7619SYang Yingliang 	host->bits_per_word_mask = SPI_BPW_MASK(8);
5651a9e7619SYang Yingliang 	host->max_transfer_size = bcm63xx_spi_max_length;
5661a9e7619SYang Yingliang 	host->max_message_size = bcm63xx_spi_max_length;
5671a9e7619SYang Yingliang 	host->auto_runtime_pm = true;
56844d8fb30SJonas Gorski 	bs->msg_type_shift = bs->reg_offsets[SPI_MSG_TYPE_SHIFT];
56944d8fb30SJonas Gorski 	bs->msg_ctl_width = bs->reg_offsets[SPI_MSG_CTL_WIDTH];
57044d8fb30SJonas Gorski 	bs->tx_io = (u8 *)(bs->regs + bs->reg_offsets[SPI_MSG_DATA]);
57144d8fb30SJonas Gorski 	bs->rx_io = (const u8 *)(bs->regs + bs->reg_offsets[SPI_RX_DATA]);
5725a670445SFlorian Fainelli 
573b42dfed8SFlorian Fainelli 	/* Initialize hardware */
574ea01e8a4SJonas Gorski 	ret = clk_prepare_enable(bs->clk);
575ea01e8a4SJonas Gorski 	if (ret)
576ea01e8a4SJonas Gorski 		goto out_err;
577ea01e8a4SJonas Gorski 
57838807adeSÁlvaro Fernández Rojas 	ret = reset_control_reset(reset);
57938807adeSÁlvaro Fernández Rojas 	if (ret) {
58038807adeSÁlvaro Fernández Rojas 		dev_err(dev, "unable to reset device: %d\n", ret);
58138807adeSÁlvaro Fernández Rojas 		goto out_clk_disable;
58238807adeSÁlvaro Fernández Rojas 	}
58338807adeSÁlvaro Fernández Rojas 
584b42dfed8SFlorian Fainelli 	bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS);
585b42dfed8SFlorian Fainelli 
5862d13f2ffSÁlvaro Fernández Rojas 	pm_runtime_enable(&pdev->dev);
5872d13f2ffSÁlvaro Fernández Rojas 
588b42dfed8SFlorian Fainelli 	/* register and we are done */
5891a9e7619SYang Yingliang 	ret = devm_spi_register_controller(dev, host);
590b42dfed8SFlorian Fainelli 	if (ret) {
591b42dfed8SFlorian Fainelli 		dev_err(dev, "spi register failed\n");
5922d13f2ffSÁlvaro Fernández Rojas 		goto out_pm_disable;
593b42dfed8SFlorian Fainelli 	}
594b42dfed8SFlorian Fainelli 
5950ba2cf70SArnd Bergmann 	dev_info(dev, "at %pr (irq %d, FIFOs size %d)\n",
5960ba2cf70SArnd Bergmann 		 r, irq, bs->fifo_size);
597b42dfed8SFlorian Fainelli 
598b42dfed8SFlorian Fainelli 	return 0;
599b42dfed8SFlorian Fainelli 
6002d13f2ffSÁlvaro Fernández Rojas out_pm_disable:
6012d13f2ffSÁlvaro Fernández Rojas 	pm_runtime_disable(&pdev->dev);
602b42dfed8SFlorian Fainelli out_clk_disable:
6034fbb82a7SJonas Gorski 	clk_disable_unprepare(clk);
604b42dfed8SFlorian Fainelli out_err:
6051a9e7619SYang Yingliang 	spi_controller_put(host);
606b42dfed8SFlorian Fainelli 	return ret;
607b42dfed8SFlorian Fainelli }
608b42dfed8SFlorian Fainelli 
6098c26432eSUwe Kleine-König static void bcm63xx_spi_remove(struct platform_device *pdev)
610b42dfed8SFlorian Fainelli {
6111a9e7619SYang Yingliang 	struct spi_controller *host = platform_get_drvdata(pdev);
6121a9e7619SYang Yingliang 	struct bcm63xx_spi *bs = spi_controller_get_devdata(host);
613b42dfed8SFlorian Fainelli 
614b42dfed8SFlorian Fainelli 	/* reset spi block */
615b42dfed8SFlorian Fainelli 	bcm_spi_writeb(bs, 0, SPI_INT_MASK);
616b42dfed8SFlorian Fainelli 
617b42dfed8SFlorian Fainelli 	/* HW shutdown */
6184fbb82a7SJonas Gorski 	clk_disable_unprepare(bs->clk);
619b42dfed8SFlorian Fainelli }
620b42dfed8SFlorian Fainelli 
621b42dfed8SFlorian Fainelli static int bcm63xx_spi_suspend(struct device *dev)
622b42dfed8SFlorian Fainelli {
6231a9e7619SYang Yingliang 	struct spi_controller *host = dev_get_drvdata(dev);
6241a9e7619SYang Yingliang 	struct bcm63xx_spi *bs = spi_controller_get_devdata(host);
625b42dfed8SFlorian Fainelli 
6261a9e7619SYang Yingliang 	spi_controller_suspend(host);
62796519957SFlorian Fainelli 
6284fbb82a7SJonas Gorski 	clk_disable_unprepare(bs->clk);
629b42dfed8SFlorian Fainelli 
630b42dfed8SFlorian Fainelli 	return 0;
631b42dfed8SFlorian Fainelli }
632b42dfed8SFlorian Fainelli 
633b42dfed8SFlorian Fainelli static int bcm63xx_spi_resume(struct device *dev)
634b42dfed8SFlorian Fainelli {
6351a9e7619SYang Yingliang 	struct spi_controller *host = dev_get_drvdata(dev);
6361a9e7619SYang Yingliang 	struct bcm63xx_spi *bs = spi_controller_get_devdata(host);
637ea01e8a4SJonas Gorski 	int ret;
638b42dfed8SFlorian Fainelli 
639ea01e8a4SJonas Gorski 	ret = clk_prepare_enable(bs->clk);
640ea01e8a4SJonas Gorski 	if (ret)
641ea01e8a4SJonas Gorski 		return ret;
642b42dfed8SFlorian Fainelli 
6431a9e7619SYang Yingliang 	spi_controller_resume(host);
64496519957SFlorian Fainelli 
645b42dfed8SFlorian Fainelli 	return 0;
646b42dfed8SFlorian Fainelli }
647b42dfed8SFlorian Fainelli 
648cc5f6fa4SDhruva Gole static DEFINE_SIMPLE_DEV_PM_OPS(bcm63xx_spi_pm_ops, bcm63xx_spi_suspend, bcm63xx_spi_resume);
649b42dfed8SFlorian Fainelli 
650b42dfed8SFlorian Fainelli static struct platform_driver bcm63xx_spi_driver = {
651b42dfed8SFlorian Fainelli 	.driver = {
652b42dfed8SFlorian Fainelli 		.name	= "bcm63xx-spi",
6531bae2028SJonas Gorski 		.pm	= &bcm63xx_spi_pm_ops,
654c29f0889SJonas Gorski 		.of_match_table = bcm63xx_spi_of_match,
655b42dfed8SFlorian Fainelli 	},
65644d8fb30SJonas Gorski 	.id_table	= bcm63xx_spi_dev_match,
657b42dfed8SFlorian Fainelli 	.probe		= bcm63xx_spi_probe,
6588c26432eSUwe Kleine-König 	.remove_new	= bcm63xx_spi_remove,
659b42dfed8SFlorian Fainelli };
660b42dfed8SFlorian Fainelli 
661b42dfed8SFlorian Fainelli module_platform_driver(bcm63xx_spi_driver);
662b42dfed8SFlorian Fainelli 
663b42dfed8SFlorian Fainelli MODULE_ALIAS("platform:bcm63xx_spi");
664b42dfed8SFlorian Fainelli MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
665b42dfed8SFlorian Fainelli MODULE_AUTHOR("Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>");
666b42dfed8SFlorian Fainelli MODULE_DESCRIPTION("Broadcom BCM63xx SPI Controller driver");
667b42dfed8SFlorian Fainelli MODULE_LICENSE("GPL");
668