diff options
Diffstat (limited to 'drivers/mmc/host/sdricoh_cs.c')
| -rw-r--r-- | drivers/mmc/host/sdricoh_cs.c | 141 |
1 files changed, 60 insertions, 81 deletions
diff --git a/drivers/mmc/host/sdricoh_cs.c b/drivers/mmc/host/sdricoh_cs.c index 70cb00aa79a0..481cb552c2b4 100644 --- a/drivers/mmc/host/sdricoh_cs.c +++ b/drivers/mmc/host/sdricoh_cs.c @@ -1,23 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * sdricoh_cs.c - driver for Ricoh Secure Digital Card Readers that can be * found on some Ricoh RL5c476 II cardbus bridge * * Copyright (C) 2006 - 2008 Sascha Sommer <saschasommer@freenet.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * */ /* @@ -29,6 +15,7 @@ #include <linux/module.h> #include <linux/pci.h> #include <linux/ioport.h> +#include <linux/iopoll.h> #include <linux/scatterlist.h> #include <pcmcia/cistpl.h> @@ -36,6 +23,7 @@ #include <linux/io.h> #include <linux/mmc/host.h> +#include <linux/mmc/mmc.h> #define DRIVER_NAME "sdricoh_cs" @@ -71,10 +59,8 @@ static unsigned int switchlocked; #define STATUS_BUSY 0x40000000 /* timeouts */ -#define INIT_TIMEOUT 100 -#define CMD_TIMEOUT 100000 -#define TRANSFER_TIMEOUT 100000 -#define BUSY_TIMEOUT 32767 +#define SDRICOH_CMD_TIMEOUT_US 1000000 +#define SDRICOH_DATA_TIMEOUT_US 1000000 /* list of supported pcmcia devices */ static const struct pcmcia_device_id pcmcia_ids[] = { @@ -115,14 +101,6 @@ static inline void sdricoh_writel(struct sdricoh_host *host, unsigned int reg, } -static inline unsigned int sdricoh_readw(struct sdricoh_host *host, - unsigned int reg) -{ - unsigned int value = readw(host->iobase + reg); - dev_vdbg(host->dev, "rb %x 0x%x\n", reg, value); - return value; -} - static inline void sdricoh_writew(struct sdricoh_host *host, unsigned int reg, unsigned short value) { @@ -138,19 +116,24 @@ static inline unsigned int sdricoh_readb(struct sdricoh_host *host, return value; } -static int sdricoh_query_status(struct sdricoh_host *host, unsigned int wanted, - unsigned int timeout){ - unsigned int loop; +static bool sdricoh_status_ok(struct sdricoh_host *host, unsigned int status, + unsigned int wanted) +{ + sdricoh_writel(host, R2E4_STATUS_RESP, status); + return status & wanted; +} + +static int sdricoh_query_status(struct sdricoh_host *host, unsigned int wanted) +{ + int ret; unsigned int status = 0; struct device *dev = host->dev; - for (loop = 0; loop < timeout; loop++) { - status = sdricoh_readl(host, R21C_STATUS); - sdricoh_writel(host, R2E4_STATUS_RESP, status); - if (status & wanted) - break; - } - if (loop == timeout) { + ret = read_poll_timeout(sdricoh_readl, status, + sdricoh_status_ok(host, status, wanted), + 32, SDRICOH_DATA_TIMEOUT_US, false, + host, R21C_STATUS); + if (ret) { dev_err(dev, "query_status: timeout waiting for %x\n", wanted); return -ETIMEDOUT; } @@ -164,35 +147,46 @@ static int sdricoh_query_status(struct sdricoh_host *host, unsigned int wanted, } -static int sdricoh_mmc_cmd(struct sdricoh_host *host, unsigned char opcode, - unsigned int arg) +static int sdricoh_mmc_cmd(struct sdricoh_host *host, struct mmc_command *cmd) { - unsigned int status; - int result = 0; - unsigned int loop = 0; + unsigned int status, timeout_us; + int ret; + unsigned char opcode = cmd->opcode; + /* reset status reg? */ sdricoh_writel(host, R21C_STATUS, 0x18); + + /* MMC_APP_CMDs need some special handling */ + if (host->app_cmd) { + opcode |= 64; + host->app_cmd = 0; + } else if (opcode == MMC_APP_CMD) + host->app_cmd = 1; + /* fill parameters */ - sdricoh_writel(host, R204_CMD_ARG, arg); + sdricoh_writel(host, R204_CMD_ARG, cmd->arg); sdricoh_writel(host, R200_CMD, (0x10000 << 8) | opcode); + /* wait for command completion */ - if (opcode) { - for (loop = 0; loop < CMD_TIMEOUT; loop++) { - status = sdricoh_readl(host, R21C_STATUS); - sdricoh_writel(host, R2E4_STATUS_RESP, status); - if (status & STATUS_CMD_FINISHED) - break; - } - /* don't check for timeout in the loop it is not always - reset correctly - */ - if (loop == CMD_TIMEOUT || status & STATUS_CMD_TIMEOUT) - result = -ETIMEDOUT; + if (!opcode) + return 0; - } + timeout_us = cmd->busy_timeout ? cmd->busy_timeout * 1000 : + SDRICOH_CMD_TIMEOUT_US; - return result; + ret = read_poll_timeout(sdricoh_readl, status, + sdricoh_status_ok(host, status, STATUS_CMD_FINISHED), + 32, timeout_us, false, + host, R21C_STATUS); + + /* + * Don't check for timeout status in the loop, as it's not always reset + * correctly. + */ + if (ret || status & STATUS_CMD_TIMEOUT) + return -ETIMEDOUT; + return 0; } static int sdricoh_reset(struct sdricoh_host *host) @@ -221,8 +215,7 @@ static int sdricoh_blockio(struct sdricoh_host *host, int read, u32 data = 0; /* wait until the data is available */ if (read) { - if (sdricoh_query_status(host, STATUS_READY_TO_READ, - TRANSFER_TIMEOUT)) + if (sdricoh_query_status(host, STATUS_READY_TO_READ)) return -ETIMEDOUT; sdricoh_writel(host, R21C_STATUS, 0x18); /* read data */ @@ -238,8 +231,7 @@ static int sdricoh_blockio(struct sdricoh_host *host, int read, } } } else { - if (sdricoh_query_status(host, STATUS_READY_TO_WRITE, - TRANSFER_TIMEOUT)) + if (sdricoh_query_status(host, STATUS_READY_TO_WRITE)) return -ETIMEDOUT; sdricoh_writel(host, R21C_STATUS, 0x18); /* write data */ @@ -265,28 +257,20 @@ static void sdricoh_request(struct mmc_host *mmc, struct mmc_request *mrq) struct mmc_command *cmd = mrq->cmd; struct mmc_data *data = cmd->data; struct device *dev = host->dev; - unsigned char opcode = cmd->opcode; int i; dev_dbg(dev, "=============================\n"); - dev_dbg(dev, "sdricoh_request opcode=%i\n", opcode); + dev_dbg(dev, "sdricoh_request opcode=%i\n", cmd->opcode); sdricoh_writel(host, R21C_STATUS, 0x18); - /* MMC_APP_CMDs need some special handling */ - if (host->app_cmd) { - opcode |= 64; - host->app_cmd = 0; - } else if (opcode == 55) - host->app_cmd = 1; - /* read/write commands seem to require this */ if (data) { sdricoh_writew(host, R226_BLOCKSIZE, data->blksz); sdricoh_writel(host, R208_DATAIO, 0); } - cmd->error = sdricoh_mmc_cmd(host, opcode, cmd->arg); + cmd->error = sdricoh_mmc_cmd(host, cmd); /* read response buffer */ if (cmd->flags & MMC_RSP_PRESENT) { @@ -337,8 +321,7 @@ static void sdricoh_request(struct mmc_host *mmc, struct mmc_request *mrq) sdricoh_writel(host, R208_DATAIO, 1); - if (sdricoh_query_status(host, STATUS_TRANSFER_FINISHED, - TRANSFER_TIMEOUT)) { + if (sdricoh_query_status(host, STATUS_TRANSFER_FINISHED)) { dev_err(dev, "sdricoh_request: transfer end error\n"); cmd->error = -EINVAL; } @@ -385,7 +368,7 @@ static int sdricoh_get_ro(struct mmc_host *mmc) return (status & STATUS_CARD_LOCKED); } -static struct mmc_host_ops sdricoh_ops = { +static const struct mmc_host_ops sdricoh_ops = { .request = sdricoh_request, .set_ios = sdricoh_set_ios, .get_ro = sdricoh_get_ro, @@ -420,9 +403,9 @@ static int sdricoh_init_mmc(struct pci_dev *pci_dev, } /* allocate privdata */ mmc = pcmcia_dev->priv = - mmc_alloc_host(sizeof(struct sdricoh_host), &pcmcia_dev->dev); + devm_mmc_alloc_host(&pcmcia_dev->dev, sizeof(*host)); if (!mmc) { - dev_err(dev, "mmc_alloc_host failed\n"); + dev_err(dev, "devm_mmc_alloc_host failed\n"); result = -ENOMEM; goto unmap_io; } @@ -448,7 +431,7 @@ static int sdricoh_init_mmc(struct pci_dev *pci_dev, if (sdricoh_reset(host)) { dev_dbg(dev, "could not reset\n"); result = -EIO; - goto free_host; + goto unmap_io; } result = mmc_add_host(mmc); @@ -457,8 +440,6 @@ static int sdricoh_init_mmc(struct pci_dev *pci_dev, dev_dbg(dev, "mmc host registered\n"); return 0; } -free_host: - mmc_free_host(mmc); unmap_io: pci_iounmap(pci_dev, iobase); return result; @@ -500,10 +481,8 @@ static void sdricoh_pcmcia_detach(struct pcmcia_device *link) mmc_remove_host(mmc); pci_iounmap(host->pci_dev, host->iobase); pci_dev_put(host->pci_dev); - mmc_free_host(mmc); } pcmcia_disable_device(link); - } #ifdef CONFIG_PM |
