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:
Linus Torvalds 2018-12-28 16:52:18 -08:00
commit 00d59fde85
117 changed files with 4010 additions and 1097 deletions

View file

@ -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)

View file

@ -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)

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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.

View file

@ -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);

View file

@ -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;

View file

@ -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,

View file

@ -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.

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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);
}

View file

@ -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);

View file

@ -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 = {

View file

@ -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);

View file

@ -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[] = {

View file

@ -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;

View file

@ -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),

View file

@ -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);
}
}
}

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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)
{

View file

@ -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");
}

View file

@ -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 */

View file

@ -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 */ }
};

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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,

View file

@ -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>

View file

@ -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);
}

View file

@ -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

View file

@ -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,

View file

@ -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",

View file

@ -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);

View file

@ -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,
};

View file

@ -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,

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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 */

View 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");

View file

@ -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));
}

View file

@ -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);