MMC core:
- Support for MMC power sequences. - SDIO function devicetree subnode parsing. - Refactor the hardware reset routines and enable it for SD cards. - Various code quality improvements, especially for slot-gpio. MMC host: - dw_mmc: Various fixes and cleanups. - dw_mmc: Convert to mmc_send_tuning(). - moxart: Fix probe logic. - sdhci: Various fixes and cleanups - sdhci: Asynchronous request handling support. - sdhci-pxav3: Various fixes and cleanups. - sdhci-tegra: Fixes for T114, T124 and T132. - rtsx: Various fixes and cleanups. - rtsx: Support for SDIO. - sdhi/tmio: Refactor and cleanup of header files. - omap_hsmmc: Use slot-gpio and common MMC DT parser. - Make all hosts to deal with errors from mmc_of_parse(). - sunxi: Various fixes and cleanups. - sdhci: Support for Fujitsu SDHCI controller f_sdh30. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJU2b9MAAoJEP4mhCVzWIwp678P/2Hjoo17FDnCQT2qXCRWmMmx 98n7mrkPw20cVm6dlXyVxHFxrgRWan1eATiu1vBdnNmXkeUmThMbuGpATDi40fIT C2g9wPDM1/naJ+Qg8mPGy0vEDQYHEzxHHlAyfOaeXdhxhll1iHqhk+Jb6cFQN5DP /CvNmuL/7m9uuFhHlGJnqSNMyenLAFFXthIiVJrQeZeYq9NZ1ZZfW7+esHDmu2lP EFkrZf+xYFmFWAqccyTR58QZsYKlDv4NS/0UMU941DkO7x7R8ZsQG8xFu9bIN5Wn EJfgP7EfEXHlD5a1/QQ918IT1ifxhPGiCbBXpdfAUt7Xte6zYyASpTyAm8v7vT2I 2hot1T1BZgADALE2EHAP4kzK49ipfhQmlVZgFeYVsTpPKk8Nvczio7Y3LYlzNmBo V0jaTUTtU7u7ICtGbo7OqOybW/Sm5E00xsq22txIXObURa7bPbZ4CnxJpstSaU2Z nweZaa79HaHZE7xyUNh9kAbxfGC0pOT0oPoPYcTxcpk2vva+atULEYnLEHUULrgs D4+m8tnbuwoZoGanlMKqgPXP8Xkau/meEdz4WaYrXQEIafrVIR2/kcXGQjhD8ucO VkjUaZDKxNXTkwOzM/siOxJwj75Ka6GDHM7JGx4F30QHqgRTtg2wzInU9nsViuiA 02698dNk9CdP3JirDtbm =ojsj -----END PGP SIGNATURE----- Merge tag 'mmc-v3.20-1' of git://git.linaro.org/people/ulf.hansson/mmc Pull MMC updates from Ulf Hansson: "MMC core: - Support for MMC power sequences. - SDIO function devicetree subnode parsing. - Refactor the hardware reset routines and enable it for SD cards. - Various code quality improvements, especially for slot-gpio. MMC host: - dw_mmc: Various fixes and cleanups. - dw_mmc: Convert to mmc_send_tuning(). - moxart: Fix probe logic. - sdhci: Various fixes and cleanups - sdhci: Asynchronous request handling support. - sdhci-pxav3: Various fixes and cleanups. - sdhci-tegra: Fixes for T114, T124 and T132. - rtsx: Various fixes and cleanups. - rtsx: Support for SDIO. - sdhi/tmio: Refactor and cleanup of header files. - omap_hsmmc: Use slot-gpio and common MMC DT parser. - Make all hosts to deal with errors from mmc_of_parse(). - sunxi: Various fixes and cleanups. - sdhci: Support for Fujitsu SDHCI controller f_sdh30" * tag 'mmc-v3.20-1' of git://git.linaro.org/people/ulf.hansson/mmc: (117 commits) mmc: sdhci-s3c: solve problem with sleeping in atomic context mmc: pwrseq: add driver for emmc hardware reset mmc: moxart: fix probe logic mmc: core: Invoke mmc_pwrseq_post_power_on() prior MMC_POWER_ON state mmc: pwrseq_simple: Add optional reference clock support mmc: pwrseq: Document optional clock for the simple power sequence mmc: pwrseq_simple: Extend to support more pins mmc: pwrseq: Document that simple sequence support more than one GPIO mmc: Add hardware dependencies for sdhci-pxav3 and sdhci-pxav2 mmc: sdhci-pxav3: Modify clock settings for the SDR50 and DDR50 modes mmc: sdhci-pxav3: Extend binding with SDIO3 conf reg for the Armada 38x mmc: sdhci-pxav3: Fix Armada 38x controller's caps according to erratum ERR-7878951 mmc: sdhci-pxav3: Fix SDR50 and DDR50 capabilities for the Armada 38x flavor mmc: sdhci: switch voltage before sdhci_set_ios in runtime resume mmc: tegra: Write xfer_mode, CMD regs in together mmc: Resolve BKOPS compatability issue mmc: sdhci-pxav3: fix setting of pdata->clk_delay_cycles mmc: dw_mmc: rockchip: remove incorrect __exit_p() mmc: dw_mmc: exynos: remove incorrect __exit_p() mmc: Fix menuconfig alignment of MMC_SDHCI_* options ...
This commit is contained in:
commit
aa7ed01f93
25
Documentation/devicetree/bindings/mmc/mmc-pwrseq-emmc.txt
Normal file
25
Documentation/devicetree/bindings/mmc/mmc-pwrseq-emmc.txt
Normal file
@ -0,0 +1,25 @@
|
||||
* The simple eMMC hardware reset provider
|
||||
|
||||
The purpose of this driver is to perform standard eMMC hw reset
|
||||
procedure, as descibed by Jedec 4.4 specification. This procedure is
|
||||
performed just after MMC core enabled power to the given mmc host (to
|
||||
fix possible issues if bootloader has left eMMC card in initialized or
|
||||
unknown state), and before performing complete system reboot (also in
|
||||
case of emergency reboot call). The latter is needed on boards, which
|
||||
doesn't have hardware reset logic connected to emmc card and (limited or
|
||||
broken) ROM bootloaders are unable to read second stage from the emmc
|
||||
card if the card is left in unknown or already initialized state.
|
||||
|
||||
Required properties:
|
||||
- compatible : contains "mmc-pwrseq-emmc".
|
||||
- reset-gpios : contains a GPIO specifier. The reset GPIO is asserted
|
||||
and then deasserted to perform eMMC card reset. To perform
|
||||
reset procedure as described in Jedec 4.4 specification, the
|
||||
gpio line should be defined as GPIO_ACTIVE_LOW.
|
||||
|
||||
Example:
|
||||
|
||||
sdhci0_pwrseq {
|
||||
compatible = "mmc-pwrseq-emmc";
|
||||
reset-gpios = <&gpio1 12 GPIO_ACTIVE_LOW>;
|
||||
}
|
25
Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt
Normal file
25
Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt
Normal file
@ -0,0 +1,25 @@
|
||||
* The simple MMC power sequence provider
|
||||
|
||||
The purpose of the simple MMC power sequence provider is to supports a set of
|
||||
common properties between various SOC designs. It thus enables us to use the
|
||||
same provider for several SOC designs.
|
||||
|
||||
Required properties:
|
||||
- compatible : contains "mmc-pwrseq-simple".
|
||||
|
||||
Optional properties:
|
||||
- reset-gpios : contains a list of GPIO specifiers. The reset GPIOs are asserted
|
||||
at initialization and prior we start the power up procedure of the card.
|
||||
They will be de-asserted right after the power has been provided to the
|
||||
card.
|
||||
- clocks : Must contain an entry for the entry in clock-names.
|
||||
See ../clocks/clock-bindings.txt for details.
|
||||
- clock-names : Must include the following entry:
|
||||
"ext_clock" (External clock provided to the card).
|
||||
|
||||
Example:
|
||||
|
||||
sdhci0_pwrseq {
|
||||
compatible = "mmc-pwrseq-simple";
|
||||
reset-gpios = <&gpio1 12 0>;
|
||||
}
|
@ -64,7 +64,43 @@ Optional SDIO properties:
|
||||
- keep-power-in-suspend: Preserves card power during a suspend/resume cycle
|
||||
- enable-sdio-wakeup: Enables wake up of host system on SDIO IRQ assertion
|
||||
|
||||
Example:
|
||||
|
||||
MMC power sequences:
|
||||
--------------------
|
||||
|
||||
System on chip designs may specify a specific MMC power sequence. To
|
||||
successfully detect an (e)MMC/SD/SDIO card, that power sequence must be
|
||||
maintained while initializing the card.
|
||||
|
||||
Optional property:
|
||||
- mmc-pwrseq: phandle to the MMC power sequence node. See "mmc-pwrseq-*"
|
||||
for documentation of MMC power sequence bindings.
|
||||
|
||||
|
||||
Use of Function subnodes
|
||||
------------------------
|
||||
|
||||
On embedded systems the cards connected to a host may need additional
|
||||
properties. These can be specified in subnodes to the host controller node.
|
||||
The subnodes are identified by the standard 'reg' property.
|
||||
Which information exactly can be specified depends on the bindings for the
|
||||
SDIO function driver for the subnode, as specified by the compatible string.
|
||||
|
||||
Required host node properties when using function subnodes:
|
||||
- #address-cells: should be one. The cell is the slot id.
|
||||
- #size-cells: should be zero.
|
||||
|
||||
Required function subnode properties:
|
||||
- compatible: name of SDIO function following generic names recommended practice
|
||||
- reg: Must contain the SDIO function number of the function this subnode
|
||||
describes. A value of 0 denotes the memory SD function, values from
|
||||
1 to 7 denote the SDIO functions.
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
Basic example:
|
||||
|
||||
sdhci@ab000000 {
|
||||
compatible = "sdhci";
|
||||
@ -77,4 +113,28 @@ sdhci@ab000000 {
|
||||
max-frequency = <50000000>;
|
||||
keep-power-in-suspend;
|
||||
enable-sdio-wakeup;
|
||||
mmc-pwrseq = <&sdhci0_pwrseq>
|
||||
}
|
||||
|
||||
Example with sdio function subnode:
|
||||
|
||||
mmc3: mmc@01c12000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&mmc3_pins_a>;
|
||||
vmmc-supply = <®_vmmc3>;
|
||||
bus-width = <4>;
|
||||
non-removable;
|
||||
mmc-pwrseq = <&sdhci0_pwrseq>
|
||||
status = "okay";
|
||||
|
||||
brcmf: bcrmf@1 {
|
||||
reg = <1>;
|
||||
compatible = "brcm,bcm43xx-fmac";
|
||||
interrupt-parent = <&pio>;
|
||||
interrupts = <10 8>; /* PH10 / EINT10 */
|
||||
interrupt-names = "host-wake";
|
||||
};
|
||||
};
|
||||
|
30
Documentation/devicetree/bindings/mmc/sdhci-fujitsu.txt
Normal file
30
Documentation/devicetree/bindings/mmc/sdhci-fujitsu.txt
Normal file
@ -0,0 +1,30 @@
|
||||
* Fujitsu SDHCI controller
|
||||
|
||||
This file documents differences between the core properties in mmc.txt
|
||||
and the properties used by the sdhci_f_sdh30 driver.
|
||||
|
||||
Required properties:
|
||||
- compatible: "fujitsu,mb86s70-sdhci-3.0"
|
||||
- clocks: Must contain an entry for each entry in clock-names. It is a
|
||||
list of phandles and clock-specifier pairs.
|
||||
See ../clocks/clock-bindings.txt for details.
|
||||
- clock-names: Should contain the following two entries:
|
||||
"iface" - clock used for sdhci interface
|
||||
"core" - core clock for sdhci controller
|
||||
|
||||
Optional properties:
|
||||
- vqmmc-supply: phandle to the regulator device tree node, mentioned
|
||||
as the VCCQ/VDD_IO supply in the eMMC/SD specs.
|
||||
|
||||
Example:
|
||||
|
||||
sdhci1: mmc@36600000 {
|
||||
compatible = "fujitsu,mb86s70-sdhci-3.0";
|
||||
reg = <0 0x36600000 0x1000>;
|
||||
interrupts = <0 172 0x4>,
|
||||
<0 173 0x4>;
|
||||
bus-width = <4>;
|
||||
vqmmc-supply = <&vccq_sdhci1>;
|
||||
clocks = <&clock 2 2 0>, <&clock 2 3 0>;
|
||||
clock-names = "iface", "core";
|
||||
};
|
@ -9,9 +9,13 @@ Required properties:
|
||||
- reg:
|
||||
* for "mrvl,pxav2-mmc" and "mrvl,pxav3-mmc", one register area for
|
||||
the SDHCI registers.
|
||||
* for "marvell,armada-380-sdhci", two register areas. The first one
|
||||
for the SDHCI registers themselves, and the second one for the
|
||||
AXI/Mbus bridge registers of the SDHCI unit.
|
||||
|
||||
* for "marvell,armada-380-sdhci", three register areas. The first
|
||||
one for the SDHCI registers themselves, the second one for the
|
||||
AXI/Mbus bridge registers of the SDHCI unit, the third one for the
|
||||
SDIO3 Configuration register
|
||||
- reg names: should be "sdhci", "mbus", "conf-sdio3". only mandatory
|
||||
for "marvell,armada-380-sdhci"
|
||||
- clocks: Array of clocks required for SDHCI; requires at least one for
|
||||
I/O clock.
|
||||
- clock-names: Array of names corresponding to clocks property; shall be
|
||||
@ -35,7 +39,10 @@ sdhci@d4280800 {
|
||||
|
||||
sdhci@d8000 {
|
||||
compatible = "marvell,armada-380-sdhci";
|
||||
reg = <0xd8000 0x1000>, <0xdc000 0x100>;
|
||||
reg-names = "sdhci", "mbus", "conf-sdio3";
|
||||
reg = <0xd8000 0x1000>,
|
||||
<0xdc000 0x100>;
|
||||
<0x18454 0x4>;
|
||||
interrupts = <0 25 0x4>;
|
||||
clocks = <&gateclk 17>;
|
||||
clock-names = "io";
|
||||
|
@ -254,12 +254,14 @@ static void pandora_wl1251_init_card(struct mmc_card *card)
|
||||
* We have TI wl1251 attached to MMC3. Pass this information to
|
||||
* SDIO core because it can't be probed by normal methods.
|
||||
*/
|
||||
if (card->type == MMC_TYPE_SDIO || card->type == MMC_TYPE_SD_COMBO) {
|
||||
card->quirks |= MMC_QUIRK_NONSTD_SDIO;
|
||||
card->cccr.wide_bus = 1;
|
||||
card->cis.vendor = 0x104c;
|
||||
card->cis.device = 0x9066;
|
||||
card->cis.blksize = 512;
|
||||
card->cis.max_dtr = 20000000;
|
||||
}
|
||||
}
|
||||
|
||||
static struct omap2_hsmmc_info omap3pandora_mmc[] = {
|
||||
|
@ -2147,7 +2147,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
|
||||
*/
|
||||
|
||||
snprintf(md->disk->disk_name, sizeof(md->disk->disk_name),
|
||||
"mmcblk%d%s", md->name_idx, subname ? subname : "");
|
||||
"mmcblk%u%s", md->name_idx, subname ? subname : "");
|
||||
|
||||
if (mmc_card_mmc(card))
|
||||
blk_queue_logical_block_size(md->queue.queue,
|
||||
@ -2193,7 +2193,6 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
|
||||
static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
|
||||
{
|
||||
sector_t size;
|
||||
struct mmc_blk_data *md;
|
||||
|
||||
if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) {
|
||||
/*
|
||||
@ -2209,9 +2208,8 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
|
||||
size = card->csd.capacity << (card->csd.read_blkbits - 9);
|
||||
}
|
||||
|
||||
md = mmc_blk_alloc_req(card, &card->dev, size, false, NULL,
|
||||
return mmc_blk_alloc_req(card, &card->dev, size, false, NULL,
|
||||
MMC_BLK_DATA_AREA_MAIN);
|
||||
return md;
|
||||
}
|
||||
|
||||
static int mmc_blk_alloc_part(struct mmc_card *card,
|
||||
|
@ -2342,20 +2342,16 @@ static int mmc_test_hw_reset(struct mmc_test_card *test)
|
||||
struct mmc_host *host = card->host;
|
||||
int err;
|
||||
|
||||
err = mmc_hw_reset_check(host);
|
||||
if (!err)
|
||||
return RESULT_OK;
|
||||
|
||||
if (err == -ENOSYS)
|
||||
return RESULT_FAIL;
|
||||
|
||||
if (err != -EOPNOTSUPP)
|
||||
return err;
|
||||
|
||||
if (!mmc_can_reset(card))
|
||||
if (!mmc_card_mmc(card) || !mmc_can_reset(card))
|
||||
return RESULT_UNSUP_CARD;
|
||||
|
||||
err = mmc_hw_reset(host);
|
||||
if (!err)
|
||||
return RESULT_OK;
|
||||
else if (err == -EOPNOTSUPP)
|
||||
return RESULT_UNSUP_HOST;
|
||||
|
||||
return RESULT_FAIL;
|
||||
}
|
||||
|
||||
static const struct mmc_test_case mmc_test_cases[] = {
|
||||
|
@ -8,5 +8,5 @@ mmc_core-y := core.o bus.o host.o \
|
||||
sdio.o sdio_ops.o sdio_bus.o \
|
||||
sdio_cis.o sdio_io.o sdio_irq.o \
|
||||
quirks.o slot-gpio.o
|
||||
|
||||
mmc_core-$(CONFIG_OF) += pwrseq.o pwrseq_simple.o pwrseq_emmc.o
|
||||
mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/mmc/card.h>
|
||||
@ -321,6 +322,8 @@ int mmc_add_card(struct mmc_card *card)
|
||||
#endif
|
||||
mmc_init_context_info(card->host);
|
||||
|
||||
card->dev.of_node = mmc_of_find_child_device(card->host, 0);
|
||||
|
||||
ret = device_add(&card->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -349,6 +352,7 @@ void mmc_remove_card(struct mmc_card *card)
|
||||
mmc_hostname(card->host), card->rca);
|
||||
}
|
||||
device_del(&card->dev);
|
||||
of_node_put(card->dev.of_node);
|
||||
}
|
||||
|
||||
put_device(&card->dev);
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include "bus.h"
|
||||
#include "host.h"
|
||||
#include "sdio_bus.h"
|
||||
#include "pwrseq.h"
|
||||
|
||||
#include "mmc_ops.h"
|
||||
#include "sd_ops.h"
|
||||
@ -185,13 +186,14 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
|
||||
|
||||
EXPORT_SYMBOL(mmc_request_done);
|
||||
|
||||
static void
|
||||
mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
|
||||
static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
|
||||
{
|
||||
#ifdef CONFIG_MMC_DEBUG
|
||||
unsigned int i, sz;
|
||||
struct scatterlist *sg;
|
||||
#endif
|
||||
if (mmc_card_removed(host->card))
|
||||
return -ENOMEDIUM;
|
||||
|
||||
if (mrq->sbc) {
|
||||
pr_debug("<%s: starting CMD%u arg %08x flags %08x>\n",
|
||||
@ -251,6 +253,8 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
|
||||
mmc_host_clk_hold(host);
|
||||
led_trigger_event(host->led, LED_FULL);
|
||||
host->ops->request(host, mrq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -271,7 +275,7 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)
|
||||
|
||||
BUG_ON(!card);
|
||||
|
||||
if (!card->ext_csd.bkops_en || mmc_card_doing_bkops(card))
|
||||
if (!card->ext_csd.man_bkops_en || mmc_card_doing_bkops(card))
|
||||
return;
|
||||
|
||||
err = mmc_read_bkops_status(card);
|
||||
@ -345,29 +349,34 @@ static void mmc_wait_done(struct mmc_request *mrq)
|
||||
*/
|
||||
static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq)
|
||||
{
|
||||
int err;
|
||||
|
||||
mrq->done = mmc_wait_data_done;
|
||||
mrq->host = host;
|
||||
if (mmc_card_removed(host->card)) {
|
||||
mrq->cmd->error = -ENOMEDIUM;
|
||||
mmc_wait_data_done(mrq);
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
mmc_start_request(host, mrq);
|
||||
|
||||
return 0;
|
||||
err = mmc_start_request(host, mrq);
|
||||
if (err) {
|
||||
mrq->cmd->error = err;
|
||||
mmc_wait_data_done(mrq);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
|
||||
{
|
||||
int err;
|
||||
|
||||
init_completion(&mrq->completion);
|
||||
mrq->done = mmc_wait_done;
|
||||
if (mmc_card_removed(host->card)) {
|
||||
mrq->cmd->error = -ENOMEDIUM;
|
||||
|
||||
err = mmc_start_request(host, mrq);
|
||||
if (err) {
|
||||
mrq->cmd->error = err;
|
||||
complete(&mrq->completion);
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
mmc_start_request(host, mrq);
|
||||
return 0;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1077,6 +1086,30 @@ void mmc_set_ungated(struct mmc_host *host)
|
||||
}
|
||||
#endif
|
||||
|
||||
int mmc_execute_tuning(struct mmc_card *card)
|
||||
{
|
||||
struct mmc_host *host = card->host;
|
||||
u32 opcode;
|
||||
int err;
|
||||
|
||||
if (!host->ops->execute_tuning)
|
||||
return 0;
|
||||
|
||||
if (mmc_card_mmc(card))
|
||||
opcode = MMC_SEND_TUNING_BLOCK_HS200;
|
||||
else
|
||||
opcode = MMC_SEND_TUNING_BLOCK;
|
||||
|
||||
mmc_host_clk_hold(host);
|
||||
err = host->ops->execute_tuning(host, opcode);
|
||||
mmc_host_clk_release(host);
|
||||
|
||||
if (err)
|
||||
pr_err("%s: tuning execution failed\n", mmc_hostname(host));
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Change the bus mode (open drain/push-pull) of a host.
|
||||
*/
|
||||
@ -1232,6 +1265,34 @@ EXPORT_SYMBOL(mmc_of_parse_voltage);
|
||||
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
static int mmc_of_get_func_num(struct device_node *node)
|
||||
{
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
ret = of_property_read_u32(node, "reg", ®);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return reg;
|
||||
}
|
||||
|
||||
struct device_node *mmc_of_find_child_device(struct mmc_host *host,
|
||||
unsigned func_num)
|
||||
{
|
||||
struct device_node *node;
|
||||
|
||||
if (!host->parent || !host->parent->of_node)
|
||||
return NULL;
|
||||
|
||||
for_each_child_of_node(host->parent->of_node, node) {
|
||||
if (mmc_of_get_func_num(node) == func_num)
|
||||
return node;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_REGULATOR
|
||||
|
||||
/**
|
||||
@ -1555,6 +1616,8 @@ void mmc_power_up(struct mmc_host *host, u32 ocr)
|
||||
|
||||
mmc_host_clk_hold(host);
|
||||
|
||||
mmc_pwrseq_pre_power_on(host);
|
||||
|
||||
host->ios.vdd = fls(ocr) - 1;
|
||||
host->ios.power_mode = MMC_POWER_UP;
|
||||
/* Set initial state and call mmc_set_ios */
|
||||
@ -1574,6 +1637,8 @@ void mmc_power_up(struct mmc_host *host, u32 ocr)
|
||||
*/
|
||||
mmc_delay(10);
|
||||
|
||||
mmc_pwrseq_post_power_on(host);
|
||||
|
||||
host->ios.clock = host->f_init;
|
||||
|
||||
host->ios.power_mode = MMC_POWER_ON;
|
||||
@ -1595,6 +1660,8 @@ void mmc_power_off(struct mmc_host *host)
|
||||
|
||||
mmc_host_clk_hold(host);
|
||||
|
||||
mmc_pwrseq_power_off(host);
|
||||
|
||||
host->ios.clock = 0;
|
||||
host->ios.vdd = 0;
|
||||
|
||||
@ -2245,67 +2312,28 @@ static void mmc_hw_reset_for_init(struct mmc_host *host)
|
||||
mmc_host_clk_release(host);
|
||||
}
|
||||
|
||||
int mmc_can_reset(struct mmc_card *card)
|
||||
{
|
||||
u8 rst_n_function;
|
||||
|
||||
if (!mmc_card_mmc(card))
|
||||
return 0;
|
||||
rst_n_function = card->ext_csd.rst_n_function;
|
||||
if ((rst_n_function & EXT_CSD_RST_N_EN_MASK) != EXT_CSD_RST_N_ENABLED)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_can_reset);
|
||||
|
||||
static int mmc_do_hw_reset(struct mmc_host *host, int check)
|
||||
{
|
||||
struct mmc_card *card = host->card;
|
||||
|
||||
if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!card)
|
||||
return -EINVAL;
|
||||
|
||||
if (!mmc_can_reset(card))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
mmc_host_clk_hold(host);
|
||||
mmc_set_clock(host, host->f_init);
|
||||
|
||||
host->ops->hw_reset(host);
|
||||
|
||||
/* If the reset has happened, then a status command will fail */
|
||||
if (check) {
|
||||
u32 status;
|
||||
|
||||
if (!mmc_send_status(card, &status)) {
|
||||
mmc_host_clk_release(host);
|
||||
return -ENOSYS;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set initial state and call mmc_set_ios */
|
||||
mmc_set_initial_state(host);
|
||||
|
||||
mmc_host_clk_release(host);
|
||||
|
||||
return host->bus_ops->power_restore(host);
|
||||
}
|
||||
|
||||
int mmc_hw_reset(struct mmc_host *host)
|
||||
{
|
||||
return mmc_do_hw_reset(host, 0);
|
||||
int ret;
|
||||
|
||||
if (!host->card)
|
||||
return -EINVAL;
|
||||
|
||||
mmc_bus_get(host);
|
||||
if (!host->bus_ops || host->bus_dead || !host->bus_ops->reset) {
|
||||
mmc_bus_put(host);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
ret = host->bus_ops->reset(host);
|
||||
mmc_bus_put(host);
|
||||
|
||||
pr_warn("%s: tried to reset card\n", mmc_hostname(host));
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_hw_reset);
|
||||
|
||||
int mmc_hw_reset_check(struct mmc_host *host)
|
||||
{
|
||||
return mmc_do_hw_reset(host, 1);
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_hw_reset_check);
|
||||
|
||||
static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
|
||||
{
|
||||
host->f_init = freq;
|
||||
|
@ -27,11 +27,15 @@ struct mmc_bus_ops {
|
||||
int (*power_restore)(struct mmc_host *);
|
||||
int (*alive)(struct mmc_host *);
|
||||
int (*shutdown)(struct mmc_host *);
|
||||
int (*reset)(struct mmc_host *);
|
||||
};
|
||||
|
||||
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
|
||||
void mmc_detach_bus(struct mmc_host *host);
|
||||
|
||||
struct device_node *mmc_of_find_child_device(struct mmc_host *host,
|
||||
unsigned func_num);
|
||||
|
||||
void mmc_init_erase(struct mmc_card *card);
|
||||
|
||||
void mmc_set_chip_select(struct mmc_host *host, int mode);
|
||||
@ -82,5 +86,8 @@ void mmc_add_card_debugfs(struct mmc_card *card);
|
||||
void mmc_remove_card_debugfs(struct mmc_card *card);
|
||||
|
||||
void mmc_init_context_info(struct mmc_host *host);
|
||||
|
||||
int mmc_execute_tuning(struct mmc_card *card);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -29,13 +29,20 @@
|
||||
|
||||
#include "core.h"
|
||||
#include "host.h"
|
||||
#include "slot-gpio.h"
|
||||
#include "pwrseq.h"
|
||||
|
||||
#define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev)
|
||||
|
||||
static DEFINE_IDR(mmc_host_idr);
|
||||
static DEFINE_SPINLOCK(mmc_host_lock);
|
||||
|
||||
static void mmc_host_classdev_release(struct device *dev)
|
||||
{
|
||||
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
||||
mutex_destroy(&host->slot.lock);
|
||||
spin_lock(&mmc_host_lock);
|
||||
idr_remove(&mmc_host_idr, host->index);
|
||||
spin_unlock(&mmc_host_lock);
|
||||
kfree(host);
|
||||
}
|
||||
|
||||
@ -54,9 +61,6 @@ void mmc_unregister_host_class(void)
|
||||
class_unregister(&mmc_host_class);
|
||||
}
|
||||
|
||||
static DEFINE_IDR(mmc_host_idr);
|
||||
static DEFINE_SPINLOCK(mmc_host_lock);
|
||||
|
||||
#ifdef CONFIG_MMC_CLKGATE
|
||||
static ssize_t clkgate_delay_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
@ -367,16 +371,10 @@ int mmc_of_parse(struct mmc_host *host)
|
||||
|
||||
ret = mmc_gpiod_request_cd(host, "cd", 0, true,
|
||||
0, &cd_gpio_invert);
|
||||
if (ret) {
|
||||
if (ret == -EPROBE_DEFER)
|
||||
return ret;
|
||||
if (ret != -ENOENT) {
|
||||
dev_err(host->parent,
|
||||
"Failed to request CD GPIO: %d\n",
|
||||
ret);
|
||||
}
|
||||
} else
|
||||
if (!ret)
|
||||
dev_info(host->parent, "Got CD GPIO\n");
|
||||
else if (ret != -ENOENT)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* There are two ways to flag that the CD line is inverted:
|
||||
@ -397,16 +395,10 @@ int mmc_of_parse(struct mmc_host *host)
|
||||
ro_cap_invert = of_property_read_bool(np, "wp-inverted");
|
||||
|
||||
ret = mmc_gpiod_request_ro(host, "wp", 0, false, 0, &ro_gpio_invert);
|
||||
if (ret) {
|
||||
if (ret == -EPROBE_DEFER)
|
||||
goto out;
|
||||
if (ret != -ENOENT) {
|
||||
dev_err(host->parent,
|
||||
"Failed to request WP GPIO: %d\n",
|
||||
ret);
|
||||
}
|
||||
} else
|
||||
if (!ret)
|
||||
dev_info(host->parent, "Got WP GPIO\n");
|
||||
else if (ret != -ENOENT)
|
||||
return ret;
|
||||
|
||||
/* See the comment on CD inversion above */
|
||||
if (ro_cap_invert ^ ro_gpio_invert)
|
||||
@ -457,11 +449,7 @@ int mmc_of_parse(struct mmc_host *host)
|
||||
host->dsr_req = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
mmc_gpio_free_cd(host);
|
||||
return ret;
|
||||
return mmc_pwrseq_alloc(host);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(mmc_of_parse);
|
||||
@ -491,8 +479,10 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
|
||||
host->index = err;
|
||||
spin_unlock(&mmc_host_lock);
|
||||
idr_preload_end();
|
||||
if (err < 0)
|
||||
goto free;
|
||||
if (err < 0) {
|
||||
kfree(host);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dev_set_name(&host->class_dev, "mmc%d", host->index);
|
||||
|
||||
@ -501,10 +491,12 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
|
||||
host->class_dev.class = &mmc_host_class;
|
||||
device_initialize(&host->class_dev);
|
||||
|
||||
mmc_host_clk_init(host);
|
||||
if (mmc_gpio_alloc(host)) {
|
||||
put_device(&host->class_dev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mutex_init(&host->slot.lock);
|
||||
host->slot.cd_irq = -EINVAL;
|
||||
mmc_host_clk_init(host);
|
||||
|
||||
spin_lock_init(&host->lock);
|
||||
init_waitqueue_head(&host->wq);
|
||||
@ -525,10 +517,6 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
|
||||
host->max_blk_count = PAGE_CACHE_SIZE / 512;
|
||||
|
||||
return host;
|
||||
|
||||
free:
|
||||
kfree(host);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(mmc_alloc_host);
|
||||
@ -601,10 +589,7 @@ EXPORT_SYMBOL(mmc_remove_host);
|
||||
*/
|
||||
void mmc_free_host(struct mmc_host *host)
|
||||
{
|
||||
spin_lock(&mmc_host_lock);
|
||||
idr_remove(&mmc_host_idr, host->index);
|
||||
spin_unlock(&mmc_host_lock);
|
||||
|
||||
mmc_pwrseq_free(host);
|
||||
put_device(&host->class_dev);
|
||||
}
|
||||
|
||||
|
@ -483,11 +483,13 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
||||
/* check whether the eMMC card supports BKOPS */
|
||||
if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) {
|
||||
card->ext_csd.bkops = 1;
|
||||
card->ext_csd.bkops_en = ext_csd[EXT_CSD_BKOPS_EN];
|
||||
card->ext_csd.man_bkops_en =
|
||||
(ext_csd[EXT_CSD_BKOPS_EN] &
|
||||
EXT_CSD_MANUAL_BKOPS_MASK);
|
||||
card->ext_csd.raw_bkops_status =
|
||||
ext_csd[EXT_CSD_BKOPS_STATUS];
|
||||
if (!card->ext_csd.bkops_en)
|
||||
pr_info("%s: BKOPS_EN bit is not set\n",
|
||||
if (!card->ext_csd.man_bkops_en)
|
||||
pr_info("%s: MAN_BKOPS_EN bit is not set\n",
|
||||
mmc_hostname(card->host));
|
||||
}
|
||||
|
||||
@ -1155,38 +1157,6 @@ bus_speed:
|
||||
return err;
|
||||
}
|
||||
|
||||
const u8 tuning_blk_pattern_4bit[MMC_TUNING_BLK_PATTERN_4BIT_SIZE] = {
|
||||
0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc,
|
||||
0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef,
|
||||
0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb,
|
||||
0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef,
|
||||
0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c,
|
||||
0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee,
|
||||
0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff,
|
||||
0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde,
|
||||
};
|
||||
EXPORT_SYMBOL(tuning_blk_pattern_4bit);
|
||||
|
||||
const u8 tuning_blk_pattern_8bit[MMC_TUNING_BLK_PATTERN_8BIT_SIZE] = {
|
||||
0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00,
|
||||
0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc,
|
||||
0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff,
|
||||
0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff,
|
||||
0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd,
|
||||
0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb,
|
||||
0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff,
|
||||
0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff,
|
||||
0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00,
|
||||
0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc,
|
||||
0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff,
|
||||
0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee,
|
||||
0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd,
|
||||
0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff,
|
||||
0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff,
|
||||
0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee,
|
||||
};
|
||||
EXPORT_SYMBOL(tuning_blk_pattern_8bit);
|
||||
|
||||
/*
|
||||
* Execute tuning sequence to seek the proper bus operating
|
||||
* conditions for HS200 and HS400, which sends CMD21 to the device.
|
||||
@ -1194,7 +1164,6 @@ EXPORT_SYMBOL(tuning_blk_pattern_8bit);
|
||||
static int mmc_hs200_tuning(struct mmc_card *card)
|
||||
{
|
||||
struct mmc_host *host = card->host;
|
||||
int err = 0;
|
||||
|
||||
/*
|
||||
* Timing should be adjusted to the HS400 target
|
||||
@ -1205,18 +1174,7 @@ static int mmc_hs200_tuning(struct mmc_card *card)
|
||||
if (host->ops->prepare_hs400_tuning)
|
||||
host->ops->prepare_hs400_tuning(host, &host->ios);
|
||||
|
||||
if (host->ops->execute_tuning) {
|
||||
mmc_host_clk_hold(host);
|
||||
err = host->ops->execute_tuning(host,
|
||||
MMC_SEND_TUNING_BLOCK_HS200);
|
||||
mmc_host_clk_release(host);
|
||||
|
||||
if (err)
|
||||
pr_err("%s: tuning execution failed\n",
|
||||
mmc_hostname(host));
|
||||
}
|
||||
|
||||
return err;
|
||||
return mmc_execute_tuning(card);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1296,6 +1254,12 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
|
||||
}
|
||||
|
||||
/*
|
||||
* Call the optional HC's init_card function to handle quirks.
|
||||
*/
|
||||
if (host->ops->init_card)
|
||||
host->ops->init_card(host, card);
|
||||
|
||||
/*
|
||||
* For native busses: set card RCA and quit open drain mode.
|
||||
*/
|
||||
@ -1821,6 +1785,46 @@ static int mmc_power_restore(struct mmc_host *host)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mmc_can_reset(struct mmc_card *card)
|
||||
{
|
||||
u8 rst_n_function;
|
||||
|
||||
rst_n_function = card->ext_csd.rst_n_function;
|
||||
if ((rst_n_function & EXT_CSD_RST_N_EN_MASK) != EXT_CSD_RST_N_ENABLED)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_can_reset);
|
||||
|
||||
static int mmc_reset(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_card *card = host->card;
|
||||
u32 status;
|
||||
|
||||
if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!mmc_can_reset(card))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
mmc_host_clk_hold(host);
|
||||
mmc_set_clock(host, host->f_init);
|
||||
|
||||
host->ops->hw_reset(host);
|
||||
|
||||
/* If the reset has happened, then a status command will fail */
|
||||
if (!mmc_send_status(card, &status)) {
|
||||
mmc_host_clk_release(host);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
/* Set initial state and call mmc_set_ios */
|
||||
mmc_set_initial_state(host);
|
||||
mmc_host_clk_release(host);
|
||||
|
||||
return mmc_power_restore(host);
|
||||
}
|
||||
|
||||
static const struct mmc_bus_ops mmc_ops = {
|
||||
.remove = mmc_remove,
|
||||
.detect = mmc_detect,
|
||||
@ -1831,6 +1835,7 @@ static const struct mmc_bus_ops mmc_ops = {
|
||||
.power_restore = mmc_power_restore,
|
||||
.alive = mmc_alive,
|
||||
.shutdown = mmc_shutdown,
|
||||
.reset = mmc_reset,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -23,6 +23,36 @@
|
||||
|
||||
#define MMC_OPS_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */
|
||||
|
||||
static const u8 tuning_blk_pattern_4bit[] = {
|
||||
0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc,
|
||||
0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef,
|
||||
0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb,
|
||||
0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef,
|
||||
0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c,
|
||||
0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee,
|
||||
0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff,
|
||||
0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde,
|
||||
};
|
||||
|
||||
static const u8 tuning_blk_pattern_8bit[] = {
|
||||
0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00,
|
||||
0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc,
|
||||
0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff,
|
||||
0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff,
|
||||
0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd,
|
||||
0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb,
|
||||
0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff,
|
||||
0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff,
|
||||
0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00,
|
||||
0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc,
|
||||
0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff,
|
||||
0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee,
|
||||
0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd,
|
||||
0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff,
|
||||
0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff,
|
||||
0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee,
|
||||
};
|
||||
|
||||
static inline int __mmc_send_status(struct mmc_card *card, u32 *status,
|
||||
bool ignore_crc)
|
||||
{
|
||||
|
112
drivers/mmc/core/pwrseq.c
Normal file
112
drivers/mmc/core/pwrseq.c
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Linaro Ltd
|
||||
*
|
||||
* Author: Ulf Hansson <ulf.hansson@linaro.org>
|
||||
*
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
*
|
||||
* MMC power sequence management
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
#include <linux/mmc/host.h>
|
||||
|
||||
#include "pwrseq.h"
|
||||
|
||||
struct mmc_pwrseq_match {
|
||||
const char *compatible;
|
||||
int (*alloc)(struct mmc_host *host, struct device *dev);
|
||||
};
|
||||
|
||||
static struct mmc_pwrseq_match pwrseq_match[] = {
|
||||
{
|
||||
.compatible = "mmc-pwrseq-simple",
|
||||
.alloc = mmc_pwrseq_simple_alloc,
|
||||
}, {
|
||||
.compatible = "mmc-pwrseq-emmc",
|
||||
.alloc = mmc_pwrseq_emmc_alloc,
|
||||
},
|
||||
};
|
||||
|
||||
static struct mmc_pwrseq_match *mmc_pwrseq_find(struct device_node *np)
|
||||
{
|
||||
struct mmc_pwrseq_match *match = ERR_PTR(-ENODEV);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pwrseq_match); i++) {
|
||||
if (of_device_is_compatible(np, pwrseq_match[i].compatible)) {
|
||||
match = &pwrseq_match[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
int mmc_pwrseq_alloc(struct mmc_host *host)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
struct device_node *np;
|
||||
struct mmc_pwrseq_match *match;
|
||||
int ret = 0;
|
||||
|
||||
np = of_parse_phandle(host->parent->of_node, "mmc-pwrseq", 0);
|
||||
if (!np)
|
||||
return 0;
|
||||
|
||||
pdev = of_find_device_by_node(np);
|
||||
if (!pdev) {
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
match = mmc_pwrseq_find(np);
|
||||
if (IS_ERR(match)) {
|
||||
ret = PTR_ERR(match);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = match->alloc(host, &pdev->dev);
|
||||
if (!ret)
|
||||
dev_info(host->parent, "allocated mmc-pwrseq\n");
|
||||
|
||||
err:
|
||||
of_node_put(np);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void mmc_pwrseq_pre_power_on(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_pwrseq *pwrseq = host->pwrseq;
|
||||
|
||||
if (pwrseq && pwrseq->ops && pwrseq->ops->pre_power_on)
|
||||
pwrseq->ops->pre_power_on(host);
|
||||
}
|
||||
|
||||
void mmc_pwrseq_post_power_on(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_pwrseq *pwrseq = host->pwrseq;
|
||||
|
||||
if (pwrseq && pwrseq->ops && pwrseq->ops->post_power_on)
|
||||
pwrseq->ops->post_power_on(host);
|
||||
}
|
||||
|
||||
void mmc_pwrseq_power_off(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_pwrseq *pwrseq = host->pwrseq;
|
||||
|
||||
if (pwrseq && pwrseq->ops && pwrseq->ops->power_off)
|
||||
pwrseq->ops->power_off(host);
|
||||
}
|
||||
|
||||
void mmc_pwrseq_free(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_pwrseq *pwrseq = host->pwrseq;
|
||||
|
||||
if (pwrseq && pwrseq->ops && pwrseq->ops->free)
|
||||
pwrseq->ops->free(host);
|
||||
}
|
43
drivers/mmc/core/pwrseq.h
Normal file
43
drivers/mmc/core/pwrseq.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Linaro Ltd
|
||||
*
|
||||
* Author: Ulf Hansson <ulf.hansson@linaro.org>
|
||||
*
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
*/
|
||||
#ifndef _MMC_CORE_PWRSEQ_H
|
||||
#define _MMC_CORE_PWRSEQ_H
|
||||
|
||||
struct mmc_pwrseq_ops {
|
||||
void (*pre_power_on)(struct mmc_host *host);
|
||||
void (*post_power_on)(struct mmc_host *host);
|
||||
void (*power_off)(struct mmc_host *host);
|
||||
void (*free)(struct mmc_host *host);
|
||||
};
|
||||
|
||||
struct mmc_pwrseq {
|
||||
struct mmc_pwrseq_ops *ops;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
||||
int mmc_pwrseq_alloc(struct mmc_host *host);
|
||||
void mmc_pwrseq_pre_power_on(struct mmc_host *host);
|
||||
void mmc_pwrseq_post_power_on(struct mmc_host *host);
|
||||
void mmc_pwrseq_power_off(struct mmc_host *host);
|
||||
void mmc_pwrseq_free(struct mmc_host *host);
|
||||
|
||||
int mmc_pwrseq_simple_alloc(struct mmc_host *host, struct device *dev);
|
||||
int mmc_pwrseq_emmc_alloc(struct mmc_host *host, struct device *dev);
|
||||
|
||||
#else
|
||||
|
||||
static inline int mmc_pwrseq_alloc(struct mmc_host *host) { return 0; }
|
||||
static inline void mmc_pwrseq_pre_power_on(struct mmc_host *host) {}
|
||||
static inline void mmc_pwrseq_post_power_on(struct mmc_host *host) {}
|
||||
static inline void mmc_pwrseq_power_off(struct mmc_host *host) {}
|
||||
static inline void mmc_pwrseq_free(struct mmc_host *host) {}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
101
drivers/mmc/core/pwrseq_emmc.c
Normal file
101
drivers/mmc/core/pwrseq_emmc.c
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (C) 2015, Samsung Electronics Co., Ltd.
|
||||
*
|
||||
* Author: Marek Szyprowski <m.szyprowski@samsung.com>
|
||||
*
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
*
|
||||
* Simple eMMC hardware reset provider
|
||||
*/
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/reboot.h>
|
||||
|
||||
#include <linux/mmc/host.h>
|
||||
|
||||
#include "pwrseq.h"
|
||||
|
||||
struct mmc_pwrseq_emmc {
|
||||
struct mmc_pwrseq pwrseq;
|
||||
struct notifier_block reset_nb;
|
||||
struct gpio_desc *reset_gpio;
|
||||
};
|
||||
|
||||
static void __mmc_pwrseq_emmc_reset(struct mmc_pwrseq_emmc *pwrseq)
|
||||
{
|
||||
gpiod_set_value(pwrseq->reset_gpio, 1);
|
||||
udelay(1);
|
||||
gpiod_set_value(pwrseq->reset_gpio, 0);
|
||||
udelay(200);
|
||||
}
|
||||
|
||||
static void mmc_pwrseq_emmc_reset(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_pwrseq_emmc *pwrseq = container_of(host->pwrseq,
|
||||
struct mmc_pwrseq_emmc, pwrseq);
|
||||
|
||||
__mmc_pwrseq_emmc_reset(pwrseq);
|
||||
}
|
||||
|
||||
static void mmc_pwrseq_emmc_free(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_pwrseq_emmc *pwrseq = container_of(host->pwrseq,
|
||||
struct mmc_pwrseq_emmc, pwrseq);
|
||||
|
||||
unregister_restart_handler(&pwrseq->reset_nb);
|
||||
gpiod_put(pwrseq->reset_gpio);
|
||||
kfree(pwrseq);
|
||||
host->pwrseq = NULL;
|
||||
}
|
||||
|
||||
static struct mmc_pwrseq_ops mmc_pwrseq_emmc_ops = {
|
||||
.post_power_on = mmc_pwrseq_emmc_reset,
|
||||
.free = mmc_pwrseq_emmc_free,
|
||||
};
|
||||
|
||||
static int mmc_pwrseq_emmc_reset_nb(struct notifier_block *this,
|
||||
unsigned long mode, void *cmd)
|
||||
{
|
||||
struct mmc_pwrseq_emmc *pwrseq = container_of(this,
|
||||
struct mmc_pwrseq_emmc, reset_nb);
|
||||
|
||||
__mmc_pwrseq_emmc_reset(pwrseq);
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
int mmc_pwrseq_emmc_alloc(struct mmc_host *host, struct device *dev)
|
||||
{
|
||||
struct mmc_pwrseq_emmc *pwrseq;
|
||||
int ret = 0;
|
||||
|
||||
pwrseq = kzalloc(sizeof(struct mmc_pwrseq_emmc), GFP_KERNEL);
|
||||
if (!pwrseq)
|
||||
return -ENOMEM;
|
||||
|
||||
pwrseq->reset_gpio = gpiod_get_index(dev, "reset", 0, GPIOD_OUT_LOW);
|
||||
if (IS_ERR(pwrseq->reset_gpio)) {
|
||||
ret = PTR_ERR(pwrseq->reset_gpio);
|
||||
goto free;
|
||||
}
|
||||
|
||||
/*
|
||||
* register reset handler to ensure emmc reset also from
|
||||
* emergency_reboot(), priority 129 schedules it just before
|
||||
* system reboot
|
||||
*/
|
||||
pwrseq->reset_nb.notifier_call = mmc_pwrseq_emmc_reset_nb;
|
||||
pwrseq->reset_nb.priority = 129;
|
||||
register_restart_handler(&pwrseq->reset_nb);
|
||||
|
||||
pwrseq->pwrseq.ops = &mmc_pwrseq_emmc_ops;
|
||||
host->pwrseq = &pwrseq->pwrseq;
|
||||
|
||||
return 0;
|
||||
free:
|
||||
kfree(pwrseq);
|
||||
return ret;
|
||||
}
|
145
drivers/mmc/core/pwrseq_simple.c
Normal file
145
drivers/mmc/core/pwrseq_simple.c
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Linaro Ltd
|
||||
*
|
||||
* Author: Ulf Hansson <ulf.hansson@linaro.org>
|
||||
*
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
*
|
||||
* Simple MMC power sequence management
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
#include <linux/mmc/host.h>
|
||||
|
||||
#include "pwrseq.h"
|
||||
|
||||
struct mmc_pwrseq_simple {
|
||||
struct mmc_pwrseq pwrseq;
|
||||
bool clk_enabled;
|
||||
struct clk *ext_clk;
|
||||
int nr_gpios;
|
||||
struct gpio_desc *reset_gpios[0];
|
||||
};
|
||||
|
||||
static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
|
||||
int value)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pwrseq->nr_gpios; i++)
|
||||
if (!IS_ERR(pwrseq->reset_gpios[i]))
|
||||
gpiod_set_value_cansleep(pwrseq->reset_gpios[i], value);
|
||||
}
|
||||
|
||||
static void mmc_pwrseq_simple_pre_power_on(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq,
|
||||
struct mmc_pwrseq_simple, pwrseq);
|
||||
|
||||
if (!IS_ERR(pwrseq->ext_clk) && !pwrseq->clk_enabled) {
|
||||
clk_prepare_enable(pwrseq->ext_clk);
|
||||
pwrseq->clk_enabled = true;
|
||||
}
|
||||
|
||||
mmc_pwrseq_simple_set_gpios_value(pwrseq, 1);
|
||||
}
|
||||
|
||||
static void mmc_pwrseq_simple_post_power_on(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq,
|
||||
struct mmc_pwrseq_simple, pwrseq);
|
||||
|
||||
mmc_pwrseq_simple_set_gpios_value(pwrseq, 0);
|
||||
}
|
||||
|
||||
static void mmc_pwrseq_simple_power_off(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq,
|
||||
struct mmc_pwrseq_simple, pwrseq);
|
||||
|
||||
mmc_pwrseq_simple_set_gpios_value(pwrseq, 1);
|
||||
|
||||
if (!IS_ERR(pwrseq->ext_clk) && pwrseq->clk_enabled) {
|
||||
clk_disable_unprepare(pwrseq->ext_clk);
|
||||
pwrseq->clk_enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void mmc_pwrseq_simple_free(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq,
|
||||
struct mmc_pwrseq_simple, pwrseq);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pwrseq->nr_gpios; i++)
|
||||
if (!IS_ERR(pwrseq->reset_gpios[i]))
|
||||
gpiod_put(pwrseq->reset_gpios[i]);
|
||||
|
||||
if (!IS_ERR(pwrseq->ext_clk))
|
||||
clk_put(pwrseq->ext_clk);
|
||||
|
||||
kfree(pwrseq);
|
||||
host->pwrseq = NULL;
|
||||
}
|
||||
|
||||
static struct mmc_pwrseq_ops mmc_pwrseq_simple_ops = {
|
||||
.pre_power_on = mmc_pwrseq_simple_pre_power_on,
|
||||
.post_power_on = mmc_pwrseq_simple_post_power_on,
|
||||
.power_off = mmc_pwrseq_simple_power_off,
|
||||
.free = mmc_pwrseq_simple_free,
|
||||
};
|
||||
|
||||
int mmc_pwrseq_simple_alloc(struct mmc_host *host, struct device *dev)
|
||||
{
|
||||
struct mmc_pwrseq_simple *pwrseq;
|
||||
int i, nr_gpios, ret = 0;
|
||||
|
||||
nr_gpios = of_gpio_named_count(dev->of_node, "reset-gpios");
|
||||
if (nr_gpios < 0)
|
||||
nr_gpios = 0;
|
||||
|
||||
pwrseq = kzalloc(sizeof(struct mmc_pwrseq_simple) + nr_gpios *
|
||||
sizeof(struct gpio_desc *), GFP_KERNEL);
|
||||
if (!pwrseq)
|
||||
return -ENOMEM;
|
||||
|
||||
pwrseq->ext_clk = clk_get(dev, "ext_clock");
|
||||
if (IS_ERR(pwrseq->ext_clk) &&
|
||||
PTR_ERR(pwrseq->ext_clk) != -ENOENT) {
|
||||
ret = PTR_ERR(pwrseq->ext_clk);
|
||||
goto free;
|
||||
}
|
||||
|
||||
for (i = 0; i < nr_gpios; i++) {
|
||||
pwrseq->reset_gpios[i] = gpiod_get_index(dev, "reset", i,
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(pwrseq->reset_gpios[i]) &&
|
||||
PTR_ERR(pwrseq->reset_gpios[i]) != -ENOENT &&
|
||||
PTR_ERR(pwrseq->reset_gpios[i]) != -ENOSYS) {
|
||||
ret = PTR_ERR(pwrseq->reset_gpios[i]);
|
||||
|
||||
while (--i)
|
||||
gpiod_put(pwrseq->reset_gpios[i]);
|
||||
|
||||
goto clk_put;
|
||||
}
|
||||
}
|
||||
|
||||
pwrseq->nr_gpios = nr_gpios;
|
||||
pwrseq->pwrseq.ops = &mmc_pwrseq_simple_ops;
|
||||
host->pwrseq = &pwrseq->pwrseq;
|
||||
|
||||
return 0;
|
||||
clk_put:
|
||||
if (!IS_ERR(pwrseq->ext_clk))
|
||||
clk_put(pwrseq->ext_clk);
|
||||
free:
|
||||
kfree(pwrseq);
|
||||
return ret;
|
||||
}
|
@ -660,15 +660,10 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
|
||||
* SPI mode doesn't define CMD19 and tuning is only valid for SDR50 and
|
||||
* SDR104 mode SD-cards. Note that tuning is mandatory for SDR104.
|
||||
*/
|
||||
if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning &&
|
||||
if (!mmc_host_is_spi(card->host) &&
|
||||
(card->sd_bus_speed == UHS_SDR50_BUS_SPEED ||
|
||||
card->sd_bus_speed == UHS_SDR104_BUS_SPEED)) {
|
||||
mmc_host_clk_hold(card->host);
|
||||
err = card->host->ops->execute_tuning(card->host,
|
||||
MMC_SEND_TUNING_BLOCK);
|
||||
mmc_host_clk_release(card->host);
|
||||
}
|
||||
|
||||
card->sd_bus_speed == UHS_SDR104_BUS_SPEED))
|
||||
err = mmc_execute_tuning(card);
|
||||
out:
|
||||
kfree(status);
|
||||
|
||||
@ -932,6 +927,12 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
|
||||
memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
|
||||
}
|
||||
|
||||
/*
|
||||
* Call the optional HC's init_card function to handle quirks.
|
||||
*/
|
||||
if (host->ops->init_card)
|
||||
host->ops->init_card(host, card);
|
||||
|
||||
/*
|
||||
* For native busses: get card RCA and quit open drain mode.
|
||||
*/
|
||||
@ -1191,6 +1192,12 @@ static int mmc_sd_power_restore(struct mmc_host *host)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mmc_sd_reset(struct mmc_host *host)
|
||||
{
|
||||
mmc_power_cycle(host, host->card->ocr);
|
||||
return mmc_sd_power_restore(host);
|
||||
}
|
||||
|
||||
static const struct mmc_bus_ops mmc_sd_ops = {
|
||||
.remove = mmc_sd_remove,
|
||||
.detect = mmc_sd_detect,
|
||||
@ -1201,6 +1208,7 @@ static const struct mmc_bus_ops mmc_sd_ops = {
|
||||
.power_restore = mmc_sd_power_restore,
|
||||
.alive = mmc_sd_alive,
|
||||
.shutdown = mmc_sd_suspend,
|
||||
.reset = mmc_sd_reset,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -1271,4 +1279,3 @@ err:
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -567,17 +567,11 @@ static int mmc_sdio_init_uhs_card(struct mmc_card *card)
|
||||
* SPI mode doesn't define CMD19 and tuning is only valid for SDR50 and
|
||||
* SDR104 mode SD-cards. Note that tuning is mandatory for SDR104.
|
||||
*/
|
||||
if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning &&
|
||||
if (!mmc_host_is_spi(card->host) &&
|
||||
((card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR50) ||
|
||||
(card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR104))) {
|
||||
mmc_host_clk_hold(card->host);
|
||||
err = card->host->ops->execute_tuning(card->host,
|
||||
MMC_SEND_TUNING_BLOCK);
|
||||
mmc_host_clk_release(card->host);
|
||||
}
|
||||
|
||||
(card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR104)))
|
||||
err = mmc_execute_tuning(card);
|
||||
out:
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,9 @@
|
||||
#include <linux/mmc/card.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/sdio_func.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "sdio_cis.h"
|
||||
#include "sdio_bus.h"
|
||||
|
||||
@ -295,6 +297,13 @@ static void sdio_acpi_set_handle(struct sdio_func *func)
|
||||
static inline void sdio_acpi_set_handle(struct sdio_func *func) {}
|
||||
#endif
|
||||
|
||||
static void sdio_set_of_node(struct sdio_func *func)
|
||||
{
|
||||
struct mmc_host *host = func->card->host;
|
||||
|
||||
func->dev.of_node = mmc_of_find_child_device(host, func->num);
|
||||
}
|
||||
|
||||
/*
|
||||
* Register a new SDIO function with the driver model.
|
||||
*/
|
||||
@ -304,6 +313,7 @@ int sdio_add_func(struct sdio_func *func)
|
||||
|
||||
dev_set_name(&func->dev, "%s:%d", mmc_card_id(func->card), func->num);
|
||||
|
||||
sdio_set_of_node(func);
|
||||
sdio_acpi_set_handle(func);
|
||||
ret = device_add(&func->dev);
|
||||
if (ret == 0) {
|
||||
@ -327,6 +337,7 @@ void sdio_remove_func(struct sdio_func *func)
|
||||
|
||||
dev_pm_domain_detach(&func->dev, false);
|
||||
device_del(&func->dev);
|
||||
of_node_put(func->dev.of_node);
|
||||
put_device(&func->dev);
|
||||
}
|
||||
|
||||
|
@ -18,11 +18,14 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "slot-gpio.h"
|
||||
|
||||
struct mmc_gpio {
|
||||
struct gpio_desc *ro_gpio;
|
||||
struct gpio_desc *cd_gpio;
|
||||
bool override_ro_active_level;
|
||||
bool override_cd_active_level;
|
||||
irqreturn_t (*cd_gpio_isr)(int irq, void *dev_id);
|
||||
char *ro_label;
|
||||
char cd_label[0];
|
||||
};
|
||||
@ -38,31 +41,19 @@ static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int mmc_gpio_alloc(struct mmc_host *host)
|
||||
int mmc_gpio_alloc(struct mmc_host *host)
|
||||
{
|
||||
size_t len = strlen(dev_name(host->parent)) + 4;
|
||||
struct mmc_gpio *ctx;
|
||||
struct mmc_gpio *ctx = devm_kzalloc(host->parent,
|
||||
sizeof(*ctx) + 2 * len, GFP_KERNEL);
|
||||
|
||||
mutex_lock(&host->slot.lock);
|
||||
|
||||
ctx = host->slot.handler_priv;
|
||||
if (!ctx) {
|
||||
/*
|
||||
* devm_kzalloc() can be called after device_initialize(), even
|
||||
* before device_add(), i.e., between mmc_alloc_host() and
|
||||
* mmc_add_host()
|
||||
*/
|
||||
ctx = devm_kzalloc(&host->class_dev, sizeof(*ctx) + 2 * len,
|
||||
GFP_KERNEL);
|
||||
if (ctx) {
|
||||
ctx->ro_label = ctx->cd_label + len;
|
||||
snprintf(ctx->cd_label, len, "%s cd", dev_name(host->parent));
|
||||
snprintf(ctx->ro_label, len, "%s ro", dev_name(host->parent));
|
||||
host->slot.handler_priv = ctx;
|
||||
host->slot.cd_irq = -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&host->slot.lock);
|
||||
|
||||
return ctx ? 0 : -ENOMEM;
|
||||
}
|
||||
@ -103,29 +94,19 @@ EXPORT_SYMBOL(mmc_gpio_get_cd);
|
||||
* @gpio: gpio number requested
|
||||
*
|
||||
* As devm_* managed functions are used in mmc_gpio_request_ro(), client
|
||||
* drivers do not need to explicitly call mmc_gpio_free_ro() for freeing up,
|
||||
* if the requesting and freeing are only needed at probing and unbinding time
|
||||
* for once. However, if client drivers do something special like runtime
|
||||
* switching for write-protection, they are responsible for calling
|
||||
* mmc_gpio_request_ro() and mmc_gpio_free_ro() as a pair on their own.
|
||||
* drivers do not need to worry about freeing up memory.
|
||||
*
|
||||
* Returns zero on success, else an error.
|
||||
*/
|
||||
int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio)
|
||||
{
|
||||
struct mmc_gpio *ctx;
|
||||
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||
int ret;
|
||||
|
||||
if (!gpio_is_valid(gpio))
|
||||
return -EINVAL;
|
||||
|
||||
ret = mmc_gpio_alloc(host);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ctx = host->slot.handler_priv;
|
||||
|
||||
ret = devm_gpio_request_one(&host->class_dev, gpio, GPIOF_DIR_IN,
|
||||
ret = devm_gpio_request_one(host->parent, gpio, GPIOF_DIR_IN,
|
||||
ctx->ro_label);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@ -156,8 +137,10 @@ void mmc_gpiod_request_cd_irq(struct mmc_host *host)
|
||||
irq = -EINVAL;
|
||||
|
||||
if (irq >= 0) {
|
||||
ret = devm_request_threaded_irq(&host->class_dev, irq,
|
||||
NULL, mmc_gpio_cd_irqt,
|
||||
if (!ctx->cd_gpio_isr)
|
||||
ctx->cd_gpio_isr = mmc_gpio_cd_irqt;
|
||||
ret = devm_request_threaded_irq(host->parent, irq,
|
||||
NULL, ctx->cd_gpio_isr,
|
||||
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
ctx->cd_label, host);
|
||||
if (ret < 0)
|
||||
@ -171,6 +154,19 @@ void mmc_gpiod_request_cd_irq(struct mmc_host *host)
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_gpiod_request_cd_irq);
|
||||
|
||||
/* Register an alternate interrupt service routine for
|
||||
* the card-detect GPIO.
|
||||
*/
|
||||
void mmc_gpio_set_cd_isr(struct mmc_host *host,
|
||||
irqreturn_t (*isr)(int irq, void *dev_id))
|
||||
{
|
||||
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||
|
||||
WARN_ON(ctx->cd_gpio_isr);
|
||||
ctx->cd_gpio_isr = isr;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_gpio_set_cd_isr);
|
||||
|
||||
/**
|
||||
* mmc_gpio_request_cd - request a gpio for card-detection
|
||||
* @host: mmc host
|
||||
@ -178,11 +174,7 @@ EXPORT_SYMBOL(mmc_gpiod_request_cd_irq);
|
||||
* @debounce: debounce time in microseconds
|
||||
*
|
||||
* As devm_* managed functions are used in mmc_gpio_request_cd(), client
|
||||
* drivers do not need to explicitly call mmc_gpio_free_cd() for freeing up,
|
||||
* if the requesting and freeing are only needed at probing and unbinding time
|
||||
* for once. However, if client drivers do something special like runtime
|
||||
* switching for card-detection, they are responsible for calling
|
||||
* mmc_gpio_request_cd() and mmc_gpio_free_cd() as a pair on their own.
|
||||
* drivers do not need to worry about freeing up memory.
|
||||
*
|
||||
* If GPIO debouncing is desired, set the debounce parameter to a non-zero
|
||||
* value. The caller is responsible for ensuring that the GPIO driver associated
|
||||
@ -193,16 +185,10 @@ EXPORT_SYMBOL(mmc_gpiod_request_cd_irq);
|
||||
int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio,
|
||||
unsigned int debounce)
|
||||
{
|
||||
struct mmc_gpio *ctx;
|
||||
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||
int ret;
|
||||
|
||||
ret = mmc_gpio_alloc(host);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ctx = host->slot.handler_priv;
|
||||
|
||||
ret = devm_gpio_request_one(&host->class_dev, gpio, GPIOF_DIR_IN,
|
||||
ret = devm_gpio_request_one(host->parent, gpio, GPIOF_DIR_IN,
|
||||
ctx->cd_label);
|
||||
if (ret < 0)
|
||||
/*
|
||||
@ -225,55 +211,6 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio,
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_gpio_request_cd);
|
||||
|
||||
/**
|
||||
* mmc_gpio_free_ro - free the write-protection gpio
|
||||
* @host: mmc host
|
||||
*
|
||||
* It's provided only for cases that client drivers need to manually free
|
||||
* up the write-protection gpio requested by mmc_gpio_request_ro().
|
||||
*/
|
||||
void mmc_gpio_free_ro(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||
int gpio;
|
||||
|
||||
if (!ctx || !ctx->ro_gpio)
|
||||
return;
|
||||
|
||||
gpio = desc_to_gpio(ctx->ro_gpio);
|
||||
ctx->ro_gpio = NULL;
|
||||
|
||||
devm_gpio_free(&host->class_dev, gpio);
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_gpio_free_ro);
|
||||
|
||||
/**
|
||||
* mmc_gpio_free_cd - free the card-detection gpio
|
||||
* @host: mmc host
|
||||
*
|
||||
* It's provided only for cases that client drivers need to manually free
|
||||
* up the card-detection gpio requested by mmc_gpio_request_cd().
|
||||
*/
|
||||
void mmc_gpio_free_cd(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||
int gpio;
|
||||
|
||||
if (!ctx || !ctx->cd_gpio)
|
||||
return;
|
||||
|
||||
if (host->slot.cd_irq >= 0) {
|
||||
devm_free_irq(&host->class_dev, host->slot.cd_irq, host);
|
||||
host->slot.cd_irq = -EINVAL;
|
||||
}
|
||||
|
||||
gpio = desc_to_gpio(ctx->cd_gpio);
|
||||
ctx->cd_gpio = NULL;
|
||||
|
||||
devm_gpio_free(&host->class_dev, gpio);
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_gpio_free_cd);
|
||||
|
||||
/**
|
||||
* mmc_gpiod_request_cd - request a gpio descriptor for card-detection
|
||||
* @host: mmc host
|
||||
@ -285,8 +222,7 @@ EXPORT_SYMBOL(mmc_gpio_free_cd);
|
||||
* to NULL to ignore
|
||||
*
|
||||
* Use this function in place of mmc_gpio_request_cd() to use the GPIO
|
||||
* descriptor API. Note that it is paired with mmc_gpiod_free_cd() not
|
||||
* mmc_gpio_free_cd(). Note also that it must be called prior to mmc_add_host()
|
||||
* descriptor API. Note that it must be called prior to mmc_add_host()
|
||||
* otherwise the caller must also call mmc_gpiod_request_cd_irq().
|
||||
*
|
||||
* Returns zero on success, else an error.
|
||||
@ -295,16 +231,10 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
|
||||
unsigned int idx, bool override_active_level,
|
||||
unsigned int debounce, bool *gpio_invert)
|
||||
{
|
||||
struct mmc_gpio *ctx;
|
||||
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||
struct gpio_desc *desc;
|
||||
int ret;
|
||||
|
||||
ret = mmc_gpio_alloc(host);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ctx = host->slot.handler_priv;
|
||||
|
||||
if (!con_id)
|
||||
con_id = ctx->cd_label;
|
||||
|
||||
@ -339,8 +269,7 @@ EXPORT_SYMBOL(mmc_gpiod_request_cd);
|
||||
* set to NULL to ignore
|
||||
*
|
||||
* Use this function in place of mmc_gpio_request_ro() to use the GPIO
|
||||
* descriptor API. Note that it is paired with mmc_gpiod_free_ro() not
|
||||
* mmc_gpio_free_ro().
|
||||
* descriptor API.
|
||||
*
|
||||
* Returns zero on success, else an error.
|
||||
*/
|
||||
@ -348,16 +277,10 @@ int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
|
||||
unsigned int idx, bool override_active_level,
|
||||
unsigned int debounce, bool *gpio_invert)
|
||||
{
|
||||
struct mmc_gpio *ctx;
|
||||
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||
struct gpio_desc *desc;
|
||||
int ret;
|
||||
|
||||
ret = mmc_gpio_alloc(host);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ctx = host->slot.handler_priv;
|
||||
|
||||
if (!con_id)
|
||||
con_id = ctx->ro_label;
|
||||
|
||||
@ -380,28 +303,3 @@ int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_gpiod_request_ro);
|
||||
|
||||
/**
|
||||
* mmc_gpiod_free_cd - free the card-detection gpio descriptor
|
||||
* @host: mmc host
|
||||
*
|
||||
* It's provided only for cases that client drivers need to manually free
|
||||
* up the card-detection gpio requested by mmc_gpiod_request_cd().
|
||||
*/
|
||||
void mmc_gpiod_free_cd(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||
|
||||
if (!ctx || !ctx->cd_gpio)
|
||||
return;
|
||||
|
||||
if (host->slot.cd_irq >= 0) {
|
||||
devm_free_irq(&host->class_dev, host->slot.cd_irq, host);
|
||||
host->slot.cd_irq = -EINVAL;
|
||||
}
|
||||
|
||||
devm_gpiod_put(host->parent, ctx->cd_gpio);
|
||||
|
||||
ctx->cd_gpio = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_gpiod_free_cd);
|
||||
|
13
drivers/mmc/core/slot-gpio.h
Normal file
13
drivers/mmc/core/slot-gpio.h
Normal file
@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Linaro Ltd
|
||||
*
|
||||
* Author: Ulf Hansson <ulf.hansson@linaro.org>
|
||||
*
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
*/
|
||||
#ifndef _MMC_CORE_SLOTGPIO_H
|
||||
#define _MMC_CORE_SLOTGPIO_H
|
||||
|
||||
int mmc_gpio_alloc(struct mmc_host *host);
|
||||
|
||||
#endif
|
@ -57,6 +57,7 @@ config MMC_SDHCI_IO_ACCESSORS
|
||||
|
||||
config MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
|
||||
bool
|
||||
depends on MMC_SDHCI
|
||||
select MMC_SDHCI_IO_ACCESSORS
|
||||
help
|
||||
This option is selected by drivers running on big endian hosts
|
||||
@ -82,6 +83,7 @@ config MMC_SDHCI_PCI
|
||||
config MMC_RICOH_MMC
|
||||
bool "Ricoh MMC Controller Disabler"
|
||||
depends on MMC_SDHCI_PCI
|
||||
default y
|
||||
help
|
||||
This adds a pci quirk to disable Ricoh MMC Controller. This
|
||||
proprietary controller is unnecessary because the SDHCI driver
|
||||
@ -228,6 +230,7 @@ config MMC_SDHCI_PXAV3
|
||||
tristate "Marvell MMP2 SD Host Controller support (PXAV3)"
|
||||
depends on CLKDEV_LOOKUP
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
depends on ARCH_MMP || COMPILE_TEST
|
||||
default CPU_MMP2
|
||||
help
|
||||
This selects the Marvell(R) PXAV3 SD Host Controller.
|
||||
@ -240,6 +243,7 @@ config MMC_SDHCI_PXAV2
|
||||
tristate "Marvell PXA9XX SD Host Controller support (PXAV2)"
|
||||
depends on CLKDEV_LOOKUP
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
depends on ARCH_MMP || COMPILE_TEST
|
||||
default CPU_PXA910
|
||||
help
|
||||
This selects the Marvell(R) PXAV2 SD Host Controller.
|
||||
@ -292,6 +296,17 @@ config MMC_SDHCI_BCM2835
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_SDHCI_F_SDH30
|
||||
tristate "SDHCI support for Fujitsu Semiconductor F_SDH30"
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
depends on OF
|
||||
help
|
||||
This selects the Secure Digital Host Controller Interface (SDHCI)
|
||||
Needed by some Fujitsu SoC for MMC / SD / SDIO support.
|
||||
If you have a controller with this interface, say Y or M here.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_MOXART
|
||||
tristate "MOXART SD/MMC Host Controller support"
|
||||
depends on ARCH_MOXART && MMC
|
||||
|
@ -16,6 +16,7 @@ obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o
|
||||
obj-$(CONFIG_MMC_SDHCI_PXAV2) += sdhci-pxav2.o
|
||||
obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o
|
||||
obj-$(CONFIG_MMC_SDHCI_SIRF) += sdhci-sirf.o
|
||||
obj-$(CONFIG_MMC_SDHCI_F_SDH30) += sdhci_f_sdh30.o
|
||||
obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o
|
||||
obj-$(CONFIG_MMC_WBSD) += wbsd.o
|
||||
obj-$(CONFIG_MMC_AU1X) += au1xmmc.o
|
||||
|
@ -21,43 +21,7 @@
|
||||
|
||||
#include "dw_mmc.h"
|
||||
#include "dw_mmc-pltfm.h"
|
||||
|
||||
#define NUM_PINS(x) (x + 2)
|
||||
|
||||
#define SDMMC_CLKSEL 0x09C
|
||||
#define SDMMC_CLKSEL64 0x0A8
|
||||
#define SDMMC_CLKSEL_CCLK_SAMPLE(x) (((x) & 7) << 0)
|
||||
#define SDMMC_CLKSEL_CCLK_DRIVE(x) (((x) & 7) << 16)
|
||||
#define SDMMC_CLKSEL_CCLK_DIVIDER(x) (((x) & 7) << 24)
|
||||
#define SDMMC_CLKSEL_GET_DRV_WD3(x) (((x) >> 16) & 0x7)
|
||||
#define SDMMC_CLKSEL_TIMING(x, y, z) (SDMMC_CLKSEL_CCLK_SAMPLE(x) | \
|
||||
SDMMC_CLKSEL_CCLK_DRIVE(y) | \
|
||||
SDMMC_CLKSEL_CCLK_DIVIDER(z))
|
||||
#define SDMMC_CLKSEL_WAKEUP_INT BIT(11)
|
||||
|
||||
#define EXYNOS4210_FIXED_CIU_CLK_DIV 2
|
||||
#define EXYNOS4412_FIXED_CIU_CLK_DIV 4
|
||||
|
||||
/* Block number in eMMC */
|
||||
#define DWMCI_BLOCK_NUM 0xFFFFFFFF
|
||||
|
||||
#define SDMMC_EMMCP_BASE 0x1000
|
||||
#define SDMMC_MPSECURITY (SDMMC_EMMCP_BASE + 0x0010)
|
||||
#define SDMMC_MPSBEGIN0 (SDMMC_EMMCP_BASE + 0x0200)
|
||||
#define SDMMC_MPSEND0 (SDMMC_EMMCP_BASE + 0x0204)
|
||||
#define SDMMC_MPSCTRL0 (SDMMC_EMMCP_BASE + 0x020C)
|
||||
|
||||
/* SMU control bits */
|
||||
#define DWMCI_MPSCTRL_SECURE_READ_BIT BIT(7)
|
||||
#define DWMCI_MPSCTRL_SECURE_WRITE_BIT BIT(6)
|
||||
#define DWMCI_MPSCTRL_NON_SECURE_READ_BIT BIT(5)
|
||||
#define DWMCI_MPSCTRL_NON_SECURE_WRITE_BIT BIT(4)
|
||||
#define DWMCI_MPSCTRL_USE_FUSE_KEY BIT(3)
|
||||
#define DWMCI_MPSCTRL_ECB_MODE BIT(2)
|
||||
#define DWMCI_MPSCTRL_ENCRYPTION BIT(1)
|
||||
#define DWMCI_MPSCTRL_VALID BIT(0)
|
||||
|
||||
#define EXYNOS_CCLKIN_MIN 50000000 /* unit: HZ */
|
||||
#include "dw_mmc-exynos.h"
|
||||
|
||||
/* Variations in Exynos specific dw-mshc controller */
|
||||
enum dw_mci_exynos_type {
|
||||
@ -114,11 +78,11 @@ static int dw_mci_exynos_priv_init(struct dw_mci *host)
|
||||
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420_SMU ||
|
||||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) {
|
||||
mci_writel(host, MPSBEGIN0, 0);
|
||||
mci_writel(host, MPSEND0, DWMCI_BLOCK_NUM);
|
||||
mci_writel(host, MPSCTRL0, DWMCI_MPSCTRL_SECURE_WRITE_BIT |
|
||||
DWMCI_MPSCTRL_NON_SECURE_READ_BIT |
|
||||
DWMCI_MPSCTRL_VALID |
|
||||
DWMCI_MPSCTRL_NON_SECURE_WRITE_BIT);
|
||||
mci_writel(host, MPSEND0, SDMMC_ENDING_SEC_NR_MAX);
|
||||
mci_writel(host, MPSCTRL0, SDMMC_MPSCTRL_SECURE_WRITE_BIT |
|
||||
SDMMC_MPSCTRL_NON_SECURE_READ_BIT |
|
||||
SDMMC_MPSCTRL_VALID |
|
||||
SDMMC_MPSCTRL_NON_SECURE_WRITE_BIT);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -127,9 +91,9 @@ static int dw_mci_exynos_priv_init(struct dw_mci *host)
|
||||
static int dw_mci_exynos_setup_clock(struct dw_mci *host)
|
||||
{
|
||||
struct dw_mci_exynos_priv_data *priv = host->priv;
|
||||
unsigned long rate = clk_get_rate(host->ciu_clk);
|
||||
|
||||
host->bus_hz = rate / (priv->ciu_div + 1);
|
||||
host->bus_hz /= (priv->ciu_div + 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -232,8 +196,11 @@ static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios)
|
||||
mci_writel(host, CLKSEL, priv->sdr_timing);
|
||||
}
|
||||
|
||||
/* Don't care if wanted clock is zero */
|
||||
if (!wanted)
|
||||
/*
|
||||
* Don't care if wanted clock is zero or
|
||||
* ciu clock is unavailable
|
||||
*/
|
||||
if (!wanted || IS_ERR(host->ciu_clk))
|
||||
return;
|
||||
|
||||
/* Guaranteed minimum frequency for cclkin */
|
||||
@ -263,10 +230,8 @@ static int dw_mci_exynos_parse_dt(struct dw_mci *host)
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
dev_err(host->dev, "mem alloc failed for private data\n");
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) {
|
||||
if (of_device_is_compatible(np, exynos_compat[idx].compatible))
|
||||
@ -375,64 +340,23 @@ out:
|
||||
return loc;
|
||||
}
|
||||
|
||||
static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode,
|
||||
struct dw_mci_tuning_data *tuning_data)
|
||||
static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot)
|
||||
{
|
||||
struct dw_mci *host = slot->host;
|
||||
struct mmc_host *mmc = slot->mmc;
|
||||
const u8 *blk_pattern = tuning_data->blk_pattern;
|
||||
u8 *blk_test;
|
||||
unsigned int blksz = tuning_data->blksz;
|
||||
u8 start_smpl, smpl, candiates = 0;
|
||||
s8 found = -1;
|
||||
int ret = 0;
|
||||
|
||||
blk_test = kmalloc(blksz, GFP_KERNEL);
|
||||
if (!blk_test)
|
||||
return -ENOMEM;
|
||||
|
||||
start_smpl = dw_mci_exynos_get_clksmpl(host);
|
||||
|
||||
do {
|
||||
struct mmc_request mrq = {NULL};
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command stop = {0};
|
||||
struct mmc_data data = {0};
|
||||
struct scatterlist sg;
|
||||
|
||||
cmd.opcode = opcode;
|
||||
cmd.arg = 0;
|
||||
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
|
||||
|
||||
stop.opcode = MMC_STOP_TRANSMISSION;
|
||||
stop.arg = 0;
|
||||
stop.flags = MMC_RSP_R1B | MMC_CMD_AC;
|
||||
|
||||
data.blksz = blksz;
|
||||
data.blocks = 1;
|
||||
data.flags = MMC_DATA_READ;
|
||||
data.sg = &sg;
|
||||
data.sg_len = 1;
|
||||
|
||||
sg_init_one(&sg, blk_test, blksz);
|
||||
mrq.cmd = &cmd;
|
||||
mrq.stop = &stop;
|
||||
mrq.data = &data;
|
||||
host->mrq = &mrq;
|
||||
|
||||
mci_writel(host, TMOUT, ~0);
|
||||
smpl = dw_mci_exynos_move_next_clksmpl(host);
|
||||
|
||||
mmc_wait_for_req(mmc, &mrq);
|
||||
|
||||
if (!cmd.error && !data.error) {
|
||||
if (!memcmp(blk_pattern, blk_test, blksz))
|
||||
if (!mmc_send_tuning(mmc))
|
||||
candiates |= (1 << smpl);
|
||||
} else {
|
||||
dev_dbg(host->dev,
|
||||
"Tuning error: cmd.error:%d, data.error:%d\n",
|
||||
cmd.error, data.error);
|
||||
}
|
||||
|
||||
} while (start_smpl != smpl);
|
||||
|
||||
found = dw_mci_exynos_get_best_clksmpl(candiates);
|
||||
@ -441,7 +365,6 @@ static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode,
|
||||
else
|
||||
ret = -EIO;
|
||||
|
||||
kfree(blk_test);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -499,7 +422,7 @@ static const struct dev_pm_ops dw_mci_exynos_pmops = {
|
||||
|
||||
static struct platform_driver dw_mci_exynos_pltfm_driver = {
|
||||
.probe = dw_mci_exynos_probe,
|
||||
.remove = __exit_p(dw_mci_pltfm_remove),
|
||||
.remove = dw_mci_pltfm_remove,
|
||||
.driver = {
|
||||
.name = "dwmmc_exynos",
|
||||
.of_match_table = dw_mci_exynos_match,
|
||||
|
56
drivers/mmc/host/dw_mmc-exynos.h
Normal file
56
drivers/mmc/host/dw_mmc-exynos.h
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Exynos Specific Extensions for Synopsys DW Multimedia Card Interface driver
|
||||
*
|
||||
* Copyright (C) 2012-2014 Samsung Electronics Co., Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _DW_MMC_EXYNOS_H_
|
||||
#define _DW_MMC_EXYNOS_H_
|
||||
|
||||
/* Extended Register's Offset */
|
||||
#define SDMMC_CLKSEL 0x09C
|
||||
#define SDMMC_CLKSEL64 0x0A8
|
||||
|
||||
/* CLKSEL register defines */
|
||||
#define SDMMC_CLKSEL_CCLK_SAMPLE(x) (((x) & 7) << 0)
|
||||
#define SDMMC_CLKSEL_CCLK_DRIVE(x) (((x) & 7) << 16)
|
||||
#define SDMMC_CLKSEL_CCLK_DIVIDER(x) (((x) & 7) << 24)
|
||||
#define SDMMC_CLKSEL_GET_DRV_WD3(x) (((x) >> 16) & 0x7)
|
||||
#define SDMMC_CLKSEL_TIMING(x, y, z) (SDMMC_CLKSEL_CCLK_SAMPLE(x) | \
|
||||
SDMMC_CLKSEL_CCLK_DRIVE(y) | \
|
||||
SDMMC_CLKSEL_CCLK_DIVIDER(z))
|
||||
#define SDMMC_CLKSEL_WAKEUP_INT BIT(11)
|
||||
|
||||
/* Protector Register */
|
||||
#define SDMMC_EMMCP_BASE 0x1000
|
||||
#define SDMMC_MPSECURITY (SDMMC_EMMCP_BASE + 0x0010)
|
||||
#define SDMMC_MPSBEGIN0 (SDMMC_EMMCP_BASE + 0x0200)
|
||||
#define SDMMC_MPSEND0 (SDMMC_EMMCP_BASE + 0x0204)
|
||||
#define SDMMC_MPSCTRL0 (SDMMC_EMMCP_BASE + 0x020C)
|
||||
|
||||
/* SMU control defines */
|
||||
#define SDMMC_MPSCTRL_SECURE_READ_BIT BIT(7)
|
||||
#define SDMMC_MPSCTRL_SECURE_WRITE_BIT BIT(6)
|
||||
#define SDMMC_MPSCTRL_NON_SECURE_READ_BIT BIT(5)
|
||||
#define SDMMC_MPSCTRL_NON_SECURE_WRITE_BIT BIT(4)
|
||||
#define SDMMC_MPSCTRL_USE_FUSE_KEY BIT(3)
|
||||
#define SDMMC_MPSCTRL_ECB_MODE BIT(2)
|
||||
#define SDMMC_MPSCTRL_ENCRYPTION BIT(1)
|
||||
#define SDMMC_MPSCTRL_VALID BIT(0)
|
||||
|
||||
/* Maximum number of Ending sector */
|
||||
#define SDMMC_ENDING_SEC_NR_MAX 0xFFFFFFFF
|
||||
|
||||
/* Fixed clock divider */
|
||||
#define EXYNOS4210_FIXED_CIU_CLK_DIV 2
|
||||
#define EXYNOS4412_FIXED_CIU_CLK_DIV 4
|
||||
|
||||
/* Minimal required clock frequency for cclkin, unit: HZ */
|
||||
#define EXYNOS_CCLKIN_MIN 50000000
|
||||
|
||||
#endif /* _DW_MMC_EXYNOS_H_ */
|
@ -133,7 +133,7 @@ static SIMPLE_DEV_PM_OPS(dw_mci_rockchip_pmops,
|
||||
|
||||
static struct platform_driver dw_mci_rockchip_pltfm_driver = {
|
||||
.probe = dw_mci_rockchip_probe,
|
||||
.remove = __exit_p(dw_mci_pltfm_remove),
|
||||
.remove = dw_mci_pltfm_remove,
|
||||
.driver = {
|
||||
.name = "dwmmc_rockchip",
|
||||
.of_match_table = dw_mci_rockchip_match,
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <linux/stat.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/mmc/card.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/mmc/sd.h>
|
||||
@ -313,7 +314,9 @@ static u32 dw_mci_prep_stop_abort(struct dw_mci *host, struct mmc_command *cmd)
|
||||
if (cmdr == MMC_READ_SINGLE_BLOCK ||
|
||||
cmdr == MMC_READ_MULTIPLE_BLOCK ||
|
||||
cmdr == MMC_WRITE_BLOCK ||
|
||||
cmdr == MMC_WRITE_MULTIPLE_BLOCK) {
|
||||
cmdr == MMC_WRITE_MULTIPLE_BLOCK ||
|
||||
cmdr == MMC_SEND_TUNING_BLOCK ||
|
||||
cmdr == MMC_SEND_TUNING_BLOCK_HS200) {
|
||||
stop->opcode = MMC_STOP_TRANSMISSION;
|
||||
stop->arg = 0;
|
||||
stop->flags = MMC_RSP_R1B | MMC_CMD_AC;
|
||||
@ -758,6 +761,7 @@ disable:
|
||||
|
||||
static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
|
||||
{
|
||||
unsigned long irqflags;
|
||||
int sg_len;
|
||||
u32 temp;
|
||||
|
||||
@ -794,9 +798,11 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
|
||||
mci_writel(host, CTRL, temp);
|
||||
|
||||
/* Disable RX/TX IRQs, let DMA handle it */
|
||||
spin_lock_irqsave(&host->irq_lock, irqflags);
|
||||
temp = mci_readl(host, INTMASK);
|
||||
temp &= ~(SDMMC_INT_RXDR | SDMMC_INT_TXDR);
|
||||
mci_writel(host, INTMASK, temp);
|
||||
spin_unlock_irqrestore(&host->irq_lock, irqflags);
|
||||
|
||||
host->dma_ops->start(host, sg_len);
|
||||
|
||||
@ -805,6 +811,7 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
|
||||
|
||||
static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
|
||||
{
|
||||
unsigned long irqflags;
|
||||
u32 temp;
|
||||
|
||||
data->error = -EINPROGRESS;
|
||||
@ -833,9 +840,12 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
|
||||
host->part_buf_count = 0;
|
||||
|
||||
mci_writel(host, RINTSTS, SDMMC_INT_TXDR | SDMMC_INT_RXDR);
|
||||
|
||||
spin_lock_irqsave(&host->irq_lock, irqflags);
|
||||
temp = mci_readl(host, INTMASK);
|
||||
temp |= SDMMC_INT_TXDR | SDMMC_INT_RXDR;
|
||||
mci_writel(host, INTMASK, temp);
|
||||
spin_unlock_irqrestore(&host->irq_lock, irqflags);
|
||||
|
||||
temp = mci_readl(host, CTRL);
|
||||
temp &= ~SDMMC_CTRL_DMA_ENABLE;
|
||||
@ -926,7 +936,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
|
||||
|
||||
/* enable clock; only low power if no SDIO */
|
||||
clk_en_a = SDMMC_CLKEN_ENABLE << slot->id;
|
||||
if (!(mci_readl(host, INTMASK) & SDMMC_INT_SDIO(slot->sdio_id)))
|
||||
if (!test_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags))
|
||||
clk_en_a |= SDMMC_CLKEN_LOW_PWR << slot->id;
|
||||
mci_writel(host, CLKENA, clk_en_a);
|
||||
|
||||
@ -1109,6 +1119,12 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
return;
|
||||
}
|
||||
}
|
||||
set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags);
|
||||
regs = mci_readl(slot->host, PWREN);
|
||||
regs |= (1 << slot->id);
|
||||
mci_writel(slot->host, PWREN, regs);
|
||||
break;
|
||||
case MMC_POWER_ON:
|
||||
if (!IS_ERR(mmc->supply.vqmmc) && !slot->host->vqmmc_enabled) {
|
||||
ret = regulator_enable(mmc->supply.vqmmc);
|
||||
if (ret < 0)
|
||||
@ -1117,10 +1133,6 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
else
|
||||
slot->host->vqmmc_enabled = true;
|
||||
}
|
||||
set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags);
|
||||
regs = mci_readl(slot->host, PWREN);
|
||||
regs |= (1 << slot->id);
|
||||
mci_writel(slot->host, PWREN, regs);
|
||||
break;
|
||||
case MMC_POWER_OFF:
|
||||
if (!IS_ERR(mmc->supply.vmmc))
|
||||
@ -1245,53 +1257,58 @@ static int dw_mci_get_cd(struct mmc_host *mmc)
|
||||
return present;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable lower power mode.
|
||||
*
|
||||
static void dw_mci_init_card(struct mmc_host *mmc, struct mmc_card *card)
|
||||
{
|
||||
struct dw_mci_slot *slot = mmc_priv(mmc);
|
||||
struct dw_mci *host = slot->host;
|
||||
|
||||
/*
|
||||
* Low power mode will stop the card clock when idle. According to the
|
||||
* description of the CLKENA register we should disable low power mode
|
||||
* for SDIO cards if we need SDIO interrupts to work.
|
||||
*
|
||||
* This function is fast if low power mode is already disabled.
|
||||
*/
|
||||
static void dw_mci_disable_low_power(struct dw_mci_slot *slot)
|
||||
{
|
||||
struct dw_mci *host = slot->host;
|
||||
u32 clk_en_a;
|
||||
if (mmc->caps & MMC_CAP_SDIO_IRQ) {
|
||||
const u32 clken_low_pwr = SDMMC_CLKEN_LOW_PWR << slot->id;
|
||||
u32 clk_en_a_old;
|
||||
u32 clk_en_a;
|
||||
|
||||
clk_en_a = mci_readl(host, CLKENA);
|
||||
clk_en_a_old = mci_readl(host, CLKENA);
|
||||
|
||||
if (clk_en_a & clken_low_pwr) {
|
||||
mci_writel(host, CLKENA, clk_en_a & ~clken_low_pwr);
|
||||
if (card->type == MMC_TYPE_SDIO ||
|
||||
card->type == MMC_TYPE_SD_COMBO) {
|
||||
set_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags);
|
||||
clk_en_a = clk_en_a_old & ~clken_low_pwr;
|
||||
} else {
|
||||
clear_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags);
|
||||
clk_en_a = clk_en_a_old | clken_low_pwr;
|
||||
}
|
||||
|
||||
if (clk_en_a != clk_en_a_old) {
|
||||
mci_writel(host, CLKENA, clk_en_a);
|
||||
mci_send_cmd(slot, SDMMC_CMD_UPD_CLK |
|
||||
SDMMC_CMD_PRV_DAT_WAIT, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb)
|
||||
{
|
||||
struct dw_mci_slot *slot = mmc_priv(mmc);
|
||||
struct dw_mci *host = slot->host;
|
||||
unsigned long irqflags;
|
||||
u32 int_mask;
|
||||
|
||||
spin_lock_irqsave(&host->irq_lock, irqflags);
|
||||
|
||||
/* Enable/disable Slot Specific SDIO interrupt */
|
||||
int_mask = mci_readl(host, INTMASK);
|
||||
if (enb) {
|
||||
/*
|
||||
* Turn off low power mode if it was enabled. This is a bit of
|
||||
* a heavy operation and we disable / enable IRQs a lot, so
|
||||
* we'll leave low power mode disabled and it will get
|
||||
* re-enabled again in dw_mci_setup_bus().
|
||||
*/
|
||||
dw_mci_disable_low_power(slot);
|
||||
if (enb)
|
||||
int_mask |= SDMMC_INT_SDIO(slot->sdio_id);
|
||||
else
|
||||
int_mask &= ~SDMMC_INT_SDIO(slot->sdio_id);
|
||||
mci_writel(host, INTMASK, int_mask);
|
||||
|
||||
mci_writel(host, INTMASK,
|
||||
(int_mask | SDMMC_INT_SDIO(slot->sdio_id)));
|
||||
} else {
|
||||
mci_writel(host, INTMASK,
|
||||
(int_mask & ~SDMMC_INT_SDIO(slot->sdio_id)));
|
||||
}
|
||||
spin_unlock_irqrestore(&host->irq_lock, irqflags);
|
||||
}
|
||||
|
||||
static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
||||
@ -1299,30 +1316,10 @@ static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
||||
struct dw_mci_slot *slot = mmc_priv(mmc);
|
||||
struct dw_mci *host = slot->host;
|
||||
const struct dw_mci_drv_data *drv_data = host->drv_data;
|
||||
struct dw_mci_tuning_data tuning_data;
|
||||
int err = -ENOSYS;
|
||||
|
||||
if (opcode == MMC_SEND_TUNING_BLOCK_HS200) {
|
||||
if (mmc->ios.bus_width == MMC_BUS_WIDTH_8) {
|
||||
tuning_data.blk_pattern = tuning_blk_pattern_8bit;
|
||||
tuning_data.blksz = sizeof(tuning_blk_pattern_8bit);
|
||||
} else if (mmc->ios.bus_width == MMC_BUS_WIDTH_4) {
|
||||
tuning_data.blk_pattern = tuning_blk_pattern_4bit;
|
||||
tuning_data.blksz = sizeof(tuning_blk_pattern_4bit);
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (opcode == MMC_SEND_TUNING_BLOCK) {
|
||||
tuning_data.blk_pattern = tuning_blk_pattern_4bit;
|
||||
tuning_data.blksz = sizeof(tuning_blk_pattern_4bit);
|
||||
} else {
|
||||
dev_err(host->dev,
|
||||
"Undefined command(%d) for tuning\n", opcode);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (drv_data && drv_data->execute_tuning)
|
||||
err = drv_data->execute_tuning(slot, opcode, &tuning_data);
|
||||
err = drv_data->execute_tuning(slot);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -1337,7 +1334,7 @@ static const struct mmc_host_ops dw_mci_ops = {
|
||||
.execute_tuning = dw_mci_execute_tuning,
|
||||
.card_busy = dw_mci_card_busy,
|
||||
.start_signal_voltage_switch = dw_mci_switch_voltage,
|
||||
|
||||
.init_card = dw_mci_init_card,
|
||||
};
|
||||
|
||||
static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
|
||||
@ -2319,9 +2316,9 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
||||
#ifdef CONFIG_MMC_DW_IDMAC
|
||||
mmc->max_segs = host->ring_size;
|
||||
mmc->max_blk_size = 65536;
|
||||
mmc->max_blk_count = host->ring_size;
|
||||
mmc->max_seg_size = 0x1000;
|
||||
mmc->max_req_size = mmc->max_seg_size * mmc->max_blk_count;
|
||||
mmc->max_req_size = mmc->max_seg_size * host->ring_size;
|
||||
mmc->max_blk_count = mmc->max_req_size / 512;
|
||||
#else
|
||||
mmc->max_segs = 64;
|
||||
mmc->max_blk_size = 65536; /* BLKSIZ is 16 bits */
|
||||
@ -2533,10 +2530,8 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
|
||||
u32 clock_frequency;
|
||||
|
||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata) {
|
||||
dev_err(dev, "could not allocate memory for pdata\n");
|
||||
if (!pdata)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
/* find out number of slots supported */
|
||||
if (of_property_read_u32(dev->of_node, "num-slots",
|
||||
@ -2660,6 +2655,7 @@ int dw_mci_probe(struct dw_mci *host)
|
||||
host->quirks = host->pdata->quirks;
|
||||
|
||||
spin_lock_init(&host->lock);
|
||||
spin_lock_init(&host->irq_lock);
|
||||
INIT_LIST_HEAD(&host->queue);
|
||||
|
||||
/*
|
||||
|
@ -244,15 +244,11 @@ struct dw_mci_slot {
|
||||
unsigned long flags;
|
||||
#define DW_MMC_CARD_PRESENT 0
|
||||
#define DW_MMC_CARD_NEED_INIT 1
|
||||
#define DW_MMC_CARD_NO_LOW_PWR 2
|
||||
int id;
|
||||
int sdio_id;
|
||||
};
|
||||
|
||||
struct dw_mci_tuning_data {
|
||||
const u8 *blk_pattern;
|
||||
unsigned int blksz;
|
||||
};
|
||||
|
||||
/**
|
||||
* dw_mci driver data - dw-mshc implementation specific driver data.
|
||||
* @caps: mmc subsystem specified capabilities of the controller(s).
|
||||
@ -274,7 +270,6 @@ struct dw_mci_drv_data {
|
||||
void (*prepare_command)(struct dw_mci *host, u32 *cmdr);
|
||||
void (*set_ios)(struct dw_mci *host, struct mmc_ios *ios);
|
||||
int (*parse_dt)(struct dw_mci *host);
|
||||
int (*execute_tuning)(struct dw_mci_slot *slot, u32 opcode,
|
||||
struct dw_mci_tuning_data *tuning_data);
|
||||
int (*execute_tuning)(struct dw_mci_slot *slot);
|
||||
};
|
||||
#endif /* _DW_MMC_H_ */
|
||||
|
@ -430,7 +430,6 @@ static void mmci_init_sg(struct mmci_host *host, struct mmc_data *data)
|
||||
static void mmci_dma_setup(struct mmci_host *host)
|
||||
{
|
||||
const char *rxname, *txname;
|
||||
dma_cap_mask_t mask;
|
||||
struct variant_data *variant = host->variant;
|
||||
|
||||
host->dma_rx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "rx");
|
||||
@ -439,10 +438,6 @@ static void mmci_dma_setup(struct mmci_host *host)
|
||||
/* initialize pre request cookie */
|
||||
host->next_data.cookie = 1;
|
||||
|
||||
/* Try to acquire a generic DMA engine slave channel */
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
|
||||
/*
|
||||
* If only an RX channel is specified, the driver will
|
||||
* attempt to use it bidirectionally, however if it is
|
||||
@ -1739,10 +1734,10 @@ static int mmci_probe(struct amba_device *dev,
|
||||
|
||||
pm_runtime_set_autosuspend_delay(&dev->dev, 50);
|
||||
pm_runtime_use_autosuspend(&dev->dev);
|
||||
pm_runtime_put(&dev->dev);
|
||||
|
||||
mmc_add_host(mmc);
|
||||
|
||||
pm_runtime_put(&dev->dev);
|
||||
return 0;
|
||||
|
||||
clk_disable:
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
@ -562,7 +563,6 @@ static int moxart_probe(struct platform_device *pdev)
|
||||
struct dma_slave_config cfg;
|
||||
struct clk *clk;
|
||||
void __iomem *reg_mmc;
|
||||
dma_cap_mask_t mask;
|
||||
int irq, ret;
|
||||
u32 i;
|
||||
|
||||
@ -586,9 +586,8 @@ static int moxart_probe(struct platform_device *pdev)
|
||||
goto out;
|
||||
}
|
||||
|
||||
clk = of_clk_get(node, 0);
|
||||
clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(dev, "of_clk_get failed\n");
|
||||
ret = PTR_ERR(clk);
|
||||
goto out;
|
||||
}
|
||||
@ -599,10 +598,9 @@ static int moxart_probe(struct platform_device *pdev)
|
||||
goto out;
|
||||
}
|
||||
|
||||
mmc_of_parse(mmc);
|
||||
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
ret = mmc_of_parse(mmc);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
host = mmc_priv(mmc);
|
||||
host->mmc = mmc;
|
||||
@ -611,8 +609,8 @@ static int moxart_probe(struct platform_device *pdev)
|
||||
host->timeout = msecs_to_jiffies(1000);
|
||||
host->sysclk = clk_get_rate(clk);
|
||||
host->fifo_width = readl(host->base + REG_FEATURE) << 2;
|
||||
host->dma_chan_tx = of_dma_request_slave_channel(node, "tx");
|
||||
host->dma_chan_rx = of_dma_request_slave_channel(node, "rx");
|
||||
host->dma_chan_tx = dma_request_slave_channel_reason(dev, "tx");
|
||||
host->dma_chan_rx = dma_request_slave_channel_reason(dev, "rx");
|
||||
|
||||
spin_lock_init(&host->lock);
|
||||
|
||||
@ -622,6 +620,11 @@ static int moxart_probe(struct platform_device *pdev)
|
||||
mmc->ocr_avail = 0xffff00; /* Support 2.0v - 3.6v power. */
|
||||
|
||||
if (IS_ERR(host->dma_chan_tx) || IS_ERR(host->dma_chan_rx)) {
|
||||
if (PTR_ERR(host->dma_chan_tx) == -EPROBE_DEFER ||
|
||||
PTR_ERR(host->dma_chan_rx) == -EPROBE_DEFER) {
|
||||
ret = -EPROBE_DEFER;
|
||||
goto out;
|
||||
}
|
||||
dev_dbg(dev, "PIO mode transfer enabled\n");
|
||||
host->have_dma = false;
|
||||
} else {
|
||||
@ -700,9 +703,6 @@ static int moxart_remove(struct platform_device *pdev)
|
||||
writel(readl(host->base + REG_CLOCK_CONTROL) | CLK_OFF,
|
||||
host->base + REG_CLOCK_CONTROL);
|
||||
}
|
||||
|
||||
kfree(host);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,6 @@
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
|
||||
#include <asm/sizes.h>
|
||||
#include <asm/unaligned.h>
|
||||
@ -704,7 +703,6 @@ static int mvsd_probe(struct platform_device *pdev)
|
||||
const struct mbus_dram_target_info *dram;
|
||||
struct resource *r;
|
||||
int ret, irq;
|
||||
struct pinctrl *pinctrl;
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
@ -721,10 +719,6 @@ static int mvsd_probe(struct platform_device *pdev)
|
||||
host->mmc = mmc;
|
||||
host->dev = &pdev->dev;
|
||||
|
||||
pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
|
||||
if (IS_ERR(pinctrl))
|
||||
dev_warn(&pdev->dev, "no pins associated\n");
|
||||
|
||||
/*
|
||||
* Some non-DT platforms do not pass a clock, and the clock
|
||||
* frequency is passed through platform_data. On DT platforms,
|
||||
@ -828,8 +822,6 @@ static int mvsd_probe(struct platform_device *pdev)
|
||||
|
||||
out:
|
||||
if (mmc) {
|
||||
mmc_gpio_free_cd(mmc);
|
||||
mmc_gpio_free_ro(mmc);
|
||||
if (!IS_ERR(host->clk))
|
||||
clk_disable_unprepare(host->clk);
|
||||
mmc_free_host(mmc);
|
||||
@ -844,8 +836,6 @@ static int mvsd_remove(struct platform_device *pdev)
|
||||
|
||||
struct mvsd_host *host = mmc_priv(mmc);
|
||||
|
||||
mmc_gpio_free_cd(mmc);
|
||||
mmc_gpio_free_ro(mmc);
|
||||
mmc_remove_host(mmc);
|
||||
del_timer_sync(&host->timer);
|
||||
mvsd_power_down(host);
|
||||
|
@ -677,7 +677,6 @@ static int mxs_mmc_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
|
||||
out_free_dma:
|
||||
if (ssp->dmach)
|
||||
dma_release_channel(ssp->dmach);
|
||||
out_clk_disable:
|
||||
clk_disable_unprepare(ssp->clk);
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/core.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/gpio.h>
|
||||
@ -251,55 +252,24 @@ static void omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host);
|
||||
static int omap_hsmmc_card_detect(struct device *dev)
|
||||
{
|
||||
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
|
||||
struct omap_hsmmc_platform_data *mmc = host->pdata;
|
||||
|
||||
/* NOTE: assumes card detect signal is active-low */
|
||||
return !gpio_get_value_cansleep(mmc->switch_pin);
|
||||
return mmc_gpio_get_cd(host->mmc);
|
||||
}
|
||||
|
||||
static int omap_hsmmc_get_wp(struct device *dev)
|
||||
{
|
||||
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
|
||||
struct omap_hsmmc_platform_data *mmc = host->pdata;
|
||||
|
||||
/* NOTE: assumes write protect signal is active-high */
|
||||
return gpio_get_value_cansleep(mmc->gpio_wp);
|
||||
return mmc_gpio_get_ro(host->mmc);
|
||||
}
|
||||
|
||||
static int omap_hsmmc_get_cover_state(struct device *dev)
|
||||
{
|
||||
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
|
||||
struct omap_hsmmc_platform_data *mmc = host->pdata;
|
||||
|
||||
/* NOTE: assumes card detect signal is active-low */
|
||||
return !gpio_get_value_cansleep(mmc->switch_pin);
|
||||
return mmc_gpio_get_cd(host->mmc);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int omap_hsmmc_suspend_cdirq(struct device *dev)
|
||||
{
|
||||
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
|
||||
|
||||
disable_irq(host->card_detect_irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omap_hsmmc_resume_cdirq(struct device *dev)
|
||||
{
|
||||
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
|
||||
|
||||
enable_irq(host->card_detect_irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define omap_hsmmc_suspend_cdirq NULL
|
||||
#define omap_hsmmc_resume_cdirq NULL
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_REGULATOR
|
||||
|
||||
static int omap_hsmmc_set_power(struct device *dev, int power_on, int vdd)
|
||||
@ -464,7 +434,10 @@ static inline int omap_hsmmc_have_reg(void)
|
||||
|
||||
#endif
|
||||
|
||||
static int omap_hsmmc_gpio_init(struct omap_hsmmc_host *host,
|
||||
static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id);
|
||||
|
||||
static int omap_hsmmc_gpio_init(struct mmc_host *mmc,
|
||||
struct omap_hsmmc_host *host,
|
||||
struct omap_hsmmc_platform_data *pdata)
|
||||
{
|
||||
int ret;
|
||||
@ -477,46 +450,24 @@ static int omap_hsmmc_gpio_init(struct omap_hsmmc_host *host,
|
||||
host->card_detect = omap_hsmmc_card_detect;
|
||||
host->card_detect_irq =
|
||||
gpio_to_irq(pdata->switch_pin);
|
||||
ret = gpio_request(pdata->switch_pin, "mmc_cd");
|
||||
mmc_gpio_set_cd_isr(mmc, omap_hsmmc_detect);
|
||||
ret = mmc_gpio_request_cd(mmc, pdata->switch_pin, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = gpio_direction_input(pdata->switch_pin);
|
||||
if (ret)
|
||||
goto err_free_sp;
|
||||
} else {
|
||||
pdata->switch_pin = -EINVAL;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(pdata->gpio_wp)) {
|
||||
host->get_ro = omap_hsmmc_get_wp;
|
||||
ret = gpio_request(pdata->gpio_wp, "mmc_wp");
|
||||
ret = mmc_gpio_request_ro(mmc, pdata->gpio_wp);
|
||||
if (ret)
|
||||
goto err_free_cd;
|
||||
ret = gpio_direction_input(pdata->gpio_wp);
|
||||
if (ret)
|
||||
goto err_free_wp;
|
||||
return ret;
|
||||
} else {
|
||||
pdata->gpio_wp = -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_wp:
|
||||
gpio_free(pdata->gpio_wp);
|
||||
err_free_cd:
|
||||
if (gpio_is_valid(pdata->switch_pin))
|
||||
err_free_sp:
|
||||
gpio_free(pdata->switch_pin);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void omap_hsmmc_gpio_free(struct omap_hsmmc_host *host,
|
||||
struct omap_hsmmc_platform_data *pdata)
|
||||
{
|
||||
if (gpio_is_valid(pdata->gpio_wp))
|
||||
gpio_free(pdata->gpio_wp);
|
||||
if (gpio_is_valid(pdata->switch_pin))
|
||||
gpio_free(pdata->switch_pin);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1978,13 +1929,6 @@ static struct omap_hsmmc_platform_data *of_get_hsmmc_pdata(struct device *dev)
|
||||
{
|
||||
struct omap_hsmmc_platform_data *pdata;
|
||||
struct device_node *np = dev->of_node;
|
||||
u32 bus_width, max_freq;
|
||||
int cd_gpio, wp_gpio;
|
||||
|
||||
cd_gpio = of_get_named_gpio(np, "cd-gpios", 0);
|
||||
wp_gpio = of_get_named_gpio(np, "wp-gpios", 0);
|
||||
if (cd_gpio == -EPROBE_DEFER || wp_gpio == -EPROBE_DEFER)
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
@ -1993,34 +1937,20 @@ static struct omap_hsmmc_platform_data *of_get_hsmmc_pdata(struct device *dev)
|
||||
if (of_find_property(np, "ti,dual-volt", NULL))
|
||||
pdata->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT;
|
||||
|
||||
pdata->switch_pin = cd_gpio;
|
||||
pdata->gpio_wp = wp_gpio;
|
||||
pdata->switch_pin = -EINVAL;
|
||||
pdata->gpio_wp = -EINVAL;
|
||||
|
||||
if (of_find_property(np, "ti,non-removable", NULL)) {
|
||||
pdata->nonremovable = true;
|
||||
pdata->no_regulator_off_init = true;
|
||||
}
|
||||
of_property_read_u32(np, "bus-width", &bus_width);
|
||||
if (bus_width == 4)
|
||||
pdata->caps |= MMC_CAP_4_BIT_DATA;
|
||||
else if (bus_width == 8)
|
||||
pdata->caps |= MMC_CAP_8_BIT_DATA;
|
||||
|
||||
if (of_find_property(np, "ti,needs-special-reset", NULL))
|
||||
pdata->features |= HSMMC_HAS_UPDATED_RESET;
|
||||
|
||||
if (!of_property_read_u32(np, "max-frequency", &max_freq))
|
||||
pdata->max_freq = max_freq;
|
||||
|
||||
if (of_find_property(np, "ti,needs-special-hs-handling", NULL))
|
||||
pdata->features |= HSMMC_HAS_HSPE_SUPPORT;
|
||||
|
||||
if (of_find_property(np, "keep-power-in-suspend", NULL))
|
||||
pdata->pm_caps |= MMC_PM_KEEP_POWER;
|
||||
|
||||
if (of_find_property(np, "enable-sdio-wakeup", NULL))
|
||||
pdata->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
|
||||
|
||||
return pdata;
|
||||
}
|
||||
#else
|
||||
@ -2078,6 +2008,10 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = mmc_of_parse(mmc);
|
||||
if (ret)
|
||||
goto err1;
|
||||
|
||||
host = mmc_priv(mmc);
|
||||
host->mmc = mmc;
|
||||
host->pdata = pdata;
|
||||
@ -2091,7 +2025,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
||||
host->next_data.cookie = 1;
|
||||
host->pbias_enabled = 0;
|
||||
|
||||
ret = omap_hsmmc_gpio_init(host, pdata);
|
||||
ret = omap_hsmmc_gpio_init(mmc, host, pdata);
|
||||
if (ret)
|
||||
goto err_gpio;
|
||||
|
||||
@ -2106,7 +2040,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
||||
|
||||
if (pdata->max_freq > 0)
|
||||
mmc->f_max = pdata->max_freq;
|
||||
else
|
||||
else if (mmc->f_max == 0)
|
||||
mmc->f_max = OMAP_MMC_MAX_CLOCK;
|
||||
|
||||
spin_lock_init(&host->irq_lock);
|
||||
@ -2160,7 +2094,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
||||
if (mmc_pdata(host)->nonremovable)
|
||||
mmc->caps |= MMC_CAP_NONREMOVABLE;
|
||||
|
||||
mmc->pm_caps = mmc_pdata(host)->pm_caps;
|
||||
mmc->pm_caps |= mmc_pdata(host)->pm_caps;
|
||||
|
||||
omap_hsmmc_conf_bus_power(host);
|
||||
|
||||
@ -2222,22 +2156,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
||||
|
||||
mmc->ocr_avail = mmc_pdata(host)->ocr_mask;
|
||||
|
||||
/* Request IRQ for card detect */
|
||||
if (host->card_detect_irq) {
|
||||
ret = devm_request_threaded_irq(&pdev->dev,
|
||||
host->card_detect_irq,
|
||||
NULL, omap_hsmmc_detect,
|
||||
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
mmc_hostname(mmc), host);
|
||||
if (ret) {
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"Unable to grab MMC CD IRQ\n");
|
||||
goto err_irq_cd;
|
||||
}
|
||||
host->suspend = omap_hsmmc_suspend_cdirq;
|
||||
host->resume = omap_hsmmc_resume_cdirq;
|
||||
}
|
||||
|
||||
omap_hsmmc_disable_irq(host);
|
||||
|
||||
/*
|
||||
@ -2276,7 +2194,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
||||
|
||||
err_slot_name:
|
||||
mmc_remove_host(mmc);
|
||||
err_irq_cd:
|
||||
if (host->use_reg)
|
||||
omap_hsmmc_reg_put(host);
|
||||
err_irq:
|
||||
@ -2289,7 +2206,6 @@ err_irq:
|
||||
if (host->dbclk)
|
||||
clk_disable_unprepare(host->dbclk);
|
||||
err1:
|
||||
omap_hsmmc_gpio_free(host, pdata);
|
||||
err_gpio:
|
||||
mmc_free_host(mmc);
|
||||
err:
|
||||
@ -2315,32 +2231,12 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
|
||||
if (host->dbclk)
|
||||
clk_disable_unprepare(host->dbclk);
|
||||
|
||||
omap_hsmmc_gpio_free(host, host->pdata);
|
||||
mmc_free_host(host->mmc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int omap_hsmmc_prepare(struct device *dev)
|
||||
{
|
||||
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
|
||||
|
||||
if (host->suspend)
|
||||
return host->suspend(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void omap_hsmmc_complete(struct device *dev)
|
||||
{
|
||||
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
|
||||
|
||||
if (host->resume)
|
||||
host->resume(dev);
|
||||
|
||||
}
|
||||
|
||||
static int omap_hsmmc_suspend(struct device *dev)
|
||||
{
|
||||
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
|
||||
@ -2398,8 +2294,6 @@ static int omap_hsmmc_resume(struct device *dev)
|
||||
}
|
||||
|
||||
#else
|
||||
#define omap_hsmmc_prepare NULL
|
||||
#define omap_hsmmc_complete NULL
|
||||
#define omap_hsmmc_suspend NULL
|
||||
#define omap_hsmmc_resume NULL
|
||||
#endif
|
||||
@ -2484,8 +2378,6 @@ static int omap_hsmmc_runtime_resume(struct device *dev)
|
||||
static struct dev_pm_ops omap_hsmmc_dev_pm_ops = {
|
||||
.suspend = omap_hsmmc_suspend,
|
||||
.resume = omap_hsmmc_resume,
|
||||
.prepare = omap_hsmmc_prepare,
|
||||
.complete = omap_hsmmc_complete,
|
||||
.runtime_suspend = omap_hsmmc_runtime_suspend,
|
||||
.runtime_resume = omap_hsmmc_runtime_resume,
|
||||
};
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/mmc/sd.h>
|
||||
#include <linux/mmc/sdio.h>
|
||||
#include <linux/mmc/card.h>
|
||||
#include <linux/mfd/rtsx_pci.h>
|
||||
#include <asm/unaligned.h>
|
||||
@ -53,9 +54,9 @@ struct realtek_pci_sdmmc {
|
||||
#define SDMMC_POWER_ON 1
|
||||
#define SDMMC_POWER_OFF 0
|
||||
|
||||
unsigned int sg_count;
|
||||
int sg_count;
|
||||
s32 cookie;
|
||||
unsigned int cookie_sg_count;
|
||||
int cookie_sg_count;
|
||||
bool using_cookie;
|
||||
};
|
||||
|
||||
@ -71,30 +72,83 @@ static inline void sd_clear_error(struct realtek_pci_sdmmc *host)
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static void dump_reg_range(struct realtek_pci_sdmmc *host, u16 start, u16 end)
|
||||
{
|
||||
u16 len = end - start + 1;
|
||||
int i;
|
||||
u8 data[8];
|
||||
|
||||
for (i = 0; i < len; i += 8) {
|
||||
int j;
|
||||
int n = min(8, len - i);
|
||||
|
||||
memset(&data, 0, sizeof(data));
|
||||
for (j = 0; j < n; j++)
|
||||
rtsx_pci_read_register(host->pcr, start + i + j,
|
||||
data + j);
|
||||
dev_dbg(sdmmc_dev(host), "0x%04X(%d): %8ph\n",
|
||||
start + i, n, data);
|
||||
}
|
||||
}
|
||||
|
||||
static void sd_print_debug_regs(struct realtek_pci_sdmmc *host)
|
||||
{
|
||||
struct rtsx_pcr *pcr = host->pcr;
|
||||
u16 i;
|
||||
u8 *ptr;
|
||||
|
||||
/* Print SD host internal registers */
|
||||
rtsx_pci_init_cmd(pcr);
|
||||
for (i = 0xFDA0; i <= 0xFDAE; i++)
|
||||
rtsx_pci_add_cmd(pcr, READ_REG_CMD, i, 0, 0);
|
||||
for (i = 0xFD52; i <= 0xFD69; i++)
|
||||
rtsx_pci_add_cmd(pcr, READ_REG_CMD, i, 0, 0);
|
||||
rtsx_pci_send_cmd(pcr, 100);
|
||||
|
||||
ptr = rtsx_pci_get_cmd_data(pcr);
|
||||
for (i = 0xFDA0; i <= 0xFDAE; i++)
|
||||
dev_dbg(sdmmc_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++));
|
||||
for (i = 0xFD52; i <= 0xFD69; i++)
|
||||
dev_dbg(sdmmc_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++));
|
||||
dump_reg_range(host, 0xFDA0, 0xFDB3);
|
||||
dump_reg_range(host, 0xFD52, 0xFD69);
|
||||
}
|
||||
#else
|
||||
#define sd_print_debug_regs(host)
|
||||
#endif /* DEBUG */
|
||||
|
||||
static inline int sd_get_cd_int(struct realtek_pci_sdmmc *host)
|
||||
{
|
||||
return rtsx_pci_readl(host->pcr, RTSX_BIPR) & SD_EXIST;
|
||||
}
|
||||
|
||||
static void sd_cmd_set_sd_cmd(struct rtsx_pcr *pcr, struct mmc_command *cmd)
|
||||
{
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD0, 0xFF,
|
||||
SD_CMD_START | cmd->opcode);
|
||||
rtsx_pci_write_be32(pcr, SD_CMD1, cmd->arg);
|
||||
}
|
||||
|
||||
static void sd_cmd_set_data_len(struct rtsx_pcr *pcr, u16 blocks, u16 blksz)
|
||||
{
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_L, 0xFF, blocks);
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_H, 0xFF, blocks >> 8);
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, blksz);
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_H, 0xFF, blksz >> 8);
|
||||
}
|
||||
|
||||
static int sd_response_type(struct mmc_command *cmd)
|
||||
{
|
||||
switch (mmc_resp_type(cmd)) {
|
||||
case MMC_RSP_NONE:
|
||||
return SD_RSP_TYPE_R0;
|
||||
case MMC_RSP_R1:
|
||||
return SD_RSP_TYPE_R1;
|
||||
case MMC_RSP_R1 & ~MMC_RSP_CRC:
|
||||
return SD_RSP_TYPE_R1 | SD_NO_CHECK_CRC7;
|
||||
case MMC_RSP_R1B:
|
||||
return SD_RSP_TYPE_R1b;
|
||||
case MMC_RSP_R2:
|
||||
return SD_RSP_TYPE_R2;
|
||||
case MMC_RSP_R3:
|
||||
return SD_RSP_TYPE_R3;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int sd_status_index(int resp_type)
|
||||
{
|
||||
if (resp_type == SD_RSP_TYPE_R0)
|
||||
return 0;
|
||||
else if (resp_type == SD_RSP_TYPE_R2)
|
||||
return 16;
|
||||
|
||||
return 5;
|
||||
}
|
||||
/*
|
||||
* sd_pre_dma_transfer - do dma_map_sg() or using cookie
|
||||
*
|
||||
@ -166,123 +220,6 @@ static void sdmmc_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
|
||||
data->host_cookie = 0;
|
||||
}
|
||||
|
||||
static int sd_read_data(struct realtek_pci_sdmmc *host, u8 *cmd, u16 byte_cnt,
|
||||
u8 *buf, int buf_len, int timeout)
|
||||
{
|
||||
struct rtsx_pcr *pcr = host->pcr;
|
||||
int err, i;
|
||||
u8 trans_mode;
|
||||
|
||||
dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD%d\n", __func__, cmd[0] - 0x40);
|
||||
|
||||
if (!buf)
|
||||
buf_len = 0;
|
||||
|
||||
if ((cmd[0] & 0x3F) == MMC_SEND_TUNING_BLOCK)
|
||||
trans_mode = SD_TM_AUTO_TUNING;
|
||||
else
|
||||
trans_mode = SD_TM_NORMAL_READ;
|
||||
|
||||
rtsx_pci_init_cmd(pcr);
|
||||
|
||||
for (i = 0; i < 5; i++)
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD0 + i, 0xFF, cmd[i]);
|
||||
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, (u8)byte_cnt);
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_H,
|
||||
0xFF, (u8)(byte_cnt >> 8));
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_L, 0xFF, 1);
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_H, 0xFF, 0);
|
||||
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF,
|
||||
SD_CALCULATE_CRC7 | SD_CHECK_CRC16 |
|
||||
SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_6);
|
||||
if (trans_mode != SD_TM_AUTO_TUNING)
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
|
||||
CARD_DATA_SOURCE, 0x01, PINGPONG_BUFFER);
|
||||
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER,
|
||||
0xFF, trans_mode | SD_TRANSFER_START);
|
||||
rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER,
|
||||
SD_TRANSFER_END, SD_TRANSFER_END);
|
||||
|
||||
err = rtsx_pci_send_cmd(pcr, timeout);
|
||||
if (err < 0) {
|
||||
sd_print_debug_regs(host);
|
||||
dev_dbg(sdmmc_dev(host),
|
||||
"rtsx_pci_send_cmd fail (err = %d)\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (buf && buf_len) {
|
||||
err = rtsx_pci_read_ppbuf(pcr, buf, buf_len);
|
||||
if (err < 0) {
|
||||
dev_dbg(sdmmc_dev(host),
|
||||
"rtsx_pci_read_ppbuf fail (err = %d)\n", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_write_data(struct realtek_pci_sdmmc *host, u8 *cmd, u16 byte_cnt,
|
||||
u8 *buf, int buf_len, int timeout)
|
||||
{
|
||||
struct rtsx_pcr *pcr = host->pcr;
|
||||
int err, i;
|
||||
u8 trans_mode;
|
||||
|
||||
if (!buf)
|
||||
buf_len = 0;
|
||||
|
||||
if (buf && buf_len) {
|
||||
err = rtsx_pci_write_ppbuf(pcr, buf, buf_len);
|
||||
if (err < 0) {
|
||||
dev_dbg(sdmmc_dev(host),
|
||||
"rtsx_pci_write_ppbuf fail (err = %d)\n", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
trans_mode = cmd ? SD_TM_AUTO_WRITE_2 : SD_TM_AUTO_WRITE_3;
|
||||
rtsx_pci_init_cmd(pcr);
|
||||
|
||||
if (cmd) {
|
||||
dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d\n", __func__,
|
||||
cmd[0] - 0x40);
|
||||
|
||||
for (i = 0; i < 5; i++)
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
|
||||
SD_CMD0 + i, 0xFF, cmd[i]);
|
||||
}
|
||||
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, (u8)byte_cnt);
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_H,
|
||||
0xFF, (u8)(byte_cnt >> 8));
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_L, 0xFF, 1);
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_H, 0xFF, 0);
|
||||
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF,
|
||||
SD_CALCULATE_CRC7 | SD_CHECK_CRC16 |
|
||||
SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_6);
|
||||
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, 0xFF,
|
||||
trans_mode | SD_TRANSFER_START);
|
||||
rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER,
|
||||
SD_TRANSFER_END, SD_TRANSFER_END);
|
||||
|
||||
err = rtsx_pci_send_cmd(pcr, timeout);
|
||||
if (err < 0) {
|
||||
sd_print_debug_regs(host);
|
||||
dev_dbg(sdmmc_dev(host),
|
||||
"rtsx_pci_send_cmd fail (err = %d)\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
|
||||
struct mmc_command *cmd)
|
||||
{
|
||||
@ -293,47 +230,18 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
|
||||
int timeout = 100;
|
||||
int i;
|
||||
u8 *ptr;
|
||||
int stat_idx = 0;
|
||||
u8 rsp_type;
|
||||
int rsp_len = 5;
|
||||
int rsp_type;
|
||||
int stat_idx;
|
||||
bool clock_toggled = false;
|
||||
|
||||
dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n",
|
||||
__func__, cmd_idx, arg);
|
||||
|
||||
/* Response type:
|
||||
* R0
|
||||
* R1, R5, R6, R7
|
||||
* R1b
|
||||
* R2
|
||||
* R3, R4
|
||||
*/
|
||||
switch (mmc_resp_type(cmd)) {
|
||||
case MMC_RSP_NONE:
|
||||
rsp_type = SD_RSP_TYPE_R0;
|
||||
rsp_len = 0;
|
||||
break;
|
||||
case MMC_RSP_R1:
|
||||
rsp_type = SD_RSP_TYPE_R1;
|
||||
break;
|
||||
case MMC_RSP_R1 & ~MMC_RSP_CRC:
|
||||
rsp_type = SD_RSP_TYPE_R1 | SD_NO_CHECK_CRC7;
|
||||
break;
|
||||
case MMC_RSP_R1B:
|
||||
rsp_type = SD_RSP_TYPE_R1b;
|
||||
break;
|
||||
case MMC_RSP_R2:
|
||||
rsp_type = SD_RSP_TYPE_R2;
|
||||
rsp_len = 16;
|
||||
break;
|
||||
case MMC_RSP_R3:
|
||||
rsp_type = SD_RSP_TYPE_R3;
|
||||
break;
|
||||
default:
|
||||
dev_dbg(sdmmc_dev(host), "cmd->flag is not valid\n");
|
||||
err = -EINVAL;
|
||||
rsp_type = sd_response_type(cmd);
|
||||
if (rsp_type < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
stat_idx = sd_status_index(rsp_type);
|
||||
|
||||
if (rsp_type == SD_RSP_TYPE_R1b)
|
||||
timeout = 3000;
|
||||
@ -348,13 +256,7 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
|
||||
}
|
||||
|
||||
rtsx_pci_init_cmd(pcr);
|
||||
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD0, 0xFF, 0x40 | cmd_idx);
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD1, 0xFF, (u8)(arg >> 24));
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD2, 0xFF, (u8)(arg >> 16));
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD3, 0xFF, (u8)(arg >> 8));
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD4, 0xFF, (u8)arg);
|
||||
|
||||
sd_cmd_set_sd_cmd(pcr, cmd);
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, rsp_type);
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE,
|
||||
0x01, PINGPONG_BUFFER);
|
||||
@ -368,12 +270,10 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
|
||||
/* Read data from ping-pong buffer */
|
||||
for (i = PPBUF_BASE2; i < PPBUF_BASE2 + 16; i++)
|
||||
rtsx_pci_add_cmd(pcr, READ_REG_CMD, (u16)i, 0, 0);
|
||||
stat_idx = 16;
|
||||
} else if (rsp_type != SD_RSP_TYPE_R0) {
|
||||
/* Read data from SD_CMDx registers */
|
||||
for (i = SD_CMD0; i <= SD_CMD4; i++)
|
||||
rtsx_pci_add_cmd(pcr, READ_REG_CMD, (u16)i, 0, 0);
|
||||
stat_idx = 5;
|
||||
}
|
||||
|
||||
rtsx_pci_add_cmd(pcr, READ_REG_CMD, SD_STAT1, 0, 0);
|
||||
@ -438,40 +338,133 @@ out:
|
||||
SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0);
|
||||
}
|
||||
|
||||
static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq)
|
||||
static int sd_read_data(struct realtek_pci_sdmmc *host, struct mmc_command *cmd,
|
||||
u16 byte_cnt, u8 *buf, int buf_len, int timeout)
|
||||
{
|
||||
struct rtsx_pcr *pcr = host->pcr;
|
||||
int err;
|
||||
u8 trans_mode;
|
||||
|
||||
dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n",
|
||||
__func__, cmd->opcode, cmd->arg);
|
||||
|
||||
if (!buf)
|
||||
buf_len = 0;
|
||||
|
||||
if (cmd->opcode == MMC_SEND_TUNING_BLOCK)
|
||||
trans_mode = SD_TM_AUTO_TUNING;
|
||||
else
|
||||
trans_mode = SD_TM_NORMAL_READ;
|
||||
|
||||
rtsx_pci_init_cmd(pcr);
|
||||
sd_cmd_set_sd_cmd(pcr, cmd);
|
||||
sd_cmd_set_data_len(pcr, 1, byte_cnt);
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF,
|
||||
SD_CALCULATE_CRC7 | SD_CHECK_CRC16 |
|
||||
SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_6);
|
||||
if (trans_mode != SD_TM_AUTO_TUNING)
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
|
||||
CARD_DATA_SOURCE, 0x01, PINGPONG_BUFFER);
|
||||
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER,
|
||||
0xFF, trans_mode | SD_TRANSFER_START);
|
||||
rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER,
|
||||
SD_TRANSFER_END, SD_TRANSFER_END);
|
||||
|
||||
err = rtsx_pci_send_cmd(pcr, timeout);
|
||||
if (err < 0) {
|
||||
sd_print_debug_regs(host);
|
||||
dev_dbg(sdmmc_dev(host),
|
||||
"rtsx_pci_send_cmd fail (err = %d)\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (buf && buf_len) {
|
||||
err = rtsx_pci_read_ppbuf(pcr, buf, buf_len);
|
||||
if (err < 0) {
|
||||
dev_dbg(sdmmc_dev(host),
|
||||
"rtsx_pci_read_ppbuf fail (err = %d)\n", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_write_data(struct realtek_pci_sdmmc *host,
|
||||
struct mmc_command *cmd, u16 byte_cnt, u8 *buf, int buf_len,
|
||||
int timeout)
|
||||
{
|
||||
struct rtsx_pcr *pcr = host->pcr;
|
||||
int err;
|
||||
|
||||
dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n",
|
||||
__func__, cmd->opcode, cmd->arg);
|
||||
|
||||
if (!buf)
|
||||
buf_len = 0;
|
||||
|
||||
sd_send_cmd_get_rsp(host, cmd);
|
||||
if (cmd->error)
|
||||
return cmd->error;
|
||||
|
||||
if (buf && buf_len) {
|
||||
err = rtsx_pci_write_ppbuf(pcr, buf, buf_len);
|
||||
if (err < 0) {
|
||||
dev_dbg(sdmmc_dev(host),
|
||||
"rtsx_pci_write_ppbuf fail (err = %d)\n", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
rtsx_pci_init_cmd(pcr);
|
||||
sd_cmd_set_data_len(pcr, 1, byte_cnt);
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF,
|
||||
SD_CALCULATE_CRC7 | SD_CHECK_CRC16 |
|
||||
SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_0);
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, 0xFF,
|
||||
SD_TRANSFER_START | SD_TM_AUTO_WRITE_3);
|
||||
rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER,
|
||||
SD_TRANSFER_END, SD_TRANSFER_END);
|
||||
|
||||
err = rtsx_pci_send_cmd(pcr, timeout);
|
||||
if (err < 0) {
|
||||
sd_print_debug_regs(host);
|
||||
dev_dbg(sdmmc_dev(host),
|
||||
"rtsx_pci_send_cmd fail (err = %d)\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_read_long_data(struct realtek_pci_sdmmc *host,
|
||||
struct mmc_request *mrq)
|
||||
{
|
||||
struct rtsx_pcr *pcr = host->pcr;
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
struct mmc_card *card = mmc->card;
|
||||
struct mmc_command *cmd = mrq->cmd;
|
||||
struct mmc_data *data = mrq->data;
|
||||
int uhs = mmc_card_uhs(card);
|
||||
int read = (data->flags & MMC_DATA_READ) ? 1 : 0;
|
||||
u8 cfg2, trans_mode;
|
||||
u8 cfg2 = 0;
|
||||
int err;
|
||||
int resp_type;
|
||||
size_t data_len = data->blksz * data->blocks;
|
||||
|
||||
if (read) {
|
||||
cfg2 = SD_CALCULATE_CRC7 | SD_CHECK_CRC16 |
|
||||
SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_0;
|
||||
trans_mode = SD_TM_AUTO_READ_3;
|
||||
} else {
|
||||
cfg2 = SD_NO_CALCULATE_CRC7 | SD_CHECK_CRC16 |
|
||||
SD_NO_WAIT_BUSY_END | SD_NO_CHECK_CRC7 | SD_RSP_LEN_0;
|
||||
trans_mode = SD_TM_AUTO_WRITE_3;
|
||||
}
|
||||
dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n",
|
||||
__func__, cmd->opcode, cmd->arg);
|
||||
|
||||
resp_type = sd_response_type(cmd);
|
||||
if (resp_type < 0)
|
||||
return resp_type;
|
||||
|
||||
if (!uhs)
|
||||
cfg2 |= SD_NO_CHECK_WAIT_CRC_TO;
|
||||
|
||||
rtsx_pci_init_cmd(pcr);
|
||||
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, 0x00);
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_H, 0xFF, 0x02);
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_L,
|
||||
0xFF, (u8)data->blocks);
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_H,
|
||||
0xFF, (u8)(data->blocks >> 8));
|
||||
|
||||
sd_cmd_set_sd_cmd(pcr, cmd);
|
||||
sd_cmd_set_data_len(pcr, data->blocks, data->blksz);
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, IRQSTAT0,
|
||||
DMA_DONE_INT, DMA_DONE_INT);
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC3,
|
||||
@ -481,28 +474,77 @@ static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq)
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC1,
|
||||
0xFF, (u8)(data_len >> 8));
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC0, 0xFF, (u8)data_len);
|
||||
if (read) {
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMACTL,
|
||||
0x03 | DMA_PACK_SIZE_MASK,
|
||||
DMA_DIR_FROM_CARD | DMA_EN | DMA_512);
|
||||
} else {
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE,
|
||||
0x01, RING_BUFFER);
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, cfg2 | resp_type);
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, 0xFF,
|
||||
SD_TRANSFER_START | SD_TM_AUTO_READ_2);
|
||||
rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER,
|
||||
SD_TRANSFER_END, SD_TRANSFER_END);
|
||||
rtsx_pci_send_cmd_no_wait(pcr);
|
||||
|
||||
err = rtsx_pci_dma_transfer(pcr, data->sg, host->sg_count, 1, 10000);
|
||||
if (err < 0) {
|
||||
sd_print_debug_regs(host);
|
||||
sd_clear_error(host);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_write_long_data(struct realtek_pci_sdmmc *host,
|
||||
struct mmc_request *mrq)
|
||||
{
|
||||
struct rtsx_pcr *pcr = host->pcr;
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
struct mmc_card *card = mmc->card;
|
||||
struct mmc_command *cmd = mrq->cmd;
|
||||
struct mmc_data *data = mrq->data;
|
||||
int uhs = mmc_card_uhs(card);
|
||||
u8 cfg2;
|
||||
int err;
|
||||
size_t data_len = data->blksz * data->blocks;
|
||||
|
||||
sd_send_cmd_get_rsp(host, cmd);
|
||||
if (cmd->error)
|
||||
return cmd->error;
|
||||
|
||||
dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n",
|
||||
__func__, cmd->opcode, cmd->arg);
|
||||
|
||||
cfg2 = SD_NO_CALCULATE_CRC7 | SD_CHECK_CRC16 |
|
||||
SD_NO_WAIT_BUSY_END | SD_NO_CHECK_CRC7 | SD_RSP_LEN_0;
|
||||
|
||||
if (!uhs)
|
||||
cfg2 |= SD_NO_CHECK_WAIT_CRC_TO;
|
||||
|
||||
rtsx_pci_init_cmd(pcr);
|
||||
sd_cmd_set_data_len(pcr, data->blocks, data->blksz);
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, IRQSTAT0,
|
||||
DMA_DONE_INT, DMA_DONE_INT);
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC3,
|
||||
0xFF, (u8)(data_len >> 24));
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC2,
|
||||
0xFF, (u8)(data_len >> 16));
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC1,
|
||||
0xFF, (u8)(data_len >> 8));
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC0, 0xFF, (u8)data_len);
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMACTL,
|
||||
0x03 | DMA_PACK_SIZE_MASK,
|
||||
DMA_DIR_TO_CARD | DMA_EN | DMA_512);
|
||||
}
|
||||
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE,
|
||||
0x01, RING_BUFFER);
|
||||
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, cfg2);
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, 0xFF,
|
||||
trans_mode | SD_TRANSFER_START);
|
||||
SD_TRANSFER_START | SD_TM_AUTO_WRITE_3);
|
||||
rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER,
|
||||
SD_TRANSFER_END, SD_TRANSFER_END);
|
||||
|
||||
rtsx_pci_send_cmd_no_wait(pcr);
|
||||
|
||||
err = rtsx_pci_dma_transfer(pcr, data->sg, host->sg_count, read, 10000);
|
||||
err = rtsx_pci_dma_transfer(pcr, data->sg, host->sg_count, 0, 10000);
|
||||
if (err < 0) {
|
||||
sd_clear_error(host);
|
||||
return err;
|
||||
@ -511,6 +553,23 @@ static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq)
|
||||
{
|
||||
struct mmc_data *data = mrq->data;
|
||||
|
||||
if (host->sg_count < 0) {
|
||||
data->error = host->sg_count;
|
||||
dev_dbg(sdmmc_dev(host), "%s: sg_count = %d is invalid\n",
|
||||
__func__, host->sg_count);
|
||||
return data->error;
|
||||
}
|
||||
|
||||
if (data->flags & MMC_DATA_READ)
|
||||
return sd_read_long_data(host, mrq);
|
||||
|
||||
return sd_write_long_data(host, mrq);
|
||||
}
|
||||
|
||||
static inline void sd_enable_initial_mode(struct realtek_pci_sdmmc *host)
|
||||
{
|
||||
rtsx_pci_write_register(host->pcr, SD_CFG1,
|
||||
@ -528,10 +587,7 @@ static void sd_normal_rw(struct realtek_pci_sdmmc *host,
|
||||
{
|
||||
struct mmc_command *cmd = mrq->cmd;
|
||||
struct mmc_data *data = mrq->data;
|
||||
u8 _cmd[5], *buf;
|
||||
|
||||
_cmd[0] = 0x40 | (u8)cmd->opcode;
|
||||
put_unaligned_be32(cmd->arg, (u32 *)(&_cmd[1]));
|
||||
u8 *buf;
|
||||
|
||||
buf = kzalloc(data->blksz, GFP_NOIO);
|
||||
if (!buf) {
|
||||
@ -543,7 +599,7 @@ static void sd_normal_rw(struct realtek_pci_sdmmc *host,
|
||||
if (host->initial_mode)
|
||||
sd_disable_initial_mode(host);
|
||||
|
||||
cmd->error = sd_read_data(host, _cmd, (u16)data->blksz, buf,
|
||||
cmd->error = sd_read_data(host, cmd, (u16)data->blksz, buf,
|
||||
data->blksz, 200);
|
||||
|
||||
if (host->initial_mode)
|
||||
@ -553,7 +609,7 @@ static void sd_normal_rw(struct realtek_pci_sdmmc *host,
|
||||
} else {
|
||||
sg_copy_to_buffer(data->sg, data->sg_len, buf, data->blksz);
|
||||
|
||||
cmd->error = sd_write_data(host, _cmd, (u16)data->blksz, buf,
|
||||
cmd->error = sd_write_data(host, cmd, (u16)data->blksz, buf,
|
||||
data->blksz, 200);
|
||||
}
|
||||
|
||||
@ -653,14 +709,14 @@ static int sd_tuning_rx_cmd(struct realtek_pci_sdmmc *host,
|
||||
u8 opcode, u8 sample_point)
|
||||
{
|
||||
int err;
|
||||
u8 cmd[5] = {0};
|
||||
struct mmc_command cmd = {0};
|
||||
|
||||
err = sd_change_phase(host, sample_point, true);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
cmd[0] = 0x40 | opcode;
|
||||
err = sd_read_data(host, cmd, 0x40, NULL, 0, 100);
|
||||
cmd.opcode = opcode;
|
||||
err = sd_read_data(host, &cmd, 0x40, NULL, 0, 100);
|
||||
if (err < 0) {
|
||||
/* Wait till SD DATA IDLE */
|
||||
sd_wait_data_idle(host);
|
||||
@ -727,6 +783,12 @@ static int sd_tuning_rx(struct realtek_pci_sdmmc *host, u8 opcode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int sdio_extblock_cmd(struct mmc_command *cmd,
|
||||
struct mmc_data *data)
|
||||
{
|
||||
return (cmd->opcode == SD_IO_RW_EXTENDED) && (data->blksz == 512);
|
||||
}
|
||||
|
||||
static inline int sd_rw_cmd(struct mmc_command *cmd)
|
||||
{
|
||||
return mmc_op_multi(cmd->opcode) ||
|
||||
@ -748,7 +810,7 @@ static void sd_request(struct work_struct *work)
|
||||
unsigned int data_size = 0;
|
||||
int err;
|
||||
|
||||
if (host->eject) {
|
||||
if (host->eject || !sd_get_cd_int(host)) {
|
||||
cmd->error = -ENOMEDIUM;
|
||||
goto finish;
|
||||
}
|
||||
@ -776,17 +838,15 @@ static void sd_request(struct work_struct *work)
|
||||
if (mrq->data)
|
||||
data_size = data->blocks * data->blksz;
|
||||
|
||||
if (!data_size || sd_rw_cmd(cmd)) {
|
||||
if (!data_size) {
|
||||
sd_send_cmd_get_rsp(host, cmd);
|
||||
|
||||
if (!cmd->error && data_size) {
|
||||
sd_rw_multi(host, mrq);
|
||||
} else if (sd_rw_cmd(cmd) || sdio_extblock_cmd(cmd, data)) {
|
||||
cmd->error = sd_rw_multi(host, mrq);
|
||||
if (!host->using_cookie)
|
||||
sdmmc_post_req(host->mmc, host->mrq, 0);
|
||||
|
||||
if (mmc_op_multi(cmd->opcode) && mrq->stop)
|
||||
sd_send_cmd_get_rsp(host, mrq->stop);
|
||||
}
|
||||
} else {
|
||||
sd_normal_rw(host, mrq);
|
||||
}
|
||||
@ -801,8 +861,10 @@ static void sd_request(struct work_struct *work)
|
||||
mutex_unlock(&pcr->pcr_mutex);
|
||||
|
||||
finish:
|
||||
if (cmd->error)
|
||||
dev_dbg(sdmmc_dev(host), "cmd->error = %d\n", cmd->error);
|
||||
if (cmd->error) {
|
||||
dev_dbg(sdmmc_dev(host), "CMD %d 0x%08x error(%d)\n",
|
||||
cmd->opcode, cmd->arg, cmd->error);
|
||||
}
|
||||
|
||||
mutex_lock(&host->host_mutex);
|
||||
host->mrq = NULL;
|
||||
@ -820,7 +882,7 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
host->mrq = mrq;
|
||||
mutex_unlock(&host->host_mutex);
|
||||
|
||||
if (sd_rw_cmd(mrq->cmd))
|
||||
if (sd_rw_cmd(mrq->cmd) || sdio_extblock_cmd(mrq->cmd, data))
|
||||
host->using_cookie = sd_pre_dma_transfer(host, data, false);
|
||||
|
||||
queue_work(host->workq, &host->work);
|
||||
@ -1066,7 +1128,7 @@ static int sdmmc_get_cd(struct mmc_host *mmc)
|
||||
u32 val;
|
||||
|
||||
if (host->eject)
|
||||
return -ENOMEDIUM;
|
||||
return cd;
|
||||
|
||||
mutex_lock(&pcr->pcr_mutex);
|
||||
|
||||
@ -1317,6 +1379,7 @@ static void rtsx_pci_sdmmc_card_event(struct platform_device *pdev)
|
||||
{
|
||||
struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev);
|
||||
|
||||
host->cookie = -1;
|
||||
mmc_detect_change(host->mmc, 0);
|
||||
}
|
||||
|
||||
@ -1349,6 +1412,7 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev)
|
||||
host->pcr = pcr;
|
||||
host->mmc = mmc;
|
||||
host->pdev = pdev;
|
||||
host->cookie = -1;
|
||||
host->power_state = SDMMC_POWER_OFF;
|
||||
INIT_WORK(&host->work, sd_request);
|
||||
platform_set_drvdata(pdev, host);
|
||||
|
@ -158,7 +158,7 @@ static int sdhci_acpi_emmc_probe_slot(struct platform_device *pdev,
|
||||
|
||||
host = c->host;
|
||||
|
||||
/* Platform specific code during emmc proble slot goes here */
|
||||
/* Platform specific code during emmc probe slot goes here */
|
||||
|
||||
if (hid && uid && !strcmp(hid, "80860F14") && !strcmp(uid, "1") &&
|
||||
sdhci_readl(host, SDHCI_CAPABILITIES) == 0x446cc8b2 &&
|
||||
@ -179,7 +179,7 @@ static int sdhci_acpi_sdio_probe_slot(struct platform_device *pdev,
|
||||
|
||||
host = c->host;
|
||||
|
||||
/* Platform specific code during emmc proble slot goes here */
|
||||
/* Platform specific code during sdio probe slot goes here */
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -195,7 +195,7 @@ static int sdhci_acpi_sd_probe_slot(struct platform_device *pdev,
|
||||
|
||||
host = c->host;
|
||||
|
||||
/* Platform specific code during emmc proble slot goes here */
|
||||
/* Platform specific code during sd probe slot goes here */
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -448,18 +448,13 @@ static int sdhci_acpi_runtime_resume(struct device *dev)
|
||||
return sdhci_runtime_resume_host(c->host);
|
||||
}
|
||||
|
||||
static int sdhci_acpi_runtime_idle(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops sdhci_acpi_pm_ops = {
|
||||
.suspend = sdhci_acpi_suspend,
|
||||
.resume = sdhci_acpi_resume,
|
||||
SET_RUNTIME_PM_OPS(sdhci_acpi_runtime_suspend,
|
||||
sdhci_acpi_runtime_resume, sdhci_acpi_runtime_idle)
|
||||
sdhci_acpi_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver sdhci_acpi_driver = {
|
||||
|
@ -254,7 +254,9 @@ static int sdhci_bcm_kona_probe(struct platform_device *pdev)
|
||||
kona_dev = sdhci_pltfm_priv(pltfm_priv);
|
||||
mutex_init(&kona_dev->write_lock);
|
||||
|
||||
mmc_of_parse(host->mmc);
|
||||
ret = mmc_of_parse(host->mmc);
|
||||
if (ret)
|
||||
goto err_pltfm_free;
|
||||
|
||||
if (!host->mmc->f_max) {
|
||||
dev_err(&pdev->dev, "Missing max-freq for SDHCI cfg\n");
|
||||
|
@ -1080,10 +1080,10 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
||||
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);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
@ -1103,16 +1103,15 @@ static int sdhci_esdhc_imx_remove(struct platform_device *pdev)
|
||||
struct pltfm_imx_data *imx_data = pltfm_host->priv;
|
||||
int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
|
||||
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
|
||||
sdhci_remove_host(host, dead);
|
||||
|
||||
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
if (!IS_ENABLED(CONFIG_PM)) {
|
||||
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);
|
||||
|
||||
|
@ -276,6 +276,14 @@ static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)
|
||||
ESDHC_CTRL_BUSWIDTH_MASK, ctrl);
|
||||
}
|
||||
|
||||
static void esdhc_reset(struct sdhci_host *host, u8 mask)
|
||||
{
|
||||
sdhci_reset(host, mask);
|
||||
|
||||
sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
|
||||
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
|
||||
}
|
||||
|
||||
static const struct sdhci_ops sdhci_esdhc_ops = {
|
||||
.read_l = esdhc_readl,
|
||||
.read_w = esdhc_readw,
|
||||
@ -290,7 +298,7 @@ static const struct sdhci_ops sdhci_esdhc_ops = {
|
||||
.platform_init = esdhc_of_platform_init,
|
||||
.adma_workaround = esdhci_of_adma_workaround,
|
||||
.set_bus_width = esdhc_pltfm_set_bus_width,
|
||||
.reset = sdhci_reset,
|
||||
.reset = esdhc_reset,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
};
|
||||
|
||||
@ -362,13 +370,19 @@ static int sdhci_esdhc_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
/* call to generic mmc_of_parse to support additional capabilities */
|
||||
mmc_of_parse(host->mmc);
|
||||
ret = mmc_of_parse(host->mmc);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
mmc_of_parse_voltage(np, &host->ocr_mask);
|
||||
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret)
|
||||
sdhci_pltfm_free(pdev);
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
sdhci_pltfm_free(pdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1367,11 +1367,6 @@ static int sdhci_pci_runtime_resume(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdhci_pci_runtime_idle(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else /* CONFIG_PM */
|
||||
|
||||
#define sdhci_pci_suspend NULL
|
||||
@ -1383,7 +1378,7 @@ static const struct dev_pm_ops sdhci_pci_pm_ops = {
|
||||
.suspend = sdhci_pci_suspend,
|
||||
.resume = sdhci_pci_resume,
|
||||
SET_RUNTIME_PM_OPS(sdhci_pci_runtime_suspend,
|
||||
sdhci_pci_runtime_resume, sdhci_pci_runtime_idle)
|
||||
sdhci_pci_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
/*****************************************************************************\
|
||||
|
@ -62,6 +62,7 @@ struct sdhci_pxa {
|
||||
struct clk *clk_core;
|
||||
struct clk *clk_io;
|
||||
u8 power_mode;
|
||||
void __iomem *sdio3_conf_reg;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -72,6 +73,14 @@ struct sdhci_pxa {
|
||||
#define SDHCI_WINDOW_BASE(i) (0x84 + ((i) << 3))
|
||||
#define SDHCI_MAX_WIN_NUM 8
|
||||
|
||||
/*
|
||||
* Fields below belong to SDIO3 Configuration Register (third register
|
||||
* region for the Armada 38x flavor)
|
||||
*/
|
||||
|
||||
#define SDIO3_CONF_CLK_INV BIT(0)
|
||||
#define SDIO3_CONF_SD_FB_CLK BIT(2)
|
||||
|
||||
static int mv_conf_mbus_windows(struct platform_device *pdev,
|
||||
const struct mbus_dram_target_info *dram)
|
||||
{
|
||||
@ -118,6 +127,51 @@ static int mv_conf_mbus_windows(struct platform_device *pdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int armada_38x_quirks(struct platform_device *pdev,
|
||||
struct sdhci_host *host)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_pxa *pxa = pltfm_host->priv;
|
||||
struct resource *res;
|
||||
|
||||
host->quirks |= SDHCI_QUIRK_MISSING_CAPS;
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"conf-sdio3");
|
||||
if (res) {
|
||||
pxa->sdio3_conf_reg = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(pxa->sdio3_conf_reg))
|
||||
return PTR_ERR(pxa->sdio3_conf_reg);
|
||||
} else {
|
||||
/*
|
||||
* According to erratum 'FE-2946959' both SDR50 and DDR50
|
||||
* modes require specific clock adjustments in SDIO3
|
||||
* Configuration register, if the adjustment is not done,
|
||||
* remove them from the capabilities.
|
||||
*/
|
||||
host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1);
|
||||
host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50);
|
||||
|
||||
dev_warn(&pdev->dev, "conf-sdio3 register not found: disabling SDR50 and DDR50 modes.\nConsider updating your dtb\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* According to erratum 'ERR-7878951' Armada 38x SDHCI
|
||||
* controller has different capabilities than the ones shown
|
||||
* in its registers
|
||||
*/
|
||||
host->caps = sdhci_readl(host, SDHCI_CAPABILITIES);
|
||||
if (of_property_read_bool(np, "no-1-8-v")) {
|
||||
host->caps &= ~SDHCI_CAN_VDD_180;
|
||||
host->mmc->caps &= ~MMC_CAP_1_8V_DDR;
|
||||
} else {
|
||||
host->caps &= ~SDHCI_CAN_VDD_330;
|
||||
}
|
||||
host->caps1 &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_USE_SDR50_TUNING);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pxav3_reset(struct sdhci_host *host, u8 mask)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc));
|
||||
@ -194,6 +248,8 @@ static void pxav3_gen_init_74_clocks(struct sdhci_host *host, u8 power_mode)
|
||||
|
||||
static void pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_pxa *pxa = pltfm_host->priv;
|
||||
u16 ctrl_2;
|
||||
|
||||
/*
|
||||
@ -223,6 +279,24 @@ static void pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update SDIO3 Configuration register according to erratum
|
||||
* FE-2946959
|
||||
*/
|
||||
if (pxa->sdio3_conf_reg) {
|
||||
u8 reg_val = readb(pxa->sdio3_conf_reg);
|
||||
|
||||
if (uhs == MMC_TIMING_UHS_SDR50 ||
|
||||
uhs == MMC_TIMING_UHS_DDR50) {
|
||||
reg_val &= ~SDIO3_CONF_CLK_INV;
|
||||
reg_val |= SDIO3_CONF_SD_FB_CLK;
|
||||
} else {
|
||||
reg_val |= SDIO3_CONF_CLK_INV;
|
||||
reg_val &= ~SDIO3_CONF_SD_FB_CLK;
|
||||
}
|
||||
writeb(reg_val, pxa->sdio3_conf_reg);
|
||||
}
|
||||
|
||||
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
|
||||
dev_dbg(mmc_dev(host->mmc),
|
||||
"%s uhs = %d, ctrl_2 = %04X\n",
|
||||
@ -268,8 +342,8 @@ static struct sdhci_pxa_platdata *pxav3_get_mmc_pdata(struct device *dev)
|
||||
if (!pdata)
|
||||
return NULL;
|
||||
|
||||
of_property_read_u32(np, "mrvl,clk-delay-cycles", &clk_delay_cycles);
|
||||
if (clk_delay_cycles > 0)
|
||||
if (!of_property_read_u32(np, "mrvl,clk-delay-cycles",
|
||||
&clk_delay_cycles))
|
||||
pdata->clk_delay_cycles = clk_delay_cycles;
|
||||
|
||||
return pdata;
|
||||
@ -318,15 +392,18 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
|
||||
if (!IS_ERR(pxa->clk_core))
|
||||
clk_prepare_enable(pxa->clk_core);
|
||||
|
||||
/* enable 1/8V DDR capable */
|
||||
host->mmc->caps |= MMC_CAP_1_8V_DDR;
|
||||
|
||||
if (of_device_is_compatible(np, "marvell,armada-380-sdhci")) {
|
||||
ret = armada_38x_quirks(pdev, host);
|
||||
if (ret < 0)
|
||||
goto err_clk_get;
|
||||
ret = mv_conf_mbus_windows(pdev, mv_mbus_dram_info());
|
||||
if (ret < 0)
|
||||
goto err_mbus_win;
|
||||
}
|
||||
|
||||
/* enable 1/8V DDR capable */
|
||||
host->mmc->caps |= MMC_CAP_1_8V_DDR;
|
||||
|
||||
match = of_match_device(of_match_ptr(sdhci_pxav3_of_match), &pdev->dev);
|
||||
if (match) {
|
||||
ret = mmc_of_parse(host->mmc);
|
||||
@ -365,10 +442,11 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
pm_runtime_get_noresume(&pdev->dev);
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, PXAV3_RPM_DELAY_MS);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_suspend_ignore_children(&pdev->dev, 1);
|
||||
|
||||
ret = sdhci_add_host(host);
|
||||
@ -391,13 +469,12 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
|
||||
err_add_host:
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
err_of_parse:
|
||||
err_cd_req:
|
||||
err_mbus_win:
|
||||
clk_disable_unprepare(pxa->clk_io);
|
||||
if (!IS_ERR(pxa->clk_core))
|
||||
clk_disable_unprepare(pxa->clk_core);
|
||||
err_clk_get:
|
||||
sdhci_pltfm_free(pdev);
|
||||
@ -411,11 +488,12 @@ static int sdhci_pxav3_remove(struct platform_device *pdev)
|
||||
struct sdhci_pxa *pxa = pltfm_host->priv;
|
||||
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
sdhci_remove_host(host, 1);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
|
||||
sdhci_remove_host(host, 1);
|
||||
|
||||
clk_disable_unprepare(pxa->clk_io);
|
||||
if (!IS_ERR(pxa->clk_core))
|
||||
clk_disable_unprepare(pxa->clk_core);
|
||||
|
||||
sdhci_pltfm_free(pdev);
|
||||
@ -457,11 +535,11 @@ static int sdhci_pxav3_runtime_suspend(struct device *dev)
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_pxa *pxa = pltfm_host->priv;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
host->runtime_suspended = true;
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
ret = sdhci_runtime_suspend_host(host);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clk_disable_unprepare(pxa->clk_io);
|
||||
if (!IS_ERR(pxa->clk_core))
|
||||
@ -475,17 +553,12 @@ static int sdhci_pxav3_runtime_resume(struct device *dev)
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_pxa *pxa = pltfm_host->priv;
|
||||
unsigned long flags;
|
||||
|
||||
clk_prepare_enable(pxa->clk_io);
|
||||
if (!IS_ERR(pxa->clk_core))
|
||||
clk_prepare_enable(pxa->clk_core);
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
host->runtime_suspended = false;
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
return 0;
|
||||
return sdhci_runtime_resume_host(host);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/platform_device.h>
|
||||
@ -312,7 +313,14 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
|
||||
sdhci_s3c_set_clock(host, clock);
|
||||
|
||||
/* Reset SD Clock Enable */
|
||||
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
||||
clk &= ~SDHCI_CLOCK_CARD_EN;
|
||||
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
||||
|
||||
spin_unlock_irq(&host->lock);
|
||||
ret = clk_set_rate(ourhost->clk_bus[ourhost->cur_clk], clock);
|
||||
spin_lock_irq(&host->lock);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "%s: failed to set clock rate %uHz\n",
|
||||
mmc_hostname(host->mmc), clock);
|
||||
@ -607,7 +615,9 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_suspend_ignore_children(&pdev->dev, 1);
|
||||
|
||||
mmc_of_parse(host->mmc);
|
||||
ret = mmc_of_parse(host->mmc);
|
||||
if (ret)
|
||||
goto err_req_regs;
|
||||
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret) {
|
||||
|
@ -15,7 +15,9 @@
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
#include "sdhci-pltfm.h"
|
||||
|
||||
#define SDHCI_CLK_DELAY_SETTING 0x4C
|
||||
#define SDHCI_SIRF_8BITBUS BIT(3)
|
||||
#define SIRF_TUNING_COUNT 128
|
||||
|
||||
struct sdhci_sirf_priv {
|
||||
struct clk *clk;
|
||||
@ -49,7 +51,76 @@ static void sdhci_sirf_set_bus_width(struct sdhci_host *host, int width)
|
||||
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
|
||||
}
|
||||
|
||||
static int sdhci_sirf_execute_tuning(struct sdhci_host *host, u32 opcode)
|
||||
{
|
||||
int tuning_seq_cnt = 3;
|
||||
u8 phase, tuned_phases[SIRF_TUNING_COUNT];
|
||||
u8 tuned_phase_cnt = 0;
|
||||
int rc, longest_range = 0;
|
||||
int start = -1, end = 0, tuning_value = -1, range = 0;
|
||||
u16 clock_setting;
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
|
||||
clock_setting = sdhci_readw(host, SDHCI_CLK_DELAY_SETTING);
|
||||
clock_setting &= ~0x3fff;
|
||||
|
||||
retry:
|
||||
phase = 0;
|
||||
do {
|
||||
sdhci_writel(host,
|
||||
clock_setting | phase | (phase << 7) | (phase << 16),
|
||||
SDHCI_CLK_DELAY_SETTING);
|
||||
|
||||
if (!mmc_send_tuning(mmc)) {
|
||||
/* Tuning is successful at this tuning point */
|
||||
tuned_phases[tuned_phase_cnt++] = phase;
|
||||
dev_dbg(mmc_dev(mmc), "%s: Found good phase = %d\n",
|
||||
mmc_hostname(mmc), phase);
|
||||
if (start == -1)
|
||||
start = phase;
|
||||
end = phase;
|
||||
range++;
|
||||
if (phase == (SIRF_TUNING_COUNT - 1)
|
||||
&& range > longest_range)
|
||||
tuning_value = (start + end) / 2;
|
||||
} else {
|
||||
dev_dbg(mmc_dev(mmc), "%s: Found bad phase = %d\n",
|
||||
mmc_hostname(mmc), phase);
|
||||
if (range > longest_range) {
|
||||
tuning_value = (start + end) / 2;
|
||||
longest_range = range;
|
||||
}
|
||||
start = -1;
|
||||
end = range = 0;
|
||||
}
|
||||
} while (++phase < ARRAY_SIZE(tuned_phases));
|
||||
|
||||
if (tuned_phase_cnt && tuning_value > 0) {
|
||||
/*
|
||||
* Finally set the selected phase in delay
|
||||
* line hw block.
|
||||
*/
|
||||
phase = tuning_value;
|
||||
sdhci_writel(host,
|
||||
clock_setting | phase | (phase << 7) | (phase << 16),
|
||||
SDHCI_CLK_DELAY_SETTING);
|
||||
|
||||
dev_dbg(mmc_dev(mmc), "%s: Setting the tuning phase to %d\n",
|
||||
mmc_hostname(mmc), phase);
|
||||
} else {
|
||||
if (--tuning_seq_cnt)
|
||||
goto retry;
|
||||
/* Tuning failed */
|
||||
dev_dbg(mmc_dev(mmc), "%s: No tuning point found\n",
|
||||
mmc_hostname(mmc));
|
||||
rc = -EIO;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct sdhci_ops sdhci_sirf_ops = {
|
||||
.platform_execute_tuning = sdhci_sirf_execute_tuning,
|
||||
.set_clock = sdhci_set_clock,
|
||||
.get_max_clock = sdhci_sirf_get_max_clk,
|
||||
.set_bus_width = sdhci_sirf_set_bus_width,
|
||||
@ -138,9 +209,6 @@ static int sdhci_sirf_remove(struct platform_device *pdev)
|
||||
|
||||
sdhci_pltfm_unregister(pdev);
|
||||
|
||||
if (gpio_is_valid(priv->gpio_cd))
|
||||
mmc_gpio_free_cd(host->mmc);
|
||||
|
||||
clk_disable_unprepare(priv->clk);
|
||||
return 0;
|
||||
}
|
||||
|
@ -78,10 +78,9 @@ static int sdhci_st_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
ret = mmc_of_parse(host->mmc);
|
||||
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed mmc_of_parse\n");
|
||||
return ret;
|
||||
goto err_of;
|
||||
}
|
||||
|
||||
clk_prepare_enable(clk);
|
||||
@ -108,6 +107,7 @@ static int sdhci_st_probe(struct platform_device *pdev)
|
||||
|
||||
err_out:
|
||||
clk_disable_unprepare(clk);
|
||||
err_of:
|
||||
sdhci_pltfm_free(pdev);
|
||||
|
||||
return ret;
|
||||
|
@ -41,6 +41,7 @@
|
||||
#define NVQUIRK_DISABLE_SDR50 BIT(3)
|
||||
#define NVQUIRK_DISABLE_SDR104 BIT(4)
|
||||
#define NVQUIRK_DISABLE_DDR50 BIT(5)
|
||||
#define NVQUIRK_SHADOW_XFER_MODE_REG BIT(6)
|
||||
|
||||
struct sdhci_tegra_soc_data {
|
||||
const struct sdhci_pltfm_data *pdata;
|
||||
@ -67,6 +68,31 @@ static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
|
||||
return readw(host->ioaddr + reg);
|
||||
}
|
||||
|
||||
static void tegra_sdhci_writew(struct sdhci_host *host, u16 val, int reg)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_tegra *tegra_host = pltfm_host->priv;
|
||||
const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
|
||||
|
||||
if (soc_data->nvquirks & NVQUIRK_SHADOW_XFER_MODE_REG) {
|
||||
switch (reg) {
|
||||
case SDHCI_TRANSFER_MODE:
|
||||
/*
|
||||
* Postpone this write, we must do it together with a
|
||||
* command write that is down below.
|
||||
*/
|
||||
pltfm_host->xfer_mode_shadow = val;
|
||||
return;
|
||||
case SDHCI_COMMAND:
|
||||
writel((val << 16) | pltfm_host->xfer_mode_shadow,
|
||||
host->ioaddr + SDHCI_TRANSFER_MODE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
writew(val, host->ioaddr + reg);
|
||||
}
|
||||
|
||||
static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
@ -147,6 +173,7 @@ static void tegra_sdhci_set_bus_width(struct sdhci_host *host, int bus_width)
|
||||
static const struct sdhci_ops tegra_sdhci_ops = {
|
||||
.get_ro = tegra_sdhci_get_ro,
|
||||
.read_w = tegra_sdhci_readw,
|
||||
.write_w = tegra_sdhci_writew,
|
||||
.write_l = tegra_sdhci_writel,
|
||||
.set_clock = sdhci_set_clock,
|
||||
.set_bus_width = tegra_sdhci_set_bus_width,
|
||||
@ -201,7 +228,8 @@ static struct sdhci_tegra_soc_data soc_data_tegra114 = {
|
||||
.pdata = &sdhci_tegra114_pdata,
|
||||
.nvquirks = NVQUIRK_DISABLE_SDR50 |
|
||||
NVQUIRK_DISABLE_DDR50 |
|
||||
NVQUIRK_DISABLE_SDR104,
|
||||
NVQUIRK_DISABLE_SDR104 |
|
||||
NVQUIRK_SHADOW_XFER_MODE_REG,
|
||||
};
|
||||
|
||||
static const struct of_device_id sdhci_tegra_dt_match[] = {
|
||||
|
@ -53,6 +53,9 @@ static void sdhci_finish_command(struct sdhci_host *);
|
||||
static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
|
||||
static void sdhci_tuning_timer(unsigned long data);
|
||||
static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
|
||||
static int sdhci_pre_dma_transfer(struct sdhci_host *host,
|
||||
struct mmc_data *data,
|
||||
struct sdhci_host_next *next);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int sdhci_runtime_pm_get(struct sdhci_host *host);
|
||||
@ -505,9 +508,8 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
|
||||
goto fail;
|
||||
BUG_ON(host->align_addr & host->align_mask);
|
||||
|
||||
host->sg_count = dma_map_sg(mmc_dev(host->mmc),
|
||||
data->sg, data->sg_len, direction);
|
||||
if (host->sg_count == 0)
|
||||
host->sg_count = sdhci_pre_dma_transfer(host, data, NULL);
|
||||
if (host->sg_count < 0)
|
||||
goto unmap_align;
|
||||
|
||||
desc = host->adma_table;
|
||||
@ -531,8 +533,6 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
|
||||
if (offset) {
|
||||
if (data->flags & MMC_DATA_WRITE) {
|
||||
buffer = sdhci_kmap_atomic(sg, &flags);
|
||||
WARN_ON(((long)buffer & (PAGE_SIZE - 1)) >
|
||||
(PAGE_SIZE - offset));
|
||||
memcpy(align, buffer, offset);
|
||||
sdhci_kunmap_atomic(buffer, &flags);
|
||||
}
|
||||
@ -639,8 +639,6 @@ static void sdhci_adma_table_post(struct sdhci_host *host,
|
||||
(sg_dma_address(sg) & host->align_mask);
|
||||
|
||||
buffer = sdhci_kmap_atomic(sg, &flags);
|
||||
WARN_ON(((long)buffer & (PAGE_SIZE - 1)) >
|
||||
(PAGE_SIZE - size));
|
||||
memcpy(buffer, align, size);
|
||||
sdhci_kunmap_atomic(buffer, &flags);
|
||||
|
||||
@ -649,6 +647,7 @@ static void sdhci_adma_table_post(struct sdhci_host *host,
|
||||
}
|
||||
}
|
||||
|
||||
if (!data->host_cookie)
|
||||
dma_unmap_sg(mmc_dev(host->mmc), data->sg,
|
||||
data->sg_len, direction);
|
||||
}
|
||||
@ -846,11 +845,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
|
||||
} else {
|
||||
int sg_cnt;
|
||||
|
||||
sg_cnt = dma_map_sg(mmc_dev(host->mmc),
|
||||
data->sg, data->sg_len,
|
||||
(data->flags & MMC_DATA_READ) ?
|
||||
DMA_FROM_DEVICE :
|
||||
DMA_TO_DEVICE);
|
||||
sg_cnt = sdhci_pre_dma_transfer(host, data, NULL);
|
||||
if (sg_cnt == 0) {
|
||||
/*
|
||||
* This only happens when someone fed
|
||||
@ -909,7 +904,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
|
||||
static void sdhci_set_transfer_mode(struct sdhci_host *host,
|
||||
struct mmc_command *cmd)
|
||||
{
|
||||
u16 mode;
|
||||
u16 mode = 0;
|
||||
struct mmc_data *data = cmd->data;
|
||||
|
||||
if (data == NULL) {
|
||||
@ -927,9 +922,11 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
|
||||
|
||||
WARN_ON(!host->data);
|
||||
|
||||
if (!(host->quirks2 & SDHCI_QUIRK2_SUPPORT_SINGLE))
|
||||
mode = SDHCI_TRNS_BLK_CNT_EN;
|
||||
|
||||
if (mmc_op_multi(cmd->opcode) || data->blocks > 1) {
|
||||
mode |= SDHCI_TRNS_MULTI;
|
||||
mode = SDHCI_TRNS_BLK_CNT_EN | SDHCI_TRNS_MULTI;
|
||||
/*
|
||||
* If we are sending CMD23, CMD12 never gets sent
|
||||
* on successful completion (so no Auto-CMD12).
|
||||
@ -963,8 +960,10 @@ static void sdhci_finish_data(struct sdhci_host *host)
|
||||
if (host->flags & SDHCI_USE_ADMA)
|
||||
sdhci_adma_table_post(host, data);
|
||||
else {
|
||||
dma_unmap_sg(mmc_dev(host->mmc), data->sg,
|
||||
data->sg_len, (data->flags & MMC_DATA_READ) ?
|
||||
if (!data->host_cookie)
|
||||
dma_unmap_sg(mmc_dev(host->mmc),
|
||||
data->sg, data->sg_len,
|
||||
(data->flags & MMC_DATA_READ) ?
|
||||
DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
||||
}
|
||||
}
|
||||
@ -1630,7 +1629,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
|
||||
* signalling timeout and CRC errors even on CMD0. Resetting
|
||||
* it on each ios seems to solve the problem.
|
||||
*/
|
||||
if(host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS)
|
||||
if (host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS)
|
||||
sdhci_do_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
|
||||
|
||||
mmiowb();
|
||||
@ -1832,6 +1831,10 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
|
||||
ctrl |= SDHCI_CTRL_VDD_180;
|
||||
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
|
||||
|
||||
/* Some controller need to do more when switching */
|
||||
if (host->ops->voltage_switch)
|
||||
host->ops->voltage_switch(host);
|
||||
|
||||
/* 1.8V regulator output should be stable within 5 ms */
|
||||
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
||||
if (ctrl & SDHCI_CTRL_VDD_180)
|
||||
@ -1960,6 +1963,8 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
||||
|
||||
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
||||
ctrl |= SDHCI_CTRL_EXEC_TUNING;
|
||||
if (host->quirks2 & SDHCI_QUIRK2_TUNING_WORK_AROUND)
|
||||
ctrl |= SDHCI_CTRL_TUNED_CLK;
|
||||
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
|
||||
|
||||
/*
|
||||
@ -2129,6 +2134,77 @@ static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable)
|
||||
}
|
||||
}
|
||||
|
||||
static void sdhci_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
|
||||
int err)
|
||||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
struct mmc_data *data = mrq->data;
|
||||
|
||||
if (host->flags & SDHCI_REQ_USE_DMA) {
|
||||
if (data->host_cookie)
|
||||
dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
|
||||
data->flags & MMC_DATA_WRITE ?
|
||||
DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
mrq->data->host_cookie = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int sdhci_pre_dma_transfer(struct sdhci_host *host,
|
||||
struct mmc_data *data,
|
||||
struct sdhci_host_next *next)
|
||||
{
|
||||
int sg_count;
|
||||
|
||||
if (!next && data->host_cookie &&
|
||||
data->host_cookie != host->next_data.cookie) {
|
||||
pr_debug(DRIVER_NAME "[%s] invalid cookie: %d, next-cookie %d\n",
|
||||
__func__, data->host_cookie, host->next_data.cookie);
|
||||
data->host_cookie = 0;
|
||||
}
|
||||
|
||||
/* Check if next job is already prepared */
|
||||
if (next ||
|
||||
(!next && data->host_cookie != host->next_data.cookie)) {
|
||||
sg_count = dma_map_sg(mmc_dev(host->mmc), data->sg,
|
||||
data->sg_len,
|
||||
data->flags & MMC_DATA_WRITE ?
|
||||
DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
|
||||
} else {
|
||||
sg_count = host->next_data.sg_count;
|
||||
host->next_data.sg_count = 0;
|
||||
}
|
||||
|
||||
|
||||
if (sg_count == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (next) {
|
||||
next->sg_count = sg_count;
|
||||
data->host_cookie = ++next->cookie < 0 ? 1 : next->cookie;
|
||||
} else
|
||||
host->sg_count = sg_count;
|
||||
|
||||
return sg_count;
|
||||
}
|
||||
|
||||
static void sdhci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
|
||||
bool is_first_req)
|
||||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
|
||||
if (mrq->data->host_cookie) {
|
||||
mrq->data->host_cookie = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (host->flags & SDHCI_REQ_USE_DMA)
|
||||
if (sdhci_pre_dma_transfer(host,
|
||||
mrq->data,
|
||||
&host->next_data) < 0)
|
||||
mrq->data->host_cookie = 0;
|
||||
}
|
||||
|
||||
static void sdhci_card_event(struct mmc_host *mmc)
|
||||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
@ -2162,6 +2238,8 @@ static void sdhci_card_event(struct mmc_host *mmc)
|
||||
|
||||
static const struct mmc_host_ops sdhci_ops = {
|
||||
.request = sdhci_request,
|
||||
.post_req = sdhci_post_req,
|
||||
.pre_req = sdhci_pre_req,
|
||||
.set_ios = sdhci_set_ios,
|
||||
.get_cd = sdhci_get_cd,
|
||||
.get_ro = sdhci_get_ro,
|
||||
@ -2793,9 +2871,9 @@ int sdhci_runtime_resume_host(struct sdhci_host *host)
|
||||
/* Force clock and power re-program */
|
||||
host->pwr = 0;
|
||||
host->clock = 0;
|
||||
sdhci_do_start_signal_voltage_switch(host, &host->mmc->ios);
|
||||
sdhci_do_set_ios(host, &host->mmc->ios);
|
||||
|
||||
sdhci_do_start_signal_voltage_switch(host, &host->mmc->ios);
|
||||
if ((host_flags & SDHCI_PV_ENABLED) &&
|
||||
!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN)) {
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
@ -3019,6 +3097,7 @@ int sdhci_add_host(struct sdhci_host *host)
|
||||
host->max_clk = host->ops->get_max_clock(host);
|
||||
}
|
||||
|
||||
host->next_data.cookie = 1;
|
||||
/*
|
||||
* In case of Host Controller v3.00, find out whether clock
|
||||
* multiplier is supported.
|
||||
@ -3338,9 +3417,9 @@ int sdhci_add_host(struct sdhci_host *host)
|
||||
|
||||
setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host);
|
||||
|
||||
if (host->version >= SDHCI_SPEC_300) {
|
||||
init_waitqueue_head(&host->buf_ready_int);
|
||||
|
||||
if (host->version >= SDHCI_SPEC_300) {
|
||||
/* Initialize re-tuning timer */
|
||||
init_timer(&host->tuning_timer);
|
||||
host->tuning_timer.data = (unsigned long)host;
|
||||
|
@ -339,6 +339,7 @@ struct sdhci_ops {
|
||||
void (*adma_workaround)(struct sdhci_host *host, u32 intmask);
|
||||
void (*platform_init)(struct sdhci_host *host);
|
||||
void (*card_event)(struct sdhci_host *host);
|
||||
void (*voltage_switch)(struct sdhci_host *host);
|
||||
};
|
||||
|
||||
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
|
||||
|
237
drivers/mmc/host/sdhci_f_sdh30.c
Normal file
237
drivers/mmc/host/sdhci_f_sdh30.c
Normal file
@ -0,0 +1,237 @@
|
||||
/*
|
||||
* linux/drivers/mmc/host/sdhci_f_sdh30.c
|
||||
*
|
||||
* Copyright (C) 2013 - 2015 Fujitsu Semiconductor, Ltd
|
||||
* Vincent Yang <vincent.yang@tw.fujitsu.com>
|
||||
* Copyright (C) 2015 Linaro Ltd Andy Green <andy.green@linaro.org>
|
||||
*
|
||||
* 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, version 2 of the License.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include "sdhci-pltfm.h"
|
||||
|
||||
/* F_SDH30 extended Controller registers */
|
||||
#define F_SDH30_AHB_CONFIG 0x100
|
||||
#define F_SDH30_AHB_BIGED 0x00000040
|
||||
#define F_SDH30_BUSLOCK_DMA 0x00000020
|
||||
#define F_SDH30_BUSLOCK_EN 0x00000010
|
||||
#define F_SDH30_SIN 0x00000008
|
||||
#define F_SDH30_AHB_INCR_16 0x00000004
|
||||
#define F_SDH30_AHB_INCR_8 0x00000002
|
||||
#define F_SDH30_AHB_INCR_4 0x00000001
|
||||
|
||||
#define F_SDH30_TUNING_SETTING 0x108
|
||||
#define F_SDH30_CMD_CHK_DIS 0x00010000
|
||||
|
||||
#define F_SDH30_IO_CONTROL2 0x114
|
||||
#define F_SDH30_CRES_O_DN 0x00080000
|
||||
#define F_SDH30_MSEL_O_1_8 0x00040000
|
||||
|
||||
#define F_SDH30_ESD_CONTROL 0x124
|
||||
#define F_SDH30_EMMC_RST 0x00000002
|
||||
#define F_SDH30_EMMC_HS200 0x01000000
|
||||
|
||||
#define F_SDH30_CMD_DAT_DELAY 0x200
|
||||
|
||||
#define F_SDH30_MIN_CLOCK 400000
|
||||
|
||||
struct f_sdhost_priv {
|
||||
struct clk *clk_iface;
|
||||
struct clk *clk;
|
||||
u32 vendor_hs200;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
void sdhci_f_sdh30_soft_voltage_switch(struct sdhci_host *host)
|
||||
{
|
||||
struct f_sdhost_priv *priv = sdhci_priv(host);
|
||||
u32 ctrl = 0;
|
||||
|
||||
usleep_range(2500, 3000);
|
||||
ctrl = sdhci_readl(host, F_SDH30_IO_CONTROL2);
|
||||
ctrl |= F_SDH30_CRES_O_DN;
|
||||
sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2);
|
||||
ctrl |= F_SDH30_MSEL_O_1_8;
|
||||
sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2);
|
||||
|
||||
ctrl &= ~F_SDH30_CRES_O_DN;
|
||||
sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2);
|
||||
usleep_range(2500, 3000);
|
||||
|
||||
if (priv->vendor_hs200) {
|
||||
dev_info(priv->dev, "%s: setting hs200\n", __func__);
|
||||
ctrl = sdhci_readl(host, F_SDH30_ESD_CONTROL);
|
||||
ctrl |= priv->vendor_hs200;
|
||||
sdhci_writel(host, ctrl, F_SDH30_ESD_CONTROL);
|
||||
}
|
||||
|
||||
ctrl = sdhci_readl(host, F_SDH30_TUNING_SETTING);
|
||||
ctrl |= F_SDH30_CMD_CHK_DIS;
|
||||
sdhci_writel(host, ctrl, F_SDH30_TUNING_SETTING);
|
||||
}
|
||||
|
||||
unsigned int sdhci_f_sdh30_get_min_clock(struct sdhci_host *host)
|
||||
{
|
||||
return F_SDH30_MIN_CLOCK;
|
||||
}
|
||||
|
||||
void sdhci_f_sdh30_reset(struct sdhci_host *host, u8 mask)
|
||||
{
|
||||
if (sdhci_readw(host, SDHCI_CLOCK_CONTROL) == 0)
|
||||
sdhci_writew(host, 0xBC01, SDHCI_CLOCK_CONTROL);
|
||||
|
||||
sdhci_reset(host, mask);
|
||||
}
|
||||
|
||||
static const struct sdhci_ops sdhci_f_sdh30_ops = {
|
||||
.voltage_switch = sdhci_f_sdh30_soft_voltage_switch,
|
||||
.get_min_clock = sdhci_f_sdh30_get_min_clock,
|
||||
.reset = sdhci_f_sdh30_reset,
|
||||
.set_clock = sdhci_set_clock,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
};
|
||||
|
||||
static int sdhci_f_sdh30_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
int irq, ctrl = 0, ret = 0;
|
||||
struct f_sdhost_priv *priv;
|
||||
u32 reg = 0;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "%s: no irq specified\n", __func__);
|
||||
return irq;
|
||||
}
|
||||
|
||||
host = sdhci_alloc_host(dev, sizeof(struct sdhci_host) +
|
||||
sizeof(struct f_sdhost_priv));
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
|
||||
priv = sdhci_priv(host);
|
||||
priv->dev = dev;
|
||||
|
||||
host->quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
|
||||
SDHCI_QUIRK_INVERTED_WRITE_PROTECT;
|
||||
host->quirks2 = SDHCI_QUIRK2_SUPPORT_SINGLE |
|
||||
SDHCI_QUIRK2_TUNING_WORK_AROUND;
|
||||
|
||||
ret = mmc_of_parse(host->mmc);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
platform_set_drvdata(pdev, host);
|
||||
|
||||
sdhci_get_of_property(pdev);
|
||||
host->hw_name = "f_sdh30";
|
||||
host->ops = &sdhci_f_sdh30_ops;
|
||||
host->irq = irq;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
host->ioaddr = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(host->ioaddr)) {
|
||||
ret = PTR_ERR(host->ioaddr);
|
||||
goto err;
|
||||
}
|
||||
|
||||
priv->clk_iface = devm_clk_get(&pdev->dev, "iface");
|
||||
if (IS_ERR(priv->clk_iface)) {
|
||||
ret = PTR_ERR(priv->clk_iface);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(priv->clk_iface);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
priv->clk = devm_clk_get(&pdev->dev, "core");
|
||||
if (IS_ERR(priv->clk)) {
|
||||
ret = PTR_ERR(priv->clk);
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(priv->clk);
|
||||
if (ret)
|
||||
goto err_clk;
|
||||
|
||||
/* init vendor specific regs */
|
||||
ctrl = sdhci_readw(host, F_SDH30_AHB_CONFIG);
|
||||
ctrl |= F_SDH30_SIN | F_SDH30_AHB_INCR_16 | F_SDH30_AHB_INCR_8 |
|
||||
F_SDH30_AHB_INCR_4;
|
||||
ctrl &= ~(F_SDH30_AHB_BIGED | F_SDH30_BUSLOCK_EN);
|
||||
sdhci_writew(host, ctrl, F_SDH30_AHB_CONFIG);
|
||||
|
||||
reg = sdhci_readl(host, F_SDH30_ESD_CONTROL);
|
||||
sdhci_writel(host, reg & ~F_SDH30_EMMC_RST, F_SDH30_ESD_CONTROL);
|
||||
msleep(20);
|
||||
sdhci_writel(host, reg | F_SDH30_EMMC_RST, F_SDH30_ESD_CONTROL);
|
||||
|
||||
reg = sdhci_readl(host, SDHCI_CAPABILITIES);
|
||||
if (reg & SDHCI_CAN_DO_8BIT)
|
||||
priv->vendor_hs200 = F_SDH30_EMMC_HS200;
|
||||
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret)
|
||||
goto err_add_host;
|
||||
|
||||
return 0;
|
||||
|
||||
err_add_host:
|
||||
clk_disable_unprepare(priv->clk);
|
||||
err_clk:
|
||||
clk_disable_unprepare(priv->clk_iface);
|
||||
err:
|
||||
sdhci_free_host(host);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sdhci_f_sdh30_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct f_sdhost_priv *priv = sdhci_priv(host);
|
||||
|
||||
sdhci_remove_host(host, readl(host->ioaddr + SDHCI_INT_STATUS) ==
|
||||
0xffffffff);
|
||||
|
||||
clk_disable_unprepare(priv->clk_iface);
|
||||
clk_disable_unprepare(priv->clk);
|
||||
|
||||
sdhci_free_host(host);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id f_sdh30_dt_ids[] = {
|
||||
{ .compatible = "fujitsu,mb86s70-sdhci-3.0" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, f_sdh30_dt_ids);
|
||||
|
||||
static struct platform_driver sdhci_f_sdh30_driver = {
|
||||
.driver = {
|
||||
.name = "f_sdh30",
|
||||
.of_match_table = f_sdh30_dt_ids,
|
||||
.pm = SDHCI_PLTFM_PMOPS,
|
||||
},
|
||||
.probe = sdhci_f_sdh30_probe,
|
||||
.remove = sdhci_f_sdh30_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(sdhci_f_sdh30_driver);
|
||||
|
||||
MODULE_DESCRIPTION("F_SDH30 SD Card Controller driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("FUJITSU SEMICONDUCTOR LTD.");
|
||||
MODULE_ALIAS("platform:f_sdh30");
|
@ -35,10 +35,13 @@
|
||||
|
||||
#define EXT_ACC 0xe4
|
||||
|
||||
#define host_to_priv(host) container_of((host)->pdata, struct sh_mobile_sdhi, mmc_data)
|
||||
|
||||
struct sh_mobile_sdhi_of_data {
|
||||
unsigned long tmio_flags;
|
||||
unsigned long capabilities;
|
||||
unsigned long capabilities2;
|
||||
enum dma_slave_buswidth dma_buswidth;
|
||||
dma_addr_t dma_rx_offset;
|
||||
};
|
||||
|
||||
@ -58,6 +61,7 @@ static const struct sh_mobile_sdhi_of_data of_rcar_gen2_compatible = {
|
||||
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
|
||||
TMIO_MMC_CLK_ACTUAL,
|
||||
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
|
||||
.dma_buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES,
|
||||
.dma_rx_offset = 0x2000,
|
||||
};
|
||||
|
||||
@ -84,16 +88,43 @@ struct sh_mobile_sdhi {
|
||||
struct tmio_mmc_dma dma_priv;
|
||||
};
|
||||
|
||||
static void sh_mobile_sdhi_sdbuf_width(struct tmio_mmc_host *host, int width)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/*
|
||||
* see also
|
||||
* sh_mobile_sdhi_of_data :: dma_buswidth
|
||||
*/
|
||||
switch (sd_ctrl_read16(host, CTL_VERSION)) {
|
||||
case 0x490C:
|
||||
val = (width == 32) ? 0x0001 : 0x0000;
|
||||
break;
|
||||
case 0xCB0D:
|
||||
val = (width == 32) ? 0x0000 : 0x0001;
|
||||
break;
|
||||
default:
|
||||
/* nothing to do */
|
||||
return;
|
||||
}
|
||||
|
||||
sd_ctrl_write16(host, EXT_ACC, val);
|
||||
}
|
||||
|
||||
static int sh_mobile_sdhi_clk_enable(struct platform_device *pdev, unsigned int *f)
|
||||
{
|
||||
struct mmc_host *mmc = platform_get_drvdata(pdev);
|
||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||
struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data);
|
||||
struct sh_mobile_sdhi *priv = host_to_priv(host);
|
||||
int ret = clk_prepare_enable(priv->clk);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*f = clk_get_rate(priv->clk);
|
||||
|
||||
/* enable 16bit data access on SDBUF as default */
|
||||
sh_mobile_sdhi_sdbuf_width(host, 16);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -101,7 +132,7 @@ static void sh_mobile_sdhi_clk_disable(struct platform_device *pdev)
|
||||
{
|
||||
struct mmc_host *mmc = platform_get_drvdata(pdev);
|
||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||
struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data);
|
||||
struct sh_mobile_sdhi *priv = host_to_priv(host);
|
||||
clk_disable_unprepare(priv->clk);
|
||||
}
|
||||
|
||||
@ -113,7 +144,7 @@ static int sh_mobile_sdhi_wait_idle(struct tmio_mmc_host *host)
|
||||
udelay(1);
|
||||
|
||||
if (!timeout) {
|
||||
dev_warn(host->pdata->dev, "timeout waiting for SD bus idle\n");
|
||||
dev_warn(&host->pdev->dev, "timeout waiting for SD bus idle\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
@ -156,14 +187,13 @@ static int sh_mobile_sdhi_multi_io_quirk(struct mmc_card *card,
|
||||
return blk_size;
|
||||
}
|
||||
|
||||
static void sh_mobile_sdhi_cd_wakeup(const struct platform_device *pdev)
|
||||
static void sh_mobile_sdhi_enable_dma(struct tmio_mmc_host *host, bool enable)
|
||||
{
|
||||
mmc_detect_change(platform_get_drvdata(pdev), msecs_to_jiffies(100));
|
||||
}
|
||||
sd_ctrl_write16(host, CTL_DMA_ENABLE, enable ? 2 : 0);
|
||||
|
||||
static const struct sh_mobile_sdhi_ops sdhi_ops = {
|
||||
.cd_wakeup = sh_mobile_sdhi_cd_wakeup,
|
||||
};
|
||||
/* enable 32bit access if DMA mode if possibile */
|
||||
sh_mobile_sdhi_sdbuf_width(host, enable ? 32 : 16);
|
||||
}
|
||||
|
||||
static int sh_mobile_sdhi_probe(struct platform_device *pdev)
|
||||
{
|
||||
@ -177,7 +207,6 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
|
||||
int irq, ret, i = 0;
|
||||
bool multiplexed_isr = true;
|
||||
struct tmio_mmc_dma *dma_priv;
|
||||
u16 ver;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
@ -192,26 +221,31 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
|
||||
mmc_data = &priv->mmc_data;
|
||||
dma_priv = &priv->dma_priv;
|
||||
|
||||
if (p) {
|
||||
if (p->init) {
|
||||
ret = p->init(pdev, &sdhi_ops);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
priv->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(priv->clk)) {
|
||||
ret = PTR_ERR(priv->clk);
|
||||
dev_err(&pdev->dev, "cannot get clock: %d\n", ret);
|
||||
goto eclkget;
|
||||
goto eprobe;
|
||||
}
|
||||
|
||||
mmc_data->clk_enable = sh_mobile_sdhi_clk_enable;
|
||||
mmc_data->clk_disable = sh_mobile_sdhi_clk_disable;
|
||||
host = tmio_mmc_host_alloc(pdev);
|
||||
if (!host) {
|
||||
ret = -ENOMEM;
|
||||
goto eprobe;
|
||||
}
|
||||
|
||||
host->dma = dma_priv;
|
||||
host->write16_hook = sh_mobile_sdhi_write16_hook;
|
||||
host->clk_enable = sh_mobile_sdhi_clk_enable;
|
||||
host->clk_disable = sh_mobile_sdhi_clk_disable;
|
||||
host->multi_io_quirk = sh_mobile_sdhi_multi_io_quirk;
|
||||
/* SD control register space size is 0x100, 0x200 for bus_shift=1 */
|
||||
if (resource_size(res) > 0x100)
|
||||
host->bus_shift = 1;
|
||||
else
|
||||
host->bus_shift = 0;
|
||||
|
||||
mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED;
|
||||
mmc_data->write16_hook = sh_mobile_sdhi_write16_hook;
|
||||
mmc_data->multi_io_quirk = sh_mobile_sdhi_multi_io_quirk;
|
||||
if (p) {
|
||||
mmc_data->flags = p->tmio_flags;
|
||||
mmc_data->ocr_mask = p->tmio_ocr_mask;
|
||||
@ -231,11 +265,10 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
|
||||
dma_priv->slave_id_rx = p->dma_slave_rx;
|
||||
}
|
||||
}
|
||||
|
||||
dma_priv->alignment_shift = 1; /* 2-byte alignment */
|
||||
dma_priv->filter = shdma_chan_filter;
|
||||
dma_priv->enable = sh_mobile_sdhi_enable_dma;
|
||||
|
||||
mmc_data->dma = dma_priv;
|
||||
mmc_data->alignment_shift = 1; /* 2-byte alignment */
|
||||
|
||||
/*
|
||||
* All SDHI blocks support 2-byte and larger block sizes in 4-bit
|
||||
@ -258,33 +291,18 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
|
||||
*/
|
||||
mmc_data->flags |= TMIO_MMC_SDIO_STATUS_QUIRK;
|
||||
|
||||
/*
|
||||
* All SDHI have DMA control register
|
||||
*/
|
||||
mmc_data->flags |= TMIO_MMC_HAVE_CTL_DMA_REG;
|
||||
|
||||
if (of_id && of_id->data) {
|
||||
const struct sh_mobile_sdhi_of_data *of_data = of_id->data;
|
||||
mmc_data->flags |= of_data->tmio_flags;
|
||||
mmc_data->capabilities |= of_data->capabilities;
|
||||
mmc_data->capabilities2 |= of_data->capabilities2;
|
||||
dma_priv->dma_rx_offset = of_data->dma_rx_offset;
|
||||
mmc_data->dma_rx_offset = of_data->dma_rx_offset;
|
||||
dma_priv->dma_buswidth = of_data->dma_buswidth;
|
||||
}
|
||||
|
||||
/* 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, mmc_data);
|
||||
if (ret < 0)
|
||||
goto eprobe;
|
||||
|
||||
/*
|
||||
* FIXME:
|
||||
* this Workaround can be more clever method
|
||||
*/
|
||||
ver = sd_ctrl_read16(host, CTL_VERSION);
|
||||
if (ver == 0xCB0D)
|
||||
sd_ctrl_write16(host, EXT_ACC, 1);
|
||||
goto efree;
|
||||
|
||||
/*
|
||||
* Allow one or more specific (named) ISRs or
|
||||
@ -351,10 +369,9 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
|
||||
|
||||
eirq:
|
||||
tmio_mmc_host_remove(host);
|
||||
efree:
|
||||
tmio_mmc_host_free(host);
|
||||
eprobe:
|
||||
eclkget:
|
||||
if (p && p->cleanup)
|
||||
p->cleanup(pdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -362,13 +379,9 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mmc_host *mmc = platform_get_drvdata(pdev);
|
||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||
struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
|
||||
|
||||
tmio_mmc_host_remove(host);
|
||||
|
||||
if (p && p->cleanup)
|
||||
p->cleanup(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -252,7 +252,7 @@ static int sunxi_mmc_reset_host(struct sunxi_mmc_host *host)
|
||||
unsigned long expire = jiffies + msecs_to_jiffies(250);
|
||||
u32 rval;
|
||||
|
||||
mmc_writel(host, REG_CMDR, SDXC_HARDWARE_RESET);
|
||||
mmc_writel(host, REG_GCTRL, SDXC_HARDWARE_RESET);
|
||||
do {
|
||||
rval = mmc_readl(host, REG_GCTRL);
|
||||
} while (time_before(jiffies, expire) && (rval & SDXC_HARDWARE_RESET));
|
||||
@ -310,7 +310,9 @@ static void sunxi_mmc_init_idma_des(struct sunxi_mmc_host *host,
|
||||
}
|
||||
|
||||
pdes[0].config |= SDXC_IDMAC_DES0_FD;
|
||||
pdes[i - 1].config = SDXC_IDMAC_DES0_OWN | SDXC_IDMAC_DES0_LD;
|
||||
pdes[i - 1].config |= SDXC_IDMAC_DES0_LD | SDXC_IDMAC_DES0_ER;
|
||||
pdes[i - 1].config &= ~SDXC_IDMAC_DES0_DIC;
|
||||
pdes[i - 1].buf_addr_ptr2 = 0;
|
||||
|
||||
/*
|
||||
* Avoid the io-store starting the idmac hitting io-mem before the
|
||||
@ -570,6 +572,15 @@ static irqreturn_t sunxi_mmc_handle_manual_stop(int irq, void *dev_id)
|
||||
}
|
||||
|
||||
dev_err(mmc_dev(host->mmc), "data error, sending stop command\n");
|
||||
|
||||
/*
|
||||
* We will never have more than one outstanding request,
|
||||
* and we do not complete the request until after
|
||||
* we've cleared host->manual_stop_mrq so we do not need to
|
||||
* spin lock this function.
|
||||
* Additionally we have wait states within this function
|
||||
* so having it in a lock is a very bad idea.
|
||||
*/
|
||||
sunxi_mmc_send_manual_stop(host, mrq);
|
||||
|
||||
spin_lock_irqsave(&host->lock, iflags);
|
||||
@ -616,7 +627,7 @@ static int sunxi_mmc_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en)
|
||||
static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host,
|
||||
struct mmc_ios *ios)
|
||||
{
|
||||
u32 rate, oclk_dly, rval, sclk_dly, src_clk;
|
||||
u32 rate, oclk_dly, rval, sclk_dly;
|
||||
int ret;
|
||||
|
||||
rate = clk_round_rate(host->clk_mmc, ios->clock);
|
||||
@ -661,14 +672,6 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host,
|
||||
sclk_dly = 4;
|
||||
}
|
||||
|
||||
src_clk = clk_get_rate(clk_get_parent(host->clk_mmc));
|
||||
if (src_clk >= 300000000 && src_clk <= 400000000) {
|
||||
if (oclk_dly)
|
||||
oclk_dly--;
|
||||
if (sclk_dly)
|
||||
sclk_dly--;
|
||||
}
|
||||
|
||||
clk_sunxi_mmc_phase_control(host->clk_mmc, sclk_dly, oclk_dly);
|
||||
|
||||
return sunxi_mmc_oclk_onoff(host, 1);
|
||||
@ -766,6 +769,7 @@ static void sunxi_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
unsigned long iflags;
|
||||
u32 imask = SDXC_INTERRUPT_ERROR_BIT;
|
||||
u32 cmd_val = SDXC_START | (cmd->opcode & 0x3f);
|
||||
bool wait_dma = host->wait_dma;
|
||||
int ret;
|
||||
|
||||
/* Check for set_ios errors (should never happen) */
|
||||
@ -816,7 +820,7 @@ static void sunxi_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
if (cmd->data->flags & MMC_DATA_WRITE)
|
||||
cmd_val |= SDXC_WRITE;
|
||||
else
|
||||
host->wait_dma = true;
|
||||
wait_dma = true;
|
||||
} else {
|
||||
imask |= SDXC_COMMAND_DONE;
|
||||
}
|
||||
@ -850,6 +854,7 @@ static void sunxi_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
}
|
||||
|
||||
host->mrq = mrq;
|
||||
host->wait_dma = wait_dma;
|
||||
mmc_writel(host, REG_IMASK, host->sdio_imask | imask);
|
||||
mmc_writel(host, REG_CARG, cmd->arg);
|
||||
mmc_writel(host, REG_CMDR, cmd_val);
|
||||
|
@ -88,14 +88,19 @@ static int tmio_mmc_probe(struct platform_device *pdev)
|
||||
if (!res)
|
||||
return -EINVAL;
|
||||
|
||||
/* SD control register space size is 0x200, 0x400 for bus_shift=1 */
|
||||
pdata->bus_shift = resource_size(res) >> 10;
|
||||
pdata->flags |= TMIO_MMC_HAVE_HIGH_REG;
|
||||
|
||||
ret = tmio_mmc_host_probe(&host, pdev, pdata);
|
||||
if (ret)
|
||||
host = tmio_mmc_host_alloc(pdev);
|
||||
if (!host)
|
||||
goto cell_disable;
|
||||
|
||||
/* SD control register space size is 0x200, 0x400 for bus_shift=1 */
|
||||
host->bus_shift = resource_size(res) >> 10;
|
||||
|
||||
ret = tmio_mmc_host_probe(host, pdata);
|
||||
if (ret)
|
||||
goto host_free;
|
||||
|
||||
ret = request_irq(irq, tmio_mmc_irq, IRQF_TRIGGER_FALLING,
|
||||
dev_name(&pdev->dev), host);
|
||||
if (ret)
|
||||
@ -108,6 +113,8 @@ static int tmio_mmc_probe(struct platform_device *pdev)
|
||||
|
||||
host_remove:
|
||||
tmio_mmc_host_remove(host);
|
||||
host_free:
|
||||
tmio_mmc_host_free(host);
|
||||
cell_disable:
|
||||
if (cell->disable)
|
||||
cell->disable(pdev);
|
||||
|
@ -16,6 +16,7 @@
|
||||
#ifndef TMIO_MMC_H
|
||||
#define TMIO_MMC_H
|
||||
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/mmc/tmio.h>
|
||||
#include <linux/mutex.h>
|
||||
@ -39,6 +40,17 @@
|
||||
#define TMIO_MASK_IRQ (TMIO_MASK_READOP | TMIO_MASK_WRITEOP | TMIO_MASK_CMD)
|
||||
|
||||
struct tmio_mmc_data;
|
||||
struct tmio_mmc_host;
|
||||
|
||||
struct tmio_mmc_dma {
|
||||
void *chan_priv_tx;
|
||||
void *chan_priv_rx;
|
||||
int slave_id_tx;
|
||||
int slave_id_rx;
|
||||
enum dma_slave_buswidth dma_buswidth;
|
||||
bool (*filter)(struct dma_chan *chan, void *arg);
|
||||
void (*enable)(struct tmio_mmc_host *host, bool enable);
|
||||
};
|
||||
|
||||
struct tmio_mmc_host {
|
||||
void __iomem *ctl;
|
||||
@ -56,9 +68,11 @@ struct tmio_mmc_host {
|
||||
struct scatterlist *sg_orig;
|
||||
unsigned int sg_len;
|
||||
unsigned int sg_off;
|
||||
unsigned long bus_shift;
|
||||
|
||||
struct platform_device *pdev;
|
||||
struct tmio_mmc_data *pdata;
|
||||
struct tmio_mmc_dma *dma;
|
||||
|
||||
/* DMA support */
|
||||
bool force_pio;
|
||||
@ -83,10 +97,17 @@ struct tmio_mmc_host {
|
||||
struct mutex ios_lock; /* protect set_ios() context */
|
||||
bool native_hotplug;
|
||||
bool sdio_irq_enabled;
|
||||
|
||||
int (*write16_hook)(struct tmio_mmc_host *host, int addr);
|
||||
int (*clk_enable)(struct platform_device *pdev, unsigned int *f);
|
||||
void (*clk_disable)(struct platform_device *pdev);
|
||||
int (*multi_io_quirk)(struct mmc_card *card,
|
||||
unsigned int direction, int blk_size);
|
||||
};
|
||||
|
||||
int tmio_mmc_host_probe(struct tmio_mmc_host **host,
|
||||
struct platform_device *pdev,
|
||||
struct tmio_mmc_host *tmio_mmc_host_alloc(struct platform_device *pdev);
|
||||
void tmio_mmc_host_free(struct tmio_mmc_host *host);
|
||||
int tmio_mmc_host_probe(struct tmio_mmc_host *host,
|
||||
struct tmio_mmc_data *pdata);
|
||||
void tmio_mmc_host_remove(struct tmio_mmc_host *host);
|
||||
void tmio_mmc_do_data_irq(struct tmio_mmc_host *host);
|
||||
@ -151,19 +172,19 @@ int tmio_mmc_host_runtime_resume(struct device *dev);
|
||||
|
||||
static inline u16 sd_ctrl_read16(struct tmio_mmc_host *host, int addr)
|
||||
{
|
||||
return readw(host->ctl + (addr << host->pdata->bus_shift));
|
||||
return readw(host->ctl + (addr << host->bus_shift));
|
||||
}
|
||||
|
||||
static inline void sd_ctrl_read16_rep(struct tmio_mmc_host *host, int addr,
|
||||
u16 *buf, int count)
|
||||
{
|
||||
readsw(host->ctl + (addr << host->pdata->bus_shift), buf, count);
|
||||
readsw(host->ctl + (addr << host->bus_shift), buf, count);
|
||||
}
|
||||
|
||||
static inline u32 sd_ctrl_read32(struct tmio_mmc_host *host, int addr)
|
||||
{
|
||||
return readw(host->ctl + (addr << host->pdata->bus_shift)) |
|
||||
readw(host->ctl + ((addr + 2) << host->pdata->bus_shift)) << 16;
|
||||
return readw(host->ctl + (addr << host->bus_shift)) |
|
||||
readw(host->ctl + ((addr + 2) << host->bus_shift)) << 16;
|
||||
}
|
||||
|
||||
static inline void sd_ctrl_write16(struct tmio_mmc_host *host, int addr, u16 val)
|
||||
@ -171,21 +192,21 @@ static inline void sd_ctrl_write16(struct tmio_mmc_host *host, int addr, u16 val
|
||||
/* If there is a hook and it returns non-zero then there
|
||||
* is an error and the write should be skipped
|
||||
*/
|
||||
if (host->pdata->write16_hook && host->pdata->write16_hook(host, addr))
|
||||
if (host->write16_hook && host->write16_hook(host, addr))
|
||||
return;
|
||||
writew(val, host->ctl + (addr << host->pdata->bus_shift));
|
||||
writew(val, host->ctl + (addr << host->bus_shift));
|
||||
}
|
||||
|
||||
static inline void sd_ctrl_write16_rep(struct tmio_mmc_host *host, int addr,
|
||||
u16 *buf, int count)
|
||||
{
|
||||
writesw(host->ctl + (addr << host->pdata->bus_shift), buf, count);
|
||||
writesw(host->ctl + (addr << host->bus_shift), buf, count);
|
||||
}
|
||||
|
||||
static inline void sd_ctrl_write32(struct tmio_mmc_host *host, int addr, u32 val)
|
||||
{
|
||||
writew(val, host->ctl + (addr << host->pdata->bus_shift));
|
||||
writew(val >> 16, host->ctl + ((addr + 2) << host->pdata->bus_shift));
|
||||
writew(val, host->ctl + (addr << host->bus_shift));
|
||||
writew(val >> 16, host->ctl + ((addr + 2) << host->bus_shift));
|
||||
}
|
||||
|
||||
|
||||
|
@ -28,8 +28,8 @@ void tmio_mmc_enable_dma(struct tmio_mmc_host *host, bool enable)
|
||||
if (!host->chan_tx || !host->chan_rx)
|
||||
return;
|
||||
|
||||
if (host->pdata->flags & TMIO_MMC_HAVE_CTL_DMA_REG)
|
||||
sd_ctrl_write16(host, CTL_DMA_ENABLE, enable ? 2 : 0);
|
||||
if (host->dma->enable)
|
||||
host->dma->enable(host, enable);
|
||||
}
|
||||
|
||||
void tmio_mmc_abort_dma(struct tmio_mmc_host *host)
|
||||
@ -49,11 +49,10 @@ static void tmio_mmc_start_dma_rx(struct tmio_mmc_host *host)
|
||||
struct scatterlist *sg = host->sg_ptr, *sg_tmp;
|
||||
struct dma_async_tx_descriptor *desc = NULL;
|
||||
struct dma_chan *chan = host->chan_rx;
|
||||
struct tmio_mmc_data *pdata = host->pdata;
|
||||
dma_cookie_t cookie;
|
||||
int ret, i;
|
||||
bool aligned = true, multiple = true;
|
||||
unsigned int align = (1 << pdata->dma->alignment_shift) - 1;
|
||||
unsigned int align = (1 << host->pdata->alignment_shift) - 1;
|
||||
|
||||
for_each_sg(sg, sg_tmp, host->sg_len, i) {
|
||||
if (sg_tmp->offset & align)
|
||||
@ -126,11 +125,10 @@ static void tmio_mmc_start_dma_tx(struct tmio_mmc_host *host)
|
||||
struct scatterlist *sg = host->sg_ptr, *sg_tmp;
|
||||
struct dma_async_tx_descriptor *desc = NULL;
|
||||
struct dma_chan *chan = host->chan_tx;
|
||||
struct tmio_mmc_data *pdata = host->pdata;
|
||||
dma_cookie_t cookie;
|
||||
int ret, i;
|
||||
bool aligned = true, multiple = true;
|
||||
unsigned int align = (1 << pdata->dma->alignment_shift) - 1;
|
||||
unsigned int align = (1 << host->pdata->alignment_shift) - 1;
|
||||
|
||||
for_each_sg(sg, sg_tmp, host->sg_len, i) {
|
||||
if (sg_tmp->offset & align)
|
||||
@ -262,8 +260,8 @@ out:
|
||||
void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdata)
|
||||
{
|
||||
/* We can only either use DMA for both Tx and Rx or not use it at all */
|
||||
if (!pdata->dma || (!host->pdev->dev.of_node &&
|
||||
(!pdata->dma->chan_priv_tx || !pdata->dma->chan_priv_rx)))
|
||||
if (!host->dma || (!host->pdev->dev.of_node &&
|
||||
(!host->dma->chan_priv_tx || !host->dma->chan_priv_rx)))
|
||||
return;
|
||||
|
||||
if (!host->chan_tx && !host->chan_rx) {
|
||||
@ -280,7 +278,7 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
|
||||
host->chan_tx = dma_request_slave_channel_compat(mask,
|
||||
pdata->dma->filter, pdata->dma->chan_priv_tx,
|
||||
host->dma->filter, host->dma->chan_priv_tx,
|
||||
&host->pdev->dev, "tx");
|
||||
dev_dbg(&host->pdev->dev, "%s: TX: got channel %p\n", __func__,
|
||||
host->chan_tx);
|
||||
@ -288,10 +286,12 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat
|
||||
if (!host->chan_tx)
|
||||
return;
|
||||
|
||||
if (pdata->dma->chan_priv_tx)
|
||||
cfg.slave_id = pdata->dma->slave_id_tx;
|
||||
if (host->dma->chan_priv_tx)
|
||||
cfg.slave_id = host->dma->slave_id_tx;
|
||||
cfg.direction = DMA_MEM_TO_DEV;
|
||||
cfg.dst_addr = res->start + (CTL_SD_DATA_PORT << host->pdata->bus_shift);
|
||||
cfg.dst_addr = res->start + (CTL_SD_DATA_PORT << host->bus_shift);
|
||||
cfg.dst_addr_width = host->dma->dma_buswidth;
|
||||
if (!cfg.dst_addr_width)
|
||||
cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
|
||||
cfg.src_addr = 0;
|
||||
ret = dmaengine_slave_config(host->chan_tx, &cfg);
|
||||
@ -299,7 +299,7 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat
|
||||
goto ecfgtx;
|
||||
|
||||
host->chan_rx = dma_request_slave_channel_compat(mask,
|
||||
pdata->dma->filter, pdata->dma->chan_priv_rx,
|
||||
host->dma->filter, host->dma->chan_priv_rx,
|
||||
&host->pdev->dev, "rx");
|
||||
dev_dbg(&host->pdev->dev, "%s: RX: got channel %p\n", __func__,
|
||||
host->chan_rx);
|
||||
@ -307,10 +307,12 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat
|
||||
if (!host->chan_rx)
|
||||
goto ereqrx;
|
||||
|
||||
if (pdata->dma->chan_priv_rx)
|
||||
cfg.slave_id = pdata->dma->slave_id_rx;
|
||||
if (host->dma->chan_priv_rx)
|
||||
cfg.slave_id = host->dma->slave_id_rx;
|
||||
cfg.direction = DMA_DEV_TO_MEM;
|
||||
cfg.src_addr = cfg.dst_addr + pdata->dma->dma_rx_offset;
|
||||
cfg.src_addr = cfg.dst_addr + host->pdata->dma_rx_offset;
|
||||
cfg.src_addr_width = host->dma->dma_buswidth;
|
||||
if (!cfg.src_addr_width)
|
||||
cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
|
||||
cfg.dst_addr = 0;
|
||||
ret = dmaengine_slave_config(host->chan_rx, &cfg);
|
||||
|
@ -835,13 +835,12 @@ fail:
|
||||
static int tmio_mmc_clk_update(struct tmio_mmc_host *host)
|
||||
{
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
struct tmio_mmc_data *pdata = host->pdata;
|
||||
int ret;
|
||||
|
||||
if (!pdata->clk_enable)
|
||||
if (!host->clk_enable)
|
||||
return -ENOTSUPP;
|
||||
|
||||
ret = pdata->clk_enable(host->pdev, &mmc->f_max);
|
||||
ret = host->clk_enable(host->pdev, &mmc->f_max);
|
||||
if (!ret)
|
||||
mmc->f_min = mmc->f_max / 512;
|
||||
|
||||
@ -1005,10 +1004,9 @@ static int tmio_multi_io_quirk(struct mmc_card *card,
|
||||
unsigned int direction, int blk_size)
|
||||
{
|
||||
struct tmio_mmc_host *host = mmc_priv(card->host);
|
||||
struct tmio_mmc_data *pdata = host->pdata;
|
||||
|
||||
if (pdata->multi_io_quirk)
|
||||
return pdata->multi_io_quirk(card, direction, blk_size);
|
||||
if (host->multi_io_quirk)
|
||||
return host->multi_io_quirk(card, direction, blk_size);
|
||||
|
||||
return blk_size;
|
||||
}
|
||||
@ -1054,12 +1052,37 @@ static void tmio_mmc_of_parse(struct platform_device *pdev,
|
||||
pdata->flags |= TMIO_MMC_WRPROTECT_DISABLE;
|
||||
}
|
||||
|
||||
int tmio_mmc_host_probe(struct tmio_mmc_host **host,
|
||||
struct platform_device *pdev,
|
||||
struct tmio_mmc_host*
|
||||
tmio_mmc_host_alloc(struct platform_device *pdev)
|
||||
{
|
||||
struct tmio_mmc_host *host;
|
||||
struct mmc_host *mmc;
|
||||
|
||||
mmc = mmc_alloc_host(sizeof(struct tmio_mmc_host), &pdev->dev);
|
||||
if (!mmc)
|
||||
return NULL;
|
||||
|
||||
host = mmc_priv(mmc);
|
||||
host->mmc = mmc;
|
||||
host->pdev = pdev;
|
||||
|
||||
return host;
|
||||
}
|
||||
EXPORT_SYMBOL(tmio_mmc_host_alloc);
|
||||
|
||||
void tmio_mmc_host_free(struct tmio_mmc_host *host)
|
||||
{
|
||||
mmc_free_host(host->mmc);
|
||||
|
||||
host->mmc = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(tmio_mmc_host_free);
|
||||
|
||||
int tmio_mmc_host_probe(struct tmio_mmc_host *_host,
|
||||
struct tmio_mmc_data *pdata)
|
||||
{
|
||||
struct tmio_mmc_host *_host;
|
||||
struct mmc_host *mmc;
|
||||
struct platform_device *pdev = _host->pdev;
|
||||
struct mmc_host *mmc = _host->mmc;
|
||||
struct resource *res_ctl;
|
||||
int ret;
|
||||
u32 irq_mask = TMIO_MASK_CMD;
|
||||
@ -1067,25 +1090,17 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host,
|
||||
tmio_mmc_of_parse(pdev, pdata);
|
||||
|
||||
if (!(pdata->flags & TMIO_MMC_HAS_IDLE_WAIT))
|
||||
pdata->write16_hook = NULL;
|
||||
_host->write16_hook = NULL;
|
||||
|
||||
res_ctl = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res_ctl)
|
||||
return -EINVAL;
|
||||
|
||||
mmc = mmc_alloc_host(sizeof(struct tmio_mmc_host), &pdev->dev);
|
||||
if (!mmc)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = mmc_of_parse(mmc);
|
||||
if (ret < 0)
|
||||
goto host_free;
|
||||
|
||||
pdata->dev = &pdev->dev;
|
||||
_host = mmc_priv(mmc);
|
||||
_host->pdata = pdata;
|
||||
_host->mmc = mmc;
|
||||
_host->pdev = pdev;
|
||||
platform_set_drvdata(pdev, mmc);
|
||||
|
||||
_host->set_pwr = pdata->set_pwr;
|
||||
@ -1192,12 +1207,9 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host,
|
||||
mmc_gpiod_request_cd_irq(mmc);
|
||||
}
|
||||
|
||||
*host = _host;
|
||||
|
||||
return 0;
|
||||
|
||||
host_free:
|
||||
mmc_free_host(mmc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -1222,7 +1234,6 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host)
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
iounmap(host->ctl);
|
||||
mmc_free_host(mmc);
|
||||
}
|
||||
EXPORT_SYMBOL(tmio_mmc_host_remove);
|
||||
|
||||
@ -1237,8 +1248,8 @@ int tmio_mmc_host_runtime_suspend(struct device *dev)
|
||||
if (host->clk_cache)
|
||||
tmio_mmc_clk_stop(host);
|
||||
|
||||
if (host->pdata->clk_disable)
|
||||
host->pdata->clk_disable(host->pdev);
|
||||
if (host->clk_disable)
|
||||
host->clk_disable(host->pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -176,7 +176,8 @@ static irqreturn_t toshsd_thread_irq(int irq, void *dev_id)
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
if (!sg_miter_next(sg_miter))
|
||||
return IRQ_HANDLED;
|
||||
goto done;
|
||||
|
||||
buf = sg_miter->addr;
|
||||
|
||||
/* Ensure we dont read more than one block. The chip will interrupt us
|
||||
@ -198,6 +199,7 @@ static irqreturn_t toshsd_thread_irq(int irq, void *dev_id)
|
||||
sg_miter->consumed = count;
|
||||
sg_miter_stop(sg_miter);
|
||||
|
||||
done:
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
@ -699,18 +701,7 @@ static struct pci_driver toshsd_driver = {
|
||||
.driver.pm = &toshsd_pm_ops,
|
||||
};
|
||||
|
||||
static int __init toshsd_drv_init(void)
|
||||
{
|
||||
return pci_register_driver(&toshsd_driver);
|
||||
}
|
||||
|
||||
static void __exit toshsd_drv_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&toshsd_driver);
|
||||
}
|
||||
|
||||
module_init(toshsd_drv_init);
|
||||
module_exit(toshsd_drv_exit);
|
||||
module_pci_driver(toshsd_driver);
|
||||
|
||||
MODULE_AUTHOR("Ondrej Zary, Richard Betts");
|
||||
MODULE_DESCRIPTION("Toshiba PCI Secure Digital Host Controller Interface driver");
|
||||
|
@ -659,7 +659,7 @@ static void __vub300_irqpoll_response(struct vub300_mmc_host *vub300)
|
||||
static void __do_poll(struct vub300_mmc_host *vub300)
|
||||
{
|
||||
/* cmd_mutex is held by vub300_pollwork_thread */
|
||||
long commretval;
|
||||
unsigned long commretval;
|
||||
mod_timer(&vub300->inactivity_timer, jiffies + HZ);
|
||||
init_completion(&vub300->irqpoll_complete);
|
||||
send_irqpoll(vub300);
|
||||
@ -671,8 +671,6 @@ static void __do_poll(struct vub300_mmc_host *vub300)
|
||||
vub300->usb_timed_out = 1;
|
||||
usb_kill_urb(vub300->command_out_urb);
|
||||
usb_kill_urb(vub300->command_res_urb);
|
||||
} else if (commretval < 0) {
|
||||
vub300_queue_poll_work(vub300, 1);
|
||||
} else { /* commretval > 0 */
|
||||
__vub300_irqpoll_response(vub300);
|
||||
}
|
||||
|
@ -95,11 +95,6 @@
|
||||
*/
|
||||
#define TMIO_MMC_SDIO_STATUS_QUIRK (1 << 8)
|
||||
|
||||
/*
|
||||
* Some controllers have DMA enable/disable register
|
||||
*/
|
||||
#define TMIO_MMC_HAVE_CTL_DMA_REG (1 << 9)
|
||||
|
||||
/*
|
||||
* Some controllers allows to set SDx actual clock
|
||||
*/
|
||||
@ -112,18 +107,6 @@ void tmio_core_mmc_clk_div(void __iomem *cnf, int shift, int state);
|
||||
|
||||
struct dma_chan;
|
||||
|
||||
struct tmio_mmc_dma {
|
||||
void *chan_priv_tx;
|
||||
void *chan_priv_rx;
|
||||
int slave_id_tx;
|
||||
int slave_id_rx;
|
||||
int alignment_shift;
|
||||
dma_addr_t dma_rx_offset;
|
||||
bool (*filter)(struct dma_chan *chan, void *arg);
|
||||
};
|
||||
|
||||
struct tmio_mmc_host;
|
||||
|
||||
/*
|
||||
* data for the MMC controller
|
||||
*/
|
||||
@ -132,19 +115,12 @@ struct tmio_mmc_data {
|
||||
unsigned long capabilities;
|
||||
unsigned long capabilities2;
|
||||
unsigned long flags;
|
||||
unsigned long bus_shift;
|
||||
u32 ocr_mask; /* available voltages */
|
||||
struct tmio_mmc_dma *dma;
|
||||
struct device *dev;
|
||||
unsigned int cd_gpio;
|
||||
int alignment_shift;
|
||||
dma_addr_t dma_rx_offset;
|
||||
void (*set_pwr)(struct platform_device *host, int state);
|
||||
void (*set_clk_div)(struct platform_device *host, int state);
|
||||
int (*write16_hook)(struct tmio_mmc_host *host, int addr);
|
||||
/* clock management callbacks */
|
||||
int (*clk_enable)(struct platform_device *pdev, unsigned int *f);
|
||||
void (*clk_disable)(struct platform_device *pdev);
|
||||
int (*multi_io_quirk)(struct mmc_card *card,
|
||||
unsigned int direction, int blk_size);
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -83,7 +83,7 @@ struct mmc_ext_csd {
|
||||
bool hpi; /* HPI support bit */
|
||||
unsigned int hpi_cmd; /* cmd used as HPI */
|
||||
bool bkops; /* background support bit */
|
||||
bool bkops_en; /* background enable bit */
|
||||
bool man_bkops_en; /* manual bkops enable bit */
|
||||
unsigned int data_sector_size; /* 512 bytes or 4KB */
|
||||
unsigned int data_tag_unit_size; /* DATA TAG UNIT size */
|
||||
unsigned int boot_ro_lock; /* ro lock support */
|
||||
|
@ -182,7 +182,6 @@ extern int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen);
|
||||
extern int mmc_set_blockcount(struct mmc_card *card, unsigned int blockcount,
|
||||
bool is_rel_write);
|
||||
extern int mmc_hw_reset(struct mmc_host *host);
|
||||
extern int mmc_hw_reset_check(struct mmc_host *host);
|
||||
extern int mmc_can_reset(struct mmc_card *card);
|
||||
|
||||
extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *);
|
||||
|
@ -106,6 +106,11 @@ struct mmc_data;
|
||||
* @cur_slot, @mrq and @state. These must always be updated
|
||||
* at the same time while holding @lock.
|
||||
*
|
||||
* @irq_lock is an irq-safe spinlock protecting the INTMASK register
|
||||
* to allow the interrupt handler to modify it directly. Held for only long
|
||||
* enough to read-modify-write INTMASK and no other locks are grabbed when
|
||||
* holding this one.
|
||||
*
|
||||
* The @mrq field of struct dw_mci_slot is also protected by @lock,
|
||||
* and must always be written at the same time as the slot is added to
|
||||
* @queue.
|
||||
@ -125,6 +130,7 @@ struct mmc_data;
|
||||
*/
|
||||
struct dw_mci {
|
||||
spinlock_t lock;
|
||||
spinlock_t irq_lock;
|
||||
void __iomem *regs;
|
||||
|
||||
struct scatterlist *sg;
|
||||
|
@ -166,7 +166,6 @@ struct mmc_async_req {
|
||||
* struct mmc_slot - MMC slot functions
|
||||
*
|
||||
* @cd_irq: MMC/SD-card slot hotplug detection IRQ or -EINVAL
|
||||
* @lock: protect the @handler_priv pointer
|
||||
* @handler_priv: MMC/SD-card slot context
|
||||
*
|
||||
* Some MMC/SD host controllers implement slot-functions like card and
|
||||
@ -176,7 +175,6 @@ struct mmc_async_req {
|
||||
*/
|
||||
struct mmc_slot {
|
||||
int cd_irq;
|
||||
struct mutex lock;
|
||||
void *handler_priv;
|
||||
};
|
||||
|
||||
@ -197,6 +195,7 @@ struct mmc_context_info {
|
||||
};
|
||||
|
||||
struct regulator;
|
||||
struct mmc_pwrseq;
|
||||
|
||||
struct mmc_supply {
|
||||
struct regulator *vmmc; /* Card power supply */
|
||||
@ -208,6 +207,7 @@ struct mmc_host {
|
||||
struct device class_dev;
|
||||
int index;
|
||||
const struct mmc_host_ops *ops;
|
||||
struct mmc_pwrseq *pwrseq;
|
||||
unsigned int f_min;
|
||||
unsigned int f_max;
|
||||
unsigned int f_init;
|
||||
|
@ -53,11 +53,6 @@
|
||||
#define MMC_SEND_TUNING_BLOCK 19 /* adtc R1 */
|
||||
#define MMC_SEND_TUNING_BLOCK_HS200 21 /* adtc R1 */
|
||||
|
||||
#define MMC_TUNING_BLK_PATTERN_4BIT_SIZE 64
|
||||
#define MMC_TUNING_BLK_PATTERN_8BIT_SIZE 128
|
||||
extern const u8 tuning_blk_pattern_4bit[MMC_TUNING_BLK_PATTERN_4BIT_SIZE];
|
||||
extern const u8 tuning_blk_pattern_8bit[MMC_TUNING_BLK_PATTERN_8BIT_SIZE];
|
||||
|
||||
/* class 3 */
|
||||
#define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */
|
||||
|
||||
@ -432,6 +427,11 @@ struct _mmc_csd {
|
||||
*/
|
||||
#define EXT_CSD_BKOPS_LEVEL_2 0x2
|
||||
|
||||
/*
|
||||
* BKOPS modes
|
||||
*/
|
||||
#define EXT_CSD_MANUAL_BKOPS_MASK 0x01
|
||||
|
||||
/*
|
||||
* MMC_SWITCH access modes
|
||||
*/
|
||||
|
@ -17,6 +17,11 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/mmc/host.h>
|
||||
|
||||
struct sdhci_host_next {
|
||||
unsigned int sg_count;
|
||||
s32 cookie;
|
||||
};
|
||||
|
||||
struct sdhci_host {
|
||||
/* Data set by hardware interface driver */
|
||||
const char *hw_name; /* Hardware bus name */
|
||||
@ -106,6 +111,10 @@ struct sdhci_host {
|
||||
#define SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD (1<<10)
|
||||
/* Capability register bit-63 indicates HS400 support */
|
||||
#define SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 (1<<11)
|
||||
/* forced tuned clock */
|
||||
#define SDHCI_QUIRK2_TUNING_WORK_AROUND (1<<12)
|
||||
/* disable the block count for single block transactions */
|
||||
#define SDHCI_QUIRK2_SUPPORT_SINGLE (1<<13)
|
||||
|
||||
int irq; /* Device IRQ */
|
||||
void __iomem *ioaddr; /* Mapped address */
|
||||
@ -203,6 +212,7 @@ struct sdhci_host {
|
||||
#define SDHCI_TUNING_MODE_1 0
|
||||
struct timer_list tuning_timer; /* Timer for tuning */
|
||||
|
||||
struct sdhci_host_next next_data;
|
||||
unsigned long private[0] ____cacheline_aligned;
|
||||
};
|
||||
#endif /* LINUX_MMC_SDHCI_H */
|
||||
|
@ -3,20 +3,10 @@
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct platform_device;
|
||||
|
||||
#define SH_MOBILE_SDHI_IRQ_CARD_DETECT "card_detect"
|
||||
#define SH_MOBILE_SDHI_IRQ_SDCARD "sdcard"
|
||||
#define SH_MOBILE_SDHI_IRQ_SDIO "sdio"
|
||||
|
||||
/**
|
||||
* struct sh_mobile_sdhi_ops - SDHI driver callbacks
|
||||
* @cd_wakeup: trigger a card-detection run
|
||||
*/
|
||||
struct sh_mobile_sdhi_ops {
|
||||
void (*cd_wakeup)(const struct platform_device *pdev);
|
||||
};
|
||||
|
||||
struct sh_mobile_sdhi_info {
|
||||
int dma_slave_tx;
|
||||
int dma_slave_rx;
|
||||
@ -25,11 +15,6 @@ struct sh_mobile_sdhi_info {
|
||||
unsigned long tmio_caps2;
|
||||
u32 tmio_ocr_mask; /* available MMC voltages */
|
||||
unsigned int cd_gpio;
|
||||
|
||||
/* callbacks for board specific setup code */
|
||||
int (*init)(struct platform_device *pdev,
|
||||
const struct sh_mobile_sdhi_ops *ops);
|
||||
void (*cleanup)(struct platform_device *pdev);
|
||||
};
|
||||
|
||||
#endif /* LINUX_MMC_SH_MOBILE_SDHI_H */
|
||||
|
@ -15,12 +15,10 @@ struct mmc_host;
|
||||
|
||||
int mmc_gpio_get_ro(struct mmc_host *host);
|
||||
int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio);
|
||||
void mmc_gpio_free_ro(struct mmc_host *host);
|
||||
|
||||
int mmc_gpio_get_cd(struct mmc_host *host);
|
||||
int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio,
|
||||
unsigned int debounce);
|
||||
void mmc_gpio_free_cd(struct mmc_host *host);
|
||||
|
||||
int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
|
||||
unsigned int idx, bool override_active_level,
|
||||
@ -28,7 +26,8 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
|
||||
int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
|
||||
unsigned int idx, bool override_active_level,
|
||||
unsigned int debounce, bool *gpio_invert);
|
||||
void mmc_gpiod_free_cd(struct mmc_host *host);
|
||||
void mmc_gpio_set_cd_isr(struct mmc_host *host,
|
||||
irqreturn_t (*isr)(int irq, void *dev_id));
|
||||
void mmc_gpiod_request_cd_irq(struct mmc_host *host);
|
||||
|
||||
#endif
|
||||
|
@ -31,10 +31,6 @@ struct omap_mmc_platform_data {
|
||||
void (*cleanup)(struct device *dev);
|
||||
void (*shutdown)(struct device *dev);
|
||||
|
||||
/* To handle board related suspend/resume functionality for MMC */
|
||||
int (*suspend)(struct device *dev, int slot);
|
||||
int (*resume)(struct device *dev, int slot);
|
||||
|
||||
/* Return context loss count due to PM states changing */
|
||||
int (*get_context_loss_count)(struct device *dev);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user