forked from Minki/linux
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:
commit
dc73d6a8d4
@ -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>;
|
||||||
|
};
|
@ -12,6 +12,7 @@ Required properties:
|
|||||||
"mediatek,mt8173-mmc": for mmc host ip compatible with mt8173
|
"mediatek,mt8173-mmc": for mmc host ip compatible with mt8173
|
||||||
"mediatek,mt2701-mmc": for mmc host ip compatible with mt2701
|
"mediatek,mt2701-mmc": for mmc host ip compatible with mt2701
|
||||||
"mediatek,mt2712-mmc": for mmc host ip compatible with mt2712
|
"mediatek,mt2712-mmc": for mmc host ip compatible with mt2712
|
||||||
|
"mediatek,mt7622-mmc": for MT7622 SoC
|
||||||
"mediatek,mt7623-mmc", "mediatek,mt2701-mmc": for MT7623 SoC
|
"mediatek,mt7623-mmc", "mediatek,mt2701-mmc": for MT7623 SoC
|
||||||
|
|
||||||
- reg: physical base address of the controller and length
|
- reg: physical base address of the controller and length
|
||||||
|
@ -59,15 +59,6 @@ Optional properties:
|
|||||||
is specified and the ciu clock is specified then we'll try to set the ciu
|
is specified and the ciu clock is specified then we'll try to set the ciu
|
||||||
clock to this at probe time.
|
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
|
* 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
|
specified, the default value of the fifo size is determined from the
|
||||||
controller registers.
|
controller registers.
|
||||||
|
@ -50,7 +50,6 @@ Required properties:
|
|||||||
2: R7S72100
|
2: R7S72100
|
||||||
|
|
||||||
Optional properties:
|
Optional properties:
|
||||||
- toshiba,mmc-wrprotect-disable: write-protect detection is unavailable
|
|
||||||
- pinctrl-names: should be "default", "state_uhs"
|
- pinctrl-names: should be "default", "state_uhs"
|
||||||
- pinctrl-0: should contain default/high speed pin ctrl
|
- pinctrl-0: should contain default/high speed pin ctrl
|
||||||
- pinctrl-1: should contain uhs mode pin ctrl
|
- pinctrl-1: should contain uhs mode pin ctrl
|
||||||
|
@ -115,7 +115,6 @@
|
|||||||
compatible = "snps,dw-mshc";
|
compatible = "snps,dw-mshc";
|
||||||
reg = <0x40004000 0x1000>;
|
reg = <0x40004000 0x1000>;
|
||||||
interrupts = <6>;
|
interrupts = <6>;
|
||||||
num-slots = <1>;
|
|
||||||
clocks = <&ccu2 CLK_SDIO>, <&ccu1 CLK_CPU_SDIO>;
|
clocks = <&ccu2 CLK_SDIO>, <&ccu1 CLK_CPU_SDIO>;
|
||||||
clock-names = "ciu", "biu";
|
clock-names = "ciu", "biu";
|
||||||
resets = <&rgu 20>;
|
resets = <&rgu 20>;
|
||||||
|
@ -20,7 +20,6 @@
|
|||||||
|
|
||||||
&mmc {
|
&mmc {
|
||||||
status = "okay";
|
status = "okay";
|
||||||
num-slots = <1>;
|
|
||||||
cap-sd-highspeed;
|
cap-sd-highspeed;
|
||||||
broken-cd;
|
broken-cd;
|
||||||
bus-width = <4>;
|
bus-width = <4>;
|
||||||
|
@ -30,7 +30,6 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
mmc0: dwmmc0@ff704000 {
|
mmc0: dwmmc0@ff704000 {
|
||||||
num-slots = <1>;
|
|
||||||
broken-cd;
|
broken-cd;
|
||||||
bus-width = <4>;
|
bus-width = <4>;
|
||||||
cap-mmc-highspeed;
|
cap-mmc-highspeed;
|
||||||
|
@ -31,7 +31,6 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
mmc0: dwmmc0@ff704000 {
|
mmc0: dwmmc0@ff704000 {
|
||||||
num-slots = <1>;
|
|
||||||
broken-cd;
|
broken-cd;
|
||||||
bus-width = <4>;
|
bus-width = <4>;
|
||||||
cap-mmc-highspeed;
|
cap-mmc-highspeed;
|
||||||
|
@ -42,7 +42,6 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
dwmmc0@ff704000 {
|
dwmmc0@ff704000 {
|
||||||
num-slots = <1>;
|
|
||||||
broken-cd;
|
broken-cd;
|
||||||
bus-width = <4>;
|
bus-width = <4>;
|
||||||
cap-mmc-highspeed;
|
cap-mmc-highspeed;
|
||||||
|
@ -88,7 +88,6 @@
|
|||||||
|
|
||||||
&mmc {
|
&mmc {
|
||||||
status = "okay";
|
status = "okay";
|
||||||
num-slots = <1>;
|
|
||||||
cap-sd-highspeed;
|
cap-sd-highspeed;
|
||||||
broken-cd;
|
broken-cd;
|
||||||
bus-width = <4>;
|
bus-width = <4>;
|
||||||
|
@ -922,7 +922,6 @@
|
|||||||
#size-cells = <0>;
|
#size-cells = <0>;
|
||||||
cd-inverted;
|
cd-inverted;
|
||||||
compatible = "hisilicon,hi3660-dw-mshc";
|
compatible = "hisilicon,hi3660-dw-mshc";
|
||||||
num-slots = <1>;
|
|
||||||
bus-width = <0x4>;
|
bus-width = <0x4>;
|
||||||
disable-wp;
|
disable-wp;
|
||||||
cap-sd-highspeed;
|
cap-sd-highspeed;
|
||||||
@ -960,7 +959,6 @@
|
|||||||
compatible = "hisilicon,hi3660-dw-mshc";
|
compatible = "hisilicon,hi3660-dw-mshc";
|
||||||
reg = <0x0 0xff3ff000 0x0 0x1000>;
|
reg = <0x0 0xff3ff000 0x0 0x1000>;
|
||||||
interrupts = <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>;
|
interrupts = <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
num-slots = <1>;
|
|
||||||
clocks = <&crg_ctrl HI3660_CLK_GATE_SDIO0>,
|
clocks = <&crg_ctrl HI3660_CLK_GATE_SDIO0>,
|
||||||
<&crg_ctrl HI3660_HCLK_GATE_SDIO0>;
|
<&crg_ctrl HI3660_HCLK_GATE_SDIO0>;
|
||||||
clock-names = "ciu", "biu";
|
clock-names = "ciu", "biu";
|
||||||
|
@ -375,8 +375,8 @@ static struct resource kfr2r09_sh_sdhi0_resources[] = {
|
|||||||
static struct tmio_mmc_data sh7724_sdhi0_data = {
|
static struct tmio_mmc_data sh7724_sdhi0_data = {
|
||||||
.chan_priv_tx = (void *)SHDMA_SLAVE_SDHI0_TX,
|
.chan_priv_tx = (void *)SHDMA_SLAVE_SDHI0_TX,
|
||||||
.chan_priv_rx = (void *)SHDMA_SLAVE_SDHI0_RX,
|
.chan_priv_rx = (void *)SHDMA_SLAVE_SDHI0_RX,
|
||||||
.flags = TMIO_MMC_WRPROTECT_DISABLE,
|
|
||||||
.capabilities = MMC_CAP_SDIO_IRQ,
|
.capabilities = MMC_CAP_SDIO_IRQ,
|
||||||
|
.capabilities2 = MMC_CAP2_NO_WRITE_PROTECT,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct platform_device kfr2r09_sh_sdhi0_device = {
|
static struct platform_device kfr2r09_sh_sdhi0_device = {
|
||||||
|
@ -376,22 +376,15 @@ static struct mmc_blk_ioc_data *mmc_blk_ioctl_copy_from_user(
|
|||||||
return idata;
|
return idata;
|
||||||
}
|
}
|
||||||
|
|
||||||
idata->buf = kmalloc(idata->buf_bytes, GFP_KERNEL);
|
idata->buf = memdup_user((void __user *)(unsigned long)
|
||||||
if (!idata->buf) {
|
idata->ic.data_ptr, idata->buf_bytes);
|
||||||
err = -ENOMEM;
|
if (IS_ERR(idata->buf)) {
|
||||||
|
err = PTR_ERR(idata->buf);
|
||||||
goto idata_err;
|
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;
|
return idata;
|
||||||
|
|
||||||
copy_err:
|
|
||||||
kfree(idata->buf);
|
|
||||||
idata_err:
|
idata_err:
|
||||||
kfree(idata);
|
kfree(idata);
|
||||||
out:
|
out:
|
||||||
|
@ -2369,7 +2369,7 @@ unsigned int mmc_calc_max_discard(struct mmc_card *card)
|
|||||||
return card->pref_erase;
|
return card->pref_erase;
|
||||||
|
|
||||||
max_discard = mmc_do_calc_max_discard(card, MMC_ERASE_ARG);
|
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);
|
max_trim = mmc_do_calc_max_discard(card, MMC_TRIM_ARG);
|
||||||
if (max_trim < max_discard)
|
if (max_trim < max_discard)
|
||||||
max_discard = max_trim;
|
max_discard = max_trim;
|
||||||
@ -2655,8 +2655,7 @@ void mmc_start_host(struct mmc_host *host)
|
|||||||
void mmc_stop_host(struct mmc_host *host)
|
void mmc_stop_host(struct mmc_host *host)
|
||||||
{
|
{
|
||||||
if (host->slot.cd_irq >= 0) {
|
if (host->slot.cd_irq >= 0) {
|
||||||
if (host->slot.cd_wake_enabled)
|
mmc_gpio_set_cd_wake(host, false);
|
||||||
disable_irq_wake(host->slot.cd_irq);
|
|
||||||
disable_irq(host->slot.cd_irq);
|
disable_irq(host->slot.cd_irq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,18 +196,7 @@ static int mmc_ios_show(struct seq_file *s, void *data)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
DEFINE_SHOW_ATTRIBUTE(mmc_ios);
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int mmc_clock_opt_get(void *data, u64 *val)
|
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))
|
if (!debugfs_create_file("ios", S_IRUSR, root, host, &mmc_ios_fops))
|
||||||
goto err_node;
|
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,
|
if (!debugfs_create_file("clock", S_IRUSR | S_IWUSR, root, host,
|
||||||
&mmc_clock_fops))
|
&mmc_clock_fops))
|
||||||
goto err_node;
|
goto err_node;
|
||||||
|
@ -56,7 +56,8 @@ static inline int mmc_host_uhs(struct mmc_host *host)
|
|||||||
return host->caps &
|
return host->caps &
|
||||||
(MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
|
(MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
|
||||||
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
|
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)
|
static inline bool mmc_card_hs200(struct mmc_card *card)
|
||||||
|
@ -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(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(rel_sectors, "%#x\n", card->ext_csd.rel_sectors);
|
||||||
MMC_DEV_ATTR(ocr, "0x%08x\n", card->ocr);
|
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);
|
MMC_DEV_ATTR(cmdq_en, "%d\n", card->ext_csd.cmdq_en);
|
||||||
|
|
||||||
static ssize_t mmc_fwrev_show(struct device *dev,
|
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_raw_rpmb_size_mult.attr,
|
||||||
&dev_attr_rel_sectors.attr,
|
&dev_attr_rel_sectors.attr,
|
||||||
&dev_attr_ocr.attr,
|
&dev_attr_ocr.attr,
|
||||||
|
&dev_attr_rca.attr,
|
||||||
&dev_attr_dsr.attr,
|
&dev_attr_dsr.attr,
|
||||||
&dev_attr_cmdq_en.attr,
|
&dev_attr_cmdq_en.attr,
|
||||||
NULL,
|
NULL,
|
||||||
|
@ -291,8 +291,6 @@ static int mmc_read_switch(struct mmc_card *card)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = -EIO;
|
|
||||||
|
|
||||||
status = kmalloc(64, GFP_KERNEL);
|
status = kmalloc(64, GFP_KERNEL);
|
||||||
if (!status)
|
if (!status)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
@ -582,9 +580,6 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
|
|||||||
int err;
|
int err;
|
||||||
u8 *status;
|
u8 *status;
|
||||||
|
|
||||||
if (!card->scr.sda_spec3)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (!(card->csd.cmdclass & CCC_SWITCH))
|
if (!(card->csd.cmdclass & CCC_SWITCH))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@ -593,14 +588,11 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
/* Set 4-bit bus width */
|
/* Set 4-bit bus width */
|
||||||
if ((card->host->caps & MMC_CAP_4_BIT_DATA) &&
|
err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
|
||||||
(card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
|
if (err)
|
||||||
err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
|
goto out;
|
||||||
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
|
* 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(oemid, "0x%04x\n", card->cid.oemid);
|
||||||
MMC_DEV_ATTR(serial, "0x%08x\n", card->cid.serial);
|
MMC_DEV_ATTR(serial, "0x%08x\n", card->cid.serial);
|
||||||
MMC_DEV_ATTR(ocr, "0x%08x\n", card->ocr);
|
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,
|
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_oemid.attr,
|
||||||
&dev_attr_serial.attr,
|
&dev_attr_serial.attr,
|
||||||
&dev_attr_ocr.attr,
|
&dev_attr_ocr.attr,
|
||||||
|
&dev_attr_rca.attr,
|
||||||
&dev_attr_dsr.attr,
|
&dev_attr_dsr.attr,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
@ -1033,7 +1027,7 @@ retry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Initialization sequence for UHS-I cards */
|
/* 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);
|
err = mmc_sd_init_uhs_card(card);
|
||||||
if (err)
|
if (err)
|
||||||
goto free_card;
|
goto free_card;
|
||||||
|
@ -518,11 +518,10 @@ static int mmc_sdio_init_uhs_card(struct mmc_card *card)
|
|||||||
if (!card->scr.sda_spec3)
|
if (!card->scr.sda_spec3)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/*
|
/* Switch to wider bus */
|
||||||
* Switch to wider bus (if supported).
|
err = sdio_enable_4bit_bus(card);
|
||||||
*/
|
if (err)
|
||||||
if (card->host->caps & MMC_CAP_4_BIT_DATA)
|
goto out;
|
||||||
err = sdio_enable_4bit_bus(card);
|
|
||||||
|
|
||||||
/* Set the driver strength for the card */
|
/* Set the driver strength for the card */
|
||||||
sdio_select_driver_type(card);
|
sdio_select_driver_type(card);
|
||||||
|
@ -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
|
* 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
|
* 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
|
* claimed already when the handler is called so the handler should not
|
||||||
* call sdio_claim_host() nor sdio_release_host().
|
* call sdio_claim_host() or sdio_release_host().
|
||||||
*/
|
*/
|
||||||
int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler)
|
int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler)
|
||||||
{
|
{
|
||||||
|
@ -149,11 +149,30 @@ void mmc_gpiod_request_cd_irq(struct mmc_host *host)
|
|||||||
|
|
||||||
if (irq < 0)
|
if (irq < 0)
|
||||||
host->caps |= MMC_CAP_NEEDS_POLL;
|
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);
|
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
|
/* Register an alternate interrupt service routine for
|
||||||
* the card-detect GPIO.
|
* the card-detect GPIO.
|
||||||
*/
|
*/
|
||||||
|
@ -699,6 +699,15 @@ config MMC_DW_EXYNOS
|
|||||||
Synopsys DesignWare Memory Card Interface driver. Select this option
|
Synopsys DesignWare Memory Card Interface driver. Select this option
|
||||||
for platforms based on Exynos4 and Exynos5 SoC's.
|
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
|
config MMC_DW_K3
|
||||||
tristate "K3 specific extensions for Synopsys DW Memory Card Interface"
|
tristate "K3 specific extensions for Synopsys DW Memory Card Interface"
|
||||||
depends on MMC_DW
|
depends on MMC_DW
|
||||||
|
@ -50,6 +50,7 @@ obj-$(CONFIG_MMC_CAVIUM_THUNDERX) += thunderx-mmc.o
|
|||||||
obj-$(CONFIG_MMC_DW) += dw_mmc.o
|
obj-$(CONFIG_MMC_DW) += dw_mmc.o
|
||||||
obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o
|
obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o
|
||||||
obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.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_K3) += dw_mmc-k3.o
|
||||||
obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o
|
obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o
|
||||||
obj-$(CONFIG_MMC_DW_ROCKCHIP) += dw_mmc-rockchip.o
|
obj-$(CONFIG_MMC_DW_ROCKCHIP) += dw_mmc-rockchip.o
|
||||||
|
202
drivers/mmc/host/dw_mmc-hi3798cv200.c
Normal file
202
drivers/mmc/host/dw_mmc-hi3798cv200.c
Normal 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");
|
@ -29,7 +29,6 @@
|
|||||||
MMC_CAP_SDIO_IRQ)
|
MMC_CAP_SDIO_IRQ)
|
||||||
|
|
||||||
static struct dw_mci_board pci_board_data = {
|
static struct dw_mci_board pci_board_data = {
|
||||||
.num_slots = 1,
|
|
||||||
.caps = DW_MCI_CAPABILITIES,
|
.caps = DW_MCI_CAPABILITIES,
|
||||||
.bus_hz = 33 * 1000 * 1000,
|
.bus_hz = 33 * 1000 * 1000,
|
||||||
.detect_delay_ms = 200,
|
.detect_delay_ms = 200,
|
||||||
|
@ -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");
|
priv->drv_clk = devm_clk_get(host->dev, "ciu-drive");
|
||||||
if (IS_ERR(priv->drv_clk))
|
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");
|
priv->sample_clk = devm_clk_get(host->dev, "ciu-sample");
|
||||||
if (IS_ERR(priv->sample_clk))
|
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;
|
host->priv = priv;
|
||||||
|
|
||||||
|
@ -147,19 +147,7 @@ static int dw_mci_req_show(struct seq_file *s, void *v)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
DEFINE_SHOW_ATTRIBUTE(dw_mci_req);
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int dw_mci_regs_show(struct seq_file *s, void *v)
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
DEFINE_SHOW_ATTRIBUTE(dw_mci_regs);
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void dw_mci_init_debugfs(struct dw_mci_slot *slot)
|
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);
|
set_bit(EVENT_CMD_COMPLETE, &host->completed_events);
|
||||||
err = dw_mci_command_complete(host, cmd);
|
err = dw_mci_command_complete(host, cmd);
|
||||||
if (cmd == mrq->sbc && !err) {
|
if (cmd == mrq->sbc && !err) {
|
||||||
prev_state = state = STATE_SENDING_CMD;
|
|
||||||
__dw_mci_start_request(host, host->slot,
|
__dw_mci_start_request(host, host->slot,
|
||||||
mrq->cmd);
|
mrq->cmd);
|
||||||
goto unlock;
|
goto unlock;
|
||||||
@ -2826,6 +2801,10 @@ static int dw_mci_init_slot_caps(struct dw_mci_slot *slot)
|
|||||||
if (host->pdata->caps2)
|
if (host->pdata->caps2)
|
||||||
mmc->caps2 = 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. */
|
/* Process SDIO IRQs through the sdio_irq_work. */
|
||||||
if (mmc->caps & MMC_CAP_SDIO_IRQ)
|
if (mmc->caps & MMC_CAP_SDIO_IRQ)
|
||||||
mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
|
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 mmc_host *mmc;
|
||||||
struct dw_mci_slot *slot;
|
struct dw_mci_slot *slot;
|
||||||
int ret;
|
int ret;
|
||||||
u32 freq[2];
|
|
||||||
|
|
||||||
mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);
|
mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);
|
||||||
if (!mmc)
|
if (!mmc)
|
||||||
@ -2852,16 +2830,6 @@ static int dw_mci_init_slot(struct dw_mci *host)
|
|||||||
host->slot = slot;
|
host->slot = slot;
|
||||||
|
|
||||||
mmc->ops = &dw_mci_ops;
|
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*/
|
/*if there are external regulators, get them*/
|
||||||
ret = mmc_regulator_get_supply(mmc);
|
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);
|
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))
|
if (device_property_read_u32(dev, "fifo-depth", &pdata->fifo_depth))
|
||||||
dev_info(dev,
|
dev_info(dev,
|
||||||
"fifo-depth property not found, using value of FIFOTH register as default\n");
|
"fifo-depth property not found, using value of FIFOTH register as default\n");
|
||||||
|
@ -65,8 +65,7 @@ struct dw_mci_dma_slave {
|
|||||||
* @fifo_reg: Pointer to MMIO registers for data FIFO
|
* @fifo_reg: Pointer to MMIO registers for data FIFO
|
||||||
* @sg: Scatterlist entry currently being processed by PIO code, if any.
|
* @sg: Scatterlist entry currently being processed by PIO code, if any.
|
||||||
* @sg_miter: PIO mapping scatterlist iterator.
|
* @sg_miter: PIO mapping scatterlist iterator.
|
||||||
* @cur_slot: The slot which is currently using the controller.
|
* @mrq: The request currently being processed on @slot,
|
||||||
* @mrq: The request currently being processed on @cur_slot,
|
|
||||||
* or NULL if the controller is idle.
|
* or NULL if the controller is idle.
|
||||||
* @cmd: The command currently being sent to the card, or NULL.
|
* @cmd: The command currently being sent to the card, or NULL.
|
||||||
* @data: The data currently being transferred, or NULL if no data
|
* @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
|
* @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus
|
||||||
* rate and timeout calculations.
|
* rate and timeout calculations.
|
||||||
* @current_speed: Configured rate of the controller.
|
* @current_speed: Configured rate of the controller.
|
||||||
* @num_slots: Number of slots available.
|
|
||||||
* @fifoth_val: The value of FIFOTH register.
|
* @fifoth_val: The value of FIFOTH register.
|
||||||
* @verid: Denote Version ID.
|
* @verid: Denote Version ID.
|
||||||
* @dev: Device associated with the MMC controller.
|
* @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
|
* @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.
|
* 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
|
* @irq_lock is an irq-safe spinlock protecting the INTMASK register
|
||||||
* to allow the interrupt handler to modify it directly. Held for only long
|
* 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
|
* enough to read-modify-write INTMASK and no other locks are grabbed when
|
||||||
* holding this one.
|
* 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
|
* @pending_events and @completed_events are accessed using atomic bit
|
||||||
* operations, so they don't need any locking.
|
* operations, so they don't need any locking.
|
||||||
*
|
*
|
||||||
@ -253,8 +251,6 @@ struct dma_pdata;
|
|||||||
|
|
||||||
/* Board platform data */
|
/* Board platform data */
|
||||||
struct dw_mci_board {
|
struct dw_mci_board {
|
||||||
u32 num_slots;
|
|
||||||
|
|
||||||
unsigned int bus_hz; /* Clock speed at the cclk_in pad */
|
unsigned int bus_hz; /* Clock speed at the cclk_in pad */
|
||||||
|
|
||||||
u32 caps; /* Capabilities */
|
u32 caps; /* Capabilities */
|
||||||
@ -318,11 +314,12 @@ struct dw_mci_board {
|
|||||||
#define SDMMC_BUFADDR 0x098
|
#define SDMMC_BUFADDR 0x098
|
||||||
#define SDMMC_CDTHRCTL 0x100
|
#define SDMMC_CDTHRCTL 0x100
|
||||||
#define SDMMC_UHS_REG_EXT 0x108
|
#define SDMMC_UHS_REG_EXT 0x108
|
||||||
|
#define SDMMC_DDR_REG 0x10c
|
||||||
#define SDMMC_ENABLE_SHIFT 0x110
|
#define SDMMC_ENABLE_SHIFT 0x110
|
||||||
#define SDMMC_DATA(x) (x)
|
#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_DBADDRL 0x088
|
||||||
#define SDMMC_DBADDRU 0x08c
|
#define SDMMC_DBADDRU 0x08c
|
||||||
#define SDMMC_IDSTS64 0x090
|
#define SDMMC_IDSTS64 0x090
|
||||||
@ -443,13 +440,19 @@ struct dw_mci_board {
|
|||||||
#define SDMMC_CARD_WR_THR_EN BIT(2)
|
#define SDMMC_CARD_WR_THR_EN BIT(2)
|
||||||
#define SDMMC_CARD_RD_THR_EN BIT(0)
|
#define SDMMC_CARD_RD_THR_EN BIT(0)
|
||||||
/* UHS-1 register defines */
|
/* UHS-1 register defines */
|
||||||
|
#define SDMMC_UHS_DDR BIT(16)
|
||||||
#define SDMMC_UHS_18V BIT(0)
|
#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 */
|
/* All ctrl reset bits */
|
||||||
#define SDMMC_CTRL_ALL_RESET_FLAGS \
|
#define SDMMC_CTRL_ALL_RESET_FLAGS \
|
||||||
(SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET)
|
(SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET)
|
||||||
|
|
||||||
/* FIFO register access macros. These should not change the data endian-ness
|
/* 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_readw(__reg) __raw_readw(__reg)
|
||||||
#define mci_fifo_readl(__reg) __raw_readl(__reg)
|
#define mci_fifo_readl(__reg) __raw_readl(__reg)
|
||||||
#define mci_fifo_readq(__reg) __raw_readq(__reg)
|
#define mci_fifo_readq(__reg) __raw_readq(__reg)
|
||||||
|
@ -438,11 +438,23 @@ static const struct mtk_mmc_compatible mt2712_compat = {
|
|||||||
.enhance_rx = true,
|
.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[] = {
|
static const struct of_device_id msdc_of_ids[] = {
|
||||||
{ .compatible = "mediatek,mt8135-mmc", .data = &mt8135_compat},
|
{ .compatible = "mediatek,mt8135-mmc", .data = &mt8135_compat},
|
||||||
{ .compatible = "mediatek,mt8173-mmc", .data = &mt8173_compat},
|
{ .compatible = "mediatek,mt8173-mmc", .data = &mt8173_compat},
|
||||||
{ .compatible = "mediatek,mt2701-mmc", .data = &mt2701_compat},
|
{ .compatible = "mediatek,mt2701-mmc", .data = &mt2701_compat},
|
||||||
{ .compatible = "mediatek,mt2712-mmc", .data = &mt2712_compat},
|
{ .compatible = "mediatek,mt2712-mmc", .data = &mt2712_compat},
|
||||||
|
{ .compatible = "mediatek,mt7622-mmc", .data = &mt7622_compat},
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, msdc_of_ids);
|
MODULE_DEVICE_TABLE(of, msdc_of_ids);
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
#include "renesas_sdhi.h"
|
#include "renesas_sdhi.h"
|
||||||
#include "tmio_mmc.h"
|
#include "tmio_mmc.h"
|
||||||
|
|
||||||
#define EXT_ACC 0xe4
|
#define HOST_MODE 0xe4
|
||||||
|
|
||||||
#define SDHI_VER_GEN2_SDR50 0x490c
|
#define SDHI_VER_GEN2_SDR50 0x490c
|
||||||
#define SDHI_VER_RZ_A1 0x820b
|
#define SDHI_VER_RZ_A1 0x820b
|
||||||
@ -76,7 +76,7 @@ static void renesas_sdhi_sdbuf_width(struct tmio_mmc_host *host, int width)
|
|||||||
return;
|
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)
|
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_SD_MEM_CARD_OPT:
|
||||||
case CTL_TRANSACTION_CTL:
|
case CTL_TRANSACTION_CTL:
|
||||||
case CTL_DMA_ENABLE:
|
case CTL_DMA_ENABLE:
|
||||||
case EXT_ACC:
|
case HOST_MODE:
|
||||||
if (host->pdata->flags & TMIO_MMC_HAVE_CBSY)
|
if (host->pdata->flags & TMIO_MMC_HAVE_CBSY)
|
||||||
bit = TMIO_STAT_CMD_BUSY;
|
bit = TMIO_STAT_CMD_BUSY;
|
||||||
/* fallthrough */
|
/* fallthrough */
|
||||||
|
@ -71,9 +71,8 @@ static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = {
|
static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = {
|
||||||
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
|
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
|
||||||
TMIO_MMC_CLK_ACTUAL | TMIO_MMC_HAVE_CBSY |
|
TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
|
||||||
TMIO_MMC_MIN_RCAR2,
|
|
||||||
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
|
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
|
||||||
MMC_CAP_CMD23,
|
MMC_CAP_CMD23,
|
||||||
.bus_shift = 2,
|
.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;
|
u32 dtran_mode = DTRAN_MODE_BUS_WID_TH | DTRAN_MODE_ADDR_MODE;
|
||||||
enum dma_data_direction dir;
|
enum dma_data_direction dir;
|
||||||
int ret;
|
int ret;
|
||||||
u32 irq_mask;
|
|
||||||
|
|
||||||
/* This DMAC cannot handle if sg_len is not 1 */
|
/* This DMAC cannot handle if sg_len is not 1 */
|
||||||
WARN_ON(host->sg_len > 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) {
|
if (data->flags & MMC_DATA_READ) {
|
||||||
dtran_mode |= DTRAN_MODE_CH_NUM_CH1;
|
dtran_mode |= DTRAN_MODE_CH_NUM_CH1;
|
||||||
dir = DMA_FROM_DEVICE;
|
dir = DMA_FROM_DEVICE;
|
||||||
irq_mask = TMIO_STAT_RXRDY;
|
|
||||||
} else {
|
} else {
|
||||||
dtran_mode |= DTRAN_MODE_CH_NUM_CH0;
|
dtran_mode |= DTRAN_MODE_CH_NUM_CH0;
|
||||||
dir = DMA_TO_DEVICE;
|
dir = DMA_TO_DEVICE;
|
||||||
irq_mask = TMIO_STAT_TXRQ;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = dma_map_sg(&host->pdev->dev, sg, host->sg_len, dir);
|
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);
|
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 */
|
/* set dma parameters */
|
||||||
renesas_sdhi_internal_dmac_dm_write(host, DM_CM_DTRAN_MODE,
|
renesas_sdhi_internal_dmac_dm_write(host, DM_CM_DTRAN_MODE,
|
||||||
dtran_mode);
|
dtran_mode);
|
||||||
|
@ -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 = {
|
static const struct renesas_sdhi_of_data of_rcar_gen1_compatible = {
|
||||||
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
|
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL,
|
||||||
TMIO_MMC_CLK_ACTUAL,
|
|
||||||
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
|
.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 = {
|
static const struct renesas_sdhi_of_data of_rcar_gen2_compatible = {
|
||||||
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
|
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
|
||||||
TMIO_MMC_CLK_ACTUAL | TMIO_MMC_HAVE_CBSY |
|
TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
|
||||||
TMIO_MMC_MIN_RCAR2,
|
|
||||||
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
|
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
|
||||||
MMC_CAP_CMD23,
|
MMC_CAP_CMD23,
|
||||||
.dma_buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES,
|
.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 = {
|
static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = {
|
||||||
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
|
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
|
||||||
TMIO_MMC_CLK_ACTUAL | TMIO_MMC_HAVE_CBSY |
|
TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
|
||||||
TMIO_MMC_MIN_RCAR2,
|
|
||||||
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
|
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
|
||||||
MMC_CAP_CMD23,
|
MMC_CAP_CMD23,
|
||||||
.bus_shift = 2,
|
.bus_shift = 2,
|
||||||
@ -205,8 +202,6 @@ static void renesas_sdhi_sys_dmac_start_dma_rx(struct tmio_mmc_host *host)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tmio_mmc_disable_mmc_irqs(host, TMIO_STAT_RXRDY);
|
|
||||||
|
|
||||||
/* The only sg element can be unaligned, use our bounce buffer then */
|
/* The only sg element can be unaligned, use our bounce buffer then */
|
||||||
if (!aligned) {
|
if (!aligned) {
|
||||||
sg_init_one(&host->bounce_sg, host->bounce_buf, sg->length);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tmio_mmc_disable_mmc_irqs(host, TMIO_STAT_TXRQ);
|
|
||||||
|
|
||||||
/* The only sg element can be unaligned, use our bounce buffer then */
|
/* The only sg element can be unaligned, use our bounce buffer then */
|
||||||
if (!aligned) {
|
if (!aligned) {
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
@ -214,6 +214,7 @@ static const struct sdhci_pltfm_data sdhci_bcm2835_pltfm_data = {
|
|||||||
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
|
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
|
||||||
SDHCI_QUIRK_MISSING_CAPS |
|
SDHCI_QUIRK_MISSING_CAPS |
|
||||||
SDHCI_QUIRK_NO_HISPD_BIT,
|
SDHCI_QUIRK_NO_HISPD_BIT,
|
||||||
|
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
||||||
.ops = &sdhci_iproc_32only_ops,
|
.ops = &sdhci_iproc_32only_ops,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -25,17 +25,32 @@
|
|||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
|
#include <linux/pinctrl/consumer.h>
|
||||||
|
|
||||||
#include "sdhci-pltfm.h"
|
#include "sdhci-pltfm.h"
|
||||||
|
|
||||||
#define SDHCI_OMAP_CON 0x12c
|
#define SDHCI_OMAP_CON 0x12c
|
||||||
#define CON_DW8 BIT(5)
|
#define CON_DW8 BIT(5)
|
||||||
#define CON_DMA_MASTER BIT(20)
|
#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_INIT BIT(1)
|
||||||
#define CON_OD BIT(0)
|
#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_CMD 0x20c
|
||||||
|
|
||||||
|
#define SDHCI_OMAP_PSTATE 0x0224
|
||||||
|
#define PSTATE_DLEV_DAT0 BIT(20)
|
||||||
|
#define PSTATE_DATI BIT(1)
|
||||||
|
|
||||||
#define SDHCI_OMAP_HCTL 0x228
|
#define SDHCI_OMAP_HCTL 0x228
|
||||||
#define HCTL_SDBP BIT(8)
|
#define HCTL_SDBP BIT(8)
|
||||||
#define HCTL_SDVS_SHIFT 9
|
#define HCTL_SDVS_SHIFT 9
|
||||||
@ -56,12 +71,16 @@
|
|||||||
|
|
||||||
#define SDHCI_OMAP_AC12 0x23c
|
#define SDHCI_OMAP_AC12 0x23c
|
||||||
#define AC12_V1V8_SIGEN BIT(19)
|
#define AC12_V1V8_SIGEN BIT(19)
|
||||||
|
#define AC12_SCLK_SEL BIT(23)
|
||||||
|
|
||||||
#define SDHCI_OMAP_CAPA 0x240
|
#define SDHCI_OMAP_CAPA 0x240
|
||||||
#define CAPA_VS33 BIT(24)
|
#define CAPA_VS33 BIT(24)
|
||||||
#define CAPA_VS30 BIT(25)
|
#define CAPA_VS30 BIT(25)
|
||||||
#define CAPA_VS18 BIT(26)
|
#define CAPA_VS18 BIT(26)
|
||||||
|
|
||||||
|
#define SDHCI_OMAP_CAPA2 0x0244
|
||||||
|
#define CAPA2_TSDR50 BIT(13)
|
||||||
|
|
||||||
#define SDHCI_OMAP_TIMEOUT 1 /* 1 msec */
|
#define SDHCI_OMAP_TIMEOUT 1 /* 1 msec */
|
||||||
|
|
||||||
#define SYSCTL_CLKD_MAX 0x3FF
|
#define SYSCTL_CLKD_MAX 0x3FF
|
||||||
@ -70,8 +89,14 @@
|
|||||||
#define IOV_3V0 3000000 /* 300000 uV */
|
#define IOV_3V0 3000000 /* 300000 uV */
|
||||||
#define IOV_3V3 3300000 /* 330000 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 {
|
struct sdhci_omap_data {
|
||||||
u32 offset;
|
u32 offset;
|
||||||
|
u8 flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sdhci_omap_host {
|
struct sdhci_omap_host {
|
||||||
@ -82,8 +107,16 @@ struct sdhci_omap_host {
|
|||||||
struct sdhci_host *host;
|
struct sdhci_host *host;
|
||||||
u8 bus_mode;
|
u8 bus_mode;
|
||||||
u8 power_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,
|
static inline u32 sdhci_omap_readl(struct sdhci_omap_host *host,
|
||||||
unsigned int offset)
|
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,
|
static int sdhci_omap_start_signal_voltage_switch(struct mmc_host *mmc,
|
||||||
struct mmc_ios *ios)
|
struct mmc_ios *ios)
|
||||||
{
|
{
|
||||||
@ -244,6 +449,39 @@ static int sdhci_omap_start_signal_voltage_switch(struct mmc_host *mmc,
|
|||||||
return 0;
|
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,
|
static void sdhci_omap_set_bus_mode(struct sdhci_omap_host *omap_host,
|
||||||
unsigned int mode)
|
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);
|
omap_host = sdhci_pltfm_priv(pltfm_host);
|
||||||
|
|
||||||
sdhci_omap_set_bus_mode(omap_host, ios->bus_mode);
|
sdhci_omap_set_bus_mode(omap_host, ios->bus_mode);
|
||||||
|
sdhci_omap_set_timing(omap_host, ios->timing);
|
||||||
sdhci_set_ios(mmc, ios);
|
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,
|
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);
|
sdhci_omap_writel(omap_host, SDHCI_OMAP_STAT, INT_CC_EN);
|
||||||
|
|
||||||
enable_irq(host->irq);
|
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 = {
|
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,
|
.set_bus_width = sdhci_omap_set_bus_width,
|
||||||
.platform_send_init_74_clocks = sdhci_omap_init_74_clocks,
|
.platform_send_init_74_clocks = sdhci_omap_init_74_clocks,
|
||||||
.reset = sdhci_reset,
|
.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)
|
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_CAP_CLOCK_BASE_BROKEN |
|
||||||
SDHCI_QUIRK_NO_HISPD_BIT |
|
SDHCI_QUIRK_NO_HISPD_BIT |
|
||||||
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
|
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
|
||||||
.quirks2 = SDHCI_QUIRK2_NO_1_8_V |
|
.quirks2 = SDHCI_QUIRK2_ACMD23_BROKEN |
|
||||||
SDHCI_QUIRK2_ACMD23_BROKEN |
|
SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||||
SDHCI_QUIRK2_RSP_136_HAS_CRC,
|
SDHCI_QUIRK2_RSP_136_HAS_CRC,
|
||||||
.ops = &sdhci_omap_ops,
|
.ops = &sdhci_omap_ops,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct sdhci_omap_data dra7_data = {
|
static const struct sdhci_omap_data dra7_data = {
|
||||||
.offset = 0x200,
|
.offset = 0x200,
|
||||||
|
.flags = SDHCI_OMAP_REQUIRE_IODELAY,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id omap_sdhci_match[] = {
|
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);
|
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)
|
static int sdhci_omap_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
@ -504,6 +865,9 @@ static int sdhci_omap_probe(struct platform_device *pdev)
|
|||||||
omap_host->host = host;
|
omap_host->host = host;
|
||||||
omap_host->base = host->ioaddr;
|
omap_host->base = host->ioaddr;
|
||||||
omap_host->dev = dev;
|
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;
|
host->ioaddr += offset;
|
||||||
|
|
||||||
mmc = host->mmc;
|
mmc = host->mmc;
|
||||||
@ -552,10 +916,16 @@ static int sdhci_omap_probe(struct platform_device *pdev)
|
|||||||
goto err_put_sync;
|
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.get_ro = mmc_gpio_get_ro;
|
||||||
host->mmc_host_ops.start_signal_voltage_switch =
|
host->mmc_host_ops.start_signal_voltage_switch =
|
||||||
sdhci_omap_start_signal_voltage_switch;
|
sdhci_omap_start_signal_voltage_switch;
|
||||||
host->mmc_host_ops.set_ios = sdhci_omap_set_ios;
|
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);
|
sdhci_read_caps(host);
|
||||||
host->caps |= SDHCI_CAN_DO_ADMA2;
|
host->caps |= SDHCI_CAN_DO_ADMA2;
|
||||||
|
@ -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)
|
static int sdhci_pci_init_wakeup(struct sdhci_pci_chip *chip)
|
||||||
{
|
{
|
||||||
mmc_pm_flag_t pm_flags = 0;
|
mmc_pm_flag_t pm_flags = 0;
|
||||||
|
bool cap_cd_wake = false;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < chip->num_slots; i++) {
|
for (i = 0; i < chip->num_slots; i++) {
|
||||||
struct sdhci_pci_slot *slot = chip->slots[i];
|
struct sdhci_pci_slot *slot = chip->slots[i];
|
||||||
|
|
||||||
if (slot)
|
if (slot) {
|
||||||
pm_flags |= slot->host->mmc->pm_flags;
|
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,
|
if ((pm_flags & MMC_PM_KEEP_POWER) && (pm_flags & MMC_PM_WAKE_SDIO_IRQ))
|
||||||
(pm_flags & MMC_PM_KEEP_POWER) &&
|
return device_wakeup_enable(&chip->pdev->dev);
|
||||||
(pm_flags & MMC_PM_WAKE_SDIO_IRQ));
|
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)
|
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);
|
ret = sdhci_suspend_host(host);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_pci_suspend;
|
goto err_pci_suspend;
|
||||||
|
|
||||||
|
if (device_may_wakeup(&chip->pdev->dev))
|
||||||
|
mmc_gpio_set_cd_wake(host->mmc, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -99,6 +109,8 @@ int sdhci_pci_resume_host(struct sdhci_pci_chip *chip)
|
|||||||
ret = sdhci_resume_host(slot->host);
|
ret = sdhci_resume_host(slot->host);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
mmc_gpio_set_cd_wake(slot->host->mmc, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -712,26 +724,8 @@ static int glk_emmc_probe_slot(struct sdhci_pci_slot *slot)
|
|||||||
return ret;
|
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 = {
|
static const struct cqhci_host_ops glk_cqhci_ops = {
|
||||||
.enable = glk_cqe_enable,
|
.enable = sdhci_cqe_enable,
|
||||||
.disable = sdhci_cqe_disable,
|
.disable = sdhci_cqe_disable,
|
||||||
.dumpregs = sdhci_pci_dumpregs,
|
.dumpregs = sdhci_pci_dumpregs,
|
||||||
};
|
};
|
||||||
@ -1716,6 +1710,9 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
|
|||||||
if (device_can_wakeup(&pdev->dev))
|
if (device_can_wakeup(&pdev->dev))
|
||||||
host->mmc->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
|
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) {
|
if (slot->cd_idx >= 0) {
|
||||||
ret = mmc_gpiod_request_cd(host->mmc, NULL, slot->cd_idx,
|
ret = mmc_gpiod_request_cd(host->mmc, NULL, slot->cd_idx,
|
||||||
slot->cd_override_level, 0, NULL);
|
slot->cd_override_level, 0, NULL);
|
||||||
|
@ -2899,6 +2899,14 @@ static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
|
|||||||
\*****************************************************************************/
|
\*****************************************************************************/
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#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
|
* To enable wakeup events, the corresponding events have to be enabled in
|
||||||
* the Interrupt Status Enable register too. See 'Table 1-6: Wakeup Signal
|
* 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 wake_val = 0;
|
||||||
u8 val;
|
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;
|
wake_val |= SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE;
|
||||||
irq_val |= SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE;
|
irq_val |= SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
wake_val |= SDHCI_WAKE_ON_INT;
|
if (mmc_card_wake_sdio_irq(host->mmc)) {
|
||||||
irq_val |= SDHCI_INT_CARD_INT;
|
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 = sdhci_readb(host, SDHCI_WAKE_UP_CONTROL);
|
||||||
val &= ~mask;
|
val &= ~mask;
|
||||||
|
@ -7,13 +7,6 @@
|
|||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation; either version 2 of the License.
|
* 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>
|
#include <linux/module.h>
|
||||||
|
|
||||||
#define DRIVER_NAME "sh_mmcif"
|
#define DRIVER_NAME "sh_mmcif"
|
||||||
#define DRIVER_VERSION "2010-04-28"
|
|
||||||
|
|
||||||
/* CE_CMD_SET */
|
/* CE_CMD_SET */
|
||||||
#define CMD_MASK 0x3f000000
|
#define CMD_MASK 0x3f000000
|
||||||
|
@ -268,6 +268,7 @@ struct sunxi_mmc_cfg {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct sunxi_mmc_host {
|
struct sunxi_mmc_host {
|
||||||
|
struct device *dev;
|
||||||
struct mmc_host *mmc;
|
struct mmc_host *mmc;
|
||||||
struct reset_control *reset;
|
struct reset_control *reset;
|
||||||
const struct sunxi_mmc_cfg *cfg;
|
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);
|
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,
|
static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host,
|
||||||
struct platform_device *pdev)
|
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)
|
if (PTR_ERR(host->reset) == -EPROBE_DEFER)
|
||||||
return PTR_ERR(host->reset);
|
return PTR_ERR(host->reset);
|
||||||
|
|
||||||
ret = clk_prepare_enable(host->clk_ahb);
|
ret = sunxi_mmc_enable(host);
|
||||||
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);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
goto error_assert_reset;
|
return ret;
|
||||||
|
|
||||||
host->irq = platform_get_irq(pdev, 0);
|
host->irq = platform_get_irq(pdev, 0);
|
||||||
if (host->irq <= 0) {
|
if (host->irq <= 0) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto error_assert_reset;
|
goto error_disable_mmc;
|
||||||
}
|
}
|
||||||
|
|
||||||
return devm_request_threaded_irq(&pdev->dev, host->irq, sunxi_mmc_irq,
|
return devm_request_threaded_irq(&pdev->dev, host->irq, sunxi_mmc_irq,
|
||||||
sunxi_mmc_handle_manual_stop, 0, "sunxi-mmc", host);
|
sunxi_mmc_handle_manual_stop, 0, "sunxi-mmc", host);
|
||||||
|
|
||||||
error_assert_reset:
|
error_disable_mmc:
|
||||||
if (!IS_ERR(host->reset))
|
sunxi_mmc_disable(host);
|
||||||
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);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1288,8 +1318,10 @@ static int sunxi_mmc_probe(struct platform_device *pdev)
|
|||||||
dev_err(&pdev->dev, "mmc alloc host failed\n");
|
dev_err(&pdev->dev, "mmc alloc host failed\n");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
platform_set_drvdata(pdev, mmc);
|
||||||
|
|
||||||
host = mmc_priv(mmc);
|
host = mmc_priv(mmc);
|
||||||
|
host->dev = &pdev->dev;
|
||||||
host->mmc = mmc;
|
host->mmc = mmc;
|
||||||
spin_lock_init(&host->lock);
|
spin_lock_init(&host->lock);
|
||||||
|
|
||||||
@ -1353,7 +1385,6 @@ static int sunxi_mmc_probe(struct platform_device *pdev)
|
|||||||
goto error_free_dma;
|
goto error_free_dma;
|
||||||
|
|
||||||
dev_info(&pdev->dev, "base:0x%p irq:%u\n", host->reg_base, host->irq);
|
dev_info(&pdev->dev, "base:0x%p irq:%u\n", host->reg_base, host->irq);
|
||||||
platform_set_drvdata(pdev, mmc);
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error_free_dma:
|
error_free_dma:
|
||||||
@ -1370,16 +1401,7 @@ static int sunxi_mmc_remove(struct platform_device *pdev)
|
|||||||
|
|
||||||
mmc_remove_host(mmc);
|
mmc_remove_host(mmc);
|
||||||
disable_irq(host->irq);
|
disable_irq(host->irq);
|
||||||
sunxi_mmc_reset_host(host);
|
sunxi_mmc_disable(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);
|
|
||||||
|
|
||||||
dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
|
dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
|
||||||
mmc_free_host(mmc);
|
mmc_free_host(mmc);
|
||||||
|
|
||||||
|
@ -278,7 +278,6 @@ static void tmio_mmc_reset_work(struct work_struct *work)
|
|||||||
|
|
||||||
host->cmd = NULL;
|
host->cmd = NULL;
|
||||||
host->data = NULL;
|
host->data = NULL;
|
||||||
host->force_pio = false;
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
|
|
||||||
@ -350,8 +349,6 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host,
|
|||||||
c |= TRANSFER_READ;
|
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);
|
tmio_mmc_enable_mmc_irqs(host, irq_mask);
|
||||||
|
|
||||||
/* Fire off the command */
|
/* 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 && (!cmd->error || cmd->error == -EILSEQ)) {
|
||||||
if (host->data->flags & MMC_DATA_READ) {
|
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);
|
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);
|
tasklet_schedule(&host->dma_issue);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (host->force_pio || !host->chan_tx)
|
if (host->force_pio || !host->chan_tx) {
|
||||||
tmio_mmc_enable_mmc_irqs(host, TMIO_MASK_WRITEOP);
|
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);
|
tasklet_schedule(&host->dma_issue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
schedule_work(&host->done);
|
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);
|
tmio_mmc_init_sg(host, data);
|
||||||
host->data = data;
|
host->data = data;
|
||||||
|
host->force_pio = false;
|
||||||
|
|
||||||
/* Set transfer length / blocksize */
|
/* Set transfer length / blocksize */
|
||||||
sd_ctrl_write16(host, CTL_SD_XFER_LEN, data->blksz);
|
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;
|
return;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
host->force_pio = false;
|
|
||||||
host->mrq = NULL;
|
host->mrq = NULL;
|
||||||
mrq->cmd->error = ret;
|
mrq->cmd->error = ret;
|
||||||
mmc_request_done(host->mmc, mrq);
|
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) {
|
if (host->cmd != mrq->sbc) {
|
||||||
host->cmd = NULL;
|
host->cmd = NULL;
|
||||||
host->data = NULL;
|
host->data = NULL;
|
||||||
host->force_pio = false;
|
|
||||||
host->mrq = NULL;
|
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)
|
static int tmio_mmc_get_ro(struct mmc_host *mmc)
|
||||||
{
|
{
|
||||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||||
struct tmio_mmc_data *pdata = host->pdata;
|
|
||||||
|
|
||||||
return !((pdata->flags & TMIO_MMC_WRPROTECT_DISABLE) ||
|
return !(sd_ctrl_read16_and_16_as_32(host, CTL_STATUS) &
|
||||||
(sd_ctrl_read16_and_16_as_32(host, CTL_STATUS) & TMIO_STAT_WRPROTECT));
|
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,
|
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,
|
.request = tmio_mmc_request,
|
||||||
.set_ios = tmio_mmc_set_ios,
|
.set_ios = tmio_mmc_set_ios,
|
||||||
.get_ro = tmio_mmc_get_ro,
|
.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,
|
.enable_sdio_irq = tmio_mmc_enable_sdio_irq,
|
||||||
.multi_io_quirk = tmio_multi_io_quirk,
|
.multi_io_quirk = tmio_multi_io_quirk,
|
||||||
.hw_reset = tmio_mmc_hw_reset,
|
.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,
|
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;
|
const struct device_node *np = pdev->dev.of_node;
|
||||||
|
|
||||||
if (!np)
|
if (!np)
|
||||||
return;
|
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))
|
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,
|
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;
|
goto free;
|
||||||
}
|
}
|
||||||
|
|
||||||
tmio_mmc_of_parse(pdev, pdata);
|
tmio_mmc_of_parse(pdev, mmc);
|
||||||
|
|
||||||
platform_set_drvdata(pdev, host);
|
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 tmio_mmc_data *pdata = _host->pdata;
|
||||||
struct mmc_host *mmc = _host->mmc;
|
struct mmc_host *mmc = _host->mmc;
|
||||||
int ret;
|
int ret;
|
||||||
u32 irq_mask = TMIO_MASK_CMD;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check the sanity of mmc->f_min to prevent tmio_mmc_set_clock() from
|
* 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))
|
if (mmc_can_gpio_ro(mmc))
|
||||||
_host->ops.get_ro = mmc_gpio_get_ro;
|
_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) ||
|
_host->native_hotplug = !(mmc_can_gpio_cd(mmc) ||
|
||||||
mmc->caps & MMC_CAP_NEEDS_POLL ||
|
mmc->caps & MMC_CAP_NEEDS_POLL ||
|
||||||
!mmc_card_is_removable(mmc));
|
!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);
|
_host->sdcard_irq_mask = sd_ctrl_read16_and_16_as_32(_host, CTL_IRQ_MASK);
|
||||||
tmio_mmc_disable_mmc_irqs(_host, TMIO_MASK_ALL);
|
tmio_mmc_disable_mmc_irqs(_host, TMIO_MASK_ALL);
|
||||||
|
|
||||||
/* Unmask the IRQs we want to know about */
|
if (_host->native_hotplug)
|
||||||
if (!_host->chan_rx)
|
tmio_mmc_enable_mmc_irqs(_host,
|
||||||
irq_mask |= TMIO_MASK_READOP;
|
TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT);
|
||||||
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;
|
|
||||||
|
|
||||||
spin_lock_init(&_host->lock);
|
spin_lock_init(&_host->lock);
|
||||||
mutex_init(&_host->ios_lock);
|
mutex_init(&_host->ios_lock);
|
||||||
@ -1367,6 +1377,10 @@ int tmio_mmc_host_runtime_resume(struct device *dev)
|
|||||||
if (host->clk_cache)
|
if (host->clk_cache)
|
||||||
tmio_mmc_set_clock(host, 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);
|
tmio_mmc_enable_dma(host, true);
|
||||||
|
|
||||||
if (tmio_mmc_can_retune(host) && host->select_tuning(host))
|
if (tmio_mmc_can_retune(host) && host->select_tuning(host))
|
||||||
|
@ -309,8 +309,6 @@ static void ushc_request(struct mmc_host *mmc, struct mmc_request *req)
|
|||||||
|
|
||||||
/* Submit CSW. */
|
/* Submit CSW. */
|
||||||
ret = usb_submit_urb(ushc->csw_urb, GFP_ATOMIC);
|
ret = usb_submit_urb(ushc->csw_urb, GFP_ATOMIC);
|
||||||
if (ret < 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
out:
|
out:
|
||||||
spin_unlock_irqrestore(&ushc->lock, flags);
|
spin_unlock_irqrestore(&ushc->lock, flags);
|
||||||
|
@ -36,7 +36,6 @@
|
|||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
/* tmio MMC platform flags */
|
/* tmio MMC platform flags */
|
||||||
#define TMIO_MMC_WRPROTECT_DISABLE BIT(0)
|
|
||||||
/*
|
/*
|
||||||
* Some controllers can support a 2-byte block size when the bus width
|
* Some controllers can support a 2-byte block size when the bus width
|
||||||
* is configured in 4-bit mode.
|
* is configured in 4-bit mode.
|
||||||
|
@ -31,6 +31,7 @@ int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
|
|||||||
unsigned int debounce, bool *gpio_invert);
|
unsigned int debounce, bool *gpio_invert);
|
||||||
void mmc_gpio_set_cd_isr(struct mmc_host *host,
|
void mmc_gpio_set_cd_isr(struct mmc_host *host,
|
||||||
irqreturn_t (*isr)(int irq, void *dev_id));
|
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);
|
void mmc_gpiod_request_cd_irq(struct mmc_host *host);
|
||||||
bool mmc_can_gpio_cd(struct mmc_host *host);
|
bool mmc_can_gpio_cd(struct mmc_host *host);
|
||||||
bool mmc_can_gpio_ro(struct mmc_host *host);
|
bool mmc_can_gpio_ro(struct mmc_host *host);
|
||||||
|
Loading…
Reference in New Issue
Block a user