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