mirror of
https://github.com/torvalds/linux.git
synced 2024-11-11 22:51:42 +00:00
MMC core:
- Consolidation and cleanups. - Some improvements regarding error handling. - Increase maximum amount of block devices. - Use correct OCR mask for SDIO when restoring power. - Fix prepared requests while doing BKOPS. - Convert to modern PM ops. - Add mmc_send_tuning() API and convert some hosts to use it. MMC host: - toshsd: New Toshiba PCI SD controller driver. - sdhci: 64-bit ADMA support. - sdhci: Some regulator fixes. - sdhci: HS400 support. - sdhci: Various fixes cleanups. - atmel-mci: Modernization and cleanups. - atmel-mci: Runtime PM support. - omap_hsmmc: Modernization and cleanups. - omap_hsmmc: Fix UHS card with DDR50 support. - dw_mmc: Support for ARM64 and Exynos 7 variant. - dw_mmc: Add support for IMG Pistachio variant. - dw_mmc: Various fixes and cleanups. - mvsdio: DMA fixes. - mxs-mmc: Modernization and cleanups. - mxcmmc: Various fixes. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJUhYTTAAoJEP4mhCVzWIwpRvUP/22/mNJkYg7IEPtzAnPF1YJW FKU9LdXLGQhrYR99A4grKJ8XMkqLn1FYQh+6j8rRKwrI0RFjmRyeVHsRKHBy1vV+ QjY7U2+GOAxoQopGq0HT+lECB6E7A3BQpJDu92HI9/1ambPmChh5at6qByscmpLc y4Bs/rNwClWmbDBCZ0hrGNE4iGr3pvnmz1fXE2KwhJA5Jzk70j/p8FTSMiAEdlv5 8R9Pu3l+eQbsxBH+mQ6F6F4ZcHSc4JjCjL168icEn2b9cuT1Qa7+GqEOktmwelEc 29gcLgeOuCi5Q5YqxmueC7jJVHQ0hVg46NOb0aivlbSrvhE2Xk5Crj8Ro7rlh7Q4 s4v6OseYO4zjIx7l27C/B0/iaqwCok1zeNe23voR8ne/6RYYaYK/4IfpZM0fo4u8 pUlTtY9aqY98BhynJCKFv29z2Ifbo4v8/8FkIWz4FKvCmq27ibXXB7rjx64l1/bR 4BQp8HzTD254RnT5FhNADG5lnj0EhZtiKli8/vVu+kfao7GAE9M/vd9NxJsQQw// LYM4EKTaaT8z7LgHFg2Cs5KMIXxuvgjTj9/SPbILzqyCvnjEZYbJxViH4dkB9IQC LoiF8PvKddDUH1Z9+brTg6qrf/C5j/+jHuAWKF61vtdQ6xTzlv/Y6YxKeE0ntSsm HAqzQTAPfwr5/Aq9RuBJ =ODSj -----END PGP SIGNATURE----- Merge tag 'mmc-v3.19-1' of git://git.linaro.org/people/ulf.hansson/mmc Pull MMC updates from Ulf Hansson: "MMC core: - Consolidation and cleanups. - Some improvements regarding error handling. - Increase maximum amount of block devices. - Use correct OCR mask for SDIO when restoring power. - Fix prepared requests while doing BKOPS. - Convert to modern PM ops. - Add mmc_send_tuning() API and convert some hosts to use it. MMC host: - toshsd: New Toshiba PCI SD controller driver. - sdhci: 64-bit ADMA support. - sdhci: Some regulator fixes. - sdhci: HS400 support. - sdhci: Various fixes cleanups. - atmel-mci: Modernization and cleanups. - atmel-mci: Runtime PM support. - omap_hsmmc: Modernization and cleanups. - omap_hsmmc: Fix UHS card with DDR50 support. - dw_mmc: Support for ARM64 and Exynos 7 variant. - dw_mmc: Add support for IMG Pistachio variant. - dw_mmc: Various fixes and cleanups. - mvsdio: DMA fixes. - mxs-mmc: Modernization and cleanups. - mxcmmc: Various fixes" * tag 'mmc-v3.19-1' of git://git.linaro.org/people/ulf.hansson/mmc: (126 commits) mmc: sdhci-msm: Convert to mmc_send_tuning() mmc: sdhci-esdhc-imx: Convert to mmc_send_tuning() mmc: core: Let mmc_send_tuning() to take struct mmc_host* as parameter mmc: queue: Improve error handling during allocation of bounce buffers mmc: sdhci-acpi: Add two host capabilities for Intel mmc: sdhci-pci: Add two host capabilities for BYT mmc: sdhci-acpi: Add SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC mmc: sdhci-pci: Add SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC to BYT mmc: atmel-mci: use probe deferring if dma controller is not ready yet mmc: atmel-mci: stop using specific initcall mmc: atmel-mci: remove __init/__exit attributes mmc: atmel-mci: remove useless DMA stuff for non-dt devices mmc: omap_hsmmc: Fix UHS card with DDR50 support mmc: core: add core-level function for sending tuning commands mmc: core: hold SD Clock before CMD11 during Signal mmc: mxs-mmc: Check for clk_prepare_enable() error mmc: mxs-mmc: Propagate the real error mmc: mxs-mmc: No need to do NULL check on 'iores' mmc: dw_mmc: Add support for IMG Pistachio mmc: mxs-mmc: Simplify PM hooks ...
This commit is contained in:
commit
f2fb38049c
@ -18,6 +18,10 @@ Required Properties:
|
|||||||
specific extensions.
|
specific extensions.
|
||||||
- "samsung,exynos5420-dw-mshc": for controllers with Samsung Exynos5420
|
- "samsung,exynos5420-dw-mshc": for controllers with Samsung Exynos5420
|
||||||
specific extensions.
|
specific extensions.
|
||||||
|
- "samsung,exynos7-dw-mshc": for controllers with Samsung Exynos7
|
||||||
|
specific extensions.
|
||||||
|
- "samsung,exynos7-dw-mshc-smu": for controllers with Samsung Exynos7
|
||||||
|
specific extensions having an SMU.
|
||||||
|
|
||||||
* samsung,dw-mshc-ciu-div: Specifies the divider value for the card interface
|
* samsung,dw-mshc-ciu-div: Specifies the divider value for the card interface
|
||||||
unit (ciu) clock. This property is applicable only for Exynos5 SoC's and
|
unit (ciu) clock. This property is applicable only for Exynos5 SoC's and
|
||||||
|
29
Documentation/devicetree/bindings/mmc/img-dw-mshc.txt
Normal file
29
Documentation/devicetree/bindings/mmc/img-dw-mshc.txt
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
* Imagination specific extensions to the Synopsys Designware Mobile Storage
|
||||||
|
Host Controller
|
||||||
|
|
||||||
|
The Synopsys designware mobile storage host controller is used to interface
|
||||||
|
a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
|
||||||
|
differences between the core Synopsys dw mshc controller properties described
|
||||||
|
by synopsys-dw-mshc.txt and the properties used by the Imagination specific
|
||||||
|
extensions to the Synopsys Designware Mobile Storage Host Controller.
|
||||||
|
|
||||||
|
Required Properties:
|
||||||
|
|
||||||
|
* compatible: should be
|
||||||
|
- "img,pistachio-dw-mshc": for Pistachio SoCs
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
mmc@18142000 {
|
||||||
|
compatible = "img,pistachio-dw-mshc";
|
||||||
|
reg = <0x18142000 0x400>;
|
||||||
|
interrupts = <GIC_SHARED 39 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
|
||||||
|
clocks = <&system_clk>, <&sdhost_clk>;
|
||||||
|
clock-names = "biu", "ciu";
|
||||||
|
|
||||||
|
fifo-depth = <0x20>;
|
||||||
|
bus-width = <4>;
|
||||||
|
num-slots = <1>;
|
||||||
|
disable-wp;
|
||||||
|
};
|
@ -12,6 +12,10 @@ Required properties:
|
|||||||
* for "marvell,armada-380-sdhci", two register areas. The first one
|
* for "marvell,armada-380-sdhci", two register areas. The first one
|
||||||
for the SDHCI registers themselves, and the second one for the
|
for the SDHCI registers themselves, and the second one for the
|
||||||
AXI/Mbus bridge registers of the SDHCI unit.
|
AXI/Mbus bridge registers of the SDHCI unit.
|
||||||
|
- clocks: Array of clocks required for SDHCI; requires at least one for
|
||||||
|
I/O clock.
|
||||||
|
- clock-names: Array of names corresponding to clocks property; shall be
|
||||||
|
"io" for I/O clock and "core" for optional core clock.
|
||||||
|
|
||||||
Optional properties:
|
Optional properties:
|
||||||
- mrvl,clk-delay-cycles: Specify a number of cycles to delay for tuning.
|
- mrvl,clk-delay-cycles: Specify a number of cycles to delay for tuning.
|
||||||
@ -23,6 +27,8 @@ sdhci@d4280800 {
|
|||||||
reg = <0xd4280800 0x800>;
|
reg = <0xd4280800 0x800>;
|
||||||
bus-width = <8>;
|
bus-width = <8>;
|
||||||
interrupts = <27>;
|
interrupts = <27>;
|
||||||
|
clocks = <&chip CLKID_SDIO1XIN>, <&chip CLKID_SDIO1>;
|
||||||
|
clock-names = "io", "core";
|
||||||
non-removable;
|
non-removable;
|
||||||
mrvl,clk-delay-cycles = <31>;
|
mrvl,clk-delay-cycles = <31>;
|
||||||
};
|
};
|
||||||
@ -32,5 +38,6 @@ sdhci@d8000 {
|
|||||||
reg = <0xd8000 0x1000>, <0xdc000 0x100>;
|
reg = <0xd8000 0x1000>, <0xdc000 0x100>;
|
||||||
interrupts = <0 25 0x4>;
|
interrupts = <0 25 0x4>;
|
||||||
clocks = <&gateclk 17>;
|
clocks = <&gateclk 17>;
|
||||||
|
clock-names = "io";
|
||||||
mrvl,clk-delay-cycles = <0x1F>;
|
mrvl,clk-delay-cycles = <0x1F>;
|
||||||
};
|
};
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include <linux/i2c-gpio.h>
|
#include <linux/i2c-gpio.h>
|
||||||
#include <linux/atmel-mci.h>
|
#include <linux/atmel-mci.h>
|
||||||
#include <linux/platform_data/crypto-atmel.h>
|
#include <linux/platform_data/crypto-atmel.h>
|
||||||
|
#include <linux/platform_data/mmc-atmel-mci.h>
|
||||||
|
|
||||||
#include <linux/platform_data/at91_adc.h>
|
#include <linux/platform_data/at91_adc.h>
|
||||||
|
|
||||||
@ -30,7 +31,6 @@
|
|||||||
#include <mach/at91_matrix.h>
|
#include <mach/at91_matrix.h>
|
||||||
#include <mach/at91sam9_smc.h>
|
#include <mach/at91sam9_smc.h>
|
||||||
#include <linux/platform_data/dma-atmel.h>
|
#include <linux/platform_data/dma-atmel.h>
|
||||||
#include <mach/atmel-mci.h>
|
|
||||||
#include <mach/hardware.h>
|
#include <mach/hardware.h>
|
||||||
|
|
||||||
#include <media/atmel-isi.h>
|
#include <media/atmel-isi.h>
|
||||||
|
@ -21,8 +21,10 @@
|
|||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/usb/musb.h>
|
#include <linux/usb/musb.h>
|
||||||
|
#include <linux/mmc/host.h>
|
||||||
#include <linux/platform_data/spi-omap2-mcspi.h>
|
#include <linux/platform_data/spi-omap2-mcspi.h>
|
||||||
#include <linux/platform_data/mtd-onenand-omap2.h>
|
#include <linux/platform_data/mtd-onenand-omap2.h>
|
||||||
|
#include <linux/platform_data/mmc-omap.h>
|
||||||
#include <linux/mfd/menelaus.h>
|
#include <linux/mfd/menelaus.h>
|
||||||
#include <sound/tlv320aic3x.h>
|
#include <sound/tlv320aic3x.h>
|
||||||
|
|
||||||
|
@ -484,7 +484,7 @@ static struct omap_mux_partition *partition;
|
|||||||
* Current flows to eMMC when eMMC is off and the data lines are pulled up,
|
* Current flows to eMMC when eMMC is off and the data lines are pulled up,
|
||||||
* so pull them down. N.B. we pull 8 lines because we are using 8 lines.
|
* so pull them down. N.B. we pull 8 lines because we are using 8 lines.
|
||||||
*/
|
*/
|
||||||
static void rx51_mmc2_remux(struct device *dev, int slot, int power_on)
|
static void rx51_mmc2_remux(struct device *dev, int power_on)
|
||||||
{
|
{
|
||||||
if (power_on)
|
if (power_on)
|
||||||
omap_mux_write_array(partition, rx51_mmc2_on_mux);
|
omap_mux_write_array(partition, rx51_mmc2_on_mux);
|
||||||
@ -500,7 +500,6 @@ static struct omap2_hsmmc_info mmc[] __initdata = {
|
|||||||
.cover_only = true,
|
.cover_only = true,
|
||||||
.gpio_cd = 160,
|
.gpio_cd = 160,
|
||||||
.gpio_wp = -EINVAL,
|
.gpio_wp = -EINVAL,
|
||||||
.power_saving = true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.name = "internal",
|
.name = "internal",
|
||||||
@ -510,7 +509,6 @@ static struct omap2_hsmmc_info mmc[] __initdata = {
|
|||||||
.gpio_cd = -EINVAL,
|
.gpio_cd = -EINVAL,
|
||||||
.gpio_wp = -EINVAL,
|
.gpio_wp = -EINVAL,
|
||||||
.nonremovable = true,
|
.nonremovable = true,
|
||||||
.power_saving = true,
|
|
||||||
.remux = rx51_mmc2_remux,
|
.remux = rx51_mmc2_remux,
|
||||||
},
|
},
|
||||||
{} /* Terminator */
|
{} /* Terminator */
|
||||||
|
@ -14,14 +14,15 @@
|
|||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
|
#include <linux/mmc/host.h>
|
||||||
#include <linux/platform_data/gpio-omap.h>
|
#include <linux/platform_data/gpio-omap.h>
|
||||||
|
#include <linux/platform_data/hsmmc-omap.h>
|
||||||
|
|
||||||
#include "soc.h"
|
#include "soc.h"
|
||||||
#include "omap_device.h"
|
#include "omap_device.h"
|
||||||
#include "omap-pm.h"
|
#include "omap-pm.h"
|
||||||
|
|
||||||
#include "mux.h"
|
#include "mux.h"
|
||||||
#include "mmc.h"
|
|
||||||
#include "hsmmc.h"
|
#include "hsmmc.h"
|
||||||
#include "control.h"
|
#include "control.h"
|
||||||
|
|
||||||
@ -32,25 +33,14 @@ static u16 control_devconf1_offset;
|
|||||||
|
|
||||||
#define HSMMC_NAME_LEN 9
|
#define HSMMC_NAME_LEN 9
|
||||||
|
|
||||||
#if defined(CONFIG_ARCH_OMAP3) && defined(CONFIG_PM)
|
static void omap_hsmmc1_before_set_reg(struct device *dev,
|
||||||
|
int power_on, int vdd)
|
||||||
static int hsmmc_get_context_loss(struct device *dev)
|
|
||||||
{
|
|
||||||
return omap_pm_get_dev_context_loss_count(dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
#define hsmmc_get_context_loss NULL
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void omap_hsmmc1_before_set_reg(struct device *dev, int slot,
|
|
||||||
int power_on, int vdd)
|
|
||||||
{
|
{
|
||||||
u32 reg, prog_io;
|
u32 reg, prog_io;
|
||||||
struct omap_mmc_platform_data *mmc = dev->platform_data;
|
struct omap_hsmmc_platform_data *mmc = dev->platform_data;
|
||||||
|
|
||||||
if (mmc->slots[0].remux)
|
if (mmc->remux)
|
||||||
mmc->slots[0].remux(dev, slot, power_on);
|
mmc->remux(dev, power_on);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Assume we power both OMAP VMMC1 (for CMD, CLK, DAT0..3) and the
|
* Assume we power both OMAP VMMC1 (for CMD, CLK, DAT0..3) and the
|
||||||
@ -72,7 +62,7 @@ static void omap_hsmmc1_before_set_reg(struct device *dev, int slot,
|
|||||||
omap_ctrl_writel(reg, OMAP243X_CONTROL_DEVCONF1);
|
omap_ctrl_writel(reg, OMAP243X_CONTROL_DEVCONF1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mmc->slots[0].internal_clock) {
|
if (mmc->internal_clock) {
|
||||||
reg = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0);
|
reg = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0);
|
||||||
reg |= OMAP2_MMCSDIO1ADPCLKISEL;
|
reg |= OMAP2_MMCSDIO1ADPCLKISEL;
|
||||||
omap_ctrl_writel(reg, OMAP2_CONTROL_DEVCONF0);
|
omap_ctrl_writel(reg, OMAP2_CONTROL_DEVCONF0);
|
||||||
@ -96,8 +86,7 @@ static void omap_hsmmc1_before_set_reg(struct device *dev, int slot,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void omap_hsmmc1_after_set_reg(struct device *dev, int slot,
|
static void omap_hsmmc1_after_set_reg(struct device *dev, int power_on, int vdd)
|
||||||
int power_on, int vdd)
|
|
||||||
{
|
{
|
||||||
u32 reg;
|
u32 reg;
|
||||||
|
|
||||||
@ -120,34 +109,32 @@ static void omap_hsmmc1_after_set_reg(struct device *dev, int slot,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hsmmc2_select_input_clk_src(struct omap_mmc_platform_data *mmc)
|
static void hsmmc2_select_input_clk_src(struct omap_hsmmc_platform_data *mmc)
|
||||||
{
|
{
|
||||||
u32 reg;
|
u32 reg;
|
||||||
|
|
||||||
reg = omap_ctrl_readl(control_devconf1_offset);
|
reg = omap_ctrl_readl(control_devconf1_offset);
|
||||||
if (mmc->slots[0].internal_clock)
|
if (mmc->internal_clock)
|
||||||
reg |= OMAP2_MMCSDIO2ADPCLKISEL;
|
reg |= OMAP2_MMCSDIO2ADPCLKISEL;
|
||||||
else
|
else
|
||||||
reg &= ~OMAP2_MMCSDIO2ADPCLKISEL;
|
reg &= ~OMAP2_MMCSDIO2ADPCLKISEL;
|
||||||
omap_ctrl_writel(reg, control_devconf1_offset);
|
omap_ctrl_writel(reg, control_devconf1_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hsmmc2_before_set_reg(struct device *dev, int slot,
|
static void hsmmc2_before_set_reg(struct device *dev, int power_on, int vdd)
|
||||||
int power_on, int vdd)
|
|
||||||
{
|
{
|
||||||
struct omap_mmc_platform_data *mmc = dev->platform_data;
|
struct omap_hsmmc_platform_data *mmc = dev->platform_data;
|
||||||
|
|
||||||
if (mmc->slots[0].remux)
|
if (mmc->remux)
|
||||||
mmc->slots[0].remux(dev, slot, power_on);
|
mmc->remux(dev, power_on);
|
||||||
|
|
||||||
if (power_on)
|
if (power_on)
|
||||||
hsmmc2_select_input_clk_src(mmc);
|
hsmmc2_select_input_clk_src(mmc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int am35x_hsmmc2_set_power(struct device *dev, int slot,
|
static int am35x_hsmmc2_set_power(struct device *dev, int power_on, int vdd)
|
||||||
int power_on, int vdd)
|
|
||||||
{
|
{
|
||||||
struct omap_mmc_platform_data *mmc = dev->platform_data;
|
struct omap_hsmmc_platform_data *mmc = dev->platform_data;
|
||||||
|
|
||||||
if (power_on)
|
if (power_on)
|
||||||
hsmmc2_select_input_clk_src(mmc);
|
hsmmc2_select_input_clk_src(mmc);
|
||||||
@ -155,23 +142,22 @@ static int am35x_hsmmc2_set_power(struct device *dev, int slot,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nop_mmc_set_power(struct device *dev, int slot, int power_on,
|
static int nop_mmc_set_power(struct device *dev, int power_on, int vdd)
|
||||||
int vdd)
|
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void omap_hsmmc_mux(struct omap_mmc_platform_data *mmc_controller,
|
static inline void omap_hsmmc_mux(struct omap_hsmmc_platform_data
|
||||||
int controller_nr)
|
*mmc_controller, int controller_nr)
|
||||||
{
|
{
|
||||||
if (gpio_is_valid(mmc_controller->slots[0].switch_pin) &&
|
if (gpio_is_valid(mmc_controller->switch_pin) &&
|
||||||
(mmc_controller->slots[0].switch_pin < OMAP_MAX_GPIO_LINES))
|
(mmc_controller->switch_pin < OMAP_MAX_GPIO_LINES))
|
||||||
omap_mux_init_gpio(mmc_controller->slots[0].switch_pin,
|
omap_mux_init_gpio(mmc_controller->switch_pin,
|
||||||
OMAP_PIN_INPUT_PULLUP);
|
OMAP_PIN_INPUT_PULLUP);
|
||||||
if (gpio_is_valid(mmc_controller->slots[0].gpio_wp) &&
|
if (gpio_is_valid(mmc_controller->gpio_wp) &&
|
||||||
(mmc_controller->slots[0].gpio_wp < OMAP_MAX_GPIO_LINES))
|
(mmc_controller->gpio_wp < OMAP_MAX_GPIO_LINES))
|
||||||
omap_mux_init_gpio(mmc_controller->slots[0].gpio_wp,
|
omap_mux_init_gpio(mmc_controller->gpio_wp,
|
||||||
OMAP_PIN_INPUT_PULLUP);
|
OMAP_PIN_INPUT_PULLUP);
|
||||||
if (cpu_is_omap34xx()) {
|
if (cpu_is_omap34xx()) {
|
||||||
if (controller_nr == 0) {
|
if (controller_nr == 0) {
|
||||||
omap_mux_init_signal("sdmmc1_clk",
|
omap_mux_init_signal("sdmmc1_clk",
|
||||||
@ -180,7 +166,7 @@ static inline void omap_hsmmc_mux(struct omap_mmc_platform_data *mmc_controller,
|
|||||||
OMAP_PIN_INPUT_PULLUP);
|
OMAP_PIN_INPUT_PULLUP);
|
||||||
omap_mux_init_signal("sdmmc1_dat0",
|
omap_mux_init_signal("sdmmc1_dat0",
|
||||||
OMAP_PIN_INPUT_PULLUP);
|
OMAP_PIN_INPUT_PULLUP);
|
||||||
if (mmc_controller->slots[0].caps &
|
if (mmc_controller->caps &
|
||||||
(MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)) {
|
(MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)) {
|
||||||
omap_mux_init_signal("sdmmc1_dat1",
|
omap_mux_init_signal("sdmmc1_dat1",
|
||||||
OMAP_PIN_INPUT_PULLUP);
|
OMAP_PIN_INPUT_PULLUP);
|
||||||
@ -189,7 +175,7 @@ static inline void omap_hsmmc_mux(struct omap_mmc_platform_data *mmc_controller,
|
|||||||
omap_mux_init_signal("sdmmc1_dat3",
|
omap_mux_init_signal("sdmmc1_dat3",
|
||||||
OMAP_PIN_INPUT_PULLUP);
|
OMAP_PIN_INPUT_PULLUP);
|
||||||
}
|
}
|
||||||
if (mmc_controller->slots[0].caps &
|
if (mmc_controller->caps &
|
||||||
MMC_CAP_8_BIT_DATA) {
|
MMC_CAP_8_BIT_DATA) {
|
||||||
omap_mux_init_signal("sdmmc1_dat4",
|
omap_mux_init_signal("sdmmc1_dat4",
|
||||||
OMAP_PIN_INPUT_PULLUP);
|
OMAP_PIN_INPUT_PULLUP);
|
||||||
@ -214,7 +200,7 @@ static inline void omap_hsmmc_mux(struct omap_mmc_platform_data *mmc_controller,
|
|||||||
* For 8 wire configurations, Lines DAT4, 5, 6 and 7
|
* For 8 wire configurations, Lines DAT4, 5, 6 and 7
|
||||||
* need to be muxed in the board-*.c files
|
* need to be muxed in the board-*.c files
|
||||||
*/
|
*/
|
||||||
if (mmc_controller->slots[0].caps &
|
if (mmc_controller->caps &
|
||||||
(MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)) {
|
(MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)) {
|
||||||
omap_mux_init_signal("sdmmc2_dat1",
|
omap_mux_init_signal("sdmmc2_dat1",
|
||||||
OMAP_PIN_INPUT_PULLUP);
|
OMAP_PIN_INPUT_PULLUP);
|
||||||
@ -223,7 +209,7 @@ static inline void omap_hsmmc_mux(struct omap_mmc_platform_data *mmc_controller,
|
|||||||
omap_mux_init_signal("sdmmc2_dat3",
|
omap_mux_init_signal("sdmmc2_dat3",
|
||||||
OMAP_PIN_INPUT_PULLUP);
|
OMAP_PIN_INPUT_PULLUP);
|
||||||
}
|
}
|
||||||
if (mmc_controller->slots[0].caps &
|
if (mmc_controller->caps &
|
||||||
MMC_CAP_8_BIT_DATA) {
|
MMC_CAP_8_BIT_DATA) {
|
||||||
omap_mux_init_signal("sdmmc2_dat4.sdmmc2_dat4",
|
omap_mux_init_signal("sdmmc2_dat4.sdmmc2_dat4",
|
||||||
OMAP_PIN_INPUT_PULLUP);
|
OMAP_PIN_INPUT_PULLUP);
|
||||||
@ -243,7 +229,7 @@ static inline void omap_hsmmc_mux(struct omap_mmc_platform_data *mmc_controller,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int __init omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c,
|
static int __init omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c,
|
||||||
struct omap_mmc_platform_data *mmc)
|
struct omap_hsmmc_platform_data *mmc)
|
||||||
{
|
{
|
||||||
char *hc_name;
|
char *hc_name;
|
||||||
|
|
||||||
@ -259,38 +245,22 @@ static int __init omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c,
|
|||||||
else
|
else
|
||||||
snprintf(hc_name, (HSMMC_NAME_LEN + 1), "mmc%islot%i",
|
snprintf(hc_name, (HSMMC_NAME_LEN + 1), "mmc%islot%i",
|
||||||
c->mmc, 1);
|
c->mmc, 1);
|
||||||
mmc->slots[0].name = hc_name;
|
mmc->name = hc_name;
|
||||||
mmc->nr_slots = 1;
|
mmc->caps = c->caps;
|
||||||
mmc->slots[0].caps = c->caps;
|
mmc->internal_clock = !c->ext_clock;
|
||||||
mmc->slots[0].pm_caps = c->pm_caps;
|
|
||||||
mmc->slots[0].internal_clock = !c->ext_clock;
|
|
||||||
mmc->max_freq = c->max_freq;
|
|
||||||
mmc->reg_offset = 0;
|
mmc->reg_offset = 0;
|
||||||
mmc->get_context_loss_count = hsmmc_get_context_loss;
|
|
||||||
|
|
||||||
mmc->slots[0].switch_pin = c->gpio_cd;
|
mmc->switch_pin = c->gpio_cd;
|
||||||
mmc->slots[0].gpio_wp = c->gpio_wp;
|
mmc->gpio_wp = c->gpio_wp;
|
||||||
|
|
||||||
mmc->slots[0].remux = c->remux;
|
mmc->remux = c->remux;
|
||||||
mmc->slots[0].init_card = c->init_card;
|
mmc->init_card = c->init_card;
|
||||||
|
|
||||||
if (c->cover_only)
|
if (c->cover_only)
|
||||||
mmc->slots[0].cover = 1;
|
mmc->cover = 1;
|
||||||
|
|
||||||
if (c->nonremovable)
|
if (c->nonremovable)
|
||||||
mmc->slots[0].nonremovable = 1;
|
mmc->nonremovable = 1;
|
||||||
|
|
||||||
if (c->power_saving)
|
|
||||||
mmc->slots[0].power_saving = 1;
|
|
||||||
|
|
||||||
if (c->no_off)
|
|
||||||
mmc->slots[0].no_off = 1;
|
|
||||||
|
|
||||||
if (c->no_off_init)
|
|
||||||
mmc->slots[0].no_regulator_off_init = c->no_off_init;
|
|
||||||
|
|
||||||
if (c->vcc_aux_disable_is_sleep)
|
|
||||||
mmc->slots[0].vcc_aux_disable_is_sleep = 1;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* NOTE: MMC slots should have a Vcc regulator set up.
|
* NOTE: MMC slots should have a Vcc regulator set up.
|
||||||
@ -300,42 +270,42 @@ static int __init omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c,
|
|||||||
* temporary HACK: ocr_mask instead of fixed supply
|
* temporary HACK: ocr_mask instead of fixed supply
|
||||||
*/
|
*/
|
||||||
if (soc_is_am35xx())
|
if (soc_is_am35xx())
|
||||||
mmc->slots[0].ocr_mask = MMC_VDD_165_195 |
|
mmc->ocr_mask = MMC_VDD_165_195 |
|
||||||
MMC_VDD_26_27 |
|
MMC_VDD_26_27 |
|
||||||
MMC_VDD_27_28 |
|
MMC_VDD_27_28 |
|
||||||
MMC_VDD_29_30 |
|
MMC_VDD_29_30 |
|
||||||
MMC_VDD_30_31 |
|
MMC_VDD_30_31 |
|
||||||
MMC_VDD_31_32;
|
MMC_VDD_31_32;
|
||||||
else
|
else
|
||||||
mmc->slots[0].ocr_mask = c->ocr_mask;
|
mmc->ocr_mask = c->ocr_mask;
|
||||||
|
|
||||||
if (!soc_is_am35xx())
|
if (!soc_is_am35xx())
|
||||||
mmc->slots[0].features |= HSMMC_HAS_PBIAS;
|
mmc->features |= HSMMC_HAS_PBIAS;
|
||||||
|
|
||||||
switch (c->mmc) {
|
switch (c->mmc) {
|
||||||
case 1:
|
case 1:
|
||||||
if (mmc->slots[0].features & HSMMC_HAS_PBIAS) {
|
if (mmc->features & HSMMC_HAS_PBIAS) {
|
||||||
/* on-chip level shifting via PBIAS0/PBIAS1 */
|
/* on-chip level shifting via PBIAS0/PBIAS1 */
|
||||||
mmc->slots[0].before_set_reg =
|
mmc->before_set_reg =
|
||||||
omap_hsmmc1_before_set_reg;
|
omap_hsmmc1_before_set_reg;
|
||||||
mmc->slots[0].after_set_reg =
|
mmc->after_set_reg =
|
||||||
omap_hsmmc1_after_set_reg;
|
omap_hsmmc1_after_set_reg;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (soc_is_am35xx())
|
if (soc_is_am35xx())
|
||||||
mmc->slots[0].set_power = nop_mmc_set_power;
|
mmc->set_power = nop_mmc_set_power;
|
||||||
|
|
||||||
/* OMAP3630 HSMMC1 supports only 4-bit */
|
/* OMAP3630 HSMMC1 supports only 4-bit */
|
||||||
if (cpu_is_omap3630() &&
|
if (cpu_is_omap3630() &&
|
||||||
(c->caps & MMC_CAP_8_BIT_DATA)) {
|
(c->caps & MMC_CAP_8_BIT_DATA)) {
|
||||||
c->caps &= ~MMC_CAP_8_BIT_DATA;
|
c->caps &= ~MMC_CAP_8_BIT_DATA;
|
||||||
c->caps |= MMC_CAP_4_BIT_DATA;
|
c->caps |= MMC_CAP_4_BIT_DATA;
|
||||||
mmc->slots[0].caps = c->caps;
|
mmc->caps = c->caps;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
if (soc_is_am35xx())
|
if (soc_is_am35xx())
|
||||||
mmc->slots[0].set_power = am35x_hsmmc2_set_power;
|
mmc->set_power = am35x_hsmmc2_set_power;
|
||||||
|
|
||||||
if (c->ext_clock)
|
if (c->ext_clock)
|
||||||
c->transceiver = 1;
|
c->transceiver = 1;
|
||||||
@ -343,17 +313,17 @@ static int __init omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c,
|
|||||||
c->caps &= ~MMC_CAP_8_BIT_DATA;
|
c->caps &= ~MMC_CAP_8_BIT_DATA;
|
||||||
c->caps |= MMC_CAP_4_BIT_DATA;
|
c->caps |= MMC_CAP_4_BIT_DATA;
|
||||||
}
|
}
|
||||||
if (mmc->slots[0].features & HSMMC_HAS_PBIAS) {
|
if (mmc->features & HSMMC_HAS_PBIAS) {
|
||||||
/* off-chip level shifting, or none */
|
/* off-chip level shifting, or none */
|
||||||
mmc->slots[0].before_set_reg = hsmmc2_before_set_reg;
|
mmc->before_set_reg = hsmmc2_before_set_reg;
|
||||||
mmc->slots[0].after_set_reg = NULL;
|
mmc->after_set_reg = NULL;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
case 4:
|
case 4:
|
||||||
case 5:
|
case 5:
|
||||||
mmc->slots[0].before_set_reg = NULL;
|
mmc->before_set_reg = NULL;
|
||||||
mmc->slots[0].after_set_reg = NULL;
|
mmc->after_set_reg = NULL;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
pr_err("MMC%d configuration not supported!\n", c->mmc);
|
pr_err("MMC%d configuration not supported!\n", c->mmc);
|
||||||
@ -368,7 +338,7 @@ static int omap_hsmmc_done;
|
|||||||
void omap_hsmmc_late_init(struct omap2_hsmmc_info *c)
|
void omap_hsmmc_late_init(struct omap2_hsmmc_info *c)
|
||||||
{
|
{
|
||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
struct omap_mmc_platform_data *mmc_pdata;
|
struct omap_hsmmc_platform_data *mmc_pdata;
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
if (omap_hsmmc_done != 1)
|
if (omap_hsmmc_done != 1)
|
||||||
@ -388,8 +358,8 @@ void omap_hsmmc_late_init(struct omap2_hsmmc_info *c)
|
|||||||
if (!mmc_pdata)
|
if (!mmc_pdata)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
mmc_pdata->slots[0].switch_pin = c->gpio_cd;
|
mmc_pdata->switch_pin = c->gpio_cd;
|
||||||
mmc_pdata->slots[0].gpio_wp = c->gpio_wp;
|
mmc_pdata->gpio_wp = c->gpio_wp;
|
||||||
|
|
||||||
res = omap_device_register(pdev);
|
res = omap_device_register(pdev);
|
||||||
if (res)
|
if (res)
|
||||||
@ -408,12 +378,12 @@ static void __init omap_hsmmc_init_one(struct omap2_hsmmc_info *hsmmcinfo,
|
|||||||
struct omap_device *od;
|
struct omap_device *od;
|
||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
char oh_name[MAX_OMAP_MMC_HWMOD_NAME_LEN];
|
char oh_name[MAX_OMAP_MMC_HWMOD_NAME_LEN];
|
||||||
struct omap_mmc_platform_data *mmc_data;
|
struct omap_hsmmc_platform_data *mmc_data;
|
||||||
struct omap_mmc_dev_attr *mmc_dev_attr;
|
struct omap_hsmmc_dev_attr *mmc_dev_attr;
|
||||||
char *name;
|
char *name;
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
mmc_data = kzalloc(sizeof(struct omap_mmc_platform_data), GFP_KERNEL);
|
mmc_data = kzalloc(sizeof(*mmc_data), GFP_KERNEL);
|
||||||
if (!mmc_data) {
|
if (!mmc_data) {
|
||||||
pr_err("Cannot allocate memory for mmc device!\n");
|
pr_err("Cannot allocate memory for mmc device!\n");
|
||||||
return;
|
return;
|
||||||
@ -463,7 +433,7 @@ static void __init omap_hsmmc_init_one(struct omap2_hsmmc_info *hsmmcinfo,
|
|||||||
}
|
}
|
||||||
|
|
||||||
res = platform_device_add_data(pdev, mmc_data,
|
res = platform_device_add_data(pdev, mmc_data,
|
||||||
sizeof(struct omap_mmc_platform_data));
|
sizeof(struct omap_hsmmc_platform_data));
|
||||||
if (res) {
|
if (res) {
|
||||||
pr_err("Could not add pdata for %s\n", name);
|
pr_err("Could not add pdata for %s\n", name);
|
||||||
goto put_pdev;
|
goto put_pdev;
|
||||||
@ -489,7 +459,7 @@ put_pdev:
|
|||||||
platform_device_put(pdev);
|
platform_device_put(pdev);
|
||||||
|
|
||||||
free_name:
|
free_name:
|
||||||
kfree(mmc_data->slots[0].name);
|
kfree(mmc_data->name);
|
||||||
|
|
||||||
free_mmc:
|
free_mmc:
|
||||||
kfree(mmc_data);
|
kfree(mmc_data);
|
||||||
|
@ -12,25 +12,18 @@ struct omap2_hsmmc_info {
|
|||||||
u8 mmc; /* controller 1/2/3 */
|
u8 mmc; /* controller 1/2/3 */
|
||||||
u32 caps; /* 4/8 wires and any additional host
|
u32 caps; /* 4/8 wires and any additional host
|
||||||
* capabilities OR'd (ref. linux/mmc/host.h) */
|
* capabilities OR'd (ref. linux/mmc/host.h) */
|
||||||
u32 pm_caps; /* PM capabilities */
|
|
||||||
bool transceiver; /* MMC-2 option */
|
bool transceiver; /* MMC-2 option */
|
||||||
bool ext_clock; /* use external pin for input clock */
|
bool ext_clock; /* use external pin for input clock */
|
||||||
bool cover_only; /* No card detect - just cover switch */
|
bool cover_only; /* No card detect - just cover switch */
|
||||||
bool nonremovable; /* Nonremovable e.g. eMMC */
|
bool nonremovable; /* Nonremovable e.g. eMMC */
|
||||||
bool power_saving; /* Try to sleep or power off when possible */
|
|
||||||
bool no_off; /* power_saving and power is not to go off */
|
|
||||||
bool no_off_init; /* no power off when not in MMC sleep state */
|
|
||||||
bool vcc_aux_disable_is_sleep; /* Regulator off remapped to sleep */
|
|
||||||
bool deferred; /* mmc needs a deferred probe */
|
bool deferred; /* mmc needs a deferred probe */
|
||||||
int gpio_cd; /* or -EINVAL */
|
int gpio_cd; /* or -EINVAL */
|
||||||
int gpio_wp; /* or -EINVAL */
|
int gpio_wp; /* or -EINVAL */
|
||||||
char *name; /* or NULL for default */
|
char *name; /* or NULL for default */
|
||||||
struct platform_device *pdev; /* mmc controller instance */
|
struct platform_device *pdev; /* mmc controller instance */
|
||||||
int ocr_mask; /* temporary HACK */
|
int ocr_mask; /* temporary HACK */
|
||||||
int max_freq; /* maximum clock, if constrained by external
|
|
||||||
* circuitry, or 0 for default */
|
|
||||||
/* Remux (pad configuration) when powering on/off */
|
/* Remux (pad configuration) when powering on/off */
|
||||||
void (*remux)(struct device *dev, int slot, int power_on);
|
void (*remux)(struct device *dev, int power_on);
|
||||||
/* init some special card */
|
/* init some special card */
|
||||||
void (*init_card)(struct mmc_card *card);
|
void (*init_card)(struct mmc_card *card);
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
#include <linux/mmc/host.h>
|
|
||||||
#include <linux/platform_data/mmc-omap.h>
|
|
||||||
|
|
||||||
#define OMAP24XX_NR_MMC 2
|
#define OMAP24XX_NR_MMC 2
|
||||||
#define OMAP2420_MMC_SIZE OMAP1_MMC_SIZE
|
#define OMAP2420_MMC_SIZE OMAP1_MMC_SIZE
|
||||||
@ -7,14 +5,6 @@
|
|||||||
|
|
||||||
#define OMAP4_MMC_REG_OFFSET 0x100
|
#define OMAP4_MMC_REG_OFFSET 0x100
|
||||||
|
|
||||||
#if defined(CONFIG_MMC_OMAP) || defined(CONFIG_MMC_OMAP_MODULE)
|
|
||||||
void omap242x_init_mmc(struct omap_mmc_platform_data **mmc_data);
|
|
||||||
#else
|
|
||||||
static inline void omap242x_init_mmc(struct omap_mmc_platform_data **mmc_data)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct omap_hwmod;
|
struct omap_hwmod;
|
||||||
int omap_msdi_reset(struct omap_hwmod *oh);
|
int omap_msdi_reset(struct omap_hwmod *oh);
|
||||||
|
|
||||||
|
@ -36,7 +36,6 @@
|
|||||||
#include "soc.h"
|
#include "soc.h"
|
||||||
#include "iomap.h"
|
#include "iomap.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "mmc.h"
|
|
||||||
#include "prminst44xx.h"
|
#include "prminst44xx.h"
|
||||||
#include "prcm_mpu44xx.h"
|
#include "prcm_mpu44xx.h"
|
||||||
#include "omap4-sar-layout.h"
|
#include "omap4-sar-layout.h"
|
||||||
|
@ -15,12 +15,12 @@
|
|||||||
|
|
||||||
#include <linux/i2c-omap.h>
|
#include <linux/i2c-omap.h>
|
||||||
#include <linux/platform_data/asoc-ti-mcbsp.h>
|
#include <linux/platform_data/asoc-ti-mcbsp.h>
|
||||||
|
#include <linux/platform_data/hsmmc-omap.h>
|
||||||
#include <linux/platform_data/spi-omap2-mcspi.h>
|
#include <linux/platform_data/spi-omap2-mcspi.h>
|
||||||
#include <linux/omap-dma.h>
|
#include <linux/omap-dma.h>
|
||||||
#include <plat/dmtimer.h>
|
#include <plat/dmtimer.h>
|
||||||
|
|
||||||
#include "omap_hwmod.h"
|
#include "omap_hwmod.h"
|
||||||
#include "mmc.h"
|
|
||||||
#include "l3_2xxx.h"
|
#include "l3_2xxx.h"
|
||||||
|
|
||||||
#include "soc.h"
|
#include "soc.h"
|
||||||
@ -372,7 +372,7 @@ static struct omap_hwmod_opt_clk omap2430_mmc1_opt_clks[] = {
|
|||||||
{ .role = "dbck", .clk = "mmchsdb1_fck" },
|
{ .role = "dbck", .clk = "mmchsdb1_fck" },
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct omap_mmc_dev_attr mmc1_dev_attr = {
|
static struct omap_hsmmc_dev_attr mmc1_dev_attr = {
|
||||||
.flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT,
|
.flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -15,10 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/platform_data/gpio-omap.h>
|
#include <linux/platform_data/gpio-omap.h>
|
||||||
|
#include <linux/platform_data/hsmmc-omap.h>
|
||||||
#include <linux/platform_data/spi-omap2-mcspi.h>
|
#include <linux/platform_data/spi-omap2-mcspi.h>
|
||||||
#include "omap_hwmod.h"
|
#include "omap_hwmod.h"
|
||||||
#include "i2c.h"
|
#include "i2c.h"
|
||||||
#include "mmc.h"
|
|
||||||
#include "wd_timer.h"
|
#include "wd_timer.h"
|
||||||
#include "cm33xx.h"
|
#include "cm33xx.h"
|
||||||
#include "prm33xx.h"
|
#include "prm33xx.h"
|
||||||
@ -836,7 +836,7 @@ static struct omap_hwmod_class am33xx_mmc_hwmod_class = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* mmc0 */
|
/* mmc0 */
|
||||||
static struct omap_mmc_dev_attr am33xx_mmc0_dev_attr = {
|
static struct omap_hsmmc_dev_attr am33xx_mmc0_dev_attr = {
|
||||||
.flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT,
|
.flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -854,7 +854,7 @@ struct omap_hwmod am33xx_mmc0_hwmod = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* mmc1 */
|
/* mmc1 */
|
||||||
static struct omap_mmc_dev_attr am33xx_mmc1_dev_attr = {
|
static struct omap_hsmmc_dev_attr am33xx_mmc1_dev_attr = {
|
||||||
.flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT,
|
.flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -872,7 +872,7 @@ struct omap_hwmod am33xx_mmc1_hwmod = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* mmc2 */
|
/* mmc2 */
|
||||||
static struct omap_mmc_dev_attr am33xx_mmc2_dev_attr = {
|
static struct omap_hsmmc_dev_attr am33xx_mmc2_dev_attr = {
|
||||||
.flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT,
|
.flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT,
|
||||||
};
|
};
|
||||||
struct omap_hwmod am33xx_mmc2_hwmod = {
|
struct omap_hwmod am33xx_mmc2_hwmod = {
|
||||||
|
@ -27,7 +27,6 @@
|
|||||||
#include "prm33xx.h"
|
#include "prm33xx.h"
|
||||||
#include "prm-regbits-33xx.h"
|
#include "prm-regbits-33xx.h"
|
||||||
#include "i2c.h"
|
#include "i2c.h"
|
||||||
#include "mmc.h"
|
|
||||||
#include "wd_timer.h"
|
#include "wd_timer.h"
|
||||||
#include "omap_hwmod_33xx_43xx_common_data.h"
|
#include "omap_hwmod_33xx_43xx_common_data.h"
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include <linux/i2c-omap.h>
|
#include <linux/i2c-omap.h>
|
||||||
#include <linux/power/smartreflex.h>
|
#include <linux/power/smartreflex.h>
|
||||||
#include <linux/platform_data/gpio-omap.h>
|
#include <linux/platform_data/gpio-omap.h>
|
||||||
|
#include <linux/platform_data/hsmmc-omap.h>
|
||||||
|
|
||||||
#include <linux/omap-dma.h>
|
#include <linux/omap-dma.h>
|
||||||
#include "l3_3xxx.h"
|
#include "l3_3xxx.h"
|
||||||
@ -37,7 +38,6 @@
|
|||||||
#include "cm-regbits-34xx.h"
|
#include "cm-regbits-34xx.h"
|
||||||
|
|
||||||
#include "i2c.h"
|
#include "i2c.h"
|
||||||
#include "mmc.h"
|
|
||||||
#include "wd_timer.h"
|
#include "wd_timer.h"
|
||||||
#include "serial.h"
|
#include "serial.h"
|
||||||
|
|
||||||
@ -1786,12 +1786,12 @@ static struct omap_hwmod_opt_clk omap34xx_mmc1_opt_clks[] = {
|
|||||||
{ .role = "dbck", .clk = "omap_32k_fck", },
|
{ .role = "dbck", .clk = "omap_32k_fck", },
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct omap_mmc_dev_attr mmc1_dev_attr = {
|
static struct omap_hsmmc_dev_attr mmc1_dev_attr = {
|
||||||
.flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT,
|
.flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* See 35xx errata 2.1.1.128 in SPRZ278F */
|
/* See 35xx errata 2.1.1.128 in SPRZ278F */
|
||||||
static struct omap_mmc_dev_attr mmc1_pre_es3_dev_attr = {
|
static struct omap_hsmmc_dev_attr mmc1_pre_es3_dev_attr = {
|
||||||
.flags = (OMAP_HSMMC_SUPPORTS_DUAL_VOLT |
|
.flags = (OMAP_HSMMC_SUPPORTS_DUAL_VOLT |
|
||||||
OMAP_HSMMC_BROKEN_MULTIBLOCK_READ),
|
OMAP_HSMMC_BROKEN_MULTIBLOCK_READ),
|
||||||
};
|
};
|
||||||
@ -1854,7 +1854,7 @@ static struct omap_hwmod_opt_clk omap34xx_mmc2_opt_clks[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* See 35xx errata 2.1.1.128 in SPRZ278F */
|
/* See 35xx errata 2.1.1.128 in SPRZ278F */
|
||||||
static struct omap_mmc_dev_attr mmc2_pre_es3_dev_attr = {
|
static struct omap_hsmmc_dev_attr mmc2_pre_es3_dev_attr = {
|
||||||
.flags = OMAP_HSMMC_BROKEN_MULTIBLOCK_READ,
|
.flags = OMAP_HSMMC_BROKEN_MULTIBLOCK_READ,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/platform_data/gpio-omap.h>
|
#include <linux/platform_data/gpio-omap.h>
|
||||||
|
#include <linux/platform_data/hsmmc-omap.h>
|
||||||
#include <linux/power/smartreflex.h>
|
#include <linux/power/smartreflex.h>
|
||||||
#include <linux/i2c-omap.h>
|
#include <linux/i2c-omap.h>
|
||||||
|
|
||||||
@ -39,7 +40,6 @@
|
|||||||
#include "prm44xx.h"
|
#include "prm44xx.h"
|
||||||
#include "prm-regbits-44xx.h"
|
#include "prm-regbits-44xx.h"
|
||||||
#include "i2c.h"
|
#include "i2c.h"
|
||||||
#include "mmc.h"
|
|
||||||
#include "wd_timer.h"
|
#include "wd_timer.h"
|
||||||
|
|
||||||
/* Base offset for all OMAP4 interrupts external to MPUSS */
|
/* Base offset for all OMAP4 interrupts external to MPUSS */
|
||||||
@ -1952,7 +1952,7 @@ static struct omap_hwmod_dma_info omap44xx_mmc1_sdma_reqs[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* mmc1 dev_attr */
|
/* mmc1 dev_attr */
|
||||||
static struct omap_mmc_dev_attr mmc1_dev_attr = {
|
static struct omap_hsmmc_dev_attr mmc1_dev_attr = {
|
||||||
.flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT,
|
.flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/platform_data/gpio-omap.h>
|
#include <linux/platform_data/gpio-omap.h>
|
||||||
|
#include <linux/platform_data/hsmmc-omap.h>
|
||||||
#include <linux/power/smartreflex.h>
|
#include <linux/power/smartreflex.h>
|
||||||
#include <linux/i2c-omap.h>
|
#include <linux/i2c-omap.h>
|
||||||
|
|
||||||
@ -33,7 +34,6 @@
|
|||||||
#include "cm2_54xx.h"
|
#include "cm2_54xx.h"
|
||||||
#include "prm54xx.h"
|
#include "prm54xx.h"
|
||||||
#include "i2c.h"
|
#include "i2c.h"
|
||||||
#include "mmc.h"
|
|
||||||
#include "wd_timer.h"
|
#include "wd_timer.h"
|
||||||
|
|
||||||
/* Base offset for all OMAP5 interrupts external to MPUSS */
|
/* Base offset for all OMAP5 interrupts external to MPUSS */
|
||||||
@ -1269,7 +1269,7 @@ static struct omap_hwmod_opt_clk mmc1_opt_clks[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* mmc1 dev_attr */
|
/* mmc1 dev_attr */
|
||||||
static struct omap_mmc_dev_attr mmc1_dev_attr = {
|
static struct omap_hsmmc_dev_attr mmc1_dev_attr = {
|
||||||
.flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT,
|
.flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/platform_data/gpio-omap.h>
|
#include <linux/platform_data/gpio-omap.h>
|
||||||
|
#include <linux/platform_data/hsmmc-omap.h>
|
||||||
#include <linux/power/smartreflex.h>
|
#include <linux/power/smartreflex.h>
|
||||||
#include <linux/i2c-omap.h>
|
#include <linux/i2c-omap.h>
|
||||||
|
|
||||||
@ -33,7 +34,6 @@
|
|||||||
#include "cm2_7xx.h"
|
#include "cm2_7xx.h"
|
||||||
#include "prm7xx.h"
|
#include "prm7xx.h"
|
||||||
#include "i2c.h"
|
#include "i2c.h"
|
||||||
#include "mmc.h"
|
|
||||||
#include "wd_timer.h"
|
#include "wd_timer.h"
|
||||||
#include "soc.h"
|
#include "soc.h"
|
||||||
|
|
||||||
@ -1301,7 +1301,7 @@ static struct omap_hwmod_opt_clk mmc1_opt_clks[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* mmc1 dev_attr */
|
/* mmc1 dev_attr */
|
||||||
static struct omap_mmc_dev_attr mmc1_dev_attr = {
|
static struct omap_hsmmc_dev_attr mmc1_dev_attr = {
|
||||||
.flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT,
|
.flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/usb/atmel_usba_udc.h>
|
#include <linux/usb/atmel_usba_udc.h>
|
||||||
|
|
||||||
#include <mach/atmel-mci.h>
|
#include <linux/platform_data/mmc-atmel-mci.h>
|
||||||
#include <linux/atmel-mci.h>
|
#include <linux/atmel-mci.h>
|
||||||
|
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
#ifndef __MACH_ATMEL_MCI_H
|
|
||||||
#define __MACH_ATMEL_MCI_H
|
|
||||||
|
|
||||||
#include <linux/platform_data/dma-dw.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct mci_dma_data - DMA data for MCI interface
|
|
||||||
*/
|
|
||||||
struct mci_dma_data {
|
|
||||||
struct dw_dma_slave sdata;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* accessor macros */
|
|
||||||
#define slave_data_ptr(s) (&(s)->sdata)
|
|
||||||
#define find_slave_dev(s) ((s)->sdata.dma_dev)
|
|
||||||
|
|
||||||
#endif /* __MACH_ATMEL_MCI_H */
|
|
@ -78,13 +78,16 @@ static int perdev_minors = CONFIG_MMC_BLOCK_MINORS;
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* We've only got one major, so number of mmcblk devices is
|
* We've only got one major, so number of mmcblk devices is
|
||||||
* limited to 256 / number of minors per device.
|
* limited to (1 << 20) / number of minors per device. It is also
|
||||||
|
* currently limited by the size of the static bitmaps below.
|
||||||
*/
|
*/
|
||||||
static int max_devices;
|
static int max_devices;
|
||||||
|
|
||||||
/* 256 minors, so at most 256 separate devices */
|
#define MAX_DEVICES 256
|
||||||
static DECLARE_BITMAP(dev_use, 256);
|
|
||||||
static DECLARE_BITMAP(name_use, 256);
|
/* TODO: Replace these with struct ida */
|
||||||
|
static DECLARE_BITMAP(dev_use, MAX_DEVICES);
|
||||||
|
static DECLARE_BITMAP(name_use, MAX_DEVICES);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* There is one mmc_blk_data per slot.
|
* There is one mmc_blk_data per slot.
|
||||||
@ -112,7 +115,7 @@ struct mmc_blk_data {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Only set in main mmc_blk_data associated
|
* Only set in main mmc_blk_data associated
|
||||||
* with mmc_card with mmc_set_drvdata, and keeps
|
* with mmc_card with dev_set_drvdata, and keeps
|
||||||
* track of the current selected device partition.
|
* track of the current selected device partition.
|
||||||
*/
|
*/
|
||||||
unsigned int part_curr;
|
unsigned int part_curr;
|
||||||
@ -260,7 +263,7 @@ static ssize_t force_ro_show(struct device *dev, struct device_attribute *attr,
|
|||||||
int ret;
|
int ret;
|
||||||
struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
|
struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
|
||||||
|
|
||||||
ret = snprintf(buf, PAGE_SIZE, "%d",
|
ret = snprintf(buf, PAGE_SIZE, "%d\n",
|
||||||
get_disk_ro(dev_to_disk(dev)) ^
|
get_disk_ro(dev_to_disk(dev)) ^
|
||||||
md->read_only);
|
md->read_only);
|
||||||
mmc_blk_put(md);
|
mmc_blk_put(md);
|
||||||
@ -642,7 +645,7 @@ static inline int mmc_blk_part_switch(struct mmc_card *card,
|
|||||||
struct mmc_blk_data *md)
|
struct mmc_blk_data *md)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
struct mmc_blk_data *main_md = mmc_get_drvdata(card);
|
struct mmc_blk_data *main_md = dev_get_drvdata(&card->dev);
|
||||||
|
|
||||||
if (main_md->part_curr == md->part_type)
|
if (main_md->part_curr == md->part_type)
|
||||||
return 0;
|
return 0;
|
||||||
@ -1004,7 +1007,8 @@ static int mmc_blk_reset(struct mmc_blk_data *md, struct mmc_host *host,
|
|||||||
err = mmc_hw_reset(host);
|
err = mmc_hw_reset(host);
|
||||||
/* Ensure we switch back to the correct partition */
|
/* Ensure we switch back to the correct partition */
|
||||||
if (err != -EOPNOTSUPP) {
|
if (err != -EOPNOTSUPP) {
|
||||||
struct mmc_blk_data *main_md = mmc_get_drvdata(host->card);
|
struct mmc_blk_data *main_md =
|
||||||
|
dev_get_drvdata(&host->card->dev);
|
||||||
int part_err;
|
int part_err;
|
||||||
|
|
||||||
main_md->part_curr = main_md->part_type;
|
main_md->part_curr = main_md->part_type;
|
||||||
@ -1308,19 +1312,11 @@ static int mmc_blk_packed_err_check(struct mmc_card *card,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (status & R1_EXCEPTION_EVENT) {
|
if (status & R1_EXCEPTION_EVENT) {
|
||||||
ext_csd = kzalloc(512, GFP_KERNEL);
|
err = mmc_get_ext_csd(card, &ext_csd);
|
||||||
if (!ext_csd) {
|
|
||||||
pr_err("%s: unable to allocate buffer for ext_csd\n",
|
|
||||||
req->rq_disk->disk_name);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = mmc_send_ext_csd(card, ext_csd);
|
|
||||||
if (err) {
|
if (err) {
|
||||||
pr_err("%s: error %d sending ext_csd\n",
|
pr_err("%s: error %d sending ext_csd\n",
|
||||||
req->rq_disk->disk_name, err);
|
req->rq_disk->disk_name, err);
|
||||||
check = MMC_BLK_ABORT;
|
return MMC_BLK_ABORT;
|
||||||
goto free;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ext_csd[EXT_CSD_EXP_EVENTS_STATUS] &
|
if ((ext_csd[EXT_CSD_EXP_EVENTS_STATUS] &
|
||||||
@ -1338,7 +1334,6 @@ static int mmc_blk_packed_err_check(struct mmc_card *card,
|
|||||||
req->rq_disk->disk_name, packed->nr_entries,
|
req->rq_disk->disk_name, packed->nr_entries,
|
||||||
packed->blocks, packed->idx_failure);
|
packed->blocks, packed->idx_failure);
|
||||||
}
|
}
|
||||||
free:
|
|
||||||
kfree(ext_csd);
|
kfree(ext_csd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2093,7 +2088,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* !subname implies we are creating main mmc_blk_data that will be
|
* !subname implies we are creating main mmc_blk_data that will be
|
||||||
* associated with mmc_card with mmc_set_drvdata. Due to device
|
* associated with mmc_card with dev_set_drvdata. Due to device
|
||||||
* partitions, devidx will not coincide with a per-physical card
|
* partitions, devidx will not coincide with a per-physical card
|
||||||
* index anymore so we keep track of a name index.
|
* index anymore so we keep track of a name index.
|
||||||
*/
|
*/
|
||||||
@ -2425,8 +2420,9 @@ static const struct mmc_fixup blk_fixups[] =
|
|||||||
END_FIXUP
|
END_FIXUP
|
||||||
};
|
};
|
||||||
|
|
||||||
static int mmc_blk_probe(struct mmc_card *card)
|
static int mmc_blk_probe(struct device *dev)
|
||||||
{
|
{
|
||||||
|
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||||
struct mmc_blk_data *md, *part_md;
|
struct mmc_blk_data *md, *part_md;
|
||||||
char cap_str[10];
|
char cap_str[10];
|
||||||
|
|
||||||
@ -2451,7 +2447,7 @@ static int mmc_blk_probe(struct mmc_card *card)
|
|||||||
if (mmc_blk_alloc_parts(card, md))
|
if (mmc_blk_alloc_parts(card, md))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
mmc_set_drvdata(card, md);
|
dev_set_drvdata(dev, md);
|
||||||
|
|
||||||
if (mmc_add_disk(md))
|
if (mmc_add_disk(md))
|
||||||
goto out;
|
goto out;
|
||||||
@ -2481,9 +2477,10 @@ static int mmc_blk_probe(struct mmc_card *card)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mmc_blk_remove(struct mmc_card *card)
|
static int mmc_blk_remove(struct device *dev)
|
||||||
{
|
{
|
||||||
struct mmc_blk_data *md = mmc_get_drvdata(card);
|
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||||
|
struct mmc_blk_data *md = dev_get_drvdata(dev);
|
||||||
|
|
||||||
mmc_blk_remove_parts(card, md);
|
mmc_blk_remove_parts(card, md);
|
||||||
pm_runtime_get_sync(&card->dev);
|
pm_runtime_get_sync(&card->dev);
|
||||||
@ -2494,13 +2491,15 @@ static void mmc_blk_remove(struct mmc_card *card)
|
|||||||
pm_runtime_disable(&card->dev);
|
pm_runtime_disable(&card->dev);
|
||||||
pm_runtime_put_noidle(&card->dev);
|
pm_runtime_put_noidle(&card->dev);
|
||||||
mmc_blk_remove_req(md);
|
mmc_blk_remove_req(md);
|
||||||
mmc_set_drvdata(card, NULL);
|
dev_set_drvdata(dev, NULL);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _mmc_blk_suspend(struct mmc_card *card)
|
static int _mmc_blk_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct mmc_blk_data *part_md;
|
struct mmc_blk_data *part_md;
|
||||||
struct mmc_blk_data *md = mmc_get_drvdata(card);
|
struct mmc_blk_data *md = dev_get_drvdata(dev);
|
||||||
|
|
||||||
if (md) {
|
if (md) {
|
||||||
mmc_queue_suspend(&md->queue);
|
mmc_queue_suspend(&md->queue);
|
||||||
@ -2511,21 +2510,21 @@ static int _mmc_blk_suspend(struct mmc_card *card)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mmc_blk_shutdown(struct mmc_card *card)
|
static void mmc_blk_shutdown(struct device *dev)
|
||||||
{
|
{
|
||||||
_mmc_blk_suspend(card);
|
_mmc_blk_suspend(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM_SLEEP
|
||||||
static int mmc_blk_suspend(struct mmc_card *card)
|
static int mmc_blk_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
return _mmc_blk_suspend(card);
|
return _mmc_blk_suspend(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mmc_blk_resume(struct mmc_card *card)
|
static int mmc_blk_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct mmc_blk_data *part_md;
|
struct mmc_blk_data *part_md;
|
||||||
struct mmc_blk_data *md = mmc_get_drvdata(card);
|
struct mmc_blk_data *md = dev_get_drvdata(dev);
|
||||||
|
|
||||||
if (md) {
|
if (md) {
|
||||||
/*
|
/*
|
||||||
@ -2540,19 +2539,15 @@ static int mmc_blk_resume(struct mmc_card *card)
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
#define mmc_blk_suspend NULL
|
|
||||||
#define mmc_blk_resume NULL
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static struct mmc_driver mmc_driver = {
|
static SIMPLE_DEV_PM_OPS(mmc_blk_pm_ops, mmc_blk_suspend, mmc_blk_resume);
|
||||||
.drv = {
|
|
||||||
.name = "mmcblk",
|
static struct device_driver mmc_driver = {
|
||||||
},
|
.name = "mmcblk",
|
||||||
|
.pm = &mmc_blk_pm_ops,
|
||||||
.probe = mmc_blk_probe,
|
.probe = mmc_blk_probe,
|
||||||
.remove = mmc_blk_remove,
|
.remove = mmc_blk_remove,
|
||||||
.suspend = mmc_blk_suspend,
|
|
||||||
.resume = mmc_blk_resume,
|
|
||||||
.shutdown = mmc_blk_shutdown,
|
.shutdown = mmc_blk_shutdown,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2563,7 +2558,7 @@ static int __init mmc_blk_init(void)
|
|||||||
if (perdev_minors != CONFIG_MMC_BLOCK_MINORS)
|
if (perdev_minors != CONFIG_MMC_BLOCK_MINORS)
|
||||||
pr_info("mmcblk: using %d minors per device\n", perdev_minors);
|
pr_info("mmcblk: using %d minors per device\n", perdev_minors);
|
||||||
|
|
||||||
max_devices = 256 / perdev_minors;
|
max_devices = min(MAX_DEVICES, (1 << MINORBITS) / perdev_minors);
|
||||||
|
|
||||||
res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");
|
res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");
|
||||||
if (res)
|
if (res)
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include <linux/mmc/host.h>
|
#include <linux/mmc/host.h>
|
||||||
#include <linux/mmc/mmc.h>
|
#include <linux/mmc/mmc.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
|
||||||
#include <linux/scatterlist.h>
|
#include <linux/scatterlist.h>
|
||||||
#include <linux/swap.h> /* For nr_free_buffer_pages() */
|
#include <linux/swap.h> /* For nr_free_buffer_pages() */
|
||||||
@ -32,6 +33,8 @@
|
|||||||
#define BUFFER_ORDER 2
|
#define BUFFER_ORDER 2
|
||||||
#define BUFFER_SIZE (PAGE_SIZE << BUFFER_ORDER)
|
#define BUFFER_SIZE (PAGE_SIZE << BUFFER_ORDER)
|
||||||
|
|
||||||
|
#define TEST_ALIGN_END 8
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Limit the test area size to the maximum MMC HC erase group size. Note that
|
* Limit the test area size to the maximum MMC HC erase group size. Note that
|
||||||
* the maximum SD allocation unit size is just 4MiB.
|
* the maximum SD allocation unit size is just 4MiB.
|
||||||
@ -1174,7 +1177,7 @@ static int mmc_test_align_write(struct mmc_test_card *test)
|
|||||||
int ret, i;
|
int ret, i;
|
||||||
struct scatterlist sg;
|
struct scatterlist sg;
|
||||||
|
|
||||||
for (i = 1;i < 4;i++) {
|
for (i = 1; i < TEST_ALIGN_END; i++) {
|
||||||
sg_init_one(&sg, test->buffer + i, 512);
|
sg_init_one(&sg, test->buffer + i, 512);
|
||||||
ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1);
|
ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1);
|
||||||
if (ret)
|
if (ret)
|
||||||
@ -1189,7 +1192,7 @@ static int mmc_test_align_read(struct mmc_test_card *test)
|
|||||||
int ret, i;
|
int ret, i;
|
||||||
struct scatterlist sg;
|
struct scatterlist sg;
|
||||||
|
|
||||||
for (i = 1;i < 4;i++) {
|
for (i = 1; i < TEST_ALIGN_END; i++) {
|
||||||
sg_init_one(&sg, test->buffer + i, 512);
|
sg_init_one(&sg, test->buffer + i, 512);
|
||||||
ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0);
|
ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0);
|
||||||
if (ret)
|
if (ret)
|
||||||
@ -1216,7 +1219,7 @@ static int mmc_test_align_multi_write(struct mmc_test_card *test)
|
|||||||
if (size < 1024)
|
if (size < 1024)
|
||||||
return RESULT_UNSUP_HOST;
|
return RESULT_UNSUP_HOST;
|
||||||
|
|
||||||
for (i = 1;i < 4;i++) {
|
for (i = 1; i < TEST_ALIGN_END; i++) {
|
||||||
sg_init_one(&sg, test->buffer + i, size);
|
sg_init_one(&sg, test->buffer + i, size);
|
||||||
ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
|
ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
|
||||||
if (ret)
|
if (ret)
|
||||||
@ -1243,7 +1246,7 @@ static int mmc_test_align_multi_read(struct mmc_test_card *test)
|
|||||||
if (size < 1024)
|
if (size < 1024)
|
||||||
return RESULT_UNSUP_HOST;
|
return RESULT_UNSUP_HOST;
|
||||||
|
|
||||||
for (i = 1;i < 4;i++) {
|
for (i = 1; i < TEST_ALIGN_END; i++) {
|
||||||
sg_init_one(&sg, test->buffer + i, size);
|
sg_init_one(&sg, test->buffer + i, size);
|
||||||
ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
|
ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
|
||||||
if (ret)
|
if (ret)
|
||||||
@ -2997,8 +3000,9 @@ err:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mmc_test_probe(struct mmc_card *card)
|
static int mmc_test_probe(struct device *dev)
|
||||||
{
|
{
|
||||||
|
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!mmc_card_mmc(card) && !mmc_card_sd(card))
|
if (!mmc_card_mmc(card) && !mmc_card_sd(card))
|
||||||
@ -3013,20 +3017,22 @@ static int mmc_test_probe(struct mmc_card *card)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mmc_test_remove(struct mmc_card *card)
|
static int mmc_test_remove(struct device *dev)
|
||||||
{
|
{
|
||||||
|
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||||
|
|
||||||
mmc_test_free_result(card);
|
mmc_test_free_result(card);
|
||||||
mmc_test_free_dbgfs_file(card);
|
mmc_test_free_dbgfs_file(card);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mmc_test_shutdown(struct mmc_card *card)
|
static void mmc_test_shutdown(struct device *dev)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct mmc_driver mmc_driver = {
|
static struct device_driver mmc_driver = {
|
||||||
.drv = {
|
.name = "mmc_test",
|
||||||
.name = "mmc_test",
|
|
||||||
},
|
|
||||||
.probe = mmc_test_probe,
|
.probe = mmc_test_probe,
|
||||||
.remove = mmc_test_remove,
|
.remove = mmc_test_remove,
|
||||||
.shutdown = mmc_test_shutdown,
|
.shutdown = mmc_test_shutdown,
|
||||||
|
@ -232,13 +232,15 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card,
|
|||||||
if (!mqrq_cur->bounce_buf) {
|
if (!mqrq_cur->bounce_buf) {
|
||||||
pr_warn("%s: unable to allocate bounce cur buffer\n",
|
pr_warn("%s: unable to allocate bounce cur buffer\n",
|
||||||
mmc_card_name(card));
|
mmc_card_name(card));
|
||||||
}
|
} else {
|
||||||
mqrq_prev->bounce_buf = kmalloc(bouncesz, GFP_KERNEL);
|
mqrq_prev->bounce_buf =
|
||||||
if (!mqrq_prev->bounce_buf) {
|
kmalloc(bouncesz, GFP_KERNEL);
|
||||||
pr_warn("%s: unable to allocate bounce prev buffer\n",
|
if (!mqrq_prev->bounce_buf) {
|
||||||
mmc_card_name(card));
|
pr_warn("%s: unable to allocate bounce prev buffer\n",
|
||||||
kfree(mqrq_cur->bounce_buf);
|
mmc_card_name(card));
|
||||||
mqrq_cur->bounce_buf = NULL;
|
kfree(mqrq_cur->bounce_buf);
|
||||||
|
mqrq_cur->bounce_buf = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,8 +25,6 @@
|
|||||||
#include "sdio_cis.h"
|
#include "sdio_cis.h"
|
||||||
#include "bus.h"
|
#include "bus.h"
|
||||||
|
|
||||||
#define to_mmc_driver(d) container_of(d, struct mmc_driver, drv)
|
|
||||||
|
|
||||||
static ssize_t type_show(struct device *dev,
|
static ssize_t type_show(struct device *dev,
|
||||||
struct device_attribute *attr, char *buf)
|
struct device_attribute *attr, char *buf)
|
||||||
{
|
{
|
||||||
@ -106,33 +104,14 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mmc_bus_probe(struct device *dev)
|
|
||||||
{
|
|
||||||
struct mmc_driver *drv = to_mmc_driver(dev->driver);
|
|
||||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
|
||||||
|
|
||||||
return drv->probe(card);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int mmc_bus_remove(struct device *dev)
|
|
||||||
{
|
|
||||||
struct mmc_driver *drv = to_mmc_driver(dev->driver);
|
|
||||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
|
||||||
|
|
||||||
drv->remove(card);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void mmc_bus_shutdown(struct device *dev)
|
static void mmc_bus_shutdown(struct device *dev)
|
||||||
{
|
{
|
||||||
struct mmc_driver *drv = to_mmc_driver(dev->driver);
|
|
||||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||||
struct mmc_host *host = card->host;
|
struct mmc_host *host = card->host;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (dev->driver && drv->shutdown)
|
if (dev->driver && dev->driver->shutdown)
|
||||||
drv->shutdown(card);
|
dev->driver->shutdown(dev);
|
||||||
|
|
||||||
if (host->bus_ops->shutdown) {
|
if (host->bus_ops->shutdown) {
|
||||||
ret = host->bus_ops->shutdown(host);
|
ret = host->bus_ops->shutdown(host);
|
||||||
@ -145,16 +124,13 @@ static void mmc_bus_shutdown(struct device *dev)
|
|||||||
#ifdef CONFIG_PM_SLEEP
|
#ifdef CONFIG_PM_SLEEP
|
||||||
static int mmc_bus_suspend(struct device *dev)
|
static int mmc_bus_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct mmc_driver *drv = to_mmc_driver(dev->driver);
|
|
||||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||||
struct mmc_host *host = card->host;
|
struct mmc_host *host = card->host;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (dev->driver && drv->suspend) {
|
ret = pm_generic_suspend(dev);
|
||||||
ret = drv->suspend(card);
|
if (ret)
|
||||||
if (ret)
|
return ret;
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = host->bus_ops->suspend(host);
|
ret = host->bus_ops->suspend(host);
|
||||||
return ret;
|
return ret;
|
||||||
@ -162,7 +138,6 @@ static int mmc_bus_suspend(struct device *dev)
|
|||||||
|
|
||||||
static int mmc_bus_resume(struct device *dev)
|
static int mmc_bus_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct mmc_driver *drv = to_mmc_driver(dev->driver);
|
|
||||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||||
struct mmc_host *host = card->host;
|
struct mmc_host *host = card->host;
|
||||||
int ret;
|
int ret;
|
||||||
@ -172,9 +147,7 @@ static int mmc_bus_resume(struct device *dev)
|
|||||||
pr_warn("%s: error %d during resume (card was removed?)\n",
|
pr_warn("%s: error %d during resume (card was removed?)\n",
|
||||||
mmc_hostname(host), ret);
|
mmc_hostname(host), ret);
|
||||||
|
|
||||||
if (dev->driver && drv->resume)
|
ret = pm_generic_resume(dev);
|
||||||
ret = drv->resume(card);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -207,8 +180,6 @@ static struct bus_type mmc_bus_type = {
|
|||||||
.dev_groups = mmc_dev_groups,
|
.dev_groups = mmc_dev_groups,
|
||||||
.match = mmc_bus_match,
|
.match = mmc_bus_match,
|
||||||
.uevent = mmc_bus_uevent,
|
.uevent = mmc_bus_uevent,
|
||||||
.probe = mmc_bus_probe,
|
|
||||||
.remove = mmc_bus_remove,
|
|
||||||
.shutdown = mmc_bus_shutdown,
|
.shutdown = mmc_bus_shutdown,
|
||||||
.pm = &mmc_bus_pm_ops,
|
.pm = &mmc_bus_pm_ops,
|
||||||
};
|
};
|
||||||
@ -227,24 +198,22 @@ void mmc_unregister_bus(void)
|
|||||||
* mmc_register_driver - register a media driver
|
* mmc_register_driver - register a media driver
|
||||||
* @drv: MMC media driver
|
* @drv: MMC media driver
|
||||||
*/
|
*/
|
||||||
int mmc_register_driver(struct mmc_driver *drv)
|
int mmc_register_driver(struct device_driver *drv)
|
||||||
{
|
{
|
||||||
drv->drv.bus = &mmc_bus_type;
|
drv->bus = &mmc_bus_type;
|
||||||
return driver_register(&drv->drv);
|
return driver_register(drv);
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL(mmc_register_driver);
|
EXPORT_SYMBOL(mmc_register_driver);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mmc_unregister_driver - unregister a media driver
|
* mmc_unregister_driver - unregister a media driver
|
||||||
* @drv: MMC media driver
|
* @drv: MMC media driver
|
||||||
*/
|
*/
|
||||||
void mmc_unregister_driver(struct mmc_driver *drv)
|
void mmc_unregister_driver(struct device_driver *drv)
|
||||||
{
|
{
|
||||||
drv->drv.bus = &mmc_bus_type;
|
drv->bus = &mmc_bus_type;
|
||||||
driver_unregister(&drv->drv);
|
driver_unregister(drv);
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL(mmc_unregister_driver);
|
EXPORT_SYMBOL(mmc_unregister_driver);
|
||||||
|
|
||||||
static void mmc_release_card(struct device *dev)
|
static void mmc_release_card(struct device *dev)
|
||||||
|
@ -149,6 +149,14 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
|
|||||||
|
|
||||||
led_trigger_event(host->led, LED_OFF);
|
led_trigger_event(host->led, LED_OFF);
|
||||||
|
|
||||||
|
if (mrq->sbc) {
|
||||||
|
pr_debug("%s: req done <CMD%u>: %d: %08x %08x %08x %08x\n",
|
||||||
|
mmc_hostname(host), mrq->sbc->opcode,
|
||||||
|
mrq->sbc->error,
|
||||||
|
mrq->sbc->resp[0], mrq->sbc->resp[1],
|
||||||
|
mrq->sbc->resp[2], mrq->sbc->resp[3]);
|
||||||
|
}
|
||||||
|
|
||||||
pr_debug("%s: req done (CMD%u): %d: %08x %08x %08x %08x\n",
|
pr_debug("%s: req done (CMD%u): %d: %08x %08x %08x %08x\n",
|
||||||
mmc_hostname(host), cmd->opcode, err,
|
mmc_hostname(host), cmd->opcode, err,
|
||||||
cmd->resp[0], cmd->resp[1],
|
cmd->resp[0], cmd->resp[1],
|
||||||
@ -214,6 +222,10 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
|
|||||||
|
|
||||||
mrq->cmd->error = 0;
|
mrq->cmd->error = 0;
|
||||||
mrq->cmd->mrq = mrq;
|
mrq->cmd->mrq = mrq;
|
||||||
|
if (mrq->sbc) {
|
||||||
|
mrq->sbc->error = 0;
|
||||||
|
mrq->sbc->mrq = mrq;
|
||||||
|
}
|
||||||
if (mrq->data) {
|
if (mrq->data) {
|
||||||
BUG_ON(mrq->data->blksz > host->max_blk_size);
|
BUG_ON(mrq->data->blksz > host->max_blk_size);
|
||||||
BUG_ON(mrq->data->blocks > host->max_blk_count);
|
BUG_ON(mrq->data->blocks > host->max_blk_count);
|
||||||
@ -538,8 +550,18 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
|
|||||||
if (host->card && mmc_card_mmc(host->card) &&
|
if (host->card && mmc_card_mmc(host->card) &&
|
||||||
((mmc_resp_type(host->areq->mrq->cmd) == MMC_RSP_R1) ||
|
((mmc_resp_type(host->areq->mrq->cmd) == MMC_RSP_R1) ||
|
||||||
(mmc_resp_type(host->areq->mrq->cmd) == MMC_RSP_R1B)) &&
|
(mmc_resp_type(host->areq->mrq->cmd) == MMC_RSP_R1B)) &&
|
||||||
(host->areq->mrq->cmd->resp[0] & R1_EXCEPTION_EVENT))
|
(host->areq->mrq->cmd->resp[0] & R1_EXCEPTION_EVENT)) {
|
||||||
|
|
||||||
|
/* Cancel the prepared request */
|
||||||
|
if (areq)
|
||||||
|
mmc_post_req(host, areq->mrq, -EINVAL);
|
||||||
|
|
||||||
mmc_start_bkops(host->card, true);
|
mmc_start_bkops(host->card, true);
|
||||||
|
|
||||||
|
/* prepare the request again */
|
||||||
|
if (areq)
|
||||||
|
mmc_pre_req(host, areq->mrq, !host->areq);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!err && areq)
|
if (!err && areq)
|
||||||
@ -709,27 +731,16 @@ int mmc_read_bkops_status(struct mmc_card *card)
|
|||||||
int err;
|
int err;
|
||||||
u8 *ext_csd;
|
u8 *ext_csd;
|
||||||
|
|
||||||
/*
|
|
||||||
* In future work, we should consider storing the entire ext_csd.
|
|
||||||
*/
|
|
||||||
ext_csd = kmalloc(512, GFP_KERNEL);
|
|
||||||
if (!ext_csd) {
|
|
||||||
pr_err("%s: could not allocate buffer to receive the ext_csd.\n",
|
|
||||||
mmc_hostname(card->host));
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
mmc_claim_host(card->host);
|
mmc_claim_host(card->host);
|
||||||
err = mmc_send_ext_csd(card, ext_csd);
|
err = mmc_get_ext_csd(card, &ext_csd);
|
||||||
mmc_release_host(card->host);
|
mmc_release_host(card->host);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
return err;
|
||||||
|
|
||||||
card->ext_csd.raw_bkops_status = ext_csd[EXT_CSD_BKOPS_STATUS];
|
card->ext_csd.raw_bkops_status = ext_csd[EXT_CSD_BKOPS_STATUS];
|
||||||
card->ext_csd.raw_exception_status = ext_csd[EXT_CSD_EXP_EVENTS_STATUS];
|
card->ext_csd.raw_exception_status = ext_csd[EXT_CSD_EXP_EVENTS_STATUS];
|
||||||
out:
|
|
||||||
kfree(ext_csd);
|
kfree(ext_csd);
|
||||||
return err;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(mmc_read_bkops_status);
|
EXPORT_SYMBOL(mmc_read_bkops_status);
|
||||||
|
|
||||||
@ -1088,6 +1099,22 @@ void mmc_set_bus_width(struct mmc_host *host, unsigned int width)
|
|||||||
mmc_host_clk_release(host);
|
mmc_host_clk_release(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set initial state after a power cycle or a hw_reset.
|
||||||
|
*/
|
||||||
|
void mmc_set_initial_state(struct mmc_host *host)
|
||||||
|
{
|
||||||
|
if (mmc_host_is_spi(host))
|
||||||
|
host->ios.chip_select = MMC_CS_HIGH;
|
||||||
|
else
|
||||||
|
host->ios.chip_select = MMC_CS_DONTCARE;
|
||||||
|
host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
|
||||||
|
host->ios.bus_width = MMC_BUS_WIDTH_1;
|
||||||
|
host->ios.timing = MMC_TIMING_LEGACY;
|
||||||
|
|
||||||
|
mmc_set_ios(host);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mmc_vdd_to_ocrbitnum - Convert a voltage to the OCR bit number
|
* mmc_vdd_to_ocrbitnum - Convert a voltage to the OCR bit number
|
||||||
* @vdd: voltage (mV)
|
* @vdd: voltage (mV)
|
||||||
@ -1420,18 +1447,20 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr)
|
|||||||
pr_warn("%s: cannot verify signal voltage switch\n",
|
pr_warn("%s: cannot verify signal voltage switch\n",
|
||||||
mmc_hostname(host));
|
mmc_hostname(host));
|
||||||
|
|
||||||
|
mmc_host_clk_hold(host);
|
||||||
|
|
||||||
cmd.opcode = SD_SWITCH_VOLTAGE;
|
cmd.opcode = SD_SWITCH_VOLTAGE;
|
||||||
cmd.arg = 0;
|
cmd.arg = 0;
|
||||||
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
|
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
|
||||||
|
|
||||||
err = mmc_wait_for_cmd(host, &cmd, 0);
|
err = mmc_wait_for_cmd(host, &cmd, 0);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
goto err_command;
|
||||||
|
|
||||||
if (!mmc_host_is_spi(host) && (cmd.resp[0] & R1_ERROR))
|
if (!mmc_host_is_spi(host) && (cmd.resp[0] & R1_ERROR)) {
|
||||||
return -EIO;
|
err = -EIO;
|
||||||
|
goto err_command;
|
||||||
mmc_host_clk_hold(host);
|
}
|
||||||
/*
|
/*
|
||||||
* The card should drive cmd and dat[0:3] low immediately
|
* The card should drive cmd and dat[0:3] low immediately
|
||||||
* after the response of cmd11, but wait 1 ms to be sure
|
* after the response of cmd11, but wait 1 ms to be sure
|
||||||
@ -1480,6 +1509,7 @@ power_cycle:
|
|||||||
mmc_power_cycle(host, ocr);
|
mmc_power_cycle(host, ocr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err_command:
|
||||||
mmc_host_clk_release(host);
|
mmc_host_clk_release(host);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
@ -1526,15 +1556,9 @@ void mmc_power_up(struct mmc_host *host, u32 ocr)
|
|||||||
mmc_host_clk_hold(host);
|
mmc_host_clk_hold(host);
|
||||||
|
|
||||||
host->ios.vdd = fls(ocr) - 1;
|
host->ios.vdd = fls(ocr) - 1;
|
||||||
if (mmc_host_is_spi(host))
|
|
||||||
host->ios.chip_select = MMC_CS_HIGH;
|
|
||||||
else
|
|
||||||
host->ios.chip_select = MMC_CS_DONTCARE;
|
|
||||||
host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
|
|
||||||
host->ios.power_mode = MMC_POWER_UP;
|
host->ios.power_mode = MMC_POWER_UP;
|
||||||
host->ios.bus_width = MMC_BUS_WIDTH_1;
|
/* Set initial state and call mmc_set_ios */
|
||||||
host->ios.timing = MMC_TIMING_LEGACY;
|
mmc_set_initial_state(host);
|
||||||
mmc_set_ios(host);
|
|
||||||
|
|
||||||
/* Try to set signal voltage to 3.3V but fall back to 1.8v or 1.2v */
|
/* Try to set signal voltage to 3.3V but fall back to 1.8v or 1.2v */
|
||||||
if (__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330) == 0)
|
if (__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330) == 0)
|
||||||
@ -1574,14 +1598,9 @@ void mmc_power_off(struct mmc_host *host)
|
|||||||
host->ios.clock = 0;
|
host->ios.clock = 0;
|
||||||
host->ios.vdd = 0;
|
host->ios.vdd = 0;
|
||||||
|
|
||||||
if (!mmc_host_is_spi(host)) {
|
|
||||||
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
|
|
||||||
host->ios.chip_select = MMC_CS_DONTCARE;
|
|
||||||
}
|
|
||||||
host->ios.power_mode = MMC_POWER_OFF;
|
host->ios.power_mode = MMC_POWER_OFF;
|
||||||
host->ios.bus_width = MMC_BUS_WIDTH_1;
|
/* Set initial state and call mmc_set_ios */
|
||||||
host->ios.timing = MMC_TIMING_LEGACY;
|
mmc_set_initial_state(host);
|
||||||
mmc_set_ios(host);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Some configurations, such as the 802.11 SDIO card in the OLPC
|
* Some configurations, such as the 802.11 SDIO card in the OLPC
|
||||||
@ -2259,30 +2278,16 @@ static int mmc_do_hw_reset(struct mmc_host *host, int check)
|
|||||||
|
|
||||||
/* If the reset has happened, then a status command will fail */
|
/* If the reset has happened, then a status command will fail */
|
||||||
if (check) {
|
if (check) {
|
||||||
struct mmc_command cmd = {0};
|
u32 status;
|
||||||
int err;
|
|
||||||
|
|
||||||
cmd.opcode = MMC_SEND_STATUS;
|
if (!mmc_send_status(card, &status)) {
|
||||||
if (!mmc_host_is_spi(card->host))
|
|
||||||
cmd.arg = card->rca << 16;
|
|
||||||
cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
|
|
||||||
err = mmc_wait_for_cmd(card->host, &cmd, 0);
|
|
||||||
if (!err) {
|
|
||||||
mmc_host_clk_release(host);
|
mmc_host_clk_release(host);
|
||||||
return -ENOSYS;
|
return -ENOSYS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mmc_host_is_spi(host)) {
|
/* Set initial state and call mmc_set_ios */
|
||||||
host->ios.chip_select = MMC_CS_HIGH;
|
mmc_set_initial_state(host);
|
||||||
host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
|
|
||||||
} else {
|
|
||||||
host->ios.chip_select = MMC_CS_DONTCARE;
|
|
||||||
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
|
|
||||||
}
|
|
||||||
host->ios.bus_width = MMC_BUS_WIDTH_1;
|
|
||||||
host->ios.timing = MMC_TIMING_LEGACY;
|
|
||||||
mmc_set_ios(host);
|
|
||||||
|
|
||||||
mmc_host_clk_release(host);
|
mmc_host_clk_release(host);
|
||||||
|
|
||||||
|
@ -49,6 +49,7 @@ void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
|
|||||||
void mmc_power_up(struct mmc_host *host, u32 ocr);
|
void mmc_power_up(struct mmc_host *host, u32 ocr);
|
||||||
void mmc_power_off(struct mmc_host *host);
|
void mmc_power_off(struct mmc_host *host);
|
||||||
void mmc_power_cycle(struct mmc_host *host, u32 ocr);
|
void mmc_power_cycle(struct mmc_host *host, u32 ocr);
|
||||||
|
void mmc_set_initial_state(struct mmc_host *host);
|
||||||
|
|
||||||
static inline void mmc_delay(unsigned int ms)
|
static inline void mmc_delay(unsigned int ms)
|
||||||
{
|
{
|
||||||
|
@ -291,14 +291,8 @@ static int mmc_ext_csd_open(struct inode *inode, struct file *filp)
|
|||||||
if (!buf)
|
if (!buf)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
ext_csd = kmalloc(512, GFP_KERNEL);
|
|
||||||
if (!ext_csd) {
|
|
||||||
err = -ENOMEM;
|
|
||||||
goto out_free;
|
|
||||||
}
|
|
||||||
|
|
||||||
mmc_get_card(card);
|
mmc_get_card(card);
|
||||||
err = mmc_send_ext_csd(card, ext_csd);
|
err = mmc_get_ext_csd(card, &ext_csd);
|
||||||
mmc_put_card(card);
|
mmc_put_card(card);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_free;
|
goto out_free;
|
||||||
@ -314,7 +308,6 @@ static int mmc_ext_csd_open(struct inode *inode, struct file *filp)
|
|||||||
|
|
||||||
out_free:
|
out_free:
|
||||||
kfree(buf);
|
kfree(buf);
|
||||||
kfree(ext_csd);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,65 +177,6 @@ static int mmc_decode_csd(struct mmc_card *card)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Read extended CSD.
|
|
||||||
*/
|
|
||||||
static int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
u8 *ext_csd;
|
|
||||||
|
|
||||||
BUG_ON(!card);
|
|
||||||
BUG_ON(!new_ext_csd);
|
|
||||||
|
|
||||||
*new_ext_csd = NULL;
|
|
||||||
|
|
||||||
if (card->csd.mmca_vsn < CSD_SPEC_VER_4)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* As the ext_csd is so large and mostly unused, we don't store the
|
|
||||||
* raw block in mmc_card.
|
|
||||||
*/
|
|
||||||
ext_csd = kmalloc(512, GFP_KERNEL);
|
|
||||||
if (!ext_csd) {
|
|
||||||
pr_err("%s: could not allocate a buffer to "
|
|
||||||
"receive the ext_csd.\n", mmc_hostname(card->host));
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = mmc_send_ext_csd(card, ext_csd);
|
|
||||||
if (err) {
|
|
||||||
kfree(ext_csd);
|
|
||||||
*new_ext_csd = NULL;
|
|
||||||
|
|
||||||
/* If the host or the card can't do the switch,
|
|
||||||
* fail more gracefully. */
|
|
||||||
if ((err != -EINVAL)
|
|
||||||
&& (err != -ENOSYS)
|
|
||||||
&& (err != -EFAULT))
|
|
||||||
return err;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* High capacity cards should have this "magic" size
|
|
||||||
* stored in their CSD.
|
|
||||||
*/
|
|
||||||
if (card->csd.capacity == (4096 * 512)) {
|
|
||||||
pr_err("%s: unable to read EXT_CSD "
|
|
||||||
"on a possible high capacity card. "
|
|
||||||
"Card will be ignored.\n",
|
|
||||||
mmc_hostname(card->host));
|
|
||||||
} else {
|
|
||||||
pr_warn("%s: unable to read EXT_CSD, performance might suffer\n",
|
|
||||||
mmc_hostname(card->host));
|
|
||||||
err = 0;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
*new_ext_csd = ext_csd;
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void mmc_select_card_type(struct mmc_card *card)
|
static void mmc_select_card_type(struct mmc_card *card)
|
||||||
{
|
{
|
||||||
struct mmc_host *host = card->host;
|
struct mmc_host *host = card->host;
|
||||||
@ -391,16 +332,11 @@ static void mmc_manage_gp_partitions(struct mmc_card *card, u8 *ext_csd)
|
|||||||
/*
|
/*
|
||||||
* Decode extended CSD.
|
* Decode extended CSD.
|
||||||
*/
|
*/
|
||||||
static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
||||||
{
|
{
|
||||||
int err = 0, idx;
|
int err = 0, idx;
|
||||||
unsigned int part_size;
|
unsigned int part_size;
|
||||||
|
|
||||||
BUG_ON(!card);
|
|
||||||
|
|
||||||
if (!ext_csd)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Version is coded in the CSD_STRUCTURE byte in the EXT_CSD register */
|
/* Version is coded in the CSD_STRUCTURE byte in the EXT_CSD register */
|
||||||
card->ext_csd.raw_ext_csd_structure = ext_csd[EXT_CSD_STRUCTURE];
|
card->ext_csd.raw_ext_csd_structure = ext_csd[EXT_CSD_STRUCTURE];
|
||||||
if (card->csd.structure == 3) {
|
if (card->csd.structure == 3) {
|
||||||
@ -628,15 +564,55 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
|||||||
card->ext_csd.data_sector_size = 512;
|
card->ext_csd.data_sector_size = 512;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* eMMC v5 or later */
|
||||||
|
if (card->ext_csd.rev >= 7) {
|
||||||
|
memcpy(card->ext_csd.fwrev, &ext_csd[EXT_CSD_FIRMWARE_VERSION],
|
||||||
|
MMC_FIRMWARE_LEN);
|
||||||
|
card->ext_csd.ffu_capable =
|
||||||
|
(ext_csd[EXT_CSD_SUPPORTED_MODE] & 0x1) &&
|
||||||
|
!(ext_csd[EXT_CSD_FW_CONFIG] & 0x1);
|
||||||
|
}
|
||||||
out:
|
out:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void mmc_free_ext_csd(u8 *ext_csd)
|
static int mmc_read_ext_csd(struct mmc_card *card)
|
||||||
{
|
{
|
||||||
kfree(ext_csd);
|
u8 *ext_csd;
|
||||||
}
|
int err;
|
||||||
|
|
||||||
|
if (!mmc_can_ext_csd(card))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err = mmc_get_ext_csd(card, &ext_csd);
|
||||||
|
if (err) {
|
||||||
|
/* If the host or the card can't do the switch,
|
||||||
|
* fail more gracefully. */
|
||||||
|
if ((err != -EINVAL)
|
||||||
|
&& (err != -ENOSYS)
|
||||||
|
&& (err != -EFAULT))
|
||||||
|
return err;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* High capacity cards should have this "magic" size
|
||||||
|
* stored in their CSD.
|
||||||
|
*/
|
||||||
|
if (card->csd.capacity == (4096 * 512)) {
|
||||||
|
pr_err("%s: unable to read EXT_CSD on a possible high capacity card. Card will be ignored.\n",
|
||||||
|
mmc_hostname(card->host));
|
||||||
|
} else {
|
||||||
|
pr_warn("%s: unable to read EXT_CSD, performance might suffer\n",
|
||||||
|
mmc_hostname(card->host));
|
||||||
|
err = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = mmc_decode_ext_csd(card, ext_csd);
|
||||||
|
kfree(ext_csd);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static int mmc_compare_ext_csds(struct mmc_card *card, unsigned bus_width)
|
static int mmc_compare_ext_csds(struct mmc_card *card, unsigned bus_width)
|
||||||
{
|
{
|
||||||
@ -647,11 +623,8 @@ static int mmc_compare_ext_csds(struct mmc_card *card, unsigned bus_width)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err = mmc_get_ext_csd(card, &bw_ext_csd);
|
err = mmc_get_ext_csd(card, &bw_ext_csd);
|
||||||
|
if (err)
|
||||||
if (err || bw_ext_csd == NULL) {
|
return err;
|
||||||
err = -EINVAL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* only compare read only fields */
|
/* only compare read only fields */
|
||||||
err = !((card->ext_csd.raw_partition_support ==
|
err = !((card->ext_csd.raw_partition_support ==
|
||||||
@ -710,8 +683,7 @@ static int mmc_compare_ext_csds(struct mmc_card *card, unsigned bus_width)
|
|||||||
if (err)
|
if (err)
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
|
|
||||||
out:
|
kfree(bw_ext_csd);
|
||||||
mmc_free_ext_csd(bw_ext_csd);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -722,7 +694,7 @@ MMC_DEV_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1],
|
|||||||
MMC_DEV_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year);
|
MMC_DEV_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year);
|
||||||
MMC_DEV_ATTR(erase_size, "%u\n", card->erase_size << 9);
|
MMC_DEV_ATTR(erase_size, "%u\n", card->erase_size << 9);
|
||||||
MMC_DEV_ATTR(preferred_erase_size, "%u\n", card->pref_erase << 9);
|
MMC_DEV_ATTR(preferred_erase_size, "%u\n", card->pref_erase << 9);
|
||||||
MMC_DEV_ATTR(fwrev, "0x%x\n", card->cid.fwrev);
|
MMC_DEV_ATTR(ffu_capable, "%d\n", card->ext_csd.ffu_capable);
|
||||||
MMC_DEV_ATTR(hwrev, "0x%x\n", card->cid.hwrev);
|
MMC_DEV_ATTR(hwrev, "0x%x\n", card->cid.hwrev);
|
||||||
MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid);
|
MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid);
|
||||||
MMC_DEV_ATTR(name, "%s\n", card->cid.prod_name);
|
MMC_DEV_ATTR(name, "%s\n", card->cid.prod_name);
|
||||||
@ -735,6 +707,22 @@ MMC_DEV_ATTR(enhanced_area_size, "%u\n", card->ext_csd.enhanced_area_size);
|
|||||||
MMC_DEV_ATTR(raw_rpmb_size_mult, "%#x\n", card->ext_csd.raw_rpmb_size_mult);
|
MMC_DEV_ATTR(raw_rpmb_size_mult, "%#x\n", card->ext_csd.raw_rpmb_size_mult);
|
||||||
MMC_DEV_ATTR(rel_sectors, "%#x\n", card->ext_csd.rel_sectors);
|
MMC_DEV_ATTR(rel_sectors, "%#x\n", card->ext_csd.rel_sectors);
|
||||||
|
|
||||||
|
static ssize_t mmc_fwrev_show(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||||
|
|
||||||
|
if (card->ext_csd.rev < 7) {
|
||||||
|
return sprintf(buf, "0x%x\n", card->cid.fwrev);
|
||||||
|
} else {
|
||||||
|
return sprintf(buf, "0x%*phN\n", MMC_FIRMWARE_LEN,
|
||||||
|
card->ext_csd.fwrev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEVICE_ATTR(fwrev, S_IRUGO, mmc_fwrev_show, NULL);
|
||||||
|
|
||||||
static struct attribute *mmc_std_attrs[] = {
|
static struct attribute *mmc_std_attrs[] = {
|
||||||
&dev_attr_cid.attr,
|
&dev_attr_cid.attr,
|
||||||
&dev_attr_csd.attr,
|
&dev_attr_csd.attr,
|
||||||
@ -742,6 +730,7 @@ static struct attribute *mmc_std_attrs[] = {
|
|||||||
&dev_attr_erase_size.attr,
|
&dev_attr_erase_size.attr,
|
||||||
&dev_attr_preferred_erase_size.attr,
|
&dev_attr_preferred_erase_size.attr,
|
||||||
&dev_attr_fwrev.attr,
|
&dev_attr_fwrev.attr,
|
||||||
|
&dev_attr_ffu_capable.attr,
|
||||||
&dev_attr_hwrev.attr,
|
&dev_attr_hwrev.attr,
|
||||||
&dev_attr_manfid.attr,
|
&dev_attr_manfid.attr,
|
||||||
&dev_attr_name.attr,
|
&dev_attr_name.attr,
|
||||||
@ -774,14 +763,6 @@ static int __mmc_select_powerclass(struct mmc_card *card,
|
|||||||
unsigned int pwrclass_val = 0;
|
unsigned int pwrclass_val = 0;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
/* Power class selection is supported for versions >= 4.0 */
|
|
||||||
if (card->csd.mmca_vsn < CSD_SPEC_VER_4)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Power class values are defined only for 4/8 bit bus */
|
|
||||||
if (bus_width == EXT_CSD_BUS_WIDTH_1)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
switch (1 << host->ios.vdd) {
|
switch (1 << host->ios.vdd) {
|
||||||
case MMC_VDD_165_195:
|
case MMC_VDD_165_195:
|
||||||
if (host->ios.clock <= MMC_HIGH_26_MAX_DTR)
|
if (host->ios.clock <= MMC_HIGH_26_MAX_DTR)
|
||||||
@ -844,7 +825,7 @@ static int mmc_select_powerclass(struct mmc_card *card)
|
|||||||
int err, ddr;
|
int err, ddr;
|
||||||
|
|
||||||
/* Power class selection is supported for versions >= 4.0 */
|
/* Power class selection is supported for versions >= 4.0 */
|
||||||
if (card->csd.mmca_vsn < CSD_SPEC_VER_4)
|
if (!mmc_can_ext_csd(card))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
bus_width = host->ios.bus_width;
|
bus_width = host->ios.bus_width;
|
||||||
@ -905,7 +886,7 @@ static int mmc_select_bus_width(struct mmc_card *card)
|
|||||||
unsigned idx, bus_width = 0;
|
unsigned idx, bus_width = 0;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
if ((card->csd.mmca_vsn < CSD_SPEC_VER_4) &&
|
if (!mmc_can_ext_csd(card) &&
|
||||||
!(host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)))
|
!(host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@ -998,7 +979,7 @@ static int mmc_select_hs_ddr(struct mmc_card *card)
|
|||||||
ext_csd_bits,
|
ext_csd_bits,
|
||||||
card->ext_csd.generic_cmd6_time);
|
card->ext_csd.generic_cmd6_time);
|
||||||
if (err) {
|
if (err) {
|
||||||
pr_warn("%s: switch to bus width %d ddr failed\n",
|
pr_err("%s: switch to bus width %d ddr failed\n",
|
||||||
mmc_hostname(host), 1 << bus_width);
|
mmc_hostname(host), 1 << bus_width);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@ -1069,7 +1050,7 @@ static int mmc_select_hs400(struct mmc_card *card)
|
|||||||
card->ext_csd.generic_cmd6_time,
|
card->ext_csd.generic_cmd6_time,
|
||||||
true, true, true);
|
true, true, true);
|
||||||
if (err) {
|
if (err) {
|
||||||
pr_warn("%s: switch to high-speed from hs200 failed, err:%d\n",
|
pr_err("%s: switch to high-speed from hs200 failed, err:%d\n",
|
||||||
mmc_hostname(host), err);
|
mmc_hostname(host), err);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@ -1079,7 +1060,7 @@ static int mmc_select_hs400(struct mmc_card *card)
|
|||||||
EXT_CSD_DDR_BUS_WIDTH_8,
|
EXT_CSD_DDR_BUS_WIDTH_8,
|
||||||
card->ext_csd.generic_cmd6_time);
|
card->ext_csd.generic_cmd6_time);
|
||||||
if (err) {
|
if (err) {
|
||||||
pr_warn("%s: switch to bus width for hs400 failed, err:%d\n",
|
pr_err("%s: switch to bus width for hs400 failed, err:%d\n",
|
||||||
mmc_hostname(host), err);
|
mmc_hostname(host), err);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@ -1089,7 +1070,7 @@ static int mmc_select_hs400(struct mmc_card *card)
|
|||||||
card->ext_csd.generic_cmd6_time,
|
card->ext_csd.generic_cmd6_time,
|
||||||
true, true, true);
|
true, true, true);
|
||||||
if (err) {
|
if (err) {
|
||||||
pr_warn("%s: switch to hs400 failed, err:%d\n",
|
pr_err("%s: switch to hs400 failed, err:%d\n",
|
||||||
mmc_hostname(host), err);
|
mmc_hostname(host), err);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@ -1146,8 +1127,7 @@ static int mmc_select_timing(struct mmc_card *card)
|
|||||||
{
|
{
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
if ((card->csd.mmca_vsn < CSD_SPEC_VER_4 &&
|
if (!mmc_can_ext_csd(card))
|
||||||
card->ext_csd.hs_max_dtr == 0))
|
|
||||||
goto bus_speed;
|
goto bus_speed;
|
||||||
|
|
||||||
if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200)
|
if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200)
|
||||||
@ -1232,7 +1212,7 @@ static int mmc_hs200_tuning(struct mmc_card *card)
|
|||||||
mmc_host_clk_release(host);
|
mmc_host_clk_release(host);
|
||||||
|
|
||||||
if (err)
|
if (err)
|
||||||
pr_warn("%s: tuning execution failed\n",
|
pr_err("%s: tuning execution failed\n",
|
||||||
mmc_hostname(host));
|
mmc_hostname(host));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1252,7 +1232,6 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
|||||||
int err;
|
int err;
|
||||||
u32 cid[4];
|
u32 cid[4];
|
||||||
u32 rocr;
|
u32 rocr;
|
||||||
u8 *ext_csd = NULL;
|
|
||||||
|
|
||||||
BUG_ON(!host);
|
BUG_ON(!host);
|
||||||
WARN_ON(!host->claimed);
|
WARN_ON(!host->claimed);
|
||||||
@ -1361,14 +1340,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!oldcard) {
|
if (!oldcard) {
|
||||||
/*
|
/* Read extended CSD. */
|
||||||
* Fetch and process extended CSD.
|
err = mmc_read_ext_csd(card);
|
||||||
*/
|
|
||||||
|
|
||||||
err = mmc_get_ext_csd(card, &ext_csd);
|
|
||||||
if (err)
|
|
||||||
goto free_card;
|
|
||||||
err = mmc_read_ext_csd(card, ext_csd);
|
|
||||||
if (err)
|
if (err)
|
||||||
goto free_card;
|
goto free_card;
|
||||||
|
|
||||||
@ -1458,18 +1431,18 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
|||||||
if (mmc_card_hs200(card)) {
|
if (mmc_card_hs200(card)) {
|
||||||
err = mmc_hs200_tuning(card);
|
err = mmc_hs200_tuning(card);
|
||||||
if (err)
|
if (err)
|
||||||
goto err;
|
goto free_card;
|
||||||
|
|
||||||
err = mmc_select_hs400(card);
|
err = mmc_select_hs400(card);
|
||||||
if (err)
|
if (err)
|
||||||
goto err;
|
goto free_card;
|
||||||
} else if (mmc_card_hs(card)) {
|
} else if (mmc_card_hs(card)) {
|
||||||
/* Select the desired bus width optionally */
|
/* Select the desired bus width optionally */
|
||||||
err = mmc_select_bus_width(card);
|
err = mmc_select_bus_width(card);
|
||||||
if (!IS_ERR_VALUE(err)) {
|
if (!IS_ERR_VALUE(err)) {
|
||||||
err = mmc_select_hs_ddr(card);
|
err = mmc_select_hs_ddr(card);
|
||||||
if (err)
|
if (err)
|
||||||
goto err;
|
goto free_card;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1545,15 +1518,12 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
|||||||
if (!oldcard)
|
if (!oldcard)
|
||||||
host->card = card;
|
host->card = card;
|
||||||
|
|
||||||
mmc_free_ext_csd(ext_csd);
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
free_card:
|
free_card:
|
||||||
if (!oldcard)
|
if (!oldcard)
|
||||||
mmc_remove_card(card);
|
mmc_remove_card(card);
|
||||||
err:
|
err:
|
||||||
mmc_free_ext_csd(ext_csd);
|
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,20 +264,6 @@ mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host,
|
|||||||
struct mmc_command cmd = {0};
|
struct mmc_command cmd = {0};
|
||||||
struct mmc_data data = {0};
|
struct mmc_data data = {0};
|
||||||
struct scatterlist sg;
|
struct scatterlist sg;
|
||||||
void *data_buf;
|
|
||||||
int is_on_stack;
|
|
||||||
|
|
||||||
is_on_stack = object_is_on_stack(buf);
|
|
||||||
if (is_on_stack) {
|
|
||||||
/*
|
|
||||||
* dma onto stack is unsafe/nonportable, but callers to this
|
|
||||||
* routine normally provide temporary on-stack buffers ...
|
|
||||||
*/
|
|
||||||
data_buf = kmalloc(len, GFP_KERNEL);
|
|
||||||
if (!data_buf)
|
|
||||||
return -ENOMEM;
|
|
||||||
} else
|
|
||||||
data_buf = buf;
|
|
||||||
|
|
||||||
mrq.cmd = &cmd;
|
mrq.cmd = &cmd;
|
||||||
mrq.data = &data;
|
mrq.data = &data;
|
||||||
@ -298,7 +284,7 @@ mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host,
|
|||||||
data.sg = &sg;
|
data.sg = &sg;
|
||||||
data.sg_len = 1;
|
data.sg_len = 1;
|
||||||
|
|
||||||
sg_init_one(&sg, data_buf, len);
|
sg_init_one(&sg, buf, len);
|
||||||
|
|
||||||
if (opcode == MMC_SEND_CSD || opcode == MMC_SEND_CID) {
|
if (opcode == MMC_SEND_CSD || opcode == MMC_SEND_CID) {
|
||||||
/*
|
/*
|
||||||
@ -312,11 +298,6 @@ mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host,
|
|||||||
|
|
||||||
mmc_wait_for_req(host, &mrq);
|
mmc_wait_for_req(host, &mrq);
|
||||||
|
|
||||||
if (is_on_stack) {
|
|
||||||
memcpy(buf, data_buf, len);
|
|
||||||
kfree(data_buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cmd.error)
|
if (cmd.error)
|
||||||
return cmd.error;
|
return cmd.error;
|
||||||
if (data.error)
|
if (data.error)
|
||||||
@ -334,7 +315,7 @@ int mmc_send_csd(struct mmc_card *card, u32 *csd)
|
|||||||
return mmc_send_cxd_native(card->host, card->rca << 16,
|
return mmc_send_cxd_native(card->host, card->rca << 16,
|
||||||
csd, MMC_SEND_CSD);
|
csd, MMC_SEND_CSD);
|
||||||
|
|
||||||
csd_tmp = kmalloc(16, GFP_KERNEL);
|
csd_tmp = kzalloc(16, GFP_KERNEL);
|
||||||
if (!csd_tmp)
|
if (!csd_tmp)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
@ -362,7 +343,7 @@ int mmc_send_cid(struct mmc_host *host, u32 *cid)
|
|||||||
cid, MMC_SEND_CID);
|
cid, MMC_SEND_CID);
|
||||||
}
|
}
|
||||||
|
|
||||||
cid_tmp = kmalloc(16, GFP_KERNEL);
|
cid_tmp = kzalloc(16, GFP_KERNEL);
|
||||||
if (!cid_tmp)
|
if (!cid_tmp)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
@ -378,12 +359,35 @@ err:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd)
|
||||||
{
|
{
|
||||||
return mmc_send_cxd_data(card, card->host, MMC_SEND_EXT_CSD,
|
int err;
|
||||||
ext_csd, 512);
|
u8 *ext_csd;
|
||||||
|
|
||||||
|
if (!card || !new_ext_csd)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (!mmc_can_ext_csd(card))
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* As the ext_csd is so large and mostly unused, we don't store the
|
||||||
|
* raw block in mmc_card.
|
||||||
|
*/
|
||||||
|
ext_csd = kzalloc(512, GFP_KERNEL);
|
||||||
|
if (!ext_csd)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
err = mmc_send_cxd_data(card, card->host, MMC_SEND_EXT_CSD, ext_csd,
|
||||||
|
512);
|
||||||
|
if (err)
|
||||||
|
kfree(ext_csd);
|
||||||
|
else
|
||||||
|
*new_ext_csd = ext_csd;
|
||||||
|
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(mmc_send_ext_csd);
|
EXPORT_SYMBOL_GPL(mmc_get_ext_csd);
|
||||||
|
|
||||||
int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp)
|
int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp)
|
||||||
{
|
{
|
||||||
@ -543,6 +547,75 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(mmc_switch);
|
EXPORT_SYMBOL_GPL(mmc_switch);
|
||||||
|
|
||||||
|
int mmc_send_tuning(struct mmc_host *host)
|
||||||
|
{
|
||||||
|
struct mmc_request mrq = {NULL};
|
||||||
|
struct mmc_command cmd = {0};
|
||||||
|
struct mmc_data data = {0};
|
||||||
|
struct scatterlist sg;
|
||||||
|
struct mmc_ios *ios = &host->ios;
|
||||||
|
const u8 *tuning_block_pattern;
|
||||||
|
int size, err = 0;
|
||||||
|
u8 *data_buf;
|
||||||
|
u32 opcode;
|
||||||
|
|
||||||
|
if (ios->bus_width == MMC_BUS_WIDTH_8) {
|
||||||
|
tuning_block_pattern = tuning_blk_pattern_8bit;
|
||||||
|
size = sizeof(tuning_blk_pattern_8bit);
|
||||||
|
opcode = MMC_SEND_TUNING_BLOCK_HS200;
|
||||||
|
} else if (ios->bus_width == MMC_BUS_WIDTH_4) {
|
||||||
|
tuning_block_pattern = tuning_blk_pattern_4bit;
|
||||||
|
size = sizeof(tuning_blk_pattern_4bit);
|
||||||
|
opcode = MMC_SEND_TUNING_BLOCK;
|
||||||
|
} else
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
data_buf = kzalloc(size, GFP_KERNEL);
|
||||||
|
if (!data_buf)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
mrq.cmd = &cmd;
|
||||||
|
mrq.data = &data;
|
||||||
|
|
||||||
|
cmd.opcode = opcode;
|
||||||
|
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
|
||||||
|
|
||||||
|
data.blksz = size;
|
||||||
|
data.blocks = 1;
|
||||||
|
data.flags = MMC_DATA_READ;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* According to the tuning specs, Tuning process
|
||||||
|
* is normally shorter 40 executions of CMD19,
|
||||||
|
* and timeout value should be shorter than 150 ms
|
||||||
|
*/
|
||||||
|
data.timeout_ns = 150 * NSEC_PER_MSEC;
|
||||||
|
|
||||||
|
data.sg = &sg;
|
||||||
|
data.sg_len = 1;
|
||||||
|
sg_init_one(&sg, data_buf, size);
|
||||||
|
|
||||||
|
mmc_wait_for_req(host, &mrq);
|
||||||
|
|
||||||
|
if (cmd.error) {
|
||||||
|
err = cmd.error;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.error) {
|
||||||
|
err = data.error;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memcmp(data_buf, tuning_block_pattern, size))
|
||||||
|
err = -EIO;
|
||||||
|
|
||||||
|
out:
|
||||||
|
kfree(data_buf);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(mmc_send_tuning);
|
||||||
|
|
||||||
static int
|
static int
|
||||||
mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode,
|
mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode,
|
||||||
u8 len)
|
u8 len)
|
||||||
@ -675,3 +748,8 @@ int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int mmc_can_ext_csd(struct mmc_card *card)
|
||||||
|
{
|
||||||
|
return (card && card->csd.mmca_vsn > CSD_SPEC_VER_3);
|
||||||
|
}
|
||||||
|
@ -20,13 +20,13 @@ int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
|
|||||||
int mmc_all_send_cid(struct mmc_host *host, u32 *cid);
|
int mmc_all_send_cid(struct mmc_host *host, u32 *cid);
|
||||||
int mmc_set_relative_addr(struct mmc_card *card);
|
int mmc_set_relative_addr(struct mmc_card *card);
|
||||||
int mmc_send_csd(struct mmc_card *card, u32 *csd);
|
int mmc_send_csd(struct mmc_card *card, u32 *csd);
|
||||||
int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd);
|
|
||||||
int mmc_send_status(struct mmc_card *card, u32 *status);
|
int mmc_send_status(struct mmc_card *card, u32 *status);
|
||||||
int mmc_send_cid(struct mmc_host *host, u32 *cid);
|
int mmc_send_cid(struct mmc_host *host, u32 *cid);
|
||||||
int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
|
int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
|
||||||
int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
|
int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
|
||||||
int mmc_bus_test(struct mmc_card *card, u8 bus_width);
|
int mmc_bus_test(struct mmc_card *card, u8 bus_width);
|
||||||
int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status);
|
int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status);
|
||||||
|
int mmc_can_ext_csd(struct mmc_card *card);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -980,8 +980,12 @@ static int mmc_sdio_resume(struct mmc_host *host)
|
|||||||
if (mmc_card_is_removable(host) || !mmc_card_keep_power(host)) {
|
if (mmc_card_is_removable(host) || !mmc_card_keep_power(host)) {
|
||||||
sdio_reset(host);
|
sdio_reset(host);
|
||||||
mmc_go_idle(host);
|
mmc_go_idle(host);
|
||||||
err = mmc_sdio_init_card(host, host->card->ocr, host->card,
|
mmc_send_if_cond(host, host->card->ocr);
|
||||||
mmc_card_keep_power(host));
|
err = mmc_send_io_op_cond(host, 0, NULL);
|
||||||
|
if (!err)
|
||||||
|
err = mmc_sdio_init_card(host, host->card->ocr,
|
||||||
|
host->card,
|
||||||
|
mmc_card_keep_power(host));
|
||||||
} else if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) {
|
} else if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) {
|
||||||
/* We may have switched to 1-bit mode during suspend */
|
/* We may have switched to 1-bit mode during suspend */
|
||||||
err = sdio_enable_4bit_bus(host->card);
|
err = sdio_enable_4bit_bus(host->card);
|
||||||
@ -1035,7 +1039,7 @@ static int mmc_sdio_power_restore(struct mmc_host *host)
|
|||||||
|
|
||||||
sdio_reset(host);
|
sdio_reset(host);
|
||||||
mmc_go_idle(host);
|
mmc_go_idle(host);
|
||||||
mmc_send_if_cond(host, host->ocr_avail);
|
mmc_send_if_cond(host, host->card->ocr);
|
||||||
|
|
||||||
ret = mmc_send_io_op_cond(host, 0, NULL);
|
ret = mmc_send_io_op_cond(host, 0, NULL);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -26,6 +26,8 @@
|
|||||||
#include "sdio_cis.h"
|
#include "sdio_cis.h"
|
||||||
#include "sdio_bus.h"
|
#include "sdio_bus.h"
|
||||||
|
|
||||||
|
#define to_sdio_driver(d) container_of(d, struct sdio_driver, drv)
|
||||||
|
|
||||||
/* show configuration fields */
|
/* show configuration fields */
|
||||||
#define sdio_config_attr(field, format_string) \
|
#define sdio_config_attr(field, format_string) \
|
||||||
static ssize_t \
|
static ssize_t \
|
||||||
@ -196,8 +198,6 @@ static int sdio_bus_remove(struct device *dev)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
|
||||||
|
|
||||||
static const struct dev_pm_ops sdio_bus_pm_ops = {
|
static const struct dev_pm_ops sdio_bus_pm_ops = {
|
||||||
SET_SYSTEM_SLEEP_PM_OPS(pm_generic_suspend, pm_generic_resume)
|
SET_SYSTEM_SLEEP_PM_OPS(pm_generic_suspend, pm_generic_resume)
|
||||||
SET_RUNTIME_PM_OPS(
|
SET_RUNTIME_PM_OPS(
|
||||||
@ -207,14 +207,6 @@ static const struct dev_pm_ops sdio_bus_pm_ops = {
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
#define SDIO_PM_OPS_PTR (&sdio_bus_pm_ops)
|
|
||||||
|
|
||||||
#else /* !CONFIG_PM */
|
|
||||||
|
|
||||||
#define SDIO_PM_OPS_PTR NULL
|
|
||||||
|
|
||||||
#endif /* !CONFIG_PM */
|
|
||||||
|
|
||||||
static struct bus_type sdio_bus_type = {
|
static struct bus_type sdio_bus_type = {
|
||||||
.name = "sdio",
|
.name = "sdio",
|
||||||
.dev_groups = sdio_dev_groups,
|
.dev_groups = sdio_dev_groups,
|
||||||
@ -222,7 +214,7 @@ static struct bus_type sdio_bus_type = {
|
|||||||
.uevent = sdio_bus_uevent,
|
.uevent = sdio_bus_uevent,
|
||||||
.probe = sdio_bus_probe,
|
.probe = sdio_bus_probe,
|
||||||
.remove = sdio_bus_remove,
|
.remove = sdio_bus_remove,
|
||||||
.pm = SDIO_PM_OPS_PTR,
|
.pm = &sdio_bus_pm_ops,
|
||||||
};
|
};
|
||||||
|
|
||||||
int sdio_register_bus(void)
|
int sdio_register_bus(void)
|
||||||
@ -295,7 +287,7 @@ struct sdio_func *sdio_alloc_func(struct mmc_card *card)
|
|||||||
static void sdio_acpi_set_handle(struct sdio_func *func)
|
static void sdio_acpi_set_handle(struct sdio_func *func)
|
||||||
{
|
{
|
||||||
struct mmc_host *host = func->card->host;
|
struct mmc_host *host = func->card->host;
|
||||||
u64 addr = (host->slotno << 16) | func->num;
|
u64 addr = ((u64)host->slotno << 16) | func->num;
|
||||||
|
|
||||||
acpi_preset_companion(&func->dev, ACPI_COMPANION(host->parent), addr);
|
acpi_preset_companion(&func->dev, ACPI_COMPANION(host->parent), addr);
|
||||||
}
|
}
|
||||||
|
@ -580,7 +580,7 @@ config SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND
|
|||||||
config MMC_DW
|
config MMC_DW
|
||||||
tristate "Synopsys DesignWare Memory Card Interface"
|
tristate "Synopsys DesignWare Memory Card Interface"
|
||||||
depends on HAS_DMA
|
depends on HAS_DMA
|
||||||
depends on ARC || ARM || MIPS || COMPILE_TEST
|
depends on ARC || ARM || ARM64 || MIPS || COMPILE_TEST
|
||||||
help
|
help
|
||||||
This selects support for the Synopsys DesignWare Mobile Storage IP
|
This selects support for the Synopsys DesignWare Mobile Storage IP
|
||||||
block, this provides host support for SD and MMC interfaces, in both
|
block, this provides host support for SD and MMC interfaces, in both
|
||||||
@ -748,3 +748,8 @@ config MMC_SUNXI
|
|||||||
help
|
help
|
||||||
This selects support for the SD/MMC Host Controller on
|
This selects support for the SD/MMC Host Controller on
|
||||||
Allwinner sunxi SoCs.
|
Allwinner sunxi SoCs.
|
||||||
|
|
||||||
|
config MMC_TOSHIBA_PCI
|
||||||
|
tristate "Toshiba Type A SD/MMC Card Interface Driver"
|
||||||
|
depends on PCI
|
||||||
|
help
|
||||||
|
@ -55,6 +55,7 @@ obj-$(CONFIG_MMC_WMT) += wmt-sdmmc.o
|
|||||||
obj-$(CONFIG_MMC_MOXART) += moxart-mmc.o
|
obj-$(CONFIG_MMC_MOXART) += moxart-mmc.o
|
||||||
obj-$(CONFIG_MMC_SUNXI) += sunxi-mmc.o
|
obj-$(CONFIG_MMC_SUNXI) += sunxi-mmc.o
|
||||||
obj-$(CONFIG_MMC_USDHI6ROL0) += usdhi6rol0.o
|
obj-$(CONFIG_MMC_USDHI6ROL0) += usdhi6rol0.o
|
||||||
|
obj-$(CONFIG_MMC_TOSHIBA_PCI) += toshsd.o
|
||||||
|
|
||||||
obj-$(CONFIG_MMC_REALTEK_PCI) += rtsx_pci_sdmmc.o
|
obj-$(CONFIG_MMC_REALTEK_PCI) += rtsx_pci_sdmmc.o
|
||||||
obj-$(CONFIG_MMC_REALTEK_USB) += rtsx_usb_sdmmc.o
|
obj-$(CONFIG_MMC_REALTEK_USB) += rtsx_usb_sdmmc.o
|
||||||
|
@ -30,13 +30,16 @@
|
|||||||
#include <linux/stat.h>
|
#include <linux/stat.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/platform_data/atmel.h>
|
#include <linux/platform_data/atmel.h>
|
||||||
|
#include <linux/platform_data/mmc-atmel-mci.h>
|
||||||
|
|
||||||
#include <linux/mmc/host.h>
|
#include <linux/mmc/host.h>
|
||||||
#include <linux/mmc/sdio.h>
|
#include <linux/mmc/sdio.h>
|
||||||
|
|
||||||
#include <mach/atmel-mci.h>
|
|
||||||
#include <linux/atmel-mci.h>
|
#include <linux/atmel-mci.h>
|
||||||
#include <linux/atmel_pdc.h>
|
#include <linux/atmel_pdc.h>
|
||||||
|
#include <linux/pm.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/pinctrl/consumer.h>
|
||||||
|
|
||||||
#include <asm/cacheflush.h>
|
#include <asm/cacheflush.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
@ -44,6 +47,8 @@
|
|||||||
|
|
||||||
#include "atmel-mci-regs.h"
|
#include "atmel-mci-regs.h"
|
||||||
|
|
||||||
|
#define AUTOSUSPEND_DELAY 50
|
||||||
|
|
||||||
#define ATMCI_DATA_ERROR_FLAGS (ATMCI_DCRCE | ATMCI_DTOE | ATMCI_OVRE | ATMCI_UNRE)
|
#define ATMCI_DATA_ERROR_FLAGS (ATMCI_DCRCE | ATMCI_DTOE | ATMCI_OVRE | ATMCI_UNRE)
|
||||||
#define ATMCI_DMA_THRESHOLD 16
|
#define ATMCI_DMA_THRESHOLD 16
|
||||||
|
|
||||||
@ -386,20 +391,19 @@ static int atmci_regs_show(struct seq_file *s, void *v)
|
|||||||
if (!buf)
|
if (!buf)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
pm_runtime_get_sync(&host->pdev->dev);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Grab a more or less consistent snapshot. Note that we're
|
* Grab a more or less consistent snapshot. Note that we're
|
||||||
* not disabling interrupts, so IMR and SR may not be
|
* not disabling interrupts, so IMR and SR may not be
|
||||||
* consistent.
|
* consistent.
|
||||||
*/
|
*/
|
||||||
ret = clk_prepare_enable(host->mck);
|
|
||||||
if (ret)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
spin_lock_bh(&host->lock);
|
spin_lock_bh(&host->lock);
|
||||||
memcpy_fromio(buf, host->regs, ATMCI_REGS_SIZE);
|
memcpy_fromio(buf, host->regs, ATMCI_REGS_SIZE);
|
||||||
spin_unlock_bh(&host->lock);
|
spin_unlock_bh(&host->lock);
|
||||||
|
|
||||||
clk_disable_unprepare(host->mck);
|
pm_runtime_mark_last_busy(&host->pdev->dev);
|
||||||
|
pm_runtime_put_autosuspend(&host->pdev->dev);
|
||||||
|
|
||||||
seq_printf(s, "MR:\t0x%08x%s%s ",
|
seq_printf(s, "MR:\t0x%08x%s%s ",
|
||||||
buf[ATMCI_MR / 4],
|
buf[ATMCI_MR / 4],
|
||||||
@ -449,7 +453,6 @@ static int atmci_regs_show(struct seq_file *s, void *v)
|
|||||||
val & ATMCI_CFG_LSYNC ? " LSYNC" : "");
|
val & ATMCI_CFG_LSYNC ? " LSYNC" : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
|
||||||
kfree(buf);
|
kfree(buf);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -560,6 +563,9 @@ atmci_of_init(struct platform_device *pdev)
|
|||||||
pdata->slot[slot_id].detect_is_active_high =
|
pdata->slot[slot_id].detect_is_active_high =
|
||||||
of_property_read_bool(cnp, "cd-inverted");
|
of_property_read_bool(cnp, "cd-inverted");
|
||||||
|
|
||||||
|
pdata->slot[slot_id].non_removable =
|
||||||
|
of_property_read_bool(cnp, "non-removable");
|
||||||
|
|
||||||
pdata->slot[slot_id].wp_pin =
|
pdata->slot[slot_id].wp_pin =
|
||||||
of_get_named_gpio(cnp, "wp-gpios", 0);
|
of_get_named_gpio(cnp, "wp-gpios", 0);
|
||||||
}
|
}
|
||||||
@ -1252,6 +1258,8 @@ static void atmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||||||
WARN_ON(slot->mrq);
|
WARN_ON(slot->mrq);
|
||||||
dev_dbg(&host->pdev->dev, "MRQ: cmd %u\n", mrq->cmd->opcode);
|
dev_dbg(&host->pdev->dev, "MRQ: cmd %u\n", mrq->cmd->opcode);
|
||||||
|
|
||||||
|
pm_runtime_get_sync(&host->pdev->dev);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We may "know" the card is gone even though there's still an
|
* We may "know" the card is gone even though there's still an
|
||||||
* electrical connection. If so, we really need to communicate
|
* electrical connection. If so, we really need to communicate
|
||||||
@ -1281,7 +1289,8 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||||||
struct atmel_mci_slot *slot = mmc_priv(mmc);
|
struct atmel_mci_slot *slot = mmc_priv(mmc);
|
||||||
struct atmel_mci *host = slot->host;
|
struct atmel_mci *host = slot->host;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
bool unprepare_clk;
|
|
||||||
|
pm_runtime_get_sync(&host->pdev->dev);
|
||||||
|
|
||||||
slot->sdc_reg &= ~ATMCI_SDCBUS_MASK;
|
slot->sdc_reg &= ~ATMCI_SDCBUS_MASK;
|
||||||
switch (ios->bus_width) {
|
switch (ios->bus_width) {
|
||||||
@ -1297,13 +1306,8 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||||||
unsigned int clock_min = ~0U;
|
unsigned int clock_min = ~0U;
|
||||||
u32 clkdiv;
|
u32 clkdiv;
|
||||||
|
|
||||||
clk_prepare(host->mck);
|
|
||||||
unprepare_clk = true;
|
|
||||||
|
|
||||||
spin_lock_bh(&host->lock);
|
spin_lock_bh(&host->lock);
|
||||||
if (!host->mode_reg) {
|
if (!host->mode_reg) {
|
||||||
clk_enable(host->mck);
|
|
||||||
unprepare_clk = false;
|
|
||||||
atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST);
|
atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST);
|
||||||
atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIEN);
|
atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIEN);
|
||||||
if (host->caps.has_cfg_reg)
|
if (host->caps.has_cfg_reg)
|
||||||
@ -1371,8 +1375,6 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||||||
} else {
|
} else {
|
||||||
bool any_slot_active = false;
|
bool any_slot_active = false;
|
||||||
|
|
||||||
unprepare_clk = false;
|
|
||||||
|
|
||||||
spin_lock_bh(&host->lock);
|
spin_lock_bh(&host->lock);
|
||||||
slot->clock = 0;
|
slot->clock = 0;
|
||||||
for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) {
|
for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) {
|
||||||
@ -1385,17 +1387,12 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||||||
atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIDIS);
|
atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIDIS);
|
||||||
if (host->mode_reg) {
|
if (host->mode_reg) {
|
||||||
atmci_readl(host, ATMCI_MR);
|
atmci_readl(host, ATMCI_MR);
|
||||||
clk_disable(host->mck);
|
|
||||||
unprepare_clk = true;
|
|
||||||
}
|
}
|
||||||
host->mode_reg = 0;
|
host->mode_reg = 0;
|
||||||
}
|
}
|
||||||
spin_unlock_bh(&host->lock);
|
spin_unlock_bh(&host->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unprepare_clk)
|
|
||||||
clk_unprepare(host->mck);
|
|
||||||
|
|
||||||
switch (ios->power_mode) {
|
switch (ios->power_mode) {
|
||||||
case MMC_POWER_OFF:
|
case MMC_POWER_OFF:
|
||||||
if (!IS_ERR(mmc->supply.vmmc))
|
if (!IS_ERR(mmc->supply.vmmc))
|
||||||
@ -1421,6 +1418,9 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||||||
*/
|
*/
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pm_runtime_mark_last_busy(&host->pdev->dev);
|
||||||
|
pm_runtime_put_autosuspend(&host->pdev->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int atmci_get_ro(struct mmc_host *mmc)
|
static int atmci_get_ro(struct mmc_host *mmc)
|
||||||
@ -1512,6 +1512,9 @@ static void atmci_request_end(struct atmel_mci *host, struct mmc_request *mrq)
|
|||||||
spin_unlock(&host->lock);
|
spin_unlock(&host->lock);
|
||||||
mmc_request_done(prev_mmc, mrq);
|
mmc_request_done(prev_mmc, mrq);
|
||||||
spin_lock(&host->lock);
|
spin_lock(&host->lock);
|
||||||
|
|
||||||
|
pm_runtime_mark_last_busy(&host->pdev->dev);
|
||||||
|
pm_runtime_put_autosuspend(&host->pdev->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void atmci_command_complete(struct atmel_mci *host,
|
static void atmci_command_complete(struct atmel_mci *host,
|
||||||
@ -2137,7 +2140,7 @@ static irqreturn_t atmci_detect_interrupt(int irq, void *dev_id)
|
|||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init atmci_init_slot(struct atmel_mci *host,
|
static int atmci_init_slot(struct atmel_mci *host,
|
||||||
struct mci_slot_pdata *slot_data, unsigned int id,
|
struct mci_slot_pdata *slot_data, unsigned int id,
|
||||||
u32 sdc_reg, u32 sdio_irq)
|
u32 sdc_reg, u32 sdio_irq)
|
||||||
{
|
{
|
||||||
@ -2206,8 +2209,12 @@ static int __init atmci_init_slot(struct atmel_mci *host,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!gpio_is_valid(slot->detect_pin))
|
if (!gpio_is_valid(slot->detect_pin)) {
|
||||||
mmc->caps |= MMC_CAP_NEEDS_POLL;
|
if (slot_data->non_removable)
|
||||||
|
mmc->caps |= MMC_CAP_NONREMOVABLE;
|
||||||
|
else
|
||||||
|
mmc->caps |= MMC_CAP_NEEDS_POLL;
|
||||||
|
}
|
||||||
|
|
||||||
if (gpio_is_valid(slot->wp_pin)) {
|
if (gpio_is_valid(slot->wp_pin)) {
|
||||||
if (devm_gpio_request(&host->pdev->dev, slot->wp_pin,
|
if (devm_gpio_request(&host->pdev->dev, slot->wp_pin,
|
||||||
@ -2265,55 +2272,25 @@ static void atmci_cleanup_slot(struct atmel_mci_slot *slot,
|
|||||||
mmc_free_host(slot->mmc);
|
mmc_free_host(slot->mmc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool atmci_filter(struct dma_chan *chan, void *pdata)
|
static int atmci_configure_dma(struct atmel_mci *host)
|
||||||
{
|
{
|
||||||
struct mci_platform_data *sl_pdata = pdata;
|
host->dma.chan = dma_request_slave_channel_reason(&host->pdev->dev,
|
||||||
struct mci_dma_data *sl;
|
"rxtx");
|
||||||
|
if (IS_ERR(host->dma.chan))
|
||||||
|
return PTR_ERR(host->dma.chan);
|
||||||
|
|
||||||
if (!sl_pdata)
|
dev_info(&host->pdev->dev, "using %s for DMA transfers\n",
|
||||||
return false;
|
dma_chan_name(host->dma.chan));
|
||||||
|
|
||||||
sl = sl_pdata->dma_slave;
|
host->dma_conf.src_addr = host->mapbase + ATMCI_RDR;
|
||||||
if (sl && find_slave_dev(sl) == chan->device->dev) {
|
host->dma_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||||
chan->private = slave_data_ptr(sl);
|
host->dma_conf.src_maxburst = 1;
|
||||||
return true;
|
host->dma_conf.dst_addr = host->mapbase + ATMCI_TDR;
|
||||||
} else {
|
host->dma_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||||
return false;
|
host->dma_conf.dst_maxburst = 1;
|
||||||
}
|
host->dma_conf.device_fc = false;
|
||||||
}
|
|
||||||
|
|
||||||
static bool atmci_configure_dma(struct atmel_mci *host)
|
return 0;
|
||||||
{
|
|
||||||
struct mci_platform_data *pdata;
|
|
||||||
dma_cap_mask_t mask;
|
|
||||||
|
|
||||||
if (host == NULL)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
pdata = host->pdev->dev.platform_data;
|
|
||||||
|
|
||||||
dma_cap_zero(mask);
|
|
||||||
dma_cap_set(DMA_SLAVE, mask);
|
|
||||||
|
|
||||||
host->dma.chan = dma_request_slave_channel_compat(mask, atmci_filter, pdata,
|
|
||||||
&host->pdev->dev, "rxtx");
|
|
||||||
if (!host->dma.chan) {
|
|
||||||
dev_warn(&host->pdev->dev, "no DMA channel available\n");
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
dev_info(&host->pdev->dev,
|
|
||||||
"using %s for DMA transfers\n",
|
|
||||||
dma_chan_name(host->dma.chan));
|
|
||||||
|
|
||||||
host->dma_conf.src_addr = host->mapbase + ATMCI_RDR;
|
|
||||||
host->dma_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
|
||||||
host->dma_conf.src_maxburst = 1;
|
|
||||||
host->dma_conf.dst_addr = host->mapbase + ATMCI_TDR;
|
|
||||||
host->dma_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
|
||||||
host->dma_conf.dst_maxburst = 1;
|
|
||||||
host->dma_conf.device_fc = false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2321,7 +2298,7 @@ static bool atmci_configure_dma(struct atmel_mci *host)
|
|||||||
* HSMCI provides DMA support and a new config register but no more supports
|
* HSMCI provides DMA support and a new config register but no more supports
|
||||||
* PDC.
|
* PDC.
|
||||||
*/
|
*/
|
||||||
static void __init atmci_get_cap(struct atmel_mci *host)
|
static void atmci_get_cap(struct atmel_mci *host)
|
||||||
{
|
{
|
||||||
unsigned int version;
|
unsigned int version;
|
||||||
|
|
||||||
@ -2370,7 +2347,7 @@ static void __init atmci_get_cap(struct atmel_mci *host)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init atmci_probe(struct platform_device *pdev)
|
static int atmci_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct mci_platform_data *pdata;
|
struct mci_platform_data *pdata;
|
||||||
struct atmel_mci *host;
|
struct atmel_mci *host;
|
||||||
@ -2417,19 +2394,23 @@ static int __init atmci_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST);
|
atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST);
|
||||||
host->bus_hz = clk_get_rate(host->mck);
|
host->bus_hz = clk_get_rate(host->mck);
|
||||||
clk_disable_unprepare(host->mck);
|
|
||||||
|
|
||||||
host->mapbase = regs->start;
|
host->mapbase = regs->start;
|
||||||
|
|
||||||
tasklet_init(&host->tasklet, atmci_tasklet_func, (unsigned long)host);
|
tasklet_init(&host->tasklet, atmci_tasklet_func, (unsigned long)host);
|
||||||
|
|
||||||
ret = request_irq(irq, atmci_interrupt, 0, dev_name(&pdev->dev), host);
|
ret = request_irq(irq, atmci_interrupt, 0, dev_name(&pdev->dev), host);
|
||||||
if (ret)
|
if (ret) {
|
||||||
|
clk_disable_unprepare(host->mck);
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* Get MCI capabilities and set operations according to it */
|
/* Get MCI capabilities and set operations according to it */
|
||||||
atmci_get_cap(host);
|
atmci_get_cap(host);
|
||||||
if (atmci_configure_dma(host)) {
|
ret = atmci_configure_dma(host);
|
||||||
|
if (ret == -EPROBE_DEFER)
|
||||||
|
goto err_dma_probe_defer;
|
||||||
|
if (ret == 0) {
|
||||||
host->prepare_data = &atmci_prepare_data_dma;
|
host->prepare_data = &atmci_prepare_data_dma;
|
||||||
host->submit_data = &atmci_submit_data_dma;
|
host->submit_data = &atmci_submit_data_dma;
|
||||||
host->stop_transfer = &atmci_stop_transfer_dma;
|
host->stop_transfer = &atmci_stop_transfer_dma;
|
||||||
@ -2449,6 +2430,12 @@ static int __init atmci_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
setup_timer(&host->timer, atmci_timeout_timer, (unsigned long)host);
|
setup_timer(&host->timer, atmci_timeout_timer, (unsigned long)host);
|
||||||
|
|
||||||
|
pm_runtime_get_noresume(&pdev->dev);
|
||||||
|
pm_runtime_set_active(&pdev->dev);
|
||||||
|
pm_runtime_set_autosuspend_delay(&pdev->dev, AUTOSUSPEND_DELAY);
|
||||||
|
pm_runtime_use_autosuspend(&pdev->dev);
|
||||||
|
pm_runtime_enable(&pdev->dev);
|
||||||
|
|
||||||
/* We need at least one slot to succeed */
|
/* We need at least one slot to succeed */
|
||||||
nr_slots = 0;
|
nr_slots = 0;
|
||||||
ret = -ENODEV;
|
ret = -ENODEV;
|
||||||
@ -2491,6 +2478,9 @@ static int __init atmci_probe(struct platform_device *pdev)
|
|||||||
"Atmel MCI controller at 0x%08lx irq %d, %u slots\n",
|
"Atmel MCI controller at 0x%08lx irq %d, %u slots\n",
|
||||||
host->mapbase, irq, nr_slots);
|
host->mapbase, irq, nr_slots);
|
||||||
|
|
||||||
|
pm_runtime_mark_last_busy(&host->pdev->dev);
|
||||||
|
pm_runtime_put_autosuspend(&pdev->dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_dma_alloc:
|
err_dma_alloc:
|
||||||
@ -2499,18 +2489,26 @@ err_dma_alloc:
|
|||||||
atmci_cleanup_slot(host->slot[i], i);
|
atmci_cleanup_slot(host->slot[i], i);
|
||||||
}
|
}
|
||||||
err_init_slot:
|
err_init_slot:
|
||||||
|
clk_disable_unprepare(host->mck);
|
||||||
|
|
||||||
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
pm_runtime_put_noidle(&pdev->dev);
|
||||||
|
|
||||||
del_timer_sync(&host->timer);
|
del_timer_sync(&host->timer);
|
||||||
if (host->dma.chan)
|
if (!IS_ERR(host->dma.chan))
|
||||||
dma_release_channel(host->dma.chan);
|
dma_release_channel(host->dma.chan);
|
||||||
|
err_dma_probe_defer:
|
||||||
free_irq(irq, host);
|
free_irq(irq, host);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __exit atmci_remove(struct platform_device *pdev)
|
static int atmci_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct atmel_mci *host = platform_get_drvdata(pdev);
|
struct atmel_mci *host = platform_get_drvdata(pdev);
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
|
pm_runtime_get_sync(&pdev->dev);
|
||||||
|
|
||||||
if (host->buffer)
|
if (host->buffer)
|
||||||
dma_free_coherent(&pdev->dev, host->buf_size,
|
dma_free_coherent(&pdev->dev, host->buf_size,
|
||||||
host->buffer, host->buf_phys_addr);
|
host->buffer, host->buf_phys_addr);
|
||||||
@ -2520,41 +2518,62 @@ static int __exit atmci_remove(struct platform_device *pdev)
|
|||||||
atmci_cleanup_slot(host->slot[i], i);
|
atmci_cleanup_slot(host->slot[i], i);
|
||||||
}
|
}
|
||||||
|
|
||||||
clk_prepare_enable(host->mck);
|
|
||||||
atmci_writel(host, ATMCI_IDR, ~0UL);
|
atmci_writel(host, ATMCI_IDR, ~0UL);
|
||||||
atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIDIS);
|
atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIDIS);
|
||||||
atmci_readl(host, ATMCI_SR);
|
atmci_readl(host, ATMCI_SR);
|
||||||
clk_disable_unprepare(host->mck);
|
|
||||||
|
|
||||||
del_timer_sync(&host->timer);
|
del_timer_sync(&host->timer);
|
||||||
if (host->dma.chan)
|
if (!IS_ERR(host->dma.chan))
|
||||||
dma_release_channel(host->dma.chan);
|
dma_release_channel(host->dma.chan);
|
||||||
|
|
||||||
free_irq(platform_get_irq(pdev, 0), host);
|
free_irq(platform_get_irq(pdev, 0), host);
|
||||||
|
|
||||||
|
clk_disable_unprepare(host->mck);
|
||||||
|
|
||||||
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
pm_runtime_put_noidle(&pdev->dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
static int atmci_runtime_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct atmel_mci *host = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
clk_disable_unprepare(host->mck);
|
||||||
|
|
||||||
|
pinctrl_pm_select_sleep_state(dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int atmci_runtime_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct atmel_mci *host = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
pinctrl_pm_select_default_state(dev);
|
||||||
|
|
||||||
|
return clk_prepare_enable(host->mck);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const struct dev_pm_ops atmci_dev_pm_ops = {
|
||||||
|
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||||
|
pm_runtime_force_resume)
|
||||||
|
SET_PM_RUNTIME_PM_OPS(atmci_runtime_suspend, atmci_runtime_resume, NULL)
|
||||||
|
};
|
||||||
|
|
||||||
static struct platform_driver atmci_driver = {
|
static struct platform_driver atmci_driver = {
|
||||||
.remove = __exit_p(atmci_remove),
|
.probe = atmci_probe,
|
||||||
|
.remove = atmci_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "atmel_mci",
|
.name = "atmel_mci",
|
||||||
.of_match_table = of_match_ptr(atmci_dt_ids),
|
.of_match_table = of_match_ptr(atmci_dt_ids),
|
||||||
|
.pm = &atmci_dev_pm_ops,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
module_platform_driver(atmci_driver);
|
||||||
static int __init atmci_init(void)
|
|
||||||
{
|
|
||||||
return platform_driver_probe(&atmci_driver, atmci_probe);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __exit atmci_exit(void)
|
|
||||||
{
|
|
||||||
platform_driver_unregister(&atmci_driver);
|
|
||||||
}
|
|
||||||
|
|
||||||
late_initcall(atmci_init); /* try to load after dma driver when built-in */
|
|
||||||
module_exit(atmci_exit);
|
|
||||||
|
|
||||||
MODULE_DESCRIPTION("Atmel Multimedia Card Interface driver");
|
MODULE_DESCRIPTION("Atmel Multimedia Card Interface driver");
|
||||||
MODULE_AUTHOR("Haavard Skinnemoen (Atmel)");
|
MODULE_AUTHOR("Haavard Skinnemoen (Atmel)");
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#define NUM_PINS(x) (x + 2)
|
#define NUM_PINS(x) (x + 2)
|
||||||
|
|
||||||
#define SDMMC_CLKSEL 0x09C
|
#define SDMMC_CLKSEL 0x09C
|
||||||
|
#define SDMMC_CLKSEL64 0x0A8
|
||||||
#define SDMMC_CLKSEL_CCLK_SAMPLE(x) (((x) & 7) << 0)
|
#define SDMMC_CLKSEL_CCLK_SAMPLE(x) (((x) & 7) << 0)
|
||||||
#define SDMMC_CLKSEL_CCLK_DRIVE(x) (((x) & 7) << 16)
|
#define SDMMC_CLKSEL_CCLK_DRIVE(x) (((x) & 7) << 16)
|
||||||
#define SDMMC_CLKSEL_CCLK_DIVIDER(x) (((x) & 7) << 24)
|
#define SDMMC_CLKSEL_CCLK_DIVIDER(x) (((x) & 7) << 24)
|
||||||
@ -65,6 +66,8 @@ enum dw_mci_exynos_type {
|
|||||||
DW_MCI_TYPE_EXYNOS5250,
|
DW_MCI_TYPE_EXYNOS5250,
|
||||||
DW_MCI_TYPE_EXYNOS5420,
|
DW_MCI_TYPE_EXYNOS5420,
|
||||||
DW_MCI_TYPE_EXYNOS5420_SMU,
|
DW_MCI_TYPE_EXYNOS5420_SMU,
|
||||||
|
DW_MCI_TYPE_EXYNOS7,
|
||||||
|
DW_MCI_TYPE_EXYNOS7_SMU,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Exynos implementation specific driver private data */
|
/* Exynos implementation specific driver private data */
|
||||||
@ -95,6 +98,12 @@ static struct dw_mci_exynos_compatible {
|
|||||||
}, {
|
}, {
|
||||||
.compatible = "samsung,exynos5420-dw-mshc-smu",
|
.compatible = "samsung,exynos5420-dw-mshc-smu",
|
||||||
.ctrl_type = DW_MCI_TYPE_EXYNOS5420_SMU,
|
.ctrl_type = DW_MCI_TYPE_EXYNOS5420_SMU,
|
||||||
|
}, {
|
||||||
|
.compatible = "samsung,exynos7-dw-mshc",
|
||||||
|
.ctrl_type = DW_MCI_TYPE_EXYNOS7,
|
||||||
|
}, {
|
||||||
|
.compatible = "samsung,exynos7-dw-mshc-smu",
|
||||||
|
.ctrl_type = DW_MCI_TYPE_EXYNOS7_SMU,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -102,7 +111,8 @@ static int dw_mci_exynos_priv_init(struct dw_mci *host)
|
|||||||
{
|
{
|
||||||
struct dw_mci_exynos_priv_data *priv = host->priv;
|
struct dw_mci_exynos_priv_data *priv = host->priv;
|
||||||
|
|
||||||
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) {
|
||||||
mci_writel(host, MPSBEGIN0, 0);
|
mci_writel(host, MPSBEGIN0, 0);
|
||||||
mci_writel(host, MPSEND0, DWMCI_BLOCK_NUM);
|
mci_writel(host, MPSEND0, DWMCI_BLOCK_NUM);
|
||||||
mci_writel(host, MPSCTRL0, DWMCI_MPSCTRL_SECURE_WRITE_BIT |
|
mci_writel(host, MPSCTRL0, DWMCI_MPSCTRL_SECURE_WRITE_BIT |
|
||||||
@ -153,11 +163,22 @@ static int dw_mci_exynos_resume(struct device *dev)
|
|||||||
static int dw_mci_exynos_resume_noirq(struct device *dev)
|
static int dw_mci_exynos_resume_noirq(struct device *dev)
|
||||||
{
|
{
|
||||||
struct dw_mci *host = dev_get_drvdata(dev);
|
struct dw_mci *host = dev_get_drvdata(dev);
|
||||||
|
struct dw_mci_exynos_priv_data *priv = host->priv;
|
||||||
u32 clksel;
|
u32 clksel;
|
||||||
|
|
||||||
clksel = mci_readl(host, CLKSEL);
|
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
|
||||||
if (clksel & SDMMC_CLKSEL_WAKEUP_INT)
|
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
|
||||||
mci_writel(host, CLKSEL, clksel);
|
clksel = mci_readl(host, CLKSEL64);
|
||||||
|
else
|
||||||
|
clksel = mci_readl(host, CLKSEL);
|
||||||
|
|
||||||
|
if (clksel & SDMMC_CLKSEL_WAKEUP_INT) {
|
||||||
|
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
|
||||||
|
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
|
||||||
|
mci_writel(host, CLKSEL64, clksel);
|
||||||
|
else
|
||||||
|
mci_writel(host, CLKSEL, clksel);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -169,6 +190,7 @@ static int dw_mci_exynos_resume_noirq(struct device *dev)
|
|||||||
|
|
||||||
static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr)
|
static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr)
|
||||||
{
|
{
|
||||||
|
struct dw_mci_exynos_priv_data *priv = host->priv;
|
||||||
/*
|
/*
|
||||||
* Exynos4412 and Exynos5250 extends the use of CMD register with the
|
* Exynos4412 and Exynos5250 extends the use of CMD register with the
|
||||||
* use of bit 29 (which is reserved on standard MSHC controllers) for
|
* use of bit 29 (which is reserved on standard MSHC controllers) for
|
||||||
@ -176,8 +198,14 @@ static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr)
|
|||||||
* HOLD register should be bypassed in case there is no phase shift
|
* HOLD register should be bypassed in case there is no phase shift
|
||||||
* applied on CMD/DATA that is sent to the card.
|
* applied on CMD/DATA that is sent to the card.
|
||||||
*/
|
*/
|
||||||
if (SDMMC_CLKSEL_GET_DRV_WD3(mci_readl(host, CLKSEL)))
|
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
|
||||||
*cmdr |= SDMMC_CMD_USE_HOLD_REG;
|
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) {
|
||||||
|
if (SDMMC_CLKSEL_GET_DRV_WD3(mci_readl(host, CLKSEL64)))
|
||||||
|
*cmdr |= SDMMC_CMD_USE_HOLD_REG;
|
||||||
|
} else {
|
||||||
|
if (SDMMC_CLKSEL_GET_DRV_WD3(mci_readl(host, CLKSEL)))
|
||||||
|
*cmdr |= SDMMC_CMD_USE_HOLD_REG;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios)
|
static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios)
|
||||||
@ -188,12 +216,20 @@ static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios)
|
|||||||
u8 div = priv->ciu_div + 1;
|
u8 div = priv->ciu_div + 1;
|
||||||
|
|
||||||
if (ios->timing == MMC_TIMING_MMC_DDR52) {
|
if (ios->timing == MMC_TIMING_MMC_DDR52) {
|
||||||
mci_writel(host, CLKSEL, priv->ddr_timing);
|
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
|
||||||
|
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
|
||||||
|
mci_writel(host, CLKSEL64, priv->ddr_timing);
|
||||||
|
else
|
||||||
|
mci_writel(host, CLKSEL, priv->ddr_timing);
|
||||||
/* Should be double rate for DDR mode */
|
/* Should be double rate for DDR mode */
|
||||||
if (ios->bus_width == MMC_BUS_WIDTH_8)
|
if (ios->bus_width == MMC_BUS_WIDTH_8)
|
||||||
wanted <<= 1;
|
wanted <<= 1;
|
||||||
} else {
|
} else {
|
||||||
mci_writel(host, CLKSEL, priv->sdr_timing);
|
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
|
||||||
|
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
|
||||||
|
mci_writel(host, CLKSEL64, priv->sdr_timing);
|
||||||
|
else
|
||||||
|
mci_writel(host, CLKSEL, priv->sdr_timing);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Don't care if wanted clock is zero */
|
/* Don't care if wanted clock is zero */
|
||||||
@ -265,26 +301,51 @@ static int dw_mci_exynos_parse_dt(struct dw_mci *host)
|
|||||||
|
|
||||||
static inline u8 dw_mci_exynos_get_clksmpl(struct dw_mci *host)
|
static inline u8 dw_mci_exynos_get_clksmpl(struct dw_mci *host)
|
||||||
{
|
{
|
||||||
return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL));
|
struct dw_mci_exynos_priv_data *priv = host->priv;
|
||||||
|
|
||||||
|
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
|
||||||
|
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
|
||||||
|
return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL64));
|
||||||
|
else
|
||||||
|
return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample)
|
static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample)
|
||||||
{
|
{
|
||||||
u32 clksel;
|
u32 clksel;
|
||||||
clksel = mci_readl(host, CLKSEL);
|
struct dw_mci_exynos_priv_data *priv = host->priv;
|
||||||
|
|
||||||
|
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
|
||||||
|
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
|
||||||
|
clksel = mci_readl(host, CLKSEL64);
|
||||||
|
else
|
||||||
|
clksel = mci_readl(host, CLKSEL);
|
||||||
clksel = (clksel & ~0x7) | SDMMC_CLKSEL_CCLK_SAMPLE(sample);
|
clksel = (clksel & ~0x7) | SDMMC_CLKSEL_CCLK_SAMPLE(sample);
|
||||||
mci_writel(host, CLKSEL, clksel);
|
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
|
||||||
|
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
|
||||||
|
mci_writel(host, CLKSEL64, clksel);
|
||||||
|
else
|
||||||
|
mci_writel(host, CLKSEL, clksel);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host)
|
static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host)
|
||||||
{
|
{
|
||||||
|
struct dw_mci_exynos_priv_data *priv = host->priv;
|
||||||
u32 clksel;
|
u32 clksel;
|
||||||
u8 sample;
|
u8 sample;
|
||||||
|
|
||||||
clksel = mci_readl(host, CLKSEL);
|
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
|
||||||
|
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
|
||||||
|
clksel = mci_readl(host, CLKSEL64);
|
||||||
|
else
|
||||||
|
clksel = mci_readl(host, CLKSEL);
|
||||||
sample = (clksel + 1) & 0x7;
|
sample = (clksel + 1) & 0x7;
|
||||||
clksel = (clksel & ~0x7) | sample;
|
clksel = (clksel & ~0x7) | sample;
|
||||||
mci_writel(host, CLKSEL, clksel);
|
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
|
||||||
|
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
|
||||||
|
mci_writel(host, CLKSEL64, clksel);
|
||||||
|
else
|
||||||
|
mci_writel(host, CLKSEL, clksel);
|
||||||
return sample;
|
return sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -411,6 +472,10 @@ static const struct of_device_id dw_mci_exynos_match[] = {
|
|||||||
.data = &exynos_drv_data, },
|
.data = &exynos_drv_data, },
|
||||||
{ .compatible = "samsung,exynos5420-dw-mshc-smu",
|
{ .compatible = "samsung,exynos5420-dw-mshc-smu",
|
||||||
.data = &exynos_drv_data, },
|
.data = &exynos_drv_data, },
|
||||||
|
{ .compatible = "samsung,exynos7-dw-mshc",
|
||||||
|
.data = &exynos_drv_data, },
|
||||||
|
{ .compatible = "samsung,exynos7-dw-mshc-smu",
|
||||||
|
.data = &exynos_drv_data, },
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, dw_mci_exynos_match);
|
MODULE_DEVICE_TABLE(of, dw_mci_exynos_match);
|
||||||
|
@ -35,6 +35,10 @@ static const struct dw_mci_drv_data socfpga_drv_data = {
|
|||||||
.prepare_command = dw_mci_pltfm_prepare_command,
|
.prepare_command = dw_mci_pltfm_prepare_command,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct dw_mci_drv_data pistachio_drv_data = {
|
||||||
|
.prepare_command = dw_mci_pltfm_prepare_command,
|
||||||
|
};
|
||||||
|
|
||||||
int dw_mci_pltfm_register(struct platform_device *pdev,
|
int dw_mci_pltfm_register(struct platform_device *pdev,
|
||||||
const struct dw_mci_drv_data *drv_data)
|
const struct dw_mci_drv_data *drv_data)
|
||||||
{
|
{
|
||||||
@ -90,6 +94,8 @@ static const struct of_device_id dw_mci_pltfm_match[] = {
|
|||||||
{ .compatible = "snps,dw-mshc", },
|
{ .compatible = "snps,dw-mshc", },
|
||||||
{ .compatible = "altr,socfpga-dw-mshc",
|
{ .compatible = "altr,socfpga-dw-mshc",
|
||||||
.data = &socfpga_drv_data },
|
.data = &socfpga_drv_data },
|
||||||
|
{ .compatible = "img,pistachio-dw-mshc",
|
||||||
|
.data = &pistachio_drv_data },
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match);
|
MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match);
|
||||||
|
@ -37,6 +37,9 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios)
|
|||||||
unsigned int cclkin;
|
unsigned int cclkin;
|
||||||
u32 bus_hz;
|
u32 bus_hz;
|
||||||
|
|
||||||
|
if (ios->clock == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* cclkin: source clock of mmc controller
|
* cclkin: source clock of mmc controller
|
||||||
* bus_hz: card interface clock generated by CLKGEN
|
* bus_hz: card interface clock generated by CLKGEN
|
||||||
@ -65,14 +68,24 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int dw_mci_rockchip_init(struct dw_mci *host)
|
||||||
|
{
|
||||||
|
/* It is slot 8 on Rockchip SoCs */
|
||||||
|
host->sdio_id0 = 8;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct dw_mci_drv_data rk2928_drv_data = {
|
static const struct dw_mci_drv_data rk2928_drv_data = {
|
||||||
.prepare_command = dw_mci_rockchip_prepare_command,
|
.prepare_command = dw_mci_rockchip_prepare_command,
|
||||||
|
.init = dw_mci_rockchip_init,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct dw_mci_drv_data rk3288_drv_data = {
|
static const struct dw_mci_drv_data rk3288_drv_data = {
|
||||||
.prepare_command = dw_mci_rockchip_prepare_command,
|
.prepare_command = dw_mci_rockchip_prepare_command,
|
||||||
.set_ios = dw_mci_rk3288_set_ios,
|
.set_ios = dw_mci_rk3288_set_ios,
|
||||||
.setup_clock = dw_mci_rk3288_setup_clock,
|
.setup_clock = dw_mci_rk3288_setup_clock,
|
||||||
|
.init = dw_mci_rockchip_init,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id dw_mci_rockchip_match[] = {
|
static const struct of_device_id dw_mci_rockchip_match[] = {
|
||||||
|
@ -34,7 +34,6 @@
|
|||||||
#include <linux/mmc/dw_mmc.h>
|
#include <linux/mmc/dw_mmc.h>
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/workqueue.h>
|
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_gpio.h>
|
#include <linux/of_gpio.h>
|
||||||
#include <linux/mmc/slot-gpio.h>
|
#include <linux/mmc/slot-gpio.h>
|
||||||
@ -62,6 +61,24 @@
|
|||||||
SDMMC_IDMAC_INT_FBE | SDMMC_IDMAC_INT_RI | \
|
SDMMC_IDMAC_INT_FBE | SDMMC_IDMAC_INT_RI | \
|
||||||
SDMMC_IDMAC_INT_TI)
|
SDMMC_IDMAC_INT_TI)
|
||||||
|
|
||||||
|
struct idmac_desc_64addr {
|
||||||
|
u32 des0; /* Control Descriptor */
|
||||||
|
|
||||||
|
u32 des1; /* Reserved */
|
||||||
|
|
||||||
|
u32 des2; /*Buffer sizes */
|
||||||
|
#define IDMAC_64ADDR_SET_BUFFER1_SIZE(d, s) \
|
||||||
|
((d)->des2 = ((d)->des2 & 0x03ffe000) | ((s) & 0x1fff))
|
||||||
|
|
||||||
|
u32 des3; /* Reserved */
|
||||||
|
|
||||||
|
u32 des4; /* Lower 32-bits of Buffer Address Pointer 1*/
|
||||||
|
u32 des5; /* Upper 32-bits of Buffer Address Pointer 1*/
|
||||||
|
|
||||||
|
u32 des6; /* Lower 32-bits of Next Descriptor Address */
|
||||||
|
u32 des7; /* Upper 32-bits of Next Descriptor Address */
|
||||||
|
};
|
||||||
|
|
||||||
struct idmac_desc {
|
struct idmac_desc {
|
||||||
u32 des0; /* Control Descriptor */
|
u32 des0; /* Control Descriptor */
|
||||||
#define IDMAC_DES0_DIC BIT(1)
|
#define IDMAC_DES0_DIC BIT(1)
|
||||||
@ -83,6 +100,7 @@ struct idmac_desc {
|
|||||||
#endif /* CONFIG_MMC_DW_IDMAC */
|
#endif /* CONFIG_MMC_DW_IDMAC */
|
||||||
|
|
||||||
static bool dw_mci_reset(struct dw_mci *host);
|
static bool dw_mci_reset(struct dw_mci *host);
|
||||||
|
static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset);
|
||||||
|
|
||||||
#if defined(CONFIG_DEBUG_FS)
|
#if defined(CONFIG_DEBUG_FS)
|
||||||
static int dw_mci_req_show(struct seq_file *s, void *v)
|
static int dw_mci_req_show(struct seq_file *s, void *v)
|
||||||
@ -414,31 +432,67 @@ static void dw_mci_translate_sglist(struct dw_mci *host, struct mmc_data *data,
|
|||||||
unsigned int sg_len)
|
unsigned int sg_len)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
struct idmac_desc *desc = host->sg_cpu;
|
if (host->dma_64bit_address == 1) {
|
||||||
|
struct idmac_desc_64addr *desc = host->sg_cpu;
|
||||||
|
|
||||||
for (i = 0; i < sg_len; i++, desc++) {
|
for (i = 0; i < sg_len; i++, desc++) {
|
||||||
unsigned int length = sg_dma_len(&data->sg[i]);
|
unsigned int length = sg_dma_len(&data->sg[i]);
|
||||||
u32 mem_addr = sg_dma_address(&data->sg[i]);
|
u64 mem_addr = sg_dma_address(&data->sg[i]);
|
||||||
|
|
||||||
/* Set the OWN bit and disable interrupts for this descriptor */
|
/*
|
||||||
desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | IDMAC_DES0_CH;
|
* Set the OWN bit and disable interrupts for this
|
||||||
|
* descriptor
|
||||||
|
*/
|
||||||
|
desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC |
|
||||||
|
IDMAC_DES0_CH;
|
||||||
|
/* Buffer length */
|
||||||
|
IDMAC_64ADDR_SET_BUFFER1_SIZE(desc, length);
|
||||||
|
|
||||||
/* Buffer length */
|
/* Physical address to DMA to/from */
|
||||||
IDMAC_SET_BUFFER1_SIZE(desc, length);
|
desc->des4 = mem_addr & 0xffffffff;
|
||||||
|
desc->des5 = mem_addr >> 32;
|
||||||
|
}
|
||||||
|
|
||||||
/* Physical address to DMA to/from */
|
/* Set first descriptor */
|
||||||
desc->des2 = mem_addr;
|
desc = host->sg_cpu;
|
||||||
|
desc->des0 |= IDMAC_DES0_FD;
|
||||||
|
|
||||||
|
/* Set last descriptor */
|
||||||
|
desc = host->sg_cpu + (i - 1) *
|
||||||
|
sizeof(struct idmac_desc_64addr);
|
||||||
|
desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC);
|
||||||
|
desc->des0 |= IDMAC_DES0_LD;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
struct idmac_desc *desc = host->sg_cpu;
|
||||||
|
|
||||||
|
for (i = 0; i < sg_len; i++, desc++) {
|
||||||
|
unsigned int length = sg_dma_len(&data->sg[i]);
|
||||||
|
u32 mem_addr = sg_dma_address(&data->sg[i]);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the OWN bit and disable interrupts for this
|
||||||
|
* descriptor
|
||||||
|
*/
|
||||||
|
desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC |
|
||||||
|
IDMAC_DES0_CH;
|
||||||
|
/* Buffer length */
|
||||||
|
IDMAC_SET_BUFFER1_SIZE(desc, length);
|
||||||
|
|
||||||
|
/* Physical address to DMA to/from */
|
||||||
|
desc->des2 = mem_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set first descriptor */
|
||||||
|
desc = host->sg_cpu;
|
||||||
|
desc->des0 |= IDMAC_DES0_FD;
|
||||||
|
|
||||||
|
/* Set last descriptor */
|
||||||
|
desc = host->sg_cpu + (i - 1) * sizeof(struct idmac_desc);
|
||||||
|
desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC);
|
||||||
|
desc->des0 |= IDMAC_DES0_LD;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set first descriptor */
|
|
||||||
desc = host->sg_cpu;
|
|
||||||
desc->des0 |= IDMAC_DES0_FD;
|
|
||||||
|
|
||||||
/* Set last descriptor */
|
|
||||||
desc = host->sg_cpu + (i - 1) * sizeof(struct idmac_desc);
|
|
||||||
desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC);
|
|
||||||
desc->des0 |= IDMAC_DES0_LD;
|
|
||||||
|
|
||||||
wmb();
|
wmb();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -448,6 +502,10 @@ static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len)
|
|||||||
|
|
||||||
dw_mci_translate_sglist(host, host->data, sg_len);
|
dw_mci_translate_sglist(host, host->data, sg_len);
|
||||||
|
|
||||||
|
/* Make sure to reset DMA in case we did PIO before this */
|
||||||
|
dw_mci_ctrl_reset(host, SDMMC_CTRL_DMA_RESET);
|
||||||
|
dw_mci_idmac_reset(host);
|
||||||
|
|
||||||
/* Select IDMAC interface */
|
/* Select IDMAC interface */
|
||||||
temp = mci_readl(host, CTRL);
|
temp = mci_readl(host, CTRL);
|
||||||
temp |= SDMMC_CTRL_USE_IDMAC;
|
temp |= SDMMC_CTRL_USE_IDMAC;
|
||||||
@ -466,29 +524,71 @@ static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len)
|
|||||||
|
|
||||||
static int dw_mci_idmac_init(struct dw_mci *host)
|
static int dw_mci_idmac_init(struct dw_mci *host)
|
||||||
{
|
{
|
||||||
struct idmac_desc *p;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* Number of descriptors in the ring buffer */
|
if (host->dma_64bit_address == 1) {
|
||||||
host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc);
|
struct idmac_desc_64addr *p;
|
||||||
|
/* Number of descriptors in the ring buffer */
|
||||||
|
host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc_64addr);
|
||||||
|
|
||||||
/* Forward link the descriptor list */
|
/* Forward link the descriptor list */
|
||||||
for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++)
|
for (i = 0, p = host->sg_cpu; i < host->ring_size - 1;
|
||||||
p->des3 = host->sg_dma + (sizeof(struct idmac_desc) * (i + 1));
|
i++, p++) {
|
||||||
|
p->des6 = (host->sg_dma +
|
||||||
|
(sizeof(struct idmac_desc_64addr) *
|
||||||
|
(i + 1))) & 0xffffffff;
|
||||||
|
|
||||||
/* Set the last descriptor as the end-of-ring descriptor */
|
p->des7 = (u64)(host->sg_dma +
|
||||||
p->des3 = host->sg_dma;
|
(sizeof(struct idmac_desc_64addr) *
|
||||||
p->des0 = IDMAC_DES0_ER;
|
(i + 1))) >> 32;
|
||||||
|
/* Initialize reserved and buffer size fields to "0" */
|
||||||
|
p->des1 = 0;
|
||||||
|
p->des2 = 0;
|
||||||
|
p->des3 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the last descriptor as the end-of-ring descriptor */
|
||||||
|
p->des6 = host->sg_dma & 0xffffffff;
|
||||||
|
p->des7 = (u64)host->sg_dma >> 32;
|
||||||
|
p->des0 = IDMAC_DES0_ER;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
struct idmac_desc *p;
|
||||||
|
/* Number of descriptors in the ring buffer */
|
||||||
|
host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc);
|
||||||
|
|
||||||
|
/* Forward link the descriptor list */
|
||||||
|
for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++)
|
||||||
|
p->des3 = host->sg_dma + (sizeof(struct idmac_desc) *
|
||||||
|
(i + 1));
|
||||||
|
|
||||||
|
/* Set the last descriptor as the end-of-ring descriptor */
|
||||||
|
p->des3 = host->sg_dma;
|
||||||
|
p->des0 = IDMAC_DES0_ER;
|
||||||
|
}
|
||||||
|
|
||||||
dw_mci_idmac_reset(host);
|
dw_mci_idmac_reset(host);
|
||||||
|
|
||||||
/* Mask out interrupts - get Tx & Rx complete only */
|
if (host->dma_64bit_address == 1) {
|
||||||
mci_writel(host, IDSTS, IDMAC_INT_CLR);
|
/* Mask out interrupts - get Tx & Rx complete only */
|
||||||
mci_writel(host, IDINTEN, SDMMC_IDMAC_INT_NI | SDMMC_IDMAC_INT_RI |
|
mci_writel(host, IDSTS64, IDMAC_INT_CLR);
|
||||||
SDMMC_IDMAC_INT_TI);
|
mci_writel(host, IDINTEN64, SDMMC_IDMAC_INT_NI |
|
||||||
|
SDMMC_IDMAC_INT_RI | SDMMC_IDMAC_INT_TI);
|
||||||
|
|
||||||
|
/* Set the descriptor base address */
|
||||||
|
mci_writel(host, DBADDRL, host->sg_dma & 0xffffffff);
|
||||||
|
mci_writel(host, DBADDRU, (u64)host->sg_dma >> 32);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
/* Mask out interrupts - get Tx & Rx complete only */
|
||||||
|
mci_writel(host, IDSTS, IDMAC_INT_CLR);
|
||||||
|
mci_writel(host, IDINTEN, SDMMC_IDMAC_INT_NI |
|
||||||
|
SDMMC_IDMAC_INT_RI | SDMMC_IDMAC_INT_TI);
|
||||||
|
|
||||||
|
/* Set the descriptor base address */
|
||||||
|
mci_writel(host, DBADDR, host->sg_dma);
|
||||||
|
}
|
||||||
|
|
||||||
/* Set the descriptor base address */
|
|
||||||
mci_writel(host, DBADDR, host->sg_dma);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -626,6 +726,13 @@ static void dw_mci_ctrl_rd_thld(struct dw_mci *host, struct mmc_data *data)
|
|||||||
|
|
||||||
WARN_ON(!(data->flags & MMC_DATA_READ));
|
WARN_ON(!(data->flags & MMC_DATA_READ));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CDTHRCTL doesn't exist prior to 240A (in fact that register offset is
|
||||||
|
* in the FIFO region, so we really shouldn't access it).
|
||||||
|
*/
|
||||||
|
if (host->verid < DW_MMC_240A)
|
||||||
|
return;
|
||||||
|
|
||||||
if (host->timing != MMC_TIMING_MMC_HS200 &&
|
if (host->timing != MMC_TIMING_MMC_HS200 &&
|
||||||
host->timing != MMC_TIMING_UHS_SDR104)
|
host->timing != MMC_TIMING_UHS_SDR104)
|
||||||
goto disable;
|
goto disable;
|
||||||
@ -819,7 +926,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->id)))
|
if (!(mci_readl(host, INTMASK) & SDMMC_INT_SDIO(slot->sdio_id)))
|
||||||
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);
|
||||||
|
|
||||||
@ -1075,7 +1182,7 @@ static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||||||
ret = regulator_set_voltage(mmc->supply.vqmmc, min_uv, max_uv);
|
ret = regulator_set_voltage(mmc->supply.vqmmc, min_uv, max_uv);
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&mmc->class_dev,
|
dev_dbg(&mmc->class_dev,
|
||||||
"Regulator set error %d: %d - %d\n",
|
"Regulator set error %d: %d - %d\n",
|
||||||
ret, min_uv, max_uv);
|
ret, min_uv, max_uv);
|
||||||
return ret;
|
return ret;
|
||||||
@ -1180,10 +1287,10 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb)
|
|||||||
dw_mci_disable_low_power(slot);
|
dw_mci_disable_low_power(slot);
|
||||||
|
|
||||||
mci_writel(host, INTMASK,
|
mci_writel(host, INTMASK,
|
||||||
(int_mask | SDMMC_INT_SDIO(slot->id)));
|
(int_mask | SDMMC_INT_SDIO(slot->sdio_id)));
|
||||||
} else {
|
} else {
|
||||||
mci_writel(host, INTMASK,
|
mci_writel(host, INTMASK,
|
||||||
(int_mask & ~SDMMC_INT_SDIO(slot->id)));
|
(int_mask & ~SDMMC_INT_SDIO(slot->sdio_id)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1954,6 +2061,23 @@ static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status)
|
|||||||
tasklet_schedule(&host->tasklet);
|
tasklet_schedule(&host->tasklet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void dw_mci_handle_cd(struct dw_mci *host)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < host->num_slots; i++) {
|
||||||
|
struct dw_mci_slot *slot = host->slot[i];
|
||||||
|
|
||||||
|
if (!slot)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (slot->mmc->ops->card_event)
|
||||||
|
slot->mmc->ops->card_event(slot->mmc);
|
||||||
|
mmc_detect_change(slot->mmc,
|
||||||
|
msecs_to_jiffies(host->pdata->detect_delay_ms));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
|
static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
struct dw_mci *host = dev_id;
|
struct dw_mci *host = dev_id;
|
||||||
@ -2029,14 +2153,15 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
|
|||||||
|
|
||||||
if (pending & SDMMC_INT_CD) {
|
if (pending & SDMMC_INT_CD) {
|
||||||
mci_writel(host, RINTSTS, SDMMC_INT_CD);
|
mci_writel(host, RINTSTS, SDMMC_INT_CD);
|
||||||
queue_work(host->card_workqueue, &host->card_work);
|
dw_mci_handle_cd(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle SDIO Interrupts */
|
/* Handle SDIO Interrupts */
|
||||||
for (i = 0; i < host->num_slots; i++) {
|
for (i = 0; i < host->num_slots; i++) {
|
||||||
struct dw_mci_slot *slot = host->slot[i];
|
struct dw_mci_slot *slot = host->slot[i];
|
||||||
if (pending & SDMMC_INT_SDIO(i)) {
|
if (pending & SDMMC_INT_SDIO(slot->sdio_id)) {
|
||||||
mci_writel(host, RINTSTS, SDMMC_INT_SDIO(i));
|
mci_writel(host, RINTSTS,
|
||||||
|
SDMMC_INT_SDIO(slot->sdio_id));
|
||||||
mmc_signal_sdio_irq(slot->mmc);
|
mmc_signal_sdio_irq(slot->mmc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2045,99 +2170,28 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
|
|||||||
|
|
||||||
#ifdef CONFIG_MMC_DW_IDMAC
|
#ifdef CONFIG_MMC_DW_IDMAC
|
||||||
/* Handle DMA interrupts */
|
/* Handle DMA interrupts */
|
||||||
pending = mci_readl(host, IDSTS);
|
if (host->dma_64bit_address == 1) {
|
||||||
if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) {
|
pending = mci_readl(host, IDSTS64);
|
||||||
mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI);
|
if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) {
|
||||||
mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI);
|
mci_writel(host, IDSTS64, SDMMC_IDMAC_INT_TI |
|
||||||
host->dma_ops->complete(host);
|
SDMMC_IDMAC_INT_RI);
|
||||||
|
mci_writel(host, IDSTS64, SDMMC_IDMAC_INT_NI);
|
||||||
|
host->dma_ops->complete(host);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pending = mci_readl(host, IDSTS);
|
||||||
|
if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) {
|
||||||
|
mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI |
|
||||||
|
SDMMC_IDMAC_INT_RI);
|
||||||
|
mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI);
|
||||||
|
host->dma_ops->complete(host);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dw_mci_work_routine_card(struct work_struct *work)
|
|
||||||
{
|
|
||||||
struct dw_mci *host = container_of(work, struct dw_mci, card_work);
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < host->num_slots; i++) {
|
|
||||||
struct dw_mci_slot *slot = host->slot[i];
|
|
||||||
struct mmc_host *mmc = slot->mmc;
|
|
||||||
struct mmc_request *mrq;
|
|
||||||
int present;
|
|
||||||
|
|
||||||
present = dw_mci_get_cd(mmc);
|
|
||||||
while (present != slot->last_detect_state) {
|
|
||||||
dev_dbg(&slot->mmc->class_dev, "card %s\n",
|
|
||||||
present ? "inserted" : "removed");
|
|
||||||
|
|
||||||
spin_lock_bh(&host->lock);
|
|
||||||
|
|
||||||
/* Card change detected */
|
|
||||||
slot->last_detect_state = present;
|
|
||||||
|
|
||||||
/* Clean up queue if present */
|
|
||||||
mrq = slot->mrq;
|
|
||||||
if (mrq) {
|
|
||||||
if (mrq == host->mrq) {
|
|
||||||
host->data = NULL;
|
|
||||||
host->cmd = NULL;
|
|
||||||
|
|
||||||
switch (host->state) {
|
|
||||||
case STATE_IDLE:
|
|
||||||
case STATE_WAITING_CMD11_DONE:
|
|
||||||
break;
|
|
||||||
case STATE_SENDING_CMD11:
|
|
||||||
case STATE_SENDING_CMD:
|
|
||||||
mrq->cmd->error = -ENOMEDIUM;
|
|
||||||
if (!mrq->data)
|
|
||||||
break;
|
|
||||||
/* fall through */
|
|
||||||
case STATE_SENDING_DATA:
|
|
||||||
mrq->data->error = -ENOMEDIUM;
|
|
||||||
dw_mci_stop_dma(host);
|
|
||||||
break;
|
|
||||||
case STATE_DATA_BUSY:
|
|
||||||
case STATE_DATA_ERROR:
|
|
||||||
if (mrq->data->error == -EINPROGRESS)
|
|
||||||
mrq->data->error = -ENOMEDIUM;
|
|
||||||
/* fall through */
|
|
||||||
case STATE_SENDING_STOP:
|
|
||||||
if (mrq->stop)
|
|
||||||
mrq->stop->error = -ENOMEDIUM;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
dw_mci_request_end(host, mrq);
|
|
||||||
} else {
|
|
||||||
list_del(&slot->queue_node);
|
|
||||||
mrq->cmd->error = -ENOMEDIUM;
|
|
||||||
if (mrq->data)
|
|
||||||
mrq->data->error = -ENOMEDIUM;
|
|
||||||
if (mrq->stop)
|
|
||||||
mrq->stop->error = -ENOMEDIUM;
|
|
||||||
|
|
||||||
spin_unlock(&host->lock);
|
|
||||||
mmc_request_done(slot->mmc, mrq);
|
|
||||||
spin_lock(&host->lock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Power down slot */
|
|
||||||
if (present == 0)
|
|
||||||
dw_mci_reset(host);
|
|
||||||
|
|
||||||
spin_unlock_bh(&host->lock);
|
|
||||||
|
|
||||||
present = dw_mci_get_cd(mmc);
|
|
||||||
}
|
|
||||||
|
|
||||||
mmc_detect_change(slot->mmc,
|
|
||||||
msecs_to_jiffies(host->pdata->detect_delay_ms));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
#ifdef CONFIG_OF
|
||||||
/* given a slot id, find out the device node representing that slot */
|
/* given a slot id, find out the device node representing that slot */
|
||||||
static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot)
|
static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot)
|
||||||
@ -2206,6 +2260,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
|||||||
|
|
||||||
slot = mmc_priv(mmc);
|
slot = mmc_priv(mmc);
|
||||||
slot->id = id;
|
slot->id = id;
|
||||||
|
slot->sdio_id = host->sdio_id0 + id;
|
||||||
slot->mmc = mmc;
|
slot->mmc = mmc;
|
||||||
slot->host = host;
|
slot->host = host;
|
||||||
host->slot[id] = slot;
|
host->slot[id] = slot;
|
||||||
@ -2289,9 +2344,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
|||||||
dw_mci_init_debugfs(slot);
|
dw_mci_init_debugfs(slot);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Card initially undetected */
|
|
||||||
slot->last_detect_state = 0;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_host_allocated:
|
err_host_allocated:
|
||||||
@ -2309,6 +2361,22 @@ static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id)
|
|||||||
|
|
||||||
static void dw_mci_init_dma(struct dw_mci *host)
|
static void dw_mci_init_dma(struct dw_mci *host)
|
||||||
{
|
{
|
||||||
|
int addr_config;
|
||||||
|
/* Check ADDR_CONFIG bit in HCON to find IDMAC address bus width */
|
||||||
|
addr_config = (mci_readl(host, HCON) >> 27) & 0x01;
|
||||||
|
|
||||||
|
if (addr_config == 1) {
|
||||||
|
/* host supports IDMAC in 64-bit address mode */
|
||||||
|
host->dma_64bit_address = 1;
|
||||||
|
dev_info(host->dev, "IDMAC supports 64-bit address mode.\n");
|
||||||
|
if (!dma_set_mask(host->dev, DMA_BIT_MASK(64)))
|
||||||
|
dma_set_coherent_mask(host->dev, DMA_BIT_MASK(64));
|
||||||
|
} else {
|
||||||
|
/* host supports IDMAC in 32-bit address mode */
|
||||||
|
host->dma_64bit_address = 0;
|
||||||
|
dev_info(host->dev, "IDMAC supports 32-bit address mode.\n");
|
||||||
|
}
|
||||||
|
|
||||||
/* Alloc memory for sg translation */
|
/* Alloc memory for sg translation */
|
||||||
host->sg_cpu = dmam_alloc_coherent(host->dev, PAGE_SIZE,
|
host->sg_cpu = dmam_alloc_coherent(host->dev, PAGE_SIZE,
|
||||||
&host->sg_dma, GFP_KERNEL);
|
&host->sg_dma, GFP_KERNEL);
|
||||||
@ -2672,17 +2740,10 @@ int dw_mci_probe(struct dw_mci *host)
|
|||||||
host->data_offset = DATA_240A_OFFSET;
|
host->data_offset = DATA_240A_OFFSET;
|
||||||
|
|
||||||
tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host);
|
tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host);
|
||||||
host->card_workqueue = alloc_workqueue("dw-mci-card",
|
|
||||||
WQ_MEM_RECLAIM, 1);
|
|
||||||
if (!host->card_workqueue) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto err_dmaunmap;
|
|
||||||
}
|
|
||||||
INIT_WORK(&host->card_work, dw_mci_work_routine_card);
|
|
||||||
ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt,
|
ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt,
|
||||||
host->irq_flags, "dw-mci", host);
|
host->irq_flags, "dw-mci", host);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_workqueue;
|
goto err_dmaunmap;
|
||||||
|
|
||||||
if (host->pdata->num_slots)
|
if (host->pdata->num_slots)
|
||||||
host->num_slots = host->pdata->num_slots;
|
host->num_slots = host->pdata->num_slots;
|
||||||
@ -2718,7 +2779,7 @@ int dw_mci_probe(struct dw_mci *host)
|
|||||||
} else {
|
} else {
|
||||||
dev_dbg(host->dev, "attempted to initialize %d slots, "
|
dev_dbg(host->dev, "attempted to initialize %d slots, "
|
||||||
"but failed on all\n", host->num_slots);
|
"but failed on all\n", host->num_slots);
|
||||||
goto err_workqueue;
|
goto err_dmaunmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO)
|
if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO)
|
||||||
@ -2726,9 +2787,6 @@ int dw_mci_probe(struct dw_mci *host)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_workqueue:
|
|
||||||
destroy_workqueue(host->card_workqueue);
|
|
||||||
|
|
||||||
err_dmaunmap:
|
err_dmaunmap:
|
||||||
if (host->use_dma && host->dma_ops->exit)
|
if (host->use_dma && host->dma_ops->exit)
|
||||||
host->dma_ops->exit(host);
|
host->dma_ops->exit(host);
|
||||||
@ -2762,8 +2820,6 @@ void dw_mci_remove(struct dw_mci *host)
|
|||||||
mci_writel(host, CLKENA, 0);
|
mci_writel(host, CLKENA, 0);
|
||||||
mci_writel(host, CLKSRC, 0);
|
mci_writel(host, CLKSRC, 0);
|
||||||
|
|
||||||
destroy_workqueue(host->card_workqueue);
|
|
||||||
|
|
||||||
if (host->use_dma && host->dma_ops->exit)
|
if (host->use_dma && host->dma_ops->exit)
|
||||||
host->dma_ops->exit(host);
|
host->dma_ops->exit(host);
|
||||||
|
|
||||||
|
@ -55,6 +55,17 @@
|
|||||||
#define SDMMC_BUFADDR 0x098
|
#define SDMMC_BUFADDR 0x098
|
||||||
#define SDMMC_CDTHRCTL 0x100
|
#define SDMMC_CDTHRCTL 0x100
|
||||||
#define SDMMC_DATA(x) (x)
|
#define SDMMC_DATA(x) (x)
|
||||||
|
/*
|
||||||
|
* Registers to support idmac 64-bit address mode
|
||||||
|
*/
|
||||||
|
#define SDMMC_DBADDRL 0x088
|
||||||
|
#define SDMMC_DBADDRU 0x08c
|
||||||
|
#define SDMMC_IDSTS64 0x090
|
||||||
|
#define SDMMC_IDINTEN64 0x094
|
||||||
|
#define SDMMC_DSCADDRL 0x098
|
||||||
|
#define SDMMC_DSCADDRU 0x09c
|
||||||
|
#define SDMMC_BUFADDRL 0x0A0
|
||||||
|
#define SDMMC_BUFADDRU 0x0A4
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Data offset is difference according to Version
|
* Data offset is difference according to Version
|
||||||
@ -214,7 +225,7 @@ extern int dw_mci_resume(struct dw_mci *host);
|
|||||||
* with CONFIG_MMC_CLKGATE.
|
* with CONFIG_MMC_CLKGATE.
|
||||||
* @flags: Random state bits associated with the slot.
|
* @flags: Random state bits associated with the slot.
|
||||||
* @id: Number of this slot.
|
* @id: Number of this slot.
|
||||||
* @last_detect_state: Most recently observed card detect state.
|
* @sdio_id: Number of this slot in the SDIO interrupt registers.
|
||||||
*/
|
*/
|
||||||
struct dw_mci_slot {
|
struct dw_mci_slot {
|
||||||
struct mmc_host *mmc;
|
struct mmc_host *mmc;
|
||||||
@ -234,7 +245,7 @@ struct dw_mci_slot {
|
|||||||
#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
|
||||||
int id;
|
int id;
|
||||||
int last_detect_state;
|
int sdio_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct dw_mci_tuning_data {
|
struct dw_mci_tuning_data {
|
||||||
|
@ -736,8 +736,15 @@ static void mmci_post_request(struct mmc_host *mmc, struct mmc_request *mrq,
|
|||||||
chan = host->dma_tx_channel;
|
chan = host->dma_tx_channel;
|
||||||
dmaengine_terminate_all(chan);
|
dmaengine_terminate_all(chan);
|
||||||
|
|
||||||
|
if (host->dma_desc_current == next->dma_desc)
|
||||||
|
host->dma_desc_current = NULL;
|
||||||
|
|
||||||
|
if (host->dma_current == next->dma_chan)
|
||||||
|
host->dma_current = NULL;
|
||||||
|
|
||||||
next->dma_desc = NULL;
|
next->dma_desc = NULL;
|
||||||
next->dma_chan = NULL;
|
next->dma_chan = NULL;
|
||||||
|
data->host_cookie = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1360,7 +1360,7 @@ msmsdcc_probe(struct platform_device *pdev)
|
|||||||
if (ret)
|
if (ret)
|
||||||
goto cmd_irq_free;
|
goto cmd_irq_free;
|
||||||
|
|
||||||
mmc_set_drvdata(pdev, mmc);
|
platform_set_drvdata(pdev, mmc);
|
||||||
mmc_add_host(mmc);
|
mmc_add_host(mmc);
|
||||||
|
|
||||||
pr_info("%s: Qualcomm MSM SDCC at 0x%016llx irq %d,%d dma %d\n",
|
pr_info("%s: Qualcomm MSM SDCC at 0x%016llx irq %d,%d dma %d\n",
|
||||||
@ -1419,7 +1419,7 @@ ioremap_free:
|
|||||||
static int
|
static int
|
||||||
msmsdcc_suspend(struct platform_device *dev, pm_message_t state)
|
msmsdcc_suspend(struct platform_device *dev, pm_message_t state)
|
||||||
{
|
{
|
||||||
struct mmc_host *mmc = mmc_get_drvdata(dev);
|
struct mmc_host *mmc = platform_get_drvdata(dev);
|
||||||
|
|
||||||
if (mmc) {
|
if (mmc) {
|
||||||
struct msmsdcc_host *host = mmc_priv(mmc);
|
struct msmsdcc_host *host = mmc_priv(mmc);
|
||||||
@ -1437,7 +1437,7 @@ msmsdcc_suspend(struct platform_device *dev, pm_message_t state)
|
|||||||
static int
|
static int
|
||||||
msmsdcc_resume(struct platform_device *dev)
|
msmsdcc_resume(struct platform_device *dev)
|
||||||
{
|
{
|
||||||
struct mmc_host *mmc = mmc_get_drvdata(dev);
|
struct mmc_host *mmc = platform_get_drvdata(dev);
|
||||||
|
|
||||||
if (mmc) {
|
if (mmc) {
|
||||||
struct msmsdcc_host *host = mmc_priv(mmc);
|
struct msmsdcc_host *host = mmc_priv(mmc);
|
||||||
|
@ -111,10 +111,15 @@ static int mvsd_setup_data(struct mvsd_host *host, struct mmc_data *data)
|
|||||||
mvsd_write(MVSD_BLK_COUNT, data->blocks);
|
mvsd_write(MVSD_BLK_COUNT, data->blocks);
|
||||||
mvsd_write(MVSD_BLK_SIZE, data->blksz);
|
mvsd_write(MVSD_BLK_SIZE, data->blksz);
|
||||||
|
|
||||||
if (nodma || (data->blksz | data->sg->offset) & 3) {
|
if (nodma || (data->blksz | data->sg->offset) & 3 ||
|
||||||
|
((!(data->flags & MMC_DATA_READ) && data->sg->offset & 0x3f))) {
|
||||||
/*
|
/*
|
||||||
* We cannot do DMA on a buffer which offset or size
|
* We cannot do DMA on a buffer which offset or size
|
||||||
* is not aligned on a 4-byte boundary.
|
* is not aligned on a 4-byte boundary.
|
||||||
|
*
|
||||||
|
* It also appears the host to card DMA can corrupt
|
||||||
|
* data when the buffer is not aligned on a 64 byte
|
||||||
|
* boundary.
|
||||||
*/
|
*/
|
||||||
host->pio_size = data->blocks * data->blksz;
|
host->pio_size = data->blocks * data->blksz;
|
||||||
host->pio_ptr = sg_virt(data->sg);
|
host->pio_ptr = sg_virt(data->sg);
|
||||||
|
@ -373,13 +373,9 @@ static void mxcmci_dma_callback(void *data)
|
|||||||
del_timer(&host->watchdog);
|
del_timer(&host->watchdog);
|
||||||
|
|
||||||
stat = mxcmci_readl(host, MMC_REG_STATUS);
|
stat = mxcmci_readl(host, MMC_REG_STATUS);
|
||||||
mxcmci_writel(host, stat & ~STATUS_DATA_TRANS_DONE, MMC_REG_STATUS);
|
|
||||||
|
|
||||||
dev_dbg(mmc_dev(host->mmc), "%s: 0x%08x\n", __func__, stat);
|
dev_dbg(mmc_dev(host->mmc), "%s: 0x%08x\n", __func__, stat);
|
||||||
|
|
||||||
if (stat & STATUS_READ_OP_DONE)
|
|
||||||
mxcmci_writel(host, STATUS_READ_OP_DONE, MMC_REG_STATUS);
|
|
||||||
|
|
||||||
mxcmci_data_done(host, stat);
|
mxcmci_data_done(host, stat);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -743,10 +739,8 @@ static irqreturn_t mxcmci_irq(int irq, void *devid)
|
|||||||
sdio_irq = (stat & STATUS_SDIO_INT_ACTIVE) && host->use_sdio;
|
sdio_irq = (stat & STATUS_SDIO_INT_ACTIVE) && host->use_sdio;
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
|
|
||||||
if (mxcmci_use_dma(host) &&
|
if (mxcmci_use_dma(host) && (stat & (STATUS_WRITE_OP_DONE)))
|
||||||
(stat & (STATUS_READ_OP_DONE | STATUS_WRITE_OP_DONE)))
|
mxcmci_writel(host, STATUS_WRITE_OP_DONE, MMC_REG_STATUS);
|
||||||
mxcmci_writel(host, STATUS_READ_OP_DONE | STATUS_WRITE_OP_DONE,
|
|
||||||
MMC_REG_STATUS);
|
|
||||||
|
|
||||||
if (sdio_irq) {
|
if (sdio_irq) {
|
||||||
mxcmci_writel(host, STATUS_SDIO_INT_ACTIVE, MMC_REG_STATUS);
|
mxcmci_writel(host, STATUS_SDIO_INT_ACTIVE, MMC_REG_STATUS);
|
||||||
@ -756,8 +750,7 @@ static irqreturn_t mxcmci_irq(int irq, void *devid)
|
|||||||
if (stat & STATUS_END_CMD_RESP)
|
if (stat & STATUS_END_CMD_RESP)
|
||||||
mxcmci_cmd_done(host, stat);
|
mxcmci_cmd_done(host, stat);
|
||||||
|
|
||||||
if (mxcmci_use_dma(host) &&
|
if (mxcmci_use_dma(host) && (stat & STATUS_WRITE_OP_DONE)) {
|
||||||
(stat & (STATUS_DATA_TRANS_DONE | STATUS_WRITE_OP_DONE))) {
|
|
||||||
del_timer(&host->watchdog);
|
del_timer(&host->watchdog);
|
||||||
mxcmci_data_done(host, stat);
|
mxcmci_data_done(host, stat);
|
||||||
}
|
}
|
||||||
@ -1084,12 +1077,14 @@ static int mxcmci_probe(struct platform_device *pdev)
|
|||||||
dat3_card_detect = true;
|
dat3_card_detect = true;
|
||||||
|
|
||||||
ret = mmc_regulator_get_supply(mmc);
|
ret = mmc_regulator_get_supply(mmc);
|
||||||
if (ret) {
|
if (ret == -EPROBE_DEFER)
|
||||||
if (pdata && ret != -EPROBE_DEFER)
|
goto out_free;
|
||||||
mmc->ocr_avail = pdata->ocr_avail ? :
|
|
||||||
MMC_VDD_32_33 | MMC_VDD_33_34;
|
if (!mmc->ocr_avail) {
|
||||||
|
if (pdata && pdata->ocr_avail)
|
||||||
|
mmc->ocr_avail = pdata->ocr_avail;
|
||||||
else
|
else
|
||||||
goto out_free;
|
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dat3_card_detect)
|
if (dat3_card_detect)
|
||||||
|
@ -581,10 +581,9 @@ static int mxs_mmc_probe(struct platform_device *pdev)
|
|||||||
struct regulator *reg_vmmc;
|
struct regulator *reg_vmmc;
|
||||||
struct mxs_ssp *ssp;
|
struct mxs_ssp *ssp;
|
||||||
|
|
||||||
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
||||||
irq_err = platform_get_irq(pdev, 0);
|
irq_err = platform_get_irq(pdev, 0);
|
||||||
if (!iores || irq_err < 0)
|
if (irq_err < 0)
|
||||||
return -EINVAL;
|
return irq_err;
|
||||||
|
|
||||||
mmc = mmc_alloc_host(sizeof(struct mxs_mmc_host), &pdev->dev);
|
mmc = mmc_alloc_host(sizeof(struct mxs_mmc_host), &pdev->dev);
|
||||||
if (!mmc)
|
if (!mmc)
|
||||||
@ -593,6 +592,7 @@ static int mxs_mmc_probe(struct platform_device *pdev)
|
|||||||
host = mmc_priv(mmc);
|
host = mmc_priv(mmc);
|
||||||
ssp = &host->ssp;
|
ssp = &host->ssp;
|
||||||
ssp->dev = &pdev->dev;
|
ssp->dev = &pdev->dev;
|
||||||
|
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
ssp->base = devm_ioremap_resource(&pdev->dev, iores);
|
ssp->base = devm_ioremap_resource(&pdev->dev, iores);
|
||||||
if (IS_ERR(ssp->base)) {
|
if (IS_ERR(ssp->base)) {
|
||||||
ret = PTR_ERR(ssp->base);
|
ret = PTR_ERR(ssp->base);
|
||||||
@ -619,7 +619,9 @@ static int mxs_mmc_probe(struct platform_device *pdev)
|
|||||||
ret = PTR_ERR(ssp->clk);
|
ret = PTR_ERR(ssp->clk);
|
||||||
goto out_mmc_free;
|
goto out_mmc_free;
|
||||||
}
|
}
|
||||||
clk_prepare_enable(ssp->clk);
|
ret = clk_prepare_enable(ssp->clk);
|
||||||
|
if (ret)
|
||||||
|
goto out_mmc_free;
|
||||||
|
|
||||||
ret = mxs_mmc_reset(host);
|
ret = mxs_mmc_reset(host);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
@ -660,7 +662,7 @@ static int mxs_mmc_probe(struct platform_device *pdev)
|
|||||||
platform_set_drvdata(pdev, mmc);
|
platform_set_drvdata(pdev, mmc);
|
||||||
|
|
||||||
ret = devm_request_irq(&pdev->dev, irq_err, mxs_mmc_irq_handler, 0,
|
ret = devm_request_irq(&pdev->dev, irq_err, mxs_mmc_irq_handler, 0,
|
||||||
DRIVER_NAME, host);
|
dev_name(&pdev->dev), host);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_free_dma;
|
goto out_free_dma;
|
||||||
|
|
||||||
@ -702,7 +704,7 @@ static int mxs_mmc_remove(struct platform_device *pdev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM_SLEEP
|
||||||
static int mxs_mmc_suspend(struct device *dev)
|
static int mxs_mmc_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct mmc_host *mmc = dev_get_drvdata(dev);
|
struct mmc_host *mmc = dev_get_drvdata(dev);
|
||||||
@ -719,25 +721,19 @@ static int mxs_mmc_resume(struct device *dev)
|
|||||||
struct mxs_mmc_host *host = mmc_priv(mmc);
|
struct mxs_mmc_host *host = mmc_priv(mmc);
|
||||||
struct mxs_ssp *ssp = &host->ssp;
|
struct mxs_ssp *ssp = &host->ssp;
|
||||||
|
|
||||||
clk_prepare_enable(ssp->clk);
|
return clk_prepare_enable(ssp->clk);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct dev_pm_ops mxs_mmc_pm_ops = {
|
|
||||||
.suspend = mxs_mmc_suspend,
|
|
||||||
.resume = mxs_mmc_resume,
|
|
||||||
};
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static SIMPLE_DEV_PM_OPS(mxs_mmc_pm_ops, mxs_mmc_suspend, mxs_mmc_resume);
|
||||||
|
|
||||||
static struct platform_driver mxs_mmc_driver = {
|
static struct platform_driver mxs_mmc_driver = {
|
||||||
.probe = mxs_mmc_probe,
|
.probe = mxs_mmc_probe,
|
||||||
.remove = mxs_mmc_remove,
|
.remove = mxs_mmc_remove,
|
||||||
.id_table = mxs_ssp_ids,
|
.id_table = mxs_ssp_ids,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = DRIVER_NAME,
|
.name = DRIVER_NAME,
|
||||||
#ifdef CONFIG_PM
|
|
||||||
.pm = &mxs_mmc_pm_ops,
|
.pm = &mxs_mmc_pm_ops,
|
||||||
#endif
|
|
||||||
.of_match_table = mxs_mmc_dt_ids,
|
.of_match_table = mxs_mmc_dt_ids,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/pinctrl/consumer.h>
|
#include <linux/pinctrl/consumer.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/platform_data/mmc-omap.h>
|
#include <linux/platform_data/hsmmc-omap.h>
|
||||||
|
|
||||||
/* OMAP HSMMC Host Controller Registers */
|
/* OMAP HSMMC Host Controller Registers */
|
||||||
#define OMAP_HSMMC_SYSSTATUS 0x0014
|
#define OMAP_HSMMC_SYSSTATUS 0x0014
|
||||||
@ -155,7 +155,7 @@
|
|||||||
* omap.c controller driver. Luckily this is not currently done on any known
|
* omap.c controller driver. Luckily this is not currently done on any known
|
||||||
* omap_hsmmc.c device.
|
* omap_hsmmc.c device.
|
||||||
*/
|
*/
|
||||||
#define mmc_slot(host) (host->pdata->slots[host->slot_id])
|
#define mmc_pdata(host) host->pdata
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MMC Host controller read/write API's
|
* MMC Host controller read/write API's
|
||||||
@ -207,7 +207,6 @@ struct omap_hsmmc_host {
|
|||||||
int use_dma, dma_ch;
|
int use_dma, dma_ch;
|
||||||
struct dma_chan *tx_chan;
|
struct dma_chan *tx_chan;
|
||||||
struct dma_chan *rx_chan;
|
struct dma_chan *rx_chan;
|
||||||
int slot_id;
|
|
||||||
int response_busy;
|
int response_busy;
|
||||||
int context_loss;
|
int context_loss;
|
||||||
int protect_card;
|
int protect_card;
|
||||||
@ -220,7 +219,26 @@ struct omap_hsmmc_host {
|
|||||||
#define HSMMC_SDIO_IRQ_ENABLED (1 << 1) /* SDIO irq enabled */
|
#define HSMMC_SDIO_IRQ_ENABLED (1 << 1) /* SDIO irq enabled */
|
||||||
#define HSMMC_WAKE_IRQ_ENABLED (1 << 2)
|
#define HSMMC_WAKE_IRQ_ENABLED (1 << 2)
|
||||||
struct omap_hsmmc_next next_data;
|
struct omap_hsmmc_next next_data;
|
||||||
struct omap_mmc_platform_data *pdata;
|
struct omap_hsmmc_platform_data *pdata;
|
||||||
|
|
||||||
|
/* To handle board related suspend/resume functionality for MMC */
|
||||||
|
int (*suspend)(struct device *dev);
|
||||||
|
int (*resume)(struct device *dev);
|
||||||
|
|
||||||
|
/* return MMC cover switch state, can be NULL if not supported.
|
||||||
|
*
|
||||||
|
* possible return values:
|
||||||
|
* 0 - closed
|
||||||
|
* 1 - open
|
||||||
|
*/
|
||||||
|
int (*get_cover_state)(struct device *dev);
|
||||||
|
|
||||||
|
/* Card detection IRQs */
|
||||||
|
int card_detect_irq;
|
||||||
|
|
||||||
|
int (*card_detect)(struct device *dev);
|
||||||
|
int (*get_ro)(struct device *dev);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct omap_mmc_of_data {
|
struct omap_mmc_of_data {
|
||||||
@ -230,50 +248,48 @@ struct omap_mmc_of_data {
|
|||||||
|
|
||||||
static void omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host);
|
static void omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host);
|
||||||
|
|
||||||
static int omap_hsmmc_card_detect(struct device *dev, int slot)
|
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_mmc_platform_data *mmc = host->pdata;
|
struct omap_hsmmc_platform_data *mmc = host->pdata;
|
||||||
|
|
||||||
/* NOTE: assumes card detect signal is active-low */
|
/* NOTE: assumes card detect signal is active-low */
|
||||||
return !gpio_get_value_cansleep(mmc->slots[0].switch_pin);
|
return !gpio_get_value_cansleep(mmc->switch_pin);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int omap_hsmmc_get_wp(struct device *dev, int slot)
|
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_mmc_platform_data *mmc = host->pdata;
|
struct omap_hsmmc_platform_data *mmc = host->pdata;
|
||||||
|
|
||||||
/* NOTE: assumes write protect signal is active-high */
|
/* NOTE: assumes write protect signal is active-high */
|
||||||
return gpio_get_value_cansleep(mmc->slots[0].gpio_wp);
|
return gpio_get_value_cansleep(mmc->gpio_wp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int omap_hsmmc_get_cover_state(struct device *dev, int slot)
|
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_mmc_platform_data *mmc = host->pdata;
|
struct omap_hsmmc_platform_data *mmc = host->pdata;
|
||||||
|
|
||||||
/* NOTE: assumes card detect signal is active-low */
|
/* NOTE: assumes card detect signal is active-low */
|
||||||
return !gpio_get_value_cansleep(mmc->slots[0].switch_pin);
|
return !gpio_get_value_cansleep(mmc->switch_pin);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
|
|
||||||
static int omap_hsmmc_suspend_cdirq(struct device *dev, int slot)
|
static int omap_hsmmc_suspend_cdirq(struct device *dev)
|
||||||
{
|
{
|
||||||
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
|
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
|
||||||
struct omap_mmc_platform_data *mmc = host->pdata;
|
|
||||||
|
|
||||||
disable_irq(mmc->slots[0].card_detect_irq);
|
disable_irq(host->card_detect_irq);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int omap_hsmmc_resume_cdirq(struct device *dev, int slot)
|
static int omap_hsmmc_resume_cdirq(struct device *dev)
|
||||||
{
|
{
|
||||||
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
|
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
|
||||||
struct omap_mmc_platform_data *mmc = host->pdata;
|
|
||||||
|
|
||||||
enable_irq(mmc->slots[0].card_detect_irq);
|
enable_irq(host->card_detect_irq);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,8 +302,7 @@ static int omap_hsmmc_resume_cdirq(struct device *dev, int slot)
|
|||||||
|
|
||||||
#ifdef CONFIG_REGULATOR
|
#ifdef CONFIG_REGULATOR
|
||||||
|
|
||||||
static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on,
|
static int omap_hsmmc_set_power(struct device *dev, int power_on, int vdd)
|
||||||
int vdd)
|
|
||||||
{
|
{
|
||||||
struct omap_hsmmc_host *host =
|
struct omap_hsmmc_host *host =
|
||||||
platform_get_drvdata(to_platform_device(dev));
|
platform_get_drvdata(to_platform_device(dev));
|
||||||
@ -300,8 +315,8 @@ static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on,
|
|||||||
if (!host->vcc)
|
if (!host->vcc)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (mmc_slot(host).before_set_reg)
|
if (mmc_pdata(host)->before_set_reg)
|
||||||
mmc_slot(host).before_set_reg(dev, slot, power_on, vdd);
|
mmc_pdata(host)->before_set_reg(dev, power_on, vdd);
|
||||||
|
|
||||||
if (host->pbias) {
|
if (host->pbias) {
|
||||||
if (host->pbias_enabled == 1) {
|
if (host->pbias_enabled == 1) {
|
||||||
@ -363,8 +378,8 @@ static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mmc_slot(host).after_set_reg)
|
if (mmc_pdata(host)->after_set_reg)
|
||||||
mmc_slot(host).after_set_reg(dev, slot, power_on, vdd);
|
mmc_pdata(host)->after_set_reg(dev, power_on, vdd);
|
||||||
|
|
||||||
error_set_power:
|
error_set_power:
|
||||||
return ret;
|
return ret;
|
||||||
@ -383,18 +398,18 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
|
|||||||
} else {
|
} else {
|
||||||
host->vcc = reg;
|
host->vcc = reg;
|
||||||
ocr_value = mmc_regulator_get_ocrmask(reg);
|
ocr_value = mmc_regulator_get_ocrmask(reg);
|
||||||
if (!mmc_slot(host).ocr_mask) {
|
if (!mmc_pdata(host)->ocr_mask) {
|
||||||
mmc_slot(host).ocr_mask = ocr_value;
|
mmc_pdata(host)->ocr_mask = ocr_value;
|
||||||
} else {
|
} else {
|
||||||
if (!(mmc_slot(host).ocr_mask & ocr_value)) {
|
if (!(mmc_pdata(host)->ocr_mask & ocr_value)) {
|
||||||
dev_err(host->dev, "ocrmask %x is not supported\n",
|
dev_err(host->dev, "ocrmask %x is not supported\n",
|
||||||
mmc_slot(host).ocr_mask);
|
mmc_pdata(host)->ocr_mask);
|
||||||
mmc_slot(host).ocr_mask = 0;
|
mmc_pdata(host)->ocr_mask = 0;
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mmc_slot(host).set_power = omap_hsmmc_set_power;
|
mmc_pdata(host)->set_power = omap_hsmmc_set_power;
|
||||||
|
|
||||||
/* Allow an aux regulator */
|
/* Allow an aux regulator */
|
||||||
reg = devm_regulator_get_optional(host->dev, "vmmc_aux");
|
reg = devm_regulator_get_optional(host->dev, "vmmc_aux");
|
||||||
@ -404,7 +419,7 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
|
|||||||
host->pbias = IS_ERR(reg) ? NULL : reg;
|
host->pbias = IS_ERR(reg) ? NULL : reg;
|
||||||
|
|
||||||
/* For eMMC do not power off when not in sleep state */
|
/* For eMMC do not power off when not in sleep state */
|
||||||
if (mmc_slot(host).no_regulator_off_init)
|
if (mmc_pdata(host)->no_regulator_off_init)
|
||||||
return 0;
|
return 0;
|
||||||
/*
|
/*
|
||||||
* To disable boot_on regulator, enable regulator
|
* To disable boot_on regulator, enable regulator
|
||||||
@ -412,10 +427,10 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
|
|||||||
*/
|
*/
|
||||||
if ((host->vcc && regulator_is_enabled(host->vcc) > 0) ||
|
if ((host->vcc && regulator_is_enabled(host->vcc) > 0) ||
|
||||||
(host->vcc_aux && regulator_is_enabled(host->vcc_aux))) {
|
(host->vcc_aux && regulator_is_enabled(host->vcc_aux))) {
|
||||||
int vdd = ffs(mmc_slot(host).ocr_mask) - 1;
|
int vdd = ffs(mmc_pdata(host)->ocr_mask) - 1;
|
||||||
|
|
||||||
mmc_slot(host).set_power(host->dev, host->slot_id, 1, vdd);
|
mmc_pdata(host)->set_power(host->dev, 1, vdd);
|
||||||
mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
|
mmc_pdata(host)->set_power(host->dev, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -423,7 +438,7 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
|
|||||||
|
|
||||||
static void omap_hsmmc_reg_put(struct omap_hsmmc_host *host)
|
static void omap_hsmmc_reg_put(struct omap_hsmmc_host *host)
|
||||||
{
|
{
|
||||||
mmc_slot(host).set_power = NULL;
|
mmc_pdata(host)->set_power = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int omap_hsmmc_have_reg(void)
|
static inline int omap_hsmmc_have_reg(void)
|
||||||
@ -449,55 +464,59 @@ static inline int omap_hsmmc_have_reg(void)
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int omap_hsmmc_gpio_init(struct omap_mmc_platform_data *pdata)
|
static int omap_hsmmc_gpio_init(struct omap_hsmmc_host *host,
|
||||||
|
struct omap_hsmmc_platform_data *pdata)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (gpio_is_valid(pdata->slots[0].switch_pin)) {
|
if (gpio_is_valid(pdata->switch_pin)) {
|
||||||
if (pdata->slots[0].cover)
|
if (pdata->cover)
|
||||||
pdata->slots[0].get_cover_state =
|
host->get_cover_state =
|
||||||
omap_hsmmc_get_cover_state;
|
omap_hsmmc_get_cover_state;
|
||||||
else
|
else
|
||||||
pdata->slots[0].card_detect = omap_hsmmc_card_detect;
|
host->card_detect = omap_hsmmc_card_detect;
|
||||||
pdata->slots[0].card_detect_irq =
|
host->card_detect_irq =
|
||||||
gpio_to_irq(pdata->slots[0].switch_pin);
|
gpio_to_irq(pdata->switch_pin);
|
||||||
ret = gpio_request(pdata->slots[0].switch_pin, "mmc_cd");
|
ret = gpio_request(pdata->switch_pin, "mmc_cd");
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
ret = gpio_direction_input(pdata->slots[0].switch_pin);
|
ret = gpio_direction_input(pdata->switch_pin);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_free_sp;
|
goto err_free_sp;
|
||||||
} else
|
} else {
|
||||||
pdata->slots[0].switch_pin = -EINVAL;
|
pdata->switch_pin = -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
if (gpio_is_valid(pdata->slots[0].gpio_wp)) {
|
if (gpio_is_valid(pdata->gpio_wp)) {
|
||||||
pdata->slots[0].get_ro = omap_hsmmc_get_wp;
|
host->get_ro = omap_hsmmc_get_wp;
|
||||||
ret = gpio_request(pdata->slots[0].gpio_wp, "mmc_wp");
|
ret = gpio_request(pdata->gpio_wp, "mmc_wp");
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_free_cd;
|
goto err_free_cd;
|
||||||
ret = gpio_direction_input(pdata->slots[0].gpio_wp);
|
ret = gpio_direction_input(pdata->gpio_wp);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_free_wp;
|
goto err_free_wp;
|
||||||
} else
|
} else {
|
||||||
pdata->slots[0].gpio_wp = -EINVAL;
|
pdata->gpio_wp = -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_free_wp:
|
err_free_wp:
|
||||||
gpio_free(pdata->slots[0].gpio_wp);
|
gpio_free(pdata->gpio_wp);
|
||||||
err_free_cd:
|
err_free_cd:
|
||||||
if (gpio_is_valid(pdata->slots[0].switch_pin))
|
if (gpio_is_valid(pdata->switch_pin))
|
||||||
err_free_sp:
|
err_free_sp:
|
||||||
gpio_free(pdata->slots[0].switch_pin);
|
gpio_free(pdata->switch_pin);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void omap_hsmmc_gpio_free(struct omap_mmc_platform_data *pdata)
|
static void omap_hsmmc_gpio_free(struct omap_hsmmc_host *host,
|
||||||
|
struct omap_hsmmc_platform_data *pdata)
|
||||||
{
|
{
|
||||||
if (gpio_is_valid(pdata->slots[0].gpio_wp))
|
if (gpio_is_valid(pdata->gpio_wp))
|
||||||
gpio_free(pdata->slots[0].gpio_wp);
|
gpio_free(pdata->gpio_wp);
|
||||||
if (gpio_is_valid(pdata->slots[0].switch_pin))
|
if (gpio_is_valid(pdata->switch_pin))
|
||||||
gpio_free(pdata->slots[0].switch_pin);
|
gpio_free(pdata->switch_pin);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -607,8 +626,9 @@ static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host)
|
|||||||
* in capabilities register
|
* in capabilities register
|
||||||
* - MMC/SD clock coming out of controller > 25MHz
|
* - MMC/SD clock coming out of controller > 25MHz
|
||||||
*/
|
*/
|
||||||
if ((mmc_slot(host).features & HSMMC_HAS_HSPE_SUPPORT) &&
|
if ((mmc_pdata(host)->features & HSMMC_HAS_HSPE_SUPPORT) &&
|
||||||
(ios->timing != MMC_TIMING_MMC_DDR52) &&
|
(ios->timing != MMC_TIMING_MMC_DDR52) &&
|
||||||
|
(ios->timing != MMC_TIMING_UHS_DDR50) &&
|
||||||
((OMAP_HSMMC_READ(host->base, CAPA) & HSS) == HSS)) {
|
((OMAP_HSMMC_READ(host->base, CAPA) & HSS) == HSS)) {
|
||||||
regval = OMAP_HSMMC_READ(host->base, HCTL);
|
regval = OMAP_HSMMC_READ(host->base, HCTL);
|
||||||
if (clkdiv && (clk_get_rate(host->fclk)/clkdiv) > 25000000)
|
if (clkdiv && (clk_get_rate(host->fclk)/clkdiv) > 25000000)
|
||||||
@ -628,7 +648,8 @@ static void omap_hsmmc_set_bus_width(struct omap_hsmmc_host *host)
|
|||||||
u32 con;
|
u32 con;
|
||||||
|
|
||||||
con = OMAP_HSMMC_READ(host->base, CON);
|
con = OMAP_HSMMC_READ(host->base, CON);
|
||||||
if (ios->timing == MMC_TIMING_MMC_DDR52)
|
if (ios->timing == MMC_TIMING_MMC_DDR52 ||
|
||||||
|
ios->timing == MMC_TIMING_UHS_DDR50)
|
||||||
con |= DDR; /* configure in DDR mode */
|
con |= DDR; /* configure in DDR mode */
|
||||||
else
|
else
|
||||||
con &= ~DDR;
|
con &= ~DDR;
|
||||||
@ -791,8 +812,8 @@ int omap_hsmmc_cover_is_closed(struct omap_hsmmc_host *host)
|
|||||||
{
|
{
|
||||||
int r = 1;
|
int r = 1;
|
||||||
|
|
||||||
if (mmc_slot(host).get_cover_state)
|
if (host->get_cover_state)
|
||||||
r = mmc_slot(host).get_cover_state(host->dev, host->slot_id);
|
r = host->get_cover_state(host->dev);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -816,7 +837,7 @@ omap_hsmmc_show_slot_name(struct device *dev, struct device_attribute *attr,
|
|||||||
struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev);
|
struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev);
|
||||||
struct omap_hsmmc_host *host = mmc_priv(mmc);
|
struct omap_hsmmc_host *host = mmc_priv(mmc);
|
||||||
|
|
||||||
return sprintf(buf, "%s\n", mmc_slot(host).name);
|
return sprintf(buf, "%s\n", mmc_pdata(host)->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static DEVICE_ATTR(slot_name, S_IRUGO, omap_hsmmc_show_slot_name, NULL);
|
static DEVICE_ATTR(slot_name, S_IRUGO, omap_hsmmc_show_slot_name, NULL);
|
||||||
@ -1061,7 +1082,7 @@ static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host,
|
|||||||
* OMAP4 ES2 and greater has an updated reset logic.
|
* OMAP4 ES2 and greater has an updated reset logic.
|
||||||
* Monitor a 0->1 transition first
|
* Monitor a 0->1 transition first
|
||||||
*/
|
*/
|
||||||
if (mmc_slot(host).features & HSMMC_HAS_UPDATED_RESET) {
|
if (mmc_pdata(host)->features & HSMMC_HAS_UPDATED_RESET) {
|
||||||
while ((!(OMAP_HSMMC_READ(host->base, SYSCTL) & bit))
|
while ((!(OMAP_HSMMC_READ(host->base, SYSCTL) & bit))
|
||||||
&& (i++ < limit))
|
&& (i++ < limit))
|
||||||
udelay(1);
|
udelay(1);
|
||||||
@ -1210,12 +1231,11 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd)
|
|||||||
clk_disable_unprepare(host->dbclk);
|
clk_disable_unprepare(host->dbclk);
|
||||||
|
|
||||||
/* Turn the power off */
|
/* Turn the power off */
|
||||||
ret = mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
|
ret = mmc_pdata(host)->set_power(host->dev, 0, 0);
|
||||||
|
|
||||||
/* Turn the power ON with given VDD 1.8 or 3.0v */
|
/* Turn the power ON with given VDD 1.8 or 3.0v */
|
||||||
if (!ret)
|
if (!ret)
|
||||||
ret = mmc_slot(host).set_power(host->dev, host->slot_id, 1,
|
ret = mmc_pdata(host)->set_power(host->dev, 1, vdd);
|
||||||
vdd);
|
|
||||||
pm_runtime_get_sync(host->dev);
|
pm_runtime_get_sync(host->dev);
|
||||||
if (host->dbclk)
|
if (host->dbclk)
|
||||||
clk_prepare_enable(host->dbclk);
|
clk_prepare_enable(host->dbclk);
|
||||||
@ -1259,11 +1279,11 @@ err:
|
|||||||
/* Protect the card while the cover is open */
|
/* Protect the card while the cover is open */
|
||||||
static void omap_hsmmc_protect_card(struct omap_hsmmc_host *host)
|
static void omap_hsmmc_protect_card(struct omap_hsmmc_host *host)
|
||||||
{
|
{
|
||||||
if (!mmc_slot(host).get_cover_state)
|
if (!host->get_cover_state)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
host->reqs_blocked = 0;
|
host->reqs_blocked = 0;
|
||||||
if (mmc_slot(host).get_cover_state(host->dev, host->slot_id)) {
|
if (host->get_cover_state(host->dev)) {
|
||||||
if (host->protect_card) {
|
if (host->protect_card) {
|
||||||
dev_info(host->dev, "%s: cover is closed, "
|
dev_info(host->dev, "%s: cover is closed, "
|
||||||
"card is now accessible\n",
|
"card is now accessible\n",
|
||||||
@ -1286,13 +1306,12 @@ static void omap_hsmmc_protect_card(struct omap_hsmmc_host *host)
|
|||||||
static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id)
|
static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
struct omap_hsmmc_host *host = dev_id;
|
struct omap_hsmmc_host *host = dev_id;
|
||||||
struct omap_mmc_slot_data *slot = &mmc_slot(host);
|
|
||||||
int carddetect;
|
int carddetect;
|
||||||
|
|
||||||
sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch");
|
sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch");
|
||||||
|
|
||||||
if (slot->card_detect)
|
if (host->card_detect)
|
||||||
carddetect = slot->card_detect(host->dev, host->slot_id);
|
carddetect = host->card_detect(host->dev);
|
||||||
else {
|
else {
|
||||||
omap_hsmmc_protect_card(host);
|
omap_hsmmc_protect_card(host);
|
||||||
carddetect = -ENOSYS;
|
carddetect = -ENOSYS;
|
||||||
@ -1618,12 +1637,10 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||||||
if (ios->power_mode != host->power_mode) {
|
if (ios->power_mode != host->power_mode) {
|
||||||
switch (ios->power_mode) {
|
switch (ios->power_mode) {
|
||||||
case MMC_POWER_OFF:
|
case MMC_POWER_OFF:
|
||||||
mmc_slot(host).set_power(host->dev, host->slot_id,
|
mmc_pdata(host)->set_power(host->dev, 0, 0);
|
||||||
0, 0);
|
|
||||||
break;
|
break;
|
||||||
case MMC_POWER_UP:
|
case MMC_POWER_UP:
|
||||||
mmc_slot(host).set_power(host->dev, host->slot_id,
|
mmc_pdata(host)->set_power(host->dev, 1, ios->vdd);
|
||||||
1, ios->vdd);
|
|
||||||
break;
|
break;
|
||||||
case MMC_POWER_ON:
|
case MMC_POWER_ON:
|
||||||
do_send_init_stream = 1;
|
do_send_init_stream = 1;
|
||||||
@ -1668,26 +1685,26 @@ static int omap_hsmmc_get_cd(struct mmc_host *mmc)
|
|||||||
{
|
{
|
||||||
struct omap_hsmmc_host *host = mmc_priv(mmc);
|
struct omap_hsmmc_host *host = mmc_priv(mmc);
|
||||||
|
|
||||||
if (!mmc_slot(host).card_detect)
|
if (!host->card_detect)
|
||||||
return -ENOSYS;
|
return -ENOSYS;
|
||||||
return mmc_slot(host).card_detect(host->dev, host->slot_id);
|
return host->card_detect(host->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int omap_hsmmc_get_ro(struct mmc_host *mmc)
|
static int omap_hsmmc_get_ro(struct mmc_host *mmc)
|
||||||
{
|
{
|
||||||
struct omap_hsmmc_host *host = mmc_priv(mmc);
|
struct omap_hsmmc_host *host = mmc_priv(mmc);
|
||||||
|
|
||||||
if (!mmc_slot(host).get_ro)
|
if (!host->get_ro)
|
||||||
return -ENOSYS;
|
return -ENOSYS;
|
||||||
return mmc_slot(host).get_ro(host->dev, 0);
|
return host->get_ro(host->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void omap_hsmmc_init_card(struct mmc_host *mmc, struct mmc_card *card)
|
static void omap_hsmmc_init_card(struct mmc_host *mmc, struct mmc_card *card)
|
||||||
{
|
{
|
||||||
struct omap_hsmmc_host *host = mmc_priv(mmc);
|
struct omap_hsmmc_host *host = mmc_priv(mmc);
|
||||||
|
|
||||||
if (mmc_slot(host).init_card)
|
if (mmc_pdata(host)->init_card)
|
||||||
mmc_slot(host).init_card(card);
|
mmc_pdata(host)->init_card(card);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void omap_hsmmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
|
static void omap_hsmmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
|
||||||
@ -1957,9 +1974,9 @@ static const struct of_device_id omap_mmc_of_match[] = {
|
|||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, omap_mmc_of_match);
|
MODULE_DEVICE_TABLE(of, omap_mmc_of_match);
|
||||||
|
|
||||||
static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev)
|
static struct omap_hsmmc_platform_data *of_get_hsmmc_pdata(struct device *dev)
|
||||||
{
|
{
|
||||||
struct omap_mmc_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;
|
u32 bus_width, max_freq;
|
||||||
int cd_gpio, wp_gpio;
|
int cd_gpio, wp_gpio;
|
||||||
@ -1976,40 +1993,38 @@ static struct omap_mmc_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;
|
||||||
|
|
||||||
/* This driver only supports 1 slot */
|
pdata->switch_pin = cd_gpio;
|
||||||
pdata->nr_slots = 1;
|
pdata->gpio_wp = wp_gpio;
|
||||||
pdata->slots[0].switch_pin = cd_gpio;
|
|
||||||
pdata->slots[0].gpio_wp = wp_gpio;
|
|
||||||
|
|
||||||
if (of_find_property(np, "ti,non-removable", NULL)) {
|
if (of_find_property(np, "ti,non-removable", NULL)) {
|
||||||
pdata->slots[0].nonremovable = true;
|
pdata->nonremovable = true;
|
||||||
pdata->slots[0].no_regulator_off_init = true;
|
pdata->no_regulator_off_init = true;
|
||||||
}
|
}
|
||||||
of_property_read_u32(np, "bus-width", &bus_width);
|
of_property_read_u32(np, "bus-width", &bus_width);
|
||||||
if (bus_width == 4)
|
if (bus_width == 4)
|
||||||
pdata->slots[0].caps |= MMC_CAP_4_BIT_DATA;
|
pdata->caps |= MMC_CAP_4_BIT_DATA;
|
||||||
else if (bus_width == 8)
|
else if (bus_width == 8)
|
||||||
pdata->slots[0].caps |= MMC_CAP_8_BIT_DATA;
|
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->slots[0].features |= HSMMC_HAS_UPDATED_RESET;
|
pdata->features |= HSMMC_HAS_UPDATED_RESET;
|
||||||
|
|
||||||
if (!of_property_read_u32(np, "max-frequency", &max_freq))
|
if (!of_property_read_u32(np, "max-frequency", &max_freq))
|
||||||
pdata->max_freq = 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->slots[0].features |= HSMMC_HAS_HSPE_SUPPORT;
|
pdata->features |= HSMMC_HAS_HSPE_SUPPORT;
|
||||||
|
|
||||||
if (of_find_property(np, "keep-power-in-suspend", NULL))
|
if (of_find_property(np, "keep-power-in-suspend", NULL))
|
||||||
pdata->slots[0].pm_caps |= MMC_PM_KEEP_POWER;
|
pdata->pm_caps |= MMC_PM_KEEP_POWER;
|
||||||
|
|
||||||
if (of_find_property(np, "enable-sdio-wakeup", NULL))
|
if (of_find_property(np, "enable-sdio-wakeup", NULL))
|
||||||
pdata->slots[0].pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
|
pdata->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
|
||||||
|
|
||||||
return pdata;
|
return pdata;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static inline struct omap_mmc_platform_data
|
static inline struct omap_hsmmc_platform_data
|
||||||
*of_get_hsmmc_pdata(struct device *dev)
|
*of_get_hsmmc_pdata(struct device *dev)
|
||||||
{
|
{
|
||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
@ -2018,7 +2033,7 @@ static inline struct omap_mmc_platform_data
|
|||||||
|
|
||||||
static int omap_hsmmc_probe(struct platform_device *pdev)
|
static int omap_hsmmc_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct omap_mmc_platform_data *pdata = pdev->dev.platform_data;
|
struct omap_hsmmc_platform_data *pdata = pdev->dev.platform_data;
|
||||||
struct mmc_host *mmc;
|
struct mmc_host *mmc;
|
||||||
struct omap_hsmmc_host *host = NULL;
|
struct omap_hsmmc_host *host = NULL;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
@ -2048,11 +2063,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
|||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pdata->nr_slots == 0) {
|
|
||||||
dev_err(&pdev->dev, "No Slots\n");
|
|
||||||
return -ENXIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
irq = platform_get_irq(pdev, 0);
|
irq = platform_get_irq(pdev, 0);
|
||||||
if (res == NULL || irq < 0)
|
if (res == NULL || irq < 0)
|
||||||
@ -2062,14 +2072,10 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
|||||||
if (IS_ERR(base))
|
if (IS_ERR(base))
|
||||||
return PTR_ERR(base);
|
return PTR_ERR(base);
|
||||||
|
|
||||||
ret = omap_hsmmc_gpio_init(pdata);
|
|
||||||
if (ret)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
mmc = mmc_alloc_host(sizeof(struct omap_hsmmc_host), &pdev->dev);
|
mmc = mmc_alloc_host(sizeof(struct omap_hsmmc_host), &pdev->dev);
|
||||||
if (!mmc) {
|
if (!mmc) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto err_alloc;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
host = mmc_priv(mmc);
|
host = mmc_priv(mmc);
|
||||||
@ -2079,13 +2085,16 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
|||||||
host->use_dma = 1;
|
host->use_dma = 1;
|
||||||
host->dma_ch = -1;
|
host->dma_ch = -1;
|
||||||
host->irq = irq;
|
host->irq = irq;
|
||||||
host->slot_id = 0;
|
|
||||||
host->mapbase = res->start + pdata->reg_offset;
|
host->mapbase = res->start + pdata->reg_offset;
|
||||||
host->base = base + pdata->reg_offset;
|
host->base = base + pdata->reg_offset;
|
||||||
host->power_mode = MMC_POWER_OFF;
|
host->power_mode = MMC_POWER_OFF;
|
||||||
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);
|
||||||
|
if (ret)
|
||||||
|
goto err_gpio;
|
||||||
|
|
||||||
platform_set_drvdata(pdev, host);
|
platform_set_drvdata(pdev, host);
|
||||||
|
|
||||||
if (pdev->dev.of_node)
|
if (pdev->dev.of_node)
|
||||||
@ -2144,14 +2153,14 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
|||||||
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
|
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
|
||||||
MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_ERASE;
|
MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_ERASE;
|
||||||
|
|
||||||
mmc->caps |= mmc_slot(host).caps;
|
mmc->caps |= mmc_pdata(host)->caps;
|
||||||
if (mmc->caps & MMC_CAP_8_BIT_DATA)
|
if (mmc->caps & MMC_CAP_8_BIT_DATA)
|
||||||
mmc->caps |= MMC_CAP_4_BIT_DATA;
|
mmc->caps |= MMC_CAP_4_BIT_DATA;
|
||||||
|
|
||||||
if (mmc_slot(host).nonremovable)
|
if (mmc_pdata(host)->nonremovable)
|
||||||
mmc->caps |= MMC_CAP_NONREMOVABLE;
|
mmc->caps |= MMC_CAP_NONREMOVABLE;
|
||||||
|
|
||||||
mmc->pm_caps = mmc_slot(host).pm_caps;
|
mmc->pm_caps = mmc_pdata(host)->pm_caps;
|
||||||
|
|
||||||
omap_hsmmc_conf_bus_power(host);
|
omap_hsmmc_conf_bus_power(host);
|
||||||
|
|
||||||
@ -2204,27 +2213,19 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
|||||||
goto err_irq;
|
goto err_irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pdata->init != NULL) {
|
if (omap_hsmmc_have_reg() && !mmc_pdata(host)->set_power) {
|
||||||
if (pdata->init(&pdev->dev) != 0) {
|
|
||||||
dev_err(mmc_dev(host->mmc),
|
|
||||||
"Unable to configure MMC IRQs\n");
|
|
||||||
goto err_irq;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (omap_hsmmc_have_reg() && !mmc_slot(host).set_power) {
|
|
||||||
ret = omap_hsmmc_reg_get(host);
|
ret = omap_hsmmc_reg_get(host);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_reg;
|
goto err_irq;
|
||||||
host->use_reg = 1;
|
host->use_reg = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
mmc->ocr_avail = mmc_slot(host).ocr_mask;
|
mmc->ocr_avail = mmc_pdata(host)->ocr_mask;
|
||||||
|
|
||||||
/* Request IRQ for card detect */
|
/* Request IRQ for card detect */
|
||||||
if ((mmc_slot(host).card_detect_irq)) {
|
if (host->card_detect_irq) {
|
||||||
ret = devm_request_threaded_irq(&pdev->dev,
|
ret = devm_request_threaded_irq(&pdev->dev,
|
||||||
mmc_slot(host).card_detect_irq,
|
host->card_detect_irq,
|
||||||
NULL, omap_hsmmc_detect,
|
NULL, omap_hsmmc_detect,
|
||||||
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||||
mmc_hostname(mmc), host);
|
mmc_hostname(mmc), host);
|
||||||
@ -2233,8 +2234,8 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
|||||||
"Unable to grab MMC CD IRQ\n");
|
"Unable to grab MMC CD IRQ\n");
|
||||||
goto err_irq_cd;
|
goto err_irq_cd;
|
||||||
}
|
}
|
||||||
pdata->suspend = omap_hsmmc_suspend_cdirq;
|
host->suspend = omap_hsmmc_suspend_cdirq;
|
||||||
pdata->resume = omap_hsmmc_resume_cdirq;
|
host->resume = omap_hsmmc_resume_cdirq;
|
||||||
}
|
}
|
||||||
|
|
||||||
omap_hsmmc_disable_irq(host);
|
omap_hsmmc_disable_irq(host);
|
||||||
@ -2255,12 +2256,12 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
mmc_add_host(mmc);
|
mmc_add_host(mmc);
|
||||||
|
|
||||||
if (mmc_slot(host).name != NULL) {
|
if (mmc_pdata(host)->name != NULL) {
|
||||||
ret = device_create_file(&mmc->class_dev, &dev_attr_slot_name);
|
ret = device_create_file(&mmc->class_dev, &dev_attr_slot_name);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_slot_name;
|
goto err_slot_name;
|
||||||
}
|
}
|
||||||
if (mmc_slot(host).card_detect_irq && mmc_slot(host).get_cover_state) {
|
if (host->card_detect_irq && host->get_cover_state) {
|
||||||
ret = device_create_file(&mmc->class_dev,
|
ret = device_create_file(&mmc->class_dev,
|
||||||
&dev_attr_cover_switch);
|
&dev_attr_cover_switch);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
@ -2278,9 +2279,6 @@ err_slot_name:
|
|||||||
err_irq_cd:
|
err_irq_cd:
|
||||||
if (host->use_reg)
|
if (host->use_reg)
|
||||||
omap_hsmmc_reg_put(host);
|
omap_hsmmc_reg_put(host);
|
||||||
err_reg:
|
|
||||||
if (host->pdata->cleanup)
|
|
||||||
host->pdata->cleanup(&pdev->dev);
|
|
||||||
err_irq:
|
err_irq:
|
||||||
if (host->tx_chan)
|
if (host->tx_chan)
|
||||||
dma_release_channel(host->tx_chan);
|
dma_release_channel(host->tx_chan);
|
||||||
@ -2291,9 +2289,9 @@ 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:
|
||||||
mmc_free_host(mmc);
|
mmc_free_host(mmc);
|
||||||
err_alloc:
|
|
||||||
omap_hsmmc_gpio_free(pdata);
|
|
||||||
err:
|
err:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -2306,8 +2304,6 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
|
|||||||
mmc_remove_host(host->mmc);
|
mmc_remove_host(host->mmc);
|
||||||
if (host->use_reg)
|
if (host->use_reg)
|
||||||
omap_hsmmc_reg_put(host);
|
omap_hsmmc_reg_put(host);
|
||||||
if (host->pdata->cleanup)
|
|
||||||
host->pdata->cleanup(&pdev->dev);
|
|
||||||
|
|
||||||
if (host->tx_chan)
|
if (host->tx_chan)
|
||||||
dma_release_channel(host->tx_chan);
|
dma_release_channel(host->tx_chan);
|
||||||
@ -2319,7 +2315,7 @@ 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->pdata);
|
omap_hsmmc_gpio_free(host, host->pdata);
|
||||||
mmc_free_host(host->mmc);
|
mmc_free_host(host->mmc);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -2330,8 +2326,8 @@ static int omap_hsmmc_prepare(struct device *dev)
|
|||||||
{
|
{
|
||||||
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
|
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
|
||||||
|
|
||||||
if (host->pdata->suspend)
|
if (host->suspend)
|
||||||
return host->pdata->suspend(dev, host->slot_id);
|
return host->suspend(dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -2340,8 +2336,8 @@ static void omap_hsmmc_complete(struct device *dev)
|
|||||||
{
|
{
|
||||||
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
|
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
|
||||||
|
|
||||||
if (host->pdata->resume)
|
if (host->resume)
|
||||||
host->pdata->resume(dev, host->slot_id);
|
host->resume(dev);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,6 +76,7 @@ struct sdhci_acpi_host {
|
|||||||
const struct sdhci_acpi_slot *slot;
|
const struct sdhci_acpi_slot *slot;
|
||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
bool use_runtime_pm;
|
bool use_runtime_pm;
|
||||||
|
bool dma_setup;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline bool sdhci_acpi_flag(struct sdhci_acpi_host *c, unsigned int flag)
|
static inline bool sdhci_acpi_flag(struct sdhci_acpi_host *c, unsigned int flag)
|
||||||
@ -85,7 +86,29 @@ static inline bool sdhci_acpi_flag(struct sdhci_acpi_host *c, unsigned int flag)
|
|||||||
|
|
||||||
static int sdhci_acpi_enable_dma(struct sdhci_host *host)
|
static int sdhci_acpi_enable_dma(struct sdhci_host *host)
|
||||||
{
|
{
|
||||||
return 0;
|
struct sdhci_acpi_host *c = sdhci_priv(host);
|
||||||
|
struct device *dev = &c->pdev->dev;
|
||||||
|
int err = -1;
|
||||||
|
|
||||||
|
if (c->dma_setup)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (host->flags & SDHCI_USE_64_BIT_DMA) {
|
||||||
|
if (host->quirks2 & SDHCI_QUIRK2_BROKEN_64_BIT_DMA) {
|
||||||
|
host->flags &= ~SDHCI_USE_64_BIT_DMA;
|
||||||
|
} else {
|
||||||
|
err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
|
||||||
|
if (err)
|
||||||
|
dev_warn(dev, "Failed to set 64-bit DMA mask\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
|
||||||
|
|
||||||
|
c->dma_setup = !err;
|
||||||
|
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sdhci_acpi_int_hw_reset(struct sdhci_host *host)
|
static void sdhci_acpi_int_hw_reset(struct sdhci_host *host)
|
||||||
@ -180,17 +203,21 @@ static int sdhci_acpi_sd_probe_slot(struct platform_device *pdev,
|
|||||||
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = {
|
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = {
|
||||||
.chip = &sdhci_acpi_chip_int,
|
.chip = &sdhci_acpi_chip_int,
|
||||||
.caps = MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE |
|
.caps = MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE |
|
||||||
MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR,
|
MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR |
|
||||||
|
MMC_CAP_BUS_WIDTH_TEST | MMC_CAP_WAIT_WHILE_BUSY,
|
||||||
.caps2 = MMC_CAP2_HC_ERASE_SZ,
|
.caps2 = MMC_CAP2_HC_ERASE_SZ,
|
||||||
.flags = SDHCI_ACPI_RUNTIME_PM,
|
.flags = SDHCI_ACPI_RUNTIME_PM,
|
||||||
|
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_STOP_WITH_TC,
|
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_STOP_WITH_TC,
|
||||||
.probe_slot = sdhci_acpi_emmc_probe_slot,
|
.probe_slot = sdhci_acpi_emmc_probe_slot,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {
|
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {
|
||||||
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION,
|
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
|
||||||
|
SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||||
.quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON,
|
.quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON,
|
||||||
.caps = MMC_CAP_NONREMOVABLE | MMC_CAP_POWER_OFF_CARD,
|
.caps = MMC_CAP_NONREMOVABLE | MMC_CAP_POWER_OFF_CARD |
|
||||||
|
MMC_CAP_BUS_WIDTH_TEST | MMC_CAP_WAIT_WHILE_BUSY,
|
||||||
.flags = SDHCI_ACPI_RUNTIME_PM,
|
.flags = SDHCI_ACPI_RUNTIME_PM,
|
||||||
.pm_caps = MMC_PM_KEEP_POWER,
|
.pm_caps = MMC_PM_KEEP_POWER,
|
||||||
.probe_slot = sdhci_acpi_sdio_probe_slot,
|
.probe_slot = sdhci_acpi_sdio_probe_slot,
|
||||||
@ -199,8 +226,10 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {
|
|||||||
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sd = {
|
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sd = {
|
||||||
.flags = SDHCI_ACPI_SD_CD | SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL |
|
.flags = SDHCI_ACPI_SD_CD | SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL |
|
||||||
SDHCI_ACPI_RUNTIME_PM,
|
SDHCI_ACPI_RUNTIME_PM,
|
||||||
|
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||||
.quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON |
|
.quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON |
|
||||||
SDHCI_QUIRK2_STOP_WITH_TC,
|
SDHCI_QUIRK2_STOP_WITH_TC,
|
||||||
|
.caps = MMC_CAP_BUS_WIDTH_TEST | MMC_CAP_WAIT_WHILE_BUSY,
|
||||||
.probe_slot = sdhci_acpi_sd_probe_slot,
|
.probe_slot = sdhci_acpi_sd_probe_slot,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -305,21 +334,6 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
|
|||||||
goto err_free;
|
goto err_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dev->dma_mask) {
|
|
||||||
u64 dma_mask;
|
|
||||||
|
|
||||||
if (sdhci_readl(host, SDHCI_CAPABILITIES) & SDHCI_CAN_64BIT) {
|
|
||||||
/* 64-bit DMA is not supported at present */
|
|
||||||
dma_mask = DMA_BIT_MASK(32);
|
|
||||||
} else {
|
|
||||||
dma_mask = DMA_BIT_MASK(32);
|
|
||||||
}
|
|
||||||
|
|
||||||
err = dma_coerce_mask_and_coherent(dev, dma_mask);
|
|
||||||
if (err)
|
|
||||||
goto err_free;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c->slot) {
|
if (c->slot) {
|
||||||
if (c->slot->probe_slot) {
|
if (c->slot->probe_slot) {
|
||||||
err = c->slot->probe_slot(pdev, hid, uid);
|
err = c->slot->probe_slot(pdev, hid, uid);
|
||||||
|
@ -65,8 +65,6 @@
|
|||||||
/* NOTE: the minimum valid tuning start tap for mx6sl is 1 */
|
/* NOTE: the minimum valid tuning start tap for mx6sl is 1 */
|
||||||
#define ESDHC_TUNING_START_TAP 0x1
|
#define ESDHC_TUNING_START_TAP 0x1
|
||||||
|
|
||||||
#define ESDHC_TUNING_BLOCK_PATTERN_LEN 64
|
|
||||||
|
|
||||||
/* pinctrl state */
|
/* pinctrl state */
|
||||||
#define ESDHC_PINCTRL_STATE_100MHZ "state_100mhz"
|
#define ESDHC_PINCTRL_STATE_100MHZ "state_100mhz"
|
||||||
#define ESDHC_PINCTRL_STATE_200MHZ "state_200mhz"
|
#define ESDHC_PINCTRL_STATE_200MHZ "state_200mhz"
|
||||||
@ -692,8 +690,6 @@ static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
|
|||||||
/* FIXME: delay a bit for card to be ready for next tuning due to errors */
|
/* FIXME: delay a bit for card to be ready for next tuning due to errors */
|
||||||
mdelay(1);
|
mdelay(1);
|
||||||
|
|
||||||
/* This is balanced by the runtime put in sdhci_tasklet_finish */
|
|
||||||
pm_runtime_get_sync(host->mmc->parent);
|
|
||||||
reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
||||||
reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL |
|
reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL |
|
||||||
ESDHC_MIX_CTRL_FBCLK_SEL;
|
ESDHC_MIX_CTRL_FBCLK_SEL;
|
||||||
@ -704,54 +700,6 @@ static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
|
|||||||
val, readl(host->ioaddr + ESDHC_TUNE_CTRL_STATUS));
|
val, readl(host->ioaddr + ESDHC_TUNE_CTRL_STATUS));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void esdhc_request_done(struct mmc_request *mrq)
|
|
||||||
{
|
|
||||||
complete(&mrq->completion);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode,
|
|
||||||
struct scatterlist *sg)
|
|
||||||
{
|
|
||||||
struct mmc_command cmd = {0};
|
|
||||||
struct mmc_request mrq = {NULL};
|
|
||||||
struct mmc_data data = {0};
|
|
||||||
|
|
||||||
cmd.opcode = opcode;
|
|
||||||
cmd.arg = 0;
|
|
||||||
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
|
|
||||||
|
|
||||||
data.blksz = ESDHC_TUNING_BLOCK_PATTERN_LEN;
|
|
||||||
data.blocks = 1;
|
|
||||||
data.flags = MMC_DATA_READ;
|
|
||||||
data.sg = sg;
|
|
||||||
data.sg_len = 1;
|
|
||||||
|
|
||||||
mrq.cmd = &cmd;
|
|
||||||
mrq.cmd->mrq = &mrq;
|
|
||||||
mrq.data = &data;
|
|
||||||
mrq.data->mrq = &mrq;
|
|
||||||
mrq.cmd->data = mrq.data;
|
|
||||||
|
|
||||||
mrq.done = esdhc_request_done;
|
|
||||||
init_completion(&(mrq.completion));
|
|
||||||
|
|
||||||
spin_lock_irq(&host->lock);
|
|
||||||
host->mrq = &mrq;
|
|
||||||
|
|
||||||
sdhci_send_command(host, mrq.cmd);
|
|
||||||
|
|
||||||
spin_unlock_irq(&host->lock);
|
|
||||||
|
|
||||||
wait_for_completion(&mrq.completion);
|
|
||||||
|
|
||||||
if (cmd.error)
|
|
||||||
return cmd.error;
|
|
||||||
if (data.error)
|
|
||||||
return data.error;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void esdhc_post_tuning(struct sdhci_host *host)
|
static void esdhc_post_tuning(struct sdhci_host *host)
|
||||||
{
|
{
|
||||||
u32 reg;
|
u32 reg;
|
||||||
@ -763,21 +711,13 @@ static void esdhc_post_tuning(struct sdhci_host *host)
|
|||||||
|
|
||||||
static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
|
static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
|
||||||
{
|
{
|
||||||
struct scatterlist sg;
|
|
||||||
char *tuning_pattern;
|
|
||||||
int min, max, avg, ret;
|
int min, max, avg, ret;
|
||||||
|
|
||||||
tuning_pattern = kmalloc(ESDHC_TUNING_BLOCK_PATTERN_LEN, GFP_KERNEL);
|
|
||||||
if (!tuning_pattern)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
sg_init_one(&sg, tuning_pattern, ESDHC_TUNING_BLOCK_PATTERN_LEN);
|
|
||||||
|
|
||||||
/* find the mininum delay first which can pass tuning */
|
/* find the mininum delay first which can pass tuning */
|
||||||
min = ESDHC_TUNE_CTRL_MIN;
|
min = ESDHC_TUNE_CTRL_MIN;
|
||||||
while (min < ESDHC_TUNE_CTRL_MAX) {
|
while (min < ESDHC_TUNE_CTRL_MAX) {
|
||||||
esdhc_prepare_tuning(host, min);
|
esdhc_prepare_tuning(host, min);
|
||||||
if (!esdhc_send_tuning_cmd(host, opcode, &sg))
|
if (!mmc_send_tuning(host->mmc))
|
||||||
break;
|
break;
|
||||||
min += ESDHC_TUNE_CTRL_STEP;
|
min += ESDHC_TUNE_CTRL_STEP;
|
||||||
}
|
}
|
||||||
@ -786,7 +726,7 @@ static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
|
|||||||
max = min + ESDHC_TUNE_CTRL_STEP;
|
max = min + ESDHC_TUNE_CTRL_STEP;
|
||||||
while (max < ESDHC_TUNE_CTRL_MAX) {
|
while (max < ESDHC_TUNE_CTRL_MAX) {
|
||||||
esdhc_prepare_tuning(host, max);
|
esdhc_prepare_tuning(host, max);
|
||||||
if (esdhc_send_tuning_cmd(host, opcode, &sg)) {
|
if (mmc_send_tuning(host->mmc)) {
|
||||||
max -= ESDHC_TUNE_CTRL_STEP;
|
max -= ESDHC_TUNE_CTRL_STEP;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -796,11 +736,9 @@ static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
|
|||||||
/* use average delay to get the best timing */
|
/* use average delay to get the best timing */
|
||||||
avg = (min + max) / 2;
|
avg = (min + max) / 2;
|
||||||
esdhc_prepare_tuning(host, avg);
|
esdhc_prepare_tuning(host, avg);
|
||||||
ret = esdhc_send_tuning_cmd(host, opcode, &sg);
|
ret = mmc_send_tuning(host->mmc);
|
||||||
esdhc_post_tuning(host);
|
esdhc_post_tuning(host);
|
||||||
|
|
||||||
kfree(tuning_pattern);
|
|
||||||
|
|
||||||
dev_dbg(mmc_dev(host->mmc), "tunning %s at 0x%x ret %d\n",
|
dev_dbg(mmc_dev(host->mmc), "tunning %s at 0x%x ret %d\n",
|
||||||
ret ? "failed" : "passed", avg, ret);
|
ret ? "failed" : "passed", avg, ret);
|
||||||
|
|
||||||
@ -1031,11 +969,8 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
imx_data->pins_default = pinctrl_lookup_state(imx_data->pinctrl,
|
imx_data->pins_default = pinctrl_lookup_state(imx_data->pinctrl,
|
||||||
PINCTRL_STATE_DEFAULT);
|
PINCTRL_STATE_DEFAULT);
|
||||||
if (IS_ERR(imx_data->pins_default)) {
|
if (IS_ERR(imx_data->pins_default))
|
||||||
err = PTR_ERR(imx_data->pins_default);
|
dev_warn(mmc_dev(host->mmc), "could not get default state\n");
|
||||||
dev_err(mmc_dev(host->mmc), "could not get default state\n");
|
|
||||||
goto disable_clk;
|
|
||||||
}
|
|
||||||
|
|
||||||
host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
|
host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
|
||||||
|
|
||||||
@ -1123,7 +1058,8 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* sdr50 and sdr104 needs work on 1.8v signal voltage */
|
/* sdr50 and sdr104 needs work on 1.8v signal voltage */
|
||||||
if ((boarddata->support_vsel) && esdhc_is_usdhc(imx_data)) {
|
if ((boarddata->support_vsel) && esdhc_is_usdhc(imx_data) &&
|
||||||
|
!IS_ERR(imx_data->pins_default)) {
|
||||||
imx_data->pins_100mhz = pinctrl_lookup_state(imx_data->pinctrl,
|
imx_data->pins_100mhz = pinctrl_lookup_state(imx_data->pinctrl,
|
||||||
ESDHC_PINCTRL_STATE_100MHZ);
|
ESDHC_PINCTRL_STATE_100MHZ);
|
||||||
imx_data->pins_200mhz = pinctrl_lookup_state(imx_data->pinctrl,
|
imx_data->pins_200mhz = pinctrl_lookup_state(imx_data->pinctrl,
|
||||||
|
@ -339,9 +339,7 @@ static int msm_init_cm_dll(struct sdhci_host *host)
|
|||||||
static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
|
static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
|
||||||
{
|
{
|
||||||
int tuning_seq_cnt = 3;
|
int tuning_seq_cnt = 3;
|
||||||
u8 phase, *data_buf, tuned_phases[16], tuned_phase_cnt = 0;
|
u8 phase, tuned_phases[16], tuned_phase_cnt = 0;
|
||||||
const u8 *tuning_block_pattern = tuning_blk_pattern_4bit;
|
|
||||||
int size = sizeof(tuning_blk_pattern_4bit);
|
|
||||||
int rc;
|
int rc;
|
||||||
struct mmc_host *mmc = host->mmc;
|
struct mmc_host *mmc = host->mmc;
|
||||||
struct mmc_ios ios = host->mmc->ios;
|
struct mmc_ios ios = host->mmc->ios;
|
||||||
@ -355,53 +353,21 @@ static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
|
|||||||
(ios.timing == MMC_TIMING_UHS_SDR104)))
|
(ios.timing == MMC_TIMING_UHS_SDR104)))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if ((opcode == MMC_SEND_TUNING_BLOCK_HS200) &&
|
|
||||||
(mmc->ios.bus_width == MMC_BUS_WIDTH_8)) {
|
|
||||||
tuning_block_pattern = tuning_blk_pattern_8bit;
|
|
||||||
size = sizeof(tuning_blk_pattern_8bit);
|
|
||||||
}
|
|
||||||
|
|
||||||
data_buf = kmalloc(size, GFP_KERNEL);
|
|
||||||
if (!data_buf)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
retry:
|
retry:
|
||||||
/* First of all reset the tuning block */
|
/* First of all reset the tuning block */
|
||||||
rc = msm_init_cm_dll(host);
|
rc = msm_init_cm_dll(host);
|
||||||
if (rc)
|
if (rc)
|
||||||
goto out;
|
return rc;
|
||||||
|
|
||||||
phase = 0;
|
phase = 0;
|
||||||
do {
|
do {
|
||||||
struct mmc_command cmd = { 0 };
|
|
||||||
struct mmc_data data = { 0 };
|
|
||||||
struct mmc_request mrq = {
|
|
||||||
.cmd = &cmd,
|
|
||||||
.data = &data
|
|
||||||
};
|
|
||||||
struct scatterlist sg;
|
|
||||||
|
|
||||||
/* Set the phase in delay line hw block */
|
/* Set the phase in delay line hw block */
|
||||||
rc = msm_config_cm_dll_phase(host, phase);
|
rc = msm_config_cm_dll_phase(host, phase);
|
||||||
if (rc)
|
if (rc)
|
||||||
goto out;
|
return rc;
|
||||||
|
|
||||||
cmd.opcode = opcode;
|
rc = mmc_send_tuning(mmc);
|
||||||
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
|
if (!rc) {
|
||||||
|
|
||||||
data.blksz = size;
|
|
||||||
data.blocks = 1;
|
|
||||||
data.flags = MMC_DATA_READ;
|
|
||||||
data.timeout_ns = NSEC_PER_SEC; /* 1 second */
|
|
||||||
|
|
||||||
data.sg = &sg;
|
|
||||||
data.sg_len = 1;
|
|
||||||
sg_init_one(&sg, data_buf, size);
|
|
||||||
memset(data_buf, 0, size);
|
|
||||||
mmc_wait_for_req(mmc, &mrq);
|
|
||||||
|
|
||||||
if (!cmd.error && !data.error &&
|
|
||||||
!memcmp(data_buf, tuning_block_pattern, size)) {
|
|
||||||
/* Tuning is successful at this tuning point */
|
/* Tuning is successful at this tuning point */
|
||||||
tuned_phases[tuned_phase_cnt++] = phase;
|
tuned_phases[tuned_phase_cnt++] = phase;
|
||||||
dev_dbg(mmc_dev(mmc), "%s: Found good phase = %d\n",
|
dev_dbg(mmc_dev(mmc), "%s: Found good phase = %d\n",
|
||||||
@ -413,7 +379,7 @@ retry:
|
|||||||
rc = msm_find_most_appropriate_phase(host, tuned_phases,
|
rc = msm_find_most_appropriate_phase(host, tuned_phases,
|
||||||
tuned_phase_cnt);
|
tuned_phase_cnt);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
goto out;
|
return rc;
|
||||||
else
|
else
|
||||||
phase = rc;
|
phase = rc;
|
||||||
|
|
||||||
@ -423,7 +389,7 @@ retry:
|
|||||||
*/
|
*/
|
||||||
rc = msm_config_cm_dll_phase(host, phase);
|
rc = msm_config_cm_dll_phase(host, phase);
|
||||||
if (rc)
|
if (rc)
|
||||||
goto out;
|
return rc;
|
||||||
dev_dbg(mmc_dev(mmc), "%s: Setting the tuning phase to %d\n",
|
dev_dbg(mmc_dev(mmc), "%s: Setting the tuning phase to %d\n",
|
||||||
mmc_hostname(mmc), phase);
|
mmc_hostname(mmc), phase);
|
||||||
} else {
|
} else {
|
||||||
@ -435,8 +401,6 @@ retry:
|
|||||||
rc = -EIO;
|
rc = -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
|
||||||
kfree(data_buf);
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,7 +165,6 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
|
|||||||
host = sdhci_pltfm_init(pdev, &sdhci_arasan_pdata, 0);
|
host = sdhci_pltfm_init(pdev, &sdhci_arasan_pdata, 0);
|
||||||
if (IS_ERR(host)) {
|
if (IS_ERR(host)) {
|
||||||
ret = PTR_ERR(host);
|
ret = PTR_ERR(host);
|
||||||
dev_err(&pdev->dev, "platform init failed (%u)\n", ret);
|
|
||||||
goto clk_disable_all;
|
goto clk_disable_all;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,10 +174,8 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
|
|||||||
pltfm_host->clk = clk_xin;
|
pltfm_host->clk = clk_xin;
|
||||||
|
|
||||||
ret = sdhci_add_host(host);
|
ret = sdhci_add_host(host);
|
||||||
if (ret) {
|
if (ret)
|
||||||
dev_err(&pdev->dev, "platform register failed (%u)\n", ret);
|
|
||||||
goto err_pltfm_free;
|
goto err_pltfm_free;
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -127,8 +127,6 @@ void sdhci_pci_o2_fujin2_pci_init(struct sdhci_pci_chip *chip)
|
|||||||
return;
|
return;
|
||||||
scratch_32 &= ~((1 << 21) | (1 << 30));
|
scratch_32 &= ~((1 << 21) | (1 << 30));
|
||||||
|
|
||||||
/* Set RTD3 function disabled */
|
|
||||||
scratch_32 |= ((1 << 29) | (1 << 28));
|
|
||||||
pci_write_config_dword(chip->pdev, O2_SD_FUNC_REG3, scratch_32);
|
pci_write_config_dword(chip->pdev, O2_SD_FUNC_REG3, scratch_32);
|
||||||
|
|
||||||
/* Set L1 Entrance Timer */
|
/* Set L1 Entrance Timer */
|
||||||
|
@ -269,7 +269,9 @@ static void sdhci_pci_int_hw_reset(struct sdhci_host *host)
|
|||||||
static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
|
static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
|
||||||
{
|
{
|
||||||
slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE |
|
slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE |
|
||||||
MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR;
|
MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR |
|
||||||
|
MMC_CAP_BUS_WIDTH_TEST |
|
||||||
|
MMC_CAP_WAIT_WHILE_BUSY;
|
||||||
slot->host->mmc->caps2 |= MMC_CAP2_HC_ERASE_SZ;
|
slot->host->mmc->caps2 |= MMC_CAP2_HC_ERASE_SZ;
|
||||||
slot->hw_reset = sdhci_pci_int_hw_reset;
|
slot->hw_reset = sdhci_pci_int_hw_reset;
|
||||||
if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_BSW_EMMC)
|
if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_BSW_EMMC)
|
||||||
@ -279,12 +281,16 @@ static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
|
|||||||
|
|
||||||
static int byt_sdio_probe_slot(struct sdhci_pci_slot *slot)
|
static int byt_sdio_probe_slot(struct sdhci_pci_slot *slot)
|
||||||
{
|
{
|
||||||
slot->host->mmc->caps |= MMC_CAP_POWER_OFF_CARD | MMC_CAP_NONREMOVABLE;
|
slot->host->mmc->caps |= MMC_CAP_POWER_OFF_CARD | MMC_CAP_NONREMOVABLE |
|
||||||
|
MMC_CAP_BUS_WIDTH_TEST |
|
||||||
|
MMC_CAP_WAIT_WHILE_BUSY;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int byt_sd_probe_slot(struct sdhci_pci_slot *slot)
|
static int byt_sd_probe_slot(struct sdhci_pci_slot *slot)
|
||||||
{
|
{
|
||||||
|
slot->host->mmc->caps |= MMC_CAP_BUS_WIDTH_TEST |
|
||||||
|
MMC_CAP_WAIT_WHILE_BUSY;
|
||||||
slot->cd_con_id = NULL;
|
slot->cd_con_id = NULL;
|
||||||
slot->cd_idx = 0;
|
slot->cd_idx = 0;
|
||||||
slot->cd_override_level = true;
|
slot->cd_override_level = true;
|
||||||
@ -294,11 +300,13 @@ static int byt_sd_probe_slot(struct sdhci_pci_slot *slot)
|
|||||||
static const struct sdhci_pci_fixes sdhci_intel_byt_emmc = {
|
static const struct sdhci_pci_fixes sdhci_intel_byt_emmc = {
|
||||||
.allow_runtime_pm = true,
|
.allow_runtime_pm = true,
|
||||||
.probe_slot = byt_emmc_probe_slot,
|
.probe_slot = byt_emmc_probe_slot,
|
||||||
|
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||||
SDHCI_QUIRK2_STOP_WITH_TC,
|
SDHCI_QUIRK2_STOP_WITH_TC,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = {
|
static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = {
|
||||||
|
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||||
.quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON |
|
.quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON |
|
||||||
SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
||||||
.allow_runtime_pm = true,
|
.allow_runtime_pm = true,
|
||||||
@ -306,6 +314,7 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const struct sdhci_pci_fixes sdhci_intel_byt_sd = {
|
static const struct sdhci_pci_fixes sdhci_intel_byt_sd = {
|
||||||
|
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||||
.quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON |
|
.quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON |
|
||||||
SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||||
SDHCI_QUIRK2_STOP_WITH_TC,
|
SDHCI_QUIRK2_STOP_WITH_TC,
|
||||||
@ -645,6 +654,25 @@ static const struct sdhci_pci_fixes sdhci_rtsx = {
|
|||||||
.probe_slot = rtsx_probe_slot,
|
.probe_slot = rtsx_probe_slot,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int amd_probe(struct sdhci_pci_chip *chip)
|
||||||
|
{
|
||||||
|
struct pci_dev *smbus_dev;
|
||||||
|
|
||||||
|
smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD,
|
||||||
|
PCI_DEVICE_ID_AMD_HUDSON2_SMBUS, NULL);
|
||||||
|
|
||||||
|
if (smbus_dev && (smbus_dev->revision < 0x51)) {
|
||||||
|
chip->quirks2 |= SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD;
|
||||||
|
chip->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct sdhci_pci_fixes sdhci_amd = {
|
||||||
|
.probe = amd_probe,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct pci_device_id pci_ids[] = {
|
static const struct pci_device_id pci_ids[] = {
|
||||||
{
|
{
|
||||||
.vendor = PCI_VENDOR_ID_RICOH,
|
.vendor = PCI_VENDOR_ID_RICOH,
|
||||||
@ -1044,7 +1072,15 @@ static const struct pci_device_id pci_ids[] = {
|
|||||||
.subdevice = PCI_ANY_ID,
|
.subdevice = PCI_ANY_ID,
|
||||||
.driver_data = (kernel_ulong_t)&sdhci_o2,
|
.driver_data = (kernel_ulong_t)&sdhci_o2,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.vendor = PCI_VENDOR_ID_AMD,
|
||||||
|
.device = PCI_ANY_ID,
|
||||||
|
.class = PCI_CLASS_SYSTEM_SDHCI << 8,
|
||||||
|
.class_mask = 0xFFFF00,
|
||||||
|
.subvendor = PCI_ANY_ID,
|
||||||
|
.subdevice = PCI_ANY_ID,
|
||||||
|
.driver_data = (kernel_ulong_t)&sdhci_amd,
|
||||||
|
},
|
||||||
{ /* Generic SD host controller */
|
{ /* Generic SD host controller */
|
||||||
PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00)
|
PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00)
|
||||||
},
|
},
|
||||||
@ -1064,7 +1100,7 @@ static int sdhci_pci_enable_dma(struct sdhci_host *host)
|
|||||||
{
|
{
|
||||||
struct sdhci_pci_slot *slot;
|
struct sdhci_pci_slot *slot;
|
||||||
struct pci_dev *pdev;
|
struct pci_dev *pdev;
|
||||||
int ret;
|
int ret = -1;
|
||||||
|
|
||||||
slot = sdhci_priv(host);
|
slot = sdhci_priv(host);
|
||||||
pdev = slot->chip->pdev;
|
pdev = slot->chip->pdev;
|
||||||
@ -1076,7 +1112,17 @@ static int sdhci_pci_enable_dma(struct sdhci_host *host)
|
|||||||
"doesn't fully claim to support it.\n");
|
"doesn't fully claim to support it.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
|
if (host->flags & SDHCI_USE_64_BIT_DMA) {
|
||||||
|
if (host->quirks2 & SDHCI_QUIRK2_BROKEN_64_BIT_DMA) {
|
||||||
|
host->flags &= ~SDHCI_USE_64_BIT_DMA;
|
||||||
|
} else {
|
||||||
|
ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
|
||||||
|
if (ret)
|
||||||
|
dev_warn(&pdev->dev, "Failed to set 64-bit DMA mask\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ret)
|
||||||
|
ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -167,23 +167,17 @@ static int sdhci_pxav2_probe(struct platform_device *pdev)
|
|||||||
struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data;
|
struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data;
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct sdhci_host *host = NULL;
|
struct sdhci_host *host = NULL;
|
||||||
struct sdhci_pxa *pxa = NULL;
|
|
||||||
const struct of_device_id *match;
|
const struct of_device_id *match;
|
||||||
|
|
||||||
int ret;
|
int ret;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
|
|
||||||
pxa = kzalloc(sizeof(struct sdhci_pxa), GFP_KERNEL);
|
|
||||||
if (!pxa)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
host = sdhci_pltfm_init(pdev, NULL, 0);
|
host = sdhci_pltfm_init(pdev, NULL, 0);
|
||||||
if (IS_ERR(host)) {
|
if (IS_ERR(host))
|
||||||
kfree(pxa);
|
|
||||||
return PTR_ERR(host);
|
return PTR_ERR(host);
|
||||||
}
|
|
||||||
pltfm_host = sdhci_priv(host);
|
pltfm_host = sdhci_priv(host);
|
||||||
pltfm_host->priv = pxa;
|
pltfm_host->priv = NULL;
|
||||||
|
|
||||||
clk = clk_get(dev, "PXA-SDHCLK");
|
clk = clk_get(dev, "PXA-SDHCLK");
|
||||||
if (IS_ERR(clk)) {
|
if (IS_ERR(clk)) {
|
||||||
@ -238,7 +232,6 @@ err_add_host:
|
|||||||
clk_put(clk);
|
clk_put(clk);
|
||||||
err_clk_get:
|
err_clk_get:
|
||||||
sdhci_pltfm_free(pdev);
|
sdhci_pltfm_free(pdev);
|
||||||
kfree(pxa);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,14 +239,12 @@ static int sdhci_pxav2_remove(struct platform_device *pdev)
|
|||||||
{
|
{
|
||||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||||
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;
|
|
||||||
|
|
||||||
sdhci_remove_host(host, 1);
|
sdhci_remove_host(host, 1);
|
||||||
|
|
||||||
clk_disable_unprepare(pltfm_host->clk);
|
clk_disable_unprepare(pltfm_host->clk);
|
||||||
clk_put(pltfm_host->clk);
|
clk_put(pltfm_host->clk);
|
||||||
sdhci_pltfm_free(pdev);
|
sdhci_pltfm_free(pdev);
|
||||||
kfree(pxa);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -58,6 +58,12 @@
|
|||||||
#define SDCE_MISC_INT (1<<2)
|
#define SDCE_MISC_INT (1<<2)
|
||||||
#define SDCE_MISC_INT_EN (1<<1)
|
#define SDCE_MISC_INT_EN (1<<1)
|
||||||
|
|
||||||
|
struct sdhci_pxa {
|
||||||
|
struct clk *clk_core;
|
||||||
|
struct clk *clk_io;
|
||||||
|
u8 power_mode;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These registers are relative to the second register region, for the
|
* These registers are relative to the second register region, for the
|
||||||
* MBus bridge.
|
* MBus bridge.
|
||||||
@ -211,6 +217,7 @@ static void pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
|
|||||||
case MMC_TIMING_UHS_SDR104:
|
case MMC_TIMING_UHS_SDR104:
|
||||||
ctrl_2 |= SDHCI_CTRL_UHS_SDR104 | SDHCI_CTRL_VDD_180;
|
ctrl_2 |= SDHCI_CTRL_UHS_SDR104 | SDHCI_CTRL_VDD_180;
|
||||||
break;
|
break;
|
||||||
|
case MMC_TIMING_MMC_DDR52:
|
||||||
case MMC_TIMING_UHS_DDR50:
|
case MMC_TIMING_UHS_DDR50:
|
||||||
ctrl_2 |= SDHCI_CTRL_UHS_DDR50 | SDHCI_CTRL_VDD_180;
|
ctrl_2 |= SDHCI_CTRL_UHS_DDR50 | SDHCI_CTRL_VDD_180;
|
||||||
break;
|
break;
|
||||||
@ -283,9 +290,7 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
|
|||||||
struct sdhci_host *host = NULL;
|
struct sdhci_host *host = NULL;
|
||||||
struct sdhci_pxa *pxa = NULL;
|
struct sdhci_pxa *pxa = NULL;
|
||||||
const struct of_device_id *match;
|
const struct of_device_id *match;
|
||||||
|
|
||||||
int ret;
|
int ret;
|
||||||
struct clk *clk;
|
|
||||||
|
|
||||||
pxa = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_pxa), GFP_KERNEL);
|
pxa = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_pxa), GFP_KERNEL);
|
||||||
if (!pxa)
|
if (!pxa)
|
||||||
@ -305,14 +310,20 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
|
|||||||
pltfm_host = sdhci_priv(host);
|
pltfm_host = sdhci_priv(host);
|
||||||
pltfm_host->priv = pxa;
|
pltfm_host->priv = pxa;
|
||||||
|
|
||||||
clk = devm_clk_get(dev, NULL);
|
pxa->clk_io = devm_clk_get(dev, "io");
|
||||||
if (IS_ERR(clk)) {
|
if (IS_ERR(pxa->clk_io))
|
||||||
|
pxa->clk_io = devm_clk_get(dev, NULL);
|
||||||
|
if (IS_ERR(pxa->clk_io)) {
|
||||||
dev_err(dev, "failed to get io clock\n");
|
dev_err(dev, "failed to get io clock\n");
|
||||||
ret = PTR_ERR(clk);
|
ret = PTR_ERR(pxa->clk_io);
|
||||||
goto err_clk_get;
|
goto err_clk_get;
|
||||||
}
|
}
|
||||||
pltfm_host->clk = clk;
|
pltfm_host->clk = pxa->clk_io;
|
||||||
clk_prepare_enable(clk);
|
clk_prepare_enable(pxa->clk_io);
|
||||||
|
|
||||||
|
pxa->clk_core = devm_clk_get(dev, "core");
|
||||||
|
if (!IS_ERR(pxa->clk_core))
|
||||||
|
clk_prepare_enable(pxa->clk_core);
|
||||||
|
|
||||||
/* enable 1/8V DDR capable */
|
/* enable 1/8V DDR capable */
|
||||||
host->mmc->caps |= MMC_CAP_1_8V_DDR;
|
host->mmc->caps |= MMC_CAP_1_8V_DDR;
|
||||||
@ -385,7 +396,9 @@ err_add_host:
|
|||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
err_of_parse:
|
err_of_parse:
|
||||||
err_cd_req:
|
err_cd_req:
|
||||||
clk_disable_unprepare(clk);
|
clk_disable_unprepare(pxa->clk_io);
|
||||||
|
if (!IS_ERR(pxa->clk_core))
|
||||||
|
clk_disable_unprepare(pxa->clk_core);
|
||||||
err_clk_get:
|
err_clk_get:
|
||||||
err_mbus_win:
|
err_mbus_win:
|
||||||
sdhci_pltfm_free(pdev);
|
sdhci_pltfm_free(pdev);
|
||||||
@ -396,12 +409,15 @@ static int sdhci_pxav3_remove(struct platform_device *pdev)
|
|||||||
{
|
{
|
||||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||||
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;
|
||||||
|
|
||||||
pm_runtime_get_sync(&pdev->dev);
|
pm_runtime_get_sync(&pdev->dev);
|
||||||
sdhci_remove_host(host, 1);
|
sdhci_remove_host(host, 1);
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
|
||||||
clk_disable_unprepare(pltfm_host->clk);
|
clk_disable_unprepare(pxa->clk_io);
|
||||||
|
if (!IS_ERR(pxa->clk_core))
|
||||||
|
clk_disable_unprepare(pxa->clk_core);
|
||||||
|
|
||||||
sdhci_pltfm_free(pdev);
|
sdhci_pltfm_free(pdev);
|
||||||
|
|
||||||
@ -441,15 +457,16 @@ 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;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
if (pltfm_host->clk) {
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
spin_lock_irqsave(&host->lock, flags);
|
host->runtime_suspended = true;
|
||||||
host->runtime_suspended = true;
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
|
||||||
|
|
||||||
clk_disable_unprepare(pltfm_host->clk);
|
clk_disable_unprepare(pxa->clk_io);
|
||||||
}
|
if (!IS_ERR(pxa->clk_core))
|
||||||
|
clk_disable_unprepare(pxa->clk_core);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -458,15 +475,16 @@ 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;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
if (pltfm_host->clk) {
|
clk_prepare_enable(pxa->clk_io);
|
||||||
clk_prepare_enable(pltfm_host->clk);
|
if (!IS_ERR(pxa->clk_core))
|
||||||
|
clk_prepare_enable(pxa->clk_core);
|
||||||
|
|
||||||
spin_lock_irqsave(&host->lock, flags);
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
host->runtime_suspended = false;
|
host->runtime_suspended = false;
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -300,6 +300,7 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||||||
struct device *dev = &ourhost->pdev->dev;
|
struct device *dev = &ourhost->pdev->dev;
|
||||||
unsigned long timeout;
|
unsigned long timeout;
|
||||||
u16 clk = 0;
|
u16 clk = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
host->mmc->actual_clock = 0;
|
host->mmc->actual_clock = 0;
|
||||||
|
|
||||||
@ -311,7 +312,12 @@ 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);
|
||||||
|
|
||||||
clk_set_rate(ourhost->clk_bus[ourhost->cur_clk], clock);
|
ret = clk_set_rate(ourhost->clk_bus[ourhost->cur_clk], clock);
|
||||||
|
if (ret != 0) {
|
||||||
|
dev_err(dev, "%s: failed to set clock rate %uHz\n",
|
||||||
|
mmc_hostname(host->mmc), clock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
clk = SDHCI_CLOCK_INT_EN;
|
clk = SDHCI_CLOCK_INT_EN;
|
||||||
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
||||||
|
@ -44,8 +44,6 @@
|
|||||||
|
|
||||||
#define MAX_TUNING_LOOP 40
|
#define MAX_TUNING_LOOP 40
|
||||||
|
|
||||||
#define ADMA_SIZE ((128 * 2 + 1) * 4)
|
|
||||||
|
|
||||||
static unsigned int debug_quirks = 0;
|
static unsigned int debug_quirks = 0;
|
||||||
static unsigned int debug_quirks2;
|
static unsigned int debug_quirks2;
|
||||||
|
|
||||||
@ -119,10 +117,17 @@ static void sdhci_dumpregs(struct sdhci_host *host)
|
|||||||
pr_debug(DRIVER_NAME ": Host ctl2: 0x%08x\n",
|
pr_debug(DRIVER_NAME ": Host ctl2: 0x%08x\n",
|
||||||
sdhci_readw(host, SDHCI_HOST_CONTROL2));
|
sdhci_readw(host, SDHCI_HOST_CONTROL2));
|
||||||
|
|
||||||
if (host->flags & SDHCI_USE_ADMA)
|
if (host->flags & SDHCI_USE_ADMA) {
|
||||||
pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n",
|
if (host->flags & SDHCI_USE_64_BIT_DMA)
|
||||||
readl(host->ioaddr + SDHCI_ADMA_ERROR),
|
pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x%08x\n",
|
||||||
readl(host->ioaddr + SDHCI_ADMA_ADDRESS));
|
readl(host->ioaddr + SDHCI_ADMA_ERROR),
|
||||||
|
readl(host->ioaddr + SDHCI_ADMA_ADDRESS_HI),
|
||||||
|
readl(host->ioaddr + SDHCI_ADMA_ADDRESS));
|
||||||
|
else
|
||||||
|
pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n",
|
||||||
|
readl(host->ioaddr + SDHCI_ADMA_ERROR),
|
||||||
|
readl(host->ioaddr + SDHCI_ADMA_ADDRESS));
|
||||||
|
}
|
||||||
|
|
||||||
pr_debug(DRIVER_NAME ": ===========================================\n");
|
pr_debug(DRIVER_NAME ": ===========================================\n");
|
||||||
}
|
}
|
||||||
@ -448,18 +453,26 @@ static void sdhci_kunmap_atomic(void *buffer, unsigned long *flags)
|
|||||||
local_irq_restore(*flags);
|
local_irq_restore(*flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sdhci_set_adma_desc(u8 *desc, u32 addr, int len, unsigned cmd)
|
static void sdhci_adma_write_desc(struct sdhci_host *host, void *desc,
|
||||||
|
dma_addr_t addr, int len, unsigned cmd)
|
||||||
{
|
{
|
||||||
__le32 *dataddr = (__le32 __force *)(desc + 4);
|
struct sdhci_adma2_64_desc *dma_desc = desc;
|
||||||
__le16 *cmdlen = (__le16 __force *)desc;
|
|
||||||
|
|
||||||
/* SDHCI specification says ADMA descriptors should be 4 byte
|
/* 32-bit and 64-bit descriptors have these members in same position */
|
||||||
* aligned, so using 16 or 32bit operations should be safe. */
|
dma_desc->cmd = cpu_to_le16(cmd);
|
||||||
|
dma_desc->len = cpu_to_le16(len);
|
||||||
|
dma_desc->addr_lo = cpu_to_le32((u32)addr);
|
||||||
|
|
||||||
cmdlen[0] = cpu_to_le16(cmd);
|
if (host->flags & SDHCI_USE_64_BIT_DMA)
|
||||||
cmdlen[1] = cpu_to_le16(len);
|
dma_desc->addr_hi = cpu_to_le32((u64)addr >> 32);
|
||||||
|
}
|
||||||
|
|
||||||
dataddr[0] = cpu_to_le32(addr);
|
static void sdhci_adma_mark_end(void *desc)
|
||||||
|
{
|
||||||
|
struct sdhci_adma2_64_desc *dma_desc = desc;
|
||||||
|
|
||||||
|
/* 32-bit and 64-bit descriptors have 'cmd' in same position */
|
||||||
|
dma_desc->cmd |= cpu_to_le16(ADMA2_END);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sdhci_adma_table_pre(struct sdhci_host *host,
|
static int sdhci_adma_table_pre(struct sdhci_host *host,
|
||||||
@ -467,8 +480,8 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
|
|||||||
{
|
{
|
||||||
int direction;
|
int direction;
|
||||||
|
|
||||||
u8 *desc;
|
void *desc;
|
||||||
u8 *align;
|
void *align;
|
||||||
dma_addr_t addr;
|
dma_addr_t addr;
|
||||||
dma_addr_t align_addr;
|
dma_addr_t align_addr;
|
||||||
int len, offset;
|
int len, offset;
|
||||||
@ -489,17 +502,17 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
|
|||||||
direction = DMA_TO_DEVICE;
|
direction = DMA_TO_DEVICE;
|
||||||
|
|
||||||
host->align_addr = dma_map_single(mmc_dev(host->mmc),
|
host->align_addr = dma_map_single(mmc_dev(host->mmc),
|
||||||
host->align_buffer, 128 * 4, direction);
|
host->align_buffer, host->align_buffer_sz, direction);
|
||||||
if (dma_mapping_error(mmc_dev(host->mmc), host->align_addr))
|
if (dma_mapping_error(mmc_dev(host->mmc), host->align_addr))
|
||||||
goto fail;
|
goto fail;
|
||||||
BUG_ON(host->align_addr & 0x3);
|
BUG_ON(host->align_addr & host->align_mask);
|
||||||
|
|
||||||
host->sg_count = dma_map_sg(mmc_dev(host->mmc),
|
host->sg_count = dma_map_sg(mmc_dev(host->mmc),
|
||||||
data->sg, data->sg_len, direction);
|
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_desc;
|
desc = host->adma_table;
|
||||||
align = host->align_buffer;
|
align = host->align_buffer;
|
||||||
|
|
||||||
align_addr = host->align_addr;
|
align_addr = host->align_addr;
|
||||||
@ -515,24 +528,27 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
|
|||||||
* the (up to three) bytes that screw up the
|
* the (up to three) bytes that screw up the
|
||||||
* alignment.
|
* alignment.
|
||||||
*/
|
*/
|
||||||
offset = (4 - (addr & 0x3)) & 0x3;
|
offset = (host->align_sz - (addr & host->align_mask)) &
|
||||||
|
host->align_mask;
|
||||||
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_MASK) > (PAGE_SIZE - 3));
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* tran, valid */
|
/* tran, valid */
|
||||||
sdhci_set_adma_desc(desc, align_addr, offset, 0x21);
|
sdhci_adma_write_desc(host, desc, align_addr, offset,
|
||||||
|
ADMA2_TRAN_VALID);
|
||||||
|
|
||||||
BUG_ON(offset > 65536);
|
BUG_ON(offset > 65536);
|
||||||
|
|
||||||
align += 4;
|
align += host->align_sz;
|
||||||
align_addr += 4;
|
align_addr += host->align_sz;
|
||||||
|
|
||||||
desc += 8;
|
desc += host->desc_sz;
|
||||||
|
|
||||||
addr += offset;
|
addr += offset;
|
||||||
len -= offset;
|
len -= offset;
|
||||||
@ -541,23 +557,23 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
|
|||||||
BUG_ON(len > 65536);
|
BUG_ON(len > 65536);
|
||||||
|
|
||||||
/* tran, valid */
|
/* tran, valid */
|
||||||
sdhci_set_adma_desc(desc, addr, len, 0x21);
|
sdhci_adma_write_desc(host, desc, addr, len, ADMA2_TRAN_VALID);
|
||||||
desc += 8;
|
desc += host->desc_sz;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If this triggers then we have a calculation bug
|
* If this triggers then we have a calculation bug
|
||||||
* somewhere. :/
|
* somewhere. :/
|
||||||
*/
|
*/
|
||||||
WARN_ON((desc - host->adma_desc) > ADMA_SIZE);
|
WARN_ON((desc - host->adma_table) >= host->adma_table_sz);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (host->quirks & SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC) {
|
if (host->quirks & SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC) {
|
||||||
/*
|
/*
|
||||||
* Mark the last descriptor as the terminating descriptor
|
* Mark the last descriptor as the terminating descriptor
|
||||||
*/
|
*/
|
||||||
if (desc != host->adma_desc) {
|
if (desc != host->adma_table) {
|
||||||
desc -= 8;
|
desc -= host->desc_sz;
|
||||||
desc[0] |= 0x2; /* end */
|
sdhci_adma_mark_end(desc);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
@ -565,7 +581,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* nop, end, valid */
|
/* nop, end, valid */
|
||||||
sdhci_set_adma_desc(desc, 0, 0, 0x3);
|
sdhci_adma_write_desc(host, desc, 0, 0, ADMA2_NOP_END_VALID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -573,14 +589,14 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
|
|||||||
*/
|
*/
|
||||||
if (data->flags & MMC_DATA_WRITE) {
|
if (data->flags & MMC_DATA_WRITE) {
|
||||||
dma_sync_single_for_device(mmc_dev(host->mmc),
|
dma_sync_single_for_device(mmc_dev(host->mmc),
|
||||||
host->align_addr, 128 * 4, direction);
|
host->align_addr, host->align_buffer_sz, direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
unmap_align:
|
unmap_align:
|
||||||
dma_unmap_single(mmc_dev(host->mmc), host->align_addr,
|
dma_unmap_single(mmc_dev(host->mmc), host->align_addr,
|
||||||
128 * 4, direction);
|
host->align_buffer_sz, direction);
|
||||||
fail:
|
fail:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
@ -592,7 +608,7 @@ static void sdhci_adma_table_post(struct sdhci_host *host,
|
|||||||
|
|
||||||
struct scatterlist *sg;
|
struct scatterlist *sg;
|
||||||
int i, size;
|
int i, size;
|
||||||
u8 *align;
|
void *align;
|
||||||
char *buffer;
|
char *buffer;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
bool has_unaligned;
|
bool has_unaligned;
|
||||||
@ -603,12 +619,12 @@ static void sdhci_adma_table_post(struct sdhci_host *host,
|
|||||||
direction = DMA_TO_DEVICE;
|
direction = DMA_TO_DEVICE;
|
||||||
|
|
||||||
dma_unmap_single(mmc_dev(host->mmc), host->align_addr,
|
dma_unmap_single(mmc_dev(host->mmc), host->align_addr,
|
||||||
128 * 4, direction);
|
host->align_buffer_sz, direction);
|
||||||
|
|
||||||
/* Do a quick scan of the SG list for any unaligned mappings */
|
/* Do a quick scan of the SG list for any unaligned mappings */
|
||||||
has_unaligned = false;
|
has_unaligned = false;
|
||||||
for_each_sg(data->sg, sg, host->sg_count, i)
|
for_each_sg(data->sg, sg, host->sg_count, i)
|
||||||
if (sg_dma_address(sg) & 3) {
|
if (sg_dma_address(sg) & host->align_mask) {
|
||||||
has_unaligned = true;
|
has_unaligned = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -620,15 +636,17 @@ static void sdhci_adma_table_post(struct sdhci_host *host,
|
|||||||
align = host->align_buffer;
|
align = host->align_buffer;
|
||||||
|
|
||||||
for_each_sg(data->sg, sg, host->sg_count, i) {
|
for_each_sg(data->sg, sg, host->sg_count, i) {
|
||||||
if (sg_dma_address(sg) & 0x3) {
|
if (sg_dma_address(sg) & host->align_mask) {
|
||||||
size = 4 - (sg_dma_address(sg) & 0x3);
|
size = host->align_sz -
|
||||||
|
(sg_dma_address(sg) & host->align_mask);
|
||||||
|
|
||||||
buffer = sdhci_kmap_atomic(sg, &flags);
|
buffer = sdhci_kmap_atomic(sg, &flags);
|
||||||
WARN_ON(((long)buffer & PAGE_MASK) > (PAGE_SIZE - 3));
|
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);
|
||||||
|
|
||||||
align += 4;
|
align += host->align_sz;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -822,6 +840,10 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
|
|||||||
} else {
|
} else {
|
||||||
sdhci_writel(host, host->adma_addr,
|
sdhci_writel(host, host->adma_addr,
|
||||||
SDHCI_ADMA_ADDRESS);
|
SDHCI_ADMA_ADDRESS);
|
||||||
|
if (host->flags & SDHCI_USE_64_BIT_DMA)
|
||||||
|
sdhci_writel(host,
|
||||||
|
(u64)host->adma_addr >> 32,
|
||||||
|
SDHCI_ADMA_ADDRESS_HI);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
int sg_cnt;
|
int sg_cnt;
|
||||||
@ -855,10 +877,14 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
|
|||||||
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
|
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
|
||||||
ctrl &= ~SDHCI_CTRL_DMA_MASK;
|
ctrl &= ~SDHCI_CTRL_DMA_MASK;
|
||||||
if ((host->flags & SDHCI_REQ_USE_DMA) &&
|
if ((host->flags & SDHCI_REQ_USE_DMA) &&
|
||||||
(host->flags & SDHCI_USE_ADMA))
|
(host->flags & SDHCI_USE_ADMA)) {
|
||||||
ctrl |= SDHCI_CTRL_ADMA32;
|
if (host->flags & SDHCI_USE_64_BIT_DMA)
|
||||||
else
|
ctrl |= SDHCI_CTRL_ADMA64;
|
||||||
|
else
|
||||||
|
ctrl |= SDHCI_CTRL_ADMA32;
|
||||||
|
} else {
|
||||||
ctrl |= SDHCI_CTRL_SDMA;
|
ctrl |= SDHCI_CTRL_SDMA;
|
||||||
|
}
|
||||||
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
|
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -889,10 +915,15 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
|
|||||||
struct mmc_data *data = cmd->data;
|
struct mmc_data *data = cmd->data;
|
||||||
|
|
||||||
if (data == NULL) {
|
if (data == NULL) {
|
||||||
|
if (host->quirks2 &
|
||||||
|
SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD) {
|
||||||
|
sdhci_writew(host, 0x0, SDHCI_TRANSFER_MODE);
|
||||||
|
} else {
|
||||||
/* clear Auto CMD settings for no data CMDs */
|
/* clear Auto CMD settings for no data CMDs */
|
||||||
mode = sdhci_readw(host, SDHCI_TRANSFER_MODE);
|
mode = sdhci_readw(host, SDHCI_TRANSFER_MODE);
|
||||||
sdhci_writew(host, mode & ~(SDHCI_TRNS_AUTO_CMD12 |
|
sdhci_writew(host, mode & ~(SDHCI_TRNS_AUTO_CMD12 |
|
||||||
SDHCI_TRNS_AUTO_CMD23), SDHCI_TRANSFER_MODE);
|
SDHCI_TRNS_AUTO_CMD23), SDHCI_TRANSFER_MODE);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1117,6 +1148,9 @@ static u16 sdhci_get_preset_value(struct sdhci_host *host)
|
|||||||
case MMC_TIMING_UHS_DDR50:
|
case MMC_TIMING_UHS_DDR50:
|
||||||
preset = sdhci_readw(host, SDHCI_PRESET_FOR_DDR50);
|
preset = sdhci_readw(host, SDHCI_PRESET_FOR_DDR50);
|
||||||
break;
|
break;
|
||||||
|
case MMC_TIMING_MMC_HS400:
|
||||||
|
preset = sdhci_readw(host, SDHCI_PRESET_FOR_HS400);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
pr_warn("%s: Invalid UHS-I mode selected\n",
|
pr_warn("%s: Invalid UHS-I mode selected\n",
|
||||||
mmc_hostname(host->mmc));
|
mmc_hostname(host->mmc));
|
||||||
@ -1444,6 +1478,8 @@ void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
|
|||||||
else if ((timing == MMC_TIMING_UHS_DDR50) ||
|
else if ((timing == MMC_TIMING_UHS_DDR50) ||
|
||||||
(timing == MMC_TIMING_MMC_DDR52))
|
(timing == MMC_TIMING_MMC_DDR52))
|
||||||
ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
|
ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
|
||||||
|
else if (timing == MMC_TIMING_MMC_HS400)
|
||||||
|
ctrl_2 |= SDHCI_CTRL_HS400; /* Non-standard */
|
||||||
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
|
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(sdhci_set_uhs_signaling);
|
EXPORT_SYMBOL_GPL(sdhci_set_uhs_signaling);
|
||||||
@ -1515,7 +1551,8 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
|
|||||||
u16 clk, ctrl_2;
|
u16 clk, ctrl_2;
|
||||||
|
|
||||||
/* In case of UHS-I modes, set High Speed Enable */
|
/* In case of UHS-I modes, set High Speed Enable */
|
||||||
if ((ios->timing == MMC_TIMING_MMC_HS200) ||
|
if ((ios->timing == MMC_TIMING_MMC_HS400) ||
|
||||||
|
(ios->timing == MMC_TIMING_MMC_HS200) ||
|
||||||
(ios->timing == MMC_TIMING_MMC_DDR52) ||
|
(ios->timing == MMC_TIMING_MMC_DDR52) ||
|
||||||
(ios->timing == MMC_TIMING_UHS_SDR50) ||
|
(ios->timing == MMC_TIMING_UHS_SDR50) ||
|
||||||
(ios->timing == MMC_TIMING_UHS_SDR104) ||
|
(ios->timing == MMC_TIMING_UHS_SDR104) ||
|
||||||
@ -1862,6 +1899,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|||||||
* tuning function has to be executed.
|
* tuning function has to be executed.
|
||||||
*/
|
*/
|
||||||
switch (host->timing) {
|
switch (host->timing) {
|
||||||
|
case MMC_TIMING_MMC_HS400:
|
||||||
case MMC_TIMING_MMC_HS200:
|
case MMC_TIMING_MMC_HS200:
|
||||||
case MMC_TIMING_UHS_SDR104:
|
case MMC_TIMING_UHS_SDR104:
|
||||||
break;
|
break;
|
||||||
@ -2144,9 +2182,10 @@ static void sdhci_tasklet_finish(unsigned long param)
|
|||||||
*/
|
*/
|
||||||
if (!(host->flags & SDHCI_DEVICE_DEAD) &&
|
if (!(host->flags & SDHCI_DEVICE_DEAD) &&
|
||||||
((mrq->cmd && mrq->cmd->error) ||
|
((mrq->cmd && mrq->cmd->error) ||
|
||||||
(mrq->data && (mrq->data->error ||
|
(mrq->sbc && mrq->sbc->error) ||
|
||||||
(mrq->data->stop && mrq->data->stop->error))) ||
|
(mrq->data && ((mrq->data->error && !mrq->data->stop) ||
|
||||||
(host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST))) {
|
(mrq->data->stop && mrq->data->stop->error))) ||
|
||||||
|
(host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST))) {
|
||||||
|
|
||||||
/* Some controllers need this kick or reset won't work here */
|
/* Some controllers need this kick or reset won't work here */
|
||||||
if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET)
|
if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET)
|
||||||
@ -2282,32 +2321,36 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *mask)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_MMC_DEBUG
|
#ifdef CONFIG_MMC_DEBUG
|
||||||
static void sdhci_show_adma_error(struct sdhci_host *host)
|
static void sdhci_adma_show_error(struct sdhci_host *host)
|
||||||
{
|
{
|
||||||
const char *name = mmc_hostname(host->mmc);
|
const char *name = mmc_hostname(host->mmc);
|
||||||
u8 *desc = host->adma_desc;
|
void *desc = host->adma_table;
|
||||||
__le32 *dma;
|
|
||||||
__le16 *len;
|
|
||||||
u8 attr;
|
|
||||||
|
|
||||||
sdhci_dumpregs(host);
|
sdhci_dumpregs(host);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
dma = (__le32 *)(desc + 4);
|
struct sdhci_adma2_64_desc *dma_desc = desc;
|
||||||
len = (__le16 *)(desc + 2);
|
|
||||||
attr = *desc;
|
|
||||||
|
|
||||||
DBG("%s: %p: DMA 0x%08x, LEN 0x%04x, Attr=0x%02x\n",
|
if (host->flags & SDHCI_USE_64_BIT_DMA)
|
||||||
name, desc, le32_to_cpu(*dma), le16_to_cpu(*len), attr);
|
DBG("%s: %p: DMA 0x%08x%08x, LEN 0x%04x, Attr=0x%02x\n",
|
||||||
|
name, desc, le32_to_cpu(dma_desc->addr_hi),
|
||||||
|
le32_to_cpu(dma_desc->addr_lo),
|
||||||
|
le16_to_cpu(dma_desc->len),
|
||||||
|
le16_to_cpu(dma_desc->cmd));
|
||||||
|
else
|
||||||
|
DBG("%s: %p: DMA 0x%08x, LEN 0x%04x, Attr=0x%02x\n",
|
||||||
|
name, desc, le32_to_cpu(dma_desc->addr_lo),
|
||||||
|
le16_to_cpu(dma_desc->len),
|
||||||
|
le16_to_cpu(dma_desc->cmd));
|
||||||
|
|
||||||
desc += 8;
|
desc += host->desc_sz;
|
||||||
|
|
||||||
if (attr & 2)
|
if (dma_desc->cmd & cpu_to_le16(ADMA2_END))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static void sdhci_show_adma_error(struct sdhci_host *host) { }
|
static void sdhci_adma_show_error(struct sdhci_host *host) { }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
|
static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
|
||||||
@ -2370,7 +2413,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
|
|||||||
host->data->error = -EILSEQ;
|
host->data->error = -EILSEQ;
|
||||||
else if (intmask & SDHCI_INT_ADMA_ERROR) {
|
else if (intmask & SDHCI_INT_ADMA_ERROR) {
|
||||||
pr_err("%s: ADMA error\n", mmc_hostname(host->mmc));
|
pr_err("%s: ADMA error\n", mmc_hostname(host->mmc));
|
||||||
sdhci_show_adma_error(host);
|
sdhci_adma_show_error(host);
|
||||||
host->data->error = -EIO;
|
host->data->error = -EIO;
|
||||||
if (host->ops->adma_workaround)
|
if (host->ops->adma_workaround)
|
||||||
host->ops->adma_workaround(host, intmask);
|
host->ops->adma_workaround(host, intmask);
|
||||||
@ -2849,6 +2892,16 @@ int sdhci_add_host(struct sdhci_host *host)
|
|||||||
host->flags &= ~SDHCI_USE_ADMA;
|
host->flags &= ~SDHCI_USE_ADMA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* It is assumed that a 64-bit capable device has set a 64-bit DMA mask
|
||||||
|
* and *must* do 64-bit DMA. A driver has the opportunity to change
|
||||||
|
* that during the first call to ->enable_dma(). Similarly
|
||||||
|
* SDHCI_QUIRK2_BROKEN_64_BIT_DMA must be left to the drivers to
|
||||||
|
* implement.
|
||||||
|
*/
|
||||||
|
if (sdhci_readl(host, SDHCI_CAPABILITIES) & SDHCI_CAN_64BIT)
|
||||||
|
host->flags |= SDHCI_USE_64_BIT_DMA;
|
||||||
|
|
||||||
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
|
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
|
||||||
if (host->ops->enable_dma) {
|
if (host->ops->enable_dma) {
|
||||||
if (host->ops->enable_dma(host)) {
|
if (host->ops->enable_dma(host)) {
|
||||||
@ -2860,33 +2913,56 @@ int sdhci_add_host(struct sdhci_host *host)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* SDMA does not support 64-bit DMA */
|
||||||
|
if (host->flags & SDHCI_USE_64_BIT_DMA)
|
||||||
|
host->flags &= ~SDHCI_USE_SDMA;
|
||||||
|
|
||||||
if (host->flags & SDHCI_USE_ADMA) {
|
if (host->flags & SDHCI_USE_ADMA) {
|
||||||
/*
|
/*
|
||||||
* We need to allocate descriptors for all sg entries
|
* The DMA descriptor table size is calculated as the maximum
|
||||||
* (128) and potentially one alignment transfer for
|
* number of segments times 2, to allow for an alignment
|
||||||
* each of those entries.
|
* descriptor for each segment, plus 1 for a nop end descriptor,
|
||||||
|
* all multipled by the descriptor size.
|
||||||
*/
|
*/
|
||||||
host->adma_desc = dma_alloc_coherent(mmc_dev(mmc),
|
if (host->flags & SDHCI_USE_64_BIT_DMA) {
|
||||||
ADMA_SIZE, &host->adma_addr,
|
host->adma_table_sz = (SDHCI_MAX_SEGS * 2 + 1) *
|
||||||
GFP_KERNEL);
|
SDHCI_ADMA2_64_DESC_SZ;
|
||||||
host->align_buffer = kmalloc(128 * 4, GFP_KERNEL);
|
host->align_buffer_sz = SDHCI_MAX_SEGS *
|
||||||
if (!host->adma_desc || !host->align_buffer) {
|
SDHCI_ADMA2_64_ALIGN;
|
||||||
dma_free_coherent(mmc_dev(mmc), ADMA_SIZE,
|
host->desc_sz = SDHCI_ADMA2_64_DESC_SZ;
|
||||||
host->adma_desc, host->adma_addr);
|
host->align_sz = SDHCI_ADMA2_64_ALIGN;
|
||||||
|
host->align_mask = SDHCI_ADMA2_64_ALIGN - 1;
|
||||||
|
} else {
|
||||||
|
host->adma_table_sz = (SDHCI_MAX_SEGS * 2 + 1) *
|
||||||
|
SDHCI_ADMA2_32_DESC_SZ;
|
||||||
|
host->align_buffer_sz = SDHCI_MAX_SEGS *
|
||||||
|
SDHCI_ADMA2_32_ALIGN;
|
||||||
|
host->desc_sz = SDHCI_ADMA2_32_DESC_SZ;
|
||||||
|
host->align_sz = SDHCI_ADMA2_32_ALIGN;
|
||||||
|
host->align_mask = SDHCI_ADMA2_32_ALIGN - 1;
|
||||||
|
}
|
||||||
|
host->adma_table = dma_alloc_coherent(mmc_dev(mmc),
|
||||||
|
host->adma_table_sz,
|
||||||
|
&host->adma_addr,
|
||||||
|
GFP_KERNEL);
|
||||||
|
host->align_buffer = kmalloc(host->align_buffer_sz, GFP_KERNEL);
|
||||||
|
if (!host->adma_table || !host->align_buffer) {
|
||||||
|
dma_free_coherent(mmc_dev(mmc), host->adma_table_sz,
|
||||||
|
host->adma_table, host->adma_addr);
|
||||||
kfree(host->align_buffer);
|
kfree(host->align_buffer);
|
||||||
pr_warn("%s: Unable to allocate ADMA buffers - falling back to standard DMA\n",
|
pr_warn("%s: Unable to allocate ADMA buffers - falling back to standard DMA\n",
|
||||||
mmc_hostname(mmc));
|
mmc_hostname(mmc));
|
||||||
host->flags &= ~SDHCI_USE_ADMA;
|
host->flags &= ~SDHCI_USE_ADMA;
|
||||||
host->adma_desc = NULL;
|
host->adma_table = NULL;
|
||||||
host->align_buffer = NULL;
|
host->align_buffer = NULL;
|
||||||
} else if (host->adma_addr & 3) {
|
} else if (host->adma_addr & host->align_mask) {
|
||||||
pr_warn("%s: unable to allocate aligned ADMA descriptor\n",
|
pr_warn("%s: unable to allocate aligned ADMA descriptor\n",
|
||||||
mmc_hostname(mmc));
|
mmc_hostname(mmc));
|
||||||
host->flags &= ~SDHCI_USE_ADMA;
|
host->flags &= ~SDHCI_USE_ADMA;
|
||||||
dma_free_coherent(mmc_dev(mmc), ADMA_SIZE,
|
dma_free_coherent(mmc_dev(mmc), host->adma_table_sz,
|
||||||
host->adma_desc, host->adma_addr);
|
host->adma_table, host->adma_addr);
|
||||||
kfree(host->align_buffer);
|
kfree(host->align_buffer);
|
||||||
host->adma_desc = NULL;
|
host->adma_table = NULL;
|
||||||
host->align_buffer = NULL;
|
host->align_buffer = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3027,7 +3103,7 @@ int sdhci_add_host(struct sdhci_host *host)
|
|||||||
if (ret) {
|
if (ret) {
|
||||||
pr_warn("%s: Failed to enable vqmmc regulator: %d\n",
|
pr_warn("%s: Failed to enable vqmmc regulator: %d\n",
|
||||||
mmc_hostname(mmc), ret);
|
mmc_hostname(mmc), ret);
|
||||||
mmc->supply.vqmmc = NULL;
|
mmc->supply.vqmmc = ERR_PTR(-EINVAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3046,16 +3122,21 @@ int sdhci_add_host(struct sdhci_host *host)
|
|||||||
/* SD3.0: SDR104 is supported so (for eMMC) the caps2
|
/* SD3.0: SDR104 is supported so (for eMMC) the caps2
|
||||||
* field can be promoted to support HS200.
|
* field can be promoted to support HS200.
|
||||||
*/
|
*/
|
||||||
if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_HS200)) {
|
if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_HS200))
|
||||||
mmc->caps2 |= MMC_CAP2_HS200;
|
mmc->caps2 |= MMC_CAP2_HS200;
|
||||||
if (IS_ERR(mmc->supply.vqmmc) ||
|
|
||||||
!regulator_is_supported_voltage
|
|
||||||
(mmc->supply.vqmmc, 1100000, 1300000))
|
|
||||||
mmc->caps2 &= ~MMC_CAP2_HS200_1_2V_SDR;
|
|
||||||
}
|
|
||||||
} else if (caps[1] & SDHCI_SUPPORT_SDR50)
|
} else if (caps[1] & SDHCI_SUPPORT_SDR50)
|
||||||
mmc->caps |= MMC_CAP_UHS_SDR50;
|
mmc->caps |= MMC_CAP_UHS_SDR50;
|
||||||
|
|
||||||
|
if (host->quirks2 & SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 &&
|
||||||
|
(caps[1] & SDHCI_SUPPORT_HS400))
|
||||||
|
mmc->caps2 |= MMC_CAP2_HS400;
|
||||||
|
|
||||||
|
if ((mmc->caps2 & MMC_CAP2_HSX00_1_2V) &&
|
||||||
|
(IS_ERR(mmc->supply.vqmmc) ||
|
||||||
|
!regulator_is_supported_voltage(mmc->supply.vqmmc, 1100000,
|
||||||
|
1300000)))
|
||||||
|
mmc->caps2 &= ~MMC_CAP2_HSX00_1_2V;
|
||||||
|
|
||||||
if ((caps[1] & SDHCI_SUPPORT_DDR50) &&
|
if ((caps[1] & SDHCI_SUPPORT_DDR50) &&
|
||||||
!(host->quirks2 & SDHCI_QUIRK2_BROKEN_DDR50))
|
!(host->quirks2 & SDHCI_QUIRK2_BROKEN_DDR50))
|
||||||
mmc->caps |= MMC_CAP_UHS_DDR50;
|
mmc->caps |= MMC_CAP_UHS_DDR50;
|
||||||
@ -3175,11 +3256,11 @@ int sdhci_add_host(struct sdhci_host *host)
|
|||||||
* can do scatter/gather or not.
|
* can do scatter/gather or not.
|
||||||
*/
|
*/
|
||||||
if (host->flags & SDHCI_USE_ADMA)
|
if (host->flags & SDHCI_USE_ADMA)
|
||||||
mmc->max_segs = 128;
|
mmc->max_segs = SDHCI_MAX_SEGS;
|
||||||
else if (host->flags & SDHCI_USE_SDMA)
|
else if (host->flags & SDHCI_USE_SDMA)
|
||||||
mmc->max_segs = 1;
|
mmc->max_segs = 1;
|
||||||
else /* PIO */
|
else /* PIO */
|
||||||
mmc->max_segs = 128;
|
mmc->max_segs = SDHCI_MAX_SEGS;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Maximum number of sectors in one transfer. Limited by DMA boundary
|
* Maximum number of sectors in one transfer. Limited by DMA boundary
|
||||||
@ -3277,7 +3358,8 @@ int sdhci_add_host(struct sdhci_host *host)
|
|||||||
|
|
||||||
pr_info("%s: SDHCI controller on %s [%s] using %s\n",
|
pr_info("%s: SDHCI controller on %s [%s] using %s\n",
|
||||||
mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)),
|
mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)),
|
||||||
(host->flags & SDHCI_USE_ADMA) ? "ADMA" :
|
(host->flags & SDHCI_USE_ADMA) ?
|
||||||
|
(host->flags & SDHCI_USE_64_BIT_DMA) ? "ADMA 64-bit" : "ADMA" :
|
||||||
(host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO");
|
(host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO");
|
||||||
|
|
||||||
sdhci_enable_card_detection(host);
|
sdhci_enable_card_detection(host);
|
||||||
@ -3339,18 +3421,15 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
|
|||||||
|
|
||||||
tasklet_kill(&host->finish_tasklet);
|
tasklet_kill(&host->finish_tasklet);
|
||||||
|
|
||||||
if (!IS_ERR(mmc->supply.vmmc))
|
|
||||||
regulator_disable(mmc->supply.vmmc);
|
|
||||||
|
|
||||||
if (!IS_ERR(mmc->supply.vqmmc))
|
if (!IS_ERR(mmc->supply.vqmmc))
|
||||||
regulator_disable(mmc->supply.vqmmc);
|
regulator_disable(mmc->supply.vqmmc);
|
||||||
|
|
||||||
if (host->adma_desc)
|
if (host->adma_table)
|
||||||
dma_free_coherent(mmc_dev(mmc), ADMA_SIZE,
|
dma_free_coherent(mmc_dev(mmc), host->adma_table_sz,
|
||||||
host->adma_desc, host->adma_addr);
|
host->adma_table, host->adma_addr);
|
||||||
kfree(host->align_buffer);
|
kfree(host->align_buffer);
|
||||||
|
|
||||||
host->adma_desc = NULL;
|
host->adma_table = NULL;
|
||||||
host->align_buffer = NULL;
|
host->align_buffer = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,7 +161,7 @@
|
|||||||
#define SDHCI_CTRL_UHS_SDR50 0x0002
|
#define SDHCI_CTRL_UHS_SDR50 0x0002
|
||||||
#define SDHCI_CTRL_UHS_SDR104 0x0003
|
#define SDHCI_CTRL_UHS_SDR104 0x0003
|
||||||
#define SDHCI_CTRL_UHS_DDR50 0x0004
|
#define SDHCI_CTRL_UHS_DDR50 0x0004
|
||||||
#define SDHCI_CTRL_HS_SDR200 0x0005 /* reserved value in SDIO spec */
|
#define SDHCI_CTRL_HS400 0x0005 /* Non-standard */
|
||||||
#define SDHCI_CTRL_VDD_180 0x0008
|
#define SDHCI_CTRL_VDD_180 0x0008
|
||||||
#define SDHCI_CTRL_DRV_TYPE_MASK 0x0030
|
#define SDHCI_CTRL_DRV_TYPE_MASK 0x0030
|
||||||
#define SDHCI_CTRL_DRV_TYPE_B 0x0000
|
#define SDHCI_CTRL_DRV_TYPE_B 0x0000
|
||||||
@ -204,6 +204,7 @@
|
|||||||
#define SDHCI_RETUNING_MODE_SHIFT 14
|
#define SDHCI_RETUNING_MODE_SHIFT 14
|
||||||
#define SDHCI_CLOCK_MUL_MASK 0x00FF0000
|
#define SDHCI_CLOCK_MUL_MASK 0x00FF0000
|
||||||
#define SDHCI_CLOCK_MUL_SHIFT 16
|
#define SDHCI_CLOCK_MUL_SHIFT 16
|
||||||
|
#define SDHCI_SUPPORT_HS400 0x80000000 /* Non-standard */
|
||||||
|
|
||||||
#define SDHCI_CAPABILITIES_1 0x44
|
#define SDHCI_CAPABILITIES_1 0x44
|
||||||
|
|
||||||
@ -227,6 +228,7 @@
|
|||||||
/* 55-57 reserved */
|
/* 55-57 reserved */
|
||||||
|
|
||||||
#define SDHCI_ADMA_ADDRESS 0x58
|
#define SDHCI_ADMA_ADDRESS 0x58
|
||||||
|
#define SDHCI_ADMA_ADDRESS_HI 0x5C
|
||||||
|
|
||||||
/* 60-FB reserved */
|
/* 60-FB reserved */
|
||||||
|
|
||||||
@ -235,6 +237,7 @@
|
|||||||
#define SDHCI_PRESET_FOR_SDR50 0x6A
|
#define SDHCI_PRESET_FOR_SDR50 0x6A
|
||||||
#define SDHCI_PRESET_FOR_SDR104 0x6C
|
#define SDHCI_PRESET_FOR_SDR104 0x6C
|
||||||
#define SDHCI_PRESET_FOR_DDR50 0x6E
|
#define SDHCI_PRESET_FOR_DDR50 0x6E
|
||||||
|
#define SDHCI_PRESET_FOR_HS400 0x74 /* Non-standard */
|
||||||
#define SDHCI_PRESET_DRV_MASK 0xC000
|
#define SDHCI_PRESET_DRV_MASK 0xC000
|
||||||
#define SDHCI_PRESET_DRV_SHIFT 14
|
#define SDHCI_PRESET_DRV_SHIFT 14
|
||||||
#define SDHCI_PRESET_CLKGEN_SEL_MASK 0x400
|
#define SDHCI_PRESET_CLKGEN_SEL_MASK 0x400
|
||||||
@ -266,6 +269,46 @@
|
|||||||
#define SDHCI_DEFAULT_BOUNDARY_SIZE (512 * 1024)
|
#define SDHCI_DEFAULT_BOUNDARY_SIZE (512 * 1024)
|
||||||
#define SDHCI_DEFAULT_BOUNDARY_ARG (ilog2(SDHCI_DEFAULT_BOUNDARY_SIZE) - 12)
|
#define SDHCI_DEFAULT_BOUNDARY_ARG (ilog2(SDHCI_DEFAULT_BOUNDARY_SIZE) - 12)
|
||||||
|
|
||||||
|
/* ADMA2 32-bit DMA descriptor size */
|
||||||
|
#define SDHCI_ADMA2_32_DESC_SZ 8
|
||||||
|
|
||||||
|
/* ADMA2 32-bit DMA alignment */
|
||||||
|
#define SDHCI_ADMA2_32_ALIGN 4
|
||||||
|
|
||||||
|
/* ADMA2 32-bit descriptor */
|
||||||
|
struct sdhci_adma2_32_desc {
|
||||||
|
__le16 cmd;
|
||||||
|
__le16 len;
|
||||||
|
__le32 addr;
|
||||||
|
} __packed __aligned(SDHCI_ADMA2_32_ALIGN);
|
||||||
|
|
||||||
|
/* ADMA2 64-bit DMA descriptor size */
|
||||||
|
#define SDHCI_ADMA2_64_DESC_SZ 12
|
||||||
|
|
||||||
|
/* ADMA2 64-bit DMA alignment */
|
||||||
|
#define SDHCI_ADMA2_64_ALIGN 8
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ADMA2 64-bit descriptor. Note 12-byte descriptor can't always be 8-byte
|
||||||
|
* aligned.
|
||||||
|
*/
|
||||||
|
struct sdhci_adma2_64_desc {
|
||||||
|
__le16 cmd;
|
||||||
|
__le16 len;
|
||||||
|
__le32 addr_lo;
|
||||||
|
__le32 addr_hi;
|
||||||
|
} __packed __aligned(4);
|
||||||
|
|
||||||
|
#define ADMA2_TRAN_VALID 0x21
|
||||||
|
#define ADMA2_NOP_END_VALID 0x3
|
||||||
|
#define ADMA2_END 0x2
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Maximum segments assuming a 512KiB maximum requisition size and a minimum
|
||||||
|
* 4KiB page size.
|
||||||
|
*/
|
||||||
|
#define SDHCI_MAX_SEGS 128
|
||||||
|
|
||||||
struct sdhci_ops {
|
struct sdhci_ops {
|
||||||
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
|
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
|
||||||
u32 (*read_l)(struct sdhci_host *host, int reg);
|
u32 (*read_l)(struct sdhci_host *host, int reg);
|
||||||
|
@ -21,7 +21,6 @@
|
|||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/clk-private.h>
|
|
||||||
#include <linux/clk/sunxi.h>
|
#include <linux/clk/sunxi.h>
|
||||||
|
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
|
717
drivers/mmc/host/toshsd.c
Normal file
717
drivers/mmc/host/toshsd.c
Normal file
@ -0,0 +1,717 @@
|
|||||||
|
/*
|
||||||
|
* Toshiba PCI Secure Digital Host Controller Interface driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Ondrej Zary
|
||||||
|
* Copyright (C) 2007 Richard Betts, All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Based on asic3_mmc.c, copyright (c) 2005 SDG Systems, LLC and,
|
||||||
|
* sdhci.c, copyright (C) 2005-2006 Pierre Ossman
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/pci.h>
|
||||||
|
#include <linux/scatterlist.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/pm.h>
|
||||||
|
#include <linux/mmc/host.h>
|
||||||
|
#include <linux/mmc/mmc.h>
|
||||||
|
|
||||||
|
#include "toshsd.h"
|
||||||
|
|
||||||
|
#define DRIVER_NAME "toshsd"
|
||||||
|
|
||||||
|
static const struct pci_device_id pci_ids[] = {
|
||||||
|
{ PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA, 0x0805) },
|
||||||
|
{ /* end: all zeroes */ },
|
||||||
|
};
|
||||||
|
|
||||||
|
MODULE_DEVICE_TABLE(pci, pci_ids);
|
||||||
|
|
||||||
|
static void toshsd_init(struct toshsd_host *host)
|
||||||
|
{
|
||||||
|
/* enable clock */
|
||||||
|
pci_write_config_byte(host->pdev, SD_PCICFG_CLKSTOP,
|
||||||
|
SD_PCICFG_CLKSTOP_ENABLE_ALL);
|
||||||
|
pci_write_config_byte(host->pdev, SD_PCICFG_CARDDETECT, 2);
|
||||||
|
|
||||||
|
/* reset */
|
||||||
|
iowrite16(0, host->ioaddr + SD_SOFTWARERESET); /* assert */
|
||||||
|
mdelay(2);
|
||||||
|
iowrite16(1, host->ioaddr + SD_SOFTWARERESET); /* deassert */
|
||||||
|
mdelay(2);
|
||||||
|
|
||||||
|
/* Clear card registers */
|
||||||
|
iowrite16(0, host->ioaddr + SD_CARDCLOCKCTRL);
|
||||||
|
iowrite32(0, host->ioaddr + SD_CARDSTATUS);
|
||||||
|
iowrite32(0, host->ioaddr + SD_ERRORSTATUS0);
|
||||||
|
iowrite16(0, host->ioaddr + SD_STOPINTERNAL);
|
||||||
|
|
||||||
|
/* SDIO clock? */
|
||||||
|
iowrite16(0x100, host->ioaddr + SDIO_BASE + SDIO_CLOCKNWAITCTRL);
|
||||||
|
|
||||||
|
/* enable LED */
|
||||||
|
pci_write_config_byte(host->pdev, SD_PCICFG_SDLED_ENABLE1,
|
||||||
|
SD_PCICFG_LED_ENABLE1_START);
|
||||||
|
pci_write_config_byte(host->pdev, SD_PCICFG_SDLED_ENABLE2,
|
||||||
|
SD_PCICFG_LED_ENABLE2_START);
|
||||||
|
|
||||||
|
/* set interrupt masks */
|
||||||
|
iowrite32(~(u32)(SD_CARD_RESP_END | SD_CARD_RW_END
|
||||||
|
| SD_CARD_CARD_REMOVED_0 | SD_CARD_CARD_INSERTED_0
|
||||||
|
| SD_BUF_READ_ENABLE | SD_BUF_WRITE_ENABLE
|
||||||
|
| SD_BUF_CMD_TIMEOUT),
|
||||||
|
host->ioaddr + SD_INTMASKCARD);
|
||||||
|
|
||||||
|
iowrite16(0x1000, host->ioaddr + SD_TRANSACTIONCTRL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set MMC clock / power.
|
||||||
|
* Note: This controller uses a simple divider scheme therefore it cannot run
|
||||||
|
* SD/MMC cards at full speed (24/20MHz). HCLK (=33MHz PCI clock?) is too high
|
||||||
|
* and the next slowest is 16MHz (div=2).
|
||||||
|
*/
|
||||||
|
static void __toshsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||||
|
{
|
||||||
|
struct toshsd_host *host = mmc_priv(mmc);
|
||||||
|
|
||||||
|
if (ios->clock) {
|
||||||
|
u16 clk;
|
||||||
|
int div = 1;
|
||||||
|
|
||||||
|
while (ios->clock < HCLK / div)
|
||||||
|
div *= 2;
|
||||||
|
|
||||||
|
clk = div >> 2;
|
||||||
|
|
||||||
|
if (div == 1) { /* disable the divider */
|
||||||
|
pci_write_config_byte(host->pdev, SD_PCICFG_CLKMODE,
|
||||||
|
SD_PCICFG_CLKMODE_DIV_DISABLE);
|
||||||
|
clk |= SD_CARDCLK_DIV_DISABLE;
|
||||||
|
} else
|
||||||
|
pci_write_config_byte(host->pdev, SD_PCICFG_CLKMODE, 0);
|
||||||
|
|
||||||
|
clk |= SD_CARDCLK_ENABLE_CLOCK;
|
||||||
|
iowrite16(clk, host->ioaddr + SD_CARDCLOCKCTRL);
|
||||||
|
|
||||||
|
mdelay(10);
|
||||||
|
} else
|
||||||
|
iowrite16(0, host->ioaddr + SD_CARDCLOCKCTRL);
|
||||||
|
|
||||||
|
switch (ios->power_mode) {
|
||||||
|
case MMC_POWER_OFF:
|
||||||
|
pci_write_config_byte(host->pdev, SD_PCICFG_POWER1,
|
||||||
|
SD_PCICFG_PWR1_OFF);
|
||||||
|
mdelay(1);
|
||||||
|
break;
|
||||||
|
case MMC_POWER_UP:
|
||||||
|
break;
|
||||||
|
case MMC_POWER_ON:
|
||||||
|
pci_write_config_byte(host->pdev, SD_PCICFG_POWER1,
|
||||||
|
SD_PCICFG_PWR1_33V);
|
||||||
|
pci_write_config_byte(host->pdev, SD_PCICFG_POWER2,
|
||||||
|
SD_PCICFG_PWR2_AUTO);
|
||||||
|
mdelay(20);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (ios->bus_width) {
|
||||||
|
case MMC_BUS_WIDTH_1:
|
||||||
|
iowrite16(SD_CARDOPT_REQUIRED | SD_CARDOPT_DATA_RESP_TIMEOUT(14)
|
||||||
|
| SD_CARDOPT_C2_MODULE_ABSENT
|
||||||
|
| SD_CARDOPT_DATA_XFR_WIDTH_1,
|
||||||
|
host->ioaddr + SD_CARDOPTIONSETUP);
|
||||||
|
break;
|
||||||
|
case MMC_BUS_WIDTH_4:
|
||||||
|
iowrite16(SD_CARDOPT_REQUIRED | SD_CARDOPT_DATA_RESP_TIMEOUT(14)
|
||||||
|
| SD_CARDOPT_C2_MODULE_ABSENT
|
||||||
|
| SD_CARDOPT_DATA_XFR_WIDTH_4,
|
||||||
|
host->ioaddr + SD_CARDOPTIONSETUP);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void toshsd_set_led(struct toshsd_host *host, unsigned char state)
|
||||||
|
{
|
||||||
|
iowrite16(state, host->ioaddr + SDIO_BASE + SDIO_LEDCTRL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void toshsd_finish_request(struct toshsd_host *host)
|
||||||
|
{
|
||||||
|
struct mmc_request *mrq = host->mrq;
|
||||||
|
|
||||||
|
/* Write something to end the command */
|
||||||
|
host->mrq = NULL;
|
||||||
|
host->cmd = NULL;
|
||||||
|
host->data = NULL;
|
||||||
|
|
||||||
|
toshsd_set_led(host, 0);
|
||||||
|
mmc_request_done(host->mmc, mrq);
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t toshsd_thread_irq(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct toshsd_host *host = dev_id;
|
||||||
|
struct mmc_data *data = host->data;
|
||||||
|
struct sg_mapping_iter *sg_miter = &host->sg_miter;
|
||||||
|
unsigned short *buf;
|
||||||
|
int count;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
dev_warn(&host->pdev->dev, "Spurious Data IRQ\n");
|
||||||
|
if (host->cmd) {
|
||||||
|
host->cmd->error = -EIO;
|
||||||
|
toshsd_finish_request(host);
|
||||||
|
}
|
||||||
|
return IRQ_NONE;
|
||||||
|
}
|
||||||
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
|
|
||||||
|
if (!sg_miter_next(sg_miter))
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
buf = sg_miter->addr;
|
||||||
|
|
||||||
|
/* Ensure we dont read more than one block. The chip will interrupt us
|
||||||
|
* When the next block is available.
|
||||||
|
*/
|
||||||
|
count = sg_miter->length;
|
||||||
|
if (count > data->blksz)
|
||||||
|
count = data->blksz;
|
||||||
|
|
||||||
|
dev_dbg(&host->pdev->dev, "count: %08x, flags %08x\n", count,
|
||||||
|
data->flags);
|
||||||
|
|
||||||
|
/* Transfer the data */
|
||||||
|
if (data->flags & MMC_DATA_READ)
|
||||||
|
ioread32_rep(host->ioaddr + SD_DATAPORT, buf, count >> 2);
|
||||||
|
else
|
||||||
|
iowrite32_rep(host->ioaddr + SD_DATAPORT, buf, count >> 2);
|
||||||
|
|
||||||
|
sg_miter->consumed = count;
|
||||||
|
sg_miter_stop(sg_miter);
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void toshsd_cmd_irq(struct toshsd_host *host)
|
||||||
|
{
|
||||||
|
struct mmc_command *cmd = host->cmd;
|
||||||
|
u8 *buf;
|
||||||
|
u16 data;
|
||||||
|
|
||||||
|
if (!host->cmd) {
|
||||||
|
dev_warn(&host->pdev->dev, "Spurious CMD irq\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
buf = (u8 *)cmd->resp;
|
||||||
|
host->cmd = NULL;
|
||||||
|
|
||||||
|
if (cmd->flags & MMC_RSP_PRESENT && cmd->flags & MMC_RSP_136) {
|
||||||
|
/* R2 */
|
||||||
|
buf[12] = 0xff;
|
||||||
|
data = ioread16(host->ioaddr + SD_RESPONSE0);
|
||||||
|
buf[13] = data & 0xff;
|
||||||
|
buf[14] = data >> 8;
|
||||||
|
data = ioread16(host->ioaddr + SD_RESPONSE1);
|
||||||
|
buf[15] = data & 0xff;
|
||||||
|
buf[8] = data >> 8;
|
||||||
|
data = ioread16(host->ioaddr + SD_RESPONSE2);
|
||||||
|
buf[9] = data & 0xff;
|
||||||
|
buf[10] = data >> 8;
|
||||||
|
data = ioread16(host->ioaddr + SD_RESPONSE3);
|
||||||
|
buf[11] = data & 0xff;
|
||||||
|
buf[4] = data >> 8;
|
||||||
|
data = ioread16(host->ioaddr + SD_RESPONSE4);
|
||||||
|
buf[5] = data & 0xff;
|
||||||
|
buf[6] = data >> 8;
|
||||||
|
data = ioread16(host->ioaddr + SD_RESPONSE5);
|
||||||
|
buf[7] = data & 0xff;
|
||||||
|
buf[0] = data >> 8;
|
||||||
|
data = ioread16(host->ioaddr + SD_RESPONSE6);
|
||||||
|
buf[1] = data & 0xff;
|
||||||
|
buf[2] = data >> 8;
|
||||||
|
data = ioread16(host->ioaddr + SD_RESPONSE7);
|
||||||
|
buf[3] = data & 0xff;
|
||||||
|
} else if (cmd->flags & MMC_RSP_PRESENT) {
|
||||||
|
/* R1, R1B, R3, R6, R7 */
|
||||||
|
data = ioread16(host->ioaddr + SD_RESPONSE0);
|
||||||
|
buf[0] = data & 0xff;
|
||||||
|
buf[1] = data >> 8;
|
||||||
|
data = ioread16(host->ioaddr + SD_RESPONSE1);
|
||||||
|
buf[2] = data & 0xff;
|
||||||
|
buf[3] = data >> 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_dbg(&host->pdev->dev, "Command IRQ complete %d %d %x\n",
|
||||||
|
cmd->opcode, cmd->error, cmd->flags);
|
||||||
|
|
||||||
|
/* If there is data to handle we will
|
||||||
|
* finish the request in the mmc_data_end_irq handler.*/
|
||||||
|
if (host->data)
|
||||||
|
return;
|
||||||
|
|
||||||
|
toshsd_finish_request(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void toshsd_data_end_irq(struct toshsd_host *host)
|
||||||
|
{
|
||||||
|
struct mmc_data *data = host->data;
|
||||||
|
|
||||||
|
host->data = NULL;
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
dev_warn(&host->pdev->dev, "Spurious data end IRQ\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data->error == 0)
|
||||||
|
data->bytes_xfered = data->blocks * data->blksz;
|
||||||
|
else
|
||||||
|
data->bytes_xfered = 0;
|
||||||
|
|
||||||
|
dev_dbg(&host->pdev->dev, "Completed data request xfr=%d\n",
|
||||||
|
data->bytes_xfered);
|
||||||
|
|
||||||
|
iowrite16(0, host->ioaddr + SD_STOPINTERNAL);
|
||||||
|
|
||||||
|
toshsd_finish_request(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t toshsd_irq(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct toshsd_host *host = dev_id;
|
||||||
|
u32 int_reg, int_mask, int_status, detail;
|
||||||
|
int error = 0, ret = IRQ_HANDLED;
|
||||||
|
|
||||||
|
spin_lock(&host->lock);
|
||||||
|
int_status = ioread32(host->ioaddr + SD_CARDSTATUS);
|
||||||
|
int_mask = ioread32(host->ioaddr + SD_INTMASKCARD);
|
||||||
|
int_reg = int_status & ~int_mask & ~IRQ_DONT_CARE_BITS;
|
||||||
|
|
||||||
|
dev_dbg(&host->pdev->dev, "IRQ status:%x mask:%x\n",
|
||||||
|
int_status, int_mask);
|
||||||
|
|
||||||
|
/* nothing to do: it's not our IRQ */
|
||||||
|
if (!int_reg) {
|
||||||
|
ret = IRQ_NONE;
|
||||||
|
goto irq_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (int_reg & SD_BUF_CMD_TIMEOUT) {
|
||||||
|
error = -ETIMEDOUT;
|
||||||
|
dev_dbg(&host->pdev->dev, "Timeout\n");
|
||||||
|
} else if (int_reg & SD_BUF_CRC_ERR) {
|
||||||
|
error = -EILSEQ;
|
||||||
|
dev_err(&host->pdev->dev, "BadCRC\n");
|
||||||
|
} else if (int_reg & (SD_BUF_ILLEGAL_ACCESS
|
||||||
|
| SD_BUF_CMD_INDEX_ERR
|
||||||
|
| SD_BUF_STOP_BIT_END_ERR
|
||||||
|
| SD_BUF_OVERFLOW
|
||||||
|
| SD_BUF_UNDERFLOW
|
||||||
|
| SD_BUF_DATA_TIMEOUT)) {
|
||||||
|
dev_err(&host->pdev->dev, "Buffer status error: { %s%s%s%s%s%s}\n",
|
||||||
|
int_reg & SD_BUF_ILLEGAL_ACCESS ? "ILLEGAL_ACC " : "",
|
||||||
|
int_reg & SD_BUF_CMD_INDEX_ERR ? "CMD_INDEX " : "",
|
||||||
|
int_reg & SD_BUF_STOP_BIT_END_ERR ? "STOPBIT_END " : "",
|
||||||
|
int_reg & SD_BUF_OVERFLOW ? "OVERFLOW " : "",
|
||||||
|
int_reg & SD_BUF_UNDERFLOW ? "UNDERFLOW " : "",
|
||||||
|
int_reg & SD_BUF_DATA_TIMEOUT ? "DATA_TIMEOUT " : "");
|
||||||
|
|
||||||
|
detail = ioread32(host->ioaddr + SD_ERRORSTATUS0);
|
||||||
|
dev_err(&host->pdev->dev, "detail error status { %s%s%s%s%s%s%s%s%s%s%s%s%s}\n",
|
||||||
|
detail & SD_ERR0_RESP_CMD_ERR ? "RESP_CMD " : "",
|
||||||
|
detail & SD_ERR0_RESP_NON_CMD12_END_BIT_ERR ? "RESP_END_BIT " : "",
|
||||||
|
detail & SD_ERR0_RESP_CMD12_END_BIT_ERR ? "RESP_END_BIT " : "",
|
||||||
|
detail & SD_ERR0_READ_DATA_END_BIT_ERR ? "READ_DATA_END_BIT " : "",
|
||||||
|
detail & SD_ERR0_WRITE_CRC_STATUS_END_BIT_ERR ? "WRITE_CMD_END_BIT " : "",
|
||||||
|
detail & SD_ERR0_RESP_NON_CMD12_CRC_ERR ? "RESP_CRC " : "",
|
||||||
|
detail & SD_ERR0_RESP_CMD12_CRC_ERR ? "RESP_CRC " : "",
|
||||||
|
detail & SD_ERR0_READ_DATA_CRC_ERR ? "READ_DATA_CRC " : "",
|
||||||
|
detail & SD_ERR0_WRITE_CMD_CRC_ERR ? "WRITE_CMD_CRC " : "",
|
||||||
|
detail & SD_ERR1_NO_CMD_RESP ? "NO_CMD_RESP " : "",
|
||||||
|
detail & SD_ERR1_TIMEOUT_READ_DATA ? "READ_DATA_TIMEOUT " : "",
|
||||||
|
detail & SD_ERR1_TIMEOUT_CRS_STATUS ? "CRS_STATUS_TIMEOUT " : "",
|
||||||
|
detail & SD_ERR1_TIMEOUT_CRC_BUSY ? "CRC_BUSY_TIMEOUT " : "");
|
||||||
|
error = -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
if (host->cmd)
|
||||||
|
host->cmd->error = error;
|
||||||
|
|
||||||
|
if (error == -ETIMEDOUT) {
|
||||||
|
iowrite32(int_status &
|
||||||
|
~(SD_BUF_CMD_TIMEOUT | SD_CARD_RESP_END),
|
||||||
|
host->ioaddr + SD_CARDSTATUS);
|
||||||
|
} else {
|
||||||
|
toshsd_init(host);
|
||||||
|
__toshsd_set_ios(host->mmc, &host->mmc->ios);
|
||||||
|
goto irq_end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Card insert/remove. The mmc controlling code is stateless. */
|
||||||
|
if (int_reg & (SD_CARD_CARD_INSERTED_0 | SD_CARD_CARD_REMOVED_0)) {
|
||||||
|
iowrite32(int_status &
|
||||||
|
~(SD_CARD_CARD_REMOVED_0 | SD_CARD_CARD_INSERTED_0),
|
||||||
|
host->ioaddr + SD_CARDSTATUS);
|
||||||
|
|
||||||
|
if (int_reg & SD_CARD_CARD_INSERTED_0)
|
||||||
|
toshsd_init(host);
|
||||||
|
|
||||||
|
mmc_detect_change(host->mmc, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Data transfer */
|
||||||
|
if (int_reg & (SD_BUF_READ_ENABLE | SD_BUF_WRITE_ENABLE)) {
|
||||||
|
iowrite32(int_status &
|
||||||
|
~(SD_BUF_WRITE_ENABLE | SD_BUF_READ_ENABLE),
|
||||||
|
host->ioaddr + SD_CARDSTATUS);
|
||||||
|
|
||||||
|
ret = IRQ_WAKE_THREAD;
|
||||||
|
goto irq_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Command completion */
|
||||||
|
if (int_reg & SD_CARD_RESP_END) {
|
||||||
|
iowrite32(int_status & ~(SD_CARD_RESP_END),
|
||||||
|
host->ioaddr + SD_CARDSTATUS);
|
||||||
|
toshsd_cmd_irq(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Data transfer completion */
|
||||||
|
if (int_reg & SD_CARD_RW_END) {
|
||||||
|
iowrite32(int_status & ~(SD_CARD_RW_END),
|
||||||
|
host->ioaddr + SD_CARDSTATUS);
|
||||||
|
toshsd_data_end_irq(host);
|
||||||
|
}
|
||||||
|
irq_end:
|
||||||
|
spin_unlock(&host->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void toshsd_start_cmd(struct toshsd_host *host, struct mmc_command *cmd)
|
||||||
|
{
|
||||||
|
struct mmc_data *data = host->data;
|
||||||
|
int c = cmd->opcode;
|
||||||
|
|
||||||
|
dev_dbg(&host->pdev->dev, "Command opcode: %d\n", cmd->opcode);
|
||||||
|
|
||||||
|
if (cmd->opcode == MMC_STOP_TRANSMISSION) {
|
||||||
|
iowrite16(SD_STOPINT_ISSUE_CMD12,
|
||||||
|
host->ioaddr + SD_STOPINTERNAL);
|
||||||
|
|
||||||
|
cmd->resp[0] = cmd->opcode;
|
||||||
|
cmd->resp[1] = 0;
|
||||||
|
cmd->resp[2] = 0;
|
||||||
|
cmd->resp[3] = 0;
|
||||||
|
|
||||||
|
toshsd_finish_request(host);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (mmc_resp_type(cmd)) {
|
||||||
|
case MMC_RSP_NONE:
|
||||||
|
c |= SD_CMD_RESP_TYPE_NONE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MMC_RSP_R1:
|
||||||
|
c |= SD_CMD_RESP_TYPE_EXT_R1;
|
||||||
|
break;
|
||||||
|
case MMC_RSP_R1B:
|
||||||
|
c |= SD_CMD_RESP_TYPE_EXT_R1B;
|
||||||
|
break;
|
||||||
|
case MMC_RSP_R2:
|
||||||
|
c |= SD_CMD_RESP_TYPE_EXT_R2;
|
||||||
|
break;
|
||||||
|
case MMC_RSP_R3:
|
||||||
|
c |= SD_CMD_RESP_TYPE_EXT_R3;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
dev_err(&host->pdev->dev, "Unknown response type %d\n",
|
||||||
|
mmc_resp_type(cmd));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
host->cmd = cmd;
|
||||||
|
|
||||||
|
if (cmd->opcode == MMC_APP_CMD)
|
||||||
|
c |= SD_CMD_TYPE_ACMD;
|
||||||
|
|
||||||
|
if (cmd->opcode == MMC_GO_IDLE_STATE)
|
||||||
|
c |= (3 << 8); /* removed from ipaq-asic3.h for some reason */
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
c |= SD_CMD_DATA_PRESENT;
|
||||||
|
|
||||||
|
if (data->blocks > 1) {
|
||||||
|
iowrite16(SD_STOPINT_AUTO_ISSUE_CMD12,
|
||||||
|
host->ioaddr + SD_STOPINTERNAL);
|
||||||
|
c |= SD_CMD_MULTI_BLOCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data->flags & MMC_DATA_READ)
|
||||||
|
c |= SD_CMD_TRANSFER_READ;
|
||||||
|
|
||||||
|
/* MMC_DATA_WRITE does not require a bit to be set */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send the command */
|
||||||
|
iowrite32(cmd->arg, host->ioaddr + SD_ARG0);
|
||||||
|
iowrite16(c, host->ioaddr + SD_CMD);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void toshsd_start_data(struct toshsd_host *host, struct mmc_data *data)
|
||||||
|
{
|
||||||
|
unsigned int flags = SG_MITER_ATOMIC;
|
||||||
|
|
||||||
|
dev_dbg(&host->pdev->dev, "setup data transfer: blocksize %08x nr_blocks %d, offset: %08x\n",
|
||||||
|
data->blksz, data->blocks, data->sg->offset);
|
||||||
|
|
||||||
|
host->data = data;
|
||||||
|
|
||||||
|
if (data->flags & MMC_DATA_READ)
|
||||||
|
flags |= SG_MITER_TO_SG;
|
||||||
|
else
|
||||||
|
flags |= SG_MITER_FROM_SG;
|
||||||
|
|
||||||
|
sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags);
|
||||||
|
|
||||||
|
/* Set transfer length and blocksize */
|
||||||
|
iowrite16(data->blocks, host->ioaddr + SD_BLOCKCOUNT);
|
||||||
|
iowrite16(data->blksz, host->ioaddr + SD_CARDXFERDATALEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process requests from the MMC layer */
|
||||||
|
static void toshsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||||
|
{
|
||||||
|
struct toshsd_host *host = mmc_priv(mmc);
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
/* abort if card not present */
|
||||||
|
if (!(ioread16(host->ioaddr + SD_CARDSTATUS) & SD_CARD_PRESENT_0)) {
|
||||||
|
mrq->cmd->error = -ENOMEDIUM;
|
||||||
|
mmc_request_done(mmc, mrq);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
|
|
||||||
|
WARN_ON(host->mrq != NULL);
|
||||||
|
|
||||||
|
host->mrq = mrq;
|
||||||
|
|
||||||
|
if (mrq->data)
|
||||||
|
toshsd_start_data(host, mrq->data);
|
||||||
|
|
||||||
|
toshsd_set_led(host, 1);
|
||||||
|
|
||||||
|
toshsd_start_cmd(host, mrq->cmd);
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void toshsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||||
|
{
|
||||||
|
struct toshsd_host *host = mmc_priv(mmc);
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
|
__toshsd_set_ios(mmc, ios);
|
||||||
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int toshsd_get_ro(struct mmc_host *mmc)
|
||||||
|
{
|
||||||
|
struct toshsd_host *host = mmc_priv(mmc);
|
||||||
|
|
||||||
|
/* active low */
|
||||||
|
return !(ioread16(host->ioaddr + SD_CARDSTATUS) & SD_CARD_WRITE_PROTECT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int toshsd_get_cd(struct mmc_host *mmc)
|
||||||
|
{
|
||||||
|
struct toshsd_host *host = mmc_priv(mmc);
|
||||||
|
|
||||||
|
return !!(ioread16(host->ioaddr + SD_CARDSTATUS) & SD_CARD_PRESENT_0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct mmc_host_ops toshsd_ops = {
|
||||||
|
.request = toshsd_request,
|
||||||
|
.set_ios = toshsd_set_ios,
|
||||||
|
.get_ro = toshsd_get_ro,
|
||||||
|
.get_cd = toshsd_get_cd,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static void toshsd_powerdown(struct toshsd_host *host)
|
||||||
|
{
|
||||||
|
/* mask all interrupts */
|
||||||
|
iowrite32(0xffffffff, host->ioaddr + SD_INTMASKCARD);
|
||||||
|
/* disable card clock */
|
||||||
|
iowrite16(0x000, host->ioaddr + SDIO_BASE + SDIO_CLOCKNWAITCTRL);
|
||||||
|
iowrite16(0, host->ioaddr + SD_CARDCLOCKCTRL);
|
||||||
|
/* power down card */
|
||||||
|
pci_write_config_byte(host->pdev, SD_PCICFG_POWER1, SD_PCICFG_PWR1_OFF);
|
||||||
|
/* disable clock */
|
||||||
|
pci_write_config_byte(host->pdev, SD_PCICFG_CLKSTOP, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_SLEEP
|
||||||
|
static int toshsd_pm_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct pci_dev *pdev = to_pci_dev(dev);
|
||||||
|
struct toshsd_host *host = pci_get_drvdata(pdev);
|
||||||
|
|
||||||
|
toshsd_powerdown(host);
|
||||||
|
|
||||||
|
pci_save_state(pdev);
|
||||||
|
pci_enable_wake(pdev, PCI_D3hot, 0);
|
||||||
|
pci_disable_device(pdev);
|
||||||
|
pci_set_power_state(pdev, PCI_D3hot);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int toshsd_pm_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct pci_dev *pdev = to_pci_dev(dev);
|
||||||
|
struct toshsd_host *host = pci_get_drvdata(pdev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
pci_set_power_state(pdev, PCI_D0);
|
||||||
|
pci_restore_state(pdev);
|
||||||
|
ret = pci_enable_device(pdev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
toshsd_init(host);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_PM_SLEEP */
|
||||||
|
|
||||||
|
static int toshsd_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct toshsd_host *host;
|
||||||
|
struct mmc_host *mmc;
|
||||||
|
resource_size_t base;
|
||||||
|
|
||||||
|
ret = pci_enable_device(pdev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
mmc = mmc_alloc_host(sizeof(struct toshsd_host), &pdev->dev);
|
||||||
|
if (!mmc) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
host = mmc_priv(mmc);
|
||||||
|
host->mmc = mmc;
|
||||||
|
|
||||||
|
host->pdev = pdev;
|
||||||
|
pci_set_drvdata(pdev, host);
|
||||||
|
|
||||||
|
ret = pci_request_regions(pdev, DRIVER_NAME);
|
||||||
|
if (ret)
|
||||||
|
goto free;
|
||||||
|
|
||||||
|
host->ioaddr = pci_iomap(pdev, 0, 0);
|
||||||
|
if (!host->ioaddr) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto release;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set MMC host parameters */
|
||||||
|
mmc->ops = &toshsd_ops;
|
||||||
|
mmc->caps = MMC_CAP_4_BIT_DATA;
|
||||||
|
mmc->ocr_avail = MMC_VDD_32_33;
|
||||||
|
|
||||||
|
mmc->f_min = HCLK / 512;
|
||||||
|
mmc->f_max = HCLK;
|
||||||
|
|
||||||
|
spin_lock_init(&host->lock);
|
||||||
|
|
||||||
|
toshsd_init(host);
|
||||||
|
|
||||||
|
ret = request_threaded_irq(pdev->irq, toshsd_irq, toshsd_thread_irq,
|
||||||
|
IRQF_SHARED, DRIVER_NAME, host);
|
||||||
|
if (ret)
|
||||||
|
goto unmap;
|
||||||
|
|
||||||
|
mmc_add_host(mmc);
|
||||||
|
|
||||||
|
base = pci_resource_start(pdev, 0);
|
||||||
|
dev_dbg(&pdev->dev, "MMIO %pa, IRQ %d\n", &base, pdev->irq);
|
||||||
|
|
||||||
|
pm_suspend_ignore_children(&pdev->dev, 1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
unmap:
|
||||||
|
pci_iounmap(pdev, host->ioaddr);
|
||||||
|
release:
|
||||||
|
pci_release_regions(pdev);
|
||||||
|
free:
|
||||||
|
mmc_free_host(mmc);
|
||||||
|
pci_set_drvdata(pdev, NULL);
|
||||||
|
err:
|
||||||
|
pci_disable_device(pdev);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void toshsd_remove(struct pci_dev *pdev)
|
||||||
|
{
|
||||||
|
struct toshsd_host *host = pci_get_drvdata(pdev);
|
||||||
|
|
||||||
|
mmc_remove_host(host->mmc);
|
||||||
|
toshsd_powerdown(host);
|
||||||
|
free_irq(pdev->irq, host);
|
||||||
|
pci_iounmap(pdev, host->ioaddr);
|
||||||
|
pci_release_regions(pdev);
|
||||||
|
mmc_free_host(host->mmc);
|
||||||
|
pci_set_drvdata(pdev, NULL);
|
||||||
|
pci_disable_device(pdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct dev_pm_ops toshsd_pm_ops = {
|
||||||
|
SET_SYSTEM_SLEEP_PM_OPS(toshsd_pm_suspend, toshsd_pm_resume)
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct pci_driver toshsd_driver = {
|
||||||
|
.name = DRIVER_NAME,
|
||||||
|
.id_table = pci_ids,
|
||||||
|
.probe = toshsd_probe,
|
||||||
|
.remove = toshsd_remove,
|
||||||
|
.driver.pm = &toshsd_pm_ops,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init toshsd_drv_init(void)
|
||||||
|
{
|
||||||
|
return pci_register_driver(&toshsd_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit toshsd_drv_exit(void)
|
||||||
|
{
|
||||||
|
pci_unregister_driver(&toshsd_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(toshsd_drv_init);
|
||||||
|
module_exit(toshsd_drv_exit);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Ondrej Zary, Richard Betts");
|
||||||
|
MODULE_DESCRIPTION("Toshiba PCI Secure Digital Host Controller Interface driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
176
drivers/mmc/host/toshsd.h
Normal file
176
drivers/mmc/host/toshsd.h
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
/*
|
||||||
|
* Toshiba PCI Secure Digital Host Controller Interface driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Ondrej Zary
|
||||||
|
* Copyright (C) 2007 Richard Betts, All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Based on asic3_mmc.c Copyright (c) 2005 SDG Systems, LLC
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define HCLK 33000000 /* 33 MHz (PCI clock) */
|
||||||
|
|
||||||
|
#define SD_PCICFG_CLKSTOP 0x40 /* 0x1f = clock controller, 0 = stop */
|
||||||
|
#define SD_PCICFG_GATEDCLK 0x41 /* Gated clock */
|
||||||
|
#define SD_PCICFG_CLKMODE 0x42 /* Control clock of SD controller */
|
||||||
|
#define SD_PCICFG_PINSTATUS 0x44 /* R/O: read status of SD pins */
|
||||||
|
#define SD_PCICFG_POWER1 0x48
|
||||||
|
#define SD_PCICFG_POWER2 0x49
|
||||||
|
#define SD_PCICFG_POWER3 0x4a
|
||||||
|
#define SD_PCICFG_CARDDETECT 0x4c
|
||||||
|
#define SD_PCICFG_SLOTS 0x50 /* R/O: define support slot number */
|
||||||
|
#define SD_PCICFG_EXTGATECLK1 0xf0 /* Could be used for gated clock */
|
||||||
|
#define SD_PCICFG_EXTGATECLK2 0xf1 /* Could be used for gated clock */
|
||||||
|
#define SD_PCICFG_EXTGATECLK3 0xf9 /* Bit 1: double buffer/single buffer */
|
||||||
|
#define SD_PCICFG_SDLED_ENABLE1 0xfa
|
||||||
|
#define SD_PCICFG_SDLED_ENABLE2 0xfe
|
||||||
|
|
||||||
|
#define SD_PCICFG_CLKMODE_DIV_DISABLE BIT(0)
|
||||||
|
#define SD_PCICFG_CLKSTOP_ENABLE_ALL 0x1f
|
||||||
|
#define SD_PCICFG_LED_ENABLE1_START 0x12
|
||||||
|
#define SD_PCICFG_LED_ENABLE2_START 0x80
|
||||||
|
|
||||||
|
#define SD_PCICFG_PWR1_33V 0x08 /* Set for 3.3 volts */
|
||||||
|
#define SD_PCICFG_PWR1_OFF 0x00 /* Turn off power */
|
||||||
|
#define SD_PCICFG_PWR2_AUTO 0x02
|
||||||
|
|
||||||
|
#define SD_CMD 0x00 /* also for SDIO */
|
||||||
|
#define SD_ARG0 0x04 /* also for SDIO */
|
||||||
|
#define SD_ARG1 0x06 /* also for SDIO */
|
||||||
|
#define SD_STOPINTERNAL 0x08
|
||||||
|
#define SD_BLOCKCOUNT 0x0a /* also for SDIO */
|
||||||
|
#define SD_RESPONSE0 0x0c /* also for SDIO */
|
||||||
|
#define SD_RESPONSE1 0x0e /* also for SDIO */
|
||||||
|
#define SD_RESPONSE2 0x10 /* also for SDIO */
|
||||||
|
#define SD_RESPONSE3 0x12 /* also for SDIO */
|
||||||
|
#define SD_RESPONSE4 0x14 /* also for SDIO */
|
||||||
|
#define SD_RESPONSE5 0x16 /* also for SDIO */
|
||||||
|
#define SD_RESPONSE6 0x18 /* also for SDIO */
|
||||||
|
#define SD_RESPONSE7 0x1a /* also for SDIO */
|
||||||
|
#define SD_CARDSTATUS 0x1c /* also for SDIO */
|
||||||
|
#define SD_BUFFERCTRL 0x1e /* also for SDIO */
|
||||||
|
#define SD_INTMASKCARD 0x20 /* also for SDIO */
|
||||||
|
#define SD_INTMASKBUFFER 0x22 /* also for SDIO */
|
||||||
|
#define SD_CARDCLOCKCTRL 0x24
|
||||||
|
#define SD_CARDXFERDATALEN 0x26 /* also for SDIO */
|
||||||
|
#define SD_CARDOPTIONSETUP 0x28 /* also for SDIO */
|
||||||
|
#define SD_ERRORSTATUS0 0x2c /* also for SDIO */
|
||||||
|
#define SD_ERRORSTATUS1 0x2e /* also for SDIO */
|
||||||
|
#define SD_DATAPORT 0x30 /* also for SDIO */
|
||||||
|
#define SD_TRANSACTIONCTRL 0x34 /* also for SDIO */
|
||||||
|
#define SD_SOFTWARERESET 0xe0 /* also for SDIO */
|
||||||
|
|
||||||
|
/* registers above marked "also for SDIO" and all SDIO registers below can be
|
||||||
|
* accessed at SDIO_BASE + reg address */
|
||||||
|
#define SDIO_BASE 0x100
|
||||||
|
|
||||||
|
#define SDIO_CARDPORTSEL 0x02
|
||||||
|
#define SDIO_CARDINTCTRL 0x36
|
||||||
|
#define SDIO_CLOCKNWAITCTRL 0x38
|
||||||
|
#define SDIO_HOSTINFORMATION 0x3a
|
||||||
|
#define SDIO_ERRORCTRL 0x3c
|
||||||
|
#define SDIO_LEDCTRL 0x3e
|
||||||
|
|
||||||
|
#define SD_TRANSCTL_SET BIT(8)
|
||||||
|
|
||||||
|
#define SD_CARDCLK_DIV_DISABLE BIT(15)
|
||||||
|
#define SD_CARDCLK_ENABLE_CLOCK BIT(8)
|
||||||
|
#define SD_CARDCLK_CLK_DIV_512 BIT(7)
|
||||||
|
#define SD_CARDCLK_CLK_DIV_256 BIT(6)
|
||||||
|
#define SD_CARDCLK_CLK_DIV_128 BIT(5)
|
||||||
|
#define SD_CARDCLK_CLK_DIV_64 BIT(4)
|
||||||
|
#define SD_CARDCLK_CLK_DIV_32 BIT(3)
|
||||||
|
#define SD_CARDCLK_CLK_DIV_16 BIT(2)
|
||||||
|
#define SD_CARDCLK_CLK_DIV_8 BIT(1)
|
||||||
|
#define SD_CARDCLK_CLK_DIV_4 BIT(0)
|
||||||
|
#define SD_CARDCLK_CLK_DIV_2 0
|
||||||
|
|
||||||
|
#define SD_CARDOPT_REQUIRED 0x000e
|
||||||
|
#define SD_CARDOPT_DATA_RESP_TIMEOUT(x) (((x) & 0x0f) << 4) /* 4 bits */
|
||||||
|
#define SD_CARDOPT_C2_MODULE_ABSENT BIT(14)
|
||||||
|
#define SD_CARDOPT_DATA_XFR_WIDTH_1 (1 << 15)
|
||||||
|
#define SD_CARDOPT_DATA_XFR_WIDTH_4 (0 << 15)
|
||||||
|
|
||||||
|
#define SD_CMD_TYPE_CMD (0 << 6)
|
||||||
|
#define SD_CMD_TYPE_ACMD (1 << 6)
|
||||||
|
#define SD_CMD_TYPE_AUTHEN (2 << 6)
|
||||||
|
#define SD_CMD_RESP_TYPE_NONE (3 << 8)
|
||||||
|
#define SD_CMD_RESP_TYPE_EXT_R1 (4 << 8)
|
||||||
|
#define SD_CMD_RESP_TYPE_EXT_R1B (5 << 8)
|
||||||
|
#define SD_CMD_RESP_TYPE_EXT_R2 (6 << 8)
|
||||||
|
#define SD_CMD_RESP_TYPE_EXT_R3 (7 << 8)
|
||||||
|
#define SD_CMD_RESP_TYPE_EXT_R6 (4 << 8)
|
||||||
|
#define SD_CMD_RESP_TYPE_EXT_R7 (4 << 8)
|
||||||
|
#define SD_CMD_DATA_PRESENT BIT(11)
|
||||||
|
#define SD_CMD_TRANSFER_READ BIT(12)
|
||||||
|
#define SD_CMD_MULTI_BLOCK BIT(13)
|
||||||
|
#define SD_CMD_SECURITY_CMD BIT(14)
|
||||||
|
|
||||||
|
#define SD_STOPINT_ISSUE_CMD12 BIT(0)
|
||||||
|
#define SD_STOPINT_AUTO_ISSUE_CMD12 BIT(8)
|
||||||
|
|
||||||
|
#define SD_CARD_RESP_END BIT(0)
|
||||||
|
#define SD_CARD_RW_END BIT(2)
|
||||||
|
#define SD_CARD_CARD_REMOVED_0 BIT(3)
|
||||||
|
#define SD_CARD_CARD_INSERTED_0 BIT(4)
|
||||||
|
#define SD_CARD_PRESENT_0 BIT(5)
|
||||||
|
#define SD_CARD_UNK6 BIT(6)
|
||||||
|
#define SD_CARD_WRITE_PROTECT BIT(7)
|
||||||
|
#define SD_CARD_CARD_REMOVED_3 BIT(8)
|
||||||
|
#define SD_CARD_CARD_INSERTED_3 BIT(9)
|
||||||
|
#define SD_CARD_PRESENT_3 BIT(10)
|
||||||
|
|
||||||
|
#define SD_BUF_CMD_INDEX_ERR BIT(16)
|
||||||
|
#define SD_BUF_CRC_ERR BIT(17)
|
||||||
|
#define SD_BUF_STOP_BIT_END_ERR BIT(18)
|
||||||
|
#define SD_BUF_DATA_TIMEOUT BIT(19)
|
||||||
|
#define SD_BUF_OVERFLOW BIT(20)
|
||||||
|
#define SD_BUF_UNDERFLOW BIT(21)
|
||||||
|
#define SD_BUF_CMD_TIMEOUT BIT(22)
|
||||||
|
#define SD_BUF_UNK7 BIT(23)
|
||||||
|
#define SD_BUF_READ_ENABLE BIT(24)
|
||||||
|
#define SD_BUF_WRITE_ENABLE BIT(25)
|
||||||
|
#define SD_BUF_ILLEGAL_FUNCTION BIT(29)
|
||||||
|
#define SD_BUF_CMD_BUSY BIT(30)
|
||||||
|
#define SD_BUF_ILLEGAL_ACCESS BIT(31)
|
||||||
|
|
||||||
|
#define SD_ERR0_RESP_CMD_ERR BIT(0)
|
||||||
|
#define SD_ERR0_RESP_NON_CMD12_END_BIT_ERR BIT(2)
|
||||||
|
#define SD_ERR0_RESP_CMD12_END_BIT_ERR BIT(3)
|
||||||
|
#define SD_ERR0_READ_DATA_END_BIT_ERR BIT(4)
|
||||||
|
#define SD_ERR0_WRITE_CRC_STATUS_END_BIT_ERR BIT(5)
|
||||||
|
#define SD_ERR0_RESP_NON_CMD12_CRC_ERR BIT(8)
|
||||||
|
#define SD_ERR0_RESP_CMD12_CRC_ERR BIT(9)
|
||||||
|
#define SD_ERR0_READ_DATA_CRC_ERR BIT(10)
|
||||||
|
#define SD_ERR0_WRITE_CMD_CRC_ERR BIT(11)
|
||||||
|
|
||||||
|
#define SD_ERR1_NO_CMD_RESP BIT(16)
|
||||||
|
#define SD_ERR1_TIMEOUT_READ_DATA BIT(20)
|
||||||
|
#define SD_ERR1_TIMEOUT_CRS_STATUS BIT(21)
|
||||||
|
#define SD_ERR1_TIMEOUT_CRC_BUSY BIT(22)
|
||||||
|
|
||||||
|
#define IRQ_DONT_CARE_BITS (SD_CARD_PRESENT_3 \
|
||||||
|
| SD_CARD_WRITE_PROTECT \
|
||||||
|
| SD_CARD_UNK6 \
|
||||||
|
| SD_CARD_PRESENT_0 \
|
||||||
|
| SD_BUF_UNK7 \
|
||||||
|
| SD_BUF_CMD_BUSY)
|
||||||
|
|
||||||
|
struct toshsd_host {
|
||||||
|
struct pci_dev *pdev;
|
||||||
|
struct mmc_host *mmc;
|
||||||
|
|
||||||
|
spinlock_t lock;
|
||||||
|
|
||||||
|
struct mmc_request *mrq;/* Current request */
|
||||||
|
struct mmc_command *cmd;/* Current command */
|
||||||
|
struct mmc_data *data; /* Current data request */
|
||||||
|
|
||||||
|
struct sg_mapping_iter sg_miter; /* for PIO */
|
||||||
|
|
||||||
|
void __iomem *ioaddr; /* mapped address */
|
||||||
|
};
|
@ -11,6 +11,7 @@
|
|||||||
* @detect_pin: GPIO pin wired to the card detect switch
|
* @detect_pin: GPIO pin wired to the card detect switch
|
||||||
* @wp_pin: GPIO pin wired to the write protect sensor
|
* @wp_pin: GPIO pin wired to the write protect sensor
|
||||||
* @detect_is_active_high: The state of the detect pin when it is active
|
* @detect_is_active_high: The state of the detect pin when it is active
|
||||||
|
* @non_removable: The slot is not removable, only detect once
|
||||||
*
|
*
|
||||||
* If a given slot is not present on the board, @bus_width should be
|
* If a given slot is not present on the board, @bus_width should be
|
||||||
* set to 0. The other fields are ignored in this case.
|
* set to 0. The other fields are ignored in this case.
|
||||||
@ -26,6 +27,7 @@ struct mci_slot_pdata {
|
|||||||
int detect_pin;
|
int detect_pin;
|
||||||
int wp_pin;
|
int wp_pin;
|
||||||
bool detect_is_active_high;
|
bool detect_is_active_high;
|
||||||
|
bool non_removable;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -88,6 +88,9 @@ struct mmc_ext_csd {
|
|||||||
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 */
|
||||||
bool boot_ro_lockable;
|
bool boot_ro_lockable;
|
||||||
|
bool ffu_capable; /* Firmware upgrade support */
|
||||||
|
#define MMC_FIRMWARE_LEN 8
|
||||||
|
u8 fwrev[MMC_FIRMWARE_LEN]; /* FW version */
|
||||||
u8 raw_exception_status; /* 54 */
|
u8 raw_exception_status; /* 54 */
|
||||||
u8 raw_partition_support; /* 160 */
|
u8 raw_partition_support; /* 160 */
|
||||||
u8 raw_rpmb_size_mult; /* 168 */
|
u8 raw_rpmb_size_mult; /* 168 */
|
||||||
@ -509,24 +512,8 @@ static inline int mmc_card_broken_irq_polling(const struct mmc_card *c)
|
|||||||
|
|
||||||
#define mmc_dev_to_card(d) container_of(d, struct mmc_card, dev)
|
#define mmc_dev_to_card(d) container_of(d, struct mmc_card, dev)
|
||||||
|
|
||||||
#define mmc_list_to_card(l) container_of(l, struct mmc_card, node)
|
extern int mmc_register_driver(struct device_driver *);
|
||||||
#define mmc_get_drvdata(c) dev_get_drvdata(&(c)->dev)
|
extern void mmc_unregister_driver(struct device_driver *);
|
||||||
#define mmc_set_drvdata(c,d) dev_set_drvdata(&(c)->dev, d)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* MMC device driver (e.g., Flash card, I/O card...)
|
|
||||||
*/
|
|
||||||
struct mmc_driver {
|
|
||||||
struct device_driver drv;
|
|
||||||
int (*probe)(struct mmc_card *);
|
|
||||||
void (*remove)(struct mmc_card *);
|
|
||||||
int (*suspend)(struct mmc_card *);
|
|
||||||
int (*resume)(struct mmc_card *);
|
|
||||||
void (*shutdown)(struct mmc_card *);
|
|
||||||
};
|
|
||||||
|
|
||||||
extern int mmc_register_driver(struct mmc_driver *);
|
|
||||||
extern void mmc_unregister_driver(struct mmc_driver *);
|
|
||||||
|
|
||||||
extern void mmc_fixup_device(struct mmc_card *card,
|
extern void mmc_fixup_device(struct mmc_card *card,
|
||||||
const struct mmc_fixup *table);
|
const struct mmc_fixup *table);
|
||||||
|
@ -154,7 +154,8 @@ extern void mmc_start_bkops(struct mmc_card *card, bool from_exception);
|
|||||||
extern int __mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int, bool,
|
extern int __mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int, bool,
|
||||||
bool, bool);
|
bool, bool);
|
||||||
extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);
|
extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);
|
||||||
extern int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd);
|
extern int mmc_send_tuning(struct mmc_host *host);
|
||||||
|
extern int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd);
|
||||||
|
|
||||||
#define MMC_ERASE_ARG 0x00000000
|
#define MMC_ERASE_ARG 0x00000000
|
||||||
#define MMC_SECURE_ERASE_ARG 0x80000000
|
#define MMC_SECURE_ERASE_ARG 0x80000000
|
||||||
|
@ -54,6 +54,7 @@ struct mmc_data;
|
|||||||
* transfer is in progress.
|
* transfer is in progress.
|
||||||
* @use_dma: Whether DMA channel is initialized or not.
|
* @use_dma: Whether DMA channel is initialized or not.
|
||||||
* @using_dma: Whether DMA is in use for the current transfer.
|
* @using_dma: Whether DMA is in use for the current transfer.
|
||||||
|
* @dma_64bit_address: Whether DMA supports 64-bit address mode or not.
|
||||||
* @sg_dma: Bus address of DMA buffer.
|
* @sg_dma: Bus address of DMA buffer.
|
||||||
* @sg_cpu: Virtual address of DMA buffer.
|
* @sg_cpu: Virtual address of DMA buffer.
|
||||||
* @dma_ops: Pointer to platform-specific DMA callbacks.
|
* @dma_ops: Pointer to platform-specific DMA callbacks.
|
||||||
@ -96,6 +97,7 @@ struct mmc_data;
|
|||||||
* @quirks: Set of quirks that apply to specific versions of the IP.
|
* @quirks: Set of quirks that apply to specific versions of the IP.
|
||||||
* @irq_flags: The flags to be passed to request_irq.
|
* @irq_flags: The flags to be passed to request_irq.
|
||||||
* @irq: The irq value to be passed to request_irq.
|
* @irq: The irq value to be passed to request_irq.
|
||||||
|
* @sdio_id0: Number of slot0 in the SDIO interrupt registers.
|
||||||
*
|
*
|
||||||
* Locking
|
* Locking
|
||||||
* =======
|
* =======
|
||||||
@ -135,11 +137,11 @@ struct dw_mci {
|
|||||||
struct mmc_command stop_abort;
|
struct mmc_command stop_abort;
|
||||||
unsigned int prev_blksz;
|
unsigned int prev_blksz;
|
||||||
unsigned char timing;
|
unsigned char timing;
|
||||||
struct workqueue_struct *card_workqueue;
|
|
||||||
|
|
||||||
/* DMA interface members*/
|
/* DMA interface members*/
|
||||||
int use_dma;
|
int use_dma;
|
||||||
int using_dma;
|
int using_dma;
|
||||||
|
int dma_64bit_address;
|
||||||
|
|
||||||
dma_addr_t sg_dma;
|
dma_addr_t sg_dma;
|
||||||
void *sg_cpu;
|
void *sg_cpu;
|
||||||
@ -154,7 +156,6 @@ struct dw_mci {
|
|||||||
u32 stop_cmdr;
|
u32 stop_cmdr;
|
||||||
u32 dir_status;
|
u32 dir_status;
|
||||||
struct tasklet_struct tasklet;
|
struct tasklet_struct tasklet;
|
||||||
struct work_struct card_work;
|
|
||||||
unsigned long pending_events;
|
unsigned long pending_events;
|
||||||
unsigned long completed_events;
|
unsigned long completed_events;
|
||||||
enum dw_mci_state state;
|
enum dw_mci_state state;
|
||||||
@ -193,6 +194,8 @@ struct dw_mci {
|
|||||||
bool vqmmc_enabled;
|
bool vqmmc_enabled;
|
||||||
unsigned long irq_flags; /* IRQ flags */
|
unsigned long irq_flags; /* IRQ flags */
|
||||||
int irq;
|
int irq;
|
||||||
|
|
||||||
|
int sdio_id0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* DMA ops for Internal/External DMAC interface */
|
/* DMA ops for Internal/External DMAC interface */
|
||||||
|
@ -289,6 +289,7 @@ struct mmc_host {
|
|||||||
#define MMC_CAP2_HS400_1_2V (1 << 16) /* Can support HS400 1.2V */
|
#define MMC_CAP2_HS400_1_2V (1 << 16) /* Can support HS400 1.2V */
|
||||||
#define MMC_CAP2_HS400 (MMC_CAP2_HS400_1_8V | \
|
#define MMC_CAP2_HS400 (MMC_CAP2_HS400_1_8V | \
|
||||||
MMC_CAP2_HS400_1_2V)
|
MMC_CAP2_HS400_1_2V)
|
||||||
|
#define MMC_CAP2_HSX00_1_2V (MMC_CAP2_HS200_1_2V_SDR | MMC_CAP2_HS400_1_2V)
|
||||||
#define MMC_CAP2_SDIO_IRQ_NOTHREAD (1 << 17)
|
#define MMC_CAP2_SDIO_IRQ_NOTHREAD (1 << 17)
|
||||||
|
|
||||||
mmc_pm_flag_t pm_caps; /* supported pm features */
|
mmc_pm_flag_t pm_caps; /* supported pm features */
|
||||||
|
@ -296,6 +296,7 @@ struct _mmc_csd {
|
|||||||
#define EXT_CSD_SANITIZE_START 165 /* W */
|
#define EXT_CSD_SANITIZE_START 165 /* W */
|
||||||
#define EXT_CSD_WR_REL_PARAM 166 /* RO */
|
#define EXT_CSD_WR_REL_PARAM 166 /* RO */
|
||||||
#define EXT_CSD_RPMB_MULT 168 /* RO */
|
#define EXT_CSD_RPMB_MULT 168 /* RO */
|
||||||
|
#define EXT_CSD_FW_CONFIG 169 /* R/W */
|
||||||
#define EXT_CSD_BOOT_WP 173 /* R/W */
|
#define EXT_CSD_BOOT_WP 173 /* R/W */
|
||||||
#define EXT_CSD_ERASE_GROUP_DEF 175 /* R/W */
|
#define EXT_CSD_ERASE_GROUP_DEF 175 /* R/W */
|
||||||
#define EXT_CSD_PART_CONFIG 179 /* R/W */
|
#define EXT_CSD_PART_CONFIG 179 /* R/W */
|
||||||
@ -332,6 +333,8 @@ struct _mmc_csd {
|
|||||||
#define EXT_CSD_GENERIC_CMD6_TIME 248 /* RO */
|
#define EXT_CSD_GENERIC_CMD6_TIME 248 /* RO */
|
||||||
#define EXT_CSD_CACHE_SIZE 249 /* RO, 4 bytes */
|
#define EXT_CSD_CACHE_SIZE 249 /* RO, 4 bytes */
|
||||||
#define EXT_CSD_PWR_CL_DDR_200_360 253 /* RO */
|
#define EXT_CSD_PWR_CL_DDR_200_360 253 /* RO */
|
||||||
|
#define EXT_CSD_FIRMWARE_VERSION 254 /* RO, 8 bytes */
|
||||||
|
#define EXT_CSD_SUPPORTED_MODE 493 /* RO */
|
||||||
#define EXT_CSD_TAG_UNIT_SIZE 498 /* RO */
|
#define EXT_CSD_TAG_UNIT_SIZE 498 /* RO */
|
||||||
#define EXT_CSD_DATA_TAG_SUPPORT 499 /* RO */
|
#define EXT_CSD_DATA_TAG_SUPPORT 499 /* RO */
|
||||||
#define EXT_CSD_MAX_PACKED_WRITES 500 /* RO */
|
#define EXT_CSD_MAX_PACKED_WRITES 500 /* RO */
|
||||||
|
@ -100,6 +100,12 @@ struct sdhci_host {
|
|||||||
#define SDHCI_QUIRK2_BROKEN_DDR50 (1<<7)
|
#define SDHCI_QUIRK2_BROKEN_DDR50 (1<<7)
|
||||||
/* Stop command (CMD12) can set Transfer Complete when not using MMC_RSP_BUSY */
|
/* Stop command (CMD12) can set Transfer Complete when not using MMC_RSP_BUSY */
|
||||||
#define SDHCI_QUIRK2_STOP_WITH_TC (1<<8)
|
#define SDHCI_QUIRK2_STOP_WITH_TC (1<<8)
|
||||||
|
/* Controller does not support 64-bit DMA */
|
||||||
|
#define SDHCI_QUIRK2_BROKEN_64_BIT_DMA (1<<9)
|
||||||
|
/* need clear transfer mode register before send cmd */
|
||||||
|
#define SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD (1<<10)
|
||||||
|
/* Capability register bit-63 indicates HS400 support */
|
||||||
|
#define SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 (1<<11)
|
||||||
|
|
||||||
int irq; /* Device IRQ */
|
int irq; /* Device IRQ */
|
||||||
void __iomem *ioaddr; /* Mapped address */
|
void __iomem *ioaddr; /* Mapped address */
|
||||||
@ -130,6 +136,7 @@ struct sdhci_host {
|
|||||||
#define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */
|
#define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */
|
||||||
#define SDHCI_SDR104_NEEDS_TUNING (1<<10) /* SDR104/HS200 needs tuning */
|
#define SDHCI_SDR104_NEEDS_TUNING (1<<10) /* SDR104/HS200 needs tuning */
|
||||||
#define SDHCI_USING_RETUNING_TIMER (1<<11) /* Host is using a retuning timer for the card */
|
#define SDHCI_USING_RETUNING_TIMER (1<<11) /* Host is using a retuning timer for the card */
|
||||||
|
#define SDHCI_USE_64_BIT_DMA (1<<12) /* Use 64-bit DMA */
|
||||||
|
|
||||||
unsigned int version; /* SDHCI spec. version */
|
unsigned int version; /* SDHCI spec. version */
|
||||||
|
|
||||||
@ -155,12 +162,19 @@ struct sdhci_host {
|
|||||||
|
|
||||||
int sg_count; /* Mapped sg entries */
|
int sg_count; /* Mapped sg entries */
|
||||||
|
|
||||||
u8 *adma_desc; /* ADMA descriptor table */
|
void *adma_table; /* ADMA descriptor table */
|
||||||
u8 *align_buffer; /* Bounce buffer */
|
void *align_buffer; /* Bounce buffer */
|
||||||
|
|
||||||
|
size_t adma_table_sz; /* ADMA descriptor table size */
|
||||||
|
size_t align_buffer_sz; /* Bounce buffer size */
|
||||||
|
|
||||||
dma_addr_t adma_addr; /* Mapped ADMA descr. table */
|
dma_addr_t adma_addr; /* Mapped ADMA descr. table */
|
||||||
dma_addr_t align_addr; /* Mapped bounce buffer */
|
dma_addr_t align_addr; /* Mapped bounce buffer */
|
||||||
|
|
||||||
|
unsigned int desc_sz; /* ADMA descriptor size */
|
||||||
|
unsigned int align_sz; /* ADMA alignment */
|
||||||
|
unsigned int align_mask; /* ADMA alignment mask */
|
||||||
|
|
||||||
struct tasklet_struct finish_tasklet; /* Tasklet structures */
|
struct tasklet_struct finish_tasklet; /* Tasklet structures */
|
||||||
|
|
||||||
struct timer_list timer; /* Timer for timeouts */
|
struct timer_list timer; /* Timer for timeouts */
|
||||||
|
@ -84,8 +84,6 @@ struct sdio_driver {
|
|||||||
struct device_driver drv;
|
struct device_driver drv;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define to_sdio_driver(d) container_of(d, struct sdio_driver, drv)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SDIO_DEVICE - macro used to describe a specific SDIO device
|
* SDIO_DEVICE - macro used to describe a specific SDIO device
|
||||||
* @vend: the 16 bit manufacturer code
|
* @vend: the 16 bit manufacturer code
|
||||||
|
90
include/linux/platform_data/hsmmc-omap.h
Normal file
90
include/linux/platform_data/hsmmc-omap.h
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* MMC definitions for OMAP2
|
||||||
|
*
|
||||||
|
* Copyright (C) 2006 Nokia Corporation
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* struct omap_hsmmc_dev_attr.flags possibilities
|
||||||
|
*
|
||||||
|
* OMAP_HSMMC_SUPPORTS_DUAL_VOLT: Some HSMMC controller instances can
|
||||||
|
* operate with either 1.8Vdc or 3.0Vdc card voltages; this flag
|
||||||
|
* should be set if this is the case. See for example Section 22.5.3
|
||||||
|
* "MMC/SD/SDIO1 Bus Voltage Selection" of the OMAP34xx Multimedia
|
||||||
|
* Device Silicon Revision 3.1.x Revision ZR (July 2011) (SWPU223R).
|
||||||
|
*
|
||||||
|
* OMAP_HSMMC_BROKEN_MULTIBLOCK_READ: Multiple-block read transfers
|
||||||
|
* don't work correctly on some MMC controller instances on some
|
||||||
|
* OMAP3 SoCs; this flag should be set if this is the case. See
|
||||||
|
* for example Advisory 2.1.1.128 "MMC: Multiple Block Read
|
||||||
|
* Operation Issue" in _OMAP3530/3525/3515/3503 Silicon Errata_
|
||||||
|
* Revision F (October 2010) (SPRZ278F).
|
||||||
|
*/
|
||||||
|
#define OMAP_HSMMC_SUPPORTS_DUAL_VOLT BIT(0)
|
||||||
|
#define OMAP_HSMMC_BROKEN_MULTIBLOCK_READ BIT(1)
|
||||||
|
#define OMAP_HSMMC_SWAKEUP_MISSING BIT(2)
|
||||||
|
|
||||||
|
struct omap_hsmmc_dev_attr {
|
||||||
|
u8 flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mmc_card;
|
||||||
|
|
||||||
|
struct omap_hsmmc_platform_data {
|
||||||
|
/* back-link to device */
|
||||||
|
struct device *dev;
|
||||||
|
|
||||||
|
/* set if your board has components or wiring that limits the
|
||||||
|
* maximum frequency on the MMC bus */
|
||||||
|
unsigned int max_freq;
|
||||||
|
|
||||||
|
/* Integrating attributes from the omap_hwmod layer */
|
||||||
|
u8 controller_flags;
|
||||||
|
|
||||||
|
/* Register offset deviation */
|
||||||
|
u16 reg_offset;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 4/8 wires and any additional host capabilities
|
||||||
|
* need to OR'd all capabilities (ref. linux/mmc/host.h)
|
||||||
|
*/
|
||||||
|
u32 caps; /* Used for the MMC driver on 2430 and later */
|
||||||
|
u32 pm_caps; /* PM capabilities of the mmc */
|
||||||
|
|
||||||
|
/* switch pin can be for card detect (default) or card cover */
|
||||||
|
unsigned cover:1;
|
||||||
|
|
||||||
|
/* use the internal clock */
|
||||||
|
unsigned internal_clock:1;
|
||||||
|
|
||||||
|
/* nonremovable e.g. eMMC */
|
||||||
|
unsigned nonremovable:1;
|
||||||
|
|
||||||
|
/* eMMC does not handle power off when not in sleep state */
|
||||||
|
unsigned no_regulator_off_init:1;
|
||||||
|
|
||||||
|
/* we can put the features above into this variable */
|
||||||
|
#define HSMMC_HAS_PBIAS (1 << 0)
|
||||||
|
#define HSMMC_HAS_UPDATED_RESET (1 << 1)
|
||||||
|
#define HSMMC_HAS_HSPE_SUPPORT (1 << 2)
|
||||||
|
unsigned features;
|
||||||
|
|
||||||
|
int switch_pin; /* gpio (card detect) */
|
||||||
|
int gpio_wp; /* gpio (write protect) */
|
||||||
|
|
||||||
|
int (*set_power)(struct device *dev, int power_on, int vdd);
|
||||||
|
void (*remux)(struct device *dev, int power_on);
|
||||||
|
/* Call back before enabling / disabling regulators */
|
||||||
|
void (*before_set_reg)(struct device *dev, int power_on, int vdd);
|
||||||
|
/* Call back after enabling / disabling regulators */
|
||||||
|
void (*after_set_reg)(struct device *dev, int power_on, int vdd);
|
||||||
|
/* if we have special card, init it using this callback */
|
||||||
|
void (*init_card)(struct mmc_card *card);
|
||||||
|
|
||||||
|
const char *name;
|
||||||
|
u32 ocr_mask;
|
||||||
|
};
|
@ -1,17 +1,22 @@
|
|||||||
#ifndef __MACH_ATMEL_MCI_H
|
#ifndef __MMC_ATMEL_MCI_H
|
||||||
#define __MACH_ATMEL_MCI_H
|
#define __MMC_ATMEL_MCI_H
|
||||||
|
|
||||||
#include <linux/platform_data/dma-atmel.h>
|
#include <linux/platform_data/dma-atmel.h>
|
||||||
|
#include <linux/platform_data/dma-dw.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct mci_dma_data - DMA data for MCI interface
|
* struct mci_dma_data - DMA data for MCI interface
|
||||||
*/
|
*/
|
||||||
struct mci_dma_data {
|
struct mci_dma_data {
|
||||||
struct at_dma_slave sdata;
|
#ifdef CONFIG_ARM
|
||||||
|
struct at_dma_slave sdata;
|
||||||
|
#else
|
||||||
|
struct dw_dma_slave sdata;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
/* accessor macros */
|
/* accessor macros */
|
||||||
#define slave_data_ptr(s) (&(s)->sdata)
|
#define slave_data_ptr(s) (&(s)->sdata)
|
||||||
#define find_slave_dev(s) ((s)->sdata.dma_dev)
|
#define find_slave_dev(s) ((s)->sdata.dma_dev)
|
||||||
|
|
||||||
#endif /* __MACH_ATMEL_MCI_H */
|
#endif /* __MMC_ATMEL_MCI_H */
|
@ -10,32 +10,8 @@
|
|||||||
|
|
||||||
#define OMAP_MMC_MAX_SLOTS 2
|
#define OMAP_MMC_MAX_SLOTS 2
|
||||||
|
|
||||||
/*
|
|
||||||
* struct omap_mmc_dev_attr.flags possibilities
|
|
||||||
*
|
|
||||||
* OMAP_HSMMC_SUPPORTS_DUAL_VOLT: Some HSMMC controller instances can
|
|
||||||
* operate with either 1.8Vdc or 3.0Vdc card voltages; this flag
|
|
||||||
* should be set if this is the case. See for example Section 22.5.3
|
|
||||||
* "MMC/SD/SDIO1 Bus Voltage Selection" of the OMAP34xx Multimedia
|
|
||||||
* Device Silicon Revision 3.1.x Revision ZR (July 2011) (SWPU223R).
|
|
||||||
*
|
|
||||||
* OMAP_HSMMC_BROKEN_MULTIBLOCK_READ: Multiple-block read transfers
|
|
||||||
* don't work correctly on some MMC controller instances on some
|
|
||||||
* OMAP3 SoCs; this flag should be set if this is the case. See
|
|
||||||
* for example Advisory 2.1.1.128 "MMC: Multiple Block Read
|
|
||||||
* Operation Issue" in _OMAP3530/3525/3515/3503 Silicon Errata_
|
|
||||||
* Revision F (October 2010) (SPRZ278F).
|
|
||||||
*/
|
|
||||||
#define OMAP_HSMMC_SUPPORTS_DUAL_VOLT BIT(0)
|
|
||||||
#define OMAP_HSMMC_BROKEN_MULTIBLOCK_READ BIT(1)
|
|
||||||
#define OMAP_HSMMC_SWAKEUP_MISSING BIT(2)
|
|
||||||
|
|
||||||
struct mmc_card;
|
struct mmc_card;
|
||||||
|
|
||||||
struct omap_mmc_dev_attr {
|
|
||||||
u8 flags;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct omap_mmc_platform_data {
|
struct omap_mmc_platform_data {
|
||||||
/* back-link to device */
|
/* back-link to device */
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
@ -106,9 +82,6 @@ struct omap_mmc_platform_data {
|
|||||||
unsigned vcc_aux_disable_is_sleep:1;
|
unsigned vcc_aux_disable_is_sleep:1;
|
||||||
|
|
||||||
/* we can put the features above into this variable */
|
/* we can put the features above into this variable */
|
||||||
#define HSMMC_HAS_PBIAS (1 << 0)
|
|
||||||
#define HSMMC_HAS_UPDATED_RESET (1 << 1)
|
|
||||||
#define HSMMC_HAS_HSPE_SUPPORT (1 << 2)
|
|
||||||
#define MMC_OMAP7XX (1 << 3)
|
#define MMC_OMAP7XX (1 << 3)
|
||||||
#define MMC_OMAP15XX (1 << 4)
|
#define MMC_OMAP15XX (1 << 4)
|
||||||
#define MMC_OMAP16XX (1 << 5)
|
#define MMC_OMAP16XX (1 << 5)
|
||||||
|
@ -55,9 +55,4 @@ struct sdhci_pxa_platdata {
|
|||||||
unsigned int quirks2;
|
unsigned int quirks2;
|
||||||
unsigned int pm_caps;
|
unsigned int pm_caps;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sdhci_pxa {
|
|
||||||
u8 clk_enable;
|
|
||||||
u8 power_mode;
|
|
||||||
};
|
|
||||||
#endif /* _PXA_SDHCI_H_ */
|
#endif /* _PXA_SDHCI_H_ */
|
||||||
|
Loading…
Reference in New Issue
Block a user