int sdio_force_ecsi_on_if_1bit_mode(struct sdio_func *func) { int ret; unsigned char val; ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IF, 0, &val); if (ret) return ret; // check if bus width set if ((val & 0x3) == SDIO_BUS_WIDTH_1BIT) { val |= SDIO_BUS_ECSI_ENABLED; ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IF, val, NULL); } return ret; }
static int process_sdio_pending_irqs(struct mmc_host *host) { struct mmc_card *card = host->card; int i, ret, count; unsigned char pending; struct sdio_func *func; if ((!host->sdio_irq_pending) && (host->caps & MMC_CAP_SDIO_IRQ)) return 0; /* * Optimization, if there is only 1 function interrupt registered * and we know an IRQ was signaled then call irq handler directly. * Otherwise do the full probe. */ func = card->sdio_single_irq; if (func && host->sdio_irq_pending) { func->irq_handler(func); if (func->func_status == func_suspended) host->break_suspend = 1; return 1; } ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, &pending); if (ret) { pr_debug("%s: error %d reading SDIO_CCCR_INTx\n", mmc_card_id(card), ret); return ret; } count = 0; for (i = 1; i <= 7; i++) { if (pending & (1 << i)) { func = card->sdio_func[i - 1]; if (!func) { pr_warning("%s: pending IRQ for " "non-existent function\n", mmc_card_id(card)); ret = -EINVAL; } else if (func->irq_handler) { if (func->func_status == func_suspended) host->break_suspend = 1; func->irq_handler(func); count++; } else { pr_warning("%s: pending IRQ with no handler\n", sdio_func_id(func)); ret = -EINVAL; } } } if (count) return count; return ret; }
/** * sdio_release_irq - release the IRQ for a SDIO function * @func: SDIO function * * Disable and release the IRQ for the given SDIO function. */ int sdio_release_irq(struct sdio_func *func) { int ret; unsigned char reg; BUG_ON(!func); BUG_ON(!func->card); pr_debug("SDIO: Disabling IRQ for %s...\n", sdio_func_id(func)); #ifndef CONFIG_HUAWEI_WIFI_SDCC if (func->irq_handler) { func->irq_handler = NULL; sdio_card_irq_put(func->card); } #endif ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, ®); if (ret) return ret; reg &= ~(1 << func->num); /* Disable master interrupt with the last function interrupt */ if (!(reg & 0xFE)) reg = 0; ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL); if (ret) return ret; #ifdef CONFIG_HUAWEI_WIFI_SDCC if (func->irq_handler) { func->irq_handler = NULL; sdio_card_irq_put(func->card); } #endif return 0; }
/** * sdio_writeb - write a single byte to a SDIO function * @func: SDIO function to access * @b: byte to write * @addr: address to write to * @err_ret: optional status value from transfer * * Writes a single byte to the address space of a given SDIO * function. @err_ret will contain the status of the actual * transfer. */ void sdio_writeb(struct sdio_func *func, u8 b, unsigned int addr, int *err_ret) { int ret; BUG_ON(!func); ret = mmc_io_rw_direct(func->card, 1, func->num, addr, b, NULL); if (err_ret) *err_ret = ret; }
/** * sdio_enable_func - enables a SDIO function for usage * @func: SDIO function to enable * * Powers up and activates a SDIO function so that register * access is possible. */ int sdio_enable_func(struct sdio_func *func) { int ret; unsigned char reg; unsigned long timeout; BUG_ON(!func); BUG_ON(!func->card); pr_debug("SDIO: Enabling device %s...\n", sdio_func_id(func)); ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IOEx, 0, ®); if (ret) goto err; reg |= 1 << func->num; ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IOEx, reg, NULL); if (ret) goto err; timeout = jiffies + msecs_to_jiffies(func->enable_timeout); while (1) { ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IORx, 0, ®); if (ret) goto err; if (reg & (1 << func->num)) break; ret = -ETIME; if (time_after(jiffies, timeout)) goto err; } pr_debug("SDIO: Enabled device %s\n", sdio_func_id(func)); return 0; err: pr_debug("SDIO: Failed to enable device %s\n", sdio_func_id(func)); return ret; }
/** * sdio_writeb - write a single byte to a SDIO function * @func: SDIO function to access * @b: byte to write * @addr: address to write to * @err_ret: optional status value from transfer * * Writes a single byte to the address space of a given SDIO * function. @err_ret will contain the status of the actual * transfer. */ void sdio_writeb(struct sdio_func *func, uint8 b, unsigned int addr, int *err_ret) { int ret; if (!func) return; ret = mmc_io_rw_direct(func->card, SDIO_W, func->num, addr, b, NULL); if (err_ret) *err_ret = ret; }
static int sdio_enable_wide(void *pCardInfo) { SDM_CARD_INFO* card; int ret; uint8 ctrl; HOST_BUS_WIDTH wide = BUS_WIDTH_INVALID; card = (SDM_CARD_INFO*)pCardInfo; /* * whether is card internal support wide bus */ if (card->cccr.low_speed && !card->cccr.wide_bus) return 0; /* * whether is SDC iomux config wide bus. */ ret = SDC_GetBusWidth(card->SDCPort, &wide); if ((ret != SDC_SUCCESS) || (wide != BUS_WIDTH_4_BIT)) return 0; ret = mmc_io_rw_direct(card, SDIO_R, SDIO_FUN_0, SDIO_CCCR_IF, 0, &ctrl); if (ret) return ret; ctrl |= SDIO_BUS_WIDTH_4BIT; ret = mmc_io_rw_direct(card, SDIO_W, SDIO_FUN_0, SDIO_CCCR_IF, ctrl, NULL); if (ret) return ret; /* * config SDC is 4 bits bus width. */ ret = SDIOC_SetBusWidth(card->SDCPort, BUS_WIDTH_4_BIT); if (SDC_SUCCESS != ret) return ret; return 1; }
/** * sdio_enable_func - enables a SDIO function for usage * @func: SDIO function to enable * * Powers up and activates a SDIO function so that register * access is possible. */ int sdio_enable_func(struct sdio_func *func) { int ret, i; unsigned char reg; if (!func || !func->card) return -EPERM; ret = mmc_io_rw_direct(func->card, SDIO_R, SDIO_FUN_0, SDIO_CCCR_IOEx, 0, ®); if (ret) goto err; reg |= 1 << func->num; ret = mmc_io_rw_direct(func->card, SDIO_W, SDIO_FUN_0, SDIO_CCCR_IOEx, reg, NULL); if (ret) goto err; i = 0; while (1) { ret = mmc_io_rw_direct(func->card, SDIO_R, SDIO_FUN_0, SDIO_CCCR_IORx, 0, ®); if (ret) goto err; if (reg & (1 << func->num)) break; ret = -ETIME; if (i++ >= 200) break; DelayUs(1000); } return 0; err: return ret; }
/** * sdio_writeb_readb - write and read a byte from SDIO function * @func: SDIO function to access * @write_byte: byte to write * @addr: address to write to * @err_ret: optional status value from transfer * * Performs a RAW (Read after Write) operation as defined by SDIO spec - * single byte is written to address space of a given SDIO function and * response is read back from the same address, both using single request. * If there is a problem with the operation, 0xff is returned and * @err_ret will contain the error code. */ uint8 sdio_writeb_readb(struct sdio_func *func, uint8 write_byte, unsigned int addr, int *err_ret) { int ret; uint8 val; ret = mmc_io_rw_direct(func->card, SDIO_W, func->num, addr, write_byte, &val); if (err_ret) *err_ret = ret; if (ret) val = 0xff; return val; }
static int process_sdio_pending_irqs(struct mmc_card *card) { int i, ret, count; unsigned char pending; if(card->disabled){ printk(KERN_DEBUG "%s: irq arrive after controller is " "disabled, defer it.\n", mmc_card_id(card)); card->pending_interrupt = 1; return 0; } ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, &pending); if (ret) { printk(KERN_DEBUG "%s: error %d reading SDIO_CCCR_INTx\n", mmc_card_id(card), ret); return ret; } count = 0; for (i = 1; i <= 7; i++) { if (pending & (1 << i)) { struct sdio_func *func = card->sdio_func[i - 1]; if (!func) { printk(KERN_WARNING "%s: pending IRQ for " "non-existant function\n", mmc_card_id(card)); ret = -EINVAL; } else if (func->irq_handler) { if (func->suspended) { card->pending_interrupt = 1; printk(KERN_WARNING "%s: IRQ that arrives in suspend" " mode.\n", sdio_func_id(func)); } func->irq_handler(func); count++; } else { printk(KERN_WARNING "%s: pending IRQ with no handler\n", sdio_func_id(func)); ret = -EINVAL; } } } if (count) return count; return ret; }
/** * sdio_disable_func - disable a SDIO function * @func: SDIO function to disable * * Powers down and deactivates a SDIO function. Register access * to this function will fail until the function is reenabled. */ int sdio_disable_func(struct sdio_func *func) { int ret; unsigned char reg; if (!func || !func->card) return -EPERM; ret = mmc_io_rw_direct(func->card, SDIO_R, SDIO_FUN_0, SDIO_CCCR_IOEx, 0, ®); if (ret) goto err; reg &= ~(1 << func->num); ret = mmc_io_rw_direct(func->card, SDIO_W, SDIO_FUN_0, SDIO_CCCR_IOEx, reg, NULL); if (ret) goto err; return 0; err: return -EIO; }
/** * sdio_f0_writeb - write a single byte to SDIO function 0 * @func: an SDIO function of the card * @b: byte to write * @addr: address to write to * @err_ret: optional status value from transfer * * Writes a single byte to the address space of SDIO function 0. * @err_ret will contain the status of the actual transfer. * * Only writes to the vendor specific CCCR registers (0xF0 - * 0xFF) are permiited; @err_ret will be set to -EINVAL for * * writes outside this range. */ void sdio_f0_writeb(struct sdio_func *func, unsigned char b, unsigned int addr, int *err_ret) { int ret; if (addr < 0xF0 || addr > 0xFF) { if (err_ret) *err_ret = -EINVAL; return; } ret = mmc_io_rw_direct(func->card, SDIO_W, SDIO_FUN_0, addr, b, NULL); if (err_ret) *err_ret = ret; }
static int process_sdio_pending_irqs(struct mmc_card *card) { int i, ret, count; unsigned char pending; struct sdio_func *func; /* * Optimization, if there is only 1 function interrupt registered * call irq handler directly */ func = card->sdio_single_irq; if (func) { func->irq_handler(func); return 1; } ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, &pending); if (ret) { pr_debug("%s: error %d reading SDIO_CCCR_INTx\n", mmc_card_id(card), ret); return ret; } count = 0; for (i = 1; i <= 7; i++) { if (pending & (1 << i)) { func = card->sdio_func[i - 1]; if (!func) { pr_warning("%s: pending IRQ for " "non-existent function\n", mmc_card_id(card)); ret = -EINVAL; } else if (func->irq_handler) { func->irq_handler(func); count++; } else { pr_warning("%s: pending IRQ with no handler\n", sdio_func_id(func)); ret = -EINVAL; } } } if (count) return count; return ret; }
static int process_sdio_pending_irqs(struct mmc_card *card) { int i, ret, count; unsigned char pending; ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, &pending); if (ret) { printk(KERN_DEBUG "%s: error %d reading SDIO_CCCR_INTx\n", mmc_card_id(card), ret); sdio_debug = 1; return ret; } count = 0; for (i = 1; i <= 7; i++) { if (pending & (1 << i)) { struct sdio_func *func = card->sdio_func[i - 1]; if (!func) { printk(KERN_WARNING "%s: pending IRQ for " "non-existant function\n", mmc_card_id(card)); ret = -EINVAL; } else if (func->irq_handler) { if (sdio_debug) { printk(KERN_DEBUG "%s: pending irq for func%d\n", mmc_card_id(card), i); } func->irq_handler(func); count++; } else { printk(KERN_WARNING "%s: pending IRQ with no handler\n", sdio_func_id(func)); ret = -EINVAL; } } } if (sdio_debug) { printk(KERN_DEBUG "%s: pending irq 0x%x\n", mmc_card_id(card), pending); sdio_debug = 0; } if (count) return count; return ret; }
void sdio_f0_writeb(struct sdio_func *func, unsigned char b, unsigned int addr, int *err_ret) { int ret; BUG_ON(!func); if ((addr < 0xF0 || addr > 0xFF) && (!mmc_card_lenient_fn0(func->card))) { if (err_ret) *err_ret = -EINVAL; return; } ret = mmc_io_rw_direct(func->card, 1, 0, addr, b, NULL); if (err_ret) *err_ret = ret; }
/** * sdio_f0_readb - read a single byte from SDIO function 0 * @func: an SDIO function of the card * @addr: address to read * @err_ret: optional status value from transfer * * Reads a single byte from the address space of SDIO function 0. * If there is a problem reading the address, 0xff is returned * and @err_ret will contain the error code. */ unsigned char sdio_f0_readb(struct sdio_func *func, unsigned int addr, int *err_ret) { int ret; unsigned char val; if (err_ret) *err_ret = 0; ret = mmc_io_rw_direct(func->card, SDIO_R, SDIO_FUN_0, addr, 0, &val); if (ret) { if (err_ret) *err_ret = ret; return 0xFF; } return val; }
/** * sdio_readb - read a single byte from a SDIO function * @func: SDIO function to access * @addr: address to read * @err_ret: optional status value from transfer * * Reads a single byte from the address space of a given SDIO * function. If there is a problem reading the address, 0xff * is returned and @err_ret will contain the error code. */ u8 sdio_readb(struct sdio_func *func, unsigned int addr, int *err_ret) { int ret; u8 val; BUG_ON(!func); if (err_ret) *err_ret = 0; ret = mmc_io_rw_direct(func->card, 0, func->num, addr, 0, &val); if (ret) { if (err_ret) *err_ret = ret; return 0xFF; } return val; }
/** * sdio_readb_ext - read a single byte from a SDIO function * @func: SDIO function to access * @addr: address to read * @err_ret: optional status value from transfer * @in: value to add to argument * * Reads a single byte from the address space of a given SDIO * function. If there is a problem reading the address, 0xff * is returned and @err_ret will contain the error code. */ unsigned char sdio_readb_ext(struct sdio_func *func, unsigned int addr, int *err_ret, unsigned in) { int ret; unsigned char val; BUG_ON(!func); if (err_ret) *err_ret = 0; ret = mmc_io_rw_direct(func->card, 0, func->num, addr, (u8)in, &val); if (ret) { if (err_ret) *err_ret = ret; return 0xFF; } return val; }
static int generic_write_bytes(unsigned int uFunc, unsigned int uHwAddr, unsigned char *pData, unsigned int uLen, unsigned int bIncAddr, unsigned int bMore) { unsigned int i; int ret; PDEBUG("%s: uFunc %d uHwAddr %d pData %x uLen %d\n", __func__, uFunc, uHwAddr, (unsigned int) pData, uLen); BUG_ON(uFunc != SDIO_CTRL_FUNC && uFunc != SDIO_WLAN_FUNC); for (i = 0; i < uLen; i++) { if (uFunc == 0) { if (uHwAddr < 0xF0 || uHwAddr > 0xFF) { ret = mmc_io_rw_direct(tiwlan_func[uFunc]->card, 1, uFunc, uHwAddr, *pData, NULL); if (ret != 0) printk("%s: mmc_io_rw_direct error\n", __func__); } else { sdio_f0_writeb(tiwlan_func[uFunc], *pData, uHwAddr, &ret); if (ret != 0) printk(KERN_ERR "sdio_f0_writeb: function %d sdio error: %d\n", __func__, uFunc, ret); } } else { sdio_writeb(tiwlan_func[uFunc], *pData, uHwAddr, &ret); } if (0 != ret) { printk(KERN_ERR "%s: function %d sdio error: %d\n", __func__, uFunc, ret); return -1; } pData++; if (bIncAddr) uHwAddr++; } return 0; }
/** * sdio_f0_writeb - write a single byte to SDIO function 0 * @func: an SDIO function of the card * @b: byte to write * @addr: address to write to * @err_ret: optional status value from transfer * * Writes a single byte to the address space of SDIO function 0. * @err_ret will contain the status of the actual transfer. * * Only writes to the vendor specific CCCR registers (0xF0 - * 0xFF) are permiited; @err_ret will be set to -EINVAL for * * writes outside this range. */ void sdio_f0_writeb(struct sdio_func *func, unsigned char b, unsigned int addr, int *err_ret) { int ret; BUG_ON(!func); /*allow SDIO FN0 writes outside of VS CCCR*/ #define MMC_QUIRK_LENIENT_FUNC0 (1<<1) if ((addr < 0xF0 || addr > 0xFF) && (!(func->card->quirks & MMC_QUIRK_LENIENT_FUNC0)) #if defined(CONFIG_MACH_OMAP3530_LV_SOM) || defined(CONFIG_MACH_DM3730_SOM_LV) && (!(func->card->host->caps & MMC_CAP_FUNKY_F0_WRITEB_STATUS)) #endif ) { if (err_ret) *err_ret = -EINVAL; return; } ret = mmc_io_rw_direct(func->card, 1, 0, addr, b, NULL); if (err_ret) *err_ret = ret; }
static int process_sdio_pending_irqs(struct mmc_host *host) { struct mmc_card *card = host->card; int i, ret, count; unsigned char pending; struct sdio_func *func; /* * Optimization, if there is only 1 function interrupt registered * and we know an IRQ was signaled then call irq handler directly. * Otherwise do the full probe. */ func = card->sdio_single_irq; if (func && host->sdio_irq_pending) { func->irq_handler(func); return 1; } ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, &pending); if (ret) { pr_debug("%s: error %d reading SDIO_CCCR_INTx\n", mmc_card_id(card), ret); return ret; } if (pending && mmc_card_broken_irq_polling(card) && !(host->caps & MMC_CAP_SDIO_IRQ)) { unsigned char dummy; /* A fake interrupt could be created when we poll SDIO_CCCR_INTx * register with a Marvell SD8797 card. A dummy CMD52 read to * function 0 register 0xff can avoid this. */ mmc_io_rw_direct(card, 0, 0, 0xff, 0, &dummy); } count = 0; for (i = 1; i <= 7; i++) { if (pending & (1 << i)) { func = card->sdio_func[i - 1]; if (!func) { pr_warning("%s: pending IRQ for " "non-existent function\n", mmc_card_id(card)); ret = -EINVAL; } else if (func->irq_handler) { func->irq_handler(func); count++; } else { pr_warning("%s: pending IRQ with no handler\n", sdio_func_id(func)); ret = -EINVAL; } } } if (count) return count; return ret; }
static int sdio_irq_thread(void *_host) { struct mmc_host *host = _host; struct sched_param param = { .sched_priority = 1 }; unsigned long period, idle_period; int ret; sched_setscheduler(current, SCHED_FIFO, ¶m); /* * We want to allow for SDIO cards to work even on non SDIO * aware hosts. One thing that non SDIO host cannot do is * asynchronous notification of pending SDIO card interrupts * hence we poll for them in that case. */ idle_period = msecs_to_jiffies(10); period = (host->caps & MMC_CAP_SDIO_IRQ) ? MAX_SCHEDULE_TIMEOUT : idle_period; pr_debug("%s: IRQ thread started (poll period = %lu jiffies)\n", mmc_hostname(host), period); do { /* * We claim the host here on drivers behalf for a couple * reasons: * * 1) it is already needed to retrieve the CCCR_INTx; * 2) we want the driver(s) to clear the IRQ condition ASAP; * 3) we need to control the abort condition locally. * * Just like traditional hard IRQ handlers, we expect SDIO * IRQ handlers to be quick and to the point, so that the * holding of the host lock does not cover too much work * that doesn't require that lock to be held. */ ret = __mmc_claim_host(host, &host->sdio_irq_thread_abort); if (ret) break; ret = process_sdio_pending_irqs(host); host->sdio_irq_pending = false; mmc_release_host(host); /* * Give other threads a chance to run in the presence of * errors. */ if (ret < 0) { set_current_state(TASK_INTERRUPTIBLE); if (!kthread_should_stop()) schedule_timeout(HZ); set_current_state(TASK_RUNNING); } /* * Adaptive polling frequency based on the assumption * that an interrupt will be closely followed by more. * This has a substantial benefit for network devices. */ if (!(host->caps & MMC_CAP_SDIO_IRQ)) { if (ret > 0) period /= 2; else { period++; if (period > idle_period) period = idle_period; } } set_current_state(TASK_INTERRUPTIBLE); if (host->caps & MMC_CAP_SDIO_IRQ) { mmc_host_clk_hold(host); host->ops->enable_sdio_irq(host, 1); mmc_host_clk_release(host); } if (!kthread_should_stop()) schedule_timeout(period); set_current_state(TASK_RUNNING); } while (!kthread_should_stop()); if (host->caps & MMC_CAP_SDIO_IRQ) { mmc_host_clk_hold(host); host->ops->enable_sdio_irq(host, 0); mmc_host_clk_release(host); } pr_debug("%s: IRQ thread exiting with code %d\n", mmc_hostname(host), ret); return ret; } static int sdio_card_irq_get(struct mmc_card *card) { struct mmc_host *host = card->host; WARN_ON(!host->claimed); if (!host->sdio_irqs++) { if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) { atomic_set(&host->sdio_irq_thread_abort, 0); host->sdio_irq_thread = kthread_run(sdio_irq_thread, host, "ksdioirqd/%s", mmc_hostname(host)); if (IS_ERR(host->sdio_irq_thread)) { int err = PTR_ERR(host->sdio_irq_thread); host->sdio_irqs--; return err; } } else { mmc_host_clk_hold(host); host->ops->enable_sdio_irq(host, 1); mmc_host_clk_release(host); } } return 0; } static int sdio_card_irq_put(struct mmc_card *card) { struct mmc_host *host = card->host; WARN_ON(!host->claimed); BUG_ON(host->sdio_irqs < 1); if (!--host->sdio_irqs) { if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) { atomic_set(&host->sdio_irq_thread_abort, 1); kthread_stop(host->sdio_irq_thread); } else { mmc_host_clk_hold(host); host->ops->enable_sdio_irq(host, 0); mmc_host_clk_release(host); } } return 0; } /* If there is only 1 function registered set sdio_single_irq */ static void sdio_single_irq_set(struct mmc_card *card) { struct sdio_func *func; int i; card->sdio_single_irq = NULL; if ((card->host->caps & MMC_CAP_SDIO_IRQ) && card->host->sdio_irqs == 1) for (i = 0; i < card->sdio_funcs; i++) { func = card->sdio_func[i]; if (func && func->irq_handler) { card->sdio_single_irq = func; break; } } } /** * sdio_claim_irq - claim the IRQ for a SDIO function * @func: SDIO function * @handler: IRQ handler callback * * Claim and activate the IRQ for the given SDIO function. The provided * handler will be called when that IRQ is asserted. The host is always * claimed already when the handler is called so the handler must not * call sdio_claim_host() nor sdio_release_host(). */ int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler) { int ret; unsigned char reg; BUG_ON(!func); BUG_ON(!func->card); pr_debug("SDIO: Enabling IRQ for %s...\n", sdio_func_id(func)); if (func->irq_handler) { pr_debug("SDIO: IRQ for %s already in use.\n", sdio_func_id(func)); return -EBUSY; } ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, ®); if (ret) return ret; reg |= 1 << func->num; reg |= 1; /* Master interrupt enable */ ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL); if (ret) return ret; func->irq_handler = handler; ret = sdio_card_irq_get(func->card); if (ret) func->irq_handler = NULL; sdio_single_irq_set(func->card); return ret; }
static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func) { int ret; struct sdio_func_tuple *this, **prev; unsigned i, ptr = 0; /* */ for (i = 0; i < 3; i++) { unsigned char x, fn; if (func) fn = func->num; else fn = 0; ret = mmc_io_rw_direct(card, 0, 0, SDIO_FBR_BASE(fn) + SDIO_FBR_CIS + i, 0, &x); if (ret) return ret; ptr |= x << (i * 8); } if (func) prev = &func->tuples; else prev = &card->tuples; BUG_ON(*prev); do { unsigned char tpl_code, tpl_link; ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_code); if (ret) break; /* */ if (tpl_code == 0xff) break; /* */ if (tpl_code == 0x00) { if (card->cis.vendor == 0x70 && (card->cis.device == 0x2460 || card->cis.device == 0x0460 || card->cis.device == 0x23F1 || card->cis.device == 0x23F0)) break; else continue; } ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_link); if (ret) break; /* */ if (tpl_link == 0xff) break; this = kmalloc(sizeof(*this) + tpl_link, GFP_KERNEL); if (!this) return -ENOMEM; for (i = 0; i < tpl_link; i++) { ret = mmc_io_rw_direct(card, 0, 0, ptr + i, 0, &this->data[i]); if (ret) break; } if (ret) { kfree(this); break; } /* */ ret = cis_tpl_parse(card, func, "CIS", cis_tpl_list, ARRAY_SIZE(cis_tpl_list), tpl_code, this->data, tpl_link); if (ret == -EILSEQ || ret == -ENOENT) { /* */ this->next = NULL; this->code = tpl_code; this->size = tpl_link; *prev = this; prev = &this->next; if (ret == -ENOENT) { /* */ pr_warning("%s: queuing unknown" " CIS tuple 0x%02x (%u bytes)\n", mmc_hostname(card->host), tpl_code, tpl_link); } /* */ ret = 0; } else { /* */ kfree(this); } ptr += tpl_link; } while (!ret); /* */ if (func) *prev = card->tuples; return ret; }
static int sdio_irq_thread(void *_host) { struct mmc_host *host = _host; struct sched_param param = { .sched_priority = 1 }; unsigned long period, idle_period; int ret; sched_setscheduler(current, SCHED_FIFO, ¶m); idle_period = msecs_to_jiffies(10); period = (host->caps & MMC_CAP_SDIO_IRQ) ? MAX_SCHEDULE_TIMEOUT : idle_period; pr_debug("%s: IRQ thread started (poll period = %lu jiffies)\n", mmc_hostname(host), period); do { ret = __mmc_claim_host(host, &host->sdio_irq_thread_abort); if (ret) break; ret = process_sdio_pending_irqs(host); host->sdio_irq_pending = false; mmc_release_host(host); if (ret < 0) { set_current_state(TASK_INTERRUPTIBLE); if (!kthread_should_stop()) schedule_timeout(HZ); set_current_state(TASK_RUNNING); } if (!(host->caps & MMC_CAP_SDIO_IRQ)) { if (ret > 0) period /= 2; else { period++; if (period > idle_period) period = idle_period; } } set_current_state(TASK_INTERRUPTIBLE); if (host->caps & MMC_CAP_SDIO_IRQ) { mmc_host_clk_hold(host); host->ops->enable_sdio_irq(host, 1); mmc_host_clk_release(host); } if (!kthread_should_stop()) schedule_timeout(period); set_current_state(TASK_RUNNING); } while (!kthread_should_stop()); if (host->caps & MMC_CAP_SDIO_IRQ) { mmc_host_clk_hold(host); host->ops->enable_sdio_irq(host, 0); mmc_host_clk_release(host); } pr_debug("%s: IRQ thread exiting with code %d\n", mmc_hostname(host), ret); return ret; } static int sdio_card_irq_get(struct mmc_card *card) { struct mmc_host *host = card->host; WARN_ON(!host->claimed); if (!host->sdio_irqs++) { atomic_set(&host->sdio_irq_thread_abort, 0); host->sdio_irq_thread = kthread_run(sdio_irq_thread, host, "ksdioirqd/%s", mmc_hostname(host)); if (IS_ERR(host->sdio_irq_thread)) { int err = PTR_ERR(host->sdio_irq_thread); host->sdio_irqs--; return err; } if (host->caps & MMC_CAP_SDIO_IRQ) { mmc_host_clk_hold(host); host->ops->enable_sdio_irq(host, 1); mmc_host_clk_release(host); } } return 0; } static int sdio_card_irq_put(struct mmc_card *card) { struct mmc_host *host = card->host; WARN_ON(!host->claimed); BUG_ON(host->sdio_irqs < 1); if (host->sdio_irqs == 1) { if (host->caps & MMC_CAP_SDIO_IRQ) { mmc_host_clk_hold(host); host->ops->enable_sdio_irq(host, 0); mmc_host_clk_release(host); } } if (!--host->sdio_irqs) { atomic_set(&host->sdio_irq_thread_abort, 1); kthread_stop(host->sdio_irq_thread); } return 0; } static void sdio_single_irq_set(struct mmc_card *card) { struct sdio_func *func; int i; card->sdio_single_irq = NULL; if ((card->host->caps & MMC_CAP_SDIO_IRQ) && card->host->sdio_irqs == 1) for (i = 0; i < card->sdio_funcs; i++) { func = card->sdio_func[i]; if (func && func->irq_handler) { card->sdio_single_irq = func; break; } } } int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler) { int ret; unsigned char reg; BUG_ON(!func); BUG_ON(!func->card); pr_debug("SDIO: Enabling IRQ for %s...\n", sdio_func_id(func)); if (func->irq_handler) { pr_debug("SDIO: IRQ for %s already in use.\n", sdio_func_id(func)); return -EBUSY; } ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, ®); if (ret) return ret; reg |= 1 << func->num; reg |= 1; ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL); if (ret) return ret; func->irq_handler = handler; ret = sdio_card_irq_get(func->card); if (ret) func->irq_handler = NULL; sdio_single_irq_set(func->card); return ret; }
static int process_sdio_pending_irqs(struct mmc_card *card) { int i, ret, count; unsigned char pending; struct sdio_func *func; // 20120317 [email protected][wo0gi] QCT patch : SDIO kernel crash [START] unsigned char reg; // 20120317 [email protected][wo0gi] QCT patch : SDIO kernel crash [END] /* * Optimization, if there is only 1 function interrupt registered * call irq handler directly */ // 20120317 [email protected][wo0gi] QCT patch : SDIO kernel crash [START] #if 0 func = card->sdio_single_irq; if (func) { func->irq_handler(func); return 1; } #endif ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_IENx, 0, ®); if (ret) { printk(KERN_ERR "%s: error %d reading SDIO_CCCR_IENx\n", mmc_card_id(card), ret); return ret; } // 20120317 [email protected][wo0gi] QCT patch : SDIO kernel crash [END] ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, &pending); if (ret) { printk(KERN_DEBUG "%s: error %d reading SDIO_CCCR_INTx\n", mmc_card_id(card), ret); return ret; } count = 0; for (i = 1; i <= 7; i++) { if (pending & (1 << i)) { // 20120317 [email protected][wo0gi] QCT patch : SDIO kernel crash [START] if (!(reg & 0x1)) { pr_err("%s: Master interrupt is disabled but still " "we have pending interrupt, bug in h/w??\n", __func__); return -EINVAL; } // 20120317 [email protected][wo0gi] QCT patch : SDIO kernel crash [END] func = card->sdio_func[i - 1]; if (!func) { printk(KERN_WARNING "%s: pending IRQ for " "non-existent function\n", mmc_card_id(card)); ret = -EINVAL; } else if (func->irq_handler) { // 20120317 [email protected][wo0gi] QCT patch : SDIO kernel crash [START] if ((reg & (1 << func->num))) { // 20120317 [email protected][wo0gi] QCT patch : SDIO kernel crash [END] func->irq_handler(func); count++; // 20120317 [email protected][wo0gi] QCT patch : SDIO kernel crash [START] } else { pr_err("%s: Interrupt ocurred even when IEx " "bit is not set, bug in h/w??\n", mmc_card_id(card)); ret = -EINVAL; } // 20120317 [email protected][wo0gi] QCT patch : SDIO kernel crash [END] } else { printk(KERN_WARNING "%s: pending IRQ with no handler\n", sdio_func_id(func)); ret = -EINVAL; } } } if (count) return count; return ret; }
void mmc_rescan(struct work_struct *work) { struct mmc_host *host = container_of(work, struct mmc_host, detect.work); u32 ocr; int err; struct mmc_card card; mmc_bus_get(host); /* if there is a card registered, check whether it is still present */ if ((host->bus_ops != NULL) && host->bus_ops->detect && !host->bus_dead) host->bus_ops->detect(host); mmc_bus_put(host); mmc_bus_get(host); /* if there still is a card present, stop here */ if (host->bus_ops != NULL) { mmc_bus_put(host); goto out; } /* detect a newly inserted card */ /* * Only we can add a new handler, so it's safe to * release the lock here. */ mmc_bus_put(host); if (host->ops->get_cd && host->ops->get_cd(host) == 0) goto out; mmc_claim_host(host); mmc_power_up(host); sdio_reset(host); mmc_go_idle(host); mmc_send_if_cond(host, host->ocr_avail); /* * First we search for SDIO... */ // Change for EP7 card.host = host; mmc_io_rw_direct(&card, 1, 0, SDIO_CCCR_ABORT, 0x08, NULL); err = mmc_send_io_op_cond(host, 0, &ocr); if (!err) { if (mmc_attach_sdio(host, ocr)) mmc_power_off(host); goto out; } /* * ...then normal SD... */ err = mmc_send_app_op_cond(host, 0, &ocr); if (!err) { if (mmc_attach_sd(host, ocr)) mmc_power_off(host); goto out; } /* * ...and finally MMC. */ err = mmc_send_op_cond(host, 0, &ocr); if (!err) { if (mmc_attach_mmc(host, ocr)) mmc_power_off(host); goto out; } mmc_release_host(host); mmc_power_off(host); out: if (host->caps & MMC_CAP_NEEDS_POLL) mmc_schedule_delayed_work(&host->detect, HZ); }
#ifdef CONFIG_MMC_EMBEDDED_SDIO #include <linux/mmc/sdio_ids.h> #endif static int sdio_read_fbr(struct sdio_func *func) { int ret; unsigned char data; if (mmc_card_nonstd_func_interface(func->card)) { func->class = SDIO_CLASS_NONE; return 0; } ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_FBR_BASE(func->num) + SDIO_FBR_STD_IF, 0, &data); if (ret) goto out; data &= 0x0f; if (data == 0x0f) { ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_FBR_BASE(func->num) + SDIO_FBR_STD_IF_EXT, 0, &data); if (ret) goto out; } func->class = data; out:
static int sdio_read_cis(struct mmc_host *host) { int ret = 0; unsigned int i, ptr = 0; unsigned int vendor, device; struct sdio_func_tuple *this; for (i = 0; i < 3; i++) { unsigned char x, fn = 0; ret = mmc_io_rw_direct(host, 0, 0, SDIO_FBR_BASE(fn) + SDIO_FBR_CIS + i, 0, &x); if (ret) return ret; ptr |= x << (i * 8); } DPRINT("ptr = 0x%x\n", ptr); do { unsigned char tpl_code, tpl_link; ret = mmc_io_rw_direct(host, 0, 0, ptr++, 0, &tpl_code); if (ret) break; /* 0xff means we're done */ if (tpl_code == 0xff) break; /* null entries have no link field or data */ if (tpl_code == 0x00) continue; ret = mmc_io_rw_direct(host, 0, 0, ptr++, 0, &tpl_link); if (ret) break; /* a size of 0xff also means we're done */ if (tpl_link == 0xff) break; this = malloc(sizeof(*this) + tpl_link); if (!this) return -ENOMEM; for (i = 0; i < tpl_link; i++) { ret = mmc_io_rw_direct(host, 0, 0, ptr + i, 0, &this->data[i]); if (ret) break; } if (ret) { free(this); break; } ptr += tpl_link; if (tpl_code == 0x20) { if (tpl_link < 4) { printf("bad CIS tuple len = %d\n", tpl_link); return -EINVAL; } vendor = this->data[0] | (this->data[1] << 8); device = this->data[2] | (this->data[3] << 8); printf("vendor = 0x%x, device = 0x%x\n", vendor, device); } } while (!ret); return ret; }
static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func) { int ret; struct sdio_func_tuple *this, **prev; unsigned i, ptr = 0; /* * Note that this works for the common CIS (function number 0) as * well as a function's CIS * since SDIO_CCCR_CIS and SDIO_FBR_CIS * have the same offset. */ for (i = 0; i < 3; i++) { unsigned char x, fn; if (func) fn = func->num; else fn = 0; ret = mmc_io_rw_direct(card, 0, 0, SDIO_FBR_BASE(fn) + SDIO_FBR_CIS + i, 0, &x); if (ret) return ret; ptr |= x << (i * 8); } if (func) prev = &func->tuples; else prev = &card->tuples; if (*prev) return -EINVAL; do { unsigned char tpl_code, tpl_link; ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_code); if (ret) break; /* 0xff means we're done */ if (tpl_code == 0xff) break; /* null entries have no link field or data */ if (tpl_code == 0x00) continue; ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_link); if (ret) break; /* a size of 0xff also means we're done */ if (tpl_link == 0xff) break; this = kmalloc(sizeof(*this) + tpl_link, GFP_KERNEL); if (!this) return -ENOMEM; for (i = 0; i < tpl_link; i++) { ret = mmc_io_rw_direct(card, 0, 0, ptr + i, 0, &this->data[i]); if (ret) break; } if (ret) { kfree(this); break; } /* Try to parse the CIS tuple */ ret = cis_tpl_parse(card, func, "CIS", cis_tpl_list, ARRAY_SIZE(cis_tpl_list), tpl_code, this->data, tpl_link); if (ret == -EILSEQ || ret == -ENOENT) { /* * The tuple is unknown or known but not parsed. * Queue the tuple for the function driver. */ this->next = NULL; this->code = tpl_code; this->size = tpl_link; *prev = this; prev = &this->next; if (ret == -ENOENT) { /* warn about unknown tuples */ pr_warn_ratelimited("%s: queuing unknown" " CIS tuple 0x%02x (%u bytes)\n", mmc_hostname(card->host), tpl_code, tpl_link); } /* keep on analyzing tuples */ ret = 0; } else { /* * We don't need the tuple anymore if it was * successfully parsed by the SDIO core or if it is * not going to be queued for a driver. */ kfree(this); } ptr += tpl_link; } while (!ret); /* * Link in all unknown tuples found in the common CIS so that * drivers don't have to go digging in two places. */ if (func) *prev = card->tuples; return ret; }