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
|
@ -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>;
|
||||||
|
}
|
|
@ -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
|
- 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
|
- 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 {
|
sdhci@ab000000 {
|
||||||
compatible = "sdhci";
|
compatible = "sdhci";
|
||||||
|
@ -77,4 +113,28 @@ sdhci@ab000000 {
|
||||||
max-frequency = <50000000>;
|
max-frequency = <50000000>;
|
||||||
keep-power-in-suspend;
|
keep-power-in-suspend;
|
||||||
enable-sdio-wakeup;
|
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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
|
@ -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:
|
- reg:
|
||||||
* for "mrvl,pxav2-mmc" and "mrvl,pxav3-mmc", one register area for
|
* for "mrvl,pxav2-mmc" and "mrvl,pxav3-mmc", one register area for
|
||||||
the SDHCI registers.
|
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
|
* for "marvell,armada-380-sdhci", three register areas. The first
|
||||||
AXI/Mbus bridge registers of the SDHCI unit.
|
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
|
- clocks: Array of clocks required for SDHCI; requires at least one for
|
||||||
I/O clock.
|
I/O clock.
|
||||||
- clock-names: Array of names corresponding to clocks property; shall be
|
- clock-names: Array of names corresponding to clocks property; shall be
|
||||||
|
@ -35,7 +39,10 @@ sdhci@d4280800 {
|
||||||
|
|
||||||
sdhci@d8000 {
|
sdhci@d8000 {
|
||||||
compatible = "marvell,armada-380-sdhci";
|
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>;
|
interrupts = <0 25 0x4>;
|
||||||
clocks = <&gateclk 17>;
|
clocks = <&gateclk 17>;
|
||||||
clock-names = "io";
|
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
|
* We have TI wl1251 attached to MMC3. Pass this information to
|
||||||
* SDIO core because it can't be probed by normal methods.
|
* SDIO core because it can't be probed by normal methods.
|
||||||
*/
|
*/
|
||||||
card->quirks |= MMC_QUIRK_NONSTD_SDIO;
|
if (card->type == MMC_TYPE_SDIO || card->type == MMC_TYPE_SD_COMBO) {
|
||||||
card->cccr.wide_bus = 1;
|
card->quirks |= MMC_QUIRK_NONSTD_SDIO;
|
||||||
card->cis.vendor = 0x104c;
|
card->cccr.wide_bus = 1;
|
||||||
card->cis.device = 0x9066;
|
card->cis.vendor = 0x104c;
|
||||||
card->cis.blksize = 512;
|
card->cis.device = 0x9066;
|
||||||
card->cis.max_dtr = 20000000;
|
card->cis.blksize = 512;
|
||||||
|
card->cis.max_dtr = 20000000;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct omap2_hsmmc_info omap3pandora_mmc[] = {
|
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),
|
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))
|
if (mmc_card_mmc(card))
|
||||||
blk_queue_logical_block_size(md->queue.queue,
|
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)
|
static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
|
||||||
{
|
{
|
||||||
sector_t size;
|
sector_t size;
|
||||||
struct mmc_blk_data *md;
|
|
||||||
|
|
||||||
if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) {
|
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);
|
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);
|
MMC_BLK_DATA_AREA_MAIN);
|
||||||
return md;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mmc_blk_alloc_part(struct mmc_card *card,
|
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;
|
struct mmc_host *host = card->host;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = mmc_hw_reset_check(host);
|
if (!mmc_card_mmc(card) || !mmc_can_reset(card))
|
||||||
if (!err)
|
|
||||||
return RESULT_OK;
|
|
||||||
|
|
||||||
if (err == -ENOSYS)
|
|
||||||
return RESULT_FAIL;
|
|
||||||
|
|
||||||
if (err != -EOPNOTSUPP)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
if (!mmc_can_reset(card))
|
|
||||||
return RESULT_UNSUP_CARD;
|
return RESULT_UNSUP_CARD;
|
||||||
|
|
||||||
return RESULT_UNSUP_HOST;
|
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[] = {
|
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.o sdio_ops.o sdio_bus.o \
|
||||||
sdio_cis.o sdio_io.o sdio_irq.o \
|
sdio_cis.o sdio_io.o sdio_irq.o \
|
||||||
quirks.o slot-gpio.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
|
mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/stat.h>
|
#include <linux/stat.h>
|
||||||
|
#include <linux/of.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
|
|
||||||
#include <linux/mmc/card.h>
|
#include <linux/mmc/card.h>
|
||||||
|
@ -321,6 +322,8 @@ int mmc_add_card(struct mmc_card *card)
|
||||||
#endif
|
#endif
|
||||||
mmc_init_context_info(card->host);
|
mmc_init_context_info(card->host);
|
||||||
|
|
||||||
|
card->dev.of_node = mmc_of_find_child_device(card->host, 0);
|
||||||
|
|
||||||
ret = device_add(&card->dev);
|
ret = device_add(&card->dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -349,6 +352,7 @@ void mmc_remove_card(struct mmc_card *card)
|
||||||
mmc_hostname(card->host), card->rca);
|
mmc_hostname(card->host), card->rca);
|
||||||
}
|
}
|
||||||
device_del(&card->dev);
|
device_del(&card->dev);
|
||||||
|
of_node_put(card->dev.of_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
put_device(&card->dev);
|
put_device(&card->dev);
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
#include "bus.h"
|
#include "bus.h"
|
||||||
#include "host.h"
|
#include "host.h"
|
||||||
#include "sdio_bus.h"
|
#include "sdio_bus.h"
|
||||||
|
#include "pwrseq.h"
|
||||||
|
|
||||||
#include "mmc_ops.h"
|
#include "mmc_ops.h"
|
||||||
#include "sd_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);
|
EXPORT_SYMBOL(mmc_request_done);
|
||||||
|
|
||||||
static void
|
static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
|
||||||
mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
|
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_MMC_DEBUG
|
#ifdef CONFIG_MMC_DEBUG
|
||||||
unsigned int i, sz;
|
unsigned int i, sz;
|
||||||
struct scatterlist *sg;
|
struct scatterlist *sg;
|
||||||
#endif
|
#endif
|
||||||
|
if (mmc_card_removed(host->card))
|
||||||
|
return -ENOMEDIUM;
|
||||||
|
|
||||||
if (mrq->sbc) {
|
if (mrq->sbc) {
|
||||||
pr_debug("<%s: starting CMD%u arg %08x flags %08x>\n",
|
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);
|
mmc_host_clk_hold(host);
|
||||||
led_trigger_event(host->led, LED_FULL);
|
led_trigger_event(host->led, LED_FULL);
|
||||||
host->ops->request(host, mrq);
|
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);
|
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;
|
return;
|
||||||
|
|
||||||
err = mmc_read_bkops_status(card);
|
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)
|
static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq)
|
||||||
{
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
mrq->done = mmc_wait_data_done;
|
mrq->done = mmc_wait_data_done;
|
||||||
mrq->host = host;
|
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)
|
static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
|
||||||
{
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
init_completion(&mrq->completion);
|
init_completion(&mrq->completion);
|
||||||
mrq->done = mmc_wait_done;
|
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);
|
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
|
#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.
|
* Change the bus mode (open drain/push-pull) of a host.
|
||||||
*/
|
*/
|
||||||
|
@ -1232,6 +1265,34 @@ EXPORT_SYMBOL(mmc_of_parse_voltage);
|
||||||
|
|
||||||
#endif /* CONFIG_OF */
|
#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
|
#ifdef CONFIG_REGULATOR
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1555,6 +1616,8 @@ void mmc_power_up(struct mmc_host *host, u32 ocr)
|
||||||
|
|
||||||
mmc_host_clk_hold(host);
|
mmc_host_clk_hold(host);
|
||||||
|
|
||||||
|
mmc_pwrseq_pre_power_on(host);
|
||||||
|
|
||||||
host->ios.vdd = fls(ocr) - 1;
|
host->ios.vdd = fls(ocr) - 1;
|
||||||
host->ios.power_mode = MMC_POWER_UP;
|
host->ios.power_mode = MMC_POWER_UP;
|
||||||
/* Set initial state and call mmc_set_ios */
|
/* 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_delay(10);
|
||||||
|
|
||||||
|
mmc_pwrseq_post_power_on(host);
|
||||||
|
|
||||||
host->ios.clock = host->f_init;
|
host->ios.clock = host->f_init;
|
||||||
|
|
||||||
host->ios.power_mode = MMC_POWER_ON;
|
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_host_clk_hold(host);
|
||||||
|
|
||||||
|
mmc_pwrseq_power_off(host);
|
||||||
|
|
||||||
host->ios.clock = 0;
|
host->ios.clock = 0;
|
||||||
host->ios.vdd = 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);
|
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)
|
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);
|
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)
|
static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
|
||||||
{
|
{
|
||||||
host->f_init = freq;
|
host->f_init = freq;
|
||||||
|
|
|
@ -27,11 +27,15 @@ struct mmc_bus_ops {
|
||||||
int (*power_restore)(struct mmc_host *);
|
int (*power_restore)(struct mmc_host *);
|
||||||
int (*alive)(struct mmc_host *);
|
int (*alive)(struct mmc_host *);
|
||||||
int (*shutdown)(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_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
|
||||||
void mmc_detach_bus(struct mmc_host *host);
|
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_init_erase(struct mmc_card *card);
|
||||||
|
|
||||||
void mmc_set_chip_select(struct mmc_host *host, int mode);
|
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_remove_card_debugfs(struct mmc_card *card);
|
||||||
|
|
||||||
void mmc_init_context_info(struct mmc_host *host);
|
void mmc_init_context_info(struct mmc_host *host);
|
||||||
|
|
||||||
|
int mmc_execute_tuning(struct mmc_card *card);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -29,13 +29,20 @@
|
||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
#include "host.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)
|
#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)
|
static void mmc_host_classdev_release(struct device *dev)
|
||||||
{
|
{
|
||||||
struct mmc_host *host = cls_dev_to_mmc_host(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);
|
kfree(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,9 +61,6 @@ void mmc_unregister_host_class(void)
|
||||||
class_unregister(&mmc_host_class);
|
class_unregister(&mmc_host_class);
|
||||||
}
|
}
|
||||||
|
|
||||||
static DEFINE_IDR(mmc_host_idr);
|
|
||||||
static DEFINE_SPINLOCK(mmc_host_lock);
|
|
||||||
|
|
||||||
#ifdef CONFIG_MMC_CLKGATE
|
#ifdef CONFIG_MMC_CLKGATE
|
||||||
static ssize_t clkgate_delay_show(struct device *dev,
|
static ssize_t clkgate_delay_show(struct device *dev,
|
||||||
struct device_attribute *attr, char *buf)
|
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,
|
ret = mmc_gpiod_request_cd(host, "cd", 0, true,
|
||||||
0, &cd_gpio_invert);
|
0, &cd_gpio_invert);
|
||||||
if (ret) {
|
if (!ret)
|
||||||
if (ret == -EPROBE_DEFER)
|
|
||||||
return ret;
|
|
||||||
if (ret != -ENOENT) {
|
|
||||||
dev_err(host->parent,
|
|
||||||
"Failed to request CD GPIO: %d\n",
|
|
||||||
ret);
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
dev_info(host->parent, "Got CD GPIO\n");
|
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:
|
* 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");
|
ro_cap_invert = of_property_read_bool(np, "wp-inverted");
|
||||||
|
|
||||||
ret = mmc_gpiod_request_ro(host, "wp", 0, false, 0, &ro_gpio_invert);
|
ret = mmc_gpiod_request_ro(host, "wp", 0, false, 0, &ro_gpio_invert);
|
||||||
if (ret) {
|
if (!ret)
|
||||||
if (ret == -EPROBE_DEFER)
|
|
||||||
goto out;
|
|
||||||
if (ret != -ENOENT) {
|
|
||||||
dev_err(host->parent,
|
|
||||||
"Failed to request WP GPIO: %d\n",
|
|
||||||
ret);
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
dev_info(host->parent, "Got WP GPIO\n");
|
dev_info(host->parent, "Got WP GPIO\n");
|
||||||
|
else if (ret != -ENOENT)
|
||||||
|
return ret;
|
||||||
|
|
||||||
/* See the comment on CD inversion above */
|
/* See the comment on CD inversion above */
|
||||||
if (ro_cap_invert ^ ro_gpio_invert)
|
if (ro_cap_invert ^ ro_gpio_invert)
|
||||||
|
@ -457,11 +449,7 @@ int mmc_of_parse(struct mmc_host *host)
|
||||||
host->dsr_req = 0;
|
host->dsr_req = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return mmc_pwrseq_alloc(host);
|
||||||
|
|
||||||
out:
|
|
||||||
mmc_gpio_free_cd(host);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL(mmc_of_parse);
|
EXPORT_SYMBOL(mmc_of_parse);
|
||||||
|
@ -491,8 +479,10 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
|
||||||
host->index = err;
|
host->index = err;
|
||||||
spin_unlock(&mmc_host_lock);
|
spin_unlock(&mmc_host_lock);
|
||||||
idr_preload_end();
|
idr_preload_end();
|
||||||
if (err < 0)
|
if (err < 0) {
|
||||||
goto free;
|
kfree(host);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
dev_set_name(&host->class_dev, "mmc%d", host->index);
|
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;
|
host->class_dev.class = &mmc_host_class;
|
||||||
device_initialize(&host->class_dev);
|
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);
|
mmc_host_clk_init(host);
|
||||||
host->slot.cd_irq = -EINVAL;
|
|
||||||
|
|
||||||
spin_lock_init(&host->lock);
|
spin_lock_init(&host->lock);
|
||||||
init_waitqueue_head(&host->wq);
|
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;
|
host->max_blk_count = PAGE_CACHE_SIZE / 512;
|
||||||
|
|
||||||
return host;
|
return host;
|
||||||
|
|
||||||
free:
|
|
||||||
kfree(host);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL(mmc_alloc_host);
|
EXPORT_SYMBOL(mmc_alloc_host);
|
||||||
|
@ -601,10 +589,7 @@ EXPORT_SYMBOL(mmc_remove_host);
|
||||||
*/
|
*/
|
||||||
void mmc_free_host(struct mmc_host *host)
|
void mmc_free_host(struct mmc_host *host)
|
||||||
{
|
{
|
||||||
spin_lock(&mmc_host_lock);
|
mmc_pwrseq_free(host);
|
||||||
idr_remove(&mmc_host_idr, host->index);
|
|
||||||
spin_unlock(&mmc_host_lock);
|
|
||||||
|
|
||||||
put_device(&host->class_dev);
|
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 */
|
/* check whether the eMMC card supports BKOPS */
|
||||||
if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) {
|
if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) {
|
||||||
card->ext_csd.bkops = 1;
|
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 =
|
card->ext_csd.raw_bkops_status =
|
||||||
ext_csd[EXT_CSD_BKOPS_STATUS];
|
ext_csd[EXT_CSD_BKOPS_STATUS];
|
||||||
if (!card->ext_csd.bkops_en)
|
if (!card->ext_csd.man_bkops_en)
|
||||||
pr_info("%s: BKOPS_EN bit is not set\n",
|
pr_info("%s: MAN_BKOPS_EN bit is not set\n",
|
||||||
mmc_hostname(card->host));
|
mmc_hostname(card->host));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1155,38 +1157,6 @@ bus_speed:
|
||||||
return err;
|
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
|
* Execute tuning sequence to seek the proper bus operating
|
||||||
* conditions for HS200 and HS400, which sends CMD21 to the device.
|
* 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)
|
static int mmc_hs200_tuning(struct mmc_card *card)
|
||||||
{
|
{
|
||||||
struct mmc_host *host = card->host;
|
struct mmc_host *host = card->host;
|
||||||
int err = 0;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Timing should be adjusted to the HS400 target
|
* 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)
|
if (host->ops->prepare_hs400_tuning)
|
||||||
host->ops->prepare_hs400_tuning(host, &host->ios);
|
host->ops->prepare_hs400_tuning(host, &host->ios);
|
||||||
|
|
||||||
if (host->ops->execute_tuning) {
|
return mmc_execute_tuning(card);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1296,6 +1254,12 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||||
memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
|
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.
|
* 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;
|
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 = {
|
static const struct mmc_bus_ops mmc_ops = {
|
||||||
.remove = mmc_remove,
|
.remove = mmc_remove,
|
||||||
.detect = mmc_detect,
|
.detect = mmc_detect,
|
||||||
|
@ -1831,6 +1835,7 @@ static const struct mmc_bus_ops mmc_ops = {
|
||||||
.power_restore = mmc_power_restore,
|
.power_restore = mmc_power_restore,
|
||||||
.alive = mmc_alive,
|
.alive = mmc_alive,
|
||||||
.shutdown = mmc_shutdown,
|
.shutdown = mmc_shutdown,
|
||||||
|
.reset = mmc_reset,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -23,6 +23,36 @@
|
||||||
|
|
||||||
#define MMC_OPS_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */
|
#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,
|
static inline int __mmc_send_status(struct mmc_card *card, u32 *status,
|
||||||
bool ignore_crc)
|
bool ignore_crc)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
* 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.
|
* 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_SDR50_BUS_SPEED ||
|
||||||
card->sd_bus_speed == UHS_SDR104_BUS_SPEED)) {
|
card->sd_bus_speed == UHS_SDR104_BUS_SPEED))
|
||||||
mmc_host_clk_hold(card->host);
|
err = mmc_execute_tuning(card);
|
||||||
err = card->host->ops->execute_tuning(card->host,
|
|
||||||
MMC_SEND_TUNING_BLOCK);
|
|
||||||
mmc_host_clk_release(card->host);
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
out:
|
||||||
kfree(status);
|
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));
|
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.
|
* 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;
|
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 = {
|
static const struct mmc_bus_ops mmc_sd_ops = {
|
||||||
.remove = mmc_sd_remove,
|
.remove = mmc_sd_remove,
|
||||||
.detect = mmc_sd_detect,
|
.detect = mmc_sd_detect,
|
||||||
|
@ -1201,6 +1208,7 @@ static const struct mmc_bus_ops mmc_sd_ops = {
|
||||||
.power_restore = mmc_sd_power_restore,
|
.power_restore = mmc_sd_power_restore,
|
||||||
.alive = mmc_sd_alive,
|
.alive = mmc_sd_alive,
|
||||||
.shutdown = mmc_sd_suspend,
|
.shutdown = mmc_sd_suspend,
|
||||||
|
.reset = mmc_sd_reset,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1271,4 +1279,3 @@ err:
|
||||||
|
|
||||||
return 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
|
* 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.
|
* 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_SDR50) ||
|
||||||
(card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR104))) {
|
(card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR104)))
|
||||||
mmc_host_clk_hold(card->host);
|
err = mmc_execute_tuning(card);
|
||||||
err = card->host->ops->execute_tuning(card->host,
|
|
||||||
MMC_SEND_TUNING_BLOCK);
|
|
||||||
mmc_host_clk_release(card->host);
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,9 @@
|
||||||
#include <linux/mmc/card.h>
|
#include <linux/mmc/card.h>
|
||||||
#include <linux/mmc/host.h>
|
#include <linux/mmc/host.h>
|
||||||
#include <linux/mmc/sdio_func.h>
|
#include <linux/mmc/sdio_func.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
|
||||||
|
#include "core.h"
|
||||||
#include "sdio_cis.h"
|
#include "sdio_cis.h"
|
||||||
#include "sdio_bus.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) {}
|
static inline void sdio_acpi_set_handle(struct sdio_func *func) {}
|
||||||
#endif
|
#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.
|
* 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);
|
dev_set_name(&func->dev, "%s:%d", mmc_card_id(func->card), func->num);
|
||||||
|
|
||||||
|
sdio_set_of_node(func);
|
||||||
sdio_acpi_set_handle(func);
|
sdio_acpi_set_handle(func);
|
||||||
ret = device_add(&func->dev);
|
ret = device_add(&func->dev);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
|
@ -327,6 +337,7 @@ void sdio_remove_func(struct sdio_func *func)
|
||||||
|
|
||||||
dev_pm_domain_detach(&func->dev, false);
|
dev_pm_domain_detach(&func->dev, false);
|
||||||
device_del(&func->dev);
|
device_del(&func->dev);
|
||||||
|
of_node_put(func->dev.of_node);
|
||||||
put_device(&func->dev);
|
put_device(&func->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,11 +18,14 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
#include "slot-gpio.h"
|
||||||
|
|
||||||
struct mmc_gpio {
|
struct mmc_gpio {
|
||||||
struct gpio_desc *ro_gpio;
|
struct gpio_desc *ro_gpio;
|
||||||
struct gpio_desc *cd_gpio;
|
struct gpio_desc *cd_gpio;
|
||||||
bool override_ro_active_level;
|
bool override_ro_active_level;
|
||||||
bool override_cd_active_level;
|
bool override_cd_active_level;
|
||||||
|
irqreturn_t (*cd_gpio_isr)(int irq, void *dev_id);
|
||||||
char *ro_label;
|
char *ro_label;
|
||||||
char cd_label[0];
|
char cd_label[0];
|
||||||
};
|
};
|
||||||
|
@ -38,32 +41,20 @@ static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id)
|
||||||
return IRQ_HANDLED;
|
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;
|
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);
|
if (ctx) {
|
||||||
|
ctx->ro_label = ctx->cd_label + len;
|
||||||
ctx = host->slot.handler_priv;
|
snprintf(ctx->cd_label, len, "%s cd", dev_name(host->parent));
|
||||||
if (!ctx) {
|
snprintf(ctx->ro_label, len, "%s ro", dev_name(host->parent));
|
||||||
/*
|
host->slot.handler_priv = ctx;
|
||||||
* devm_kzalloc() can be called after device_initialize(), even
|
host->slot.cd_irq = -EINVAL;
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_unlock(&host->slot.lock);
|
|
||||||
|
|
||||||
return ctx ? 0 : -ENOMEM;
|
return ctx ? 0 : -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,29 +94,19 @@ EXPORT_SYMBOL(mmc_gpio_get_cd);
|
||||||
* @gpio: gpio number requested
|
* @gpio: gpio number requested
|
||||||
*
|
*
|
||||||
* As devm_* managed functions are used in mmc_gpio_request_ro(), client
|
* 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,
|
* drivers do not need to worry about freeing up memory.
|
||||||
* 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.
|
|
||||||
*
|
*
|
||||||
* Returns zero on success, else an error.
|
* Returns zero on success, else an error.
|
||||||
*/
|
*/
|
||||||
int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio)
|
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;
|
int ret;
|
||||||
|
|
||||||
if (!gpio_is_valid(gpio))
|
if (!gpio_is_valid(gpio))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
ret = mmc_gpio_alloc(host);
|
ret = devm_gpio_request_one(host->parent, gpio, GPIOF_DIR_IN,
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ctx = host->slot.handler_priv;
|
|
||||||
|
|
||||||
ret = devm_gpio_request_one(&host->class_dev, gpio, GPIOF_DIR_IN,
|
|
||||||
ctx->ro_label);
|
ctx->ro_label);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -156,8 +137,10 @@ void mmc_gpiod_request_cd_irq(struct mmc_host *host)
|
||||||
irq = -EINVAL;
|
irq = -EINVAL;
|
||||||
|
|
||||||
if (irq >= 0) {
|
if (irq >= 0) {
|
||||||
ret = devm_request_threaded_irq(&host->class_dev, irq,
|
if (!ctx->cd_gpio_isr)
|
||||||
NULL, mmc_gpio_cd_irqt,
|
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,
|
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||||
ctx->cd_label, host);
|
ctx->cd_label, host);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
@ -171,6 +154,19 @@ void mmc_gpiod_request_cd_irq(struct mmc_host *host)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(mmc_gpiod_request_cd_irq);
|
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
|
* mmc_gpio_request_cd - request a gpio for card-detection
|
||||||
* @host: mmc host
|
* @host: mmc host
|
||||||
|
@ -178,11 +174,7 @@ EXPORT_SYMBOL(mmc_gpiod_request_cd_irq);
|
||||||
* @debounce: debounce time in microseconds
|
* @debounce: debounce time in microseconds
|
||||||
*
|
*
|
||||||
* As devm_* managed functions are used in mmc_gpio_request_cd(), client
|
* 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,
|
* drivers do not need to worry about freeing up memory.
|
||||||
* 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.
|
|
||||||
*
|
*
|
||||||
* If GPIO debouncing is desired, set the debounce parameter to a non-zero
|
* 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
|
* 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,
|
int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio,
|
||||||
unsigned int debounce)
|
unsigned int debounce)
|
||||||
{
|
{
|
||||||
struct mmc_gpio *ctx;
|
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = mmc_gpio_alloc(host);
|
ret = devm_gpio_request_one(host->parent, gpio, GPIOF_DIR_IN,
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ctx = host->slot.handler_priv;
|
|
||||||
|
|
||||||
ret = devm_gpio_request_one(&host->class_dev, gpio, GPIOF_DIR_IN,
|
|
||||||
ctx->cd_label);
|
ctx->cd_label);
|
||||||
if (ret < 0)
|
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);
|
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
|
* mmc_gpiod_request_cd - request a gpio descriptor for card-detection
|
||||||
* @host: mmc host
|
* @host: mmc host
|
||||||
|
@ -285,8 +222,7 @@ EXPORT_SYMBOL(mmc_gpio_free_cd);
|
||||||
* to NULL to ignore
|
* to NULL to ignore
|
||||||
*
|
*
|
||||||
* Use this function in place of mmc_gpio_request_cd() to use the GPIO
|
* 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
|
* descriptor API. Note that it must be called prior to mmc_add_host()
|
||||||
* mmc_gpio_free_cd(). Note also that it must be called prior to mmc_add_host()
|
|
||||||
* otherwise the caller must also call mmc_gpiod_request_cd_irq().
|
* otherwise the caller must also call mmc_gpiod_request_cd_irq().
|
||||||
*
|
*
|
||||||
* Returns zero on success, else an error.
|
* 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 idx, bool override_active_level,
|
||||||
unsigned int debounce, bool *gpio_invert)
|
unsigned int debounce, bool *gpio_invert)
|
||||||
{
|
{
|
||||||
struct mmc_gpio *ctx;
|
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||||
struct gpio_desc *desc;
|
struct gpio_desc *desc;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = mmc_gpio_alloc(host);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ctx = host->slot.handler_priv;
|
|
||||||
|
|
||||||
if (!con_id)
|
if (!con_id)
|
||||||
con_id = ctx->cd_label;
|
con_id = ctx->cd_label;
|
||||||
|
|
||||||
|
@ -339,8 +269,7 @@ EXPORT_SYMBOL(mmc_gpiod_request_cd);
|
||||||
* set to NULL to ignore
|
* set to NULL to ignore
|
||||||
*
|
*
|
||||||
* Use this function in place of mmc_gpio_request_ro() to use the GPIO
|
* 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
|
* descriptor API.
|
||||||
* mmc_gpio_free_ro().
|
|
||||||
*
|
*
|
||||||
* Returns zero on success, else an error.
|
* 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 idx, bool override_active_level,
|
||||||
unsigned int debounce, bool *gpio_invert)
|
unsigned int debounce, bool *gpio_invert)
|
||||||
{
|
{
|
||||||
struct mmc_gpio *ctx;
|
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||||
struct gpio_desc *desc;
|
struct gpio_desc *desc;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = mmc_gpio_alloc(host);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ctx = host->slot.handler_priv;
|
|
||||||
|
|
||||||
if (!con_id)
|
if (!con_id)
|
||||||
con_id = ctx->ro_label;
|
con_id = ctx->ro_label;
|
||||||
|
|
||||||
|
@ -380,28 +303,3 @@ int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(mmc_gpiod_request_ro);
|
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);
|
|
||||||
|
|
|
@ -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
|
config MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
|
||||||
bool
|
bool
|
||||||
|
depends on MMC_SDHCI
|
||||||
select MMC_SDHCI_IO_ACCESSORS
|
select MMC_SDHCI_IO_ACCESSORS
|
||||||
help
|
help
|
||||||
This option is selected by drivers running on big endian hosts
|
This option is selected by drivers running on big endian hosts
|
||||||
|
@ -82,6 +83,7 @@ config MMC_SDHCI_PCI
|
||||||
config MMC_RICOH_MMC
|
config MMC_RICOH_MMC
|
||||||
bool "Ricoh MMC Controller Disabler"
|
bool "Ricoh MMC Controller Disabler"
|
||||||
depends on MMC_SDHCI_PCI
|
depends on MMC_SDHCI_PCI
|
||||||
|
default y
|
||||||
help
|
help
|
||||||
This adds a pci quirk to disable Ricoh MMC Controller. This
|
This adds a pci quirk to disable Ricoh MMC Controller. This
|
||||||
proprietary controller is unnecessary because the SDHCI driver
|
proprietary controller is unnecessary because the SDHCI driver
|
||||||
|
@ -228,6 +230,7 @@ config MMC_SDHCI_PXAV3
|
||||||
tristate "Marvell MMP2 SD Host Controller support (PXAV3)"
|
tristate "Marvell MMP2 SD Host Controller support (PXAV3)"
|
||||||
depends on CLKDEV_LOOKUP
|
depends on CLKDEV_LOOKUP
|
||||||
depends on MMC_SDHCI_PLTFM
|
depends on MMC_SDHCI_PLTFM
|
||||||
|
depends on ARCH_MMP || COMPILE_TEST
|
||||||
default CPU_MMP2
|
default CPU_MMP2
|
||||||
help
|
help
|
||||||
This selects the Marvell(R) PXAV3 SD Host Controller.
|
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)"
|
tristate "Marvell PXA9XX SD Host Controller support (PXAV2)"
|
||||||
depends on CLKDEV_LOOKUP
|
depends on CLKDEV_LOOKUP
|
||||||
depends on MMC_SDHCI_PLTFM
|
depends on MMC_SDHCI_PLTFM
|
||||||
|
depends on ARCH_MMP || COMPILE_TEST
|
||||||
default CPU_PXA910
|
default CPU_PXA910
|
||||||
help
|
help
|
||||||
This selects the Marvell(R) PXAV2 SD Host Controller.
|
This selects the Marvell(R) PXAV2 SD Host Controller.
|
||||||
|
@ -292,6 +296,17 @@ config MMC_SDHCI_BCM2835
|
||||||
|
|
||||||
If unsure, say N.
|
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
|
config MMC_MOXART
|
||||||
tristate "MOXART SD/MMC Host Controller support"
|
tristate "MOXART SD/MMC Host Controller support"
|
||||||
depends on ARCH_MOXART && MMC
|
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_PXAV2) += sdhci-pxav2.o
|
||||||
obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o
|
obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o
|
||||||
obj-$(CONFIG_MMC_SDHCI_SIRF) += sdhci-sirf.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_SDHCI_SPEAR) += sdhci-spear.o
|
||||||
obj-$(CONFIG_MMC_WBSD) += wbsd.o
|
obj-$(CONFIG_MMC_WBSD) += wbsd.o
|
||||||
obj-$(CONFIG_MMC_AU1X) += au1xmmc.o
|
obj-$(CONFIG_MMC_AU1X) += au1xmmc.o
|
||||||
|
|
|
@ -21,43 +21,7 @@
|
||||||
|
|
||||||
#include "dw_mmc.h"
|
#include "dw_mmc.h"
|
||||||
#include "dw_mmc-pltfm.h"
|
#include "dw_mmc-pltfm.h"
|
||||||
|
#include "dw_mmc-exynos.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 */
|
|
||||||
|
|
||||||
/* Variations in Exynos specific dw-mshc controller */
|
/* Variations in Exynos specific dw-mshc controller */
|
||||||
enum dw_mci_exynos_type {
|
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 ||
|
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420_SMU ||
|
||||||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) {
|
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) {
|
||||||
mci_writel(host, MPSBEGIN0, 0);
|
mci_writel(host, MPSBEGIN0, 0);
|
||||||
mci_writel(host, MPSEND0, DWMCI_BLOCK_NUM);
|
mci_writel(host, MPSEND0, SDMMC_ENDING_SEC_NR_MAX);
|
||||||
mci_writel(host, MPSCTRL0, DWMCI_MPSCTRL_SECURE_WRITE_BIT |
|
mci_writel(host, MPSCTRL0, SDMMC_MPSCTRL_SECURE_WRITE_BIT |
|
||||||
DWMCI_MPSCTRL_NON_SECURE_READ_BIT |
|
SDMMC_MPSCTRL_NON_SECURE_READ_BIT |
|
||||||
DWMCI_MPSCTRL_VALID |
|
SDMMC_MPSCTRL_VALID |
|
||||||
DWMCI_MPSCTRL_NON_SECURE_WRITE_BIT);
|
SDMMC_MPSCTRL_NON_SECURE_WRITE_BIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
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)
|
static int dw_mci_exynos_setup_clock(struct dw_mci *host)
|
||||||
{
|
{
|
||||||
struct dw_mci_exynos_priv_data *priv = host->priv;
|
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;
|
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);
|
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;
|
return;
|
||||||
|
|
||||||
/* Guaranteed minimum frequency for cclkin */
|
/* Guaranteed minimum frequency for cclkin */
|
||||||
|
@ -263,10 +230,8 @@ static int dw_mci_exynos_parse_dt(struct dw_mci *host)
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
|
priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
|
||||||
if (!priv) {
|
if (!priv)
|
||||||
dev_err(host->dev, "mem alloc failed for private data\n");
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
|
||||||
|
|
||||||
for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) {
|
for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) {
|
||||||
if (of_device_is_compatible(np, exynos_compat[idx].compatible))
|
if (of_device_is_compatible(np, exynos_compat[idx].compatible))
|
||||||
|
@ -375,64 +340,23 @@ out:
|
||||||
return loc;
|
return loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode,
|
static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot)
|
||||||
struct dw_mci_tuning_data *tuning_data)
|
|
||||||
{
|
{
|
||||||
struct dw_mci *host = slot->host;
|
struct dw_mci *host = slot->host;
|
||||||
struct mmc_host *mmc = slot->mmc;
|
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;
|
u8 start_smpl, smpl, candiates = 0;
|
||||||
s8 found = -1;
|
s8 found = -1;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
blk_test = kmalloc(blksz, GFP_KERNEL);
|
|
||||||
if (!blk_test)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
start_smpl = dw_mci_exynos_get_clksmpl(host);
|
start_smpl = dw_mci_exynos_get_clksmpl(host);
|
||||||
|
|
||||||
do {
|
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);
|
mci_writel(host, TMOUT, ~0);
|
||||||
smpl = dw_mci_exynos_move_next_clksmpl(host);
|
smpl = dw_mci_exynos_move_next_clksmpl(host);
|
||||||
|
|
||||||
mmc_wait_for_req(mmc, &mrq);
|
if (!mmc_send_tuning(mmc))
|
||||||
|
candiates |= (1 << smpl);
|
||||||
|
|
||||||
if (!cmd.error && !data.error) {
|
|
||||||
if (!memcmp(blk_pattern, blk_test, blksz))
|
|
||||||
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);
|
} while (start_smpl != smpl);
|
||||||
|
|
||||||
found = dw_mci_exynos_get_best_clksmpl(candiates);
|
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
|
else
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
|
|
||||||
kfree(blk_test);
|
|
||||||
return ret;
|
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 = {
|
static struct platform_driver dw_mci_exynos_pltfm_driver = {
|
||||||
.probe = dw_mci_exynos_probe,
|
.probe = dw_mci_exynos_probe,
|
||||||
.remove = __exit_p(dw_mci_pltfm_remove),
|
.remove = dw_mci_pltfm_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "dwmmc_exynos",
|
.name = "dwmmc_exynos",
|
||||||
.of_match_table = dw_mci_exynos_match,
|
.of_match_table = dw_mci_exynos_match,
|
||||||
|
|
|
@ -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 = {
|
static struct platform_driver dw_mci_rockchip_pltfm_driver = {
|
||||||
.probe = dw_mci_rockchip_probe,
|
.probe = dw_mci_rockchip_probe,
|
||||||
.remove = __exit_p(dw_mci_pltfm_remove),
|
.remove = dw_mci_pltfm_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "dwmmc_rockchip",
|
.name = "dwmmc_rockchip",
|
||||||
.of_match_table = dw_mci_rockchip_match,
|
.of_match_table = dw_mci_rockchip_match,
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include <linux/stat.h>
|
#include <linux/stat.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
|
#include <linux/mmc/card.h>
|
||||||
#include <linux/mmc/host.h>
|
#include <linux/mmc/host.h>
|
||||||
#include <linux/mmc/mmc.h>
|
#include <linux/mmc/mmc.h>
|
||||||
#include <linux/mmc/sd.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 ||
|
if (cmdr == MMC_READ_SINGLE_BLOCK ||
|
||||||
cmdr == MMC_READ_MULTIPLE_BLOCK ||
|
cmdr == MMC_READ_MULTIPLE_BLOCK ||
|
||||||
cmdr == MMC_WRITE_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->opcode = MMC_STOP_TRANSMISSION;
|
||||||
stop->arg = 0;
|
stop->arg = 0;
|
||||||
stop->flags = MMC_RSP_R1B | MMC_CMD_AC;
|
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)
|
static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
|
||||||
{
|
{
|
||||||
|
unsigned long irqflags;
|
||||||
int sg_len;
|
int sg_len;
|
||||||
u32 temp;
|
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);
|
mci_writel(host, CTRL, temp);
|
||||||
|
|
||||||
/* Disable RX/TX IRQs, let DMA handle it */
|
/* Disable RX/TX IRQs, let DMA handle it */
|
||||||
|
spin_lock_irqsave(&host->irq_lock, irqflags);
|
||||||
temp = mci_readl(host, INTMASK);
|
temp = mci_readl(host, INTMASK);
|
||||||
temp &= ~(SDMMC_INT_RXDR | SDMMC_INT_TXDR);
|
temp &= ~(SDMMC_INT_RXDR | SDMMC_INT_TXDR);
|
||||||
mci_writel(host, INTMASK, temp);
|
mci_writel(host, INTMASK, temp);
|
||||||
|
spin_unlock_irqrestore(&host->irq_lock, irqflags);
|
||||||
|
|
||||||
host->dma_ops->start(host, sg_len);
|
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)
|
static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
|
||||||
{
|
{
|
||||||
|
unsigned long irqflags;
|
||||||
u32 temp;
|
u32 temp;
|
||||||
|
|
||||||
data->error = -EINPROGRESS;
|
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;
|
host->part_buf_count = 0;
|
||||||
|
|
||||||
mci_writel(host, RINTSTS, SDMMC_INT_TXDR | SDMMC_INT_RXDR);
|
mci_writel(host, RINTSTS, SDMMC_INT_TXDR | SDMMC_INT_RXDR);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&host->irq_lock, irqflags);
|
||||||
temp = mci_readl(host, INTMASK);
|
temp = mci_readl(host, INTMASK);
|
||||||
temp |= SDMMC_INT_TXDR | SDMMC_INT_RXDR;
|
temp |= SDMMC_INT_TXDR | SDMMC_INT_RXDR;
|
||||||
mci_writel(host, INTMASK, temp);
|
mci_writel(host, INTMASK, temp);
|
||||||
|
spin_unlock_irqrestore(&host->irq_lock, irqflags);
|
||||||
|
|
||||||
temp = mci_readl(host, CTRL);
|
temp = mci_readl(host, CTRL);
|
||||||
temp &= ~SDMMC_CTRL_DMA_ENABLE;
|
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 */
|
/* enable clock; only low power if no SDIO */
|
||||||
clk_en_a = SDMMC_CLKEN_ENABLE << slot->id;
|
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;
|
clk_en_a |= SDMMC_CLKEN_LOW_PWR << slot->id;
|
||||||
mci_writel(host, CLKENA, clk_en_a);
|
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;
|
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) {
|
if (!IS_ERR(mmc->supply.vqmmc) && !slot->host->vqmmc_enabled) {
|
||||||
ret = regulator_enable(mmc->supply.vqmmc);
|
ret = regulator_enable(mmc->supply.vqmmc);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
@ -1117,10 +1133,6 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||||
else
|
else
|
||||||
slot->host->vqmmc_enabled = true;
|
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;
|
break;
|
||||||
case MMC_POWER_OFF:
|
case MMC_POWER_OFF:
|
||||||
if (!IS_ERR(mmc->supply.vmmc))
|
if (!IS_ERR(mmc->supply.vmmc))
|
||||||
|
@ -1245,27 +1257,37 @@ static int dw_mci_get_cd(struct mmc_host *mmc)
|
||||||
return present;
|
return present;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static void dw_mci_init_card(struct mmc_host *mmc, struct mmc_card *card)
|
||||||
* Disable lower power mode.
|
|
||||||
*
|
|
||||||
* 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_slot *slot = mmc_priv(mmc);
|
||||||
struct dw_mci *host = slot->host;
|
struct dw_mci *host = slot->host;
|
||||||
u32 clk_en_a;
|
|
||||||
const u32 clken_low_pwr = SDMMC_CLKEN_LOW_PWR << slot->id;
|
|
||||||
|
|
||||||
clk_en_a = mci_readl(host, CLKENA);
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
|
||||||
if (clk_en_a & clken_low_pwr) {
|
clk_en_a_old = mci_readl(host, CLKENA);
|
||||||
mci_writel(host, CLKENA, clk_en_a & ~clken_low_pwr);
|
|
||||||
mci_send_cmd(slot, SDMMC_CMD_UPD_CLK |
|
if (card->type == MMC_TYPE_SDIO ||
|
||||||
SDMMC_CMD_PRV_DAT_WAIT, 0);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1273,25 +1295,20 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb)
|
||||||
{
|
{
|
||||||
struct dw_mci_slot *slot = mmc_priv(mmc);
|
struct dw_mci_slot *slot = mmc_priv(mmc);
|
||||||
struct dw_mci *host = slot->host;
|
struct dw_mci *host = slot->host;
|
||||||
|
unsigned long irqflags;
|
||||||
u32 int_mask;
|
u32 int_mask;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&host->irq_lock, irqflags);
|
||||||
|
|
||||||
/* Enable/disable Slot Specific SDIO interrupt */
|
/* Enable/disable Slot Specific SDIO interrupt */
|
||||||
int_mask = mci_readl(host, INTMASK);
|
int_mask = mci_readl(host, INTMASK);
|
||||||
if (enb) {
|
if (enb)
|
||||||
/*
|
int_mask |= SDMMC_INT_SDIO(slot->sdio_id);
|
||||||
* Turn off low power mode if it was enabled. This is a bit of
|
else
|
||||||
* a heavy operation and we disable / enable IRQs a lot, so
|
int_mask &= ~SDMMC_INT_SDIO(slot->sdio_id);
|
||||||
* we'll leave low power mode disabled and it will get
|
mci_writel(host, INTMASK, int_mask);
|
||||||
* re-enabled again in dw_mci_setup_bus().
|
|
||||||
*/
|
|
||||||
dw_mci_disable_low_power(slot);
|
|
||||||
|
|
||||||
mci_writel(host, INTMASK,
|
spin_unlock_irqrestore(&host->irq_lock, irqflags);
|
||||||
(int_mask | SDMMC_INT_SDIO(slot->sdio_id)));
|
|
||||||
} else {
|
|
||||||
mci_writel(host, INTMASK,
|
|
||||||
(int_mask & ~SDMMC_INT_SDIO(slot->sdio_id)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
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_slot *slot = mmc_priv(mmc);
|
||||||
struct dw_mci *host = slot->host;
|
struct dw_mci *host = slot->host;
|
||||||
const struct dw_mci_drv_data *drv_data = host->drv_data;
|
const struct dw_mci_drv_data *drv_data = host->drv_data;
|
||||||
struct dw_mci_tuning_data tuning_data;
|
|
||||||
int err = -ENOSYS;
|
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)
|
if (drv_data && drv_data->execute_tuning)
|
||||||
err = drv_data->execute_tuning(slot, opcode, &tuning_data);
|
err = drv_data->execute_tuning(slot);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1337,7 +1334,7 @@ static const struct mmc_host_ops dw_mci_ops = {
|
||||||
.execute_tuning = dw_mci_execute_tuning,
|
.execute_tuning = dw_mci_execute_tuning,
|
||||||
.card_busy = dw_mci_card_busy,
|
.card_busy = dw_mci_card_busy,
|
||||||
.start_signal_voltage_switch = dw_mci_switch_voltage,
|
.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)
|
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
|
#ifdef CONFIG_MMC_DW_IDMAC
|
||||||
mmc->max_segs = host->ring_size;
|
mmc->max_segs = host->ring_size;
|
||||||
mmc->max_blk_size = 65536;
|
mmc->max_blk_size = 65536;
|
||||||
mmc->max_blk_count = host->ring_size;
|
|
||||||
mmc->max_seg_size = 0x1000;
|
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
|
#else
|
||||||
mmc->max_segs = 64;
|
mmc->max_segs = 64;
|
||||||
mmc->max_blk_size = 65536; /* BLKSIZ is 16 bits */
|
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;
|
u32 clock_frequency;
|
||||||
|
|
||||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||||
if (!pdata) {
|
if (!pdata)
|
||||||
dev_err(dev, "could not allocate memory for pdata\n");
|
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
}
|
|
||||||
|
|
||||||
/* find out number of slots supported */
|
/* find out number of slots supported */
|
||||||
if (of_property_read_u32(dev->of_node, "num-slots",
|
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;
|
host->quirks = host->pdata->quirks;
|
||||||
|
|
||||||
spin_lock_init(&host->lock);
|
spin_lock_init(&host->lock);
|
||||||
|
spin_lock_init(&host->irq_lock);
|
||||||
INIT_LIST_HEAD(&host->queue);
|
INIT_LIST_HEAD(&host->queue);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -244,15 +244,11 @@ struct dw_mci_slot {
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
#define DW_MMC_CARD_PRESENT 0
|
#define DW_MMC_CARD_PRESENT 0
|
||||||
#define DW_MMC_CARD_NEED_INIT 1
|
#define DW_MMC_CARD_NEED_INIT 1
|
||||||
|
#define DW_MMC_CARD_NO_LOW_PWR 2
|
||||||
int id;
|
int id;
|
||||||
int sdio_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.
|
* dw_mci driver data - dw-mshc implementation specific driver data.
|
||||||
* @caps: mmc subsystem specified capabilities of the controller(s).
|
* @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 (*prepare_command)(struct dw_mci *host, u32 *cmdr);
|
||||||
void (*set_ios)(struct dw_mci *host, struct mmc_ios *ios);
|
void (*set_ios)(struct dw_mci *host, struct mmc_ios *ios);
|
||||||
int (*parse_dt)(struct dw_mci *host);
|
int (*parse_dt)(struct dw_mci *host);
|
||||||
int (*execute_tuning)(struct dw_mci_slot *slot, u32 opcode,
|
int (*execute_tuning)(struct dw_mci_slot *slot);
|
||||||
struct dw_mci_tuning_data *tuning_data);
|
|
||||||
};
|
};
|
||||||
#endif /* _DW_MMC_H_ */
|
#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)
|
static void mmci_dma_setup(struct mmci_host *host)
|
||||||
{
|
{
|
||||||
const char *rxname, *txname;
|
const char *rxname, *txname;
|
||||||
dma_cap_mask_t mask;
|
|
||||||
struct variant_data *variant = host->variant;
|
struct variant_data *variant = host->variant;
|
||||||
|
|
||||||
host->dma_rx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "rx");
|
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 */
|
/* initialize pre request cookie */
|
||||||
host->next_data.cookie = 1;
|
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
|
* If only an RX channel is specified, the driver will
|
||||||
* attempt to use it bidirectionally, however if it is
|
* 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_set_autosuspend_delay(&dev->dev, 50);
|
||||||
pm_runtime_use_autosuspend(&dev->dev);
|
pm_runtime_use_autosuspend(&dev->dev);
|
||||||
pm_runtime_put(&dev->dev);
|
|
||||||
|
|
||||||
mmc_add_host(mmc);
|
mmc_add_host(mmc);
|
||||||
|
|
||||||
|
pm_runtime_put(&dev->dev);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
clk_disable:
|
clk_disable:
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
#include <linux/errno.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/blkdev.h>
|
#include <linux/blkdev.h>
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
|
@ -562,7 +563,6 @@ static int moxart_probe(struct platform_device *pdev)
|
||||||
struct dma_slave_config cfg;
|
struct dma_slave_config cfg;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
void __iomem *reg_mmc;
|
void __iomem *reg_mmc;
|
||||||
dma_cap_mask_t mask;
|
|
||||||
int irq, ret;
|
int irq, ret;
|
||||||
u32 i;
|
u32 i;
|
||||||
|
|
||||||
|
@ -586,9 +586,8 @@ static int moxart_probe(struct platform_device *pdev)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
clk = of_clk_get(node, 0);
|
clk = devm_clk_get(dev, NULL);
|
||||||
if (IS_ERR(clk)) {
|
if (IS_ERR(clk)) {
|
||||||
dev_err(dev, "of_clk_get failed\n");
|
|
||||||
ret = PTR_ERR(clk);
|
ret = PTR_ERR(clk);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
@ -599,10 +598,9 @@ static int moxart_probe(struct platform_device *pdev)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
mmc_of_parse(mmc);
|
ret = mmc_of_parse(mmc);
|
||||||
|
if (ret)
|
||||||
dma_cap_zero(mask);
|
goto out;
|
||||||
dma_cap_set(DMA_SLAVE, mask);
|
|
||||||
|
|
||||||
host = mmc_priv(mmc);
|
host = mmc_priv(mmc);
|
||||||
host->mmc = mmc;
|
host->mmc = mmc;
|
||||||
|
@ -611,8 +609,8 @@ static int moxart_probe(struct platform_device *pdev)
|
||||||
host->timeout = msecs_to_jiffies(1000);
|
host->timeout = msecs_to_jiffies(1000);
|
||||||
host->sysclk = clk_get_rate(clk);
|
host->sysclk = clk_get_rate(clk);
|
||||||
host->fifo_width = readl(host->base + REG_FEATURE) << 2;
|
host->fifo_width = readl(host->base + REG_FEATURE) << 2;
|
||||||
host->dma_chan_tx = of_dma_request_slave_channel(node, "tx");
|
host->dma_chan_tx = dma_request_slave_channel_reason(dev, "tx");
|
||||||
host->dma_chan_rx = of_dma_request_slave_channel(node, "rx");
|
host->dma_chan_rx = dma_request_slave_channel_reason(dev, "rx");
|
||||||
|
|
||||||
spin_lock_init(&host->lock);
|
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. */
|
mmc->ocr_avail = 0xffff00; /* Support 2.0v - 3.6v power. */
|
||||||
|
|
||||||
if (IS_ERR(host->dma_chan_tx) || IS_ERR(host->dma_chan_rx)) {
|
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");
|
dev_dbg(dev, "PIO mode transfer enabled\n");
|
||||||
host->have_dma = false;
|
host->have_dma = false;
|
||||||
} else {
|
} else {
|
||||||
|
@ -700,9 +703,6 @@ static int moxart_remove(struct platform_device *pdev)
|
||||||
writel(readl(host->base + REG_CLOCK_CONTROL) | CLK_OFF,
|
writel(readl(host->base + REG_CLOCK_CONTROL) | CLK_OFF,
|
||||||
host->base + REG_CLOCK_CONTROL);
|
host->base + REG_CLOCK_CONTROL);
|
||||||
}
|
}
|
||||||
|
|
||||||
kfree(host);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,6 @@
|
||||||
#include <linux/of_irq.h>
|
#include <linux/of_irq.h>
|
||||||
#include <linux/mmc/host.h>
|
#include <linux/mmc/host.h>
|
||||||
#include <linux/mmc/slot-gpio.h>
|
#include <linux/mmc/slot-gpio.h>
|
||||||
#include <linux/pinctrl/consumer.h>
|
|
||||||
|
|
||||||
#include <asm/sizes.h>
|
#include <asm/sizes.h>
|
||||||
#include <asm/unaligned.h>
|
#include <asm/unaligned.h>
|
||||||
|
@ -704,7 +703,6 @@ static int mvsd_probe(struct platform_device *pdev)
|
||||||
const struct mbus_dram_target_info *dram;
|
const struct mbus_dram_target_info *dram;
|
||||||
struct resource *r;
|
struct resource *r;
|
||||||
int ret, irq;
|
int ret, irq;
|
||||||
struct pinctrl *pinctrl;
|
|
||||||
|
|
||||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
irq = platform_get_irq(pdev, 0);
|
irq = platform_get_irq(pdev, 0);
|
||||||
|
@ -721,10 +719,6 @@ static int mvsd_probe(struct platform_device *pdev)
|
||||||
host->mmc = mmc;
|
host->mmc = mmc;
|
||||||
host->dev = &pdev->dev;
|
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
|
* Some non-DT platforms do not pass a clock, and the clock
|
||||||
* frequency is passed through platform_data. On DT platforms,
|
* frequency is passed through platform_data. On DT platforms,
|
||||||
|
@ -828,8 +822,6 @@ static int mvsd_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
out:
|
out:
|
||||||
if (mmc) {
|
if (mmc) {
|
||||||
mmc_gpio_free_cd(mmc);
|
|
||||||
mmc_gpio_free_ro(mmc);
|
|
||||||
if (!IS_ERR(host->clk))
|
if (!IS_ERR(host->clk))
|
||||||
clk_disable_unprepare(host->clk);
|
clk_disable_unprepare(host->clk);
|
||||||
mmc_free_host(mmc);
|
mmc_free_host(mmc);
|
||||||
|
@ -844,8 +836,6 @@ static int mvsd_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
struct mvsd_host *host = mmc_priv(mmc);
|
struct mvsd_host *host = mmc_priv(mmc);
|
||||||
|
|
||||||
mmc_gpio_free_cd(mmc);
|
|
||||||
mmc_gpio_free_ro(mmc);
|
|
||||||
mmc_remove_host(mmc);
|
mmc_remove_host(mmc);
|
||||||
del_timer_sync(&host->timer);
|
del_timer_sync(&host->timer);
|
||||||
mvsd_power_down(host);
|
mvsd_power_down(host);
|
||||||
|
|
|
@ -677,8 +677,7 @@ static int mxs_mmc_probe(struct platform_device *pdev)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_free_dma:
|
out_free_dma:
|
||||||
if (ssp->dmach)
|
dma_release_channel(ssp->dmach);
|
||||||
dma_release_channel(ssp->dmach);
|
|
||||||
out_clk_disable:
|
out_clk_disable:
|
||||||
clk_disable_unprepare(ssp->clk);
|
clk_disable_unprepare(ssp->clk);
|
||||||
out_mmc_free:
|
out_mmc_free:
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
#include <linux/mmc/host.h>
|
#include <linux/mmc/host.h>
|
||||||
#include <linux/mmc/core.h>
|
#include <linux/mmc/core.h>
|
||||||
#include <linux/mmc/mmc.h>
|
#include <linux/mmc/mmc.h>
|
||||||
|
#include <linux/mmc/slot-gpio.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
#include <linux/gpio.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)
|
static int omap_hsmmc_card_detect(struct device *dev)
|
||||||
{
|
{
|
||||||
struct omap_hsmmc_host *host = dev_get_drvdata(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 mmc_gpio_get_cd(host->mmc);
|
||||||
return !gpio_get_value_cansleep(mmc->switch_pin);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int omap_hsmmc_get_wp(struct device *dev)
|
static int omap_hsmmc_get_wp(struct device *dev)
|
||||||
{
|
{
|
||||||
struct omap_hsmmc_host *host = dev_get_drvdata(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 mmc_gpio_get_ro(host->mmc);
|
||||||
return gpio_get_value_cansleep(mmc->gpio_wp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int omap_hsmmc_get_cover_state(struct device *dev)
|
static int omap_hsmmc_get_cover_state(struct device *dev)
|
||||||
{
|
{
|
||||||
struct omap_hsmmc_host *host = dev_get_drvdata(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 mmc_gpio_get_cd(host->mmc);
|
||||||
return !gpio_get_value_cansleep(mmc->switch_pin);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#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
|
#ifdef CONFIG_REGULATOR
|
||||||
|
|
||||||
static int omap_hsmmc_set_power(struct device *dev, int power_on, int vdd)
|
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
|
#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)
|
struct omap_hsmmc_platform_data *pdata)
|
||||||
{
|
{
|
||||||
int ret;
|
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 = omap_hsmmc_card_detect;
|
||||||
host->card_detect_irq =
|
host->card_detect_irq =
|
||||||
gpio_to_irq(pdata->switch_pin);
|
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)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
ret = gpio_direction_input(pdata->switch_pin);
|
|
||||||
if (ret)
|
|
||||||
goto err_free_sp;
|
|
||||||
} else {
|
} else {
|
||||||
pdata->switch_pin = -EINVAL;
|
pdata->switch_pin = -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gpio_is_valid(pdata->gpio_wp)) {
|
if (gpio_is_valid(pdata->gpio_wp)) {
|
||||||
host->get_ro = omap_hsmmc_get_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)
|
if (ret)
|
||||||
goto err_free_cd;
|
return ret;
|
||||||
ret = gpio_direction_input(pdata->gpio_wp);
|
|
||||||
if (ret)
|
|
||||||
goto err_free_wp;
|
|
||||||
} else {
|
} else {
|
||||||
pdata->gpio_wp = -EINVAL;
|
pdata->gpio_wp = -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
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 omap_hsmmc_platform_data *pdata;
|
||||||
struct device_node *np = dev->of_node;
|
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);
|
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||||
if (!pdata)
|
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))
|
if (of_find_property(np, "ti,dual-volt", NULL))
|
||||||
pdata->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT;
|
pdata->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT;
|
||||||
|
|
||||||
pdata->switch_pin = cd_gpio;
|
pdata->switch_pin = -EINVAL;
|
||||||
pdata->gpio_wp = wp_gpio;
|
pdata->gpio_wp = -EINVAL;
|
||||||
|
|
||||||
if (of_find_property(np, "ti,non-removable", NULL)) {
|
if (of_find_property(np, "ti,non-removable", NULL)) {
|
||||||
pdata->nonremovable = true;
|
pdata->nonremovable = true;
|
||||||
pdata->no_regulator_off_init = 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))
|
if (of_find_property(np, "ti,needs-special-reset", NULL))
|
||||||
pdata->features |= HSMMC_HAS_UPDATED_RESET;
|
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))
|
if (of_find_property(np, "ti,needs-special-hs-handling", NULL))
|
||||||
pdata->features |= HSMMC_HAS_HSPE_SUPPORT;
|
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;
|
return pdata;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
@ -2078,6 +2008,10 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = mmc_of_parse(mmc);
|
||||||
|
if (ret)
|
||||||
|
goto err1;
|
||||||
|
|
||||||
host = mmc_priv(mmc);
|
host = mmc_priv(mmc);
|
||||||
host->mmc = mmc;
|
host->mmc = mmc;
|
||||||
host->pdata = pdata;
|
host->pdata = pdata;
|
||||||
|
@ -2091,7 +2025,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
||||||
host->next_data.cookie = 1;
|
host->next_data.cookie = 1;
|
||||||
host->pbias_enabled = 0;
|
host->pbias_enabled = 0;
|
||||||
|
|
||||||
ret = omap_hsmmc_gpio_init(host, pdata);
|
ret = omap_hsmmc_gpio_init(mmc, host, pdata);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_gpio;
|
goto err_gpio;
|
||||||
|
|
||||||
|
@ -2106,7 +2040,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
if (pdata->max_freq > 0)
|
if (pdata->max_freq > 0)
|
||||||
mmc->f_max = pdata->max_freq;
|
mmc->f_max = pdata->max_freq;
|
||||||
else
|
else if (mmc->f_max == 0)
|
||||||
mmc->f_max = OMAP_MMC_MAX_CLOCK;
|
mmc->f_max = OMAP_MMC_MAX_CLOCK;
|
||||||
|
|
||||||
spin_lock_init(&host->irq_lock);
|
spin_lock_init(&host->irq_lock);
|
||||||
|
@ -2160,7 +2094,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
||||||
if (mmc_pdata(host)->nonremovable)
|
if (mmc_pdata(host)->nonremovable)
|
||||||
mmc->caps |= MMC_CAP_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);
|
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;
|
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);
|
omap_hsmmc_disable_irq(host);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2276,7 +2194,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
err_slot_name:
|
err_slot_name:
|
||||||
mmc_remove_host(mmc);
|
mmc_remove_host(mmc);
|
||||||
err_irq_cd:
|
|
||||||
if (host->use_reg)
|
if (host->use_reg)
|
||||||
omap_hsmmc_reg_put(host);
|
omap_hsmmc_reg_put(host);
|
||||||
err_irq:
|
err_irq:
|
||||||
|
@ -2289,7 +2206,6 @@ err_irq:
|
||||||
if (host->dbclk)
|
if (host->dbclk)
|
||||||
clk_disable_unprepare(host->dbclk);
|
clk_disable_unprepare(host->dbclk);
|
||||||
err1:
|
err1:
|
||||||
omap_hsmmc_gpio_free(host, pdata);
|
|
||||||
err_gpio:
|
err_gpio:
|
||||||
mmc_free_host(mmc);
|
mmc_free_host(mmc);
|
||||||
err:
|
err:
|
||||||
|
@ -2315,32 +2231,12 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
|
||||||
if (host->dbclk)
|
if (host->dbclk)
|
||||||
clk_disable_unprepare(host->dbclk);
|
clk_disable_unprepare(host->dbclk);
|
||||||
|
|
||||||
omap_hsmmc_gpio_free(host, host->pdata);
|
|
||||||
mmc_free_host(host->mmc);
|
mmc_free_host(host->mmc);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#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)
|
static int omap_hsmmc_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
|
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
|
||||||
|
@ -2398,8 +2294,6 @@ static int omap_hsmmc_resume(struct device *dev)
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#define omap_hsmmc_prepare NULL
|
|
||||||
#define omap_hsmmc_complete NULL
|
|
||||||
#define omap_hsmmc_suspend NULL
|
#define omap_hsmmc_suspend NULL
|
||||||
#define omap_hsmmc_resume NULL
|
#define omap_hsmmc_resume NULL
|
||||||
#endif
|
#endif
|
||||||
|
@ -2484,8 +2378,6 @@ static int omap_hsmmc_runtime_resume(struct device *dev)
|
||||||
static struct dev_pm_ops omap_hsmmc_dev_pm_ops = {
|
static struct dev_pm_ops omap_hsmmc_dev_pm_ops = {
|
||||||
.suspend = omap_hsmmc_suspend,
|
.suspend = omap_hsmmc_suspend,
|
||||||
.resume = omap_hsmmc_resume,
|
.resume = omap_hsmmc_resume,
|
||||||
.prepare = omap_hsmmc_prepare,
|
|
||||||
.complete = omap_hsmmc_complete,
|
|
||||||
.runtime_suspend = omap_hsmmc_runtime_suspend,
|
.runtime_suspend = omap_hsmmc_runtime_suspend,
|
||||||
.runtime_resume = omap_hsmmc_runtime_resume,
|
.runtime_resume = omap_hsmmc_runtime_resume,
|
||||||
};
|
};
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include <linux/mmc/host.h>
|
#include <linux/mmc/host.h>
|
||||||
#include <linux/mmc/mmc.h>
|
#include <linux/mmc/mmc.h>
|
||||||
#include <linux/mmc/sd.h>
|
#include <linux/mmc/sd.h>
|
||||||
|
#include <linux/mmc/sdio.h>
|
||||||
#include <linux/mmc/card.h>
|
#include <linux/mmc/card.h>
|
||||||
#include <linux/mfd/rtsx_pci.h>
|
#include <linux/mfd/rtsx_pci.h>
|
||||||
#include <asm/unaligned.h>
|
#include <asm/unaligned.h>
|
||||||
|
@ -53,9 +54,9 @@ struct realtek_pci_sdmmc {
|
||||||
#define SDMMC_POWER_ON 1
|
#define SDMMC_POWER_ON 1
|
||||||
#define SDMMC_POWER_OFF 0
|
#define SDMMC_POWER_OFF 0
|
||||||
|
|
||||||
unsigned int sg_count;
|
int sg_count;
|
||||||
s32 cookie;
|
s32 cookie;
|
||||||
unsigned int cookie_sg_count;
|
int cookie_sg_count;
|
||||||
bool using_cookie;
|
bool using_cookie;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -71,30 +72,83 @@ static inline void sd_clear_error(struct realtek_pci_sdmmc *host)
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG
|
#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)
|
static void sd_print_debug_regs(struct realtek_pci_sdmmc *host)
|
||||||
{
|
{
|
||||||
struct rtsx_pcr *pcr = host->pcr;
|
dump_reg_range(host, 0xFDA0, 0xFDB3);
|
||||||
u16 i;
|
dump_reg_range(host, 0xFD52, 0xFD69);
|
||||||
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++));
|
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
#define sd_print_debug_regs(host)
|
#define sd_print_debug_regs(host)
|
||||||
#endif /* DEBUG */
|
#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
|
* 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;
|
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,
|
static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
|
||||||
struct mmc_command *cmd)
|
struct mmc_command *cmd)
|
||||||
{
|
{
|
||||||
|
@ -293,47 +230,18 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
|
||||||
int timeout = 100;
|
int timeout = 100;
|
||||||
int i;
|
int i;
|
||||||
u8 *ptr;
|
u8 *ptr;
|
||||||
int stat_idx = 0;
|
int rsp_type;
|
||||||
u8 rsp_type;
|
int stat_idx;
|
||||||
int rsp_len = 5;
|
|
||||||
bool clock_toggled = false;
|
bool clock_toggled = false;
|
||||||
|
|
||||||
dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n",
|
dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n",
|
||||||
__func__, cmd_idx, arg);
|
__func__, cmd_idx, arg);
|
||||||
|
|
||||||
/* Response type:
|
rsp_type = sd_response_type(cmd);
|
||||||
* R0
|
if (rsp_type < 0)
|
||||||
* 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;
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
|
stat_idx = sd_status_index(rsp_type);
|
||||||
|
|
||||||
if (rsp_type == SD_RSP_TYPE_R1b)
|
if (rsp_type == SD_RSP_TYPE_R1b)
|
||||||
timeout = 3000;
|
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_init_cmd(pcr);
|
||||||
|
sd_cmd_set_sd_cmd(pcr, cmd);
|
||||||
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);
|
|
||||||
|
|
||||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, rsp_type);
|
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, rsp_type);
|
||||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE,
|
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE,
|
||||||
0x01, PINGPONG_BUFFER);
|
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 */
|
/* Read data from ping-pong buffer */
|
||||||
for (i = PPBUF_BASE2; i < PPBUF_BASE2 + 16; i++)
|
for (i = PPBUF_BASE2; i < PPBUF_BASE2 + 16; i++)
|
||||||
rtsx_pci_add_cmd(pcr, READ_REG_CMD, (u16)i, 0, 0);
|
rtsx_pci_add_cmd(pcr, READ_REG_CMD, (u16)i, 0, 0);
|
||||||
stat_idx = 16;
|
|
||||||
} else if (rsp_type != SD_RSP_TYPE_R0) {
|
} else if (rsp_type != SD_RSP_TYPE_R0) {
|
||||||
/* Read data from SD_CMDx registers */
|
/* Read data from SD_CMDx registers */
|
||||||
for (i = SD_CMD0; i <= SD_CMD4; i++)
|
for (i = SD_CMD0; i <= SD_CMD4; i++)
|
||||||
rtsx_pci_add_cmd(pcr, READ_REG_CMD, (u16)i, 0, 0);
|
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);
|
rtsx_pci_add_cmd(pcr, READ_REG_CMD, SD_STAT1, 0, 0);
|
||||||
|
@ -438,71 +338,213 @@ out:
|
||||||
SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0);
|
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 rtsx_pcr *pcr = host->pcr;
|
||||||
struct mmc_host *mmc = host->mmc;
|
struct mmc_host *mmc = host->mmc;
|
||||||
struct mmc_card *card = mmc->card;
|
struct mmc_card *card = mmc->card;
|
||||||
|
struct mmc_command *cmd = mrq->cmd;
|
||||||
struct mmc_data *data = mrq->data;
|
struct mmc_data *data = mrq->data;
|
||||||
int uhs = mmc_card_uhs(card);
|
int uhs = mmc_card_uhs(card);
|
||||||
int read = (data->flags & MMC_DATA_READ) ? 1 : 0;
|
u8 cfg2 = 0;
|
||||||
u8 cfg2, trans_mode;
|
|
||||||
int err;
|
int err;
|
||||||
|
int resp_type;
|
||||||
size_t data_len = data->blksz * data->blocks;
|
size_t data_len = data->blksz * data->blocks;
|
||||||
|
|
||||||
if (read) {
|
dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n",
|
||||||
cfg2 = SD_CALCULATE_CRC7 | SD_CHECK_CRC16 |
|
__func__, cmd->opcode, cmd->arg);
|
||||||
SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_0;
|
|
||||||
trans_mode = SD_TM_AUTO_READ_3;
|
resp_type = sd_response_type(cmd);
|
||||||
} else {
|
if (resp_type < 0)
|
||||||
cfg2 = SD_NO_CALCULATE_CRC7 | SD_CHECK_CRC16 |
|
return resp_type;
|
||||||
SD_NO_WAIT_BUSY_END | SD_NO_CHECK_CRC7 | SD_RSP_LEN_0;
|
|
||||||
trans_mode = SD_TM_AUTO_WRITE_3;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!uhs)
|
if (!uhs)
|
||||||
cfg2 |= SD_NO_CHECK_WAIT_CRC_TO;
|
cfg2 |= SD_NO_CHECK_WAIT_CRC_TO;
|
||||||
|
|
||||||
rtsx_pci_init_cmd(pcr);
|
rtsx_pci_init_cmd(pcr);
|
||||||
|
sd_cmd_set_sd_cmd(pcr, cmd);
|
||||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, 0x00);
|
sd_cmd_set_data_len(pcr, data->blocks, data->blksz);
|
||||||
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));
|
|
||||||
|
|
||||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, IRQSTAT0,
|
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, IRQSTAT0,
|
||||||
DMA_DONE_INT, DMA_DONE_INT);
|
DMA_DONE_INT, DMA_DONE_INT);
|
||||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC3,
|
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC3,
|
||||||
0xFF, (u8)(data_len >> 24));
|
0xFF, (u8)(data_len >> 24));
|
||||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC2,
|
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC2,
|
||||||
0xFF, (u8)(data_len >> 16));
|
0xFF, (u8)(data_len >> 16));
|
||||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC1,
|
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC1,
|
||||||
0xFF, (u8)(data_len >> 8));
|
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, DMATC0, 0xFF, (u8)data_len);
|
||||||
if (read) {
|
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMACTL,
|
||||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMACTL,
|
0x03 | DMA_PACK_SIZE_MASK,
|
||||||
0x03 | DMA_PACK_SIZE_MASK,
|
DMA_DIR_FROM_CARD | DMA_EN | DMA_512);
|
||||||
DMA_DIR_FROM_CARD | DMA_EN | DMA_512);
|
|
||||||
} else {
|
|
||||||
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,
|
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE,
|
||||||
0x01, RING_BUFFER);
|
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_CFG2, 0xFF, cfg2);
|
|
||||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, 0xFF,
|
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, 0xFF,
|
||||||
trans_mode | SD_TRANSFER_START);
|
SD_TRANSFER_START | SD_TM_AUTO_READ_2);
|
||||||
rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER,
|
rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER,
|
||||||
SD_TRANSFER_END, SD_TRANSFER_END);
|
SD_TRANSFER_END, SD_TRANSFER_END);
|
||||||
|
|
||||||
rtsx_pci_send_cmd_no_wait(pcr);
|
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, 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,
|
||||||
|
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, 0, 10000);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
sd_clear_error(host);
|
sd_clear_error(host);
|
||||||
return err;
|
return err;
|
||||||
|
@ -511,6 +553,23 @@ static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq)
|
||||||
return 0;
|
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)
|
static inline void sd_enable_initial_mode(struct realtek_pci_sdmmc *host)
|
||||||
{
|
{
|
||||||
rtsx_pci_write_register(host->pcr, SD_CFG1,
|
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_command *cmd = mrq->cmd;
|
||||||
struct mmc_data *data = mrq->data;
|
struct mmc_data *data = mrq->data;
|
||||||
u8 _cmd[5], *buf;
|
u8 *buf;
|
||||||
|
|
||||||
_cmd[0] = 0x40 | (u8)cmd->opcode;
|
|
||||||
put_unaligned_be32(cmd->arg, (u32 *)(&_cmd[1]));
|
|
||||||
|
|
||||||
buf = kzalloc(data->blksz, GFP_NOIO);
|
buf = kzalloc(data->blksz, GFP_NOIO);
|
||||||
if (!buf) {
|
if (!buf) {
|
||||||
|
@ -543,7 +599,7 @@ static void sd_normal_rw(struct realtek_pci_sdmmc *host,
|
||||||
if (host->initial_mode)
|
if (host->initial_mode)
|
||||||
sd_disable_initial_mode(host);
|
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);
|
data->blksz, 200);
|
||||||
|
|
||||||
if (host->initial_mode)
|
if (host->initial_mode)
|
||||||
|
@ -553,7 +609,7 @@ static void sd_normal_rw(struct realtek_pci_sdmmc *host,
|
||||||
} else {
|
} else {
|
||||||
sg_copy_to_buffer(data->sg, data->sg_len, buf, data->blksz);
|
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);
|
data->blksz, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -653,14 +709,14 @@ static int sd_tuning_rx_cmd(struct realtek_pci_sdmmc *host,
|
||||||
u8 opcode, u8 sample_point)
|
u8 opcode, u8 sample_point)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
u8 cmd[5] = {0};
|
struct mmc_command cmd = {0};
|
||||||
|
|
||||||
err = sd_change_phase(host, sample_point, true);
|
err = sd_change_phase(host, sample_point, true);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
cmd[0] = 0x40 | opcode;
|
cmd.opcode = opcode;
|
||||||
err = sd_read_data(host, cmd, 0x40, NULL, 0, 100);
|
err = sd_read_data(host, &cmd, 0x40, NULL, 0, 100);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
/* Wait till SD DATA IDLE */
|
/* Wait till SD DATA IDLE */
|
||||||
sd_wait_data_idle(host);
|
sd_wait_data_idle(host);
|
||||||
|
@ -727,6 +783,12 @@ static int sd_tuning_rx(struct realtek_pci_sdmmc *host, u8 opcode)
|
||||||
return 0;
|
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)
|
static inline int sd_rw_cmd(struct mmc_command *cmd)
|
||||||
{
|
{
|
||||||
return mmc_op_multi(cmd->opcode) ||
|
return mmc_op_multi(cmd->opcode) ||
|
||||||
|
@ -748,7 +810,7 @@ static void sd_request(struct work_struct *work)
|
||||||
unsigned int data_size = 0;
|
unsigned int data_size = 0;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (host->eject) {
|
if (host->eject || !sd_get_cd_int(host)) {
|
||||||
cmd->error = -ENOMEDIUM;
|
cmd->error = -ENOMEDIUM;
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
@ -776,17 +838,15 @@ static void sd_request(struct work_struct *work)
|
||||||
if (mrq->data)
|
if (mrq->data)
|
||||||
data_size = data->blocks * data->blksz;
|
data_size = data->blocks * data->blksz;
|
||||||
|
|
||||||
if (!data_size || sd_rw_cmd(cmd)) {
|
if (!data_size) {
|
||||||
sd_send_cmd_get_rsp(host, cmd);
|
sd_send_cmd_get_rsp(host, cmd);
|
||||||
|
} 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 (!cmd->error && data_size) {
|
if (mmc_op_multi(cmd->opcode) && mrq->stop)
|
||||||
sd_rw_multi(host, mrq);
|
sd_send_cmd_get_rsp(host, mrq->stop);
|
||||||
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 {
|
} else {
|
||||||
sd_normal_rw(host, mrq);
|
sd_normal_rw(host, mrq);
|
||||||
}
|
}
|
||||||
|
@ -801,8 +861,10 @@ static void sd_request(struct work_struct *work)
|
||||||
mutex_unlock(&pcr->pcr_mutex);
|
mutex_unlock(&pcr->pcr_mutex);
|
||||||
|
|
||||||
finish:
|
finish:
|
||||||
if (cmd->error)
|
if (cmd->error) {
|
||||||
dev_dbg(sdmmc_dev(host), "cmd->error = %d\n", 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);
|
mutex_lock(&host->host_mutex);
|
||||||
host->mrq = NULL;
|
host->mrq = NULL;
|
||||||
|
@ -820,7 +882,7 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||||
host->mrq = mrq;
|
host->mrq = mrq;
|
||||||
mutex_unlock(&host->host_mutex);
|
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);
|
host->using_cookie = sd_pre_dma_transfer(host, data, false);
|
||||||
|
|
||||||
queue_work(host->workq, &host->work);
|
queue_work(host->workq, &host->work);
|
||||||
|
@ -1066,7 +1128,7 @@ static int sdmmc_get_cd(struct mmc_host *mmc)
|
||||||
u32 val;
|
u32 val;
|
||||||
|
|
||||||
if (host->eject)
|
if (host->eject)
|
||||||
return -ENOMEDIUM;
|
return cd;
|
||||||
|
|
||||||
mutex_lock(&pcr->pcr_mutex);
|
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);
|
struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
host->cookie = -1;
|
||||||
mmc_detect_change(host->mmc, 0);
|
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->pcr = pcr;
|
||||||
host->mmc = mmc;
|
host->mmc = mmc;
|
||||||
host->pdev = pdev;
|
host->pdev = pdev;
|
||||||
|
host->cookie = -1;
|
||||||
host->power_state = SDMMC_POWER_OFF;
|
host->power_state = SDMMC_POWER_OFF;
|
||||||
INIT_WORK(&host->work, sd_request);
|
INIT_WORK(&host->work, sd_request);
|
||||||
platform_set_drvdata(pdev, host);
|
platform_set_drvdata(pdev, host);
|
||||||
|
|
|
@ -158,7 +158,7 @@ static int sdhci_acpi_emmc_probe_slot(struct platform_device *pdev,
|
||||||
|
|
||||||
host = c->host;
|
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") &&
|
if (hid && uid && !strcmp(hid, "80860F14") && !strcmp(uid, "1") &&
|
||||||
sdhci_readl(host, SDHCI_CAPABILITIES) == 0x446cc8b2 &&
|
sdhci_readl(host, SDHCI_CAPABILITIES) == 0x446cc8b2 &&
|
||||||
|
@ -179,7 +179,7 @@ static int sdhci_acpi_sdio_probe_slot(struct platform_device *pdev,
|
||||||
|
|
||||||
host = c->host;
|
host = c->host;
|
||||||
|
|
||||||
/* Platform specific code during emmc proble slot goes here */
|
/* Platform specific code during sdio probe slot goes here */
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -195,7 +195,7 @@ static int sdhci_acpi_sd_probe_slot(struct platform_device *pdev,
|
||||||
|
|
||||||
host = c->host;
|
host = c->host;
|
||||||
|
|
||||||
/* Platform specific code during emmc proble slot goes here */
|
/* Platform specific code during sd probe slot goes here */
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -448,18 +448,13 @@ static int sdhci_acpi_runtime_resume(struct device *dev)
|
||||||
return sdhci_runtime_resume_host(c->host);
|
return sdhci_runtime_resume_host(c->host);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sdhci_acpi_runtime_idle(struct device *dev)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const struct dev_pm_ops sdhci_acpi_pm_ops = {
|
static const struct dev_pm_ops sdhci_acpi_pm_ops = {
|
||||||
.suspend = sdhci_acpi_suspend,
|
.suspend = sdhci_acpi_suspend,
|
||||||
.resume = sdhci_acpi_resume,
|
.resume = sdhci_acpi_resume,
|
||||||
SET_RUNTIME_PM_OPS(sdhci_acpi_runtime_suspend,
|
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 = {
|
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);
|
kona_dev = sdhci_pltfm_priv(pltfm_priv);
|
||||||
mutex_init(&kona_dev->write_lock);
|
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) {
|
if (!host->mmc->f_max) {
|
||||||
dev_err(&pdev->dev, "Missing max-freq for SDHCI cfg\n");
|
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;
|
goto disable_clk;
|
||||||
|
|
||||||
pm_runtime_set_active(&pdev->dev);
|
pm_runtime_set_active(&pdev->dev);
|
||||||
pm_runtime_enable(&pdev->dev);
|
|
||||||
pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
|
pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
|
||||||
pm_runtime_use_autosuspend(&pdev->dev);
|
pm_runtime_use_autosuspend(&pdev->dev);
|
||||||
pm_suspend_ignore_children(&pdev->dev, 1);
|
pm_suspend_ignore_children(&pdev->dev, 1);
|
||||||
|
pm_runtime_enable(&pdev->dev);
|
||||||
|
|
||||||
return 0;
|
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;
|
struct pltfm_imx_data *imx_data = pltfm_host->priv;
|
||||||
int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
|
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);
|
sdhci_remove_host(host, dead);
|
||||||
|
|
||||||
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
clk_disable_unprepare(imx_data->clk_per);
|
||||||
pm_runtime_disable(&pdev->dev);
|
clk_disable_unprepare(imx_data->clk_ipg);
|
||||||
|
clk_disable_unprepare(imx_data->clk_ahb);
|
||||||
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);
|
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);
|
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 = {
|
static const struct sdhci_ops sdhci_esdhc_ops = {
|
||||||
.read_l = esdhc_readl,
|
.read_l = esdhc_readl,
|
||||||
.read_w = esdhc_readw,
|
.read_w = esdhc_readw,
|
||||||
|
@ -290,7 +298,7 @@ static const struct sdhci_ops sdhci_esdhc_ops = {
|
||||||
.platform_init = esdhc_of_platform_init,
|
.platform_init = esdhc_of_platform_init,
|
||||||
.adma_workaround = esdhci_of_adma_workaround,
|
.adma_workaround = esdhci_of_adma_workaround,
|
||||||
.set_bus_width = esdhc_pltfm_set_bus_width,
|
.set_bus_width = esdhc_pltfm_set_bus_width,
|
||||||
.reset = sdhci_reset,
|
.reset = esdhc_reset,
|
||||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
.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 */
|
/* 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);
|
mmc_of_parse_voltage(np, &host->ocr_mask);
|
||||||
|
|
||||||
ret = sdhci_add_host(host);
|
ret = sdhci_add_host(host);
|
||||||
if (ret)
|
if (ret)
|
||||||
sdhci_pltfm_free(pdev);
|
goto err;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
err:
|
||||||
|
sdhci_pltfm_free(pdev);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1367,11 +1367,6 @@ static int sdhci_pci_runtime_resume(struct device *dev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sdhci_pci_runtime_idle(struct device *dev)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else /* CONFIG_PM */
|
#else /* CONFIG_PM */
|
||||||
|
|
||||||
#define sdhci_pci_suspend NULL
|
#define sdhci_pci_suspend NULL
|
||||||
|
@ -1383,7 +1378,7 @@ static const struct dev_pm_ops sdhci_pci_pm_ops = {
|
||||||
.suspend = sdhci_pci_suspend,
|
.suspend = sdhci_pci_suspend,
|
||||||
.resume = sdhci_pci_resume,
|
.resume = sdhci_pci_resume,
|
||||||
SET_RUNTIME_PM_OPS(sdhci_pci_runtime_suspend,
|
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_core;
|
||||||
struct clk *clk_io;
|
struct clk *clk_io;
|
||||||
u8 power_mode;
|
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_WINDOW_BASE(i) (0x84 + ((i) << 3))
|
||||||
#define SDHCI_MAX_WIN_NUM 8
|
#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,
|
static int mv_conf_mbus_windows(struct platform_device *pdev,
|
||||||
const struct mbus_dram_target_info *dram)
|
const struct mbus_dram_target_info *dram)
|
||||||
{
|
{
|
||||||
|
@ -118,6 +127,51 @@ static int mv_conf_mbus_windows(struct platform_device *pdev,
|
||||||
return 0;
|
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)
|
static void pxav3_reset(struct sdhci_host *host, u8 mask)
|
||||||
{
|
{
|
||||||
struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc));
|
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)
|
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;
|
u16 ctrl_2;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -223,6 +279,24 @@ static void pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
|
||||||
break;
|
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);
|
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
|
||||||
dev_dbg(mmc_dev(host->mmc),
|
dev_dbg(mmc_dev(host->mmc),
|
||||||
"%s uhs = %d, ctrl_2 = %04X\n",
|
"%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)
|
if (!pdata)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
of_property_read_u32(np, "mrvl,clk-delay-cycles", &clk_delay_cycles);
|
if (!of_property_read_u32(np, "mrvl,clk-delay-cycles",
|
||||||
if (clk_delay_cycles > 0)
|
&clk_delay_cycles))
|
||||||
pdata->clk_delay_cycles = clk_delay_cycles;
|
pdata->clk_delay_cycles = clk_delay_cycles;
|
||||||
|
|
||||||
return pdata;
|
return pdata;
|
||||||
|
@ -318,15 +392,18 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
|
||||||
if (!IS_ERR(pxa->clk_core))
|
if (!IS_ERR(pxa->clk_core))
|
||||||
clk_prepare_enable(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")) {
|
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());
|
ret = mv_conf_mbus_windows(pdev, mv_mbus_dram_info());
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_mbus_win;
|
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);
|
match = of_match_device(of_match_ptr(sdhci_pxav3_of_match), &pdev->dev);
|
||||||
if (match) {
|
if (match) {
|
||||||
ret = mmc_of_parse(host->mmc);
|
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_noresume(&pdev->dev);
|
||||||
pm_runtime_get_sync(&pdev->dev);
|
pm_runtime_set_active(&pdev->dev);
|
||||||
pm_runtime_set_autosuspend_delay(&pdev->dev, PXAV3_RPM_DELAY_MS);
|
pm_runtime_set_autosuspend_delay(&pdev->dev, PXAV3_RPM_DELAY_MS);
|
||||||
pm_runtime_use_autosuspend(&pdev->dev);
|
pm_runtime_use_autosuspend(&pdev->dev);
|
||||||
|
pm_runtime_enable(&pdev->dev);
|
||||||
pm_suspend_ignore_children(&pdev->dev, 1);
|
pm_suspend_ignore_children(&pdev->dev, 1);
|
||||||
|
|
||||||
ret = sdhci_add_host(host);
|
ret = sdhci_add_host(host);
|
||||||
|
@ -391,14 +469,13 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_add_host:
|
err_add_host:
|
||||||
pm_runtime_put_sync(&pdev->dev);
|
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
pm_runtime_put_noidle(&pdev->dev);
|
||||||
err_of_parse:
|
err_of_parse:
|
||||||
err_cd_req:
|
err_cd_req:
|
||||||
err_mbus_win:
|
err_mbus_win:
|
||||||
clk_disable_unprepare(pxa->clk_io);
|
clk_disable_unprepare(pxa->clk_io);
|
||||||
if (!IS_ERR(pxa->clk_core))
|
clk_disable_unprepare(pxa->clk_core);
|
||||||
clk_disable_unprepare(pxa->clk_core);
|
|
||||||
err_clk_get:
|
err_clk_get:
|
||||||
sdhci_pltfm_free(pdev);
|
sdhci_pltfm_free(pdev);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -411,12 +488,13 @@ static int sdhci_pxav3_remove(struct platform_device *pdev)
|
||||||
struct sdhci_pxa *pxa = pltfm_host->priv;
|
struct sdhci_pxa *pxa = pltfm_host->priv;
|
||||||
|
|
||||||
pm_runtime_get_sync(&pdev->dev);
|
pm_runtime_get_sync(&pdev->dev);
|
||||||
sdhci_remove_host(host, 1);
|
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
pm_runtime_put_noidle(&pdev->dev);
|
||||||
|
|
||||||
|
sdhci_remove_host(host, 1);
|
||||||
|
|
||||||
clk_disable_unprepare(pxa->clk_io);
|
clk_disable_unprepare(pxa->clk_io);
|
||||||
if (!IS_ERR(pxa->clk_core))
|
clk_disable_unprepare(pxa->clk_core);
|
||||||
clk_disable_unprepare(pxa->clk_core);
|
|
||||||
|
|
||||||
sdhci_pltfm_free(pdev);
|
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_host *host = dev_get_drvdata(dev);
|
||||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
struct sdhci_pxa *pxa = pltfm_host->priv;
|
struct sdhci_pxa *pxa = pltfm_host->priv;
|
||||||
unsigned long flags;
|
int ret;
|
||||||
|
|
||||||
spin_lock_irqsave(&host->lock, flags);
|
ret = sdhci_runtime_suspend_host(host);
|
||||||
host->runtime_suspended = true;
|
if (ret)
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
return ret;
|
||||||
|
|
||||||
clk_disable_unprepare(pxa->clk_io);
|
clk_disable_unprepare(pxa->clk_io);
|
||||||
if (!IS_ERR(pxa->clk_core))
|
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_host *host = dev_get_drvdata(dev);
|
||||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
struct sdhci_pxa *pxa = pltfm_host->priv;
|
struct sdhci_pxa *pxa = pltfm_host->priv;
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
clk_prepare_enable(pxa->clk_io);
|
clk_prepare_enable(pxa->clk_io);
|
||||||
if (!IS_ERR(pxa->clk_core))
|
if (!IS_ERR(pxa->clk_core))
|
||||||
clk_prepare_enable(pxa->clk_core);
|
clk_prepare_enable(pxa->clk_core);
|
||||||
|
|
||||||
spin_lock_irqsave(&host->lock, flags);
|
return sdhci_runtime_resume_host(host);
|
||||||
host->runtime_suspended = false;
|
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
* published by the Free Software Foundation.
|
* published by the Free Software Foundation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/spinlock.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
#include <linux/platform_device.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);
|
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);
|
ret = clk_set_rate(ourhost->clk_bus[ourhost->cur_clk], clock);
|
||||||
|
spin_lock_irq(&host->lock);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
dev_err(dev, "%s: failed to set clock rate %uHz\n",
|
dev_err(dev, "%s: failed to set clock rate %uHz\n",
|
||||||
mmc_hostname(host->mmc), clock);
|
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_runtime_use_autosuspend(&pdev->dev);
|
||||||
pm_suspend_ignore_children(&pdev->dev, 1);
|
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);
|
ret = sdhci_add_host(host);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
|
|
@ -15,7 +15,9 @@
|
||||||
#include <linux/mmc/slot-gpio.h>
|
#include <linux/mmc/slot-gpio.h>
|
||||||
#include "sdhci-pltfm.h"
|
#include "sdhci-pltfm.h"
|
||||||
|
|
||||||
|
#define SDHCI_CLK_DELAY_SETTING 0x4C
|
||||||
#define SDHCI_SIRF_8BITBUS BIT(3)
|
#define SDHCI_SIRF_8BITBUS BIT(3)
|
||||||
|
#define SIRF_TUNING_COUNT 128
|
||||||
|
|
||||||
struct sdhci_sirf_priv {
|
struct sdhci_sirf_priv {
|
||||||
struct clk *clk;
|
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);
|
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 = {
|
static struct sdhci_ops sdhci_sirf_ops = {
|
||||||
|
.platform_execute_tuning = sdhci_sirf_execute_tuning,
|
||||||
.set_clock = sdhci_set_clock,
|
.set_clock = sdhci_set_clock,
|
||||||
.get_max_clock = sdhci_sirf_get_max_clk,
|
.get_max_clock = sdhci_sirf_get_max_clk,
|
||||||
.set_bus_width = sdhci_sirf_set_bus_width,
|
.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);
|
sdhci_pltfm_unregister(pdev);
|
||||||
|
|
||||||
if (gpio_is_valid(priv->gpio_cd))
|
|
||||||
mmc_gpio_free_cd(host->mmc);
|
|
||||||
|
|
||||||
clk_disable_unprepare(priv->clk);
|
clk_disable_unprepare(priv->clk);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,10 +78,9 @@ static int sdhci_st_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = mmc_of_parse(host->mmc);
|
ret = mmc_of_parse(host->mmc);
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "Failed mmc_of_parse\n");
|
dev_err(&pdev->dev, "Failed mmc_of_parse\n");
|
||||||
return ret;
|
goto err_of;
|
||||||
}
|
}
|
||||||
|
|
||||||
clk_prepare_enable(clk);
|
clk_prepare_enable(clk);
|
||||||
|
@ -108,6 +107,7 @@ static int sdhci_st_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
err_out:
|
err_out:
|
||||||
clk_disable_unprepare(clk);
|
clk_disable_unprepare(clk);
|
||||||
|
err_of:
|
||||||
sdhci_pltfm_free(pdev);
|
sdhci_pltfm_free(pdev);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#define NVQUIRK_DISABLE_SDR50 BIT(3)
|
#define NVQUIRK_DISABLE_SDR50 BIT(3)
|
||||||
#define NVQUIRK_DISABLE_SDR104 BIT(4)
|
#define NVQUIRK_DISABLE_SDR104 BIT(4)
|
||||||
#define NVQUIRK_DISABLE_DDR50 BIT(5)
|
#define NVQUIRK_DISABLE_DDR50 BIT(5)
|
||||||
|
#define NVQUIRK_SHADOW_XFER_MODE_REG BIT(6)
|
||||||
|
|
||||||
struct sdhci_tegra_soc_data {
|
struct sdhci_tegra_soc_data {
|
||||||
const struct sdhci_pltfm_data *pdata;
|
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);
|
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)
|
static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
|
||||||
{
|
{
|
||||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
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 = {
|
static const struct sdhci_ops tegra_sdhci_ops = {
|
||||||
.get_ro = tegra_sdhci_get_ro,
|
.get_ro = tegra_sdhci_get_ro,
|
||||||
.read_w = tegra_sdhci_readw,
|
.read_w = tegra_sdhci_readw,
|
||||||
|
.write_w = tegra_sdhci_writew,
|
||||||
.write_l = tegra_sdhci_writel,
|
.write_l = tegra_sdhci_writel,
|
||||||
.set_clock = sdhci_set_clock,
|
.set_clock = sdhci_set_clock,
|
||||||
.set_bus_width = tegra_sdhci_set_bus_width,
|
.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,
|
.pdata = &sdhci_tegra114_pdata,
|
||||||
.nvquirks = NVQUIRK_DISABLE_SDR50 |
|
.nvquirks = NVQUIRK_DISABLE_SDR50 |
|
||||||
NVQUIRK_DISABLE_DDR50 |
|
NVQUIRK_DISABLE_DDR50 |
|
||||||
NVQUIRK_DISABLE_SDR104,
|
NVQUIRK_DISABLE_SDR104 |
|
||||||
|
NVQUIRK_SHADOW_XFER_MODE_REG,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id sdhci_tegra_dt_match[] = {
|
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 int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
|
||||||
static void sdhci_tuning_timer(unsigned long data);
|
static void sdhci_tuning_timer(unsigned long data);
|
||||||
static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
|
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
|
#ifdef CONFIG_PM
|
||||||
static int sdhci_runtime_pm_get(struct sdhci_host *host);
|
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;
|
goto fail;
|
||||||
BUG_ON(host->align_addr & host->align_mask);
|
BUG_ON(host->align_addr & host->align_mask);
|
||||||
|
|
||||||
host->sg_count = dma_map_sg(mmc_dev(host->mmc),
|
host->sg_count = sdhci_pre_dma_transfer(host, data, NULL);
|
||||||
data->sg, data->sg_len, direction);
|
if (host->sg_count < 0)
|
||||||
if (host->sg_count == 0)
|
|
||||||
goto unmap_align;
|
goto unmap_align;
|
||||||
|
|
||||||
desc = host->adma_table;
|
desc = host->adma_table;
|
||||||
|
@ -531,8 +533,6 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
|
||||||
if (offset) {
|
if (offset) {
|
||||||
if (data->flags & MMC_DATA_WRITE) {
|
if (data->flags & MMC_DATA_WRITE) {
|
||||||
buffer = sdhci_kmap_atomic(sg, &flags);
|
buffer = sdhci_kmap_atomic(sg, &flags);
|
||||||
WARN_ON(((long)buffer & (PAGE_SIZE - 1)) >
|
|
||||||
(PAGE_SIZE - offset));
|
|
||||||
memcpy(align, buffer, offset);
|
memcpy(align, buffer, offset);
|
||||||
sdhci_kunmap_atomic(buffer, &flags);
|
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);
|
(sg_dma_address(sg) & host->align_mask);
|
||||||
|
|
||||||
buffer = sdhci_kmap_atomic(sg, &flags);
|
buffer = sdhci_kmap_atomic(sg, &flags);
|
||||||
WARN_ON(((long)buffer & (PAGE_SIZE - 1)) >
|
|
||||||
(PAGE_SIZE - size));
|
|
||||||
memcpy(buffer, align, size);
|
memcpy(buffer, align, size);
|
||||||
sdhci_kunmap_atomic(buffer, &flags);
|
sdhci_kunmap_atomic(buffer, &flags);
|
||||||
|
|
||||||
|
@ -649,8 +647,9 @@ static void sdhci_adma_table_post(struct sdhci_host *host,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dma_unmap_sg(mmc_dev(host->mmc), data->sg,
|
if (!data->host_cookie)
|
||||||
data->sg_len, direction);
|
dma_unmap_sg(mmc_dev(host->mmc), data->sg,
|
||||||
|
data->sg_len, direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
|
static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
|
||||||
|
@ -846,11 +845,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
|
||||||
} else {
|
} else {
|
||||||
int sg_cnt;
|
int sg_cnt;
|
||||||
|
|
||||||
sg_cnt = dma_map_sg(mmc_dev(host->mmc),
|
sg_cnt = sdhci_pre_dma_transfer(host, data, NULL);
|
||||||
data->sg, data->sg_len,
|
|
||||||
(data->flags & MMC_DATA_READ) ?
|
|
||||||
DMA_FROM_DEVICE :
|
|
||||||
DMA_TO_DEVICE);
|
|
||||||
if (sg_cnt == 0) {
|
if (sg_cnt == 0) {
|
||||||
/*
|
/*
|
||||||
* This only happens when someone fed
|
* 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,
|
static void sdhci_set_transfer_mode(struct sdhci_host *host,
|
||||||
struct mmc_command *cmd)
|
struct mmc_command *cmd)
|
||||||
{
|
{
|
||||||
u16 mode;
|
u16 mode = 0;
|
||||||
struct mmc_data *data = cmd->data;
|
struct mmc_data *data = cmd->data;
|
||||||
|
|
||||||
if (data == NULL) {
|
if (data == NULL) {
|
||||||
|
@ -927,9 +922,11 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
|
||||||
|
|
||||||
WARN_ON(!host->data);
|
WARN_ON(!host->data);
|
||||||
|
|
||||||
mode = SDHCI_TRNS_BLK_CNT_EN;
|
if (!(host->quirks2 & SDHCI_QUIRK2_SUPPORT_SINGLE))
|
||||||
|
mode = SDHCI_TRNS_BLK_CNT_EN;
|
||||||
|
|
||||||
if (mmc_op_multi(cmd->opcode) || data->blocks > 1) {
|
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
|
* If we are sending CMD23, CMD12 never gets sent
|
||||||
* on successful completion (so no Auto-CMD12).
|
* 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)
|
if (host->flags & SDHCI_USE_ADMA)
|
||||||
sdhci_adma_table_post(host, data);
|
sdhci_adma_table_post(host, data);
|
||||||
else {
|
else {
|
||||||
dma_unmap_sg(mmc_dev(host->mmc), data->sg,
|
if (!data->host_cookie)
|
||||||
data->sg_len, (data->flags & MMC_DATA_READ) ?
|
dma_unmap_sg(mmc_dev(host->mmc),
|
||||||
|
data->sg, data->sg_len,
|
||||||
|
(data->flags & MMC_DATA_READ) ?
|
||||||
DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
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
|
* signalling timeout and CRC errors even on CMD0. Resetting
|
||||||
* it on each ios seems to solve the problem.
|
* 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);
|
sdhci_do_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
|
||||||
|
|
||||||
mmiowb();
|
mmiowb();
|
||||||
|
@ -1832,6 +1831,10 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
|
||||||
ctrl |= SDHCI_CTRL_VDD_180;
|
ctrl |= SDHCI_CTRL_VDD_180;
|
||||||
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
|
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 */
|
/* 1.8V regulator output should be stable within 5 ms */
|
||||||
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
||||||
if (ctrl & SDHCI_CTRL_VDD_180)
|
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_readw(host, SDHCI_HOST_CONTROL2);
|
||||||
ctrl |= SDHCI_CTRL_EXEC_TUNING;
|
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);
|
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)
|
static void sdhci_card_event(struct mmc_host *mmc)
|
||||||
{
|
{
|
||||||
struct sdhci_host *host = mmc_priv(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 = {
|
static const struct mmc_host_ops sdhci_ops = {
|
||||||
.request = sdhci_request,
|
.request = sdhci_request,
|
||||||
|
.post_req = sdhci_post_req,
|
||||||
|
.pre_req = sdhci_pre_req,
|
||||||
.set_ios = sdhci_set_ios,
|
.set_ios = sdhci_set_ios,
|
||||||
.get_cd = sdhci_get_cd,
|
.get_cd = sdhci_get_cd,
|
||||||
.get_ro = sdhci_get_ro,
|
.get_ro = sdhci_get_ro,
|
||||||
|
@ -2793,9 +2871,9 @@ int sdhci_runtime_resume_host(struct sdhci_host *host)
|
||||||
/* Force clock and power re-program */
|
/* Force clock and power re-program */
|
||||||
host->pwr = 0;
|
host->pwr = 0;
|
||||||
host->clock = 0;
|
host->clock = 0;
|
||||||
|
sdhci_do_start_signal_voltage_switch(host, &host->mmc->ios);
|
||||||
sdhci_do_set_ios(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) &&
|
if ((host_flags & SDHCI_PV_ENABLED) &&
|
||||||
!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN)) {
|
!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN)) {
|
||||||
spin_lock_irqsave(&host->lock, flags);
|
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->max_clk = host->ops->get_max_clock(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
host->next_data.cookie = 1;
|
||||||
/*
|
/*
|
||||||
* In case of Host Controller v3.00, find out whether clock
|
* In case of Host Controller v3.00, find out whether clock
|
||||||
* multiplier is supported.
|
* 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);
|
setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host);
|
||||||
|
|
||||||
if (host->version >= SDHCI_SPEC_300) {
|
init_waitqueue_head(&host->buf_ready_int);
|
||||||
init_waitqueue_head(&host->buf_ready_int);
|
|
||||||
|
|
||||||
|
if (host->version >= SDHCI_SPEC_300) {
|
||||||
/* Initialize re-tuning timer */
|
/* Initialize re-tuning timer */
|
||||||
init_timer(&host->tuning_timer);
|
init_timer(&host->tuning_timer);
|
||||||
host->tuning_timer.data = (unsigned long)host;
|
host->tuning_timer.data = (unsigned long)host;
|
||||||
|
|
|
@ -339,6 +339,7 @@ struct sdhci_ops {
|
||||||
void (*adma_workaround)(struct sdhci_host *host, u32 intmask);
|
void (*adma_workaround)(struct sdhci_host *host, u32 intmask);
|
||||||
void (*platform_init)(struct sdhci_host *host);
|
void (*platform_init)(struct sdhci_host *host);
|
||||||
void (*card_event)(struct sdhci_host *host);
|
void (*card_event)(struct sdhci_host *host);
|
||||||
|
void (*voltage_switch)(struct sdhci_host *host);
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
|
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
|
||||||
|
|
|
@ -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 EXT_ACC 0xe4
|
||||||
|
|
||||||
|
#define host_to_priv(host) container_of((host)->pdata, struct sh_mobile_sdhi, mmc_data)
|
||||||
|
|
||||||
struct sh_mobile_sdhi_of_data {
|
struct sh_mobile_sdhi_of_data {
|
||||||
unsigned long tmio_flags;
|
unsigned long tmio_flags;
|
||||||
unsigned long capabilities;
|
unsigned long capabilities;
|
||||||
unsigned long capabilities2;
|
unsigned long capabilities2;
|
||||||
|
enum dma_slave_buswidth dma_buswidth;
|
||||||
dma_addr_t dma_rx_offset;
|
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_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
|
||||||
TMIO_MMC_CLK_ACTUAL,
|
TMIO_MMC_CLK_ACTUAL,
|
||||||
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
|
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
|
||||||
|
.dma_buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES,
|
||||||
.dma_rx_offset = 0x2000,
|
.dma_rx_offset = 0x2000,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -84,16 +88,43 @@ struct sh_mobile_sdhi {
|
||||||
struct tmio_mmc_dma dma_priv;
|
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)
|
static int sh_mobile_sdhi_clk_enable(struct platform_device *pdev, unsigned int *f)
|
||||||
{
|
{
|
||||||
struct mmc_host *mmc = platform_get_drvdata(pdev);
|
struct mmc_host *mmc = platform_get_drvdata(pdev);
|
||||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||||
struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data);
|
struct sh_mobile_sdhi *priv = host_to_priv(host);
|
||||||
int ret = clk_prepare_enable(priv->clk);
|
int ret = clk_prepare_enable(priv->clk);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
*f = clk_get_rate(priv->clk);
|
*f = clk_get_rate(priv->clk);
|
||||||
|
|
||||||
|
/* enable 16bit data access on SDBUF as default */
|
||||||
|
sh_mobile_sdhi_sdbuf_width(host, 16);
|
||||||
|
|
||||||
return 0;
|
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 mmc_host *mmc = platform_get_drvdata(pdev);
|
||||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||||
struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data);
|
struct sh_mobile_sdhi *priv = host_to_priv(host);
|
||||||
clk_disable_unprepare(priv->clk);
|
clk_disable_unprepare(priv->clk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +144,7 @@ static int sh_mobile_sdhi_wait_idle(struct tmio_mmc_host *host)
|
||||||
udelay(1);
|
udelay(1);
|
||||||
|
|
||||||
if (!timeout) {
|
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;
|
return -EBUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,14 +187,13 @@ static int sh_mobile_sdhi_multi_io_quirk(struct mmc_card *card,
|
||||||
return blk_size;
|
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 = {
|
/* enable 32bit access if DMA mode if possibile */
|
||||||
.cd_wakeup = sh_mobile_sdhi_cd_wakeup,
|
sh_mobile_sdhi_sdbuf_width(host, enable ? 32 : 16);
|
||||||
};
|
}
|
||||||
|
|
||||||
static int sh_mobile_sdhi_probe(struct platform_device *pdev)
|
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;
|
int irq, ret, i = 0;
|
||||||
bool multiplexed_isr = true;
|
bool multiplexed_isr = true;
|
||||||
struct tmio_mmc_dma *dma_priv;
|
struct tmio_mmc_dma *dma_priv;
|
||||||
u16 ver;
|
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
if (!res)
|
if (!res)
|
||||||
|
@ -192,26 +221,31 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
|
||||||
mmc_data = &priv->mmc_data;
|
mmc_data = &priv->mmc_data;
|
||||||
dma_priv = &priv->dma_priv;
|
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);
|
priv->clk = devm_clk_get(&pdev->dev, NULL);
|
||||||
if (IS_ERR(priv->clk)) {
|
if (IS_ERR(priv->clk)) {
|
||||||
ret = PTR_ERR(priv->clk);
|
ret = PTR_ERR(priv->clk);
|
||||||
dev_err(&pdev->dev, "cannot get clock: %d\n", ret);
|
dev_err(&pdev->dev, "cannot get clock: %d\n", ret);
|
||||||
goto eclkget;
|
goto eprobe;
|
||||||
}
|
}
|
||||||
|
|
||||||
mmc_data->clk_enable = sh_mobile_sdhi_clk_enable;
|
host = tmio_mmc_host_alloc(pdev);
|
||||||
mmc_data->clk_disable = sh_mobile_sdhi_clk_disable;
|
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->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) {
|
if (p) {
|
||||||
mmc_data->flags = p->tmio_flags;
|
mmc_data->flags = p->tmio_flags;
|
||||||
mmc_data->ocr_mask = p->tmio_ocr_mask;
|
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->slave_id_rx = p->dma_slave_rx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dma_priv->alignment_shift = 1; /* 2-byte alignment */
|
|
||||||
dma_priv->filter = shdma_chan_filter;
|
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
|
* 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;
|
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) {
|
if (of_id && of_id->data) {
|
||||||
const struct sh_mobile_sdhi_of_data *of_data = of_id->data;
|
const struct sh_mobile_sdhi_of_data *of_data = of_id->data;
|
||||||
mmc_data->flags |= of_data->tmio_flags;
|
mmc_data->flags |= of_data->tmio_flags;
|
||||||
mmc_data->capabilities |= of_data->capabilities;
|
mmc_data->capabilities |= of_data->capabilities;
|
||||||
mmc_data->capabilities2 |= of_data->capabilities2;
|
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 */
|
ret = tmio_mmc_host_probe(host, mmc_data);
|
||||||
mmc_data->bus_shift = resource_size(res) >> 9;
|
|
||||||
|
|
||||||
ret = tmio_mmc_host_probe(&host, pdev, mmc_data);
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto eprobe;
|
goto efree;
|
||||||
|
|
||||||
/*
|
|
||||||
* FIXME:
|
|
||||||
* this Workaround can be more clever method
|
|
||||||
*/
|
|
||||||
ver = sd_ctrl_read16(host, CTL_VERSION);
|
|
||||||
if (ver == 0xCB0D)
|
|
||||||
sd_ctrl_write16(host, EXT_ACC, 1);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allow one or more specific (named) ISRs or
|
* Allow one or more specific (named) ISRs or
|
||||||
|
@ -351,10 +369,9 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
eirq:
|
eirq:
|
||||||
tmio_mmc_host_remove(host);
|
tmio_mmc_host_remove(host);
|
||||||
|
efree:
|
||||||
|
tmio_mmc_host_free(host);
|
||||||
eprobe:
|
eprobe:
|
||||||
eclkget:
|
|
||||||
if (p && p->cleanup)
|
|
||||||
p->cleanup(pdev);
|
|
||||||
return ret;
|
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 mmc_host *mmc = platform_get_drvdata(pdev);
|
||||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||||
struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
|
|
||||||
|
|
||||||
tmio_mmc_host_remove(host);
|
tmio_mmc_host_remove(host);
|
||||||
|
|
||||||
if (p && p->cleanup)
|
|
||||||
p->cleanup(pdev);
|
|
||||||
|
|
||||||
return 0;
|
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);
|
unsigned long expire = jiffies + msecs_to_jiffies(250);
|
||||||
u32 rval;
|
u32 rval;
|
||||||
|
|
||||||
mmc_writel(host, REG_CMDR, SDXC_HARDWARE_RESET);
|
mmc_writel(host, REG_GCTRL, SDXC_HARDWARE_RESET);
|
||||||
do {
|
do {
|
||||||
rval = mmc_readl(host, REG_GCTRL);
|
rval = mmc_readl(host, REG_GCTRL);
|
||||||
} while (time_before(jiffies, expire) && (rval & SDXC_HARDWARE_RESET));
|
} 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[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
|
* 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");
|
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);
|
sunxi_mmc_send_manual_stop(host, mrq);
|
||||||
|
|
||||||
spin_lock_irqsave(&host->lock, iflags);
|
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,
|
static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host,
|
||||||
struct mmc_ios *ios)
|
struct mmc_ios *ios)
|
||||||
{
|
{
|
||||||
u32 rate, oclk_dly, rval, sclk_dly, src_clk;
|
u32 rate, oclk_dly, rval, sclk_dly;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
rate = clk_round_rate(host->clk_mmc, ios->clock);
|
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;
|
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);
|
clk_sunxi_mmc_phase_control(host->clk_mmc, sclk_dly, oclk_dly);
|
||||||
|
|
||||||
return sunxi_mmc_oclk_onoff(host, 1);
|
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;
|
unsigned long iflags;
|
||||||
u32 imask = SDXC_INTERRUPT_ERROR_BIT;
|
u32 imask = SDXC_INTERRUPT_ERROR_BIT;
|
||||||
u32 cmd_val = SDXC_START | (cmd->opcode & 0x3f);
|
u32 cmd_val = SDXC_START | (cmd->opcode & 0x3f);
|
||||||
|
bool wait_dma = host->wait_dma;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* Check for set_ios errors (should never happen) */
|
/* 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)
|
if (cmd->data->flags & MMC_DATA_WRITE)
|
||||||
cmd_val |= SDXC_WRITE;
|
cmd_val |= SDXC_WRITE;
|
||||||
else
|
else
|
||||||
host->wait_dma = true;
|
wait_dma = true;
|
||||||
} else {
|
} else {
|
||||||
imask |= SDXC_COMMAND_DONE;
|
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->mrq = mrq;
|
||||||
|
host->wait_dma = wait_dma;
|
||||||
mmc_writel(host, REG_IMASK, host->sdio_imask | imask);
|
mmc_writel(host, REG_IMASK, host->sdio_imask | imask);
|
||||||
mmc_writel(host, REG_CARG, cmd->arg);
|
mmc_writel(host, REG_CARG, cmd->arg);
|
||||||
mmc_writel(host, REG_CMDR, cmd_val);
|
mmc_writel(host, REG_CMDR, cmd_val);
|
||||||
|
|
|
@ -88,14 +88,19 @@ static int tmio_mmc_probe(struct platform_device *pdev)
|
||||||
if (!res)
|
if (!res)
|
||||||
return -EINVAL;
|
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;
|
pdata->flags |= TMIO_MMC_HAVE_HIGH_REG;
|
||||||
|
|
||||||
ret = tmio_mmc_host_probe(&host, pdev, pdata);
|
host = tmio_mmc_host_alloc(pdev);
|
||||||
if (ret)
|
if (!host)
|
||||||
goto cell_disable;
|
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,
|
ret = request_irq(irq, tmio_mmc_irq, IRQF_TRIGGER_FALLING,
|
||||||
dev_name(&pdev->dev), host);
|
dev_name(&pdev->dev), host);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -108,6 +113,8 @@ static int tmio_mmc_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
host_remove:
|
host_remove:
|
||||||
tmio_mmc_host_remove(host);
|
tmio_mmc_host_remove(host);
|
||||||
|
host_free:
|
||||||
|
tmio_mmc_host_free(host);
|
||||||
cell_disable:
|
cell_disable:
|
||||||
if (cell->disable)
|
if (cell->disable)
|
||||||
cell->disable(pdev);
|
cell->disable(pdev);
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#ifndef TMIO_MMC_H
|
#ifndef TMIO_MMC_H
|
||||||
#define TMIO_MMC_H
|
#define TMIO_MMC_H
|
||||||
|
|
||||||
|
#include <linux/dmaengine.h>
|
||||||
#include <linux/highmem.h>
|
#include <linux/highmem.h>
|
||||||
#include <linux/mmc/tmio.h>
|
#include <linux/mmc/tmio.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
|
@ -39,6 +40,17 @@
|
||||||
#define TMIO_MASK_IRQ (TMIO_MASK_READOP | TMIO_MASK_WRITEOP | TMIO_MASK_CMD)
|
#define TMIO_MASK_IRQ (TMIO_MASK_READOP | TMIO_MASK_WRITEOP | TMIO_MASK_CMD)
|
||||||
|
|
||||||
struct tmio_mmc_data;
|
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 {
|
struct tmio_mmc_host {
|
||||||
void __iomem *ctl;
|
void __iomem *ctl;
|
||||||
|
@ -56,9 +68,11 @@ struct tmio_mmc_host {
|
||||||
struct scatterlist *sg_orig;
|
struct scatterlist *sg_orig;
|
||||||
unsigned int sg_len;
|
unsigned int sg_len;
|
||||||
unsigned int sg_off;
|
unsigned int sg_off;
|
||||||
|
unsigned long bus_shift;
|
||||||
|
|
||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
struct tmio_mmc_data *pdata;
|
struct tmio_mmc_data *pdata;
|
||||||
|
struct tmio_mmc_dma *dma;
|
||||||
|
|
||||||
/* DMA support */
|
/* DMA support */
|
||||||
bool force_pio;
|
bool force_pio;
|
||||||
|
@ -83,10 +97,17 @@ struct tmio_mmc_host {
|
||||||
struct mutex ios_lock; /* protect set_ios() context */
|
struct mutex ios_lock; /* protect set_ios() context */
|
||||||
bool native_hotplug;
|
bool native_hotplug;
|
||||||
bool sdio_irq_enabled;
|
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 tmio_mmc_host *tmio_mmc_host_alloc(struct platform_device *pdev);
|
||||||
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);
|
struct tmio_mmc_data *pdata);
|
||||||
void tmio_mmc_host_remove(struct tmio_mmc_host *host);
|
void tmio_mmc_host_remove(struct tmio_mmc_host *host);
|
||||||
void tmio_mmc_do_data_irq(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)
|
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,
|
static inline void sd_ctrl_read16_rep(struct tmio_mmc_host *host, int addr,
|
||||||
u16 *buf, int count)
|
u16 *buf, int count)
|
||||||
{
|
{
|
||||||
readsw(host->ctl + (addr << host->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)
|
static inline u32 sd_ctrl_read32(struct tmio_mmc_host *host, int addr)
|
||||||
{
|
{
|
||||||
return readw(host->ctl + (addr << host->pdata->bus_shift)) |
|
return readw(host->ctl + (addr << host->bus_shift)) |
|
||||||
readw(host->ctl + ((addr + 2) << host->pdata->bus_shift)) << 16;
|
readw(host->ctl + ((addr + 2) << host->bus_shift)) << 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void sd_ctrl_write16(struct tmio_mmc_host *host, int addr, u16 val)
|
static inline void sd_ctrl_write16(struct tmio_mmc_host *host, int addr, u16 val)
|
||||||
|
@ -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
|
/* If there is a hook and it returns non-zero then there
|
||||||
* is an error and the write should be skipped
|
* 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;
|
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,
|
static inline void sd_ctrl_write16_rep(struct tmio_mmc_host *host, int addr,
|
||||||
u16 *buf, int count)
|
u16 *buf, int count)
|
||||||
{
|
{
|
||||||
writesw(host->ctl + (addr << host->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)
|
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, host->ctl + (addr << host->bus_shift));
|
||||||
writew(val >> 16, host->ctl + ((addr + 2) << host->pdata->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)
|
if (!host->chan_tx || !host->chan_rx)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (host->pdata->flags & TMIO_MMC_HAVE_CTL_DMA_REG)
|
if (host->dma->enable)
|
||||||
sd_ctrl_write16(host, CTL_DMA_ENABLE, enable ? 2 : 0);
|
host->dma->enable(host, enable);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tmio_mmc_abort_dma(struct tmio_mmc_host *host)
|
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 scatterlist *sg = host->sg_ptr, *sg_tmp;
|
||||||
struct dma_async_tx_descriptor *desc = NULL;
|
struct dma_async_tx_descriptor *desc = NULL;
|
||||||
struct dma_chan *chan = host->chan_rx;
|
struct dma_chan *chan = host->chan_rx;
|
||||||
struct tmio_mmc_data *pdata = host->pdata;
|
|
||||||
dma_cookie_t cookie;
|
dma_cookie_t cookie;
|
||||||
int ret, i;
|
int ret, i;
|
||||||
bool aligned = true, multiple = true;
|
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) {
|
for_each_sg(sg, sg_tmp, host->sg_len, i) {
|
||||||
if (sg_tmp->offset & align)
|
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 scatterlist *sg = host->sg_ptr, *sg_tmp;
|
||||||
struct dma_async_tx_descriptor *desc = NULL;
|
struct dma_async_tx_descriptor *desc = NULL;
|
||||||
struct dma_chan *chan = host->chan_tx;
|
struct dma_chan *chan = host->chan_tx;
|
||||||
struct tmio_mmc_data *pdata = host->pdata;
|
|
||||||
dma_cookie_t cookie;
|
dma_cookie_t cookie;
|
||||||
int ret, i;
|
int ret, i;
|
||||||
bool aligned = true, multiple = true;
|
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) {
|
for_each_sg(sg, sg_tmp, host->sg_len, i) {
|
||||||
if (sg_tmp->offset & align)
|
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)
|
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 */
|
/* 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 &&
|
if (!host->dma || (!host->pdev->dev.of_node &&
|
||||||
(!pdata->dma->chan_priv_tx || !pdata->dma->chan_priv_rx)))
|
(!host->dma->chan_priv_tx || !host->dma->chan_priv_rx)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!host->chan_tx && !host->chan_rx) {
|
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);
|
dma_cap_set(DMA_SLAVE, mask);
|
||||||
|
|
||||||
host->chan_tx = dma_request_slave_channel_compat(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");
|
&host->pdev->dev, "tx");
|
||||||
dev_dbg(&host->pdev->dev, "%s: TX: got channel %p\n", __func__,
|
dev_dbg(&host->pdev->dev, "%s: TX: got channel %p\n", __func__,
|
||||||
host->chan_tx);
|
host->chan_tx);
|
||||||
|
@ -288,18 +286,20 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat
|
||||||
if (!host->chan_tx)
|
if (!host->chan_tx)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (pdata->dma->chan_priv_tx)
|
if (host->dma->chan_priv_tx)
|
||||||
cfg.slave_id = pdata->dma->slave_id_tx;
|
cfg.slave_id = host->dma->slave_id_tx;
|
||||||
cfg.direction = DMA_MEM_TO_DEV;
|
cfg.direction = DMA_MEM_TO_DEV;
|
||||||
cfg.dst_addr = res->start + (CTL_SD_DATA_PORT << host->pdata->bus_shift);
|
cfg.dst_addr = res->start + (CTL_SD_DATA_PORT << host->bus_shift);
|
||||||
cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
|
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;
|
cfg.src_addr = 0;
|
||||||
ret = dmaengine_slave_config(host->chan_tx, &cfg);
|
ret = dmaengine_slave_config(host->chan_tx, &cfg);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto ecfgtx;
|
goto ecfgtx;
|
||||||
|
|
||||||
host->chan_rx = dma_request_slave_channel_compat(mask,
|
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");
|
&host->pdev->dev, "rx");
|
||||||
dev_dbg(&host->pdev->dev, "%s: RX: got channel %p\n", __func__,
|
dev_dbg(&host->pdev->dev, "%s: RX: got channel %p\n", __func__,
|
||||||
host->chan_rx);
|
host->chan_rx);
|
||||||
|
@ -307,11 +307,13 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat
|
||||||
if (!host->chan_rx)
|
if (!host->chan_rx)
|
||||||
goto ereqrx;
|
goto ereqrx;
|
||||||
|
|
||||||
if (pdata->dma->chan_priv_rx)
|
if (host->dma->chan_priv_rx)
|
||||||
cfg.slave_id = pdata->dma->slave_id_rx;
|
cfg.slave_id = host->dma->slave_id_rx;
|
||||||
cfg.direction = DMA_DEV_TO_MEM;
|
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 = DMA_SLAVE_BUSWIDTH_2_BYTES;
|
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;
|
cfg.dst_addr = 0;
|
||||||
ret = dmaengine_slave_config(host->chan_rx, &cfg);
|
ret = dmaengine_slave_config(host->chan_rx, &cfg);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
|
|
@ -835,13 +835,12 @@ fail:
|
||||||
static int tmio_mmc_clk_update(struct tmio_mmc_host *host)
|
static int tmio_mmc_clk_update(struct tmio_mmc_host *host)
|
||||||
{
|
{
|
||||||
struct mmc_host *mmc = host->mmc;
|
struct mmc_host *mmc = host->mmc;
|
||||||
struct tmio_mmc_data *pdata = host->pdata;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!pdata->clk_enable)
|
if (!host->clk_enable)
|
||||||
return -ENOTSUPP;
|
return -ENOTSUPP;
|
||||||
|
|
||||||
ret = pdata->clk_enable(host->pdev, &mmc->f_max);
|
ret = host->clk_enable(host->pdev, &mmc->f_max);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
mmc->f_min = mmc->f_max / 512;
|
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)
|
unsigned int direction, int blk_size)
|
||||||
{
|
{
|
||||||
struct tmio_mmc_host *host = mmc_priv(card->host);
|
struct tmio_mmc_host *host = mmc_priv(card->host);
|
||||||
struct tmio_mmc_data *pdata = host->pdata;
|
|
||||||
|
|
||||||
if (pdata->multi_io_quirk)
|
if (host->multi_io_quirk)
|
||||||
return pdata->multi_io_quirk(card, direction, blk_size);
|
return host->multi_io_quirk(card, direction, blk_size);
|
||||||
|
|
||||||
return 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;
|
pdata->flags |= TMIO_MMC_WRPROTECT_DISABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tmio_mmc_host_probe(struct tmio_mmc_host **host,
|
struct tmio_mmc_host*
|
||||||
struct platform_device *pdev,
|
tmio_mmc_host_alloc(struct platform_device *pdev)
|
||||||
struct tmio_mmc_data *pdata)
|
|
||||||
{
|
{
|
||||||
struct tmio_mmc_host *_host;
|
struct tmio_mmc_host *host;
|
||||||
struct mmc_host *mmc;
|
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 platform_device *pdev = _host->pdev;
|
||||||
|
struct mmc_host *mmc = _host->mmc;
|
||||||
struct resource *res_ctl;
|
struct resource *res_ctl;
|
||||||
int ret;
|
int ret;
|
||||||
u32 irq_mask = TMIO_MASK_CMD;
|
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);
|
tmio_mmc_of_parse(pdev, pdata);
|
||||||
|
|
||||||
if (!(pdata->flags & TMIO_MMC_HAS_IDLE_WAIT))
|
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);
|
res_ctl = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
if (!res_ctl)
|
if (!res_ctl)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
mmc = mmc_alloc_host(sizeof(struct tmio_mmc_host), &pdev->dev);
|
|
||||||
if (!mmc)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
ret = mmc_of_parse(mmc);
|
ret = mmc_of_parse(mmc);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto host_free;
|
goto host_free;
|
||||||
|
|
||||||
pdata->dev = &pdev->dev;
|
|
||||||
_host = mmc_priv(mmc);
|
|
||||||
_host->pdata = pdata;
|
_host->pdata = pdata;
|
||||||
_host->mmc = mmc;
|
|
||||||
_host->pdev = pdev;
|
|
||||||
platform_set_drvdata(pdev, mmc);
|
platform_set_drvdata(pdev, mmc);
|
||||||
|
|
||||||
_host->set_pwr = pdata->set_pwr;
|
_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);
|
mmc_gpiod_request_cd_irq(mmc);
|
||||||
}
|
}
|
||||||
|
|
||||||
*host = _host;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
host_free:
|
host_free:
|
||||||
mmc_free_host(mmc);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1222,7 +1234,6 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host)
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
|
||||||
iounmap(host->ctl);
|
iounmap(host->ctl);
|
||||||
mmc_free_host(mmc);
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(tmio_mmc_host_remove);
|
EXPORT_SYMBOL(tmio_mmc_host_remove);
|
||||||
|
|
||||||
|
@ -1237,8 +1248,8 @@ int tmio_mmc_host_runtime_suspend(struct device *dev)
|
||||||
if (host->clk_cache)
|
if (host->clk_cache)
|
||||||
tmio_mmc_clk_stop(host);
|
tmio_mmc_clk_stop(host);
|
||||||
|
|
||||||
if (host->pdata->clk_disable)
|
if (host->clk_disable)
|
||||||
host->pdata->clk_disable(host->pdev);
|
host->clk_disable(host->pdev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -176,7 +176,8 @@ static irqreturn_t toshsd_thread_irq(int irq, void *dev_id)
|
||||||
spin_lock_irqsave(&host->lock, flags);
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
|
|
||||||
if (!sg_miter_next(sg_miter))
|
if (!sg_miter_next(sg_miter))
|
||||||
return IRQ_HANDLED;
|
goto done;
|
||||||
|
|
||||||
buf = sg_miter->addr;
|
buf = sg_miter->addr;
|
||||||
|
|
||||||
/* Ensure we dont read more than one block. The chip will interrupt us
|
/* 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->consumed = count;
|
||||||
sg_miter_stop(sg_miter);
|
sg_miter_stop(sg_miter);
|
||||||
|
|
||||||
|
done:
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
|
@ -699,18 +701,7 @@ static struct pci_driver toshsd_driver = {
|
||||||
.driver.pm = &toshsd_pm_ops,
|
.driver.pm = &toshsd_pm_ops,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init toshsd_drv_init(void)
|
module_pci_driver(toshsd_driver);
|
||||||
{
|
|
||||||
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_AUTHOR("Ondrej Zary, Richard Betts");
|
MODULE_AUTHOR("Ondrej Zary, Richard Betts");
|
||||||
MODULE_DESCRIPTION("Toshiba PCI Secure Digital Host Controller Interface driver");
|
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)
|
static void __do_poll(struct vub300_mmc_host *vub300)
|
||||||
{
|
{
|
||||||
/* cmd_mutex is held by vub300_pollwork_thread */
|
/* cmd_mutex is held by vub300_pollwork_thread */
|
||||||
long commretval;
|
unsigned long commretval;
|
||||||
mod_timer(&vub300->inactivity_timer, jiffies + HZ);
|
mod_timer(&vub300->inactivity_timer, jiffies + HZ);
|
||||||
init_completion(&vub300->irqpoll_complete);
|
init_completion(&vub300->irqpoll_complete);
|
||||||
send_irqpoll(vub300);
|
send_irqpoll(vub300);
|
||||||
|
@ -671,8 +671,6 @@ static void __do_poll(struct vub300_mmc_host *vub300)
|
||||||
vub300->usb_timed_out = 1;
|
vub300->usb_timed_out = 1;
|
||||||
usb_kill_urb(vub300->command_out_urb);
|
usb_kill_urb(vub300->command_out_urb);
|
||||||
usb_kill_urb(vub300->command_res_urb);
|
usb_kill_urb(vub300->command_res_urb);
|
||||||
} else if (commretval < 0) {
|
|
||||||
vub300_queue_poll_work(vub300, 1);
|
|
||||||
} else { /* commretval > 0 */
|
} else { /* commretval > 0 */
|
||||||
__vub300_irqpoll_response(vub300);
|
__vub300_irqpoll_response(vub300);
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,11 +95,6 @@
|
||||||
*/
|
*/
|
||||||
#define TMIO_MMC_SDIO_STATUS_QUIRK (1 << 8)
|
#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
|
* 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 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
|
* data for the MMC controller
|
||||||
*/
|
*/
|
||||||
|
@ -132,19 +115,12 @@ struct tmio_mmc_data {
|
||||||
unsigned long capabilities;
|
unsigned long capabilities;
|
||||||
unsigned long capabilities2;
|
unsigned long capabilities2;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
unsigned long bus_shift;
|
|
||||||
u32 ocr_mask; /* available voltages */
|
u32 ocr_mask; /* available voltages */
|
||||||
struct tmio_mmc_dma *dma;
|
|
||||||
struct device *dev;
|
|
||||||
unsigned int cd_gpio;
|
unsigned int cd_gpio;
|
||||||
|
int alignment_shift;
|
||||||
|
dma_addr_t dma_rx_offset;
|
||||||
void (*set_pwr)(struct platform_device *host, int state);
|
void (*set_pwr)(struct platform_device *host, int state);
|
||||||
void (*set_clk_div)(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 */
|
bool hpi; /* HPI support bit */
|
||||||
unsigned int hpi_cmd; /* cmd used as HPI */
|
unsigned int hpi_cmd; /* cmd used as HPI */
|
||||||
bool bkops; /* background support bit */
|
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_sector_size; /* 512 bytes or 4KB */
|
||||||
unsigned int data_tag_unit_size; /* DATA TAG UNIT size */
|
unsigned int data_tag_unit_size; /* DATA TAG UNIT size */
|
||||||
unsigned int boot_ro_lock; /* ro lock support */
|
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,
|
extern int mmc_set_blockcount(struct mmc_card *card, unsigned int blockcount,
|
||||||
bool is_rel_write);
|
bool is_rel_write);
|
||||||
extern int mmc_hw_reset(struct mmc_host *host);
|
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 int mmc_can_reset(struct mmc_card *card);
|
||||||
|
|
||||||
extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_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
|
* @cur_slot, @mrq and @state. These must always be updated
|
||||||
* at the same time while holding @lock.
|
* 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,
|
* 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
|
* and must always be written at the same time as the slot is added to
|
||||||
* @queue.
|
* @queue.
|
||||||
|
@ -125,6 +130,7 @@ struct mmc_data;
|
||||||
*/
|
*/
|
||||||
struct dw_mci {
|
struct dw_mci {
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
|
spinlock_t irq_lock;
|
||||||
void __iomem *regs;
|
void __iomem *regs;
|
||||||
|
|
||||||
struct scatterlist *sg;
|
struct scatterlist *sg;
|
||||||
|
|
|
@ -166,7 +166,6 @@ struct mmc_async_req {
|
||||||
* struct mmc_slot - MMC slot functions
|
* struct mmc_slot - MMC slot functions
|
||||||
*
|
*
|
||||||
* @cd_irq: MMC/SD-card slot hotplug detection IRQ or -EINVAL
|
* @cd_irq: MMC/SD-card slot hotplug detection IRQ or -EINVAL
|
||||||
* @lock: protect the @handler_priv pointer
|
|
||||||
* @handler_priv: MMC/SD-card slot context
|
* @handler_priv: MMC/SD-card slot context
|
||||||
*
|
*
|
||||||
* Some MMC/SD host controllers implement slot-functions like card and
|
* Some MMC/SD host controllers implement slot-functions like card and
|
||||||
|
@ -176,7 +175,6 @@ struct mmc_async_req {
|
||||||
*/
|
*/
|
||||||
struct mmc_slot {
|
struct mmc_slot {
|
||||||
int cd_irq;
|
int cd_irq;
|
||||||
struct mutex lock;
|
|
||||||
void *handler_priv;
|
void *handler_priv;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -197,6 +195,7 @@ struct mmc_context_info {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct regulator;
|
struct regulator;
|
||||||
|
struct mmc_pwrseq;
|
||||||
|
|
||||||
struct mmc_supply {
|
struct mmc_supply {
|
||||||
struct regulator *vmmc; /* Card power supply */
|
struct regulator *vmmc; /* Card power supply */
|
||||||
|
@ -208,6 +207,7 @@ struct mmc_host {
|
||||||
struct device class_dev;
|
struct device class_dev;
|
||||||
int index;
|
int index;
|
||||||
const struct mmc_host_ops *ops;
|
const struct mmc_host_ops *ops;
|
||||||
|
struct mmc_pwrseq *pwrseq;
|
||||||
unsigned int f_min;
|
unsigned int f_min;
|
||||||
unsigned int f_max;
|
unsigned int f_max;
|
||||||
unsigned int f_init;
|
unsigned int f_init;
|
||||||
|
|
|
@ -53,11 +53,6 @@
|
||||||
#define MMC_SEND_TUNING_BLOCK 19 /* adtc R1 */
|
#define MMC_SEND_TUNING_BLOCK 19 /* adtc R1 */
|
||||||
#define MMC_SEND_TUNING_BLOCK_HS200 21 /* 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 */
|
/* class 3 */
|
||||||
#define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */
|
#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
|
#define EXT_CSD_BKOPS_LEVEL_2 0x2
|
||||||
|
|
||||||
|
/*
|
||||||
|
* BKOPS modes
|
||||||
|
*/
|
||||||
|
#define EXT_CSD_MANUAL_BKOPS_MASK 0x01
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MMC_SWITCH access modes
|
* MMC_SWITCH access modes
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -17,6 +17,11 @@
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/mmc/host.h>
|
#include <linux/mmc/host.h>
|
||||||
|
|
||||||
|
struct sdhci_host_next {
|
||||||
|
unsigned int sg_count;
|
||||||
|
s32 cookie;
|
||||||
|
};
|
||||||
|
|
||||||
struct sdhci_host {
|
struct sdhci_host {
|
||||||
/* Data set by hardware interface driver */
|
/* Data set by hardware interface driver */
|
||||||
const char *hw_name; /* Hardware bus name */
|
const char *hw_name; /* Hardware bus name */
|
||||||
|
@ -106,6 +111,10 @@ struct sdhci_host {
|
||||||
#define SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD (1<<10)
|
#define SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD (1<<10)
|
||||||
/* Capability register bit-63 indicates HS400 support */
|
/* Capability register bit-63 indicates HS400 support */
|
||||||
#define SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 (1<<11)
|
#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 */
|
int irq; /* Device IRQ */
|
||||||
void __iomem *ioaddr; /* Mapped address */
|
void __iomem *ioaddr; /* Mapped address */
|
||||||
|
@ -203,6 +212,7 @@ struct sdhci_host {
|
||||||
#define SDHCI_TUNING_MODE_1 0
|
#define SDHCI_TUNING_MODE_1 0
|
||||||
struct timer_list tuning_timer; /* Timer for tuning */
|
struct timer_list tuning_timer; /* Timer for tuning */
|
||||||
|
|
||||||
|
struct sdhci_host_next next_data;
|
||||||
unsigned long private[0] ____cacheline_aligned;
|
unsigned long private[0] ____cacheline_aligned;
|
||||||
};
|
};
|
||||||
#endif /* LINUX_MMC_SDHCI_H */
|
#endif /* LINUX_MMC_SDHCI_H */
|
||||||
|
|
|
@ -3,20 +3,10 @@
|
||||||
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
|
||||||
struct platform_device;
|
|
||||||
|
|
||||||
#define SH_MOBILE_SDHI_IRQ_CARD_DETECT "card_detect"
|
#define SH_MOBILE_SDHI_IRQ_CARD_DETECT "card_detect"
|
||||||
#define SH_MOBILE_SDHI_IRQ_SDCARD "sdcard"
|
#define SH_MOBILE_SDHI_IRQ_SDCARD "sdcard"
|
||||||
#define SH_MOBILE_SDHI_IRQ_SDIO "sdio"
|
#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 {
|
struct sh_mobile_sdhi_info {
|
||||||
int dma_slave_tx;
|
int dma_slave_tx;
|
||||||
int dma_slave_rx;
|
int dma_slave_rx;
|
||||||
|
@ -25,11 +15,6 @@ struct sh_mobile_sdhi_info {
|
||||||
unsigned long tmio_caps2;
|
unsigned long tmio_caps2;
|
||||||
u32 tmio_ocr_mask; /* available MMC voltages */
|
u32 tmio_ocr_mask; /* available MMC voltages */
|
||||||
unsigned int cd_gpio;
|
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 */
|
#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_get_ro(struct mmc_host *host);
|
||||||
int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio);
|
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_get_cd(struct mmc_host *host);
|
||||||
int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio,
|
int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio,
|
||||||
unsigned int debounce);
|
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,
|
int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
|
||||||
unsigned int idx, bool override_active_level,
|
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,
|
int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
|
||||||
unsigned int idx, bool override_active_level,
|
unsigned int idx, bool override_active_level,
|
||||||
unsigned int debounce, bool *gpio_invert);
|
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);
|
void mmc_gpiod_request_cd_irq(struct mmc_host *host);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -31,10 +31,6 @@ struct omap_mmc_platform_data {
|
||||||
void (*cleanup)(struct device *dev);
|
void (*cleanup)(struct device *dev);
|
||||||
void (*shutdown)(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 */
|
/* Return context loss count due to PM states changing */
|
||||||
int (*get_context_loss_count)(struct device *dev);
|
int (*get_context_loss_count)(struct device *dev);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue