• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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