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