summaryrefslogtreecommitdiff
path: root/target/linux/bcm53xx/patches-3.14/180-spi-bcm53xx-driver-for-SPI-controller-on-Broadcom-bc.patch
blob: f79e7299959dc51a643a5bb3a2f3c803982564d2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
From a59f3fa1dd4cb284171a53cb7a614ad947c544f6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
Date: Wed, 13 Aug 2014 14:11:39 +0200
Subject: [PATCH V2] spi: bcm53xx: driver for SPI controller on Broadcom bcma
 SoC
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Broadcom 53xx ARM SoCs use bcma bus that contains various cores (AKA
devices). If board has a serial flash, it's connected over SPI and the
bcma bus includes a SPI controller. Example log from such a board:
bus0: Found chip with id 53010, rev 0x00 and package 0x02
(...)
bus0: Core 18 found: SPI flash controller (manuf 0x4BF, id 0x50A, rev 0x01, class 0x0)

This patch adds a bcma driver for SPI core, it registers SPI master
controller and "bcm53xxspiflash" SPI device.

Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
---
Since RFC: Add Signed-off-by
	   Update to compile on top of for-next
V2: Use wait timeout
    Describe bcm53xxspi_calc_timeout a bit
    Use devm_spi_register_master

Thanks Mark for your comments!
---
 drivers/spi/Kconfig       |   6 +
 drivers/spi/Makefile      |   1 +
 drivers/spi/spi-bcm53xx.c | 295 ++++++++++++++++++++++++++++++++++++++++++++++
 drivers/spi/spi-bcm53xx.h |  72 +++++++++++
 4 files changed, 374 insertions(+)
 create mode 100644 drivers/spi/spi-bcm53xx.c
 create mode 100644 drivers/spi/spi-bcm53xx.h

--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -112,6 +112,12 @@ config SPI_AU1550
 	  If you say yes to this option, support will be included for the
 	  PSC SPI controller found on Au1550, Au1200 and Au1300 series.
 
+config SPI_BCM53XX
+	tristate "Broadcom BCM53xx SPI controller"
+	depends on ARCH_BCM_5301X
+	help
+          Enable support for the SPI controller on Broadcom BCM53xx ARM SoCs.
+
 config SPI_BCM63XX
 	tristate "Broadcom BCM63xx SPI controller"
 	depends on BCM63XX
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_SPI_ATMEL)			+= spi-atmel.o
 obj-$(CONFIG_SPI_ATH79)			+= spi-ath79.o
 obj-$(CONFIG_SPI_AU1550)		+= spi-au1550.o
 obj-$(CONFIG_SPI_BCM2835)		+= spi-bcm2835.o
+obj-$(CONFIG_SPI_BCM53XX)		+= spi-bcm53xx.o
 obj-$(CONFIG_SPI_BCM63XX)		+= spi-bcm63xx.o
 obj-$(CONFIG_SPI_BCM63XX_HSSPI)		+= spi-bcm63xx-hsspi.o
 obj-$(CONFIG_SPI_BFIN5XX)		+= spi-bfin5xx.o
--- /dev/null
+++ b/drivers/spi/spi-bcm53xx.c
@@ -0,0 +1,295 @@
+#define pr_fmt(fmt)		KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/bcma/bcma.h>
+#include <linux/spi/spi.h>
+
+#include "spi-bcm53xx.h"
+
+#define BCM53XXSPI_MAX_SPI_BAUD	13500000	/* 216 MHz? */
+
+/* The longest observed required wait was 19 ms */
+#define BCM53XXSPI_SPE_TIMEOUT_MS	80
+
+struct bcm53xxspi {
+	struct bcma_device *core;
+	struct spi_master *master;
+
+	size_t read_offset;
+};
+
+static inline u32 bcm53xxspi_read(struct bcm53xxspi *b53spi, u16 offset)
+{
+	return bcma_read32(b53spi->core, offset);
+}
+
+static inline void bcm53xxspi_write(struct bcm53xxspi *b53spi, u16 offset,
+				    u32 value)
+{
+	bcma_write32(b53spi->core, offset, value);
+}
+
+static inline unsigned int bcm53xxspi_calc_timeout(size_t len)
+{
+	/* Do some magic calculation based on length and buad. Add 10% and 1. */
+	return (len * 9000 / BCM53XXSPI_MAX_SPI_BAUD * 110 / 100) + 1;
+}
+
+static int bcm53xxspi_wait(struct bcm53xxspi *b53spi, unsigned int timeout_ms)
+{
+	unsigned long deadline;
+	u32 tmp;
+
+	/* SPE bit has to be 0 before we read MSPI STATUS */
+	deadline = jiffies + BCM53XXSPI_SPE_TIMEOUT_MS * HZ / 1000;
+	do {
+		tmp = bcm53xxspi_read(b53spi, B53SPI_MSPI_SPCR2);
+		if (!(tmp & B53SPI_MSPI_SPCR2_SPE))
+			break;
+		udelay(5);
+	} while (!time_after_eq(jiffies, deadline));
+
+	if (tmp & B53SPI_MSPI_SPCR2_SPE)
+		goto spi_timeout;
+
+	/* Check status */
+	deadline = jiffies + timeout_ms * HZ / 1000;
+	do {
+		tmp = bcm53xxspi_read(b53spi, B53SPI_MSPI_MSPI_STATUS);
+		if (tmp & B53SPI_MSPI_MSPI_STATUS_SPIF) {
+			bcm53xxspi_write(b53spi, B53SPI_MSPI_MSPI_STATUS, 0);
+			return 0;
+		}
+
+		cpu_relax();
+		udelay(100);
+	} while (!time_after_eq(jiffies, deadline));
+
+spi_timeout:
+	bcm53xxspi_write(b53spi, B53SPI_MSPI_MSPI_STATUS, 0);
+
+	pr_err("Timeout waiting for SPI to be ready!\n");
+
+	return -EBUSY;
+}
+
+static void bcm53xxspi_buf_write(struct bcm53xxspi *b53spi, u8 *w_buf,
+				 size_t len, bool cont)
+{
+	u32 tmp;
+	int i;
+
+	for (i = 0; i < len; i++) {
+		/* Transmit Register File MSB */
+		bcm53xxspi_write(b53spi, B53SPI_MSPI_TXRAM + 4 * (i * 2),
+				 (unsigned int)w_buf[i]);
+	}
+
+	for (i = 0; i < len; i++) {
+		tmp = B53SPI_CDRAM_CONT | B53SPI_CDRAM_PCS_DISABLE_ALL |
+		      B53SPI_CDRAM_PCS_DSCK;
+		if (!cont && i == len - 1)
+			tmp &= ~B53SPI_CDRAM_CONT;
+		tmp &= ~0x1;
+		/* Command Register File */
+		bcm53xxspi_write(b53spi, B53SPI_MSPI_CDRAM + 4 * i, tmp);
+	}
+
+	/* Set queue pointers */
+	bcm53xxspi_write(b53spi, B53SPI_MSPI_NEWQP, 0);
+	bcm53xxspi_write(b53spi, B53SPI_MSPI_ENDQP, len - 1);
+
+	if (cont)
+		bcm53xxspi_write(b53spi, B53SPI_MSPI_WRITE_LOCK, 1);
+
+	/* Start SPI transfer */
+	tmp = bcm53xxspi_read(b53spi, B53SPI_MSPI_SPCR2);
+	tmp |= B53SPI_MSPI_SPCR2_SPE;
+	if (cont)
+		tmp |= B53SPI_MSPI_SPCR2_CONT_AFTER_CMD;
+	bcm53xxspi_write(b53spi, B53SPI_MSPI_SPCR2, tmp);
+
+	/* Wait for SPI to finish */
+	bcm53xxspi_wait(b53spi, bcm53xxspi_calc_timeout(len));
+
+	if (!cont)
+		bcm53xxspi_write(b53spi, B53SPI_MSPI_WRITE_LOCK, 0);
+
+	b53spi->read_offset = len;
+}
+
+static void bcm53xxspi_buf_read(struct bcm53xxspi *b53spi, u8 *r_buf,
+				size_t len, bool cont)
+{
+	u32 tmp;
+	int i;
+
+	for (i = 0; i < b53spi->read_offset + len; i++) {
+		tmp = B53SPI_CDRAM_CONT | B53SPI_CDRAM_PCS_DISABLE_ALL |
+		      B53SPI_CDRAM_PCS_DSCK;
+		if (!cont && i == b53spi->read_offset + len - 1)
+			tmp &= ~B53SPI_CDRAM_CONT;
+		tmp &= ~0x1;
+		/* Command Register File */
+		bcm53xxspi_write(b53spi, B53SPI_MSPI_CDRAM + 4 * i, tmp);
+	}
+
+	/* Set queue pointers */
+	bcm53xxspi_write(b53spi, B53SPI_MSPI_NEWQP, 0);
+	bcm53xxspi_write(b53spi, B53SPI_MSPI_ENDQP,
+			 b53spi->read_offset + len - 1);
+
+	if (cont)
+		bcm53xxspi_write(b53spi, B53SPI_MSPI_WRITE_LOCK, 1);
+
+	/* Start SPI transfer */
+	tmp = bcm53xxspi_read(b53spi, B53SPI_MSPI_SPCR2);
+	tmp |= B53SPI_MSPI_SPCR2_SPE;
+	if (cont)
+		tmp |= B53SPI_MSPI_SPCR2_CONT_AFTER_CMD;
+	bcm53xxspi_write(b53spi, B53SPI_MSPI_SPCR2, tmp);
+
+	/* Wait for SPI to finish */
+	bcm53xxspi_wait(b53spi, bcm53xxspi_calc_timeout(len));
+
+	if (!cont)
+		bcm53xxspi_write(b53spi, B53SPI_MSPI_WRITE_LOCK, 0);
+
+	for (i = 0; i < len; ++i) {
+		int offset = b53spi->read_offset + i;
+
+		/* Data stored in the transmit register file LSB */
+		r_buf[i] = (u8)bcm53xxspi_read(b53spi, B53SPI_MSPI_RXRAM + 4 * (1 + offset * 2));
+	}
+
+	b53spi->read_offset = 0;
+}
+
+static int bcm53xxspi_transfer_one(struct spi_master *master,
+				   struct spi_device *spi,
+				   struct spi_transfer *t)
+{
+	struct bcm53xxspi *b53spi = spi_master_get_devdata(master);
+	u8 *buf;
+	size_t left;
+
+	if (t->tx_buf) {
+		buf = (u8 *)t->tx_buf;
+		left = t->len;
+		while (left) {
+			size_t to_write = min_t(size_t, 16, left);
+			bool cont = left - to_write > 0;
+
+			bcm53xxspi_buf_write(b53spi, buf, to_write, cont);
+			left -= to_write;
+			buf += to_write;
+		}
+	}
+
+	if (t->rx_buf) {
+		buf = (u8 *)t->rx_buf;
+		left = t->len;
+		while (left) {
+			size_t to_read = min_t(size_t, 16 - b53spi->read_offset,
+					       left);
+			bool cont = left - to_read > 0;
+
+			bcm53xxspi_buf_read(b53spi, buf, to_read, cont);
+			left -= to_read;
+			buf += to_read;
+		}
+	}
+
+	return 0;
+}
+
+/**************************************************
+ * BCMA
+ **************************************************/
+
+struct spi_board_info bcm53xx_info = {
+	.modalias	= "bcm53xxspiflash",
+};
+
+static const struct bcma_device_id bcm53xxspi_bcma_tbl[] = {
+	BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_NS_QSPI, BCMA_ANY_REV, BCMA_ANY_CLASS),
+	BCMA_CORETABLE_END
+};
+MODULE_DEVICE_TABLE(bcma, bcm53xxspi_bcma_tbl);
+
+static int bcm53xxspi_bcma_probe(struct bcma_device *core)
+{
+	struct bcm53xxspi *b53spi;
+	struct spi_master *master;
+	int err;
+
+	if (core->bus->drv_cc.core->id.rev != 42) {
+		pr_err("SPI on SoC with unsupported ChipCommon rev\n");
+		return -ENOTSUPP;
+	}
+
+	master = spi_alloc_master(&core->dev, sizeof(*b53spi));
+	if (!master)
+		return -ENOMEM;
+
+	b53spi = spi_master_get_devdata(master);
+	b53spi->master = master;
+	b53spi->core = core;
+
+	master->transfer_one = bcm53xxspi_transfer_one;
+
+	bcma_set_drvdata(core, b53spi);
+
+	err = devm_spi_register_master(&core->dev, master);
+	if (err) {
+		spi_master_put(master);
+		bcma_set_drvdata(core, NULL);
+		goto out;
+	}
+
+	/* Broadcom SoCs (at least with the CC rev 42) use SPI for flash only */
+	spi_new_device(master, &bcm53xx_info);
+
+out:
+	return err;
+}
+
+static void bcm53xxspi_bcma_remove(struct bcma_device *core)
+{
+	struct bcm53xxspi *b53spi = bcma_get_drvdata(core);
+
+	spi_unregister_master(b53spi->master);
+}
+
+static struct bcma_driver bcm53xxspi_bcma_driver = {
+	.name		= KBUILD_MODNAME,
+	.id_table	= bcm53xxspi_bcma_tbl,
+	.probe		= bcm53xxspi_bcma_probe,
+	.remove		= bcm53xxspi_bcma_remove,
+};
+
+/**************************************************
+ * Init & exit
+ **************************************************/
+
+static int __init bcm53xxspi_module_init(void)
+{
+	int err = 0;
+
+	err = bcma_driver_register(&bcm53xxspi_bcma_driver);
+	if (err)
+		pr_err("Failed to register bcma driver: %d\n", err);
+
+	return err;
+}
+
+static void __exit bcm53xxspi_module_exit(void)
+{
+	bcma_driver_unregister(&bcm53xxspi_bcma_driver);
+}
+
+module_init(bcm53xxspi_module_init);
+module_exit(bcm53xxspi_module_exit);
--- /dev/null
+++ b/drivers/spi/spi-bcm53xx.h
@@ -0,0 +1,72 @@
+#ifndef SPI_BCM53XX_H
+#define SPI_BCM53XX_H
+
+#define B53SPI_BSPI_REVISION_ID			0x000
+#define B53SPI_BSPI_SCRATCH			0x004
+#define B53SPI_BSPI_MAST_N_BOOT_CTRL		0x008
+#define B53SPI_BSPI_BUSY_STATUS			0x00c
+#define B53SPI_BSPI_INTR_STATUS			0x010
+#define B53SPI_BSPI_B0_STATUS			0x014
+#define B53SPI_BSPI_B0_CTRL			0x018
+#define B53SPI_BSPI_B1_STATUS			0x01c
+#define B53SPI_BSPI_B1_CTRL			0x020
+#define B53SPI_BSPI_STRAP_OVERRIDE_CTRL		0x024
+#define B53SPI_BSPI_FLEX_MODE_ENABLE		0x028
+#define B53SPI_BSPI_BITS_PER_CYCLE		0x02c
+#define B53SPI_BSPI_BITS_PER_PHASE		0x030
+#define B53SPI_BSPI_CMD_AND_MODE_BYTE		0x034
+#define B53SPI_BSPI_BSPI_FLASH_UPPER_ADDR_BYTE	0x038
+#define B53SPI_BSPI_BSPI_XOR_VALUE		0x03c
+#define B53SPI_BSPI_BSPI_XOR_ENABLE		0x040
+#define B53SPI_BSPI_BSPI_PIO_MODE_ENABLE	0x044
+#define B53SPI_BSPI_BSPI_PIO_IODIR		0x048
+#define B53SPI_BSPI_BSPI_PIO_DATA		0x04c
+
+/* RAF */
+#define B53SPI_RAF_START_ADDR			0x100
+#define B53SPI_RAF_NUM_WORDS			0x104
+#define B53SPI_RAF_CTRL				0x108
+#define B53SPI_RAF_FULLNESS			0x10c
+#define B53SPI_RAF_WATERMARK			0x110
+#define B53SPI_RAF_STATUS			0x114
+#define B53SPI_RAF_READ_DATA			0x118
+#define B53SPI_RAF_WORD_CNT			0x11c
+#define B53SPI_RAF_CURR_ADDR			0x120
+
+/* MSPI */
+#define B53SPI_MSPI_SPCR0_LSB			0x200
+#define B53SPI_MSPI_SPCR0_MSB			0x204
+#define B53SPI_MSPI_SPCR1_LSB			0x208
+#define B53SPI_MSPI_SPCR1_MSB			0x20c
+#define B53SPI_MSPI_NEWQP			0x210
+#define B53SPI_MSPI_ENDQP			0x214
+#define B53SPI_MSPI_SPCR2			0x218
+#define  B53SPI_MSPI_SPCR2_SPE			0x00000040
+#define  B53SPI_MSPI_SPCR2_CONT_AFTER_CMD	0x00000080
+#define B53SPI_MSPI_MSPI_STATUS			0x220
+#define  B53SPI_MSPI_MSPI_STATUS_SPIF		0x00000001
+#define B53SPI_MSPI_CPTQP			0x224
+#define B53SPI_MSPI_TXRAM			0x240 /* 32 registers, up to 0x2b8 */
+#define B53SPI_MSPI_RXRAM			0x2c0 /* 32 registers, up to 0x33c */
+#define B53SPI_MSPI_CDRAM			0x340 /* 16 registers, up to 0x37c */
+#define  B53SPI_CDRAM_PCS_PCS0			0x00000001
+#define  B53SPI_CDRAM_PCS_PCS1			0x00000002
+#define  B53SPI_CDRAM_PCS_PCS2			0x00000004
+#define  B53SPI_CDRAM_PCS_PCS3			0x00000008
+#define  B53SPI_CDRAM_PCS_DISABLE_ALL		0x0000000f
+#define  B53SPI_CDRAM_PCS_DSCK			0x00000010
+#define  B53SPI_CDRAM_BITSE			0x00000040
+#define  B53SPI_CDRAM_CONT			0x00000080
+#define B53SPI_MSPI_WRITE_LOCK			0x380
+#define B53SPI_MSPI_DISABLE_FLUSH_GEN		0x384
+
+/* Interrupt */
+#define B53SPI_INTR_RAF_LR_FULLNESS_REACHED	0x3a0
+#define B53SPI_INTR_RAF_LR_TRUNCATED		0x3a4
+#define B53SPI_INTR_RAF_LR_IMPATIENT		0x3a8
+#define B53SPI_INTR_RAF_LR_SESSION_DONE		0x3ac
+#define B53SPI_INTR_RAF_LR_OVERREAD		0x3b0
+#define B53SPI_INTR_MSPI_DONE			0x3b4
+#define B53SPI_INTR_MSPI_HALT_SET_TRANSACTION_DONE	0x3b8
+
+#endif /* SPI_BCM53XX_H */