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: zynq
|
||||||
N: xilinx
|
N: xilinx
|
||||||
F: drivers/clocksource/cadence_ttc_timer.c
|
F: drivers/clocksource/cadence_ttc_timer.c
|
||||||
|
F: drivers/mmc/host/sdhci-of-arasan.c
|
||||||
|
|
||||||
ARM SMMU DRIVER
|
ARM SMMU DRIVER
|
||||||
M: Will Deacon <will.deacon@arm.com>
|
M: Will Deacon <will.deacon@arm.com>
|
||||||
|
@ -5782,7 +5783,7 @@ F: drivers/mfd/
|
||||||
F: include/linux/mfd/
|
F: include/linux/mfd/
|
||||||
|
|
||||||
MULTIMEDIA CARD (MMC), SECURE DIGITAL (SD) AND SDIO SUBSYSTEM
|
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
|
L: linux-mmc@vger.kernel.org
|
||||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc.git
|
T: git git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc.git
|
||||||
S: Maintained
|
S: Maintained
|
||||||
|
@ -7609,7 +7610,7 @@ S: Maintained
|
||||||
F: drivers/mmc/host/sdricoh_cs.c
|
F: drivers/mmc/host/sdricoh_cs.c
|
||||||
|
|
||||||
SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) DRIVER
|
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
|
L: linux-mmc@vger.kernel.org
|
||||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc.git
|
T: git git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc.git
|
||||||
S: Maintained
|
S: Maintained
|
||||||
|
@ -8768,7 +8769,6 @@ F: include/linux/toshiba.h
|
||||||
F: include/uapi/linux/toshiba.h
|
F: include/uapi/linux/toshiba.h
|
||||||
|
|
||||||
TMIO MMC DRIVER
|
TMIO MMC DRIVER
|
||||||
M: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
|
|
||||||
M: Ian Molton <ian@mnementh.co.uk>
|
M: Ian Molton <ian@mnementh.co.uk>
|
||||||
L: linux-mmc@vger.kernel.org
|
L: linux-mmc@vger.kernel.org
|
||||||
S: Maintained
|
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_card *card = md->queue.card;
|
||||||
struct mmc_host *host = card->host;
|
struct mmc_host *host = card->host;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
unsigned int cmd_flags = req ? req->cmd_flags : 0;
|
||||||
|
|
||||||
if (req && !mq->mqrq_prev->req)
|
if (req && !mq->mqrq_prev->req)
|
||||||
/* claim host only for the first request */
|
/* 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;
|
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 */
|
/* complete ongoing async transfer before issuing discard */
|
||||||
if (card->host->areq)
|
if (card->host->areq)
|
||||||
mmc_blk_issue_rw_rq(mq, NULL);
|
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);
|
ret = mmc_blk_issue_secdiscard_rq(mq, req);
|
||||||
else
|
else
|
||||||
ret = mmc_blk_issue_discard_rq(mq, req);
|
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 */
|
/* complete ongoing async transfer before issuing flush */
|
||||||
if (card->host->areq)
|
if (card->host->areq)
|
||||||
mmc_blk_issue_rw_rq(mq, NULL);
|
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:
|
out:
|
||||||
if ((!req && !(mq->flags & MMC_QUEUE_NEW_REQUEST)) ||
|
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
|
* Release host when there are no more requests
|
||||||
* and after special request(discard, flush) is done.
|
* and after special request(discard, flush) is done.
|
||||||
|
|
|
@ -2460,7 +2460,8 @@ void mmc_rescan(struct work_struct *work)
|
||||||
*/
|
*/
|
||||||
mmc_bus_put(host);
|
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_claim_host(host);
|
||||||
mmc_power_off(host);
|
mmc_power_off(host);
|
||||||
mmc_release_host(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 (mmc_card_highspeed(card)) {
|
||||||
if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_8V)
|
if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_8V)
|
||||||
&& ((host->caps & (MMC_CAP_1_8V_DDR |
|
&& (host->caps & MMC_CAP_1_8V_DDR))
|
||||||
MMC_CAP_UHS_DDR50))
|
|
||||||
== (MMC_CAP_1_8V_DDR | MMC_CAP_UHS_DDR50)))
|
|
||||||
ddr = MMC_1_8V_DDR_MODE;
|
ddr = MMC_1_8V_DDR_MODE;
|
||||||
else if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_2V)
|
else if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_2V)
|
||||||
&& ((host->caps & (MMC_CAP_1_2V_DDR |
|
&& (host->caps & MMC_CAP_1_2V_DDR))
|
||||||
MMC_CAP_UHS_DDR50))
|
|
||||||
== (MMC_CAP_1_2V_DDR | MMC_CAP_UHS_DDR50)))
|
|
||||||
ddr = MMC_1_2V_DDR_MODE;
|
ddr = MMC_1_2V_DDR_MODE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/export.h>
|
#include <linux/export.h>
|
||||||
#include <linux/mmc/card.h>
|
#include <linux/mmc/card.h>
|
||||||
|
#include <linux/mmc/sdio_ids.h>
|
||||||
|
|
||||||
#ifndef SDIO_VENDOR_ID_TI
|
#ifndef SDIO_VENDOR_ID_TI
|
||||||
#define SDIO_VENDOR_ID_TI 0x0097
|
#define SDIO_VENDOR_ID_TI 0x0097
|
||||||
|
@ -30,6 +31,10 @@
|
||||||
#define SDIO_DEVICE_ID_STE_CW1200 0x2280
|
#define SDIO_DEVICE_ID_STE_CW1200 0x2280
|
||||||
#endif
|
#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
|
* 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,
|
SDIO_FIXUP(SDIO_VENDOR_ID_STE, SDIO_DEVICE_ID_STE_CW1200,
|
||||||
add_quirk, MMC_QUIRK_BROKEN_BYTE_MODE_512),
|
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
|
END_FIXUP
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
|
#include <linux/sizes.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/stat.h>
|
#include <linux/stat.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
|
@ -45,6 +46,13 @@ static const unsigned int tacc_mant[] = {
|
||||||
35, 40, 45, 50, 55, 60, 70, 80,
|
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) \
|
#define UNSTUFF_BITS(resp,start,size) \
|
||||||
({ \
|
({ \
|
||||||
const int __size = 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)
|
static int mmc_read_ssr(struct mmc_card *card)
|
||||||
{
|
{
|
||||||
unsigned int au, es, et, eo;
|
unsigned int au, es, et, eo;
|
||||||
int err, i, max_au;
|
int err, i;
|
||||||
u32 *ssr;
|
u32 *ssr;
|
||||||
|
|
||||||
if (!(card->csd.cmdclass & CCC_APP_SPEC)) {
|
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++)
|
for (i = 0; i < 16; i++)
|
||||||
ssr[i] = be32_to_cpu(ssr[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
|
* UNSTUFF_BITS only works with four u32s so we have to offset the
|
||||||
* bitfield positions accordingly.
|
* bitfield positions accordingly.
|
||||||
*/
|
*/
|
||||||
au = UNSTUFF_BITS(ssr, 428 - 384, 4);
|
au = UNSTUFF_BITS(ssr, 428 - 384, 4);
|
||||||
if (au > 0 && au <= max_au) {
|
if (au) {
|
||||||
card->ssr.au = 1 << (au + 4);
|
if (au <= 9 || card->scr.sda_spec3) {
|
||||||
es = UNSTUFF_BITS(ssr, 408 - 384, 16);
|
card->ssr.au = sd_au_size[au];
|
||||||
et = UNSTUFF_BITS(ssr, 402 - 384, 6);
|
es = UNSTUFF_BITS(ssr, 408 - 384, 16);
|
||||||
eo = UNSTUFF_BITS(ssr, 400 - 384, 2);
|
et = UNSTUFF_BITS(ssr, 402 - 384, 6);
|
||||||
if (es && et) {
|
if (es && et) {
|
||||||
card->ssr.erase_timeout = (et * 1000) / es;
|
eo = UNSTUFF_BITS(ssr, 400 - 384, 2);
|
||||||
card->ssr.erase_offset = eo * 1000;
|
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:
|
out:
|
||||||
kfree(ssr);
|
kfree(ssr);
|
||||||
|
|
|
@ -53,6 +53,17 @@ static int process_sdio_pending_irqs(struct mmc_host *host)
|
||||||
return ret;
|
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;
|
count = 0;
|
||||||
for (i = 1; i <= 7; i++) {
|
for (i = 1; i <= 7; i++) {
|
||||||
if (pending & (1 << i)) {
|
if (pending & (1 << i)) {
|
||||||
|
|
|
@ -104,6 +104,18 @@ config MMC_SDHCI_PLTFM
|
||||||
|
|
||||||
If unsure, say N.
|
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
|
config MMC_SDHCI_OF_ESDHC
|
||||||
tristate "SDHCI OF support for the Freescale eSDHC controller"
|
tristate "SDHCI OF support for the Freescale eSDHC controller"
|
||||||
depends on MMC_SDHCI_PLTFM
|
depends on MMC_SDHCI_PLTFM
|
||||||
|
@ -479,7 +491,8 @@ config MMC_TMIO
|
||||||
|
|
||||||
config MMC_SDHI
|
config MMC_SDHI
|
||||||
tristate "SH-Mobile SDHI SD/SDIO controller support"
|
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
|
select MMC_TMIO_CORE
|
||||||
help
|
help
|
||||||
This provides support for the SDHI SD/SDIO controller found in
|
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
|
This selects support for Altera SoCFPGA specific extensions to the
|
||||||
Synopsys DesignWare Memory Card Interface driver.
|
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
|
config MMC_DW_PCI
|
||||||
tristate "Synopsys Designware MCI support on PCI bus"
|
tristate "Synopsys Designware MCI support on PCI bus"
|
||||||
depends on MMC_DW && PCI
|
depends on MMC_DW && PCI
|
||||||
|
@ -588,7 +611,8 @@ config MMC_DW_PCI
|
||||||
|
|
||||||
config MMC_SH_MMCIF
|
config MMC_SH_MMCIF
|
||||||
tristate "SuperH Internal MMCIF support"
|
tristate "SuperH Internal MMCIF support"
|
||||||
depends on MMC_BLOCK && (SUPERH || ARCH_SHMOBILE)
|
depends on MMC_BLOCK
|
||||||
|
depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
|
||||||
help
|
help
|
||||||
This selects the MMC Host Interface controller (MMCIF).
|
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) += sdhci.o
|
||||||
obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.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-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_ACPI) += sdhci-acpi.o
|
||||||
obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o
|
obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o
|
||||||
obj-$(CONFIG_MMC_SDHCI_PXAV2) += sdhci-pxav2.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_PLTFM) += dw_mmc-pltfm.o
|
||||||
obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o
|
obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o
|
||||||
obj-$(CONFIG_MMC_DW_SOCFPGA) += dw_mmc-socfpga.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_DW_PCI) += dw_mmc-pci.o
|
||||||
obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
|
obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
|
||||||
obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.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_ESDHC_IMX) += sdhci-esdhc-imx.o
|
||||||
obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o
|
obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o
|
||||||
obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.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_ESDHC) += sdhci-of-esdhc.o
|
||||||
obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o
|
obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o
|
||||||
obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.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;
|
iflags |= ATMCI_CMDRDY;
|
||||||
cmd = mrq->cmd;
|
cmd = mrq->cmd;
|
||||||
cmdflags = atmci_prepare_command(slot->mmc, 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)
|
if (data)
|
||||||
host->submit_data(host, data);
|
host->submit_data(host, data);
|
||||||
|
|
||||||
|
if (host->submit_data == &atmci_submit_data_dma)
|
||||||
|
atmci_send_command(host, cmd, cmdflags);
|
||||||
|
|
||||||
if (mrq->stop) {
|
if (mrq->stop) {
|
||||||
host->stop_cmdr = atmci_prepare_command(slot->mmc, mrq->stop);
|
host->stop_cmdr = atmci_prepare_command(slot->mmc, mrq->stop);
|
||||||
host->stop_cmdr |= ATMCI_CMDR_STOP_XFER;
|
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);
|
clk_unprepare(host->mck);
|
||||||
|
|
||||||
switch (ios->power_mode) {
|
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:
|
case MMC_POWER_UP:
|
||||||
set_bit(ATMCI_CARD_NEED_INIT, &slot->flags);
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
/*
|
/*
|
||||||
|
@ -2204,6 +2221,7 @@ static int __init atmci_init_slot(struct atmel_mci *host,
|
||||||
}
|
}
|
||||||
|
|
||||||
host->slot[id] = slot;
|
host->slot[id] = slot;
|
||||||
|
mmc_regulator_get_supply(mmc);
|
||||||
mmc_add_host(mmc);
|
mmc_add_host(mmc);
|
||||||
|
|
||||||
if (gpio_is_valid(slot->detect_pin)) {
|
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/workqueue.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_gpio.h>
|
#include <linux/of_gpio.h>
|
||||||
|
#include <linux/mmc/slot-gpio.h>
|
||||||
|
|
||||||
#include "dw_mmc.h"
|
#include "dw_mmc.h"
|
||||||
|
|
||||||
|
@ -1032,20 +1033,29 @@ static int dw_mci_get_cd(struct mmc_host *mmc)
|
||||||
int present;
|
int present;
|
||||||
struct dw_mci_slot *slot = mmc_priv(mmc);
|
struct dw_mci_slot *slot = mmc_priv(mmc);
|
||||||
struct dw_mci_board *brd = slot->host->pdata;
|
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 */
|
/* Use platform get_cd function, else try onboard card detect */
|
||||||
if (brd->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION)
|
if (brd->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION)
|
||||||
present = 1;
|
present = 1;
|
||||||
else if (brd->get_cd)
|
else if (brd->get_cd)
|
||||||
present = !brd->get_cd(slot->id);
|
present = !brd->get_cd(slot->id);
|
||||||
|
else if (!IS_ERR_VALUE(gpio_cd))
|
||||||
|
present = gpio_cd;
|
||||||
else
|
else
|
||||||
present = (mci_readl(slot->host, CDETECT) & (1 << slot->id))
|
present = (mci_readl(slot->host, CDETECT) & (1 << slot->id))
|
||||||
== 0 ? 1 : 0;
|
== 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");
|
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");
|
dev_dbg(&mmc->class_dev, "card is not present\n");
|
||||||
|
}
|
||||||
|
spin_unlock_bh(&host->lock);
|
||||||
|
|
||||||
return present;
|
return present;
|
||||||
}
|
}
|
||||||
|
@ -1926,10 +1936,6 @@ static void dw_mci_work_routine_card(struct work_struct *work)
|
||||||
/* Card change detected */
|
/* Card change detected */
|
||||||
slot->last_detect_state = present;
|
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 */
|
/* Clean up queue if present */
|
||||||
mrq = slot->mrq;
|
mrq = slot->mrq;
|
||||||
if (mrq) {
|
if (mrq) {
|
||||||
|
@ -1977,8 +1983,6 @@ static void dw_mci_work_routine_card(struct work_struct *work)
|
||||||
|
|
||||||
/* Power down slot */
|
/* Power down slot */
|
||||||
if (present == 0) {
|
if (present == 0) {
|
||||||
clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
|
|
||||||
|
|
||||||
/* Clear down the FIFO */
|
/* Clear down the FIFO */
|
||||||
dw_mci_fifo_reset(host);
|
dw_mci_fifo_reset(host);
|
||||||
#ifdef CONFIG_MMC_DW_IDMAC
|
#ifdef CONFIG_MMC_DW_IDMAC
|
||||||
|
@ -2079,6 +2083,26 @@ static int dw_mci_of_get_wp_gpio(struct device *dev, u8 slot)
|
||||||
|
|
||||||
return gpio;
|
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 */
|
#else /* CONFIG_OF */
|
||||||
static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot)
|
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;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
static void dw_mci_of_get_cd_gpio(struct device *dev, u8 slot,
|
||||||
|
struct mmc_host *mmc)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
#endif /* CONFIG_OF */
|
#endif /* CONFIG_OF */
|
||||||
|
|
||||||
static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
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 */
|
#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);
|
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);
|
ret = mmc_add_host(mmc);
|
||||||
if (ret)
|
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))
|
if (of_find_property(np, "caps2-mmc-hs200-1_2v", NULL))
|
||||||
pdata->caps2 |= MMC_CAP2_HS200_1_2V_SDR;
|
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;
|
return pdata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
#include <linux/mmc/host.h>
|
#include <linux/mmc/host.h>
|
||||||
#include <linux/mmc/mmc.h>
|
#include <linux/mmc/mmc.h>
|
||||||
#include <linux/mmc/sdio.h>
|
#include <linux/mmc/sdio.h>
|
||||||
|
#include <linux/mmc/slot-gpio.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
@ -69,37 +70,25 @@ struct mxs_mmc_host {
|
||||||
unsigned char bus_width;
|
unsigned char bus_width;
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
int sdio_irq_en;
|
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)
|
static int mxs_mmc_get_cd(struct mmc_host *mmc)
|
||||||
{
|
{
|
||||||
struct mxs_mmc_host *host = mmc_priv(mmc);
|
struct mxs_mmc_host *host = mmc_priv(mmc);
|
||||||
struct mxs_ssp *ssp = &host->ssp;
|
struct mxs_ssp *ssp = &host->ssp;
|
||||||
|
int present, ret;
|
||||||
|
|
||||||
return host->non_removable || host->broken_cd ||
|
ret = mmc_gpio_get_cd(mmc);
|
||||||
!(readl(ssp->base + HW_SSP_STATUS(ssp)) &
|
if (ret >= 0)
|
||||||
BM_SSP_STATUS_CARD_DETECT) ^ host->cd_inverted;
|
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)
|
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 = {
|
static const struct mmc_host_ops mxs_mmc_ops = {
|
||||||
.request = mxs_mmc_request,
|
.request = mxs_mmc_request,
|
||||||
.get_ro = mxs_mmc_get_ro,
|
.get_ro = mmc_gpio_get_ro,
|
||||||
.get_cd = mxs_mmc_get_cd,
|
.get_cd = mxs_mmc_get_cd,
|
||||||
.set_ios = mxs_mmc_set_ios,
|
.set_ios = mxs_mmc_set_ios,
|
||||||
.enable_sdio_irq = mxs_mmc_enable_sdio_irq,
|
.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 =
|
const struct of_device_id *of_id =
|
||||||
of_match_device(mxs_mmc_dt_ids, &pdev->dev);
|
of_match_device(mxs_mmc_dt_ids, &pdev->dev);
|
||||||
struct device_node *np = pdev->dev.of_node;
|
|
||||||
struct mxs_mmc_host *host;
|
struct mxs_mmc_host *host;
|
||||||
struct mmc_host *mmc;
|
struct mmc_host *mmc;
|
||||||
struct resource *iores;
|
struct resource *iores;
|
||||||
int ret = 0, irq_err;
|
int ret = 0, irq_err;
|
||||||
struct regulator *reg_vmmc;
|
struct regulator *reg_vmmc;
|
||||||
enum of_gpio_flags flags;
|
|
||||||
struct mxs_ssp *ssp;
|
struct mxs_ssp *ssp;
|
||||||
u32 bus_width = 0;
|
|
||||||
|
|
||||||
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
irq_err = platform_get_irq(pdev, 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->caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
|
||||||
MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL;
|
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_min = 400000;
|
||||||
mmc->f_max = 288000000;
|
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->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
|
||||||
|
|
||||||
mmc->max_segs = 52;
|
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 },
|
{ "80860F14" , "3" , &sdhci_acpi_slot_int_sd },
|
||||||
{ "INT33BB" , "2" , &sdhci_acpi_slot_int_sdio },
|
{ "INT33BB" , "2" , &sdhci_acpi_slot_int_sdio },
|
||||||
{ "INT33C6" , NULL, &sdhci_acpi_slot_int_sdio },
|
{ "INT33C6" , NULL, &sdhci_acpi_slot_int_sdio },
|
||||||
|
{ "INT3436" , NULL, &sdhci_acpi_slot_int_sdio },
|
||||||
{ "PNP0D40" },
|
{ "PNP0D40" },
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
|
@ -151,6 +152,7 @@ static const struct acpi_device_id sdhci_acpi_ids[] = {
|
||||||
{ "80860F14" },
|
{ "80860F14" },
|
||||||
{ "INT33BB" },
|
{ "INT33BB" },
|
||||||
{ "INT33C6" },
|
{ "INT33C6" },
|
||||||
|
{ "INT3436" },
|
||||||
{ "PNP0D40" },
|
{ "PNP0D40" },
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include <linux/of_gpio.h>
|
#include <linux/of_gpio.h>
|
||||||
#include <linux/pinctrl/consumer.h>
|
#include <linux/pinctrl/consumer.h>
|
||||||
#include <linux/platform_data/mmc-esdhc-imx.h>
|
#include <linux/platform_data/mmc-esdhc-imx.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
#include "sdhci-pltfm.h"
|
#include "sdhci-pltfm.h"
|
||||||
#include "sdhci-esdhc.h"
|
#include "sdhci-esdhc.h"
|
||||||
|
|
||||||
|
@ -45,6 +46,8 @@
|
||||||
#define ESDHC_MIX_CTRL_FBCLK_SEL (1 << 25)
|
#define ESDHC_MIX_CTRL_FBCLK_SEL (1 << 25)
|
||||||
/* Bits 3 and 6 are not SDHCI standard definitions */
|
/* Bits 3 and 6 are not SDHCI standard definitions */
|
||||||
#define ESDHC_MIX_CTRL_SDHCI_MASK 0xb7
|
#define ESDHC_MIX_CTRL_SDHCI_MASK 0xb7
|
||||||
|
/* Tuning bits */
|
||||||
|
#define ESDHC_MIX_CTRL_TUNING_MASK 0x03c00000
|
||||||
|
|
||||||
/* dll control register */
|
/* dll control register */
|
||||||
#define ESDHC_DLL_CTRL 0x60
|
#define ESDHC_DLL_CTRL 0x60
|
||||||
|
@ -385,6 +388,22 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
|
||||||
return ret;
|
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);
|
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) {
|
} else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
|
||||||
u32 v = readl(host->ioaddr + SDHCI_ACMD12_ERR);
|
u32 v = readl(host->ioaddr + SDHCI_ACMD12_ERR);
|
||||||
u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
||||||
new_val = readl(host->ioaddr + ESDHC_TUNING_CTRL);
|
if (val & SDHCI_CTRL_TUNED_CLK) {
|
||||||
if (val & SDHCI_CTRL_EXEC_TUNING) {
|
v |= ESDHC_MIX_CTRL_SMPCLK_SEL;
|
||||||
new_val |= ESDHC_STD_TUNING_EN |
|
|
||||||
ESDHC_TUNING_START_TAP;
|
|
||||||
v |= ESDHC_MIX_CTRL_EXE_TUNE;
|
|
||||||
m |= ESDHC_MIX_CTRL_FBCLK_SEL;
|
|
||||||
} else {
|
} else {
|
||||||
new_val &= ~ESDHC_STD_TUNING_EN;
|
v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
|
||||||
v &= ~ESDHC_MIX_CTRL_EXE_TUNE;
|
|
||||||
m &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
|
m &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (val & SDHCI_CTRL_TUNED_CLK)
|
if (val & SDHCI_CTRL_EXEC_TUNING) {
|
||||||
v |= ESDHC_MIX_CTRL_SMPCLK_SEL;
|
v |= ESDHC_MIX_CTRL_EXE_TUNE;
|
||||||
else
|
m |= ESDHC_MIX_CTRL_FBCLK_SEL;
|
||||||
v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
|
} else {
|
||||||
|
v &= ~ESDHC_MIX_CTRL_EXE_TUNE;
|
||||||
|
}
|
||||||
|
|
||||||
writel(new_val, host->ioaddr + ESDHC_TUNING_CTRL);
|
|
||||||
writel(v, host->ioaddr + SDHCI_ACMD12_ERR);
|
writel(v, host->ioaddr + SDHCI_ACMD12_ERR);
|
||||||
writel(m, host->ioaddr + ESDHC_MIX_CTRL);
|
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.
|
* Do it manually here.
|
||||||
*/
|
*/
|
||||||
if (esdhc_is_usdhc(imx_data)) {
|
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;
|
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 pltfm_imx_data *imx_data = pltfm_host->priv;
|
||||||
struct esdhc_platform_data *boarddata = &imx_data->boarddata;
|
struct esdhc_platform_data *boarddata = &imx_data->boarddata;
|
||||||
|
|
||||||
u32 f_host = clk_get_rate(pltfm_host->clk);
|
if (boarddata->f_max && (boarddata->f_max < pltfm_host->clock))
|
||||||
|
|
||||||
if (boarddata->f_max && (boarddata->f_max < f_host))
|
|
||||||
return boarddata->f_max;
|
return boarddata->f_max;
|
||||||
else
|
else
|
||||||
return f_host;
|
return pltfm_host->clock;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host)
|
static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host)
|
||||||
{
|
{
|
||||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(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,
|
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 sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
struct pltfm_imx_data *imx_data = pltfm_host->priv;
|
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 pre_div = 2;
|
||||||
int div = 1;
|
int div = 1;
|
||||||
u32 temp, val;
|
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 */
|
/* FIXME: delay a bit for card to be ready for next tuning due to errors */
|
||||||
mdelay(1);
|
mdelay(1);
|
||||||
|
|
||||||
|
pm_runtime_get_sync(host->mmc->parent);
|
||||||
reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
||||||
reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL |
|
reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL |
|
||||||
ESDHC_MIX_CTRL_FBCLK_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)
|
static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode)
|
||||||
{
|
{
|
||||||
struct mmc_command cmd = {0};
|
struct mmc_command cmd = {0};
|
||||||
struct mmc_request mrq = {0};
|
struct mmc_request mrq = {NULL};
|
||||||
struct mmc_data data = {0};
|
struct mmc_data data = {0};
|
||||||
struct scatterlist sg;
|
struct scatterlist sg;
|
||||||
char tuning_pattern[ESDHC_TUNING_BLOCK_PATTERN_LEN];
|
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;
|
pinctrl = imx_data->pins_100mhz;
|
||||||
break;
|
break;
|
||||||
case MMC_TIMING_UHS_SDR104:
|
case MMC_TIMING_UHS_SDR104:
|
||||||
|
case MMC_TIMING_MMC_HS200:
|
||||||
pinctrl = imx_data->pins_200mhz;
|
pinctrl = imx_data->pins_200mhz;
|
||||||
break;
|
break;
|
||||||
default:
|
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;
|
imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR50;
|
||||||
break;
|
break;
|
||||||
case MMC_TIMING_UHS_SDR104:
|
case MMC_TIMING_UHS_SDR104:
|
||||||
|
case MMC_TIMING_MMC_HS200:
|
||||||
imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR104;
|
imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR104;
|
||||||
break;
|
break;
|
||||||
case MMC_TIMING_UHS_DDR50:
|
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->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_per);
|
||||||
clk_prepare_enable(imx_data->clk_ipg);
|
clk_prepare_enable(imx_data->clk_ipg);
|
||||||
clk_prepare_enable(imx_data->clk_ahb);
|
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)) {
|
if (esdhc_is_usdhc(imx_data)) {
|
||||||
writel(0x08100810, host->ioaddr + ESDHC_WTMK_LVL);
|
writel(0x08100810, host->ioaddr + ESDHC_WTMK_LVL);
|
||||||
host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
|
host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
|
||||||
|
host->mmc->caps |= MMC_CAP_1_8V_DDR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
|
if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
|
||||||
sdhci_esdhc_ops.platform_execute_tuning =
|
sdhci_esdhc_ops.platform_execute_tuning =
|
||||||
esdhc_executing_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;
|
boarddata = &imx_data->boarddata;
|
||||||
if (sdhci_esdhc_imx_probe_dt(pdev, boarddata) < 0) {
|
if (sdhci_esdhc_imx_probe_dt(pdev, boarddata) < 0) {
|
||||||
if (!host->mmc->parent->platform_data) {
|
if (!host->mmc->parent->platform_data) {
|
||||||
|
@ -1053,7 +1079,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ESDHC_CD_PERMANENT:
|
case ESDHC_CD_PERMANENT:
|
||||||
host->mmc->caps = MMC_CAP_NONREMOVABLE;
|
host->mmc->caps |= MMC_CAP_NONREMOVABLE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ESDHC_CD_NONE:
|
case ESDHC_CD_NONE:
|
||||||
|
@ -1094,6 +1120,12 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
||||||
if (err)
|
if (err)
|
||||||
goto disable_clk;
|
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;
|
return 0;
|
||||||
|
|
||||||
disable_clk:
|
disable_clk:
|
||||||
|
@ -1114,21 +1146,63 @@ static int sdhci_esdhc_imx_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
sdhci_remove_host(host, dead);
|
sdhci_remove_host(host, dead);
|
||||||
|
|
||||||
clk_disable_unprepare(imx_data->clk_per);
|
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
||||||
clk_disable_unprepare(imx_data->clk_ipg);
|
pm_runtime_disable(&pdev->dev);
|
||||||
clk_disable_unprepare(imx_data->clk_ahb);
|
|
||||||
|
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);
|
sdhci_pltfm_free(pdev);
|
||||||
|
|
||||||
return 0;
|
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 = {
|
static struct platform_driver sdhci_esdhc_imx_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-esdhc-imx",
|
.name = "sdhci-esdhc-imx",
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.of_match_table = imx_esdhc_dt_ids,
|
.of_match_table = imx_esdhc_dt_ids,
|
||||||
.pm = SDHCI_PLTFM_PMOPS,
|
.pm = &sdhci_esdhc_pmops,
|
||||||
},
|
},
|
||||||
.id_table = imx_esdhc_devtype,
|
.id_table = imx_esdhc_devtype,
|
||||||
.probe = sdhci_esdhc_imx_probe,
|
.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 <linux/mmc/sdhci-pci-data.h>
|
||||||
|
|
||||||
#include "sdhci.h"
|
#include "sdhci.h"
|
||||||
|
#include "sdhci-pci.h"
|
||||||
/*
|
#include "sdhci-pci-o2micro.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 */
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************\
|
/*****************************************************************************\
|
||||||
* *
|
* *
|
||||||
|
@ -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 = {
|
static const struct sdhci_pci_fixes sdhci_intel_mfd_sd = {
|
||||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||||
.allow_runtime_pm = true,
|
.allow_runtime_pm = true,
|
||||||
|
.own_cd_for_runtime_pm = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct sdhci_pci_fixes sdhci_intel_mfd_sdio = {
|
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 = {
|
static const struct sdhci_pci_fixes sdhci_intel_byt_sd = {
|
||||||
.quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON,
|
.quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON,
|
||||||
.allow_runtime_pm = true,
|
.allow_runtime_pm = true,
|
||||||
|
.own_cd_for_runtime_pm = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Define Host controllers for Intel Merrifield platform */
|
/* 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 = {
|
static const struct sdhci_pci_fixes sdhci_intel_mrfl_mmc = {
|
||||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||||
|
.quirks2 = SDHCI_QUIRK2_BROKEN_HS200,
|
||||||
.probe_slot = intel_mrfl_mmc_probe_slot,
|
.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_ADMA2 0xE7
|
||||||
#define O2_SD_INF_MOD 0xF1
|
#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)
|
static int jmicron_pmos(struct sdhci_pci_chip *chip, int on)
|
||||||
{
|
{
|
||||||
u8 scratch;
|
u8 scratch;
|
||||||
|
@ -642,7 +515,10 @@ static int jmicron_resume(struct sdhci_pci_chip *chip)
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct sdhci_pci_fixes sdhci_o2 = {
|
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 = {
|
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,
|
.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 */
|
{ /* Generic SD host controller */
|
||||||
PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00)
|
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);
|
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;
|
return slot;
|
||||||
|
|
||||||
remove:
|
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);
|
EXPORT_SYMBOL_GPL(sdhci_pltfm_unregister);
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#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);
|
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||||
|
|
||||||
return sdhci_suspend_host(host);
|
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);
|
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||||
|
|
||||||
return sdhci_resume_host(host);
|
return sdhci_resume_host(host);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(sdhci_pltfm_resume);
|
||||||
|
|
||||||
const struct dev_pm_ops sdhci_pltfm_pmops = {
|
const struct dev_pm_ops sdhci_pltfm_pmops = {
|
||||||
.suspend = sdhci_pltfm_suspend,
|
.suspend = sdhci_pltfm_suspend,
|
||||||
|
|
|
@ -111,6 +111,8 @@ static inline void *sdhci_pltfm_priv(struct sdhci_pltfm_host *host)
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#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;
|
extern const struct dev_pm_ops sdhci_pltfm_pmops;
|
||||||
#define SDHCI_PLTFM_PMOPS (&sdhci_pltfm_pmops)
|
#define SDHCI_PLTFM_PMOPS (&sdhci_pltfm_pmops)
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -198,6 +198,7 @@ static struct sdhci_tegra_soc_data soc_data_tegra114 = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id sdhci_tegra_dt_match[] = {
|
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,tegra114-sdhci", .data = &soc_data_tegra114 },
|
||||||
{ .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 },
|
{ .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 },
|
||||||
{ .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 },
|
{ .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 },
|
||||||
|
|
|
@ -898,8 +898,13 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
|
||||||
u16 mode;
|
u16 mode;
|
||||||
struct mmc_data *data = cmd->data;
|
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;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
WARN_ON(!host->data);
|
WARN_ON(!host->data);
|
||||||
|
|
||||||
|
@ -1013,7 +1018,12 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
|
||||||
mdelay(1);
|
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;
|
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->card->type == MMC_TYPE_MMC ?
|
||||||
MMC_SEND_TUNING_BLOCK_HS200 :
|
MMC_SEND_TUNING_BLOCK_HS200 :
|
||||||
MMC_SEND_TUNING_BLOCK;
|
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);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
sdhci_execute_tuning(mmc, tuning_opcode);
|
sdhci_execute_tuning(mmc, tuning_opcode);
|
||||||
spin_lock_irqsave(&host->lock, flags);
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
|
@ -1845,12 +1862,12 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
||||||
unsigned long timeout;
|
unsigned long timeout;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
bool requires_tuning_nonuhs = false;
|
bool requires_tuning_nonuhs = false;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
host = mmc_priv(mmc);
|
host = mmc_priv(mmc);
|
||||||
|
|
||||||
sdhci_runtime_pm_get(host);
|
sdhci_runtime_pm_get(host);
|
||||||
disable_irq(host->irq);
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
spin_lock(&host->lock);
|
|
||||||
|
|
||||||
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
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)
|
requires_tuning_nonuhs)
|
||||||
ctrl |= SDHCI_CTRL_EXEC_TUNING;
|
ctrl |= SDHCI_CTRL_EXEC_TUNING;
|
||||||
else {
|
else {
|
||||||
spin_unlock(&host->lock);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
enable_irq(host->irq);
|
|
||||||
sdhci_runtime_pm_put(host);
|
sdhci_runtime_pm_put(host);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (host->ops->platform_execute_tuning) {
|
if (host->ops->platform_execute_tuning) {
|
||||||
spin_unlock(&host->lock);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
enable_irq(host->irq);
|
|
||||||
err = host->ops->platform_execute_tuning(host, opcode);
|
err = host->ops->platform_execute_tuning(host, opcode);
|
||||||
sdhci_runtime_pm_put(host);
|
sdhci_runtime_pm_put(host);
|
||||||
return err;
|
return err;
|
||||||
|
@ -1951,15 +1966,12 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
||||||
host->cmd = NULL;
|
host->cmd = NULL;
|
||||||
host->mrq = NULL;
|
host->mrq = NULL;
|
||||||
|
|
||||||
spin_unlock(&host->lock);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
enable_irq(host->irq);
|
|
||||||
|
|
||||||
/* Wait for Buffer Read Ready interrupt */
|
/* Wait for Buffer Read Ready interrupt */
|
||||||
wait_event_interruptible_timeout(host->buf_ready_int,
|
wait_event_interruptible_timeout(host->buf_ready_int,
|
||||||
(host->tuning_done == 1),
|
(host->tuning_done == 1),
|
||||||
msecs_to_jiffies(50));
|
msecs_to_jiffies(50));
|
||||||
disable_irq(host->irq);
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
spin_lock(&host->lock);
|
|
||||||
|
|
||||||
if (!host->tuning_done) {
|
if (!host->tuning_done) {
|
||||||
pr_info(DRIVER_NAME ": Timeout waiting for "
|
pr_info(DRIVER_NAME ": Timeout waiting for "
|
||||||
|
@ -2034,8 +2046,7 @@ out:
|
||||||
err = 0;
|
err = 0;
|
||||||
|
|
||||||
sdhci_clear_set_irqs(host, SDHCI_INT_DATA_AVAIL, ier);
|
sdhci_clear_set_irqs(host, SDHCI_INT_DATA_AVAIL, ier);
|
||||||
spin_unlock(&host->lock);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
enable_irq(host->irq);
|
|
||||||
sdhci_runtime_pm_put(host);
|
sdhci_runtime_pm_put(host);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
@ -3004,7 +3015,8 @@ int sdhci_add_host(struct sdhci_host *host)
|
||||||
/* SD3.0: SDR104 is supported so (for eMMC) the caps2
|
/* SD3.0: SDR104 is supported so (for eMMC) the caps2
|
||||||
* field can be promoted to support HS200.
|
* 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)
|
} else if (caps[1] & SDHCI_SUPPORT_SDR50)
|
||||||
mmc->caps |= MMC_CAP_UHS_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);
|
desc, cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sh_mmcif_request_dma(struct sh_mmcif_host *host,
|
static struct dma_chan *
|
||||||
struct sh_mmcif_plat_data *pdata)
|
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_slave_config cfg;
|
||||||
|
struct dma_chan *chan;
|
||||||
|
unsigned int slave_id;
|
||||||
|
struct resource *res;
|
||||||
dma_cap_mask_t mask;
|
dma_cap_mask_t mask;
|
||||||
int ret;
|
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;
|
host->dma_active = false;
|
||||||
|
|
||||||
if (pdata) {
|
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 */
|
/* We can only either use DMA for both Tx and Rx or not use it at all */
|
||||||
dma_cap_zero(mask);
|
host->chan_tx = sh_mmcif_request_dma_one(host, pdata, DMA_MEM_TO_DEV);
|
||||||
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);
|
|
||||||
|
|
||||||
if (!host->chan_tx)
|
if (!host->chan_tx)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* In the OF case the driver will get the slave ID from the DT */
|
host->chan_rx = sh_mmcif_request_dma_one(host, pdata, DMA_DEV_TO_MEM);
|
||||||
if (pdata)
|
if (!host->chan_rx) {
|
||||||
cfg.slave_id = pdata->slave_id_tx;
|
dma_release_channel(host->chan_tx);
|
||||||
cfg.direction = DMA_MEM_TO_DEV;
|
host->chan_tx = NULL;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sh_mmcif_release_dma(struct sh_mmcif_host *host)
|
static void sh_mmcif_release_dma(struct sh_mmcif_host *host)
|
||||||
|
|
|
@ -33,6 +33,8 @@
|
||||||
|
|
||||||
#include "tmio_mmc.h"
|
#include "tmio_mmc.h"
|
||||||
|
|
||||||
|
#define EXT_ACC 0xe4
|
||||||
|
|
||||||
struct sh_mobile_sdhi_of_data {
|
struct sh_mobile_sdhi_of_data {
|
||||||
unsigned long tmio_flags;
|
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 mmc_host *mmc = platform_get_drvdata(pdev);
|
||||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||||
struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data);
|
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)
|
if (ret < 0)
|
||||||
return ret;
|
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 mmc_host *mmc = platform_get_drvdata(pdev);
|
||||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||||
struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data);
|
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)
|
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 tmio_mmc_data *mmc_data;
|
||||||
struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
|
struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
|
||||||
struct tmio_mmc_host *host;
|
struct tmio_mmc_host *host;
|
||||||
|
struct resource *res;
|
||||||
int irq, ret, i = 0;
|
int irq, ret, i = 0;
|
||||||
bool multiplexed_isr = true;
|
bool multiplexed_isr = true;
|
||||||
struct tmio_mmc_dma *dma_priv;
|
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);
|
priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_mobile_sdhi), GFP_KERNEL);
|
||||||
if (priv == NULL) {
|
if (priv == NULL) {
|
||||||
|
@ -206,10 +214,21 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
|
||||||
mmc_data->flags |= of_data->tmio_flags;
|
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);
|
ret = tmio_mmc_host_probe(&host, pdev, mmc_data);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto eprobe;
|
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
|
* Allow one or more specific (named) ISRs or
|
||||||
* one or more multiplexed (un-named) ISRs.
|
* 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);
|
const struct mfd_cell *cell = mfd_get_cell(pdev);
|
||||||
struct tmio_mmc_data *pdata;
|
struct tmio_mmc_data *pdata;
|
||||||
struct tmio_mmc_host *host;
|
struct tmio_mmc_host *host;
|
||||||
|
struct resource *res;
|
||||||
int ret = -EINVAL, irq;
|
int ret = -EINVAL, irq;
|
||||||
|
|
||||||
if (pdev->num_resources != 2)
|
if (pdev->num_resources != 2)
|
||||||
|
@ -84,6 +85,14 @@ static int tmio_mmc_probe(struct platform_device *pdev)
|
||||||
goto out;
|
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);
|
ret = tmio_mmc_host_probe(&host, pdev, pdata);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto cell_disable;
|
goto cell_disable;
|
||||||
|
|
|
@ -58,7 +58,6 @@ enum tmio_mmc_power {
|
||||||
|
|
||||||
struct tmio_mmc_host {
|
struct tmio_mmc_host {
|
||||||
void __iomem *ctl;
|
void __iomem *ctl;
|
||||||
unsigned long bus_shift;
|
|
||||||
struct mmc_command *cmd;
|
struct mmc_command *cmd;
|
||||||
struct mmc_request *mrq;
|
struct mmc_request *mrq;
|
||||||
struct mmc_data *data;
|
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)
|
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,
|
static inline void sd_ctrl_read16_rep(struct tmio_mmc_host *host, int addr,
|
||||||
u16 *buf, int count)
|
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)
|
static inline u32 sd_ctrl_read32(struct tmio_mmc_host *host, int addr)
|
||||||
{
|
{
|
||||||
return readw(host->ctl + (addr << host->bus_shift)) |
|
return readw(host->ctl + (addr << host->pdata->bus_shift)) |
|
||||||
readw(host->ctl + ((addr + 2) << host->bus_shift)) << 16;
|
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)
|
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))
|
if (host->pdata->write16_hook && host->pdata->write16_hook(host, addr))
|
||||||
return;
|
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,
|
static inline void sd_ctrl_write16_rep(struct tmio_mmc_host *host, int addr,
|
||||||
u16 *buf, int count)
|
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)
|
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, host->ctl + (addr << host->pdata->bus_shift));
|
||||||
writew(val >> 16, host->ctl + ((addr + 2) << host->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)
|
if (pdata->dma->chan_priv_tx)
|
||||||
cfg.slave_id = pdata->dma->slave_id_tx;
|
cfg.slave_id = pdata->dma->slave_id_tx;
|
||||||
cfg.direction = DMA_MEM_TO_DEV;
|
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;
|
cfg.src_addr = 0;
|
||||||
ret = dmaengine_slave_config(host->chan_tx, &cfg);
|
ret = dmaengine_slave_config(host->chan_tx, &cfg);
|
||||||
if (ret < 0)
|
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)
|
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) */
|
/* 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);
|
sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0000);
|
||||||
msleep(10);
|
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)
|
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_write16(host, CTL_SD_CARD_CLK_CTL, 0x0100 |
|
||||||
sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
|
sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
|
||||||
msleep(10);
|
msleep(10);
|
||||||
|
|
||||||
/* implicit BUG_ON(!res) */
|
/* 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);
|
sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0100);
|
||||||
msleep(10);
|
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)
|
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 */
|
/* FIXME - should we set stop clock reg here */
|
||||||
sd_ctrl_write16(host, CTL_RESET_SD, 0x0000);
|
sd_ctrl_write16(host, CTL_RESET_SD, 0x0000);
|
||||||
/* implicit BUG_ON(!res) */
|
/* 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);
|
sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0000);
|
||||||
msleep(10);
|
msleep(10);
|
||||||
sd_ctrl_write16(host, CTL_RESET_SD, 0x0001);
|
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);
|
sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0001);
|
||||||
msleep(10);
|
msleep(10);
|
||||||
}
|
}
|
||||||
|
@ -944,17 +938,25 @@ static const struct mmc_host_ops tmio_mmc_ops = {
|
||||||
.enable_sdio_irq = tmio_mmc_enable_sdio_irq,
|
.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 tmio_mmc_data *pdata = host->pdata;
|
||||||
struct mmc_host *mmc = host->mmc;
|
struct mmc_host *mmc = host->mmc;
|
||||||
|
|
||||||
mmc_regulator_get_supply(mmc);
|
mmc_regulator_get_supply(mmc);
|
||||||
|
|
||||||
|
/* use ocr_mask if no regulator */
|
||||||
if (!mmc->ocr_avail)
|
if (!mmc->ocr_avail)
|
||||||
mmc->ocr_avail = pdata->ocr_mask ? : MMC_VDD_32_33 | MMC_VDD_33_34;
|
mmc->ocr_avail = pdata->ocr_mask;
|
||||||
else if (pdata->ocr_mask)
|
|
||||||
dev_warn(mmc_dev(mmc), "Platform OCR mask is ignored\n");
|
/*
|
||||||
|
* 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,
|
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_pwr = pdata->set_pwr;
|
||||||
_host->set_clk_div = pdata->set_clk_div;
|
_host->set_clk_div = pdata->set_clk_div;
|
||||||
|
|
||||||
/* SD control register space size is 0x200, 0x400 for bus_shift=1 */
|
ret = tmio_mmc_init_ocr(_host);
|
||||||
_host->bus_shift = resource_size(res_ctl) >> 10;
|
if (ret < 0)
|
||||||
|
goto host_free;
|
||||||
|
|
||||||
_host->ctl = ioremap(res_ctl->start, resource_size(res_ctl));
|
_host->ctl = ioremap(res_ctl->start, resource_size(res_ctl));
|
||||||
if (!_host->ctl) {
|
if (!_host->ctl) {
|
||||||
|
@ -1016,14 +1019,13 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host,
|
||||||
|
|
||||||
mmc->ops = &tmio_mmc_ops;
|
mmc->ops = &tmio_mmc_ops;
|
||||||
mmc->caps |= MMC_CAP_4_BIT_DATA | pdata->capabilities;
|
mmc->caps |= MMC_CAP_4_BIT_DATA | pdata->capabilities;
|
||||||
mmc->caps2 = pdata->capabilities2;
|
mmc->caps2 |= pdata->capabilities2;
|
||||||
mmc->max_segs = 32;
|
mmc->max_segs = 32;
|
||||||
mmc->max_blk_size = 512;
|
mmc->max_blk_size = 512;
|
||||||
mmc->max_blk_count = (PAGE_CACHE_SIZE / mmc->max_blk_size) *
|
mmc->max_blk_count = (PAGE_CACHE_SIZE / mmc->max_blk_size) *
|
||||||
mmc->max_segs;
|
mmc->max_segs;
|
||||||
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
|
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
|
||||||
mmc->max_seg_size = mmc->max_req_size;
|
mmc->max_seg_size = mmc->max_req_size;
|
||||||
tmio_mmc_init_ocr(_host);
|
|
||||||
|
|
||||||
_host->native_hotplug = !(pdata->flags & TMIO_MMC_USE_GPIO_CD ||
|
_host->native_hotplug = !(pdata->flags & TMIO_MMC_USE_GPIO_CD ||
|
||||||
mmc->caps & MMC_CAP_NEEDS_POLL ||
|
mmc->caps & MMC_CAP_NEEDS_POLL ||
|
||||||
|
|
|
@ -76,6 +76,13 @@
|
||||||
*/
|
*/
|
||||||
#define TMIO_MMC_USE_GPIO_CD (1 << 5)
|
#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_enable(void __iomem *cnf, int shift, unsigned long base);
|
||||||
int tmio_core_mmc_resume(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);
|
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 capabilities;
|
||||||
unsigned long capabilities2;
|
unsigned long capabilities2;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
unsigned long bus_shift;
|
||||||
u32 ocr_mask; /* available voltages */
|
u32 ocr_mask; /* available voltages */
|
||||||
struct tmio_mmc_dma *dma;
|
struct tmio_mmc_dma *dma;
|
||||||
struct device *dev;
|
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_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_BLK_NO_CMD23 (1<<7) /* Avoid CMD23 for regular multiblock */
|
||||||
#define MMC_QUIRK_BROKEN_BYTE_MODE_512 (1<<8) /* Avoid sending 512 bytes in */
|
#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_LONG_READ_TIME (1<<9) /* Data read time > CSD says */
|
||||||
#define MMC_QUIRK_SEC_ERASE_TRIM_BROKEN (1<<10) /* Skip secure for erase/trim */
|
#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_size; /* erase size in sectors */
|
||||||
unsigned int erase_shift; /* if erase unit is power 2 */
|
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;
|
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_name(c) ((c)->cid.prod_name)
|
||||||
#define mmc_card_id(c) (dev_name(&(c)->dev))
|
#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)
|
#define SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON (1<<4)
|
||||||
/* Controller has a non-standard host control register */
|
/* Controller has a non-standard host control register */
|
||||||
#define SDHCI_QUIRK2_BROKEN_HOST_CONTROL (1<<5)
|
#define SDHCI_QUIRK2_BROKEN_HOST_CONTROL (1<<5)
|
||||||
|
/* Controller does not support HS200 */
|
||||||
|
#define SDHCI_QUIRK2_BROKEN_HS200 (1<<6)
|
||||||
|
|
||||||
int irq; /* Device IRQ */
|
int irq; /* Device IRQ */
|
||||||
void __iomem *ioaddr; /* Mapped address */
|
void __iomem *ioaddr; /* Mapped address */
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#define CTL_SDIO_IRQ_MASK 0x38
|
#define CTL_SDIO_IRQ_MASK 0x38
|
||||||
#define CTL_DMA_ENABLE 0xd8
|
#define CTL_DMA_ENABLE 0xd8
|
||||||
#define CTL_RESET_SD 0xe0
|
#define CTL_RESET_SD 0xe0
|
||||||
|
#define CTL_VERSION 0xe2
|
||||||
#define CTL_SDIO_REGS 0x100
|
#define CTL_SDIO_REGS 0x100
|
||||||
#define CTL_CLK_AND_WAIT_CTL 0x138
|
#define CTL_CLK_AND_WAIT_CTL 0x138
|
||||||
#define CTL_RESET_SDIO 0x1e0
|
#define CTL_RESET_SDIO 0x1e0
|
||||||
|
|
Loading…
Reference in New Issue