mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-28 18:51:56 -04:00
MMC core:
- Cleanup BKOPS support - Introduce MMC_CAP_SYNC_RUNTIME_PM - slot-gpio: Delete legacy slot GPIO handling MMC host: - alcor: Add new mmc host driver for Alcor Micro PCI based cardreader - bcm2835: Several improvements to better recover from errors - jz4740: Rework and fixup pre|post_req support - mediatek: Add support for SDIO IRQs - meson-gx: Improve clock phase management - meson-gx: Stop descriptor on errors - mmci: Complete the sbc error path by sending a stop command - renesas_sdhi/tmio: Fixup reset/resume operations - renesas_sdhi: Add support for r8a774c0 and R7S9210 - renesas_sdhi: Whitelist R8A77990 SDHI - renesas_sdhi: Fixup eMMC HS400 compatibility issues for H3 and M3-W - rtsx_usb_sdmmc: Re-work card detection/removal support - rtsx_usb_sdmmc: Re-work runtime PM support - sdhci: Fix timeout loops for some variant drivers - sdhci: Improve support for error handling due to failing commands - sdhci-acpi/pci: Disable LED control for Intel BYT-based controllers - sdhci_am654: Add new SDHCI variant driver to support TI's AM654 SOCs - sdhci-of-esdhc: Add support for eMMC HS400 mode - sdhci-omap: Fixup reset support - sdhci-omap: Workaround errata regarding SDR104/HS200 tuning failures - sdhci-msm: Fixup sporadic write transfers issues for SDR104/HS200 - sdhci-msm: Fixup dynamical clock gating issues - various: Complete converting all hosts into using slot GPIO descriptors Other: - Move GPIO mmc platform data for mips/sh/arm to GPIO descriptors - Add new Alcor Micro cardreader PCI driver - Support runtime power management for memstick rtsx_usb_ms driver - Use USB remote wakeups for card detection for rtsx_usb misc driver -----BEGIN PGP SIGNATURE----- iQJLBAABCgA1FiEEugLDXPmKSktSkQsV/iaEJXNYjCkFAlwk3CcXHHVsZi5oYW5z c29uQGxpbmFyby5vcmcACgkQ/iaEJXNYjCn3ug/+Kra3JxvVcD9I6NZV5CEBWRdw nlNN/hexyzpf+zJ6Gb/YS1PSNVQl3a/gND+7mQRHQxJobhkSzaJ3vkZqRMo2HN8p D1Gh1j2qBfX2uKj87Svy8nygIulbDbeiBYWrNV070JQaOki9osWTv2JRGl2zufc8 zonoW1Aou9K6AkrFoFKiaiIZFG9+h5imGSdZTTZ17iOMvs/3DzhjV8UgIvye0Tzm Pic/4m6C7YeU7cj+aWyJFRgVuR3AG041d1likIuufxKwwhMSPf16L/xK1q8P8CCQ ErScSODqo0hGPmRLNQ7lBN+3A3NLBWOw2Ph5OabfNIPWz1kr6s2ixN9pxkPT7usE YMnVQ0YA0fJ13SbtdZ/mjr2A2zMkHN+4PNQC6DRDiDt4WWdNC/1aedOk0CKxRPME ppw8MnbSl3lranNoz+opU10spSXZ2m5sGI3t7gD032PJfM3dOcJgLNTpcES5NdTR jxqD/RYrtlg4IwZoLZgNt6BPIHBIo+D7JobqcLbELC3MKSSrO9nTKGHF2HxF6Nes YvCzKrUAsuxKSVAuNSq/f0ZP0Uk2Nic6iN7Kt2tmkpiMZ2CmynXNtyk/Ff1b1FF/ urqOSjKYvq2bvyej5fVMGg6cieEsPZr3CiHYNWq3vwpDK87HsraO3op/qj3ud0Y0 nAPkQbeHfKKhwPGtSQU= =mB5P -----END PGP SIGNATURE----- Merge tag 'mmc-v4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc Pull MMC updates from Ulf Hansson: "This time, this pull request contains changes crossing subsystems and archs/platforms, which is mainly because of a bigger modernization of moving from legacy GPIO to GPIO descriptors for MMC (by Linus Walleij). Additionally, once again, I am funneling changes to drivers/misc/cardreader/* and drivers/memstick/* through my MMC tree, mostly due to that we lack a maintainer for these. Summary: MMC core: - Cleanup BKOPS support - Introduce MMC_CAP_SYNC_RUNTIME_PM - slot-gpio: Delete legacy slot GPIO handling MMC host: - alcor: Add new mmc host driver for Alcor Micro PCI based cardreader - bcm2835: Several improvements to better recover from errors - jz4740: Rework and fixup pre|post_req support - mediatek: Add support for SDIO IRQs - meson-gx: Improve clock phase management - meson-gx: Stop descriptor on errors - mmci: Complete the sbc error path by sending a stop command - renesas_sdhi/tmio: Fixup reset/resume operations - renesas_sdhi: Add support for r8a774c0 and R7S9210 - renesas_sdhi: Whitelist R8A77990 SDHI - renesas_sdhi: Fixup eMMC HS400 compatibility issues for H3 and M3-W - rtsx_usb_sdmmc: Re-work card detection/removal support - rtsx_usb_sdmmc: Re-work runtime PM support - sdhci: Fix timeout loops for some variant drivers - sdhci: Improve support for error handling due to failing commands - sdhci-acpi/pci: Disable LED control for Intel BYT-based controllers - sdhci_am654: Add new SDHCI variant driver to support TI's AM654 SOCs - sdhci-of-esdhc: Add support for eMMC HS400 mode - sdhci-omap: Fixup reset support - sdhci-omap: Workaround errata regarding SDR104/HS200 tuning failures - sdhci-msm: Fixup sporadic write transfers issues for SDR104/HS200 - sdhci-msm: Fixup dynamical clock gating issues - various: Complete converting all hosts into using slot GPIO descriptors Other: - Move GPIO mmc platform data for mips/sh/arm to GPIO descriptors - Add new Alcor Micro cardreader PCI driver - Support runtime power management for memstick rtsx_usb_ms driver - Use USB remote wakeups for card detection for rtsx_usb misc driver" * tag 'mmc-v4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (99 commits) mmc: mediatek: Add MMC_CAP_SDIO_IRQ support mmc: renesas_sdhi_internal_dmac: Whitelist r8a774c0 dt-bindings: mmc: renesas_sdhi: Add r8a774c0 support mmc: core: Cleanup BKOPS support mmc: core: Drop redundant check in mmc_send_hpi_cmd() mmc: sdhci-omap: Workaround errata regarding SDR104/HS200 tuning failures (i929) dt-bindings: sdhci-omap: Add note for cpu_thermal mmc: sdhci-acpi: Disable LED control for Intel BYT-based controllers mmc: sdhci-pci: Disable LED control for Intel BYT-based controllers mmc: sdhci: Add quirk to disable LED control mmc: mmci: add variant property to set command stop bit misc: alcor_pci: fix spelling mistake "invailid" -> "invalid" mmc: meson-gx: add signal resampling mmc: meson-gx: align default phase on soc vendor tree mmc: meson-gx: remove useless lock mmc: meson-gx: make sure the descriptor is stopped on errors mmc: sdhci_am654: Add Initial Support for AM654 SDHCI driver dt-bindings: mmc: sdhci-of-arasan: Add deprecated message for AM65 dt-bindings: mmc: sdhci-am654: Document bindings for the host controllers on TI's AM654 SOCs mmc: sdhci-msm: avoid unused function warning ...
This commit is contained in:
commit
00d59fde85
117 changed files with 4010 additions and 1097 deletions
|
|
@ -1960,7 +1960,7 @@ static void mmc_blk_urgent_bkops(struct mmc_queue *mq,
|
|||
struct mmc_queue_req *mqrq)
|
||||
{
|
||||
if (mmc_blk_urgent_bkops_needed(mq, mqrq))
|
||||
mmc_start_bkops(mq->card, true);
|
||||
mmc_run_bkops(mq->card);
|
||||
}
|
||||
|
||||
void mmc_blk_mq_complete(struct request *req)
|
||||
|
|
|
|||
|
|
@ -23,15 +23,13 @@
|
|||
#define MMC_STATE_BLOCKADDR (1<<2) /* card uses block-addressing */
|
||||
#define MMC_CARD_SDXC (1<<3) /* card is SDXC */
|
||||
#define MMC_CARD_REMOVED (1<<4) /* card has been removed */
|
||||
#define MMC_STATE_DOING_BKOPS (1<<5) /* card is doing BKOPS */
|
||||
#define MMC_STATE_SUSPENDED (1<<6) /* card is suspended */
|
||||
#define MMC_STATE_SUSPENDED (1<<5) /* card is suspended */
|
||||
|
||||
#define mmc_card_present(c) ((c)->state & MMC_STATE_PRESENT)
|
||||
#define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY)
|
||||
#define mmc_card_blockaddr(c) ((c)->state & MMC_STATE_BLOCKADDR)
|
||||
#define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC)
|
||||
#define mmc_card_removed(c) ((c) && ((c)->state & MMC_CARD_REMOVED))
|
||||
#define mmc_card_doing_bkops(c) ((c)->state & MMC_STATE_DOING_BKOPS)
|
||||
#define mmc_card_suspended(c) ((c)->state & MMC_STATE_SUSPENDED)
|
||||
|
||||
#define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT)
|
||||
|
|
@ -39,8 +37,6 @@
|
|||
#define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR)
|
||||
#define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC)
|
||||
#define mmc_card_set_removed(c) ((c)->state |= MMC_CARD_REMOVED)
|
||||
#define mmc_card_set_doing_bkops(c) ((c)->state |= MMC_STATE_DOING_BKOPS)
|
||||
#define mmc_card_clr_doing_bkops(c) ((c)->state &= ~MMC_STATE_DOING_BKOPS)
|
||||
#define mmc_card_set_suspended(c) ((c)->state |= MMC_STATE_SUSPENDED)
|
||||
#define mmc_card_clr_suspended(c) ((c)->state &= ~MMC_STATE_SUSPENDED)
|
||||
|
||||
|
|
|
|||
|
|
@ -887,7 +887,10 @@ void mmc_release_host(struct mmc_host *host)
|
|||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
wake_up(&host->wq);
|
||||
pm_runtime_mark_last_busy(mmc_dev(host));
|
||||
pm_runtime_put_autosuspend(mmc_dev(host));
|
||||
if (host->caps & MMC_CAP_SYNC_RUNTIME_PM)
|
||||
pm_runtime_put_sync_suspend(mmc_dev(host));
|
||||
else
|
||||
pm_runtime_put_autosuspend(mmc_dev(host));
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_release_host);
|
||||
|
|
@ -2413,20 +2416,6 @@ int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen)
|
|||
}
|
||||
EXPORT_SYMBOL(mmc_set_blocklen);
|
||||
|
||||
int mmc_set_blockcount(struct mmc_card *card, unsigned int blockcount,
|
||||
bool is_rel_write)
|
||||
{
|
||||
struct mmc_command cmd = {};
|
||||
|
||||
cmd.opcode = MMC_SET_BLOCK_COUNT;
|
||||
cmd.arg = blockcount & 0x0000FFFF;
|
||||
if (is_rel_write)
|
||||
cmd.arg |= 1 << 31;
|
||||
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
|
||||
return mmc_wait_for_cmd(card->host, &cmd, 5);
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_set_blockcount);
|
||||
|
||||
static void mmc_hw_reset_for_init(struct mmc_host *host)
|
||||
{
|
||||
mmc_pwrseq_reset(host);
|
||||
|
|
|
|||
|
|
@ -118,8 +118,6 @@ int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from,
|
|||
unsigned int mmc_calc_max_discard(struct mmc_card *card);
|
||||
|
||||
int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen);
|
||||
int mmc_set_blockcount(struct mmc_card *card, unsigned int blockcount,
|
||||
bool is_rel_write);
|
||||
|
||||
int __mmc_claim_host(struct mmc_host *host, struct mmc_ctx *ctx,
|
||||
atomic_t *abort);
|
||||
|
|
|
|||
|
|
@ -1181,6 +1181,9 @@ static int mmc_select_hs400(struct mmc_card *card)
|
|||
if (err)
|
||||
goto out_err;
|
||||
|
||||
if (host->ops->hs400_prepare_ddr)
|
||||
host->ops->hs400_prepare_ddr(host);
|
||||
|
||||
/* Switch card to DDR */
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_BUS_WIDTH,
|
||||
|
|
@ -2011,12 +2014,6 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
|
|||
if (mmc_card_suspended(host->card))
|
||||
goto out;
|
||||
|
||||
if (mmc_card_doing_bkops(host->card)) {
|
||||
err = mmc_stop_bkops(host->card);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = mmc_flush_cache(host->card);
|
||||
if (err)
|
||||
goto out;
|
||||
|
|
|
|||
|
|
@ -802,12 +802,6 @@ static int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status)
|
|||
unsigned int opcode;
|
||||
int err;
|
||||
|
||||
if (!card->ext_csd.hpi) {
|
||||
pr_warn("%s: Card didn't support HPI command\n",
|
||||
mmc_hostname(card->host));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
opcode = card->ext_csd.hpi_cmd;
|
||||
if (opcode == MMC_STOP_TRANSMISSION)
|
||||
cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
|
||||
|
|
@ -897,34 +891,6 @@ int mmc_can_ext_csd(struct mmc_card *card)
|
|||
return (card && card->csd.mmca_vsn > CSD_SPEC_VER_3);
|
||||
}
|
||||
|
||||
/**
|
||||
* mmc_stop_bkops - stop ongoing BKOPS
|
||||
* @card: MMC card to check BKOPS
|
||||
*
|
||||
* Send HPI command to stop ongoing background operations to
|
||||
* allow rapid servicing of foreground operations, e.g. read/
|
||||
* writes. Wait until the card comes out of the programming state
|
||||
* to avoid errors in servicing read/write requests.
|
||||
*/
|
||||
int mmc_stop_bkops(struct mmc_card *card)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
err = mmc_interrupt_hpi(card);
|
||||
|
||||
/*
|
||||
* If err is EINVAL, we can't issue an HPI.
|
||||
* It should complete the BKOPS.
|
||||
*/
|
||||
if (!err || (err == -EINVAL)) {
|
||||
mmc_card_clr_doing_bkops(card);
|
||||
mmc_retune_release(card->host);
|
||||
err = 0;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mmc_read_bkops_status(struct mmc_card *card)
|
||||
{
|
||||
int err;
|
||||
|
|
@ -941,22 +907,17 @@ static int mmc_read_bkops_status(struct mmc_card *card)
|
|||
}
|
||||
|
||||
/**
|
||||
* mmc_start_bkops - start BKOPS for supported cards
|
||||
* @card: MMC card to start BKOPS
|
||||
* @from_exception: A flag to indicate if this function was
|
||||
* called due to an exception raised by the card
|
||||
* mmc_run_bkops - Run BKOPS for supported cards
|
||||
* @card: MMC card to run BKOPS for
|
||||
*
|
||||
* Start background operations whenever requested.
|
||||
* When the urgent BKOPS bit is set in a R1 command response
|
||||
* then background operations should be started immediately.
|
||||
* Run background operations synchronously for cards having manual BKOPS
|
||||
* enabled and in case it reports urgent BKOPS level.
|
||||
*/
|
||||
void mmc_start_bkops(struct mmc_card *card, bool from_exception)
|
||||
void mmc_run_bkops(struct mmc_card *card)
|
||||
{
|
||||
int err;
|
||||
int timeout;
|
||||
bool use_busy_signal;
|
||||
|
||||
if (!card->ext_csd.man_bkops_en || mmc_card_doing_bkops(card))
|
||||
if (!card->ext_csd.man_bkops_en)
|
||||
return;
|
||||
|
||||
err = mmc_read_bkops_status(card);
|
||||
|
|
@ -966,44 +927,26 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!card->ext_csd.raw_bkops_status)
|
||||
if (!card->ext_csd.raw_bkops_status ||
|
||||
card->ext_csd.raw_bkops_status < EXT_CSD_BKOPS_LEVEL_2)
|
||||
return;
|
||||
|
||||
if (card->ext_csd.raw_bkops_status < EXT_CSD_BKOPS_LEVEL_2 &&
|
||||
from_exception)
|
||||
return;
|
||||
|
||||
if (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2) {
|
||||
timeout = MMC_OPS_TIMEOUT_MS;
|
||||
use_busy_signal = true;
|
||||
} else {
|
||||
timeout = 0;
|
||||
use_busy_signal = false;
|
||||
}
|
||||
|
||||
mmc_retune_hold(card->host);
|
||||
|
||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_BKOPS_START, 1, timeout, 0,
|
||||
use_busy_signal, true, false);
|
||||
if (err) {
|
||||
/*
|
||||
* For urgent BKOPS status, LEVEL_2 and higher, let's execute
|
||||
* synchronously. Future wise, we may consider to start BKOPS, for less
|
||||
* urgent levels by using an asynchronous background task, when idle.
|
||||
*/
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_BKOPS_START, 1, MMC_OPS_TIMEOUT_MS);
|
||||
if (err)
|
||||
pr_warn("%s: Error %d starting bkops\n",
|
||||
mmc_hostname(card->host), err);
|
||||
mmc_retune_release(card->host);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* For urgent bkops status (LEVEL_2 and more)
|
||||
* bkops executed synchronously, otherwise
|
||||
* the operation is in progress
|
||||
*/
|
||||
if (!use_busy_signal)
|
||||
mmc_card_set_doing_bkops(card);
|
||||
else
|
||||
mmc_retune_release(card->host);
|
||||
mmc_retune_release(card->host);
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_start_bkops);
|
||||
EXPORT_SYMBOL(mmc_run_bkops);
|
||||
|
||||
/*
|
||||
* Flush the cache to the non-volatile storage.
|
||||
|
|
|
|||
|
|
@ -40,8 +40,7 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
|||
bool use_busy_signal, bool send_status, bool retry_crc_err);
|
||||
int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
||||
unsigned int timeout_ms);
|
||||
int mmc_stop_bkops(struct mmc_card *card);
|
||||
void mmc_start_bkops(struct mmc_card *card, bool from_exception);
|
||||
void mmc_run_bkops(struct mmc_card *card);
|
||||
int mmc_flush_cache(struct mmc_card *card);
|
||||
int mmc_cmdq_enable(struct mmc_card *card);
|
||||
int mmc_cmdq_disable(struct mmc_card *card);
|
||||
|
|
|
|||
|
|
@ -3145,17 +3145,7 @@ static int mtf_testlist_show(struct seq_file *sf, void *data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int mtf_testlist_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, mtf_testlist_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations mmc_test_fops_testlist = {
|
||||
.open = mtf_testlist_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
DEFINE_SHOW_ATTRIBUTE(mtf_testlist);
|
||||
|
||||
static void mmc_test_free_dbgfs_file(struct mmc_card *card)
|
||||
{
|
||||
|
|
@ -3216,7 +3206,7 @@ static int mmc_test_register_dbgfs_file(struct mmc_card *card)
|
|||
goto err;
|
||||
|
||||
ret = __mmc_test_register_dbgfs_file(card, "testlist", S_IRUGO,
|
||||
&mmc_test_fops_testlist);
|
||||
&mtf_testlist_fops);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/jiffies.h>
|
||||
|
|
@ -27,8 +26,8 @@ struct mmc_gpio {
|
|||
bool override_cd_active_level;
|
||||
irqreturn_t (*cd_gpio_isr)(int irq, void *dev_id);
|
||||
char *ro_label;
|
||||
char *cd_label;
|
||||
u32 cd_debounce_delay_ms;
|
||||
char cd_label[];
|
||||
};
|
||||
|
||||
static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id)
|
||||
|
|
@ -45,15 +44,19 @@ static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id)
|
|||
|
||||
int mmc_gpio_alloc(struct mmc_host *host)
|
||||
{
|
||||
size_t len = strlen(dev_name(host->parent)) + 4;
|
||||
struct mmc_gpio *ctx = devm_kzalloc(host->parent,
|
||||
sizeof(*ctx) + 2 * len, GFP_KERNEL);
|
||||
sizeof(*ctx), GFP_KERNEL);
|
||||
|
||||
if (ctx) {
|
||||
ctx->ro_label = ctx->cd_label + len;
|
||||
ctx->cd_debounce_delay_ms = 200;
|
||||
snprintf(ctx->cd_label, len, "%s cd", dev_name(host->parent));
|
||||
snprintf(ctx->ro_label, len, "%s ro", dev_name(host->parent));
|
||||
ctx->cd_label = devm_kasprintf(host->parent, GFP_KERNEL,
|
||||
"%s cd", dev_name(host->parent));
|
||||
if (!ctx->cd_label)
|
||||
return -ENOMEM;
|
||||
ctx->ro_label = devm_kasprintf(host->parent, GFP_KERNEL,
|
||||
"%s ro", dev_name(host->parent));
|
||||
if (!ctx->ro_label)
|
||||
return -ENOMEM;
|
||||
host->slot.handler_priv = ctx;
|
||||
host->slot.cd_irq = -EINVAL;
|
||||
}
|
||||
|
|
@ -98,36 +101,6 @@ int mmc_gpio_get_cd(struct mmc_host *host)
|
|||
}
|
||||
EXPORT_SYMBOL(mmc_gpio_get_cd);
|
||||
|
||||
/**
|
||||
* mmc_gpio_request_ro - request a gpio for write-protection
|
||||
* @host: mmc host
|
||||
* @gpio: gpio number requested
|
||||
*
|
||||
* As devm_* managed functions are used in mmc_gpio_request_ro(), client
|
||||
* drivers do not need to worry about freeing up memory.
|
||||
*
|
||||
* Returns zero on success, else an error.
|
||||
*/
|
||||
int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio)
|
||||
{
|
||||
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||
int ret;
|
||||
|
||||
if (!gpio_is_valid(gpio))
|
||||
return -EINVAL;
|
||||
|
||||
ret = devm_gpio_request_one(host->parent, gpio, GPIOF_DIR_IN,
|
||||
ctx->ro_label);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ctx->override_ro_active_level = true;
|
||||
ctx->ro_gpio = gpio_to_desc(gpio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_gpio_request_ro);
|
||||
|
||||
void mmc_gpiod_request_cd_irq(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||
|
|
@ -196,50 +169,6 @@ void mmc_gpio_set_cd_isr(struct mmc_host *host,
|
|||
}
|
||||
EXPORT_SYMBOL(mmc_gpio_set_cd_isr);
|
||||
|
||||
/**
|
||||
* mmc_gpio_request_cd - request a gpio for card-detection
|
||||
* @host: mmc host
|
||||
* @gpio: gpio number requested
|
||||
* @debounce: debounce time in microseconds
|
||||
*
|
||||
* As devm_* managed functions are used in mmc_gpio_request_cd(), client
|
||||
* drivers do not need to worry about freeing up memory.
|
||||
*
|
||||
* If GPIO debouncing is desired, set the debounce parameter to a non-zero
|
||||
* value. The caller is responsible for ensuring that the GPIO driver associated
|
||||
* with the GPIO supports debouncing, otherwise an error will be returned.
|
||||
*
|
||||
* Returns zero on success, else an error.
|
||||
*/
|
||||
int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio,
|
||||
unsigned int debounce)
|
||||
{
|
||||
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||
int ret;
|
||||
|
||||
ret = devm_gpio_request_one(host->parent, gpio, GPIOF_DIR_IN,
|
||||
ctx->cd_label);
|
||||
if (ret < 0)
|
||||
/*
|
||||
* don't bother freeing memory. It might still get used by other
|
||||
* slot functions, in any case it will be freed, when the device
|
||||
* is destroyed.
|
||||
*/
|
||||
return ret;
|
||||
|
||||
if (debounce) {
|
||||
ret = gpio_set_debounce(gpio, debounce);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ctx->override_cd_active_level = true;
|
||||
ctx->cd_gpio = gpio_to_desc(gpio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_gpio_request_cd);
|
||||
|
||||
/**
|
||||
* mmc_gpiod_request_cd - request a gpio descriptor for card-detection
|
||||
* @host: mmc host
|
||||
|
|
@ -250,8 +179,7 @@ EXPORT_SYMBOL(mmc_gpio_request_cd);
|
|||
* @gpio_invert: will return whether the GPIO line is inverted or not, set
|
||||
* to NULL to ignore
|
||||
*
|
||||
* Use this function in place of mmc_gpio_request_cd() to use the GPIO
|
||||
* descriptor API. Note that it must be called prior to mmc_add_host()
|
||||
* Note that this must be called prior to mmc_add_host()
|
||||
* otherwise the caller must also call mmc_gpiod_request_cd_irq().
|
||||
*
|
||||
* Returns zero on success, else an error.
|
||||
|
|
@ -302,9 +230,6 @@ EXPORT_SYMBOL(mmc_can_gpio_cd);
|
|||
* @gpio_invert: will return whether the GPIO line is inverted or not,
|
||||
* set to NULL to ignore
|
||||
*
|
||||
* Use this function in place of mmc_gpio_request_ro() to use the GPIO
|
||||
* descriptor API.
|
||||
*
|
||||
* Returns zero on success, else an error.
|
||||
*/
|
||||
int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
|
||||
|
|
|
|||
|
|
@ -441,6 +441,13 @@ config MMC_WBSD
|
|||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_ALCOR
|
||||
tristate "Alcor Micro/Alcor Link SD/MMC controller"
|
||||
depends on MISC_ALCOR_PCI
|
||||
help
|
||||
Say Y here to include driver code to support SD/MMC card interface
|
||||
of Alcor Micro PCI-E card reader
|
||||
|
||||
config MMC_AU1X
|
||||
tristate "Alchemy AU1XX0 MMC Card Interface support"
|
||||
depends on MIPS_ALCHEMY
|
||||
|
|
@ -646,13 +653,14 @@ config MMC_SDHI_SYS_DMAC
|
|||
|
||||
config MMC_SDHI_INTERNAL_DMAC
|
||||
tristate "DMA for SDHI SD/SDIO controllers using on-chip bus mastering"
|
||||
depends on ARM64 || ARCH_R8A77470 || COMPILE_TEST
|
||||
depends on ARM64 || ARCH_R7S9210 || ARCH_R8A77470 || COMPILE_TEST
|
||||
depends on MMC_SDHI
|
||||
default MMC_SDHI if (ARM64 || ARCH_R8A77470)
|
||||
default MMC_SDHI if (ARM64 || ARCH_R7S9210 || ARCH_R8A77470)
|
||||
help
|
||||
This provides DMA support for SDHI SD/SDIO controllers
|
||||
using on-chip bus mastering. This supports the controllers
|
||||
found in arm64 based SoCs.
|
||||
found in arm64 based SoCs. This controller is also found in
|
||||
some RZ family SoCs.
|
||||
|
||||
config MMC_UNIPHIER
|
||||
tristate "UniPhier SD/eMMC Host Controller support"
|
||||
|
|
@ -969,6 +977,8 @@ config MMC_SDHCI_XENON
|
|||
config MMC_SDHCI_OMAP
|
||||
tristate "TI SDHCI Controller Support"
|
||||
depends on MMC_SDHCI_PLTFM && OF
|
||||
select THERMAL
|
||||
select TI_SOC_THERMAL
|
||||
help
|
||||
This selects the Secure Digital Host Controller Interface (SDHCI)
|
||||
support present in TI's DRA7 SOCs. The controller supports
|
||||
|
|
@ -977,3 +987,15 @@ config MMC_SDHCI_OMAP
|
|||
If you have a controller with this interface, say Y or M here.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_SDHCI_AM654
|
||||
tristate "Support for the SDHCI Controller in TI's AM654 SOCs"
|
||||
depends on MMC_SDHCI_PLTFM && OF
|
||||
help
|
||||
This selects the Secure Digital Host Controller Interface (SDHCI)
|
||||
support present in TI's AM654 SOCs. The controller supports
|
||||
SD/MMC/SDIO devices.
|
||||
|
||||
If you have a controller with this interface, say Y or M here.
|
||||
|
||||
If unsure, say N.
|
||||
|
|
|
|||
|
|
@ -22,8 +22,10 @@ obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o
|
|||
obj-$(CONFIG_MMC_SDHCI_SIRF) += sdhci-sirf.o
|
||||
obj-$(CONFIG_MMC_SDHCI_F_SDH30) += sdhci_f_sdh30.o
|
||||
obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o
|
||||
obj-$(CONFIG_MMC_SDHCI_AM654) += sdhci_am654.o
|
||||
obj-$(CONFIG_MMC_WBSD) += wbsd.o
|
||||
obj-$(CONFIG_MMC_AU1X) += au1xmmc.o
|
||||
obj-$(CONFIG_MMC_ALCOR) += alcor.o
|
||||
obj-$(CONFIG_MMC_MTK) += mtk-sd.o
|
||||
obj-$(CONFIG_MMC_OMAP) += omap.o
|
||||
obj-$(CONFIG_MMC_OMAP_HS) += omap_hsmmc.o
|
||||
|
|
|
|||
1162
drivers/mmc/host/alcor.c
Normal file
1162
drivers/mmc/host/alcor.c
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -446,18 +446,7 @@ static int atmci_req_show(struct seq_file *s, void *v)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int atmci_req_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, atmci_req_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations atmci_req_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = atmci_req_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
DEFINE_SHOW_ATTRIBUTE(atmci_req);
|
||||
|
||||
static void atmci_show_status_reg(struct seq_file *s,
|
||||
const char *regname, u32 value)
|
||||
|
|
@ -583,18 +572,7 @@ static int atmci_regs_show(struct seq_file *s, void *v)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int atmci_regs_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, atmci_regs_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations atmci_regs_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = atmci_regs_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
DEFINE_SHOW_ATTRIBUTE(atmci_regs);
|
||||
|
||||
static void atmci_init_debugfs(struct atmel_mci_slot *slot)
|
||||
{
|
||||
|
|
@ -608,13 +586,14 @@ static void atmci_init_debugfs(struct atmel_mci_slot *slot)
|
|||
return;
|
||||
|
||||
node = debugfs_create_file("regs", S_IRUSR, root, host,
|
||||
&atmci_regs_fops);
|
||||
&atmci_regs_fops);
|
||||
if (IS_ERR(node))
|
||||
return;
|
||||
if (!node)
|
||||
goto err;
|
||||
|
||||
node = debugfs_create_file("req", S_IRUSR, root, slot, &atmci_req_fops);
|
||||
node = debugfs_create_file("req", S_IRUSR, root, slot,
|
||||
&atmci_req_fops);
|
||||
if (!node)
|
||||
goto err;
|
||||
|
||||
|
|
@ -1954,13 +1933,14 @@ static void atmci_tasklet_func(unsigned long priv)
|
|||
}
|
||||
|
||||
atmci_request_end(host, host->mrq);
|
||||
state = STATE_IDLE;
|
||||
goto unlock; /* atmci_request_end() sets host->state */
|
||||
break;
|
||||
}
|
||||
} while (state != prev_state);
|
||||
|
||||
host->state = state;
|
||||
|
||||
unlock:
|
||||
spin_unlock(&host->lock);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* bcm2835 sdhost driver.
|
||||
*
|
||||
|
|
@ -25,18 +26,6 @@
|
|||
* sdhci-bcm2708.c by Broadcom
|
||||
* sdhci-bcm2835.c by Stephen Warren and Oleksandr Tymoshenko
|
||||
* sdhci.c and sdhci-pci.c by Pierre Ossman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
|
|
@ -286,6 +275,7 @@ static void bcm2835_reset(struct mmc_host *mmc)
|
|||
|
||||
if (host->dma_chan)
|
||||
dmaengine_terminate_sync(host->dma_chan);
|
||||
host->dma_chan = NULL;
|
||||
bcm2835_reset_internal(host);
|
||||
}
|
||||
|
||||
|
|
@ -463,7 +453,7 @@ static void bcm2835_transfer_pio(struct bcm2835_host *host)
|
|||
static
|
||||
void bcm2835_prepare_dma(struct bcm2835_host *host, struct mmc_data *data)
|
||||
{
|
||||
int len, dir_data, dir_slave;
|
||||
int sg_len, dir_data, dir_slave;
|
||||
struct dma_async_tx_descriptor *desc = NULL;
|
||||
struct dma_chan *dma_chan;
|
||||
|
||||
|
|
@ -509,23 +499,24 @@ void bcm2835_prepare_dma(struct bcm2835_host *host, struct mmc_data *data)
|
|||
&host->dma_cfg_rx :
|
||||
&host->dma_cfg_tx);
|
||||
|
||||
len = dma_map_sg(dma_chan->device->dev, data->sg, data->sg_len,
|
||||
dir_data);
|
||||
sg_len = dma_map_sg(dma_chan->device->dev, data->sg, data->sg_len,
|
||||
dir_data);
|
||||
if (!sg_len)
|
||||
return;
|
||||
|
||||
if (len > 0) {
|
||||
desc = dmaengine_prep_slave_sg(dma_chan, data->sg,
|
||||
len, dir_slave,
|
||||
DMA_PREP_INTERRUPT |
|
||||
DMA_CTRL_ACK);
|
||||
desc = dmaengine_prep_slave_sg(dma_chan, data->sg, sg_len, dir_slave,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
|
||||
if (!desc) {
|
||||
dma_unmap_sg(dma_chan->device->dev, data->sg, sg_len, dir_data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (desc) {
|
||||
desc->callback = bcm2835_dma_complete;
|
||||
desc->callback_param = host;
|
||||
host->dma_desc = desc;
|
||||
host->dma_chan = dma_chan;
|
||||
host->dma_dir = dir_data;
|
||||
}
|
||||
desc->callback = bcm2835_dma_complete;
|
||||
desc->callback_param = host;
|
||||
host->dma_desc = desc;
|
||||
host->dma_chan = dma_chan;
|
||||
host->dma_dir = dir_data;
|
||||
}
|
||||
|
||||
static void bcm2835_start_dma(struct bcm2835_host *host)
|
||||
|
|
@ -607,7 +598,7 @@ static void bcm2835_finish_request(struct bcm2835_host *host)
|
|||
struct dma_chan *terminate_chan = NULL;
|
||||
struct mmc_request *mrq;
|
||||
|
||||
cancel_delayed_work(&host->timeout_work);
|
||||
cancel_delayed_work_sync(&host->timeout_work);
|
||||
|
||||
mrq = host->mrq;
|
||||
|
||||
|
|
@ -772,6 +763,8 @@ static void bcm2835_finish_command(struct bcm2835_host *host)
|
|||
|
||||
if (!(sdhsts & SDHSTS_CRC7_ERROR) ||
|
||||
(host->cmd->opcode != MMC_SEND_OP_COND)) {
|
||||
u32 edm, fsm;
|
||||
|
||||
if (sdhsts & SDHSTS_CMD_TIME_OUT) {
|
||||
host->cmd->error = -ETIMEDOUT;
|
||||
} else {
|
||||
|
|
@ -780,6 +773,13 @@ static void bcm2835_finish_command(struct bcm2835_host *host)
|
|||
bcm2835_dumpregs(host);
|
||||
host->cmd->error = -EILSEQ;
|
||||
}
|
||||
edm = readl(host->ioaddr + SDEDM);
|
||||
fsm = edm & SDEDM_FSM_MASK;
|
||||
if (fsm == SDEDM_FSM_READWAIT ||
|
||||
fsm == SDEDM_FSM_WRITESTART1)
|
||||
/* Kick the FSM out of its wait */
|
||||
writel(edm | SDEDM_FORCE_DATA_MODE,
|
||||
host->ioaddr + SDEDM);
|
||||
bcm2835_finish_request(host);
|
||||
return;
|
||||
}
|
||||
|
|
@ -837,6 +837,8 @@ static void bcm2835_timeout(struct work_struct *work)
|
|||
dev_err(dev, "timeout waiting for hardware interrupt.\n");
|
||||
bcm2835_dumpregs(host);
|
||||
|
||||
bcm2835_reset(host->mmc);
|
||||
|
||||
if (host->data) {
|
||||
host->data->error = -ETIMEDOUT;
|
||||
bcm2835_finish_data(host);
|
||||
|
|
@ -1052,10 +1054,12 @@ static void bcm2835_dma_complete_work(struct work_struct *work)
|
|||
{
|
||||
struct bcm2835_host *host =
|
||||
container_of(work, struct bcm2835_host, dma_work);
|
||||
struct mmc_data *data = host->data;
|
||||
struct mmc_data *data;
|
||||
|
||||
mutex_lock(&host->mutex);
|
||||
|
||||
data = host->data;
|
||||
|
||||
if (host->dma_chan) {
|
||||
dma_unmap_sg(host->dma_chan->device->dev,
|
||||
data->sg, data->sg_len,
|
||||
|
|
@ -1180,9 +1184,6 @@ static void bcm2835_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||
return;
|
||||
}
|
||||
|
||||
if (host->use_dma && mrq->data && (mrq->data->blocks > PIO_THRESHOLD))
|
||||
bcm2835_prepare_dma(host, mrq->data);
|
||||
|
||||
mutex_lock(&host->mutex);
|
||||
|
||||
WARN_ON(host->mrq);
|
||||
|
|
@ -1206,6 +1207,9 @@ static void bcm2835_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||
return;
|
||||
}
|
||||
|
||||
if (host->use_dma && mrq->data && (mrq->data->blocks > PIO_THRESHOLD))
|
||||
bcm2835_prepare_dma(host, mrq->data);
|
||||
|
||||
host->use_sbc = !!mrq->sbc && host->mrq->data &&
|
||||
(host->mrq->data->flags & MMC_DATA_READ);
|
||||
if (host->use_sbc) {
|
||||
|
|
@ -1445,6 +1449,9 @@ static int bcm2835_remove(struct platform_device *pdev)
|
|||
cancel_work_sync(&host->dma_work);
|
||||
cancel_delayed_work_sync(&host->timeout_work);
|
||||
|
||||
if (host->dma_chan_rxtx)
|
||||
dma_release_channel(host->dma_chan_rxtx);
|
||||
|
||||
mmc_free_host(host->mmc);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
|
|
|
|||
|
|
@ -52,16 +52,7 @@ MODULE_DEVICE_TABLE(of, dw_mci_bluefield_match);
|
|||
|
||||
static int dw_mci_bluefield_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct dw_mci_drv_data *drv_data = NULL;
|
||||
const struct of_device_id *match;
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
match = of_match_node(dw_mci_bluefield_match,
|
||||
pdev->dev.of_node);
|
||||
drv_data = match->data;
|
||||
}
|
||||
|
||||
return dw_mci_pltfm_register(pdev, drv_data);
|
||||
return dw_mci_pltfm_register(pdev, &bluefield_drv_data);
|
||||
}
|
||||
|
||||
static struct platform_driver dw_mci_bluefield_pltfm_driver = {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
#include <linux/dmaengine.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
|
|
@ -126,9 +126,23 @@ enum jz4740_mmc_state {
|
|||
JZ4740_MMC_STATE_DONE,
|
||||
};
|
||||
|
||||
struct jz4740_mmc_host_next {
|
||||
int sg_len;
|
||||
s32 cookie;
|
||||
/*
|
||||
* The MMC core allows to prepare a mmc_request while another mmc_request
|
||||
* is in-flight. This is used via the pre_req/post_req hooks.
|
||||
* This driver uses the pre_req/post_req hooks to map/unmap the mmc_request.
|
||||
* Following what other drivers do (sdhci, dw_mmc) we use the following cookie
|
||||
* flags to keep track of the mmc_request mapping state.
|
||||
*
|
||||
* COOKIE_UNMAPPED: the request is not mapped.
|
||||
* COOKIE_PREMAPPED: the request was mapped in pre_req,
|
||||
* and should be unmapped in post_req.
|
||||
* COOKIE_MAPPED: the request was mapped in the irq handler,
|
||||
* and should be unmapped before mmc_request_done is called..
|
||||
*/
|
||||
enum jz4780_cookie {
|
||||
COOKIE_UNMAPPED = 0,
|
||||
COOKIE_PREMAPPED,
|
||||
COOKIE_MAPPED,
|
||||
};
|
||||
|
||||
struct jz4740_mmc_host {
|
||||
|
|
@ -136,6 +150,7 @@ struct jz4740_mmc_host {
|
|||
struct platform_device *pdev;
|
||||
struct jz4740_mmc_platform_data *pdata;
|
||||
struct clk *clk;
|
||||
struct gpio_desc *power;
|
||||
|
||||
enum jz4740_mmc_version version;
|
||||
|
||||
|
|
@ -162,9 +177,7 @@ struct jz4740_mmc_host {
|
|||
/* DMA support */
|
||||
struct dma_chan *dma_rx;
|
||||
struct dma_chan *dma_tx;
|
||||
struct jz4740_mmc_host_next next_data;
|
||||
bool use_dma;
|
||||
int sg_len;
|
||||
|
||||
/* The DMA trigger level is 8 words, that is to say, the DMA read
|
||||
* trigger is when data words in MSC_RXFIFO is >= 8 and the DMA write
|
||||
|
|
@ -226,9 +239,6 @@ static int jz4740_mmc_acquire_dma_channels(struct jz4740_mmc_host *host)
|
|||
return PTR_ERR(host->dma_rx);
|
||||
}
|
||||
|
||||
/* Initialize DMA pre request cookie */
|
||||
host->next_data.cookie = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -245,60 +255,44 @@ static void jz4740_mmc_dma_unmap(struct jz4740_mmc_host *host,
|
|||
enum dma_data_direction dir = mmc_get_dma_dir(data);
|
||||
|
||||
dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, dir);
|
||||
data->host_cookie = COOKIE_UNMAPPED;
|
||||
}
|
||||
|
||||
/* Prepares DMA data for current/next transfer, returns non-zero on failure */
|
||||
/* Prepares DMA data for current or next transfer.
|
||||
* A request can be in-flight when this is called.
|
||||
*/
|
||||
static int jz4740_mmc_prepare_dma_data(struct jz4740_mmc_host *host,
|
||||
struct mmc_data *data,
|
||||
struct jz4740_mmc_host_next *next,
|
||||
struct dma_chan *chan)
|
||||
int cookie)
|
||||
{
|
||||
struct jz4740_mmc_host_next *next_data = &host->next_data;
|
||||
struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data);
|
||||
enum dma_data_direction dir = mmc_get_dma_dir(data);
|
||||
int sg_len;
|
||||
int sg_count;
|
||||
|
||||
if (!next && data->host_cookie &&
|
||||
data->host_cookie != host->next_data.cookie) {
|
||||
dev_warn(mmc_dev(host->mmc),
|
||||
"[%s] invalid cookie: data->host_cookie %d host->next_data.cookie %d\n",
|
||||
__func__,
|
||||
data->host_cookie,
|
||||
host->next_data.cookie);
|
||||
data->host_cookie = 0;
|
||||
}
|
||||
if (data->host_cookie == COOKIE_PREMAPPED)
|
||||
return data->sg_count;
|
||||
|
||||
/* Check if next job is already prepared */
|
||||
if (next || data->host_cookie != host->next_data.cookie) {
|
||||
sg_len = dma_map_sg(chan->device->dev,
|
||||
data->sg,
|
||||
data->sg_len,
|
||||
dir);
|
||||
sg_count = dma_map_sg(chan->device->dev,
|
||||
data->sg,
|
||||
data->sg_len,
|
||||
dir);
|
||||
|
||||
} else {
|
||||
sg_len = next_data->sg_len;
|
||||
next_data->sg_len = 0;
|
||||
}
|
||||
|
||||
if (sg_len <= 0) {
|
||||
if (sg_count <= 0) {
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"Failed to map scatterlist for DMA operation\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (next) {
|
||||
next->sg_len = sg_len;
|
||||
data->host_cookie = ++next->cookie < 0 ? 1 : next->cookie;
|
||||
} else
|
||||
host->sg_len = sg_len;
|
||||
data->sg_count = sg_count;
|
||||
data->host_cookie = cookie;
|
||||
|
||||
return 0;
|
||||
return data->sg_count;
|
||||
}
|
||||
|
||||
static int jz4740_mmc_start_dma_transfer(struct jz4740_mmc_host *host,
|
||||
struct mmc_data *data)
|
||||
{
|
||||
int ret;
|
||||
struct dma_chan *chan;
|
||||
struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data);
|
||||
struct dma_async_tx_descriptor *desc;
|
||||
struct dma_slave_config conf = {
|
||||
.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
|
||||
|
|
@ -306,29 +300,26 @@ static int jz4740_mmc_start_dma_transfer(struct jz4740_mmc_host *host,
|
|||
.src_maxburst = JZ4740_MMC_FIFO_HALF_SIZE,
|
||||
.dst_maxburst = JZ4740_MMC_FIFO_HALF_SIZE,
|
||||
};
|
||||
int sg_count;
|
||||
|
||||
if (data->flags & MMC_DATA_WRITE) {
|
||||
conf.direction = DMA_MEM_TO_DEV;
|
||||
conf.dst_addr = host->mem_res->start + JZ_REG_MMC_TXFIFO;
|
||||
conf.slave_id = JZ4740_DMA_TYPE_MMC_TRANSMIT;
|
||||
chan = host->dma_tx;
|
||||
} else {
|
||||
conf.direction = DMA_DEV_TO_MEM;
|
||||
conf.src_addr = host->mem_res->start + JZ_REG_MMC_RXFIFO;
|
||||
conf.slave_id = JZ4740_DMA_TYPE_MMC_RECEIVE;
|
||||
chan = host->dma_rx;
|
||||
}
|
||||
|
||||
ret = jz4740_mmc_prepare_dma_data(host, data, NULL, chan);
|
||||
if (ret)
|
||||
return ret;
|
||||
sg_count = jz4740_mmc_prepare_dma_data(host, data, COOKIE_MAPPED);
|
||||
if (sg_count < 0)
|
||||
return sg_count;
|
||||
|
||||
dmaengine_slave_config(chan, &conf);
|
||||
desc = dmaengine_prep_slave_sg(chan,
|
||||
data->sg,
|
||||
host->sg_len,
|
||||
conf.direction,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
desc = dmaengine_prep_slave_sg(chan, data->sg, sg_count,
|
||||
conf.direction,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
if (!desc) {
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"Failed to allocate DMA %s descriptor",
|
||||
|
|
@ -342,7 +333,8 @@ static int jz4740_mmc_start_dma_transfer(struct jz4740_mmc_host *host,
|
|||
return 0;
|
||||
|
||||
dma_unmap:
|
||||
jz4740_mmc_dma_unmap(host, data);
|
||||
if (data->host_cookie == COOKIE_MAPPED)
|
||||
jz4740_mmc_dma_unmap(host, data);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
|
@ -351,16 +343,13 @@ static void jz4740_mmc_pre_request(struct mmc_host *mmc,
|
|||
{
|
||||
struct jz4740_mmc_host *host = mmc_priv(mmc);
|
||||
struct mmc_data *data = mrq->data;
|
||||
struct jz4740_mmc_host_next *next_data = &host->next_data;
|
||||
|
||||
BUG_ON(data->host_cookie);
|
||||
if (!host->use_dma)
|
||||
return;
|
||||
|
||||
if (host->use_dma) {
|
||||
struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data);
|
||||
|
||||
if (jz4740_mmc_prepare_dma_data(host, data, next_data, chan))
|
||||
data->host_cookie = 0;
|
||||
}
|
||||
data->host_cookie = COOKIE_UNMAPPED;
|
||||
if (jz4740_mmc_prepare_dma_data(host, data, COOKIE_PREMAPPED) < 0)
|
||||
data->host_cookie = COOKIE_UNMAPPED;
|
||||
}
|
||||
|
||||
static void jz4740_mmc_post_request(struct mmc_host *mmc,
|
||||
|
|
@ -370,10 +359,8 @@ static void jz4740_mmc_post_request(struct mmc_host *mmc,
|
|||
struct jz4740_mmc_host *host = mmc_priv(mmc);
|
||||
struct mmc_data *data = mrq->data;
|
||||
|
||||
if (host->use_dma && data->host_cookie) {
|
||||
if (data && data->host_cookie != COOKIE_UNMAPPED)
|
||||
jz4740_mmc_dma_unmap(host, data);
|
||||
data->host_cookie = 0;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data);
|
||||
|
|
@ -436,10 +423,14 @@ static void jz4740_mmc_reset(struct jz4740_mmc_host *host)
|
|||
static void jz4740_mmc_request_done(struct jz4740_mmc_host *host)
|
||||
{
|
||||
struct mmc_request *req;
|
||||
struct mmc_data *data;
|
||||
|
||||
req = host->req;
|
||||
data = req->data;
|
||||
host->req = NULL;
|
||||
|
||||
if (data && data->host_cookie == COOKIE_MAPPED)
|
||||
jz4740_mmc_dma_unmap(host, data);
|
||||
mmc_request_done(host->mmc, req);
|
||||
}
|
||||
|
||||
|
|
@ -903,18 +894,16 @@ static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
switch (ios->power_mode) {
|
||||
case MMC_POWER_UP:
|
||||
jz4740_mmc_reset(host);
|
||||
if (host->pdata && gpio_is_valid(host->pdata->gpio_power))
|
||||
gpio_set_value(host->pdata->gpio_power,
|
||||
!host->pdata->power_active_low);
|
||||
if (host->power)
|
||||
gpiod_set_value(host->power, 1);
|
||||
host->cmdat |= JZ_MMC_CMDAT_INIT;
|
||||
clk_prepare_enable(host->clk);
|
||||
break;
|
||||
case MMC_POWER_ON:
|
||||
break;
|
||||
default:
|
||||
if (host->pdata && gpio_is_valid(host->pdata->gpio_power))
|
||||
gpio_set_value(host->pdata->gpio_power,
|
||||
host->pdata->power_active_low);
|
||||
if (host->power)
|
||||
gpiod_set_value(host->power, 0);
|
||||
clk_disable_unprepare(host->clk);
|
||||
break;
|
||||
}
|
||||
|
|
@ -947,30 +936,9 @@ static const struct mmc_host_ops jz4740_mmc_ops = {
|
|||
.enable_sdio_irq = jz4740_mmc_enable_sdio_irq,
|
||||
};
|
||||
|
||||
static int jz4740_mmc_request_gpio(struct device *dev, int gpio,
|
||||
const char *name, bool output, int value)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!gpio_is_valid(gpio))
|
||||
return 0;
|
||||
|
||||
ret = gpio_request(gpio, name);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to request %s gpio: %d\n", name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (output)
|
||||
gpio_direction_output(gpio, value);
|
||||
else
|
||||
gpio_direction_input(gpio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jz4740_mmc_request_gpios(struct mmc_host *mmc,
|
||||
struct platform_device *pdev)
|
||||
static int jz4740_mmc_request_gpios(struct jz4740_mmc_host *host,
|
||||
struct mmc_host *mmc,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
struct jz4740_mmc_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
int ret = 0;
|
||||
|
|
@ -983,31 +951,21 @@ static int jz4740_mmc_request_gpios(struct mmc_host *mmc,
|
|||
if (!pdata->read_only_active_low)
|
||||
mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
|
||||
|
||||
if (gpio_is_valid(pdata->gpio_card_detect)) {
|
||||
ret = mmc_gpio_request_cd(mmc, pdata->gpio_card_detect, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
/*
|
||||
* Get optional card detect and write protect GPIOs,
|
||||
* only back out on probe deferral.
|
||||
*/
|
||||
ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0, NULL);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
return ret;
|
||||
|
||||
if (gpio_is_valid(pdata->gpio_read_only)) {
|
||||
ret = mmc_gpio_request_ro(mmc, pdata->gpio_read_only);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
ret = mmc_gpiod_request_ro(mmc, "wp", 0, false, 0, NULL);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
return ret;
|
||||
|
||||
return jz4740_mmc_request_gpio(&pdev->dev, pdata->gpio_power,
|
||||
"MMC read only", true, pdata->power_active_low);
|
||||
}
|
||||
|
||||
static void jz4740_mmc_free_gpios(struct platform_device *pdev)
|
||||
{
|
||||
struct jz4740_mmc_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
|
||||
if (!pdata)
|
||||
return;
|
||||
|
||||
if (gpio_is_valid(pdata->gpio_power))
|
||||
gpio_free(pdata->gpio_power);
|
||||
host->power = devm_gpiod_get_optional(&pdev->dev, "power",
|
||||
GPIOD_OUT_HIGH);
|
||||
return PTR_ERR_OR_ZERO(host->power);
|
||||
}
|
||||
|
||||
static const struct of_device_id jz4740_mmc_of_match[] = {
|
||||
|
|
@ -1053,7 +1011,7 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
|
|||
mmc->caps |= MMC_CAP_SDIO_IRQ;
|
||||
if (!(pdata && pdata->data_1bit))
|
||||
mmc->caps |= MMC_CAP_4_BIT_DATA;
|
||||
ret = jz4740_mmc_request_gpios(mmc, pdev);
|
||||
ret = jz4740_mmc_request_gpios(host, mmc, pdev);
|
||||
if (ret)
|
||||
goto err_free_host;
|
||||
}
|
||||
|
|
@ -1104,7 +1062,7 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
|
|||
dev_name(&pdev->dev), host);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to request irq: %d\n", ret);
|
||||
goto err_free_gpios;
|
||||
goto err_free_host;
|
||||
}
|
||||
|
||||
jz4740_mmc_clock_disable(host);
|
||||
|
|
@ -1135,8 +1093,6 @@ err_release_dma:
|
|||
jz4740_mmc_release_dma_channels(host);
|
||||
err_free_irq:
|
||||
free_irq(host->irq, host);
|
||||
err_free_gpios:
|
||||
jz4740_mmc_free_gpios(pdev);
|
||||
err_free_host:
|
||||
mmc_free_host(mmc);
|
||||
|
||||
|
|
@ -1155,8 +1111,6 @@ static int jz4740_mmc_remove(struct platform_device *pdev)
|
|||
|
||||
free_irq(host->irq, host);
|
||||
|
||||
jz4740_mmc_free_gpios(pdev);
|
||||
|
||||
if (host->use_dma)
|
||||
jz4740_mmc_release_dma_channels(host);
|
||||
|
||||
|
|
|
|||
|
|
@ -21,11 +21,11 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
|
|
@ -66,6 +66,9 @@
|
|||
|
||||
#define SD_EMMC_DELAY 0x4
|
||||
#define SD_EMMC_ADJUST 0x8
|
||||
#define ADJUST_ADJ_DELAY_MASK GENMASK(21, 16)
|
||||
#define ADJUST_DS_EN BIT(15)
|
||||
#define ADJUST_ADJ_EN BIT(13)
|
||||
|
||||
#define SD_EMMC_DELAY1 0x4
|
||||
#define SD_EMMC_DELAY2 0x8
|
||||
|
|
@ -90,9 +93,11 @@
|
|||
#define CFG_CLK_ALWAYS_ON BIT(18)
|
||||
#define CFG_CHK_DS BIT(20)
|
||||
#define CFG_AUTO_CLK BIT(23)
|
||||
#define CFG_ERR_ABORT BIT(27)
|
||||
|
||||
#define SD_EMMC_STATUS 0x48
|
||||
#define STATUS_BUSY BIT(31)
|
||||
#define STATUS_DESC_BUSY BIT(30)
|
||||
#define STATUS_DATI GENMASK(23, 16)
|
||||
|
||||
#define SD_EMMC_IRQ_EN 0x4c
|
||||
|
|
@ -141,6 +146,7 @@ struct meson_mmc_data {
|
|||
unsigned int tx_delay_mask;
|
||||
unsigned int rx_delay_mask;
|
||||
unsigned int always_on;
|
||||
unsigned int adjust;
|
||||
};
|
||||
|
||||
struct sd_emmc_desc {
|
||||
|
|
@ -156,7 +162,6 @@ struct meson_host {
|
|||
struct mmc_host *mmc;
|
||||
struct mmc_command *cmd;
|
||||
|
||||
spinlock_t lock;
|
||||
void __iomem *regs;
|
||||
struct clk *core_clk;
|
||||
struct clk *mmc_clk;
|
||||
|
|
@ -633,14 +638,8 @@ static int meson_mmc_clk_init(struct meson_host *host)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Set phases : These values are mostly the datasheet recommended ones
|
||||
* except for the Tx phase. Datasheet recommends 180 but some cards
|
||||
* fail at initialisation with it. 270 works just fine, it fixes these
|
||||
* initialisation issues and enable eMMC DDR52 mode.
|
||||
*/
|
||||
clk_set_phase(host->mmc_clk, 180);
|
||||
clk_set_phase(host->tx_clk, 270);
|
||||
clk_set_phase(host->tx_clk, 0);
|
||||
clk_set_phase(host->rx_clk, 0);
|
||||
|
||||
return clk_prepare_enable(host->mmc_clk);
|
||||
|
|
@ -928,6 +927,7 @@ static void meson_mmc_start_cmd(struct mmc_host *mmc, struct mmc_command *cmd)
|
|||
|
||||
cmd_cfg |= FIELD_PREP(CMD_CFG_CMD_INDEX_MASK, cmd->opcode);
|
||||
cmd_cfg |= CMD_CFG_OWNER; /* owned by CPU */
|
||||
cmd_cfg |= CMD_CFG_ERROR; /* stop in case of error */
|
||||
|
||||
meson_mmc_set_response_bits(cmd, &cmd_cfg);
|
||||
|
||||
|
|
@ -1022,29 +1022,34 @@ static irqreturn_t meson_mmc_irq(int irq, void *dev_id)
|
|||
u32 irq_en, status, raw_status;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
|
||||
if (WARN_ON(!host) || WARN_ON(!host->cmd))
|
||||
return IRQ_NONE;
|
||||
|
||||
spin_lock(&host->lock);
|
||||
|
||||
cmd = host->cmd;
|
||||
data = cmd->data;
|
||||
irq_en = readl(host->regs + SD_EMMC_IRQ_EN);
|
||||
raw_status = readl(host->regs + SD_EMMC_STATUS);
|
||||
status = raw_status & irq_en;
|
||||
|
||||
if (!status) {
|
||||
dev_dbg(host->dev,
|
||||
"Unexpected IRQ! irq_en 0x%08x - status 0x%08x\n",
|
||||
irq_en, raw_status);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
if (WARN_ON(!host) || WARN_ON(!host->cmd))
|
||||
return IRQ_NONE;
|
||||
|
||||
cmd = host->cmd;
|
||||
data = cmd->data;
|
||||
cmd->error = 0;
|
||||
if (status & IRQ_CRC_ERR) {
|
||||
dev_dbg(host->dev, "CRC Error - status 0x%08x\n", status);
|
||||
cmd->error = -EILSEQ;
|
||||
ret = IRQ_HANDLED;
|
||||
ret = IRQ_WAKE_THREAD;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (status & IRQ_TIMEOUTS) {
|
||||
dev_dbg(host->dev, "Timeout - status 0x%08x\n", status);
|
||||
cmd->error = -ETIMEDOUT;
|
||||
ret = IRQ_HANDLED;
|
||||
ret = IRQ_WAKE_THREAD;
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
|
@ -1069,17 +1074,48 @@ out:
|
|||
/* ack all enabled interrupts */
|
||||
writel(irq_en, host->regs + SD_EMMC_STATUS);
|
||||
|
||||
if (cmd->error) {
|
||||
/* Stop desc in case of errors */
|
||||
u32 start = readl(host->regs + SD_EMMC_START);
|
||||
|
||||
start &= ~START_DESC_BUSY;
|
||||
writel(start, host->regs + SD_EMMC_START);
|
||||
}
|
||||
|
||||
if (ret == IRQ_HANDLED)
|
||||
meson_mmc_request_done(host->mmc, cmd->mrq);
|
||||
else if (ret == IRQ_NONE)
|
||||
dev_warn(host->dev,
|
||||
"Unexpected IRQ! status=0x%08x, irq_en=0x%08x\n",
|
||||
raw_status, irq_en);
|
||||
|
||||
spin_unlock(&host->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int meson_mmc_wait_desc_stop(struct meson_host *host)
|
||||
{
|
||||
int loop;
|
||||
u32 status;
|
||||
|
||||
/*
|
||||
* It may sometimes take a while for it to actually halt. Here, we
|
||||
* are giving it 5ms to comply
|
||||
*
|
||||
* If we don't confirm the descriptor is stopped, it might raise new
|
||||
* IRQs after we have called mmc_request_done() which is bad.
|
||||
*/
|
||||
for (loop = 50; loop; loop--) {
|
||||
status = readl(host->regs + SD_EMMC_STATUS);
|
||||
if (status & (STATUS_BUSY | STATUS_DESC_BUSY))
|
||||
udelay(100);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (status & (STATUS_BUSY | STATUS_DESC_BUSY)) {
|
||||
dev_err(host->dev, "Timed out waiting for host to stop\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t meson_mmc_irq_thread(int irq, void *dev_id)
|
||||
{
|
||||
struct meson_host *host = dev_id;
|
||||
|
|
@ -1090,6 +1126,13 @@ static irqreturn_t meson_mmc_irq_thread(int irq, void *dev_id)
|
|||
if (WARN_ON(!cmd))
|
||||
return IRQ_NONE;
|
||||
|
||||
if (cmd->error) {
|
||||
meson_mmc_wait_desc_stop(host);
|
||||
meson_mmc_request_done(host->mmc, cmd->mrq);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
data = cmd->data;
|
||||
if (meson_mmc_bounce_buf_read(data)) {
|
||||
xfer_bytes = data->blksz * data->blocks;
|
||||
|
|
@ -1123,14 +1166,21 @@ static int meson_mmc_get_cd(struct mmc_host *mmc)
|
|||
|
||||
static void meson_mmc_cfg_init(struct meson_host *host)
|
||||
{
|
||||
u32 cfg = 0;
|
||||
u32 cfg = 0, adj = 0;
|
||||
|
||||
cfg |= FIELD_PREP(CFG_RESP_TIMEOUT_MASK,
|
||||
ilog2(SD_EMMC_CFG_RESP_TIMEOUT));
|
||||
cfg |= FIELD_PREP(CFG_RC_CC_MASK, ilog2(SD_EMMC_CFG_CMD_GAP));
|
||||
cfg |= FIELD_PREP(CFG_BLK_LEN_MASK, ilog2(SD_EMMC_CFG_BLK_SIZE));
|
||||
|
||||
/* abort chain on R/W errors */
|
||||
cfg |= CFG_ERR_ABORT;
|
||||
|
||||
writel(cfg, host->regs + SD_EMMC_CFG);
|
||||
|
||||
/* enable signal resampling w/o delay */
|
||||
adj = ADJUST_ADJ_EN;
|
||||
writel(adj, host->regs + host->data->adjust);
|
||||
}
|
||||
|
||||
static int meson_mmc_card_busy(struct mmc_host *mmc)
|
||||
|
|
@ -1191,8 +1241,6 @@ static int meson_mmc_probe(struct platform_device *pdev)
|
|||
host->dev = &pdev->dev;
|
||||
dev_set_drvdata(&pdev->dev, host);
|
||||
|
||||
spin_lock_init(&host->lock);
|
||||
|
||||
/* Get regulators and the supported OCR mask */
|
||||
host->vqmmc_enabled = false;
|
||||
ret = mmc_regulator_get_supply(mmc);
|
||||
|
|
@ -1356,12 +1404,14 @@ static const struct meson_mmc_data meson_gx_data = {
|
|||
.tx_delay_mask = CLK_V2_TX_DELAY_MASK,
|
||||
.rx_delay_mask = CLK_V2_RX_DELAY_MASK,
|
||||
.always_on = CLK_V2_ALWAYS_ON,
|
||||
.adjust = SD_EMMC_ADJUST,
|
||||
};
|
||||
|
||||
static const struct meson_mmc_data meson_axg_data = {
|
||||
.tx_delay_mask = CLK_V3_TX_DELAY_MASK,
|
||||
.rx_delay_mask = CLK_V3_RX_DELAY_MASK,
|
||||
.always_on = CLK_V3_ALWAYS_ON,
|
||||
.adjust = SD_EMMC_V3_ADJUST,
|
||||
};
|
||||
|
||||
static const struct of_device_id meson_mmc_of_match[] = {
|
||||
|
|
|
|||
|
|
@ -596,6 +596,9 @@ static int meson_mx_mmc_register_clks(struct meson_mx_mmc_host *host)
|
|||
init.name = devm_kasprintf(host->controller_dev, GFP_KERNEL,
|
||||
"%s#fixed_factor",
|
||||
dev_name(host->controller_dev));
|
||||
if (!init.name)
|
||||
return -ENOMEM;
|
||||
|
||||
init.ops = &clk_fixed_factor_ops;
|
||||
init.flags = 0;
|
||||
init.parent_names = &clk_fixed_factor_parent;
|
||||
|
|
@ -612,6 +615,9 @@ static int meson_mx_mmc_register_clks(struct meson_mx_mmc_host *host)
|
|||
clk_div_parent = __clk_get_name(host->fixed_factor_clk);
|
||||
init.name = devm_kasprintf(host->controller_dev, GFP_KERNEL,
|
||||
"%s#div", dev_name(host->controller_dev));
|
||||
if (!init.name)
|
||||
return -ENOMEM;
|
||||
|
||||
init.ops = &clk_divider_ops;
|
||||
init.flags = CLK_SET_RATE_PARENT;
|
||||
init.parent_names = &clk_div_parent;
|
||||
|
|
|
|||
|
|
@ -1434,13 +1434,16 @@ static int mmc_spi_probe(struct spi_device *spi)
|
|||
if (status != 0)
|
||||
goto fail_add_host;
|
||||
|
||||
if (host->pdata && host->pdata->flags & MMC_SPI_USE_CD_GPIO) {
|
||||
status = mmc_gpio_request_cd(mmc, host->pdata->cd_gpio,
|
||||
host->pdata->cd_debounce);
|
||||
if (status != 0)
|
||||
goto fail_add_host;
|
||||
|
||||
/* The platform has a CD GPIO signal that may support
|
||||
/*
|
||||
* Index 0 is card detect
|
||||
* Old boardfiles were specifying 1 ms as debounce
|
||||
*/
|
||||
status = mmc_gpiod_request_cd(mmc, NULL, 0, false, 1, NULL);
|
||||
if (status == -EPROBE_DEFER)
|
||||
goto fail_add_host;
|
||||
if (!status) {
|
||||
/*
|
||||
* The platform has a CD GPIO signal that may support
|
||||
* interrupts, so let mmc_gpiod_request_cd_irq() decide
|
||||
* if polling is needed or not.
|
||||
*/
|
||||
|
|
@ -1448,12 +1451,12 @@ static int mmc_spi_probe(struct spi_device *spi)
|
|||
mmc_gpiod_request_cd_irq(mmc);
|
||||
}
|
||||
|
||||
if (host->pdata && host->pdata->flags & MMC_SPI_USE_RO_GPIO) {
|
||||
/* Index 1 is write protect/read only */
|
||||
status = mmc_gpiod_request_ro(mmc, NULL, 1, false, 0, NULL);
|
||||
if (status == -EPROBE_DEFER)
|
||||
goto fail_add_host;
|
||||
if (!status)
|
||||
has_ro = true;
|
||||
status = mmc_gpio_request_ro(mmc, host->pdata->ro_gpio);
|
||||
if (status != 0)
|
||||
goto fail_add_host;
|
||||
}
|
||||
|
||||
dev_info(&spi->dev, "SD/MMC host %s%s%s%s%s\n",
|
||||
dev_name(&mmc->class_dev),
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/mmc/pm.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/card.h>
|
||||
|
|
@ -274,6 +275,7 @@ static struct variant_data variant_stm32_sdmmc = {
|
|||
.cmdreg_lrsp_crc = MCI_CPSM_STM32_LRSP_CRC,
|
||||
.cmdreg_srsp_crc = MCI_CPSM_STM32_SRSP_CRC,
|
||||
.cmdreg_srsp = MCI_CPSM_STM32_SRSP,
|
||||
.cmdreg_stop = MCI_CPSM_STM32_CMDSTOP,
|
||||
.data_cmd_enable = MCI_CPSM_STM32_CMDTRANS,
|
||||
.irq_pio_mask = MCI_IRQ_PIO_STM32_MASK,
|
||||
.datactrl_first = true,
|
||||
|
|
@ -1100,6 +1102,10 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c)
|
|||
mmci_reg_delay(host);
|
||||
}
|
||||
|
||||
if (host->variant->cmdreg_stop &&
|
||||
cmd->opcode == MMC_STOP_TRANSMISSION)
|
||||
c |= host->variant->cmdreg_stop;
|
||||
|
||||
c |= cmd->opcode | host->variant->cmdreg_cpsm_enable;
|
||||
if (cmd->flags & MMC_RSP_PRESENT) {
|
||||
if (cmd->flags & MMC_RSP_136)
|
||||
|
|
@ -1190,11 +1196,10 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
|
|||
/* The error clause is handled above, success! */
|
||||
data->bytes_xfered = data->blksz * data->blocks;
|
||||
|
||||
if (!data->stop || host->mrq->sbc) {
|
||||
if (!data->stop || (host->mrq->sbc && !data->error))
|
||||
mmci_request_end(host, data->mrq);
|
||||
} else {
|
||||
else
|
||||
mmci_start_command(host, data->stop, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -264,6 +264,7 @@ struct mmci_host;
|
|||
* @cmdreg_lrsp_crc: enable value for long response with crc
|
||||
* @cmdreg_srsp_crc: enable value for short response with crc
|
||||
* @cmdreg_srsp: enable value for short response without crc
|
||||
* @cmdreg_stop: enable value for stop and abort transmission
|
||||
* @datalength_bits: number of bits in the MMCIDATALENGTH register
|
||||
* @fifosize: number of bytes that can be written when MMCI_TXFIFOEMPTY
|
||||
* is asserted (likewise for RX)
|
||||
|
|
@ -316,6 +317,7 @@ struct variant_data {
|
|||
unsigned int cmdreg_lrsp_crc;
|
||||
unsigned int cmdreg_srsp_crc;
|
||||
unsigned int cmdreg_srsp;
|
||||
unsigned int cmdreg_stop;
|
||||
unsigned int datalength_bits;
|
||||
unsigned int fifosize;
|
||||
unsigned int fifohalfsize;
|
||||
|
|
|
|||
|
|
@ -1114,6 +1114,7 @@ static void msdc_start_command(struct msdc_host *host,
|
|||
struct mmc_request *mrq, struct mmc_command *cmd)
|
||||
{
|
||||
u32 rawcmd;
|
||||
unsigned long flags;
|
||||
|
||||
WARN_ON(host->cmd);
|
||||
host->cmd = cmd;
|
||||
|
|
@ -1131,7 +1132,10 @@ static void msdc_start_command(struct msdc_host *host,
|
|||
cmd->error = 0;
|
||||
rawcmd = msdc_cmd_prepare_raw_cmd(host, mrq, cmd);
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
sdr_set_bits(host->base + MSDC_INTEN, cmd_ints_mask);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
writel(cmd->arg, host->base + SDC_ARG);
|
||||
writel(rawcmd, host->base + SDC_CMD);
|
||||
}
|
||||
|
|
@ -1351,6 +1355,31 @@ static void msdc_request_timeout(struct work_struct *work)
|
|||
}
|
||||
}
|
||||
|
||||
static void __msdc_enable_sdio_irq(struct mmc_host *mmc, int enb)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct msdc_host *host = mmc_priv(mmc);
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
if (enb)
|
||||
sdr_set_bits(host->base + MSDC_INTEN, MSDC_INTEN_SDIOIRQ);
|
||||
else
|
||||
sdr_clr_bits(host->base + MSDC_INTEN, MSDC_INTEN_SDIOIRQ);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
}
|
||||
|
||||
static void msdc_enable_sdio_irq(struct mmc_host *mmc, int enb)
|
||||
{
|
||||
struct msdc_host *host = mmc_priv(mmc);
|
||||
|
||||
__msdc_enable_sdio_irq(mmc, enb);
|
||||
|
||||
if (enb)
|
||||
pm_runtime_get_noresume(host->dev);
|
||||
else
|
||||
pm_runtime_put_noidle(host->dev);
|
||||
}
|
||||
|
||||
static irqreturn_t msdc_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct msdc_host *host = (struct msdc_host *) dev_id;
|
||||
|
|
@ -1373,7 +1402,12 @@ static irqreturn_t msdc_irq(int irq, void *dev_id)
|
|||
data = host->data;
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
if (!(events & event_mask))
|
||||
if ((events & event_mask) & MSDC_INT_SDIOIRQ) {
|
||||
__msdc_enable_sdio_irq(host->mmc, 0);
|
||||
sdio_signal_irq(host->mmc);
|
||||
}
|
||||
|
||||
if (!(events & (event_mask & ~MSDC_INT_SDIOIRQ)))
|
||||
break;
|
||||
|
||||
if (!mrq) {
|
||||
|
|
@ -1493,8 +1527,11 @@ static void msdc_init_hw(struct msdc_host *host)
|
|||
*/
|
||||
sdr_set_bits(host->base + SDC_CFG, SDC_CFG_SDIO);
|
||||
|
||||
/* disable detect SDIO device interrupt function */
|
||||
sdr_clr_bits(host->base + SDC_CFG, SDC_CFG_SDIOIDE);
|
||||
/* Config SDIO device detect interrupt function */
|
||||
if (host->mmc->caps & MMC_CAP_SDIO_IRQ)
|
||||
sdr_set_bits(host->base + SDC_CFG, SDC_CFG_SDIOIDE);
|
||||
else
|
||||
sdr_clr_bits(host->base + SDC_CFG, SDC_CFG_SDIOIDE);
|
||||
|
||||
/* Configure to default data timeout */
|
||||
sdr_set_field(host->base + SDC_CFG, SDC_CFG_DTOC, 3);
|
||||
|
|
@ -2013,6 +2050,11 @@ static void msdc_hw_reset(struct mmc_host *mmc)
|
|||
sdr_clr_bits(host->base + EMMC_IOCON, 1);
|
||||
}
|
||||
|
||||
static void msdc_ack_sdio_irq(struct mmc_host *mmc)
|
||||
{
|
||||
__msdc_enable_sdio_irq(mmc, 1);
|
||||
}
|
||||
|
||||
static const struct mmc_host_ops mt_msdc_ops = {
|
||||
.post_req = msdc_post_req,
|
||||
.pre_req = msdc_pre_req,
|
||||
|
|
@ -2020,6 +2062,8 @@ static const struct mmc_host_ops mt_msdc_ops = {
|
|||
.set_ios = msdc_ops_set_ios,
|
||||
.get_ro = mmc_gpio_get_ro,
|
||||
.get_cd = mmc_gpio_get_cd,
|
||||
.enable_sdio_irq = msdc_enable_sdio_irq,
|
||||
.ack_sdio_irq = msdc_ack_sdio_irq,
|
||||
.start_signal_voltage_switch = msdc_ops_switch_volt,
|
||||
.card_busy = msdc_card_busy,
|
||||
.execute_tuning = msdc_execute_tuning,
|
||||
|
|
@ -2147,6 +2191,9 @@ static int msdc_drv_probe(struct platform_device *pdev)
|
|||
else
|
||||
mmc->f_min = DIV_ROUND_UP(host->src_clk_freq, 4 * 4095);
|
||||
|
||||
if (mmc->caps & MMC_CAP_SDIO_IRQ)
|
||||
mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
|
||||
|
||||
mmc->caps |= MMC_CAP_ERASE | MMC_CAP_CMD23;
|
||||
/* MMC core transfer sizes tunable parameters */
|
||||
mmc->max_segs = MAX_BD_NUM;
|
||||
|
|
|
|||
|
|
@ -16,9 +16,7 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/mmc_spi.h>
|
||||
|
|
@ -32,15 +30,7 @@
|
|||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
enum {
|
||||
CD_GPIO = 0,
|
||||
WP_GPIO,
|
||||
NUM_GPIOS,
|
||||
};
|
||||
|
||||
struct of_mmc_spi {
|
||||
int gpios[NUM_GPIOS];
|
||||
bool alow_gpios[NUM_GPIOS];
|
||||
int detect_irq;
|
||||
struct mmc_spi_platform_data pdata;
|
||||
};
|
||||
|
|
@ -102,30 +92,6 @@ struct mmc_spi_platform_data *mmc_spi_get_pdata(struct spi_device *spi)
|
|||
oms->pdata.ocr_mask |= mask;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(oms->gpios); i++) {
|
||||
enum of_gpio_flags gpio_flags;
|
||||
|
||||
oms->gpios[i] = of_get_gpio_flags(np, i, &gpio_flags);
|
||||
if (!gpio_is_valid(oms->gpios[i]))
|
||||
continue;
|
||||
|
||||
if (gpio_flags & OF_GPIO_ACTIVE_LOW)
|
||||
oms->alow_gpios[i] = true;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(oms->gpios[CD_GPIO])) {
|
||||
oms->pdata.cd_gpio = oms->gpios[CD_GPIO];
|
||||
oms->pdata.flags |= MMC_SPI_USE_CD_GPIO;
|
||||
if (!oms->alow_gpios[CD_GPIO])
|
||||
oms->pdata.caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;
|
||||
}
|
||||
if (gpio_is_valid(oms->gpios[WP_GPIO])) {
|
||||
oms->pdata.ro_gpio = oms->gpios[WP_GPIO];
|
||||
oms->pdata.flags |= MMC_SPI_USE_RO_GPIO;
|
||||
if (!oms->alow_gpios[WP_GPIO])
|
||||
oms->pdata.caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
|
||||
}
|
||||
|
||||
oms->detect_irq = irq_of_parse_and_map(np, 0);
|
||||
if (oms->detect_irq != 0) {
|
||||
oms->pdata.init = of_mmc_spi_init;
|
||||
|
|
|
|||
|
|
@ -1652,7 +1652,7 @@ static struct mmc_host_ops omap_hsmmc_ops = {
|
|||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
||||
static int omap_hsmmc_regs_show(struct seq_file *s, void *data)
|
||||
static int mmc_regs_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct mmc_host *mmc = s->private;
|
||||
struct omap_hsmmc_host *host = mmc_priv(mmc);
|
||||
|
|
@ -1691,17 +1691,7 @@ static int omap_hsmmc_regs_show(struct seq_file *s, void *data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int omap_hsmmc_regs_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, omap_hsmmc_regs_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations mmc_regs_fops = {
|
||||
.open = omap_hsmmc_regs_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
DEFINE_SHOW_ATTRIBUTE(mmc_regs);
|
||||
|
||||
static void omap_hsmmc_debugfs(struct mmc_host *mmc)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -30,10 +30,9 @@
|
|||
#include <linux/mmc/slot-gpio.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include <asm/sizes.h>
|
||||
|
|
@ -63,6 +62,8 @@ struct pxamci_host {
|
|||
unsigned int imask;
|
||||
unsigned int power_mode;
|
||||
unsigned long detect_delay_ms;
|
||||
bool use_ro_gpio;
|
||||
struct gpio_desc *power;
|
||||
struct pxamci_platform_data *pdata;
|
||||
|
||||
struct mmc_request *mrq;
|
||||
|
|
@ -101,16 +102,13 @@ static inline int pxamci_set_power(struct pxamci_host *host,
|
|||
{
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
struct regulator *supply = mmc->supply.vmmc;
|
||||
int on;
|
||||
|
||||
if (!IS_ERR(supply))
|
||||
return mmc_regulator_set_ocr(mmc, supply, vdd);
|
||||
|
||||
if (host->pdata &&
|
||||
gpio_is_valid(host->pdata->gpio_power)) {
|
||||
on = ((1 << vdd) & host->pdata->ocr_mask);
|
||||
gpio_set_value(host->pdata->gpio_power,
|
||||
!!on ^ host->pdata->gpio_power_invert);
|
||||
if (host->power) {
|
||||
bool on = !!((1 << vdd) & host->pdata->ocr_mask);
|
||||
gpiod_set_value(host->power, on);
|
||||
}
|
||||
|
||||
if (host->pdata && host->pdata->setpower)
|
||||
|
|
@ -432,7 +430,7 @@ static int pxamci_get_ro(struct mmc_host *mmc)
|
|||
{
|
||||
struct pxamci_host *host = mmc_priv(mmc);
|
||||
|
||||
if (host->pdata && gpio_is_valid(host->pdata->gpio_card_ro))
|
||||
if (host->use_ro_gpio)
|
||||
return mmc_gpio_get_ro(mmc);
|
||||
if (host->pdata && host->pdata->get_ro)
|
||||
return !!host->pdata->get_ro(mmc_dev(mmc));
|
||||
|
|
@ -730,52 +728,38 @@ static int pxamci_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
if (host->pdata) {
|
||||
int gpio_cd = host->pdata->gpio_card_detect;
|
||||
int gpio_ro = host->pdata->gpio_card_ro;
|
||||
int gpio_power = host->pdata->gpio_power;
|
||||
|
||||
host->detect_delay_ms = host->pdata->detect_delay_ms;
|
||||
|
||||
if (gpio_is_valid(gpio_power)) {
|
||||
ret = devm_gpio_request(dev, gpio_power,
|
||||
"mmc card power");
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"Failed requesting gpio_power %d\n",
|
||||
gpio_power);
|
||||
goto out;
|
||||
}
|
||||
gpio_direction_output(gpio_power,
|
||||
host->pdata->gpio_power_invert);
|
||||
}
|
||||
|
||||
if (gpio_is_valid(gpio_ro)) {
|
||||
ret = mmc_gpio_request_ro(mmc, gpio_ro);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"Failed requesting gpio_ro %d\n",
|
||||
gpio_ro);
|
||||
goto out;
|
||||
} else {
|
||||
mmc->caps2 |= host->pdata->gpio_card_ro_invert ?
|
||||
0 : MMC_CAP2_RO_ACTIVE_HIGH;
|
||||
}
|
||||
}
|
||||
|
||||
if (gpio_is_valid(gpio_cd))
|
||||
ret = mmc_gpio_request_cd(mmc, gpio_cd, 0);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed requesting gpio_cd %d\n",
|
||||
gpio_cd);
|
||||
host->power = devm_gpiod_get_optional(dev, "power", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(host->power)) {
|
||||
dev_err(dev, "Failed requesting gpio_power\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* FIXME: should we pass detection delay to debounce? */
|
||||
ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0, NULL);
|
||||
if (ret && ret != -ENOENT) {
|
||||
dev_err(dev, "Failed requesting gpio_cd\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = mmc_gpiod_request_ro(mmc, "wp", 0, false, 0, NULL);
|
||||
if (ret && ret != -ENOENT) {
|
||||
dev_err(dev, "Failed requesting gpio_ro\n");
|
||||
goto out;
|
||||
}
|
||||
if (!ret) {
|
||||
host->use_ro_gpio = true;
|
||||
mmc->caps2 |= host->pdata->gpio_card_ro_invert ?
|
||||
0 : MMC_CAP2_RO_ACTIVE_HIGH;
|
||||
}
|
||||
|
||||
if (host->pdata->init)
|
||||
host->pdata->init(dev, pxamci_detect_irq, mmc);
|
||||
|
||||
if (gpio_is_valid(gpio_power) && host->pdata->setpower)
|
||||
if (host->power && host->pdata->setpower)
|
||||
dev_warn(dev, "gpio_power and setpower() both defined\n");
|
||||
if (gpio_is_valid(gpio_ro) && host->pdata->get_ro)
|
||||
if (host->use_ro_gpio && host->pdata->get_ro)
|
||||
dev_warn(dev, "gpio_ro and get_ro() both defined\n");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/pinctrl/pinctrl-state.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/sys_soc.h>
|
||||
|
||||
#include "renesas_sdhi.h"
|
||||
#include "tmio_mmc.h"
|
||||
|
|
@ -45,6 +46,11 @@
|
|||
#define SDHI_VER_GEN3_SD 0xcc10
|
||||
#define SDHI_VER_GEN3_SDMMC 0xcd10
|
||||
|
||||
struct renesas_sdhi_quirks {
|
||||
bool hs400_disabled;
|
||||
bool hs400_4taps;
|
||||
};
|
||||
|
||||
static void renesas_sdhi_sdbuf_width(struct tmio_mmc_host *host, int width)
|
||||
{
|
||||
u32 val;
|
||||
|
|
@ -163,15 +169,6 @@ static void renesas_sdhi_set_clock(struct tmio_mmc_host *host,
|
|||
if (new_clock == 0)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Both HS400 and HS200/SD104 set 200MHz, but some devices need to
|
||||
* set 400MHz to distinguish the CPG settings in HS400.
|
||||
*/
|
||||
if (host->mmc->ios.timing == MMC_TIMING_MMC_HS400 &&
|
||||
host->pdata->flags & TMIO_MMC_HAVE_4TAP_HS400 &&
|
||||
new_clock == 200000000)
|
||||
new_clock = 400000000;
|
||||
|
||||
clock = renesas_sdhi_clk_update(host, new_clock) / 512;
|
||||
|
||||
for (clk = 0x80000080; new_clock >= (clock << 1); clk >>= 1)
|
||||
|
|
@ -532,6 +529,10 @@ static void renesas_sdhi_hw_reset(struct tmio_mmc_host *host)
|
|||
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL,
|
||||
~SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN &
|
||||
sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL));
|
||||
|
||||
if (host->pdata->flags & TMIO_MMC_MIN_RCAR2)
|
||||
sd_ctrl_write32_as_16_and_16(host, CTL_IRQ_MASK,
|
||||
TMIO_MASK_INIT_RCAR2);
|
||||
}
|
||||
|
||||
static int renesas_sdhi_wait_idle(struct tmio_mmc_host *host, u32 bit)
|
||||
|
|
@ -602,11 +603,31 @@ static void renesas_sdhi_enable_dma(struct tmio_mmc_host *host, bool enable)
|
|||
renesas_sdhi_sdbuf_width(host, enable ? width : 16);
|
||||
}
|
||||
|
||||
static const struct renesas_sdhi_quirks sdhi_quirks_h3_m3w_es1 = {
|
||||
.hs400_disabled = true,
|
||||
.hs400_4taps = true,
|
||||
};
|
||||
|
||||
static const struct renesas_sdhi_quirks sdhi_quirks_h3_es2 = {
|
||||
.hs400_disabled = false,
|
||||
.hs400_4taps = true,
|
||||
};
|
||||
|
||||
static const struct soc_device_attribute sdhi_quirks_match[] = {
|
||||
{ .soc_id = "r8a7795", .revision = "ES1.*", .data = &sdhi_quirks_h3_m3w_es1 },
|
||||
{ .soc_id = "r8a7795", .revision = "ES2.0", .data = &sdhi_quirks_h3_es2 },
|
||||
{ .soc_id = "r8a7796", .revision = "ES1.0", .data = &sdhi_quirks_h3_m3w_es1 },
|
||||
{ .soc_id = "r8a7796", .revision = "ES1.1", .data = &sdhi_quirks_h3_m3w_es1 },
|
||||
{ /* Sentinel. */ },
|
||||
};
|
||||
|
||||
int renesas_sdhi_probe(struct platform_device *pdev,
|
||||
const struct tmio_mmc_dma_ops *dma_ops)
|
||||
{
|
||||
struct tmio_mmc_data *mmd = pdev->dev.platform_data;
|
||||
const struct renesas_sdhi_quirks *quirks = NULL;
|
||||
const struct renesas_sdhi_of_data *of_data;
|
||||
const struct soc_device_attribute *attr;
|
||||
struct tmio_mmc_data *mmc_data;
|
||||
struct tmio_mmc_dma *dma_priv;
|
||||
struct tmio_mmc_host *host;
|
||||
|
|
@ -616,6 +637,10 @@ int renesas_sdhi_probe(struct platform_device *pdev,
|
|||
|
||||
of_data = of_device_get_match_data(&pdev->dev);
|
||||
|
||||
attr = soc_device_match(sdhi_quirks_match);
|
||||
if (attr)
|
||||
quirks = attr->data;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -EINVAL;
|
||||
|
|
@ -681,6 +706,12 @@ int renesas_sdhi_probe(struct platform_device *pdev,
|
|||
host->multi_io_quirk = renesas_sdhi_multi_io_quirk;
|
||||
host->dma_ops = dma_ops;
|
||||
|
||||
if (quirks && quirks->hs400_disabled)
|
||||
host->mmc->caps2 &= ~(MMC_CAP2_HS400 | MMC_CAP2_HS400_ES);
|
||||
|
||||
if (quirks && quirks->hs400_4taps)
|
||||
mmc_data->flags |= TMIO_MMC_HAVE_4TAP_HS400;
|
||||
|
||||
/* For some SoC, we disable internal WP. GPIO may override this */
|
||||
if (mmc_can_gpio_ro(host->mmc))
|
||||
mmc_data->capabilities2 &= ~MMC_CAP2_NO_WRITE_PROTECT;
|
||||
|
|
@ -691,6 +722,7 @@ int renesas_sdhi_probe(struct platform_device *pdev,
|
|||
host->ops.card_busy = renesas_sdhi_card_busy;
|
||||
host->ops.start_signal_voltage_switch =
|
||||
renesas_sdhi_start_signal_voltage_switch;
|
||||
host->sdcard_irq_setbit_mask = TMIO_STAT_ALWAYS_SET_27;
|
||||
}
|
||||
|
||||
/* Orginally registers were 16 bit apart, could be 32 or 64 nowadays */
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
#define DTRAN_MODE_CH_NUM_CH0 0 /* "downstream" = for write commands */
|
||||
#define DTRAN_MODE_CH_NUM_CH1 BIT(16) /* "upstream" = for read commands */
|
||||
#define DTRAN_MODE_BUS_WIDTH (BIT(5) | BIT(4))
|
||||
#define DTRAN_MODE_ADDR_MODE BIT(0) /* 1 = Increment address */
|
||||
#define DTRAN_MODE_ADDR_MODE BIT(0) /* 1 = Increment address, 0 = Fixed */
|
||||
|
||||
/* DM_CM_DTRAN_CTRL */
|
||||
#define DTRAN_CTRL_DM_START BIT(0)
|
||||
|
|
@ -73,6 +73,9 @@ static unsigned long global_flags;
|
|||
#define SDHI_INTERNAL_DMAC_ONE_RX_ONLY 0
|
||||
#define SDHI_INTERNAL_DMAC_RX_IN_USE 1
|
||||
|
||||
/* RZ/A2 does not have the ADRR_MODE bit */
|
||||
#define SDHI_INTERNAL_DMAC_ADDR_MODE_FIXED_ONLY 2
|
||||
|
||||
/* Definitions for sampling clocks */
|
||||
static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = {
|
||||
{
|
||||
|
|
@ -81,15 +84,14 @@ static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static const struct renesas_sdhi_of_data of_rcar_r8a7795_compatible = {
|
||||
static const struct renesas_sdhi_of_data of_rza2_compatible = {
|
||||
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
|
||||
TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2 |
|
||||
TMIO_MMC_HAVE_4TAP_HS400,
|
||||
TMIO_MMC_HAVE_CBSY,
|
||||
.tmio_ocr_mask = MMC_VDD_32_33,
|
||||
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
|
||||
MMC_CAP_CMD23,
|
||||
.capabilities2 = MMC_CAP2_NO_WRITE_PROTECT,
|
||||
.bus_shift = 2,
|
||||
.scc_offset = 0x1000,
|
||||
.scc_offset = 0 - 0x1000,
|
||||
.taps = rcar_gen3_scc_taps,
|
||||
.taps_num = ARRAY_SIZE(rcar_gen3_scc_taps),
|
||||
/* DMAC can handle 0xffffffff blk count but only 1 segment */
|
||||
|
|
@ -113,9 +115,10 @@ static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = {
|
|||
};
|
||||
|
||||
static const struct of_device_id renesas_sdhi_internal_dmac_of_match[] = {
|
||||
{ .compatible = "renesas,sdhi-r7s9210", .data = &of_rza2_compatible, },
|
||||
{ .compatible = "renesas,sdhi-mmc-r8a77470", .data = &of_rcar_gen3_compatible, },
|
||||
{ .compatible = "renesas,sdhi-r8a7795", .data = &of_rcar_r8a7795_compatible, },
|
||||
{ .compatible = "renesas,sdhi-r8a7796", .data = &of_rcar_r8a7795_compatible, },
|
||||
{ .compatible = "renesas,sdhi-r8a7795", .data = &of_rcar_gen3_compatible, },
|
||||
{ .compatible = "renesas,sdhi-r8a7796", .data = &of_rcar_gen3_compatible, },
|
||||
{ .compatible = "renesas,rcar-gen3-sdhi", .data = &of_rcar_gen3_compatible, },
|
||||
{},
|
||||
};
|
||||
|
|
@ -172,7 +175,10 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host,
|
|||
struct mmc_data *data)
|
||||
{
|
||||
struct scatterlist *sg = host->sg_ptr;
|
||||
u32 dtran_mode = DTRAN_MODE_BUS_WIDTH | DTRAN_MODE_ADDR_MODE;
|
||||
u32 dtran_mode = DTRAN_MODE_BUS_WIDTH;
|
||||
|
||||
if (!test_bit(SDHI_INTERNAL_DMAC_ADDR_MODE_FIXED_ONLY, &global_flags))
|
||||
dtran_mode |= DTRAN_MODE_ADDR_MODE;
|
||||
|
||||
if (!dma_map_sg(&host->pdev->dev, sg, host->sg_len,
|
||||
mmc_get_dma_dir(data)))
|
||||
|
|
@ -292,18 +298,22 @@ static const struct tmio_mmc_dma_ops renesas_sdhi_internal_dmac_dma_ops = {
|
|||
*/
|
||||
static const struct soc_device_attribute soc_whitelist[] = {
|
||||
/* specific ones */
|
||||
{ .soc_id = "r7s9210",
|
||||
.data = (void *)BIT(SDHI_INTERNAL_DMAC_ADDR_MODE_FIXED_ONLY) },
|
||||
{ .soc_id = "r8a7795", .revision = "ES1.*",
|
||||
.data = (void *)BIT(SDHI_INTERNAL_DMAC_ONE_RX_ONLY) },
|
||||
{ .soc_id = "r8a7796", .revision = "ES1.0",
|
||||
.data = (void *)BIT(SDHI_INTERNAL_DMAC_ONE_RX_ONLY) },
|
||||
/* generic ones */
|
||||
{ .soc_id = "r8a774a1" },
|
||||
{ .soc_id = "r8a774c0" },
|
||||
{ .soc_id = "r8a77470" },
|
||||
{ .soc_id = "r8a7795" },
|
||||
{ .soc_id = "r8a7796" },
|
||||
{ .soc_id = "r8a77965" },
|
||||
{ .soc_id = "r8a77970" },
|
||||
{ .soc_id = "r8a77980" },
|
||||
{ .soc_id = "r8a77990" },
|
||||
{ .soc_id = "r8a77995" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -75,19 +75,6 @@ static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static const struct renesas_sdhi_of_data of_rcar_r8a7795_compatible = {
|
||||
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
|
||||
TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2 |
|
||||
TMIO_MMC_HAVE_4TAP_HS400,
|
||||
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
|
||||
MMC_CAP_CMD23,
|
||||
.capabilities2 = MMC_CAP2_NO_WRITE_PROTECT,
|
||||
.bus_shift = 2,
|
||||
.scc_offset = 0x1000,
|
||||
.taps = rcar_gen3_scc_taps,
|
||||
.taps_num = ARRAY_SIZE(rcar_gen3_scc_taps),
|
||||
};
|
||||
|
||||
static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = {
|
||||
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
|
||||
TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
|
||||
|
|
@ -114,8 +101,8 @@ static const struct of_device_id renesas_sdhi_sys_dmac_of_match[] = {
|
|||
{ .compatible = "renesas,sdhi-r8a7792", .data = &of_rcar_gen2_compatible, },
|
||||
{ .compatible = "renesas,sdhi-r8a7793", .data = &of_rcar_gen2_compatible, },
|
||||
{ .compatible = "renesas,sdhi-r8a7794", .data = &of_rcar_gen2_compatible, },
|
||||
{ .compatible = "renesas,sdhi-r8a7795", .data = &of_rcar_r8a7795_compatible, },
|
||||
{ .compatible = "renesas,sdhi-r8a7796", .data = &of_rcar_r8a7795_compatible, },
|
||||
{ .compatible = "renesas,sdhi-r8a7795", .data = &of_rcar_gen3_compatible, },
|
||||
{ .compatible = "renesas,sdhi-r8a7796", .data = &of_rcar_gen3_compatible, },
|
||||
{ .compatible = "renesas,rcar-gen1-sdhi", .data = &of_rcar_gen1_compatible, },
|
||||
{ .compatible = "renesas,rcar-gen2-sdhi", .data = &of_rcar_gen2_compatible, },
|
||||
{ .compatible = "renesas,rcar-gen3-sdhi", .data = &of_rcar_gen3_compatible, },
|
||||
|
|
@ -493,8 +480,7 @@ static const struct soc_device_attribute gen3_soc_whitelist[] = {
|
|||
|
||||
static int renesas_sdhi_sys_dmac_probe(struct platform_device *pdev)
|
||||
{
|
||||
if ((of_device_get_match_data(&pdev->dev) == &of_rcar_gen3_compatible ||
|
||||
of_device_get_match_data(&pdev->dev) == &of_rcar_r8a7795_compatible) &&
|
||||
if (of_device_get_match_data(&pdev->dev) == &of_rcar_gen3_compatible &&
|
||||
!soc_device_match(gen3_soc_whitelist))
|
||||
return -ENODEV;
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#include <linux/mmc/sd.h>
|
||||
#include <linux/mmc/card.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/rtsx_usb.h>
|
||||
|
|
@ -1042,9 +1043,9 @@ static int sd_set_power_mode(struct rtsx_usb_sdmmc *host,
|
|||
|
||||
if (power_mode == MMC_POWER_OFF) {
|
||||
err = sd_power_off(host);
|
||||
pm_runtime_put(sdmmc_dev(host));
|
||||
pm_runtime_put_noidle(sdmmc_dev(host));
|
||||
} else {
|
||||
pm_runtime_get_sync(sdmmc_dev(host));
|
||||
pm_runtime_get_noresume(sdmmc_dev(host));
|
||||
err = sd_power_on(host);
|
||||
}
|
||||
|
||||
|
|
@ -1297,16 +1298,20 @@ static void rtsx_usb_update_led(struct work_struct *work)
|
|||
container_of(work, struct rtsx_usb_sdmmc, led_work);
|
||||
struct rtsx_ucr *ucr = host->ucr;
|
||||
|
||||
pm_runtime_get_sync(sdmmc_dev(host));
|
||||
pm_runtime_get_noresume(sdmmc_dev(host));
|
||||
mutex_lock(&ucr->dev_mutex);
|
||||
|
||||
if (host->power_mode == MMC_POWER_OFF)
|
||||
goto out;
|
||||
|
||||
if (host->led.brightness == LED_OFF)
|
||||
rtsx_usb_turn_off_led(ucr);
|
||||
else
|
||||
rtsx_usb_turn_on_led(ucr);
|
||||
|
||||
out:
|
||||
mutex_unlock(&ucr->dev_mutex);
|
||||
pm_runtime_put(sdmmc_dev(host));
|
||||
pm_runtime_put_sync_suspend(sdmmc_dev(host));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -1320,7 +1325,7 @@ static void rtsx_usb_init_host(struct rtsx_usb_sdmmc *host)
|
|||
mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED |
|
||||
MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST |
|
||||
MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 |
|
||||
MMC_CAP_NEEDS_POLL | MMC_CAP_ERASE;
|
||||
MMC_CAP_ERASE | MMC_CAP_SYNC_RUNTIME_PM;
|
||||
mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE |
|
||||
MMC_CAP2_NO_SDIO;
|
||||
|
||||
|
|
@ -1363,8 +1368,6 @@ static int rtsx_usb_sdmmc_drv_probe(struct platform_device *pdev)
|
|||
|
||||
mutex_init(&host->host_mutex);
|
||||
rtsx_usb_init_host(host);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
#ifdef RTSX_USB_USE_LEDS_CLASS
|
||||
|
|
@ -1419,7 +1422,6 @@ static int rtsx_usb_sdmmc_drv_remove(struct platform_device *pdev)
|
|||
|
||||
mmc_free_host(mmc);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
dev_dbg(&(pdev->dev),
|
||||
|
|
@ -1428,6 +1430,31 @@ static int rtsx_usb_sdmmc_drv_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int rtsx_usb_sdmmc_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct rtsx_usb_sdmmc *host = dev_get_drvdata(dev);
|
||||
|
||||
host->mmc->caps &= ~MMC_CAP_NEEDS_POLL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtsx_usb_sdmmc_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct rtsx_usb_sdmmc *host = dev_get_drvdata(dev);
|
||||
|
||||
host->mmc->caps |= MMC_CAP_NEEDS_POLL;
|
||||
if (sdmmc_get_cd(host->mmc) == 1)
|
||||
mmc_detect_change(host->mmc, 0);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops rtsx_usb_sdmmc_dev_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(rtsx_usb_sdmmc_runtime_suspend,
|
||||
rtsx_usb_sdmmc_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct platform_device_id rtsx_usb_sdmmc_ids[] = {
|
||||
{
|
||||
.name = "rtsx_usb_sdmmc",
|
||||
|
|
@ -1443,6 +1470,7 @@ static struct platform_driver rtsx_usb_sdmmc_driver = {
|
|||
.id_table = rtsx_usb_sdmmc_ids,
|
||||
.driver = {
|
||||
.name = "rtsx_usb_sdmmc",
|
||||
.pm = &rtsx_usb_sdmmc_dev_pm_ops,
|
||||
},
|
||||
};
|
||||
module_platform_driver(rtsx_usb_sdmmc_driver);
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
|
||||
#include <plat/gpio-cfg.h>
|
||||
|
|
@ -1406,18 +1405,7 @@ static int s3cmci_state_show(struct seq_file *seq, void *v)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int s3cmci_state_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, s3cmci_state_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations s3cmci_fops_state = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = s3cmci_state_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
DEFINE_SHOW_ATTRIBUTE(s3cmci_state);
|
||||
|
||||
#define DBG_REG(_r) { .addr = S3C2410_SDI##_r, .name = #_r }
|
||||
|
||||
|
|
@ -1459,18 +1447,7 @@ static int s3cmci_regs_show(struct seq_file *seq, void *v)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int s3cmci_regs_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, s3cmci_regs_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations s3cmci_fops_regs = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = s3cmci_regs_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
DEFINE_SHOW_ATTRIBUTE(s3cmci_regs);
|
||||
|
||||
static void s3cmci_debugfs_attach(struct s3cmci_host *host)
|
||||
{
|
||||
|
|
@ -1484,14 +1461,14 @@ static void s3cmci_debugfs_attach(struct s3cmci_host *host)
|
|||
|
||||
host->debug_state = debugfs_create_file("state", 0444,
|
||||
host->debug_root, host,
|
||||
&s3cmci_fops_state);
|
||||
&s3cmci_state_fops);
|
||||
|
||||
if (IS_ERR(host->debug_state))
|
||||
dev_err(dev, "failed to create debug state file\n");
|
||||
|
||||
host->debug_regs = debugfs_create_file("regs", 0444,
|
||||
host->debug_root, host,
|
||||
&s3cmci_fops_regs);
|
||||
&s3cmci_regs_fops);
|
||||
|
||||
if (IS_ERR(host->debug_regs))
|
||||
dev_err(dev, "failed to create debug regs file\n");
|
||||
|
|
@ -1545,25 +1522,19 @@ static int s3cmci_probe_pdata(struct s3cmci_host *host)
|
|||
if (pdata->wprotect_invert)
|
||||
mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
|
||||
|
||||
if (pdata->detect_invert)
|
||||
mmc->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;
|
||||
|
||||
if (gpio_is_valid(pdata->gpio_detect)) {
|
||||
ret = mmc_gpio_request_cd(mmc, pdata->gpio_detect, 0);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "error requesting GPIO for CD %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
/* If we get -ENOENT we have no card detect GPIO line */
|
||||
ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0, NULL);
|
||||
if (ret != -ENOENT) {
|
||||
dev_err(&pdev->dev, "error requesting GPIO for CD %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(pdata->gpio_wprotect)) {
|
||||
ret = mmc_gpio_request_ro(mmc, pdata->gpio_wprotect);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "error requesting GPIO for WP %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
ret = mmc_gpiod_request_ro(host->mmc, "wp", 0, false, 0, NULL);
|
||||
if (ret != -ENOENT) {
|
||||
dev_err(&pdev->dev, "error requesting GPIO for WP %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -437,7 +437,8 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = {
|
|||
MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR |
|
||||
MMC_CAP_CMD_DURING_TFR | MMC_CAP_WAIT_WHILE_BUSY,
|
||||
.flags = SDHCI_ACPI_RUNTIME_PM,
|
||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
|
||||
SDHCI_QUIRK_NO_LED,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||
SDHCI_QUIRK2_STOP_WITH_TC |
|
||||
SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400,
|
||||
|
|
@ -448,6 +449,7 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = {
|
|||
|
||||
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {
|
||||
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
|
||||
SDHCI_QUIRK_NO_LED |
|
||||
SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||
.quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON,
|
||||
.caps = MMC_CAP_NONREMOVABLE | MMC_CAP_POWER_OFF_CARD |
|
||||
|
|
@ -462,7 +464,8 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {
|
|||
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sd = {
|
||||
.flags = SDHCI_ACPI_SD_CD | SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL |
|
||||
SDHCI_ACPI_RUNTIME_PM,
|
||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
|
||||
SDHCI_QUIRK_NO_LED,
|
||||
.quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON |
|
||||
SDHCI_QUIRK2_STOP_WITH_TC,
|
||||
.caps = MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_AGGRESSIVE_PM,
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mmc/host.h>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mmc/host.h>
|
||||
|
|
@ -21,7 +20,6 @@
|
|||
#include <linux/mmc/slot-gpio.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/platform_data/mmc-esdhc-imx.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
|
@ -429,7 +427,7 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
|
|||
val = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
||||
else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING)
|
||||
/* the std tuning bits is in ACMD12_ERR for imx6sl */
|
||||
val = readl(host->ioaddr + SDHCI_ACMD12_ERR);
|
||||
val = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
|
||||
}
|
||||
|
||||
if (val & ESDHC_MIX_CTRL_EXE_TUNE)
|
||||
|
|
@ -494,7 +492,7 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
|
|||
}
|
||||
writel(new_val , host->ioaddr + ESDHC_MIX_CTRL);
|
||||
} else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
|
||||
u32 v = readl(host->ioaddr + SDHCI_ACMD12_ERR);
|
||||
u32 v = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
|
||||
u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
||||
if (val & SDHCI_CTRL_TUNED_CLK) {
|
||||
v |= ESDHC_MIX_CTRL_SMPCLK_SEL;
|
||||
|
|
@ -512,7 +510,7 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
|
|||
v &= ~ESDHC_MIX_CTRL_EXE_TUNE;
|
||||
}
|
||||
|
||||
writel(v, host->ioaddr + SDHCI_ACMD12_ERR);
|
||||
writel(v, host->ioaddr + SDHCI_AUTO_CMD_STATUS);
|
||||
writel(m, host->ioaddr + ESDHC_MIX_CTRL);
|
||||
}
|
||||
return;
|
||||
|
|
@ -957,9 +955,9 @@ static void esdhc_reset_tuning(struct sdhci_host *host)
|
|||
writel(ctrl, host->ioaddr + ESDHC_MIX_CTRL);
|
||||
writel(0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
|
||||
} else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
|
||||
ctrl = readl(host->ioaddr + SDHCI_ACMD12_ERR);
|
||||
ctrl = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
|
||||
ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
|
||||
writel(ctrl, host->ioaddr + SDHCI_ACMD12_ERR);
|
||||
writel(ctrl, host->ioaddr + SDHCI_AUTO_CMD_STATUS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1139,8 +1137,12 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
|
|||
if (of_get_property(np, "fsl,wp-controller", NULL))
|
||||
boarddata->wp_type = ESDHC_WP_CONTROLLER;
|
||||
|
||||
boarddata->wp_gpio = of_get_named_gpio(np, "wp-gpios", 0);
|
||||
if (gpio_is_valid(boarddata->wp_gpio))
|
||||
/*
|
||||
* If we have this property, then activate WP check.
|
||||
* Retrieveing and requesting the actual WP GPIO will happen
|
||||
* in the call to mmc_of_parse().
|
||||
*/
|
||||
if (of_property_read_bool(np, "wp-gpios"))
|
||||
boarddata->wp_type = ESDHC_WP_GPIO;
|
||||
|
||||
of_property_read_u32(np, "fsl,tuning-step", &boarddata->tuning_step);
|
||||
|
|
@ -1198,7 +1200,7 @@ static int sdhci_esdhc_imx_probe_nondt(struct platform_device *pdev,
|
|||
host->mmc->parent->platform_data);
|
||||
/* write_protect */
|
||||
if (boarddata->wp_type == ESDHC_WP_GPIO) {
|
||||
err = mmc_gpio_request_ro(host->mmc, boarddata->wp_gpio);
|
||||
err = mmc_gpiod_request_ro(host->mmc, "wp", 0, false, 0, NULL);
|
||||
if (err) {
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"failed to request write-protect gpio!\n");
|
||||
|
|
@ -1210,7 +1212,7 @@ static int sdhci_esdhc_imx_probe_nondt(struct platform_device *pdev,
|
|||
/* card_detect */
|
||||
switch (boarddata->cd_type) {
|
||||
case ESDHC_CD_GPIO:
|
||||
err = mmc_gpio_request_cd(host->mmc, boarddata->cd_gpio, 0);
|
||||
err = mmc_gpiod_request_cd(host->mmc, "cd", 0, false, 0, NULL);
|
||||
if (err) {
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"failed to request card-detect gpio!\n");
|
||||
|
|
@ -1317,7 +1319,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
|||
|
||||
/* clear tuning bits in case ROM has set it already */
|
||||
writel(0x0, host->ioaddr + ESDHC_MIX_CTRL);
|
||||
writel(0x0, host->ioaddr + SDHCI_ACMD12_ERR);
|
||||
writel(0x0, host->ioaddr + SDHCI_AUTO_CMD_STATUS);
|
||||
writel(0x0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -59,9 +59,33 @@
|
|||
|
||||
/* Tuning Block Control Register */
|
||||
#define ESDHC_TBCTL 0x120
|
||||
#define ESDHC_HS400_WNDW_ADJUST 0x00000040
|
||||
#define ESDHC_HS400_MODE 0x00000010
|
||||
#define ESDHC_TB_EN 0x00000004
|
||||
#define ESDHC_TBPTR 0x128
|
||||
|
||||
/* SD Clock Control Register */
|
||||
#define ESDHC_SDCLKCTL 0x144
|
||||
#define ESDHC_LPBK_CLK_SEL 0x80000000
|
||||
#define ESDHC_CMD_CLK_CTL 0x00008000
|
||||
|
||||
/* SD Timing Control Register */
|
||||
#define ESDHC_SDTIMNGCTL 0x148
|
||||
#define ESDHC_FLW_CTL_BG 0x00008000
|
||||
|
||||
/* DLL Config 0 Register */
|
||||
#define ESDHC_DLLCFG0 0x160
|
||||
#define ESDHC_DLL_ENABLE 0x80000000
|
||||
#define ESDHC_DLL_FREQ_SEL 0x08000000
|
||||
|
||||
/* DLL Config 1 Register */
|
||||
#define ESDHC_DLLCFG1 0x164
|
||||
#define ESDHC_DLL_PD_PULSE_STRETCH_SEL 0x80000000
|
||||
|
||||
/* DLL Status 0 Register */
|
||||
#define ESDHC_DLLSTAT0 0x170
|
||||
#define ESDHC_DLL_STS_SLV_LOCK 0x08000000
|
||||
|
||||
/* Control Register for DMA transfer */
|
||||
#define ESDHC_DMA_SYSCTL 0x40c
|
||||
#define ESDHC_PERIPHERAL_CLK_SEL 0x00080000
|
||||
|
|
|
|||
|
|
@ -232,6 +232,7 @@ struct sdhci_msm_variant_ops {
|
|||
*/
|
||||
struct sdhci_msm_variant_info {
|
||||
bool mci_removed;
|
||||
bool restore_dll_config;
|
||||
const struct sdhci_msm_variant_ops *var_ops;
|
||||
const struct sdhci_msm_offset *offset;
|
||||
};
|
||||
|
|
@ -256,8 +257,11 @@ struct sdhci_msm_host {
|
|||
bool pwr_irq_flag;
|
||||
u32 caps_0;
|
||||
bool mci_removed;
|
||||
bool restore_dll_config;
|
||||
const struct sdhci_msm_variant_ops *var_ops;
|
||||
const struct sdhci_msm_offset *offset;
|
||||
bool use_cdr;
|
||||
u32 transfer_mode;
|
||||
};
|
||||
|
||||
static const struct sdhci_msm_offset *sdhci_priv_msm_offset(struct sdhci_host *host)
|
||||
|
|
@ -1025,6 +1029,69 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static bool sdhci_msm_is_tuning_needed(struct sdhci_host *host)
|
||||
{
|
||||
struct mmc_ios *ios = &host->mmc->ios;
|
||||
|
||||
/*
|
||||
* Tuning is required for SDR104, HS200 and HS400 cards and
|
||||
* if clock frequency is greater than 100MHz in these modes.
|
||||
*/
|
||||
if (host->clock <= CORE_FREQ_100MHZ ||
|
||||
!(ios->timing == MMC_TIMING_MMC_HS400 ||
|
||||
ios->timing == MMC_TIMING_MMC_HS200 ||
|
||||
ios->timing == MMC_TIMING_UHS_SDR104) ||
|
||||
ios->enhanced_strobe)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int sdhci_msm_restore_sdr_dll_config(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* SDR DLL comes into picture only for timing modes which needs
|
||||
* tuning.
|
||||
*/
|
||||
if (!sdhci_msm_is_tuning_needed(host))
|
||||
return 0;
|
||||
|
||||
/* Reset the tuning block */
|
||||
ret = msm_init_cm_dll(host);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Restore the tuning block */
|
||||
ret = msm_config_cm_dll_phase(host, msm_host->saved_tuning_phase);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sdhci_msm_set_cdr(struct sdhci_host *host, bool enable)
|
||||
{
|
||||
const struct sdhci_msm_offset *msm_offset = sdhci_priv_msm_offset(host);
|
||||
u32 config, oldconfig = readl_relaxed(host->ioaddr +
|
||||
msm_offset->core_dll_config);
|
||||
|
||||
config = oldconfig;
|
||||
if (enable) {
|
||||
config |= CORE_CDR_EN;
|
||||
config &= ~CORE_CDR_EXT_EN;
|
||||
} else {
|
||||
config &= ~CORE_CDR_EN;
|
||||
config |= CORE_CDR_EXT_EN;
|
||||
}
|
||||
|
||||
if (config != oldconfig) {
|
||||
writel_relaxed(config, host->ioaddr +
|
||||
msm_offset->core_dll_config);
|
||||
}
|
||||
}
|
||||
|
||||
static int sdhci_msm_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
||||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
|
|
@ -1035,15 +1102,14 @@ static int sdhci_msm_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
/*
|
||||
* Tuning is required for SDR104, HS200 and HS400 cards and
|
||||
* if clock frequency is greater than 100MHz in these modes.
|
||||
*/
|
||||
if (host->clock <= CORE_FREQ_100MHZ ||
|
||||
!(ios.timing == MMC_TIMING_MMC_HS400 ||
|
||||
ios.timing == MMC_TIMING_MMC_HS200 ||
|
||||
ios.timing == MMC_TIMING_UHS_SDR104))
|
||||
if (!sdhci_msm_is_tuning_needed(host)) {
|
||||
msm_host->use_cdr = false;
|
||||
sdhci_msm_set_cdr(host, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Clock-Data-Recovery used to dynamically adjust RX sampling point */
|
||||
msm_host->use_cdr = true;
|
||||
|
||||
/*
|
||||
* For HS400 tuning in HS200 timing requires:
|
||||
|
|
@ -1069,7 +1135,6 @@ retry:
|
|||
if (rc)
|
||||
return rc;
|
||||
|
||||
msm_host->saved_tuning_phase = phase;
|
||||
rc = mmc_send_tuning(mmc, opcode, NULL);
|
||||
if (!rc) {
|
||||
/* Tuning is successful at this tuning point */
|
||||
|
|
@ -1094,6 +1159,7 @@ retry:
|
|||
rc = msm_config_cm_dll_phase(host, phase);
|
||||
if (rc)
|
||||
return rc;
|
||||
msm_host->saved_tuning_phase = phase;
|
||||
dev_dbg(mmc_dev(mmc), "%s: Setting the tuning phase to %d\n",
|
||||
mmc_hostname(mmc), phase);
|
||||
} else {
|
||||
|
|
@ -1525,6 +1591,19 @@ static int __sdhci_msm_check_write(struct sdhci_host *host, u16 val, int reg)
|
|||
case SDHCI_POWER_CONTROL:
|
||||
req_type = !val ? REQ_BUS_OFF : REQ_BUS_ON;
|
||||
break;
|
||||
case SDHCI_TRANSFER_MODE:
|
||||
msm_host->transfer_mode = val;
|
||||
break;
|
||||
case SDHCI_COMMAND:
|
||||
if (!msm_host->use_cdr)
|
||||
break;
|
||||
if ((msm_host->transfer_mode & SDHCI_TRNS_READ) &&
|
||||
SDHCI_GET_CMD(val) != MMC_SEND_TUNING_BLOCK_HS200 &&
|
||||
SDHCI_GET_CMD(val) != MMC_SEND_TUNING_BLOCK)
|
||||
sdhci_msm_set_cdr(host, true);
|
||||
else
|
||||
sdhci_msm_set_cdr(host, false);
|
||||
break;
|
||||
}
|
||||
|
||||
if (req_type) {
|
||||
|
|
@ -1616,7 +1695,6 @@ static const struct sdhci_msm_variant_ops v5_var_ops = {
|
|||
};
|
||||
|
||||
static const struct sdhci_msm_variant_info sdhci_msm_mci_var = {
|
||||
.mci_removed = false,
|
||||
.var_ops = &mci_var_ops,
|
||||
.offset = &sdhci_msm_mci_offset,
|
||||
};
|
||||
|
|
@ -1627,9 +1705,17 @@ static const struct sdhci_msm_variant_info sdhci_msm_v5_var = {
|
|||
.offset = &sdhci_msm_v5_offset,
|
||||
};
|
||||
|
||||
static const struct sdhci_msm_variant_info sdm845_sdhci_var = {
|
||||
.mci_removed = true,
|
||||
.restore_dll_config = true,
|
||||
.var_ops = &v5_var_ops,
|
||||
.offset = &sdhci_msm_v5_offset,
|
||||
};
|
||||
|
||||
static const struct of_device_id sdhci_msm_dt_match[] = {
|
||||
{.compatible = "qcom,sdhci-msm-v4", .data = &sdhci_msm_mci_var},
|
||||
{.compatible = "qcom,sdhci-msm-v5", .data = &sdhci_msm_v5_var},
|
||||
{.compatible = "qcom,sdm845-sdhci", .data = &sdm845_sdhci_var},
|
||||
{},
|
||||
};
|
||||
|
||||
|
|
@ -1689,6 +1775,7 @@ static int sdhci_msm_probe(struct platform_device *pdev)
|
|||
var_info = of_device_get_match_data(&pdev->dev);
|
||||
|
||||
msm_host->mci_removed = var_info->mci_removed;
|
||||
msm_host->restore_dll_config = var_info->restore_dll_config;
|
||||
msm_host->var_ops = var_info->var_ops;
|
||||
msm_host->offset = var_info->offset;
|
||||
|
||||
|
|
@ -1910,8 +1997,7 @@ static int sdhci_msm_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int sdhci_msm_runtime_suspend(struct device *dev)
|
||||
static __maybe_unused int sdhci_msm_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
|
|
@ -1923,16 +2009,26 @@ static int sdhci_msm_runtime_suspend(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int sdhci_msm_runtime_resume(struct device *dev)
|
||||
static __maybe_unused int sdhci_msm_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
||||
int ret;
|
||||
|
||||
return clk_bulk_prepare_enable(ARRAY_SIZE(msm_host->bulk_clks),
|
||||
ret = clk_bulk_prepare_enable(ARRAY_SIZE(msm_host->bulk_clks),
|
||||
msm_host->bulk_clks);
|
||||
if (ret)
|
||||
return ret;
|
||||
/*
|
||||
* Whenever core-clock is gated dynamically, it's needed to
|
||||
* restore the SDR DLL settings when the clock is ungated.
|
||||
*/
|
||||
if (msm_host->restore_dll_config && msm_host->clk_rate)
|
||||
return sdhci_msm_restore_sdr_dll_config(host);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops sdhci_msm_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
|
|
|
|||
|
|
@ -231,25 +231,6 @@ static void sdhci_arasan_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||
}
|
||||
}
|
||||
|
||||
static void sdhci_arasan_am654_set_clock(struct sdhci_host *host,
|
||||
unsigned int clock)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
if (sdhci_arasan->is_phy_on) {
|
||||
phy_power_off(sdhci_arasan->phy);
|
||||
sdhci_arasan->is_phy_on = false;
|
||||
}
|
||||
|
||||
sdhci_set_clock(host, clock);
|
||||
|
||||
if (clock > PHY_CLK_TOO_SLOW_HZ) {
|
||||
phy_power_on(sdhci_arasan->phy);
|
||||
sdhci_arasan->is_phy_on = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void sdhci_arasan_hs400_enhanced_strobe(struct mmc_host *mmc,
|
||||
struct mmc_ios *ios)
|
||||
{
|
||||
|
|
@ -335,29 +316,6 @@ static struct sdhci_arasan_of_data sdhci_arasan_data = {
|
|||
.pdata = &sdhci_arasan_pdata,
|
||||
};
|
||||
|
||||
static const struct sdhci_ops sdhci_arasan_am654_ops = {
|
||||
.set_clock = sdhci_arasan_am654_set_clock,
|
||||
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
.get_timeout_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
.reset = sdhci_arasan_reset,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_arasan_am654_pdata = {
|
||||
.ops = &sdhci_arasan_am654_ops,
|
||||
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
|
||||
SDHCI_QUIRK_INVERTED_WRITE_PROTECT |
|
||||
SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN |
|
||||
SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400,
|
||||
};
|
||||
|
||||
static const struct sdhci_arasan_of_data sdhci_arasan_am654_data = {
|
||||
.pdata = &sdhci_arasan_am654_pdata,
|
||||
};
|
||||
|
||||
static u32 sdhci_arasan_cqhci_irq(struct sdhci_host *host, u32 intmask)
|
||||
{
|
||||
int cmd_error = 0;
|
||||
|
|
@ -520,10 +478,6 @@ static const struct of_device_id sdhci_arasan_of_match[] = {
|
|||
.compatible = "rockchip,rk3399-sdhci-5.1",
|
||||
.data = &sdhci_arasan_rk3399_data,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,am654-sdhci-5.1",
|
||||
.data = &sdhci_arasan_am654_data,
|
||||
},
|
||||
/* Generic compatible below here */
|
||||
{
|
||||
.compatible = "arasan,sdhci-8.9a",
|
||||
|
|
|
|||
|
|
@ -78,6 +78,8 @@ struct sdhci_esdhc {
|
|||
u8 vendor_ver;
|
||||
u8 spec_ver;
|
||||
bool quirk_incorrect_hostver;
|
||||
bool quirk_limited_clk_division;
|
||||
bool quirk_unreliable_pulse_detection;
|
||||
bool quirk_fixup_tuning;
|
||||
unsigned int peripheral_clock;
|
||||
const struct esdhc_clk_fixup *clk_fixup;
|
||||
|
|
@ -528,8 +530,12 @@ static void esdhc_clock_enable(struct sdhci_host *host, bool enable)
|
|||
/* Wait max 20 ms */
|
||||
timeout = ktime_add_ms(ktime_get(), 20);
|
||||
val = ESDHC_CLOCK_STABLE;
|
||||
while (!(sdhci_readl(host, ESDHC_PRSSTAT) & val)) {
|
||||
if (ktime_after(ktime_get(), timeout)) {
|
||||
while (1) {
|
||||
bool timedout = ktime_after(ktime_get(), timeout);
|
||||
|
||||
if (sdhci_readl(host, ESDHC_PRSSTAT) & val)
|
||||
break;
|
||||
if (timedout) {
|
||||
pr_err("%s: Internal clock never stabilised.\n",
|
||||
mmc_hostname(host->mmc));
|
||||
break;
|
||||
|
|
@ -544,6 +550,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||
struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
|
||||
int pre_div = 1;
|
||||
int div = 1;
|
||||
int division;
|
||||
ktime_t timeout;
|
||||
long fixup = 0;
|
||||
u32 temp;
|
||||
|
|
@ -579,6 +586,26 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||
while (host->max_clk / pre_div / div > clock && div < 16)
|
||||
div++;
|
||||
|
||||
if (esdhc->quirk_limited_clk_division &&
|
||||
clock == MMC_HS200_MAX_DTR &&
|
||||
(host->mmc->ios.timing == MMC_TIMING_MMC_HS400 ||
|
||||
host->flags & SDHCI_HS400_TUNING)) {
|
||||
division = pre_div * div;
|
||||
if (division <= 4) {
|
||||
pre_div = 4;
|
||||
div = 1;
|
||||
} else if (division <= 8) {
|
||||
pre_div = 4;
|
||||
div = 2;
|
||||
} else if (division <= 12) {
|
||||
pre_div = 4;
|
||||
div = 3;
|
||||
} else {
|
||||
pr_warn("%s: using unsupported clock division.\n",
|
||||
mmc_hostname(host->mmc));
|
||||
}
|
||||
}
|
||||
|
||||
dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
|
||||
clock, host->max_clk / pre_div / div);
|
||||
host->mmc->actual_clock = host->max_clk / pre_div / div;
|
||||
|
|
@ -592,10 +619,36 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||
| (pre_div << ESDHC_PREDIV_SHIFT));
|
||||
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
|
||||
|
||||
if (host->mmc->ios.timing == MMC_TIMING_MMC_HS400 &&
|
||||
clock == MMC_HS200_MAX_DTR) {
|
||||
temp = sdhci_readl(host, ESDHC_TBCTL);
|
||||
sdhci_writel(host, temp | ESDHC_HS400_MODE, ESDHC_TBCTL);
|
||||
temp = sdhci_readl(host, ESDHC_SDCLKCTL);
|
||||
sdhci_writel(host, temp | ESDHC_CMD_CLK_CTL, ESDHC_SDCLKCTL);
|
||||
esdhc_clock_enable(host, true);
|
||||
|
||||
temp = sdhci_readl(host, ESDHC_DLLCFG0);
|
||||
temp |= ESDHC_DLL_ENABLE;
|
||||
if (host->mmc->actual_clock == MMC_HS200_MAX_DTR)
|
||||
temp |= ESDHC_DLL_FREQ_SEL;
|
||||
sdhci_writel(host, temp, ESDHC_DLLCFG0);
|
||||
temp = sdhci_readl(host, ESDHC_TBCTL);
|
||||
sdhci_writel(host, temp | ESDHC_HS400_WNDW_ADJUST, ESDHC_TBCTL);
|
||||
|
||||
esdhc_clock_enable(host, false);
|
||||
temp = sdhci_readl(host, ESDHC_DMA_SYSCTL);
|
||||
temp |= ESDHC_FLUSH_ASYNC_FIFO;
|
||||
sdhci_writel(host, temp, ESDHC_DMA_SYSCTL);
|
||||
}
|
||||
|
||||
/* Wait max 20 ms */
|
||||
timeout = ktime_add_ms(ktime_get(), 20);
|
||||
while (!(sdhci_readl(host, ESDHC_PRSSTAT) & ESDHC_CLOCK_STABLE)) {
|
||||
if (ktime_after(ktime_get(), timeout)) {
|
||||
while (1) {
|
||||
bool timedout = ktime_after(ktime_get(), timeout);
|
||||
|
||||
if (sdhci_readl(host, ESDHC_PRSSTAT) & ESDHC_CLOCK_STABLE)
|
||||
break;
|
||||
if (timedout) {
|
||||
pr_err("%s: Internal clock never stabilised.\n",
|
||||
mmc_hostname(host->mmc));
|
||||
return;
|
||||
|
|
@ -603,6 +656,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||
udelay(10);
|
||||
}
|
||||
|
||||
temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
|
||||
temp |= ESDHC_CLOCK_SDCLKEN;
|
||||
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
|
||||
}
|
||||
|
|
@ -631,6 +685,8 @@ static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)
|
|||
|
||||
static void esdhc_reset(struct sdhci_host *host, u8 mask)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
|
||||
u32 val;
|
||||
|
||||
sdhci_reset(host, mask);
|
||||
|
|
@ -642,6 +698,12 @@ static void esdhc_reset(struct sdhci_host *host, u8 mask)
|
|||
val = sdhci_readl(host, ESDHC_TBCTL);
|
||||
val &= ~ESDHC_TB_EN;
|
||||
sdhci_writel(host, val, ESDHC_TBCTL);
|
||||
|
||||
if (esdhc->quirk_unreliable_pulse_detection) {
|
||||
val = sdhci_readl(host, ESDHC_DLLCFG1);
|
||||
val &= ~ESDHC_DLL_PD_PULSE_STRETCH_SEL;
|
||||
sdhci_writel(host, val, ESDHC_DLLCFG1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -728,25 +790,50 @@ static struct soc_device_attribute soc_fixup_tuning[] = {
|
|||
{ },
|
||||
};
|
||||
|
||||
static int esdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
||||
static void esdhc_tuning_block_enable(struct sdhci_host *host, bool enable)
|
||||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
|
||||
u32 val;
|
||||
|
||||
/* Use tuning block for tuning procedure */
|
||||
esdhc_clock_enable(host, false);
|
||||
|
||||
val = sdhci_readl(host, ESDHC_DMA_SYSCTL);
|
||||
val |= ESDHC_FLUSH_ASYNC_FIFO;
|
||||
sdhci_writel(host, val, ESDHC_DMA_SYSCTL);
|
||||
|
||||
val = sdhci_readl(host, ESDHC_TBCTL);
|
||||
val |= ESDHC_TB_EN;
|
||||
if (enable)
|
||||
val |= ESDHC_TB_EN;
|
||||
else
|
||||
val &= ~ESDHC_TB_EN;
|
||||
sdhci_writel(host, val, ESDHC_TBCTL);
|
||||
esdhc_clock_enable(host, true);
|
||||
|
||||
sdhci_execute_tuning(mmc, opcode);
|
||||
esdhc_clock_enable(host, true);
|
||||
}
|
||||
|
||||
static int esdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
||||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
|
||||
bool hs400_tuning;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
if (esdhc->quirk_limited_clk_division &&
|
||||
host->flags & SDHCI_HS400_TUNING)
|
||||
esdhc_of_set_clock(host, host->clock);
|
||||
|
||||
esdhc_tuning_block_enable(host, true);
|
||||
|
||||
hs400_tuning = host->flags & SDHCI_HS400_TUNING;
|
||||
ret = sdhci_execute_tuning(mmc, opcode);
|
||||
|
||||
if (hs400_tuning) {
|
||||
val = sdhci_readl(host, ESDHC_SDTIMNGCTL);
|
||||
val |= ESDHC_FLW_CTL_BG;
|
||||
sdhci_writel(host, val, ESDHC_SDTIMNGCTL);
|
||||
}
|
||||
|
||||
if (host->tuning_err == -EAGAIN && esdhc->quirk_fixup_tuning) {
|
||||
|
||||
/* program TBPTR[TB_WNDW_END_PTR] = 3*DIV_RATIO and
|
||||
|
|
@ -765,7 +852,16 @@ static int esdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|||
sdhci_writel(host, val, ESDHC_TBCTL);
|
||||
sdhci_execute_tuning(mmc, opcode);
|
||||
}
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void esdhc_set_uhs_signaling(struct sdhci_host *host,
|
||||
unsigned int timing)
|
||||
{
|
||||
if (timing == MMC_TIMING_MMC_HS400)
|
||||
esdhc_tuning_block_enable(host, true);
|
||||
else
|
||||
sdhci_set_uhs_signaling(host, timing);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
|
@ -814,7 +910,7 @@ static const struct sdhci_ops sdhci_esdhc_be_ops = {
|
|||
.adma_workaround = esdhc_of_adma_workaround,
|
||||
.set_bus_width = esdhc_pltfm_set_bus_width,
|
||||
.reset = esdhc_reset,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
.set_uhs_signaling = esdhc_set_uhs_signaling,
|
||||
};
|
||||
|
||||
static const struct sdhci_ops sdhci_esdhc_le_ops = {
|
||||
|
|
@ -831,7 +927,7 @@ static const struct sdhci_ops sdhci_esdhc_le_ops = {
|
|||
.adma_workaround = esdhc_of_adma_workaround,
|
||||
.set_bus_width = esdhc_pltfm_set_bus_width,
|
||||
.reset = esdhc_reset,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
.set_uhs_signaling = esdhc_set_uhs_signaling,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_esdhc_be_pdata = {
|
||||
|
|
@ -857,6 +953,16 @@ static struct soc_device_attribute soc_incorrect_hostver[] = {
|
|||
{ },
|
||||
};
|
||||
|
||||
static struct soc_device_attribute soc_fixup_sdhc_clkdivs[] = {
|
||||
{ .family = "QorIQ LX2160A", .revision = "1.0", },
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct soc_device_attribute soc_unreliable_pulse_detection[] = {
|
||||
{ .family = "QorIQ LX2160A", .revision = "1.0", },
|
||||
{ },
|
||||
};
|
||||
|
||||
static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
|
|
@ -879,6 +985,16 @@ static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host)
|
|||
else
|
||||
esdhc->quirk_incorrect_hostver = false;
|
||||
|
||||
if (soc_device_match(soc_fixup_sdhc_clkdivs))
|
||||
esdhc->quirk_limited_clk_division = true;
|
||||
else
|
||||
esdhc->quirk_limited_clk_division = false;
|
||||
|
||||
if (soc_device_match(soc_unreliable_pulse_detection))
|
||||
esdhc->quirk_unreliable_pulse_detection = true;
|
||||
else
|
||||
esdhc->quirk_unreliable_pulse_detection = false;
|
||||
|
||||
match = of_match_node(sdhci_esdhc_of_match, pdev->dev.of_node);
|
||||
if (match)
|
||||
esdhc->clk_fixup = match->data;
|
||||
|
|
@ -909,6 +1025,12 @@ static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host)
|
|||
}
|
||||
}
|
||||
|
||||
static int esdhc_hs400_prepare_ddr(struct mmc_host *mmc)
|
||||
{
|
||||
esdhc_tuning_block_enable(mmc_priv(mmc), false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdhci_esdhc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host;
|
||||
|
|
@ -932,6 +1054,7 @@ static int sdhci_esdhc_probe(struct platform_device *pdev)
|
|||
host->mmc_host_ops.start_signal_voltage_switch =
|
||||
esdhc_signal_voltage_switch;
|
||||
host->mmc_host_ops.execute_tuning = esdhc_execute_tuning;
|
||||
host->mmc_host_ops.hs400_prepare_ddr = esdhc_hs400_prepare_ddr;
|
||||
host->tuning_delay = 1;
|
||||
|
||||
esdhc_init(pdev, host);
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/sys_soc.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#include "sdhci-pltfm.h"
|
||||
|
||||
|
|
@ -115,6 +116,7 @@ struct sdhci_omap_host {
|
|||
|
||||
struct pinctrl *pinctrl;
|
||||
struct pinctrl_state **pinctrl_state;
|
||||
bool is_tuning;
|
||||
};
|
||||
|
||||
static void sdhci_omap_start_clock(struct sdhci_omap_host *omap_host);
|
||||
|
|
@ -220,8 +222,12 @@ static void sdhci_omap_conf_bus_power(struct sdhci_omap_host *omap_host,
|
|||
|
||||
/* wait 1ms */
|
||||
timeout = ktime_add_ms(ktime_get(), SDHCI_OMAP_TIMEOUT);
|
||||
while (!(sdhci_omap_readl(omap_host, SDHCI_OMAP_HCTL) & HCTL_SDBP)) {
|
||||
if (WARN_ON(ktime_after(ktime_get(), timeout)))
|
||||
while (1) {
|
||||
bool timedout = ktime_after(ktime_get(), timeout);
|
||||
|
||||
if (sdhci_omap_readl(omap_host, SDHCI_OMAP_HCTL) & HCTL_SDBP)
|
||||
break;
|
||||
if (WARN_ON(timedout))
|
||||
return;
|
||||
usleep_range(5, 10);
|
||||
}
|
||||
|
|
@ -285,19 +291,19 @@ static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
|
||||
struct thermal_zone_device *thermal_dev;
|
||||
struct device *dev = omap_host->dev;
|
||||
struct mmc_ios *ios = &mmc->ios;
|
||||
u32 start_window = 0, max_window = 0;
|
||||
bool single_point_failure = false;
|
||||
bool dcrc_was_enabled = false;
|
||||
u8 cur_match, prev_match = 0;
|
||||
u32 length = 0, max_len = 0;
|
||||
u32 phase_delay = 0;
|
||||
int temperature;
|
||||
int ret = 0;
|
||||
u32 reg;
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
omap_host = sdhci_pltfm_priv(pltfm_host);
|
||||
dev = omap_host->dev;
|
||||
int i;
|
||||
|
||||
/* clock tuning is not needed for upto 52MHz */
|
||||
if (ios->clock <= 52000000)
|
||||
|
|
@ -307,6 +313,16 @@ static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|||
if (ios->timing == MMC_TIMING_UHS_SDR50 && !(reg & CAPA2_TSDR50))
|
||||
return 0;
|
||||
|
||||
thermal_dev = thermal_zone_get_zone_by_name("cpu_thermal");
|
||||
if (IS_ERR(thermal_dev)) {
|
||||
dev_err(dev, "Unable to get thermal zone for tuning\n");
|
||||
return PTR_ERR(thermal_dev);
|
||||
}
|
||||
|
||||
ret = thermal_zone_get_temp(thermal_dev, &temperature);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_DLL);
|
||||
reg |= DLL_SWT;
|
||||
sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg);
|
||||
|
|
@ -322,6 +338,13 @@ static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|||
dcrc_was_enabled = true;
|
||||
}
|
||||
|
||||
omap_host->is_tuning = true;
|
||||
|
||||
/*
|
||||
* Stage 1: Search for a maximum pass window ignoring any
|
||||
* any single point failures. If the tuning value ends up
|
||||
* near it, move away from it in stage 2 below
|
||||
*/
|
||||
while (phase_delay <= MAX_PHASE_DELAY) {
|
||||
sdhci_omap_set_dll(omap_host, phase_delay);
|
||||
|
||||
|
|
@ -329,10 +352,15 @@ static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|||
if (cur_match) {
|
||||
if (prev_match) {
|
||||
length++;
|
||||
} else if (single_point_failure) {
|
||||
/* ignore single point failure */
|
||||
length++;
|
||||
} else {
|
||||
start_window = phase_delay;
|
||||
length = 1;
|
||||
}
|
||||
} else {
|
||||
single_point_failure = prev_match;
|
||||
}
|
||||
|
||||
if (length > max_len) {
|
||||
|
|
@ -350,18 +378,84 @@ static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|||
goto tuning_error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Assign tuning value as a ratio of maximum pass window based
|
||||
* on temperature
|
||||
*/
|
||||
if (temperature < -20000)
|
||||
phase_delay = min(max_window + 4 * max_len - 24,
|
||||
max_window +
|
||||
DIV_ROUND_UP(13 * max_len, 16) * 4);
|
||||
else if (temperature < 20000)
|
||||
phase_delay = max_window + DIV_ROUND_UP(9 * max_len, 16) * 4;
|
||||
else if (temperature < 40000)
|
||||
phase_delay = max_window + DIV_ROUND_UP(8 * max_len, 16) * 4;
|
||||
else if (temperature < 70000)
|
||||
phase_delay = max_window + DIV_ROUND_UP(7 * max_len, 16) * 4;
|
||||
else if (temperature < 90000)
|
||||
phase_delay = max_window + DIV_ROUND_UP(5 * max_len, 16) * 4;
|
||||
else if (temperature < 120000)
|
||||
phase_delay = max_window + DIV_ROUND_UP(4 * max_len, 16) * 4;
|
||||
else
|
||||
phase_delay = max_window + DIV_ROUND_UP(3 * max_len, 16) * 4;
|
||||
|
||||
/*
|
||||
* Stage 2: Search for a single point failure near the chosen tuning
|
||||
* value in two steps. First in the +3 to +10 range and then in the
|
||||
* +2 to -10 range. If found, move away from it in the appropriate
|
||||
* direction by the appropriate amount depending on the temperature.
|
||||
*/
|
||||
for (i = 3; i <= 10; i++) {
|
||||
sdhci_omap_set_dll(omap_host, phase_delay + i);
|
||||
|
||||
if (mmc_send_tuning(mmc, opcode, NULL)) {
|
||||
if (temperature < 10000)
|
||||
phase_delay += i + 6;
|
||||
else if (temperature < 20000)
|
||||
phase_delay += i - 12;
|
||||
else if (temperature < 70000)
|
||||
phase_delay += i - 8;
|
||||
else
|
||||
phase_delay += i - 6;
|
||||
|
||||
goto single_failure_found;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 2; i >= -10; i--) {
|
||||
sdhci_omap_set_dll(omap_host, phase_delay + i);
|
||||
|
||||
if (mmc_send_tuning(mmc, opcode, NULL)) {
|
||||
if (temperature < 10000)
|
||||
phase_delay += i + 12;
|
||||
else if (temperature < 20000)
|
||||
phase_delay += i + 8;
|
||||
else if (temperature < 70000)
|
||||
phase_delay += i + 8;
|
||||
else if (temperature < 90000)
|
||||
phase_delay += i + 10;
|
||||
else
|
||||
phase_delay += i + 12;
|
||||
|
||||
goto single_failure_found;
|
||||
}
|
||||
}
|
||||
|
||||
single_failure_found:
|
||||
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12);
|
||||
if (!(reg & AC12_SCLK_SEL)) {
|
||||
ret = -EIO;
|
||||
goto tuning_error;
|
||||
}
|
||||
|
||||
phase_delay = max_window + 4 * (max_len >> 1);
|
||||
sdhci_omap_set_dll(omap_host, phase_delay);
|
||||
|
||||
omap_host->is_tuning = false;
|
||||
|
||||
goto ret;
|
||||
|
||||
tuning_error:
|
||||
omap_host->is_tuning = false;
|
||||
dev_err(dev, "Tuning failed\n");
|
||||
sdhci_omap_disable_tuning(omap_host);
|
||||
|
||||
|
|
@ -653,8 +747,12 @@ static void sdhci_omap_init_74_clocks(struct sdhci_host *host, u8 power_mode)
|
|||
|
||||
/* wait 1ms */
|
||||
timeout = ktime_add_ms(ktime_get(), SDHCI_OMAP_TIMEOUT);
|
||||
while (!(sdhci_omap_readl(omap_host, SDHCI_OMAP_STAT) & INT_CC_EN)) {
|
||||
if (WARN_ON(ktime_after(ktime_get(), timeout)))
|
||||
while (1) {
|
||||
bool timedout = ktime_after(ktime_get(), timeout);
|
||||
|
||||
if (sdhci_omap_readl(omap_host, SDHCI_OMAP_STAT) & INT_CC_EN)
|
||||
break;
|
||||
if (WARN_ON(timedout))
|
||||
return;
|
||||
usleep_range(5, 10);
|
||||
}
|
||||
|
|
@ -687,6 +785,18 @@ static void sdhci_omap_set_uhs_signaling(struct sdhci_host *host,
|
|||
sdhci_omap_start_clock(omap_host);
|
||||
}
|
||||
|
||||
void sdhci_omap_reset(struct sdhci_host *host, u8 mask)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
/* Don't reset data lines during tuning operation */
|
||||
if (omap_host->is_tuning)
|
||||
mask &= ~SDHCI_RESET_DATA;
|
||||
|
||||
sdhci_reset(host, mask);
|
||||
}
|
||||
|
||||
static struct sdhci_ops sdhci_omap_ops = {
|
||||
.set_clock = sdhci_omap_set_clock,
|
||||
.set_power = sdhci_omap_set_power,
|
||||
|
|
@ -695,7 +805,7 @@ static struct sdhci_ops sdhci_omap_ops = {
|
|||
.get_min_clock = sdhci_omap_get_min_clock,
|
||||
.set_bus_width = sdhci_omap_set_bus_width,
|
||||
.platform_send_init_74_clocks = sdhci_omap_init_74_clocks,
|
||||
.reset = sdhci_reset,
|
||||
.reset = sdhci_omap_reset,
|
||||
.set_uhs_signaling = sdhci_omap_set_uhs_signaling,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -710,11 +710,15 @@ static int intel_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|||
static void byt_probe_slot(struct sdhci_pci_slot *slot)
|
||||
{
|
||||
struct mmc_host_ops *ops = &slot->host->mmc_host_ops;
|
||||
struct device *dev = &slot->chip->pdev->dev;
|
||||
struct mmc_host *mmc = slot->host->mmc;
|
||||
|
||||
byt_read_dsm(slot);
|
||||
|
||||
ops->execute_tuning = intel_execute_tuning;
|
||||
ops->start_signal_voltage_switch = intel_start_signal_voltage_switch;
|
||||
|
||||
device_property_read_u32(dev, "max-frequency", &mmc->f_max);
|
||||
}
|
||||
|
||||
static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
|
||||
|
|
@ -937,7 +941,8 @@ static int byt_sd_probe_slot(struct sdhci_pci_slot *slot)
|
|||
static const struct sdhci_pci_fixes sdhci_intel_byt_emmc = {
|
||||
.allow_runtime_pm = true,
|
||||
.probe_slot = byt_emmc_probe_slot,
|
||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
|
||||
SDHCI_QUIRK_NO_LED,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||
SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 |
|
||||
SDHCI_QUIRK2_STOP_WITH_TC,
|
||||
|
|
@ -957,7 +962,8 @@ static const struct sdhci_pci_fixes sdhci_intel_glk_emmc = {
|
|||
.runtime_suspend = glk_runtime_suspend,
|
||||
.runtime_resume = glk_runtime_resume,
|
||||
#endif
|
||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
|
||||
SDHCI_QUIRK_NO_LED,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||
SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 |
|
||||
SDHCI_QUIRK2_STOP_WITH_TC,
|
||||
|
|
@ -966,7 +972,8 @@ static const struct sdhci_pci_fixes sdhci_intel_glk_emmc = {
|
|||
};
|
||||
|
||||
static const struct sdhci_pci_fixes sdhci_ni_byt_sdio = {
|
||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
|
||||
SDHCI_QUIRK_NO_LED,
|
||||
.quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON |
|
||||
SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
||||
.allow_runtime_pm = true,
|
||||
|
|
@ -976,7 +983,8 @@ static const struct sdhci_pci_fixes sdhci_ni_byt_sdio = {
|
|||
};
|
||||
|
||||
static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = {
|
||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
|
||||
SDHCI_QUIRK_NO_LED,
|
||||
.quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON |
|
||||
SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
||||
.allow_runtime_pm = true,
|
||||
|
|
@ -986,7 +994,8 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = {
|
|||
};
|
||||
|
||||
static const struct sdhci_pci_fixes sdhci_intel_byt_sd = {
|
||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
|
||||
SDHCI_QUIRK_NO_LED,
|
||||
.quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON |
|
||||
SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||
SDHCI_QUIRK2_STOP_WITH_TC,
|
||||
|
|
|
|||
|
|
@ -357,9 +357,13 @@ static int xenon_emmc_phy_enable_dll(struct sdhci_host *host)
|
|||
|
||||
/* Wait max 32 ms */
|
||||
timeout = ktime_add_ms(ktime_get(), 32);
|
||||
while (!(sdhci_readw(host, XENON_SLOT_EXT_PRESENT_STATE) &
|
||||
XENON_DLL_LOCK_STATE)) {
|
||||
if (ktime_after(ktime_get(), timeout)) {
|
||||
while (1) {
|
||||
bool timedout = ktime_after(ktime_get(), timeout);
|
||||
|
||||
if (sdhci_readw(host, XENON_SLOT_EXT_PRESENT_STATE) &
|
||||
XENON_DLL_LOCK_STATE)
|
||||
break;
|
||||
if (timedout) {
|
||||
dev_err(mmc_dev(host->mmc), "Wait for DLL Lock time-out\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,9 +34,13 @@ static int xenon_enable_internal_clk(struct sdhci_host *host)
|
|||
sdhci_writel(host, reg, SDHCI_CLOCK_CONTROL);
|
||||
/* Wait max 20 ms */
|
||||
timeout = ktime_add_ms(ktime_get(), 20);
|
||||
while (!((reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
|
||||
& SDHCI_CLOCK_INT_STABLE)) {
|
||||
if (ktime_after(ktime_get(), timeout)) {
|
||||
while (1) {
|
||||
bool timedout = ktime_after(ktime_get(), timeout);
|
||||
|
||||
reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
||||
if (reg & SDHCI_CLOCK_INT_STABLE)
|
||||
break;
|
||||
if (timedout) {
|
||||
dev_err(mmc_dev(host->mmc), "Internal clock never stabilised.\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,8 +82,8 @@ void sdhci_dumpregs(struct sdhci_host *host)
|
|||
SDHCI_DUMP("Int enab: 0x%08x | Sig enab: 0x%08x\n",
|
||||
sdhci_readl(host, SDHCI_INT_ENABLE),
|
||||
sdhci_readl(host, SDHCI_SIGNAL_ENABLE));
|
||||
SDHCI_DUMP("AC12 err: 0x%08x | Slot int: 0x%08x\n",
|
||||
sdhci_readw(host, SDHCI_ACMD12_ERR),
|
||||
SDHCI_DUMP("ACmd stat: 0x%08x | Slot int: 0x%08x\n",
|
||||
sdhci_readw(host, SDHCI_AUTO_CMD_STATUS),
|
||||
sdhci_readw(host, SDHCI_SLOT_INT_STATUS));
|
||||
SDHCI_DUMP("Caps: 0x%08x | Caps_1: 0x%08x\n",
|
||||
sdhci_readl(host, SDHCI_CAPABILITIES),
|
||||
|
|
@ -349,6 +349,9 @@ static void __sdhci_led_activate(struct sdhci_host *host)
|
|||
{
|
||||
u8 ctrl;
|
||||
|
||||
if (host->quirks & SDHCI_QUIRK_NO_LED)
|
||||
return;
|
||||
|
||||
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
|
||||
ctrl |= SDHCI_CTRL_LED;
|
||||
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
|
||||
|
|
@ -358,6 +361,9 @@ static void __sdhci_led_deactivate(struct sdhci_host *host)
|
|||
{
|
||||
u8 ctrl;
|
||||
|
||||
if (host->quirks & SDHCI_QUIRK_NO_LED)
|
||||
return;
|
||||
|
||||
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
|
||||
ctrl &= ~SDHCI_CTRL_LED;
|
||||
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
|
||||
|
|
@ -387,6 +393,9 @@ static int sdhci_led_register(struct sdhci_host *host)
|
|||
{
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
|
||||
if (host->quirks & SDHCI_QUIRK_NO_LED)
|
||||
return 0;
|
||||
|
||||
snprintf(host->led_name, sizeof(host->led_name),
|
||||
"%s::", mmc_hostname(mmc));
|
||||
|
||||
|
|
@ -400,6 +409,9 @@ static int sdhci_led_register(struct sdhci_host *host)
|
|||
|
||||
static void sdhci_led_unregister(struct sdhci_host *host)
|
||||
{
|
||||
if (host->quirks & SDHCI_QUIRK_NO_LED)
|
||||
return;
|
||||
|
||||
led_classdev_unregister(&host->led);
|
||||
}
|
||||
|
||||
|
|
@ -933,6 +945,11 @@ static void sdhci_set_transfer_irqs(struct sdhci_host *host)
|
|||
else
|
||||
host->ier = (host->ier & ~dma_irqs) | pio_irqs;
|
||||
|
||||
if (host->flags & (SDHCI_AUTO_CMD23 | SDHCI_AUTO_CMD12))
|
||||
host->ier |= SDHCI_INT_AUTO_CMD_ERR;
|
||||
else
|
||||
host->ier &= ~SDHCI_INT_AUTO_CMD_ERR;
|
||||
|
||||
sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
|
||||
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
|
||||
}
|
||||
|
|
@ -1191,8 +1208,7 @@ static bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq)
|
|||
return (!(host->flags & SDHCI_DEVICE_DEAD) &&
|
||||
((mrq->cmd && mrq->cmd->error) ||
|
||||
(mrq->sbc && mrq->sbc->error) ||
|
||||
(mrq->data && ((mrq->data->error && !mrq->data->stop) ||
|
||||
(mrq->data->stop && mrq->data->stop->error))) ||
|
||||
(mrq->data && mrq->data->stop && mrq->data->stop->error) ||
|
||||
(host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST)));
|
||||
}
|
||||
|
||||
|
|
@ -1244,6 +1260,16 @@ static void sdhci_finish_data(struct sdhci_host *host)
|
|||
host->data = NULL;
|
||||
host->data_cmd = NULL;
|
||||
|
||||
/*
|
||||
* The controller needs a reset of internal state machines upon error
|
||||
* conditions.
|
||||
*/
|
||||
if (data->error) {
|
||||
if (!host->cmd || host->cmd == data_cmd)
|
||||
sdhci_do_reset(host, SDHCI_RESET_CMD);
|
||||
sdhci_do_reset(host, SDHCI_RESET_DATA);
|
||||
}
|
||||
|
||||
if ((host->flags & (SDHCI_REQ_USE_DMA | SDHCI_USE_ADMA)) ==
|
||||
(SDHCI_REQ_USE_DMA | SDHCI_USE_ADMA))
|
||||
sdhci_adma_table_post(host, data);
|
||||
|
|
@ -1268,17 +1294,6 @@ static void sdhci_finish_data(struct sdhci_host *host)
|
|||
if (data->stop &&
|
||||
(data->error ||
|
||||
!data->mrq->sbc)) {
|
||||
|
||||
/*
|
||||
* The controller needs a reset of internal state machines
|
||||
* upon error conditions.
|
||||
*/
|
||||
if (data->error) {
|
||||
if (!host->cmd || host->cmd == data_cmd)
|
||||
sdhci_do_reset(host, SDHCI_RESET_CMD);
|
||||
sdhci_do_reset(host, SDHCI_RESET_DATA);
|
||||
}
|
||||
|
||||
/*
|
||||
* 'cap_cmd_during_tfr' request must not use the command line
|
||||
* after mmc_command_done() has been called. It is upper layer's
|
||||
|
|
@ -2757,8 +2772,23 @@ static void sdhci_timeout_data_timer(struct timer_list *t)
|
|||
* *
|
||||
\*****************************************************************************/
|
||||
|
||||
static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
|
||||
static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *intmask_p)
|
||||
{
|
||||
/* Handle auto-CMD12 error */
|
||||
if (intmask & SDHCI_INT_AUTO_CMD_ERR && host->data_cmd) {
|
||||
struct mmc_request *mrq = host->data_cmd->mrq;
|
||||
u16 auto_cmd_status = sdhci_readw(host, SDHCI_AUTO_CMD_STATUS);
|
||||
int data_err_bit = (auto_cmd_status & SDHCI_AUTO_CMD_TIMEOUT) ?
|
||||
SDHCI_INT_DATA_TIMEOUT :
|
||||
SDHCI_INT_DATA_CRC;
|
||||
|
||||
/* Treat auto-CMD12 error the same as data error */
|
||||
if (!mrq->sbc && (host->flags & SDHCI_AUTO_CMD12)) {
|
||||
*intmask_p |= data_err_bit;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!host->cmd) {
|
||||
/*
|
||||
* SDHCI recovers from errors by resetting the cmd and data
|
||||
|
|
@ -2780,20 +2810,12 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
|
|||
else
|
||||
host->cmd->error = -EILSEQ;
|
||||
|
||||
/*
|
||||
* If this command initiates a data phase and a response
|
||||
* CRC error is signalled, the card can start transferring
|
||||
* data - the card may have received the command without
|
||||
* error. We must not terminate the mmc_request early.
|
||||
*
|
||||
* If the card did not receive the command or returned an
|
||||
* error which prevented it sending data, the data phase
|
||||
* will time out.
|
||||
*/
|
||||
/* Treat data command CRC error the same as data CRC error */
|
||||
if (host->cmd->data &&
|
||||
(intmask & (SDHCI_INT_CRC | SDHCI_INT_TIMEOUT)) ==
|
||||
SDHCI_INT_CRC) {
|
||||
host->cmd = NULL;
|
||||
*intmask_p |= SDHCI_INT_DATA_CRC;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -2801,6 +2823,21 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
|
|||
return;
|
||||
}
|
||||
|
||||
/* Handle auto-CMD23 error */
|
||||
if (intmask & SDHCI_INT_AUTO_CMD_ERR) {
|
||||
struct mmc_request *mrq = host->cmd->mrq;
|
||||
u16 auto_cmd_status = sdhci_readw(host, SDHCI_AUTO_CMD_STATUS);
|
||||
int err = (auto_cmd_status & SDHCI_AUTO_CMD_TIMEOUT) ?
|
||||
-ETIMEDOUT :
|
||||
-EILSEQ;
|
||||
|
||||
if (mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) {
|
||||
mrq->sbc->error = err;
|
||||
sdhci_finish_mrq(host, mrq);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (intmask & SDHCI_INT_RESPONSE)
|
||||
sdhci_finish_command(host);
|
||||
}
|
||||
|
|
@ -3021,7 +3058,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
|
|||
}
|
||||
|
||||
if (intmask & SDHCI_INT_CMD_MASK)
|
||||
sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK);
|
||||
sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK, &intmask);
|
||||
|
||||
if (intmask & SDHCI_INT_DATA_MASK)
|
||||
sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK);
|
||||
|
|
@ -3541,7 +3578,7 @@ void __sdhci_read_caps(struct sdhci_host *host, u16 *ver, u32 *caps, u32 *caps1)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(__sdhci_read_caps);
|
||||
|
||||
static int sdhci_allocate_bounce_buffer(struct sdhci_host *host)
|
||||
static void sdhci_allocate_bounce_buffer(struct sdhci_host *host)
|
||||
{
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
unsigned int max_blocks;
|
||||
|
|
@ -3579,7 +3616,7 @@ static int sdhci_allocate_bounce_buffer(struct sdhci_host *host)
|
|||
* Exiting with zero here makes sure we proceed with
|
||||
* mmc->max_segs == 1.
|
||||
*/
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
host->bounce_addr = dma_map_single(mmc->parent,
|
||||
|
|
@ -3589,7 +3626,7 @@ static int sdhci_allocate_bounce_buffer(struct sdhci_host *host)
|
|||
ret = dma_mapping_error(mmc->parent, host->bounce_addr);
|
||||
if (ret)
|
||||
/* Again fall back to max_segs == 1 */
|
||||
return 0;
|
||||
return;
|
||||
host->bounce_buffer_size = bounce_size;
|
||||
|
||||
/* Lie about this since we're bouncing */
|
||||
|
|
@ -3599,8 +3636,6 @@ static int sdhci_allocate_bounce_buffer(struct sdhci_host *host)
|
|||
|
||||
pr_info("%s bounce up to %u segments into one, max segment size %u bytes\n",
|
||||
mmc_hostname(mmc), max_blocks, bounce_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool sdhci_can_64bit_dma(struct sdhci_host *host)
|
||||
|
|
@ -4134,12 +4169,9 @@ int sdhci_setup_host(struct sdhci_host *host)
|
|||
*/
|
||||
mmc->max_blk_count = (host->quirks & SDHCI_QUIRK_NO_MULTIBLOCK) ? 1 : 65535;
|
||||
|
||||
if (mmc->max_segs == 1) {
|
||||
if (mmc->max_segs == 1)
|
||||
/* This may alter mmc->*_blk_* parameters */
|
||||
ret = sdhci_allocate_bounce_buffer(host);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
sdhci_allocate_bounce_buffer(host);
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -146,14 +146,15 @@
|
|||
#define SDHCI_INT_DATA_CRC 0x00200000
|
||||
#define SDHCI_INT_DATA_END_BIT 0x00400000
|
||||
#define SDHCI_INT_BUS_POWER 0x00800000
|
||||
#define SDHCI_INT_ACMD12ERR 0x01000000
|
||||
#define SDHCI_INT_AUTO_CMD_ERR 0x01000000
|
||||
#define SDHCI_INT_ADMA_ERROR 0x02000000
|
||||
|
||||
#define SDHCI_INT_NORMAL_MASK 0x00007FFF
|
||||
#define SDHCI_INT_ERROR_MASK 0xFFFF8000
|
||||
|
||||
#define SDHCI_INT_CMD_MASK (SDHCI_INT_RESPONSE | SDHCI_INT_TIMEOUT | \
|
||||
SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX)
|
||||
SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX | \
|
||||
SDHCI_INT_AUTO_CMD_ERR)
|
||||
#define SDHCI_INT_DATA_MASK (SDHCI_INT_DATA_END | SDHCI_INT_DMA_END | \
|
||||
SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \
|
||||
SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \
|
||||
|
|
@ -168,7 +169,11 @@
|
|||
|
||||
#define SDHCI_CQE_INT_MASK (SDHCI_CQE_INT_ERR_MASK | SDHCI_INT_CQE)
|
||||
|
||||
#define SDHCI_ACMD12_ERR 0x3C
|
||||
#define SDHCI_AUTO_CMD_STATUS 0x3C
|
||||
#define SDHCI_AUTO_CMD_TIMEOUT 0x00000002
|
||||
#define SDHCI_AUTO_CMD_CRC 0x00000004
|
||||
#define SDHCI_AUTO_CMD_END_BIT 0x00000008
|
||||
#define SDHCI_AUTO_CMD_INDEX 0x00000010
|
||||
|
||||
#define SDHCI_HOST_CONTROL2 0x3E
|
||||
#define SDHCI_CTRL_UHS_MASK 0x0007
|
||||
|
|
@ -403,6 +408,8 @@ struct sdhci_host {
|
|||
#define SDHCI_QUIRK_INVERTED_WRITE_PROTECT (1<<16)
|
||||
/* Controller does not like fast PIO transfers */
|
||||
#define SDHCI_QUIRK_PIO_NEEDS_DELAY (1<<18)
|
||||
/* Controller does not have a LED */
|
||||
#define SDHCI_QUIRK_NO_LED (1<<19)
|
||||
/* Controller has to be forced to use block size of 2048 bytes */
|
||||
#define SDHCI_QUIRK_FORCE_BLK_SZ_2048 (1<<20)
|
||||
/* Controller cannot do multi-block transfers */
|
||||
|
|
|
|||
374
drivers/mmc/host/sdhci_am654.c
Normal file
374
drivers/mmc/host/sdhci_am654.c
Normal file
|
|
@ -0,0 +1,374 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* sdhci_am654.c - SDHCI driver for TI's AM654 SOCs
|
||||
*
|
||||
* Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com
|
||||
*
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "sdhci-pltfm.h"
|
||||
|
||||
/* CTL_CFG Registers */
|
||||
#define CTL_CFG_2 0x14
|
||||
|
||||
#define SLOTTYPE_MASK GENMASK(31, 30)
|
||||
#define SLOTTYPE_EMBEDDED BIT(30)
|
||||
|
||||
/* PHY Registers */
|
||||
#define PHY_CTRL1 0x100
|
||||
#define PHY_CTRL2 0x104
|
||||
#define PHY_CTRL3 0x108
|
||||
#define PHY_CTRL4 0x10C
|
||||
#define PHY_CTRL5 0x110
|
||||
#define PHY_CTRL6 0x114
|
||||
#define PHY_STAT1 0x130
|
||||
#define PHY_STAT2 0x134
|
||||
|
||||
#define IOMUX_ENABLE_SHIFT 31
|
||||
#define IOMUX_ENABLE_MASK BIT(IOMUX_ENABLE_SHIFT)
|
||||
#define OTAPDLYENA_SHIFT 20
|
||||
#define OTAPDLYENA_MASK BIT(OTAPDLYENA_SHIFT)
|
||||
#define OTAPDLYSEL_SHIFT 12
|
||||
#define OTAPDLYSEL_MASK GENMASK(15, 12)
|
||||
#define STRBSEL_SHIFT 24
|
||||
#define STRBSEL_MASK GENMASK(27, 24)
|
||||
#define SEL50_SHIFT 8
|
||||
#define SEL50_MASK BIT(SEL50_SHIFT)
|
||||
#define SEL100_SHIFT 9
|
||||
#define SEL100_MASK BIT(SEL100_SHIFT)
|
||||
#define DLL_TRIM_ICP_SHIFT 4
|
||||
#define DLL_TRIM_ICP_MASK GENMASK(7, 4)
|
||||
#define DR_TY_SHIFT 20
|
||||
#define DR_TY_MASK GENMASK(22, 20)
|
||||
#define ENDLL_SHIFT 1
|
||||
#define ENDLL_MASK BIT(ENDLL_SHIFT)
|
||||
#define DLLRDY_SHIFT 0
|
||||
#define DLLRDY_MASK BIT(DLLRDY_SHIFT)
|
||||
#define PDB_SHIFT 0
|
||||
#define PDB_MASK BIT(PDB_SHIFT)
|
||||
#define CALDONE_SHIFT 1
|
||||
#define CALDONE_MASK BIT(CALDONE_SHIFT)
|
||||
#define RETRIM_SHIFT 17
|
||||
#define RETRIM_MASK BIT(RETRIM_SHIFT)
|
||||
|
||||
#define DRIVER_STRENGTH_50_OHM 0x0
|
||||
#define DRIVER_STRENGTH_33_OHM 0x1
|
||||
#define DRIVER_STRENGTH_66_OHM 0x2
|
||||
#define DRIVER_STRENGTH_100_OHM 0x3
|
||||
#define DRIVER_STRENGTH_40_OHM 0x4
|
||||
|
||||
#define CLOCK_TOO_SLOW_HZ 400000
|
||||
|
||||
static struct regmap_config sdhci_am654_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.fast_io = true,
|
||||
};
|
||||
|
||||
struct sdhci_am654_data {
|
||||
struct regmap *base;
|
||||
int otap_del_sel;
|
||||
int trm_icp;
|
||||
int drv_strength;
|
||||
bool dll_on;
|
||||
};
|
||||
|
||||
static void sdhci_am654_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host);
|
||||
int sel50, sel100;
|
||||
u32 mask, val;
|
||||
int ret;
|
||||
|
||||
if (sdhci_am654->dll_on) {
|
||||
regmap_update_bits(sdhci_am654->base, PHY_CTRL1,
|
||||
ENDLL_MASK, 0);
|
||||
|
||||
sdhci_am654->dll_on = false;
|
||||
}
|
||||
|
||||
sdhci_set_clock(host, clock);
|
||||
|
||||
if (clock > CLOCK_TOO_SLOW_HZ) {
|
||||
/* Setup DLL Output TAP delay */
|
||||
mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK;
|
||||
val = (1 << OTAPDLYENA_SHIFT) |
|
||||
(sdhci_am654->otap_del_sel << OTAPDLYSEL_SHIFT);
|
||||
regmap_update_bits(sdhci_am654->base, PHY_CTRL4,
|
||||
mask, val);
|
||||
switch (clock) {
|
||||
case 200000000:
|
||||
sel50 = 0;
|
||||
sel100 = 0;
|
||||
break;
|
||||
case 100000000:
|
||||
sel50 = 0;
|
||||
sel100 = 1;
|
||||
break;
|
||||
default:
|
||||
sel50 = 1;
|
||||
sel100 = 0;
|
||||
}
|
||||
|
||||
/* Configure PHY DLL frequency */
|
||||
mask = SEL50_MASK | SEL100_MASK;
|
||||
val = (sel50 << SEL50_SHIFT) | (sel100 << SEL100_SHIFT);
|
||||
regmap_update_bits(sdhci_am654->base, PHY_CTRL5,
|
||||
mask, val);
|
||||
/* Configure DLL TRIM */
|
||||
mask = DLL_TRIM_ICP_MASK;
|
||||
val = sdhci_am654->trm_icp << DLL_TRIM_ICP_SHIFT;
|
||||
|
||||
/* Configure DLL driver strength */
|
||||
mask |= DR_TY_MASK;
|
||||
val |= sdhci_am654->drv_strength << DR_TY_SHIFT;
|
||||
regmap_update_bits(sdhci_am654->base, PHY_CTRL1,
|
||||
mask, val);
|
||||
/* Enable DLL */
|
||||
regmap_update_bits(sdhci_am654->base, PHY_CTRL1,
|
||||
ENDLL_MASK, 0x1 << ENDLL_SHIFT);
|
||||
/*
|
||||
* Poll for DLL ready. Use a one second timeout.
|
||||
* Works in all experiments done so far
|
||||
*/
|
||||
ret = regmap_read_poll_timeout(sdhci_am654->base,
|
||||
PHY_STAT1, val,
|
||||
val & DLLRDY_MASK,
|
||||
1000, 1000000);
|
||||
|
||||
sdhci_am654->dll_on = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void sdhci_am654_set_power(struct sdhci_host *host, unsigned char mode,
|
||||
unsigned short vdd)
|
||||
{
|
||||
if (!IS_ERR(host->mmc->supply.vmmc)) {
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
|
||||
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
|
||||
}
|
||||
sdhci_set_power_noreg(host, mode, vdd);
|
||||
}
|
||||
|
||||
struct sdhci_ops sdhci_am654_ops = {
|
||||
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
.get_timeout_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
.set_power = sdhci_am654_set_power,
|
||||
.set_clock = sdhci_am654_set_clock,
|
||||
.reset = sdhci_reset,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_am654_pdata = {
|
||||
.ops = &sdhci_am654_ops,
|
||||
.quirks = SDHCI_QUIRK_INVERTED_WRITE_PROTECT |
|
||||
SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
||||
};
|
||||
|
||||
static int sdhci_am654_init(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host);
|
||||
u32 ctl_cfg_2 = 0;
|
||||
u32 mask;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
/* Reset OTAP to default value */
|
||||
mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK;
|
||||
regmap_update_bits(sdhci_am654->base, PHY_CTRL4,
|
||||
mask, 0x0);
|
||||
|
||||
regmap_read(sdhci_am654->base, PHY_STAT1, &val);
|
||||
if (~val & CALDONE_MASK) {
|
||||
/* Calibrate IO lines */
|
||||
regmap_update_bits(sdhci_am654->base, PHY_CTRL1,
|
||||
PDB_MASK, PDB_MASK);
|
||||
ret = regmap_read_poll_timeout(sdhci_am654->base, PHY_STAT1,
|
||||
val, val & CALDONE_MASK, 1, 20);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Enable pins by setting IO mux to 0 */
|
||||
regmap_update_bits(sdhci_am654->base, PHY_CTRL1,
|
||||
IOMUX_ENABLE_MASK, 0);
|
||||
|
||||
/* Set slot type based on SD or eMMC */
|
||||
if (host->mmc->caps & MMC_CAP_NONREMOVABLE)
|
||||
ctl_cfg_2 = SLOTTYPE_EMBEDDED;
|
||||
|
||||
regmap_update_bits(sdhci_am654->base, CTL_CFG_2,
|
||||
ctl_cfg_2, SLOTTYPE_MASK);
|
||||
|
||||
return sdhci_add_host(host);
|
||||
}
|
||||
|
||||
static int sdhci_am654_get_of_property(struct platform_device *pdev,
|
||||
struct sdhci_am654_data *sdhci_am654)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
int drv_strength;
|
||||
int ret;
|
||||
|
||||
ret = device_property_read_u32(dev, "ti,trm-icp",
|
||||
&sdhci_am654->trm_icp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = device_property_read_u32(dev, "ti,otap-del-sel",
|
||||
&sdhci_am654->otap_del_sel);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = device_property_read_u32(dev, "ti,driver-strength-ohm",
|
||||
&drv_strength);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (drv_strength) {
|
||||
case 50:
|
||||
sdhci_am654->drv_strength = DRIVER_STRENGTH_50_OHM;
|
||||
break;
|
||||
case 33:
|
||||
sdhci_am654->drv_strength = DRIVER_STRENGTH_33_OHM;
|
||||
break;
|
||||
case 66:
|
||||
sdhci_am654->drv_strength = DRIVER_STRENGTH_66_OHM;
|
||||
break;
|
||||
case 100:
|
||||
sdhci_am654->drv_strength = DRIVER_STRENGTH_100_OHM;
|
||||
break;
|
||||
case 40:
|
||||
sdhci_am654->drv_strength = DRIVER_STRENGTH_40_OHM;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "Invalid driver strength\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sdhci_get_of_property(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdhci_am654_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct sdhci_am654_data *sdhci_am654;
|
||||
struct sdhci_host *host;
|
||||
struct resource *res;
|
||||
struct clk *clk_xin;
|
||||
struct device *dev = &pdev->dev;
|
||||
void __iomem *base;
|
||||
int ret;
|
||||
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_am654_pdata, sizeof(*sdhci_am654));
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
sdhci_am654 = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
clk_xin = devm_clk_get(dev, "clk_xin");
|
||||
if (IS_ERR(clk_xin)) {
|
||||
dev_err(dev, "clk_xin clock not found.\n");
|
||||
ret = PTR_ERR(clk_xin);
|
||||
goto err_pltfm_free;
|
||||
}
|
||||
|
||||
pltfm_host->clk = clk_xin;
|
||||
|
||||
/* Clocks are enabled using pm_runtime */
|
||||
pm_runtime_enable(dev);
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(dev);
|
||||
goto pm_runtime_disable;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(base)) {
|
||||
ret = PTR_ERR(base);
|
||||
goto pm_runtime_put;
|
||||
}
|
||||
|
||||
sdhci_am654->base = devm_regmap_init_mmio(dev, base,
|
||||
&sdhci_am654_regmap_config);
|
||||
if (IS_ERR(sdhci_am654->base)) {
|
||||
dev_err(dev, "Failed to initialize regmap\n");
|
||||
ret = PTR_ERR(sdhci_am654->base);
|
||||
goto pm_runtime_put;
|
||||
}
|
||||
|
||||
ret = sdhci_am654_get_of_property(pdev, sdhci_am654);
|
||||
if (ret)
|
||||
goto pm_runtime_put;
|
||||
|
||||
ret = mmc_of_parse(host->mmc);
|
||||
if (ret) {
|
||||
dev_err(dev, "parsing dt failed (%d)\n", ret);
|
||||
goto pm_runtime_put;
|
||||
}
|
||||
|
||||
ret = sdhci_am654_init(host);
|
||||
if (ret)
|
||||
goto pm_runtime_put;
|
||||
|
||||
return 0;
|
||||
|
||||
pm_runtime_put:
|
||||
pm_runtime_put_sync(dev);
|
||||
pm_runtime_disable:
|
||||
pm_runtime_disable(dev);
|
||||
err_pltfm_free:
|
||||
sdhci_pltfm_free(pdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sdhci_am654_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
int ret;
|
||||
|
||||
sdhci_remove_host(host, true);
|
||||
ret = pm_runtime_put_sync(&pdev->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
sdhci_pltfm_free(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id sdhci_am654_of_match[] = {
|
||||
{ .compatible = "ti,am654-sdhci-5.1" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static struct platform_driver sdhci_am654_driver = {
|
||||
.driver = {
|
||||
.name = "sdhci-am654",
|
||||
.of_match_table = sdhci_am654_of_match,
|
||||
},
|
||||
.probe = sdhci_am654_probe,
|
||||
.remove = sdhci_am654_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(sdhci_am654_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Driver for SDHCI Controller on TI's AM654 devices");
|
||||
MODULE_AUTHOR("Faiz Abbas <[email protected]>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -70,6 +70,7 @@
|
|||
#define TMIO_STAT_DAT0 BIT(23) /* only known on R-Car so far */
|
||||
#define TMIO_STAT_RXRDY BIT(24)
|
||||
#define TMIO_STAT_TXRQ BIT(25)
|
||||
#define TMIO_STAT_ALWAYS_SET_27 BIT(27) /* only known on R-Car 2+ so far */
|
||||
#define TMIO_STAT_ILL_FUNC BIT(29) /* only when !TMIO_MMC_HAS_IDLE_WAIT */
|
||||
#define TMIO_STAT_SCLKDIVEN BIT(29) /* only when TMIO_MMC_HAS_IDLE_WAIT */
|
||||
#define TMIO_STAT_CMD_BUSY BIT(30)
|
||||
|
|
@ -96,6 +97,7 @@
|
|||
|
||||
/* Define some IRQ masks */
|
||||
/* This is the mask used at reset by the chip */
|
||||
#define TMIO_MASK_INIT_RCAR2 0x8b7f031d /* Initial value for R-Car Gen2+ */
|
||||
#define TMIO_MASK_ALL 0x837f031d
|
||||
#define TMIO_MASK_READOP (TMIO_STAT_RXRDY | TMIO_STAT_DATAEND)
|
||||
#define TMIO_MASK_WRITEOP (TMIO_STAT_TXRQ | TMIO_STAT_DATAEND)
|
||||
|
|
@ -153,6 +155,7 @@ struct tmio_mmc_host {
|
|||
u32 sdcard_irq_mask;
|
||||
u32 sdio_irq_mask;
|
||||
unsigned int clk_cache;
|
||||
u32 sdcard_irq_setbit_mask;
|
||||
|
||||
spinlock_t lock; /* protect host private data */
|
||||
unsigned long last_req_ts;
|
||||
|
|
@ -267,6 +270,9 @@ static inline void sd_ctrl_write16_rep(struct tmio_mmc_host *host, int addr,
|
|||
static inline void sd_ctrl_write32_as_16_and_16(struct tmio_mmc_host *host,
|
||||
int addr, u32 val)
|
||||
{
|
||||
if (addr == CTL_IRQ_MASK || addr == CTL_STATUS)
|
||||
val |= host->sdcard_irq_setbit_mask;
|
||||
|
||||
iowrite16(val & 0xffff, host->ctl + (addr << host->bus_shift));
|
||||
iowrite16(val >> 16, host->ctl + ((addr + 2) << host->bus_shift));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -171,6 +171,18 @@ static void tmio_mmc_reset(struct tmio_mmc_host *host)
|
|||
}
|
||||
}
|
||||
|
||||
static void tmio_mmc_hw_reset(struct mmc_host *mmc)
|
||||
{
|
||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||
|
||||
host->reset(host);
|
||||
|
||||
tmio_mmc_abort_dma(host);
|
||||
|
||||
if (host->hw_reset)
|
||||
host->hw_reset(host);
|
||||
}
|
||||
|
||||
static void tmio_mmc_reset_work(struct work_struct *work)
|
||||
{
|
||||
struct tmio_mmc_host *host = container_of(work, struct tmio_mmc_host,
|
||||
|
|
@ -209,12 +221,11 @@ static void tmio_mmc_reset_work(struct work_struct *work)
|
|||
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
host->reset(host);
|
||||
tmio_mmc_hw_reset(host->mmc);
|
||||
|
||||
/* Ready for new calls */
|
||||
host->mrq = NULL;
|
||||
|
||||
tmio_mmc_abort_dma(host);
|
||||
mmc_request_done(host->mmc, mrq);
|
||||
}
|
||||
|
||||
|
|
@ -696,14 +707,6 @@ static int tmio_mmc_start_data(struct tmio_mmc_host *host,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void tmio_mmc_hw_reset(struct mmc_host *mmc)
|
||||
{
|
||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||
|
||||
if (host->hw_reset)
|
||||
host->hw_reset(host);
|
||||
}
|
||||
|
||||
static int tmio_mmc_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
||||
{
|
||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||
|
|
@ -734,8 +737,6 @@ static int tmio_mmc_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|||
ret = mmc_send_tuning(mmc, opcode, NULL);
|
||||
if (ret == 0)
|
||||
set_bit(i, host->taps);
|
||||
|
||||
usleep_range(1000, 1200);
|
||||
}
|
||||
|
||||
ret = host->select_tuning(host);
|
||||
|
|
@ -1167,11 +1168,13 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (pdata->flags & TMIO_MMC_USE_GPIO_CD) {
|
||||
ret = mmc_gpio_request_cd(mmc, pdata->cd_gpio, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
/*
|
||||
* Look for a card detect GPIO, if it fails with anything
|
||||
* else than a probe deferral, just live without it.
|
||||
*/
|
||||
ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0, NULL);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
return ret;
|
||||
|
||||
mmc->caps |= MMC_CAP_4_BIT_DATA | pdata->capabilities;
|
||||
mmc->caps2 |= pdata->capabilities2;
|
||||
|
|
@ -1228,7 +1231,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
|
|||
_host->sdio_irq_mask = TMIO_SDIO_MASK_ALL;
|
||||
|
||||
_host->set_clock(_host, 0);
|
||||
_host->reset(_host);
|
||||
tmio_mmc_hw_reset(mmc);
|
||||
|
||||
_host->sdcard_irq_mask = sd_ctrl_read16_and_16_as_32(_host, CTL_IRQ_MASK);
|
||||
tmio_mmc_disable_mmc_irqs(_host, TMIO_MASK_ALL);
|
||||
|
|
@ -1328,8 +1331,8 @@ int tmio_mmc_host_runtime_resume(struct device *dev)
|
|||
{
|
||||
struct tmio_mmc_host *host = dev_get_drvdata(dev);
|
||||
|
||||
host->reset(host);
|
||||
tmio_mmc_clk_enable(host);
|
||||
tmio_mmc_hw_reset(host->mmc);
|
||||
|
||||
if (host->clk_cache)
|
||||
host->set_clock(host, host->clk_cache);
|
||||
|
|
|
|||
Loading…
Reference in a new issue