mirror of https://gitee.com/openkylin/linux.git
MMC highlights for 3.14:
Core: - Avoid get_cd() on cards marked nonremovable. Drivers: - arasan: New driver for controllers found in e.g. Xilinx Zynq SoC. - dwmmc: Support Hisilicon K3 SoC controllers. - esdhc-imx: Support for HS200 mode, DDR modes on MX6, runtime PM. - sdhci-pci: Support O2Micro/BayHubTech controllers used in laptops like Lenovo ThinkPad W540, Dell Latitude E5440, Dell Latitude E6540. - tegra: Support Tegra124 SoCs. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.14 (GNU/Linux) iQIcBAABAgAGBQJS5G3QAAoJEHNBYZ7TNxYMO2AP/j5JoGfNCga/Ub708yGdJJvm /2j2dmfrjdAnohfhwkJI8gdQcaQylZi1QVEnbsyCYCRKo5575miywnHpj4dDyYxn bKaYgxFZI3zK7NxOvomCm0GI1C3/bm/10+rS8bqgIGGczRo8owWK0wkipgUgeEIK 8UHIjSLsITXRCWuFSd9SemDY3kd1VobFRD1ujJmeBaovRjkkKjAzb4cBOjoWWJPW YSZvbX4qm6eBrYz/pbpQX5RBUuGE91tcjD3yNtrMw/+tvg9G41vGg9iuinMmwJKh 3CqYpaNZPhl0QyJlyMjiAmfa/XEtuzhHS9Bly9ge7FzDfg7er5QqUSbmE3FxF6NZ gAS49OKObT6mDdz1L7xxJQKFBE3zT+Kvz+l7ZdtFZInhhH0pdHfjva32JvBp8aEr oJzJa5xzjtSLq18ZVlUpKkv73ClHGckuJUm41XN/xewuP5TcndKpzVDY/xpsl4lW AaI12HME//uCgn9NMZUUfiMXQPTXwGF4NFQBCB+lWnAnLbuFGNPG/2Ioeh2raKEA +ACwAvcxSW4PtVVbA8wCTqgL1B3GMllwQChtAqfA0twmf5MucoP0OMHRONU9TpDh WnpbmVvI9i+ZGF4wHxcgTicDIipuzlZsBsvHuGvVhNOXe374Iyksdz4AILPmLD3m 7kZy0K5Ms9B2IUATiUdg =wZBx -----END PGP SIGNATURE----- Merge tag 'mmc-updates-for-3.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc Pull MMC updates from Chris Ball: "MMC highlights for 3.14: Core: - Avoid get_cd() on cards marked nonremovable Drivers: - arasan: New driver for controllers found in e.g. Xilinx Zynq SoC - dwmmc: Support Hisilicon K3 SoC controllers - esdhc-imx: Support for HS200 mode, DDR modes on MX6, runtime PM - sdhci-pci: Support O2Micro/BayHubTech controllers used in laptops like Lenovo ThinkPad W540, Dell Latitude E5440, Dell Latitude E6540 - tegra: Support Tegra124 SoCs" * tag 'mmc-updates-for-3.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc: (55 commits) mmc: sdhci-pci: Fix possibility of chip->fixes being null mmc: sdhci-pci: Fix BYT sd card getting stuck in runtime suspend mmc: sdhci: Allow for long command timeouts mmc: sdio: add a quirk for broken SDIO_CCCR_INTx polling mmc: sdhci: fix lockdep error in tuning routine mmc: dw_mmc: k3: remove clk_table mmc: dw_mmc: fix dw_mci_get_cd mmc: dw_mmc: fix sparse non static symbol warning mmc: sdhci-esdhc-imx: fix warning during module remove function mmc: sdhci-esdhc-imx: fix access hardirq-unsafe lock in atomic context mmc: core: sd: implement proper support for sd3.0 au sizes mmc: atmel-mci: add vmmc-supply support mmc: sdhci-pci: add broken HS200 quirk for Intel Merrifield mmc: sdhci: add quirk for broken HS200 support mmc: arasan: Add driver for Arasan SDHCI mmc: dw_mmc: add dw_mmc-k3 for k3 platform mmc: dw_mmc: use slot-gpio to handle cd pin mmc: sdhci-pci: add support of O2Micro/BayHubTech SD hosts mmc: sdhci-pci: break out definitions to header file mmc: tmio: fixup compile error ... Conflicts: MAINTAINERS
This commit is contained in:
commit
ccc039d65f
|
@ -0,0 +1,27 @@
|
|||
Device Tree Bindings for the Arasan SDHCI Controller
|
||||
|
||||
The bindings follow the mmc[1], clock[2] and interrupt[3] bindings. Only
|
||||
deviations are documented here.
|
||||
|
||||
[1] Documentation/devicetree/bindings/mmc/mmc.txt
|
||||
[2] Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
[3] Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
|
||||
|
||||
Required Properties:
|
||||
- compatible: Compatibility string. Must be 'arasan,sdhci-8.9a'
|
||||
- reg: From mmc bindings: Register location and length.
|
||||
- clocks: From clock bindings: Handles to clock inputs.
|
||||
- clock-names: From clock bindings: Tuple including "clk_xin" and "clk_ahb"
|
||||
- interrupts: Interrupt specifier
|
||||
- interrupt-parent: Phandle for the interrupt controller that services
|
||||
interrupts for this device.
|
||||
|
||||
Example:
|
||||
sdhci@e0100000 {
|
||||
compatible = "arasan,sdhci-8.9a";
|
||||
reg = <0xe0100000 0x1000>;
|
||||
clock-names = "clk_xin", "clk_ahb";
|
||||
clocks = <&clkc 21>, <&clkc 32>;
|
||||
interrupt-parent = <&gic>;
|
||||
interrupts = <0 24 4>;
|
||||
} ;
|
|
@ -0,0 +1,46 @@
|
|||
* Hisilicon 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 specific
|
||||
extensions to the Synopsys Designware Mobile Storage Host Controller.
|
||||
|
||||
Required Properties:
|
||||
|
||||
* compatible: should be one of the following.
|
||||
- "hisilicon,hi4511-dw-mshc": for controllers with hi4511 specific extentions.
|
||||
|
||||
Example:
|
||||
|
||||
/* for Hi3620 */
|
||||
|
||||
/* SoC portion */
|
||||
dwmmc_0: dwmmc0@fcd03000 {
|
||||
compatible = "hisilicon,hi4511-dw-mshc";
|
||||
reg = <0xfcd03000 0x1000>;
|
||||
interrupts = <0 16 4>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clocks = <&mmc_clock HI3620_SD_CIUCLK>, <&clock HI3620_DDRC_PER_CLK>;
|
||||
clock-names = "ciu", "biu";
|
||||
};
|
||||
|
||||
/* Board portion */
|
||||
dwmmc0@fcd03000 {
|
||||
num-slots = <1>;
|
||||
vmmc-supply = <&ldo12>;
|
||||
fifo-depth = <0x100>;
|
||||
supports-highspeed;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&sd_pmx_pins &sd_cfg_func1 &sd_cfg_func2>;
|
||||
slot@0 {
|
||||
reg = <0>;
|
||||
bus-width = <4>;
|
||||
disable-wp;
|
||||
cd-gpios = <&gpio10 3 0>;
|
||||
};
|
||||
};
|
|
@ -1409,6 +1409,7 @@ F: drivers/cpuidle/cpuidle-zynq.c
|
|||
N: zynq
|
||||
N: xilinx
|
||||
F: drivers/clocksource/cadence_ttc_timer.c
|
||||
F: drivers/mmc/host/sdhci-of-arasan.c
|
||||
|
||||
ARM SMMU DRIVER
|
||||
M: Will Deacon <will.deacon@arm.com>
|
||||
|
@ -5782,7 +5783,7 @@ F: drivers/mfd/
|
|||
F: include/linux/mfd/
|
||||
|
||||
MULTIMEDIA CARD (MMC), SECURE DIGITAL (SD) AND SDIO SUBSYSTEM
|
||||
M: Chris Ball <cjb@laptop.org>
|
||||
M: Chris Ball <chris@printf.net>
|
||||
L: linux-mmc@vger.kernel.org
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc.git
|
||||
S: Maintained
|
||||
|
@ -7609,7 +7610,7 @@ S: Maintained
|
|||
F: drivers/mmc/host/sdricoh_cs.c
|
||||
|
||||
SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) DRIVER
|
||||
M: Chris Ball <cjb@laptop.org>
|
||||
M: Chris Ball <chris@printf.net>
|
||||
L: linux-mmc@vger.kernel.org
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc.git
|
||||
S: Maintained
|
||||
|
@ -8768,7 +8769,6 @@ F: include/linux/toshiba.h
|
|||
F: include/uapi/linux/toshiba.h
|
||||
|
||||
TMIO MMC DRIVER
|
||||
M: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
|
||||
M: Ian Molton <ian@mnementh.co.uk>
|
||||
L: linux-mmc@vger.kernel.org
|
||||
S: Maintained
|
||||
|
|
|
@ -1959,6 +1959,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
|
|||
struct mmc_card *card = md->queue.card;
|
||||
struct mmc_host *host = card->host;
|
||||
unsigned long flags;
|
||||
unsigned int cmd_flags = req ? req->cmd_flags : 0;
|
||||
|
||||
if (req && !mq->mqrq_prev->req)
|
||||
/* claim host only for the first request */
|
||||
|
@ -1974,7 +1975,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
|
|||
}
|
||||
|
||||
mq->flags &= ~MMC_QUEUE_NEW_REQUEST;
|
||||
if (req && req->cmd_flags & REQ_DISCARD) {
|
||||
if (cmd_flags & REQ_DISCARD) {
|
||||
/* complete ongoing async transfer before issuing discard */
|
||||
if (card->host->areq)
|
||||
mmc_blk_issue_rw_rq(mq, NULL);
|
||||
|
@ -1983,7 +1984,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
|
|||
ret = mmc_blk_issue_secdiscard_rq(mq, req);
|
||||
else
|
||||
ret = mmc_blk_issue_discard_rq(mq, req);
|
||||
} else if (req && req->cmd_flags & REQ_FLUSH) {
|
||||
} else if (cmd_flags & REQ_FLUSH) {
|
||||
/* complete ongoing async transfer before issuing flush */
|
||||
if (card->host->areq)
|
||||
mmc_blk_issue_rw_rq(mq, NULL);
|
||||
|
@ -1999,7 +2000,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
|
|||
|
||||
out:
|
||||
if ((!req && !(mq->flags & MMC_QUEUE_NEW_REQUEST)) ||
|
||||
(req && (req->cmd_flags & MMC_REQ_SPECIAL_MASK)))
|
||||
(cmd_flags & MMC_REQ_SPECIAL_MASK))
|
||||
/*
|
||||
* Release host when there are no more requests
|
||||
* and after special request(discard, flush) is done.
|
||||
|
|
|
@ -2460,7 +2460,8 @@ void mmc_rescan(struct work_struct *work)
|
|||
*/
|
||||
mmc_bus_put(host);
|
||||
|
||||
if (host->ops->get_cd && host->ops->get_cd(host) == 0) {
|
||||
if (!(host->caps & MMC_CAP_NONREMOVABLE) && host->ops->get_cd &&
|
||||
host->ops->get_cd(host) == 0) {
|
||||
mmc_claim_host(host);
|
||||
mmc_power_off(host);
|
||||
mmc_release_host(host);
|
||||
|
|
|
@ -1119,14 +1119,10 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
|||
*/
|
||||
if (mmc_card_highspeed(card)) {
|
||||
if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_8V)
|
||||
&& ((host->caps & (MMC_CAP_1_8V_DDR |
|
||||
MMC_CAP_UHS_DDR50))
|
||||
== (MMC_CAP_1_8V_DDR | MMC_CAP_UHS_DDR50)))
|
||||
&& (host->caps & MMC_CAP_1_8V_DDR))
|
||||
ddr = MMC_1_8V_DDR_MODE;
|
||||
else if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_2V)
|
||||
&& ((host->caps & (MMC_CAP_1_2V_DDR |
|
||||
MMC_CAP_UHS_DDR50))
|
||||
== (MMC_CAP_1_2V_DDR | MMC_CAP_UHS_DDR50)))
|
||||
&& (host->caps & MMC_CAP_1_2V_DDR))
|
||||
ddr = MMC_1_2V_DDR_MODE;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/mmc/card.h>
|
||||
#include <linux/mmc/sdio_ids.h>
|
||||
|
||||
#ifndef SDIO_VENDOR_ID_TI
|
||||
#define SDIO_VENDOR_ID_TI 0x0097
|
||||
|
@ -30,6 +31,10 @@
|
|||
#define SDIO_DEVICE_ID_STE_CW1200 0x2280
|
||||
#endif
|
||||
|
||||
#ifndef SDIO_DEVICE_ID_MARVELL_8797_F0
|
||||
#define SDIO_DEVICE_ID_MARVELL_8797_F0 0x9128
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This hook just adds a quirk for all sdio devices
|
||||
*/
|
||||
|
@ -58,6 +63,9 @@ static const struct mmc_fixup mmc_fixup_methods[] = {
|
|||
SDIO_FIXUP(SDIO_VENDOR_ID_STE, SDIO_DEVICE_ID_STE_CW1200,
|
||||
add_quirk, MMC_QUIRK_BROKEN_BYTE_MODE_512),
|
||||
|
||||
SDIO_FIXUP(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797_F0,
|
||||
add_quirk, MMC_QUIRK_BROKEN_IRQ_POLLING),
|
||||
|
||||
END_FIXUP
|
||||
};
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
@ -45,6 +46,13 @@ static const unsigned int tacc_mant[] = {
|
|||
35, 40, 45, 50, 55, 60, 70, 80,
|
||||
};
|
||||
|
||||
static const unsigned int sd_au_size[] = {
|
||||
0, SZ_16K / 512, SZ_32K / 512, SZ_64K / 512,
|
||||
SZ_128K / 512, SZ_256K / 512, SZ_512K / 512, SZ_1M / 512,
|
||||
SZ_2M / 512, SZ_4M / 512, SZ_8M / 512, (SZ_8M + SZ_4M) / 512,
|
||||
SZ_16M / 512, (SZ_16M + SZ_8M) / 512, SZ_32M / 512, SZ_64M / 512,
|
||||
};
|
||||
|
||||
#define UNSTUFF_BITS(resp,start,size) \
|
||||
({ \
|
||||
const int __size = size; \
|
||||
|
@ -216,7 +224,7 @@ static int mmc_decode_scr(struct mmc_card *card)
|
|||
static int mmc_read_ssr(struct mmc_card *card)
|
||||
{
|
||||
unsigned int au, es, et, eo;
|
||||
int err, i, max_au;
|
||||
int err, i;
|
||||
u32 *ssr;
|
||||
|
||||
if (!(card->csd.cmdclass & CCC_APP_SPEC)) {
|
||||
|
@ -240,26 +248,25 @@ static int mmc_read_ssr(struct mmc_card *card)
|
|||
for (i = 0; i < 16; i++)
|
||||
ssr[i] = be32_to_cpu(ssr[i]);
|
||||
|
||||
/* SD3.0 increases max AU size to 64MB (0xF) from 4MB (0x9) */
|
||||
max_au = card->scr.sda_spec3 ? 0xF : 0x9;
|
||||
|
||||
/*
|
||||
* UNSTUFF_BITS only works with four u32s so we have to offset the
|
||||
* bitfield positions accordingly.
|
||||
*/
|
||||
au = UNSTUFF_BITS(ssr, 428 - 384, 4);
|
||||
if (au > 0 && au <= max_au) {
|
||||
card->ssr.au = 1 << (au + 4);
|
||||
es = UNSTUFF_BITS(ssr, 408 - 384, 16);
|
||||
et = UNSTUFF_BITS(ssr, 402 - 384, 6);
|
||||
eo = UNSTUFF_BITS(ssr, 400 - 384, 2);
|
||||
if (es && et) {
|
||||
card->ssr.erase_timeout = (et * 1000) / es;
|
||||
card->ssr.erase_offset = eo * 1000;
|
||||
if (au) {
|
||||
if (au <= 9 || card->scr.sda_spec3) {
|
||||
card->ssr.au = sd_au_size[au];
|
||||
es = UNSTUFF_BITS(ssr, 408 - 384, 16);
|
||||
et = UNSTUFF_BITS(ssr, 402 - 384, 6);
|
||||
if (es && et) {
|
||||
eo = UNSTUFF_BITS(ssr, 400 - 384, 2);
|
||||
card->ssr.erase_timeout = (et * 1000) / es;
|
||||
card->ssr.erase_offset = eo * 1000;
|
||||
}
|
||||
} else {
|
||||
pr_warning("%s: SD Status: Invalid Allocation Unit size.\n",
|
||||
mmc_hostname(card->host));
|
||||
}
|
||||
} else {
|
||||
pr_warning("%s: SD Status: Invalid Allocation Unit "
|
||||
"size.\n", mmc_hostname(card->host));
|
||||
}
|
||||
out:
|
||||
kfree(ssr);
|
||||
|
|
|
@ -53,6 +53,17 @@ static int process_sdio_pending_irqs(struct mmc_host *host)
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (pending && mmc_card_broken_irq_polling(card) &&
|
||||
!(host->caps & MMC_CAP_SDIO_IRQ)) {
|
||||
unsigned char dummy;
|
||||
|
||||
/* A fake interrupt could be created when we poll SDIO_CCCR_INTx
|
||||
* register with a Marvell SD8797 card. A dummy CMD52 read to
|
||||
* function 0 register 0xff can avoid this.
|
||||
*/
|
||||
mmc_io_rw_direct(card, 0, 0, 0xff, 0, &dummy);
|
||||
}
|
||||
|
||||
count = 0;
|
||||
for (i = 1; i <= 7; i++) {
|
||||
if (pending & (1 << i)) {
|
||||
|
|
|
@ -104,6 +104,18 @@ config MMC_SDHCI_PLTFM
|
|||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_SDHCI_OF_ARASAN
|
||||
tristate "SDHCI OF support for the Arasan SDHCI controllers"
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
depends on OF
|
||||
help
|
||||
This selects the Arasan Secure Digital Host Controller Interface
|
||||
(SDHCI). This hardware is found e.g. in Xilinx' Zynq SoC.
|
||||
|
||||
If you have a controller with this interface, say Y or M here.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_SDHCI_OF_ESDHC
|
||||
tristate "SDHCI OF support for the Freescale eSDHC controller"
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
|
@ -479,7 +491,8 @@ config MMC_TMIO
|
|||
|
||||
config MMC_SDHI
|
||||
tristate "SH-Mobile SDHI SD/SDIO controller support"
|
||||
depends on SUPERH || ARCH_SHMOBILE
|
||||
depends on SUPERH || ARM
|
||||
depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
|
||||
select MMC_TMIO_CORE
|
||||
help
|
||||
This provides support for the SDHI SD/SDIO controller found in
|
||||
|
@ -575,6 +588,16 @@ config MMC_DW_SOCFPGA
|
|||
This selects support for Altera SoCFPGA specific extensions to the
|
||||
Synopsys DesignWare Memory Card Interface driver.
|
||||
|
||||
config MMC_DW_K3
|
||||
tristate "K3 specific extensions for Synopsys DW Memory Card Interface"
|
||||
depends on MMC_DW
|
||||
select MMC_DW_PLTFM
|
||||
select MMC_DW_IDMAC
|
||||
help
|
||||
This selects support for Hisilicon K3 SoC specific extensions to the
|
||||
Synopsys DesignWare Memory Card Interface driver. Select this option
|
||||
for platforms based on Hisilicon K3 SoC's.
|
||||
|
||||
config MMC_DW_PCI
|
||||
tristate "Synopsys Designware MCI support on PCI bus"
|
||||
depends on MMC_DW && PCI
|
||||
|
@ -588,7 +611,8 @@ config MMC_DW_PCI
|
|||
|
||||
config MMC_SH_MMCIF
|
||||
tristate "SuperH Internal MMCIF support"
|
||||
depends on MMC_BLOCK && (SUPERH || ARCH_SHMOBILE)
|
||||
depends on MMC_BLOCK
|
||||
depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
|
||||
help
|
||||
This selects the MMC Host Interface controller (MMCIF).
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ obj-$(CONFIG_MMC_MXS) += mxs-mmc.o
|
|||
obj-$(CONFIG_MMC_SDHCI) += sdhci.o
|
||||
obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o
|
||||
obj-$(subst m,y,$(CONFIG_MMC_SDHCI_PCI)) += sdhci-pci-data.o
|
||||
obj-$(subst m,y,$(CONFIG_MMC_SDHCI_PCI)) += sdhci-pci-o2micro.o
|
||||
obj-$(CONFIG_MMC_SDHCI_ACPI) += sdhci-acpi.o
|
||||
obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o
|
||||
obj-$(CONFIG_MMC_SDHCI_PXAV2) += sdhci-pxav2.o
|
||||
|
@ -43,6 +44,7 @@ obj-$(CONFIG_MMC_DW) += dw_mmc.o
|
|||
obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o
|
||||
obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o
|
||||
obj-$(CONFIG_MMC_DW_SOCFPGA) += dw_mmc-socfpga.o
|
||||
obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o
|
||||
obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o
|
||||
obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
|
||||
obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
|
||||
|
@ -57,6 +59,7 @@ obj-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o
|
|||
obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o
|
||||
obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o
|
||||
obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o
|
||||
obj-$(CONFIG_MMC_SDHCI_OF_ARASAN) += sdhci-of-arasan.o
|
||||
obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o
|
||||
obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o
|
||||
obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o
|
||||
|
|
|
@ -1192,11 +1192,22 @@ static void atmci_start_request(struct atmel_mci *host,
|
|||
iflags |= ATMCI_CMDRDY;
|
||||
cmd = mrq->cmd;
|
||||
cmdflags = atmci_prepare_command(slot->mmc, cmd);
|
||||
atmci_send_command(host, cmd, cmdflags);
|
||||
|
||||
/*
|
||||
* DMA transfer should be started before sending the command to avoid
|
||||
* unexpected errors especially for read operations in SDIO mode.
|
||||
* Unfortunately, in PDC mode, command has to be sent before starting
|
||||
* the transfer.
|
||||
*/
|
||||
if (host->submit_data != &atmci_submit_data_dma)
|
||||
atmci_send_command(host, cmd, cmdflags);
|
||||
|
||||
if (data)
|
||||
host->submit_data(host, data);
|
||||
|
||||
if (host->submit_data == &atmci_submit_data_dma)
|
||||
atmci_send_command(host, cmd, cmdflags);
|
||||
|
||||
if (mrq->stop) {
|
||||
host->stop_cmdr = atmci_prepare_command(slot->mmc, mrq->stop);
|
||||
host->stop_cmdr |= ATMCI_CMDR_STOP_XFER;
|
||||
|
@ -1391,8 +1402,14 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
clk_unprepare(host->mck);
|
||||
|
||||
switch (ios->power_mode) {
|
||||
case MMC_POWER_OFF:
|
||||
if (!IS_ERR(mmc->supply.vmmc))
|
||||
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
|
||||
break;
|
||||
case MMC_POWER_UP:
|
||||
set_bit(ATMCI_CARD_NEED_INIT, &slot->flags);
|
||||
if (!IS_ERR(mmc->supply.vmmc))
|
||||
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd);
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
|
@ -2204,6 +2221,7 @@ static int __init atmci_init_slot(struct atmel_mci *host,
|
|||
}
|
||||
|
||||
host->slot[id] = slot;
|
||||
mmc_regulator_get_supply(mmc);
|
||||
mmc_add_host(mmc);
|
||||
|
||||
if (gpio_is_valid(slot->detect_pin)) {
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Copyright (c) 2013 Linaro Ltd.
|
||||
* Copyright (c) 2013 Hisilicon Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/dw_mmc.h>
|
||||
#include <linux/of_address.h>
|
||||
|
||||
#include "dw_mmc.h"
|
||||
#include "dw_mmc-pltfm.h"
|
||||
|
||||
static void dw_mci_k3_set_ios(struct dw_mci *host, struct mmc_ios *ios)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = clk_set_rate(host->ciu_clk, ios->clock);
|
||||
if (ret)
|
||||
dev_warn(host->dev, "failed to set rate %uHz\n", ios->clock);
|
||||
|
||||
host->bus_hz = clk_get_rate(host->ciu_clk);
|
||||
}
|
||||
|
||||
static const struct dw_mci_drv_data k3_drv_data = {
|
||||
.set_ios = dw_mci_k3_set_ios,
|
||||
};
|
||||
|
||||
static const struct of_device_id dw_mci_k3_match[] = {
|
||||
{ .compatible = "hisilicon,hi4511-dw-mshc", .data = &k3_drv_data, },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dw_mci_k3_match);
|
||||
|
||||
static int dw_mci_k3_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct dw_mci_drv_data *drv_data;
|
||||
const struct of_device_id *match;
|
||||
|
||||
match = of_match_node(dw_mci_k3_match, pdev->dev.of_node);
|
||||
drv_data = match->data;
|
||||
|
||||
return dw_mci_pltfm_register(pdev, drv_data);
|
||||
}
|
||||
|
||||
static int dw_mci_k3_suspend(struct device *dev)
|
||||
{
|
||||
struct dw_mci *host = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = dw_mci_suspend(host);
|
||||
if (!ret)
|
||||
clk_disable_unprepare(host->ciu_clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dw_mci_k3_resume(struct device *dev)
|
||||
{
|
||||
struct dw_mci *host = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(host->ciu_clk);
|
||||
if (ret) {
|
||||
dev_err(host->dev, "failed to enable ciu clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return dw_mci_resume(host);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(dw_mci_k3_pmops, dw_mci_k3_suspend, dw_mci_k3_resume);
|
||||
|
||||
static struct platform_driver dw_mci_k3_pltfm_driver = {
|
||||
.probe = dw_mci_k3_probe,
|
||||
.remove = dw_mci_pltfm_remove,
|
||||
.driver = {
|
||||
.name = "dwmmc_k3",
|
||||
.of_match_table = dw_mci_k3_match,
|
||||
.pm = &dw_mci_k3_pmops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(dw_mci_k3_pltfm_driver);
|
||||
|
||||
MODULE_DESCRIPTION("K3 Specific DW-MSHC Driver Extension");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:dwmmc-k3");
|
|
@ -36,6 +36,7 @@
|
|||
#include <linux/workqueue.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
|
||||
#include "dw_mmc.h"
|
||||
|
||||
|
@ -1032,20 +1033,29 @@ static int dw_mci_get_cd(struct mmc_host *mmc)
|
|||
int present;
|
||||
struct dw_mci_slot *slot = mmc_priv(mmc);
|
||||
struct dw_mci_board *brd = slot->host->pdata;
|
||||
struct dw_mci *host = slot->host;
|
||||
int gpio_cd = mmc_gpio_get_cd(mmc);
|
||||
|
||||
/* Use platform get_cd function, else try onboard card detect */
|
||||
if (brd->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION)
|
||||
present = 1;
|
||||
else if (brd->get_cd)
|
||||
present = !brd->get_cd(slot->id);
|
||||
else if (!IS_ERR_VALUE(gpio_cd))
|
||||
present = gpio_cd;
|
||||
else
|
||||
present = (mci_readl(slot->host, CDETECT) & (1 << slot->id))
|
||||
== 0 ? 1 : 0;
|
||||
|
||||
if (present)
|
||||
spin_lock_bh(&host->lock);
|
||||
if (present) {
|
||||
set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
|
||||
dev_dbg(&mmc->class_dev, "card is present\n");
|
||||
else
|
||||
} else {
|
||||
clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
|
||||
dev_dbg(&mmc->class_dev, "card is not present\n");
|
||||
}
|
||||
spin_unlock_bh(&host->lock);
|
||||
|
||||
return present;
|
||||
}
|
||||
|
@ -1926,10 +1936,6 @@ static void dw_mci_work_routine_card(struct work_struct *work)
|
|||
/* Card change detected */
|
||||
slot->last_detect_state = present;
|
||||
|
||||
/* Mark card as present if applicable */
|
||||
if (present != 0)
|
||||
set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
|
||||
|
||||
/* Clean up queue if present */
|
||||
mrq = slot->mrq;
|
||||
if (mrq) {
|
||||
|
@ -1977,8 +1983,6 @@ static void dw_mci_work_routine_card(struct work_struct *work)
|
|||
|
||||
/* Power down slot */
|
||||
if (present == 0) {
|
||||
clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
|
||||
|
||||
/* Clear down the FIFO */
|
||||
dw_mci_fifo_reset(host);
|
||||
#ifdef CONFIG_MMC_DW_IDMAC
|
||||
|
@ -2079,6 +2083,26 @@ static int dw_mci_of_get_wp_gpio(struct device *dev, u8 slot)
|
|||
|
||||
return gpio;
|
||||
}
|
||||
|
||||
/* find the cd gpio for a given slot */
|
||||
static void dw_mci_of_get_cd_gpio(struct device *dev, u8 slot,
|
||||
struct mmc_host *mmc)
|
||||
{
|
||||
struct device_node *np = dw_mci_of_find_slot_node(dev, slot);
|
||||
int gpio;
|
||||
|
||||
if (!np)
|
||||
return;
|
||||
|
||||
gpio = of_get_named_gpio(np, "cd-gpios", 0);
|
||||
|
||||
/* Having a missing entry is valid; return silently */
|
||||
if (!gpio_is_valid(gpio))
|
||||
return;
|
||||
|
||||
if (mmc_gpio_request_cd(mmc, gpio, 0))
|
||||
dev_warn(dev, "gpio [%d] request failed\n", gpio);
|
||||
}
|
||||
#else /* CONFIG_OF */
|
||||
static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot)
|
||||
{
|
||||
|
@ -2096,6 +2120,11 @@ static int dw_mci_of_get_wp_gpio(struct device *dev, u8 slot)
|
|||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
static void dw_mci_of_get_cd_gpio(struct device *dev, u8 slot,
|
||||
struct mmc_host *mmc)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
||||
|
@ -2197,12 +2226,8 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
|||
#endif /* CONFIG_MMC_DW_IDMAC */
|
||||
}
|
||||
|
||||
if (dw_mci_get_cd(mmc))
|
||||
set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
|
||||
else
|
||||
clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
|
||||
|
||||
slot->wp_gpio = dw_mci_of_get_wp_gpio(host->dev, slot->id);
|
||||
dw_mci_of_get_cd_gpio(host->dev, slot->id, mmc);
|
||||
|
||||
ret = mmc_add_host(mmc);
|
||||
if (ret)
|
||||
|
@ -2389,6 +2414,9 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
|
|||
if (of_find_property(np, "caps2-mmc-hs200-1_2v", NULL))
|
||||
pdata->caps2 |= MMC_CAP2_HS200_1_2V_SDR;
|
||||
|
||||
if (of_get_property(np, "cd-inverted", NULL))
|
||||
pdata->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;
|
||||
|
||||
return pdata;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/mmc/sdio.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -69,37 +70,25 @@ struct mxs_mmc_host {
|
|||
unsigned char bus_width;
|
||||
spinlock_t lock;
|
||||
int sdio_irq_en;
|
||||
int wp_gpio;
|
||||
bool wp_inverted;
|
||||
bool cd_inverted;
|
||||
bool broken_cd;
|
||||
bool non_removable;
|
||||
};
|
||||
|
||||
static int mxs_mmc_get_ro(struct mmc_host *mmc)
|
||||
{
|
||||
struct mxs_mmc_host *host = mmc_priv(mmc);
|
||||
int ret;
|
||||
|
||||
if (!gpio_is_valid(host->wp_gpio))
|
||||
return -EINVAL;
|
||||
|
||||
ret = gpio_get_value(host->wp_gpio);
|
||||
|
||||
if (host->wp_inverted)
|
||||
ret = !ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mxs_mmc_get_cd(struct mmc_host *mmc)
|
||||
{
|
||||
struct mxs_mmc_host *host = mmc_priv(mmc);
|
||||
struct mxs_ssp *ssp = &host->ssp;
|
||||
int present, ret;
|
||||
|
||||
return host->non_removable || host->broken_cd ||
|
||||
!(readl(ssp->base + HW_SSP_STATUS(ssp)) &
|
||||
BM_SSP_STATUS_CARD_DETECT) ^ host->cd_inverted;
|
||||
ret = mmc_gpio_get_cd(mmc);
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
|
||||
present = !(readl(ssp->base + HW_SSP_STATUS(ssp)) &
|
||||
BM_SSP_STATUS_CARD_DETECT);
|
||||
|
||||
if (mmc->caps2 & MMC_CAP2_CD_ACTIVE_HIGH)
|
||||
present = !present;
|
||||
|
||||
return present;
|
||||
}
|
||||
|
||||
static int mxs_mmc_reset(struct mxs_mmc_host *host)
|
||||
|
@ -549,7 +538,7 @@ static void mxs_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
|
|||
|
||||
static const struct mmc_host_ops mxs_mmc_ops = {
|
||||
.request = mxs_mmc_request,
|
||||
.get_ro = mxs_mmc_get_ro,
|
||||
.get_ro = mmc_gpio_get_ro,
|
||||
.get_cd = mxs_mmc_get_cd,
|
||||
.set_ios = mxs_mmc_set_ios,
|
||||
.enable_sdio_irq = mxs_mmc_enable_sdio_irq,
|
||||
|
@ -579,15 +568,12 @@ static int mxs_mmc_probe(struct platform_device *pdev)
|
|||
{
|
||||
const struct of_device_id *of_id =
|
||||
of_match_device(mxs_mmc_dt_ids, &pdev->dev);
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct mxs_mmc_host *host;
|
||||
struct mmc_host *mmc;
|
||||
struct resource *iores;
|
||||
int ret = 0, irq_err;
|
||||
struct regulator *reg_vmmc;
|
||||
enum of_gpio_flags flags;
|
||||
struct mxs_ssp *ssp;
|
||||
u32 bus_width = 0;
|
||||
|
||||
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
irq_err = platform_get_irq(pdev, 0);
|
||||
|
@ -648,23 +634,13 @@ static int mxs_mmc_probe(struct platform_device *pdev)
|
|||
mmc->caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
|
||||
MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL;
|
||||
|
||||
of_property_read_u32(np, "bus-width", &bus_width);
|
||||
if (bus_width == 4)
|
||||
mmc->caps |= MMC_CAP_4_BIT_DATA;
|
||||
else if (bus_width == 8)
|
||||
mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA;
|
||||
host->broken_cd = of_property_read_bool(np, "broken-cd");
|
||||
host->non_removable = of_property_read_bool(np, "non-removable");
|
||||
if (host->non_removable)
|
||||
mmc->caps |= MMC_CAP_NONREMOVABLE;
|
||||
host->wp_gpio = of_get_named_gpio_flags(np, "wp-gpios", 0, &flags);
|
||||
if (flags & OF_GPIO_ACTIVE_LOW)
|
||||
host->wp_inverted = 1;
|
||||
|
||||
host->cd_inverted = of_property_read_bool(np, "cd-inverted");
|
||||
|
||||
mmc->f_min = 400000;
|
||||
mmc->f_max = 288000000;
|
||||
|
||||
ret = mmc_of_parse(mmc);
|
||||
if (ret)
|
||||
goto out_clk_disable;
|
||||
|
||||
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
|
||||
|
||||
mmc->max_segs = 52;
|
||||
|
|
|
@ -143,6 +143,7 @@ static const struct sdhci_acpi_uid_slot sdhci_acpi_uids[] = {
|
|||
{ "80860F14" , "3" , &sdhci_acpi_slot_int_sd },
|
||||
{ "INT33BB" , "2" , &sdhci_acpi_slot_int_sdio },
|
||||
{ "INT33C6" , NULL, &sdhci_acpi_slot_int_sdio },
|
||||
{ "INT3436" , NULL, &sdhci_acpi_slot_int_sdio },
|
||||
{ "PNP0D40" },
|
||||
{ },
|
||||
};
|
||||
|
@ -151,6 +152,7 @@ static const struct acpi_device_id sdhci_acpi_ids[] = {
|
|||
{ "80860F14" },
|
||||
{ "INT33BB" },
|
||||
{ "INT33C6" },
|
||||
{ "INT3436" },
|
||||
{ "PNP0D40" },
|
||||
{ },
|
||||
};
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <linux/of_gpio.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/platform_data/mmc-esdhc-imx.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include "sdhci-pltfm.h"
|
||||
#include "sdhci-esdhc.h"
|
||||
|
||||
|
@ -45,6 +46,8 @@
|
|||
#define ESDHC_MIX_CTRL_FBCLK_SEL (1 << 25)
|
||||
/* Bits 3 and 6 are not SDHCI standard definitions */
|
||||
#define ESDHC_MIX_CTRL_SDHCI_MASK 0xb7
|
||||
/* Tuning bits */
|
||||
#define ESDHC_MIX_CTRL_TUNING_MASK 0x03c00000
|
||||
|
||||
/* dll control register */
|
||||
#define ESDHC_DLL_CTRL 0x60
|
||||
|
@ -385,6 +388,22 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (unlikely(reg == SDHCI_TRANSFER_MODE)) {
|
||||
if (esdhc_is_usdhc(imx_data)) {
|
||||
u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
||||
ret = m & ESDHC_MIX_CTRL_SDHCI_MASK;
|
||||
/* Swap AC23 bit */
|
||||
if (m & ESDHC_MIX_CTRL_AC23EN) {
|
||||
ret &= ~ESDHC_MIX_CTRL_AC23EN;
|
||||
ret |= SDHCI_TRNS_AUTO_CMD23;
|
||||
}
|
||||
} else {
|
||||
ret = readw(host->ioaddr + SDHCI_TRANSFER_MODE);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
return readw(host->ioaddr + reg);
|
||||
}
|
||||
|
||||
|
@ -421,24 +440,20 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
|
|||
} else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
|
||||
u32 v = readl(host->ioaddr + SDHCI_ACMD12_ERR);
|
||||
u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
||||
new_val = readl(host->ioaddr + ESDHC_TUNING_CTRL);
|
||||
if (val & SDHCI_CTRL_EXEC_TUNING) {
|
||||
new_val |= ESDHC_STD_TUNING_EN |
|
||||
ESDHC_TUNING_START_TAP;
|
||||
v |= ESDHC_MIX_CTRL_EXE_TUNE;
|
||||
m |= ESDHC_MIX_CTRL_FBCLK_SEL;
|
||||
if (val & SDHCI_CTRL_TUNED_CLK) {
|
||||
v |= ESDHC_MIX_CTRL_SMPCLK_SEL;
|
||||
} else {
|
||||
new_val &= ~ESDHC_STD_TUNING_EN;
|
||||
v &= ~ESDHC_MIX_CTRL_EXE_TUNE;
|
||||
v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
|
||||
m &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
|
||||
}
|
||||
|
||||
if (val & SDHCI_CTRL_TUNED_CLK)
|
||||
v |= ESDHC_MIX_CTRL_SMPCLK_SEL;
|
||||
else
|
||||
v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
|
||||
if (val & SDHCI_CTRL_EXEC_TUNING) {
|
||||
v |= ESDHC_MIX_CTRL_EXE_TUNE;
|
||||
m |= ESDHC_MIX_CTRL_FBCLK_SEL;
|
||||
} else {
|
||||
v &= ~ESDHC_MIX_CTRL_EXE_TUNE;
|
||||
}
|
||||
|
||||
writel(new_val, host->ioaddr + ESDHC_TUNING_CTRL);
|
||||
writel(v, host->ioaddr + SDHCI_ACMD12_ERR);
|
||||
writel(m, host->ioaddr + ESDHC_MIX_CTRL);
|
||||
}
|
||||
|
@ -546,7 +561,10 @@ static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
|
|||
* Do it manually here.
|
||||
*/
|
||||
if (esdhc_is_usdhc(imx_data)) {
|
||||
writel(0, host->ioaddr + ESDHC_MIX_CTRL);
|
||||
/* the tuning bits should be kept during reset */
|
||||
new_val = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
||||
writel(new_val & ESDHC_MIX_CTRL_TUNING_MASK,
|
||||
host->ioaddr + ESDHC_MIX_CTRL);
|
||||
imx_data->is_ddr = 0;
|
||||
}
|
||||
}
|
||||
|
@ -558,19 +576,17 @@ static unsigned int esdhc_pltfm_get_max_clock(struct sdhci_host *host)
|
|||
struct pltfm_imx_data *imx_data = pltfm_host->priv;
|
||||
struct esdhc_platform_data *boarddata = &imx_data->boarddata;
|
||||
|
||||
u32 f_host = clk_get_rate(pltfm_host->clk);
|
||||
|
||||
if (boarddata->f_max && (boarddata->f_max < f_host))
|
||||
if (boarddata->f_max && (boarddata->f_max < pltfm_host->clock))
|
||||
return boarddata->f_max;
|
||||
else
|
||||
return f_host;
|
||||
return pltfm_host->clock;
|
||||
}
|
||||
|
||||
static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
|
||||
return clk_get_rate(pltfm_host->clk) / 256 / 16;
|
||||
return pltfm_host->clock / 256 / 16;
|
||||
}
|
||||
|
||||
static inline void esdhc_pltfm_set_clock(struct sdhci_host *host,
|
||||
|
@ -578,7 +594,7 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host,
|
|||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct pltfm_imx_data *imx_data = pltfm_host->priv;
|
||||
unsigned int host_clock = clk_get_rate(pltfm_host->clk);
|
||||
unsigned int host_clock = pltfm_host->clock;
|
||||
int pre_div = 2;
|
||||
int div = 1;
|
||||
u32 temp, val;
|
||||
|
@ -681,6 +697,7 @@ static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
|
|||
/* FIXME: delay a bit for card to be ready for next tuning due to errors */
|
||||
mdelay(1);
|
||||
|
||||
pm_runtime_get_sync(host->mmc->parent);
|
||||
reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
||||
reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL |
|
||||
ESDHC_MIX_CTRL_FBCLK_SEL;
|
||||
|
@ -699,7 +716,7 @@ static void esdhc_request_done(struct mmc_request *mrq)
|
|||
static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode)
|
||||
{
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_request mrq = {0};
|
||||
struct mmc_request mrq = {NULL};
|
||||
struct mmc_data data = {0};
|
||||
struct scatterlist sg;
|
||||
char tuning_pattern[ESDHC_TUNING_BLOCK_PATTERN_LEN];
|
||||
|
@ -809,6 +826,7 @@ static int esdhc_change_pinstate(struct sdhci_host *host,
|
|||
pinctrl = imx_data->pins_100mhz;
|
||||
break;
|
||||
case MMC_TIMING_UHS_SDR104:
|
||||
case MMC_TIMING_MMC_HS200:
|
||||
pinctrl = imx_data->pins_200mhz;
|
||||
break;
|
||||
default:
|
||||
|
@ -836,6 +854,7 @@ static int esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
|
|||
imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR50;
|
||||
break;
|
||||
case MMC_TIMING_UHS_SDR104:
|
||||
case MMC_TIMING_MMC_HS200:
|
||||
imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR104;
|
||||
break;
|
||||
case MMC_TIMING_UHS_DDR50:
|
||||
|
@ -976,7 +995,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
pltfm_host->clk = imx_data->clk_per;
|
||||
|
||||
pltfm_host->clock = clk_get_rate(pltfm_host->clk);
|
||||
clk_prepare_enable(imx_data->clk_per);
|
||||
clk_prepare_enable(imx_data->clk_ipg);
|
||||
clk_prepare_enable(imx_data->clk_ahb);
|
||||
|
@ -1009,11 +1028,18 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
|||
if (esdhc_is_usdhc(imx_data)) {
|
||||
writel(0x08100810, host->ioaddr + ESDHC_WTMK_LVL);
|
||||
host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
|
||||
host->mmc->caps |= MMC_CAP_1_8V_DDR;
|
||||
}
|
||||
|
||||
if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
|
||||
sdhci_esdhc_ops.platform_execute_tuning =
|
||||
esdhc_executing_tuning;
|
||||
|
||||
if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING)
|
||||
writel(readl(host->ioaddr + ESDHC_TUNING_CTRL) |
|
||||
ESDHC_STD_TUNING_EN | ESDHC_TUNING_START_TAP,
|
||||
host->ioaddr + ESDHC_TUNING_CTRL);
|
||||
|
||||
boarddata = &imx_data->boarddata;
|
||||
if (sdhci_esdhc_imx_probe_dt(pdev, boarddata) < 0) {
|
||||
if (!host->mmc->parent->platform_data) {
|
||||
|
@ -1053,7 +1079,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
|||
break;
|
||||
|
||||
case ESDHC_CD_PERMANENT:
|
||||
host->mmc->caps = MMC_CAP_NONREMOVABLE;
|
||||
host->mmc->caps |= MMC_CAP_NONREMOVABLE;
|
||||
break;
|
||||
|
||||
case ESDHC_CD_NONE:
|
||||
|
@ -1094,6 +1120,12 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
|||
if (err)
|
||||
goto disable_clk;
|
||||
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_suspend_ignore_children(&pdev->dev, 1);
|
||||
|
||||
return 0;
|
||||
|
||||
disable_clk:
|
||||
|
@ -1114,21 +1146,63 @@ static int sdhci_esdhc_imx_remove(struct platform_device *pdev)
|
|||
|
||||
sdhci_remove_host(host, dead);
|
||||
|
||||
clk_disable_unprepare(imx_data->clk_per);
|
||||
clk_disable_unprepare(imx_data->clk_ipg);
|
||||
clk_disable_unprepare(imx_data->clk_ahb);
|
||||
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
if (!IS_ENABLED(CONFIG_PM_RUNTIME)) {
|
||||
clk_disable_unprepare(imx_data->clk_per);
|
||||
clk_disable_unprepare(imx_data->clk_ipg);
|
||||
clk_disable_unprepare(imx_data->clk_ahb);
|
||||
}
|
||||
|
||||
sdhci_pltfm_free(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
static int sdhci_esdhc_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct pltfm_imx_data *imx_data = pltfm_host->priv;
|
||||
int ret;
|
||||
|
||||
ret = sdhci_runtime_suspend_host(host);
|
||||
|
||||
clk_disable_unprepare(imx_data->clk_per);
|
||||
clk_disable_unprepare(imx_data->clk_ipg);
|
||||
clk_disable_unprepare(imx_data->clk_ahb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sdhci_esdhc_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct pltfm_imx_data *imx_data = pltfm_host->priv;
|
||||
|
||||
clk_prepare_enable(imx_data->clk_per);
|
||||
clk_prepare_enable(imx_data->clk_ipg);
|
||||
clk_prepare_enable(imx_data->clk_ahb);
|
||||
|
||||
return sdhci_runtime_resume_host(host);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops sdhci_esdhc_pmops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(sdhci_pltfm_suspend, sdhci_pltfm_resume)
|
||||
SET_RUNTIME_PM_OPS(sdhci_esdhc_runtime_suspend,
|
||||
sdhci_esdhc_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver sdhci_esdhc_imx_driver = {
|
||||
.driver = {
|
||||
.name = "sdhci-esdhc-imx",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = imx_esdhc_dt_ids,
|
||||
.pm = SDHCI_PLTFM_PMOPS,
|
||||
.pm = &sdhci_esdhc_pmops,
|
||||
},
|
||||
.id_table = imx_esdhc_devtype,
|
||||
.probe = sdhci_esdhc_imx_probe,
|
||||
|
|
|
@ -0,0 +1,224 @@
|
|||
/*
|
||||
* Arasan Secure Digital Host Controller Interface.
|
||||
* Copyright (C) 2011 - 2012 Michal Simek <monstr@monstr.eu>
|
||||
* Copyright (c) 2012 Wind River Systems, Inc.
|
||||
* Copyright (C) 2013 Pengutronix e.K.
|
||||
* Copyright (C) 2013 Xilinx Inc.
|
||||
*
|
||||
* Based on sdhci-of-esdhc.c
|
||||
*
|
||||
* Copyright (c) 2007 Freescale Semiconductor, Inc.
|
||||
* Copyright (c) 2009 MontaVista Software, Inc.
|
||||
*
|
||||
* Authors: Xiaobo Xie <X.Xie@freescale.com>
|
||||
* Anton Vorontsov <avorontsov@ru.mvista.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include "sdhci-pltfm.h"
|
||||
|
||||
#define SDHCI_ARASAN_CLK_CTRL_OFFSET 0x2c
|
||||
|
||||
#define CLK_CTRL_TIMEOUT_SHIFT 16
|
||||
#define CLK_CTRL_TIMEOUT_MASK (0xf << CLK_CTRL_TIMEOUT_SHIFT)
|
||||
#define CLK_CTRL_TIMEOUT_MIN_EXP 13
|
||||
|
||||
/**
|
||||
* struct sdhci_arasan_data
|
||||
* @clk_ahb: Pointer to the AHB clock
|
||||
*/
|
||||
struct sdhci_arasan_data {
|
||||
struct clk *clk_ahb;
|
||||
};
|
||||
|
||||
static unsigned int sdhci_arasan_get_timeout_clock(struct sdhci_host *host)
|
||||
{
|
||||
u32 div;
|
||||
unsigned long freq;
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
|
||||
div = readl(host->ioaddr + SDHCI_ARASAN_CLK_CTRL_OFFSET);
|
||||
div = (div & CLK_CTRL_TIMEOUT_MASK) >> CLK_CTRL_TIMEOUT_SHIFT;
|
||||
|
||||
freq = clk_get_rate(pltfm_host->clk);
|
||||
freq /= 1 << (CLK_CTRL_TIMEOUT_MIN_EXP + div);
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
static struct sdhci_ops sdhci_arasan_ops = {
|
||||
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
.get_timeout_clock = sdhci_arasan_get_timeout_clock,
|
||||
};
|
||||
|
||||
static struct sdhci_pltfm_data sdhci_arasan_pdata = {
|
||||
.ops = &sdhci_arasan_ops,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
/**
|
||||
* sdhci_arasan_suspend - Suspend method for the driver
|
||||
* @dev: Address of the device structure
|
||||
* Returns 0 on success and error value on error
|
||||
*
|
||||
* Put the device in a low power state.
|
||||
*/
|
||||
static int sdhci_arasan_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv;
|
||||
int ret;
|
||||
|
||||
ret = sdhci_suspend_host(host);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clk_disable(pltfm_host->clk);
|
||||
clk_disable(sdhci_arasan->clk_ahb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* sdhci_arasan_resume - Resume method for the driver
|
||||
* @dev: Address of the device structure
|
||||
* Returns 0 on success and error value on error
|
||||
*
|
||||
* Resume operation after suspend
|
||||
*/
|
||||
static int sdhci_arasan_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv;
|
||||
int ret;
|
||||
|
||||
ret = clk_enable(sdhci_arasan->clk_ahb);
|
||||
if (ret) {
|
||||
dev_err(dev, "Cannot enable AHB clock.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_enable(pltfm_host->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "Cannot enable SD clock.\n");
|
||||
clk_disable(sdhci_arasan->clk_ahb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return sdhci_resume_host(host);
|
||||
}
|
||||
#endif /* ! CONFIG_PM_SLEEP */
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend,
|
||||
sdhci_arasan_resume);
|
||||
|
||||
static int sdhci_arasan_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct clk *clk_xin;
|
||||
struct sdhci_host *host;
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct sdhci_arasan_data *sdhci_arasan;
|
||||
|
||||
sdhci_arasan = devm_kzalloc(&pdev->dev, sizeof(*sdhci_arasan),
|
||||
GFP_KERNEL);
|
||||
if (!sdhci_arasan)
|
||||
return -ENOMEM;
|
||||
|
||||
sdhci_arasan->clk_ahb = devm_clk_get(&pdev->dev, "clk_ahb");
|
||||
if (IS_ERR(sdhci_arasan->clk_ahb)) {
|
||||
dev_err(&pdev->dev, "clk_ahb clock not found.\n");
|
||||
return PTR_ERR(sdhci_arasan->clk_ahb);
|
||||
}
|
||||
|
||||
clk_xin = devm_clk_get(&pdev->dev, "clk_xin");
|
||||
if (IS_ERR(clk_xin)) {
|
||||
dev_err(&pdev->dev, "clk_xin clock not found.\n");
|
||||
return PTR_ERR(clk_xin);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(sdhci_arasan->clk_ahb);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Unable to enable AHB clock.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(clk_xin);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Unable to enable SD clock.\n");
|
||||
goto clk_dis_ahb;
|
||||
}
|
||||
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_arasan_pdata, 0);
|
||||
if (IS_ERR(host)) {
|
||||
ret = PTR_ERR(host);
|
||||
dev_err(&pdev->dev, "platform init failed (%u)\n", ret);
|
||||
goto clk_disable_all;
|
||||
}
|
||||
|
||||
sdhci_get_of_property(pdev);
|
||||
pltfm_host = sdhci_priv(host);
|
||||
pltfm_host->priv = sdhci_arasan;
|
||||
pltfm_host->clk = clk_xin;
|
||||
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "platform register failed (%u)\n", ret);
|
||||
goto err_pltfm_free;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_pltfm_free:
|
||||
sdhci_pltfm_free(pdev);
|
||||
clk_disable_all:
|
||||
clk_disable_unprepare(clk_xin);
|
||||
clk_dis_ahb:
|
||||
clk_disable_unprepare(sdhci_arasan->clk_ahb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sdhci_arasan_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv;
|
||||
|
||||
clk_disable_unprepare(pltfm_host->clk);
|
||||
clk_disable_unprepare(sdhci_arasan->clk_ahb);
|
||||
|
||||
return sdhci_pltfm_unregister(pdev);
|
||||
}
|
||||
|
||||
static const struct of_device_id sdhci_arasan_of_match[] = {
|
||||
{ .compatible = "arasan,sdhci-8.9a" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
|
||||
|
||||
static struct platform_driver sdhci_arasan_driver = {
|
||||
.driver = {
|
||||
.name = "sdhci-arasan",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = sdhci_arasan_of_match,
|
||||
.pm = &sdhci_arasan_dev_pm_ops,
|
||||
},
|
||||
.probe = sdhci_arasan_probe,
|
||||
.remove = sdhci_arasan_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(sdhci_arasan_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Driver for the Arasan SDHCI Controller");
|
||||
MODULE_AUTHOR("Soeren Brinkmann <soren.brinkmann@xilinx.com>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,321 @@
|
|||
/*
|
||||
* Copyright (C) 2013 BayHub Technology Ltd.
|
||||
*
|
||||
* Authors: Peter Guo <peter.guo@bayhubtech.com>
|
||||
* Adam Lee <adam.lee@canonical.com>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "sdhci.h"
|
||||
#include "sdhci-pci.h"
|
||||
#include "sdhci-pci-o2micro.h"
|
||||
|
||||
void sdhci_pci_o2_fujin2_pci_init(struct sdhci_pci_chip *chip)
|
||||
{
|
||||
u32 scratch_32;
|
||||
int ret;
|
||||
/* Improve write performance for SD3.0 */
|
||||
ret = pci_read_config_dword(chip->pdev, O2_SD_DEV_CTRL, &scratch_32);
|
||||
if (ret)
|
||||
return;
|
||||
scratch_32 &= ~((1 << 12) | (1 << 13) | (1 << 14));
|
||||
pci_write_config_dword(chip->pdev, O2_SD_DEV_CTRL, scratch_32);
|
||||
|
||||
/* Enable Link abnormal reset generating Reset */
|
||||
ret = pci_read_config_dword(chip->pdev, O2_SD_MISC_REG5, &scratch_32);
|
||||
if (ret)
|
||||
return;
|
||||
scratch_32 &= ~((1 << 19) | (1 << 11));
|
||||
scratch_32 |= (1 << 10);
|
||||
pci_write_config_dword(chip->pdev, O2_SD_MISC_REG5, scratch_32);
|
||||
|
||||
/* set card power over current protection */
|
||||
ret = pci_read_config_dword(chip->pdev, O2_SD_TEST_REG, &scratch_32);
|
||||
if (ret)
|
||||
return;
|
||||
scratch_32 |= (1 << 4);
|
||||
pci_write_config_dword(chip->pdev, O2_SD_TEST_REG, scratch_32);
|
||||
|
||||
/* adjust the output delay for SD mode */
|
||||
pci_write_config_dword(chip->pdev, O2_SD_DELAY_CTRL, 0x00002492);
|
||||
|
||||
/* Set the output voltage setting of Aux 1.2v LDO */
|
||||
ret = pci_read_config_dword(chip->pdev, O2_SD_LD0_CTRL, &scratch_32);
|
||||
if (ret)
|
||||
return;
|
||||
scratch_32 &= ~(3 << 12);
|
||||
pci_write_config_dword(chip->pdev, O2_SD_LD0_CTRL, scratch_32);
|
||||
|
||||
/* Set Max power supply capability of SD host */
|
||||
ret = pci_read_config_dword(chip->pdev, O2_SD_CAP_REG0, &scratch_32);
|
||||
if (ret)
|
||||
return;
|
||||
scratch_32 &= ~(0x01FE);
|
||||
scratch_32 |= 0x00CC;
|
||||
pci_write_config_dword(chip->pdev, O2_SD_CAP_REG0, scratch_32);
|
||||
/* Set DLL Tuning Window */
|
||||
ret = pci_read_config_dword(chip->pdev,
|
||||
O2_SD_TUNING_CTRL, &scratch_32);
|
||||
if (ret)
|
||||
return;
|
||||
scratch_32 &= ~(0x000000FF);
|
||||
scratch_32 |= 0x00000066;
|
||||
pci_write_config_dword(chip->pdev, O2_SD_TUNING_CTRL, scratch_32);
|
||||
|
||||
/* Set UHS2 T_EIDLE */
|
||||
ret = pci_read_config_dword(chip->pdev,
|
||||
O2_SD_UHS2_L1_CTRL, &scratch_32);
|
||||
if (ret)
|
||||
return;
|
||||
scratch_32 &= ~(0x000000FC);
|
||||
scratch_32 |= 0x00000084;
|
||||
pci_write_config_dword(chip->pdev, O2_SD_UHS2_L1_CTRL, scratch_32);
|
||||
|
||||
/* Set UHS2 Termination */
|
||||
ret = pci_read_config_dword(chip->pdev, O2_SD_FUNC_REG3, &scratch_32);
|
||||
if (ret)
|
||||
return;
|
||||
scratch_32 &= ~((1 << 21) | (1 << 30));
|
||||
|
||||
/* Set RTD3 function disabled */
|
||||
scratch_32 |= ((1 << 29) | (1 << 28));
|
||||
pci_write_config_dword(chip->pdev, O2_SD_FUNC_REG3, scratch_32);
|
||||
|
||||
/* Set L1 Entrance Timer */
|
||||
ret = pci_read_config_dword(chip->pdev, O2_SD_CAPS, &scratch_32);
|
||||
if (ret)
|
||||
return;
|
||||
scratch_32 &= ~(0xf0000000);
|
||||
scratch_32 |= 0x30000000;
|
||||
pci_write_config_dword(chip->pdev, O2_SD_CAPS, scratch_32);
|
||||
|
||||
ret = pci_read_config_dword(chip->pdev,
|
||||
O2_SD_MISC_CTRL4, &scratch_32);
|
||||
if (ret)
|
||||
return;
|
||||
scratch_32 &= ~(0x000f0000);
|
||||
scratch_32 |= 0x00080000;
|
||||
pci_write_config_dword(chip->pdev, O2_SD_MISC_CTRL4, scratch_32);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sdhci_pci_o2_fujin2_pci_init);
|
||||
|
||||
int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot)
|
||||
{
|
||||
struct sdhci_pci_chip *chip;
|
||||
struct sdhci_host *host;
|
||||
u32 reg;
|
||||
|
||||
chip = slot->chip;
|
||||
host = slot->host;
|
||||
switch (chip->pdev->device) {
|
||||
case PCI_DEVICE_ID_O2_SDS0:
|
||||
case PCI_DEVICE_ID_O2_SEABIRD0:
|
||||
case PCI_DEVICE_ID_O2_SEABIRD1:
|
||||
case PCI_DEVICE_ID_O2_SDS1:
|
||||
case PCI_DEVICE_ID_O2_FUJIN2:
|
||||
reg = sdhci_readl(host, O2_SD_VENDOR_SETTING);
|
||||
if (reg & 0x1)
|
||||
host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
|
||||
|
||||
if (chip->pdev->device != PCI_DEVICE_ID_O2_FUJIN2)
|
||||
break;
|
||||
/* set dll watch dog timer */
|
||||
reg = sdhci_readl(host, O2_SD_VENDOR_SETTING2);
|
||||
reg |= (1 << 12);
|
||||
sdhci_writel(host, reg, O2_SD_VENDOR_SETTING2);
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sdhci_pci_o2_probe_slot);
|
||||
|
||||
int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip)
|
||||
{
|
||||
int ret;
|
||||
u8 scratch;
|
||||
u32 scratch_32;
|
||||
|
||||
switch (chip->pdev->device) {
|
||||
case PCI_DEVICE_ID_O2_8220:
|
||||
case PCI_DEVICE_ID_O2_8221:
|
||||
case PCI_DEVICE_ID_O2_8320:
|
||||
case PCI_DEVICE_ID_O2_8321:
|
||||
/* This extra setup is required due to broken ADMA. */
|
||||
ret = pci_read_config_byte(chip->pdev,
|
||||
O2_SD_LOCK_WP, &scratch);
|
||||
if (ret)
|
||||
return ret;
|
||||
scratch &= 0x7f;
|
||||
pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch);
|
||||
|
||||
/* Set Multi 3 to VCC3V# */
|
||||
pci_write_config_byte(chip->pdev, O2_SD_MULTI_VCC3V, 0x08);
|
||||
|
||||
/* Disable CLK_REQ# support after media DET */
|
||||
ret = pci_read_config_byte(chip->pdev,
|
||||
O2_SD_CLKREQ, &scratch);
|
||||
if (ret)
|
||||
return ret;
|
||||
scratch |= 0x20;
|
||||
pci_write_config_byte(chip->pdev, O2_SD_CLKREQ, scratch);
|
||||
|
||||
/* Choose capabilities, enable SDMA. We have to write 0x01
|
||||
* to the capabilities register first to unlock it.
|
||||
*/
|
||||
ret = pci_read_config_byte(chip->pdev, O2_SD_CAPS, &scratch);
|
||||
if (ret)
|
||||
return ret;
|
||||
scratch |= 0x01;
|
||||
pci_write_config_byte(chip->pdev, O2_SD_CAPS, scratch);
|
||||
pci_write_config_byte(chip->pdev, O2_SD_CAPS, 0x73);
|
||||
|
||||
/* Disable ADMA1/2 */
|
||||
pci_write_config_byte(chip->pdev, O2_SD_ADMA1, 0x39);
|
||||
pci_write_config_byte(chip->pdev, O2_SD_ADMA2, 0x08);
|
||||
|
||||
/* Disable the infinite transfer mode */
|
||||
ret = pci_read_config_byte(chip->pdev,
|
||||
O2_SD_INF_MOD, &scratch);
|
||||
if (ret)
|
||||
return ret;
|
||||
scratch |= 0x08;
|
||||
pci_write_config_byte(chip->pdev, O2_SD_INF_MOD, scratch);
|
||||
|
||||
/* Lock WP */
|
||||
ret = pci_read_config_byte(chip->pdev,
|
||||
O2_SD_LOCK_WP, &scratch);
|
||||
if (ret)
|
||||
return ret;
|
||||
scratch |= 0x80;
|
||||
pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch);
|
||||
break;
|
||||
case PCI_DEVICE_ID_O2_SDS0:
|
||||
case PCI_DEVICE_ID_O2_SDS1:
|
||||
case PCI_DEVICE_ID_O2_FUJIN2:
|
||||
/* UnLock WP */
|
||||
ret = pci_read_config_byte(chip->pdev,
|
||||
O2_SD_LOCK_WP, &scratch);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
scratch &= 0x7f;
|
||||
pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch);
|
||||
|
||||
/* Set timeout CLK */
|
||||
ret = pci_read_config_dword(chip->pdev,
|
||||
O2_SD_CLK_SETTING, &scratch_32);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
scratch_32 &= ~(0xFF00);
|
||||
scratch_32 |= 0x07E0C800;
|
||||
pci_write_config_dword(chip->pdev,
|
||||
O2_SD_CLK_SETTING, scratch_32);
|
||||
|
||||
ret = pci_read_config_dword(chip->pdev,
|
||||
O2_SD_CLKREQ, &scratch_32);
|
||||
if (ret)
|
||||
return ret;
|
||||
scratch_32 |= 0x3;
|
||||
pci_write_config_dword(chip->pdev, O2_SD_CLKREQ, scratch_32);
|
||||
|
||||
ret = pci_read_config_dword(chip->pdev,
|
||||
O2_SD_PLL_SETTING, &scratch_32);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
scratch_32 &= ~(0x1F3F070E);
|
||||
scratch_32 |= 0x18270106;
|
||||
pci_write_config_dword(chip->pdev,
|
||||
O2_SD_PLL_SETTING, scratch_32);
|
||||
|
||||
/* Disable UHS1 funciton */
|
||||
ret = pci_read_config_dword(chip->pdev,
|
||||
O2_SD_CAP_REG2, &scratch_32);
|
||||
if (ret)
|
||||
return ret;
|
||||
scratch_32 &= ~(0xE0);
|
||||
pci_write_config_dword(chip->pdev,
|
||||
O2_SD_CAP_REG2, scratch_32);
|
||||
|
||||
if (chip->pdev->device == PCI_DEVICE_ID_O2_FUJIN2)
|
||||
sdhci_pci_o2_fujin2_pci_init(chip);
|
||||
|
||||
/* Lock WP */
|
||||
ret = pci_read_config_byte(chip->pdev,
|
||||
O2_SD_LOCK_WP, &scratch);
|
||||
if (ret)
|
||||
return ret;
|
||||
scratch |= 0x80;
|
||||
pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch);
|
||||
break;
|
||||
case PCI_DEVICE_ID_O2_SEABIRD0:
|
||||
case PCI_DEVICE_ID_O2_SEABIRD1:
|
||||
/* UnLock WP */
|
||||
ret = pci_read_config_byte(chip->pdev,
|
||||
O2_SD_LOCK_WP, &scratch);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
scratch &= 0x7f;
|
||||
pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch);
|
||||
|
||||
ret = pci_read_config_dword(chip->pdev,
|
||||
O2_SD_FUNC_REG0, &scratch_32);
|
||||
|
||||
if ((scratch_32 & 0xff000000) == 0x01000000) {
|
||||
scratch_32 &= 0x0000FFFF;
|
||||
scratch_32 |= 0x1F340000;
|
||||
|
||||
pci_write_config_dword(chip->pdev,
|
||||
O2_SD_PLL_SETTING, scratch_32);
|
||||
} else {
|
||||
scratch_32 &= 0x0000FFFF;
|
||||
scratch_32 |= 0x2c280000;
|
||||
|
||||
pci_write_config_dword(chip->pdev,
|
||||
O2_SD_PLL_SETTING, scratch_32);
|
||||
|
||||
ret = pci_read_config_dword(chip->pdev,
|
||||
O2_SD_FUNC_REG4,
|
||||
&scratch_32);
|
||||
scratch_32 |= (1 << 22);
|
||||
pci_write_config_dword(chip->pdev,
|
||||
O2_SD_FUNC_REG4, scratch_32);
|
||||
}
|
||||
|
||||
/* Lock WP */
|
||||
ret = pci_read_config_byte(chip->pdev,
|
||||
O2_SD_LOCK_WP, &scratch);
|
||||
if (ret)
|
||||
return ret;
|
||||
scratch |= 0x80;
|
||||
pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sdhci_pci_o2_probe);
|
||||
|
||||
int sdhci_pci_o2_resume(struct sdhci_pci_chip *chip)
|
||||
{
|
||||
sdhci_pci_o2_probe(chip);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sdhci_pci_o2_resume);
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright (C) 2013 BayHub Technology Ltd.
|
||||
*
|
||||
* Authors: Peter Guo <peter.guo@bayhubtech.com>
|
||||
* Adam Lee <adam.lee@canonical.com>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __SDHCI_PCI_O2MICRO_H
|
||||
#define __SDHCI_PCI_O2MICRO_H
|
||||
|
||||
#include "sdhci-pci.h"
|
||||
|
||||
/*
|
||||
* O2Micro device IDs
|
||||
*/
|
||||
|
||||
#define PCI_DEVICE_ID_O2_SDS0 0x8420
|
||||
#define PCI_DEVICE_ID_O2_SDS1 0x8421
|
||||
#define PCI_DEVICE_ID_O2_FUJIN2 0x8520
|
||||
#define PCI_DEVICE_ID_O2_SEABIRD0 0x8620
|
||||
#define PCI_DEVICE_ID_O2_SEABIRD1 0x8621
|
||||
|
||||
/*
|
||||
* O2Micro device registers
|
||||
*/
|
||||
|
||||
#define O2_SD_MISC_REG5 0x64
|
||||
#define O2_SD_LD0_CTRL 0x68
|
||||
#define O2_SD_DEV_CTRL 0x88
|
||||
#define O2_SD_LOCK_WP 0xD3
|
||||
#define O2_SD_TEST_REG 0xD4
|
||||
#define O2_SD_FUNC_REG0 0xDC
|
||||
#define O2_SD_MULTI_VCC3V 0xEE
|
||||
#define O2_SD_CLKREQ 0xEC
|
||||
#define O2_SD_CAPS 0xE0
|
||||
#define O2_SD_ADMA1 0xE2
|
||||
#define O2_SD_ADMA2 0xE7
|
||||
#define O2_SD_INF_MOD 0xF1
|
||||
#define O2_SD_MISC_CTRL4 0xFC
|
||||
#define O2_SD_TUNING_CTRL 0x300
|
||||
#define O2_SD_PLL_SETTING 0x304
|
||||
#define O2_SD_CLK_SETTING 0x328
|
||||
#define O2_SD_CAP_REG2 0x330
|
||||
#define O2_SD_CAP_REG0 0x334
|
||||
#define O2_SD_UHS1_CAP_SETTING 0x33C
|
||||
#define O2_SD_DELAY_CTRL 0x350
|
||||
#define O2_SD_UHS2_L1_CTRL 0x35C
|
||||
#define O2_SD_FUNC_REG3 0x3E0
|
||||
#define O2_SD_FUNC_REG4 0x3E4
|
||||
|
||||
#define O2_SD_VENDOR_SETTING 0x110
|
||||
#define O2_SD_VENDOR_SETTING2 0x1C8
|
||||
|
||||
extern void sdhci_pci_o2_fujin2_pci_init(struct sdhci_pci_chip *chip);
|
||||
|
||||
extern int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot);
|
||||
|
||||
extern int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip);
|
||||
|
||||
extern int sdhci_pci_o2_resume(struct sdhci_pci_chip *chip);
|
||||
|
||||
#endif /* __SDHCI_PCI_O2MICRO_H */
|
|
@ -27,79 +27,8 @@
|
|||
#include <linux/mmc/sdhci-pci-data.h>
|
||||
|
||||
#include "sdhci.h"
|
||||
|
||||
/*
|
||||
* PCI device IDs
|
||||
*/
|
||||
#define PCI_DEVICE_ID_INTEL_PCH_SDIO0 0x8809
|
||||
#define PCI_DEVICE_ID_INTEL_PCH_SDIO1 0x880a
|
||||
#define PCI_DEVICE_ID_INTEL_BYT_EMMC 0x0f14
|
||||
#define PCI_DEVICE_ID_INTEL_BYT_SDIO 0x0f15
|
||||
#define PCI_DEVICE_ID_INTEL_BYT_SD 0x0f16
|
||||
#define PCI_DEVICE_ID_INTEL_BYT_EMMC2 0x0f50
|
||||
#define PCI_DEVICE_ID_INTEL_MRFL_MMC 0x1190
|
||||
#define PCI_DEVICE_ID_INTEL_CLV_SDIO0 0x08f9
|
||||
#define PCI_DEVICE_ID_INTEL_CLV_SDIO1 0x08fa
|
||||
#define PCI_DEVICE_ID_INTEL_CLV_SDIO2 0x08fb
|
||||
#define PCI_DEVICE_ID_INTEL_CLV_EMMC0 0x08e5
|
||||
#define PCI_DEVICE_ID_INTEL_CLV_EMMC1 0x08e6
|
||||
|
||||
/*
|
||||
* PCI registers
|
||||
*/
|
||||
|
||||
#define PCI_SDHCI_IFPIO 0x00
|
||||
#define PCI_SDHCI_IFDMA 0x01
|
||||
#define PCI_SDHCI_IFVENDOR 0x02
|
||||
|
||||
#define PCI_SLOT_INFO 0x40 /* 8 bits */
|
||||
#define PCI_SLOT_INFO_SLOTS(x) ((x >> 4) & 7)
|
||||
#define PCI_SLOT_INFO_FIRST_BAR_MASK 0x07
|
||||
|
||||
#define MAX_SLOTS 8
|
||||
|
||||
struct sdhci_pci_chip;
|
||||
struct sdhci_pci_slot;
|
||||
|
||||
struct sdhci_pci_fixes {
|
||||
unsigned int quirks;
|
||||
unsigned int quirks2;
|
||||
bool allow_runtime_pm;
|
||||
|
||||
int (*probe) (struct sdhci_pci_chip *);
|
||||
|
||||
int (*probe_slot) (struct sdhci_pci_slot *);
|
||||
void (*remove_slot) (struct sdhci_pci_slot *, int);
|
||||
|
||||
int (*suspend) (struct sdhci_pci_chip *);
|
||||
int (*resume) (struct sdhci_pci_chip *);
|
||||
};
|
||||
|
||||
struct sdhci_pci_slot {
|
||||
struct sdhci_pci_chip *chip;
|
||||
struct sdhci_host *host;
|
||||
struct sdhci_pci_data *data;
|
||||
|
||||
int pci_bar;
|
||||
int rst_n_gpio;
|
||||
int cd_gpio;
|
||||
int cd_irq;
|
||||
|
||||
void (*hw_reset)(struct sdhci_host *host);
|
||||
};
|
||||
|
||||
struct sdhci_pci_chip {
|
||||
struct pci_dev *pdev;
|
||||
|
||||
unsigned int quirks;
|
||||
unsigned int quirks2;
|
||||
bool allow_runtime_pm;
|
||||
const struct sdhci_pci_fixes *fixes;
|
||||
|
||||
int num_slots; /* Slots on controller */
|
||||
struct sdhci_pci_slot *slots[MAX_SLOTS]; /* Pointers to host slots */
|
||||
};
|
||||
|
||||
#include "sdhci-pci.h"
|
||||
#include "sdhci-pci-o2micro.h"
|
||||
|
||||
/*****************************************************************************\
|
||||
* *
|
||||
|
@ -296,6 +225,7 @@ static const struct sdhci_pci_fixes sdhci_intel_mrst_hc1_hc2 = {
|
|||
static const struct sdhci_pci_fixes sdhci_intel_mfd_sd = {
|
||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||
.allow_runtime_pm = true,
|
||||
.own_cd_for_runtime_pm = true,
|
||||
};
|
||||
|
||||
static const struct sdhci_pci_fixes sdhci_intel_mfd_sdio = {
|
||||
|
@ -360,6 +290,7 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = {
|
|||
static const struct sdhci_pci_fixes sdhci_intel_byt_sd = {
|
||||
.quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON,
|
||||
.allow_runtime_pm = true,
|
||||
.own_cd_for_runtime_pm = true,
|
||||
};
|
||||
|
||||
/* Define Host controllers for Intel Merrifield platform */
|
||||
|
@ -381,6 +312,7 @@ static int intel_mrfl_mmc_probe_slot(struct sdhci_pci_slot *slot)
|
|||
|
||||
static const struct sdhci_pci_fixes sdhci_intel_mrfl_mmc = {
|
||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||
.quirks2 = SDHCI_QUIRK2_BROKEN_HS200,
|
||||
.probe_slot = intel_mrfl_mmc_probe_slot,
|
||||
};
|
||||
|
||||
|
@ -393,65 +325,6 @@ static const struct sdhci_pci_fixes sdhci_intel_mrfl_mmc = {
|
|||
#define O2_SD_ADMA2 0xE7
|
||||
#define O2_SD_INF_MOD 0xF1
|
||||
|
||||
static int o2_probe(struct sdhci_pci_chip *chip)
|
||||
{
|
||||
int ret;
|
||||
u8 scratch;
|
||||
|
||||
switch (chip->pdev->device) {
|
||||
case PCI_DEVICE_ID_O2_8220:
|
||||
case PCI_DEVICE_ID_O2_8221:
|
||||
case PCI_DEVICE_ID_O2_8320:
|
||||
case PCI_DEVICE_ID_O2_8321:
|
||||
/* This extra setup is required due to broken ADMA. */
|
||||
ret = pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch);
|
||||
if (ret)
|
||||
return ret;
|
||||
scratch &= 0x7f;
|
||||
pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch);
|
||||
|
||||
/* Set Multi 3 to VCC3V# */
|
||||
pci_write_config_byte(chip->pdev, O2_SD_MULTI_VCC3V, 0x08);
|
||||
|
||||
/* Disable CLK_REQ# support after media DET */
|
||||
ret = pci_read_config_byte(chip->pdev, O2_SD_CLKREQ, &scratch);
|
||||
if (ret)
|
||||
return ret;
|
||||
scratch |= 0x20;
|
||||
pci_write_config_byte(chip->pdev, O2_SD_CLKREQ, scratch);
|
||||
|
||||
/* Choose capabilities, enable SDMA. We have to write 0x01
|
||||
* to the capabilities register first to unlock it.
|
||||
*/
|
||||
ret = pci_read_config_byte(chip->pdev, O2_SD_CAPS, &scratch);
|
||||
if (ret)
|
||||
return ret;
|
||||
scratch |= 0x01;
|
||||
pci_write_config_byte(chip->pdev, O2_SD_CAPS, scratch);
|
||||
pci_write_config_byte(chip->pdev, O2_SD_CAPS, 0x73);
|
||||
|
||||
/* Disable ADMA1/2 */
|
||||
pci_write_config_byte(chip->pdev, O2_SD_ADMA1, 0x39);
|
||||
pci_write_config_byte(chip->pdev, O2_SD_ADMA2, 0x08);
|
||||
|
||||
/* Disable the infinite transfer mode */
|
||||
ret = pci_read_config_byte(chip->pdev, O2_SD_INF_MOD, &scratch);
|
||||
if (ret)
|
||||
return ret;
|
||||
scratch |= 0x08;
|
||||
pci_write_config_byte(chip->pdev, O2_SD_INF_MOD, scratch);
|
||||
|
||||
/* Lock WP */
|
||||
ret = pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch);
|
||||
if (ret)
|
||||
return ret;
|
||||
scratch |= 0x80;
|
||||
pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jmicron_pmos(struct sdhci_pci_chip *chip, int on)
|
||||
{
|
||||
u8 scratch;
|
||||
|
@ -642,7 +515,10 @@ static int jmicron_resume(struct sdhci_pci_chip *chip)
|
|||
}
|
||||
|
||||
static const struct sdhci_pci_fixes sdhci_o2 = {
|
||||
.probe = o2_probe,
|
||||
.probe = sdhci_pci_o2_probe,
|
||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||
.probe_slot = sdhci_pci_o2_probe_slot,
|
||||
.resume = sdhci_pci_o2_resume,
|
||||
};
|
||||
|
||||
static const struct sdhci_pci_fixes sdhci_jmicron = {
|
||||
|
@ -1055,6 +931,46 @@ static const struct pci_device_id pci_ids[] = {
|
|||
.driver_data = (kernel_ulong_t)&sdhci_o2,
|
||||
},
|
||||
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_O2,
|
||||
.device = PCI_DEVICE_ID_O2_FUJIN2,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.driver_data = (kernel_ulong_t)&sdhci_o2,
|
||||
},
|
||||
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_O2,
|
||||
.device = PCI_DEVICE_ID_O2_SDS0,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.driver_data = (kernel_ulong_t)&sdhci_o2,
|
||||
},
|
||||
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_O2,
|
||||
.device = PCI_DEVICE_ID_O2_SDS1,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.driver_data = (kernel_ulong_t)&sdhci_o2,
|
||||
},
|
||||
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_O2,
|
||||
.device = PCI_DEVICE_ID_O2_SEABIRD0,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.driver_data = (kernel_ulong_t)&sdhci_o2,
|
||||
},
|
||||
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_O2,
|
||||
.device = PCI_DEVICE_ID_O2_SEABIRD1,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.driver_data = (kernel_ulong_t)&sdhci_o2,
|
||||
},
|
||||
|
||||
{ /* Generic SD host controller */
|
||||
PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00)
|
||||
},
|
||||
|
@ -1457,6 +1373,15 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
|
|||
|
||||
sdhci_pci_add_own_cd(slot);
|
||||
|
||||
/*
|
||||
* Check if the chip needs a separate GPIO for card detect to wake up
|
||||
* from runtime suspend. If it is not there, don't allow runtime PM.
|
||||
* Note sdhci_pci_add_own_cd() sets slot->cd_gpio to -EINVAL on failure.
|
||||
*/
|
||||
if (chip->fixes && chip->fixes->own_cd_for_runtime_pm &&
|
||||
!gpio_is_valid(slot->cd_gpio))
|
||||
chip->allow_runtime_pm = false;
|
||||
|
||||
return slot;
|
||||
|
||||
remove:
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
#ifndef __SDHCI_PCI_H
|
||||
#define __SDHCI_PCI_H
|
||||
|
||||
/*
|
||||
* PCI device IDs
|
||||
*/
|
||||
|
||||
#define PCI_DEVICE_ID_INTEL_PCH_SDIO0 0x8809
|
||||
#define PCI_DEVICE_ID_INTEL_PCH_SDIO1 0x880a
|
||||
#define PCI_DEVICE_ID_INTEL_BYT_EMMC 0x0f14
|
||||
#define PCI_DEVICE_ID_INTEL_BYT_SDIO 0x0f15
|
||||
#define PCI_DEVICE_ID_INTEL_BYT_SD 0x0f16
|
||||
#define PCI_DEVICE_ID_INTEL_BYT_EMMC2 0x0f50
|
||||
#define PCI_DEVICE_ID_INTEL_MRFL_MMC 0x1190
|
||||
#define PCI_DEVICE_ID_INTEL_CLV_SDIO0 0x08f9
|
||||
#define PCI_DEVICE_ID_INTEL_CLV_SDIO1 0x08fa
|
||||
#define PCI_DEVICE_ID_INTEL_CLV_SDIO2 0x08fb
|
||||
#define PCI_DEVICE_ID_INTEL_CLV_EMMC0 0x08e5
|
||||
#define PCI_DEVICE_ID_INTEL_CLV_EMMC1 0x08e6
|
||||
|
||||
/*
|
||||
* PCI registers
|
||||
*/
|
||||
|
||||
#define PCI_SDHCI_IFPIO 0x00
|
||||
#define PCI_SDHCI_IFDMA 0x01
|
||||
#define PCI_SDHCI_IFVENDOR 0x02
|
||||
|
||||
#define PCI_SLOT_INFO 0x40 /* 8 bits */
|
||||
#define PCI_SLOT_INFO_SLOTS(x) ((x >> 4) & 7)
|
||||
#define PCI_SLOT_INFO_FIRST_BAR_MASK 0x07
|
||||
|
||||
#define MAX_SLOTS 8
|
||||
|
||||
struct sdhci_pci_chip;
|
||||
struct sdhci_pci_slot;
|
||||
|
||||
struct sdhci_pci_fixes {
|
||||
unsigned int quirks;
|
||||
unsigned int quirks2;
|
||||
bool allow_runtime_pm;
|
||||
bool own_cd_for_runtime_pm;
|
||||
|
||||
int (*probe) (struct sdhci_pci_chip *);
|
||||
|
||||
int (*probe_slot) (struct sdhci_pci_slot *);
|
||||
void (*remove_slot) (struct sdhci_pci_slot *, int);
|
||||
|
||||
int (*suspend) (struct sdhci_pci_chip *);
|
||||
int (*resume) (struct sdhci_pci_chip *);
|
||||
};
|
||||
|
||||
struct sdhci_pci_slot {
|
||||
struct sdhci_pci_chip *chip;
|
||||
struct sdhci_host *host;
|
||||
struct sdhci_pci_data *data;
|
||||
|
||||
int pci_bar;
|
||||
int rst_n_gpio;
|
||||
int cd_gpio;
|
||||
int cd_irq;
|
||||
|
||||
void (*hw_reset)(struct sdhci_host *host);
|
||||
};
|
||||
|
||||
struct sdhci_pci_chip {
|
||||
struct pci_dev *pdev;
|
||||
|
||||
unsigned int quirks;
|
||||
unsigned int quirks2;
|
||||
bool allow_runtime_pm;
|
||||
const struct sdhci_pci_fixes *fixes;
|
||||
|
||||
int num_slots; /* Slots on controller */
|
||||
struct sdhci_pci_slot *slots[MAX_SLOTS]; /* Pointers to host slots */
|
||||
};
|
||||
|
||||
#endif /* __SDHCI_PCI_H */
|
|
@ -237,19 +237,21 @@ int sdhci_pltfm_unregister(struct platform_device *pdev)
|
|||
EXPORT_SYMBOL_GPL(sdhci_pltfm_unregister);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int sdhci_pltfm_suspend(struct device *dev)
|
||||
int sdhci_pltfm_suspend(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
|
||||
return sdhci_suspend_host(host);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sdhci_pltfm_suspend);
|
||||
|
||||
static int sdhci_pltfm_resume(struct device *dev)
|
||||
int sdhci_pltfm_resume(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
|
||||
return sdhci_resume_host(host);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sdhci_pltfm_resume);
|
||||
|
||||
const struct dev_pm_ops sdhci_pltfm_pmops = {
|
||||
.suspend = sdhci_pltfm_suspend,
|
||||
|
|
|
@ -111,6 +111,8 @@ static inline void *sdhci_pltfm_priv(struct sdhci_pltfm_host *host)
|
|||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
extern int sdhci_pltfm_suspend(struct device *dev);
|
||||
extern int sdhci_pltfm_resume(struct device *dev);
|
||||
extern const struct dev_pm_ops sdhci_pltfm_pmops;
|
||||
#define SDHCI_PLTFM_PMOPS (&sdhci_pltfm_pmops)
|
||||
#else
|
||||
|
|
|
@ -198,6 +198,7 @@ static struct sdhci_tegra_soc_data soc_data_tegra114 = {
|
|||
};
|
||||
|
||||
static const struct of_device_id sdhci_tegra_dt_match[] = {
|
||||
{ .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra114 },
|
||||
{ .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra114 },
|
||||
{ .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 },
|
||||
{ .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 },
|
||||
|
|
|
@ -898,8 +898,13 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
|
|||
u16 mode;
|
||||
struct mmc_data *data = cmd->data;
|
||||
|
||||
if (data == NULL)
|
||||
if (data == NULL) {
|
||||
/* clear Auto CMD settings for no data CMDs */
|
||||
mode = sdhci_readw(host, SDHCI_TRANSFER_MODE);
|
||||
sdhci_writew(host, mode & ~(SDHCI_TRNS_AUTO_CMD12 |
|
||||
SDHCI_TRNS_AUTO_CMD23), SDHCI_TRANSFER_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
WARN_ON(!host->data);
|
||||
|
||||
|
@ -1013,7 +1018,12 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
|
|||
mdelay(1);
|
||||
}
|
||||
|
||||
mod_timer(&host->timer, jiffies + 10 * HZ);
|
||||
timeout = jiffies;
|
||||
if (!cmd->data && cmd->cmd_timeout_ms > 9000)
|
||||
timeout += DIV_ROUND_UP(cmd->cmd_timeout_ms, 1000) * HZ + HZ;
|
||||
else
|
||||
timeout += 10 * HZ;
|
||||
mod_timer(&host->timer, timeout);
|
||||
|
||||
host->cmd = cmd;
|
||||
|
||||
|
@ -1391,6 +1401,13 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||
mmc->card->type == MMC_TYPE_MMC ?
|
||||
MMC_SEND_TUNING_BLOCK_HS200 :
|
||||
MMC_SEND_TUNING_BLOCK;
|
||||
|
||||
/* Here we need to set the host->mrq to NULL,
|
||||
* in case the pending finish_tasklet
|
||||
* finishes it incorrectly.
|
||||
*/
|
||||
host->mrq = NULL;
|
||||
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
sdhci_execute_tuning(mmc, tuning_opcode);
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
@ -1845,12 +1862,12 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|||
unsigned long timeout;
|
||||
int err = 0;
|
||||
bool requires_tuning_nonuhs = false;
|
||||
unsigned long flags;
|
||||
|
||||
host = mmc_priv(mmc);
|
||||
|
||||
sdhci_runtime_pm_get(host);
|
||||
disable_irq(host->irq);
|
||||
spin_lock(&host->lock);
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
||||
|
||||
|
@ -1870,15 +1887,13 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|||
requires_tuning_nonuhs)
|
||||
ctrl |= SDHCI_CTRL_EXEC_TUNING;
|
||||
else {
|
||||
spin_unlock(&host->lock);
|
||||
enable_irq(host->irq);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
sdhci_runtime_pm_put(host);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (host->ops->platform_execute_tuning) {
|
||||
spin_unlock(&host->lock);
|
||||
enable_irq(host->irq);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
err = host->ops->platform_execute_tuning(host, opcode);
|
||||
sdhci_runtime_pm_put(host);
|
||||
return err;
|
||||
|
@ -1951,15 +1966,12 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|||
host->cmd = NULL;
|
||||
host->mrq = NULL;
|
||||
|
||||
spin_unlock(&host->lock);
|
||||
enable_irq(host->irq);
|
||||
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
/* Wait for Buffer Read Ready interrupt */
|
||||
wait_event_interruptible_timeout(host->buf_ready_int,
|
||||
(host->tuning_done == 1),
|
||||
msecs_to_jiffies(50));
|
||||
disable_irq(host->irq);
|
||||
spin_lock(&host->lock);
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
if (!host->tuning_done) {
|
||||
pr_info(DRIVER_NAME ": Timeout waiting for "
|
||||
|
@ -2034,8 +2046,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|||
err = 0;
|
||||
|
||||
sdhci_clear_set_irqs(host, SDHCI_INT_DATA_AVAIL, ier);
|
||||
spin_unlock(&host->lock);
|
||||
enable_irq(host->irq);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
sdhci_runtime_pm_put(host);
|
||||
|
||||
return err;
|
||||
|
@ -3004,7 +3015,8 @@ int sdhci_add_host(struct sdhci_host *host)
|
|||
/* SD3.0: SDR104 is supported so (for eMMC) the caps2
|
||||
* field can be promoted to support HS200.
|
||||
*/
|
||||
mmc->caps2 |= MMC_CAP2_HS200;
|
||||
if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_HS200))
|
||||
mmc->caps2 |= MMC_CAP2_HS200;
|
||||
} else if (caps[1] & SDHCI_SUPPORT_SDR50)
|
||||
mmc->caps |= MMC_CAP_UHS_SDR50;
|
||||
|
||||
|
|
|
@ -381,14 +381,56 @@ static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host)
|
|||
desc, cookie);
|
||||
}
|
||||
|
||||
static void sh_mmcif_request_dma(struct sh_mmcif_host *host,
|
||||
struct sh_mmcif_plat_data *pdata)
|
||||
static struct dma_chan *
|
||||
sh_mmcif_request_dma_one(struct sh_mmcif_host *host,
|
||||
struct sh_mmcif_plat_data *pdata,
|
||||
enum dma_transfer_direction direction)
|
||||
{
|
||||
struct resource *res = platform_get_resource(host->pd, IORESOURCE_MEM, 0);
|
||||
struct dma_slave_config cfg;
|
||||
struct dma_chan *chan;
|
||||
unsigned int slave_id;
|
||||
struct resource *res;
|
||||
dma_cap_mask_t mask;
|
||||
int ret;
|
||||
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
|
||||
if (pdata)
|
||||
slave_id = direction == DMA_MEM_TO_DEV
|
||||
? pdata->slave_id_tx : pdata->slave_id_rx;
|
||||
else
|
||||
slave_id = 0;
|
||||
|
||||
chan = dma_request_slave_channel_compat(mask, shdma_chan_filter,
|
||||
(void *)(unsigned long)slave_id, &host->pd->dev,
|
||||
direction == DMA_MEM_TO_DEV ? "tx" : "rx");
|
||||
|
||||
dev_dbg(&host->pd->dev, "%s: %s: got channel %p\n", __func__,
|
||||
direction == DMA_MEM_TO_DEV ? "TX" : "RX", chan);
|
||||
|
||||
if (!chan)
|
||||
return NULL;
|
||||
|
||||
res = platform_get_resource(host->pd, IORESOURCE_MEM, 0);
|
||||
|
||||
/* In the OF case the driver will get the slave ID from the DT */
|
||||
cfg.slave_id = slave_id;
|
||||
cfg.direction = direction;
|
||||
cfg.dst_addr = res->start + MMCIF_CE_DATA;
|
||||
cfg.src_addr = 0;
|
||||
ret = dmaengine_slave_config(chan, &cfg);
|
||||
if (ret < 0) {
|
||||
dma_release_channel(chan);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return chan;
|
||||
}
|
||||
|
||||
static void sh_mmcif_request_dma(struct sh_mmcif_host *host,
|
||||
struct sh_mmcif_plat_data *pdata)
|
||||
{
|
||||
host->dma_active = false;
|
||||
|
||||
if (pdata) {
|
||||
|
@ -399,55 +441,15 @@ static void sh_mmcif_request_dma(struct sh_mmcif_host *host,
|
|||
}
|
||||
|
||||
/* We can only either use DMA for both Tx and Rx or not use it at all */
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
|
||||
host->chan_tx = dma_request_slave_channel_compat(mask, shdma_chan_filter,
|
||||
pdata ? (void *)pdata->slave_id_tx : NULL,
|
||||
&host->pd->dev, "tx");
|
||||
dev_dbg(&host->pd->dev, "%s: TX: got channel %p\n", __func__,
|
||||
host->chan_tx);
|
||||
|
||||
host->chan_tx = sh_mmcif_request_dma_one(host, pdata, DMA_MEM_TO_DEV);
|
||||
if (!host->chan_tx)
|
||||
return;
|
||||
|
||||
/* In the OF case the driver will get the slave ID from the DT */
|
||||
if (pdata)
|
||||
cfg.slave_id = pdata->slave_id_tx;
|
||||
cfg.direction = DMA_MEM_TO_DEV;
|
||||
cfg.dst_addr = res->start + MMCIF_CE_DATA;
|
||||
cfg.src_addr = 0;
|
||||
ret = dmaengine_slave_config(host->chan_tx, &cfg);
|
||||
if (ret < 0)
|
||||
goto ecfgtx;
|
||||
|
||||
host->chan_rx = dma_request_slave_channel_compat(mask, shdma_chan_filter,
|
||||
pdata ? (void *)pdata->slave_id_rx : NULL,
|
||||
&host->pd->dev, "rx");
|
||||
dev_dbg(&host->pd->dev, "%s: RX: got channel %p\n", __func__,
|
||||
host->chan_rx);
|
||||
|
||||
if (!host->chan_rx)
|
||||
goto erqrx;
|
||||
|
||||
if (pdata)
|
||||
cfg.slave_id = pdata->slave_id_rx;
|
||||
cfg.direction = DMA_DEV_TO_MEM;
|
||||
cfg.dst_addr = 0;
|
||||
cfg.src_addr = res->start + MMCIF_CE_DATA;
|
||||
ret = dmaengine_slave_config(host->chan_rx, &cfg);
|
||||
if (ret < 0)
|
||||
goto ecfgrx;
|
||||
|
||||
return;
|
||||
|
||||
ecfgrx:
|
||||
dma_release_channel(host->chan_rx);
|
||||
host->chan_rx = NULL;
|
||||
erqrx:
|
||||
ecfgtx:
|
||||
dma_release_channel(host->chan_tx);
|
||||
host->chan_tx = NULL;
|
||||
host->chan_rx = sh_mmcif_request_dma_one(host, pdata, DMA_DEV_TO_MEM);
|
||||
if (!host->chan_rx) {
|
||||
dma_release_channel(host->chan_tx);
|
||||
host->chan_tx = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void sh_mmcif_release_dma(struct sh_mmcif_host *host)
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
|
||||
#include "tmio_mmc.h"
|
||||
|
||||
#define EXT_ACC 0xe4
|
||||
|
||||
struct sh_mobile_sdhi_of_data {
|
||||
unsigned long tmio_flags;
|
||||
};
|
||||
|
@ -54,7 +56,7 @@ static int sh_mobile_sdhi_clk_enable(struct platform_device *pdev, unsigned int
|
|||
struct mmc_host *mmc = platform_get_drvdata(pdev);
|
||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||
struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data);
|
||||
int ret = clk_enable(priv->clk);
|
||||
int ret = clk_prepare_enable(priv->clk);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -67,7 +69,7 @@ static void sh_mobile_sdhi_clk_disable(struct platform_device *pdev)
|
|||
struct mmc_host *mmc = platform_get_drvdata(pdev);
|
||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||
struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data);
|
||||
clk_disable(priv->clk);
|
||||
clk_disable_unprepare(priv->clk);
|
||||
}
|
||||
|
||||
static int sh_mobile_sdhi_wait_idle(struct tmio_mmc_host *host)
|
||||
|
@ -133,9 +135,15 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
|
|||
struct tmio_mmc_data *mmc_data;
|
||||
struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
|
||||
struct tmio_mmc_host *host;
|
||||
struct resource *res;
|
||||
int irq, ret, i = 0;
|
||||
bool multiplexed_isr = true;
|
||||
struct tmio_mmc_dma *dma_priv;
|
||||
u16 ver;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -EINVAL;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_mobile_sdhi), GFP_KERNEL);
|
||||
if (priv == NULL) {
|
||||
|
@ -206,10 +214,21 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
|
|||
mmc_data->flags |= of_data->tmio_flags;
|
||||
}
|
||||
|
||||
/* SD control register space size is 0x100, 0x200 for bus_shift=1 */
|
||||
mmc_data->bus_shift = resource_size(res) >> 9;
|
||||
|
||||
ret = tmio_mmc_host_probe(&host, pdev, mmc_data);
|
||||
if (ret < 0)
|
||||
goto eprobe;
|
||||
|
||||
/*
|
||||
* FIXME:
|
||||
* this Workaround can be more clever method
|
||||
*/
|
||||
ver = sd_ctrl_read16(host, CTL_VERSION);
|
||||
if (ver == 0xCB0D)
|
||||
sd_ctrl_write16(host, EXT_ACC, 1);
|
||||
|
||||
/*
|
||||
* Allow one or more specific (named) ISRs or
|
||||
* one or more multiplexed (un-named) ISRs.
|
||||
|
|
|
@ -62,6 +62,7 @@ static int tmio_mmc_probe(struct platform_device *pdev)
|
|||
const struct mfd_cell *cell = mfd_get_cell(pdev);
|
||||
struct tmio_mmc_data *pdata;
|
||||
struct tmio_mmc_host *host;
|
||||
struct resource *res;
|
||||
int ret = -EINVAL, irq;
|
||||
|
||||
if (pdev->num_resources != 2)
|
||||
|
@ -84,6 +85,14 @@ static int tmio_mmc_probe(struct platform_device *pdev)
|
|||
goto out;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -EINVAL;
|
||||
|
||||
/* SD control register space size is 0x200, 0x400 for bus_shift=1 */
|
||||
pdata->bus_shift = resource_size(res) >> 10;
|
||||
pdata->flags |= TMIO_MMC_HAVE_HIGH_REG;
|
||||
|
||||
ret = tmio_mmc_host_probe(&host, pdev, pdata);
|
||||
if (ret)
|
||||
goto cell_disable;
|
||||
|
|
|
@ -58,7 +58,6 @@ enum tmio_mmc_power {
|
|||
|
||||
struct tmio_mmc_host {
|
||||
void __iomem *ctl;
|
||||
unsigned long bus_shift;
|
||||
struct mmc_command *cmd;
|
||||
struct mmc_request *mrq;
|
||||
struct mmc_data *data;
|
||||
|
@ -176,19 +175,19 @@ int tmio_mmc_host_runtime_resume(struct device *dev);
|
|||
|
||||
static inline u16 sd_ctrl_read16(struct tmio_mmc_host *host, int addr)
|
||||
{
|
||||
return readw(host->ctl + (addr << host->bus_shift));
|
||||
return readw(host->ctl + (addr << host->pdata->bus_shift));
|
||||
}
|
||||
|
||||
static inline void sd_ctrl_read16_rep(struct tmio_mmc_host *host, int addr,
|
||||
u16 *buf, int count)
|
||||
{
|
||||
readsw(host->ctl + (addr << host->bus_shift), buf, count);
|
||||
readsw(host->ctl + (addr << host->pdata->bus_shift), buf, count);
|
||||
}
|
||||
|
||||
static inline u32 sd_ctrl_read32(struct tmio_mmc_host *host, int addr)
|
||||
{
|
||||
return readw(host->ctl + (addr << host->bus_shift)) |
|
||||
readw(host->ctl + ((addr + 2) << host->bus_shift)) << 16;
|
||||
return readw(host->ctl + (addr << host->pdata->bus_shift)) |
|
||||
readw(host->ctl + ((addr + 2) << host->pdata->bus_shift)) << 16;
|
||||
}
|
||||
|
||||
static inline void sd_ctrl_write16(struct tmio_mmc_host *host, int addr, u16 val)
|
||||
|
@ -198,19 +197,19 @@ static inline void sd_ctrl_write16(struct tmio_mmc_host *host, int addr, u16 val
|
|||
*/
|
||||
if (host->pdata->write16_hook && host->pdata->write16_hook(host, addr))
|
||||
return;
|
||||
writew(val, host->ctl + (addr << host->bus_shift));
|
||||
writew(val, host->ctl + (addr << host->pdata->bus_shift));
|
||||
}
|
||||
|
||||
static inline void sd_ctrl_write16_rep(struct tmio_mmc_host *host, int addr,
|
||||
u16 *buf, int count)
|
||||
{
|
||||
writesw(host->ctl + (addr << host->bus_shift), buf, count);
|
||||
writesw(host->ctl + (addr << host->pdata->bus_shift), buf, count);
|
||||
}
|
||||
|
||||
static inline void sd_ctrl_write32(struct tmio_mmc_host *host, int addr, u32 val)
|
||||
{
|
||||
writew(val, host->ctl + (addr << host->bus_shift));
|
||||
writew(val >> 16, host->ctl + ((addr + 2) << host->bus_shift));
|
||||
writew(val, host->ctl + (addr << host->pdata->bus_shift));
|
||||
writew(val >> 16, host->ctl + ((addr + 2) << host->pdata->bus_shift));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -293,7 +293,7 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat
|
|||
if (pdata->dma->chan_priv_tx)
|
||||
cfg.slave_id = pdata->dma->slave_id_tx;
|
||||
cfg.direction = DMA_MEM_TO_DEV;
|
||||
cfg.dst_addr = res->start + (CTL_SD_DATA_PORT << host->bus_shift);
|
||||
cfg.dst_addr = res->start + (CTL_SD_DATA_PORT << host->pdata->bus_shift);
|
||||
cfg.src_addr = 0;
|
||||
ret = dmaengine_slave_config(host->chan_tx, &cfg);
|
||||
if (ret < 0)
|
||||
|
|
|
@ -161,10 +161,8 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host, int new_clock)
|
|||
|
||||
static void tmio_mmc_clk_stop(struct tmio_mmc_host *host)
|
||||
{
|
||||
struct resource *res = platform_get_resource(host->pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
/* implicit BUG_ON(!res) */
|
||||
if (resource_size(res) > 0x100) {
|
||||
if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG) {
|
||||
sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0000);
|
||||
msleep(10);
|
||||
}
|
||||
|
@ -176,14 +174,12 @@ static void tmio_mmc_clk_stop(struct tmio_mmc_host *host)
|
|||
|
||||
static void tmio_mmc_clk_start(struct tmio_mmc_host *host)
|
||||
{
|
||||
struct resource *res = platform_get_resource(host->pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, 0x0100 |
|
||||
sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
|
||||
msleep(10);
|
||||
|
||||
/* implicit BUG_ON(!res) */
|
||||
if (resource_size(res) > 0x100) {
|
||||
if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG) {
|
||||
sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0100);
|
||||
msleep(10);
|
||||
}
|
||||
|
@ -191,16 +187,14 @@ static void tmio_mmc_clk_start(struct tmio_mmc_host *host)
|
|||
|
||||
static void tmio_mmc_reset(struct tmio_mmc_host *host)
|
||||
{
|
||||
struct resource *res = platform_get_resource(host->pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
/* FIXME - should we set stop clock reg here */
|
||||
sd_ctrl_write16(host, CTL_RESET_SD, 0x0000);
|
||||
/* implicit BUG_ON(!res) */
|
||||
if (resource_size(res) > 0x100)
|
||||
if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG)
|
||||
sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0000);
|
||||
msleep(10);
|
||||
sd_ctrl_write16(host, CTL_RESET_SD, 0x0001);
|
||||
if (resource_size(res) > 0x100)
|
||||
if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG)
|
||||
sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0001);
|
||||
msleep(10);
|
||||
}
|
||||
|
@ -944,17 +938,25 @@ static const struct mmc_host_ops tmio_mmc_ops = {
|
|||
.enable_sdio_irq = tmio_mmc_enable_sdio_irq,
|
||||
};
|
||||
|
||||
static void tmio_mmc_init_ocr(struct tmio_mmc_host *host)
|
||||
static int tmio_mmc_init_ocr(struct tmio_mmc_host *host)
|
||||
{
|
||||
struct tmio_mmc_data *pdata = host->pdata;
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
|
||||
mmc_regulator_get_supply(mmc);
|
||||
|
||||
/* use ocr_mask if no regulator */
|
||||
if (!mmc->ocr_avail)
|
||||
mmc->ocr_avail = pdata->ocr_mask ? : MMC_VDD_32_33 | MMC_VDD_33_34;
|
||||
else if (pdata->ocr_mask)
|
||||
dev_warn(mmc_dev(mmc), "Platform OCR mask is ignored\n");
|
||||
mmc->ocr_avail = pdata->ocr_mask;
|
||||
|
||||
/*
|
||||
* try again.
|
||||
* There is possibility that regulator has not been probed
|
||||
*/
|
||||
if (!mmc->ocr_avail)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tmio_mmc_of_parse(struct platform_device *pdev,
|
||||
|
@ -1005,8 +1007,9 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host,
|
|||
_host->set_pwr = pdata->set_pwr;
|
||||
_host->set_clk_div = pdata->set_clk_div;
|
||||
|
||||
/* SD control register space size is 0x200, 0x400 for bus_shift=1 */
|
||||
_host->bus_shift = resource_size(res_ctl) >> 10;
|
||||
ret = tmio_mmc_init_ocr(_host);
|
||||
if (ret < 0)
|
||||
goto host_free;
|
||||
|
||||
_host->ctl = ioremap(res_ctl->start, resource_size(res_ctl));
|
||||
if (!_host->ctl) {
|
||||
|
@ -1016,14 +1019,13 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host,
|
|||
|
||||
mmc->ops = &tmio_mmc_ops;
|
||||
mmc->caps |= MMC_CAP_4_BIT_DATA | pdata->capabilities;
|
||||
mmc->caps2 = pdata->capabilities2;
|
||||
mmc->caps2 |= pdata->capabilities2;
|
||||
mmc->max_segs = 32;
|
||||
mmc->max_blk_size = 512;
|
||||
mmc->max_blk_count = (PAGE_CACHE_SIZE / mmc->max_blk_size) *
|
||||
mmc->max_segs;
|
||||
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
|
||||
mmc->max_seg_size = mmc->max_req_size;
|
||||
tmio_mmc_init_ocr(_host);
|
||||
|
||||
_host->native_hotplug = !(pdata->flags & TMIO_MMC_USE_GPIO_CD ||
|
||||
mmc->caps & MMC_CAP_NEEDS_POLL ||
|
||||
|
|
|
@ -76,6 +76,13 @@
|
|||
*/
|
||||
#define TMIO_MMC_USE_GPIO_CD (1 << 5)
|
||||
|
||||
/*
|
||||
* Some controllers doesn't have over 0x100 register.
|
||||
* it is used to checking accessibility of
|
||||
* CTL_SD_CARD_CLK_CTL / CTL_CLK_AND_WAIT_CTL
|
||||
*/
|
||||
#define TMIO_MMC_HAVE_HIGH_REG (1 << 6)
|
||||
|
||||
int tmio_core_mmc_enable(void __iomem *cnf, int shift, unsigned long base);
|
||||
int tmio_core_mmc_resume(void __iomem *cnf, int shift, unsigned long base);
|
||||
void tmio_core_mmc_pwr(void __iomem *cnf, int shift, int state);
|
||||
|
@ -102,6 +109,7 @@ struct tmio_mmc_data {
|
|||
unsigned long capabilities;
|
||||
unsigned long capabilities2;
|
||||
unsigned long flags;
|
||||
unsigned long bus_shift;
|
||||
u32 ocr_mask; /* available voltages */
|
||||
struct tmio_mmc_dma *dma;
|
||||
struct device *dev;
|
||||
|
|
|
@ -271,9 +271,10 @@ struct mmc_card {
|
|||
#define MMC_QUIRK_INAND_CMD38 (1<<6) /* iNAND devices have broken CMD38 */
|
||||
#define MMC_QUIRK_BLK_NO_CMD23 (1<<7) /* Avoid CMD23 for regular multiblock */
|
||||
#define MMC_QUIRK_BROKEN_BYTE_MODE_512 (1<<8) /* Avoid sending 512 bytes in */
|
||||
/* byte mode */
|
||||
#define MMC_QUIRK_LONG_READ_TIME (1<<9) /* Data read time > CSD says */
|
||||
#define MMC_QUIRK_SEC_ERASE_TRIM_BROKEN (1<<10) /* Skip secure for erase/trim */
|
||||
/* byte mode */
|
||||
#define MMC_QUIRK_BROKEN_IRQ_POLLING (1<<11) /* Polling SDIO_CCCR_INTx could create a fake interrupt */
|
||||
|
||||
unsigned int erase_size; /* erase size in sectors */
|
||||
unsigned int erase_shift; /* if erase unit is power 2 */
|
||||
|
@ -505,6 +506,11 @@ static inline int mmc_card_long_read_time(const struct mmc_card *c)
|
|||
return c->quirks & MMC_QUIRK_LONG_READ_TIME;
|
||||
}
|
||||
|
||||
static inline int mmc_card_broken_irq_polling(const struct mmc_card *c)
|
||||
{
|
||||
return c->quirks & MMC_QUIRK_BROKEN_IRQ_POLLING;
|
||||
}
|
||||
|
||||
#define mmc_card_name(c) ((c)->cid.prod_name)
|
||||
#define mmc_card_id(c) (dev_name(&(c)->dev))
|
||||
|
||||
|
|
|
@ -98,6 +98,8 @@ struct sdhci_host {
|
|||
#define SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON (1<<4)
|
||||
/* Controller has a non-standard host control register */
|
||||
#define SDHCI_QUIRK2_BROKEN_HOST_CONTROL (1<<5)
|
||||
/* Controller does not support HS200 */
|
||||
#define SDHCI_QUIRK2_BROKEN_HS200 (1<<6)
|
||||
|
||||
int irq; /* Device IRQ */
|
||||
void __iomem *ioaddr; /* Mapped address */
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#define CTL_SDIO_IRQ_MASK 0x38
|
||||
#define CTL_DMA_ENABLE 0xd8
|
||||
#define CTL_RESET_SD 0xe0
|
||||
#define CTL_VERSION 0xe2
|
||||
#define CTL_SDIO_REGS 0x100
|
||||
#define CTL_CLK_AND_WAIT_CTL 0x138
|
||||
#define CTL_RESET_SDIO 0x1e0
|
||||
|
|
Loading…
Reference in New Issue