Commit 943c2ace authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'mmc-merge-for-3.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc

Pull MMC updates from Chris Ball:
 "Core:
   - Add DT properties for card detection (broken-cd, cd-gpios,
     non-removable)
   - Don't poll non-removable devices
   - Fixup/rework eMMC sleep mode/"power off notify" feature
   - Support eMMC background operations (BKOPS).  To set the one-time
     programmable fuse that enables bkops on an eMMC that doesn't
     already have it set, you can use the "mmc bkops enable" command in:

       git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc-utils.git

  Drivers:
   - atmel-mci, dw_mmc, pxa-mci, dove, s3c, spear: Add device tree
     support
   - bfin_sdh: Add support for the controller in bf60x
   - dw_mmc: Support Samsung Exynos SoCs
   - eSDHC: Add ADMA support
   - sdhci: Support testing a cd-gpio (from slot-gpio) instead of
     presence bit
   - sdhci-pltfm: Support broken-cd DT property
   - tegra: Convert to only supporting DT (mach-tegra has gone DT-only)"

* tag 'mmc-merge-for-3.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc: (67 commits)
  mmc: core: Fixup broken suspend and eMMC4.5 power off notify
  mmc: sdhci-spear: Add clk_{un}prepare() support
  mmc: sdhci-spear: add device tree bindings
  mmc: sdhci-s3c: Add clk_(enable/disable) in runtime suspend/resume
  mmc: core: Replace MMC_CAP2_BROKEN_VOLTAGE with test for fixed regulator
  mmc: sdhci-pxav3: Use sdhci_get_of_property for parsing DT quirks
  mmc: dt: Support "broken-cd" property in sdhci-pltfm
  mmc: sdhci-s3c: fix the wrong number of max bus clocks
  mmc: sh-mmcif: avoid oops on spurious interrupts
  mmc: sh-mmcif: properly handle MMC_WRITE_MULTIPLE_BLOCK completion IRQ
  mmc: sdhci-s3c: Fix crash on module insertion for second time
  mmc: sdhci-s3c: Enable only required bus clock
  mmc: Revert "mmc: dw_mmc: Add check for IDMAC configuration"
  mmc: mxcmmc: fix bug that may block a data transfer forever
  mmc: omap_hsmmc: Pass on the suspend failure to the PM core
  mmc: atmel-mci: AP700x PDC is not connected to MCI
  mmc: atmel-mci: DMA can be used with other controllers
  mmc: mmci: use clk_prepare_enable and clk_disable_unprepare
  mmc: sdhci-s3c: Add device tree support
  mmc: dw_mmc: add support for exynos specific implementation of dw-mshc
  ...
parents 10f39f04 e6c08586
* Atmel High Speed MultiMedia Card Interface
This controller on atmel products provides an interface for MMC, SD and SDIO
types of memory cards.
This file documents differences between the core properties described
by mmc.txt and the properties used by the atmel-mci driver.
1) MCI node
Required properties:
- compatible: should be "atmel,hsmci"
- #address-cells: should be one. The cell is the slot id.
- #size-cells: should be zero.
- at least one slot node
The node contains child nodes for each slot that the platform uses
Example MCI node:
mmc0: mmc@f0008000 {
compatible = "atmel,hsmci";
reg = <0xf0008000 0x600>;
interrupts = <12 4>;
#address-cells = <1>;
#size-cells = <0>;
[ child node definitions...]
};
2) slot nodes
Required properties:
- reg: should contain the slot id.
- bus-width: number of data lines connected to the controller
Optional properties:
- cd-gpios: specify GPIOs for card detection
- cd-inverted: invert the value of external card detect gpio line
- wp-gpios: specify GPIOs for write protection
Example slot node:
slot@0 {
reg = <0>;
bus-width = <4>;
cd-gpios = <&pioD 15 0>
cd-inverted;
};
Example full MCI node:
mmc0: mmc@f0008000 {
compatible = "atmel,hsmci";
reg = <0xf0008000 0x600>;
interrupts = <12 4>;
#address-cells = <1>;
#size-cells = <0>;
slot@0 {
reg = <0>;
bus-width = <4>;
cd-gpios = <&pioD 15 0>
cd-inverted;
};
slot@1 {
reg = <1>;
bus-width = <4>;
};
};
* Samsung Exynos specific extensions to the Synopsis Designware Mobile
Storage Host Controller
The Synopsis 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 Synopsis dw mshc controller properties described
by synposis-dw-mshc.txt and the properties used by the Samsung Exynos specific
extensions to the Synopsis Designware Mobile Storage Host Controller.
Required Properties:
* compatible: should be
- "samsung,exynos4210-dw-mshc": for controllers with Samsung Exynos4210
specific extentions.
- "samsung,exynos4412-dw-mshc": for controllers with Samsung Exynos4412
specific extentions.
- "samsung,exynos5250-dw-mshc": for controllers with Samsung Exynos5250
specific extentions.
* 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
ignored for Exynos4 SoC's. The valid range of divider value is 0 to 7.
* samsung,dw-mshc-sdr-timing: Specifies the value of CIU clock phase shift value
in transmit mode and CIU clock phase shift value in receive mode for single
data rate mode operation. Refer notes below for the order of the cells and the
valid values.
* samsung,dw-mshc-ddr-timing: Specifies the value of CUI clock phase shift value
in transmit mode and CIU clock phase shift value in receive mode for double
data rate mode operation. Refer notes below for the order of the cells and the
valid values.
Notes for the sdr-timing and ddr-timing values:
The order of the cells should be
- First Cell: CIU clock phase shift value for tx mode.
- Second Cell: CIU clock phase shift value for rx mode.
Valid values for SDR and DDR CIU clock timing for Exynos5250:
- valid value for tx phase shift and rx phase shift is 0 to 7.
- when CIU clock divider value is set to 3, all possible 8 phase shift
values can be used.
- if CIU clock divider value is 0 (that is divide by 1), both tx and rx
phase shift clocks should be 0.
Required properties for a slot:
* gpios: specifies a list of gpios used for command, clock and data bus. The
first gpio is the command line and the second gpio is the clock line. The
rest of the gpios (depending on the bus-width property) are the data lines in
no particular order. The format of the gpio specifier depends on the gpio
controller.
Example:
The MSHC controller node can be split into two portions, SoC specific and
board specific portions as listed below.
dwmmc0@12200000 {
compatible = "samsung,exynos5250-dw-mshc";
reg = <0x12200000 0x1000>;
interrupts = <0 75 0>;
#address-cells = <1>;
#size-cells = <0>;
};
dwmmc0@12200000 {
num-slots = <1>;
supports-highspeed;
broken-cd;
fifo-depth = <0x80>;
card-detect-delay = <200>;
samsung,dw-mshc-ciu-div = <3>;
samsung,dw-mshc-sdr-timing = <2 3>;
samsung,dw-mshc-ddr-timing = <1 2>;
slot@0 {
reg = <0>;
bus-width = <8>;
gpios = <&gpc0 0 2 0 3>, <&gpc0 1 2 0 3>,
<&gpc1 0 2 3 3>, <&gpc1 1 2 3 3>,
<&gpc1 2 2 3 3>, <&gpc1 3 2 3 3>,
<&gpc0 3 2 3 3>, <&gpc0 4 2 3 3>,
<&gpc0 5 2 3 3>, <&gpc0 6 2 3 3>;
};
};
...@@ -9,12 +9,17 @@ Interpreted by the OF core: ...@@ -9,12 +9,17 @@ Interpreted by the OF core:
Required properties: Required properties:
- bus-width: Number of data lines, can be <1>, <4>, or <8> - bus-width: Number of data lines, can be <1>, <4>, or <8>
Card detection:
If no property below is supplied, standard SDHCI card detect is used.
Only one of the properties in this section should be supplied:
- broken-cd: There is no card detection available; polling must be used.
- cd-gpios: Specify GPIOs for card detection, see gpio binding
- non-removable: non-removable slot (like eMMC); assume always present.
Optional properties: Optional properties:
- cd-gpios: Specify GPIOs for card detection, see gpio binding
- wp-gpios: Specify GPIOs for write protection, see gpio binding - wp-gpios: Specify GPIOs for write protection, see gpio binding
- cd-inverted: when present, polarity on the cd gpio line is inverted - cd-inverted: when present, polarity on the cd gpio line is inverted
- wp-inverted: when present, polarity on the wp gpio line is inverted - wp-inverted: when present, polarity on the wp gpio line is inverted
- non-removable: non-removable slot (like eMMC)
- max-frequency: maximum operating clock frequency - max-frequency: maximum operating clock frequency
Example: Example:
......
* PXA MMC drivers
Driver bindings for the PXA MCI (MMC/SDIO) interfaces
Required properties:
- compatible: Should be "marvell,pxa-mmc".
- vmmc-supply: A regulator for VMMC
Optional properties:
- marvell,detect-delay-ms: sets the detection delay timeout in ms.
- marvell,gpio-power: GPIO spec for the card power enable pin
This file documents differences between the core properties in mmc.txt
and the properties used by the pxa-mmc driver.
Examples:
mmc0: mmc@41100000 {
compatible = "marvell,pxa-mmc";
reg = <0x41100000 0x1000>;
interrupts = <23>;
cd-gpios = <&gpio 23 0>;
wp-gpios = <&gpio 24 0>;
};
* Samsung's SDHCI Controller device tree bindings
Samsung's SDHCI controller is used as a connectivity interface with external
MMC, SD and eMMC storage mediums. This file documents differences between the
core mmc properties described by mmc.txt and the properties used by the
Samsung implmentation of the SDHCI controller.
Note: The mmc core bindings documentation states that if none of the core
card-detect bindings are used, then the standard sdhci card detect mechanism
is used. The Samsung's SDHCI controller bindings extends this as listed below.
[A] The property "samsung,cd-pinmux-gpio" can be used as stated in the
"Optional Board Specific Properties" section below.
[B] If core card-detect bindings and "samsung,cd-pinmux-gpio" property
is not specified, it is assumed that there is no card detection
mechanism used.
Required SoC Specific Properties:
- compatible: should be one of the following
- "samsung,s3c6410-sdhci": For controllers compatible with s3c6410 sdhci
controller.
- "samsung,exynos4210-sdhci": For controllers compatible with Exynos4 sdhci
controller.
Required Board Specific Properties:
- gpios: Should specify the gpios used for clock, command and data lines. The
gpio specifier format depends on the gpio controller.
Optional Board Specific Properties:
- samsung,cd-pinmux-gpio: Specifies the card detect line that is routed
through a pinmux to the card-detect pin of the card slot. This property
should be used only if none of the mmc core card-detect properties are
used.
Example:
sdhci@12530000 {
compatible = "samsung,exynos4210-sdhci";
reg = <0x12530000 0x100>;
interrupts = <0 75 0>;
bus-width = <4>;
cd-gpios = <&gpk2 2 2 3 3>;
gpios = <&gpk2 0 2 0 3>, /* clock line */
<&gpk2 1 2 0 3>, /* command line */
<&gpk2 3 2 3 3>, /* data line 0 */
<&gpk2 4 2 3 3>, /* data line 1 */
<&gpk2 5 2 3 3>, /* data line 2 */
<&gpk2 6 2 3 3>; /* data line 3 */
};
Note: This example shows both SoC specific and board specific properties
in a single device node. The properties can be actually be seperated
into SoC specific node and board specific node.
* Marvell sdhci-dove controller
This file documents differences between the core properties in mmc.txt
and the properties used by the sdhci-pxav2 and sdhci-pxav3 drivers.
- compatible: Should be "marvell,dove-sdhci".
Example:
sdio0: sdio@92000 {
compatible = "marvell,dove-sdhci";
reg = <0x92000 0x100>;
interrupts = <35>;
};
* SPEAr SDHCI Controller
This file documents differences between the core properties in mmc.txt
and the properties used by the sdhci-spear driver.
Required properties:
- compatible: "st,spear300-sdhci"
Optional properties:
- cd-gpios: card detect gpio, with zero flags.
Example:
sdhci@fc000000 {
compatible = "st,spear300-sdhci";
reg = <0xfc000000 0x1000>;
cd-gpios = <&gpio0 6 0>;
};
* Synopsis Designware Mobile Storage Host Controller
The Synopsis 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 mmc properties described by mmc.txt and the
properties used by the Synopsis Designware Mobile Storage Host Controller.
Required Properties:
* compatible: should be
- snps,dw-mshc: for controllers compliant with synopsis dw-mshc.
* #address-cells: should be 1.
* #size-cells: should be 0.
# Slots: The slot specific information are contained within child-nodes with
each child-node representing a supported slot. There should be atleast one
child node representing a card slot. The name of the child node representing
the slot is recommended to be slot@n where n is the unique number of the slot
connnected to the controller. The following are optional properties which
can be included in the slot child node.
* reg: specifies the physical slot number. The valid values of this
property is 0 to (num-slots -1), where num-slots is the value
specified by the num-slots property.
* bus-width: as documented in mmc core bindings.
* wp-gpios: specifies the write protect gpio line. The format of the
gpio specifier depends on the gpio controller. If the write-protect
line is not available, this property is optional.
Optional properties:
* num-slots: specifies the number of slots supported by the controller.
The number of physical slots actually used could be equal or less than the
value specified by num-slots. If this property is not specified, the value
of num-slot property is assumed to be 1.
* fifo-depth: The maximum size of the tx/rx fifo's. If this property is not
specified, the default value of the fifo size is determined from the
controller registers.
* card-detect-delay: Delay in milli-seconds before detecting card after card
insert event. The default value is 0.
* supports-highspeed: Enables support for high speed cards (upto 50MHz)
* broken-cd: as documented in mmc core bindings.
Aliases:
- All the MSHC controller nodes should be represented in the aliases node using
the following format 'mshc{n}' where n is a unique number for the alias.
Example:
The MSHC controller node can be split into two portions, SoC specific and
board specific portions as listed below.
dwmmc0@12200000 {
compatible = "snps,dw-mshc";
reg = <0x12200000 0x1000>;
interrupts = <0 75 0>;
#address-cells = <1>;
#size-cells = <0>;
};
dwmmc0@12200000 {
num-slots = <1>;
supports-highspeed;
broken-cd;
fifo-depth = <0x80>;
card-detect-delay = <200>;
slot@0 {
reg = <0>;
bus-width = <8>;
};
};
...@@ -1544,7 +1544,7 @@ S: Supported ...@@ -1544,7 +1544,7 @@ S: Supported
F: drivers/rtc/rtc-bfin.c F: drivers/rtc/rtc-bfin.c
BLACKFIN SDH DRIVER BLACKFIN SDH DRIVER
M: Cliff Cai <cliff.cai@analog.com> M: Sonic Zhang <sonic.zhang@analog.com>
L: uclinux-dist-devel@blackfin.uclinux.org L: uclinux-dist-devel@blackfin.uclinux.org
W: http://blackfin.uclinux.org W: http://blackfin.uclinux.org
S: Supported S: Supported
...@@ -5207,8 +5207,10 @@ S: Maintained ...@@ -5207,8 +5207,10 @@ S: Maintained
F: drivers/mmc/host/omap.c F: drivers/mmc/host/omap.c
OMAP HS MMC SUPPORT OMAP HS MMC SUPPORT
M: Venkatraman S <svenkatr@ti.com>
L: linux-mmc@vger.kernel.org
L: linux-omap@vger.kernel.org L: linux-omap@vger.kernel.org
S: Orphan S: Maintained
F: drivers/mmc/host/omap_hsmmc.c F: drivers/mmc/host/omap_hsmmc.c
OMAP RANDOM NUMBER GENERATOR SUPPORT OMAP RANDOM NUMBER GENERATOR SUPPORT
......
...@@ -80,8 +80,7 @@ gmac: eth@e0800000 { ...@@ -80,8 +80,7 @@ gmac: eth@e0800000 {
}; };
sdhci@70000000 { sdhci@70000000 {
int-gpio = <&gpio1 0 0>; cd-gpios = <&gpio1 0 0>;
power-gpio = <&gpio1 2 1>;
status = "okay"; status = "okay";
}; };
......
...@@ -103,8 +103,6 @@ gmac: eth@e0800000 { ...@@ -103,8 +103,6 @@ gmac: eth@e0800000 {
}; };
sdhci@70000000 { sdhci@70000000 {
power-gpio = <&gpio0 2 1>;
power_always_enb;
status = "okay"; status = "okay";
}; };
......
This diff is collapsed.
...@@ -281,7 +281,7 @@ static int mmc_ext_csd_open(struct inode *inode, struct file *filp) ...@@ -281,7 +281,7 @@ static int mmc_ext_csd_open(struct inode *inode, struct file *filp)
if (err) if (err)
goto out_free; goto out_free;
for (i = 511; i >= 0; i--) for (i = 0; i < 512; i++)
n += sprintf(buf + n, "%02x", ext_csd[i]); n += sprintf(buf + n, "%02x", ext_csd[i]);
n += sprintf(buf + n, "\n"); n += sprintf(buf + n, "\n");
BUG_ON(n != EXT_CSD_STR_LEN); BUG_ON(n != EXT_CSD_STR_LEN);
......
...@@ -463,6 +463,17 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) ...@@ -463,6 +463,17 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
} }
if (card->ext_csd.rev >= 5) { if (card->ext_csd.rev >= 5) {
/* check whether the eMMC card supports BKOPS */
if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) {
card->ext_csd.bkops = 1;
card->ext_csd.bkops_en = ext_csd[EXT_CSD_BKOPS_EN];
card->ext_csd.raw_bkops_status =
ext_csd[EXT_CSD_BKOPS_STATUS];
if (!card->ext_csd.bkops_en)
pr_info("%s: BKOPS_EN bit is not set\n",
mmc_hostname(card->host));
}
/* check whether the eMMC card supports HPI */ /* check whether the eMMC card supports HPI */
if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1) { if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1) {
card->ext_csd.hpi = 1; card->ext_csd.hpi = 1;
...@@ -996,7 +1007,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -996,7 +1007,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
* so check for success and update the flag * so check for success and update the flag
*/ */
if (!err) if (!err)
card->poweroff_notify_state = MMC_POWERED_ON; card->ext_csd.power_off_notification = EXT_CSD_POWER_ON;
} }
/* /*
...@@ -1262,6 +1273,35 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -1262,6 +1273,35 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
return err; return err;
} }
static int mmc_can_poweroff_notify(const struct mmc_card *card)
{
return card &&
mmc_card_mmc(card) &&
(card->ext_csd.power_off_notification == EXT_CSD_POWER_ON);
}
static int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type)
{
unsigned int timeout = card->ext_csd.generic_cmd6_time;
int err;
/* Use EXT_CSD_POWER_OFF_SHORT as default notification type. */
if (notify_type == EXT_CSD_POWER_OFF_LONG)
timeout = card->ext_csd.power_off_longtime;
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_POWER_OFF_NOTIFICATION,
notify_type, timeout);
if (err)
pr_err("%s: Power Off Notification timed out, %u\n",
mmc_hostname(card->host), timeout);
/* Disable the power off notification after the switch operation. */
card->ext_csd.power_off_notification = EXT_CSD_NO_POWER_NOTIFICATION;
return err;
}
/* /*
* Host is being removed. Free up the current card. * Host is being removed. Free up the current card.
*/ */
...@@ -1322,11 +1362,11 @@ static int mmc_suspend(struct mmc_host *host) ...@@ -1322,11 +1362,11 @@ static int mmc_suspend(struct mmc_host *host)
BUG_ON(!host->card); BUG_ON(!host->card);
mmc_claim_host(host); mmc_claim_host(host);
if (mmc_card_can_sleep(host)) { if (mmc_can_poweroff_notify(host->card))
err = mmc_poweroff_notify(host->card, EXT_CSD_POWER_OFF_SHORT);
else if (mmc_card_can_sleep(host))
err = mmc_card_sleep(host); err = mmc_card_sleep(host);
if (!err) else if (!mmc_host_is_spi(host))
mmc_card_set_sleep(host->card);
} else if (!mmc_host_is_spi(host))
err = mmc_deselect_cards(host); err = mmc_deselect_cards(host);
host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200); host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200);
mmc_release_host(host); mmc_release_host(host);
...@@ -1348,10 +1388,6 @@ static int mmc_resume(struct mmc_host *host) ...@@ -1348,10 +1388,6 @@ static int mmc_resume(struct mmc_host *host)
BUG_ON(!host->card); BUG_ON(!host->card);
mmc_claim_host(host); mmc_claim_host(host);
if (mmc_card_is_sleep(host->card)) {
err = mmc_card_awake(host);
mmc_card_clr_sleep(host->card);
} else
err = mmc_init_card(host, host->ocr, host->card); err = mmc_init_card(host, host->ocr, host->card);
mmc_release_host(host); mmc_release_host(host);
...@@ -1363,7 +1399,6 @@ static int mmc_power_restore(struct mmc_host *host) ...@@ -1363,7 +1399,6 @@ static int mmc_power_restore(struct mmc_host *host)
int ret; int ret;
host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200); host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200);
mmc_card_clr_sleep(host->card);
mmc_claim_host(host); mmc_claim_host(host);
ret = mmc_init_card(host, host->ocr, host->card); ret = mmc_init_card(host, host->ocr, host->card);
mmc_release_host(host); mmc_release_host(host);
......
...@@ -230,6 +230,10 @@ mmc_send_cxd_native(struct mmc_host *host, u32 arg, u32 *cxd, int opcode) ...@@ -230,6 +230,10 @@ mmc_send_cxd_native(struct mmc_host *host, u32 arg, u32 *cxd, int opcode)
return 0; return 0;
} }
/*
* NOTE: void *buf, caller for the buf is required to use DMA-capable
* buffer or on-stack buffer (with some overhead in callee).
*/
static int static int
mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host, mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host,
u32 opcode, void *buf, unsigned len) u32 opcode, void *buf, unsigned len)
...@@ -239,13 +243,19 @@ mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host, ...@@ -239,13 +243,19 @@ mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host,
struct mmc_data data = {0}; struct mmc_data data = {0};
struct scatterlist sg; struct scatterlist sg;
void *data_buf; void *data_buf;
int is_on_stack;
/* dma onto stack is unsafe/nonportable, but callers to this 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 ... * routine normally provide temporary on-stack buffers ...
*/ */
data_buf = kmalloc(len, GFP_KERNEL); data_buf = kmalloc(len, GFP_KERNEL);
if (data_buf == NULL) if (!data_buf)
return -ENOMEM; return -ENOMEM;
} else
data_buf = buf;
mrq.cmd = &cmd; mrq.cmd = &cmd;
mrq.data = &data; mrq.data = &data;
...@@ -280,8 +290,10 @@ mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host, ...@@ -280,8 +290,10 @@ 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); memcpy(buf, data_buf, len);
kfree(data_buf); kfree(data_buf);
}
if (cmd.error) if (cmd.error)
return cmd.error; return cmd.error;
...@@ -294,24 +306,32 @@ mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host, ...@@ -294,24 +306,32 @@ mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host,
int mmc_send_csd(struct mmc_card *card, u32 *csd) int mmc_send_csd(struct mmc_card *card, u32 *csd)
{ {
int ret, i; int ret, i;
u32 *csd_tmp;
if (!mmc_host_is_spi(card->host)) if (!mmc_host_is_spi(card->host))
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);
ret = mmc_send_cxd_data(card, card->host, MMC_SEND_CSD, csd, 16); csd_tmp = kmalloc(16, GFP_KERNEL);
if (!csd_tmp)
return -ENOMEM;
ret = mmc_send_cxd_data(card, card->host, MMC_SEND_CSD, csd_tmp, 16);
if (ret) if (ret)
return ret; goto err;
for (i = 0;i < 4;i++) for (i = 0;i < 4;i++)
csd[i] = be32_to_cpu(csd[i]); csd[i] = be32_to_cpu(csd_tmp[i]);
return 0; err:
kfree(csd_tmp);
return ret;
} }
int mmc_send_cid(struct mmc_host *host, u32 *cid) int mmc_send_cid(struct mmc_host *host, u32 *cid)
{ {
int ret, i; int ret, i;
u32 *cid_tmp;
if (!mmc_host_is_spi(host)) { if (!mmc_host_is_spi(host)) {
if (!host->card) if (!host->card)
...@@ -320,14 +340,20 @@ int mmc_send_cid(struct mmc_host *host, u32 *cid) ...@@ -320,14 +340,20 @@ int mmc_send_cid(struct mmc_host *host, u32 *cid)
cid, MMC_SEND_CID); cid, MMC_SEND_CID);
} }
ret = mmc_send_cxd_data(NULL, host, MMC_SEND_CID, cid, 16); cid_tmp = kmalloc(16, GFP_KERNEL);
if (!cid_tmp)
return -ENOMEM;
ret = mmc_send_cxd_data(NULL, host, MMC_SEND_CID, cid_tmp, 16);
if (ret) if (ret)
return ret; goto err;
for (i = 0;i < 4;i++) for (i = 0;i < 4;i++)
cid[i] = be32_to_cpu(cid[i]); cid[i] = be32_to_cpu(cid_tmp[i]);
return 0; err:
kfree(cid_tmp);
return ret;
} }
int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd) int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
...@@ -367,18 +393,19 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc) ...@@ -367,18 +393,19 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc)
} }
/** /**
* mmc_switch - modify EXT_CSD register * __mmc_switch - modify EXT_CSD register
* @card: the MMC card associated with the data transfer * @card: the MMC card associated with the data transfer
* @set: cmd set values * @set: cmd set values
* @index: EXT_CSD register index * @index: EXT_CSD register index
* @value: value to program into EXT_CSD register * @value: value to program into EXT_CSD register
* @timeout_ms: timeout (ms) for operation performed by register write, * @timeout_ms: timeout (ms) for operation performed by register write,
* timeout of zero implies maximum possible timeout * timeout of zero implies maximum possible timeout
* @use_busy_signal: use the busy signal as response type
* *
* Modifies the EXT_CSD register for selected card. * Modifies the EXT_CSD register for selected card.
*/ */
int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
unsigned int timeout_ms) unsigned int timeout_ms, bool use_busy_signal)
{ {
int err; int err;
struct mmc_command cmd = {0}; struct mmc_command cmd = {0};
...@@ -392,13 +419,23 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, ...@@ -392,13 +419,23 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
(index << 16) | (index << 16) |
(value << 8) | (value << 8) |
set; set;
cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; cmd.flags = MMC_CMD_AC;
if (use_busy_signal)
cmd.flags |= MMC_RSP_SPI_R1B | MMC_RSP_R1B;
else
cmd.flags |= MMC_RSP_SPI_R1 | MMC_RSP_R1;
cmd.cmd_timeout_ms = timeout_ms; cmd.cmd_timeout_ms = timeout_ms;
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
if (err) if (err)
return err; return err;
/* No need to check card status in case of unblocking command */
if (!use_busy_signal)
return 0;
/* Must check status to be sure of no errors */ /* Must check status to be sure of no errors */
do { do {
err = mmc_send_status(card, &status); err = mmc_send_status(card, &status);
...@@ -423,6 +460,13 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, ...@@ -423,6 +460,13 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(__mmc_switch);
int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
unsigned int timeout_ms)
{
return __mmc_switch(card, set, index, value, timeout_ms, true);
}
EXPORT_SYMBOL_GPL(mmc_switch); EXPORT_SYMBOL_GPL(mmc_switch);
int mmc_send_status(struct mmc_card *card, u32 *status) int mmc_send_status(struct mmc_card *card, u32 *status)
......
...@@ -193,14 +193,7 @@ static int sdio_bus_remove(struct device *dev) ...@@ -193,14 +193,7 @@ static int sdio_bus_remove(struct device *dev)
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int pm_no_operation(struct device *dev)
{
return 0;
}
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_no_operation, pm_no_operation)
SET_RUNTIME_PM_OPS( SET_RUNTIME_PM_OPS(
pm_generic_runtime_suspend, pm_generic_runtime_suspend,
pm_generic_runtime_resume, pm_generic_runtime_resume,
......
...@@ -100,7 +100,13 @@ int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio) ...@@ -100,7 +100,13 @@ int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio)
ctx = host->slot.handler_priv; ctx = host->slot.handler_priv;
return gpio_request_one(gpio, GPIOF_DIR_IN, ctx->ro_label); ret = gpio_request_one(gpio, GPIOF_DIR_IN, ctx->ro_label);
if (ret < 0)
return ret;
ctx->ro_gpio = gpio;
return 0;
} }
EXPORT_SYMBOL(mmc_gpio_request_ro); EXPORT_SYMBOL(mmc_gpio_request_ro);
......
...@@ -540,6 +540,15 @@ config MMC_DW_PLTFM ...@@ -540,6 +540,15 @@ config MMC_DW_PLTFM
If unsure, say Y. If unsure, say Y.
config MMC_DW_EXYNOS
tristate "Exynos specific extentions for Synopsys DW Memory Card Interface"
depends on MMC_DW
select MMC_DW_PLTFM
help
This selects support for Samsung Exynos SoC specific extensions to the
Synopsys DesignWare Memory Card Interface driver. Select this option
for platforms based on Exynos4 and Exynos5 SoC's.
config MMC_DW_PCI config MMC_DW_PCI
tristate "Synopsys Designware MCI support on PCI bus" tristate "Synopsys Designware MCI support on PCI bus"
depends on MMC_DW && PCI depends on MMC_DW && PCI
......
...@@ -39,6 +39,7 @@ obj-$(CONFIG_MMC_VIA_SDMMC) += via-sdmmc.o ...@@ -39,6 +39,7 @@ obj-$(CONFIG_MMC_VIA_SDMMC) += via-sdmmc.o
obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o
obj-$(CONFIG_MMC_DW) += dw_mmc.o obj-$(CONFIG_MMC_DW) += dw_mmc.o
obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o
obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o
obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o
obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
......
...@@ -140,6 +140,13 @@ ...@@ -140,6 +140,13 @@
#define atmci_writel(port,reg,value) \ #define atmci_writel(port,reg,value) \
__raw_writel((value), (port)->regs + reg) __raw_writel((value), (port)->regs + reg)
/* On AVR chips the Peripheral DMA Controller is not connected to MCI. */
#ifdef CONFIG_AVR32
# define ATMCI_PDC_CONNECTED 0
#else
# define ATMCI_PDC_CONNECTED 1
#endif
/* /*
* Fix sconfig's burst size according to atmel MCI. We need to convert them as: * Fix sconfig's burst size according to atmel MCI. We need to convert them as:
* 1 -> 0, 4 -> 1, 8 -> 2, 16 -> 3. * 1 -> 0, 4 -> 1, 8 -> 2, 16 -> 3.
......
...@@ -19,6 +19,9 @@ ...@@ -19,6 +19,9 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
...@@ -71,7 +74,7 @@ enum atmci_pdc_buf { ...@@ -71,7 +74,7 @@ enum atmci_pdc_buf {
}; };
struct atmel_mci_caps { struct atmel_mci_caps {
bool has_dma; bool has_dma_conf_reg;
bool has_pdc; bool has_pdc;
bool has_cfg_reg; bool has_cfg_reg;
bool has_cstor_reg; bool has_cstor_reg;
...@@ -418,7 +421,7 @@ static int atmci_regs_show(struct seq_file *s, void *v) ...@@ -418,7 +421,7 @@ static int atmci_regs_show(struct seq_file *s, void *v)
atmci_show_status_reg(s, "SR", buf[ATMCI_SR / 4]); atmci_show_status_reg(s, "SR", buf[ATMCI_SR / 4]);
atmci_show_status_reg(s, "IMR", buf[ATMCI_IMR / 4]); atmci_show_status_reg(s, "IMR", buf[ATMCI_IMR / 4]);
if (host->caps.has_dma) { if (host->caps.has_dma_conf_reg) {
u32 val; u32 val;
val = buf[ATMCI_DMA / 4]; val = buf[ATMCI_DMA / 4];
...@@ -500,6 +503,70 @@ static void atmci_init_debugfs(struct atmel_mci_slot *slot) ...@@ -500,6 +503,70 @@ static void atmci_init_debugfs(struct atmel_mci_slot *slot)
dev_err(&mmc->class_dev, "failed to initialize debugfs for slot\n"); dev_err(&mmc->class_dev, "failed to initialize debugfs for slot\n");
} }
#if defined(CONFIG_OF)
static const struct of_device_id atmci_dt_ids[] = {
{ .compatible = "atmel,hsmci" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, atmci_dt_ids);
static struct mci_platform_data __devinit*
atmci_of_init(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct device_node *cnp;
struct mci_platform_data *pdata;
u32 slot_id;
if (!np) {
dev_err(&pdev->dev, "device node not found\n");
return ERR_PTR(-EINVAL);
}
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
dev_err(&pdev->dev, "could not allocate memory for pdata\n");
return ERR_PTR(-ENOMEM);
}
for_each_child_of_node(np, cnp) {
if (of_property_read_u32(cnp, "reg", &slot_id)) {
dev_warn(&pdev->dev, "reg property is missing for %s\n",
cnp->full_name);
continue;
}
if (slot_id >= ATMCI_MAX_NR_SLOTS) {
dev_warn(&pdev->dev, "can't have more than %d slots\n",
ATMCI_MAX_NR_SLOTS);
break;
}
if (of_property_read_u32(cnp, "bus-width",
&pdata->slot[slot_id].bus_width))
pdata->slot[slot_id].bus_width = 1;
pdata->slot[slot_id].detect_pin =
of_get_named_gpio(cnp, "cd-gpios", 0);
pdata->slot[slot_id].detect_is_active_high =
of_property_read_bool(cnp, "cd-inverted");
pdata->slot[slot_id].wp_pin =
of_get_named_gpio(cnp, "wp-gpios", 0);
}
return pdata;
}
#else /* CONFIG_OF */
static inline struct mci_platform_data*
atmci_of_init(struct platform_device *dev)
{
return ERR_PTR(-EINVAL);
}
#endif
static inline unsigned int atmci_get_version(struct atmel_mci *host) static inline unsigned int atmci_get_version(struct atmel_mci *host)
{ {
return atmci_readl(host, ATMCI_VERSION) & 0x00000fff; return atmci_readl(host, ATMCI_VERSION) & 0x00000fff;
...@@ -774,7 +841,7 @@ static void atmci_dma_complete(void *arg) ...@@ -774,7 +841,7 @@ static void atmci_dma_complete(void *arg)
dev_vdbg(&host->pdev->dev, "DMA complete\n"); dev_vdbg(&host->pdev->dev, "DMA complete\n");
if (host->caps.has_dma) if (host->caps.has_dma_conf_reg)
/* Disable DMA hardware handshaking on MCI */ /* Disable DMA hardware handshaking on MCI */
atmci_writel(host, ATMCI_DMA, atmci_readl(host, ATMCI_DMA) & ~ATMCI_DMAEN); atmci_writel(host, ATMCI_DMA, atmci_readl(host, ATMCI_DMA) & ~ATMCI_DMAEN);
...@@ -961,7 +1028,9 @@ atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data) ...@@ -961,7 +1028,9 @@ atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data)
maxburst = atmci_convert_chksize(host->dma_conf.dst_maxburst); maxburst = atmci_convert_chksize(host->dma_conf.dst_maxburst);
} }
atmci_writel(host, ATMCI_DMA, ATMCI_DMA_CHKSIZE(maxburst) | ATMCI_DMAEN); if (host->caps.has_dma_conf_reg)
atmci_writel(host, ATMCI_DMA, ATMCI_DMA_CHKSIZE(maxburst) |
ATMCI_DMAEN);
sglen = dma_map_sg(chan->device->dev, data->sg, sglen = dma_map_sg(chan->device->dev, data->sg,
data->sg_len, direction); data->sg_len, direction);
...@@ -2046,6 +2115,13 @@ static int __init atmci_init_slot(struct atmel_mci *host, ...@@ -2046,6 +2115,13 @@ static int __init atmci_init_slot(struct atmel_mci *host,
slot->sdc_reg = sdc_reg; slot->sdc_reg = sdc_reg;
slot->sdio_irq = sdio_irq; slot->sdio_irq = sdio_irq;
dev_dbg(&mmc->class_dev,
"slot[%u]: bus_width=%u, detect_pin=%d, "
"detect_is_active_high=%s, wp_pin=%d\n",
id, slot_data->bus_width, slot_data->detect_pin,
slot_data->detect_is_active_high ? "true" : "false",
slot_data->wp_pin);
mmc->ops = &atmci_ops; mmc->ops = &atmci_ops;
mmc->f_min = DIV_ROUND_UP(host->bus_hz, 512); mmc->f_min = DIV_ROUND_UP(host->bus_hz, 512);
mmc->f_max = host->bus_hz / 2; mmc->f_max = host->bus_hz / 2;
...@@ -2169,7 +2245,10 @@ static bool atmci_configure_dma(struct atmel_mci *host) ...@@ -2169,7 +2245,10 @@ static bool atmci_configure_dma(struct atmel_mci *host)
pdata = host->pdev->dev.platform_data; pdata = host->pdev->dev.platform_data;
if (pdata && find_slave_dev(pdata->dma_slave)) { if (!pdata)
return false;
if (pdata->dma_slave && find_slave_dev(pdata->dma_slave)) {
dma_cap_mask_t mask; dma_cap_mask_t mask;
/* Try to grab a DMA channel */ /* Try to grab a DMA channel */
...@@ -2210,8 +2289,8 @@ static void __init atmci_get_cap(struct atmel_mci *host) ...@@ -2210,8 +2289,8 @@ static void __init atmci_get_cap(struct atmel_mci *host)
dev_info(&host->pdev->dev, dev_info(&host->pdev->dev,
"version: 0x%x\n", version); "version: 0x%x\n", version);
host->caps.has_dma = 0; host->caps.has_dma_conf_reg = 0;
host->caps.has_pdc = 1; host->caps.has_pdc = ATMCI_PDC_CONNECTED;
host->caps.has_cfg_reg = 0; host->caps.has_cfg_reg = 0;
host->caps.has_cstor_reg = 0; host->caps.has_cstor_reg = 0;
host->caps.has_highspeed = 0; host->caps.has_highspeed = 0;
...@@ -2228,12 +2307,7 @@ static void __init atmci_get_cap(struct atmel_mci *host) ...@@ -2228,12 +2307,7 @@ static void __init atmci_get_cap(struct atmel_mci *host)
host->caps.has_odd_clk_div = 1; host->caps.has_odd_clk_div = 1;
case 0x400: case 0x400:
case 0x300: case 0x300:
#ifdef CONFIG_AT_HDMAC host->caps.has_dma_conf_reg = 1;
host->caps.has_dma = 1;
#else
dev_info(&host->pdev->dev,
"has dma capability but dma engine is not selected, then use pio\n");
#endif
host->caps.has_pdc = 0; host->caps.has_pdc = 0;
host->caps.has_cfg_reg = 1; host->caps.has_cfg_reg = 1;
host->caps.has_cstor_reg = 1; host->caps.has_cstor_reg = 1;
...@@ -2268,8 +2342,14 @@ static int __init atmci_probe(struct platform_device *pdev) ...@@ -2268,8 +2342,14 @@ static int __init atmci_probe(struct platform_device *pdev)
if (!regs) if (!regs)
return -ENXIO; return -ENXIO;
pdata = pdev->dev.platform_data; pdata = pdev->dev.platform_data;
if (!pdata) if (!pdata) {
return -ENXIO; pdata = atmci_of_init(pdev);
if (IS_ERR(pdata)) {
dev_err(&pdev->dev, "platform data not available\n");
return PTR_ERR(pdata);
}
}
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
if (irq < 0) if (irq < 0)
return irq; return irq;
...@@ -2308,7 +2388,7 @@ static int __init atmci_probe(struct platform_device *pdev) ...@@ -2308,7 +2388,7 @@ static int __init atmci_probe(struct platform_device *pdev)
/* 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 (host->caps.has_dma && atmci_configure_dma(host)) { if (atmci_configure_dma(host)) {
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;
...@@ -2487,6 +2567,7 @@ static struct platform_driver atmci_driver = { ...@@ -2487,6 +2567,7 @@ static struct platform_driver atmci_driver = {
.driver = { .driver = {
.name = "atmel_mci", .name = "atmel_mci",
.pm = ATMCI_PM_OPS, .pm = ATMCI_PM_OPS,
.of_match_table = of_match_ptr(atmci_dt_ids),
}, },
}; };
......
This diff is collapsed.
...@@ -30,11 +30,12 @@ ...@@ -30,11 +30,12 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/edma.h>
#include <linux/mmc/mmc.h> #include <linux/mmc/mmc.h>
#include <linux/platform_data/mmc-davinci.h> #include <linux/platform_data/mmc-davinci.h>
#include <mach/edma.h>
/* /*
* Register Definitions * Register Definitions
...@@ -200,21 +201,13 @@ struct mmc_davinci_host { ...@@ -200,21 +201,13 @@ struct mmc_davinci_host {
u32 bytes_left; u32 bytes_left;
u32 rxdma, txdma; u32 rxdma, txdma;
struct dma_chan *dma_tx;
struct dma_chan *dma_rx;
bool use_dma; bool use_dma;
bool do_dma; bool do_dma;
bool sdio_int; bool sdio_int;
bool active_request; bool active_request;
/* Scatterlist DMA uses one or more parameter RAM entries:
* the main one (associated with rxdma or txdma) plus zero or
* more links. The entries for a given transfer differ only
* by memory buffer (address, length) and link field.
*/
struct edmacc_param tx_template;
struct edmacc_param rx_template;
unsigned n_link;
u32 links[MAX_NR_SG - 1];
/* For PIO we walk scatterlists one segment at a time. */ /* For PIO we walk scatterlists one segment at a time. */
unsigned int sg_len; unsigned int sg_len;
struct scatterlist *sg; struct scatterlist *sg;
...@@ -410,153 +403,74 @@ static void mmc_davinci_start_command(struct mmc_davinci_host *host, ...@@ -410,153 +403,74 @@ static void mmc_davinci_start_command(struct mmc_davinci_host *host,
static void davinci_abort_dma(struct mmc_davinci_host *host) static void davinci_abort_dma(struct mmc_davinci_host *host)
{ {
int sync_dev; struct dma_chan *sync_dev;
if (host->data_dir == DAVINCI_MMC_DATADIR_READ) if (host->data_dir == DAVINCI_MMC_DATADIR_READ)
sync_dev = host->rxdma; sync_dev = host->dma_rx;
else else
sync_dev = host->txdma; sync_dev = host->dma_tx;
edma_stop(sync_dev);
edma_clean_channel(sync_dev);
}
static void
mmc_davinci_xfer_done(struct mmc_davinci_host *host, struct mmc_data *data);
static void mmc_davinci_dma_cb(unsigned channel, u16 ch_status, void *data)
{
if (DMA_COMPLETE != ch_status) {
struct mmc_davinci_host *host = data;
/* Currently means: DMA Event Missed, or "null" transfer
* request was seen. In the future, TC errors (like bad
* addresses) might be presented too.
*/
dev_warn(mmc_dev(host->mmc), "DMA %s error\n",
(host->data->flags & MMC_DATA_WRITE)
? "write" : "read");
host->data->error = -EIO;
mmc_davinci_xfer_done(host, host->data);
}
}
/* Set up tx or rx template, to be modified and updated later */
static void __init mmc_davinci_dma_setup(struct mmc_davinci_host *host,
bool tx, struct edmacc_param *template)
{
unsigned sync_dev;
const u16 acnt = 4;
const u16 bcnt = rw_threshold >> 2;
const u16 ccnt = 0;
u32 src_port = 0;
u32 dst_port = 0;
s16 src_bidx, dst_bidx;
s16 src_cidx, dst_cidx;
/*
* A-B Sync transfer: each DMA request is for one "frame" of
* rw_threshold bytes, broken into "acnt"-size chunks repeated
* "bcnt" times. Each segment needs "ccnt" such frames; since
* we tell the block layer our mmc->max_seg_size limit, we can
* trust (later) that it's within bounds.
*
* The FIFOs are read/written in 4-byte chunks (acnt == 4) and
* EDMA will optimize memory operations to use larger bursts.
*/
if (tx) {
sync_dev = host->txdma;
/* src_prt, ccnt, and link to be set up later */
src_bidx = acnt;
src_cidx = acnt * bcnt;
dst_port = host->mem_res->start + DAVINCI_MMCDXR;
dst_bidx = 0;
dst_cidx = 0;
} else {
sync_dev = host->rxdma;
src_port = host->mem_res->start + DAVINCI_MMCDRR;
src_bidx = 0;
src_cidx = 0;
/* dst_prt, ccnt, and link to be set up later */
dst_bidx = acnt;
dst_cidx = acnt * bcnt;
}
/*
* We can't use FIFO mode for the FIFOs because MMC FIFO addresses
* are not 256-bit (32-byte) aligned. So we use INCR, and the W8BIT
* parameter is ignored.
*/
edma_set_src(sync_dev, src_port, INCR, W8BIT);
edma_set_dest(sync_dev, dst_port, INCR, W8BIT);
edma_set_src_index(sync_dev, src_bidx, src_cidx);
edma_set_dest_index(sync_dev, dst_bidx, dst_cidx);
edma_set_transfer_params(sync_dev, acnt, bcnt, ccnt, 8, ABSYNC);
edma_read_slot(sync_dev, template);
/* don't bother with irqs or chaining */ dmaengine_terminate_all(sync_dev);
template->opt |= EDMA_CHAN_SLOT(sync_dev) << 12;
} }
static void mmc_davinci_send_dma_request(struct mmc_davinci_host *host, static int mmc_davinci_send_dma_request(struct mmc_davinci_host *host,
struct mmc_data *data) struct mmc_data *data)
{ {
struct edmacc_param *template; struct dma_chan *chan;
int channel, slot; struct dma_async_tx_descriptor *desc;
unsigned link; int ret = 0;
struct scatterlist *sg;
unsigned sg_len;
unsigned bytes_left = host->bytes_left;
const unsigned shift = ffs(rw_threshold) - 1;
if (host->data_dir == DAVINCI_MMC_DATADIR_WRITE) { if (host->data_dir == DAVINCI_MMC_DATADIR_WRITE) {
template = &host->tx_template; struct dma_slave_config dma_tx_conf = {
channel = host->txdma; .direction = DMA_MEM_TO_DEV,
.dst_addr = host->mem_res->start + DAVINCI_MMCDXR,
.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
.dst_maxburst =
rw_threshold / DMA_SLAVE_BUSWIDTH_4_BYTES,
};
chan = host->dma_tx;
dmaengine_slave_config(host->dma_tx, &dma_tx_conf);
desc = dmaengine_prep_slave_sg(host->dma_tx,
data->sg,
host->sg_len,
DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
dev_dbg(mmc_dev(host->mmc),
"failed to allocate DMA TX descriptor");
ret = -1;
goto out;
}
} else { } else {
template = &host->rx_template; struct dma_slave_config dma_rx_conf = {
channel = host->rxdma; .direction = DMA_DEV_TO_MEM,
.src_addr = host->mem_res->start + DAVINCI_MMCDRR,
.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
.src_maxburst =
rw_threshold / DMA_SLAVE_BUSWIDTH_4_BYTES,
};
chan = host->dma_rx;
dmaengine_slave_config(host->dma_rx, &dma_rx_conf);
desc = dmaengine_prep_slave_sg(host->dma_rx,
data->sg,
host->sg_len,
DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
dev_dbg(mmc_dev(host->mmc),
"failed to allocate DMA RX descriptor");
ret = -1;
goto out;
} }
/* We know sg_len and ccnt will never be out of range because
* we told the mmc layer which in turn tells the block layer
* to ensure that it only hands us one scatterlist segment
* per EDMA PARAM entry. Update the PARAM
* entries needed for each segment of this scatterlist.
*/
for (slot = channel, link = 0, sg = data->sg, sg_len = host->sg_len;
sg_len-- != 0 && bytes_left;
sg = sg_next(sg), slot = host->links[link++]) {
u32 buf = sg_dma_address(sg);
unsigned count = sg_dma_len(sg);
template->link_bcntrld = sg_len
? (EDMA_CHAN_SLOT(host->links[link]) << 5)
: 0xffff;
if (count > bytes_left)
count = bytes_left;
bytes_left -= count;
if (host->data_dir == DAVINCI_MMC_DATADIR_WRITE)
template->src = buf;
else
template->dst = buf;
template->ccnt = count >> shift;
edma_write_slot(slot, template);
} }
if (host->version == MMC_CTLR_VERSION_2) dmaengine_submit(desc);
edma_clear_event(channel); dma_async_issue_pending(chan);
edma_start(channel); out:
return ret;
} }
static int mmc_davinci_start_dma_transfer(struct mmc_davinci_host *host, static int mmc_davinci_start_dma_transfer(struct mmc_davinci_host *host,
...@@ -564,6 +478,7 @@ static int mmc_davinci_start_dma_transfer(struct mmc_davinci_host *host, ...@@ -564,6 +478,7 @@ static int mmc_davinci_start_dma_transfer(struct mmc_davinci_host *host,
{ {
int i; int i;
int mask = rw_threshold - 1; int mask = rw_threshold - 1;
int ret = 0;
host->sg_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, host->sg_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
((data->flags & MMC_DATA_WRITE) ((data->flags & MMC_DATA_WRITE)
...@@ -583,70 +498,48 @@ static int mmc_davinci_start_dma_transfer(struct mmc_davinci_host *host, ...@@ -583,70 +498,48 @@ static int mmc_davinci_start_dma_transfer(struct mmc_davinci_host *host,
} }
host->do_dma = 1; host->do_dma = 1;
mmc_davinci_send_dma_request(host, data); ret = mmc_davinci_send_dma_request(host, data);
return 0; return ret;
} }
static void __init_or_module static void __init_or_module
davinci_release_dma_channels(struct mmc_davinci_host *host) davinci_release_dma_channels(struct mmc_davinci_host *host)
{ {
unsigned i;
if (!host->use_dma) if (!host->use_dma)
return; return;
for (i = 0; i < host->n_link; i++) dma_release_channel(host->dma_tx);
edma_free_slot(host->links[i]); dma_release_channel(host->dma_rx);
edma_free_channel(host->txdma);
edma_free_channel(host->rxdma);
} }
static int __init davinci_acquire_dma_channels(struct mmc_davinci_host *host) static int __init davinci_acquire_dma_channels(struct mmc_davinci_host *host)
{ {
u32 link_size; int r;
int r, i; dma_cap_mask_t mask;
/* Acquire master DMA write channel */
r = edma_alloc_channel(host->txdma, mmc_davinci_dma_cb, host,
EVENTQ_DEFAULT);
if (r < 0) {
dev_warn(mmc_dev(host->mmc), "alloc %s channel err %d\n",
"tx", r);
return r;
}
mmc_davinci_dma_setup(host, true, &host->tx_template);
/* Acquire master DMA read channel */
r = edma_alloc_channel(host->rxdma, mmc_davinci_dma_cb, host,
EVENTQ_DEFAULT);
if (r < 0) {
dev_warn(mmc_dev(host->mmc), "alloc %s channel err %d\n",
"rx", r);
goto free_master_write;
}
mmc_davinci_dma_setup(host, false, &host->rx_template);
/* Allocate parameter RAM slots, which will later be bound to a dma_cap_zero(mask);
* channel as needed to handle a scatterlist. dma_cap_set(DMA_SLAVE, mask);
*/
link_size = min_t(unsigned, host->nr_sg, ARRAY_SIZE(host->links)); host->dma_tx =
for (i = 0; i < link_size; i++) { dma_request_channel(mask, edma_filter_fn, &host->txdma);
r = edma_alloc_slot(EDMA_CTLR(host->txdma), EDMA_SLOT_ANY); if (!host->dma_tx) {
if (r < 0) { dev_err(mmc_dev(host->mmc), "Can't get dma_tx channel\n");
dev_dbg(mmc_dev(host->mmc), "dma PaRAM alloc --> %d\n", return -ENODEV;
r);
break;
} }
host->links[i] = r;
host->dma_rx =
dma_request_channel(mask, edma_filter_fn, &host->rxdma);
if (!host->dma_rx) {
dev_err(mmc_dev(host->mmc), "Can't get dma_rx channel\n");
r = -ENODEV;
goto free_master_write;
} }
host->n_link = i;
return 0; return 0;
free_master_write: free_master_write:
edma_free_channel(host->txdma); dma_release_channel(host->dma_tx);
return r; return r;
} }
...@@ -1359,7 +1252,7 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev) ...@@ -1359,7 +1252,7 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev)
* Each hw_seg uses one EDMA parameter RAM slot, always one * Each hw_seg uses one EDMA parameter RAM slot, always one
* channel and then usually some linked slots. * channel and then usually some linked slots.
*/ */
mmc->max_segs = 1 + host->n_link; mmc->max_segs = MAX_NR_SG;
/* EDMA limit per hw segment (one or two MBytes) */ /* EDMA limit per hw segment (one or two MBytes) */
mmc->max_seg_size = MAX_CCNT * rw_threshold; mmc->max_seg_size = MAX_CCNT * rw_threshold;
......
/*
* Exynos Specific Extensions for Synopsys DW Multimedia Card Interface driver
*
* Copyright (C) 2012, Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/mmc/host.h>
#include <linux/mmc/dw_mmc.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include "dw_mmc.h"
#include "dw_mmc-pltfm.h"
#define NUM_PINS(x) (x + 2)
#define SDMMC_CLKSEL 0x09C
#define SDMMC_CLKSEL_CCLK_SAMPLE(x) (((x) & 7) << 0)
#define SDMMC_CLKSEL_CCLK_DRIVE(x) (((x) & 7) << 16)
#define SDMMC_CLKSEL_CCLK_DIVIDER(x) (((x) & 7) << 24)
#define SDMMC_CLKSEL_GET_DRV_WD3(x) (((x) >> 16) & 0x7)
#define SDMMC_CLKSEL_TIMING(x, y, z) (SDMMC_CLKSEL_CCLK_SAMPLE(x) | \
SDMMC_CLKSEL_CCLK_DRIVE(y) | \
SDMMC_CLKSEL_CCLK_DIVIDER(z))
#define SDMMC_CMD_USE_HOLD_REG BIT(29)
#define EXYNOS4210_FIXED_CIU_CLK_DIV 2
#define EXYNOS4412_FIXED_CIU_CLK_DIV 4
/* Variations in Exynos specific dw-mshc controller */
enum dw_mci_exynos_type {
DW_MCI_TYPE_EXYNOS4210,
DW_MCI_TYPE_EXYNOS4412,
DW_MCI_TYPE_EXYNOS5250,
};
/* Exynos implementation specific driver private data */
struct dw_mci_exynos_priv_data {
enum dw_mci_exynos_type ctrl_type;
u8 ciu_div;
u32 sdr_timing;
u32 ddr_timing;
};
static struct dw_mci_exynos_compatible {
char *compatible;
enum dw_mci_exynos_type ctrl_type;
} exynos_compat[] = {
{
.compatible = "samsung,exynos4210-dw-mshc",
.ctrl_type = DW_MCI_TYPE_EXYNOS4210,
}, {
.compatible = "samsung,exynos4412-dw-mshc",
.ctrl_type = DW_MCI_TYPE_EXYNOS4412,
}, {
.compatible = "samsung,exynos5250-dw-mshc",
.ctrl_type = DW_MCI_TYPE_EXYNOS5250,
},
};
static int dw_mci_exynos_priv_init(struct dw_mci *host)
{
struct dw_mci_exynos_priv_data *priv;
int idx;
priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
if (!priv) {
dev_err(host->dev, "mem alloc failed for private data\n");
return -ENOMEM;
}
for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) {
if (of_device_is_compatible(host->dev->of_node,
exynos_compat[idx].compatible))
priv->ctrl_type = exynos_compat[idx].ctrl_type;
}
host->priv = priv;
return 0;
}
static int dw_mci_exynos_setup_clock(struct dw_mci *host)
{
struct dw_mci_exynos_priv_data *priv = host->priv;
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5250)
host->bus_hz /= (priv->ciu_div + 1);
else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412)
host->bus_hz /= EXYNOS4412_FIXED_CIU_CLK_DIV;
else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210)
host->bus_hz /= EXYNOS4210_FIXED_CIU_CLK_DIV;
return 0;
}
static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr)
{
/*
* Exynos4412 and Exynos5250 extends the use of CMD register with the
* use of bit 29 (which is reserved on standard MSHC controllers) for
* optionally bypassing the HOLD register for command and data. The
* HOLD register should be bypassed in case there is no phase shift
* applied on CMD/DATA that is sent to the card.
*/
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)
{
struct dw_mci_exynos_priv_data *priv = host->priv;
if (ios->timing == MMC_TIMING_UHS_DDR50)
mci_writel(host, CLKSEL, priv->ddr_timing);
else
mci_writel(host, CLKSEL, priv->sdr_timing);
}
static int dw_mci_exynos_parse_dt(struct dw_mci *host)
{
struct dw_mci_exynos_priv_data *priv = host->priv;
struct device_node *np = host->dev->of_node;
u32 timing[2];
u32 div = 0;
int ret;
of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div);
priv->ciu_div = div;
ret = of_property_read_u32_array(np,
"samsung,dw-mshc-sdr-timing", timing, 2);
if (ret)
return ret;
priv->sdr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
ret = of_property_read_u32_array(np,
"samsung,dw-mshc-ddr-timing", timing, 2);
if (ret)
return ret;
priv->ddr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
return 0;
}
static int dw_mci_exynos_setup_bus(struct dw_mci *host,
struct device_node *slot_np, u8 bus_width)
{
int idx, gpio, ret;
if (!slot_np)
return -EINVAL;
/* cmd + clock + bus-width pins */
for (idx = 0; idx < NUM_PINS(bus_width); idx++) {
gpio = of_get_gpio(slot_np, idx);
if (!gpio_is_valid(gpio)) {
dev_err(host->dev, "invalid gpio: %d\n", gpio);
return -EINVAL;
}
ret = devm_gpio_request(host->dev, gpio, "dw-mci-bus");
if (ret) {
dev_err(host->dev, "gpio [%d] request failed\n", gpio);
return -EBUSY;
}
}
gpio = of_get_named_gpio(slot_np, "wp-gpios", 0);
if (gpio_is_valid(gpio)) {
if (devm_gpio_request(host->dev, gpio, "dw-mci-wp"))
dev_info(host->dev, "gpio [%d] request failed\n",
gpio);
} else {
dev_info(host->dev, "wp gpio not available");
host->pdata->quirks |= DW_MCI_QUIRK_NO_WRITE_PROTECT;
}
if (host->pdata->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION)
return 0;
gpio = of_get_named_gpio(slot_np, "samsung,cd-pinmux-gpio", 0);
if (gpio_is_valid(gpio)) {
if (devm_gpio_request(host->dev, gpio, "dw-mci-cd"))
dev_err(host->dev, "gpio [%d] request failed\n", gpio);
} else {
dev_info(host->dev, "cd gpio not available");
}
return 0;
}
/* Exynos5250 controller specific capabilities */
static unsigned long exynos5250_dwmmc_caps[4] = {
MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR |
MMC_CAP_8_BIT_DATA | MMC_CAP_CMD23,
MMC_CAP_CMD23,
MMC_CAP_CMD23,
MMC_CAP_CMD23,
};
static struct dw_mci_drv_data exynos5250_drv_data = {
.caps = exynos5250_dwmmc_caps,
.init = dw_mci_exynos_priv_init,
.setup_clock = dw_mci_exynos_setup_clock,
.prepare_command = dw_mci_exynos_prepare_command,
.set_ios = dw_mci_exynos_set_ios,
.parse_dt = dw_mci_exynos_parse_dt,
.setup_bus = dw_mci_exynos_setup_bus,
};
static const struct of_device_id dw_mci_exynos_match[] = {
{ .compatible = "samsung,exynos5250-dw-mshc",
.data = (void *)&exynos5250_drv_data, },
{},
};
MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match);
int dw_mci_exynos_probe(struct platform_device *pdev)
{
struct dw_mci_drv_data *drv_data;
const struct of_device_id *match;
match = of_match_node(dw_mci_exynos_match, pdev->dev.of_node);
drv_data = match->data;
return dw_mci_pltfm_register(pdev, drv_data);
}
static struct platform_driver dw_mci_exynos_pltfm_driver = {
.probe = dw_mci_exynos_probe,
.remove = __exit_p(dw_mci_pltfm_remove),
.driver = {
.name = "dwmmc_exynos",
.of_match_table = of_match_ptr(dw_mci_exynos_match),
.pm = &dw_mci_pltfm_pmops,
},
};
module_platform_driver(dw_mci_exynos_pltfm_driver);
MODULE_DESCRIPTION("Samsung Specific DW-MSHC Driver Extension");
MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:dwmmc-exynos");
...@@ -59,7 +59,7 @@ static int __devinit dw_mci_pci_probe(struct pci_dev *pdev, ...@@ -59,7 +59,7 @@ static int __devinit dw_mci_pci_probe(struct pci_dev *pdev,
host->irq = pdev->irq; host->irq = pdev->irq;
host->irq_flags = IRQF_SHARED; host->irq_flags = IRQF_SHARED;
host->dev = pdev->dev; host->dev = &pdev->dev;
host->pdata = &pci_board_data; host->pdata = &pci_board_data;
host->regs = pci_iomap(pdev, PCI_BAR_NO, COMPLETE_BAR); host->regs = pci_iomap(pdev, PCI_BAR_NO, COMPLETE_BAR);
...@@ -140,18 +140,7 @@ static struct pci_driver dw_mci_pci_driver = { ...@@ -140,18 +140,7 @@ static struct pci_driver dw_mci_pci_driver = {
}, },
}; };
static int __init dw_mci_init(void) module_pci_driver(dw_mci_pci_driver);
{
return pci_register_driver(&dw_mci_pci_driver);
}
static void __exit dw_mci_exit(void)
{
pci_unregister_driver(&dw_mci_pci_driver);
}
module_init(dw_mci_init);
module_exit(dw_mci_exit);
MODULE_DESCRIPTION("DW Multimedia Card PCI Interface driver"); MODULE_DESCRIPTION("DW Multimedia Card PCI Interface driver");
MODULE_AUTHOR("Shashidhar Hiremath <shashidharh@vayavyalabs.com>"); MODULE_AUTHOR("Shashidhar Hiremath <shashidharh@vayavyalabs.com>");
......
...@@ -19,59 +19,63 @@ ...@@ -19,59 +19,63 @@
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/mmc/mmc.h> #include <linux/mmc/mmc.h>
#include <linux/mmc/dw_mmc.h> #include <linux/mmc/dw_mmc.h>
#include <linux/of.h>
#include "dw_mmc.h" #include "dw_mmc.h"
static int dw_mci_pltfm_probe(struct platform_device *pdev) int dw_mci_pltfm_register(struct platform_device *pdev,
struct dw_mci_drv_data *drv_data)
{ {
struct dw_mci *host; struct dw_mci *host;
struct resource *regs; struct resource *regs;
int ret; int ret;
host = kzalloc(sizeof(struct dw_mci), GFP_KERNEL); host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL);
if (!host) if (!host)
return -ENOMEM; return -ENOMEM;
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!regs) { if (!regs)
ret = -ENXIO; return -ENXIO;
goto err_free;
}
host->irq = platform_get_irq(pdev, 0); host->irq = platform_get_irq(pdev, 0);
if (host->irq < 0) { if (host->irq < 0)
ret = host->irq; return host->irq;
goto err_free;
}
host->dev = pdev->dev; host->drv_data = drv_data;
host->dev = &pdev->dev;
host->irq_flags = 0; host->irq_flags = 0;
host->pdata = pdev->dev.platform_data; host->pdata = pdev->dev.platform_data;
ret = -ENOMEM; host->regs = devm_request_and_ioremap(&pdev->dev, regs);
host->regs = ioremap(regs->start, resource_size(regs));
if (!host->regs) if (!host->regs)
goto err_free; return -ENOMEM;
platform_set_drvdata(pdev, host);
ret = dw_mci_probe(host); if (host->drv_data->init) {
ret = host->drv_data->init(host);
if (ret) if (ret)
goto err_out;
return ret; return ret;
err_out: }
iounmap(host->regs);
err_free: platform_set_drvdata(pdev, host);
kfree(host); ret = dw_mci_probe(host);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(dw_mci_pltfm_register);
static int __devinit dw_mci_pltfm_probe(struct platform_device *pdev)
{
return dw_mci_pltfm_register(pdev, NULL);
}
static int __exit dw_mci_pltfm_remove(struct platform_device *pdev) static int __devexit dw_mci_pltfm_remove(struct platform_device *pdev)
{ {
struct dw_mci *host = platform_get_drvdata(pdev); struct dw_mci *host = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
dw_mci_remove(host); dw_mci_remove(host);
iounmap(host->regs);
kfree(host);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(dw_mci_pltfm_remove);
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
/* /*
...@@ -105,12 +109,20 @@ static int dw_mci_pltfm_resume(struct device *dev) ...@@ -105,12 +109,20 @@ static int dw_mci_pltfm_resume(struct device *dev)
#define dw_mci_pltfm_resume NULL #define dw_mci_pltfm_resume NULL
#endif /* CONFIG_PM_SLEEP */ #endif /* CONFIG_PM_SLEEP */
static SIMPLE_DEV_PM_OPS(dw_mci_pltfm_pmops, dw_mci_pltfm_suspend, dw_mci_pltfm_resume); SIMPLE_DEV_PM_OPS(dw_mci_pltfm_pmops, dw_mci_pltfm_suspend, dw_mci_pltfm_resume);
EXPORT_SYMBOL_GPL(dw_mci_pltfm_pmops);
static const struct of_device_id dw_mci_pltfm_match[] = {
{ .compatible = "snps,dw-mshc", },
{},
};
MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match);
static struct platform_driver dw_mci_pltfm_driver = { static struct platform_driver dw_mci_pltfm_driver = {
.remove = __exit_p(dw_mci_pltfm_remove), .remove = __exit_p(dw_mci_pltfm_remove),
.driver = { .driver = {
.name = "dw_mmc", .name = "dw_mmc",
.of_match_table = of_match_ptr(dw_mci_pltfm_match),
.pm = &dw_mci_pltfm_pmops, .pm = &dw_mci_pltfm_pmops,
}, },
}; };
......
/*
* Synopsys DesignWare Multimedia Card Interface Platform driver
*
* Copyright (C) 2012, Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef _DW_MMC_PLTFM_H_
#define _DW_MMC_PLTFM_H_
extern int dw_mci_pltfm_register(struct platform_device *pdev,
struct dw_mci_drv_data *drv_data);
extern int __devexit dw_mci_pltfm_remove(struct platform_device *pdev);
extern const struct dev_pm_ops dw_mci_pltfm_pmops;
#endif /* _DW_MMC_PLTFM_H_ */
This diff is collapsed.
...@@ -182,4 +182,28 @@ extern int dw_mci_suspend(struct dw_mci *host); ...@@ -182,4 +182,28 @@ extern int dw_mci_suspend(struct dw_mci *host);
extern int dw_mci_resume(struct dw_mci *host); extern int dw_mci_resume(struct dw_mci *host);
#endif #endif
/**
* dw_mci driver data - dw-mshc implementation specific driver data.
* @caps: mmc subsystem specified capabilities of the controller(s).
* @init: early implementation specific initialization.
* @setup_clock: implementation specific clock configuration.
* @prepare_command: handle CMD register extensions.
* @set_ios: handle bus specific extensions.
* @parse_dt: parse implementation specific device tree properties.
* @setup_bus: initialize io-interface
*
* Provide controller implementation specific extensions. The usage of this
* data structure is fully optional and usage of each member in this structure
* is optional as well.
*/
struct dw_mci_drv_data {
unsigned long *caps;
int (*init)(struct dw_mci *host);
int (*setup_clock)(struct dw_mci *host);
void (*prepare_command)(struct dw_mci *host, u32 *cmdr);
void (*set_ios)(struct dw_mci *host, struct mmc_ios *ios);
int (*parse_dt)(struct dw_mci *host);
int (*setup_bus)(struct dw_mci *host,
struct device_node *slot_np, u8 bus_width);
};
#endif /* _DW_MMC_H_ */ #endif /* _DW_MMC_H_ */
...@@ -1532,20 +1532,7 @@ static struct spi_driver mmc_spi_driver = { ...@@ -1532,20 +1532,7 @@ static struct spi_driver mmc_spi_driver = {
.remove = __devexit_p(mmc_spi_remove), .remove = __devexit_p(mmc_spi_remove),
}; };
module_spi_driver(mmc_spi_driver);
static int __init mmc_spi_init(void)
{
return spi_register_driver(&mmc_spi_driver);
}
module_init(mmc_spi_init);
static void __exit mmc_spi_exit(void)
{
spi_unregister_driver(&mmc_spi_driver);
}
module_exit(mmc_spi_exit);
MODULE_AUTHOR("Mike Lavender, David Brownell, " MODULE_AUTHOR("Mike Lavender, David Brownell, "
"Hans-Peter Nilsson, Jan Nikitenko"); "Hans-Peter Nilsson, Jan Nikitenko");
......
...@@ -1309,14 +1309,10 @@ static int __devinit mmci_probe(struct amba_device *dev, ...@@ -1309,14 +1309,10 @@ static int __devinit mmci_probe(struct amba_device *dev,
goto host_free; goto host_free;
} }
ret = clk_prepare(host->clk); ret = clk_prepare_enable(host->clk);
if (ret) if (ret)
goto clk_free; goto clk_free;
ret = clk_enable(host->clk);
if (ret)
goto clk_unprep;
host->plat = plat; host->plat = plat;
host->variant = variant; host->variant = variant;
host->mclk = clk_get_rate(host->clk); host->mclk = clk_get_rate(host->clk);
...@@ -1515,9 +1511,7 @@ static int __devinit mmci_probe(struct amba_device *dev, ...@@ -1515,9 +1511,7 @@ static int __devinit mmci_probe(struct amba_device *dev,
err_gpio_cd: err_gpio_cd:
iounmap(host->base); iounmap(host->base);
clk_disable: clk_disable:
clk_disable(host->clk); clk_disable_unprepare(host->clk);
clk_unprep:
clk_unprepare(host->clk);
clk_free: clk_free:
clk_put(host->clk); clk_put(host->clk);
host_free: host_free:
...@@ -1564,8 +1558,7 @@ static int __devexit mmci_remove(struct amba_device *dev) ...@@ -1564,8 +1558,7 @@ static int __devexit mmci_remove(struct amba_device *dev)
gpio_free(host->gpio_cd); gpio_free(host->gpio_cd);
iounmap(host->base); iounmap(host->base);
clk_disable(host->clk); clk_disable_unprepare(host->clk);
clk_unprepare(host->clk);
clk_put(host->clk); clk_put(host->clk);
if (host->vcc) if (host->vcc)
......
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
#include <mach/hardware.h> #include <mach/hardware.h>
#define DRIVER_NAME "mxc-mmc" #define DRIVER_NAME "mxc-mmc"
#define MXCMCI_TIMEOUT_MS 10000
#define MMC_REG_STR_STP_CLK 0x00 #define MMC_REG_STR_STP_CLK 0x00
#define MMC_REG_STATUS 0x04 #define MMC_REG_STATUS 0x04
...@@ -150,6 +151,8 @@ struct mxcmci_host { ...@@ -150,6 +151,8 @@ struct mxcmci_host {
int dmareq; int dmareq;
struct dma_slave_config dma_slave_config; struct dma_slave_config dma_slave_config;
struct imx_dma_data dma_data; struct imx_dma_data dma_data;
struct timer_list watchdog;
}; };
static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios); static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios);
...@@ -271,9 +274,32 @@ static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data) ...@@ -271,9 +274,32 @@ static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
dmaengine_submit(host->desc); dmaengine_submit(host->desc);
dma_async_issue_pending(host->dma); dma_async_issue_pending(host->dma);
mod_timer(&host->watchdog, jiffies + msecs_to_jiffies(MXCMCI_TIMEOUT_MS));
return 0; return 0;
} }
static void mxcmci_cmd_done(struct mxcmci_host *host, unsigned int stat);
static void mxcmci_data_done(struct mxcmci_host *host, unsigned int stat);
static void mxcmci_dma_callback(void *data)
{
struct mxcmci_host *host = data;
u32 stat;
del_timer(&host->watchdog);
stat = readl(host->base + MMC_REG_STATUS);
writel(stat & ~STATUS_DATA_TRANS_DONE, host->base + MMC_REG_STATUS);
dev_dbg(mmc_dev(host->mmc), "%s: 0x%08x\n", __func__, stat);
if (stat & STATUS_READ_OP_DONE)
writel(STATUS_READ_OP_DONE, host->base + MMC_REG_STATUS);
mxcmci_data_done(host, stat);
}
static int mxcmci_start_cmd(struct mxcmci_host *host, struct mmc_command *cmd, static int mxcmci_start_cmd(struct mxcmci_host *host, struct mmc_command *cmd,
unsigned int cmdat) unsigned int cmdat)
{ {
...@@ -305,8 +331,14 @@ static int mxcmci_start_cmd(struct mxcmci_host *host, struct mmc_command *cmd, ...@@ -305,8 +331,14 @@ static int mxcmci_start_cmd(struct mxcmci_host *host, struct mmc_command *cmd,
int_cntr = INT_END_CMD_RES_EN; int_cntr = INT_END_CMD_RES_EN;
if (mxcmci_use_dma(host)) if (mxcmci_use_dma(host)) {
int_cntr |= INT_READ_OP_EN | INT_WRITE_OP_DONE_EN; if (host->dma_dir == DMA_FROM_DEVICE) {
host->desc->callback = mxcmci_dma_callback;
host->desc->callback_param = host;
} else {
int_cntr |= INT_WRITE_OP_DONE_EN;
}
}
spin_lock_irqsave(&host->lock, flags); spin_lock_irqsave(&host->lock, flags);
if (host->use_sdio) if (host->use_sdio)
...@@ -345,11 +377,9 @@ static int mxcmci_finish_data(struct mxcmci_host *host, unsigned int stat) ...@@ -345,11 +377,9 @@ static int mxcmci_finish_data(struct mxcmci_host *host, unsigned int stat)
struct mmc_data *data = host->data; struct mmc_data *data = host->data;
int data_error; int data_error;
if (mxcmci_use_dma(host)) { if (mxcmci_use_dma(host))
dmaengine_terminate_all(host->dma);
dma_unmap_sg(host->dma->device->dev, data->sg, data->sg_len, dma_unmap_sg(host->dma->device->dev, data->sg, data->sg_len,
host->dma_dir); host->dma_dir);
}
if (stat & STATUS_ERR_MASK) { if (stat & STATUS_ERR_MASK) {
dev_dbg(mmc_dev(host->mmc), "request failed. status: 0x%08x\n", dev_dbg(mmc_dev(host->mmc), "request failed. status: 0x%08x\n",
...@@ -624,8 +654,10 @@ static irqreturn_t mxcmci_irq(int irq, void *devid) ...@@ -624,8 +654,10 @@ static irqreturn_t mxcmci_irq(int irq, void *devid)
mxcmci_cmd_done(host, stat); mxcmci_cmd_done(host, stat);
if (mxcmci_use_dma(host) && if (mxcmci_use_dma(host) &&
(stat & (STATUS_DATA_TRANS_DONE | STATUS_WRITE_OP_DONE))) (stat & (STATUS_DATA_TRANS_DONE | STATUS_WRITE_OP_DONE))) {
del_timer(&host->watchdog);
mxcmci_data_done(host, stat); mxcmci_data_done(host, stat);
}
if (host->default_irq_mask && if (host->default_irq_mask &&
(stat & (STATUS_CARD_INSERTION | STATUS_CARD_REMOVAL))) (stat & (STATUS_CARD_INSERTION | STATUS_CARD_REMOVAL)))
...@@ -836,6 +868,34 @@ static bool filter(struct dma_chan *chan, void *param) ...@@ -836,6 +868,34 @@ static bool filter(struct dma_chan *chan, void *param)
return true; return true;
} }
static void mxcmci_watchdog(unsigned long data)
{
struct mmc_host *mmc = (struct mmc_host *)data;
struct mxcmci_host *host = mmc_priv(mmc);
struct mmc_request *req = host->req;
unsigned int stat = readl(host->base + MMC_REG_STATUS);
if (host->dma_dir == DMA_FROM_DEVICE) {
dmaengine_terminate_all(host->dma);
dev_err(mmc_dev(host->mmc),
"%s: read time out (status = 0x%08x)\n",
__func__, stat);
} else {
dev_err(mmc_dev(host->mmc),
"%s: write time out (status = 0x%08x)\n",
__func__, stat);
mxcmci_softreset(host);
}
/* Mark transfer as erroneus and inform the upper layers */
host->data->error = -ETIMEDOUT;
host->req = NULL;
host->cmd = NULL;
host->data = NULL;
mmc_request_done(host->mmc, req);
}
static const struct mmc_host_ops mxcmci_ops = { static const struct mmc_host_ops mxcmci_ops = {
.request = mxcmci_request, .request = mxcmci_request,
.set_ios = mxcmci_set_ios, .set_ios = mxcmci_set_ios,
...@@ -968,6 +1028,10 @@ static int mxcmci_probe(struct platform_device *pdev) ...@@ -968,6 +1028,10 @@ static int mxcmci_probe(struct platform_device *pdev)
mmc_add_host(mmc); mmc_add_host(mmc);
init_timer(&host->watchdog);
host->watchdog.function = &mxcmci_watchdog;
host->watchdog.data = (unsigned long)mmc;
return 0; return 0;
out_free_irq: out_free_irq:
......
...@@ -27,16 +27,10 @@ ...@@ -27,16 +27,10 @@
#include <linux/mmc/card.h> #include <linux/mmc/card.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
#include <linux/i2c/tps65010.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <plat/mmc.h> #include <plat/mmc.h>
#include <asm/gpio.h>
#include <plat/dma.h> #include <plat/dma.h>
#include <plat/fpga.h>
#define OMAP_MMC_REG_CMD 0x00 #define OMAP_MMC_REG_CMD 0x00
#define OMAP_MMC_REG_ARGL 0x01 #define OMAP_MMC_REG_ARGL 0x01
...@@ -105,7 +99,6 @@ struct mmc_omap_slot { ...@@ -105,7 +99,6 @@ struct mmc_omap_slot {
u16 saved_con; u16 saved_con;
u16 bus_mode; u16 bus_mode;
unsigned int fclk_freq; unsigned int fclk_freq;
unsigned powered:1;
struct tasklet_struct cover_tasklet; struct tasklet_struct cover_tasklet;
struct timer_list cover_timer; struct timer_list cover_timer;
...@@ -137,7 +130,6 @@ struct mmc_omap_host { ...@@ -137,7 +130,6 @@ struct mmc_omap_host {
unsigned int phys_base; unsigned int phys_base;
int irq; int irq;
unsigned char bus_mode; unsigned char bus_mode;
unsigned char hw_bus_mode;
unsigned int reg_shift; unsigned int reg_shift;
struct work_struct cmd_abort_work; struct work_struct cmd_abort_work;
...@@ -695,22 +687,29 @@ mmc_omap_xfer_data(struct mmc_omap_host *host, int write) ...@@ -695,22 +687,29 @@ mmc_omap_xfer_data(struct mmc_omap_host *host, int write)
host->buffer += nwords; host->buffer += nwords;
} }
static inline void mmc_omap_report_irq(u16 status) #ifdef CONFIG_MMC_DEBUG
static void mmc_omap_report_irq(struct mmc_omap_host *host, u16 status)
{ {
static const char *mmc_omap_status_bits[] = { static const char *mmc_omap_status_bits[] = {
"EOC", "CD", "CB", "BRS", "EOFB", "DTO", "DCRC", "CTO", "EOC", "CD", "CB", "BRS", "EOFB", "DTO", "DCRC", "CTO",
"CCRC", "CRW", "AF", "AE", "OCRB", "CIRQ", "CERR" "CCRC", "CRW", "AF", "AE", "OCRB", "CIRQ", "CERR"
}; };
int i, c = 0; int i;
char res[64], *buf = res;
buf += sprintf(buf, "MMC IRQ 0x%x:", status);
for (i = 0; i < ARRAY_SIZE(mmc_omap_status_bits); i++) for (i = 0; i < ARRAY_SIZE(mmc_omap_status_bits); i++)
if (status & (1 << i)) { if (status & (1 << i))
if (c) buf += sprintf(buf, " %s", mmc_omap_status_bits[i]);
printk(" "); dev_vdbg(mmc_dev(host->mmc), "%s\n", res);
printk("%s", mmc_omap_status_bits[i]);
c++;
}
} }
#else
static void mmc_omap_report_irq(struct mmc_omap_host *host, u16 status)
{
}
#endif
static irqreturn_t mmc_omap_irq(int irq, void *dev_id) static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
{ {
...@@ -744,12 +743,10 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) ...@@ -744,12 +743,10 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
cmd = host->cmd->opcode; cmd = host->cmd->opcode;
else else
cmd = -1; cmd = -1;
#ifdef CONFIG_MMC_DEBUG
dev_dbg(mmc_dev(host->mmc), "MMC IRQ %04x (CMD %d): ", dev_dbg(mmc_dev(host->mmc), "MMC IRQ %04x (CMD %d): ",
status, cmd); status, cmd);
mmc_omap_report_irq(status); mmc_omap_report_irq(host, status);
printk("\n");
#endif
if (host->total_bytes_left) { if (host->total_bytes_left) {
if ((status & OMAP_MMC_STAT_A_FULL) || if ((status & OMAP_MMC_STAT_A_FULL) ||
(status & OMAP_MMC_STAT_END_OF_DATA)) (status & OMAP_MMC_STAT_END_OF_DATA))
......
...@@ -35,7 +35,6 @@ ...@@ -35,7 +35,6 @@
#include <linux/mmc/core.h> #include <linux/mmc/core.h>
#include <linux/mmc/mmc.h> #include <linux/mmc/mmc.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/semaphore.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
...@@ -44,7 +43,6 @@ ...@@ -44,7 +43,6 @@
#include <plat/cpu.h> #include <plat/cpu.h>
/* OMAP HSMMC Host Controller Registers */ /* OMAP HSMMC Host Controller Registers */
#define OMAP_HSMMC_SYSCONFIG 0x0010
#define OMAP_HSMMC_SYSSTATUS 0x0014 #define OMAP_HSMMC_SYSSTATUS 0x0014
#define OMAP_HSMMC_CON 0x002C #define OMAP_HSMMC_CON 0x002C
#define OMAP_HSMMC_BLK 0x0104 #define OMAP_HSMMC_BLK 0x0104
...@@ -161,8 +159,6 @@ struct omap_hsmmc_host { ...@@ -161,8 +159,6 @@ struct omap_hsmmc_host {
unsigned int dma_sg_idx; unsigned int dma_sg_idx;
unsigned char bus_mode; unsigned char bus_mode;
unsigned char power_mode; unsigned char power_mode;
u32 *buffer;
u32 bytesleft;
int suspended; int suspended;
int irq; int irq;
int use_dma, dma_ch; int use_dma, dma_ch;
...@@ -171,7 +167,6 @@ struct omap_hsmmc_host { ...@@ -171,7 +167,6 @@ struct omap_hsmmc_host {
int slot_id; int slot_id;
int response_busy; int response_busy;
int context_loss; int context_loss;
int vdd;
int protect_card; int protect_card;
int reqs_blocked; int reqs_blocked;
int use_reg; int use_reg;
...@@ -300,12 +295,12 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) ...@@ -300,12 +295,12 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
struct regulator *reg; struct regulator *reg;
int ocr_value = 0; int ocr_value = 0;
mmc_slot(host).set_power = omap_hsmmc_set_power;
reg = regulator_get(host->dev, "vmmc"); reg = regulator_get(host->dev, "vmmc");
if (IS_ERR(reg)) { if (IS_ERR(reg)) {
dev_dbg(host->dev, "vmmc regulator missing\n"); dev_dbg(host->dev, "vmmc regulator missing\n");
return PTR_ERR(reg);
} else { } else {
mmc_slot(host).set_power = omap_hsmmc_set_power;
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_slot(host).ocr_mask) {
...@@ -495,7 +490,7 @@ static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host) ...@@ -495,7 +490,7 @@ static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host)
unsigned long regval; unsigned long regval;
unsigned long timeout; unsigned long timeout;
dev_dbg(mmc_dev(host->mmc), "Set clock to %uHz\n", ios->clock); dev_vdbg(mmc_dev(host->mmc), "Set clock to %uHz\n", ios->clock);
omap_hsmmc_stop_clock(host); omap_hsmmc_stop_clock(host);
...@@ -579,21 +574,8 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host) ...@@ -579,21 +574,8 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
if (host->context_loss == context_loss) if (host->context_loss == context_loss)
return 1; return 1;
/* Wait for hardware reset */ if (!OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE)
timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS); return 1;
while ((OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE) != RESETDONE
&& time_before(jiffies, timeout))
;
/* Do software reset */
OMAP_HSMMC_WRITE(host->base, SYSCONFIG, SOFTRESET);
timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
while ((OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE) != RESETDONE
&& time_before(jiffies, timeout))
;
OMAP_HSMMC_WRITE(host->base, SYSCONFIG,
OMAP_HSMMC_READ(host->base, SYSCONFIG) | AUTOIDLE);
if (host->pdata->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT) { if (host->pdata->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT) {
if (host->power_mode != MMC_POWER_OFF && if (host->power_mode != MMC_POWER_OFF &&
...@@ -745,7 +727,7 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd, ...@@ -745,7 +727,7 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,
{ {
int cmdreg = 0, resptype = 0, cmdtype = 0; int cmdreg = 0, resptype = 0, cmdtype = 0;
dev_dbg(mmc_dev(host->mmc), "%s: CMD%d, argument 0x%08x\n", dev_vdbg(mmc_dev(host->mmc), "%s: CMD%d, argument 0x%08x\n",
mmc_hostname(host->mmc), cmd->opcode, cmd->arg); mmc_hostname(host->mmc), cmd->opcode, cmd->arg);
host->cmd = cmd; host->cmd = cmd;
...@@ -934,7 +916,7 @@ static void omap_hsmmc_dbg_report_irq(struct omap_hsmmc_host *host, u32 status) ...@@ -934,7 +916,7 @@ static void omap_hsmmc_dbg_report_irq(struct omap_hsmmc_host *host, u32 status)
buf += len; buf += len;
} }
dev_dbg(mmc_dev(host->mmc), "%s\n", res); dev_vdbg(mmc_dev(host->mmc), "%s\n", res);
} }
#else #else
static inline void omap_hsmmc_dbg_report_irq(struct omap_hsmmc_host *host, static inline void omap_hsmmc_dbg_report_irq(struct omap_hsmmc_host *host,
...@@ -981,72 +963,40 @@ static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host, ...@@ -981,72 +963,40 @@ static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host,
__func__); __func__);
} }
static void hsmmc_command_incomplete(struct omap_hsmmc_host *host, int err)
{
omap_hsmmc_reset_controller_fsm(host, SRC);
host->cmd->error = err;
if (host->data) {
omap_hsmmc_reset_controller_fsm(host, SRD);
omap_hsmmc_dma_cleanup(host, err);
}
}
static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status) static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
{ {
struct mmc_data *data; struct mmc_data *data;
int end_cmd = 0, end_trans = 0; int end_cmd = 0, end_trans = 0;
if (!host->req_in_progress) {
do {
OMAP_HSMMC_WRITE(host->base, STAT, status);
/* Flush posted write */
status = OMAP_HSMMC_READ(host->base, STAT);
} while (status & INT_EN_MASK);
return;
}
data = host->data; data = host->data;
dev_dbg(mmc_dev(host->mmc), "IRQ Status is %x\n", status); dev_vdbg(mmc_dev(host->mmc), "IRQ Status is %x\n", status);
if (status & ERR) { if (status & ERR) {
omap_hsmmc_dbg_report_irq(host, status); omap_hsmmc_dbg_report_irq(host, status);
if ((status & CMD_TIMEOUT) || if (status & (CMD_TIMEOUT | DATA_TIMEOUT))
(status & CMD_CRC)) { hsmmc_command_incomplete(host, -ETIMEDOUT);
if (host->cmd) { else if (status & (CMD_CRC | DATA_CRC))
if (status & CMD_TIMEOUT) { hsmmc_command_incomplete(host, -EILSEQ);
omap_hsmmc_reset_controller_fsm(host,
SRC);
host->cmd->error = -ETIMEDOUT;
} else {
host->cmd->error = -EILSEQ;
}
end_cmd = 1;
}
if (host->data || host->response_busy) {
if (host->data)
omap_hsmmc_dma_cleanup(host,
-ETIMEDOUT);
host->response_busy = 0;
omap_hsmmc_reset_controller_fsm(host, SRD);
}
}
if ((status & DATA_TIMEOUT) ||
(status & DATA_CRC)) {
if (host->data || host->response_busy) {
int err = (status & DATA_TIMEOUT) ?
-ETIMEDOUT : -EILSEQ;
if (host->data)
omap_hsmmc_dma_cleanup(host, err);
else
host->mrq->cmd->error = err;
host->response_busy = 0;
omap_hsmmc_reset_controller_fsm(host, SRD);
end_trans = 1;
}
}
if (status & CARD_ERR) {
dev_dbg(mmc_dev(host->mmc),
"Ignoring card err CMD%d\n", host->cmd->opcode);
if (host->cmd)
end_cmd = 1; end_cmd = 1;
if (host->data) if (host->data || host->response_busy) {
end_trans = 1; end_trans = 1;
host->response_busy = 0;
} }
} }
OMAP_HSMMC_WRITE(host->base, STAT, status);
if (end_cmd || ((status & CC) && host->cmd)) if (end_cmd || ((status & CC) && host->cmd))
omap_hsmmc_cmd_done(host, host->cmd); omap_hsmmc_cmd_done(host, host->cmd);
if ((end_trans || (status & TC)) && host->mrq) if ((end_trans || (status & TC)) && host->mrq)
...@@ -1062,11 +1012,13 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id) ...@@ -1062,11 +1012,13 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
int status; int status;
status = OMAP_HSMMC_READ(host->base, STAT); status = OMAP_HSMMC_READ(host->base, STAT);
do { while (status & INT_EN_MASK && host->req_in_progress) {
omap_hsmmc_do_irq(host, status); omap_hsmmc_do_irq(host, status);
/* Flush posted write */ /* Flush posted write */
OMAP_HSMMC_WRITE(host->base, STAT, status);
status = OMAP_HSMMC_READ(host->base, STAT); status = OMAP_HSMMC_READ(host->base, STAT);
} while (status & INT_EN_MASK); }
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -1501,12 +1453,10 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ...@@ -1501,12 +1453,10 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
case MMC_POWER_OFF: case MMC_POWER_OFF:
mmc_slot(host).set_power(host->dev, host->slot_id, mmc_slot(host).set_power(host->dev, host->slot_id,
0, 0); 0, 0);
host->vdd = 0;
break; break;
case MMC_POWER_UP: case MMC_POWER_UP:
mmc_slot(host).set_power(host->dev, host->slot_id, mmc_slot(host).set_power(host->dev, host->slot_id,
1, ios->vdd); 1, ios->vdd);
host->vdd = ios->vdd;
break; break;
case MMC_POWER_ON: case MMC_POWER_ON:
do_send_init_stream = 1; do_send_init_stream = 1;
...@@ -1598,10 +1548,6 @@ static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host) ...@@ -1598,10 +1548,6 @@ static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
value = OMAP_HSMMC_READ(host->base, CAPA); value = OMAP_HSMMC_READ(host->base, CAPA);
OMAP_HSMMC_WRITE(host->base, CAPA, value | capa); OMAP_HSMMC_WRITE(host->base, CAPA, value | capa);
/* Set the controller to AUTO IDLE mode */
value = OMAP_HSMMC_READ(host->base, SYSCONFIG);
OMAP_HSMMC_WRITE(host->base, SYSCONFIG, value | AUTOIDLE);
/* Set SD bus power bit */ /* Set SD bus power bit */
set_sd_bus_power(host); set_sd_bus_power(host);
} }
...@@ -1659,8 +1605,6 @@ static int omap_hsmmc_regs_show(struct seq_file *s, void *data) ...@@ -1659,8 +1605,6 @@ static int omap_hsmmc_regs_show(struct seq_file *s, void *data)
pm_runtime_get_sync(host->dev); pm_runtime_get_sync(host->dev);
seq_printf(s, "SYSCONFIG:\t0x%08x\n",
OMAP_HSMMC_READ(host->base, SYSCONFIG));
seq_printf(s, "CON:\t\t0x%08x\n", seq_printf(s, "CON:\t\t0x%08x\n",
OMAP_HSMMC_READ(host->base, CON)); OMAP_HSMMC_READ(host->base, CON));
seq_printf(s, "HCTL:\t\t0x%08x\n", seq_printf(s, "HCTL:\t\t0x%08x\n",
...@@ -2105,8 +2049,7 @@ static int omap_hsmmc_suspend(struct device *dev) ...@@ -2105,8 +2049,7 @@ static int omap_hsmmc_suspend(struct device *dev)
if (ret) { if (ret) {
host->suspended = 0; host->suspended = 0;
if (host->pdata->resume) { if (host->pdata->resume) {
ret = host->pdata->resume(dev, host->slot_id); if (host->pdata->resume(dev, host->slot_id))
if (ret)
dev_dbg(dev, "Unmask interrupt failed\n"); dev_dbg(dev, "Unmask interrupt failed\n");
} }
goto err; goto err;
......
...@@ -30,6 +30,9 @@ ...@@ -30,6 +30,9 @@
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/gfp.h> #include <linux/gfp.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_device.h>
#include <asm/sizes.h> #include <asm/sizes.h>
...@@ -573,6 +576,50 @@ static irqreturn_t pxamci_detect_irq(int irq, void *devid) ...@@ -573,6 +576,50 @@ static irqreturn_t pxamci_detect_irq(int irq, void *devid)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
#ifdef CONFIG_OF
static const struct of_device_id pxa_mmc_dt_ids[] = {
{ .compatible = "marvell,pxa-mmc" },
{ }
};
MODULE_DEVICE_TABLE(of, pxa_mmc_dt_ids);
static int __devinit pxamci_of_init(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct pxamci_platform_data *pdata;
u32 tmp;
if (!np)
return 0;
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
pdata->gpio_card_detect =
of_get_named_gpio(np, "cd-gpios", 0);
pdata->gpio_card_ro =
of_get_named_gpio(np, "wp-gpios", 0);
/* pxa-mmc specific */
pdata->gpio_power =
of_get_named_gpio(np, "pxa-mmc,gpio-power", 0);
if (of_property_read_u32(np, "pxa-mmc,detect-delay-ms", &tmp) == 0)
pdata->detect_delay_ms = tmp;
pdev->dev.platform_data = pdata;
return 0;
}
#else
static int __devinit pxamci_of_init(struct platform_device *pdev)
{
return 0;
}
#endif
static int pxamci_probe(struct platform_device *pdev) static int pxamci_probe(struct platform_device *pdev)
{ {
struct mmc_host *mmc; struct mmc_host *mmc;
...@@ -580,6 +627,10 @@ static int pxamci_probe(struct platform_device *pdev) ...@@ -580,6 +627,10 @@ static int pxamci_probe(struct platform_device *pdev)
struct resource *r, *dmarx, *dmatx; struct resource *r, *dmarx, *dmatx;
int ret, irq, gpio_cd = -1, gpio_ro = -1, gpio_power = -1; int ret, irq, gpio_cd = -1, gpio_ro = -1, gpio_power = -1;
ret = pxamci_of_init(pdev);
if (ret)
return ret;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0); r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
if (!r || irq < 0) if (!r || irq < 0)
...@@ -866,6 +917,7 @@ static struct platform_driver pxamci_driver = { ...@@ -866,6 +917,7 @@ static struct platform_driver pxamci_driver = {
.driver = { .driver = {
.name = DRIVER_NAME, .name = DRIVER_NAME,
.owner = THIS_MODULE, .owner = THIS_MODULE,
.of_match_table = of_match_ptr(pxa_mmc_dt_ids),
#ifdef CONFIG_PM #ifdef CONFIG_PM
.pm = &pxamci_pm_ops, .pm = &pxamci_pm_ops,
#endif #endif
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/of.h>
#include "sdhci-pltfm.h" #include "sdhci-pltfm.h"
...@@ -126,11 +127,18 @@ static int __devexit sdhci_dove_remove(struct platform_device *pdev) ...@@ -126,11 +127,18 @@ static int __devexit sdhci_dove_remove(struct platform_device *pdev)
return sdhci_pltfm_unregister(pdev); return sdhci_pltfm_unregister(pdev);
} }
static const struct of_device_id sdhci_dove_of_match_table[] __devinitdata = {
{ .compatible = "marvell,dove-sdhci", },
{}
};
MODULE_DEVICE_TABLE(of, sdhci_dove_of_match_table);
static struct platform_driver sdhci_dove_driver = { static struct platform_driver sdhci_dove_driver = {
.driver = { .driver = {
.name = "sdhci-dove", .name = "sdhci-dove",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = SDHCI_PLTFM_PMOPS, .pm = SDHCI_PLTFM_PMOPS,
.of_match_table = of_match_ptr(sdhci_dove_of_match_table),
}, },
.probe = sdhci_dove_probe, .probe = sdhci_dove_probe,
.remove = __devexit_p(sdhci_dove_remove), .remove = __devexit_p(sdhci_dove_remove),
......
...@@ -21,6 +21,32 @@ ...@@ -21,6 +21,32 @@
#include "sdhci-pltfm.h" #include "sdhci-pltfm.h"
#include "sdhci-esdhc.h" #include "sdhci-esdhc.h"
#define VENDOR_V_22 0x12
static u32 esdhc_readl(struct sdhci_host *host, int reg)
{
u32 ret;
ret = in_be32(host->ioaddr + reg);
/*
* The bit of ADMA flag in eSDHC is not compatible with standard
* SDHC register, so set fake flag SDHCI_CAN_DO_ADMA2 when ADMA is
* supported by eSDHC.
* And for many FSL eSDHC controller, the reset value of field
* SDHCI_CAN_DO_ADMA1 is one, but some of them can't support ADMA,
* only these vendor version is greater than 2.2/0x12 support ADMA.
* For FSL eSDHC, must aligned 4-byte, so use 0xFC to read the
* the verdor version number, oxFE is SDHCI_HOST_VERSION.
*/
if ((reg == SDHCI_CAPABILITIES) && (ret & SDHCI_CAN_DO_ADMA1)) {
u32 tmp = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS);
tmp = (tmp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT;
if (tmp > VENDOR_V_22)
ret |= SDHCI_CAN_DO_ADMA2;
}
return ret;
}
static u16 esdhc_readw(struct sdhci_host *host, int reg) static u16 esdhc_readw(struct sdhci_host *host, int reg)
{ {
u16 ret; u16 ret;
...@@ -144,7 +170,7 @@ static void esdhc_of_resume(struct sdhci_host *host) ...@@ -144,7 +170,7 @@ static void esdhc_of_resume(struct sdhci_host *host)
#endif #endif
static struct sdhci_ops sdhci_esdhc_ops = { static struct sdhci_ops sdhci_esdhc_ops = {
.read_l = sdhci_be32bs_readl, .read_l = esdhc_readl,
.read_w = esdhc_readw, .read_w = esdhc_readw,
.read_b = esdhc_readb, .read_b = esdhc_readb,
.write_l = sdhci_be32bs_writel, .write_l = sdhci_be32bs_writel,
...@@ -161,9 +187,13 @@ static struct sdhci_ops sdhci_esdhc_ops = { ...@@ -161,9 +187,13 @@ static struct sdhci_ops sdhci_esdhc_ops = {
}; };
static struct sdhci_pltfm_data sdhci_esdhc_pdata = { static struct sdhci_pltfm_data sdhci_esdhc_pdata = {
/* card detection could be handled via GPIO */ /*
* card detection could be handled via GPIO
* eSDHC cannot support End Attribute in NOP ADMA descriptor
*/
.quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_CARD_DETECTION .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_CARD_DETECTION
| SDHCI_QUIRK_NO_CARD_NO_RESET, | SDHCI_QUIRK_NO_CARD_NO_RESET
| SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.ops = &sdhci_esdhc_ops, .ops = &sdhci_esdhc_ops,
}; };
......
...@@ -1476,24 +1476,7 @@ static struct pci_driver sdhci_driver = { ...@@ -1476,24 +1476,7 @@ static struct pci_driver sdhci_driver = {
}, },
}; };
/*****************************************************************************\ module_pci_driver(sdhci_driver);
* *
* Driver init/exit *
* *
\*****************************************************************************/
static int __init sdhci_drv_init(void)
{
return pci_register_driver(&sdhci_driver);
}
static void __exit sdhci_drv_exit(void)
{
pci_unregister_driver(&sdhci_driver);
}
module_init(sdhci_drv_init);
module_exit(sdhci_drv_exit);
MODULE_AUTHOR("Pierre Ossman <pierre@ossman.eu>"); MODULE_AUTHOR("Pierre Ossman <pierre@ossman.eu>");
MODULE_DESCRIPTION("Secure Digital Host Controller Interface PCI driver"); MODULE_DESCRIPTION("Secure Digital Host Controller Interface PCI driver");
......
...@@ -75,6 +75,9 @@ void sdhci_get_of_property(struct platform_device *pdev) ...@@ -75,6 +75,9 @@ void sdhci_get_of_property(struct platform_device *pdev)
if (sdhci_of_wp_inverted(np)) if (sdhci_of_wp_inverted(np))
host->quirks |= SDHCI_QUIRK_INVERTED_WRITE_PROTECT; host->quirks |= SDHCI_QUIRK_INVERTED_WRITE_PROTECT;
if (of_get_property(np, "broken-cd", NULL))
host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
if (of_device_is_compatible(np, "fsl,p2020-rev1-esdhc")) if (of_device_is_compatible(np, "fsl,p2020-rev1-esdhc"))
host->quirks |= SDHCI_QUIRK_BROKEN_DMA; host->quirks |= SDHCI_QUIRK_BROKEN_DMA;
......
...@@ -197,7 +197,7 @@ static int __devinit sdhci_pxav2_probe(struct platform_device *pdev) ...@@ -197,7 +197,7 @@ static int __devinit sdhci_pxav2_probe(struct platform_device *pdev)
goto err_clk_get; goto err_clk_get;
} }
pltfm_host->clk = clk; pltfm_host->clk = clk;
clk_enable(clk); clk_prepare_enable(clk);
host->quirks = SDHCI_QUIRK_BROKEN_ADMA host->quirks = SDHCI_QUIRK_BROKEN_ADMA
| SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL
...@@ -239,7 +239,7 @@ static int __devinit sdhci_pxav2_probe(struct platform_device *pdev) ...@@ -239,7 +239,7 @@ static int __devinit sdhci_pxav2_probe(struct platform_device *pdev)
return 0; return 0;
err_add_host: err_add_host:
clk_disable(clk); clk_disable_unprepare(clk);
clk_put(clk); clk_put(clk);
err_clk_get: err_clk_get:
sdhci_pltfm_free(pdev); sdhci_pltfm_free(pdev);
...@@ -255,7 +255,7 @@ static int __devexit sdhci_pxav2_remove(struct platform_device *pdev) ...@@ -255,7 +255,7 @@ static int __devexit sdhci_pxav2_remove(struct platform_device *pdev)
sdhci_remove_host(host, 1); sdhci_remove_host(host, 1);
clk_disable(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); kfree(pxa);
......
...@@ -24,12 +24,14 @@ ...@@ -24,12 +24,14 @@
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/mmc/card.h> #include <linux/mmc/card.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/mmc/slot-gpio.h>
#include <linux/platform_data/pxa_sdhci.h> #include <linux/platform_data/pxa_sdhci.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/of_gpio.h>
#include "sdhci.h" #include "sdhci.h"
#include "sdhci-pltfm.h" #include "sdhci-pltfm.h"
...@@ -182,6 +184,7 @@ static struct sdhci_pxa_platdata *pxav3_get_mmc_pdata(struct device *dev) ...@@ -182,6 +184,7 @@ static struct sdhci_pxa_platdata *pxav3_get_mmc_pdata(struct device *dev)
struct device_node *np = dev->of_node; struct device_node *np = dev->of_node;
u32 bus_width; u32 bus_width;
u32 clk_delay_cycles; u32 clk_delay_cycles;
enum of_gpio_flags gpio_flags;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) if (!pdata)
...@@ -198,6 +201,10 @@ static struct sdhci_pxa_platdata *pxav3_get_mmc_pdata(struct device *dev) ...@@ -198,6 +201,10 @@ static struct sdhci_pxa_platdata *pxav3_get_mmc_pdata(struct device *dev)
if (clk_delay_cycles > 0) if (clk_delay_cycles > 0)
pdata->clk_delay_cycles = clk_delay_cycles; pdata->clk_delay_cycles = clk_delay_cycles;
pdata->ext_cd_gpio = of_get_named_gpio_flags(np, "cd-gpios", 0, &gpio_flags);
if (gpio_flags != OF_GPIO_ACTIVE_LOW)
pdata->host_caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;
return pdata; return pdata;
} }
#else #else
...@@ -231,14 +238,14 @@ static int __devinit sdhci_pxav3_probe(struct platform_device *pdev) ...@@ -231,14 +238,14 @@ static int __devinit 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 = clk_get(dev, "PXA-SDHCLK"); clk = clk_get(dev, NULL);
if (IS_ERR(clk)) { if (IS_ERR(clk)) {
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(clk);
goto err_clk_get; goto err_clk_get;
} }
pltfm_host->clk = clk; pltfm_host->clk = clk;
clk_enable(clk); clk_prepare_enable(clk);
host->quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL host->quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL
| SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC
...@@ -266,12 +273,25 @@ static int __devinit sdhci_pxav3_probe(struct platform_device *pdev) ...@@ -266,12 +273,25 @@ static int __devinit sdhci_pxav3_probe(struct platform_device *pdev)
host->quirks |= pdata->quirks; host->quirks |= pdata->quirks;
if (pdata->host_caps) if (pdata->host_caps)
host->mmc->caps |= pdata->host_caps; host->mmc->caps |= pdata->host_caps;
if (pdata->host_caps2)
host->mmc->caps2 |= pdata->host_caps2;
if (pdata->pm_caps) if (pdata->pm_caps)
host->mmc->pm_caps |= pdata->pm_caps; host->mmc->pm_caps |= pdata->pm_caps;
if (gpio_is_valid(pdata->ext_cd_gpio)) {
ret = mmc_gpio_request_cd(host->mmc, pdata->ext_cd_gpio);
if (ret) {
dev_err(mmc_dev(host->mmc),
"failed to allocate card detect gpio\n");
goto err_cd_req;
}
}
} }
host->ops = &pxav3_sdhci_ops; host->ops = &pxav3_sdhci_ops;
sdhci_get_of_property(pdev);
ret = sdhci_add_host(host); ret = sdhci_add_host(host);
if (ret) { if (ret) {
dev_err(&pdev->dev, "failed to add host\n"); dev_err(&pdev->dev, "failed to add host\n");
...@@ -283,8 +303,10 @@ static int __devinit sdhci_pxav3_probe(struct platform_device *pdev) ...@@ -283,8 +303,10 @@ static int __devinit sdhci_pxav3_probe(struct platform_device *pdev)
return 0; return 0;
err_add_host: err_add_host:
clk_disable(clk); clk_disable_unprepare(clk);
clk_put(clk); clk_put(clk);
mmc_gpio_free_cd(host->mmc);
err_cd_req:
err_clk_get: err_clk_get:
sdhci_pltfm_free(pdev); sdhci_pltfm_free(pdev);
kfree(pxa); kfree(pxa);
...@@ -296,11 +318,16 @@ static int __devexit sdhci_pxav3_remove(struct platform_device *pdev) ...@@ -296,11 +318,16 @@ static int __devexit 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; struct sdhci_pxa *pxa = pltfm_host->priv;
struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data;
sdhci_remove_host(host, 1); sdhci_remove_host(host, 1);
clk_disable(pltfm_host->clk); clk_disable_unprepare(pltfm_host->clk);
clk_put(pltfm_host->clk); clk_put(pltfm_host->clk);
if (gpio_is_valid(pdata->ext_cd_gpio))
mmc_gpio_free_cd(host->mmc);
sdhci_pltfm_free(pdev); sdhci_pltfm_free(pdev);
kfree(pxa); kfree(pxa);
......
This diff is collapsed.
...@@ -20,6 +20,8 @@ ...@@ -20,6 +20,8 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -68,8 +70,42 @@ static irqreturn_t sdhci_gpio_irq(int irq, void *dev_id) ...@@ -68,8 +70,42 @@ static irqreturn_t sdhci_gpio_irq(int irq, void *dev_id)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
#ifdef CONFIG_OF
static struct sdhci_plat_data * __devinit
sdhci_probe_config_dt(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct sdhci_plat_data *pdata = NULL;
int cd_gpio;
cd_gpio = of_get_named_gpio(np, "cd-gpios", 0);
if (!gpio_is_valid(cd_gpio))
cd_gpio = -1;
/* If pdata is required */
if (cd_gpio != -1) {
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
dev_err(&pdev->dev, "DT: kzalloc failed\n");
return ERR_PTR(-ENOMEM);
}
}
pdata->card_int_gpio = cd_gpio;
return pdata;
}
#else
static struct sdhci_plat_data * __devinit
sdhci_probe_config_dt(struct platform_device *pdev)
{
return ERR_PTR(-ENOSYS);
}
#endif
static int __devinit sdhci_probe(struct platform_device *pdev) static int __devinit sdhci_probe(struct platform_device *pdev)
{ {
struct device_node *np = pdev->dev.of_node;
struct sdhci_host *host; struct sdhci_host *host;
struct resource *iomem; struct resource *iomem;
struct spear_sdhci *sdhci; struct spear_sdhci *sdhci;
...@@ -104,14 +140,22 @@ static int __devinit sdhci_probe(struct platform_device *pdev) ...@@ -104,14 +140,22 @@ static int __devinit sdhci_probe(struct platform_device *pdev)
goto err; goto err;
} }
ret = clk_enable(sdhci->clk); ret = clk_prepare_enable(sdhci->clk);
if (ret) { if (ret) {
dev_dbg(&pdev->dev, "Error enabling clock\n"); dev_dbg(&pdev->dev, "Error enabling clock\n");
goto put_clk; goto put_clk;
} }
/* overwrite platform_data */ if (np) {
sdhci->data = sdhci_probe_config_dt(pdev);
if (IS_ERR(sdhci->data)) {
dev_err(&pdev->dev, "DT: Failed to get pdata\n");
return -ENODEV;
}
} else {
sdhci->data = dev_get_platdata(&pdev->dev); sdhci->data = dev_get_platdata(&pdev->dev);
}
pdev->dev.platform_data = sdhci; pdev->dev.platform_data = sdhci;
if (pdev->dev.parent) if (pdev->dev.parent)
...@@ -216,7 +260,7 @@ static int __devinit sdhci_probe(struct platform_device *pdev) ...@@ -216,7 +260,7 @@ static int __devinit sdhci_probe(struct platform_device *pdev)
free_host: free_host:
sdhci_free_host(host); sdhci_free_host(host);
disable_clk: disable_clk:
clk_disable(sdhci->clk); clk_disable_unprepare(sdhci->clk);
put_clk: put_clk:
clk_put(sdhci->clk); clk_put(sdhci->clk);
err: err:
...@@ -238,7 +282,7 @@ static int __devexit sdhci_remove(struct platform_device *pdev) ...@@ -238,7 +282,7 @@ static int __devexit sdhci_remove(struct platform_device *pdev)
sdhci_remove_host(host, dead); sdhci_remove_host(host, dead);
sdhci_free_host(host); sdhci_free_host(host);
clk_disable(sdhci->clk); clk_disable_unprepare(sdhci->clk);
clk_put(sdhci->clk); clk_put(sdhci->clk);
return 0; return 0;
...@@ -253,7 +297,7 @@ static int sdhci_suspend(struct device *dev) ...@@ -253,7 +297,7 @@ static int sdhci_suspend(struct device *dev)
ret = sdhci_suspend_host(host); ret = sdhci_suspend_host(host);
if (!ret) if (!ret)
clk_disable(sdhci->clk); clk_disable_unprepare(sdhci->clk);
return ret; return ret;
} }
...@@ -264,7 +308,7 @@ static int sdhci_resume(struct device *dev) ...@@ -264,7 +308,7 @@ static int sdhci_resume(struct device *dev)
struct spear_sdhci *sdhci = dev_get_platdata(dev); struct spear_sdhci *sdhci = dev_get_platdata(dev);
int ret; int ret;
ret = clk_enable(sdhci->clk); ret = clk_prepare_enable(sdhci->clk);
if (ret) { if (ret) {
dev_dbg(dev, "Resume: Error enabling clock\n"); dev_dbg(dev, "Resume: Error enabling clock\n");
return ret; return ret;
...@@ -276,11 +320,20 @@ static int sdhci_resume(struct device *dev) ...@@ -276,11 +320,20 @@ static int sdhci_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(sdhci_pm_ops, sdhci_suspend, sdhci_resume); static SIMPLE_DEV_PM_OPS(sdhci_pm_ops, sdhci_suspend, sdhci_resume);
#ifdef CONFIG_OF
static const struct of_device_id sdhci_spear_id_table[] = {
{ .compatible = "st,spear300-sdhci" },
{}
};
MODULE_DEVICE_TABLE(of, sdhci_spear_id_table);
#endif
static struct platform_driver sdhci_driver = { static struct platform_driver sdhci_driver = {
.driver = { .driver = {
.name = "sdhci", .name = "sdhci",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &sdhci_pm_ops, .pm = &sdhci_pm_ops,
.of_match_table = of_match_ptr(sdhci_spear_id_table),
}, },
.probe = sdhci_probe, .probe = sdhci_probe,
.remove = __devexit_p(sdhci_remove), .remove = __devexit_p(sdhci_remove),
......
...@@ -257,10 +257,9 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev) ...@@ -257,10 +257,9 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
int rc; int rc;
match = of_match_device(sdhci_tegra_dt_match, &pdev->dev); match = of_match_device(sdhci_tegra_dt_match, &pdev->dev);
if (match) if (!match)
return -EINVAL;
soc_data = match->data; soc_data = match->data;
else
soc_data = &soc_data_tegra20;
host = sdhci_pltfm_init(pdev, soc_data->pdata); host = sdhci_pltfm_init(pdev, soc_data->pdata);
if (IS_ERR(host)) if (IS_ERR(host))
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include <linux/mmc/mmc.h> #include <linux/mmc/mmc.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/mmc/card.h> #include <linux/mmc/card.h>
#include <linux/mmc/slot-gpio.h>
#include "sdhci.h" #include "sdhci.h"
...@@ -1293,6 +1294,13 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) ...@@ -1293,6 +1294,13 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
present = sdhci_readl(host, SDHCI_PRESENT_STATE) & present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
SDHCI_CARD_PRESENT; SDHCI_CARD_PRESENT;
/* If we're using a cd-gpio, testing the presence bit might fail. */
if (!present) {
int ret = mmc_gpio_get_cd(host->mmc);
if (ret > 0)
present = true;
}
if (!present || host->flags & SDHCI_DEVICE_DEAD) { if (!present || host->flags & SDHCI_DEVICE_DEAD) {
host->mrq->cmd->error = -ENOMEDIUM; host->mrq->cmd->error = -ENOMEDIUM;
tasklet_schedule(&host->finish_tasklet); tasklet_schedule(&host->finish_tasklet);
...@@ -1597,30 +1605,23 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) ...@@ -1597,30 +1605,23 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
spin_unlock_irqrestore(&host->lock, flags); spin_unlock_irqrestore(&host->lock, flags);
} }
static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, static int sdhci_do_3_3v_signal_voltage_switch(struct sdhci_host *host,
struct mmc_ios *ios) u16 ctrl)
{ {
u8 pwr; int ret;
u16 clk, ctrl;
u32 present_state;
/*
* Signal Voltage Switching is only applicable for Host Controllers
* v3.00 and above.
*/
if (host->version < SDHCI_SPEC_300)
return 0;
/*
* We first check whether the request is to set signalling voltage
* to 3.3V. If so, we change the voltage to 3.3V and return quickly.
*/
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
/* Set 1.8V Signal Enable in the Host Control2 register to 0 */ /* Set 1.8V Signal Enable in the Host Control2 register to 0 */
ctrl &= ~SDHCI_CTRL_VDD_180; ctrl &= ~SDHCI_CTRL_VDD_180;
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
if (host->vqmmc) {
ret = regulator_set_voltage(host->vqmmc, 3300000, 3300000);
if (ret) {
pr_warning("%s: Switching to 3.3V signalling voltage "
" failed\n", mmc_hostname(host->mmc));
return -EIO;
}
}
/* Wait for 5ms */ /* Wait for 5ms */
usleep_range(5000, 5500); usleep_range(5000, 5500);
...@@ -1628,13 +1629,21 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, ...@@ -1628,13 +1629,21 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
if (!(ctrl & SDHCI_CTRL_VDD_180)) if (!(ctrl & SDHCI_CTRL_VDD_180))
return 0; return 0;
else {
pr_info(DRIVER_NAME ": Switching to 3.3V " pr_warning("%s: 3.3V regulator output did not became stable\n",
"signalling voltage failed\n"); mmc_hostname(host->mmc));
return -EIO; return -EIO;
} }
} else if (!(ctrl & SDHCI_CTRL_VDD_180) &&
(ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180)) { static int sdhci_do_1_8v_signal_voltage_switch(struct sdhci_host *host,
u16 ctrl)
{
u8 pwr;
u16 clk;
u32 present_state;
int ret;
/* Stop SDCLK */ /* Stop SDCLK */
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
clk &= ~SDHCI_CLOCK_CARD_EN; clk &= ~SDHCI_CLOCK_CARD_EN;
...@@ -1648,6 +1657,13 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, ...@@ -1648,6 +1657,13 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
* Enable 1.8V Signal Enable in the Host Control2 * Enable 1.8V Signal Enable in the Host Control2
* register * register
*/ */
if (host->vqmmc)
ret = regulator_set_voltage(host->vqmmc,
1800000, 1800000);
else
ret = 0;
if (!ret) {
ctrl |= SDHCI_CTRL_VDD_180; ctrl |= SDHCI_CTRL_VDD_180;
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
...@@ -1656,7 +1672,7 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, ...@@ -1656,7 +1672,7 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
if (ctrl & SDHCI_CTRL_VDD_180) { if (ctrl & SDHCI_CTRL_VDD_180) {
/* Provide SDCLK again and wait for 1ms*/ /* Provide SDCLK again and wait for 1ms */
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
clk |= SDHCI_CLOCK_CARD_EN; clk |= SDHCI_CLOCK_CARD_EN;
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
...@@ -1673,6 +1689,7 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, ...@@ -1673,6 +1689,7 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
return 0; return 0;
} }
} }
}
/* /*
* If we are here, that means the switch to 1.8V signaling * If we are here, that means the switch to 1.8V signaling
...@@ -1692,10 +1709,35 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, ...@@ -1692,10 +1709,35 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
if (host->vmmc) if (host->vmmc)
regulator_enable(host->vmmc); regulator_enable(host->vmmc);
pr_info(DRIVER_NAME ": Switching to 1.8V signalling " pr_warning("%s: Switching to 1.8V signalling voltage failed, "
"voltage failed, retrying with S18R set to 0\n"); "retrying with S18R set to 0\n", mmc_hostname(host->mmc));
return -EAGAIN; return -EAGAIN;
} else }
static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
struct mmc_ios *ios)
{
u16 ctrl;
/*
* Signal Voltage Switching is only applicable for Host Controllers
* v3.00 and above.
*/
if (host->version < SDHCI_SPEC_300)
return 0;
/*
* We first check whether the request is to set signalling voltage
* to 3.3V. If so, we change the voltage to 3.3V and return quickly.
*/
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330)
return sdhci_do_3_3v_signal_voltage_switch(host, ctrl);
else if (!(ctrl & SDHCI_CTRL_VDD_180) &&
(ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180))
return sdhci_do_1_8v_signal_voltage_switch(host, ctrl);
else
/* No signal voltage switch required */ /* No signal voltage switch required */
return 0; return 0;
} }
...@@ -2802,6 +2844,18 @@ int sdhci_add_host(struct sdhci_host *host) ...@@ -2802,6 +2844,18 @@ int sdhci_add_host(struct sdhci_host *host)
!(host->mmc->caps & MMC_CAP_NONREMOVABLE)) !(host->mmc->caps & MMC_CAP_NONREMOVABLE))
mmc->caps |= MMC_CAP_NEEDS_POLL; mmc->caps |= MMC_CAP_NEEDS_POLL;
/* If vqmmc regulator and no 1.8V signalling, then there's no UHS */
host->vqmmc = regulator_get(mmc_dev(mmc), "vqmmc");
if (IS_ERR(host->vqmmc)) {
pr_info("%s: no vqmmc regulator found\n", mmc_hostname(mmc));
host->vqmmc = NULL;
}
else if (regulator_is_supported_voltage(host->vqmmc, 1800000, 1800000))
regulator_enable(host->vqmmc);
else
caps[1] &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 |
SDHCI_SUPPORT_DDR50);
/* Any UHS-I mode in caps implies SDR12 and SDR25 support. */ /* Any UHS-I mode in caps implies SDR12 and SDR25 support. */
if (caps[1] & (SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 | if (caps[1] & (SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 |
SDHCI_SUPPORT_DDR50)) SDHCI_SUPPORT_DDR50))
...@@ -2832,15 +2886,6 @@ int sdhci_add_host(struct sdhci_host *host) ...@@ -2832,15 +2886,6 @@ int sdhci_add_host(struct sdhci_host *host)
if (caps[1] & SDHCI_DRIVER_TYPE_D) if (caps[1] & SDHCI_DRIVER_TYPE_D)
mmc->caps |= MMC_CAP_DRIVER_TYPE_D; mmc->caps |= MMC_CAP_DRIVER_TYPE_D;
/*
* If Power Off Notify capability is enabled by the host,
* set notify to short power off notify timeout value.
*/
if (mmc->caps2 & MMC_CAP2_POWEROFF_NOTIFY)
mmc->power_notify_type = MMC_HOST_PW_NOTIFY_SHORT;
else
mmc->power_notify_type = MMC_HOST_PW_NOTIFY_NONE;
/* Initial value for re-tuning timer count */ /* Initial value for re-tuning timer count */
host->tuning_count = (caps[1] & SDHCI_RETUNING_TIMER_COUNT_MASK) >> host->tuning_count = (caps[1] & SDHCI_RETUNING_TIMER_COUNT_MASK) >>
SDHCI_RETUNING_TIMER_COUNT_SHIFT; SDHCI_RETUNING_TIMER_COUNT_SHIFT;
...@@ -2862,7 +2907,8 @@ int sdhci_add_host(struct sdhci_host *host) ...@@ -2862,7 +2907,8 @@ int sdhci_add_host(struct sdhci_host *host)
if (IS_ERR(host->vmmc)) { if (IS_ERR(host->vmmc)) {
pr_info("%s: no vmmc regulator found\n", mmc_hostname(mmc)); pr_info("%s: no vmmc regulator found\n", mmc_hostname(mmc));
host->vmmc = NULL; host->vmmc = NULL;
} } else
regulator_enable(host->vmmc);
#ifdef CONFIG_REGULATOR #ifdef CONFIG_REGULATOR
if (host->vmmc) { if (host->vmmc) {
...@@ -3119,8 +3165,15 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) ...@@ -3119,8 +3165,15 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
tasklet_kill(&host->card_tasklet); tasklet_kill(&host->card_tasklet);
tasklet_kill(&host->finish_tasklet); tasklet_kill(&host->finish_tasklet);
if (host->vmmc) if (host->vmmc) {
regulator_disable(host->vmmc);
regulator_put(host->vmmc); regulator_put(host->vmmc);
}
if (host->vqmmc) {
regulator_disable(host->vqmmc);
regulator_put(host->vqmmc);
}
kfree(host->adma_desc); kfree(host->adma_desc);
kfree(host->align_buffer); kfree(host->align_buffer);
......
...@@ -1213,7 +1213,9 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id) ...@@ -1213,7 +1213,9 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id)
sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_BUFRE); sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_BUFRE);
sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFRE); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFRE);
} else if (state & INT_DTRANE) { } else if (state & INT_DTRANE) {
sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_DTRANE); sh_mmcif_writel(host->addr, MMCIF_CE_INT,
~(INT_CMD12DRE | INT_CMD12RBE |
INT_CMD12CRE | INT_DTRANE));
sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MDTRANE); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MDTRANE);
} else if (state & INT_CMD12RBE) { } else if (state & INT_CMD12RBE) {
sh_mmcif_writel(host->addr, MMCIF_CE_INT, sh_mmcif_writel(host->addr, MMCIF_CE_INT,
...@@ -1229,6 +1231,10 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id) ...@@ -1229,6 +1231,10 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id)
host->sd_error = true; host->sd_error = true;
dev_dbg(&host->pd->dev, "int err state = %08x\n", state); dev_dbg(&host->pd->dev, "int err state = %08x\n", state);
} }
if (host->state == STATE_IDLE) {
dev_info(&host->pd->dev, "Spurious IRQ status 0x%x", state);
return IRQ_HANDLED;
}
if (state & ~(INT_CMD12RBE | INT_CMD12CRE)) { if (state & ~(INT_CMD12RBE | INT_CMD12CRE)) {
if (!host->dma_active) if (!host->dma_active)
return IRQ_WAKE_THREAD; return IRQ_WAKE_THREAD;
......
...@@ -1337,21 +1337,7 @@ static struct pci_driver via_sd_driver = { ...@@ -1337,21 +1337,7 @@ static struct pci_driver via_sd_driver = {
.resume = via_sd_resume, .resume = via_sd_resume,
}; };
static int __init via_sd_drv_init(void) module_pci_driver(via_sd_driver);
{
pr_info(DRV_NAME ": VIA SD/MMC Card Reader driver "
"(C) 2008 VIA Technologies, Inc.\n");
return pci_register_driver(&via_sd_driver);
}
static void __exit via_sd_drv_exit(void)
{
pci_unregister_driver(&via_sd_driver);
}
module_init(via_sd_drv_init);
module_exit(via_sd_drv_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("VIA Technologies Inc."); MODULE_AUTHOR("VIA Technologies Inc.");
......
...@@ -2358,9 +2358,9 @@ static int vub300_probe(struct usb_interface *interface, ...@@ -2358,9 +2358,9 @@ static int vub300_probe(struct usb_interface *interface,
* which is contained at the end of struct mmc * which is contained at the end of struct mmc
*/ */
error4: error4:
usb_free_urb(command_out_urb);
error1:
usb_free_urb(command_res_urb); usb_free_urb(command_res_urb);
error1:
usb_free_urb(command_out_urb);
error0: error0:
return retval; return retval;
} }
......
...@@ -57,6 +57,7 @@ struct mmc_ext_csd { ...@@ -57,6 +57,7 @@ struct mmc_ext_csd {
unsigned int sa_timeout; /* Units: 100ns */ unsigned int sa_timeout; /* Units: 100ns */
unsigned int generic_cmd6_time; /* Units: 10ms */ unsigned int generic_cmd6_time; /* Units: 10ms */
unsigned int power_off_longtime; /* Units: ms */ unsigned int power_off_longtime; /* Units: ms */
u8 power_off_notification; /* state */
unsigned int hs_max_dtr; unsigned int hs_max_dtr;
#define MMC_HIGH_26_MAX_DTR 26000000 #define MMC_HIGH_26_MAX_DTR 26000000
#define MMC_HIGH_52_MAX_DTR 52000000 #define MMC_HIGH_52_MAX_DTR 52000000
...@@ -76,10 +77,13 @@ struct mmc_ext_csd { ...@@ -76,10 +77,13 @@ struct mmc_ext_csd {
bool hpi_en; /* HPI enablebit */ bool hpi_en; /* HPI enablebit */
bool hpi; /* HPI support bit */ bool hpi; /* HPI support bit */
unsigned int hpi_cmd; /* cmd used as HPI */ unsigned int hpi_cmd; /* cmd used as HPI */
bool bkops; /* background support bit */
bool bkops_en; /* background enable bit */
unsigned int data_sector_size; /* 512 bytes or 4KB */ unsigned int data_sector_size; /* 512 bytes or 4KB */
unsigned int data_tag_unit_size; /* DATA TAG UNIT size */ unsigned int data_tag_unit_size; /* DATA TAG UNIT size */
unsigned int boot_ro_lock; /* ro lock support */ unsigned int boot_ro_lock; /* ro lock support */
bool boot_ro_lockable; bool boot_ro_lockable;
u8 raw_exception_status; /* 53 */
u8 raw_partition_support; /* 160 */ u8 raw_partition_support; /* 160 */
u8 raw_erased_mem_count; /* 181 */ u8 raw_erased_mem_count; /* 181 */
u8 raw_ext_csd_structure; /* 194 */ u8 raw_ext_csd_structure; /* 194 */
...@@ -93,6 +97,7 @@ struct mmc_ext_csd { ...@@ -93,6 +97,7 @@ struct mmc_ext_csd {
u8 raw_sec_erase_mult; /* 230 */ u8 raw_sec_erase_mult; /* 230 */
u8 raw_sec_feature_support;/* 231 */ u8 raw_sec_feature_support;/* 231 */
u8 raw_trim_mult; /* 232 */ u8 raw_trim_mult; /* 232 */
u8 raw_bkops_status; /* 246 */
u8 raw_sectors[4]; /* 212 - 4 bytes */ u8 raw_sectors[4]; /* 212 - 4 bytes */
unsigned int feature_support; unsigned int feature_support;
...@@ -225,7 +230,7 @@ struct mmc_card { ...@@ -225,7 +230,7 @@ struct mmc_card {
#define MMC_CARD_SDXC (1<<6) /* card is SDXC */ #define MMC_CARD_SDXC (1<<6) /* card is SDXC */
#define MMC_CARD_REMOVED (1<<7) /* card has been removed */ #define MMC_CARD_REMOVED (1<<7) /* card has been removed */
#define MMC_STATE_HIGHSPEED_200 (1<<8) /* card is in HS200 mode */ #define MMC_STATE_HIGHSPEED_200 (1<<8) /* card is in HS200 mode */
#define MMC_STATE_SLEEP (1<<9) /* card is in sleep state */ #define MMC_STATE_DOING_BKOPS (1<<10) /* card is doing BKOPS */
unsigned int quirks; /* card quirks */ unsigned int quirks; /* card quirks */
#define MMC_QUIRK_LENIENT_FN0 (1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */ #define MMC_QUIRK_LENIENT_FN0 (1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */
#define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func->cur_blksize */ #define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func->cur_blksize */
...@@ -241,11 +246,6 @@ struct mmc_card { ...@@ -241,11 +246,6 @@ struct mmc_card {
#define MMC_QUIRK_LONG_READ_TIME (1<<9) /* Data read time > CSD says */ #define MMC_QUIRK_LONG_READ_TIME (1<<9) /* Data read time > CSD says */
#define MMC_QUIRK_SEC_ERASE_TRIM_BROKEN (1<<10) /* Skip secure for erase/trim */ #define MMC_QUIRK_SEC_ERASE_TRIM_BROKEN (1<<10) /* Skip secure for erase/trim */
/* byte mode */ /* byte mode */
unsigned int poweroff_notify_state; /* eMMC4.5 notify feature */
#define MMC_NO_POWER_NOTIFICATION 0
#define MMC_POWERED_ON 1
#define MMC_POWEROFF_SHORT 2
#define MMC_POWEROFF_LONG 3
unsigned int erase_size; /* erase size in sectors */ unsigned int erase_size; /* erase size in sectors */
unsigned int erase_shift; /* if erase unit is power 2 */ unsigned int erase_shift; /* if erase unit is power 2 */
...@@ -392,7 +392,7 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) ...@@ -392,7 +392,7 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
#define mmc_sd_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED) #define mmc_sd_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
#define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC) #define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC)
#define mmc_card_removed(c) ((c) && ((c)->state & MMC_CARD_REMOVED)) #define mmc_card_removed(c) ((c) && ((c)->state & MMC_CARD_REMOVED))
#define mmc_card_is_sleep(c) ((c)->state & MMC_STATE_SLEEP) #define mmc_card_doing_bkops(c) ((c)->state & MMC_STATE_DOING_BKOPS)
#define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT) #define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT)
#define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY) #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
...@@ -404,9 +404,9 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) ...@@ -404,9 +404,9 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
#define mmc_sd_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED) #define mmc_sd_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
#define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC) #define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC)
#define mmc_card_set_removed(c) ((c)->state |= MMC_CARD_REMOVED) #define mmc_card_set_removed(c) ((c)->state |= MMC_CARD_REMOVED)
#define mmc_card_set_sleep(c) ((c)->state |= MMC_STATE_SLEEP) #define mmc_card_set_doing_bkops(c) ((c)->state |= MMC_STATE_DOING_BKOPS)
#define mmc_card_clr_doing_bkops(c) ((c)->state &= ~MMC_STATE_DOING_BKOPS)
#define mmc_card_clr_sleep(c) ((c)->state &= ~MMC_STATE_SLEEP)
/* /*
* Quirk add/remove for MMC products. * Quirk add/remove for MMC products.
*/ */
......
...@@ -134,6 +134,8 @@ struct mmc_host; ...@@ -134,6 +134,8 @@ struct mmc_host;
struct mmc_card; struct mmc_card;
struct mmc_async_req; struct mmc_async_req;
extern int mmc_stop_bkops(struct mmc_card *);
extern int mmc_read_bkops_status(struct mmc_card *);
extern struct mmc_async_req *mmc_start_req(struct mmc_host *, extern struct mmc_async_req *mmc_start_req(struct mmc_host *,
struct mmc_async_req *, int *); struct mmc_async_req *, int *);
extern int mmc_interrupt_hpi(struct mmc_card *); extern int mmc_interrupt_hpi(struct mmc_card *);
...@@ -142,6 +144,8 @@ extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int); ...@@ -142,6 +144,8 @@ extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);
extern int mmc_app_cmd(struct mmc_host *, struct mmc_card *); extern int mmc_app_cmd(struct mmc_host *, struct mmc_card *);
extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *, extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
struct mmc_command *, int); struct mmc_command *, int);
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); extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);
#define MMC_ERASE_ARG 0x00000000 #define MMC_ERASE_ARG 0x00000000
......
...@@ -78,6 +78,10 @@ struct mmc_data; ...@@ -78,6 +78,10 @@ struct mmc_data;
* @data_offset: Set the offset of DATA register according to VERID. * @data_offset: Set the offset of DATA register according to VERID.
* @dev: Device associated with the MMC controller. * @dev: Device associated with the MMC controller.
* @pdata: Platform data associated with the MMC controller. * @pdata: Platform data associated with the MMC controller.
* @drv_data: Driver specific data for identified variant of the controller
* @priv: Implementation defined private data.
* @biu_clk: Pointer to bus interface unit clock instance.
* @ciu_clk: Pointer to card interface unit clock instance.
* @slot: Slots sharing this MMC controller. * @slot: Slots sharing this MMC controller.
* @fifo_depth: depth of FIFO. * @fifo_depth: depth of FIFO.
* @data_shift: log2 of FIFO item size. * @data_shift: log2 of FIFO item size.
...@@ -156,8 +160,12 @@ struct dw_mci { ...@@ -156,8 +160,12 @@ struct dw_mci {
u32 fifoth_val; u32 fifoth_val;
u16 verid; u16 verid;
u16 data_offset; u16 data_offset;
struct device dev; struct device *dev;
struct dw_mci_board *pdata; struct dw_mci_board *pdata;
struct dw_mci_drv_data *drv_data;
void *priv;
struct clk *biu_clk;
struct clk *ciu_clk;
struct dw_mci_slot *slot[MAX_MCI_SLOTS]; struct dw_mci_slot *slot[MAX_MCI_SLOTS];
/* FIFO push and pull */ /* FIFO push and pull */
...@@ -201,7 +209,8 @@ struct dw_mci_dma_ops { ...@@ -201,7 +209,8 @@ struct dw_mci_dma_ops {
#define DW_MCI_QUIRK_HIGHSPEED BIT(2) #define DW_MCI_QUIRK_HIGHSPEED BIT(2)
/* Unreliable card detection */ /* Unreliable card detection */
#define DW_MCI_QUIRK_BROKEN_CARD_DETECTION BIT(3) #define DW_MCI_QUIRK_BROKEN_CARD_DETECTION BIT(3)
/* Write Protect detection not available */
#define DW_MCI_QUIRK_NO_WRITE_PROTECT BIT(4)
struct dma_pdata; struct dma_pdata;
...@@ -218,7 +227,7 @@ struct dw_mci_board { ...@@ -218,7 +227,7 @@ struct dw_mci_board {
u32 num_slots; u32 num_slots;
u32 quirks; /* Workaround / Quirk flags */ u32 quirks; /* Workaround / Quirk flags */
unsigned int bus_hz; /* Bus speed */ unsigned int bus_hz; /* Clock speed at the cclk_in pad */
unsigned int caps; /* Capabilities */ unsigned int caps; /* Capabilities */
unsigned int caps2; /* More capabilities */ unsigned int caps2; /* More capabilities */
......
...@@ -259,10 +259,6 @@ struct mmc_host { ...@@ -259,10 +259,6 @@ struct mmc_host {
#define MMC_CAP2_RO_ACTIVE_HIGH (1 << 11) /* Write-protect signal active high */ #define MMC_CAP2_RO_ACTIVE_HIGH (1 << 11) /* Write-protect signal active high */
mmc_pm_flag_t pm_caps; /* supported pm features */ mmc_pm_flag_t pm_caps; /* supported pm features */
unsigned int power_notify_type;
#define MMC_HOST_PW_NOTIFY_NONE 0
#define MMC_HOST_PW_NOTIFY_SHORT 1
#define MMC_HOST_PW_NOTIFY_LONG 2
#ifdef CONFIG_MMC_CLKGATE #ifdef CONFIG_MMC_CLKGATE
int clk_requests; /* internal reference counter */ int clk_requests; /* internal reference counter */
...@@ -300,6 +296,7 @@ struct mmc_host { ...@@ -300,6 +296,7 @@ struct mmc_host {
#endif #endif
int rescan_disable; /* disable card detection */ int rescan_disable; /* disable card detection */
int rescan_entered; /* used with nonremovable devices */
struct mmc_card *card; /* device attached to this host */ struct mmc_card *card; /* device attached to this host */
......
...@@ -139,6 +139,7 @@ static inline bool mmc_op_multi(u32 opcode) ...@@ -139,6 +139,7 @@ static inline bool mmc_op_multi(u32 opcode)
#define R1_CURRENT_STATE(x) ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */ #define R1_CURRENT_STATE(x) ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */
#define R1_READY_FOR_DATA (1 << 8) /* sx, a */ #define R1_READY_FOR_DATA (1 << 8) /* sx, a */
#define R1_SWITCH_ERROR (1 << 7) /* sx, c */ #define R1_SWITCH_ERROR (1 << 7) /* sx, c */
#define R1_EXCEPTION_EVENT (1 << 6) /* sx, a */
#define R1_APP_CMD (1 << 5) /* sr, c */ #define R1_APP_CMD (1 << 5) /* sr, c */
#define R1_STATE_IDLE 0 #define R1_STATE_IDLE 0
...@@ -274,12 +275,15 @@ struct _mmc_csd { ...@@ -274,12 +275,15 @@ struct _mmc_csd {
#define EXT_CSD_FLUSH_CACHE 32 /* W */ #define EXT_CSD_FLUSH_CACHE 32 /* W */
#define EXT_CSD_CACHE_CTRL 33 /* R/W */ #define EXT_CSD_CACHE_CTRL 33 /* R/W */
#define EXT_CSD_POWER_OFF_NOTIFICATION 34 /* R/W */ #define EXT_CSD_POWER_OFF_NOTIFICATION 34 /* R/W */
#define EXT_CSD_EXP_EVENTS_STATUS 54 /* RO */
#define EXT_CSD_DATA_SECTOR_SIZE 61 /* R */ #define EXT_CSD_DATA_SECTOR_SIZE 61 /* R */
#define EXT_CSD_GP_SIZE_MULT 143 /* R/W */ #define EXT_CSD_GP_SIZE_MULT 143 /* R/W */
#define EXT_CSD_PARTITION_ATTRIBUTE 156 /* R/W */ #define EXT_CSD_PARTITION_ATTRIBUTE 156 /* R/W */
#define EXT_CSD_PARTITION_SUPPORT 160 /* RO */ #define EXT_CSD_PARTITION_SUPPORT 160 /* RO */
#define EXT_CSD_HPI_MGMT 161 /* R/W */ #define EXT_CSD_HPI_MGMT 161 /* R/W */
#define EXT_CSD_RST_N_FUNCTION 162 /* R/W */ #define EXT_CSD_RST_N_FUNCTION 162 /* R/W */
#define EXT_CSD_BKOPS_EN 163 /* R/W */
#define EXT_CSD_BKOPS_START 164 /* W */
#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_BOOT_WP 173 /* R/W */ #define EXT_CSD_BOOT_WP 173 /* R/W */
...@@ -313,11 +317,13 @@ struct _mmc_csd { ...@@ -313,11 +317,13 @@ struct _mmc_csd {
#define EXT_CSD_PWR_CL_200_360 237 /* RO */ #define EXT_CSD_PWR_CL_200_360 237 /* RO */
#define EXT_CSD_PWR_CL_DDR_52_195 238 /* RO */ #define EXT_CSD_PWR_CL_DDR_52_195 238 /* RO */
#define EXT_CSD_PWR_CL_DDR_52_360 239 /* RO */ #define EXT_CSD_PWR_CL_DDR_52_360 239 /* RO */
#define EXT_CSD_BKOPS_STATUS 246 /* RO */
#define EXT_CSD_POWER_OFF_LONG_TIME 247 /* RO */ #define EXT_CSD_POWER_OFF_LONG_TIME 247 /* RO */
#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_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_BKOPS_SUPPORT 502 /* RO */
#define EXT_CSD_HPI_FEATURES 503 /* RO */ #define EXT_CSD_HPI_FEATURES 503 /* RO */
/* /*
...@@ -377,6 +383,19 @@ struct _mmc_csd { ...@@ -377,6 +383,19 @@ struct _mmc_csd {
#define EXT_CSD_PWR_CL_4BIT_MASK 0x0F /* 8 bit PWR CLS */ #define EXT_CSD_PWR_CL_4BIT_MASK 0x0F /* 8 bit PWR CLS */
#define EXT_CSD_PWR_CL_8BIT_SHIFT 4 #define EXT_CSD_PWR_CL_8BIT_SHIFT 4
#define EXT_CSD_PWR_CL_4BIT_SHIFT 0 #define EXT_CSD_PWR_CL_4BIT_SHIFT 0
/*
* EXCEPTION_EVENT_STATUS field
*/
#define EXT_CSD_URGENT_BKOPS BIT(0)
#define EXT_CSD_DYNCAP_NEEDED BIT(1)
#define EXT_CSD_SYSPOOL_EXHAUSTED BIT(2)
#define EXT_CSD_PACKED_FAILURE BIT(3)
/*
* BKOPS status level
*/
#define EXT_CSD_BKOPS_LEVEL_2 0x2
/* /*
* MMC_SWITCH access modes * MMC_SWITCH access modes
*/ */
......
...@@ -97,7 +97,8 @@ struct sdhci_host { ...@@ -97,7 +97,8 @@ struct sdhci_host {
const struct sdhci_ops *ops; /* Low level hw interface */ const struct sdhci_ops *ops; /* Low level hw interface */
struct regulator *vmmc; /* Power regulator */ struct regulator *vmmc; /* Power regulator (vmmc) */
struct regulator *vqmmc; /* Signaling regulator (vccq) */
/* Internal data */ /* Internal data */
struct mmc_host *mmc; /* MMC structure */ struct mmc_host *mmc; /* MMC structure */
......
...@@ -49,6 +49,7 @@ struct sdhci_pxa_platdata { ...@@ -49,6 +49,7 @@ struct sdhci_pxa_platdata {
bool ext_cd_gpio_invert; bool ext_cd_gpio_invert;
unsigned int max_speed; unsigned int max_speed;
unsigned int host_caps; unsigned int host_caps;
unsigned int host_caps2;
unsigned int quirks; unsigned int quirks;
unsigned int pm_caps; unsigned int pm_caps;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment