Skip to content

Commit

Permalink
Merge commit '32dedbc22e44' into sunxi-next
Browse files Browse the repository at this point in the history
Merge mmc/next up to 32dedbc ("mmc: sunxi: Add card busy
detection")

This should fix SDIO WiFi devices not responding properly.
  • Loading branch information
wens committed Sep 27, 2015
2 parents 690ec25 + 32dedbc commit 8340ea9
Show file tree
Hide file tree
Showing 26 changed files with 286 additions and 123 deletions.
206 changes: 151 additions & 55 deletions drivers/mmc/card/block.c
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,24 @@ static struct mmc_blk_ioc_data *mmc_blk_ioctl_copy_from_user(
return ERR_PTR(err);
}

static int mmc_blk_ioctl_copy_to_user(struct mmc_ioc_cmd __user *ic_ptr,
struct mmc_blk_ioc_data *idata)
{
struct mmc_ioc_cmd *ic = &idata->ic;

if (copy_to_user(&(ic_ptr->response), ic->response,
sizeof(ic->response)))
return -EFAULT;

if (!idata->ic.write_flag) {
if (copy_to_user((void __user *)(unsigned long)ic->data_ptr,
idata->buf, idata->buf_bytes))
return -EFAULT;
}

return 0;
}

static int ioctl_rpmb_card_status_poll(struct mmc_card *card, u32 *status,
u32 retries_max)
{
Expand Down Expand Up @@ -447,12 +465,9 @@ static int ioctl_do_sanitize(struct mmc_card *card)
return err;
}

static int mmc_blk_ioctl_cmd(struct block_device *bdev,
struct mmc_ioc_cmd __user *ic_ptr)
static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
struct mmc_blk_ioc_data *idata)
{
struct mmc_blk_ioc_data *idata;
struct mmc_blk_data *md;
struct mmc_card *card;
struct mmc_command cmd = {0};
struct mmc_data data = {0};
struct mmc_request mrq = {NULL};
Expand All @@ -461,33 +476,12 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
int is_rpmb = false;
u32 status = 0;

/*
* The caller must have CAP_SYS_RAWIO, and must be calling this on the
* whole block device, not on a partition. This prevents overspray
* between sibling partitions.
*/
if ((!capable(CAP_SYS_RAWIO)) || (bdev != bdev->bd_contains))
return -EPERM;

idata = mmc_blk_ioctl_copy_from_user(ic_ptr);
if (IS_ERR(idata))
return PTR_ERR(idata);

md = mmc_blk_get(bdev->bd_disk);
if (!md) {
err = -EINVAL;
goto cmd_err;
}
if (!card || !md || !idata)
return -EINVAL;

if (md->area_type & MMC_BLK_DATA_AREA_RPMB)
is_rpmb = true;

card = md->queue.card;
if (IS_ERR(card)) {
err = PTR_ERR(card);
goto cmd_done;
}

cmd.opcode = idata->ic.opcode;
cmd.arg = idata->ic.arg;
cmd.flags = idata->ic.flags;
Expand Down Expand Up @@ -530,23 +524,21 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,

mrq.cmd = &cmd;

mmc_get_card(card);

err = mmc_blk_part_switch(card, md);
if (err)
goto cmd_rel_host;
return err;

if (idata->ic.is_acmd) {
err = mmc_app_cmd(card->host, card);
if (err)
goto cmd_rel_host;
return err;
}

if (is_rpmb) {
err = mmc_set_blockcount(card, data.blocks,
idata->ic.write_flag & (1 << 31));
if (err)
goto cmd_rel_host;
return err;
}

if ((MMC_EXTRACT_INDEX_FROM_ARG(cmd.arg) == EXT_CSD_SANITIZE_START) &&
Expand All @@ -557,22 +549,20 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
pr_err("%s: ioctl_do_sanitize() failed. err = %d",
__func__, err);

goto cmd_rel_host;
return err;
}

mmc_wait_for_req(card->host, &mrq);

if (cmd.error) {
dev_err(mmc_dev(card->host), "%s: cmd error %d\n",
__func__, cmd.error);
err = cmd.error;
goto cmd_rel_host;
return cmd.error;
}
if (data.error) {
dev_err(mmc_dev(card->host), "%s: data error %d\n",
__func__, data.error);
err = data.error;
goto cmd_rel_host;
return data.error;
}

/*
Expand All @@ -582,18 +572,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
if (idata->ic.postsleep_min_us)
usleep_range(idata->ic.postsleep_min_us, idata->ic.postsleep_max_us);

if (copy_to_user(&(ic_ptr->response), cmd.resp, sizeof(cmd.resp))) {
err = -EFAULT;
goto cmd_rel_host;
}

if (!idata->ic.write_flag) {
if (copy_to_user((void __user *)(unsigned long) idata->ic.data_ptr,
idata->buf, idata->buf_bytes)) {
err = -EFAULT;
goto cmd_rel_host;
}
}
memcpy(&(idata->ic.response), cmd.resp, sizeof(cmd.resp));

if (is_rpmb) {
/*
Expand All @@ -607,9 +586,42 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
__func__, status, err);
}

cmd_rel_host:
return err;
}

static int mmc_blk_ioctl_cmd(struct block_device *bdev,
struct mmc_ioc_cmd __user *ic_ptr)
{
struct mmc_blk_ioc_data *idata;
struct mmc_blk_data *md;
struct mmc_card *card;
int err;

idata = mmc_blk_ioctl_copy_from_user(ic_ptr);
if (IS_ERR(idata))
return PTR_ERR(idata);

md = mmc_blk_get(bdev->bd_disk);
if (!md) {
err = -EINVAL;
goto cmd_err;
}

card = md->queue.card;
if (IS_ERR(card)) {
err = PTR_ERR(card);
goto cmd_done;
}

mmc_get_card(card);

err = __mmc_blk_ioctl_cmd(card, md, idata);

mmc_put_card(card);

if (!err)
err = mmc_blk_ioctl_copy_to_user(ic_ptr, idata);

cmd_done:
mmc_blk_put(md);
cmd_err:
Expand All @@ -618,13 +630,97 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
return err;
}

static int mmc_blk_ioctl_multi_cmd(struct block_device *bdev,
struct mmc_ioc_multi_cmd __user *user)
{
struct mmc_blk_ioc_data **idata = NULL;
struct mmc_ioc_cmd __user *cmds = user->cmds;
struct mmc_card *card;
struct mmc_blk_data *md;
int i, err = -EFAULT;
__u64 num_of_cmds;

if (copy_from_user(&num_of_cmds, &user->num_of_cmds,
sizeof(num_of_cmds)))
return -EFAULT;

if (num_of_cmds > MMC_IOC_MAX_CMDS)
return -EINVAL;

idata = kcalloc(num_of_cmds, sizeof(*idata), GFP_KERNEL);
if (!idata)
return -ENOMEM;

for (i = 0; i < num_of_cmds; i++) {
idata[i] = mmc_blk_ioctl_copy_from_user(&cmds[i]);
if (IS_ERR(idata[i])) {
err = PTR_ERR(idata[i]);
num_of_cmds = i;
goto cmd_err;
}
}

md = mmc_blk_get(bdev->bd_disk);
if (!md)
goto cmd_err;

card = md->queue.card;
if (IS_ERR(card)) {
err = PTR_ERR(card);
goto cmd_done;
}

mmc_get_card(card);

for (i = 0; i < num_of_cmds; i++) {
err = __mmc_blk_ioctl_cmd(card, md, idata[i]);
if (err) {
mmc_put_card(card);
goto cmd_done;
}
}

mmc_put_card(card);

/* copy to user if data and response */
for (i = 0; i < num_of_cmds; i++) {
err = mmc_blk_ioctl_copy_to_user(&cmds[i], idata[i]);
if (err)
break;
}

cmd_done:
mmc_blk_put(md);
cmd_err:
for (i = 0; i < num_of_cmds; i++) {
kfree(idata[i]->buf);
kfree(idata[i]);
}
kfree(idata);
return err;
}

static int mmc_blk_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long arg)
{
int ret = -EINVAL;
if (cmd == MMC_IOC_CMD)
ret = mmc_blk_ioctl_cmd(bdev, (struct mmc_ioc_cmd __user *)arg);
return ret;
/*
* The caller must have CAP_SYS_RAWIO, and must be calling this on the
* whole block device, not on a partition. This prevents overspray
* between sibling partitions.
*/
if ((!capable(CAP_SYS_RAWIO)) || (bdev != bdev->bd_contains))
return -EPERM;

switch (cmd) {
case MMC_IOC_CMD:
return mmc_blk_ioctl_cmd(bdev,
(struct mmc_ioc_cmd __user *)arg);
case MMC_IOC_MULTI_CMD:
return mmc_blk_ioctl_multi_cmd(bdev,
(struct mmc_ioc_multi_cmd __user *)arg);
default:
return -EINVAL;
}
}

#ifdef CONFIG_COMPAT
Expand Down
23 changes: 23 additions & 0 deletions drivers/mmc/core/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,23 @@ static void __mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
return;
}

/*
* For sdio rw commands we must wait for card busy otherwise some
* sdio devices won't work properly.
*/
if (mmc_is_io_op(mrq->cmd->opcode) && host->ops->card_busy) {
int tries = 500; /* Wait aprox 500ms at maximum */

while (host->ops->card_busy(host) && --tries)
mmc_delay(1);

if (tries == 0) {
mrq->cmd->error = -EBUSY;
mmc_request_done(host, mrq);
return;
}
}

host->ops->request(host, mrq);
}

Expand Down Expand Up @@ -2631,10 +2648,14 @@ void mmc_start_host(struct mmc_host *host)
host->f_init = max(freqs[0], host->f_min);
host->rescan_disable = 0;
host->ios.power_mode = MMC_POWER_UNDEFINED;

mmc_claim_host(host);
if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)
mmc_power_off(host);
else
mmc_power_up(host, host->ocr_avail);
mmc_release_host(host);

mmc_gpiod_request_cd_irq(host);
_mmc_detect_change(host, 0, false);
}
Expand Down Expand Up @@ -2672,7 +2693,9 @@ void mmc_stop_host(struct mmc_host *host)

BUG_ON(host->card);

mmc_claim_host(host);
mmc_power_off(host);
mmc_release_host(host);
}

int mmc_power_save_host(struct mmc_host *host)
Expand Down
19 changes: 19 additions & 0 deletions drivers/mmc/core/debugfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,25 @@ static int mmc_ios_show(struct seq_file *s, void *data)
}
seq_printf(s, "signal voltage:\t%u (%s)\n", ios->chip_select, str);

switch (ios->drv_type) {
case MMC_SET_DRIVER_TYPE_A:
str = "driver type A";
break;
case MMC_SET_DRIVER_TYPE_B:
str = "driver type B";
break;
case MMC_SET_DRIVER_TYPE_C:
str = "driver type C";
break;
case MMC_SET_DRIVER_TYPE_D:
str = "driver type D";
break;
default:
str = "invalid";
break;
}
seq_printf(s, "driver type:\t%u (%s)\n", ios->drv_type, str);

return 0;
}

Expand Down
4 changes: 2 additions & 2 deletions drivers/mmc/core/host.c
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,7 @@ int mmc_of_parse(struct mmc_host *host)
0, &cd_gpio_invert);
if (!ret)
dev_info(host->parent, "Got CD GPIO\n");
else if (ret != -ENOENT)
else if (ret != -ENOENT && ret != -ENOSYS)
return ret;

/*
Expand All @@ -481,7 +481,7 @@ int mmc_of_parse(struct mmc_host *host)
ret = mmc_gpiod_request_ro(host, "wp", 0, false, 0, &ro_gpio_invert);
if (!ret)
dev_info(host->parent, "Got WP GPIO\n");
else if (ret != -ENOENT)
else if (ret != -ENOENT && ret != -ENOSYS)
return ret;

if (of_property_read_bool(np, "disable-wp"))
Expand Down
1 change: 0 additions & 1 deletion drivers/mmc/core/mmc_ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,6 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,

return err;
}
EXPORT_SYMBOL_GPL(__mmc_switch);

int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
unsigned int timeout_ms)
Expand Down

0 comments on commit 8340ea9

Please sign in to comment.