Commit 0bae60fc authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'mmc-v4.16' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc

Pull MMC updates from Ulf Hansson:
 "There are two major achievements for MMC in this release, which
  deserves to be specially highlighted.

  First, we have converted the MMC block device from using the legacy
  blk interface into using the modern blkmq interface. Not only do we
  get all the nice effects from using blkmq, but it also means that new
  fresh nice code replaces old rusty code. Great news to everybody that
  cares about MMC/SD!

  It should also be noted that converting to blkmq has not been trivial,
  mostly because of that we have been carrying too much of MMC specific
  optimizations for the I/O request path, rather than striving to move
  these to the generic blk layer. Hopefully we won't be doing that
  mistake, ever again.

  Special thanks to Adrian Hunter (Intel) and to Linus Walleij (Linaro),
  who both have been working on this for quite some time!

  Second, on top of the blkmq deployment, we have enabled full support
  the eMMC command queuing feature, introduced in the eMMC v.5.1 spec.
  This also includes an implementation of a host driver library,
  supporting the corresponding CQHCI HW. Ideally, those controllers that
  supports CQHCI should only need some minor adaptations to make this
  play.

  So far the sdhci-pci driver for the Intel GLKs and the sdhci-of-arasan
  driver used on Rockchip RK3399, have enabled support for eMMC command
  queueing.

  Worth to highlight is also that, implementing the eMMC command queuing
  support has been a collaborative effort, as several people from
  Codeaurora, Rockchip, Intel and Linaro have been involved. However,
  the work has been driven by Adrian Hunter (Intel).

  In some shadow of the above, here are the rest of the highlights:

  MMC core:
   - Don't remove non-removable cards during system suspend
   - Add a slot-gpio helper to check capability of GPIO WP detection

  MMC host:
   - sdhci: Cleanups and improvements of some wakeup related code
   - sdhci-pci-arasan: New variant to support Arasan PCI HW with integrated phy
   - sdhci-acpi: Avoid broken UHS transfer modes on Intel CHT
   - sdhci-acpi: Add support for ACPI HID of AMD Controller with HS400
   - sdhci_f_sdh30: Add ACPI support
   - sdhci-esdhc-imx: Enable/disable clock at runtime suspend/resume
   - sdhci-of-esdhc: A few minor fixes
   - mmci: Add support for new STM32 variant
   - renesas_sdhi: enable R-Car D3 (r8a77995) support
   - tmio/renesas_sdhi: Re-structuring, cleanups and modernizations"

* tag 'mmc-v4.16' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (96 commits)
  mmc: mmci: fix error return code in mmci_probe()
  mmc: davinci: suppress error message on EPROBE_DEFER
  mmc: davinci: dont' use module_platform_driver_probe()
  mmc: tmio: hide unused tmio_mmc_clk_disable/tmio_mmc_clk_enable functions
  mmc: mmci: Add STM32 variant
  mmc: mmci: Add support for setting pad type via pinctrl
  mmc: mmci: Don't pretend all variants to have OPENDRAIN bit
  mmc: mmci: Don't pretend all variants to have MCI_STARBITERR flag
  mmc: mmci: Don't pretend all variants to have MMCIMASK1 register
  mmc: tmio: refactor .get_ro hook
  mmc: slot-gpio: add a helper to check capability of GPIO WP detection
  mmc: tmio: remove dma_ops from tmio_mmc_host_probe() argument
  mmc: tmio: move {tmio_}mmc_of_parse() to tmio_mmc_host_alloc()
  mmc: tmio: move clk_enable/disable out of tmio_mmc_host_probe()
  mmc: tmio: ioremap memory resource in tmio_mmc_host_alloc()
  mmc: sh_mmcif: remove redundant initialization of 'opc'
  mmc: sdhci: Rework sdhci_enable_irq_wakeups()
  mmc: sdhci: Handle failure of enable_irq_wake()
  mmc: sdhci: Stop exporting sdhci_enable_irq_wakeups()
  mmc: sdhci-pci: Use device wakeup capability to determine MMC_PM_WAKE_SDIO_IRQ capability
  ...
parents 47d5cc5b 310eb252
...@@ -12,6 +12,8 @@ Required properties: ...@@ -12,6 +12,8 @@ Required properties:
"mediatek,mt8173-mmc": for mmc host ip compatible with mt8173 "mediatek,mt8173-mmc": for mmc host ip compatible with mt8173
"mediatek,mt2701-mmc": for mmc host ip compatible with mt2701 "mediatek,mt2701-mmc": for mmc host ip compatible with mt2701
"mediatek,mt2712-mmc": for mmc host ip compatible with mt2712 "mediatek,mt2712-mmc": for mmc host ip compatible with mt2712
"mediatek,mt7623-mmc", "mediatek,mt2701-mmc": for MT7623 SoC
- reg: physical base address of the controller and length - reg: physical base address of the controller and length
- interrupts: Should contain MSDC interrupt number - interrupts: Should contain MSDC interrupt number
- clocks: Should contain phandle for the clock feeding the MMC controller - clocks: Should contain phandle for the clock feeding the MMC controller
......
...@@ -26,6 +26,7 @@ Required properties: ...@@ -26,6 +26,7 @@ Required properties:
"renesas,sdhi-r8a7794" - SDHI IP on R8A7794 SoC "renesas,sdhi-r8a7794" - SDHI IP on R8A7794 SoC
"renesas,sdhi-r8a7795" - SDHI IP on R8A7795 SoC "renesas,sdhi-r8a7795" - SDHI IP on R8A7795 SoC
"renesas,sdhi-r8a7796" - SDHI IP on R8A7796 SoC "renesas,sdhi-r8a7796" - SDHI IP on R8A7796 SoC
"renesas,sdhi-r8a77995" - SDHI IP on R8A77995 SoC
"renesas,sdhi-shmobile" - a generic sh-mobile SDHI controller "renesas,sdhi-shmobile" - a generic sh-mobile SDHI controller
"renesas,rcar-gen1-sdhi" - a generic R-Car Gen1 SDHI controller "renesas,rcar-gen1-sdhi" - a generic R-Car Gen1 SDHI controller
"renesas,rcar-gen2-sdhi" - a generic R-Car Gen2 or RZ/G1 "renesas,rcar-gen2-sdhi" - a generic R-Car Gen2 or RZ/G1
......
This diff is collapsed.
...@@ -5,6 +5,16 @@ ...@@ -5,6 +5,16 @@
struct mmc_queue; struct mmc_queue;
struct request; struct request;
void mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req); void mmc_blk_cqe_recovery(struct mmc_queue *mq);
enum mmc_issued;
enum mmc_issued mmc_blk_mq_issue_rq(struct mmc_queue *mq, struct request *req);
void mmc_blk_mq_complete(struct request *req);
void mmc_blk_mq_recovery(struct mmc_queue *mq);
struct work_struct;
void mmc_blk_mq_complete_work(struct work_struct *work);
#endif #endif
...@@ -351,8 +351,6 @@ int mmc_add_card(struct mmc_card *card) ...@@ -351,8 +351,6 @@ int mmc_add_card(struct mmc_card *card)
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
mmc_add_card_debugfs(card); mmc_add_card_debugfs(card);
#endif #endif
mmc_init_context_info(card->host);
card->dev.of_node = mmc_of_find_child_device(card->host, 0); card->dev.of_node = mmc_of_find_child_device(card->host, 0);
device_enable_async_suspend(&card->dev); device_enable_async_suspend(&card->dev);
......
...@@ -341,6 +341,8 @@ int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) ...@@ -341,6 +341,8 @@ int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
{ {
int err; int err;
init_completion(&mrq->cmd_completion);
mmc_retune_hold(host); mmc_retune_hold(host);
if (mmc_card_removed(host->card)) if (mmc_card_removed(host->card))
...@@ -361,20 +363,6 @@ int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) ...@@ -361,20 +363,6 @@ int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
} }
EXPORT_SYMBOL(mmc_start_request); EXPORT_SYMBOL(mmc_start_request);
/*
* mmc_wait_data_done() - done callback for data request
* @mrq: done data request
*
* Wakes up mmc context, passed as a callback to host controller driver
*/
static void mmc_wait_data_done(struct mmc_request *mrq)
{
struct mmc_context_info *context_info = &mrq->host->context_info;
context_info->is_done_rcv = true;
wake_up_interruptible(&context_info->wait);
}
static void mmc_wait_done(struct mmc_request *mrq) static void mmc_wait_done(struct mmc_request *mrq)
{ {
complete(&mrq->completion); complete(&mrq->completion);
...@@ -392,37 +380,6 @@ static inline void mmc_wait_ongoing_tfr_cmd(struct mmc_host *host) ...@@ -392,37 +380,6 @@ static inline void mmc_wait_ongoing_tfr_cmd(struct mmc_host *host)
wait_for_completion(&ongoing_mrq->cmd_completion); wait_for_completion(&ongoing_mrq->cmd_completion);
} }
/*
*__mmc_start_data_req() - starts data request
* @host: MMC host to start the request
* @mrq: data request to start
*
* Sets the done callback to be called when request is completed by the card.
* Starts data mmc request execution
* If an ongoing transfer is already in progress, wait for the command line
* to become available before sending another command.
*/
static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq)
{
int err;
mmc_wait_ongoing_tfr_cmd(host);
mrq->done = mmc_wait_data_done;
mrq->host = host;
init_completion(&mrq->cmd_completion);
err = mmc_start_request(host, mrq);
if (err) {
mrq->cmd->error = err;
mmc_complete_cmd(mrq);
mmc_wait_data_done(mrq);
}
return err;
}
static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq) static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
{ {
int err; int err;
...@@ -432,8 +389,6 @@ static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq) ...@@ -432,8 +389,6 @@ static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
init_completion(&mrq->completion); init_completion(&mrq->completion);
mrq->done = mmc_wait_done; mrq->done = mmc_wait_done;
init_completion(&mrq->cmd_completion);
err = mmc_start_request(host, mrq); err = mmc_start_request(host, mrq);
if (err) { if (err) {
mrq->cmd->error = err; mrq->cmd->error = err;
...@@ -650,163 +605,10 @@ EXPORT_SYMBOL(mmc_cqe_recovery); ...@@ -650,163 +605,10 @@ EXPORT_SYMBOL(mmc_cqe_recovery);
*/ */
bool mmc_is_req_done(struct mmc_host *host, struct mmc_request *mrq) bool mmc_is_req_done(struct mmc_host *host, struct mmc_request *mrq)
{ {
if (host->areq)
return host->context_info.is_done_rcv;
else
return completion_done(&mrq->completion); return completion_done(&mrq->completion);
} }
EXPORT_SYMBOL(mmc_is_req_done); EXPORT_SYMBOL(mmc_is_req_done);
/**
* mmc_pre_req - Prepare for a new request
* @host: MMC host to prepare command
* @mrq: MMC request to prepare for
*
* mmc_pre_req() is called in prior to mmc_start_req() to let
* host prepare for the new request. Preparation of a request may be
* performed while another request is running on the host.
*/
static void mmc_pre_req(struct mmc_host *host, struct mmc_request *mrq)
{
if (host->ops->pre_req)
host->ops->pre_req(host, mrq);
}
/**
* mmc_post_req - Post process a completed request
* @host: MMC host to post process command
* @mrq: MMC request to post process for
* @err: Error, if non zero, clean up any resources made in pre_req
*
* Let the host post process a completed request. Post processing of
* a request may be performed while another reuqest is running.
*/
static void mmc_post_req(struct mmc_host *host, struct mmc_request *mrq,
int err)
{
if (host->ops->post_req)
host->ops->post_req(host, mrq, err);
}
/**
* mmc_finalize_areq() - finalize an asynchronous request
* @host: MMC host to finalize any ongoing request on
*
* Returns the status of the ongoing asynchronous request, but
* MMC_BLK_SUCCESS if no request was going on.
*/
static enum mmc_blk_status mmc_finalize_areq(struct mmc_host *host)
{
struct mmc_context_info *context_info = &host->context_info;
enum mmc_blk_status status;
if (!host->areq)
return MMC_BLK_SUCCESS;
while (1) {
wait_event_interruptible(context_info->wait,
(context_info->is_done_rcv ||
context_info->is_new_req));
if (context_info->is_done_rcv) {
struct mmc_command *cmd;
context_info->is_done_rcv = false;
cmd = host->areq->mrq->cmd;
if (!cmd->error || !cmd->retries ||
mmc_card_removed(host->card)) {
status = host->areq->err_check(host->card,
host->areq);
break; /* return status */
} else {
mmc_retune_recheck(host);
pr_info("%s: req failed (CMD%u): %d, retrying...\n",
mmc_hostname(host),
cmd->opcode, cmd->error);
cmd->retries--;
cmd->error = 0;
__mmc_start_request(host, host->areq->mrq);
continue; /* wait for done/new event again */
}
}
return MMC_BLK_NEW_REQUEST;
}
mmc_retune_release(host);
/*
* Check BKOPS urgency for each R1 response
*/
if (host->card && mmc_card_mmc(host->card) &&
((mmc_resp_type(host->areq->mrq->cmd) == MMC_RSP_R1) ||
(mmc_resp_type(host->areq->mrq->cmd) == MMC_RSP_R1B)) &&
(host->areq->mrq->cmd->resp[0] & R1_EXCEPTION_EVENT)) {
mmc_start_bkops(host->card, true);
}
return status;
}
/**
* mmc_start_areq - start an asynchronous request
* @host: MMC host to start command
* @areq: asynchronous request to start
* @ret_stat: out parameter for status
*
* Start a new MMC custom command request for a host.
* If there is on ongoing async request wait for completion
* of that request and start the new one and return.
* Does not wait for the new request to complete.
*
* Returns the completed request, NULL in case of none completed.
* Wait for the an ongoing request (previoulsy started) to complete and
* return the completed request. If there is no ongoing request, NULL
* is returned without waiting. NULL is not an error condition.
*/
struct mmc_async_req *mmc_start_areq(struct mmc_host *host,
struct mmc_async_req *areq,
enum mmc_blk_status *ret_stat)
{
enum mmc_blk_status status;
int start_err = 0;
struct mmc_async_req *previous = host->areq;
/* Prepare a new request */
if (areq)
mmc_pre_req(host, areq->mrq);
/* Finalize previous request */
status = mmc_finalize_areq(host);
if (ret_stat)
*ret_stat = status;
/* The previous request is still going on... */
if (status == MMC_BLK_NEW_REQUEST)
return NULL;
/* Fine so far, start the new request! */
if (status == MMC_BLK_SUCCESS && areq)
start_err = __mmc_start_data_req(host, areq->mrq);
/* Postprocess the old request at this point */
if (host->areq)
mmc_post_req(host, host->areq->mrq, 0);
/* Cancel a prepared request if it was not started. */
if ((status != MMC_BLK_SUCCESS || start_err) && areq)
mmc_post_req(host, areq->mrq, -EINVAL);
if (status != MMC_BLK_SUCCESS)
host->areq = NULL;
else
host->areq = areq;
return previous;
}
EXPORT_SYMBOL(mmc_start_areq);
/** /**
* mmc_wait_for_req - start a request and wait for completion * mmc_wait_for_req - start a request and wait for completion
* @host: MMC host to start command * @host: MMC host to start command
...@@ -2959,6 +2761,14 @@ static int mmc_pm_notify(struct notifier_block *notify_block, ...@@ -2959,6 +2761,14 @@ static int mmc_pm_notify(struct notifier_block *notify_block,
if (!err) if (!err)
break; break;
if (!mmc_card_is_removable(host)) {
dev_warn(mmc_dev(host),
"pre_suspend failed for non-removable host: "
"%d\n", err);
/* Avoid removing non-removable hosts */
break;
}
/* Calling bus_ops->remove() with a claimed host can deadlock */ /* Calling bus_ops->remove() with a claimed host can deadlock */
host->bus_ops->remove(host); host->bus_ops->remove(host);
mmc_claim_host(host); mmc_claim_host(host);
...@@ -2994,22 +2804,6 @@ void mmc_unregister_pm_notifier(struct mmc_host *host) ...@@ -2994,22 +2804,6 @@ void mmc_unregister_pm_notifier(struct mmc_host *host)
} }
#endif #endif
/**
* mmc_init_context_info() - init synchronization context
* @host: mmc host
*
* Init struct context_info needed to implement asynchronous
* request mechanism, used by mmc core, host driver and mmc requests
* supplier.
*/
void mmc_init_context_info(struct mmc_host *host)
{
host->context_info.is_new_req = false;
host->context_info.is_done_rcv = false;
host->context_info.is_waiting_last_req = false;
init_waitqueue_head(&host->context_info.wait);
}
static int __init mmc_init(void) static int __init mmc_init(void)
{ {
int ret; int ret;
......
...@@ -62,12 +62,10 @@ void mmc_set_initial_state(struct mmc_host *host); ...@@ -62,12 +62,10 @@ void mmc_set_initial_state(struct mmc_host *host);
static inline void mmc_delay(unsigned int ms) static inline void mmc_delay(unsigned int ms)
{ {
if (ms < 1000 / HZ) { if (ms <= 20)
cond_resched(); usleep_range(ms * 1000, ms * 1250);
mdelay(ms); else
} else {
msleep(ms); msleep(ms);
}
} }
void mmc_rescan(struct work_struct *work); void mmc_rescan(struct work_struct *work);
...@@ -91,8 +89,6 @@ void mmc_remove_host_debugfs(struct mmc_host *host); ...@@ -91,8 +89,6 @@ void mmc_remove_host_debugfs(struct mmc_host *host);
void mmc_add_card_debugfs(struct mmc_card *card); void mmc_add_card_debugfs(struct mmc_card *card);
void mmc_remove_card_debugfs(struct mmc_card *card); void mmc_remove_card_debugfs(struct mmc_card *card);
void mmc_init_context_info(struct mmc_host *host);
int mmc_execute_tuning(struct mmc_card *card); int mmc_execute_tuning(struct mmc_card *card);
int mmc_hs200_to_hs400(struct mmc_card *card); int mmc_hs200_to_hs400(struct mmc_card *card);
int mmc_hs400_to_hs200(struct mmc_card *card); int mmc_hs400_to_hs200(struct mmc_card *card);
...@@ -110,12 +106,6 @@ bool mmc_is_req_done(struct mmc_host *host, struct mmc_request *mrq); ...@@ -110,12 +106,6 @@ bool mmc_is_req_done(struct mmc_host *host, struct mmc_request *mrq);
int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq); int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq);
struct mmc_async_req;
struct mmc_async_req *mmc_start_areq(struct mmc_host *host,
struct mmc_async_req *areq,
enum mmc_blk_status *ret_stat);
int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr, int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr,
unsigned int arg); unsigned int arg);
int mmc_can_erase(struct mmc_card *card); int mmc_can_erase(struct mmc_card *card);
...@@ -152,4 +142,35 @@ int mmc_cqe_start_req(struct mmc_host *host, struct mmc_request *mrq); ...@@ -152,4 +142,35 @@ int mmc_cqe_start_req(struct mmc_host *host, struct mmc_request *mrq);
void mmc_cqe_post_req(struct mmc_host *host, struct mmc_request *mrq); void mmc_cqe_post_req(struct mmc_host *host, struct mmc_request *mrq);
int mmc_cqe_recovery(struct mmc_host *host); int mmc_cqe_recovery(struct mmc_host *host);
/**
* mmc_pre_req - Prepare for a new request
* @host: MMC host to prepare command
* @mrq: MMC request to prepare for
*
* mmc_pre_req() is called in prior to mmc_start_req() to let
* host prepare for the new request. Preparation of a request may be
* performed while another request is running on the host.
*/
static inline void mmc_pre_req(struct mmc_host *host, struct mmc_request *mrq)
{
if (host->ops->pre_req)
host->ops->pre_req(host, mrq);
}
/**
* mmc_post_req - Post process a completed request
* @host: MMC host to post process command
* @mrq: MMC request to post process for
* @err: Error, if non zero, clean up any resources made in pre_req
*
* Let the host post process a completed request. Post processing of
* a request may be performed while another request is running.
*/
static inline void mmc_post_req(struct mmc_host *host, struct mmc_request *mrq,
int err)
{
if (host->ops->post_req)
host->ops->post_req(host, mrq, err);
}
#endif #endif
...@@ -41,6 +41,11 @@ static inline int mmc_host_cmd23(struct mmc_host *host) ...@@ -41,6 +41,11 @@ static inline int mmc_host_cmd23(struct mmc_host *host)
return host->caps & MMC_CAP_CMD23; return host->caps & MMC_CAP_CMD23;
} }
static inline bool mmc_host_done_complete(struct mmc_host *host)
{
return host->caps & MMC_CAP_DONE_COMPLETE;
}
static inline int mmc_boot_partition_access(struct mmc_host *host) static inline int mmc_boot_partition_access(struct mmc_host *host)
{ {
return !(host->caps2 & MMC_CAP2_BOOTPART_NOACC); return !(host->caps2 & MMC_CAP2_BOOTPART_NOACC);
...@@ -74,6 +79,5 @@ static inline bool mmc_card_hs400es(struct mmc_card *card) ...@@ -74,6 +79,5 @@ static inline bool mmc_card_hs400es(struct mmc_card *card)
return card->host->ios.enhanced_strobe; return card->host->ios.enhanced_strobe;
} }
#endif #endif
This diff is collapsed.
This diff is collapsed.
...@@ -8,6 +8,20 @@ ...@@ -8,6 +8,20 @@
#include <linux/mmc/core.h> #include <linux/mmc/core.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
enum mmc_issued {
MMC_REQ_STARTED,
MMC_REQ_BUSY,
MMC_REQ_FAILED_TO_START,
MMC_REQ_FINISHED,
};
enum mmc_issue_type {
MMC_ISSUE_SYNC,
MMC_ISSUE_DCMD,
MMC_ISSUE_ASYNC,
MMC_ISSUE_MAX,
};
static inline struct mmc_queue_req *req_to_mmc_queue_req(struct request *rq) static inline struct mmc_queue_req *req_to_mmc_queue_req(struct request *rq)
{ {
return blk_mq_rq_to_pdu(rq); return blk_mq_rq_to_pdu(rq);
...@@ -20,7 +34,6 @@ static inline struct request *mmc_queue_req_to_req(struct mmc_queue_req *mqr) ...@@ -20,7 +34,6 @@ static inline struct request *mmc_queue_req_to_req(struct mmc_queue_req *mqr)
return blk_mq_rq_from_pdu(mqr); return blk_mq_rq_from_pdu(mqr);
} }
struct task_struct;
struct mmc_blk_data; struct mmc_blk_data;
struct mmc_blk_ioc_data; struct mmc_blk_ioc_data;
...@@ -30,7 +43,6 @@ struct mmc_blk_request { ...@@ -30,7 +43,6 @@ struct mmc_blk_request {
struct mmc_command cmd; struct mmc_command cmd;
struct mmc_command stop; struct mmc_command stop;
struct mmc_data data; struct mmc_data data;
int retune_retry_done;
}; };
/** /**
...@@ -52,28 +64,34 @@ enum mmc_drv_op { ...@@ -52,28 +64,34 @@ enum mmc_drv_op {
struct mmc_queue_req { struct mmc_queue_req {
struct mmc_blk_request brq; struct mmc_blk_request brq;
struct scatterlist *sg; struct scatterlist *sg;
struct mmc_async_req areq;
enum mmc_drv_op drv_op; enum mmc_drv_op drv_op;
int drv_op_result; int drv_op_result;
void *drv_op_data; void *drv_op_data;
unsigned int ioc_count; unsigned int ioc_count;
int retries;
}; };
struct mmc_queue { struct mmc_queue {
struct mmc_card *card; struct mmc_card *card;
struct task_struct *thread; struct mmc_ctx ctx;
struct semaphore thread_sem; struct blk_mq_tag_set tag_set;
bool suspended;
bool asleep;
struct mmc_blk_data *blkdata; struct mmc_blk_data *blkdata;
struct request_queue *queue; struct request_queue *queue;
/* int in_flight[MMC_ISSUE_MAX];
* FIXME: this counter is not a very reliable way of keeping unsigned int cqe_busy;
* track of how many requests that are ongoing. Switch to just #define MMC_CQE_DCMD_BUSY BIT(0)
* letting the block core keep track of requests and per-request #define MMC_CQE_QUEUE_FULL BIT(1)
* associated mmc_queue_req data. bool use_cqe;
*/ bool recovery_needed;
int qcnt; bool in_recovery;
bool rw_wait;
bool waiting;
struct work_struct recovery_work;
wait_queue_head_t wait;
struct request *recovery_req;
struct request *complete_req;
struct mutex complete_lock;
struct work_struct complete_work;
}; };
extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *, extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *,
...@@ -84,4 +102,22 @@ extern void mmc_queue_resume(struct mmc_queue *); ...@@ -84,4 +102,22 @@ extern void mmc_queue_resume(struct mmc_queue *);
extern unsigned int mmc_queue_map_sg(struct mmc_queue *, extern unsigned int mmc_queue_map_sg(struct mmc_queue *,
struct mmc_queue_req *); struct mmc_queue_req *);
void mmc_cqe_check_busy(struct mmc_queue *mq);
void mmc_cqe_recovery_notifier(struct mmc_request *mrq);
enum mmc_issue_type mmc_issue_type(struct mmc_queue *mq, struct request *req);
static inline int mmc_tot_in_flight(struct mmc_queue *mq)
{
return mq->in_flight[MMC_ISSUE_SYNC] +
mq->in_flight[MMC_ISSUE_DCMD] +
mq->in_flight[MMC_ISSUE_ASYNC];
}
static inline int mmc_cqe_qcnt(struct mmc_queue *mq)
{
return mq->in_flight[MMC_ISSUE_DCMD] +
mq->in_flight[MMC_ISSUE_ASYNC];
}
#endif #endif
...@@ -121,20 +121,18 @@ EXPORT_SYMBOL(mmc_gpio_request_ro); ...@@ -121,20 +121,18 @@ EXPORT_SYMBOL(mmc_gpio_request_ro);
void mmc_gpiod_request_cd_irq(struct mmc_host *host) void mmc_gpiod_request_cd_irq(struct mmc_host *host)
{ {
struct mmc_gpio *ctx = host->slot.handler_priv; struct mmc_gpio *ctx = host->slot.handler_priv;
int ret, irq; int irq = -EINVAL;
int ret;
if (host->slot.cd_irq >= 0 || !ctx || !ctx->cd_gpio) if (host->slot.cd_irq >= 0 || !ctx || !ctx->cd_gpio)
return; return;
irq = gpiod_to_irq(ctx->cd_gpio);
/* /*
* Even if gpiod_to_irq() returns a valid IRQ number, the platform might * Do not use IRQ if the platform prefers to poll, e.g., because that
* still prefer to poll, e.g., because that IRQ number is already used * IRQ number is already used by another unit and cannot be shared.
* by another unit and cannot be shared.
*/ */
if (irq >= 0 && host->caps & MMC_CAP_NEEDS_POLL) if (!(host->caps & MMC_CAP_NEEDS_POLL))
irq = -EINVAL; irq = gpiod_to_irq(ctx->cd_gpio);
if (irq >= 0) { if (irq >= 0) {
if (!ctx->cd_gpio_isr) if (!ctx->cd_gpio_isr)
...@@ -307,3 +305,11 @@ int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id, ...@@ -307,3 +305,11 @@ int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
return 0; return 0;
} }
EXPORT_SYMBOL(mmc_gpiod_request_ro); EXPORT_SYMBOL(mmc_gpiod_request_ro);
bool mmc_can_gpio_ro(struct mmc_host *host)
{
struct mmc_gpio *ctx = host->slot.handler_priv;
return ctx->ro_gpio ? true : false;
}
EXPORT_SYMBOL(mmc_can_gpio_ro);
...@@ -81,6 +81,7 @@ config MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER ...@@ -81,6 +81,7 @@ config MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
config MMC_SDHCI_PCI config MMC_SDHCI_PCI
tristate "SDHCI support on PCI bus" tristate "SDHCI support on PCI bus"
depends on MMC_SDHCI && PCI depends on MMC_SDHCI && PCI
select MMC_CQHCI
help help
This selects the PCI Secure Digital Host Controller Interface. This selects the PCI Secure Digital Host Controller Interface.
Most controllers found today are PCI devices. Most controllers found today are PCI devices.
...@@ -132,6 +133,7 @@ config MMC_SDHCI_OF_ARASAN ...@@ -132,6 +133,7 @@ config MMC_SDHCI_OF_ARASAN
depends on MMC_SDHCI_PLTFM depends on MMC_SDHCI_PLTFM
depends on OF depends on OF
depends on COMMON_CLK depends on COMMON_CLK
select MMC_CQHCI
help help
This selects the Arasan Secure Digital Host Controller Interface This selects the Arasan Secure Digital Host Controller Interface
(SDHCI). This hardware is found e.g. in Xilinx' Zynq SoC. (SDHCI). This hardware is found e.g. in Xilinx' Zynq SoC.
...@@ -320,7 +322,7 @@ config MMC_SDHCI_BCM_KONA ...@@ -320,7 +322,7 @@ config MMC_SDHCI_BCM_KONA
config MMC_SDHCI_F_SDH30 config MMC_SDHCI_F_SDH30
tristate "SDHCI support for Fujitsu Semiconductor F_SDH30" tristate "SDHCI support for Fujitsu Semiconductor F_SDH30"
depends on MMC_SDHCI_PLTFM depends on MMC_SDHCI_PLTFM
depends on OF depends on OF || ACPI
help help
This selects the Secure Digital Host Controller Interface (SDHCI) This selects the Secure Digital Host Controller Interface (SDHCI)
Needed by some Fujitsu SoC for MMC / SD / SDIO support. Needed by some Fujitsu SoC for MMC / SD / SDIO support.
...@@ -595,11 +597,8 @@ config MMC_TMIO ...@@ -595,11 +597,8 @@ config MMC_TMIO
config MMC_SDHI config MMC_SDHI
tristate "Renesas SDHI SD/SDIO controller support" tristate "Renesas SDHI SD/SDIO controller support"
depends on SUPERH || ARM || ARM64
depends on SUPERH || ARCH_RENESAS || COMPILE_TEST depends on SUPERH || ARCH_RENESAS || COMPILE_TEST
select MMC_TMIO_CORE select MMC_TMIO_CORE
select MMC_SDHI_SYS_DMAC if (SUPERH || ARM)
select MMC_SDHI_INTERNAL_DMAC if ARM64
help help
This provides support for the SDHI SD/SDIO controller found in This provides support for the SDHI SD/SDIO controller found in
Renesas SuperH, ARM and ARM64 based SoCs Renesas SuperH, ARM and ARM64 based SoCs
...@@ -607,6 +606,7 @@ config MMC_SDHI ...@@ -607,6 +606,7 @@ config MMC_SDHI
config MMC_SDHI_SYS_DMAC config MMC_SDHI_SYS_DMAC
tristate "DMA for SDHI SD/SDIO controllers using SYS-DMAC" tristate "DMA for SDHI SD/SDIO controllers using SYS-DMAC"
depends on MMC_SDHI depends on MMC_SDHI
default MMC_SDHI if (SUPERH || ARM)
help help
This provides DMA support for SDHI SD/SDIO controllers This provides DMA support for SDHI SD/SDIO controllers
using SYS-DMAC via DMA Engine. This supports the controllers using SYS-DMAC via DMA Engine. This supports the controllers
...@@ -616,6 +616,7 @@ config MMC_SDHI_INTERNAL_DMAC ...@@ -616,6 +616,7 @@ config MMC_SDHI_INTERNAL_DMAC
tristate "DMA for SDHI SD/SDIO controllers using on-chip bus mastering" tristate "DMA for SDHI SD/SDIO controllers using on-chip bus mastering"
depends on ARM64 || COMPILE_TEST depends on ARM64 || COMPILE_TEST
depends on MMC_SDHI depends on MMC_SDHI
default MMC_SDHI if ARM64
help help
This provides DMA support for SDHI SD/SDIO controllers This provides DMA support for SDHI SD/SDIO controllers
using on-chip bus mastering. This supports the controllers using on-chip bus mastering. This supports the controllers
...@@ -857,6 +858,19 @@ config MMC_SUNXI ...@@ -857,6 +858,19 @@ config MMC_SUNXI
This selects support for the SD/MMC Host Controller on This selects support for the SD/MMC Host Controller on
Allwinner sunxi SoCs. Allwinner sunxi SoCs.
config MMC_CQHCI
tristate "Command Queue Host Controller Interface support"
depends on HAS_DMA
help
This selects the Command Queue Host Controller Interface (CQHCI)
support present in host controllers of Qualcomm Technologies, Inc
amongst others.
This controller supports eMMC devices with command queue support.
If you have a controller with this interface, say Y or M here.
If unsure, say N.
config MMC_TOSHIBA_PCI config MMC_TOSHIBA_PCI
tristate "Toshiba Type A SD/MMC Card Interface Driver" tristate "Toshiba Type A SD/MMC Card Interface Driver"
depends on PCI depends on PCI
......
...@@ -11,7 +11,7 @@ obj-$(CONFIG_MMC_MXC) += mxcmmc.o ...@@ -11,7 +11,7 @@ obj-$(CONFIG_MMC_MXC) += mxcmmc.o
obj-$(CONFIG_MMC_MXS) += mxs-mmc.o obj-$(CONFIG_MMC_MXS) += mxs-mmc.o
obj-$(CONFIG_MMC_SDHCI) += sdhci.o obj-$(CONFIG_MMC_SDHCI) += sdhci.o
obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o
sdhci-pci-y += sdhci-pci-core.o sdhci-pci-o2micro.o sdhci-pci-y += sdhci-pci-core.o sdhci-pci-o2micro.o sdhci-pci-arasan.o
obj-$(subst m,y,$(CONFIG_MMC_SDHCI_PCI)) += sdhci-pci-data.o obj-$(subst m,y,$(CONFIG_MMC_SDHCI_PCI)) += sdhci-pci-data.o
obj-$(CONFIG_MMC_SDHCI_ACPI) += sdhci-acpi.o obj-$(CONFIG_MMC_SDHCI_ACPI) += sdhci-acpi.o
obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o
...@@ -39,12 +39,8 @@ obj-$(CONFIG_MMC_SDRICOH_CS) += sdricoh_cs.o ...@@ -39,12 +39,8 @@ obj-$(CONFIG_MMC_SDRICOH_CS) += sdricoh_cs.o
obj-$(CONFIG_MMC_TMIO) += tmio_mmc.o obj-$(CONFIG_MMC_TMIO) += tmio_mmc.o
obj-$(CONFIG_MMC_TMIO_CORE) += tmio_mmc_core.o obj-$(CONFIG_MMC_TMIO_CORE) += tmio_mmc_core.o
obj-$(CONFIG_MMC_SDHI) += renesas_sdhi_core.o obj-$(CONFIG_MMC_SDHI) += renesas_sdhi_core.o
ifeq ($(subst m,y,$(CONFIG_MMC_SDHI_SYS_DMAC)),y) obj-$(CONFIG_MMC_SDHI_SYS_DMAC) += renesas_sdhi_sys_dmac.o
obj-$(CONFIG_MMC_SDHI) += renesas_sdhi_sys_dmac.o obj-$(CONFIG_MMC_SDHI_INTERNAL_DMAC) += renesas_sdhi_internal_dmac.o
endif
ifeq ($(subst m,y,$(CONFIG_MMC_SDHI_INTERNAL_DMAC)),y)
obj-$(CONFIG_MMC_SDHI) += renesas_sdhi_internal_dmac.o
endif
obj-$(CONFIG_MMC_CB710) += cb710-mmc.o obj-$(CONFIG_MMC_CB710) += cb710-mmc.o
obj-$(CONFIG_MMC_VIA_SDMMC) += via-sdmmc.o obj-$(CONFIG_MMC_VIA_SDMMC) += via-sdmmc.o
obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o
...@@ -92,6 +88,7 @@ obj-$(CONFIG_MMC_SDHCI_ST) += sdhci-st.o ...@@ -92,6 +88,7 @@ obj-$(CONFIG_MMC_SDHCI_ST) += sdhci-st.o
obj-$(CONFIG_MMC_SDHCI_MICROCHIP_PIC32) += sdhci-pic32.o obj-$(CONFIG_MMC_SDHCI_MICROCHIP_PIC32) += sdhci-pic32.o
obj-$(CONFIG_MMC_SDHCI_BRCMSTB) += sdhci-brcmstb.o obj-$(CONFIG_MMC_SDHCI_BRCMSTB) += sdhci-brcmstb.o
obj-$(CONFIG_MMC_SDHCI_OMAP) += sdhci-omap.o obj-$(CONFIG_MMC_SDHCI_OMAP) += sdhci-omap.o
obj-$(CONFIG_MMC_CQHCI) += cqhci.o
ifeq ($(CONFIG_CB710_DEBUG),y) ifeq ($(CONFIG_CB710_DEBUG),y)
CFLAGS-cb710-mmc += -DDEBUG CFLAGS-cb710-mmc += -DDEBUG
......
...@@ -42,13 +42,11 @@ ...@@ -42,13 +42,11 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/scatterlist.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/types.h> #include <asm/types.h>
#include <asm/io.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#define DRIVER_NAME "goldfish_mmc" #define DRIVER_NAME "goldfish_mmc"
......
This diff is collapsed.
/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* 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.
*/
#ifndef LINUX_MMC_CQHCI_H
#define LINUX_MMC_CQHCI_H
#include <linux/compiler.h>
#include <linux/bitops.h>
#include <linux/spinlock_types.h>
#include <linux/types.h>
#include <linux/completion.h>
#include <linux/wait.h>
#include <linux/irqreturn.h>
#include <asm/io.h>
/* registers */
/* version */
#define CQHCI_VER 0x00
#define CQHCI_VER_MAJOR(x) (((x) & GENMASK(11, 8)) >> 8)
#define CQHCI_VER_MINOR1(x) (((x) & GENMASK(7, 4)) >> 4)
#define CQHCI_VER_MINOR2(x) ((x) & GENMASK(3, 0))
/* capabilities */
#define CQHCI_CAP 0x04
/* configuration */
#define CQHCI_CFG 0x08
#define CQHCI_DCMD 0x00001000
#define CQHCI_TASK_DESC_SZ 0x00000100
#define CQHCI_ENABLE 0x00000001
/* control */
#define CQHCI_CTL 0x0C
#define CQHCI_CLEAR_ALL_TASKS 0x00000100
#define CQHCI_HALT 0x00000001
/* interrupt status */
#define CQHCI_IS 0x10
#define CQHCI_IS_HAC BIT(0)
#define CQHCI_IS_TCC BIT(1)
#define CQHCI_IS_RED BIT(2)
#define CQHCI_IS_TCL BIT(3)
#define CQHCI_IS_MASK (CQHCI_IS_TCC | CQHCI_IS_RED)
/* interrupt status enable */
#define CQHCI_ISTE 0x14
/* interrupt signal enable */
#define CQHCI_ISGE 0x18
/* interrupt coalescing */
#define CQHCI_IC 0x1C
#define CQHCI_IC_ENABLE BIT(31)
#define CQHCI_IC_RESET BIT(16)
#define CQHCI_IC_ICCTHWEN BIT(15)
#define CQHCI_IC_ICCTH(x) (((x) & 0x1F) << 8)
#define CQHCI_IC_ICTOVALWEN BIT(7)
#define CQHCI_IC_ICTOVAL(x) ((x) & 0x7F)
/* task list base address */
#define CQHCI_TDLBA 0x20
/* task list base address upper */
#define CQHCI_TDLBAU 0x24
/* door-bell */
#define CQHCI_TDBR 0x28
/* task completion notification */
#define CQHCI_TCN 0x2C
/* device queue status */
#define CQHCI_DQS 0x30
/* device pending tasks */
#define CQHCI_DPT 0x34
/* task clear */
#define CQHCI_TCLR 0x38
/* send status config 1 */
#define CQHCI_SSC1 0x40
/* send status config 2 */
#define CQHCI_SSC2 0x44
/* response for dcmd */
#define CQHCI_CRDCT 0x48
/* response mode error mask */
#define CQHCI_RMEM 0x50
/* task error info */
#define CQHCI_TERRI 0x54
#define CQHCI_TERRI_C_INDEX(x) ((x) & GENMASK(5, 0))
#define CQHCI_TERRI_C_TASK(x) (((x) & GENMASK(12, 8)) >> 8)
#define CQHCI_TERRI_C_VALID(x) ((x) & BIT(15))
#define CQHCI_TERRI_D_INDEX(x) (((x) & GENMASK(21, 16)) >> 16)
#define CQHCI_TERRI_D_TASK(x) (((x) & GENMASK(28, 24)) >> 24)
#define CQHCI_TERRI_D_VALID(x) ((x) & BIT(31))
/* command response index */
#define CQHCI_CRI 0x58
/* command response argument */
#define CQHCI_CRA 0x5C
#define CQHCI_INT_ALL 0xF
#define CQHCI_IC_DEFAULT_ICCTH 31
#define CQHCI_IC_DEFAULT_ICTOVAL 1
/* attribute fields */
#define CQHCI_VALID(x) (((x) & 1) << 0)
#define CQHCI_END(x) (((x) & 1) << 1)
#define CQHCI_INT(x) (((x) & 1) << 2)
#define CQHCI_ACT(x) (((x) & 0x7) << 3)
/* data command task descriptor fields */
#define CQHCI_FORCED_PROG(x) (((x) & 1) << 6)
#define CQHCI_CONTEXT(x) (((x) & 0xF) << 7)
#define CQHCI_DATA_TAG(x) (((x) & 1) << 11)
#define CQHCI_DATA_DIR(x) (((x) & 1) << 12)
#define CQHCI_PRIORITY(x) (((x) & 1) << 13)
#define CQHCI_QBAR(x) (((x) & 1) << 14)
#define CQHCI_REL_WRITE(x) (((x) & 1) << 15)
#define CQHCI_BLK_COUNT(x) (((x) & 0xFFFF) << 16)
#define CQHCI_BLK_ADDR(x) (((x) & 0xFFFFFFFF) << 32)
/* direct command task descriptor fields */
#define CQHCI_CMD_INDEX(x) (((x) & 0x3F) << 16)
#define CQHCI_CMD_TIMING(x) (((x) & 1) << 22)
#define CQHCI_RESP_TYPE(x) (((x) & 0x3) << 23)
/* transfer descriptor fields */
#define CQHCI_DAT_LENGTH(x) (((x) & 0xFFFF) << 16)
#define CQHCI_DAT_ADDR_LO(x) (((x) & 0xFFFFFFFF) << 32)
#define CQHCI_DAT_ADDR_HI(x) (((x) & 0xFFFFFFFF) << 0)
struct cqhci_host_ops;
struct mmc_host;
struct cqhci_slot;
struct cqhci_host {
const struct cqhci_host_ops *ops;
void __iomem *mmio;
struct mmc_host *mmc;
spinlock_t lock;
/* relative card address of device */
unsigned int rca;
/* 64 bit DMA */
bool dma64;
int num_slots;
int qcnt;
u32 dcmd_slot;
u32 caps;
#define CQHCI_TASK_DESC_SZ_128 0x1
u32 quirks;
#define CQHCI_QUIRK_SHORT_TXFR_DESC_SZ 0x1
bool enabled;
bool halted;
bool init_done;
bool activated;
bool waiting_for_idle;
bool recovery_halt;
size_t desc_size;
size_t data_size;
u8 *desc_base;
/* total descriptor size */
u8 slot_sz;
/* 64/128 bit depends on CQHCI_CFG */
u8 task_desc_len;
/* 64 bit on 32-bit arch, 128 bit on 64-bit */
u8 link_desc_len;
u8 *trans_desc_base;
/* same length as transfer descriptor */
u8 trans_desc_len;
dma_addr_t desc_dma_base;
dma_addr_t trans_desc_dma_base;
struct completion halt_comp;
wait_queue_head_t wait_queue;
struct cqhci_slot *slot;
};
struct cqhci_host_ops {
void (*dumpregs)(struct mmc_host *mmc);
void (*write_l)(struct cqhci_host *host, u32 val, int reg);
u32 (*read_l)(struct cqhci_host *host, int reg);
void (*enable)(struct mmc_host *mmc);
void (*disable)(struct mmc_host *mmc, bool recovery);
};
static inline void cqhci_writel(struct cqhci_host *host, u32 val, int reg)
{
if (unlikely(host->ops->write_l))
host->ops->write_l(host, val, reg);
else
writel_relaxed(val, host->mmio + reg);
}
static inline u32 cqhci_readl(struct cqhci_host *host, int reg)
{
if (unlikely(host->ops->read_l))
return host->ops->read_l(host, reg);
else
return readl_relaxed(host->mmio + reg);
}
struct platform_device;
irqreturn_t cqhci_irq(struct mmc_host *mmc, u32 intmask, int cmd_error,
int data_error);
int cqhci_init(struct cqhci_host *cq_host, struct mmc_host *mmc, bool dma64);
struct cqhci_host *cqhci_pltfm_init(struct platform_device *pdev);
int cqhci_suspend(struct mmc_host *mmc);
int cqhci_resume(struct mmc_host *mmc);
#endif
...@@ -174,7 +174,7 @@ module_param(poll_loopcount, uint, S_IRUGO); ...@@ -174,7 +174,7 @@ module_param(poll_loopcount, uint, S_IRUGO);
MODULE_PARM_DESC(poll_loopcount, MODULE_PARM_DESC(poll_loopcount,
"Maximum polling loop count. Default = 32"); "Maximum polling loop count. Default = 32");
static unsigned __initdata use_dma = 1; static unsigned use_dma = 1;
module_param(use_dma, uint, 0); module_param(use_dma, uint, 0);
MODULE_PARM_DESC(use_dma, "Whether to use DMA or not. Default = 1"); MODULE_PARM_DESC(use_dma, "Whether to use DMA or not. Default = 1");
...@@ -496,8 +496,7 @@ static int mmc_davinci_start_dma_transfer(struct mmc_davinci_host *host, ...@@ -496,8 +496,7 @@ static int mmc_davinci_start_dma_transfer(struct mmc_davinci_host *host,
return ret; return ret;
} }
static void __init_or_module static void davinci_release_dma_channels(struct mmc_davinci_host *host)
davinci_release_dma_channels(struct mmc_davinci_host *host)
{ {
if (!host->use_dma) if (!host->use_dma)
return; return;
...@@ -506,7 +505,7 @@ davinci_release_dma_channels(struct mmc_davinci_host *host) ...@@ -506,7 +505,7 @@ davinci_release_dma_channels(struct mmc_davinci_host *host)
dma_release_channel(host->dma_rx); dma_release_channel(host->dma_rx);
} }
static int __init davinci_acquire_dma_channels(struct mmc_davinci_host *host) static int davinci_acquire_dma_channels(struct mmc_davinci_host *host)
{ {
host->dma_tx = dma_request_chan(mmc_dev(host->mmc), "tx"); host->dma_tx = dma_request_chan(mmc_dev(host->mmc), "tx");
if (IS_ERR(host->dma_tx)) { if (IS_ERR(host->dma_tx)) {
...@@ -1201,7 +1200,7 @@ static int mmc_davinci_parse_pdata(struct mmc_host *mmc) ...@@ -1201,7 +1200,7 @@ static int mmc_davinci_parse_pdata(struct mmc_host *mmc)
return 0; return 0;
} }
static int __init davinci_mmcsd_probe(struct platform_device *pdev) static int davinci_mmcsd_probe(struct platform_device *pdev)
{ {
const struct of_device_id *match; const struct of_device_id *match;
struct mmc_davinci_host *host = NULL; struct mmc_davinci_host *host = NULL;
...@@ -1254,6 +1253,7 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev) ...@@ -1254,6 +1253,7 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev)
pdev->id_entry = match->data; pdev->id_entry = match->data;
ret = mmc_of_parse(mmc); ret = mmc_of_parse(mmc);
if (ret) { if (ret) {
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev, dev_err(&pdev->dev,
"could not parse of data: %d\n", ret); "could not parse of data: %d\n", ret);
goto parse_fail; goto parse_fail;
...@@ -1414,11 +1414,12 @@ static struct platform_driver davinci_mmcsd_driver = { ...@@ -1414,11 +1414,12 @@ static struct platform_driver davinci_mmcsd_driver = {
.pm = davinci_mmcsd_pm_ops, .pm = davinci_mmcsd_pm_ops,
.of_match_table = davinci_mmc_dt_ids, .of_match_table = davinci_mmc_dt_ids,
}, },
.probe = davinci_mmcsd_probe,
.remove = __exit_p(davinci_mmcsd_remove), .remove = __exit_p(davinci_mmcsd_remove),
.id_table = davinci_mmc_devtype, .id_table = davinci_mmc_devtype,
}; };
module_platform_driver_probe(davinci_mmcsd_driver, davinci_mmcsd_probe); module_platform_driver(davinci_mmcsd_driver);
MODULE_AUTHOR("Texas Instruments India"); MODULE_AUTHOR("Texas Instruments India");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
......
...@@ -1208,7 +1208,7 @@ static int meson_mmc_probe(struct platform_device *pdev) ...@@ -1208,7 +1208,7 @@ static int meson_mmc_probe(struct platform_device *pdev)
} }
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
if (!irq) { if (irq <= 0) {
dev_err(&pdev->dev, "failed to get interrupt resource.\n"); dev_err(&pdev->dev, "failed to get interrupt resource.\n");
ret = -EINVAL; ret = -EINVAL;
goto free_host; goto free_host;
......
...@@ -82,6 +82,10 @@ static unsigned int fmax = 515633; ...@@ -82,6 +82,10 @@ static unsigned int fmax = 515633;
* @qcom_fifo: enables qcom specific fifo pio read logic. * @qcom_fifo: enables qcom specific fifo pio read logic.
* @qcom_dml: enables qcom specific dma glue for dma transfers. * @qcom_dml: enables qcom specific dma glue for dma transfers.
* @reversed_irq_handling: handle data irq before cmd irq. * @reversed_irq_handling: handle data irq before cmd irq.
* @mmcimask1: true if variant have a MMCIMASK1 register.
* @start_err: bitmask identifying the STARTBITERR bit inside MMCISTATUS
* register.
* @opendrain: bitmask identifying the OPENDRAIN bit inside MMCIPOWER register
*/ */
struct variant_data { struct variant_data {
unsigned int clkreg; unsigned int clkreg;
...@@ -111,6 +115,9 @@ struct variant_data { ...@@ -111,6 +115,9 @@ struct variant_data {
bool qcom_fifo; bool qcom_fifo;
bool qcom_dml; bool qcom_dml;
bool reversed_irq_handling; bool reversed_irq_handling;
bool mmcimask1;
u32 start_err;
u32 opendrain;
}; };
static struct variant_data variant_arm = { static struct variant_data variant_arm = {
...@@ -120,6 +127,9 @@ static struct variant_data variant_arm = { ...@@ -120,6 +127,9 @@ static struct variant_data variant_arm = {
.pwrreg_powerup = MCI_PWR_UP, .pwrreg_powerup = MCI_PWR_UP,
.f_max = 100000000, .f_max = 100000000,
.reversed_irq_handling = true, .reversed_irq_handling = true,
.mmcimask1 = true,
.start_err = MCI_STARTBITERR,
.opendrain = MCI_ROD,
}; };
static struct variant_data variant_arm_extended_fifo = { static struct variant_data variant_arm_extended_fifo = {
...@@ -128,6 +138,9 @@ static struct variant_data variant_arm_extended_fifo = { ...@@ -128,6 +138,9 @@ static struct variant_data variant_arm_extended_fifo = {
.datalength_bits = 16, .datalength_bits = 16,
.pwrreg_powerup = MCI_PWR_UP, .pwrreg_powerup = MCI_PWR_UP,
.f_max = 100000000, .f_max = 100000000,
.mmcimask1 = true,
.start_err = MCI_STARTBITERR,
.opendrain = MCI_ROD,
}; };
static struct variant_data variant_arm_extended_fifo_hwfc = { static struct variant_data variant_arm_extended_fifo_hwfc = {
...@@ -137,6 +150,9 @@ static struct variant_data variant_arm_extended_fifo_hwfc = { ...@@ -137,6 +150,9 @@ static struct variant_data variant_arm_extended_fifo_hwfc = {
.datalength_bits = 16, .datalength_bits = 16,
.pwrreg_powerup = MCI_PWR_UP, .pwrreg_powerup = MCI_PWR_UP,
.f_max = 100000000, .f_max = 100000000,
.mmcimask1 = true,
.start_err = MCI_STARTBITERR,
.opendrain = MCI_ROD,
}; };
static struct variant_data variant_u300 = { static struct variant_data variant_u300 = {
...@@ -152,6 +168,9 @@ static struct variant_data variant_u300 = { ...@@ -152,6 +168,9 @@ static struct variant_data variant_u300 = {
.signal_direction = true, .signal_direction = true,
.pwrreg_clkgate = true, .pwrreg_clkgate = true,
.pwrreg_nopower = true, .pwrreg_nopower = true,
.mmcimask1 = true,
.start_err = MCI_STARTBITERR,
.opendrain = MCI_OD,
}; };
static struct variant_data variant_nomadik = { static struct variant_data variant_nomadik = {
...@@ -168,6 +187,9 @@ static struct variant_data variant_nomadik = { ...@@ -168,6 +187,9 @@ static struct variant_data variant_nomadik = {
.signal_direction = true, .signal_direction = true,
.pwrreg_clkgate = true, .pwrreg_clkgate = true,
.pwrreg_nopower = true, .pwrreg_nopower = true,
.mmcimask1 = true,
.start_err = MCI_STARTBITERR,
.opendrain = MCI_OD,
}; };
static struct variant_data variant_ux500 = { static struct variant_data variant_ux500 = {
...@@ -190,6 +212,9 @@ static struct variant_data variant_ux500 = { ...@@ -190,6 +212,9 @@ static struct variant_data variant_ux500 = {
.busy_detect_flag = MCI_ST_CARDBUSY, .busy_detect_flag = MCI_ST_CARDBUSY,
.busy_detect_mask = MCI_ST_BUSYENDMASK, .busy_detect_mask = MCI_ST_BUSYENDMASK,
.pwrreg_nopower = true, .pwrreg_nopower = true,
.mmcimask1 = true,
.start_err = MCI_STARTBITERR,
.opendrain = MCI_OD,
}; };
static struct variant_data variant_ux500v2 = { static struct variant_data variant_ux500v2 = {
...@@ -214,6 +239,26 @@ static struct variant_data variant_ux500v2 = { ...@@ -214,6 +239,26 @@ static struct variant_data variant_ux500v2 = {
.busy_detect_flag = MCI_ST_CARDBUSY, .busy_detect_flag = MCI_ST_CARDBUSY,
.busy_detect_mask = MCI_ST_BUSYENDMASK, .busy_detect_mask = MCI_ST_BUSYENDMASK,
.pwrreg_nopower = true, .pwrreg_nopower = true,
.mmcimask1 = true,
.start_err = MCI_STARTBITERR,
.opendrain = MCI_OD,
};
static struct variant_data variant_stm32 = {
.fifosize = 32 * 4,
.fifohalfsize = 8 * 4,
.clkreg = MCI_CLK_ENABLE,
.clkreg_enable = MCI_ST_UX500_HWFCEN,
.clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS,
.clkreg_neg_edge_enable = MCI_ST_UX500_NEG_EDGE,
.datalength_bits = 24,
.datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN,
.st_sdio = true,
.st_clkdiv = true,
.pwrreg_powerup = MCI_PWR_ON,
.f_max = 48000000,
.pwrreg_clkgate = true,
.pwrreg_nopower = true,
}; };
static struct variant_data variant_qcom = { static struct variant_data variant_qcom = {
...@@ -232,6 +277,9 @@ static struct variant_data variant_qcom = { ...@@ -232,6 +277,9 @@ static struct variant_data variant_qcom = {
.explicit_mclk_control = true, .explicit_mclk_control = true,
.qcom_fifo = true, .qcom_fifo = true,
.qcom_dml = true, .qcom_dml = true,
.mmcimask1 = true,
.start_err = MCI_STARTBITERR,
.opendrain = MCI_ROD,
}; };
/* Busy detection for the ST Micro variant */ /* Busy detection for the ST Micro variant */
...@@ -396,6 +444,7 @@ mmci_request_end(struct mmci_host *host, struct mmc_request *mrq) ...@@ -396,6 +444,7 @@ mmci_request_end(struct mmci_host *host, struct mmc_request *mrq)
static void mmci_set_mask1(struct mmci_host *host, unsigned int mask) static void mmci_set_mask1(struct mmci_host *host, unsigned int mask)
{ {
void __iomem *base = host->base; void __iomem *base = host->base;
struct variant_data *variant = host->variant;
if (host->singleirq) { if (host->singleirq) {
unsigned int mask0 = readl(base + MMCIMASK0); unsigned int mask0 = readl(base + MMCIMASK0);
...@@ -406,7 +455,10 @@ static void mmci_set_mask1(struct mmci_host *host, unsigned int mask) ...@@ -406,7 +455,10 @@ static void mmci_set_mask1(struct mmci_host *host, unsigned int mask)
writel(mask0, base + MMCIMASK0); writel(mask0, base + MMCIMASK0);
} }
if (variant->mmcimask1)
writel(mask, base + MMCIMASK1); writel(mask, base + MMCIMASK1);
host->mask1_reg = mask;
} }
static void mmci_stop_data(struct mmci_host *host) static void mmci_stop_data(struct mmci_host *host)
...@@ -921,8 +973,9 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data, ...@@ -921,8 +973,9 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
return; return;
/* First check for errors */ /* First check for errors */
if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_STARTBITERR| if (status & (MCI_DATACRCFAIL | MCI_DATATIMEOUT |
MCI_TXUNDERRUN|MCI_RXOVERRUN)) { host->variant->start_err |
MCI_TXUNDERRUN | MCI_RXOVERRUN)) {
u32 remain, success; u32 remain, success;
/* Terminate the DMA transfer */ /* Terminate the DMA transfer */
...@@ -1286,7 +1339,7 @@ static irqreturn_t mmci_irq(int irq, void *dev_id) ...@@ -1286,7 +1339,7 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
status = readl(host->base + MMCISTATUS); status = readl(host->base + MMCISTATUS);
if (host->singleirq) { if (host->singleirq) {
if (status & readl(host->base + MMCIMASK1)) if (status & host->mask1_reg)
mmci_pio_irq(irq, dev_id); mmci_pio_irq(irq, dev_id);
status &= ~MCI_IRQ1MASK; status &= ~MCI_IRQ1MASK;
...@@ -1429,16 +1482,18 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ...@@ -1429,16 +1482,18 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
~MCI_ST_DATA2DIREN); ~MCI_ST_DATA2DIREN);
} }
if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) { if (variant->opendrain) {
if (host->hw_designer != AMBA_VENDOR_ST) if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
pwr |= MCI_ROD; pwr |= variant->opendrain;
else { } else {
/* /*
* The ST Micro variant use the ROD bit for something * If the variant cannot configure the pads by its own, then we
* else and only has OD (Open Drain). * expect the pinctrl to be able to do that for us
*/ */
pwr |= MCI_OD; if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
} pinctrl_select_state(host->pinctrl, host->pins_opendrain);
else
pinctrl_select_state(host->pinctrl, host->pins_default);
} }
/* /*
...@@ -1583,6 +1638,35 @@ static int mmci_probe(struct amba_device *dev, ...@@ -1583,6 +1638,35 @@ static int mmci_probe(struct amba_device *dev,
host = mmc_priv(mmc); host = mmc_priv(mmc);
host->mmc = mmc; host->mmc = mmc;
/*
* Some variant (STM32) doesn't have opendrain bit, nevertheless
* pins can be set accordingly using pinctrl
*/
if (!variant->opendrain) {
host->pinctrl = devm_pinctrl_get(&dev->dev);
if (IS_ERR(host->pinctrl)) {
dev_err(&dev->dev, "failed to get pinctrl");
ret = PTR_ERR(host->pinctrl);
goto host_free;
}
host->pins_default = pinctrl_lookup_state(host->pinctrl,
PINCTRL_STATE_DEFAULT);
if (IS_ERR(host->pins_default)) {
dev_err(mmc_dev(mmc), "Can't select default pins\n");
ret = PTR_ERR(host->pins_default);
goto host_free;
}
host->pins_opendrain = pinctrl_lookup_state(host->pinctrl,
MMCI_PINCTRL_STATE_OPENDRAIN);
if (IS_ERR(host->pins_opendrain)) {
dev_err(mmc_dev(mmc), "Can't select opendrain pins\n");
ret = PTR_ERR(host->pins_opendrain);
goto host_free;
}
}
host->hw_designer = amba_manf(dev); host->hw_designer = amba_manf(dev);
host->hw_revision = amba_rev(dev); host->hw_revision = amba_rev(dev);
dev_dbg(mmc_dev(mmc), "designer ID = 0x%02x\n", host->hw_designer); dev_dbg(mmc_dev(mmc), "designer ID = 0x%02x\n", host->hw_designer);
...@@ -1729,7 +1813,10 @@ static int mmci_probe(struct amba_device *dev, ...@@ -1729,7 +1813,10 @@ static int mmci_probe(struct amba_device *dev,
spin_lock_init(&host->lock); spin_lock_init(&host->lock);
writel(0, host->base + MMCIMASK0); writel(0, host->base + MMCIMASK0);
if (variant->mmcimask1)
writel(0, host->base + MMCIMASK1); writel(0, host->base + MMCIMASK1);
writel(0xfff, host->base + MMCICLEAR); writel(0xfff, host->base + MMCICLEAR);
/* /*
...@@ -1809,6 +1896,7 @@ static int mmci_remove(struct amba_device *dev) ...@@ -1809,6 +1896,7 @@ static int mmci_remove(struct amba_device *dev)
if (mmc) { if (mmc) {
struct mmci_host *host = mmc_priv(mmc); struct mmci_host *host = mmc_priv(mmc);
struct variant_data *variant = host->variant;
/* /*
* Undo pm_runtime_put() in probe. We use the _sync * Undo pm_runtime_put() in probe. We use the _sync
...@@ -1819,6 +1907,8 @@ static int mmci_remove(struct amba_device *dev) ...@@ -1819,6 +1907,8 @@ static int mmci_remove(struct amba_device *dev)
mmc_remove_host(mmc); mmc_remove_host(mmc);
writel(0, host->base + MMCIMASK0); writel(0, host->base + MMCIMASK0);
if (variant->mmcimask1)
writel(0, host->base + MMCIMASK1); writel(0, host->base + MMCIMASK1);
writel(0, host->base + MMCICOMMAND); writel(0, host->base + MMCICOMMAND);
...@@ -1951,6 +2041,11 @@ static const struct amba_id mmci_ids[] = { ...@@ -1951,6 +2041,11 @@ static const struct amba_id mmci_ids[] = {
.mask = 0xf0ffffff, .mask = 0xf0ffffff,
.data = &variant_ux500v2, .data = &variant_ux500v2,
}, },
{
.id = 0x00880180,
.mask = 0x00ffffff,
.data = &variant_stm32,
},
/* Qualcomm variants */ /* Qualcomm variants */
{ {
.id = 0x00051180, .id = 0x00051180,
......
...@@ -192,6 +192,8 @@ ...@@ -192,6 +192,8 @@
#define NR_SG 128 #define NR_SG 128
#define MMCI_PINCTRL_STATE_OPENDRAIN "opendrain"
struct clk; struct clk;
struct variant_data; struct variant_data;
struct dma_chan; struct dma_chan;
...@@ -223,9 +225,13 @@ struct mmci_host { ...@@ -223,9 +225,13 @@ struct mmci_host {
u32 clk_reg; u32 clk_reg;
u32 datactrl_reg; u32 datactrl_reg;
u32 busy_status; u32 busy_status;
u32 mask1_reg;
bool vqmmc_enabled; bool vqmmc_enabled;
struct mmci_platform_data *plat; struct mmci_platform_data *plat;
struct variant_data *variant; struct variant_data *variant;
struct pinctrl *pinctrl;
struct pinctrl_state *pins_default;
struct pinctrl_state *pins_opendrain;
u8 hw_designer; u8 hw_designer;
u8 hw_revision:4; u8 hw_revision:4;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -1660,7 +1660,7 @@ static int s3cmci_probe(struct platform_device *pdev) ...@@ -1660,7 +1660,7 @@ static int s3cmci_probe(struct platform_device *pdev)
} }
host->irq = platform_get_irq(pdev, 0); host->irq = platform_get_irq(pdev, 0);
if (host->irq == 0) { if (host->irq <= 0) {
dev_err(&pdev->dev, "failed to get interrupt resource.\n"); dev_err(&pdev->dev, "failed to get interrupt resource.\n");
ret = -EINVAL; ret = -EINVAL;
goto probe_iounmap; goto probe_iounmap;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -82,6 +82,10 @@ static int sdhci_probe(struct platform_device *pdev) ...@@ -82,6 +82,10 @@ static int sdhci_probe(struct platform_device *pdev)
host->hw_name = "sdhci"; host->hw_name = "sdhci";
host->ops = &sdhci_pltfm_ops; host->ops = &sdhci_pltfm_ops;
host->irq = platform_get_irq(pdev, 0); host->irq = platform_get_irq(pdev, 0);
if (host->irq <= 0) {
ret = -EINVAL;
goto err_host;
}
host->quirks = SDHCI_QUIRK_BROKEN_ADMA; host->quirks = SDHCI_QUIRK_BROKEN_ADMA;
sdhci = sdhci_priv(host); sdhci = sdhci_priv(host);
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment