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