MMC core:

- Export host capabilities through debugfs
  - Export card RCA register via sysfs
  - Improve card initializing sequence while enabling 4-bit bus
  - Export a function to enable/disable wakeup for card detect IRQ
 
 MMC host:
  - dw_mmc: Add support for new hi3798cv200 variant
  - dw_mmc: Remove support for some deprecated DT properties
  - mediatek: Add support for new variant used on MT7622 SoC
  - sdhci: Improve wakeup support for SDIO IRQs
  - sdhci: Improve wakeup support for card detect IRQs
  - sdhci-omap: Add tuning support
  - sdhci_omap: Add UHS-I mode support
  - sunxi: Prepare for runtime PM support via a few re-factorings
  - tmio: deprecate "toshiba,mmc-wrprotect-disable" DT property
  - tmio/renesas_sdhi: Consolidate code supporting write protect
  - tmio: Improve DMA vs PIO handling
  - tmio: Add support for IP-builtin card detection logic
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJaw0MuAAoJEP4mhCVzWIwpW78P/2N2qPt21OfvnIbSexY1nyPd
 5+rjScgy+oTUnNxITA9LOnM4P6Wg/9PgvhjUdNdHzd5ZaXIchw372+Xu6hZHdujI
 vBfnpsI/xtyL1TQ6nvJc9b+Sp4W4LL2MlCqxi+btAKLvTAU3NNm7uJRRP6xRNDd5
 8wcsCoIN4C3k5hmh5++DDLCfuzpwmbC1cEbGtL/QEV9xeCY5+fJq4Qpq14L2TJ0g
 LeDls3X4HAJa3SsgSGOdNr8rttKUSS+j7i1XsShHMyq89DD21PJ8fvMhdzlXJub9
 A4PatcOXALS/Xd2YVjeLHrRRlTitssD+Sllt46NsB4eJJOO+DempecY+i0WoW60y
 p9IUSSX86yokGoyweiL2GI0Ja1Bg8xgdzTGVbfZ6w6UKHjQAcItnojyMghDM5+zt
 VjDIU59rzm+BEDqcktsbGgLPjOZ0ZWgJPPp2NPkechxsojvCJ9Smm6yPjzaUnZ60
 U9PxsYbs4NB+nE9iKDioyA1ABP0N+BloUkTf/cVHdPBBPMmOyI7jYEDS7mqollZU
 bQCFx+BqcQMbnvI/TZxNDTr4HsiyiXJa0GTCB77sge8tZdcUTjUsWyhc+BYpIqgA
 68/dlwTcawSXDliup5XxtYcAjGugtHbPnS/39uBXvscqWVej1WI5RTyzKdC/hnos
 BP5uz0K1Wic1Dl+jqlWc
 =2Pd6
 -----END PGP SIGNATURE-----

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

Pull MMC updates from Ulf Hansson:
 "MMC core:
   - Export host capabilities through debugfs
   - Export card RCA register via sysfs
   - Improve card initializing sequence while enabling 4-bit bus
   - Export a function to enable/disable wakeup for card detect IRQ

  MMC host:
   - dw_mmc: Add support for new hi3798cv200 variant
   - dw_mmc: Remove support for some deprecated DT properties
   - mediatek: Add support for new variant used on MT7622 SoC
   - sdhci: Improve wakeup support for SDIO IRQs
   - sdhci: Improve wakeup support for card detect IRQs
   - sdhci-omap: Add tuning support
   - sdhci_omap: Add UHS-I mode support
   - sunxi: Prepare for runtime PM support via a few re-factorings
   - tmio: deprecate "toshiba,mmc-wrprotect-disable" DT property
   - tmio/renesas_sdhi: Consolidate code supporting write protect
   - tmio: Improve DMA vs PIO handling
   - tmio: Add support for IP-builtin card detection logic"

* tag 'mmc-v4.17' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (55 commits)
  mmc: renesas_sdhi: replace EXT_ACC with HOST_MODE
  mmc: update sdio_claim_irq documentation
  mmc: Export host capabilities to debugfs.
  mmc: core: Disable HPI for certain Micron (Numonyx) eMMC cards
  mmc: block: fix updating ext_csd caches on ioctl call
  mmc: sunxi: Set our device drvdata earlier
  mmc: sunxi: Move the reset deassertion before enabling the clocks
  mmc: sunxi: Move resources management to separate functions
  mmc: dw_mmc: add support for hi3798cv200 specific extensions of dw-mshc
  dt-bindings: mmc: add bindings for hi3798cv200-dw-mshc
  mmc: core: Export card RCA register via sysfs
  mmc: renesas_sdhi: fix WP detection
  mmc: core: Use memdup_user() rather than duplicating its implementation
  mmc: dw_mmc-rockchip: correct property names in debug
  mmc: sd: Remove redundant err assignment from mmc_read_switch
  mmc: sdio: Check the return value of sdio_enable_4bit_bus
  mmc: core: Don't try UHS-I mode if 4-bit mode isn't supported
  arm64: dts: hi3660: remove 'num-slots' property for dwmmc
  ARM: dts: lpc18xx: remove 'num-slots' property for dwmmc
  arm64: dts: stratix10: remove 'num-slots' property for dwmmc
  ...
This commit is contained in:
Linus Torvalds 2018-04-03 12:17:25 -07:00
commit dc73d6a8d4
42 changed files with 885 additions and 277 deletions

View File

@ -0,0 +1,40 @@
* Hisilicon Hi3798CV200 specific extensions to the Synopsys Designware Mobile
Storage Host Controller
Read synopsys-dw-mshc.txt for more details
The Synopsys designware mobile storage host controller is used to interface
a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
differences between the core Synopsys dw mshc controller properties described
by synopsys-dw-mshc.txt and the properties used by the Hisilicon Hi3798CV200
specific extensions to the Synopsys Designware Mobile Storage Host Controller.
Required Properties:
- compatible: Should contain "hisilicon,hi3798cv200-dw-mshc".
- clocks: A list of phandle + clock-specifier pairs for the clocks listed
in clock-names.
- clock-names: Should contain the following:
"ciu" - The ciu clock described in synopsys-dw-mshc.txt.
"biu" - The biu clock described in synopsys-dw-mshc.txt.
"ciu-sample" - Hi3798CV200 extended phase clock for ciu sampling.
"ciu-drive" - Hi3798CV200 extended phase clock for ciu driving.
Example:
emmc: mmc@9830000 {
compatible = "hisilicon,hi3798cv200-dw-mshc";
reg = <0x9830000 0x10000>;
interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&crg HISTB_MMC_CIU_CLK>,
<&crg HISTB_MMC_BIU_CLK>,
<&crg HISTB_MMC_SAMPLE_CLK>,
<&crg HISTB_MMC_DRV_CLK>;
clock-names = "ciu", "biu", "ciu-sample", "ciu-drive";
fifo-depth = <256>;
clock-frequency = <200000000>;
cap-mmc-highspeed;
mmc-ddr-1_8v;
mmc-hs200-1_8v;
non-removable;
bus-width = <8>;
};

View File

@ -12,6 +12,7 @@ Required properties:
"mediatek,mt8173-mmc": for mmc host ip compatible with mt8173
"mediatek,mt2701-mmc": for mmc host ip compatible with mt2701
"mediatek,mt2712-mmc": for mmc host ip compatible with mt2712
"mediatek,mt7622-mmc": for MT7622 SoC
"mediatek,mt7623-mmc", "mediatek,mt2701-mmc": for MT7623 SoC
- reg: physical base address of the controller and length

View File

@ -59,15 +59,6 @@ Optional properties:
is specified and the ciu clock is specified then we'll try to set the ciu
clock to this at probe time.
* clock-freq-min-max (DEPRECATED): Minimum and Maximum clock frequency for card output
clock(cclk_out). If it's not specified, max is 200MHZ and min is 400KHz by default.
(Use the "max-frequency" instead of "clock-freq-min-max".)
* num-slots (DEPRECATED): specifies the number of slots supported by the controller.
The number of physical slots actually used could be equal or less than the
value specified by num-slots. If this property is not specified, the value
of num-slot property is assumed to be 1.
* fifo-depth: The maximum size of the tx/rx fifo's. If this property is not
specified, the default value of the fifo size is determined from the
controller registers.

View File

@ -50,7 +50,6 @@ Required properties:
2: R7S72100
Optional properties:
- toshiba,mmc-wrprotect-disable: write-protect detection is unavailable
- pinctrl-names: should be "default", "state_uhs"
- pinctrl-0: should contain default/high speed pin ctrl
- pinctrl-1: should contain uhs mode pin ctrl

View File

@ -115,7 +115,6 @@
compatible = "snps,dw-mshc";
reg = <0x40004000 0x1000>;
interrupts = <6>;
num-slots = <1>;
clocks = <&ccu2 CLK_SDIO>, <&ccu1 CLK_CPU_SDIO>;
clock-names = "ciu", "biu";
resets = <&rgu 20>;

View File

@ -20,7 +20,6 @@
&mmc {
status = "okay";
num-slots = <1>;
cap-sd-highspeed;
broken-cd;
bus-width = <4>;

View File

@ -30,7 +30,6 @@
};
mmc0: dwmmc0@ff704000 {
num-slots = <1>;
broken-cd;
bus-width = <4>;
cap-mmc-highspeed;

View File

@ -31,7 +31,6 @@
};
mmc0: dwmmc0@ff704000 {
num-slots = <1>;
broken-cd;
bus-width = <4>;
cap-mmc-highspeed;

View File

@ -42,7 +42,6 @@
};
dwmmc0@ff704000 {
num-slots = <1>;
broken-cd;
bus-width = <4>;
cap-mmc-highspeed;

View File

@ -88,7 +88,6 @@
&mmc {
status = "okay";
num-slots = <1>;
cap-sd-highspeed;
broken-cd;
bus-width = <4>;

View File

@ -922,7 +922,6 @@
#size-cells = <0>;
cd-inverted;
compatible = "hisilicon,hi3660-dw-mshc";
num-slots = <1>;
bus-width = <0x4>;
disable-wp;
cap-sd-highspeed;
@ -960,7 +959,6 @@
compatible = "hisilicon,hi3660-dw-mshc";
reg = <0x0 0xff3ff000 0x0 0x1000>;
interrupts = <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>;
num-slots = <1>;
clocks = <&crg_ctrl HI3660_CLK_GATE_SDIO0>,
<&crg_ctrl HI3660_HCLK_GATE_SDIO0>;
clock-names = "ciu", "biu";

View File

@ -375,8 +375,8 @@ static struct resource kfr2r09_sh_sdhi0_resources[] = {
static struct tmio_mmc_data sh7724_sdhi0_data = {
.chan_priv_tx = (void *)SHDMA_SLAVE_SDHI0_TX,
.chan_priv_rx = (void *)SHDMA_SLAVE_SDHI0_RX,
.flags = TMIO_MMC_WRPROTECT_DISABLE,
.capabilities = MMC_CAP_SDIO_IRQ,
.capabilities2 = MMC_CAP2_NO_WRITE_PROTECT,
};
static struct platform_device kfr2r09_sh_sdhi0_device = {

View File

@ -376,22 +376,15 @@ static struct mmc_blk_ioc_data *mmc_blk_ioctl_copy_from_user(
return idata;
}
idata->buf = kmalloc(idata->buf_bytes, GFP_KERNEL);
if (!idata->buf) {
err = -ENOMEM;
idata->buf = memdup_user((void __user *)(unsigned long)
idata->ic.data_ptr, idata->buf_bytes);
if (IS_ERR(idata->buf)) {
err = PTR_ERR(idata->buf);
goto idata_err;
}
if (copy_from_user(idata->buf, (void __user *)(unsigned long)
idata->ic.data_ptr, idata->buf_bytes)) {
err = -EFAULT;
goto copy_err;
}
return idata;
copy_err:
kfree(idata->buf);
idata_err:
kfree(idata);
out:

View File

@ -2369,7 +2369,7 @@ unsigned int mmc_calc_max_discard(struct mmc_card *card)
return card->pref_erase;
max_discard = mmc_do_calc_max_discard(card, MMC_ERASE_ARG);
if (mmc_can_trim(card)) {
if (max_discard && mmc_can_trim(card)) {
max_trim = mmc_do_calc_max_discard(card, MMC_TRIM_ARG);
if (max_trim < max_discard)
max_discard = max_trim;
@ -2655,8 +2655,7 @@ void mmc_start_host(struct mmc_host *host)
void mmc_stop_host(struct mmc_host *host)
{
if (host->slot.cd_irq >= 0) {
if (host->slot.cd_wake_enabled)
disable_irq_wake(host->slot.cd_irq);
mmc_gpio_set_cd_wake(host, false);
disable_irq(host->slot.cd_irq);
}

View File

@ -196,18 +196,7 @@ static int mmc_ios_show(struct seq_file *s, void *data)
return 0;
}
static int mmc_ios_open(struct inode *inode, struct file *file)
{
return single_open(file, mmc_ios_show, inode->i_private);
}
static const struct file_operations mmc_ios_fops = {
.open = mmc_ios_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
DEFINE_SHOW_ATTRIBUTE(mmc_ios);
static int mmc_clock_opt_get(void *data, u64 *val)
{
@ -254,6 +243,12 @@ void mmc_add_host_debugfs(struct mmc_host *host)
if (!debugfs_create_file("ios", S_IRUSR, root, host, &mmc_ios_fops))
goto err_node;
if (!debugfs_create_x32("caps", S_IRUSR, root, &host->caps))
goto err_node;
if (!debugfs_create_x32("caps2", S_IRUSR, root, &host->caps2))
goto err_node;
if (!debugfs_create_file("clock", S_IRUSR | S_IWUSR, root, host,
&mmc_clock_fops))
goto err_node;

View File

@ -56,7 +56,8 @@ static inline int mmc_host_uhs(struct mmc_host *host)
return host->caps &
(MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
MMC_CAP_UHS_DDR50);
MMC_CAP_UHS_DDR50) &&
host->caps & MMC_CAP_4_BIT_DATA;
}
static inline bool mmc_card_hs200(struct mmc_card *card)

View File

@ -792,6 +792,7 @@ MMC_DEV_ATTR(enhanced_area_size, "%u\n", card->ext_csd.enhanced_area_size);
MMC_DEV_ATTR(raw_rpmb_size_mult, "%#x\n", card->ext_csd.raw_rpmb_size_mult);
MMC_DEV_ATTR(rel_sectors, "%#x\n", card->ext_csd.rel_sectors);
MMC_DEV_ATTR(ocr, "0x%08x\n", card->ocr);
MMC_DEV_ATTR(rca, "0x%04x\n", card->rca);
MMC_DEV_ATTR(cmdq_en, "%d\n", card->ext_csd.cmdq_en);
static ssize_t mmc_fwrev_show(struct device *dev,
@ -848,6 +849,7 @@ static struct attribute *mmc_std_attrs[] = {
&dev_attr_raw_rpmb_size_mult.attr,
&dev_attr_rel_sectors.attr,
&dev_attr_ocr.attr,
&dev_attr_rca.attr,
&dev_attr_dsr.attr,
&dev_attr_cmdq_en.attr,
NULL,

View File

@ -291,8 +291,6 @@ static int mmc_read_switch(struct mmc_card *card)
return 0;
}
err = -EIO;
status = kmalloc(64, GFP_KERNEL);
if (!status)
return -ENOMEM;
@ -582,9 +580,6 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
int err;
u8 *status;
if (!card->scr.sda_spec3)
return 0;
if (!(card->csd.cmdclass & CCC_SWITCH))
return 0;
@ -593,14 +588,11 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
return -ENOMEM;
/* Set 4-bit bus width */
if ((card->host->caps & MMC_CAP_4_BIT_DATA) &&
(card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
if (err)
goto out;
err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
if (err)
goto out;
mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
}
mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
/*
* Select the bus speed mode depending on host
@ -676,6 +668,7 @@ MMC_DEV_ATTR(name, "%s\n", card->cid.prod_name);
MMC_DEV_ATTR(oemid, "0x%04x\n", card->cid.oemid);
MMC_DEV_ATTR(serial, "0x%08x\n", card->cid.serial);
MMC_DEV_ATTR(ocr, "0x%08x\n", card->ocr);
MMC_DEV_ATTR(rca, "0x%04x\n", card->rca);
static ssize_t mmc_dsr_show(struct device *dev,
@ -709,6 +702,7 @@ static struct attribute *sd_std_attrs[] = {
&dev_attr_oemid.attr,
&dev_attr_serial.attr,
&dev_attr_ocr.attr,
&dev_attr_rca.attr,
&dev_attr_dsr.attr,
NULL,
};
@ -1033,7 +1027,7 @@ retry:
}
/* Initialization sequence for UHS-I cards */
if (rocr & SD_ROCR_S18A) {
if (rocr & SD_ROCR_S18A && mmc_host_uhs(host)) {
err = mmc_sd_init_uhs_card(card);
if (err)
goto free_card;

View File

@ -518,11 +518,10 @@ static int mmc_sdio_init_uhs_card(struct mmc_card *card)
if (!card->scr.sda_spec3)
return 0;
/*
* Switch to wider bus (if supported).
*/
if (card->host->caps & MMC_CAP_4_BIT_DATA)
err = sdio_enable_4bit_bus(card);
/* Switch to wider bus */
err = sdio_enable_4bit_bus(card);
if (err)
goto out;
/* Set the driver strength for the card */
sdio_select_driver_type(card);

View File

@ -277,8 +277,8 @@ static void sdio_single_irq_set(struct mmc_card *card)
*
* Claim and activate the IRQ for the given SDIO function. The provided
* handler will be called when that IRQ is asserted. The host is always
* claimed already when the handler is called so the handler must not
* call sdio_claim_host() nor sdio_release_host().
* claimed already when the handler is called so the handler should not
* call sdio_claim_host() or sdio_release_host().
*/
int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler)
{

View File

@ -149,11 +149,30 @@ void mmc_gpiod_request_cd_irq(struct mmc_host *host)
if (irq < 0)
host->caps |= MMC_CAP_NEEDS_POLL;
else if ((host->caps & MMC_CAP_CD_WAKE) && !enable_irq_wake(irq))
host->slot.cd_wake_enabled = true;
}
EXPORT_SYMBOL(mmc_gpiod_request_cd_irq);
int mmc_gpio_set_cd_wake(struct mmc_host *host, bool on)
{
int ret = 0;
if (!(host->caps & MMC_CAP_CD_WAKE) ||
host->slot.cd_irq < 0 ||
on == host->slot.cd_wake_enabled)
return 0;
if (on) {
ret = enable_irq_wake(host->slot.cd_irq);
host->slot.cd_wake_enabled = !ret;
} else {
disable_irq_wake(host->slot.cd_irq);
host->slot.cd_wake_enabled = false;
}
return ret;
}
EXPORT_SYMBOL(mmc_gpio_set_cd_wake);
/* Register an alternate interrupt service routine for
* the card-detect GPIO.
*/

View File

@ -699,6 +699,15 @@ config MMC_DW_EXYNOS
Synopsys DesignWare Memory Card Interface driver. Select this option
for platforms based on Exynos4 and Exynos5 SoC's.
config MMC_DW_HI3798CV200
tristate "Hi3798CV200 specific extensions for Synopsys DW Memory Card Interface"
depends on MMC_DW
select MMC_DW_PLTFM
help
This selects support for HiSilicon Hi3798CV200 SoC specific extensions to the
Synopsys DesignWare Memory Card Interface driver. Select this option
for platforms based on HiSilicon Hi3798CV200 SoC.
config MMC_DW_K3
tristate "K3 specific extensions for Synopsys DW Memory Card Interface"
depends on MMC_DW

View File

@ -50,6 +50,7 @@ obj-$(CONFIG_MMC_CAVIUM_THUNDERX) += thunderx-mmc.o
obj-$(CONFIG_MMC_DW) += dw_mmc.o
obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o
obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o
obj-$(CONFIG_MMC_DW_HI3798CV200) += dw_mmc-hi3798cv200.o
obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o
obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o
obj-$(CONFIG_MMC_DW_ROCKCHIP) += dw_mmc-rockchip.o

View File

@ -0,0 +1,202 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2018 HiSilicon Technologies Co., Ltd.
*/
#include <linux/clk.h>
#include <linux/mfd/syscon.h>
#include <linux/mmc/host.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include "dw_mmc.h"
#include "dw_mmc-pltfm.h"
#define ALL_INT_CLR 0x1ffff
struct hi3798cv200_priv {
struct clk *sample_clk;
struct clk *drive_clk;
};
static void dw_mci_hi3798cv200_set_ios(struct dw_mci *host, struct mmc_ios *ios)
{
struct hi3798cv200_priv *priv = host->priv;
u32 val;
val = mci_readl(host, UHS_REG);
if (ios->timing == MMC_TIMING_MMC_DDR52 ||
ios->timing == MMC_TIMING_UHS_DDR50)
val |= SDMMC_UHS_DDR;
else
val &= ~SDMMC_UHS_DDR;
mci_writel(host, UHS_REG, val);
val = mci_readl(host, ENABLE_SHIFT);
if (ios->timing == MMC_TIMING_MMC_DDR52)
val |= SDMMC_ENABLE_PHASE;
else
val &= ~SDMMC_ENABLE_PHASE;
mci_writel(host, ENABLE_SHIFT, val);
val = mci_readl(host, DDR_REG);
if (ios->timing == MMC_TIMING_MMC_HS400)
val |= SDMMC_DDR_HS400;
else
val &= ~SDMMC_DDR_HS400;
mci_writel(host, DDR_REG, val);
if (ios->timing == MMC_TIMING_MMC_HS ||
ios->timing == MMC_TIMING_LEGACY)
clk_set_phase(priv->drive_clk, 180);
else if (ios->timing == MMC_TIMING_MMC_HS200)
clk_set_phase(priv->drive_clk, 135);
}
static int dw_mci_hi3798cv200_execute_tuning(struct dw_mci_slot *slot,
u32 opcode)
{
int degrees[] = { 0, 45, 90, 135, 180, 225, 270, 315 };
struct dw_mci *host = slot->host;
struct hi3798cv200_priv *priv = host->priv;
int raise_point = -1, fall_point = -1;
int err, prev_err = -1;
int found = 0;
int i;
for (i = 0; i < ARRAY_SIZE(degrees); i++) {
clk_set_phase(priv->sample_clk, degrees[i]);
mci_writel(host, RINTSTS, ALL_INT_CLR);
err = mmc_send_tuning(slot->mmc, opcode, NULL);
if (!err)
found = 1;
if (i > 0) {
if (err && !prev_err)
fall_point = i - 1;
if (!err && prev_err)
raise_point = i;
}
if (raise_point != -1 && fall_point != -1)
goto tuning_out;
prev_err = err;
err = 0;
}
tuning_out:
if (found) {
if (raise_point == -1)
raise_point = 0;
if (fall_point == -1)
fall_point = ARRAY_SIZE(degrees) - 1;
if (fall_point < raise_point) {
if ((raise_point + fall_point) >
(ARRAY_SIZE(degrees) - 1))
i = fall_point / 2;
else
i = (raise_point + ARRAY_SIZE(degrees) - 1) / 2;
} else {
i = (raise_point + fall_point) / 2;
}
clk_set_phase(priv->sample_clk, degrees[i]);
dev_dbg(host->dev, "Tuning clk_sample[%d, %d], set[%d]\n",
raise_point, fall_point, degrees[i]);
} else {
dev_err(host->dev, "No valid clk_sample shift! use default\n");
err = -EINVAL;
}
mci_writel(host, RINTSTS, ALL_INT_CLR);
return err;
}
static int dw_mci_hi3798cv200_init(struct dw_mci *host)
{
struct hi3798cv200_priv *priv;
int ret;
priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->sample_clk = devm_clk_get(host->dev, "ciu-sample");
if (IS_ERR(priv->sample_clk)) {
dev_err(host->dev, "failed to get ciu-sample clock\n");
return PTR_ERR(priv->sample_clk);
}
priv->drive_clk = devm_clk_get(host->dev, "ciu-drive");
if (IS_ERR(priv->drive_clk)) {
dev_err(host->dev, "failed to get ciu-drive clock\n");
return PTR_ERR(priv->drive_clk);
}
ret = clk_prepare_enable(priv->sample_clk);
if (ret) {
dev_err(host->dev, "failed to enable ciu-sample clock\n");
return ret;
}
ret = clk_prepare_enable(priv->drive_clk);
if (ret) {
dev_err(host->dev, "failed to enable ciu-drive clock\n");
goto disable_sample_clk;
}
host->priv = priv;
return 0;
disable_sample_clk:
clk_disable_unprepare(priv->sample_clk);
return ret;
}
static const struct dw_mci_drv_data hi3798cv200_data = {
.init = dw_mci_hi3798cv200_init,
.set_ios = dw_mci_hi3798cv200_set_ios,
.execute_tuning = dw_mci_hi3798cv200_execute_tuning,
};
static int dw_mci_hi3798cv200_probe(struct platform_device *pdev)
{
return dw_mci_pltfm_register(pdev, &hi3798cv200_data);
}
static int dw_mci_hi3798cv200_remove(struct platform_device *pdev)
{
struct dw_mci *host = platform_get_drvdata(pdev);
struct hi3798cv200_priv *priv = host->priv;
clk_disable_unprepare(priv->drive_clk);
clk_disable_unprepare(priv->sample_clk);
return dw_mci_pltfm_remove(pdev);
}
static const struct of_device_id dw_mci_hi3798cv200_match[] = {
{ .compatible = "hisilicon,hi3798cv200-dw-mshc", },
{},
};
MODULE_DEVICE_TABLE(of, dw_mci_hi3798cv200_match);
static struct platform_driver dw_mci_hi3798cv200_driver = {
.probe = dw_mci_hi3798cv200_probe,
.remove = dw_mci_hi3798cv200_remove,
.driver = {
.name = "dwmmc_hi3798cv200",
.of_match_table = dw_mci_hi3798cv200_match,
},
};
module_platform_driver(dw_mci_hi3798cv200_driver);
MODULE_DESCRIPTION("HiSilicon Hi3798CV200 Specific DW-MSHC Driver Extension");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:dwmmc_hi3798cv200");

View File

@ -29,7 +29,6 @@
MMC_CAP_SDIO_IRQ)
static struct dw_mci_board pci_board_data = {
.num_slots = 1,
.caps = DW_MCI_CAPABILITIES,
.bus_hz = 33 * 1000 * 1000,
.detect_delay_ms = 200,

View File

@ -282,11 +282,11 @@ static int dw_mci_rk3288_parse_dt(struct dw_mci *host)
priv->drv_clk = devm_clk_get(host->dev, "ciu-drive");
if (IS_ERR(priv->drv_clk))
dev_dbg(host->dev, "ciu_drv not available\n");
dev_dbg(host->dev, "ciu-drive not available\n");
priv->sample_clk = devm_clk_get(host->dev, "ciu-sample");
if (IS_ERR(priv->sample_clk))
dev_dbg(host->dev, "ciu_sample not available\n");
dev_dbg(host->dev, "ciu-sample not available\n");
host->priv = priv;

View File

@ -147,19 +147,7 @@ static int dw_mci_req_show(struct seq_file *s, void *v)
return 0;
}
static int dw_mci_req_open(struct inode *inode, struct file *file)
{
return single_open(file, dw_mci_req_show, inode->i_private);
}
static const struct file_operations dw_mci_req_fops = {
.owner = THIS_MODULE,
.open = dw_mci_req_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
DEFINE_SHOW_ATTRIBUTE(dw_mci_req);
static int dw_mci_regs_show(struct seq_file *s, void *v)
{
@ -178,19 +166,7 @@ static int dw_mci_regs_show(struct seq_file *s, void *v)
return 0;
}
static int dw_mci_regs_open(struct inode *inode, struct file *file)
{
return single_open(file, dw_mci_regs_show, inode->i_private);
}
static const struct file_operations dw_mci_regs_fops = {
.owner = THIS_MODULE,
.open = dw_mci_regs_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
DEFINE_SHOW_ATTRIBUTE(dw_mci_regs);
static void dw_mci_init_debugfs(struct dw_mci_slot *slot)
{
@ -2030,7 +2006,6 @@ static void dw_mci_tasklet_func(unsigned long priv)
set_bit(EVENT_CMD_COMPLETE, &host->completed_events);
err = dw_mci_command_complete(host, cmd);
if (cmd == mrq->sbc && !err) {
prev_state = state = STATE_SENDING_CMD;
__dw_mci_start_request(host, host->slot,
mrq->cmd);
goto unlock;
@ -2826,6 +2801,10 @@ static int dw_mci_init_slot_caps(struct dw_mci_slot *slot)
if (host->pdata->caps2)
mmc->caps2 = host->pdata->caps2;
mmc->f_min = DW_MCI_FREQ_MIN;
if (!mmc->f_max)
mmc->f_max = DW_MCI_FREQ_MAX;
/* Process SDIO IRQs through the sdio_irq_work. */
if (mmc->caps & MMC_CAP_SDIO_IRQ)
mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
@ -2838,7 +2817,6 @@ static int dw_mci_init_slot(struct dw_mci *host)
struct mmc_host *mmc;
struct dw_mci_slot *slot;
int ret;
u32 freq[2];
mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);
if (!mmc)
@ -2852,16 +2830,6 @@ static int dw_mci_init_slot(struct dw_mci *host)
host->slot = slot;
mmc->ops = &dw_mci_ops;
if (device_property_read_u32_array(host->dev, "clock-freq-min-max",
freq, 2)) {
mmc->f_min = DW_MCI_FREQ_MIN;
mmc->f_max = DW_MCI_FREQ_MAX;
} else {
dev_info(host->dev,
"'clock-freq-min-max' property was deprecated.\n");
mmc->f_min = freq[0];
mmc->f_max = freq[1];
}
/*if there are external regulators, get them*/
ret = mmc_regulator_get_supply(mmc);
@ -3160,10 +3128,6 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
return ERR_PTR(-EPROBE_DEFER);
}
/* find out number of slots supported */
if (!device_property_read_u32(dev, "num-slots", &pdata->num_slots))
dev_info(dev, "'num-slots' was deprecated.\n");
if (device_property_read_u32(dev, "fifo-depth", &pdata->fifo_depth))
dev_info(dev,
"fifo-depth property not found, using value of FIFOTH register as default\n");

View File

@ -65,8 +65,7 @@ struct dw_mci_dma_slave {
* @fifo_reg: Pointer to MMIO registers for data FIFO
* @sg: Scatterlist entry currently being processed by PIO code, if any.
* @sg_miter: PIO mapping scatterlist iterator.
* @cur_slot: The slot which is currently using the controller.
* @mrq: The request currently being processed on @cur_slot,
* @mrq: The request currently being processed on @slot,
* or NULL if the controller is idle.
* @cmd: The command currently being sent to the card, or NULL.
* @data: The data currently being transferred, or NULL if no data
@ -102,7 +101,6 @@ struct dw_mci_dma_slave {
* @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus
* rate and timeout calculations.
* @current_speed: Configured rate of the controller.
* @num_slots: Number of slots available.
* @fifoth_val: The value of FIFOTH register.
* @verid: Denote Version ID.
* @dev: Device associated with the MMC controller.
@ -134,17 +132,17 @@ struct dw_mci_dma_slave {
* =======
*
* @lock is a softirq-safe spinlock protecting @queue as well as
* @slot, @mrq and @state. These must always be updated
* at the same time while holding @lock.
* The @mrq field of struct dw_mci_slot is also protected by @lock,
* and must always be written at the same time as the slot is added to
* @queue.
*
* @irq_lock is an irq-safe spinlock protecting the INTMASK register
* to allow the interrupt handler to modify it directly. Held for only long
* enough to read-modify-write INTMASK and no other locks are grabbed when
* holding this one.
*
* The @mrq field of struct dw_mci_slot is also protected by @lock,
* and must always be written at the same time as the slot is added to
* @queue.
*
* @pending_events and @completed_events are accessed using atomic bit
* operations, so they don't need any locking.
*
@ -253,8 +251,6 @@ struct dma_pdata;
/* Board platform data */
struct dw_mci_board {
u32 num_slots;
unsigned int bus_hz; /* Clock speed at the cclk_in pad */
u32 caps; /* Capabilities */
@ -318,11 +314,12 @@ struct dw_mci_board {
#define SDMMC_BUFADDR 0x098
#define SDMMC_CDTHRCTL 0x100
#define SDMMC_UHS_REG_EXT 0x108
#define SDMMC_DDR_REG 0x10c
#define SDMMC_ENABLE_SHIFT 0x110
#define SDMMC_DATA(x) (x)
/*
* Registers to support idmac 64-bit address mode
*/
* Registers to support idmac 64-bit address mode
*/
#define SDMMC_DBADDRL 0x088
#define SDMMC_DBADDRU 0x08c
#define SDMMC_IDSTS64 0x090
@ -443,13 +440,19 @@ struct dw_mci_board {
#define SDMMC_CARD_WR_THR_EN BIT(2)
#define SDMMC_CARD_RD_THR_EN BIT(0)
/* UHS-1 register defines */
#define SDMMC_UHS_DDR BIT(16)
#define SDMMC_UHS_18V BIT(0)
/* DDR register defines */
#define SDMMC_DDR_HS400 BIT(31)
/* Enable shift register defines */
#define SDMMC_ENABLE_PHASE BIT(0)
/* All ctrl reset bits */
#define SDMMC_CTRL_ALL_RESET_FLAGS \
(SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET)
/* FIFO register access macros. These should not change the data endian-ness
* as they are written to memory to be dealt with by the upper layers */
* as they are written to memory to be dealt with by the upper layers
*/
#define mci_fifo_readw(__reg) __raw_readw(__reg)
#define mci_fifo_readl(__reg) __raw_readl(__reg)
#define mci_fifo_readq(__reg) __raw_readq(__reg)

View File

@ -438,11 +438,23 @@ static const struct mtk_mmc_compatible mt2712_compat = {
.enhance_rx = true,
};
static const struct mtk_mmc_compatible mt7622_compat = {
.clk_div_bits = 12,
.hs400_tune = false,
.pad_tune_reg = MSDC_PAD_TUNE0,
.async_fifo = true,
.data_tune = true,
.busy_check = true,
.stop_clk_fix = true,
.enhance_rx = true,
};
static const struct of_device_id msdc_of_ids[] = {
{ .compatible = "mediatek,mt8135-mmc", .data = &mt8135_compat},
{ .compatible = "mediatek,mt8173-mmc", .data = &mt8173_compat},
{ .compatible = "mediatek,mt2701-mmc", .data = &mt2701_compat},
{ .compatible = "mediatek,mt2712-mmc", .data = &mt2712_compat},
{ .compatible = "mediatek,mt7622-mmc", .data = &mt7622_compat},
{}
};
MODULE_DEVICE_TABLE(of, msdc_of_ids);

View File

@ -38,7 +38,7 @@
#include "renesas_sdhi.h"
#include "tmio_mmc.h"
#define EXT_ACC 0xe4
#define HOST_MODE 0xe4
#define SDHI_VER_GEN2_SDR50 0x490c
#define SDHI_VER_RZ_A1 0x820b
@ -76,7 +76,7 @@ static void renesas_sdhi_sdbuf_width(struct tmio_mmc_host *host, int width)
return;
}
sd_ctrl_write16(host, EXT_ACC, val);
sd_ctrl_write16(host, HOST_MODE, val);
}
static int renesas_sdhi_clk_enable(struct tmio_mmc_host *host)
@ -417,7 +417,7 @@ static int renesas_sdhi_write16_hook(struct tmio_mmc_host *host, int addr)
case CTL_SD_MEM_CARD_OPT:
case CTL_TRANSACTION_CTL:
case CTL_DMA_ENABLE:
case EXT_ACC:
case HOST_MODE:
if (host->pdata->flags & TMIO_MMC_HAVE_CBSY)
bit = TMIO_STAT_CMD_BUSY;
/* fallthrough */

View File

@ -71,9 +71,8 @@ static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = {
};
static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = {
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
TMIO_MMC_CLK_ACTUAL | TMIO_MMC_HAVE_CBSY |
TMIO_MMC_MIN_RCAR2,
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
MMC_CAP_CMD23,
.bus_shift = 2,
@ -145,7 +144,6 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host,
u32 dtran_mode = DTRAN_MODE_BUS_WID_TH | DTRAN_MODE_ADDR_MODE;
enum dma_data_direction dir;
int ret;
u32 irq_mask;
/* This DMAC cannot handle if sg_len is not 1 */
WARN_ON(host->sg_len > 1);
@ -157,11 +155,9 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host,
if (data->flags & MMC_DATA_READ) {
dtran_mode |= DTRAN_MODE_CH_NUM_CH1;
dir = DMA_FROM_DEVICE;
irq_mask = TMIO_STAT_RXRDY;
} else {
dtran_mode |= DTRAN_MODE_CH_NUM_CH0;
dir = DMA_TO_DEVICE;
irq_mask = TMIO_STAT_TXRQ;
}
ret = dma_map_sg(&host->pdev->dev, sg, host->sg_len, dir);
@ -170,9 +166,6 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host,
renesas_sdhi_internal_dmac_enable_dma(host, true);
/* disable PIO irqs to avoid "PIO IRQ in DMA mode!" */
tmio_mmc_disable_mmc_irqs(host, irq_mask);
/* set dma parameters */
renesas_sdhi_internal_dmac_dm_write(host, DM_CM_DTRAN_MODE,
dtran_mode);

View File

@ -40,8 +40,7 @@ static const struct renesas_sdhi_of_data of_rz_compatible = {
};
static const struct renesas_sdhi_of_data of_rcar_gen1_compatible = {
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
TMIO_MMC_CLK_ACTUAL,
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL,
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
};
@ -58,9 +57,8 @@ static struct renesas_sdhi_scc rcar_gen2_scc_taps[] = {
};
static const struct renesas_sdhi_of_data of_rcar_gen2_compatible = {
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
TMIO_MMC_CLK_ACTUAL | TMIO_MMC_HAVE_CBSY |
TMIO_MMC_MIN_RCAR2,
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
MMC_CAP_CMD23,
.dma_buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES,
@ -79,9 +77,8 @@ static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = {
};
static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = {
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
TMIO_MMC_CLK_ACTUAL | TMIO_MMC_HAVE_CBSY |
TMIO_MMC_MIN_RCAR2,
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
MMC_CAP_CMD23,
.bus_shift = 2,
@ -205,8 +202,6 @@ static void renesas_sdhi_sys_dmac_start_dma_rx(struct tmio_mmc_host *host)
return;
}
tmio_mmc_disable_mmc_irqs(host, TMIO_STAT_RXRDY);
/* The only sg element can be unaligned, use our bounce buffer then */
if (!aligned) {
sg_init_one(&host->bounce_sg, host->bounce_buf, sg->length);
@ -280,8 +275,6 @@ static void renesas_sdhi_sys_dmac_start_dma_tx(struct tmio_mmc_host *host)
return;
}
tmio_mmc_disable_mmc_irqs(host, TMIO_STAT_TXRQ);
/* The only sg element can be unaligned, use our bounce buffer then */
if (!aligned) {
unsigned long flags;

View File

@ -214,6 +214,7 @@ static const struct sdhci_pltfm_data sdhci_bcm2835_pltfm_data = {
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
SDHCI_QUIRK_MISSING_CAPS |
SDHCI_QUIRK_NO_HISPD_BIT,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
.ops = &sdhci_iproc_32only_ops,
};

View File

@ -25,17 +25,32 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/pinctrl/consumer.h>
#include "sdhci-pltfm.h"
#define SDHCI_OMAP_CON 0x12c
#define CON_DW8 BIT(5)
#define CON_DMA_MASTER BIT(20)
#define CON_DDR BIT(19)
#define CON_CLKEXTFREE BIT(16)
#define CON_PADEN BIT(15)
#define CON_INIT BIT(1)
#define CON_OD BIT(0)
#define SDHCI_OMAP_DLL 0x0134
#define DLL_SWT BIT(20)
#define DLL_FORCE_SR_C_SHIFT 13
#define DLL_FORCE_SR_C_MASK (0x7f << DLL_FORCE_SR_C_SHIFT)
#define DLL_FORCE_VALUE BIT(12)
#define DLL_CALIB BIT(1)
#define SDHCI_OMAP_CMD 0x20c
#define SDHCI_OMAP_PSTATE 0x0224
#define PSTATE_DLEV_DAT0 BIT(20)
#define PSTATE_DATI BIT(1)
#define SDHCI_OMAP_HCTL 0x228
#define HCTL_SDBP BIT(8)
#define HCTL_SDVS_SHIFT 9
@ -56,12 +71,16 @@
#define SDHCI_OMAP_AC12 0x23c
#define AC12_V1V8_SIGEN BIT(19)
#define AC12_SCLK_SEL BIT(23)
#define SDHCI_OMAP_CAPA 0x240
#define CAPA_VS33 BIT(24)
#define CAPA_VS30 BIT(25)
#define CAPA_VS18 BIT(26)
#define SDHCI_OMAP_CAPA2 0x0244
#define CAPA2_TSDR50 BIT(13)
#define SDHCI_OMAP_TIMEOUT 1 /* 1 msec */
#define SYSCTL_CLKD_MAX 0x3FF
@ -70,8 +89,14 @@
#define IOV_3V0 3000000 /* 300000 uV */
#define IOV_3V3 3300000 /* 330000 uV */
#define MAX_PHASE_DELAY 0x7C
/* sdhci-omap controller flags */
#define SDHCI_OMAP_REQUIRE_IODELAY BIT(0)
struct sdhci_omap_data {
u32 offset;
u8 flags;
};
struct sdhci_omap_host {
@ -82,8 +107,16 @@ struct sdhci_omap_host {
struct sdhci_host *host;
u8 bus_mode;
u8 power_mode;
u8 timing;
u8 flags;
struct pinctrl *pinctrl;
struct pinctrl_state **pinctrl_state;
};
static void sdhci_omap_start_clock(struct sdhci_omap_host *omap_host);
static void sdhci_omap_stop_clock(struct sdhci_omap_host *omap_host);
static inline u32 sdhci_omap_readl(struct sdhci_omap_host *host,
unsigned int offset)
{
@ -191,6 +224,178 @@ static void sdhci_omap_conf_bus_power(struct sdhci_omap_host *omap_host,
}
}
static inline void sdhci_omap_set_dll(struct sdhci_omap_host *omap_host,
int count)
{
int i;
u32 reg;
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_DLL);
reg |= DLL_FORCE_VALUE;
reg &= ~DLL_FORCE_SR_C_MASK;
reg |= (count << DLL_FORCE_SR_C_SHIFT);
sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg);
reg |= DLL_CALIB;
sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg);
for (i = 0; i < 1000; i++) {
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_DLL);
if (reg & DLL_CALIB)
break;
}
reg &= ~DLL_CALIB;
sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg);
}
static void sdhci_omap_disable_tuning(struct sdhci_omap_host *omap_host)
{
u32 reg;
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12);
reg &= ~AC12_SCLK_SEL;
sdhci_omap_writel(omap_host, SDHCI_OMAP_AC12, reg);
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_DLL);
reg &= ~(DLL_FORCE_VALUE | DLL_SWT);
sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg);
}
static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode)
{
struct sdhci_host *host = mmc_priv(mmc);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
struct device *dev = omap_host->dev;
struct mmc_ios *ios = &mmc->ios;
u32 start_window = 0, max_window = 0;
u8 cur_match, prev_match = 0;
u32 length = 0, max_len = 0;
u32 ier = host->ier;
u32 phase_delay = 0;
int ret = 0;
u32 reg;
pltfm_host = sdhci_priv(host);
omap_host = sdhci_pltfm_priv(pltfm_host);
dev = omap_host->dev;
/* clock tuning is not needed for upto 52MHz */
if (ios->clock <= 52000000)
return 0;
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA2);
if (ios->timing == MMC_TIMING_UHS_SDR50 && !(reg & CAPA2_TSDR50))
return 0;
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_DLL);
reg |= DLL_SWT;
sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg);
/*
* OMAP5/DRA74X/DRA72x Errata i802:
* DCRC error interrupts (MMCHS_STAT[21] DCRC=0x1) can occur
* during the tuning procedure. So disable it during the
* tuning procedure.
*/
ier &= ~SDHCI_INT_DATA_CRC;
sdhci_writel(host, ier, SDHCI_INT_ENABLE);
sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE);
while (phase_delay <= MAX_PHASE_DELAY) {
sdhci_omap_set_dll(omap_host, phase_delay);
cur_match = !mmc_send_tuning(mmc, opcode, NULL);
if (cur_match) {
if (prev_match) {
length++;
} else {
start_window = phase_delay;
length = 1;
}
}
if (length > max_len) {
max_window = start_window;
max_len = length;
}
prev_match = cur_match;
phase_delay += 4;
}
if (!max_len) {
dev_err(dev, "Unable to find match\n");
ret = -EIO;
goto tuning_error;
}
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12);
if (!(reg & AC12_SCLK_SEL)) {
ret = -EIO;
goto tuning_error;
}
phase_delay = max_window + 4 * (max_len >> 1);
sdhci_omap_set_dll(omap_host, phase_delay);
goto ret;
tuning_error:
dev_err(dev, "Tuning failed\n");
sdhci_omap_disable_tuning(omap_host);
ret:
sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
return ret;
}
static int sdhci_omap_card_busy(struct mmc_host *mmc)
{
u32 reg, ac12;
int ret = false;
struct sdhci_host *host = mmc_priv(mmc);
struct sdhci_pltfm_host *pltfm_host;
struct sdhci_omap_host *omap_host;
u32 ier = host->ier;
pltfm_host = sdhci_priv(host);
omap_host = sdhci_pltfm_priv(pltfm_host);
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
ac12 = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12);
reg &= ~CON_CLKEXTFREE;
if (ac12 & AC12_V1V8_SIGEN)
reg |= CON_CLKEXTFREE;
reg |= CON_PADEN;
sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
disable_irq(host->irq);
ier |= SDHCI_INT_CARD_INT;
sdhci_writel(host, ier, SDHCI_INT_ENABLE);
sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE);
/*
* Delay is required for PSTATE to correctly reflect
* DLEV/CLEV values after PADEN is set.
*/
usleep_range(50, 100);
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_PSTATE);
if ((reg & PSTATE_DATI) || !(reg & PSTATE_DLEV_DAT0))
ret = true;
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
reg &= ~(CON_CLKEXTFREE | CON_PADEN);
sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
enable_irq(host->irq);
return ret;
}
static int sdhci_omap_start_signal_voltage_switch(struct mmc_host *mmc,
struct mmc_ios *ios)
{
@ -244,6 +449,39 @@ static int sdhci_omap_start_signal_voltage_switch(struct mmc_host *mmc,
return 0;
}
static void sdhci_omap_set_timing(struct sdhci_omap_host *omap_host, u8 timing)
{
int ret;
struct pinctrl_state *pinctrl_state;
struct device *dev = omap_host->dev;
if (!(omap_host->flags & SDHCI_OMAP_REQUIRE_IODELAY))
return;
if (omap_host->timing == timing)
return;
sdhci_omap_stop_clock(omap_host);
pinctrl_state = omap_host->pinctrl_state[timing];
ret = pinctrl_select_state(omap_host->pinctrl, pinctrl_state);
if (ret) {
dev_err(dev, "failed to select pinctrl state\n");
return;
}
sdhci_omap_start_clock(omap_host);
omap_host->timing = timing;
}
static void sdhci_omap_set_power_mode(struct sdhci_omap_host *omap_host,
u8 power_mode)
{
if (omap_host->bus_mode == MMC_POWER_OFF)
sdhci_omap_disable_tuning(omap_host);
omap_host->power_mode = power_mode;
}
static void sdhci_omap_set_bus_mode(struct sdhci_omap_host *omap_host,
unsigned int mode)
{
@ -272,7 +510,9 @@ static void sdhci_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
omap_host = sdhci_pltfm_priv(pltfm_host);
sdhci_omap_set_bus_mode(omap_host, ios->bus_mode);
sdhci_omap_set_timing(omap_host, ios->timing);
sdhci_set_ios(mmc, ios);
sdhci_omap_set_power_mode(omap_host, ios->power_mode);
}
static u16 sdhci_omap_calc_divisor(struct sdhci_pltfm_host *host,
@ -401,8 +641,26 @@ static void sdhci_omap_init_74_clocks(struct sdhci_host *host, u8 power_mode)
sdhci_omap_writel(omap_host, SDHCI_OMAP_STAT, INT_CC_EN);
enable_irq(host->irq);
}
omap_host->power_mode = power_mode;
static void sdhci_omap_set_uhs_signaling(struct sdhci_host *host,
unsigned int timing)
{
u32 reg;
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
sdhci_omap_stop_clock(omap_host);
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
if (timing == MMC_TIMING_UHS_DDR50 || timing == MMC_TIMING_MMC_DDR52)
reg |= CON_DDR;
else
reg &= ~CON_DDR;
sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
sdhci_set_uhs_signaling(host, timing);
sdhci_omap_start_clock(omap_host);
}
static struct sdhci_ops sdhci_omap_ops = {
@ -414,7 +672,7 @@ static struct sdhci_ops sdhci_omap_ops = {
.set_bus_width = sdhci_omap_set_bus_width,
.platform_send_init_74_clocks = sdhci_omap_init_74_clocks,
.reset = sdhci_reset,
.set_uhs_signaling = sdhci_set_uhs_signaling,
.set_uhs_signaling = sdhci_omap_set_uhs_signaling,
};
static int sdhci_omap_set_capabilities(struct sdhci_omap_host *omap_host)
@ -453,14 +711,15 @@ static const struct sdhci_pltfm_data sdhci_omap_pdata = {
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
SDHCI_QUIRK_NO_HISPD_BIT |
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
.quirks2 = SDHCI_QUIRK2_NO_1_8_V |
SDHCI_QUIRK2_ACMD23_BROKEN |
.quirks2 = SDHCI_QUIRK2_ACMD23_BROKEN |
SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
SDHCI_QUIRK2_RSP_136_HAS_CRC,
.ops = &sdhci_omap_ops,
};
static const struct sdhci_omap_data dra7_data = {
.offset = 0x200,
.flags = SDHCI_OMAP_REQUIRE_IODELAY,
};
static const struct of_device_id omap_sdhci_match[] = {
@ -469,6 +728,108 @@ static const struct of_device_id omap_sdhci_match[] = {
};
MODULE_DEVICE_TABLE(of, omap_sdhci_match);
static struct pinctrl_state
*sdhci_omap_iodelay_pinctrl_state(struct sdhci_omap_host *omap_host, char *mode,
u32 *caps, u32 capmask)
{
struct device *dev = omap_host->dev;
struct pinctrl_state *pinctrl_state = ERR_PTR(-ENODEV);
if (!(*caps & capmask))
goto ret;
pinctrl_state = pinctrl_lookup_state(omap_host->pinctrl, mode);
if (IS_ERR(pinctrl_state)) {
dev_err(dev, "no pinctrl state for %s mode", mode);
*caps &= ~capmask;
}
ret:
return pinctrl_state;
}
static int sdhci_omap_config_iodelay_pinctrl_state(struct sdhci_omap_host
*omap_host)
{
struct device *dev = omap_host->dev;
struct sdhci_host *host = omap_host->host;
struct mmc_host *mmc = host->mmc;
u32 *caps = &mmc->caps;
u32 *caps2 = &mmc->caps2;
struct pinctrl_state *state;
struct pinctrl_state **pinctrl_state;
if (!(omap_host->flags & SDHCI_OMAP_REQUIRE_IODELAY))
return 0;
pinctrl_state = devm_kzalloc(dev, sizeof(*pinctrl_state) *
(MMC_TIMING_MMC_HS200 + 1), GFP_KERNEL);
if (!pinctrl_state)
return -ENOMEM;
omap_host->pinctrl = devm_pinctrl_get(omap_host->dev);
if (IS_ERR(omap_host->pinctrl)) {
dev_err(dev, "Cannot get pinctrl\n");
return PTR_ERR(omap_host->pinctrl);
}
state = pinctrl_lookup_state(omap_host->pinctrl, "default");
if (IS_ERR(state)) {
dev_err(dev, "no pinctrl state for default mode\n");
return PTR_ERR(state);
}
pinctrl_state[MMC_TIMING_LEGACY] = state;
state = sdhci_omap_iodelay_pinctrl_state(omap_host, "sdr104", caps,
MMC_CAP_UHS_SDR104);
if (!IS_ERR(state))
pinctrl_state[MMC_TIMING_UHS_SDR104] = state;
state = sdhci_omap_iodelay_pinctrl_state(omap_host, "ddr50", caps,
MMC_CAP_UHS_DDR50);
if (!IS_ERR(state))
pinctrl_state[MMC_TIMING_UHS_DDR50] = state;
state = sdhci_omap_iodelay_pinctrl_state(omap_host, "sdr50", caps,
MMC_CAP_UHS_SDR50);
if (!IS_ERR(state))
pinctrl_state[MMC_TIMING_UHS_SDR50] = state;
state = sdhci_omap_iodelay_pinctrl_state(omap_host, "sdr25", caps,
MMC_CAP_UHS_SDR25);
if (!IS_ERR(state))
pinctrl_state[MMC_TIMING_UHS_SDR25] = state;
state = sdhci_omap_iodelay_pinctrl_state(omap_host, "sdr12", caps,
MMC_CAP_UHS_SDR12);
if (!IS_ERR(state))
pinctrl_state[MMC_TIMING_UHS_SDR12] = state;
state = sdhci_omap_iodelay_pinctrl_state(omap_host, "ddr_1_8v", caps,
MMC_CAP_1_8V_DDR);
if (!IS_ERR(state))
pinctrl_state[MMC_TIMING_MMC_DDR52] = state;
state = sdhci_omap_iodelay_pinctrl_state(omap_host, "hs", caps,
MMC_CAP_SD_HIGHSPEED);
if (!IS_ERR(state))
pinctrl_state[MMC_TIMING_SD_HS] = state;
state = sdhci_omap_iodelay_pinctrl_state(omap_host, "hs", caps,
MMC_CAP_MMC_HIGHSPEED);
if (!IS_ERR(state))
pinctrl_state[MMC_TIMING_MMC_HS] = state;
state = sdhci_omap_iodelay_pinctrl_state(omap_host, "hs200_1_8v", caps2,
MMC_CAP2_HS200_1_8V_SDR);
if (!IS_ERR(state))
pinctrl_state[MMC_TIMING_MMC_HS200] = state;
omap_host->pinctrl_state = pinctrl_state;
return 0;
}
static int sdhci_omap_probe(struct platform_device *pdev)
{
int ret;
@ -504,6 +865,9 @@ static int sdhci_omap_probe(struct platform_device *pdev)
omap_host->host = host;
omap_host->base = host->ioaddr;
omap_host->dev = dev;
omap_host->power_mode = MMC_POWER_UNDEFINED;
omap_host->timing = MMC_TIMING_LEGACY;
omap_host->flags = data->flags;
host->ioaddr += offset;
mmc = host->mmc;
@ -552,10 +916,16 @@ static int sdhci_omap_probe(struct platform_device *pdev)
goto err_put_sync;
}
ret = sdhci_omap_config_iodelay_pinctrl_state(omap_host);
if (ret)
goto err_put_sync;
host->mmc_host_ops.get_ro = mmc_gpio_get_ro;
host->mmc_host_ops.start_signal_voltage_switch =
sdhci_omap_start_signal_voltage_switch;
host->mmc_host_ops.set_ios = sdhci_omap_set_ios;
host->mmc_host_ops.card_busy = sdhci_omap_card_busy;
host->mmc_host_ops.execute_tuning = sdhci_omap_execute_tuning;
sdhci_read_caps(host);
host->caps |= SDHCI_CAN_DO_ADMA2;

View File

@ -41,18 +41,25 @@ static void sdhci_pci_hw_reset(struct sdhci_host *host);
static int sdhci_pci_init_wakeup(struct sdhci_pci_chip *chip)
{
mmc_pm_flag_t pm_flags = 0;
bool cap_cd_wake = false;
int i;
for (i = 0; i < chip->num_slots; i++) {
struct sdhci_pci_slot *slot = chip->slots[i];
if (slot)
if (slot) {
pm_flags |= slot->host->mmc->pm_flags;
if (slot->host->mmc->caps & MMC_CAP_CD_WAKE)
cap_cd_wake = true;
}
}
return device_set_wakeup_enable(&chip->pdev->dev,
(pm_flags & MMC_PM_KEEP_POWER) &&
(pm_flags & MMC_PM_WAKE_SDIO_IRQ));
if ((pm_flags & MMC_PM_KEEP_POWER) && (pm_flags & MMC_PM_WAKE_SDIO_IRQ))
return device_wakeup_enable(&chip->pdev->dev);
else if (!cap_cd_wake)
return device_wakeup_disable(&chip->pdev->dev);
return 0;
}
static int sdhci_pci_suspend_host(struct sdhci_pci_chip *chip)
@ -76,6 +83,9 @@ static int sdhci_pci_suspend_host(struct sdhci_pci_chip *chip)
ret = sdhci_suspend_host(host);
if (ret)
goto err_pci_suspend;
if (device_may_wakeup(&chip->pdev->dev))
mmc_gpio_set_cd_wake(host->mmc, true);
}
return 0;
@ -99,6 +109,8 @@ int sdhci_pci_resume_host(struct sdhci_pci_chip *chip)
ret = sdhci_resume_host(slot->host);
if (ret)
return ret;
mmc_gpio_set_cd_wake(slot->host->mmc, false);
}
return 0;
@ -712,26 +724,8 @@ static int glk_emmc_probe_slot(struct sdhci_pci_slot *slot)
return ret;
}
static void glk_cqe_enable(struct mmc_host *mmc)
{
struct sdhci_host *host = mmc_priv(mmc);
u32 reg;
/*
* CQE gets stuck if it sees Buffer Read Enable bit set, which can be
* the case after tuning, so ensure the buffer is drained.
*/
reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
while (reg & SDHCI_DATA_AVAILABLE) {
sdhci_readl(host, SDHCI_BUFFER);
reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
}
sdhci_cqe_enable(mmc);
}
static const struct cqhci_host_ops glk_cqhci_ops = {
.enable = glk_cqe_enable,
.enable = sdhci_cqe_enable,
.disable = sdhci_cqe_disable,
.dumpregs = sdhci_pci_dumpregs,
};
@ -1716,6 +1710,9 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
if (device_can_wakeup(&pdev->dev))
host->mmc->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
if (host->mmc->caps & MMC_CAP_CD_WAKE)
device_init_wakeup(&pdev->dev, true);
if (slot->cd_idx >= 0) {
ret = mmc_gpiod_request_cd(host->mmc, NULL, slot->cd_idx,
slot->cd_override_level, 0, NULL);

View File

@ -2899,6 +2899,14 @@ static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
\*****************************************************************************/
#ifdef CONFIG_PM
static bool sdhci_cd_irq_can_wakeup(struct sdhci_host *host)
{
return mmc_card_is_removable(host->mmc) &&
!(host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) &&
!mmc_can_gpio_cd(host->mmc);
}
/*
* To enable wakeup events, the corresponding events have to be enabled in
* the Interrupt Status Enable register too. See 'Table 1-6: Wakeup Signal
@ -2915,13 +2923,18 @@ static bool sdhci_enable_irq_wakeups(struct sdhci_host *host)
u8 wake_val = 0;
u8 val;
if (!(host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)) {
if (sdhci_cd_irq_can_wakeup(host)) {
wake_val |= SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE;
irq_val |= SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE;
}
wake_val |= SDHCI_WAKE_ON_INT;
irq_val |= SDHCI_INT_CARD_INT;
if (mmc_card_wake_sdio_irq(host->mmc)) {
wake_val |= SDHCI_WAKE_ON_INT;
irq_val |= SDHCI_INT_CARD_INT;
}
if (!irq_val)
return false;
val = sdhci_readb(host, SDHCI_WAKE_UP_CONTROL);
val &= ~mask;

View File

@ -7,13 +7,6 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License.
*
*
* TODO
* 1. DMA
* 2. Power management
* 3. Handle MMC errors better
*
*/
/*
@ -67,7 +60,6 @@
#include <linux/module.h>
#define DRIVER_NAME "sh_mmcif"
#define DRIVER_VERSION "2010-04-28"
/* CE_CMD_SET */
#define CMD_MASK 0x3f000000

View File

@ -268,6 +268,7 @@ struct sunxi_mmc_cfg {
};
struct sunxi_mmc_host {
struct device *dev;
struct mmc_host *mmc;
struct reset_control *reset;
const struct sunxi_mmc_cfg *cfg;
@ -1165,6 +1166,80 @@ static const struct of_device_id sunxi_mmc_of_match[] = {
};
MODULE_DEVICE_TABLE(of, sunxi_mmc_of_match);
static int sunxi_mmc_enable(struct sunxi_mmc_host *host)
{
int ret;
if (!IS_ERR(host->reset)) {
ret = reset_control_reset(host->reset);
if (ret) {
dev_err(host->dev, "Couldn't reset the MMC controller (%d)\n",
ret);
return ret;
}
}
ret = clk_prepare_enable(host->clk_ahb);
if (ret) {
dev_err(host->dev, "Couldn't enable the bus clocks (%d)\n", ret);
goto error_assert_reset;
}
ret = clk_prepare_enable(host->clk_mmc);
if (ret) {
dev_err(host->dev, "Enable mmc clk err %d\n", ret);
goto error_disable_clk_ahb;
}
ret = clk_prepare_enable(host->clk_output);
if (ret) {
dev_err(host->dev, "Enable output clk err %d\n", ret);
goto error_disable_clk_mmc;
}
ret = clk_prepare_enable(host->clk_sample);
if (ret) {
dev_err(host->dev, "Enable sample clk err %d\n", ret);
goto error_disable_clk_output;
}
/*
* Sometimes the controller asserts the irq on boot for some reason,
* make sure the controller is in a sane state before enabling irqs.
*/
ret = sunxi_mmc_reset_host(host);
if (ret)
goto error_disable_clk_sample;
return 0;
error_disable_clk_sample:
clk_disable_unprepare(host->clk_sample);
error_disable_clk_output:
clk_disable_unprepare(host->clk_output);
error_disable_clk_mmc:
clk_disable_unprepare(host->clk_mmc);
error_disable_clk_ahb:
clk_disable_unprepare(host->clk_ahb);
error_assert_reset:
if (!IS_ERR(host->reset))
reset_control_assert(host->reset);
return ret;
}
static void sunxi_mmc_disable(struct sunxi_mmc_host *host)
{
sunxi_mmc_reset_host(host);
clk_disable_unprepare(host->clk_sample);
clk_disable_unprepare(host->clk_output);
clk_disable_unprepare(host->clk_mmc);
clk_disable_unprepare(host->clk_ahb);
if (!IS_ERR(host->reset))
reset_control_assert(host->reset);
}
static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host,
struct platform_device *pdev)
{
@ -1214,66 +1289,21 @@ static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host,
if (PTR_ERR(host->reset) == -EPROBE_DEFER)
return PTR_ERR(host->reset);
ret = clk_prepare_enable(host->clk_ahb);
if (ret) {
dev_err(&pdev->dev, "Enable ahb clk err %d\n", ret);
return ret;
}
ret = clk_prepare_enable(host->clk_mmc);
if (ret) {
dev_err(&pdev->dev, "Enable mmc clk err %d\n", ret);
goto error_disable_clk_ahb;
}
ret = clk_prepare_enable(host->clk_output);
if (ret) {
dev_err(&pdev->dev, "Enable output clk err %d\n", ret);
goto error_disable_clk_mmc;
}
ret = clk_prepare_enable(host->clk_sample);
if (ret) {
dev_err(&pdev->dev, "Enable sample clk err %d\n", ret);
goto error_disable_clk_output;
}
if (!IS_ERR(host->reset)) {
ret = reset_control_reset(host->reset);
if (ret) {
dev_err(&pdev->dev, "reset err %d\n", ret);
goto error_disable_clk_sample;
}
}
/*
* Sometimes the controller asserts the irq on boot for some reason,
* make sure the controller is in a sane state before enabling irqs.
*/
ret = sunxi_mmc_reset_host(host);
ret = sunxi_mmc_enable(host);
if (ret)
goto error_assert_reset;
return ret;
host->irq = platform_get_irq(pdev, 0);
if (host->irq <= 0) {
ret = -EINVAL;
goto error_assert_reset;
goto error_disable_mmc;
}
return devm_request_threaded_irq(&pdev->dev, host->irq, sunxi_mmc_irq,
sunxi_mmc_handle_manual_stop, 0, "sunxi-mmc", host);
error_assert_reset:
if (!IS_ERR(host->reset))
reset_control_assert(host->reset);
error_disable_clk_sample:
clk_disable_unprepare(host->clk_sample);
error_disable_clk_output:
clk_disable_unprepare(host->clk_output);
error_disable_clk_mmc:
clk_disable_unprepare(host->clk_mmc);
error_disable_clk_ahb:
clk_disable_unprepare(host->clk_ahb);
error_disable_mmc:
sunxi_mmc_disable(host);
return ret;
}
@ -1288,8 +1318,10 @@ static int sunxi_mmc_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "mmc alloc host failed\n");
return -ENOMEM;
}
platform_set_drvdata(pdev, mmc);
host = mmc_priv(mmc);
host->dev = &pdev->dev;
host->mmc = mmc;
spin_lock_init(&host->lock);
@ -1353,7 +1385,6 @@ static int sunxi_mmc_probe(struct platform_device *pdev)
goto error_free_dma;
dev_info(&pdev->dev, "base:0x%p irq:%u\n", host->reg_base, host->irq);
platform_set_drvdata(pdev, mmc);
return 0;
error_free_dma:
@ -1370,16 +1401,7 @@ static int sunxi_mmc_remove(struct platform_device *pdev)
mmc_remove_host(mmc);
disable_irq(host->irq);
sunxi_mmc_reset_host(host);
if (!IS_ERR(host->reset))
reset_control_assert(host->reset);
clk_disable_unprepare(host->clk_sample);
clk_disable_unprepare(host->clk_output);
clk_disable_unprepare(host->clk_mmc);
clk_disable_unprepare(host->clk_ahb);
sunxi_mmc_disable(host);
dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
mmc_free_host(mmc);

View File

@ -278,7 +278,6 @@ static void tmio_mmc_reset_work(struct work_struct *work)
host->cmd = NULL;
host->data = NULL;
host->force_pio = false;
spin_unlock_irqrestore(&host->lock, flags);
@ -350,8 +349,6 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host,
c |= TRANSFER_READ;
}
if (!host->native_hotplug)
irq_mask &= ~(TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT);
tmio_mmc_enable_mmc_irqs(host, irq_mask);
/* Fire off the command */
@ -623,15 +620,21 @@ static void tmio_mmc_cmd_irq(struct tmio_mmc_host *host, unsigned int stat)
*/
if (host->data && (!cmd->error || cmd->error == -EILSEQ)) {
if (host->data->flags & MMC_DATA_READ) {
if (host->force_pio || !host->chan_rx)
if (host->force_pio || !host->chan_rx) {
tmio_mmc_enable_mmc_irqs(host, TMIO_MASK_READOP);
else
} else {
tmio_mmc_disable_mmc_irqs(host,
TMIO_MASK_READOP);
tasklet_schedule(&host->dma_issue);
}
} else {
if (host->force_pio || !host->chan_tx)
if (host->force_pio || !host->chan_tx) {
tmio_mmc_enable_mmc_irqs(host, TMIO_MASK_WRITEOP);
else
} else {
tmio_mmc_disable_mmc_irqs(host,
TMIO_MASK_WRITEOP);
tasklet_schedule(&host->dma_issue);
}
}
} else {
schedule_work(&host->done);
@ -755,6 +758,7 @@ static int tmio_mmc_start_data(struct tmio_mmc_host *host,
tmio_mmc_init_sg(host, data);
host->data = data;
host->force_pio = false;
/* Set transfer length / blocksize */
sd_ctrl_write16(host, CTL_SD_XFER_LEN, data->blksz);
@ -846,7 +850,6 @@ static void tmio_process_mrq(struct tmio_mmc_host *host,
return;
fail:
host->force_pio = false;
host->mrq = NULL;
mrq->cmd->error = ret;
mmc_request_done(host->mmc, mrq);
@ -896,7 +899,6 @@ static void tmio_mmc_finish_request(struct tmio_mmc_host *host)
if (host->cmd != mrq->sbc) {
host->cmd = NULL;
host->data = NULL;
host->force_pio = false;
host->mrq = NULL;
}
@ -1061,10 +1063,17 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
static int tmio_mmc_get_ro(struct mmc_host *mmc)
{
struct tmio_mmc_host *host = mmc_priv(mmc);
struct tmio_mmc_data *pdata = host->pdata;
return !((pdata->flags & TMIO_MMC_WRPROTECT_DISABLE) ||
(sd_ctrl_read16_and_16_as_32(host, CTL_STATUS) & TMIO_STAT_WRPROTECT));
return !(sd_ctrl_read16_and_16_as_32(host, CTL_STATUS) &
TMIO_STAT_WRPROTECT);
}
static int tmio_mmc_get_cd(struct mmc_host *mmc)
{
struct tmio_mmc_host *host = mmc_priv(mmc);
return !!(sd_ctrl_read16_and_16_as_32(host, CTL_STATUS) &
TMIO_STAT_SIGSTATE);
}
static int tmio_multi_io_quirk(struct mmc_card *card,
@ -1082,7 +1091,7 @@ static const struct mmc_host_ops tmio_mmc_ops = {
.request = tmio_mmc_request,
.set_ios = tmio_mmc_set_ios,
.get_ro = tmio_mmc_get_ro,
.get_cd = mmc_gpio_get_cd,
.get_cd = tmio_mmc_get_cd,
.enable_sdio_irq = tmio_mmc_enable_sdio_irq,
.multi_io_quirk = tmio_multi_io_quirk,
.hw_reset = tmio_mmc_hw_reset,
@ -1114,15 +1123,20 @@ static int tmio_mmc_init_ocr(struct tmio_mmc_host *host)
}
static void tmio_mmc_of_parse(struct platform_device *pdev,
struct tmio_mmc_data *pdata)
struct mmc_host *mmc)
{
const struct device_node *np = pdev->dev.of_node;
if (!np)
return;
/*
* DEPRECATED:
* For new platforms, please use "disable-wp" instead of
* "toshiba,mmc-wrprotect-disable"
*/
if (of_get_property(np, "toshiba,mmc-wrprotect-disable", NULL))
pdata->flags |= TMIO_MMC_WRPROTECT_DISABLE;
mmc->caps2 |= MMC_CAP2_NO_WRITE_PROTECT;
}
struct tmio_mmc_host *tmio_mmc_host_alloc(struct platform_device *pdev,
@ -1157,7 +1171,7 @@ struct tmio_mmc_host *tmio_mmc_host_alloc(struct platform_device *pdev,
goto free;
}
tmio_mmc_of_parse(pdev, pdata);
tmio_mmc_of_parse(pdev, mmc);
platform_set_drvdata(pdev, host);
@ -1181,7 +1195,6 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
struct tmio_mmc_data *pdata = _host->pdata;
struct mmc_host *mmc = _host->mmc;
int ret;
u32 irq_mask = TMIO_MASK_CMD;
/*
* Check the sanity of mmc->f_min to prevent tmio_mmc_set_clock() from
@ -1230,6 +1243,9 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
if (mmc_can_gpio_ro(mmc))
_host->ops.get_ro = mmc_gpio_get_ro;
if (mmc_can_gpio_cd(mmc))
_host->ops.get_cd = mmc_gpio_get_cd;
_host->native_hotplug = !(mmc_can_gpio_cd(mmc) ||
mmc->caps & MMC_CAP_NEEDS_POLL ||
!mmc_card_is_removable(mmc));
@ -1260,15 +1276,9 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
_host->sdcard_irq_mask = sd_ctrl_read16_and_16_as_32(_host, CTL_IRQ_MASK);
tmio_mmc_disable_mmc_irqs(_host, TMIO_MASK_ALL);
/* Unmask the IRQs we want to know about */
if (!_host->chan_rx)
irq_mask |= TMIO_MASK_READOP;
if (!_host->chan_tx)
irq_mask |= TMIO_MASK_WRITEOP;
if (!_host->native_hotplug)
irq_mask &= ~(TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT);
_host->sdcard_irq_mask &= ~irq_mask;
if (_host->native_hotplug)
tmio_mmc_enable_mmc_irqs(_host,
TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT);
spin_lock_init(&_host->lock);
mutex_init(&_host->ios_lock);
@ -1367,6 +1377,10 @@ int tmio_mmc_host_runtime_resume(struct device *dev)
if (host->clk_cache)
tmio_mmc_set_clock(host, host->clk_cache);
if (host->native_hotplug)
tmio_mmc_enable_mmc_irqs(host,
TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT);
tmio_mmc_enable_dma(host, true);
if (tmio_mmc_can_retune(host) && host->select_tuning(host))

View File

@ -309,8 +309,6 @@ static void ushc_request(struct mmc_host *mmc, struct mmc_request *req)
/* Submit CSW. */
ret = usb_submit_urb(ushc->csw_urb, GFP_ATOMIC);
if (ret < 0)
goto out;
out:
spin_unlock_irqrestore(&ushc->lock, flags);

View File

@ -36,7 +36,6 @@
} while (0)
/* tmio MMC platform flags */
#define TMIO_MMC_WRPROTECT_DISABLE BIT(0)
/*
* Some controllers can support a 2-byte block size when the bus width
* is configured in 4-bit mode.

View File

@ -31,6 +31,7 @@ int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
unsigned int debounce, bool *gpio_invert);
void mmc_gpio_set_cd_isr(struct mmc_host *host,
irqreturn_t (*isr)(int irq, void *dev_id));
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);
bool mmc_can_gpio_ro(struct mmc_host *host);