1 /* 2 * 'Standard' SDIO HOST CONTROLLER driver - linux portion 3 * 4 * Copyright (C) 1999-2010, Broadcom Corporation 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 * Notwithstanding the above, under no circumstances may you combine this 21 * software in any way with any other Broadcom software provided under a license 22 * other than the GPL, without Broadcom's express prior written consent. 23 * 24 * $Id: bcmsdstd_linux.c,v 1.11.18.2.16.1 2010/08/17 17:03:13 Exp $ 25 */ 26 27 #include <typedefs.h> 28 #include <pcicfg.h> 29 #include <bcmutils.h> 30 #include <sdio.h> /* SDIO Specs */ 31 #include <bcmsdbus.h> /* bcmsdh to/from specific controller APIs */ 32 #include <sdiovar.h> /* to get msglevel bit values */ 33 34 #include <linux/sched.h> /* request_irq() */ 35 36 #include <bcmsdstd.h> 37 38 struct sdos_info { 39 sdioh_info_t *sd; 40 spinlock_t lock; 41 wait_queue_head_t intr_wait_queue; 42 }; 43 44 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) 45 #define BLOCKABLE() (!in_atomic()) 46 #else 47 #define BLOCKABLE() (!in_interrupt()) 48 #endif 49 50 /* Interrupt handler */ 51 static irqreturn_t 52 sdstd_isr(int irq, void *dev_id 53 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) 54 , struct pt_regs *ptregs 55 #endif 56 ) 57 { 58 sdioh_info_t *sd; 59 struct sdos_info *sdos; 60 bool ours; 61 62 sd = (sdioh_info_t *)dev_id; 63 64 if (!sd->card_init_done) { 65 sd_err(("%s: Hey Bogus intr...not even initted: irq %d\n", __FUNCTION__, irq)); 66 return IRQ_RETVAL(FALSE); 67 } else { 68 ours = check_client_intr(sd); 69 70 /* For local interrupts, wake the waiting process */ 71 if (ours && sd->got_hcint) { 72 sd_trace(("INTR->WAKE\n")); 73 sdos = (struct sdos_info *)sd->sdos_info; 74 wake_up_interruptible(&sdos->intr_wait_queue); 75 } 76 return IRQ_RETVAL(ours); 77 } 78 } 79 80 /* Register with Linux for interrupts */ 81 int 82 sdstd_register_irq(sdioh_info_t *sd, uint irq) 83 { 84 sd_trace(("Entering %s: irq == %d\n", __FUNCTION__, irq)); 85 if (request_irq(irq, sdstd_isr, IRQF_SHARED, "bcmsdstd", sd) < 0) { 86 sd_err(("%s: request_irq() failed\n", __FUNCTION__)); 87 return ERROR; 88 } 89 return SUCCESS; 90 } 91 92 /* Free Linux irq */ 93 void 94 sdstd_free_irq(uint irq, sdioh_info_t *sd) 95 { 96 free_irq(irq, sd); 97 } 98 99 /* Map Host controller registers */ 100 101 uint32 * 102 sdstd_reg_map(osl_t *osh, int32 addr, int size) 103 { 104 return (uint32 *)REG_MAP(addr, size); 105 } 106 107 void 108 sdstd_reg_unmap(osl_t *osh, int32 addr, int size) 109 { 110 REG_UNMAP((void*)(uintptr)addr); 111 } 112 113 int 114 sdstd_osinit(sdioh_info_t *sd) 115 { 116 struct sdos_info *sdos; 117 118 sdos = (struct sdos_info*)MALLOC(sd->osh, sizeof(struct sdos_info)); 119 sd->sdos_info = (void*)sdos; 120 if (sdos == NULL) 121 return BCME_NOMEM; 122 123 sdos->sd = sd; 124 spin_lock_init(&sdos->lock); 125 init_waitqueue_head(&sdos->intr_wait_queue); 126 return BCME_OK; 127 } 128 129 void 130 sdstd_osfree(sdioh_info_t *sd) 131 { 132 struct sdos_info *sdos; 133 ASSERT(sd && sd->sdos_info); 134 135 sdos = (struct sdos_info *)sd->sdos_info; 136 MFREE(sd->osh, sdos, sizeof(struct sdos_info)); 137 } 138 139 /* Interrupt enable/disable */ 140 SDIOH_API_RC 141 sdioh_interrupt_set(sdioh_info_t *sd, bool enable) 142 { 143 ulong flags; 144 struct sdos_info *sdos; 145 146 sd_trace(("%s: %s\n", __FUNCTION__, enable ? "Enabling" : "Disabling")); 147 148 sdos = (struct sdos_info *)sd->sdos_info; 149 ASSERT(sdos); 150 151 if (!(sd->host_init_done && sd->card_init_done)) { 152 sd_err(("%s: Card & Host are not initted - bailing\n", __FUNCTION__)); 153 return SDIOH_API_RC_FAIL; 154 } 155 156 if (enable && !(sd->intr_handler && sd->intr_handler_arg)) { 157 sd_err(("%s: no handler registered, will not enable\n", __FUNCTION__)); 158 return SDIOH_API_RC_FAIL; 159 } 160 161 /* Ensure atomicity for enable/disable calls */ 162 spin_lock_irqsave(&sdos->lock, flags); 163 164 sd->client_intr_enabled = enable; 165 if (enable && !sd->lockcount) 166 sdstd_devintr_on(sd); 167 else 168 sdstd_devintr_off(sd); 169 170 spin_unlock_irqrestore(&sdos->lock, flags); 171 172 return SDIOH_API_RC_SUCCESS; 173 } 174 175 /* Protect against reentrancy (disable device interrupts while executing) */ 176 void 177 sdstd_lock(sdioh_info_t *sd) 178 { 179 ulong flags; 180 struct sdos_info *sdos; 181 182 sdos = (struct sdos_info *)sd->sdos_info; 183 ASSERT(sdos); 184 185 sd_trace(("%s: %d\n", __FUNCTION__, sd->lockcount)); 186 187 spin_lock_irqsave(&sdos->lock, flags); 188 if (sd->lockcount) { 189 sd_err(("%s: Already locked! called from %p\n", 190 __FUNCTION__, 191 __builtin_return_address(0))); 192 ASSERT(sd->lockcount == 0); 193 } 194 sdstd_devintr_off(sd); 195 sd->lockcount++; 196 spin_unlock_irqrestore(&sdos->lock, flags); 197 } 198 199 /* Enable client interrupt */ 200 void 201 sdstd_unlock(sdioh_info_t *sd) 202 { 203 ulong flags; 204 struct sdos_info *sdos; 205 206 sd_trace(("%s: %d, %d\n", __FUNCTION__, sd->lockcount, sd->client_intr_enabled)); 207 ASSERT(sd->lockcount > 0); 208 209 sdos = (struct sdos_info *)sd->sdos_info; 210 ASSERT(sdos); 211 212 spin_lock_irqsave(&sdos->lock, flags); 213 if (--sd->lockcount == 0 && sd->client_intr_enabled) { 214 sdstd_devintr_on(sd); 215 } 216 spin_unlock_irqrestore(&sdos->lock, flags); 217 } 218 219 uint16 220 sdstd_waitbits(sdioh_info_t *sd, uint16 norm, uint16 err, bool yield) 221 { 222 struct sdos_info *sdos; 223 224 sdos = (struct sdos_info *)sd->sdos_info; 225 226 #ifndef BCMSDYIELD 227 ASSERT(!yield); 228 #endif 229 sd_trace(("%s: int 0x%02x err 0x%02x yield %d canblock %d\n", 230 __FUNCTION__, norm, err, yield, BLOCKABLE())); 231 232 /* Clear the "interrupt happened" flag and last intrstatus */ 233 sd->got_hcint = FALSE; 234 sd->last_intrstatus = 0; 235 236 #ifdef BCMSDYIELD 237 if (yield && BLOCKABLE()) { 238 /* Enable interrupts, wait for the indication, then disable */ 239 sdstd_intrs_on(sd, norm, err); 240 wait_event_interruptible(sdos->intr_wait_queue, (sd->got_hcint)); 241 sdstd_intrs_off(sd, norm, err); 242 } else 243 #endif /* BCMSDYIELD */ 244 { 245 sdstd_spinbits(sd, norm, err); 246 } 247 248 sd_trace(("%s: last_intrstatus 0x%04x\n", __FUNCTION__, sd->last_intrstatus)); 249 250 return sd->last_intrstatus; 251 } 252