Esempio n. 1
0
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, &reg);
	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;
}
Esempio n. 4
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;
}
Esempio n. 5
0
/**
 *	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, &reg);
	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, &reg);
		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;
}
Esempio n. 6
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, 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;
}
Esempio n. 7
0
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;
}
Esempio n. 8
0
/**
 *	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, &reg);
	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, &reg);
		if (ret)
			goto err;
		if (reg & (1 << func->num))
			break;
			
		ret = -ETIME;
		if (i++ >= 200)
			break;
		
		DelayUs(1000);
	}

	return 0;

err:
	return ret;
}
Esempio n. 9
0
/**
 *	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;
}
Esempio n. 10
0
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;
}
Esempio n. 11
0
/**
 *	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, &reg);
	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;
}
Esempio n. 12
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;

	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;
}
Esempio n. 13
0
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;
}
Esempio n. 15
0
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;
}
Esempio n. 16
0
/**
 *	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;
}
Esempio n. 17
0
/**
 *	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;
}
Esempio n. 18
0
/**
 *	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;
}
Esempio n. 19
0
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;
}
Esempio n. 20
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;
}
Esempio n. 21
0
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;
}
Esempio n. 22
0
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, &param);

	/*
	 * 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, &reg);
	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;
}
Esempio n. 23
0
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;
}
Esempio n. 24
0
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, &param);

    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, &reg);
    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;
}
Esempio n. 25
0
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, &reg);
	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;
}
Esempio n. 26
0
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);
}
Esempio n. 27
0
#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:
Esempio n. 28
0
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;
}
Esempio n. 29
0
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;
}