xref: /linux/drivers/spi/spi-bcm63xx.c (revision b85d65dd47e1606299dc054d2162b795df65c94a)
1b42dfed8SFlorian Fainelli /*
2b42dfed8SFlorian Fainelli  * Broadcom BCM63xx SPI controller support
3b42dfed8SFlorian Fainelli  *
4cde4384eSFlorian Fainelli  * Copyright (C) 2009-2012 Florian Fainelli <florian@openwrt.org>
5b42dfed8SFlorian Fainelli  * Copyright (C) 2010 Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>
6b42dfed8SFlorian Fainelli  *
7b42dfed8SFlorian Fainelli  * This program is free software; you can redistribute it and/or
8b42dfed8SFlorian Fainelli  * modify it under the terms of the GNU General Public License
9b42dfed8SFlorian Fainelli  * as published by the Free Software Foundation; either version 2
10b42dfed8SFlorian Fainelli  * of the License, or (at your option) any later version.
11b42dfed8SFlorian Fainelli  *
12b42dfed8SFlorian Fainelli  * This program is distributed in the hope that it will be useful,
13b42dfed8SFlorian Fainelli  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14b42dfed8SFlorian Fainelli  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15b42dfed8SFlorian Fainelli  * GNU General Public License for more details.
16b42dfed8SFlorian Fainelli  */
17b42dfed8SFlorian Fainelli 
18b42dfed8SFlorian Fainelli #include <linux/kernel.h>
19b42dfed8SFlorian Fainelli #include <linux/clk.h>
20b42dfed8SFlorian Fainelli #include <linux/io.h>
21b42dfed8SFlorian Fainelli #include <linux/module.h>
22b42dfed8SFlorian Fainelli #include <linux/platform_device.h>
23b42dfed8SFlorian Fainelli #include <linux/delay.h>
24b42dfed8SFlorian Fainelli #include <linux/interrupt.h>
25b42dfed8SFlorian Fainelli #include <linux/spi/spi.h>
26b42dfed8SFlorian Fainelli #include <linux/completion.h>
27b42dfed8SFlorian Fainelli #include <linux/err.h>
28cde4384eSFlorian Fainelli #include <linux/pm_runtime.h>
29c29f0889SJonas Gorski #include <linux/of.h>
30b42dfed8SFlorian Fainelli 
3144d8fb30SJonas Gorski /* BCM 6338/6348 SPI core */
3244d8fb30SJonas Gorski #define SPI_6348_RSET_SIZE		64
3344d8fb30SJonas Gorski #define SPI_6348_CMD			0x00	/* 16-bits register */
3444d8fb30SJonas Gorski #define SPI_6348_INT_STATUS		0x02
3544d8fb30SJonas Gorski #define SPI_6348_INT_MASK_ST		0x03
3644d8fb30SJonas Gorski #define SPI_6348_INT_MASK		0x04
3744d8fb30SJonas Gorski #define SPI_6348_ST			0x05
3844d8fb30SJonas Gorski #define SPI_6348_CLK_CFG		0x06
3944d8fb30SJonas Gorski #define SPI_6348_FILL_BYTE		0x07
4044d8fb30SJonas Gorski #define SPI_6348_MSG_TAIL		0x09
4144d8fb30SJonas Gorski #define SPI_6348_RX_TAIL		0x0b
4244d8fb30SJonas Gorski #define SPI_6348_MSG_CTL		0x40	/* 8-bits register */
4344d8fb30SJonas Gorski #define SPI_6348_MSG_CTL_WIDTH		8
4444d8fb30SJonas Gorski #define SPI_6348_MSG_DATA		0x41
4544d8fb30SJonas Gorski #define SPI_6348_MSG_DATA_SIZE		0x3f
4644d8fb30SJonas Gorski #define SPI_6348_RX_DATA		0x80
4744d8fb30SJonas Gorski #define SPI_6348_RX_DATA_SIZE		0x3f
4844d8fb30SJonas Gorski 
4944d8fb30SJonas Gorski /* BCM 3368/6358/6262/6368 SPI core */
5044d8fb30SJonas Gorski #define SPI_6358_RSET_SIZE		1804
5144d8fb30SJonas Gorski #define SPI_6358_MSG_CTL		0x00	/* 16-bits register */
5244d8fb30SJonas Gorski #define SPI_6358_MSG_CTL_WIDTH		16
5344d8fb30SJonas Gorski #define SPI_6358_MSG_DATA		0x02
5444d8fb30SJonas Gorski #define SPI_6358_MSG_DATA_SIZE		0x21e
5544d8fb30SJonas Gorski #define SPI_6358_RX_DATA		0x400
5644d8fb30SJonas Gorski #define SPI_6358_RX_DATA_SIZE		0x220
5744d8fb30SJonas Gorski #define SPI_6358_CMD			0x700	/* 16-bits register */
5844d8fb30SJonas Gorski #define SPI_6358_INT_STATUS		0x702
5944d8fb30SJonas Gorski #define SPI_6358_INT_MASK_ST		0x703
6044d8fb30SJonas Gorski #define SPI_6358_INT_MASK		0x704
6144d8fb30SJonas Gorski #define SPI_6358_ST			0x705
6244d8fb30SJonas Gorski #define SPI_6358_CLK_CFG		0x706
6344d8fb30SJonas Gorski #define SPI_6358_FILL_BYTE		0x707
6444d8fb30SJonas Gorski #define SPI_6358_MSG_TAIL		0x709
6544d8fb30SJonas Gorski #define SPI_6358_RX_TAIL		0x70B
6644d8fb30SJonas Gorski 
6744d8fb30SJonas Gorski /* Shared SPI definitions */
6844d8fb30SJonas Gorski 
6944d8fb30SJonas Gorski /* Message configuration */
7044d8fb30SJonas Gorski #define SPI_FD_RW			0x00
7144d8fb30SJonas Gorski #define SPI_HD_W			0x01
7244d8fb30SJonas Gorski #define SPI_HD_R			0x02
7344d8fb30SJonas Gorski #define SPI_BYTE_CNT_SHIFT		0
7444d8fb30SJonas Gorski #define SPI_6348_MSG_TYPE_SHIFT		6
7544d8fb30SJonas Gorski #define SPI_6358_MSG_TYPE_SHIFT		14
7644d8fb30SJonas Gorski 
7744d8fb30SJonas Gorski /* Command */
7844d8fb30SJonas Gorski #define SPI_CMD_NOOP			0x00
7944d8fb30SJonas Gorski #define SPI_CMD_SOFT_RESET		0x01
8044d8fb30SJonas Gorski #define SPI_CMD_HARD_RESET		0x02
8144d8fb30SJonas Gorski #define SPI_CMD_START_IMMEDIATE		0x03
8244d8fb30SJonas Gorski #define SPI_CMD_COMMAND_SHIFT		0
8344d8fb30SJonas Gorski #define SPI_CMD_COMMAND_MASK		0x000f
8444d8fb30SJonas Gorski #define SPI_CMD_DEVICE_ID_SHIFT		4
8544d8fb30SJonas Gorski #define SPI_CMD_PREPEND_BYTE_CNT_SHIFT	8
8644d8fb30SJonas Gorski #define SPI_CMD_ONE_BYTE_SHIFT		11
8744d8fb30SJonas Gorski #define SPI_CMD_ONE_WIRE_SHIFT		12
8844d8fb30SJonas Gorski #define SPI_DEV_ID_0			0
8944d8fb30SJonas Gorski #define SPI_DEV_ID_1			1
9044d8fb30SJonas Gorski #define SPI_DEV_ID_2			2
9144d8fb30SJonas Gorski #define SPI_DEV_ID_3			3
9244d8fb30SJonas Gorski 
9344d8fb30SJonas Gorski /* Interrupt mask */
9444d8fb30SJonas Gorski #define SPI_INTR_CMD_DONE		0x01
9544d8fb30SJonas Gorski #define SPI_INTR_RX_OVERFLOW		0x02
9644d8fb30SJonas Gorski #define SPI_INTR_TX_UNDERFLOW		0x04
9744d8fb30SJonas Gorski #define SPI_INTR_TX_OVERFLOW		0x08
9844d8fb30SJonas Gorski #define SPI_INTR_RX_UNDERFLOW		0x10
9944d8fb30SJonas Gorski #define SPI_INTR_CLEAR_ALL		0x1f
10044d8fb30SJonas Gorski 
10144d8fb30SJonas Gorski /* Status */
10244d8fb30SJonas Gorski #define SPI_RX_EMPTY			0x02
10344d8fb30SJonas Gorski #define SPI_CMD_BUSY			0x04
10444d8fb30SJonas Gorski #define SPI_SERIAL_BUSY			0x08
10544d8fb30SJonas Gorski 
10644d8fb30SJonas Gorski /* Clock configuration */
10744d8fb30SJonas Gorski #define SPI_CLK_20MHZ			0x00
10844d8fb30SJonas Gorski #define SPI_CLK_0_391MHZ		0x01
10944d8fb30SJonas Gorski #define SPI_CLK_0_781MHZ		0x02	/* default */
11044d8fb30SJonas Gorski #define SPI_CLK_1_563MHZ		0x03
11144d8fb30SJonas Gorski #define SPI_CLK_3_125MHZ		0x04
11244d8fb30SJonas Gorski #define SPI_CLK_6_250MHZ		0x05
11344d8fb30SJonas Gorski #define SPI_CLK_12_50MHZ		0x06
11444d8fb30SJonas Gorski #define SPI_CLK_MASK			0x07
11544d8fb30SJonas Gorski #define SPI_SSOFFTIME_MASK		0x38
11644d8fb30SJonas Gorski #define SPI_SSOFFTIME_SHIFT		3
11744d8fb30SJonas Gorski #define SPI_BYTE_SWAP			0x80
11844d8fb30SJonas Gorski 
11944d8fb30SJonas Gorski enum bcm63xx_regs_spi {
12044d8fb30SJonas Gorski 	SPI_CMD,
12144d8fb30SJonas Gorski 	SPI_INT_STATUS,
12244d8fb30SJonas Gorski 	SPI_INT_MASK_ST,
12344d8fb30SJonas Gorski 	SPI_INT_MASK,
12444d8fb30SJonas Gorski 	SPI_ST,
12544d8fb30SJonas Gorski 	SPI_CLK_CFG,
12644d8fb30SJonas Gorski 	SPI_FILL_BYTE,
12744d8fb30SJonas Gorski 	SPI_MSG_TAIL,
12844d8fb30SJonas Gorski 	SPI_RX_TAIL,
12944d8fb30SJonas Gorski 	SPI_MSG_CTL,
13044d8fb30SJonas Gorski 	SPI_MSG_DATA,
13144d8fb30SJonas Gorski 	SPI_RX_DATA,
13244d8fb30SJonas Gorski 	SPI_MSG_TYPE_SHIFT,
13344d8fb30SJonas Gorski 	SPI_MSG_CTL_WIDTH,
13444d8fb30SJonas Gorski 	SPI_MSG_DATA_SIZE,
13544d8fb30SJonas Gorski };
136b42dfed8SFlorian Fainelli 
137b17de076SJonas Gorski #define BCM63XX_SPI_MAX_PREPEND		15
138b17de076SJonas Gorski 
13965059997SJonas Gorski #define BCM63XX_SPI_MAX_CS		8
140a45fcea5SJonas Gorski #define BCM63XX_SPI_BUS_NUM		0
14165059997SJonas Gorski 
142b42dfed8SFlorian Fainelli struct bcm63xx_spi {
143b42dfed8SFlorian Fainelli 	struct completion	done;
144b42dfed8SFlorian Fainelli 
145b42dfed8SFlorian Fainelli 	void __iomem		*regs;
146b42dfed8SFlorian Fainelli 	int			irq;
147b42dfed8SFlorian Fainelli 
148b42dfed8SFlorian Fainelli 	/* Platform data */
14944d8fb30SJonas Gorski 	const unsigned long	*reg_offsets;
150*b85d65ddSAravind Thokala 	unsigned int		fifo_size;
1515a670445SFlorian Fainelli 	unsigned int		msg_type_shift;
1525a670445SFlorian Fainelli 	unsigned int		msg_ctl_width;
153b42dfed8SFlorian Fainelli 
154b42dfed8SFlorian Fainelli 	/* data iomem */
155b42dfed8SFlorian Fainelli 	u8 __iomem		*tx_io;
156b42dfed8SFlorian Fainelli 	const u8 __iomem	*rx_io;
157b42dfed8SFlorian Fainelli 
158b42dfed8SFlorian Fainelli 	struct clk		*clk;
159b42dfed8SFlorian Fainelli 	struct platform_device	*pdev;
160b42dfed8SFlorian Fainelli };
161b42dfed8SFlorian Fainelli 
162b42dfed8SFlorian Fainelli static inline u8 bcm_spi_readb(struct bcm63xx_spi *bs,
163b42dfed8SFlorian Fainelli 			       unsigned int offset)
164b42dfed8SFlorian Fainelli {
16544d8fb30SJonas Gorski 	return readb(bs->regs + bs->reg_offsets[offset]);
166b42dfed8SFlorian Fainelli }
167b42dfed8SFlorian Fainelli 
168b42dfed8SFlorian Fainelli static inline u16 bcm_spi_readw(struct bcm63xx_spi *bs,
169b42dfed8SFlorian Fainelli 				unsigned int offset)
170b42dfed8SFlorian Fainelli {
171682b5280SJonas Gorski #ifdef CONFIG_CPU_BIG_ENDIAN
17244d8fb30SJonas Gorski 	return ioread16be(bs->regs + bs->reg_offsets[offset]);
173158fcc4eSJonas Gorski #else
17444d8fb30SJonas Gorski 	return readw(bs->regs + bs->reg_offsets[offset]);
175158fcc4eSJonas Gorski #endif
176b42dfed8SFlorian Fainelli }
177b42dfed8SFlorian Fainelli 
178b42dfed8SFlorian Fainelli static inline void bcm_spi_writeb(struct bcm63xx_spi *bs,
179b42dfed8SFlorian Fainelli 				  u8 value, unsigned int offset)
180b42dfed8SFlorian Fainelli {
18144d8fb30SJonas Gorski 	writeb(value, bs->regs + bs->reg_offsets[offset]);
182b42dfed8SFlorian Fainelli }
183b42dfed8SFlorian Fainelli 
184b42dfed8SFlorian Fainelli static inline void bcm_spi_writew(struct bcm63xx_spi *bs,
185b42dfed8SFlorian Fainelli 				  u16 value, unsigned int offset)
186b42dfed8SFlorian Fainelli {
187682b5280SJonas Gorski #ifdef CONFIG_CPU_BIG_ENDIAN
18844d8fb30SJonas Gorski 	iowrite16be(value, bs->regs + bs->reg_offsets[offset]);
189158fcc4eSJonas Gorski #else
19044d8fb30SJonas Gorski 	writew(value, bs->regs + bs->reg_offsets[offset]);
191158fcc4eSJonas Gorski #endif
192b42dfed8SFlorian Fainelli }
193b42dfed8SFlorian Fainelli 
194*b85d65ddSAravind Thokala static const unsigned int bcm63xx_spi_freq_table[SPI_CLK_MASK][2] = {
195b42dfed8SFlorian Fainelli 	{ 20000000, SPI_CLK_20MHZ },
196b42dfed8SFlorian Fainelli 	{ 12500000, SPI_CLK_12_50MHZ },
197b42dfed8SFlorian Fainelli 	{  6250000, SPI_CLK_6_250MHZ },
198b42dfed8SFlorian Fainelli 	{  3125000, SPI_CLK_3_125MHZ },
199b42dfed8SFlorian Fainelli 	{  1563000, SPI_CLK_1_563MHZ },
200b42dfed8SFlorian Fainelli 	{   781000, SPI_CLK_0_781MHZ },
201b42dfed8SFlorian Fainelli 	{   391000, SPI_CLK_0_391MHZ }
202b42dfed8SFlorian Fainelli };
203b42dfed8SFlorian Fainelli 
204cde4384eSFlorian Fainelli static void bcm63xx_spi_setup_transfer(struct spi_device *spi,
205cde4384eSFlorian Fainelli 				      struct spi_transfer *t)
206cde4384eSFlorian Fainelli {
207cde4384eSFlorian Fainelli 	struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master);
208cde4384eSFlorian Fainelli 	u8 clk_cfg, reg;
209cde4384eSFlorian Fainelli 	int i;
210cde4384eSFlorian Fainelli 
2114dde99bdSGeert Uytterhoeven 	/* Default to lowest clock configuration */
2124dde99bdSGeert Uytterhoeven 	clk_cfg = SPI_CLK_0_391MHZ;
2134dde99bdSGeert Uytterhoeven 
214b42dfed8SFlorian Fainelli 	/* Find the closest clock configuration */
215b42dfed8SFlorian Fainelli 	for (i = 0; i < SPI_CLK_MASK; i++) {
21668792e2aSJonas Gorski 		if (t->speed_hz >= bcm63xx_spi_freq_table[i][0]) {
217b42dfed8SFlorian Fainelli 			clk_cfg = bcm63xx_spi_freq_table[i][1];
218b42dfed8SFlorian Fainelli 			break;
219b42dfed8SFlorian Fainelli 		}
220b42dfed8SFlorian Fainelli 	}
221b42dfed8SFlorian Fainelli 
222b42dfed8SFlorian Fainelli 	/* clear existing clock configuration bits of the register */
223b42dfed8SFlorian Fainelli 	reg = bcm_spi_readb(bs, SPI_CLK_CFG);
224b42dfed8SFlorian Fainelli 	reg &= ~SPI_CLK_MASK;
225b42dfed8SFlorian Fainelli 	reg |= clk_cfg;
226b42dfed8SFlorian Fainelli 
227b42dfed8SFlorian Fainelli 	bcm_spi_writeb(bs, reg, SPI_CLK_CFG);
228b42dfed8SFlorian Fainelli 	dev_dbg(&spi->dev, "Setting clock register to %02x (hz %d)\n",
22968792e2aSJonas Gorski 		clk_cfg, t->speed_hz);
230b42dfed8SFlorian Fainelli }
231b42dfed8SFlorian Fainelli 
232b42dfed8SFlorian Fainelli /* the spi->mode bits understood by this driver: */
233b42dfed8SFlorian Fainelli #define MODEBITS (SPI_CPOL | SPI_CPHA)
234b42dfed8SFlorian Fainelli 
235b17de076SJonas Gorski static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *first,
236b17de076SJonas Gorski 				unsigned int num_transfers)
237b42dfed8SFlorian Fainelli {
238b42dfed8SFlorian Fainelli 	struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master);
239b42dfed8SFlorian Fainelli 	u16 msg_ctl;
240b42dfed8SFlorian Fainelli 	u16 cmd;
241b17de076SJonas Gorski 	unsigned int i, timeout = 0, prepend_len = 0, len = 0;
242b17de076SJonas Gorski 	struct spi_transfer *t = first;
243b17de076SJonas Gorski 	bool do_rx = false;
244b17de076SJonas Gorski 	bool do_tx = false;
245b42dfed8SFlorian Fainelli 
246cde4384eSFlorian Fainelli 	/* Disable the CMD_DONE interrupt */
247cde4384eSFlorian Fainelli 	bcm_spi_writeb(bs, 0, SPI_INT_MASK);
248cde4384eSFlorian Fainelli 
249b42dfed8SFlorian Fainelli 	dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n",
250b42dfed8SFlorian Fainelli 		t->tx_buf, t->rx_buf, t->len);
251b42dfed8SFlorian Fainelli 
252b17de076SJonas Gorski 	if (num_transfers > 1 && t->tx_buf && t->len <= BCM63XX_SPI_MAX_PREPEND)
253b17de076SJonas Gorski 		prepend_len = t->len;
254b17de076SJonas Gorski 
255b17de076SJonas Gorski 	/* prepare the buffer */
256b17de076SJonas Gorski 	for (i = 0; i < num_transfers; i++) {
257b17de076SJonas Gorski 		if (t->tx_buf) {
258b17de076SJonas Gorski 			do_tx = true;
259b17de076SJonas Gorski 			memcpy_toio(bs->tx_io + len, t->tx_buf, t->len);
260b17de076SJonas Gorski 
261b17de076SJonas Gorski 			/* don't prepend more than one tx */
262b17de076SJonas Gorski 			if (t != first)
263b17de076SJonas Gorski 				prepend_len = 0;
264b17de076SJonas Gorski 		}
265b17de076SJonas Gorski 
266b17de076SJonas Gorski 		if (t->rx_buf) {
267b17de076SJonas Gorski 			do_rx = true;
268b17de076SJonas Gorski 			/* prepend is half-duplex write only */
269b17de076SJonas Gorski 			if (t == first)
270b17de076SJonas Gorski 				prepend_len = 0;
271b17de076SJonas Gorski 		}
272b17de076SJonas Gorski 
273b17de076SJonas Gorski 		len += t->len;
274b17de076SJonas Gorski 
275b17de076SJonas Gorski 		t = list_entry(t->transfer_list.next, struct spi_transfer,
276b17de076SJonas Gorski 			       transfer_list);
277b17de076SJonas Gorski 	}
278b17de076SJonas Gorski 
279aa0fe826SAxel Lin 	reinit_completion(&bs->done);
280b42dfed8SFlorian Fainelli 
281b42dfed8SFlorian Fainelli 	/* Fill in the Message control register */
282b17de076SJonas Gorski 	msg_ctl = (len << SPI_BYTE_CNT_SHIFT);
283b42dfed8SFlorian Fainelli 
284b17de076SJonas Gorski 	if (do_rx && do_tx && prepend_len == 0)
2855a670445SFlorian Fainelli 		msg_ctl |= (SPI_FD_RW << bs->msg_type_shift);
286b17de076SJonas Gorski 	else if (do_rx)
2875a670445SFlorian Fainelli 		msg_ctl |= (SPI_HD_R << bs->msg_type_shift);
288b17de076SJonas Gorski 	else if (do_tx)
2895a670445SFlorian Fainelli 		msg_ctl |= (SPI_HD_W << bs->msg_type_shift);
290b42dfed8SFlorian Fainelli 
2915a670445SFlorian Fainelli 	switch (bs->msg_ctl_width) {
2925a670445SFlorian Fainelli 	case 8:
2935a670445SFlorian Fainelli 		bcm_spi_writeb(bs, msg_ctl, SPI_MSG_CTL);
2945a670445SFlorian Fainelli 		break;
2955a670445SFlorian Fainelli 	case 16:
296b42dfed8SFlorian Fainelli 		bcm_spi_writew(bs, msg_ctl, SPI_MSG_CTL);
2975a670445SFlorian Fainelli 		break;
2985a670445SFlorian Fainelli 	}
299b42dfed8SFlorian Fainelli 
300b42dfed8SFlorian Fainelli 	/* Issue the transfer */
301b42dfed8SFlorian Fainelli 	cmd = SPI_CMD_START_IMMEDIATE;
302b17de076SJonas Gorski 	cmd |= (prepend_len << SPI_CMD_PREPEND_BYTE_CNT_SHIFT);
303b42dfed8SFlorian Fainelli 	cmd |= (spi->chip_select << SPI_CMD_DEVICE_ID_SHIFT);
304b42dfed8SFlorian Fainelli 	bcm_spi_writew(bs, cmd, SPI_CMD);
305b42dfed8SFlorian Fainelli 
306cde4384eSFlorian Fainelli 	/* Enable the CMD_DONE interrupt */
307cde4384eSFlorian Fainelli 	bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK);
308b42dfed8SFlorian Fainelli 
309c0fde3baSJonas Gorski 	timeout = wait_for_completion_timeout(&bs->done, HZ);
310c0fde3baSJonas Gorski 	if (!timeout)
311c0fde3baSJonas Gorski 		return -ETIMEDOUT;
312c0fde3baSJonas Gorski 
31320e9e78fSJonas Gorski 	if (!do_rx)
314b17de076SJonas Gorski 		return 0;
315b17de076SJonas Gorski 
316b17de076SJonas Gorski 	len = 0;
317b17de076SJonas Gorski 	t = first;
318c0fde3baSJonas Gorski 	/* Read out all the data */
319b17de076SJonas Gorski 	for (i = 0; i < num_transfers; i++) {
320b17de076SJonas Gorski 		if (t->rx_buf)
321b17de076SJonas Gorski 			memcpy_fromio(t->rx_buf, bs->rx_io + len, t->len);
322b17de076SJonas Gorski 
323b17de076SJonas Gorski 		if (t != first || prepend_len == 0)
324b17de076SJonas Gorski 			len += t->len;
325b17de076SJonas Gorski 
326b17de076SJonas Gorski 		t = list_entry(t->transfer_list.next, struct spi_transfer,
327b17de076SJonas Gorski 			       transfer_list);
328b17de076SJonas Gorski 	}
329c0fde3baSJonas Gorski 
330c0fde3baSJonas Gorski 	return 0;
331b42dfed8SFlorian Fainelli }
332b42dfed8SFlorian Fainelli 
333cde4384eSFlorian Fainelli static int bcm63xx_spi_transfer_one(struct spi_master *master,
334cde4384eSFlorian Fainelli 					struct spi_message *m)
335cde4384eSFlorian Fainelli {
336cde4384eSFlorian Fainelli 	struct bcm63xx_spi *bs = spi_master_get_devdata(master);
337b17de076SJonas Gorski 	struct spi_transfer *t, *first = NULL;
338cde4384eSFlorian Fainelli 	struct spi_device *spi = m->spi;
339cde4384eSFlorian Fainelli 	int status = 0;
340b17de076SJonas Gorski 	unsigned int n_transfers = 0, total_len = 0;
341b17de076SJonas Gorski 	bool can_use_prepend = false;
342cde4384eSFlorian Fainelli 
343b17de076SJonas Gorski 	/*
344b17de076SJonas Gorski 	 * This SPI controller does not support keeping CS active after a
345b17de076SJonas Gorski 	 * transfer.
346b17de076SJonas Gorski 	 * Work around this by merging as many transfers we can into one big
347b17de076SJonas Gorski 	 * full-duplex transfers.
348b17de076SJonas Gorski 	 */
349cde4384eSFlorian Fainelli 	list_for_each_entry(t, &m->transfers, transfer_list) {
350b17de076SJonas Gorski 		if (!first)
351b17de076SJonas Gorski 			first = t;
352b17de076SJonas Gorski 
353b17de076SJonas Gorski 		n_transfers++;
354b17de076SJonas Gorski 		total_len += t->len;
355b17de076SJonas Gorski 
356b17de076SJonas Gorski 		if (n_transfers == 2 && !first->rx_buf && !t->tx_buf &&
357b17de076SJonas Gorski 		    first->len <= BCM63XX_SPI_MAX_PREPEND)
358b17de076SJonas Gorski 			can_use_prepend = true;
359b17de076SJonas Gorski 		else if (can_use_prepend && t->tx_buf)
360b17de076SJonas Gorski 			can_use_prepend = false;
361b17de076SJonas Gorski 
362c0fde3baSJonas Gorski 		/* we can only transfer one fifo worth of data */
363b17de076SJonas Gorski 		if ((can_use_prepend &&
364b17de076SJonas Gorski 		     total_len > (bs->fifo_size + BCM63XX_SPI_MAX_PREPEND)) ||
365b17de076SJonas Gorski 		    (!can_use_prepend && total_len > bs->fifo_size)) {
366c0fde3baSJonas Gorski 			dev_err(&spi->dev, "unable to do transfers larger than FIFO size (%i > %i)\n",
367b17de076SJonas Gorski 				total_len, bs->fifo_size);
368b17de076SJonas Gorski 			status = -EINVAL;
369b17de076SJonas Gorski 			goto exit;
370b17de076SJonas Gorski 		}
371b17de076SJonas Gorski 
372b17de076SJonas Gorski 		/* all combined transfers have to have the same speed */
373b17de076SJonas Gorski 		if (t->speed_hz != first->speed_hz) {
374b17de076SJonas Gorski 			dev_err(&spi->dev, "unable to change speed between transfers\n");
375c0fde3baSJonas Gorski 			status = -EINVAL;
376cde4384eSFlorian Fainelli 			goto exit;
377cde4384eSFlorian Fainelli 		}
378cde4384eSFlorian Fainelli 
379c0fde3baSJonas Gorski 		/* CS will be deasserted directly after transfer */
380c0fde3baSJonas Gorski 		if (t->delay_usecs) {
381c0fde3baSJonas Gorski 			dev_err(&spi->dev, "unable to keep CS asserted after transfer\n");
382c0fde3baSJonas Gorski 			status = -EINVAL;
383c0fde3baSJonas Gorski 			goto exit;
384cde4384eSFlorian Fainelli 		}
385cde4384eSFlorian Fainelli 
386b17de076SJonas Gorski 		if (t->cs_change ||
387b17de076SJonas Gorski 		    list_is_last(&t->transfer_list, &m->transfers)) {
388c0fde3baSJonas Gorski 			/* configure adapter for a new transfer */
389b17de076SJonas Gorski 			bcm63xx_spi_setup_transfer(spi, first);
390c0fde3baSJonas Gorski 
391c0fde3baSJonas Gorski 			/* send the data */
392b17de076SJonas Gorski 			status = bcm63xx_txrx_bufs(spi, first, n_transfers);
393c0fde3baSJonas Gorski 			if (status)
394c0fde3baSJonas Gorski 				goto exit;
395c0fde3baSJonas Gorski 
396b17de076SJonas Gorski 			m->actual_length += total_len;
397b17de076SJonas Gorski 
398b17de076SJonas Gorski 			first = NULL;
399b17de076SJonas Gorski 			n_transfers = 0;
400b17de076SJonas Gorski 			total_len = 0;
401b17de076SJonas Gorski 			can_use_prepend = false;
402b17de076SJonas Gorski 		}
403cde4384eSFlorian Fainelli 	}
404cde4384eSFlorian Fainelli exit:
405cde4384eSFlorian Fainelli 	m->status = status;
406cde4384eSFlorian Fainelli 	spi_finalize_current_message(master);
407cde4384eSFlorian Fainelli 
408cde4384eSFlorian Fainelli 	return 0;
409b42dfed8SFlorian Fainelli }
410b42dfed8SFlorian Fainelli 
411b42dfed8SFlorian Fainelli /* This driver supports single master mode only. Hence
412b42dfed8SFlorian Fainelli  * CMD_DONE is the only interrupt we care about
413b42dfed8SFlorian Fainelli  */
414b42dfed8SFlorian Fainelli static irqreturn_t bcm63xx_spi_interrupt(int irq, void *dev_id)
415b42dfed8SFlorian Fainelli {
416b42dfed8SFlorian Fainelli 	struct spi_master *master = (struct spi_master *)dev_id;
417b42dfed8SFlorian Fainelli 	struct bcm63xx_spi *bs = spi_master_get_devdata(master);
418b42dfed8SFlorian Fainelli 	u8 intr;
419b42dfed8SFlorian Fainelli 
420b42dfed8SFlorian Fainelli 	/* Read interupts and clear them immediately */
421b42dfed8SFlorian Fainelli 	intr = bcm_spi_readb(bs, SPI_INT_STATUS);
422b42dfed8SFlorian Fainelli 	bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS);
423b42dfed8SFlorian Fainelli 	bcm_spi_writeb(bs, 0, SPI_INT_MASK);
424b42dfed8SFlorian Fainelli 
425cde4384eSFlorian Fainelli 	/* A transfer completed */
426cde4384eSFlorian Fainelli 	if (intr & SPI_INTR_CMD_DONE)
427b42dfed8SFlorian Fainelli 		complete(&bs->done);
428b42dfed8SFlorian Fainelli 
429b42dfed8SFlorian Fainelli 	return IRQ_HANDLED;
430b42dfed8SFlorian Fainelli }
431b42dfed8SFlorian Fainelli 
432ccd0657cSJonas Gorski static size_t bcm63xx_spi_max_length(struct spi_device *spi)
4330135c03dSJonas Gorski {
4340135c03dSJonas Gorski 	struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master);
4350135c03dSJonas Gorski 
4360135c03dSJonas Gorski 	return bs->fifo_size;
4370135c03dSJonas Gorski }
4380135c03dSJonas Gorski 
43944d8fb30SJonas Gorski static const unsigned long bcm6348_spi_reg_offsets[] = {
44044d8fb30SJonas Gorski 	[SPI_CMD]		= SPI_6348_CMD,
44144d8fb30SJonas Gorski 	[SPI_INT_STATUS]	= SPI_6348_INT_STATUS,
44244d8fb30SJonas Gorski 	[SPI_INT_MASK_ST]	= SPI_6348_INT_MASK_ST,
44344d8fb30SJonas Gorski 	[SPI_INT_MASK]		= SPI_6348_INT_MASK,
44444d8fb30SJonas Gorski 	[SPI_ST]		= SPI_6348_ST,
44544d8fb30SJonas Gorski 	[SPI_CLK_CFG]		= SPI_6348_CLK_CFG,
44644d8fb30SJonas Gorski 	[SPI_FILL_BYTE]		= SPI_6348_FILL_BYTE,
44744d8fb30SJonas Gorski 	[SPI_MSG_TAIL]		= SPI_6348_MSG_TAIL,
44844d8fb30SJonas Gorski 	[SPI_RX_TAIL]		= SPI_6348_RX_TAIL,
44944d8fb30SJonas Gorski 	[SPI_MSG_CTL]		= SPI_6348_MSG_CTL,
45044d8fb30SJonas Gorski 	[SPI_MSG_DATA]		= SPI_6348_MSG_DATA,
45144d8fb30SJonas Gorski 	[SPI_RX_DATA]		= SPI_6348_RX_DATA,
45244d8fb30SJonas Gorski 	[SPI_MSG_TYPE_SHIFT]	= SPI_6348_MSG_TYPE_SHIFT,
45344d8fb30SJonas Gorski 	[SPI_MSG_CTL_WIDTH]	= SPI_6348_MSG_CTL_WIDTH,
45444d8fb30SJonas Gorski 	[SPI_MSG_DATA_SIZE]	= SPI_6348_MSG_DATA_SIZE,
45544d8fb30SJonas Gorski };
45644d8fb30SJonas Gorski 
45744d8fb30SJonas Gorski static const unsigned long bcm6358_spi_reg_offsets[] = {
45844d8fb30SJonas Gorski 	[SPI_CMD]		= SPI_6358_CMD,
45944d8fb30SJonas Gorski 	[SPI_INT_STATUS]	= SPI_6358_INT_STATUS,
46044d8fb30SJonas Gorski 	[SPI_INT_MASK_ST]	= SPI_6358_INT_MASK_ST,
46144d8fb30SJonas Gorski 	[SPI_INT_MASK]		= SPI_6358_INT_MASK,
46244d8fb30SJonas Gorski 	[SPI_ST]		= SPI_6358_ST,
46344d8fb30SJonas Gorski 	[SPI_CLK_CFG]		= SPI_6358_CLK_CFG,
46444d8fb30SJonas Gorski 	[SPI_FILL_BYTE]		= SPI_6358_FILL_BYTE,
46544d8fb30SJonas Gorski 	[SPI_MSG_TAIL]		= SPI_6358_MSG_TAIL,
46644d8fb30SJonas Gorski 	[SPI_RX_TAIL]		= SPI_6358_RX_TAIL,
46744d8fb30SJonas Gorski 	[SPI_MSG_CTL]		= SPI_6358_MSG_CTL,
46844d8fb30SJonas Gorski 	[SPI_MSG_DATA]		= SPI_6358_MSG_DATA,
46944d8fb30SJonas Gorski 	[SPI_RX_DATA]		= SPI_6358_RX_DATA,
47044d8fb30SJonas Gorski 	[SPI_MSG_TYPE_SHIFT]	= SPI_6358_MSG_TYPE_SHIFT,
47144d8fb30SJonas Gorski 	[SPI_MSG_CTL_WIDTH]	= SPI_6358_MSG_CTL_WIDTH,
47244d8fb30SJonas Gorski 	[SPI_MSG_DATA_SIZE]	= SPI_6358_MSG_DATA_SIZE,
47344d8fb30SJonas Gorski };
47444d8fb30SJonas Gorski 
47544d8fb30SJonas Gorski static const struct platform_device_id bcm63xx_spi_dev_match[] = {
47644d8fb30SJonas Gorski 	{
47744d8fb30SJonas Gorski 		.name = "bcm6348-spi",
47844d8fb30SJonas Gorski 		.driver_data = (unsigned long)bcm6348_spi_reg_offsets,
47944d8fb30SJonas Gorski 	},
48044d8fb30SJonas Gorski 	{
48144d8fb30SJonas Gorski 		.name = "bcm6358-spi",
48244d8fb30SJonas Gorski 		.driver_data = (unsigned long)bcm6358_spi_reg_offsets,
48344d8fb30SJonas Gorski 	},
48444d8fb30SJonas Gorski 	{
48544d8fb30SJonas Gorski 	},
48644d8fb30SJonas Gorski };
487b42dfed8SFlorian Fainelli 
488c29f0889SJonas Gorski static const struct of_device_id bcm63xx_spi_of_match[] = {
489c29f0889SJonas Gorski 	{ .compatible = "brcm,bcm6348-spi", .data = &bcm6348_spi_reg_offsets },
490c29f0889SJonas Gorski 	{ .compatible = "brcm,bcm6358-spi", .data = &bcm6358_spi_reg_offsets },
491c29f0889SJonas Gorski 	{ },
492c29f0889SJonas Gorski };
493c29f0889SJonas Gorski 
494fd4a319bSGrant Likely static int bcm63xx_spi_probe(struct platform_device *pdev)
495b42dfed8SFlorian Fainelli {
496b42dfed8SFlorian Fainelli 	struct resource *r;
49744d8fb30SJonas Gorski 	const unsigned long *bcm63xx_spireg;
498b42dfed8SFlorian Fainelli 	struct device *dev = &pdev->dev;
499c29f0889SJonas Gorski 	int irq, bus_num;
500b42dfed8SFlorian Fainelli 	struct spi_master *master;
501b42dfed8SFlorian Fainelli 	struct clk *clk;
502b42dfed8SFlorian Fainelli 	struct bcm63xx_spi *bs;
503b42dfed8SFlorian Fainelli 	int ret;
504c29f0889SJonas Gorski 	u32 num_cs = BCM63XX_SPI_MAX_CS;
505b42dfed8SFlorian Fainelli 
506c29f0889SJonas Gorski 	if (dev->of_node) {
507c29f0889SJonas Gorski 		const struct of_device_id *match;
508c29f0889SJonas Gorski 
509c29f0889SJonas Gorski 		match = of_match_node(bcm63xx_spi_of_match, dev->of_node);
510c29f0889SJonas Gorski 		if (!match)
51144d8fb30SJonas Gorski 			return -EINVAL;
512c29f0889SJonas Gorski 		bcm63xx_spireg = match->data;
51344d8fb30SJonas Gorski 
514c29f0889SJonas Gorski 		of_property_read_u32(dev->of_node, "num-cs", &num_cs);
515c29f0889SJonas Gorski 		if (num_cs > BCM63XX_SPI_MAX_CS) {
516c29f0889SJonas Gorski 			dev_warn(dev, "unsupported number of cs (%i), reducing to 8\n",
517c29f0889SJonas Gorski 				 num_cs);
518c29f0889SJonas Gorski 			num_cs = BCM63XX_SPI_MAX_CS;
519c29f0889SJonas Gorski 		}
520c29f0889SJonas Gorski 
521c29f0889SJonas Gorski 		bus_num = -1;
522c29f0889SJonas Gorski 	} else if (pdev->id_entry->driver_data) {
523c29f0889SJonas Gorski 		const struct platform_device_id *match = pdev->id_entry;
524c29f0889SJonas Gorski 
525c29f0889SJonas Gorski 		bcm63xx_spireg = (const unsigned long *)match->driver_data;
526c29f0889SJonas Gorski 		bus_num = BCM63XX_SPI_BUS_NUM;
527c29f0889SJonas Gorski 	} else {
528c29f0889SJonas Gorski 		return -EINVAL;
529c29f0889SJonas Gorski 	}
53044d8fb30SJonas Gorski 
531b42dfed8SFlorian Fainelli 	irq = platform_get_irq(pdev, 0);
532b42dfed8SFlorian Fainelli 	if (irq < 0) {
533b42dfed8SFlorian Fainelli 		dev_err(dev, "no irq\n");
534acf4fc6fSJingoo Han 		return -ENXIO;
535b42dfed8SFlorian Fainelli 	}
536b42dfed8SFlorian Fainelli 
537acf4fc6fSJingoo Han 	clk = devm_clk_get(dev, "spi");
538b42dfed8SFlorian Fainelli 	if (IS_ERR(clk)) {
539b42dfed8SFlorian Fainelli 		dev_err(dev, "no clock for device\n");
540acf4fc6fSJingoo Han 		return PTR_ERR(clk);
541b42dfed8SFlorian Fainelli 	}
542b42dfed8SFlorian Fainelli 
543b42dfed8SFlorian Fainelli 	master = spi_alloc_master(dev, sizeof(*bs));
544b42dfed8SFlorian Fainelli 	if (!master) {
545b42dfed8SFlorian Fainelli 		dev_err(dev, "out of memory\n");
546acf4fc6fSJingoo Han 		return -ENOMEM;
547b42dfed8SFlorian Fainelli 	}
548b42dfed8SFlorian Fainelli 
549b42dfed8SFlorian Fainelli 	bs = spi_master_get_devdata(master);
550aa0fe826SAxel Lin 	init_completion(&bs->done);
551b42dfed8SFlorian Fainelli 
552b42dfed8SFlorian Fainelli 	platform_set_drvdata(pdev, master);
553b42dfed8SFlorian Fainelli 	bs->pdev = pdev;
554b42dfed8SFlorian Fainelli 
555de0fa83cSJulia Lawall 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
556b66c7730SJonas Gorski 	bs->regs = devm_ioremap_resource(&pdev->dev, r);
557b66c7730SJonas Gorski 	if (IS_ERR(bs->regs)) {
558b66c7730SJonas Gorski 		ret = PTR_ERR(bs->regs);
559b42dfed8SFlorian Fainelli 		goto out_err;
560b42dfed8SFlorian Fainelli 	}
561b42dfed8SFlorian Fainelli 
562b42dfed8SFlorian Fainelli 	bs->irq = irq;
563b42dfed8SFlorian Fainelli 	bs->clk = clk;
56444d8fb30SJonas Gorski 	bs->reg_offsets = bcm63xx_spireg;
56544d8fb30SJonas Gorski 	bs->fifo_size = bs->reg_offsets[SPI_MSG_DATA_SIZE];
566b42dfed8SFlorian Fainelli 
567b42dfed8SFlorian Fainelli 	ret = devm_request_irq(&pdev->dev, irq, bcm63xx_spi_interrupt, 0,
568b42dfed8SFlorian Fainelli 							pdev->name, master);
569b42dfed8SFlorian Fainelli 	if (ret) {
570b42dfed8SFlorian Fainelli 		dev_err(dev, "unable to request irq\n");
571b42dfed8SFlorian Fainelli 		goto out_err;
572b42dfed8SFlorian Fainelli 	}
573b42dfed8SFlorian Fainelli 
574c29f0889SJonas Gorski 	master->dev.of_node = dev->of_node;
575c29f0889SJonas Gorski 	master->bus_num = bus_num;
576c29f0889SJonas Gorski 	master->num_chipselect = num_cs;
577cde4384eSFlorian Fainelli 	master->transfer_one_message = bcm63xx_spi_transfer_one;
57888a3a255SFlorian Fainelli 	master->mode_bits = MODEBITS;
57924778be2SStephen Warren 	master->bits_per_word_mask = SPI_BPW_MASK(8);
5800135c03dSJonas Gorski 	master->max_transfer_size = bcm63xx_spi_max_length;
5810135c03dSJonas Gorski 	master->max_message_size = bcm63xx_spi_max_length;
5825355d96dSMark Brown 	master->auto_runtime_pm = true;
58344d8fb30SJonas Gorski 	bs->msg_type_shift = bs->reg_offsets[SPI_MSG_TYPE_SHIFT];
58444d8fb30SJonas Gorski 	bs->msg_ctl_width = bs->reg_offsets[SPI_MSG_CTL_WIDTH];
58544d8fb30SJonas Gorski 	bs->tx_io = (u8 *)(bs->regs + bs->reg_offsets[SPI_MSG_DATA]);
58644d8fb30SJonas Gorski 	bs->rx_io = (const u8 *)(bs->regs + bs->reg_offsets[SPI_RX_DATA]);
5875a670445SFlorian Fainelli 
588b42dfed8SFlorian Fainelli 	/* Initialize hardware */
589ea01e8a4SJonas Gorski 	ret = clk_prepare_enable(bs->clk);
590ea01e8a4SJonas Gorski 	if (ret)
591ea01e8a4SJonas Gorski 		goto out_err;
592ea01e8a4SJonas Gorski 
593b42dfed8SFlorian Fainelli 	bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS);
594b42dfed8SFlorian Fainelli 
595b42dfed8SFlorian Fainelli 	/* register and we are done */
596bca76931SJingoo Han 	ret = devm_spi_register_master(dev, master);
597b42dfed8SFlorian Fainelli 	if (ret) {
598b42dfed8SFlorian Fainelli 		dev_err(dev, "spi register failed\n");
599b42dfed8SFlorian Fainelli 		goto out_clk_disable;
600b42dfed8SFlorian Fainelli 	}
601b42dfed8SFlorian Fainelli 
6020ba2cf70SArnd Bergmann 	dev_info(dev, "at %pr (irq %d, FIFOs size %d)\n",
6030ba2cf70SArnd Bergmann 		 r, irq, bs->fifo_size);
604b42dfed8SFlorian Fainelli 
605b42dfed8SFlorian Fainelli 	return 0;
606b42dfed8SFlorian Fainelli 
607b42dfed8SFlorian Fainelli out_clk_disable:
6084fbb82a7SJonas Gorski 	clk_disable_unprepare(clk);
609b42dfed8SFlorian Fainelli out_err:
610b42dfed8SFlorian Fainelli 	spi_master_put(master);
611b42dfed8SFlorian Fainelli 	return ret;
612b42dfed8SFlorian Fainelli }
613b42dfed8SFlorian Fainelli 
614fd4a319bSGrant Likely static int bcm63xx_spi_remove(struct platform_device *pdev)
615b42dfed8SFlorian Fainelli {
6169637b86fSWei Yongjun 	struct spi_master *master = platform_get_drvdata(pdev);
617b42dfed8SFlorian Fainelli 	struct bcm63xx_spi *bs = spi_master_get_devdata(master);
618b42dfed8SFlorian Fainelli 
619b42dfed8SFlorian Fainelli 	/* reset spi block */
620b42dfed8SFlorian Fainelli 	bcm_spi_writeb(bs, 0, SPI_INT_MASK);
621b42dfed8SFlorian Fainelli 
622b42dfed8SFlorian Fainelli 	/* HW shutdown */
6234fbb82a7SJonas Gorski 	clk_disable_unprepare(bs->clk);
624b42dfed8SFlorian Fainelli 
625b42dfed8SFlorian Fainelli 	return 0;
626b42dfed8SFlorian Fainelli }
627b42dfed8SFlorian Fainelli 
6281bae2028SJonas Gorski #ifdef CONFIG_PM_SLEEP
629b42dfed8SFlorian Fainelli static int bcm63xx_spi_suspend(struct device *dev)
630b42dfed8SFlorian Fainelli {
631a1216394SAxel Lin 	struct spi_master *master = dev_get_drvdata(dev);
632b42dfed8SFlorian Fainelli 	struct bcm63xx_spi *bs = spi_master_get_devdata(master);
633b42dfed8SFlorian Fainelli 
63496519957SFlorian Fainelli 	spi_master_suspend(master);
63596519957SFlorian Fainelli 
6364fbb82a7SJonas Gorski 	clk_disable_unprepare(bs->clk);
637b42dfed8SFlorian Fainelli 
638b42dfed8SFlorian Fainelli 	return 0;
639b42dfed8SFlorian Fainelli }
640b42dfed8SFlorian Fainelli 
641b42dfed8SFlorian Fainelli static int bcm63xx_spi_resume(struct device *dev)
642b42dfed8SFlorian Fainelli {
643a1216394SAxel Lin 	struct spi_master *master = dev_get_drvdata(dev);
644b42dfed8SFlorian Fainelli 	struct bcm63xx_spi *bs = spi_master_get_devdata(master);
645ea01e8a4SJonas Gorski 	int ret;
646b42dfed8SFlorian Fainelli 
647ea01e8a4SJonas Gorski 	ret = clk_prepare_enable(bs->clk);
648ea01e8a4SJonas Gorski 	if (ret)
649ea01e8a4SJonas Gorski 		return ret;
650b42dfed8SFlorian Fainelli 
65196519957SFlorian Fainelli 	spi_master_resume(master);
65296519957SFlorian Fainelli 
653b42dfed8SFlorian Fainelli 	return 0;
654b42dfed8SFlorian Fainelli }
6551bae2028SJonas Gorski #endif
656b42dfed8SFlorian Fainelli 
657b42dfed8SFlorian Fainelli static const struct dev_pm_ops bcm63xx_spi_pm_ops = {
6581bae2028SJonas Gorski 	SET_SYSTEM_SLEEP_PM_OPS(bcm63xx_spi_suspend, bcm63xx_spi_resume)
659b42dfed8SFlorian Fainelli };
660b42dfed8SFlorian Fainelli 
661b42dfed8SFlorian Fainelli static struct platform_driver bcm63xx_spi_driver = {
662b42dfed8SFlorian Fainelli 	.driver = {
663b42dfed8SFlorian Fainelli 		.name	= "bcm63xx-spi",
6641bae2028SJonas Gorski 		.pm	= &bcm63xx_spi_pm_ops,
665c29f0889SJonas Gorski 		.of_match_table = bcm63xx_spi_of_match,
666b42dfed8SFlorian Fainelli 	},
66744d8fb30SJonas Gorski 	.id_table	= bcm63xx_spi_dev_match,
668b42dfed8SFlorian Fainelli 	.probe		= bcm63xx_spi_probe,
669fd4a319bSGrant Likely 	.remove		= bcm63xx_spi_remove,
670b42dfed8SFlorian Fainelli };
671b42dfed8SFlorian Fainelli 
672b42dfed8SFlorian Fainelli module_platform_driver(bcm63xx_spi_driver);
673b42dfed8SFlorian Fainelli 
674b42dfed8SFlorian Fainelli MODULE_ALIAS("platform:bcm63xx_spi");
675b42dfed8SFlorian Fainelli MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
676b42dfed8SFlorian Fainelli MODULE_AUTHOR("Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>");
677b42dfed8SFlorian Fainelli MODULE_DESCRIPTION("Broadcom BCM63xx SPI Controller driver");
678b42dfed8SFlorian Fainelli MODULE_LICENSE("GPL");
679