+/**
+ * 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;
+}
+EXPORT_SYMBOL_GPL(sdio_enable_func);
+
+/**
+ * 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;
+
+ BUG_ON(!func);
+ BUG_ON(!func->card);
+
+ pr_debug("SDIO: Disabling 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;
+
+ pr_debug("SDIO: Disabled device %s\n", sdio_func_id(func));
+
+ return 0;
+
+err:
+ pr_debug("SDIO: Failed to disable device %s\n", sdio_func_id(func));
+ return -EIO;
+}
+EXPORT_SYMBOL_GPL(sdio_disable_func);
+
+/**
+ * sdio_set_block_size - set the block size of an SDIO function
+ * @func: SDIO function to change
+ * @blksz: new block size or 0 to use the default.
+ *
+ * The default block size is the largest supported by both the function
+ * and the host, with a maximum of 512 to ensure that arbitrarily sized
+ * data transfer use the optimal (least) number of commands.
+ *
+ * A driver may call this to override the default block size set by the
+ * core. This can be used to set a block size greater than the maximum
+ * that reported by the card; it is the driver's responsibility to ensure
+ * it uses a value that the card supports.
+ *
+ * Returns 0 on success, -EINVAL if the host does not support the
+ * requested block size, or -EIO (etc.) if one of the resultant FBR block
+ * size register writes failed.
+ *
+ */
+int sdio_set_block_size(struct sdio_func *func, unsigned blksz)
+{
+ int ret;
+
+ if (blksz > func->card->host->max_blk_size)
+ return -EINVAL;
+
+ if (blksz == 0) {
+ blksz = min(func->max_blksize, func->card->host->max_blk_size);
+ blksz = min(blksz, 512u);
+ }
+
+ ret = mmc_io_rw_direct(func->card, 1, 0,
+ SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE,
+ blksz & 0xff, NULL);
+ if (ret)
+ return ret;
+ ret = mmc_io_rw_direct(func->card, 1, 0,
+ SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE + 1,
+ (blksz >> 8) & 0xff, NULL);
+ if (ret)
+ return ret;
+ func->cur_blksize = blksz;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sdio_set_block_size);
+
+/*
+ * Calculate the maximum byte mode transfer size
+ */
+static inline unsigned int sdio_max_byte_size(struct sdio_func *func)
+{
+ unsigned mval = min(func->card->host->max_seg_size,
+ func->card->host->max_blk_size);
+ mval = min(mval, func->max_blksize);
+ return min(mval, 512u); /* maximum size for byte mode */
+}
+
+/**
+ * sdio_align_size - pads a transfer size to a more optimal value
+ * @func: SDIO function
+ * @sz: original transfer size
+ *
+ * Pads the original data size with a number of extra bytes in
+ * order to avoid controller bugs and/or performance hits
+ * (e.g. some controllers revert to PIO for certain sizes).
+ *
+ * If possible, it will also adjust the size so that it can be
+ * handled in just a single request.
+ *
+ * Returns the improved size, which might be unmodified.
+ */
+unsigned int sdio_align_size(struct sdio_func *func, unsigned int sz)
+{
+ unsigned int orig_sz;
+ unsigned int blk_sz, byte_sz;
+ unsigned chunk_sz;
+
+ orig_sz = sz;
+
+ /*
+ * Do a first check with the controller, in case it
+ * wants to increase the size up to a point where it
+ * might need more than one block.
+ */
+ sz = mmc_align_data_size(func->card, sz);
+
+ /*
+ * If we can still do this with just a byte transfer, then
+ * we're done.
+ */
+ if (sz <= sdio_max_byte_size(func))
+ return sz;
+
+ if (func->card->cccr.multi_block) {
+ /*
+ * Check if the transfer is already block aligned
+ */
+ if ((sz % func->cur_blksize) == 0)
+ return sz;
+
+ /*
+ * Realign it so that it can be done with one request,
+ * and recheck if the controller still likes it.
+ */
+ blk_sz = ((sz + func->cur_blksize - 1) /
+ func->cur_blksize) * func->cur_blksize;
+ blk_sz = mmc_align_data_size(func->card, blk_sz);
+
+ /*
+ * This value is only good if it is still just
+ * one request.
+ */
+ if ((blk_sz % func->cur_blksize) == 0)
+ return blk_sz;
+
+ /*
+ * We failed to do one request, but at least try to
+ * pad the remainder properly.
+ */
+ byte_sz = mmc_align_data_size(func->card,
+ sz % func->cur_blksize);
+ if (byte_sz <= sdio_max_byte_size(func)) {
+ blk_sz = sz / func->cur_blksize;
+ return blk_sz * func->cur_blksize + byte_sz;
+ }
+ } else {
+ /*
+ * We need multiple requests, so first check that the
+ * controller can handle the chunk size;
+ */
+ chunk_sz = mmc_align_data_size(func->card,
+ sdio_max_byte_size(func));
+ if (chunk_sz == sdio_max_byte_size(func)) {
+ /*
+ * Fix up the size of the remainder (if any)
+ */
+ byte_sz = orig_sz % chunk_sz;
+ if (byte_sz) {
+ byte_sz = mmc_align_data_size(func->card,
+ byte_sz);
+ }
+
+ return (orig_sz / chunk_sz) * chunk_sz + byte_sz;
+ }
+ }
+
+ /*
+ * The controller is simply incapable of transferring the size
+ * we want in decent manner, so just return the original size.
+ */
+ return orig_sz;
+}
+EXPORT_SYMBOL_GPL(sdio_align_size);
+
+/* Split an arbitrarily sized data transfer into several
+ * IO_RW_EXTENDED commands. */
+static int sdio_io_rw_ext_helper(struct sdio_func *func, int write,
+ unsigned addr, int incr_addr, u8 *buf, unsigned size)
+{
+ unsigned remainder = size;
+ unsigned max_blocks;
+ int ret;
+
+ /* Do the bulk of the transfer using block mode (if supported). */
+ if (func->card->cccr.multi_block && (size > sdio_max_byte_size(func))) {
+ /* Blocks per command is limited by host count, host transfer
+ * size (we only use a single sg entry) and the maximum for
+ * IO_RW_EXTENDED of 511 blocks. */
+ max_blocks = min(func->card->host->max_blk_count,
+ func->card->host->max_seg_size / func->cur_blksize);
+ max_blocks = min(max_blocks, 511u);
+
+ while (remainder > func->cur_blksize) {
+ unsigned blocks;
+
+ blocks = remainder / func->cur_blksize;
+ if (blocks > max_blocks)
+ blocks = max_blocks;
+ size = blocks * func->cur_blksize;
+
+ ret = mmc_io_rw_extended(func->card, write,
+ func->num, addr, incr_addr, buf,
+ blocks, func->cur_blksize);
+ if (ret)
+ return ret;
+
+ remainder -= size;
+ buf += size;
+ if (incr_addr)
+ addr += size;
+ }
+ }
+
+ /* Write the remainder using byte mode. */
+ while (remainder > 0) {
+ size = min(remainder, sdio_max_byte_size(func));
+
+ ret = mmc_io_rw_extended(func->card, write, func->num, addr,
+ incr_addr, buf, 1, size);
+ if (ret)
+ return ret;
+
+ remainder -= size;
+ buf += size;
+ if (incr_addr)
+ addr += size;
+ }
+ return 0;
+}
+