xref: /OK3568_Linux_fs/kernel/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/bcmsdstd_linux.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 /*
2  *  'Standard' SDIO HOST CONTROLLER driver - linux portion
3  *
4  * Copyright (C) 2020, 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 of
16  * the license of that module.  An independent module is a module which is not
17  * derived from this software.  The special exception does not apply to any
18  * modifications of the software.
19  *
20  *
21  * <<Broadcom-WL-IPTag/Open:>>
22  *
23  * $Id$
24  */
25 
26 #include <linux/sched.h>	/* request_irq() */
27 #include <typedefs.h>
28 #include <pcicfg.h>
29 #include <bcmutils.h>
30 #include <sdio.h>	/* SDIO Device and Protocol Specs */
31 #include <sdioh.h> /* SDIO Host Controller Spec header file */
32 #include <bcmsdbus.h>	/* bcmsdh to/from specific controller APIs */
33 #include <sdiovar.h>	/* to get msglevel bit values */
34 #include <bcmsdstd.h>
35 #include <bcmdevs.h>
36 
37 extern void* bcmsdh_probe(osl_t *osh, void *dev, void *sdioh, void *adapter_info, uint bus_type,
38 	uint bus_num, uint slot_num);
39 extern int bcmsdh_remove(bcmsdh_info_t *bcmsdh);
40 
41 /* Extern functions for sdio power save */
42 extern uint8 sdstd_turn_on_clock(sdioh_info_t *sd);
43 extern uint8 sdstd_turn_off_clock(sdioh_info_t *sd);
44 /* Extern variable for sdio power save. This is enabled or disabled using the IOCTL call */
45 extern uint sd_3_power_save;
46 
47 struct sdos_info {
48 	sdioh_info_t *sd;
49 	spinlock_t lock;
50 	wait_queue_head_t intr_wait_queue;
51 	timer_list_compat_t tuning_timer;
52 	int tuning_timer_exp;
53 	atomic_t timer_enab;
54 	struct tasklet_struct tuning_tasklet;
55 };
56 
57 #define SDSTD_WAITBITS_TIMEOUT		(5 * HZ)	/* seconds * HZ */
58 
59 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
60 #define BLOCKABLE()	(!in_atomic())
61 #else
62 #define BLOCKABLE()	(!in_interrupt()) /* XXX Doesn't handle CONFIG_PREEMPT? */
63 #endif
64 
65 static void
66 sdstd_3_ostasklet(ulong data);
67 static void
68 sdstd_3_tuning_timer(ulong data);
69 
70 /* Interrupt handler */
71 static irqreturn_t
sdstd_isr(int irq,void * dev_id,struct pt_regs * ptregs)72 sdstd_isr(int irq, void *dev_id
73 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
74 , struct pt_regs *ptregs
75 #endif
76 )
77 {
78 	sdioh_info_t *sd;
79 	struct sdos_info *sdos;
80 	bool ours;
81 
82 	unsigned long flags;
83 	sd = (sdioh_info_t *)dev_id;
84 	sdos = (struct sdos_info *)sd->sdos_info;
85 
86 	if (!sd->card_init_done) {
87 		sd_err(("%s: Hey Bogus intr...not even initted: irq %d\n", __FUNCTION__, irq));
88 		return IRQ_RETVAL(FALSE);
89 	} else {
90 		if (sdstd_3_is_retuning_int_set(sd)) {
91 			/* for 3.0 host, retuning request might come in this path */
92 			/* * disable ISR's */
93 			local_irq_save(flags);
94 
95 			if (sdstd_3_check_and_set_retuning(sd))
96 				tasklet_schedule(&sdos->tuning_tasklet);
97 
98 			/* * enable back ISR's */
99 			local_irq_restore(flags);
100 
101 			/* * disable tuning isr signaling */
102 			sdstd_3_disable_retuning_int(sd);
103 			/* * note: check_client_intr() checks for  intmask also to
104 				wakeup. so be careful to use sd->intmask to disable
105 				re-tuning ISR.
106 				*/
107 		}
108 		ours = check_client_intr(sd);
109 
110 		/* For local interrupts, wake the waiting process */
111 		if (ours && sd->got_hcint) {
112 			sd_trace(("INTR->WAKE\n"));
113 /* 			sdos = (struct sdos_info *)sd->sdos_info; */
114 			wake_up_interruptible(&sdos->intr_wait_queue);
115 		}
116 		return IRQ_RETVAL(ours);
117 	}
118 }
119 
120 /* Register with Linux for interrupts */
121 int
sdstd_register_irq(sdioh_info_t * sd,uint irq)122 sdstd_register_irq(sdioh_info_t *sd, uint irq)
123 {
124 	sd_trace(("Entering %s: irq == %d\n", __FUNCTION__, irq));
125 	if (request_irq(irq, sdstd_isr, IRQF_SHARED, "bcmsdstd", sd) < 0) {
126 		sd_err(("%s: request_irq() failed\n", __FUNCTION__));
127 		return ERROR;
128 	}
129 	return SUCCESS;
130 }
131 
132 /* Free Linux irq */
133 void
sdstd_free_irq(uint irq,sdioh_info_t * sd)134 sdstd_free_irq(uint irq, sdioh_info_t *sd)
135 {
136 	free_irq(irq, sd);
137 }
138 
139 /* Map Host controller registers */
140 
141 uint32 *
sdstd_reg_map(osl_t * osh,dmaaddr_t addr,int size)142 sdstd_reg_map(osl_t *osh, dmaaddr_t addr, int size)
143 {
144 	return (uint32 *)REG_MAP(addr, size);
145 }
146 
147 void
sdstd_reg_unmap(osl_t * osh,dmaaddr_t addr,int size)148 sdstd_reg_unmap(osl_t *osh, dmaaddr_t addr, int size)
149 {
150 	REG_UNMAP((void*)(uintptr)addr);
151 }
152 
153 int
sdstd_osinit(sdioh_info_t * sd)154 sdstd_osinit(sdioh_info_t *sd)
155 {
156 	struct sdos_info *sdos;
157 
158 	sdos = (struct sdos_info*)MALLOC(sd->osh, sizeof(struct sdos_info));
159 	sd->sdos_info = (void*)sdos;
160 	if (sdos == NULL)
161 		return BCME_NOMEM;
162 
163 	sdos->sd = sd;
164 	spin_lock_init(&sdos->lock);
165 	atomic_set(&sdos->timer_enab, FALSE);
166 	init_waitqueue_head(&sdos->intr_wait_queue);
167 	return BCME_OK;
168 }
169 
170 /* initilize tuning related OS structures */
171 void
sdstd_3_osinit_tuning(sdioh_info_t * sd)172 sdstd_3_osinit_tuning(sdioh_info_t *sd)
173 {
174 	struct sdos_info *sdos = (struct sdos_info *)sd->sdos_info;
175 	uint8 timer_count = sdstd_3_get_tuning_exp(sdos->sd);
176 
177 	sd_trace(("%s Enter\n", __FUNCTION__));
178 
179 	init_timer_compat(&sdos->tuning_timer, sdstd_3_tuning_timer, sdos);
180 	if (timer_count == CAP3_RETUNING_TC_DISABLED || timer_count > CAP3_RETUNING_TC_1024S) {
181 		sdos->tuning_timer_exp = 0;
182 	} else {
183 		sdos->tuning_timer_exp = 1 << (timer_count - 1);
184 	}
185 	tasklet_init(&sdos->tuning_tasklet, sdstd_3_ostasklet, (ulong)sdos);
186 	if (sdos->tuning_timer_exp) {
187 		timer_expires(&sdos->tuning_timer) = jiffies + sdos->tuning_timer_exp * HZ;
188 		add_timer(&sdos->tuning_timer);
189 		atomic_set(&sdos->timer_enab, TRUE);
190 	}
191 }
192 
193 /* finalize tuning related OS structures */
194 void
sdstd_3_osclean_tuning(sdioh_info_t * sd)195 sdstd_3_osclean_tuning(sdioh_info_t *sd)
196 {
197 	struct sdos_info *sdos = (struct sdos_info *)sd->sdos_info;
198 	if (atomic_read(&sdos->timer_enab) == TRUE) {
199 		/* disable timer if it was running */
200 		del_timer_sync(&sdos->tuning_timer);
201 		atomic_set(&sdos->timer_enab, FALSE);
202 	}
203 	tasklet_kill(&sdos->tuning_tasklet);
204 }
205 
206 static void
sdstd_3_ostasklet(ulong data)207 sdstd_3_ostasklet(ulong data)
208 {
209 	struct sdos_info *sdos = (struct sdos_info *)data;
210 	int tune_state = sdstd_3_get_tune_state(sdos->sd);
211 	int data_state = sdstd_3_get_data_state(sdos->sd);
212 	if ((tune_state == TUNING_START) || (tune_state == TUNING_ONGOING) ||
213 		(tune_state == TUNING_START_AFTER_DAT)) {
214 		return;
215 	}
216 	else if (data_state == DATA_TRANSFER_IDLE)
217 		sdstd_3_set_tune_state(sdos->sd, TUNING_START);
218 	else if (data_state == DATA_TRANSFER_ONGOING)
219 		sdstd_3_set_tune_state(sdos->sd, TUNING_START_AFTER_DAT);
220 }
221 
222 static void
sdstd_3_tuning_timer(ulong data)223 sdstd_3_tuning_timer(ulong data)
224 {
225 	struct sdos_info *sdos = (struct sdos_info *)data;
226 /* 	uint8 timeout = 0; */
227 	unsigned long int_flags;
228 
229 	sd_trace(("%s: enter\n", __FUNCTION__));
230 	/* schedule tasklet */
231 	/* * disable ISR's */
232 	local_irq_save(int_flags);
233 	if (sdstd_3_check_and_set_retuning(sdos->sd))
234 		tasklet_schedule(&sdos->tuning_tasklet);
235 
236 	/* * enable back ISR's */
237 	local_irq_restore(int_flags);
238 }
239 
sdstd_3_start_tuning(sdioh_info_t * sd)240 void sdstd_3_start_tuning(sdioh_info_t *sd)
241 {
242 	int tune_state;
243 	unsigned long int_flags = 0;
244 	unsigned int timer_enab;
245 	struct sdos_info *sdos = (struct sdos_info *)sd->sdos_info;
246 	sd_trace(("%s: enter\n", __FUNCTION__));
247 	/* * disable ISR's */
248 	local_irq_save(int_flags);
249 	timer_enab = atomic_read(&sdos->timer_enab);
250 
251 	tune_state = sdstd_3_get_tune_state(sd);
252 
253 	if (tune_state == TUNING_ONGOING) {
254 		/* do nothing */
255 		local_irq_restore(int_flags);
256 		goto exit;
257 	}
258 	/* change state */
259 	sdstd_3_set_tune_state(sd, TUNING_ONGOING);
260 	/* * enable ISR's */
261 	local_irq_restore(int_flags);
262 	sdstd_3_clk_tuning(sd, sdstd_3_get_uhsi_clkmode(sd));
263 #ifdef BCMSDIOH_STD_TUNING_WAR
264 	/*
265 	 * Observed intermittent SDIO command error after re-tuning done
266 	 * successfully.  Re-tuning twice is giving much reliable results.
267 	 */
268 	sdstd_3_clk_tuning(sd, sdstd_3_get_uhsi_clkmode(sd));
269 #endif /* BCMSDIOH_STD_TUNING_WAR */
270 	/* * disable ISR's */
271 	local_irq_save(int_flags);
272 	sdstd_3_set_tune_state(sd, TUNING_IDLE);
273 	/* * enable ISR's */
274 	local_irq_restore(int_flags);
275 
276 	/* enable retuning intrrupt */
277 	sdstd_3_enable_retuning_int(sd);
278 
279 	/* start retuning timer if enabled */
280 	if ((sdos->tuning_timer_exp) && (timer_enab)) {
281 		if (sd->sd3_tuning_reqd) {
282 			timer_expires(&sdos->tuning_timer) = jiffies + sdos->tuning_timer_exp * HZ;
283 			mod_timer(&sdos->tuning_timer, timer_expires(&sdos->tuning_timer));
284 		}
285 	}
286 exit:
287 	return;
288 
289 }
290 
291 void
sdstd_osfree(sdioh_info_t * sd)292 sdstd_osfree(sdioh_info_t *sd)
293 {
294 	struct sdos_info *sdos;
295 	ASSERT(sd && sd->sdos_info);
296 
297 	sdos = (struct sdos_info *)sd->sdos_info;
298 	MFREE(sd->osh, sdos, sizeof(struct sdos_info));
299 }
300 
301 /* Interrupt enable/disable */
302 SDIOH_API_RC
sdioh_interrupt_set(sdioh_info_t * sd,bool enable)303 sdioh_interrupt_set(sdioh_info_t *sd, bool enable)
304 {
305 	ulong flags;
306 	struct sdos_info *sdos;
307 
308 	sd_trace(("%s: %s\n", __FUNCTION__, enable ? "Enabling" : "Disabling"));
309 
310 	sdos = (struct sdos_info *)sd->sdos_info;
311 	ASSERT(sdos);
312 
313 	if (!(sd->host_init_done && sd->card_init_done)) {
314 		sd_err(("%s: Card & Host are not initted - bailing\n", __FUNCTION__));
315 		return SDIOH_API_RC_FAIL;
316 	}
317 
318 	if (enable && !(sd->intr_handler && sd->intr_handler_arg)) {
319 		sd_err(("%s: no handler registered, will not enable\n", __FUNCTION__));
320 		return SDIOH_API_RC_FAIL;
321 	}
322 
323 	/* Ensure atomicity for enable/disable calls */
324 	spin_lock_irqsave(&sdos->lock, flags);
325 
326 	sd->client_intr_enabled = enable;
327 	if (enable && !sd->lockcount)
328 		sdstd_devintr_on(sd);
329 	else
330 		sdstd_devintr_off(sd);
331 
332 	spin_unlock_irqrestore(&sdos->lock, flags);
333 
334 	return SDIOH_API_RC_SUCCESS;
335 }
336 
337 /* Protect against reentrancy (disable device interrupts while executing) */
338 void
sdstd_lock(sdioh_info_t * sd)339 sdstd_lock(sdioh_info_t *sd)
340 {
341 	ulong flags;
342 	struct sdos_info *sdos;
343 	int    wait_count = 0;
344 
345 	sdos = (struct sdos_info *)sd->sdos_info;
346 	ASSERT(sdos);
347 
348 	sd_trace(("%s: %d\n", __FUNCTION__, sd->lockcount));
349 
350 	spin_lock_irqsave(&sdos->lock, flags);
351 	while (sd->lockcount)
352 	{
353 	    spin_unlock_irqrestore(&sdos->lock, flags);
354 	    yield();
355 		spin_lock_irqsave(&sdos->lock, flags);
356 		if (++wait_count == 25000) {
357 		    if (!(sd->lockcount == 0)) {
358 			sd_err(("%s: ERROR: sd->lockcount == 0\n", __FUNCTION__));
359 		    }
360 		}
361 	}
362 	/* PR86684: Add temporary debugging print */
363 	if (wait_count)
364 		printk("sdstd_lock: wait count = %d\n", wait_count);
365 	sdstd_devintr_off(sd);
366 	sd->lockcount++;
367 	spin_unlock_irqrestore(&sdos->lock, flags);
368 	if ((sd->controller_type == SDIOH_TYPE_RICOH_R5C822) && (sd->version == HOST_CONTR_VER_3))
369 		sdstd_turn_on_clock(sd);
370 }
371 
372 /* Enable client interrupt */
373 void
sdstd_unlock(sdioh_info_t * sd)374 sdstd_unlock(sdioh_info_t *sd)
375 {
376 	ulong flags;
377 	struct sdos_info *sdos;
378 
379 	sd_trace(("%s: %d, %d\n", __FUNCTION__, sd->lockcount, sd->client_intr_enabled));
380 	ASSERT(sd->lockcount > 0);
381 
382 	sdos = (struct sdos_info *)sd->sdos_info;
383 	ASSERT(sdos);
384 
385 	spin_lock_irqsave(&sdos->lock, flags);
386 	if (--sd->lockcount == 0 && sd->client_intr_enabled) {
387 		sdstd_devintr_on(sd);
388 	}
389 	spin_unlock_irqrestore(&sdos->lock, flags);
390 	if (sd_3_power_save)
391 	{
392 		if ((sd->controller_type == SDIOH_TYPE_RICOH_R5C822) &&
393 			(sd->version == HOST_CONTR_VER_3))
394 			sdstd_turn_off_clock(sd);
395 	}
396 }
397 
398 void
sdstd_os_lock_irqsave(sdioh_info_t * sd,ulong * flags)399 sdstd_os_lock_irqsave(sdioh_info_t *sd, ulong* flags)
400 {
401 	struct sdos_info *sdos = (struct sdos_info *)sd->sdos_info;
402 	spin_lock_irqsave(&sdos->lock, *flags);
403 }
404 void
sdstd_os_unlock_irqrestore(sdioh_info_t * sd,ulong * flags)405 sdstd_os_unlock_irqrestore(sdioh_info_t *sd, ulong* flags)
406 {
407 	struct sdos_info *sdos = (struct sdos_info *)sd->sdos_info;
408 	spin_unlock_irqrestore(&sdos->lock, *flags);
409 }
410 
411 void
sdstd_waitlockfree(sdioh_info_t * sd)412 sdstd_waitlockfree(sdioh_info_t *sd)
413 {
414 	if (sd->lockcount) {
415 		printk("wait lock free\n");
416 		while (sd->lockcount)
417 		{
418 		    yield();
419 		}
420 	}
421 }
422 
423 #ifdef BCMQT
424 void
sdstd_os_yield(sdioh_info_t * sd)425 sdstd_os_yield(sdioh_info_t *sd)
426 {
427 #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 29))
428 /*
429  * FC4/11 issue on QT if driver hogs > 10s of CPU causing:
430  *    BUG: soft lockup detected on CPU#0!
431  *
432  * XXX Hack: For now, interleave yielding of CPU when we're spinning waiting for
433  * XXX register status
434  */
435 	yield();
436 #endif
437 }
438 #endif /* BCMQT */
439 
440 /* Returns 0 for success, -1 for interrupted, -2 for timeout */
441 int
sdstd_waitbits(sdioh_info_t * sd,uint16 norm,uint16 err,bool local_yield,uint16 * bits)442 sdstd_waitbits(sdioh_info_t *sd, uint16 norm, uint16 err, bool local_yield, uint16 *bits)
443 {
444 	struct sdos_info *sdos;
445 	int rc = 0;
446 
447 	sdos = (struct sdos_info *)sd->sdos_info;
448 
449 #ifndef BCMSDYIELD
450 	ASSERT(!local_yield);
451 #endif
452 	sd_trace(("%s: int 0x%02x err 0x%02x yield %d canblock %d\n",
453 	          __FUNCTION__, norm, err, local_yield, BLOCKABLE()));
454 
455 	/* Clear the "interrupt happened" flag and last intrstatus */
456 	sd->got_hcint = FALSE;
457 	sd->last_intrstatus = 0;
458 
459 #ifdef BCMSDYIELD
460 	if (local_yield && BLOCKABLE()) {
461 		/* Enable interrupts, wait for the indication, then disable */
462 		sdstd_intrs_on(sd, norm, err);
463 		rc = wait_event_interruptible_timeout(sdos->intr_wait_queue,
464 		                                      (sd->got_hcint),
465 		                                      SDSTD_WAITBITS_TIMEOUT);
466 		if (rc < 0)
467 			rc = -1;	/* interrupted */
468 		else if (rc == 0)
469 			rc = -2;	/* timeout */
470 		else
471 			rc = 0;		/* success */
472 		sdstd_intrs_off(sd, norm, err);
473 	} else
474 #endif /* BCMSDYIELD */
475 	{
476 		sdstd_spinbits(sd, norm, err);
477 	}
478 
479 	sd_trace(("%s: last_intrstatus 0x%04x\n", __FUNCTION__, sd->last_intrstatus));
480 
481 	*bits = sd->last_intrstatus;
482 
483 	return rc;
484 }
485 
486 #ifdef DHD_DEBUG
sdstd_enable_disable_periodic_timer(sdioh_info_t * sd,uint val)487 void sdstd_enable_disable_periodic_timer(sdioh_info_t *sd, uint val)
488 {
489 	struct sdos_info *sdos = (struct sdos_info *)sd->sdos_info;
490 
491 	    if (val == SD_DHD_ENABLE_PERIODIC_TUNING) {
492 			/* start of tuning timer */
493 			timer_expires(&sdos->tuning_timer) = jiffies +  sdos->tuning_timer_exp * HZ;
494 			mod_timer(&sdos->tuning_timer, timer_expires(&sdos->tuning_timer));
495 		}
496 	    if (val == SD_DHD_DISABLE_PERIODIC_TUNING) {
497 			/* stop periodic timer */
498 		   del_timer_sync(&sdos->tuning_timer);
499 		}
500 }
501 #endif /* debugging purpose */
502 
503 /* forward declarations for PCI probe and remove functions. */
504 static int __devinit bcmsdh_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent);
505 static void __devexit bcmsdh_pci_remove(struct pci_dev *pdev);
506 
507 /**
508  * pci id table
509  */
510 static struct pci_device_id bcmsdh_pci_devid[] __devinitdata = {
511 	{ vendor: PCI_ANY_ID,
512 	device: PCI_ANY_ID,
513 	subvendor: PCI_ANY_ID,
514 	subdevice: PCI_ANY_ID,
515 	class: 0,
516 	class_mask: 0,
517 	driver_data: 0,
518 	},
519 	{ 0, 0, 0, 0, 0, 0, 0}
520 };
521 MODULE_DEVICE_TABLE(pci, bcmsdh_pci_devid);
522 
523 /**
524  * SDIO Host Controller pci driver info
525  */
526 static struct pci_driver bcmsdh_pci_driver = {
527 	node:		{&(bcmsdh_pci_driver.node), &(bcmsdh_pci_driver.node)},
528 	name:		"bcmsdh",
529 	id_table:	bcmsdh_pci_devid,
530 	probe:		bcmsdh_pci_probe,
531 	remove:		bcmsdh_pci_remove,
532 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
533 	save_state:	NULL,
534 #endif
535 	suspend:	NULL,
536 	resume:		NULL,
537 	};
538 
539 extern uint sd_pci_slot;	/* Force detection to a particular PCI */
540 							/* slot only . Allows for having multiple */
541 							/* WL devices at once in a PC */
542 							/* Only one instance of dhd will be */
543 							/* usable at a time */
544 							/* Upper word is bus number, */
545 							/* lower word is slot number */
546 							/* Default value of 0xffffffff turns this */
547 							/* off */
548 module_param(sd_pci_slot, uint, 0);
549 
550 /**
551  * Detect supported SDIO Host Controller and attach if found.
552  *
553  * Determine if the device described by pdev is a supported SDIO Host
554  * Controller.  If so, attach to it and attach to the target device.
555  */
556 static int __devinit
bcmsdh_pci_probe(struct pci_dev * pdev,const struct pci_device_id * ent)557 bcmsdh_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
558 {
559 	osl_t *osh = NULL;
560 	sdioh_info_t *sdioh = NULL;
561 	int rc;
562 
563 	if (sd_pci_slot != 0xFFFFffff) {
564 		if (pdev->bus->number != (sd_pci_slot>>16) ||
565 			PCI_SLOT(pdev->devfn) != (sd_pci_slot&0xffff)) {
566 			sd_err(("%s: %s: bus %X, slot %X, vend %X, dev %X\n",
567 				__FUNCTION__,
568 				bcmsdh_chipmatch(pdev->vendor, pdev->device)
569 				?"Found compatible SDIOHC"
570 				:"Probing unknown device",
571 				pdev->bus->number, PCI_SLOT(pdev->devfn), pdev->vendor,
572 				pdev->device));
573 			return -ENODEV;
574 		}
575 		sd_err(("%s: %s: bus %X, slot %X, vendor %X, device %X (good PCI location)\n",
576 			__FUNCTION__,
577 			bcmsdh_chipmatch(pdev->vendor, pdev->device)
578 			?"Using compatible SDIOHC"
579 			:"WARNING, forced use of unkown device",
580 			pdev->bus->number, PCI_SLOT(pdev->devfn), pdev->vendor, pdev->device));
581 	}
582 
583 	if ((pdev->vendor == VENDOR_TI) && ((pdev->device == PCIXX21_FLASHMEDIA_ID) ||
584 	    (pdev->device == PCIXX21_FLASHMEDIA0_ID))) {
585 		uint32 config_reg;
586 
587 		sd_err(("%s: Disabling TI FlashMedia Controller.\n", __FUNCTION__));
588 		if (!(osh = osl_attach(pdev, SDIO_BUS, TRUE))) {
589 			sd_err(("%s: osl_attach failed\n", __FUNCTION__));
590 			goto err;
591 		}
592 
593 		config_reg = OSL_PCI_READ_CONFIG(osh, 0x4c, 4);
594 
595 		/*
596 		 * Set MMC_SD_DIS bit in FlashMedia Controller.
597 		 * Disbling the SD/MMC Controller in the FlashMedia Controller
598 		 * allows the Standard SD Host Controller to take over control
599 		 * of the SD Slot.
600 		 */
601 		config_reg |= 0x02;
602 		OSL_PCI_WRITE_CONFIG(osh, 0x4c, 4, config_reg);
603 		osl_detach(osh);
604 	}
605 	/* match this pci device with what we support */
606 	/* we can't solely rely on this to believe it is our SDIO Host Controller! */
607 	if (!bcmsdh_chipmatch(pdev->vendor, pdev->device)) {
608 		if (pdev->vendor == VENDOR_BROADCOM) {
609 			sd_err(("%s: Unknown Broadcom device (vendor: %#x, device: %#x).\n",
610 				__FUNCTION__, pdev->vendor, pdev->device));
611 		}
612 		return -ENODEV;
613 	}
614 
615 	/* this is a pci device we might support */
616 	sd_err(("%s: Found possible SDIO Host Controller: bus %d slot %d func %d irq %d\n",
617 		__FUNCTION__,
618 		pdev->bus->number, PCI_SLOT(pdev->devfn),
619 		PCI_FUNC(pdev->devfn), pdev->irq));
620 
621 	/* use bcmsdh_query_device() to get the vendor ID of the target device so
622 	 * it will eventually appear in the Broadcom string on the console
623 	 */
624 
625 	/* allocate SDIO Host Controller state info */
626 	if (!(osh = osl_attach(pdev, SDIO_BUS, TRUE))) {
627 		sd_err(("%s: osl_attach failed\n", __FUNCTION__));
628 		goto err;
629 	}
630 
631 	/* map to address where host can access */
632 	pci_set_master(pdev);
633 	rc = pci_enable_device(pdev);
634 	if (rc) {
635 		sd_err(("%s: Cannot enable PCI device\n", __FUNCTION__));
636 		goto err;
637 	}
638 
639 	sdioh = sdioh_attach(osh, (void *)(ulong)pci_resource_start(pdev, 0), pdev->irq);
640 	if (sdioh == NULL) {
641 		sd_err(("%s: sdioh_attach failed\n", __FUNCTION__));
642 		goto err;
643 	}
644 	sdioh->bcmsdh = bcmsdh_probe(osh, &pdev->dev, sdioh, NULL, PCI_BUS, -1, -1);
645 	if (sdioh->bcmsdh == NULL) {
646 		sd_err(("%s: bcmsdh_probe failed\n", __FUNCTION__));
647 		goto err;
648 	}
649 
650 	pci_set_drvdata(pdev, sdioh);
651 	return 0;
652 
653 err:
654 	if (sdioh != NULL)
655 		sdioh_detach(osh, sdioh);
656 	if (osh != NULL)
657 		osl_detach(osh);
658 	return -ENOMEM;
659 }
660 
661 /**
662  * Detach from target devices and SDIO Host Controller
663  */
664 static void __devexit
bcmsdh_pci_remove(struct pci_dev * pdev)665 bcmsdh_pci_remove(struct pci_dev *pdev)
666 {
667 	sdioh_info_t *sdioh;
668 	osl_t *osh;
669 
670 	sdioh = pci_get_drvdata(pdev);
671 	if (sdioh == NULL) {
672 		sd_err(("%s: error, no sdioh handler found\n", __FUNCTION__));
673 		return;
674 	}
675 
676 	osh = sdioh->osh;
677 	bcmsdh_remove(sdioh->bcmsdh);
678 	sdioh_detach(osh, sdioh);
679 	osl_detach(osh);
680 }
681 
bcmsdh_register_client_driver(void)682 int bcmsdh_register_client_driver(void)
683 {
684 	return pci_module_init(&bcmsdh_pci_driver);
685 }
686 
bcmsdh_unregister_client_driver(void)687 void bcmsdh_unregister_client_driver(void)
688 {
689 	pci_unregister_driver(&bcmsdh_pci_driver);
690 }
691