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