1 /*
2 * Broadcom SPI Host Controller Driver - Linux Per-port
3 *
4 * Copyright (C) 1999-2019, Broadcom.
5 *
6 * Unless you and Broadcom execute a separate written software license
7 * agreement governing use of this software, this software is licensed to you
8 * under the terms of the GNU General Public License version 2 (the "GPL"),
9 * available at http://www.broadcom.com/licenses/GPLv2.php, with the
10 * following added to such license:
11 *
12 * As a special exception, the copyright holders of this software give you
13 * permission to link this software with independent modules, and to copy and
14 * distribute the resulting executable under terms of your choice, provided that
15 * you also meet, for each linked independent module, the terms and conditions
16 * of the license of that module. An independent module is a module which is
17 * not derived from this software. The special exception does not apply to any
18 * modifications of the software.
19 *
20 * Notwithstanding the above, under no circumstances may you combine this
21 * software in any way with any other Broadcom software provided under a license
22 * other than the GPL, without Broadcom's express prior written consent.
23 *
24 *
25 * <<Broadcom-WL-IPTag/Open:>>
26 *
27 * $Id: bcmsdspi_linux.c 514727 2014-11-12 03:02:48Z $
28 */
29
30 #include <typedefs.h>
31 #include <bcmutils.h>
32
33 #include <bcmsdbus.h> /* bcmsdh to/from specific controller APIs */
34 #include <sdiovar.h> /* to get msglevel bit values */
35
36 #ifdef BCMSPI_ANDROID
37 #include <bcmsdh.h>
38 #include <bcmspibrcm.h>
39 #include <linux/spi/spi.h>
40 #else
41 #include <pcicfg.h>
42 #include <sdio.h> /* SDIO Device and Protocol Specs */
43 #include <linux/sched.h> /* request_irq(), free_irq() */
44 #include <bcmsdspi.h>
45 #include <bcmspi.h>
46 #endif /* BCMSPI_ANDROID */
47
48 #ifndef BCMSPI_ANDROID
49 extern uint sd_crc;
50 module_param(sd_crc, uint, 0);
51
52 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
53 #define KERNEL26
54 #endif // endif
55 #endif /* !BCMSPI_ANDROID */
56
57 struct sdos_info {
58 sdioh_info_t *sd;
59 spinlock_t lock;
60 #ifndef BCMSPI_ANDROID
61 wait_queue_head_t intr_wait_queue;
62 #endif /* !BCMSPI_ANDROID */
63 };
64
65 #ifndef BCMSPI_ANDROID
66 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
67 #define BLOCKABLE() (!in_atomic())
68 #else
69 #define BLOCKABLE() (!in_interrupt())
70 #endif // endif
71
72 /* Interrupt handler */
sdspi_isr(int irq,void * dev_id,struct pt_regs * ptregs)73 static irqreturn_t sdspi_isr(int irq, void *dev_id
74 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
75 ,
76 struct pt_regs *ptregs
77 #endif // endif
78 )
79 {
80 sdioh_info_t *sd;
81 struct sdos_info *sdos;
82 bool ours;
83
84 sd = (sdioh_info_t *)dev_id;
85 sd->local_intrcount++;
86
87 if (!sd->card_init_done) {
88 sd_err(("%s: Hey Bogus intr...not even initted: irq %d\n", __FUNCTION__,
89 irq));
90 return IRQ_RETVAL(FALSE);
91 } else {
92 ours = spi_check_client_intr(sd, NULL);
93 /* For local interrupts, wake the waiting process */
94 if (ours && sd->got_hcint) {
95 sdos = (struct sdos_info *)sd->sdos_info;
96 wake_up_interruptible(&sdos->intr_wait_queue);
97 }
98
99 return IRQ_RETVAL(ours);
100 }
101 }
102 #endif /* !BCMSPI_ANDROID */
103
104 #ifdef BCMSPI_ANDROID
105 static struct spi_device *gBCMSPI = NULL;
106
107 extern int bcmsdh_probe(struct device *dev);
108 extern int bcmsdh_remove(struct device *dev);
109
bcmsdh_spi_probe(struct spi_device * spi_dev)110 static int bcmsdh_spi_probe(struct spi_device *spi_dev)
111 {
112 int ret = 0;
113
114 gBCMSPI = spi_dev;
115
116 #ifdef SPI_PIO_32BIT_RW
117 spi_dev->bits_per_word = 32;
118 #else
119 spi_dev->bits_per_word = 0x8;
120 #endif /* SPI_PIO_32BIT_RW */
121 ret = spi_setup(spi_dev);
122 if (ret) {
123 sd_err(("bcmsdh_spi_probe: spi_setup fail with %d\n", ret));
124 }
125 sd_err(("bcmsdh_spi_probe: spi_setup with %d, bits_per_word=%d\n", ret,
126 spi_dev->bits_per_word));
127 ret = bcmsdh_probe(&spi_dev->dev);
128
129 return ret;
130 }
131
bcmsdh_spi_remove(struct spi_device * spi_dev)132 static int bcmsdh_spi_remove(struct spi_device *spi_dev)
133 {
134 int ret = 0;
135
136 ret = bcmsdh_remove(&spi_dev->dev);
137 gBCMSPI = NULL;
138
139 return ret;
140 }
141
142 static struct spi_driver bcmsdh_spi_driver = {
143 .probe = bcmsdh_spi_probe,
144 .remove = bcmsdh_spi_remove,
145 .driver =
146 {
147 .name = "wlan_spi",
148 .bus = &spi_bus_type,
149 .owner = THIS_MODULE,
150 },
151 };
152
153 /*
154 * module init
155 */
bcmsdh_register_client_driver(void)156 int bcmsdh_register_client_driver(void)
157 {
158 int error = 0;
159 sd_trace(("bcmsdh_gspi: %s Enter\n", __FUNCTION__));
160
161 error = spi_register_driver(&bcmsdh_spi_driver);
162
163 return error;
164 }
165
166 /*
167 * module cleanup
168 */
bcmsdh_unregister_client_driver(void)169 void bcmsdh_unregister_client_driver(void)
170 {
171 sd_trace(("%s Enter\n", __FUNCTION__));
172 spi_unregister_driver(&bcmsdh_spi_driver);
173 }
174 #endif /* BCMSPI_ANDROID */
175
176 /* Register with Linux for interrupts */
spi_register_irq(sdioh_info_t * sd,uint irq)177 int spi_register_irq(sdioh_info_t *sd, uint irq)
178 {
179 #ifndef BCMSPI_ANDROID
180 sd_trace(("Entering %s: irq == %d\n", __FUNCTION__, irq));
181 if (request_irq(irq, sdspi_isr, IRQF_SHARED, "bcmsdspi", sd) < 0) {
182 sd_err(("%s: request_irq() failed\n", __FUNCTION__));
183 return ERROR;
184 }
185 #endif /* !BCMSPI_ANDROID */
186 return SUCCESS;
187 }
188
189 /* Free Linux irq */
spi_free_irq(uint irq,sdioh_info_t * sd)190 void spi_free_irq(uint irq, sdioh_info_t *sd)
191 {
192 #ifndef BCMSPI_ANDROID
193 free_irq(irq, sd);
194 #endif /* !BCMSPI_ANDROID */
195 }
196
197 /* Map Host controller registers */
198 #ifndef BCMSPI_ANDROID
spi_reg_map(osl_t * osh,uintptr addr,int size)199 uint32 *spi_reg_map(osl_t *osh, uintptr addr, int size)
200 {
201 return (uint32 *)REG_MAP(addr, size);
202 }
203
spi_reg_unmap(osl_t * osh,uintptr addr,int size)204 void spi_reg_unmap(osl_t *osh, uintptr addr, int size)
205 {
206 REG_UNMAP((void *)(uintptr)addr);
207 }
208 #endif /* !BCMSPI_ANDROID */
209
spi_osinit(sdioh_info_t * sd)210 int spi_osinit(sdioh_info_t *sd)
211 {
212 struct sdos_info *sdos;
213
214 sdos = (struct sdos_info *)MALLOC(sd->osh, sizeof(struct sdos_info));
215 sd->sdos_info = (void *)sdos;
216 if (sdos == NULL) {
217 return BCME_NOMEM;
218 }
219
220 sdos->sd = sd;
221 spin_lock_init(&sdos->lock);
222 #ifndef BCMSPI_ANDROID
223 init_waitqueue_head(&sdos->intr_wait_queue);
224 #endif /* !BCMSPI_ANDROID */
225 return BCME_OK;
226 }
227
spi_osfree(sdioh_info_t * sd)228 void spi_osfree(sdioh_info_t *sd)
229 {
230 struct sdos_info *sdos;
231 ASSERT(sd && sd->sdos_info);
232
233 sdos = (struct sdos_info *)sd->sdos_info;
234 MFREE(sd->osh, sdos, sizeof(struct sdos_info));
235 }
236
237 /* Interrupt enable/disable */
238 SDIOH_API_RC
sdioh_interrupt_set(sdioh_info_t * sd,bool enable)239 sdioh_interrupt_set(sdioh_info_t *sd, bool enable)
240 {
241 ulong flags;
242 struct sdos_info *sdos;
243
244 sd_trace(("%s: %s\n", __FUNCTION__, enable ? "Enabling" : "Disabling"));
245
246 sdos = (struct sdos_info *)sd->sdos_info;
247 ASSERT(sdos);
248
249 if (!(sd->host_init_done && sd->card_init_done)) {
250 sd_err(("%s: Card & Host are not initted - bailing\n", __FUNCTION__));
251 return SDIOH_API_RC_FAIL;
252 }
253
254 #ifndef BCMSPI_ANDROID
255 if (enable && !(sd->intr_handler && sd->intr_handler_arg)) {
256 sd_err(("%s: no handler registered, will not enable\n", __FUNCTION__));
257 return SDIOH_API_RC_FAIL;
258 }
259 #endif /* !BCMSPI_ANDROID */
260
261 /* Ensure atomicity for enable/disable calls */
262 spin_lock_irqsave(&sdos->lock, flags);
263
264 sd->client_intr_enabled = enable;
265 #ifndef BCMSPI_ANDROID
266 if (enable && !sd->lockcount) {
267 spi_devintr_on(sd);
268 } else {
269 spi_devintr_off(sd);
270 }
271 #endif /* !BCMSPI_ANDROID */
272
273 spin_unlock_irqrestore(&sdos->lock, flags);
274
275 return SDIOH_API_RC_SUCCESS;
276 }
277
278 /* Protect against reentrancy (disable device interrupts while executing) */
spi_lock(sdioh_info_t * sd)279 void spi_lock(sdioh_info_t *sd)
280 {
281 ulong flags;
282 struct sdos_info *sdos;
283
284 sdos = (struct sdos_info *)sd->sdos_info;
285 ASSERT(sdos);
286
287 sd_trace(("%s: %d\n", __FUNCTION__, sd->lockcount));
288
289 spin_lock_irqsave(&sdos->lock, flags);
290 if (sd->lockcount) {
291 sd_err(("%s: Already locked!\n", __FUNCTION__));
292 ASSERT(sd->lockcount == 0);
293 }
294 #ifdef BCMSPI_ANDROID
295 if (sd->client_intr_enabled) {
296 bcmsdh_oob_intr_set(0);
297 }
298 #else
299 spi_devintr_off(sd);
300 #endif /* BCMSPI_ANDROID */
301 sd->lockcount++;
302 spin_unlock_irqrestore(&sdos->lock, flags);
303 }
304
305 /* Enable client interrupt */
spi_unlock(sdioh_info_t * sd)306 void spi_unlock(sdioh_info_t *sd)
307 {
308 ulong flags;
309 struct sdos_info *sdos;
310
311 sd_trace(
312 ("%s: %d, %d\n", __FUNCTION__, sd->lockcount, sd->client_intr_enabled));
313 ASSERT(sd->lockcount > 0);
314
315 sdos = (struct sdos_info *)sd->sdos_info;
316 ASSERT(sdos);
317
318 spin_lock_irqsave(&sdos->lock, flags);
319 if (--sd->lockcount == 0 && sd->client_intr_enabled) {
320 #ifdef BCMSPI_ANDROID
321 bcmsdh_oob_intr_set(1);
322 #else
323 spi_devintr_on(sd);
324 #endif /* BCMSPI_ANDROID */
325 }
326 spin_unlock_irqrestore(&sdos->lock, flags);
327 }
328
329 #ifndef BCMSPI_ANDROID
spi_waitbits(sdioh_info_t * sd,bool yield)330 void spi_waitbits(sdioh_info_t *sd, bool yield)
331 {
332 #ifndef BCMSDYIELD
333 ASSERT(!yield);
334 #endif // endif
335 sd_trace(("%s: yield %d canblock %d\n", __FUNCTION__, yield, BLOCKABLE()));
336
337 /* Clear the "interrupt happened" flag and last intrstatus */
338 sd->got_hcint = FALSE;
339
340 #ifdef BCMSDYIELD
341 if (yield && BLOCKABLE()) {
342 struct sdos_info *sdos;
343 sdos = (struct sdos_info *)sd->sdos_info;
344 /* Wait for the indication, the interrupt will be masked when the ISR
345 * fires. */
346 wait_event_interruptible(sdos->intr_wait_queue, (sd->got_hcint));
347 } else
348 #endif /* BCMSDYIELD */
349 {
350 spi_spinbits(sd);
351 }
352 }
353 #else /* !BCMSPI_ANDROID */
354 int bcmgspi_dump =
355 0; /* Set to dump complete trace of all SPI bus transactions */
356
hexdump(char * pfx,unsigned char * msg,int msglen)357 static void hexdump(char *pfx, unsigned char *msg, int msglen)
358 {
359 int i, col;
360 char buf[80];
361
362 ASSERT(strlen(pfx) + 0x31 <= sizeof(buf));
363
364 col = 0;
365
366 for (i = 0; i < msglen; i++, col++) {
367 if (col % 0x10 == 0) {
368 strcpy(buf, pfx);
369 }
370 sprintf(buf + strlen(buf), "%02x", msg[i]);
371 if ((col + 1) % 0x10 == 0) {
372 printf("%s\n", buf);
373 } else {
374 sprintf(buf + strlen(buf), " ");
375 }
376 }
377
378 if (col % 0x10 != 0) {
379 printf("%s\n", buf);
380 }
381 }
382
383 /* Send/Receive an SPI Packet */
spi_sendrecv(sdioh_info_t * sd,uint8 * msg_out,uint8 * msg_in,int msglen)384 void spi_sendrecv(sdioh_info_t *sd, uint8 *msg_out, uint8 *msg_in, int msglen)
385 {
386 int write = 0;
387 int tx_len = 0;
388 struct spi_message msg;
389 struct spi_transfer t[0x2];
390
391 spi_message_init(&msg);
392 memset(t, 0, 0x2 * sizeof(struct spi_transfer));
393
394 if (sd->wordlen == 0x2)
395 #if !(defined(SPI_PIO_RW_BIGENDIAN) && defined(SPI_PIO_32BIT_RW))
396 write = msg_out[0x2] & 0x80;
397 #else
398 write = msg_out[1] & 0x80;
399 #endif /* !(defined(SPI_PIO_RW_BIGENDIAN) && defined(SPI_PIO_32BIT_RW)) */
400 if (sd->wordlen == 0x4)
401 #if !(defined(SPI_PIO_RW_BIGENDIAN) && defined(SPI_PIO_32BIT_RW))
402 write = msg_out[0] & 0x80;
403 #else
404 write = msg_out[0x3] & 0x80;
405 #endif /* !(defined(SPI_PIO_RW_BIGENDIAN) && defined(SPI_PIO_32BIT_RW)) */
406
407 if (bcmgspi_dump) {
408 hexdump(" OUT: ", msg_out, msglen);
409 }
410
411 tx_len = write ? msglen - 0x4 : 0x4;
412
413 sd_trace(
414 ("spi_sendrecv: %s, wordlen %d, cmd : 0x%02x 0x%02x 0x%02x 0x%02x\n",
415 write ? "WR" : "RD", sd->wordlen, msg_out[0], msg_out[1], msg_out[0x2],
416 msg_out[0x3]));
417
418 t[0].tx_buf = (char *)&msg_out[0];
419 t[0].rx_buf = 0;
420 t[0].len = tx_len;
421
422 spi_message_add_tail(&t[0], &msg);
423
424 t[1].rx_buf = (char *)&msg_in[tx_len];
425 t[1].tx_buf = 0;
426 t[1].len = msglen - tx_len;
427
428 spi_message_add_tail(&t[1], &msg);
429 spi_sync(gBCMSPI, &msg);
430
431 if (bcmgspi_dump) {
432 hexdump(" IN : ", msg_in, msglen);
433 }
434 }
435 #endif /* !BCMSPI_ANDROID */
436