mirror of
https://github.com/torvalds/linux.git
synced 2024-11-21 19:46:16 +00:00
MMC core:
- Increase the timeout period of the ACMD41 command - Add card entry for quirks to debugfs - Add mmc_gpiod_set_cd_config() function - Store owner from SDIO modules with sdio_register_driver() MMC host: - atmel-mci: Some cleanups and a switch to use dev_err_probe() - renesas_sdhi: Add support for RZ/G2L, RZ/G3S and RZ/V2M variants - renesas_sdhi: Set the SDBUF after reset - sdhci: Add support for "Tuning Error" interrupts - sdhci-acpi: Add quirk to enable pull-up on the card-detect GPIO on Asus T100TA - sdhci-acpi: Disable write protect detection on Toshiba WT10-A - sdhci-acpi: Fix Lenovo Yoga Tablet 2 Pro 1380 sdcard slot not working - sdhci_am654: Re-work and fix the tuning support for multiple speed-modes - sdhci_am654: Add tuning algorithm for delay chain - sdhci-esdhc-imx: Add NXP S32G3 support - sdhci-of-dwcmshc: Add tuning support for Sophgo CV1800B and SG200X - sdhci-of-dwcmshc: Implement SDHCI CQE support - sdhci-pci-gli: Use the proper pci_set_power_state() instead of PMCSR writes MEMSTICK: - Convert a couple of drivers to use the ->remove_new() callback -----BEGIN PGP SIGNATURE----- iQJLBAABCgA1FiEEugLDXPmKSktSkQsV/iaEJXNYjCkFAmZGI0UXHHVsZi5oYW5z c29uQGxpbmFyby5vcmcACgkQ/iaEJXNYjCk0Bw//cSgatLPu9ypQv8N2pIL9FTBR 0Y7cwoQmEp35WArHUl+ouqN9It8FHKUB55jmBTv1STd9Rf+VMKlCCRODYjVZwcTV cQRlIx6iAwvVyNr8JY1V9nfpgYnHCtd1uJJXqBm1d3p7cU3RQl6Bnn/0WxOgcCxT jUwR4VeMj6m+g9q9vck5+PFVZ4plznygzcJ6mIBtWSiQj8/XpYS1TaoYX4bxCfB7 vESICzp4+VpK8SbJzxtfcLL+SGU2HIpHhFOW7vsI2pY+Tf4wu2QDyB/6od5RxfDY xC8mCRFp+WpG5Txvb0kbbf9munFCTl+xiXemqIlCZ4gsmjPNhtPhp2xeJhL1e9yI s9C20SCABgS0AbV0AmS+vQNe9cEL1/rFOjaNZi1scseVTSxsDr7YHTp3bPs8C/rs JZgjmxBmnQfmSwKjRuv9Rj77L3VD4RcMzrc0SQ0PjXEivtf/m636AVlbiytCRrTo BlqoIOoWBXUkoKiFCJn5ne63jGRRjYv5BhZaVmeaYVvbooQhaFpzBRkozoExm8Mk ZiY2HdUY5uDdwWVPyldP/f9PHIJAAKdzbPf0K6sQBDJgzzH5OMT39SdxDQ9rAAUl RO03A2Ntkx9eFZOo/4TjShI1rCKOVXytoB3sjONSCBFg+teYUdpmDvbZXtAcqJWV Sowz+pVjVkiefXBLGgg= =I2JP -----END PGP SIGNATURE----- Merge tag 'mmc-v6.10' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc Pull MMC updates from Ulf Hansson: "MMC core: - Increase the timeout period of the ACMD41 command - Add card entry for quirks to debugfs - Add mmc_gpiod_set_cd_config() function - Store owner from SDIO modules with sdio_register_driver() MMC host: - atmel-mci: Some cleanups and a switch to use dev_err_probe() - renesas_sdhi: - Add support for RZ/G2L, RZ/G3S and RZ/V2M variants - Set the SDBUF after reset - sdhci: Add support for "Tuning Error" interrupts - sdhci-acpi: - Add quirk to enable pull-up on the card-detect GPIO on Asus T100TA - Disable write protect detection on Toshiba WT10-A - Fix Lenovo Yoga Tablet 2 Pro 1380 sdcard slot not working - sdhci_am654: - Re-work and fix the tuning support for multiple speed-modes - Add tuning algorithm for delay chain - sdhci-esdhc-imx: Add NXP S32G3 support - sdhci-of-dwcmshc: - Add tuning support for Sophgo CV1800B and SG200X - Implement SDHCI CQE support - sdhci-pci-gli: Use the proper pci_set_power_state() instead of PMCSR writes" MEMSTICK: - Convert a couple of drivers to use the ->remove_new() callback" * tag 'mmc-v6.10' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (59 commits) mmc: renesas_sdhi: Add compatible string for RZ/G2L family, RZ/G3S, and RZ/V2M SoCs dt-bindings: mmc: renesas,sdhi: Document RZ/G2L family compatibility dt-bindings: mmc: renesas,sdhi: Group single const value items into an enum list mmc: renesas_sdhi: Set the SDBUF after reset mmc: core: Increase the timeout period of the ACMD41 command mmc: core: Convert to use __mmc_poll_for_busy() SD_APP_OP_COND too mmc: atmel-mci: Switch to use dev_err_probe() mmc: atmel-mci: Incapsulate used to be a platform data into host structure mmc: atmel-mci: Replace platform device pointer by generic one mmc: atmel-mci: Use temporary variable for struct device mmc: atmel-mci: Get rid of platform data leftovers mmc: sdhci-of-dwcmshc: Add tuning support for Sophgo CV1800B and SG200X mmc: sdhci-of-dwcmshc: Remove useless "&" of th1520_execute_tuning mmc: sdhci-s3c: Choose sdhci_ops based on variant mmc: sdhci_am654: Constify struct sdhci_ops mmc: sdhci-sprd: Constify struct sdhci_ops mmc: sdhci-omap: Constify struct sdhci_ops mmc: sdhci-esdhc-mcf: Constify struct sdhci_ops mmc: slot-gpio: Use irq_handler_t type mmc: sdhci-acpi: Add quirk to enable pull-up on the card-detect GPIO on Asus T100TA ...
This commit is contained in:
commit
8b06f7538a
@ -91,6 +91,9 @@ properties:
|
||||
- enum:
|
||||
- fsl,imxrt1170-usdhc
|
||||
- const: fsl,imxrt1050-usdhc
|
||||
- items:
|
||||
- const: nxp,s32g3-usdhc
|
||||
- const: nxp,s32g2-usdhc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
@ -12,16 +12,13 @@ maintainers:
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- items:
|
||||
- const: renesas,sdhi-sh73a0 # R-Mobile APE6
|
||||
- items:
|
||||
- const: renesas,sdhi-r7s72100 # RZ/A1H
|
||||
- items:
|
||||
- const: renesas,sdhi-r7s9210 # SH-Mobile AG5
|
||||
- items:
|
||||
- const: renesas,sdhi-r8a73a4 # R-Mobile APE6
|
||||
- items:
|
||||
- const: renesas,sdhi-r8a7740 # R-Mobile A1
|
||||
- enum:
|
||||
- renesas,sdhi-mmc-r8a77470 # RZ/G1C
|
||||
- renesas,sdhi-r7s72100 # RZ/A1H
|
||||
- renesas,sdhi-r7s9210 # SH-Mobile AG5
|
||||
- renesas,sdhi-r8a73a4 # R-Mobile APE6
|
||||
- renesas,sdhi-r8a7740 # R-Mobile A1
|
||||
- renesas,sdhi-sh73a0 # R-Mobile APE6
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,sdhi-r8a7778 # R-Car M1
|
||||
@ -40,8 +37,6 @@ properties:
|
||||
- renesas,sdhi-r8a7793 # R-Car M2-N
|
||||
- renesas,sdhi-r8a7794 # R-Car E2
|
||||
- const: renesas,rcar-gen2-sdhi # R-Car Gen2 and RZ/G1
|
||||
- items:
|
||||
- const: renesas,sdhi-mmc-r8a77470 # RZ/G1C (SDHI/MMC IP)
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,sdhi-r8a774a1 # RZ/G2M
|
||||
@ -56,11 +51,6 @@ properties:
|
||||
- renesas,sdhi-r8a77980 # R-Car V3H
|
||||
- renesas,sdhi-r8a77990 # R-Car E3
|
||||
- renesas,sdhi-r8a77995 # R-Car D3
|
||||
- renesas,sdhi-r9a07g043 # RZ/G2UL and RZ/Five
|
||||
- renesas,sdhi-r9a07g044 # RZ/G2{L,LC}
|
||||
- renesas,sdhi-r9a07g054 # RZ/V2L
|
||||
- renesas,sdhi-r9a08g045 # RZ/G3S
|
||||
- renesas,sdhi-r9a09g011 # RZ/V2M
|
||||
- const: renesas,rcar-gen3-sdhi # R-Car Gen3 or RZ/G2
|
||||
- items:
|
||||
- enum:
|
||||
@ -69,6 +59,14 @@ properties:
|
||||
- renesas,sdhi-r8a779g0 # R-Car V4H
|
||||
- renesas,sdhi-r8a779h0 # R-Car V4M
|
||||
- const: renesas,rcar-gen4-sdhi # R-Car Gen4
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,sdhi-r9a07g043 # RZ/G2UL and RZ/Five
|
||||
- renesas,sdhi-r9a07g044 # RZ/G2{L,LC}
|
||||
- renesas,sdhi-r9a07g054 # RZ/V2L
|
||||
- renesas,sdhi-r9a08g045 # RZ/G3S
|
||||
- renesas,sdhi-r9a09g011 # RZ/V2M
|
||||
- const: renesas,rzg2l-sdhi
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
@ -120,12 +118,7 @@ allOf:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- renesas,sdhi-r9a07g043
|
||||
- renesas,sdhi-r9a07g044
|
||||
- renesas,sdhi-r9a07g054
|
||||
- renesas,sdhi-r9a08g045
|
||||
- renesas,sdhi-r9a09g011
|
||||
const: renesas,rzg2l-sdhi
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
|
@ -8616,7 +8616,7 @@ F: Documentation/devicetree/bindings/crypto/fsl,sec-v4.0*
|
||||
F: drivers/crypto/caam/
|
||||
|
||||
FREESCALE COLDFIRE M5441X MMC DRIVER
|
||||
M: Angelo Dureghello <angelo.dureghello@timesys.com>
|
||||
M: Angelo Dureghello <adureghello@baylibre.com>
|
||||
L: linux-mmc@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/mmc/host/sdhci-esdhc-mcf.c
|
||||
|
@ -1736,7 +1736,6 @@ static struct sdio_driver bt_mrvl_sdio = {
|
||||
.probe = btmrvl_sdio_probe,
|
||||
.remove = btmrvl_sdio_remove,
|
||||
.drv = {
|
||||
.owner = THIS_MODULE,
|
||||
.coredump = btmrvl_sdio_coredump,
|
||||
.pm = &btmrvl_sdio_pm_ops,
|
||||
}
|
||||
|
@ -1519,7 +1519,6 @@ static struct sdio_driver btmtksdio_driver = {
|
||||
.remove = btmtksdio_remove,
|
||||
.id_table = btmtksdio_table,
|
||||
.drv = {
|
||||
.owner = THIS_MODULE,
|
||||
.pm = BTMTKSDIO_PM_OPS,
|
||||
}
|
||||
};
|
||||
|
@ -574,16 +574,13 @@ static int rtsx_pci_ms_drv_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtsx_pci_ms_drv_remove(struct platform_device *pdev)
|
||||
static void rtsx_pci_ms_drv_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct realtek_pci_ms *host = platform_get_drvdata(pdev);
|
||||
struct rtsx_pcr *pcr;
|
||||
struct memstick_host *msh;
|
||||
int rc;
|
||||
|
||||
if (!host)
|
||||
return 0;
|
||||
|
||||
pcr = host->pcr;
|
||||
pcr->slots[RTSX_MS_CARD].p_dev = NULL;
|
||||
pcr->slots[RTSX_MS_CARD].card_event = NULL;
|
||||
@ -613,8 +610,6 @@ static int rtsx_pci_ms_drv_remove(struct platform_device *pdev)
|
||||
|
||||
dev_dbg(&(pdev->dev),
|
||||
": Realtek PCI-E Memstick controller has been removed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_device_id rtsx_pci_ms_ids[] = {
|
||||
@ -628,7 +623,7 @@ MODULE_DEVICE_TABLE(platform, rtsx_pci_ms_ids);
|
||||
|
||||
static struct platform_driver rtsx_pci_ms_driver = {
|
||||
.probe = rtsx_pci_ms_drv_probe,
|
||||
.remove = rtsx_pci_ms_drv_remove,
|
||||
.remove_new = rtsx_pci_ms_drv_remove,
|
||||
.id_table = rtsx_pci_ms_ids,
|
||||
.suspend = rtsx_pci_ms_suspend,
|
||||
.resume = rtsx_pci_ms_resume,
|
||||
|
@ -805,7 +805,7 @@ static int rtsx_usb_ms_drv_probe(struct platform_device *pdev)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int rtsx_usb_ms_drv_remove(struct platform_device *pdev)
|
||||
static void rtsx_usb_ms_drv_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rtsx_usb_ms *host = platform_get_drvdata(pdev);
|
||||
struct memstick_host *msh = host->msh;
|
||||
@ -840,8 +840,6 @@ static int rtsx_usb_ms_drv_remove(struct platform_device *pdev)
|
||||
": Realtek USB Memstick controller has been removed\n");
|
||||
memstick_free_host(msh);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_device_id rtsx_usb_ms_ids[] = {
|
||||
@ -855,7 +853,7 @@ MODULE_DEVICE_TABLE(platform, rtsx_usb_ms_ids);
|
||||
|
||||
static struct platform_driver rtsx_usb_ms_driver = {
|
||||
.probe = rtsx_usb_ms_drv_probe,
|
||||
.remove = rtsx_usb_ms_drv_remove,
|
||||
.remove_new = rtsx_usb_ms_drv_remove,
|
||||
.id_table = rtsx_usb_ms_ids,
|
||||
.driver = {
|
||||
.name = "rtsx_usb_ms",
|
||||
|
@ -234,7 +234,7 @@ static ssize_t power_ro_lock_show(struct device *dev,
|
||||
else if (card->ext_csd.boot_ro_lock & EXT_CSD_BOOT_WP_B_PWR_WP_EN)
|
||||
locked = 1;
|
||||
|
||||
ret = snprintf(buf, PAGE_SIZE, "%d\n", locked);
|
||||
ret = sysfs_emit(buf, "%d\n", locked);
|
||||
|
||||
mmc_blk_put(md);
|
||||
|
||||
@ -296,9 +296,9 @@ static ssize_t force_ro_show(struct device *dev, struct device_attribute *attr,
|
||||
int ret;
|
||||
struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
|
||||
|
||||
ret = snprintf(buf, PAGE_SIZE, "%d\n",
|
||||
get_disk_ro(dev_to_disk(dev)) ^
|
||||
md->read_only);
|
||||
ret = sysfs_emit(buf, "%d\n",
|
||||
get_disk_ro(dev_to_disk(dev)) ^
|
||||
md->read_only);
|
||||
mmc_blk_put(md);
|
||||
return ret;
|
||||
}
|
||||
|
@ -351,11 +351,11 @@ void mmc_add_host_debugfs(struct mmc_host *host)
|
||||
root = debugfs_create_dir(mmc_hostname(host), NULL);
|
||||
host->debugfs_root = root;
|
||||
|
||||
debugfs_create_file("ios", S_IRUSR, root, host, &mmc_ios_fops);
|
||||
debugfs_create_file("ios", 0400, root, host, &mmc_ios_fops);
|
||||
debugfs_create_file("caps", 0600, root, &host->caps, &mmc_caps_fops);
|
||||
debugfs_create_file("caps2", 0600, root, &host->caps2,
|
||||
&mmc_caps2_fops);
|
||||
debugfs_create_file_unsafe("clock", S_IRUSR | S_IWUSR, root, host,
|
||||
debugfs_create_file_unsafe("clock", 0600, root, host,
|
||||
&mmc_clock_fops);
|
||||
|
||||
debugfs_create_file_unsafe("err_state", 0600, root, host,
|
||||
@ -388,7 +388,8 @@ void mmc_add_card_debugfs(struct mmc_card *card)
|
||||
root = debugfs_create_dir(mmc_card_id(card), host->debugfs_root);
|
||||
card->debugfs_root = root;
|
||||
|
||||
debugfs_create_x32("state", S_IRUSR, root, &card->state);
|
||||
debugfs_create_x32("state", 0400, root, &card->state);
|
||||
debugfs_create_x32("quirks", 0400, root, &card->quirks);
|
||||
}
|
||||
|
||||
void mmc_remove_card_debugfs(struct mmc_card *card)
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/pm_wakeup.h>
|
||||
#include <linux/export.h>
|
||||
|
@ -19,6 +19,20 @@
|
||||
#include "sd_ops.h"
|
||||
#include "mmc_ops.h"
|
||||
|
||||
/*
|
||||
* Extensive testing has shown that some specific SD cards
|
||||
* require an increased command timeout to be successfully
|
||||
* initialized.
|
||||
*/
|
||||
#define SD_APP_OP_COND_PERIOD_US (10 * 1000) /* 10ms */
|
||||
#define SD_APP_OP_COND_TIMEOUT_MS 2000 /* 2s */
|
||||
|
||||
struct sd_app_op_cond_busy_data {
|
||||
struct mmc_host *host;
|
||||
u32 ocr;
|
||||
struct mmc_command *cmd;
|
||||
};
|
||||
|
||||
int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
|
||||
{
|
||||
int err;
|
||||
@ -115,10 +129,45 @@ int mmc_app_set_bus_width(struct mmc_card *card, int width)
|
||||
return mmc_wait_for_app_cmd(card->host, card, &cmd);
|
||||
}
|
||||
|
||||
static int sd_app_op_cond_cb(void *cb_data, bool *busy)
|
||||
{
|
||||
struct sd_app_op_cond_busy_data *data = cb_data;
|
||||
struct mmc_host *host = data->host;
|
||||
struct mmc_command *cmd = data->cmd;
|
||||
u32 ocr = data->ocr;
|
||||
int err;
|
||||
|
||||
*busy = false;
|
||||
|
||||
err = mmc_wait_for_app_cmd(host, NULL, cmd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* If we're just probing, do a single pass. */
|
||||
if (ocr == 0)
|
||||
return 0;
|
||||
|
||||
/* Wait until reset completes. */
|
||||
if (mmc_host_is_spi(host)) {
|
||||
if (!(cmd->resp[0] & R1_SPI_IDLE))
|
||||
return 0;
|
||||
} else if (cmd->resp[0] & MMC_CARD_BUSY) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*busy = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
|
||||
{
|
||||
struct mmc_command cmd = {};
|
||||
int i, err = 0;
|
||||
struct sd_app_op_cond_busy_data cb_data = {
|
||||
.host = host,
|
||||
.ocr = ocr,
|
||||
.cmd = &cmd
|
||||
};
|
||||
int err;
|
||||
|
||||
cmd.opcode = SD_APP_OP_COND;
|
||||
if (mmc_host_is_spi(host))
|
||||
@ -127,36 +176,16 @@ int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
|
||||
cmd.arg = ocr;
|
||||
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;
|
||||
|
||||
for (i = 100; i; i--) {
|
||||
err = mmc_wait_for_app_cmd(host, NULL, &cmd);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
/* if we're just probing, do a single pass */
|
||||
if (ocr == 0)
|
||||
break;
|
||||
|
||||
/* otherwise wait until reset completes */
|
||||
if (mmc_host_is_spi(host)) {
|
||||
if (!(cmd.resp[0] & R1_SPI_IDLE))
|
||||
break;
|
||||
} else {
|
||||
if (cmd.resp[0] & MMC_CARD_BUSY)
|
||||
break;
|
||||
}
|
||||
|
||||
err = -ETIMEDOUT;
|
||||
|
||||
mmc_delay(10);
|
||||
}
|
||||
|
||||
if (!i)
|
||||
pr_err("%s: card never left busy state\n", mmc_hostname(host));
|
||||
err = __mmc_poll_for_busy(host, SD_APP_OP_COND_PERIOD_US,
|
||||
SD_APP_OP_COND_TIMEOUT_MS, &sd_app_op_cond_cb,
|
||||
&cb_data);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (rocr && !mmc_host_is_spi(host))
|
||||
*rocr = cmd.resp[0];
|
||||
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __mmc_send_if_cond(struct mmc_host *host, u32 ocr, u8 pcie_bits,
|
||||
|
@ -265,16 +265,19 @@ void sdio_unregister_bus(void)
|
||||
}
|
||||
|
||||
/**
|
||||
* sdio_register_driver - register a function driver
|
||||
* __sdio_register_driver - register a function driver
|
||||
* @drv: SDIO function driver
|
||||
* @owner: owning module/driver
|
||||
*/
|
||||
int sdio_register_driver(struct sdio_driver *drv)
|
||||
int __sdio_register_driver(struct sdio_driver *drv, struct module *owner)
|
||||
{
|
||||
drv->drv.name = drv->name;
|
||||
drv->drv.bus = &sdio_bus_type;
|
||||
drv->drv.owner = owner;
|
||||
|
||||
return driver_register(&drv->drv);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sdio_register_driver);
|
||||
EXPORT_SYMBOL_GPL(__sdio_register_driver);
|
||||
|
||||
/**
|
||||
* sdio_unregister_driver - unregister a function driver
|
||||
|
@ -19,7 +19,7 @@
|
||||
struct mmc_gpio {
|
||||
struct gpio_desc *ro_gpio;
|
||||
struct gpio_desc *cd_gpio;
|
||||
irqreturn_t (*cd_gpio_isr)(int irq, void *dev_id);
|
||||
irq_handler_t cd_gpio_isr;
|
||||
char *ro_label;
|
||||
char *cd_label;
|
||||
u32 cd_debounce_delay_ms;
|
||||
@ -162,8 +162,7 @@ EXPORT_SYMBOL(mmc_gpio_set_cd_wake);
|
||||
/* Register an alternate interrupt service routine for
|
||||
* the card-detect GPIO.
|
||||
*/
|
||||
void mmc_gpio_set_cd_isr(struct mmc_host *host,
|
||||
irqreturn_t (*isr)(int irq, void *dev_id))
|
||||
void mmc_gpio_set_cd_isr(struct mmc_host *host, irq_handler_t isr)
|
||||
{
|
||||
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||
|
||||
@ -221,6 +220,26 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_gpiod_request_cd);
|
||||
|
||||
/**
|
||||
* mmc_gpiod_set_cd_config - set config for card-detection GPIO
|
||||
* @host: mmc host
|
||||
* @config: Generic pinconf config (from pinconf_to_config_packed())
|
||||
*
|
||||
* This can be used by mmc host drivers to fixup a card-detection GPIO's config
|
||||
* (e.g. set PIN_CONFIG_BIAS_PULL_UP) after acquiring the GPIO descriptor
|
||||
* through mmc_gpiod_request_cd().
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success, or a negative errno value on error.
|
||||
*/
|
||||
int mmc_gpiod_set_cd_config(struct mmc_host *host, unsigned long config)
|
||||
{
|
||||
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||
|
||||
return gpiod_set_config(ctx->cd_gpio, config);
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_gpiod_set_cd_config);
|
||||
|
||||
bool mmc_can_gpio_cd(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||
|
@ -233,6 +233,7 @@ config MMC_SDHCI_OF_DWCMSHC
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
depends on OF
|
||||
depends on COMMON_CLK
|
||||
select MMC_CQHCI
|
||||
help
|
||||
This selects Synopsys DesignWare Cores Mobile Storage Controller
|
||||
support.
|
||||
|
@ -224,18 +224,6 @@ struct mci_slot_pdata {
|
||||
bool non_removable;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mci_platform_data - board-specific MMC/SDcard configuration
|
||||
* @dma_slave: DMA slave interface to use in data transfers.
|
||||
* @dma_filter: Filtering function to filter the DMA channel
|
||||
* @slot: Per-slot configuration data.
|
||||
*/
|
||||
struct mci_platform_data {
|
||||
void *dma_slave;
|
||||
dma_filter_fn dma_filter;
|
||||
struct mci_slot_pdata slot[ATMCI_MAX_NR_SLOTS];
|
||||
};
|
||||
|
||||
struct atmel_mci_caps {
|
||||
bool has_dma_conf_reg;
|
||||
bool has_pdc;
|
||||
@ -300,7 +288,8 @@ struct atmel_mci_dma {
|
||||
* rate and timeout calculations.
|
||||
* @mapbase: Physical address of the MMIO registers.
|
||||
* @mck: The peripheral bus clock hooked up to the MMC controller.
|
||||
* @pdev: Platform device associated with the MMC controller.
|
||||
* @dev: Device associated with the MMC controller.
|
||||
* @pdata: Per-slot configuration data.
|
||||
* @slot: Slots sharing this MMC controller.
|
||||
* @caps: MCI capabilities depending on MCI version.
|
||||
* @prepare_data: function to setup MCI before data transfer which
|
||||
@ -377,8 +366,9 @@ struct atmel_mci {
|
||||
unsigned long bus_hz;
|
||||
unsigned long mapbase;
|
||||
struct clk *mck;
|
||||
struct platform_device *pdev;
|
||||
struct device *dev;
|
||||
|
||||
struct mci_slot_pdata pdata[ATMCI_MAX_NR_SLOTS];
|
||||
struct atmel_mci_slot *slot[ATMCI_MAX_NR_SLOTS];
|
||||
|
||||
struct atmel_mci_caps caps;
|
||||
@ -530,6 +520,7 @@ static void atmci_show_status_reg(struct seq_file *s,
|
||||
static int atmci_regs_show(struct seq_file *s, void *v)
|
||||
{
|
||||
struct atmel_mci *host = s->private;
|
||||
struct device *dev = host->dev;
|
||||
u32 *buf;
|
||||
int ret = 0;
|
||||
|
||||
@ -538,7 +529,7 @@ static int atmci_regs_show(struct seq_file *s, void *v)
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
pm_runtime_get_sync(&host->pdev->dev);
|
||||
pm_runtime_get_sync(dev);
|
||||
|
||||
/*
|
||||
* Grab a more or less consistent snapshot. Note that we're
|
||||
@ -549,8 +540,8 @@ static int atmci_regs_show(struct seq_file *s, void *v)
|
||||
memcpy_fromio(buf, host->regs, ATMCI_REGS_SIZE);
|
||||
spin_unlock_bh(&host->lock);
|
||||
|
||||
pm_runtime_mark_last_busy(&host->pdev->dev);
|
||||
pm_runtime_put_autosuspend(&host->pdev->dev);
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
||||
seq_printf(s, "MR:\t0x%08x%s%s ",
|
||||
buf[ATMCI_MR / 4],
|
||||
@ -626,7 +617,6 @@ static void atmci_init_debugfs(struct atmel_mci_slot *slot)
|
||||
&host->completed_events);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
static const struct of_device_id atmci_dt_ids[] = {
|
||||
{ .compatible = "atmel,hsmci" },
|
||||
{ /* sentinel */ }
|
||||
@ -634,79 +624,64 @@ static const struct of_device_id atmci_dt_ids[] = {
|
||||
|
||||
MODULE_DEVICE_TABLE(of, atmci_dt_ids);
|
||||
|
||||
static struct mci_platform_data*
|
||||
atmci_of_init(struct platform_device *pdev)
|
||||
static int atmci_of_init(struct atmel_mci *host)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device *dev = host->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct device_node *cnp;
|
||||
struct mci_platform_data *pdata;
|
||||
u32 slot_id;
|
||||
int err;
|
||||
|
||||
if (!np) {
|
||||
dev_err(&pdev->dev, "device node not found\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
if (!np)
|
||||
return dev_err_probe(dev, -EINVAL, "device node not found\n");
|
||||
|
||||
for_each_child_of_node(np, cnp) {
|
||||
if (of_property_read_u32(cnp, "reg", &slot_id)) {
|
||||
dev_warn(&pdev->dev, "reg property is missing for %pOF\n",
|
||||
cnp);
|
||||
dev_warn(dev, "reg property is missing for %pOF\n", cnp);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (slot_id >= ATMCI_MAX_NR_SLOTS) {
|
||||
dev_warn(&pdev->dev, "can't have more than %d slots\n",
|
||||
dev_warn(dev, "can't have more than %d slots\n",
|
||||
ATMCI_MAX_NR_SLOTS);
|
||||
of_node_put(cnp);
|
||||
break;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(cnp, "bus-width",
|
||||
&pdata->slot[slot_id].bus_width))
|
||||
pdata->slot[slot_id].bus_width = 1;
|
||||
&host->pdata[slot_id].bus_width))
|
||||
host->pdata[slot_id].bus_width = 1;
|
||||
|
||||
pdata->slot[slot_id].detect_pin =
|
||||
devm_fwnode_gpiod_get(&pdev->dev, of_fwnode_handle(cnp),
|
||||
host->pdata[slot_id].detect_pin =
|
||||
devm_fwnode_gpiod_get(dev, of_fwnode_handle(cnp),
|
||||
"cd", GPIOD_IN, "cd-gpios");
|
||||
err = PTR_ERR_OR_ZERO(pdata->slot[slot_id].detect_pin);
|
||||
err = PTR_ERR_OR_ZERO(host->pdata[slot_id].detect_pin);
|
||||
if (err) {
|
||||
if (err != -ENOENT) {
|
||||
of_node_put(cnp);
|
||||
return ERR_PTR(err);
|
||||
return err;
|
||||
}
|
||||
pdata->slot[slot_id].detect_pin = NULL;
|
||||
host->pdata[slot_id].detect_pin = NULL;
|
||||
}
|
||||
|
||||
pdata->slot[slot_id].non_removable =
|
||||
host->pdata[slot_id].non_removable =
|
||||
of_property_read_bool(cnp, "non-removable");
|
||||
|
||||
pdata->slot[slot_id].wp_pin =
|
||||
devm_fwnode_gpiod_get(&pdev->dev, of_fwnode_handle(cnp),
|
||||
host->pdata[slot_id].wp_pin =
|
||||
devm_fwnode_gpiod_get(dev, of_fwnode_handle(cnp),
|
||||
"wp", GPIOD_IN, "wp-gpios");
|
||||
err = PTR_ERR_OR_ZERO(pdata->slot[slot_id].wp_pin);
|
||||
err = PTR_ERR_OR_ZERO(host->pdata[slot_id].wp_pin);
|
||||
if (err) {
|
||||
if (err != -ENOENT) {
|
||||
of_node_put(cnp);
|
||||
return ERR_PTR(err);
|
||||
return err;
|
||||
}
|
||||
pdata->slot[slot_id].wp_pin = NULL;
|
||||
host->pdata[slot_id].wp_pin = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return pdata;
|
||||
return 0;
|
||||
}
|
||||
#else /* CONFIG_OF */
|
||||
static inline struct mci_platform_data*
|
||||
atmci_of_init(struct platform_device *dev)
|
||||
{
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline unsigned int atmci_get_version(struct atmel_mci *host)
|
||||
{
|
||||
@ -738,11 +713,10 @@ static inline unsigned int atmci_convert_chksize(struct atmel_mci *host,
|
||||
|
||||
static void atmci_timeout_timer(struct timer_list *t)
|
||||
{
|
||||
struct atmel_mci *host;
|
||||
struct atmel_mci *host = from_timer(host, t, timer);
|
||||
struct device *dev = host->dev;
|
||||
|
||||
host = from_timer(host, t, timer);
|
||||
|
||||
dev_dbg(&host->pdev->dev, "software timeout\n");
|
||||
dev_dbg(dev, "software timeout\n");
|
||||
|
||||
if (host->mrq->cmd->data) {
|
||||
host->mrq->cmd->data->error = -ETIMEDOUT;
|
||||
@ -860,15 +834,14 @@ static u32 atmci_prepare_command(struct mmc_host *mmc,
|
||||
static void atmci_send_command(struct atmel_mci *host,
|
||||
struct mmc_command *cmd, u32 cmd_flags)
|
||||
{
|
||||
struct device *dev = host->dev;
|
||||
unsigned int timeout_ms = cmd->busy_timeout ? cmd->busy_timeout :
|
||||
ATMCI_CMD_TIMEOUT_MS;
|
||||
|
||||
WARN_ON(host->cmd);
|
||||
host->cmd = cmd;
|
||||
|
||||
dev_vdbg(&host->pdev->dev,
|
||||
"start command: ARGR=0x%08x CMDR=0x%08x\n",
|
||||
cmd->arg, cmd_flags);
|
||||
dev_vdbg(dev, "start command: ARGR=0x%08x CMDR=0x%08x\n", cmd->arg, cmd_flags);
|
||||
|
||||
atmci_writel(host, ATMCI_ARGR, cmd->arg);
|
||||
atmci_writel(host, ATMCI_CMDR, cmd_flags);
|
||||
@ -878,7 +851,9 @@ static void atmci_send_command(struct atmel_mci *host,
|
||||
|
||||
static void atmci_send_stop_cmd(struct atmel_mci *host, struct mmc_data *data)
|
||||
{
|
||||
dev_dbg(&host->pdev->dev, "send stop command\n");
|
||||
struct device *dev = host->dev;
|
||||
|
||||
dev_dbg(dev, "send stop command\n");
|
||||
atmci_send_command(host, data->stop, host->stop_cmdr);
|
||||
atmci_writel(host, ATMCI_IER, ATMCI_CMDRDY);
|
||||
}
|
||||
@ -951,11 +926,10 @@ static void atmci_pdc_set_both_buf(struct atmel_mci *host, int dir)
|
||||
static void atmci_pdc_cleanup(struct atmel_mci *host)
|
||||
{
|
||||
struct mmc_data *data = host->data;
|
||||
struct device *dev = host->dev;
|
||||
|
||||
if (data)
|
||||
dma_unmap_sg(&host->pdev->dev,
|
||||
data->sg, data->sg_len,
|
||||
mmc_get_dma_dir(data));
|
||||
dma_unmap_sg(dev, data->sg, data->sg_len, mmc_get_dma_dir(data));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -965,6 +939,7 @@ static void atmci_pdc_cleanup(struct atmel_mci *host)
|
||||
*/
|
||||
static void atmci_pdc_complete(struct atmel_mci *host)
|
||||
{
|
||||
struct device *dev = host->dev;
|
||||
int transfer_size = host->data->blocks * host->data->blksz;
|
||||
int i;
|
||||
|
||||
@ -981,7 +956,7 @@ static void atmci_pdc_complete(struct atmel_mci *host)
|
||||
|
||||
atmci_pdc_cleanup(host);
|
||||
|
||||
dev_dbg(&host->pdev->dev, "(%s) set pending xfer complete\n", __func__);
|
||||
dev_dbg(dev, "(%s) set pending xfer complete\n", __func__);
|
||||
atmci_set_pending(host, EVENT_XFER_COMPLETE);
|
||||
tasklet_schedule(&host->tasklet);
|
||||
}
|
||||
@ -1003,8 +978,9 @@ static void atmci_dma_complete(void *arg)
|
||||
{
|
||||
struct atmel_mci *host = arg;
|
||||
struct mmc_data *data = host->data;
|
||||
struct device *dev = host->dev;
|
||||
|
||||
dev_vdbg(&host->pdev->dev, "DMA complete\n");
|
||||
dev_vdbg(dev, "DMA complete\n");
|
||||
|
||||
if (host->caps.has_dma_conf_reg)
|
||||
/* Disable DMA hardware handshaking on MCI */
|
||||
@ -1017,8 +993,7 @@ static void atmci_dma_complete(void *arg)
|
||||
* to send the stop command or waiting for NBUSY in this case.
|
||||
*/
|
||||
if (data) {
|
||||
dev_dbg(&host->pdev->dev,
|
||||
"(%s) set pending xfer complete\n", __func__);
|
||||
dev_dbg(dev, "(%s) set pending xfer complete\n", __func__);
|
||||
atmci_set_pending(host, EVENT_XFER_COMPLETE);
|
||||
tasklet_schedule(&host->tasklet);
|
||||
|
||||
@ -1092,6 +1067,7 @@ static u32 atmci_prepare_data(struct atmel_mci *host, struct mmc_data *data)
|
||||
static u32
|
||||
atmci_prepare_data_pdc(struct atmel_mci *host, struct mmc_data *data)
|
||||
{
|
||||
struct device *dev = host->dev;
|
||||
u32 iflags, tmp;
|
||||
int i;
|
||||
|
||||
@ -1117,8 +1093,7 @@ atmci_prepare_data_pdc(struct atmel_mci *host, struct mmc_data *data)
|
||||
|
||||
/* Configure PDC */
|
||||
host->data_size = data->blocks * data->blksz;
|
||||
dma_map_sg(&host->pdev->dev, data->sg, data->sg_len,
|
||||
mmc_get_dma_dir(data));
|
||||
dma_map_sg(dev, data->sg, data->sg_len, mmc_get_dma_dir(data));
|
||||
|
||||
if ((!host->caps.has_rwproof)
|
||||
&& (host->data->flags & MMC_DATA_WRITE)) {
|
||||
@ -1244,8 +1219,9 @@ atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data)
|
||||
|
||||
static void atmci_stop_transfer(struct atmel_mci *host)
|
||||
{
|
||||
dev_dbg(&host->pdev->dev,
|
||||
"(%s) set pending xfer complete\n", __func__);
|
||||
struct device *dev = host->dev;
|
||||
|
||||
dev_dbg(dev, "(%s) set pending xfer complete\n", __func__);
|
||||
atmci_set_pending(host, EVENT_XFER_COMPLETE);
|
||||
atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
|
||||
}
|
||||
@ -1261,14 +1237,14 @@ static void atmci_stop_transfer_pdc(struct atmel_mci *host)
|
||||
static void atmci_stop_transfer_dma(struct atmel_mci *host)
|
||||
{
|
||||
struct dma_chan *chan = host->data_chan;
|
||||
struct device *dev = host->dev;
|
||||
|
||||
if (chan) {
|
||||
dmaengine_terminate_all(chan);
|
||||
atmci_dma_cleanup(host);
|
||||
} else {
|
||||
/* Data transfer was stopped by the interrupt handler */
|
||||
dev_dbg(&host->pdev->dev,
|
||||
"(%s) set pending xfer complete\n", __func__);
|
||||
dev_dbg(dev, "(%s) set pending xfer complete\n", __func__);
|
||||
atmci_set_pending(host, EVENT_XFER_COMPLETE);
|
||||
atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
|
||||
}
|
||||
@ -1281,6 +1257,7 @@ static void atmci_stop_transfer_dma(struct atmel_mci *host)
|
||||
static void atmci_start_request(struct atmel_mci *host,
|
||||
struct atmel_mci_slot *slot)
|
||||
{
|
||||
struct device *dev = host->dev;
|
||||
struct mmc_request *mrq;
|
||||
struct mmc_command *cmd;
|
||||
struct mmc_data *data;
|
||||
@ -1296,7 +1273,7 @@ static void atmci_start_request(struct atmel_mci *host,
|
||||
host->cmd_status = 0;
|
||||
host->data_status = 0;
|
||||
|
||||
dev_dbg(&host->pdev->dev, "start request: cmd %u\n", mrq->cmd->opcode);
|
||||
dev_dbg(dev, "start request: cmd %u\n", mrq->cmd->opcode);
|
||||
|
||||
if (host->need_reset || host->caps.need_reset_after_xfer) {
|
||||
iflags = atmci_readl(host, ATMCI_IMR);
|
||||
@ -1375,6 +1352,8 @@ static void atmci_start_request(struct atmel_mci *host,
|
||||
static void atmci_queue_request(struct atmel_mci *host,
|
||||
struct atmel_mci_slot *slot, struct mmc_request *mrq)
|
||||
{
|
||||
struct device *dev = host->dev;
|
||||
|
||||
dev_vdbg(&slot->mmc->class_dev, "queue request: state=%d\n",
|
||||
host->state);
|
||||
|
||||
@ -1384,7 +1363,7 @@ static void atmci_queue_request(struct atmel_mci *host,
|
||||
host->state = STATE_SENDING_CMD;
|
||||
atmci_start_request(host, slot);
|
||||
} else {
|
||||
dev_dbg(&host->pdev->dev, "queue request\n");
|
||||
dev_dbg(dev, "queue request\n");
|
||||
list_add_tail(&slot->queue_node, &host->queue);
|
||||
}
|
||||
spin_unlock_bh(&host->lock);
|
||||
@ -1394,10 +1373,11 @@ static void atmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
{
|
||||
struct atmel_mci_slot *slot = mmc_priv(mmc);
|
||||
struct atmel_mci *host = slot->host;
|
||||
struct device *dev = host->dev;
|
||||
struct mmc_data *data;
|
||||
|
||||
WARN_ON(slot->mrq);
|
||||
dev_dbg(&host->pdev->dev, "MRQ: cmd %u\n", mrq->cmd->opcode);
|
||||
dev_dbg(dev, "MRQ: cmd %u\n", mrq->cmd->opcode);
|
||||
|
||||
/*
|
||||
* We may "know" the card is gone even though there's still an
|
||||
@ -1607,6 +1587,7 @@ static void atmci_request_end(struct atmel_mci *host, struct mmc_request *mrq)
|
||||
{
|
||||
struct atmel_mci_slot *slot = NULL;
|
||||
struct mmc_host *prev_mmc = host->cur_slot->mmc;
|
||||
struct device *dev = host->dev;
|
||||
|
||||
WARN_ON(host->cmd || host->data);
|
||||
|
||||
@ -1629,12 +1610,11 @@ static void atmci_request_end(struct atmel_mci *host, struct mmc_request *mrq)
|
||||
slot = list_entry(host->queue.next,
|
||||
struct atmel_mci_slot, queue_node);
|
||||
list_del(&slot->queue_node);
|
||||
dev_vdbg(&host->pdev->dev, "list not empty: %s is next\n",
|
||||
mmc_hostname(slot->mmc));
|
||||
dev_vdbg(dev, "list not empty: %s is next\n", mmc_hostname(slot->mmc));
|
||||
host->state = STATE_SENDING_CMD;
|
||||
atmci_start_request(host, slot);
|
||||
} else {
|
||||
dev_vdbg(&host->pdev->dev, "list empty\n");
|
||||
dev_vdbg(dev, "list empty\n");
|
||||
host->state = STATE_IDLE;
|
||||
}
|
||||
|
||||
@ -1770,6 +1750,7 @@ static void atmci_tasklet_func(struct tasklet_struct *t)
|
||||
struct atmel_mci *host = from_tasklet(host, t, tasklet);
|
||||
struct mmc_request *mrq = host->mrq;
|
||||
struct mmc_data *data = host->data;
|
||||
struct device *dev = host->dev;
|
||||
enum atmel_mci_state state = host->state;
|
||||
enum atmel_mci_state prev_state;
|
||||
u32 status;
|
||||
@ -1778,14 +1759,13 @@ static void atmci_tasklet_func(struct tasklet_struct *t)
|
||||
|
||||
state = host->state;
|
||||
|
||||
dev_vdbg(&host->pdev->dev,
|
||||
"tasklet: state %u pending/completed/mask %lx/%lx/%x\n",
|
||||
dev_vdbg(dev, "tasklet: state %u pending/completed/mask %lx/%lx/%x\n",
|
||||
state, host->pending_events, host->completed_events,
|
||||
atmci_readl(host, ATMCI_IMR));
|
||||
|
||||
do {
|
||||
prev_state = state;
|
||||
dev_dbg(&host->pdev->dev, "FSM: state=%d\n", state);
|
||||
dev_dbg(dev, "FSM: state=%d\n", state);
|
||||
|
||||
switch (state) {
|
||||
case STATE_IDLE:
|
||||
@ -1798,18 +1778,17 @@ static void atmci_tasklet_func(struct tasklet_struct *t)
|
||||
* END_REQUEST by default, WAITING_NOTBUSY if it's a
|
||||
* command needing it or DATA_XFER if there is data.
|
||||
*/
|
||||
dev_dbg(&host->pdev->dev, "FSM: cmd ready?\n");
|
||||
dev_dbg(dev, "FSM: cmd ready?\n");
|
||||
if (!atmci_test_and_clear_pending(host,
|
||||
EVENT_CMD_RDY))
|
||||
break;
|
||||
|
||||
dev_dbg(&host->pdev->dev, "set completed cmd ready\n");
|
||||
dev_dbg(dev, "set completed cmd ready\n");
|
||||
host->cmd = NULL;
|
||||
atmci_set_completed(host, EVENT_CMD_RDY);
|
||||
atmci_command_complete(host, mrq->cmd);
|
||||
if (mrq->data) {
|
||||
dev_dbg(&host->pdev->dev,
|
||||
"command with data transfer");
|
||||
dev_dbg(dev, "command with data transfer\n");
|
||||
/*
|
||||
* If there is a command error don't start
|
||||
* data transfer.
|
||||
@ -1824,8 +1803,7 @@ static void atmci_tasklet_func(struct tasklet_struct *t)
|
||||
} else
|
||||
state = STATE_DATA_XFER;
|
||||
} else if ((!mrq->data) && (mrq->cmd->flags & MMC_RSP_BUSY)) {
|
||||
dev_dbg(&host->pdev->dev,
|
||||
"command response need waiting notbusy");
|
||||
dev_dbg(dev, "command response need waiting notbusy\n");
|
||||
atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
|
||||
state = STATE_WAITING_NOTBUSY;
|
||||
} else
|
||||
@ -1836,7 +1814,7 @@ static void atmci_tasklet_func(struct tasklet_struct *t)
|
||||
case STATE_DATA_XFER:
|
||||
if (atmci_test_and_clear_pending(host,
|
||||
EVENT_DATA_ERROR)) {
|
||||
dev_dbg(&host->pdev->dev, "set completed data error\n");
|
||||
dev_dbg(dev, "set completed data error\n");
|
||||
atmci_set_completed(host, EVENT_DATA_ERROR);
|
||||
state = STATE_END_REQUEST;
|
||||
break;
|
||||
@ -1849,14 +1827,12 @@ static void atmci_tasklet_func(struct tasklet_struct *t)
|
||||
* to the next step which is WAITING_NOTBUSY in write
|
||||
* case and directly SENDING_STOP in read case.
|
||||
*/
|
||||
dev_dbg(&host->pdev->dev, "FSM: xfer complete?\n");
|
||||
dev_dbg(dev, "FSM: xfer complete?\n");
|
||||
if (!atmci_test_and_clear_pending(host,
|
||||
EVENT_XFER_COMPLETE))
|
||||
break;
|
||||
|
||||
dev_dbg(&host->pdev->dev,
|
||||
"(%s) set completed xfer complete\n",
|
||||
__func__);
|
||||
dev_dbg(dev, "(%s) set completed xfer complete\n", __func__);
|
||||
atmci_set_completed(host, EVENT_XFER_COMPLETE);
|
||||
|
||||
if (host->caps.need_notbusy_for_read_ops ||
|
||||
@ -1881,12 +1857,12 @@ static void atmci_tasklet_func(struct tasklet_struct *t)
|
||||
* included) or a write operation. In the latest case,
|
||||
* we need to send a stop command.
|
||||
*/
|
||||
dev_dbg(&host->pdev->dev, "FSM: not busy?\n");
|
||||
dev_dbg(dev, "FSM: not busy?\n");
|
||||
if (!atmci_test_and_clear_pending(host,
|
||||
EVENT_NOTBUSY))
|
||||
break;
|
||||
|
||||
dev_dbg(&host->pdev->dev, "set completed not busy\n");
|
||||
dev_dbg(dev, "set completed not busy\n");
|
||||
atmci_set_completed(host, EVENT_NOTBUSY);
|
||||
|
||||
if (host->data) {
|
||||
@ -1916,12 +1892,12 @@ static void atmci_tasklet_func(struct tasklet_struct *t)
|
||||
* in order to go to the end request state instead of
|
||||
* sending stop again.
|
||||
*/
|
||||
dev_dbg(&host->pdev->dev, "FSM: cmd ready?\n");
|
||||
dev_dbg(dev, "FSM: cmd ready?\n");
|
||||
if (!atmci_test_and_clear_pending(host,
|
||||
EVENT_CMD_RDY))
|
||||
break;
|
||||
|
||||
dev_dbg(&host->pdev->dev, "FSM: cmd ready\n");
|
||||
dev_dbg(dev, "FSM: cmd ready\n");
|
||||
host->cmd = NULL;
|
||||
data->bytes_xfered = data->blocks * data->blksz;
|
||||
data->error = 0;
|
||||
@ -2120,6 +2096,7 @@ static void atmci_sdio_interrupt(struct atmel_mci *host, u32 status)
|
||||
static irqreturn_t atmci_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct atmel_mci *host = dev_id;
|
||||
struct device *dev = host->dev;
|
||||
u32 status, mask, pending;
|
||||
unsigned int pass_count = 0;
|
||||
|
||||
@ -2131,21 +2108,21 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
|
||||
break;
|
||||
|
||||
if (pending & ATMCI_DATA_ERROR_FLAGS) {
|
||||
dev_dbg(&host->pdev->dev, "IRQ: data error\n");
|
||||
dev_dbg(dev, "IRQ: data error\n");
|
||||
atmci_writel(host, ATMCI_IDR, ATMCI_DATA_ERROR_FLAGS
|
||||
| ATMCI_RXRDY | ATMCI_TXRDY
|
||||
| ATMCI_ENDRX | ATMCI_ENDTX
|
||||
| ATMCI_RXBUFF | ATMCI_TXBUFE);
|
||||
|
||||
host->data_status = status;
|
||||
dev_dbg(&host->pdev->dev, "set pending data error\n");
|
||||
dev_dbg(dev, "set pending data error\n");
|
||||
smp_wmb();
|
||||
atmci_set_pending(host, EVENT_DATA_ERROR);
|
||||
tasklet_schedule(&host->tasklet);
|
||||
}
|
||||
|
||||
if (pending & ATMCI_TXBUFE) {
|
||||
dev_dbg(&host->pdev->dev, "IRQ: tx buffer empty\n");
|
||||
dev_dbg(dev, "IRQ: tx buffer empty\n");
|
||||
atmci_writel(host, ATMCI_IDR, ATMCI_TXBUFE);
|
||||
atmci_writel(host, ATMCI_IDR, ATMCI_ENDTX);
|
||||
/*
|
||||
@ -2161,7 +2138,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
|
||||
atmci_pdc_complete(host);
|
||||
}
|
||||
} else if (pending & ATMCI_ENDTX) {
|
||||
dev_dbg(&host->pdev->dev, "IRQ: end of tx buffer\n");
|
||||
dev_dbg(dev, "IRQ: end of tx buffer\n");
|
||||
atmci_writel(host, ATMCI_IDR, ATMCI_ENDTX);
|
||||
|
||||
if (host->data_size) {
|
||||
@ -2172,7 +2149,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
|
||||
}
|
||||
|
||||
if (pending & ATMCI_RXBUFF) {
|
||||
dev_dbg(&host->pdev->dev, "IRQ: rx buffer full\n");
|
||||
dev_dbg(dev, "IRQ: rx buffer full\n");
|
||||
atmci_writel(host, ATMCI_IDR, ATMCI_RXBUFF);
|
||||
atmci_writel(host, ATMCI_IDR, ATMCI_ENDRX);
|
||||
/*
|
||||
@ -2188,7 +2165,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
|
||||
atmci_pdc_complete(host);
|
||||
}
|
||||
} else if (pending & ATMCI_ENDRX) {
|
||||
dev_dbg(&host->pdev->dev, "IRQ: end of rx buffer\n");
|
||||
dev_dbg(dev, "IRQ: end of rx buffer\n");
|
||||
atmci_writel(host, ATMCI_IDR, ATMCI_ENDRX);
|
||||
|
||||
if (host->data_size) {
|
||||
@ -2205,19 +2182,19 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
|
||||
* The appropriate workaround is to use the BLKE signal.
|
||||
*/
|
||||
if (pending & ATMCI_BLKE) {
|
||||
dev_dbg(&host->pdev->dev, "IRQ: blke\n");
|
||||
dev_dbg(dev, "IRQ: blke\n");
|
||||
atmci_writel(host, ATMCI_IDR, ATMCI_BLKE);
|
||||
smp_wmb();
|
||||
dev_dbg(&host->pdev->dev, "set pending notbusy\n");
|
||||
dev_dbg(dev, "set pending notbusy\n");
|
||||
atmci_set_pending(host, EVENT_NOTBUSY);
|
||||
tasklet_schedule(&host->tasklet);
|
||||
}
|
||||
|
||||
if (pending & ATMCI_NOTBUSY) {
|
||||
dev_dbg(&host->pdev->dev, "IRQ: not_busy\n");
|
||||
dev_dbg(dev, "IRQ: not_busy\n");
|
||||
atmci_writel(host, ATMCI_IDR, ATMCI_NOTBUSY);
|
||||
smp_wmb();
|
||||
dev_dbg(&host->pdev->dev, "set pending notbusy\n");
|
||||
dev_dbg(dev, "set pending notbusy\n");
|
||||
atmci_set_pending(host, EVENT_NOTBUSY);
|
||||
tasklet_schedule(&host->tasklet);
|
||||
}
|
||||
@ -2228,11 +2205,11 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
|
||||
atmci_write_data_pio(host);
|
||||
|
||||
if (pending & ATMCI_CMDRDY) {
|
||||
dev_dbg(&host->pdev->dev, "IRQ: cmd ready\n");
|
||||
dev_dbg(dev, "IRQ: cmd ready\n");
|
||||
atmci_writel(host, ATMCI_IDR, ATMCI_CMDRDY);
|
||||
host->cmd_status = status;
|
||||
smp_wmb();
|
||||
dev_dbg(&host->pdev->dev, "set pending cmd rdy\n");
|
||||
dev_dbg(dev, "set pending cmd rdy\n");
|
||||
atmci_set_pending(host, EVENT_CMD_RDY);
|
||||
tasklet_schedule(&host->tasklet);
|
||||
}
|
||||
@ -2264,11 +2241,12 @@ static int atmci_init_slot(struct atmel_mci *host,
|
||||
struct mci_slot_pdata *slot_data, unsigned int id,
|
||||
u32 sdc_reg, u32 sdio_irq)
|
||||
{
|
||||
struct device *dev = host->dev;
|
||||
struct mmc_host *mmc;
|
||||
struct atmel_mci_slot *slot;
|
||||
int ret;
|
||||
|
||||
mmc = mmc_alloc_host(sizeof(struct atmel_mci_slot), &host->pdev->dev);
|
||||
mmc = mmc_alloc_host(sizeof(struct atmel_mci_slot), dev);
|
||||
if (!mmc)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -2387,29 +2365,13 @@ static void atmci_cleanup_slot(struct atmel_mci_slot *slot,
|
||||
|
||||
static int atmci_configure_dma(struct atmel_mci *host)
|
||||
{
|
||||
host->dma.chan = dma_request_chan(&host->pdev->dev, "rxtx");
|
||||
|
||||
if (PTR_ERR(host->dma.chan) == -ENODEV) {
|
||||
struct mci_platform_data *pdata = host->pdev->dev.platform_data;
|
||||
dma_cap_mask_t mask;
|
||||
|
||||
if (!pdata || !pdata->dma_filter)
|
||||
return -ENODEV;
|
||||
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
|
||||
host->dma.chan = dma_request_channel(mask, pdata->dma_filter,
|
||||
pdata->dma_slave);
|
||||
if (!host->dma.chan)
|
||||
host->dma.chan = ERR_PTR(-ENODEV);
|
||||
}
|
||||
struct device *dev = host->dev;
|
||||
|
||||
host->dma.chan = dma_request_chan(dev, "rxtx");
|
||||
if (IS_ERR(host->dma.chan))
|
||||
return PTR_ERR(host->dma.chan);
|
||||
|
||||
dev_info(&host->pdev->dev, "using %s for DMA transfers\n",
|
||||
dma_chan_name(host->dma.chan));
|
||||
dev_info(dev, "using %s for DMA transfers\n", dma_chan_name(host->dma.chan));
|
||||
|
||||
host->dma_conf.src_addr = host->mapbase + ATMCI_RDR;
|
||||
host->dma_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
@ -2429,11 +2391,11 @@ static int atmci_configure_dma(struct atmel_mci *host)
|
||||
*/
|
||||
static void atmci_get_cap(struct atmel_mci *host)
|
||||
{
|
||||
struct device *dev = host->dev;
|
||||
unsigned int version;
|
||||
|
||||
version = atmci_get_version(host);
|
||||
dev_info(&host->pdev->dev,
|
||||
"version: 0x%x\n", version);
|
||||
dev_info(dev, "version: 0x%x\n", version);
|
||||
|
||||
host->caps.has_dma_conf_reg = false;
|
||||
host->caps.has_pdc = true;
|
||||
@ -2474,15 +2436,14 @@ static void atmci_get_cap(struct atmel_mci *host)
|
||||
break;
|
||||
default:
|
||||
host->caps.has_pdc = false;
|
||||
dev_warn(&host->pdev->dev,
|
||||
"Unmanaged mci version, set minimum capabilities\n");
|
||||
dev_warn(dev, "Unmanaged mci version, set minimum capabilities\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int atmci_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mci_platform_data *pdata;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct atmel_mci *host;
|
||||
struct resource *regs;
|
||||
unsigned int nr_slots;
|
||||
@ -2492,32 +2453,28 @@ static int atmci_probe(struct platform_device *pdev)
|
||||
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!regs)
|
||||
return -ENXIO;
|
||||
pdata = pdev->dev.platform_data;
|
||||
if (!pdata) {
|
||||
pdata = atmci_of_init(pdev);
|
||||
if (IS_ERR(pdata)) {
|
||||
dev_err(&pdev->dev, "platform data not available\n");
|
||||
return PTR_ERR(pdata);
|
||||
}
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
|
||||
host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
|
||||
if (!host)
|
||||
return -ENOMEM;
|
||||
|
||||
host->pdev = pdev;
|
||||
host->dev = dev;
|
||||
spin_lock_init(&host->lock);
|
||||
INIT_LIST_HEAD(&host->queue);
|
||||
|
||||
host->mck = devm_clk_get(&pdev->dev, "mci_clk");
|
||||
ret = atmci_of_init(host);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Slot information not available\n");
|
||||
|
||||
host->mck = devm_clk_get(dev, "mci_clk");
|
||||
if (IS_ERR(host->mck))
|
||||
return PTR_ERR(host->mck);
|
||||
|
||||
host->regs = devm_ioremap(&pdev->dev, regs->start, resource_size(regs));
|
||||
host->regs = devm_ioremap(dev, regs->start, resource_size(regs));
|
||||
if (!host->regs)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -2532,7 +2489,7 @@ static int atmci_probe(struct platform_device *pdev)
|
||||
|
||||
tasklet_setup(&host->tasklet, atmci_tasklet_func);
|
||||
|
||||
ret = request_irq(irq, atmci_interrupt, 0, dev_name(&pdev->dev), host);
|
||||
ret = request_irq(irq, atmci_interrupt, 0, dev_name(dev), host);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(host->mck);
|
||||
return ret;
|
||||
@ -2548,12 +2505,12 @@ static int atmci_probe(struct platform_device *pdev)
|
||||
host->submit_data = &atmci_submit_data_dma;
|
||||
host->stop_transfer = &atmci_stop_transfer_dma;
|
||||
} else if (host->caps.has_pdc) {
|
||||
dev_info(&pdev->dev, "using PDC\n");
|
||||
dev_info(dev, "using PDC\n");
|
||||
host->prepare_data = &atmci_prepare_data_pdc;
|
||||
host->submit_data = &atmci_submit_data_pdc;
|
||||
host->stop_transfer = &atmci_stop_transfer_pdc;
|
||||
} else {
|
||||
dev_info(&pdev->dev, "using PIO\n");
|
||||
dev_info(dev, "using PIO\n");
|
||||
host->prepare_data = &atmci_prepare_data;
|
||||
host->submit_data = &atmci_submit_data;
|
||||
host->stop_transfer = &atmci_stop_transfer;
|
||||
@ -2563,25 +2520,25 @@ static int atmci_probe(struct platform_device *pdev)
|
||||
|
||||
timer_setup(&host->timer, atmci_timeout_timer, 0);
|
||||
|
||||
pm_runtime_get_noresume(&pdev->dev);
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, AUTOSUSPEND_DELAY);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_get_noresume(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_set_autosuspend_delay(dev, AUTOSUSPEND_DELAY);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
/* We need at least one slot to succeed */
|
||||
nr_slots = 0;
|
||||
ret = -ENODEV;
|
||||
if (pdata->slot[0].bus_width) {
|
||||
ret = atmci_init_slot(host, &pdata->slot[0],
|
||||
if (host->pdata[0].bus_width) {
|
||||
ret = atmci_init_slot(host, &host->pdata[0],
|
||||
0, ATMCI_SDCSEL_SLOT_A, ATMCI_SDIOIRQA);
|
||||
if (!ret) {
|
||||
nr_slots++;
|
||||
host->buf_size = host->slot[0]->mmc->max_req_size;
|
||||
}
|
||||
}
|
||||
if (pdata->slot[1].bus_width) {
|
||||
ret = atmci_init_slot(host, &pdata->slot[1],
|
||||
if (host->pdata[1].bus_width) {
|
||||
ret = atmci_init_slot(host, &host->pdata[1],
|
||||
1, ATMCI_SDCSEL_SLOT_B, ATMCI_SDIOIRQB);
|
||||
if (!ret) {
|
||||
nr_slots++;
|
||||
@ -2592,27 +2549,25 @@ static int atmci_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
if (!nr_slots) {
|
||||
dev_err(&pdev->dev, "init failed: no slot defined\n");
|
||||
dev_err_probe(dev, ret, "init failed: no slot defined\n");
|
||||
goto err_init_slot;
|
||||
}
|
||||
|
||||
if (!host->caps.has_rwproof) {
|
||||
host->buffer = dma_alloc_coherent(&pdev->dev, host->buf_size,
|
||||
host->buffer = dma_alloc_coherent(dev, host->buf_size,
|
||||
&host->buf_phys_addr,
|
||||
GFP_KERNEL);
|
||||
if (!host->buffer) {
|
||||
ret = -ENOMEM;
|
||||
dev_err(&pdev->dev, "buffer allocation failed\n");
|
||||
ret = dev_err_probe(dev, -ENOMEM, "buffer allocation failed\n");
|
||||
goto err_dma_alloc;
|
||||
}
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev,
|
||||
"Atmel MCI controller at 0x%08lx irq %d, %u slots\n",
|
||||
host->mapbase, irq, nr_slots);
|
||||
dev_info(dev, "Atmel MCI controller at 0x%08lx irq %d, %u slots\n",
|
||||
host->mapbase, irq, nr_slots);
|
||||
|
||||
pm_runtime_mark_last_busy(&host->pdev->dev);
|
||||
pm_runtime_put_autosuspend(&pdev->dev);
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
@ -2624,8 +2579,8 @@ static int atmci_probe(struct platform_device *pdev)
|
||||
err_init_slot:
|
||||
clk_disable_unprepare(host->mck);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_put_noidle(dev);
|
||||
|
||||
del_timer_sync(&host->timer);
|
||||
if (!IS_ERR(host->dma.chan))
|
||||
@ -2638,13 +2593,13 @@ static int atmci_probe(struct platform_device *pdev)
|
||||
static void atmci_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct atmel_mci *host = platform_get_drvdata(pdev);
|
||||
struct device *dev = &pdev->dev;
|
||||
unsigned int i;
|
||||
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
pm_runtime_get_sync(dev);
|
||||
|
||||
if (host->buffer)
|
||||
dma_free_coherent(&pdev->dev, host->buf_size,
|
||||
host->buffer, host->buf_phys_addr);
|
||||
dma_free_coherent(dev, host->buf_size, host->buffer, host->buf_phys_addr);
|
||||
|
||||
for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) {
|
||||
if (host->slot[i])
|
||||
@ -2663,8 +2618,8 @@ static void atmci_remove(struct platform_device *pdev)
|
||||
|
||||
clk_disable_unprepare(host->mck);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_put_noidle(dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
@ -2701,7 +2656,7 @@ static struct platform_driver atmci_driver = {
|
||||
.driver = {
|
||||
.name = "atmel_mci",
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
.of_match_table = of_match_ptr(atmci_dt_ids),
|
||||
.of_match_table = atmci_dt_ids,
|
||||
.pm = &atmci_dev_pm_ops,
|
||||
},
|
||||
};
|
||||
|
@ -474,8 +474,8 @@ static int cqhci_dma_map(struct mmc_host *host, struct mmc_request *mrq)
|
||||
return sg_count;
|
||||
}
|
||||
|
||||
static void cqhci_set_tran_desc(u8 *desc, dma_addr_t addr, int len, bool end,
|
||||
bool dma64)
|
||||
void cqhci_set_tran_desc(u8 *desc, dma_addr_t addr, int len, bool end,
|
||||
bool dma64)
|
||||
{
|
||||
__le32 *attr = (__le32 __force *)desc;
|
||||
|
||||
@ -495,6 +495,7 @@ static void cqhci_set_tran_desc(u8 *desc, dma_addr_t addr, int len, bool end,
|
||||
dataddr[0] = cpu_to_le32(addr);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(cqhci_set_tran_desc);
|
||||
|
||||
static int cqhci_prep_tran_desc(struct mmc_request *mrq,
|
||||
struct cqhci_host *cq_host, int tag)
|
||||
@ -522,7 +523,11 @@ static int cqhci_prep_tran_desc(struct mmc_request *mrq,
|
||||
|
||||
if ((i+1) == sg_count)
|
||||
end = true;
|
||||
cqhci_set_tran_desc(desc, addr, len, end, dma64);
|
||||
if (cq_host->ops->set_tran_desc)
|
||||
cq_host->ops->set_tran_desc(cq_host, &desc, addr, len, end, dma64);
|
||||
else
|
||||
cqhci_set_tran_desc(desc, addr, len, end, dma64);
|
||||
|
||||
desc += cq_host->trans_desc_len;
|
||||
}
|
||||
|
||||
|
@ -293,6 +293,9 @@ struct cqhci_host_ops {
|
||||
int (*program_key)(struct cqhci_host *cq_host,
|
||||
const union cqhci_crypto_cfg_entry *cfg, int slot);
|
||||
#endif
|
||||
void (*set_tran_desc)(struct cqhci_host *cq_host, u8 **desc,
|
||||
dma_addr_t addr, int len, bool end, bool dma64);
|
||||
|
||||
};
|
||||
|
||||
static inline void cqhci_writel(struct cqhci_host *host, u32 val, int reg)
|
||||
@ -318,6 +321,7 @@ irqreturn_t cqhci_irq(struct mmc_host *mmc, u32 intmask, int cmd_error,
|
||||
int cqhci_init(struct cqhci_host *cq_host, struct mmc_host *mmc, bool dma64);
|
||||
struct cqhci_host *cqhci_pltfm_init(struct platform_device *pdev);
|
||||
int cqhci_deactivate(struct mmc_host *mmc);
|
||||
void cqhci_set_tran_desc(u8 *desc, dma_addr_t addr, int len, bool end, bool dma64);
|
||||
static inline int cqhci_suspend(struct mmc_host *mmc)
|
||||
{
|
||||
return cqhci_deactivate(mmc);
|
||||
|
@ -1337,7 +1337,7 @@ static int davinci_mmcsd_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit davinci_mmcsd_remove(struct platform_device *pdev)
|
||||
static void davinci_mmcsd_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mmc_davinci_host *host = platform_get_drvdata(pdev);
|
||||
|
||||
@ -1392,7 +1392,7 @@ static struct platform_driver davinci_mmcsd_driver = {
|
||||
.of_match_table = davinci_mmc_dt_ids,
|
||||
},
|
||||
.probe = davinci_mmcsd_probe,
|
||||
.remove_new = __exit_p(davinci_mmcsd_remove),
|
||||
.remove_new = davinci_mmcsd_remove,
|
||||
.id_table = davinci_mmc_devtype,
|
||||
};
|
||||
|
||||
|
@ -87,7 +87,6 @@ static int dw_mci_hi3798cv200_execute_tuning(struct dw_mci_slot *slot,
|
||||
goto tuning_out;
|
||||
|
||||
prev_err = err;
|
||||
err = 0;
|
||||
}
|
||||
|
||||
tuning_out:
|
||||
|
@ -133,7 +133,6 @@ static int dw_mci_hi3798mv200_execute_tuning_mix_mode(struct dw_mci_slot *slot,
|
||||
goto tuning_out;
|
||||
|
||||
prev_err = err;
|
||||
err = 0;
|
||||
}
|
||||
|
||||
tuning_out:
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
|
@ -589,6 +589,9 @@ static void renesas_sdhi_reset(struct tmio_mmc_host *host, bool preserve)
|
||||
sd_ctrl_write16(host, CTL_RESET_SD, 0x0001);
|
||||
priv->needs_adjust_hs400 = false;
|
||||
renesas_sdhi_set_clock(host, host->clk_cache);
|
||||
|
||||
/* Ensure default value for this driver. */
|
||||
renesas_sdhi_sdbuf_width(host, 16);
|
||||
} else if (priv->scc_ctl) {
|
||||
renesas_sdhi_scc_reset(host, priv);
|
||||
}
|
||||
|
@ -210,7 +210,7 @@ static const struct renesas_sdhi_quirks sdhi_quirks_r8a77990 = {
|
||||
.manual_tap_correction = true,
|
||||
};
|
||||
|
||||
static const struct renesas_sdhi_quirks sdhi_quirks_r9a09g011 = {
|
||||
static const struct renesas_sdhi_quirks sdhi_quirks_rzg2l = {
|
||||
.fixed_addr_mode = true,
|
||||
.hs400_disabled = true,
|
||||
};
|
||||
@ -255,9 +255,9 @@ static const struct renesas_sdhi_of_data_with_quirks of_r8a77990_compatible = {
|
||||
.quirks = &sdhi_quirks_r8a77990,
|
||||
};
|
||||
|
||||
static const struct renesas_sdhi_of_data_with_quirks of_r9a09g011_compatible = {
|
||||
static const struct renesas_sdhi_of_data_with_quirks of_rzg2l_compatible = {
|
||||
.of_data = &of_data_rcar_gen3,
|
||||
.quirks = &sdhi_quirks_r9a09g011,
|
||||
.quirks = &sdhi_quirks_rzg2l,
|
||||
};
|
||||
|
||||
static const struct renesas_sdhi_of_data_with_quirks of_rcar_gen3_compatible = {
|
||||
@ -283,7 +283,8 @@ static const struct of_device_id renesas_sdhi_internal_dmac_of_match[] = {
|
||||
{ .compatible = "renesas,sdhi-r8a77970", .data = &of_r8a77970_compatible, },
|
||||
{ .compatible = "renesas,sdhi-r8a77990", .data = &of_r8a77990_compatible, },
|
||||
{ .compatible = "renesas,sdhi-r8a77995", .data = &of_rcar_gen3_nohs400_compatible, },
|
||||
{ .compatible = "renesas,sdhi-r9a09g011", .data = &of_r9a09g011_compatible, },
|
||||
{ .compatible = "renesas,sdhi-r9a09g011", .data = &of_rzg2l_compatible, },
|
||||
{ .compatible = "renesas,rzg2l-sdhi", .data = &of_rzg2l_compatible, },
|
||||
{ .compatible = "renesas,rcar-gen3-sdhi", .data = &of_rcar_gen3_compatible, },
|
||||
{ .compatible = "renesas,rcar-gen4-sdhi", .data = &of_rcar_gen3_compatible, },
|
||||
{},
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <linux/export.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/pinctrl/pinconf-generic.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/io.h>
|
||||
@ -80,6 +81,8 @@ struct sdhci_acpi_host {
|
||||
enum {
|
||||
DMI_QUIRK_RESET_SD_SIGNAL_VOLT_ON_SUSP = BIT(0),
|
||||
DMI_QUIRK_SD_NO_WRITE_PROTECT = BIT(1),
|
||||
DMI_QUIRK_SD_CD_ACTIVE_HIGH = BIT(2),
|
||||
DMI_QUIRK_SD_CD_ENABLE_PULL_UP = BIT(3),
|
||||
};
|
||||
|
||||
static inline void *sdhci_acpi_priv(struct sdhci_acpi_host *c)
|
||||
@ -719,7 +722,28 @@ static const struct acpi_device_id sdhci_acpi_ids[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, sdhci_acpi_ids);
|
||||
|
||||
/* Please keep this list sorted alphabetically */
|
||||
static const struct dmi_system_id sdhci_acpi_quirks[] = {
|
||||
{
|
||||
/*
|
||||
* The Acer Aspire Switch 10 (SW5-012) microSD slot always
|
||||
* reports the card being write-protected even though microSD
|
||||
* cards do not have a write-protect switch at all.
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire SW5-012"),
|
||||
},
|
||||
.driver_data = (void *)DMI_QUIRK_SD_NO_WRITE_PROTECT,
|
||||
},
|
||||
{
|
||||
/* Asus T100TA, needs pull-up for cd but DSDT GpioInt has NoPull set */
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "T100TA"),
|
||||
},
|
||||
.driver_data = (void *)DMI_QUIRK_SD_CD_ENABLE_PULL_UP,
|
||||
},
|
||||
{
|
||||
/*
|
||||
* The Lenovo Miix 320-10ICR has a bug in the _PS0 method of
|
||||
@ -736,15 +760,23 @@ static const struct dmi_system_id sdhci_acpi_quirks[] = {
|
||||
},
|
||||
{
|
||||
/*
|
||||
* The Acer Aspire Switch 10 (SW5-012) microSD slot always
|
||||
* reports the card being write-protected even though microSD
|
||||
* cards do not have a write-protect switch at all.
|
||||
* Lenovo Yoga Tablet 2 Pro 1380F/L (13" Android version) this
|
||||
* has broken WP reporting and an inverted CD signal.
|
||||
* Note this has more or less the same BIOS as the Lenovo Yoga
|
||||
* Tablet 2 830F/L or 1050F/L (8" and 10" Android), but unlike
|
||||
* the 830 / 1050 models which share the same mainboard this
|
||||
* model has a different mainboard and the inverted CD and
|
||||
* broken WP are unique to this board.
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire SW5-012"),
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "VALLEYVIEW C0 PLATFORM"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "BYT-T FFD8"),
|
||||
/* Full match so as to NOT match the 830/1050 BIOS */
|
||||
DMI_MATCH(DMI_BIOS_VERSION, "BLADE_21.X64.0005.R00.1504101516"),
|
||||
},
|
||||
.driver_data = (void *)DMI_QUIRK_SD_NO_WRITE_PROTECT,
|
||||
.driver_data = (void *)(DMI_QUIRK_SD_NO_WRITE_PROTECT |
|
||||
DMI_QUIRK_SD_CD_ACTIVE_HIGH),
|
||||
},
|
||||
{
|
||||
/*
|
||||
@ -757,6 +789,17 @@ static const struct dmi_system_id sdhci_acpi_quirks[] = {
|
||||
},
|
||||
.driver_data = (void *)DMI_QUIRK_SD_NO_WRITE_PROTECT,
|
||||
},
|
||||
{
|
||||
/*
|
||||
* The Toshiba WT10-A's microSD slot always reports the card being
|
||||
* write-protected.
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "TOSHIBA WT10-A"),
|
||||
},
|
||||
.driver_data = (void *)DMI_QUIRK_SD_NO_WRITE_PROTECT,
|
||||
},
|
||||
{} /* Terminating entry */
|
||||
};
|
||||
|
||||
@ -866,12 +909,18 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
|
||||
if (sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD)) {
|
||||
bool v = sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL);
|
||||
|
||||
if (quirks & DMI_QUIRK_SD_CD_ACTIVE_HIGH)
|
||||
host->mmc->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;
|
||||
|
||||
err = mmc_gpiod_request_cd(host->mmc, NULL, 0, v, 0);
|
||||
if (err) {
|
||||
if (err == -EPROBE_DEFER)
|
||||
goto err_free;
|
||||
dev_warn(dev, "failed to setup card detect gpio\n");
|
||||
c->use_runtime_pm = false;
|
||||
} else if (quirks & DMI_QUIRK_SD_CD_ENABLE_PULL_UP) {
|
||||
mmc_gpiod_set_cd_config(host->mmc,
|
||||
PIN_CONF_PACKED(PIN_CONFIG_BIAS_PULL_UP, 20000));
|
||||
}
|
||||
|
||||
if (quirks & DMI_QUIRK_RESET_SD_SIGNAL_VOLT_ON_SUSP)
|
||||
|
@ -335,7 +335,7 @@ static void esdhc_mcf_copy_to_bounce_buffer(struct sdhci_host *host,
|
||||
data->blksz * data->blocks);
|
||||
}
|
||||
|
||||
static struct sdhci_ops sdhci_esdhc_ops = {
|
||||
static const struct sdhci_ops sdhci_esdhc_ops = {
|
||||
.reset = esdhc_mcf_reset,
|
||||
.set_clock = esdhc_mcf_pltfm_set_clock,
|
||||
.get_max_clock = esdhc_mcf_pltfm_get_max_clock,
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <linux/sizes.h>
|
||||
|
||||
#include "sdhci-pltfm.h"
|
||||
#include "cqhci.h"
|
||||
|
||||
#define SDHCI_DWCMSHC_ARG2_STUFF GENMASK(31, 16)
|
||||
|
||||
@ -52,6 +53,9 @@
|
||||
#define AT_CTRL_SWIN_TH_VAL_MASK GENMASK(31, 24) /* bits [31:24] */
|
||||
#define AT_CTRL_SWIN_TH_VAL 0x9 /* sampling window threshold */
|
||||
|
||||
/* DWC IP vendor area 2 pointer */
|
||||
#define DWCMSHC_P_VENDOR_AREA2 0xea
|
||||
|
||||
/* Sophgo CV18XX specific Registers */
|
||||
#define CV18XX_SDHCI_MSHC_CTRL 0x00
|
||||
#define CV18XX_EMMC_FUNC_EN BIT(0)
|
||||
@ -66,6 +70,10 @@
|
||||
#define CV18XX_SDHCI_PHY_CONFIG 0x4c
|
||||
#define CV18XX_PHY_TX_BPS BIT(0)
|
||||
|
||||
#define CV18XX_TUNE_MAX 128
|
||||
#define CV18XX_TUNE_STEP 1
|
||||
#define CV18XX_RETRY_TUNING_MAX 50
|
||||
|
||||
/* Rockchip specific Registers */
|
||||
#define DWCMSHC_EMMC_DLL_CTRL 0x800
|
||||
#define DWCMSHC_EMMC_DLL_RXCLK 0x804
|
||||
@ -181,6 +189,10 @@
|
||||
#define BOUNDARY_OK(addr, len) \
|
||||
((addr | (SZ_128M - 1)) == ((addr + len - 1) | (SZ_128M - 1)))
|
||||
|
||||
#define DWCMSHC_SDHCI_CQE_TRNS_MODE (SDHCI_TRNS_MULTI | \
|
||||
SDHCI_TRNS_BLK_CNT_EN | \
|
||||
SDHCI_TRNS_DMA)
|
||||
|
||||
enum dwcmshc_rk_type {
|
||||
DWCMSHC_RK3568,
|
||||
DWCMSHC_RK3588,
|
||||
@ -196,7 +208,9 @@ struct rk35xx_priv {
|
||||
|
||||
struct dwcmshc_priv {
|
||||
struct clk *bus_clk;
|
||||
int vendor_specific_area1; /* P_VENDOR_SPECIFIC_AREA reg */
|
||||
int vendor_specific_area1; /* P_VENDOR_SPECIFIC_AREA1 reg */
|
||||
int vendor_specific_area2; /* P_VENDOR_SPECIFIC_AREA2 reg */
|
||||
|
||||
void *priv; /* pointer to SoC private stuff */
|
||||
u16 delay_line;
|
||||
u16 flags;
|
||||
@ -455,6 +469,90 @@ static void dwcmshc_hs400_enhanced_strobe(struct mmc_host *mmc,
|
||||
sdhci_writel(host, vendor, reg);
|
||||
}
|
||||
|
||||
static int dwcmshc_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
||||
{
|
||||
int err = sdhci_execute_tuning(mmc, opcode);
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* Tuning can leave the IP in an active state (Buffer Read Enable bit
|
||||
* set) which prevents the entry to low power states (i.e. S0i3). Data
|
||||
* reset will clear it.
|
||||
*/
|
||||
sdhci_reset(host, SDHCI_RESET_DATA);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 dwcmshc_cqe_irq_handler(struct sdhci_host *host, u32 intmask)
|
||||
{
|
||||
int cmd_error = 0;
|
||||
int data_error = 0;
|
||||
|
||||
if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error))
|
||||
return intmask;
|
||||
|
||||
cqhci_irq(host->mmc, intmask, cmd_error, data_error);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dwcmshc_sdhci_cqe_enable(struct mmc_host *mmc)
|
||||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
u8 ctrl;
|
||||
|
||||
sdhci_writew(host, DWCMSHC_SDHCI_CQE_TRNS_MODE, SDHCI_TRANSFER_MODE);
|
||||
|
||||
sdhci_cqe_enable(mmc);
|
||||
|
||||
/*
|
||||
* The "DesignWare Cores Mobile Storage Host Controller
|
||||
* DWC_mshc / DWC_mshc_lite Databook" says:
|
||||
* when Host Version 4 Enable" is 1 in Host Control 2 register,
|
||||
* SDHCI_CTRL_ADMA32 bit means ADMA2 is selected.
|
||||
* Selection of 32-bit/64-bit System Addressing:
|
||||
* either 32-bit or 64-bit system addressing is selected by
|
||||
* 64-bit Addressing bit in Host Control 2 register.
|
||||
*
|
||||
* On the other hand the "DesignWare Cores Mobile Storage Host
|
||||
* Controller DWC_mshc / DWC_mshc_lite User Guide" says, that we have to
|
||||
* set DMA_SEL to ADMA2 _only_ mode in the Host Control 2 register.
|
||||
*/
|
||||
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
|
||||
ctrl &= ~SDHCI_CTRL_DMA_MASK;
|
||||
ctrl |= SDHCI_CTRL_ADMA32;
|
||||
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
|
||||
}
|
||||
|
||||
static void dwcmshc_set_tran_desc(struct cqhci_host *cq_host, u8 **desc,
|
||||
dma_addr_t addr, int len, bool end, bool dma64)
|
||||
{
|
||||
int tmplen, offset;
|
||||
|
||||
if (likely(!len || BOUNDARY_OK(addr, len))) {
|
||||
cqhci_set_tran_desc(*desc, addr, len, end, dma64);
|
||||
return;
|
||||
}
|
||||
|
||||
offset = addr & (SZ_128M - 1);
|
||||
tmplen = SZ_128M - offset;
|
||||
cqhci_set_tran_desc(*desc, addr, tmplen, false, dma64);
|
||||
|
||||
addr += tmplen;
|
||||
len -= tmplen;
|
||||
*desc += cq_host->trans_desc_len;
|
||||
cqhci_set_tran_desc(*desc, addr, len, end, dma64);
|
||||
}
|
||||
|
||||
static void dwcmshc_cqhci_dumpregs(struct mmc_host *mmc)
|
||||
{
|
||||
sdhci_dumpregs(mmc_priv(mmc));
|
||||
}
|
||||
|
||||
static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
@ -686,6 +784,113 @@ static void cv18xx_sdhci_reset(struct sdhci_host *host, u8 mask)
|
||||
sdhci_writel(host, val, priv->vendor_specific_area1 + CV18XX_SDHCI_PHY_TX_RX_DLY);
|
||||
}
|
||||
|
||||
static void cv18xx_sdhci_set_tap(struct sdhci_host *host, int tap)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||
u16 clk;
|
||||
u32 val;
|
||||
|
||||
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
||||
clk &= ~SDHCI_CLOCK_CARD_EN;
|
||||
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
||||
|
||||
val = sdhci_readl(host, priv->vendor_specific_area1 + CV18XX_SDHCI_MSHC_CTRL);
|
||||
val &= ~CV18XX_LATANCY_1T;
|
||||
sdhci_writel(host, val, priv->vendor_specific_area1 + CV18XX_SDHCI_MSHC_CTRL);
|
||||
|
||||
val = (FIELD_PREP(CV18XX_PHY_TX_DLY_MSK, 0) |
|
||||
FIELD_PREP(CV18XX_PHY_TX_SRC_MSK, CV18XX_PHY_TX_SRC_INVERT_CLK_TX) |
|
||||
FIELD_PREP(CV18XX_PHY_RX_DLY_MSK, tap));
|
||||
sdhci_writel(host, val, priv->vendor_specific_area1 + CV18XX_SDHCI_PHY_TX_RX_DLY);
|
||||
|
||||
sdhci_writel(host, 0, priv->vendor_specific_area1 + CV18XX_SDHCI_PHY_CONFIG);
|
||||
|
||||
clk |= SDHCI_CLOCK_CARD_EN;
|
||||
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
||||
usleep_range(1000, 2000);
|
||||
}
|
||||
|
||||
static int cv18xx_retry_tuning(struct mmc_host *mmc, u32 opcode, int *cmd_error)
|
||||
{
|
||||
int ret, retry = 0;
|
||||
|
||||
while (retry < CV18XX_RETRY_TUNING_MAX) {
|
||||
ret = mmc_send_tuning(mmc, opcode, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
retry++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cv18xx_sdhci_post_tuning(struct sdhci_host *host)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = sdhci_readl(host, SDHCI_INT_STATUS);
|
||||
val |= SDHCI_INT_DATA_AVAIL;
|
||||
sdhci_writel(host, val, SDHCI_INT_STATUS);
|
||||
|
||||
sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
|
||||
}
|
||||
|
||||
static int cv18xx_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode)
|
||||
{
|
||||
int min, max, avg, ret;
|
||||
int win_length, target_min, target_max, target_win_length;
|
||||
|
||||
min = max = 0;
|
||||
target_win_length = 0;
|
||||
|
||||
sdhci_reset_tuning(host);
|
||||
|
||||
while (max < CV18XX_TUNE_MAX) {
|
||||
/* find the mininum delay first which can pass tuning */
|
||||
while (min < CV18XX_TUNE_MAX) {
|
||||
cv18xx_sdhci_set_tap(host, min);
|
||||
if (!cv18xx_retry_tuning(host->mmc, opcode, NULL))
|
||||
break;
|
||||
min += CV18XX_TUNE_STEP;
|
||||
}
|
||||
|
||||
/* find the maxinum delay which can not pass tuning */
|
||||
max = min + CV18XX_TUNE_STEP;
|
||||
while (max < CV18XX_TUNE_MAX) {
|
||||
cv18xx_sdhci_set_tap(host, max);
|
||||
if (cv18xx_retry_tuning(host->mmc, opcode, NULL)) {
|
||||
max -= CV18XX_TUNE_STEP;
|
||||
break;
|
||||
}
|
||||
max += CV18XX_TUNE_STEP;
|
||||
}
|
||||
|
||||
win_length = max - min + 1;
|
||||
/* get the largest pass window */
|
||||
if (win_length > target_win_length) {
|
||||
target_win_length = win_length;
|
||||
target_min = min;
|
||||
target_max = max;
|
||||
}
|
||||
|
||||
/* continue to find the next pass window */
|
||||
min = max + CV18XX_TUNE_STEP;
|
||||
}
|
||||
|
||||
cv18xx_sdhci_post_tuning(host);
|
||||
|
||||
/* use average delay to get the best timing */
|
||||
avg = (target_min + target_max) / 2;
|
||||
cv18xx_sdhci_set_tap(host, avg);
|
||||
ret = mmc_send_tuning(host->mmc, opcode, NULL);
|
||||
|
||||
dev_dbg(mmc_dev(host->mmc), "tuning %s at 0x%x ret %d\n",
|
||||
ret ? "failed" : "passed", avg, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct sdhci_ops sdhci_dwcmshc_ops = {
|
||||
.set_clock = sdhci_set_clock,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
@ -693,6 +898,7 @@ static const struct sdhci_ops sdhci_dwcmshc_ops = {
|
||||
.get_max_clock = dwcmshc_get_max_clock,
|
||||
.reset = sdhci_reset,
|
||||
.adma_write_desc = dwcmshc_adma_write_desc,
|
||||
.irq = dwcmshc_cqe_irq_handler,
|
||||
};
|
||||
|
||||
static const struct sdhci_ops sdhci_dwcmshc_rk35xx_ops = {
|
||||
@ -712,7 +918,7 @@ static const struct sdhci_ops sdhci_dwcmshc_th1520_ops = {
|
||||
.reset = th1520_sdhci_reset,
|
||||
.adma_write_desc = dwcmshc_adma_write_desc,
|
||||
.voltage_switch = dwcmshc_phy_1_8v_init,
|
||||
.platform_execute_tuning = &th1520_execute_tuning,
|
||||
.platform_execute_tuning = th1520_execute_tuning,
|
||||
};
|
||||
|
||||
static const struct sdhci_ops sdhci_dwcmshc_cv18xx_ops = {
|
||||
@ -722,6 +928,7 @@ static const struct sdhci_ops sdhci_dwcmshc_cv18xx_ops = {
|
||||
.get_max_clock = dwcmshc_get_max_clock,
|
||||
.reset = cv18xx_sdhci_reset,
|
||||
.adma_write_desc = dwcmshc_adma_write_desc,
|
||||
.platform_execute_tuning = cv18xx_sdhci_execute_tuning,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_dwcmshc_pdata = {
|
||||
@ -759,6 +966,73 @@ static const struct sdhci_pltfm_data sdhci_dwcmshc_cv18xx_pdata = {
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
||||
};
|
||||
|
||||
static const struct cqhci_host_ops dwcmshc_cqhci_ops = {
|
||||
.enable = dwcmshc_sdhci_cqe_enable,
|
||||
.disable = sdhci_cqe_disable,
|
||||
.dumpregs = dwcmshc_cqhci_dumpregs,
|
||||
.set_tran_desc = dwcmshc_set_tran_desc,
|
||||
};
|
||||
|
||||
static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device *pdev)
|
||||
{
|
||||
struct cqhci_host *cq_host;
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||
bool dma64 = false;
|
||||
u16 clk;
|
||||
int err;
|
||||
|
||||
host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD;
|
||||
cq_host = devm_kzalloc(&pdev->dev, sizeof(*cq_host), GFP_KERNEL);
|
||||
if (!cq_host) {
|
||||
dev_err(mmc_dev(host->mmc), "Unable to setup CQE: not enough memory\n");
|
||||
goto dsbl_cqe_caps;
|
||||
}
|
||||
|
||||
/*
|
||||
* For dwcmshc host controller we have to enable internal clock
|
||||
* before access to some registers from Vendor Specific Area 2.
|
||||
*/
|
||||
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
||||
clk |= SDHCI_CLOCK_INT_EN;
|
||||
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
||||
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
||||
if (!(clk & SDHCI_CLOCK_INT_EN)) {
|
||||
dev_err(mmc_dev(host->mmc), "Unable to setup CQE: internal clock enable error\n");
|
||||
goto free_cq_host;
|
||||
}
|
||||
|
||||
cq_host->mmio = host->ioaddr + priv->vendor_specific_area2;
|
||||
cq_host->ops = &dwcmshc_cqhci_ops;
|
||||
|
||||
/* Enable using of 128-bit task descriptors */
|
||||
dma64 = host->flags & SDHCI_USE_64_BIT_DMA;
|
||||
if (dma64) {
|
||||
dev_dbg(mmc_dev(host->mmc), "128-bit task descriptors\n");
|
||||
cq_host->caps |= CQHCI_TASK_DESC_SZ_128;
|
||||
}
|
||||
err = cqhci_init(cq_host, host->mmc, dma64);
|
||||
if (err) {
|
||||
dev_err(mmc_dev(host->mmc), "Unable to setup CQE: error %d\n", err);
|
||||
goto int_clock_disable;
|
||||
}
|
||||
|
||||
dev_dbg(mmc_dev(host->mmc), "CQE init done\n");
|
||||
|
||||
return;
|
||||
|
||||
int_clock_disable:
|
||||
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
||||
clk &= ~SDHCI_CLOCK_INT_EN;
|
||||
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
||||
|
||||
free_cq_host:
|
||||
devm_kfree(&pdev->dev, cq_host);
|
||||
|
||||
dsbl_cqe_caps:
|
||||
host->mmc->caps2 &= ~(MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD);
|
||||
}
|
||||
|
||||
static int dwcmshc_rk35xx_init(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv)
|
||||
{
|
||||
int err;
|
||||
@ -863,7 +1137,7 @@ static int dwcmshc_probe(struct platform_device *pdev)
|
||||
struct rk35xx_priv *rk_priv = NULL;
|
||||
const struct sdhci_pltfm_data *pltfm_data;
|
||||
int err;
|
||||
u32 extra;
|
||||
u32 extra, caps;
|
||||
|
||||
pltfm_data = device_get_match_data(&pdev->dev);
|
||||
if (!pltfm_data) {
|
||||
@ -914,6 +1188,7 @@ static int dwcmshc_probe(struct platform_device *pdev)
|
||||
|
||||
host->mmc_host_ops.request = dwcmshc_request;
|
||||
host->mmc_host_ops.hs400_enhanced_strobe = dwcmshc_hs400_enhanced_strobe;
|
||||
host->mmc_host_ops.execute_tuning = dwcmshc_execute_tuning;
|
||||
|
||||
if (pltfm_data == &sdhci_dwcmshc_rk35xx_pdata) {
|
||||
rk_priv = devm_kzalloc(&pdev->dev, sizeof(struct rk35xx_priv), GFP_KERNEL);
|
||||
@ -963,6 +1238,10 @@ static int dwcmshc_probe(struct platform_device *pdev)
|
||||
sdhci_enable_v4_mode(host);
|
||||
#endif
|
||||
|
||||
caps = sdhci_readl(host, SDHCI_CAPABILITIES);
|
||||
if (caps & SDHCI_CAN_64BIT_V4)
|
||||
sdhci_enable_v4_mode(host);
|
||||
|
||||
host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
|
||||
|
||||
pm_runtime_get_noresume(dev);
|
||||
@ -973,6 +1252,14 @@ static int dwcmshc_probe(struct platform_device *pdev)
|
||||
if (err)
|
||||
goto err_rpm;
|
||||
|
||||
/* Setup Command Queue Engine if enabled */
|
||||
if (device_property_read_bool(&pdev->dev, "supports-cqe")) {
|
||||
priv->vendor_specific_area2 =
|
||||
sdhci_readw(host, DWCMSHC_P_VENDOR_AREA2);
|
||||
|
||||
dwcmshc_cqhci_init(host, pdev);
|
||||
}
|
||||
|
||||
if (rk_priv)
|
||||
dwcmshc_rk35xx_postinit(host, priv);
|
||||
|
||||
@ -1045,6 +1332,12 @@ static int dwcmshc_suspend(struct device *dev)
|
||||
|
||||
pm_runtime_resume(dev);
|
||||
|
||||
if (host->mmc->caps2 & MMC_CAP2_CQE) {
|
||||
ret = cqhci_suspend(host->mmc);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sdhci_suspend_host(host);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -1089,6 +1382,12 @@ static int dwcmshc_resume(struct device *dev)
|
||||
if (ret)
|
||||
goto disable_rockchip_clks;
|
||||
|
||||
if (host->mmc->caps2 & MMC_CAP2_CQE) {
|
||||
ret = cqhci_resume(host->mmc);
|
||||
if (ret)
|
||||
goto disable_rockchip_clks;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
disable_rockchip_clks:
|
||||
|
@ -925,7 +925,7 @@ static void sdhci_omap_set_timeout(struct sdhci_host *host,
|
||||
__sdhci_set_timeout(host, cmd);
|
||||
}
|
||||
|
||||
static struct sdhci_ops sdhci_omap_ops = {
|
||||
static const struct sdhci_ops sdhci_omap_ops = {
|
||||
.set_clock = sdhci_omap_set_clock,
|
||||
.set_power = sdhci_omap_set_power,
|
||||
.enable_dma = sdhci_omap_enable_dma,
|
||||
|
@ -25,12 +25,6 @@
|
||||
#define GLI_9750_WT_EN_ON 0x1
|
||||
#define GLI_9750_WT_EN_OFF 0x0
|
||||
|
||||
#define PCI_GLI_9750_PM_CTRL 0xFC
|
||||
#define PCI_GLI_9750_PM_STATE GENMASK(1, 0)
|
||||
|
||||
#define PCI_GLI_9750_CORRERR_MASK 0x214
|
||||
#define PCI_GLI_9750_CORRERR_MASK_REPLAY_TIMER_TIMEOUT BIT(12)
|
||||
|
||||
#define SDHCI_GLI_9750_CFG2 0x848
|
||||
#define SDHCI_GLI_9750_CFG2_L1DLY GENMASK(28, 24)
|
||||
#define GLI_9750_CFG2_L1DLY_VALUE 0x1F
|
||||
@ -152,12 +146,6 @@
|
||||
#define PCI_GLI_9755_MISC 0x78
|
||||
#define PCI_GLI_9755_MISC_SSC_OFF BIT(26)
|
||||
|
||||
#define PCI_GLI_9755_PM_CTRL 0xFC
|
||||
#define PCI_GLI_9755_PM_STATE GENMASK(1, 0)
|
||||
|
||||
#define PCI_GLI_9755_CORRERR_MASK 0x214
|
||||
#define PCI_GLI_9755_CORRERR_MASK_REPLAY_TIMER_TIMEOUT BIT(12)
|
||||
|
||||
#define SDHCI_GLI_9767_GM_BURST_SIZE 0x510
|
||||
#define SDHCI_GLI_9767_GM_BURST_SIZE_AXI_ALWAYS_SET BIT(8)
|
||||
|
||||
@ -547,6 +535,7 @@ static void gl9750_hw_setting(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pci_slot *slot = sdhci_priv(host);
|
||||
struct pci_dev *pdev;
|
||||
int aer;
|
||||
u32 value;
|
||||
|
||||
pdev = slot->chip->pdev;
|
||||
@ -561,16 +550,16 @@ static void gl9750_hw_setting(struct sdhci_host *host)
|
||||
sdhci_writel(host, value, SDHCI_GLI_9750_CFG2);
|
||||
|
||||
/* toggle PM state to allow GL9750 to enter ASPM L1.2 */
|
||||
pci_read_config_dword(pdev, PCI_GLI_9750_PM_CTRL, &value);
|
||||
value |= PCI_GLI_9750_PM_STATE;
|
||||
pci_write_config_dword(pdev, PCI_GLI_9750_PM_CTRL, value);
|
||||
value &= ~PCI_GLI_9750_PM_STATE;
|
||||
pci_write_config_dword(pdev, PCI_GLI_9750_PM_CTRL, value);
|
||||
pci_set_power_state(pdev, PCI_D3hot);
|
||||
pci_set_power_state(pdev, PCI_D0);
|
||||
|
||||
/* mask the replay timer timeout of AER */
|
||||
pci_read_config_dword(pdev, PCI_GLI_9750_CORRERR_MASK, &value);
|
||||
value |= PCI_GLI_9750_CORRERR_MASK_REPLAY_TIMER_TIMEOUT;
|
||||
pci_write_config_dword(pdev, PCI_GLI_9750_CORRERR_MASK, value);
|
||||
aer = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR);
|
||||
if (aer) {
|
||||
pci_read_config_dword(pdev, aer + PCI_ERR_COR_MASK, &value);
|
||||
value |= PCI_ERR_COR_REP_TIMER;
|
||||
pci_write_config_dword(pdev, aer + PCI_ERR_COR_MASK, value);
|
||||
}
|
||||
|
||||
gl9750_wt_off(host);
|
||||
}
|
||||
@ -745,6 +734,7 @@ static void sdhci_gl9755_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
static void gl9755_hw_setting(struct sdhci_pci_slot *slot)
|
||||
{
|
||||
struct pci_dev *pdev = slot->chip->pdev;
|
||||
int aer;
|
||||
u32 value;
|
||||
|
||||
gl9755_wt_on(pdev);
|
||||
@ -775,16 +765,16 @@ static void gl9755_hw_setting(struct sdhci_pci_slot *slot)
|
||||
pci_write_config_dword(pdev, PCI_GLI_9755_CFG2, value);
|
||||
|
||||
/* toggle PM state to allow GL9755 to enter ASPM L1.2 */
|
||||
pci_read_config_dword(pdev, PCI_GLI_9755_PM_CTRL, &value);
|
||||
value |= PCI_GLI_9755_PM_STATE;
|
||||
pci_write_config_dword(pdev, PCI_GLI_9755_PM_CTRL, value);
|
||||
value &= ~PCI_GLI_9755_PM_STATE;
|
||||
pci_write_config_dword(pdev, PCI_GLI_9755_PM_CTRL, value);
|
||||
pci_set_power_state(pdev, PCI_D3hot);
|
||||
pci_set_power_state(pdev, PCI_D0);
|
||||
|
||||
/* mask the replay timer timeout of AER */
|
||||
pci_read_config_dword(pdev, PCI_GLI_9755_CORRERR_MASK, &value);
|
||||
value |= PCI_GLI_9755_CORRERR_MASK_REPLAY_TIMER_TIMEOUT;
|
||||
pci_write_config_dword(pdev, PCI_GLI_9755_CORRERR_MASK, value);
|
||||
aer = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR);
|
||||
if (aer) {
|
||||
pci_read_config_dword(pdev, aer + PCI_ERR_COR_MASK, &value);
|
||||
value |= PCI_ERR_COR_REP_TIMER;
|
||||
pci_write_config_dword(pdev, aer + PCI_ERR_COR_MASK, value);
|
||||
}
|
||||
|
||||
gl9755_wt_off(pdev);
|
||||
}
|
||||
|
@ -17,10 +17,8 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
@ -132,14 +130,16 @@ struct sdhci_s3c {
|
||||
* struct sdhci_s3c_drv_data - S3C SDHCI platform specific driver data
|
||||
* @sdhci_quirks: sdhci host specific quirks.
|
||||
* @no_divider: no or non-standard internal clock divider.
|
||||
* @ops: sdhci_ops to use for this variant
|
||||
*
|
||||
* Specifies platform specific configuration of sdhci controller.
|
||||
* Note: A structure for driver specific platform data is used for future
|
||||
* expansion of its usage.
|
||||
*/
|
||||
struct sdhci_s3c_drv_data {
|
||||
unsigned int sdhci_quirks;
|
||||
bool no_divider;
|
||||
unsigned int sdhci_quirks;
|
||||
bool no_divider;
|
||||
const struct sdhci_ops *ops;
|
||||
};
|
||||
|
||||
static inline struct sdhci_s3c *to_s3c(struct sdhci_host *host)
|
||||
@ -414,7 +414,7 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
||||
}
|
||||
|
||||
static struct sdhci_ops sdhci_s3c_ops = {
|
||||
static const struct sdhci_ops sdhci_s3c_ops_s3c6410 = {
|
||||
.get_max_clock = sdhci_s3c_get_max_clk,
|
||||
.set_clock = sdhci_s3c_set_clock,
|
||||
.get_min_clock = sdhci_s3c_get_min_clock,
|
||||
@ -423,6 +423,15 @@ static struct sdhci_ops sdhci_s3c_ops = {
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
};
|
||||
|
||||
static const struct sdhci_ops sdhci_s3c_ops_exynos4 __maybe_unused = {
|
||||
.get_max_clock = sdhci_cmu_get_max_clock,
|
||||
.set_clock = sdhci_cmu_set_clock,
|
||||
.get_min_clock = sdhci_cmu_get_min_clock,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
.reset = sdhci_reset,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static int sdhci_s3c_parse_dt(struct device *dev,
|
||||
struct sdhci_host *host, struct s3c_sdhci_platdata *pdata)
|
||||
@ -446,7 +455,7 @@ static int sdhci_s3c_parse_dt(struct device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (of_get_named_gpio(node, "cd-gpios", 0))
|
||||
if (of_property_present(node, "cd-gpios"))
|
||||
return 0;
|
||||
|
||||
/* assuming internal card detect that will be configured by pinctrl */
|
||||
@ -562,7 +571,7 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
|
||||
pdata->cfg_gpio(pdev, pdata->max_width);
|
||||
|
||||
host->hw_name = "samsung-hsmmc";
|
||||
host->ops = &sdhci_s3c_ops;
|
||||
host->ops = &sdhci_s3c_ops_s3c6410;
|
||||
host->quirks = 0;
|
||||
host->quirks2 = 0;
|
||||
host->irq = irq;
|
||||
@ -572,6 +581,7 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
|
||||
host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT;
|
||||
if (drv_data) {
|
||||
host->quirks |= drv_data->sdhci_quirks;
|
||||
host->ops = drv_data->ops;
|
||||
sc->no_divider = drv_data->no_divider;
|
||||
}
|
||||
|
||||
@ -619,16 +629,6 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
|
||||
/* HSMMC on Samsung SoCs uses SDCLK as timeout clock */
|
||||
host->quirks |= SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK;
|
||||
|
||||
/*
|
||||
* If controller does not have internal clock divider,
|
||||
* we can use overriding functions instead of default.
|
||||
*/
|
||||
if (sc->no_divider) {
|
||||
sdhci_s3c_ops.set_clock = sdhci_cmu_set_clock;
|
||||
sdhci_s3c_ops.get_min_clock = sdhci_cmu_get_min_clock;
|
||||
sdhci_s3c_ops.get_max_clock = sdhci_cmu_get_max_clock;
|
||||
}
|
||||
|
||||
/* It supports additional host capabilities if needed */
|
||||
if (pdata->host_caps)
|
||||
host->mmc->caps |= pdata->host_caps;
|
||||
@ -760,6 +760,7 @@ MODULE_DEVICE_TABLE(platform, sdhci_s3c_driver_ids);
|
||||
#ifdef CONFIG_OF
|
||||
static const struct sdhci_s3c_drv_data exynos4_sdhci_drv_data = {
|
||||
.no_divider = true,
|
||||
.ops = &sdhci_s3c_ops_exynos4,
|
||||
};
|
||||
|
||||
static const struct of_device_id sdhci_s3c_dt_match[] = {
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
@ -440,7 +439,7 @@ static void sdhci_sprd_set_power(struct sdhci_host *host, unsigned char mode,
|
||||
}
|
||||
}
|
||||
|
||||
static struct sdhci_ops sdhci_sprd_ops = {
|
||||
static const struct sdhci_ops sdhci_sprd_ops = {
|
||||
.read_l = sdhci_sprd_readl,
|
||||
.write_l = sdhci_sprd_writel,
|
||||
.write_w = sdhci_sprd_writew,
|
||||
|
@ -3439,12 +3439,18 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
|
||||
host->data->error = -EILSEQ;
|
||||
if (!mmc_op_tuning(SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND))))
|
||||
sdhci_err_stats_inc(host, DAT_CRC);
|
||||
} else if ((intmask & SDHCI_INT_DATA_CRC) &&
|
||||
} else if ((intmask & (SDHCI_INT_DATA_CRC | SDHCI_INT_TUNING_ERROR)) &&
|
||||
SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND))
|
||||
!= MMC_BUS_TEST_R) {
|
||||
host->data->error = -EILSEQ;
|
||||
if (!mmc_op_tuning(SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND))))
|
||||
sdhci_err_stats_inc(host, DAT_CRC);
|
||||
if (intmask & SDHCI_INT_TUNING_ERROR) {
|
||||
u16 ctrl2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
||||
|
||||
ctrl2 &= ~SDHCI_CTRL_TUNED_CLK;
|
||||
sdhci_writew(host, ctrl2, SDHCI_HOST_CONTROL2);
|
||||
}
|
||||
} else if (intmask & SDHCI_INT_ADMA_ERROR) {
|
||||
pr_err("%s: ADMA error: 0x%08x\n", mmc_hostname(host->mmc),
|
||||
intmask);
|
||||
@ -3979,7 +3985,7 @@ bool sdhci_cqe_irq(struct sdhci_host *host, u32 intmask, int *cmd_error,
|
||||
} else
|
||||
*cmd_error = 0;
|
||||
|
||||
if (intmask & (SDHCI_INT_DATA_END_BIT | SDHCI_INT_DATA_CRC)) {
|
||||
if (intmask & (SDHCI_INT_DATA_END_BIT | SDHCI_INT_DATA_CRC | SDHCI_INT_TUNING_ERROR)) {
|
||||
*data_error = -EILSEQ;
|
||||
if (!mmc_op_tuning(SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND))))
|
||||
sdhci_err_stats_inc(host, DAT_CRC);
|
||||
|
@ -158,6 +158,7 @@
|
||||
#define SDHCI_INT_BUS_POWER 0x00800000
|
||||
#define SDHCI_INT_AUTO_CMD_ERR 0x01000000
|
||||
#define SDHCI_INT_ADMA_ERROR 0x02000000
|
||||
#define SDHCI_INT_TUNING_ERROR 0x04000000
|
||||
|
||||
#define SDHCI_INT_NORMAL_MASK 0x00007FFF
|
||||
#define SDHCI_INT_ERROR_MASK 0xFFFF8000
|
||||
@ -169,7 +170,7 @@
|
||||
SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \
|
||||
SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \
|
||||
SDHCI_INT_DATA_END_BIT | SDHCI_INT_ADMA_ERROR | \
|
||||
SDHCI_INT_BLK_GAP)
|
||||
SDHCI_INT_BLK_GAP | SDHCI_INT_TUNING_ERROR)
|
||||
#define SDHCI_INT_ALL_MASK ((unsigned int)-1)
|
||||
|
||||
#define SDHCI_CQE_INT_ERR_MASK ( \
|
||||
|
@ -141,18 +141,26 @@ static const struct timing_data td[] = {
|
||||
|
||||
struct sdhci_am654_data {
|
||||
struct regmap *base;
|
||||
int otap_del_sel[ARRAY_SIZE(td)];
|
||||
int itap_del_sel[ARRAY_SIZE(td)];
|
||||
u32 otap_del_sel[ARRAY_SIZE(td)];
|
||||
u32 itap_del_sel[ARRAY_SIZE(td)];
|
||||
u32 itap_del_ena[ARRAY_SIZE(td)];
|
||||
int clkbuf_sel;
|
||||
int trm_icp;
|
||||
int drv_strength;
|
||||
int strb_sel;
|
||||
u32 flags;
|
||||
u32 quirks;
|
||||
bool dll_enable;
|
||||
|
||||
#define SDHCI_AM654_QUIRK_FORCE_CDTEST BIT(0)
|
||||
};
|
||||
|
||||
struct window {
|
||||
u8 start;
|
||||
u8 end;
|
||||
u8 length;
|
||||
};
|
||||
|
||||
struct sdhci_am654_driver_data {
|
||||
const struct sdhci_pltfm_data *pdata;
|
||||
u32 flags;
|
||||
@ -232,11 +240,13 @@ static void sdhci_am654_setup_dll(struct sdhci_host *host, unsigned int clock)
|
||||
}
|
||||
|
||||
static void sdhci_am654_write_itapdly(struct sdhci_am654_data *sdhci_am654,
|
||||
u32 itapdly)
|
||||
u32 itapdly, u32 enable)
|
||||
{
|
||||
/* Set ITAPCHGWIN before writing to ITAPDLY */
|
||||
regmap_update_bits(sdhci_am654->base, PHY_CTRL4, ITAPCHGWIN_MASK,
|
||||
1 << ITAPCHGWIN_SHIFT);
|
||||
regmap_update_bits(sdhci_am654->base, PHY_CTRL4, ITAPDLYENA_MASK,
|
||||
enable << ITAPDLYENA_SHIFT);
|
||||
regmap_update_bits(sdhci_am654->base, PHY_CTRL4, ITAPDLYSEL_MASK,
|
||||
itapdly << ITAPDLYSEL_SHIFT);
|
||||
regmap_update_bits(sdhci_am654->base, PHY_CTRL4, ITAPCHGWIN_MASK, 0);
|
||||
@ -253,8 +263,8 @@ static void sdhci_am654_setup_delay_chain(struct sdhci_am654_data *sdhci_am654,
|
||||
mask = SELDLYTXCLK_MASK | SELDLYRXCLK_MASK;
|
||||
regmap_update_bits(sdhci_am654->base, PHY_CTRL5, mask, val);
|
||||
|
||||
sdhci_am654_write_itapdly(sdhci_am654,
|
||||
sdhci_am654->itap_del_sel[timing]);
|
||||
sdhci_am654_write_itapdly(sdhci_am654, sdhci_am654->itap_del_sel[timing],
|
||||
sdhci_am654->itap_del_ena[timing]);
|
||||
}
|
||||
|
||||
static void sdhci_am654_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
@ -263,19 +273,17 @@ static void sdhci_am654_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host);
|
||||
unsigned char timing = host->mmc->ios.timing;
|
||||
u32 otap_del_sel;
|
||||
u32 otap_del_ena;
|
||||
u32 mask, val;
|
||||
|
||||
regmap_update_bits(sdhci_am654->base, PHY_CTRL1, ENDLL_MASK, 0);
|
||||
|
||||
sdhci_set_clock(host, clock);
|
||||
|
||||
/* Setup DLL Output TAP delay */
|
||||
/* Setup Output TAP delay */
|
||||
otap_del_sel = sdhci_am654->otap_del_sel[timing];
|
||||
otap_del_ena = (timing > MMC_TIMING_UHS_SDR25) ? 1 : 0;
|
||||
|
||||
mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK;
|
||||
val = (otap_del_ena << OTAPDLYENA_SHIFT) |
|
||||
val = (0x1 << OTAPDLYENA_SHIFT) |
|
||||
(otap_del_sel << OTAPDLYSEL_SHIFT);
|
||||
|
||||
/* Write to STRBSEL for HS400 speed mode */
|
||||
@ -290,10 +298,21 @@ static void sdhci_am654_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
|
||||
regmap_update_bits(sdhci_am654->base, PHY_CTRL4, mask, val);
|
||||
|
||||
if (timing > MMC_TIMING_UHS_SDR25 && clock >= CLOCK_TOO_SLOW_HZ)
|
||||
if (timing > MMC_TIMING_UHS_SDR25 && clock >= CLOCK_TOO_SLOW_HZ) {
|
||||
sdhci_am654_setup_dll(host, clock);
|
||||
else
|
||||
sdhci_am654->dll_enable = true;
|
||||
|
||||
if (timing == MMC_TIMING_MMC_HS400) {
|
||||
sdhci_am654->itap_del_ena[timing] = 0x1;
|
||||
sdhci_am654->itap_del_sel[timing] = sdhci_am654->itap_del_sel[timing - 1];
|
||||
}
|
||||
|
||||
sdhci_am654_write_itapdly(sdhci_am654, sdhci_am654->itap_del_sel[timing],
|
||||
sdhci_am654->itap_del_ena[timing]);
|
||||
} else {
|
||||
sdhci_am654_setup_delay_chain(sdhci_am654, timing);
|
||||
sdhci_am654->dll_enable = false;
|
||||
}
|
||||
|
||||
regmap_update_bits(sdhci_am654->base, PHY_CTRL5, CLKBUFSEL_MASK,
|
||||
sdhci_am654->clkbuf_sel);
|
||||
@ -306,16 +325,29 @@ static void sdhci_j721e_4bit_set_clock(struct sdhci_host *host,
|
||||
struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host);
|
||||
unsigned char timing = host->mmc->ios.timing;
|
||||
u32 otap_del_sel;
|
||||
u32 itap_del_ena;
|
||||
u32 itap_del_sel;
|
||||
u32 mask, val;
|
||||
|
||||
/* Setup DLL Output TAP delay */
|
||||
/* Setup Output TAP delay */
|
||||
otap_del_sel = sdhci_am654->otap_del_sel[timing];
|
||||
|
||||
mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK;
|
||||
val = (0x1 << OTAPDLYENA_SHIFT) |
|
||||
(otap_del_sel << OTAPDLYSEL_SHIFT);
|
||||
regmap_update_bits(sdhci_am654->base, PHY_CTRL4, mask, val);
|
||||
|
||||
/* Setup Input TAP delay */
|
||||
itap_del_ena = sdhci_am654->itap_del_ena[timing];
|
||||
itap_del_sel = sdhci_am654->itap_del_sel[timing];
|
||||
|
||||
mask |= ITAPDLYENA_MASK | ITAPDLYSEL_MASK;
|
||||
val |= (itap_del_ena << ITAPDLYENA_SHIFT) |
|
||||
(itap_del_sel << ITAPDLYSEL_SHIFT);
|
||||
|
||||
regmap_update_bits(sdhci_am654->base, PHY_CTRL4, ITAPCHGWIN_MASK,
|
||||
1 << ITAPCHGWIN_SHIFT);
|
||||
regmap_update_bits(sdhci_am654->base, PHY_CTRL4, mask, val);
|
||||
regmap_update_bits(sdhci_am654->base, PHY_CTRL4, ITAPCHGWIN_MASK, 0);
|
||||
regmap_update_bits(sdhci_am654->base, PHY_CTRL5, CLKBUFSEL_MASK,
|
||||
sdhci_am654->clkbuf_sel);
|
||||
|
||||
@ -408,45 +440,110 @@ static u32 sdhci_am654_cqhci_irq(struct sdhci_host *host, u32 intmask)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ITAP_MAX 32
|
||||
#define ITAPDLY_LENGTH 32
|
||||
#define ITAPDLY_LAST_INDEX (ITAPDLY_LENGTH - 1)
|
||||
|
||||
static u32 sdhci_am654_calculate_itap(struct sdhci_host *host, struct window
|
||||
*fail_window, u8 num_fails, bool circular_buffer)
|
||||
{
|
||||
u8 itap = 0, start_fail = 0, end_fail = 0, pass_length = 0;
|
||||
u8 first_fail_start = 0, last_fail_end = 0;
|
||||
struct device *dev = mmc_dev(host->mmc);
|
||||
struct window pass_window = {0, 0, 0};
|
||||
int prev_fail_end = -1;
|
||||
u8 i;
|
||||
|
||||
if (!num_fails)
|
||||
return ITAPDLY_LAST_INDEX >> 1;
|
||||
|
||||
if (fail_window->length == ITAPDLY_LENGTH) {
|
||||
dev_err(dev, "No passing ITAPDLY, return 0\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
first_fail_start = fail_window->start;
|
||||
last_fail_end = fail_window[num_fails - 1].end;
|
||||
|
||||
for (i = 0; i < num_fails; i++) {
|
||||
start_fail = fail_window[i].start;
|
||||
end_fail = fail_window[i].end;
|
||||
pass_length = start_fail - (prev_fail_end + 1);
|
||||
|
||||
if (pass_length > pass_window.length) {
|
||||
pass_window.start = prev_fail_end + 1;
|
||||
pass_window.length = pass_length;
|
||||
}
|
||||
prev_fail_end = end_fail;
|
||||
}
|
||||
|
||||
if (!circular_buffer)
|
||||
pass_length = ITAPDLY_LAST_INDEX - last_fail_end;
|
||||
else
|
||||
pass_length = ITAPDLY_LAST_INDEX - last_fail_end + first_fail_start;
|
||||
|
||||
if (pass_length > pass_window.length) {
|
||||
pass_window.start = last_fail_end + 1;
|
||||
pass_window.length = pass_length;
|
||||
}
|
||||
|
||||
if (!circular_buffer)
|
||||
itap = pass_window.start + (pass_window.length >> 1);
|
||||
else
|
||||
itap = (pass_window.start + (pass_window.length >> 1)) % ITAPDLY_LENGTH;
|
||||
|
||||
return (itap > ITAPDLY_LAST_INDEX) ? ITAPDLY_LAST_INDEX >> 1 : itap;
|
||||
}
|
||||
|
||||
static int sdhci_am654_platform_execute_tuning(struct sdhci_host *host,
|
||||
u32 opcode)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host);
|
||||
int cur_val, prev_val = 1, fail_len = 0, pass_window = 0, pass_len;
|
||||
u32 itap;
|
||||
unsigned char timing = host->mmc->ios.timing;
|
||||
struct window fail_window[ITAPDLY_LENGTH];
|
||||
u8 curr_pass, itap;
|
||||
u8 fail_index = 0;
|
||||
u8 prev_pass = 1;
|
||||
|
||||
memset(fail_window, 0, sizeof(fail_window));
|
||||
|
||||
/* Enable ITAPDLY */
|
||||
regmap_update_bits(sdhci_am654->base, PHY_CTRL4, ITAPDLYENA_MASK,
|
||||
1 << ITAPDLYENA_SHIFT);
|
||||
sdhci_am654->itap_del_ena[timing] = 0x1;
|
||||
|
||||
for (itap = 0; itap < ITAP_MAX; itap++) {
|
||||
sdhci_am654_write_itapdly(sdhci_am654, itap);
|
||||
for (itap = 0; itap < ITAPDLY_LENGTH; itap++) {
|
||||
sdhci_am654_write_itapdly(sdhci_am654, itap, sdhci_am654->itap_del_ena[timing]);
|
||||
|
||||
cur_val = !mmc_send_tuning(host->mmc, opcode, NULL);
|
||||
if (cur_val && !prev_val)
|
||||
pass_window = itap;
|
||||
curr_pass = !mmc_send_tuning(host->mmc, opcode, NULL);
|
||||
|
||||
if (!cur_val)
|
||||
fail_len++;
|
||||
if (!curr_pass && prev_pass)
|
||||
fail_window[fail_index].start = itap;
|
||||
|
||||
prev_val = cur_val;
|
||||
if (!curr_pass) {
|
||||
fail_window[fail_index].end = itap;
|
||||
fail_window[fail_index].length++;
|
||||
}
|
||||
|
||||
if (curr_pass && !prev_pass)
|
||||
fail_index++;
|
||||
|
||||
prev_pass = curr_pass;
|
||||
}
|
||||
/*
|
||||
* Having determined the length of the failing window and start of
|
||||
* the passing window calculate the length of the passing window and
|
||||
* set the final value halfway through it considering the range as a
|
||||
* circular buffer
|
||||
*/
|
||||
pass_len = ITAP_MAX - fail_len;
|
||||
itap = (pass_window + (pass_len >> 1)) % ITAP_MAX;
|
||||
sdhci_am654_write_itapdly(sdhci_am654, itap);
|
||||
|
||||
if (fail_window[fail_index].length != 0)
|
||||
fail_index++;
|
||||
|
||||
itap = sdhci_am654_calculate_itap(host, fail_window, fail_index,
|
||||
sdhci_am654->dll_enable);
|
||||
|
||||
sdhci_am654_write_itapdly(sdhci_am654, itap, sdhci_am654->itap_del_ena[timing]);
|
||||
|
||||
/* Save ITAPDLY */
|
||||
sdhci_am654->itap_del_sel[timing] = itap;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct sdhci_ops sdhci_am654_ops = {
|
||||
static const struct sdhci_ops sdhci_am654_ops = {
|
||||
.platform_execute_tuning = sdhci_am654_platform_execute_tuning,
|
||||
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
.get_timeout_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
@ -476,7 +573,7 @@ static const struct sdhci_am654_driver_data sdhci_am654_drvdata = {
|
||||
.flags = IOMUX_PRESENT | FREQSEL_2_BIT | STRBSEL_4_BIT | DLL_PRESENT,
|
||||
};
|
||||
|
||||
static struct sdhci_ops sdhci_j721e_8bit_ops = {
|
||||
static const struct sdhci_ops sdhci_j721e_8bit_ops = {
|
||||
.platform_execute_tuning = sdhci_am654_platform_execute_tuning,
|
||||
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
.get_timeout_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
@ -500,7 +597,7 @@ static const struct sdhci_am654_driver_data sdhci_j721e_8bit_drvdata = {
|
||||
.flags = DLL_PRESENT | DLL_CALIB,
|
||||
};
|
||||
|
||||
static struct sdhci_ops sdhci_j721e_4bit_ops = {
|
||||
static const struct sdhci_ops sdhci_j721e_4bit_ops = {
|
||||
.platform_execute_tuning = sdhci_am654_platform_execute_tuning,
|
||||
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
.get_timeout_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
@ -590,9 +687,12 @@ static int sdhci_am654_get_otap_delay(struct sdhci_host *host,
|
||||
host->mmc->caps2 &= ~td[i].capability;
|
||||
}
|
||||
|
||||
if (td[i].itap_binding)
|
||||
device_property_read_u32(dev, td[i].itap_binding,
|
||||
&sdhci_am654->itap_del_sel[i]);
|
||||
if (td[i].itap_binding) {
|
||||
ret = device_property_read_u32(dev, td[i].itap_binding,
|
||||
&sdhci_am654->itap_del_sel[i]);
|
||||
if (!ret)
|
||||
sdhci_am654->itap_del_ena[i] = 0x1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -2667,29 +2667,10 @@ static struct sdio_driver ath10k_sdio_driver = {
|
||||
.probe = ath10k_sdio_probe,
|
||||
.remove = ath10k_sdio_remove,
|
||||
.drv = {
|
||||
.owner = THIS_MODULE,
|
||||
.pm = ATH10K_SDIO_PM_OPS,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init ath10k_sdio_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sdio_register_driver(&ath10k_sdio_driver);
|
||||
if (ret)
|
||||
pr_err("sdio driver registration failed: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit ath10k_sdio_exit(void)
|
||||
{
|
||||
sdio_unregister_driver(&ath10k_sdio_driver);
|
||||
}
|
||||
|
||||
module_init(ath10k_sdio_init);
|
||||
module_exit(ath10k_sdio_exit);
|
||||
module_sdio_driver(ath10k_sdio_driver);
|
||||
|
||||
MODULE_AUTHOR("Qualcomm Atheros");
|
||||
MODULE_DESCRIPTION("Driver support for Qualcomm Atheros 802.11ac WLAN SDIO devices");
|
||||
|
@ -1238,7 +1238,6 @@ static struct sdio_driver brcmf_sdmmc_driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.id_table = brcmf_sdmmc_ids,
|
||||
.drv = {
|
||||
.owner = THIS_MODULE,
|
||||
.pm = pm_sleep_ptr(&brcmf_sdio_pm_ops),
|
||||
.coredump = brcmf_dev_coredump,
|
||||
},
|
||||
|
@ -979,7 +979,6 @@ static struct sdio_driver mwifiex_sdio = {
|
||||
.probe = mwifiex_sdio_probe,
|
||||
.remove = mwifiex_sdio_remove,
|
||||
.drv = {
|
||||
.owner = THIS_MODULE,
|
||||
.coredump = mwifiex_sdio_coredump,
|
||||
.pm = &mwifiex_sdio_pm_ops,
|
||||
}
|
||||
|
@ -267,7 +267,6 @@ struct sdio_driver wfx_sdio_driver = {
|
||||
.probe = wfx_sdio_probe,
|
||||
.remove = wfx_sdio_remove,
|
||||
.drv = {
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = wfx_sdio_of_match,
|
||||
}
|
||||
};
|
||||
|
@ -106,7 +106,10 @@ struct sdio_driver {
|
||||
.class = (dev_class), \
|
||||
.vendor = SDIO_ANY_ID, .device = SDIO_ANY_ID
|
||||
|
||||
extern int sdio_register_driver(struct sdio_driver *);
|
||||
/* use a macro to avoid include chaining to get THIS_MODULE */
|
||||
#define sdio_register_driver(drv) \
|
||||
__sdio_register_driver(drv, THIS_MODULE)
|
||||
extern int __sdio_register_driver(struct sdio_driver *, struct module *);
|
||||
extern void sdio_unregister_driver(struct sdio_driver *);
|
||||
|
||||
/**
|
||||
|
@ -8,8 +8,8 @@
|
||||
#ifndef MMC_SLOT_GPIO_H
|
||||
#define MMC_SLOT_GPIO_H
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/irqreturn.h>
|
||||
|
||||
struct mmc_host;
|
||||
|
||||
@ -21,8 +21,8 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
|
||||
unsigned int debounce);
|
||||
int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
|
||||
unsigned int idx, unsigned int debounce);
|
||||
void mmc_gpio_set_cd_isr(struct mmc_host *host,
|
||||
irqreturn_t (*isr)(int irq, void *dev_id));
|
||||
int mmc_gpiod_set_cd_config(struct mmc_host *host, unsigned long config);
|
||||
void mmc_gpio_set_cd_isr(struct mmc_host *host, irq_handler_t isr);
|
||||
int mmc_gpio_set_cd_wake(struct mmc_host *host, bool on);
|
||||
void mmc_gpiod_request_cd_irq(struct mmc_host *host);
|
||||
bool mmc_can_gpio_cd(struct mmc_host *host);
|
||||
|
Loading…
Reference in New Issue
Block a user